From d78ca6297d281c1a24be21764af089d33782faeb Mon Sep 17 00:00:00 2001 From: John Kessenich <cepheus@frii.com> Date: Tue, 19 Aug 2014 06:08:38 +0000 Subject: [PATCH] Implement atomic counter offset semantics. git-svn-id: https://cvs.khronos.org/svn/repos/ogl/trunk/ecosystem/public/sdk/tools/glslang@27760 e7fa87d3-cd2b-0410-9028-fcbf551c1848 --- Test/atomic_uint.frag | 11 ++- Test/baseResults/310.comp.out | 31 +++---- Test/baseResults/atomic_uint.frag.out | 69 ++++++++++------ Test/baseResults/specExamples.vert.out | 16 ++-- Todo.txt | 11 ++- glslang/MachineIndependent/ParseHelper.cpp | 80 +++++++++++++++++-- glslang/MachineIndependent/ParseHelper.h | 4 + glslang/MachineIndependent/glslang.y | 3 +- glslang/MachineIndependent/linkValidate.cpp | 27 ++++++- .../MachineIndependent/localintermediate.h | 21 ++++- 10 files changed, 207 insertions(+), 66 deletions(-) diff --git a/Test/atomic_uint.frag b/Test/atomic_uint.frag index 43ef0c9df..9a95a48ad 100644 --- a/Test/atomic_uint.frag +++ b/Test/atomic_uint.frag @@ -36,4 +36,13 @@ void opac() in atomic_uint acin; // ERROR atomic_uint acg; // ERROR - +uniform atomic_uint; +uniform atomic_uint aNoBind; // ERROR, no binding +layout(binding=0, offset=32) uniform atomic_uint aOffset; +layout(binding=0, offset=4) uniform atomic_uint; +layout(binding=0) uniform atomic_uint bar3; // offset is 4 +layout(binding=0) uniform atomic_uint ac[3]; // offset = 8 +layout(binding=0) uniform atomic_uint ad; // offset = 20 +layout(offset=8) uniform atomic_uint bar4; // ERROR, no binding +layout(binding = 0, offset = 12) uniform atomic_uint overlap; // ERROR, overlapping offsets +layout(binding = 20) uniform atomic_uint bigBind; // ERROR, binding too big diff --git a/Test/baseResults/310.comp.out b/Test/baseResults/310.comp.out index e2816ed82..69dd191a0 100644 --- a/Test/baseResults/310.comp.out +++ b/Test/baseResults/310.comp.out @@ -38,9 +38,10 @@ ERROR: 0:119: 'r8ui' : does not apply to signed integer images ERROR: 0:128: 'atomic_uint' : samplers and atomic_uints cannot be output parameters ERROR: 0:130: 'return' : type does not match, or is not convertible to, the function's return type ERROR: 0:136: 'atomic_uint' : atomic_uints can only be used in uniform variables or function parameters: non_uniform_counter +ERROR: 0:136: 'atomic_uint' : layout(binding=X) is required ERROR: 0:141: 'atomic_uint' : atomic counters can only be highp -ERROR: 0:141: 'binding' : cannot be greater-than-or-equal to gl_MaxAtomicCounterBindings -ERROR: 0:143: 'binding' : cannot be greater-than-or-equal to gl_MaxAtomicCounterBindings +ERROR: 0:141: 'binding' : atomic_uint binding is too large; see gl_MaxAtomicCounterBindings +ERROR: 0:143: 'binding' : atomic_uint binding is too large; see gl_MaxAtomicCounterBindings ERROR: 0:149: '[]' : scalar integer expression required ERROR: 0:166: 'precision' : can only apply highp to atomic_uint ERROR: 0:168: 'precise' : Reserved word. @@ -56,7 +57,7 @@ ERROR: 0:173: '' : image variables not declared 'writeonly' must have a format l ERROR: 0:174: 'uimage2DMSArray' : Reserved word. ERROR: 0:174: 'sampler/image' : type requires declaration of default precision qualifier ERROR: 0:174: '' : image variables not declared 'writeonly' must have a format layout qualifier -ERROR: 56 compilation errors. No code generated. +ERROR: 57 compilation errors. No code generated. Shader version: 310 @@ -174,10 +175,10 @@ ERROR: node is still EOpNull! 0:128 'c' (out highp atomic_uint) 0:130 Sequence 0:130 Branch: Return with expression -0:130 'counter' (layout(binding=0 ) uniform highp atomic_uint) +0:130 'counter' (layout(binding=0 offset=0 ) uniform highp atomic_uint) 0:131 Branch: Return with expression 0:131 Function Call: atomicCounter(au1; (highp uint) -0:131 'counter' (layout(binding=0 ) uniform highp atomic_uint) +0:131 'counter' (layout(binding=0 offset=0 ) uniform highp atomic_uint) 0:134 Function Definition: mainAC( (void) 0:134 Function Parameters: 0:? Sequence @@ -185,15 +186,15 @@ ERROR: node is still EOpNull! 0:137 move second child to first child (highp uint) 0:137 'val' (highp uint) 0:137 Function Call: atomicCounter(au1; (highp uint) -0:137 'counter' (layout(binding=0 ) uniform highp atomic_uint) +0:137 'counter' (layout(binding=0 offset=0 ) uniform highp atomic_uint) 0:138 Function Call: atomicCounterDecrement(au1; (highp uint) -0:138 'counter' (layout(binding=0 ) uniform highp atomic_uint) +0:138 'counter' (layout(binding=0 offset=0 ) uniform highp atomic_uint) 0:146 Function Definition: opac( (void) 0:146 Function Parameters: 0:? Sequence 0:149 indirect index (highp int) 0:149 'a' (3-element array of highp int) -0:149 'counter' (layout(binding=0 ) uniform highp atomic_uint) +0:149 'counter' (layout(binding=0 offset=0 ) uniform highp atomic_uint) 0:150 direct index (layout(binding=2 offset=4 ) highp atomic_uint) 0:150 'countArr' (layout(binding=2 offset=4 ) uniform 4-element array of highp atomic_uint) 0:150 Constant: @@ -270,7 +271,7 @@ ERROR: node is still EOpNull! 0:? 'i4bad' (layout(r8_snorm ) uniform highp iimage2D) 0:? 'i5bad' (layout(rgba32ui ) uniform highp iimage2D) 0:? 'i6bad' (layout(r8ui ) uniform highp iimage2D) -0:? 'counter' (layout(binding=0 ) uniform highp atomic_uint) +0:? 'counter' (layout(binding=0 offset=0 ) uniform highp atomic_uint) 0:? 'counterBad' (layout(binding=1 ) uniform mediump atomic_uint) 0:? 'countArr' (layout(binding=2 offset=4 ) uniform 4-element array of highp atomic_uint) 0:? 'i' (uniform highp int) @@ -402,10 +403,10 @@ ERROR: node is still EOpNull! 0:128 'c' (out highp atomic_uint) 0:130 Sequence 0:130 Branch: Return with expression -0:130 'counter' (layout(binding=0 ) uniform highp atomic_uint) +0:130 'counter' (layout(binding=0 offset=0 ) uniform highp atomic_uint) 0:131 Branch: Return with expression 0:131 Function Call: atomicCounter(au1; (highp uint) -0:131 'counter' (layout(binding=0 ) uniform highp atomic_uint) +0:131 'counter' (layout(binding=0 offset=0 ) uniform highp atomic_uint) 0:134 Function Definition: mainAC( (void) 0:134 Function Parameters: 0:? Sequence @@ -413,15 +414,15 @@ ERROR: node is still EOpNull! 0:137 move second child to first child (highp uint) 0:137 'val' (highp uint) 0:137 Function Call: atomicCounter(au1; (highp uint) -0:137 'counter' (layout(binding=0 ) uniform highp atomic_uint) +0:137 'counter' (layout(binding=0 offset=0 ) uniform highp atomic_uint) 0:138 Function Call: atomicCounterDecrement(au1; (highp uint) -0:138 'counter' (layout(binding=0 ) uniform highp atomic_uint) +0:138 'counter' (layout(binding=0 offset=0 ) uniform highp atomic_uint) 0:146 Function Definition: opac( (void) 0:146 Function Parameters: 0:? Sequence 0:149 indirect index (highp int) 0:149 'a' (3-element array of highp int) -0:149 'counter' (layout(binding=0 ) uniform highp atomic_uint) +0:149 'counter' (layout(binding=0 offset=0 ) uniform highp atomic_uint) 0:150 direct index (layout(binding=2 offset=4 ) highp atomic_uint) 0:150 'countArr' (layout(binding=2 offset=4 ) uniform 4-element array of highp atomic_uint) 0:150 Constant: @@ -498,7 +499,7 @@ ERROR: node is still EOpNull! 0:? 'i4bad' (layout(r8_snorm ) uniform highp iimage2D) 0:? 'i5bad' (layout(rgba32ui ) uniform highp iimage2D) 0:? 'i6bad' (layout(r8ui ) uniform highp iimage2D) -0:? 'counter' (layout(binding=0 ) uniform highp atomic_uint) +0:? 'counter' (layout(binding=0 offset=0 ) uniform highp atomic_uint) 0:? 'counterBad' (layout(binding=1 ) uniform mediump atomic_uint) 0:? 'countArr' (layout(binding=2 offset=4 ) uniform 4-element array of highp atomic_uint) 0:? 'i' (uniform highp int) diff --git a/Test/baseResults/atomic_uint.frag.out b/Test/baseResults/atomic_uint.frag.out index 71d413d7e..ddea17f66 100644 --- a/Test/baseResults/atomic_uint.frag.out +++ b/Test/baseResults/atomic_uint.frag.out @@ -3,15 +3,22 @@ Warning, version 420 is not yet complete; most version-specific features are pre ERROR: 0:10: 'atomic_uint' : samplers and atomic_uints cannot be output parameters ERROR: 0:12: 'return' : type does not match, or is not convertible to, the function's return type ERROR: 0:18: 'atomic_uint' : atomic_uints can only be used in uniform variables or function parameters: non_uniform_counter -ERROR: 0:23: 'binding' : cannot be greater-than-or-equal to gl_MaxAtomicCounterBindings -ERROR: 0:28: '+' : wrong operand types: no operation '+' exists that takes a left-hand operand of type 'layout(binding=0 ) uniform atomic_uint' and a right operand of type 'layout(binding=0 ) uniform atomic_uint' (or there is no acceptable conversion) -ERROR: 0:29: '-' : wrong operand type no operation '-' exists that takes an operand of type layout(binding=0 ) uniform atomic_uint (or there is no acceptable conversion) +ERROR: 0:18: 'atomic_uint' : layout(binding=X) is required +ERROR: 0:23: 'binding' : atomic_uint binding is too large; see gl_MaxAtomicCounterBindings +ERROR: 0:28: '+' : wrong operand types: no operation '+' exists that takes a left-hand operand of type 'layout(binding=0 offset=0 ) uniform atomic_uint' and a right operand of type 'layout(binding=0 offset=0 ) uniform atomic_uint' (or there is no acceptable conversion) +ERROR: 0:29: '-' : wrong operand type no operation '-' exists that takes an operand of type layout(binding=0 offset=0 ) uniform atomic_uint (or there is no acceptable conversion) ERROR: 0:31: '[]' : scalar integer expression required ERROR: 0:34: 'assign' : l-value required "counter" (can't modify a uniform) -ERROR: 0:34: 'assign' : cannot convert from 'const int' to 'layout(binding=0 ) uniform atomic_uint' +ERROR: 0:34: 'assign' : cannot convert from 'const int' to 'layout(binding=0 offset=0 ) uniform atomic_uint' ERROR: 0:37: 'atomic_uint' : atomic_uints can only be used in uniform variables or function parameters: acin +ERROR: 0:37: 'atomic_uint' : layout(binding=X) is required ERROR: 0:38: 'atomic_uint' : atomic_uints can only be used in uniform variables or function parameters: acg -ERROR: 11 compilation errors. No code generated. +ERROR: 0:38: 'atomic_uint' : layout(binding=X) is required +ERROR: 0:40: 'atomic_uint' : layout(binding=X) is required +ERROR: 0:46: 'atomic_uint' : layout(binding=X) is required +ERROR: 0:47: 'offset' : atomic counters sharing the same offset: 12 +ERROR: 0:48: 'binding' : atomic_uint binding is too large; see gl_MaxAtomicCounterBindings +ERROR: 18 compilation errors. No code generated. Shader version: 420 @@ -28,10 +35,10 @@ ERROR: node is still EOpNull! 0:10 'c' (out atomic_uint) 0:12 Sequence 0:12 Branch: Return with expression -0:12 'counter' (layout(binding=0 ) uniform atomic_uint) +0:12 'counter' (layout(binding=0 offset=0 ) uniform atomic_uint) 0:13 Branch: Return with expression 0:13 Function Call: atomicCounter(au1; (uint) -0:13 'counter' (layout(binding=0 ) uniform atomic_uint) +0:13 'counter' (layout(binding=0 offset=0 ) uniform atomic_uint) 0:16 Function Definition: main( (void) 0:16 Function Parameters: 0:? Sequence @@ -39,17 +46,17 @@ ERROR: node is still EOpNull! 0:19 move second child to first child (uint) 0:19 'val' (uint) 0:19 Function Call: atomicCounter(au1; (uint) -0:19 'counter' (layout(binding=0 ) uniform atomic_uint) +0:19 'counter' (layout(binding=0 offset=0 ) uniform atomic_uint) 0:20 Function Call: atomicCounterDecrement(au1; (uint) -0:20 'counter' (layout(binding=0 ) uniform atomic_uint) +0:20 'counter' (layout(binding=0 offset=0 ) uniform atomic_uint) 0:26 Function Definition: opac( (void) 0:26 Function Parameters: 0:28 Sequence -0:28 'counter' (layout(binding=0 ) uniform atomic_uint) -0:29 'counter' (layout(binding=0 ) uniform atomic_uint) +0:28 'counter' (layout(binding=0 offset=0 ) uniform atomic_uint) +0:29 'counter' (layout(binding=0 offset=0 ) uniform atomic_uint) 0:31 indirect index (int) 0:31 'a' (3-element array of int) -0:31 'counter' (layout(binding=0 ) uniform atomic_uint) +0:31 'counter' (layout(binding=0 offset=0 ) uniform atomic_uint) 0:32 direct index (layout(binding=1 offset=3 ) atomic_uint) 0:32 'countArr' (layout(binding=1 offset=3 ) uniform 4-element array of atomic_uint) 0:32 Constant: @@ -57,13 +64,21 @@ ERROR: node is still EOpNull! 0:33 indirect index (layout(binding=1 offset=3 ) atomic_uint) 0:33 'countArr' (layout(binding=1 offset=3 ) uniform 4-element array of atomic_uint) 0:33 'i' (uniform int) -0:34 'counter' (layout(binding=0 ) uniform atomic_uint) +0:34 'counter' (layout(binding=0 offset=0 ) uniform atomic_uint) 0:? Linker Objects -0:? 'counter' (layout(binding=0 ) uniform atomic_uint) +0:? 'counter' (layout(binding=0 offset=0 ) uniform atomic_uint) 0:? 'countArr' (layout(binding=1 offset=3 ) uniform 4-element array of atomic_uint) 0:? 'i' (uniform int) 0:? 'acin' (smooth in atomic_uint) 0:? 'acg' (atomic_uint) +0:? 'aNoBind' (uniform atomic_uint) +0:? 'aOffset' (layout(binding=0 offset=32 ) uniform atomic_uint) +0:? 'bar3' (layout(binding=0 offset=4 ) uniform atomic_uint) +0:? 'ac' (layout(binding=0 offset=8 ) uniform 3-element array of atomic_uint) +0:? 'ad' (layout(binding=0 offset=20 ) uniform atomic_uint) +0:? 'bar4' (layout(offset=8 ) uniform atomic_uint) +0:? 'overlap' (layout(binding=0 offset=12 ) uniform atomic_uint) +0:? 'bigBind' (layout(binding=20 ) uniform atomic_uint) Linked fragment stage: @@ -83,10 +98,10 @@ ERROR: node is still EOpNull! 0:10 'c' (out atomic_uint) 0:12 Sequence 0:12 Branch: Return with expression -0:12 'counter' (layout(binding=0 ) uniform atomic_uint) +0:12 'counter' (layout(binding=0 offset=0 ) uniform atomic_uint) 0:13 Branch: Return with expression 0:13 Function Call: atomicCounter(au1; (uint) -0:13 'counter' (layout(binding=0 ) uniform atomic_uint) +0:13 'counter' (layout(binding=0 offset=0 ) uniform atomic_uint) 0:16 Function Definition: main( (void) 0:16 Function Parameters: 0:? Sequence @@ -94,17 +109,17 @@ ERROR: node is still EOpNull! 0:19 move second child to first child (uint) 0:19 'val' (uint) 0:19 Function Call: atomicCounter(au1; (uint) -0:19 'counter' (layout(binding=0 ) uniform atomic_uint) +0:19 'counter' (layout(binding=0 offset=0 ) uniform atomic_uint) 0:20 Function Call: atomicCounterDecrement(au1; (uint) -0:20 'counter' (layout(binding=0 ) uniform atomic_uint) +0:20 'counter' (layout(binding=0 offset=0 ) uniform atomic_uint) 0:26 Function Definition: opac( (void) 0:26 Function Parameters: 0:28 Sequence -0:28 'counter' (layout(binding=0 ) uniform atomic_uint) -0:29 'counter' (layout(binding=0 ) uniform atomic_uint) +0:28 'counter' (layout(binding=0 offset=0 ) uniform atomic_uint) +0:29 'counter' (layout(binding=0 offset=0 ) uniform atomic_uint) 0:31 indirect index (int) 0:31 'a' (3-element array of int) -0:31 'counter' (layout(binding=0 ) uniform atomic_uint) +0:31 'counter' (layout(binding=0 offset=0 ) uniform atomic_uint) 0:32 direct index (layout(binding=1 offset=3 ) atomic_uint) 0:32 'countArr' (layout(binding=1 offset=3 ) uniform 4-element array of atomic_uint) 0:32 Constant: @@ -112,11 +127,19 @@ ERROR: node is still EOpNull! 0:33 indirect index (layout(binding=1 offset=3 ) atomic_uint) 0:33 'countArr' (layout(binding=1 offset=3 ) uniform 4-element array of atomic_uint) 0:33 'i' (uniform int) -0:34 'counter' (layout(binding=0 ) uniform atomic_uint) +0:34 'counter' (layout(binding=0 offset=0 ) uniform atomic_uint) 0:? Linker Objects -0:? 'counter' (layout(binding=0 ) uniform atomic_uint) +0:? 'counter' (layout(binding=0 offset=0 ) uniform atomic_uint) 0:? 'countArr' (layout(binding=1 offset=3 ) uniform 4-element array of atomic_uint) 0:? 'i' (uniform int) 0:? 'acin' (smooth in atomic_uint) 0:? 'acg' (atomic_uint) +0:? 'aNoBind' (uniform atomic_uint) +0:? 'aOffset' (layout(binding=0 offset=32 ) uniform atomic_uint) +0:? 'bar3' (layout(binding=0 offset=4 ) uniform atomic_uint) +0:? 'ac' (layout(binding=0 offset=8 ) uniform 3-element array of atomic_uint) +0:? 'ad' (layout(binding=0 offset=20 ) uniform atomic_uint) +0:? 'bar4' (layout(offset=8 ) uniform atomic_uint) +0:? 'overlap' (layout(binding=0 offset=12 ) uniform atomic_uint) +0:? 'bigBind' (layout(binding=20 ) uniform atomic_uint) diff --git a/Test/baseResults/specExamples.vert.out b/Test/baseResults/specExamples.vert.out index 3189787bd..5e6439d05 100644 --- a/Test/baseResults/specExamples.vert.out +++ b/Test/baseResults/specExamples.vert.out @@ -17,15 +17,15 @@ ERROR: 0:47: 'stream' : there is no such layout identifier for this stage taking ERROR: 0:50: 'stream' : there is no such layout identifier for this stage taking an assigned value ERROR: 0:55: 'stream' : there is no such layout identifier for this stage taking an assigned value ERROR: 0:80: 's17' : redefinition -ERROR: 0:85: 'binding' : cannot be greater-than-or-equal to gl_MaxAtomicCounterBindings -ERROR: 0:87: 'binding' : cannot be greater-than-or-equal to gl_MaxAtomicCounterBindings -WARNING: 0:89: 'layout' : useless application of layout qualifier +ERROR: 0:85: 'binding' : atomic_uint binding is too large; see gl_MaxAtomicCounterBindings +ERROR: 0:87: 'binding' : atomic_uint binding is too large; see gl_MaxAtomicCounterBindings +ERROR: 0:89: 'binding' : atomic_uint binding is too large ERROR: 0:91: 'bar' : redefinition -ERROR: 0:92: 'offset' : a binding is required +ERROR: 0:92: 'atomic_uint' : layout(binding=X) is required ERROR: 0:94: 'a2' : redefinition -ERROR: 0:95: 'binding' : cannot be greater-than-or-equal to gl_MaxAtomicCounterBindings -ERROR: 0:96: 'binding' : cannot be greater-than-or-equal to gl_MaxAtomicCounterBindings -ERROR: 0:97: 'binding' : cannot be greater-than-or-equal to gl_MaxAtomicCounterBindings +ERROR: 0:95: 'binding' : atomic_uint binding is too large; see gl_MaxAtomicCounterBindings +ERROR: 0:96: 'binding' : atomic_uint binding is too large; see gl_MaxAtomicCounterBindings +ERROR: 0:97: 'binding' : atomic_uint binding is too large; see gl_MaxAtomicCounterBindings ERROR: 0:106: '' : vertex input cannot be further qualified ERROR: 0:106: 'redeclaration' : cannot change storage, memory, or auxiliary qualification of gl_FrontColor ERROR: 0:112: 'ColorIvn' : identifier not previously declared @@ -38,7 +38,7 @@ ERROR: 0:170: 'coherent' : argument cannot drop memory qualifier when passed to ERROR: 0:192: 'constructor' : constructing from a non-dereferenced array ERROR: 0:193: 'constructor' : constructing from a non-dereferenced array ERROR: 0:194: 'constructor' : constructing from a non-dereferenced array -ERROR: 36 compilation errors. No code generated. +ERROR: 37 compilation errors. No code generated. Shader version: 430 diff --git a/Todo.txt b/Todo.txt index 9be6445f3..5f8b254bf 100644 --- a/Todo.txt +++ b/Todo.txt @@ -38,7 +38,7 @@ Link Validation + exactly one main + ES 3.0: fragment outputs all have locations, if more than one + location aliasing/overlap (except desktop vertex shader inputs) - - Non ES: binding overlap for atomic counters + + binding overlap for atomic counters + Non ES: geometry shader input array sizes and input layout qualifier declaration + Non ES: read or write to both gl_ClipVertex and gl_ClipDistance + Non ES: write to only one of gl_FragColor, gl_FragData, or user-declared @@ -56,9 +56,10 @@ Link Validation + value checking of uniform initializers + location match - block matching - - component/binding/index/offset match check + + component/binding/index/offset match check + compute shader layout(local_size_*) matching + mixed es/non-es profiles are an error + - binding overlap for atomic counters - matching redeclarations of interface blocks - 4.3: implicit array sizing is cross shader within a stage - 4.4: If gl_FragCoord is redeclared in any fragment shader in a program, it must be redeclared in all the fragment shaders in that program that have a static use gl_FragCoord @@ -80,9 +81,7 @@ Shader Functionality to Implement/Finish + Texture gather + Bitfield operations + Integer mix function - - overlapping bindings - - offset post incrementing - - overlapping offsets + + overlapping bindings/offsets and offset post increment + frexp/ldexp + packUnorm4x8(),packSnorm4x8(), unpackUnorm4x8(), unpackSnorm4x8() + 2DMS samplers and images @@ -202,7 +201,7 @@ Shader Functionality to Implement/Finish + Parameter qualifiers can include precision and memory qualifiers. + Add a new atomic_uint type to support atomic counters. Also, add built-in functions for manipulating atomic counters. + atomicCounterIncrement, atomicCounterDecrement, and atomicCounter - - Add layout qualifier identifiers binding and offset to bind units to sampler and image variable declarations, atomic counters, and uniform blocks. + + Add layout qualifier identifiers binding and offset to bind units to sampler and image variable declarations, atomic counters, and uniform blocks. + Add built-in functions to pack/unpack 16 bit floating-point numbers (ARB_shading_language_pack2f). + packHalf2x16 and unpackHalf2x16 + packSnorm2x16and unpackSnorm2x16 diff --git a/glslang/MachineIndependent/ParseHelper.cpp b/glslang/MachineIndependent/ParseHelper.cpp index 9abd95805..b19e1e44c 100644 --- a/glslang/MachineIndependent/ParseHelper.cpp +++ b/glslang/MachineIndependent/ParseHelper.cpp @@ -54,7 +54,7 @@ TParseContext::TParseContext(TSymbolTable& symt, TIntermediate& interm, bool pb, contextPragma(true, false), loopNestingLevel(0), controlFlowNestingLevel(0), structNestingLevel(0), tokensBeforeEOF(false), limits(resources.limits), currentScanner(0), numErrors(0), parsingBuiltins(pb), afterEOF(false), - anyIndexLimits(false) + atomicUintOffsets(0), anyIndexLimits(false) { // ensure we always have a linkage node, even if empty, to simplify tree topology algorithms linkage = new TIntermAggregate; @@ -103,7 +103,6 @@ TParseContext::TParseContext(TSymbolTable& symt, TIntermediate& interm, bool pb, globalBufferDefaults.layoutPacking = ElpShared; globalInputDefaults.clear(); - globalOutputDefaults.clear(); // "Shaders in the transform @@ -119,6 +118,11 @@ TParseContext::TParseContext(TSymbolTable& symt, TIntermediate& interm, bool pb, globalOutputDefaults.layoutStream = 0; } +TParseContext::~TParseContext() +{ + delete [] atomicUintOffsets; +} + void TParseContext::setLimits(const TBuiltInResource& r) { resources = r; @@ -131,6 +135,13 @@ void TParseContext::setLimits(const TBuiltInResource& r) ! limits.generalVaryingIndexing; intermediate.setLimits(resources); + + // "Each binding point tracks its own current default offset for + // inheritance of subsequent variables using the same binding. The initial state of compilation is that all + // binding points have an offset of 0." + atomicUintOffsets = new int[resources.maxAtomicCounterBindings]; + for (int b = 0; b < resources.maxAtomicCounterBindings; ++b) + atomicUintOffsets[b] = 0; } // @@ -3388,10 +3399,6 @@ void TParseContext::layoutObjectCheck(TSourceLoc loc, const TSymbol& symbol) // "The offset qualifier can only be used on block members of blocks..." if (qualifier.hasOffset() && type.getBasicType() != EbtAtomicUint) error(loc, "cannot specify on a variable declaration", "offset", ""); - if (qualifier.hasOffset() && ! qualifier.hasBinding() && type.getBasicType() == EbtAtomicUint) - error(loc, "a binding is required", "offset", ""); - if (qualifier.hasBinding() && (int)qualifier.layoutBinding >= resources.maxAtomicCounterBindings && type.getBasicType() == EbtAtomicUint) - error(loc, "cannot be greater-than-or-equal to gl_MaxAtomicCounterBindings", "binding", ""); // "The align qualifier can only be used on blocks or block members..." if (qualifier.hasAlign()) error(loc, "cannot specify on a variable declaration", "align", ""); @@ -3492,6 +3499,18 @@ void TParseContext::layoutTypeCheck(TSourceLoc loc, const TType& type) if (lastBinding >= resources.maxCombinedTextureImageUnits) error(loc, "sampler binding not less than gl_MaxCombinedTextureImageUnits", "binding", type.isArray() ? "(using array)" : ""); } + if (type.getBasicType() == EbtAtomicUint) { + if (qualifier.layoutBinding >= (unsigned int)resources.maxAtomicCounterBindings) { + error(loc, "atomic_uint binding is too large; see gl_MaxAtomicCounterBindings", "binding", ""); + return; + } + } + } + + // atomic_uint + if (type.getBasicType() == EbtAtomicUint) { + if (! type.getQualifier().hasBinding()) + error(loc, "layout(binding=X) is required", "atomic_uint", ""); } // "The offset qualifier can only be used on block members of blocks..." @@ -3633,6 +3652,35 @@ void TParseContext::checkNoShaderLayouts(TSourceLoc loc, const TShaderQualifiers } } +// Correct and/or advance an object's offset layout qualifier. +void TParseContext::fixOffset(TSourceLoc loc, TSymbol& symbol) +{ + const TQualifier& qualifier = symbol.getType().getQualifier(); + if (symbol.getType().getBasicType() == EbtAtomicUint) { + if (qualifier.hasBinding() && (int)qualifier.layoutBinding < resources.maxAtomicCounterBindings) { + + // Set the offset + int offset; + if (qualifier.hasOffset()) + offset = qualifier.layoutOffset; + else + offset = atomicUintOffsets[qualifier.layoutBinding]; + symbol.getWritableType().getQualifier().layoutOffset = offset; + + // Check for overlap + int numOffsets = 4; + if (symbol.getType().isArray()) + numOffsets *= symbol.getType().getArraySize(); + int repeated = intermediate.addUsedOffsets(qualifier.layoutBinding, offset, numOffsets); + if (repeated >= 0) + error(loc, "atomic counters sharing the same offset:", "offset", "%d", repeated); + + // Bump the default offset + atomicUintOffsets[qualifier.layoutBinding] = offset + numOffsets; + } + } +} + // // Look up a function name in the symbol table, and make sure it is a function. // @@ -3748,6 +3796,23 @@ const TFunction* TParseContext::findFunction400(TSourceLoc loc, const TFunction& return findFunction120(loc, call, builtIn); } +// When a declaration includes a type, but not a variable name, it can be +// to establish defaults. +void TParseContext::declareTypeDefaults(TSourceLoc loc, const TPublicType& publicType) +{ + if (publicType.basicType == EbtAtomicUint && publicType.qualifier.hasBinding() && publicType.qualifier.hasOffset()) { + if (publicType.qualifier.layoutBinding >= (unsigned int)resources.maxAtomicCounterBindings) { + error(loc, "atomic_uint binding is too large", "binding", ""); + return; + } + atomicUintOffsets[publicType.qualifier.layoutBinding] = publicType.qualifier.layoutOffset; + return; + } + + if (publicType.qualifier.hasLayout()) + warn(loc, "useless application of layout qualifier", "layout", ""); +} + // // Do everything necessary to handle a variable (non-block) declaration. // Either redeclaring a variable, or making a new one, updating the symbol @@ -3826,8 +3891,9 @@ TIntermNode* TParseContext::declareVariable(TSourceLoc loc, TString& identifier, initNode = executeInitializer(loc, identifier, initializer, variable); } - // look for errors/adjustments in layout qualifier use + // look for errors in layout qualifier use layoutObjectCheck(loc, *symbol); + fixOffset(loc, *symbol); // see if it's a linker-level object to track if (newDeclaration && symbolTable.atGlobalLevel()) diff --git a/glslang/MachineIndependent/ParseHelper.h b/glslang/MachineIndependent/ParseHelper.h index b129e60fe..0d1f018a9 100644 --- a/glslang/MachineIndependent/ParseHelper.h +++ b/glslang/MachineIndependent/ParseHelper.h @@ -64,6 +64,7 @@ class TParseContext { public: TParseContext(TSymbolTable&, TIntermediate&, bool parsingBuiltins, int version, EProfile, EShLanguage, TInfoSink&, bool forwardCompatible = false, EShMessages messages = EShMsgDefault); + virtual ~TParseContext(); void setLimits(const TBuiltInResource&); bool parseShaderStrings(TPpContext&, TInputScanner& input, bool versionWillBeError = false); @@ -159,11 +160,13 @@ public: void layoutTypeCheck(TSourceLoc, const TType&); void layoutQualifierCheck(TSourceLoc, const TQualifier&); void checkNoShaderLayouts(TSourceLoc, const TShaderQualifiers&); + void fixOffset(TSourceLoc, TSymbol&); const TFunction* findFunction(TSourceLoc loc, const TFunction& call, bool& builtIn); const TFunction* findFunctionExact(TSourceLoc loc, const TFunction& call, bool& builtIn); const TFunction* findFunction120(TSourceLoc loc, const TFunction& call, bool& builtIn); const TFunction* findFunction400(TSourceLoc loc, const TFunction& call, bool& builtIn); + void declareTypeDefaults(TSourceLoc, const TPublicType&); TIntermNode* declareVariable(TSourceLoc, TString& identifier, const TPublicType&, TArraySizes* typeArray = 0, TIntermTyped* initializer = 0); TIntermTyped* addConstructor(TSourceLoc, TIntermNode*, const TType&, TOperator); TIntermTyped* constructStruct(TIntermNode*, const TType&, int, TSourceLoc); @@ -265,6 +268,7 @@ protected: TQualifier globalUniformDefaults; TQualifier globalInputDefaults; TQualifier globalOutputDefaults; + int* atomicUintOffsets; // to become an array of the right size to hold an offset per binding point TString currentCaller; TIdSetType inductiveLoopIds; bool anyIndexLimits; diff --git a/glslang/MachineIndependent/glslang.y b/glslang/MachineIndependent/glslang.y index c71c6e213..f2e25b3ba 100644 --- a/glslang/MachineIndependent/glslang.y +++ b/glslang/MachineIndependent/glslang.y @@ -966,8 +966,7 @@ single_declaration : fully_specified_type { $$.type = $1; $$.intermNode = 0; - if ($$.type.qualifier.hasLayout()) - parseContext.warn($1.loc, "useless application of layout qualifier", "layout", ""); + parseContext.declareTypeDefaults($$.loc, $$.type); } | fully_specified_type IDENTIFIER { $$.type = $1; diff --git a/glslang/MachineIndependent/linkValidate.cpp b/glslang/MachineIndependent/linkValidate.cpp index d183a65f2..72e2a83bb 100644 --- a/glslang/MachineIndependent/linkValidate.cpp +++ b/glslang/MachineIndependent/linkValidate.cpp @@ -321,7 +321,8 @@ void TIntermediate::mergeErrorCheck(TInfoSink& infoSink, const TIntermSymbol& sy symbol.getQualifier().layoutLocation != unitSymbol.getQualifier().layoutLocation || symbol.getQualifier().layoutComponent != unitSymbol.getQualifier().layoutComponent || symbol.getQualifier().layoutIndex != unitSymbol.getQualifier().layoutIndex || - symbol.getQualifier().layoutBinding != unitSymbol.getQualifier().layoutBinding) { + symbol.getQualifier().layoutBinding != unitSymbol.getQualifier().layoutBinding || + (symbol.getQualifier().hasBinding() && (symbol.getQualifier().layoutOffset != unitSymbol.getQualifier().layoutOffset))) { error(infoSink, "Layout qualification must match:"); writeTypeComparison = true; } @@ -661,6 +662,30 @@ int TIntermediate::addUsedLocation(const TQualifier& qualifier, const TType& typ return -1; // no collision } +// Accumulate locations used for inputs, outputs, and uniforms, and check for collisions +// as the accumulation is done. +// +// Returns < 0 if no collision, >= 0 if collision and the value returned is a colliding value. +// +int TIntermediate::addUsedOffsets(int binding, int offset, int numOffsets) +{ + TRange bindingRange(binding, binding); + TRange offsetRange(offset, offset + numOffsets - 1); + TOffsetRange range(bindingRange, offsetRange); + + // check for collisions, except for vertex inputs on desktop + for (size_t r = 0; r < usedAtomics.size(); ++r) { + if (range.overlap(usedAtomics[r])) { + // there is a collision; pick one + return std::max(offset, usedAtomics[r].offset.start); + } + } + + usedAtomics.push_back(range); + + return -1; // no collision +} + // Recursively figure out how many locations are used up by an input or output type. // Return the size of type, as measured by "locations". int TIntermediate::computeTypeLocationSize(const TType& type) const diff --git a/glslang/MachineIndependent/localintermediate.h b/glslang/MachineIndependent/localintermediate.h index d762efec8..133139533 100644 --- a/glslang/MachineIndependent/localintermediate.h +++ b/glslang/MachineIndependent/localintermediate.h @@ -93,6 +93,19 @@ struct TIoRange { int index; }; +// An IO range is a 2-D rectangle; the set of (binding, offset) pairs all lying +// within the same binding and offset range. +struct TOffsetRange { + TOffsetRange(TRange binding, TRange offset) + : binding(binding), offset(offset) { } + bool overlap(const TOffsetRange& rhs) const + { + return binding.overlap(rhs.binding) && offset.overlap(rhs.offset); + } + TRange binding; + TRange offset; +}; + // Things that need to be tracked per xfb buffer. struct TXfbBuffer { TXfbBuffer() : stride(TQualifier::layoutXfbStrideEnd), implicitStride(0), containsDouble(false) { } @@ -269,6 +282,7 @@ public: bool inIoAccessed(const TString& name) const { return ioAccessed.find(name) != ioAccessed.end(); } int addUsedLocation(const TQualifier&, const TType&, bool& typeCollision); + int addUsedOffsets(int binding, int offset, int numOffsets); int computeTypeLocationSize(const TType&) const; bool setXfbBufferStride(int buffer, int stride) @@ -320,9 +334,10 @@ protected: typedef std::list<TCall> TGraph; TGraph callGraph; - std::set<TString> ioAccessed; // set of names of statically read/written I/O that might need extra checking - std::vector<TIoRange> usedIo[4]; // sets of used locations, one for each of in, out, uniform and buffers - std::vector<TXfbBuffer> xfbBuffers; // all the data we need to track per xfb buffer + std::set<TString> ioAccessed; // set of names of statically read/written I/O that might need extra checking + std::vector<TIoRange> usedIo[4]; // sets of used locations, one for each of in, out, uniform, and buffers + std::vector<TOffsetRange> usedAtomics; // sets of bindings used by atomic counters + std::vector<TXfbBuffer> xfbBuffers; // all the data we need to track per xfb buffer private: void operator=(TIntermediate&); // prevent assignments -- GitLab