Edit

kc3-lang/angle/src/tests/gl_tests/CopyTextureTest.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/CopyTextureTest.cpp
  • //
    // Copyright 2016 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.
    //
    
    // CopyTextureTest.cpp: Tests of the GL_CHROMIUM_copy_texture extension
    
    #include "test_utils/ANGLETest.h"
    
    #include "test_utils/gl_raii.h"
    
    namespace angle
    {
    
    class CopyTextureTest : public ANGLETest
    {
      protected:
        CopyTextureTest()
        {
            setWindowWidth(256);
            setWindowHeight(256);
            setConfigRedBits(8);
            setConfigGreenBits(8);
            setConfigBlueBits(8);
            setConfigAlphaBits(8);
        }
    
        void SetUp() override
        {
            ANGLETest::SetUp();
    
            glGenTextures(2, mTextures);
            glBindTexture(GL_TEXTURE_2D, mTextures[1]);
    
            glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
            glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    
            glGenFramebuffers(1, &mFramebuffer);
            glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[1],
                                   0);
    
            if (extensionEnabled("GL_CHROMIUM_copy_texture"))
            {
                glCopyTextureCHROMIUM = reinterpret_cast<PFNGLCOPYTEXTURECHROMIUMPROC>(
                    eglGetProcAddress("glCopyTextureCHROMIUM"));
                glCopySubTextureCHROMIUM = reinterpret_cast<PFNGLCOPYSUBTEXTURECHROMIUMPROC>(
                    eglGetProcAddress("glCopySubTextureCHROMIUM"));
            }
        }
    
        void TearDown() override
        {
            glDeleteTextures(2, mTextures);
            glDeleteFramebuffers(1, &mFramebuffer);
    
            ANGLETest::TearDown();
        }
    
        bool checkExtensions() const
        {
            if (!extensionEnabled("GL_CHROMIUM_copy_texture"))
            {
                std::cout << "Test skipped because GL_CHROMIUM_copy_texture is not available."
                          << std::endl;
                return false;
            }
    
            EXPECT_NE(nullptr, glCopyTextureCHROMIUM);
            EXPECT_NE(nullptr, glCopySubTextureCHROMIUM);
            return true;
        }
    
        void calculateCopyTextureResults(GLenum sourceFormat,
                                         GLenum destFormat,
                                         bool premultiplyAlpha,
                                         bool unmultiplyAlpha,
                                         const uint8_t *sourceColor,
                                         GLColor *destColor)
        {
            GLColor color;
    
            switch (sourceFormat)
            {
                case GL_RGB:
                    color = GLColor(sourceColor[0], sourceColor[1], sourceColor[2], 255);
                    break;
                case GL_RGBA:
                    color = GLColor(sourceColor[0], sourceColor[1], sourceColor[2], sourceColor[3]);
                    break;
                case GL_LUMINANCE:
                    color = GLColor(sourceColor[0], sourceColor[0], sourceColor[0], 255);
                    break;
                case GL_ALPHA:
                    color = GLColor(0, 0, 0, sourceColor[0]);
                    break;
                case GL_LUMINANCE_ALPHA:
                    color = GLColor(sourceColor[0], sourceColor[0], sourceColor[0], sourceColor[1]);
                    break;
                case GL_BGRA_EXT:
                    color = GLColor(sourceColor[2], sourceColor[1], sourceColor[0], sourceColor[3]);
                    break;
                default:
                    EXPECT_EQ(true, false);
            }
    
            if (premultiplyAlpha != unmultiplyAlpha)
            {
                float alpha = color.A / 255.0f;
                if (premultiplyAlpha)
                {
                    color.R = static_cast<GLubyte>(static_cast<float>(color.R) * alpha);
                    color.G = static_cast<GLubyte>(static_cast<float>(color.G) * alpha);
                    color.B = static_cast<GLubyte>(static_cast<float>(color.B) * alpha);
                }
                else if (unmultiplyAlpha && color.A != 0)
                {
                    color.R = static_cast<GLubyte>(static_cast<float>(color.R) / alpha);
                    color.G = static_cast<GLubyte>(static_cast<float>(color.G) / alpha);
                    color.B = static_cast<GLubyte>(static_cast<float>(color.B) / alpha);
                }
            }
    
            switch (destFormat)
            {
                case GL_RGB:
                    color.A = 255;
                    break;
                case GL_RGBA:
                case GL_BGRA_EXT:
                    break;
                default:
                    EXPECT_EQ(true, false);
            }
    
            *destColor = color;
        }
    
        const uint8_t *getSourceColors(GLenum sourceFormat, size_t *colorCount, uint8_t *componentCount)
        {
            // Note: in all the following values, alpha is larger than RGB so unmultiply alpha doesn't
            // overflow
            constexpr static uint8_t kRgbaColors[7 * 4] = {
                255u, 127u, 63u,  255u,  // 0
                31u,  127u, 63u,  127u,  // 1
                31u,  63u,  127u, 255u,  // 2
                15u,  127u, 31u,  127u,  // 3
                127u, 255u, 63u,  0u,    // 4
                31u,  63u,  127u, 0u,    // 5
                15u,  31u,  63u,  63u,   // 6
            };
    
            constexpr static uint8_t kRgbColors[7 * 3] = {
                255u, 127u, 63u,   // 0
                31u,  127u, 63u,   // 1
                31u,  63u,  127u,  // 2
                15u,  127u, 31u,   // 3
                127u, 255u, 63u,   // 4
                31u,  63u,  127u,  // 5
                15u,  31u,  63u,   // 6
            };
    
            constexpr static uint8_t kLumColors[7 * 1] = {
                255u,  // 0
                163u,  // 1
                78u,   // 2
                114u,  // 3
                51u,   // 4
                0u,    // 5
                217u,  // 6
            };
    
            constexpr static uint8_t kLumaColors[7 * 2] = {
                255u, 255u,  // 0
                67u,  163u,  // 1
                78u,  231u,  // 2
                8u,   114u,  // 3
                51u,  199u,  // 4
                0u,   173u,  // 5
                34u,  217u,  // 6
            };
    
            constexpr static uint8_t kAlphaColors[7 * 1] = {
                255u,  // 0
                67u,   // 1
                231u,  // 2
                8u,    // 3
                199u,  // 4
                173u,  // 5
                34u,   // 6
            };
    
            *colorCount = 7;
    
            switch (sourceFormat)
            {
                case GL_RGB:
                    *componentCount = 3;
                    return kRgbColors;
                case GL_RGBA:
                case GL_BGRA_EXT:
                    *componentCount = 4;
                    return kRgbaColors;
                case GL_LUMINANCE:
                    *componentCount = 1;
                    return kLumColors;
                case GL_ALPHA:
                    *componentCount = 1;
                    return kAlphaColors;
                case GL_LUMINANCE_ALPHA:
                    *componentCount = 2;
                    return kLumaColors;
                default:
                    EXPECT_EQ(true, false);
                    return nullptr;
            }
        }
    
        void initializeSourceTexture(GLenum sourceFormat,
                                     const uint8_t *srcColors,
                                     uint8_t componentCount)
        {
            // The texture is initialized as 2x2.  If the componentCount is 1 or 3, then the input data
            // will have a row pitch of 2 or 6, which needs to be padded to 4 or 8 respectively.
            uint8_t srcColorsPadded[4 * 4];
            size_t srcRowPitch =
                2 * componentCount + (componentCount == 1 || componentCount == 3 ? 2 : 0);
            size_t inputRowPitch = 2 * componentCount;
            for (size_t row = 0; row < 2; ++row)
            {
                memcpy(&srcColorsPadded[row * srcRowPitch], &srcColors[row * inputRowPitch],
                       inputRowPitch);
                memset(&srcColorsPadded[row * srcRowPitch + inputRowPitch], 0,
                       srcRowPitch - inputRowPitch);
            }
    
            glBindTexture(GL_TEXTURE_2D, mTextures[0]);
            glTexImage2D(GL_TEXTURE_2D, 0, sourceFormat, 2, 2, 0, sourceFormat, GL_UNSIGNED_BYTE,
                         srcColorsPadded);
        }
    
        void testCopyTexture(GLenum sourceFormat,
                             GLenum destFormat,
                             bool flipY,
                             bool premultiplyAlpha,
                             bool unmultiplyAlpha)
        {
            if (!checkExtensions())
            {
                return;
            }
    
            size_t colorCount;
            uint8_t componentCount;
            const uint8_t *srcColors = getSourceColors(sourceFormat, &colorCount, &componentCount);
    
            std::vector<GLColor> destColors(colorCount);
            for (size_t i = 0; i < colorCount; ++i)
            {
                calculateCopyTextureResults(sourceFormat, destFormat, premultiplyAlpha, unmultiplyAlpha,
                                            &srcColors[i * componentCount], &destColors[i]);
            }
    
            for (size_t i = 0; i < colorCount - 3; ++i)
            {
                initializeSourceTexture(sourceFormat, &srcColors[i * componentCount], componentCount);
    
                glCopyTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, destFormat,
                                      GL_UNSIGNED_BYTE, flipY, premultiplyAlpha, unmultiplyAlpha);
    
                EXPECT_GL_NO_ERROR();
    
                // Check that FB is complete.
                EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
    
                if (flipY)
                {
                    EXPECT_PIXEL_COLOR_NEAR(0, 0, destColors[i + 2], 1.0);
                    EXPECT_PIXEL_COLOR_NEAR(1, 0, destColors[i + 3], 1.0);
                    EXPECT_PIXEL_COLOR_NEAR(0, 1, destColors[i + 0], 1.0);
                    EXPECT_PIXEL_COLOR_NEAR(1, 1, destColors[i + 1], 1.0);
                }
                else
                {
                    EXPECT_PIXEL_COLOR_NEAR(0, 0, destColors[i + 0], 1.0);
                    EXPECT_PIXEL_COLOR_NEAR(1, 0, destColors[i + 1], 1.0);
                    EXPECT_PIXEL_COLOR_NEAR(0, 1, destColors[i + 2], 1.0);
                    EXPECT_PIXEL_COLOR_NEAR(1, 1, destColors[i + 3], 1.0);
                }
    
                EXPECT_GL_NO_ERROR();
            }
        }
    
        void testCopySubTexture(GLenum sourceFormat,
                                GLenum destFormat,
                                bool flipY,
                                bool premultiplyAlpha,
                                bool unmultiplyAlpha)
        {
            if (!checkExtensions())
            {
                return;
            }
    
            size_t colorCount;
            uint8_t componentCount;
            const uint8_t *srcColors = getSourceColors(sourceFormat, &colorCount, &componentCount);
    
            std::vector<GLColor> destColors(colorCount);
            for (size_t i = 0; i < colorCount; ++i)
            {
                calculateCopyTextureResults(sourceFormat, destFormat, premultiplyAlpha, unmultiplyAlpha,
                                            &srcColors[i * componentCount], &destColors[i]);
            }
    
            for (size_t i = 0; i < colorCount - 3; ++i)
            {
                initializeSourceTexture(sourceFormat, &srcColors[i * componentCount], componentCount);
    
                glBindTexture(GL_TEXTURE_2D, mTextures[1]);
                glTexImage2D(GL_TEXTURE_2D, 0, destFormat, 2, 2, 0, destFormat, GL_UNSIGNED_BYTE,
                             nullptr);
    
                glCopySubTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, 0, 0, 0, 0, 2,
                                         2, flipY, premultiplyAlpha, unmultiplyAlpha);
    
                EXPECT_GL_NO_ERROR();
    
                if (sourceFormat != GL_LUMINANCE && sourceFormat != GL_LUMINANCE_ALPHA &&
                    sourceFormat != GL_ALPHA)
                {
                    // Check that FB is complete.
                    EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
                }
    
                if (flipY)
                {
                    EXPECT_PIXEL_COLOR_NEAR(0, 0, destColors[i + 2], 1.0);
                    EXPECT_PIXEL_COLOR_NEAR(1, 0, destColors[i + 3], 1.0);
                    EXPECT_PIXEL_COLOR_NEAR(0, 1, destColors[i + 0], 1.0);
                    EXPECT_PIXEL_COLOR_NEAR(1, 1, destColors[i + 1], 1.0);
                }
                else
                {
                    EXPECT_PIXEL_COLOR_NEAR(0, 0, destColors[i + 0], 1.0);
                    EXPECT_PIXEL_COLOR_NEAR(1, 0, destColors[i + 1], 1.0);
                    EXPECT_PIXEL_COLOR_NEAR(0, 1, destColors[i + 2], 1.0);
                    EXPECT_PIXEL_COLOR_NEAR(1, 1, destColors[i + 3], 1.0);
                }
    
                EXPECT_GL_NO_ERROR();
            }
        }
    
        void testGradientDownsampleUniqueValues(GLenum destFormat,
                                                GLenum destType,
                                                const std::array<size_t, 4> &expectedUniqueValues)
        {
            std::array<GLColor, 256> sourceGradient;
            for (size_t i = 0; i < sourceGradient.size(); i++)
            {
                GLubyte value     = static_cast<GLubyte>(i);
                sourceGradient[i] = GLColor(value, value, value, value);
            }
            GLTexture sourceTexture;
            glBindTexture(GL_TEXTURE_2D, sourceTexture);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                         sourceGradient.data());
    
            GLTexture destTexture;
            glBindTexture(GL_TEXTURE_2D, destTexture);
            glCopyTextureCHROMIUM(sourceTexture, 0, GL_TEXTURE_2D, destTexture, 0, destFormat, destType,
                                  GL_FALSE, GL_FALSE, GL_FALSE);
            EXPECT_GL_NO_ERROR();
    
            GLFramebuffer fbo;
            glBindFramebuffer(GL_FRAMEBUFFER, fbo);
            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, destTexture, 0);
    
            std::array<GLColor, 256> destData;
            glReadPixels(0, 0, 256, 1, GL_RGBA, GL_UNSIGNED_BYTE, destData.data());
            EXPECT_GL_NO_ERROR();
    
            std::set<GLubyte> uniqueValues[4];
            for (size_t i = 0; i < destData.size(); i++)
            {
                GLColor color = destData[i];
                uniqueValues[0].insert(color.R);
                uniqueValues[1].insert(color.G);
                uniqueValues[2].insert(color.B);
                uniqueValues[3].insert(color.A);
            }
    
            EXPECT_EQ(expectedUniqueValues[0], uniqueValues[0].size());
            EXPECT_EQ(expectedUniqueValues[1], uniqueValues[1].size());
            EXPECT_EQ(expectedUniqueValues[2], uniqueValues[2].size());
            EXPECT_EQ(expectedUniqueValues[3], uniqueValues[3].size());
        }
    
        GLuint mTextures[2] = {
            0,
            0,
        };
        GLuint mFramebuffer = 0;
    
        PFNGLCOPYTEXTURECHROMIUMPROC glCopyTextureCHROMIUM       = nullptr;
        PFNGLCOPYSUBTEXTURECHROMIUMPROC glCopySubTextureCHROMIUM = nullptr;
    };
    
    class CopyTextureTestDest : public CopyTextureTest
    {};
    
    class CopyTextureTestWebGL : public CopyTextureTest
    {
      protected:
        CopyTextureTestWebGL() : CopyTextureTest() { setWebGLCompatibilityEnabled(true); }
    };
    
    class CopyTextureTestES3 : public CopyTextureTest
    {};
    
    // Test to ensure that the basic functionality of the extension works.
    TEST_P(CopyTextureTest, BasicCopyTexture)
    {
        testCopyTexture(GL_RGBA, GL_RGBA, false, false, false);
    }
    
    // Test to ensure that the basic functionality of the extension works.
    TEST_P(CopyTextureTest, BasicCopySubTexture)
    {
        testCopySubTexture(GL_RGBA, GL_RGBA, false, false, false);
    }
    
    // Test that CopyTexture cannot redefine an immutable texture and CopySubTexture can copy data to
    // immutable textures
    TEST_P(CopyTextureTest, ImmutableTexture)
    {
        if (!checkExtensions())
        {
            return;
        }
    
        ANGLE_SKIP_TEST_IF(
            getClientMajorVersion() < 3 &&
            (!extensionEnabled("GL_EXT_texture_storage") || !extensionEnabled("GL_OES_rgb8_rgba8")));
    
        GLColor pixels = GLColor::red;
    
        glBindTexture(GL_TEXTURE_2D, mTextures[0]);
        glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8_OES, 1, 1);
        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixels);
    
        glBindTexture(GL_TEXTURE_2D, mTextures[1]);
        glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8_OES, 1, 1);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[1], 0);
        EXPECT_GL_NO_ERROR();
    
        // Should generate an error when the texture is redefined
        glCopyTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, GL_RGBA,
                              GL_UNSIGNED_BYTE, false, false, false);
        EXPECT_GL_ERROR(GL_INVALID_OPERATION);
    
        // Should succeed when using CopySubTexture
        glCopySubTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, 0, 0, 0, 0, 1, 1,
                                 false, false, false);
        EXPECT_GL_NO_ERROR();
    
        // Check that FB is complete.
        EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, pixels);
    
        EXPECT_GL_NO_ERROR();
    }
    
    // Test validation of internal formats in CopyTexture and CopySubTexture
    TEST_P(CopyTextureTest, InternalFormat)
    {
        if (!checkExtensions())
        {
            return;
        }
    
        std::vector<GLint> sourceFormats;
        sourceFormats.push_back(GL_ALPHA);
        sourceFormats.push_back(GL_RGB);
        sourceFormats.push_back(GL_RGBA);
        sourceFormats.push_back(GL_LUMINANCE);
        sourceFormats.push_back(GL_LUMINANCE_ALPHA);
    
        std::vector<GLint> destFormats;
        destFormats.push_back(GL_RGB);
        destFormats.push_back(GL_RGBA);
    
        if (extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            sourceFormats.push_back(GL_BGRA_EXT);
            destFormats.push_back(GL_BGRA_EXT);
        }
    
        // Test with glCopyTexture
        for (GLint sourceFormat : sourceFormats)
        {
            for (GLint destFormat : destFormats)
            {
                glBindTexture(GL_TEXTURE_2D, mTextures[0]);
                glTexImage2D(GL_TEXTURE_2D, 0, sourceFormat, 1, 1, 0, sourceFormat, GL_UNSIGNED_BYTE,
                             nullptr);
                EXPECT_GL_NO_ERROR();
    
                glCopyTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, destFormat,
                                      GL_UNSIGNED_BYTE, false, false, false);
    
                EXPECT_GL_NO_ERROR();
            }
        }
    
        // Test with glCopySubTexture
        for (GLint sourceFormat : sourceFormats)
        {
            for (GLint destFormat : destFormats)
            {
                glBindTexture(GL_TEXTURE_2D, mTextures[0]);
                glTexImage2D(GL_TEXTURE_2D, 0, sourceFormat, 1, 1, 0, sourceFormat, GL_UNSIGNED_BYTE,
                             nullptr);
                EXPECT_GL_NO_ERROR();
    
                glBindTexture(GL_TEXTURE_2D, mTextures[1]);
                glTexImage2D(GL_TEXTURE_2D, 0, destFormat, 1, 1, 0, destFormat, GL_UNSIGNED_BYTE,
                             nullptr);
                EXPECT_GL_NO_ERROR();
    
                glCopySubTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, 0, 0, 0, 0, 1,
                                         1, false, false, false);
    
                EXPECT_GL_NO_ERROR();
            }
        }
    }
    
    // Test to ensure that the destination texture is redefined if the properties are different.
    TEST_P(CopyTextureTest, RedefineDestinationTexture)
    {
        ANGLE_SKIP_TEST_IF(!checkExtensions());
        ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_EXT_texture_format_BGRA8888"));
    
        GLColor pixels[4] = {GLColor::red, GLColor::red, GLColor::red, GLColor::red};
    
        glBindTexture(GL_TEXTURE_2D, mTextures[0]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
    
        glBindTexture(GL_TEXTURE_2D, mTextures[1]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT, 1, 1, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, pixels);
        EXPECT_GL_NO_ERROR();
    
        // GL_INVALID_OPERATION due to "intrinsic format" != "internal format".
        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
        EXPECT_GL_ERROR(GL_INVALID_OPERATION);
        // GL_INVALID_VALUE due to bad dimensions.
        glTexSubImage2D(GL_TEXTURE_2D, 0, 1, 1, 1, 1, GL_BGRA_EXT, GL_UNSIGNED_BYTE, pixels);
        EXPECT_GL_ERROR(GL_INVALID_VALUE);
    
        // If the dest texture has different properties, glCopyTextureCHROMIUM()
        // redefines them.
        glCopyTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, GL_RGBA,
                              GL_UNSIGNED_BYTE, false, false, false);
        EXPECT_GL_NO_ERROR();
    
        // glTexSubImage2D() succeeds because mTextures[1] is redefined into 2x2
        // dimension and GL_RGBA format.
        glBindTexture(GL_TEXTURE_2D, mTextures[1]);
        glTexSubImage2D(GL_TEXTURE_2D, 0, 1, 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
        EXPECT_GL_NO_ERROR();
    
        // Check that FB is complete.
        EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
    
        EXPECT_PIXEL_COLOR_EQ(1, 1, pixels[3]);
        EXPECT_GL_NO_ERROR();
    }
    
    // Test that invalid dimensions in CopySubTexture are validated
    TEST_P(CopyTextureTest, CopySubTextureDimension)
    {
        if (!checkExtensions())
        {
            return;
        }
    
        glBindTexture(GL_TEXTURE_2D, mTextures[0]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        glBindTexture(GL_TEXTURE_2D, mTextures[1]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 3, 3, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        glCopySubTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, 1, 1, 0, 0, 1, 1,
                                 false, false, false);
        EXPECT_GL_NO_ERROR();
    
        // xoffset < 0
        glCopySubTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, -1, 1, 0, 0, 1, 1,
                                 false, false, false);
        EXPECT_GL_ERROR(GL_INVALID_VALUE);
    
        // x < 0
        glCopySubTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, 1, 1, -1, 0, 1, 1,
                                 false, false, false);
        EXPECT_GL_ERROR(GL_INVALID_VALUE);
    
        // xoffset + width > dest_width
        glCopySubTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, 2, 2, 0, 0, 2, 2,
                                 false, false, false);
        EXPECT_GL_ERROR(GL_INVALID_VALUE);
    
        // x + width > source_width
        glCopySubTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, 0, 0, 1, 1, 2, 2,
                                 false, false, false);
        EXPECT_GL_ERROR(GL_INVALID_VALUE);
    }
    
    // Test that invalid IDs in CopyTexture are validated
    TEST_P(CopyTextureTest, CopyTextureInvalidTextureIds)
    {
        if (!checkExtensions())
        {
            return;
        }
    
        glBindTexture(GL_TEXTURE_2D, mTextures[0]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        glBindTexture(GL_TEXTURE_2D, mTextures[1]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 3, 3, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        glCopyTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, 99993, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                              false, false, false);
        EXPECT_GL_ERROR(GL_INVALID_VALUE);
    
        glCopyTextureCHROMIUM(99994, 0, GL_TEXTURE_2D, mTextures[1], 0, GL_RGBA, GL_UNSIGNED_BYTE,
                              false, false, false);
        EXPECT_GL_ERROR(GL_INVALID_VALUE);
    
        glCopyTextureCHROMIUM(99995, 0, GL_TEXTURE_2D, 99996, 0, GL_RGBA, GL_UNSIGNED_BYTE, false,
                              false, false);
        EXPECT_GL_ERROR(GL_INVALID_VALUE);
    
        glCopyTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, GL_RGBA,
                              GL_UNSIGNED_BYTE, false, false, false);
        EXPECT_GL_NO_ERROR();
    }
    
    // Test that invalid IDs in CopySubTexture are validated
    TEST_P(CopyTextureTest, CopySubTextureInvalidTextureIds)
    {
        if (!checkExtensions())
        {
            return;
        }
    
        glBindTexture(GL_TEXTURE_2D, mTextures[0]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        glBindTexture(GL_TEXTURE_2D, mTextures[1]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 3, 3, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        glCopySubTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, 99993, 0, 1, 1, 0, 0, 1, 1, false,
                                 false, false);
        EXPECT_GL_ERROR(GL_INVALID_VALUE);
    
        glCopySubTextureCHROMIUM(99994, 0, GL_TEXTURE_2D, mTextures[1], 0, 1, 1, 0, 0, 1, 1, false,
                                 false, false);
        EXPECT_GL_ERROR(GL_INVALID_VALUE);
    
        glCopySubTextureCHROMIUM(99995, 0, GL_TEXTURE_2D, 99996, 0, 1, 1, 0, 0, 1, 1, false, false,
                                 false);
        EXPECT_GL_ERROR(GL_INVALID_VALUE);
    
        glCopySubTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, 1, 1, 0, 0, 1, 1,
                                 false, false, false);
        EXPECT_GL_NO_ERROR();
    }
    
    TEST_P(CopyTextureTest, InvalidTarget)
    {
        ANGLE_SKIP_TEST_IF(!checkExtensions());
    
        GLTexture textures[2];
    
        glBindTexture(GL_TEXTURE_2D, textures[0]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        glBindTexture(GL_TEXTURE_2D, textures[1]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 3, 3, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        // Invalid enum for a completely invalid target
        glCopySubTextureCHROMIUM(textures[0], 0, GL_INVALID_VALUE, textures[1], 0, 1, 1, 0, 0, 1, 1,
                                 false, false, false);
        EXPECT_GL_ERROR(GL_INVALID_ENUM);
    
        // Invalid value for a valid target enum but is not valid for the destination texture
        glCopySubTextureCHROMIUM(textures[0], 0, GL_TEXTURE_CUBE_MAP_POSITIVE_X, textures[1], 0, 1, 1,
                                 0, 0, 1, 1, false, false, false);
        EXPECT_GL_ERROR(GL_INVALID_VALUE);
    }
    
    // Test that using an offset in CopySubTexture works correctly
    TEST_P(CopyTextureTest, CopySubTextureOffset)
    {
        if (!checkExtensions())
        {
            return;
        }
    
        GLColor rgbaPixels[4 * 4] = {GLColor::red, GLColor::green, GLColor::blue, GLColor::black};
        glBindTexture(GL_TEXTURE_2D, mTextures[0]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgbaPixels);
    
        GLColor transparentPixels[4 * 4] = {GLColor::transparentBlack, GLColor::transparentBlack,
                                            GLColor::transparentBlack, GLColor::transparentBlack};
        glBindTexture(GL_TEXTURE_2D, mTextures[1]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, transparentPixels);
    
        // Check that FB is complete.
        EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
    
        glCopySubTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, 1, 1, 0, 0, 1, 1,
                                 false, false, false);
        EXPECT_GL_NO_ERROR();
        EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::red);
    
        glCopySubTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, 1, 0, 1, 0, 1, 1,
                                 false, false, false);
        EXPECT_GL_NO_ERROR();
        EXPECT_PIXEL_COLOR_EQ(1, 0, GLColor::green);
    
        glCopySubTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, 0, 1, 0, 1, 1, 1,
                                 false, false, false);
        EXPECT_GL_NO_ERROR();
        EXPECT_PIXEL_COLOR_EQ(0, 1, GLColor::blue);
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::transparentBlack);
        EXPECT_GL_NO_ERROR();
    }
    
    // Test every combination of copy [sub]texture parameters:
    // source: ALPHA, RGB, RGBA, LUMINANCE, LUMINANCE_ALPHA, BGRA_EXT
    // destination: RGB, RGBA, BGRA_EXT
    // flipY: false, true
    // premultiplyAlpha: false, true
    // unmultiplyAlpha: false, true
    TEST_P(CopyTextureTest, CopyTextureAToRGB)
    {
        testCopyTexture(GL_ALPHA, GL_RGB, false, false, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureAToRGB)
    {
        testCopySubTexture(GL_ALPHA, GL_RGB, false, false, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureAToRGBUnmultiplyAlpha)
    {
        testCopyTexture(GL_ALPHA, GL_RGB, false, false, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureAToRGBUnmultiplyAlpha)
    {
        testCopySubTexture(GL_ALPHA, GL_RGB, false, false, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureAToRGBPremultiplyAlpha)
    {
        testCopyTexture(GL_ALPHA, GL_RGB, false, true, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureAToRGBPremultiplyAlpha)
    {
        testCopySubTexture(GL_ALPHA, GL_RGB, false, true, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureAToRGBPremultiplyAlphaUnmultiplyAlpha)
    {
        testCopyTexture(GL_ALPHA, GL_RGB, false, true, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureAToRGBPremultiplyAlphaUnmultiplyAlpha)
    {
        testCopySubTexture(GL_ALPHA, GL_RGB, false, true, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureAToRGBFlipY)
    {
        testCopyTexture(GL_ALPHA, GL_RGB, true, false, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureAToRGBFlipY)
    {
        testCopySubTexture(GL_ALPHA, GL_RGB, true, false, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureAToRGBFlipYUnmultiplyAlpha)
    {
        testCopyTexture(GL_ALPHA, GL_RGB, true, false, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureAToRGBFlipYUnmultiplyAlpha)
    {
        testCopySubTexture(GL_ALPHA, GL_RGB, true, false, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureAToRGBFlipYPremultiplyAlpha)
    {
        testCopyTexture(GL_ALPHA, GL_RGB, true, true, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureAToRGBFlipYPremultiplyAlpha)
    {
        testCopySubTexture(GL_ALPHA, GL_RGB, true, true, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureAToRGBFlipYPremultiplyAlphaUnmultiplyAlpha)
    {
        testCopyTexture(GL_ALPHA, GL_RGB, true, true, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureAToRGBFlipYPremultiplyAlphaUnmultiplyAlpha)
    {
        testCopySubTexture(GL_ALPHA, GL_RGB, true, true, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureAToRGBA)
    {
        testCopyTexture(GL_ALPHA, GL_RGBA, false, false, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureAToRGBA)
    {
        testCopySubTexture(GL_ALPHA, GL_RGBA, false, false, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureAToRGBAUnmultiplyAlpha)
    {
        testCopyTexture(GL_ALPHA, GL_RGBA, false, false, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureAToRGBAUnmultiplyAlpha)
    {
        testCopySubTexture(GL_ALPHA, GL_RGBA, false, false, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureAToRGBAPremultiplyAlpha)
    {
        testCopyTexture(GL_ALPHA, GL_RGBA, false, true, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureAToRGBAPremultiplyAlpha)
    {
        testCopySubTexture(GL_ALPHA, GL_RGBA, false, true, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureAToRGBAPremultiplyAlphaUnmultiplyAlpha)
    {
        testCopyTexture(GL_ALPHA, GL_RGBA, false, true, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureAToRGBAPremultiplyAlphaUnmultiplyAlpha)
    {
        testCopySubTexture(GL_ALPHA, GL_RGBA, false, true, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureAToRGBAFlipY)
    {
        testCopyTexture(GL_ALPHA, GL_RGBA, true, false, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureAToRGBAFlipY)
    {
        testCopySubTexture(GL_ALPHA, GL_RGBA, true, false, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureAToRGBAFlipYUnmultiplyAlpha)
    {
        testCopyTexture(GL_ALPHA, GL_RGBA, true, false, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureAToRGBAFlipYUnmultiplyAlpha)
    {
        testCopySubTexture(GL_ALPHA, GL_RGBA, true, false, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureAToRGBAFlipYPremultiplyAlpha)
    {
        testCopyTexture(GL_ALPHA, GL_RGBA, true, true, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureAToRGBAFlipYPremultiplyAlpha)
    {
        testCopySubTexture(GL_ALPHA, GL_RGBA, true, true, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureAToRGBAFlipYPremultiplyAlphaUnmultiplyAlpha)
    {
        testCopyTexture(GL_ALPHA, GL_RGBA, true, true, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureAToRGBAFlipYPremultiplyAlphaUnmultiplyAlpha)
    {
        testCopySubTexture(GL_ALPHA, GL_RGBA, true, true, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureAToBGRA)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_ALPHA, GL_BGRA_EXT, false, false, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureAToBGRA)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_ALPHA, GL_BGRA_EXT, false, false, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureAToBGRAUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_ALPHA, GL_BGRA_EXT, false, false, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureAToBGRAUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_ALPHA, GL_BGRA_EXT, false, false, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureAToBGRAPremultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_ALPHA, GL_BGRA_EXT, false, true, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureAToBGRAPremultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_ALPHA, GL_BGRA_EXT, false, true, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureAToBGRAPremultiplyAlphaUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_ALPHA, GL_BGRA_EXT, false, true, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureAToBGRAPremultiplyAlphaUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_ALPHA, GL_BGRA_EXT, false, true, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureAToBGRAFlipY)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_ALPHA, GL_BGRA_EXT, true, false, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureAToBGRAFlipY)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_ALPHA, GL_BGRA_EXT, true, false, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureAToBGRAFlipYUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_ALPHA, GL_BGRA_EXT, true, false, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureAToBGRAFlipYUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_ALPHA, GL_BGRA_EXT, true, false, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureAToBGRAFlipYPremultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_ALPHA, GL_BGRA_EXT, true, true, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureAToBGRAFlipYPremultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_ALPHA, GL_BGRA_EXT, true, true, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureAToBGRAFlipYPremultiplyAlphaUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_ALPHA, GL_BGRA_EXT, true, true, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureAToBGRAFlipYPremultiplyAlphaUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_ALPHA, GL_BGRA_EXT, true, true, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBToRGB)
    {
        testCopyTexture(GL_RGB, GL_RGB, false, false, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBToRGB)
    {
        testCopySubTexture(GL_RGB, GL_RGB, false, false, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBToRGBUnmultiplyAlpha)
    {
        testCopyTexture(GL_RGB, GL_RGB, false, false, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBToRGBUnmultiplyAlpha)
    {
        testCopySubTexture(GL_RGB, GL_RGB, false, false, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBToRGBPremultiplyAlpha)
    {
        testCopyTexture(GL_RGB, GL_RGB, false, true, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBToRGBPremultiplyAlpha)
    {
        testCopySubTexture(GL_RGB, GL_RGB, false, true, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBToRGBPremultiplyAlphaUnmultiplyAlpha)
    {
        testCopyTexture(GL_RGB, GL_RGB, false, true, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBToRGBPremultiplyAlphaUnmultiplyAlpha)
    {
        testCopySubTexture(GL_RGB, GL_RGB, false, true, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBToRGBFlipY)
    {
        testCopyTexture(GL_RGB, GL_RGB, true, false, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBToRGBFlipY)
    {
        testCopySubTexture(GL_RGB, GL_RGB, true, false, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBToRGBFlipYUnmultiplyAlpha)
    {
        testCopyTexture(GL_RGB, GL_RGB, true, false, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBToRGBFlipYUnmultiplyAlpha)
    {
        testCopySubTexture(GL_RGB, GL_RGB, true, false, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBToRGBFlipYPremultiplyAlpha)
    {
        testCopyTexture(GL_RGB, GL_RGB, true, true, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBToRGBFlipYPremultiplyAlpha)
    {
        testCopySubTexture(GL_RGB, GL_RGB, true, true, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBToRGBFlipYPremultiplyAlphaUnmultiplyAlpha)
    {
        testCopyTexture(GL_RGB, GL_RGB, true, true, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBToRGBFlipYPremultiplyAlphaUnmultiplyAlpha)
    {
        testCopySubTexture(GL_RGB, GL_RGB, true, true, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBToRGBA)
    {
        testCopyTexture(GL_RGB, GL_RGBA, false, false, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBToRGBA)
    {
        testCopySubTexture(GL_RGB, GL_RGBA, false, false, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBToRGBAUnmultiplyAlpha)
    {
        testCopyTexture(GL_RGB, GL_RGBA, false, false, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBToRGBAUnmultiplyAlpha)
    {
        testCopySubTexture(GL_RGB, GL_RGBA, false, false, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBToRGBAPremultiplyAlpha)
    {
        testCopyTexture(GL_RGB, GL_RGBA, false, true, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBToRGBAPremultiplyAlpha)
    {
        testCopySubTexture(GL_RGB, GL_RGBA, false, true, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBToRGBAPremultiplyAlphaUnmultiplyAlpha)
    {
        testCopyTexture(GL_RGB, GL_RGBA, false, true, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBToRGBAPremultiplyAlphaUnmultiplyAlpha)
    {
        testCopySubTexture(GL_RGB, GL_RGBA, false, true, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBToRGBAFlipY)
    {
        testCopyTexture(GL_RGB, GL_RGBA, true, false, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBToRGBAFlipY)
    {
        testCopySubTexture(GL_RGB, GL_RGBA, true, false, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBToRGBAFlipYUnmultiplyAlpha)
    {
        testCopyTexture(GL_RGB, GL_RGBA, true, false, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBToRGBAFlipYUnmultiplyAlpha)
    {
        testCopySubTexture(GL_RGB, GL_RGBA, true, false, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBToRGBAFlipYPremultiplyAlpha)
    {
        testCopyTexture(GL_RGB, GL_RGBA, true, true, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBToRGBAFlipYPremultiplyAlpha)
    {
        testCopySubTexture(GL_RGB, GL_RGBA, true, true, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBToRGBAFlipYPremultiplyAlphaUnmultiplyAlpha)
    {
        testCopyTexture(GL_RGB, GL_RGBA, true, true, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBToRGBAFlipYPremultiplyAlphaUnmultiplyAlpha)
    {
        testCopySubTexture(GL_RGB, GL_RGBA, true, true, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBToBGRA)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_RGB, GL_BGRA_EXT, false, false, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBToBGRA)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_RGB, GL_BGRA_EXT, false, false, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBToBGRAUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_RGB, GL_BGRA_EXT, false, false, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBToBGRAUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_RGB, GL_BGRA_EXT, false, false, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBToBGRAPremultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_RGB, GL_BGRA_EXT, false, true, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBToBGRAPremultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_RGB, GL_BGRA_EXT, false, true, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBToBGRAPremultiplyAlphaUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_RGB, GL_BGRA_EXT, false, true, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBToBGRAPremultiplyAlphaUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_RGB, GL_BGRA_EXT, false, true, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBToBGRAFlipY)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_RGB, GL_BGRA_EXT, true, false, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBToBGRAFlipY)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_RGB, GL_BGRA_EXT, true, false, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBToBGRAFlipYUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_RGB, GL_BGRA_EXT, true, false, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBToBGRAFlipYUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_RGB, GL_BGRA_EXT, true, false, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBToBGRAFlipYPremultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_RGB, GL_BGRA_EXT, true, true, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBToBGRAFlipYPremultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_RGB, GL_BGRA_EXT, true, true, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBToBGRAFlipYPremultiplyAlphaUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_RGB, GL_BGRA_EXT, true, true, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBToBGRAFlipYPremultiplyAlphaUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_RGB, GL_BGRA_EXT, true, true, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBAToRGB)
    {
        testCopyTexture(GL_RGBA, GL_RGB, false, false, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBAToRGB)
    {
        testCopySubTexture(GL_RGBA, GL_RGB, false, false, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBAToRGBUnmultiplyAlpha)
    {
        testCopyTexture(GL_RGBA, GL_RGB, false, false, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBAToRGBUnmultiplyAlpha)
    {
        testCopySubTexture(GL_RGBA, GL_RGB, false, false, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBAToRGBPremultiplyAlpha)
    {
        testCopyTexture(GL_RGBA, GL_RGB, false, true, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBAToRGBPremultiplyAlpha)
    {
        testCopySubTexture(GL_RGBA, GL_RGB, false, true, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBAToRGBPremultiplyAlphaUnmultiplyAlpha)
    {
        testCopyTexture(GL_RGBA, GL_RGB, false, true, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBAToRGBPremultiplyAlphaUnmultiplyAlpha)
    {
        testCopySubTexture(GL_RGBA, GL_RGB, false, true, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBAToRGBFlipY)
    {
        testCopyTexture(GL_RGBA, GL_RGB, true, false, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBAToRGBFlipY)
    {
        testCopySubTexture(GL_RGBA, GL_RGB, true, false, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBAToRGBFlipYUnmultiplyAlpha)
    {
        testCopyTexture(GL_RGBA, GL_RGB, true, false, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBAToRGBFlipYUnmultiplyAlpha)
    {
        testCopySubTexture(GL_RGBA, GL_RGB, true, false, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBAToRGBFlipYPremultiplyAlpha)
    {
        testCopyTexture(GL_RGBA, GL_RGB, true, true, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBAToRGBFlipYPremultiplyAlpha)
    {
        testCopySubTexture(GL_RGBA, GL_RGB, true, true, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBAToRGBFlipYPremultiplyAlphaUnmultiplyAlpha)
    {
        testCopyTexture(GL_RGBA, GL_RGB, true, true, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBAToRGBFlipYPremultiplyAlphaUnmultiplyAlpha)
    {
        testCopySubTexture(GL_RGBA, GL_RGB, true, true, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBAToRGBA)
    {
        testCopyTexture(GL_RGBA, GL_RGBA, false, false, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBAToRGBA)
    {
        testCopySubTexture(GL_RGBA, GL_RGBA, false, false, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBAToRGBAUnmultiplyAlpha)
    {
        testCopyTexture(GL_RGBA, GL_RGBA, false, false, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBAToRGBAUnmultiplyAlpha)
    {
        testCopySubTexture(GL_RGBA, GL_RGBA, false, false, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBAToRGBAPremultiplyAlpha)
    {
        testCopyTexture(GL_RGBA, GL_RGBA, false, true, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBAToRGBAPremultiplyAlpha)
    {
        testCopySubTexture(GL_RGBA, GL_RGBA, false, true, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBAToRGBAPremultiplyAlphaUnmultiplyAlpha)
    {
        testCopyTexture(GL_RGBA, GL_RGBA, false, true, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBAToRGBAPremultiplyAlphaUnmultiplyAlpha)
    {
        testCopySubTexture(GL_RGBA, GL_RGBA, false, true, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBAToRGBAFlipY)
    {
        testCopyTexture(GL_RGBA, GL_RGBA, true, false, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBAToRGBAFlipY)
    {
        testCopySubTexture(GL_RGBA, GL_RGBA, true, false, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBAToRGBAFlipYUnmultiplyAlpha)
    {
        testCopyTexture(GL_RGBA, GL_RGBA, true, false, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBAToRGBAFlipYUnmultiplyAlpha)
    {
        testCopySubTexture(GL_RGBA, GL_RGBA, true, false, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBAToRGBAFlipYPremultiplyAlpha)
    {
        testCopyTexture(GL_RGBA, GL_RGBA, true, true, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBAToRGBAFlipYPremultiplyAlpha)
    {
        testCopySubTexture(GL_RGBA, GL_RGBA, true, true, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBAToRGBAFlipYPremultiplyAlphaUnmultiplyAlpha)
    {
        testCopyTexture(GL_RGBA, GL_RGBA, true, true, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBAToRGBAFlipYPremultiplyAlphaUnmultiplyAlpha)
    {
        testCopySubTexture(GL_RGBA, GL_RGBA, true, true, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBAToBGRA)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_RGBA, GL_BGRA_EXT, false, false, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBAToBGRA)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_RGBA, GL_BGRA_EXT, false, false, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBAToBGRAUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_RGBA, GL_BGRA_EXT, false, false, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBAToBGRAUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_RGBA, GL_BGRA_EXT, false, false, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBAToBGRAPremultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_RGBA, GL_BGRA_EXT, false, true, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBAToBGRAPremultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_RGBA, GL_BGRA_EXT, false, true, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBAToBGRAPremultiplyAlphaUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_RGBA, GL_BGRA_EXT, false, true, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBAToBGRAPremultiplyAlphaUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_RGBA, GL_BGRA_EXT, false, true, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBAToBGRAFlipY)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_RGBA, GL_BGRA_EXT, true, false, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBAToBGRAFlipY)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_RGBA, GL_BGRA_EXT, true, false, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBAToBGRAFlipYUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_RGBA, GL_BGRA_EXT, true, false, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBAToBGRAFlipYUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_RGBA, GL_BGRA_EXT, true, false, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBAToBGRAFlipYPremultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_RGBA, GL_BGRA_EXT, true, true, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBAToBGRAFlipYPremultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_RGBA, GL_BGRA_EXT, true, true, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureRGBAToBGRAFlipYPremultiplyAlphaUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_RGBA, GL_BGRA_EXT, true, true, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureRGBAToBGRAFlipYPremultiplyAlphaUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_RGBA, GL_BGRA_EXT, true, true, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLToRGB)
    {
        testCopyTexture(GL_LUMINANCE, GL_RGB, false, false, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLToRGB)
    {
        testCopySubTexture(GL_LUMINANCE, GL_RGB, false, false, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLToRGBUnmultiplyAlpha)
    {
        testCopyTexture(GL_LUMINANCE, GL_RGB, false, false, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLToRGBUnmultiplyAlpha)
    {
        testCopySubTexture(GL_LUMINANCE, GL_RGB, false, false, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLToRGBPremultiplyAlpha)
    {
        testCopyTexture(GL_LUMINANCE, GL_RGB, false, true, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLToRGBPremultiplyAlpha)
    {
        testCopySubTexture(GL_LUMINANCE, GL_RGB, false, true, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLToRGBPremultiplyAlphaUnmultiplyAlpha)
    {
        testCopyTexture(GL_LUMINANCE, GL_RGB, false, true, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLToRGBPremultiplyAlphaUnmultiplyAlpha)
    {
        testCopySubTexture(GL_LUMINANCE, GL_RGB, false, true, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLToRGBFlipY)
    {
        testCopyTexture(GL_LUMINANCE, GL_RGB, true, false, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLToRGBFlipY)
    {
        testCopySubTexture(GL_LUMINANCE, GL_RGB, true, false, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLToRGBFlipYUnmultiplyAlpha)
    {
        testCopyTexture(GL_LUMINANCE, GL_RGB, true, false, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLToRGBFlipYUnmultiplyAlpha)
    {
        testCopySubTexture(GL_LUMINANCE, GL_RGB, true, false, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLToRGBFlipYPremultiplyAlpha)
    {
        testCopyTexture(GL_LUMINANCE, GL_RGB, true, true, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLToRGBFlipYPremultiplyAlpha)
    {
        testCopySubTexture(GL_LUMINANCE, GL_RGB, true, true, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLToRGBFlipYPremultiplyAlphaUnmultiplyAlpha)
    {
        testCopyTexture(GL_LUMINANCE, GL_RGB, true, true, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLToRGBFlipYPremultiplyAlphaUnmultiplyAlpha)
    {
        testCopySubTexture(GL_LUMINANCE, GL_RGB, true, true, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLToRGBA)
    {
        testCopyTexture(GL_LUMINANCE, GL_RGBA, false, false, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLToRGBA)
    {
        testCopySubTexture(GL_LUMINANCE, GL_RGBA, false, false, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLToRGBAUnmultiplyAlpha)
    {
        testCopyTexture(GL_LUMINANCE, GL_RGBA, false, false, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLToRGBAUnmultiplyAlpha)
    {
        testCopySubTexture(GL_LUMINANCE, GL_RGBA, false, false, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLToRGBAPremultiplyAlpha)
    {
        testCopyTexture(GL_LUMINANCE, GL_RGBA, false, true, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLToRGBAPremultiplyAlpha)
    {
        testCopySubTexture(GL_LUMINANCE, GL_RGBA, false, true, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLToRGBAPremultiplyAlphaUnmultiplyAlpha)
    {
        testCopyTexture(GL_LUMINANCE, GL_RGBA, false, true, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLToRGBAPremultiplyAlphaUnmultiplyAlpha)
    {
        testCopySubTexture(GL_LUMINANCE, GL_RGBA, false, true, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLToRGBAFlipY)
    {
        testCopyTexture(GL_LUMINANCE, GL_RGBA, true, false, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLToRGBAFlipY)
    {
        testCopySubTexture(GL_LUMINANCE, GL_RGBA, true, false, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLToRGBAFlipYUnmultiplyAlpha)
    {
        testCopyTexture(GL_LUMINANCE, GL_RGBA, true, false, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLToRGBAFlipYUnmultiplyAlpha)
    {
        testCopySubTexture(GL_LUMINANCE, GL_RGBA, true, false, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLToRGBAFlipYPremultiplyAlpha)
    {
        testCopyTexture(GL_LUMINANCE, GL_RGBA, true, true, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLToRGBAFlipYPremultiplyAlpha)
    {
        testCopySubTexture(GL_LUMINANCE, GL_RGBA, true, true, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLToRGBAFlipYPremultiplyAlphaUnmultiplyAlpha)
    {
        testCopyTexture(GL_LUMINANCE, GL_RGBA, true, true, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLToRGBAFlipYPremultiplyAlphaUnmultiplyAlpha)
    {
        testCopySubTexture(GL_LUMINANCE, GL_RGBA, true, true, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLToBGRA)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_LUMINANCE, GL_BGRA_EXT, false, false, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLToBGRA)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_LUMINANCE, GL_BGRA_EXT, false, false, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLToBGRAUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_LUMINANCE, GL_BGRA_EXT, false, false, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLToBGRAUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_LUMINANCE, GL_BGRA_EXT, false, false, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLToBGRAPremultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_LUMINANCE, GL_BGRA_EXT, false, true, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLToBGRAPremultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_LUMINANCE, GL_BGRA_EXT, false, true, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLToBGRAPremultiplyAlphaUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_LUMINANCE, GL_BGRA_EXT, false, true, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLToBGRAPremultiplyAlphaUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_LUMINANCE, GL_BGRA_EXT, false, true, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLToBGRAFlipY)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_LUMINANCE, GL_BGRA_EXT, true, false, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLToBGRAFlipY)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_LUMINANCE, GL_BGRA_EXT, true, false, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLToBGRAFlipYUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_LUMINANCE, GL_BGRA_EXT, true, false, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLToBGRAFlipYUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_LUMINANCE, GL_BGRA_EXT, true, false, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLToBGRAFlipYPremultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_LUMINANCE, GL_BGRA_EXT, true, true, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLToBGRAFlipYPremultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_LUMINANCE, GL_BGRA_EXT, true, true, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLToBGRAFlipYPremultiplyAlphaUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_LUMINANCE, GL_BGRA_EXT, true, true, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLToBGRAFlipYPremultiplyAlphaUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_LUMINANCE, GL_BGRA_EXT, true, true, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLAToRGB)
    {
        testCopyTexture(GL_LUMINANCE_ALPHA, GL_RGB, false, false, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLAToRGB)
    {
        testCopySubTexture(GL_LUMINANCE_ALPHA, GL_RGB, false, false, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLAToRGBUnmultiplyAlpha)
    {
        testCopyTexture(GL_LUMINANCE_ALPHA, GL_RGB, false, false, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLAToRGBUnmultiplyAlpha)
    {
        testCopySubTexture(GL_LUMINANCE_ALPHA, GL_RGB, false, false, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLAToRGBPremultiplyAlpha)
    {
        testCopyTexture(GL_LUMINANCE_ALPHA, GL_RGB, false, true, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLAToRGBPremultiplyAlpha)
    {
        testCopySubTexture(GL_LUMINANCE_ALPHA, GL_RGB, false, true, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLAToRGBPremultiplyAlphaUnmultiplyAlpha)
    {
        testCopyTexture(GL_LUMINANCE_ALPHA, GL_RGB, false, true, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLAToRGBPremultiplyAlphaUnmultiplyAlpha)
    {
        testCopySubTexture(GL_LUMINANCE_ALPHA, GL_RGB, false, true, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLAToRGBFlipY)
    {
        testCopyTexture(GL_LUMINANCE_ALPHA, GL_RGB, true, false, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLAToRGBFlipY)
    {
        testCopySubTexture(GL_LUMINANCE_ALPHA, GL_RGB, true, false, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLAToRGBFlipYUnmultiplyAlpha)
    {
        testCopyTexture(GL_LUMINANCE_ALPHA, GL_RGB, true, false, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLAToRGBFlipYUnmultiplyAlpha)
    {
        testCopySubTexture(GL_LUMINANCE_ALPHA, GL_RGB, true, false, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLAToRGBFlipYPremultiplyAlpha)
    {
        testCopyTexture(GL_LUMINANCE_ALPHA, GL_RGB, true, true, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLAToRGBFlipYPremultiplyAlpha)
    {
        testCopySubTexture(GL_LUMINANCE_ALPHA, GL_RGB, true, true, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLAToRGBFlipYPremultiplyAlphaUnmultiplyAlpha)
    {
        testCopyTexture(GL_LUMINANCE_ALPHA, GL_RGB, true, true, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLAToRGBFlipYPremultiplyAlphaUnmultiplyAlpha)
    {
        testCopySubTexture(GL_LUMINANCE_ALPHA, GL_RGB, true, true, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLAToRGBA)
    {
        testCopyTexture(GL_LUMINANCE_ALPHA, GL_RGBA, false, false, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLAToRGBA)
    {
        testCopySubTexture(GL_LUMINANCE_ALPHA, GL_RGBA, false, false, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLAToRGBAUnmultiplyAlpha)
    {
        testCopyTexture(GL_LUMINANCE_ALPHA, GL_RGBA, false, false, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLAToRGBAUnmultiplyAlpha)
    {
        testCopySubTexture(GL_LUMINANCE_ALPHA, GL_RGBA, false, false, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLAToRGBAPremultiplyAlpha)
    {
        testCopyTexture(GL_LUMINANCE_ALPHA, GL_RGBA, false, true, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLAToRGBAPremultiplyAlpha)
    {
        testCopySubTexture(GL_LUMINANCE_ALPHA, GL_RGBA, false, true, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLAToRGBAPremultiplyAlphaUnmultiplyAlpha)
    {
        testCopyTexture(GL_LUMINANCE_ALPHA, GL_RGBA, false, true, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLAToRGBAPremultiplyAlphaUnmultiplyAlpha)
    {
        testCopySubTexture(GL_LUMINANCE_ALPHA, GL_RGBA, false, true, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLAToRGBAFlipY)
    {
        testCopyTexture(GL_LUMINANCE_ALPHA, GL_RGBA, true, false, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLAToRGBAFlipY)
    {
        testCopySubTexture(GL_LUMINANCE_ALPHA, GL_RGBA, true, false, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLAToRGBAFlipYUnmultiplyAlpha)
    {
        testCopyTexture(GL_LUMINANCE_ALPHA, GL_RGBA, true, false, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLAToRGBAFlipYUnmultiplyAlpha)
    {
        testCopySubTexture(GL_LUMINANCE_ALPHA, GL_RGBA, true, false, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLAToRGBAFlipYPremultiplyAlpha)
    {
        testCopyTexture(GL_LUMINANCE_ALPHA, GL_RGBA, true, true, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLAToRGBAFlipYPremultiplyAlpha)
    {
        testCopySubTexture(GL_LUMINANCE_ALPHA, GL_RGBA, true, true, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLAToRGBAFlipYPremultiplyAlphaUnmultiplyAlpha)
    {
        testCopyTexture(GL_LUMINANCE_ALPHA, GL_RGBA, true, true, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLAToRGBAFlipYPremultiplyAlphaUnmultiplyAlpha)
    {
        testCopySubTexture(GL_LUMINANCE_ALPHA, GL_RGBA, true, true, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLAToBGRA)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_LUMINANCE_ALPHA, GL_BGRA_EXT, false, false, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLAToBGRA)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_LUMINANCE_ALPHA, GL_BGRA_EXT, false, false, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLAToBGRAUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_LUMINANCE_ALPHA, GL_BGRA_EXT, false, false, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLAToBGRAUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_LUMINANCE_ALPHA, GL_BGRA_EXT, false, false, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLAToBGRAPremultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_LUMINANCE_ALPHA, GL_BGRA_EXT, false, true, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLAToBGRAPremultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_LUMINANCE_ALPHA, GL_BGRA_EXT, false, true, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLAToBGRAPremultiplyAlphaUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_LUMINANCE_ALPHA, GL_BGRA_EXT, false, true, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLAToBGRAPremultiplyAlphaUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_LUMINANCE_ALPHA, GL_BGRA_EXT, false, true, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLAToBGRAFlipY)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_LUMINANCE_ALPHA, GL_BGRA_EXT, true, false, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLAToBGRAFlipY)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_LUMINANCE_ALPHA, GL_BGRA_EXT, true, false, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLAToBGRAFlipYUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_LUMINANCE_ALPHA, GL_BGRA_EXT, true, false, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLAToBGRAFlipYUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_LUMINANCE_ALPHA, GL_BGRA_EXT, true, false, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLAToBGRAFlipYPremultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_LUMINANCE_ALPHA, GL_BGRA_EXT, true, true, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLAToBGRAFlipYPremultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_LUMINANCE_ALPHA, GL_BGRA_EXT, true, true, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureLAToBGRAFlipYPremultiplyAlphaUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_LUMINANCE_ALPHA, GL_BGRA_EXT, true, true, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureLAToBGRAFlipYPremultiplyAlphaUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_LUMINANCE_ALPHA, GL_BGRA_EXT, true, true, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureBGRAToRGB)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_BGRA_EXT, GL_RGB, false, false, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureBGRAToRGB)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_BGRA_EXT, GL_RGB, false, false, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureBGRAToRGBUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_BGRA_EXT, GL_RGB, false, false, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureBGRAToRGBUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_BGRA_EXT, GL_RGB, false, false, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureBGRAToRGBPremultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_BGRA_EXT, GL_RGB, false, true, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureBGRAToRGBPremultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_BGRA_EXT, GL_RGB, false, true, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureBGRAToRGBPremultiplyAlphaUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_BGRA_EXT, GL_RGB, false, true, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureBGRAToRGBPremultiplyAlphaUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_BGRA_EXT, GL_RGB, false, true, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureBGRAToRGBFlipY)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_BGRA_EXT, GL_RGB, true, false, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureBGRAToRGBFlipY)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_BGRA_EXT, GL_RGB, true, false, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureBGRAToRGBFlipYUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_BGRA_EXT, GL_RGB, true, false, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureBGRAToRGBFlipYUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_BGRA_EXT, GL_RGB, true, false, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureBGRAToRGBFlipYPremultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_BGRA_EXT, GL_RGB, true, true, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureBGRAToRGBFlipYPremultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_BGRA_EXT, GL_RGB, true, true, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureBGRAToRGBFlipYPremultiplyAlphaUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_BGRA_EXT, GL_RGB, true, true, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureBGRAToRGBFlipYPremultiplyAlphaUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_BGRA_EXT, GL_RGB, true, true, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureBGRAToRGBA)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_BGRA_EXT, GL_RGBA, false, false, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureBGRAToRGBA)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_BGRA_EXT, GL_RGBA, false, false, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureBGRAToRGBAUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_BGRA_EXT, GL_RGBA, false, false, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureBGRAToRGBAUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_BGRA_EXT, GL_RGBA, false, false, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureBGRAToRGBAPremultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_BGRA_EXT, GL_RGBA, false, true, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureBGRAToRGBAPremultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_BGRA_EXT, GL_RGBA, false, true, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureBGRAToRGBAPremultiplyAlphaUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_BGRA_EXT, GL_RGBA, false, true, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureBGRAToRGBAPremultiplyAlphaUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_BGRA_EXT, GL_RGBA, false, true, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureBGRAToRGBAFlipY)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_BGRA_EXT, GL_RGBA, true, false, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureBGRAToRGBAFlipY)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_BGRA_EXT, GL_RGBA, true, false, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureBGRAToRGBAFlipYUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_BGRA_EXT, GL_RGBA, true, false, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureBGRAToRGBAFlipYUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_BGRA_EXT, GL_RGBA, true, false, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureBGRAToRGBAFlipYPremultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_BGRA_EXT, GL_RGBA, true, true, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureBGRAToRGBAFlipYPremultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_BGRA_EXT, GL_RGBA, true, true, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureBGRAToRGBAFlipYPremultiplyAlphaUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_BGRA_EXT, GL_RGBA, true, true, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureBGRAToRGBAFlipYPremultiplyAlphaUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_BGRA_EXT, GL_RGBA, true, true, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureBGRAToBGRA)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_BGRA_EXT, GL_BGRA_EXT, false, false, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureBGRAToBGRA)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_BGRA_EXT, GL_BGRA_EXT, false, false, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureBGRAToBGRAUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_BGRA_EXT, GL_BGRA_EXT, false, false, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureBGRAToBGRAUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_BGRA_EXT, GL_BGRA_EXT, false, false, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureBGRAToBGRAPremultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_BGRA_EXT, GL_BGRA_EXT, false, true, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureBGRAToBGRAPremultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_BGRA_EXT, GL_BGRA_EXT, false, true, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureBGRAToBGRAPremultiplyAlphaUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_BGRA_EXT, GL_BGRA_EXT, false, true, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureBGRAToBGRAPremultiplyAlphaUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_BGRA_EXT, GL_BGRA_EXT, false, true, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureBGRAToBGRAFlipY)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_BGRA_EXT, GL_BGRA_EXT, true, false, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureBGRAToBGRAFlipY)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_BGRA_EXT, GL_BGRA_EXT, true, false, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureBGRAToBGRAFlipYUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_BGRA_EXT, GL_BGRA_EXT, true, false, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureBGRAToBGRAFlipYUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_BGRA_EXT, GL_BGRA_EXT, true, false, true);
    }
    
    TEST_P(CopyTextureTest, CopyTextureBGRAToBGRAFlipYPremultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_BGRA_EXT, GL_BGRA_EXT, true, true, false);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureBGRAToBGRAFlipYPremultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_BGRA_EXT, GL_BGRA_EXT, true, true, false);
    }
    
    TEST_P(CopyTextureTest, CopyTextureBGRAToBGRAFlipYPremultiplyAlphaUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopyTexture(GL_BGRA_EXT, GL_BGRA_EXT, true, true, true);
    }
    
    TEST_P(CopyTextureTest, CopySubTextureBGRAToBGRAFlipYPremultiplyAlphaUnmultiplyAlpha)
    {
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
        testCopySubTexture(GL_BGRA_EXT, GL_BGRA_EXT, true, true, true);
    }
    
    // Test that copying to cube maps works
    TEST_P(CopyTextureTest, CubeMapTarget)
    {
        if (!checkExtensions())
        {
            return;
        }
    
        // http://anglebug.com/1932
        ANGLE_SKIP_TEST_IF(IsOSX() && IsIntel() && IsDesktopOpenGL());
    
        // http://anglebug.com/3145
        ANGLE_SKIP_TEST_IF(IsFuchsia() && IsIntel() && IsVulkan());
    
        GLColor pixels[7] = {
            GLColor(10u, 13u, 16u, 19u), GLColor(20u, 23u, 26u, 29u), GLColor(30u, 33u, 36u, 39u),
            GLColor(40u, 43u, 46u, 49u), GLColor(50u, 53u, 56u, 59u), GLColor(60u, 63u, 66u, 69u),
            GLColor(70u, 73u, 76u, 79u),
        };
    
        GLTexture textures[2];
    
        glBindTexture(GL_TEXTURE_CUBE_MAP, textures[1]);
        for (GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X; face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z;
             face++)
        {
            glTexImage2D(face, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
        }
    
        for (size_t i = 0; i < 2; ++i)
        {
            for (GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X; face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z;
                 face++)
            {
                glBindTexture(GL_TEXTURE_2D, textures[0]);
                glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                             &pixels[face - GL_TEXTURE_CUBE_MAP_POSITIVE_X + i]);
    
                glCopySubTextureCHROMIUM(textures[0], 0, face, textures[1], 0, 0, 0, 0, 0, 1, 1, false,
                                         false, false);
            }
    
            EXPECT_GL_NO_ERROR();
    
            GLFramebuffer fbo;
            glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    
            for (GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X; face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z;
                 face++)
            {
                glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, face, textures[1], 0);
    
                // Check that FB is complete.
                EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
    
                EXPECT_PIXEL_COLOR_EQ(0, 0, pixels[face - GL_TEXTURE_CUBE_MAP_POSITIVE_X + i]);
    
                EXPECT_GL_NO_ERROR();
            }
        }
    }
    
    // Test BGRA to RGBA cube map copy
    TEST_P(CopyTextureTest, CubeMapTargetBGRA)
    {
        if (!checkExtensions())
        {
            return;
        }
    
        if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            return;
        }
    
        // http://anglebug.com/3145
        ANGLE_SKIP_TEST_IF(IsFuchsia() && IsIntel() && IsVulkan());
    
        GLColor pixels[7] = {
            GLColor(10u, 13u, 16u, 19u), GLColor(20u, 23u, 26u, 29u), GLColor(30u, 33u, 36u, 39u),
            GLColor(40u, 43u, 46u, 49u), GLColor(50u, 53u, 56u, 59u), GLColor(60u, 63u, 66u, 69u),
            GLColor(70u, 73u, 76u, 79u),
        };
    
        GLTexture textures[2];
    
        glBindTexture(GL_TEXTURE_CUBE_MAP, textures[1]);
        for (GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X; face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z;
             face++)
        {
            glTexImage2D(face, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
        }
    
        for (size_t i = 0; i < 2; ++i)
        {
            for (GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X; face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z;
                 face++)
            {
                glBindTexture(GL_TEXTURE_2D, textures[0]);
                glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT, 1, 1, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE,
                             &pixels[face - GL_TEXTURE_CUBE_MAP_POSITIVE_X + i]);
    
                glCopySubTextureCHROMIUM(textures[0], 0, face, textures[1], 0, 0, 0, 0, 0, 1, 1, false,
                                         false, false);
            }
    
            EXPECT_GL_NO_ERROR();
    
            GLFramebuffer fbo;
            glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    
            for (GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X; face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z;
                 face++)
            {
                glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, face, textures[1], 0);
    
                // Check that FB is complete.
                EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
    
                GLColor converted = pixels[face - GL_TEXTURE_CUBE_MAP_POSITIVE_X + i];
                std::swap(converted.R, converted.B);
                EXPECT_PIXEL_COLOR_EQ(0, 0, converted);
    
                EXPECT_GL_NO_ERROR();
            }
        }
    }
    
    // Test cube map copies with RGB format
    TEST_P(CopyTextureTest, CubeMapTargetRGB)
    {
        if (!checkExtensions())
        {
            return;
        }
    
        // http://anglebug.com/1932
        ANGLE_SKIP_TEST_IF(IsOSX() && IsIntel() && IsDesktopOpenGL());
    
        // http://anglebug.com/3145
        ANGLE_SKIP_TEST_IF(IsFuchsia() && IsIntel() && IsVulkan());
    
        constexpr uint8_t pixels[16 * 7] = {
            0u,   3u,   6u,   10u,  13u,  16u,  0, 0, 20u,  23u,  26u,  30u,  33u,  36u,  0, 0,  // 2x2
            40u,  43u,  46u,  50u,  53u,  56u,  0, 0, 60u,  63u,  66u,  70u,  73u,  76u,  0, 0,  // 2x2
            80u,  83u,  86u,  90u,  93u,  96u,  0, 0, 100u, 103u, 106u, 110u, 113u, 116u, 0, 0,  // 2x2
            120u, 123u, 126u, 130u, 133u, 136u, 0, 0, 140u, 143u, 146u, 160u, 163u, 166u, 0, 0,  // 2x2
            170u, 173u, 176u, 180u, 183u, 186u, 0, 0, 190u, 193u, 196u, 200u, 203u, 206u, 0, 0,  // 2x2
            210u, 213u, 216u, 220u, 223u, 226u, 0, 0, 230u, 233u, 236u, 240u, 243u, 246u, 0, 0,  // 2x2
            10u,  50u,  100u, 30u,  80u,  130u, 0, 0, 60u,  110u, 160u, 90u,  140u, 200u, 0, 0,  // 2x2
        };
    
        GLTexture textures[2];
    
        glBindTexture(GL_TEXTURE_CUBE_MAP, textures[1]);
        for (GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X; face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z;
             face++)
        {
            glTexImage2D(face, 0, GL_RGB, 2, 2, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
        }
    
        for (size_t i = 0; i < 2; ++i)
        {
            for (GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X; face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z;
                 face++)
            {
                glBindTexture(GL_TEXTURE_2D, textures[0]);
                glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0, GL_RGB, GL_UNSIGNED_BYTE,
                             &pixels[(face - GL_TEXTURE_CUBE_MAP_POSITIVE_X + i) * 16]);
    
                glCopySubTextureCHROMIUM(textures[0], 0, face, textures[1], 0, 0, 0, 0, 0, 2, 2, false,
                                         false, false);
            }
    
            EXPECT_GL_NO_ERROR();
    
            GLFramebuffer fbo;
            glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    
            for (GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X; face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z;
                 face++)
            {
                glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, face, textures[1], 0);
    
                // Check that FB is complete.
                EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
    
                const uint8_t *faceData = &pixels[(face - GL_TEXTURE_CUBE_MAP_POSITIVE_X + i) * 16];
                EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor(faceData[0], faceData[1], faceData[2], 255));
                EXPECT_PIXEL_COLOR_EQ(1, 0, GLColor(faceData[3], faceData[4], faceData[5], 255));
                EXPECT_PIXEL_COLOR_EQ(0, 1, GLColor(faceData[8], faceData[9], faceData[10], 255));
                EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor(faceData[11], faceData[12], faceData[13], 255));
    
                EXPECT_GL_NO_ERROR();
            }
        }
    }
    
    // Test that copying to non-zero mipmaps works
    TEST_P(CopyTextureTest, CopyToMipmap)
    {
        if (!checkExtensions())
        {
            return;
        }
    
        ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 &&
                           !extensionEnabled("GL_OES_fbo_render_mipmap"));
    
        ANGLE_SKIP_TEST_IF(IsOSX() && IsIntel());
    
        GLColor pixels[] = {GLColor::red, GLColor::red, GLColor::red, GLColor::red};
    
        GLTexture textures[2];
    
        glBindTexture(GL_TEXTURE_2D, textures[0]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
        glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
    
        glBindTexture(GL_TEXTURE_2D, textures[1]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
        glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
        glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        std::vector<std::pair<GLint, GLint>> soureDestPairs;
        soureDestPairs.push_back(std::make_pair(0, 1));
    
        // ES3 allows copying from non-zero mips
        if (getClientMajorVersion() >= 3)
        {
            soureDestPairs.push_back(std::make_pair(1, 2));
        }
    
        for (const auto &sourceDestPair : soureDestPairs)
        {
            const GLint sourceLevel = sourceDestPair.first;
            const GLint destLevel   = sourceDestPair.second;
    
            glCopyTextureCHROMIUM(textures[0], sourceLevel, GL_TEXTURE_2D, textures[1], destLevel,
                                  GL_RGBA, GL_UNSIGNED_BYTE, false, false, false);
    
            EXPECT_GL_NO_ERROR();
    
            GLFramebuffer fbo;
            glBindFramebuffer(GL_FRAMEBUFFER, fbo);
            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textures[1],
                                   destLevel);
    
            // Check that FB is complete.
            EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
    
            EXPECT_PIXEL_COLOR_EQ(0, 0, pixels[0]);
    
            EXPECT_GL_NO_ERROR();
        }
    }
    
    // Test that copying from an RGBA8 texture to RGBA4 results in exactly 4-bit precision in the result
    TEST_P(CopyTextureTest, DownsampleRGBA4444)
    {
        // Downsampling on copy is only guarenteed on D3D11
        ANGLE_SKIP_TEST_IF(!IsD3D11());
    
        GLTexture textures[2];
    
        GLColor pixels[] = {GLColor(0, 5, 6, 7), GLColor(17, 22, 25, 24), GLColor(34, 35, 36, 36),
                            GLColor(51, 53, 55, 55)};
    
        glBindTexture(GL_TEXTURE_2D, textures[0]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
    
        glBindTexture(GL_TEXTURE_2D, textures[1]);
        glCopyTextureCHROMIUM(textures[0], 0, GL_TEXTURE_2D, textures[1], 0, GL_RGBA,
                              GL_UNSIGNED_SHORT_4_4_4_4, GL_FALSE, GL_FALSE, GL_FALSE);
    
        GLFramebuffer fbo;
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textures[1], 0);
    
        EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(0, 0, 0, 0), 1.0);
        EXPECT_PIXEL_COLOR_NEAR(1, 0, GLColor(17, 17, 17, 17), 1.0);
        EXPECT_PIXEL_COLOR_NEAR(0, 1, GLColor(34, 34, 34, 34), 1.0);
        EXPECT_PIXEL_COLOR_NEAR(1, 1, GLColor(51, 51, 51, 51), 1.0);
    
        testGradientDownsampleUniqueValues(GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, {16, 16, 16, 16});
    }
    
    // Test that copying from an RGBA8 texture to RGB565 results in exactly 4-bit precision in the
    // result
    TEST_P(CopyTextureTest, DownsampleRGB565)
    {
        // Downsampling on copy is only guarenteed on D3D11
        ANGLE_SKIP_TEST_IF(!IsD3D11());
    
        GLTexture textures[2];
    
        GLColor pixels[] = {GLColor(0, 5, 2, 14), GLColor(17, 22, 25, 30), GLColor(34, 33, 36, 46),
                            GLColor(50, 54, 49, 60)};
    
        glBindTexture(GL_TEXTURE_2D, textures[0]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
    
        glBindTexture(GL_TEXTURE_2D, textures[1]);
        glCopyTextureCHROMIUM(textures[0], 0, GL_TEXTURE_2D, textures[1], 0, GL_RGB,
                              GL_UNSIGNED_SHORT_5_6_5, GL_FALSE, GL_FALSE, GL_FALSE);
    
        GLFramebuffer fbo;
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textures[1], 0);
    
        EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(0, 4, 0, 255), 1.0);
        EXPECT_PIXEL_COLOR_NEAR(1, 0, GLColor(16, 20, 25, 255), 1.0);
        EXPECT_PIXEL_COLOR_NEAR(0, 1, GLColor(33, 32, 33, 255), 1.0);
        EXPECT_PIXEL_COLOR_NEAR(1, 1, GLColor(49, 53, 49, 255), 1.0);
    
        testGradientDownsampleUniqueValues(GL_RGB, GL_UNSIGNED_SHORT_5_6_5, {32, 64, 32, 1});
    }
    
    // Test that copying from an RGBA8 texture to RGBA5551 results in exactly 4-bit precision in the
    // result
    TEST_P(CopyTextureTest, DownsampleRGBA5551)
    {
        // Downsampling on copy is only guarenteed on D3D11
        ANGLE_SKIP_TEST_IF(!IsD3D11());
    
        GLTexture textures[2];
    
        GLColor pixels[] = {GLColor(0, 1, 2, 3), GLColor(14, 16, 17, 18), GLColor(33, 34, 36, 46),
                            GLColor(50, 51, 52, 255)};
    
        glBindTexture(GL_TEXTURE_2D, textures[0]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
    
        glBindTexture(GL_TEXTURE_2D, textures[1]);
        glCopyTextureCHROMIUM(textures[0], 0, GL_TEXTURE_2D, textures[1], 0, GL_RGBA,
                              GL_UNSIGNED_SHORT_5_5_5_1, GL_FALSE, GL_FALSE, GL_FALSE);
    
        GLFramebuffer fbo;
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textures[1], 0);
    
        EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(0, 0, 0, 0), 1.0);
        EXPECT_PIXEL_COLOR_NEAR(1, 0, GLColor(16, 16, 16, 0), 1.0);
        EXPECT_PIXEL_COLOR_NEAR(0, 1, GLColor(33, 33, 33, 0), 1.0);
        EXPECT_PIXEL_COLOR_NEAR(1, 1, GLColor(49, 49, 49, 255), 1.0);
    
        testGradientDownsampleUniqueValues(GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, {32, 32, 32, 2});
    }
    
    // Test to ensure that CopyTexture works with LUMINANCE texture as a destination
    TEST_P(CopyTextureTestDest, Luminance)
    {
        if (!checkExtensions())
        {
            return;
        }
    
        GLColor originalPixels(50u, 100u, 150u, 200u);
        GLColor expectedPixels(50u, 50u, 50u, 255u);
    
        // ReadPixels doesn't work with LUMINANCE (non-renderable), so we copy again back to an RGBA
        // texture to verify contents.
        glBindTexture(GL_TEXTURE_2D, mTextures[1]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &originalPixels);
        glBindTexture(GL_TEXTURE_2D, mTextures[0]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 1, 1, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, nullptr);
    
        glCopyTextureCHROMIUM(mTextures[1], 0, GL_TEXTURE_2D, mTextures[0], 0, GL_LUMINANCE,
                              GL_UNSIGNED_BYTE, false, false, false);
    
        EXPECT_GL_NO_ERROR();
    
        glCopyTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, GL_RGBA,
                              GL_UNSIGNED_BYTE, false, false, false);
    
        EXPECT_GL_NO_ERROR();
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, expectedPixels);
    }
    
    // Test to ensure that CopyTexture works with LUMINANCE texture as a destination with
    // UnpackPremultiply parameter
    TEST_P(CopyTextureTestDest, LuminanceMultiply)
    {
        if (!checkExtensions())
        {
            return;
        }
    
        GLColor originalPixels(50u, 100u, 150u, 200u);
        GLColor expectedPixels(39u, 39u, 39u, 255u);
    
        // ReadPixels doesn't work with LUMINANCE (non-renderable), so we copy again back to an RGBA
        // texture to verify contents.
        glBindTexture(GL_TEXTURE_2D, mTextures[1]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &originalPixels);
        glBindTexture(GL_TEXTURE_2D, mTextures[0]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 1, 1, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, nullptr);
    
        glCopyTextureCHROMIUM(mTextures[1], 0, GL_TEXTURE_2D, mTextures[0], 0, GL_LUMINANCE,
                              GL_UNSIGNED_BYTE, false, true, false);
    
        EXPECT_GL_NO_ERROR();
    
        glCopyTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, GL_RGBA,
                              GL_UNSIGNED_BYTE, false, false, false);
    
        EXPECT_GL_NO_ERROR();
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, expectedPixels);
    }
    
    // Test to ensure that CopyTexture works with LUMINANCE texture as a destination with
    // UnpackUnmultiply parameter
    TEST_P(CopyTextureTestDest, LuminanceUnmultiply)
    {
        if (!checkExtensions())
        {
            return;
        }
    
        GLColor originalPixels(50u, 100u, 150u, 200u);
        GLColor expectedPixels(64u, 64u, 64u, 255u);
    
        // ReadPixels doesn't work with LUMINANCE (non-renderable), so we copy again back to an RGBA
        // texture to verify contents.
        glBindTexture(GL_TEXTURE_2D, mTextures[1]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &originalPixels);
        glBindTexture(GL_TEXTURE_2D, mTextures[0]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 1, 1, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, nullptr);
    
        glCopyTextureCHROMIUM(mTextures[1], 0, GL_TEXTURE_2D, mTextures[0], 0, GL_LUMINANCE,
                              GL_UNSIGNED_BYTE, false, false, true);
    
        EXPECT_GL_NO_ERROR();
    
        glCopyTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, GL_RGBA,
                              GL_UNSIGNED_BYTE, false, false, false);
    
        EXPECT_GL_NO_ERROR();
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, expectedPixels);
    }
    
    // Test to ensure that CopyTexture works with LUMINANCE_ALPHA texture as a destination
    TEST_P(CopyTextureTestDest, LuminanceAlpha)
    {
        if (!checkExtensions())
        {
            return;
        }
    
        GLColor originalPixels(50u, 100u, 150u, 200u);
        GLColor expectedPixels(50u, 50u, 50u, 200u);
    
        // ReadPixels doesn't work with LUMINANCE_ALPHA (non-renderable), so we copy again back to an
        // RGBA texture to verify contents.
        glBindTexture(GL_TEXTURE_2D, mTextures[1]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &originalPixels);
        glBindTexture(GL_TEXTURE_2D, mTextures[0]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, 1, 1, 0, GL_LUMINANCE_ALPHA,
                     GL_UNSIGNED_BYTE, nullptr);
    
        glCopyTextureCHROMIUM(mTextures[1], 0, GL_TEXTURE_2D, mTextures[0], 0, GL_LUMINANCE_ALPHA,
                              GL_UNSIGNED_BYTE, false, false, false);
    
        EXPECT_GL_NO_ERROR();
    
        glCopyTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, GL_RGBA,
                              GL_UNSIGNED_BYTE, false, false, false);
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, expectedPixels);
    }
    
    // Test to ensure that CopyTexture works with LUMINANCE_ALPHA texture as a destination with
    // UnpackPremultiply parameter
    TEST_P(CopyTextureTestDest, LuminanceAlphaMultiply)
    {
        if (!checkExtensions())
        {
            return;
        }
    
        GLColor originalPixels(50u, 100u, 150u, 200u);
        GLColor expectedPixels(39u, 39u, 39u, 200u);
    
        // ReadPixels doesn't work with LUMINANCE_ALPHA (non-renderable), so we copy again back to an
        // RGBA texture to verify contents.
        glBindTexture(GL_TEXTURE_2D, mTextures[1]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &originalPixels);
        glBindTexture(GL_TEXTURE_2D, mTextures[0]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, 1, 1, 0, GL_LUMINANCE_ALPHA,
                     GL_UNSIGNED_BYTE, nullptr);
    
        glCopyTextureCHROMIUM(mTextures[1], 0, GL_TEXTURE_2D, mTextures[0], 0, GL_LUMINANCE_ALPHA,
                              GL_UNSIGNED_BYTE, false, true, false);
    
        EXPECT_GL_NO_ERROR();
    
        glCopyTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, GL_RGBA,
                              GL_UNSIGNED_BYTE, false, false, false);
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, expectedPixels);
    }
    
    // Test to ensure that CopyTexture works with LUMINANCE_ALPHA texture as a destination with
    // UnpackUnmultiplyAlpha parameter
    TEST_P(CopyTextureTestDest, LuminanceAlphaUnmultiply)
    {
        if (!checkExtensions())
        {
            return;
        }
    
        GLColor originalPixels(50u, 100u, 150u, 200u);
        GLColor expectedPixels(64u, 64u, 64u, 200u);
    
        // ReadPixels doesn't work with LUMINANCE_ALPHA (non-renderable), so we copy again back to an
        // RGBA texture to verify contents.
        glBindTexture(GL_TEXTURE_2D, mTextures[1]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &originalPixels);
        glBindTexture(GL_TEXTURE_2D, mTextures[0]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, 1, 1, 0, GL_LUMINANCE_ALPHA,
                     GL_UNSIGNED_BYTE, nullptr);
    
        glCopyTextureCHROMIUM(mTextures[1], 0, GL_TEXTURE_2D, mTextures[0], 0, GL_LUMINANCE_ALPHA,
                              GL_UNSIGNED_BYTE, false, false, true);
    
        EXPECT_GL_NO_ERROR();
    
        glCopyTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, GL_RGBA,
                              GL_UNSIGNED_BYTE, false, false, false);
    
        EXPECT_GL_NO_ERROR();
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, expectedPixels);
    }
    
    // Test to ensure that CopyTexture works with ALPHA texture as a destination
    TEST_P(CopyTextureTestDest, Alpha)
    {
        if (!checkExtensions())
        {
            return;
        }
    
        GLColor originalPixels(50u, 100u, 150u, 155u);
        GLColor expectedPixels(0u, 0u, 0u, 155u);
    
        // ReadPixels doesn't work with ALPHA (non-renderable), so we copy again back to an RGBA
        // texture to verify contents.
        glBindTexture(GL_TEXTURE_2D, mTextures[1]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &originalPixels);
        glBindTexture(GL_TEXTURE_2D, mTextures[0]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 1, 1, 0, GL_ALPHA, GL_UNSIGNED_BYTE, nullptr);
    
        glCopyTextureCHROMIUM(mTextures[1], 0, GL_TEXTURE_2D, mTextures[0], 0, GL_ALPHA,
                              GL_UNSIGNED_BYTE, false, false, false);
    
        EXPECT_GL_NO_ERROR();
    
        glCopyTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, GL_RGBA,
                              GL_UNSIGNED_BYTE, false, false, false);
    
        EXPECT_GL_NO_ERROR();
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, expectedPixels);
    }
    
    // Test to ensure that CopyTexture works with ALPHA texture as a destination with
    // UnpackPremultiplyAlpha parameter
    TEST_P(CopyTextureTestDest, AlphaMultiply)
    {
        if (!checkExtensions())
        {
            return;
        }
    
        GLColor originalPixels(50u, 100u, 150u, 155u);
        GLColor expectedPixels(0u, 0u, 0u, 155u);
    
        // ReadPixels doesn't work with ALPHA (non-renderable), so we copy again back to an RGBA
        // texture to verify contents.
        glBindTexture(GL_TEXTURE_2D, mTextures[1]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &originalPixels);
        glBindTexture(GL_TEXTURE_2D, mTextures[0]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 1, 1, 0, GL_ALPHA, GL_UNSIGNED_BYTE, nullptr);
    
        glCopyTextureCHROMIUM(mTextures[1], 0, GL_TEXTURE_2D, mTextures[0], 0, GL_ALPHA,
                              GL_UNSIGNED_BYTE, false, true, false);
    
        EXPECT_GL_NO_ERROR();
    
        glCopyTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, GL_RGBA,
                              GL_UNSIGNED_BYTE, false, false, false);
    
        EXPECT_GL_NO_ERROR();
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, expectedPixels);
    }
    
    // Test to ensure that CopyTexture works with ALPHA texture as a destination with
    // UnpackUnmultiplyAlpha parameter
    TEST_P(CopyTextureTestDest, AlphaUnmultiply)
    {
        if (!checkExtensions())
        {
            return;
        }
    
        GLColor originalPixels(50u, 100u, 150u, 155u);
        GLColor expectedPixels(0u, 0u, 0u, 155u);
    
        // ReadPixels doesn't work with ALPHA (non-renderable), so we copy again back to an RGBA
        // texture to verify contents.
        glBindTexture(GL_TEXTURE_2D, mTextures[1]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &originalPixels);
        glBindTexture(GL_TEXTURE_2D, mTextures[0]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 1, 1, 0, GL_ALPHA, GL_UNSIGNED_BYTE, nullptr);
    
        glCopyTextureCHROMIUM(mTextures[1], 0, GL_TEXTURE_2D, mTextures[0], 0, GL_ALPHA,
                              GL_UNSIGNED_BYTE, false, false, true);
    
        EXPECT_GL_NO_ERROR();
    
        glCopyTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, GL_RGBA,
                              GL_UNSIGNED_BYTE, false, false, false);
    
        EXPECT_GL_NO_ERROR();
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, expectedPixels);
    }
    
    // Test to ensure that CopyTexture uses the correct ALPHA passthrough shader to ensure RGB channels
    // are set to 0.
    TEST_P(CopyTextureTestDest, AlphaCopyWithRGB)
    {
        ANGLE_SKIP_TEST_IF(!checkExtensions());
    
        GLColor originalPixels(50u, 100u, 150u, 155u);
        GLColor expectedPixels(0u, 0u, 0u, 155u);
    
        // ReadPixels doesn't work with ALPHA (non-renderable), so we copy again back to an RGBA
        // texture to verify contents.
        glBindTexture(GL_TEXTURE_2D, mTextures[1]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &originalPixels);
        glBindTexture(GL_TEXTURE_2D, mTextures[0]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 1, 1, 0, GL_ALPHA, GL_HALF_FLOAT_OES, nullptr);
    
        glCopyTextureCHROMIUM(mTextures[1], 0, GL_TEXTURE_2D, mTextures[0], 0, GL_ALPHA,
                              GL_HALF_FLOAT_OES, false, false, false);
    
        EXPECT_GL_NO_ERROR();
    
        glCopyTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, GL_RGBA,
                              GL_UNSIGNED_BYTE, false, false, false);
    
        EXPECT_GL_NO_ERROR();
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, expectedPixels);
    }
    
    // Test to ensure that CopyTexture will fail with a non-zero level and NPOT texture in WebGL
    TEST_P(CopyTextureTestWebGL, NPOT)
    {
        if (extensionRequestable("GL_CHROMIUM_copy_texture"))
        {
            glRequestExtensionANGLE("GL_CHROMIUM_copy_texture");
        }
        ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_CHROMIUM_copy_texture"));
    
        std::vector<GLColor> pixelData(10 * 10, GLColor::red);
    
        glBindTexture(GL_TEXTURE_2D, mTextures[0]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 10, 10, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixelData.data());
    
        // Do a basic copy to make sure things work
        glCopyTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, GL_RGBA,
                              GL_UNSIGNED_BYTE, false, false, false);
    
        EXPECT_GL_NO_ERROR();
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
    
        // Do the same operation with destLevel 1, which should fail
        glCopyTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 1, GL_RGBA,
                              GL_UNSIGNED_BYTE, false, false, false);
    
        EXPECT_GL_ERROR(GL_INVALID_VALUE);
    }
    
    // Test the newly added ES3 unorm formats
    TEST_P(CopyTextureTestES3, ES3UnormFormats)
    {
        if (!checkExtensions())
        {
            return;
        }
    
        auto testOutput = [this](GLuint texture, const GLColor &expectedColor) {
            constexpr char kVS[] =
                "#version 300 es\n"
                "in vec4 position;\n"
                "out vec2 texcoord;\n"
                "void main()\n"
                "{\n"
                "    gl_Position = vec4(position.xy, 0.0, 1.0);\n"
                "    texcoord = (position.xy * 0.5) + 0.5;\n"
                "}\n";
    
            constexpr char kFS[] =
                "#version 300 es\n"
                "precision mediump float;\n"
                "uniform sampler2D tex;\n"
                "in vec2 texcoord;\n"
                "out vec4 color;\n"
                "void main()\n"
                "{\n"
                "    color = texture(tex, texcoord);\n"
                "}\n";
    
            ANGLE_GL_PROGRAM(program, kVS, kFS);
            glUseProgram(program);
    
            GLRenderbuffer rbo;
            glBindRenderbuffer(GL_RENDERBUFFER, rbo);
            glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 1, 1);
    
            GLFramebuffer fbo;
            glBindFramebuffer(GL_FRAMEBUFFER, fbo);
            glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo);
    
            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D, texture);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
            glUniform1i(glGetUniformLocation(program.get(), "tex"), 0);
    
            drawQuad(program, "position", 0.5f, 1.0f, true);
    
            EXPECT_PIXEL_COLOR_NEAR(0, 0, expectedColor, 1.0);
        };
    
        auto testCopyCombination = [this, testOutput](GLenum sourceInternalFormat, GLenum sourceFormat,
                                                      GLenum sourceType, const GLColor &sourceColor,
                                                      GLenum destInternalFormat, GLenum destType,
                                                      bool flipY, bool premultiplyAlpha,
                                                      bool unmultiplyAlpha,
                                                      const GLColor &expectedColor) {
            GLTexture sourceTexture;
            glBindTexture(GL_TEXTURE_2D, sourceTexture);
            glTexImage2D(GL_TEXTURE_2D, 0, sourceInternalFormat, 1, 1, 0, sourceFormat, sourceType,
                         &sourceColor);
    
            GLTexture destTexture;
            glBindTexture(GL_TEXTURE_2D, destTexture);
    
            glCopyTextureCHROMIUM(sourceTexture, 0, GL_TEXTURE_2D, destTexture, 0, destInternalFormat,
                                  destType, flipY, premultiplyAlpha, unmultiplyAlpha);
            ASSERT_GL_NO_ERROR();
    
            testOutput(destTexture, expectedColor);
        };
    
        auto testSubCopyCombination = [this, testOutput](
                                          GLenum sourceInternalFormat, GLenum sourceFormat,
                                          GLenum sourceType, const GLColor &sourceColor,
                                          GLenum destInternalFormat, GLenum destFormat, GLenum destType,
                                          bool flipY, bool premultiplyAlpha, bool unmultiplyAlpha,
                                          const GLColor &expectedColor) {
            GLTexture sourceTexture;
            glBindTexture(GL_TEXTURE_2D, sourceTexture);
            glTexImage2D(GL_TEXTURE_2D, 0, sourceInternalFormat, 1, 1, 0, sourceFormat, sourceType,
                         &sourceColor);
    
            GLTexture destTexture;
            glBindTexture(GL_TEXTURE_2D, destTexture);
    
            glTexImage2D(GL_TEXTURE_2D, 0, destInternalFormat, 1, 1, 0, destFormat, destType, nullptr);
            glCopySubTextureCHROMIUM(sourceTexture, 0, GL_TEXTURE_2D, destTexture, 0, 0, 0, 0, 0, 1, 1,
                                     flipY, premultiplyAlpha, unmultiplyAlpha);
            ASSERT_GL_NO_ERROR();
    
            testOutput(destTexture, expectedColor);
        };
    
        // New LUMA source formats
        testCopyCombination(GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE, GLColor(128, 0, 0, 0), GL_RGB,
                            GL_UNSIGNED_BYTE, false, false, false, GLColor(128, 128, 128, 255));
        testCopyCombination(GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE,
                            GLColor(128, 64, 0, 0), GL_RGB, GL_UNSIGNED_BYTE, false, false, false,
                            GLColor(128, 128, 128, 255));
        testCopyCombination(GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE,
                            GLColor(128, 64, 0, 0), GL_RGB, GL_UNSIGNED_BYTE, false, true, false,
                            GLColor(32, 32, 32, 255));
        testCopyCombination(GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE,
                            GLColor(128, 128, 0, 0), GL_RGB, GL_UNSIGNED_BYTE, false, false, true,
                            GLColor(255, 255, 255, 255));
        testCopyCombination(GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE, GLColor(128, 0, 0, 0), GL_RGBA,
                            GL_UNSIGNED_BYTE, false, false, false, GLColor(0, 0, 0, 128));
        testCopyCombination(GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE, GLColor(128, 0, 0, 0), GL_RGBA,
                            GL_UNSIGNED_BYTE, false, false, true, GLColor(0, 0, 0, 128));
        testCopyCombination(GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE, GLColor(128, 0, 0, 0), GL_RGBA,
                            GL_UNSIGNED_BYTE, false, true, false, GLColor(0, 0, 0, 128));
    
        // New sRGB dest formats
        if (extensionEnabled("GL_EXT_sRGB"))
        {
            testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_SRGB,
                                GL_UNSIGNED_BYTE, false, false, false, GLColor(55, 13, 4, 255));
            testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_SRGB,
                                GL_UNSIGNED_BYTE, false, true, false, GLColor(13, 4, 1, 255));
            testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128),
                                GL_SRGB_ALPHA_EXT, GL_UNSIGNED_BYTE, false, false, false,
                                GLColor(55, 13, 4, 128));
    
            testSubCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128),
                                   GL_SRGB, GL_SRGB, GL_UNSIGNED_BYTE, false, false, false,
                                   GLColor(55, 13, 4, 255));
            testSubCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128),
                                   GL_SRGB, GL_SRGB, GL_UNSIGNED_BYTE, false, true, false,
                                   GLColor(13, 4, 1, 255));
            testSubCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128),
                                   GL_SRGB_ALPHA_EXT, GL_SRGB_ALPHA_EXT, GL_UNSIGNED_BYTE, false, false,
                                   false, GLColor(55, 13, 4, 128));
        }
    }
    
    // Test the newly added ES3 float formats
    TEST_P(CopyTextureTestES3, ES3FloatFormats)
    {
        if (!checkExtensions())
        {
            return;
        }
    
        ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_EXT_color_buffer_float"));
    
        auto testOutput = [this](GLuint texture, const GLColor32F &expectedColor) {
            constexpr char kVS[] =
                "#version 300 es\n"
                "in vec4 position;\n"
                "out vec2 texcoord;\n"
                "void main()\n"
                "{\n"
                "    gl_Position = vec4(position.xy, 0.0, 1.0);\n"
                "    texcoord = (position.xy * 0.5) + 0.5;\n"
                "}\n";
    
            constexpr char kFS[] =
                "#version 300 es\n"
                "precision mediump float;\n"
                "uniform sampler2D tex;\n"
                "in vec2 texcoord;\n"
                "out vec4 color;\n"
                "void main()\n"
                "{\n"
                "    color = texture(tex, texcoord);\n"
                "}\n";
    
            ANGLE_GL_PROGRAM(program, kVS, kFS);
            glUseProgram(program);
    
            GLRenderbuffer rbo;
            glBindRenderbuffer(GL_RENDERBUFFER, rbo);
            glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA32F, 1, 1);
    
            GLFramebuffer fbo;
            glBindFramebuffer(GL_FRAMEBUFFER, fbo);
            glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo);
    
            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D, texture);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
            glUniform1i(glGetUniformLocation(program.get(), "tex"), 0);
    
            drawQuad(program, "position", 0.5f, 1.0f, true);
    
            EXPECT_PIXEL_COLOR32F_NEAR(0, 0, expectedColor, 0.05);
        };
    
        auto testCopyCombination = [this, testOutput](GLenum sourceInternalFormat, GLenum sourceFormat,
                                                      GLenum sourceType, const GLColor &sourceColor,
                                                      GLenum destInternalFormat, GLenum destType,
                                                      bool flipY, bool premultiplyAlpha,
                                                      bool unmultiplyAlpha,
                                                      const GLColor32F &expectedColor) {
            GLTexture sourceTexture;
            glBindTexture(GL_TEXTURE_2D, sourceTexture);
            glTexImage2D(GL_TEXTURE_2D, 0, sourceInternalFormat, 1, 1, 0, sourceFormat, sourceType,
                         &sourceColor);
    
            GLTexture destTexture;
            glBindTexture(GL_TEXTURE_2D, destTexture);
    
            glCopyTextureCHROMIUM(sourceTexture, 0, GL_TEXTURE_2D, destTexture, 0, destInternalFormat,
                                  destType, flipY, premultiplyAlpha, unmultiplyAlpha);
            ASSERT_GL_NO_ERROR();
    
            testOutput(destTexture, expectedColor);
        };
    
        testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RGBA32F,
                            GL_FLOAT, false, false, false, GLColor32F(0.5f, 0.25f, 0.125f, 0.5f));
        testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RGBA32F,
                            GL_FLOAT, false, true, false, GLColor32F(0.25f, 0.125f, 0.0625f, 0.5f));
        testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RGBA32F,
                            GL_FLOAT, false, false, true, GLColor32F(1.0f, 0.5f, 0.25f, 0.5f));
    
        testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_R16F,
                            GL_FLOAT, false, false, false, GLColor32F(0.5f, 0.0f, 0.0f, 1.0f));
        testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_R16F,
                            GL_FLOAT, false, true, false, GLColor32F(0.25f, 0.0f, 0.0f, 1.0f));
        testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_R16F,
                            GL_FLOAT, false, false, true, GLColor32F(1.0f, 0.0f, 0.0f, 1.0f));
    
        testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RG16F,
                            GL_FLOAT, false, false, false, GLColor32F(0.5f, 0.25f, 0.0f, 1.0f));
        testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RG16F,
                            GL_FLOAT, false, true, false, GLColor32F(0.25f, 0.125f, 0.0f, 1.0f));
        testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RG16F,
                            GL_FLOAT, false, false, true, GLColor32F(1.0f, 0.5f, 0.0f, 1.0f));
    
        testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RGB16F,
                            GL_FLOAT, false, false, false, GLColor32F(0.5f, 0.25f, 0.125f, 1.0f));
        testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RGB16F,
                            GL_FLOAT, false, true, false, GLColor32F(0.25f, 0.125f, 0.0625f, 1.0f));
        testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RGB16F,
                            GL_FLOAT, false, false, true, GLColor32F(1.0f, 0.5f, 0.25f, 1.0f));
    
        testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128),
                            GL_R11F_G11F_B10F, GL_FLOAT, false, false, false,
                            GLColor32F(0.5f, 0.25f, 0.125f, 1.0f));
        testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128),
                            GL_R11F_G11F_B10F, GL_FLOAT, false, true, false,
                            GLColor32F(0.25f, 0.125f, 0.0625f, 1.0f));
        testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128),
                            GL_R11F_G11F_B10F, GL_FLOAT, false, false, true,
                            GLColor32F(1.0f, 0.5f, 0.25f, 1.0f));
    
        testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RGB9_E5,
                            GL_FLOAT, false, false, false, GLColor32F(0.5f, 0.25f, 0.125f, 1.0f));
        testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RGB9_E5,
                            GL_FLOAT, false, true, false, GLColor32F(0.25f, 0.125f, 0.0625f, 1.0f));
        testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RGB9_E5,
                            GL_FLOAT, false, false, true, GLColor32F(1.0f, 0.5f, 0.25f, 1.0f));
    }
    
    // Test the newly added ES3 unsigned integer formats
    TEST_P(CopyTextureTestES3, ES3UintFormats)
    {
        ANGLE_SKIP_TEST_IF(IsLinux() && IsOpenGL() && IsIntel());
    
        if (!checkExtensions())
        {
            return;
        }
    
        using GLColor32U = std::tuple<GLuint, GLuint, GLuint, GLuint>;
    
        auto testOutput = [this](GLuint texture, const GLColor32U &expectedColor) {
            constexpr char kVS[] =
                "#version 300 es\n"
                "in vec4 position;\n"
                "out vec2 texcoord;\n"
                "void main()\n"
                "{\n"
                "    gl_Position = vec4(position.xy, 0.0, 1.0);\n"
                "    texcoord = (position.xy * 0.5) + 0.5;\n"
                "}\n";
    
            constexpr char kFS[] =
                "#version 300 es\n"
                "precision mediump float;\n"
                "precision mediump usampler2D;\n"
                "in vec2 texcoord;\n"
                "uniform usampler2D tex;\n"
                "out uvec4 color;\n"
                "void main()\n"
                "{\n"
                "    color = texture(tex, texcoord);\n"
                "}\n";
    
            ANGLE_GL_PROGRAM(program, kVS, kFS);
            glUseProgram(program);
    
            GLRenderbuffer rbo;
            glBindRenderbuffer(GL_RENDERBUFFER, rbo);
            glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8UI, 1, 1);
    
            GLFramebuffer fbo;
            glBindFramebuffer(GL_FRAMEBUFFER, fbo);
            glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo);
    
            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D, texture);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
            glUniform1i(glGetUniformLocation(program.get(), "tex"), 0);
    
            drawQuad(program, "position", 0.5f, 1.0f, true);
            ASSERT_GL_NO_ERROR();
    
            GLuint pixel[4] = {0};
            glReadPixels(0, 0, 1, 1, GL_RGBA_INTEGER, GL_UNSIGNED_INT, pixel);
            ASSERT_GL_NO_ERROR();
            EXPECT_NEAR(std::get<0>(expectedColor), pixel[0], 1);
            EXPECT_NEAR(std::get<1>(expectedColor), pixel[1], 1);
            EXPECT_NEAR(std::get<2>(expectedColor), pixel[2], 1);
            EXPECT_NEAR(std::get<3>(expectedColor), pixel[3], 1);
        };
    
        auto testCopyCombination = [this, testOutput](GLenum sourceInternalFormat, GLenum sourceFormat,
                                                      GLenum sourceType, const GLColor &sourceColor,
                                                      GLenum destInternalFormat, GLenum destType,
                                                      bool flipY, bool premultiplyAlpha,
                                                      bool unmultiplyAlpha,
                                                      const GLColor32U &expectedColor) {
            GLTexture sourceTexture;
            glBindTexture(GL_TEXTURE_2D, sourceTexture);
            glTexImage2D(GL_TEXTURE_2D, 0, sourceInternalFormat, 1, 1, 0, sourceFormat, sourceType,
                         &sourceColor);
    
            GLTexture destTexture;
            glBindTexture(GL_TEXTURE_2D, destTexture);
    
            glCopyTextureCHROMIUM(sourceTexture, 0, GL_TEXTURE_2D, destTexture, 0, destInternalFormat,
                                  destType, flipY, premultiplyAlpha, unmultiplyAlpha);
            ASSERT_GL_NO_ERROR();
    
            testOutput(destTexture, expectedColor);
        };
    
        testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RGBA8UI,
                            GL_UNSIGNED_BYTE, false, false, false, GLColor32U(128, 64, 32, 128));
        testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RGBA8UI,
                            GL_UNSIGNED_BYTE, false, true, false, GLColor32U(64, 32, 16, 128));
        testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RGBA8UI,
                            GL_UNSIGNED_BYTE, false, false, true, GLColor32U(255, 128, 64, 128));
    
        testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RGB8UI,
                            GL_UNSIGNED_BYTE, false, false, false, GLColor32U(128, 64, 32, 1));
        testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RGB8UI,
                            GL_UNSIGNED_BYTE, false, true, false, GLColor32U(64, 32, 16, 1));
        testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RGB8UI,
                            GL_UNSIGNED_BYTE, false, false, true, GLColor32U(255, 128, 64, 1));
    
        testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RG8UI,
                            GL_UNSIGNED_BYTE, false, false, false, GLColor32U(128, 64, 0, 1));
        testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RG8UI,
                            GL_UNSIGNED_BYTE, false, true, false, GLColor32U(64, 32, 0, 1));
        testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RG8UI,
                            GL_UNSIGNED_BYTE, false, false, true, GLColor32U(255, 128, 0, 1));
    
        testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_R8UI,
                            GL_UNSIGNED_BYTE, false, false, false, GLColor32U(128, 0, 0, 1));
        testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_R8UI,
                            GL_UNSIGNED_BYTE, false, true, false, GLColor32U(64, 0, 0, 1));
        testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(120, 64, 32, 128), GL_R8UI,
                            GL_UNSIGNED_BYTE, false, false, true, GLColor32U(240, 0, 0, 1));
    }
    
    // Use this to select which configurations (e.g. which renderer, which GLES major version) these
    // tests should be run against.
    ANGLE_INSTANTIATE_TEST(CopyTextureTest,
                           ES2_D3D9(),
                           ES2_D3D11(),
                           ES2_OPENGL(),
                           ES2_OPENGLES(),
                           ES2_VULKAN());
    ANGLE_INSTANTIATE_TEST(CopyTextureTestWebGL,
                           ES2_D3D9(),
                           ES2_D3D11(),
                           ES2_OPENGL(),
                           ES2_OPENGLES(),
                           ES2_VULKAN());
    ANGLE_INSTANTIATE_TEST(CopyTextureTestDest, ES2_D3D11());
    ANGLE_INSTANTIATE_TEST(CopyTextureTestES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
    
    }  // namespace angle