diff --git a/docs/user-guide/a2-01-spirv-target-specific.md b/docs/user-guide/a2-01-spirv-target-specific.md index d6d1190bba..e96b81162b 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 249a084b18..817e3c39db 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 7eb4af1350..84beb3b4bf 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 575c7268bb..6ffaee7db3 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 22f6bb91ed..bd5c9b4e39 100644 --- a/source/slang/slang-ast-builder.h +++ b/source/slang/slang-ast-builder.h @@ -39,6 +39,8 @@ class SharedASTBuilder : public RefObject /// Get the `IDifferentiable` type Type* getDiffInterfaceType(); + Type* getIBufferDataLayoutType(); + Type* getErrorType(); Type* getBottomType(); Type* getInitializerListType(); @@ -89,6 +91,7 @@ class SharedASTBuilder : public RefObject 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 @@ class ASTBuilder : public RefObject 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 @@ class ASTBuilder : public RefObject 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 c55e0011fb..c5c5de5d27 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 07d5dd1fab..251ce6a696 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 95a6d00cc3..95ec872a5a 100644 --- a/source/slang/slang-check-impl.h +++ b/source/slang/slang-check-impl.h @@ -1149,6 +1149,8 @@ struct SemanticsVisitor : public SemanticsContext 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 80c950c178..52a70034a8 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 34f16751b3..d9691a8281 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 43ab7b74ec..6c525d064a 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 26d37d1f55..824680b72f 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 010b4bc921..326fc702da 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 cd03a61ef9..1c833a2948 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 0dfdc2d8ef..b3073a97d0 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 80bc0a781c..21cee3c4ed 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 97989e9ce9..fb06863d4a 100644 --- a/source/slang/slang-ir-insts.h +++ b/source/slang/slang-ir-insts.h @@ -3719,7 +3719,7 @@ struct IRBuilder 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 2af3c4c09a..9154277f50 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 2f8631e183..bd3e350bca 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 f29cbe2228..dfa07ca674 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 e7f82f5ad3..f6c662a98d 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 05a6fa55cb..d24c2d12b2 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 6d827a4125..7695ba3854 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 f70ac77578..eaee373b20 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 3621c6c00b..e45eb46523 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 589a170fae..9e448a5fa6 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 62caecf726..da4cd458b9 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 87d9123ff4..2cdb4b3866 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 854d90df50..e4de612762 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 bc5963fe07..7f27d2c566 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 13dfe2fcd6..0b906d4613 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 0000000000..90aa1eef84 --- /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 0000000000..eb7d80f758 --- /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 0000000000..b4dd865c79 --- /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