Edit

kc3-lang/angle/src/tests/gl_tests/GeometryShaderTest.cpp

Branch :

  • Show log

    Commit

  • Author : Jiawei Shao
    Date : 2018-05-28 11:17:47
    Hash : a8802477
    Message : ES31: Implement FramebufferTextureEXT on OpenGL back-ends This patch intends to implement FramebufferTextureEXT on OpenGL back-ends. 1. Support layered framebuffer attachments. 2. Add new framebuffer completeness rules on layered framebuffer attachments. 3. Support FRAMEBUFFER_ATTACHMENT_LAYERED_EXT as a valid <pname> parameter of GetFramebufferAttachmentParameteriv. Note that for an entire level of a cube map: 1. It has no TextureTarget because TEXTURE_CUBE is not a valid target for TexImage*D. 2. It corresponds to 6 ImageDescs (that represents its faces) in class Texture, so when the cube map is cube complete, we return the ImageDesc of its first face, meanwhile we do not allow querying ImageDesc if it is not cube complete. BUG=angleproject:1941 TEST=angle_end2end_tests dEQP-GLES31.functional.geometry_shading.query.framebuffer_attachment_layers dEQP-GLES31.functional.geometry_shading.query.framebuffer_incomplete_layer_targets dEQP-GLES31.functional.geometry_shading.layered.* dEQP-GLES31.functional.geometry_shading.instanced.invocation_per_layer_* dEQP-GLES31.functional.geometry_shading.instanced.multiple_layers_per_invocation_* Change-Id: I44393b513ec8f1a682fd1c47d3eaa6f3b3fae877 Reviewed-on: https://chromium-review.googlesource.com/1075811 Commit-Queue: Jiawei Shao <jiawei.shao@intel.com> Reviewed-by: Corentin Wallez <cwallez@chromium.org>

  • src/tests/gl_tests/GeometryShaderTest.cpp
  • //
    // Copyright 2017 The ANGLE Project Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style license that can be
    // found in the LICENSE file.
    //
    
    // GeometryShaderTest.cpp : Tests of the implementation of geometry shader
    
    #include "test_utils/ANGLETest.h"
    #include "test_utils/gl_raii.h"
    
    using namespace angle;
    
    namespace
    {
    
    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();
        }
    };
    
    class GeometryShaderTestES3 : public ANGLETest
    {
    };
    
    // Verify that Geometry Shader cannot be created in an OpenGL ES 3.0 context.
    TEST_P(GeometryShaderTestES3, CreateGeometryShaderInES3)
    {
        EXPECT_TRUE(!extensionEnabled("GL_EXT_geometry_shader"));
        GLuint geometryShader = glCreateShader(GL_GEOMETRY_SHADER_EXT);
        EXPECT_EQ(0u, geometryShader);
        EXPECT_GL_ERROR(GL_INVALID_ENUM);
    }
    
    // Verify that Geometry Shader can be created and attached to a program.
    TEST_P(GeometryShaderTest, CreateAndAttachGeometryShader)
    {
        ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_EXT_geometry_shader"));
    
        const std::string &geometryShaderSource =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (invocations = 3, triangles) in;
            layout (triangle_strip, max_vertices = 3) out;
            in vec4 texcoord[];
            out vec4 o_texcoord;
            void main()
            {
                int n;
                for (n = 0; n < gl_in.length(); n++)
                {
                    gl_Position = gl_in[n].gl_Position;
                    gl_Layer   = gl_InvocationID;
                    o_texcoord = texcoord[n];
                    EmitVertex();
                }
                EndPrimitive();
            })";
    
        GLuint geometryShader = CompileShader(GL_GEOMETRY_SHADER_EXT, geometryShaderSource);
    
        EXPECT_NE(0u, geometryShader);
    
        GLuint programID = glCreateProgram();
        glAttachShader(programID, geometryShader);
    
        glDetachShader(programID, geometryShader);
        glDeleteShader(geometryShader);
        glDeleteProgram(programID);
    
        EXPECT_GL_NO_ERROR();
    }
    
    // Verify that all the implementation dependent geometry shader related resource limits meet the
    // requirement of GL_EXT_geometry_shader SPEC.
    TEST_P(GeometryShaderTest, GeometryShaderImplementationDependentLimits)
    {
        ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_EXT_geometry_shader"));
    
        const std::map<GLenum, int> limits = {{GL_MAX_FRAMEBUFFER_LAYERS_EXT, 256},
                                              {GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_EXT, 1024},
                                              {GL_MAX_GEOMETRY_UNIFORM_BLOCKS_EXT, 12},
                                              {GL_MAX_GEOMETRY_INPUT_COMPONENTS_EXT, 64},
                                              {GL_MAX_GEOMETRY_OUTPUT_COMPONENTS_EXT, 64},
                                              {GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT, 256},
                                              {GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_EXT, 1024},
                                              {GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_EXT, 16},
                                              {GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS_EXT, 0},
                                              {GL_MAX_GEOMETRY_ATOMIC_COUNTERS_EXT, 0},
                                              {GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS_EXT, 0},
                                              {GL_MAX_GEOMETRY_IMAGE_UNIFORMS_EXT, 0},
                                              {GL_MAX_GEOMETRY_SHADER_INVOCATIONS_EXT, 32}};
    
        GLint value;
        for (const auto &limit : limits)
        {
            value = 0;
            glGetIntegerv(limit.first, &value);
            EXPECT_GL_NO_ERROR();
            EXPECT_GE(value, limit.second);
        }
    
        value = 0;
        glGetIntegerv(GL_LAYER_PROVOKING_VERTEX_EXT, &value);
        EXPECT_GL_NO_ERROR();
        EXPECT_TRUE(value == GL_FIRST_VERTEX_CONVENTION_EXT || value == GL_LAST_VERTEX_CONVENTION_EXT ||
                    value == GL_UNDEFINED_VERTEX_EXT);
    }
    
    // Verify that all the combined resource limits meet the requirement of GL_EXT_geometry_shader SPEC.
    TEST_P(GeometryShaderTest, CombinedResourceLimits)
    {
        ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_EXT_geometry_shader"));
    
        // See http://anglebug.com/2261.
        ANGLE_SKIP_TEST_IF(IsAndroid());
    
        const std::map<GLenum, int> limits = {{GL_MAX_UNIFORM_BUFFER_BINDINGS, 48},
                                              {GL_MAX_COMBINED_UNIFORM_BLOCKS, 36},
                                              {GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, 64}};
    
        GLint value;
        for (const auto &limit : limits)
        {
            value = 0;
            glGetIntegerv(limit.first, &value);
            EXPECT_GL_NO_ERROR();
            EXPECT_GE(value, limit.second);
        }
    }
    
    // Verify that linking a program with an uncompiled geometry shader causes a link failure.
    TEST_P(GeometryShaderTest, LinkWithUncompiledGeometryShader)
    {
        ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_EXT_geometry_shader"));
    
        GLuint vertexShader   = CompileShader(GL_VERTEX_SHADER, essl31_shaders::vs::Simple());
        GLuint fragmentShader = CompileShader(GL_FRAGMENT_SHADER, essl31_shaders::fs::Red());
        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 &emptyGeometryShader = CreateEmptyGeometryShader("points", "points", 2, 1);
    
        GLuint program = CompileProgramWithGS(essl3_shaders::vs::Simple(), emptyGeometryShader,
                                              essl3_shaders::fs::Red());
        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(essl31_shaders::vs::Simple(), gsWithoutInvocations,
                                              essl31_shaders::fs::Red());
        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(essl31_shaders::vs::Simple(), gsWithoutInputPrimitive,
                                       essl31_shaders::fs::Red());
        EXPECT_EQ(0u, program);
    
        program = CompileProgramWithGS(essl31_shaders::vs::Simple(), gsWithoutOutputPrimitive,
                                       essl31_shaders::fs::Red());
        EXPECT_EQ(0u, program);
    
        program = CompileProgramWithGS(essl31_shaders::vs::Simple(), gsWithoutMaxVertices,
                                       essl31_shaders::fs::Red());
        EXPECT_EQ(0u, program);
    
        ASSERT_GL_NO_ERROR();
    }
    
    // Verify that an link error occurs when the vertex shader has an array output and there is a
    // geometry shader in the program.
    TEST_P(GeometryShaderTest, VertexShaderArrayOutput)
    {
        ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_EXT_geometry_shader"));
    
        const std::string &vertexShader =
            R"(#version 310 es
            in vec4 vertex_in;
            out vec4 vertex_out[3];
            void main()
            {
                gl_Position = vertex_in;
                vertex_out[0] = vec4(1.0, 0.0, 0.0, 1.0);
                vertex_out[1] = vec4(0.0, 1.0, 0.0, 1.0);
                vertex_out[2] = vec4(0.0, 0.0, 1.0, 1.0);
            })";
    
        const std::string &geometryShader =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            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];
                EmitVertex();
            })";
    
        const std::string &fragmentShader =
            R"(#version 310 es
            precision mediump float;
            in vec4 geometry_color;
            layout (location = 0) out vec4 output_color;
            void main()
            {
                output_color = geometry_color;
            })";
    
        GLuint program = CompileProgramWithGS(vertexShader, geometryShader, fragmentShader);
        EXPECT_EQ(0u, program);
    
        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();
    }
    
    // Verify that an link error occurs when the number of uniform blocks in a geometry shader exceeds
    // MAX_GEOMETRY_UNIFORM_BLOCKS_EXT.
    TEST_P(GeometryShaderTest, TooManyUniformBlocks)
    {
        ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_EXT_geometry_shader"));
    
        GLint maxGeometryUniformBlocks = 0;
        glGetIntegerv(GL_MAX_GEOMETRY_UNIFORM_BLOCKS_EXT, &maxGeometryUniformBlocks);
    
        GLint numUniformBlocks = maxGeometryUniformBlocks + 1;
        std::ostringstream stream;
        stream << "#version 310 es\n"
                  "#extension GL_EXT_geometry_shader : require\n"
                  "uniform ubo\n"
                  "{\n"
                  "    vec4 value1;\n"
                  "} block0["
               << numUniformBlocks
               << "];\n"
                  "layout (triangles) in;\n"
                  "layout (points, max_vertices = 1) out;\n"
                  "void main()\n"
                  "{\n"
                  "    gl_Position = gl_in[0].gl_Position;\n";
    
        for (GLint i = 0; i < numUniformBlocks; ++i)
        {
            stream << "    gl_Position += block0[" << i << "].value1;\n";
        }
        stream << "    EmitVertex();\n"
                  "}\n";
    
        GLuint program =
            CompileProgramWithGS(essl31_shaders::vs::Simple(), stream.str(), essl31_shaders::fs::Red());
        EXPECT_EQ(0u, program);
    
        EXPECT_GL_NO_ERROR();
    }
    
    // Verify that an link error occurs when the number of shader storage blocks in a geometry shader
    // exceeds MAX_GEOMETRY_SHADER_STORAGE_BLOCKS_EXT.
    TEST_P(GeometryShaderTest, TooManyShaderStorageBlocks)
    {
        ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_EXT_geometry_shader"));
    
        GLint maxGeometryShaderStorageBlocks = 0;
        glGetIntegerv(GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS_EXT, &maxGeometryShaderStorageBlocks);
    
        GLint numSSBOs = maxGeometryShaderStorageBlocks + 1;
        std::ostringstream stream;
        stream << "#version 310 es\n"
                  "#extension GL_EXT_geometry_shader : require\n"
                  "buffer ssbo\n"
                  "{\n"
                  "    vec4 value1;\n"
                  "} block0["
               << numSSBOs
               << "];\n"
                  "layout (triangles) in;\n"
                  "layout (points, max_vertices = 1) out;\n"
                  "void main()\n"
                  "{\n"
                  "    gl_Position = gl_in[0].gl_Position;\n";
    
        for (GLint i = 0; i < numSSBOs; ++i)
        {
            stream << "    gl_Position += block0[" << i << "].value1;\n";
        }
        stream << "    EmitVertex();\n"
                  "}\n";
    
        GLuint program =
            CompileProgramWithGS(essl31_shaders::vs::Simple(), stream.str(), essl31_shaders::fs::Red());
        EXPECT_EQ(0u, program);
    
        EXPECT_GL_NO_ERROR();
    }
    
    // Verify that an link error occurs when the definition of a unform block in the vertex shader is
    // different from that in a geometry shader.
    TEST_P(GeometryShaderTest, UniformBlockMismatchBetweenVertexAndGeometryShader)
    {
        ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_EXT_geometry_shader"));
    
        const std::string &vertexShader =
            R"(#version 310 es
            uniform ubo
            {
                vec4 uniform_value_vert;
            } block0;
            in vec4 vertex_in;
            out vec4 vertex_out;
            void main()
            {
                gl_Position = vertex_in;
                vertex_out = block0.uniform_value_vert;
            })";
    
        const std::string &geometryShader =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            uniform ubo
            {
                vec4 uniform_value_geom;
            } block0;
            layout (triangles) in;
            layout (points, max_vertices = 1) out;
            in vec4 vertex_out[];
            void main()
            {
                gl_Position = gl_in[0].gl_Position + vertex_out[0];
                gl_Position += block0.uniform_value_geom;
                EmitVertex();
            })";
    
        GLuint program = CompileProgramWithGS(vertexShader, geometryShader, essl31_shaders::fs::Red());
        EXPECT_EQ(0u, program);
    
        EXPECT_GL_NO_ERROR();
    }
    
    // Verify that an link error occurs when the definition of a shader storage block in the geometry
    // shader is different from that in a fragment shader.
    TEST_P(GeometryShaderTest, ShaderStorageBlockMismatchBetweenGeometryAndFragmentShader)
    {
        ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_EXT_geometry_shader"));
    
        GLint maxGeometryShaderStorageBlocks = 0;
        glGetIntegerv(GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS_EXT, &maxGeometryShaderStorageBlocks);
    
        // The minimun value of MAX_GEOMETRY_SHADER_STORAGE_BLOCKS_EXT can be 0.
        // [EXT_geometry_shader] Table 20.43gs
        ANGLE_SKIP_TEST_IF(maxGeometryShaderStorageBlocks == 0);
    
        GLint maxFragmentShaderStorageBlocks = 0;
        glGetIntegerv(GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS, &maxFragmentShaderStorageBlocks);
    
        // The minimun value of MAX_FRAGMENT_SHADER_STORAGE_BLOCKS can be 0.
        // [OpenGL ES 3.1] Table 20.44
        ANGLE_SKIP_TEST_IF(maxFragmentShaderStorageBlocks == 0);
    
        const std::string &geometryShader =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            buffer ssbo
            {
                vec4 ssbo_value;
            } block0;
            layout (triangles) in;
            layout (points, max_vertices = 1) out;
            void main()
            {
                gl_Position = gl_in[0].gl_Position + block0.ssbo_value;
                EmitVertex();
            })";
    
        const std::string &fragmentShader =
            R"(#version 310 es
            precision highp float;
            buffer ssbo
            {
                vec3 ssbo_value;
            } block0;
            layout (location = 0) out vec4 output_color;
            void main()
            {
                output_color = vec4(block0.ssbo_value, 1);
            })";
    
        GLuint program =
            CompileProgramWithGS(essl31_shaders::vs::Simple(), geometryShader, fragmentShader);
        EXPECT_EQ(0u, program);
    
        EXPECT_GL_NO_ERROR();
    }
    
    // Verify GL_REFERENCED_BY_GEOMETRY_SHADER_EXT cannot be used on platforms that don't support
    // EXT_geometry_shader, or we will get an INVALID_ENUM error.
    TEST_P(GeometryShaderTest, ReferencedByGeometryShaderWithoutExtensionEnabled)
    {
        ANGLE_SKIP_TEST_IF(extensionEnabled("GL_EXT_geometry_shader"));
    
        const std::string &fragmentShader =
            R"(#version 310 es
            precision highp float;
            uniform vec4 color;
            layout(location = 0) out vec4 oColor;
            void main()
            {
                oColor = color;
            })";
    
        ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Simple(), fragmentShader);
    
        const GLuint index = glGetProgramResourceIndex(program, GL_UNIFORM, "color");
        ASSERT_GL_NO_ERROR();
        ASSERT_NE(GL_INVALID_INDEX, index);
    
        constexpr GLenum kProps[]    = {GL_REFERENCED_BY_GEOMETRY_SHADER_EXT};
        constexpr GLsizei kPropCount = static_cast<GLsizei>(ArraySize(kProps));
        GLint params[ArraySize(kProps)];
        GLsizei length;
    
        glGetProgramResourceiv(program, GL_UNIFORM, index, kPropCount, kProps, kPropCount, &length,
                               params);
        EXPECT_GL_ERROR(GL_INVALID_ENUM);
    }
    
    // Verify GL_REFERENCED_BY_GEOMETRY_SHADER_EXT can work correctly on platforms that support
    // EXT_geometry_shader.
    TEST_P(GeometryShaderTest, ReferencedByGeometryShader)
    {
        ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_EXT_geometry_shader"));
    
        const std::string &vertexShader =
            R"(#version 310 es
            precision highp float;
            layout(location = 0) in highp vec4 position;
            void main()
            {
                gl_Position = position;
            })";
    
        const std::string &geometryShader =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (binding = 3) uniform ubo0
            {
                vec4 ubo0_location;
            } block0;
            layout (binding = 4) uniform ubo1
            {
                vec4 ubo1_location;
            } block1;
            uniform vec4 u_color;
            layout (triangles) in;
            layout (points, max_vertices = 1) out;
            out vec4 gs_out;
            void main()
            {
                gl_Position = gl_in[0].gl_Position;
                gl_Position += block0.ubo0_location + block1.ubo1_location;
                gs_out = u_color;
                EmitVertex();
            })";
    
        const std::string &fragmentShader =
            R"(#version 310 es
            precision highp float;
            in vec4 gs_out;
            layout(location = 0) out vec4 oColor;
            void main()
            {
                oColor = gs_out;
            })";
    
        ANGLE_GL_PROGRAM_WITH_GS(program, vertexShader, geometryShader, fragmentShader);
    
        constexpr GLenum kProps[]    = {GL_REFERENCED_BY_GEOMETRY_SHADER_EXT};
        constexpr GLsizei kPropCount = static_cast<GLsizei>(ArraySize(kProps));
        std::array<GLint, ArraySize(kProps)> params;
        GLsizei length;
    
        params.fill(1);
        GLuint index = glGetProgramResourceIndex(program, GL_PROGRAM_INPUT, "position");
        glGetProgramResourceiv(program, GL_PROGRAM_INPUT, index, kPropCount, kProps, kPropCount,
                               &length, params.data());
        EXPECT_GL_NO_ERROR();
        EXPECT_EQ(0, params[0]);
    
        params.fill(1);
        index = glGetProgramResourceIndex(program, GL_PROGRAM_OUTPUT, "oColor");
        glGetProgramResourceiv(program, GL_PROGRAM_OUTPUT, index, kPropCount, kProps, kPropCount,
                               &length, params.data());
        EXPECT_GL_NO_ERROR();
        EXPECT_EQ(0, params[0]);
    
        index = glGetProgramResourceIndex(program, GL_UNIFORM, "u_color");
        glGetProgramResourceiv(program, GL_UNIFORM, index, kPropCount, kProps, kPropCount, &length,
                               params.data());
        EXPECT_GL_NO_ERROR();
        EXPECT_EQ(1, params[0]);
    
        params.fill(0);
        index = glGetProgramResourceIndex(program, GL_UNIFORM_BLOCK, "ubo1");
        glGetProgramResourceiv(program, GL_UNIFORM_BLOCK, index, kPropCount, kProps, kPropCount,
                               &length, params.data());
        EXPECT_GL_NO_ERROR();
        EXPECT_EQ(1, params[0]);
    
        GLint maxGeometryShaderStorageBlocks = 0;
        glGetIntegerv(GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS_EXT, &maxGeometryShaderStorageBlocks);
        // The maximum number of shader storage blocks in a geometry shader can be 0.
        // [EXT_geometry_shader] Table 20.43gs
        if (maxGeometryShaderStorageBlocks > 0)
        {
            const std::string &geometryShaderWithSSBO =
                R"(#version 310 es
                #extension GL_EXT_geometry_shader : require
                layout (binding = 2) buffer ssbo
                {
                    vec4 ssbo_value;
                } block0;
                layout (triangles) in;
                layout (points, max_vertices = 1) out;
                out vec4 gs_out;
                void main()
                {
                    gl_Position = gl_in[0].gl_Position + block0.ssbo_value;
                    gs_out = block0.ssbo_value;
                    EmitVertex();
                })";
    
            ANGLE_GL_PROGRAM_WITH_GS(programWithSSBO, vertexShader, geometryShaderWithSSBO,
                                     fragmentShader);
    
            params.fill(0);
            index = glGetProgramResourceIndex(programWithSSBO, GL_SHADER_STORAGE_BLOCK, "ssbo");
            glGetProgramResourceiv(programWithSSBO, GL_SHADER_STORAGE_BLOCK, index, kPropCount, kProps,
                                   kPropCount, &length, params.data());
            EXPECT_GL_NO_ERROR();
            EXPECT_EQ(1, params[0]);
    
            params.fill(0);
            index = glGetProgramResourceIndex(programWithSSBO, GL_BUFFER_VARIABLE, "ssbo.ssbo_value");
            glGetProgramResourceiv(programWithSSBO, GL_BUFFER_VARIABLE, index, kPropCount, kProps,
                                   kPropCount, &length, params.data());
            EXPECT_GL_NO_ERROR();
            EXPECT_EQ(1, params[0]);
        }
    
        GLint maxGeometryAtomicCounterBuffers = 0;
        glGetIntegerv(GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS_EXT, &maxGeometryAtomicCounterBuffers);
        // The maximum number of atomic counter buffers in a geometry shader can be 0.
        // [EXT_geometry_shader] Table 20.43gs
        if (maxGeometryAtomicCounterBuffers > 0)
        {
            const std::string &geometryShaderWithAtomicCounters =
                R"(#version 310 es
                #extension GL_EXT_geometry_shader : require
                layout(binding = 1, offset = 0) uniform atomic_uint gs_counter;
                layout (triangles) in;
                layout (points, max_vertices = 1) out;
                out vec4 gs_out;
                void main()
                {
                    atomicCounterIncrement(gs_counter);
                    gl_Position = gl_in[0].gl_Position;
                    gs_out = vec4(1.0, 0.0, 0.0, 1.0);
                    EmitVertex();
                })";
    
            ANGLE_GL_PROGRAM_WITH_GS(programWithAtomicCounter, vertexShader,
                                     geometryShaderWithAtomicCounters, fragmentShader);
    
            params.fill(0);
            index = glGetProgramResourceIndex(programWithAtomicCounter, GL_UNIFORM, "gs_counter");
            EXPECT_GL_NO_ERROR();
            glGetProgramResourceiv(programWithAtomicCounter, GL_ATOMIC_COUNTER_BUFFER, index,
                                   kPropCount, kProps, kPropCount, &length, params.data());
            EXPECT_GL_NO_ERROR();
            EXPECT_EQ(1, params[0]);
        }
    }
    
    // Verify correct errors can be reported when we use illegal parameters on FramebufferTextureEXT.
    TEST_P(GeometryShaderTest, NegativeFramebufferTextureEXT)
    {
        ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_EXT_geometry_shader"));
    
        GLFramebuffer fbo;
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    
        GLTexture tex;
        glBindTexture(GL_TEXTURE_3D, tex);
        glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA8, 32, 32, 32, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        // [EXT_geometry_shader] Section 9.2.8, "Attaching Texture Images to a Framebuffer"
        // An INVALID_ENUM error is generated if <target> is not DRAW_FRAMEBUFFER, READ_FRAMEBUFFER, or
        // FRAMEBUFFER.
        glFramebufferTextureEXT(GL_TEXTURE_2D, GL_COLOR_ATTACHMENT0, tex, 0);
        EXPECT_GL_ERROR(GL_INVALID_ENUM);
    
        // An INVALID_ENUM error is generated if <attachment> is not one of the attachments in Table
        // 9.1.
        glFramebufferTextureEXT(GL_FRAMEBUFFER, GL_TEXTURE_2D, tex, 0);
        EXPECT_GL_ERROR(GL_INVALID_ENUM);
    
        // An INVALID_OPERATION error is generated if zero is bound to <target>.
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        glFramebufferTextureEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex, 0);
        EXPECT_GL_ERROR(GL_INVALID_OPERATION);
    
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    
        // An INVALID_VALUE error is generated if <texture> is not the name of a texture object, or if
        // <level> is not a supported texture level for <texture>.
        GLuint tex2;
        glGenTextures(1, &tex2);
        glDeleteTextures(1, &tex2);
        ASSERT_FALSE(glIsTexture(tex2));
        glFramebufferTextureEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex2, 0);
        EXPECT_GL_ERROR(GL_INVALID_VALUE);
    
        GLint max3DSize;
        glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &max3DSize);
        GLint max3DLevel = std::log2(max3DSize);
        glFramebufferTextureEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex, max3DLevel + 1);
        EXPECT_GL_ERROR(GL_INVALID_VALUE);
    }
    
    // Verify CheckFramebufferStatus can work correctly on layered depth and stencil attachments.
    TEST_P(GeometryShaderTest, LayeredFramebufferCompletenessWithDepthAttachment)
    {
        ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_EXT_geometry_shader"));
    
        GLint maxFramebufferLayers;
        glGetIntegerv(GL_MAX_FRAMEBUFFER_LAYERS_EXT, &maxFramebufferLayers);
    
        constexpr GLint kTexLayers = 2;
        ASSERT_LT(kTexLayers, maxFramebufferLayers);
    
        GLTexture layeredColorTex;
        glBindTexture(GL_TEXTURE_3D, layeredColorTex);
        glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA8, 32, 32, kTexLayers, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                     nullptr);
    
        // [EXT_geometry_shader] section 9.4.1, "Framebuffer Completeness"
        // If any framebuffer attachment is layered, all populated attachments must be layered.
        // {FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT }
        GLTexture layeredDepthStencilTex;
        glBindTexture(GL_TEXTURE_2D_ARRAY, layeredDepthStencilTex);
        glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_DEPTH24_STENCIL8, 32, 32, kTexLayers, 0,
                     GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, nullptr);
    
        // 1. Color attachment is layered, while depth attachment is not layered.
        GLFramebuffer fbo1;
        glBindFramebuffer(GL_FRAMEBUFFER, fbo1);
        glFramebufferTextureEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, layeredColorTex, 0);
        glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, layeredDepthStencilTex, 0, 0);
        GLenum status1 = glCheckFramebufferStatus(GL_FRAMEBUFFER);
        ASSERT_GL_NO_ERROR();
        EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT, status1);
    
        // 2. Color attachment is not layered, while depth attachment is layered.
        GLFramebuffer fbo2;
        glBindFramebuffer(GL_FRAMEBUFFER, fbo2);
        glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, layeredColorTex, 0, 0);
        glFramebufferTextureEXT(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, layeredDepthStencilTex, 0);
        GLenum status2 = glCheckFramebufferStatus(GL_FRAMEBUFFER);
        ASSERT_GL_NO_ERROR();
        EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT, status2);
    
        // 3. Color attachment is not layered, while stencil attachment is layered.
        GLFramebuffer fbo3;
        glBindFramebuffer(GL_FRAMEBUFFER, fbo3);
        glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, layeredColorTex, 0, 0);
        glFramebufferTextureEXT(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, layeredDepthStencilTex, 0);
        GLenum status3 = glCheckFramebufferStatus(GL_FRAMEBUFFER);
        ASSERT_GL_NO_ERROR();
        EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT, status3);
    
        // [EXT_geometry_shader] section 9.4.1, "Framebuffer Completeness"
        // If <image> is a three-dimensional texture or a two-dimensional array texture and the
        // attachment is layered, the depth or layer count, respectively, of the texture is less than or
        // equal to the value of MAX_FRAMEBUFFER_LAYERS_EXT.
        GLint maxArrayTextureLayers;
        glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &maxArrayTextureLayers);
        GLint depthTexLayer4 = maxFramebufferLayers + 1;
        ANGLE_SKIP_TEST_IF(maxArrayTextureLayers < depthTexLayer4);
    
        // Use a depth attachment whose layer count exceeds MAX_FRAMEBUFFER_LAYERS
        GLTexture depthTex4;
        glBindTexture(GL_TEXTURE_2D_ARRAY, depthTex4);
        glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_DEPTH24_STENCIL8, 32, 32, depthTexLayer4, 0,
                     GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, nullptr);
        GLFramebuffer fbo4;
        glBindFramebuffer(GL_FRAMEBUFFER, fbo4);
        glFramebufferTextureEXT(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTex4, 0);
        GLenum status4 = glCheckFramebufferStatus(GL_FRAMEBUFFER);
        ASSERT_GL_NO_ERROR();
        EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT, status4);
    }
    
    // Verify correct errors can be reported when we use layered cube map attachments on a framebuffer.
    TEST_P(GeometryShaderTest, NegativeLayeredFramebufferCompletenessWithCubeMapTextures)
    {
        ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_EXT_geometry_shader"));
    
        GLTexture tex;
        glBindTexture(GL_TEXTURE_CUBE_MAP, tex);
    
        GLFramebuffer fbo;
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
        glFramebufferTextureEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex, 0);
        GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
        ASSERT_GL_NO_ERROR();
        EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT, status);
    
        glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGBA8, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                     nullptr);
        glFramebufferTextureEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex, 0);
        ASSERT_GL_NO_ERROR();
        EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT, status);
    }
    
    ANGLE_INSTANTIATE_TEST(GeometryShaderTestES3, ES3_OPENGL(), ES3_OPENGLES(), ES3_D3D11());
    ANGLE_INSTANTIATE_TEST(GeometryShaderTest, ES31_OPENGL(), ES31_OPENGLES(), ES31_D3D11());
    }