Support glCopyTex[Sub]Image to LUMA formats on the core profile.

BUG=577144
CQ_INCLUDE_TRYBOTS=tryserver.chromium.linux:linux_optional_gpu_tests_rel;tryserver.chromium.mac:mac_optional_gpu_tests_rel;tryserver.chromium.win:win_optional_gpu_tests_rel

Review-Url: https://codereview.chromium.org/2027703003
Cr-Commit-Position: refs/heads/master@{#399545}
diff --git a/gpu/command_buffer/service/BUILD.gn b/gpu/command_buffer/service/BUILD.gn
index 41da83d..c19fa7e7 100644
--- a/gpu/command_buffer/service/BUILD.gn
+++ b/gpu/command_buffer/service/BUILD.gn
@@ -56,6 +56,8 @@
     "gl_utils.h",
     "gles2_cmd_clear_framebuffer.cc",
     "gles2_cmd_clear_framebuffer.h",
+    "gles2_cmd_copy_tex_image.cc",
+    "gles2_cmd_copy_tex_image.h",
     "gles2_cmd_copy_texture_chromium.cc",
     "gles2_cmd_copy_texture_chromium.h",
     "gles2_cmd_decoder.cc",
diff --git a/gpu/command_buffer/service/gles2_cmd_copy_tex_image.cc b/gpu/command_buffer/service/gles2_cmd_copy_tex_image.cc
new file mode 100644
index 0000000..b9e6130
--- /dev/null
+++ b/gpu/command_buffer/service/gles2_cmd_copy_tex_image.cc
@@ -0,0 +1,273 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "gpu/command_buffer/service/gles2_cmd_copy_tex_image.h"
+
+#include "gpu/command_buffer/service/texture_manager.h"
+#include "ui/gl/gl_version_info.h"
+
+namespace {
+
+void CompileShader(GLuint shader, const char* shader_source) {
+  glShaderSource(shader, 1, &shader_source, 0);
+  glCompileShader(shader);
+#ifndef NDEBUG
+  GLint compile_status;
+  glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_status);
+  if (GL_TRUE != compile_status)
+    DLOG(ERROR) << "CopyTexImage: shader compilation failure.";
+#endif
+}
+
+}  // anonymous namespace
+
+namespace gpu {
+
+CopyTexImageResourceManager::CopyTexImageResourceManager(
+    const gles2::FeatureInfo* feature_info)
+    : feature_info_(feature_info) {
+  DCHECK(feature_info->gl_version_info().is_desktop_core_profile);
+}
+
+CopyTexImageResourceManager::~CopyTexImageResourceManager() {}
+
+void CopyTexImageResourceManager::Initialize(
+    const gles2::GLES2Decoder* decoder) {
+  if (initialized_) {
+    return;
+  }
+
+  blit_program_ = glCreateProgram();
+
+  // Compile the fragment shader
+  const char* vs_source =
+      "#version 150\n"
+      "out vec2 v_texcoord;\n"
+      "\n"
+      "void main()\n"
+      "{\n"
+      "    const vec2 quad_positions[6] = vec2[6]\n"
+      "    (\n"
+      "        vec2(0.0f, 0.0f),\n"
+      "        vec2(0.0f, 1.0f),\n"
+      "        vec2(1.0f, 0.0f),\n"
+      "\n"
+      "        vec2(0.0f, 1.0f),\n"
+      "        vec2(1.0f, 0.0f),\n"
+      "        vec2(1.0f, 1.0f)\n"
+      "    );\n"
+      "\n"
+      "    gl_Position = vec4((quad_positions[gl_VertexID] * 2.0) - 1.0, 0.0, "
+      "1.0);\n"
+      "    v_texcoord = quad_positions[gl_VertexID];\n"
+      "}\n";
+
+  GLuint vs = glCreateShader(GL_VERTEX_SHADER);
+  CompileShader(vs, vs_source);
+  glAttachShader(blit_program_, vs);
+  glDeleteShader(vs);
+
+  // Compile the vertex shader
+  const char* fs_source =
+      "#version 150\n"
+      "uniform sampler2D u_source_texture;\n"
+      "in vec2 v_texcoord;\n"
+      "out vec4 output_color;\n"
+      "\n"
+      "void main()\n"
+      "{\n"
+      "    output_color = texture(u_source_texture, v_texcoord);\n"
+      "}\n";
+
+  GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
+  CompileShader(fs, fs_source);
+  glAttachShader(blit_program_, fs);
+  glDeleteShader(fs);
+
+  glLinkProgram(blit_program_);
+#ifndef NDEBUG
+  GLint linked = 0;
+  glGetProgramiv(blit_program_, GL_LINK_STATUS, &linked);
+  if (!linked) {
+    DLOG(ERROR) << "CopyTexImage: program link failure.";
+  }
+#endif
+
+  GLuint texture_uniform =
+      glGetUniformLocation(blit_program_, "u_source_texture");
+  glUseProgram(blit_program_);
+  glUniform1i(texture_uniform, 0);
+
+  glGenTextures(scratch_textures_.size(), scratch_textures_.data());
+  glActiveTexture(GL_TEXTURE0);
+  for (auto scratch_texture : scratch_textures_) {
+    glBindTexture(GL_TEXTURE_2D, scratch_texture);
+
+    // Use nearest, non-mipmapped sampling with the scratch texture
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+  }
+
+  glGenFramebuffersEXT(1, &scratch_fbo_);
+  glGenVertexArraysOES(1, &vao_);
+
+  decoder->RestoreTextureUnitBindings(0);
+  decoder->RestoreActiveTexture();
+  decoder->RestoreProgramBindings();
+
+  initialized_ = true;
+}
+
+void CopyTexImageResourceManager::Destroy() {
+  if (!initialized_) {
+    return;
+  }
+
+  glDeleteProgram(blit_program_);
+  blit_program_ = 0;
+
+  glDeleteTextures(scratch_textures_.size(), scratch_textures_.data());
+  scratch_textures_.fill(0);
+
+  glDeleteFramebuffersEXT(1, &scratch_fbo_);
+  scratch_fbo_ = 0;
+
+  glDeleteVertexArraysOES(1, &vao_);
+  vao_ = 0;
+
+  initialized_ = false;
+}
+
+void CopyTexImageResourceManager::DoCopyTexImage2DToLUMAComatabilityTexture(
+    const gles2::GLES2Decoder* decoder,
+    GLuint dest_texture,
+    GLenum dest_texture_target,
+    GLenum dest_target,
+    GLenum luma_format,
+    GLenum luma_type,
+    GLint level,
+    GLenum internal_format,
+    GLint x,
+    GLint y,
+    GLsizei width,
+    GLsizei height,
+    GLuint source_framebuffer,
+    GLenum source_framebuffer_internal_format) {
+  GLenum adjusted_internal_format =
+      gles2::TextureManager::AdjustTexInternalFormat(feature_info_.get(),
+                                                     internal_format);
+  glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
+  GLenum adjusted_format = gles2::TextureManager::AdjustTexFormat(
+      feature_info_.get(), internal_format);
+  glTexImage2D(dest_target, level, adjusted_internal_format, width, height, 0,
+               adjusted_format, luma_type, nullptr);
+  DoCopyTexSubImage2DToLUMAComatabilityTexture(
+      decoder, dest_texture, dest_texture_target, dest_target, luma_format,
+      luma_type, level, 0, 0, x, y, width, height, source_framebuffer,
+      source_framebuffer_internal_format);
+}
+
+void CopyTexImageResourceManager::DoCopyTexSubImage2DToLUMAComatabilityTexture(
+    const gles2::GLES2Decoder* decoder,
+    GLuint dest_texture,
+    GLenum dest_texture_target,
+    GLenum dest_target,
+    GLenum luma_format,
+    GLenum luma_type,
+    GLint level,
+    GLint xoffset,
+    GLint yoffset,
+    GLint x,
+    GLint y,
+    GLsizei width,
+    GLsizei height,
+    GLuint source_framebuffer,
+    GLenum source_framebuffer_internal_format) {
+  DCHECK(initialized_);
+
+  // Copy the framebuffer to the first scratch texture
+  // TODO(geofflang): This could be optimized further by detecting if the source
+  // framebuffer is copying from a texture and sample directly from that texture
+  // instead of doing an extra copy
+
+  glBindFramebufferEXT(GL_FRAMEBUFFER, source_framebuffer);
+  glActiveTexture(GL_TEXTURE0);
+  glBindTexture(GL_TEXTURE_2D, scratch_textures_[0]);
+  glCopyTexImage2D(GL_TEXTURE_2D, 0, source_framebuffer_internal_format, x, y,
+                   width, height, 0);
+
+  // Set the swizzle of the scratch texture so that the channels sample into the
+  // correct emulated LUMA channels.
+  GLint swizzle[4] = {
+      (luma_format == GL_ALPHA) ? GL_ALPHA : GL_RED,
+      (luma_format == GL_LUMINANCE_ALPHA) ? GL_ALPHA : GL_ZERO, GL_ZERO,
+      GL_ZERO,
+  };
+  glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzle);
+
+  // Make a temporary framebuffer using the second scratch texture to render the
+  // swizzled result to.
+  // TODO(geofflang): Could be optimized more by rendering directly to the
+  // destination texture but this isn't always possible because the destination
+  // may be an incomplete cube map
+  glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
+  GLenum compatability_format =
+      gles2::TextureManager::AdjustTexFormat(feature_info_.get(), luma_format);
+  glBindTexture(GL_TEXTURE_2D, scratch_textures_[1]);
+  glTexImage2D(GL_TEXTURE_2D, 0, compatability_format, width, height, 0,
+               compatability_format, luma_type, nullptr);
+
+  glBindFramebufferEXT(GL_FRAMEBUFFER, scratch_fbo_);
+  glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+                            scratch_textures_[1], 0);
+
+  // Render to the destination texture, sampling from the scratch texture
+  glUseProgram(blit_program_);
+  glViewport(0, 0, width, height);
+  glDisable(GL_SCISSOR_TEST);
+  glDisable(GL_DEPTH_TEST);
+  glDisable(GL_STENCIL_TEST);
+  glDisable(GL_CULL_FACE);
+  glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+  glDepthMask(GL_FALSE);
+  glDisable(GL_BLEND);
+  glDisable(GL_DITHER);
+
+  glBindTexture(GL_TEXTURE_2D, scratch_textures_[0]);
+  glBindVertexArrayOES(vao_);
+
+  glDrawArrays(GL_TRIANGLES, 0, 6);
+
+  // Finally, copy the swizzled texture to the destination texture
+  glBindTexture(dest_texture_target, dest_texture);
+  glCopyTexSubImage2D(dest_target, level, xoffset, yoffset, 0, 0, width,
+                      height);
+
+  // Restore state
+  decoder->RestoreAllAttributes();
+  decoder->RestoreTextureUnitBindings(0);
+  decoder->RestoreActiveTexture();
+  decoder->RestoreProgramBindings();
+  decoder->RestoreBufferBindings();
+  decoder->RestoreFramebufferBindings();
+  decoder->RestoreGlobalState();
+}
+
+// static
+bool CopyTexImageResourceManager::CopyTexImageRequiresBlit(
+    const gles2::FeatureInfo* feature_info,
+    GLenum dest_texture_format) {
+  if (feature_info->gl_version_info().is_desktop_core_profile) {
+    switch (dest_texture_format) {
+      case GL_LUMINANCE:
+      case GL_ALPHA:
+      case GL_LUMINANCE_ALPHA:
+        return true;
+    }
+  }
+
+  return false;
+}
+
+}  // namespace gpu
diff --git a/gpu/command_buffer/service/gles2_cmd_copy_tex_image.h b/gpu/command_buffer/service/gles2_cmd_copy_tex_image.h
new file mode 100644
index 0000000..503862da
--- /dev/null
+++ b/gpu/command_buffer/service/gles2_cmd_copy_tex_image.h
@@ -0,0 +1,87 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_COPY_TEX_IMAGE_H_
+#define GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_COPY_TEX_IMAGE_H_
+
+#include <array>
+
+#include "base/containers/hash_tables.h"
+#include "base/macros.h"
+#include "gpu/command_buffer/service/feature_info.h"
+#include "gpu/command_buffer/service/gl_utils.h"
+#include "gpu/gpu_export.h"
+
+namespace gpu {
+namespace gles2 {
+
+class GLES2Decoder;
+
+}  // namespace gles2.
+
+// This class encapsulates the resources required to implement the
+// glCopyTexImage and glCopyTexSubImage commands.  These commands somtimes
+// require a blit.
+class GPU_EXPORT CopyTexImageResourceManager {
+ public:
+  explicit CopyTexImageResourceManager(const gles2::FeatureInfo* feature_info);
+  ~CopyTexImageResourceManager();
+
+  void Initialize(const gles2::GLES2Decoder* decoder);
+  void Destroy();
+
+  void DoCopyTexImage2DToLUMAComatabilityTexture(
+      const gles2::GLES2Decoder* decoder,
+      GLuint dest_texture,
+      GLenum dest_texture_target,
+      GLenum dest_target,
+      GLenum luma_format,
+      GLenum luma_type,
+      GLint level,
+      GLenum internal_format,
+      GLint x,
+      GLint y,
+      GLsizei width,
+      GLsizei height,
+      GLuint source_framebuffer,
+      GLenum source_framebuffer_internal_format);
+
+  void DoCopyTexSubImage2DToLUMAComatabilityTexture(
+      const gles2::GLES2Decoder* decoder,
+      GLuint dest_texture,
+      GLenum dest_texture_target,
+      GLenum dest_target,
+      GLenum luma_format,
+      GLenum luma_type,
+      GLint level,
+      GLint xoffset,
+      GLint yoffset,
+      GLint x,
+      GLint y,
+      GLsizei width,
+      GLsizei height,
+      GLuint source_framebuffer,
+      GLenum source_framebuffer_internal_format);
+
+  static bool CopyTexImageRequiresBlit(const gles2::FeatureInfo* feature_info,
+                                       GLenum dest_texture_format);
+
+ private:
+  scoped_refptr<const gles2::FeatureInfo> feature_info_;
+
+  bool initialized_ = false;
+
+  GLuint blit_program_ = 0;
+
+  std::array<GLuint, 2> scratch_textures_ = {{0, 0}};
+  GLuint scratch_fbo_ = 0;
+
+  GLuint vao_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(CopyTexImageResourceManager);
+};
+
+}  // namespace gpu.
+
+#endif  // GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_COPY_TEX_IMAGE_H_
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 6ec2b0a..d5a7355b 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -40,6 +40,7 @@
 #include "gpu/command_buffer/service/gl_stream_texture_image.h"
 #include "gpu/command_buffer/service/gl_utils.h"
 #include "gpu/command_buffer/service/gles2_cmd_clear_framebuffer.h"
