| Index: gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc | 
| diff --git a/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc b/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc | 
| index 338772a23351f630855dd291b39525b7f0656d9b..d59fc1c05e2f23237cf35c8cbd8918a334881bb8 100644 | 
| --- a/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc | 
| +++ b/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc | 
| @@ -617,6 +617,172 @@ void DoCopyTexSubImage2D(const gpu::gles2::GLES2Decoder* decoder, | 
| decoder->RestoreFramebufferBindings(); | 
| } | 
|  | 
| +// Convert RGBA/UNSIGNED_BYTE source to RGB/UNSIGNED_BYTE destination. | 
| +void convertToRGB(const uint8_t* source, | 
| +                  uint8_t* destination, | 
| +                  unsigned pixelsPerRow) { | 
| +  for (unsigned i = 0; i < pixelsPerRow; ++i) { | 
| +    destination[0] = source[0]; | 
| +    destination[1] = source[1]; | 
| +    destination[2] = source[2]; | 
| +    source += 4; | 
| +    destination += 3; | 
| +  } | 
| +} | 
| + | 
| +// Convert RGBA/UNSIGNED_BYTE source to RGB/FLOAT destination. | 
| +void convertToRGBFloat(const uint8_t* source, | 
| +                       float* destination, | 
| +                       unsigned pixelsPerRow) { | 
| +  const float scaleFactor = 1.0f / 255.0f; | 
| +  for (unsigned i = 0; i < pixelsPerRow; ++i) { | 
| +    destination[0] = source[0] * scaleFactor; | 
| +    destination[1] = source[1] * scaleFactor; | 
| +    destination[2] = source[2] * scaleFactor; | 
| +    source += 4; | 
| +    destination += 3; | 
| +  } | 
| +} | 
| + | 
| +// Prepare the image data to be uploaded to a texture in pixel unpack buffer. | 
| +void prepareUnpackBuffer(GLuint buffer[2], | 
| +                         bool is_es, | 
| +                         GLenum format, | 
| +                         GLenum type, | 
| +                         GLsizei width, | 
| +                         GLsizei height) { | 
| +  uint32_t pixel_num = width * height; | 
| +  glBindBuffer(GL_PIXEL_PACK_BUFFER, buffer[0]); | 
| + | 
| +  // Result of glReadPixels with format == GL_RGB and type == GL_UNSIGNED_BYTE | 
| +  // from read framebuffer in RGBA fromat is not correct on desktop core | 
| +  // profile on both Linux Mesa and Linux NVIDIA. This may be a driver bug. | 
| +  bool is_rgb_unsigned_byte = format == GL_RGB && type == GL_UNSIGNED_BYTE; | 
| +  if ((!is_es && !is_rgb_unsigned_byte) || | 
| +      (format == GL_RGBA && type == GL_UNSIGNED_BYTE)) { | 
| +    uint32_t bytes_per_group = | 
| +        gpu::gles2::GLES2Util::ComputeImageGroupSize(format, type); | 
| +    glBufferData(GL_PIXEL_PACK_BUFFER, pixel_num * bytes_per_group, 0, | 
| +                 GL_STATIC_READ); | 
| +    glReadPixels(0, 0, width, height, format, type, 0); | 
| +    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer[0]); | 
| +    return; | 
| +  } | 
| + | 
| +  uint32_t bytes_per_group = | 
| +      gpu::gles2::GLES2Util::ComputeImageGroupSize(GL_RGBA, GL_UNSIGNED_BYTE); | 
| +  uint32_t buf_size = pixel_num * bytes_per_group; | 
| + | 
| +  if (format == GL_RGB && type == GL_FLOAT) { | 
| +    glBufferData(GL_PIXEL_PACK_BUFFER, buf_size, 0, GL_STATIC_READ); | 
| +    glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, 0); | 
| +    void* pixels = | 
| +        glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, buf_size, GL_MAP_READ_BIT); | 
| + | 
| +    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer[1]); | 
| +    bytes_per_group = | 
| +        gpu::gles2::GLES2Util::ComputeImageGroupSize(format, type); | 
| +    buf_size = pixel_num * bytes_per_group; | 
| +    glBufferData(GL_PIXEL_UNPACK_BUFFER, buf_size, 0, GL_STATIC_DRAW); | 
| +    void* data = | 
| +        glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, buf_size, GL_MAP_WRITE_BIT); | 
| +    convertToRGBFloat((uint8_t*)pixels, (float*)data, pixel_num); | 
| +    glUnmapBuffer(GL_PIXEL_PACK_BUFFER); | 
| +    glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); | 
| +    return; | 
| +  } | 
| + | 
| +  if (format == GL_RGB && type == GL_UNSIGNED_BYTE) { | 
| +    glBufferData(GL_PIXEL_PACK_BUFFER, buf_size, 0, GL_DYNAMIC_DRAW); | 
| +    glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, 0); | 
| +    void* pixels = glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, buf_size, | 
| +                                    GL_MAP_READ_BIT | GL_MAP_WRITE_BIT); | 
| +    void* data = pixels; | 
| +    convertToRGB((uint8_t*)pixels, (uint8_t*)data, pixel_num); | 
| +    glUnmapBuffer(GL_PIXEL_PACK_BUFFER); | 
| +    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer[0]); | 
| +    return; | 
| +  } | 
| + | 
| +  NOTREACHED(); | 
| +} | 
| + | 
| +void DoReadbackAndTexImage(bool is_tex_image, | 
| +                           const gpu::gles2::GLES2Decoder* decoder, | 
| +                           GLenum source_target, | 
| +                           GLuint source_id, | 
| +                           GLint source_level, | 
| +                           GLenum dest_target, | 
| +                           GLuint dest_id, | 
| +                           GLint dest_level, | 
| +                           GLenum dest_internal_format, | 
| +                           GLint xoffset, | 
| +                           GLint yoffset, | 
| +                           GLsizei width, | 
| +                           GLsizei height, | 
| +                           GLuint framebuffer) { | 
| +  DCHECK_EQ(static_cast<GLenum>(GL_TEXTURE_2D), source_target); | 
| +  GLenum dest_binding_target = | 
| +      gpu::gles2::GLES2Util::GLFaceTargetToTextureTarget(dest_target); | 
| +  DCHECK(dest_binding_target == GL_TEXTURE_2D || | 
| +         dest_binding_target == GL_TEXTURE_CUBE_MAP); | 
| +  DCHECK(source_level == 0 || decoder->GetFeatureInfo()->IsES3Capable()); | 
| +  if (BindFramebufferTexture2D(source_target, source_id, source_level, | 
| +                               framebuffer)) { | 
| +    glBindTexture(dest_binding_target, dest_id); | 
| +    glTexParameterf(dest_binding_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | 
| +    glTexParameterf(dest_binding_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | 
| +    glTexParameteri(dest_binding_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | 
| +    glTexParameteri(dest_binding_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | 
| + | 
| +    GLenum format = GL_RGBA; | 
| +    GLenum type = GL_UNSIGNED_BYTE; | 
| +    switch (dest_internal_format) { | 
| +      case GL_RGB9_E5: | 
| +        format = GL_RGB; | 
| +        type = GL_FLOAT; | 
| +        break; | 
| +      case GL_SRGB_EXT: | 
| +      case GL_SRGB8: | 
| +        format = GL_RGB; | 
| +        break; | 
| +      case GL_RGB5_A1: | 
| +      case GL_SRGB_ALPHA_EXT: | 
| +      case GL_SRGB8_ALPHA8: | 
| +        break; | 
| +      default: | 
| +        NOTREACHED(); | 
| +        break; | 
| +    } | 
| + | 
| +    // TODO(qiankun.miao@intel.com): PIXEL_PACK_BUFFER and PIXEL_UNPACK_BUFFER | 
| +    // are not supported in ES2. | 
| +    bool is_es = decoder->GetFeatureInfo()->gl_version_info().is_es; | 
| +    DCHECK(!is_es || decoder->GetFeatureInfo()->gl_version_info().is_es3); | 
| + | 
| +    uint32_t buffer_num = is_es && format == GL_RGB && type == GL_FLOAT ? 2 : 1; | 
| +    GLuint buffer[2]; | 
| +    glGenBuffersARB(buffer_num, buffer); | 
| +    prepareUnpackBuffer(buffer, is_es, format, type, width, height); | 
| + | 
| +    if (is_tex_image) { | 
| +      glTexImage2D(dest_target, dest_level, dest_internal_format, width, height, | 
| +                   0, format, type, 0); | 
| +    } else { | 
| +      glTexSubImage2D(dest_target, dest_level, xoffset, yoffset, width, height, | 
| +                      format, type, 0); | 
| +    } | 
| +    glDeleteBuffersARB(buffer_num, buffer); | 
| +  } | 
| + | 
| +  decoder->RestoreTextureState(source_id); | 
| +  decoder->RestoreTextureState(dest_id); | 
| +  decoder->RestoreTextureUnitBindings(0); | 
| +  decoder->RestoreActiveTexture(); | 
| +  decoder->RestoreFramebufferBindings(); | 
| +  decoder->RestoreBufferBindings(); | 
| +} | 
| + | 
| }  // namespace | 
|  | 
| namespace gpu { | 
| @@ -735,9 +901,11 @@ void CopyTextureCHROMIUMResourceManager::DoCopyTexture( | 
| GLint original_dest_level = dest_level; | 
| GLenum original_dest_target = dest_target; | 
| GLenum original_internal_format = dest_internal_format; | 
| -  if (method == DRAW_AND_COPY) { | 
| +  if (method == DRAW_AND_COPY || method == DRAW_AND_READBACK) { | 
| GLenum adjusted_internal_format = | 
| -        getIntermediateFormat(dest_internal_format); | 
| +        method == DRAW_AND_READBACK | 
| +            ? GL_RGBA | 
| +            : getIntermediateFormat(dest_internal_format); | 
| dest_target = GL_TEXTURE_2D; | 
| glGenTextures(1, &intermediate_texture); | 
| glBindTexture(dest_target, intermediate_texture); | 
| @@ -758,11 +926,18 @@ void CopyTextureCHROMIUMResourceManager::DoCopyTexture( | 
| dest_target, dest_texture, dest_level, dest_internal_format, width, | 
| height, flip_y, premultiply_alpha, unpremultiply_alpha, kIdentityMatrix); | 
|  | 
| -  if (method == DRAW_AND_COPY) { | 
| +  if (method == DRAW_AND_COPY || method == DRAW_AND_READBACK) { | 
| source_level = 0; | 
| -    DoCopyTexImage2D(decoder, dest_target, intermediate_texture, source_level, | 
| -                     original_dest_target, dest_id, original_dest_level, | 
| -                     original_internal_format, width, height, framebuffer_); | 
| +    if (method == DRAW_AND_COPY) { | 
| +      DoCopyTexImage2D(decoder, dest_target, intermediate_texture, source_level, | 
| +                       original_dest_target, dest_id, original_dest_level, | 
| +                       original_internal_format, width, height, framebuffer_); | 
| +    } else if (method == DRAW_AND_READBACK) { | 
| +      DoReadbackAndTexImage(true, decoder, dest_target, intermediate_texture, | 
| +                            source_level, original_dest_target, dest_id, | 
| +                            original_dest_level, original_internal_format, 0, 0, | 
| +                            width, height, framebuffer_); | 
| +    } | 
| glDeleteTextures(1, &intermediate_texture); | 
| } | 
| } | 
| @@ -805,9 +980,12 @@ void CopyTextureCHROMIUMResourceManager::DoCopySubTexture( | 
| GLint original_dest_level = dest_level; | 
| GLenum original_dest_target = dest_target; | 
| GLuint intermediate_texture = 0; | 
| -  if (method == DRAW_AND_COPY) { | 
| +  GLenum original_internal_format = dest_internal_format; | 
| +  if (method == DRAW_AND_COPY || method == DRAW_AND_READBACK) { | 
| GLenum adjusted_internal_format = | 
| -        getIntermediateFormat(dest_internal_format); | 
| +        method == DRAW_AND_READBACK | 
| +            ? GL_RGBA | 
| +            : getIntermediateFormat(dest_internal_format); | 
| dest_target = GL_TEXTURE_2D; | 
| glGenTextures(1, &intermediate_texture); | 
| glBindTexture(dest_target, intermediate_texture); | 
| @@ -834,12 +1012,19 @@ void CopyTextureCHROMIUMResourceManager::DoCopySubTexture( | 
| source_height, flip_y, premultiply_alpha, unpremultiply_alpha, | 
| kIdentityMatrix); | 
|  | 
| -  if (method == DRAW_AND_COPY) { | 
| +  if (method == DRAW_AND_COPY || method == DRAW_AND_READBACK) { | 
| source_level = 0; | 
| -    DoCopyTexSubImage2D(decoder, dest_target, intermediate_texture, | 
| -                        source_level, original_dest_target, dest_id, | 
| -                        original_dest_level, xoffset, yoffset, 0, 0, width, | 
| -                        height, framebuffer_); | 
| +    if (method == DRAW_AND_COPY) { | 
| +      DoCopyTexSubImage2D(decoder, dest_target, intermediate_texture, | 
| +                          source_level, original_dest_target, dest_id, | 
| +                          original_dest_level, xoffset, yoffset, 0, 0, width, | 
| +                          height, framebuffer_); | 
| +    } else if (method == DRAW_AND_READBACK) { | 
| +      DoReadbackAndTexImage(false, decoder, dest_target, intermediate_texture, | 
| +                            source_level, original_dest_target, dest_id, | 
| +                            original_dest_level, original_internal_format, | 
| +                            xoffset, yoffset, width, height, framebuffer_); | 
| +    } | 
| glDeleteTextures(1, &intermediate_texture); | 
| } | 
| } | 
|  |