// reflection.cpp #include "reflection.h" #include "compiler.h" #include "type-layout.h" #include // Implementation to back public-facing reflection API using namespace Slang; using namespace Slang::Compiler; // Conversion routines to help with strongly-typed reflection API static inline ExpressionType* convert(SlangReflectionType* type) { return (ExpressionType*) type; } static inline SlangReflectionType* convert(ExpressionType* type) { return (SlangReflectionType*) type; } static inline TypeLayout* convert(SlangReflectionTypeLayout* type) { return (TypeLayout*) type; } static inline SlangReflectionTypeLayout* convert(TypeLayout* type) { return (SlangReflectionTypeLayout*) type; } static inline VarDeclBase* convert(SlangReflectionVariable* var) { return (VarDeclBase*) var; } static inline SlangReflectionVariable* convert(VarDeclBase* var) { return (SlangReflectionVariable*) var; } static inline VarLayout* convert(SlangReflectionVariableLayout* var) { return (VarLayout*) var; } static inline SlangReflectionVariableLayout* convert(VarLayout* var) { return (SlangReflectionVariableLayout*) var; } static inline EntryPointLayout* convert(SlangReflectionEntryPoint* entryPoint) { return (EntryPointLayout*) entryPoint; } static inline SlangReflectionEntryPoint* convert(EntryPointLayout* entryPoint) { return (SlangReflectionEntryPoint*) entryPoint; } static inline ProgramLayout* convert(SlangReflection* program) { return (ProgramLayout*) program; } static inline SlangReflection* convert(ProgramLayout* program) { return (SlangReflection*) program; } // Type Reflection SLANG_API SlangTypeKind spReflectionType_GetKind(SlangReflectionType* inType) { auto type = convert(inType); if(!type) return SLANG_TYPE_KIND_NONE; // TODO(tfoley: Don't emit the same type more than once... if (auto basicType = type->As()) { return SLANG_TYPE_KIND_SCALAR; } else if (auto vectorType = type->As()) { return SLANG_TYPE_KIND_VECTOR; } else if (auto matrixType = type->As()) { return SLANG_TYPE_KIND_MATRIX; } else if (auto constantBufferType = type->As()) { return SLANG_TYPE_KIND_CONSTANT_BUFFER; } else if (auto samplerStateType = type->As()) { return SLANG_TYPE_KIND_SAMPLER_STATE; } else if (auto textureType = type->As()) { return SLANG_TYPE_KIND_RESOURCE; } // TODO: need a better way to handle this stuff... #define CASE(TYPE) \ else if(type->As()) do { \ return SLANG_TYPE_KIND_RESOURCE; \ } while(0) CASE(HLSLBufferType); CASE(HLSLRWBufferType); CASE(HLSLBufferType); CASE(HLSLRWBufferType); CASE(HLSLStructuredBufferType); CASE(HLSLRWStructuredBufferType); CASE(HLSLAppendStructuredBufferType); CASE(HLSLConsumeStructuredBufferType); CASE(HLSLByteAddressBufferType); CASE(HLSLRWByteAddressBufferType); CASE(UntypedBufferResourceType); #undef CASE else if (auto arrayType = type->As()) { return SLANG_TYPE_KIND_ARRAY; } else if( auto declRefType = type->As() ) { auto declRef = declRefType->declRef; if( auto structDeclRef = declRef.As() ) { return SLANG_TYPE_KIND_STRUCT; } } assert(!"unexpected"); return SLANG_TYPE_KIND_NONE; } SLANG_API unsigned int spReflectionType_GetFieldCount(SlangReflectionType* inType) { auto type = convert(inType); if(!type) return 0; // TODO: maybe filter based on kind if(auto declRefType = dynamic_cast(type)) { auto declRef = declRefType->declRef; if( auto structDeclRef = declRef.As()) { return structDeclRef.GetFields().Count(); } } return 0; } SLANG_API SlangReflectionVariable* spReflectionType_GetFieldByIndex(SlangReflectionType* inType, unsigned index) { auto type = convert(inType); if(!type) return nullptr; // TODO: maybe filter based on kind if(auto declRefType = dynamic_cast(type)) { auto declRef = declRefType->declRef; if( auto structDeclRef = declRef.As()) { auto fieldDeclRef = structDeclRef.GetFields().ToArray()[index]; return (SlangReflectionVariable*) fieldDeclRef.GetDecl(); } } return nullptr; } SLANG_API size_t spReflectionType_GetElementCount(SlangReflectionType* inType) { auto type = convert(inType); if(!type) return 0; if(auto arrayType = dynamic_cast(type)) { return GetIntVal(arrayType->ArrayLength); } else if( auto vectorType = dynamic_cast(type)) { return GetIntVal(vectorType->elementCount); } return 0; } SLANG_API SlangReflectionType* spReflectionType_GetElementType(SlangReflectionType* inType) { auto type = convert(inType); if(!type) return nullptr; if(auto arrayType = dynamic_cast(type)) { return (SlangReflectionType*) arrayType->BaseType.Ptr(); } else if( auto constantBufferType = dynamic_cast(type)) { return convert(constantBufferType->elementType.Ptr()); } else if( auto vectorType = dynamic_cast(type)) { return convert(vectorType->elementType.Ptr()); } else if( auto matrixType = dynamic_cast(type)) { return convert(matrixType->getElementType()); } return nullptr; } SLANG_API unsigned int spReflectionType_GetRowCount(SlangReflectionType* inType) { auto type = convert(inType); if(!type) return 0; if(auto matrixType = dynamic_cast(type)) { return GetIntVal(matrixType->getRowCount()); } else if(auto vectorType = dynamic_cast(type)) { return 1; } else if( auto basicType = dynamic_cast(type) ) { return 1; } return 0; } SLANG_API unsigned int spReflectionType_GetColumnCount(SlangReflectionType* inType) { auto type = convert(inType); if(!type) return 0; if(auto matrixType = dynamic_cast(type)) { return GetIntVal(matrixType->getColumnCount()); } else if(auto vectorType = dynamic_cast(type)) { return GetIntVal(vectorType->elementCount); } else if( auto basicType = dynamic_cast(type) ) { return 1; } return 0; } SLANG_API SlangScalarType spReflectionType_GetScalarType(SlangReflectionType* inType) { auto type = convert(inType); if(!type) return 0; if(auto matrixType = dynamic_cast(type)) { type = matrixType->getElementType(); } else if(auto vectorType = dynamic_cast(type)) { type = vectorType->elementType.Ptr(); } if(auto basicType = dynamic_cast(type)) { switch (basicType->BaseType) { #define CASE(BASE, TAG) \ case BaseType::BASE: return SLANG_SCALAR_TYPE_##TAG CASE(Void, VOID); CASE(Int, INT32); CASE(Float, FLOAT32); CASE(UInt, UINT32); CASE(Bool, BOOL); CASE(UInt64, UINT64); #undef CASE default: assert(!"unexpected"); return SLANG_SCALAR_TYPE_NONE; break; } } return SLANG_SCALAR_TYPE_NONE; } SLANG_API SlangResourceShape spReflectionType_GetResourceShape(SlangReflectionType* inType) { auto type = convert(inType); if(!type) return 0; while(auto arrayType = type->As()) { type = arrayType->BaseType.Ptr(); } if(auto textureType = type->As()) { return textureType->getShape(); } // TODO: need a better way to handle this stuff... #define CASE(TYPE, SHAPE, ACCESS) \ else if(type->As()) do { \ return SHAPE; \ } while(0) CASE(HLSLBufferType, SLANG_TEXTURE_BUFFER, SLANG_RESOURCE_ACCESS_READ); CASE(HLSLRWBufferType, SLANG_TEXTURE_BUFFER, SLANG_RESOURCE_ACCESS_READ_WRITE); CASE(HLSLBufferType, SLANG_TEXTURE_BUFFER, SLANG_RESOURCE_ACCESS_READ); CASE(HLSLRWBufferType, SLANG_TEXTURE_BUFFER, SLANG_RESOURCE_ACCESS_READ_WRITE); CASE(HLSLStructuredBufferType, SLANG_STRUCTURED_BUFFER, SLANG_RESOURCE_ACCESS_READ); CASE(HLSLRWStructuredBufferType, SLANG_STRUCTURED_BUFFER, SLANG_RESOURCE_ACCESS_READ_WRITE); CASE(HLSLAppendStructuredBufferType, SLANG_STRUCTURED_BUFFER, SLANG_RESOURCE_ACCESS_APPEND); CASE(HLSLConsumeStructuredBufferType, SLANG_STRUCTURED_BUFFER, SLANG_RESOURCE_ACCESS_CONSUME); CASE(HLSLByteAddressBufferType, SLANG_BYTE_ADDRESS_BUFFER, SLANG_RESOURCE_ACCESS_READ); CASE(HLSLRWByteAddressBufferType, SLANG_BYTE_ADDRESS_BUFFER, SLANG_RESOURCE_ACCESS_READ_WRITE); CASE(UntypedBufferResourceType, SLANG_BYTE_ADDRESS_BUFFER, SLANG_RESOURCE_ACCESS_READ); #undef CASE return SLANG_RESOURCE_NONE; } SLANG_API SlangResourceAccess spReflectionType_GetResourceAccess(SlangReflectionType* inType) { auto type = convert(inType); if(!type) return 0; while(auto arrayType = type->As()) { type = arrayType->BaseType.Ptr(); } if(auto textureType = type->As()) { return textureType->getAccess(); } // TODO: need a better way to handle this stuff... #define CASE(TYPE, SHAPE, ACCESS) \ else if(type->As()) do { \ return ACCESS; \ } while(0) CASE(HLSLBufferType, SLANG_TEXTURE_BUFFER, SLANG_RESOURCE_ACCESS_READ); CASE(HLSLRWBufferType, SLANG_TEXTURE_BUFFER, SLANG_RESOURCE_ACCESS_READ_WRITE); CASE(HLSLBufferType, SLANG_TEXTURE_BUFFER, SLANG_RESOURCE_ACCESS_READ); CASE(HLSLRWBufferType, SLANG_TEXTURE_BUFFER, SLANG_RESOURCE_ACCESS_READ_WRITE); CASE(HLSLStructuredBufferType, SLANG_STRUCTURED_BUFFER, SLANG_RESOURCE_ACCESS_READ); CASE(HLSLRWStructuredBufferType, SLANG_STRUCTURED_BUFFER, SLANG_RESOURCE_ACCESS_READ_WRITE); CASE(HLSLAppendStructuredBufferType, SLANG_STRUCTURED_BUFFER, SLANG_RESOURCE_ACCESS_APPEND); CASE(HLSLConsumeStructuredBufferType, SLANG_STRUCTURED_BUFFER, SLANG_RESOURCE_ACCESS_CONSUME); CASE(HLSLByteAddressBufferType, SLANG_BYTE_ADDRESS_BUFFER, SLANG_RESOURCE_ACCESS_READ); CASE(HLSLRWByteAddressBufferType, SLANG_BYTE_ADDRESS_BUFFER, SLANG_RESOURCE_ACCESS_READ_WRITE); CASE(UntypedBufferResourceType, SLANG_BYTE_ADDRESS_BUFFER, SLANG_RESOURCE_ACCESS_READ); #undef CASE return SLANG_RESOURCE_ACCESS_NONE; } SLANG_API SlangReflectionType* spReflectionType_GetResourceResultType(SlangReflectionType* inType) { auto type = convert(inType); if(!type) return nullptr; while(auto arrayType = type->As()) { type = arrayType->BaseType.Ptr(); } if (auto textureType = type->As()) { return convert(textureType->elementType.Ptr()); } // TODO: need a better way to handle this stuff... #define CASE(TYPE, SHAPE, ACCESS) \ else if(type->As()) do { \ return convert(type->As()->elementType.Ptr()); \ } while(0) CASE(HLSLBufferType, SLANG_TEXTURE_BUFFER, SLANG_RESOURCE_ACCESS_READ); CASE(HLSLRWBufferType, SLANG_TEXTURE_BUFFER, SLANG_RESOURCE_ACCESS_READ_WRITE); CASE(HLSLBufferType, SLANG_TEXTURE_BUFFER, SLANG_RESOURCE_ACCESS_READ); CASE(HLSLRWBufferType, SLANG_TEXTURE_BUFFER, SLANG_RESOURCE_ACCESS_READ_WRITE); // TODO: structured buffer needs to expose type layout! CASE(HLSLStructuredBufferType, SLANG_STRUCTURED_BUFFER, SLANG_RESOURCE_ACCESS_READ); CASE(HLSLRWStructuredBufferType, SLANG_STRUCTURED_BUFFER, SLANG_RESOURCE_ACCESS_READ_WRITE); CASE(HLSLAppendStructuredBufferType, SLANG_STRUCTURED_BUFFER, SLANG_RESOURCE_ACCESS_APPEND); CASE(HLSLConsumeStructuredBufferType, SLANG_STRUCTURED_BUFFER, SLANG_RESOURCE_ACCESS_CONSUME); #undef CASE return nullptr; } // Type Layout Reflection SLANG_API SlangReflectionType* spReflectionTypeLayout_GetType(SlangReflectionTypeLayout* inTypeLayout) { auto typeLayout = convert(inTypeLayout); if(!typeLayout) return nullptr; return (SlangReflectionType*) typeLayout->type.Ptr(); } SLANG_API size_t spReflectionTypeLayout_GetSize(SlangReflectionTypeLayout* inTypeLayout, SlangParameterCategory category) { auto typeLayout = convert(inTypeLayout); if(!typeLayout) return 0; auto info = typeLayout->FindResourceInfo(LayoutResourceKind(category)); if(!info) return 0; return info->count; } SLANG_API SlangReflectionVariableLayout* spReflectionTypeLayout_GetFieldByIndex(SlangReflectionTypeLayout* inTypeLayout, unsigned index) { auto typeLayout = convert(inTypeLayout); if(!typeLayout) return nullptr; if(auto structTypeLayout = dynamic_cast(typeLayout)) { return (SlangReflectionVariableLayout*) structTypeLayout->fields[index].Ptr(); } return nullptr; } SLANG_API size_t spReflectionTypeLayout_GetElementStride(SlangReflectionTypeLayout* inTypeLayout, SlangParameterCategory category) { auto typeLayout = convert(inTypeLayout); if(!typeLayout) return 0; if( auto arrayTypeLayout = dynamic_cast(typeLayout)) { if(category == SLANG_PARAMETER_CATEGORY_UNIFORM) { return arrayTypeLayout->uniformStride; } else { auto elementTypeLayout = arrayTypeLayout->elementTypeLayout; auto info = elementTypeLayout->FindResourceInfo(LayoutResourceKind(category)); if(!info) return 0; return info->count; } } return 0; } SLANG_API SlangReflectionTypeLayout* spReflectionTypeLayout_GetElementTypeLayout(SlangReflectionTypeLayout* inTypeLayout) { auto typeLayout = convert(inTypeLayout); if(!typeLayout) return nullptr; if( auto arrayTypeLayout = dynamic_cast(typeLayout)) { return (SlangReflectionTypeLayout*) arrayTypeLayout->elementTypeLayout.Ptr(); } else if( auto constantBufferTypeLayout = dynamic_cast(typeLayout)) { return convert(constantBufferTypeLayout->elementTypeLayout.Ptr()); } else if( auto structuredBufferTypeLayout = dynamic_cast(typeLayout)) { return convert(structuredBufferTypeLayout->elementTypeLayout.Ptr()); } return nullptr; } static SlangParameterCategory getParameterCategory( LayoutResourceKind kind) { return SlangParameterCategory(kind); } static SlangParameterCategory getParameterCategory( TypeLayout* typeLayout) { auto resourceInfoCount = typeLayout->resourceInfos.Count(); if(resourceInfoCount == 1) { return getParameterCategory(typeLayout->resourceInfos[0].kind); } else if(resourceInfoCount == 0) { // TODO: can this ever happen? return SLANG_PARAMETER_CATEGORY_NONE; } return SLANG_PARAMETER_CATEGORY_MIXED; } SLANG_API SlangParameterCategory spReflectionTypeLayout_GetParameterCategory(SlangReflectionTypeLayout* inTypeLayout) { auto typeLayout = convert(inTypeLayout); if(!typeLayout) return SLANG_PARAMETER_CATEGORY_NONE; return getParameterCategory(typeLayout); } SLANG_API unsigned spReflectionTypeLayout_GetCategoryCount(SlangReflectionTypeLayout* inTypeLayout) { auto typeLayout = convert(inTypeLayout); if(!typeLayout) return 0; return (unsigned) typeLayout->resourceInfos.Count(); } SLANG_API SlangParameterCategory spReflectionTypeLayout_GetCategoryByIndex(SlangReflectionTypeLayout* inTypeLayout, unsigned index) { auto typeLayout = convert(inTypeLayout); if(!typeLayout) return SLANG_PARAMETER_CATEGORY_NONE; return typeLayout->resourceInfos[index].kind; } // Variable Reflection SLANG_API char const* spReflectionVariable_GetName(SlangReflectionVariable* inVar) { auto var = convert(inVar); if(!var) return nullptr; // If the variable is one that has an "external" name that is supposed // to be exposed for reflection, then report it here if(auto reflectionNameMod = var->FindModifier()) return reflectionNameMod->nameToken.Content.Buffer(); return var->getName().Buffer(); } SLANG_API SlangReflectionType* spReflectionVariable_GetType(SlangReflectionVariable* inVar) { auto var = convert(inVar); if(!var) return nullptr; return convert(var->getType()); } // Variable Layout Reflection SLANG_API SlangReflectionVariable* spReflectionVariableLayout_GetVariable(SlangReflectionVariableLayout* inVarLayout) { auto varLayout = convert(inVarLayout); if(!varLayout) return nullptr; return convert(varLayout->varDecl.GetDecl()); } SLANG_API SlangReflectionTypeLayout* spReflectionVariableLayout_GetTypeLayout(SlangReflectionVariableLayout* inVarLayout) { auto varLayout = convert(inVarLayout); if(!varLayout) return nullptr; return convert(varLayout->getTypeLayout()); } SLANG_API size_t spReflectionVariableLayout_GetOffset(SlangReflectionVariableLayout* inVarLayout, SlangParameterCategory category) { auto varLayout = convert(inVarLayout); if(!varLayout) return 0; auto info = varLayout->FindResourceInfo(LayoutResourceKind(category)); if(!info) return 0; return info->index; } SLANG_API size_t spReflectionVariableLayout_GetSpace(SlangReflectionVariableLayout* inVarLayout, SlangParameterCategory category) { auto varLayout = convert(inVarLayout); if(!varLayout) return 0; auto info = varLayout->FindResourceInfo(LayoutResourceKind(category)); if(!info) return 0; return info->space; } // Shader Parameter Reflection SLANG_API unsigned spReflectionParameter_GetBindingIndex(SlangReflectionParameter* inVarLayout) { auto varLayout = convert(inVarLayout); if(!varLayout) return 0; if(varLayout->resourceInfos.Count() > 0) { return (unsigned) varLayout->resourceInfos[0].index; } return 0; } SLANG_API unsigned spReflectionParameter_GetBindingSpace(SlangReflectionParameter* inVarLayout) { auto varLayout = convert(inVarLayout); if(!varLayout) return 0; if(varLayout->resourceInfos.Count() > 0) { return (unsigned) varLayout->resourceInfos[0].space; } return 0; } // Entry Point Reflection SLANG_API SlangStage spReflectionEntryPoint_getStage(SlangReflectionEntryPoint* inEntryPoint) { auto entryPointLayout = convert(inEntryPoint); if(!entryPointLayout) return SLANG_STAGE_NONE; return SlangStage(entryPointLayout->profile.GetStage()); } SLANG_API void spReflectionEntryPoint_getComputeThreadGroupSize( SlangReflectionEntryPoint* inEntryPoint, SlangUInt axisCount, SlangUInt* outSizeAlongAxis) { auto entryPointLayout = convert(inEntryPoint); if(!entryPointLayout) return; if(!axisCount) return; if(!outSizeAlongAxis) return; auto entryPointFunc = entryPointLayout->entryPoint; if(!entryPointFunc) return; auto numThreadsAttribute = entryPointFunc->FindModifier(); if(!numThreadsAttribute) return; if(axisCount > 0) outSizeAlongAxis[0] = numThreadsAttribute->x; if(axisCount > 1) outSizeAlongAxis[1] = numThreadsAttribute->y; if(axisCount > 2) outSizeAlongAxis[2] = numThreadsAttribute->z; for( SlangUInt aa = 3; aa < axisCount; ++aa ) { outSizeAlongAxis[aa] = 1; } } // Shader Reflection SLANG_API unsigned spReflection_GetParameterCount(SlangReflection* inProgram) { auto program = convert(inProgram); if(!program) return 0; auto globalLayout = program->globalScopeLayout; if(auto globalConstantBufferLayout = globalLayout.As()) { globalLayout = globalConstantBufferLayout->elementTypeLayout; } if(auto globalStructLayout = globalLayout.As()) { return globalStructLayout->fields.Count(); } return 0; } SLANG_API SlangReflectionParameter* spReflection_GetParameterByIndex(SlangReflection* inProgram, unsigned index) { auto program = convert(inProgram); if(!program) return nullptr; auto globalLayout = program->globalScopeLayout; if(auto globalConstantBufferLayout = globalLayout.As()) { globalLayout = globalConstantBufferLayout->elementTypeLayout; } if(auto globalStructLayout = globalLayout.As()) { return convert(globalStructLayout->fields[index].Ptr()); } return nullptr; } SLANG_API SlangUInt spReflection_getEntryPointCount(SlangReflection* inProgram) { auto program = convert(inProgram); if(!program) return 0; return SlangUInt(program->entryPoints.Count()); } SLANG_API SlangReflectionEntryPoint* spReflection_getEntryPointByIndex(SlangReflection* inProgram, SlangUInt index) { auto program = convert(inProgram); if(!program) return 0; return convert(program->entryPoints[(int) index].Ptr()); } namespace Slang { namespace Compiler { // Debug helper code: dump reflection data after generation struct PrettyWriter { StringBuilder sb; bool startOfLine = true; int indent = 0; }; static void adjust(PrettyWriter& writer) { if (!writer.startOfLine) return; int indent = writer.indent; for (int ii = 0; ii < indent; ++ii) writer.sb << " "; writer.startOfLine = false; } static void indent(PrettyWriter& writer) { writer.indent++; } static void dedent(PrettyWriter& writer) { writer.indent--; } static void write(PrettyWriter& writer, char const* text) { // TODO: can do this more efficiently... char const* cursor = text; for(;;) { char c = *cursor++; if (!c) break; if (c == '\n') { writer.startOfLine = true; } else { adjust(writer); } writer.sb << c; } } static void write(PrettyWriter& writer, UInt val) { adjust(writer); writer.sb << ((unsigned int) val); } static void emitReflectionVarInfoJSON(PrettyWriter& writer, slang::VariableReflection* var); static void emitReflectionTypeLayoutJSON(PrettyWriter& writer, slang::TypeLayoutReflection* type); static void emitReflectionTypeJSON(PrettyWriter& writer, slang::TypeReflection* type); static void emitReflectionVarBindingInfoJSON( PrettyWriter& writer, SlangParameterCategory category, UInt index, UInt count, UInt space = 0) { if( category == SLANG_PARAMETER_CATEGORY_UNIFORM ) { write(writer,"\"kind\": \"uniform\""); write(writer, ", "); write(writer,"\"offset\": "); write(writer, index); write(writer, ", "); write(writer, "\"size\": "); write(writer, count); } else { write(writer, "\"kind\": \""); switch( category ) { #define CASE(NAME, KIND) case SLANG_PARAMETER_CATEGORY_##NAME: write(writer, #KIND); break CASE(CONSTANT_BUFFER, constantBuffer); CASE(SHADER_RESOURCE, shaderResource); CASE(UNORDERED_ACCESS, unorderedAccess); CASE(VERTEX_INPUT, vertexInput); CASE(FRAGMENT_OUTPUT, fragmentOutput); CASE(SAMPLER_STATE, samplerState); #undef CASE default: write(writer, "unknown"); assert(!"unexpected"); break; } write(writer, "\""); if( space ) { write(writer, ", "); write(writer, "\"space\": "); write(writer, space); } write(writer, ", "); write(writer, "\"index\": "); write(writer, index); if( count != 1) { write(writer, ", "); write(writer, "\"count\": "); write(writer, count); } } } static void emitReflectionVarBindingInfoJSON( PrettyWriter& writer, slang::VariableLayoutReflection* var) { auto typeLayout = var->getTypeLayout(); auto categoryCount = var->getCategoryCount(); if( categoryCount != 1 ) { write(writer,"\"bindings\": [\n"); } else { write(writer,"\"binding\": "); } indent(writer); for(uint32_t cc = 0; cc < categoryCount; ++cc ) { auto category = var->getCategoryByIndex(cc); auto index = var->getOffset(category); auto space = var->getBindingSpace(category); auto count = typeLayout->getSize(category); if (cc != 0) write(writer, ",\n"); write(writer,"{"); emitReflectionVarBindingInfoJSON( writer, category, index, count, space); write(writer,"}"); } dedent(writer); if( categoryCount != 1 ) { write(writer,"\n]"); } } static void emitReflectionNameInfoJSON( PrettyWriter& writer, char const* name) { // TODO: deal with escaping special characters if/when needed write(writer, "\"name\": \""); write(writer, name); write(writer, "\""); } static void emitReflectionVarLayoutJSON( PrettyWriter& writer, slang::VariableLayoutReflection* var) { write(writer, "{\n"); indent(writer); emitReflectionNameInfoJSON(writer, var->getName()); write(writer, ",\n"); write(writer, "\"type\": "); emitReflectionTypeLayoutJSON(writer, var->getTypeLayout()); write(writer, ",\n"); emitReflectionVarBindingInfoJSON(writer, var); dedent(writer); write(writer, "\n}"); } static void emitReflectionScalarTypeInfoJSON( PrettyWriter& writer, SlangScalarType scalarType) { write(writer, "\"scalarType\": \""); switch (scalarType) { default: write(writer, "unknown"); assert(!"unexpected"); break; #define CASE(TAG, ID) case slang::TypeReflection::ScalarType::TAG: write(writer, #ID); break CASE(Void, void); CASE(Bool, bool); CASE(Int32, int32); CASE(UInt32, uint32); CASE(Int64, int64); CASE(UInt64, uint64); CASE(Float16, float16); CASE(Float32, float32); CASE(Float64, float64); #undef CASE } write(writer, "\""); } static void emitReflectionTypeInfoJSON( PrettyWriter& writer, slang::TypeReflection* type) { switch( type->getKind() ) { case SLANG_TYPE_KIND_SAMPLER_STATE: write(writer, "\"kind\": \"samplerState\""); break; case SLANG_TYPE_KIND_RESOURCE: { auto shape = type->getResourceShape(); auto access = type->getResourceAccess(); write(writer, "\"kind\": \"resource\""); write(writer, ",\n"); write(writer, "\"baseShape\": \""); switch (shape & SLANG_RESOURCE_BASE_SHAPE_MASK) { default: write(writer, "unknown"); assert(!"unexpected"); break; #define CASE(SHAPE, NAME) case SLANG_##SHAPE: write(writer, #NAME); break CASE(TEXTURE_1D, texture1D); CASE(TEXTURE_2D, texture2D); CASE(TEXTURE_3D, texture3D); CASE(TEXTURE_CUBE, textureCube); CASE(TEXTURE_BUFFER, textureBuffer); CASE(STRUCTURED_BUFFER, structuredBuffer); CASE(BYTE_ADDRESS_BUFFER, byteAddressBuffer); #undef CASE } write(writer, "\""); if (shape & SLANG_TEXTURE_ARRAY_FLAG) { write(writer, ",\n"); write(writer, "\"array\": true"); } if (shape & SLANG_TEXTURE_MULTISAMPLE_FLAG) { write(writer, ",\n"); write(writer, "\"multisample\": true"); } if( access != SLANG_RESOURCE_ACCESS_READ ) { write(writer, ",\n\"access\": \""); switch(access) { default: write(writer, "unknown"); assert(!"unexpected"); break; case SLANG_RESOURCE_ACCESS_READ: break; case SLANG_RESOURCE_ACCESS_READ_WRITE: write(writer, "readWrite"); break; case SLANG_RESOURCE_ACCESS_RASTER_ORDERED: write(writer, "rasterOrdered"); break; case SLANG_RESOURCE_ACCESS_APPEND: write(writer, "append"); break; case SLANG_RESOURCE_ACCESS_CONSUME: write(writer, "consume"); break; } write(writer, "\""); } } break; case SLANG_TYPE_KIND_CONSTANT_BUFFER: write(writer, "\"kind\": \"constantBuffer\""); write(writer, ",\n"); write(writer, "\"elementType\": "); emitReflectionTypeJSON( writer, type->getElementType()); break; case SLANG_TYPE_KIND_SCALAR: write(writer, "\"kind\": \"scalar\""); write(writer, ",\n"); emitReflectionScalarTypeInfoJSON( writer, type->getScalarType()); break; case SLANG_TYPE_KIND_VECTOR: write(writer, "\"kind\": \"vector\""); write(writer, ",\n"); write(writer, "\"elementCount\": "); write(writer, type->getElementCount()); write(writer, ",\n"); write(writer, "\"elementType\": "); emitReflectionTypeJSON( writer, type->getElementType()); break; case SLANG_TYPE_KIND_MATRIX: write(writer, "\"kind\": \"matrix\""); write(writer, ",\n"); write(writer, "\"rowCount\": "); write(writer, type->getRowCount()); write(writer, ",\n"); write(writer, "\"columnCount\": "); write(writer, type->getColumnCount()); write(writer, ",\n"); write(writer, "\"elementType\": "); emitReflectionTypeJSON( writer, type->getElementType()); break; case SLANG_TYPE_KIND_ARRAY: { auto arrayType = type; write(writer, "\"kind\": \"array\""); write(writer, ",\n"); write(writer, "\"elementCount\": "); write(writer, arrayType->getElementCount()); write(writer, ",\n"); write(writer, "\"elementType\": "); emitReflectionTypeJSON(writer, arrayType->getElementType()); } break; case SLANG_TYPE_KIND_STRUCT: { write(writer, "\"kind\": \"struct\",\n"); write(writer, "\"fields\": [\n"); indent(writer); auto structType = type; auto fieldCount = structType->getFieldCount(); for( uint32_t ff = 0; ff < fieldCount; ++ff ) { if (ff != 0) write(writer, ",\n"); emitReflectionVarInfoJSON( writer, structType->getFieldByIndex(ff)); } dedent(writer); write(writer, "\n]"); } break; default: assert(!"unimplemented"); break; } } static void emitReflectionTypeLayoutInfoJSON( PrettyWriter& writer, slang::TypeLayoutReflection* typeLayout) { switch( typeLayout->getKind() ) { default: emitReflectionTypeInfoJSON(writer, typeLayout->getType()); break; case SLANG_TYPE_KIND_ARRAY: { auto arrayTypeLayout = typeLayout; auto elementTypeLayout = arrayTypeLayout->getElementTypeLayout(); write(writer, "\"kind\": \"array\""); write(writer, ",\n"); write(writer, "\"elementCount\": "); write(writer, arrayTypeLayout->getElementCount()); write(writer, ",\n"); write(writer, "\"elementType\": "); emitReflectionTypeLayoutJSON( writer, elementTypeLayout); if (arrayTypeLayout->getSize(SLANG_PARAMETER_CATEGORY_UNIFORM) != 0) { write(writer, ",\n"); write(writer, "\"uniformStride\": "); write(writer, arrayTypeLayout->getElementStride(SLANG_PARAMETER_CATEGORY_UNIFORM)); } } break; case SLANG_TYPE_KIND_STRUCT: { write(writer, "\"kind\": \"struct\",\n"); write(writer, "\"fields\": [\n"); indent(writer); auto structTypeLayout = typeLayout; auto fieldCount = structTypeLayout->getFieldCount(); for( uint32_t ff = 0; ff < fieldCount; ++ff ) { if (ff != 0) write(writer, ",\n"); emitReflectionVarLayoutJSON( writer, structTypeLayout->getFieldByIndex(ff)); } dedent(writer); write(writer, "\n]"); } break; case SLANG_TYPE_KIND_CONSTANT_BUFFER: write(writer, "\"kind\": \"constantBuffer\""); write(writer, ",\n"); write(writer, "\"elementType\": "); emitReflectionTypeLayoutJSON( writer, typeLayout->getElementTypeLayout()); break; } // TODO: emit size info for types } static void emitReflectionTypeLayoutJSON( PrettyWriter& writer, slang::TypeLayoutReflection* typeLayout) { write(writer, "{\n"); indent(writer); emitReflectionTypeLayoutInfoJSON(writer, typeLayout); dedent(writer); write(writer, "\n}"); } static void emitReflectionTypeJSON( PrettyWriter& writer, slang::TypeReflection* type) { write(writer, "{\n"); indent(writer); emitReflectionTypeInfoJSON(writer, type); dedent(writer); write(writer, "\n}"); } static void emitReflectionVarInfoJSON( PrettyWriter& writer, slang::VariableReflection* var) { emitReflectionNameInfoJSON(writer, var->getName()); write(writer, ",\n"); write(writer, "\"type\": "); emitReflectionTypeJSON(writer, var->getType()); } #if 0 static void emitReflectionBindingInfoJSON( PrettyWriter& writer, ReflectionParameterNode* param) { auto info = ¶m->binding; if( info->category == SLANG_PARAMETER_CATEGORY_MIXED ) { write(writer,"\"bindings\": [\n"); indent(writer); ReflectionSize bindingCount = info->bindingCount; assert(bindingCount); ReflectionParameterBindingInfo* bindings = info->bindings; for( ReflectionSize bb = 0; bb < bindingCount; ++bb ) { if (bb != 0) write(writer, ",\n"); write(writer,"{"); auto& binding = bindings[bb]; emitReflectionVarBindingInfoJSON( writer, binding.category, binding.index, (ReflectionSize) param->GetTypeLayout()->GetSize(binding.category), binding.space); write(writer,"}"); } dedent(writer); write(writer,"\n]"); } else { write(writer,"\"binding\": {"); indent(writer); emitReflectionVarBindingInfoJSON( writer, info->category, info->index, (ReflectionSize) param->GetTypeLayout()->GetSize(info->category), info->space); dedent(writer); write(writer,"}"); } } #endif static void emitReflectionParamJSON( PrettyWriter& writer, slang::VariableLayoutReflection* param) { write(writer, "{\n"); indent(writer); emitReflectionNameInfoJSON(writer, param->getName()); write(writer, ",\n"); emitReflectionVarBindingInfoJSON(writer, param); write(writer, ",\n"); write(writer, "\"type\": "); emitReflectionTypeLayoutJSON(writer, param->getTypeLayout()); dedent(writer); write(writer, "\n}"); } template struct Range { public: Range( T begin, T end) : mBegin(begin) , mEnd(end) {} struct Iterator { public: explicit Iterator(T value) : mValue(value) {} T operator*() const { return mValue; } void operator++() { mValue++; } bool operator!=(Iterator const& other) { return mValue != other.mValue; } private: T mValue; }; Iterator begin() const { return Iterator(mBegin); } Iterator end() const { return Iterator(mEnd); } private: T mBegin; T mEnd; }; template Range range(T begin, T end) { return Range(begin, end); } template Range range(T end) { return Range(T(0), end); } static void emitReflectionJSON( PrettyWriter& writer, slang::ShaderReflection* programReflection) { write(writer, "{\n"); indent(writer); write(writer, "\"parameters\": [\n"); indent(writer); auto parameterCount = programReflection->getParameterCount(); for( auto pp : range(parameterCount) ) { if(pp != 0) write(writer, ",\n"); auto parameter = programReflection->getParameterByIndex(pp); emitReflectionParamJSON(writer, parameter); } dedent(writer); write(writer, "\n]"); dedent(writer); write(writer, "\n}\n"); } #if 0 ReflectionBlob* ReflectionBlob::Create( CollectionOfTranslationUnits* program) { ReflectionGenerationContext context; ReflectionBlob* blob = GenerateReflectionBlob(&context, program); #if 0 String debugDump = blob->emitAsJSON(); OutputDebugStringA("REFLECTION BLOB\n"); OutputDebugStringA(debugDump.begin()); #endif return blob; } #endif // JSON emit logic String emitReflectionJSON( ProgramLayout* programLayout) { auto programReflection = (slang::ShaderReflection*) programLayout; PrettyWriter writer; emitReflectionJSON(writer, programReflection); return writer.sb.ProduceString(); } }}