diff --git a/StandAlone/StandAlone.cpp b/StandAlone/StandAlone.cpp index 5d30ac2be0ab17269c0d0fea2869032838c543e7..f0a4f93cfb7d7ce529d6217bee60d38ed1ba3f79 100644 --- a/StandAlone/StandAlone.cpp +++ b/StandAlone/StandAlone.cpp @@ -584,6 +584,12 @@ void CompileAndLinkShaderUnits(std::vector<ShaderCompUnit> compUnits) if (! (Options & EOptionOutputPreprocessed) && ! program.link(messages)) LinkFailed = true; + // Map IO + if (Options & EOptionSpv) { + if (!program.mapIO()) + LinkFailed = true; + } + // Report if (! (Options & EOptionSuppressInfolog) && ! (Options & EOptionMemoryLeakMode)) { @@ -591,10 +597,6 @@ void CompileAndLinkShaderUnits(std::vector<ShaderCompUnit> compUnits) PutsIfNonEmpty(program.getInfoDebugLog()); } - // Map IO - if (Options & EOptionSpv) - program.mapIO(); - // Reflect if (Options & EOptionDumpReflection) { program.buildReflection(); diff --git a/Test/baseResults/spv.register.autoassign.rangetest.frag.out b/Test/baseResults/spv.register.autoassign.rangetest.frag.out new file mode 100644 index 0000000000000000000000000000000000000000..a521a13b845d6fe6c739c47134493754ddbbd021 --- /dev/null +++ b/Test/baseResults/spv.register.autoassign.rangetest.frag.out @@ -0,0 +1,12 @@ +spv.register.autoassign.rangetest.frag + +Linked fragment stage: + +INTERNAL ERROR: mapped binding out of range: g_tScene +INTERNAL ERROR: mapped binding out of range: g_tSamp +INTERNAL ERROR: mapped binding out of range: g_tScene +INTERNAL ERROR: mapped binding out of range: g_tSamp +INTERNAL ERROR: mapped binding out of range: g_tSamp +INTERNAL ERROR: mapped binding out of range: g_tScene + +SPIR-V is not generated for failed compile or link diff --git a/Test/spv.register.autoassign.rangetest.frag b/Test/spv.register.autoassign.rangetest.frag new file mode 100644 index 0000000000000000000000000000000000000000..c81c39594ede372ca879936ecdd43ef3c023dd30 --- /dev/null +++ b/Test/spv.register.autoassign.rangetest.frag @@ -0,0 +1,15 @@ + +SamplerState g_tSamp : register(s5); + +Texture2D g_tScene[2] : register(t5); + +struct PS_OUTPUT +{ + float4 Color : SV_Target0; +}; + +void main(out PS_OUTPUT psout) +{ + psout.Color = g_tScene[0].Sample(g_tSamp, 0.3) + + g_tScene[1].Sample(g_tSamp, 0.3); +} diff --git a/glslang/MachineIndependent/ShaderLang.cpp b/glslang/MachineIndependent/ShaderLang.cpp index e00638b60e3ef6f376aaad812f7302ed0add6528..5333af4583e7b556ed1c259ade2ef9134684c728 100644 --- a/glslang/MachineIndependent/ShaderLang.cpp +++ b/glslang/MachineIndependent/ShaderLang.cpp @@ -1724,7 +1724,7 @@ bool TProgram::mapIO() for (int s = 0; s < EShLangCount; ++s) { if (intermediate[s]) { - if (! ioMapper->addStage((EShLanguage)s, *intermediate[s])) + if (! ioMapper->addStage((EShLanguage)s, *intermediate[s], *infoSink)) return false; } } diff --git a/glslang/MachineIndependent/iomapper.cpp b/glslang/MachineIndependent/iomapper.cpp index d69cc134866e4b6c18acbc25828921f335908213..15847ddbc20d9256ac21a6680ffae180ca35e2a8 100644 --- a/glslang/MachineIndependent/iomapper.cpp +++ b/glslang/MachineIndependent/iomapper.cpp @@ -34,6 +34,7 @@ // #include "../Include/Common.h" +#include "../Include/InfoSink.h" #include "iomapper.h" #include "LiveTraverser.h" #include "localintermediate.h" @@ -150,11 +151,30 @@ protected: class TIoMappingTraverser : public TBindingTraverser { public: TIoMappingTraverser(TIntermediate& i, TBindingMap& bindingMap, TUsedBindings& usedBindings, - bool traverseDeadCode) : - TBindingTraverser(i, bindingMap, usedBindings, traverseDeadCode) + TInfoSink& infoSink, bool traverseDeadCode) : + TBindingTraverser(i, bindingMap, usedBindings, traverseDeadCode), + infoSink(infoSink), + assignError(false) { } + bool success() const { return !assignError; } + protected: + unsigned checkBindingRange(const TIntermSymbol& base, unsigned binding) + { + if (binding >= TQualifier::layoutBindingEnd) { + TString err = "mapped binding out of range: "; + err += base.getName(); + + infoSink.info.message(EPrefixInternalError, err.c_str()); + assignError = true; + + return 0; + } + + return binding; + } + void addUniform(TIntermSymbol& base) override { // Skip things we don't intend to bind. @@ -165,7 +185,7 @@ protected: // Apply existing binding, if we were given one or already made one up. if (existingBinding != -1) { - base.getWritableType().getQualifier().layoutBinding = existingBinding; + base.getWritableType().getQualifier().layoutBinding = checkBindingRange(base, existingBinding); return; } @@ -174,7 +194,7 @@ protected: const int freeBinding = getFreeBinding(base.getType(), getBindingBase(base.getType())); markBinding(base, freeBinding); - base.getWritableType().getQualifier().layoutBinding = freeBinding; + base.getWritableType().getQualifier().layoutBinding = checkBindingRange(base, freeBinding); } } @@ -195,13 +215,17 @@ protected: return nextBinding; } + +private: + bool assignError; // true if there was an error assigning the bindings + TInfoSink& infoSink; }; // Map I/O variables to provided offsets, and make bindings for // unbound but live variables. // // Returns false if the input is too malformed to do this. -bool TIoMapper::addStage(EShLanguage, TIntermediate& intermediate) +bool TIoMapper::addStage(EShLanguage, TIntermediate& intermediate, TInfoSink& infoSink) { // Trivial return if there is nothing to do. if (intermediate.getShiftSamplerBinding() == 0 && @@ -223,7 +247,7 @@ bool TIoMapper::addStage(EShLanguage, TIntermediate& intermediate) TBindingTraverser it_binding_all(intermediate, bindingMap, usedBindings, true); TBindingTraverser it_binding_live(intermediate, bindingMap, usedBindings, false); - TIoMappingTraverser it_iomap(intermediate, bindingMap, usedBindings, true); + TIoMappingTraverser it_iomap(intermediate, bindingMap, usedBindings, infoSink, true); // Traverse all (live+dead) code to find explicit bindings, so we can avoid those. root->traverse(&it_binding_all); @@ -240,7 +264,7 @@ bool TIoMapper::addStage(EShLanguage, TIntermediate& intermediate) // Bind everything that needs a binding and doesn't have one. root->traverse(&it_iomap); - return true; + return it_iomap.success(); } } // end namespace glslang diff --git a/glslang/MachineIndependent/iomapper.h b/glslang/MachineIndependent/iomapper.h index 69ed4c5bf1385c344612cd08906a83b928f20690..68dec7761a315e65a2371370cb2d0cbf7056f032 100644 --- a/glslang/MachineIndependent/iomapper.h +++ b/glslang/MachineIndependent/iomapper.h @@ -42,6 +42,8 @@ // A reflection database and its interface, consistent with the OpenGL API reflection queries. // +class TInfoSink; + namespace glslang { class TIntermediate; @@ -53,7 +55,7 @@ public: virtual ~TIoMapper() {} // grow the reflection stage by stage - bool addStage(EShLanguage, TIntermediate&); + bool addStage(EShLanguage, TIntermediate&, TInfoSink&); }; } // end namespace glslang diff --git a/gtests/Spv.FromFile.cpp b/gtests/Spv.FromFile.cpp index 64312a3ac222a6c1c57823654d3d5aaf128d255b..a55af518794b2c691b85783caa1348a2a738e307 100644 --- a/gtests/Spv.FromFile.cpp +++ b/gtests/Spv.FromFile.cpp @@ -287,6 +287,10 @@ INSTANTIATE_TEST_CASE_P( { "spv.register.noautoassign.frag", "main_ep", 5, 10, 15, false, false }, { "spv.register.autoassign-2.frag", "main", 5, 10, 15, true, true }, { "spv.buffer.autoassign.frag", "main", 5, 10, 15, true, true }, + { "spv.register.autoassign.rangetest.frag", "main", + glslang::TQualifier::layoutBindingEnd-2, + glslang::TQualifier::layoutBindingEnd+5, + 20, true, false }, }), FileNameAsCustomTestSuffixIoMap );