From fdf061e278720ec066a1fac8f1f35a22e817bf2d Mon Sep 17 00:00:00 2001 From: Yong He Date: Thu, 21 Nov 2024 14:07:23 -0800 Subject: Add datalayout for constant buffers. (#5608) * Add datalayout for constant buffers. * Fixes. * Fix test. * Fix glsl codegen. * Update spirv-specific doc. * Fix test. * Fix binding in the presense of specialization constants. * address comments. * Add a test for constant buffer layout. --- docs/user-guide/a2-01-spirv-target-specific.md | 147 +++++++++++---------- source/slang/core.meta.slang | 2 +- source/slang/hlsl.meta.slang | 9 +- source/slang/slang-ast-builder.cpp | 37 +++++- source/slang/slang-ast-builder.h | 13 +- source/slang/slang-ast-type.h | 31 +++++ source/slang/slang-check-decl.cpp | 43 ++++-- source/slang/slang-check-impl.h | 2 + source/slang/slang-check-shader.cpp | 2 + source/slang/slang-check-type.cpp | 7 + source/slang/slang-emit-glsl.cpp | 27 +++- source/slang/slang-emit-hlsl.cpp | 7 + source/slang/slang-emit.cpp | 1 + source/slang/slang-ir-collect-global-uniforms.cpp | 4 +- source/slang/slang-ir-entry-point-uniforms.cpp | 11 +- source/slang/slang-ir-entry-point-uniforms.h | 1 + source/slang/slang-ir-insts.h | 2 +- source/slang/slang-ir-legalize-types.cpp | 23 +++- .../slang/slang-ir-lower-buffer-element-type.cpp | 20 ++- .../slang/slang-ir-optix-entry-point-uniforms.cpp | 4 +- source/slang/slang-ir.cpp | 6 +- source/slang/slang-ir.h | 9 +- source/slang/slang-legalize-types.cpp | 42 ++++-- source/slang/slang-legalize-types.h | 11 +- source/slang/slang-parameter-binding.cpp | 42 ++++-- source/slang/slang-syntax.cpp | 5 + source/slang/slang-type-layout.cpp | 59 ++++++--- source/slang/slang-type-layout.h | 4 +- source/slang/slang.cpp | 4 +- tests/bugs/vk-shift-uniform-issue.slang | 2 +- .../binding-push-constant-gl.hlsl.expected | 2 +- tests/spirv/constant-buffer-layout.slang | 45 +++++++ tests/spirv/push-constant-layout.slang | 26 ++++ tests/spirv/spec-constant-space.slang | 17 +++ 34 files changed, 515 insertions(+), 152 deletions(-) create mode 100644 tests/spirv/constant-buffer-layout.slang create mode 100644 tests/spirv/push-constant-layout.slang create mode 100644 tests/spirv/spec-constant-space.slang diff --git a/docs/user-guide/a2-01-spirv-target-specific.md b/docs/user-guide/a2-01-spirv-target-specific.md index d6d1190bb..e96b81162 100644 --- a/docs/user-guide/a2-01-spirv-target-specific.md +++ b/docs/user-guide/a2-01-spirv-target-specific.md @@ -68,7 +68,7 @@ The system-value semantics are translated to the following SPIR-V code. | `SV_ViewID` | `BuiltIn ViewIndex` | | `SV_ViewportArrayIndex` | `BuiltIn ViewportIndex` | -*Note* that `SV_PointSize` is a unique keyword that HLSL doesn't have. +*Note* that `SV_PointSize` is a Slang-specific semantic that is not defined in HLSL. Behavior of `discard` after SPIR-V 1.6 @@ -131,17 +131,17 @@ SPIR-V 1.5 with [SPV_EXT_shader_atomic_float16_add](https://github.com/KhronosGr | SPIR-V | Yes | Yes | Yes (SPV1.5+ext) | Yes (SPV1.5+ext) | Yes (SPV1.5+ext) | -ConstantBuffer, (RW/RasterizerOrdered)StructuredBuffer, (RW/RasterizerOrdered)ByteAddressBuffer +ConstantBuffer, StructuredBuffer and ByteAddressBuffer ----------------------------------------------------------------------------------------------- Each member in a `ConstantBuffer` will be emitted as `uniform` parameter in a uniform block. -StructuredBuffer and ByteAddressBuffer are translated to a shader storage buffer with `readonly` layout. -RWStructuredBuffer and RWByteAddressBuffer are translated to a shader storage buffer with `read-write` layout. +StructuredBuffer and ByteAddressBuffer are translated to a shader storage buffer with `readonly` access. +RWStructuredBuffer and RWByteAddressBuffer are translated to a shader storage buffer with `read-write` access. RasterizerOrderedStructuredBuffer and RasterizerOrderedByteAddressBuffer will use an extension, `SPV_EXT_fragment_shader_interlock`. -If you need to apply a different buffer layout for indivisual StructuredBuffer, you can specify the layout as a second generic argument to StructuredBuffer. E.g., StructuredBuffer, StructuredBuffer or StructuredBuffer. +If you need to apply a different buffer layout for indivisual `ConstantBuffer` or `StructuredBuffer`, you can specify the layout as a second generic argument. E.g., `ConstantBuffer`, `StructuredBuffer`, `StructuredBuffer` or `StructuredBuffer`. -Note that there are compiler options, "-fvk-use-scalar-layout" and "-force-glsl-scalar-layout". +Note that there are compiler options, "-fvk-use-scalar-layout" / "-force-glsl-scalar-layout" and "-fvk-use-dx-layout". These options do the same but they are applied globally. @@ -149,9 +149,11 @@ ParameterBlock for SPIR-V target -------------------------------- `ParameterBlock` is a Slang generic type for binding uniform parameters. -It is similar to `ConstantBuffer` in HLSL, and `ParameterBlock` can include not only constant parameters but also descriptors such as Texture2D or StructuredBuffer. +In contrast to `ConstantBuffer`, a `ParameterBlock` introduces a new descriptor set ID for resource/sampler handles defined in the element type `T`. -`ParameterBlock` is designed specifically for d3d/vulkan/metal, so that parameters are laid out more naturally on these platforms. For Vulkan, when a ParameterBlock doesn't contain nested parameter block fields, it always maps to a single descriptor set, with a dedicated set number and every resources is placed into the set with binding index starting from 0. +`ParameterBlock` is designed specifically for D3D12/Vulkan/Metal/WebGPU, so that parameters defined in `T` can be placed into an independent descriptor table/descriptor set/argument buffer/binding group. + +For example, when targeting Vulkan, when a ParameterBlock doesn't contain nested parameter block fields, it will always map to a single descriptor set, with a dedicated set number and every resources is placed into the set with binding index starting from 0. This allows the user application to create and pre-populate the descriptor set and reuse it during command encoding, without explicilty specifying the binding index for each individual parameter. When both ordinary data fields and resource typed fields exist in a parameter block, all ordinary data fields will be grouped together into a uniform buffer and appear as a binding 0 of the resulting descriptor set. @@ -160,8 +162,8 @@ Push Constants --------------------- By default, a `uniform` parameter defined in the parameter list of an entrypoint function is translated to a push constant in SPIRV, if the type of the parameter is ordinary data type (no resources/textures). -All `uniform` parameter defined in global scope are grouped together and placed in a default constant bbuffer. You can make a global uniform parameter laid out as a push constant by using the `[vk::push_constant]` attribute -on the uniform parameter. +All `uniform` parameters defined in global scope are grouped together and placed in a default constant buffer. You can make a global uniform parameter laid out as a push constant by using the `[vk::push_constant]` attribute +on the uniform parameter. All push constants follow the std430 layout by default. Specialization Constants ------------------------ @@ -184,65 +186,6 @@ Alternatively, the GLSL `layout` syntax is also supported by Slang: layout(constant_id = 1) const int MyConst = 1; ``` -SPIR-V specific Compiler options --------------------------------- - -The following compiler options are specific to SPIR-V. - -### -emit-spirv-directly -Generate SPIR-V output directly (default) -It cannot be used with -emit-spirv-via-glsl - -### -emit-spirv-via-glsl -Generate SPIR-V output by compiling to glsl source first, then use glslang compiler to produce SPIRV from the glsl. -It cannot be used with -emit-spirv-directly - -### -g -Include debug information in the generated code, where possible. -When targeting SPIR-V, this option emits [SPIR-V NonSemantic Shader DebugInfo Instructions](https://github.com/KhronosGroup/SPIRV-Registry/blob/main/nonsemantic/NonSemantic.Shader.DebugInfo.100.asciidoc). - -### -O -Set the optimization level. -Under `-O0` option, Slang will not perform extensive inlining for all functions calls, instead it will preserve the call graph as much as possible to help with understanding the SPIRV structure and diagnosing any downstream toolchain issues. - -### -fvk-{b|s|t|u}-shift -For example '-fvk-b-shift ' shifts by N the inferred binding -numbers for all resources in 'b' registers of space . For a resource attached with :register(bX, ) -but not [vk::binding(...)], sets its Vulkan descriptor set to and binding number to X + N. If you need to -shift the inferred binding numbers for more than one space, provide more than one such option. If more than one -such option is provided for the same space, the last one takes effect. If you need to shift the inferred binding -numbers for all sets, use 'all' as . - -For more information, see the following pages: - - [DXC description](https://github.com/Microsoft/DirectXShaderCompiler/blob/main/docs/SPIR-V.rst#implicit-binding-number-assignment) - - [GLSL wiki](https://github.com/KhronosGroup/glslang/wiki/HLSL-FAQ#auto-mapped-binding-numbers) - -### -fvk-bind-globals -Places the $Globals cbuffer at descriptor set and binding . -It lets you specify the descriptor for the source at a certain register. - -For more information, see the following pages: - - [DXC description](https://github.com/Microsoft/DirectXShaderCompiler/blob/main/docs/SPIR-V.rst#hlsl-global-variables-and-vulkan-binding) - -### -fvk-use-scalar-layout, -force-glsl-scalar-layout -Make data accessed through ConstantBuffer, ParameterBlock, StructuredBuffer, ByteAddressBuffer and general pointers follow the 'scalar' layout when targeting GLSL or SPIRV. - -### -fvk-use-gl-layout -Use std430 layout instead of D3D buffer layout for raw buffer load/stores. - -### -fvk-use-dx-layout -Pack members using FXCs member packing rules when targeting GLSL or SPIRV. - -### -fvk-use-entrypoint-name -Uses the entrypoint name from the source instead of 'main' in the spirv output. - -### -fspv-reflect -Include reflection decorations in the resulting SPIRV for shader parameters. - -### -spirv-core-grammar -A path to a specific spirv.core.grammar.json to use when generating SPIR-V output - - SPIR-V specific Attributes -------------------------- @@ -291,13 +234,13 @@ When there are more than one entry point, the default behavior will prevent a sh To generate a valid SPIR-V with multiple entry points, use `-fvk-use-entrypoint-name` compiler option to disable the renaming behavior and preserve the entry point names. -Memory pointer is experimental +Global memory pointers ------------------------------ -Slang supports memory pointers when targetting SPIRV. See [an example and explanation](convenience-features.html#pointers-limited). +Slang supports global memory pointers when targetting SPIRV. See [an example and explanation](convenience-features.html#pointers-limited). -When a memory pointer points to a physical memory location, the pointer will be translated to a PhysicalStorageBuffer storage class in SPIRV. -When a slang module uses a pointer, the resulting SPIRV will be using the SpvAddressingModelPhysicalStorageBuffer64 addressing mode. Modules with pointers but they don't point to a physical memory location will use SpvAddressingModelLogical addressing mode. +`float4*` in user code will be translated to a pointer in PhysicalStorageBuffer storage class in SPIRV. +When a slang module uses a pointer type, the resulting SPIRV will be using the SpvAddressingModelPhysicalStorageBuffer64 addressing mode. Modules without use of pointers will use SpvAddressingModelLogical addressing mode. Matrix type translation @@ -409,4 +352,62 @@ void main() { This behavior is same to [how DXC translates Hull shader from HLSL to SPIR-V](https://github.com/Microsoft/DirectXShaderCompiler/blob/main/docs/SPIR-V.rst#patch-constant-function). +SPIR-V specific Compiler options +-------------------------------- + +The following compiler options are specific to SPIR-V. + +### -emit-spirv-directly +Generate SPIR-V output directly (default) +It cannot be used with -emit-spirv-via-glsl + +### -emit-spirv-via-glsl +Generate SPIR-V output by compiling to glsl source first, then use glslang compiler to produce SPIRV from the glsl. +It cannot be used with -emit-spirv-directly + +### -g +Include debug information in the generated code, where possible. +When targeting SPIR-V, this option emits [SPIR-V NonSemantic Shader DebugInfo Instructions](https://github.com/KhronosGroup/SPIRV-Registry/blob/main/nonsemantic/NonSemantic.Shader.DebugInfo.100.asciidoc). + +### -O +Set the optimization level. +Under `-O0` option, Slang will not perform extensive inlining for all functions calls, instead it will preserve the call graph as much as possible to help with understanding the SPIRV structure and diagnosing any downstream toolchain issues. + +### -fvk-{b|s|t|u}-shift +For example '-fvk-b-shift ' shifts by N the inferred binding +numbers for all resources in 'b' registers of space . For a resource attached with :register(bX, ) +but not [vk::binding(...)], sets its Vulkan descriptor set to and binding number to X + N. If you need to +shift the inferred binding numbers for more than one space, provide more than one such option. If more than one +such option is provided for the same space, the last one takes effect. If you need to shift the inferred binding +numbers for all sets, use 'all' as . + +For more information, see the following pages: + - [DXC description](https://github.com/Microsoft/DirectXShaderCompiler/blob/main/docs/SPIR-V.rst#implicit-binding-number-assignment) + - [GLSL wiki](https://github.com/KhronosGroup/glslang/wiki/HLSL-FAQ#auto-mapped-binding-numbers) + +### -fvk-bind-globals +Places the $Globals cbuffer at descriptor set and binding . +It lets you specify the descriptor for the source at a certain register. + +For more information, see the following pages: + - [DXC description](https://github.com/Microsoft/DirectXShaderCompiler/blob/main/docs/SPIR-V.rst#hlsl-global-variables-and-vulkan-binding) + +### -fvk-use-scalar-layout, -force-glsl-scalar-layout +Make data accessed through ConstantBuffer, ParameterBlock, StructuredBuffer, ByteAddressBuffer and general pointers follow the 'scalar' layout when targeting GLSL or SPIRV. + +### -fvk-use-gl-layout +Use std430 layout instead of D3D buffer layout for raw buffer load/stores. + +### -fvk-use-dx-layout +Pack members using FXCs member packing rules when targeting GLSL or SPIRV. + +### -fvk-use-entrypoint-name +Uses the entrypoint name from the source instead of 'main' in the spirv output. + +### -fspv-reflect +Include reflection decorations in the resulting SPIRV for shader parameters. + +### -spirv-core-grammar +A path to a specific spirv.core.grammar.json to use when generating SPIR-V output + diff --git a/source/slang/core.meta.slang b/source/slang/core.meta.slang index 249a084b1..817e3c39d 100644 --- a/source/slang/core.meta.slang +++ b/source/slang/core.meta.slang @@ -1871,7 +1871,7 @@ for (int tt = 0; tt < kBaseTypeCount; ++tt) //@ public: -__generic +__generic __intrinsic_type($(kIROp_ConstantBufferType)) __magic_type(ConstantBufferType) struct ConstantBuffer {} diff --git a/source/slang/hlsl.meta.slang b/source/slang/hlsl.meta.slang index 7eb4af135..84beb3b4b 100644 --- a/source/slang/hlsl.meta.slang +++ b/source/slang/hlsl.meta.slang @@ -10,12 +10,14 @@ void __requireGLSLExtension(String extensionName); /// Represents an interface for buffer data layout. /// This interface is used as a base for defining specific data layouts for buffers. [sealed] +__magic_type(IBufferDataLayoutType) interface IBufferDataLayout { } /// @category misc_types __intrinsic_type($(kIROp_DefaultBufferLayoutType)) +__magic_type(DefaultDataLayoutType) struct DefaultDataLayout : IBufferDataLayout {}; @@ -23,6 +25,7 @@ struct DefaultDataLayout : IBufferDataLayout __intrinsic_type($(kIROp_Std140BufferLayoutType)) [require(spirv)] [require(glsl)] +__magic_type(Std140DataLayoutType) struct Std140DataLayout : IBufferDataLayout {}; @@ -30,11 +33,13 @@ struct Std140DataLayout : IBufferDataLayout __intrinsic_type($(kIROp_Std430BufferLayoutType)) [require(spirv)] [require(glsl)] +__magic_type(Std430DataLayoutType) struct Std430DataLayout : IBufferDataLayout {}; /// @category misc_types __intrinsic_type($(kIROp_ScalarBufferLayoutType)) +__magic_type(ScalarDataLayoutType) struct ScalarDataLayout : IBufferDataLayout {}; @@ -20588,7 +20593,7 @@ const char* kDynamicResourceCastableTypes[] = { "SamplerState", "SamplerComparisonState", - "ConstantBuffer", "TextureBuffer", + "ConstantBuffer", "TextureBuffer", }; for (auto typeName : kDynamicResourceCastableTypes) { @@ -20596,6 +20601,8 @@ for (auto typeName : kDynamicResourceCastableTypes) { if (strstr(typeName, "StructuredBuffer")) sb << "__generic\n"; + else if (strstr(typeName, "ConstantBuffer")) + sb << "__generic\n"; else if (strstr(typeName, "Buffer")) sb << "__generic\n"; }}}} diff --git a/source/slang/slang-ast-builder.cpp b/source/slang/slang-ast-builder.cpp index 575c7268b..6ffaee7db 100644 --- a/source/slang/slang-ast-builder.cpp +++ b/source/slang/slang-ast-builder.cpp @@ -146,6 +146,16 @@ Type* SharedASTBuilder::getDiffInterfaceType() return m_diffInterfaceType; } +Type* SharedASTBuilder::getIBufferDataLayoutType() +{ + if (!m_IBufferDataLayoutType) + { + auto decl = findMagicDecl("IBufferDataLayoutType"); + m_IBufferDataLayoutType = DeclRefType::create(m_astBuilder, makeDeclRef(decl)); + } + return m_IBufferDataLayoutType; +} + Type* SharedASTBuilder::getErrorType() { if (!m_errorType) @@ -296,6 +306,23 @@ PtrType* ASTBuilder::getPtrType(Type* valueType, AddressSpace addrSpace) return dynamicCast(getPtrType(valueType, addrSpace, "PtrType")); } +Type* ASTBuilder::getDefaultLayoutType() +{ + return getSpecializedBuiltinType({}, "DefaultDataLayoutType"); +} +Type* ASTBuilder::getStd140LayoutType() +{ + return getSpecializedBuiltinType({}, "Std140DataLayoutType"); +} +Type* ASTBuilder::getStd430LayoutType() +{ + return getSpecializedBuiltinType({}, "Std430DataLayoutType"); +} +Type* ASTBuilder::getScalarLayoutType() +{ + return getSpecializedBuiltinType({}, "ScalarDataLayoutType"); +} + // Construct the type `Out` OutType* ASTBuilder::getOutType(Type* valueType) { @@ -358,9 +385,15 @@ ArrayExpressionType* ASTBuilder::getArrayType(Type* elementType, IntVal* element getSpecializedBuiltinType(makeArrayView(args), "ArrayExpressionType")); } -ConstantBufferType* ASTBuilder::getConstantBufferType(Type* elementType) +ConstantBufferType* ASTBuilder::getConstantBufferType( + Type* elementType, + Type* layoutType, + Val* layoutWitness) { - return as(getSpecializedBuiltinType(elementType, "ConstantBufferType")); + Val* args[] = {elementType, layoutType, layoutWitness}; + + return as( + getSpecializedBuiltinType(makeArrayView(args), "ConstantBufferType")); } ParameterBlockType* ASTBuilder::getParameterBlockType(Type* elementType) diff --git a/source/slang/slang-ast-builder.h b/source/slang/slang-ast-builder.h index 22f6bb91e..bd5c9b4e3 100644 --- a/source/slang/slang-ast-builder.h +++ b/source/slang/slang-ast-builder.h @@ -39,6 +39,8 @@ public: /// Get the `IDifferentiable` type Type* getDiffInterfaceType(); + Type* getIBufferDataLayoutType(); + Type* getErrorType(); Type* getBottomType(); Type* getInitializerListType(); @@ -89,6 +91,7 @@ protected: Type* m_bottomType = nullptr; Type* m_initializerListType = nullptr; Type* m_overloadedType = nullptr; + Type* m_IBufferDataLayoutType = nullptr; // The following types are created lazily, such that part of their definition // can be in the core module. @@ -482,6 +485,11 @@ public: Type* getSpecializedBuiltinType(Type* typeParam, const char* magicTypeName); Type* getSpecializedBuiltinType(ArrayView genericArgs, const char* magicTypeName); + Type* getDefaultLayoutType(); + Type* getStd140LayoutType(); + Type* getStd430LayoutType(); + Type* getScalarLayoutType(); + Type* getInitializerListType() { return m_sharedASTBuilder->getInitializerListType(); } Type* getOverloadedType() { return m_sharedASTBuilder->getOverloadedType(); } Type* getErrorType() { return m_sharedASTBuilder->getErrorType(); } @@ -525,7 +533,10 @@ public: IntVal* colCount, IntVal* layout); - ConstantBufferType* getConstantBufferType(Type* elementType); + ConstantBufferType* getConstantBufferType( + Type* elementType, + Type* layoutType, + Val* layoutIsILayout); ParameterBlockType* getParameterBlockType(Type* elementType); diff --git a/source/slang/slang-ast-type.h b/source/slang/slang-ast-type.h index c55e0011f..c5c5de5d2 100644 --- a/source/slang/slang-ast-type.h +++ b/source/slang/slang-ast-type.h @@ -113,6 +113,36 @@ class BuiltinType : public DeclRefType SLANG_ABSTRACT_AST_CLASS(BuiltinType) }; +class DataLayoutType : public BuiltinType +{ + SLANG_ABSTRACT_AST_CLASS(DataLayoutType) +}; + +class IBufferDataLayoutType : public BuiltinType +{ + SLANG_AST_CLASS(IBufferDataLayoutType) +}; + +class DefaultDataLayoutType : public DataLayoutType +{ + SLANG_AST_CLASS(DefaultDataLayoutType) +}; + +class Std430DataLayoutType : public DataLayoutType +{ + SLANG_AST_CLASS(Std430DataLayoutType) +}; + +class Std140DataLayoutType : public DataLayoutType +{ + SLANG_AST_CLASS(Std140DataLayoutType) +}; + +class ScalarDataLayoutType : public DataLayoutType +{ + SLANG_AST_CLASS(ScalarDataLayoutType) +}; + class FeedbackType : public BuiltinType { SLANG_AST_CLASS(FeedbackType) @@ -374,6 +404,7 @@ class ParameterGroupType : public PointerLikeType class UniformParameterGroupType : public ParameterGroupType { SLANG_AST_CLASS(UniformParameterGroupType) + Type* getLayoutType(); }; class VaryingParameterGroupType : public ParameterGroupType diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp index 07d5dd1fa..251ce6a69 100644 --- a/source/slang/slang-check-decl.cpp +++ b/source/slang/slang-check-decl.cpp @@ -110,6 +110,7 @@ struct SemanticsDeclHeaderVisitor : public SemanticsDeclVisitorBase, void checkMeshOutputDecl(VarDeclBase* varDecl); void maybeApplyLayoutModifier(VarDeclBase* varDecl); void checkVarDeclCommon(VarDeclBase* varDecl); + void checkPushConstantBufferType(VarDeclBase* varDecl); void visitVarDecl(VarDecl* varDecl) { checkVarDeclCommon(varDecl); } @@ -1707,6 +1708,10 @@ void SemanticsDeclHeaderVisitor::maybeApplyLayoutModifier(VarDeclBase* varDecl) addModifier(varDecl, formatAttrib); } } + else + { + checkPushConstantBufferType(varDecl); + } } bool isSpecializationConstant(VarDeclBase* varDecl) @@ -1721,6 +1726,32 @@ bool isSpecializationConstant(VarDeclBase* varDecl) return false; } +void SemanticsDeclHeaderVisitor::checkPushConstantBufferType(VarDeclBase* varDecl) +{ + if (varDecl->findModifier()) + { + // If we see a ConstantBuffer parameter marked as "push_constant", we need + // to set its type to ConstantBuffer. + if (auto cbufferType = as(varDecl->type)) + { + if (cbufferType->getLayoutType() == m_astBuilder->getDefaultLayoutType()) + { + varDecl->type.type = getConstantBufferType( + cbufferType->getElementType(), + m_astBuilder->getStd430LayoutType()); + } + } + else if (isGlobalShaderParameter(varDecl)) + { + // If this is a global variable with [vk::push_constant] attribute, + // we need to make sure to wrap it in a `ConstantBuffer`. + // + varDecl->type.type = + getConstantBufferType(varDecl->type, m_astBuilder->getStd430LayoutType()); + } + } +} + void SemanticsDeclHeaderVisitor::checkVarDeclCommon(VarDeclBase* varDecl) { // A variable that didn't have an explicit type written must @@ -1935,20 +1966,8 @@ void SemanticsDeclHeaderVisitor::checkVarDeclCommon(VarDeclBase* varDecl) } } - if (as(varDecl->parentDecl)) { - // If this is a global variable with [vk::push_constant] attribute, - // we need to make sure to wrap it in a `ConstantBuffer`. - - if (!as(varDecl->type)) - { - if (varDecl->findModifier()) - { - varDecl->type.type = m_astBuilder->getConstantBufferType(varDecl->type); - } - } - if (getModuleDecl(varDecl)->hasModifier()) { // If we are in GLSL compatiblity mode, we want to treat all global variables diff --git a/source/slang/slang-check-impl.h b/source/slang/slang-check-impl.h index 95a6d00cc..95ec872a5 100644 --- a/source/slang/slang-check-impl.h +++ b/source/slang/slang-check-impl.h @@ -1149,6 +1149,8 @@ public: TypeExp TranslateTypeNodeForced(TypeExp const& typeExp); TypeExp TranslateTypeNode(TypeExp const& typeExp); Type* getRemovedModifierType(ModifiedType* type, ModifierVal* modifier); + Type* getConstantBufferType(Type* elementType, Type* layoutType); + DeclRefType* getExprDeclRefType(Expr* expr); /// Is `decl` usable as a static member? diff --git a/source/slang/slang-check-shader.cpp b/source/slang/slang-check-shader.cpp index 80c950c17..52a70034a 100644 --- a/source/slang/slang-check-shader.cpp +++ b/source/slang/slang-check-shader.cpp @@ -280,6 +280,8 @@ bool isUniformParameterType(Type* type) return true; if (as(type)) return true; + if (as(type)) + return true; if (auto arrayType = as(type)) return isUniformParameterType(arrayType->getElementType()); if (auto modType = as(type)) diff --git a/source/slang/slang-check-type.cpp b/source/slang/slang-check-type.cpp index 34f16751b..d9691a828 100644 --- a/source/slang/slang-check-type.cpp +++ b/source/slang/slang-check-type.cpp @@ -101,6 +101,13 @@ Type* SemanticsVisitor::getRemovedModifierType(ModifiedType* modifiedType, Modif return m_astBuilder->getModifiedType(modifiedType->getBase(), newModifiers); } +Type* SemanticsVisitor::getConstantBufferType(Type* elementType, Type* layoutType) +{ + auto iBufferDataLayoutType = m_astBuilder->getSharedASTBuilder()->getIBufferDataLayoutType(); + auto witness = isSubtype(layoutType, iBufferDataLayoutType, IsSubTypeOptions()); + return m_astBuilder->getConstantBufferType(elementType, layoutType, witness); +} + Expr* SemanticsVisitor::ExpectATypeRepr(Expr* expr) { if (auto overloadedExpr = as(expr)) diff --git a/source/slang/slang-emit-glsl.cpp b/source/slang/slang-emit-glsl.cpp index 43ab7b74e..6c525d064 100644 --- a/source/slang/slang-emit-glsl.cpp +++ b/source/slang/slang-emit-glsl.cpp @@ -478,8 +478,31 @@ void GLSLSourceEmitter::_emitGLSLParameterGroup( { // uniform is implicitly read only m_writer->emit("layout("); - m_writer->emit( - getTargetProgram()->getOptionSet().shouldUseScalarLayout() ? "scalar" : "std140"); + if (getTargetProgram()->getOptionSet().shouldUseScalarLayout()) + m_writer->emit("scalar"); + else if (auto cbufferType = as(type)) + { + switch (cbufferType->getDataLayout()->getOp()) + { + case kIROp_Std140BufferLayoutType: + m_writer->emit("std140"); + break; + case kIROp_Std430BufferLayoutType: + m_writer->emit("std430"); + break; + case kIROp_ScalarBufferLayoutType: + _requireGLSLExtension(toSlice("GL_EXT_scalar_block_layout")); + m_writer->emit("scalar"); + break; + default: + m_writer->emit("std140"); + break; + } + } + else + { + m_writer->emit("std140"); + } m_writer->emit(") uniform "); } diff --git a/source/slang/slang-emit-hlsl.cpp b/source/slang/slang-emit-hlsl.cpp index 26d37d1f5..824680b72 100644 --- a/source/slang/slang-emit-hlsl.cpp +++ b/source/slang/slang-emit-hlsl.cpp @@ -1310,6 +1310,13 @@ void HLSLSourceEmitter::emitSimpleTypeImpl(IRType* type) _emitHLSLSubpassInputType(subpassType); return; } + else if (auto cbufferType = as(type)) + { + m_writer->emit("ConstantBuffer<"); + emitType(cbufferType->getElementType()); + m_writer->emit(" >"); + return; + } else if (auto structuredBufferType = as(type)) { switch (structuredBufferType->getOp()) diff --git a/source/slang/slang-emit.cpp b/source/slang/slang-emit.cpp index 010b4bc92..326fc702d 100644 --- a/source/slang/slang-emit.cpp +++ b/source/slang/slang-emit.cpp @@ -642,6 +642,7 @@ Result linkAndOptimizeIR( // { CollectEntryPointUniformParamsOptions passOptions; + passOptions.targetReq = targetRequest; switch (target) { case CodeGenTarget::HostCPPSource: diff --git a/source/slang/slang-ir-collect-global-uniforms.cpp b/source/slang/slang-ir-collect-global-uniforms.cpp index cd03a61ef..1c833a294 100644 --- a/source/slang/slang-ir-collect-global-uniforms.cpp +++ b/source/slang/slang-ir-collect-global-uniforms.cpp @@ -155,7 +155,9 @@ struct CollectGlobalUniformParametersContext IRType* wrapperParamType = wrapperStructType; if (globalParameterGroupTypeLayout) { - auto wrapperParamGroupType = builder->getConstantBufferType(wrapperStructType); + auto wrapperParamGroupType = builder->getConstantBufferType( + wrapperStructType, + builder->getType(kIROp_DefaultBufferLayoutType)); wrapperParamType = wrapperParamGroupType; } diff --git a/source/slang/slang-ir-entry-point-uniforms.cpp b/source/slang/slang-ir-entry-point-uniforms.cpp index 0dfdc2d8e..b3073a97d 100644 --- a/source/slang/slang-ir-entry-point-uniforms.cpp +++ b/source/slang/slang-ir-entry-point-uniforms.cpp @@ -403,7 +403,16 @@ struct CollectEntryPointUniformParams : PerEntryPointPass // If we need a constant buffer, then the global // shader parameter will be a `ConstantBuffer` // - auto constantBufferType = builder.getConstantBufferType(paramStructType); + IRType* layoutType = nullptr; + + if (m_options.targetReq->getOptionSet().getBoolOption( + CompilerOptionName::GLSLForceScalarLayout)) + layoutType = builder.getType(kIROp_ScalarBufferLayoutType); + else if (isKhronosTarget(m_options.targetReq)) + layoutType = builder.getType(kIROp_Std430BufferLayoutType); + else + layoutType = builder.getType(kIROp_DefaultBufferLayoutType); + auto constantBufferType = builder.getConstantBufferType(paramStructType, layoutType); collectedParam = builder.createParam(constantBufferType); } else diff --git a/source/slang/slang-ir-entry-point-uniforms.h b/source/slang/slang-ir-entry-point-uniforms.h index 80bc0a781..21cee3c4e 100644 --- a/source/slang/slang-ir-entry-point-uniforms.h +++ b/source/slang/slang-ir-entry-point-uniforms.h @@ -12,6 +12,7 @@ struct CollectEntryPointUniformParamsOptions // TODO(JS): Not sure if it makes sense to initialize to true or false. Go with false as // seems to fit usage. bool alwaysCreateCollectedParam = false; + TargetRequest* targetReq = nullptr; }; /// Collect entry point uniform parameters into a wrapper `struct` and/or buffer diff --git a/source/slang/slang-ir-insts.h b/source/slang/slang-ir-insts.h index 97989e9ce..fb06863d4 100644 --- a/source/slang/slang-ir-insts.h +++ b/source/slang/slang-ir-insts.h @@ -3719,7 +3719,7 @@ public: return getFuncType(paramTypes.getCount(), paramTypes.getBuffer(), resultType); } - IRConstantBufferType* getConstantBufferType(IRType* elementType); + IRConstantBufferType* getConstantBufferType(IRType* elementType, IRType* layout); IRGLSLOutputParameterGroupType* getGLSLOutputParameterGroupType(IRType* valueType); diff --git a/source/slang/slang-ir-legalize-types.cpp b/source/slang/slang-ir-legalize-types.cpp index 2af3c4c09..9154277f5 100644 --- a/source/slang/slang-ir-legalize-types.cpp +++ b/source/slang/slang-ir-legalize-types.cpp @@ -4016,12 +4016,15 @@ struct IRResourceTypeLegalizationContext : IRTypeLegalizationContext bool isSimpleType(IRType*) override { return false; } - LegalType createLegalUniformBufferType(IROp op, LegalType legalElementType) override + LegalType createLegalUniformBufferType( + IROp op, + LegalType legalElementType, + IRInst* layoutOperand) override { // The appropriate strategy for legalizing uniform buffers // with resources inside already exists, so we can delegate to it. // - return createLegalUniformBufferTypeForResources(this, op, legalElementType); + return createLegalUniformBufferTypeForResources(this, op, legalElementType, layoutOperand); } }; @@ -4045,7 +4048,10 @@ struct IRExistentialTypeLegalizationContext : IRTypeLegalizationContext bool isSimpleType(IRType*) override { return false; } - LegalType createLegalUniformBufferType(IROp op, LegalType legalElementType) override + LegalType createLegalUniformBufferType( + IROp op, + LegalType legalElementType, + IRInst* layoutOperand) override { // We'll delegate the logic for creating uniform buffers // over a mix of ordinary and existential-box types to @@ -4054,7 +4060,11 @@ struct IRExistentialTypeLegalizationContext : IRTypeLegalizationContext // TODO: We should eventually try to refactor this code // so that related functionality is grouped together. // - return createLegalUniformBufferTypeForExistentials(this, op, legalElementType); + return createLegalUniformBufferTypeForExistentials( + this, + op, + legalElementType, + layoutOperand); } }; @@ -4090,7 +4100,10 @@ struct IREmptyTypeLegalizationContext : IRTypeLegalizationContext return false; } - LegalType createLegalUniformBufferType(IROp, LegalType) override { return LegalType(); } + LegalType createLegalUniformBufferType(IROp, LegalType, IRInst*) override + { + return LegalType(); + } }; // The main entry points that are used when transforming IR code diff --git a/source/slang/slang-ir-lower-buffer-element-type.cpp b/source/slang/slang-ir-lower-buffer-element-type.cpp index 2f8631e18..bd3e350bc 100644 --- a/source/slang/slang-ir-lower-buffer-element-type.cpp +++ b/source/slang/slang-ir-lower-buffer-element-type.cpp @@ -1376,7 +1376,25 @@ IRTypeLayoutRules* getTypeLayoutRuleForBuffer(TargetProgram* target, IRType* buf } case kIROp_ConstantBufferType: case kIROp_ParameterBlockType: - return IRTypeLayoutRules::getStd140(); + { + auto parameterGroupType = as(bufferType); + + auto layoutTypeOp = parameterGroupType->getDataLayout() + ? parameterGroupType->getDataLayout()->getOp() + : kIROp_DefaultBufferLayoutType; + switch (layoutTypeOp) + { + case kIROp_DefaultBufferLayoutType: + return IRTypeLayoutRules::getStd140(); + case kIROp_Std140BufferLayoutType: + return IRTypeLayoutRules::getStd140(); + case kIROp_Std430BufferLayoutType: + return IRTypeLayoutRules::getStd430(); + case kIROp_ScalarBufferLayoutType: + return IRTypeLayoutRules::getNatural(); + } + return IRTypeLayoutRules::getStd140(); + } case kIROp_PtrType: return IRTypeLayoutRules::getNatural(); } diff --git a/source/slang/slang-ir-optix-entry-point-uniforms.cpp b/source/slang/slang-ir-optix-entry-point-uniforms.cpp index f29cbe222..dfa07ca67 100644 --- a/source/slang/slang-ir-optix-entry-point-uniforms.cpp +++ b/source/slang/slang-ir-optix-entry-point-uniforms.cpp @@ -253,7 +253,9 @@ struct CollectOptixEntryPointUniformParams : PerEntryPointPass // TODO: reconcile this with OptiX, as the current logic works, but is still focused on // VK/DXR.. // - auto constantBufferType = builder.getConstantBufferType(paramStructType); + auto constantBufferType = builder.getConstantBufferType( + paramStructType, + builder.getType(kIROp_DefaultBufferLayoutType)); collectedParam = builder.createParam(constantBufferType); // The global shader parameter should have the layout diff --git a/source/slang/slang-ir.cpp b/source/slang/slang-ir.cpp index e7f82f5ad..f6c662a98 100644 --- a/source/slang/slang-ir.cpp +++ b/source/slang/slang-ir.cpp @@ -3046,10 +3046,10 @@ IRWitnessTableIDType* IRBuilder::getWitnessTableIDType(IRType* baseType) createIntrinsicInst(nullptr, kIROp_WitnessTableIDType, 1, (IRInst* const*)&baseType); } -IRConstantBufferType* IRBuilder::getConstantBufferType(IRType* elementType) +IRConstantBufferType* IRBuilder::getConstantBufferType(IRType* elementType, IRType* layoutType) { - IRInst* operands[] = {elementType}; - return (IRConstantBufferType*)getType(kIROp_ConstantBufferType, 1, operands); + IRInst* operands[] = {elementType, layoutType}; + return (IRConstantBufferType*)getType(kIROp_ConstantBufferType, 2, operands); } IRGLSLOutputParameterGroupType* IRBuilder::getGLSLOutputParameterGroupType(IRType* elementType) diff --git a/source/slang/slang-ir.h b/source/slang/slang-ir.h index 05a6fa55c..d24c2d12b 100644 --- a/source/slang/slang-ir.h +++ b/source/slang/slang-ir.h @@ -1574,7 +1574,14 @@ SIMPLE_IR_TYPE(MetalMeshGridPropertiesType, Type) SIMPLE_IR_TYPE(GLSLInputAttachmentType, Type) SIMPLE_IR_PARENT_TYPE(ParameterGroupType, PointerLikeType) -SIMPLE_IR_PARENT_TYPE(UniformParameterGroupType, ParameterGroupType) + +struct IRUniformParameterGroupType : IRParameterGroupType +{ + IR_PARENT_ISA(UniformParameterGroupType) + + IRType* getDataLayout() { return getOperandCount() > 1 ? (IRType*)getOperand(1) : nullptr; } +}; + SIMPLE_IR_PARENT_TYPE(VaryingParameterGroupType, ParameterGroupType) SIMPLE_IR_TYPE(ConstantBufferType, UniformParameterGroupType) SIMPLE_IR_TYPE(TextureBufferType, UniformParameterGroupType) diff --git a/source/slang/slang-legalize-types.cpp b/source/slang/slang-legalize-types.cpp index 6d827a412..7695ba385 100644 --- a/source/slang/slang-legalize-types.cpp +++ b/source/slang/slang-legalize-types.cpp @@ -527,12 +527,25 @@ static IRType* createBuiltinGenericType( return context->getBuilder()->getType(op, 1, operands); } +static IRType* createBuiltinGenericType( + TypeLegalizationContext* context, + IROp op, + IRType* elementType, + IRInst* layoutOperand) +{ + if (!layoutOperand) + return createBuiltinGenericType(context, op, elementType); + IRInst* operands[] = {elementType, layoutOperand}; + return context->getBuilder()->getType(op, 2, operands); +} + // Create a uniform buffer type with a given legalized // element type. static LegalType createLegalUniformBufferType( TypeLegalizationContext* context, IROp op, - LegalType legalElementType) + LegalType legalElementType, + IRInst* layoutOperand) { // We will handle some of the easy/non-interesting // cases here in the main routine, but for all @@ -543,7 +556,7 @@ static LegalType createLegalUniformBufferType( switch (legalElementType.flavor) { default: - return context->createLegalUniformBufferType(op, legalElementType); + return context->createLegalUniformBufferType(op, legalElementType, layoutOperand); case LegalType::Flavor::none: return LegalType(); @@ -558,7 +571,7 @@ static LegalType createLegalUniformBufferType( // an unlikely case in practice. // return LegalType::simple( - createBuiltinGenericType(context, op, legalElementType.getSimple())); + createBuiltinGenericType(context, op, legalElementType.getSimple(), layoutOperand)); } break; @@ -581,7 +594,8 @@ static LegalType createLegalUniformBufferType( return LegalType::implicitDeref(createLegalUniformBufferType( context, op, - legalElementType.getImplicitDeref()->valueType)); + legalElementType.getImplicitDeref()->valueType, + layoutOperand)); } break; } @@ -593,7 +607,8 @@ static LegalType createLegalUniformBufferType( LegalType createLegalUniformBufferTypeForResources( TypeLegalizationContext* context, IROp op, - LegalType legalElementType) + LegalType legalElementType, + IRInst* layoutOperand) { switch (legalElementType.flavor) { @@ -627,7 +642,8 @@ LegalType createLegalUniformBufferTypeForResources( // buffer with the appropriate `op`, so that case // is easy: // - auto ordinaryType = createLegalUniformBufferType(context, op, pairType->ordinaryType); + auto ordinaryType = + createLegalUniformBufferType(context, op, pairType->ordinaryType, layoutOperand); // For the special side, we really just want to turn // a special field of type `R` into a value of type @@ -824,7 +840,8 @@ LegalElementWrapping declareStructFields( LegalType createLegalUniformBufferTypeForExistentials( TypeLegalizationContext* context, IROp op, - LegalType legalElementType) + LegalType legalElementType, + IRInst* layoutOperand) { auto builder = context->getBuilder(); @@ -840,7 +857,7 @@ LegalType createLegalUniformBufferTypeForExistentials( // (not a `LegalType`) we can go ahead and create an // IR uniform buffer type that wraps it. // - auto bufferType = createBuiltinGenericType(context, op, structType); + auto bufferType = createBuiltinGenericType(context, op, structType, layoutOperand); // The `elementWrapping` computed when we declared all // the `struct` fields tells us how to get from the @@ -859,7 +876,11 @@ static LegalType createLegalUniformBufferType( IRUniformParameterGroupType* uniformBufferType, LegalType legalElementType) { - return createLegalUniformBufferType(context, uniformBufferType->getOp(), legalElementType); + return createLegalUniformBufferType( + context, + uniformBufferType->getOp(), + legalElementType, + uniformBufferType->getDataLayout()); } // Create a pointer type with a given legalized value type. @@ -1141,7 +1162,8 @@ LegalType legalizeTypeImpl(TypeLegalizationContext* context, IRType* type) // return context->createLegalUniformBufferType( uniformBufferType->getOp(), - legalElementType); + legalElementType, + uniformBufferType->getDataLayout()); } } diff --git a/source/slang/slang-legalize-types.h b/source/slang/slang-legalize-types.h index f70ac7757..eaee373b2 100644 --- a/source/slang/slang-legalize-types.h +++ b/source/slang/slang-legalize-types.h @@ -654,7 +654,10 @@ struct IRTypeLegalizationContext /// This function will only be called if `legalElementType` is /// somehow non-trivial. /// - virtual LegalType createLegalUniformBufferType(IROp op, LegalType legalElementType) = 0; + virtual LegalType createLegalUniformBufferType( + IROp op, + LegalType legalElementType, + IRInst* layoutOperand) = 0; }; // This typedef exists to support pre-existing code from when @@ -675,7 +678,8 @@ ModuleDecl* findModuleForDecl(Decl* decl); LegalType createLegalUniformBufferTypeForResources( TypeLegalizationContext* context, IROp op, - LegalType legalElementType); + LegalType legalElementType, + IRInst* layoutOperand); /// Create a uniform buffer type suitable for existential legalization. /// @@ -686,7 +690,8 @@ LegalType createLegalUniformBufferTypeForResources( LegalType createLegalUniformBufferTypeForExistentials( TypeLegalizationContext* context, IROp op, - LegalType legalElementType); + LegalType legalElementType, + IRInst* layoutOperand); void legalizeExistentialTypeLayout(TargetProgram* target, IRModule* module, DiagnosticSink* sink); diff --git a/source/slang/slang-parameter-binding.cpp b/source/slang/slang-parameter-binding.cpp index 3621c6c00..e45eb4652 100644 --- a/source/slang/slang-parameter-binding.cpp +++ b/source/slang/slang-parameter-binding.cpp @@ -707,7 +707,7 @@ RefPtr getTypeLayoutForGlobalShaderParameter( // If the target doesn't support specialization constants, then we will // layout them as ordinary uniform data. specializationConstantRule = - rules->getConstantBufferRules(context->getTargetRequest()->getOptionSet()); + rules->getConstantBufferRules(context->getTargetRequest()->getOptionSet(), type); } return createTypeLayoutWith(layoutContext, specializationConstantRule, type); } @@ -718,7 +718,7 @@ RefPtr getTypeLayoutForGlobalShaderParameter( // shader parameter. return createTypeLayoutWith( layoutContext, - rules->getConstantBufferRules(context->getTargetRequest()->getOptionSet()), + rules->getConstantBufferRules(context->getTargetRequest()->getOptionSet(), type), type); } @@ -2421,11 +2421,21 @@ static RefPtr computeEntryPointParameterTypeLayout( // a uniform shader parameter passed via the implicitly-defined // constant buffer (e.g., the `$Params` constant buffer seen in fxc/dxc output). // - return createTypeLayoutWith( - context->layoutContext, - context->getRulesFamily()->getConstantBufferRules( - context->getTargetRequest()->getOptionSet()), - paramType); + LayoutRulesImpl* layoutRules = nullptr; + if (isKhronosTarget(context->getTargetRequest())) + { + // For Vulkan, entry point uniform parameters are laid out using push constant buffer + // rules (defaults to std430). + layoutRules = context->getRulesFamily()->getShaderStorageBufferRules( + context->getTargetProgram()->getOptionSet()); + } + else + { + layoutRules = context->getRulesFamily()->getConstantBufferRules( + context->getTargetRequest()->getOptionSet(), + paramType); + } + return createTypeLayoutWith(context->layoutContext, layoutRules, paramType); } else { @@ -2783,12 +2793,13 @@ static ParameterBindingAndKindInfo _allocateConstantBufferBinding(ParameterBindi UInt space = context->shared->defaultSpace; auto usedRangeSet = _getOrCreateUsedRangeSetForSpace(context, space); - auto layoutInfo = context->getRulesFamily() - ->getConstantBufferRules(context->getTargetRequest()->getOptionSet()) - ->GetObjectLayout( - ShaderParameterKind::ConstantBuffer, - context->layoutContext.objectLayoutOptions) - .getSimple(); + auto layoutInfo = + context->getRulesFamily() + ->getConstantBufferRules(context->getTargetRequest()->getOptionSet(), nullptr) + ->GetObjectLayout( + ShaderParameterKind::ConstantBuffer, + context->layoutContext.objectLayoutOptions) + .getSimple(); ParameterBindingAndKindInfo info; info.kind = layoutInfo.kind; @@ -2809,7 +2820,9 @@ static ParameterBindingAndKindInfo _assignConstantBufferBinding( auto usedRangeSet = _getOrCreateUsedRangeSetForSpace(context, space); auto layoutInfo = context->getRulesFamily() - ->getConstantBufferRules(context->getTargetRequest()->getOptionSet()) + ->getConstantBufferRules( + context->getTargetRequest()->getOptionSet(), + varLayout->typeLayout ? varLayout->typeLayout->getType() : nullptr) ->GetObjectLayout( ShaderParameterKind::ConstantBuffer, context->layoutContext.objectLayoutOptions) @@ -3786,6 +3799,7 @@ static bool _calcNeedsDefaultSpace(SharedParameterBindingContext& sharedContext) case LayoutResourceKind::RegisterSpace: case LayoutResourceKind::SubElementRegisterSpace: case LayoutResourceKind::PushConstantBuffer: + case LayoutResourceKind::SpecializationConstant: continue; case LayoutResourceKind::Uniform: { diff --git a/source/slang/slang-syntax.cpp b/source/slang/slang-syntax.cpp index 589a170fa..9e448a5fa 100644 --- a/source/slang/slang-syntax.cpp +++ b/source/slang/slang-syntax.cpp @@ -985,6 +985,11 @@ Val* _tryLookupConcreteAssociatedTypeFromThisTypeSubst(ASTBuilder* builder, Decl return nullptr; } +Type* UniformParameterGroupType::getLayoutType() +{ + return as(getGenericArg(getDeclRef(), 1)); +} + ModuleDecl* getModuleDecl(Decl* decl) { for (auto dd = decl; dd; dd = dd->parentDecl) diff --git a/source/slang/slang-type-layout.cpp b/source/slang/slang-type-layout.cpp index 62caecf72..da4cd458b 100644 --- a/source/slang/slang-type-layout.cpp +++ b/source/slang/slang-type-layout.cpp @@ -1022,7 +1022,9 @@ MetalVaryingLayoutRulesImpl kMetalVaryingOutputLayoutRulesImpl(LayoutResourceKin struct GLSLLayoutRulesFamilyImpl : LayoutRulesFamilyImpl { virtual LayoutRulesImpl* getAnyValueRules() override; - virtual LayoutRulesImpl* getConstantBufferRules(CompilerOptionSet& compilerOptions) override; + virtual LayoutRulesImpl* getConstantBufferRules( + CompilerOptionSet& compilerOptions, + Type* containerType) override; virtual LayoutRulesImpl* getPushConstantBufferRules() override; virtual LayoutRulesImpl* getTextureBufferRules(CompilerOptionSet& compilerOptions) override; virtual LayoutRulesImpl* getVaryingInputRules() override; @@ -1044,7 +1046,9 @@ struct GLSLLayoutRulesFamilyImpl : LayoutRulesFamilyImpl struct HLSLLayoutRulesFamilyImpl : LayoutRulesFamilyImpl { virtual LayoutRulesImpl* getAnyValueRules() override; - virtual LayoutRulesImpl* getConstantBufferRules(CompilerOptionSet& compilerOptions) override; + virtual LayoutRulesImpl* getConstantBufferRules( + CompilerOptionSet& compilerOptions, + Type* containerType) override; virtual LayoutRulesImpl* getPushConstantBufferRules() override; virtual LayoutRulesImpl* getTextureBufferRules(CompilerOptionSet& compilerOptions) override; virtual LayoutRulesImpl* getVaryingInputRules() override; @@ -1066,7 +1070,9 @@ struct HLSLLayoutRulesFamilyImpl : LayoutRulesFamilyImpl struct CPULayoutRulesFamilyImpl : LayoutRulesFamilyImpl { virtual LayoutRulesImpl* getAnyValueRules() override; - virtual LayoutRulesImpl* getConstantBufferRules(CompilerOptionSet& compilerOptions) override; + virtual LayoutRulesImpl* getConstantBufferRules( + CompilerOptionSet& compilerOptions, + Type* containerType) override; virtual LayoutRulesImpl* getPushConstantBufferRules() override; virtual LayoutRulesImpl* getTextureBufferRules(CompilerOptionSet& compilerOptions) override; virtual LayoutRulesImpl* getVaryingInputRules() override; @@ -1087,7 +1093,9 @@ struct CPULayoutRulesFamilyImpl : LayoutRulesFamilyImpl struct CUDALayoutRulesFamilyImpl : LayoutRulesFamilyImpl { virtual LayoutRulesImpl* getAnyValueRules() override; - virtual LayoutRulesImpl* getConstantBufferRules(CompilerOptionSet& compilerOptions) override; + virtual LayoutRulesImpl* getConstantBufferRules( + CompilerOptionSet& compilerOptions, + Type* containerType) override; virtual LayoutRulesImpl* getPushConstantBufferRules() override; virtual LayoutRulesImpl* getTextureBufferRules(CompilerOptionSet& compilerOptions) override; virtual LayoutRulesImpl* getVaryingInputRules() override; @@ -1108,7 +1116,9 @@ struct CUDALayoutRulesFamilyImpl : LayoutRulesFamilyImpl struct MetalLayoutRulesFamilyImpl : LayoutRulesFamilyImpl { virtual LayoutRulesImpl* getAnyValueRules() override; - virtual LayoutRulesImpl* getConstantBufferRules(CompilerOptionSet& compilerOptions) override; + virtual LayoutRulesImpl* getConstantBufferRules( + CompilerOptionSet& compilerOptions, + Type* containerType) override; virtual LayoutRulesImpl* getPushConstantBufferRules() override; virtual LayoutRulesImpl* getTextureBufferRules(CompilerOptionSet& compilerOptions) override; virtual LayoutRulesImpl* getVaryingInputRules() override; @@ -1129,7 +1139,9 @@ struct MetalLayoutRulesFamilyImpl : LayoutRulesFamilyImpl struct WGSLLayoutRulesFamilyImpl : LayoutRulesFamilyImpl { virtual LayoutRulesImpl* getAnyValueRules() override; - virtual LayoutRulesImpl* getConstantBufferRules(CompilerOptionSet& compilerOptions) override; + virtual LayoutRulesImpl* getConstantBufferRules( + CompilerOptionSet& compilerOptions, + Type* containerType) override; virtual LayoutRulesImpl* getPushConstantBufferRules() override; virtual LayoutRulesImpl* getTextureBufferRules(CompilerOptionSet& compilerOptions) override; virtual LayoutRulesImpl* getVaryingInputRules() override; @@ -1514,13 +1526,28 @@ LayoutRulesImpl* GLSLLayoutRulesFamilyImpl::getAnyValueRules() } LayoutRulesImpl* GLSLLayoutRulesFamilyImpl::getConstantBufferRules( - CompilerOptionSet& compilerOptions) + CompilerOptionSet& compilerOptions, + Type* containerType) { if (compilerOptions.shouldUseScalarLayout()) return &kScalarLayoutRulesImpl_; else if (compilerOptions.shouldUseDXLayout()) return &kFXCConstantBufferLayoutRulesFamilyImpl; - + if (auto cbufferType = as(containerType)) + { + switch (cbufferType->getLayoutType()->astNodeType) + { + case ASTNodeType::DefaultDataLayoutType: + case ASTNodeType::Std140DataLayoutType: + return &kStd140LayoutRulesImpl_; + case ASTNodeType::Std430DataLayoutType: + return &kStd430LayoutRulesImpl_; + case ASTNodeType::ScalarDataLayoutType: + return &kScalarLayoutRulesImpl_; + default: + break; + } + } return &kStd140LayoutRulesImpl_; } @@ -1615,7 +1642,7 @@ LayoutRulesImpl* HLSLLayoutRulesFamilyImpl::getAnyValueRules() return &kHLSLAnyValueLayoutRulesImpl_; } -LayoutRulesImpl* HLSLLayoutRulesFamilyImpl::getConstantBufferRules(CompilerOptionSet&) +LayoutRulesImpl* HLSLLayoutRulesFamilyImpl::getConstantBufferRules(CompilerOptionSet&, Type*) { return &kHLSLConstantBufferLayoutRulesImpl_; } @@ -1689,7 +1716,7 @@ LayoutRulesImpl* CPULayoutRulesFamilyImpl::getAnyValueRules() return &kCPUAnyValueLayoutRulesImpl_; } -LayoutRulesImpl* CPULayoutRulesFamilyImpl::getConstantBufferRules(CompilerOptionSet&) +LayoutRulesImpl* CPULayoutRulesFamilyImpl::getConstantBufferRules(CompilerOptionSet&, Type*) { return &kCPULayoutRulesImpl_; } @@ -1755,7 +1782,7 @@ LayoutRulesImpl* CUDALayoutRulesFamilyImpl::getAnyValueRules() return &kCUDAAnyValueLayoutRulesImpl_; } -LayoutRulesImpl* CUDALayoutRulesFamilyImpl::getConstantBufferRules(CompilerOptionSet&) +LayoutRulesImpl* CUDALayoutRulesFamilyImpl::getConstantBufferRules(CompilerOptionSet&, Type*) { return &kCUDALayoutRulesImpl_; } @@ -1944,7 +1971,7 @@ LayoutRulesImpl* MetalLayoutRulesFamilyImpl::getAnyValueRules() return &kHLSLAnyValueLayoutRulesImpl_; } -LayoutRulesImpl* MetalLayoutRulesFamilyImpl::getConstantBufferRules(CompilerOptionSet&) +LayoutRulesImpl* MetalLayoutRulesFamilyImpl::getConstantBufferRules(CompilerOptionSet&, Type*) { return &kMetalConstantBufferLayoutRulesImpl_; } @@ -2022,7 +2049,7 @@ LayoutRulesImpl* WGSLLayoutRulesFamilyImpl::getAnyValueRules() return &kGLSLAnyValueLayoutRulesImpl_; } -LayoutRulesImpl* WGSLLayoutRulesFamilyImpl::getConstantBufferRules(CompilerOptionSet&) +LayoutRulesImpl* WGSLLayoutRulesFamilyImpl::getConstantBufferRules(CompilerOptionSet&, Type*) { return &kWGSLConstantBufferLayoutRulesImpl_; } @@ -2178,7 +2205,7 @@ TypeLayoutContext getInitialLayoutContextForTarget( if (rulesFamily) { - context.rules = rulesFamily->getConstantBufferRules(targetReq->getOptionSet()); + context.rules = rulesFamily->getConstantBufferRules(targetReq->getOptionSet(), nullptr); } return context; @@ -3438,7 +3465,9 @@ LayoutRulesImpl* getParameterBufferElementTypeLayoutRules( { if (as(parameterGroupType)) { - return rules->getLayoutRulesFamily()->getConstantBufferRules(compilerOptions); + return rules->getLayoutRulesFamily()->getConstantBufferRules( + compilerOptions, + parameterGroupType); } else if (as(parameterGroupType)) { diff --git a/source/slang/slang-type-layout.h b/source/slang/slang-type-layout.h index 87d9123ff..2cdb4b386 100644 --- a/source/slang/slang-type-layout.h +++ b/source/slang/slang-type-layout.h @@ -1105,7 +1105,9 @@ struct LayoutRulesImpl struct LayoutRulesFamilyImpl { virtual LayoutRulesImpl* getAnyValueRules() = 0; - virtual LayoutRulesImpl* getConstantBufferRules(CompilerOptionSet& compilerOptions) = 0; + virtual LayoutRulesImpl* getConstantBufferRules( + CompilerOptionSet& compilerOptions, + Type* containerType) = 0; virtual LayoutRulesImpl* getPushConstantBufferRules() = 0; virtual LayoutRulesImpl* getTextureBufferRules(CompilerOptionSet& compilerOptions) = 0; virtual LayoutRulesImpl* getVaryingInputRules() = 0; diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp index 854d90df5..e4de61276 100644 --- a/source/slang/slang.cpp +++ b/source/slang/slang.cpp @@ -1608,7 +1608,9 @@ SLANG_NO_THROW slang::TypeReflection* SLANG_MCALL Linkage::getContainerType( { case slang::ContainerType::ConstantBuffer: { - ConstantBufferType* cbType = getASTBuilder()->getConstantBufferType(type); + SemanticsVisitor visitor(getSemanticsForReflection()); + auto layoutType = getASTBuilder()->getDefaultLayoutType(); + Type* cbType = visitor.getConstantBufferType(type, layoutType); containerTypeReflection = cbType; } break; diff --git a/tests/bugs/vk-shift-uniform-issue.slang b/tests/bugs/vk-shift-uniform-issue.slang index bc5963fe0..7f27d2c56 100644 --- a/tests/bugs/vk-shift-uniform-issue.slang +++ b/tests/bugs/vk-shift-uniform-issue.slang @@ -13,7 +13,7 @@ // CHECK-NEXT:uniform sampler sampler1_0; // CHECK: layout(push_constant) -// CHECK-NEXT: layout(std140) uniform +// CHECK-NEXT: layout(std430) uniform // CHECK:layout(binding = 1004) // CHECK-NEXT:layout(std140) uniform diff --git a/tests/reflection/binding-push-constant-gl.hlsl.expected b/tests/reflection/binding-push-constant-gl.hlsl.expected index 13dfe2fcd..0b906d461 100644 --- a/tests/reflection/binding-push-constant-gl.hlsl.expected +++ b/tests/reflection/binding-push-constant-gl.hlsl.expected @@ -138,7 +138,7 @@ standard output = { } ] }, - "binding": {"kind": "uniform", "offset": 0, "size": 16} + "binding": {"kind": "uniform", "offset": 0, "size": 8} } } }, diff --git a/tests/spirv/constant-buffer-layout.slang b/tests/spirv/constant-buffer-layout.slang new file mode 100644 index 000000000..90aa1eef8 --- /dev/null +++ b/tests/spirv/constant-buffer-layout.slang @@ -0,0 +1,45 @@ +//TEST:SIMPLE(filecheck=SPIRV): -target spirv -emit-spirv-directly + +//SPIRV: ArrayStride 12 + +struct Test +{ +//SPIRV: Offset 0 + uint v0; + +//SPIRV: Offset 4 +// matrix always start on a new register + float3x3 v1; +//SPIRV: Offset 40 +// Non-matrix can pack with a partially filled register + uint v2; +}; + +ConstantBuffer buffer; + +//TEST_INPUT:ubuffer(data=[0], stride=4):out,name outputBuffer +RWStructuredBuffer outputBuffer; + +__generic +bool comp(vector v1, vector v2) +{ + for (uint i = 0; i < N; i++) + if (v1[i] != v2[i]) + return false; + + return true; +} + +[shader("compute")] +[numthreads(2, 2, 1)] +void computeMain() +{ + // CHECK: 64 + outputBuffer[0] = (true + && buffer.v0 == 1 + && comp(buffer.v1[0], float3(2, 3, 4)) + && comp(buffer.v1[1], float3(5, 6, 7)) + && comp(buffer.v1[2], float3(8, 9, 10)) + && buffer.v2 == 11 + ) ? 100 : 0; +} \ No newline at end of file diff --git a/tests/spirv/push-constant-layout.slang b/tests/spirv/push-constant-layout.slang new file mode 100644 index 000000000..eb7d80f75 --- /dev/null +++ b/tests/spirv/push-constant-layout.slang @@ -0,0 +1,26 @@ +//TEST:SIMPLE(filecheck=CHECK): -target spirv -fvk-use-entrypoint-name +// CHECK-NOT: std140 +struct Transform +{ + float4 Tint; + float2x2 ScaleRot; + float2 Translation; +}; + +[[vk::push_constant]] +ConstantBuffer transform1; + +RWStructuredBuffer outputBuffer; + +[numthreads(1,1,1)] +void computeMain1() +{ + outputBuffer[0] = transform1.Translation.x; +} + +[numthreads(1,1,1)] +void computeMain2( + [vk::push_constant] ConstantBuffer transform2) +{ + outputBuffer[0] = transform2.Translation.x; +} \ No newline at end of file diff --git a/tests/spirv/spec-constant-space.slang b/tests/spirv/spec-constant-space.slang new file mode 100644 index 000000000..b4dd865c7 --- /dev/null +++ b/tests/spirv/spec-constant-space.slang @@ -0,0 +1,17 @@ +// Test that use of specialization constants does not cause space 0 to be reserved. + +//TEST:SIMPLE(filecheck=CHECK): -target spirv + +// CHECK-NOT: DescriptorSet 1 + +struct MyData { float4 val; RWStructuredBuffer outputBuffer; } + + +[vk::specialization_constant] +const int kSpecializationConstant = 0; + +[NumThreads(1,1,1)] +void main(ParameterBlock g_data) +{ + g_data.outputBuffer[0] = g_data.val.x; +} \ No newline at end of file -- cgit v1.2.3