Implement the CPU fallback for CopyTextureCHROMIUM on OpenGL.

BUG=angleproject:1932

Change-Id: Iabc1a3e361d66313dc16bf19b392402b7836f8a5
Reviewed-on: https://chromium-review.googlesource.com/612562
Commit-Queue: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/common/angleutils.h b/src/common/angleutils.h
index 7fd346d..809ddea 100644
--- a/src/common/angleutils.h
+++ b/src/common/angleutils.h
@@ -236,6 +236,7 @@
 #define snprintf _snprintf
 #endif
 
+#define GL_BGRX8_ANGLEX 0x6ABA
 #define GL_BGR565_ANGLEX 0x6ABB
 #define GL_BGRA4_ANGLEX 0x6ABC
 #define GL_BGR5_A1_ANGLEX 0x6ABD
diff --git a/src/libANGLE/renderer/Format.h b/src/libANGLE/renderer/Format.h
index bf4f0d0..3808733 100644
--- a/src/libANGLE/renderer/Format.h
+++ b/src/libANGLE/renderer/Format.h
@@ -38,6 +38,7 @@
                      GLuint stencilBits);
 
     static const Format &Get(ID id);
+    static ID InternalFormatToID(GLenum internalFormat);
 
     ID id;
 
diff --git a/src/libANGLE/renderer/Format_table_autogen.cpp b/src/libANGLE/renderer/Format_table_autogen.cpp
index 3c5aced..559c416 100644
--- a/src/libANGLE/renderer/Format_table_autogen.cpp
+++ b/src/libANGLE/renderer/Format_table_autogen.cpp
@@ -153,6 +153,146 @@
 };
 
 // static
