Edit

kc3-lang/angle/src/tests/compiler_tests/GeometryShader_test.cpp

Branch :

  • Show log

    Commit

  • Author : Shahbaz Youssefi
    Date : 2021-01-06 23:20:36
    Hash : 157ddfdc
    Message : Build compiler_tests only if GLSL or ESSL build is enabled Most of these tests use TranslatorGLSL or TranslatorESSL (often through ShaderCompileTreeTest). In specialized builds that disable GLSL and ESSL shader generation, disable these unit tests. Bug: chromium:1161513 Change-Id: Ib87e651706f141a41ffdaebfb0cbe5168582e341 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2613202 Reviewed-by: Yuly Novikov <ynovikov@chromium.org> Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>

  • src/tests/compiler_tests/GeometryShader_test.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.
    //
    // GeometryShader_test.cpp:
    // tests for compiling a Geometry Shader
    //
    
    #include "GLSLANG/ShaderLang.h"
    #include "angle_gl.h"
    #include "compiler/translator/BaseTypes.h"
    #include "gtest/gtest.h"
    #include "tests/test_utils/ShaderCompileTreeTest.h"
    #include "tests/test_utils/compiler_test.h"
    
    using namespace sh;
    
    class GeometryShaderTest : public ShaderCompileTreeTest
    {
      public:
        GeometryShaderTest() {}
    
      protected:
        void initResources(ShBuiltInResources *resources) override
        {
            resources->EXT_geometry_shader = 1;
        }
    
        ::GLenum getShaderType() const override { return GL_GEOMETRY_SHADER_EXT; }
    
        ShShaderSpec getShaderSpec() const override { return SH_GLES3_1_SPEC; }
    
        bool compileGeometryShader(const std::string &statement1, const std::string &statement2)
        {
            std::ostringstream sstream;
            sstream << kHeader << statement1 << statement2 << kEmptyBody;
            return compile(sstream.str());
        }
    
        bool compileGeometryShader(const std::string &statement1,
                                   const std::string &statement2,
                                   const std::string &statement3,
                                   const std::string &statement4)
        {
            std::ostringstream sstream;
            sstream << kHeader << statement1 << statement2 << statement3 << statement4 << kEmptyBody;
            return compile(sstream.str());
        }
    
        static std::string GetGeometryShaderLayout(const std::string &layoutType,
                                                   const std::string &primitive,
                                                   int invocations,
                                                   int maxVertices)
        {
            std::ostringstream sstream;
    
            sstream << "layout (";
            if (!primitive.empty())
            {
                sstream << primitive;
            }
            if (invocations > 0)
            {
                sstream << ", invocations = " << invocations;
            }
            if (maxVertices >= 0)
            {
                sstream << ", max_vertices = " << maxVertices;
            }
            sstream << ") " << layoutType << ";" << std::endl;
    
            return sstream.str();
        }
    
        static std::string GetInputDeclaration(const std::string &var, int size)
        {
            std::ostringstream sstream;
    
            sstream << "in ";
            if (size < 0)
            {
                sstream << var << "[];\n";
            }
            else
            {
                sstream << var << "[" << size << "];\n";
            }
    
            return sstream.str();
        }
    
        const std::string kVersion = "#version 310 es\n";
        const std::string kHeader =
            "#version 310 es\n"
            "#extension GL_EXT_geometry_shader : require\n";
        const std::string kInputLayout  = "layout (points) in;\n";
        const std::string kOutputLayout = "layout (points, max_vertices = 1) out;\n";
    
        const std::array<std::string, 4> kInterpolationQualifiers = {{"flat", "smooth", "centroid"}};
        const std::map<std::string, int> kInputPrimitivesAndInputArraySizeMap = {
            {"points", 1},
            {"lines", 2},
            {"lines_adjacency", 4},
            {"triangles", 3},
            {"triangles_adjacency", 6}};
    
        const std::string kEmptyBody =
            "void main()\n"
            "{\n"
            "}\n";
    };
    
    class GeometryShaderOutputCodeTest : public MatchOutputCodeTest
    {
      public:
        GeometryShaderOutputCodeTest()
            : MatchOutputCodeTest(GL_GEOMETRY_SHADER_EXT, SH_OBJECT_CODE, SH_ESSL_OUTPUT)
        {
            getResources()->EXT_geometry_shader = 1;
        }
    };
    
    // Geometry Shaders are not supported in GLSL ES shaders version lower than 310.
    TEST_F(GeometryShaderTest, Version300)
    {
        const std::string &shaderString =
            R"(#version 300 es
            void main()
            {
            })";
    
        if (compile(shaderString))
        {
            FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
        }
    }
    
    // Geometry Shaders are not supported in GLSL ES shaders version 310 without extension
    // EXT_geometry_shader enabled.
    TEST_F(GeometryShaderTest, Version310WithoutExtension)
    {
        const std::string &shaderString =
            R"(#version 310 es
            void main()
            {
            })";
    
        if (compile(shaderString))
        {
            FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
        }
    }
    
    // Geometry Shaders are not supported in GLSL ES shaders version 310 with extension
    // EXT_geometry_shader disabled.
    TEST_F(GeometryShaderTest, Version310ExtensionDisabled)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : disable
            void main()
            {
            })";
    
        if (compile(shaderString))
        {
            FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
        }
    }
    
    // Geometry Shaders are supported in GLSL ES shaders version 310 with EXT_geometry_shader enabled.
    TEST_F(GeometryShaderTest, Version310WithEXTExtension)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout(points) in;
            layout(points, max_vertices = 1) out;
            void main()
            {
            })";
    
        if (!compile(shaderString))
        {
            FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
        }
    }
    
    // Missing the declaration of input primitive in a geometry shader should be a link error instead of
    // a compile error.
    TEST_F(GeometryShaderTest, NoInputPrimitives)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout(points, max_vertices = 1) out;
            void main()
            {
            })";
    
        if (!compile(shaderString))
        {
            FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
        }
    }
    
    // Geometry Shaders can only support 5 kinds of input primitives, which cannot be used as output
    // primitives except 'points'.
    // Skip testing "points" as it can be used as both input and output primitives.
    TEST_F(GeometryShaderTest, ValidInputPrimitives)
    {
        const std::array<std::string, 4> kInputPrimitives = {
            {"lines", "lines_adjacency", "triangles", "triangles_adjacency"}};
    
        for (const std::string &inputPrimitive : kInputPrimitives)
        {
            if (!compileGeometryShader(GetGeometryShaderLayout("in", inputPrimitive, -1, -1),
                                       kOutputLayout))
            {
                FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
            }
            if (compileGeometryShader(kInputLayout,
                                      GetGeometryShaderLayout("out", inputPrimitive, -1, 6)))
            {
                FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
            }
        }
    }
    
    // Geometry Shaders allow duplicated declaration of input primitive, but all of them must be same.
    TEST_F(GeometryShaderTest, RedeclareInputPrimitives)
    {
        const std::array<std::string, 5> kInputPrimitives = {
            {"points", "lines", "lines_adjacency", "triangles", "triangles_adjacency"}};
    
        for (GLuint i = 0; i < kInputPrimitives.size(); ++i)
        {
            const std::string &inputLayoutStr1 =
                GetGeometryShaderLayout("in", kInputPrimitives[i], -1, -1);
            if (!compileGeometryShader(inputLayoutStr1, inputLayoutStr1, kOutputLayout, ""))
            {
                FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
            }
    
            for (GLuint j = i + 1; j < kInputPrimitives.size(); ++j)
            {
                const std::string &inputLayoutStr2 =
                    GetGeometryShaderLayout("in", kInputPrimitives[j], -1, -1);
                if (compileGeometryShader(inputLayoutStr1, inputLayoutStr2, kOutputLayout, ""))
                {
                    FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
                }
            }
        }
    }
    
    // Geometry Shaders don't allow declaring different input primitives in one layout.
    TEST_F(GeometryShaderTest, DeclareDifferentInputPrimitivesInOneLayout)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points, triangles) in;
            layout (points, max_vertices = 1) out;
            void main()
            {
            })";
    
        if (compile(shaderString))
        {
            FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
        }
    }
    
    // Geometry Shaders don't allow 'invocations' < 1.
    TEST_F(GeometryShaderTest, InvocationsLessThanOne)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points, invocations = 0) in;
            layout (points, max_vertices = 1) out;
            void main()
            {
            })";
    
        if (compile(shaderString))
        {
            FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
        }
    }
    
    // Geometry Shaders allow declaring 'invocations' == 1 together with input primitive declaration in
    // one layout.
    TEST_F(GeometryShaderTest, InvocationsEqualsOne)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points, invocations = 1) in;
            layout (points, max_vertices = 1) out;
            void main()
            {
            })";
    
        if (!compile(shaderString))
        {
            FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
        }
    }
    
    // Geometry Shaders allow declaring 'invocations' == 1 in an individual layout.
    TEST_F(GeometryShaderTest, InvocationsEqualsOneInSeparatedLayout)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points) in;
            layout (invocations = 1) in;
            layout (points, max_vertices = 1) out;
            void main()
            {
            })";
    
        if (!compile(shaderString))
        {
            FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
        }
    }
    
    // Geometry Shaders don't allow 'invocations' larger than the implementation-dependent maximum value
    // (32 in this test).
    TEST_F(GeometryShaderTest, TooLargeInvocations)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points, invocations = 9989899) in;
            layout (points, max_vertices = 1) out;
            void main()
            {
            })";
    
        if (compile(shaderString))
        {
            FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
        }
    }
    
    // Geometry Shaders allow 'invocations' declared together with input primitives in one layout.
    TEST_F(GeometryShaderTest, InvocationsDeclaredWithInputPrimitives)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points, invocations = 3) in;
            layout (points, max_vertices = 1) out;
            void main()
            {
            })";
    
        if (!compile(shaderString))
        {
            FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
        }
    }
    
    // Geometry Shaders allow 'invocations' declared before input primitives in one input layout.
    TEST_F(GeometryShaderTest, InvocationsBeforeInputPrimitives)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (invocations = 3, points) in;
            layout (points, max_vertices = 1) out;
            void main()
            {
            })";
    
        if (!compile(shaderString))
        {
            FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
        }
    }
    
    // Geometry Shaders allow 'invocations' declared in an individual input layout.
    TEST_F(GeometryShaderTest, InvocationsInIndividualLayout)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points) in;
            layout (invocations = 3) in;
            layout (points, max_vertices = 1) out;
            void main()
            {
            })";
    
        if (!compile(shaderString))
        {
            FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
        }
    }
    
    // Geometry Shaders allow duplicated 'invocations' declarations.
    TEST_F(GeometryShaderTest, DuplicatedInvocations)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points, invocations = 3) in;
            layout (invocations = 3) in;
            layout (points, max_vertices = 1) out;
            void main()
            {
            })";
    
        if (!compile(shaderString))
        {
            FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
        }
    }
    
    // Geometry Shaders don't allow multiple different 'invocations' declarations in different
    // layouts.
    TEST_F(GeometryShaderTest, RedeclareDifferentInvocations)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points, invocations = 3) in;
            layout (invocations = 5) in;
            layout (points, max_vertices = 1) out;
            void main()
            {
            })";
    
        if (compile(shaderString))
        {
            FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
        }
    }
    
    // Geometry Shaders don't allow multiple different 'invocations' declarations in different
    // layouts.
    TEST_F(GeometryShaderTest, RedeclareDifferentInvocationsAfterInvocationEqualsOne)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points, invocations = 1) in;
            layout (invocations = 5) in;
            layout (points, max_vertices = 1) out;
            void main()
            {
            })";
    
        if (compile(shaderString))
        {
            FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
        }
    }
    
    // Geometry Shaders don't allow multiple different 'invocations' declarations in one layout.
    TEST_F(GeometryShaderTest, RedeclareDifferentInvocationsInOneLayout)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points, invocations = 3, invocations = 5) in;
            layout (points, max_vertices = 1) out;
            void main()
            {
            })";
    
        if (compile(shaderString))
        {
            FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
        }
    }
    
    // Geometry Shaders don't allow 'invocations' in out layouts.
    TEST_F(GeometryShaderTest, DeclareInvocationsInOutLayout)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points) in;
            layout (points, invocations = 3, max_vertices = 1) out;
            void main()
            {
            })";
    
        if (compile(shaderString))
        {
            FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
        }
    }
    
    // Geometry Shaders don't allow 'invocations' in layouts without 'in' qualifier.
    TEST_F(GeometryShaderTest, DeclareInvocationsInLayoutNoQualifier)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points) in;
            layout (invocations = 3);
            layout (points, max_vertices = 1) out;
            void main()
            {
            })";
    
        if (compile(shaderString))
        {
            FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
        }
    }
    
    // Geometry Shaders allow declaring output primitive before input primitive declaration.
    TEST_F(GeometryShaderTest, DeclareOutputPrimitiveBeforeInputPrimitiveDeclare)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points, max_vertices = 1) out;
            layout (points) in;
            void main()
            {
            })";
    
        if (!compile(shaderString))
        {
            FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
        }
    }
    
    // Geometry Shaders allow declaring 'max_vertices' before output primitive in one output layout.
    TEST_F(GeometryShaderTest, DeclareMaxVerticesBeforeOutputPrimitive)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points) in;
            layout (max_vertices = 1, points) out;
            void main()
            {
            })";
    
        if (!compile(shaderString))
        {
            FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
        }
    }
    
    // Missing the declaration of output primitive should be a link error instead of a compile error in
    // a geometry shader.
    TEST_F(GeometryShaderTest, NoOutputPrimitives)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points) in;
            layout (max_vertices = 1) out;
            void main()
            {
            })";
    
        if (!compile(shaderString))
        {
            FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
        }
    }
    
    // Geometry Shaders can only support 3 kinds of output primitives, which cannot be used as input
    // primitives except 'points'.
    // Skip testing "points" as it can be used as both input and output primitives.
    TEST_F(GeometryShaderTest, ValidateOutputPrimitives)
    {
        const std::string outputPrimitives[] = {"line_strip", "triangle_strip"};
    
        for (const std::string &outPrimitive : outputPrimitives)
        {
            if (!compileGeometryShader(kInputLayout,
                                       GetGeometryShaderLayout("out", outPrimitive, -1, 6)))
            {
                FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
            }
    
            if (compileGeometryShader(GetGeometryShaderLayout("in", outPrimitive, -1, -1),
                                      kOutputLayout))
            {
                FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
            }
        }
    }
    
    // Geometry Shaders allow duplicated output primitive declarations, but all of them must be same.
    TEST_F(GeometryShaderTest, RedeclareOutputPrimitives)
    {
        const std::array<std::string, 3> outPrimitives = {{"points", "line_strip", "triangle_strip"}};
    
        for (GLuint i = 0; i < outPrimitives.size(); i++)
        {
            constexpr int maxVertices = 1;
            const std::string &outputLayoutStr1 =
                GetGeometryShaderLayout("out", outPrimitives[i], -1, maxVertices);
            const std::string &outputLayoutStr2 =
                GetGeometryShaderLayout("out", outPrimitives[i], -1, -1);
            if (!compileGeometryShader(kInputLayout, outputLayoutStr1, outputLayoutStr2, ""))
            {
                FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
            }
            for (GLuint j = i + 1; j < outPrimitives.size(); j++)
            {
                const std::string &outputLayoutStr3 =
                    GetGeometryShaderLayout("out", outPrimitives[j], -1, -1);
                if (compileGeometryShader(kInputLayout, outputLayoutStr1, outputLayoutStr3, ""))
                {
                    FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
                }
            }
        }
    }
    
    // Geometry Shaders don't allow declaring different output primitives in one layout.
    TEST_F(GeometryShaderTest, RedeclareDifferentOutputPrimitivesInOneLayout)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points) in;
            layout (points, max_vertices = 3, line_strip) out;
            void main()
            {
            })";
    
        if (compile(shaderString))
        {
            FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
        }
    }
    
    // Missing the declarations of output primitives and 'max_vertices' in a geometry shader should
    // be a link error instead of a compile error.
    TEST_F(GeometryShaderTest, NoOutLayouts)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points) in;
            void main()
            {
            })";
    
        if (!compile(shaderString))
        {
            FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
        }
    }
    
    // Missing the declarations of 'max_vertices' in a geometry shader should be a link error
    // instead of a compile error.
    TEST_F(GeometryShaderTest, NoMaxVertices)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points) in;
            layout (points) out;
            void main()
            {
            })";
    
        if (!compile(shaderString))
        {
            FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
        }
    }
    
    // Geometry Shaders cannot declare a negative 'max_vertices'.
    TEST_F(GeometryShaderTest, NegativeMaxVertices)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points) in;
            layout (points, max_vertices = -1) out;
            void main()
            {
            })";
    
        if (compile(shaderString))
        {
            FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
        }
    }
    
    // Geometry Shaders allow max_vertices == 0.
    TEST_F(GeometryShaderTest, ZeroMaxVertices)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points) in;
            layout (points, max_vertices = 0) out;
            void main()
            {
            })";
    
        if (!compile(shaderString))
        {
            FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
        }
    }
    
    // Geometry Shaders cannot declare a 'max_vertices' that is greater than
    // MAX_GEOMETRY_OUTPUT_VERTICES_EXT (256 in this test).
    TEST_F(GeometryShaderTest, TooLargeMaxVertices)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points) in;
            layout (points, max_vertices = 257) out;
            void main()
            {
            })";
    
        if (compile(shaderString))
        {
            FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
        }
    }
    
    // Geometry Shaders can declare 'max_vertices' in an individual out layout.
    TEST_F(GeometryShaderTest, MaxVerticesInIndividualLayout)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points) in;
            layout (points) out;
            layout (max_vertices = 1) out;
            void main()
            {
            })";
    
        if (!compile(shaderString))
        {
            FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
        }
    }
    
    // Geometry Shaders allow duplicated 'max_vertices' declarations.
    TEST_F(GeometryShaderTest, DuplicatedMaxVertices)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points) in;
            layout (points, max_vertices = 1) out;
            layout (max_vertices = 1) out;
            void main()
            {
            })";
    
        if (!compile(shaderString))
        {
            FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
        }
    }
    
    // Geometry Shaders don't allow declaring different 'max_vertices'.
    TEST_F(GeometryShaderTest, RedeclareDifferentMaxVertices)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points) in;
            layout (points, max_vertices = 1) out;
            layout (max_vertices = 2) out;
            void main()
            {
            })";
    
        if (compile(shaderString))
        {
            FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
        }
    }
    
    // Geometry Shaders don't allow declaring different 'max_vertices'.
    TEST_F(GeometryShaderTest, RedeclareDifferentMaxVerticesInOneLayout)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points) in;
            layout (points, max_vertices = 2, max_vertices = 1) out;
            void main()
            {
            })";
    
        if (compile(shaderString))
        {
            FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
        }
    }
    
    // Geometry Shaders don't allow 'location' declared with input/output primitives in one layout.
    TEST_F(GeometryShaderTest, InvalidLocation)
    {
        const std::string &shaderString1 =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points, location = 1) in;
            layout (points, max_vertices = 2) out;
            void main()
            {
            })";
    
        const std::string &shaderString2 =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points) in;
            layout (invocations = 2, location = 1) in;
            layout (points, max_vertices = 2) out;
            void main()
            {
            })";
    
        const std::string &shaderString3 =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points) in;
            layout (points, location = 3, max_vertices = 2) out;
            void main()
            {
            })";
    
        const std::string &shaderString4 =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points) in;
            layout (points) out;
            layout (max_vertices = 2, location = 3) out;
            void main()
            {
            })";
    
        if (compile(shaderString1) || compile(shaderString2) || compile(shaderString3) ||
            compile(shaderString4))
        {
            FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
        }
    }
    
    // Geometry Shaders don't allow invalid layout qualifier declarations.
    TEST_F(GeometryShaderTest, InvalidLayoutQualifiers)
    {
        const std::string &shaderString1 =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points, abc) in;
            layout (points, max_vertices = 2) out;
            void main()
            {
            })";
    
        const std::string &shaderString2 =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points) in;
            layout (points, abc, max_vertices = 2) out;
            void main()
            {
            })";
    
        const std::string &shaderString3 =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points, xyz = 2) in;
            layout (points, max_vertices = 2) out;
            void main()
            {
            })";
    
        const std::string &shaderString4 =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points) in;
            layout (points) out;
            layout (max_vertices = 2, xyz = 3) out;
            void main()
            {
            })";
    
        if (compile(shaderString1) || compile(shaderString2) || compile(shaderString3) ||
            compile(shaderString4))
        {
            FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
        }
    }
    
    // Verify that indexing an array with a constant integer on gl_in is legal.
    TEST_F(GeometryShaderTest, IndexGLInByConstantInteger)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points) in;
            layout (points, max_vertices = 2) out;
            void main()
            {
                vec4 position;
                position = gl_in[0].gl_Position;
            })";
    
        if (!compile(shaderString))
        {
            FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
        }
    }
    
    // Verify that indexing an array with an integer variable on gl_in is legal.
    TEST_F(GeometryShaderTest, IndexGLInByVariable)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (lines) in;
            layout (points, max_vertices = 2) out;
            void main()
            {
                vec4 position;
                for (int i = 0; i < 2; i++)
                {
                    position = gl_in[i].gl_Position;
                }
            })";
    
        if (!compile(shaderString))
        {
            FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
        }
    }
    
    // Verify that indexing an array on gl_in without input primitive declaration causes a compile
    // error.
    TEST_F(GeometryShaderTest, IndexGLInWithoutInputPrimitive)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points, max_vertices = 2) out;
            void main()
            {
                vec4 position = gl_in[0].gl_Position;
            })";
    
        if (compile(shaderString))
        {
            FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
        }
    }
    
    // Verify that using gl_in.length() without input primitive declaration causes a compile error.
    TEST_F(GeometryShaderTest, UseGLInLengthWithoutInputPrimitive)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points, max_vertices = 2) out;
            void main()
            {
                int length = gl_in.length();
            })";
    
        if (compile(shaderString))
        {
            FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
        }
    }
    
    // Verify that using gl_in.length() with input primitive declaration can compile.
    TEST_F(GeometryShaderTest, UseGLInLengthWithInputPrimitive)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points) in;
            layout (points, max_vertices = 2) out;
            void main()
            {
                gl_Position = vec4(gl_in.length());
                EmitVertex();
            })";
    
        if (!compile(shaderString))
        {
            FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
        }
    }
    
    // Verify that gl_in[].gl_Position cannot be l-value.
    TEST_F(GeometryShaderTest, AssignValueToGLIn)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points) in;
            layout (points, max_vertices = 2) out;
            void main()
            {
                gl_in[0].gl_Position = vec4(0, 0, 0, 1);
            })";
        if (compile(shaderString))
        {
            FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
        }
    }
    
    // Verify Geometry Shader supports all required built-in variables.
    TEST_F(GeometryShaderTest, BuiltInVariables)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points, invocations = 2) in;
            layout (points, max_vertices = 2) out;
            void main()
            {
                gl_Position = gl_in[gl_InvocationID].gl_Position;
                int invocation = gl_InvocationID;
                gl_Layer = invocation;
                int primitiveIn = gl_PrimitiveIDIn;
                gl_PrimitiveID = primitiveIn;
            })";
        if (!compile(shaderString))
        {
            FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
        }
    }
    
    // Verify that gl_PrimitiveIDIn cannot be l-value.
    TEST_F(GeometryShaderTest, AssignValueToGLPrimitiveIn)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points, invocations = 2) in;
            layout (points, max_vertices = 2) out;
            void main()
            {
                gl_PrimitiveIDIn = 1;
            })";
        if (compile(shaderString))
        {
            FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
        }
    }
    
    // Verify that gl_InvocationID cannot be l-value.
    TEST_F(GeometryShaderTest, AssignValueToGLInvocations)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points, invocations = 2) in;
            layout (points, max_vertices = 2) out;
            void main()
            {
                gl_InvocationID = 1;
            })";
        if (compile(shaderString))
        {
            FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
        }
    }
    
    // Verify that both EmitVertex() and EndPrimitive() are supported in Geometry Shader.
    TEST_F(GeometryShaderTest, GeometryShaderBuiltInFunctions)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points) in;
            layout (points, max_vertices = 2) out;
            void main()
            {
                gl_Position = gl_in[0].gl_Position;
                EmitVertex();
                EndPrimitive();
            })";
    
        if (!compile(shaderString))
        {
            FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
        }
    }
    
    // Verify that using EmitVertex() or EndPrimitive() without GL_EXT_geometry_shader declared causes a
    // compile error.
    TEST_F(GeometryShaderTest, GeometryShaderBuiltInFunctionsWithoutExtension)
    {
        const std::string &shaderString1 =
            R"(#version 310 es
            void main()
            {
                EmitVertex();
            })";
    
        const std::string &shaderString2 =
            R"(#version 310 es
            void main()
            {
                EndPrimitive();
            })";
    
        if (compile(shaderString1) || compile(shaderString2))
        {
            FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
        }
    }
    
    // Verify that all required built-in constant values are supported in Geometry Shaders
    TEST_F(GeometryShaderTest, GeometryShaderBuiltInConstants)
    {
        const std::string &kShaderHeader =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points) in;
            layout (points, max_vertices = 2) out;
            void main()
            {
                gl_Position.x = float()";
    
        const std::array<std::string, 9> kGeometryShaderBuiltinConstants = {{
            "gl_MaxGeometryInputComponents",
            "gl_MaxGeometryOutputComponents",
            "gl_MaxGeometryImageUniforms",
            "gl_MaxGeometryTextureImageUnits",
            "gl_MaxGeometryOutputVertices",
            "gl_MaxGeometryTotalOutputComponents",
            "gl_MaxGeometryUniformComponents",
            "gl_MaxGeometryAtomicCounters",
            "gl_MaxGeometryAtomicCounterBuffers",
        }};
    
        const std::string &kShaderTail =
            R"();
                EmitVertex();
            })";
    
        for (const std::string &kGSBuiltinConstant : kGeometryShaderBuiltinConstants)
        {
            std::ostringstream ostream;
            ostream << kShaderHeader << kGSBuiltinConstant << kShaderTail;
            if (!compile(ostream.str()))
            {
                FAIL() << "Shader compilation failed, expecting success: \n" << mInfoLog;
            }
        }
    }
    
    // Verify that using any Geometry Shader built-in constant values without GL_EXT_geometry_shader
    // declared causes a compile error.
    TEST_F(GeometryShaderTest, GeometryShaderBuiltInConstantsWithoutExtension)
    {
        const std::string &kShaderHeader =
            "#version 310 es\n"
            "void main()\n"
            "{\n"
            "    int val = ";
    
        const std::array<std::string, 9> kGeometryShaderBuiltinConstants = {{
            "gl_MaxGeometryInputComponents",
            "gl_MaxGeometryOutputComponents",
            "gl_MaxGeometryImageUniforms",
            "gl_MaxGeometryTextureImageUnits",
            "gl_MaxGeometryOutputVertices",
            "gl_MaxGeometryTotalOutputComponents",
            "gl_MaxGeometryUniformComponents",
            "gl_MaxGeometryAtomicCounters",
            "gl_MaxGeometryAtomicCounterBuffers",
        }};
    
        const std::string &kShaderTail =
            ";\n"
            "}\n";
    
        for (const std::string &kGSBuiltinConstant : kGeometryShaderBuiltinConstants)
        {
            std::ostringstream ostream;
            ostream << kShaderHeader << kGSBuiltinConstant << kShaderTail;
            if (compile(ostream.str()))
            {
                FAIL() << "Shader compilation succeeded, expecting failure: \n" << mInfoLog;
            }
        }
    }
    
    // Verify that Geometry Shaders cannot accept non-array inputs.
    TEST_F(GeometryShaderTest, NonArrayInput)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points) in;
            layout (points, max_vertices = 1) out;
            in vec4 texcoord;
            void main()
            {
            })";
    
        if (compile(shaderString))
        {
            FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
        }
    }
    
    // Verify that it is a compile error to declare an unsized Geometry Shader input before a valid
    // input primitive declaration.
    TEST_F(GeometryShaderTest, DeclareUnsizedInputBeforeInputPrimitive)
    {
        const std::string &shaderString1 =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            in vec4 texcoord[];
            layout (points) in;
            layout (points, max_vertices = 1) out;
            void main()
            {
                vec4 coord = texcoord[0];
                int length = texcoord.length();
            })";
    
        const std::string &shaderString2 =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            in vec4 texcoord1[1];
            in vec4 texcoord2[];
            layout (points) in;
            layout (points, max_vertices = 1) out;
            void main()
            {
                vec4 coord = texcoord2[0];
                int length = texcoord2.length();
            })";
    
        if (compile(shaderString1) || compile(shaderString2))
        {
            FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
        }
    }
    
    // Verify that it is a compile error to declare an unsized Geometry Shader input without a valid
    // input primitive declaration.
    TEST_F(GeometryShaderTest, DeclareUnsizedInputWithoutInputPrimitive)
    {
        const std::string &shaderString1 =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points, max_vertices = 1) out;
            in vec4 texcoord[];
            void main()
            {
            })";
    
        const std::string &shaderString2 =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points, max_vertices = 1) out;
            in vec4 texcoord1[1];
            in vec4 texcoord2[];
            void main()
            {
            })";
    
        if (compile(shaderString1) || compile(shaderString2))
        {
            FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
        }
    }
    
    // Verify that indexing an unsized Geometry Shader input which is declared after a
    // valid input primitive declaration can compile.
    TEST_F(GeometryShaderTest, IndexingUnsizedInputDeclaredAfterInputPrimitive)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points) in;
            layout (points, max_vertices = 1) out;
            in vec4 texcoord[], texcoord2[];
            in vec4[] texcoord3, texcoord4;
            void main()
            {
                gl_Position = texcoord[0] + texcoord2[0] + texcoord3[0] + texcoord4[0];
                EmitVertex();
            })";
    
        if (!compile(shaderString))
        {
            FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
        }
    }
    
    // Verify that calling length() function on an unsized Geometry Shader input which
    // is declared before a valid input primitive declaration can compile.
    TEST_F(GeometryShaderTest, CallingLengthOnUnsizedInputDeclaredAfterInputPrimitive)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points) in;
            layout (points, max_vertices = 1) out;
            in vec4 texcoord[];
            void main()
            {
                gl_Position = vec4(texcoord.length());
                EmitVertex();
            })";
    
        if (!compile(shaderString))
        {
            FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
        }
    }
    
    // Verify that assigning a value to the input of a geometry shader causes a compile error.
    TEST_F(GeometryShaderTest, AssignValueToInput)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points) in;
            layout (points, max_vertices = 1) out;
            in vec4 texcoord[];
            void main()
            {
                texcoord[0] = vec4(1.0, 0.0, 0.0, 1.0);
            })";
    
        if (compile(shaderString))
        {
            FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
        }
    }
    
    // Geometry Shaders allow inputs with location qualifier.
    TEST_F(GeometryShaderTest, InputWithLocations)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (triangles) in;
            layout (points, max_vertices = 1) out;
            layout (location = 0) in vec4 texcoord1[];
            layout (location = 1) in vec4 texcoord2[];
            void main()
            {
                int index = 0;
                vec4 coord1 = texcoord1[0];
                vec4 coord2 = texcoord2[index];
                gl_Position = coord1 + coord2;
            })";
    
        if (!compile(shaderString))
        {
            FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
        }
    }
    
    // Geometry Shaders allow inputs with explicit size declared before the declaration of the
    // input primitive, but they should have same size and match the declaration of the
    // following input primitive declarations.
    TEST_F(GeometryShaderTest, InputWithSizeBeforeInputPrimitive)
    {
        for (auto &primitiveAndArraySize : kInputPrimitivesAndInputArraySizeMap)
        {
            const std::string &inputLayoutStr =
                GetGeometryShaderLayout("in", primitiveAndArraySize.first, -1, -1);
            const int inputSize = primitiveAndArraySize.second;
    
            const std::string &inputDeclaration1 = GetInputDeclaration("vec4 input1", inputSize);
            if (!compileGeometryShader(inputDeclaration1, "", inputLayoutStr, kOutputLayout))
            {
                FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
            }
    
            const std::string &inputDeclaration2 = GetInputDeclaration("vec4 input2", inputSize + 1);
            if (compileGeometryShader(inputDeclaration2, "", inputLayoutStr, kOutputLayout) ||
                compileGeometryShader(inputDeclaration1, inputDeclaration2, inputLayoutStr,
                                      kOutputLayout))
            {
                FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
            }
        }
    }
    
    // Geometry shaders allow inputs with explicit size declared after the declaration of the
    // input primitive, but their sizes should match the previous input primitive declaration.
    TEST_F(GeometryShaderTest, InputWithSizeAfterInputPrimitive)
    {
        for (auto &primitiveAndArraySize : kInputPrimitivesAndInputArraySizeMap)
        {
            const std::string &inputLayoutStr =
                GetGeometryShaderLayout("in", primitiveAndArraySize.first, -1, -1);
            const int inputSize = primitiveAndArraySize.second;
    
            const std::string &inputDeclaration1 = GetInputDeclaration("vec4 input1", inputSize);
            if (!compileGeometryShader(inputLayoutStr, kOutputLayout, inputDeclaration1, ""))
            {
                FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
            }
    
            const std::string &inputDeclaration2 = GetInputDeclaration("vec4 input2", inputSize + 1);
            if (compileGeometryShader(inputLayoutStr, kOutputLayout, inputDeclaration2, ""))
            {
                FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
            }
        }
    }
    
    // Verify that Geometry Shaders accept non-array outputs.
    TEST_F(GeometryShaderTest, NonArrayOutputs)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points) in;
            layout (points, max_vertices = 1) out;
            out vec4 color;
            void main()
            {
            })";
    
        if (!compile(shaderString))
        {
            FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
        }
    }
    
    // Verify that Geometry Shaders allow declaring outputs with 'location' layout qualifier.
    TEST_F(GeometryShaderTest, OutputsWithLocation)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (triangles) in;
            layout (points, max_vertices = 1) out;
            layout (location = 0) out vec4 color1;
            layout (location = 1) out vec4 color2;
            void main()
            {
                color1 = vec4(0.0, 1.0, 0.0, 1.0);
                color2 = vec4(1.0, 0.0, 0.0, 1.0);
            })";
    
        if (!compile(shaderString))
        {
            FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
        }
    }
    
    // Geometry Shaders allow declaring sized array outputs.
    TEST_F(GeometryShaderTest, SizedArrayOutputs)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (triangles) in;
            layout (points, max_vertices = 1) out;
            out vec4 color[2];
            void main()
            {
                color[0] = vec4(0.0, 1.0, 0.0, 1.0);
                color[1] = vec4(1.0, 0.0, 0.0, 1.0);
            })";
    
        if (!compile(shaderString))
        {
            FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
        }
    }
    
    // Verify that Geometry Shader outputs cannot be declared as an unsized array.
    TEST_F(GeometryShaderTest, UnsizedArrayOutputs)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (triangles) in;
            layout (points, max_vertices = 1) out;
            out vec4 color[];
            void main()
            {
            })";
    
        if (compile(shaderString))
        {
            FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
        }
    }
    
    // Verify that Geometry Shader inputs can use interpolation qualifiers.
    TEST_F(GeometryShaderTest, InputWithInterpolationQualifiers)
    {
        for (const std::string &qualifier : kInterpolationQualifiers)
        {
            std::ostringstream stream;
            stream << kHeader << kInputLayout << kOutputLayout << qualifier << " in vec4 texcoord[];\n"
                   << kEmptyBody;
    
            if (!compile(stream.str()))
            {
                FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
            }
        }
    }
    
    // Verify that Geometry Shader outputs can use interpolation qualifiers.
    TEST_F(GeometryShaderTest, OutputWithInterpolationQualifiers)
    {
        for (const std::string &qualifier : kInterpolationQualifiers)
        {
            std::ostringstream stream;
            stream << kHeader << kInputLayout << kOutputLayout << qualifier << " out vec4 color;\n"
                   << kEmptyBody;
    
            if (!compile(stream.str()))
            {
                FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
            }
        }
    }
    
    // Verify that Geometry Shader outputs can use 'invariant' qualifier.
    TEST_F(GeometryShaderTest, InvariantOutput)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points) in;
            layout (points, max_vertices = 2) out;
            invariant out vec4 gs_output;
            void main()
            {
                gl_Position = gl_in[0].gl_Position;
            })";
    
        if (!compile(shaderString))
        {
            FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
        }
    }
    
    // Verify that the member of gl_in won't be incorrectly changed in the output shader string.
    TEST_F(GeometryShaderOutputCodeTest, ValidateGLInMembersInOutputShaderString)
    {
        const std::string &shaderString1 =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (lines) in;
            layout (points, max_vertices = 2) out;
            void main()
            {
                vec4 position;
                for (int i = 0; i < 2; i++)
                {
                    position = gl_in[i].gl_Position;
                }
            })";
    
        compile(shaderString1);
        EXPECT_TRUE(foundInESSLCode("].gl_Position"));
    
        const std::string &shaderString2 =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points) in;
            layout (points, max_vertices = 2) out;
            void main()
            {
                vec4 position;
                position = gl_in[0].gl_Position;
            })";
    
        compile(shaderString2);
        EXPECT_TRUE(foundInESSLCode("].gl_Position"));
    }
    
    // Verify that geometry shader inputs can be declared as struct arrays.
    TEST_F(GeometryShaderTest, StructArrayInput)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points) in;
            layout (points, max_vertices = 2) out;
            struct S
            {
                float value1;
                vec4 value2;
            };
            in S gs_input[];
            out S gs_output;
            void main()
            {
                gs_output = gs_input[0];
            })";
    
        if (!compile(shaderString))
        {
            FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
        }
    }
    
    // Verify that geometry shader outputs cannot be declared as struct arrays.
    TEST_F(GeometryShaderTest, StructArrayOutput)
    {
        const std::string &shaderString =
            R"(#version 310 es
            #extension GL_EXT_geometry_shader : require
            layout (points) in;
            layout (points, max_vertices = 2) out;
            struct S
            {
                float value1;
                vec4 value2;
            };
            out S gs_output[1];
            void main()
            {
                gs_output[0].value1 = 1.0;
                gs_output[0].value2 = vec4(1.0, 0.0, 0.0, 1.0);
            })";
    
        if (compile(shaderString))
        {
            FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
        }
    }