diff options
Diffstat (limited to 'source/slang/reflection.cpp')
| -rw-r--r-- | source/slang/reflection.cpp | 1404 |
1 files changed, 1404 insertions, 0 deletions
diff --git a/source/slang/reflection.cpp b/source/slang/reflection.cpp new file mode 100644 index 000000000..1a56a8c1f --- /dev/null +++ b/source/slang/reflection.cpp @@ -0,0 +1,1404 @@ +// reflection.cpp +#include "reflection.h" + +#include "compiler.h" +#include "type-layout.h" + +#include <assert.h> + +// 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<BasicExpressionType>()) + { + return SLANG_TYPE_KIND_SCALAR; + } + else if (auto vectorType = type->As<VectorExpressionType>()) + { + return SLANG_TYPE_KIND_VECTOR; + } + else if (auto matrixType = type->As<MatrixExpressionType>()) + { + return SLANG_TYPE_KIND_MATRIX; + } + else if (auto constantBufferType = type->As<ConstantBufferType>()) + { + return SLANG_TYPE_KIND_CONSTANT_BUFFER; + } + else if (auto samplerStateType = type->As<SamplerStateType>()) + { + return SLANG_TYPE_KIND_SAMPLER_STATE; + } + else if (auto textureType = type->As<TextureType>()) + { + return SLANG_TYPE_KIND_RESOURCE; + } + + // TODO: need a better way to handle this stuff... +#define CASE(TYPE) \ + else if(type->As<TYPE>()) 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<ArrayExpressionType>()) + { + return SLANG_TYPE_KIND_ARRAY; + } + else if( auto declRefType = type->As<DeclRefType>() ) + { + auto declRef = declRefType->declRef; + if( auto structDeclRef = declRef.As<StructDeclRef>() ) + { + 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<DeclRefType*>(type)) + { + auto declRef = declRefType->declRef; + if( auto structDeclRef = declRef.As<StructDeclRef>()) + { + 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<DeclRefType*>(type)) + { + auto declRef = declRefType->declRef; + if( auto structDeclRef = declRef.As<StructDeclRef>()) + { + 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<ArrayExpressionType*>(type)) + { + return GetIntVal(arrayType->ArrayLength); + } + else if( auto vectorType = dynamic_cast<VectorExpressionType*>(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<ArrayExpressionType*>(type)) + { + return (SlangReflectionType*) arrayType->BaseType.Ptr(); + } + else if( auto constantBufferType = dynamic_cast<ConstantBufferType*>(type)) + { + return convert(constantBufferType->elementType.Ptr()); + } + else if( auto vectorType = dynamic_cast<VectorExpressionType*>(type)) + { + return convert(vectorType->elementType.Ptr()); + } + else if( auto matrixType = dynamic_cast<MatrixExpressionType*>(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<MatrixExpressionType*>(type)) + { + return GetIntVal(matrixType->getRowCount()); + } + else if(auto vectorType = dynamic_cast<VectorExpressionType*>(type)) + { + return 1; + } + else if( auto basicType = dynamic_cast<BasicExpressionType*>(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<MatrixExpressionType*>(type)) + { + return GetIntVal(matrixType->getColumnCount()); + } + else if(auto vectorType = dynamic_cast<VectorExpressionType*>(type)) + { + return GetIntVal(vectorType->elementCount); + } + else if( auto basicType = dynamic_cast<BasicExpressionType*>(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<MatrixExpressionType*>(type)) + { + type = matrixType->getElementType(); + } + else if(auto vectorType = dynamic_cast<VectorExpressionType*>(type)) + { + type = vectorType->elementType.Ptr(); + } + + if(auto basicType = dynamic_cast<BasicExpressionType*>(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<ArrayExpressionType>()) + { + type = arrayType->BaseType.Ptr(); + } + + if(auto textureType = type->As<TextureType>()) + { + return textureType->getShape(); + } + + // TODO: need a better way to handle this stuff... +#define CASE(TYPE, SHAPE, ACCESS) \ + else if(type->As<TYPE>()) 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<ArrayExpressionType>()) + { + type = arrayType->BaseType.Ptr(); + } + + if(auto textureType = type->As<TextureType>()) + { + return textureType->getAccess(); + } + + // TODO: need a better way to handle this stuff... +#define CASE(TYPE, SHAPE, ACCESS) \ + else if(type->As<TYPE>()) 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<ArrayExpressionType>()) + { + type = arrayType->BaseType.Ptr(); + } + + if (auto textureType = type->As<TextureType>()) + { + return convert(textureType->elementType.Ptr()); + } + + // TODO: need a better way to handle this stuff... +#define CASE(TYPE, SHAPE, ACCESS) \ + else if(type->As<TYPE>()) do { \ + return convert(type->As<TYPE>()->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<StructTypeLayout*>(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<ArrayTypeLayout*>(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<ArrayTypeLayout*>(typeLayout)) + { + return (SlangReflectionTypeLayout*) arrayTypeLayout->elementTypeLayout.Ptr(); + } + else if( auto constantBufferTypeLayout = dynamic_cast<ParameterBlockTypeLayout*>(typeLayout)) + { + return convert(constantBufferTypeLayout->elementTypeLayout.Ptr()); + } + else if( auto structuredBufferTypeLayout = dynamic_cast<StructuredBufferTypeLayout*>(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<ParameterBlockReflectionName>()) + 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<HLSLNumThreadsAttribute>(); + 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<ParameterBlockTypeLayout>()) + { + globalLayout = globalConstantBufferLayout->elementTypeLayout; + } + + if(auto globalStructLayout = globalLayout.As<StructTypeLayout>()) + { + 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<ParameterBlockTypeLayout>()) + { + globalLayout = globalConstantBufferLayout->elementTypeLayout; + } + + if(auto globalStructLayout = globalLayout.As<StructTypeLayout>()) + { + 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<typename T> +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<typename T> +Range<T> range(T begin, T end) +{ + return Range<T>(begin, end); +} + +template<typename T> +Range<T> range(T end) +{ + return Range<T>(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(); +} + +}} |
