8. io mapping refine & qualifier member check & resolver expand (#2396)
* 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.
diff --git a/SPIRV/GlslangToSpv.cpp b/SPIRV/GlslangToSpv.cpp
index 0b6c62b..7fc5c1e 100644
--- a/SPIRV/GlslangToSpv.cpp
+++ b/SPIRV/GlslangToSpv.cpp
@@ -3959,6 +3959,8 @@
// 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 @@
// 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
diff --git a/Test/baseResults/300layout.vert.out b/Test/baseResults/300layout.vert.out
index e245cbc..527425b 100644
--- a/Test/baseResults/300layout.vert.out
+++ b/Test/baseResults/300layout.vert.out
@@ -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 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
diff --git a/Test/baseResults/310.frag.out b/Test/baseResults/310.frag.out
index 9fe93e9..9f73c67 100644
--- a/Test/baseResults/310.frag.out
+++ b/Test/baseResults/310.frag.out
@@ -40,10 +40,14 @@
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: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
diff --git a/Test/baseResults/310.vert.out b/Test/baseResults/310.vert.out
index a910e34..1d2152d 100644
--- a/Test/baseResults/310.vert.out
+++ b/Test/baseResults/310.vert.out
@@ -21,6 +21,7 @@
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: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
diff --git a/Test/baseResults/320.vert.out b/Test/baseResults/320.vert.out
index bf127d4..0313b61 100644
--- a/Test/baseResults/320.vert.out
+++ b/Test/baseResults/320.vert.out
@@ -4,6 +4,7 @@
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: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
diff --git a/Test/baseResults/410.geom.out b/Test/baseResults/410.geom.out
index c38ba48..498da5a 100644
--- a/Test/baseResults/410.geom.out
+++ b/Test/baseResults/410.geom.out
@@ -5,9 +5,10 @@
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
diff --git a/Test/baseResults/hlsl.structbuffer.append.fn.frag.out b/Test/baseResults/hlsl.structbuffer.append.fn.frag.out
index 6a239da..2ac904e 100644
--- a/Test/baseResults/hlsl.structbuffer.append.fn.frag.out
+++ b/Test/baseResults/hlsl.structbuffer.append.fn.frag.out
@@ -190,13 +190,13 @@
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
diff --git a/Test/baseResults/hlsl.structbuffer.fn.frag.out b/Test/baseResults/hlsl.structbuffer.fn.frag.out
index c5afd35..bd2a4e6 100644
--- a/Test/baseResults/hlsl.structbuffer.fn.frag.out
+++ b/Test/baseResults/hlsl.structbuffer.fn.frag.out
@@ -191,9 +191,9 @@
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
diff --git a/Test/baseResults/link.vk.multiBlocksValid.0.0.vert.out b/Test/baseResults/link.vk.multiBlocksValid.0.0.vert.out
index ddb856d..bdabab1 100644
--- a/Test/baseResults/link.vk.multiBlocksValid.0.0.vert.out
+++ b/Test/baseResults/link.vk.multiBlocksValid.0.0.vert.out
@@ -211,7 +211,7 @@
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 @@
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
diff --git a/Test/baseResults/link.vk.multiBlocksValid.1.0.geom.out b/Test/baseResults/link.vk.multiBlocksValid.1.0.geom.out
index 63be728..c0b33b7 100644
--- a/Test/baseResults/link.vk.multiBlocksValid.1.0.geom.out
+++ b/Test/baseResults/link.vk.multiBlocksValid.1.0.geom.out
@@ -304,7 +304,7 @@
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 @@
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
diff --git a/Test/baseResults/spv.specConstant.vert.out b/Test/baseResults/spv.specConstant.vert.out
index 921cc68..76f3de6 100644
--- a/Test/baseResults/spv.specConstant.vert.out
+++ b/Test/baseResults/spv.specConstant.vert.out
@@ -11,7 +11,7 @@
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 @@
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
diff --git a/glslang/Include/intermediate.h b/glslang/Include/intermediate.h
index 30cb6fb..9e41a99 100644
--- a/glslang/Include/intermediate.h
+++ b/glslang/Include/intermediate.h
@@ -1282,6 +1282,8 @@
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
diff --git a/glslang/MachineIndependent/IntermTraverse.cpp b/glslang/MachineIndependent/IntermTraverse.cpp
index f46010b..553b1b5 100644
--- a/glslang/MachineIndependent/IntermTraverse.cpp
+++ b/glslang/MachineIndependent/IntermTraverse.cpp
@@ -71,6 +71,13 @@
it->visitConstantUnion(this);
}
+const TString& TIntermSymbol::getAccessName() const {
+ if (getBasicType() == EbtBlock)
+ return getType().getTypeName();
+ else
+ return getName();
+}
+
//
// Traverse a binary node.
//
diff --git a/glslang/MachineIndependent/ParseContextBase.cpp b/glslang/MachineIndependent/ParseContextBase.cpp
index b464009..3efa27a 100644
--- a/glslang/MachineIndependent/ParseContextBase.cpp
+++ b/glslang/MachineIndependent/ParseContextBase.cpp
@@ -127,22 +127,6 @@
{
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 @@
// 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 @@
// 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, "");
+
+ } else {
+ if (binaryNode) {
+ switch (binaryNode->getOp()) {
+ case EOpIndexDirect:
+ case EOpIndexIndirect:
+ case EOpIndexDirectStruct:
+ case EOpVectorSwizzle:
+ case EOpMatrixSwizzle:
+ rValueErrorCheck(loc, op, binaryNode->getLeft());
+ default:
+ break;
+ }
}
-
- return;
}
-
- 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
diff --git a/glslang/MachineIndependent/ParseHelper.cpp b/glslang/MachineIndependent/ParseHelper.cpp
index b9b37fb..032774f 100644
--- a/glslang/MachineIndependent/ParseHelper.cpp
+++ b/glslang/MachineIndependent/ParseHelper.cpp
@@ -3368,7 +3368,7 @@
//
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 @@
//
// 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 @@
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 @@
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 @@
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::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 @@
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 @@
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 @@
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 @@
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 @@
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 {
diff --git a/glslang/MachineIndependent/ParseHelper.h b/glslang/MachineIndependent/ParseHelper.h
index 0f09ada..fe2b6fb 100644
--- a/glslang/MachineIndependent/ParseHelper.h
+++ b/glslang/MachineIndependent/ParseHelper.h
@@ -83,7 +83,7 @@
: 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 @@
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 @@
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);
diff --git a/glslang/MachineIndependent/Scan.cpp b/glslang/MachineIndependent/Scan.cpp
index bd3181a..3a5f090 100644
--- a/glslang/MachineIndependent/Scan.cpp
+++ b/glslang/MachineIndependent/Scan.cpp
@@ -982,7 +982,7 @@
return keyword;
case PACKED:
if ((parseContext.isEsProfile() && parseContext.version < 300) ||
- (!parseContext.isEsProfile() && parseContext.version < 330))
+ (!parseContext.isEsProfile() && parseContext.version < 140))
return reservedWord();
return identifierOrType();
diff --git a/glslang/MachineIndependent/SymbolTable.cpp b/glslang/MachineIndependent/SymbolTable.cpp
index 007f22c..e245814 100644
--- a/glslang/MachineIndependent/SymbolTable.cpp
+++ b/glslang/MachineIndependent/SymbolTable.cpp
@@ -146,6 +146,8 @@
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);
}
diff --git a/glslang/MachineIndependent/SymbolTable.h b/glslang/MachineIndependent/SymbolTable.h
index ec4bc3c..db16c19 100644
--- a/glslang/MachineIndependent/SymbolTable.h
+++ b/glslang/MachineIndependent/SymbolTable.h
@@ -613,20 +613,24 @@
//
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 @@
{
assert(thisSymbol.getName().size() == 0);
table.push_back(new TSymbolTableLevel);
+ updateUniqueIdLevelFlag();
table.back()->setThisLevel();
insert(thisSymbol);
}
@@ -648,6 +653,7 @@
table[currentLevel()]->getPreviousDefaultPrecisions(p);
delete table.back();
table.pop_back();
+ updateUniqueIdLevelFlag();
}
//
@@ -867,12 +873,20 @@
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;
diff --git a/glslang/MachineIndependent/glslang.m4 b/glslang/MachineIndependent/glslang.m4
index 0b4b53f..6bd3759 100644
--- a/glslang/MachineIndependent/glslang.m4
+++ b/glslang/MachineIndependent/glslang.m4
@@ -905,7 +905,7 @@
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);
diff --git a/glslang/MachineIndependent/glslang.y b/glslang/MachineIndependent/glslang.y
index 23adcb0..f507002 100644
--- a/glslang/MachineIndependent/glslang.y
+++ b/glslang/MachineIndependent/glslang.y
@@ -905,7 +905,7 @@
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);
diff --git a/glslang/MachineIndependent/glslang_tab.cpp b/glslang/MachineIndependent/glslang_tab.cpp
index ac35797..0204660 100644
--- a/glslang/MachineIndependent/glslang_tab.cpp
+++ b/glslang/MachineIndependent/glslang_tab.cpp
@@ -5186,7 +5186,7 @@
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);
diff --git a/glslang/MachineIndependent/iomapper.cpp b/glslang/MachineIndependent/iomapper.cpp
index 905cf65..0e9e717 100644
--- a/glslang/MachineIndependent/iomapper.cpp
+++ b/glslang/MachineIndependent/iomapper.cpp
@@ -37,9 +37,11 @@
#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 @@
// 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 @@
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 @@
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 @@
};
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 @@
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 @@
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 @@
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 @@
TIoMapResolver& resolver;
TInfoSink& infoSink;
bool& error;
-
+ TVarLiveMap* uniformVarMap[EShLangCount];
private:
TResolverUniformAdaptor& operator=(TResolverUniformAdaptor&) = delete;
};
@@ -261,7 +286,7 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
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::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 @@
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::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::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::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::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 @@
}
}
-const TString& TDefaultGlslIoResolver::getAccessName(const TIntermSymbol* symbol)
-{
- return symbol->getBasicType() == EbtBlock ?
- symbol->getType().getTypeName() :
- symbol->getName();
-}
-
//TDefaultGlslIoResolver end
/*
@@ -1117,25 +1421,23 @@
}
// 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 @@
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 @@
//
// 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();
- bool somethingToDo = ! intermediate.getResourceSetBinding().empty() || intermediate.getAutoMapBindings() ||
- intermediate.getAutoMapLocations();
+ // Profile and version are use for symbol validate.
+ profile = intermediate.getProfile();
+ version = intermediate.getVersion();
+
// 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 @@
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 @@
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);
}
}
diff --git a/glslang/MachineIndependent/iomapper.h b/glslang/MachineIndependent/iomapper.h
index 6741327..7934c4a 100644
--- a/glslang/MachineIndependent/iomapper.h
+++ b/glslang/MachineIndependent/iomapper.h
@@ -203,7 +203,6 @@
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 @@
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 @@
*uniformVarMap[EShLangCount];
TIntermediate* intermediates[EShLangCount];
bool hadError = false;
+ EProfile profile;
+ int version;
};
} // end namespace glslang
diff --git a/glslang/MachineIndependent/reflection.cpp b/glslang/MachineIndependent/reflection.cpp
index 0aabf37..7295002 100644
--- a/glslang/MachineIndependent/reflection.cpp
+++ b/glslang/MachineIndependent/reflection.cpp
@@ -658,14 +658,17 @@
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());
+ }
}
}