diff options
| author | Yong He <yonghe@outlook.com> | 2024-01-19 18:02:40 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-01-19 18:02:40 -0800 |
| commit | fdc17a974970559d8ff76d52c3ce40aaa056d441 (patch) | |
| tree | a7b2264776982707b36f901ac50acd4e047fd2bd /source | |
| parent | 84b214389cacde9f3c94d61b3d4ca6a927cecd04 (diff) | |
Add `-fspv-reflect` support. (#3464)
* Add `-fspv-reflect` support.
Closes #3462.
* Fix.
* Fix.
* Remove use of `SPV_GOOGLE_hlsl_functionality1`.
* Fix spirv validation error.
* Fix test.
* Update typename hints.
* Update commandline options doc.
* Remove superfluous empty lines.
---------
Co-authored-by: Yong He <yhe@nvidia.com>
Diffstat (limited to 'source')
| -rw-r--r-- | source/slang/slang-emit-spirv-ops.h | 31 | ||||
| -rw-r--r-- | source/slang/slang-emit-spirv.cpp | 57 | ||||
| -rw-r--r-- | source/slang/slang-hlsl-to-vulkan-layout-options.cpp | 2 | ||||
| -rw-r--r-- | source/slang/slang-hlsl-to-vulkan-layout-options.h | 7 | ||||
| -rw-r--r-- | source/slang/slang-ir-inst-defs.h | 5 | ||||
| -rw-r--r-- | source/slang/slang-ir-insts.h | 20 | ||||
| -rw-r--r-- | source/slang/slang-ir-legalize-types.cpp | 57 | ||||
| -rw-r--r-- | source/slang/slang-ir-lower-append-consume-structured-buffer.cpp | 2 | ||||
| -rw-r--r-- | source/slang/slang-ir-spirv-legalize.cpp | 15 | ||||
| -rw-r--r-- | source/slang/slang-ir-util.cpp | 190 | ||||
| -rw-r--r-- | source/slang/slang-options.cpp | 8 |
11 files changed, 383 insertions, 11 deletions
diff --git a/source/slang/slang-emit-spirv-ops.h b/source/slang/slang-emit-spirv-ops.h index 8840d033f..891372fa6 100644 --- a/source/slang/slang-emit-spirv-ops.h +++ b/source/slang/slang-emit-spirv-ops.h @@ -691,6 +691,35 @@ SpvInst* emitOpDecorateBuiltIn( } // https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#OpDecorate +template<typename T> +SpvInst* emitOpMemberDecorateString( + SpvInstParent* parent, + IRInst* inst, + const T& target, + const SpvLiteralInteger& index, + SpvDecoration decoration, + UnownedStringSlice text +) +{ + static_assert(isSingular<T>); + return emitInst(parent, inst, SpvOpDecorate, target, index, decoration, text); +} + +// https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#OpDecorate +template<typename T> +SpvInst* emitOpDecorateString( + SpvInstParent* parent, + IRInst* inst, + const T& target, + SpvDecoration decoration, + UnownedStringSlice text +) +{ + static_assert(isSingular<T>); + return emitInst(parent, inst, SpvOpDecorate, target, decoration, text); +} + +// https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#OpDecorate template<typename T1, typename T2> SpvInst* emitOpDecorateUniformId( SpvInstParent* parent, @@ -808,7 +837,7 @@ SpvInst* emitOpDecorateCounterBuffer( { static_assert(isSingular<T1>); static_assert(isSingular<T2>); - return emitInst(parent, inst, SpvOpDecorate, target, SpvDecorationCounterBuffer, counterBuffer); + return emitInst(parent, inst, SpvOpDecorateId, target, SpvDecorationCounterBuffer, counterBuffer); } // https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#OpDecorate diff --git a/source/slang/slang-emit-spirv.cpp b/source/slang/slang-emit-spirv.cpp index 07f8a4cd2..a600d4b10 100644 --- a/source/slang/slang-emit-spirv.cpp +++ b/source/slang/slang-emit-spirv.cpp @@ -1237,6 +1237,13 @@ struct SPIRVEmitContext Dictionary<SpvTypeInstKey, SpvInst*> m_spvTypeInsts; + bool shouldEmitSPIRVReflectionInfo() + { + if (!m_targetRequest->getHLSLToVulkanLayoutOptions()) + return false; + return m_targetRequest->getHLSLToVulkanLayoutOptions()->shouldEmitSPIRVReflectionInfo(); + } + // Next, let's look at emitting some of the instructions // that can occur at global scope. @@ -1958,6 +1965,7 @@ struct SPIRVEmitContext if (auto layout = getVarLayout(param)) emitVarLayout(param, varInst, layout); maybeEmitName(varInst, param); + emitDecorations(param, getID(varInst)); return varInst; } @@ -2833,6 +2841,42 @@ struct SPIRVEmitContext break; // ... } + + if (shouldEmitSPIRVReflectionInfo()) + { + switch (decoration->getOp()) + { + default: + break; + case kIROp_SemanticDecoration: + { + emitOpDecorateString(getSection(SpvLogicalSectionID::Annotations), + decoration, + dstID, + SpvDecorationUserSemantic, + cast<IRSemanticDecoration>(decoration)->getSemanticName()); + } + break; + case kIROp_UserTypeNameDecoration: + { + ensureExtensionDeclaration(toSlice("SPV_GOOGLE_user_type")); + emitOpDecorateString(getSection(SpvLogicalSectionID::Annotations), + decoration, + dstID, + SpvDecorationUserTypeGOOGLE, + cast<IRUserTypeNameDecoration>(decoration)->getUserTypeName()->getStringSlice()); + } + break; + case kIROp_CounterBufferDecoration: + { + emitOpDecorateCounterBuffer(getSection(SpvLogicalSectionID::Annotations), + decoration, + dstID, + as<IRCounterBufferDecoration>(decoration)->getCounterBuffer()); + } + break; + } + } } void emitLayoutDecorations(IRStructType* structType, SpvWord spvStructID) @@ -2948,6 +2992,19 @@ struct SPIRVEmitContext SpvLiteralInteger::from32(id), SpvLiteralInteger::from32((int32_t)matrixStride)); } + if (shouldEmitSPIRVReflectionInfo()) + { + if (auto semanticDecor = field->getKey()->findDecoration<IRSemanticDecoration>()) + { + emitOpMemberDecorateString( + getSection(SpvLogicalSectionID::Annotations), + nullptr, + spvStructID, + SpvLiteralInteger::from32(id), + SpvDecorationUserSemantic, + semanticDecor->getSemanticName()); + } + } id++; } } diff --git a/source/slang/slang-hlsl-to-vulkan-layout-options.cpp b/source/slang/slang-hlsl-to-vulkan-layout-options.cpp index d15b60f3b..36044e43a 100644 --- a/source/slang/slang-hlsl-to-vulkan-layout-options.cpp +++ b/source/slang/slang-hlsl-to-vulkan-layout-options.cpp @@ -92,7 +92,7 @@ Index HLSLToVulkanLayoutOptions::getShift(Kind kind, Index set) const bool HLSLToVulkanLayoutOptions::hasState() const { return canInferBindings() || hasGlobalsBinding() || shouldInvertY() || getUseOriginalEntryPointName() - || shouldUseGLLayout(); + || shouldUseGLLayout() || shouldEmitSPIRVReflectionInfo(); } HLSLToVulkanLayoutOptions::Binding HLSLToVulkanLayoutOptions::inferBinding(Kind kind, const Binding& inBinding) const diff --git a/source/slang/slang-hlsl-to-vulkan-layout-options.h b/source/slang/slang-hlsl-to-vulkan-layout-options.h index c88805412..97de2a610 100644 --- a/source/slang/slang-hlsl-to-vulkan-layout-options.h +++ b/source/slang/slang-hlsl-to-vulkan-layout-options.h @@ -121,6 +121,8 @@ public: bool shouldUseGLLayout() const { return m_useGLLayout; } + bool shouldEmitSPIRVReflectionInfo() const { return m_emitSPIRVReflectionInfo; } + bool getUseOriginalEntryPointName() const { return m_useOriginalEntryPointName; } /// Given an kind and a binding infer the vulkan binding. @@ -153,6 +155,8 @@ public: void setUseGLLayout(bool value) { m_useGLLayout = value; } + void setEmitSPIRVReflectionInfo(bool value) { m_emitSPIRVReflectionInfo = value; } + /// Ctor HLSLToVulkanLayoutOptions(); @@ -185,6 +189,9 @@ protected: /// If set, raw buffer load/stores will follow std430 layout. bool m_useGLLayout = false; + + /// If set, will emit SPIR-V reflection info. + bool m_emitSPIRVReflectionInfo = false; }; } // namespace Slang diff --git a/source/slang/slang-ir-inst-defs.h b/source/slang/slang-ir-inst-defs.h index 453fbf16d..ff0b815d4 100644 --- a/source/slang/slang-ir-inst-defs.h +++ b/source/slang/slang-ir-inst-defs.h @@ -853,6 +853,11 @@ INST(HighLevelDeclDecoration, highLevelDecl, 1, 0) INST(SemanticDecoration, semantic, 2, 0) INST(PackOffsetDecoration, packoffset, 2, 0) + // Reflection metadata for a shader parameter that provides the original type name. + INST(UserTypeNameDecoration, UserTypeName, 1, 0) + // Reflection metadata for a shader parameter that refers to the associated counter buffer of a UAV. + INST(CounterBufferDecoration, CounterBuffer, 1, 0) + INST(RequireSPIRVDescriptorIndexingExtensionDecoration, RequireSPIRVDescriptorIndexingExtensionDecoration, 0, 0) INST(SPIRVOpDecoration, spirvOpDecoration, 1, 0) diff --git a/source/slang/slang-ir-insts.h b/source/slang/slang-ir-insts.h index 2a7852c1e..2d24efb43 100644 --- a/source/slang/slang-ir-insts.h +++ b/source/slang/slang-ir-insts.h @@ -1268,6 +1268,26 @@ struct IRPackOffsetDecoration : IRDecoration IRIntLit* getComponentOffset() { return cast<IRIntLit>(getOperand(1)); } }; +struct IRUserTypeNameDecoration : IRDecoration +{ + enum + { + kOp = kIROp_UserTypeNameDecoration + }; + IR_LEAF_ISA(UserTypeNameDecoration) + IRStringLit* getUserTypeName() { return cast<IRStringLit>(getOperand(0)); } +}; + +struct IRCounterBufferDecoration : IRDecoration +{ + enum + { + kOp = kIROp_CounterBufferDecoration + }; + IR_LEAF_ISA(CounterBufferDecoration) + IRInst* getCounterBuffer() { return getOperand(0); } +}; + struct IRStageAccessDecoration : public IRDecoration { IR_PARENT_ISA(StageAccessDecoration) diff --git a/source/slang/slang-ir-legalize-types.cpp b/source/slang/slang-ir-legalize-types.cpp index bd5c45ff4..6c5ab1223 100644 --- a/source/slang/slang-ir-legalize-types.cpp +++ b/source/slang/slang-ir-legalize-types.cpp @@ -2518,6 +2518,24 @@ static LegalVal legalizeFunc( return builder.build(irFunc); } +static void cloneDecorationToVar(IRInst* srcInst, IRInst* varInst) +{ + for (auto decoration : srcInst->getDecorations()) + { + switch (decoration->getOp()) + { + case kIROp_FormatDecoration: + case kIROp_UserTypeNameDecoration: + case kIROp_SemanticDecoration: + cloneDecoration(decoration, varInst); + break; + + default: + break; + } + } +} + static LegalVal declareSimpleVar( IRTypeLegalizationContext* context, IROp op, @@ -2601,16 +2619,17 @@ static LegalVal declareSimpleVar( if( leafVar ) { - for( auto decoration : leafVar->getDecorations() ) + cloneDecorationToVar(leafVar, irVar); + if (as<IRStructKey>(leafVar)) { - switch( decoration->getOp() ) + // Find the struct field and clone any decorations on the field over. + for (auto use = leafVar->firstUse; use; use = use->nextUse) { - case kIROp_FormatDecoration: - cloneDecoration(decoration, irVar); - break; - - default: - break; + if (auto field = as<IRStructField>(use->getUser())) + { + cloneDecorationToVar(field, irVar); + break; + } } } } @@ -3330,6 +3349,28 @@ static LegalVal declareVars( tupleVal->elements.add(element); } + if (tupleVal->elements.getCount() == 2 && + tupleVal->elements[0].key && + tupleVal->elements[0].key->findDecorationImpl(kIROp_CounterBufferDecoration)) + { + // If this is a lowered struct from a structured buffer type that has an atomic counter, + // insert decorations to each element var to associate the element buffer with the atomic buffer. + // This decoration is inserted to all lowered structs in the slang-ir-lower-append-consume-structured-buffer + // pass. + // + if (tupleVal->elements[0].val.flavor == LegalVal::Flavor::simple && + tupleVal->elements[1].val.flavor == LegalVal::Flavor::simple) + { + auto simpleElementVar = tupleVal->elements[0].val.getSimple(); + auto simpleCounterVar = tupleVal->elements[1].val.getSimple(); + IRBuilder builder(simpleElementVar); + builder.addDecoration(simpleElementVar, kIROp_CounterBufferDecoration, simpleCounterVar); + // Clone decorations from leafVar to both element and counter var. + cloneDecorationToVar(leafVar, simpleElementVar); + cloneDecorationToVar(leafVar, simpleCounterVar); + } + } + return LegalVal::tuple(tupleVal); } break; diff --git a/source/slang/slang-ir-lower-append-consume-structured-buffer.cpp b/source/slang/slang-ir-lower-append-consume-structured-buffer.cpp index ed04541ef..5a7c7ee1c 100644 --- a/source/slang/slang-ir-lower-append-consume-structured-buffer.cpp +++ b/source/slang/slang-ir-lower-append-consume-structured-buffer.cpp @@ -31,6 +31,8 @@ namespace Slang auto counterBufferKey = builder.createStructKey(); builder.addNameHintDecoration(counterBufferKey, UnownedStringSlice("counter")); + builder.addDecoration(elementBufferKey, kIROp_CounterBufferDecoration, counterBufferKey); + auto elementBufferType = builder.getType(kIROp_HLSLRWStructuredBufferType, elementType); auto counterBufferType = builder.getType(kIROp_HLSLRWStructuredBufferType, builder.getIntType()); diff --git a/source/slang/slang-ir-spirv-legalize.cpp b/source/slang/slang-ir-spirv-legalize.cpp index d5b24dd31..c4b0a471a 100644 --- a/source/slang/slang-ir-spirv-legalize.cpp +++ b/source/slang/slang-ir-spirv-legalize.cpp @@ -496,6 +496,21 @@ struct SPIRVLegalizationContext : public SourceEmitterBase void processGlobalParam(IRGlobalParam* inst) { + if (inst->getDataType()) + { + // Preserve the original type name as a decoration before we do any type lowering. + // This is needed to implement -fspv-reflect, which allows the compiler to output the + // original user-friendly type name of each shader parameter as a SPIRV decoration. + // + StringBuilder sb; + getTypeNameHint(sb, inst->getDataType()); + if (sb.getLength()) + { + IRBuilder builder(inst); + builder.addDecoration(inst, kIROp_UserTypeNameDecoration, builder.getStringValue(sb.produceString().getUnownedSlice())); + } + } + // If the param is a texture, infer its format. if (auto textureType = as<IRTextureTypeBase>(unwrapArray(inst->getDataType()))) { diff --git a/source/slang/slang-ir-util.cpp b/source/slang/slang-ir-util.cpp index 6afb6b719..b4a7b6b99 100644 --- a/source/slang/slang-ir-util.cpp +++ b/source/slang/slang-ir-util.cpp @@ -328,8 +328,196 @@ void getTypeNameHint(StringBuilder& sb, IRInst* type) sb << "string"; break; case kIROp_ArrayType: - sb << "array_"; + sb << "array<"; getTypeNameHint(sb, type->getOperand(0)); + sb << ","; + getTypeNameHint(sb, as<IRArrayType>(type)->getElementCount()); + sb << ">"; + break; + case kIROp_UnsizedArrayType: + sb << "runtime_array<"; + getTypeNameHint(sb, type->getOperand(0)); + sb << ">"; + break; + case kIROp_TextureType: + case kIROp_GLSLImageType: + { + auto textureType = as<IRResourceTypeBase>(type); + switch (textureType->getAccess()) + { + case SLANG_RESOURCE_ACCESS_APPEND: + sb << "Append"; + break; + case SLANG_RESOURCE_ACCESS_CONSUME: + sb << "Consume"; + break; + case SLANG_RESOURCE_ACCESS_RASTER_ORDERED: + sb << "RasterizerOrdered"; + break; + case SLANG_RESOURCE_ACCESS_WRITE: + sb << "RW"; + break; + case SLANG_RESOURCE_ACCESS_FEEDBACK: + sb << "Feedback"; + break; + case SLANG_RESOURCE_ACCESS_READ: + break; + } + if (textureType->isCombined()) + { + switch (textureType->GetBaseShape()) + { + case SLANG_TEXTURE_1D: + sb << "Sampler1D"; + break; + case SLANG_TEXTURE_2D: + sb << "Sampler2D"; + break; + case SLANG_TEXTURE_3D: + sb << "Sampler3D"; + break; + case SLANG_TEXTURE_CUBE: + sb << "SamplerCube"; + break; + case SLANG_TEXTURE_BUFFER: + sb << "SamplerBuffer"; + break; + } + } + else + { + switch (textureType->GetBaseShape()) + { + case SLANG_TEXTURE_1D: + sb << "Texture1D"; + break; + case SLANG_TEXTURE_2D: + sb << "Texture2D"; + break; + case SLANG_TEXTURE_3D: + sb << "Texture3D"; + break; + case SLANG_TEXTURE_CUBE: + sb << "TextureCube"; + break; + case SLANG_TEXTURE_BUFFER: + sb << "Buffer"; + break; + } + } + if (textureType->isMultisample()) + { + sb << "MS"; + } + if (textureType->isArray()) + { + sb << "Array"; + } + if (textureType->isShadow()) + { + sb << "Shadow"; + } + } + break; + case kIROp_ParameterBlockType: + sb << "ParameterBlock<"; + getTypeNameHint(sb, as<IRParameterBlockType>(type)->getElementType()); + sb << ">"; + break; + case kIROp_ConstantBufferType: + sb << "cbuffer<"; + getTypeNameHint(sb, as<IRConstantBufferType>(type)->getElementType()); + sb << ">"; + break; + case kIROp_TextureBufferType: + sb << "tbuffer<"; + getTypeNameHint(sb, as<IRTextureBufferType>(type)->getElementType()); + sb << ">"; + break; + case kIROp_GLSLShaderStorageBufferType: + sb << "StorageBuffer<"; + getTypeNameHint(sb, as<IRGLSLShaderStorageBufferType>(type)->getElementType()); + sb << ">"; + break; + case kIROp_HLSLByteAddressBufferType: + sb << "ByteAddressBuffer"; + break; + case kIROp_HLSLRWByteAddressBufferType: + sb << "RWByteAddressBuffer"; + break; + case kIROp_HLSLRasterizerOrderedByteAddressBufferType: + sb << "RasterizerOrderedByteAddressBuffer"; + break; + case kIROp_RaytracingAccelerationStructureType: + sb << "RayTracingAccelerationStructure"; + break; + case kIROp_HitObjectType: + sb << "HitObject"; + break; + case kIROp_HLSLConstBufferPointerType: + sb << "ConstantBufferPointer<"; + getTypeNameHint(sb, as<IRHLSLConstBufferPointerType>(type)->getValueType()); + sb << ">"; + break; + case kIROp_HLSLStructuredBufferType: + sb << "StructuredBuffer<"; + getTypeNameHint(sb, as<IRHLSLStructuredBufferTypeBase>(type)->getElementType()); + sb << ">"; + break; + case kIROp_HLSLRWStructuredBufferType: + sb << "RWStructuredBuffer<"; + getTypeNameHint(sb, as<IRHLSLStructuredBufferTypeBase>(type)->getElementType()); + sb << ">"; + break; + case kIROp_HLSLAppendStructuredBufferType: + sb << "AppendStructuredBuffer<"; + getTypeNameHint(sb, as<IRHLSLStructuredBufferTypeBase>(type)->getElementType()); + sb << ">"; + break; + case kIROp_HLSLConsumeStructuredBufferType: + sb << "ConsumeStructuredBuffer<"; + getTypeNameHint(sb, as<IRHLSLStructuredBufferTypeBase>(type)->getElementType()); + sb << ">"; + break; + case kIROp_HLSLRasterizerOrderedStructuredBufferType: + sb << "RasterizerOrderedStructuredBuffer<"; + getTypeNameHint(sb, as<IRHLSLStructuredBufferTypeBase>(type)->getElementType()); + sb << ">"; + break; + case kIROp_SamplerStateType: + sb << "SamplerState"; + break; + case kIROp_SamplerComparisonStateType: + sb << "SamplerComparisonState"; + break; + case kIROp_TextureFootprintType: + sb << "TextureFootprint"; + break; + case kIROp_Specialize: + { + auto specialize = as<IRSpecialize>(type); + getTypeNameHint(sb, specialize->getBase()); + sb << "<"; + bool isFirst = true; + for (UInt i = 0; i < specialize->getArgCount(); i++) + { + auto arg = specialize->getArg(i); + if (!arg->getDataType()) + continue; + if (arg->getDataType()->getOp() == kIROp_WitnessTableType) + continue; + if (!isFirst) sb << ","; + getTypeNameHint(sb, arg); + isFirst = false; + } + sb << ">"; + } + break; + case kIROp_AttributedType: + getTypeNameHint(sb, as<IRAttributedType>(type)->getBaseType()); + break; + case kIROp_RateQualifiedType: + getTypeNameHint(sb, as<IRRateQualifiedType>(type)->getValueType()); break; case kIROp_VectorType: getTypeNameHint(sb, type->getOperand(0)); diff --git a/source/slang/slang-options.cpp b/source/slang/slang-options.cpp index 7c442a9d2..c9527bf5e 100644 --- a/source/slang/slang-options.cpp +++ b/source/slang/slang-options.cpp @@ -95,6 +95,7 @@ enum class OptionKind VulkanInvertY, VulkanUseEntryPointName, VulkanUseGLLayout, + VulkanEmitReflection, GLSLForceScalarLayout, EnableEffectAnnotations, @@ -512,6 +513,7 @@ void initCommandOptions(CommandOptions& options) { OptionKind::VulkanInvertY, "-fvk-invert-y", nullptr, "Negates (additively inverts) SV_Position.y before writing to stage output."}, { OptionKind::VulkanUseEntryPointName, "-fvk-use-entrypoint-name", nullptr, "Uses the entrypoint name from the source instead of 'main' in the spirv output."}, { OptionKind::VulkanUseGLLayout, "-fvk-use-gl-layout", nullptr, "Use std430 layout instead of D3D buffer layout for raw buffer load/stores."}, + { OptionKind::VulkanEmitReflection, "-fspv-reflect", nullptr, "Include reflection decorations in the resulting SPIRV for shader parameters."}, { OptionKind::EnableEffectAnnotations, "-enable-effect-annotations", nullptr, "Enables support for legacy effect annotation syntax."}, @@ -2100,6 +2102,12 @@ SlangResult OptionsParser::_parse( m_hlslToVulkanLayoutOptions->setUseGLLayout(true); break; } + case OptionKind::VulkanEmitReflection: + { + // -fvk-invert-y + m_hlslToVulkanLayoutOptions->setEmitSPIRVReflectionInfo(true); + break; + } case OptionKind::Profile: SLANG_RETURN_ON_FAIL(_parseProfile(arg)); break; case OptionKind::Capability: { |
