Finish implementing compute shaders, within #version 430, partly based on a submission.


git-svn-id: https://cvs.khronos.org/svn/repos/ogl/trunk/ecosystem/public/sdk/tools/glslang@27674 e7fa87d3-cd2b-0410-9028-fcbf551c1848
diff --git a/Test/430.comp b/Test/430.comp
index 7905454..13a812d 100644
--- a/Test/430.comp
+++ b/Test/430.comp
@@ -1,12 +1,38 @@
 #version 430 core

 

+layout(local_size_x = 2) in;

+layout(local_size_x = 16) in;     // ERROR, changing

+layout(local_size_z = 4096) in;   // ERROR, too large

+layout(local_size_x = 2) in;

+

+const int total = gl_MaxComputeWorkGroupCount.y 

+                + gl_MaxComputeUniformComponents

+                + gl_MaxComputeTextureImageUnits

+                + gl_MaxComputeImageUniforms

+                + gl_MaxComputeAtomicCounters

+                + gl_MaxComputeAtomicCounterBuffers;

+

 void main()

 {

-	memoryBarrierAtomicCounter();

-	memoryBarrierBuffer();

-	memoryBarrierShared();

-	memoryBarrierImage();

-	groupMemoryBarrier();

+    barrier();

+    memoryBarrier();

+    memoryBarrierAtomicCounter();

+    memoryBarrierBuffer();

+    memoryBarrierShared();

+    memoryBarrierImage();

+    groupMemoryBarrier();

 }

 

-layout(location = 2) in vec3 v3;

+layout(location = 2) in vec3 v3;      // ERROR

+in float f;                           // ERROR

+out float fo;                         // ERROR

+

+shared vec4 s;

+layout(location = 2) shared vec4 sl;  // ERROR

+shared float fs = 4.2;                // ERROR

+

+layout(local_size_x = 2, local_size_y = 3, local_size_z = 4) out;  // ERROR

+

+int arrX[gl_WorkGroupSize.x];

+int arrY[gl_WorkGroupSize.y];

+int arrZ[gl_WorkGroupSize.z];

diff --git a/Test/430.vert b/Test/430.vert
index 145aabf..399a895 100644
--- a/Test/430.vert
+++ b/Test/430.vert
@@ -142,3 +142,16 @@
     layout(xfb_offset=0) vec4 bbv1;

     layout(xfb_stride=80, xfb_buffer=1, xfb_offset=64) vec4 bbv2;

 } bbinst5;

+

+shared vec4 sharedv;

+

+void fooBarrier()

+{

+    barrier();                       // ERROR

+    memoryBarrier();

+    memoryBarrierAtomicCounter();

+    memoryBarrierBuffer();

+    memoryBarrierShared();           // ERROR

+    memoryBarrierImage();

+    groupMemoryBarrier();            // ERROR

+}

diff --git a/Test/baseResults/300layout.vert.out b/Test/baseResults/300layout.vert.out
index f954252..52de07b 100644
--- a/Test/baseResults/300layout.vert.out
+++ b/Test/baseResults/300layout.vert.out
@@ -15,10 +15,9 @@
 ERROR: 0:42: 'location qualifier on output' : not supported in this stage: vertex

 ERROR: 0:50: 'shared' : not supported with this profile: es

 ERROR: 0:50: 'shared' : not supported in this stage: vertex

-ERROR: 0:50: '' : memory qualifiers can only be used on image types 

 ERROR: 0:54: 'layout' : cannot specify packing on a variable declaration 

 ERROR: 0:57: 'location' : overlapping use of location 40

-ERROR: 19 compilation errors.  No code generated.

+ERROR: 18 compilation errors.  No code generated.

 

 

 Shader version: 300

diff --git a/Test/baseResults/430.comp.out b/Test/baseResults/430.comp.out
index d8f981a..4e3f8fd 100644
--- a/Test/baseResults/430.comp.out
+++ b/Test/baseResults/430.comp.out
@@ -1,36 +1,81 @@
 430.comp

 Warning, version 430 is not yet complete; most version-specific features are present, but some are missing.