+#include "gpu/command_buffer/service/gles2_cmd_copy_tex_image.h"
 #include "gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.h"
 #include "gpu/command_buffer/service/gles2_cmd_decoder_passthrough.h"
 #include "gpu/command_buffer/service/gles2_cmd_validation.h"
@@ -1973,6 +1974,7 @@
                                        GLuint* source_texture_service_id,
                                        GLenum* source_texture_target);
 
+  bool InitializeCopyTexImageBlitter(const char* function_name);
   bool InitializeCopyTextureCHROMIUM(const char* function_name);
   // Generate a member function prototype for each command in an automated and
   // typesafe way.
@@ -2155,6 +2157,7 @@
   // Log extra info.
   bool service_logging_;
 
+  std::unique_ptr<CopyTexImageResourceManager> copy_tex_image_blit_;
   std::unique_ptr<CopyTextureCHROMIUMResourceManager> copy_texture_CHROMIUM_;
   std::unique_ptr<ClearFramebufferResourceManager> clear_framebuffer_blit_;
 
@@ -4276,6 +4279,11 @@
   }
   ReleaseAllBackTextures();
   if (have_context) {
+    if (copy_tex_image_blit_.get()) {
+      copy_tex_image_blit_->Destroy();
+      copy_tex_image_blit_.reset();
+    }
+
     if (copy_texture_CHROMIUM_.get()) {
       copy_texture_CHROMIUM_->Destroy();
       copy_texture_CHROMIUM_.reset();
@@ -4347,6 +4355,7 @@
   // state_.current_program object.
   state_.current_program = NULL;
 
+  copy_tex_image_blit_.reset();
   copy_texture_CHROMIUM_.reset();
   clear_framebuffer_blit_.reset();
 
@@ -12374,6 +12383,14 @@
     framebuffer_state_.clear_state_dirty = true;
   }
 
+  bool requires_luma_blit =
+      CopyTexImageResourceManager::CopyTexImageRequiresBlit(feature_info_.get(),
+                                                            format);
+  if (requires_luma_blit &&
+      !InitializeCopyTexImageBlitter("glCopyTexSubImage2D")) {
+    return;
+  }
+
   // Clip to size to source dimensions
   GLint copyX = 0;
   GLint copyY = 0;
@@ -12389,21 +12406,29 @@
     // some part was clipped so clear the rect.
     std::unique_ptr<char[]> zero(new char[pixels_size]);
     memset(zero.get(), 0, pixels_size);
-    glTexImage2D(target, level,
-                 texture_manager()->AdjustTexInternalFormat(internal_format),
+    glTexImage2D(target, level, TextureManager::AdjustTexInternalFormat(
+                                    feature_info_.get(), internal_format),
                  width, height, border, format, type, zero.get());
     if (copyHeight > 0 && copyWidth > 0) {
       GLint dx = copyX - x;
       GLint dy = copyY - y;
       GLint destX = dx;
       GLint destY = dy;
-      glCopyTexSubImage2D(target, level,
-                          destX, destY, copyX, copyY,
-                          copyWidth, copyHeight);
+      if (requires_luma_blit) {
+        copy_tex_image_blit_->DoCopyTexSubImage2DToLUMAComatabilityTexture(
+            this, texture->service_id(), texture->target(), target, format,
+            type, level, destX, destY, copyX, copyY, copyWidth, copyHeight,
+            framebuffer_state_.bound_read_framebuffer->service_id(),
+            framebuffer_state_.bound_read_framebuffer
+                ->GetReadBufferInternalFormat());
+      } else {
+        glCopyTexSubImage2D(target, level, destX, destY, copyX, copyY,
+                            copyWidth, copyHeight);
+      }
     }
   } else {
-    GLenum final_internal_format =
-        texture_manager()->AdjustTexInternalFormat(internal_format);
+    GLenum final_internal_format = TextureManager::AdjustTexInternalFormat(
+        feature_info_.get(), internal_format);
 
     // The service id and target of the texture attached to READ_FRAMEBUFFER.
     GLuint source_texture_service_id = 0;
@@ -12413,6 +12438,7 @@
         final_internal_format, channels_exist, &source_texture_service_id,
         &source_texture_target);
     if (use_workaround) {
+      DCHECK(!requires_luma_blit);
       GLenum dest_texture_target = target;
       GLenum framebuffer_target = features().chromium_framebuffer_multisample
                                       ? GL_READ_FRAMEBUFFER_EXT
@@ -12453,8 +12479,17 @@
 
       glDeleteTextures(1, &temp_texture);
     } else {
-      glCopyTexImage2D(target, level, final_internal_format, copyX, copyY,
-                       copyWidth, copyHeight, border);
+      if (requires_luma_blit) {
+        copy_tex_image_blit_->DoCopyTexImage2DToLUMAComatabilityTexture(
+            this, texture->service_id(), texture->target(), target, format,
+            type, level, internal_format, copyX, copyY, copyWidth, copyHeight,
+            framebuffer_state_.bound_read_framebuffer->service_id(),
+            framebuffer_state_.bound_read_framebuffer
+                ->GetReadBufferInternalFormat());
+      } else {
+        glCopyTexImage2D(target, level, final_internal_format, copyX, copyY,
+                         copyWidth, copyHeight, border);
+      }
     }
   }
   GLenum error = LOCAL_PEEK_GL_ERROR(func_name);
