Add option to unwrap I/O block aggregates in reflection
authorbaldurk <baldurk@baldurk.org>
Mon, 11 Feb 2019 11:50:24 +0000 (11:50 +0000)
committerbaldurk <baldurk@baldurk.org>
Mon, 11 Feb 2019 11:53:52 +0000 (11:53 +0000)
* We follow similar rules to uniform block exploding.

StandAlone/StandAlone.cpp
Test/baseResults/reflection.linked.options.out
Test/baseResults/reflection.options.frag.out
Test/baseResults/reflection.options.geom.out [new file with mode: 0644]
Test/baseResults/reflection.options.vert.out
Test/reflection.options.geom [new file with mode: 0644]
Test/reflection.options.vert
Test/runtests
glslang/MachineIndependent/reflection.cpp
glslang/Public/ShaderLang.h

index 740ec7b..bb37a84 100644 (file)
@@ -534,6 +534,8 @@ void ProcessArguments(std::vector<std::unique_ptr<glslang::TWorkItem>>& workItem
                         ReflectOptions |= EShReflectionSeparateBuffers;
                     } else if (lowerword == "reflect-all-block-variables") {
                         ReflectOptions |= EShReflectionAllBlockVariables;
+                    } else if (lowerword == "reflect-unwrap-io-blocks") {
+                        ReflectOptions |= EShReflectionUnwrapIOBlocks;
                     } else if (lowerword == "resource-set-bindings" ||  // synonyms
                                lowerword == "resource-set-binding"  ||
                                lowerword == "rsb") {
@@ -1538,6 +1540,8 @@ void usage()
            "                                    separately to uniforms\n"
            "  --reflect-all-block-variables     reflect all variables in blocks, whether\n"
            "                                    inactive or active\n"
+           "  --reflect-unwrap-io-blocks        unwrap input/output blocks the same as\n"
+           "                                    uniform blocks\n"
            "  --resource-set-binding [stage] name set binding\n"
            "                                    set descriptor set and binding for\n"
            "                                    individual resources\n"
index b121626..af07bbd 100644 (file)
@@ -14,8 +14,8 @@ Buffer variable reflection:
 Buffer block reflection:
 
 Pipeline input reflection:
-vertin: offset 0, type 1406, size 0, index 0, binding -1, stages 1
+vertin: offset 0, type 1406, size 1, index 0, binding -1, stages 1
 
 Pipeline output reflection:
-fragout: offset 0, type 1406, size 0, index 0, binding -1, stages 16
+fragout: offset 0, type 1406, size 1, index 0, binding -1, stages 16
 
index f3ea4ca..ec12392 100644 (file)
@@ -8,7 +8,7 @@ Buffer variable reflection:
 Buffer block reflection:
 
 Pipeline input reflection:
-inval: offset 0, type 1406, size 0, index 0, binding -1, stages 16
+inval: offset 0, type 1406, size 1, index 0, binding -1, stages 16
 
 Pipeline output reflection:
 
diff --git a/Test/baseResults/reflection.options.geom.out b/Test/baseResults/reflection.options.geom.out
new file mode 100644 (file)
index 0000000..7ffe82d
--- /dev/null
@@ -0,0 +1,25 @@
+reflection.options.geom
+Uniform reflection:
+
+Uniform block reflection:
+
+Buffer variable reflection:
+
+Buffer block reflection:
+
+Pipeline input reflection:
+gl_PerVertex.gl_Position: offset 0, type 8b52, size 1, index 0, binding -1, stages 8
+gl_PerVertex.gl_PointSize: offset 0, type 1406, size 1, index 0, binding -1, stages 8
+gl_PerVertex.gl_ClipDistance[0]: offset 0, type 1406, size 1, index 0, binding -1, stages 8
+block.Color: offset 0, type 8b50, size 1, index 0, binding -1, stages 8
+block.Texcoord: offset 0, type 8b50, size 1, index 0, binding -1, stages 8
+block.in_a: offset 0, type 8b54, size 1, index 0, binding -1, stages 8
+
+Pipeline output reflection:
+gl_Position: offset 0, type 8b52, size 1, index 0, binding -1, stages 8
+gl_PointSize: offset 0, type 1406, size 1, index 0, binding -1, stages 8
+gl_ClipDistance[0]: offset 0, type 1406, size 1, index 0, binding -1, stages 8
+block.Color: offset 0, type 8b52, size 1, index 0, binding -1, stages 8
+block.a: offset 0, type 8b52, size 1, index 0, binding -1, stages 8
+block.b[0]: offset 0, type 8b50, size 3, index 0, binding -1, stages 8
+
index 0a3b118..45a7a87 100644 (file)
@@ -33,8 +33,11 @@ VertexCollection: offset -1, type ffffffff, size 400, index -1, binding -1, stag
 MultipleArrays: offset -1, type ffffffff, size 500, index -1, binding -1, stages 1, numMembers 9
 
 Pipeline input reflection:
-gl_InstanceID: offset 0, type 1404, size 0, index 0, binding -1, stages 1
+gl_InstanceID: offset 0, type 1404, size 1, index 0, binding -1, stages 1
 
 Pipeline output reflection:
-outval: offset 0, type 1406, size 0, index 0, binding -1, stages 1
+outval.val: offset 0, type 1406, size 1, index 0, binding -1, stages 1
+outval.a: offset 0, type 8b51, size 1, index 0, binding -1, stages 1
+outval.b[0]: offset 0, type 8b50, size 4, index 0, binding -1, stages 1
+outval.c: offset 0, type 8b5a, size 1, index 0, binding -1, stages 1
 
diff --git a/Test/reflection.options.geom b/Test/reflection.options.geom
new file mode 100644 (file)
index 0000000..76ba3c0
--- /dev/null
@@ -0,0 +1,31 @@
+#version 330 core\r
+\r
+precision highp float;\r
+\r
+layout(triangles) in;\r
+layout(triangle_strip, max_vertices = 4) out;\r
+\r
+in block\r
+{\r
+    vec2 Color;\r
+    vec2 Texcoord;\r
+    flat ivec3 in_a;\r
+} In[];\r
+\r
+out block\r
+{\r
+    vec4 Color;\r
+    vec4 a;\r
+    vec2 b[3];\r
+} Out;\r
+\r
+void main()\r
+{\r
+    for(int i = 0; i < gl_in.length(); ++i)\r
+    {\r
+        gl_Position = gl_in[i].gl_Position;\r
+        Out.Color = vec4(In[i].Color, 0, 1);\r
+        EmitVertex();\r
+    }\r
+    EndPrimitive();\r
+}\r
index b8ecc89..38c82ba 100644 (file)
@@ -26,7 +26,14 @@ uniform UBO {
     uvec4 unused;\r
 } ubo;\r
 \r
-out float outval;\r
+struct OutputStruct {\r
+    float val;\r
+    vec3 a;\r
+    vec2 b[4];\r
+    mat2x2 c;\r
+};\r
+\r
+out OutputStruct outval;\r
 \r
 void main()\r
 {\r
@@ -40,5 +47,5 @@ void main()
     f += ubo.verts[gl_InstanceID].position[0];\r
     f += ubo.flt[gl_InstanceID];\r
     TriangleInfo tlocal[5] = t;\r
-    outval = f;\r
+    outval.val = f;\r
 }\r
index f659da4..cc34306 100755 (executable)
@@ -32,15 +32,17 @@ 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 --reflect-intermediate-io --reflect-separate-buffers --reflect-all-block-variables reflection.options.vert > $TARGETDIR/reflection.options.vert.out
+$EXE -l -q -C --reflect-strict-array-suffix --reflect-basic-array-suffix --reflect-intermediate-io --reflect-separate-buffers --reflect-all-block-variables --reflect-unwrap-io-blocks 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 --reflect-separate-buffers --reflect-all-block-variables reflection.frag > $TARGETDIR/reflection.options.frag.out
+$EXE -l -q -C --reflect-strict-array-suffix --reflect-basic-array-suffix --reflect-intermediate-io --reflect-separate-buffers --reflect-all-block-variables --reflect-unwrap-io-blocks reflection.frag > $TARGETDIR/reflection.options.frag.out
 diff -b $BASEDIR/reflection.options.frag.out $TARGETDIR/reflection.options.frag.out || HASERROR=1
+$EXE -l -q -C --reflect-strict-array-suffix --reflect-basic-array-suffix --reflect-intermediate-io --reflect-separate-buffers --reflect-all-block-variables --reflect-unwrap-io-blocks reflection.options.geom > $TARGETDIR/reflection.options.geom.out
+diff -b $BASEDIR/reflection.options.geom.out $TARGETDIR/reflection.options.geom.out || HASERROR=1
 $EXE -l -q -C reflection.linked.vert reflection.linked.frag > $TARGETDIR/reflection.linked.out
 diff -b $BASEDIR/reflection.linked.out $TARGETDIR/reflection.linked.out || HASERROR=1
-$EXE -l -q -C --reflect-strict-array-suffix --reflect-basic-array-suffix --reflect-intermediate-io --reflect-separate-buffers --reflect-all-block-variables reflection.linked.vert reflection.linked.frag > $TARGETDIR/reflection.linked.options.out
+$EXE -l -q -C --reflect-strict-array-suffix --reflect-basic-array-suffix --reflect-intermediate-io --reflect-separate-buffers --reflect-all-block-variables --reflect-unwrap-io-blocks reflection.linked.vert reflection.linked.frag > $TARGETDIR/reflection.linked.options.out
 diff -b $BASEDIR/reflection.linked.options.out $TARGETDIR/reflection.linked.options.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
index df81879..92fa968 100644 (file)
@@ -98,46 +98,46 @@ public:
         }
     }
 