-ERROR: 0:12: 'location qualifier on input' : not supported in this stage: compute

-ERROR: 1 compilation errors.  No code generated.

+ERROR: 0:4: 'local_size' : cannot change previously set size 

+ERROR: 0:5: 'local_size' : too large; see gl_MaxComputeWorkGroupSize 

+ERROR: 0:26: 'in' : global storage input qualifier cannot be used in a compute shader 

+ERROR: 0:26: 'location qualifier on input' : not supported in this stage: compute

+ERROR: 0:27: 'in' : global storage input qualifier cannot be used in a compute shader 

+ERROR: 0:28: 'out' : global storage output qualifier cannot be used in a compute shader 

+ERROR: 0:31: 'shared' : cannot apply layout qualifiers to a shared variable 

+ERROR: 0:31: 'location' : can only appy to uniform, buffer, in, or out storage qualifiers 

+ERROR: 0:32: 'shared' :  cannot initialize this type of qualifier  

+ERROR: 0:34: 'local_size' : can only apply to 'in' 

+ERROR: 0:34: 'local_size' : can only apply to 'in' 

+ERROR: 0:34: 'local_size' : can only apply to 'in' 

+ERROR: 12 compilation errors.  No code generated.

 

 

 Shader version: 430

+local_size = (2, 1, 4096)

 ERROR: node is still EOpNull!

