Implement location layout qualifier for uniforms

This is a complete implementation of the uniform location layout
qualifier. Uniform location set in the shader is plumbed to shader
linking, which does several link-time checks for conflicts and
recursively applies the location to struct members.

Validate that location is consistent as specified in the table in
section 9.2.1 of the ESSL 3.10.4 spec. The location set in the shader
overrides the one set via the CHROMIUM_bind_uniform_location API.

Location conflicts must be checked even if the uniforms are not
statically used. Because of this unused uniforms are now recorded
during uniform linking. After linking checks are done, unused uniforms
are pruned from the program state.

Location is validated against the maximum number of uniform locations
at compile time as specified in section 4.4.3 of the ESSL 3.10.4 spec.

All dEQP uniform location tests don't yet pass due to unrelated bugs.

BUG=angleproject:1442
TEST=angle_end2end_tests, dEQP-GLES31.functional.uniform_location.*

Change-Id: I1f968e971f521fbc804b01e1a7c2b4d14f24d20f
Reviewed-on: https://chromium-review.googlesource.com/447942
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
diff --git a/src/compiler/translator/ParseContext.cpp b/src/compiler/translator/ParseContext.cpp
index 258af09..47325d4 100644
--- a/src/compiler/translator/ParseContext.cpp
+++ b/src/compiler/translator/ParseContext.cpp
@@ -130,6 +130,7 @@
       mMaxNumViews(resources.MaxViewsOVR),
       mMaxImageUnits(resources.MaxImageUnits),
       mMaxCombinedTextureImageUnits(resources.MaxCombinedTextureImageUnits),
+      mMaxUniformLocations(resources.MaxUniformLocations),
       mDeclaringFunction(false)
 {
     mComputeShaderLocalSize.fill(-1);
@@ -845,8 +846,13 @@
 {
     if (layoutQualifier.location != -1)
     {
-        error(location, "invalid layout qualifier: only valid on program inputs and outputs",
-              "location");
+        const char *errorMsg = "invalid layout qualifier: only valid on program inputs and outputs";
+        if (mShaderVersion >= 310)
+        {
+            errorMsg =
+                "invalid layout qualifier: only valid on program inputs, outputs, and uniforms";
+        }
+        error(location, errorMsg, "location");
     }
 }
 
@@ -1199,7 +1205,23 @@
         return;
     }
 
-    if (publicType.qualifier != EvqVertexIn && publicType.qualifier != EvqFragmentOut)
+    bool canHaveLocation =
+        publicType.qualifier == EvqVertexIn || publicType.qualifier == EvqFragmentOut;
+
+    if (mShaderVersion >= 310 && publicType.qualifier == EvqUniform)
+    {
+        canHaveLocation = true;
+
+        // Valid uniform declarations can't be unsized arrays since uniforms can't be initialized.
+        // But invalid shaders may still reach here with an unsized array declaration.
+        if (!publicType.isUnsizedArray())
+        {
+            TType type(publicType);
+            checkUniformLocationInRange(identifierLocation, type.getLocationCount(),
+                                        publicType.layoutQualifier);
+        }
+    }
+    if (!canHaveLocation)
     {
         checkLocationIsNotSpecified(identifierLocation, publicType.layoutQualifier);
     }
@@ -1369,6 +1391,17 @@
     }
 }
 
+void TParseContext::checkUniformLocationInRange(const TSourceLoc &location,
+                                                int objectLocationCount,
+                                                const TLayoutQualifier &layoutQualifier)
+{
+    int loc = layoutQualifier.location;
+    if (loc >= 0 && loc + objectLocationCount > mMaxUniformLocations)
+    {
+        error(location, "Uniform location out of range", "location");
+    }
+}
+
 void TParseContext::functionCallLValueErrorCheck(const TFunction *fnCandidate,
                                                  TIntermAggregate *fnCall)
 {