+Format::ID Format::InternalFormatToID(GLenum internalFormat)
+{
+    switch (internalFormat)
+    {
+        // clang-format off
+        case GL_RGBA16_EXT: return Format::ID::R16G16B16A16_UNORM;
+        case GL_ETC1_RGB8_LOSSY_DECODE_ANGLE: return Format::ID::NONE;
+        case GL_RG8I: return Format::ID::R8G8_SINT;
+        case GL_R16F: return Format::ID::R16_FLOAT;
+        case GL_RGBA8I: return Format::ID::R8G8B8A8_SINT;
+        case GL_RG8UI: return Format::ID::R8G8_UINT;
+        case GL_RGBA8_SNORM: return Format::ID::R8G8B8A8_SNORM;
+        case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR: return Format::ID::ASTC_12x10_SRGB_BLOCK;
+        case GL_RG8_SNORM: return Format::ID::R8G8_SNORM;
+        case GL_BGR565_ANGLEX: return Format::ID::B5G6R5_UNORM;
+        case GL_DEPTH_COMPONENT24: return Format::ID::D24_UNORM;
+        case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: return Format::ID::ETC2_R8G8B8A1_UNORM_BLOCK;
+        case GL_COMPRESSED_RGBA_ASTC_10x10_KHR: return Format::ID::ASTC_10x10_UNORM_BLOCK;
+        case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR: return Format::ID::ASTC_8x6_SRGB_BLOCK;
+        case GL_RGB32UI: return Format::ID::R32G32B32_UINT;
+        case GL_COMPRESSED_RGBA_ASTC_6x5_KHR: return Format::ID::ASTC_6x5_UNORM_BLOCK;
+        case GL_ALPHA32F_EXT: return Format::ID::A32_FLOAT;
+        case GL_R16UI: return Format::ID::R16_UINT;
+        case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR: return Format::ID::ASTC_5x4_SRGB_BLOCK;
+        case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR: return Format::ID::ASTC_5x5_SRGB_BLOCK;
+        case GL_COMPRESSED_R11_EAC: return Format::ID::EAC_R11_UNORM_BLOCK;
+        case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR: return Format::ID::ASTC_10x10_SRGB_BLOCK;
+        case GL_RGBA32UI: return Format::ID::R32G32B32A32_UINT;
+        case GL_R8_SNORM: return Format::ID::R8_SNORM;
+        case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: return Format::ID::BC1_RGBA_UNORM_SRGB_BLOCK;
+        case GL_LUMINANCE32F_EXT: return Format::ID::L32_FLOAT;
+        case GL_RG16_EXT: return Format::ID::R16G16_UNORM;
+        case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: return Format::ID::ETC2_R8G8B8A1_SRGB_BLOCK;
+        case GL_SRGB8: return Format::ID::R8G8B8_UNORM_SRGB;
+        case GL_LUMINANCE8_ALPHA8_EXT: return Format::ID::L8A8_UNORM;
+        case GL_BGRX8_ANGLEX: return Format::ID::B8G8R8X8_UNORM;
+        case GL_RGB16_SNORM_EXT: return Format::ID::R16G16B16_SNORM;
+        case GL_RGBA8UI: return Format::ID::R8G8B8A8_UINT;
+        case GL_BGRA4_ANGLEX: return Format::ID::B4G4R4A4_UNORM;
+        case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: return Format::ID::ETC2_R8G8B8A8_SRGB_BLOCK;
+        case GL_LUMINANCE8_EXT: return Format::ID::L8_UNORM;
+        case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE: return Format::ID::BC3_RGBA_UNORM_BLOCK;
+        case GL_R16I: return Format::ID::R16_SINT;
+        case GL_RGB5_A1: return Format::ID::R5G5B5A1_UNORM;
+        case GL_RGB16UI: return Format::ID::R16G16B16_UINT;
+        case GL_COMPRESSED_RGBA_ASTC_4x4_KHR: return Format::ID::ASTC_4x4_UNORM_BLOCK;
+        case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT: return Format::ID::BC2_RGBA_UNORM_SRGB_BLOCK;
+        case GL_R16_SNORM_EXT: return Format::ID::R16_SNORM;
+        case GL_COMPRESSED_RGB8_ETC2: return Format::ID::ETC2_R8G8B8_UNORM_BLOCK;
+        case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT: return Format::ID::BC1_RGB_UNORM_SRGB_BLOCK;
+        case GL_RGBA32F: return Format::ID::R32G32B32A32_FLOAT;
+        case GL_RGBA32I: return Format::ID::R32G32B32A32_SINT;
+        case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: return Format::ID::BC3_RGBA_UNORM_SRGB_BLOCK;
+        case GL_COMPRESSED_RGBA_ASTC_8x5_KHR: return Format::ID::ASTC_8x5_UNORM_BLOCK;
+        case GL_RG8: return Format::ID::R8G8_UNORM;
+        case GL_COMPRESSED_RGBA_ASTC_8x8_KHR: return Format::ID::ASTC_8x8_UNORM_BLOCK;
+        case GL_RGB10_A2: return Format::ID::R10G10B10A2_UNORM;
+        case GL_COMPRESSED_SIGNED_RG11_EAC: return Format::ID::EAC_R11G11_SNORM_BLOCK;
+        case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR: return Format::ID::ASTC_6x6_SRGB_BLOCK;
+        case GL_DEPTH_COMPONENT16: return Format::ID::D16_UNORM;
+        case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR: return Format::ID::ASTC_10x5_SRGB_BLOCK;
+        case GL_RGB32I: return Format::ID::R32G32B32_SINT;
+        case GL_R8: return Format::ID::R8_UNORM;
+        case GL_RGB32F: return Format::ID::R32G32B32_FLOAT;
+        case GL_R16_EXT: return Format::ID::R16_UNORM;
+        case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR: return Format::ID::ASTC_8x8_SRGB_BLOCK;
+        case GL_COMPRESSED_RGBA_ASTC_10x5_KHR: return Format::ID::ASTC_10x5_UNORM_BLOCK;
+        case GL_R11F_G11F_B10F: return Format::ID::R11G11B10_FLOAT;
+        case GL_RGB8: return Format::ID::R8G8B8_UNORM;
+        case GL_COMPRESSED_RGBA_ASTC_5x5_KHR: return Format::ID::ASTC_5x5_UNORM_BLOCK;
+        case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR: return Format::ID::ASTC_8x5_SRGB_BLOCK;
+        case GL_RGBA16I: return Format::ID::R16G16B16A16_SINT;
+        case GL_R8I: return Format::ID::R8_SINT;
+        case GL_RGB8_SNORM: return Format::ID::R8G8B8_SNORM;
+        case GL_RG32F: return Format::ID::R32G32_FLOAT;
+        case GL_DEPTH_COMPONENT32F: return Format::ID::D32_FLOAT;
+        case GL_RG32I: return Format::ID::R32G32_SINT;
+        case GL_ALPHA8_EXT: return Format::ID::A8_UNORM;
+        case GL_RGB16_EXT: return Format::ID::R16G16B16_UNORM;
+        case GL_BGRA8_EXT: return Format::ID::B8G8R8A8_UNORM;
+        case GL_RG32UI: return Format::ID::R32G32_UINT;
+        case GL_RGBA16UI: return Format::ID::R16G16B16A16_UINT;
+        case GL_COMPRESSED_RGBA8_ETC2_EAC: return Format::ID::ETC2_R8G8B8A8_UNORM_BLOCK;
+        case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: return Format::ID::BC1_RGBA_UNORM_BLOCK;
+        case GL_COMPRESSED_RGBA_ASTC_10x6_KHR: return Format::ID::ASTC_10x6_UNORM_BLOCK;
+        case GL_COMPRESSED_SRGB8_ETC2: return Format::ID::ETC2_R8G8B8_SRGB_BLOCK;
+        case GL_DEPTH32F_STENCIL8: return Format::ID::D32_FLOAT_S8X24_UINT;
+        case GL_COMPRESSED_RGBA_ASTC_6x6_KHR: return Format::ID::ASTC_6x6_UNORM_BLOCK;
+        case GL_R32UI: return Format::ID::R32_UINT;
+        case GL_BGR5_A1_ANGLEX: return Format::ID::B5G5R5A1_UNORM;
+        case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR: return Format::ID::ASTC_12x12_SRGB_BLOCK;
+        case GL_COMPRESSED_RG11_EAC: return Format::ID::EAC_R11G11_UNORM_BLOCK;
+        case GL_SRGB8_ALPHA8: return Format::ID::R8G8B8A8_UNORM_SRGB;
+        case GL_LUMINANCE_ALPHA16F_EXT: return Format::ID::L16A16_FLOAT;
+        case GL_RGBA: return Format::ID::R8G8B8A8_UNORM;
+        case GL_ETC1_RGB8_OES: return Format::ID::NONE;
+        case GL_DEPTH24_STENCIL8: return Format::ID::D24_UNORM_S8_UINT;
+        case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR: return Format::ID::ASTC_4x4_SRGB_BLOCK;
+        case GL_RGB16I: return Format::ID::R16G16B16_SINT;
+        case GL_R8UI: return Format::ID::R8_UINT;
+        case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR: return Format::ID::ASTC_10x6_SRGB_BLOCK;
+        case GL_RGBA16F: return Format::ID::R16G16B16A16_FLOAT;
+        case GL_COMPRESSED_SIGNED_R11_EAC: return Format::ID::EAC_R11_SNORM_BLOCK;
+        case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: return Format::ID::BC1_RGB_UNORM_BLOCK;
+        case GL_RGB8I: return Format::ID::R8G8B8_SINT;
+        case GL_COMPRESSED_RGBA_ASTC_8x6_KHR: return Format::ID::ASTC_8x6_UNORM_BLOCK;
+        case GL_STENCIL_INDEX8: return Format::ID::S8_UINT;
+        case GL_LUMINANCE_ALPHA32F_EXT: return Format::ID::L32A32_FLOAT;
+        case GL_ALPHA16F_EXT: return Format::ID::A16_FLOAT;
+        case GL_RGB8UI: return Format::ID::R8G8B8_UINT;
+        case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR: return Format::ID::ASTC_10x8_SRGB_BLOCK;
+        case GL_COMPRESSED_RGBA_ASTC_12x10_KHR: return Format::ID::ASTC_12x10_UNORM_BLOCK;
+        case GL_RGB9_E5: return Format::ID::R9G9B9E5_SHAREDEXP;
+        case GL_RGBA16_SNORM_EXT: return Format::ID::R16G16B16A16_SNORM;
+        case GL_R32I: return Format::ID::R32_SINT;
+        case GL_DEPTH_COMPONENT32_OES: return Format::ID::D32_UNORM;
+        case GL_R32F: return Format::ID::R32_FLOAT;
+        case GL_NONE: return Format::ID::NONE;
+        case GL_RG16F: return Format::ID::R16G16_FLOAT;
+        case GL_RGB: return Format::ID::R8G8B8_UNORM;
+        case GL_RGB565: return Format::ID::R5G6B5_UNORM;
+        case GL_LUMINANCE16F_EXT: return Format::ID::L16_FLOAT;
+        case GL_RG16UI: return Format::ID::R16G16_UINT;
+        case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE: return Format::ID::BC2_RGBA_UNORM_BLOCK;
+        case GL_RG16I: return Format::ID::R16G16_SINT;
+        case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR: return Format::ID::ASTC_6x5_SRGB_BLOCK;
+        case GL_RG16_SNORM_EXT: return Format::ID::R16G16_SNORM;
+        case GL_COMPRESSED_RGBA_ASTC_12x12_KHR: return Format::ID::ASTC_12x12_UNORM_BLOCK;
+        case GL_COMPRESSED_RGBA_ASTC_5x4_KHR: return Format::ID::ASTC_5x4_UNORM_BLOCK;
+        case GL_COMPRESSED_RGBA_ASTC_10x8_KHR: return Format::ID::ASTC_10x8_UNORM_BLOCK;
+        case GL_RGBA4: return Format::ID::R4G4B4A4_UNORM;
+        case GL_RGBA8: return Format::ID::R8G8B8A8_UNORM;
+        case GL_RGB16F: return Format::ID::R16G16B16_FLOAT;
+        case GL_RGB10_A2UI: return Format::ID::R10G10B10A2_UINT;
+        default: return Format::ID::NONE;
+            // clang-format on
+    }
+}
+
+// static
 const Format &Format::Get(ID id)
 {
     return g_formatInfoTable[static_cast<size_t>(id)];
diff --git a/src/libANGLE/renderer/gen_angle_format_table.py b/src/libANGLE/renderer/gen_angle_format_table.py
index 2afab2a..c66bce7 100644
--- a/src/libANGLE/renderer/gen_angle_format_table.py
+++ b/src/libANGLE/renderer/gen_angle_format_table.py
@@ -66,6 +66,17 @@
 }};
 
 // static
