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