ES31: Add link validations on geometry shader uniforms

This patch adds the link validations on the uniforms defined in a
geometry shader.

1. Validate if there is any link mismatch between a geometry shader
   uniform and a uniform defined in another shader in the current
   graphics pipeline.
2. Validate if the number of images, samplers or atomic counters in
   a geometry shader exceeds the related resource limit.
3. Validate if there is name contradiction between a geometry shader
   uniform and a vertex shader attribute.

BUG=angleproject:1941
TEST=dEQP-GLES31.functional.shaders.linkage.es31.geometry.uniform.*
     dEQP-GLES31.functional.geometry_shading.basic.*
     dEQP-GLES31.functional.geometry_shading.conversion.*
     dEQP-GLES31.functional.geometry_shading.emit.*
     dEQP-GLES31.functional.geometry_shading.varying.*
     dEQP-GLES31.functional.geometry_shading.instanced.geometry_*
     dEQP-GLES31.functional.geometry_shading.instanced.invocation_output_*
     dEQP-GLES31.functional.geometry_shading.instanced.draw_*

Change-Id: I365aee624a3a79658c3e4c7487a586cf9532b529
Reviewed-on: https://chromium-review.googlesource.com/939264
Commit-Queue: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/libANGLE/MemoryProgramCache.cpp b/src/libANGLE/MemoryProgramCache.cpp
index 89721fd..aa9cc64 100644
--- a/src/libANGLE/MemoryProgramCache.cpp
+++ b/src/libANGLE/MemoryProgramCache.cpp
@@ -67,6 +67,7 @@
     stream->writeInt(var.vertexStaticUse);
     stream->writeInt(var.fragmentStaticUse);
     stream->writeInt(var.computeStaticUse);
+    stream->writeInt(var.geometryStaticUse);
 
     stream->writeInt(var.memberIndexes.size());
     for (unsigned int memberCounterIndex : var.memberIndexes)
@@ -82,6 +83,7 @@
     var->vertexStaticUse   = stream->readBool();
     var->fragmentStaticUse = stream->readBool();
     var->computeStaticUse  = stream->readBool();
+    var->geometryStaticUse = stream->readBool();
 
     unsigned int numMembers = stream->readInt<unsigned int>();
     for (unsigned int blockMemberIndex = 0; blockMemberIndex < numMembers; blockMemberIndex++)
diff --git a/src/libANGLE/Program.cpp b/src/libANGLE/Program.cpp
index ec32a15..2b29002 100644
--- a/src/libANGLE/Program.cpp
+++ b/src/libANGLE/Program.cpp
@@ -2466,7 +2466,7 @@
         }
     }
     // TODO(jie.a.chen@intel.com): Count each atomic counter buffer to validate against
-    // gl_Max[Vertex|Fragment|Compute|Combined]AtomicCounterBuffers.
+    // gl_Max[Vertex|Fragment|Compute|Geometry|Combined]AtomicCounterBuffers.
 
     return true;
 }
@@ -3108,6 +3108,9 @@
         mState.mAttachedVertexShader->getUniforms(context);
     const std::vector<sh::Uniform> &fragmentUniforms =
         mState.mAttachedFragmentShader->getUniforms(context);
+    const std::vector<sh::Uniform> *geometryUniforms =
+        (mState.mAttachedGeometryShader) ? &mState.mAttachedGeometryShader->getUniforms(context)
+                                         : nullptr;
     const std::vector<sh::Attribute> &attributes =
         mState.mAttachedVertexShader->getActiveAttributes(context);
     for (const auto &attrib : attributes)
@@ -3128,6 +3131,17 @@
                 return false;
             }
         }
+        if (geometryUniforms)
+        {
+            for (const auto &uniform : *geometryUniforms)
+            {
+                if (uniform.name == attrib.name)
+                {
+                    infoLog << "Name conflicts between a uniform and an attribute: " << attrib.name;
+                    return false;
+                }
+            }
+        }
     }
     return true;
 }
diff --git a/src/libANGLE/ProgramLinkedResources.cpp b/src/libANGLE/ProgramLinkedResources.cpp
index 158f5ae..a372ff4 100644
--- a/src/libANGLE/ProgramLinkedResources.cpp
+++ b/src/libANGLE/ProgramLinkedResources.cpp
@@ -64,6 +64,80 @@
     }
 }
 