+Format::ID Format::InternalFormatToID(GLenum internalFormat)
+{{
+    switch (internalFormat)
+    {{
+        // clang-format off
+{angle_format_switch}
+        // clang-format on
+    }}
+}}
+
+// static
 const Format &Format::Get(ID id)
 {{
     return g_formatInfoTable[static_cast<size_t>(id)];
@@ -216,6 +227,14 @@
         enum_data += ',\n    ' + format_id
     return enum_data
 
+def gen_map_switch_string(gl_to_angle):
+    switch_data = '';
+    for gl_format in gl_to_angle:
+        angle_format = gl_to_angle[gl_format]
+        switch_data += "        case " + gl_format + ": return Format::ID::" + angle_format + ";\n"
+    switch_data += "        default: return Format::ID::NONE;"
+    return switch_data;
+
 gl_to_angle = angle_format.load_forward_table('angle_format_map.json')
 angle_to_gl = angle_format.load_inverse_table('angle_format_map.json')
 data_source_name = 'angle_format_data.json'
@@ -224,10 +243,12 @@
 
 angle_format_cases = parse_angle_format_table(
     all_angle, json_data, angle_to_gl)
+switch_data = gen_map_switch_string(gl_to_angle)
 output_cpp = template_autogen_inl.format(
     script_name = sys.argv[0],
     copyright_year = date.today().year,
     angle_format_info_cases = angle_format_cases,
+    angle_format_switch = switch_data,
     data_source_name = data_source_name)
 with open('Format_table_autogen.cpp', 'wt') as out_file:
     out_file.write(output_cpp)
diff --git a/src/libANGLE/renderer/gl/BlitGL.cpp b/src/libANGLE/renderer/gl/BlitGL.cpp
index 4f074de..90ee4a7 100644
--- a/src/libANGLE/renderer/gl/BlitGL.cpp
+++ b/src/libANGLE/renderer/gl/BlitGL.cpp
@@ -9,14 +9,18 @@
 #include "libANGLE/renderer/gl/BlitGL.h"
 
 #include "common/vector_utils.h"
-#include "libANGLE/formatutils.h"
+#include "image_util/copyimage.h"
+#include "libANGLE/Context.h"
 #include "libANGLE/Framebuffer.h"
-#include "libANGLE/renderer/gl/formatutilsgl.h"
+#include "libANGLE/formatutils.h"
+#include "libANGLE/renderer/Format.h"
 #include "libANGLE/renderer/gl/FramebufferGL.h"
 #include "libANGLE/renderer/gl/FunctionsGL.h"
-#include "libANGLE/renderer/gl/TextureGL.h"
 #include "libANGLE/renderer/gl/StateManagerGL.h"
+#include "libANGLE/renderer/gl/TextureGL.h"
 #include "libANGLE/renderer/gl/WorkaroundsGL.h"
+#include "libANGLE/renderer/gl/formatutilsgl.h"
+#include "libANGLE/renderer/renderer_utils.h"
 
 using angle::Vector2;
 
@@ -521,6 +525,78 @@
     return gl::NoError();
 }
 
+gl::Error BlitGL::copySubTextureCPUReadback(const gl::Context *context,
+                                            TextureGL *source,
+                                            size_t sourceLevel,
+                                            GLenum sourceComponentType,
+                                            TextureGL *dest,
+                                            GLenum destTarget,
+                                            size_t destLevel,
+                                            GLenum destFormat,
+                                            GLenum destType,
+                                            const gl::Rectangle &sourceArea,
+                                            const gl::Offset &destOffset,
+                                            bool unpackFlipY,
+                                            bool unpackPremultiplyAlpha,
+                                            bool unpackUnmultiplyAlpha)
+{
+    ASSERT(source->getTarget() == GL_TEXTURE_2D);
+    const auto &destInternalFormatInfo = gl::GetInternalFormatInfo(destFormat, destType);
+
+    // Create a buffer for holding the source and destination memory
+    const size_t sourcePixelSize = 4;
+    size_t sourceBufferSize      = sourceArea.width * sourceArea.height * sourcePixelSize;
+    size_t destBufferSize =
+        sourceArea.width * sourceArea.height * destInternalFormatInfo.pixelBytes;
+    angle::MemoryBuffer *buffer = nullptr;
+    ANGLE_TRY(context->getScratchBuffer(sourceBufferSize + destBufferSize, &buffer));
+    uint8_t *sourceMemory = buffer->data();
+    uint8_t *destMemory   = buffer->data() + sourceBufferSize;
+
+    mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mScratchFBO);
+    mFunctions->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, source->getTarget(),
+                                     source->getTextureID(), static_cast<GLint>(sourceLevel));
+
+    GLenum readPixelsFormat        = GL_NONE;
+    ColorReadFunction readFunction = nullptr;
+    if (sourceComponentType == GL_UNSIGNED_INT)
+    {
+        readPixelsFormat = GL_RGBA_INTEGER;
+        readFunction     = angle::ReadColor<angle::R8G8B8A8, GLuint>;
+    }
+    else
+    {
+        ASSERT(sourceComponentType != GL_INT);
+        readPixelsFormat = GL_RGBA;
+        readFunction     = angle::ReadColor<angle::R8G8B8A8, GLfloat>;
+    }
+
+    mStateManager->setPixelUnpackState(gl::PixelUnpackState(1, 0));
+    mFunctions->readPixels(sourceArea.x, sourceArea.y, sourceArea.width, sourceArea.height,
+                           readPixelsFormat, GL_UNSIGNED_BYTE, sourceMemory);
+
+    angle::Format::ID destFormatID =
+        angle::Format::InternalFormatToID(destInternalFormatInfo.sizedInternalFormat);
+    const auto &destFormatInfo = angle::Format::Get(destFormatID);
+    CopyImageCHROMIUM(
+        sourceMemory, sourceArea.width * sourcePixelSize, sourcePixelSize, readFunction, destMemory,
+        sourceArea.width * destInternalFormatInfo.pixelBytes, destInternalFormatInfo.pixelBytes,
+        destFormatInfo.colorWriteFunction, destInternalFormatInfo.format,
+        destInternalFormatInfo.componentType, sourceArea.width, sourceArea.height, unpackFlipY,
+        unpackPremultiplyAlpha, unpackUnmultiplyAlpha);
+
+    mStateManager->setPixelPackState(gl::PixelPackState(1, false));
+
+    nativegl::TexSubImageFormat texSubImageFormat =
+        nativegl::GetTexSubImageFormat(mFunctions, mWorkarounds, destFormat, destType);
+
+    mFunctions->texSubImage2D(destTarget, static_cast<GLint>(destLevel), destOffset.x, destOffset.y,
+                              sourceArea.width, sourceArea.height, texSubImageFormat.format,
+                              texSubImageFormat.type, destMemory);
+
+    return gl::NoError();
+}
+
 gl::Error BlitGL::copyTexSubImage(TextureGL *source,
                                   size_t sourceLevel,
                                   TextureGL *dest,
diff --git a/src/libANGLE/renderer/gl/BlitGL.h b/src/libANGLE/renderer/gl/BlitGL.h
index 96d3d8c..ea17b89 100644
--- a/src/libANGLE/renderer/gl/BlitGL.h
+++ b/src/libANGLE/renderer/gl/BlitGL.h
@@ -81,6 +81,21 @@
                              bool unpackPremultiplyAlpha,
                              bool unpackUnmultiplyAlpha);
 