@@ -12554,9 +12589,21 @@
   }
 
   if (copyHeight > 0 && copyWidth > 0) {
-    glCopyTexSubImage2D(target, level,
-                        destX, destY, copyX, copyY,
-                        copyWidth, copyHeight);
+    if (CopyTexImageResourceManager::CopyTexImageRequiresBlit(
+            feature_info_.get(), internal_format)) {
+      if (!InitializeCopyTexImageBlitter("glCopyTexSubImage2D")) {
+        return;
+      }
+      copy_tex_image_blit_->DoCopyTexSubImage2DToLUMAComatabilityTexture(
+          this, texture->service_id(), texture->target(), target,
+          internal_format, type, level, xoffset, yoffset, x, y, width, height,
+          framebuffer_state_.bound_read_framebuffer->service_id(),
+          framebuffer_state_.bound_read_framebuffer
+              ->GetReadBufferInternalFormat());
+    } else {
+      glCopyTexSubImage2D(target, level, destX, destY, copyX, copyY, copyWidth,
+                          copyHeight);
+    }
   }
 
   // This may be a slow command.  Exit command processing to allow for
@@ -14448,11 +14495,12 @@
     // Ensure that the glTexImage2D succeeds.
     LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER(kFunctionName);
     glBindTexture(dest_target, dest_texture->service_id());