+// GLSL ES Spec 3.00.3, section 4.3.5.
+LinkMismatchError LinkValidateUniforms(const sh::Uniform &uniform1,
+                                       const sh::Uniform &uniform2,
+                                       std::string *mismatchedStructFieldName)
+{
+#if ANGLE_PROGRAM_LINK_VALIDATE_UNIFORM_PRECISION == ANGLE_ENABLED
+    const bool validatePrecision = true;
+#else
+    const bool validatePrecision = false;
+#endif
+
+    LinkMismatchError linkError = Program::LinkValidateVariablesBase(
+        uniform1, uniform2, validatePrecision, true, mismatchedStructFieldName);
+    if (linkError != LinkMismatchError::NO_MISMATCH)
+    {
+        return linkError;
+    }
+
+    // GLSL ES Spec 3.10.4, section 4.4.5.
+    if (uniform1.binding != -1 && uniform2.binding != -1 && uniform1.binding != uniform2.binding)
+    {
+        return LinkMismatchError::BINDING_MISMATCH;
+    }
+
+    // GLSL ES Spec 3.10.4, section 9.2.1.
+    if (uniform1.location != -1 && uniform2.location != -1 &&
+        uniform1.location != uniform2.location)
+    {
+        return LinkMismatchError::LOCATION_MISMATCH;
+    }
+    if (uniform1.offset != uniform2.offset)
+    {
+        return LinkMismatchError::OFFSET_MISMATCH;
+    }
+
+    return LinkMismatchError::NO_MISMATCH;
+}
+
+using ShaderUniform = std::pair<GLenum, const sh::Uniform *>;
+
+bool ValidateGraphicsUniformsPerShader(const Context *context,
+                                       Shader *shaderToLink,
+                                       bool extendLinkedUniforms,
+                                       std::map<std::string, ShaderUniform> *linkedUniforms,
+                                       InfoLog &infoLog)
+{
+    ASSERT(context && shaderToLink && linkedUniforms);
+
+    for (const sh::Uniform &uniform : shaderToLink->getUniforms(context))
+    {
+        const auto &entry = linkedUniforms->find(uniform.name);
+        if (entry != linkedUniforms->end())
+        {
+            const sh::Uniform &linkedUniform = *(entry->second.second);
+            std::string mismatchedStructFieldName;
+            LinkMismatchError linkError =
+                LinkValidateUniforms(uniform, linkedUniform, &mismatchedStructFieldName);
+            if (linkError != LinkMismatchError::NO_MISMATCH)
+            {
+                LogLinkMismatch(infoLog, uniform.name, "uniform", linkError,
+                                mismatchedStructFieldName, entry->second.first,
+                                shaderToLink->getType());
+                return false;
+            }
+        }
+        else if (extendLinkedUniforms)
+        {
+            (*linkedUniforms)[uniform.name] = std::make_pair(shaderToLink->getType(), &uniform);
+        }
+    }
+
+    return true;
+}
+
 }  // anonymous namespace
 
 UniformLinker::UniformLinker(const ProgramState &state) : mState(state)
