Reflection:
authorJohn Kessenich <cepheus@frii.com>
Fri, 15 Nov 2013 01:34:27 +0000 (01:34 +0000)
committerJohn Kessenich <cepheus@frii.com>
Fri, 15 Nov 2013 01:34:27 +0000 (01:34 +0000)
 - correct block data size
 - handle deep dereference chains (block.member.member.member)
 - more clear interface argument names

(Still TBD: optimizing array size based on biggest used index and handling variable array index in middle of deep dereference chain)

git-svn-id: https://cvs.khronos.org/svn/repos/ogl/trunk/ecosystem/public/sdk/tools/glslang@24072 e7fa87d3-cd2b-0410-9028-fcbf551c1848

Test/baseResults/reflection.vert.out
Test/reflection.vert
Todo.txt
glslang/MachineIndependent/reflection.cpp
glslang/MachineIndependent/reflection.h
glslang/Public/ShaderLang.h

index dd72f3b..f5591ce 100644 (file)
@@ -8,60 +8,39 @@ Linked vertex stage:
 \r
 \r
 Uniform reflection:\r
-0: image_ui2D: offset -1, type 9063, arraySize 1, index -1\r
-1: sampler_2D: offset -1, type 8b5e, arraySize 1, index -1\r
-2: sampler_2DMSArray: offset -1, type 910b, arraySize 1, index -1\r
-3: anonMember3: offset 80, type 8b52, arraySize 1, index 0\r
-4: s.a: offset -1, type 1404, arraySize 1, index -1\r
-5: ablock.scalar: offset 12, type 1404, arraySize 1, index 1\r
-6: m23: offset 16, type 8b67, arraySize 1, index 0\r
-7: scalarAfterm23: offset 48, type 1404, arraySize 1, index 0\r
-8: c_m23: offset 16, type 8b67, arraySize 1, index 2\r
-9: c_scalarAfterm23: offset 64, type 1404, arraySize 1, index 2\r
-10: scalarBeforeArray: offset 96, type 1404, arraySize 1, index 0\r
-11: floatArray: offset 112, type 1406, arraySize 5, index 0\r
-12: scalarAfterArray: offset 192, type 1404, arraySize 1, index 0\r
-13: ablock.memvec2: offset 48, type 8b50, arraySize 1, index 1\r
-14: ablock.memf1: offset 56, type 1406, arraySize 1, index 1\r
-15: ablock.memf2: offset 60, type 8b56, arraySize 1, index 1\r
-16: ablock.memf3: offset 64, type 1404, arraySize 1, index 1\r
-17: ablock.memvec2a: offset 72, type 8b50, arraySize 1, index 1\r
-18: anonMember1: offset 0, type 8b51, arraySize 1, index 0\r
-19: uf1: offset -1, type 1406, arraySize 1, index -1\r
-20: uf2: offset -1, type 1406, arraySize 1, index -1\r
-21: ablock.member3: offset 32, type 8b52, arraySize 1, index 1\r
+image_ui2D: offset -1, type 9063, size 1, index -1\r
+sampler_2D: offset -1, type 8b5e, size 1, index -1\r
+sampler_2DMSArray: offset -1, type 910b, size 1, index -1\r
+anonMember3: offset 80, type 8b52, size 1, index 0\r
+s.a: offset -1, type 1404, size 1, index -1\r
+ablock.scalar: offset 12, type 1404, size 1, index 1\r
+m23: offset 16, type 8b67, size 1, index 0\r
+scalarAfterm23: offset 48, type 1404, size 1, index 0\r
+c_m23: offset 16, type 8b67, size 1, index 2\r
+c_scalarAfterm23: offset 64, type 1404, size 1, index 2\r
+scalarBeforeArray: offset 96, type 1404, size 1, index 0\r
+floatArray: offset 112, type 1406, size 5, index 0\r
+scalarAfterArray: offset 192, type 1404, size 1, index 0\r
+ablock.memvec2: offset 48, type 8b50, size 1, index 1\r
+ablock.memf1: offset 56, type 1406, size 1, index 1\r
+ablock.memf2: offset 60, type 8b56, size 1, index 1\r
+ablock.memf3: offset 64, type 1404, size 1, index 1\r
+ablock.memvec2a: offset 72, type 8b50, size 1, index 1\r
+ablock.m22: offset 80, type 8b5a, size 7, index 1\r
+dm22: offset -1, type 8b5a, size 10, index -1\r
+m22: offset 208, type 8b5a, size 9, index 0\r
+nest.foo.n1.a: offset 0, type 1406, size 1, index 3\r
+nest.foo.n2.b: offset 16, type 1406, size 1, index 3\r
+nest.foo.n2.c: offset 20, type 1406, size 1, index 3\r
+nest.foo.n2.d: offset 24, type 1406, size 1, index 3\r
+anonMember1: offset 0, type 8b51, size 1, index 0\r
+uf1: offset -1, type 1406, size 1, index -1\r
+uf2: offset -1, type 1406, size 1, index -1\r
+ablock.member3: offset 32, type 8b52, size 1, index 1\r
 \r
 Uniform block reflection:\r
