ES31: Add link validation on geometry shader itself

This patch intends to support program link validation on geometry
shader itself. A link error should occur when linking a program with
a geometry shader that lacks input primitive or output primitive or
the declaration of 'max_vertices'.

This patch also adds the support of linking a program with geometry
shader in angle_end2end_tests.

BUG=angleproject:1941
TEST=angle_end2end_tests
     dEQP-GLES31.functional.shaders.linkage.es31.geometry.varying.rules.unspecified_*

Change-Id: I25fb08514753102f5dd3ab86211c05d2ca4fd185
Reviewed-on: https://chromium-review.googlesource.com/898842
Commit-Queue: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/tests/gl_tests/GeometryShaderTest.cpp b/src/tests/gl_tests/GeometryShaderTest.cpp
index bf55d06..072492a 100644
--- a/src/tests/gl_tests/GeometryShaderTest.cpp
+++ b/src/tests/gl_tests/GeometryShaderTest.cpp
@@ -15,6 +15,52 @@
 
 class GeometryShaderTest : public ANGLETest
 {
+  protected:
+    static std::string CreateEmptyGeometryShader(const std::string &inputPrimitive,
+                                                 const std::string &outputPrimitive,
+                                                 int invocations,
+                                                 int maxVertices)
+    {
+        std::ostringstream ostream;
+        ostream << "#version 310 es\n"
+                   "#extension GL_EXT_geometry_shader : require\n";
+        if (!inputPrimitive.empty())
+        {
+            ostream << "layout (" << inputPrimitive << ") in;\n";
+        }
+        if (!outputPrimitive.empty())
+        {
+            ostream << "layout (" << outputPrimitive << ") out;\n";
+        }
+        if (invocations > 0)
+        {
+            ostream << "layout (invocations = " << invocations << ") in;\n";
+        }
+        if (maxVertices >= 0)
+        {
+            ostream << "layout (max_vertices = " << maxVertices << ") out;\n";
+        }
+        ostream << "void main()\n"
+                   "{\n"
+                   "}";
+        return ostream.str();
+    }
+
+    const std::string kDefaultVertexShader =
+        R"(#version 310 es
+        void main()
+        {
+            gl_Position = vec4(1.0, 0.0, 0.0, 1.0);
+        })";
+
+    const std::string kDefaultFragmentShader =
+        R"(#version 310 es
+        precision mediump float;
+        layout (location = 0) out vec4 frag_out;
+        void main()
+        {
+            frag_out = vec4(1.0, 0.0, 0.0, 1.0);
+        })";
 };
 
 class GeometryShaderTestES3 : public ANGLETest
@@ -127,6 +173,101 @@
     }
 }
 
+// Verify that linking a program with an uncompiled geometry shader causes a link failure.
+TEST_P(GeometryShaderTest, LinkWithUncompiledGeoemtryShader)
+{
+    ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_EXT_geometry_shader"));
+
+    GLuint vertexShader   = CompileShader(GL_VERTEX_SHADER, kDefaultVertexShader);
+    GLuint fragmentShader = CompileShader(GL_FRAGMENT_SHADER, kDefaultFragmentShader);
+    ASSERT_NE(0u, vertexShader);
+    ASSERT_NE(0u, fragmentShader);
+
+    GLuint geometryShader = glCreateShader(GL_GEOMETRY_SHADER_EXT);
+
+    GLuint program = glCreateProgram();
+    glAttachShader(program, vertexShader);
+    glAttachShader(program, fragmentShader);
+    glAttachShader(program, geometryShader);
+    glDeleteShader(vertexShader);
+    glDeleteShader(fragmentShader);
+    glDeleteShader(geometryShader);
+
+    glLinkProgram(program);
+
+    GLint linkStatus;
+    glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
+    EXPECT_EQ(0, linkStatus);
+
+    glDeleteProgram(program);
+    ASSERT_GL_NO_ERROR();
+}
+
+// Verify that linking a program with geometry shader whose version is different from other shaders
+// in this program causes a link error.
+TEST_P(GeometryShaderTest, LinkWhenShaderVersionMismatch)
+{
+    ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_EXT_geometry_shader"));
+
+    const std::string &kDefaultVertexShaderVersion300 =
+        R"(#version 300 es
+        void main()
+        {
+            gl_Position = vec4(1.0, 0.0, 0.0, 1.0);
+        })";
+
+    const std::string kDefaultFragmentShaderVersion300 =
+        R"(#version 300 es
+        precision mediump float;
+        layout (location = 0) out vec4 frag_out;
+        void main()
+        {
+            frag_out = vec4(1.0, 0.0, 0.0, 1.0);
+        })";
+
+    const std::string &emptyGeometryShader = CreateEmptyGeometryShader("points", "points", 2, 1);
+
+    GLuint program = CompileProgramWithGS(kDefaultVertexShaderVersion300, emptyGeometryShader,
+                                          kDefaultFragmentShaderVersion300);
+    EXPECT_EQ(0u, program);
+}
+
+// Verify that linking a program with geometry shader that lacks input primitive,
+// output primitive, or declaration on 'max_vertices' causes a link failure.
+TEST_P(GeometryShaderTest, LinkValidationOnGeometryShaderLayouts)
+{
+    ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_EXT_geometry_shader"));
+
+    const std::string &gsWithoutInputPrimitive  = CreateEmptyGeometryShader("", "points", 2, 1);
+    const std::string &gsWithoutOutputPrimitive = CreateEmptyGeometryShader("points", "", 2, 1);
+    const std::string &gsWithoutInvocations = CreateEmptyGeometryShader("points", "points", -1, 1);
+    const std::string &gsWithoutMaxVertices = CreateEmptyGeometryShader("points", "points", 2, -1);
+
+    // Linking a program with a geometry shader that only lacks 'invocations' should not cause a
+    // link failure.
+    GLuint program =
+        CompileProgramWithGS(kDefaultVertexShader, gsWithoutInvocations, kDefaultFragmentShader);
+    EXPECT_NE(0u, program);
+
+    glDeleteProgram(program);
+
+    // Linking a program with a geometry shader that lacks input primitive, output primitive or
+    // 'max_vertices' causes a link failure.
+    program =
+        CompileProgramWithGS(kDefaultVertexShader, gsWithoutInputPrimitive, kDefaultFragmentShader);
+    EXPECT_EQ(0u, program);
+
+    program = CompileProgramWithGS(kDefaultVertexShader, gsWithoutOutputPrimitive,
+                                   kDefaultFragmentShader);
+    EXPECT_EQ(0u, program);
+
+    program =
+        CompileProgramWithGS(kDefaultVertexShader, gsWithoutMaxVertices, kDefaultFragmentShader);
+    EXPECT_EQ(0u, program);
+
+    ASSERT_GL_NO_ERROR();
+}
+
 ANGLE_INSTANTIATE_TEST(GeometryShaderTestES3, ES3_OPENGL(), ES3_OPENGLES(), ES3_D3D11());
 ANGLE_INSTANTIATE_TEST(GeometryShaderTest, ES31_OPENGL(), ES31_OPENGLES(), ES31_D3D11());
 }