Edit

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

Branch :

  • Show log

    Commit

  • Author : Jamie Madill
    Date : 2018-12-29 10:29:33
    Hash : ba319ba3
    Message : Re-land "Load entry points dynamically in tests and samples." Fixes the Android/ChromeOS/Fuchsia builds by using consistent EGL headers. This CL adds a dynamic loader generator based on XML files. It also refactors the entry point generation script to move the XML parsing into a helper class. Additionally this includes a new GLES 1.0 base header. The new header allows for function pointer types and hiding prototypes. All tests and samples now load ANGLE dynamically. In the future this will be extended to load entry points from the driver directly when possible. This will allow us to perform more accurate A/B testing. The new build configuration leads to some tests having more warnings applied. The CL includes fixes for the new warnings. Bug: angleproject:2995 Change-Id: I5a8772f41a0f89570b3736b785f44b7de1539b57 Reviewed-on: https://chromium-review.googlesource.com/c/1392382 Reviewed-by: Jamie Madill <jmadill@chromium.org> Commit-Queue: Jamie Madill <jmadill@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"));
    
        constexpr char kGS[] = 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, kGS);
    
        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.c_str(),
                                              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.c_str(), 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.c_str(),
                                       essl31_shaders::fs::Red());
        EXPECT_EQ(0u, program);
    
        program = CompileProgramWithGS(essl31_shaders::vs::Simple(), gsWithoutOutputPrimitive.c_str(),
                                       essl31_shaders::fs::Red());
        EXPECT_EQ(0u, program);
    
        program = CompileProgramWithGS(essl31_shaders::vs::Simple(), gsWithoutMaxVertices.c_str(),
                                       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"));
    
        constexpr char kVS[] = 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);
    })";
    
        constexpr char kGS[] = 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();
    })";
    
        constexpr char kFS[] = 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(kVS, kGS, kFS);
        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"));
    
        constexpr char kVS[] = 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;
    })";
    
        constexpr char kGS[] = 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();
    })";
    
        constexpr char kFS[] = 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(kVS, kGS, kFS);
        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().c_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().c_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"));
    
        constexpr char kVS[] = 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;
    })";
    
        constexpr char kGS[] = 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(kVS, kGS, 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);
    
        constexpr char kGS[] = 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();
    })";
    
        constexpr char kFS[] = 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(), kGS, kFS);
        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"));
    
        constexpr char kFS[] = 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(), kFS);
    
        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"));
    
        constexpr char kVS[] = R"(#version 310 es
    precision highp float;
    layout(location = 0) in highp vec4 position;
    void main()
    {
        gl_Position = position;
    })";
    
        constexpr char kGS[] = 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();
    })";
    
        constexpr char kFS[] = 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, kVS, kGS, kFS);
    
        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)
        {
            constexpr char kGSWithSSBO[] = 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, kVS, kGSWithSSBO, kFS);
    
            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)
        {
            constexpr char kGSWithAtomicCounters[] = 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, kVS, kGSWithAtomicCounters, kFS);
    
            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 = static_cast<GLint>(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());
    }  // namespace