-    void addPipeInput(const TIntermSymbol& base)
+    void addPipeIOVariable(const TIntermSymbol& base)
     {
         if (processedDerefs.find(&base) == processedDerefs.end()) {
             processedDerefs.insert(&base);
 
             const TString &name = base.getName();
             const TType &type = base.getType();
+            const bool input = base.getQualifier().isPipeInput();
 
-            TReflection::TNameToIndex::const_iterator it = reflection.nameToIndex.find(name.c_str());
-            if (it == reflection.nameToIndex.end()) {
-                reflection.nameToIndex[name.c_str()] = (int)reflection.indexToPipeInput.size();
-                reflection.indexToPipeInput.push_back(TObjectReflection(name.c_str(), type, 0, mapToGlType(type), 0, 0));
+            TReflection::TMapIndexToReflection &ioItems =
+                input ? reflection.indexToPipeInput : reflection.indexToPipeOutput;
 
-                EShLanguageMask& stages = reflection.indexToPipeInput.back().stages;
-                stages = static_cast<EShLanguageMask>(stages | 1 << intermediate.getStage());
-            } else {
-                EShLanguageMask& stages = reflection.indexToPipeInput[it->second].stages;
-                stages = static_cast<EShLanguageMask>(stages | 1 << intermediate.getStage());
-            }
-        }
-    }
+            if (reflection.options & EShReflectionUnwrapIOBlocks) {
+                bool anonymous = IsAnonymous(name);
 
-    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));
+                TString baseName;
+                if (type.getBasicType() == EbtBlock) {
+                    baseName = anonymous ? TString() : type.getTypeName();
+                } else {
+                    baseName = anonymous ? TString() : name;
+                }
 