-0: nameless: offset -1, type ffffffff, arraySize 1, index -1\r
-1: ablock: offset -1, type ffffffff, arraySize 1, index -1\r
-2: c_nameless: offset -1, type ffffffff, arraySize 1, index -1\r
-\r
-Live names\r
-ablock: 1\r
-ablock.member3: 21\r
-ablock.memf1: 14\r
-ablock.memf2: 15\r
-ablock.memf3: 16\r
-ablock.memvec2: 13\r
-ablock.memvec2a: 17\r
-ablock.scalar: 5\r
-anonMember1: 18\r
-anonMember3: 3\r
-c_m23: 8\r
-c_nameless: 2\r
-c_scalarAfterm23: 9\r
-floatArray: 11\r
-image_ui2D: 0\r
-liveFunction1(uI21;s21;sA21;: -1\r
-liveFunction2(: -1\r
-m23: 6\r
-nameless: 0\r
-s.a: 4\r
-sampler_2D: 1\r
-sampler_2DMSArray: 2\r
-scalarAfterArray: 12\r
-scalarAfterm23: 7\r
-scalarBeforeArray: 10\r
-uf1: 19\r
-uf2: 20\r
+nameless: offset -1, type ffffffff, size 496, index -1\r
+ablock: offset -1, type ffffffff, size 304, index -1\r
+c_nameless: offset -1, type ffffffff, size 112, index -1\r
+nest: offset -1, type ffffffff, size 28, index -1\r
 \r
index 14f68ad..cd2625f 100644 (file)
@@ -9,6 +9,7 @@ layout(std140, row_major) uniform nameless {
     int scalarBeforeArray;\r
     float floatArray[5];\r
     int scalarAfterArray;\r
+    mat2x2 m22[9];\r
 };\r
 \r
 layout(std140, column_major) uniform c_nameless {\r
@@ -29,6 +30,7 @@ layout(std140) uniform named {
     bool  memf2;\r
     int   memf3;\r
     vec2 memvec2a;\r
+    mat2x2 m22[7];\r
 } ablock;\r
 \r
 layout(std140) uniform namelessdead {\r
@@ -39,6 +41,25 @@ layout(std140) uniform namedDead {
     int b;\r
 } bblock;\r
 \r
+struct N1 {\r
+    float a;\r
+};\r
+\r
+struct N2 {\r
+    float b;\r
+    float c;\r
+    float d;\r
+};\r
+\r
+struct N3 {\r
+    N1 n1;\r
+    N2 n2;\r
+};\r
+\r
+layout(std140) uniform nested {\r
+    N3 foo;\r
+} nest;\r
+\r
 struct TS {\r
     int a;\r
     int dead;\r
@@ -55,6 +76,8 @@ uniform uimage2D image_ui2D;
 uniform sampler2D sampler_2D;\r
 uniform sampler2DMSArray sampler_2DMSArray;\r
 \r
+uniform mat2 dm22[10];\r
+\r
 const bool control = true;\r
 \r
 void deadFunction()\r
@@ -87,7 +110,7 @@ void main()
         deadFunction();\r
 \r
     float f;\r
-\r
+    int i;\r
     if (control) {\r
         liveFunction2();\r
         f = anonMember3.z;\r
@@ -103,6 +126,10 @@ void main()
         f += float(ablock.memf2);\r
         f += ablock.memf3;\r
         f += ablock.memvec2a.y;\r
+        f += ablock.m22[i][1][0];\r
+        f += dm22[3][0][1];\r
+        f += m22[2][1].y;\r
+        f += nest.foo.n1.a + nest.foo.n2.b + nest.foo.n2.c + nest.foo.n2.d;\r
     } else\r
         f = ufDead3;\r
 }\r
index 7aff9fe..3438316 100644 (file)
--- a/Todo.txt
+++ b/Todo.txt
@@ -20,6 +20,7 @@ Link Validation
       - 4.3: Allow mismatches in interpolation and auxiliary qualification across stages.
          - 4.4: A stage contains two different blocks, each with no instance name, where the blocks contain a member with the same name.
     Intra-stage linking, single shader
+      + recursion for functions
       - limits checking:
           - number of texture image units
           - texel offsets (or compile-time?)
@@ -34,28 +35,27 @@ Link Validation
       + Non ES: geometry shader input array sizes and input layout qualifier declaration
       - Non ES: read or write to both gl_ClipVertex and gl_ClipDistance
       - Non ES: write to only one of gl_FragColor, gl_FragData, or user-declared
+      + 1.50: match between all explicit input array sizes and input primitive
       - 1.50: at least one geometry shader says input primitive and at least one says output primitive...
       - 1.50: at least one geometry shader says max_vertices...
       - 1.50: geometry shaders: max_vertices must be checked against gl_MaxGeometryOutputVertices (maybe at compile time)
       + 1.50: origin_upper_left and pixel_center_integer have to match
+      - Even the potential for recursion through subroutine uniforms is an error.
          - 4.4: An interface contains two different blocks, each with no instance name, where the blocks contain a member with the same name.
          - 4.4: component aliasing (except desktop vertex shader inputs)
-    Intra-stage linking, multiple shader
-      + Non ES: type consistency check of uniforms, globals, ins, and outs
-      + Non ES: value checking of global const initializers
-      + Non ES: value checking of uniform initializers
-      + Non ES: location match
-      + recursion for functions
-      - Non ES: block matching
-      - Non ES: component/binding/index/offset match check
-      - Non ES: compute shader layout(local_size_*) matching
+      - 4.4: overlapping transform/feedback offsets, offset/stride overflow checks, and stride matching
+    Intra-stage linking, multiple shader (Non-ES)
+      + type consistency check of uniforms, globals, ins, and outs
+      + value checking of global const initializers
+      + value checking of uniform initializers
+      + location match
+      - block matching
+      - component/binding/index/offset match check
+      - compute shader layout(local_size_*) matching
       + mixed es/non-es profiles are an error
-      - Non ES: Even the potential for recursion through subroutine uniforms is an error.
-      - Non ES: matching redeclarations of interface blocks
-      - 1.50: match between all explicit input array sizes and input primitive
+      - matching redeclarations of interface blocks
       - 4.3: early_fragment_tests contradictions
       - 4.3: implicit array sizing is cross shader within a stage
-      - 4.4: overlapping transform/feedback offsets, offset/stride overflow checks, and stride matching
          - 4.4: If gl_FragCoord is redeclared in any fragment shader in a program, it must be redeclared in all the fragment shaders in that program that have a static use gl_FragCoord
 
 Shader Functionality to Implement/Finish
index 4c902f5..27cd53d 100644 (file)
@@ -144,7 +144,7 @@ public:
         if (type.getBasicType() == EbtStruct) {\r
             const TTypeList& memberList = *type.getStruct();\r
 \r
-            int size = 0;\r
+            size = 0;\r
             int maxAlignment = std140 ? baseAlignmentVec4Std140 : 0;\r
             for (size_t m = 0; m < memberList.size(); ++m) {\r
                 int memberSize;\r
@@ -203,7 +203,7 @@ public:
     // block offset rules.\r
     int getBlockMemberOffset(const TType& blockType, int index)\r
     {\r
-        // TODO: reflection performance: cache these results instead of recomputing them\r
+        // TODO: reflection performance: cache intermediate results instead of recomputing them\r
 \r
         int offset = 0;\r
         const TTypeList& memberList = *blockType.getStruct();        \r
@@ -219,55 +219,99 @@ public:
         return offset;\r
     }\r
 \r
+    // Calculate the block data size.\r
+    // Arrayness is not taken into account, each element is backed by a separate buffer.\r
+    int getBlockSize(const TType& blockType)\r
+    {\r
+        int size = 0;\r
+        const TTypeList& memberList = *blockType.getStruct();        \r
+        int memberSize;\r
+        for (size_t m = 0; m < memberList.size(); ++m) {\r
+            int memberAlignment = getBaseAlignment(*memberList[m].type, memberSize, blockType.getQualifier().layoutPacking == ElpStd140);\r
+            align(size, memberAlignment);\r
+            size += memberSize;\r
+        }\r
+\r
+        return size;\r
+    }\r
+\r
     // Add a complex uniform reference where blocks/struct/arrays are involved in the access.\r
-    void addDereferencedUniform(TIntermSymbol* base, TIntermBinary* node)\r
+    // Handles the situation where the left node is at too coarse a granularity to be a single \r
+    // uniform, while the result of the operation at 'node' is at the right granuarity.\r
+    //\r
+    // Note: Simpler things like the following are already handled elsewhere:\r
+    //  - a simple non-array, non-struct variable\r
+    //  - a variable that's an array of non-struct\r
+    //\r
+    // So, this code is for cases like \r
+    //   - a struct/block holding a member (member is array or not)\r
+    //   - an array of struct\r
+    //   - structs/arrays containing the above\r
+    //\r
+    void addDereferencedUniform(TIntermSymbol* base, TIntermBinary* topNode)\r
     {\r
-        bool block = base->getBasicType() == EbtBlock;\r
         int offset = -1;\r
         int blockIndex = -1;\r
         bool anonymous = false;\r
+\r
+        // See if we need to record the block itself\r
+        bool block = base->getBasicType() == EbtBlock;\r
         if (block) {\r
+            // TODO: how is an array of blocks handled differently?\r
             anonymous = base->getName().compare(0, 6, "__anon") == 0;\r
             const TString& blockName = anonymous ? base->getType().getTypeName() : base->getName();\r
             TReflection::TNameToIndex::const_iterator it = reflection.nameToIndex.find(blockName);\r
             if (it == reflection.nameToIndex.end()) {\r
                 blockIndex = reflection.indexToUniformBlock.size();\r
                 reflection.nameToIndex[blockName] = blockIndex;\r
-                reflection.indexToUniformBlock.push_back(TObjectReflection(blockName, -1,  -1, 1, -1));\r
+                reflection.indexToUniformBlock.push_back(TObjectReflection(blockName, offset,  -1, getBlockSize(base->getType()), -1));\r
             } else\r
                 blockIndex = it->second;\r
         }\r
-        TString name;\r
 \r
-        switch (node->getOp()) {\r
-        case EOpIndexDirect:\r
-        case EOpIndexIndirect:\r
-            // TODO: reflection: handle array dereferences\r
-            //name = base->getName();\r
-            //name.append("[]");\r
-            break;\r
-        case EOpIndexDirectStruct:\r
-        {\r
-            if (! anonymous) {\r
-                name = base->getName();\r
-                name.append(".");\r
+        // Process the dereference chain, backward, accumulating the pieces on a stack\r
+        if (block)\r
+            offset = 0;\r
+        std::list<TString> derefs;\r
+        for (TIntermBinary* visitNode = topNode; visitNode; visitNode = visitNode->getLeft()->getAsBinaryNode()) {\r
+            int index;\r
+            switch (visitNode->getOp()) {\r
+            case EOpIndexIndirect:\r
+                // TODO handle indirect references in mid-chain: enumerate all possibilities?\r
+                derefs.push_back(TString("[") + String(0) + "]");\r
+                break;\r
+            case EOpIndexDirect:\r
+                // TODO: reflection: track the highest used index for an array, to reduce the array's size\r
+                index = visitNode->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst();\r
+                derefs.push_back(TString("[") + String(index) + "]");\r
+                break;\r
+            case EOpIndexDirectStruct:\r
+                index = visitNode->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst();\r
+                if (block)\r
+                    offset += getBlockMemberOffset(visitNode->getLeft()->getType(), index);\r
+                derefs.push_back(TString(""));\r
+                if (visitNode->getLeft()->getAsSymbolNode() != base || ! anonymous)\r
+                    derefs.back().append(".");\r
+                derefs.back().append((*visitNode->getLeft()->getType().getStruct())[index].type->getFieldName().c_str());\r
+                break;\r
+            default:\r
+                break;\r
             }\r
-            int structIndex = node->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst();\r
-            if (block)\r
-                offset = getBlockMemberOffset(base->getType(), structIndex);\r
-            name.append((*base->getType().getStruct())[structIndex].type->getFieldName().c_str());\r
-            break;\r
-        }\r
-        default:\r
-            break;\r
         }\r
 \r
-        // TODO: reflection: handle deeper dereference chains than just one dereference\r
+        // Put the dereference chain together, forward (reversing the stack)\r
+        TString name;\r
+        if (! anonymous)\r
+            name = base->getName();\r
+        while (! derefs.empty()) {\r
+            name += derefs.back();\r
+            derefs.pop_back();\r
+        }\r
 \r
         if (name.size() > 0) {\r
             if (reflection.nameToIndex.find(name) == reflection.nameToIndex.end()) {\r
                 reflection.nameToIndex[name] = reflection.indexToUniform.size();                        \r
-                reflection.indexToUniform.push_back(TObjectReflection(name, offset, mapToGlType(node->getType()), mapToGlArraySize(node->getType()), blockIndex));\r
+                reflection.indexToUniform.push_back(TObjectReflection(name, offset, mapToGlType(topNode->getType()), mapToGlArraySize(topNode->getType()), blockIndex));\r
             }\r
         }\r
     }\r
@@ -585,7 +629,8 @@ bool LiveBinary(bool /* preVisit */, TIntermBinary* node, TIntermTraverser* it)
         // this operation, and pick it up when the left side is visited.\r
         if (! oit->isReflectionGranularity(node->getLeft()->getType()) &&\r
             oit->isReflectionGranularity(node->getType())) {            \r
-            // right granularity; see if this really is a uniform-based dereference\r
+            // right granularity; see if this really is a uniform-based dereference,\r
+            // and if so, process it\r
             TIntermSymbol* base = oit->findBase(node);\r
             if (base && base->getQualifier().storage == EvqUniform)\r
                 oit->addDereferencedUniform(base, node);\r
@@ -660,23 +705,19 @@ bool TReflection::addStage(EShLanguage, const TIntermediate& intermediate)
 void TReflection::dump()\r
 {\r
     printf("Uniform reflection:\n");\r
-    for (size_t i = 0; i < indexToUniform.size(); ++i) {\r
-        printf("%d: ", (int)i);\r
+    for (size_t i = 0; i < indexToUniform.size(); ++i)\r
         indexToUniform[i].dump();\r
-    }\r
     printf("\n");\r
 \r
     printf("Uniform block reflection:\n");\r
-    for (size_t i = 0; i < indexToUniformBlock.size(); ++i) {\r
-        printf("%d: ", (int)i);\r
+    for (size_t i = 0; i < indexToUniformBlock.size(); ++i)\r
         indexToUniformBlock[i].dump();\r
-    }\r
     printf("\n");\r
 \r
-    printf("Live names\n");\r
-    for (TNameToIndex::const_iterator it = nameToIndex.begin(); it != nameToIndex.end(); ++it)\r
-        printf("%s: %d\n", it->first.c_str(), it->second);\r
-    printf("\n");\r
+    //printf("Live names\n");\r
+    //for (TNameToIndex::const_iterator it = nameToIndex.begin(); it != nameToIndex.end(); ++it)\r
+    //    printf("%s: %d\n", it->first.c_str(), it->second);\r
+    //printf("\n");\r
 }\r
 \r
 } // end namespace glslang\r
index 1b9a92a..430524f 100644 (file)
@@ -56,7 +56,7 @@ class TObjectReflection {
 public:\r
     TObjectReflection(const TString& pName, int pOffset, int pGLDefineType, int pSize, int pIndex) : \r
         name(pName), offset(pOffset), glDefineType(pGLDefineType), size(pSize), index(pIndex) { }\r
-    void dump() const { printf("%s: offset %d, type %x, arraySize %d, index %d\n", name.c_str(), offset, glDefineType, size, index); }\r
+    void dump() const { printf("%s: offset %d, type %x, size %d, index %d\n", name.c_str(), offset, glDefineType, size, index); }\r
     TString name;\r
     int offset;\r
     int glDefineType;\r
index 9202647..d452614 100644 (file)
@@ -328,17 +328,17 @@ public:
     const char* getInfoDebugLog();
 
     // Reflection Interface
-    bool buildReflection();                     // call first, to do liveness analysis, index mapping, etc.; returns false on failure
-    int getNumLiveUniformVariables();           // can be used for glGetProgramiv(GL_ACTIVE_UNIFORMS)
-    int getNumLiveUniformBlocks();              // can be used for glGetProgramiv(GL_ACTIVE_UNIFORM_BLOCKS)
-    const char* getUniformName(int index);      // can be used for "name" part of glGetActiveUniform()
-    const char* getUniformBlockName(int index); // can be used for glGetActiveUniformBlockName()
-    int getUniformBlockSize(int index);         // can be used for glGetActiveUniformBlockiv(UNIFORM_BLOCK_DATA_SIZE)
-    int getUniformIndex(const char* name);      // can be used for glGetUniformIndices()
-    int getUniformBlockIndex(int index);        // can be used for glGetActiveUniformsiv(GL_UNIFORM_BLOCK_INDEX)
-    int getUniformType(int index);              // can be used for glGetActiveUniformsiv(GL_UNIFORM_TYPE)
-    int getUniformBufferOffset(int index);      // can be used for glGetActiveUniformsiv(GL_UNIFORM_OFFSET)
-    int getUniformArraySize(int index);         // can be used for glGetActiveUniformsiv(GL_UNIFORM_SIZE)
+    bool buildReflection();                          // call first, to do liveness analysis, index mapping, etc.; returns false on failure
+    int getNumLiveUniformVariables();                // can be used for glGetProgramiv(GL_ACTIVE_UNIFORMS)
+    int getNumLiveUniformBlocks();                   // can be used for glGetProgramiv(GL_ACTIVE_UNIFORM_BLOCKS)
+    const char* getUniformName(int index);           // can be used for "name" part of glGetActiveUniform()
+    const char* getUniformBlockName(int blockIndex); // can be used for glGetActiveUniformBlockName()
+    int getUniformBlockSize(int blockIndex);         // can be used for glGetActiveUniformBlockiv(UNIFORM_BLOCK_DATA_SIZE)
+    int getUniformIndex(const char* name);           // can be used for glGetUniformIndices()
+    int getUniformBlockIndex(int index);             // can be used for glGetActiveUniformsiv(GL_UNIFORM_BLOCK_INDEX)
+    int getUniformType(int index);                   // can be used for glGetActiveUniformsiv(GL_UNIFORM_TYPE)
+    int getUniformBufferOffset(int index);           // can be used for glGetActiveUniformsiv(GL_UNIFORM_OFFSET)
+    int getUniformArraySize(int index);              // can be used for glGetActiveUniformsiv(GL_UNIFORM_SIZE)
     void dumpReflection();
 
 protected: