summaryrefslogtreecommitdiffstats
path: root/source/slang/type-layout.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/slang/type-layout.cpp')
-rw-r--r--source/slang/type-layout.cpp1125
1 files changed, 1125 insertions, 0 deletions
diff --git a/source/slang/type-layout.cpp b/source/slang/type-layout.cpp
new file mode 100644
index 000000000..4e5c98ed5
--- /dev/null
+++ b/source/slang/type-layout.cpp
@@ -0,0 +1,1125 @@
+// TypeLayout.cpp
+#include "type-layout.h"
+
+#include "syntax.h"
+
+#include <assert.h>
+
+namespace Slang {
+namespace Compiler {
+
+size_t RoundToAlignment(size_t offset, size_t alignment)
+{
+ size_t remainder = offset % alignment;
+ if (remainder == 0)
+ return offset;
+ else
+ return offset + (alignment - remainder);
+}
+
+static size_t RoundUpToPowerOfTwo( size_t value )
+{
+ // TODO(tfoley): I know this isn't a fast approach
+ size_t result = 1;
+ while (result < value)
+ result *= 2;
+ return result;
+}
+
+struct DefaultLayoutRulesImpl : SimpleLayoutRulesImpl
+{
+ // Get size and alignment for a single value of base type.
+ SimpleLayoutInfo GetScalarLayout(BaseType baseType) override
+ {
+ switch (baseType)
+ {
+ case BaseType::Int:
+ case BaseType::UInt:
+ case BaseType::Float:
+ case BaseType::Bool:
+ return SimpleLayoutInfo( LayoutResourceKind::Uniform, 4, 4 );
+
+ default:
+ assert(!"unimplemented");
+ return SimpleLayoutInfo( LayoutResourceKind::Uniform, 0, 1 );
+ }
+ }
+
+ virtual SimpleLayoutInfo GetScalarLayout(slang::TypeReflection::ScalarType scalarType)
+ {
+ switch( scalarType )
+ {
+ case slang::TypeReflection::ScalarType::Void: return SimpleLayoutInfo();
+ case slang::TypeReflection::ScalarType::None: return SimpleLayoutInfo();
+
+ // TODO(tfoley): At some point we don't want to lay out `bool` as 4 bytes by default...
+ case slang::TypeReflection::ScalarType::Bool: return SimpleLayoutInfo( LayoutResourceKind::Uniform, 4,4);
+ case slang::TypeReflection::ScalarType::Int32: return SimpleLayoutInfo( LayoutResourceKind::Uniform, 4,4);
+ case slang::TypeReflection::ScalarType::UInt32: return SimpleLayoutInfo( LayoutResourceKind::Uniform, 4,4);
+ case slang::TypeReflection::ScalarType::Int64: return SimpleLayoutInfo( LayoutResourceKind::Uniform, 8,8);
+ case slang::TypeReflection::ScalarType::UInt64: return SimpleLayoutInfo( LayoutResourceKind::Uniform, 8,8);
+
+ // TODO(tfoley): What actually happens if you use `half` in a constant buffer?
+ case slang::TypeReflection::ScalarType::Float16: return SimpleLayoutInfo( LayoutResourceKind::Uniform, 2,2);
+ case slang::TypeReflection::ScalarType::Float32: return SimpleLayoutInfo( LayoutResourceKind::Uniform, 4,4);
+ case slang::TypeReflection::ScalarType::Float64: return SimpleLayoutInfo( LayoutResourceKind::Uniform, 8,8);
+
+ default:
+ assert(!"unimplemented");
+ return SimpleLayoutInfo();
+ }
+ }
+
+ SimpleArrayLayoutInfo GetArrayLayout( SimpleLayoutInfo elementInfo, size_t elementCount) override
+ {
+ size_t stride = elementInfo.size;
+
+ SimpleArrayLayoutInfo arrayInfo;
+ arrayInfo.kind = elementInfo.kind;
+ arrayInfo.size = stride * elementCount;
+ arrayInfo.alignment = elementInfo.alignment;
+ arrayInfo.elementStride = stride;
+ return arrayInfo;
+ }
+
+ SimpleLayoutInfo GetVectorLayout(SimpleLayoutInfo elementInfo, size_t elementCount) override
+ {
+ SimpleLayoutInfo vectorInfo;
+ vectorInfo.kind = elementInfo.kind;
+ vectorInfo.size = elementInfo.size * elementCount;
+ vectorInfo.alignment = elementInfo.alignment;
+ return vectorInfo;
+ }
+
+ SimpleLayoutInfo GetMatrixLayout(SimpleLayoutInfo elementInfo, size_t rowCount, size_t columnCount) override
+ {
+ return GetArrayLayout(
+ GetVectorLayout(elementInfo, columnCount),
+ rowCount);
+ }
+
+ UniformLayoutInfo BeginStructLayout() override
+ {
+ UniformLayoutInfo structInfo(0, 1);
+ return structInfo;
+ }
+
+ size_t AddStructField(UniformLayoutInfo* ioStructInfo, UniformLayoutInfo fieldInfo) override
+ {
+ // Skip zero-size fields
+ if(fieldInfo.size == 0)
+ return ioStructInfo->size;
+
+ ioStructInfo->alignment = std::max(ioStructInfo->alignment, fieldInfo.alignment);
+ ioStructInfo->size = RoundToAlignment(ioStructInfo->size, fieldInfo.alignment);
+ size_t fieldOffset = ioStructInfo->size;
+ ioStructInfo->size += fieldInfo.size;
+ return fieldOffset;
+ }
+
+
+ void EndStructLayout(UniformLayoutInfo* ioStructInfo) override
+ {
+ ioStructInfo->size = RoundToAlignment(ioStructInfo->size, ioStructInfo->alignment);
+ }
+};
+
+// Capture common behavior betwen HLSL and GLSL (`std140`) constnat buffer rules
+struct DefaultConstantBufferLayoutRulesImpl : DefaultLayoutRulesImpl
+{
+ // The `std140` rules require that all array elements
+ // be a multiple of 16 bytes.
+ //
+ // HLSL agrees.
+ SimpleArrayLayoutInfo GetArrayLayout(SimpleLayoutInfo elementInfo, size_t elementCount) override
+ {
+ if(elementInfo.kind == LayoutResourceKind::Uniform)
+ {
+ if (elementInfo.alignment < 16)
+ elementInfo.alignment = 16;
+ elementInfo.size = RoundToAlignment(elementInfo.size, elementInfo.alignment);
+ }
+ return DefaultLayoutRulesImpl::GetArrayLayout(elementInfo, elementCount);
+ }
+
+ // The `std140` rules require that a `struct` type be
+ // aligned to at least 16.
+ //
+ // HLSL agrees.
+ UniformLayoutInfo BeginStructLayout() override
+ {
+ return UniformLayoutInfo(0, 16);
+ }
+};
+
+struct GLSLConstantBufferLayoutRulesImpl : DefaultConstantBufferLayoutRulesImpl
+{
+};
+
+struct Std140LayoutRulesImpl : GLSLConstantBufferLayoutRulesImpl
+{
+ // The `std140` rules require vectors to be aligned to the next power of two
+ // up from their size (so a `float2` is 8-byte aligned, and a `float3` is
+ // 16-byte aligned).
+ SimpleLayoutInfo GetVectorLayout(SimpleLayoutInfo elementInfo, size_t elementCount) override
+ {
+ assert(elementInfo.kind == LayoutResourceKind::Uniform);
+ SimpleLayoutInfo vectorInfo(
+ LayoutResourceKind::Uniform,
+ elementInfo.size * elementCount,
+ RoundUpToPowerOfTwo(elementInfo.size * elementInfo.alignment));
+ return vectorInfo;
+ }
+};
+
+struct HLSLConstantBufferLayoutRulesImpl : DefaultConstantBufferLayoutRulesImpl
+{
+ // Can't let a `struct` field straddle a register (16-byte) boundary
+ size_t AddStructField(UniformLayoutInfo* ioStructInfo, UniformLayoutInfo fieldInfo) override
+ {
+ // Skip zero-size fields
+ if(fieldInfo.size == 0)
+ return ioStructInfo->size;
+
+ ioStructInfo->alignment = std::max(ioStructInfo->alignment, fieldInfo.alignment);
+ ioStructInfo->size = RoundToAlignment(ioStructInfo->size, fieldInfo.alignment);
+
+ size_t fieldOffset = ioStructInfo->size;
+ size_t fieldSize = fieldInfo.size;
+
+ // Would this field cross a 16-byte boundary?
+ auto registerSize = 16;
+ auto startRegister = fieldOffset / registerSize;
+ auto endRegister = (fieldOffset + fieldSize - 1) / registerSize;
+ if (startRegister != endRegister)
+ {
+ ioStructInfo->size = RoundToAlignment(ioStructInfo->size, size_t(registerSize));
+ fieldOffset = ioStructInfo->size;
+ }
+
+ ioStructInfo->size += fieldInfo.size;
+ return fieldOffset;
+ }
+};
+
+struct HLSLStructuredBufferLayoutRulesImpl : DefaultLayoutRulesImpl
+{
+ // TODO: customize these to be correct...
+};
+
+struct Std430LayoutRulesImpl : GLSLConstantBufferLayoutRulesImpl
+{
+};
+
+struct DefaultVaryingLayoutRulesImpl : DefaultLayoutRulesImpl
+{
+ LayoutResourceKind kind;
+
+ DefaultVaryingLayoutRulesImpl(LayoutResourceKind kind)
+ : kind(kind)
+ {}
+
+
+ // hook to allow differentiating for input/output
+ virtual LayoutResourceKind getKind()
+ {
+ return kind;
+ }
+
+ SimpleLayoutInfo GetScalarLayout(BaseType baseType) override
+ {
+ // Assume that all scalars take up one "slot"
+ return SimpleLayoutInfo(
+ getKind(),
+ 1);
+ }
+
+ virtual SimpleLayoutInfo GetScalarLayout(slang::TypeReflection::ScalarType scalarType)
+ {
+ // Assume that all scalars take up one "slot"
+ return SimpleLayoutInfo(
+ getKind(),
+ 1);
+ }
+
+ SimpleLayoutInfo GetVectorLayout(SimpleLayoutInfo elementInfo, size_t elementCount) override
+ {
+ // Vectors take up one slot by default
+ //
+ // TODO: some platforms may decide that vectors of `double` need
+ // special handling
+ return SimpleLayoutInfo(
+ getKind(),
+ 1);
+ }
+};
+
+struct GLSLVaryingLayoutRulesImpl : DefaultVaryingLayoutRulesImpl
+{
+ GLSLVaryingLayoutRulesImpl(LayoutResourceKind kind)
+ : DefaultVaryingLayoutRulesImpl(kind)
+ {}
+};
+
+struct HLSLVaryingLayoutRulesImpl : DefaultVaryingLayoutRulesImpl
+{
+ HLSLVaryingLayoutRulesImpl(LayoutResourceKind kind)
+ : DefaultVaryingLayoutRulesImpl(kind)
+ {}
+};
+
+//
+
+struct GLSLSpecializationConstantLayoutRulesImpl : DefaultLayoutRulesImpl
+{
+ LayoutResourceKind getKind()
+ {
+ return LayoutResourceKind::SpecializationConstant;
+ }
+
+ SimpleLayoutInfo GetScalarLayout(BaseType baseType) override
+ {
+ // Assume that all scalars take up one "slot"
+ return SimpleLayoutInfo(
+ getKind(),
+ 1);
+ }
+
+ virtual SimpleLayoutInfo GetScalarLayout(slang::TypeReflection::ScalarType scalarType)
+ {
+ // Assume that all scalars take up one "slot"
+ return SimpleLayoutInfo(
+ getKind(),
+ 1);
+ }
+
+ SimpleLayoutInfo GetVectorLayout(SimpleLayoutInfo elementInfo, size_t elementCount) override
+ {
+ // GLSL doesn't support vectors of specialization constants,
+ // but we will assume that, if supported, they would use one slot per element.
+ return SimpleLayoutInfo(
+ getKind(),
+ elementCount);
+ }
+};
+
+GLSLSpecializationConstantLayoutRulesImpl kGLSLSpecializationConstantLayoutRulesImpl;
+
+//
+
+struct GLSLObjectLayoutRulesImpl : ObjectLayoutRulesImpl
+{
+ virtual SimpleLayoutInfo GetObjectLayout(ShaderParameterKind kind) override
+ {
+ // In Vulkan GLSL, pretty much every object is just a descriptor-table slot.
+ // We can refine this method once we support a case where this isn't true.
+ return SimpleLayoutInfo(LayoutResourceKind::DescriptorTableSlot, 1);
+ }
+};
+GLSLObjectLayoutRulesImpl kGLSLObjectLayoutRulesImpl;
+
+struct HLSLObjectLayoutRulesImpl : ObjectLayoutRulesImpl
+{
+ virtual SimpleLayoutInfo GetObjectLayout(ShaderParameterKind kind) override
+ {
+ switch( kind )
+ {
+ case ShaderParameterKind::ConstantBuffer:
+ return SimpleLayoutInfo(LayoutResourceKind::ConstantBuffer, 1);
+
+ case ShaderParameterKind::TextureUniformBuffer:
+ case ShaderParameterKind::StructuredBuffer:
+ case ShaderParameterKind::SampledBuffer:
+ case ShaderParameterKind::RawBuffer:
+ case ShaderParameterKind::Buffer:
+ case ShaderParameterKind::Texture:
+ return SimpleLayoutInfo(LayoutResourceKind::ShaderResource, 1);
+
+ case ShaderParameterKind::MutableStructuredBuffer:
+ case ShaderParameterKind::MutableSampledBuffer:
+ case ShaderParameterKind::MutableRawBuffer:
+ case ShaderParameterKind::MutableBuffer:
+ case ShaderParameterKind::MutableTexture:
+ return SimpleLayoutInfo(LayoutResourceKind::UnorderedAccess, 1);
+
+ case ShaderParameterKind::SamplerState:
+ return SimpleLayoutInfo(LayoutResourceKind::SamplerState, 1);
+
+ case ShaderParameterKind::TextureSampler:
+ case ShaderParameterKind::MutableTextureSampler:
+ case ShaderParameterKind::InputRenderTarget:
+ // TODO: how to handle these?
+ default:
+ assert(!"unimplemented");
+ return SimpleLayoutInfo();
+ }
+ }
+};
+HLSLObjectLayoutRulesImpl kHLSLObjectLayoutRulesImpl;
+
+Std140LayoutRulesImpl kStd140LayoutRulesImpl;
+Std430LayoutRulesImpl kStd430LayoutRulesImpl;
+HLSLConstantBufferLayoutRulesImpl kHLSLConstantBufferLayoutRulesImpl;
+HLSLStructuredBufferLayoutRulesImpl kHLSLStructuredBufferLayoutRulesImpl;
+
+GLSLVaryingLayoutRulesImpl kGLSLVaryingInputLayoutRulesImpl(LayoutResourceKind::VertexInput);
+GLSLVaryingLayoutRulesImpl kGLSLVaryingOutputLayoutRulesImpl(LayoutResourceKind::FragmentOutput);
+
+HLSLVaryingLayoutRulesImpl kHLSLVaryingInputLayoutRulesImpl(LayoutResourceKind::VertexInput);
+HLSLVaryingLayoutRulesImpl kHLSLVaryingOutputLayoutRulesImpl(LayoutResourceKind::FragmentOutput);
+
+//
+
+struct GLSLLayoutRulesFamilyImpl : LayoutRulesFamilyImpl
+{
+ virtual LayoutRulesImpl* getConstantBufferRules() override;
+ virtual LayoutRulesImpl* getTextureBufferRules() override;
+ virtual LayoutRulesImpl* getVaryingInputRules() override;
+ virtual LayoutRulesImpl* getVaryingOutputRules() override;
+ virtual LayoutRulesImpl* getSpecializationConstantRules() override;
+ virtual LayoutRulesImpl* getShaderStorageBufferRules() override;
+};
+
+struct HLSLLayoutRulesFamilyImpl : LayoutRulesFamilyImpl
+{
+ virtual LayoutRulesImpl* getConstantBufferRules() override;
+ virtual LayoutRulesImpl* getTextureBufferRules() override;
+ virtual LayoutRulesImpl* getVaryingInputRules() override;
+ virtual LayoutRulesImpl* getVaryingOutputRules() override;
+ virtual LayoutRulesImpl* getSpecializationConstantRules() override;
+ virtual LayoutRulesImpl* getShaderStorageBufferRules() override;
+};
+
+GLSLLayoutRulesFamilyImpl kGLSLLayoutRulesFamilyImpl;
+HLSLLayoutRulesFamilyImpl kHLSLLayoutRulesFamilyImpl;
+
+
+// GLSL cases
+
+LayoutRulesImpl kStd140LayoutRulesImpl_ = {
+ &kGLSLLayoutRulesFamilyImpl, &kStd140LayoutRulesImpl, &kGLSLObjectLayoutRulesImpl,
+};
+
+LayoutRulesImpl kStd430LayoutRulesImpl_ = {
+ &kGLSLLayoutRulesFamilyImpl, &kStd430LayoutRulesImpl, &kGLSLObjectLayoutRulesImpl,
+};
+
+LayoutRulesImpl kGLSLVaryingInputLayoutRulesImpl_ = {
+ &kGLSLLayoutRulesFamilyImpl, &kGLSLVaryingInputLayoutRulesImpl, &kGLSLObjectLayoutRulesImpl,
+};
+
+LayoutRulesImpl kGLSLVaryingOutputLayoutRulesImpl_ = {
+ &kGLSLLayoutRulesFamilyImpl, &kGLSLVaryingOutputLayoutRulesImpl, &kGLSLObjectLayoutRulesImpl,
+};
+
+LayoutRulesImpl kGLSLSpecializationConstantLayoutRulesImpl_ = {
+ &kGLSLLayoutRulesFamilyImpl, &kGLSLSpecializationConstantLayoutRulesImpl, &kGLSLObjectLayoutRulesImpl,
+};
+
+// HLSL cases
+
+LayoutRulesImpl kHLSLConstantBufferLayoutRulesImpl_ = {
+ &kHLSLLayoutRulesFamilyImpl, &kHLSLConstantBufferLayoutRulesImpl, &kHLSLObjectLayoutRulesImpl,
+};
+
+LayoutRulesImpl kHLSLStructuredBufferLayoutRulesImpl_ = {
+ &kHLSLLayoutRulesFamilyImpl, &kHLSLStructuredBufferLayoutRulesImpl, &kHLSLObjectLayoutRulesImpl,
+};
+
+LayoutRulesImpl kHLSLVaryingInputLayoutRulesImpl_ = {
+ &kHLSLLayoutRulesFamilyImpl, &kHLSLVaryingInputLayoutRulesImpl, &kHLSLObjectLayoutRulesImpl,
+};
+
+LayoutRulesImpl kHLSLVaryingOutputLayoutRulesImpl_ = {
+ &kHLSLLayoutRulesFamilyImpl, &kHLSLVaryingOutputLayoutRulesImpl, &kHLSLObjectLayoutRulesImpl,
+};
+
+//
+
+LayoutRulesImpl* GLSLLayoutRulesFamilyImpl::getConstantBufferRules()
+{
+ return &kStd140LayoutRulesImpl_;
+}
+
+LayoutRulesImpl* GLSLLayoutRulesFamilyImpl::getTextureBufferRules()
+{
+ return nullptr;
+}
+
+LayoutRulesImpl* GLSLLayoutRulesFamilyImpl::getVaryingInputRules()
+{
+ return &kGLSLVaryingInputLayoutRulesImpl_;
+}
+
+LayoutRulesImpl* GLSLLayoutRulesFamilyImpl::getVaryingOutputRules()
+{
+ return &kGLSLVaryingOutputLayoutRulesImpl_;
+}
+
+LayoutRulesImpl* GLSLLayoutRulesFamilyImpl::getSpecializationConstantRules()
+{
+ return &kGLSLSpecializationConstantLayoutRulesImpl_;
+}
+
+LayoutRulesImpl* GLSLLayoutRulesFamilyImpl::getShaderStorageBufferRules()
+{
+ return &kStd430LayoutRulesImpl_;
+}
+
+//
+
+LayoutRulesImpl* HLSLLayoutRulesFamilyImpl::getConstantBufferRules()
+{
+ return &kHLSLConstantBufferLayoutRulesImpl_;
+}
+
+LayoutRulesImpl* HLSLLayoutRulesFamilyImpl::getTextureBufferRules()
+{
+ return nullptr;
+}
+
+LayoutRulesImpl* HLSLLayoutRulesFamilyImpl::getVaryingInputRules()
+{
+ return &kHLSLVaryingInputLayoutRulesImpl_;
+}
+
+LayoutRulesImpl* HLSLLayoutRulesFamilyImpl::getVaryingOutputRules()
+{
+ return &kHLSLVaryingOutputLayoutRulesImpl_;
+}
+
+LayoutRulesImpl* HLSLLayoutRulesFamilyImpl::getSpecializationConstantRules()
+{
+ return nullptr;
+}
+
+LayoutRulesImpl* HLSLLayoutRulesFamilyImpl::getShaderStorageBufferRules()
+{
+ return nullptr;
+}
+
+//
+
+LayoutRulesImpl* GetLayoutRulesImpl(LayoutRule rule)
+{
+ switch (rule)
+ {
+ case LayoutRule::Std140: return &kStd140LayoutRulesImpl_;
+ case LayoutRule::Std430: return &kStd430LayoutRulesImpl_;
+ case LayoutRule::HLSLConstantBuffer: return &kHLSLConstantBufferLayoutRulesImpl_;
+ case LayoutRule::HLSLStructuredBuffer: return &kHLSLStructuredBufferLayoutRulesImpl_;
+ default:
+ return nullptr;
+ }
+}
+
+LayoutRulesFamilyImpl* GetLayoutRulesFamilyImpl(LayoutRulesFamily rule)
+{
+ switch (rule)
+ {
+ case LayoutRulesFamily::HLSL: return &kHLSLLayoutRulesFamilyImpl;
+ case LayoutRulesFamily::GLSL: return &kGLSLLayoutRulesFamilyImpl;
+ default:
+ return nullptr;
+ }
+}
+
+LayoutRulesFamilyImpl* GetLayoutRulesFamilyImpl(SourceLanguage language)
+{
+ switch (language)
+ {
+ case SourceLanguage::Slang:
+ case SourceLanguage::HLSL:
+ return &kHLSLLayoutRulesFamilyImpl;
+
+ case SourceLanguage::GLSL:
+ return &kGLSLLayoutRulesFamilyImpl;
+
+ default:
+ return nullptr;
+ }
+}
+
+
+static int GetElementCount(RefPtr<IntVal> val)
+{
+ if (auto constantVal = val.As<ConstantIntVal>())
+ {
+ return constantVal->value;
+ }
+ else if( auto varRefVal = val.As<GenericParamIntVal>() )
+ {
+ // TODO(tfoley): do something sensible in this case
+ return 0;
+ }
+ assert(!"unexpected");
+ return 0;
+}
+
+bool IsResourceKind(LayoutResourceKind kind)
+{
+ switch (kind)
+ {
+ case LayoutResourceKind::None:
+ case LayoutResourceKind::Uniform:
+ return false;
+
+ default:
+ return true;
+ }
+
+}
+
+SimpleLayoutInfo GetSimpleLayoutImpl(
+ SimpleLayoutInfo info,
+ RefPtr<ExpressionType> type,
+ LayoutRulesImpl* rules,
+ RefPtr<TypeLayout>* outTypeLayout)
+{
+ if (outTypeLayout)
+ {
+ RefPtr<TypeLayout> typeLayout = new TypeLayout();
+ *outTypeLayout = typeLayout;
+
+ typeLayout->type = type;
+ typeLayout->rules = rules;
+
+ typeLayout->uniformAlignment = info.alignment;
+
+ typeLayout->addResourceUsage(info.kind, info.size);
+ }
+
+ return info;
+}
+
+static SimpleLayoutInfo getParameterBlockLayoutInfo(
+ RefPtr<ParameterBlockType> type,
+ LayoutRulesImpl* rules)
+{
+ if( type->As<ConstantBufferType>() )
+ {
+ return rules->GetObjectLayout(ShaderParameterKind::ConstantBuffer);
+ }
+ else if( type->As<TextureBufferType>() )
+ {
+ return rules->GetObjectLayout(ShaderParameterKind::TextureUniformBuffer);
+ }
+ else if( type->As<GLSLShaderStorageBufferType>() )
+ {
+ return rules->GetObjectLayout(ShaderParameterKind::ShaderStorageBuffer);
+ }
+ // TODO: the vertex-input and fragment-output cases should
+ // only actually apply when we are at the appropriate stage in
+ // the pipeline...
+ else if( type->As<GLSLInputParameterBlockType>() )
+ {
+ return SimpleLayoutInfo(LayoutResourceKind::VertexInput, 0);
+ }
+ else if( type->As<GLSLOutputParameterBlockType>() )
+ {
+ return SimpleLayoutInfo(LayoutResourceKind::FragmentOutput, 0);
+ }
+ else
+ {
+ assert(!"unexpected");
+ return SimpleLayoutInfo();
+ }
+}
+
+
+RefPtr<ParameterBlockTypeLayout>
+createParameterBlockTypeLayout(
+ RefPtr<ParameterBlockType> parameterBlockType,
+ RefPtr<TypeLayout> elementTypeLayout,
+ LayoutRulesImpl* rules)
+{
+ auto info = getParameterBlockLayoutInfo(
+ parameterBlockType,
+ rules);
+
+ auto typeLayout = new ParameterBlockTypeLayout();
+
+ typeLayout->type = parameterBlockType;
+ typeLayout->rules = rules;
+
+ typeLayout->elementTypeLayout = elementTypeLayout;
+
+ // The layout of the constant buffer if it gets stored
+ // in another constant buffer is just what we computed
+ // originally (which should be a single binding "slot"
+ // and hence no uniform data).
+ //
+ typeLayout->uniformAlignment = info.alignment;
+ assert(!typeLayout->FindResourceInfo(LayoutResourceKind::Uniform));
+ assert(typeLayout->uniformAlignment == 1);
+
+ // TODO(tfoley): There is a subtle question here of whether
+ // a constant buffer declaration that then contains zero
+ // bytes of uniform data should actually allocate a CB
+ // binding slot. For now I'm going to try to ignore it,
+ // but handling this robustly could let other code
+ // simply handle the "global scope" as a giant outer
+ // CB declaration...
+
+ // Make sure that we allocate resource usage for the
+ // parameter block itself.
+ if( info.size )
+ {
+ typeLayout->addResourceUsage(
+ info.kind,
+ info.size);
+ }
+
+ // Now, if the element type itself had any resources, then
+ // we need to make these part of the layout for our block
+ //
+ // TODO: re-consider this decision, since it creates
+ // complications...
+ for( auto elementResourceInfo : elementTypeLayout->resourceInfos )
+ {
+ // Skip uniform data, since that is encapsualted behind the constant buffer
+ if(elementResourceInfo.kind == LayoutResourceKind::Uniform)
+ break;
+
+ typeLayout->addResourceUsage(elementResourceInfo);
+ }
+
+ return typeLayout;
+}
+
+LayoutRulesImpl* getParameterBufferElementTypeLayoutRules(
+ RefPtr<ParameterBlockType> parameterBlockType,
+ LayoutRulesImpl* rules)
+{
+ if( parameterBlockType->As<ConstantBufferType>() )
+ {
+ return rules->getLayoutRulesFamily()->getConstantBufferRules();
+ }
+ else if( parameterBlockType->As<TextureBufferType>() )
+ {
+ return rules->getLayoutRulesFamily()->getTextureBufferRules();
+ }
+ else if( parameterBlockType->As<GLSLInputParameterBlockType>() )
+ {
+ return rules->getLayoutRulesFamily()->getVaryingInputRules();
+ }
+ else if( parameterBlockType->As<GLSLOutputParameterBlockType>() )
+ {
+ return rules->getLayoutRulesFamily()->getVaryingOutputRules();
+ }
+ else if( parameterBlockType->As<GLSLShaderStorageBufferType>() )
+ {
+ return rules->getLayoutRulesFamily()->getShaderStorageBufferRules();
+ }
+ else
+ {
+ assert(!"unexpected");
+ return nullptr;
+ }
+}
+
+RefPtr<ParameterBlockTypeLayout>
+createParameterBlockTypeLayout(
+ RefPtr<ParameterBlockType> parameterBlockType,
+ LayoutRulesImpl* rules)
+{
+ // Determine the layout rules to use for the contents of the block
+ auto parameterBlockLayoutRules = getParameterBufferElementTypeLayoutRules(
+ parameterBlockType,
+ rules);
+
+ // Create and save type layout for the buffer contents.
+ auto elementTypeLayout = CreateTypeLayout(
+ parameterBlockType->elementType.Ptr(),
+ parameterBlockLayoutRules);
+
+ return createParameterBlockTypeLayout(
+ parameterBlockType,
+ elementTypeLayout,
+ rules);
+}
+
+// Create a type layout for a structured buffer type.
+RefPtr<StructuredBufferTypeLayout>
+createStructuredBufferTypeLayout(
+ ShaderParameterKind kind,
+ RefPtr<ExpressionType> structuredBufferType,
+ RefPtr<TypeLayout> elementTypeLayout,
+ LayoutRulesImpl* rules)
+{
+ auto info = rules->GetObjectLayout(kind);
+
+ auto typeLayout = new StructuredBufferTypeLayout();
+
+ typeLayout->type = structuredBufferType;
+ typeLayout->rules = rules;
+
+ typeLayout->elementTypeLayout = elementTypeLayout;
+
+ typeLayout->uniformAlignment = info.alignment;
+ assert(!typeLayout->FindResourceInfo(LayoutResourceKind::Uniform));
+ assert(typeLayout->uniformAlignment == 1);
+
+ if( info.size != 0 )
+ {
+ typeLayout->addResourceUsage(info.kind, info.size);
+ }
+
+ // Note: for now we don't deal with the case of a structured
+ // buffer that might contain anything other than "uniform" data,
+ // because there really isn't a way to implement that.
+
+ return typeLayout;
+}
+
+// Create a type layout for a structured buffer type.
+RefPtr<StructuredBufferTypeLayout>
+createStructuredBufferTypeLayout(
+ ShaderParameterKind kind,
+ RefPtr<ExpressionType> structuredBufferType,
+ RefPtr<ExpressionType> elementType,
+ LayoutRulesImpl* rules)
+{
+ // TODO(tfoley): need to compute the layout for the constant
+ // buffer's contents...
+ auto structuredBufferLayoutRules = GetLayoutRulesImpl(
+ LayoutRule::HLSLStructuredBuffer);
+
+ // Create and save type layout for the buffer contents.
+ auto elementTypeLayout = CreateTypeLayout(
+ elementType.Ptr(),
+ structuredBufferLayoutRules);
+
+ return createStructuredBufferTypeLayout(
+ kind,
+ structuredBufferType,
+ elementTypeLayout,
+ rules);
+
+}
+
+SimpleLayoutInfo GetLayoutImpl(
+ ExpressionType* type,
+ LayoutRulesImpl* rules,
+ RefPtr<TypeLayout>* outTypeLayout)
+{
+ if (auto parameterBlockType = type->As<ParameterBlockType>())
+ {
+ // If the user is just interested in uniform layout info,
+ // then this is easy: a `ConstantBuffer<T>` is really no
+ // different from a `Texture2D<U>` in terms of how it
+ // should be handled as a member of a container.
+ //
+ auto info = getParameterBlockLayoutInfo(parameterBlockType, rules);
+
+ // The more interesting case, though, is when the user
+ // is requesting us to actually create a `TypeLayout`,
+ // since in that case we need to:
+ //
+ // 1. Compute a layout for the data inside the constant
+ // buffer, including offsets, etc.
+ //
+ // 2. Compute information about any object types inside
+ // the constant buffer, which need to be surfaces out
+ // to the top level.
+ //
+ if (outTypeLayout)
+ {
+ *outTypeLayout = createParameterBlockTypeLayout(
+ parameterBlockType,
+ rules);
+ }
+
+ return info;
+ }
+ else if (auto samplerStateType = type->As<SamplerStateType>())
+ {
+ return GetSimpleLayoutImpl(
+ rules->GetObjectLayout(ShaderParameterKind::SamplerState),
+ type,
+ rules,
+ outTypeLayout);
+ }
+ else if (auto textureType = type->As<TextureType>())
+ {
+ // TODO: the logic here should really be defined by the rules,
+ // and not at this top level...
+ ShaderParameterKind kind;
+ switch( textureType->getAccess() )
+ {
+ default:
+ kind = ShaderParameterKind::MutableTexture;
+ break;
+
+ case SLANG_RESOURCE_ACCESS_READ:
+ kind = ShaderParameterKind::Texture;
+ break;
+ }
+
+ return GetSimpleLayoutImpl(
+ rules->GetObjectLayout(kind),
+ type,
+ rules,
+ outTypeLayout);
+ }
+ else if (auto textureSamplerType = type->As<TextureSamplerType>())
+ {
+ // TODO: the logic here should really be defined by the rules,
+ // and not at this top level...
+ ShaderParameterKind kind;
+ switch( textureSamplerType->getAccess() )
+ {
+ default:
+ kind = ShaderParameterKind::MutableTextureSampler;
+ break;
+
+ case SLANG_RESOURCE_ACCESS_READ:
+ kind = ShaderParameterKind::TextureSampler;
+ break;
+ }
+
+ return GetSimpleLayoutImpl(
+ rules->GetObjectLayout(kind),
+ type,
+ rules,
+ outTypeLayout);
+ }
+
+ // TODO: need a better way to handle this stuff...
+#define CASE(TYPE, KIND) \
+ else if(auto type_##TYPE = type->As<TYPE>()) do { \
+ auto info = rules->GetObjectLayout(ShaderParameterKind::KIND); \
+ if (outTypeLayout) \
+ { \
+ *outTypeLayout = createStructuredBufferTypeLayout( \
+ ShaderParameterKind::KIND, \
+ type_##TYPE, \
+ type_##TYPE->elementType.Ptr(), \
+ rules); \
+ } \
+ return info; \
+ } while(0)
+
+ CASE(HLSLStructuredBufferType, StructuredBuffer);
+ CASE(HLSLRWStructuredBufferType, MutableStructuredBuffer);
+ CASE(HLSLAppendStructuredBufferType, MutableStructuredBuffer);
+ CASE(HLSLConsumeStructuredBufferType, MutableStructuredBuffer);
+
+#undef CASE
+
+
+ // TODO: need a better way to handle this stuff...
+#define CASE(TYPE, KIND) \
+ else if(type->As<TYPE>()) do { \
+ return GetSimpleLayoutImpl( \
+ rules->GetObjectLayout(ShaderParameterKind::KIND), \
+ type, rules, outTypeLayout); \
+ } while(0)
+
+ CASE(HLSLBufferType, SampledBuffer);
+ CASE(HLSLRWBufferType, MutableSampledBuffer);
+ CASE(HLSLByteAddressBufferType, RawBuffer);
+ CASE(HLSLRWByteAddressBufferType, MutableRawBuffer);
+
+ CASE(GLSLInputAttachmentType, InputRenderTarget);
+
+ // This case is mostly to allow users to add new resource types...
+ CASE(UntypedBufferResourceType, RawBuffer);
+
+#undef CASE
+
+ //
+ // TODO(tfoley): Need to recognize any UAV types here
+ //
+ else if(auto basicType = type->As<BasicExpressionType>())
+ {
+ return GetSimpleLayoutImpl(
+ rules->GetScalarLayout(basicType->BaseType),
+ type,
+ rules,
+ outTypeLayout);
+ }
+ else if(auto vecType = type->As<VectorExpressionType>())
+ {
+ return GetSimpleLayoutImpl(
+ rules->GetVectorLayout(
+ GetLayout(vecType->elementType.Ptr(), rules),
+ GetIntVal(vecType->elementCount)),
+ type,
+ rules,
+ outTypeLayout);
+ }
+ else if(auto matType = type->As<MatrixExpressionType>())
+ {
+ return GetSimpleLayoutImpl(
+ rules->GetMatrixLayout(
+ GetLayout(matType->getElementType(), rules),
+ GetIntVal(matType->getRowCount()),
+ GetIntVal(matType->getColumnCount())),
+ type,
+ rules,
+ outTypeLayout);
+ }
+ else if (auto arrayType = type->As<ArrayExpressionType>())
+ {
+ RefPtr<TypeLayout> elementTypeLayout;
+ auto elementInfo = GetLayoutImpl(
+ arrayType->BaseType.Ptr(),
+ rules,
+ outTypeLayout ? &elementTypeLayout : nullptr);
+
+ // For layout purposes, we treat an unsized array as an array of zero elements.
+ //
+ // TODO: Longer term we are going to need to be careful to include some indication
+ // that a type has logically "infinite" size in some resource kind. In particular
+ // this affects how we would allocate space for parameter binding purposes.
+ auto elementCount = arrayType->ArrayLength ? GetElementCount(arrayType->ArrayLength) : 0;
+ auto arrayUniformInfo = rules->GetArrayLayout(
+ elementInfo,
+ elementCount).getUniformLayout();
+
+ if (outTypeLayout)
+ {
+ RefPtr<ArrayTypeLayout> typeLayout = new ArrayTypeLayout();
+ *outTypeLayout = typeLayout;
+
+ typeLayout->type = type;
+ typeLayout->elementTypeLayout = elementTypeLayout;
+ typeLayout->rules = rules;
+
+ typeLayout->uniformAlignment = arrayUniformInfo.alignment;
+ typeLayout->uniformStride = arrayUniformInfo.elementStride;
+
+ typeLayout->addResourceUsage(LayoutResourceKind::Uniform, arrayUniformInfo.size);
+
+ // translate element-type resources into array-type resources
+ for( auto elementResourceInfo : elementTypeLayout->resourceInfos )
+ {
+ // The uniform case was already handled above
+ if( elementResourceInfo.kind == LayoutResourceKind::Uniform )
+ continue;
+
+ typeLayout->addResourceUsage(
+ elementResourceInfo.kind,
+ elementResourceInfo.count * elementCount);
+ }
+ }
+ return arrayUniformInfo;
+ }
+ else if (auto declRefType = type->As<DeclRefType>())
+ {
+ auto declRef = declRefType->declRef;
+
+ if (auto structDeclRef = declRef.As<StructDeclRef>())
+ {
+ RefPtr<StructTypeLayout> typeLayout;
+ if (outTypeLayout)
+ {
+ typeLayout = new StructTypeLayout();
+ typeLayout->type = type;
+ typeLayout->rules = rules;
+ *outTypeLayout = typeLayout;
+ }
+
+ UniformLayoutInfo info = rules->BeginStructLayout();
+
+ for (auto field : structDeclRef.GetFields())
+ {
+ RefPtr<TypeLayout> fieldTypeLayout;
+ UniformLayoutInfo fieldInfo = GetLayoutImpl(
+ field.GetType().Ptr(),
+ rules,
+ outTypeLayout ? &fieldTypeLayout : nullptr).getUniformLayout();
+
+ // Note: we don't add any zero-size fields
+ // when computing structure layout, just
+ // to avoid having a resource type impact
+ // the final layout.
+ //
+ // This means that the code to generate final
+ // declarations needs to *also* eliminate zero-size
+ // fields to be safe...
+ size_t uniformOffset = info.size;
+ if(fieldInfo.size != 0)
+ {
+ uniformOffset = rules->AddStructField(&info, fieldInfo);
+ }
+
+ if (outTypeLayout)
+ {
+ // If we are computing a complete layout,
+ // then we need to create variable layouts
+ // for each field of the structure.
+ RefPtr<VarLayout> fieldLayout = new VarLayout();
+ fieldLayout->varDecl = field;
+ fieldLayout->typeLayout = fieldTypeLayout;
+ typeLayout->fields.Add(fieldLayout);
+ typeLayout->mapVarToLayout.Add(field.GetDecl(), fieldLayout);
+
+ // Set up uniform offset information, if there is any uniform data in the field
+ if( fieldTypeLayout->FindResourceInfo(LayoutResourceKind::Uniform) )
+ {
+ fieldLayout->AddResourceInfo(LayoutResourceKind::Uniform)->index = uniformOffset;
+ }
+
+ // Add offset information for any other resource kinds
+ for( auto fieldTypeResourceInfo : fieldTypeLayout->resourceInfos )
+ {
+ // Uniforms were dealt with above
+ if(fieldTypeResourceInfo.kind == LayoutResourceKind::Uniform)
+ continue;
+
+ // We should not have already processed this resource type
+ assert(!fieldLayout->FindResourceInfo(fieldTypeResourceInfo.kind));
+
+ // The field will need offset information for this kind
+ auto fieldResourceInfo = fieldLayout->AddResourceInfo(fieldTypeResourceInfo.kind);
+
+ // Check how many slots of the given kind have already been added to the type
+ auto structTypeResourceInfo = typeLayout->findOrAddResourceInfo(fieldTypeResourceInfo.kind);
+ fieldResourceInfo->index = structTypeResourceInfo->count;
+ structTypeResourceInfo->count += fieldTypeResourceInfo.count;
+ }
+ }
+ }
+
+ rules->EndStructLayout(&info);
+ if (outTypeLayout)
+ {
+ typeLayout->uniformAlignment = info.alignment;
+ typeLayout->addResourceUsage(LayoutResourceKind::Uniform, info.size);
+ }
+
+ return info;
+ }
+ }
+
+ // catch-all case in case nothing matched
+ assert(!"unimplemented");
+ SimpleLayoutInfo info;
+ return GetSimpleLayoutImpl(
+ info,
+ type,
+ rules,
+ outTypeLayout);
+}
+
+SimpleLayoutInfo GetLayout(ExpressionType* inType, LayoutRulesImpl* rules)
+{
+ return GetLayoutImpl(inType, rules, nullptr);
+}
+
+RefPtr<TypeLayout> CreateTypeLayout(ExpressionType* type, LayoutRulesImpl* rules)
+{
+ RefPtr<TypeLayout> typeLayout;
+ GetLayoutImpl(type, rules, &typeLayout);
+ return typeLayout;
+}
+
+SimpleLayoutInfo GetLayout(ExpressionType* type, LayoutRule rule)
+{
+ LayoutRulesImpl* rulesImpl = GetLayoutRulesImpl(rule);
+ return GetLayout(type, rulesImpl);
+}
+
+}}