-                EShLanguageMask& stages = reflection.indexToPipeOutput.back().stages;
-                stages = static_cast<EShLanguageMask>(stages | 1 << intermediate.getStage());
+                // by convention if this is an arrayed block we ignore the array in the reflection
+                if (type.isArray()) {
+                    blowUpIOAggregate(input, baseName, TType(type, 0));
+                } else {               
+                    blowUpIOAggregate(input, baseName, type);
+                }
             } else {
-                EShLanguageMask& stages = reflection.indexToPipeOutput[it->second].stages;
-                stages = static_cast<EShLanguageMask>(stages | 1 << intermediate.getStage());
+                TReflection::TNameToIndex::const_iterator it = reflection.nameToIndex.find(name.c_str());
+                if (it == reflection.nameToIndex.end()) {
+                    reflection.nameToIndex[name.c_str()] = (int)ioItems.size();
+                    ioItems.push_back(TObjectReflection(name.c_str(), type, 0, mapToGlType(type), 0, 0));
+
+                    EShLanguageMask& stages = ioItems.back().stages;
+                    stages = static_cast<EShLanguageMask>(stages | 1 << intermediate.getStage());
+                } else {
+                    EShLanguageMask& stages = ioItems[it->second].stages;
+                    stages = static_cast<EShLanguageMask>(stages | 1 << intermediate.getStage());
+                }
             }
         }
     }