-    glTexImage2D(dest_target, 0,
-                 texture_manager()->AdjustTexInternalFormat(internal_format),
-                 source_width, source_height, 0,
-                 texture_manager()->AdjustTexFormat(internal_format), dest_type,
-                 NULL);
+    glTexImage2D(
+        dest_target, 0, TextureManager::AdjustTexInternalFormat(
+                            feature_info_.get(), internal_format),
+        source_width, source_height, 0,
+        TextureManager::AdjustTexFormat(feature_info_.get(), internal_format),
+        dest_type, NULL);
     GLenum error = LOCAL_PEEK_GL_ERROR(kFunctionName);
     if (error != GL_NO_ERROR) {
       RestoreCurrentTextureBindings(&state_, dest_target);
@@ -14693,6 +14741,19 @@
       unpack_premultiply_alpha == GL_TRUE, unpack_unmultiply_alpha == GL_TRUE);
 }
 
+bool GLES2DecoderImpl::InitializeCopyTexImageBlitter(
+    const char* function_name) {
+  if (!copy_tex_image_blit_.get()) {
+    LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER(function_name);
+    copy_tex_image_blit_.reset(
+        new CopyTexImageResourceManager(feature_info_.get()));
+    copy_tex_image_blit_->Initialize(this);
+    if (LOCAL_PEEK_GL_ERROR(function_name) != GL_NO_ERROR)
+      return false;
+  }
+  return true;
+}
+
 bool GLES2DecoderImpl::InitializeCopyTextureCHROMIUM(
     const char* function_name) {
   // Defer initializing the CopyTextureCHROMIUMResourceManager until it is
diff --git a/gpu/command_buffer/service/texture_manager.cc b/gpu/command_buffer/service/texture_manager.cc
index 4813a0f..f3cc1d1 100644
--- a/gpu/command_buffer/service/texture_manager.cc
+++ b/gpu/command_buffer/service/texture_manager.cc
@@ -272,6 +272,7 @@
     {GL_LUMINANCE_ALPHA, GL_RG, GL_RED, GL_RED, GL_RED, GL_GREEN},
 };
 
