Implement the new formats/features of the ES3 CHROMIUM_copy_texture.

Some non-renderable texture formats remain unimplemented.

BUG=angleproject:1932

Change-Id: Id206432d6e26a70fc0e84478a4e43e9eefadcf2f
Reviewed-on: https://chromium-review.googlesource.com/491948
Commit-Queue: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
diff --git a/src/tests/gl_tests/CopyTextureTest.cpp b/src/tests/gl_tests/CopyTextureTest.cpp
index bd5e72e..ba2a2b3 100644
--- a/src/tests/gl_tests/CopyTextureTest.cpp
+++ b/src/tests/gl_tests/CopyTextureTest.cpp
@@ -8,6 +8,8 @@
 
 #include "test_utils/ANGLETest.h"
 
+#include "test_utils/gl_raii.h"
+
 namespace angle
 {
 
@@ -81,6 +83,10 @@
     PFNGLCOPYSUBTEXTURECHROMIUMPROC glCopySubTextureCHROMIUM = nullptr;
 };
 
+class CopyTextureTestES3 : public CopyTextureTest
+{
+};
+
 // Test to ensure that the basic functionality of the extension works.
 TEST_P(CopyTextureTest, BasicCopyTexture)
 {
@@ -243,47 +249,6 @@
     }
 }
 
-// Check that invalid internal formats return errors.
-TEST_P(CopyTextureTest, InternalFormatNotSupported)
-{
-    if (!checkExtensions())
-    {
-        return;
-    }
-
-    glBindTexture(GL_TEXTURE_2D, mTextures[0]);
-    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
-    EXPECT_GL_NO_ERROR();
-
-    std::vector<GLint> unsupportedDestFormats;
-    unsupportedDestFormats.push_back(GL_ALPHA);
-    unsupportedDestFormats.push_back(GL_LUMINANCE);
-    unsupportedDestFormats.push_back(GL_LUMINANCE_ALPHA);
-
-    if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
-    {
-        unsupportedDestFormats.push_back(GL_BGRA_EXT);
-    }
-
-    // Check unsupported format reports an error.
-    for (GLint unsupportedDestFormat : unsupportedDestFormats)
-    {
-        glCopyTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0,
-                              unsupportedDestFormat, GL_UNSIGNED_BYTE, false, false, false);
-        EXPECT_GL_ERROR(GL_INVALID_OPERATION);
-    }
-
-    for (GLint unsupportedDestFormat : unsupportedDestFormats)
-    {
-        glBindTexture(GL_TEXTURE_2D, mTextures[1]);
-        glTexImage2D(GL_TEXTURE_2D, 0, unsupportedDestFormat, 1, 1, 0, unsupportedDestFormat,
-                     GL_UNSIGNED_BYTE, nullptr);
-        glCopySubTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, 0, 0, 0, 0, 1, 1,
-                                 false, false, false);
-        EXPECT_GL_ERROR(GL_INVALID_OPERATION);
-    }
-}
-
 // Test to ensure that the destination texture is redefined if the properties are different.
 TEST_P(CopyTextureTest, RedefineDestinationTexture)
 {
@@ -645,8 +610,464 @@
     EXPECT_PIXEL_COLOR_EQ(0, 0, expectedPixels);
 }
 
+// Test that copying to cube maps works
+TEST_P(CopyTextureTest, CubeMapTarget)
+{
+    if (!checkExtensions())
+    {
+        return;
+    }
+
+    GLColor pixels = GLColor::red;
+
+    GLTexture textures[2];
+
+    glBindTexture(GL_TEXTURE_2D, textures[0]);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &pixels);
+
+    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);
+    }
+
+    glCopySubTextureCHROMIUM(textures[0], 0, GL_TEXTURE_CUBE_MAP_POSITIVE_X, textures[1], 0, 0, 0,
+                             0, 0, 1, 1, false, false, false);
+
+    EXPECT_GL_NO_ERROR();
+
+    GLFramebuffer fbo;
+    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X,
+                           textures[1], 0);
+
+    // 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 that copying to non-zero mipmaps works
+TEST_P(CopyTextureTestES3, CopyToMipmap)
+{
+    if (!checkExtensions())
+    {
+        return;
+    }
+
+    if (IsOSX() && IsIntel())
+    {
+        std::cout << "Test skipped on Mac Intel." << std::endl;
+        return;
+    }
+
+    GLColor pixels = GLColor::red;
+
+    GLTexture textures[2];
+
+    const GLint sourceLevel = 1;
+    const GLint destLevel   = 2;
+
+    glBindTexture(GL_TEXTURE_2D, textures[0]);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+    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);
+
+    glCopySubTextureCHROMIUM(textures[0], sourceLevel, GL_TEXTURE_2D, textures[1], destLevel, 0, 0,
+                             0, 0, 1, 1, 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);
+
+    EXPECT_GL_NO_ERROR();
+}
+
+// Test the newly added ES3 unorm formats
+TEST_P(CopyTextureTestES3, ES3UnormFormats)
+{
+    if (!checkExtensions())
+    {
+        return;
+    }
+
+    auto testOutput = [this](GLuint texture, const GLColor &expectedColor) {
+        const std::string vs =
+            "#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";
+
+        const std::string fs =
+            "#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, vs, fs);
+        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);
+    };
+
+    // 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 (IsOpenGLES())
+    {
+        std::cout << "Skipping GL_SRGB and GL_SRGB_ALPHA because it is not implemented yet."
+                  << std::endl;
+    }
+    else
+    {
+        testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_SRGB,
+                            GL_UNSIGNED_BYTE, false, false, false, GLColor(128, 64, 32, 255));
+        testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_SRGB,
+                            GL_UNSIGNED_BYTE, false, true, false, GLColor(64, 32, 16, 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(128, 64, 32, 128));
+    }
+}
+
+// Test the newly added ES3 float formats
+TEST_P(CopyTextureTestES3, ES3FloatFormats)
+{
+    if (!checkExtensions())
+    {
+        return;
+    }
+
+    if (!extensionEnabled("GL_EXT_color_buffer_float"))
+    {
+        std::cout << "Test skipped due to missing GL_EXT_color_buffer_float." << std::endl;
+        return;
+    }
+
+    auto testOutput = [this](GLuint texture, const GLColor32F &expectedColor) {
+        const std::string vs =
+            "#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";
+
+        const std::string fs =
+            "#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, vs, fs);
+        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));
+
+    if (IsD3D11() || IsOpenGL() || IsOpenGLES())
+    {
+        std::cout << "Skipping GL_RGB9_E5 because it is not implemented yet." << std::endl;
+    }
+    else
+    {
+        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)
+{
+    if (!checkExtensions())
+    {
+        return;
+    }
+
+    if (IsOpenGL() || IsOpenGLES())
+    {
+        std::cout << "Test on OpenGL and OpenGLES because not all formats are implemented yet."
+                  << std::endl;
+        return;
+    }
+
+    using GLColor32U = std::tuple<GLuint, GLuint, GLuint, GLuint>;
+
+    auto testOutput = [this](GLuint texture, const GLColor32U &expectedColor) {
+        const std::string vs =
+            "#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";
+
+        std::string fs =
+            "#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, vs, fs);
+        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());
+ANGLE_INSTANTIATE_TEST(CopyTextureTestES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
 
 }  // namespace angle