-0:3  Function Definition: main( (void)

-0:3    Function Parameters: 

-0:5    Sequence

-0:5      MemoryBarrierAtomicCounter (void)

-0:6      MemoryBarrierBuffer (void)

-0:7      MemoryBarrierShared (void)

-0:8      MemoryBarrierImage (void)

-0:9      GroupMemoryBarrier (void)

+0:15  Function Definition: main( (void)

+0:15    Function Parameters: 

+0:17    Sequence

+0:17      Barrier (void)

+0:18      MemoryBarrier (void)

+0:19      MemoryBarrierAtomicCounter (void)

+0:20      MemoryBarrierBuffer (void)

+0:21      MemoryBarrierShared (void)

+0:22      MemoryBarrierImage (void)

+0:23      GroupMemoryBarrier (void)

 0:?   Linker Objects

+0:?     'gl_WorkGroupSize' (const 3-component vector of uint)

+0:?       2 (const uint)

+0:?       1 (const uint)

+0:?       4096 (const uint)

+0:?     'total' (const int)

+0:?       66592 (const int)

 0:?     'v3' (layout(location=2 ) in 3-component vector of float)

+0:?     'f' (in float)

+0:?     'fo' (out float)

+0:?     's' (shared 4-component vector of float)

+0:?     'sl' (layout(location=2 ) shared 4-component vector of float)

+0:?     'fs' (shared float)

+0:?     'arrX' (2-element array of int)

+0:?     'arrY' (1-element array of int)

+0:?     'arrZ' (4096-element array of int)

 

 

 Linked compute stage:

 

 

 Shader version: 430

+local_size = (2, 1, 4096)

 ERROR: node is still EOpNull!

-0:3  Function Definition: main( (void)

-0:3    Function Parameters: 

-0:5    Sequence

-0:5      MemoryBarrierAtomicCounter (void)

-0:6      MemoryBarrierBuffer (void)

-0:7      MemoryBarrierShared (void)

-0:8      MemoryBarrierImage (void)

-0:9      GroupMemoryBarrier (void)

+0:15  Function Definition: main( (void)

+0:15    Function Parameters: 

+0:17    Sequence

+0:17      Barrier (void)

+0:18      MemoryBarrier (void)

+0:19      MemoryBarrierAtomicCounter (void)

+0:20      MemoryBarrierBuffer (void)

+0:21      MemoryBarrierShared (void)

+0:22      MemoryBarrierImage (void)

+0:23      GroupMemoryBarrier (void)

 0:?   Linker Objects

+0:?     'gl_WorkGroupSize' (const 3-component vector of uint)

+0:?       2 (const uint)

+0:?       1 (const uint)

+0:?       4096 (const uint)

+0:?     'total' (const int)

+0:?       66592 (const int)

 0:?     'v3' (layout(location=2 ) in 3-component vector of float)

+0:?     'f' (in float)

+0:?     'fo' (out float)

+0:?     's' (shared 4-component vector of float)

+0:?     'sl' (layout(location=2 ) shared 4-component vector of float)

+0:?     'fs' (shared float)

+0:?     'arrX' (2-element array of int)

+0:?     'arrY' (1-element array of int)

+0:?     'arrZ' (4096-element array of int)

 

diff --git a/Test/baseResults/430.vert.out b/Test/baseResults/430.vert.out
index 641bab2..4e8b1ef 100644
--- a/Test/baseResults/430.vert.out
+++ b/Test/baseResults/430.vert.out
@@ -44,7 +44,11 @@
 ERROR: 0:93: 'transform feedback qualifier' : not supported for this version or the enabled extensions 

 ERROR: 0:93: 'transform feedback qualifier' : not supported for this version or the enabled extensions 

 ERROR: 0:93: 'transform feedback qualifier' : not supported for this version or the enabled extensions 

-ERROR: 44 compilation errors.  No code generated.

+ERROR: 0:146: 'shared' : not supported in this stage: vertex

+ERROR: 0:150: 'barrier' : no matching overloaded function found 

+ERROR: 0:154: 'memoryBarrierShared' : no matching overloaded function found 

+ERROR: 0:156: 'groupMemoryBarrier' : no matching overloaded function found 

+ERROR: 48 compilation errors.  No code generated.

 

 

 Shader version: 430

@@ -70,6 +74,19 @@
 0:31      'v3' (volatile in 3-component vector of float)

 0:31      'v2' (in 2-component vector of float)

 0:31      'cv3' (in 3-component vector of float)

+0:148  Function Definition: fooBarrier( (void)

+0:148    Function Parameters: 

+0:150    Sequence

+0:150      Constant:

+0:150        0.000000

+0:151      MemoryBarrier (void)

+0:152      MemoryBarrierAtomicCounter (void)

+0:153      MemoryBarrierBuffer (void)

+0:154      Constant:

+0:154        0.000000

+0:155      MemoryBarrierImage (void)

+0:156      Constant:

+0:156        0.000000

 0:?   Linker Objects

 0:?     'v4' (layout(location=3 ) 4-component vector of float)

 0:?     'uv4' (layout(location=4 ) uniform 4-component vector of float)

@@ -109,6 +126,7 @@
 0:?     'bh' (layout(xfb_buffer=1 xfb_offset=32 xfb_stride=80 ) smooth out 4-component vector of float)

 0:?     'bbinst4' (layout(xfb_stride=80 ) out block{layout(xfb_buffer=1 xfb_offset=16 ) out 4-component vector of float bbv1})

 0:?     'bbinst5' (out block{layout(xfb_buffer=1 xfb_offset=0 ) out 4-component vector of float bbv1, layout(xfb_buffer=1 xfb_offset=64 xfb_stride=80 ) out 4-component vector of float bbv2})

+0:?     'sharedv' (shared 4-component vector of float)

 0:?     'gl_VertexID' (gl_VertexId int)

 0:?     'gl_InstanceID' (gl_InstanceId int)

 

@@ -142,6 +160,19 @@
 0:31      'v3' (volatile in 3-component vector of float)

 0:31      'v2' (in 2-component vector of float)

 0:31      'cv3' (in 3-component vector of float)

+0:148  Function Definition: fooBarrier( (void)

+0:148    Function Parameters: 

+0:150    Sequence

+0:150      Constant:

+0:150        0.000000

+0:151      MemoryBarrier (void)

+0:152      MemoryBarrierAtomicCounter (void)

+0:153      MemoryBarrierBuffer (void)

+0:154      Constant:

+0:154        0.000000

+0:155      MemoryBarrierImage (void)

+0:156      Constant:

+0:156        0.000000

 0:?   Linker Objects

 0:?     'v4' (layout(location=3 ) 4-component vector of float)

 0:?     'uv4' (layout(location=4 ) uniform 4-component vector of float)

@@ -181,6 +212,7 @@
 0:?     'bh' (layout(xfb_buffer=1 xfb_offset=32 xfb_stride=80 ) smooth out 4-component vector of float)

 0:?     'bbinst4' (layout(xfb_stride=80 ) out block{layout(xfb_buffer=1 xfb_offset=16 ) out 4-component vector of float bbv1})

 0:?     'bbinst5' (out block{layout(xfb_buffer=1 xfb_offset=0 ) out 4-component vector of float bbv1, layout(xfb_buffer=1 xfb_offset=64 xfb_stride=80 ) out 4-component vector of float bbv2})

+0:?     'sharedv' (shared 4-component vector of float)

 0:?     'gl_VertexID' (gl_VertexId int)

 0:?     'gl_InstanceID' (gl_InstanceId int)

 

diff --git a/Test/baseResults/specExamples.vert.out b/Test/baseResults/specExamples.vert.out
index 3b20d8f..9d02d02 100644
--- a/Test/baseResults/specExamples.vert.out
+++ b/Test/baseResults/specExamples.vert.out
@@ -40,7 +40,6 @@
 ERROR: 0:128: '' : memory qualifiers can only be used on image types 

 ERROR: 0:129: '' : memory qualifiers can only be used on image types 

 ERROR: 0:132: 'shared' : not supported in this stage: vertex

-ERROR: 0:132: '' : memory qualifiers can only be used on image types 

 ERROR: 0:134: '' : function does not return a value: funcA

 ERROR: 0:136: '' : function does not return a value: funcB

 ERROR: 0:153: '' : function does not return a value: func3

@@ -48,7 +47,7 @@
 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: 46 compilation errors.  No code generated.

+ERROR: 45 compilation errors.  No code generated.

 

 

 Shader version: 430

diff --git a/Todo.txt b/Todo.txt
index c4e1a70..1a6095c 100644
--- a/Todo.txt
+++ b/Todo.txt
@@ -57,10 +57,9 @@
       + location match
       - block matching
       - component/binding/index/offset match check
-      - compute shader layout(local_size_*) matching
+      + compute shader layout(local_size_*) matching
       + mixed es/non-es profiles are an error
       - matching redeclarations of interface blocks
-      - 4.3: early_fragment_tests contradictions
       - 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
 
@@ -211,7 +210,7 @@
             - Arrays of other objects (uniform blocks) containing implicitly sized arrays will have the same implicit size for all 
                 elements of the array.
       - Arrays of arrays are now supported, as per the GL_ARB_arrays_of_arrays extension.
-      - Compute shaders are now supported, as per the GL_ARB_compute_shader extension.
+      + Compute shaders are now supported, as per the GL_ARB_compute_shader extension.
       - Added imageSize() built-ins to query the dimensions of an image.
       - All choice of depth or stencil texturing, for a packed depth-stencil texture, as per the 
         GL_ARB_stencil_texturing extension.
diff --git a/glslang/Include/BaseTypes.h b/glslang/Include/BaseTypes.h
index 1690df8..5b9b2f5 100644
--- a/glslang/Include/BaseTypes.h
+++ b/glslang/Include/BaseTypes.h
@@ -69,6 +69,7 @@
     EvqVaryingOut,    // pipeline ouput, read/write
     EvqUniform,       // read only, shader with app
     EvqBuffer,        // read only, shader with app
+    EvqShared,        // compute shader's read/write 'shared' qualifier
     
     // parameters
     EvqIn,            // also, for 'in' in the grammar before we know if it's a pipeline input or an 'in' parameter
@@ -109,6 +110,8 @@
     case EvqVaryingIn:      return "in";             break;
     case EvqVaryingOut:     return "out";            break;
     case EvqUniform:        return "uniform";        break;
+    case EvqBuffer:         return "buffer";         break;
+    case EvqShared:         return "shared";         break;
     case EvqIn:             return "in";             break;
     case EvqOut:            return "out";            break;
     case EvqInOut:          return "inout";          break;
diff --git a/glslang/Include/Types.h b/glslang/Include/Types.h
index ef1dcd9..4d5c354 100644
--- a/glslang/Include/Types.h
+++ b/glslang/Include/Types.h
@@ -342,7 +342,7 @@
 
     bool isMemory() const
     {
-        return shared || coherent || volatil || restrict || readonly || writeonly;
+        return coherent || volatil || restrict || readonly || writeonly;
     }
     bool isInterpolation() const
     {
@@ -693,6 +693,7 @@
     TVertexSpacing spacing;
     TVertexOrder order;
     bool pointMode;
+    int localSize[3];         // compute shader
     bool earlyFragmentTests;  // fragment input
 
     void init()
@@ -705,6 +706,9 @@
         spacing = EvsNone;
         order = EvoNone;
         pointMode = false;
+        localSize[0] = 1;
+        localSize[1] = 1;
+        localSize[2] = 1;
         earlyFragmentTests = false;
     }
 
@@ -728,6 +732,10 @@
             order = src.order;
         if (src.pointMode)
             pointMode = true;
+        for (int i = 0; i < 3; ++i) {
+            if (src.localSize[i] > 1)
+                localSize[i] = src.localSize[i];
+        }
         if (src.earlyFragmentTests)
             earlyFragmentTests = true;
     }
diff --git a/glslang/MachineIndependent/Initialize.cpp b/glslang/MachineIndependent/Initialize.cpp
index 4ea7c96..d2b849b 100644
--- a/glslang/MachineIndependent/Initialize.cpp
+++ b/glslang/MachineIndependent/Initialize.cpp
@@ -1079,14 +1079,14 @@
     if (version >= 430) {
         stageBuiltins[EShLangCompute].append(
             "in uvec3 gl_NumWorkGroups;"
-            // TODO: 4.3 functionality: compute shader: constant with no initializer                "const uvec3 gl_WorkGroupSize;"
+            "const uvec3 gl_WorkGroupSize = uvec3(1,1,1);"
 
             "in uvec3 gl_WorkGroupID;"
             "in uvec3 gl_LocalInvocationID;"
 
             "in uvec3 gl_GlobalInvocationID;"
             "in uint gl_LocalInvocationIndex;"
-            
+
             "\n");
     }
 
diff --git a/glslang/MachineIndependent/ParseHelper.cpp b/glslang/MachineIndependent/ParseHelper.cpp
index 6187402..4da9aa7 100644
--- a/glslang/MachineIndependent/ParseHelper.cpp
+++ b/glslang/MachineIndependent/ParseHelper.cpp
@@ -1969,6 +1969,8 @@
             break;
 
         case EShLangCompute:
+            if (! symbolTable.atBuiltInLevel())
+                error(loc, "global storage input qualifier cannot be used in a compute shader", "in", "");
             break;
 
         default:
@@ -2002,9 +2004,10 @@
 
                 return;
             }
-            break;
+        break;
 
         case EShLangCompute:
+            error(loc, "global storage output qualifier cannot be used in a compute shader", "out", "");
             break;
 
         default:
@@ -3178,6 +3181,18 @@
         break;
 
     case EShLangCompute:
+        if (id == "local_size_x") {
+            publicType.shaderQualifiers.localSize[0] = value;
+            return;
+        }
+        if (id == "local_size_y") {
+            publicType.shaderQualifiers.localSize[1] = value;
+            return;
+        }
+        if (id == "local_size_z") {
+            publicType.shaderQualifiers.localSize[2] = value;
+            return;
+        }
         break;
 
     default:
@@ -3401,6 +3416,9 @@
 // if there are blocks, atomic counters, variables, etc.
 void TParseContext::layoutQualifierCheck(TSourceLoc loc, const TQualifier& qualifier)
 {
+    if (qualifier.storage == EvqShared && qualifier.hasLayout())
+        error(loc, "cannot apply layout qualifiers to a shared variable", "shared", "");
+
     // "It is a compile-time error to use *component* without also specifying the location qualifier (order does not matter)."
     if (qualifier.layoutComponent != TQualifier::layoutComponentEnd && qualifier.layoutLocation == TQualifier::layoutLocationEnd)
         error(loc, "must specify 'location' to use 'component'", "component", "");
@@ -3490,6 +3508,10 @@
         else
             assert(0);
     }
+    for (int i = 0; i < 3; ++i) {
+        if (shaderQualifiers.localSize[i] > 1)
+            error(loc, message, "local_size", "");
+    }
 }
 
 //
@@ -4561,6 +4583,34 @@
         else
             error(loc, "can only apply to 'in'", "point_mode", "");
     }
+    for (int i = 0; i < 3; ++i) {
+        if (publicType.shaderQualifiers.localSize[i] > 1) {
+            if (publicType.qualifier.storage == EvqVaryingIn) {
+                if (! intermediate.setLocalSize(i, publicType.shaderQualifiers.localSize[i]))
+                    error(loc, "cannot change previously set size", "local_size", "");
+                else {
+                    int max;
+                    switch (i) {
+                    case 0: max = resources.maxComputeWorkGroupSizeX; break;
+                    case 1: max = resources.maxComputeWorkGroupSizeY; break;
+                    case 2: max = resources.maxComputeWorkGroupSizeZ; break;
+                    default: break;
+                    }
+                    if (intermediate.getLocalSize(i) > (unsigned int)max)
+                        error(loc, "too large; see gl_MaxComputeWorkGroupSize", "local_size", "");
+
+                    // Fix the existing constant gl_WorkGroupSize with this new information.
+                    bool builtIn;
+                    TSymbol* symbol = symbolTable.find("gl_WorkGroupSize", &builtIn);
+                    if (builtIn)
+                        makeEditable(symbol);
+                    TVariable* workGroupSize = symbol->getAsVariable();
+                    workGroupSize->getWritableConstArray()[i].setUConst(intermediate.getLocalSize(i));
+                }
+            } else
+                error(loc, "can only apply to 'in'", "local_size", "");
+        }
+    }
     if (publicType.shaderQualifiers.earlyFragmentTests) {
         if (publicType.qualifier.storage == EvqVaryingIn)
             intermediate.setEarlyFragmentTests();
diff --git a/glslang/MachineIndependent/SymbolTable.cpp b/glslang/MachineIndependent/SymbolTable.cpp
index 9a2f3b2..6309b34 100644
--- a/glslang/MachineIndependent/SymbolTable.cpp
+++ b/glslang/MachineIndependent/SymbolTable.cpp
@@ -242,8 +242,7 @@
 
     if (! copyOf.unionArray.empty()) {
         assert(! copyOf.type.isStruct());
-        TConstUnionArray newArray(1);
-        newArray[0] = copyOf.unionArray[0];
+        TConstUnionArray newArray(copyOf.unionArray, 0, copyOf.unionArray.size());
         unionArray = newArray;
     }
 }
diff --git a/glslang/MachineIndependent/SymbolTable.h b/glslang/MachineIndependent/SymbolTable.h
index afc8a2c..c08569a 100644
--- a/glslang/MachineIndependent/SymbolTable.h
+++ b/glslang/MachineIndependent/SymbolTable.h
@@ -154,6 +154,7 @@
     virtual TType& getWritableType() { assert(writable); return type; }
     virtual bool isUserType() const { return userType; }
     virtual const TConstUnionArray& getConstArray() const { return unionArray; }
+    virtual TConstUnionArray& getWritableConstArray() { assert(writable); return unionArray; }
     virtual void setConstArray(const TConstUnionArray& constArray) { unionArray = constArray; }
 
     virtual void dump(TInfoSink &infoSink) const;
diff --git a/glslang/MachineIndependent/glslang.y b/glslang/MachineIndependent/glslang.y
index 912bd03..bc9ac87 100644
--- a/glslang/MachineIndependent/glslang.y
+++ b/glslang/MachineIndependent/glslang.y
@@ -1220,7 +1220,7 @@
         parseContext.profileRequires($1.loc, ECoreProfile, 430, 0, "shared");

         parseContext.requireStage($1.loc, EShLangCompute, "shared");

         $$.init($1.loc);

-        $$.qualifier.shared = true;

+        $$.qualifier.storage = EvqShared;

     }

     | COHERENT {

         $$.init($1.loc);

diff --git a/glslang/MachineIndependent/intermOut.cpp b/glslang/MachineIndependent/intermOut.cpp
index 8cc62f7..955f31a 100644
--- a/glslang/MachineIndependent/intermOut.cpp
+++ b/glslang/MachineIndependent/intermOut.cpp
@@ -429,7 +429,7 @@
         case EbtFloat:
         case EbtDouble:
             {
-				const int maxSize = 300;
+                const int maxSize = 300;
                 char buf[maxSize];
                 snprintf(buf, maxSize, "%f", constUnion[i].getDConst());
 
@@ -438,7 +438,7 @@
             break;
         case EbtInt:
             {
-				const int maxSize = 300;
+                const int maxSize = 300;
                 char buf[maxSize];
                 snprintf(buf, maxSize, "%d (%s)", constUnion[i].getIConst(), "const int");
 
@@ -447,7 +447,7 @@
             break;
         case EbtUint:
             {
-				const int maxSize = 300;
+                const int maxSize = 300;
                 char buf[maxSize];
                 snprintf(buf, maxSize, "%u (%s)", constUnion[i].getUConst(), "const uint");
 
@@ -616,9 +616,10 @@
         break;
 
     case EShLangCompute:
+        infoSink.debug << "local_size = (" << localSize[0] << ", " << localSize[1] << ", " << localSize[2] << ")\n";
         break;
 
-	default:
+    default:
         break;
     }
 
diff --git a/glslang/MachineIndependent/linkValidate.cpp b/glslang/MachineIndependent/linkValidate.cpp
index b53bc31..1c4d5b8 100644
--- a/glslang/MachineIndependent/linkValidate.cpp
+++ b/glslang/MachineIndependent/linkValidate.cpp
@@ -117,6 +117,13 @@
     if (unit.pointMode)

         pointMode = true;

 

+    for (int i = 0; i < 3; ++i) {

+        if (localSize[i] > 1)

+            localSize[i] = unit.localSize[i];

+        else if (localSize[i] != unit.localSize[i])

+            error(infoSink, "Contradictory local size");

+    }

+

     if (unit.xfbMode)

         xfbMode = true;

     for (size_t b = 0; b < xfbBuffers.size(); ++b) {

diff --git a/glslang/MachineIndependent/localintermediate.h b/glslang/MachineIndependent/localintermediate.h
index 00d7ba2..463aa86 100644
--- a/glslang/MachineIndependent/localintermediate.h
+++ b/glslang/MachineIndependent/localintermediate.h
@@ -114,6 +114,9 @@
         invocations(0), vertices(0), inputPrimitive(ElgNone), outputPrimitive(ElgNone), pixelCenterInteger(false), originUpperLeft(false),
         vertexSpacing(EvsNone), vertexOrder(EvoNone), pointMode(false), earlyFragmentTests(false), xfbMode(false)
     {
+        localSize[0] = 1;
+        localSize[1] = 1;
+        localSize[2] = 1;
         xfbBuffers.resize(TQualifier::layoutXfbBufferEnd);
     }
     void setLimits(const TBuiltInResource& r) { resources = r; }
@@ -221,6 +224,16 @@
     TVertexOrder getVertexOrder() const { return vertexOrder; }
     void setPointMode() { pointMode = true; }
     bool getPointMode() const { return pointMode; }
+    
+    bool setLocalSize(int dim, int size)
+    {
+        if (localSize[dim] > 1)
+            return size == localSize[dim];
+        localSize[dim] = size;
+        return true;
+    }
+    unsigned int getLocalSize(int dim) const { return localSize[dim]; }
+
     void setXfbMode() { xfbMode = true; }
     bool getXfbMode() const { return xfbMode; }
     bool setOutputPrimitive(TLayoutGeometry p)
@@ -289,6 +302,7 @@
     TVertexSpacing vertexSpacing;
     TVertexOrder vertexOrder;
     bool pointMode;
+    int localSize[3];
     bool earlyFragmentTests;
     bool xfbMode;