@@ -114,76 +188,34 @@
 
 bool UniformLinker::validateGraphicsUniforms(const Context *context, InfoLog &infoLog) const
 {
-    // Check that uniforms defined in the vertex and fragment shaders are identical
-    std::map<std::string, const sh::Uniform *> linkedUniforms;
-    const std::vector<sh::Uniform> &vertexUniforms =
-        mState.getAttachedVertexShader()->getUniforms(context);
-    const std::vector<sh::Uniform> &fragmentUniforms =
-        mState.getAttachedFragmentShader()->getUniforms(context);
-
-    for (const sh::Uniform &vertexUniform : vertexUniforms)
+    // Check that uniforms defined in the graphics shaders are identical
+    std::map<std::string, ShaderUniform> linkedUniforms;
+    for (const sh::Uniform &vertexUniform : mState.getAttachedVertexShader()->getUniforms(context))
     {
-        linkedUniforms[vertexUniform.name] = &vertexUniform;
+        linkedUniforms[vertexUniform.name] = std::make_pair(GL_VERTEX_SHADER, &vertexUniform);
     }
 
-    for (const sh::Uniform &fragmentUniform : fragmentUniforms)
+    std::vector<Shader *> activeShadersToLink;
+    if (mState.getAttachedGeometryShader())
     {
-        auto entry = linkedUniforms.find(fragmentUniform.name);
-        if (entry != linkedUniforms.end())
+        activeShadersToLink.push_back(mState.getAttachedGeometryShader());
+    }
+    activeShadersToLink.push_back(mState.getAttachedFragmentShader());
+
+    const size_t numActiveShadersToLink = activeShadersToLink.size();
+    for (size_t shaderIndex = 0; shaderIndex < numActiveShadersToLink; ++shaderIndex)
+    {
+        bool isLastShader = (shaderIndex == numActiveShadersToLink - 1);
+        if (!ValidateGraphicsUniformsPerShader(context, activeShadersToLink[shaderIndex],
+                                               !isLastShader, &linkedUniforms, infoLog))
         {
-            const sh::Uniform &vertexUniform = *(entry->second);
-            std::string mismatchedStructFieldName;
-            LinkMismatchError linkError =
-                LinkValidateUniforms(vertexUniform, fragmentUniform, &mismatchedStructFieldName);
-            if (linkError != LinkMismatchError::NO_MISMATCH)
-            {
-                LogLinkMismatch(infoLog, fragmentUniform.name, "uniform", linkError,
-                                mismatchedStructFieldName, GL_VERTEX_SHADER, GL_FRAGMENT_SHADER);
-                return false;
-            }
+            return false;
         }
     }
+
     return true;
 }
 
-// GLSL ES Spec 3.00.3, section 4.3.5.
-LinkMismatchError UniformLinker::LinkValidateUniforms(const sh::Uniform &uniform1,
-                                                      const sh::Uniform &uniform2,
-                                                      std::string *mismatchedStructFieldName)
-{
-#if ANGLE_PROGRAM_LINK_VALIDATE_UNIFORM_PRECISION == ANGLE_ENABLED
-    const bool validatePrecision = true;
-#else
-    const bool validatePrecision = false;
-#endif
-
-    LinkMismatchError linkError = Program::LinkValidateVariablesBase(
-        uniform1, uniform2, validatePrecision, true, mismatchedStructFieldName);
-    if (linkError != LinkMismatchError::NO_MISMATCH)
-    {
-        return linkError;
-    }
-
-    // GLSL ES Spec 3.10.4, section 4.4.5.
-    if (uniform1.binding != -1 && uniform2.binding != -1 && uniform1.binding != uniform2.binding)
-    {
-        return LinkMismatchError::BINDING_MISMATCH;
-    }
-
-    // GLSL ES Spec 3.10.4, section 9.2.1.
-    if (uniform1.location != -1 && uniform2.location != -1 &&
-        uniform1.location != uniform2.location)
-    {
-        return LinkMismatchError::LOCATION_MISMATCH;
-    }
-    if (uniform1.offset != uniform2.offset)
-    {
-        return LinkMismatchError::OFFSET_MISMATCH;
-    }
-
-    return LinkMismatchError::NO_MISMATCH;
-}
-
 bool UniformLinker::indexUniforms(InfoLog &infoLog, const ProgramBindings &uniformLocationBindings)
 {
     // All the locations where another uniform can't be located.
@@ -467,6 +499,22 @@
         {
             return false;
         }
+
+        Shader *geometryShader = mState.getAttachedGeometryShader();
+        // TODO (jiawei.shao@intel.com): check whether we need finer-grained component counting
+        if (geometryShader &&
+            !flattenUniformsAndCheckCapsForShader(
+                context, geometryShader, caps.maxGeometryUniformComponents / 4,
+                caps.maxGeometryTextureImageUnits, caps.maxGeometryImageUniforms,
+                caps.maxGeometryAtomicCounters,
+                "Geometry shader active uniforms exceed MAX_GEOMETRY_UNIFORM_VECTORS_EXT (",
+                "Geometry shader sampler count exceeds MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_EXT (",
+                "Geometry shader image count exceeds MAX_GEOMETRY_IMAGE_UNIFORMS_EXT (",
+                "Geometry shader atomic counter count exceeds MAX_GEOMETRY_ATOMIC_COUNTERS_EXT (",
+                samplerUniforms, imageUniforms, atomicCounterUniforms, infoLog))
+        {
+            return false;
+        }
     }
 
     mUniforms.insert(mUniforms.end(), samplerUniforms.begin(), samplerUniforms.end());