+// static
 const Texture::CompatibilitySwizzle* GetCompatibilitySwizzle(GLenum format) {
   size_t count = arraysize(kSwizzledFormats);
   for (size_t i = 0; i < count; ++i) {
@@ -2675,23 +2676,28 @@
     // to look it up.
     if (args.command_type == DoTexSubImageArguments::kTexSubImage3D) {
       glTexImage3D(args.target, args.level, internal_format, args.width,
-                   args.height, args.depth, 0, AdjustTexFormat(args.format),
-                   args.type, args.pixels);
-    } else {
-      glTexImage2D(args.target, args.level,
-                   AdjustTexInternalFormat(internal_format), args.width,
-                   args.height, 0, AdjustTexFormat(args.format), args.type,
+                   args.height, args.depth, 0,
+                   AdjustTexFormat(feature_info_.get(), args.format), args.type,
                    args.pixels);
+    } else {
+      glTexImage2D(
+          args.target, args.level,
+          AdjustTexInternalFormat(feature_info_.get(), internal_format),
+          args.width, args.height, 0,
+          AdjustTexFormat(feature_info_.get(), args.format), args.type,
+          args.pixels);
     }
   } else {
     ScopedTextureUploadTimer timer(texture_state);
     if (args.command_type == DoTexSubImageArguments::kTexSubImage3D) {
       glTexSubImage3D(args.target, args.level, args.xoffset, args.yoffset,
                       args.zoffset, args.width, args.height, args.depth,
-                      AdjustTexFormat(args.format), args.type, args.pixels);
+                      AdjustTexFormat(feature_info_.get(), args.format),
+                      args.type, args.pixels);
     } else {
       glTexSubImage2D(args.target, args.level, args.xoffset, args.yoffset,
-                      args.width, args.height, AdjustTexFormat(args.format),
+                      args.width, args.height,
+                      AdjustTexFormat(feature_info_.get(), args.format),
                       args.type, args.pixels);
     }
   }