+    gl::Error copySubTextureCPUReadback(const gl::Context *context,
+                                        TextureGL *source,
+                                        size_t sourceLevel,
+                                        GLenum sourceComponentType,
+                                        TextureGL *dest,
+                                        GLenum destTarget,
+                                        size_t destLevel,
+                                        GLenum destFormat,
+                                        GLenum destType,
+                                        const gl::Rectangle &sourceArea,
+                                        const gl::Offset &destOffset,
+                                        bool unpackFlipY,
+                                        bool unpackPremultiplyAlpha,
+                                        bool unpackUnmultiplyAlpha);
+
     gl::Error copyTexSubImage(TextureGL *source,
                               size_t sourceLevel,
                               TextureGL *dest,
diff --git a/src/libANGLE/renderer/gl/TextureGL.cpp b/src/libANGLE/renderer/gl/TextureGL.cpp
index 50f07dd..dcf37e7 100644
--- a/src/libANGLE/renderer/gl/TextureGL.cpp
+++ b/src/libANGLE/renderer/gl/TextureGL.cpp
@@ -767,10 +767,13 @@
         (sourceFormat == GL_RGBA && destFormat == GL_RGB);
 
     GLenum sourceComponentType = sourceImageDesc.format.info->componentType;
-    GLenum destComponentType   = gl::GetInternalFormatInfo(destFormat, destType).componentType;
+    const auto &destInternalFormatInfo = gl::GetInternalFormatInfo(destFormat, destType);
+    GLenum destComponentType           = destInternalFormatInfo.componentType;
+    bool destSRGB                      = destInternalFormatInfo.colorEncoding == GL_SRGB;
     if (source->getTarget() == GL_TEXTURE_2D && !unpackFlipY &&
         unpackPremultiplyAlpha == unpackUnmultiplyAlpha && !needsLumaWorkaround &&
-        sourceFormatContainSupersetOfDestFormat && sourceComponentType == destComponentType)
+        sourceFormatContainSupersetOfDestFormat && sourceComponentType == destComponentType &&
+        !destSRGB)
     {
         return mBlitter->copyTexSubImage(sourceGL, sourceLevel, this, target, level, sourceArea,
                                          destOffset);
@@ -778,7 +781,8 @@
 
     // Check if the destination is renderable and copy on the GPU
     const LevelInfoGL &destLevelInfo = getLevelInfo(target, level);
-    if (nativegl::SupportsNativeRendering(mFunctions, target, destLevelInfo.nativeInternalFormat))
+    if (!destSRGB &&
+        nativegl::SupportsNativeRendering(mFunctions, target, destLevelInfo.nativeInternalFormat))
     {
         return mBlitter->copySubTexture(context, sourceGL, sourceLevel, sourceComponentType, this,
                                         target, level, destComponentType, sourceImageDesc.size,
@@ -788,9 +792,10 @@
     }
 
     // Fall back to CPU-readback
-    UNIMPLEMENTED();
-
-    return gl::NoError();
+    return mBlitter->copySubTextureCPUReadback(context, sourceGL, sourceLevel, sourceComponentType,
+                                               this, target, level, destFormat, destType,
+                                               sourceArea, destOffset, unpackFlipY,
+                                               unpackPremultiplyAlpha, unpackUnmultiplyAlpha);
 }
 
 gl::Error TextureGL::setStorage(const gl::Context *context,
diff --git a/src/tests/gl_tests/CopyTextureTest.cpp b/src/tests/gl_tests/CopyTextureTest.cpp
index 7046499..3de1cca 100644
--- a/src/tests/gl_tests/CopyTextureTest.cpp
+++ b/src/tests/gl_tests/CopyTextureTest.cpp
@@ -1099,21 +1099,13 @@
                         GL_UNSIGNED_BYTE, false, true, false, GLColor(0, 0, 0, 128));
 
     // New sRGB dest formats
-    if (IsOpenGLES() || IsOpenGL())
-    {
-        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(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));
-    }
+    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));
 }
 
 // Test the newly added ES3 float formats
@@ -1234,22 +1226,12 @@
                         GL_R11F_G11F_B10F, GL_FLOAT, false, false, true,
                         GLColor32F(1.0f, 0.5f, 0.25f, 1.0f));
 
-    if (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));
-    }
+    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
@@ -1343,22 +1325,12 @@
     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));
 
-    if (IsOpenGL() || IsOpenGLES())
-    {
-        std::cout << "Skipping GL_RGB8UI because it is not implemented yet." << std::endl;
-    }
-    else
-    {
-        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_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));