diff options
25 files changed, 468 insertions, 38 deletions
diff --git a/docs/command-line-slangc-reference.md b/docs/command-line-slangc-reference.md index cf858a54c..36947d7b4 100644 --- a/docs/command-line-slangc-reference.md +++ b/docs/command-line-slangc-reference.md @@ -474,6 +474,11 @@ Make data accessed through ConstantBuffer, ParameterBlock, StructuredBuffer, Byt Pack members using FXCs member packing rules when targeting GLSL or SPIRV. +<a id="fvk-use-c-layout"></a> +### -fvk-use-c-layout +Make data accessed through ConstantBuffer, ParameterBlock, StructuredBuffer, ByteAddressBuffer and general pointers follow the C/C++ structure layout rules when targeting SPIRV. + + <a id="fvk-b-shift"></a> ### -fvk-b-shift, -fvk-s-shift, -fvk-t-shift, -fvk-u-shift diff --git a/docs/user-guide/a2-01-spirv-target-specific.md b/docs/user-guide/a2-01-spirv-target-specific.md index 899fb39fd..c560297f7 100644 --- a/docs/user-guide/a2-01-spirv-target-specific.md +++ b/docs/user-guide/a2-01-spirv-target-specific.md @@ -192,9 +192,9 @@ StructuredBuffer and ByteAddressBuffer are translated to a shader storage buffer 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 individual `ConstantBuffer` or `StructuredBuffer`, you can specify the layout as a second generic argument. E.g., `ConstantBuffer<T, Std430DataLayout>`, `StructuredBuffer<T, Std140DataLayout>`, `StructuredBuffer<T, Std430DataLayout>` or `StructuredBuffer<T, ScalarDataLayout>`. +If you need to apply a different buffer layout for individual `ConstantBuffer` or `StructuredBuffer`, you can specify the layout as a second generic argument. E.g., `ConstantBuffer<T, Std430DataLayout>`, `StructuredBuffer<T, Std140DataLayout>`, `StructuredBuffer<T, Std430DataLayout>`, `StructuredBuffer<T, ScalarDataLayout>` or `StructuredBuffer<T, CDataLayout>`. -Note that there are compiler options, "-fvk-use-scalar-layout" / "-force-glsl-scalar-layout" and "-fvk-use-dx-layout". +Note that there are compiler options, "-fvk-use-scalar-layout" / "-force-glsl-scalar-layout", "-fvk-use-dx-layout" and "-fvk-use-c-layout". These options do the same but they are applied globally. @@ -454,6 +454,9 @@ 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-c-layout +Make data accessed through ConstantBuffer, ParameterBlock, StructuredBuffer, ByteAddressBuffer and general pointers follow the C/C++ structure layout rules when targeting SPIRV. + ### -fvk-use-entrypoint-name Uses the entrypoint name from the source instead of 'main' in the spirv output. diff --git a/include/slang-deprecated.h b/include/slang-deprecated.h index 32db65007..c81ae8537 100644 --- a/include/slang-deprecated.h +++ b/include/slang-deprecated.h @@ -1608,6 +1608,8 @@ struct ICompileRequest : public ISlangUnknown virtual SLANG_NO_THROW void SLANG_MCALL setTargetEmbedDownstreamIR(int targetIndex, bool value) = 0; + + virtual SLANG_NO_THROW void SLANG_MCALL setTargetForceCLayout(int targetIndex, bool value) = 0; }; #define SLANG_UUID_ICompileRequest ICompileRequest::getTypeGuid() diff --git a/include/slang.h b/include/slang.h index 02f6bdce6..3c2a31413 100644 --- a/include/slang.h +++ b/include/slang.h @@ -1053,6 +1053,8 @@ typedef uint32_t SlangSizeT; // Bitfield options UseMSVCStyleBitfieldPacking, // bool + ForceCLayout, // bool + CountOf, }; diff --git a/source/slang-record-replay/util/emum-to-string.h b/source/slang-record-replay/util/emum-to-string.h index 29365e6af..fb3947c8c 100644 --- a/source/slang-record-replay/util/emum-to-string.h +++ b/source/slang-record-replay/util/emum-to-string.h @@ -182,6 +182,7 @@ static Slang::String CompilerOptionNameToString(const slang::CompilerOptionName CASE(VulkanEmitReflection); CASE(GLSLForceScalarLayout); CASE(ForceDXLayout); + CASE(ForceCLayout); CASE(EnableEffectAnnotations); CASE(EmitSpirvViaGLSL); CASE(EmitSpirvDirectly); diff --git a/source/slang/hlsl.meta.slang b/source/slang/hlsl.meta.slang index 7f8488236..0dbca2479 100644 --- a/source/slang/hlsl.meta.slang +++ b/source/slang/hlsl.meta.slang @@ -57,6 +57,13 @@ __magic_type(ScalarDataLayoutType) struct ScalarDataLayout : IBufferDataLayout {}; +/// @category misc_types +__intrinsic_type($(kIROp_CBufferLayoutType)) +__magic_type(CDataLayoutType) +[require(spirv)] +struct CDataLayout : IBufferDataLayout +{}; + //@hidden: __generic<T, L : IBufferDataLayout = DefaultDataLayout> __intrinsic_type($(kIROp_GLSLShaderStorageBufferType)) diff --git a/source/slang/slang-ast-builder.cpp b/source/slang/slang-ast-builder.cpp index d576ed0fb..a71abf570 100644 --- a/source/slang/slang-ast-builder.cpp +++ b/source/slang/slang-ast-builder.cpp @@ -489,6 +489,11 @@ Type* ASTBuilder::getScalarLayoutType() return getSpecializedBuiltinType({}, "ScalarDataLayoutType"); } +Type* ASTBuilder::getCLayoutType() +{ + return getSpecializedBuiltinType({}, "CDataLayoutType"); +} + // Construct the type `Out<valueType>` OutType* ASTBuilder::getOutType(Type* valueType) { diff --git a/source/slang/slang-ast-builder.h b/source/slang/slang-ast-builder.h index 72f875f91..798e1ddc0 100644 --- a/source/slang/slang-ast-builder.h +++ b/source/slang/slang-ast-builder.h @@ -517,6 +517,7 @@ public: Type* getStd140LayoutType(); Type* getStd430LayoutType(); Type* getScalarLayoutType(); + Type* getCLayoutType(); Type* getInitializerListType() { return m_sharedASTBuilder->getInitializerListType(); } Type* getOverloadedType() { return m_sharedASTBuilder->getOverloadedType(); } diff --git a/source/slang/slang-ast-type.h b/source/slang/slang-ast-type.h index d262f1ad5..842af8b88 100644 --- a/source/slang/slang-ast-type.h +++ b/source/slang/slang-ast-type.h @@ -162,6 +162,12 @@ class ScalarDataLayoutType : public DataLayoutType }; FIDDLE() +class CDataLayoutType : public DataLayoutType +{ + FIDDLE(...) +}; + +FIDDLE() class FeedbackType : public BuiltinType { FIDDLE(...) diff --git a/source/slang/slang-compiler-options.cpp b/source/slang/slang-compiler-options.cpp index 843e0e7cb..fa2b99b98 100644 --- a/source/slang/slang-compiler-options.cpp +++ b/source/slang/slang-compiler-options.cpp @@ -143,6 +143,7 @@ void CompilerOptionSet::writeCommandLineArgs(Session* globalSession, StringBuild case CompilerOptionName::EmitSpirvDirectly: case CompilerOptionName::GLSLForceScalarLayout: case CompilerOptionName::ForceDXLayout: + case CompilerOptionName::ForceCLayout: case CompilerOptionName::MatrixLayoutRow: case CompilerOptionName::MatrixLayoutColumn: case CompilerOptionName::VulkanInvertY: diff --git a/source/slang/slang-compiler-options.h b/source/slang/slang-compiler-options.h index 5986c4e82..ec9383e08 100644 --- a/source/slang/slang-compiler-options.h +++ b/source/slang/slang-compiler-options.h @@ -348,6 +348,8 @@ struct CompilerOptionSet bool shouldUseDXLayout() { return getBoolOption(CompilerOptionName::ForceDXLayout); } + bool shouldUseCLayout() { return getBoolOption(CompilerOptionName::ForceCLayout); } + bool shouldDumpIntermediates() { return getBoolOption(CompilerOptionName::DumpIntermediates); } bool shouldDumpIR() { return getBoolOption(CompilerOptionName::DumpIr); } diff --git a/source/slang/slang-emit-spirv.cpp b/source/slang/slang-emit-spirv.cpp index 6b42896d0..d6f4ac176 100644 --- a/source/slang/slang-emit-spirv.cpp +++ b/source/slang/slang-emit-spirv.cpp @@ -1847,10 +1847,22 @@ struct SPIRVEmitContext : public SourceEmitterBase, public SPIRVEmitSharedContex IRSizeAndAlignment sizeAndAlignment; uint32_t stride; - getNaturalSizeAndAlignment( - m_targetProgram->getOptionSet(), - valueType, - &sizeAndAlignment); + if (auto layout = valueType->findDecoration<IRSizeAndAlignmentDecoration>()) + { + auto rule = IRTypeLayoutRules::get(layout->getLayoutName()); + getSizeAndAlignment( + m_targetProgram->getOptionSet(), + rule, + valueType, + &sizeAndAlignment); + } + else + { + getNaturalSizeAndAlignment( + m_targetProgram->getOptionSet(), + valueType, + &sizeAndAlignment); + } uint64_t valueSize = sizeAndAlignment.size; // Any unsized data type (e.g. struct or array) will have size of diff --git a/source/slang/slang-end-to-end-request.cpp b/source/slang/slang-end-to-end-request.cpp index 725fc1854..fc2d6d55e 100644 --- a/source/slang/slang-end-to-end-request.cpp +++ b/source/slang/slang-end-to-end-request.cpp @@ -894,6 +894,11 @@ void EndToEndCompileRequest::setTargetForceDXLayout(int targetIndex, bool value) getTargetOptionSet(targetIndex).set(CompilerOptionName::ForceDXLayout, value); } +void EndToEndCompileRequest::setTargetForceCLayout(int targetIndex, bool value) +{ + getTargetOptionSet(targetIndex).set(CompilerOptionName::ForceCLayout, value); +} + void EndToEndCompileRequest::setTargetFloatingPointMode( int targetIndex, SlangFloatingPointMode mode) diff --git a/source/slang/slang-end-to-end-request.h b/source/slang/slang-end-to-end-request.h index af04eac63..f8d666970 100644 --- a/source/slang/slang-end-to-end-request.h +++ b/source/slang/slang-end-to-end-request.h @@ -100,6 +100,8 @@ public: setTargetGenerateWholeProgram(int targetIndex, bool value) SLANG_OVERRIDE; virtual SLANG_NO_THROW void SLANG_MCALL setTargetEmbedDownstreamIR(int targetIndex, bool value) SLANG_OVERRIDE; + virtual SLANG_NO_THROW void SLANG_MCALL setTargetForceCLayout(int targetIndex, bool value) + SLANG_OVERRIDE; virtual SLANG_NO_THROW void SLANG_MCALL setMatrixLayoutMode(SlangMatrixLayoutMode mode) SLANG_OVERRIDE; virtual SLANG_NO_THROW void SLANG_MCALL setDebugInfoLevel(SlangDebugInfoLevel level) diff --git a/source/slang/slang-ir-entry-point-uniforms.cpp b/source/slang/slang-ir-entry-point-uniforms.cpp index 586d64b4f..e75be9e24 100644 --- a/source/slang/slang-ir-entry-point-uniforms.cpp +++ b/source/slang/slang-ir-entry-point-uniforms.cpp @@ -557,6 +557,9 @@ struct CollectEntryPointUniformParams : PerEntryPointPass if (m_options.targetReq->getOptionSet().getBoolOption( CompilerOptionName::GLSLForceScalarLayout)) layoutType = builder.getType(kIROp_ScalarBufferLayoutType); + else if (m_options.targetReq->getOptionSet().getBoolOption( + CompilerOptionName::ForceCLayout)) + layoutType = builder.getType(kIROp_CBufferLayoutType); else if (isKhronosTarget(m_options.targetReq)) layoutType = builder.getType(kIROp_Std430BufferLayoutType); else diff --git a/source/slang/slang-ir-insts-stable-names.lua b/source/slang/slang-ir-insts-stable-names.lua index db2a8f8c8..589c4b130 100644 --- a/source/slang/slang-ir-insts-stable-names.lua +++ b/source/slang/slang-ir-insts-stable-names.lua @@ -669,4 +669,5 @@ return { ["SPIRVAsmOperand.__sampledType"] = 665, ["SPIRVAsmOperand.__imageType"] = 666, ["SPIRVAsmOperand.__sampledImageType"] = 667, + ["Type.CLayout"] = 668, } diff --git a/source/slang/slang-ir-insts.lua b/source/slang/slang-ir-insts.lua index 0325fcbfd..c1777c2cf 100644 --- a/source/slang/slang-ir-insts.lua +++ b/source/slang/slang-ir-insts.lua @@ -206,6 +206,7 @@ local insts = { { Std140Layout = { struct_name = "Std140BufferLayoutType", hoistable = true } }, { Std430Layout = { struct_name = "Std430BufferLayoutType", hoistable = true } }, { ScalarLayout = { struct_name = "ScalarBufferLayoutType", hoistable = true } }, + { CLayout = { struct_name = "CBufferLayoutType", hoistable = true } }, { SubpassInputType = { operands = { { "elementType", "IRType" }, { "isMultisampleInst" } }, hoistable = true } }, { TextureFootprintType = { min_operands = 1, hoistable = true } }, { TextureShape1DType = { hoistable = true } }, diff --git a/source/slang/slang-ir-layout.cpp b/source/slang/slang-ir-layout.cpp index ac0c79601..d3b86a1c6 100644 --- a/source/slang/slang-ir-layout.cpp +++ b/source/slang/slang-ir-layout.cpp @@ -83,9 +83,8 @@ IRIntegerValue getIntegerValueFromInst(IRInst* inst) return as<IRIntLit>(inst)->value.intVal; } -static Result _calcSizeAndAlignment( +Result IRTypeLayoutRules::calcSizeAndAlignment( CompilerOptionSet& optionSet, - IRTypeLayoutRules* rules, IRType* type, IRSizeAndAlignment* outSizeAndAlignment) { @@ -131,10 +130,6 @@ static Result _calcSizeAndAlignment( // We are currently handling `bool` following the HLSL // precednet of storing it in 4 bytes. // - // TODO: It would be good to try to make this follow - // per-platform conventions, or at least to be able - // to use a 1-byte encoding where available. - // BASE(Bool, 4); // The Slang `void` type is treated as a zero-byte @@ -142,7 +137,7 @@ static Result _calcSizeAndAlignment( // CASE(Void, 0, 1); -#undef CASE +#undef BASE #undef CASE @@ -163,7 +158,7 @@ static Result _calcSizeAndAlignment( IRSizeAndAlignment fieldTypeLayout; SLANG_RETURN_ON_FAIL( - getSizeAndAlignment(optionSet, rules, field->getFieldType(), &fieldTypeLayout)); + getSizeAndAlignment(optionSet, this, field->getFieldType(), &fieldTypeLayout)); seenFinalUnsizedArrayField = fieldTypeLayout.size == IRSizeAndAlignment::kIndeterminateSize; @@ -174,7 +169,7 @@ static Result _calcSizeAndAlignment( } else { - offset = rules->adjustOffset( + offset = adjustOffset( offset, fieldTypeLayout.size, lastFieldType, @@ -199,7 +194,7 @@ static Result _calcSizeAndAlignment( builder.addDecoration( field, kIROp_OffsetDecoration, - builder.getIntValue(intType, (IRIntegerValue)rules->ruleName), + builder.getIntValue(intType, (IRIntegerValue)ruleName), builder.getIntValue(intType, fieldOffset)); } if (!seenFinalUnsizedArrayField) @@ -208,7 +203,7 @@ static Result _calcSizeAndAlignment( lastFieldType = field->getFieldType(); lastFieldAlignment = fieldTypeLayout.alignment; } - *outSizeAndAlignment = rules->alignCompositeElement(structLayout); + *outSizeAndAlignment = alignCompositeElement(structLayout); return SLANG_OK; } break; @@ -219,7 +214,7 @@ static Result _calcSizeAndAlignment( return _calcArraySizeAndAlignment( optionSet, - rules, + this, arrayType->getElementType(), arrayType->getElementCount(), outSizeAndAlignment); @@ -229,11 +224,7 @@ static Result _calcSizeAndAlignment( case kIROp_AtomicType: { auto atomicType = cast<IRAtomicType>(type); - _calcSizeAndAlignment( - optionSet, - rules, - atomicType->getElementType(), - outSizeAndAlignment); + calcSizeAndAlignment(optionSet, atomicType->getElementType(), outSizeAndAlignment); return SLANG_OK; } break; @@ -243,7 +234,7 @@ static Result _calcSizeAndAlignment( auto unsizedArrayType = cast<IRUnsizedArrayType>(type); getSizeAndAlignment( optionSet, - rules, + this, unsizedArrayType->getElementType(), outSizeAndAlignment); outSizeAndAlignment->size = IRSizeAndAlignment::kIndeterminateSize; @@ -255,8 +246,8 @@ static Result _calcSizeAndAlignment( { auto vecType = cast<IRVectorType>(type); IRSizeAndAlignment elementTypeLayout; - getSizeAndAlignment(optionSet, rules, vecType->getElementType(), &elementTypeLayout); - *outSizeAndAlignment = rules->getVectorSizeAndAlignment( + getSizeAndAlignment(optionSet, this, vecType->getElementType(), &elementTypeLayout); + *outSizeAndAlignment = getVectorSizeAndAlignment( elementTypeLayout, getIntegerValueFromInst(vecType->getElementCount())); return SLANG_OK; @@ -267,7 +258,7 @@ static Result _calcSizeAndAlignment( auto anyValType = cast<IRAnyValueType>(type); outSizeAndAlignment->size = getIntVal(anyValType->getSize()); outSizeAndAlignment->alignment = 4; - *outSizeAndAlignment = rules->alignCompositeElement(*outSizeAndAlignment); + *outSizeAndAlignment = alignCompositeElement(*outSizeAndAlignment); return SLANG_OK; } break; @@ -282,8 +273,8 @@ static Result _calcSizeAndAlignment( auto elementType = tupleType->getOperand(i); IRSizeAndAlignment fieldTypeLayout; SLANG_RETURN_ON_FAIL( - getSizeAndAlignment(optionSet, rules, (IRType*)elementType, &fieldTypeLayout)); - resultLayout.size = rules->adjustOffset( + getSizeAndAlignment(optionSet, this, (IRType*)elementType, &fieldTypeLayout)); + resultLayout.size = adjustOffset( resultLayout.size, fieldTypeLayout.size, lastFieldType, @@ -296,7 +287,7 @@ static Result _calcSizeAndAlignment( lastFieldType = as<IRType>(elementType); lastFieldAlignment = fieldTypeLayout.alignment; } - *outSizeAndAlignment = rules->alignCompositeElement(resultLayout); + *outSizeAndAlignment = alignCompositeElement(resultLayout); return SLANG_OK; } break; @@ -320,7 +311,7 @@ static Result _calcSizeAndAlignment( IRSizeAndAlignment resultLayout; resultLayout.size = size; resultLayout.alignment = 4; - *outSizeAndAlignment = rules->alignCompositeElement(resultLayout); + *outSizeAndAlignment = alignCompositeElement(resultLayout); return SLANG_OK; } break; @@ -334,7 +325,7 @@ static Result _calcSizeAndAlignment( builder.getVectorType(matType->getElementType(), matType->getRowCount()); return _calcArraySizeAndAlignment( optionSet, - rules, + this, colVector, matType->getColumnCount(), outSizeAndAlignment); @@ -345,7 +336,7 @@ static Result _calcSizeAndAlignment( builder.getVectorType(matType->getElementType(), matType->getColumnCount()); return _calcArraySizeAndAlignment( optionSet, - rules, + this, rowVector, matType->getRowCount(), outSizeAndAlignment); @@ -369,6 +360,7 @@ static Result _calcSizeAndAlignment( } break; case kIROp_ScalarBufferLayoutType: + case kIROp_CBufferLayoutType: case kIROp_Std140BufferLayoutType: case kIROp_Std430BufferLayoutType: case kIROp_DefaultBufferLayoutType: @@ -380,7 +372,7 @@ static Result _calcSizeAndAlignment( builder.setInsertBefore(type); auto uintType = builder.getUIntType(); auto uint2Type = builder.getVectorType(uintType, 2); - return getSizeAndAlignment(optionSet, rules, uint2Type, outSizeAndAlignment); + return getSizeAndAlignment(optionSet, this, uint2Type, outSizeAndAlignment); } case kIROp_AttributedType: { @@ -388,7 +380,7 @@ static Result _calcSizeAndAlignment( SLANG_ASSERT(attributedType->getAttr()->getOp() == kIROp_NoDiffAttr); return getSizeAndAlignment( optionSet, - rules, + this, attributedType->getBaseType(), outSizeAndAlignment); } @@ -396,7 +388,7 @@ static Result _calcSizeAndAlignment( { auto enumType = cast<IREnumType>(type); auto tagType = enumType->getTagType(); - return _calcSizeAndAlignment(optionSet, rules, tagType, outSizeAndAlignment); + return calcSizeAndAlignment(optionSet, tagType, outSizeAndAlignment); } break; default: @@ -439,7 +431,7 @@ Result getSizeAndAlignment( } IRSizeAndAlignment sizeAndAlignment; - SLANG_RETURN_ON_FAIL(_calcSizeAndAlignment(optionSet, rules, type, &sizeAndAlignment)); + SLANG_RETURN_ON_FAIL(rules->calcSizeAndAlignment(optionSet, type, &sizeAndAlignment)); if (auto module = type->getModule()) { @@ -538,6 +530,49 @@ struct NaturalLayoutRules : IRTypeLayoutRules } }; +struct CLayoutRules : IRTypeLayoutRules +{ + CLayoutRules() { ruleName = IRTypeLayoutRuleName::C; } + + virtual Result calcSizeAndAlignment( + CompilerOptionSet& optionSet, + IRType* type, + IRSizeAndAlignment* outSizeAndAlignment) + { + if (type->getOp() == kIROp_BoolType) + { + *outSizeAndAlignment = IRSizeAndAlignment(1, 1); + return SLANG_OK; + } + return IRTypeLayoutRules::calcSizeAndAlignment(optionSet, type, outSizeAndAlignment); + } + + virtual IRIntegerValue adjustOffset( + IRIntegerValue offset, + IRIntegerValue elementSize, + IRType* lastFieldType, + IRIntegerValue lastFieldAlignment) + { + SLANG_UNUSED(elementSize); + SLANG_UNUSED(lastFieldType); + return align(offset, (int)lastFieldAlignment); + } + + virtual IRSizeAndAlignment alignCompositeElement(IRSizeAndAlignment elementSize) + { + IRSizeAndAlignment alignedSize = elementSize; + alignedSize.size = align(alignedSize.size, alignedSize.alignment); + return alignedSize; + } + + virtual IRSizeAndAlignment getVectorSizeAndAlignment( + IRSizeAndAlignment element, + IRIntegerValue count) + { + return IRSizeAndAlignment(element.size * count, element.alignment); + } +}; + struct ConstantBufferLayoutRules : IRTypeLayoutRules { ConstantBufferLayoutRules() { ruleName = IRTypeLayoutRuleName::D3DConstantBuffer; } @@ -713,6 +748,12 @@ IRTypeLayoutRules* IRTypeLayoutRules::getNatural() return &rules; } +IRTypeLayoutRules* IRTypeLayoutRules::getC() +{ + static CLayoutRules rules; + return &rules; +} + IRTypeLayoutRules* IRTypeLayoutRules::getConstantBuffer() { static ConstantBufferLayoutRules rules; @@ -729,6 +770,8 @@ IRTypeLayoutRules* IRTypeLayoutRules::get(IRTypeLayoutRuleName name) return getStd140(); case IRTypeLayoutRuleName::Natural: return getNatural(); + case IRTypeLayoutRuleName::C: + return getC(); case IRTypeLayoutRuleName::D3DConstantBuffer: return getConstantBuffer(); default: diff --git a/source/slang/slang-ir-layout.h b/source/slang/slang-ir-layout.h index 1918f31b6..a0f144c86 100644 --- a/source/slang/slang-ir-layout.h +++ b/source/slang/slang-ir-layout.h @@ -56,6 +56,12 @@ struct IRTypeLayoutRules public: IRTypeLayoutRuleName ruleName; + /// This function calculates the size and alignment of the given type. + virtual Result calcSizeAndAlignment( + CompilerOptionSet& optionSet, + IRType* type, + IRSizeAndAlignment* outSizeAndAlignment); + /// Align composite based on rule. Type is aligned assuming /// it is apart of a composite (array, struct, matrix, etc...) virtual IRSizeAndAlignment alignCompositeElement(IRSizeAndAlignment elementSize) = 0; @@ -79,6 +85,7 @@ public: static IRTypeLayoutRules* getStd430(); static IRTypeLayoutRules* getStd140(); static IRTypeLayoutRules* getNatural(); + static IRTypeLayoutRules* getC(); static IRTypeLayoutRules* getConstantBuffer(); static IRTypeLayoutRules* get(IRTypeLayoutRuleName name); }; diff --git a/source/slang/slang-ir-lower-buffer-element-type.cpp b/source/slang/slang-ir-lower-buffer-element-type.cpp index ed0a3b309..2f40c6cf9 100644 --- a/source/slang/slang-ir-lower-buffer-element-type.cpp +++ b/source/slang/slang-ir-lower-buffer-element-type.cpp @@ -426,6 +426,8 @@ struct LoweredElementTypeContext return "std430"; case IRTypeLayoutRuleName::Natural: return "natural"; + case IRTypeLayoutRuleName::C: + return "c"; default: return "default"; } @@ -797,7 +799,26 @@ struct LoweredElementTypeContext if (as<IRBoolType>(scalarType)) { // Bool is an abstract type in SPIRV, so we need to lower them into an int. - info.loweredType = builder.getIntType(); + + // Find an integer type of the correct size for the current layout rule. + IRSizeAndAlignment boolSizeAndAlignment; + if (getSizeAndAlignment( + target->getOptionSet(), + config.layoutRule, + scalarType, + &boolSizeAndAlignment) == SLANG_OK) + { + IntInfo ii; + ii.width = boolSizeAndAlignment.size * 8; + ii.isSigned = true; + info.loweredType = builder.getType(getIntTypeOpFromInfo(ii)); + } + else + { + // Just in case that fails for some reason, just use an int. + info.loweredType = builder.getIntType(); + } + if (vectorType) info.loweredType = builder.getVectorType( info.loweredType, @@ -1467,6 +1488,8 @@ IRTypeLayoutRules* getTypeLayoutRulesFromOp(IROp layoutTypeOp, IRTypeLayoutRules return IRTypeLayoutRules::getStd430(); case kIROp_ScalarBufferLayoutType: return IRTypeLayoutRules::getNatural(); + case kIROp_CBufferLayoutType: + return IRTypeLayoutRules::getC(); } return defaultLayout; } @@ -1482,6 +1505,10 @@ IRTypeLayoutRules* getTypeLayoutRuleForBuffer(TargetProgram* target, IRType* buf if (!target->shouldEmitSPIRVDirectly()) return IRTypeLayoutRules::getNatural(); + // If the user specified a C-compatible buffer layout, then do that. + if (target->getOptionSet().shouldUseCLayout()) + return IRTypeLayoutRules::getC(); + // If the user specified a scalar buffer layout, then just use that. if (target->getOptionSet().shouldUseScalarLayout()) return IRTypeLayoutRules::getNatural(); diff --git a/source/slang/slang-ir.h b/source/slang/slang-ir.h index 1f6418036..ad14edf21 100644 --- a/source/slang/slang-ir.h +++ b/source/slang/slang-ir.h @@ -524,6 +524,7 @@ enum class IRTypeLayoutRuleName Std430, Std140, D3DConstantBuffer, + C, _Count, }; diff --git a/source/slang/slang-options.cpp b/source/slang/slang-options.cpp index b0eb1748d..2115a096f 100644 --- a/source/slang/slang-options.cpp +++ b/source/slang/slang-options.cpp @@ -663,6 +663,12 @@ void initCommandOptions(CommandOptions& options) "-fvk-use-dx-layout", nullptr, "Pack members using FXCs member packing rules when targeting GLSL or SPIRV."}, + {OptionKind::ForceCLayout, + "-fvk-use-c-layout", + nullptr, + "Make data accessed through ConstantBuffer, ParameterBlock, StructuredBuffer, " + "ByteAddressBuffer and general pointers follow the C/C++ structure layout rules " + "when targeting SPIRV."}, {OptionKind::VulkanBindShift, vkShiftNames.getBuffer(), "-fvk-<vulkan-shift>-shift <N> <space>", @@ -2670,6 +2676,11 @@ SlangResult OptionsParser::_parse(int argc, char const* const* argv) getCurrentTarget()->optionSet.add(CompilerOptionName::ForceDXLayout, true); break; } + case OptionKind::ForceCLayout: + { + getCurrentTarget()->optionSet.add(CompilerOptionName::ForceCLayout, true); + break; + } case OptionKind::EnableEffectAnnotations: { m_compileRequest->setEnableEffectAnnotations(true); @@ -3707,6 +3718,11 @@ SlangResult OptionsParser::_parse(int argc, char const* const* argv) m_compileRequest->setTargetForceDXLayout(targetID, true); } + if (rawTarget.optionSet.shouldUseCLayout()) + { + m_compileRequest->setTargetForceCLayout(targetID, true); + } + if (rawTarget.optionSet.getBoolOption(CompilerOptionName::GenerateWholeProgram)) { m_compileRequest->setTargetGenerateWholeProgram(targetID, true); diff --git a/source/slang/slang-type-layout.cpp b/source/slang/slang-type-layout.cpp index 275a178d0..3e2560755 100644 --- a/source/slang/slang-type-layout.cpp +++ b/source/slang/slang-type-layout.cpp @@ -1090,6 +1090,29 @@ struct CPULayoutRulesFamilyImpl : LayoutRulesFamilyImpl LayoutRulesImpl* getStructuredBufferRules(CompilerOptionSet& compilerOptions) override; }; +struct CLayoutRulesFamilyImpl : LayoutRulesFamilyImpl +{ + virtual LayoutRulesImpl* getAnyValueRules() override; + virtual LayoutRulesImpl* getConstantBufferRules( + CompilerOptionSet& compilerOptions, + Type* containerType) override; + virtual LayoutRulesImpl* getPushConstantBufferRules() override; + virtual LayoutRulesImpl* getTextureBufferRules(CompilerOptionSet& compilerOptions) override; + virtual LayoutRulesImpl* getVaryingInputRules() override; + virtual LayoutRulesImpl* getVaryingOutputRules() override; + virtual LayoutRulesImpl* getSpecializationConstantRules() override; + virtual LayoutRulesImpl* getShaderStorageBufferRules( + CompilerOptionSet& compilerOptions) override; + virtual LayoutRulesImpl* getParameterBlockRules(CompilerOptionSet& compilerOptions) override; + + LayoutRulesImpl* getRayPayloadParameterRules() override; + LayoutRulesImpl* getCallablePayloadParameterRules() override; + LayoutRulesImpl* getHitAttributesParameterRules() override; + + LayoutRulesImpl* getShaderRecordConstantBufferRules() override; + LayoutRulesImpl* getStructuredBufferRules(CompilerOptionSet& compilerOptions) override; +}; + struct CUDALayoutRulesFamilyImpl : LayoutRulesFamilyImpl { virtual LayoutRulesImpl* getAnyValueRules() override; @@ -1170,6 +1193,7 @@ struct WGSLLayoutRulesFamilyImpl : LayoutRulesFamilyImpl GLSLLayoutRulesFamilyImpl kGLSLLayoutRulesFamilyImpl; HLSLLayoutRulesFamilyImpl kHLSLLayoutRulesFamilyImpl; CPULayoutRulesFamilyImpl kCPULayoutRulesFamilyImpl; +CLayoutRulesFamilyImpl kCLayoutRulesFamilyImpl; CUDALayoutRulesFamilyImpl kCUDALayoutRulesFamilyImpl; MetalLayoutRulesFamilyImpl kMetalLayoutRulesFamilyImpl; MetalArgumentBufferTier2LayoutRulesFamilyImpl kMetalArgumentBufferTier2LayoutRulesFamilyImpl; @@ -1341,6 +1365,69 @@ LayoutRulesImpl kCPUAnyValueLayoutRulesImpl_ = { &kCPUObjectLayoutRulesImpl, }; +// C layout + +LayoutRulesImpl kCLayoutRulesImpl_ = { + &kCLayoutRulesFamilyImpl, + &kCPULayoutRulesImpl, + &kGLSLObjectLayoutRulesImpl, +}; + +LayoutRulesImpl kCAnyValueLayoutRulesImpl_ = { + &kCLayoutRulesFamilyImpl, + &kDefaultLayoutRulesImpl, + &kCPUObjectLayoutRulesImpl, +}; + +LayoutRulesImpl kCPushConstantRulesImpl_ = { + &kCLayoutRulesFamilyImpl, + &kCPULayoutRulesImpl, + &kGLSLPushConstantBufferObjectLayoutRulesImpl_, +}; + +LayoutRulesImpl kCVaryingInputLayoutRulesImpl_ = { + &kCLayoutRulesFamilyImpl, + &kGLSLVaryingOutputLayoutRulesImpl, + &kGLSLObjectLayoutRulesImpl, +}; + +LayoutRulesImpl kCVaryingOutputLayoutRulesImpl_ = { + &kCLayoutRulesFamilyImpl, + &kGLSLVaryingOutputLayoutRulesImpl, + &kGLSLObjectLayoutRulesImpl, +}; + +LayoutRulesImpl kCSpecializationConstantLayoutRulesImpl_ = { + &kCLayoutRulesFamilyImpl, + &kGLSLSpecializationConstantLayoutRulesImpl, + &kGLSLObjectLayoutRulesImpl, +}; + +LayoutRulesImpl kCShaderRecordLayoutRulesImpl_ = { + &kCLayoutRulesFamilyImpl, + &kCPULayoutRulesImpl, + &kGLSLShaderRecordConstantBufferObjectLayoutRulesImpl_, +}; + +LayoutRulesImpl kCRayPayloadParameterLayoutRulesImpl_ = { + &kCLayoutRulesFamilyImpl, + &kGLSLRayPayloadParameterLayoutRulesImpl, + &kGLSLObjectLayoutRulesImpl, +}; + +LayoutRulesImpl kCCallablePayloadParameterLayoutRulesImpl_ = { + &kCLayoutRulesFamilyImpl, + &kGLSLCallablePayloadParameterLayoutRulesImpl, + &kGLSLObjectLayoutRulesImpl, +}; + +LayoutRulesImpl kCHitAttributesParameterLayoutRulesImpl_ = { + &kCLayoutRulesFamilyImpl, + &kGLSLHitAttributesParameterLayoutRulesImpl, + &kGLSLObjectLayoutRulesImpl, +}; + + // CUDA static CUDAObjectLayoutRulesImpl kCUDAObjectLayoutRulesImpl; @@ -1534,6 +1621,8 @@ LayoutRulesImpl* GLSLLayoutRulesFamilyImpl::getConstantBufferRules( { if (compilerOptions.shouldUseScalarLayout()) return &kScalarLayoutRulesImpl_; + else if (compilerOptions.shouldUseCLayout()) + return &kCLayoutRulesImpl_; else if (compilerOptions.shouldUseDXLayout()) return &kFXCConstantBufferLayoutRulesFamilyImpl; if (auto cbufferType = as<ConstantBufferType>(containerType)) @@ -1548,6 +1637,8 @@ LayoutRulesImpl* GLSLLayoutRulesFamilyImpl::getConstantBufferRules( return &kStd430LayoutRulesImpl_; case ASTNodeType::ScalarDataLayoutType: return &kScalarLayoutRulesImpl_; + case ASTNodeType::CDataLayoutType: + return &kCLayoutRulesImpl_; default: break; } @@ -1560,6 +1651,8 @@ LayoutRulesImpl* GLSLLayoutRulesFamilyImpl::getParameterBlockRules( { if (compilerOptions.shouldUseScalarLayout()) return &kScalarLayoutRulesImpl_; + else if (compilerOptions.shouldUseCLayout()) + return &kCLayoutRulesImpl_; else if (compilerOptions.shouldUseDXLayout()) return &kFXCConstantBufferLayoutRulesFamilyImpl; @@ -1581,6 +1674,8 @@ LayoutRulesImpl* GLSLLayoutRulesFamilyImpl::getTextureBufferRules( { if (compilerOptions.shouldUseScalarLayout()) return &kScalarLayoutRulesImpl_; + else if (compilerOptions.shouldUseCLayout()) + return &kCLayoutRulesImpl_; else if (compilerOptions.shouldUseDXLayout()) return &kFXCConstantBufferLayoutRulesFamilyImpl; @@ -1607,6 +1702,8 @@ LayoutRulesImpl* GLSLLayoutRulesFamilyImpl::getShaderStorageBufferRules( { if (compilerOptions.shouldUseScalarLayout()) return &kScalarLayoutRulesImpl_; + else if (compilerOptions.shouldUseCLayout()) + return &kCLayoutRulesImpl_; else if (compilerOptions.shouldUseDXLayout()) return &kFXCShaderResourceLayoutRulesFamilyImpl; @@ -1633,6 +1730,8 @@ LayoutRulesImpl* GLSLLayoutRulesFamilyImpl::getStructuredBufferRules( { if (compilerOptions.shouldUseScalarLayout()) return &kScalarLayoutRulesImpl_; + else if (compilerOptions.shouldUseCLayout()) + return &kCLayoutRulesImpl_; else if (compilerOptions.shouldUseDXLayout()) return &kFXCShaderResourceLayoutRulesFamilyImpl; @@ -1779,6 +1878,70 @@ LayoutRulesImpl* CPULayoutRulesFamilyImpl::getStructuredBufferRules(CompilerOpti return &kCPULayoutRulesImpl_; } +// C compatible layout family + +LayoutRulesImpl* CLayoutRulesFamilyImpl::getAnyValueRules() +{ + return &kCAnyValueLayoutRulesImpl_; +} + +LayoutRulesImpl* CLayoutRulesFamilyImpl::getConstantBufferRules(CompilerOptionSet&, Type*) +{ + return &kCLayoutRulesImpl_; +} + +LayoutRulesImpl* CLayoutRulesFamilyImpl::getPushConstantBufferRules() +{ + return &kCPushConstantRulesImpl_; +} + +LayoutRulesImpl* CLayoutRulesFamilyImpl::getTextureBufferRules(CompilerOptionSet&) +{ + return &kCLayoutRulesImpl_; +} + +LayoutRulesImpl* CLayoutRulesFamilyImpl::getVaryingInputRules() +{ + return &kCVaryingInputLayoutRulesImpl_; +} +LayoutRulesImpl* CLayoutRulesFamilyImpl::getVaryingOutputRules() +{ + return &kCVaryingOutputLayoutRulesImpl_; +} +LayoutRulesImpl* CLayoutRulesFamilyImpl::getSpecializationConstantRules() +{ + return &kCSpecializationConstantLayoutRulesImpl_; +} +LayoutRulesImpl* CLayoutRulesFamilyImpl::getShaderStorageBufferRules(CompilerOptionSet&) +{ + return &kCLayoutRulesImpl_; +} +LayoutRulesImpl* CLayoutRulesFamilyImpl::getParameterBlockRules(CompilerOptionSet&) +{ + return &kCLayoutRulesImpl_; +} +LayoutRulesImpl* CLayoutRulesFamilyImpl::getRayPayloadParameterRules() +{ + return &kCRayPayloadParameterLayoutRulesImpl_; +} +LayoutRulesImpl* CLayoutRulesFamilyImpl::getCallablePayloadParameterRules() +{ + return &kCCallablePayloadParameterLayoutRulesImpl_; +} +LayoutRulesImpl* CLayoutRulesFamilyImpl::getHitAttributesParameterRules() +{ + return &kCHitAttributesParameterLayoutRulesImpl_; +} +LayoutRulesImpl* CLayoutRulesFamilyImpl::getShaderRecordConstantBufferRules() +{ + return &kCShaderRecordLayoutRulesImpl_; +} + +LayoutRulesImpl* CLayoutRulesFamilyImpl::getStructuredBufferRules(CompilerOptionSet&) +{ + return &kCLayoutRulesImpl_; +} + // CUDA Family LayoutRulesImpl* CUDALayoutRulesFamilyImpl::getAnyValueRules() diff --git a/tests/spirv/c-layout-buffer-2.slang b/tests/spirv/c-layout-buffer-2.slang new file mode 100644 index 000000000..0d388d095 --- /dev/null +++ b/tests/spirv/c-layout-buffer-2.slang @@ -0,0 +1,57 @@ +//TEST:SIMPLE(filecheck=SPIRV): -stage closesthit -entry chit -target spirv -fvk-use-c-layout +struct RayPayload +{ + float3 color; +}; + +struct A { + int64_t a1; + int32_t a2; +}; + +struct B { + A a; + uint32_t b; +}; + +struct Push { + B *p; +}; + +[[vk::push_constant]] Push push; + +struct C { + int i; + A a; +}; + +struct D { + C c; + int j; +}; + +[[vk::shader_record]] +cbuffer ShaderRecord +{ + D d; +} + +[[shader("closesthit")]] +void chit(inout RayPayload payload : SV_RayPayload, in float2 barys_yz : SV_IntersectionAttributes) { + let barys = float3(1.0 - barys_yz.x - barys_yz.y, barys_yz); + payload.color = barys; + + push.p->b = d.j + d.c.i; +} + +// SPIRV: OpMemberDecorate %A_c 0 Offset 0 +// SPIRV: OpMemberDecorate %A_c 1 Offset 8 +// SPIRV: OpMemberDecorate %C_c 0 Offset 0 +// SPIRV: OpMemberDecorate %C_c 1 Offset 8 +// SPIRV: OpMemberDecorate %D_c 0 Offset 0 +// SPIRV: OpMemberDecorate %D_c 1 Offset 24 +// SPIRV: OpMemberDecorate %B_c 0 Offset 0 +// SPIRV: OpMemberDecorate %B_c 1 Offset 16 + +// SPIRV: %_ptr_PushConstant_Push_c = OpTypePointer PushConstant %Push_c +// SPIRV: %_ptr_ShaderRecordBufferKHR_SLANG_ParameterGroup_ShaderRecord_c = OpTypePointer ShaderRecordBufferKHR %SLANG_ParameterGroup_ShaderRecord_c diff --git a/tests/spirv/c-layout-buffer.slang b/tests/spirv/c-layout-buffer.slang new file mode 100644 index 000000000..c94d5402d --- /dev/null +++ b/tests/spirv/c-layout-buffer.slang @@ -0,0 +1,57 @@ +//TEST(compute):COMPARE_COMPUTE_EX(filecheck-buffer=CHECK): -vk -emit-spirv-directly -compute +//TEST:SIMPLE(filecheck=SPIRV): -stage compute -entry computeMain -target spirv -fvk-use-c-layout +struct A +{ + uint64_t f0; + uint32_t f1; + // Padding of 4 bytes must exist in CPU layout, but not in scalar layout. +}; + +struct B +{ + float3 f; +}; + +struct C // Total size should be 40 bytes. +{ + A a; // 16 bytes, alignment 8 + uint32_t test1; // 4 bytes, alignment 4 + B b; // 12 bytes, alignment 4 + uint32_t test2; // 4 bytes, alignment 4 + bool c; // 1 bytes, alignment 1 + bool d; // 1 bytes, alignment 1 +}; + +//TEST_INPUT:set cpuBuffer = ubuffer(data=[1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20], stride=4) +StructuredBuffer<C, CDataLayout> cpuBuffer; + +//TEST_INPUT:set outputBuffer = out ubuffer(data=[0 0 0 0], stride=4) +RWStructuredBuffer<int> outputBuffer; + +[numthreads(1,1,1)] +void computeMain() +{ + // The CPU layout should be the same as ScalarDataLayout, except that the + // size of structs is rounded up to their alignment. + + // CHECK: 5 + // CHECK: 9 + // CHECK: F + // CHECK: 13 + outputBuffer[0] = cpuBuffer[0].test1; + outputBuffer[1] = cpuBuffer[0].test2; + outputBuffer[2] = cpuBuffer[1].test1; + outputBuffer[3] = cpuBuffer[1].test2; +} + +// SPIRV: OpMemberDecorate %A_c 0 Offset 0 +// SPIRV: OpMemberDecorate %A_c 1 Offset 8 +// SPIRV: OpMemberDecorate %B_c 0 Offset 0 +// SPIRV: OpMemberDecorate %C_c 0 Offset 0 +// SPIRV: OpMemberDecorate %C_c 1 Offset 16 +// SPIRV: OpMemberDecorate %C_c 2 Offset 20 +// SPIRV: OpMemberDecorate %C_c 3 Offset 32 +// SPIRV: OpMemberDecorate %C_c 4 Offset 36 +// SPIRV: OpMemberDecorate %C_c 5 Offset 37 +// SPIRV: OpDecorate %_runtimearr_C_c ArrayStride 40 + |
