8. io mapping refine & qualifier member check & resolver expand (#2396)
authorChow <laddoc@outlook.com>
Tue, 3 Nov 2020 20:34:19 +0000 (04:34 +0800)
committerGitHub <noreply@github.com>
Tue, 3 Nov 2020 20:34:19 +0000 (13:34 -0700)
* Code refine and adding missing features

1. Add new level for built in symbols.
2. Fix issues for structure members' qualifiers.
3. Global qualifier fix.
4. IO Mapper refine. Add support for checking with mangle names.

* Additional missing features

* Invariant member. (Only check non-interface).

* Split block nesting level and struct nesting level. To fix issues of checking 'invariant' qualifier.

Current grammar would check block/struct member without its parent class's information.
So we split nesting level, and 'invariant' would only be checked within a struct.

* Format anonymous block names. Refine codes for symbols from all kinds of resouces.

* Fix writeonly check.

* Use LValueBase to find operator.

* Fix random null ptr issue.

* invariant check, stage in io mapping, reference parameter should be used and remove wrong codes introduced with ordering vector.

* Remained: to be fixed with double check link.vk.multiblocksValid

* Fix version error.

invariant

* Revert loc modification.

25 files changed:
SPIRV/GlslangToSpv.cpp
Test/baseResults/300layout.vert.out
Test/baseResults/310.frag.out
Test/baseResults/310.vert.out
Test/baseResults/320.vert.out
Test/baseResults/410.geom.out
Test/baseResults/hlsl.structbuffer.append.fn.frag.out
Test/baseResults/hlsl.structbuffer.fn.frag.out
Test/baseResults/link.vk.multiBlocksValid.0.0.vert.out
Test/baseResults/link.vk.multiBlocksValid.1.0.geom.out
Test/baseResults/spv.specConstant.vert.out
glslang/Include/intermediate.h
glslang/MachineIndependent/IntermTraverse.cpp
glslang/MachineIndependent/ParseContextBase.cpp
glslang/MachineIndependent/ParseHelper.cpp
glslang/MachineIndependent/ParseHelper.h
glslang/MachineIndependent/Scan.cpp
glslang/MachineIndependent/SymbolTable.cpp
glslang/MachineIndependent/SymbolTable.h
glslang/MachineIndependent/glslang.m4
glslang/MachineIndependent/glslang.y
glslang/MachineIndependent/glslang_tab.cpp
glslang/MachineIndependent/iomapper.cpp
glslang/MachineIndependent/iomapper.h
glslang/MachineIndependent/reflection.cpp

index 0b6c62b..7fc5c1e 100644 (file)
@@ -3959,6 +3959,8 @@ void TGlslangToSpvTraverser::decorateStructType(const glslang::TType& type,
     // Name and decorate the non-hidden members
     int offset = -1;
     int locationOffset = 0;  // for use within the members of this struct
+    bool memberLocationInvalid = type.isArrayOfArrays() ||
+        (type.isArray() && (type.getQualifier().isArrayedIo(glslangIntermediate->getStage()) == false));
     for (int i = 0; i < (int)glslangMembers->size(); i++) {
         glslang::TType& glslangMember = *(*glslangMembers)[i].type;
         int member = i;
@@ -4011,7 +4013,7 @@ void TGlslangToSpvTraverser::decorateStructType(const glslang::TType& type,
         // just track whether a member needs to be decorated.
         // Ignore member locations if the container is an array, as that's
         // ill-specified and decisions have been made to not allow this.
-        if (! type.isArray() && memberQualifier.hasLocation())
+        if (!memberLocationInvalid && memberQualifier.hasLocation())
             builder.addMemberDecoration(spvType, member, spv::DecorationLocation, memberQualifier.layoutLocation);
 
         if (qualifier.hasLocation())      // track for upcoming inheritance
index e245cbc..527425b 100644 (file)
@@ -1,6 +1,7 @@
 300layout.vert
 ERROR: 0:7: 'vertex input arrays' : not supported with this profile: es
 ERROR: 0:8: 'in' : cannot be a structure or array 
+ERROR: 0:8: 's' : A structure containing an array is not allowed as input in ES 
 ERROR: 0:8: 'vertex input arrays' : not supported with this profile: es
 ERROR: 0:8: 'location' : overlapping use of location 10
 ERROR: 0:12: 'layout' : cannot specify matrix layout on a variable declaration 
@@ -18,7 +19,7 @@ ERROR: 0:50: 'shared' : not supported for this version or the enabled extensions
 ERROR: 0:50: 'shared' : not supported in this stage: vertex
 ERROR: 0:54: 'layout' : cannot specify packing on a variable declaration 
 ERROR: 0:57: 'location' : overlapping use of location 40
-ERROR: 19 compilation errors.  No code generated.
+ERROR: 20 compilation errors.  No code generated.
 
 
 Shader version: 300
index 9fe93e9..9f73c67 100644 (file)
@@ -40,10 +40,14 @@ ERROR: 0:112: 'out' : cannot be a matrix
 ERROR: 0:114: 'in' : cannot be bool 
 ERROR: 0:115: 'sampler2D' : sampler/image types can only be used in uniform variables or function parameters: ino
 ERROR: 0:117: 'fragment-shader array-of-array input' : not supported with this profile: es
+ERROR: 0:120: 'S' : A structure containing an array is not allowed as input in ES 
 ERROR: 0:120: 'fragment-shader array-of-struct input' : not supported with this profile: es
+ERROR: 0:121: 'S' : A structure containing an array is not allowed as input in ES 
 ERROR: 0:121: 'fragment-shader array-of-struct input' : not supported with this profile: es
 ERROR: 0:123: 'fragment-shader struct input containing an array' : not supported with this profile: es
+ERROR: 0:123: 'SA' : A structure containing an array is not allowed as input in ES 
 ERROR: 0:125: 'fragment-shader struct input containing structure' : not supported with this profile: es
+ERROR: 0:125: 'SS' : A structure containing an struct is not allowed as input in ES 
 ERROR: 0:133: 'output block' : not supported in this stage: fragment
 ERROR: 0:138: '' : cannot nest a structure definition inside a structure or block 
 ERROR: 0:146: 'location' : overlapping use of location 13
@@ -139,7 +143,7 @@ ERROR: 0:461: 'func' : function already has a body
 ERROR: 0:463: 'return' : void function cannot return a value 
 ERROR: 0:472: '=' :  cannot convert from ' temp mediump uint' to ' temp mediump int'
 ERROR: 0:485: '=' :  cannot convert from ' global mediump int' to ' temp mediump uint'
-ERROR: 132 compilation errors.  No code generated.
+ERROR: 136 compilation errors.  No code generated.
 
 
 Shader version: 310
index a910e34..1d2152d 100644 (file)
@@ -21,6 +21,7 @@ ERROR: 0:105: 'location' : overlapping use of location 12
 ERROR: 0:107: 'input block' : not supported in this stage: vertex
 ERROR: 0:109: 'gl_PerVertex' : block redeclaration has extra members 
 ERROR: 0:119: 'gl_PointSize' : member of nameless block was not redeclared 
+ERROR: 0:119: 'assign' :  l-value required "gl_PerVertex" (can't modify void)
 ERROR: 0:119: 'assign' :  cannot convert from ' const float' to ' gl_PointSize highp void PointSize'
 ERROR: 0:122: 'gl_PerVertex' : can only redeclare a built-in block once, and before any use 
 ERROR: 0:127: 'flat/smooth/noperspective' : cannot use interpolation qualifiers on an interface block 
@@ -96,7 +97,7 @@ ERROR: 0:389: 'sample' : Reserved word.
 ERROR: 0:400: 'interpolateAtCentroid' : no matching overloaded function found 
 ERROR: 0:401: 'interpolateAtSample' : no matching overloaded function found 
 ERROR: 0:402: 'interpolateAtOffset' : no matching overloaded function found 
-ERROR: 93 compilation errors.  No code generated.
+ERROR: 94 compilation errors.  No code generated.
 
 
 Shader version: 310
index bf127d4..0313b61 100644 (file)
@@ -4,6 +4,7 @@ ERROR: 0:14: 'location' : overlapping use of location 12
 ERROR: 0:16: 'input block' : not supported in this stage: vertex
 ERROR: 0:18: 'gl_PerVertex' : block redeclaration has extra members 
 ERROR: 0:28: 'gl_PointSize' : member of nameless block was not redeclared 
+ERROR: 0:28: 'assign' :  l-value required "gl_PerVertex" (can't modify void)
 ERROR: 0:28: 'assign' :  cannot convert from ' const float' to ' gl_PointSize highp void PointSize'
 ERROR: 0:31: 'gl_PerVertex' : can only redeclare a built-in block once, and before any use 
 ERROR: 0:36: 'flat/smooth/noperspective' : cannot use interpolation qualifiers on an interface block 
@@ -33,7 +34,7 @@ ERROR: 0:211: '=' :  cannot convert from ' const float' to ' temp highp 3-compon
 ERROR: 0:252: 'interpolateAtCentroid' : no matching overloaded function found 
 ERROR: 0:253: 'interpolateAtSample' : no matching overloaded function found 
 ERROR: 0:254: 'interpolateAtOffset' : no matching overloaded function found 
-ERROR: 34 compilation errors.  No code generated.
+ERROR: 35 compilation errors.  No code generated.
 
 
 Shader version: 320
index c38ba48..498da5a 100644 (file)
@@ -5,9 +5,10 @@ ERROR: 0:20: 'gl_PerVertex' : can only redeclare a built-in block once, and befo
 ERROR: 0:32: 'gl_Position' : no such field in structure 
 ERROR: 0:32: '=' :  cannot convert from ' temp block{ in float PointSize gl_PointSize}' to ' temp 4-component vector of float'
 ERROR: 0:33: 'gl_Position' : member of nameless block was not redeclared 
+ERROR: 0:33: 'assign' :  l-value required "gl_PerVertex" (can't modify void)
 ERROR: 0:33: 'assign' :  cannot convert from ' const 4-component vector of float' to 'layout( stream=0) gl_Position void Position'
 WARNING: 0:38: 'return' : type conversion on return values was not explicitly allowed until version 420 
-ERROR: 7 compilation errors.  No code generated.
+ERROR: 8 compilation errors.  No code generated.
 
 
 Shader version: 410
index 6a239da..2ac904e 100644 (file)
@@ -190,13 +190,13 @@ Validation failed
                               Decorate 9 BufferBlock
                               Decorate 12 BufferBlock
                               Decorate 49(sbuf_a) DescriptorSet 0
-                              Decorate 49(sbuf_a) Binding 4
+                              Decorate 49(sbuf_a) Binding 0
                               Decorate 50(sbuf_a@count) DescriptorSet 0
-                              Decorate 50(sbuf_a@count) Binding 6
+                              Decorate 50(sbuf_a@count) Binding 0
                               Decorate 51(sbuf_c) DescriptorSet 0
-                              Decorate 51(sbuf_c) Binding 5
+                              Decorate 51(sbuf_c) Binding 1
                               Decorate 52(sbuf_c@count) DescriptorSet 0
-                              Decorate 52(sbuf_c@count) Binding 7
+                              Decorate 52(sbuf_c@count) Binding 0
                               Decorate 58(pos) Flat
                               Decorate 58(pos) Location 0
                               Decorate 61(@entryPointOutput) Location 0
index c5afd35..bd2a4e6 100644 (file)
@@ -191,9 +191,9 @@ Validation failed
                               Decorate 18 BufferBlock
                               Decorate 20 BufferBlock
                               Decorate 47(sbuf2) DescriptorSet 0
-                              Decorate 47(sbuf2) Binding 2
+                              Decorate 47(sbuf2) Binding 0
                               Decorate 48(sbuf2@count) DescriptorSet 0
-                              Decorate 48(sbuf2@count) Binding 3
+                              Decorate 48(sbuf2@count) Binding 0
                               Decorate 50(sbuf) DescriptorSet 0
                               Decorate 50(sbuf) Binding 10
                               Decorate 63(pos) Flat
index ddb856d..bdabab1 100644 (file)
@@ -211,7 +211,7 @@ Shader version: 430
                               Name 70  "BufferBlock"
                               MemberName 70(BufferBlock) 0  "p"
                               Name 72  "uBuf"
-                              Decorate 14(oColor) Location 4
+                              Decorate 14(oColor) Location 2
                               MemberDecorate 16(ColorBlock) 0 Offset 0
                               MemberDecorate 16(ColorBlock) 1 Offset 16
                               MemberDecorate 16(ColorBlock) 2 Offset 32
@@ -224,7 +224,7 @@ Shader version: 430
                               Decorate 28(uColorBuf) DescriptorSet 0
                               Decorate 28(uColorBuf) Binding 0
                               Decorate 32(Vertex) Block
-                              Decorate 34(oV) Location 2
+                              Decorate 34(oV) Location 0
                               MemberDecorate 40(gl_PerVertex) 0 BuiltIn Position
                               MemberDecorate 40(gl_PerVertex) 1 BuiltIn PointSize
                               MemberDecorate 40(gl_PerVertex) 2 BuiltIn ClipDistance
index 63be728..c0b33b7 100644 (file)
@@ -304,7 +304,7 @@ output primitive = triangle_strip
                               MemberName 95(BufferBlock) 0  "p"
                               Name 97  "uBuf"
                               Name 100  "P"
-                              Decorate 18(oColor) Location 2
+                              Decorate 18(oColor) Location 1
                               MemberDecorate 20(ColorBlock) 0 Offset 0
                               MemberDecorate 20(ColorBlock) 1 Offset 16
                               MemberDecorate 20(ColorBlock) 2 Offset 32
@@ -326,16 +326,16 @@ output primitive = triangle_strip
                               Decorate 50(uM) DescriptorSet 0
                               Decorate 50(uM) Binding 0
                               Decorate 59(Vertex) Block
-                              Decorate 61(oV) Location 1
+                              Decorate 61(oV) Location 0
                               Decorate 64(Vertex) Block
-                              Decorate 68(iV) Location 0
+                              Decorate 68(iV) Location 1
                               MemberDecorate 95(BufferBlock) 0 ColMajor
                               MemberDecorate 95(BufferBlock) 0 Offset 0
                               MemberDecorate 95(BufferBlock) 0 MatrixStride 16
                               Decorate 95(BufferBlock) BufferBlock
                               Decorate 97(uBuf) DescriptorSet 0
                               Decorate 97(uBuf) Binding 1
-                              Decorate 100(P) Location 4
+                              Decorate 100(P) Location 0
                2:             TypeVoid
                3:             TypeFunction 2
                6:             TypeFloat 32
index 921cc68..76f3de6 100644 (file)
@@ -11,7 +11,7 @@ spv.specConstant.vert
                               Source GLSL 400
                               Name 4  "main"
                               Name 9  "arraySize"
-                              Name 14  "foo(vf4[s4546];"
+                              Name 14  "foo(vf4[s805310914];"
                               Name 13  "p"
                               Name 17  "builtin_spec_constant("
                               Name 20  "color"
@@ -106,10 +106,10 @@ spv.specConstant.vert
                               Store 20(color) 46
               48:          10 Load 22(ucol)
                               Store 47(param) 48
-              49:           2 FunctionCall 14(foo(vf4[s4546];) 47(param)
+              49:           2 FunctionCall 14(foo(vf4[s805310914];) 47(param)
                               Return
                               FunctionEnd
-14(foo(vf4[s4546];):           2 Function None 12
+14(foo(vf4[s805310914];):           2 Function None 12
            13(p):     11(ptr) FunctionParameter
               15:             Label
               54:     24(ptr) AccessChain 53(dupUcol) 23
index 30cb6fb..9e41a99 100644 (file)
@@ -1282,6 +1282,8 @@ public:
     TIntermTyped* getConstSubtree() const { return constSubtree; }
 #ifndef GLSLANG_WEB
     void setFlattenSubset(int subset) { flattenSubset = subset; }
+    virtual const TString& getAccessName() const;
+
     int getFlattenSubset() const { return flattenSubset; } // -1 means full object
 #endif
 
index f46010b..553b1b5 100644 (file)
@@ -71,6 +71,13 @@ void TIntermConstantUnion::traverse(TIntermTraverser *it)
     it->visitConstantUnion(this);
 }
 
+const TString& TIntermSymbol::getAccessName() const {
+    if (getBasicType() == EbtBlock)
+        return getType().getTypeName();
+    else
+        return getName();
+}
+
 //
 // Traverse a binary node.
 //
index b464009..3efa27a 100644 (file)
@@ -127,22 +127,6 @@ bool TParseContextBase::lValueErrorCheck(const TSourceLoc& loc, const char* op,
 {
     TIntermBinary* binaryNode = node->getAsBinaryNode();
 
-    if (binaryNode) {
-        switch(binaryNode->getOp()) {
-        case EOpIndexDirect:
-        case EOpIndexIndirect:     // fall through
-        case EOpIndexDirectStruct: // fall through
-        case EOpVectorSwizzle:
-        case EOpMatrixSwizzle:
-            return lValueErrorCheck(loc, op, binaryNode->getLeft());
-        default:
-            break;
-        }
-        error(loc, " l-value required", op, "", "");
-
-        return true;
-    }
-
     const char* symbol = nullptr;
     TIntermSymbol* symNode = node->getAsSymbolNode();
     if (symNode != nullptr)
@@ -203,15 +187,40 @@ bool TParseContextBase::lValueErrorCheck(const TSourceLoc& loc, const char* op,
     // Everything else is okay, no error.
     //
     if (message == nullptr)
+    {
+        if (binaryNode) {
+            switch (binaryNode->getOp()) {
+            case EOpIndexDirect:
+            case EOpIndexIndirect:     // fall through
+            case EOpIndexDirectStruct: // fall through
+            case EOpVectorSwizzle:
+            case EOpMatrixSwizzle:
+                return lValueErrorCheck(loc, op, binaryNode->getLeft());
+            default:
+                break;
+            }
+            error(loc, " l-value required", op, "", "");
+
+            return true;
+        }
         return false;
+    }
 
     //
     // If we get here, we have an error and a message.
     //
+    const TIntermTyped* leftMostTypeNode = TIntermediate::findLValueBase(node, true);
+
     if (symNode)
         error(loc, " l-value required", op, "\"%s\" (%s)", symbol, message);
     else
-        error(loc, " l-value required", op, "(%s)", message);
+        if (binaryNode && binaryNode->getAsOperator()->getOp() == EOpIndexDirectStruct)
+            if(IsAnonymous(leftMostTypeNode->getAsSymbolNode()->getName()))
+                error(loc, " l-value required", op, "\"%s\" (%s)", leftMostTypeNode->getAsSymbolNode()->getAccessName().c_str(), message);
+            else
+                error(loc, " l-value required", op, "\"%s\" (%s)", leftMostTypeNode->getAsSymbolNode()->getName().c_str(), message);
+        else
+            error(loc, " l-value required", op, "(%s)", message);
 
     return true;
 }
@@ -219,28 +228,41 @@ bool TParseContextBase::lValueErrorCheck(const TSourceLoc& loc, const char* op,
 // Test for and give an error if the node can't be read from.
 void TParseContextBase::rValueErrorCheck(const TSourceLoc& loc, const char* op, TIntermTyped* node)
 {
+    TIntermBinary* binaryNode = node->getAsBinaryNode();
+    const TIntermSymbol* symNode = node->getAsSymbolNode();
+
     if (! node)
         return;
 
-    TIntermBinary* binaryNode = node->getAsBinaryNode();
-    if (binaryNode) {
-        switch(binaryNode->getOp()) {
-        case EOpIndexDirect:
-        case EOpIndexIndirect:
-        case EOpIndexDirectStruct:
-        case EOpVectorSwizzle:
-        case EOpMatrixSwizzle:
-            rValueErrorCheck(loc, op, binaryNode->getLeft());
-        default:
-            break;
-        }
+    if (node->getQualifier().isWriteOnly()) {
+        const TIntermTyped* leftMostTypeNode = TIntermediate::findLValueBase(node, true);
+
+        if (symNode != nullptr)
+            error(loc, "can't read from writeonly object: ", op, symNode->getName().c_str());
+        else if (binaryNode &&
+                (binaryNode->getAsOperator()->getOp() == EOpIndexDirectStruct ||
+                 binaryNode->getAsOperator()->getOp() == EOpIndexDirect))
+            if(IsAnonymous(leftMostTypeNode->getAsSymbolNode()->getName()))
+                error(loc, "can't read from writeonly object: ", op, leftMostTypeNode->getAsSymbolNode()->getAccessName().c_str());
+            else
+                error(loc, "can't read from writeonly object: ", op, leftMostTypeNode->getAsSymbolNode()->getName().c_str());
+        else
+            error(loc, "can't read from writeonly object: ", op, "");
 
-        return;
+    } else {
+        if (binaryNode) {
+            switch (binaryNode->getOp()) {
+            case EOpIndexDirect:
+            case EOpIndexIndirect:
+            case EOpIndexDirectStruct:
+            case EOpVectorSwizzle:
+            case EOpMatrixSwizzle:
+                rValueErrorCheck(loc, op, binaryNode->getLeft());
+            default:
+                break;
+            }
+        }
     }
-
-    TIntermSymbol* symNode = node->getAsSymbolNode();
-    if (symNode && symNode->getQualifier().isWriteOnly())
-        error(loc, "can't read from writeonly object: ", op, symNode->getName().c_str());
 }
 
 // Add 'symbol' to the list of deferred linkage symbols, which
index b9b37fb..032774f 100644 (file)
@@ -3368,7 +3368,7 @@ void TParseContext::transparentOpaqueCheck(const TSourceLoc& loc, const TType& t
 //
 void TParseContext::memberQualifierCheck(glslang::TPublicType& publicType)
 {
-    globalQualifierFixCheck(publicType.loc, publicType.qualifier);
+    globalQualifierFixCheck(publicType.loc, publicType.qualifier, true);
     checkNoShaderLayouts(publicType.loc, publicType.shaderQualifiers);
     if (publicType.qualifier.isNonUniform()) {
         error(publicType.loc, "not allowed on block or structure members", "nonuniformEXT", "");
@@ -3379,7 +3379,7 @@ void TParseContext::memberQualifierCheck(glslang::TPublicType& publicType)
 //
 // Check/fix just a full qualifier (no variables or types yet, but qualifier is complete) at global level.
 //
-void TParseContext::globalQualifierFixCheck(const TSourceLoc& loc, TQualifier& qualifier)
+void TParseContext::globalQualifierFixCheck(const TSourceLoc& loc, TQualifier& qualifier, bool isMemberCheck)
 {
     bool nonuniformOkay = false;
 
@@ -3404,6 +3404,16 @@ void TParseContext::globalQualifierFixCheck(const TSourceLoc& loc, TQualifier& q
     case EvqTemporary:
         nonuniformOkay = true;
         break;
+    case EvqUniform:
+        // According to GLSL spec: The std430 qualifier is supported only for shader storage blocks; a shader using
+        // the std430 qualifier on a uniform block will fail to compile.
+        // Only check the global declaration: layout(std430) uniform;
+        if (blockName == nullptr &&
+            qualifier.layoutPacking == ElpStd430)
+        {
+            error(loc, "it is invalid to declare std430 qualifier on uniform", "", "");
+        }
+        break;
     default:
         break;
     }
@@ -3411,7 +3421,9 @@ void TParseContext::globalQualifierFixCheck(const TSourceLoc& loc, TQualifier& q
     if (!nonuniformOkay && qualifier.isNonUniform())
         error(loc, "for non-parameter, can only apply to 'in' or no storage qualifier", "nonuniformEXT", "");
 
-    invariantCheck(loc, qualifier);
+    // Storage qualifier isn't ready for memberQualifierCheck, we should skip invariantCheck for it.
+    if (!isMemberCheck || structNestingLevel > 0)
+        invariantCheck(loc, qualifier);
 }
 
 //
@@ -4083,6 +4095,9 @@ void TParseContext::checkRuntimeSizable(const TSourceLoc& loc, const TIntermType
     if (isRuntimeLength(base))
         return;
 
+    if (base.getType().getQualifier().builtIn == EbvSampleMask)
+        return;
+
     // Check for last member of a bufferreference type, which is runtime sizeable
     // but doesn't support runtime length
     if (base.getType().getQualifier().storage == EvqBuffer) {
@@ -4634,14 +4649,14 @@ void TParseContext::paramCheckFix(const TSourceLoc& loc, const TQualifier& quali
 
 void TParseContext::nestedBlockCheck(const TSourceLoc& loc)
 {
-    if (structNestingLevel > 0)
+    if (structNestingLevel > 0 || blockNestingLevel > 0)
         error(loc, "cannot nest a block definition inside a structure or block", "", "");
-    ++structNestingLevel;
+    ++blockNestingLevel;
 }
 
 void TParseContext::nestedStructCheck(const TSourceLoc& loc)
 {
-    if (structNestingLevel > 0)
+    if (structNestingLevel > 0 || blockNestingLevel > 0)
         error(loc, "cannot nest a structure definition inside a structure or block", "", "");
     ++structNestingLevel;
 }
@@ -6537,13 +6552,15 @@ void TParseContext::declareTypeDefaults(const TSourceLoc& loc, const TPublicType
             error(loc, "atomic_uint binding is too large", "binding", "");
             return;
         }
-
-        if(publicType.qualifier.hasOffset()) {
+        if (publicType.qualifier.hasOffset())
             atomicUintOffsets[publicType.qualifier.layoutBinding] = publicType.qualifier.layoutOffset;
-        }
         return;
     }
 
+    if (publicType.arraySizes) {
+        error(loc, "expect an array name", "", "");
+    }
+
     if (publicType.qualifier.hasLayout() && !publicType.qualifier.hasBufferReference())
         warn(loc, "useless application of layout qualifier", "layout", "");
 #endif
@@ -6634,6 +6651,22 @@ TIntermNode* TParseContext::declareVariable(const TSourceLoc& loc, TString& iden
     if (type.getQualifier().storage == EvqShared && type.containsCoopMat())
         error(loc, "qualifier", "Cooperative matrix types must not be used in shared memory", "");
 
+    if (profile == EEsProfile) {
+        if (type.getQualifier().isPipeInput() && type.getBasicType() == EbtStruct) {
+            if (type.getQualifier().isArrayedIo(language)) {
+                TType perVertexType(type, 0);
+                if (perVertexType.containsArray() && perVertexType.containsBuiltIn() == false) {
+                    error(loc, "A per vertex structure containing an array is not allowed as input in ES", type.getTypeName().c_str(), "");
+                }
+            }
+            else if (type.containsArray() && type.containsBuiltIn() == false) {
+                error(loc, "A structure containing an array is not allowed as input in ES", type.getTypeName().c_str(), "");
+            }
+            if (type.containsStructure())
+                error(loc, "A structure containing an struct is not allowed as input in ES", type.getTypeName().c_str(), "");
+        }
+    }
+
     if (identifier != "gl_FragCoord" && (publicType.shaderQualifiers.originUpperLeft || publicType.shaderQualifiers.pixelCenterInteger))
         error(loc, "can only apply origin_upper_left and pixel_center_origin to gl_FragCoord", "layout qualifier", "");
     if (identifier != "gl_FragDepth" && publicType.shaderQualifiers.getDepth() != EldNone)
@@ -6956,6 +6989,15 @@ TIntermTyped* TParseContext::convertInitializerList(const TSourceLoc& loc, const
             error(loc, "wrong vector size (or rows in a matrix column):", "initializer list", type.getCompleteString().c_str());
             return nullptr;
         }
+        TBasicType destType = type.getBasicType();
+        for (int i = 0; i < type.getVectorSize(); ++i) {
+            TBasicType initType = initList->getSequence()[i]->getAsTyped()->getBasicType();
+            if (destType != initType && !intermediate.canImplicitlyPromote(initType, destType)) {
+                error(loc, "type mismatch in initializer list", "initializer list", type.getCompleteString().c_str());
+                return nullptr;
+            }
+
+        }
     } else {
         error(loc, "unexpected initializer-list type:", "initializer list", type.getCompleteString().c_str());
         return nullptr;
@@ -7492,10 +7534,10 @@ void TParseContext::declareBlock(const TSourceLoc& loc, TTypeList& typeList, con
         TType& memberType = *typeList[member].type;
         TQualifier& memberQualifier = memberType.getQualifier();
         const TSourceLoc& memberLoc = typeList[member].loc;
-        globalQualifierFixCheck(memberLoc, memberQualifier);
         if (memberQualifier.storage != EvqTemporary && memberQualifier.storage != EvqGlobal && memberQualifier.storage != currentBlockQualifier.storage)
             error(memberLoc, "member storage qualifier cannot contradict block storage qualifier", memberType.getFieldName().c_str(), "");
         memberQualifier.storage = currentBlockQualifier.storage;
+        globalQualifierFixCheck(memberLoc, memberQualifier);
 #ifndef GLSLANG_WEB
         inheritMemoryQualifiers(currentBlockQualifier, memberQualifier);
         if (currentBlockQualifier.perPrimitiveNV)
@@ -8193,7 +8235,7 @@ void TParseContext::invariantCheck(const TSourceLoc& loc, const TQualifier& qual
 
     bool pipeOut = qualifier.isPipeOutput();
     bool pipeIn = qualifier.isPipeInput();
-    if (version >= 300 || (!isEsProfile() && version >= 420)) {
+    if ((version >= 300 && isEsProfile()) || (!isEsProfile() && version >= 420)) {
         if (! pipeOut)
             error(loc, "can only apply to an output", "invariant", "");
     } else {
index 0f09ada..fe2b6fb 100644 (file)
@@ -83,7 +83,7 @@ public:
           : TParseVersions(interm, version, profile, spvVersion, language, infoSink, forwardCompatible, messages),
             scopeMangler("::"),
             symbolTable(symbolTable),
-            statementNestingLevel(0), loopNestingLevel(0), structNestingLevel(0), controlFlowNestingLevel(0),
+            statementNestingLevel(0), loopNestingLevel(0), structNestingLevel(0), blockNestingLevel(0), controlFlowNestingLevel(0),
             currentFunctionType(nullptr),
             postEntryPointReturn(false),
             contextPragma(true, false),
@@ -178,7 +178,8 @@ public:
     TSymbolTable& symbolTable;        // symbol table that goes with the current language, version, and profile
     int statementNestingLevel;        // 0 if outside all flow control or compound statements
     int loopNestingLevel;             // 0 if outside all loops
-    int structNestingLevel;           // 0 if outside blocks and structures
+    int structNestingLevel;           // 0 if outside structures
+    int blockNestingLevel;            // 0 if outside blocks
     int controlFlowNestingLevel;      // 0 if outside all flow control
     const TType* currentFunctionType; // the return type of the function that's currently being parsed
     bool functionReturnsValue;        // true if a non-void function has a return
@@ -365,7 +366,7 @@ public:
     void accStructCheck(const TSourceLoc & loc, const TType & type, const TString & identifier);
     void transparentOpaqueCheck(const TSourceLoc&, const TType&, const TString& identifier);
     void memberQualifierCheck(glslang::TPublicType&);
-    void globalQualifierFixCheck(const TSourceLoc&, TQualifier&);
+    void globalQualifierFixCheck(const TSourceLoc&, TQualifier&, bool isMemberCheck = false);
     void globalQualifierTypeCheck(const TSourceLoc&, const TQualifier&, const TPublicType&);
     bool structQualifierErrorCheck(const TSourceLoc&, const TPublicType& pType);
     void mergeQualifiers(const TSourceLoc&, TQualifier& dst, const TQualifier& src, bool force);
index bd3181a..3a5f090 100644 (file)
@@ -982,7 +982,7 @@ int TScanContext::tokenizeIdentifier()
         return keyword;
     case PACKED:
         if ((parseContext.isEsProfile() && parseContext.version < 300) ||
-            (!parseContext.isEsProfile() && parseContext.version < 330))
+            (!parseContext.isEsProfile() && parseContext.version < 140))
             return reservedWord();
         return identifierOrType();
 
index 007f22c..e245814 100644 (file)
@@ -146,6 +146,8 @@ void TType::buildMangledName(TString& mangledName) const
         if (typeName)
             mangledName += *typeName;
         for (unsigned int i = 0; i < structure->size(); ++i) {
+            if ((*structure)[i].type->getBasicType() == EbtVoid)
+                continue;
             mangledName += '-';
             (*structure)[i].type->buildMangledName(mangledName);
         }
index ec4bc3c..db16c19 100644 (file)
@@ -613,20 +613,24 @@ public:
     //
 protected:
     static const int globalLevel = 3;
-    bool isSharedLevel(int level)  { return level <= 1; }              // exclude all per-compile levels
-    bool isBuiltInLevel(int level) { return level <= 2; }              // exclude user globals
-    bool isGlobalLevel(int level)  { return level <= globalLevel; }    // include user globals
+    static bool isSharedLevel(int level)  { return level <= 1; }            // exclude all per-compile levels
+    static bool isBuiltInLevel(int level) { return level <= 2; }            // exclude user globals
+    static bool isGlobalLevel(int level)  { return level <= globalLevel; }  // include user globals
 public:
     bool isEmpty() { return table.size() == 0; }
     bool atBuiltInLevel() { return isBuiltInLevel(currentLevel()); }
     bool atGlobalLevel()  { return isGlobalLevel(currentLevel()); }
-
+    static bool isBuiltInSymbol(int uniqueId) {
+        int level = uniqueId >> LevelFlagBitOffset;
+        return isBuiltInLevel(level);
+    }
     void setNoBuiltInRedeclarations() { noBuiltInRedeclarations = true; }
     void setSeparateNameSpaces() { separateNameSpaces = true; }
 
     void push()
     {
         table.push_back(new TSymbolTableLevel);
+        updateUniqueIdLevelFlag();
     }
 
     // Make a new symbol-table level to represent the scope introduced by a structure
@@ -639,6 +643,7 @@ public:
     {
         assert(thisSymbol.getName().size() == 0);
         table.push_back(new TSymbolTableLevel);
+        updateUniqueIdLevelFlag();
         table.back()->setThisLevel();
         insert(thisSymbol);
     }
@@ -648,6 +653,7 @@ public:
         table[currentLevel()]->getPreviousDefaultPrecisions(p);
         delete table.back();
         table.pop_back();
+        updateUniqueIdLevelFlag();
     }
 
     //
@@ -867,12 +873,20 @@ public:
             table[level]->readOnly();
     }
 
+    // Add current level in the high-bits of unique id
+    void updateUniqueIdLevelFlag() {
+        // clamp level to avoid overflow
+        uint32_t level = currentLevel() > 7 ? 7 : currentLevel();
+        uniqueId &= ((1 << LevelFlagBitOffset) - 1);
+        uniqueId |= (level << LevelFlagBitOffset);
+    }
+
 protected:
     TSymbolTable(TSymbolTable&);
     TSymbolTable& operator=(TSymbolTableLevel&);
 
     int currentLevel() const { return static_cast<int>(table.size()) - 1; }
-
+    static const uint32_t LevelFlagBitOffset = 28;
     std::vector<TSymbolTableLevel*> table;
     int uniqueId;     // for unique identification in code generation
     bool noBuiltInRedeclarations;
index 0b4b53f..6bd3759 100644 (file)
@@ -905,7 +905,7 @@ declaration
 
 block_structure
     : type_qualifier IDENTIFIER LEFT_BRACE { parseContext.nestedBlockCheck($1.loc); } struct_declaration_list RIGHT_BRACE {
-        --parseContext.structNestingLevel;
+        --parseContext.blockNestingLevel;
         parseContext.blockName = $2.string;
         parseContext.globalQualifierFixCheck($1.loc, $1.qualifier);
         parseContext.checkNoShaderLayouts($1.loc, $1.shaderQualifiers);
index 23adcb0..f507002 100644 (file)
@@ -905,7 +905,7 @@ declaration
 
 block_structure
     : type_qualifier IDENTIFIER LEFT_BRACE { parseContext.nestedBlockCheck($1.loc); } struct_declaration_list RIGHT_BRACE {
-        --parseContext.structNestingLevel;
+        --parseContext.blockNestingLevel;
         parseContext.blockName = $2.string;
         parseContext.globalQualifierFixCheck($1.loc, $1.qualifier);
         parseContext.checkNoShaderLayouts($1.loc, $1.shaderQualifiers);
index ac35797..0204660 100644 (file)
@@ -5186,7 +5186,7 @@ yyreduce:
   case 104:
 #line 907 "MachineIndependent/glslang.y" /* yacc.c:1646  */
     {
-        --parseContext.structNestingLevel;
+        --parseContext.blockNestingLevel;
         parseContext.blockName = (yyvsp[-4].lex).string;
         parseContext.globalQualifierFixCheck((yyvsp[-5].interm.type).loc, (yyvsp[-5].interm.type).qualifier);
         parseContext.checkNoShaderLayouts((yyvsp[-5].interm.type).loc, (yyvsp[-5].interm.type).shaderQualifiers);
index 905cf65..0e9e717 100644 (file)
 
 #include "../Include/Common.h"
 #include "../Include/InfoSink.h"
+#include "../Include/Types.h"
 
 #include "gl_types.h"
 #include "iomapper.h"
+#include "SymbolTable.h"
 
 //
 // Map IO bindings.
@@ -82,17 +84,17 @@ public:
         // If a global is being visited, then we should also traverse it incase it's evaluation
         // ends up visiting inputs we want to tag as live
         else if (base->getQualifier().storage == EvqGlobal)
-            addGlobalReference(base->getName());
+            addGlobalReference(base->getAccessName());
 
         if (target) {
             TVarEntryInfo ent = {base->getId(), base, ! traverseAll};
             ent.stage = intermediate.getStage();
             TVarLiveMap::iterator at = target->find(
-                ent.symbol->getName()); // std::lower_bound(target->begin(), target->end(), ent, TVarEntryInfo::TOrderById());
+                ent.symbol->getAccessName()); // std::lower_bound(target->begin(), target->end(), ent, TVarEntryInfo::TOrderById());
             if (at != target->end() && at->second.id == ent.id)
                 at->second.live = at->second.live || ! traverseAll; // update live state
             else
-                (*target)[ent.symbol->getName()] = ent;
+                (*target)[ent.symbol->getAccessName()] = ent;
         }
     }
 
@@ -125,7 +127,8 @@ public:
             return;
 
         TVarEntryInfo ent = { base->getId() };
-        TVarLiveMap::const_iterator at = source->find(base->getName());
+        // Fix a defect, when block has no instance name, we need to find its block name
+        TVarLiveMap::const_iterator at = source->find(base->getAccessName());
         if (at == source->end())
             return;
 
@@ -181,7 +184,7 @@ struct TNotifyInOutAdaptor
 
     inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey)
     {
-        resolver.notifyInOut(stage, entKey.second);
+        resolver.notifyInOut(entKey.second.stage, entKey.second);
     }
 
 private:
@@ -189,12 +192,13 @@ private:
 };
 
 struct TResolverUniformAdaptor {
-    TResolverUniformAdaptor(EShLanguage s, TIoMapResolver& r, TInfoSink& i, bool& e)
+    TResolverUniformAdaptor(EShLanguage s, TIoMapResolver& r, TVarLiveMap* uniform[EShLangCount], TInfoSink& i, bool& e)
       : stage(s)
       , resolver(r)
       , infoSink(i)
       , error(e)
     {
+        memcpy(uniformVarMap, uniform, EShLangCount * (sizeof(TVarLiveMap*)));
     }
 
     inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey) {
@@ -206,9 +210,9 @@ struct TResolverUniformAdaptor {
         ent.newIndex = -1;
         const bool isValid = resolver.validateBinding(stage, ent);
         if (isValid) {
-            resolver.resolveBinding(stage, ent);
-            resolver.resolveSet(stage, ent);
-            resolver.resolveUniformLocation(stage, ent);
+            resolver.resolveBinding(ent.stage, ent);
+            resolver.resolveSet(ent.stage, ent);
+            resolver.resolveUniformLocation(ent.stage, ent);
 
             if (ent.newBinding != -1) {
                 if (ent.newBinding >= int(TQualifier::layoutBindingEnd)) {
@@ -217,6 +221,17 @@ struct TResolverUniformAdaptor {
                     infoSink.info.message(EPrefixInternalError, err.c_str());
                     error = true;
                 }
+
+                if (ent.symbol->getQualifier().hasBinding()) {
+                    for (uint32_t idx = EShLangVertex; idx < EShLangCount; ++idx) {
+                        if (idx == ent.stage || uniformVarMap[idx] == nullptr)
+                            continue;
+                        auto entKey2 = uniformVarMap[idx]->find(entKey.first);
+                        if (entKey2 != uniformVarMap[idx]->end()) {
+                            entKey2->second.newBinding = ent.newBinding;
+                        }
+                    }
+                }
             }
             if (ent.newSet != -1) {
                 if (ent.newSet >= int(TQualifier::layoutSetEnd)) {
@@ -225,6 +240,16 @@ struct TResolverUniformAdaptor {
                     infoSink.info.message(EPrefixInternalError, err.c_str());
                     error = true;
                 }
+                if (ent.symbol->getQualifier().hasSet()) {
+                    for (uint32_t idx = EShLangVertex; idx < EShLangCount; ++idx) {
+                        if ((idx == stage) || (uniformVarMap[idx] == nullptr))
+                            continue;
+                        auto entKey2 = uniformVarMap[idx]->find(entKey.first);
+                        if (entKey2 != uniformVarMap[idx]->end()) {
+                            entKey2->second.newSet = ent.newSet;
+                        }
+                    }
+                }
             }
         } else {
             TString errorMsg = "Invalid binding: " + entKey.first;
@@ -239,7 +264,7 @@ struct TResolverUniformAdaptor {
     TIoMapResolver& resolver;
     TInfoSink&      infoSink;
     bool&           error;
-
+    TVarLiveMap*    uniformVarMap[EShLangCount];
 private:
     TResolverUniformAdaptor& operator=(TResolverUniformAdaptor&) = delete;
 };
@@ -261,7 +286,7 @@ struct TResolverInOutAdaptor {
         ent.newBinding = -1;
         ent.newSet = -1;
         ent.newIndex = -1;
-        const bool isValid = resolver.validateInOut(stage, ent);
+        const bool isValid = resolver.validateInOut(ent.stage, ent);
         if (isValid) {
             resolver.resolveInOutLocation(stage, ent);
             resolver.resolveInOutComponent(stage, ent);
@@ -296,17 +321,116 @@ private:
 struct TSymbolValidater
 {
     TSymbolValidater(TIoMapResolver& r, TInfoSink& i, TVarLiveMap* in[EShLangCount], TVarLiveMap* out[EShLangCount],
-                     TVarLiveMap* uniform[EShLangCount], bool& hadError)
+                     TVarLiveMap* uniform[EShLangCount], bool& hadError, EProfile profile, int version)
         : preStage(EShLangCount)
         , currentStage(EShLangCount)
         , nextStage(EShLangCount)
         , resolver(r)
         , infoSink(i)
         , hadError(hadError)
+        , profile(profile)
+        , version(version)
     {
         memcpy(inVarMaps, in, EShLangCount * (sizeof(TVarLiveMap*)));
         memcpy(outVarMaps, out, EShLangCount * (sizeof(TVarLiveMap*)));
         memcpy(uniformVarMap, uniform, EShLangCount * (sizeof(TVarLiveMap*)));
+
+        std::map<TString, TString> anonymousMemberMap;
+        std::vector<TRange> usedUniformLocation;
+        std::vector<TString> usedUniformName;
+        usedUniformLocation.clear();
+        usedUniformName.clear();
+        for (int i = 0; i < EShLangCount; i++) {
+            if (uniformVarMap[i]) {
+                for (auto uniformVar : *uniformVarMap[i])
+                {
+                    TIntermSymbol* pSymbol = uniformVar.second.symbol;
+                    TQualifier qualifier = uniformVar.second.symbol->getQualifier();
+                    TString symbolName = pSymbol->getAccessName();
+
+                    // All the uniform needs multi-stage location check (block/default)
+                    int uniformLocation = qualifier.layoutLocation;
+
+                    if (uniformLocation != TQualifier::layoutLocationEnd) {
+                        // Total size of current uniform, could be block, struct or other types.
+                        int size = TIntermediate::computeTypeUniformLocationSize(pSymbol->getType());
+
+                        TRange locationRange(uniformLocation, uniformLocation + size - 1);
+
+                        // Combine location and component ranges
+                        int overlapLocation = -1;
+                        bool diffLocation = false;
+
+                        // Check for collisions, except for vertex inputs on desktop targeting OpenGL
+                        overlapLocation = checkLocationOverlap(locationRange, usedUniformLocation, symbolName, usedUniformName, diffLocation);
+
+                        // Overlap locations of uniforms, regardless of components (multi stages)
+                        if (overlapLocation == -1) {
+                            usedUniformLocation.push_back(locationRange);
+                            usedUniformName.push_back(symbolName);
+                        }
+                        else if (overlapLocation >= 0) {
+                            if (diffLocation == true) {
+                                TString err = "Uniform location should be equal for same uniforms: " + overlapLocation;
+                                infoSink.info.message(EPrefixInternalError, err.c_str());
+                                hadError = true;
+                                break;
+                            }
+                            else {
+                                TString err = "Uniform location overlaps across stages: " + overlapLocation;
+                                infoSink.info.message(EPrefixInternalError, err.c_str());
+                                hadError = true;
+                                break;
+                            }
+                        }
+                    }
+
+                    if ((uniformVar.second.symbol->getBasicType() == EbtBlock) &&
+                        IsAnonymous(uniformVar.second.symbol->getName()))
+                    {
+                        auto blockType = uniformVar.second.symbol->getType().getStruct();
+                        for (size_t memberIdx = 0; memberIdx < blockType->size(); ++memberIdx) {
+                            auto memberName = (*blockType)[memberIdx].type->getFieldName();
+                            if (anonymousMemberMap.find(memberName) != anonymousMemberMap.end())
+                            {
+                                if (anonymousMemberMap[memberName] != uniformVar.second.symbol->getType().getTypeName())
+                                {
+                                    TString err = "Invalid block member name: " + memberName;
+                                    infoSink.info.message(EPrefixInternalError, err.c_str());
+                                    hadError = true;
+                                    break;
+                                }
+                            }
+                            else
+                            {
+                                anonymousMemberMap[memberName] = uniformVar.second.symbol->getType().getTypeName();
+                            }
+                        }
+                    }
+                    if (hadError)
+                        break;
+                }
+            }
+        }
+    }
+
+    // In case we need to new an intermediate, which costs too much
+    int checkLocationOverlap(const TRange& locationRange, std::vector<TRange>& usedUniformLocation, const TString symbolName, std::vector<TString>& usedUniformName, bool& diffLocation)
+    {
+        for (size_t r = 0; r < usedUniformLocation.size(); ++r) {
+            if (usedUniformName[r] == symbolName) {
+                diffLocation = true;
+                return (usedUniformLocation[r].start == locationRange.start &&
+                        usedUniformLocation[r].last == locationRange.last)
+                       ? -2 : std::max(locationRange.start, usedUniformLocation[r].start);
+            }
+            if (locationRange.overlap(usedUniformLocation[r])) {
+                // there is a collision; pick one
+                return std::max(locationRange.start, usedUniformLocation[r].start);
+            }
+        }
+
+        return -1; // no collision
     }
 
     inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey) {
@@ -339,11 +463,24 @@ struct TSymbolValidater
             // validate stage in;
             if (preStage == EShLangCount)
                 return;
-            if (name == "gl_PerVertex")
+            if (TSymbolTable::isBuiltInSymbol(base->getId()))
                 return;
             if (outVarMaps[preStage] != nullptr) {
                 auto ent2 = outVarMaps[preStage]->find(name);
+                uint32_t location = base->getType().getQualifier().layoutLocation;
+                if (ent2 == outVarMaps[preStage]->end() &&
+                    location != glslang::TQualifier::layoutLocationEnd) {
+                    for (auto var = outVarMaps[preStage]->begin(); var != ent2; var++) {
+                        if (var->second.symbol->getType().getQualifier().layoutLocation == location) {
+                            ent2 = var;
+                            break;
+                        }
+                    }
+                }
                 if (ent2 != outVarMaps[preStage]->end()) {
+                    auto& type1 = base->getType();
+                    auto& type2 = ent2->second.symbol->getType();
+                    hadError = hadError || typeCheck(&type1, &type2, name.c_str(), false);
                     if (ent2->second.symbol->getType().getQualifier().isArrayedIo(preStage)) {
                         TType subType(ent2->second.symbol->getType(), 0);
                         subType.appendMangledName(mangleName2);
@@ -351,23 +488,49 @@ struct TSymbolValidater
                     else {
                         ent2->second.symbol->getType().appendMangledName(mangleName2);
                     }
-                    if (mangleName1 == mangleName2)
+
+                    if (mangleName1 == mangleName2) {
+                        // For ES 3.0 only, other versions have no such restrictions
+                        // According to ES 3.0 spec: The type and presence of the interpolation qualifiers and
+                        // storage qualifiers  of variables with the same name declared in all linked shaders must
+                        // match, otherwise the link command will fail.
+                        if (profile == EEsProfile && version == 300) {
+                            // Don't need to check smooth qualifier, as it uses the default interpolation mode
+                            if (ent1.stage == EShLangFragment && type1.isBuiltIn() == false) {
+                                if (type1.getQualifier().flat != type2.getQualifier().flat ||
+                                    type1.getQualifier().nopersp != type2.getQualifier().nopersp) {
+                                    TString err = "Interpolation qualifier mismatch : " + entKey.first;
+                                    infoSink.info.message(EPrefixInternalError, err.c_str());
+                                    hadError = true;
+                                }
+                            }
+                        }
                         return;
+                    }
                     else {
                         TString err = "Invalid In/Out variable type : " + entKey.first;
                         infoSink.info.message(EPrefixInternalError, err.c_str());
                         hadError = true;
                     }
                 }
+                else if (!base->getType().isBuiltIn()) {
+                    // According to spec: A link error is generated if any statically referenced input variable
+                    // or block does not have a matching output
+                    if (profile == EEsProfile && ent1.live) {
+                        hadError = true;
+                        TString errorStr = name + ": not been declare as a output variable in pre shader stage.";
+                        infoSink.info.message(EPrefixError, errorStr.c_str());
+                    }
+                }
                 return;
             }
         } else if (base->getQualifier().storage == EvqVaryingOut) {
             // validate stage out;
             if (nextStage == EShLangCount)
                 return;
-            if (name == "gl_PerVertex")
+            if (TSymbolTable::isBuiltInSymbol(base->getId()))
                 return;
-            if (outVarMaps[nextStage] != nullptr) {
+            if (inVarMaps[nextStage] != nullptr) {
                 auto ent2 = inVarMaps[nextStage]->find(name);
                 if (ent2 != inVarMaps[nextStage]->end()) {
                     if (ent2->second.symbol->getType().getQualifier().isArrayedIo(nextStage)) {
@@ -400,11 +563,50 @@ struct TSymbolValidater
                             hadError = true;
                         }
                         mangleName2.clear();
+
+                        // validate instance name of blocks
+                        if (hadError == false &&
+                            base->getType().getBasicType() == EbtBlock &&
+                            IsAnonymous(base->getName()) != IsAnonymous(ent2->second.symbol->getName())) {
+                            TString err = "Matched uniform block names must also either all be lacking "
+                                          "an instance name or all having an instance name: " + entKey.first;
+                            infoSink.info.message(EPrefixInternalError, err.c_str());
+                            hadError = true;
+                        }
+
+                        // validate uniform block member qualifier and member names
+                        auto& type1 = base->getType();
+                        auto& type2 = ent2->second.symbol->getType();
+                        if (hadError == false && base->getType().getBasicType() == EbtBlock) {
+                            hadError = hadError || typeCheck(&type1, &type2, name.c_str(), true);
+                        }
+                        else {
+                            hadError = hadError || typeCheck(&type1, &type2, name.c_str(), false);
+                        }
+                    }
+                    else if (base->getBasicType() == EbtBlock)
+                    {
+                        if (IsAnonymous(base->getName()))
+                        {
+                            // The name of anonymous block member can't same with default uniform variable.
+                            auto blockType1 = base->getType().getStruct();
+                            for (size_t memberIdx = 0; memberIdx < blockType1->size(); ++memberIdx) {
+                                auto memberName = (*blockType1)[memberIdx].type->getFieldName();
+                                if (uniformVarMap[i]->find(memberName) != uniformVarMap[i]->end())
+                                {
+                                    TString err = "Invalid Uniform variable name : " + memberName;
+                                    infoSink.info.message(EPrefixInternalError, err.c_str());
+                                    hadError = true;
+                                    break;
+                                }
+                            }
+                        }
                     }
                 }
             }
         }
     }
+
     TVarLiveMap *inVarMaps[EShLangCount], *outVarMaps[EShLangCount], *uniformVarMap[EShLangCount];
     // Use for mark pre stage, to get more interface symbol information.
     EShLanguage preStage, currentStage, nextStage;
@@ -412,9 +614,118 @@ struct TSymbolValidater
     TIoMapResolver& resolver;
     TInfoSink& infoSink;
     bool& hadError;
+    EProfile profile;
+    int version;
 
 private:
     TSymbolValidater& operator=(TSymbolValidater&) = delete;
+
+    bool qualifierCheck(const TType* const type1, const TType* const type2, const std::string& name, bool isBlock)
+    {
+        bool hasError = false;
+        const TQualifier& qualifier1 = type1->getQualifier();
+        const TQualifier& qualifier2 = type2->getQualifier();
+
+        if (isBlock == false &&
+            (type1->getQualifier().storage == EvqUniform && type2->getQualifier().storage == EvqUniform) ||
+            (type1->getQualifier().storage == EvqGlobal && type2->getQualifier().storage == EvqGlobal)) {
+            if (qualifier1.precision != qualifier2.precision) {
+                hasError = true;
+                std::string errorStr = name + ": have precision conflict cross stage.";
+                infoSink.info.message(EPrefixError, errorStr.c_str());
+            }
+            if (qualifier1.hasFormat() && qualifier2.hasFormat()) {
+                if (qualifier1.layoutFormat != qualifier2.layoutFormat) {
+                    hasError = true;
+                    std::string errorStr = name + ": have layout format conflict cross stage.";
+                    infoSink.info.message(EPrefixError, errorStr.c_str());
+                }
+
+            }
+        }
+
+        if (isBlock == true) {
+            if (qualifier1.layoutPacking != qualifier2.layoutPacking) {
+                hasError = true;
+                std::string errorStr = name + ": have layoutPacking conflict cross stage.";
+                infoSink.info.message(EPrefixError, errorStr.c_str());
+            }
+            if (qualifier1.layoutMatrix != qualifier2.layoutMatrix) {
+                hasError = true;
+                std::string errorStr = name + ": have layoutMatrix conflict cross stage.";
+                infoSink.info.message(EPrefixError, errorStr.c_str());
+            }
+            if (qualifier1.layoutOffset != qualifier2.layoutOffset) {
+                hasError = true;
+                std::string errorStr = name + ": have layoutOffset conflict cross stage.";
+                infoSink.info.message(EPrefixError, errorStr.c_str());
+            }
+            if (qualifier1.layoutAlign != qualifier2.layoutAlign) {
+                hasError = true;
+                std::string errorStr = name + ": have layoutAlign conflict cross stage.";
+                infoSink.info.message(EPrefixError, errorStr.c_str());
+            }
+        }
+
+        return hasError;
+    }
+
+    bool typeCheck(const TType* const type1, const TType* const type2, const std::string& name, bool isBlock)
+    {
+        bool hasError = false;
+        if (!(type1->isStruct() && type2->isStruct())) {
+            hasError = hasError || qualifierCheck(type1, type2, name, isBlock);
+        }
+        else {
+            if (type1->getBasicType() == EbtBlock && type2->getBasicType() == EbtBlock)
+                isBlock = true;
+            const TTypeList* typeList1 = type1->getStruct();
+            const TTypeList* typeList2 = type2->getStruct();
+
+            std::string newName = name;
+            size_t memberCount = typeList1->size();
+            size_t index2 = 0;
+            for (size_t index = 0; index < memberCount; index++, index2++) {
+                // Skip inactive member
+                if (typeList1->at(index).type->getBasicType() == EbtVoid)
+                    continue;
+                while (index2 < typeList2->size() && typeList2->at(index2).type->getBasicType() == EbtVoid) {
+                    ++index2;
+                }
+
+                // TypeList1 has more members in list
+                if (index2 == typeList2->size()) {
+                    std::string errorStr = name + ": struct mismatch.";
+                    infoSink.info.message(EPrefixError, errorStr.c_str());
+                    hasError = true;
+                    break;
+                }
+
+                if (typeList1->at(index).type->getFieldName() != typeList2->at(index2).type->getFieldName()) {
+                    std::string errorStr = name + ": member name mismatch.";
+                    infoSink.info.message(EPrefixError, errorStr.c_str());
+                    hasError = true;
+                }
+                else {
+                    newName = typeList1->at(index).type->getFieldName().c_str();
+                }
+                hasError = hasError || typeCheck(typeList1->at(index).type, typeList2->at(index2).type, newName, isBlock);
+            }
+
+            while (index2 < typeList2->size())
+            {
+                // TypeList2 has more members
+                if (typeList2->at(index2).type->getBasicType() != EbtVoid) {
+                    std::string errorStr = name + ": struct mismatch.";
+                    infoSink.info.message(EPrefixError, errorStr.c_str());
+                    hasError = true;
+                    break;
+                }
+                ++index2;
+            }
+        }
+        return hasError;
+    }
 };
 
 struct TSlotCollector {
@@ -500,7 +811,7 @@ int TDefaultIoResolverBase::resolveSet(EShLanguage /*stage*/, TVarEntryInfo& ent
 
 int TDefaultIoResolverBase::resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) {
     const TType& type = ent.symbol->getType();
-    const char* name = ent.symbol->getName().c_str();
+    const char* name =  ent.symbol->getAccessName().c_str();
     // kick out of not doing this
     if (! doAutoLocationMapping()) {
         return ent.newLocation = -1;
@@ -609,7 +920,7 @@ TDefaultGlslIoResolver::TDefaultGlslIoResolver(const TIntermediate& intermediate
 
 int TDefaultGlslIoResolver::resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) {
     const TType& type = ent.symbol->getType();
-    const TString& name = getAccessName(ent.symbol);
+    const TString& name = ent.symbol->getAccessName();
     if (currentStage != stage) {
         preStage = currentStage;
         currentStage = stage;
@@ -693,7 +1004,7 @@ int TDefaultGlslIoResolver::resolveInOutLocation(EShLanguage stage, TVarEntryInf
 
 int TDefaultGlslIoResolver::resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) {
     const TType& type = ent.symbol->getType();
-    const TString& name = getAccessName(ent.symbol);
+    const TString& name = ent.symbol->getAccessName();
     // kick out of not doing this
     if (! doAutoLocationMapping()) {
         return ent.newLocation = -1;
@@ -764,7 +1075,7 @@ int TDefaultGlslIoResolver::resolveUniformLocation(EShLanguage /*stage*/, TVarEn
 
 int TDefaultGlslIoResolver::resolveBinding(EShLanguage /*stage*/, TVarEntryInfo& ent) {
     const TType& type = ent.symbol->getType();
-    const TString& name = getAccessName(ent.symbol);
+    const TString& name = ent.symbol->getAccessName();
     // On OpenGL arrays of opaque types take a separate binding for each element
     int numBindings = intermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1;
     TResourceType resource = getResourceType(type);
@@ -839,7 +1150,7 @@ void TDefaultGlslIoResolver::endCollect(EShLanguage /*stage*/) {
 
 void TDefaultGlslIoResolver::reserverStorageSlot(TVarEntryInfo& ent, TInfoSink& infoSink) {
     const TType& type = ent.symbol->getType();
-    const TString& name = getAccessName(ent.symbol);
+    const TString& name = ent.symbol->getAccessName();
     TStorageQualifier storage = type.getQualifier().storage;
     EShLanguage stage(EShLangCount);
     switch (storage) {
@@ -899,7 +1210,7 @@ void TDefaultGlslIoResolver::reserverStorageSlot(TVarEntryInfo& ent, TInfoSink&
 
 void TDefaultGlslIoResolver::reserverResourceSlot(TVarEntryInfo& ent, TInfoSink& infoSink) {
     const TType& type = ent.symbol->getType();
-    const TString& name = getAccessName(ent.symbol);
+    const TString& name = ent.symbol->getAccessName();
     int resource = getResourceType(type);
     if (type.getQualifier().hasBinding()) {
         TVarSlotMap& varSlotMap = resourceSlotMap[resource];
@@ -922,13 +1233,6 @@ void TDefaultGlslIoResolver::reserverResourceSlot(TVarEntryInfo& ent, TInfoSink&
     }
 }
 
-const TString& TDefaultGlslIoResolver::getAccessName(const TIntermSymbol* symbol)
-{
-    return symbol->getBasicType() == EbtBlock ?
-        symbol->getType().getTypeName() :
-        symbol->getName();
-}
-
 //TDefaultGlslIoResolver end
 
 /*
@@ -1117,25 +1421,23 @@ bool TIoMapper::addStage(EShLanguage stage, TIntermediate& intermediate, TInfoSi
     }
 
     // sort entries by priority. see TVarEntryInfo::TOrderByPriority for info.
-    std::for_each(inVarMap.begin(), inVarMap.end(),
-                  [&inVector](TVarLivePair p) { inVector.push_back(p); });
+    for (auto& var : inVarMap) { inVector.push_back(var); }
     std::sort(inVector.begin(), inVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
         return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
     });
-    std::for_each(outVarMap.begin(), outVarMap.end(),
-                  [&outVector](TVarLivePair p) { outVector.push_back(p); });
+    for (auto& var : outVarMap) { outVector.push_back(var); }
     std::sort(outVector.begin(), outVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
         return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
     });
-    std::for_each(uniformVarMap.begin(), uniformVarMap.end(),
-                  [&uniformVector](TVarLivePair p) { uniformVector.push_back(p); });
+    for (auto& var : uniformVarMap) { uniformVector.push_back(var); }
     std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
         return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
     });
     bool hadError = false;
+    TVarLiveMap* dummyUniformVarMap[EShLangCount] = {};
     TNotifyInOutAdaptor inOutNotify(stage, *resolver);
     TNotifyUniformAdaptor uniformNotify(stage, *resolver);
-    TResolverUniformAdaptor uniformResolve(stage, *resolver, infoSink, hadError);
+    TResolverUniformAdaptor uniformResolve(stage, *resolver, dummyUniformVarMap, infoSink, hadError);
     TResolverInOutAdaptor inOutResolve(stage, *resolver, infoSink, hadError);
     resolver->beginNotifications(stage);
     std::for_each(inVector.begin(), inVector.end(), inOutNotify);
@@ -1143,22 +1445,22 @@ bool TIoMapper::addStage(EShLanguage stage, TIntermediate& intermediate, TInfoSi
     std::for_each(uniformVector.begin(), uniformVector.end(), uniformNotify);
     resolver->endNotifications(stage);
     resolver->beginResolve(stage);
-    std::for_each(inVector.begin(), inVector.end(), inOutResolve);
+    for (auto& var : inVector) { inOutResolve(var); }
     std::for_each(inVector.begin(), inVector.end(), [&inVarMap](TVarLivePair p) {
-        auto at = inVarMap.find(p.second.symbol->getName());
-        if (at != inVarMap.end())
+        auto at = inVarMap.find(p.second.symbol->getAccessName());
+        if (at != inVarMap.end() && p.second.id == at->second.id)
             at->second = p.second;
     });
-    std::for_each(outVector.begin(), outVector.end(), inOutResolve);
+    for (auto& var : outVector) { inOutResolve(var); }
     std::for_each(outVector.begin(), outVector.end(), [&outVarMap](TVarLivePair p) {
-        auto at = outVarMap.find(p.second.symbol->getName());
-        if (at != outVarMap.end())
+        auto at = outVarMap.find(p.second.symbol->getAccessName());
+        if (at != outVarMap.end() && p.second.id == at->second.id)
             at->second = p.second;
     });
     std::for_each(uniformVector.begin(), uniformVector.end(), uniformResolve);
     std::for_each(uniformVector.begin(), uniformVector.end(), [&uniformVarMap](TVarLivePair p) {
-        auto at = uniformVarMap.find(p.second.symbol->getName());
-        if (at != uniformVarMap.end())
+        auto at = uniformVarMap.find(p.second.symbol->getAccessName());
+        if (at != uniformVarMap.end() && p.second.id == at->second.id)
             at->second = p.second;
     });
     resolver->endResolve(stage);
@@ -1174,9 +1476,14 @@ bool TIoMapper::addStage(EShLanguage stage, TIntermediate& intermediate, TInfoSi
 //
 // Returns false if the input is too malformed to do this.
 bool TGlslIoMapper::addStage(EShLanguage stage, TIntermediate& intermediate, TInfoSink& infoSink, TIoMapResolver* resolver) {
+    bool somethingToDo = !intermediate.getResourceSetBinding().empty() ||
+        intermediate.getAutoMapBindings() ||
+        intermediate.getAutoMapLocations();
+
+    // Profile and version are use for symbol validate.
+    profile = intermediate.getProfile();
+    version = intermediate.getVersion();
 
-    bool somethingToDo = ! intermediate.getResourceSetBinding().empty() || intermediate.getAutoMapBindings() ||
-                         intermediate.getAutoMapLocations();
     // Restrict the stricter condition to further check 'somethingToDo' only if 'somethingToDo' has not been set, reduce
     // unnecessary or insignificant for-loop operation after 'somethingToDo' have been true.
     for (int res = 0; (res < EResCount && !somethingToDo); ++res) {
@@ -1236,31 +1543,30 @@ bool TGlslIoMapper::doMap(TIoMapResolver* resolver, TInfoSink& infoSink) {
     resolver->endResolve(EShLangCount);
     if (!hadError) {
         //Resolve uniform location, ubo/ssbo/opaque bindings across stages
-        TResolverUniformAdaptor uniformResolve(EShLangCount, *resolver, infoSink, hadError);
+        TResolverUniformAdaptor uniformResolve(EShLangCount, *resolver, uniformVarMap, infoSink, hadError);
         TResolverInOutAdaptor inOutResolve(EShLangCount, *resolver, infoSink, hadError);
-        TSymbolValidater symbolValidater(*resolver, infoSink, inVarMaps, outVarMaps, uniformVarMap, hadError);
+        TSymbolValidater symbolValidater(*resolver, infoSink, inVarMaps,
+                                         outVarMaps, uniformVarMap, hadError, profile, version);
         TVarLiveVector uniformVector;
         resolver->beginResolve(EShLangCount);
         for (int stage = EShLangVertex; stage < EShLangCount; stage++) {
             if (inVarMaps[stage] != nullptr) {
                 inOutResolve.setStage(EShLanguage(stage));
-                std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(), symbolValidater);
-                std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(), inOutResolve);
-                std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(), symbolValidater);
-                std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(), inOutResolve);
+                for (auto& var : *(inVarMaps[stage])) { symbolValidater(var); }
+                for (auto& var : *(inVarMaps[stage])) { inOutResolve(var); }
+                for (auto& var : *(outVarMaps[stage])) { symbolValidater(var); }
+                for (auto& var : *(outVarMaps[stage])) { inOutResolve(var); }
             }
             if (uniformVarMap[stage] != nullptr) {
                 uniformResolve.setStage(EShLanguage(stage));
-                // sort entries by priority. see TVarEntryInfo::TOrderByPriority for info.
-                std::for_each(uniformVarMap[stage]->begin(), uniformVarMap[stage]->end(),
-                              [&uniformVector](TVarLivePair p) { uniformVector.push_back(p); });
+                for (auto& var : *(uniformVarMap[stage])) { uniformVector.push_back(var); }
             }
         }
         std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
             return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
         });
-        std::for_each(uniformVector.begin(), uniformVector.end(), symbolValidater);
-        std::for_each(uniformVector.begin(), uniformVector.end(), uniformResolve);
+        for (auto& var : uniformVector) { symbolValidater(var); }
+        for (auto& var : uniformVector) { uniformResolve(var); }
         std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
             return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
         });
@@ -1269,14 +1575,18 @@ bool TGlslIoMapper::doMap(TIoMapResolver* resolver, TInfoSink& infoSink) {
             if (intermediates[stage] != nullptr) {
                 // traverse each stage, set new location to each input/output and unifom symbol, set new binding to
                 // ubo, ssbo and opaque symbols
-                TVarLiveMap** pUniformVarMap = uniformVarMap;
+                TVarLiveMap** pUniformVarMap = uniformResolve.uniformVarMap;
                 std::for_each(uniformVector.begin(), uniformVector.end(), [pUniformVarMap, stage](TVarLivePair p) {
-                    auto at = pUniformVarMap[stage]->find(p.second.symbol->getName());
-                    if (at != pUniformVarMap[stage]->end())
+                    auto at = pUniformVarMap[stage]->find(p.second.symbol->getAccessName());
+                    if (at != pUniformVarMap[stage]->end() && at->second.id == p.second.id){
+                        int resolvedBinding = at->second.newBinding;
                         at->second = p.second;
+                        if (resolvedBinding > 0)
+                            at->second.newBinding = resolvedBinding;
+                    }
                 });
                 TVarSetTraverser iter_iomap(*intermediates[stage], *inVarMaps[stage], *outVarMaps[stage],
-                                            *uniformVarMap[stage]);
+                                            *uniformResolve.uniformVarMap[stage]);
                 intermediates[stage]->getTreeRoot()->traverse(&iter_iomap);
             }
         }
index 6741327..7934c4a 100644 (file)
@@ -203,7 +203,6 @@ public:
     void endCollect(EShLanguage) override;
     void reserverStorageSlot(TVarEntryInfo& ent, TInfoSink& infoSink) override;
     void reserverResourceSlot(TVarEntryInfo& ent, TInfoSink& infoSink) override;
-    const TString& getAccessName(const TIntermSymbol*);
     // in/out symbol and uniform symbol are stored in the same resourceSlotMap, the storage key is used to identify each type of symbol.
     // We use stage and storage qualifier to construct a storage key. it can help us identify the same storage resource used in different stage.
     // if a resource is a program resource and we don't need know it usage stage, we can use same stage to build storage key.
@@ -263,10 +262,12 @@ public:
 class TGlslIoMapper : public TIoMapper {
 public:
     TGlslIoMapper() {
-        memset(inVarMaps,     0, sizeof(TVarLiveMap*)   * EShLangCount);
-        memset(outVarMaps,    0, sizeof(TVarLiveMap*)   * EShLangCount);
-        memset(uniformVarMap, 0, sizeof(TVarLiveMap*)   * EShLangCount);
-        memset(intermediates, 0, sizeof(TIntermediate*) * EShLangCount);
+        memset(inVarMaps,     0, sizeof(TVarLiveMap*)   * (EShLangCount + 1));
+        memset(outVarMaps,    0, sizeof(TVarLiveMap*)   * (EShLangCount + 1));
+        memset(uniformVarMap, 0, sizeof(TVarLiveMap*)   * (EShLangCount + 1));
+        memset(intermediates, 0, sizeof(TIntermediate*) * (EShLangCount + 1));
+        profile = ENoProfile;
+        version = 0;
     }
     virtual ~TGlslIoMapper() {
         for (size_t stage = 0; stage < EShLangCount; stage++) {
@@ -293,6 +294,8 @@ public:
                 *uniformVarMap[EShLangCount];
     TIntermediate* intermediates[EShLangCount];
     bool hadError = false;
+    EProfile profile;
+    int version;
 };
 
 } // end namespace glslang
index 0aabf37..7295002 100644 (file)
@@ -658,14 +658,17 @@ public:
 
                 blocks.back().numMembers = countAggregateMembers(type);
 
-                EShLanguageMask& stages = blocks.back().stages;
-                stages = static_cast<EShLanguageMask>(stages | 1 << intermediate.getStage());
+                if (updateStageMasks) {
+                    EShLanguageMask& stages = blocks.back().stages;
+                    stages = static_cast<EShLanguageMask>(stages | 1 << intermediate.getStage());
+                }
             }
             else {
                 blockIndex = it->second;
-
-                EShLanguageMask& stages = blocks[blockIndex].stages;
-                stages = static_cast<EShLanguageMask>(stages | 1 << intermediate.getStage());
+                if (updateStageMasks) {
+                    EShLanguageMask& stages = blocks[blockIndex].stages;
+                    stages = static_cast<EShLanguageMask>(stages | 1 << intermediate.getStage());
+                }
             }
         }