From f3e2a894524e2ad680c87721aefdb7136535d8b7 Mon Sep 17 00:00:00 2001 From: John Kessenich Date: Fri, 13 Dec 2013 18:38:43 +0000 Subject: [PATCH] Tessellation: Implicit array sizing and consistency checking of control-shader output arrays based on layout(vertices=...). git-svn-id: https://cvs.khronos.org/svn/repos/ogl/trunk/ecosystem/public/sdk/tools/glslang@24518 e7fa87d3-cd2b-0410-9028-fcbf551c1848 --- Test/400.tesc | 8 + Test/410.tesc | 11 ++ Test/420.tesc | 27 ++++ Test/420_size_gl_in.geom | 21 +++ Test/baseResults/400.geom.out | 6 +- Test/baseResults/400.tesc.out | 26 +++- Test/baseResults/410.geom.out | 6 +- Test/baseResults/410.tesc.out | 21 +++ Test/baseResults/420.geom.out | 8 +- Test/baseResults/420.tesc.out | 85 ++++++++++ Test/baseResults/420.tese.out | 2 +- Test/baseResults/420_size_gl_in.geom.out | 49 ++++++ Test/baseResults/mains1.frag.out | 4 +- Test/testlist | 3 + glslang/Include/revision.h | 4 +- glslang/MachineIndependent/ParseHelper.cpp | 205 +++++++++++++++++-------- glslang/MachineIndependent/ParseHelper.h | 29 ++-- glslang/MachineIndependent/linkValidate.cpp | 22 ++- glslang/MachineIndependent/localintermediate.h | 1 + 19 files changed, 434 insertions(+), 104 deletions(-) create mode 100644 Test/410.tesc create mode 100644 Test/420.tesc create mode 100644 Test/420_size_gl_in.geom create mode 100644 Test/baseResults/410.tesc.out create mode 100644 Test/baseResults/420.tesc.out create mode 100644 Test/baseResults/420_size_gl_in.geom.out diff --git a/Test/400.tesc b/Test/400.tesc index 8c76694..e4463ac 100644 --- a/Test/400.tesc +++ b/Test/400.tesc @@ -35,3 +35,11 @@ void main() gl_TessLevelOuter[3] = 3.2; gl_TessLevelInner[1] = 1.3; } + +layout(vertices = 4) in; // ERROR +layout(vertices = 5) out; // ERROR + +void foo() +{ + gl_out[4].gl_PointSize; // ERROR +} \ No newline at end of file diff --git a/Test/410.tesc b/Test/410.tesc new file mode 100644 index 0000000..d96a270 --- /dev/null +++ b/Test/410.tesc @@ -0,0 +1,11 @@ +#version 400 core + +// no layout(vertices = ...) out; +int outa[gl_out.length()]; // ERROR + +patch out vec4 patchOut; + +void main() +{ + +} diff --git a/Test/420.tesc b/Test/420.tesc new file mode 100644 index 0000000..e289238 --- /dev/null +++ b/Test/420.tesc @@ -0,0 +1,27 @@ +#version 400 core + +#extension GL_ARB_separate_shader_objects : enable + +layout(vertices = 4) out; + +out gl_PerVertex { + vec4 gl_Position; +} gl_out[3]; // ERROR, wrong size + +out int a[gl_out.length()]; +out int outb[5]; // ERROR, wrong size +out int outc[]; + +void main() +{ + vec4 p = gl_in[1].gl_Position; + float ps = gl_in[1].gl_PointSize; + float cd = gl_in[1].gl_ClipDistance[2]; + + int pvi = gl_PatchVerticesIn; + int pid = gl_PrimitiveID; + int iid = gl_InvocationID; + + gl_out[1].gl_Position = p; + gl_out[1].gl_PointSize = ps; // ERROR +} diff --git a/Test/420_size_gl_in.geom b/Test/420_size_gl_in.geom new file mode 100644 index 0000000..fd6d134 --- /dev/null +++ b/Test/420_size_gl_in.geom @@ -0,0 +1,21 @@ +#version 420 core + +// testing input arrays without a gl_in[] block redeclaration, see 400.geom for with + +int i; + +layout(triangles) in; +in vec4 colorun[]; +in vec4 color3[3]; + +void foo() +{ + gl_in.length(); + gl_in[1].gl_Position; + gl_in.length(); + gl_in[i].gl_Position; +} + +in gl_PerVertex { // ERROR, already used + vec4 gl_Position; +} gl_in[]; diff --git a/Test/baseResults/400.geom.out b/Test/baseResults/400.geom.out index 141a0c7..a9d7408 100644 --- a/Test/baseResults/400.geom.out +++ b/Test/baseResults/400.geom.out @@ -4,8 +4,8 @@ ERROR: 0:13: 'invocations' : can only apply to a standalone qualifier ERROR: 0:20: 'patch' : not supported in this stage: geometry ERROR: 0:20: 'gl_PointSize' : cannot add layout to redeclared block member ERROR: 0:20: 'gl_PointSize' : cannot add patch to redeclared block member -ERROR: 0:25: 'length' : array must be declared with a size before using this method -ERROR: 0:36: 'length' : array must be declared with a size before using this method +ERROR: 0:25: 'length' : array must first be sized by a redeclaration or layout qualifier +ERROR: 0:36: 'length' : array must first be sized by a redeclaration or layout qualifier ERROR: 0:40: 'triangles' : inconsistent input primitive for array size colorBad ERROR: 0:44: 'triangles' : inconsistent input primitive for array size colorbad2 ERROR: 0:56: 'location' : repeated use of location 4 @@ -81,7 +81,7 @@ ERROR: node is still EOpNull! Linked geometry stage: -ERROR: Linking geometry stage: At least one geometry shader must specify an output layout primitive +ERROR: Linking geometry stage: At least one shader must specify an output layout primitive invocations = 4 max_vertices = 127 diff --git a/Test/baseResults/400.tesc.out b/Test/baseResults/400.tesc.out index 490ed3a..3e10c21 100644 --- a/Test/baseResults/400.tesc.out +++ b/Test/baseResults/400.tesc.out @@ -1,11 +1,13 @@ 400.tesc Warning, version 400 is not yet complete; some version-specific features are present, but many are missing. -ERROR: 0:4: 'length' : array must be declared with a size before using this method ERROR: 0:6: 'quads' : unrecognized layout identifier, or qualifier requires assignemnt (e.g., binding = 4) ERROR: 0:7: 'ccw' : unrecognized layout identifier, or qualifier requires assignemnt (e.g., binding = 4) ERROR: 0:8: 'fractional_even_spacing' : unrecognized layout identifier, or qualifier requires assignemnt (e.g., binding = 4) ERROR: 0:10: 'patch' : can only use on output in tessellation-control shader -ERROR: 5 compilation errors. No code generated. +ERROR: 0:39: 'vertices' : can only apply to 'out' +ERROR: 0:40: 'vertices' : cannot change previously set layout value +ERROR: 0:44: '[' : array index out of range '4' +ERROR: 7 compilation errors. No code generated. vertices = 4 @@ -67,7 +69,7 @@ ERROR: node is still EOpNull! 0:31 move second child to first child (4-component vector of float) 0:31 gl_Position: direct index for structure (4-component vector of float) 0:31 direct index (block{gl_Position,gl_PointSize,gl_ClipDistance}) -0:31 'gl_out' (out unsized array of block{gl_Position,gl_PointSize,gl_ClipDistance}) +0:31 'gl_out' (out 4-element array of block{gl_Position,gl_PointSize,gl_ClipDistance}) 0:31 Constant: 0:31 1 (const int) 0:31 Constant: @@ -76,7 +78,7 @@ ERROR: node is still EOpNull! 0:32 move second child to first child (float) 0:32 gl_PointSize: direct index for structure (float) 0:32 direct index (block{gl_Position,gl_PointSize,gl_ClipDistance}) -0:32 'gl_out' (out unsized array of block{gl_Position,gl_PointSize,gl_ClipDistance}) +0:32 'gl_out' (out 4-element array of block{gl_Position,gl_PointSize,gl_ClipDistance}) 0:32 Constant: 0:32 1 (const int) 0:32 Constant: @@ -86,7 +88,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 (block{gl_Position,gl_PointSize,gl_ClipDistance}) -0:33 'gl_out' (out unsized array of block{gl_Position,gl_PointSize,gl_ClipDistance}) +0:33 'gl_out' (out 4-element array of block{gl_Position,gl_PointSize,gl_ClipDistance}) 0:33 Constant: 0:33 1 (const int) 0:33 Constant: @@ -108,11 +110,21 @@ ERROR: node is still EOpNull! 0:36 1 (const int) 0:36 Constant: 0:36 1.300000 +0:42 Function Definition: foo( (void) +0:42 Function Parameters: +0:44 Sequence +0:44 gl_PointSize: direct index for structure (float) +0:44 direct index (block{gl_Position,gl_PointSize,gl_ClipDistance}) +0:44 'gl_out' (out 4-element array of block{gl_Position,gl_PointSize,gl_ClipDistance}) +0:44 Constant: +0:44 4 (const int) +0:44 Constant: +0:44 1 (const int) 0:? Linker Objects -0:? 'outa' (1-element array of int) +0:? 'outa' (4-element array of int) 0:? 'patchIn' (patch in 4-component vector of float) 0:? 'patchOut' (patch out 4-component vector of float) -0:? 'gl_out' (out unsized array of block{gl_Position,gl_PointSize,gl_ClipDistance}) +0:? 'gl_out' (out 4-element array of block{gl_Position,gl_PointSize,gl_ClipDistance}) Linked tessellation control stage: diff --git a/Test/baseResults/410.geom.out b/Test/baseResults/410.geom.out index 1a4c580..1eaeec6 100644 --- a/Test/baseResults/410.geom.out +++ b/Test/baseResults/410.geom.out @@ -52,9 +52,9 @@ ERROR: node is still EOpNull! Linked geometry stage: -ERROR: Linking geometry stage: At least one geometry shader must specify an input layout primitive -ERROR: Linking geometry stage: At least one geometry shader must specify an output layout primitive -ERROR: Linking geometry stage: At least one geometry shader must specify a layout(max_vertices = value) +ERROR: Linking geometry stage: At least one shader must specify an input layout primitive +ERROR: Linking geometry stage: At least one shader must specify an output layout primitive +ERROR: Linking geometry stage: At least one shader must specify a layout(max_vertices = value) invocations = 0 max_vertices = 0 diff --git a/Test/baseResults/410.tesc.out b/Test/baseResults/410.tesc.out new file mode 100644 index 0000000..832ee90 --- /dev/null +++ b/Test/baseResults/410.tesc.out @@ -0,0 +1,21 @@ +410.tesc +Warning, version 400 is not yet complete; some version-specific features are present, but many are missing. +ERROR: 0:4: 'length' : array must first be sized by a redeclaration or layout qualifier +ERROR: 1 compilation errors. No code generated. + + +vertices = 0 +ERROR: node is still EOpNull! +0:8 Function Definition: main( (void) +0:8 Function Parameters: +0:? Linker Objects +0:? 'outa' (1-element array of int) +0:? 'patchOut' (patch out 4-component vector of float) + + +Linked tessellation control stage: + +ERROR: Linking tessellation control stage: At least one shader must specify an output layout(vertices=...) + +vertices = 0 + diff --git a/Test/baseResults/420.geom.out b/Test/baseResults/420.geom.out index 10af913..fc6e6c9 100644 --- a/Test/baseResults/420.geom.out +++ b/Test/baseResults/420.geom.out @@ -1,7 +1,7 @@ 420.geom Warning, version 420 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: 0:9: 'length' : array must first be sized by a redeclaration or layout qualifier +ERROR: 0:11: '[' : array must be sized by a redeclaration or layout qualifier before being indexed with a variable ERROR: 0:42: 'assign' : l-value required (can't modify a const) ERROR: 0:43: 'assign' : l-value required "v4" (can't modify a uniform) ERROR: 0:48: 'gl_PointSize' : cannot change arrayness of redeclared block member @@ -123,8 +123,8 @@ ERROR: node is still EOpNull! Linked geometry stage: ERROR: Linking geometry stage: Missing entry point: Each stage requires one "void main()" entry point -ERROR: Linking geometry stage: At least one geometry shader must specify an output layout primitive -ERROR: Linking geometry stage: At least one geometry shader must specify a layout(max_vertices = value) +ERROR: Linking geometry stage: At least one shader must specify an output layout primitive +ERROR: Linking geometry stage: At least one shader must specify a layout(max_vertices = value) invocations = 0 max_vertices = 0 diff --git a/Test/baseResults/420.tesc.out b/Test/baseResults/420.tesc.out new file mode 100644 index 0000000..4ffba0b --- /dev/null +++ b/Test/baseResults/420.tesc.out @@ -0,0 +1,85 @@ +420.tesc +Warning, version 400 is not yet complete; some version-specific features are present, but many are missing. +ERROR: 0:7: 'vertices' : inconsistent output number of vertices for array size gl_out +ERROR: 0:11: 'vertices' : inconsistent output number of vertices for array size a +ERROR: 0:12: 'vertices' : inconsistent output number of vertices for array size outb +ERROR: 0:26: 'gl_PointSize' : no such field in structure +ERROR: 0:26: 'assign' : cannot convert from 'float' to 'block{gl_Position}' +ERROR: 5 compilation errors. No code generated. + + +vertices = 4 +ERROR: node is still EOpNull! +0:15 Function Definition: main( (void) +0:15 Function Parameters: +0:17 Sequence +0:17 Sequence +0:17 move second child to first child (4-component vector of float) +0:17 'p' (4-component vector of float) +0:17 gl_Position: direct index for structure (4-component vector of float) +0:17 direct index (block{gl_Position,gl_PointSize,gl_ClipDistance}) +0:17 'gl_in' (in 32-element array of block{gl_Position,gl_PointSize,gl_ClipDistance}) +0:17 Constant: +0:17 1 (const int) +0:17 Constant: +0:17 0 (const int) +0:18 Sequence +0:18 move second child to first child (float) +0:18 'ps' (float) +0:18 gl_PointSize: direct index for structure (float) +0:18 direct index (block{gl_Position,gl_PointSize,gl_ClipDistance}) +0:18 'gl_in' (in 32-element array of block{gl_Position,gl_PointSize,gl_ClipDistance}) +0:18 Constant: +0:18 1 (const int) +0:18 Constant: +0:18 1 (const int) +0:19 Sequence +0:19 move second child to first child (float) +0:19 'cd' (float) +0:19 direct index (float) +0:19 gl_ClipDistance: direct index for structure (unsized array of float) +0:19 direct index (block{gl_Position,gl_PointSize,gl_ClipDistance}) +0:19 'gl_in' (in 32-element array of block{gl_Position,gl_PointSize,gl_ClipDistance}) +0:19 Constant: +0:19 1 (const int) +0:19 Constant: +0:19 2 (const int) +0:19 Constant: +0:19 2 (const int) +0:21 Sequence +0:21 move second child to first child (int) +0:21 'pvi' (int) +0:21 'gl_PatchVerticesIn' (in int) +0:22 Sequence +0:22 move second child to first child (int) +0:22 'pid' (int) +0:22 'gl_PrimitiveID' (in int) +0:23 Sequence +0:23 move second child to first child (int) +0:23 'iid' (int) +0:23 'gl_InvocationID' (in int) +0:25 move second child to first child (4-component vector of float) +0:25 gl_Position: direct index for structure (4-component vector of float) +0:25 direct index (block{gl_Position}) +0:25 'gl_out' (out 3-element array of block{gl_Position}) +0:25 Constant: +0:25 1 (const int) +0:25 Constant: +0:25 0 (const int) +0:25 'p' (4-component vector of float) +0:26 direct index (block{gl_Position}) +0:26 'gl_out' (out 3-element array of block{gl_Position}) +0:26 Constant: +0:26 1 (const int) +0:? Linker Objects +0:? 'gl_out' (out 3-element array of block{gl_Position}) +0:? 'a' (out 3-element array of int) +0:? 'outb' (out 5-element array of int) +0:? 'outc' (out 4-element array of int) + + +Linked tessellation control stage: + + +vertices = 4 + diff --git a/Test/baseResults/420.tese.out b/Test/baseResults/420.tese.out index 2df4c15..71ddd93 100644 --- a/Test/baseResults/420.tese.out +++ b/Test/baseResults/420.tese.out @@ -163,7 +163,7 @@ ERROR: node is still EOpNull! Linked tessellation evaluation stage: -ERROR: Linking tessellation evaluation stage: At least one tessellation shader must specify an input layout primitive +ERROR: Linking tessellation evaluation stage: At least one shader must specify an input layout primitive input primitive = none vertex spacing = equal_spacing diff --git a/Test/baseResults/420_size_gl_in.geom.out b/Test/baseResults/420_size_gl_in.geom.out new file mode 100644 index 0000000..0915868 --- /dev/null +++ b/Test/baseResults/420_size_gl_in.geom.out @@ -0,0 +1,49 @@ +420_size_gl_in.geom +Warning, version 420 is not yet complete; some version-specific features are present, but many are missing. +ERROR: 0:19: 'gl_PerVertex' : can only redeclare a built-in block once, and before any use +ERROR: 1 compilation errors. No code generated. + + +invocations = 0 +max_vertices = 0 +input primitive = triangles +output primitive = none +ERROR: node is still EOpNull! +0:11 Function Definition: foo( (void) +0:11 Function Parameters: +0:13 Sequence +0:13 Constant: +0:13 3 (const int) +0:14 gl_Position: direct index for structure (4-component vector of float) +0:14 direct index (block{gl_Position,gl_PointSize,gl_ClipDistance}) +0:14 'gl_in' (in 3-element array of block{gl_Position,gl_PointSize,gl_ClipDistance}) +0:14 Constant: +0:14 1 (const int) +0:14 Constant: +0:14 0 (const int) +0:15 Constant: +0:15 3 (const int) +0:16 gl_Position: direct index for structure (4-component vector of float) +0:16 indirect index (block{gl_Position,gl_PointSize,gl_ClipDistance}) +0:16 'gl_in' (in 3-element array of block{gl_Position,gl_PointSize,gl_ClipDistance}) +0:16 'i' (int) +0:16 Constant: +0:16 0 (const int) +0:? Linker Objects +0:? 'i' (int) +0:? 'colorun' (in 3-element array of 4-component vector of float) +0:? 'color3' (in 3-element array of 4-component vector of float) +0:? 'gl_in' (in 3-element array of block{gl_Position,gl_PointSize,gl_ClipDistance}) + + +Linked geometry stage: + +ERROR: Linking geometry stage: Missing entry point: Each stage requires one "void main()" entry point +ERROR: Linking geometry stage: At least one shader must specify an output layout primitive +ERROR: Linking geometry stage: At least one shader must specify a layout(max_vertices = value) + +invocations = 0 +max_vertices = 0 +input primitive = triangles +output primitive = none + diff --git a/Test/baseResults/mains1.frag.out b/Test/baseResults/mains1.frag.out index 87d8800..4510d74 100644 --- a/Test/baseResults/mains1.frag.out +++ b/Test/baseResults/mains1.frag.out @@ -44,8 +44,8 @@ Linked geometry stage: ERROR: Linking geometry stage: Contradictory output layout primitives ERROR: Linking geometry stage: Missing entry point: Each stage requires one "void main()" entry point -ERROR: Linking geometry stage: At least one geometry shader must specify an input layout primitive -ERROR: Linking geometry stage: At least one geometry shader must specify a layout(max_vertices = value) +ERROR: Linking geometry stage: At least one shader must specify an input layout primitive +ERROR: Linking geometry stage: At least one shader must specify a layout(max_vertices = value) Linked fragment stage: diff --git a/Test/testlist b/Test/testlist index f32ea4e..ac63cbb 100644 --- a/Test/testlist +++ b/Test/testlist @@ -49,7 +49,9 @@ tokenLength.vert 300scope.vert 400.frag 420.vert +420.tesc 420.geom +420_size_gl_in.geom 430scope.vert lineContinuation100.vert lineContinuation.vert @@ -58,6 +60,7 @@ numeral.frag 400.tesc 400.tese 410.geom +410.tesc 420.tese 430.vert 430.comp diff --git a/glslang/Include/revision.h b/glslang/Include/revision.h index 92b2c47..096b251 100644 --- a/glslang/Include/revision.h +++ b/glslang/Include/revision.h @@ -9,5 +9,5 @@ // source have to figure out how to create revision.h just to get a build // going. However, if it is not updated, it can be a version behind. -#define GLSLANG_REVISION "24480" -#define GLSLANG_DATE "2013/12/11 15:38:19" +#define GLSLANG_REVISION "24486" +#define GLSLANG_DATE "2013/12/11 18:25:37" diff --git a/glslang/MachineIndependent/ParseHelper.cpp b/glslang/MachineIndependent/ParseHelper.cpp index d413176..6e01da5 100644 --- a/glslang/MachineIndependent/ParseHelper.cpp +++ b/glslang/MachineIndependent/ParseHelper.cpp @@ -437,13 +437,21 @@ TIntermTyped* TParseContext::handleBracketDereference(TSourceLoc loc, TIntermTyp return intermediate.foldDereference(base, indexValue, loc); else { // at least one of base and index is variable... + + if (isIoResizeArray(base->getType())) + handleIoResizeArrayAccess(loc, base); + if (index->getQualifier().storage == EvqConst) { if (base->isArray() && base->getType().getArraySize() == 0) updateMaxArraySize(loc, base, indexValue); result = intermediate.addIndex(EOpIndexDirect, base, index, loc); } else { - if (base->isArray() && base->getType().getArraySize() == 0) - error(loc, "", "[", "array must be redeclared with a size before being indexed with a variable"); + if (base->isArray() && base->getType().getArraySize() == 0) { + if (isIoResizeArray(base->getType())) + error(loc, "", "[", "array must be sized by a redeclaration or layout qualifier before being indexed with a variable"); + else + 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"); else if (language == EShLangFragment && base->getQualifier().isPipeOutput()) @@ -474,9 +482,6 @@ TIntermTyped* TParseContext::handleBracketDereference(TSourceLoc loc, TIntermTyp if (anyIndexLimits) handleIndexLimits(loc, base, index); - - if (language == EShLangGeometry && base->isArray()) - handleInputArrayAccess(loc, base); } return result; @@ -523,62 +528,99 @@ void TParseContext::handleIndexLimits(TSourceLoc loc, TIntermTyped* base, TInter } } -// Handle a dereference of a geometry shader input arrays. -// See inputArrayNodeResizeList comment in ParseHelper.h. -// -void TParseContext::handleInputArrayAccess(TSourceLoc loc, TIntermTyped* base) +// Return true if this is a geometry shader input array or tessellation control output array. +bool TParseContext::isIoResizeArray(const TType& type) { - 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); + return type.isArray() && + ((language == EShLangGeometry && type.getQualifier().storage == EvqVaryingIn) || + (language == EShLangTessControl && type.getQualifier().storage == EvqVaryingOut && ! type.getQualifier().patch)); +} - // Save it in the AST for linker use. - intermediate.addSymbolLinkageNode(linkage, *input); +// Handle a dereference of a geometry shader input array or tessellation control output array. +// See ioArrayNodeResizeList comment in ParseHelper.h. +// +void TParseContext::handleIoResizeArrayAccess(TSourceLoc loc, TIntermTyped* base) +{ + TIntermSymbol* symbol = base->getAsSymbolNode(); + assert(symbol); + ioArrayNodeResizeList.push_back(symbol); + if (symbol && builtInName(symbol->getName())) { + // make sure we have a user-modifiable copy of this built-in input array + TSymbol* arry = symbolTable.find(symbol->getName()); + if (arry->isReadOnly()) { + arry = symbolTable.copyUp(arry); + + // fix array size, if already implicitly size + if (arry->getType().getArraySize() == 0) { + int newSize = getIoArrayImplicitSize(); + if (newSize) { + arry->getWritableType().changeArraySize(newSize); + symbol->getWritableType().changeArraySize(newSize); + } } + + ioArraySymbolResizeList.push_back(arry); + + // Save it in the AST for linker use. + intermediate.addSymbolLinkageNode(linkage, *arry); } } } -// If there has been an input primitive declaration, make sure all input array types +// If there has been an input primitive declaration (geometry shader) or an output +// number of vertices declaration(tessellation shader), 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) +void TParseContext::checkIoArraysConsistency(TSourceLoc loc, bool tailOnly) { - TLayoutGeometry primitive = intermediate.getInputPrimitive(); - if (primitive == ElgNone) + int requiredSize = getIoArrayImplicitSize(); + if (requiredSize == 0) return; + const char* feature; + if (language == EShLangGeometry) + feature = TQualifier::getGeometryString(intermediate.getInputPrimitive()); + else if (language == EShLangTessControl) + feature = "vertices"; + if (tailOnly) { - checkInputArrayConsistency(loc, primitive, inputArraySymbolResizeList.back()->getWritableType(), inputArraySymbolResizeList.back()->getName()); + checkIoArrayConsistency(loc, requiredSize, feature, ioArraySymbolResizeList.back()->getWritableType(), ioArraySymbolResizeList.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 < ioArrayNodeResizeList.size(); ++i) + checkIoArrayConsistency(loc, requiredSize, feature, ioArrayNodeResizeList[i]->getWritableType(), ioArrayNodeResizeList[i]->getName()); - for (size_t i = 0; i < inputArraySymbolResizeList.size(); ++i) - checkInputArrayConsistency(loc, primitive, inputArraySymbolResizeList[i]->getWritableType(), inputArraySymbolResizeList[i]->getName()); + for (size_t i = 0; i < ioArraySymbolResizeList.size(); ++i) + checkIoArrayConsistency(loc, requiredSize, feature, ioArraySymbolResizeList[i]->getWritableType(), ioArraySymbolResizeList[i]->getName()); } -void TParseContext::checkInputArrayConsistency(TSourceLoc loc, TLayoutGeometry primitive, TType& type, const TString& name) +int TParseContext::getIoArrayImplicitSize() const { - int requiredSize = TQualifier::mapGeometryToSize(primitive); + if (language == EShLangGeometry) + return TQualifier::mapGeometryToSize(intermediate.getInputPrimitive()); + else if (language == EShLangTessControl) + return intermediate.getVertices(); + else + return 0; +} +void TParseContext::checkIoArrayConsistency(TSourceLoc loc, int requiredSize, const char* feature, TType& type, const TString& name) +{ 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()); + else if (type.getArraySize() != requiredSize) { + if (language == EShLangGeometry) + error(loc, "inconsistent input primitive for array size", feature, name.c_str()); + else if (language == EShLangTessControl) + error(loc, "inconsistent output number of vertices for array size", feature, name.c_str()); + else + assert(0); + } } // @@ -837,13 +879,31 @@ TIntermTyped* TParseContext::handleFunctionCall(TSourceLoc loc, TFunction* fnCal if (op == EOpArrayLength) { if (fnCall->getParamCount() > 0) error(loc, "method does not accept any arguments", fnCall->getName().c_str(), ""); - int length; - if (intermNode->getAsTyped() == 0 || ! intermNode->getAsTyped()->getType().isArray() || intermNode->getAsTyped()->getType().getArraySize() == 0) { - error(loc, "", fnCall->getName().c_str(), "array must be declared with a size before using this method"); - length = 1; + int length = 0; + if (intermNode->getAsTyped() == 0 || ! intermNode->getAsTyped()->getType().isArray()) + error(loc, "", fnCall->getName().c_str(), "can only be applied to an array"); + else if (intermNode->getAsTyped()->getType().getArraySize() == 0) { + bool implicitlySized = false; + if (intermNode->getAsSymbolNode() && isIoResizeArray(intermNode->getAsTyped()->getType())) { + // We could be between a layout declaration that gives a built-in io array implicit size and + // a user redeclaration of that array, meaning we have to substitute its implicit size here + // without actually redeclaring the array. (It is an error to use a member before the + // redeclaration, but not an error to use the array name itself.) + const TString& name = intermNode->getAsSymbolNode()->getName(); + if (name == "gl_in" || name == "gl_out") + length = getIoArrayImplicitSize(); + } + if (length == 0) { + if (isIoResizeArray(intermNode->getAsTyped()->getType())) + error(loc, "", fnCall->getName().c_str(), "array must first be sized by a redeclaration or layout qualifier"); + else + error(loc, "", fnCall->getName().c_str(), "array must be declared with a size before using this method"); + } } else length = intermNode->getAsTyped()->getType().getArraySize(); + if (length == 0) + length = 1; TConstUnionArray unionArray(1); unionArray[0].setIConst(length); result = intermediate.addConstantUnion(unionArray, TType(EbtInt, EvqConst), loc); @@ -1986,10 +2046,9 @@ void TParseContext::declareArray(TSourceLoc loc, TString& identifier, const TTyp 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); + if (! symbolTable.atBuiltInLevel() && isIoResizeArray(type)) { + ioArraySymbolResizeList.push_back(symbol); + checkIoArraysConsistency(loc, true); } return; @@ -2016,8 +2075,8 @@ void TParseContext::declareArray(TSourceLoc loc, TString& identifier, const TTyp return; } if (newType.getArraySize() > 0) { - // 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())) + // be more leniant for input arrays to geometry shaders and tessellation control outputs, where the redeclaration is the same size + if (! (isIoResizeArray(type) && newType.getArraySize() == type.getArraySize())) error(loc, "redeclaration of array with size", identifier.c_str(), ""); return; } @@ -2031,8 +2090,8 @@ void TParseContext::declareArray(TSourceLoc loc, TString& identifier, const TTyp newType.shareArraySizes(type); - if (language == EShLangGeometry && type.getQualifier().storage == EvqVaryingIn) - checkInputArrayConsistency(loc); + if (isIoResizeArray(type)) + checkIoArraysConsistency(loc); } void TParseContext::updateMaxArraySize(TSourceLoc loc, TIntermNode *node, int index) @@ -2064,9 +2123,8 @@ void TParseContext::updateMaxArraySize(TSourceLoc loc, TIntermNode *node, int in 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); + if (isIoResizeArray(symbol->getType())) + ioArraySymbolResizeList.push_back(symbol); // Save it in the AST for linker use. intermediate.addSymbolLinkageNode(linkage, *symbol); @@ -2128,16 +2186,15 @@ TSymbol* TParseContext::redeclareBuiltinVariable(TSourceLoc loc, const TString& return 0; // If it wasn't at a built-in level, then it's already been redeclared; - // that is, this is a redeclaration of a redeclaration, reuse that initial + // that is, this is a redeclaration of a redeclaration; reuse that initial // redeclaration. Otherwise, make the new one. if (builtIn) { // 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); + if (isIoResizeArray(symbol->getType())) + ioArraySymbolResizeList.push_back(symbol); // Save it in the AST for linker use. intermediate.addSymbolLinkageNode(linkage, *symbol); @@ -2248,10 +2305,6 @@ void TParseContext::redeclareBuiltinBlock(TSourceLoc loc, TTypeList& newTypeList return; } - // 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); - // Edit and error check the container against the redeclaration // - remove unused members // - ensure remaining qualifiers/types match @@ -2320,12 +2373,20 @@ void TParseContext::redeclareBuiltinBlock(TSourceLoc loc, TTypeList& newTypeList error(loc, "cannot change arrayness of redeclared block", blockName.c_str(), ""); else if (type.isArray() && type.getArraySize() > 0 && type.getArraySize() != arraySizes->getSize()) error(loc, "cannot change array size of redeclared block", blockName.c_str(), ""); + else if (type.isArray() && type.getArraySize() == 0 && arraySizes->getSize() > 0) + type.changeArraySize(arraySizes->getSize()); symbolTable.insert(*block); // Check for general layout qualifier errors layoutTypeCheck(loc, *block); + // Tracking for implicit sizing of array + if (isIoResizeArray(block->getType())) { + ioArraySymbolResizeList.push_back(block); + checkIoArraysConsistency(loc, true); + } + // Save it in the AST for linker use. intermediate.addSymbolLinkageNode(linkage, *block); } @@ -2728,8 +2789,6 @@ void TParseContext::setLayoutQualifier(TSourceLoc loc, TPublicType& publicType, case EShLangTessControl: if (id == "vertices") { - // TODO: tessellation: implement gl_out[] array sizing based on this - // TODO: tessellation: semantic check this is on the out qualifier only publicType.shaderQualifiers.vertices = value; return; } @@ -2932,8 +2991,14 @@ void TParseContext::checkNoShaderLayouts(TSourceLoc loc, const TShaderQualifiers error(loc, message, TQualifier::getGeometryString(shaderQualifiers.geometry), ""); if (shaderQualifiers.invocations > 0) error(loc, message, "invocations", ""); - if (shaderQualifiers.vertices > 0) - error(loc, message, "max_vertices", ""); + if (shaderQualifiers.vertices > 0) { + if (language == EShLangGeometry) + error(loc, message, "max_vertices", ""); + else if (language == EShLangTessControl) + error(loc, message, "vertices", ""); + else + assert(0); + } } // @@ -3726,8 +3791,15 @@ void TParseContext::updateStandaloneQualifierDefaults(TSourceLoc loc, const TPub if (! intermediate.setVertices(publicType.shaderQualifiers.vertices)) { if (language == EShLangGeometry) error(loc, "cannot change previously set layout value", "max_vertices", ""); - else + else if (language == EShLangTessControl) error(loc, "cannot change previously set layout value", "vertices", ""); + else + assert(0); + } else if (language == EShLangTessControl) { + if (publicType.qualifier.storage != EvqVaryingOut) + error(loc, "can only apply to 'out'", "vertices", ""); + else + checkIoArraysConsistency(loc); } } if (publicType.shaderQualifiers.invocations) { @@ -3744,9 +3816,10 @@ void TParseContext::updateStandaloneQualifierDefaults(TSourceLoc loc, const TPub case ElgTrianglesAdjacency: case ElgQuads: case ElgIsolines: - if (intermediate.setInputPrimitive(publicType.shaderQualifiers.geometry)) - checkInputArrayConsistency(loc); - else + if (intermediate.setInputPrimitive(publicType.shaderQualifiers.geometry)) { + if (language == EShLangGeometry) + checkIoArraysConsistency(loc); + } else error(loc, "cannot change previously set input primitive", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), ""); break; default: @@ -3761,7 +3834,7 @@ void TParseContext::updateStandaloneQualifierDefaults(TSourceLoc loc, const TPub error(loc, "cannot change previously set output primitive", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), ""); break; default: - error(loc, "does not only apply to output", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), ""); + error(loc, "does not apply to output", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), ""); } } else error(loc, "cannot be used here", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), ""); diff --git a/glslang/MachineIndependent/ParseHelper.h b/glslang/MachineIndependent/ParseHelper.h index 5414470..3d4713b 100644 --- a/glslang/MachineIndependent/ParseHelper.h +++ b/glslang/MachineIndependent/ParseHelper.h @@ -84,9 +84,13 @@ public: TIntermTyped* handleBracketDereference(TSourceLoc, TIntermTyped* base, TIntermTyped* index); void checkIndex(TSourceLoc, const TType&, int& 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&); + + bool isIoResizeArray(const TType&); + void handleIoResizeArrayAccess(TSourceLoc, TIntermTyped* base); + void checkIoArraysConsistency(TSourceLoc, bool tailOnly = false); + int getIoArrayImplicitSize() const; + void checkIoArrayConsistency(TSourceLoc, int requiredSize, const char* feature, TType&, const TString&); + TIntermTyped* handleDotDereference(TSourceLoc, TIntermTyped* base, TString& field); TFunction* handleFunctionDeclarator(TSourceLoc loc, TFunction& function, bool prototype); TIntermAggregate* handleFunctionDefinition(TSourceLoc, TFunction&); @@ -257,6 +261,11 @@ protected: // // Geometry shader input arrays: // - array sizing is based on input primitive and/or explicit size + // + // Tessellation control output arrays: + // - array sizing is based on output layout(vertices=...) and/or explicit size + // + // Both: // - array sizing is retroactive // - built-in block redeclarations interact with this // @@ -270,23 +279,23 @@ protected: // - 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 + // - on built-in 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 user 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 a layout declaration that sizes the array, 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 + // array-sizing declarations // - TVector inputArrayNodeResizeList; - TVector inputArraySymbolResizeList; + TVector ioArrayNodeResizeList; + TVector ioArraySymbolResizeList; }; } // end namespace glslang diff --git a/glslang/MachineIndependent/linkValidate.cpp b/glslang/MachineIndependent/linkValidate.cpp index 74394c5..802bb24 100644 --- a/glslang/MachineIndependent/linkValidate.cpp +++ b/glslang/MachineIndependent/linkValidate.cpp @@ -88,8 +88,14 @@ void TIntermediate::merge(TInfoSink& infoSink, TIntermediate& unit) if (vertices == 0) vertices = unit.vertices; - else if (vertices != unit.vertices) - error(infoSink, "Contradictory layout max_vertices values"); + else if (vertices != unit.vertices) { + if (language == EShLangGeometry) + error(infoSink, "Contradictory layout max_vertices values"); + else if (language == EShLangTessControl) + error(infoSink, "Contradictory layout vertices values"); + else + assert(0); + } if (vertexSpacing == EvsNone) vertexSpacing = unit.vertexSpacing; @@ -297,11 +303,14 @@ void TIntermediate::finalCheck(TInfoSink& infoSink) switch (language) { case EShLangVertex: + break; case EShLangTessControl: + if (vertices == 0) + error(infoSink, "At least one shader must specify an output layout(vertices=...)"); break; case EShLangTessEvaluation: if (inputPrimitive == ElgNone) - error(infoSink, "At least one tessellation shader must specify an input layout primitive"); + error(infoSink, "At least one shader must specify an input layout primitive"); if (vertexSpacing == EvsNone) vertexSpacing = EvsEqual; if (vertexOrder == EvoNone) @@ -309,13 +318,14 @@ void TIntermediate::finalCheck(TInfoSink& infoSink) break; case EShLangGeometry: if (inputPrimitive == ElgNone) - error(infoSink, "At least one geometry shader must specify an input layout primitive"); + error(infoSink, "At least one shader must specify an input layout primitive"); if (outputPrimitive == ElgNone) - error(infoSink, "At least one geometry shader must specify an output layout primitive"); + error(infoSink, "At least one shader must specify an output layout primitive"); if (vertices == 0) - error(infoSink, "At least one geometry shader must specify a layout(max_vertices = value)"); + error(infoSink, "At least one shader must specify a layout(max_vertices = value)"); break; case EShLangFragment: + break; case EShLangCompute: break; } diff --git a/glslang/MachineIndependent/localintermediate.h b/glslang/MachineIndependent/localintermediate.h index b9b51c3..539a947 100644 --- a/glslang/MachineIndependent/localintermediate.h +++ b/glslang/MachineIndependent/localintermediate.h @@ -129,6 +129,7 @@ public: vertices = m; return true; } + int getVertices() const { return vertices; } bool setInputPrimitive(TLayoutGeometry p) { if (inputPrimitive != ElgNone) -- 2.7.4