diff --git a/StandAlone/StandAlone.cpp b/StandAlone/StandAlone.cpp index c9b1ededf..7588320f1 100644 --- a/StandAlone/StandAlone.cpp +++ b/StandAlone/StandAlone.cpp @@ -528,6 +528,8 @@ void ProcessArguments(std::vector>& workItem ReflectOptions |= EShReflectionStrictArraySuffix; } else if (lowerword == "reflect-basic-array-suffix") { ReflectOptions |= EShReflectionBasicArraySuffix; + } else if (lowerword == "reflect-intermediate-io") { + ReflectOptions |= EShReflectionIntermediateIO; } else if (lowerword == "resource-set-bindings" || // synonyms lowerword == "resource-set-binding" || lowerword == "rsb") { @@ -1525,6 +1527,8 @@ void usage() " --no-storage-format | --nsf use Unknown image format\n" " --reflect-strict-array-suffix use strict array suffix rules when reflecting\n" " --reflect-basic-array-suffix arrays of basic types will have trailing [0]\n" + " --reflect-intermediate-io reflection includes inputs/outputs of linked shaders\n" + " rather than just vertex/fragment\n" " --resource-set-binding [stage] name set binding\n" " set descriptor set and binding for\n" " individual resources\n" diff --git a/Test/baseResults/hlsl.automap.frag.out b/Test/baseResults/hlsl.automap.frag.out index b9ab49cd1..7a871a7b5 100644 --- a/Test/baseResults/hlsl.automap.frag.out +++ b/Test/baseResults/hlsl.automap.frag.out @@ -25,5 +25,8 @@ u6: offset -1, type ffffffff, size 0, index -1, binding 46, stages 0 cb: offset -1, type ffffffff, size 4, index -1, binding 51, stages 0 tb: offset -1, type ffffffff, size 4, index -1, binding 17, stages 0 -Vertex attribute reflection: +Pipeline input reflection: + +Pipeline output reflection: +@entryPointOutput: offset 0, type 8b52, size 0, index 0, binding -1, stages 0 diff --git a/Test/baseResults/hlsl.reflection.binding.frag.out b/Test/baseResults/hlsl.reflection.binding.frag.out index 464ce0f74..3c0d134d2 100644 --- a/Test/baseResults/hlsl.reflection.binding.frag.out +++ b/Test/baseResults/hlsl.reflection.binding.frag.out @@ -15,5 +15,8 @@ Uniform block reflection: cbuff1: offset -1, type ffffffff, size 24, index -1, binding 2, stages 0 cbuff2: offset -1, type ffffffff, size 24, index -1, binding 3, stages 0 -Vertex attribute reflection: +Pipeline input reflection: + +Pipeline output reflection: +psout.Color: offset 0, type 8b52, size 0, index 0, binding -1, stages 0 diff --git a/Test/baseResults/hlsl.reflection.vert.out b/Test/baseResults/hlsl.reflection.vert.out index 3403bd358..92cb6ab1e 100644 --- a/Test/baseResults/hlsl.reflection.vert.out +++ b/Test/baseResults/hlsl.reflection.vert.out @@ -67,10 +67,12 @@ nested: offset -1, type ffffffff, size 32, index -1, binding -1, stages 0 abl: offset -1, type ffffffff, size 4, index -1, binding -1, stages 0 abl2: offset -1, type ffffffff, size 4, index -1, binding -1, stages 0 -Vertex attribute reflection: +Pipeline input reflection: attributeFloat: offset 0, type 1406, size 0, index 0, binding -1, stages 0 attributeFloat2: offset 0, type 8b50, size 0, index 0, binding -1, stages 0 attributeFloat3: offset 0, type 8b51, size 0, index 0, binding -1, stages 0 attributeFloat4: offset 0, type 8b52, size 0, index 0, binding -1, stages 0 attributeMat4: offset 0, type 8b5c, size 0, index 0, binding -1, stages 0 +Pipeline output reflection: + diff --git a/Test/baseResults/hlsl.shift.per-set.frag.out b/Test/baseResults/hlsl.shift.per-set.frag.out index 7c7d38304..02c6e4a21 100644 --- a/Test/baseResults/hlsl.shift.per-set.frag.out +++ b/Test/baseResults/hlsl.shift.per-set.frag.out @@ -226,5 +226,8 @@ u6: offset -1, type ffffffff, size 0, index -1, binding 34, stages 0 cb: offset -1, type ffffffff, size 4, index -1, binding 51, stages 0 tb: offset -1, type ffffffff, size 4, index -1, binding 27, stages 0 -Vertex attribute reflection: +Pipeline input reflection: + +Pipeline output reflection: +@entryPointOutput: offset 0, type 8b52, size 0, index 0, binding -1, stages 0 diff --git a/Test/baseResults/reflection.frag.out b/Test/baseResults/reflection.frag.out new file mode 100644 index 000000000..d3d90a27b --- /dev/null +++ b/Test/baseResults/reflection.frag.out @@ -0,0 +1,9 @@ +reflection.frag +Uniform reflection: + +Uniform block reflection: + +Pipeline input reflection: + +Pipeline output reflection: + diff --git a/Test/baseResults/reflection.options.frag.out b/Test/baseResults/reflection.options.frag.out new file mode 100644 index 000000000..d0fe56e36 --- /dev/null +++ b/Test/baseResults/reflection.options.frag.out @@ -0,0 +1,10 @@ +reflection.frag +Uniform reflection: + +Uniform block reflection: + +Pipeline input reflection: +inval: offset 0, type 1406, size 0, index 0, binding -1, stages 0 + +Pipeline output reflection: + diff --git a/Test/baseResults/reflection.options.vert.out b/Test/baseResults/reflection.options.vert.out index 22177c7ca..178779a3a 100644 --- a/Test/baseResults/reflection.options.vert.out +++ b/Test/baseResults/reflection.options.vert.out @@ -10,6 +10,9 @@ t[0].v[2].normal[0]: offset 60, type 1406, size 3, index 0, binding -1, stages 1 Uniform block reflection: VertexCollection: offset -1, type ffffffff, size 360, index -1, binding -1, stages 0 -Vertex attribute reflection: +Pipeline input reflection: gl_InstanceID: offset 0, type 1404, size 0, index 0, binding -1, stages 0 +Pipeline output reflection: +outval: offset 0, type 1406, size 0, index 0, binding -1, stages 0 + diff --git a/Test/baseResults/reflection.vert.out b/Test/baseResults/reflection.vert.out index 30aa7a3b3..fd3c8fe09 100644 --- a/Test/baseResults/reflection.vert.out +++ b/Test/baseResults/reflection.vert.out @@ -146,7 +146,7 @@ buf4: offset -1, type ffffffff, size 4, index -1, binding -1, stages 0 nested2: offset -1, type ffffffff, size 208, index -1, binding -1, stages 0 VertexCollection: offset -1, type ffffffff, size 360, index -1, binding -1, stages 0 -Vertex attribute reflection: +Pipeline input reflection: attributeFloat: offset 0, type 1406, size 0, index 0, binding -1, stages 0 attributeFloat2: offset 0, type 8b50, size 0, index 0, binding -1, stages 0 attributeFloat3: offset 0, type 8b51, size 0, index 0, binding -1, stages 0 @@ -154,3 +154,5 @@ attributeFloat4: offset 0, type 8b52, size 0, index 0, binding -1, stages 0 attributeMat4: offset 0, type 8b5c, size 0, index 0, binding -1, stages 0 gl_InstanceID: offset 0, type 1404, size 0, index 0, binding -1, stages 0 +Pipeline output reflection: + diff --git a/Test/reflection.frag b/Test/reflection.frag new file mode 100644 index 000000000..929965313 --- /dev/null +++ b/Test/reflection.frag @@ -0,0 +1,8 @@ +#version 440 core + +in float inval; + +void main() +{ + float f = inval; +} diff --git a/Test/reflection.options.vert b/Test/reflection.options.vert index c671a625d..5a8d2194b 100644 --- a/Test/reflection.options.vert +++ b/Test/reflection.options.vert @@ -13,6 +13,8 @@ buffer VertexCollection { TriangleInfo t[5]; }; +out float outval; + void main() { float f; @@ -20,4 +22,5 @@ void main() f += t[gl_InstanceID].v[gl_InstanceID].position[gl_InstanceID]; f += t[gl_InstanceID].v[gl_InstanceID].normal[gl_InstanceID]; TriangleInfo tlocal[5] = t; + outval = f; } diff --git a/Test/reflection.vert b/Test/reflection.vert index 76ec5a304..939e52a3c 100644 --- a/Test/reflection.vert +++ b/Test/reflection.vert @@ -174,6 +174,8 @@ buffer VertexCollection { TriangleInfo t[5]; }; +out float outval; + void main() { liveFunction1(image_ui2D, sampler_2D, sampler_2DMSArray); @@ -234,4 +236,6 @@ void main() f += t[gl_InstanceID].v[gl_InstanceID].position[gl_InstanceID]; f += t[gl_InstanceID].v[gl_InstanceID].normal[gl_InstanceID]; TriangleInfo tlocal[5] = t; + + outval = f; } diff --git a/Test/runtests b/Test/runtests index c01366e63..44d3dc351 100755 --- a/Test/runtests +++ b/Test/runtests @@ -32,8 +32,12 @@ diff -b $BASEDIR/badMacroArgs.frag.out $TARGETDIR/badMacroArgs.frag.out || HASER echo Running reflection... $EXE -l -q -C reflection.vert > $TARGETDIR/reflection.vert.out diff -b $BASEDIR/reflection.vert.out $TARGETDIR/reflection.vert.out || HASERROR=1 -$EXE -l -q -C --reflect-strict-array-suffix --reflect-basic-array-suffix reflection.options.vert > $TARGETDIR/reflection.options.vert.out +$EXE -l -q -C --reflect-strict-array-suffix --reflect-basic-array-suffix --reflect-intermediate-io reflection.options.vert > $TARGETDIR/reflection.options.vert.out diff -b $BASEDIR/reflection.options.vert.out $TARGETDIR/reflection.options.vert.out || HASERROR=1 +$EXE -l -q -C reflection.frag > $TARGETDIR/reflection.frag.out +diff -b $BASEDIR/reflection.frag.out $TARGETDIR/reflection.frag.out || HASERROR=1 +$EXE -l -q -C --reflect-strict-array-suffix --reflect-basic-array-suffix --reflect-intermediate-io reflection.frag > $TARGETDIR/reflection.options.frag.out +diff -b $BASEDIR/reflection.options.frag.out $TARGETDIR/reflection.options.frag.out || HASERROR=1 $EXE -D -Od -e flizv -l -q -C -V -Od hlsl.reflection.vert > $TARGETDIR/hlsl.reflection.vert.out diff -b $BASEDIR/hlsl.reflection.vert.out $TARGETDIR/hlsl.reflection.vert.out || HASERROR=1 $EXE -D -Od -e main -l -q -C -V -Od hlsl.reflection.binding.frag > $TARGETDIR/hlsl.reflection.binding.frag.out diff --git a/glslang/MachineIndependent/ShaderLang.cpp b/glslang/MachineIndependent/ShaderLang.cpp index 3e77292f6..a32d60535 100755 --- a/glslang/MachineIndependent/ShaderLang.cpp +++ b/glslang/MachineIndependent/ShaderLang.cpp @@ -1971,7 +1971,22 @@ bool TProgram::buildReflection(int opts) if (! linked || reflection) return false; - reflection = new TReflection((EShReflectionOptions)opts); + int firstStage = EShLangVertex, lastStage = EShLangFragment; + + if (opts & EShReflectionIntermediateIO) { + // if we're reflecting intermediate I/O, determine the first and last stage linked and use those as the + // boundaries for which stages generate pipeline inputs/outputs + firstStage = EShLangCount; + lastStage = 0; + for (int s = 0; s < EShLangCount; ++s) { + if (intermediate[s]) { + firstStage = std::min(firstStage, s); + lastStage = std::max(lastStage, s); + } + } + } + + reflection = new TReflection((EShReflectionOptions)opts, (EShLanguage)firstStage, (EShLanguage)lastStage); for (int s = 0; s < EShLangCount; ++s) { if (intermediate[s]) { @@ -1990,8 +2005,10 @@ int TProgram::getNumUniformVariables() const { return ref const TObjectReflection& TProgram::getUniform(int index) const { return reflection->getUniform(index); } int TProgram::getNumUniformBlocks() const { return reflection->getNumUniformBlocks(); } const TObjectReflection& TProgram::getUniformBlock(int index) const { return reflection->getUniformBlock(index); } -int TProgram::getNumAttributes() const { return reflection->getNumAttributes(); } -const TObjectReflection& TProgram::getAttribute(int index) const { return reflection->getAttribute(index); } +int TProgram::getNumPipeInputs() const { return reflection->getNumPipeInputs(); } +const TObjectReflection& TProgram::getPipeInput(int index) const { return reflection->getPipeInput(index); } +int TProgram::getNumPipeOutputs() const { return reflection->getNumPipeOutputs(); } +const TObjectReflection& TProgram::getPipeOutput(int index) const { return reflection->getPipeOutput(index); } void TProgram::dumpReflection() { reflection->dump(); } diff --git a/glslang/MachineIndependent/reflection.cpp b/glslang/MachineIndependent/reflection.cpp index ec627a18e..1d90fcf16 100644 --- a/glslang/MachineIndependent/reflection.cpp +++ b/glslang/MachineIndependent/reflection.cpp @@ -97,7 +97,7 @@ public: } } - void addAttribute(const TIntermSymbol& base) + void addPipeInput(const TIntermSymbol& base) { if (processedDerefs.find(&base) == processedDerefs.end()) { processedDerefs.insert(&base); @@ -107,8 +107,24 @@ public: TReflection::TNameToIndex::const_iterator it = reflection.nameToIndex.find(name.c_str()); if (it == reflection.nameToIndex.end()) { - reflection.nameToIndex[name.c_str()] = (int)reflection.indexToAttribute.size(); - reflection.indexToAttribute.push_back(TObjectReflection(name.c_str(), type, 0, mapToGlType(type), 0, 0)); + reflection.nameToIndex[name.c_str()] = (int)reflection.indexToPipeInput.size(); + reflection.indexToPipeInput.push_back(TObjectReflection(name.c_str(), type, 0, mapToGlType(type), 0, 0)); + } + } + } + + void addPipeOutput(const TIntermSymbol& base) + { + if (processedDerefs.find(&base) == processedDerefs.end()) { + processedDerefs.insert(&base); + + const TString &name = base.getName(); + const TType &type = base.getType(); + + TReflection::TNameToIndex::const_iterator it = reflection.nameToIndex.find(name.c_str()); + if (it == reflection.nameToIndex.end()) { + reflection.nameToIndex[name.c_str()] = (int)reflection.indexToPipeOutput.size(); + reflection.indexToPipeOutput.push_back(TObjectReflection(name.c_str(), type, 0, mapToGlType(type), 0, 0)); } } } @@ -853,8 +869,11 @@ void TReflectionTraverser::visitSymbol(TIntermSymbol* base) if (base->getQualifier().storage == EvqUniform) addUniform(*base); - if (intermediate.getStage() == EShLangVertex && base->getQualifier().isPipeInput()) - addAttribute(*base); + if (intermediate.getStage() == reflection.firstStage && base->getQualifier().isPipeInput()) + addPipeInput(*base); + + if (intermediate.getStage() == reflection.lastStage && base->getQualifier().isPipeOutput()) + addPipeOutput(*base); } // @@ -964,9 +983,14 @@ void TReflection::dump() indexToUniformBlock[i].dump(); printf("\n"); - printf("Vertex attribute reflection:\n"); - for (size_t i = 0; i < indexToAttribute.size(); ++i) - indexToAttribute[i].dump(); + printf("Pipeline input reflection:\n"); + for (size_t i = 0; i < indexToPipeInput.size(); ++i) + indexToPipeInput[i].dump(); + printf("\n"); + + printf("Pipeline output reflection:\n"); + for (size_t i = 0; i < indexToPipeOutput.size(); ++i) + indexToPipeOutput[i].dump(); printf("\n"); if (getLocalSize(0) > 1) { diff --git a/glslang/MachineIndependent/reflection.h b/glslang/MachineIndependent/reflection.h index c4464aab6..ccd87f1ae 100644 --- a/glslang/MachineIndependent/reflection.h +++ b/glslang/MachineIndependent/reflection.h @@ -55,7 +55,8 @@ class TReflectionTraverser; // The full reflection database class TReflection { public: - TReflection(EShReflectionOptions opts) : options(opts), badReflection(TObjectReflection::badReflection()) + TReflection(EShReflectionOptions opts, EShLanguage first, EShLanguage last) + : options(opts), firstStage(first), lastStage(last), badReflection(TObjectReflection::badReflection()) { for (int dim=0; dim<3; ++dim) localSize[dim] = 0; @@ -86,17 +87,27 @@ public: return badReflection; } - // for mapping an attribute index to the attribute's description - int getNumAttributes() { return (int)indexToAttribute.size(); } - const TObjectReflection& getAttribute(int i) const + // for mapping an pipeline input index to the input's description + int getNumPipeInputs() { return (int)indexToPipeInput.size(); } + const TObjectReflection& getPipeInput(int i) const { - if (i >= 0 && i < (int)indexToAttribute.size()) - return indexToAttribute[i]; + if (i >= 0 && i < (int)indexToPipeInput.size()) + return indexToPipeInput[i]; else return badReflection; } - // for mapping any name to its index (block names, uniform names and attribute names) + // for mapping an pipeline output index to the output's description + int getNumPipeOutputs() { return (int)indexToPipeOutput.size(); } + const TObjectReflection& getPipeOutput(int i) const + { + if (i >= 0 && i < (int)indexToPipeOutput.size()) + return indexToPipeOutput[i]; + else + return badReflection; + } + + // for mapping any name to its index (block names, uniform names and input/output names) int getIndex(const char* name) const { TNameToIndex::const_iterator it = nameToIndex.find(name); @@ -127,11 +138,15 @@ protected: EShReflectionOptions options; + EShLanguage firstStage; + EShLanguage lastStage; + TObjectReflection badReflection; // return for queries of -1 or generally out of range; has expected descriptions with in it for this TNameToIndex nameToIndex; // maps names to indexes; can hold all types of data: uniform/buffer and which function names have been processed TMapIndexToReflection indexToUniform; TMapIndexToReflection indexToUniformBlock; - TMapIndexToReflection indexToAttribute; + TMapIndexToReflection indexToPipeInput; + TMapIndexToReflection indexToPipeOutput; unsigned int localSize[3]; }; diff --git a/glslang/Public/ShaderLang.h b/glslang/Public/ShaderLang.h index 9a1f4bf04..98529256b 100755 --- a/glslang/Public/ShaderLang.h +++ b/glslang/Public/ShaderLang.h @@ -246,6 +246,7 @@ typedef enum { EShReflectionDefault = 0, // default is original behaviour before options were added EShReflectionStrictArraySuffix = (1 << 0), // reflection will follow stricter rules for array-of-structs suffixes EShReflectionBasicArraySuffix = (1 << 1), // arrays of basic types will be appended with [0] as in GL reflection + EShReflectionIntermediateIO = (1 << 2), // reflect inputs and outputs to program, even with no vertex shader } EShReflectionOptions; // @@ -737,8 +738,10 @@ public: const TObjectReflection& getUniform(int index) const; int getNumUniformBlocks() const; const TObjectReflection& getUniformBlock(int index) const; - int getNumAttributes() const; - const TObjectReflection& getAttribute(int index) const; + int getNumPipeInputs() const; + const TObjectReflection& getPipeInput(int index) const; + int getNumPipeOutputs() const; + const TObjectReflection& getPipeOutput(int index) const; // Legacy Reflection Interface - expressed in terms of above interface int getNumLiveUniformVariables() const // can be used for glGetProgramiv(GL_ACTIVE_UNIFORMS) @@ -753,7 +756,7 @@ public: int getNumLiveAttributes() const // can be used for glGetProgramiv(GL_ACTIVE_ATTRIBUTES) { - return getNumAttributes(); + return getNumPipeInputs(); } int getUniformIndex(const char* name) const // can be used for glGetUniformIndices() @@ -828,17 +831,17 @@ public: const char* getAttributeName(int index) const // can be used for glGetActiveAttrib() { - return getAttribute(index).name.c_str(); + return getPipeInput(index).name.c_str(); } int getAttributeType(int index) const // can be used for glGetActiveAttrib() { - return getAttribute(index).glDefineType; + return getPipeInput(index).glDefineType; } const TType* getAttributeTType(int index) const // returns a TType* { - return getAttribute(index).getType(); + return getPipeInput(index).getType(); } void dumpReflection();