ES31: Add atomic counter for GLSL parsing

This makes shader compiler support the new basic type 'atomic_uint'
and validate its layout qualifiers properly.

BUG=angleproject:1729
TEST=angle_unittests:AtomicCounterTest
     angle_deqp_gles31_tests:dEQP-GLES31.functional.atomic_counter.layout.invalid*
     angle_deqp_gles31_tests:dEQP-GLES31.functional.debug.negative_coverage.*.atomic*

Change-Id: Ia237eadf6ea72314f436a0abbb93a05598e71eba
Reviewed-on: https://chromium-review.googlesource.com/500088
Commit-Queue: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
diff --git a/src/compiler/translator/ParseContext.cpp b/src/compiler/translator/ParseContext.cpp
index 9b84a3f..f3bd179 100644
--- a/src/compiler/translator/ParseContext.cpp
+++ b/src/compiler/translator/ParseContext.cpp
@@ -9,11 +9,12 @@
 #include <stdarg.h>
 #include <stdio.h>
 
+#include "common/mathutil.h"
 #include "compiler/preprocessor/SourceLocation.h"
 #include "compiler/translator/Cache.h"
-#include "compiler/translator/glslang.h"
-#include "compiler/translator/ValidateSwitch.h"
 #include "compiler/translator/ValidateGlobalInitializer.h"
+#include "compiler/translator/ValidateSwitch.h"
+#include "compiler/translator/glslang.h"
 #include "compiler/translator/util.h"
 
 namespace sh
@@ -68,6 +69,38 @@
 
 }  // namespace
 
+// This tracks each binding point's current default offset for inheritance of subsequent
+// variables using the same binding, and keeps offsets unique and non overlapping.
+// See GLSL ES 3.1, section 4.4.6.
+class TParseContext::AtomicCounterBindingState
+{
+  public:
+    AtomicCounterBindingState() : mDefaultOffset(0) {}
+    // Inserts a new span and returns -1 if overlapping, else returns the starting offset of
+    // newly inserted span.
+    int insertSpan(int start, size_t length)
+    {
+        gl::RangeI newSpan(start, start + static_cast<int>(length));
+        for (const auto &span : mSpans)
+        {
+            if (newSpan.intersects(span))
+            {
+                return -1;
+            }
+        }
+        mSpans.push_back(newSpan);
+        mDefaultOffset = newSpan.high();
+        return start;
+    }
+    // Inserts a new span starting from the default offset.
+    int appendSpan(size_t length) { return insertSpan(mDefaultOffset, length); }
+    void setDefaultOffset(int offset) { mDefaultOffset = offset; }
+
+  private:
+    int mDefaultOffset;
+    std::vector<gl::RangeI> mSpans;
+};
+
 TParseContext::TParseContext(TSymbolTable &symt,
                              TExtensionBehavior &ext,
                              sh::GLenum type,
@@ -114,11 +147,16 @@
       mMaxCombinedTextureImageUnits(resources.MaxCombinedTextureImageUnits),
       mMaxUniformLocations(resources.MaxUniformLocations),
       mMaxUniformBufferBindings(resources.MaxUniformBufferBindings),
+      mMaxAtomicCounterBindings(resources.MaxAtomicCounterBindings),
       mDeclaringFunction(false)
 {
     mComputeShaderLocalSize.fill(-1);
 }
 
+TParseContext::~TParseContext()
+{
+}
+
 //
 // Look at a '.' field selector string and change it into offsets
 // for a vector.
@@ -1107,6 +1145,24 @@
     }
 }
 
