diff --git a/Test/400.geom b/Test/400.geom index 84461be9eedb95875baa075cf089a46871a48af2..5388bb6cf0b3681551d24737053a37d85965ae6a 100644 --- a/Test/400.geom +++ b/Test/400.geom @@ -12,3 +12,40 @@ void main() layout(invocations = 3) out outbn { int a; }; // ERROR, not on a block layout(max_vertices = 127, invocations = 4) out; + +#extension GL_ARB_separate_shader_objects : enable + +in gl_PerVertex { // testing input arrays with a block redeclaration, see 420.geom for without + vec4 gl_Position; +} gl_in[]; + +void foo() +{ + gl_in.length(); // ERROR + gl_in[1].gl_Position; +} + +in vec4 color[]; +in vec4 color2[]; +in vec4 colorS[3]; +in vec4 colorBad[4]; + +void foo2() +{ + color.length(); // ERROR + colorS.length(); +} + +layout(triangles) in; // give ERROR just for colorBad + +in vec4 color[3]; +in vec4 color2[3]; +in vec4 colorbad2[2]; // ERROR + +void foo3() +{ + gl_in.length(); + color.length(); + color2.length(); + colorS.length(); +} diff --git a/Test/420.geom b/Test/420.geom new file mode 100644 index 0000000000000000000000000000000000000000..9740799cf3491725df1bbb6bda37da4f76456ebd --- /dev/null +++ b/Test/420.geom @@ -0,0 +1,23 @@ +#version 150 core + +// testing input arrays without a gl_in[] block redeclaration, see 400.geom for with + +int i; + +void foo() +{ + gl_in.length(); // ERROR + gl_in[1].gl_Position; + gl_in[i].gl_Position; // ERROR +} + +layout(triangles) in; + +in vec4 color3[3]; + +void foo3() +{ + gl_in.length(); + gl_in[i].gl_Position; + color3.length(); +} diff --git a/Test/baseResults/120.frag.out b/Test/baseResults/120.frag.out index 340639f6df5fb94ba26e3ebd8d3f2145b5be4f64..5691aec8aafe2e3aaad0fa26c3614bead83518a1 100644 --- a/Test/baseResults/120.frag.out +++ b/Test/baseResults/120.frag.out @@ -300,7 +300,7 @@ ERROR: node is still EOpNull! 0:121 add second child into first child (4-component vector of float) 0:121 'v' (4-component vector of float) 0:121 direct index (smooth in 4-component vector of float) -0:121 'gl_TexCoord' (smooth in unsized array of 4-component vector of float) +0:121 'gl_TexCoord' (smooth in 6-element array of 4-component vector of float) 0:121 Constant: 0:121 3 (const int) 0:? Linker Objects diff --git a/Test/baseResults/130.frag.out b/Test/baseResults/130.frag.out index 56965232ccd6b130462a9971a7c31ddce086778a..e26427567c2e3dc9b6a50a944f68f7d1c8eafca7 100644 --- a/Test/baseResults/130.frag.out +++ b/Test/baseResults/130.frag.out @@ -46,5 +46,6 @@ ERROR: node is still EOpNull! 0:? 'fflat' (flat in float) 0:? 'fsmooth' (smooth in float) 0:? 'fnop' (noperspective in float) +0:? 'gl_ClipDistance' (smooth in unsized array of float) 0:? 'sampC' (uniform samplerCube) diff --git a/Test/baseResults/140.frag.out b/Test/baseResults/140.frag.out index 3edb07097fa80a8ee6eccca7fc730764ab2603dc..1660a16c4d15d744c1b39bbcb96a63b4b70815ce 100644 --- a/Test/baseResults/140.frag.out +++ b/Test/baseResults/140.frag.out @@ -19,4 +19,5 @@ ERROR: node is still EOpNull! 0:? 'i' (smooth in 4-component vector of float) 0:? 'o' (out 4-component vector of float) 0:? 'gl_ClipDistance' (smooth in 5-element array of float) +0:? 'gl_ClipDistance' (smooth in 5-element array of float) diff --git a/Test/baseResults/150.geom.out b/Test/baseResults/150.geom.out index 2a22ff5589aa367db495b43c82230138852dfc1d..57bbecd19532c01357207bb1e5d958a6a552d2c7 100644 --- a/Test/baseResults/150.geom.out +++ b/Test/baseResults/150.geom.out @@ -63,7 +63,7 @@ ERROR: node is still EOpNull! 0:33 direct index (float) 0:33 gl_ClipDistance: direct index for structure (unsized array of float) 0:33 direct index (in block{gl_Position,gl_PointSize,gl_ClipDistance}) -0:33 'gl_in' (in unsized array of block{gl_Position,gl_PointSize,gl_ClipDistance}) +0:33 'gl_in' (in 4-element array of block{gl_Position,gl_PointSize,gl_ClipDistance}) 0:33 Constant: 0:33 1 (const int) 0:33 Constant: @@ -77,7 +77,7 @@ ERROR: node is still EOpNull! 0:34 0 (const uint) 0:34 gl_Position: direct index for structure (4-component vector of float) 0:34 direct index (in block{gl_Position,gl_PointSize,gl_ClipDistance}) -0:34 'gl_in' (in unsized array of block{gl_Position,gl_PointSize,gl_ClipDistance}) +0:34 'gl_in' (in 4-element array of block{gl_Position,gl_PointSize,gl_ClipDistance}) 0:34 Constant: 0:34 0 (const int) 0:34 Constant: @@ -89,7 +89,7 @@ ERROR: node is still EOpNull! 0:35 1 (const uint) 0:35 gl_PointSize: direct index for structure (float) 0:35 direct index (in block{gl_Position,gl_PointSize,gl_ClipDistance}) -0:35 'gl_in' (in unsized array of block{gl_Position,gl_PointSize,gl_ClipDistance}) +0:35 'gl_in' (in 4-element array of block{gl_Position,gl_PointSize,gl_ClipDistance}) 0:35 Constant: 0:35 3 (const int) 0:35 Constant: @@ -119,6 +119,7 @@ ERROR: node is still EOpNull! 0:? 'fromV' (in block{color}) 0:? 'toF' (layout(stream=0 ) out block{color}) 0:? '__anon__0' (layout(stream=0 ) out block{color}) +0:? 'gl_in' (in 4-element array of block{gl_Position,gl_PointSize,gl_ClipDistance}) 0:? 'ov0' (layout(stream=0 ) out 4-component vector of float) 0:? 'ov4' (layout(stream=4 ) out 4-component vector of float) 0:? 'o1v0' (layout(stream=0 ) out 4-component vector of float) diff --git a/Test/baseResults/400.frag.out b/Test/baseResults/400.frag.out index 5fdb4fc9e9c44134aed69bfabac1ef8e2c9ea5d6..2a1b4186692aa8d9048bf85d0ba9ba0304912219 100644 --- a/Test/baseResults/400.frag.out +++ b/Test/baseResults/400.frag.out @@ -112,6 +112,7 @@ ERROR: node is still EOpNull! 0:? 'arrayedSampler' (uniform 5-element array of sampler2D) 0:? 'samp2dr' (uniform usampler2DRect) 0:? 'isamp2DA' (uniform isampler2DArray) +0:? 'gl_ClipDistance' (smooth in unsized array of float) 0:? 'vl' (layout(location=4 ) smooth in 4-component vector of float) 0:? 'vl2' (layout(location=4 ) smooth in 4-component vector of float) 0:? 'uv3' (layout(location=3 ) uniform 3-component vector of float) diff --git a/Test/baseResults/400.geom.out b/Test/baseResults/400.geom.out index 93d603d0ab5e23e5face77ddc2b7f9faab18c8d7..2bbac98d29979a00f104c1d8051faba14feb3b6c 100644 --- a/Test/baseResults/400.geom.out +++ b/Test/baseResults/400.geom.out @@ -1,10 +1,14 @@ Warning, version 400 is not yet complete; some version-specific features are present, but many are missing. ERROR: 0:13: 'invocations' : can only apply to a standalone qualifier -ERROR: 1 compilation errors. No code generated. +ERROR: 0:24: 'length' : array must be declared with a size before using this method +ERROR: 0:35: 'length' : array must be declared with a size before using this method +ERROR: 0:39: 'triangles' : inconsistent input primitive for array size colorBad +ERROR: 0:43: 'triangles' : inconsistent input primitive for array size colorbad2 +ERROR: 5 compilation errors. No code generated. invocations = 4 max_vertices = 127 -input primitive = none +input primitive = triangles output primitive = none ERROR: node is still EOpNull! 0:3 Function Definition: main( (void) @@ -22,6 +26,42 @@ ERROR: node is still EOpNull! 0:10 move second child to first child (int) 0:10 'id' (int) 0:10 'gl_InvocationID' (in int) +0:22 Function Definition: foo( (void) +0:22 Function Parameters: +0:24 Sequence +0:24 Constant: +0:24 1 (const int) +0:25 gl_Position: direct index for structure (4-component vector of float) +0:25 direct index (in block{gl_Position}) +0:25 'gl_in' (in 3-element array of block{gl_Position}) +0:25 Constant: +0:25 1 (const int) +0:25 Constant: +0:25 0 (const int) +0:33 Function Definition: foo2( (void) +0:33 Function Parameters: +0:35 Sequence +0:35 Constant: +0:35 1 (const int) +0:36 Constant: +0:36 3 (const int) +0:45 Function Definition: foo3( (void) +0:45 Function Parameters: +0:47 Sequence +0:47 Constant: +0:47 3 (const int) +0:48 Constant: +0:48 3 (const int) +0:49 Constant: +0:49 3 (const int) +0:50 Constant: +0:50 3 (const int) 0:? Linker Objects 0:? '__anon__0' (layout(stream=0 ) out block{a}) +0:? 'gl_in' (in 3-element array of block{gl_Position}) +0:? 'color' (in 3-element array of 4-component vector of float) +0:? 'color2' (in 3-element array of 4-component vector of float) +0:? 'colorS' (in 3-element array of 4-component vector of float) +0:? 'colorBad' (in 4-element array of 4-component vector of float) +0:? 'colorbad2' (in 2-element array of 4-component vector of float) diff --git a/Test/baseResults/410.geom.out b/Test/baseResults/410.geom.out index f922453daec2a137e83796c771c9f19cf982d00b..a9a3c5efba5395fed36e287a24556348cae7b955 100644 --- a/Test/baseResults/410.geom.out +++ b/Test/baseResults/410.geom.out @@ -2,7 +2,7 @@ Warning, version 410 is not yet complete; some version-specific features are pre ERROR: 0:8: 'myIn' : cannot redeclare a built-in block with a user name ERROR: 0:8: 'gl_' : reserved built-in name ERROR: 0:12: 'gl_' : reserved built-in name -ERROR: 0:20: 'gl_PerVertex' : can only redeclare a built-in block once +ERROR: 0:20: 'gl_PerVertex' : can only redeclare a built-in block once, and before any use ERROR: 0:20: 'gl_' : reserved built-in name ERROR: 5 compilation errors. No code generated. diff --git a/Test/baseResults/420.geom.out b/Test/baseResults/420.geom.out new file mode 100644 index 0000000000000000000000000000000000000000..1c2907f63edde0f9e9619f976f97d9dfc6afd3e4 --- /dev/null +++ b/Test/baseResults/420.geom.out @@ -0,0 +1,46 @@ +Warning, version 150 is not yet complete; some version-specific features are present, but many are missing. +ERROR: 0:9: 'length' : array must be declared with a size before using this method +ERROR: 0:11: '[' : array must be redeclared with a size before being indexed with a variable +ERROR: 2 compilation errors. No code generated. + +invocations = 0 +max_vertices = 0 +input primitive = triangles +output primitive = none +ERROR: node is still EOpNull! +0:7 Function Definition: foo( (void) +0:7 Function Parameters: +0:9 Sequence +0:9 Constant: +0:9 1 (const int) +0:10 gl_Position: direct index for structure (4-component vector of float) +0:10 direct index (in block{gl_Position,gl_PointSize,gl_ClipDistance}) +0:10 'gl_in' (in 3-element array of block{gl_Position,gl_PointSize,gl_ClipDistance}) +0:10 Constant: +0:10 1 (const int) +0:10 Constant: +0:10 0 (const int) +0:11 gl_Position: direct index for structure (4-component vector of float) +0:11 indirect index (in block{gl_Position,gl_PointSize,gl_ClipDistance}) +0:11 'gl_in' (in 3-element array of block{gl_Position,gl_PointSize,gl_ClipDistance}) +0:11 'i' (int) +0:11 Constant: +0:11 0 (const int) +0:18 Function Definition: foo3( (void) +0:18 Function Parameters: +0:20 Sequence +0:20 Constant: +0:20 3 (const int) +0:21 gl_Position: direct index for structure (4-component vector of float) +0:21 indirect index (in block{gl_Position,gl_PointSize,gl_ClipDistance}) +0:21 'gl_in' (in 3-element array of block{gl_Position,gl_PointSize,gl_ClipDistance}) +0:21 'i' (int) +0:21 Constant: +0:21 0 (const int) +0:22 Constant: +0:22 3 (const int) +0:? Linker Objects +0:? 'i' (int) +0:? 'gl_in' (in 3-element array of block{gl_Position,gl_PointSize,gl_ClipDistance}) +0:? 'color3' (in 3-element array of 4-component vector of float) + diff --git a/Test/baseResults/specExamples.frag.out b/Test/baseResults/specExamples.frag.out index 7c155080c84dd3f1b6ccec421082035cb07c5afc..de312ae814f786f590c482ed938017e6a2c6017f 100644 --- a/Test/baseResults/specExamples.frag.out +++ b/Test/baseResults/specExamples.frag.out @@ -286,8 +286,11 @@ ERROR: node is still EOpNull! 0:? '__anon__1' (in block{LightPos,LightColor}) 0:? 'Materiala' (in block{Color,TexCoord}) 0:? 'gl_FragCoord' (gl_FragCoord 4-component vector of float) +0:? 'gl_FragCoord' (gl_FragCoord 4-component vector of float) 0:? 'factor' (layout(location=3 ) out 4-component vector of float) 0:? 'colors' (layout(location=2 ) out 3-element array of 4-component vector of float) 0:? 'gl_FragDepth' (gl_FragDepth float) +0:? 'gl_FragDepth' (gl_FragDepth float) +0:? '__anon__2' (in block{gl_FogFragCoord,gl_TexCoord,gl_Color,gl_SecondaryColor}) 0:? '__anon__2' (in block{gl_FogFragCoord,gl_TexCoord,gl_Color,gl_SecondaryColor}) diff --git a/Test/baseResults/specExamples.vert.out b/Test/baseResults/specExamples.vert.out index cfebc5a1ff40abb673d862ef59f89b51000fd168..030d42ef4bd4cefd3cfd267ad67aa5098d34c6b3 100644 --- a/Test/baseResults/specExamples.vert.out +++ b/Test/baseResults/specExamples.vert.out @@ -294,6 +294,7 @@ ERROR: node is still EOpNull! 0:? 'c2' (layout(binding=3 ) uniform int) 0:? 'd2' (layout(binding=2 ) uniform int) 0:? '__anon__5' (out block{gl_Position,gl_PointSize,gl_ClipDistance,gl_ClipVertex,gl_FrontColor,gl_BackColor,gl_FrontSecondaryColor,gl_BackSecondaryColor,gl_TexCoord,gl_FogFragCoord}) +0:? '__anon__5' (out block{gl_Position,gl_PointSize,gl_ClipDistance,gl_ClipVertex,gl_FrontColor,gl_BackColor,gl_FrontSecondaryColor,gl_BackSecondaryColor,gl_TexCoord,gl_FogFragCoord}) 0:? 'ColorInv' (smooth out 3-component vector of float) 0:? 'Color4' (invariant centroid smooth out 3-component vector of float) 0:? 'position' (smooth out 4-component vector of float) diff --git a/Test/baseResults/varyingArray.frag.out b/Test/baseResults/varyingArray.frag.out index 73f7b906e700c33950a3ceb9fd14a7fa0382cee8..d8d0cdca36a651b73440e3bd11fa7350633915a4 100644 --- a/Test/baseResults/varyingArray.frag.out +++ b/Test/baseResults/varyingArray.frag.out @@ -54,5 +54,6 @@ WARNING: 0:8: varying deprecated in version 130; may be removed in future releas 0:? 'color' (smooth in 4-component vector of float) 0:? 'alpha' (smooth in float) 0:? 'gl_TexCoord' (smooth in 6-element array of 4-component vector of float) +0:? 'gl_TexCoord' (smooth in 6-element array of 4-component vector of float) 0:? 'foo' (smooth in 3-element array of 4-component vector of float) diff --git a/Test/baseResults/varyingArrayIndirect.frag.out b/Test/baseResults/varyingArrayIndirect.frag.out index 8e605226813d7b289109031e38aa722be7ac6179..311638f673e8e9dcd61360882b110e2f028fce77 100644 --- a/Test/baseResults/varyingArrayIndirect.frag.out +++ b/Test/baseResults/varyingArrayIndirect.frag.out @@ -55,6 +55,7 @@ WARNING: 0:8: varying deprecated in version 130; may be removed in future releas 0:? 'color' (smooth in 4-component vector of float) 0:? 'alpha' (smooth in float) 0:? 'gl_TexCoord' (smooth in 6-element array of 4-component vector of float) +0:? 'gl_TexCoord' (smooth in 6-element array of 4-component vector of float) 0:? 'userIn' (smooth in 2-element array of 4-component vector of float) 0:? 'a' (uniform int) 0:? 'b' (uniform int) diff --git a/Test/testlist b/Test/testlist index 0e0f2e182c2ee68c818b32f9237e75cad8c4ee20..aebde4a53cb38c84250357880224826f3cc42e89 100644 --- a/Test/testlist +++ b/Test/testlist @@ -47,6 +47,7 @@ tokenLength.vert 300scope.vert 400.frag 420.vert +420.geom 430scope.vert lineContinuation.vert numeral.frag diff --git a/glslang/MachineIndependent/ParseHelper.cpp b/glslang/MachineIndependent/ParseHelper.cpp index 1b12ce725719ae36f6f476e094d0c5e23ff740c1..7abc7beec96ae621c7af5961323896cdf740c24b 100644 --- a/glslang/MachineIndependent/ParseHelper.cpp +++ b/glslang/MachineIndependent/ParseHelper.cpp @@ -423,7 +423,7 @@ TIntermTyped* TParseContext::handleVariable(TSourceLoc loc, TSymbol* symbol, TSt } else { // The symbol table search was done in the lexical phase, but // if this is a new symbol, it wouldn't have found it. - const TVariable* variable = symbol ? symbol->getAsVariable() : 0; + TVariable* variable = symbol ? symbol->getAsVariable() : 0; if (symbol && ! variable) error(loc, "variable name expected", string->c_str(), ""); @@ -435,8 +435,16 @@ TIntermTyped* TParseContext::handleVariable(TSourceLoc loc, TSymbol* symbol, TSt if (variable->getType().getQualifier().storage == EvqConst) node = intermediate.addConstantUnion(variable->getConstArray(), variable->getType(), loc); - else - node = intermediate.addSymbol(variable->getUniqueId(), variable->getName(), variable->getType(), loc); + else { + // break sharing with built-ins + TType* type; + if (variable->isReadOnly()) { + type = new TType; + type->deepCopy(variable->getType()); + } else + type = &variable->getWritableType(); + node = intermediate.addSymbol(variable->getUniqueId(), variable->getName(), *type, loc); + } } return node; @@ -470,6 +478,7 @@ TIntermTyped* TParseContext::handleBracketDereference(TSourceLoc loc, TIntermTyp result = addConstMatrixNode(index->getAsConstantUnion()->getConstArray()[0].getIConst(), base, loc); } } else { + // at least one of base and index is variable... if (index->getQualifier().storage == EvqConst) { int indexValue = index->getAsConstantUnion()->getConstArray()[0].getIConst(); if (! base->isArray() && ((base->isVector() && base->getType().getVectorSize() <= indexValue) || @@ -488,7 +497,7 @@ TIntermTyped* TParseContext::handleBracketDereference(TSourceLoc loc, TIntermTyp error(loc, "", "[", "array must be redeclared with a size before being indexed with a variable"); if (base->getBasicType() == EbtBlock) requireProfile(base->getLoc(), ~EEsProfile, "variable indexing block array"); - if (base->getBasicType() == EbtSampler && version >= 130) { + else if (base->getBasicType() == EbtSampler && version >= 130) { const char* explanation = "variable indexing sampler array"; requireProfile(base->getLoc(), ECoreProfile | ECompatibilityProfile, explanation); profileRequires(base->getLoc(), ECoreProfile | ECompatibilityProfile, 400, 0, explanation); @@ -510,25 +519,90 @@ TIntermTyped* TParseContext::handleBracketDereference(TSourceLoc loc, TIntermTyp newType.dereference(); result->setType(newType); - if (anyIndexLimits) { - // for ES 2.0 (version 100) limitations for almost all index operations except vertex-shader uniforms - if ((! limits.generalSamplerIndexing && base->getBasicType() == EbtSampler) || - (! limits.generalUniformIndexing && base->getQualifier().isUniform() && language != EShLangVertex) || - (! limits.generalAttributeMatrixVectorIndexing && base->getQualifier().isPipeInput() && language == EShLangVertex && (base->getType().isMatrix() || base->getType().isVector())) || - (! limits.generalConstantMatrixVectorIndexing && base->getAsConstantUnion()) || - (! limits.generalVariableIndexing && ! base->getType().getQualifier().isUniform() && - ! base->getType().getQualifier().isPipeInput() && - ! base->getType().getQualifier().isPipeOutput() && - base->getType().getQualifier().storage != EvqConst) || - (! limits.generalVaryingIndexing && (base->getType().getQualifier().isPipeInput() || - base->getType().getQualifier().isPipeOutput()))) { - // it's too early to know what the inductive variables are, save it for post processing - needsIndexLimitationChecking.push_back(index); + if (anyIndexLimits) + handleIndexLimits(loc, base, index); + + if (language == EShLangGeometry && base->isArray()) + handleInputArrayAccess(loc, base); + } + + return result; +} + +// for ES 2.0 (version 100) limitations for almost all index operations except vertex-shader uniforms +void TParseContext::handleIndexLimits(TSourceLoc loc, TIntermTyped* base, TIntermTyped* index) +{ + if ((! limits.generalSamplerIndexing && base->getBasicType() == EbtSampler) || + (! limits.generalUniformIndexing && base->getQualifier().isUniform() && language != EShLangVertex) || + (! limits.generalAttributeMatrixVectorIndexing && base->getQualifier().isPipeInput() && language == EShLangVertex && (base->getType().isMatrix() || base->getType().isVector())) || + (! limits.generalConstantMatrixVectorIndexing && base->getAsConstantUnion()) || + (! limits.generalVariableIndexing && ! base->getType().getQualifier().isUniform() && + ! base->getType().getQualifier().isPipeInput() && + ! base->getType().getQualifier().isPipeOutput() && + base->getType().getQualifier().storage != EvqConst) || + (! limits.generalVaryingIndexing && (base->getType().getQualifier().isPipeInput() || + base->getType().getQualifier().isPipeOutput()))) { + // it's too early to know what the inductive variables are, save it for post processing + needsIndexLimitationChecking.push_back(index); + } +} + +// Handle a dereference of a geometry shader input arrays. +// See inputArrayNodeResizeList comment in ParseHelper.h. +// +void TParseContext::handleInputArrayAccess(TSourceLoc loc, TIntermTyped* base) +{ + if (base->getType().getQualifier().storage == EvqVaryingIn) { + TIntermSymbol* symbol = base->getAsSymbolNode(); + assert(symbol); + inputArrayNodeResizeList.push_back(symbol); + if (symbol && builtInName(symbol->getName())) { + // make sure we have a user-modifiable copy of this built-in input array + TSymbol* input = symbolTable.find(symbol->getName()); + if (input->isReadOnly()) { + input = symbolTable.copyUp(input); + inputArraySymbolResizeList.push_back(input); + + // Save it in the AST for linker use. + intermediate.addSymbolLinkageNode(linkage, *input); } } } +} - return result; +// If there has been an input primitive declaration, make sure all input array types +// match it in size. Types come either from nodes in the AST or symbols in the +// symbol table. +// +// Types without an array size will be given one. +// Types already having a size that is wrong will get an error. +// +void TParseContext::checkInputArrayConsistency(TSourceLoc loc, bool tailOnly) +{ + TLayoutGeometry primitive = intermediate.getInputPrimitive(); + if (primitive == ElgNone) + return; + + if (tailOnly) { + checkInputArrayConsistency(loc, primitive, inputArraySymbolResizeList.back()->getWritableType(), inputArraySymbolResizeList.back()->getName()); + return; + } + + for (size_t i = 0; i < inputArrayNodeResizeList.size(); ++i) + checkInputArrayConsistency(loc, primitive, inputArrayNodeResizeList[i]->getWritableType(), inputArrayNodeResizeList[i]->getName()); + + for (size_t i = 0; i < inputArraySymbolResizeList.size(); ++i) + checkInputArrayConsistency(loc, primitive, inputArraySymbolResizeList[i]->getWritableType(), inputArraySymbolResizeList[i]->getName()); +} + +void TParseContext::checkInputArrayConsistency(TSourceLoc loc, TLayoutGeometry primitive, TType& type, const TString& name) +{ + int requiredSize = TQualifier::mapGeometryToSize(primitive); + + if (type.getArraySize() == 0) + type.changeArraySize(requiredSize); + else if (type.getArraySize() != requiredSize) + error(loc, "inconsistent input primitive for array size", TQualifier::getGeometryString(primitive), name.c_str()); } // @@ -1052,7 +1126,7 @@ TOperator TParseContext::mapTypeToConstructorOp(const TType& type) default: break; // some compilers want this } break; - default: + default: op = EOpNull; break; } @@ -1268,7 +1342,7 @@ void TParseContext::globalCheck(TSourceLoc loc, const char* token) bool TParseContext::reservedErrorCheck(TSourceLoc loc, const TString& identifier) { if (! symbolTable.atBuiltInLevel()) { - if (identifier.compare(0, 3, "gl_") == 0) { + if (builtInName(identifier)) { error(loc, "reserved built-in name", "gl_", ""); return true; @@ -1283,6 +1357,11 @@ bool TParseContext::reservedErrorCheck(TSourceLoc loc, const TString& identifier return false; } +bool TParseContext::builtInName(const TString& identifier) +{ + return identifier.compare(0, 3, "gl_") == 0; +} + // // Make sure there is enough data provided to the constructor to build // something of the type of the constructor. Also returns the type of @@ -1787,6 +1866,13 @@ void TParseContext::declareArray(TSourceLoc loc, TString& identifier, const TTyp symbol = new TVariable(&identifier, type); symbolTable.insert(*symbol); newDeclaration = true; + + // Handle user geometry shader input arrays: see inputArrayNodeResizeList comment in ParseHelper.h + if (language == EShLangGeometry && type.getQualifier().storage == EvqVaryingIn && ! symbolTable.atBuiltInLevel()) { + inputArraySymbolResizeList.push_back(symbol); + checkInputArrayConsistency(loc, true); + } + return; } if (symbol->getAsAnonMember()) { @@ -1811,16 +1897,21 @@ void TParseContext::declareArray(TSourceLoc loc, TString& identifier, const TTyp return; } if (newType.getArraySize() > 0) { - error(loc, "redeclaration of array with size", identifier.c_str(), ""); + // be more leniant for input arrays to geometry shaders, where the redeclaration is the same size + if (! (language == EShLangGeometry && type.getQualifier().storage == EvqVaryingIn && newType.getArraySize() == type.getArraySize())) + error(loc, "redeclaration of array with size", identifier.c_str(), ""); return; } if (! newType.sameElementType(type)) { - error(loc, "redeclaration of array with a different newType", identifier.c_str(), ""); + error(loc, "redeclaration of array with a different type", identifier.c_str(), ""); return; } newType.shareArraySizes(type); + + if (language == EShLangGeometry && type.getQualifier().storage == EvqVaryingIn) + checkInputArrayConsistency(loc); } void TParseContext::updateMaxArraySize(TSourceLoc loc, TIntermNode *node, int index) @@ -1850,8 +1941,16 @@ void TParseContext::updateMaxArraySize(TSourceLoc loc, TIntermNode *node, int in // For read-only built-ins, add a new variable for holding the maximum array size of an implicitly-sized shared array. // TODO: functionality: unsized arrays: is this new array type shared with the node? - if (symbol->isReadOnly()) + if (symbol->isReadOnly()) { symbol = symbolTable.copyUp(symbol); + + // Handle geometry shader input arrays: see inputArrayNodeResizeList comment in ParseHelper.h + if (language == EShLangGeometry && symbol->getType().getQualifier().storage == EvqVaryingIn) + inputArraySymbolResizeList.push_back(symbol); + + // Save it in the AST for linker use. + intermediate.addSymbolLinkageNode(linkage, *symbol); + } symbol->getWritableType().setMaxArraySize(index + 1); } @@ -1864,7 +1963,7 @@ void TParseContext::nonInitConstCheck(TSourceLoc loc, TString& identifier, TType // // Make the qualifier make sense, given that there is an initializer. // - if (type.getQualifier().storage == EvqConst || + if (type.getQualifier().storage == EvqConst || type.getQualifier().storage == EvqConstReadOnly) { type.getQualifier().storage = EvqTemporary; error(loc, "variables with qualifier 'const' must be initialized", identifier.c_str(), ""); @@ -1883,7 +1982,7 @@ void TParseContext::nonInitConstCheck(TSourceLoc loc, TString& identifier, TType // TSymbol* TParseContext::redeclareBuiltinVariable(TSourceLoc loc, const TString& identifier, bool& newDeclaration) { - if (profile == EEsProfile || identifier.compare(0, 3, "gl_") != 0 || symbolTable.atBuiltInLevel()) + if (profile == EEsProfile || ! builtInName(identifier) || symbolTable.atBuiltInLevel()) return 0; // Potentially redeclaring a built-in variable... @@ -1915,6 +2014,13 @@ TSymbol* TParseContext::redeclareBuiltinVariable(TSourceLoc loc, const TString& // Copy the symbol up to make a writable version newDeclaration = true; symbol = symbolTable.copyUp(symbol); + + // Handle geometry shader input arrays: see inputArrayNodeResizeList comment in ParseHelper.h + if (language == EShLangGeometry && symbol->getType().getQualifier().storage == EvqVaryingIn && symbol->getType().isArray()) + inputArraySymbolResizeList.push_back(symbol); + + // Save it in the AST for linker use. + intermediate.addSymbolLinkageNode(linkage, *symbol); } // Now, modify the type of the copy, as per the type of the current redeclaration. @@ -1929,10 +2035,10 @@ TSymbol* TParseContext::redeclareBuiltinVariable(TSourceLoc loc, const TString& bool TParseContext::redeclareBuiltinBlock(TSourceLoc loc, TTypeList& typeList, const TString& blockName, const TString* instanceName, TArraySizes* arraySizes) { // just a quick out, not everything that must be checked: - if (symbolTable.atBuiltInLevel() || profile == EEsProfile || blockName.compare(0, 3, "gl_") != 0) + if (symbolTable.atBuiltInLevel() || profile == EEsProfile || ! builtInName(blockName)) return false; - if (instanceName && instanceName->compare(0, 3, "gl_") != 0) { + if (instanceName && ! builtInName(*instanceName)) { error(loc, "cannot redeclare a built-in block with a user name", instanceName->c_str(), ""); return false; } @@ -1944,11 +2050,8 @@ bool TParseContext::redeclareBuiltinBlock(TSourceLoc loc, TTypeList& typeList, c if (blockName != "gl_PerVertex" && blockName != "gl_PerFragment") return false; - // Blocks with instance names are easy to find, lookup the instance name, - // Anonymous blocks need to be found via a member. copyUp()?? will work - // just fine for either find. - + // Anonymous blocks need to be found via a member. bool builtIn; TSymbol* block; if (instanceName) @@ -1964,7 +2067,7 @@ bool TParseContext::redeclareBuiltinBlock(TSourceLoc loc, TTypeList& typeList, c // Built-in blocks cannot be redeclared more than once, which if happened, // we'd be finding the already redeclared one here, rather than the built in. if (! builtIn) { - error(loc, "can only redeclare a built-in block once", blockName.c_str(), ""); + error(loc, "can only redeclare a built-in block once, and before any use", blockName.c_str(), ""); return false; } @@ -1976,6 +2079,10 @@ bool TParseContext::redeclareBuiltinBlock(TSourceLoc loc, TTypeList& typeList, c return false; } + // Handle geometry shader input arrays: see inputArrayNodeResizeList comment in ParseHelper.h + if (language == EShLangGeometry && block->getType().isArray() && block->getType().getQualifier().storage == EvqVaryingIn) + inputArraySymbolResizeList.push_back(block); + // TODO: semantics: block redeclaration: instance array size matching? // Edit and error check the container against the redeclaration @@ -2061,7 +2168,7 @@ void TParseContext::arrayObjectCheck(TSourceLoc loc, const TType& type, const ch // "The loop index has type int or float. // // "The for statement has the form: -// for ( init-declaration ; condition ; expression ) +// for ( init-declaration ; condition ; expression ) // init-declaration has the form: type-specifier identifier = constant-expression // condition has the form: loop-index relational_operator constant-expression // where relational_operator is one of: > >= < <= == or != @@ -2102,11 +2209,11 @@ void TParseContext::inductiveLoopCheck(TSourceLoc loc, TIntermNode* init, TInter error(loc, "inductive-loop init-declaration requires the form \"type-specifier loop-index = constant-expression\"", "limitations", ""); return; } - + // get the unique id of the loop index int loopIndex = binaryInit->getLeft()->getAsSymbolNode()->getId(); inductiveLoopIds.insert(loopIndex); - + // condition's form must be "loop-index relational-operator constant-expression" bool badCond = ! loop->getTest(); if (! badCond) { @@ -2340,7 +2447,7 @@ void TParseContext::layoutTypeCheck(TSourceLoc loc, const TSymbol& symbol) // first, qualifier only error checking layoutQualifierCheck(loc, qualifier); - + // now, error checking combining type and qualifier if (qualifier.hasLocation()) { switch (qualifier.storage) { @@ -2372,10 +2479,10 @@ void TParseContext::layoutTypeCheck(TSourceLoc loc, const TSymbol& symbol) if (qualifier.hasBinding()) { // Binding checking, from the spec: // - // "If the binding point for any uniform or shader storage block instance is less than zero, or greater than or - // equal to the implementation-dependent maximum number of uniform buffer bindings, a compile-time - // error will occur. When the binding identifier is used with a uniform or shader storage block instanced as - // an array of size N, all elements of the array from binding through binding + N – 1 must be within this + // "If the binding point for any uniform or shader storage block instance is less than zero, or greater than or + // equal to the implementation-dependent maximum number of uniform buffer bindings, a compile-time + // error will occur. When the binding identifier is used with a uniform or shader storage block instanced as + // an array of size N, all elements of the array from binding through binding + N – 1 must be within this // range." // // TODO: binding error checking against limits, arrays @@ -2599,7 +2706,7 @@ TIntermNode* TParseContext::executeInitializer(TSourceLoc loc, TString& identifi initializer = convertInitializerList(loc, variable->getType(), initializer); if (! initializer) { // error recovery; don't leave const without constant values - if (qualifier == EvqConst) + if (qualifier == EvqConst) variable->getWritableType().getQualifier().storage = EvqTemporary; return 0; } @@ -2669,13 +2776,13 @@ TIntermTyped* TParseContext::convertInitializerList(TSourceLoc loc, const TType& // Will operate recursively. Once a subtree is found that is constructor style, // everything below it is already good: Only the "top part" of the initializer // can be an initializer list, where "top part" can extend for several (or all) levels. - + // see if we have bottomed out in the tree within the initializer-list part TIntermAggregate* initList = initializer->getAsAggregate(); if (! initList || initList->getOp() != EOpNull) return initializer; - // Of the initializer-list set of nodes, need to process bottom up, + // Of the initializer-list set of nodes, need to process bottom up, // so recurse deep, then process on the way up. // Go down the tree here... @@ -2929,7 +3036,7 @@ TIntermTyped* TParseContext::constructStruct(TIntermNode* node, const TType& typ // void TParseContext::declareBlock(TSourceLoc loc, TTypeList& typeList, const TString* instanceName, TArraySizes* arraySizes) { - // This might be a redeclaration of a built-in block, find out, and get + // This might be a redeclaration of a built-in block, find out, and get // a modifiable copy if so. if (redeclareBuiltinBlock(loc, typeList, *blockName, instanceName, arraySizes)) return; @@ -3013,7 +3120,7 @@ void TParseContext::declareBlock(TSourceLoc loc, TTypeList& typeList, const TStr // reverse merge, so that currentBlockQualifier now has all layout information // (can't use defaultQualification directly, it's missing other non-layout-default-class qualifiers) mergeObjectLayoutQualifiers(loc, currentBlockQualifier, defaultQualification); - + TType blockType(&typeList, *blockName, currentBlockQualifier); if (arraySizes) blockType.setArraySizes(arraySizes); @@ -3022,9 +3129,9 @@ void TParseContext::declareBlock(TSourceLoc loc, TTypeList& typeList, const TStr // Don't make a user-defined type out of block name; that will cause an error // if the same block name gets reused in a different interface. // - // "Block names have no other use within a shader - // beyond interface matching; it is a compile-time error to use a block name at global scope for anything - // other than as a block name (e.g., use of a block name for a global variable name or function name is + // "Block names have no other use within a shader + // beyond interface matching; it is a compile-time error to use a block name at global scope for anything + // other than as a block name (e.g., use of a block name for a global variable name or function name is // currently reserved)." // // Use the symbol table to prevent normal reuse of the block's name, as a variable entry, @@ -3124,7 +3231,9 @@ void TParseContext::updateStandaloneQualifierDefaults(TSourceLoc loc, const TPub case ElgLinesAdjacency: case ElgTriangles: case ElgTrianglesAdjacency: - if (! intermediate.setInputPrimitive(publicType.geometry)) + if (intermediate.setInputPrimitive(publicType.geometry)) + checkInputArrayConsistency(loc); + else error(loc, "cannot change previously set input primitive", TQualifier::getGeometryString(publicType.geometry), ""); break; default: @@ -3178,7 +3287,7 @@ void TParseContext::updateStandaloneQualifierDefaults(TSourceLoc loc, const TPub error(loc, "default qualifier requires 'uniform', 'buffer', 'in', or 'out' storage qualification", "", ""); return; } - + if (qualifier.hasBinding()) error(loc, "cannot declare a default, include a type or full declaration", "binding", ""); if (qualifier.hasLocation()) diff --git a/glslang/MachineIndependent/ParseHelper.h b/glslang/MachineIndependent/ParseHelper.h index 9504e51a39276427077d5c1fa216ea1430333ffd..eab5fb7d3e84a35efdb40fb27a2ff3e253fad517 100644 --- a/glslang/MachineIndependent/ParseHelper.h +++ b/glslang/MachineIndependent/ParseHelper.h @@ -72,12 +72,17 @@ public: const char *szExtraInfoFormat, ...); void C_DECL warn(TSourceLoc, const char *szReason, const char *szToken, const char *szExtraInfoFormat, ...); - bool reservedErrorCheck(TSourceLoc, const TString& identifier); + bool reservedErrorCheck(TSourceLoc, const TString&); + bool builtInName(const TString&); void updateExtensionBehavior(const char* extName, const char* behavior); void handlePragma(const char **tokens, int numTokens); TIntermTyped* handleVariable(TSourceLoc, TSymbol* symbol, TString* string); TIntermTyped* handleBracketDereference(TSourceLoc, TIntermTyped* base, TIntermTyped* index); + void handleIndexLimits(TSourceLoc, TIntermTyped* base, TIntermTyped* index); + void handleInputArrayAccess(TSourceLoc, TIntermTyped* base); + void checkInputArrayConsistency(TSourceLoc, bool tailOnly = false); + void checkInputArrayConsistency(TSourceLoc, TLayoutGeometry, TType&, const TString&); TIntermTyped* handleDotDereference(TSourceLoc, TIntermTyped* base, TString& field); TFunction* handleFunctionDeclarator(TSourceLoc loc, TFunction& function); TIntermAggregate* handleFunctionPrototype(TSourceLoc, TFunction&); @@ -230,6 +235,40 @@ protected: bool anyIndexLimits; TVector<TIntermTyped*> needsIndexLimitationChecking; // TODO: desktop functionality: track use of gl_FragDepth before redeclaration + + // + // Geometry shader input arrays: + // - array sizing is based on input primitive and/or explicit size + // - array sizing is retroactive + // - built-in block redeclarations interact with this + // + // Design: + // - use a per-context "resize-list", a list of entities whose array sizes + // can be fixed; this is logically one list, but physically two: + // * a list for nodes in the AST + // * a list for symbols in the symbol table + // this could be done a bit more simply, but this allows better error messages. + // + // - the resize-list starts empty at beginning of user-shader compilation, it does + // not have built-ins in it + // + // - on built-in input array use: copy-up symbol and add both the symbol and + // its use to resize-list + // + // - on user-input array declaration: add it to the resize-list + // + // - on block redeclaration: copy-up symbol and add it to the resize-list + // * note, that appropriately gives an error if redeclaring a block that + // was already used and hence already copied-up + // + // - on seeing an input primitive-layout declaration, fix everything in the resize-list, + // giving errors for mismatch + // + // - on seeing an array size declaration, give errors on mismatch between it and previous + // input primitive declarations + // + TVector<TIntermSymbol*> inputArrayNodeResizeList; + TVector<TSymbol*> inputArraySymbolResizeList; }; } // end namespace glslang diff --git a/glslang/MachineIndependent/SymbolTable.h b/glslang/MachineIndependent/SymbolTable.h index 9da462678fe3da6196f80be015281dbea7875660..b71d62cdd1fd05b85279d4bcbc6e34bb4b104f50 100644 --- a/glslang/MachineIndependent/SymbolTable.h +++ b/glslang/MachineIndependent/SymbolTable.h @@ -99,7 +99,7 @@ public: virtual int getUniqueId() const { return uniqueId; } virtual void dump(TInfoSink &infoSink) const = 0; - virtual bool isReadOnly() { return ! writable; } + virtual bool isReadOnly() const { return ! writable; } virtual void makeReadOnly() { writable = false; } protected: @@ -417,9 +417,10 @@ public: // 3: user-shader globals // 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 <= 3; } // include user globals + bool isGlobalLevel(int level) { return level <= globalLevel; } // include user globals public: bool isEmpty() { return table.size() == 0; } bool atBuiltInLevel() { return isBuiltInLevel(currentLevel()); } @@ -482,7 +483,7 @@ public: TSymbol* copyUp(TSymbol* shared) { TSymbol* copy = copyUpDeferredInsert(shared); - table[currentLevel()]->insert(*copy); + table[globalLevel]->insert(*copy); if (shared->getAsVariable()) return copy; else {