diff options
| author | Alexey Panteleev <alpanteleev@nvidia.com> | 2022-05-18 10:57:37 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-05-18 10:57:37 -0700 |
| commit | 69cb6e8f300d77e74bd2c7dfe15d12e10b38f512 (patch) | |
| tree | c2d23f2883acb28407106a096b55c64111f098b1 | |
| parent | 1148564b9cdbbc8fec4fbecf65b0af60aa6af344 (diff) | |
Support for querying which parameters are used in emitted code (#2239)
See https://github.com/shader-slang/slang/issues/2213
44 files changed, 843 insertions, 91 deletions
diff --git a/build/visual-studio/slang/slang.vcxproj b/build/visual-studio/slang/slang.vcxproj index 49ed81173..267b25098 100644 --- a/build/visual-studio/slang/slang.vcxproj +++ b/build/visual-studio/slang/slang.vcxproj @@ -335,6 +335,7 @@ IF EXIST ..\..\..\external\slang-binaries\bin\windows-aarch64\slang-glslang.dll\ <ClInclude Include="..\..\..\source\slang\slang-emit-hlsl.h" />
<ClInclude Include="..\..\..\source\slang\slang-emit-precedence.h" />
<ClInclude Include="..\..\..\source\slang\slang-emit-source-writer.h" />
+ <ClInclude Include="..\..\..\source\slang\slang-emit.h" />
<ClInclude Include="..\..\..\source\slang\slang-glsl-extension-tracker.h" />
<ClInclude Include="..\..\..\source\slang\slang-hlsl-intrinsic-set.h" />
<ClInclude Include="..\..\..\source\slang\slang-image-format-defs.h" />
@@ -376,6 +377,7 @@ IF EXIST ..\..\..\external\slang-binaries\bin\windows-aarch64\slang-glslang.dll\ <ClInclude Include="..\..\..\source\slang\slang-ir-lower-generics.h" />
<ClInclude Include="..\..\..\source\slang\slang-ir-lower-reinterpret.h" />
<ClInclude Include="..\..\..\source\slang\slang-ir-lower-tuple-types.h" />
+ <ClInclude Include="..\..\..\source\slang\slang-ir-metadata.h" />
<ClInclude Include="..\..\..\source\slang\slang-ir-missing-return.h" />
<ClInclude Include="..\..\..\source\slang\slang-ir-optix-entry-point-uniforms.h" />
<ClInclude Include="..\..\..\source\slang\slang-ir-restructure-scoping.h" />
@@ -517,6 +519,7 @@ IF EXIST ..\..\..\external\slang-binaries\bin\windows-aarch64\slang-glslang.dll\ <ClCompile Include="..\..\..\source\slang\slang-ir-lower-generics.cpp" />
<ClCompile Include="..\..\..\source\slang\slang-ir-lower-reinterpret.cpp" />
<ClCompile Include="..\..\..\source\slang\slang-ir-lower-tuple-types.cpp" />
+ <ClCompile Include="..\..\..\source\slang\slang-ir-metadata.cpp" />
<ClCompile Include="..\..\..\source\slang\slang-ir-missing-return.cpp" />
<ClCompile Include="..\..\..\source\slang\slang-ir-optix-entry-point-uniforms.cpp" />
<ClCompile Include="..\..\..\source\slang\slang-ir-restructure-scoping.cpp" />
diff --git a/build/visual-studio/slang/slang.vcxproj.filters b/build/visual-studio/slang/slang.vcxproj.filters index d6cebe52a..18f5495a7 100644 --- a/build/visual-studio/slang/slang.vcxproj.filters +++ b/build/visual-studio/slang/slang.vcxproj.filters @@ -102,6 +102,9 @@ <ClInclude Include="..\..\..\source\slang\slang-emit-source-writer.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="..\..\..\source\slang\slang-emit.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="..\..\..\source\slang\slang-glsl-extension-tracker.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -225,6 +228,9 @@ <ClInclude Include="..\..\..\source\slang\slang-ir-lower-tuple-types.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="..\..\..\source\slang\slang-ir-metadata.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="..\..\..\source\slang\slang-ir-missing-return.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -644,6 +650,9 @@ <ClCompile Include="..\..\..\source\slang\slang-ir-lower-tuple-types.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\source\slang\slang-ir-metadata.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\source\slang\slang-ir-missing-return.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@@ -1355,6 +1355,10 @@ extern "C" SlangCompileRequest* request, SlangCompileFlags flags); + /*! @see slang::ICompileRequest::getCompileFlags */ + SLANG_API SlangCompileFlags spGetCompileFlags( + SlangCompileRequest* request); + /*! @see slang::ICompileRequest::setDumpIntermediates */ SLANG_API void spSetDumpIntermediates( SlangCompileRequest* request, @@ -2147,6 +2151,15 @@ extern "C" SLANG_API unsigned spReflectionParameter_GetBindingIndex(SlangReflectionParameter* parameter); SLANG_API unsigned spReflectionParameter_GetBindingSpace(SlangReflectionParameter* parameter); + SLANG_API SlangResult spIsParameterLocationUsed( + SlangCompileRequest* request, + SlangInt entryPointIndex, + SlangInt targetIndex, + SlangParameterCategory category, // is this a `t` register? `s` register? + SlangUInt spaceIndex, // `space` for D3D12, `set` for Vulkan + SlangUInt registerIndex, // `register` for D3D12, `binding` for Vulkan + bool& outUsed); + // Entry Point Reflection SLANG_API char const* spReflectionEntryPoint_getName( @@ -3371,6 +3384,11 @@ namespace slang SlangCompileFlags flags) = 0; /*! + @brief Returns the compilation flags previously set with `setCompileFlags` + */ + virtual SLANG_NO_THROW SlangCompileFlags SLANG_MCALL getCompileFlags() = 0; + + /*! @brief Set whether to dump intermediate results (for debugging) or not. */ virtual SLANG_NO_THROW void SLANG_MCALL setDumpIntermediates( @@ -3892,6 +3910,14 @@ namespace slang virtual SLANG_NO_THROW SlangResult SLANG_MCALL getProgramWithEntryPoints( slang::IComponentType** outProgram) = 0; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL isParameterLocationUsed( + SlangInt entryPointIndex, + SlangInt targetIndex, + SlangParameterCategory category, + SlangUInt spaceIndex, + SlangUInt registerIndex, + bool& outUsed) = 0; + /** Set the line directive mode for a target. */ virtual SLANG_NO_THROW void SLANG_MCALL setTargetLineDirectiveMode( diff --git a/source/slang/slang-api.cpp b/source/slang/slang-api.cpp index 911bd0f0f..50986660b 100644 --- a/source/slang/slang-api.cpp +++ b/source/slang/slang-api.cpp @@ -245,6 +245,13 @@ SLANG_API void spSetCompileFlags( request->setCompileFlags(flags); } +SLANG_API SlangCompileFlags spGetCompileFlags( + slang::ICompileRequest* request) +{ + SLANG_ASSERT(request); + return request->getCompileFlags(); +} + SLANG_API void spSetDumpIntermediates( slang::ICompileRequest* request, int enable) diff --git a/source/slang/slang-compiler.cpp b/source/slang/slang-compiler.cpp index 7988af3e8..74162d2ef 100644 --- a/source/slang/slang-compiler.cpp +++ b/source/slang/slang-compiler.cpp @@ -143,6 +143,28 @@ void printDiagnosticArg(StringBuilder& sb, CodeGenTarget val) return SLANG_OK; } + SlangResult CompileResult::isParameterLocationUsed(SlangParameterCategory category, UInt spaceIndex, UInt registerIndex, bool& outUsed) + { + if (!postEmitMetadata) + return SLANG_E_NOT_AVAILABLE; + + if (!ShaderBindingRange::isUsageTracked((slang::ParameterCategory)category)) + return SLANG_E_NOT_AVAILABLE; + + // TODO: optimize this with a binary search through a sorted list + for (const auto& range : postEmitMetadata->usedBindings) + { + if (range.containsBinding((slang::ParameterCategory)category, spaceIndex, registerIndex)) + { + outUsed = true; + return SLANG_OK; + } + } + + outUsed = false; + return SLANG_OK; + } + // // FrontEndEntryPointRequest // @@ -662,7 +684,8 @@ void printDiagnosticArg(StringBuilder& sb, CodeGenTarget val) } SlangResult CodeGenContext::emitEntryPointsSource( - String& outSource) + String& outSource, + RefPtr<PostEmitMetadata>& outMetadata) { outSource = String(); @@ -714,7 +737,7 @@ void printDiagnosticArg(StringBuilder& sb, CodeGenTarget val) else { return emitEntryPointsSourceFromIR( - outSource); + outSource, outMetadata); } } @@ -985,7 +1008,8 @@ void printDiagnosticArg(StringBuilder& sb, CodeGenTarget val) } SlangResult CodeGenContext::emitWithDownstreamForEntryPoints( - RefPtr<DownstreamCompileResult>& outResult) + RefPtr<DownstreamCompileResult>& outResult, + RefPtr<PostEmitMetadata>& outMetadata) { outResult.setNull(); @@ -1127,7 +1151,7 @@ void printDiagnosticArg(StringBuilder& sb, CodeGenTarget val) options.sourceContentsPath = calcSourcePathForEntryPoints(); CodeGenContext sourceCodeGenContext(this, sourceTarget, extensionTracker); - SLANG_RETURN_ON_FAIL(sourceCodeGenContext.emitEntryPointsSource(options.sourceContents)); + SLANG_RETURN_ON_FAIL(sourceCodeGenContext.emitEntryPointsSource(options.sourceContents, outMetadata)); } else { @@ -1144,7 +1168,7 @@ void printDiagnosticArg(StringBuilder& sb, CodeGenTarget val) else { CodeGenContext sourceCodeGenContext(this, sourceTarget, extensionTracker); - SLANG_RETURN_ON_FAIL(sourceCodeGenContext.emitEntryPointsSource(options.sourceContents)); + SLANG_RETURN_ON_FAIL(sourceCodeGenContext.emitEntryPointsSource(options.sourceContents, outMetadata)); sourceCodeGenContext.maybeDumpIntermediate(options.sourceContents.getBuffer()); sourceLanguage = (SourceLanguage)TypeConvertUtil::getSourceLanguageFromTarget((SlangCompileTarget)sourceTarget); @@ -1504,7 +1528,8 @@ void printDiagnosticArg(StringBuilder& sb, CodeGenTarget val) SlangResult emitSPIRVForEntryPointsDirectly( CodeGenContext* codeGenContext, - List<uint8_t>& spirvOut); + List<uint8_t>& spirvOut, + RefPtr<PostEmitMetadata>& outMetadata); static CodeGenTarget _getIntermediateTarget(CodeGenTarget target) { @@ -1519,7 +1544,8 @@ void printDiagnosticArg(StringBuilder& sb, CodeGenTarget val) /// Function to simplify the logic around emitting, and dissassembling SlangResult CodeGenContext::_emitEntryPoints( - RefPtr<DownstreamCompileResult>& outDownstreamResult) + RefPtr<DownstreamCompileResult>& outDownstreamResult, + RefPtr<PostEmitMetadata>& outMetadata) { auto target = getTargetFormat(); switch (target) @@ -1533,7 +1559,7 @@ void printDiagnosticArg(StringBuilder& sb, CodeGenTarget val) CodeGenContext intermediateContext(this, intermediateTarget); RefPtr<DownstreamCompileResult> code; - SLANG_RETURN_ON_FAIL(intermediateContext._emitEntryPoints(code)); + SLANG_RETURN_ON_FAIL(intermediateContext._emitEntryPoints(code, outMetadata)); intermediateContext.maybeDumpIntermediate(code); // Then disassemble the intermediate binary result to get the desired output @@ -1548,7 +1574,7 @@ void printDiagnosticArg(StringBuilder& sb, CodeGenTarget val) if (getTargetReq()->shouldEmitSPIRVDirectly()) { List<uint8_t> spirv; - SLANG_RETURN_ON_FAIL(emitSPIRVForEntryPointsDirectly(this, spirv)); + SLANG_RETURN_ON_FAIL(emitSPIRVForEntryPointsDirectly(this, spirv, outMetadata)); auto spirvBlob = ListBlob::moveCreate(spirv); outDownstreamResult = new BlobDownstreamCompileResult(DownstreamDiagnostics(), spirvBlob); return SLANG_OK; @@ -1560,7 +1586,7 @@ void printDiagnosticArg(StringBuilder& sb, CodeGenTarget val) case CodeGenTarget::ShaderHostCallable: case CodeGenTarget::ShaderSharedLibrary: case CodeGenTarget::HostExecutable: - SLANG_RETURN_ON_FAIL(emitWithDownstreamForEntryPoints(outDownstreamResult)); + SLANG_RETURN_ON_FAIL(emitWithDownstreamForEntryPoints(outDownstreamResult, outMetadata)); return SLANG_OK; default: break; @@ -1590,11 +1616,12 @@ void printDiagnosticArg(StringBuilder& sb, CodeGenTarget val) case CodeGenTarget::HostExecutable: { RefPtr<DownstreamCompileResult> downstreamResult; + RefPtr<PostEmitMetadata> metadata; - if (SLANG_SUCCEEDED(_emitEntryPoints(downstreamResult))) + if (SLANG_SUCCEEDED(_emitEntryPoints(downstreamResult, metadata))) { maybeDumpIntermediate(downstreamResult); - result = CompileResult(downstreamResult); + result = CompileResult(downstreamResult, metadata); } } break; @@ -1606,17 +1633,18 @@ void printDiagnosticArg(StringBuilder& sb, CodeGenTarget val) case CodeGenTarget::CSource: { RefPtr<ExtensionTracker> extensionTracker = _newExtensionTracker(target); + RefPtr<PostEmitMetadata> metadata; CodeGenContext subContext(this, target, extensionTracker); String code; - if (SLANG_FAILED(subContext.emitEntryPointsSource(code))) + if (SLANG_FAILED(subContext.emitEntryPointsSource(code, metadata))) { return result; } subContext.maybeDumpIntermediate(code.getBuffer()); - result = CompileResult(code); + result = CompileResult(code, metadata); } break; diff --git a/source/slang/slang-compiler.h b/source/slang/slang-compiler.h index 39ee830d2..e0152566c 100755 --- a/source/slang/slang-compiler.h +++ b/source/slang/slang-compiler.h @@ -141,26 +141,117 @@ namespace Slang class Module; class TranslationUnitRequest; + struct ShaderBindingRange + { + slang::ParameterCategory category = slang::ParameterCategory::None; + UInt spaceIndex = 0; + UInt registerIndex = 0; + UInt registerCount = 0; // 0 for unsized + + bool isInfinite() const + { + return registerCount == 0; + } + + bool containsBinding(slang::ParameterCategory _category, UInt _spaceIndex, UInt _registerIndex) const + { + return category == _category + && spaceIndex == _spaceIndex + && registerIndex <= _registerIndex + && (isInfinite() || registerCount + registerIndex > _registerIndex); + } + + bool intersectsWith(const ShaderBindingRange& other) const + { + if (category != other.category || spaceIndex != other.spaceIndex) + return false; + + const bool leftIntersection = (registerIndex < other.registerIndex + other.registerCount) || other.isInfinite(); + const bool rightIntersection = (other.registerIndex < registerIndex + registerCount) || isInfinite(); + + return leftIntersection && rightIntersection; + } + + bool adjacentTo(const ShaderBindingRange& other) const + { + if (category != other.category || spaceIndex != other.spaceIndex) + return false; + + const bool leftIntersection = (registerIndex <= other.registerIndex + other.registerCount) || other.isInfinite(); + const bool rightIntersection = (other.registerIndex <= registerIndex + registerCount) || isInfinite(); + + return leftIntersection && rightIntersection; + } + + void mergeWith(const ShaderBindingRange other) + { + UInt newRegisterIndex = Math::Min(registerIndex, other.registerIndex); + + if (other.isInfinite()) + registerCount = 0; + else if (!isInfinite()) + registerCount = Math::Max(registerIndex + registerCount, other.registerIndex + other.registerCount) - newRegisterIndex; + + registerIndex = newRegisterIndex; + } + + static bool isUsageTracked(slang::ParameterCategory category) + { + switch(category) + { + case slang::ConstantBuffer: + case slang::ShaderResource: + case slang::UnorderedAccess: + case slang::SamplerState: + return true; + default: + return false; + } + } + }; + + struct PostEmitMetadata : public RefObject + { + List<ShaderBindingRange> usedBindings; + }; + // Result of compiling an entry point. // Should only ever be string, binary or shared library class CompileResult { public: CompileResult() = default; - explicit CompileResult(String const& str) : format(ResultFormat::Text), outputString(str) {} - explicit CompileResult(ISlangBlob* inBlob) : format(ResultFormat::Binary), blob(inBlob) {} - explicit CompileResult(DownstreamCompileResult* inDownstreamResult): format(ResultFormat::Binary), downstreamResult(inDownstreamResult) {} - explicit CompileResult(const UnownedStringSlice& slice ) : format(ResultFormat::Text), outputString(slice) {} + explicit CompileResult(String const& str, RefPtr<PostEmitMetadata> metadata) + : format(ResultFormat::Text) + , outputString(str) + , postEmitMetadata(metadata) {} + + explicit CompileResult(ISlangBlob* inBlob) + : format(ResultFormat::Binary) + , blob(inBlob) {} + + explicit CompileResult(DownstreamCompileResult* inDownstreamResult, RefPtr<PostEmitMetadata> metadata) + : format(ResultFormat::Binary) + , downstreamResult(inDownstreamResult) + , postEmitMetadata(metadata) {} + + explicit CompileResult(const UnownedStringSlice& slice ) + : format(ResultFormat::Text) + , outputString(slice) {} SlangResult getBlob(ComPtr<ISlangBlob>& outBlob) const; SlangResult getSharedLibrary(ComPtr<ISlangSharedLibrary>& outSharedLibrary); + SlangResult isParameterLocationUsed(SlangParameterCategory category, UInt spaceIndex, UInt registerIndex, bool& outUsed); + ResultFormat format = ResultFormat::None; String outputString; ///< Only set if result type is ResultFormat::Text mutable ComPtr<ISlangBlob> blob; RefPtr<DownstreamCompileResult> downstreamResult; + + RefPtr<PostEmitMetadata> postEmitMetadata; }; /// Information collected about global or entry-point shader parameters @@ -939,7 +1030,7 @@ namespace Slang Index getShaderParamCount() SLANG_OVERRIDE { return 0; } ShaderParamInfo getShaderParam(Index index) SLANG_OVERRIDE { SLANG_UNUSED(index); return ShaderParamInfo(); } - + class EntryPointSpecializationInfo : public SpecializationInfo { public: @@ -2428,13 +2519,16 @@ namespace Slang /* Emits entry point source taking into account if a pass-through or not. Uses 'targetFormat' to determine the target (not targetReq) */ SlangResult emitEntryPointsSource( - String& outSource); + String& outSource, + RefPtr<PostEmitMetadata>& outMetadata); SlangResult emitEntryPointsSourceFromIR( - String& outSource); + String& outSource, + RefPtr<PostEmitMetadata>& outMetadata); SlangResult emitWithDownstreamForEntryPoints( - RefPtr<DownstreamCompileResult>& outResult); + RefPtr<DownstreamCompileResult>& outResult, + RefPtr<PostEmitMetadata>& outMetadata); /* Determines a suitable filename to identify the input for a given entry point being compiled. If the end-to-end compile is a pass-through case, will attempt to find the (unique) source file @@ -2450,7 +2544,8 @@ namespace Slang SlangResult _emitEntryPoints( - RefPtr<DownstreamCompileResult>& outDownstreamResult); + RefPtr<DownstreamCompileResult>& outDownstreamResult, + RefPtr<PostEmitMetadata>& outMetadata); private: Shared* m_shared = nullptr; @@ -2477,6 +2572,7 @@ namespace Slang // slang::ICompileRequest virtual SLANG_NO_THROW void SLANG_MCALL setFileSystem(ISlangFileSystem* fileSystem) SLANG_OVERRIDE; virtual SLANG_NO_THROW void SLANG_MCALL setCompileFlags(SlangCompileFlags flags) SLANG_OVERRIDE; + virtual SLANG_NO_THROW SlangCompileFlags SLANG_MCALL getCompileFlags() SLANG_OVERRIDE; virtual SLANG_NO_THROW void SLANG_MCALL setDumpIntermediates(int enable) SLANG_OVERRIDE; virtual SLANG_NO_THROW void SLANG_MCALL setDumpIntermediatePrefix(const char* prefix) SLANG_OVERRIDE; virtual SLANG_NO_THROW void SLANG_MCALL setLineDirectiveMode(SlangLineDirectiveMode mode) SLANG_OVERRIDE; @@ -2536,6 +2632,7 @@ namespace Slang virtual SLANG_NO_THROW void SLANG_MCALL setCommandLineCompilerMode() SLANG_OVERRIDE; virtual SLANG_NO_THROW SlangResult SLANG_MCALL addTargetCapability(SlangInt targetIndex, SlangCapabilityID capability) SLANG_OVERRIDE; virtual SLANG_NO_THROW SlangResult SLANG_MCALL getProgramWithEntryPoints(slang::IComponentType** outProgram) SLANG_OVERRIDE; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL isParameterLocationUsed(SlangInt entryPointIndex, SlangInt targetIndex, SlangParameterCategory category, SlangUInt spaceIndex, SlangUInt registerIndex, bool& outUsed) SLANG_OVERRIDE; virtual SLANG_NO_THROW void SLANG_MCALL setTargetLineDirectiveMode( SlangInt targetIndex, SlangLineDirectiveMode mode) SLANG_OVERRIDE; diff --git a/source/slang/slang-emit.cpp b/source/slang/slang-emit.cpp index d231acf14..a128644d3 100644 --- a/source/slang/slang-emit.cpp +++ b/source/slang/slang-emit.cpp @@ -25,6 +25,7 @@ #include "slang-ir-lower-tuple-types.h" #include "slang-ir-lower-bit-cast.h" #include "slang-ir-lower-reinterpret.h" +#include "slang-ir-metadata.h" #include "slang-ir-optix-entry-point-uniforms.h" #include "slang-ir-restructure.h" #include "slang-ir-restructure-scoping.h" @@ -818,11 +819,15 @@ Result linkAndOptimizeIR( #endif validateIRModuleIfEnabled(codeGenContext, irModule); + outLinkedIR.metadata = new PostEmitMetadata(); + collectMetadata(irModule, *outLinkedIR.metadata); + return SLANG_OK; } SlangResult CodeGenContext::emitEntryPointsSourceFromIR( - String& outSource) + String& outSource, + RefPtr<PostEmitMetadata>& outMetadata) { outSource = String(); @@ -927,6 +932,8 @@ SlangResult CodeGenContext::emitEntryPointsSourceFromIR( auto irModule = linkedIR.module; + outMetadata = linkedIR.metadata; + // After all of the required optimization and legalization // passes have been performed, we can emit target code from // the IR module. @@ -1007,7 +1014,8 @@ SlangResult emitSPIRVFromIR( SlangResult emitSPIRVForEntryPointsDirectly( CodeGenContext* codeGenContext, - List<uint8_t>& spirvOut) + List<uint8_t>& spirvOut, + RefPtr<PostEmitMetadata>& outMetadata) { // Outside because we want to keep IR in scope whilst we are processing emits LinkedIR linkedIR; @@ -1022,6 +1030,8 @@ SlangResult emitSPIRVForEntryPointsDirectly( emitSPIRVFromIR(codeGenContext, irModule, irEntryPoints, spirvOut); + outMetadata = linkedIR.metadata; + return SLANG_OK; } diff --git a/source/slang/slang-ir-collect-global-uniforms.cpp b/source/slang/slang-ir-collect-global-uniforms.cpp index 88ebe9940..87b21c819 100644 --- a/source/slang/slang-ir-collect-global-uniforms.cpp +++ b/source/slang/slang-ir-collect-global-uniforms.cpp @@ -172,19 +172,6 @@ struct CollectGlobalUniformParametersContext auto globalParamLayout = fieldLayoutAttr->getLayout(); - // If the given parameter doesn't contribute to uniform/ordinary usage, then - // we can safely leave it at the global scope and potentially avoid a lot - // of complications that might otherwise arise (that is, we don't need to worry - // about downstream passes that might have worked for a simple global parameter, - // but that would not work for one nested inside a structure. - // - // TODO: It would be more consistent and robust to *always* wrap up - // these global parameters appropriately, and ensure that all the downstream - // passes can handle that case, since they would need to do so in general. - // - if(!globalParamLayout->getTypeLayout()->findSizeAttr(LayoutResourceKind::Uniform) ) - continue; - // Once we have decided to do replacement, we need to // set ourselves up to emit the replacement code. // @@ -195,11 +182,6 @@ struct CollectGlobalUniformParametersContext // auto fieldKey = builder->createStructKey(); - // The new structure field will need to have whatever decorations - // had been put on the global parameter (notably including any name hint) - // - globalParam->transferDecorationsTo(fieldKey); - // In order to make sure that the existing IR layout information for // the global scope remains valid, we will swap out the key in the // per-field layout information to reference the key we created @@ -207,6 +189,24 @@ struct CollectGlobalUniformParametersContext // fieldLayoutAttr->setOperand(0, fieldKey); + // If the given parameter doesn't contribute to uniform/ordinary usage, then + // we can safely leave it at the global scope and potentially avoid a lot + // of complications that might otherwise arise (that is, we don't need to worry + // about downstream passes that might have worked for a simple global parameter, + // but that would not work for one nested inside a structure. + // + // TODO: It would be more consistent and robust to *always* wrap up + // these global parameters appropriately, and ensure that all the downstream + // passes can handle that case, since they would need to do so in general. + // + if (!globalParamLayout->getTypeLayout()->findSizeAttr(LayoutResourceKind::Uniform)) + continue; + + // The new structure field will need to have whatever decorations + // had been put on the global parameter (notably including any name hint) + // + globalParam->transferDecorationsTo(fieldKey); + // Now we can add a field to the `GlobalParams` type that // will stand in for the parameter: it will have the key we // just generated, and the type of the original parameter. diff --git a/source/slang/slang-ir-link.h b/source/slang/slang-ir-link.h index 6c81ae734..798013a8d 100644 --- a/source/slang/slang-ir-link.h +++ b/source/slang/slang-ir-link.h @@ -12,6 +12,7 @@ namespace Slang RefPtr<IRModule> module; IRVarLayout* globalScopeVarLayout; List<IRFunc*> entryPoints; + RefPtr<PostEmitMetadata> metadata; }; diff --git a/source/slang/slang-ir-metadata.cpp b/source/slang/slang-ir-metadata.cpp new file mode 100644 index 000000000..cc5922e93 --- /dev/null +++ b/source/slang/slang-ir-metadata.cpp @@ -0,0 +1,77 @@ +// slang-ir-metadata.cpp +#include "slang-ir-metadata.h" + +#include "slang-ir.h" +#include "slang-ir-insts.h" + +namespace Slang +{ + +// This file currently implements a pass that collects information about the shader parameters that +// are referenced in the IR. It's named 'metadata' in order to support other potential code +// analysis scenarios in the future. + + +// Inserts a single resource binding (which takes `count` slots, where 0 means unbounded) into the list of resource ranges. +static void _insertBinding(List<ShaderBindingRange>& ranges, LayoutResourceKind kind, UInt spaceIndex, UInt registerIndex, UInt count) +{ + // Construct a new range from the provided resource. + ShaderBindingRange newRange; + newRange.category = kind; + newRange.spaceIndex = spaceIndex; + newRange.registerIndex = registerIndex; + newRange.registerCount = count; + + // See if the new range is adjacent to any of the existing ranges, merge with that. + for (auto& range : ranges) + { + if (range.adjacentTo(newRange)) + { + range.mergeWith(newRange); + return; + } + } + + // No adjacent ranges found - create a new one. + ranges.add(newRange); +} + +// Collects the metadata from the provided IR module, saves it in outMetadata. +void collectMetadata(const IRModule* irModule, PostEmitMetadata& outMetadata) +{ + // Scan the instructions looking for global resource declarations + for (const auto& inst : irModule->getGlobalInsts()) + { + auto param = as<IRGlobalParam>(inst); + if (!param) continue; + + auto layoutDecoration = param->findDecoration<IRLayoutDecoration>(); + if (!layoutDecoration) continue; + + auto varLayout = as<IRVarLayout>(layoutDecoration->getLayout()); + if (!varLayout) continue; + + for(auto sizeAttr : varLayout->getTypeLayout()->getSizeAttrs()) + { + auto kind = sizeAttr->getResourceKind(); + + // Only track resource types that we can reliably track, such as textures. + // Do not track individual uniforms, for example. + if (!ShaderBindingRange::isUsageTracked(kind)) + continue; + + if (auto offsetAttr = varLayout->findOffsetAttr(kind)) + { + // Get the binding information from this attribute and insert it into the list + auto spaceIndex = offsetAttr->getSpace(); + auto registerIndex = offsetAttr->getOffset(); + auto size = sizeAttr->getSize(); + auto count = size.isFinite() ? size.getFiniteValue() : 0; + _insertBinding(outMetadata.usedBindings, kind, spaceIndex, registerIndex, count); + } + } + } +} + + +} diff --git a/source/slang/slang-ir-metadata.h b/source/slang/slang-ir-metadata.h new file mode 100644 index 000000000..b7fcd1e2b --- /dev/null +++ b/source/slang/slang-ir-metadata.h @@ -0,0 +1,12 @@ +// slang-ir-metadata.h +#pragma once + +namespace Slang +{ + +struct PostEmitMetadata; +struct IRModule; + +void collectMetadata(const IRModule* irModule, PostEmitMetadata& outMetadata); + +} diff --git a/source/slang/slang-reflection-api.cpp b/source/slang/slang-reflection-api.cpp index 583e000a6..96b1f9004 100644 --- a/source/slang/slang-reflection-api.cpp +++ b/source/slang/slang-reflection-api.cpp @@ -2580,6 +2580,20 @@ SLANG_API unsigned spReflectionParameter_GetBindingSpace(SlangReflectionParamete spReflectionVariableLayout_GetTypeLayout(varLayout))); } +SLANG_API SlangResult spIsParameterLocationUsed( + SlangCompileRequest* request, + SlangInt entryPointIndex, + SlangInt targetIndex, + SlangParameterCategory category, + SlangUInt spaceIndex, + SlangUInt registerIndex, + bool& outUsed) +{ + if (!request) + return SLANG_E_INVALID_ARG; + + return request->isParameterLocationUsed(entryPointIndex, targetIndex, category, spaceIndex, registerIndex, outUsed); +} // Entry Point Reflection diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp index 7ce3594e5..7b22d04de 100644 --- a/source/slang/slang.cpp +++ b/source/slang/slang.cpp @@ -4147,6 +4147,11 @@ void EndToEndCompileRequest::setCompileFlags(SlangCompileFlags flags) getFrontEndReq()->compileFlags = flags; } +SlangCompileFlags EndToEndCompileRequest::getCompileFlags() +{ + return getFrontEndReq()->compileFlags; +} + void EndToEndCompileRequest::setDumpIntermediates(int enable) { shouldDumpIntermediates = (enable != 0); @@ -4909,4 +4914,14 @@ SlangResult EndToEndCompileRequest::getEntryPoint(SlangInt entryPointIndex, slan return SLANG_OK; } +SlangResult EndToEndCompileRequest::isParameterLocationUsed(Int entryPointIndex, Int targetIndex, SlangParameterCategory category, UInt spaceIndex, UInt registerIndex, bool& outUsed) +{ + CompileResult* compileResult = nullptr; + if (_getEntryPointResult(this, static_cast<int>(entryPointIndex), static_cast<int>(targetIndex), &compileResult) != SLANG_OK) + return SLANG_E_INVALID_ARG; + + return compileResult->isParameterLocationUsed(category, spaceIndex, registerIndex, outUsed); +} + + } // namespace Slang diff --git a/tests/cross-compile/cpp-resource-reflection.slang b/tests/cross-compile/cpp-resource-reflection.slang index 3d6fa7638..98253259e 100644 --- a/tests/cross-compile/cpp-resource-reflection.slang +++ b/tests/cross-compile/cpp-resource-reflection.slang @@ -1,4 +1,4 @@ -//TEST:CPU_REFLECTION: -profile cs_5_0 -entry computeMain -target cpp +//TEST:CPU_REFLECTION: -profile cs_5_0 -entry computeMain -target cpp -no-codegen struct Thing diff --git a/tests/cuda/cuda-reflection.slang b/tests/cuda/cuda-reflection.slang index 95bf591c9..f7cef88c3 100644 --- a/tests/cuda/cuda-reflection.slang +++ b/tests/cuda/cuda-reflection.slang @@ -1,6 +1,6 @@ // cuda-reflection.slang -//TEST:REFLECTION:-stage compute -entry main -target cuda +//TEST:REFLECTION:-stage compute -entry main -target cuda -no-codegen struct PadLadenStruct { diff --git a/tests/hlsl-intrinsic/sampler-feedback/sampler-feedback-basic.slang b/tests/hlsl-intrinsic/sampler-feedback/sampler-feedback-basic.slang index 3b9497e5e..5f13a9ca7 100644 --- a/tests/hlsl-intrinsic/sampler-feedback/sampler-feedback-basic.slang +++ b/tests/hlsl-intrinsic/sampler-feedback/sampler-feedback-basic.slang @@ -1,4 +1,4 @@ -//TEST:REFLECTION: -entry main -stage fragment -profile sm_6_5 -target hlsl +//TEST:REFLECTION: -entry main -stage fragment -profile sm_6_5 -target hlsl -no-codegen //DISABLE_TEST:CROSS_COMPILE: -entry main -stage fragment -profile sm_6_5 -target dxil-assembly FeedbackTexture2D<SAMPLER_FEEDBACK_MIN_MIP> feedbackMinMip; diff --git a/tests/ir/string-literal-hash-reflection.slang b/tests/ir/string-literal-hash-reflection.slang index 85d6ac3e4..a0a28876b 100644 --- a/tests/ir/string-literal-hash-reflection.slang +++ b/tests/ir/string-literal-hash-reflection.slang @@ -1,4 +1,4 @@ -//TEST:REFLECTION:-stage compute -entry computeMain -target hlsl +//TEST:REFLECTION:-stage compute -entry computeMain -target hlsl -no-codegen import string_literal_module; diff --git a/tests/reflection/arrays.hlsl b/tests/reflection/arrays.hlsl index d013bc498..20d18e91e 100644 --- a/tests/reflection/arrays.hlsl +++ b/tests/reflection/arrays.hlsl @@ -1,4 +1,4 @@ -//TEST:REFLECTION:-profile ps_4_0 -target hlsl +//TEST:REFLECTION:-profile ps_4_0 -target hlsl -no-codegen // Confirm that we can generate reflection info for arrays // diff --git a/tests/reflection/attribute.slang b/tests/reflection/attribute.slang index 687148add..41179fcf5 100644 --- a/tests/reflection/attribute.slang +++ b/tests/reflection/attribute.slang @@ -2,7 +2,7 @@ // Tests reflection of user defined attributes. -//TEST:REFLECTION:-stage compute -entry main -target hlsl +//TEST:REFLECTION:-stage compute -entry main -target hlsl -no-codegen [__AttributeUsage(_AttributeTargets.Struct)] struct MyStructAttribute diff --git a/tests/reflection/binding-gl.hlsl b/tests/reflection/binding-gl.hlsl index cd2ab666a..04c65ca54 100644 --- a/tests/reflection/binding-gl.hlsl +++ b/tests/reflection/binding-gl.hlsl @@ -1,4 +1,4 @@ -//TEST:REFLECTION:-profile ps_4_0 -target spirv +//TEST:REFLECTION:-profile ps_4_0 -target spirv -no-codegen // Confirm that we can generate reflection info for arrays // diff --git a/tests/reflection/binding-push-constant-gl.hlsl b/tests/reflection/binding-push-constant-gl.hlsl index f6bf13602..a3110f5cb 100644 --- a/tests/reflection/binding-push-constant-gl.hlsl +++ b/tests/reflection/binding-push-constant-gl.hlsl @@ -1,4 +1,4 @@ -//TEST:REFLECTION:-profile ps_4_0 -target spirv +//TEST:REFLECTION:-profile ps_4_0 -target spirv -no-codegen // Confirm that we can generate reflection info for arrays // diff --git a/tests/reflection/buffer-layout.slang b/tests/reflection/buffer-layout.slang index 51b9680d1..b2cc3f724 100644 --- a/tests/reflection/buffer-layout.slang +++ b/tests/reflection/buffer-layout.slang @@ -4,8 +4,8 @@ // to confirm that our reflection logic correctly reports the offsets // that the compute test sees in practice. -//TEST:REFLECTION:-stage compute -entry main -target hlsl -//TEST:REFLECTION:-stage compute -entry main -target spirv +//TEST:REFLECTION:-stage compute -entry main -target hlsl -no-codegen +//TEST:REFLECTION:-stage compute -entry main -target spirv -no-codegen struct A { diff --git a/tests/reflection/cross-compile.slang b/tests/reflection/cross-compile.slang index 600f3ed0f..30b080c64 100644 --- a/tests/reflection/cross-compile.slang +++ b/tests/reflection/cross-compile.slang @@ -1,4 +1,4 @@ -//TEST(smoke):REFLECTION:-profile glsl_fragment -target glsl +//TEST(smoke):REFLECTION:-profile glsl_fragment -target glsl -no-codegen // Confirm that when targetting GLSL via cross compilation, // we use the Vulkan layout rules instead of HLSL ones diff --git a/tests/reflection/default-space.slang b/tests/reflection/default-space.slang index 666714b79..f279d71b2 100644 --- a/tests/reflection/default-space.slang +++ b/tests/reflection/default-space.slang @@ -1,4 +1,4 @@ -//TEST:REFLECTION:-profile sm_5_1 -stage fragment -target hlsl +//TEST:REFLECTION:-profile sm_5_1 -stage fragment -target hlsl -no-codegen // This test is to confirm that we do not allocate a "default" // space/set for global shader parameters unless it is diff --git a/tests/reflection/explicit-register-space.slang b/tests/reflection/explicit-register-space.slang index d0bdc8178..79369037f 100644 --- a/tests/reflection/explicit-register-space.slang +++ b/tests/reflection/explicit-register-space.slang @@ -1,5 +1,5 @@ // explicit-register-space.slang -//TEST:REFLECTION:-profile ps_5_1 -target hlsl +//TEST:REFLECTION:-profile ps_5_1 -target hlsl -no-codegen // Confirm that we handle explicit register spaces // on global shader parameters. diff --git a/tests/reflection/matrix-layout.slang b/tests/reflection/matrix-layout.slang index a0c1cdff1..2f0fe6a41 100644 --- a/tests/reflection/matrix-layout.slang +++ b/tests/reflection/matrix-layout.slang @@ -1,5 +1,5 @@ -//TEST:REFLECTION:-profile ps_4_0 -target hlsl -//TEST:REFLECTION:-profile ps_4_0 -target hlsl -matrix-layout-row-major +//TEST:REFLECTION:-profile ps_4_0 -target hlsl -no-codegen +//TEST:REFLECTION:-profile ps_4_0 -target hlsl -no-codegen -matrix-layout-row-major // Test that we apply matrix layout rules correctly. diff --git a/tests/reflection/mix-explicit-and-implicit-spaces.slang b/tests/reflection/mix-explicit-and-implicit-spaces.slang index 52dd4321e..22e0163ee 100644 --- a/tests/reflection/mix-explicit-and-implicit-spaces.slang +++ b/tests/reflection/mix-explicit-and-implicit-spaces.slang @@ -1,5 +1,5 @@ // mix-explicit-and-implicit-spaces.slang -//TEST:REFLECTION:-profile cs_5_1 -target hlsl +//TEST:REFLECTION:-profile cs_5_1 -target hlsl -no-codegen // Ensure that correct layout/reflection is computed // when mixing implicit and explicit spaces for diff --git a/tests/reflection/multi-file.hlsl b/tests/reflection/multi-file.hlsl index fd9235ab5..6b42f807f 100644 --- a/tests/reflection/multi-file.hlsl +++ b/tests/reflection/multi-file.hlsl @@ -1,4 +1,4 @@ -//TEST:REFLECTION:-D__SLANG__ -entry mainVS -profile vs_4_0 -target hlsl tests/reflection/multi-file-extra.hlsl -entry mainFS -profile ps_4_0 +//TEST:REFLECTION:-D__SLANG__ -entry mainVS -profile vs_4_0 -target hlsl tests/reflection/multi-file-extra.hlsl -entry mainFS -profile ps_4_0 -no-codegen // Here we are testing the case where multiple translation units are provided // at once, so that we want combined reflection information for the resulting diff --git a/tests/reflection/parameter-block-explicit-space.slang b/tests/reflection/parameter-block-explicit-space.slang index b4d3eff9c..988584bec 100644 --- a/tests/reflection/parameter-block-explicit-space.slang +++ b/tests/reflection/parameter-block-explicit-space.slang @@ -1,6 +1,6 @@ // parameter-block-explicit-space.slang -//TEST:REFLECTION:-D__SLANG__ -stage fragment -entry main -profile sm_5_1 -target hlsl +//TEST:REFLECTION:-D__SLANG__ -stage fragment -entry main -profile sm_5_1 -target hlsl -no-codegen //TEST:COMPARE_HLSL:-stage fragment -entry main -profile sm_5_1 #ifdef __SLANG__ diff --git a/tests/reflection/parameter-block.slang b/tests/reflection/parameter-block.slang index 5c91ed339..67b49aadb 100644 --- a/tests/reflection/parameter-block.slang +++ b/tests/reflection/parameter-block.slang @@ -1,6 +1,6 @@ -//TEST:REFLECTION:-stage fragment -target glsl -//TEST:REFLECTION:-stage fragment -target hlsl -profile sm_5_0 -//TEST:REFLECTION:-stage fragment -target hlsl -profile sm_5_1 +//TEST:REFLECTION:-stage fragment -target glsl -no-codegen +//TEST:REFLECTION:-stage fragment -target hlsl -no-codegen -profile sm_5_0 +//TEST:REFLECTION:-stage fragment -target hlsl -no-codegen -profile sm_5_1 // Confirm that we do parameter binding correctly // when we have both a parameter block *and* user-defined diff --git a/tests/reflection/reflect-imported-code.hlsl b/tests/reflection/reflect-imported-code.hlsl index c3562af35..9370639d2 100644 --- a/tests/reflection/reflect-imported-code.hlsl +++ b/tests/reflection/reflect-imported-code.hlsl @@ -1,4 +1,4 @@ -//TEST:REFLECTION:-profile ps_4_0 -target hlsl +//TEST:REFLECTION:-profile ps_4_0 -target hlsl -no-codegen // Confirm that shader parameters in imported modules get reflected properly. diff --git a/tests/reflection/reflect-static.slang b/tests/reflection/reflect-static.slang index f7a44cde8..cd4ebab80 100644 --- a/tests/reflection/reflect-static.slang +++ b/tests/reflection/reflect-static.slang @@ -1,4 +1,4 @@ -//TEST:REFLECTION:-profile cs_6_0 -target hlsl -entry computeMain +//TEST:REFLECTION:-profile cs_6_0 -target hlsl -entry computeMain -no-codegen struct Thing { diff --git a/tests/reflection/reflection0.hlsl b/tests/reflection/reflection0.hlsl index 22232b59a..8865b7c35 100644 --- a/tests/reflection/reflection0.hlsl +++ b/tests/reflection/reflection0.hlsl @@ -1,4 +1,4 @@ -//TEST:REFLECTION:-profile ps_4_0 -target hlsl +//TEST:REFLECTION:-profile ps_4_0 -target hlsl -no-codegen // Confirm that basic reflection info can be output diff --git a/tests/reflection/resource-in-cbuffer.hlsl b/tests/reflection/resource-in-cbuffer.hlsl index b96f73113..7c016181c 100644 --- a/tests/reflection/resource-in-cbuffer.hlsl +++ b/tests/reflection/resource-in-cbuffer.hlsl @@ -1,4 +1,4 @@ -//TEST(smoke):REFLECTION:-profile ps_4_0 -target hlsl +//TEST(smoke):REFLECTION:-profile ps_4_0 -target hlsl -no-codegen // Confirm that we can generate reflection // information for resources nested inside diff --git a/tests/reflection/sample-index-input.hlsl b/tests/reflection/sample-index-input.hlsl index edb0690d6..d4d797a26 100644 --- a/tests/reflection/sample-index-input.hlsl +++ b/tests/reflection/sample-index-input.hlsl @@ -1,4 +1,4 @@ -//TEST:REFLECTION:-profile ps_5_0 -target hlsl +//TEST:REFLECTION:-profile ps_5_0 -target hlsl -no-codegen // Confirm that we register a shader as sample-rate when // it declares `SV_SampleIndex` as an input. diff --git a/tests/reflection/sample-rate-input.hlsl b/tests/reflection/sample-rate-input.hlsl index 0545afb02..f69149df8 100644 --- a/tests/reflection/sample-rate-input.hlsl +++ b/tests/reflection/sample-rate-input.hlsl @@ -1,4 +1,4 @@ -//TEST:REFLECTION:-profile ps_5_0 -target hlsl +//TEST:REFLECTION:-profile ps_5_0 -target hlsl -no-codegen // Confirm that we register a shader as sample-rate when // it declares (not necessarly *uses*) a `sample` qualified input diff --git a/tests/reflection/shared-modifier.hlsl b/tests/reflection/shared-modifier.hlsl index 45a1dfac8..928bdb1c9 100644 --- a/tests/reflection/shared-modifier.hlsl +++ b/tests/reflection/shared-modifier.hlsl @@ -1,5 +1,5 @@ // shared-modifier.hlsl -//TEST:REFLECTION:-profile ps_5_0 -target hlsl +//TEST:REFLECTION:-profile ps_5_0 -target hlsl -no-codegen // Confirm that we expose the `shared` modifier in reflection data. diff --git a/tests/reflection/structured-buffer.slang b/tests/reflection/structured-buffer.slang index 491d61486..b3d8b860b 100644 --- a/tests/reflection/structured-buffer.slang +++ b/tests/reflection/structured-buffer.slang @@ -1,4 +1,4 @@ -//TEST:REFLECTION:-profile ps_4_0 -target hlsl +//TEST:REFLECTION:-profile ps_4_0 -target hlsl -no-codegen // Confirm that we reflect the contents of structure-buffer types correctly. diff --git a/tests/reflection/thread-group-size.hlsl b/tests/reflection/thread-group-size.hlsl index 7e0400b46..13b3148d8 100644 --- a/tests/reflection/thread-group-size.hlsl +++ b/tests/reflection/thread-group-size.hlsl @@ -1,4 +1,4 @@ -//TEST:REFLECTION:-profile cs_5_0 -target hlsl +//TEST:REFLECTION:-profile cs_5_0 -target hlsl -no-codegen // Confirm that we provide reflection data for the `numthreads` attribute diff --git a/tests/reflection/unbounded-arrays.hlsl b/tests/reflection/unbounded-arrays.hlsl index 2c3b7a7bb..603bb6071 100644 --- a/tests/reflection/unbounded-arrays.hlsl +++ b/tests/reflection/unbounded-arrays.hlsl @@ -1,7 +1,7 @@ // unbounded-arrays.hlsl //TEST:COMPARE_HLSL:-profile cs_5_1 -entry main -//TEST:REFLECTION:-profile cs_5_1 -target hlsl -D__SLANG__ +//TEST:REFLECTION:-profile cs_5_1 -target hlsl -no-codegen -D__SLANG__ // // This test is trying to make sure that we correctly compute diff --git a/tests/reflection/used-parameters.slang b/tests/reflection/used-parameters.slang new file mode 100644 index 000000000..efb605a14 --- /dev/null +++ b/tests/reflection/used-parameters.slang @@ -0,0 +1,52 @@ +// used-parameters.slang + +// Tests post-emit analysis of shader parameters to find out if they are used or not. + +//TEST:REFLECTION:-stage compute -entry main -target hlsl + + +struct S +{ + uint2 Size; +}; + +ConstantBuffer<S> UsedCB; +ConstantBuffer<S> UnusedCB; + +Texture2D UsedTexture; +Texture2D UnusedTexture; + +Buffer<uint> UsedBuffer; +Buffer<uint> UnusedBuffer; + +StructuredBuffer<uint> UsedStructuredBuffer; +StructuredBuffer<uint> UnusedStructuredBuffer; + +RWTexture2D UsedRWTexture; +RWTexture2D UnusedRWTexture; + +RWBuffer<uint> UsedRWBuffer; +RWBuffer<uint> UnusedRWBuffer; + +RWStructuredBuffer<uint> UsedRWStructuredBuffer; +RWStructuredBuffer<uint> UnusedRWStructuredBuffer; + +SamplerState UsedSampler; +SamplerState UnusedSampler; + +uniform uint UsedUniform; +uniform uint UnusedUniform; + +[numthreads(1, 1, 1)] +void main(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + float A = UsedTexture[dispatchThreadID.xy].x; + uint B = UsedBuffer[dispatchThreadID.x]; + uint C = UsedStructuredBuffer[dispatchThreadID.y]; + float D = UsedRWTexture[dispatchThreadID.xy].x; + uint E = UsedRWBuffer[dispatchThreadID.y]; + float F = UsedTexture.SampleLevel(UsedSampler, float2(dispatchThreadID.xy) / float2(UsedCB.Size), 0).x; + uint G = UsedUniform; + + UsedRWStructuredBuffer[dispatchThreadID.x + dispatchThreadID.y * UsedCB.Size.x] = uint(A) + B + C + uint(D) + E + uint(F) + G; +}
\ No newline at end of file diff --git a/tests/reflection/used-parameters.slang.expected b/tests/reflection/used-parameters.slang.expected new file mode 100644 index 000000000..2bbc5b41a --- /dev/null +++ b/tests/reflection/used-parameters.slang.expected @@ -0,0 +1,350 @@ +result code = 0 +standard error = { +} +standard output = { +{ + "parameters": [ + { + "name": "UsedCB", + "binding": {"kind": "constantBuffer", "index": 1}, + "type": { + "kind": "constantBuffer", + "elementType": { + "kind": "struct", + "name": "S", + "fields": [ + { + "name": "Size", + "type": { + "kind": "vector", + "elementCount": 2, + "elementType": { + "kind": "scalar", + "scalarType": "uint32" + } + }, + "binding": {"kind": "uniform", "offset": 0, "size": 8} + } + ] + }, + "containerVarLayout": { + "binding": {"kind": "constantBuffer", "index": 0} + }, + "elementVarLayout": { + "type": { + "kind": "struct", + "name": "S", + "fields": [ + { + "name": "Size", + "type": { + "kind": "vector", + "elementCount": 2, + "elementType": { + "kind": "scalar", + "scalarType": "uint32" + } + }, + "binding": {"kind": "uniform", "offset": 0, "size": 8} + } + ] + }, + "binding": {"kind": "uniform", "offset": 0, "size": 8} + } + } + }, + { + "name": "UnusedCB", + "binding": {"kind": "constantBuffer", "index": 2}, + "type": { + "kind": "constantBuffer", + "elementType": { + "kind": "struct", + "name": "S", + "fields": [ + { + "name": "Size", + "type": { + "kind": "vector", + "elementCount": 2, + "elementType": { + "kind": "scalar", + "scalarType": "uint32" + } + }, + "binding": {"kind": "uniform", "offset": 0, "size": 8} + } + ] + }, + "containerVarLayout": { + "binding": {"kind": "constantBuffer", "index": 0} + }, + "elementVarLayout": { + "type": { + "kind": "struct", + "name": "S", + "fields": [ + { + "name": "Size", + "type": { + "kind": "vector", + "elementCount": 2, + "elementType": { + "kind": "scalar", + "scalarType": "uint32" + } + }, + "binding": {"kind": "uniform", "offset": 0, "size": 8} + } + ] + }, + "binding": {"kind": "uniform", "offset": 0, "size": 8} + } + } + }, + { + "name": "UsedTexture", + "binding": {"kind": "shaderResource", "index": 0}, + "type": { + "kind": "resource", + "baseShape": "texture2D" + } + }, + { + "name": "UnusedTexture", + "binding": {"kind": "shaderResource", "index": 1}, + "type": { + "kind": "resource", + "baseShape": "texture2D" + } + }, + { + "name": "UsedBuffer", + "binding": {"kind": "shaderResource", "index": 2}, + "type": { + "kind": "resource", + "baseShape": "textureBuffer" + } + }, + { + "name": "UnusedBuffer", + "binding": {"kind": "shaderResource", "index": 3}, + "type": { + "kind": "resource", + "baseShape": "textureBuffer" + } + }, + { + "name": "UsedStructuredBuffer", + "binding": {"kind": "shaderResource", "index": 4}, + "type": { + "kind": "resource", + "baseShape": "structuredBuffer", + "resultType": { + "kind": "scalar", + "scalarType": "uint32" + } + } + }, + { + "name": "UnusedStructuredBuffer", + "binding": {"kind": "shaderResource", "index": 5}, + "type": { + "kind": "resource", + "baseShape": "structuredBuffer", + "resultType": { + "kind": "scalar", + "scalarType": "uint32" + } + } + }, + { + "name": "UsedRWTexture", + "binding": {"kind": "unorderedAccess", "index": 0}, + "type": { + "kind": "resource", + "baseShape": "texture2D", + "access": "readWrite" + } + }, + { + "name": "UnusedRWTexture", + "binding": {"kind": "unorderedAccess", "index": 1}, + "type": { + "kind": "resource", + "baseShape": "texture2D", + "access": "readWrite" + } + }, + { + "name": "UsedRWBuffer", + "binding": {"kind": "unorderedAccess", "index": 2}, + "type": { + "kind": "resource", + "baseShape": "textureBuffer", + "access": "readWrite" + } + }, + { + "name": "UnusedRWBuffer", + "binding": {"kind": "unorderedAccess", "index": 3}, + "type": { + "kind": "resource", + "baseShape": "textureBuffer", + "access": "readWrite" + } + }, + { + "name": "UsedRWStructuredBuffer", + "binding": {"kind": "unorderedAccess", "index": 4}, + "type": { + "kind": "resource", + "baseShape": "structuredBuffer", + "access": "readWrite", + "resultType": { + "kind": "scalar", + "scalarType": "uint32" + } + } + }, + { + "name": "UnusedRWStructuredBuffer", + "binding": {"kind": "unorderedAccess", "index": 5}, + "type": { + "kind": "resource", + "baseShape": "structuredBuffer", + "access": "readWrite", + "resultType": { + "kind": "scalar", + "scalarType": "uint32" + } + } + }, + { + "name": "UsedSampler", + "binding": {"kind": "samplerState", "index": 0}, + "type": { + "kind": "samplerState" + } + }, + { + "name": "UnusedSampler", + "binding": {"kind": "samplerState", "index": 1}, + "type": { + "kind": "samplerState" + } + }, + { + "name": "UsedUniform", + "binding": {"kind": "uniform", "offset": 0, "size": 4}, + "type": { + "kind": "scalar", + "scalarType": "uint32" + } + }, + { + "name": "UnusedUniform", + "binding": {"kind": "uniform", "offset": 4, "size": 4}, + "type": { + "kind": "scalar", + "scalarType": "uint32" + } + } + ], + "entryPoints": [ + { + "name": "main", + "stage:": "compute", + "parameters": [ + { + "name": "dispatchThreadID", + "semanticName": "SV_DISPATCHTHREADID", + "type": { + "kind": "vector", + "elementCount": 3, + "elementType": { + "kind": "scalar", + "scalarType": "uint32" + } + } + } + ], + "threadGroupSize": [1, 1, 1], + "bindings": [ + { + "name": "UsedCB", + "binding": {"kind": "constantBuffer", "index": 1, "used": 1} + }, + { + "name": "UnusedCB", + "binding": {"kind": "constantBuffer", "index": 2, "used": 0} + }, + { + "name": "UsedTexture", + "binding": {"kind": "shaderResource", "index": 0, "used": 1} + }, + { + "name": "UnusedTexture", + "binding": {"kind": "shaderResource", "index": 1, "used": 0} + }, + { + "name": "UsedBuffer", + "binding": {"kind": "shaderResource", "index": 2, "used": 1} + }, + { + "name": "UnusedBuffer", + "binding": {"kind": "shaderResource", "index": 3, "used": 0} + }, + { + "name": "UsedStructuredBuffer", + "binding": {"kind": "shaderResource", "index": 4, "used": 1} + }, + { + "name": "UnusedStructuredBuffer", + "binding": {"kind": "shaderResource", "index": 5, "used": 0} + }, + { + "name": "UsedRWTexture", + "binding": {"kind": "unorderedAccess", "index": 0, "used": 1} + }, + { + "name": "UnusedRWTexture", + "binding": {"kind": "unorderedAccess", "index": 1, "used": 0} + }, + { + "name": "UsedRWBuffer", + "binding": {"kind": "unorderedAccess", "index": 2, "used": 1} + }, + { + "name": "UnusedRWBuffer", + "binding": {"kind": "unorderedAccess", "index": 3, "used": 0} + }, + { + "name": "UsedRWStructuredBuffer", + "binding": {"kind": "unorderedAccess", "index": 4, "used": 1} + }, + { + "name": "UnusedRWStructuredBuffer", + "binding": {"kind": "unorderedAccess", "index": 5, "used": 0} + }, + { + "name": "UsedSampler", + "binding": {"kind": "samplerState", "index": 0, "used": 1} + }, + { + "name": "UnusedSampler", + "binding": {"kind": "samplerState", "index": 1, "used": 0} + }, + { + "name": "UsedUniform", + "binding": {"kind": "uniform", "offset": 0, "size": 4} + }, + { + "name": "UnusedUniform", + "binding": {"kind": "uniform", "offset": 4, "size": 4} + } + ] + } + ] +} +} diff --git a/tests/reflection/vertex-input-semantics.hlsl b/tests/reflection/vertex-input-semantics.hlsl index 87a8431bc..356746bed 100644 --- a/tests/reflection/vertex-input-semantics.hlsl +++ b/tests/reflection/vertex-input-semantics.hlsl @@ -1,4 +1,4 @@ -//TEST:REFLECTION:-profile vs_4_0 -target hlsl +//TEST:REFLECTION:-profile vs_4_0 -target hlsl -no-codegen // Confirm that we can generate reflection info for // vertex shader input parameters, including those diff --git a/tools/slang-reflection-test/slang-reflection-test-main.cpp b/tools/slang-reflection-test/slang-reflection-test-main.cpp index af6da5113..34fef404e 100644 --- a/tools/slang-reflection-test/slang-reflection-test-main.cpp +++ b/tools/slang-reflection-test/slang-reflection-test-main.cpp @@ -273,7 +273,9 @@ static void emitReflectionVarBindingInfoJSON( static void emitReflectionVarBindingInfoJSON( PrettyWriter& writer, - slang::VariableLayoutReflection* var) + slang::VariableLayoutReflection* var, + SlangCompileRequest* request = nullptr, + int entryPointIndex = -1) { auto stage = var->getStage(); if (stage != SLANG_STAGE_NONE) @@ -321,15 +323,28 @@ static void emitReflectionVarBindingInfoJSON( auto space = var->getBindingSpace(category); auto count = typeLayout->getSize(category); + // Query the paramater usage for the specified entry point. + // Note: both `request` and `entryPointIndex` may be invalid here, but that should just make the function return a failure. + bool used = false; + bool usedAvailable = spIsParameterLocationUsed(request, entryPointIndex, 0, category, space, index, used) == SLANG_OK; + if (cc != 0) write(writer, ",\n"); write(writer,"{"); + emitReflectionVarBindingInfoJSON( writer, category, index, count, space); + + if (usedAvailable) + { + write(writer, ", \"used\": "); + write(writer, used); + } + write(writer,"}"); } @@ -1030,6 +1045,27 @@ static void emitReflectionParamJSON( write(writer, "\n}"); } + +static void emitEntryPointParamJSON( + PrettyWriter& writer, + slang::VariableLayoutReflection* param, + SlangCompileRequest* request, + int entryPointIndex) +{ + write(writer, "{\n"); + indent(writer); + + if( auto name = param->getName() ) + { + emitReflectionNameInfoJSON(writer, name); + } + + emitReflectionVarBindingInfoJSON(writer, param, request, entryPointIndex); + + dedent(writer); + write(writer, "\n}"); +} + template<typename T> struct Range { @@ -1110,8 +1146,12 @@ static void emitReflectionTypeParamJSON( static void emitReflectionEntryPointJSON( PrettyWriter& writer, - slang::EntryPointReflection* entryPoint) + SlangCompileRequest* request, + slang::ShaderReflection* programReflection, + int entryPointIndex) { + slang::EntryPointReflection* entryPoint = programReflection->getEntryPointByIndex(entryPointIndex); + write(writer, "{\n"); indent(writer); @@ -1170,12 +1210,32 @@ static void emitReflectionEntryPointJSON( write(writer, "]"); } + // If code generation has been performed, print out the parameter usage by this entry point. + if ((request->getCompileFlags() & SLANG_COMPILE_FLAG_NO_CODEGEN) == 0) + { + write(writer, ",\n\"bindings\": [\n"); + indent(writer); + + auto parameterCount = programReflection->getParameterCount(); + for( auto pp : range(parameterCount) ) + { + if(pp != 0) write(writer, ",\n"); + + auto parameter = programReflection->getParameterByIndex(pp); + emitEntryPointParamJSON(writer, parameter, request, entryPointIndex); + } + + dedent(writer); + write(writer, "\n]"); + } + dedent(writer); write(writer, "\n}"); } static void emitReflectionJSON( PrettyWriter& writer, + SlangCompileRequest* request, slang::ShaderReflection* programReflection) { write(writer, "{\n"); @@ -1200,13 +1260,12 @@ static void emitReflectionJSON( { write(writer, ",\n\"entryPoints\": [\n"); indent(writer); - + for (auto ee : range(entryPointCount)) { if (ee != 0) write(writer, ",\n"); - auto entryPoint = programReflection->getEntryPointByIndex(ee); - emitReflectionEntryPointJSON(writer, entryPoint); + emitReflectionEntryPointJSON(writer, request, programReflection, (int)ee); } dedent(writer); @@ -1264,13 +1323,14 @@ static void emitReflectionJSON( } void emitReflectionJSON( + SlangCompileRequest* request, SlangReflection* reflection) { auto programReflection = (slang::ShaderReflection*) reflection; PrettyWriter writer; - emitReflectionJSON(writer, programReflection); + emitReflectionJSON(writer, request, programReflection); } static SlangResult maybeDumpDiagnostic(SlangResult res, SlangCompileRequest* request) @@ -1285,15 +1345,6 @@ static SlangResult maybeDumpDiagnostic(SlangResult res, SlangCompileRequest* req SlangResult performCompilationAndReflection(SlangCompileRequest* request, int argc, const char*const* argv) { - // We don't actually need codegen to get reflection. - // - // Ideally perhaps this would use a call to - // request->setCompileFlags(flags); - // But that relies on knowing what flags are set, and there isn't a way to get that, so do it arg way - - const char* noCodeGenArgs[] = { "-no-codegen" }; - SLANG_RETURN_ON_FAIL(maybeDumpDiagnostic(spProcessCommandLineArguments(request, noCodeGenArgs, SLANG_COUNT_OF(noCodeGenArgs)), request)); - SLANG_RETURN_ON_FAIL(maybeDumpDiagnostic(spProcessCommandLineArguments(request, &argv[1], argc - 1), request)); SLANG_RETURN_ON_FAIL(maybeDumpDiagnostic(spCompile(request), request)); @@ -1301,7 +1352,7 @@ SlangResult performCompilationAndReflection(SlangCompileRequest* request, int ar // we have. SlangReflection* reflection = spGetReflection(request); - emitReflectionJSON(reflection); + emitReflectionJSON(request, reflection); return SLANG_OK; } |