diff --git a/src/libANGLE/ProgramLinkedResources.h b/src/libANGLE/ProgramLinkedResources.h
index ba06d48..83c6fad 100644
--- a/src/libANGLE/ProgramLinkedResources.h
+++ b/src/libANGLE/ProgramLinkedResources.h
@@ -81,10 +81,6 @@
 
     bool validateGraphicsUniforms(const Context *context, InfoLog &infoLog) const;
 
-    static LinkMismatchError LinkValidateUniforms(const sh::Uniform &uniform1,
-                                                  const sh::Uniform &uniform2,
-                                                  std::string *mismatchedStructFieldName);
-
     bool flattenUniformsAndCheckCapsForShader(const Context *context,
                                               Shader *shader,
                                               GLuint maxUniformComponents,
diff --git a/src/libANGLE/Uniform.cpp b/src/libANGLE/Uniform.cpp
index dee8eee..7175118 100644
--- a/src/libANGLE/Uniform.cpp
+++ b/src/libANGLE/Uniform.cpp
@@ -14,7 +14,10 @@
 {
 
 StaticallyUsed::StaticallyUsed()
-    : vertexStaticUse(false), fragmentStaticUse(false), computeStaticUse(false)
+    : vertexStaticUse(false),
+      fragmentStaticUse(false),
+      computeStaticUse(false),
+      geometryStaticUse(false)
 {
 }
 
@@ -41,6 +44,10 @@
             computeStaticUse = used;
             break;
 
+        case GL_GEOMETRY_SHADER_EXT:
+            geometryStaticUse = used;
+            break;
+
         default:
             UNREACHABLE();
     }
@@ -51,6 +58,7 @@
     vertexStaticUse |= other.vertexStaticUse;
     fragmentStaticUse |= other.fragmentStaticUse;
     computeStaticUse |= other.computeStaticUse;
+    geometryStaticUse |= other.geometryStaticUse;
 }
 
 LinkedUniform::LinkedUniform()
diff --git a/src/libANGLE/Uniform.h b/src/libANGLE/Uniform.h
index 14c3938..03ba67b 100644
--- a/src/libANGLE/Uniform.h
+++ b/src/libANGLE/Uniform.h
@@ -34,6 +34,7 @@
     bool vertexStaticUse;
     bool fragmentStaticUse;
     bool computeStaticUse;
+    bool geometryStaticUse;
 };
 
 // Helper struct representing a single shader uniform
diff --git a/src/tests/deqp_support/deqp_gles31_test_expectations.txt b/src/tests/deqp_support/deqp_gles31_test_expectations.txt
index 692cf17..51ff53b 100644
--- a/src/tests/deqp_support/deqp_gles31_test_expectations.txt
+++ b/src/tests/deqp_support/deqp_gles31_test_expectations.txt
@@ -1117,6 +1117,13 @@
 1442 D3D11 : dEQP-GLES31.functional.shaders.builtin_var.compute.local_invocation_id = FAIL
 1442 D3D11 : dEQP-GLES31.functional.shaders.builtin_var.compute.global_invocation_id = FAIL
 1442 D3D11 : dEQP-GLES31.functional.shaders.builtin_var.compute.local_invocation_index = FAIL
+1941 D3D11 : dEQP-GLES31.functional.geometry_shading.basic.* = FAIL
+1941 D3D11 : dEQP-GLES31.functional.geometry_shading.conversion.* = FAIL
+1941 D3D11 : dEQP-GLES31.functional.geometry_shading.emit.* = FAIL
+1941 D3D11 : dEQP-GLES31.functional.geometry_shading.varying.* = FAIL
+1941 D3D11 : dEQP-GLES31.functional.geometry_shading.instanced.geometry_* = FAIL
+1941 D3D11 : dEQP-GLES31.functional.geometry_shading.instanced.invocation_output_* = FAIL
+1941 D3D11 : dEQP-GLES31.functional.geometry_shading.instanced.draw_* = FAIL
 1941 D3D11 : dEQP-GLES31.functional.shaders.linkage.es31.geometry.* = FAIL
 
 // OPENGL Failing Tests
