https://github.com/shader-slang/slang
Tip revision: d06a78d935b2743494d47ed5cd3f36e38ac9c5ac authored by Yong He on 04 February 2022, 03:17:30 UTC
Add gfx interop to allow more direct D3D12 usage scenarios. (#2117)
Add gfx interop to allow more direct D3D12 usage scenarios. (#2117)
Tip revision: d06a78d
slang-reflection-api.cpp
// slang-reflection-api.cpp
#include "../core/slang-basic.h"
#include "slang-syntax.h"
#include "../../slang.h"
#include "slang-compiler.h"
#include "slang-type-layout.h"
#include "slang-syntax.h"
#include <assert.h>
// Don't signal errors for stuff we don't implement here,
// and instead just try to return things defensively
//
// Slang developers can switch this when debugging.
#define SLANG_REFLECTION_UNEXPECTED() do {} while(0)
namespace Slang
{
// Conversion routines to help with strongly-typed reflection API
static inline UserDefinedAttribute* convert(SlangReflectionUserAttribute* attrib)
{
return (UserDefinedAttribute*)attrib;
}
static inline SlangReflectionUserAttribute* convert(UserDefinedAttribute* attrib)
{
return (SlangReflectionUserAttribute*)attrib;
}
static inline Type* convert(SlangReflectionType* type)
{
return (Type*) type;
}
static inline SlangReflectionType* convert(Type* type)
{
return (SlangReflectionType*) type;
}
static inline TypeLayout* convert(SlangReflectionTypeLayout* type)
{
return (TypeLayout*) type;
}
static inline SlangReflectionTypeLayout* convert(TypeLayout* type)
{
return (SlangReflectionTypeLayout*) type;
}
static inline SpecializationParamLayout* convert(SlangReflectionTypeParameter * typeParam)
{
return (SpecializationParamLayout*) typeParam;
}
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;
}
// user attribute
static unsigned int getUserAttributeCount(Decl* decl)
{
unsigned int count = 0;
for (auto x : decl->getModifiersOfType<UserDefinedAttribute>())
{
SLANG_UNUSED(x);
count++;
}
return count;
}
static SlangReflectionUserAttribute* findUserAttributeByName(Session* session, Decl* decl, const char* name)
{
auto nameObj = session->tryGetNameObj(name);
for (auto x : decl->getModifiersOfType<UserDefinedAttribute>())
{
if (x->keywordName == nameObj)
return (SlangReflectionUserAttribute*)(x);
}
return nullptr;
}
static SlangReflectionUserAttribute* getUserAttributeByIndex(Decl* decl, unsigned int index)
{
unsigned int id = 0;
for (auto x : decl->getModifiersOfType<UserDefinedAttribute>())
{
if (id == index)
return convert(x);
id++;
}
return nullptr;
}
// Attempt "do what I mean" remapping from the parameter category the user asked about,
// over to a parameter category that they might have meant.
static SlangParameterCategory maybeRemapParameterCategory(
TypeLayout* typeLayout,
SlangParameterCategory category)
{
// Do we have an entry for the category they asked about? Then use that.
if (typeLayout->FindResourceInfo(LayoutResourceKind(category)))
return category;
// Do we have an entry for the `DescriptorTableSlot` category?
if (typeLayout->FindResourceInfo(LayoutResourceKind::DescriptorTableSlot))
{
// Is the category they were asking about one that makes sense for the type
// of this variable?
Type* type = typeLayout->getType();
while (auto arrayType = as<ArrayExpressionType>(type))
type = arrayType->baseType;
switch (spReflectionType_GetKind(convert(type)))
{
case SLANG_TYPE_KIND_CONSTANT_BUFFER:
if (category == SLANG_PARAMETER_CATEGORY_CONSTANT_BUFFER)
return SLANG_PARAMETER_CATEGORY_DESCRIPTOR_TABLE_SLOT;
break;
case SLANG_TYPE_KIND_RESOURCE:
if (category == SLANG_PARAMETER_CATEGORY_SHADER_RESOURCE)
return SLANG_PARAMETER_CATEGORY_DESCRIPTOR_TABLE_SLOT;
break;
case SLANG_TYPE_KIND_SAMPLER_STATE:
if (category == SLANG_PARAMETER_CATEGORY_SAMPLER_STATE)
return SLANG_PARAMETER_CATEGORY_DESCRIPTOR_TABLE_SLOT;
break;
// TODO: implement more helpers here
default:
break;
}
}
return category;
}
// Helpers for getting parameter count
static unsigned getParameterCount(RefPtr<TypeLayout> typeLayout)
{
if (auto parameterGroupLayout = as<ParameterGroupTypeLayout>(typeLayout))
{
typeLayout = parameterGroupLayout->offsetElementTypeLayout;
}
if (auto structLayout = as<StructTypeLayout>(typeLayout))
{
return (unsigned)structLayout->fields.getCount();
}
return 0;
}
static VarLayout* getParameterByIndex(RefPtr<TypeLayout> typeLayout, unsigned index)
{
if (auto parameterGroupLayout = as<ParameterGroupTypeLayout>(typeLayout))
{
typeLayout = parameterGroupLayout->offsetElementTypeLayout;
}
if (auto structLayout = as<StructTypeLayout>(typeLayout))
{
return structLayout->fields[index];
}
return 0;
}
static SlangParameterCategory getParameterCategory(
LayoutResourceKind kind)
{
return SlangParameterCategory(kind);
}
static SlangParameterCategory getParameterCategory(
TypeLayout* typeLayout)
{
auto resourceInfoCount = typeLayout->resourceInfos.getCount();
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;
}
static bool hasDefaultConstantBuffer(ScopeLayout* layout)
{
auto typeLayout = layout->parametersLayout->getTypeLayout();
return as<ParameterGroupTypeLayout>(typeLayout) != nullptr;
}
} // namespace Slang
using namespace Slang;
// Implementation to back public-facing reflection API
SLANG_API char const* spReflectionUserAttribute_GetName(SlangReflectionUserAttribute* attrib)
{
auto userAttr = convert(attrib);
if (!userAttr) return nullptr;
return userAttr->getKeywordName()->text.getBuffer();
}
SLANG_API unsigned int spReflectionUserAttribute_GetArgumentCount(SlangReflectionUserAttribute* attrib)
{
auto userAttr = convert(attrib);
if (!userAttr) return 0;
return (unsigned int)userAttr->args.getCount();
}
SlangReflectionType* spReflectionUserAttribute_GetArgumentType(SlangReflectionUserAttribute* attrib, unsigned int index)
{
auto userAttr = convert(attrib);
if (!userAttr) return nullptr;
return convert(userAttr->args[index]->type.type);
}
SLANG_API SlangResult spReflectionUserAttribute_GetArgumentValueInt(SlangReflectionUserAttribute* attrib, unsigned int index, int * rs)
{
auto userAttr = convert(attrib);
if (!userAttr) return SLANG_ERROR_INVALID_PARAMETER;
if (index >= (unsigned int)userAttr->args.getCount()) return SLANG_ERROR_INVALID_PARAMETER;
NodeBase* val = nullptr;
if (userAttr->intArgVals.TryGetValue(index, val))
{
*rs = (int)as<ConstantIntVal>(val)->value;
return 0;
}
return SLANG_ERROR_INVALID_PARAMETER;
}
SLANG_API SlangResult spReflectionUserAttribute_GetArgumentValueFloat(SlangReflectionUserAttribute* attrib, unsigned int index, float * rs)
{
auto userAttr = convert(attrib);
if (!userAttr) return SLANG_ERROR_INVALID_PARAMETER;
if (index >= (unsigned int)userAttr->args.getCount()) return SLANG_ERROR_INVALID_PARAMETER;
if (auto cexpr = as<FloatingPointLiteralExpr>(userAttr->args[index]))
{
*rs = (float)cexpr->value;
return 0;
}
return SLANG_ERROR_INVALID_PARAMETER;
}
SLANG_API const char* spReflectionUserAttribute_GetArgumentValueString(SlangReflectionUserAttribute* attrib, unsigned int index, size_t* bufLen)
{
auto userAttr = convert(attrib);
if (!userAttr) return nullptr;
if (index >= (unsigned int)userAttr->args.getCount()) return nullptr;
if (auto cexpr = as<StringLiteralExpr>(userAttr->args[index]))
{
if (bufLen)
*bufLen = cexpr->token.getContentLength();
return cexpr->token.getContent().begin();
}
return nullptr;
}
// 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 = as<BasicExpressionType>(type))
{
return SLANG_TYPE_KIND_SCALAR;
}
else if (auto vectorType = as<VectorExpressionType>(type))
{
return SLANG_TYPE_KIND_VECTOR;
}
else if (auto matrixType = as<MatrixExpressionType>(type))
{
return SLANG_TYPE_KIND_MATRIX;
}
else if (auto parameterBlockType = as<ParameterBlockType>(type))
{
return SLANG_TYPE_KIND_PARAMETER_BLOCK;
}
else if (auto constantBufferType = as<ConstantBufferType>(type))
{
return SLANG_TYPE_KIND_CONSTANT_BUFFER;
}
else if( auto streamOutputType = as<HLSLStreamOutputType>(type) )
{
return SLANG_TYPE_KIND_OUTPUT_STREAM;
}
else if (as<TextureBufferType>(type))
{
return SLANG_TYPE_KIND_TEXTURE_BUFFER;
}
else if (as<GLSLShaderStorageBufferType>(type))
{
return SLANG_TYPE_KIND_SHADER_STORAGE_BUFFER;
}
else if (auto samplerStateType = as<SamplerStateType>(type))
{
return SLANG_TYPE_KIND_SAMPLER_STATE;
}
else if (auto textureType = as<TextureTypeBase>(type))
{
return SLANG_TYPE_KIND_RESOURCE;
}
else if (auto feedbackType = as<FeedbackType>(type))
{
return SLANG_TYPE_KIND_FEEDBACK;
}
// TODO: need a better way to handle this stuff...
#define CASE(TYPE) \
else if(as<TYPE>(type)) do { \
return SLANG_TYPE_KIND_RESOURCE; \
} while(0)
CASE(HLSLStructuredBufferType);
CASE(HLSLRWStructuredBufferType);
CASE(HLSLRasterizerOrderedStructuredBufferType);
CASE(HLSLAppendStructuredBufferType);
CASE(HLSLConsumeStructuredBufferType);
CASE(HLSLByteAddressBufferType);
CASE(HLSLRWByteAddressBufferType);
CASE(HLSLRasterizerOrderedByteAddressBufferType);
CASE(UntypedBufferResourceType);
#undef CASE
else if (auto arrayType = as<ArrayExpressionType>(type))
{
return SLANG_TYPE_KIND_ARRAY;
}
else if( auto declRefType = as<DeclRefType>(type) )
{
const auto& declRef = declRefType->declRef;
if(declRef.is<StructDecl>() )
{
return SLANG_TYPE_KIND_STRUCT;
}
else if (declRef.is<GlobalGenericParamDecl>())
{
return SLANG_TYPE_KIND_GENERIC_TYPE_PARAMETER;
}
else if (declRef.is<InterfaceDecl>())
{
return SLANG_TYPE_KIND_INTERFACE;
}
else if (declRef.is<FuncDecl>())
{
// This is a reference to an entry point
return SLANG_TYPE_KIND_STRUCT;
}
}
else if( auto specializedType = as<ExistentialSpecializedType>(type) )
{
return SLANG_TYPE_KIND_SPECIALIZED;
}
else if (auto errorType = as<ErrorType>(type))
{
// This means we saw a type we didn't understand in the user's code
return SLANG_TYPE_KIND_NONE;
}
SLANG_REFLECTION_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 = as<DeclRefType>(type))
{
auto declRef = declRefType->declRef;
if( auto structDeclRef = declRef.as<StructDecl>())
{
return (unsigned int)getFields(structDeclRef, MemberFilterStyle::Instance).getCount();
}
}
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 = as<DeclRefType>(type))
{
auto declRef = declRefType->declRef;
if( auto structDeclRef = declRef.as<StructDecl>())
{
auto fields = getFields(structDeclRef, MemberFilterStyle::Instance);
auto fieldDeclRef = fields[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 = as<ArrayExpressionType>(type))
{
return arrayType->arrayLength ? (size_t) getIntVal(arrayType->arrayLength) : 0;
}
else if( auto vectorType = as<VectorExpressionType>(type))
{
return (size_t) getIntVal(vectorType->elementCount);
}
return 0;
}
SLANG_API SlangReflectionType* spReflectionType_GetElementType(SlangReflectionType* inType)
{
auto type = convert(inType);
if(!type) return nullptr;
if(auto arrayType = as<ArrayExpressionType>(type))
{
return (SlangReflectionType*) arrayType->baseType;
}
else if( auto parameterGroupType = as<ParameterGroupType>(type))
{
return convert(parameterGroupType->elementType);
}
else if (auto structuredBufferType = as<HLSLStructuredBufferTypeBase>(type))
{
return convert(structuredBufferType->elementType);
}
else if( auto vectorType = as<VectorExpressionType>(type))
{
return convert(vectorType->elementType);
}
else if( auto matrixType = as<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 = as<MatrixExpressionType>(type))
{
return (unsigned int) getIntVal(matrixType->getRowCount());
}
else if(auto vectorType = as<VectorExpressionType>(type))
{
return 1;
}
else if( auto basicType = as<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 = as<MatrixExpressionType>(type))
{
return (unsigned int) getIntVal(matrixType->getColumnCount());
}
else if(auto vectorType = as<VectorExpressionType>(type))
{
return (unsigned int) getIntVal(vectorType->elementCount);
}
else if( auto basicType = as<BasicExpressionType>(type) )
{
return 1;
}
return 0;
}
SLANG_API SlangScalarType spReflectionType_GetScalarType(SlangReflectionType* inType)
{
auto type = convert(inType);
if(!type) return 0;
if(auto matrixType = as<MatrixExpressionType>(type))
{
type = matrixType->getElementType();
}
else if(auto vectorType = as<VectorExpressionType>(type))
{
type = vectorType->elementType;
}
if(auto basicType = as<BasicExpressionType>(type))
{
switch (basicType->baseType)
{
#define CASE(BASE, TAG) \
case BaseType::BASE: return SLANG_SCALAR_TYPE_##TAG
CASE(Void, VOID);
CASE(Bool, BOOL);
CASE(Int8, INT8);
CASE(Int16, INT16);
CASE(Int, INT32);
CASE(Int64, INT64);
CASE(UInt8, UINT8);
CASE(UInt16, UINT16);
CASE(UInt, UINT32);
CASE(UInt64, UINT64);
CASE(Half, FLOAT16);
CASE(Float, FLOAT32);
CASE(Double, FLOAT64);
#undef CASE
default:
SLANG_REFLECTION_UNEXPECTED();
return SLANG_SCALAR_TYPE_NONE;
break;
}
}
return SLANG_SCALAR_TYPE_NONE;
}
SLANG_API unsigned int spReflectionType_GetUserAttributeCount(SlangReflectionType* inType)
{
auto type = convert(inType);
if (!type) return 0;
if (auto declRefType = as<DeclRefType>(type))
{
return getUserAttributeCount(declRefType->declRef.getDecl());
}
return 0;
}
SLANG_API SlangReflectionUserAttribute* spReflectionType_GetUserAttribute(SlangReflectionType* inType, unsigned int index)
{
auto type = convert(inType);
if (!type) return 0;
if (auto declRefType = as<DeclRefType>(type))
{
return getUserAttributeByIndex(declRefType->declRef.getDecl(), index);
}
return 0;
}
SLANG_API SlangReflectionUserAttribute* spReflectionType_FindUserAttributeByName(SlangReflectionType* inType, char const* name)
{
auto type = convert(inType);
if (!type) return 0;
if (auto declRefType = as<DeclRefType>(type))
{
ASTBuilder* astBuilder = declRefType->getASTBuilder();
auto globalSession = astBuilder->getGlobalSession();
return findUserAttributeByName(globalSession, declRefType->declRef.getDecl(), name);
}
return 0;
}
SLANG_API SlangResourceShape spReflectionType_GetResourceShape(SlangReflectionType* inType)
{
auto type = convert(inType);
if(!type) return 0;
while(auto arrayType = as<ArrayExpressionType>(type))
{
type = arrayType->baseType;
}
if(auto textureType = as<TextureTypeBase>(type))
{
return textureType->getShape();
}
// TODO: need a better way to handle this stuff...
#define CASE(TYPE, SHAPE, ACCESS) \
else if(as<TYPE>(type)) do { \
return SHAPE; \
} while(0)
CASE(HLSLStructuredBufferType, SLANG_STRUCTURED_BUFFER, SLANG_RESOURCE_ACCESS_READ);
CASE(HLSLRWStructuredBufferType, SLANG_STRUCTURED_BUFFER, SLANG_RESOURCE_ACCESS_READ_WRITE);
CASE(HLSLRasterizerOrderedStructuredBufferType, SLANG_STRUCTURED_BUFFER, SLANG_RESOURCE_ACCESS_RASTER_ORDERED);
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(HLSLRasterizerOrderedByteAddressBufferType, SLANG_BYTE_ADDRESS_BUFFER, SLANG_RESOURCE_ACCESS_RASTER_ORDERED);
CASE(RaytracingAccelerationStructureType, SLANG_ACCELERATION_STRUCTURE, SLANG_RESOURCE_ACCESS_READ);
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 = as<ArrayExpressionType>(type))
{
type = arrayType->baseType;
}
if(auto textureType = as<TextureTypeBase>(type))
{
return textureType->getAccess();
}
// TODO: need a better way to handle this stuff...
#define CASE(TYPE, SHAPE, ACCESS) \
else if(as<TYPE>(type)) do { \
return ACCESS; \
} while(0)
CASE(HLSLStructuredBufferType, SLANG_STRUCTURED_BUFFER, SLANG_RESOURCE_ACCESS_READ);
CASE(HLSLRWStructuredBufferType, SLANG_STRUCTURED_BUFFER, SLANG_RESOURCE_ACCESS_READ_WRITE);
CASE(HLSLRasterizerOrderedStructuredBufferType, SLANG_STRUCTURED_BUFFER, SLANG_RESOURCE_ACCESS_RASTER_ORDERED);
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(HLSLRasterizerOrderedByteAddressBufferType, SLANG_BYTE_ADDRESS_BUFFER, SLANG_RESOURCE_ACCESS_RASTER_ORDERED);
CASE(UntypedBufferResourceType, SLANG_BYTE_ADDRESS_BUFFER, SLANG_RESOURCE_ACCESS_READ);
// This isn't entirely accurate, but I can live with it for now
CASE(GLSLShaderStorageBufferType, SLANG_STRUCTURED_BUFFER, SLANG_RESOURCE_ACCESS_READ_WRITE);
#undef CASE
return SLANG_RESOURCE_ACCESS_NONE;
}
SLANG_API char const* spReflectionType_GetName(SlangReflectionType* inType)
{
auto type = convert(inType);
if( auto declRefType = as<DeclRefType>(type) )
{
auto declRef = declRefType->declRef;
// Don't return a name for auto-generated anonymous types
// that represent `cbuffer` members, etc.
auto decl = declRef.getDecl();
if(decl->hasModifier<ImplicitParameterGroupElementTypeModifier>())
return nullptr;
return getText(declRef.getName()).begin();
}
return nullptr;
}
SLANG_API SlangReflectionType * spReflection_FindTypeByName(SlangReflection * reflection, char const * name)
{
auto programLayout = convert(reflection);
auto program = programLayout->getProgram();
// TODO: We should extend this API to support getting error messages
// when type lookup fails.
//
Slang::DiagnosticSink sink(
programLayout->getTargetReq()->getLinkage()->getSourceManager(),
Lexer::sourceLocationLexer);
try
{
Type* result = program->getTypeFromString(name, &sink);
return (SlangReflectionType*)result;
}
catch( ... )
{
return nullptr;
}
}
SLANG_API SlangReflectionTypeLayout* spReflection_GetTypeLayout(
SlangReflection* reflection,
SlangReflectionType* inType,
SlangLayoutRules /*rules*/)
{
auto context = convert(reflection);
auto type = convert(inType);
auto targetReq = context->getTargetReq();
auto typeLayout = targetReq->getTypeLayout(type);
return convert(typeLayout);
}
SLANG_API SlangReflectionType* spReflectionType_GetResourceResultType(SlangReflectionType* inType)
{
auto type = convert(inType);
if(!type) return nullptr;
while(auto arrayType = as<ArrayExpressionType>(type))
{
type = arrayType->baseType;
}
if (auto textureType = as<TextureTypeBase>(type))
{
return convert(textureType->elementType);
}
// TODO: need a better way to handle this stuff...
#define CASE(TYPE, SHAPE, ACCESS) \
else if(as<TYPE>(type)) do { \
return convert(as<TYPE>(type)->elementType); \
} while(0)
// 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(HLSLRasterizerOrderedStructuredBufferType, SLANG_STRUCTURED_BUFFER, SLANG_RESOURCE_ACCESS_RASTER_ORDERED);
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;
}
SLANG_API SlangTypeKind spReflectionTypeLayout_getKind(SlangReflectionTypeLayout* inTypeLayout)
{
if(!inTypeLayout) return SLANG_TYPE_KIND_NONE;
if( auto type = spReflectionTypeLayout_GetType(inTypeLayout) )
{
return spReflectionType_GetKind(type);
}
auto typeLayout = convert(inTypeLayout);
if( as<StructTypeLayout>(typeLayout) )
{
return SLANG_TYPE_KIND_STRUCT;
}
else if( as<ParameterGroupTypeLayout>(typeLayout) )
{
return SLANG_TYPE_KIND_CONSTANT_BUFFER;
}
return SLANG_TYPE_KIND_NONE;
}
namespace
{
static size_t getReflectionSize(LayoutSize size)
{
if(size.isFinite())
return size.getFiniteValue();
return SLANG_UNBOUNDED_SIZE;
}
static int32_t getAlignment(TypeLayout* typeLayout, SlangParameterCategory category)
{
if( category == SLANG_PARAMETER_CATEGORY_UNIFORM )
{
return int32_t(typeLayout->uniformAlignment);
}
else
{
return 1;
}
}
static size_t getStride(TypeLayout* typeLayout, SlangParameterCategory category)
{
auto info = typeLayout->FindResourceInfo(LayoutResourceKind(category));
if(!info) return 0;
auto size = info->count;
if(size.isInfinite())
return SLANG_UNBOUNDED_SIZE;
size_t finiteSize = size.getFiniteValue();
size_t alignment = getAlignment(typeLayout, category);
SLANG_ASSERT(alignment >= 1);
auto stride = (finiteSize + (alignment-1)) & ~(alignment-1);
return stride;
}
}
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 getReflectionSize(info->count);
}
SLANG_API size_t spReflectionTypeLayout_GetStride(SlangReflectionTypeLayout* inTypeLayout, SlangParameterCategory category)
{
auto typeLayout = convert(inTypeLayout);
if(!typeLayout) return 0;
return getStride(typeLayout, category);
}
SLANG_API int32_t spReflectionTypeLayout_getAlignment(SlangReflectionTypeLayout* inTypeLayout, SlangParameterCategory category)
{
auto typeLayout = convert(inTypeLayout);
if(!typeLayout) return 0;
return getAlignment(typeLayout, category);
}
SLANG_API SlangReflectionVariableLayout* spReflectionTypeLayout_GetFieldByIndex(SlangReflectionTypeLayout* inTypeLayout, unsigned index)
{
auto typeLayout = convert(inTypeLayout);
if(!typeLayout) return nullptr;
if(auto structTypeLayout = as<StructTypeLayout>(typeLayout))
{
return (SlangReflectionVariableLayout*) structTypeLayout->fields[index].Ptr();
}
return nullptr;
}
SLANG_API SlangInt spReflectionTypeLayout_findFieldIndexByName(SlangReflectionTypeLayout* inTypeLayout, const char* nameBegin, const char* nameEnd)
{
auto typeLayout = convert(inTypeLayout);
if(!typeLayout) return -1;
UnownedStringSlice name = nameEnd != nullptr ? UnownedStringSlice(nameBegin, nameEnd) : UnownedTerminatedStringSlice(nameBegin);
if(auto structTypeLayout = as<StructTypeLayout>(typeLayout))
{
Index fieldCount = structTypeLayout->fields.getCount();
for(Index f = 0; f < fieldCount; ++f)
{
auto field = structTypeLayout->fields[f];
if(getReflectionName(field->varDecl)->text.getUnownedSlice() == name)
return f;
}
}
return -1;
}
SLANG_API size_t spReflectionTypeLayout_GetElementStride(SlangReflectionTypeLayout* inTypeLayout, SlangParameterCategory category)
{
auto typeLayout = convert(inTypeLayout);
if(!typeLayout) return 0;
if( auto arrayTypeLayout = as<ArrayTypeLayout>(typeLayout))
{
switch (category)
{
// We store the stride explicitly for the uniform case
case SLANG_PARAMETER_CATEGORY_UNIFORM:
return arrayTypeLayout->uniformStride;
// For most other cases (resource registers), the "stride"
// of an array is simply the number of resources (if any)
// consumed by its element type.
default:
{
auto elementTypeLayout = arrayTypeLayout->elementTypeLayout;
auto info = elementTypeLayout->FindResourceInfo(LayoutResourceKind(category));
if(!info) return 0;
return getReflectionSize(info->count);
}
// An important special case, though, is Vulkan descriptor-table slots,
// where an entire array will use a single `binding`, so that the
// effective stride is zero:
case SLANG_PARAMETER_CATEGORY_DESCRIPTOR_TABLE_SLOT:
return 0;
}
}
else if (auto vectorTypeLayout = as<VectorTypeLayout>(typeLayout))
{
auto resInfo = vectorTypeLayout->elementTypeLayout->FindResourceInfo(LayoutResourceKind::Uniform);
if (!resInfo) return 0;
return resInfo->count.getFiniteValue();
}
return 0;
}
SLANG_API SlangReflectionTypeLayout* spReflectionTypeLayout_GetElementTypeLayout(SlangReflectionTypeLayout* inTypeLayout)
{
auto typeLayout = convert(inTypeLayout);
if(!typeLayout) return nullptr;
if( auto arrayTypeLayout = as<ArrayTypeLayout>(typeLayout))
{
return (SlangReflectionTypeLayout*) arrayTypeLayout->elementTypeLayout.Ptr();
}
else if( auto constantBufferTypeLayout = as<ParameterGroupTypeLayout>(typeLayout))
{
return convert(constantBufferTypeLayout->offsetElementTypeLayout.Ptr());
}
else if( auto structuredBufferTypeLayout = as<StructuredBufferTypeLayout>(typeLayout))
{
return convert(structuredBufferTypeLayout->elementTypeLayout.Ptr());
}
else if( auto specializedTypeLayout = as<ExistentialSpecializedTypeLayout>(typeLayout) )
{
return convert(specializedTypeLayout->baseTypeLayout.Ptr());
}
else if (auto vectorTypeLayout = as<VectorTypeLayout>(typeLayout))
{
return convert(vectorTypeLayout->elementTypeLayout);
}
else if (auto matrixTypeLayout = as<MatrixTypeLayout>(typeLayout))
{
return convert(matrixTypeLayout->elementTypeLayout);
}
return nullptr;
}
SLANG_API SlangReflectionVariableLayout* spReflectionTypeLayout_GetElementVarLayout(SlangReflectionTypeLayout* inTypeLayout)
{
auto typeLayout = convert(inTypeLayout);
if(!typeLayout) return nullptr;
if( auto parameterGroupTypeLayout = as<ParameterGroupTypeLayout>(typeLayout))
{
return convert(parameterGroupTypeLayout->elementVarLayout.Ptr());
}
return nullptr;
}
SLANG_API SlangReflectionVariableLayout* spReflectionTypeLayout_getContainerVarLayout(SlangReflectionTypeLayout* inTypeLayout)
{
auto typeLayout = convert(inTypeLayout);
if(!typeLayout) return nullptr;
if( auto parameterGroupTypeLayout = as<ParameterGroupTypeLayout>(typeLayout))
{
return convert(parameterGroupTypeLayout->containerVarLayout.Ptr());
}
return nullptr;
}
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.getCount();
}
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;
}
SLANG_API SlangMatrixLayoutMode spReflectionTypeLayout_GetMatrixLayoutMode(SlangReflectionTypeLayout* inTypeLayout)
{
auto typeLayout = convert(inTypeLayout);
if(!typeLayout) return SLANG_MATRIX_LAYOUT_MODE_UNKNOWN;
if( auto matrixLayout = as<MatrixTypeLayout>(typeLayout) )
{
return matrixLayout->mode;
}
else
{
return SLANG_MATRIX_LAYOUT_MODE_UNKNOWN;
}
}
SLANG_API int spReflectionTypeLayout_getGenericParamIndex(SlangReflectionTypeLayout* inTypeLayout)
{
auto typeLayout = convert(inTypeLayout);
if(!typeLayout) return -1;
if(auto genericParamTypeLayout = as<GenericParamTypeLayout>(typeLayout))
{
return (int) genericParamTypeLayout->paramIndex;
}
else
{
return -1;
}
}
SLANG_API SlangReflectionTypeLayout* spReflectionTypeLayout_getPendingDataTypeLayout(SlangReflectionTypeLayout* inTypeLayout)
{
auto typeLayout = convert(inTypeLayout);
if(!typeLayout) return nullptr;
auto pendingDataTypeLayout = typeLayout->pendingDataTypeLayout.Ptr();
return convert(pendingDataTypeLayout);
}
SLANG_API SlangReflectionVariableLayout* spReflectionVariableLayout_getPendingDataLayout(SlangReflectionVariableLayout* inVarLayout)
{
auto varLayout = convert(inVarLayout);
if(!varLayout) return nullptr;
auto pendingDataLayout = varLayout->pendingVarLayout.Ptr();
return convert(pendingDataLayout);
}
SLANG_API SlangReflectionVariableLayout* spReflectionTypeLayout_getSpecializedTypePendingDataVarLayout(SlangReflectionTypeLayout* inTypeLayout)
{
auto typeLayout = convert(inTypeLayout);
if(!typeLayout) return nullptr;
if( auto specializedTypeLayout = as<ExistentialSpecializedTypeLayout>(typeLayout) )
{
auto pendingDataVarLayout = specializedTypeLayout->pendingDataVarLayout.Ptr();
return convert(pendingDataVarLayout);
}
else
{
return nullptr;
}
}
SLANG_API SlangInt spReflectionType_getSpecializedTypeArgCount(SlangReflectionType* inType)
{
auto type = convert(inType);
if(!type) return 0;
auto specializedType = as<ExistentialSpecializedType>(type);
if(!specializedType) return 0;
return specializedType->args.getCount();
}
SLANG_API SlangReflectionType* spReflectionType_getSpecializedTypeArgType(SlangReflectionType* inType, SlangInt index)
{
auto type = convert(inType);
if(!type) return nullptr;
auto specializedType = as<ExistentialSpecializedType>(type);
if(!specializedType) return nullptr;
if(index < 0) return nullptr;
if(index >= specializedType->args.getCount()) return nullptr;
auto argType = as<Type>(specializedType->args[index].val);
return convert(argType);
}
namespace Slang
{
/// A link in a chain of `VarLayout`s that can be used to compute offset information for a nested field
struct BindingRangePathLink
{
BindingRangePathLink()
{}
BindingRangePathLink(
BindingRangePathLink* parent,
VarLayout* var)
: var(var)
, parent(parent)
{}
/// The inner-most variable that contributes to the offset along this path
VarLayout* var = nullptr;
/// The next outer link along the path
BindingRangePathLink* parent = nullptr;
};
/// A path leading to some nested field, with both parimary and "pending" data offsets
struct BindingRangePath
{
/// The chain of variables that defines the "primary" offset of a nested field
BindingRangePathLink* primary = nullptr;
/// The chain of variables that defines the offset for "pending" data of a nested field
BindingRangePathLink* pending = nullptr;
};
/// A helper type to construct a `BindingRangePath` that extends an existing path
struct ExtendedBindingRangePath : BindingRangePath
{
/// Construct a path that extends `parent` with offset information from `varLayout`
ExtendedBindingRangePath(
BindingRangePath const& parent,
VarLayout* varLayout)
{
SLANG_ASSERT(varLayout);
// We always add another link to the primary chain.
//
primaryLink = BindingRangePathLink(parent.primary, varLayout);
primary = &primaryLink;
// If the `varLayout` provided has any offset information
// for pending data, then we also add a link to the pending
// chain, but otherwise we re-use the pending chain from
// the parent path.
//
if(auto pendingLayout = varLayout->pendingVarLayout)
{
pendingLink = BindingRangePathLink(parent.pending, pendingLayout);
pending = &pendingLink;
}
else
{
pending = parent.pending;
}
}
/// Storage for a link in the primary chain, if needed
BindingRangePathLink primaryLink;
/// Storage for a link in the pending chain, if needed
BindingRangePathLink pendingLink;
};
/// Calculate the offset for resources of the given `kind` in the `path`.
Int _calcIndexOffset(BindingRangePathLink* path, LayoutResourceKind kind)
{
Int result = 0;
for( auto link = path; link; link = link->parent )
{
if( auto resInfo = link->var->FindResourceInfo(kind) )
{
result += resInfo->index;
}
}
return result;
}
/// Calculate the regsiter space / set for resources of the given `kind` in the `path`.
Int _calcSpaceOffset(BindingRangePathLink* path, LayoutResourceKind kind)
{
Int result = 0;
for( auto link = path; link; link = link->parent )
{
if( auto resInfo = link->var->FindResourceInfo(kind) )
{
result += resInfo->space;
}
}
return result;
}
SlangBindingType _calcResourceBindingType(
Type* type)
{
if(auto combinedTextureSamplerType = as<TextureSamplerType>(type))
{
return SLANG_BINDING_TYPE_COMBINED_TEXTURE_SAMPLER;
}
else if( auto resourceType = as<ResourceType>(type) )
{
auto shape = resourceType->getBaseShape();
auto access = resourceType->getAccess();
auto mutableFlag = access != SLANG_RESOURCE_ACCESS_READ ? SLANG_BINDING_TYPE_MUTABLE_FLAG : 0;
switch( shape )
{
default:
return SLANG_BINDING_TYPE_TEXTURE | mutableFlag;
case SLANG_TEXTURE_BUFFER:
return SLANG_BINDING_TYPE_TYPED_BUFFER | mutableFlag;
}
}
else if( auto structuredBufferType = as<HLSLStructuredBufferTypeBase>(type) )
{
if( as<HLSLStructuredBufferType>(type) )
{
return SLANG_BINDING_TYPE_RAW_BUFFER;
}
else
{
return SLANG_BINDING_TYPE_MUTABLE_RAW_BUFFER;
}
}
else if( as<RaytracingAccelerationStructureType>(type) )
{
return SLANG_BINDING_TYPE_RAY_TRACTING_ACCELERATION_STRUCTURE;
}
else if( auto untypedBufferType = as<UntypedBufferResourceType>(type) )
{
if( as<HLSLByteAddressBufferType>(type) )
{
return SLANG_BINDING_TYPE_RAW_BUFFER;
}
else
{
return SLANG_BINDING_TYPE_MUTABLE_RAW_BUFFER;
}
}
else if( as<ConstantBufferType>(type) )
{
return SLANG_BINDING_TYPE_CONSTANT_BUFFER;
}
else if( as<SamplerStateType>(type) )
{
return SLANG_BINDING_TYPE_SAMPLER;
}
else if (as<ParameterBlockType>(type))
{
return SLANG_BINDING_TYPE_PARAMETER_BLOCK;
}
else
{
return SLANG_BINDING_TYPE_UNKNOWN;
}
}
SlangBindingType _calcResourceBindingType(
TypeLayout* typeLayout)
{
if(auto type = typeLayout->getType())
{
return _calcResourceBindingType(type);
}
if(as<ParameterGroupTypeLayout>(typeLayout))
{
return SLANG_BINDING_TYPE_CONSTANT_BUFFER;
}
else
{
return SLANG_BINDING_TYPE_UNKNOWN;
}
}
SlangBindingType _calcBindingType(
LayoutResourceKind kind)
{
switch( kind )
{
default:
return SLANG_BINDING_TYPE_UNKNOWN;
// Some cases of `LayoutResourceKind` can be mapped
// directly to a `BindingType` because there is only
// one case of types that have that resource kind.
#define CASE(FROM, TO) \
case LayoutResourceKind::FROM: return SLANG_BINDING_TYPE_##TO
CASE(ConstantBuffer, CONSTANT_BUFFER);
CASE(SamplerState, SAMPLER);
CASE(VaryingInput, VARYING_INPUT);
CASE(VaryingOutput, VARYING_OUTPUT);
CASE(ExistentialObjectParam, EXISTENTIAL_VALUE);
CASE(PushConstantBuffer, PUSH_CONSTANT);
CASE(Uniform, INLINE_UNIFORM_DATA);
// TODO: register space
#undef CASE
}
}
SlangBindingType _calcBindingType(
Slang::TypeLayout* typeLayout,
LayoutResourceKind kind)
{
// At the type level, a push-constant buffer and a regular constant
// buffer are currently not distinct, so we need to detect push
// constant buffers/ranges before we inspect the `typeLayout` to
// avoid reflecting them all as ordinary constant buffers.
//
switch(kind)
{
default:
break;
case LayoutResourceKind::PushConstantBuffer:
return SLANG_BINDING_TYPE_PUSH_CONSTANT;
}
// If the type or type layout implies a specific binding type
// (e.g., a `Texture2D` implies a texture binding), then we
// will always favor the binding type implied.
//
if( auto bindingType = _calcResourceBindingType(typeLayout) )
{
if(bindingType != SLANG_BINDING_TYPE_UNKNOWN)
return bindingType;
}
// As a fallback, we may look at the kind of resources consumed
// by a type layout, and use that to infer the type of binding
// used. Note that, for example, a `float4` might represent
// multiple different kinds of binding, depending on where/how
// it is used (e.g., as a varying parameter, a root constant, etc.).
//
return _calcBindingType(kind);
}
static DeclRefType* asInterfaceType(Type* type)
{
if(auto declRefType = as<DeclRefType>(type))
{
if(declRefType->declRef.as<InterfaceDecl>())
{
return declRefType;
}
}
return nullptr;
}
struct ExtendedTypeLayoutContext
{
TypeLayout* m_typeLayout;
TypeLayout::ExtendedInfo* m_extendedInfo;
Dictionary<Int, Int> m_mapSpaceToDescriptorSetIndex;
Int _findOrAddDescriptorSet(Int space)
{
Int index = 0;
if(m_mapSpaceToDescriptorSetIndex.TryGetValue(space, index))
return index;
index = m_extendedInfo->m_descriptorSets.getCount();
m_mapSpaceToDescriptorSetIndex.Add(space, index);
RefPtr<TypeLayout::ExtendedInfo::DescriptorSetInfo> descriptorSet = new TypeLayout::ExtendedInfo::DescriptorSetInfo();
m_extendedInfo->m_descriptorSets.add(descriptorSet);
return index;
}
/// Create a single `VarLayout` for `typeLayout` that summarizes all of the offset information in `path`.
///
/// Note: This function does not handle "pending" layout information.
RefPtr<VarLayout> _createSimpleOffsetVarLayout(TypeLayout* typeLayout, BindingRangePathLink* path)
{
SLANG_ASSERT(typeLayout);
RefPtr<VarLayout> varLayout = new VarLayout();
varLayout->typeLayout = typeLayout;
varLayout->typeLayout.demoteToWeakReference();
for(auto typeResInfo : typeLayout->resourceInfos)
{
auto kind = typeResInfo.kind;
auto varResInfo = varLayout->findOrAddResourceInfo(kind);
varResInfo->index = _calcIndexOffset(path, kind);
varResInfo->space = _calcSpaceOffset(path, kind);
}
return varLayout;
}
/// Create a single `VarLayout` for `typeLayout` that summarizes all of the offset information in `path`.
RefPtr<VarLayout> createOffsetVarLayout(TypeLayout* typeLayout, BindingRangePath const& path)
{
auto primaryVarLayout = _createSimpleOffsetVarLayout(typeLayout, path.primary);
SLANG_ASSERT(primaryVarLayout);
if(auto pendingDataTypeLayout = typeLayout->pendingDataTypeLayout)
{
primaryVarLayout->pendingVarLayout = _createSimpleOffsetVarLayout(pendingDataTypeLayout, path.pending);
}
return primaryVarLayout;
}
void addRangesRec(TypeLayout* typeLayout, BindingRangePath const& path, LayoutSize multiplier)
{
if( auto structTypeLayout = as<StructTypeLayout>(typeLayout) )
{
// For a structure type, we need to recursively
// add the ranges for each field.
//
// Along the way we will make sure to properly update
// the offset information on the fields so that
// they properly show their binding-range offset
// within the parent type.
//
Index structBindingRangeIndex = m_extendedInfo->m_bindingRanges.getCount();
for( auto fieldVarLayout : structTypeLayout->fields )
{
Index fieldBindingRangeIndex = m_extendedInfo->m_bindingRanges.getCount();
fieldVarLayout->bindingRangeOffset = fieldBindingRangeIndex - structBindingRangeIndex;
auto fieldTypeLayout = fieldVarLayout->getTypeLayout();
ExtendedBindingRangePath fieldPath(path, fieldVarLayout);
addRangesRec(fieldTypeLayout, fieldPath, multiplier);
}
return;
}
else if( auto arrayTypeLayout = as<ArrayTypeLayout>(typeLayout) )
{
// For an array, we need to recursively add the
// element type of the array, but with an adjusted
// `multiplier` to account for the element count.
//
auto elementTypeLayout = arrayTypeLayout->elementTypeLayout;
LayoutSize elementCount = LayoutSize::infinite();
if( auto arrayType = as<ArrayExpressionType>(arrayTypeLayout->type) )
{
if( auto elementCountVal = arrayType->arrayLength )
{
elementCount = LayoutSize::RawValue(getIntVal(elementCountVal));
}
}
addRangesRec(elementTypeLayout, path, multiplier * elementCount);
return;
}
else if( auto parameterGroupTypeLayout = as<ParameterGroupTypeLayout>(typeLayout))
{
// A parameter group (whether a `ConstantBuffer<>` or `ParameterBlock<>`
// introduces a separately-allocated "sub-object" in the application's
// layout for shader objects.
//
// We will represent the parameter group with a single sub-object
// binding range (and an associated sub-object range).
//
// We start out by looking at the resources consumed by the parameter group
// itself, to determine what kind of binding range to report it as.
//
Index bindingRangeIndex = m_extendedInfo->m_bindingRanges.getCount();
SlangBindingType bindingType = SLANG_BINDING_TYPE_CONSTANT_BUFFER;
Index spaceOffset = -1;
bool shouldAllocDescriptorSet = true;
LayoutResourceKind kind = LayoutResourceKind::None;
// TODO: It is unclear if this should be looking at the resource
// usage of the parameter group, or of its "container" layout.
//
for(auto& resInfo : parameterGroupTypeLayout->resourceInfos)
{
if( spaceOffset == -1 )
{
spaceOffset = _calcSpaceOffset(path.primary, kind);
}
kind = resInfo.kind;
switch(kind)
{
default:
continue;
case LayoutResourceKind::ConstantBuffer:
case LayoutResourceKind::PushConstantBuffer:
case LayoutResourceKind::DescriptorTableSlot:
break;
// Certain cases indicate a parameter block that
// actually involves indirection.
//
// Note: the only case where a parameter group should
// reflect as consuming `Uniform` storage is on CPU/CUDA,
// where that will be the only resource it contains.
case LayoutResourceKind::Uniform:
break;
//
// TODO: If we ever support targets that don't have
// constant buffers at all, this logic would be questionable.
//
case LayoutResourceKind::RegisterSpace:
shouldAllocDescriptorSet = false;
break;
}
bindingType = _calcBindingType(typeLayout, kind);
break;
}
if(spaceOffset == -1)
{
spaceOffset = 0;
}
TypeLayout::ExtendedInfo::BindingRangeInfo bindingRange;
bindingRange.leafTypeLayout = typeLayout;
bindingRange.bindingType = bindingType;
bindingRange.count = multiplier;
bindingRange.descriptorSetIndex = -1;
bindingRange.firstDescriptorRangeIndex = 0;
bindingRange.descriptorRangeCount = 0;
// Every parameter group will introduce a sub-object range,
// which will include bindings based on the type of data
// inside the sub-object.
//
TypeLayout::ExtendedInfo::SubObjectRangeInfo subObjectRange;
subObjectRange.bindingRangeIndex = bindingRangeIndex;
subObjectRange.offsetVarLayout = createOffsetVarLayout(typeLayout, path);
subObjectRange.spaceOffset = 0;
if (kind == LayoutResourceKind::RegisterSpace && path.primary)
{
auto resInfo = path.primary->var->FindResourceInfo(LayoutResourceKind::RegisterSpace);
subObjectRange.spaceOffset = resInfo->index;
}
// It is possible that the sub-object has descriptor ranges
// that will need to be exposed upward, into the parent.
//
// Note: it is a subtle point, but we are only going to expose
// *descriptor ranges* upward and not *binding ranges*. The
// distinction here comes down to:
//
// * Descriptor ranges are used to describe the entries that
// must be allocated in one or more API descriptor sets to
// physically hold a value of a given type (layout).
//
// * Binding ranges are used to describe the entries that must
// be allocated in an application shader object to logically
// hold a value of a given type (layout).
//
// In practice, a binding range might logically belong to a
// sub-object, but physically belong to a parent. Consider:
//
// cbuffer C { Texture2D a; float b; }
//
// Independent of the API we compile for, we expect the global
// scope to have a sub-object for `C`, and for that sub-object
// to have a binding range for `a` (that is, we bind the texture
// into the sub-object).
//
// When compiling for D3D12 or Vulkan, we expect that the global
// scope must have two descriptor ranges for `C`: one for the
// constant buffer itself, and another for the texture `a`.
// The reason for this is that `a` needs to be bound as part
// of a descriptor set, and `C` doesn't create/allocate its own
// descriptor set(s).
//
// When compiling for CPU or CUDA, we expect that the global scope
// will have a descriptor range for `C` but *not* one for `C.a`,
// because the physical storage for `C.a` is provided by the
// memory allocation for `C` itself.
if (shouldAllocDescriptorSet)
{
// The logic here assumes that when a parameter group consumes
// resources that must "leak" into the outer scope (including
// reosurces consumed by the group "container"), those resources
// will amount to descriptor ranges that are part of the same
// descriptor set.
//
// (If the contents of a group consume whole spaces/sets, then
// those resources will be accounted for separately).
//
Int descriptorSetIndex = _findOrAddDescriptorSet(spaceOffset);
auto descriptorSet = m_extendedInfo->m_descriptorSets[descriptorSetIndex];
auto firstDescriptorRangeIndex = descriptorSet->descriptorRanges.getCount();
// First, we need to deal with any descriptor ranges that are
// introduced by the "container" type itself.
//
switch(kind)
{
// If the parameter group was allocated to consume one or
// more whole register spaces/sets, then nothing should
// leak through that is measured in descriptor sets.
//
case LayoutResourceKind::RegisterSpace:
case LayoutResourceKind::None:
break;
default:
{
// In a constant-buffer-like case, then all the (non-space/set) resource
// usage of the "container" should be reflected as descriptor
// ranges in the parent scope.
//
for(auto resInfo : parameterGroupTypeLayout->containerVarLayout->typeLayout->resourceInfos)
{
switch( resInfo.kind )
{
case LayoutResourceKind::RegisterSpace:
continue;
default:
break;
}
TypeLayout::ExtendedInfo::DescriptorRangeInfo descriptorRange;
descriptorRange.kind = resInfo.kind;
descriptorRange.bindingType = _calcBindingType(typeLayout, resInfo.kind);
descriptorRange.count = multiplier;
descriptorRange.indexOffset = _calcIndexOffset(path.primary, resInfo.kind);
descriptorSet->descriptorRanges.add(descriptorRange);
}
}
}
// Second, we need to consider resource usage from the "element"
// type that might leak through to the parent.
//
switch(kind)
{
// If the parameter group was allocated as a full register space/set,
// *or* if it was allocated as ordinary uniform storage (likely
// because it was compiled for CPU/CUDA), then there should
// be no "leakage" of descriptor ranges from the element type
// to the parent.
//
case LayoutResourceKind::RegisterSpace:
case LayoutResourceKind::Uniform:
case LayoutResourceKind::None:
break;
default:
{
// If we are in the constant-buffer-like case, on an API
// where constant bufers "leak" resource usage to the
// outer context, then we need to add the descriptor ranges
// implied by the element type.
//
// HACK: We enumerate these nested ranges by recurisvely
// calling `addRangesRec`, which adds all of descriptor ranges,
// binding ranges, and sub-object ranges, and then we trim
// the lists we don't actually care about as a post-process.
//
// TODO: We could try to consider a model where we first
// query the extended layout information of the element
// type (which might already be cached) and then enumerate
// the descriptor ranges and copy them over.
//
// TODO: It is possible that there could be cases where
// some, but not all, of the nested descriptor ranges ought
// to be enumerated here. In that case we might have to introduce
// a kind of "mask" parameter that is passed down into
// the recursive call so that only the appropriate ranges
// get added.
// We need to add a link to the "path" that is used when looking
// up binding information, to ensure that the descriptor ranges
// that get enumerated here have correct register/binding offsets.
//
ExtendedBindingRangePath elementPath(path, parameterGroupTypeLayout->elementVarLayout);
Index bindingRangeCountBefore = m_extendedInfo->m_bindingRanges.getCount();
Index subObjectRangeCountBefore = m_extendedInfo->m_subObjectRanges.getCount();
addRangesRec(parameterGroupTypeLayout->elementVarLayout->typeLayout, elementPath, multiplier);
m_extendedInfo->m_bindingRanges.setCount(bindingRangeCountBefore);
m_extendedInfo->m_subObjectRanges.setCount(subObjectRangeCountBefore);
}
break;
}
auto descriptorRangeCount = descriptorSet->descriptorRanges.getCount() - firstDescriptorRangeIndex;
bindingRange.descriptorSetIndex = descriptorSetIndex;
bindingRange.firstDescriptorRangeIndex = firstDescriptorRangeIndex;
bindingRange.descriptorRangeCount = descriptorRangeCount;
}
m_extendedInfo->m_bindingRanges.add(bindingRange);
m_extendedInfo->m_subObjectRanges.add(subObjectRange);
return;
}
else if(asInterfaceType(typeLayout->type))
{
// An `interface` type should introduce a binding range and a matching
// sub-object range.
//
TypeLayout::ExtendedInfo::BindingRangeInfo bindingRange;
bindingRange.leafTypeLayout = typeLayout;
bindingRange.bindingType = SLANG_BINDING_TYPE_EXISTENTIAL_VALUE;
bindingRange.count = multiplier;
bindingRange.descriptorSetIndex = 0;
bindingRange.descriptorRangeCount = 0;
bindingRange.firstDescriptorRangeIndex = 0;
TypeLayout::ExtendedInfo::SubObjectRangeInfo subObjectRange;
subObjectRange.bindingRangeIndex = m_extendedInfo->m_bindingRanges.getCount();
subObjectRange.offsetVarLayout = createOffsetVarLayout(typeLayout, path);
m_extendedInfo->m_bindingRanges.add(bindingRange);
m_extendedInfo->m_subObjectRanges.add(subObjectRange);
}
else
{
// Here we have the catch-all case that handles "leaf" fields
// that might need to introduce a binding range and descriptor
// ranges.
//
// First, we want to determine what type of binding this
// leaf field should map to, if any. We being by querying
// the type itself, since there are many distinct descriptor
// types for textures/buffers that can only be determined
// by type, rather than by a `LayoutResourceKind`.
//
auto bindingType = _calcResourceBindingType(typeLayout);
// It is possible that the type alone isn't enough to tell
// us a specific binding type, at which point we need to
// start looking at the actual resources the type layout
// consumes.
//
if(bindingType == SLANG_BINDING_TYPE_UNKNOWN)
{
// We will search through all the resource kinds that
// the type layout consumes, to see if we can find
// one that indicates a binding type we actually
// want to reflect.
//
for( auto resInfo : typeLayout->resourceInfos )
{
auto kind = resInfo.kind;
if(kind == LayoutResourceKind::Uniform)
continue;
auto kindBindingType = _calcBindingType(kind);
if(kindBindingType == SLANG_BINDING_TYPE_UNKNOWN)
continue;
// If we find a relevant binding type based on
// one of the resource kinds that are consumed,
// then we immediately stop the search and use
// the first one found (whether or not later
// entries might also provide something relevant).
//
bindingType = kindBindingType;
break;
}
}
// After we've tried to determine a binding type, if
// we have nothing to go on then we don't want to add
// a binding range.
//
if(bindingType == SLANG_BINDING_TYPE_UNKNOWN)
return;
// We now know that the leaf field will map to a single binding range,
// and zero or more descriptor ranges.
//
TypeLayout::ExtendedInfo::BindingRangeInfo bindingRange;
bindingRange.leafTypeLayout = typeLayout;
bindingRange.bindingType = bindingType;
bindingRange.count = multiplier;
bindingRange.descriptorSetIndex = 0;
bindingRange.firstDescriptorRangeIndex = 0;
bindingRange.descriptorRangeCount = 0;
// We will associate the binding range with a specific descriptor
// set on demand *if* we discover that it shold contain any
// descriptor ranges.
//
RefPtr<TypeLayout::ExtendedInfo::DescriptorSetInfo> descriptorSet;
// We will add a descriptor range for each relevant resource kind
// that the type layout consumes.
//
for(auto resInfo : typeLayout->resourceInfos)
{
auto kind = resInfo.kind;
switch( kind )
{
default:
break;
// There are many resource kinds that we do not want
// to expose as descriptor ranges simply because they
// do not actually allocate descriptors on our target
// APIs.
//
// Notably included here are uniform/ordinary data and
// varying input/output (including the ray-tracing cases).
//
// It is worth noting that we *do* allow root/push-constant
// ranges to be reflected as "descriptor" ranges here,
// despite the fact that they are not descriptor-bound
// under D3D12/Vulkan.
//
// In practice, even with us filtering out some cases here,
// an application/renderer layer will need to filter/translate
// or descriptor ranges into API-specific ones, and a one-to-one
// mapping should not be assumed.
//
// TODO: Make some clear decisions about what should and should
// not appear here.
//
case LayoutResourceKind::RegisterSpace:
case LayoutResourceKind::VaryingInput:
case LayoutResourceKind::VaryingOutput:
case LayoutResourceKind::HitAttributes:
case LayoutResourceKind::RayPayload:
case LayoutResourceKind::ExistentialTypeParam:
case LayoutResourceKind::ExistentialObjectParam:
continue;
}
// We will prefer to use a binding type derived from the specific
// resource kind, but will fall back to information from the
// type layout when that is not available.
//
// TODO: This logic probably needs a bit more work to handle
// the case of a combined texture-sampler field that is being
// compiled for an API with separate textures and samplers.
//
auto kindBindingType = _calcBindingType(kind);
if( kindBindingType == SLANG_BINDING_TYPE_UNKNOWN )
{
kindBindingType = bindingType;
}
// We now expect to allocate a descriptor range for this
// `resInfo` representing resouce usage.
//
auto count = resInfo.count * multiplier;
auto indexOffset = _calcIndexOffset(path.primary, kind);
auto spaceOffset = _calcSpaceOffset(path.primary, kind);
TypeLayout::ExtendedInfo::DescriptorRangeInfo descriptorRange;
descriptorRange.kind = kind;
descriptorRange.bindingType = kindBindingType;
descriptorRange.count = count;
descriptorRange.indexOffset = indexOffset;
if(!descriptorSet)
{
Int descriptorSetIndex = _findOrAddDescriptorSet(spaceOffset);
descriptorSet = m_extendedInfo->m_descriptorSets[descriptorSetIndex];
bindingRange.descriptorSetIndex = descriptorSetIndex;
bindingRange.firstDescriptorRangeIndex = descriptorSet->descriptorRanges.getCount();
}
descriptorSet->descriptorRanges.add(descriptorRange);
bindingRange.descriptorRangeCount++;
}
auto bindingRangeIndex = m_extendedInfo->m_bindingRanges.getCount();
m_extendedInfo->m_bindingRanges.add(bindingRange);
// For `StructuredBuffer` fields, we also make sure to report it as a sub-object range.
if (auto structuredBufferTypeLayout = as<StructuredBufferTypeLayout>(typeLayout))
{
TypeLayout::ExtendedInfo::SubObjectRangeInfo subObjectRange;
subObjectRange.bindingRangeIndex = bindingRangeIndex;
subObjectRange.offsetVarLayout = createOffsetVarLayout(typeLayout, path);
subObjectRange.spaceOffset = 0;
m_extendedInfo->m_subObjectRanges.add(subObjectRange);
}
}
}
};
TypeLayout::ExtendedInfo* getExtendedTypeLayout(TypeLayout* typeLayout)
{
if( !typeLayout->m_extendedInfo )
{
RefPtr<TypeLayout::ExtendedInfo> extendedInfo = new TypeLayout::ExtendedInfo;
ExtendedTypeLayoutContext context;
context.m_typeLayout = typeLayout;
context.m_extendedInfo = extendedInfo;
BindingRangePath rootPath;
context.addRangesRec(typeLayout, rootPath, 1);
typeLayout->m_extendedInfo = extendedInfo;
}
return typeLayout->m_extendedInfo;
}
}
SLANG_API SlangInt spReflectionTypeLayout_getBindingRangeCount(SlangReflectionTypeLayout* inTypeLayout)
{
auto typeLayout = convert(inTypeLayout);
if(!typeLayout) return 0;
auto extTypeLayout = Slang::getExtendedTypeLayout(typeLayout);
return extTypeLayout->m_bindingRanges.getCount();
}
SLANG_API SlangBindingType spReflectionTypeLayout_getBindingRangeType(SlangReflectionTypeLayout* inTypeLayout, SlangInt index)
{
auto typeLayout = convert(inTypeLayout);
if(!typeLayout) return SLANG_BINDING_TYPE_UNKNOWN;
auto extTypeLayout = Slang::getExtendedTypeLayout(typeLayout);
if(index < 0) return 0;
if(index >= extTypeLayout->m_bindingRanges.getCount()) return 0;
auto& bindingRange = extTypeLayout->m_bindingRanges[index];
return bindingRange.bindingType;
}
SLANG_API SlangInt spReflectionTypeLayout_getBindingRangeBindingCount(SlangReflectionTypeLayout* inTypeLayout, SlangInt index)
{
auto typeLayout = convert(inTypeLayout);
if(!typeLayout) return 0;
auto extTypeLayout = Slang::getExtendedTypeLayout(typeLayout);
if(index < 0) return 0;
if(index >= extTypeLayout->m_bindingRanges.getCount()) return 0;
auto& bindingRange = extTypeLayout->m_bindingRanges[index];
auto count = bindingRange.count;
return count.isFinite() ? SlangInt(count.getFiniteValue()) : -1;
}
#if 0
SLANG_API SlangInt spReflectionTypeLayout_getBindingRangeIndexOffset(SlangReflectionTypeLayout* inTypeLayout, SlangInt index)
{
auto typeLayout = convert(inTypeLayout);
if(!typeLayout) return 0;
return Slang::_findBindingRange(typeLayout, index).indexOffset;
}
SLANG_API SlangInt spReflectionTypeLayout_getBindingRangeSpaceOffset(SlangReflectionTypeLayout* inTypeLayout, SlangInt index)
{
auto typeLayout = convert(inTypeLayout);
if(!typeLayout) return 0;
return Slang::_findBindingRange(typeLayout, index).spaceOffset;
}
#endif
SLANG_API SlangReflectionTypeLayout* spReflectionTypeLayout_getBindingRangeLeafTypeLayout(SlangReflectionTypeLayout* inTypeLayout, SlangInt index)
{
auto typeLayout = convert(inTypeLayout);
if(!typeLayout) return 0;
auto extTypeLayout = Slang::getExtendedTypeLayout(typeLayout);
if(index < 0) return 0;
if(index >= extTypeLayout->m_bindingRanges.getCount()) return 0;
auto& bindingRange = extTypeLayout->m_bindingRanges[index];
return convert(bindingRange.leafTypeLayout);
}
SLANG_API SlangInt spReflectionTypeLayout_getBindingRangeDescriptorSetIndex(SlangReflectionTypeLayout* inTypeLayout, SlangInt index)
{
auto typeLayout = convert(inTypeLayout);
if(!typeLayout) return 0;
auto extTypeLayout = Slang::getExtendedTypeLayout(typeLayout);
if(index < 0) return 0;
if(index >= extTypeLayout->m_bindingRanges.getCount()) return 0;
auto& bindingRange = extTypeLayout->m_bindingRanges[index];
return bindingRange.descriptorSetIndex;
}
SLANG_API SlangInt spReflectionTypeLayout_getBindingRangeFirstDescriptorRangeIndex(SlangReflectionTypeLayout* inTypeLayout, SlangInt index)
{
auto typeLayout = convert(inTypeLayout);
if(!typeLayout) return 0;
auto extTypeLayout = Slang::getExtendedTypeLayout(typeLayout);
if(index < 0) return 0;
if(index >= extTypeLayout->m_bindingRanges.getCount()) return 0;
auto& bindingRange = extTypeLayout->m_bindingRanges[index];
return bindingRange.firstDescriptorRangeIndex;
}
SLANG_API SlangInt spReflectionTypeLayout_getBindingRangeDescriptorRangeCount(SlangReflectionTypeLayout* inTypeLayout, SlangInt index)
{
auto typeLayout = convert(inTypeLayout);
if(!typeLayout) return 0;
auto extTypeLayout = Slang::getExtendedTypeLayout(typeLayout);
if(index < 0) return 0;
if(index >= extTypeLayout->m_bindingRanges.getCount()) return 0;
auto& bindingRange = extTypeLayout->m_bindingRanges[index];
return bindingRange.descriptorRangeCount;
}
SLANG_API SlangInt spReflectionTypeLayout_getDescriptorSetCount(SlangReflectionTypeLayout* inTypeLayout)
{
auto typeLayout = convert(inTypeLayout);
if(!typeLayout) return 0;
auto extTypeLayout = Slang::getExtendedTypeLayout(typeLayout);
return extTypeLayout->m_descriptorSets.getCount();
}
SLANG_API SlangInt spReflectionTypeLayout_getDescriptorSetSpaceOffset(SlangReflectionTypeLayout* inTypeLayout, SlangInt setIndex)
{
auto typeLayout = convert(inTypeLayout);
if(!typeLayout) return 0;
auto extTypeLayout = Slang::getExtendedTypeLayout(typeLayout);
if(setIndex < 0) return 0;
if(setIndex >= extTypeLayout->m_descriptorSets.getCount()) return 0;
auto descriptorSet = extTypeLayout->m_descriptorSets[setIndex];
return descriptorSet->spaceOffset;
}
SLANG_API SlangInt spReflectionTypeLayout_getDescriptorSetDescriptorRangeCount(SlangReflectionTypeLayout* inTypeLayout, SlangInt setIndex)
{
auto typeLayout = convert(inTypeLayout);
if(!typeLayout) return 0;
auto extTypeLayout = Slang::getExtendedTypeLayout(typeLayout);
if(setIndex < 0) return 0;
if(setIndex >= extTypeLayout->m_descriptorSets.getCount()) return 0;
auto descriptorSet = extTypeLayout->m_descriptorSets[setIndex];
return descriptorSet->descriptorRanges.getCount();
}
SLANG_API SlangInt spReflectionTypeLayout_getDescriptorSetDescriptorRangeIndexOffset(SlangReflectionTypeLayout* inTypeLayout, SlangInt setIndex, SlangInt rangeIndex)
{
auto typeLayout = convert(inTypeLayout);
if(!typeLayout) return 0;
auto extTypeLayout = Slang::getExtendedTypeLayout(typeLayout);
if(setIndex < 0) return 0;
if(setIndex >= extTypeLayout->m_descriptorSets.getCount()) return 0;
auto descriptorSet = extTypeLayout->m_descriptorSets[setIndex];
if(rangeIndex < 0) return 0;
if(rangeIndex >= descriptorSet->descriptorRanges.getCount()) return 0;
auto& range = descriptorSet->descriptorRanges[rangeIndex];
return range.indexOffset;
}
SLANG_API SlangInt spReflectionTypeLayout_getDescriptorSetDescriptorRangeDescriptorCount(SlangReflectionTypeLayout* inTypeLayout, SlangInt setIndex, SlangInt rangeIndex)
{
auto typeLayout = convert(inTypeLayout);
if(!typeLayout) return 0;
auto extTypeLayout = Slang::getExtendedTypeLayout(typeLayout);
if(setIndex < 0) return 0;
if(setIndex >= extTypeLayout->m_descriptorSets.getCount()) return 0;
auto descriptorSet = extTypeLayout->m_descriptorSets[setIndex];
if(rangeIndex < 0) return 0;
if(rangeIndex >= descriptorSet->descriptorRanges.getCount()) return 0;
auto& range = descriptorSet->descriptorRanges[rangeIndex];
auto count = range.count;
return count.isFinite() ? count.getFiniteValue() : -1;
}
SLANG_API SlangBindingType spReflectionTypeLayout_getDescriptorSetDescriptorRangeType(SlangReflectionTypeLayout* inTypeLayout, SlangInt setIndex, SlangInt rangeIndex)
{
auto typeLayout = convert(inTypeLayout);
if(!typeLayout) return 0;
auto extTypeLayout = Slang::getExtendedTypeLayout(typeLayout);
if(setIndex < 0) return 0;
if(setIndex >= extTypeLayout->m_descriptorSets.getCount()) return 0;
auto descriptorSet = extTypeLayout->m_descriptorSets[setIndex];
if(rangeIndex < 0) return 0;
if(rangeIndex >= descriptorSet->descriptorRanges.getCount()) return 0;
auto& range = descriptorSet->descriptorRanges[rangeIndex];
return range.bindingType;
}
SLANG_API SlangParameterCategory spReflectionTypeLayout_getDescriptorSetDescriptorRangeCategory(SlangReflectionTypeLayout* inTypeLayout, SlangInt setIndex, SlangInt rangeIndex)
{
auto typeLayout = convert(inTypeLayout);
if(!typeLayout) return 0;
auto extTypeLayout = Slang::getExtendedTypeLayout(typeLayout);
if(setIndex < 0) return 0;
if(setIndex >= extTypeLayout->m_descriptorSets.getCount()) return 0;
auto descriptorSet = extTypeLayout->m_descriptorSets[setIndex];
if(rangeIndex < 0) return 0;
if(rangeIndex >= descriptorSet->descriptorRanges.getCount()) return 0;
auto& range = descriptorSet->descriptorRanges[rangeIndex];
return SlangParameterCategory(range.kind);
}
SLANG_API SlangInt spReflectionTypeLayout_getSubObjectRangeCount(SlangReflectionTypeLayout* inTypeLayout)
{
auto typeLayout = convert(inTypeLayout);
if(!typeLayout) return 0;
auto extTypeLayout = Slang::getExtendedTypeLayout(typeLayout);
return extTypeLayout->m_subObjectRanges.getCount();
}
SLANG_API SlangInt spReflectionTypeLayout_getSubObjectRangeBindingRangeIndex(SlangReflectionTypeLayout* inTypeLayout, SlangInt subObjectRangeIndex)
{
auto typeLayout = convert(inTypeLayout);
if(!typeLayout) return 0;
auto extTypeLayout = Slang::getExtendedTypeLayout(typeLayout);
if(subObjectRangeIndex < 0) return 0;
if(subObjectRangeIndex >= extTypeLayout->m_subObjectRanges.getCount()) return 0;
return extTypeLayout->m_subObjectRanges[subObjectRangeIndex].bindingRangeIndex;
}
SLANG_API SlangInt spReflectionTypeLayout_getSubObjectRangeSpaceOffset(
SlangReflectionTypeLayout* inTypeLayout,
SlangInt subObjectRangeIndex)
{
auto typeLayout = convert(inTypeLayout);
if (!typeLayout)
return 0;
auto extTypeLayout = Slang::getExtendedTypeLayout(typeLayout);
if (subObjectRangeIndex < 0)
return 0;
if (subObjectRangeIndex >= extTypeLayout->m_subObjectRanges.getCount())
return 0;
return extTypeLayout->m_subObjectRanges[subObjectRangeIndex].spaceOffset;
}
SLANG_API SlangReflectionVariableLayout* spReflectionTypeLayout_getSubObjectRangeOffset(
SlangReflectionTypeLayout* inTypeLayout,
SlangInt subObjectRangeIndex)
{
auto typeLayout = convert(inTypeLayout);
if (!typeLayout)
return 0;
auto extTypeLayout = Slang::getExtendedTypeLayout(typeLayout);
if (subObjectRangeIndex < 0)
return 0;
if (subObjectRangeIndex >= extTypeLayout->m_subObjectRanges.getCount())
return 0;
return convert(extTypeLayout->m_subObjectRanges[subObjectRangeIndex].offsetVarLayout);
}
#if 0
SLANG_API SlangInt spReflectionTypeLayout_getBindingRangeSubObjectRangeIndex(SlangReflectionTypeLayout* inTypeLayout, SlangInt index)
{
auto typeLayout = convert(inTypeLayout);
if(!typeLayout) return 0;
return Slang::_findBindingRange(typeLayout, index).subObjectRangeIndex;
}
#endif
SLANG_API SlangInt spReflectionTypeLayout_getFieldBindingRangeOffset(SlangReflectionTypeLayout* inTypeLayout, SlangInt fieldIndex)
{
auto typeLayout = convert(inTypeLayout);
if(!typeLayout) return 0;
if( auto structTypeLayout = as<StructTypeLayout>(typeLayout) )
{
getExtendedTypeLayout(structTypeLayout);
return structTypeLayout->fields[fieldIndex]->bindingRangeOffset;
}
return 0;
}
#if 0
SLANG_API SlangInt spReflectionTypeLayout_getSubObjectRangeCount(SlangReflectionTypeLayout* inTypeLayout)
{
auto typeLayout = convert(inTypeLayout);
if(!typeLayout) return 0;
return Slang::_calcSubObjectRangeCount(typeLayout);
}
SLANG_API SlangInt spReflectionTypeLayout_getSubObjectRangeObjectCount(SlangReflectionTypeLayout* inTypeLayout, SlangInt index)
{
auto typeLayout = convert(inTypeLayout);
if(!typeLayout) return 0;
auto count = Slang::_findSubObjectRange(typeLayout, index).count;
return count.isFinite() ? SlangInt(count.getFiniteValue()) : -1;
}
SLANG_API SlangInt spReflectionTypeLayout_getSubObjectRangeBindingRangeIndex(SlangReflectionTypeLayout* inTypeLayout, SlangInt index)
{
auto typeLayout = convert(inTypeLayout);
if(!typeLayout) return 0;
return Slang::_findSubObjectRange(typeLayout, index).bindingRangeIndex;
}
SLANG_API SlangReflectionTypeLayout* spReflectionTypeLayout_getSubObjectRangeTypeLayout(SlangReflectionTypeLayout* inTypeLayout, SlangInt index)
{
auto typeLayout = convert(inTypeLayout);
if(!typeLayout) return 0;
return convert(Slang::_findSubObjectRange(typeLayout, index).leafTypeLayout);
}
SLANG_API SlangInt spReflectionTypeLayout_getSubObjectRangeDescriptorRangeCount(SlangReflectionTypeLayout* inTypeLayout, SlangInt subObjectRangeIndex)
{
auto typeLayout = convert(inTypeLayout);
if(!typeLayout) return 0;
auto subObjectRange = Slang::_findSubObjectRange(typeLayout, subObjectRangeIndex);
return Slang::_getSubObjectDescriptorRangeCount(subObjectRange);
}
SLANG_API SlangBindingType spReflectionTypeLayout_getSubObjectRangeDescriptorRangeBindingType(SlangReflectionTypeLayout* inTypeLayout, SlangInt subObjectRangeIndex, SlangInt bindingRangeIndexInSubObject)
{
auto typeLayout = convert(inTypeLayout);
if(!typeLayout) return 0;
auto subObjectRange = Slang::_findSubObjectRange(typeLayout, subObjectRangeIndex);
return Slang::_getSubObjectDescriptorRange(subObjectRange, bindingRangeIndexInSubObject).bindingType;
}
SLANG_API SlangInt spReflectionTypeLayout_getSubObjectRangeDescriptorRangeBindingCount(SlangReflectionTypeLayout* inTypeLayout, SlangInt subObjectRangeIndex, SlangInt bindingRangeIndexInSubObject)
{
auto typeLayout = convert(inTypeLayout);
if(!typeLayout) return 0;
auto subObjectRange = Slang::_findSubObjectRange(typeLayout, subObjectRangeIndex);
auto count = Slang::_getSubObjectDescriptorRange(subObjectRange, bindingRangeIndexInSubObject).count;
return count.isFinite() ? count.getFiniteValue() : -1;
}
SLANG_API SlangInt spReflectionTypeLayout_getSubObjectRangeDescriptorRangeIndexOffset(SlangReflectionTypeLayout* inTypeLayout, SlangInt subObjectRangeIndex, SlangInt bindingRangeIndexInSubObject)
{
auto typeLayout = convert(inTypeLayout);
if(!typeLayout) return 0;
auto subObjectRange = Slang::_findSubObjectRange(typeLayout, subObjectRangeIndex);
return Slang::_getSubObjectDescriptorRange(subObjectRange, bindingRangeIndexInSubObject).indexOffset;
}
SLANG_API SlangInt spReflectionTypeLayout_getSubObjectRangeDescriptorRangeSpaceOffset(SlangReflectionTypeLayout* inTypeLayout, SlangInt subObjectRangeIndex, SlangInt bindingRangeIndexInSubObject)
{
auto typeLayout = convert(inTypeLayout);
if(!typeLayout) return 0;
auto subObjectRange = Slang::_findSubObjectRange(typeLayout, subObjectRangeIndex);
return Slang::_getSubObjectDescriptorRange(subObjectRange, bindingRangeIndexInSubObject).spaceOffset;
}
#endif
// 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<ParameterGroupReflectionName>())
return getText(reflectionNameMod->nameAndLoc.name).getBuffer();
return getText(var->getName()).getBuffer();
}
SLANG_API SlangReflectionType* spReflectionVariable_GetType(SlangReflectionVariable* inVar)
{
auto var = convert(inVar);
if(!var) return nullptr;
return convert(var->getType());
}
SLANG_API SlangReflectionModifier* spReflectionVariable_FindModifier(SlangReflectionVariable* inVar, SlangModifierID modifierID)
{
auto var = convert(inVar);
if(!var) return nullptr;
Modifier* modifier = nullptr;
switch( modifierID )
{
case SLANG_MODIFIER_SHARED:
modifier = var->findModifier<HLSLEffectSharedModifier>();
break;
default:
return nullptr;
}
return (SlangReflectionModifier*) modifier;
}
SLANG_API unsigned int spReflectionVariable_GetUserAttributeCount(SlangReflectionVariable* inVar)
{
auto varDecl = convert(inVar);
if (!varDecl) return 0;
return getUserAttributeCount(varDecl);
}
SLANG_API SlangReflectionUserAttribute* spReflectionVariable_GetUserAttribute(SlangReflectionVariable* inVar, unsigned int index)
{
auto varDecl = convert(inVar);
if (!varDecl) return 0;
return getUserAttributeByIndex(varDecl, index);
}
SLANG_API SlangReflectionUserAttribute* spReflectionVariable_FindUserAttributeByName(SlangReflectionVariable* inVar, SlangSession* session, char const* name)
{
auto varDecl = convert(inVar);
if (!varDecl) return 0;
return findUserAttributeByName(asInternal(session), varDecl, name);
}
// 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)
{
// No match with requested category? Try again with one they might have meant...
category = maybeRemapParameterCategory(varLayout->getTypeLayout(), category);
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)
{
// No match with requested category? Try again with one they might have meant...
category = maybeRemapParameterCategory(varLayout->getTypeLayout(), category);
info = varLayout->FindResourceInfo(LayoutResourceKind(category));
}
UInt space = 0;
// First, deal with any offset applied to the specific resource kind specified
if (info)
{
space += info->space;
}
// Note: this code used to try and take a variable with
// an offset for `LayoutResourceKind::RegisterSpace` and
// add it to the space returned, but that isn't going
// to be right in some cases.
//
// Imageine if we have:
//
// struct X { Texture2D y; }
// struct S { Texture2D t; ParmaeterBlock<X> x; }
//
// Texture2D gA;
// S gS;
//
// We expect `gS` to have an offset for `LayoutResourceKind::ShaderResourceView`
// of one (since its texture must come after `gA`), and an offset for
// `LayoutResourceKind::RegisterSpace` of one (since the default space will be
// space zero). It would be incorrect for us to imply that `gS.t` should
// be `t1, space1`, though, because the space offset of `gS` doesn't actually
// apply to `t`.
//
// For now we are punting on this issue and leaving it in the hands of the
// application to determine when a space offset from an "outer" variable should
// apply to the locations of things in an "inner" variable.
//
// There is no policy we can apply locally in this function that
// will Just Work, so the best we can do is try to not lie.
return space;
}
SLANG_API char const* spReflectionVariableLayout_GetSemanticName(SlangReflectionVariableLayout* inVarLayout)
{
auto varLayout = convert(inVarLayout);
if(!varLayout) return 0;
if (!(varLayout->flags & Slang::VarLayoutFlag::HasSemantic))
return 0;
return varLayout->semanticName.getBuffer();
}
SLANG_API size_t spReflectionVariableLayout_GetSemanticIndex(SlangReflectionVariableLayout* inVarLayout)
{
auto varLayout = convert(inVarLayout);
if(!varLayout) return 0;
if (!(varLayout->flags & Slang::VarLayoutFlag::HasSemantic))
return 0;
return varLayout->semanticIndex;
}
SLANG_API SlangStage spReflectionVariableLayout_getStage(
SlangReflectionVariableLayout* inVarLayout)
{
auto varLayout = convert(inVarLayout);
if(!varLayout) return SLANG_STAGE_NONE;
// A parameter that is not a varying input or output is
// not considered to belong to a single stage.
//
// TODO: We might need to reconsider this for, e.g., entry
// point parameters, where they might be stage-specific even
// if they are uniform.
if (!varLayout->FindResourceInfo(Slang::LayoutResourceKind::VaryingInput)
&& !varLayout->FindResourceInfo(Slang::LayoutResourceKind::VaryingOutput))
{
return SLANG_STAGE_NONE;
}
// TODO: We should find the stage for a variable layout by
// walking up the tree of layout information, until we find
// something that has a definitive stage attached to it (e.g.,
// either an entry point or a GLSL translation unit).
//
// We don't currently have parent links in the reflection layout
// information, so doing that walk would be tricky right now, so
// it is easier to just bloat the representation and store yet another
// field on every variable layout.
return (SlangStage) varLayout->stage;
}
// Shader Parameter Reflection
SLANG_API unsigned spReflectionParameter_GetBindingIndex(SlangReflectionParameter* inVarLayout)
{
SlangReflectionVariableLayout* varLayout = (SlangReflectionVariableLayout*)inVarLayout;
return (unsigned) spReflectionVariableLayout_GetOffset(
varLayout,
spReflectionTypeLayout_GetParameterCategory(
spReflectionVariableLayout_GetTypeLayout(varLayout)));
}
SLANG_API unsigned spReflectionParameter_GetBindingSpace(SlangReflectionParameter* inVarLayout)
{
SlangReflectionVariableLayout* varLayout = (SlangReflectionVariableLayout*)inVarLayout;
return (unsigned) spReflectionVariableLayout_GetSpace(
varLayout,
spReflectionTypeLayout_GetParameterCategory(
spReflectionVariableLayout_GetTypeLayout(varLayout)));
}
// Entry Point Reflection
SLANG_API char const* spReflectionEntryPoint_getName(
SlangReflectionEntryPoint* inEntryPoint)
{
auto entryPointLayout = convert(inEntryPoint);
return entryPointLayout ? getCstr(entryPointLayout->name) : nullptr;
}
SLANG_API unsigned spReflectionEntryPoint_getParameterCount(
SlangReflectionEntryPoint* inEntryPoint)
{
auto entryPointLayout = convert(inEntryPoint);
if(!entryPointLayout) return 0;
return getParameterCount(entryPointLayout->parametersLayout->typeLayout);
}
SLANG_API SlangReflectionVariableLayout* spReflectionEntryPoint_getParameterByIndex(
SlangReflectionEntryPoint* inEntryPoint,
unsigned index)
{
auto entryPointLayout = convert(inEntryPoint);
if(!entryPointLayout) return 0;
return convert(getParameterByIndex(entryPointLayout->parametersLayout->typeLayout, index));
}
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;
SlangUInt sizeAlongAxis[3] = { 1, 1, 1 };
// First look for the HLSL case, where we have an attribute attached to the entry point function
auto numThreadsAttribute = entryPointFunc.getDecl()->findModifier<NumThreadsAttribute>();
if (numThreadsAttribute)
{
sizeAlongAxis[0] = numThreadsAttribute->x;
sizeAlongAxis[1] = numThreadsAttribute->y;
sizeAlongAxis[2] = numThreadsAttribute->z;
}
//
if(axisCount > 0) outSizeAlongAxis[0] = sizeAlongAxis[0];
if(axisCount > 1) outSizeAlongAxis[1] = sizeAlongAxis[1];
if(axisCount > 2) outSizeAlongAxis[2] = sizeAlongAxis[2];
for( SlangUInt aa = 3; aa < axisCount; ++aa )
{
outSizeAlongAxis[aa] = 1;
}
}
SLANG_API int spReflectionEntryPoint_usesAnySampleRateInput(
SlangReflectionEntryPoint* inEntryPoint)
{
auto entryPointLayout = convert(inEntryPoint);
if(!entryPointLayout)
return 0;
if (entryPointLayout->profile.getStage() != Stage::Fragment)
return 0;
return (entryPointLayout->flags & EntryPointLayout::Flag::usesAnySampleRateInput) != 0;
}
SLANG_API SlangReflectionVariableLayout* spReflectionEntryPoint_getVarLayout(
SlangReflectionEntryPoint* inEntryPoint)
{
auto entryPointLayout = convert(inEntryPoint);
if(!entryPointLayout)
return nullptr;
return convert(entryPointLayout->parametersLayout);
}
SLANG_API SlangReflectionVariableLayout* spReflectionEntryPoint_getResultVarLayout(
SlangReflectionEntryPoint* inEntryPoint)
{
auto entryPointLayout = convert(inEntryPoint);
if(!entryPointLayout)
return nullptr;
return convert(entryPointLayout->resultLayout);
}
SLANG_API int spReflectionEntryPoint_hasDefaultConstantBuffer(
SlangReflectionEntryPoint* inEntryPoint)
{
auto entryPointLayout = convert(inEntryPoint);
if(!entryPointLayout)
return 0;
return hasDefaultConstantBuffer(entryPointLayout);
}
// SlangReflectionTypeParameter
SLANG_API char const* spReflectionTypeParameter_GetName(SlangReflectionTypeParameter * inTypeParam)
{
auto specializationParam = convert(inTypeParam);
if( auto genericParamLayout = as<GenericSpecializationParamLayout>(specializationParam) )
{
return genericParamLayout->decl->getName()->text.getBuffer();
}
// TODO: Add case for existential type parameter? They don't have as simple of a notion of "name" as the generic case...
return nullptr;
}
SLANG_API unsigned spReflectionTypeParameter_GetIndex(SlangReflectionTypeParameter * inTypeParam)
{
auto typeParam = convert(inTypeParam);
return (unsigned)(typeParam->index);
}
SLANG_API unsigned int spReflectionTypeParameter_GetConstraintCount(SlangReflectionTypeParameter* inTypeParam)
{
auto specializationParam = convert(inTypeParam);
if(auto genericParamLayout = as<GenericSpecializationParamLayout>(specializationParam))
{
if( auto globalGenericParamDecl = as<GlobalGenericParamDecl>(genericParamLayout->decl) )
{
auto constraints = globalGenericParamDecl->getMembersOfType<GenericTypeConstraintDecl>();
return (unsigned int)constraints.getCount();
}
// TODO: Add case for entry-point generic parameters.
}
// TODO: Add case for existential type parameters.
return 0;
}
SLANG_API SlangReflectionType* spReflectionTypeParameter_GetConstraintByIndex(SlangReflectionTypeParameter * inTypeParam, unsigned index)
{
auto specializationParam = convert(inTypeParam);
if(auto genericParamLayout = as<GenericSpecializationParamLayout>(specializationParam))
{
if( auto globalGenericParamDecl = as<GlobalGenericParamDecl>(genericParamLayout->decl) )
{
auto constraints = globalGenericParamDecl->getMembersOfType<GenericTypeConstraintDecl>();
return (SlangReflectionType*)constraints[index]->sup.Ptr();
}
// TODO: Add case for entry-point generic parameters.
}
// TODO: Add case for existential type parameters.
return 0;
}
// Shader Reflection
SLANG_API unsigned spReflection_GetParameterCount(SlangReflection* inProgram)
{
auto program = convert(inProgram);
if(!program) return 0;
auto globalStructLayout = getGlobalStructLayout(program);
if (!globalStructLayout)
return 0;
return (unsigned) globalStructLayout->fields.getCount();
}
SLANG_API SlangReflectionParameter* spReflection_GetParameterByIndex(SlangReflection* inProgram, unsigned index)
{
auto program = convert(inProgram);
if(!program) return nullptr;
auto globalStructLayout = getGlobalStructLayout(program);
if (!globalStructLayout)
return 0;
return convert(globalStructLayout->fields[index].Ptr());
}
SLANG_API SlangReflectionVariableLayout* spReflection_getGlobalParamsVarLayout(SlangReflection* inProgram)
{
auto program = convert(inProgram);
if(!program) return nullptr;
return convert(program->parametersLayout);
}
SLANG_API unsigned int spReflection_GetTypeParameterCount(SlangReflection * reflection)
{
auto program = convert(reflection);
return (unsigned int) program->specializationParams.getCount();
}
SLANG_API SlangReflectionTypeParameter* spReflection_GetTypeParameterByIndex(SlangReflection * reflection, unsigned int index)
{
auto program = convert(reflection);
return (SlangReflectionTypeParameter*) program->specializationParams[index].Ptr();
}
SLANG_API SlangReflectionTypeParameter * spReflection_FindTypeParameter(SlangReflection * inProgram, char const * name)
{
auto program = convert(inProgram);
if (!program) return nullptr;
for( auto& param : program->specializationParams )
{
auto genericParamLayout = as<GenericSpecializationParamLayout>(param);
if(!genericParamLayout)
continue;
if(getText(genericParamLayout->decl->getName()) != UnownedTerminatedStringSlice(name))
continue;
return (SlangReflectionTypeParameter*) genericParamLayout;
}
return 0;
}
SLANG_API SlangUInt spReflection_getEntryPointCount(SlangReflection* inProgram)
{
auto program = convert(inProgram);
if(!program) return 0;
return SlangUInt(program->entryPoints.getCount());
}
SLANG_API SlangReflectionEntryPoint* spReflection_getEntryPointByIndex(SlangReflection* inProgram, SlangUInt index)
{
auto program = convert(inProgram);
if(!program) return 0;
return convert(program->entryPoints[(int) index].Ptr());
}
SLANG_API SlangReflectionEntryPoint* spReflection_findEntryPointByName(SlangReflection* inProgram, char const* name)
{
auto program = convert(inProgram);
if(!program) return 0;
// TODO: improve on naive linear search
for(auto ep : program->entryPoints)
{
if(ep->entryPoint.getName()->text == name)
{
return convert(ep);
}
}
return nullptr;
}
SLANG_API SlangUInt spReflection_getGlobalConstantBufferBinding(SlangReflection* inProgram)
{
auto program = convert(inProgram);
if (!program) return 0;
auto cb = program->parametersLayout->FindResourceInfo(LayoutResourceKind::ConstantBuffer);
if (!cb) return 0;
return cb->index;
}
SLANG_API size_t spReflection_getGlobalConstantBufferSize(SlangReflection* inProgram)
{
auto program = convert(inProgram);
if (!program) return 0;
auto structLayout = getGlobalStructLayout(program);
auto uniform = structLayout->FindResourceInfo(LayoutResourceKind::Uniform);
if (!uniform) return 0;
return getReflectionSize(uniform->count);
}
SLANG_API SlangReflectionType* spReflection_specializeType(
SlangReflection* inProgramLayout,
SlangReflectionType* inType,
SlangInt specializationArgCount,
SlangReflectionType* const* specializationArgs,
ISlangBlob** outDiagnostics)
{
auto programLayout = convert(inProgramLayout);
if(!programLayout) return nullptr;
auto unspecializedType = convert(inType);
if(!unspecializedType) return nullptr;
auto linkage = programLayout->getProgram()->getLinkage();
DiagnosticSink sink(linkage->getSourceManager(), Lexer::sourceLocationLexer);
auto specializedType = linkage->specializeType(unspecializedType, specializationArgCount, (Type* const*) specializationArgs, &sink);
sink.getBlobIfNeeded(outDiagnostics);
return convert(specializedType);
}
SLANG_API SlangUInt spReflection_getHashedStringCount(
SlangReflection* reflection)
{
auto programLayout = convert(reflection);
auto slices = programLayout->hashedStringLiteralPool.getAdded();
return slices.getCount();
}
SLANG_API const char* spReflection_getHashedString(
SlangReflection* reflection,
SlangUInt index,
size_t* outCount)
{
auto programLayout = convert(reflection);
auto slices = programLayout->hashedStringLiteralPool.getAdded();
auto slice = slices[Index(index)];
*outCount = slice.getLength();
return slice.begin();
}
SLANG_API int spComputeStringHash(const char* chars, size_t count)
{
return (int)getStableHashCode32(chars, count);
}
SLANG_API SlangReflectionTypeLayout* spReflection_getGlobalParamsTypeLayout(
SlangReflection* reflection)
{
auto programLayout = convert(reflection);
if(!programLayout) return nullptr;
return convert(programLayout->parametersLayout->typeLayout);
}