+void TParseContext::atomicCounterQualifierErrorCheck(const TPublicType &publicType,
+                                                     const TSourceLoc &location)
+{
+    if (publicType.precision != EbpHigh)
+    {
+        error(location, "Can only be highp", "atomic counter");
+    }
+    // dEQP enforces compile error if location is specified. See uniform_location.test.
+    if (publicType.layoutQualifier.location != -1)
+    {
+        error(location, "location must not be set for atomic_uint", "layout");
+    }
+    if (publicType.layoutQualifier.binding == -1)
+    {
+        error(location, "no binding specified", "atomic counter");
+    }
+}
+
 void TParseContext::emptyDeclarationErrorCheck(const TPublicType &publicType,
                                                const TSourceLoc &location)
 {
@@ -1244,9 +1300,17 @@
     else
     {
         checkInternalFormatIsNotSpecified(identifierLocation, layoutQualifier.imageInternalFormat);
-
         checkMemoryQualifierIsNotSpecified(publicType.memoryQualifier, identifierLocation);
     }
+
+    if (IsAtomicCounter(publicType.getBasicType()))
+    {
+        atomicCounterQualifierErrorCheck(publicType, identifierLocation);
+    }
+    else
+    {
+        checkOffsetIsNotSpecified(identifierLocation, layoutQualifier.offset);
+    }
 }
 
 void TParseContext::checkBindingIsValid(const TSourceLoc &identifierLocation, const TType &type)
@@ -1261,6 +1325,10 @@
     {
         checkSamplerBindingIsValid(identifierLocation, layoutQualifier.binding, arraySize);
     }
+    else if (IsAtomicCounter(type.getBasicType()))
+    {
+        checkAtomicCounterBindingIsValid(identifierLocation, layoutQualifier.binding);
+    }
     else
     {
         ASSERT(!IsOpaqueType(type.getBasicType()));
@@ -1318,6 +1386,15 @@
     }
 }
 
+void TParseContext::checkOffsetIsNotSpecified(const TSourceLoc &location, int offset)
+{
+    if (offset != -1)
+    {
+        error(location, "invalid layout qualifier: only valid when used with atomic counters",
+              "offset");
+    }
+}
+
 void TParseContext::checkImageBindingIsValid(const TSourceLoc &location, int binding, int arraySize)
 {
     // Expects arraySize to be 1 when setting binding for only a single variable.
@@ -1347,6 +1424,14 @@
               "binding");
     }
 }
+void TParseContext::checkAtomicCounterBindingIsValid(const TSourceLoc &location, int binding)
+{
+    if (binding >= mMaxAtomicCounterBindings)
+    {
+        error(location, "atomic counter binding greater than gl_MaxAtomicCounterBindings",
+              "binding");
+    }
+}
 
 void TParseContext::checkUniformLocationInRange(const TSourceLoc &location,
                                                 int objectLocationCount,
@@ -1909,6 +1994,34 @@
     }
 }
 
