Reflect pipeline outputs as well as inputs, optionally from other stages
authorbaldurk <baldurk@baldurk.org>
Tue, 29 Jan 2019 16:04:44 +0000 (16:04 +0000)
committerbaldurk <baldurk@baldurk.org>
Mon, 4 Feb 2019 12:02:59 +0000 (12:02 +0000)
* We add an option to reflect inputs from other stages than vertex, if only a
  later subset of the stages is linked into the program.

17 files changed:
StandAlone/StandAlone.cpp
Test/baseResults/hlsl.automap.frag.out
Test/baseResults/hlsl.reflection.binding.frag.out
Test/baseResults/hlsl.reflection.vert.out
Test/baseResults/hlsl.shift.per-set.frag.out
Test/baseResults/reflection.frag.out [new file with mode: 0644]
Test/baseResults/reflection.options.frag.out [new file with mode: 0644]
Test/baseResults/reflection.options.vert.out
Test/baseResults/reflection.vert.out
Test/reflection.frag [new file with mode: 0644]
Test/reflection.options.vert
Test/reflection.vert
Test/runtests
glslang/MachineIndependent/ShaderLang.cpp
glslang/MachineIndependent/reflection.cpp
glslang/MachineIndependent/reflection.h
glslang/Public/ShaderLang.h

index c9b1ede..7588320 100644 (file)
@@ -528,6 +528,8 @@ void ProcessArguments(std::vector<std::unique_ptr<glslang::TWorkItem>>& 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"
index b9ab49c..7a871a7 100644 (file)
@@ -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
 
index 464ce0f..3c0d134 100644 (file)
@@ -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
 
index 3403bd3..92cb6ab 100644 (file)
@@ -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:
+
index 7c7d383..02c6e4a 100644 (file)
@@ -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 (file)
index 0000000..d3d90a2
--- /dev/null
@@ -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 (file)
index 0000000..d0fe56e
--- /dev/null
@@ -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:
+
index 22177c7..178779a 100644 (file)
@@ -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
+
index 30aa7a3..fd3c8fe 100644 (file)
@@ -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 (file)
index 0000000..9299653
--- /dev/null
@@ -0,0 +1,8 @@
+#version 440 core\r
+\r
+in float inval;\r
+\r
+void main()\r
+{\r
+    float f = inval;\r
+}\r
index c671a62..5a8d219 100644 (file)
@@ -13,6 +13,8 @@ buffer VertexCollection {
     TriangleInfo t[5];\r
 };\r
 \r
+out float outval;\r
+\r
 void main()\r
 {\r
     float f;\r
@@ -20,4 +22,5 @@ void main()
     f += t[gl_InstanceID].v[gl_InstanceID].position[gl_InstanceID];\r
     f += t[gl_InstanceID].v[gl_InstanceID].normal[gl_InstanceID];\r
     TriangleInfo tlocal[5] = t;\r
+    outval = f;\r
 }\r
index 76ec5a3..939e52a 100644 (file)
@@ -174,6 +174,8 @@ buffer VertexCollection {
     TriangleInfo t[5];\r
 };\r
 \r
+out float outval;\r
+\r
 void main()\r
 {\r
     liveFunction1(image_ui2D, sampler_2D, sampler_2DMSArray);\r
@@ -234,4 +236,6 @@ void main()
     f += t[gl_InstanceID].v[gl_InstanceID].position[gl_InstanceID];\r
     f += t[gl_InstanceID].v[gl_InstanceID].normal[gl_InstanceID];\r
     TriangleInfo tlocal[5] = t;\r
+\r
+    outval = f;\r
 }\r
index c01366e..44d3dc3 100755 (executable)
@@ -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
index 3e77292..a32d605 100755 (executable)
@@ -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(); }
 
index ec627a1..1d90fcf 100644 (file)
@@ -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) {
index c4464aa..ccd87f1 100644 (file)
@@ -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];
 };
index 9a1f4bf..9852925 100755 (executable)
@@ -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();