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..16c54dd99d1c9f4f16e123aacdb2672a4048d44f 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,180 @@ 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_STREAM_READ); |
+ glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, 0); |
+ |
+ glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); |
+ std::unique_ptr<uint8_t[]> pixels(new uint8_t[width * height * 4]); |
+ glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.get()); |
+ |
+ 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); |
+ */ |
+ |
+ std::unique_ptr<float[]> data(new float[width * height * 3]); |
+ convertToRGBFloat(pixels.get(), data.get(), pixel_num); |
+ glBufferData(GL_PIXEL_UNPACK_BUFFER, buf_size, data.get(), GL_STATIC_DRAW); |
+ 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 +909,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 +934,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 +988,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 +1020,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); |
} |
} |