@@ -473,6 +473,67 @@ public:
             }
         }
     }
+    
+    // similar to blowUpActiveAggregate, but with simpler rules and no dereferences to follow.
+    void blowUpIOAggregate(bool input, const TString &baseName, const TType &type)
+    {
+        TString name = baseName;
+
+        // if the type is still too coarse a granularity, this is still an aggregate to expand, expand it...
+        if (! isReflectionGranularity(type)) {
+            if (type.isArray()) {
+                // Visit all the indices of this array, and for each one,
+                // fully explode the remaining aggregate to dereference
+                for (int i = 0; i < std::max(type.getOuterArraySize(), 1); ++i) {
+                    TString newBaseName = name;
+                    newBaseName.append(TString("[") + String(i) + "]");
+                    TType derefType(type, 0);
+
+                    blowUpIOAggregate(input, newBaseName, derefType);
+                }
+            } else {
+                // Visit all members of this aggregate, and for each one,
+                // fully explode the remaining aggregate to dereference
+                const TTypeList& typeList = *type.getStruct();
+
+                for (int i = 0; i < (int)typeList.size(); ++i) {
+                    TString newBaseName = name;
+                    if (newBaseName.size() > 0)
+                        newBaseName.append(".");
+                    newBaseName.append(typeList[i].type->getFieldName());
+                    TType derefType(type, i);
+
+                    blowUpIOAggregate(input, newBaseName, derefType);
+                }
+            }
+
+            // it was all completed in the recursive calls above
+            return;
+        }
+
+        if ((reflection.options & EShReflectionBasicArraySuffix) && type.isArray()) {
+            name.append(TString("[0]"));
+        }
+
+        TReflection::TMapIndexToReflection &ioItems =
+            input ? reflection.indexToPipeInput : reflection.indexToPipeOutput;
+
+        std::string namespacedName = input ? "in " : "out ";
+        namespacedName += name.c_str();
+
+        TReflection::TNameToIndex::const_iterator it = reflection.nameToIndex.find(namespacedName);
+        if (it == reflection.nameToIndex.end()) {
+            reflection.nameToIndex[namespacedName] = (int)ioItems.size();
+            ioItems.push_back(
+                TObjectReflection(name.c_str(), type, 0, mapToGlType(type), mapToGlArraySize(type), 0));
+
+            EShLanguageMask& stages = ioItems.back().stages;
+            stages = static_cast<EShLanguageMask>(stages | 1 << intermediate.getStage());
+        } else {
+            EShLanguageMask& stages = ioItems[it->second].stages;
+            stages = static_cast<EShLanguageMask>(stages | 1 << intermediate.getStage());
+        }
+    }
 
     // Add a uniform dereference where blocks/struct/arrays are involved in the access.
     // Handles the situation where the left node is at the correct or too coarse a
@@ -1027,11 +1088,9 @@ void TReflectionTraverser::visitSymbol(TIntermSymbol* base)
     if (base->getQualifier().storage == EvqUniform)
         addUniform(*base);
 
-    if (intermediate.getStage() == reflection.firstStage && base->getQualifier().isPipeInput())
-        addPipeInput(*base);
-
-    if (intermediate.getStage() == reflection.lastStage && base->getQualifier().isPipeOutput())
-        addPipeOutput(*base);
+    if ((intermediate.getStage() == reflection.firstStage && base->getQualifier().isPipeInput()) ||
+        (intermediate.getStage() == reflection.lastStage && base->getQualifier().isPipeOutput()))
+        addPipeIOVariable(*base);
 }
 
 //
index b40acef..0c25569 100755 (executable)
@@ -246,6 +246,7 @@ typedef enum {
     EShReflectionIntermediateIO    = (1 << 2), // reflect inputs and outputs to program, even with no vertex shader
     EShReflectionSeparateBuffers   = (1 << 3), // buffer variables and buffer blocks are reflected separately
     EShReflectionAllBlockVariables = (1 << 4), // reflect all variables in blocks, even if they are inactive
+    EShReflectionUnwrapIOBlocks    = (1 << 5), // unwrap input/output blocks the same as with uniform blocks
 } EShReflectionOptions;
 
 //