Edit

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

Branch :

  • Show log

    Commit

  • Author : Olli Etuaho
    Date : 2016-09-27 11:15:38
    Hash : 1be4d493
    Message : Fix handling integer overflow in constant folding Integer operations that overflow are defined to wrap in the ESSL 3.00.6 spec. Constant folding that happens inside the shader translator should also follow the wrapping rules. The new implementations of wrapping integer addition and subtraction use unsigned integers to perform calculations. Unsigned integers are defined to implement arithmetic in modulo 2^n in the C++ spec. This behavior is also leveraged to implement wrapping unsigned integer multiplication. The implementation of wrapping signed integer multiplication is slightly trickier. The operands are casted to a wider type to perform the multiplication in a way that doesn't overflow, and then the result is truncated and casted back to the narrower integer type. Incorrect tests that expected errors to be generated from integer overflow in constant folding are removed. BUG=chromium:637050 TEST=angle_unittests Change-Id: I0de7e25881d254803455fbf22907c192f49d09ff Reviewed-on: https://chromium-review.googlesource.com/390252 Commit-Queue: Olli Etuaho <oetuaho@nvidia.com> Reviewed-by: Corentin Wallez <cwallez@chromium.org>

  • src/tests/gl_tests/GLSLTest.cpp
  • //
    // Copyright 2015 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.
    //
    
    #include "test_utils/ANGLETest.h"
    
    #include "libANGLE/Context.h"
    #include "libANGLE/Program.h"
    #include "test_utils/gl_raii.h"
    
    using namespace angle;
    
    namespace
    {
    
    class GLSLTest : public ANGLETest
    {
      protected:
        GLSLTest()
        {
            setWindowWidth(128);
            setWindowHeight(128);
            setConfigRedBits(8);
            setConfigGreenBits(8);
            setConfigBlueBits(8);
            setConfigAlphaBits(8);
        }
    
        virtual void SetUp()
        {
            ANGLETest::SetUp();
    
            mSimpleVSSource = SHADER_SOURCE
            (
                attribute vec4 inputAttribute;
                void main()
                {
                    gl_Position = inputAttribute;
                }
            );
        }
    
        std::string GenerateVaryingType(GLint vectorSize)
        {
            char varyingType[10];
    
            if (vectorSize == 1)
            {
                sprintf(varyingType, "float");
            }
            else
            {
                sprintf(varyingType, "vec%d", vectorSize);
            }
    
            return std::string(varyingType);
        }
    
        std::string GenerateVectorVaryingDeclaration(GLint vectorSize, GLint arraySize, GLint id)
        {
            char buff[100];
    
            if (arraySize == 1)
            {
                sprintf(buff, "varying %s v%d;\n", GenerateVaryingType(vectorSize).c_str(), id);
            }
            else
            {
                sprintf(buff, "varying %s v%d[%d];\n", GenerateVaryingType(vectorSize).c_str(), id, arraySize);
            }
    
            return std::string(buff);
        }
    
        std::string GenerateVectorVaryingSettingCode(GLint vectorSize, GLint arraySize, GLint id)
        {
            std::string returnString;
            char buff[100];
    
            if (arraySize == 1)
            {
                sprintf(buff, "\t v%d = %s(1.0);\n", id, GenerateVaryingType(vectorSize).c_str());
                returnString += buff;
            }
            else
            {
                for (int i = 0; i < arraySize; i++)
                {
                    sprintf(buff, "\t v%d[%d] = %s(1.0);\n", id, i, GenerateVaryingType(vectorSize).c_str());
                    returnString += buff;
                }
            }
    
            return returnString;
        }
    
        std::string GenerateVectorVaryingUseCode(GLint arraySize, GLint id)
        {
            if (arraySize == 1)
            {
                char buff[100];
                sprintf(buff, "v%d + ", id);
                return std::string(buff);
            }
            else
            {
                std::string returnString;
                for (int i = 0; i < arraySize; i++)
                {
                    char buff[100];
                    sprintf(buff, "v%d[%d] + ", id, i);
                    returnString += buff;
                }
                return returnString;
            }
        }
    
        void GenerateGLSLWithVaryings(GLint floatCount, GLint floatArrayCount, GLint vec2Count, GLint vec2ArrayCount, GLint vec3Count, GLint vec3ArrayCount,
                                      GLint vec4Count, GLint vec4ArrayCount, bool useFragCoord, bool usePointCoord, bool usePointSize,
                                      std::string* fragmentShader, std::string* vertexShader)
        {
            // Generate a string declaring the varyings, to share between the fragment shader and the vertex shader.
            std::string varyingDeclaration;
    
            unsigned int varyingCount = 0;
    
            for (GLint i = 0; i < floatCount; i++)
            {
                varyingDeclaration += GenerateVectorVaryingDeclaration(1, 1, varyingCount);
                varyingCount += 1;
            }
    
            for (GLint i = 0; i < floatArrayCount; i++)
            {
                varyingDeclaration += GenerateVectorVaryingDeclaration(1, 2, varyingCount);
                varyingCount += 1;
            }
    
            for (GLint i = 0; i < vec2Count; i++)
            {
                varyingDeclaration += GenerateVectorVaryingDeclaration(2, 1, varyingCount);
                varyingCount += 1;
            }
    
            for (GLint i = 0; i < vec2ArrayCount; i++)
            {
                varyingDeclaration += GenerateVectorVaryingDeclaration(2, 2, varyingCount);
                varyingCount += 1;
            }
    
            for (GLint i = 0; i < vec3Count; i++)
            {
                varyingDeclaration += GenerateVectorVaryingDeclaration(3, 1, varyingCount);
                varyingCount += 1;
            }
    
            for (GLint i = 0; i < vec3ArrayCount; i++)
            {
                varyingDeclaration += GenerateVectorVaryingDeclaration(3, 2, varyingCount);
                varyingCount += 1;
            }
    
            for (GLint i = 0; i < vec4Count; i++)
            {
                varyingDeclaration += GenerateVectorVaryingDeclaration(4, 1, varyingCount);
                varyingCount += 1;
            }
    
            for (GLint i = 0; i < vec4ArrayCount; i++)
            {
                varyingDeclaration += GenerateVectorVaryingDeclaration(4, 2, varyingCount);
                varyingCount += 1;
            }
    
            // Generate the vertex shader
            vertexShader->clear();
            vertexShader->append(varyingDeclaration);
            vertexShader->append("\nvoid main()\n{\n");
    
            unsigned int currentVSVarying = 0;
    
            for (GLint i = 0; i < floatCount; i++)
            {
                vertexShader->append(GenerateVectorVaryingSettingCode(1, 1, currentVSVarying));
                currentVSVarying += 1;
            }
    
            for (GLint i = 0; i < floatArrayCount; i++)
            {
                vertexShader->append(GenerateVectorVaryingSettingCode(1, 2, currentVSVarying));
                currentVSVarying += 1;
            }
    
            for (GLint i = 0; i < vec2Count; i++)
            {
                vertexShader->append(GenerateVectorVaryingSettingCode(2, 1, currentVSVarying));
                currentVSVarying += 1;
            }
    
            for (GLint i = 0; i < vec2ArrayCount; i++)
            {
                vertexShader->append(GenerateVectorVaryingSettingCode(2, 2, currentVSVarying));
                currentVSVarying += 1;
            }
    
            for (GLint i = 0; i < vec3Count; i++)
            {
                vertexShader->append(GenerateVectorVaryingSettingCode(3, 1, currentVSVarying));
                currentVSVarying += 1;
            }
    
            for (GLint i = 0; i < vec3ArrayCount; i++)
            {
                vertexShader->append(GenerateVectorVaryingSettingCode(3, 2, currentVSVarying));
                currentVSVarying += 1;
            }
    
            for (GLint i = 0; i < vec4Count; i++)
            {
                vertexShader->append(GenerateVectorVaryingSettingCode(4, 1, currentVSVarying));
                currentVSVarying += 1;
            }
    
            for (GLint i = 0; i < vec4ArrayCount; i++)
            {
                vertexShader->append(GenerateVectorVaryingSettingCode(4, 2, currentVSVarying));
                currentVSVarying += 1;
            }
    
            if (usePointSize)
            {
                vertexShader->append("gl_PointSize = 1.0;\n");
            }
    
            vertexShader->append("}\n");
    
            // Generate the fragment shader
            fragmentShader->clear();
            fragmentShader->append("precision highp float;\n");
            fragmentShader->append(varyingDeclaration);
            fragmentShader->append("\nvoid main() \n{ \n\tvec4 retColor = vec4(0,0,0,0);\n");
    
            unsigned int currentFSVarying = 0;
    
            // Make use of the float varyings
            fragmentShader->append("\tretColor += vec4(");
    
            for (GLint i = 0; i < floatCount; i++)
            {
                fragmentShader->append(GenerateVectorVaryingUseCode(1, currentFSVarying));
                currentFSVarying += 1;
            }
    
            for (GLint i = 0; i < floatArrayCount; i++)
            {
                fragmentShader->append(GenerateVectorVaryingUseCode(2, currentFSVarying));
                currentFSVarying += 1;
            }
    
            fragmentShader->append("0.0, 0.0, 0.0, 0.0);\n");
    
            // Make use of the vec2 varyings
            fragmentShader->append("\tretColor += vec4(");
    
            for (GLint i = 0; i < vec2Count; i++)
            {
                fragmentShader->append(GenerateVectorVaryingUseCode(1, currentFSVarying));
                currentFSVarying += 1;
            }
    
            for (GLint i = 0; i < vec2ArrayCount; i++)
            {
                fragmentShader->append(GenerateVectorVaryingUseCode(2, currentFSVarying));
                currentFSVarying += 1;
            }
    
            fragmentShader->append("vec2(0.0, 0.0), 0.0, 0.0);\n");
    
            // Make use of the vec3 varyings
            fragmentShader->append("\tretColor += vec4(");
    
            for (GLint i = 0; i < vec3Count; i++)
            {
                fragmentShader->append(GenerateVectorVaryingUseCode(1, currentFSVarying));
                currentFSVarying += 1;
            }
    
            for (GLint i = 0; i < vec3ArrayCount; i++)
            {
                fragmentShader->append(GenerateVectorVaryingUseCode(2, currentFSVarying));
                currentFSVarying += 1;
            }
    
            fragmentShader->append("vec3(0.0, 0.0, 0.0), 0.0);\n");
    
            // Make use of the vec4 varyings
            fragmentShader->append("\tretColor += ");
    
            for (GLint i = 0; i < vec4Count; i++)
            {
                fragmentShader->append(GenerateVectorVaryingUseCode(1, currentFSVarying));
                currentFSVarying += 1;
            }
    
            for (GLint i = 0; i < vec4ArrayCount; i++)
            {
                fragmentShader->append(GenerateVectorVaryingUseCode(2, currentFSVarying));
                currentFSVarying += 1;
            }
    
            fragmentShader->append("vec4(0.0, 0.0, 0.0, 0.0);\n");
    
            // Set gl_FragColor, and use special variables if requested
            fragmentShader->append("\tgl_FragColor = retColor");
            
            if (useFragCoord)
            {
                fragmentShader->append(" + gl_FragCoord");
            }
    
            if (usePointCoord)
            {
                fragmentShader->append(" + vec4(gl_PointCoord, 0.0, 0.0)");
            }
    
            fragmentShader->append(";\n}");
        }
    
        void VaryingTestBase(GLint floatCount, GLint floatArrayCount, GLint vec2Count, GLint vec2ArrayCount, GLint vec3Count, GLint vec3ArrayCount,
                             GLint vec4Count, GLint vec4ArrayCount, bool useFragCoord, bool usePointCoord, bool usePointSize, bool expectSuccess)
        {
            std::string fragmentShaderSource;
            std::string vertexShaderSource;
    
            GenerateGLSLWithVaryings(floatCount, floatArrayCount, vec2Count, vec2ArrayCount, vec3Count, vec3ArrayCount,
                                     vec4Count, vec4ArrayCount, useFragCoord, usePointCoord, usePointSize,
                                     &fragmentShaderSource, &vertexShaderSource);
    
            GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource);
    
            if (expectSuccess)
            {
                EXPECT_NE(0u, program);
            }
            else
            {
                EXPECT_EQ(0u, program);
            }
        }
    
        void CompileGLSLWithUniformsAndSamplers(GLint vertexUniformCount,
                                                GLint fragmentUniformCount,
                                                GLint vertexSamplersCount,
                                                GLint fragmentSamplersCount,
                                                bool expectSuccess)
        {
            std::stringstream vertexShader;
            std::stringstream fragmentShader;
    
            // Generate the vertex shader
            vertexShader << "precision mediump float;\n";
    
            for (int i = 0; i < vertexUniformCount; i++)
            {
                vertexShader << "uniform vec4 v" << i << ";\n";
            }
    
            for (int i = 0; i < vertexSamplersCount; i++)
            {
                vertexShader << "uniform sampler2D s" << i << ";\n";
            }
    
            vertexShader << "void main()\n{\n";
    
            for (int i = 0; i < vertexUniformCount; i++)
            {
                vertexShader << "    gl_Position +=  v" << i << ";\n";
            }
    
            for (int i = 0; i < vertexSamplersCount; i++)
            {
                vertexShader << "    gl_Position +=  texture2D(s" << i << ", vec2(0.0, 0.0));\n";
            }
    
            if (vertexUniformCount == 0 && vertexSamplersCount == 0)
            {
                vertexShader << "   gl_Position = vec4(0.0);\n";
            }
    
            vertexShader << "}\n";
    
            // Generate the fragment shader
            fragmentShader << "precision mediump float;\n";
    
            for (int i = 0; i < fragmentUniformCount; i++)
            {
                fragmentShader << "uniform vec4 v" << i << ";\n";
            }
    
            for (int i = 0; i < fragmentSamplersCount; i++)
            {
                fragmentShader << "uniform sampler2D s" << i << ";\n";
            }
    
            fragmentShader << "void main()\n{\n";
    
            for (int i = 0; i < fragmentUniformCount; i++)
            {
                fragmentShader << "    gl_FragColor +=  v" << i << ";\n";
            }
    
            for (int i = 0; i < fragmentSamplersCount; i++)
            {
                fragmentShader << "    gl_FragColor +=  texture2D(s" << i << ", vec2(0.0, 0.0));\n";
            }
    
            if (fragmentUniformCount == 0 && fragmentSamplersCount == 0)
            {
                fragmentShader << "    gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);\n";
            }
    
            fragmentShader << "}\n";
    
            GLuint program = CompileProgram(vertexShader.str(), fragmentShader.str());
    
            if (expectSuccess)
            {
                EXPECT_NE(0u, program);
            }
            else
            {
                EXPECT_EQ(0u, program);
            }
        }
    
        std::string mSimpleVSSource;
    };
    
    class GLSLTest_ES3 : public GLSLTest
    {
        void SetUp() override
        {
            ANGLETest::SetUp();
    
            mSimpleVSSource =
                "#version 300 es\n"
                "in vec4 inputAttribute;"
                "void main()"
                "{"
                "    gl_Position = inputAttribute;"
                "}";
        }
    };
    
    TEST_P(GLSLTest, NamelessScopedStructs)
    {
        const std::string fragmentShaderSource = SHADER_SOURCE
        (
            precision mediump float;
    
            void main()
            {
                struct
                {
                    float q;
                } b;
    
                gl_FragColor = vec4(1, 0, 0, 1);
                gl_FragColor.a += b.q;
            }
        );
    
        GLuint program = CompileProgram(mSimpleVSSource, fragmentShaderSource);
        EXPECT_NE(0u, program);
    }
    
    TEST_P(GLSLTest, ScopedStructsOrderBug)
    {
        // TODO(geofflang): Find out why this doesn't compile on Apple OpenGL drivers
        // (http://anglebug.com/1292)
        // TODO(geofflang): Find out why this doesn't compile on AMD OpenGL drivers
        // (http://anglebug.com/1291)
        if (IsDesktopOpenGL() && (IsOSX() || !IsNVIDIA()))
        {
            std::cout << "Test disabled on this OpenGL configuration." << std::endl;
            return;
        }
    
        const std::string fragmentShaderSource = SHADER_SOURCE
        (
            precision mediump float;
    
            struct T
            {
                float f;
            };
    
            void main()
            {
                T a;
    
                struct T
                {
                    float q;
                };
    
                T b;
    
                gl_FragColor = vec4(1, 0, 0, 1);
                gl_FragColor.a += a.f;
                gl_FragColor.a += b.q;
            }
        );
    
        GLuint program = CompileProgram(mSimpleVSSource, fragmentShaderSource);
        EXPECT_NE(0u, program);
    }
    
    TEST_P(GLSLTest, ScopedStructsBug)
    {
        const std::string fragmentShaderSource = SHADER_SOURCE
        (
            precision mediump float;
    
            struct T_0
            {
                float f;
            };
    
            void main()
            {
                gl_FragColor = vec4(1, 0, 0, 1);
    
                struct T
                {
                    vec2 v;
                };
    
                T_0 a;
                T b;
    
                gl_FragColor.a += a.f;
                gl_FragColor.a += b.v.x;
            }
        );
    
        GLuint program = CompileProgram(mSimpleVSSource, fragmentShaderSource);
        EXPECT_NE(0u, program);
    }
    
    TEST_P(GLSLTest, DxPositionBug)
    {
        const std::string &vertexShaderSource = SHADER_SOURCE
        (
            attribute vec4 inputAttribute;
            varying float dx_Position;
            void main()
            {
                gl_Position = vec4(inputAttribute);
                dx_Position = 0.0;
            }
        );
    
        const std::string &fragmentShaderSource = SHADER_SOURCE
        (
            precision mediump float;
    
            varying float dx_Position;
    
            void main()
            {
                gl_FragColor = vec4(dx_Position, 0, 0, 1);
            }
        );
    
        GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource);
        EXPECT_NE(0u, program);
    }
    
    TEST_P(GLSLTest, ElseIfRewriting)
    {
        const std::string &vertexShaderSource =
            "attribute vec4 a_position;\n"
            "varying float v;\n"
            "void main() {\n"
            "  gl_Position = a_position;\n"
            "  v = 1.0;\n"
            "  if (a_position.x <= 0.5) {\n"
            "    v = 0.0;\n"
            "  } else if (a_position.x >= 0.5) {\n"
            "    v = 2.0;\n"
            "  }\n"
            "}\n";
    
        const std::string &fragmentShaderSource =
            "precision highp float;\n"
            "varying float v;\n"
            "void main() {\n"
            "  vec4 color = vec4(1.0, 0.0, 0.0, 1.0);\n"
            "  if (v >= 1.0) color = vec4(0.0, 1.0, 0.0, 1.0);\n"
            "  if (v >= 2.0) color = vec4(0.0, 0.0, 1.0, 1.0);\n"
            "  gl_FragColor = color;\n"
            "}\n";
    
        GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource);
        ASSERT_NE(0u, program);
    
        drawQuad(program, "a_position", 0.5f);
    
        EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255);
        EXPECT_PIXEL_EQ(getWindowWidth()-1, 0, 0, 255, 0, 255);
    }
    
    TEST_P(GLSLTest, TwoElseIfRewriting)
    {
        const std::string &vertexShaderSource =
            "attribute vec4 a_position;\n"
            "varying float v;\n"
            "void main() {\n"
            "  gl_Position = a_position;\n"
            "  if (a_position.x == 0.0) {\n"
            "    v = 1.0;\n"
            "  } else if (a_position.x > 0.5) {\n"
            "    v = 0.0;\n"
            "  } else if (a_position.x > 0.75) {\n"
            "    v = 0.5;\n"
            "  }\n"
            "}\n";
    
        const std::string &fragmentShaderSource =
            "precision highp float;\n"
            "varying float v;\n"
            "void main() {\n"
            "  gl_FragColor = vec4(v, 0.0, 0.0, 1.0);\n"
            "}\n";
    
        GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource);
        EXPECT_NE(0u, program);
    }
    
    TEST_P(GLSLTest, FrontFacingAndVarying)
    {
        EGLPlatformParameters platform = GetParam().eglParameters;
    
        const std::string vertexShaderSource = SHADER_SOURCE
        (
            attribute vec4 a_position;
            varying float v_varying;
            void main()
            {
                v_varying = a_position.x;
                gl_Position = a_position;
            }
        );
    
        const std::string fragmentShaderSource = SHADER_SOURCE
        (
            precision mediump float;
            varying float v_varying;
            void main()
            {
                vec4 c;
    
                if (gl_FrontFacing)
                {
                    c = vec4(v_varying, 0, 0, 1.0);
                }
                else
                {
                    c = vec4(0, v_varying, 0, 1.0);
                }
                gl_FragColor = c;
            }
        );
    
        GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource);
    
        // Compilation should fail on D3D11 feature level 9_3, since gl_FrontFacing isn't supported.
        if (platform.renderer == EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE)
        {
            if (platform.majorVersion == 9 && platform.minorVersion == 3)
            {
                EXPECT_EQ(0u, program);
                return;
            }
        }
    
        // Otherwise, compilation should succeed
        EXPECT_NE(0u, program);
    }
    
    // Verify that linking shaders declaring different shading language versions fails.
    TEST_P(GLSLTest_ES3, VersionMismatch)
    {
        const std::string fragmentShaderSource100 =
            "precision mediump float;\n"
            "varying float v_varying;\n"
            "void main() { gl_FragColor = vec4(v_varying, 0, 0, 1.0); }\n";
    
        const std::string vertexShaderSource100 =
            "attribute vec4 a_position;\n"
            "varying float v_varying;\n"
            "void main() { v_varying = a_position.x; gl_Position = a_position; }\n";
    
        const std::string fragmentShaderSource300 =
            "#version 300 es\n"
            "precision mediump float;\n"
            "in float v_varying;\n"
            "out vec4 my_FragColor;\n"
            "void main() { my_FragColor = vec4(v_varying, 0, 0, 1.0); }\n";
    
        const std::string vertexShaderSource300 =
            "#version 300 es\n"
            "in vec4 a_position;\n"
            "out float v_varying;\n"
            "void main() { v_varying = a_position.x; gl_Position = a_position; }\n";
    
        GLuint program = CompileProgram(vertexShaderSource300, fragmentShaderSource100);
        EXPECT_EQ(0u, program);
    
        program = CompileProgram(vertexShaderSource100, fragmentShaderSource300);
        EXPECT_EQ(0u, program);
    }
    
    // Verify that declaring varying as invariant only in vertex shader fails in ESSL 1.00.
    TEST_P(GLSLTest, InvariantVaryingOut)
    {
        const std::string fragmentShaderSource =
            "precision mediump float;\n"
            "varying float v_varying;\n"
            "void main() { gl_FragColor = vec4(v_varying, 0, 0, 1.0); }\n";
    
        const std::string vertexShaderSource =
            "attribute vec4 a_position;\n"
            "invariant varying float v_varying;\n"
            "void main() { v_varying = a_position.x; gl_Position = a_position; }\n";
    
        GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource);
        EXPECT_EQ(0u, program);
    }
    
    // Verify that declaring varying as invariant only in vertex shader succeeds in ESSL 3.00.
    TEST_P(GLSLTest_ES3, InvariantVaryingOut)
    {
        // TODO: ESSL 3.00 -> GLSL 1.20 translation should add "invariant" in fragment shader
        // for varyings which are invariant in vertex shader (http://anglebug.com/1293)
        if (IsDesktopOpenGL())
        {
            std::cout << "Test disabled on OpenGL." << std::endl;
            return;
        }
    
        const std::string fragmentShaderSource =
            "#version 300 es\n"
            "precision mediump float;\n"
            "in float v_varying;\n"
            "out vec4 my_FragColor;\n"
            "void main() { my_FragColor = vec4(v_varying, 0, 0, 1.0); }\n";
    
        const std::string vertexShaderSource =
            "#version 300 es\n"
            "in vec4 a_position;\n"
            "invariant out float v_varying;\n"
            "void main() { v_varying = a_position.x; gl_Position = a_position; }\n";
    
        GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource);
        EXPECT_NE(0u, program);
    }
    
    // Verify that declaring varying as invariant only in fragment shader fails in ESSL 1.00.
    TEST_P(GLSLTest, InvariantVaryingIn)
    {
        const std::string fragmentShaderSource =
            "precision mediump float;\n"
            "invariant varying float v_varying;\n"
            "void main() { gl_FragColor = vec4(v_varying, 0, 0, 1.0); }\n";
    
        const std::string vertexShaderSource =
            "attribute vec4 a_position;\n"
            "varying float v_varying;\n"
            "void main() { v_varying = a_position.x; gl_Position = a_position; }\n";
    
        GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource);
        EXPECT_EQ(0u, program);
    }
    
    // Verify that declaring varying as invariant only in fragment shader fails in ESSL 3.00.
    TEST_P(GLSLTest_ES3, InvariantVaryingIn)
    {
        const std::string fragmentShaderSource =
            "#version 300 es\n"
            "precision mediump float;\n"
            "invariant in float v_varying;\n"
            "out vec4 my_FragColor;\n"
            "void main() { my_FragColor = vec4(v_varying, 0, 0, 1.0); }\n";
    
        const std::string vertexShaderSource =
            "#version 300 es\n"
            "in vec4 a_position;\n"
            "out float v_varying;\n"
            "void main() { v_varying = a_position.x; gl_Position = a_position; }\n";
    
        GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource);
        EXPECT_EQ(0u, program);
    }
    
    // Verify that declaring varying as invariant in both shaders succeeds in ESSL 1.00.
    TEST_P(GLSLTest, InvariantVaryingBoth)
    {
        const std::string fragmentShaderSource =
            "precision mediump float;\n"
            "invariant varying float v_varying;\n"
            "void main() { gl_FragColor = vec4(v_varying, 0, 0, 1.0); }\n";
    
        const std::string vertexShaderSource =
            "attribute vec4 a_position;\n"
            "invariant varying float v_varying;\n"
            "void main() { v_varying = a_position.x; gl_Position = a_position; }\n";
    
        GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource);
        EXPECT_NE(0u, program);
    }
    
    // Verify that declaring varying as invariant in both shaders fails in ESSL 3.00.
    TEST_P(GLSLTest_ES3, InvariantVaryingBoth)
    {
        const std::string fragmentShaderSource =
            "#version 300 es\n"
            "precision mediump float;\n"
            "invariant in float v_varying;\n"
            "out vec4 my_FragColor;\n"
            "void main() { my_FragColor = vec4(v_varying, 0, 0, 1.0); }\n";
    
        const std::string vertexShaderSource =
            "#version 300 es\n"
            "in vec4 a_position;\n"
            "invariant out float v_varying;\n"
            "void main() { v_varying = a_position.x; gl_Position = a_position; }\n";
    
        GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource);
        EXPECT_EQ(0u, program);
    }
    
    // Verify that declaring gl_Position as invariant succeeds in ESSL 1.00.
    TEST_P(GLSLTest, InvariantGLPosition)
    {
        const std::string fragmentShaderSource =
            "precision mediump float;\n"
            "varying float v_varying;\n"
            "void main() { gl_FragColor = vec4(v_varying, 0, 0, 1.0); }\n";
    
        const std::string vertexShaderSource =
            "attribute vec4 a_position;\n"
            "invariant gl_Position;\n"
            "varying float v_varying;\n"
            "void main() { v_varying = a_position.x; gl_Position = a_position; }\n";
    
        GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource);
        EXPECT_NE(0u, program);
    }
    
    // Verify that declaring gl_Position as invariant succeeds in ESSL 3.00.
    TEST_P(GLSLTest_ES3, InvariantGLPosition)
    {
        const std::string fragmentShaderSource =
            "#version 300 es\n"
            "precision mediump float;\n"
            "in float v_varying;\n"
            "out vec4 my_FragColor;\n"
            "void main() { my_FragColor = vec4(v_varying, 0, 0, 1.0); }\n";
    
        const std::string vertexShaderSource =
            "#version 300 es\n"
            "in vec4 a_position;\n"
            "invariant gl_Position;\n"
            "out float v_varying;\n"
            "void main() { v_varying = a_position.x; gl_Position = a_position; }\n";
    
        GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource);
        EXPECT_NE(0u, program);
    }
    
    // Verify that using invariant(all) in both shaders succeeds in ESSL 1.00.
    TEST_P(GLSLTest, InvariantAllBoth)
    {
        // TODO: ESSL 1.00 -> GLSL 1.20 translation should add "invariant" in fragment shader
        // for varyings which are invariant in vertex shader individually,
        // and remove invariant(all) from fragment shader (http://anglebug.com/1293)
        if (IsDesktopOpenGL())
        {
            std::cout << "Test disabled on OpenGL." << std::endl;
            return;
        }
    
        const std::string fragmentShaderSource =
            "#pragma STDGL invariant(all)\n"
            "precision mediump float;\n"
            "varying float v_varying;\n"
            "void main() { gl_FragColor = vec4(v_varying, 0, 0, 1.0); }\n";
    
        const std::string vertexShaderSource =
            "#pragma STDGL invariant(all)\n"
            "attribute vec4 a_position;\n"
            "varying float v_varying;\n"
            "void main() { v_varying = a_position.x; gl_Position = a_position; }\n";
    
        GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource);
        EXPECT_NE(0u, program);
    }
    
    // Verify that functions without return statements still compile
    TEST_P(GLSLTest, MissingReturnFloat)
    {
        const std::string vertexShaderSource =
            "varying float v_varying;\n"
            "float f() { if (v_varying > 0.0) return 1.0; }\n"
            "void main() { gl_Position = vec4(f(), 0, 0, 1); }\n";
    
        const std::string fragmentShaderSource =
            "precision mediump float;\n"
            "void main() { gl_FragColor = vec4(0, 0, 0, 1); }\n";
    
        GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource);
        EXPECT_NE(0u, program);
    }
    
    // Verify that functions without return statements still compile
    TEST_P(GLSLTest, MissingReturnVec2)
    {
        const std::string vertexShaderSource =
            "varying float v_varying;\n"
            "vec2 f() { if (v_varying > 0.0) return vec2(1.0, 1.0); }\n"
            "void main() { gl_Position = vec4(f().x, 0, 0, 1); }\n";
    
        const std::string fragmentShaderSource =
            "precision mediump float;\n"
            "void main() { gl_FragColor = vec4(0, 0, 0, 1); }\n";
    
        GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource);
        EXPECT_NE(0u, program);
    }
    
    // Verify that functions without return statements still compile
    TEST_P(GLSLTest, MissingReturnVec3)
    {
        const std::string vertexShaderSource =
            "varying float v_varying;\n"
            "vec3 f() { if (v_varying > 0.0) return vec3(1.0, 1.0, 1.0); }\n"
            "void main() { gl_Position = vec4(f().x, 0, 0, 1); }\n";
    
        const std::string fragmentShaderSource =
            "precision mediump float;\n"
            "void main() { gl_FragColor = vec4(0, 0, 0, 1); }\n";
    
        GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource);
        EXPECT_NE(0u, program);
    }
    
    // Verify that functions without return statements still compile
    TEST_P(GLSLTest, MissingReturnVec4)
    {
        const std::string vertexShaderSource =
            "varying float v_varying;\n"
            "vec4 f() { if (v_varying > 0.0) return vec4(1.0, 1.0, 1.0, 1.0); }\n"
            "void main() { gl_Position = vec4(f().x, 0, 0, 1); }\n";
    
        const std::string fragmentShaderSource =
            "precision mediump float;\n"
            "void main() { gl_FragColor = vec4(0, 0, 0, 1); }\n";
    
        GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource);
        EXPECT_NE(0u, program);
    }
    
    // Verify that functions without return statements still compile
    TEST_P(GLSLTest, MissingReturnIVec4)
    {
        const std::string vertexShaderSource =
            "varying float v_varying;\n"
            "ivec4 f() { if (v_varying > 0.0) return ivec4(1, 1, 1, 1); }\n"
            "void main() { gl_Position = vec4(f().x, 0, 0, 1); }\n";
    
        const std::string fragmentShaderSource =
            "precision mediump float;\n"
            "void main() { gl_FragColor = vec4(0, 0, 0, 1); }\n";
    
        GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource);
        EXPECT_NE(0u, program);
    }
    
    // Verify that functions without return statements still compile
    TEST_P(GLSLTest, MissingReturnMat4)
    {
        const std::string vertexShaderSource =
            "varying float v_varying;\n"
            "mat4 f() { if (v_varying > 0.0) return mat4(1.0); }\n"
            "void main() { gl_Position = vec4(f()[0][0], 0, 0, 1); }\n";
    
        const std::string fragmentShaderSource =
            "precision mediump float;\n"
            "void main() { gl_FragColor = vec4(0, 0, 0, 1); }\n";
    
        GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource);
        EXPECT_NE(0u, program);
    }
    
    // Verify that functions without return statements still compile
    TEST_P(GLSLTest, MissingReturnStruct)
    {
        const std::string vertexShaderSource =
            "varying float v_varying;\n"
            "struct s { float a; int b; vec2 c; };\n"
            "s f() { if (v_varying > 0.0) return s(1.0, 1, vec2(1.0, 1.0)); }\n"
            "void main() { gl_Position = vec4(f().a, 0, 0, 1); }\n";
    
        const std::string fragmentShaderSource =
            "precision mediump float;\n"
            "void main() { gl_FragColor = vec4(0, 0, 0, 1); }\n";
    
        GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource);
        EXPECT_NE(0u, program);
    }
    
    // Verify that functions without return statements still compile
    TEST_P(GLSLTest_ES3, MissingReturnArray)
    {
        const std::string vertexShaderSource =
            "#version 300 es\n"
            "in float v_varying;\n"
            "vec2[2] f() { if (v_varying > 0.0) { return vec2[2](vec2(1.0, 1.0), vec2(1.0, 1.0)); } }\n"
            "void main() { gl_Position = vec4(f()[0].x, 0, 0, 1); }\n";
    
        const std::string fragmentShaderSource =
            "#version 300 es\n"
            "precision mediump float;\n"
            "out vec4 my_FragColor;\n"
            "void main() { my_FragColor = vec4(0, 0, 0, 1); }\n";
    
        GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource);
        EXPECT_NE(0u, program);
    }
    
    // Verify that functions without return statements still compile
    TEST_P(GLSLTest_ES3, MissingReturnArrayOfStructs)
    {
        const std::string vertexShaderSource =
            "#version 300 es\n"
            "in float v_varying;\n"
            "struct s { float a; int b; vec2 c; };\n"
            "s[2] f() { if (v_varying > 0.0) { return s[2](s(1.0, 1, vec2(1.0, 1.0)), s(1.0, 1, "
            "vec2(1.0, 1.0))); } }\n"
            "void main() { gl_Position = vec4(f()[0].a, 0, 0, 1); }\n";
    
        const std::string fragmentShaderSource =
            "#version 300 es\n"
            "precision mediump float;\n"
            "out vec4 my_FragColor;\n"
            "void main() { my_FragColor = vec4(0, 0, 0, 1); }\n";
    
        GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource);
        EXPECT_NE(0u, program);
    }
    
    // Verify that functions without return statements still compile
    TEST_P(GLSLTest_ES3, MissingReturnStructOfArrays)
    {
        // TODO(cwallez) remove the suppression once NVIDIA removes the restriction for
        // GLSL >= 300. It was defined only in GLSL 2.0, section 6.1.
        if (IsNVIDIA() && IsOpenGLES())
        {
            std::cout << "Test skipped on NVIDIA OpenGL ES because it disallows returning "
                         "structure of arrays"
                      << std::endl;
            return;
        }
    
        const std::string vertexShaderSource =
            "#version 300 es\n"
            "in float v_varying;\n"
            "struct s { float a[2]; int b[2]; vec2 c[2]; };\n"
            "s f() { if (v_varying > 0.0) { return s(float[2](1.0, 1.0), int[2](1, 1),"
            "vec2[2](vec2(1.0, 1.0), vec2(1.0, 1.0))); } }\n"
            "void main() { gl_Position = vec4(f().a[0], 0, 0, 1); }\n";
    
        const std::string fragmentShaderSource =
            "#version 300 es\n"
            "precision mediump float;\n"
            "out vec4 my_FragColor;\n"
            "void main() { my_FragColor = vec4(0, 0, 0, 1); }\n";
    
        GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource);
        EXPECT_NE(0u, program);
    }
    
    // Verify that using invariant(all) in both shaders fails in ESSL 3.00.
    TEST_P(GLSLTest_ES3, InvariantAllBoth)
    {
        const std::string fragmentShaderSource =
            "#version 300 es\n"
            "#pragma STDGL invariant(all)\n"
            "precision mediump float;\n"
            "in float v_varying;\n"
            "out vec4 my_FragColor;\n"
            "void main() { my_FragColor = vec4(v_varying, 0, 0, 1.0); }\n";
    
        const std::string vertexShaderSource =
            "#version 300 es\n"
            "#pragma STDGL invariant(all)\n"
            "in vec4 a_position;\n"
            "out float v_varying;\n"
            "void main() { v_varying = a_position.x; gl_Position = a_position; }\n";
    
        GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource);
        EXPECT_EQ(0u, program);
    }
    
    // Verify that using invariant(all) only in fragment shader fails in ESSL 1.00.
    TEST_P(GLSLTest, InvariantAllIn)
    {
        const std::string fragmentShaderSource =
            "#pragma STDGL invariant(all)\n"
            "precision mediump float;\n"
            "varying float v_varying;\n"
            "void main() { gl_FragColor = vec4(v_varying, 0, 0, 1.0); }\n";
    
        const std::string vertexShaderSource =
            "attribute vec4 a_position;\n"
            "varying float v_varying;\n"
            "void main() { v_varying = a_position.x; gl_Position = a_position; }\n";
    
        GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource);
        EXPECT_EQ(0u, program);
    }
    
    // Verify that using invariant(all) only in fragment shader fails in ESSL 3.00.
    TEST_P(GLSLTest_ES3, InvariantAllIn)
    {
        const std::string fragmentShaderSource =
            "#version 300 es\n"
            "#pragma STDGL invariant(all)\n"
            "precision mediump float;\n"
            "in float v_varying;\n"
            "out vec4 my_FragColor;\n"
            "void main() { my_FragColor = vec4(v_varying, 0, 0, 1.0); }\n";
    
        const std::string vertexShaderSource =
            "#version 300 es\n"
            "in vec4 a_position;\n"
            "out float v_varying;\n"
            "void main() { v_varying = a_position.x; gl_Position = a_position; }\n";
    
        GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource);
        EXPECT_EQ(0u, program);
    }
    
    // Verify that using invariant(all) only in vertex shader fails in ESSL 1.00.
    TEST_P(GLSLTest, InvariantAllOut)
    {
        const std::string fragmentShaderSource =
            "precision mediump float;\n"
            "varying float v_varying;\n"
            "void main() { gl_FragColor = vec4(v_varying, 0, 0, 1.0); }\n";
    
        const std::string vertexShaderSource =
            "#pragma STDGL invariant(all)\n"
            "attribute vec4 a_position;\n"
            "varying float v_varying;\n"
            "void main() { v_varying = a_position.x; gl_Position = a_position; }\n";
    
        GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource);
        EXPECT_EQ(0u, program);
    }
    
    // Verify that using invariant(all) only in vertex shader succeeds in ESSL 3.00.
    TEST_P(GLSLTest_ES3, InvariantAllOut)
    {
        // TODO: ESSL 3.00 -> GLSL 1.20 translation should add "invariant" in fragment shader
        // for varyings which are invariant in vertex shader,
        // because of invariant(all) being used in vertex shader (http://anglebug.com/1293)
        if (IsDesktopOpenGL())
        {
            std::cout << "Test disabled on OpenGL." << std::endl;
            return;
        }
    
        const std::string fragmentShaderSource =
            "#version 300 es\n"
            "precision mediump float;\n"
            "in float v_varying;\n"
            "out vec4 my_FragColor;\n"
            "void main() { my_FragColor = vec4(v_varying, 0, 0, 1.0); }\n";
    
        const std::string vertexShaderSource =
            "#version 300 es\n"
            "#pragma STDGL invariant(all)\n"
            "in vec4 a_position;\n"
            "out float v_varying;\n"
            "void main() { v_varying = a_position.x; gl_Position = a_position; }\n";
    
        GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource);
        EXPECT_NE(0u, program);
    }
    
    TEST_P(GLSLTest, MaxVaryingVec4)
    {
    #if defined(__APPLE__)
        // TODO(geofflang): Find out why this doesn't compile on Apple AND OpenGL drivers
        // (http://anglebug.com/1291)
        if (IsAMD() && getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE)
        {
            std::cout << "Test disabled on Apple AMD OpenGL." << std::endl;
            return;
        }
    #endif
    
        GLint maxVaryings = 0;
        glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings);
    
        VaryingTestBase(0, 0, 0, 0, 0, 0, maxVaryings, 0, false, false, false, true);
    }
    
    TEST_P(GLSLTest, MaxMinusTwoVaryingVec4PlusTwoSpecialVariables)
    {
        GLint maxVaryings = 0;
        glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings);
    
        // Generate shader code that uses gl_FragCoord and gl_PointCoord, two special fragment shader variables.
        VaryingTestBase(0, 0, 0, 0, 0, 0, maxVaryings - 2, 0, true, true, false, true);
    }
    
    TEST_P(GLSLTest, MaxMinusTwoVaryingVec4PlusThreeSpecialVariables)
    {
        // TODO(geofflang): Figure out why this fails on OpenGL AMD (http://anglebug.com/1291)
        if (IsAMD() && getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE)
        {
            std::cout << "Test disabled on OpenGL." << std::endl;
            return;
        }
    
        GLint maxVaryings = 0;
        glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings);
    
        // Generate shader code that uses gl_FragCoord, gl_PointCoord and gl_PointSize.
        VaryingTestBase(0, 0, 0, 0, 0, 0, maxVaryings - 2, 0, true, true, true, true);
    }
    
    // Disabled because drivers are allowed to successfully compile shaders that have more than the
    // maximum number of varyings. (http://anglebug.com/1296)
    TEST_P(GLSLTest, DISABLED_MaxVaryingVec4PlusFragCoord)
    {
        GLint maxVaryings = 0;
        glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings);
    
        // Generate shader code that uses gl_FragCoord, a special fragment shader variables.
        // This test should fail, since we are really using (maxVaryings + 1) varyings.
        VaryingTestBase(0, 0, 0, 0, 0, 0, maxVaryings, 0, true, false, false, false);
    }
    
    // Disabled because drivers are allowed to successfully compile shaders that have more than the
    // maximum number of varyings. (http://anglebug.com/1296)
    TEST_P(GLSLTest, DISABLED_MaxVaryingVec4PlusPointCoord)
    {
        GLint maxVaryings = 0;
        glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings);
    
        // Generate shader code that uses gl_FragCoord, a special fragment shader variables.
        // This test should fail, since we are really using (maxVaryings + 1) varyings.
        VaryingTestBase(0, 0, 0, 0, 0, 0, maxVaryings, 0, false, true, false, false);
    }
    
    TEST_P(GLSLTest, MaxVaryingVec3)
    {
        GLint maxVaryings = 0;
        glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings);
    
        VaryingTestBase(0, 0, 0, 0, maxVaryings, 0, 0, 0, false, false, false, true);
    }
    
    TEST_P(GLSLTest, MaxVaryingVec3Array)
    {
        GLint maxVaryings = 0;
        glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings);
    
        VaryingTestBase(0, 0, 0, 0, 0, maxVaryings / 2, 0, 0, false, false, false, true);
    }
    
    // Disabled because of a failure in D3D9
    TEST_P(GLSLTest, MaxVaryingVec3AndOneFloat)
    {
        if (IsD3D9())
        {
            std::cout << "Test disabled on D3D9." << std::endl;
            return;
        }
    
        GLint maxVaryings = 0;
        glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings);
    
        VaryingTestBase(1, 0, 0, 0, maxVaryings, 0, 0, 0, false, false, false, true);
    }
    
    // Disabled because of a failure in D3D9
    TEST_P(GLSLTest, MaxVaryingVec3ArrayAndOneFloatArray)
    {
        if (IsD3D9())
        {
            std::cout << "Test disabled on D3D9." << std::endl;
            return;
        }
    
        GLint maxVaryings = 0;
        glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings);
    
        VaryingTestBase(0, 1, 0, 0, 0, maxVaryings / 2, 0, 0, false, false, false, true);
    }
    
    // Disabled because of a failure in D3D9
    TEST_P(GLSLTest, TwiceMaxVaryingVec2)
    {
        if (IsD3D9())
        {
            std::cout << "Test disabled on D3D9." << std::endl;
            return;
        }
    
        if (getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE)
        {
            // TODO(geofflang): Figure out why this fails on NVIDIA's GLES driver
            std::cout << "Test disabled on OpenGL ES." << std::endl;
            return;
        }
    
    #if defined(__APPLE__)
        // TODO(geofflang): Find out why this doesn't compile on Apple AND OpenGL drivers
        // (http://anglebug.com/1291)
        if (IsAMD() && getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE)
        {
            std::cout << "Test disabled on Apple AMD OpenGL." << std::endl;
            return;
        }
    #endif
    
        GLint maxVaryings = 0;
        glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings);
    
        VaryingTestBase(0, 0, 2 * maxVaryings, 0, 0, 0, 0, 0, false, false, false, true);
    }
    
    // Disabled because of a failure in D3D9
    TEST_P(GLSLTest, MaxVaryingVec2Arrays)
    {
        if (IsD3DSM3())
        {
            std::cout << "Test disabled on SM3." << std::endl;
            return;
        }
    
        if (getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE)
        {
            // TODO(geofflang): Figure out why this fails on NVIDIA's GLES driver
            std::cout << "Test disabled on OpenGL ES." << std::endl;
            return;
        }
    
    #if defined(__APPLE__)
        // TODO(geofflang): Find out why this doesn't compile on Apple AND OpenGL drivers
        // (http://anglebug.com/1291)
        if (IsAMD() && getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE)
        {
            std::cout << "Test disabled on Apple AMD OpenGL." << std::endl;
            return;
        }
    #endif
    
        GLint maxVaryings = 0;
        glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings);
    
        VaryingTestBase(0, 0, 0, maxVaryings, 0, 0, 0, 0, false, false, false, true);
    }
    
    // Disabled because drivers are allowed to successfully compile shaders that have more than the
    // maximum number of varyings. (http://anglebug.com/1296)
    TEST_P(GLSLTest, DISABLED_MaxPlusOneVaryingVec3)
    {
        GLint maxVaryings = 0;
        glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings);
    
        VaryingTestBase(0, 0, 0, 0, maxVaryings + 1, 0, 0, 0, false, false, false, false);
    }
    
    // Disabled because drivers are allowed to successfully compile shaders that have more than the
    // maximum number of varyings. (http://anglebug.com/1296)
    TEST_P(GLSLTest, DISABLED_MaxPlusOneVaryingVec3Array)
    {
        GLint maxVaryings = 0;
        glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings);
    
        VaryingTestBase(0, 0, 0, 0, 0, maxVaryings / 2 + 1, 0, 0, false, false, false, false);
    }
    
    // Disabled because drivers are allowed to successfully compile shaders that have more than the
    // maximum number of varyings. (http://anglebug.com/1296)
    TEST_P(GLSLTest, DISABLED_MaxVaryingVec3AndOneVec2)
    {
        GLint maxVaryings = 0;
        glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings);
    
        VaryingTestBase(0, 0, 1, 0, maxVaryings, 0, 0, 0, false, false, false, false);
    }
    
    // Disabled because drivers are allowed to successfully compile shaders that have more than the
    // maximum number of varyings. (http://anglebug.com/1296)
    TEST_P(GLSLTest, DISABLED_MaxPlusOneVaryingVec2)
    {
        GLint maxVaryings = 0;
        glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings);
    
        VaryingTestBase(0, 0, 2 * maxVaryings + 1, 0, 0, 0, 0, 0, false, false, false, false);
    }
    
    // Disabled because drivers are allowed to successfully compile shaders that have more than the
    // maximum number of varyings. (http://anglebug.com/1296)
    TEST_P(GLSLTest, DISABLED_MaxVaryingVec3ArrayAndMaxPlusOneFloatArray)
    {
        GLint maxVaryings = 0;
        glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings);
    
        VaryingTestBase(0, maxVaryings / 2 + 1, 0, 0, 0, 0, 0, maxVaryings / 2, false, false, false, false);
    }
    
    // Verify shader source with a fixed length that is less than the null-terminated length will compile.
    TEST_P(GLSLTest, FixedShaderLength)
    {
        GLuint shader = glCreateShader(GL_FRAGMENT_SHADER);
    
        const std::string appendGarbage = "abcasdfasdfasdfasdfasdf";
        const std::string source = "void main() { gl_FragColor = vec4(0, 0, 0, 0); }" + appendGarbage;
        const char *sourceArray[1] = { source.c_str() };
        GLint lengths[1] = { static_cast<GLint>(source.length() - appendGarbage.length()) };
        glShaderSource(shader, static_cast<GLsizei>(ArraySize(sourceArray)), sourceArray, lengths);
        glCompileShader(shader);
    
        GLint compileResult;
        glGetShaderiv(shader, GL_COMPILE_STATUS, &compileResult);
        EXPECT_NE(compileResult, 0);
    }
    
    // Verify that a negative shader source length is treated as a null-terminated length.
    TEST_P(GLSLTest, NegativeShaderLength)
    {
        GLuint shader = glCreateShader(GL_FRAGMENT_SHADER);
    
        const char *sourceArray[1] = { "void main() { gl_FragColor = vec4(0, 0, 0, 0); }" };
        GLint lengths[1] = { -10 };
        glShaderSource(shader, static_cast<GLsizei>(ArraySize(sourceArray)), sourceArray, lengths);
        glCompileShader(shader);
    
        GLint compileResult;
        glGetShaderiv(shader, GL_COMPILE_STATUS, &compileResult);
        EXPECT_NE(compileResult, 0);
    }
    
    // Check that having an invalid char after the "." doesn't cause an assert.
    TEST_P(GLSLTest, InvalidFieldFirstChar)
    {
        GLuint shader      = glCreateShader(GL_VERTEX_SHADER);
        const char *source = "void main() {vec4 x; x.}";
        glShaderSource(shader, 1, &source, 0);
        glCompileShader(shader);
    
        GLint compileResult;
        glGetShaderiv(shader, GL_COMPILE_STATUS, &compileResult);
        EXPECT_EQ(0, compileResult);
    }
    
    // Verify that a length array with mixed positive and negative values compiles.
    TEST_P(GLSLTest, MixedShaderLengths)
    {
        GLuint shader = glCreateShader(GL_FRAGMENT_SHADER);
    
        const char *sourceArray[] =
        {
            "void main()",
            "{",
            "    gl_FragColor = vec4(0, 0, 0, 0);",
            "}",
        };
        GLint lengths[] =
        {
            -10,
            1,
            static_cast<GLint>(strlen(sourceArray[2])),
            -1,
        };
        ASSERT_EQ(ArraySize(sourceArray), ArraySize(lengths));
    
        glShaderSource(shader, static_cast<GLsizei>(ArraySize(sourceArray)), sourceArray, lengths);
        glCompileShader(shader);
    
        GLint compileResult;
        glGetShaderiv(shader, GL_COMPILE_STATUS, &compileResult);
        EXPECT_NE(compileResult, 0);
    }
    
    // Verify that zero-length shader source does not affect shader compilation.
    TEST_P(GLSLTest, ZeroShaderLength)
    {
        GLuint shader = glCreateShader(GL_FRAGMENT_SHADER);
    
        const char *sourceArray[] =
        {
            "adfasdf",
            "34534",
            "void main() { gl_FragColor = vec4(0, 0, 0, 0); }",
            "",
            "asdfasdfsdsdf",
        };
        GLint lengths[] =
        {
            0,
            0,
            -1,
            0,
            0,
        };
        ASSERT_EQ(ArraySize(sourceArray), ArraySize(lengths));
    
        glShaderSource(shader, static_cast<GLsizei>(ArraySize(sourceArray)), sourceArray, lengths);
        glCompileShader(shader);
    
        GLint compileResult;
        glGetShaderiv(shader, GL_COMPILE_STATUS, &compileResult);
        EXPECT_NE(compileResult, 0);
    }
    
    // Tests that bad index expressions don't crash ANGLE's translator.
    // https://code.google.com/p/angleproject/issues/detail?id=857
    TEST_P(GLSLTest, BadIndexBug)
    {
        const std::string &fragmentShaderSourceVec =
            "precision mediump float;\n"
            "uniform vec4 uniformVec;\n"
            "void main()\n"
            "{\n"
            "    gl_FragColor = vec4(uniformVec[int()]);\n"
            "}";
    
        GLuint shader = CompileShader(GL_FRAGMENT_SHADER, fragmentShaderSourceVec);
        EXPECT_EQ(0u, shader);
    
        if (shader != 0)
        {
            glDeleteShader(shader);
        }
    
        const std::string &fragmentShaderSourceMat =
            "precision mediump float;\n"
            "uniform mat4 uniformMat;\n"
            "void main()\n"
            "{\n"
            "    gl_FragColor = vec4(uniformMat[int()]);\n"
            "}";
    
        shader = CompileShader(GL_FRAGMENT_SHADER, fragmentShaderSourceMat);
        EXPECT_EQ(0u, shader);
    
        if (shader != 0)
        {
            glDeleteShader(shader);
        }
    
        const std::string &fragmentShaderSourceArray =
            "precision mediump float;\n"
            "uniform vec4 uniformArray;\n"
            "void main()\n"
            "{\n"
            "    gl_FragColor = vec4(uniformArray[int()]);\n"
            "}";
    
        shader = CompileShader(GL_FRAGMENT_SHADER, fragmentShaderSourceArray);
        EXPECT_EQ(0u, shader);
    
        if (shader != 0)
        {
            glDeleteShader(shader);
        }
    }
    
    // Test that structs defined in uniforms are translated correctly.
    TEST_P(GLSLTest, StructSpecifiersUniforms)
    {
        const std::string fragmentShaderSource = SHADER_SOURCE
        (
            precision mediump float;
    
            uniform struct S { float field;} s;
    
            void main()
            {
                gl_FragColor = vec4(1, 0, 0, 1);
                gl_FragColor.a += s.field;
            }
        );
    
        GLuint program = CompileProgram(mSimpleVSSource, fragmentShaderSource);
        EXPECT_NE(0u, program);
    }
    
    // Test that gl_DepthRange is not stored as a uniform location. Since uniforms
    // beginning with "gl_" are filtered out by our validation logic, we must
    // bypass the validation to test the behaviour of the implementation.
    // (note this test is still Impl-independent)
    TEST_P(GLSLTest, DepthRangeUniforms)
    {
        const std::string fragmentShaderSource = SHADER_SOURCE
        (
            precision mediump float;
    
            void main()
            {
                gl_FragColor = vec4(gl_DepthRange.near, gl_DepthRange.far, gl_DepthRange.diff, 1);
            }
        );
    
        GLuint program = CompileProgram(mSimpleVSSource, fragmentShaderSource);
        EXPECT_NE(0u, program);
    
        // dive into the ANGLE internals, so we can bypass validation.
        gl::Context *context = reinterpret_cast<gl::Context *>(getEGLWindow()->getContext());
        gl::Program *glProgram = context->getProgram(program);
        GLint nearIndex = glProgram->getUniformLocation("gl_DepthRange.near");
        EXPECT_EQ(-1, nearIndex);
    
        // Test drawing does not throw an exception.
        drawQuad(program, "inputAttribute", 0.5f);
    
        EXPECT_GL_NO_ERROR();
    
        glDeleteProgram(program);
    }
    
    std::string GenerateSmallPowShader(double base, double exponent)
    {
        std::stringstream stream;
    
        stream.precision(8);
    
        double result = pow(base, exponent);
    
        stream << "precision highp float;\n"
               << "float fun(float arg)\n"
               << "{\n"
               << "    return pow(arg, " << std::fixed << exponent << ");\n"
               << "}\n"
               << "\n"
               << "void main()\n"
               << "{\n"
               << "    const float a = " << std::scientific << base << ";\n"
               << "    float b = fun(a);\n"
               << "    if (abs(" << result << " - b) < " << std::abs(result * 0.001) << ")\n"
               << "    {\n"
               << "        gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);\n"
               << "    }\n"
               << "    else\n"
               << "    {\n"
               << "        gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n"
               << "    }\n"
               << "}\n";
    
        return stream.str();
    }
    
    // Covers the WebGL test 'glsl/bugs/pow-of-small-constant-in-user-defined-function'
    // See http://anglebug.com/851
    TEST_P(GLSLTest, PowOfSmallConstant)
    {
        std::vector<double> bads;
        for (int eps = -1; eps <= 1; ++eps)
        {
            for (int i = -4; i <= 5; ++i)
            {
                if (i >= -1 && i <= 1)
                    continue;
                const double epsilon = 1.0e-8;
                double bad           = static_cast<double>(i) + static_cast<double>(eps) * epsilon;
                bads.push_back(bad);
            }
        }
    
        for (double bad : bads)
        {
            const std::string &fragmentShaderSource = GenerateSmallPowShader(1.0e-6, bad);
    
            ANGLE_GL_PROGRAM(program, mSimpleVSSource, fragmentShaderSource);
    
            drawQuad(program.get(), "inputAttribute", 0.5f);
    
            EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
            EXPECT_GL_NO_ERROR();
        }
    }
    
    // Test that fragment shaders which contain non-constant loop indexers and compiled for FL9_3 and
    // below
    // fail with a specific error message.
    // Additionally test that the same fragment shader compiles successfully with feature levels greater
    // than FL9_3.
    TEST_P(GLSLTest, LoopIndexingValidation)
    {
        const std::string fragmentShaderSource = SHADER_SOURCE
        (
            precision mediump float;
    
            uniform float loopMax;
    
            void main()
            {
                gl_FragColor = vec4(1, 0, 0, 1);
                for (float l = 0.0; l < loopMax; l++)
                {
                    if (loopMax > 3.0)
                    {
                        gl_FragColor.a += 0.1;
                    }
                }
            }
        );
    
        GLuint shader = glCreateShader(GL_FRAGMENT_SHADER);
    
        const char *sourceArray[1] = {fragmentShaderSource.c_str()};
        glShaderSource(shader, 1, sourceArray, nullptr);
        glCompileShader(shader);
    
        GLint compileResult;
        glGetShaderiv(shader, GL_COMPILE_STATUS, &compileResult);
    
        // If the test is configured to run limited to Feature Level 9_3, then it is
        // assumed that shader compilation will fail with an expected error message containing
        // "Loop index cannot be compared with non-constant expression"
        if ((GetParam() == ES2_D3D11_FL9_3() || GetParam() == ES2_D3D9()))
        {
            if (compileResult != 0)
            {
                FAIL() << "Shader compilation succeeded, expected failure";
            }
            else
            {
                GLint infoLogLength;
                glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength);
    
                std::string infoLog;
                infoLog.resize(infoLogLength);
                glGetShaderInfoLog(shader, static_cast<GLsizei>(infoLog.size()), NULL, &infoLog[0]);
    
                if (infoLog.find("Loop index cannot be compared with non-constant expression") ==
                    std::string::npos)
                {
                    FAIL() << "Shader compilation failed with unexpected error message";
                }
            }
        }
        else
        {
            EXPECT_NE(0, compileResult);
        }
    
        if (shader != 0)
        {
            glDeleteShader(shader);
        }
    }
    
    // Tests that the maximum uniforms count returned from querying GL_MAX_VERTEX_UNIFORM_VECTORS
    // can actually be used.
    TEST_P(GLSLTest, VerifyMaxVertexUniformVectors)
    {
        int maxUniforms = 10000;
        glGetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, &maxUniforms);
        EXPECT_GL_NO_ERROR();
        std::cout << "Validating GL_MAX_VERTEX_UNIFORM_VECTORS = " << maxUniforms << std::endl;
    
        CompileGLSLWithUniformsAndSamplers(maxUniforms, 0, 0, 0, true);
    }
    
    // Tests that the maximum uniforms count returned from querying GL_MAX_VERTEX_UNIFORM_VECTORS
    // can actually be used along with the maximum number of texture samplers.
    TEST_P(GLSLTest, VerifyMaxVertexUniformVectorsWithSamplers)
    {
        if (GetParam().eglParameters.renderer == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE ||
            GetParam().eglParameters.renderer == EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE)
        {
            std::cout << "Test disabled on OpenGL." << std::endl;
            return;
        }
    
        int maxUniforms = 10000;
        glGetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, &maxUniforms);
        EXPECT_GL_NO_ERROR();
        std::cout << "Validating GL_MAX_VERTEX_UNIFORM_VECTORS = " << maxUniforms << std::endl;
    
        int maxTextureImageUnits = 0;
        glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &maxTextureImageUnits);
    
        CompileGLSLWithUniformsAndSamplers(maxUniforms, 0, maxTextureImageUnits, 0, true);
    }
    
    // Tests that the maximum uniforms count + 1 from querying GL_MAX_VERTEX_UNIFORM_VECTORS
    // fails shader compilation.
    TEST_P(GLSLTest, VerifyMaxVertexUniformVectorsExceeded)
    {
        int maxUniforms = 10000;
        glGetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, &maxUniforms);
        EXPECT_GL_NO_ERROR();
        std::cout << "Validating GL_MAX_VERTEX_UNIFORM_VECTORS + 1 = " << maxUniforms + 1 << std::endl;
    
        CompileGLSLWithUniformsAndSamplers(maxUniforms + 1, 0, 0, 0, false);
    }
    
    // Tests that the maximum uniforms count returned from querying GL_MAX_FRAGMENT_UNIFORM_VECTORS
    // can actually be used.
    TEST_P(GLSLTest, VerifyMaxFragmentUniformVectors)
    {
        int maxUniforms = 10000;
        glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_VECTORS, &maxUniforms);
        EXPECT_GL_NO_ERROR();
        std::cout << "Validating GL_MAX_FRAGMENT_UNIFORM_VECTORS = " << maxUniforms << std::endl;
    
        CompileGLSLWithUniformsAndSamplers(0, maxUniforms, 0, 0, true);
    }
    
    // Tests that the maximum uniforms count returned from querying GL_MAX_FRAGMENT_UNIFORM_VECTORS
    // can actually be used along with the maximum number of texture samplers.
    TEST_P(GLSLTest, VerifyMaxFragmentUniformVectorsWithSamplers)
    {
        if (GetParam().eglParameters.renderer == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE ||
            GetParam().eglParameters.renderer == EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE)
        {
            std::cout << "Test disabled on OpenGL." << std::endl;
            return;
        }
    
        int maxUniforms = 10000;
        glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_VECTORS, &maxUniforms);
        EXPECT_GL_NO_ERROR();
    
        int maxTextureImageUnits = 0;
        glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureImageUnits);
    
        CompileGLSLWithUniformsAndSamplers(0, maxUniforms, 0, maxTextureImageUnits, true);
    }
    
    // Tests that the maximum uniforms count + 1 from querying GL_MAX_FRAGMENT_UNIFORM_VECTORS
    // fails shader compilation.
    TEST_P(GLSLTest, VerifyMaxFragmentUniformVectorsExceeded)
    {
        int maxUniforms = 10000;
        glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_VECTORS, &maxUniforms);
        EXPECT_GL_NO_ERROR();
        std::cout << "Validating GL_MAX_FRAGMENT_UNIFORM_VECTORS + 1 = " << maxUniforms + 1
                  << std::endl;
    
        CompileGLSLWithUniformsAndSamplers(0, maxUniforms + 1, 0, 0, false);
    }
    
    // Test that two constructors which have vec4 and mat2 parameters get disambiguated (issue in
    // HLSL).
    TEST_P(GLSLTest_ES3, AmbiguousConstructorCall2x2)
    {
        const std::string fragmentShaderSource =
            "#version 300 es\n"
            "precision highp float;\n"
            "out vec4 my_FragColor;\n"
            "void main()\n"
            "{\n"
            "    my_FragColor = vec4(0.0);\n"
            "}";
    
        const std::string vertexShaderSource =
            "#version 300 es\n"
            "precision highp float;\n"
            "in vec4 a_vec;\n"
            "in mat2 a_mat;\n"
            "void main()\n"
            "{\n"
            "    gl_Position = vec4(a_vec) + vec4(a_mat);\n"
            "}";
    
        GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource);
        EXPECT_NE(0u, program);
    }
    
    // Test that two constructors which have mat2x3 and mat3x2 parameters get disambiguated.
    // This was suspected to be an issue in HLSL, but HLSL seems to be able to natively choose between
    // the function signatures in this case.
    TEST_P(GLSLTest_ES3, AmbiguousConstructorCall2x3)
    {
        const std::string fragmentShaderSource =
            "#version 300 es\n"
            "precision highp float;\n"
            "out vec4 my_FragColor;\n"
            "void main()\n"
            "{\n"
            "    my_FragColor = vec4(0.0);\n"
            "}";
    
        const std::string vertexShaderSource =
            "#version 300 es\n"
            "precision highp float;\n"
            "in mat3x2 a_matA;\n"
            "in mat2x3 a_matB;\n"
            "void main()\n"
            "{\n"
            "    gl_Position = vec4(a_matA) + vec4(a_matB);\n"
            "}";
    
        GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource);
        EXPECT_NE(0u, program);
    }
    
    // Test that two functions which have vec4 and mat2 parameters get disambiguated (issue in HLSL).
    TEST_P(GLSLTest_ES3, AmbiguousFunctionCall2x2)
    {
        const std::string fragmentShaderSource =
            "#version 300 es\n"
            "precision highp float;\n"
            "out vec4 my_FragColor;\n"
            "void main()\n"
            "{\n"
            "    my_FragColor = vec4(0.0);\n"
            "}";
    
        const std::string vertexShaderSource =
            "#version 300 es\n"
            "precision highp float;\n"
            "in vec4 a_vec;\n"
            "in mat2 a_mat;\n"
            "vec4 foo(vec4 a)\n"
            "{\n"
            "    return a;\n"
            "}\n"
            "vec4 foo(mat2 a)\n"
            "{\n"
            "    return vec4(a[0][0]);\n"
            "}\n"
            "void main()\n"
            "{\n"
            "    gl_Position = foo(a_vec) + foo(a_mat);\n"
            "}";
    
        GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource);
        EXPECT_NE(0u, program);
    }
    
    // Test that an user-defined function with a large number of float4 parameters doesn't fail due to
    // the function name being too long.
    TEST_P(GLSLTest_ES3, LargeNumberOfFloat4Parameters)
    {
        const std::string fragmentShaderSource =
            "#version 300 es\n"
            "precision highp float;\n"
            "out vec4 my_FragColor;\n"
            "void main()\n"
            "{\n"
            "    my_FragColor = vec4(0.0);\n"
            "}";
    
        std::stringstream vertexShaderStream;
        const unsigned int paramCount = 1024u;
    
        vertexShaderStream << "#version 300 es\n"
                              "precision highp float;\n"
                              "in vec4 a_vec;\n"
                              "vec4 lotsOfVec4Parameters(";
        for (unsigned int i = 0; i < paramCount; ++i)
        {
            vertexShaderStream << "vec4 a" << i << ", ";
        }
        vertexShaderStream << "vec4 aLast)\n"
                              "{\n"
                              "    return ";
        for (unsigned int i = 0; i < paramCount; ++i)
        {
            vertexShaderStream << "a" << i << " + ";
        }
        vertexShaderStream << "aLast;\n"
                              "}\n"
                              "void main()\n"
                              "{\n"
                              "    gl_Position = lotsOfVec4Parameters(";
        for (unsigned int i = 0; i < paramCount; ++i)
        {
            vertexShaderStream << "a_vec, ";
        }
        vertexShaderStream << "a_vec);\n"
                              "}";
    
        GLuint program = CompileProgram(vertexShaderStream.str(), fragmentShaderSource);
        EXPECT_NE(0u, program);
    }
    
    // This test was written specifically to stress DeferGlobalInitializers AST transformation.
    // Test a shader where a global constant array is initialized with an expression containing array
    // indexing. This initializer is tricky to constant fold, so if it's not constant folded it needs to
    // be handled in a way that doesn't generate statements in the global scope in HLSL output.
    // Also includes multiple array initializers in one declaration, where only the second one has
    // array indexing. This makes sure that the qualifier for the declaration is set correctly if
    // transformations are applied to the declaration also in the case of ESSL output.
    TEST_P(GLSLTest_ES3, InitGlobalArrayWithArrayIndexing)
    {
        // TODO(ynovikov): re-enable once root cause of http://anglebug.com/1428 is fixed
        if (IsAndroid() && IsAdreno() && IsOpenGLES())
        {
            std::cout << "Test skipped on Adreno OpenGLES on Android." << std::endl;
            return;
        }
    
        const std::string vertexShaderSource =
            "#version 300 es\n"
            "precision highp float;\n"
            "in vec4 a_vec;\n"
            "void main()\n"
            "{\n"
            "    gl_Position = vec4(a_vec);\n"
            "}";
    
        const std::string fragmentShaderSource =
            "#version 300 es\n"
            "precision highp float;\n"
            "out vec4 my_FragColor;\n"
            "const highp float f[2] = float[2](0.1, 0.2);\n"
            "const highp float[2] g = float[2](0.3, 0.4), h = float[2](0.5, f[1]);\n"
            "void main()\n"
            "{\n"
            "    my_FragColor = vec4(h[1]);\n"
            "}";
    
        GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource);
        EXPECT_NE(0u, program);
    }
    
    // Test that index-constant sampler array indexing is supported.
    TEST_P(GLSLTest, IndexConstantSamplerArrayIndexing)
    {
        if (IsD3D11_FL93()) {
            std::cout << "Test skipped on D3D11 FL 9.3." << std::endl;
            return;
        }
    
        const std::string vertexShaderSource =
            "attribute vec4 vPosition;\n"
            "void main()\n"
            "{\n"
            "      gl_Position = vPosition;\n"
            "}";
    
        const std::string fragmentShaderSource =
            "precision mediump float;\n"
            "uniform sampler2D uni[2];\n"
            "\n"
            "float zero(int x)\n"
            "{\n"
            "    return float(x) - float(x);\n"
            "}\n"
            "\n"
            "void main()\n"
            "{\n"
            "    vec4 c = vec4(0,0,0,0);\n"
            "    for (int ii = 1; ii < 3; ++ii) {\n"
            "        if (c.x > 255.0) {\n"
            "            c.x = 255.0 + zero(ii);\n"
            "            break;\n"
            "        }\n"
            // Index the sampler array with a predictable loop index (index-constant) as opposed to
            // a true constant. This is valid in OpenGL ES but isn't in many Desktop OpenGL versions,
            // without an extension.
            "        c += texture2D(uni[ii - 1], vec2(0.5, 0.5));\n"
            "    }\n"
            "    gl_FragColor = c;\n"
            "}";
    
        GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource);
        EXPECT_NE(0u, program);
    }
    
    // Test that the #pragma directive is supported and doesn't trigger a compilation failure on the
    // native driver. The only pragma that gets passed to the OpenGL driver is "invariant" but we don't
    // want to test its behavior, so don't use any varyings.
    TEST_P(GLSLTest, PragmaDirective)
    {
        const std::string vertexShaderSource =
            "#pragma STDGL invariant(all)\n"
            "void main()\n"
            "{\n"
            "    gl_Position = vec4(1.0, 0.0, 0.0, 1.0);\n"
            "}\n";
    
        const std::string fragmentShaderSource =
            "precision mediump float;\n"
            "void main()\n"
            "{\n"
            "    gl_FragColor = vec4(1.0);\n"
            "}\n";
    
        GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource);
        EXPECT_NE(0u, program);
    }
    
    // Sequence operator evaluates operands from left to right (ESSL 3.00 section 5.9).
    // The function call that returns the array needs to be evaluated after ++j for the expression to
    // return the correct value (true).
    TEST_P(GLSLTest_ES3, SequenceOperatorEvaluationOrderArray)
    {
        const std::string &fragmentShaderSource =
            "#version 300 es\n"
            "precision mediump float;\n"
            "out vec4 my_FragColor; \n"
            "int[2] func(int param) {\n"
            "    return int[2](param, param);\n"
            "}\n"
            "void main() {\n"
            "    int a[2]; \n"
            "    for (int i = 0; i < 2; ++i) {\n"
            "        a[i] = 1;\n"
            "    }\n"
            "    int j = 0; \n"
            "    bool result = ((++j), (a == func(j)));\n"
            "    my_FragColor = vec4(0.0, (result ? 1.0 : 0.0), 0.0, 1.0);\n"
            "}\n";
    
        GLuint program = CompileProgram(mSimpleVSSource, fragmentShaderSource);
        ASSERT_NE(0u, program);
    
        drawQuad(program, "inputAttribute", 0.5f);
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    }
    
    // Sequence operator evaluates operands from left to right (ESSL 3.00 section 5.9).
    // The short-circuiting expression needs to be evaluated after ++j for the expression to return the
    // correct value (true).
    TEST_P(GLSLTest_ES3, SequenceOperatorEvaluationOrderShortCircuit)
    {
        const std::string &fragmentShaderSource =
            "#version 300 es\n"
            "precision mediump float;\n"
            "out vec4 my_FragColor; \n"
            "void main() {\n"
            "    int j = 0; \n"
            "    bool result = ((++j), (j == 1 ? true : (++j == 3)));\n"
            "    my_FragColor = vec4(0.0, ((result && j == 1) ? 1.0 : 0.0), 0.0, 1.0);\n"
            "}\n";
    
        GLuint program = CompileProgram(mSimpleVSSource, fragmentShaderSource);
        ASSERT_NE(0u, program);
    
        drawQuad(program, "inputAttribute", 0.5f);
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    }
    
    // Sequence operator evaluates operands from left to right (ESSL 3.00 section 5.9).
    // Indexing the vector needs to be evaluated after func() for the right result.
    TEST_P(GLSLTest_ES3, SequenceOperatorEvaluationOrderDynamicVectorIndexingInLValue)
    {
        const std::string &fragmentShaderSource =
            "#version 300 es\n"
            "precision mediump float;\n"
            "out vec4 my_FragColor;\n"
            "uniform int u_zero;\n"
            "int sideEffectCount = 0;\n"
            "float func() {\n"
            "    ++sideEffectCount;\n"
            "    return -1.0;\n"
            "}\n"
            "void main() {\n"
            "    vec4 v = vec4(0.0, 2.0, 4.0, 6.0); \n"
            "    float f = (func(), (++v[u_zero + sideEffectCount]));\n"
            "    bool green = abs(f - 3.0) < 0.01 && abs(v[1] - 3.0) < 0.01 && sideEffectCount == 1;\n"
            "    my_FragColor = vec4(0.0, (green ? 1.0 : 0.0), 0.0, 1.0);\n"
            "}\n";
    
        GLuint program = CompileProgram(mSimpleVSSource, fragmentShaderSource);
        ASSERT_NE(0u, program);
    
        drawQuad(program, "inputAttribute", 0.5f);
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    }
    
    // Test that using gl_PointCoord with GL_TRIANGLES doesn't produce a link error.
    // From WebGL test conformance/rendering/point-specific-shader-variables.html
    // See http://anglebug.com/1380
    TEST_P(GLSLTest, RenderTrisWithPointCoord)
    {
        const std::string &vert =
            "attribute vec2 aPosition;\n"
            "void main()\n"
            "{\n"
            "    gl_Position = vec4(aPosition, 0, 1);\n"
            "    gl_PointSize = 1.0;\n"
            "}";
        const std::string &frag =
            "void main()\n"
            "{\n"
            "    gl_FragColor = vec4(gl_PointCoord.xy, 0, 1);\n"
            "    gl_FragColor = vec4(0, 1, 0, 1);\n"
            "}";
    
        ANGLE_GL_PROGRAM(prog, vert, frag);
        drawQuad(prog.get(), "aPosition", 0.5f);
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    }
    
    // Convers a bug with the integer pow statement workaround.
    TEST_P(GLSLTest, NestedPowStatements)
    {
        const std::string &vert =
            "attribute vec2 position;\n"
            "void main()\n"
            "{\n"
            "    gl_Position = vec4(position, 0, 1);\n"
            "}";
        const std::string &frag =
            "precision mediump float;\n"
            "float func(float v)\n"
            "{\n"
            "   float f1 = pow(v, 2.0);\n"
            "   return pow(f1 + v, 2.0);\n"
            "}\n"
            "void main()\n"
            "{\n"
            "    float v = func(2.0);\n"
            "    gl_FragColor = abs(v - 36.0) < 0.001 ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1);\n"
            "}";
    
        ANGLE_GL_PROGRAM(prog, vert, frag);
        drawQuad(prog.get(), "position", 0.5f);
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    }
    
    // Convers a bug with the unary minus operator on signed integer workaround.
    TEST_P(GLSLTest_ES3, UnaryMinusOperatorSignedInt)
    {
        const std::string &vert =
            "#version 300 es\n"
            "in highp vec4 position;\n"
            "out mediump vec4 v_color;\n"
            "uniform int ui_one;\n"
            "uniform int ui_two;\n"
            "uniform int ui_three;\n"
            "void main() {\n"
            "    int s[3];\n"
            "    s[0] = ui_one;\n"
            "    s[1] = -(-(-ui_two + 1) + 1);\n"  // s[1] = -ui_two
            "    s[2] = ui_three;\n"
            "    int result = 0;\n"
            "    for (int i = 0; i < ui_three; i++) {\n"
            "        result += s[i];\n"
            "    }\n"
            "    v_color = (result == 2) ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1);\n"
            "    gl_Position = position;\n"
            "}\n";
        const std::string &frag =
            "#version 300 es\n"
            "in mediump vec4 v_color;\n"
            "layout(location=0) out mediump vec4 o_color;\n"
            "void main() {\n"
            "    o_color = v_color;\n"
            "}\n";
    
        ANGLE_GL_PROGRAM(prog, vert, frag);
    
        gl::Context *context   = reinterpret_cast<gl::Context *>(getEGLWindow()->getContext());
        gl::Program *glProgram = context->getProgram(prog.get());
        GLint oneIndex         = glProgram->getUniformLocation("ui_one");
        ASSERT_NE(-1, oneIndex);
        GLint twoIndex = glProgram->getUniformLocation("ui_two");
        ASSERT_NE(-1, twoIndex);
        GLint threeIndex = glProgram->getUniformLocation("ui_three");
        ASSERT_NE(-1, threeIndex);
        glUseProgram(prog.get());
        glUniform1i(oneIndex, 1);
        glUniform1i(twoIndex, 2);
        glUniform1i(threeIndex, 3);
    
        drawQuad(prog.get(), "position", 0.5f);
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    }
    
    // Convers a bug with the unary minus operator on unsigned integer workaround.
    TEST_P(GLSLTest_ES3, UnaryMinusOperatorUnsignedInt)
    {
        const std::string &vert =
            "#version 300 es\n"
            "in highp vec4 position;\n"
            "out mediump vec4 v_color;\n"
            "uniform uint ui_one;\n"
            "uniform uint ui_two;\n"
            "uniform uint ui_three;\n"
            "void main() {\n"
            "    uint s[3];\n"
            "    s[0] = ui_one;\n"
            "    s[1] = -(-(-ui_two + 1u) + 1u);\n"  // s[1] = -ui_two
            "    s[2] = ui_three;\n"
            "    uint result = 0u;\n"
            "    for (uint i = 0u; i < ui_three; i++) {\n"
            "        result += s[i];\n"
            "    }\n"
            "    v_color = (result == 2u) ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1);\n"
            "    gl_Position = position;\n"
            "}\n";
        const std::string &frag =
            "#version 300 es\n"
            "in mediump vec4 v_color;\n"
            "layout(location=0) out mediump vec4 o_color;\n"
            "void main() {\n"
            "    o_color = v_color;\n"
            "}\n";
    
        ANGLE_GL_PROGRAM(prog, vert, frag);
    
        gl::Context *context   = reinterpret_cast<gl::Context *>(getEGLWindow()->getContext());
        gl::Program *glProgram = context->getProgram(prog.get());
        GLint oneIndex         = glProgram->getUniformLocation("ui_one");
        ASSERT_NE(-1, oneIndex);
        GLint twoIndex = glProgram->getUniformLocation("ui_two");
        ASSERT_NE(-1, twoIndex);
        GLint threeIndex = glProgram->getUniformLocation("ui_three");
        ASSERT_NE(-1, threeIndex);
        glUseProgram(prog.get());
        glUniform1ui(oneIndex, 1u);
        glUniform1ui(twoIndex, 2u);
        glUniform1ui(threeIndex, 3u);
    
        drawQuad(prog.get(), "position", 0.5f);
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    }
    
    // Test a nested sequence operator with a ternary operator inside. The ternary operator is
    // intended to be such that it gets converted to an if statement on the HLSL backend.
    TEST_P(GLSLTest, NestedSequenceOperatorWithTernaryInside)
    {
        const std::string &vert =
            "attribute vec2 position;\n"
            "void main()\n"
            "{\n"
            "    gl_Position = vec4(position, 0, 1);\n"
            "}";
    
        // Note that the uniform keep_flop_positive doesn't need to be set - the test expects it to have
        // its default value false.
        const std::string &frag =
            "precision mediump float;\n"
            "uniform bool keep_flop_positive;\n"
            "float flop;\n"
            "void main() {\n"
            "    flop = -1.0,\n"
            "    (flop *= -1.0,\n"
            "    keep_flop_positive ? 0.0 : flop *= -1.0),\n"
            "    gl_FragColor = vec4(0, -flop, 0, 1);\n"
            "}";
    
        ANGLE_GL_PROGRAM(prog, vert, frag);
        drawQuad(prog.get(), "position", 0.5f);
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    }
    
    // Test that using a sampler2D and samplerExternalOES in the same shader works (anglebug.com/1534)
    TEST_P(GLSLTest, ExternalAnd2DSampler)
    {
        if (!extensionEnabled("GL_OES_EGL_image_external"))
        {
            std::cout << "Test skipped because GL_OES_EGL_image_external is not available."
                      << std::endl;
            return;
        }
    
        const std::string fragmentShader =
            "precision mediump float;\n"
            "uniform samplerExternalOES tex0;\n"
            "uniform sampler2D tex1;\n"
            "void main(void)\n"
            "{\n"
            " vec2 uv = vec2(0.0, 0.0);"
            " gl_FragColor = texture2D(tex0, uv) + texture2D(tex1, uv);\n"
            "}\n";
    
        ANGLE_GL_PROGRAM(program, mSimpleVSSource, fragmentShader);
    }
    
    // Test that using an invalid constant right-shift produces an error.
    TEST_P(GLSLTest_ES3, FoldedInvalidRightShift)
    {
        const std::string &fragmentShader =
            "#version 300 es\n"
            "precision mediump float;\n"
            "out vec4 color;\n"
            "void main(void)\n"
            "{\n"
            " int diff = -100 >> -100;\n"
            " color = vec4(float(diff));\n"
            "}\n";
    
        GLuint program = CompileProgram(mSimpleVSSource, fragmentShader);
        EXPECT_EQ(0u, program);
        glDeleteProgram(program);
    }
    
    // Test that using an invalid constant left-shift produces an error.
    TEST_P(GLSLTest_ES3, FoldedInvalidLeftShift)
    {
        const std::string &fragmentShader =
            "#version 300 es\n"
            "precision mediump float;\n"
            "out vec4 color;\n"
            "void main(void)\n"
            "{\n"
            " int diff = -100 << -100;\n"
            " color = vec4(float(diff));\n"
            "}\n";
    
        GLuint program = CompileProgram(mSimpleVSSource, fragmentShader);
        EXPECT_EQ(0u, program);
        glDeleteProgram(program);
    }
    
    }  // anonymous namespace
    
    // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against.
    ANGLE_INSTANTIATE_TEST(GLSLTest,
                           ES2_D3D9(),
                           ES2_D3D11(),
                           ES2_D3D11_FL9_3(),
                           ES2_OPENGL(),
                           ES3_OPENGL(),
                           ES2_OPENGLES(),
                           ES3_OPENGLES());
    
    // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against.
    ANGLE_INSTANTIATE_TEST(GLSLTest_ES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());