@@ -1633,16 +1640,8 @@
 1941 OPENGL D3D11 : dEQP-GLES31.functional.debug.negative_coverage.log.shader_directive.geometry_shader = FAIL
 1941 OPENGL D3D11 : dEQP-GLES31.functional.debug.negative_coverage.get_error.shader_directive.geometry_shader = FAIL
 1941 OPENGL D3D11 : dEQP-GLES31.functional.geometry_shading.query.* = FAIL
-1941 OPENGL D3D11 : dEQP-GLES31.functional.geometry_shading.basic.* = FAIL
 1941 OPENGL D3D11 : dEQP-GLES31.functional.geometry_shading.input.* = FAIL
-1941 OPENGL D3D11 : dEQP-GLES31.functional.geometry_shading.conversion.* = FAIL
-1941 OPENGL D3D11 : dEQP-GLES31.functional.geometry_shading.emit.* = FAIL
-1941 OPENGL D3D11 : dEQP-GLES31.functional.geometry_shading.varying.* = FAIL
-1941 OPENGL D3D11 : dEQP-GLES31.functional.geometry_shading.instanced.geometry_* = FAIL
-1941 OPENGL D3D11 : dEQP-GLES31.functional.geometry_shading.instanced.invocation_output_* = FAIL
-1941 OPENGL D3D11 : dEQP-GLES31.functional.geometry_shading.instanced.draw_* = FAIL
 1941 OPENGL D3D11 : dEQP-GLES31.functional.geometry_shading.negative.* = FAIL
-1941 OPENGL D3D11 : dEQP-GLES31.functional.shaders.linkage.es31.geometry.uniform.* = FAIL
 
 2324 DEBUG RELEASE : dEQP-GLES31.functional.debug.negative_coverage.callbacks.compute.program_not_active = FAIL
 2324 DEBUG RELEASE : dEQP-GLES31.functional.debug.negative_coverage.log.compute.program_not_active = FAIL
diff --git a/src/tests/gl_tests/GeometryShaderTest.cpp b/src/tests/gl_tests/GeometryShaderTest.cpp
index 93afb94..eefd653 100644
--- a/src/tests/gl_tests/GeometryShaderTest.cpp
+++ b/src/tests/gl_tests/GeometryShaderTest.cpp
@@ -316,6 +316,55 @@
     EXPECT_GL_NO_ERROR();
 }
 
+// Verify that an link error occurs when the definition of a unform in fragment shader is different
+// from those in a geometry shader.
+TEST_P(GeometryShaderTest, UniformMismatchBetweenGeometryAndFragmentShader)
+{
+    ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_EXT_geometry_shader"));
+
+    const std::string &vertexShader =
+        R"(#version 310 es
+        uniform highp vec4 uniform_value_vert;
+        in vec4 vertex_in;
+        out vec4 vertex_out;
+        void main()
+        {
+            gl_Position = vertex_in;
+            vertex_out = uniform_value_vert;
+        })";
+
+    const std::string &geometryShader =
+        R"(#version 310 es
+        #extension GL_EXT_geometry_shader : require
+        uniform vec4 uniform_value;
+        layout (invocations = 3, triangles) in;
+        layout (points, max_vertices = 3) out;
+        in vec4 vertex_out[];
+        out vec4 geometry_color;
+        void main()
+        {
+            gl_Position = gl_in[0].gl_Position;
+            geometry_color = vertex_out[0] + uniform_value;
+            EmitVertex();
+        })";
+
+    const std::string &fragmentShader =
+        R"(#version 310 es
+        precision highp float;
+        uniform float uniform_value;
+        in vec4 geometry_color;
+        layout (location = 0) out vec4 output_color;
+        void main()
+        {
+            output_color = vec4(geometry_color.rgb, uniform_value);
+        })";
+
+    GLuint program = CompileProgramWithGS(vertexShader, geometryShader, fragmentShader);
+    EXPECT_EQ(0u, program);
+
+    EXPECT_GL_NO_ERROR();
+}
+
 ANGLE_INSTANTIATE_TEST(GeometryShaderTestES3, ES3_OPENGL(), ES3_OPENGLES(), ES3_D3D11());
 ANGLE_INSTANTIATE_TEST(GeometryShaderTest, ES31_OPENGL(), ES31_OPENGLES(), ES31_D3D11());
 }