+// Make sure there is no offset overlapping, and store the newly assigned offset to "type" in
+// intermediate tree.
+void TParseContext::checkAtomicCounterOffsetIsNotOverlapped(TPublicType &publicType,
+                                                            size_t size,
+                                                            bool forceAppend,
+                                                            const TSourceLoc &loc,
+                                                            TType &type)
+{
+    auto &bindingState = mAtomicCounterBindingStates[publicType.layoutQualifier.binding];
+    int offset;
+    if (publicType.layoutQualifier.offset == -1 || forceAppend)
+    {
+        offset = bindingState.appendSpan(size);
+    }
+    else
+    {
+        offset = bindingState.insertSpan(publicType.layoutQualifier.offset, size);
+    }
+    if (offset == -1)
+    {
+        error(loc, "Offset overlapping", "atomic counter");
+        return;
+    }
+    TLayoutQualifier qualifier = type.getLayoutQualifier();
+    qualifier.offset           = offset;
+    type.setLayoutQualifier(qualifier);
+}
+
 TIntermDeclaration *TParseContext::parseSingleDeclaration(
     TPublicType &publicType,
     const TSourceLoc &identifierOrTypeLocation,
@@ -1958,6 +2071,10 @@
         {
             symbol = intermediate.addSymbol(0, "", type, identifierOrTypeLocation);
         }
+        else if (IsAtomicCounter(publicType.getBasicType()))
+        {
+            setAtomicCounterBindingDefaultOffset(publicType, identifierOrTypeLocation);
+        }
     }
     else
     {
@@ -1965,6 +2082,13 @@
 
         checkCanBeDeclaredWithoutInitializer(identifierOrTypeLocation, identifier, &publicType);
 
+        if (IsAtomicCounter(publicType.getBasicType()))
+        {
+
+            checkAtomicCounterOffsetIsNotOverlapped(publicType, kAtomicCounterSize, false,
+                                                    identifierOrTypeLocation, type);
+        }
+
         TVariable *variable = nullptr;
         declareVariable(identifierOrTypeLocation, identifier, type, &variable);
 
@@ -2008,6 +2132,12 @@
     // This ensures useless error messages regarding the variable's non-arrayness won't follow.
     arrayType.setArraySize(size);
 
+    if (IsAtomicCounter(publicType.getBasicType()))
+    {
+        checkAtomicCounterOffsetIsNotOverlapped(publicType, kAtomicCounterArrayStride * size, false,
+                                                identifierLocation, arrayType);
+    }
+
     TVariable *variable = nullptr;
     declareVariable(identifierLocation, identifier, arrayType, &variable);
 
@@ -2170,6 +2300,11 @@
 
     TVariable *variable = nullptr;
     TType type(publicType);
+    if (IsAtomicCounter(publicType.getBasicType()))
+    {
+        checkAtomicCounterOffsetIsNotOverlapped(publicType, kAtomicCounterSize, true,
+                                                identifierLocation, type);
+    }
     declareVariable(identifierLocation, identifier, type, &variable);
 
     TIntermSymbol *symbol = intermediate.addSymbol(0, identifier, type, identifierLocation);
@@ -2205,6 +2340,12 @@
         unsigned int size = checkIsValidArraySize(arrayLocation, indexExpression);
         arrayType.setArraySize(size);
 
+        if (IsAtomicCounter(publicType.getBasicType()))
+        {
+            checkAtomicCounterOffsetIsNotOverlapped(publicType, kAtomicCounterArrayStride * size,
+                                                    true, identifierLocation, arrayType);
+        }
+
         TVariable *variable = nullptr;
         declareVariable(identifierLocation, identifier, arrayType, &variable);
 
@@ -2292,6 +2433,19 @@
     }
 }
 
+void TParseContext::setAtomicCounterBindingDefaultOffset(const TPublicType &publicType,
+                                                         const TSourceLoc &location)
+{
+    const TLayoutQualifier &layoutQualifier = publicType.layoutQualifier;
+    checkAtomicCounterBindingIsValid(location, layoutQualifier.binding);
+    if (layoutQualifier.binding == -1 || layoutQualifier.offset == -1)
+    {
+        error(location, "Requires both binding and offset", "layout");
+        return;
+    }
+    mAtomicCounterBindingStates[layoutQualifier.binding].setDefaultOffset(layoutQualifier.offset);
+}
+
 void TParseContext::parseGlobalLayoutQualifier(const TTypeQualifierBuilder &typeQualifierBuilder)
 {
     TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(mDiagnostics);
@@ -2321,6 +2475,8 @@
 
     checkYuvIsNotSpecified(typeQualifier.line, layoutQualifier.yuv);
 
+    checkOffsetIsNotSpecified(typeQualifier.line, layoutQualifier.offset);
+
     if (typeQualifier.qualifier == EvqComputeIn)
     {
         if (mComputeShaderLocalSizeDeclared &&
@@ -3423,6 +3579,19 @@
             qualifier.binding = intValue;
         }
     }
+    else if (qualifierType == "offset")
+    {
+        checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
+        if (intValue < 0)
+        {
+            error(intValueLine, "out of range: offset must be non-negative",
+                  intValueString.c_str());
+        }
+        else
+        {
+            qualifier.offset = intValue;
+        }
+    }
     else if (qualifierType == "local_size_x")
     {
         parseLocalSize(qualifierType, qualifierTypeLine, intValue, intValueLine, intValueString, 0u,
@@ -3586,7 +3755,8 @@
         {
             error(field.line(), "invalid qualifier on struct member", "invariant");
         }
-        if (IsImage(field.type()->getBasicType()))
+        // ESSL 3.10 section 4.1.8 -- atomic_uint or images are not allowed as structure member.
+        if (IsImage(field.type()->getBasicType()) || IsAtomicCounter(field.type()->getBasicType()))
         {
             error(field.line(), "disallowed type in struct", field.type()->getBasicString());
         }