@@ -2711,7 +2717,8 @@
     if (args.height > 1) {
       glTexSubImage2D(args.target, args.level, args.xoffset, args.yoffset,
                       args.width, args.height - 1,
-                      AdjustTexFormat(args.format), args.type, args.pixels);
+                      AdjustTexFormat(feature_info_.get(), args.format),
+                      args.type, args.pixels);
       GLint actual_width = state->unpack_row_length > 0 ?
           state->unpack_row_length : args.width;
       uint32_t size;
@@ -2729,9 +2736,9 @@
     }
     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
     glTexSubImage2D(args.target, args.level, args.xoffset,
-                    args.yoffset + args.height - 1,
-                    args.width, 1, AdjustTexFormat(args.format), args.type,
-                    reinterpret_cast<const void *>(offset));
+                    args.yoffset + args.height - 1, args.width, 1,
+                    AdjustTexFormat(feature_info_.get(), args.format),
+                    args.type, reinterpret_cast<const void*>(offset));
     glPixelStorei(GL_UNPACK_ALIGNMENT, state->unpack_alignment);
     {
       uint32_t size;
@@ -2749,7 +2756,8 @@
     if (args.depth > 1) {
       glTexSubImage3D(args.target, args.level, args.xoffset, args.yoffset,
                       args.zoffset, args.width, args.height, args.depth - 1,
-                      AdjustTexFormat(args.format), args.type, args.pixels);
+                      AdjustTexFormat(feature_info_.get(), args.format),
+                      args.type, args.pixels);
       GLint actual_height = state->unpack_image_height > 0 ?
           state->unpack_image_height : args.height;
       uint32_t size;
@@ -2769,7 +2777,8 @@
     if (args.height > 1) {
       glTexSubImage3D(args.target, args.level, args.xoffset, args.yoffset,
                       args.zoffset + args.depth - 1, args.width,
-                      args.height - 1, 1, AdjustTexFormat(args.format),
+                      args.height - 1, 1,
+                      AdjustTexFormat(feature_info_.get(), args.format),
                       args.type, reinterpret_cast<const void*>(offset));
       uint32_t size;
       uint32_t padding;
@@ -2787,9 +2796,9 @@
     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
     glTexSubImage3D(args.target, args.level, args.xoffset,
                     args.yoffset + args.height - 1,
-                    args.zoffset + args.depth - 1,
-                    args.width, 1, 1, AdjustTexFormat(args.format), args.type,
-                    reinterpret_cast<const void *>(offset));
+                    args.zoffset + args.depth - 1, args.width, 1, 1,
+                    AdjustTexFormat(feature_info_.get(), args.format),
+                    args.type, reinterpret_cast<const void*>(offset));
     glPixelStorei(GL_UNPACK_ALIGNMENT, state->unpack_alignment);
     {
       uint32_t size;
@@ -2815,7 +2824,7 @@
   DCHECK_EQ(0, state->unpack_skip_rows);
   DCHECK_EQ(0, state->unpack_skip_images);
 
-  GLenum format = AdjustTexFormat(args.format);
+  GLenum format = AdjustTexFormat(feature_info_.get(), args.format);
 
   GLsizei row_bytes = unpack_params.row_length *
                       GLES2Util::ComputeImageGroupSize(format, args.type);
@@ -2856,8 +2865,11 @@
   glPixelStorei(GL_UNPACK_ROW_LENGTH, unpack_params.row_length);
 }
 
-GLenum TextureManager::AdjustTexInternalFormat(GLenum format) const {
-  if (feature_info_->gl_version_info().is_desktop_core_profile) {
+// static
+GLenum TextureManager::AdjustTexInternalFormat(
+    const gles2::FeatureInfo* feature_info,
+    GLenum format) {
+  if (feature_info->gl_version_info().is_desktop_core_profile) {
     const Texture::CompatibilitySwizzle* swizzle =
         GetCompatibilitySwizzle(format);
     if (swizzle)
@@ -2866,16 +2878,18 @@
   return format;
 }
 
-GLenum TextureManager::AdjustTexFormat(GLenum format) const {
+// static
+GLenum TextureManager::AdjustTexFormat(const gles2::FeatureInfo* feature_info,
+                                       GLenum format) {
   // TODO(bajones): GLES 3 allows for internal format and format to differ.
   // This logic may need to change as a result.
-  if (gl::GetGLImplementation() == gl::kGLImplementationDesktopGL) {
+  if (!feature_info->gl_version_info().is_es) {
     if (format == GL_SRGB_EXT)
       return GL_RGB;
     if (format == GL_SRGB_ALPHA_EXT)
       return GL_RGBA;
   }
-  if (feature_info_->gl_version_info().is_desktop_core_profile) {
+  if (feature_info->gl_version_info().is_desktop_core_profile) {
     const Texture::CompatibilitySwizzle* swizzle =
         GetCompatibilitySwizzle(format);
     if (swizzle)
@@ -2934,7 +2948,8 @@
                         args.format, args.type, args.pixels);
       } else {
         glTexSubImage2D(args.target, args.level, 0, 0, args.width, args.height,
-                        AdjustTexFormat(args.format), args.type, args.pixels);
+                        AdjustTexFormat(feature_info_.get(), args.format),
+                        args.type, args.pixels);
       }
     }
     SetLevelInfo(texture_ref, args.target, args.level, args.internal_format,
@@ -2952,10 +2967,12 @@
                    args.height, args.depth, args.border, args.format,
                    args.type, args.pixels);
     } else {
-      glTexImage2D(args.target, args.level,
-                   AdjustTexInternalFormat(args.internal_format), args.width,
-                   args.height, args.border, AdjustTexFormat(args.format),
-                   args.type, args.pixels);
+      glTexImage2D(
+          args.target, args.level,
+          AdjustTexInternalFormat(feature_info_.get(), args.internal_format),
+          args.width, args.height, args.border,
+          AdjustTexFormat(feature_info_.get(), args.format), args.type,
+          args.pixels);
     }
   }
   GLenum error = ERRORSTATE_PEEK_GL_ERROR(error_state, function_name);
diff --git a/gpu/command_buffer/service/texture_manager.h b/gpu/command_buffer/service/texture_manager.h
index 6eca1fb..50d55956 100644
--- a/gpu/command_buffer/service/texture_manager.h
+++ b/gpu/command_buffer/service/texture_manager.h
@@ -1076,8 +1076,10 @@
   uint32_t GetServiceIdGeneration() const;
   void IncrementServiceIdGeneration();
 
-  GLenum AdjustTexInternalFormat(GLenum format) const;
-  GLenum AdjustTexFormat(GLenum format) const;
+  static GLenum AdjustTexInternalFormat(const gles2::FeatureInfo* feature_info,
+                                        GLenum format);
+  static GLenum AdjustTexFormat(const gles2::FeatureInfo* feature_info,
+                                GLenum format);
 
  private:
   friend class Texture;
diff --git a/gpu/command_buffer_service.gypi b/gpu/command_buffer_service.gypi
index 320b4e1..c75c35e 100644
--- a/gpu/command_buffer_service.gypi
+++ b/gpu/command_buffer_service.gypi
@@ -60,6 +60,8 @@
     'command_buffer/service/gl_utils.h',
     'command_buffer/service/gles2_cmd_clear_framebuffer.cc',
     'command_buffer/service/gles2_cmd_clear_framebuffer.h',
+    'command_buffer/service/gles2_cmd_copy_tex_image.cc',
+    'command_buffer/service/gles2_cmd_copy_tex_image.h',
     'command_buffer/service/gles2_cmd_copy_texture_chromium.cc',
     'command_buffer/service/gles2_cmd_copy_texture_chromium.h',
     'command_buffer/service/gles2_cmd_decoder.cc',