Edit

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

Branch :

  • Show log

    Commit

  • Author : Michael Spang
    Date : 2019-01-29 15:35:09
    Hash : d8506c7e
    Message : Disable broken tests on Fuchsia (reland) This disables tests that do not work on Fuchsia. Most of them are related to cube maps which currently crash inside the intel driver. Reland disabling -Wextra-semi. BUG=angleproject:2475, angleproject:3145, angleproject:3081 TEST=angle_end2end_tests on Fuchsia Change-Id: I65ad84f43c88e8ee83c581cc2f41046d00bbae7f Reviewed-on: https://chromium-review.googlesource.com/c/1467604 Commit-Queue: Michael Spang <spang@chromium.org> Reviewed-by: Michael Spang <spang@chromium.org>

  • src/tests/gl_tests/TextureTest.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 "common/mathutil.h"
    #include "test_utils/ANGLETest.h"
    #include "test_utils/gl_raii.h"
    
    using namespace angle;
    
    namespace
    {
    
    // Take a pixel, and reset the components not covered by the format to default
    // values. In particular, the default value for the alpha component is 255
    // (1.0 as unsigned normalized fixed point value).
    GLColor SliceFormatColor(GLenum format, GLColor full)
    {
        switch (format)
        {
            case GL_RED:
                return GLColor(full.R, 0, 0, 255u);
            case GL_RG:
                return GLColor(full.R, full.G, 0, 255u);
            case GL_RGB:
                return GLColor(full.R, full.G, full.B, 255u);
            case GL_RGBA:
                return full;
            default:
                EXPECT_TRUE(false);
                return GLColor::white;
        }
    }
    
    class TexCoordDrawTest : public ANGLETest
    {
      protected:
        TexCoordDrawTest() : ANGLETest(), mProgram(0), mFramebuffer(0), mFramebufferColorTexture(0)
        {
            setWindowWidth(128);
            setWindowHeight(128);
            setConfigRedBits(8);
            setConfigGreenBits(8);
            setConfigBlueBits(8);
            setConfigAlphaBits(8);
        }
    
        virtual const char *getVertexShaderSource()
        {
            return R"(precision highp float;
    attribute vec4 position;
    varying vec2 texcoord;
    
    void main()
    {
        gl_Position = vec4(position.xy, 0.0, 1.0);
        texcoord = (position.xy * 0.5) + 0.5;
    })";
        }
    
        virtual const char *getFragmentShaderSource() = 0;
    
        virtual void setUpProgram()
        {
            const char *vertexShaderSource   = getVertexShaderSource();
            const char *fragmentShaderSource = getFragmentShaderSource();
    
            mProgram = CompileProgram(vertexShaderSource, fragmentShaderSource);
            ASSERT_NE(0u, mProgram);
            ASSERT_GL_NO_ERROR();
        }
    
        void SetUp() override
        {
            ANGLETest::SetUp();
    
            setUpFramebuffer();
        }
    
        void TearDown() override
        {
            glBindFramebuffer(GL_FRAMEBUFFER, 0);
            glDeleteFramebuffers(1, &mFramebuffer);
            glDeleteTextures(1, &mFramebufferColorTexture);
            glDeleteProgram(mProgram);
            ANGLETest::TearDown();
        }
    
        void setUpFramebuffer()
        {
            // We use an FBO to work around an issue where the default framebuffer applies SRGB
            // conversion (particularly known to happen incorrectly on Intel GL drivers). It's not
            // clear whether this issue can even be fixed on all backends. For example GLES 3.0.4 spec
            // section 4.4 says that the format of the default framebuffer is entirely up to the window
            // system, so it might be SRGB, and GLES 3.0 doesn't have a "FRAMEBUFFER_SRGB" to turn off
            // SRGB conversion like desktop GL does.
            // TODO(oetuaho): Get rid of this if the underlying issue is fixed.
            glGenFramebuffers(1, &mFramebuffer);
            glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
    
            glGenTextures(1, &mFramebufferColorTexture);
            glBindTexture(GL_TEXTURE_2D, mFramebufferColorTexture);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getWindowWidth(), getWindowHeight(), 0, GL_RGBA,
                         GL_UNSIGNED_BYTE, nullptr);
            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
                                   mFramebufferColorTexture, 0);
            ASSERT_GL_NO_ERROR();
            ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
            glBindTexture(GL_TEXTURE_2D, 0);
        }
    
        // Returns the created texture ID.
        GLuint create2DTexture()
        {
            GLuint texture2D;
            glGenTextures(1, &texture2D);
            glBindTexture(GL_TEXTURE_2D, texture2D);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
            EXPECT_GL_NO_ERROR();
            return texture2D;
        }
    
        GLuint mProgram;
        GLuint mFramebuffer;
    
      private:
        GLuint mFramebufferColorTexture;
    };
    
    class Texture2DTest : public TexCoordDrawTest
    {
      protected:
        Texture2DTest() : TexCoordDrawTest(), mTexture2D(0), mTexture2DUniformLocation(-1) {}
    
        const char *getFragmentShaderSource() override
        {
            return R"(precision highp float;
    uniform sampler2D tex;
    varying vec2 texcoord;
    
    void main()
    {
        gl_FragColor = texture2D(tex, texcoord);
    })";
        }
    
        virtual const char *getTextureUniformName() { return "tex"; }
    
        void setUpProgram() override
        {
            TexCoordDrawTest::setUpProgram();
            mTexture2DUniformLocation = glGetUniformLocation(mProgram, getTextureUniformName());
            ASSERT_NE(-1, mTexture2DUniformLocation);
        }
    
        void SetUp() override
        {
            TexCoordDrawTest::SetUp();
            mTexture2D = create2DTexture();
    
            ASSERT_GL_NO_ERROR();
        }
    
        void TearDown() override
        {
            glDeleteTextures(1, &mTexture2D);
            TexCoordDrawTest::TearDown();
        }
    
        // Tests CopyTexSubImage with floating point textures of various formats.
        void testFloatCopySubImage(int sourceImageChannels, int destImageChannels)
        {
            setUpProgram();
    
            if (getClientMajorVersion() < 3)
            {
                ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_EXT_texture_storage") ||
                                   !extensionEnabled("GL_OES_texture_float"));
    
                ANGLE_SKIP_TEST_IF((sourceImageChannels < 3 || destImageChannels < 3) &&
                                   !extensionEnabled("GL_EXT_texture_rg"));
    
                ANGLE_SKIP_TEST_IF(destImageChannels == 3 &&
                                   !extensionEnabled("GL_CHROMIUM_color_buffer_float_rgb"));
    
                ANGLE_SKIP_TEST_IF(destImageChannels == 4 &&
                                   !extensionEnabled("GL_CHROMIUM_color_buffer_float_rgba"));
    
                ANGLE_SKIP_TEST_IF(destImageChannels <= 2);
            }
            else
            {
                ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_color_buffer_float"));
    
                ANGLE_SKIP_TEST_IF(destImageChannels == 3 &&
                                   !extensionEnabled("GL_CHROMIUM_color_buffer_float_rgb"));
            }
    
            // clang-format off
            GLfloat sourceImageData[4][16] =
            {
                { // R
                    1.0f,
                    0.0f,
                    0.0f,
                    1.0f
                },
                { // RG
                    1.0f, 0.0f,
                    0.0f, 1.0f,
                    0.0f, 0.0f,
                    1.0f, 1.0f
                },
                { // RGB
                    1.0f, 0.0f, 0.0f,
                    0.0f, 1.0f, 0.0f,
                    0.0f, 0.0f, 1.0f,
                    1.0f, 1.0f, 0.0f
                },
                { // RGBA
                    1.0f, 0.0f, 0.0f, 1.0f,
                    0.0f, 1.0f, 0.0f, 1.0f,
                    0.0f, 0.0f, 1.0f, 1.0f,
                    1.0f, 1.0f, 0.0f, 1.0f
                },
            };
            // clang-format on
    
            GLenum imageFormats[] = {
                GL_R32F,
                GL_RG32F,
                GL_RGB32F,
                GL_RGBA32F,
            };
    
            GLenum sourceUnsizedFormats[] = {
                GL_RED,
                GL_RG,
                GL_RGB,
                GL_RGBA,
            };
    
            GLuint textures[2];
    
            glGenTextures(2, textures);
    
            GLfloat *imageData         = sourceImageData[sourceImageChannels - 1];
            GLenum sourceImageFormat   = imageFormats[sourceImageChannels - 1];
            GLenum sourceUnsizedFormat = sourceUnsizedFormats[sourceImageChannels - 1];
            GLenum destImageFormat     = imageFormats[destImageChannels - 1];
    
            glBindTexture(GL_TEXTURE_2D, textures[0]);
            if (getClientMajorVersion() >= 3)
            {
                glTexStorage2D(GL_TEXTURE_2D, 1, sourceImageFormat, 2, 2);
            }
            else
            {
                glTexStorage2DEXT(GL_TEXTURE_2D, 1, sourceImageFormat, 2, 2);
            }
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
            glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 2, 2, sourceUnsizedFormat, GL_FLOAT, imageData);
    
            if (sourceImageChannels < 3 && !extensionEnabled("GL_EXT_texture_rg"))
            {
                // This is not supported
                ASSERT_GL_ERROR(GL_INVALID_OPERATION);
            }
            else
            {
                ASSERT_GL_NO_ERROR();
            }
    
            GLuint fbo;
            glGenFramebuffers(1, &fbo);
            glBindFramebuffer(GL_FRAMEBUFFER, fbo);
            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textures[0], 0);
    
            glBindTexture(GL_TEXTURE_2D, textures[1]);
            if (getClientMajorVersion() >= 3)
            {
                glTexStorage2D(GL_TEXTURE_2D, 1, destImageFormat, 2, 2);
            }
            else
            {
                glTexStorage2DEXT(GL_TEXTURE_2D, 1, destImageFormat, 2, 2);
            }
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    
            glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 2, 2);
            ASSERT_GL_NO_ERROR();
    
            glBindFramebuffer(GL_FRAMEBUFFER, 0);
            drawQuad(mProgram, "position", 0.5f);
    
            int testImageChannels = std::min(sourceImageChannels, destImageChannels);
    
            EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
            if (testImageChannels > 1)
            {
                EXPECT_PIXEL_EQ(getWindowHeight() - 1, 0, 0, 255, 0, 255);
                EXPECT_PIXEL_EQ(getWindowHeight() - 1, getWindowWidth() - 1, 255, 255, 0, 255);
                if (testImageChannels > 2)
                {
                    EXPECT_PIXEL_EQ(0, getWindowWidth() - 1, 0, 0, 255, 255);
                }
            }
    
            glDeleteFramebuffers(1, &fbo);
            glDeleteTextures(2, textures);
    
            ASSERT_GL_NO_ERROR();
        }
    
        GLuint mTexture2D;
        GLint mTexture2DUniformLocation;
    };
    
    class Texture2DTestES3 : public Texture2DTest
    {
      protected:
        Texture2DTestES3() : Texture2DTest() {}
    
        const char *getVertexShaderSource() override
        {
            return "#version 300 es\n"
                   "out vec2 texcoord;\n"
                   "in vec4 position;\n"
                   "void main()\n"
                   "{\n"
                   "    gl_Position = vec4(position.xy, 0.0, 1.0);\n"
                   "    texcoord = (position.xy * 0.5) + 0.5;\n"
                   "}\n";
        }
    
        const char *getFragmentShaderSource() override
        {
            return "#version 300 es\n"
                   "precision highp float;\n"
                   "uniform highp sampler2D tex;\n"
                   "in vec2 texcoord;\n"
                   "out vec4 fragColor;\n"
                   "void main()\n"
                   "{\n"
                   "    fragColor = texture(tex, texcoord);\n"
                   "}\n";
        }
    
        void SetUp() override
        {
            Texture2DTest::SetUp();
            setUpProgram();
        }
    };
    
    class Texture2DIntegerAlpha1TestES3 : public Texture2DTest
    {
      protected:
        Texture2DIntegerAlpha1TestES3() : Texture2DTest() {}
    
        const char *getVertexShaderSource() override
        {
            return "#version 300 es\n"
                   "out vec2 texcoord;\n"
                   "in vec4 position;\n"
                   "void main()\n"
                   "{\n"
                   "    gl_Position = vec4(position.xy, 0.0, 1.0);\n"
                   "    texcoord = (position.xy * 0.5) + 0.5;\n"
                   "}\n";
        }
    
        const char *getFragmentShaderSource() override
        {
            return "#version 300 es\n"
                   "precision highp float;\n"
                   "uniform highp isampler2D tex;\n"
                   "in vec2 texcoord;\n"
                   "out vec4 fragColor;\n"
                   "void main()\n"
                   "{\n"
                   "    vec4 green = vec4(0, 1, 0, 1);\n"
                   "    vec4 black = vec4(0, 0, 0, 0);\n"
                   "    fragColor = (texture(tex, texcoord).a == 1) ? green : black;\n"
                   "}\n";
        }
    
        void SetUp() override
        {
            Texture2DTest::SetUp();
            setUpProgram();
        }
    };
    
    class Texture2DUnsignedIntegerAlpha1TestES3 : public Texture2DTest
    {
      protected:
        Texture2DUnsignedIntegerAlpha1TestES3() : Texture2DTest() {}
    
        const char *getVertexShaderSource() override
        {
            return "#version 300 es\n"
                   "out vec2 texcoord;\n"
                   "in vec4 position;\n"
                   "void main()\n"
                   "{\n"
                   "    gl_Position = vec4(position.xy, 0.0, 1.0);\n"
                   "    texcoord = (position.xy * 0.5) + 0.5;\n"
                   "}\n";
        }
    
        const char *getFragmentShaderSource() override
        {
            return "#version 300 es\n"
                   "precision highp float;\n"
                   "uniform highp usampler2D tex;\n"
                   "in vec2 texcoord;\n"
                   "out vec4 fragColor;\n"
                   "void main()\n"
                   "{\n"
                   "    vec4 green = vec4(0, 1, 0, 1);\n"
                   "    vec4 black = vec4(0, 0, 0, 0);\n"
                   "    fragColor = (texture(tex, texcoord).a == 1u) ? green : black;\n"
                   "}\n";
        }
    
        void SetUp() override
        {
            Texture2DTest::SetUp();
            setUpProgram();
        }
    };
    
    class Texture2DTestWithDrawScale : public Texture2DTest
    {
      protected:
        Texture2DTestWithDrawScale() : Texture2DTest(), mDrawScaleUniformLocation(-1) {}
    
        const char *getVertexShaderSource() override
        {
            return
                R"(precision highp float;
                attribute vec4 position;
                varying vec2 texcoord;
    
                uniform vec2 drawScale;
    
                void main()
                {
                    gl_Position = vec4(position.xy * drawScale, 0.0, 1.0);
                    texcoord = (position.xy * 0.5) + 0.5;
                })";
        }
    
        void SetUp() override
        {
            Texture2DTest::SetUp();
    
            setUpProgram();
    
            mDrawScaleUniformLocation = glGetUniformLocation(mProgram, "drawScale");
            ASSERT_NE(-1, mDrawScaleUniformLocation);
    
            glUseProgram(mProgram);
            glUniform2f(mDrawScaleUniformLocation, 1.0f, 1.0f);
            glUseProgram(0);
            ASSERT_GL_NO_ERROR();
        }
    
        GLint mDrawScaleUniformLocation;
    };
    
    class Sampler2DAsFunctionParameterTest : public Texture2DTest
    {
      protected:
        Sampler2DAsFunctionParameterTest() : Texture2DTest() {}
    
        const char *getFragmentShaderSource() override
        {
            return
                R"(precision highp float;
                uniform sampler2D tex;
                varying vec2 texcoord;
    
                vec4 computeFragColor(sampler2D aTex)
                {
                    return texture2D(aTex, texcoord);
                }
    
                void main()
                {
                    gl_FragColor = computeFragColor(tex);
                })";
        }
    
        void SetUp() override
        {
            Texture2DTest::SetUp();
            setUpProgram();
        }
    };
    
    class TextureCubeTest : public TexCoordDrawTest
    {
      protected:
        TextureCubeTest()
            : TexCoordDrawTest(),
              mTexture2D(0),
              mTextureCube(0),
              mTexture2DUniformLocation(-1),
              mTextureCubeUniformLocation(-1)
        {}
    
        const char *getFragmentShaderSource() override
        {
            return
                R"(precision highp float;
                uniform sampler2D tex2D;
                uniform samplerCube texCube;
                varying vec2 texcoord;
    
                void main()
                {
                    gl_FragColor = texture2D(tex2D, texcoord);
                    gl_FragColor += textureCube(texCube, vec3(texcoord, 0));
                })";
        }
    
        void SetUp() override
        {
            TexCoordDrawTest::SetUp();
    
            glGenTextures(1, &mTextureCube);
            glBindTexture(GL_TEXTURE_CUBE_MAP, mTextureCube);
            for (GLenum face = 0; face < 6; face++)
            {
                glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, 0, GL_RGBA, 1, 1, 0, GL_RGBA,
                             GL_UNSIGNED_BYTE, nullptr);
            }
            EXPECT_GL_NO_ERROR();
    
            mTexture2D = create2DTexture();
    
            setUpProgram();
    
            mTexture2DUniformLocation = glGetUniformLocation(mProgram, "tex2D");
            ASSERT_NE(-1, mTexture2DUniformLocation);
            mTextureCubeUniformLocation = glGetUniformLocation(mProgram, "texCube");
            ASSERT_NE(-1, mTextureCubeUniformLocation);
        }
    
        void TearDown() override
        {
            glDeleteTextures(1, &mTextureCube);
            TexCoordDrawTest::TearDown();
        }
    
        GLuint mTexture2D;
        GLuint mTextureCube;
        GLint mTexture2DUniformLocation;
        GLint mTextureCubeUniformLocation;
    };
    
    class TextureCubeTestES3 : public ANGLETest
    {
      protected:
        TextureCubeTestES3() {}
    };
    
    class SamplerArrayTest : public TexCoordDrawTest
    {
      protected:
        SamplerArrayTest()
            : TexCoordDrawTest(),
              mTexture2DA(0),
              mTexture2DB(0),
              mTexture0UniformLocation(-1),
              mTexture1UniformLocation(-1)
        {}
    
        const char *getFragmentShaderSource() override
        {
            return
                R"(precision mediump float;
                uniform highp sampler2D tex2DArray[2];
                varying vec2 texcoord;
                void main()
                {
                    gl_FragColor = texture2D(tex2DArray[0], texcoord);
                    gl_FragColor += texture2D(tex2DArray[1], texcoord);
                })";
        }
    
        void SetUp() override
        {
            TexCoordDrawTest::SetUp();
    
            setUpProgram();
    
            mTexture0UniformLocation = glGetUniformLocation(mProgram, "tex2DArray[0]");
            ASSERT_NE(-1, mTexture0UniformLocation);
            mTexture1UniformLocation = glGetUniformLocation(mProgram, "tex2DArray[1]");
            ASSERT_NE(-1, mTexture1UniformLocation);
    
            mTexture2DA = create2DTexture();
            mTexture2DB = create2DTexture();
            ASSERT_GL_NO_ERROR();
        }
    
        void TearDown() override
        {
            glDeleteTextures(1, &mTexture2DA);
            glDeleteTextures(1, &mTexture2DB);
            TexCoordDrawTest::TearDown();
        }
    
        void testSamplerArrayDraw()
        {
            GLubyte texData[4];
            texData[0] = 0;
            texData[1] = 60;
            texData[2] = 0;
            texData[3] = 255;
    
            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D, mTexture2DA);
            glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, texData);
    
            texData[1] = 120;
            glActiveTexture(GL_TEXTURE1);
            glBindTexture(GL_TEXTURE_2D, mTexture2DB);
            glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, texData);
            EXPECT_GL_ERROR(GL_NO_ERROR);
    
            glUseProgram(mProgram);
            glUniform1i(mTexture0UniformLocation, 0);
            glUniform1i(mTexture1UniformLocation, 1);
            drawQuad(mProgram, "position", 0.5f);
            EXPECT_GL_NO_ERROR();
    
            EXPECT_PIXEL_NEAR(0, 0, 0, 180, 0, 255, 2);
        }
    
        GLuint mTexture2DA;
        GLuint mTexture2DB;
        GLint mTexture0UniformLocation;
        GLint mTexture1UniformLocation;
    };
    
    class SamplerArrayAsFunctionParameterTest : public SamplerArrayTest
    {
      protected:
        SamplerArrayAsFunctionParameterTest() : SamplerArrayTest() {}
    
        const char *getFragmentShaderSource() override
        {
            return
                R"(precision mediump float;
                uniform highp sampler2D tex2DArray[2];
                varying vec2 texcoord;
    
                vec4 computeFragColor(highp sampler2D aTex2DArray[2])
                {
                    return texture2D(aTex2DArray[0], texcoord) + texture2D(aTex2DArray[1], texcoord);
                }
    
                void main()
                {
                    gl_FragColor = computeFragColor(tex2DArray);
                })";
        }
    };
    
    class Texture2DArrayTestES3 : public TexCoordDrawTest
    {
      protected:
        Texture2DArrayTestES3() : TexCoordDrawTest(), m2DArrayTexture(0), mTextureArrayLocation(-1) {}
    
        const char *getVertexShaderSource() override
        {
            return "#version 300 es\n"
                   "out vec2 texcoord;\n"
                   "in vec4 position;\n"
                   "void main()\n"
                   "{\n"
                   "    gl_Position = vec4(position.xy, 0.0, 1.0);\n"
                   "    texcoord = (position.xy * 0.5) + 0.5;\n"
                   "}\n";
        }
    
        const char *getFragmentShaderSource() override
        {
            return "#version 300 es\n"
                   "precision highp float;\n"
                   "uniform highp sampler2DArray tex2DArray;\n"
                   "in vec2 texcoord;\n"
                   "out vec4 fragColor;\n"
                   "void main()\n"
                   "{\n"
                   "    fragColor = texture(tex2DArray, vec3(texcoord.x, texcoord.y, 0.0));\n"
                   "}\n";
        }
    
        void SetUp() override
        {
            TexCoordDrawTest::SetUp();
    
            setUpProgram();
    
            mTextureArrayLocation = glGetUniformLocation(mProgram, "tex2DArray");
            ASSERT_NE(-1, mTextureArrayLocation);
    
            glGenTextures(1, &m2DArrayTexture);
            ASSERT_GL_NO_ERROR();
        }
    
        void TearDown() override
        {
            glDeleteTextures(1, &m2DArrayTexture);
            TexCoordDrawTest::TearDown();
        }
    
        GLuint m2DArrayTexture;
        GLint mTextureArrayLocation;
    };
    
    class TextureSizeTextureArrayTest : public TexCoordDrawTest
    {
      protected:
        TextureSizeTextureArrayTest()
            : TexCoordDrawTest(),
              mTexture2DA(0),
              mTexture2DB(0),
              mTexture0Location(-1),
              mTexture1Location(-1)
        {}
    
        const char *getVertexShaderSource() override { return essl3_shaders::vs::Simple(); }
    
        const char *getFragmentShaderSource() override
        {
            return "#version 300 es\n"
                   "precision highp float;\n"
                   "uniform highp sampler2D tex2DArray[2];\n"
                   "out vec4 fragColor;\n"
                   "void main()\n"
                   "{\n"
                   "    float red = float(textureSize(tex2DArray[0], 0).x) / 255.0;\n"
                   "    float green = float(textureSize(tex2DArray[1], 0).x) / 255.0;\n"
                   "    fragColor = vec4(red, green, 0.0, 1.0);\n"
                   "}\n";
        }
    
        void SetUp() override
        {
            TexCoordDrawTest::SetUp();
    
            setUpProgram();
    
            mTexture0Location = glGetUniformLocation(mProgram, "tex2DArray[0]");
            ASSERT_NE(-1, mTexture0Location);
            mTexture1Location = glGetUniformLocation(mProgram, "tex2DArray[1]");
            ASSERT_NE(-1, mTexture1Location);
    
            mTexture2DA = create2DTexture();
            mTexture2DB = create2DTexture();
            ASSERT_GL_NO_ERROR();
        }
    
        void TearDown() override
        {
            glDeleteTextures(1, &mTexture2DA);
            glDeleteTextures(1, &mTexture2DB);
            TexCoordDrawTest::TearDown();
        }
    
        GLuint mTexture2DA;
        GLuint mTexture2DB;
        GLint mTexture0Location;
        GLint mTexture1Location;
    };
    
    class Texture3DTestES3 : public TexCoordDrawTest
    {
      protected:
        Texture3DTestES3() : TexCoordDrawTest(), mTexture3D(0), mTexture3DUniformLocation(-1) {}
    
        const char *getVertexShaderSource() override
        {
            return "#version 300 es\n"
                   "out vec2 texcoord;\n"
                   "in vec4 position;\n"
                   "void main()\n"
                   "{\n"
                   "    gl_Position = vec4(position.xy, 0.0, 1.0);\n"
                   "    texcoord = (position.xy * 0.5) + 0.5;\n"
                   "}\n";
        }
    
        const char *getFragmentShaderSource() override
        {
            return "#version 300 es\n"
                   "precision highp float;\n"
                   "uniform highp sampler3D tex3D;\n"
                   "in vec2 texcoord;\n"
                   "out vec4 fragColor;\n"
                   "void main()\n"
                   "{\n"
                   "    fragColor = texture(tex3D, vec3(texcoord, 0.0));\n"
                   "}\n";
        }
    
        void SetUp() override
        {
            TexCoordDrawTest::SetUp();
    
            glGenTextures(1, &mTexture3D);
    
            setUpProgram();
    
            mTexture3DUniformLocation = glGetUniformLocation(mProgram, "tex3D");
            ASSERT_NE(-1, mTexture3DUniformLocation);
        }
    
        void TearDown() override
        {
            glDeleteTextures(1, &mTexture3D);
            TexCoordDrawTest::TearDown();
        }
    
        GLuint mTexture3D;
        GLint mTexture3DUniformLocation;
    };
    
    class ShadowSamplerPlusSampler3DTestES3 : public TexCoordDrawTest
    {
      protected:
        ShadowSamplerPlusSampler3DTestES3()
            : TexCoordDrawTest(),
              mTextureShadow(0),
              mTexture3D(0),
              mTextureShadowUniformLocation(-1),
              mTexture3DUniformLocation(-1),
              mDepthRefUniformLocation(-1)
        {}
    
        const char *getVertexShaderSource() override
        {
            return "#version 300 es\n"
                   "out vec2 texcoord;\n"
                   "in vec4 position;\n"
                   "void main()\n"
                   "{\n"
                   "    gl_Position = vec4(position.xy, 0.0, 1.0);\n"
                   "    texcoord = (position.xy * 0.5) + 0.5;\n"
                   "}\n";
        }
    
        const char *getFragmentShaderSource() override
        {
            return "#version 300 es\n"
                   "precision highp float;\n"
                   "uniform highp sampler2DShadow tex2DShadow;\n"
                   "uniform highp sampler3D tex3D;\n"
                   "in vec2 texcoord;\n"
                   "uniform float depthRef;\n"
                   "out vec4 fragColor;\n"
                   "void main()\n"
                   "{\n"
                   "    fragColor = vec4(texture(tex2DShadow, vec3(texcoord, depthRef)) * 0.5);\n"
                   "    fragColor += texture(tex3D, vec3(texcoord, 0.0));\n"
                   "}\n";
        }
    
        void SetUp() override
        {
            TexCoordDrawTest::SetUp();
    
            glGenTextures(1, &mTexture3D);
    
            glGenTextures(1, &mTextureShadow);
            glBindTexture(GL_TEXTURE_2D, mTextureShadow);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
    
            setUpProgram();
    
            mTextureShadowUniformLocation = glGetUniformLocation(mProgram, "tex2DShadow");
            ASSERT_NE(-1, mTextureShadowUniformLocation);
            mTexture3DUniformLocation = glGetUniformLocation(mProgram, "tex3D");
            ASSERT_NE(-1, mTexture3DUniformLocation);
            mDepthRefUniformLocation = glGetUniformLocation(mProgram, "depthRef");
            ASSERT_NE(-1, mDepthRefUniformLocation);
        }
    
        void TearDown() override
        {
            glDeleteTextures(1, &mTextureShadow);
            glDeleteTextures(1, &mTexture3D);
            TexCoordDrawTest::TearDown();
        }
    
        GLuint mTextureShadow;
        GLuint mTexture3D;
        GLint mTextureShadowUniformLocation;
        GLint mTexture3DUniformLocation;
        GLint mDepthRefUniformLocation;
    };
    
    class SamplerTypeMixTestES3 : public TexCoordDrawTest
    {
      protected:
        SamplerTypeMixTestES3()
            : TexCoordDrawTest(),
              mTexture2D(0),
              mTextureCube(0),
              mTexture2DShadow(0),
              mTextureCubeShadow(0),
              mTexture2DUniformLocation(-1),
              mTextureCubeUniformLocation(-1),
              mTexture2DShadowUniformLocation(-1),
              mTextureCubeShadowUniformLocation(-1),
              mDepthRefUniformLocation(-1)
        {}
    
        const char *getVertexShaderSource() override
        {
            return "#version 300 es\n"
                   "out vec2 texcoord;\n"
                   "in vec4 position;\n"
                   "void main()\n"
                   "{\n"
                   "    gl_Position = vec4(position.xy, 0.0, 1.0);\n"
                   "    texcoord = (position.xy * 0.5) + 0.5;\n"
                   "}\n";
        }
    
        const char *getFragmentShaderSource() override
        {
            return "#version 300 es\n"
                   "precision highp float;\n"
                   "uniform highp sampler2D tex2D;\n"
                   "uniform highp samplerCube texCube;\n"
                   "uniform highp sampler2DShadow tex2DShadow;\n"
                   "uniform highp samplerCubeShadow texCubeShadow;\n"
                   "in vec2 texcoord;\n"
                   "uniform float depthRef;\n"
                   "out vec4 fragColor;\n"
                   "void main()\n"
                   "{\n"
                   "    fragColor = texture(tex2D, texcoord);\n"
                   "    fragColor += texture(texCube, vec3(1.0, 0.0, 0.0));\n"
                   "    fragColor += vec4(texture(tex2DShadow, vec3(texcoord, depthRef)) * 0.25);\n"
                   "    fragColor += vec4(texture(texCubeShadow, vec4(1.0, 0.0, 0.0, depthRef)) * "
                   "0.125);\n"
                   "}\n";
        }
    
        void SetUp() override
        {
            TexCoordDrawTest::SetUp();
    
            glGenTextures(1, &mTexture2D);
            glGenTextures(1, &mTextureCube);
    
            glGenTextures(1, &mTexture2DShadow);
            glBindTexture(GL_TEXTURE_2D, mTexture2DShadow);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
    
            glGenTextures(1, &mTextureCubeShadow);
            glBindTexture(GL_TEXTURE_CUBE_MAP, mTextureCubeShadow);
            glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
    
            setUpProgram();
    
            mTexture2DUniformLocation = glGetUniformLocation(mProgram, "tex2D");
            ASSERT_NE(-1, mTexture2DUniformLocation);
            mTextureCubeUniformLocation = glGetUniformLocation(mProgram, "texCube");
            ASSERT_NE(-1, mTextureCubeUniformLocation);
            mTexture2DShadowUniformLocation = glGetUniformLocation(mProgram, "tex2DShadow");
            ASSERT_NE(-1, mTexture2DShadowUniformLocation);
            mTextureCubeShadowUniformLocation = glGetUniformLocation(mProgram, "texCubeShadow");
            ASSERT_NE(-1, mTextureCubeShadowUniformLocation);
            mDepthRefUniformLocation = glGetUniformLocation(mProgram, "depthRef");
            ASSERT_NE(-1, mDepthRefUniformLocation);
    
            ASSERT_GL_NO_ERROR();
        }
    
        void TearDown() override
        {
            glDeleteTextures(1, &mTexture2D);
            glDeleteTextures(1, &mTextureCube);
            glDeleteTextures(1, &mTexture2DShadow);
            glDeleteTextures(1, &mTextureCubeShadow);
            TexCoordDrawTest::TearDown();
        }
    
        GLuint mTexture2D;
        GLuint mTextureCube;
        GLuint mTexture2DShadow;
        GLuint mTextureCubeShadow;
        GLint mTexture2DUniformLocation;
        GLint mTextureCubeUniformLocation;
        GLint mTexture2DShadowUniformLocation;
        GLint mTextureCubeShadowUniformLocation;
        GLint mDepthRefUniformLocation;
    };
    
    class SamplerInStructTest : public Texture2DTest
    {
      protected:
        SamplerInStructTest() : Texture2DTest() {}
    
        const char *getTextureUniformName() override { return "us.tex"; }
    
        const char *getFragmentShaderSource() override
        {
            return "precision highp float;\n"
                   "struct S\n"
                   "{\n"
                   "    vec4 a;\n"
                   "    highp sampler2D tex;\n"
                   "};\n"
                   "uniform S us;\n"
                   "varying vec2 texcoord;\n"
                   "void main()\n"
                   "{\n"
                   "    gl_FragColor = texture2D(us.tex, texcoord + us.a.x);\n"
                   "}\n";
        }
    
        void runSamplerInStructTest()
        {
            setUpProgram();
    
            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D, mTexture2D);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                         &GLColor::green);
            drawQuad(mProgram, "position", 0.5f);
            EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
        }
    };
    
    class SamplerInStructAsFunctionParameterTest : public SamplerInStructTest
    {
      protected:
        SamplerInStructAsFunctionParameterTest() : SamplerInStructTest() {}
    
        const char *getFragmentShaderSource() override
        {
            return "precision highp float;\n"
                   "struct S\n"
                   "{\n"
                   "    vec4 a;\n"
                   "    highp sampler2D tex;\n"
                   "};\n"
                   "uniform S us;\n"
                   "varying vec2 texcoord;\n"
                   "vec4 sampleFrom(S s) {\n"
                   "    return texture2D(s.tex, texcoord + s.a.x);\n"
                   "}\n"
                   "void main()\n"
                   "{\n"
                   "    gl_FragColor = sampleFrom(us);\n"
                   "}\n";
        }
    };
    
    class SamplerInStructArrayAsFunctionParameterTest : public SamplerInStructTest
    {
      protected:
        SamplerInStructArrayAsFunctionParameterTest() : SamplerInStructTest() {}
    
        const char *getTextureUniformName() override { return "us[0].tex"; }
    
        const char *getFragmentShaderSource() override
        {
            return "precision highp float;\n"
                   "struct S\n"
                   "{\n"
                   "    vec4 a;\n"
                   "    highp sampler2D tex;\n"
                   "};\n"
                   "uniform S us[1];\n"
                   "varying vec2 texcoord;\n"
                   "vec4 sampleFrom(S s) {\n"
                   "    return texture2D(s.tex, texcoord + s.a.x);\n"
                   "}\n"
                   "void main()\n"
                   "{\n"
                   "    gl_FragColor = sampleFrom(us[0]);\n"
                   "}\n";
        }
    };
    
    class SamplerInNestedStructAsFunctionParameterTest : public SamplerInStructTest
    {
      protected:
        SamplerInNestedStructAsFunctionParameterTest() : SamplerInStructTest() {}
    
        const char *getTextureUniformName() override { return "us[0].sub.tex"; }
    
        const char *getFragmentShaderSource() override
        {
            return "precision highp float;\n"
                   "struct SUB\n"
                   "{\n"
                   "    vec4 a;\n"
                   "    highp sampler2D tex;\n"
                   "};\n"
                   "struct S\n"
                   "{\n"
                   "    SUB sub;\n"
                   "};\n"
                   "uniform S us[1];\n"
                   "varying vec2 texcoord;\n"
                   "vec4 sampleFrom(SUB s) {\n"
                   "    return texture2D(s.tex, texcoord + s.a.x);\n"
                   "}\n"
                   "void main()\n"
                   "{\n"
                   "    gl_FragColor = sampleFrom(us[0].sub);\n"
                   "}\n";
        }
    };
    
    class SamplerInStructAndOtherVariableTest : public SamplerInStructTest
    {
      protected:
        SamplerInStructAndOtherVariableTest() : SamplerInStructTest() {}
    
        const char *getFragmentShaderSource() override
        {
            return "precision highp float;\n"
                   "struct S\n"
                   "{\n"
                   "    vec4 a;\n"
                   "    highp sampler2D tex;\n"
                   "};\n"
                   "uniform S us;\n"
                   "uniform float us_tex;\n"
                   "varying vec2 texcoord;\n"
                   "void main()\n"
                   "{\n"
                   "    gl_FragColor = texture2D(us.tex, texcoord + us.a.x + us_tex);\n"
                   "}\n";
        }
    };
    
    TEST_P(Texture2DTest, NegativeAPISubImage)
    {
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
        EXPECT_GL_ERROR(GL_NO_ERROR);
    
        setUpProgram();
    
        const GLubyte *pixels[20] = {0};
        glTexSubImage2D(GL_TEXTURE_2D, 0, 1, 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
        EXPECT_GL_ERROR(GL_INVALID_VALUE);
    
        if (extensionEnabled("GL_EXT_texture_storage"))
        {
            // Create a 1-level immutable texture.
            glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8, 2, 2);
    
            // Try calling sub image on the second level.
            glTexSubImage2D(GL_TEXTURE_2D, 1, 1, 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
            EXPECT_GL_ERROR(GL_INVALID_OPERATION);
        }
    }
    
    // Test that querying GL_TEXTURE_BINDING* doesn't cause an unexpected error.
    TEST_P(Texture2DTest, QueryBinding)
    {
        glBindTexture(GL_TEXTURE_2D, 0);
        EXPECT_GL_ERROR(GL_NO_ERROR);
    
        GLint textureBinding;
        glGetIntegerv(GL_TEXTURE_BINDING_2D, &textureBinding);
        EXPECT_GL_NO_ERROR();
        EXPECT_EQ(0, textureBinding);
    
        glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &textureBinding);
        if (extensionEnabled("GL_OES_EGL_image_external") ||
            extensionEnabled("GL_NV_EGL_stream_consumer_external"))
        {
            EXPECT_GL_NO_ERROR();
            EXPECT_EQ(0, textureBinding);
        }
        else
        {
            EXPECT_GL_ERROR(GL_INVALID_ENUM);
        }
    }
    
    TEST_P(Texture2DTest, ZeroSizedUploads)
    {
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
        EXPECT_GL_ERROR(GL_NO_ERROR);
    
        setUpProgram();
    
        // Use the texture first to make sure it's in video memory
        glUseProgram(mProgram);
        glUniform1i(mTexture2DUniformLocation, 0);
        drawQuad(mProgram, "position", 0.5f);
    
        const GLubyte *pixel[4] = {0};
    
        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
        EXPECT_GL_NO_ERROR();
    
        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
        EXPECT_GL_NO_ERROR();
    
        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
        EXPECT_GL_NO_ERROR();
    }
    
    // Test drawing with two texture types, to trigger an ANGLE bug in validation
    TEST_P(TextureCubeTest, CubeMapBug)
    {
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_CUBE_MAP, mTextureCube);
        EXPECT_GL_ERROR(GL_NO_ERROR);
    
        glUseProgram(mProgram);
        glUniform1i(mTexture2DUniformLocation, 0);
        glUniform1i(mTextureCubeUniformLocation, 1);
        drawQuad(mProgram, "position", 0.5f);
        EXPECT_GL_NO_ERROR();
    }
    
    // Test drawing with two texture types accessed from the same shader and check that the result of
    // drawing is correct.
    TEST_P(TextureCubeTest, CubeMapDraw)
    {
        GLubyte texData[4];
        texData[0] = 0;
        texData[1] = 60;
        texData[2] = 0;
        texData[3] = 255;
    
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, texData);
    
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_CUBE_MAP, mTextureCube);
        texData[1] = 120;
        glTexSubImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE,
                        texData);
        EXPECT_GL_ERROR(GL_NO_ERROR);
    
        glUseProgram(mProgram);
        glUniform1i(mTexture2DUniformLocation, 0);
        glUniform1i(mTextureCubeUniformLocation, 1);
        drawQuad(mProgram, "position", 0.5f);
        EXPECT_GL_NO_ERROR();
    
        int px = getWindowWidth() - 1;
        int py = 0;
        EXPECT_PIXEL_NEAR(px, py, 0, 180, 0, 255, 2);
    }
    
    TEST_P(Sampler2DAsFunctionParameterTest, Sampler2DAsFunctionParameter)
    {
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
        GLubyte texData[4];
        texData[0] = 0;
        texData[1] = 128;
        texData[2] = 0;
        texData[3] = 255;
        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, texData);
        glUseProgram(mProgram);
        glUniform1i(mTexture2DUniformLocation, 0);
        drawQuad(mProgram, "position", 0.5f);
        EXPECT_GL_NO_ERROR();
    
        EXPECT_PIXEL_NEAR(0, 0, 0, 128, 0, 255, 2);
    }
    
    // Test drawing with two textures passed to the shader in a sampler array.
    TEST_P(SamplerArrayTest, SamplerArrayDraw)
    {
        testSamplerArrayDraw();
    }
    
    // Test drawing with two textures passed to the shader in a sampler array which is passed to a
    // user-defined function in the shader.
    TEST_P(SamplerArrayAsFunctionParameterTest, SamplerArrayAsFunctionParameter)
    {
        // TODO: Diagnose and fix. http://anglebug.com/2955
        ANGLE_SKIP_TEST_IF(IsVulkan() && IsAndroid());
    
        testSamplerArrayDraw();
    }
    
    // Copy of a test in conformance/textures/texture-mips, to test generate mipmaps
    TEST_P(Texture2DTestWithDrawScale, MipmapsTwice)
    {
        int px = getWindowWidth() / 2;
        int py = getWindowHeight() / 2;
    
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
    
        std::vector<GLColor> pixelsRed(16u * 16u, GLColor::red);
    
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixelsRed.data());
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glGenerateMipmap(GL_TEXTURE_2D);
    
        glUseProgram(mProgram);
        glUniform1i(mTexture2DUniformLocation, 0);
        glUniform2f(mDrawScaleUniformLocation, 0.0625f, 0.0625f);
        drawQuad(mProgram, "position", 0.5f);
        EXPECT_GL_NO_ERROR();
        EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::red);
    
        std::vector<GLColor> pixelsBlue(16u * 16u, GLColor::blue);
    
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                     pixelsBlue.data());
        glGenerateMipmap(GL_TEXTURE_2D);
    
        std::vector<GLColor> pixelsGreen(16u * 16u, GLColor::green);
    
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                     pixelsGreen.data());
        glGenerateMipmap(GL_TEXTURE_2D);
    
        drawQuad(mProgram, "position", 0.5f);
    
        EXPECT_GL_NO_ERROR();
        EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::green);
    }
    
    // Test creating a FBO with a cube map render target, to test an ANGLE bug
    // https://code.google.com/p/angleproject/issues/detail?id=849
    TEST_P(TextureCubeTest, CubeMapFBO)
    {
        // http://anglebug.com/3145
        ANGLE_SKIP_TEST_IF(IsFuchsia() && IsIntel() && IsVulkan());
    
        GLFramebuffer fbo;
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    
        glBindTexture(GL_TEXTURE_CUBE_MAP, mTextureCube);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
                               mTextureCube, 0);
    
        EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
        EXPECT_GL_NO_ERROR();
    
        // Test clearing the six mip faces individually.
        std::array<GLColor, 6> faceColors = {{GLColor::red, GLColor::green, GLColor::blue,
                                              GLColor::yellow, GLColor::cyan, GLColor::magenta}};
    
        for (size_t faceIndex = 0; faceIndex < 6; ++faceIndex)
        {
            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                                   GL_TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex, mTextureCube, 0);
    
            Vector4 clearColorF = faceColors[faceIndex].toNormalizedVector();
            glClearColor(clearColorF.x(), clearColorF.y(), clearColorF.z(), clearColorF.w());
            glClear(GL_COLOR_BUFFER_BIT);
    
            EXPECT_PIXEL_COLOR_EQ(0, 0, faceColors[faceIndex]);
        }
    
        // Iterate the faces again to make sure the colors haven't changed.
        for (size_t faceIndex = 0; faceIndex < 6; ++faceIndex)
        {
            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                                   GL_TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex, mTextureCube, 0);
            EXPECT_PIXEL_COLOR_EQ(0, 0, faceColors[faceIndex])
                << "face color " << faceIndex << " shouldn't change";
        }
    }
    
    // Tests clearing a cube map with a scissor enabled.
    TEST_P(TextureCubeTest, CubeMapFBOScissoredClear)
    {
        // TODO(jie.a.chen): Diagnose and fix. http://anglebug.com/2822
        ANGLE_SKIP_TEST_IF(IsVulkan() && IsIntel() && IsWindows());
    
        // http://anglebug.com/3145
        ANGLE_SKIP_TEST_IF(IsFuchsia() && IsIntel() && IsVulkan());
    
        constexpr size_t kSize = 16;
    
        GLFramebuffer fbo;
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
        glViewport(0, 0, kSize, kSize);
    
        GLTexture texcube;
        glBindTexture(GL_TEXTURE_CUBE_MAP, texcube);
        for (GLenum face = 0; face < 6; face++)
        {
            glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA,
                         GL_UNSIGNED_BYTE, nullptr);
        }
        ASSERT_GL_NO_ERROR();
    
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
                               texcube, 0);
    
        EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
        ASSERT_GL_NO_ERROR();
    
        glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
    
        glEnable(GL_SCISSOR_TEST);
        glScissor(kSize / 2, 0, kSize / 2, kSize);
        glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(kSize / 2 + 1, 0, GLColor::green);
    
        ASSERT_GL_NO_ERROR();
    }
    
    // Test that glTexSubImage2D works properly when glTexStorage2DEXT has initialized the image with a
    // default color.
    TEST_P(Texture2DTest, TexStorage)
    {
        ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 && !extensionEnabled("GL_EXT_texture_storage"));
    
        int width  = getWindowWidth();
        int height = getWindowHeight();
    
        GLuint tex2D;
        glGenTextures(1, &tex2D);
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, tex2D);
    
        // Fill with red
        std::vector<GLubyte> pixels(3 * 16 * 16);
        for (size_t pixelId = 0; pixelId < 16 * 16; ++pixelId)
        {
            pixels[pixelId * 3 + 0] = 255;
            pixels[pixelId * 3 + 1] = 0;
            pixels[pixelId * 3 + 2] = 0;
        }
    
        // ANGLE internally uses RGBA as the DirectX format for RGB images
        // therefore glTexStorage2DEXT initializes the image to a default color to get a consistent
        // alpha color. The data is kept in a CPU-side image and the image is marked as dirty.
        if (getClientMajorVersion() >= 3)
        {
            glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGB8, 16, 16);
        }
        else
        {
            glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGB8, 16, 16);
        }
    
        // Initializes the color of the upper-left 8x8 pixels, leaves the other pixels untouched.
        // glTexSubImage2D should take into account that the image is dirty.
        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 8, 8, GL_RGB, GL_UNSIGNED_BYTE, pixels.data());
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    
        setUpProgram();
    
        glUseProgram(mProgram);
        glUniform1i(mTexture2DUniformLocation, 0);
        drawQuad(mProgram, "position", 0.5f);
        glDeleteTextures(1, &tex2D);
        EXPECT_GL_NO_ERROR();
        EXPECT_PIXEL_EQ(width / 4, height / 4, 255, 0, 0, 255);
    
        // Validate that the region of the texture without data has an alpha of 1.0
        angle::GLColor pixel = ReadColor(3 * width / 4, 3 * height / 4);
        EXPECT_EQ(255, pixel.A);
    }
    
    // Test that glTexSubImage2D combined with a PBO works properly when glTexStorage2DEXT has
    // initialized the image with a default color.
    TEST_P(Texture2DTest, TexStorageWithPBO)
    {
        if (extensionEnabled("NV_pixel_buffer_object"))
        {
            int width  = getWindowWidth();
            int height = getWindowHeight();
    
            GLuint tex2D;
            glGenTextures(1, &tex2D);
            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D, tex2D);
    
            // Fill with red
            std::vector<GLubyte> pixels(3 * 16 * 16);
            for (size_t pixelId = 0; pixelId < 16 * 16; ++pixelId)
            {
                pixels[pixelId * 3 + 0] = 255;
                pixels[pixelId * 3 + 1] = 0;
                pixels[pixelId * 3 + 2] = 0;
            }
    
            // Read 16x16 region from red backbuffer to PBO
            GLuint pbo;
            glGenBuffers(1, &pbo);
            glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
            glBufferData(GL_PIXEL_UNPACK_BUFFER, 3 * 16 * 16, pixels.data(), GL_STATIC_DRAW);
    
            // ANGLE internally uses RGBA as the DirectX format for RGB images
            // therefore glTexStorage2DEXT initializes the image to a default color to get a consistent
            // alpha color. The data is kept in a CPU-side image and the image is marked as dirty.
            glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGB8, 16, 16);
    
            // Initializes the color of the upper-left 8x8 pixels, leaves the other pixels untouched.
            // glTexSubImage2D should take into account that the image is dirty.
            glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 8, 8, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    
            setUpProgram();
    
            glUseProgram(mProgram);
            glUniform1i(mTexture2DUniformLocation, 0);
            drawQuad(mProgram, "position", 0.5f);
            glDeleteTextures(1, &tex2D);
            glDeleteBuffers(1, &pbo);
            EXPECT_GL_NO_ERROR();
            EXPECT_PIXEL_EQ(3 * width / 4, 3 * height / 4, 0, 0, 0, 255);
            EXPECT_PIXEL_EQ(width / 4, height / 4, 255, 0, 0, 255);
        }
    }
    
    // See description on testFloatCopySubImage
    TEST_P(Texture2DTest, CopySubImageFloat_R_R)
    {
        testFloatCopySubImage(1, 1);
    }
    
    TEST_P(Texture2DTest, CopySubImageFloat_RG_R)
    {
        testFloatCopySubImage(2, 1);
    }
    
    TEST_P(Texture2DTest, CopySubImageFloat_RG_RG)
    {
        testFloatCopySubImage(2, 2);
    }
    
    TEST_P(Texture2DTest, CopySubImageFloat_RGB_R)
    {
        testFloatCopySubImage(3, 1);
    }
    
    TEST_P(Texture2DTest, CopySubImageFloat_RGB_RG)
    {
        testFloatCopySubImage(3, 2);
    }
    
    TEST_P(Texture2DTest, CopySubImageFloat_RGB_RGB)
    {
        // TODO(cwallez): Fix on Linux Intel drivers (http://anglebug.com/1346)
        ANGLE_SKIP_TEST_IF(IsIntel() && IsLinux());
    
        // Ignore SDK layers messages on D3D11 FL 9.3 (http://anglebug.com/1284)
        ANGLE_SKIP_TEST_IF(IsD3D11_FL93());
    
        testFloatCopySubImage(3, 3);
    }
    
    TEST_P(Texture2DTest, CopySubImageFloat_RGBA_R)
    {
        testFloatCopySubImage(4, 1);
    }
    
    TEST_P(Texture2DTest, CopySubImageFloat_RGBA_RG)
    {
        testFloatCopySubImage(4, 2);
    }
    
    TEST_P(Texture2DTest, CopySubImageFloat_RGBA_RGB)
    {
        // Ignore SDK layers messages on D3D11 FL 9.3 (http://anglebug.com/1284)
        ANGLE_SKIP_TEST_IF(IsD3D11_FL93());
    
        testFloatCopySubImage(4, 3);
    }
    
    TEST_P(Texture2DTest, CopySubImageFloat_RGBA_RGBA)
    {
        // TODO(lucferron): This test fails only on linux and intel.
        // http://anglebug.com/2726
        ANGLE_SKIP_TEST_IF(IsVulkan() && IsLinux() && IsIntel());
    
        // Ignore SDK layers messages on D3D11 FL 9.3 (http://anglebug.com/1284)
        ANGLE_SKIP_TEST_IF(IsD3D11_FL93());
    
        testFloatCopySubImage(4, 4);
    }
    
    // Port of
    // https://www.khronos.org/registry/webgl/conformance-suites/1.0.3/conformance/textures/texture-npot.html
    // Run against GL_ALPHA/UNSIGNED_BYTE format, to ensure that D3D11 Feature Level 9_3 correctly
    // handles GL_ALPHA
    TEST_P(Texture2DTest, TextureNPOT_GL_ALPHA_UBYTE)
    {
        const int npotTexSize = 5;
        const int potTexSize  = 4;  // Should be less than npotTexSize
        GLuint tex2D;
    
        if (extensionEnabled("GL_OES_texture_npot"))
        {
            // This test isn't applicable if texture_npot is enabled
            return;
        }
    
        setUpProgram();
    
        glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    
        // Default unpack alignment is 4. The values of 'pixels' below needs it to be 1.
        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    
        glActiveTexture(GL_TEXTURE0);
        glGenTextures(1, &tex2D);
        glBindTexture(GL_TEXTURE_2D, tex2D);
    
        const std::vector<GLubyte> pixels(1 * npotTexSize * npotTexSize, 64);
    
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    
        // Check that an NPOT texture not on level 0 generates INVALID_VALUE
        glTexImage2D(GL_TEXTURE_2D, 1, GL_ALPHA, npotTexSize, npotTexSize, 0, GL_ALPHA,
                     GL_UNSIGNED_BYTE, pixels.data());
        EXPECT_GL_ERROR(GL_INVALID_VALUE);
    
        // Check that an NPOT texture on level 0 succeeds
        glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, npotTexSize, npotTexSize, 0, GL_ALPHA,
                     GL_UNSIGNED_BYTE, pixels.data());
        EXPECT_GL_NO_ERROR();
    
        // Check that generateMipmap fails on NPOT
        glGenerateMipmap(GL_TEXTURE_2D);
        EXPECT_GL_ERROR(GL_INVALID_OPERATION);
    
        // Check that nothing is drawn if filtering is not correct for NPOT
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
        glClear(GL_COLOR_BUFFER_BIT);
        drawQuad(mProgram, "position", 1.0f);
        EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 0, 0, 255);
    
        // NPOT texture with TEXTURE_MIN_FILTER not NEAREST or LINEAR should draw with 0,0,0,255
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
        glClear(GL_COLOR_BUFFER_BIT);
        drawQuad(mProgram, "position", 1.0f);
        EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 0, 0, 255);
    
        // NPOT texture with TEXTURE_MIN_FILTER set to LINEAR should draw
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glClear(GL_COLOR_BUFFER_BIT);
        drawQuad(mProgram, "position", 1.0f);
        EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 0, 0, 64);
    
        // Check that glTexImage2D for POT texture succeeds
        glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, potTexSize, potTexSize, 0, GL_ALPHA, GL_UNSIGNED_BYTE,
                     pixels.data());
        EXPECT_GL_NO_ERROR();
    
        // Check that generateMipmap for an POT texture succeeds
        glGenerateMipmap(GL_TEXTURE_2D);
        EXPECT_GL_NO_ERROR();
    
        // POT texture with TEXTURE_MIN_FILTER set to LINEAR_MIPMAP_LINEAR should draw
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
        glClear(GL_COLOR_BUFFER_BIT);
        drawQuad(mProgram, "position", 1.0f);
        EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 0, 0, 64);
        EXPECT_GL_NO_ERROR();
    }
    
    // Test to ensure that glTexSubImage2D always accepts data for non-power-of-two subregions.
    // ANGLE previously rejected this if GL_OES_texture_npot wasn't active, which is incorrect.
    TEST_P(Texture2DTest, NPOTSubImageParameters)
    {
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
    
        // Create an 8x8 (i.e. power-of-two) texture.
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glGenerateMipmap(GL_TEXTURE_2D);
    
        // Supply a 3x3 (i.e. non-power-of-two) subimage to the texture.
        // This should always work, even if GL_OES_texture_npot isn't active.
        std::array<GLColor, 3 * 3> data;
        glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, 3, 3, GL_RGBA, GL_UNSIGNED_BYTE, data.data());
    
        EXPECT_GL_NO_ERROR();
    }
    
    // Test to check that texture completeness is determined correctly when the texture base level is
    // greater than 0, and also that level 0 is not sampled when base level is greater than 0.
    TEST_P(Texture2DTestES3, DrawWithBaseLevel1)
    {
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
    
        std::vector<GLColor> texDataRed(4u * 4u, GLColor::red);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, texDataRed.data());
        std::vector<GLColor> texDataGreen(2u * 2u, GLColor::green);
        glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                     texDataGreen.data());
        glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                     texDataGreen.data());
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 1);
    
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    }
    
    // Test that drawing works correctly when levels outside the BASE_LEVEL/MAX_LEVEL range do not
    // have images defined.
    TEST_P(Texture2DTestES3, DrawWithLevelsOutsideRangeUndefined)
    {
        // Observed crashing on AMD. Oddly the crash only happens with 2D textures, not 3D or array.
        ANGLE_SKIP_TEST_IF(IsAMD() && IsOpenGL());
    
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
        std::vector<GLColor> texDataGreen(2u * 2u, GLColor::green);
        glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                     texDataGreen.data());
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 1);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
    
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    }
    
    // Test that drawing works correctly when level 0 is undefined and base level is 1.
    TEST_P(Texture2DTestES3, DrawWithLevelZeroUndefined)
    {
        // Observed crashing on AMD. Oddly the crash only happens with 2D textures, not 3D or array.
        ANGLE_SKIP_TEST_IF(IsAMD() && IsOpenGL());
    
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
        std::vector<GLColor> texDataGreen(2u * 2u, GLColor::green);
        glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                     texDataGreen.data());
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 1);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 2);
    
        EXPECT_GL_NO_ERROR();
    
        // Texture is incomplete.
        drawQuad(mProgram, "position", 0.5f);
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black);
    
        glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                     texDataGreen.data());
    
        // Texture is now complete.
        drawQuad(mProgram, "position", 0.5f);
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    }
    
    // Test that drawing works correctly when levels outside the BASE_LEVEL/MAX_LEVEL range have
    // dimensions that don't fit the images inside the range.
    // GLES 3.0.4 section 3.8.13 Texture completeness
    TEST_P(Texture2DTestES3, DrawWithLevelsOutsideRangeWithInconsistentDimensions)
    {
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
        std::vector<GLColor> texDataRed(8u * 8u, GLColor::red);
        std::vector<GLColor> texDataGreen(2u * 2u, GLColor::green);
        std::vector<GLColor> texDataCyan(2u * 2u, GLColor::cyan);
    
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    
        // Two levels that are initially unused.
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE, texDataRed.data());
        glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA8, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                     texDataCyan.data());
    
        // One level that is used - only this level should affect completeness.
        glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                     texDataGreen.data());
    
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 1);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
    
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    
        ANGLE_SKIP_TEST_IF(IsIntel() && IsWindows() && IsOpenGL());
    
        // Switch the level that is being used to the cyan level 2.
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 2);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 2);
    
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::cyan);
    }
    
    // Test that drawing works correctly when levels outside the BASE_LEVEL/MAX_LEVEL range do not
    // have images defined.
    TEST_P(Texture3DTestES3, DrawWithLevelsOutsideRangeUndefined)
    {
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_3D, mTexture3D);
        std::vector<GLColor> texDataGreen(2u * 2u * 2u, GLColor::green);
        glTexImage3D(GL_TEXTURE_3D, 1, GL_RGBA8, 2, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                     texDataGreen.data());
        glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
        glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_BASE_LEVEL, 1);
        glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAX_LEVEL, 1);
    
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    }
    
    // Test that drawing works correctly when levels outside the BASE_LEVEL/MAX_LEVEL range have
    // dimensions that don't fit the images inside the range.
    // GLES 3.0.4 section 3.8.13 Texture completeness
    TEST_P(Texture3DTestES3, DrawWithLevelsOutsideRangeWithInconsistentDimensions)
    {
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_3D, mTexture3D);
        std::vector<GLColor> texDataRed(8u * 8u * 8u, GLColor::red);
        std::vector<GLColor> texDataGreen(2u * 2u * 2u, GLColor::green);
        std::vector<GLColor> texDataCyan(2u * 2u * 2u, GLColor::cyan);
    
        glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    
        // Two levels that are initially unused.
        glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA8, 8, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                     texDataRed.data());
        glTexImage3D(GL_TEXTURE_3D, 2, GL_RGBA8, 2, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                     texDataCyan.data());
    
        // One level that is used - only this level should affect completeness.
        glTexImage3D(GL_TEXTURE_3D, 1, GL_RGBA8, 2, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                     texDataGreen.data());
    
        glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_BASE_LEVEL, 1);
        glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAX_LEVEL, 1);
    
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    
        ANGLE_SKIP_TEST_IF(IsIntel() && IsWindows() && IsOpenGL());
    
        // Switch the level that is being used to the cyan level 2.
        glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_BASE_LEVEL, 2);
        glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAX_LEVEL, 2);
    
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::cyan);
    }
    
    // Test that drawing works correctly when levels outside the BASE_LEVEL/MAX_LEVEL range do not
    // have images defined.
    TEST_P(Texture2DArrayTestES3, DrawWithLevelsOutsideRangeUndefined)
    {
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D_ARRAY, m2DArrayTexture);
        std::vector<GLColor> texDataGreen(2u * 2u * 2u, GLColor::green);
        glTexImage3D(GL_TEXTURE_2D_ARRAY, 1, GL_RGBA8, 2, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                     texDataGreen.data());
        glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
        glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_BASE_LEVEL, 1);
        glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, 1);
    
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    }
    
    // Test that drawing works correctly when levels outside the BASE_LEVEL/MAX_LEVEL range have
    // dimensions that don't fit the images inside the range.
    // GLES 3.0.4 section 3.8.13 Texture completeness
    TEST_P(Texture2DArrayTestES3, DrawWithLevelsOutsideRangeWithInconsistentDimensions)
    {
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_3D, m2DArrayTexture);
        std::vector<GLColor> texDataRed(8u * 8u * 8u, GLColor::red);
        std::vector<GLColor> texDataGreen(2u * 2u * 2u, GLColor::green);
        std::vector<GLColor> texDataCyan(2u * 2u * 2u, GLColor::cyan);
    
        glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    
        // Two levels that are initially unused.
        glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, 8, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                     texDataRed.data());
        glTexImage3D(GL_TEXTURE_2D_ARRAY, 2, GL_RGBA8, 2, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                     texDataCyan.data());
    
        // One level that is used - only this level should affect completeness.
        glTexImage3D(GL_TEXTURE_2D_ARRAY, 1, GL_RGBA8, 2, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                     texDataGreen.data());
    
        glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_BASE_LEVEL, 1);
        glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, 1);
    
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    
        ANGLE_SKIP_TEST_IF(IsIntel() && IsWindows() && IsOpenGL());
    
        // NVIDIA was observed drawing color 0,0,0,0 instead of the texture color after the base
        // level was changed.
        ANGLE_SKIP_TEST_IF(IsNVIDIA() && (IsOpenGL() || IsOpenGLES()));
    
        // Switch the level that is being used to the cyan level 2.
        glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_BASE_LEVEL, 2);
        glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, 2);
    
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::cyan);
    }
    
    // Test that texture completeness is updated if texture max level changes.
    // GLES 3.0.4 section 3.8.13 Texture completeness
    TEST_P(Texture2DTestES3, TextureCompletenessChangesWithMaxLevel)
    {
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
        std::vector<GLColor> texDataGreen(8u * 8u, GLColor::green);
    
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    
        // A level that is initially unused.
        glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                     texDataGreen.data());
    
        // One level that is initially used - only this level should affect completeness.
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                     texDataGreen.data());
    
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
    
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    
        // Switch the max level to level 1. The levels within the used range now have inconsistent
        // dimensions and the texture should be incomplete.
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
    
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black);
    }
    
    // Test that 3D texture completeness is updated if texture max level changes.
    // GLES 3.0.4 section 3.8.13 Texture completeness
    TEST_P(Texture3DTestES3, Texture3DCompletenessChangesWithMaxLevel)
    {
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_3D, mTexture3D);
        std::vector<GLColor> texDataGreen(2u * 2u * 2u, GLColor::green);
    
        glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    
        // A level that is initially unused.
        glTexImage3D(GL_TEXTURE_3D, 1, GL_RGBA8, 1, 1, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                     texDataGreen.data());
    
        // One level that is initially used - only this level should affect completeness.
        glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA8, 2, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                     texDataGreen.data());
    
        glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_BASE_LEVEL, 0);
        glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAX_LEVEL, 0);
    
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    
        // Switch the max level to level 1. The levels within the used range now have inconsistent
        // dimensions and the texture should be incomplete.
        glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAX_LEVEL, 1);
    
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black);
    }
    
    // Test that texture completeness is updated if texture base level changes.
    // GLES 3.0.4 section 3.8.13 Texture completeness
    TEST_P(Texture2DTestES3, TextureCompletenessChangesWithBaseLevel)
    {
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
        std::vector<GLColor> texDataGreen(8u * 8u, GLColor::green);
    
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    
        // Two levels that are initially unused.
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                     texDataGreen.data());
        glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                     texDataGreen.data());
    
        // One level that is initially used - only this level should affect completeness.
        glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA8, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                     texDataGreen.data());
    
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 2);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 2);
    
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    
        // Switch the base level to level 1. The levels within the used range now have inconsistent
        // dimensions and the texture should be incomplete.
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 1);
    
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black);
    }
    
    // Test that texture is not complete if base level is greater than max level.
    // GLES 3.0.4 section 3.8.13 Texture completeness
    TEST_P(Texture2DTestES3, TextureBaseLevelGreaterThanMaxLevel)
    {
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
    
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &GLColor::green);
    
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 10000);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
    
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
    
        // Texture should be incomplete.
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black);
    }
    
    // Test that immutable texture base level and max level are clamped.
    // GLES 3.0.4 section 3.8.10 subsection Mipmapping
    TEST_P(Texture2DTestES3, ImmutableTextureBaseLevelOutOfRange)
    {
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
    
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    
        glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1);
    
        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &GLColor::green);
    
        // For immutable-format textures, base level should be clamped to [0, levels - 1], and max level
        // should be clamped to [base_level, levels - 1].
        // GLES 3.0.4 section 3.8.10 subsection Mipmapping
        // In the case of this test, those rules make the effective base level and max level 0.
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 10000);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 10000);
    
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
    
        // Texture should be complete.
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    }
    
    // Test that changing base level works when it affects the format of the texture.
    TEST_P(Texture2DTestES3, TextureFormatChangesWithBaseLevel)
    {
        // Observed rendering corruption on NVIDIA OpenGL.
        ANGLE_SKIP_TEST_IF(IsNVIDIA() && IsOpenGL());
    
        ANGLE_SKIP_TEST_IF(IsIntel() && IsWindows() && IsDesktopOpenGL());
    
        // Observed incorrect rendering on AMD OpenGL.
        ANGLE_SKIP_TEST_IF(IsAMD() && IsDesktopOpenGL());
    
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
        std::vector<GLColor> texDataCyan(4u * 4u, GLColor::cyan);
        std::vector<GLColor> texDataGreen(4u * 4u, GLColor::green);
    
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    
        // RGBA8 level that's initially unused.
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                     texDataCyan.data());
    
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 1);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
    
        // RG8 level that's initially used, with consistent dimensions with level 0 but a different
        // format. It reads green channel data from the green and alpha channels of texDataGreen
        // (this is a bit hacky but works).
        glTexImage2D(GL_TEXTURE_2D, 1, GL_RG8, 2, 2, 0, GL_RG, GL_UNSIGNED_BYTE, texDataGreen.data());
    
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    
        // Switch the texture to use the cyan level 0 with the RGBA format.
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
    
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::cyan);
    }
    
    // Test that setting a texture image works when base level is out of range.
    TEST_P(Texture2DTestES3, SetImageWhenBaseLevelOutOfRange)
    {
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
    
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 10000);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 10000);
    
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &GLColor::green);
    
        EXPECT_GL_NO_ERROR();
    
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
    
        drawQuad(mProgram, "position", 0.5f);
    
        // Texture should be complete.
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    }
    
    // In the D3D11 renderer, we need to initialize some texture formats, to fill empty channels. EG
    // RBA->RGBA8, with 1.0 in the alpha channel. This test covers a bug where redefining array textures
    // with these formats does not work as expected.
    TEST_P(Texture2DArrayTestES3, RedefineInittableArray)
    {
        std::vector<GLubyte> pixelData;
        for (size_t count = 0; count < 5000; count++)
        {
            pixelData.push_back(0u);
            pixelData.push_back(255u);
            pixelData.push_back(0u);
        }
    
        glBindTexture(GL_TEXTURE_2D_ARRAY, m2DArrayTexture);
        glUseProgram(mProgram);
        glUniform1i(mTextureArrayLocation, 0);
    
        // The first draw worked correctly.
        glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGB, 4, 4, 2, 0, GL_RGB, GL_UNSIGNED_BYTE,
                     &pixelData[0]);
    
        glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_REPEAT);
        drawQuad(mProgram, "position", 1.0f);
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    
        // The dimension of the respecification must match the original exactly to trigger the bug.
        glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGB, 4, 4, 2, 0, GL_RGB, GL_UNSIGNED_BYTE,
                     &pixelData[0]);
        drawQuad(mProgram, "position", 1.0f);
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    
        ASSERT_GL_NO_ERROR();
    }
    
    // Test shadow sampler and regular non-shadow sampler coexisting in the same shader.
    // This test is needed especially to confirm that sampler registers get assigned correctly on
    // the HLSL backend even when there's a mix of different HLSL sampler and texture types.
    TEST_P(ShadowSamplerPlusSampler3DTestES3, ShadowSamplerPlusSampler3DDraw)
    {
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_3D, mTexture3D);
        GLubyte texData[4];
        texData[0] = 0;
        texData[1] = 60;
        texData[2] = 0;
        texData[3] = 255;
        glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, texData);
    
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, mTextureShadow);
        GLfloat depthTexData[1];
        depthTexData[0] = 0.5f;
        glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, 1, 1, 0, GL_DEPTH_COMPONENT, GL_FLOAT,
                     depthTexData);
    
        glUseProgram(mProgram);
        glUniform1f(mDepthRefUniformLocation, 0.3f);
        glUniform1i(mTexture3DUniformLocation, 0);
        glUniform1i(mTextureShadowUniformLocation, 1);
    
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
        drawQuad(mProgram, "position", 0.5f);
        EXPECT_GL_NO_ERROR();
        // The shader writes 0.5 * <comparison result (1.0)> + <texture color>
        EXPECT_PIXEL_NEAR(0, 0, 128, 188, 128, 255, 2);
    
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_GREATER);
        drawQuad(mProgram, "position", 0.5f);
        EXPECT_GL_NO_ERROR();
        // The shader writes 0.5 * <comparison result (0.0)> + <texture color>
        EXPECT_PIXEL_NEAR(0, 0, 0, 60, 0, 255, 2);
    }
    
    // Test multiple different sampler types in the same shader.
    // This test makes sure that even if sampler / texture registers get grouped together based on type
    // or otherwise get shuffled around in the HLSL backend of the shader translator, the D3D renderer
    // still has the right register index information for each ESSL sampler.
    // The tested ESSL samplers have the following types in D3D11 HLSL:
    // sampler2D:         Texture2D   + SamplerState
    // samplerCube:       TextureCube + SamplerState
    // sampler2DShadow:   Texture2D   + SamplerComparisonState
    // samplerCubeShadow: TextureCube + SamplerComparisonState
    TEST_P(SamplerTypeMixTestES3, SamplerTypeMixDraw)
    {
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
        GLubyte texData[4];
        texData[0] = 0;
        texData[1] = 0;
        texData[2] = 120;
        texData[3] = 255;
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, texData);
    
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_CUBE_MAP, mTextureCube);
        texData[0] = 0;
        texData[1] = 90;
        texData[2] = 0;
        texData[3] = 255;
        glTexStorage2D(GL_TEXTURE_CUBE_MAP, 1, GL_RGBA8, 1, 1);
        glTexSubImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE,
                        texData);
    
        glActiveTexture(GL_TEXTURE2);
        glBindTexture(GL_TEXTURE_2D, mTexture2DShadow);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
        GLfloat depthTexData[1];
        depthTexData[0] = 0.5f;
        glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, 1, 1, 0, GL_DEPTH_COMPONENT, GL_FLOAT,
                     depthTexData);
    
        glActiveTexture(GL_TEXTURE3);
        glBindTexture(GL_TEXTURE_CUBE_MAP, mTextureCubeShadow);
        glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
        depthTexData[0] = 0.2f;
        glTexStorage2D(GL_TEXTURE_CUBE_MAP, 1, GL_DEPTH_COMPONENT32F, 1, 1);
        glTexSubImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, 0, 0, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT,
                        depthTexData);
    
        EXPECT_GL_NO_ERROR();
    
        glUseProgram(mProgram);
        glUniform1f(mDepthRefUniformLocation, 0.3f);
        glUniform1i(mTexture2DUniformLocation, 0);
        glUniform1i(mTextureCubeUniformLocation, 1);
        glUniform1i(mTexture2DShadowUniformLocation, 2);
        glUniform1i(mTextureCubeShadowUniformLocation, 3);
    
        drawQuad(mProgram, "position", 0.5f);
        EXPECT_GL_NO_ERROR();
        // The shader writes:
        // <texture 2d color> +
        // <cube map color> +
        // 0.25 * <comparison result (1.0)> +
        // 0.125 * <comparison result (0.0)>
        EXPECT_PIXEL_NEAR(0, 0, 64, 154, 184, 255, 2);
    }
    
    // Test different base levels on textures accessed through the same sampler array.
    // Calling textureSize() on the samplers hits the D3D sampler metadata workaround.
    TEST_P(TextureSizeTextureArrayTest, BaseLevelVariesInTextureArray)
    {
        ANGLE_SKIP_TEST_IF(IsAMD() && IsD3D11());
    
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mTexture2DA);
        GLsizei size = 64;
        for (GLint level = 0; level < 7; ++level)
        {
            ASSERT_LT(0, size);
            glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                         nullptr);
            size = size / 2;
        }
        ASSERT_EQ(0, size);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 1);
    
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, mTexture2DB);
        size = 128;
        for (GLint level = 0; level < 8; ++level)
        {
            ASSERT_LT(0, size);
            glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                         nullptr);
            size = size / 2;
        }
        ASSERT_EQ(0, size);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 3);
        EXPECT_GL_NO_ERROR();
    
        glUseProgram(mProgram);
        glUniform1i(mTexture0Location, 0);
        glUniform1i(mTexture1Location, 1);
    
        drawQuad(mProgram, essl3_shaders::PositionAttrib(), 0.5f);
        EXPECT_GL_NO_ERROR();
        // Red channel: width of level 1 of texture A: 32.
        // Green channel: width of level 3 of texture B: 16.
        EXPECT_PIXEL_NEAR(0, 0, 32, 16, 0, 255, 2);
    }
    
    // When sampling a texture without an alpha channel, "1" is returned as the alpha value.
    // ES 3.0.4 table 3.24
    TEST_P(Texture2DTestES3, TextureRGBImplicitAlpha1)
    {
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
    
        EXPECT_PIXEL_ALPHA_EQ(0, 0, 255);
    }
    
    // When sampling a texture without an alpha channel, "1" is returned as the alpha value.
    // ES 3.0.4 table 3.24
    TEST_P(Texture2DTest, TextureLuminanceImplicitAlpha1)
    {
        setUpProgram();
    
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 1, 1, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, nullptr);
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
    
        EXPECT_PIXEL_ALPHA_EQ(0, 0, 255);
    }
    
    // Validate that every component of the pixel will be equal to the luminance value we've set
    // and that the alpha channel will be 1 (or 255 to be exact).
    TEST_P(Texture2DTest, TextureLuminanceRGBSame)
    {
        setUpProgram();
    
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
        uint8_t pixel = 50;
        glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 1, 1, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, &pixel);
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor(pixel, pixel, pixel, 255));
    }
    
    // Validate that every component of the pixel will be equal to the luminance value we've set
    // and that the alpha channel will be the second component.
    TEST_P(Texture2DTest, TextureLuminanceAlphaRGBSame)
    {
        setUpProgram();
    
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
        uint8_t pixel[] = {50, 25};
        glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, 1, 1, 0, GL_LUMINANCE_ALPHA,
                     GL_UNSIGNED_BYTE, pixel);
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor(pixel[0], pixel[0], pixel[0], pixel[1]));
    }
    
    // When sampling a texture without an alpha channel, "1" is returned as the alpha value.
    // ES 3.0.4 table 3.24
    TEST_P(Texture2DTest, TextureLuminance32ImplicitAlpha1)
    {
        ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_OES_texture_float"));
        ANGLE_SKIP_TEST_IF(IsD3D9());
        ANGLE_SKIP_TEST_IF(IsVulkan());
    
        setUpProgram();
    
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 1, 1, 0, GL_LUMINANCE, GL_FLOAT, nullptr);
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
    
        EXPECT_PIXEL_ALPHA_EQ(0, 0, 255);
    }
    
    // When sampling a texture without an alpha channel, "1" is returned as the alpha value.
    // ES 3.0.4 table 3.24
    TEST_P(Texture2DTest, TextureLuminance16ImplicitAlpha1)
    {
        ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_OES_texture_float"));
        ANGLE_SKIP_TEST_IF(IsD3D9());
        ANGLE_SKIP_TEST_IF(IsVulkan());
        ANGLE_SKIP_TEST_IF(IsNVIDIA() && IsOpenGLES());
        // TODO(ynovikov): re-enable once root cause of http://anglebug.com/1420 is fixed
        ANGLE_SKIP_TEST_IF(IsAndroid() && IsAdreno() && IsOpenGLES());
    
        setUpProgram();
    
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 1, 1, 0, GL_LUMINANCE, GL_HALF_FLOAT_OES, nullptr);
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
    
        EXPECT_PIXEL_ALPHA_EQ(0, 0, 255);
    }
    
    // When sampling a texture without an alpha channel, "1" is returned as the alpha value.
    // ES 3.0.4 table 3.24
    TEST_P(Texture2DUnsignedIntegerAlpha1TestES3, TextureRGB8UIImplicitAlpha1)
    {
        ANGLE_SKIP_TEST_IF(IsIntel() && IsOSX());
    
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8UI, 1, 1, 0, GL_RGB_INTEGER, GL_UNSIGNED_BYTE, nullptr);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
    
        EXPECT_PIXEL_ALPHA_EQ(0, 0, 255);
    }
    
    // When sampling a texture without an alpha channel, "1" is returned as the alpha value.
    // ES 3.0.4 table 3.24
    TEST_P(Texture2DIntegerAlpha1TestES3, TextureRGB8IImplicitAlpha1)
    {
        ANGLE_SKIP_TEST_IF(IsIntel() && IsOSX());
    
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
    
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8I, 1, 1, 0, GL_RGB_INTEGER, GL_BYTE, nullptr);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
    
        EXPECT_PIXEL_ALPHA_EQ(0, 0, 255);
    }
    
    // When sampling a texture without an alpha channel, "1" is returned as the alpha value.
    // ES 3.0.4 table 3.24
    TEST_P(Texture2DUnsignedIntegerAlpha1TestES3, TextureRGB16UIImplicitAlpha1)
    {
        ANGLE_SKIP_TEST_IF(IsIntel() && IsOSX());
    
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16UI, 1, 1, 0, GL_RGB_INTEGER, GL_UNSIGNED_SHORT, nullptr);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
    
        EXPECT_PIXEL_ALPHA_EQ(0, 0, 255);
    }
    
    // When sampling a texture without an alpha channel, "1" is returned as the alpha value.
    // ES 3.0.4 table 3.24
    TEST_P(Texture2DIntegerAlpha1TestES3, TextureRGB16IImplicitAlpha1)
    {
        ANGLE_SKIP_TEST_IF(IsIntel() && IsOSX());
    
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16I, 1, 1, 0, GL_RGB_INTEGER, GL_SHORT, nullptr);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
    
        EXPECT_PIXEL_ALPHA_EQ(0, 0, 255);
    }
    
    // When sampling a texture without an alpha channel, "1" is returned as the alpha value.
    // ES 3.0.4 table 3.24
    TEST_P(Texture2DUnsignedIntegerAlpha1TestES3, TextureRGB32UIImplicitAlpha1)
    {
        ANGLE_SKIP_TEST_IF(IsIntel() && IsOSX());
    
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32UI, 1, 1, 0, GL_RGB_INTEGER, GL_UNSIGNED_INT, nullptr);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
    
        EXPECT_PIXEL_ALPHA_EQ(0, 0, 255);
    }
    
    // When sampling a texture without an alpha channel, "1" is returned as the alpha value.
    // ES 3.0.4 table 3.24
    TEST_P(Texture2DIntegerAlpha1TestES3, TextureRGB32IImplicitAlpha1)
    {
        ANGLE_SKIP_TEST_IF(IsIntel() && IsOSX());
    
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32I, 1, 1, 0, GL_RGB_INTEGER, GL_INT, nullptr);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
    
        EXPECT_PIXEL_ALPHA_EQ(0, 0, 255);
    }
    
    // When sampling a texture without an alpha channel, "1" is returned as the alpha value.
    // ES 3.0.4 table 3.24
    TEST_P(Texture2DTestES3, TextureRGBSNORMImplicitAlpha1)
    {
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8_SNORM, 1, 1, 0, GL_RGB, GL_BYTE, nullptr);
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
    
        EXPECT_PIXEL_ALPHA_EQ(0, 0, 255);
    }
    
    // When sampling a texture without an alpha channel, "1" is returned as the alpha value.
    // ES 3.0.4 table 3.24
    TEST_P(Texture2DTestES3, TextureRGB9E5ImplicitAlpha1)
    {
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB9_E5, 1, 1, 0, GL_RGB, GL_UNSIGNED_INT_5_9_9_9_REV,
                     nullptr);
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
    
        EXPECT_PIXEL_ALPHA_EQ(0, 0, 255);
    }
    
    // When sampling a texture without an alpha channel, "1" is returned as the alpha value.
    // ES 3.0.4 table 3.24
    TEST_P(Texture2DTestES3, TextureCOMPRESSEDRGB8ETC2ImplicitAlpha1)
    {
        // Seems to fail on OSX 10.12 Intel.
        ANGLE_SKIP_TEST_IF(IsOSX() && IsIntel() && IsOpenGL());
    
        // http://anglebug.com/2190
        ANGLE_SKIP_TEST_IF(IsOSX() && IsNVIDIA() && IsDesktopOpenGL());
    
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
        glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB8_ETC2, 1, 1, 0, 8, nullptr);
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
    
        EXPECT_PIXEL_ALPHA_EQ(0, 0, 255);
    }
    
    // When sampling a texture without an alpha channel, "1" is returned as the alpha value.
    // ES 3.0.4 table 3.24
    TEST_P(Texture2DTestES3, TextureCOMPRESSEDSRGB8ETC2ImplicitAlpha1)
    {
        // Seems to fail on OSX 10.12 Intel.
        ANGLE_SKIP_TEST_IF(IsOSX() && IsIntel() && IsOpenGL());
    
        // http://anglebug.com/2190
        ANGLE_SKIP_TEST_IF(IsOSX() && IsNVIDIA() && IsDesktopOpenGL());
    
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
        glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_SRGB8_ETC2, 1, 1, 0, 8, nullptr);
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
    
        EXPECT_PIXEL_ALPHA_EQ(0, 0, 255);
    }
    
    // Use a sampler in a uniform struct.
    TEST_P(SamplerInStructTest, SamplerInStruct)
    {
        runSamplerInStructTest();
    }
    
    // Use a sampler in a uniform struct that's passed as a function parameter.
    TEST_P(SamplerInStructAsFunctionParameterTest, SamplerInStructAsFunctionParameter)
    {
        // TODO(ynovikov): re-enable once root cause of http://anglebug.com/1427 is fixed
        ANGLE_SKIP_TEST_IF(IsAndroid() && IsAdreno() && IsOpenGLES());
    
        runSamplerInStructTest();
    }
    
    // Use a sampler in a uniform struct array with a struct from the array passed as a function
    // parameter.
    TEST_P(SamplerInStructArrayAsFunctionParameterTest, SamplerInStructArrayAsFunctionParameter)
    {
        // TODO(ynovikov): re-enable once root cause of http://anglebug.com/1427 is fixed
        ANGLE_SKIP_TEST_IF(IsAndroid() && IsAdreno() && IsOpenGLES());
    
        runSamplerInStructTest();
    }
    
    // Use a sampler in a struct inside a uniform struct with the nested struct passed as a function
    // parameter.
    TEST_P(SamplerInNestedStructAsFunctionParameterTest, SamplerInNestedStructAsFunctionParameter)
    {
        // TODO(ynovikov): re-enable once root cause of http://anglebug.com/1427 is fixed
        ANGLE_SKIP_TEST_IF(IsAndroid() && IsAdreno() && IsOpenGLES());
    
        runSamplerInStructTest();
    }
    
    // Make sure that there isn't a name conflict between sampler extracted from a struct and a
    // similarly named uniform.
    TEST_P(SamplerInStructAndOtherVariableTest, SamplerInStructAndOtherVariable)
    {
        runSamplerInStructTest();
    }
    
    // GL_OES_texture_border_clamp
    class TextureBorderClampTest : public Texture2DTest
    {
      protected:
        TextureBorderClampTest() : Texture2DTest() {}
    
        const char *getVertexShaderSource() override
        {
            return
                R"(precision highp float;
                attribute vec4 position;
                varying vec2 texcoord;
    
                void main()
                {
                    gl_Position = vec4(position.xy, 0.0, 1.0);
                    // texcoords in [-0.5, 1.5]
                    texcoord = (position.xy) + 0.5;
                })";
        }
    
        void uploadTexture()
        {
            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D, mTexture2D);
            std::vector<GLColor> texDataRed(1, GLColor::red);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                         texDataRed.data());
            EXPECT_GL_NO_ERROR();
        }
    };
    
    // Test if the color set as GL_TEXTURE_BORDER_COLOR is used when sampling outside of the texture in
    // GL_CLAMP_TO_BORDER wrap mode (set with glTexParameter).
    TEST_P(TextureBorderClampTest, TextureBorderClampFunctional)
    {
        ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_OES_texture_border_clamp"));
    
        setUpProgram();
    
        uploadTexture();
    
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, &kFloatGreen.R);
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
    
        EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(getWindowWidth() - 1, getWindowHeight() - 1, GLColor::green);
    }
    
    // Test reading back GL_TEXTURE_BORDER_COLOR by glGetTexParameter.
    TEST_P(TextureBorderClampTest, TextureBorderClampFunctional2)
    {
        ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_OES_texture_border_clamp"));
    
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
    
        glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, &kFloatGreen.R);
    
        GLint colorFixedPoint[4] = {0};
        glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, colorFixedPoint);
        constexpr GLint colorGreenFixedPoint[4] = {0, std::numeric_limits<GLint>::max(), 0,
                                                   std::numeric_limits<GLint>::max()};
        EXPECT_EQ(colorFixedPoint[0], colorGreenFixedPoint[0]);
        EXPECT_EQ(colorFixedPoint[1], colorGreenFixedPoint[1]);
        EXPECT_EQ(colorFixedPoint[2], colorGreenFixedPoint[2]);
        EXPECT_EQ(colorFixedPoint[3], colorGreenFixedPoint[3]);
    
        constexpr GLint colorBlueFixedPoint[4] = {0, 0, std::numeric_limits<GLint>::max(),
                                                  std::numeric_limits<GLint>::max()};
        glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, colorBlueFixedPoint);
    
        GLfloat color[4] = {0.0f};
        glGetTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, color);
        EXPECT_EQ(color[0], kFloatBlue.R);
        EXPECT_EQ(color[1], kFloatBlue.G);
        EXPECT_EQ(color[2], kFloatBlue.B);
        EXPECT_EQ(color[3], kFloatBlue.A);
    }
    
    // Test GL_TEXTURE_BORDER_COLOR parameter validation at glTexParameter.
    TEST_P(TextureBorderClampTest, TextureBorderClampValidation)
    {
        ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_OES_texture_border_clamp"));
    
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
    
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, 1.0f);
        EXPECT_GL_ERROR(GL_INVALID_ENUM);
    
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, std::numeric_limits<GLint>::max());
        EXPECT_GL_ERROR(GL_INVALID_ENUM);
    
        glTexParameterfv(GL_TEXTURE_2D_MULTISAMPLE, GL_TEXTURE_BORDER_COLOR, &kFloatGreen.R);
        EXPECT_GL_ERROR(GL_INVALID_ENUM);
    
        GLint colorInt[4] = {0};
        glTexParameteriv(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, GL_TEXTURE_BORDER_COLOR, colorInt);
        EXPECT_GL_ERROR(GL_INVALID_ENUM);
    
        if (getClientMajorVersion() < 3)
        {
            glTexParameterIivOES(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, colorInt);
            EXPECT_GL_ERROR(GL_INVALID_OPERATION);
            glGetTexParameterIivOES(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, colorInt);
            EXPECT_GL_ERROR(GL_INVALID_OPERATION);
    
            GLuint colorUInt[4] = {0};
            glTexParameterIuivOES(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, colorUInt);
            EXPECT_GL_ERROR(GL_INVALID_OPERATION);
            glGetTexParameterIuivOES(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, colorUInt);
            EXPECT_GL_ERROR(GL_INVALID_OPERATION);
    
            GLSampler sampler;
            glSamplerParameterIivOES(sampler, GL_TEXTURE_BORDER_COLOR, colorInt);
            EXPECT_GL_ERROR(GL_INVALID_OPERATION);
            glGetSamplerParameterIivOES(sampler, GL_TEXTURE_BORDER_COLOR, colorInt);
            EXPECT_GL_ERROR(GL_INVALID_OPERATION);
    
            glSamplerParameterIuivOES(sampler, GL_TEXTURE_BORDER_COLOR, colorUInt);
            EXPECT_GL_ERROR(GL_INVALID_OPERATION);
            glGetSamplerParameterIuivOES(sampler, GL_TEXTURE_BORDER_COLOR, colorUInt);
            EXPECT_GL_ERROR(GL_INVALID_OPERATION);
        }
    }
    
    class TextureBorderClampTestES3 : public TextureBorderClampTest
    {
      protected:
        TextureBorderClampTestES3() : TextureBorderClampTest() {}
    };
    
    // Test if the color set as GL_TEXTURE_BORDER_COLOR is used when sampling outside of the texture in
    // GL_CLAMP_TO_BORDER wrap mode (set with glSamplerParameter).
    TEST_P(TextureBorderClampTestES3, TextureBorderClampES3Functional)
    {
        ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_OES_texture_border_clamp"));
    
        setUpProgram();
    
        uploadTexture();
    
        GLSampler sampler;
        glBindSampler(0, sampler);
        glSamplerParameteri(sampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
        glSamplerParameteri(sampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
        glSamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glSamplerParameterfv(sampler, GL_TEXTURE_BORDER_COLOR, &kFloatGreen.R);
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
    
        EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(getWindowWidth() - 1, getWindowHeight() - 1, GLColor::green);
    }
    
    // Test reading back GL_TEXTURE_BORDER_COLOR by glGetSamplerParameter.
    TEST_P(TextureBorderClampTestES3, TextureBorderClampES3Functional2)
    {
        ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_OES_texture_border_clamp"));
    
        glActiveTexture(GL_TEXTURE0);
    
        GLSampler sampler;
        glBindSampler(0, sampler);
    
        glSamplerParameterfv(sampler, GL_TEXTURE_BORDER_COLOR, &kFloatGreen.R);
    
        GLint colorFixedPoint[4] = {0};
        glGetSamplerParameteriv(sampler, GL_TEXTURE_BORDER_COLOR, colorFixedPoint);
        constexpr GLint colorGreenFixedPoint[4] = {0, std::numeric_limits<GLint>::max(), 0,
                                                   std::numeric_limits<GLint>::max()};
        EXPECT_EQ(colorFixedPoint[0], colorGreenFixedPoint[0]);
        EXPECT_EQ(colorFixedPoint[1], colorGreenFixedPoint[1]);
        EXPECT_EQ(colorFixedPoint[2], colorGreenFixedPoint[2]);
        EXPECT_EQ(colorFixedPoint[3], colorGreenFixedPoint[3]);
    
        constexpr GLint colorBlueFixedPoint[4] = {0, 0, std::numeric_limits<GLint>::max(),
                                                  std::numeric_limits<GLint>::max()};
        glSamplerParameteriv(sampler, GL_TEXTURE_BORDER_COLOR, colorBlueFixedPoint);
    
        GLfloat color[4] = {0.0f};
        glGetSamplerParameterfv(sampler, GL_TEXTURE_BORDER_COLOR, color);
        EXPECT_EQ(color[0], kFloatBlue.R);
        EXPECT_EQ(color[1], kFloatBlue.G);
        EXPECT_EQ(color[2], kFloatBlue.B);
        EXPECT_EQ(color[3], kFloatBlue.A);
    
        constexpr GLint colorSomewhatRedInt[4] = {500000, 0, 0, std::numeric_limits<GLint>::max()};
        glSamplerParameterIivOES(sampler, GL_TEXTURE_BORDER_COLOR, colorSomewhatRedInt);
        GLint colorInt[4] = {0};
        glGetSamplerParameterIivOES(sampler, GL_TEXTURE_BORDER_COLOR, colorInt);
        EXPECT_EQ(colorInt[0], colorSomewhatRedInt[0]);
        EXPECT_EQ(colorInt[1], colorSomewhatRedInt[1]);
        EXPECT_EQ(colorInt[2], colorSomewhatRedInt[2]);
        EXPECT_EQ(colorInt[3], colorSomewhatRedInt[3]);
    
        constexpr GLuint colorSomewhatRedUInt[4] = {500000, 0, 0, std::numeric_limits<GLuint>::max()};
        glSamplerParameterIuivOES(sampler, GL_TEXTURE_BORDER_COLOR, colorSomewhatRedUInt);
        GLuint colorUInt[4] = {0};
        glGetSamplerParameterIuivOES(sampler, GL_TEXTURE_BORDER_COLOR, colorUInt);
        EXPECT_EQ(colorUInt[0], colorSomewhatRedUInt[0]);
        EXPECT_EQ(colorUInt[1], colorSomewhatRedUInt[1]);
        EXPECT_EQ(colorUInt[2], colorSomewhatRedUInt[2]);
        EXPECT_EQ(colorUInt[3], colorSomewhatRedUInt[3]);
    
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
    
        constexpr GLint colorSomewhatGreenInt[4] = {0, 500000, 0, std::numeric_limits<GLint>::max()};
        glTexParameterIivOES(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, colorSomewhatGreenInt);
        glGetTexParameterIivOES(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, colorInt);
        EXPECT_EQ(colorInt[0], colorSomewhatGreenInt[0]);
        EXPECT_EQ(colorInt[1], colorSomewhatGreenInt[1]);
        EXPECT_EQ(colorInt[2], colorSomewhatGreenInt[2]);
        EXPECT_EQ(colorInt[3], colorSomewhatGreenInt[3]);
    
        constexpr GLuint colorSomewhatGreenUInt[4] = {0, 500000, 0, std::numeric_limits<GLuint>::max()};
        glTexParameterIuivOES(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, colorSomewhatGreenUInt);
        glGetTexParameterIuivOES(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, colorUInt);
        EXPECT_EQ(colorUInt[0], colorSomewhatGreenUInt[0]);
        EXPECT_EQ(colorUInt[1], colorSomewhatGreenUInt[1]);
        EXPECT_EQ(colorUInt[2], colorSomewhatGreenUInt[2]);
        EXPECT_EQ(colorUInt[3], colorSomewhatGreenUInt[3]);
    }
    
    // Test GL_TEXTURE_BORDER_COLOR parameter validation at glSamplerParameter.
    TEST_P(TextureBorderClampTestES3, TextureBorderClampES3Validation)
    {
        ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_OES_texture_border_clamp"));
    
        glActiveTexture(GL_TEXTURE0);
    
        GLSampler sampler;
        glBindSampler(0, sampler);
    
        glSamplerParameterf(sampler, GL_TEXTURE_BORDER_COLOR, 1.0f);
        EXPECT_GL_ERROR(GL_INVALID_ENUM);
    
        glSamplerParameteri(sampler, GL_TEXTURE_BORDER_COLOR, std::numeric_limits<GLint>::max());
        EXPECT_GL_ERROR(GL_INVALID_ENUM);
    }
    
    class TextureBorderClampIntegerTestES3 : public Texture2DTest
    {
      protected:
        TextureBorderClampIntegerTestES3() : Texture2DTest(), isUnsignedIntTest(false) {}
    
        const char *getVertexShaderSource() override
        {
            return
                R"(#version 300 es
                out vec2 texcoord;
                in vec4 position;
    
                void main()
                {
                    gl_Position = vec4(position.xy, 0.0, 1.0);
                    // texcoords in [-0.5, 1.5]
                    texcoord = (position.xy) + 0.5;
                })";
        }
    
        const char *getFragmentShaderSource() override
        {
            if (isUnsignedIntTest)
            {
                return "#version 300 es\n"
                       "precision highp float;\n"
                       "uniform highp usampler2D tex;\n"
                       "in vec2 texcoord;\n"
                       "out vec4 fragColor;\n"
    
                       "void main()\n"
                       "{\n"
                       "vec4 red   = vec4(1.0, 0.0, 0.0, 1.0);\n"
                       "vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n"
                       "fragColor = (texture(tex, texcoord).r == 150u)"
                       "            ? green : red;\n"
                       "}\n";
            }
            else
            {
                return "#version 300 es\n"
                       "precision highp float;\n"
                       "uniform highp isampler2D tex;\n"
                       "in vec2 texcoord;\n"
                       "out vec4 fragColor;\n"
    
                       "void main()\n"
                       "{\n"
                       "vec4 red   = vec4(1.0, 0.0, 0.0, 1.0);\n"
                       "vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n"
                       "fragColor = (texture(tex, texcoord).r == -50)"
                       "            ? green : red;\n"
                       "}\n";
            }
        }
    
        void uploadTexture()
        {
            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D, mTexture2D);
            if (isUnsignedIntTest)
            {
                std::vector<GLubyte> texData(4, 100);
                glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8UI, 1, 1, 0, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE,
                             texData.data());
            }
            else
            {
                std::vector<GLbyte> texData(4, 100);
                glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8I, 1, 1, 0, GL_RGBA_INTEGER, GL_BYTE,
                             texData.data());
            }
            EXPECT_GL_NO_ERROR();
        }
    
        bool isUnsignedIntTest;
    };
    
    // Test if the integer values set as GL_TEXTURE_BORDER_COLOR is used when sampling outside of the
    // integer texture in GL_CLAMP_TO_BORDER wrap mode (set with glTexParameterIivOES).
    TEST_P(TextureBorderClampIntegerTestES3, TextureBorderClampInteger)
    {
        ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_OES_texture_border_clamp"));
    
        setUpProgram();
    
        uploadTexture();
    
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    
        constexpr GLint borderColor[4] = {-50, -50, -50, -50};
        glTexParameterIivOES(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
    
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
    
        EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(getWindowWidth() - 1, getWindowHeight() - 1, GLColor::green);
    }
    
    // Test if the integer values set as GL_TEXTURE_BORDER_COLOR is used when sampling outside of the
    // integer texture in GL_CLAMP_TO_BORDER wrap mode (set with glTexParameterIivOES).
    TEST_P(TextureBorderClampIntegerTestES3, TextureBorderClampInteger2)
    {
        ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_OES_texture_border_clamp"));
    
        setUpProgram();
    
        uploadTexture();
    
        GLSampler sampler;
        glBindSampler(0, sampler);
        glSamplerParameteri(sampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
        glSamplerParameteri(sampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
        glSamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    
        constexpr GLint borderColor[4] = {-50, -50, -50, -50};
        glSamplerParameterIivOES(sampler, GL_TEXTURE_BORDER_COLOR, borderColor);
    
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
    
        EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(getWindowWidth() - 1, getWindowHeight() - 1, GLColor::green);
    }
    
    // Test if the unsigned integer values set as GL_TEXTURE_BORDER_COLOR is used when sampling outside
    // of the unsigned integer texture in GL_CLAMP_TO_BORDER wrap mode (set with glTexParameterIuivOES).
    TEST_P(TextureBorderClampIntegerTestES3, TextureBorderClampIntegerUnsigned)
    {
        ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_OES_texture_border_clamp"));
    
        isUnsignedIntTest = true;
    
        setUpProgram();
    
        uploadTexture();
    
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    
        constexpr GLuint borderColor[4] = {150, 150, 150, 150};
        glTexParameterIuivOES(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
    
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
    
        EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(getWindowWidth() - 1, getWindowHeight() - 1, GLColor::green);
    }
    
    // Test if the unsigned integer values set as GL_TEXTURE_BORDER_COLOR is used when sampling outside
    // of the unsigned integer texture in GL_CLAMP_TO_BORDER wrap mode (set with
    // glSamplerParameterIuivOES).
    TEST_P(TextureBorderClampIntegerTestES3, TextureBorderClampIntegerUnsigned2)
    {
        ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_OES_texture_border_clamp"));
    
        isUnsignedIntTest = true;
    
        setUpProgram();
    
        uploadTexture();
    
        GLSampler sampler;
        glBindSampler(0, sampler);
        glSamplerParameteri(sampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
        glSamplerParameteri(sampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
        glSamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    
        constexpr GLuint borderColor[4] = {150, 150, 150, 150};
        glSamplerParameterIuivOES(sampler, GL_TEXTURE_BORDER_COLOR, borderColor);
    
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
    
        EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(getWindowWidth() - 1, getWindowHeight() - 1, GLColor::green);
    }
    
    // ~GL_OES_texture_border_clamp
    
    class TextureLimitsTest : public ANGLETest
    {
      protected:
        struct RGBA8
        {
            uint8_t R, G, B, A;
        };
    
        TextureLimitsTest()
            : mProgram(0), mMaxVertexTextures(0), mMaxFragmentTextures(0), mMaxCombinedTextures(0)
        {
            setWindowWidth(128);
            setWindowHeight(128);
            setConfigRedBits(8);
            setConfigGreenBits(8);
            setConfigBlueBits(8);
            setConfigAlphaBits(8);
        }
    
        void SetUp() override
        {
            ANGLETest::SetUp();
    
            glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &mMaxVertexTextures);
            glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &mMaxFragmentTextures);
            glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &mMaxCombinedTextures);
    
            ASSERT_GL_NO_ERROR();
        }
    
        void TearDown() override
        {
            if (mProgram != 0)
            {
                glDeleteProgram(mProgram);
                mProgram = 0;
    
                if (!mTextures.empty())
                {
                    glDeleteTextures(static_cast<GLsizei>(mTextures.size()), &mTextures[0]);
                }
            }
    
            ANGLETest::TearDown();
        }
    
        void compileProgramWithTextureCounts(const std::string &vertexPrefix,
                                             GLint vertexTextureCount,
                                             GLint vertexActiveTextureCount,
                                             const std::string &fragPrefix,
                                             GLint fragmentTextureCount,
                                             GLint fragmentActiveTextureCount)
        {
            std::stringstream vertexShaderStr;
            vertexShaderStr << "attribute vec2 position;\n"
                            << "varying vec4 color;\n"
                            << "varying vec2 texCoord;\n";
    
            for (GLint textureIndex = 0; textureIndex < vertexTextureCount; ++textureIndex)
            {
                vertexShaderStr << "uniform sampler2D " << vertexPrefix << textureIndex << ";\n";
            }
    
            vertexShaderStr << "void main() {\n"
                            << "  gl_Position = vec4(position, 0, 1);\n"
                            << "  texCoord = (position * 0.5) + 0.5;\n"
                            << "  color = vec4(0);\n";
    
            for (GLint textureIndex = 0; textureIndex < vertexActiveTextureCount; ++textureIndex)
            {
                vertexShaderStr << "  color += texture2D(" << vertexPrefix << textureIndex
                                << ", texCoord);\n";
            }
    
            vertexShaderStr << "}";
    
            std::stringstream fragmentShaderStr;
            fragmentShaderStr << "varying mediump vec4 color;\n"
                              << "varying mediump vec2 texCoord;\n";
    
            for (GLint textureIndex = 0; textureIndex < fragmentTextureCount; ++textureIndex)
            {
                fragmentShaderStr << "uniform sampler2D " << fragPrefix << textureIndex << ";\n";
            }
    
            fragmentShaderStr << "void main() {\n"
                              << "  gl_FragColor = color;\n";
    
            for (GLint textureIndex = 0; textureIndex < fragmentActiveTextureCount; ++textureIndex)
            {
                fragmentShaderStr << "  gl_FragColor += texture2D(" << fragPrefix << textureIndex
                                  << ", texCoord);\n";
            }
    
            fragmentShaderStr << "}";
    
            const std::string &vertexShaderSource   = vertexShaderStr.str();
            const std::string &fragmentShaderSource = fragmentShaderStr.str();
    
            mProgram = CompileProgram(vertexShaderSource.c_str(), fragmentShaderSource.c_str());
        }
    
        RGBA8 getPixel(GLint texIndex)
        {
            RGBA8 pixel = {static_cast<uint8_t>(texIndex & 0x7u), static_cast<uint8_t>(texIndex >> 3),
                           0, 255u};
            return pixel;
        }
    
        void initTextures(GLint tex2DCount, GLint texCubeCount)
        {
            GLint totalCount = tex2DCount + texCubeCount;
            mTextures.assign(totalCount, 0);
            glGenTextures(totalCount, &mTextures[0]);
            ASSERT_GL_NO_ERROR();
    
            std::vector<RGBA8> texData(16 * 16);
    
            GLint texIndex = 0;
            for (; texIndex < tex2DCount; ++texIndex)
            {
                texData.assign(texData.size(), getPixel(texIndex));
                glActiveTexture(GL_TEXTURE0 + texIndex);
                glBindTexture(GL_TEXTURE_2D, mTextures[texIndex]);
                glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                             &texData[0]);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
            }
    
            ASSERT_GL_NO_ERROR();
    
            for (; texIndex < texCubeCount; ++texIndex)
            {
                texData.assign(texData.size(), getPixel(texIndex));
                glActiveTexture(GL_TEXTURE0 + texIndex);
                glBindTexture(GL_TEXTURE_CUBE_MAP, mTextures[texIndex]);
                glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGBA, 16, 16, 0, GL_RGBA,
                             GL_UNSIGNED_BYTE, &texData[0]);
                glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGBA, 16, 16, 0, GL_RGBA,
                             GL_UNSIGNED_BYTE, &texData[0]);
                glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGBA, 16, 16, 0, GL_RGBA,
                             GL_UNSIGNED_BYTE, &texData[0]);
                glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGBA, 16, 16, 0, GL_RGBA,
                             GL_UNSIGNED_BYTE, &texData[0]);
                glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGBA, 16, 16, 0, GL_RGBA,
                             GL_UNSIGNED_BYTE, &texData[0]);
                glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGBA, 16, 16, 0, GL_RGBA,
                             GL_UNSIGNED_BYTE, &texData[0]);
                glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
                glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
                glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
                glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
            }
    
            ASSERT_GL_NO_ERROR();
        }
    
        void testWithTextures(GLint vertexTextureCount,
                              const std::string &vertexTexturePrefix,
                              GLint fragmentTextureCount,
                              const std::string &fragmentTexturePrefix)
        {
            // Generate textures
            initTextures(vertexTextureCount + fragmentTextureCount, 0);
    
            glUseProgram(mProgram);
            RGBA8 expectedSum = {0};
            for (GLint texIndex = 0; texIndex < vertexTextureCount; ++texIndex)
            {
                std::stringstream uniformNameStr;
                uniformNameStr << vertexTexturePrefix << texIndex;
                const std::string &uniformName = uniformNameStr.str();
                GLint location                 = glGetUniformLocation(mProgram, uniformName.c_str());
                ASSERT_NE(-1, location);
    
                glUniform1i(location, texIndex);
                RGBA8 contribution = getPixel(texIndex);
                expectedSum.R += contribution.R;
                expectedSum.G += contribution.G;
            }
    
            for (GLint texIndex = 0; texIndex < fragmentTextureCount; ++texIndex)
            {
                std::stringstream uniformNameStr;
                uniformNameStr << fragmentTexturePrefix << texIndex;
                const std::string &uniformName = uniformNameStr.str();
                GLint location                 = glGetUniformLocation(mProgram, uniformName.c_str());
                ASSERT_NE(-1, location);
    
                glUniform1i(location, texIndex + vertexTextureCount);
                RGBA8 contribution = getPixel(texIndex + vertexTextureCount);
                expectedSum.R += contribution.R;
                expectedSum.G += contribution.G;
            }
    
            ASSERT_GE(256u, expectedSum.G);
    
            drawQuad(mProgram, "position", 0.5f);
            ASSERT_GL_NO_ERROR();
            EXPECT_PIXEL_EQ(0, 0, expectedSum.R, expectedSum.G, 0, 255);
        }
    
        GLuint mProgram;
        std::vector<GLuint> mTextures;
        GLint mMaxVertexTextures;
        GLint mMaxFragmentTextures;
        GLint mMaxCombinedTextures;
    };
    
    // Test rendering with the maximum vertex texture units.
    TEST_P(TextureLimitsTest, MaxVertexTextures)
    {
        compileProgramWithTextureCounts("tex", mMaxVertexTextures, mMaxVertexTextures, "tex", 0, 0);
        ASSERT_NE(0u, mProgram);
        ASSERT_GL_NO_ERROR();
    
        testWithTextures(mMaxVertexTextures, "tex", 0, "tex");
    }
    
    // Test rendering with the maximum fragment texture units.
    TEST_P(TextureLimitsTest, MaxFragmentTextures)
    {
        compileProgramWithTextureCounts("tex", 0, 0, "tex", mMaxFragmentTextures, mMaxFragmentTextures);
        ASSERT_NE(0u, mProgram);
        ASSERT_GL_NO_ERROR();
    
        testWithTextures(mMaxFragmentTextures, "tex", 0, "tex");
    }
    
    // Test rendering with maximum combined texture units.
    TEST_P(TextureLimitsTest, MaxCombinedTextures)
    {
        GLint vertexTextures = mMaxVertexTextures;
    
        if (vertexTextures + mMaxFragmentTextures > mMaxCombinedTextures)
        {
            vertexTextures = mMaxCombinedTextures - mMaxFragmentTextures;
        }
    
        compileProgramWithTextureCounts("vtex", vertexTextures, vertexTextures, "ftex",
                                        mMaxFragmentTextures, mMaxFragmentTextures);
        ASSERT_NE(0u, mProgram);
        ASSERT_GL_NO_ERROR();
    
        testWithTextures(vertexTextures, "vtex", mMaxFragmentTextures, "ftex");
    }
    
    // Negative test for exceeding the number of vertex textures
    TEST_P(TextureLimitsTest, ExcessiveVertexTextures)
    {
        compileProgramWithTextureCounts("tex", mMaxVertexTextures + 1, mMaxVertexTextures + 1, "tex", 0,
                                        0);
        ASSERT_EQ(0u, mProgram);
    }
    
    // Negative test for exceeding the number of fragment textures
    TEST_P(TextureLimitsTest, ExcessiveFragmentTextures)
    {
        compileProgramWithTextureCounts("tex", 0, 0, "tex", mMaxFragmentTextures + 1,
                                        mMaxFragmentTextures + 1);
        ASSERT_EQ(0u, mProgram);
    }
    
    // Test active vertex textures under the limit, but excessive textures specified.
    TEST_P(TextureLimitsTest, MaxActiveVertexTextures)
    {
        compileProgramWithTextureCounts("tex", mMaxVertexTextures + 4, mMaxVertexTextures, "tex", 0, 0);
        ASSERT_NE(0u, mProgram);
        ASSERT_GL_NO_ERROR();
    
        testWithTextures(mMaxVertexTextures, "tex", 0, "tex");
    }
    
    // Test active fragment textures under the limit, but excessive textures specified.
    TEST_P(TextureLimitsTest, MaxActiveFragmentTextures)
    {
        compileProgramWithTextureCounts("tex", 0, 0, "tex", mMaxFragmentTextures + 4,
                                        mMaxFragmentTextures);
        ASSERT_NE(0u, mProgram);
        ASSERT_GL_NO_ERROR();
    
        testWithTextures(0, "tex", mMaxFragmentTextures, "tex");
    }
    
    // Negative test for pointing two sampler uniforms of different types to the same texture.
    // GLES 2.0.25 section 2.10.4 page 39.
    TEST_P(TextureLimitsTest, TextureTypeConflict)
    {
        constexpr char kVS[] =
            "attribute vec2 position;\n"
            "varying float color;\n"
            "uniform sampler2D tex2D;\n"
            "uniform samplerCube texCube;\n"
            "void main() {\n"
            "  gl_Position = vec4(position, 0, 1);\n"
            "  vec2 texCoord = (position * 0.5) + 0.5;\n"
            "  color = texture2D(tex2D, texCoord).x;\n"
            "  color += textureCube(texCube, vec3(texCoord, 0)).x;\n"
            "}";
        constexpr char kFS[] =
            "varying mediump float color;\n"
            "void main() {\n"
            "  gl_FragColor = vec4(color, 0, 0, 1);\n"
            "}";
    
        mProgram = CompileProgram(kVS, kFS);
        ASSERT_NE(0u, mProgram);
    
        initTextures(1, 0);
    
        glUseProgram(mProgram);
        GLint tex2DLocation = glGetUniformLocation(mProgram, "tex2D");
        ASSERT_NE(-1, tex2DLocation);
        GLint texCubeLocation = glGetUniformLocation(mProgram, "texCube");
        ASSERT_NE(-1, texCubeLocation);
    
        glUniform1i(tex2DLocation, 0);
        glUniform1i(texCubeLocation, 0);
        ASSERT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
        EXPECT_GL_ERROR(GL_INVALID_OPERATION);
    }
    
    class Texture2DNorm16TestES3 : public Texture2DTestES3
    {
      protected:
        Texture2DNorm16TestES3() : Texture2DTestES3(), mTextures{0, 0, 0}, mFBO(0), mRenderbuffer(0) {}
    
        void SetUp() override
        {
            Texture2DTestES3::SetUp();
    
            glActiveTexture(GL_TEXTURE0);
            glGenTextures(3, mTextures);
            glGenFramebuffers(1, &mFBO);
            glGenRenderbuffers(1, &mRenderbuffer);
    
            for (size_t textureIndex = 0; textureIndex < 3; textureIndex++)
            {
                glBindTexture(GL_TEXTURE_2D, mTextures[textureIndex]);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
            }
    
            glBindTexture(GL_TEXTURE_2D, 0);
    
            ASSERT_GL_NO_ERROR();
        }
    
        void TearDown() override
        {
            glDeleteTextures(3, mTextures);
            glDeleteFramebuffers(1, &mFBO);
            glDeleteRenderbuffers(1, &mRenderbuffer);
    
            Texture2DTestES3::TearDown();
        }
    
        void testNorm16Texture(GLint internalformat, GLenum format, GLenum type)
        {
            GLushort pixelValue  = (type == GL_SHORT) ? 0x7FFF : 0x6A35;
            GLushort imageData[] = {pixelValue, pixelValue, pixelValue, pixelValue};
    
            setUpProgram();
    
            glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0],
                                   0);
    
            glBindTexture(GL_TEXTURE_2D, mTextures[0]);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16_EXT, 1, 1, 0, GL_RGBA, GL_UNSIGNED_SHORT, nullptr);
    
            glBindTexture(GL_TEXTURE_2D, mTextures[1]);
            glTexImage2D(GL_TEXTURE_2D, 0, internalformat, 1, 1, 0, format, type, imageData);
    
            EXPECT_GL_NO_ERROR();
    
            drawQuad(mProgram, "position", 0.5f);
    
            GLubyte expectedValue = (type == GL_SHORT) ? 0xFF : static_cast<GLubyte>(pixelValue >> 8);
    
            EXPECT_PIXEL_COLOR_EQ(0, 0,
                                  SliceFormatColor(format, GLColor(expectedValue, expectedValue,
                                                                   expectedValue, expectedValue)));
    
            glBindFramebuffer(GL_FRAMEBUFFER, 0);
    
            ASSERT_GL_NO_ERROR();
        }
    
        void testNorm16Render(GLint internalformat, GLenum format, GLenum type)
        {
            GLushort pixelValue  = 0x6A35;
            GLushort imageData[] = {pixelValue, pixelValue, pixelValue, pixelValue};
    
            setUpProgram();
    
            glBindTexture(GL_TEXTURE_2D, mTextures[1]);
            glTexImage2D(GL_TEXTURE_2D, 0, internalformat, 1, 1, 0, format, type, nullptr);
    
            glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[1],
                                   0);
    
            glBindTexture(GL_TEXTURE_2D, mTextures[2]);
            glTexImage2D(GL_TEXTURE_2D, 0, internalformat, 1, 1, 0, format, type, imageData);
    
            EXPECT_GL_NO_ERROR();
    
            drawQuad(mProgram, "position", 0.5f);
    
            GLubyte expectedValue = static_cast<GLubyte>(pixelValue >> 8);
            EXPECT_PIXEL_COLOR_EQ(0, 0,
                                  SliceFormatColor(format, GLColor(expectedValue, expectedValue,
                                                                   expectedValue, expectedValue)));
    
            glBindRenderbuffer(GL_RENDERBUFFER, mRenderbuffer);
            glRenderbufferStorage(GL_RENDERBUFFER, internalformat, 1, 1);
            glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
                                      mRenderbuffer);
            glBindRenderbuffer(GL_RENDERBUFFER, 0);
            EXPECT_GL_NO_ERROR();
    
            glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
            glClear(GL_COLOR_BUFFER_BIT);
    
            glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 1, 1);
    
            EXPECT_PIXEL_COLOR_EQ(0, 0, SliceFormatColor(format, GLColor::white));
    
            glBindFramebuffer(GL_FRAMEBUFFER, 0);
    
            ASSERT_GL_NO_ERROR();
        }
    
        GLuint mTextures[3];
        GLuint mFBO;
        GLuint mRenderbuffer;
    };
    
    // Test texture formats enabled by the GL_EXT_texture_norm16 extension.
    TEST_P(Texture2DNorm16TestES3, TextureNorm16Test)
    {
        ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_EXT_texture_norm16"));
    
        testNorm16Texture(GL_R16_EXT, GL_RED, GL_UNSIGNED_SHORT);
        testNorm16Texture(GL_RG16_EXT, GL_RG, GL_UNSIGNED_SHORT);
        testNorm16Texture(GL_RGB16_EXT, GL_RGB, GL_UNSIGNED_SHORT);
        testNorm16Texture(GL_RGBA16_EXT, GL_RGBA, GL_UNSIGNED_SHORT);
        testNorm16Texture(GL_R16_SNORM_EXT, GL_RED, GL_SHORT);
        testNorm16Texture(GL_RG16_SNORM_EXT, GL_RG, GL_SHORT);
        testNorm16Texture(GL_RGB16_SNORM_EXT, GL_RGB, GL_SHORT);
        testNorm16Texture(GL_RGBA16_SNORM_EXT, GL_RGBA, GL_SHORT);
    
        testNorm16Render(GL_R16_EXT, GL_RED, GL_UNSIGNED_SHORT);
        testNorm16Render(GL_RG16_EXT, GL_RG, GL_UNSIGNED_SHORT);
        testNorm16Render(GL_RGBA16_EXT, GL_RGBA, GL_UNSIGNED_SHORT);
    }
    
    // Test that UNPACK_SKIP_IMAGES doesn't have an effect on 2D texture uploads.
    // GLES 3.0.4 section 3.8.3.
    TEST_P(Texture2DTestES3, UnpackSkipImages2D)
    {
        // TODO(ynovikov): re-enable once root cause of http://anglebug.com/1429 is fixed
        ANGLE_SKIP_TEST_IF(IsAndroid() && IsAdreno() && IsOpenGLES());
    
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        ASSERT_GL_NO_ERROR();
    
        // SKIP_IMAGES should not have an effect on uploading 2D textures
        glPixelStorei(GL_UNPACK_SKIP_IMAGES, 1000);
        ASSERT_GL_NO_ERROR();
    
        std::vector<GLColor> pixelsGreen(128u * 128u, GLColor::green);
    
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                     pixelsGreen.data());
        ASSERT_GL_NO_ERROR();
    
        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 128, 128, GL_RGBA, GL_UNSIGNED_BYTE,
                        pixelsGreen.data());
        ASSERT_GL_NO_ERROR();
    
        glUseProgram(mProgram);
        drawQuad(mProgram, "position", 0.5f);
        ASSERT_GL_NO_ERROR();
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    }
    
    // Test that skip defined in unpack parameters is taken into account when determining whether
    // unpacking source extends outside unpack buffer bounds.
    TEST_P(Texture2DTestES3, UnpackSkipPixelsOutOfBounds)
    {
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        ASSERT_GL_NO_ERROR();
    
        GLBuffer buf;
        glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buf.get());
        std::vector<GLColor> pixelsGreen(128u * 128u, GLColor::green);
        glBufferData(GL_PIXEL_UNPACK_BUFFER, pixelsGreen.size() * 4u, pixelsGreen.data(),
                     GL_DYNAMIC_COPY);
        ASSERT_GL_NO_ERROR();
    
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
        ASSERT_GL_NO_ERROR();
    
        glPixelStorei(GL_UNPACK_SKIP_PIXELS, 1);
        ASSERT_GL_NO_ERROR();
    
        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 128, 128, GL_RGBA, GL_UNSIGNED_BYTE, 0);
        EXPECT_GL_ERROR(GL_INVALID_OPERATION);
    
        glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
        glPixelStorei(GL_UNPACK_SKIP_ROWS, 1);
        ASSERT_GL_NO_ERROR();
    
        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 128, 128, GL_RGBA, GL_UNSIGNED_BYTE, 0);
        EXPECT_GL_ERROR(GL_INVALID_OPERATION);
    }
    
    // Test that unpacking rows that overlap in a pixel unpack buffer works as expected.
    TEST_P(Texture2DTestES3, UnpackOverlappingRowsFromUnpackBuffer)
    {
        ANGLE_SKIP_TEST_IF(IsD3D11());
    
        // Incorrect rendering results seen on OSX AMD.
        ANGLE_SKIP_TEST_IF(IsOSX() && IsAMD());
    
        const GLuint width            = 8u;
        const GLuint height           = 8u;
        const GLuint unpackRowLength  = 5u;
        const GLuint unpackSkipPixels = 1u;
    
        setWindowWidth(width);
        setWindowHeight(height);
    
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        ASSERT_GL_NO_ERROR();
    
        GLBuffer buf;
        glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buf.get());
        std::vector<GLColor> pixelsGreen((height - 1u) * unpackRowLength + width + unpackSkipPixels,
                                         GLColor::green);
    
        for (GLuint skippedPixel = 0u; skippedPixel < unpackSkipPixels; ++skippedPixel)
        {
            pixelsGreen[skippedPixel] = GLColor(255, 0, 0, 255);
        }
    
        glBufferData(GL_PIXEL_UNPACK_BUFFER, pixelsGreen.size() * 4u, pixelsGreen.data(),
                     GL_DYNAMIC_COPY);
        ASSERT_GL_NO_ERROR();
    
        glPixelStorei(GL_UNPACK_ROW_LENGTH, unpackRowLength);
        glPixelStorei(GL_UNPACK_SKIP_PIXELS, unpackSkipPixels);
        ASSERT_GL_NO_ERROR();
    
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
        ASSERT_GL_NO_ERROR();
    
        glUseProgram(mProgram);
        drawQuad(mProgram, "position", 0.5f);
        ASSERT_GL_NO_ERROR();
    
        GLuint windowPixelCount = getWindowWidth() * getWindowHeight();
        std::vector<GLColor> actual(windowPixelCount, GLColor::black);
        glReadPixels(0, 0, getWindowWidth(), getWindowHeight(), GL_RGBA, GL_UNSIGNED_BYTE,
                     actual.data());
        std::vector<GLColor> expected(windowPixelCount, GLColor::green);
        EXPECT_EQ(expected, actual);
    }
    
    template <typename T>
    T UNorm(double value)
    {
        return static_cast<T>(value * static_cast<double>(std::numeric_limits<T>::max()));
    }
    
    // Test rendering a depth texture with mipmaps.
    TEST_P(Texture2DTestES3, DepthTexturesWithMipmaps)
    {
        // TODO(cwallez) this is failing on Intel Win7 OpenGL.
        // TODO(zmo) this is faling on Win Intel HD 530 Debug.
        // http://anglebug.com/1706
        ANGLE_SKIP_TEST_IF(IsIntel() && IsWindows() && IsOpenGL());
    
        const int size = getWindowWidth();
    
        auto dim   = [size](int level) { return size >> level; };
        int levels = gl::log2(size);
    
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
        glTexStorage2D(GL_TEXTURE_2D, levels, GL_DEPTH_COMPONENT24, size, size);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
        ASSERT_GL_NO_ERROR();
    
        glUseProgram(mProgram);
        glUniform1i(mTexture2DUniformLocation, 0);
    
        std::vector<unsigned char> expected;
    
        for (int level = 0; level < levels; ++level)
        {
            double value = (static_cast<double>(level) / static_cast<double>(levels - 1));
            expected.push_back(UNorm<unsigned char>(value));
    
            int levelDim = dim(level);
    
            ASSERT_GT(levelDim, 0);
    
            std::vector<unsigned int> initData(levelDim * levelDim, UNorm<unsigned int>(value));
            glTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, levelDim, levelDim, GL_DEPTH_COMPONENT,
                            GL_UNSIGNED_INT, initData.data());
        }
        ASSERT_GL_NO_ERROR();
    
        for (int level = 0; level < levels; ++level)
        {
            glViewport(0, 0, dim(level), dim(level));
            drawQuad(mProgram, "position", 0.5f);
            GLColor actual = ReadColor(0, 0);
            EXPECT_NEAR(expected[level], actual.R, 10u);
        }
    
        ASSERT_GL_NO_ERROR();
    }
    
    // Tests unpacking into the unsized GL_ALPHA format.
    TEST_P(Texture2DTestES3, UnsizedAlphaUnpackBuffer)
    {
        // Initialize the texure.
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, getWindowWidth(), getWindowHeight(), 0, GL_ALPHA,
                     GL_UNSIGNED_BYTE, nullptr);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    
        std::vector<GLubyte> bufferData(getWindowWidth() * getWindowHeight(), 127);
    
        // Pull in the color data from the unpack buffer.
        GLBuffer unpackBuffer;
        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
        glBindBuffer(GL_PIXEL_UNPACK_BUFFER, unpackBuffer.get());
        glBufferData(GL_PIXEL_UNPACK_BUFFER, getWindowWidth() * getWindowHeight(), bufferData.data(),
                     GL_STATIC_DRAW);
    
        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, getWindowWidth(), getWindowHeight(), GL_ALPHA,
                        GL_UNSIGNED_BYTE, nullptr);
    
        // Clear to a weird color to make sure we're drawing something.
        glClearColor(0.5f, 0.8f, 1.0f, 0.2f);
        glClear(GL_COLOR_BUFFER_BIT);
    
        // Draw with the alpha texture and verify.
        drawQuad(mProgram, "position", 0.5f);
    
        ASSERT_GL_NO_ERROR();
        EXPECT_PIXEL_NEAR(0, 0, 0, 0, 0, 127, 1);
    }
    
    // Ensure stale unpack data doesn't propagate in D3D11.
    TEST_P(Texture2DTestES3, StaleUnpackData)
    {
        // Init unpack buffer.
        GLsizei pixelCount = getWindowWidth() * getWindowHeight() / 2;
        std::vector<GLColor> pixels(pixelCount, GLColor::red);
    
        GLBuffer unpackBuffer;
        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
        glBindBuffer(GL_PIXEL_UNPACK_BUFFER, unpackBuffer.get());
        GLsizei bufferSize = pixelCount * sizeof(GLColor);
        glBufferData(GL_PIXEL_UNPACK_BUFFER, bufferSize, pixels.data(), GL_STATIC_DRAW);
    
        // Create from unpack buffer.
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, getWindowWidth() / 2, getWindowHeight() / 2, 0,
                     GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    
        drawQuad(mProgram, "position", 0.5f);
    
        ASSERT_GL_NO_ERROR();
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
    
        // Fill unpack with green, recreating buffer.
        pixels.assign(getWindowWidth() * getWindowHeight(), GLColor::green);
        GLsizei size2 = getWindowWidth() * getWindowHeight() * sizeof(GLColor);
        glBufferData(GL_PIXEL_UNPACK_BUFFER, size2, pixels.data(), GL_STATIC_DRAW);
    
        // Reinit texture with green.
        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, getWindowWidth() / 2, getWindowHeight() / 2, GL_RGBA,
                        GL_UNSIGNED_BYTE, nullptr);
    
        drawQuad(mProgram, "position", 0.5f);
    
        ASSERT_GL_NO_ERROR();
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    }
    
    // Ensure that texture parameters passed as floats that are converted to ints are rounded before
    // validating they are less than 0.
    TEST_P(Texture2DTestES3, TextureBaseMaxLevelRoundingValidation)
    {
        GLTexture texture;
        glBindTexture(GL_TEXTURE_2D, texture);
    
        // Use a negative number that will round to zero when converted to an integer
        // According to the spec(2.3.1 Data Conversion For State - Setting Commands):
        // "Validation of values performed by state-setting commands is performed after conversion,
        // unless specified otherwise for a specific command."
        GLfloat param = -7.30157126e-07f;
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, param);
        EXPECT_GL_NO_ERROR();
    
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, param);
        EXPECT_GL_NO_ERROR();
    }
    
    // This test covers a D3D format redefinition bug for 3D textures. The base level format was not
    // being properly checked, and the texture storage of the previous texture format was persisting.
    // This would result in an ASSERT in debug and incorrect rendering in release.
    // See http://anglebug.com/1609 and WebGL 2 test conformance2/misc/views-with-offsets.html.
    TEST_P(Texture3DTestES3, FormatRedefinitionBug)
    {
        GLTexture tex;
        glBindTexture(GL_TEXTURE_3D, tex.get());
        glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA8, 1, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        GLFramebuffer framebuffer;
        glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
        glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex.get(), 0, 0);
    
        glCheckFramebufferStatus(GL_FRAMEBUFFER);
    
        std::vector<uint8_t> pixelData(100, 0);
    
        glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB565, 1, 1, 1, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, nullptr);
        glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, 1, 1, 1, GL_RGB, GL_UNSIGNED_SHORT_5_6_5,
                        pixelData.data());
    
        ASSERT_GL_NO_ERROR();
    }
    
    // Test basic pixel unpack buffer OOB checks when uploading to a 2D or 3D texture
    TEST_P(Texture3DTestES3, BasicUnpackBufferOOB)
    {
        // 2D tests
        {
            GLTexture tex;
            glBindTexture(GL_TEXTURE_2D, tex.get());
    
            GLBuffer pbo;
            glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo.get());
    
            // Test OOB
            glBufferData(GL_PIXEL_UNPACK_BUFFER, sizeof(GLColor) * 2 * 2 - 1, nullptr, GL_STATIC_DRAW);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
            ASSERT_GL_ERROR(GL_INVALID_OPERATION);
    
            // Test OOB
            glBufferData(GL_PIXEL_UNPACK_BUFFER, sizeof(GLColor) * 2 * 2, nullptr, GL_STATIC_DRAW);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
            ASSERT_GL_NO_ERROR();
        }
    
        // 3D tests
        {
            GLTexture tex;
            glBindTexture(GL_TEXTURE_3D, tex.get());
    
            GLBuffer pbo;
            glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo.get());
    
            // Test OOB
            glBufferData(GL_PIXEL_UNPACK_BUFFER, sizeof(GLColor) * 2 * 2 * 2 - 1, nullptr,
                         GL_STATIC_DRAW);
            glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA8, 2, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
            ASSERT_GL_ERROR(GL_INVALID_OPERATION);
    
            // Test OOB
            glBufferData(GL_PIXEL_UNPACK_BUFFER, sizeof(GLColor) * 2 * 2 * 2, nullptr, GL_STATIC_DRAW);
            glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA8, 2, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
            ASSERT_GL_NO_ERROR();
        }
    }
    
    // Tests behaviour with a single texture and multiple sampler objects.
    TEST_P(Texture2DTestES3, SingleTextureMultipleSamplers)
    {
        GLint maxTextureUnits = 0;
        glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
        ANGLE_SKIP_TEST_IF(maxTextureUnits < 4);
    
        constexpr int kSize = 16;
    
        // Make a single-level texture, fill it with red.
        std::vector<GLColor> redColors(kSize * kSize, GLColor::red);
        GLTexture tex;
        glBindTexture(GL_TEXTURE_2D, tex);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                     redColors.data());
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    
        // Simple sanity check.
        draw2DTexturedQuad(0.5f, 1.0f, true);
        ASSERT_GL_NO_ERROR();
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
    
        // Bind texture to unit 1 with a sampler object making it incomplete.
        GLSampler sampler;
        glBindSampler(0, sampler);
        glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
        glSamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    
        // Make a mipmap texture, fill it with blue.
        std::vector<GLColor> blueColors(kSize * kSize, GLColor::blue);
        GLTexture mipmapTex;
        glBindTexture(GL_TEXTURE_2D, mipmapTex);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                     blueColors.data());
        glGenerateMipmap(GL_TEXTURE_2D);
    
        // Draw with the sampler, expect blue.
        draw2DTexturedQuad(0.5f, 1.0f, true);
        ASSERT_GL_NO_ERROR();
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
    
        // Simple multitexturing program.
        constexpr char kVS[] =
            "#version 300 es\n"
            "in vec2 position;\n"
            "out vec2 texCoord;\n"
            "void main()\n"
            "{\n"
            "    gl_Position = vec4(position, 0, 1);\n"
            "    texCoord = position * 0.5 + vec2(0.5);\n"
            "}";
    
        constexpr char kFS[] =
            "#version 300 es\n"
            "precision mediump float;\n"
            "in vec2 texCoord;\n"
            "uniform sampler2D tex1;\n"
            "uniform sampler2D tex2;\n"
            "uniform sampler2D tex3;\n"
            "uniform sampler2D tex4;\n"
            "out vec4 color;\n"
            "void main()\n"
            "{\n"
            "    color = (texture(tex1, texCoord) + texture(tex2, texCoord) \n"
            "          +  texture(tex3, texCoord) + texture(tex4, texCoord)) * 0.25;\n"
            "}";
    
        ANGLE_GL_PROGRAM(program, kVS, kFS);
    
        std::array<GLint, 4> texLocations = {
            {glGetUniformLocation(program, "tex1"), glGetUniformLocation(program, "tex2"),
             glGetUniformLocation(program, "tex3"), glGetUniformLocation(program, "tex4")}};
        for (GLint location : texLocations)
        {
            ASSERT_NE(-1, location);
        }
    
        // Init the uniform data.
        glUseProgram(program);
        for (GLint location = 0; location < 4; ++location)
        {
            glUniform1i(texLocations[location], location);
        }
    
        // Initialize four samplers
        GLSampler samplers[4];
    
        // 0: non-mipped.
        glBindSampler(0, samplers[0]);
        glSamplerParameteri(samplers[0], GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glSamplerParameteri(samplers[0], GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    
        // 1: mipped.
        glBindSampler(1, samplers[1]);
        glSamplerParameteri(samplers[1], GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
        glSamplerParameteri(samplers[1], GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    
        // 2: non-mipped.
        glBindSampler(2, samplers[2]);
        glSamplerParameteri(samplers[2], GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glSamplerParameteri(samplers[2], GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    
        // 3: mipped.
        glBindSampler(3, samplers[3]);
        glSamplerParameteri(samplers[3], GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
        glSamplerParameteri(samplers[3], GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    
        // Bind two blue mipped textures and two single layer textures, should all draw.
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, tex);
    
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, mipmapTex);
    
        glActiveTexture(GL_TEXTURE2);
        glBindTexture(GL_TEXTURE_2D, tex);
    
        glActiveTexture(GL_TEXTURE3);
        glBindTexture(GL_TEXTURE_2D, mipmapTex);
    
        ASSERT_GL_NO_ERROR();
    
        drawQuad(program, "position", 0.5f);
        ASSERT_GL_NO_ERROR();
        EXPECT_PIXEL_NEAR(0, 0, 128, 0, 128, 255, 2);
    
        // Bind four single layer textures, two should be incomplete.
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, tex);
    
        glActiveTexture(GL_TEXTURE3);
        glBindTexture(GL_TEXTURE_2D, tex);
    
        drawQuad(program, "position", 0.5f);
        ASSERT_GL_NO_ERROR();
        EXPECT_PIXEL_NEAR(0, 0, 128, 0, 0, 255, 2);
    }
    
    // The test is added to cover http://anglebug.com/2153. Cubemap completeness checks used to start
    // always at level 0 instead of the base level resulting in an incomplete texture if the faces at
    // level 0 are not created. The test creates a cubemap texture, specifies the images only for mip
    // level 1 filled with white color, updates the base level to be 1 and renders a quad. The program
    // samples the cubemap using a direction vector (1,1,1).
    TEST_P(TextureCubeTestES3, SpecifyAndSampleFromBaseLevel1)
    {
        // Check http://anglebug.com/2155.
        ANGLE_SKIP_TEST_IF(IsOSX() && IsNVIDIA());
    
        constexpr char kVS[] =
            R"(#version 300 es
            precision mediump float;
            in vec3 pos;
            void main() {
                gl_Position = vec4(pos, 1.0);
            })";
    
        constexpr char kFS[] =
            R"(#version 300 es
            precision mediump float;
            out vec4 color;
            uniform samplerCube uTex;
            void main(){
                color = texture(uTex, vec3(1.0));
            })";
    
        ANGLE_GL_PROGRAM(program, kVS, kFS);
        glUseProgram(program);
    
        glUniform1i(glGetUniformLocation(program, "uTex"), 0);
        glActiveTexture(GL_TEXTURE0);
    
        GLTexture cubeTex;
        glBindTexture(GL_TEXTURE_CUBE_MAP, cubeTex);
    
        const int kFaceWidth  = 1;
        const int kFaceHeight = 1;
        std::vector<uint32_t> texData(kFaceWidth * kFaceHeight, 0xFFFFFFFF);
        glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 1, GL_RGBA8, kFaceWidth, kFaceHeight, 0, GL_RGBA,
                     GL_UNSIGNED_BYTE, texData.data());
        glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 1, GL_RGBA8, kFaceWidth, kFaceHeight, 0, GL_RGBA,
                     GL_UNSIGNED_BYTE, texData.data());
        glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 1, GL_RGBA8, kFaceWidth, kFaceHeight, 0, GL_RGBA,
                     GL_UNSIGNED_BYTE, texData.data());
        glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 1, GL_RGBA8, kFaceWidth, kFaceHeight, 0, GL_RGBA,
                     GL_UNSIGNED_BYTE, texData.data());
        glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 1, GL_RGBA8, kFaceWidth, kFaceHeight, 0, GL_RGBA,
                     GL_UNSIGNED_BYTE, texData.data());
        glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 1, GL_RGBA8, kFaceWidth, kFaceHeight, 0, GL_RGBA,
                     GL_UNSIGNED_BYTE, texData.data());
        glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BASE_LEVEL, 1);
    
        drawQuad(program, "pos", 0.5f, 1.0f, true);
        ASSERT_GL_NO_ERROR();
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, angle::GLColor::white);
    }
    
    // Verify that using negative texture base level and max level generates GL_INVALID_VALUE.
    TEST_P(Texture2DTestES3, NegativeTextureBaseLevelAndMaxLevel)
    {
        GLuint texture = create2DTexture();
    
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, -1);
        EXPECT_GL_ERROR(GL_INVALID_VALUE);
    
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, -1);
        EXPECT_GL_ERROR(GL_INVALID_VALUE);
    
        glDeleteTextures(1, &texture);
        EXPECT_GL_NO_ERROR();
    }
    
    // Test setting base level after calling generateMipmap on a LUMA texture.
    // Covers http://anglebug.com/2498
    TEST_P(Texture2DTestES3, GenerateMipmapAndBaseLevelLUMA)
    {
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
    
        constexpr const GLsizei kWidth  = 8;
        constexpr const GLsizei kHeight = 8;
        std::array<GLubyte, kWidth * kHeight * 2> whiteData;
        whiteData.fill(255u);
    
        glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, kWidth, kHeight, 0, GL_LUMINANCE_ALPHA,
                     GL_UNSIGNED_BYTE, whiteData.data());
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glGenerateMipmap(GL_TEXTURE_2D);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 1);
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, "position", 0.5f);
        EXPECT_PIXEL_COLOR_EQ(0, 0, angle::GLColor::white);
    }
    
    // Covers a bug in the D3D11 backend: http://anglebug.com/2772
    // When using a sampler the texture was created as if it has mipmaps,
    // regardless what you specified in GL_TEXTURE_MIN_FILTER via
    // glSamplerParameteri() -- mistakenly the default value
    // GL_NEAREST_MIPMAP_LINEAR or the value set via glTexParameteri() was
    // evaluated.
    // If you didn't provide mipmaps and didn't let the driver generate them
    // this led to not sampling your texture data when minification occurred.
    TEST_P(Texture2DTestES3, MinificationWithSamplerNoMipmapping)
    {
        constexpr char kVS[] =
            "#version 300 es\n"
            "out vec2 texcoord;\n"
            "in vec4 position;\n"
            "void main()\n"
            "{\n"
            "    gl_Position = vec4(position.xy * 0.1, 0.0, 1.0);\n"
            "    texcoord = (position.xy * 0.5) + 0.5;\n"
            "}\n";
    
        constexpr char kFS[] =
            "#version 300 es\n"
            "precision highp float;\n"
            "uniform highp sampler2D tex;\n"
            "in vec2 texcoord;\n"
            "out vec4 fragColor;\n"
            "void main()\n"
            "{\n"
            "    fragColor = texture(tex, texcoord);\n"
            "}\n";
    
        ANGLE_GL_PROGRAM(program, kVS, kFS);
    
        GLSampler sampler;
        glBindSampler(0, sampler);
        glSamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mTexture2D);
    
        const GLsizei texWidth  = getWindowWidth();
        const GLsizei texHeight = getWindowHeight();
        const std::vector<GLColor> whiteData(texWidth * texHeight, GLColor::white);
    
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                     whiteData.data());
        EXPECT_GL_NO_ERROR();
    
        drawQuad(program, "position", 0.5f);
        EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, angle::GLColor::white);
    }
    
    // Use this to select which configurations (e.g. which renderer, which GLES major version) these
    // tests should be run against.
    ANGLE_INSTANTIATE_TEST(Texture2DTest,
                           ES2_D3D9(),
                           ES2_D3D11(),
                           ES2_D3D11_FL9_3(),
                           ES2_OPENGL(),
                           ES2_OPENGLES(),
                           ES2_VULKAN());
    ANGLE_INSTANTIATE_TEST(TextureCubeTest,
                           ES2_D3D9(),
                           ES2_D3D11(),
                           ES2_D3D11_FL10_0(),
                           ES2_D3D11_FL9_3(),
                           ES2_OPENGL(),
                           ES2_OPENGLES(),
                           ES2_VULKAN());
    ANGLE_INSTANTIATE_TEST(Texture2DTestWithDrawScale,
                           ES2_D3D9(),
                           ES2_D3D11(),
                           ES2_D3D11_FL9_3(),
                           ES2_OPENGL(),
                           ES2_OPENGLES(),
                           ES2_VULKAN());
    ANGLE_INSTANTIATE_TEST(Sampler2DAsFunctionParameterTest,
                           ES2_D3D9(),
                           ES2_D3D11(),
                           ES2_D3D11_FL9_3(),
                           ES2_OPENGL(),
                           ES2_OPENGLES(),
                           ES2_VULKAN());
    ANGLE_INSTANTIATE_TEST(SamplerArrayTest,
                           ES2_D3D9(),
                           ES2_D3D11(),
                           ES2_D3D11_FL9_3(),
                           ES2_OPENGL(),
                           ES2_OPENGLES(),
                           ES2_VULKAN());
    ANGLE_INSTANTIATE_TEST(SamplerArrayAsFunctionParameterTest,
                           ES2_D3D9(),
                           ES2_D3D11(),
                           ES2_D3D11_FL9_3(),
                           ES2_OPENGL(),
                           ES2_OPENGLES(),
                           ES2_VULKAN());
    ANGLE_INSTANTIATE_TEST(Texture2DTestES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
    ANGLE_INSTANTIATE_TEST(Texture3DTestES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
    ANGLE_INSTANTIATE_TEST(Texture2DIntegerAlpha1TestES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
    ANGLE_INSTANTIATE_TEST(Texture2DUnsignedIntegerAlpha1TestES3,
                           ES3_D3D11(),
                           ES3_OPENGL(),
                           ES3_OPENGLES());
    ANGLE_INSTANTIATE_TEST(ShadowSamplerPlusSampler3DTestES3,
                           ES3_D3D11(),
                           ES3_OPENGL(),
                           ES3_OPENGLES());
    ANGLE_INSTANTIATE_TEST(SamplerTypeMixTestES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
    ANGLE_INSTANTIATE_TEST(Texture2DArrayTestES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
    ANGLE_INSTANTIATE_TEST(TextureSizeTextureArrayTest, ES3_D3D11(), ES3_OPENGL());
    ANGLE_INSTANTIATE_TEST(SamplerInStructTest,
                           ES2_D3D11(),
                           ES2_D3D11_FL9_3(),
                           ES2_D3D9(),
                           ES2_OPENGL(),
                           ES2_OPENGLES(),
                           ES2_VULKAN());
    ANGLE_INSTANTIATE_TEST(SamplerInStructAsFunctionParameterTest,
                           ES2_D3D11(),
                           ES2_D3D11_FL9_3(),
                           ES2_D3D9(),
                           ES2_OPENGL(),
                           ES2_OPENGLES(),
                           ES2_VULKAN());
    ANGLE_INSTANTIATE_TEST(SamplerInStructArrayAsFunctionParameterTest,
                           ES2_D3D11(),
                           ES2_D3D11_FL9_3(),
                           ES2_D3D9(),
                           ES2_OPENGL(),
                           ES2_OPENGLES(),
                           ES2_VULKAN());
    ANGLE_INSTANTIATE_TEST(SamplerInNestedStructAsFunctionParameterTest,
                           ES2_D3D11(),
                           ES2_D3D11_FL9_3(),
                           ES2_D3D9(),
                           ES2_OPENGL(),
                           ES2_OPENGLES(),
                           ES2_VULKAN());
    ANGLE_INSTANTIATE_TEST(SamplerInStructAndOtherVariableTest,
                           ES2_D3D11(),
                           ES2_D3D11_FL9_3(),
                           ES2_D3D9(),
                           ES2_OPENGL(),
                           ES2_OPENGLES(),
                           ES2_VULKAN());
    ANGLE_INSTANTIATE_TEST(TextureBorderClampTest,
                           ES2_D3D11(),
                           ES2_D3D9(),
                           ES2_OPENGL(),
                           ES2_OPENGLES(),
                           ES2_VULKAN());
    ANGLE_INSTANTIATE_TEST(TextureBorderClampTestES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
    ANGLE_INSTANTIATE_TEST(TextureBorderClampIntegerTestES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
    ANGLE_INSTANTIATE_TEST(TextureLimitsTest, ES2_D3D11(), ES2_OPENGL(), ES2_OPENGLES(), ES2_VULKAN());
    ANGLE_INSTANTIATE_TEST(Texture2DNorm16TestES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
    ANGLE_INSTANTIATE_TEST(TextureCubeTestES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
    
    }  // anonymous namespace