summaryrefslogtreecommitdiffstats
path: root/source/slang/type-layout.cpp
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2019-05-31 17:20:37 -0400
committerGitHub <noreply@github.com>2019-05-31 17:20:37 -0400
commit6cbc3929a54d37bd23cb5efa8e3320ba02f78b2f (patch)
tree5a23cb47782e9e2a77762c90dd35da1005eba8d0 /source/slang/type-layout.cpp
parentb81ff3ef968d1cc4e954b31a1812b3c391d17b02 (diff)
Use slang- prefix on slang compiler and core source (#973)
* Prefixing source files in source/slang with slang- * Prefix source in source/slang with slang- prefix. * Rename core source files with slang- prefix. * Update project files. * Fix problems from automatic merge.
Diffstat (limited to 'source/slang/type-layout.cpp')
-rw-r--r--source/slang/type-layout.cpp3209
1 files changed, 0 insertions, 3209 deletions
diff --git a/source/slang/type-layout.cpp b/source/slang/type-layout.cpp
deleted file mode 100644
index 92f7d6af3..000000000
--- a/source/slang/type-layout.cpp
+++ /dev/null
@@ -1,3209 +0,0 @@
-// TypeLayout.cpp
-#include "type-layout.h"
-
-#include "syntax.h"
-
-#include <assert.h>
-
-namespace Slang {
-
-size_t RoundToAlignment(size_t offset, size_t alignment)
-{
- size_t remainder = offset % alignment;
- if (remainder == 0)
- return offset;
- else
- return offset + (alignment - remainder);
-}
-
-LayoutSize RoundToAlignment(LayoutSize offset, size_t alignment)
-{
- // An infinite size is assumed to be maximally aligned.
- if(offset.isInfinite())
- return LayoutSize::infinite();
-
- return RoundToAlignment(offset.getFiniteValue(), alignment);
-}
-
-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::Void: return SimpleLayoutInfo();
-
- // Note: By convention, a `bool` in a constant buffer is stored as an `int.
- // This default may eventually change, at which point this logic will need
- // to be updated.
- //
- // TODO: We should probably warn in this case, since storing a `bool` in
- // a constant buffer seems like a Bad Idea anyway.
- //
- case BaseType::Bool: return SimpleLayoutInfo( LayoutResourceKind::Uniform, 4, 4 );
-
-
- case BaseType::Int8: return SimpleLayoutInfo( LayoutResourceKind::Uniform, 1,1);
- case BaseType::Int16: return SimpleLayoutInfo( LayoutResourceKind::Uniform, 2,2);
- case BaseType::Int: return SimpleLayoutInfo( LayoutResourceKind::Uniform, 4,4);
- case BaseType::Int64: return SimpleLayoutInfo( LayoutResourceKind::Uniform, 8,8);
-
- case BaseType::UInt8: return SimpleLayoutInfo( LayoutResourceKind::Uniform, 1,1);
- case BaseType::UInt16: return SimpleLayoutInfo( LayoutResourceKind::Uniform, 2,2);
- case BaseType::UInt: return SimpleLayoutInfo( LayoutResourceKind::Uniform, 4,4);
- case BaseType::UInt64: return SimpleLayoutInfo( LayoutResourceKind::Uniform, 8,8);
-
- case BaseType::Half: return SimpleLayoutInfo( LayoutResourceKind::Uniform, 2,2);
- case BaseType::Float: return SimpleLayoutInfo( LayoutResourceKind::Uniform, 4,4);
- case BaseType::Double: return SimpleLayoutInfo( LayoutResourceKind::Uniform, 8,8);
-
- default:
- SLANG_UNEXPECTED("uhandled scalar type");
- UNREACHABLE_RETURN(SimpleLayoutInfo( LayoutResourceKind::Uniform, 0, 1 ));
- }
- }
-
- SimpleArrayLayoutInfo GetArrayLayout( SimpleLayoutInfo elementInfo, LayoutSize elementCount) override
- {
- SLANG_RELEASE_ASSERT(elementInfo.size.isFinite());
- auto elementSize = elementInfo.size.getFiniteValue();
- auto elementAlignment = elementInfo.alignment;
- auto elementStride = RoundToAlignment(elementSize, elementAlignment);
-
- // An array with no elements will have zero size.
- //
- LayoutSize arraySize = 0;
- //
- // Any array with a non-zero number of elements will need
- // to have space for N elements of size `elementSize`, with
- // the constraints that there must be `elementStride` bytes
- // between consecutive elements.
- //
- if( elementCount > 0 )
- {
- // We can think of this as either allocating (N-1)
- // chunks of size `elementStride` (for most of the elements)
- // and then one final chunk of size `elementSize` for
- // the last element, or equivalently as allocating
- // N chunks of size `elementStride` and then "giving back"
- // the final `elementStride - elementSize` bytes.
- //
- arraySize = (elementStride * (elementCount-1)) + elementSize;
- }
-
- SimpleArrayLayoutInfo arrayInfo;
- arrayInfo.kind = elementInfo.kind;
- arrayInfo.size = arraySize;
- arrayInfo.alignment = elementAlignment;
- arrayInfo.elementStride = elementStride;
- 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;
- }
-
- SimpleArrayLayoutInfo GetMatrixLayout(SimpleLayoutInfo elementInfo, size_t rowCount, size_t columnCount) override
- {
- // The default behavior here is to lay out a matrix
- // as an array of row vectors (that is row-major).
- //
- // In practice, the code that calls `GetMatrixLayout` will
- // potentially transpose the row/column counts in order
- // to get layouts with a different convention.
- //
- return GetArrayLayout(
- GetVectorLayout(elementInfo, columnCount),
- rowCount);
- }
-
- UniformLayoutInfo BeginStructLayout() override
- {
- UniformLayoutInfo structInfo(0, 1);
- return structInfo;
- }
-
- LayoutSize AddStructField(UniformLayoutInfo* ioStructInfo, UniformLayoutInfo fieldInfo) override
- {
- // Skip zero-size fields
- if(fieldInfo.size == 0)
- return ioStructInfo->size;
-
- // A struct type must be at least as aligned as its most-aligned field.
- ioStructInfo->alignment = std::max(ioStructInfo->alignment, fieldInfo.alignment);
-
- // The new field will be added to the end of the struct.
- auto fieldBaseOffset = ioStructInfo->size;
-
- // We need to ensure that the offset for the field will respect its alignment
- auto fieldOffset = RoundToAlignment(fieldBaseOffset, fieldInfo.alignment);
-
- // The size of the struct must be adjusted to cover the bytes consumed
- // by this field.
- ioStructInfo->size = fieldOffset + fieldInfo.size;
-
- return fieldOffset;
- }
-
-
- void EndStructLayout(UniformLayoutInfo* ioStructInfo) override
- {
- SLANG_UNUSED(ioStructInfo);
-
- // Note: A traditional C layout algorithm would adjust the size
- // of a struct type so that it is a multiple of the alignment.
- // This is a parsimonious design choice because it means that
- // `sizeof(T)` can both be used when copying/allocating a single
- // value of type `T` or an array of N values, without having to
- // consider more details.
- //
- // Of course the choice also has down-sides in that wrapping things
- // into a `struct` can affect layout in ways that waste space. E.g.,
- // the following two cases don't lay out the same:
- //
- // struct S0 { double d; float f; float g; };
- //
- // struct X { double d; float f; }
- // struct S1 { X x; float g; }
- //
- // Even though `S0::g` and `S1::g` have the same amount of useful
- // data in front of them, they will not land at the same offset,
- // and the resulting struct sizes will differ (`sizeof(S0)` will be
- // 16 while `sizeof(S1)` will be 24).
- //
- // Slang doesn't get to be opinionated about this stuff because
- // there is already precedent in both HLSL and GLSL for types
- // that have a size that is not rounded up to their alignment.
- //
- // Our default layout rules won't implement the C-like policy,
- // and instead it will be injected in the concrete implementations
- // that require it.
- }
-};
-
- /// Common behavior for GLSL-family layout.
-struct GLSLBaseLayoutRulesImpl : DefaultLayoutRulesImpl
-{
- typedef DefaultLayoutRulesImpl Super;
-
- SimpleLayoutInfo GetVectorLayout(SimpleLayoutInfo elementInfo, size_t elementCount) override
- {
- // The `std140` and `std430` 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).
- //
- // Note that in this case we have a type layout where the size is *not* a multiple
- // of the alignment, so it should be possible to pack a scalar after a `float3`.
- //
- SLANG_RELEASE_ASSERT(elementInfo.kind == LayoutResourceKind::Uniform);
- SLANG_RELEASE_ASSERT(elementInfo.size.isFinite());
-
- auto size = elementInfo.size.getFiniteValue() * elementCount;
- SimpleLayoutInfo vectorInfo(
- LayoutResourceKind::Uniform,
- size,
- RoundUpToPowerOfTwo(size));
- return vectorInfo;
- }
-
- SimpleArrayLayoutInfo GetArrayLayout( SimpleLayoutInfo elementInfo, LayoutSize elementCount) override
- {
- // The size of an array must be rounded up to be a multiple of its alignment.
- //
- auto info = Super::GetArrayLayout(elementInfo, elementCount);
- info.size = RoundToAlignment(info.size, info.alignment);
- return info;
- }
-
- void EndStructLayout(UniformLayoutInfo* ioStructInfo) override
- {
- // The size of a `struct` must be rounded up to be a multiple of its alignment.
- //
- ioStructInfo->size = RoundToAlignment(ioStructInfo->size, ioStructInfo->alignment);
- }
-};
-
- /// The GLSL `std430` layout rules.
-struct Std430LayoutRulesImpl : GLSLBaseLayoutRulesImpl
-{
- // These rules don't actually need any differences from our
- // base/common GLSL layout rules.
-};
-
- /// The GLSL `std430` layout rules.
-struct Std140LayoutRulesImpl : GLSLBaseLayoutRulesImpl
-{
- typedef GLSLBaseLayoutRulesImpl Super;
-
- SimpleArrayLayoutInfo GetArrayLayout(SimpleLayoutInfo elementInfo, LayoutSize elementCount) override
- {
- // The `std140` rules require that array elements
- // be aligned on 16-byte boundaries.
- //
- if(elementInfo.kind == LayoutResourceKind::Uniform)
- {
- if (elementInfo.alignment < 16)
- elementInfo.alignment = 16;
- }
- return Super::GetArrayLayout(elementInfo, elementCount);
- }
-
- UniformLayoutInfo BeginStructLayout() override
- {
- // The `std140` rules require that a `struct` type
- // be at least 16-byte aligned.
- //
- return UniformLayoutInfo(0, 16);
- }
-};
-
-struct HLSLConstantBufferLayoutRulesImpl : DefaultLayoutRulesImpl
-{
- typedef DefaultLayoutRulesImpl Super;
-
- // Similar to GLSL `std140` rules, an HLSL constant buffer requires that
- // `struct` and array types have 16-byte alignement.
- //
- // Unlike GLSL `std140`, the overall size of an array or `struct` type
- // is *not* rounded up to the alignment, so it is possible for later
- // fields to sneak into the "tail space" left behind by a preceding
- // structure or array. E.g., in this example:
- //
- // struct S { float3 a[2]; float b; };
- //
- // The stride of the array `a` is 16 bytes per element, but the size
- // of `a` will only be 28 bytes (not 32), so that `b` can fit into
- // the space after the last array element and the overall structure
- // will have a size of 32 bytes.
-
- SimpleArrayLayoutInfo GetArrayLayout(SimpleLayoutInfo elementInfo, LayoutSize elementCount) override
- {
- if(elementInfo.kind == LayoutResourceKind::Uniform)
- {
- if (elementInfo.alignment < 16)
- elementInfo.alignment = 16;
- }
- return Super::GetArrayLayout(elementInfo, elementCount);
- }
-
- UniformLayoutInfo BeginStructLayout() override
- {
- return UniformLayoutInfo(0, 16);
- }
-
- // HLSL layout rules do *not* impose additional alignment
- // constraints on vectors (e.g., all of `float`, `float2`,
- // `float3`, and `float4` have 4-byte alignment), but instead
- // they impose a rule that any `struct` field must not
- // "straddle" a 16-byte boundary.
- //
- // This has the effect of making it *look* like `float4`
- // values have 16-byte alignment in practice, but the
- // effects on `float2` and `float3` are more nuanched and
- // lead to different result than the GLSL rules.
- //
- LayoutSize 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);
-
- LayoutSize fieldOffset = ioStructInfo->size;
- LayoutSize 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
-{
- // HLSL structured buffers drop the restrictions added for constant buffers,
- // but retain the rules around not adjusting the size of an array or
- // structure to its alignment. In this way they should match our
- // default layout rules.
-};
-
-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) override
- {
- // Assume that all scalars take up one "slot"
- return SimpleLayoutInfo(
- getKind(),
- 1);
- }
-
- SimpleLayoutInfo GetVectorLayout(SimpleLayoutInfo, size_t) 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) override
- {
- // Assume that all scalars take up one "slot"
- return SimpleLayoutInfo(
- getKind(),
- 1);
- }
-
- SimpleLayoutInfo GetVectorLayout(SimpleLayoutInfo, 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) 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 GLSLPushConstantBufferObjectLayoutRulesImpl : GLSLObjectLayoutRulesImpl
-{
- virtual SimpleLayoutInfo GetObjectLayout(ShaderParameterKind /*kind*/) override
- {
- // Special-case the layout for a constant-buffer, because we don't
- // want it to allocate a descriptor-table slot
- return SimpleLayoutInfo(LayoutResourceKind::PushConstantBuffer, 1);
- }
-};
-GLSLPushConstantBufferObjectLayoutRulesImpl kGLSLPushConstantBufferObjectLayoutRulesImpl_;
-
-struct GLSLShaderRecordConstantBufferObjectLayoutRulesImpl : GLSLObjectLayoutRulesImpl
-{
- virtual SimpleLayoutInfo GetObjectLayout(ShaderParameterKind /*kind*/) override
- {
- // Special-case the layout for a constant-buffer, because we don't
- // want it to allocate a descriptor-table slot
- return SimpleLayoutInfo(LayoutResourceKind::ShaderRecord, 1);
- }
-};
-GLSLShaderRecordConstantBufferObjectLayoutRulesImpl kGLSLShaderRecordConstantBufferObjectLayoutRulesImpl_;
-
-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::RawBuffer:
- case ShaderParameterKind::Buffer:
- case ShaderParameterKind::Texture:
- return SimpleLayoutInfo(LayoutResourceKind::ShaderResource, 1);
-
- case ShaderParameterKind::MutableStructuredBuffer:
- 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:
- SLANG_UNEXPECTED("unhandled shader parameter kind");
- UNREACHABLE_RETURN(SimpleLayoutInfo());
- }
- }
-};
-HLSLObjectLayoutRulesImpl kHLSLObjectLayoutRulesImpl;
-
-// HACK: Treating ray-tracing input/output as if it was another
-// case of varying input/output when it really needs to be
-// based on byte storage/layout.
-//
-struct GLSLRayTracingLayoutRulesImpl : DefaultVaryingLayoutRulesImpl
-{
- GLSLRayTracingLayoutRulesImpl(LayoutResourceKind kind)
- : DefaultVaryingLayoutRulesImpl(kind)
- {}
-};
-struct HLSLRayTracingLayoutRulesImpl : DefaultVaryingLayoutRulesImpl
-{
- HLSLRayTracingLayoutRulesImpl(LayoutResourceKind kind)
- : DefaultVaryingLayoutRulesImpl(kind)
- {}
-};
-
-Std140LayoutRulesImpl kStd140LayoutRulesImpl;
-Std430LayoutRulesImpl kStd430LayoutRulesImpl;
-HLSLConstantBufferLayoutRulesImpl kHLSLConstantBufferLayoutRulesImpl;
-HLSLStructuredBufferLayoutRulesImpl kHLSLStructuredBufferLayoutRulesImpl;
-
-GLSLVaryingLayoutRulesImpl kGLSLVaryingInputLayoutRulesImpl(LayoutResourceKind::VertexInput);
-GLSLVaryingLayoutRulesImpl kGLSLVaryingOutputLayoutRulesImpl(LayoutResourceKind::FragmentOutput);
-
-GLSLRayTracingLayoutRulesImpl kGLSLRayPayloadParameterLayoutRulesImpl(LayoutResourceKind::RayPayload);
-GLSLRayTracingLayoutRulesImpl kGLSLCallablePayloadParameterLayoutRulesImpl(LayoutResourceKind::CallablePayload);
-GLSLRayTracingLayoutRulesImpl kGLSLHitAttributesParameterLayoutRulesImpl(LayoutResourceKind::HitAttributes);
-
-HLSLVaryingLayoutRulesImpl kHLSLVaryingInputLayoutRulesImpl(LayoutResourceKind::VertexInput);
-HLSLVaryingLayoutRulesImpl kHLSLVaryingOutputLayoutRulesImpl(LayoutResourceKind::FragmentOutput);
-
-HLSLRayTracingLayoutRulesImpl kHLSLRayPayloadParameterLayoutRulesImpl(LayoutResourceKind::RayPayload);
-HLSLRayTracingLayoutRulesImpl kHLSLCallablePayloadParameterLayoutRulesImpl(LayoutResourceKind::CallablePayload);
-HLSLRayTracingLayoutRulesImpl kHLSLHitAttributesParameterLayoutRulesImpl(LayoutResourceKind::HitAttributes);
-
-//
-
-struct GLSLLayoutRulesFamilyImpl : LayoutRulesFamilyImpl
-{
- virtual LayoutRulesImpl* getConstantBufferRules() override;
- virtual LayoutRulesImpl* getPushConstantBufferRules() override;
- virtual LayoutRulesImpl* getTextureBufferRules() override;
- virtual LayoutRulesImpl* getVaryingInputRules() override;
- virtual LayoutRulesImpl* getVaryingOutputRules() override;
- virtual LayoutRulesImpl* getSpecializationConstantRules() override;
- virtual LayoutRulesImpl* getShaderStorageBufferRules() override;
- virtual LayoutRulesImpl* getParameterBlockRules() override;
-
- LayoutRulesImpl* getRayPayloadParameterRules() override;
- LayoutRulesImpl* getCallablePayloadParameterRules() override;
- LayoutRulesImpl* getHitAttributesParameterRules() override;
-
- LayoutRulesImpl* getShaderRecordConstantBufferRules() override;
-};
-
-struct HLSLLayoutRulesFamilyImpl : LayoutRulesFamilyImpl
-{
- virtual LayoutRulesImpl* getConstantBufferRules() override;
- virtual LayoutRulesImpl* getPushConstantBufferRules() override;
- virtual LayoutRulesImpl* getTextureBufferRules() override;
- virtual LayoutRulesImpl* getVaryingInputRules() override;
- virtual LayoutRulesImpl* getVaryingOutputRules() override;
- virtual LayoutRulesImpl* getSpecializationConstantRules() override;
- virtual LayoutRulesImpl* getShaderStorageBufferRules() override;
- virtual LayoutRulesImpl* getParameterBlockRules() override;
-
- LayoutRulesImpl* getRayPayloadParameterRules() override;
- LayoutRulesImpl* getCallablePayloadParameterRules() override;
- LayoutRulesImpl* getHitAttributesParameterRules() override;
-
- LayoutRulesImpl* getShaderRecordConstantBufferRules() override;
-};
-
-GLSLLayoutRulesFamilyImpl kGLSLLayoutRulesFamilyImpl;
-HLSLLayoutRulesFamilyImpl kHLSLLayoutRulesFamilyImpl;
-
-
-// GLSL cases
-
-LayoutRulesImpl kStd140LayoutRulesImpl_ = {
- &kGLSLLayoutRulesFamilyImpl, &kStd140LayoutRulesImpl, &kGLSLObjectLayoutRulesImpl,
-};
-
-LayoutRulesImpl kStd430LayoutRulesImpl_ = {
- &kGLSLLayoutRulesFamilyImpl, &kStd430LayoutRulesImpl, &kGLSLObjectLayoutRulesImpl,
-};
-
-LayoutRulesImpl kGLSLPushConstantLayoutRulesImpl_ = {
- &kGLSLLayoutRulesFamilyImpl, &kStd430LayoutRulesImpl, &kGLSLPushConstantBufferObjectLayoutRulesImpl_,
-};
-
-LayoutRulesImpl kGLSLShaderRecordLayoutRulesImpl_ = {
- &kGLSLLayoutRulesFamilyImpl, &kStd430LayoutRulesImpl, &kGLSLShaderRecordConstantBufferObjectLayoutRulesImpl_,
-};
-
-LayoutRulesImpl kGLSLVaryingInputLayoutRulesImpl_ = {
- &kGLSLLayoutRulesFamilyImpl, &kGLSLVaryingInputLayoutRulesImpl, &kGLSLObjectLayoutRulesImpl,
-};
-
-LayoutRulesImpl kGLSLVaryingOutputLayoutRulesImpl_ = {
- &kGLSLLayoutRulesFamilyImpl, &kGLSLVaryingOutputLayoutRulesImpl, &kGLSLObjectLayoutRulesImpl,
-};
-
-LayoutRulesImpl kGLSLSpecializationConstantLayoutRulesImpl_ = {
- &kGLSLLayoutRulesFamilyImpl, &kGLSLSpecializationConstantLayoutRulesImpl, &kGLSLObjectLayoutRulesImpl,
-};
-
-LayoutRulesImpl kGLSLRayPayloadParameterLayoutRulesImpl_ = {
- &kGLSLLayoutRulesFamilyImpl, &kGLSLRayPayloadParameterLayoutRulesImpl, &kGLSLObjectLayoutRulesImpl,
-};
-
-LayoutRulesImpl kGLSLCallablePayloadParameterLayoutRulesImpl_ = {
- &kGLSLLayoutRulesFamilyImpl, &kGLSLCallablePayloadParameterLayoutRulesImpl, &kGLSLObjectLayoutRulesImpl,
-};
-
-LayoutRulesImpl kGLSLHitAttributesParameterLayoutRulesImpl_ = {
- &kGLSLLayoutRulesFamilyImpl, &kGLSLHitAttributesParameterLayoutRulesImpl, &kGLSLObjectLayoutRulesImpl,
-};
-
-// HLSL cases
-
-LayoutRulesImpl kHLSLConstantBufferLayoutRulesImpl_ = {
- &kHLSLLayoutRulesFamilyImpl, &kHLSLConstantBufferLayoutRulesImpl, &kHLSLObjectLayoutRulesImpl,
-};
-
-LayoutRulesImpl kHLSLStructuredBufferLayoutRulesImpl_ = {
- &kHLSLLayoutRulesFamilyImpl, &kHLSLStructuredBufferLayoutRulesImpl, &kHLSLObjectLayoutRulesImpl,
-};
-
-LayoutRulesImpl kHLSLVaryingInputLayoutRulesImpl_ = {
- &kHLSLLayoutRulesFamilyImpl, &kHLSLVaryingInputLayoutRulesImpl, &kHLSLObjectLayoutRulesImpl,
-};
-
-LayoutRulesImpl kHLSLVaryingOutputLayoutRulesImpl_ = {
- &kHLSLLayoutRulesFamilyImpl, &kHLSLVaryingOutputLayoutRulesImpl, &kHLSLObjectLayoutRulesImpl,
-};
-
-LayoutRulesImpl kHLSLRayPayloadParameterLayoutRulesImpl_ = {
- &kHLSLLayoutRulesFamilyImpl, &kHLSLRayPayloadParameterLayoutRulesImpl, &kHLSLObjectLayoutRulesImpl,
-};
-
-LayoutRulesImpl kHLSLCallablePayloadParameterLayoutRulesImpl_ = {
- &kHLSLLayoutRulesFamilyImpl, &kHLSLCallablePayloadParameterLayoutRulesImpl, &kHLSLObjectLayoutRulesImpl,
-};
-
-LayoutRulesImpl kHLSLHitAttributesParameterLayoutRulesImpl_ = {
- &kHLSLLayoutRulesFamilyImpl, &kHLSLHitAttributesParameterLayoutRulesImpl, &kHLSLObjectLayoutRulesImpl,
-};
-
-//
-
-LayoutRulesImpl* GLSLLayoutRulesFamilyImpl::getConstantBufferRules()
-{
- return &kStd140LayoutRulesImpl_;
-}
-
-LayoutRulesImpl* GLSLLayoutRulesFamilyImpl::getParameterBlockRules()
-{
- // TODO: actually pick something appropriate
- return &kStd140LayoutRulesImpl_;
-}
-
-LayoutRulesImpl* GLSLLayoutRulesFamilyImpl::getPushConstantBufferRules()
-{
- return &kGLSLPushConstantLayoutRulesImpl_;
-}
-
-LayoutRulesImpl* GLSLLayoutRulesFamilyImpl::getShaderRecordConstantBufferRules()
-{
- return &kGLSLShaderRecordLayoutRulesImpl_;
-}
-
-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* GLSLLayoutRulesFamilyImpl::getRayPayloadParameterRules()
-{
- return &kGLSLRayPayloadParameterLayoutRulesImpl_;
-}
-
-LayoutRulesImpl* GLSLLayoutRulesFamilyImpl::getCallablePayloadParameterRules()
-{
- return &kGLSLCallablePayloadParameterLayoutRulesImpl_;
-}
-
-LayoutRulesImpl* GLSLLayoutRulesFamilyImpl::getHitAttributesParameterRules()
-{
- return &kGLSLHitAttributesParameterLayoutRulesImpl_;
-}
-
-//
-
-LayoutRulesImpl* HLSLLayoutRulesFamilyImpl::getConstantBufferRules()
-{
- return &kHLSLConstantBufferLayoutRulesImpl_;
-}
-
-LayoutRulesImpl* HLSLLayoutRulesFamilyImpl::getParameterBlockRules()
-{
- // TODO: actually pick something appropriate...
- return &kHLSLConstantBufferLayoutRulesImpl_;
-}
-
-
-LayoutRulesImpl* HLSLLayoutRulesFamilyImpl::getPushConstantBufferRules()
-{
- return &kHLSLConstantBufferLayoutRulesImpl_;
-}
-
-LayoutRulesImpl* HLSLLayoutRulesFamilyImpl::getShaderRecordConstantBufferRules()
-{
- 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* HLSLLayoutRulesFamilyImpl::getRayPayloadParameterRules()
-{
- return &kHLSLRayPayloadParameterLayoutRulesImpl_;
-}
-
-LayoutRulesImpl* HLSLLayoutRulesFamilyImpl::getCallablePayloadParameterRules()
-{
- return &kHLSLCallablePayloadParameterLayoutRulesImpl_;
-}
-
-LayoutRulesImpl* HLSLLayoutRulesFamilyImpl::getHitAttributesParameterRules()
-{
- return &kHLSLHitAttributesParameterLayoutRulesImpl_;
-}
-
-
-
-//
-
-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* getDefaultLayoutRulesFamilyForTarget(TargetRequest* targetReq)
-{
- switch (targetReq->getTarget())
- {
- case CodeGenTarget::HLSL:
- case CodeGenTarget::DXBytecode:
- case CodeGenTarget::DXBytecodeAssembly:
- case CodeGenTarget::DXIL:
- case CodeGenTarget::DXILAssembly:
- return &kHLSLLayoutRulesFamilyImpl;
-
- case CodeGenTarget::GLSL:
- case CodeGenTarget::SPIRV:
- case CodeGenTarget::SPIRVAssembly:
- return &kGLSLLayoutRulesFamilyImpl;
-
-
- case CodeGenTarget::CPPSource:
- case CodeGenTarget::CSource:
- {
- // We just need to decide here what style of layout is appropriate, in terms of memory
- // and binding. That in terms of the actual binding that will be injected into functions
- // in the form of a BindContext. For now we'll go with HLSL layout -
- // that we may want to rethink that with the use of arrays and binding VK style binding might be
- // more appropriate in some ways.
-
- return &kHLSLLayoutRulesFamilyImpl;
- }
-
- default:
- return nullptr;
- }
-}
-
-TypeLayoutContext getInitialLayoutContextForTarget(TargetRequest* targetReq, ProgramLayout* programLayout)
-{
- LayoutRulesFamilyImpl* rulesFamily = getDefaultLayoutRulesFamilyForTarget(targetReq);
-
- TypeLayoutContext context;
- context.targetReq = targetReq;
- context.programLayout = programLayout;
- context.rules = nullptr;
- context.matrixLayoutMode = targetReq->getDefaultMatrixLayoutMode();
-
- if( rulesFamily )
- {
- context.rules = rulesFamily->getConstantBufferRules();
- }
-
- return context;
-}
-
-
-static LayoutSize GetElementCount(RefPtr<IntVal> val)
-{
- // Lack of a size indicates an unbounded array.
- if(!val)
- return LayoutSize::infinite();
-
- if (auto constantVal = as<ConstantIntVal>(val))
- {
- return LayoutSize(LayoutSize::RawValue(constantVal->value));
- }
- else if( auto varRefVal = as<GenericParamIntVal>(val) )
- {
- // TODO: We want to treat the case where the number of
- // elements in an array depends on a generic parameter
- // much like the case where the number of elements is
- // unbounded, *but* we can't just blindly do that because
- // an API might disallow unbounded arrays in various
- // cases where a generic bound might work (because
- // any concrete specialization will have a finite bound...)
- //
- return 0;
- }
- SLANG_UNEXPECTED("unhandled integer literal kind");
- UNREACHABLE_RETURN(LayoutSize(0));
-}
-
-bool IsResourceKind(LayoutResourceKind kind)
-{
- switch (kind)
- {
- case LayoutResourceKind::None:
- case LayoutResourceKind::Uniform:
- return false;
-
- default:
- return true;
- }
-
-}
-
- /// Create a type layout for a type that has simple layout needs.
- ///
- /// This handles any type that can express its layout in `SimpleLayoutInfo`,
- /// and that only needs a `TypeLayout` and not a refined subclass.
- ///
-static TypeLayoutResult createSimpleTypeLayout(
- SimpleLayoutInfo info,
- RefPtr<Type> type,
- LayoutRulesImpl* rules)
-{
- RefPtr<TypeLayout> typeLayout = new TypeLayout();
-
- typeLayout->type = type;
- typeLayout->rules = rules;
-
- typeLayout->uniformAlignment = info.alignment;
-
- typeLayout->addResourceUsage(info.kind, info.size);
-
- return TypeLayoutResult(typeLayout, info);
-}
-
-static SimpleLayoutInfo getParameterGroupLayoutInfo(
- RefPtr<ParameterGroupType> type,
- LayoutRulesImpl* rules)
-{
- if( as<ConstantBufferType>(type) )
- {
- return rules->GetObjectLayout(ShaderParameterKind::ConstantBuffer);
- }
- else if( as<TextureBufferType>(type) )
- {
- return rules->GetObjectLayout(ShaderParameterKind::TextureUniformBuffer);
- }
- else if( as<GLSLShaderStorageBufferType>(type) )
- {
- return rules->GetObjectLayout(ShaderParameterKind::ShaderStorageBuffer);
- }
- else if (as<ParameterBlockType>(type))
- {
- // Note: we default to consuming zero register spces here, because
- // a parameter block might not contain anything (or all it contains
- // is other blocks), and so it won't get a space allocated.
- //
- // This choice *also* means that in the case where we don't actually
- // want to allocate register spaces to blocks at all, we haven't
- // committed to that choice here.
- //
- // TODO: wouldn't it be any different to just allocate this
- // as an empty `SimpleLayoutInfo` of any other kind?
- return SimpleLayoutInfo(LayoutResourceKind::RegisterSpace, 0);
- }
-
- // TODO: the vertex-input and fragment-output cases should
- // only actually apply when we are at the appropriate stage in
- // the pipeline...
- else if( as<GLSLInputParameterGroupType>(type) )
- {
- return SimpleLayoutInfo(LayoutResourceKind::VertexInput, 0);
- }
- else if( as<GLSLOutputParameterGroupType>(type) )
- {
- return SimpleLayoutInfo(LayoutResourceKind::FragmentOutput, 0);
- }
- else
- {
- SLANG_UNEXPECTED("unhandled parameter block type");
- UNREACHABLE_RETURN(SimpleLayoutInfo());
- }
-}
-
-static bool isOpenGLTarget(TargetRequest*)
-{
- // We aren't officially supporting OpenGL right now
- return false;
-}
-
-bool isD3DTarget(TargetRequest* targetReq)
-{
- switch( targetReq->getTarget() )
- {
- case CodeGenTarget::HLSL:
- case CodeGenTarget::DXBytecode:
- case CodeGenTarget::DXBytecodeAssembly:
- case CodeGenTarget::DXIL:
- case CodeGenTarget::DXILAssembly:
- return true;
-
- default:
- return false;
- }
-}
-
-bool isKhronosTarget(TargetRequest* targetReq)
-{
- switch( targetReq->getTarget() )
- {
- default:
- return false;
-
- case CodeGenTarget::GLSL:
- case CodeGenTarget::SPIRV:
- case CodeGenTarget::SPIRVAssembly:
- return true;
- }
-}
-
-static bool isD3D11Target(TargetRequest*)
-{
- // We aren't officially supporting D3D11 right now
- return false;
-}
-
-static bool isD3D12Target(TargetRequest* targetReq)
-{
- // We are currently only officially supporting D3D12
- return isD3DTarget(targetReq);
-}
-
-
-static bool isSM5OrEarlier(TargetRequest* targetReq)
-{
- if(!isD3DTarget(targetReq))
- return false;
-
- auto profile = targetReq->getTargetProfile();
-
- if(profile.getFamily() == ProfileFamily::DX)
- {
- if(profile.GetVersion() <= ProfileVersion::DX_5_0)
- return true;
- }
-
- return false;
-}
-
-static bool isSM5_1OrLater(TargetRequest* targetReq)
-{
- if(!isD3DTarget(targetReq))
- return false;
-
- auto profile = targetReq->getTargetProfile();
-
- if(profile.getFamily() == ProfileFamily::DX)
- {
- if(profile.GetVersion() >= ProfileVersion::DX_5_1)
- return true;
- }
-
- return false;
-}
-
-static bool isVulkanTarget(TargetRequest* targetReq)
-{
- // For right now, any Khronos-related target is assumed
- // to be a Vulkan target.
- return isKhronosTarget(targetReq);
-}
-
-static bool shouldAllocateRegisterSpaceForParameterBlock(
- TypeLayoutContext const& context)
-{
- auto targetReq = context.targetReq;
-
- // We *never* want to use register spaces/sets under
- // OpenGL, D3D11, or for Shader Model 5.0 or earlier.
- if(isOpenGLTarget(targetReq) || isD3D11Target(targetReq) || isSM5OrEarlier(targetReq))
- return false;
-
- // If we know that we are targetting Vulkan, then
- // the only way to effectively use parameter blocks
- // is by using descriptor sets.
- if(isVulkanTarget(targetReq))
- return true;
-
- // If none of the above passed, then it seems like we
- // are generating code for D3D12, and using SM5.1 or later.
- // We will use a register space for parameter blocks *if*
- // the target options tell us to:
- if( isD3D12Target(targetReq) && isSM5_1OrLater(targetReq) )
- {
- return true;
- }
-
- return false;
-}
-
-// Given an existing type layout `oldTypeLayout`, apply offsets
-// to any contained fields based on the resource infos in `offsetVarLayout`.
-RefPtr<TypeLayout> applyOffsetToTypeLayout(
- RefPtr<TypeLayout> oldTypeLayout,
- RefPtr<VarLayout> offsetVarLayout)
-{
- // There is no need to apply offsets if the old type and the offset
- // don't share any resource infos in common.
- bool anyHit = false;
- for (auto oldResInfo : oldTypeLayout->resourceInfos)
- {
- if (auto offsetResInfo = offsetVarLayout->FindResourceInfo(oldResInfo.kind))
- {
- anyHit = true;
- break;
- }
- }
-
- if (!anyHit)
- return oldTypeLayout;
-
- RefPtr<TypeLayout> newTypeLayout;
- if (auto oldStructTypeLayout = oldTypeLayout.as<StructTypeLayout>())
- {
- RefPtr<StructTypeLayout> newStructTypeLayout = new StructTypeLayout();
- newStructTypeLayout->type = oldStructTypeLayout->type;
- newStructTypeLayout->uniformAlignment = oldStructTypeLayout->uniformAlignment;
-
- Dictionary<VarLayout*, VarLayout*> mapOldFieldToNew;
-
- for (auto oldField : oldStructTypeLayout->fields)
- {
- RefPtr<VarLayout> newField = new VarLayout();
- newField->varDecl = oldField->varDecl;
- newField->typeLayout = oldField->typeLayout;
- newField->flags = oldField->flags;
- newField->semanticIndex = oldField->semanticIndex;
- newField->semanticName = oldField->semanticName;
- newField->stage = oldField->stage;
- newField->systemValueSemantic = oldField->systemValueSemantic;
- newField->systemValueSemanticIndex = oldField->systemValueSemanticIndex;
-
-
- for (auto oldResInfo : oldField->resourceInfos)
- {
- auto newResInfo = newField->findOrAddResourceInfo(oldResInfo.kind);
- newResInfo->index = oldResInfo.index;
- newResInfo->space = oldResInfo.space;
- if (auto offsetResInfo = offsetVarLayout->FindResourceInfo(oldResInfo.kind))
- {
- newResInfo->index += offsetResInfo->index;
- }
- }
-
- newStructTypeLayout->fields.add(newField);
-
- mapOldFieldToNew.Add(oldField.Ptr(), newField.Ptr());
- }
-
- for (auto entry : oldStructTypeLayout->mapVarToLayout)
- {
- VarLayout* newFieldLayout = nullptr;
- if (mapOldFieldToNew.TryGetValue(entry.Value.Ptr(), newFieldLayout))
- {
- newStructTypeLayout->mapVarToLayout.Add(entry.Key, newFieldLayout);
- }
- }
-
- newTypeLayout = newStructTypeLayout;
- }
- else
- {
- // TODO: need to handle other cases here
- return oldTypeLayout;
- }
-
- // No matter what replacement we plug in for the element type, we need to copy
- // over its resource usage:
- for (auto oldResInfo : oldTypeLayout->resourceInfos)
- {
- auto newResInfo = newTypeLayout->findOrAddResourceInfo(oldResInfo.kind);
- newResInfo->count = oldResInfo.count;
- }
-
- return newTypeLayout;
-}
-
-static bool _usesResourceKind(RefPtr<TypeLayout> typeLayout, LayoutResourceKind kind)
-{
- auto resInfo = typeLayout->FindResourceInfo(kind);
- return resInfo && resInfo->count != 0;
-}
-
-static bool _usesOrdinaryData(RefPtr<TypeLayout> typeLayout)
-{
- return _usesResourceKind(typeLayout, LayoutResourceKind::Uniform);
-}
-
- /// Add resource usage from `srcTypeLayout` to `dstTypeLayout` unless it would be "masked."
- ///
- /// This function is appropriate for applying resource usage from an element type
- /// to the resource usage of a container like a `ConstantBuffer<X>` or
- /// `ParameterBlock<X>`.
- ///
-static void _addUnmaskedResourceUsage(
- TypeLayout* dstTypeLayout,
- TypeLayout* srcTypeLayout,
- bool haveFullRegisterSpaceOrSet)
-{
- for( auto resInfo : srcTypeLayout->resourceInfos )
- {
- switch( resInfo.kind )
- {
- case LayoutResourceKind::Uniform:
- // Ordinary/uniform resource usage will always be masked.
- break;
-
- case LayoutResourceKind::RegisterSpace:
- case LayoutResourceKind::ExistentialTypeParam:
- // A parameter group will always pay for full registers
- // spaces consumed by its element type.
- //
- // The same is true for existential type parameters,
- // since these need to be exposed up through the API.
- //
- dstTypeLayout->addResourceUsage(resInfo);
- break;
-
- default:
- // For all other resource kinds, a parameter group
- // will be able to mask them if and only if it
- // has a full space/set allocated to it.
- //
- // Otherwise, the resource usage of the group must
- // include the resource usage of the element.
- //
- if( !haveFullRegisterSpaceOrSet )
- {
- dstTypeLayout->addResourceUsage(resInfo);
- }
- break;
- }
- }
-}
-
-static RefPtr<TypeLayout> _createParameterGroupTypeLayout(
- TypeLayoutContext const& context,
- RefPtr<ParameterGroupType> parameterGroupType,
- RefPtr<TypeLayout> rawElementTypeLayout)
-{
- // We are being asked to create a layout for a parameter group,
- // which is curently either a `ParameterBlock<T>` or a `ConstantBuffer<T>`
- //
- auto parameterGroupRules = context.rules;
- RefPtr<ParameterGroupTypeLayout> typeLayout = new ParameterGroupTypeLayout();
- typeLayout->type = parameterGroupType;
- typeLayout->rules = parameterGroupRules;
-
- // Computing the layout is made tricky by several factors.
- //
- // A parameter group has to draw a distinction between the element type,
- // and the resources it consumes, and the "container," which main
- // consume other resources. The type of resource consumed by
- // the two can overlap.
- //
- // Consider:
- //
- // struct MyMaterial { float2 uvScale; Texture2D albedoMap; }
- // ParameterBlock<MyMaterial> gMaterial;
- //
- // In this example, `gMaterial` will need both a constant buffer
- // binding (to hold the data for `uvScale`) and a texture binding
- // (for `albedoMap`). On Vulkan, those two things require the *same*
- // `LayoutResourceKind` (representing a GLSL `binding`). We will
- // thus track the resource usage of the "container" type and
- // element type separately, and then combine these to form
- // the overall layout for the parameter group.
-
- RefPtr<TypeLayout> containerTypeLayout = new TypeLayout();
- containerTypeLayout->type = parameterGroupType;
- containerTypeLayout->rules = parameterGroupRules;
-
- // Because the container and element types will each be situated
- // at some offset relative to the initial register/binding for
- // the group as a whole, we allocate a `VarLayout` for both
- // the container and the element type, to store that offset
- // information (think of `TypeLayout`s as holding size information,
- // while `VarLayout`s hold offset information).
-
- RefPtr<VarLayout> containerVarLayout = new VarLayout();
- containerVarLayout->typeLayout = containerTypeLayout;
- typeLayout->containerVarLayout = containerVarLayout;
-
- RefPtr<VarLayout> elementVarLayout = new VarLayout();
- elementVarLayout->typeLayout = rawElementTypeLayout;
- typeLayout->elementVarLayout = elementVarLayout;
-
- // It is possible to have a `ConstantBuffer<T>` that doesn't
- // actually need a constant buffer register/binding allocated to it,
- // because the type `T` doesn't actually contain any ordinary/uniform
- // data that needs to go into the constant buffer. For example:
- //
- // struct MyMaterial { Texture2D t; SamplerState s; };
- // ConstantBuffer<MyMaterial> gMaterial;
- //
- // In this example, the `gMaterial` parameter doesn't actually need
- // a constant buffer allocated for it. This isn't something that
- // comes up often for `ConstantBuffer`, but can happen a lot for
- // `ParameterBlock`.
- //
- // To determine if we actually need a constant-buffer binding,
- // we will inspect the element type and see if it contains
- // any ordinary/uniform data.
- //
- bool wantConstantBuffer = _usesOrdinaryData(rawElementTypeLayout);
- if( wantConstantBuffer )
- {
- // If there is any ordinary data, then we'll need to
- // allocate a constant buffer regiser/binding into
- // the overall layout, to account for it.
- //
- auto cbUsage = parameterGroupRules->GetObjectLayout(ShaderParameterKind::ConstantBuffer);
- containerTypeLayout->addResourceUsage(cbUsage.kind, cbUsage.size);
- }
-
- // Similarly to how we only need a constant buffer to be allocated
- // if the contents of the group actually had ordinary/uniform data,
- // we also only want to allocate a `space` or `set` if that is really
- // required.
- //
- //
- bool canUseSpaceOrSet = false;
- //
- // We will only allocate a `space` or `set` if the type is `ParameterBlock<T>`
- // and not just `ConstantBuffer<T>`.
- //
- // Note: `parameterGroupType` is allowed to be null here, if we are allocating
- // an anonymous constant buffer for global or entry-point parameters, but that
- // is fine because the case will just return null in that case anyway.
- //
- auto parameterBlockType = as<ParameterBlockType>(parameterGroupType);
- if( parameterBlockType )
- {
- // We also can't allocate a `space` or `set` unless the compilation
- // target actually supports them.
- //
- if( shouldAllocateRegisterSpaceForParameterBlock(context) )
- {
- canUseSpaceOrSet = true;
- }
- }
-
- // Just knowing that we *can* use a `space` or `set` doesn't tell
- // us if we would *like* to.
- //
- // The basic rule here is that if the element type of the parameter
- // block contains anything that isn't itself consuming a full
- // register `space` or `set`, then we'll want an umbrella `space`/`set`
- // for all such data.
- //
- bool wantSpaceOrSet = false;
- if( canUseSpaceOrSet )
- {
- // Note that if we are allocating a constant buffer to hold
- // some ordinary/uniform data then we definitely want a space/set,
- // but we don't need to special-case that because the loop
- // here will also detect the `LayoutResourceKind::Uniform` usage.
-
- for( auto elementResourceInfo : rawElementTypeLayout->resourceInfos )
- {
- if(elementResourceInfo.kind != LayoutResourceKind::RegisterSpace)
- {
- wantSpaceOrSet = true;
- break;
- }
- }
- }
-
- // If after all that we determine that we want a register space/set,
- // then we allocate one as part of the overall resource usage for
- // the parameter group type.
- //
- if( wantSpaceOrSet )
- {
- containerTypeLayout->addResourceUsage(LayoutResourceKind::RegisterSpace, 1);
- }
-
- // Now that we've computed basic resource requirements for the container
- // part of things (i.e., does it require a constant buffer or not?),
- // let's go ahead and assign the container variable a relative offset
- // of zero for each of the kinds of resources that it consumes.
- //
- for( auto typeResInfo : containerTypeLayout->resourceInfos )
- {
- containerVarLayout->findOrAddResourceInfo(typeResInfo.kind);
- }
-
- // Because the container's resource allocation is logically coming
- // first in the overall group, the element needs to have a layout
- // such that it comes *after* the container in the relative order.
- //
- for( auto elementTypeResInfo : rawElementTypeLayout->resourceInfos )
- {
- auto kind = elementTypeResInfo.kind;
- auto elementVarResInfo = elementVarLayout->findOrAddResourceInfo(kind);
-
- // If the container part of things is using the same resource kind
- // as the element type, then the element needs to start at an offset
- // after the container.
- //
- if( auto containerTypeResInfo = containerTypeLayout->FindResourceInfo(kind) )
- {
- SLANG_RELEASE_ASSERT(containerTypeResInfo->count.isFinite());
- elementVarResInfo->index += containerTypeResInfo->count.getFiniteValue();
- }
- }
-
- // The existing Slang reflection API was created before we really
- // understood the wrinkle that the "container" and elements parts
- // of a parameter group could collide on some resource kinds,
- // so the API doesn't currently expose the nice `VarLayout`s we've
- // just computed.
- //
- // Instead, the API allows the user to query the element type layout
- // for the group, and the user just assumes that the offsetting
- // is magically applied there. To go back to the earlier example:
- //
- // struct MyMaterial { Texture2D t; SamplerState s; };
- // ConstantBuffer<MyMaterial> gMaterial;
- //
- // A user of the existing reflection API expects to be able to
- // query the `binding` of `gMaterial` and get back zero, then
- // query the `binding` of the `t` field of the element type
- // and get *one*. It is clear that in the abstract, the
- // `MyMaterial::t` field should have an offset of zero (as
- // the first field in a `struct`), so to meet the user's
- // expectations, some cleverness is needed.
- //
- // We will use a subroutine `applyOffsetToTypeLayout`
- // that tries to recursively walk an existing `TypeLayout`
- // and apply an offset to its fields. This is currently
- // quite ad hoc, but that doesn't matter much as it
- // handles `struct` types which are the 99% case for
- // parameter blocks.
- //
- typeLayout->offsetElementTypeLayout = applyOffsetToTypeLayout(rawElementTypeLayout, elementVarLayout);
-
- // Next, resource usage from the container and element
- // types may need to "bleed through" to the overall
- // parameter group type.
- //
- // If the parameter group is a `ConstantBuffer<Foo>` then
- // any ordinary/uniform bytes consumed by `Foo` are masked,
- // but any other resources it consumes (e.g. `binding`s) need
- // to bleed through and be accounted for in the overall
- // layout of the type.
- //
- // If we have a `ParameterBlock<Foo>` then any ordinary/uniform
- // bytes are masked. Furthermore, *if* a whole `space`/`set`
- // was allocated to the block, then any `register`s or
- // `binding`s consumed by `Foo` (and by the "container" constant
- // buffer if we allocated one) are also masked. Any whole
- // spaces/sets consumed by `Foo` need to bleed through.
- //
- // We can start with the easier case of the container type,
- // since it will either be empty or consume a single constant
- // buffer. Its resource usage will only bleed through if we
- // didn't allocate a full `space` or `set`.
- //
- _addUnmaskedResourceUsage(typeLayout, containerTypeLayout, wantSpaceOrSet);
-
- // next we turn to the element type, where the cases are slightly
- // more involved (technically we could use this same logic for
- // the container, as it is more general, but it was simpler to
- // just special-case the container).
- //
-
- _addUnmaskedResourceUsage(typeLayout, rawElementTypeLayout, wantSpaceOrSet);
-
- // At this point we have handled all the complexities that
- // arise for a parameter group that doesn't include interface-type
- // fields, or that doesn't include specialization for those fields.
- //
- // The remaining complexity all arises if we have interface-type
- // data in the parameter group, and we are specializing it to
- // concrete types, that will have their own layout requirements.
- // In those cases there will be "pending data" on the element
- // type layout that need to get placed somwhere, but wasn't
- // included in the layout computed so far.
- //
- // All of this is extra work we only have to do if there is
- // "pending" data in the element type layout.
- //
- if( auto pendingElementTypeLayout = rawElementTypeLayout->pendingDataTypeLayout )
- {
- auto rules = rawElementTypeLayout->rules;
-
- // One really annoying complication we need to deal with here
- // its that it is possible that the original parameter group
- // declaration didn't need a constant buffer or `space`/`set`
- // to be allocated, but once we consider the "pending" data
- // we need to have a constant buffer and/or space.
- //
- // We will compute whether the pending data create a demand
- // for a constant buffer and/or a space/set, so that we know
- // if we are in the tricky case.
- //
- bool pendingDataWantsConstantBuffer = _usesOrdinaryData(pendingElementTypeLayout);
- bool pendingDataWantsSpaceOrSet = false;
- if( canUseSpaceOrSet )
- {
- for( auto resInfo : pendingElementTypeLayout->resourceInfos )
- {
- if( resInfo.kind != LayoutResourceKind::RegisterSpace )
- {
- pendingDataWantsSpaceOrSet = true;
- break;
- }
- }
- }
-
- // We will use a few different variables to track resource
- // usage for the pending data, with roles similar to the
- // umbrella type layout, container layout, and element layout
- // that already came up for the main part of the parameter group type.
-
-
- RefPtr<TypeLayout> pendingContainerTypeLayout = new TypeLayout();
- pendingContainerTypeLayout->type = parameterGroupType;
- pendingContainerTypeLayout->rules = parameterGroupRules;
-
- containerTypeLayout->pendingDataTypeLayout = pendingContainerTypeLayout;
-
- RefPtr<VarLayout> pendingContainerVarLayout = new VarLayout();
- pendingContainerVarLayout->typeLayout = pendingContainerTypeLayout;
-
- containerVarLayout->pendingVarLayout = pendingContainerVarLayout;
-
-
- RefPtr<VarLayout> pendingElementVarLayout = new VarLayout();
- pendingElementVarLayout->typeLayout = pendingElementTypeLayout;
-
- elementVarLayout->pendingVarLayout = pendingElementVarLayout;
-
- // If we need a space/set for the pending data, and don't already
- // have one, then we will allocate it now, as part of the
- // "full" data type.
- //
- if( pendingDataWantsSpaceOrSet && !wantSpaceOrSet )
- {
- pendingContainerTypeLayout->addResourceUsage(LayoutResourceKind::RegisterSpace, 1);
-
- // From here on, we know we have access to a register space,
- // and we can mask any registers/bindings appropriately.
- //
- wantSpaceOrSet = true;
- }
-
- // If we need a constant buffer for laying out ordinary
- // data, and didn't have one allocated before, we will create
- // one.
- //
- if( pendingDataWantsConstantBuffer && !wantConstantBuffer )
- {
- auto cbUsage = rules->GetObjectLayout(ShaderParameterKind::ConstantBuffer);
- pendingContainerTypeLayout->addResourceUsage(cbUsage.kind, cbUsage.size);
-
- wantConstantBuffer = true;
- }
-
- for( auto resInfo : pendingContainerTypeLayout->resourceInfos )
- {
- pendingContainerVarLayout->findOrAddResourceInfo(resInfo.kind);
- }
-
- // Now that we've added in the resource usage for any CB or set/space
- // we needed to allocate just for the pending data, we can safely
- // lay out the pending data itself.
- //
- // The ordinary/uniform part of things wil always be "masked" and
- // needs to come after any uniform data from the original element type.
- //
- // To kick things off we will initialize state for `struct` type layout,
- // so that we can lay out the pending data as if it were the second
- // field in a structure type, after the original data.
- //
- UniformLayoutInfo uniformLayout = rules->BeginStructLayout();
- if( auto resInfo = rawElementTypeLayout->FindResourceInfo(LayoutResourceKind::Uniform) )
- {
- uniformLayout.alignment = rawElementTypeLayout->uniformAlignment;
- uniformLayout.size = resInfo->count;
- }
-
- // Now we can scan through the resources used by the pending data.
- //
- for( auto resInfo : pendingElementTypeLayout->resourceInfos )
- {
- if( resInfo.kind == LayoutResourceKind::Uniform )
- {
- // For the ordinary/uniform resource kind, we will add the resource
- // usage as a structure field, and then write the resulting offset
- // into the variable layout for the pending data.
- //
- auto offset = rules->AddStructField(
- &uniformLayout,
- UniformLayoutInfo(
- resInfo.count,
- pendingElementTypeLayout->uniformAlignment));
- pendingElementVarLayout->findOrAddResourceInfo(resInfo.kind)->index = offset.getFiniteValue();
- }
- else
- {
- // For all other resource kinds, we will set the offset in
- // the variable layout based on the total resources of that
- // kind seen so far (including the "container" if any),
- // and then bump the count for total resource usage.
- //
- auto elementVarResInfo = pendingElementVarLayout->findOrAddResourceInfo(resInfo.kind);
- if( auto containerTypeInfo = pendingContainerTypeLayout->FindResourceInfo(resInfo.kind) )
- {
- elementVarResInfo->index = containerTypeInfo->count.getFiniteValue();
- }
- }
- }
- rules->EndStructLayout(&uniformLayout);
-
- // Okay, now we have a `VarLayout` for the element data, and an overall `TypeLayout`
- // for all the data that this parameter group needs allocated for pending
- // data.
- //
- // The next major step is to compute the version of that combined resource usage
- // that will "bleed through" and thus needs to be allocated at the next level
- // up the hierarchy.
- //
- RefPtr<TypeLayout> unmaskedPendingDataTypeLayout = new TypeLayout();
- _addUnmaskedResourceUsage(unmaskedPendingDataTypeLayout, pendingContainerTypeLayout, wantSpaceOrSet);
- _addUnmaskedResourceUsage(unmaskedPendingDataTypeLayout, pendingElementTypeLayout, wantSpaceOrSet);
-
- // TODO: we should probably optimize for the case where there is no unmasked
- // usage that needs to be reported out, since it should be a common case.
-
- // Now we need to update the type layout to what we've done.
- //
- typeLayout->pendingDataTypeLayout = unmaskedPendingDataTypeLayout;
- }
-
- return typeLayout;
-}
-
- /// Do we need to wrap the given element type in a constant buffer layout?
-static bool needsConstantBuffer(RefPtr<TypeLayout> elementTypeLayout)
-{
- // We need a constant buffer if the element type has ordinary/uniform data.
- //
- if(_usesOrdinaryData(elementTypeLayout))
- return true;
-
- // We also need a constant buffer if there is any "pending"
- // data that need ordinary/uniform data allocated to them.
- //
- if(auto pendingDataTypeLayout = elementTypeLayout->pendingDataTypeLayout)
- {
- if(_usesOrdinaryData(pendingDataTypeLayout))
- return true;
- }
-
- return false;
-}
-
-RefPtr<TypeLayout> createConstantBufferTypeLayoutIfNeeded(
- TypeLayoutContext const& context,
- RefPtr<TypeLayout> elementTypeLayout)
-{
- // First things first, we need to check whether the element type
- // we are trying to lay out even needs a constant buffer allocated
- // for it.
- //
- if(!needsConstantBuffer(elementTypeLayout))
- return elementTypeLayout;
-
- auto parameterGroupRules = context.getRulesFamily()->getConstantBufferRules();
-
- return _createParameterGroupTypeLayout(
- context
- .with(parameterGroupRules)
- .with(context.targetReq->getDefaultMatrixLayoutMode()),
- nullptr,
- elementTypeLayout);
-}
-
-
-static RefPtr<TypeLayout> _createParameterGroupTypeLayout(
- TypeLayoutContext const& context,
- RefPtr<ParameterGroupType> parameterGroupType,
- RefPtr<Type> elementType,
- LayoutRulesImpl* elementTypeRules)
-{
- // We will first compute a layout for the element type of
- // the parameter group.
- //
- auto elementTypeLayout = createTypeLayout(
- context.with(elementTypeRules),
- elementType);
-
- // Now we delegate to a routine that does the meat of
- // the complicated layout logic.
- //
- return _createParameterGroupTypeLayout(
- context,
- parameterGroupType,
- elementTypeLayout);
-}
-
-LayoutRulesImpl* getParameterBufferElementTypeLayoutRules(
- RefPtr<ParameterGroupType> parameterGroupType,
- LayoutRulesImpl* rules)
-{
- if( as<ConstantBufferType>(parameterGroupType) )
- {
- return rules->getLayoutRulesFamily()->getConstantBufferRules();
- }
- else if( as<TextureBufferType>(parameterGroupType) )
- {
- return rules->getLayoutRulesFamily()->getTextureBufferRules();
- }
- else if( as<GLSLInputParameterGroupType>(parameterGroupType) )
- {
- return rules->getLayoutRulesFamily()->getVaryingInputRules();
- }
- else if( as<GLSLOutputParameterGroupType>(parameterGroupType) )
- {
- return rules->getLayoutRulesFamily()->getVaryingOutputRules();
- }
- else if( as<GLSLShaderStorageBufferType>(parameterGroupType) )
- {
- return rules->getLayoutRulesFamily()->getShaderStorageBufferRules();
- }
- else if (as<ParameterBlockType>(parameterGroupType))
- {
- return rules->getLayoutRulesFamily()->getParameterBlockRules();
- }
- else
- {
- SLANG_UNEXPECTED("uhandled parameter block type");
- return nullptr;
- }
-}
-
-RefPtr<TypeLayout> createParameterGroupTypeLayout(
- TypeLayoutContext const& context,
- RefPtr<ParameterGroupType> parameterGroupType)
-{
- auto parameterGroupRules = context.rules;
-
- // Determine the layout rules to use for the contents of the block
- auto elementTypeRules = getParameterBufferElementTypeLayoutRules(
- parameterGroupType,
- parameterGroupRules);
-
- auto elementType = parameterGroupType->elementType;
-
- return _createParameterGroupTypeLayout(
- context,
- parameterGroupType,
- elementType,
- elementTypeRules);
-}
-
-// Create a type layout for a structured buffer type.
-RefPtr<StructuredBufferTypeLayout>
-createStructuredBufferTypeLayout(
- TypeLayoutContext const& context,
- ShaderParameterKind kind,
- RefPtr<Type> structuredBufferType,
- RefPtr<TypeLayout> elementTypeLayout)
-{
- auto rules = context.rules;
- auto info = rules->GetObjectLayout(kind);
-
- auto typeLayout = new StructuredBufferTypeLayout();
-
- typeLayout->type = structuredBufferType;
- typeLayout->rules = rules;
-
- typeLayout->elementTypeLayout = elementTypeLayout;
-
- typeLayout->uniformAlignment = info.alignment;
- SLANG_RELEASE_ASSERT(!typeLayout->FindResourceInfo(LayoutResourceKind::Uniform));
- SLANG_RELEASE_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(
- TypeLayoutContext const& context,
- ShaderParameterKind kind,
- RefPtr<Type> structuredBufferType,
- RefPtr<Type> elementType)
-{
- // TODO(tfoley): we should be looking up the appropriate rules
- // via the `LayoutRulesFamily` in use here...
- auto structuredBufferLayoutRules = GetLayoutRulesImpl(
- LayoutRule::HLSLStructuredBuffer);
-
- // Create and save type layout for the buffer contents.
- auto elementTypeLayout = createTypeLayout(
- context.with(structuredBufferLayoutRules),
- elementType.Ptr());
-
- return createStructuredBufferTypeLayout(
- context,
- kind,
- structuredBufferType,
- elementTypeLayout);
-
-}
-
- /// Create layout information for the given `type`.
- ///
- /// This internal routine returns both the constructed type
- /// layout object and the simple layout info, encapsulated
- /// together as a `TypeLayoutResult`.
- ///
-static TypeLayoutResult _createTypeLayout(
- TypeLayoutContext const& context,
- Type* type);
-
- /// Create layout information for the given `type`, obeying any layout modifiers on the given declaration.
- ///
- /// If `declForModifiers` has any matrix layout modifiers associated with it, then
- /// the resulting type layout will respect those modifiers.
- ///
-static TypeLayoutResult _createTypeLayout(
- TypeLayoutContext const& context,
- Type* type,
- Decl* declForModifiers)
-{
- TypeLayoutContext subContext = context;
-
- if (declForModifiers)
- {
- // TODO: The approach implemented here has a row/column-major
- // layout model recursively affect any sub-fields (so that
- // the layout of a nested struct depends on the context where
- // it is nested). This is consistent with the GLSL behavior
- // for these modifiers, but it is *not* how HLSL is supposed
- // to work.
- //
- // In the trivial case where `row_major` and `column_major`
- // are only applied to leaf fields/variables of matrix type
- // the difference should be immaterial.
-
- if (declForModifiers->HasModifier<RowMajorLayoutModifier>())
- subContext.matrixLayoutMode = kMatrixLayoutMode_RowMajor;
-
- if (declForModifiers->HasModifier<ColumnMajorLayoutModifier>())
- subContext.matrixLayoutMode = kMatrixLayoutMode_ColumnMajor;
-
- // TODO: really need to look for other modifiers that affect
- // layout, such as GLSL `std140`.
- }
-
- return _createTypeLayout(subContext, type);
-}
-
-int findGenericParam(List<RefPtr<GenericParamLayout>> & genericParameters, GlobalGenericParamDecl * decl)
-{
- return (int)genericParameters.findFirstIndex([=](RefPtr<GenericParamLayout> & x) {return x->decl.Ptr() == decl; });
-}
-
-// When constructing a new var layout from an existing one,
-// copy fields to the new var from the old.
-void copyVarLayoutFields(
- VarLayout* dstVarLayout,
- VarLayout* srcVarLayout)
-{
- dstVarLayout->varDecl = srcVarLayout->varDecl;
- dstVarLayout->typeLayout = srcVarLayout->typeLayout;
- dstVarLayout->flags = srcVarLayout->flags;
- dstVarLayout->systemValueSemantic = srcVarLayout->systemValueSemantic;
- dstVarLayout->systemValueSemanticIndex = srcVarLayout->systemValueSemanticIndex;
- dstVarLayout->semanticName = srcVarLayout->semanticName;
- dstVarLayout->semanticIndex = srcVarLayout->semanticIndex;
- dstVarLayout->stage = srcVarLayout->stage;
- dstVarLayout->resourceInfos = srcVarLayout->resourceInfos;
-}
-
-// When constructing a new type layout from an existing one,
-// copy fields to the new type from the old.
-void copyTypeLayoutFields(
- TypeLayout* dstTypeLayout,
- TypeLayout* srcTypeLayout)
-{
- dstTypeLayout->type = srcTypeLayout->type;
- dstTypeLayout->rules = srcTypeLayout->rules;
- dstTypeLayout->uniformAlignment = srcTypeLayout->uniformAlignment;
- dstTypeLayout->resourceInfos = srcTypeLayout->resourceInfos;
-}
-
-// Does this layout resource kind require adjustment when used in
-// an array-of-structs fashion?
-bool doesResourceRequireAdjustmentForArrayOfStructs(LayoutResourceKind kind)
-{
- switch( kind )
- {
- case LayoutResourceKind::ConstantBuffer:
- case LayoutResourceKind::ShaderResource:
- case LayoutResourceKind::UnorderedAccess:
- case LayoutResourceKind::SamplerState:
- return true;
-
- default:
- return false;
- }
-}
-
-// Given the type layout for an element of an array, apply any adjustments required
-// based on the element count of the array.
-//
-// The particular case where this matters is when we have an array of an aggregate
-// type that contains resources, since each resource field might need to be at
-// a different offset than we would otherwise expect.
-//
-// For example, given:
-//
-// struct Foo { Texture2D a; Texture2D b; }
-//
-// if we just write:
-//
-// Foo foo;
-//
-// it gets split into:
-//
-// Texture2D foo_a;
-// Texture2D foo_b;
-//
-// we expect `foo_a` to get `register(t0)` and
-// `foo_b` to get `register(t1)`. However, if we instead have an array:
-//
-// Foo foo[10];
-//
-// then we expect it to be split into:
-//
-// Texture2D foo_a[8];
-// Texture2D foo_b[8];
-//
-// and then we expect `foo_b` to get `register(t8)`, rather
-// than `register(t1)`.
-//
-static RefPtr<TypeLayout> maybeAdjustLayoutForArrayElementType(
- RefPtr<TypeLayout> originalTypeLayout,
- LayoutSize elementCount,
- UInt& ioAdditionalSpacesNeeded)
-{
- // We will start by looking for cases that we can reject out
- // of hand.
-
- // If the original element type layout doesn't use any
- // resource registers, then we are fine.
- bool anyResource = false;
- for( auto resInfo : originalTypeLayout->resourceInfos )
- {
- if( doesResourceRequireAdjustmentForArrayOfStructs(resInfo.kind) )
- {
- anyResource = true;
- break;
- }
- }
- if(!anyResource)
- return originalTypeLayout;
-
- // Let's look at the type layout we have, and see if there is anything
- // that we need to do with it.
- //
- if( auto originalArrayTypeLayout = originalTypeLayout.as<ArrayTypeLayout>() )
- {
- // The element type is itself an array, so we'll need to adjust
- // *its* element type accordingly.
- //
- // We adjust the already-adjusted element type of the inner
- // array type, so that we pick up adjustments already made:
- auto originalInnerElementTypeLayout = originalArrayTypeLayout->elementTypeLayout;
- auto adjustedInnerElementTypeLayout = maybeAdjustLayoutForArrayElementType(
- originalInnerElementTypeLayout,
- elementCount,
- ioAdditionalSpacesNeeded);
-
- // If nothing needed to be changed on the inner element type,
- // then we are done.
- if(adjustedInnerElementTypeLayout == originalInnerElementTypeLayout)
- return originalTypeLayout;
-
- // Otherwise, we need to construct a new array type layout
- RefPtr<ArrayTypeLayout> adjustedArrayTypeLayout = new ArrayTypeLayout();
- adjustedArrayTypeLayout->originalElementTypeLayout = originalInnerElementTypeLayout;
- adjustedArrayTypeLayout->elementTypeLayout = adjustedInnerElementTypeLayout;
- adjustedArrayTypeLayout->uniformStride = originalArrayTypeLayout->uniformStride;
-
- copyTypeLayoutFields(adjustedArrayTypeLayout, originalArrayTypeLayout);
-
- return adjustedArrayTypeLayout;
- }
- else if(auto originalParameterGroupTypeLayout = originalTypeLayout.as<ParameterGroupTypeLayout>() )
- {
- auto originalInnerElementTypeLayout = originalParameterGroupTypeLayout->elementVarLayout->typeLayout;
- auto adjustedInnerElementTypeLayout = maybeAdjustLayoutForArrayElementType(
- originalInnerElementTypeLayout,
- elementCount,
- ioAdditionalSpacesNeeded);
-
- // If nothing needed to be changed on the inner element type,
- // then we are done.
- if(adjustedInnerElementTypeLayout == originalInnerElementTypeLayout)
- return originalTypeLayout;
-
- // TODO: actually adjust the element type, and create all the required bits and
- // pieces of layout.
-
- SLANG_UNIMPLEMENTED_X("array of parameter group");
- UNREACHABLE_RETURN(originalTypeLayout);
- }
- else if(auto originalStructTypeLayout = originalTypeLayout.as<StructTypeLayout>() )
- {
- Index fieldCount = originalStructTypeLayout->fields.getCount();
-
- // Empty struct? Bail out.
- if(fieldCount == 0)
- return originalTypeLayout;
-
- RefPtr<StructTypeLayout> adjustedStructTypeLayout = new StructTypeLayout();
- copyTypeLayoutFields(adjustedStructTypeLayout, originalStructTypeLayout);
-
- // If the array type adjustment forces us to give a whole space to
- // one or more fields, then we'll need to carefully compute the space
- // index for each field as we go.
- //
- LayoutSize nextSpaceIndex = 0;
-
- Dictionary<RefPtr<VarLayout>, RefPtr<VarLayout>> mapOriginalFieldToAdjusted;
- for( auto originalField : originalStructTypeLayout->fields )
- {
- auto originalFieldTypeLayout = originalField->typeLayout;
-
- LayoutSize originalFieldSpaceCount = 0;
- if(auto resInfo = originalFieldTypeLayout->FindResourceInfo(LayoutResourceKind::RegisterSpace))
- originalFieldSpaceCount = resInfo->count;
-
- // Compute the adjusted type for the field
- UInt fieldAdditionalSpaces = 0;
- auto adjustedFieldTypeLayout = maybeAdjustLayoutForArrayElementType(
- originalFieldTypeLayout,
- elementCount,
- fieldAdditionalSpaces);
-
- LayoutSize adjustedFieldSpaceCount = originalFieldSpaceCount + fieldAdditionalSpaces;
-
- LayoutSize spaceOffsetForField = nextSpaceIndex;
- nextSpaceIndex += adjustedFieldSpaceCount;
-
- ioAdditionalSpacesNeeded += fieldAdditionalSpaces;
-
- // Create an adjusted field variable, that is mostly
- // a clone of the original field (just with our
- // adjusted type in place).
- RefPtr<VarLayout> adjustedField = new VarLayout();
- copyVarLayoutFields(adjustedField, originalField);
- adjustedField->typeLayout = adjustedFieldTypeLayout;
-
- // We will now walk through the resource usage for
- // the adjusted field, and try to figure out what
- // to do with it all.
- //
- for(auto& resInfo : adjustedField->resourceInfos )
- {
- if( doesResourceRequireAdjustmentForArrayOfStructs(resInfo.kind) )
- {
- if(elementCount.isFinite())
- {
- // If the array size is finite, then the field's index/offset
- // is just going to be strided by the array size since we
- // are effectively doing AoS to SoA conversion.
- //
- resInfo.index *= elementCount.getFiniteValue();
- }
- else
- {
- // If we are making an unbounded array, then a `struct`
- // field with resource type will turn into its own space,
- // and it will start at register zero in that space.
- //
- resInfo.index = 0;
- resInfo.space = spaceOffsetForField.getFiniteValue();
- }
- }
- }
-
- adjustedStructTypeLayout->fields.add(adjustedField);
-
- mapOriginalFieldToAdjusted.Add(originalField, adjustedField);
- }
-
- for( auto p : originalStructTypeLayout->mapVarToLayout )
- {
- Decl* key = p.Key;
- RefPtr<VarLayout> originalVal = p.Value;
- RefPtr<VarLayout> adjustedVal;
- if( mapOriginalFieldToAdjusted.TryGetValue(originalVal, adjustedVal) )
- {
- adjustedStructTypeLayout->mapVarToLayout.Add(key, adjustedVal);
- }
- }
-
- return adjustedStructTypeLayout;
- }
- else
- {
- // In the leaf case, we must have a field that used up some resource
- // that requires adjustment. Because there is no sub-structure to work
- // with, we can just return the type layout as-is, but we also want
- // to make a note that this value should consume an additional register
- // space *if* the element count is unbounded.
- if( elementCount.isInfinite() )
- {
- ioAdditionalSpacesNeeded++;
- }
-
- return originalTypeLayout;
- }
-}
-
- /// Convert a `TypeLayout` to a `TypeLayoutResult`
- ///
- /// A `TypeLayout` holds all the data needed to make a `TypeLayoutResult` in practice,
- /// but sometimes it is more convenient to have the data split out.
- ///
-TypeLayoutResult makeTypeLayoutResult(RefPtr<TypeLayout> typeLayout)
-{
- TypeLayoutResult result;
- result.layout = typeLayout;
-
- // If the type only consumes a single kind of non-uniform resource,
- // we can fill in the `info` field directly.
- //
- if( typeLayout->resourceInfos.getCount() == 1 )
- {
- auto resInfo = typeLayout->resourceInfos[0];
- if( resInfo.kind != LayoutResourceKind::Uniform )
- {
- result.info.kind = resInfo.kind;
- result.info.size = resInfo.count;
- return result;
- }
- }
-
- // Otherwise, we will fill out the info based on the uniform
- // resources consumed, if any.
- //
- if( auto resInfo = typeLayout->FindResourceInfo(LayoutResourceKind::Uniform) )
- {
- result.info.kind = LayoutResourceKind::Uniform;
- result.info.alignment = typeLayout->uniformAlignment;
- result.info.size = resInfo->count;
- }
-
- // If there was no ordinary/uniform resource usage, then we
- // will leave the `info` field in its default state (which
- // shows no resources consumed).
- //
- // The type layout might have more detailed information, but
- // at this point it must contain either zero, or more than one
- // `ResourceInfo`, so there is nothing unambiguous we can
- // store into `info`.
-
- return result;
-}
-
-//
-// StructTypeLayoutBuilder
-//
-
-void StructTypeLayoutBuilder::beginLayout(
- Type* type,
- LayoutRulesImpl* rules)
-{
- m_rules = rules;
-
- m_typeLayout = new StructTypeLayout();
- m_typeLayout->type = type;
- m_typeLayout->rules = m_rules;
-
- m_info = m_rules->BeginStructLayout();
-}
-
-void StructTypeLayoutBuilder::beginLayoutIfNeeded(
- Type* type,
- LayoutRulesImpl* rules)
-{
- if( !m_typeLayout )
- {
- beginLayout(type, rules);
- }
-}
-
-RefPtr<VarLayout> StructTypeLayoutBuilder::addField(
- DeclRef<VarDeclBase> field,
- TypeLayoutResult fieldResult)
-{
- SLANG_ASSERT(m_typeLayout);
-
- RefPtr<TypeLayout> fieldTypeLayout = fieldResult.layout;
- UniformLayoutInfo fieldInfo = fieldResult.info.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...
- //
- LayoutSize uniformOffset = m_info.size;
- if(fieldInfo.size != 0)
- {
- uniformOffset = m_rules->AddStructField(&m_info, fieldInfo);
- }
-
-
- // We need to create variable layouts
- // for each field of the structure.
- RefPtr<VarLayout> fieldLayout = new VarLayout();
- fieldLayout->varDecl = field;
- fieldLayout->typeLayout = fieldTypeLayout;
- m_typeLayout->fields.add(fieldLayout);
-
- if( field )
- {
- m_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.getFiniteValue();
- }
-
- // 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
- SLANG_RELEASE_ASSERT(!fieldLayout->FindResourceInfo(fieldTypeResourceInfo.kind));
-
- // The field will need offset information for this kind
- auto fieldResourceInfo = fieldLayout->AddResourceInfo(fieldTypeResourceInfo.kind);
-
- // It is possible for a `struct` field to use an unbounded array
- // type, and in the D3D case that would consume an unbounded number
- // of registers. What is more, a single `struct` could have multiple
- // such fields, or ordinary resource fields after an unbounded field.
- //
- // We handle this case by allocating a distinct register space for
- // any field that consumes an unbounded amount of registers.
- //
- if( fieldTypeResourceInfo.count.isInfinite() )
- {
- // We need to add one register space to own the storage for this field.
- //
- auto structTypeSpaceResourceInfo = m_typeLayout->findOrAddResourceInfo(LayoutResourceKind::RegisterSpace);
- auto spaceOffset = structTypeSpaceResourceInfo->count;
- structTypeSpaceResourceInfo->count += 1;
-
- // The field itself will record itself as having a zero offset into
- // the chosen space.
- //
- fieldResourceInfo->space = spaceOffset.getFiniteValue();
- fieldResourceInfo->index = 0;
- }
- else
- {
- // In the case where the field consumes a finite number of slots, we
- // can simply set its offset/index to the number of such slots consumed
- // so far, and then increment the number of slots consumed by the
- // `struct` type itself.
- //
- auto structTypeResourceInfo = m_typeLayout->findOrAddResourceInfo(fieldTypeResourceInfo.kind);
- fieldResourceInfo->index = structTypeResourceInfo->count.getFiniteValue();
- structTypeResourceInfo->count += fieldTypeResourceInfo.count;
- }
- }
-
- return fieldLayout;
-}
-
-RefPtr<VarLayout> StructTypeLayoutBuilder::addField(
- DeclRef<VarDeclBase> field,
- RefPtr<TypeLayout> fieldTypeLayout)
-{
- TypeLayoutResult fieldResult = makeTypeLayoutResult(fieldTypeLayout);
- return addField(field, fieldResult);
-}
-
-void StructTypeLayoutBuilder::endLayout()
-{
- if(!m_typeLayout) return;
-
- m_rules->EndStructLayout(&m_info);
-
- m_typeLayout->uniformAlignment = m_info.alignment;
- m_typeLayout->addResourceUsage(LayoutResourceKind::Uniform, m_info.size);
-}
-
-RefPtr<StructTypeLayout> StructTypeLayoutBuilder::getTypeLayout()
-{
- return m_typeLayout;
-}
-
-TypeLayoutResult StructTypeLayoutBuilder::getTypeLayoutResult()
-{
- return TypeLayoutResult(m_typeLayout, m_info);
-}
-
-static TypeLayoutResult _createTypeLayout(
- TypeLayoutContext const& context,
- Type* type)
-{
- auto rules = context.rules;
-
- if (auto parameterGroupType = as<ParameterGroupType>(type))
- {
- // 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 = getParameterGroupLayoutInfo(parameterGroupType, 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.
- //
- auto typeLayout = createParameterGroupTypeLayout(
- context,
- parameterGroupType);
-
- return TypeLayoutResult(typeLayout, info);
- }
- else if (auto samplerStateType = as<SamplerStateType>(type))
- {
- return createSimpleTypeLayout(
- rules->GetObjectLayout(ShaderParameterKind::SamplerState),
- type,
- rules);
- }
- else if (auto textureType = as<TextureType>(type))
- {
- // 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 createSimpleTypeLayout(
- rules->GetObjectLayout(kind),
- type,
- rules);
- }
- else if (auto imageType = as<GLSLImageType>(type))
- {
- // TODO: the logic here should really be defined by the rules,
- // and not at this top level...
- ShaderParameterKind kind;
- switch( imageType->getAccess() )
- {
- default:
- kind = ShaderParameterKind::MutableImage;
- break;
-
- case SLANG_RESOURCE_ACCESS_READ:
- kind = ShaderParameterKind::Image;
- break;
- }
-
- return createSimpleTypeLayout(
- rules->GetObjectLayout(kind),
- type,
- rules);
- }
- else if (auto textureSamplerType = as<TextureSamplerType>(type))
- {
- // 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 createSimpleTypeLayout(
- rules->GetObjectLayout(kind),
- type,
- rules);
- }
-
- // TODO: need a better way to handle this stuff...
-#define CASE(TYPE, KIND) \
- else if(auto type_##TYPE = as<TYPE>(type)) do { \
- auto info = rules->GetObjectLayout(ShaderParameterKind::KIND); \
- auto typeLayout = createStructuredBufferTypeLayout( \
- context, \
- ShaderParameterKind::KIND, \
- type_##TYPE, \
- type_##TYPE->elementType.Ptr()); \
- return TypeLayoutResult(typeLayout, info); \
- } while(0)
-
- CASE(HLSLStructuredBufferType, StructuredBuffer);
- CASE(HLSLRWStructuredBufferType, MutableStructuredBuffer);
- CASE(HLSLRasterizerOrderedStructuredBufferType, MutableStructuredBuffer);
- CASE(HLSLAppendStructuredBufferType, MutableStructuredBuffer);
- CASE(HLSLConsumeStructuredBufferType, MutableStructuredBuffer);
-
-#undef CASE
-
-
- // TODO: need a better way to handle this stuff...
-#define CASE(TYPE, KIND) \
- else if(as<TYPE>(type)) do { \
- return createSimpleTypeLayout( \
- rules->GetObjectLayout(ShaderParameterKind::KIND), \
- type, rules); \
- } while(0)
-
- CASE(HLSLByteAddressBufferType, RawBuffer);
- CASE(HLSLRWByteAddressBufferType, MutableRawBuffer);
- CASE(HLSLRasterizerOrderedByteAddressBufferType, MutableRawBuffer);
-
- CASE(GLSLInputAttachmentType, InputRenderTarget);
-
- // This case is mostly to allow users to add new resource types...
- CASE(UntypedBufferResourceType, RawBuffer);
-
-#undef CASE
-
- else if(auto basicType = as<BasicExpressionType>(type))
- {
- return createSimpleTypeLayout(
- rules->GetScalarLayout(basicType->baseType),
- type,
- rules);
- }
- else if(auto vecType = as<VectorExpressionType>(type))
- {
- auto elementType = vecType->elementType;
- size_t elementCount = (size_t) GetIntVal(vecType->elementCount);
-
- auto element = _createTypeLayout(
- context,
- elementType);
-
- auto info = rules->GetVectorLayout(element.info, elementCount);
-
- RefPtr<VectorTypeLayout> typeLayout = new VectorTypeLayout();
- typeLayout->type = type;
- typeLayout->rules = rules;
- typeLayout->uniformAlignment = info.alignment;
-
- typeLayout->elementTypeLayout = element.layout;
- typeLayout->uniformStride = element.info.getUniformLayout().size.getFiniteValue();
-
- typeLayout->addResourceUsage(info.kind, info.size);
-
- return TypeLayoutResult(typeLayout, info);
- }
- else if(auto matType = as<MatrixExpressionType>(type))
- {
- size_t rowCount = (size_t) GetIntVal(matType->getRowCount());
- size_t colCount = (size_t) GetIntVal(matType->getColumnCount());
-
- auto elementType = matType->getElementType();
- auto elementResult = _createTypeLayout(
- context,
- elementType);
- auto elementTypeLayout = elementResult.layout;
- auto elementInfo = elementResult.info;
-
- // The `GetMatrixLayout` implementation in the layout rules
- // currently defaults to assuming row-major layout,
- // so if we want column-major layout we achieve it here by
- // transposing the major/minor axes counts.
- //
- size_t layoutMajorCount = rowCount;
- size_t layoutMinorCount = colCount;
- if (context.matrixLayoutMode == kMatrixLayoutMode_ColumnMajor)
- {
- size_t tmp = layoutMajorCount;
- layoutMajorCount = layoutMinorCount;
- layoutMinorCount = tmp;
- }
- auto info = rules->GetMatrixLayout(
- elementInfo,
- layoutMajorCount,
- layoutMinorCount);
-
- auto rowType = matType->getRowType();
- RefPtr<VectorTypeLayout> rowTypeLayout = new VectorTypeLayout();
-
- auto rowInfo = rules->GetVectorLayout(
- elementInfo,
- colCount);
-
- size_t majorStride = info.elementStride;
- size_t minorStride = elementInfo.getUniformLayout().size.getFiniteValue();
-
- size_t rowStride = 0;
- size_t colStride = 0;
- if(context.matrixLayoutMode == kMatrixLayoutMode_ColumnMajor)
- {
- colStride = majorStride;
- rowStride = minorStride;
- }
- else
- {
- rowStride = majorStride;
- colStride = minorStride;
- }
-
- rowTypeLayout->type = type;
- rowTypeLayout->rules = rules;
- rowTypeLayout->uniformAlignment = elementInfo.getUniformLayout().alignment;
-
- rowTypeLayout->uniformStride = colStride;
- rowTypeLayout->elementTypeLayout = elementTypeLayout;
- rowTypeLayout->addResourceUsage(rowInfo.kind, rowInfo.size);
-
- RefPtr<MatrixTypeLayout> typeLayout = new MatrixTypeLayout();
-
- typeLayout->type = type;
- typeLayout->rules = rules;
- typeLayout->uniformAlignment = info.alignment;
-
- typeLayout->elementTypeLayout = rowTypeLayout;
- typeLayout->uniformStride = rowStride;
- typeLayout->mode = context.matrixLayoutMode;
-
- typeLayout->addResourceUsage(info.kind, info.size);
-
- return TypeLayoutResult(typeLayout, info);
- }
- else if (auto arrayType = as<ArrayExpressionType>(type))
- {
- auto elementResult = _createTypeLayout(
- context,
- arrayType->baseType.Ptr());
- auto elementInfo = elementResult.info;
- auto elementTypeLayout = elementResult.layout;
-
- // To a first approximation, an array will usually be laid out
- // by taking the element's type layout and laying out `elementCount`
- // copies of it. There are of course many details that make
- // this simplistic version of things not quite work.
- //
- // An important complication to deal with is the possibility of
- // having "unbounded" arrays, which don't specify a size.'
- // The layout rules for these vary heavily by resource kind and API.
- //
-
- auto elementCount = GetElementCount(arrayType->ArrayLength);
-
- //
- // We can compute the uniform storage layout of an array using
- // the rules for the target API.
- //
- // TODO: ensure that this does something reasonable with the unbounded
- // case, or else issue an error message that the target doesn't
- // support unbounded types.
- //
-
- auto arrayUniformInfo = rules->GetArrayLayout(
- elementInfo,
- elementCount).getUniformLayout();
-
- RefPtr<ArrayTypeLayout> typeLayout = new ArrayTypeLayout();
-
- // Some parts of the array type layout object are easy to fill in:
- typeLayout->type = type;
- typeLayout->rules = rules;
- typeLayout->originalElementTypeLayout = elementTypeLayout;
- typeLayout->uniformAlignment = arrayUniformInfo.alignment;
- typeLayout->uniformStride = arrayUniformInfo.elementStride;
-
- typeLayout->addResourceUsage(LayoutResourceKind::Uniform, arrayUniformInfo.size);
-
- //
- // The tricky part in constructing an array type layout comes when
- // the element type is (or nests) a structure with resource-type
- // fields, because in that case we need to perform AoS-to-SoA
- // conversion as part of computing the final type layout, and
- // we also need to pre-compute an "adjusted" element type
- // layout that accounts for the striding that happens with
- // resource-type contents.
- //
- // This complication is only made worse when we have to deal with
- // unbounded-size arrays over such element types, since those
- // resource-type fields will each end up consuming a full space
- // in the resulting layout.
- //
- // The `maybeAdjustLayoutForArrayElementType` computes an "adjusted"
- // type layout for the element type which takes the array stride into
- // account. If it returns the same type layout that was passed in,
- // then that means no adjustement took place.
- //
- // The `additionalSpacesNeededForAdjustedElementType` variable counts
- // the number of additional register spaces that were consumed,
- // in the case of an unbounded array.
- //
- UInt additionalSpacesNeededForAdjustedElementType = 0;
- RefPtr<TypeLayout> adjustedElementTypeLayout = maybeAdjustLayoutForArrayElementType(
- elementTypeLayout,
- elementCount,
- additionalSpacesNeededForAdjustedElementType);
-
- typeLayout->elementTypeLayout = adjustedElementTypeLayout;
-
- // We will now iterate over the resources consumed by the element
- // type to compute how they contribute to the resource usage
- // of the overall array type.
- //
- for( auto elementResourceInfo : elementTypeLayout->resourceInfos )
- {
- // The uniform case was already handled above
- if( elementResourceInfo.kind == LayoutResourceKind::Uniform )
- continue;
-
- LayoutSize arrayResourceCount = 0;
-
- // In almost all cases, the resources consumed by an array
- // will be its element count times the resources consumed
- // by its element type.
- //
- // The first exception to this is arrays of resources when
- // compiling to GLSL for Vulkan, where an entire array
- // only consumes a single descriptor-table slot.
- //
- if (elementResourceInfo.kind == LayoutResourceKind::DescriptorTableSlot)
- {
- arrayResourceCount = elementResourceInfo.count;
- }
- //
- // The next big exception is when we are forming an unbounded-size
- // array and the element type got "adjusted," because that means
- // the array type will need to allocate full spaces for any resource-type
- // fields in the element type.
- //
- // Note: we carefully carve things out so that the case of a simple
- // array of resources does *not* lead to the element type being adjusted,
- // so that this logic doesn't trigger and we instead handle it with
- // the default logic below.
- //
- else if(
- elementCount.isInfinite()
- && adjustedElementTypeLayout != elementTypeLayout
- && doesResourceRequireAdjustmentForArrayOfStructs(elementResourceInfo.kind) )
- {
- // We want to ignore resource types consumed by the element type
- // that need adjustement if the array size is infinite, since
- // we will be allocating whole spaces for that part of the
- // element's resource usage.
- }
- else
- {
- arrayResourceCount = elementResourceInfo.count * elementCount;
- }
-
- // Now that we've computed how the resource usage of the element type
- // should contribute to the resource usage of the array, we can
- // add in that resource usage.
- //
- typeLayout->addResourceUsage(
- elementResourceInfo.kind,
- arrayResourceCount);
- }
-
- // The loop above to compute the resource usage of the array from its
- // element type ignored any resource-type fields in an unbounded-size
- // array if they would have been allocated as full register spaces.
- // Those same fields were counted in `additionalSpacesNeededForAdjustedElementType`,
- // and need to be added into the total resource usage for the array
- // if we skipped them as part of the loop (which happens when
- // we detect that the element type layout had been "adjusted").
- //
- if( adjustedElementTypeLayout != elementTypeLayout )
- {
- typeLayout->addResourceUsage(LayoutResourceKind::RegisterSpace, additionalSpacesNeededForAdjustedElementType);
- }
-
- return TypeLayoutResult(typeLayout, arrayUniformInfo);
- }
- else if (auto declRefType = as<DeclRefType>(type))
- {
- auto declRef = declRefType->declRef;
-
- if (auto structDeclRef = declRef.as<StructDecl>())
- {
- StructTypeLayoutBuilder typeLayoutBuilder;
- StructTypeLayoutBuilder pendingDataTypeLayoutBuilder;
-
- typeLayoutBuilder.beginLayout(type, rules);
- auto typeLayout = typeLayoutBuilder.getTypeLayout();
- for (auto field : GetFields(structDeclRef))
- {
- // Static fields shouldn't take part in layout.
- if(field.getDecl()->HasModifier<HLSLStaticModifier>())
- continue;
-
- // The fields of a `struct` type may include existential (interface)
- // types (including as nested sub-fields), and any types present
- // in those fields will need to be specialized based on the
- // input arguments being passed to `_createTypeLayout`.
- //
- // We won't know how many type slots each field consumes until
- // we process it, but we can figure out the starting index for
- // the slots its will consume by looking at the layout we've
- // computed so far.
- //
- Int baseExistentialSlotIndex = 0;
- if(auto resInfo = typeLayout->FindResourceInfo(LayoutResourceKind::ExistentialTypeParam))
- baseExistentialSlotIndex = Int(resInfo->count.getFiniteValue());
- //
- // When computing the layout for the field, we will give it access
- // to all the incoming specialized type slots that haven't already
- // been consumed/claimed by preceding fields.
- //
- auto fieldLayoutContext = context.withExistentialTypeSlotsOffsetBy(baseExistentialSlotIndex);
-
- TypeLayoutResult fieldResult = _createTypeLayout(
- fieldLayoutContext,
- GetType(field).Ptr(),
- field.getDecl());
- auto fieldTypeLayout = fieldResult.layout;
-
- auto fieldVarLayout = typeLayoutBuilder.addField(field, fieldResult);
-
- // If any of the fields of the `struct` type had existential/interface
- // type, then we need to compute a second `StructTypeLayout` that
- // represents the layout and resource using for the "pending data"
- // that this type needs to have stored somewhere, but which can't
- // be laid out in the layout of the type itself.
- //
- if(auto fieldPendingDataTypeLayout = fieldTypeLayout->pendingDataTypeLayout)
- {
- // We only create this secondary layout on-demand, so that
- // we don't end up with a bunch of empty structure type layouts
- // created for no reason.
- //
- pendingDataTypeLayoutBuilder.beginLayoutIfNeeded(type, rules);
- auto fieldPendingVarLayout = pendingDataTypeLayoutBuilder.addField(field, fieldPendingDataTypeLayout);
- fieldVarLayout->pendingVarLayout = fieldPendingVarLayout;
- }
- }
-
- typeLayoutBuilder.endLayout();
- pendingDataTypeLayoutBuilder.endLayout();
-
- if( auto pendingDataTypeLayout = pendingDataTypeLayoutBuilder.getTypeLayout() )
- {
- typeLayout->pendingDataTypeLayout = pendingDataTypeLayout;
- }
-
- return typeLayoutBuilder.getTypeLayoutResult();
- }
- else if (auto globalGenParam = declRef.as<GlobalGenericParamDecl>())
- {
- SimpleLayoutInfo info;
- info.alignment = 0;
- info.size = 0;
- info.kind = LayoutResourceKind::GenericResource;
-
- auto genParamTypeLayout = new GenericParamTypeLayout();
- // we should have already populated ProgramLayout::genericEntryPointParams list at this point,
- // so we can find the index of this generic param decl in the list
- genParamTypeLayout->type = type;
- genParamTypeLayout->paramIndex = findGenericParam(context.programLayout->globalGenericParams, genParamTypeLayout->getGlobalGenericParamDecl());
- genParamTypeLayout->rules = rules;
- genParamTypeLayout->findOrAddResourceInfo(LayoutResourceKind::GenericResource)->count += 1;
-
- return TypeLayoutResult(genParamTypeLayout, info);
- }
- else if (auto assocTypeParam = declRef.as<AssocTypeDecl>())
- {
- return createSimpleTypeLayout(
- SimpleLayoutInfo(),
- type,
- rules);
- }
- else if( auto simpleGenericParam = declRef.as<GenericTypeParamDecl>() )
- {
- // A bare generic type parameter can come up during layout
- // of a generic entry point (or an entry point nested in
- // a generic type). For now we will just pretend like
- // the fields of generic parameter type take no space,
- // since there is no reasonable way to account for them
- // in the resulting layout.
- //
- // TODO: It might be better to completely ignore generic
- // entry points during initial layout, but doing so would
- // mean that users couldn't get layout information on
- // any parameters, even those that don't depend on
- // generics.
- //
- return createSimpleTypeLayout(
- SimpleLayoutInfo(),
- type,
- rules);
- }
- else if( auto interfaceDeclRef = declRef.as<InterfaceDecl>() )
- {
- // When laying out a type that includes interface-type fields,
- // we cannot know how much space the concrete type that
- // gets stored into the field consumes.
- //
- // If we were doing layout for a typical CPU target, then
- // we could just say that each interface-type field consumes
- // some fixed number of pointers (e.g., a data pointer plus a witness
- // table pointer).
- //
- // We will borrow the intuition from that and invent a new
- // resource kind for "existential slots" which conceptually
- // represents the indirections needed to reference the
- // data to be referenced by this field.
- //
-
- RefPtr<TypeLayout> typeLayout = new TypeLayout();
- typeLayout->type = type;
- typeLayout->rules = rules;
-
- typeLayout->addResourceUsage(LayoutResourceKind::ExistentialTypeParam, 1);
- typeLayout->addResourceUsage(LayoutResourceKind::ExistentialObjectParam, 1);
-
- // If there are any concrete types available, the first one will be
- // the value that should be plugged into the slot we just introduced.
- //
- if( context.existentialTypeArgCount )
- {
- RefPtr<Type> concreteType = context.existentialTypeArgs[0].type;
-
- RefPtr<TypeLayout> concreteTypeLayout = createTypeLayout(context, concreteType);
-
- // Layout for this specialized interface type then results
- // in a type layout that tracks both the resource usage of the
- // interface type itself (just the type + value slots introduced
- // above), plus a "pending data" type that represents the value
- // conceptually pointed to by the interface-type field/variable at runtime.
- //
- typeLayout->pendingDataTypeLayout = concreteTypeLayout;
- }
-
- return TypeLayoutResult(typeLayout, SimpleLayoutInfo());
- }
- }
- else if (auto errorType = as<ErrorType>(type))
- {
- // An error type means that we encountered something we don't understand.
- //
- // We should probably inform the user with an error message here.
-
- return createSimpleTypeLayout(
- SimpleLayoutInfo(),
- type,
- rules);
- }
- else if( auto taggedUnionType = as<TaggedUnionType>(type) )
- {
- // A tagged union type needs to be laid out as the maximum
- // size of any constituent type.
- //
- // In practice, only a tagged union of uniform data will
- // work, but for now we will compute the maximum usage
- // for each resource kind for generality.
- //
- // For the uniform data we will start with a size
- // of zero and an alignment of one for our base case
- // (this is what a tagged union of no cases would consume).
- //
- UniformLayoutInfo info(0, 1);
-
- RefPtr<TaggedUnionTypeLayout> taggedUnionLayout = new TaggedUnionTypeLayout();
- taggedUnionLayout->type = type;
- taggedUnionLayout->rules = rules;
-
- // Now we iterate over the case types and see if they
- // change our computed maximum size/alignement.
- //
- for( auto caseType : taggedUnionType->caseTypes )
- {
- // Note: A tagged union type is not expected to have any existential/interface type
- // slots; the case types that are provided must be fully specialized before the union is
- // formed. Thus we don't need to mess around with existential type slots here the
- // way we do for the `struct` case.
-
- auto caseTypeResult = _createTypeLayout(context, caseType);
- RefPtr<TypeLayout> caseTypeLayout = caseTypeResult.layout;
- UniformLayoutInfo caseTypeInfo = caseTypeResult.info.getUniformLayout();
-
- info.size = maximum(info.size, caseTypeInfo.size);
- info.alignment = std::max(info.alignment, caseTypeInfo.alignment);
-
- // We need to remember the layout of the case type
- // on the final `TaggedUnionTypeLayout`.
- //
- taggedUnionLayout->caseTypeLayouts.add(caseTypeLayout);
-
- // We also need to consider contributions for other
- // resource kinds beyond uniform data.
- //
- for( auto caseResInfo : caseTypeLayout->resourceInfos )
- {
- auto unionResInfo = taggedUnionLayout->findOrAddResourceInfo(caseResInfo.kind);
- unionResInfo->count = maximum(unionResInfo->count, caseResInfo.count);
- }
- }
-
- // After we've computed the size required to hold all the
- // case types, we will allocate space for the tag field.
- //
- // TODO: This assumes the tag will always be allocated out
- // of uniform storage, which means we can't support a tagged
- // union as part of a varying input/output signature. That is
- // probably a valid limitation, but it should get enforced
- // somewhere along the way.
- //
- {
- // The tag is always a `uint` for now.
- //
- auto tagInfo = context.rules->GetScalarLayout(BaseType::UInt);
- info.size = RoundToAlignment(info.size, tagInfo.alignment);
-
- taggedUnionLayout->tagOffset = info.size;
-
- info.size += tagInfo.size;
- info.alignment = std::max(info.alignment, tagInfo.alignment);
- }
-
- // As a final step, if we are computing a full `TypeLayout`
- // we will make sure that its information on uniform layout
- // matches what we've computed in the `UniformLayoutInfo` we return.
- //
- taggedUnionLayout->findOrAddResourceInfo(LayoutResourceKind::Uniform)->count = info.size;
- taggedUnionLayout->uniformAlignment = info.alignment;
-
- return TypeLayoutResult(taggedUnionLayout, info);
- }
- else if( auto existentialSpecializedType = as<ExistentialSpecializedType>(type) )
- {
- TypeLayoutContext subContext = context.withExistentialTypeArgs(
- existentialSpecializedType->slots.args.getCount(),
- existentialSpecializedType->slots.args.getBuffer());
-
- auto baseTypeLayoutResult = _createTypeLayout(
- subContext,
- existentialSpecializedType->baseType);
-
- UniformLayoutInfo info = rules->BeginStructLayout();
- rules->AddStructField(&info, baseTypeLayoutResult.info.getUniformLayout());
-
- RefPtr<ExistentialSpecializedTypeLayout> typeLayout = new ExistentialSpecializedTypeLayout();
- typeLayout->type = type;
- typeLayout->rules = rules;
-
- RefPtr<VarLayout> pendingDataVarLayout = new VarLayout();
- if(auto pendingDataTypeLayout = baseTypeLayoutResult.layout->pendingDataTypeLayout)
- {
- for( auto pendingResInfo : pendingDataTypeLayout->resourceInfos )
- {
- auto kind = pendingResInfo.kind;
- UInt index = 0;
- if( kind == LayoutResourceKind::Uniform )
- {
- LayoutSize uniformOffset = rules->AddStructField(
- &info,
- makeTypeLayoutResult(pendingDataTypeLayout).info.getUniformLayout());
-
- index = uniformOffset.getFiniteValue();
- }
- else
- {
- if(auto primaryResInfo = baseTypeLayoutResult.layout->FindResourceInfo(kind))
- index = primaryResInfo->count.getFiniteValue();
- }
- pendingDataVarLayout->AddResourceInfo(kind)->index = index;
- }
- }
-
- typeLayout->baseTypeLayout = baseTypeLayoutResult.layout;
- typeLayout->pendingDataVarLayout = pendingDataVarLayout;
-
- return makeTypeLayoutResult(typeLayout);
- }
-
- // catch-all case in case nothing matched
- SLANG_ASSERT(!"unimplemented case in type layout");
- return createSimpleTypeLayout(
- SimpleLayoutInfo(),
- type,
- rules);
-}
-
-RefPtr<TypeLayout> getSimpleVaryingParameterTypeLayout(
- TypeLayoutContext const& context,
- Type* type,
- EntryPointParameterDirectionMask directionMask)
-{
- auto rules = context.rules;
-
- // TODO: This logic should ideally share as much
- // as possible with the `_createTypeLayout` function,
- // to avoid duplication, but we also have to deal
- // with the many ways in which varying parameter
- // layout differs from non-varying layout.
-
- // We will compute resource consumption for the type
- // as a varying input, output, or both/neither.
- // To avoid duplication, we'll build an array that
- // includes all the layout rules we need to apply.
- //
- int varyingRulesCount = 0;
- LayoutRulesImpl* varyingRules[2];
-
- if( directionMask & kEntryPointParameterDirection_Input )
- {
- varyingRules[varyingRulesCount++] = context.getRulesFamily()->getVaryingInputRules();
- }
- if( directionMask & kEntryPointParameterDirection_Output )
- {
- varyingRules[varyingRulesCount++] = context.getRulesFamily()->getVaryingOutputRules();
- }
-
- if(auto basicType = as<BasicExpressionType>(type))
- {
- auto baseType = basicType->baseType;
-
- RefPtr<TypeLayout> typeLayout = new TypeLayout();
- typeLayout->type = type;
- typeLayout->rules = rules;
-
- for( int rr = 0; rr < varyingRulesCount; ++rr )
- {
- auto info = varyingRules[rr]->GetScalarLayout(baseType);
- typeLayout->addResourceUsage(info.kind, info.size);
- }
-
- return typeLayout;
- }
- else if(auto vecType = as<VectorExpressionType>(type))
- {
- auto elementType = vecType->elementType;
- size_t elementCount = (size_t) GetIntVal(vecType->elementCount);
-
- BaseType elementBaseType = BaseType::Void;
- if( auto elementBasicType = as<BasicExpressionType>(elementType) )
- {
- elementBaseType = elementBasicType->baseType;
- }
-
- // Note that we do *not* add any resource usage to the type
- // layout for the element type, because we currently cannot count
- // varying parameter usage at a granularity finer than
- // individual "locations."
- //
- RefPtr<TypeLayout> elementTypeLayout = new TypeLayout();
- elementTypeLayout->type = elementType;
- elementTypeLayout->rules = rules;
-
- RefPtr<VectorTypeLayout> typeLayout = new VectorTypeLayout();
- typeLayout->type = vecType;
- typeLayout->rules = rules;
- typeLayout->elementTypeLayout = elementTypeLayout;
-
- for( int rr = 0; rr < varyingRulesCount; ++rr )
- {
- auto varyingRuleSet = varyingRules[rr];
- auto elementInfo = varyingRuleSet->GetScalarLayout(elementBaseType);
- auto info = varyingRuleSet->GetVectorLayout(elementInfo, elementCount);
- typeLayout->addResourceUsage(info.kind, info.size);
- }
-
- return typeLayout;
- }
- else if(auto matType = as<MatrixExpressionType>(type))
- {
- size_t rowCount = (size_t) GetIntVal(matType->getRowCount());
- size_t colCount = (size_t) GetIntVal(matType->getColumnCount());
- auto elementType = matType->getElementType();
-
- BaseType elementBaseType = BaseType::Void;
- if( auto elementBasicType = as<BasicExpressionType>(elementType) )
- {
- elementBaseType = elementBasicType->baseType;
- }
-
- // Just as for `_createTypeLayout`, we need to handle row- and
- // column-major matrices differently, to ensure we get
- // the expected layout.
- //
- // A varying parameter with row-major layout is effectively
- // just an array of row vectors, while a column-major one
- // is just an array of column vectors.
- //
- size_t layoutMajorCount = rowCount;
- size_t layoutMinorCount = colCount;
- if (context.matrixLayoutMode == kMatrixLayoutMode_ColumnMajor)
- {
- size_t tmp = layoutMajorCount;
- layoutMajorCount = layoutMinorCount;
- layoutMinorCount = tmp;
- }
-
- RefPtr<TypeLayout> elementTypeLayout = new TypeLayout();
- elementTypeLayout->type = elementType;
- elementTypeLayout->rules = rules;
-
- RefPtr<VectorTypeLayout> rowTypeLayout = new VectorTypeLayout();
- rowTypeLayout->type = matType->getRowType();
- rowTypeLayout->rules = rules;
- rowTypeLayout->elementTypeLayout = elementTypeLayout;
-
- RefPtr<MatrixTypeLayout> typeLayout = new MatrixTypeLayout();
- typeLayout->type = type;
- typeLayout->rules = rules;
- typeLayout->elementTypeLayout = rowTypeLayout;
- typeLayout->mode = context.matrixLayoutMode;
-
- for( int rr = 0; rr < varyingRulesCount; ++rr )
- {
- auto varyingRuleSet = varyingRules[rr];
- auto elementInfo = varyingRuleSet->GetScalarLayout(elementBaseType);
-
- auto info = varyingRuleSet->GetMatrixLayout(elementInfo, layoutMajorCount, layoutMinorCount);
- typeLayout->addResourceUsage(info.kind, info.size);
-
- if(context.matrixLayoutMode == kMatrixLayoutMode_RowMajor)
- {
- // For row-major matrices only, we can compute an effective
- // resource usage for the row type.
- auto rowInfo = varyingRuleSet->GetVectorLayout(elementInfo, colCount);
- rowTypeLayout->addResourceUsage(rowInfo.kind, rowInfo.size);
- }
- }
-
- return typeLayout;
- }
-
- // catch-all case in case nothing matched
- SLANG_ASSERT(!"unimplemented case for varying parameter layout");
- return createSimpleTypeLayout(
- SimpleLayoutInfo(),
- type,
- rules).layout;
-}
-
-RefPtr<TypeLayout> createTypeLayout(
- TypeLayoutContext const& context,
- Type* type)
-{
- return _createTypeLayout(context, type).layout;
-}
-
-void TypeLayout::addResourceUsageFrom(TypeLayout* otherTypeLayout)
-{
- for(auto resInfo : otherTypeLayout->resourceInfos)
- addResourceUsage(resInfo);
-}
-
-
-RefPtr<TypeLayout> TypeLayout::unwrapArray()
-{
- TypeLayout* typeLayout = this;
-
- while(auto arrayTypeLayout = as<ArrayTypeLayout>(typeLayout))
- typeLayout = arrayTypeLayout->elementTypeLayout;
-
- return typeLayout;
-}
-
-
-RefPtr<GlobalGenericParamDecl> GenericParamTypeLayout::getGlobalGenericParamDecl()
-{
- auto declRefType = as<DeclRefType>(type);
- SLANG_ASSERT(declRefType);
- auto rsDeclRef = declRefType->declRef.as<GlobalGenericParamDecl>();
- return rsDeclRef.getDecl();
-}
-
-} // namespace Slang