Chromium Code Reviews| Index: gpu/command_buffer/service/gles2_cmd_srgb_converter.cc |
| diff --git a/gpu/command_buffer/service/gles2_cmd_srgb_converter.cc b/gpu/command_buffer/service/gles2_cmd_srgb_converter.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..aad3f8866f00aae205c5c081cf23baf7a4e5d13e |
| --- /dev/null |
| +++ b/gpu/command_buffer/service/gles2_cmd_srgb_converter.cc |
| @@ -0,0 +1,457 @@ |
| +// 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_srgb_converter.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 { |
| + |
| +SRGBConverter::SRGBConverter( |
| + const gles2::FeatureInfo* feature_info) |
| + : feature_info_(feature_info) { |
| + DCHECK(feature_info->gl_version_info().is_desktop_core_profile); |
|
Zhenyao Mo
2016/08/29 23:36:50
Again, why desktop core profile only?
yunchao
2016/08/31 09:18:45
I have removed it.
|
| +} |
| + |
| +SRGBConverter::~SRGBConverter() {} |
| + |
| +void SRGBConverter::InitializeSRGBDecoder( |
| + const gles2::GLES2Decoder* decoder) { |
| + if (srgb_decoder_initialized_) { |
| + return; |
| + } |
| + |
| + srgb_decoder_program_ = glCreateProgram(); |
| + |
| + // Compile the vertex 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" |
|
Zhenyao Mo
2016/08/29 23:36:50
nit: ugly formatting
yunchao
2016/08/31 09:18:45
Done.
|
| + " v_texcoord = quad_positions[gl_VertexID];\n" |
| + "}\n"; |
| + |
| + GLuint vs = glCreateShader(GL_VERTEX_SHADER); |
| + CompileShader(vs, vs_source); |
| + glAttachShader(srgb_decoder_program_, vs); |
| + glDeleteShader(vs); |
| + |
| + // Compile the fragment 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" |
| + "float decode(float color)\n" |
| + "{\n" |
| + " float decoded_color;\n" |
| + " if (color <= 0.04045) {\n" |
| + " decoded_color = color / 12.92;\n" |
| + " } else {\n" |
| + " decoded_color = (color + 0.055) / 1.055;\n" |
| + " decoded_color = pow(decoded_color, 4.2);\n" |
| + " }\n" |
| + " return decoded_color;\n" |
| + "}\n" |
| + "\n" |
| + "void main()\n" |
| + "{\n" |
| + " vec4 c = texture(u_source_texture, v_texcoord);\n" |
| + " output_color = vec4(decode(c.r), decode(c.g), decode(c.b), c.a);\n" |
|
yunchao
2016/08/29 15:16:52
Obviously, the decode function in shader are not n
Zhenyao Mo
2016/08/29 23:36:50
As I pointed in the original bug, 4.2, 4.3, and 4.
yunchao
2016/08/31 09:18:45
My MacOS is OGL 4.1, but it doesn't need to decode
|
| + " output_color = c;\n" |
| + "}\n"; |
| + |
| + GLuint fs = glCreateShader(GL_FRAGMENT_SHADER); |
| + CompileShader(fs, fs_source); |
| + glAttachShader(srgb_decoder_program_, fs); |
| + glDeleteShader(fs); |
| + |
| + glLinkProgram(srgb_decoder_program_); |
| +#ifndef NDEBUG |
| + GLint linked = 0; |
| + glGetProgramiv(srgb_decoder_program_, GL_LINK_STATUS, &linked); |
| + if (!linked) { |
| + DLOG(ERROR) << "BlitFramebuffer: program link failure."; |
| + } |
| +#endif |
| + |
| + GLuint texture_uniform = |
| + glGetUniformLocation(srgb_decoder_program_, "u_source_texture"); |
| + glUseProgram(srgb_decoder_program_); |
| + glUniform1i(texture_uniform, 0); |
| + |
| + glGenTextures(srgb_decoder_textures_.size(), srgb_decoder_textures_.data()); |
| + glActiveTexture(GL_TEXTURE0); |
| + for (auto srgb_decoder_texture : srgb_decoder_textures_) { |
| + glBindTexture(GL_TEXTURE_2D, srgb_decoder_texture); |
| + |
| + // Use nearest, non-mipmapped sampling with the srgb decoder texture |
| + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
|
Zhenyao Mo
2016/08/29 23:36:50
I think LINEAR is the correct filter.
You should a
yunchao
2016/08/31 09:18:45
Done. Currently there is neither filtering nor rep
|
| + } |
| + |
| + glGenFramebuffersEXT(1, &srgb_decoder_fbo_); |
| + glGenVertexArraysOES(1, &srgb_decoder_vao_); |
| + |
| + decoder->RestoreTextureUnitBindings(0); |
| + decoder->RestoreActiveTexture(); |
| + decoder->RestoreProgramBindings(); |
| + |
| + srgb_decoder_initialized_ = true; |
| +} |
| + |
| +void SRGBConverter::InitializeSRGBEncoder( |
| + const gles2::GLES2Decoder* decoder) { |
| + if (srgb_encoder_initialized_) { |
| + return; |
| + } |
| + |
| + srgb_encoder_program_ = glCreateProgram(); |
| + |
| + // Compile the vertex 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" |
|
Zhenyao Mo
2016/08/29 23:36:50
nit: ugly formatting
yunchao
2016/08/31 09:18:44
Done.
|
| + " v_texcoord = quad_positions[gl_VertexID];\n" |
| + "}\n"; |
| + |
| + GLuint vs = glCreateShader(GL_VERTEX_SHADER); |
| + CompileShader(vs, vs_source); |
| + glAttachShader(srgb_encoder_program_, vs); |
| + glDeleteShader(vs); |
| + |
| + // Compile the fragment 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" |
| + "float encode(float color)\n" |
| + "{\n" |
| + " float encoded_color;\n" |
| + " if (color < 0.0031308) {\n" |
| + " encoded_color = color * 12.92;\n" |
| + " } else {\n" |
| + " encoded_color = pow(color, 0.41666) * 1.055 - 0.055;\n" |
| + " }\n" |
| + " return encoded_color;\n" |
| + "}\n" |
| + "\n" |
| + "void main()\n" |
| + "{\n" |
| + " vec4 c = texture(u_source_texture, v_texcoord);\n" |
| + " output_color = vec4(encode(c.r), encode(c.g), encode(c.b), c.a);\n" |
| + "}\n"; |
| + |
| + GLuint fs = glCreateShader(GL_FRAGMENT_SHADER); |
| + CompileShader(fs, fs_source); |
| + glAttachShader(srgb_encoder_program_, fs); |
| + glDeleteShader(fs); |
| + |
| + glLinkProgram(srgb_encoder_program_); |
| +#ifndef NDEBUG |
| + GLint linked = 0; |
| + glGetProgramiv(srgb_encoder_program_, GL_LINK_STATUS, &linked); |
| + if (!linked) { |
| + DLOG(ERROR) << "SRGB Encoder for BlitFramebuffer: program link failure."; |
| + } |
| +#endif |
| + |
| + GLuint texture_uniform = |
| + glGetUniformLocation(srgb_encoder_program_, "u_source_texture"); |
| + glUseProgram(srgb_encoder_program_); |
| + glUniform1i(texture_uniform, 0); |
| + |
| + glGenTextures(srgb_encoder_textures_.size(), srgb_encoder_textures_.data()); |
| + glActiveTexture(GL_TEXTURE0); |
| + for (auto srgb_encoder_texture : srgb_encoder_textures_) { |
| + glBindTexture(GL_TEXTURE_2D, srgb_encoder_texture); |
| + |
| + // Use nearest, non-mipmapped sampling with the srgb encoder texture |
| + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| + } |
| + |
| + glGenFramebuffersEXT(1, &srgb_encoder_fbo_); |
| + glGenVertexArraysOES(1, &srgb_encoder_vao_); |
| + |
| + decoder->RestoreTextureUnitBindings(0); |
| + decoder->RestoreActiveTexture(); |
| + decoder->RestoreProgramBindings(); |
| + |
| + srgb_encoder_initialized_ = true; |
| +} |
| + |
| +void SRGBConverter::Destroy() { |
| + if (srgb_decoder_initialized_) { |
| + glDeleteProgram(srgb_decoder_program_); |
| + srgb_decoder_program_ = 0; |
| + |
| + glDeleteTextures(srgb_decoder_textures_.size(), |
| + srgb_decoder_textures_.data()); |
| + srgb_decoder_textures_.fill(0); |
| + |
| + glDeleteFramebuffersEXT(1, &srgb_decoder_fbo_); |
| + srgb_decoder_fbo_ = 0; |
| + |
| + glDeleteVertexArraysOES(1, &srgb_decoder_vao_); |
| + srgb_decoder_vao_ = 0; |
| + |
| + srgb_decoder_initialized_ = false; |
| + } |
| + |
| + if (srgb_encoder_initialized_) { |
| + glDeleteProgram(srgb_encoder_program_); |
| + srgb_encoder_program_ = 0; |
| + |
| + glDeleteTextures(srgb_encoder_textures_.size(), |
| + srgb_encoder_textures_.data()); |
| + srgb_encoder_textures_.fill(0); |
| + |
| + glDeleteFramebuffersEXT(1, &srgb_encoder_fbo_); |
| + srgb_encoder_fbo_ = 0; |
| + |
| + glDeleteVertexArraysOES(1, &srgb_encoder_vao_); |
| + srgb_encoder_vao_ = 0; |
| + |
| + srgb_encoder_initialized_ = false; |
| + } |
| +} |
| + |
| +void SRGBConverter::SRGBToLinear( |
| + const gles2::GLES2Decoder* decoder, |
| + GLint srcX0, |
| + GLint srcY0, |
| + GLint srcX1, |
| + GLint srcY1, |
| + GLint dstX0, |
| + GLint dstY0, |
| + GLint dstX1, |
| + GLint dstY1, |
| + GLbitfield mask, |
| + GLenum filter, |
| + const gfx::Size& framebuffer_size, |
| + GLuint src_framebuffer, |
| + GLenum src_framebuffer_internal_format, |
| + GLuint dst_framebuffer, |
| + GLenum dst_framebuffer_internal_format, |
| + GLenum dst_framebuffer_format, |
| + GLenum dst_framebuffer_type) { |
| + |
| + DCHECK(srgb_decoder_initialized_); |
| + |
| + // Copy the image from framebuffer to the first srgb decoder texture |
| + glBindFramebufferEXT(GL_FRAMEBUFFER, src_framebuffer); |
| + glActiveTexture(GL_TEXTURE0); |
| + glBindTexture(GL_TEXTURE_2D, srgb_decoder_textures_[0]); |
| + |
| + // We should not copy pixels outside of the read framebuffer. If we read |
| + // these pixels, they would become in-bound during BlitFramebuffer. However, |
| + // Out-of-bounds pixels will be initialized to 0 in CopyTexSubImage. But they |
| + // should read as if the GL_CLAMP_TO_EDGE texture mapping mode were applied |
| + // during BlitFramebuffer when the filter is GL_LINEAR. |
| + GLuint x = srcX1 > srcX0 ? srcX0 : srcX1; |
| + GLuint width = srcX1 > srcX0 ? srcX1 - srcX0 : srcX0 - srcX1; |
| + GLuint y = srcY1 > srcY0 ? srcY0 : srcY1; |
| + GLuint height = srcY1 > srcY0 ? srcY1 - srcY0 : srcY0 - srcY1; |
| + gfx::Rect c(0, 0, framebuffer_size.width(), framebuffer_size.height()); |
| + c.Intersect(gfx::Rect(x, y, width, height)); |
| + glCopyTexImage2D(GL_TEXTURE_2D, 0, src_framebuffer_internal_format, |
|
Zhenyao Mo
2016/08/29 23:36:50
If the original fbo read_buffer is a texture, can
yunchao
2016/08/31 09:18:45
Add a TODO first, will revisit this soon.
|
| + c.x(), c.y(), c.width(), c.height(), 0); |
| + |
| + // Make a temporary framebuffer using the second srgb decoder texture to |
| + // render the converted (linear to srgb) result to. |
| + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); |
| + |
| + glBindTexture(GL_TEXTURE_2D, srgb_decoder_textures_[1]); |
| + glTexImage2D(GL_TEXTURE_2D, 0, dst_framebuffer_internal_format, |
| + c.width(), c.height(), |
|
Zhenyao Mo
2016/08/30 00:22:05
Thinking more about this, it seems to me that here
yunchao
2016/08/31 09:18:45
As we discussed, the real blitFramebuffer can clam
|
| + 0, dst_framebuffer_format, dst_framebuffer_type, nullptr); |
| + glBindFramebufferEXT(GL_FRAMEBUFFER, srgb_decoder_fbo_); |
| + glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, |
| + srgb_decoder_textures_[1], 0); |
| + |
| + // Render to the second srgb decoder texture, |
| + // sampling from the first srgb decoder texture. |
| + glUseProgram(srgb_decoder_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, srgb_decoder_textures_[0]); |
| + glBindVertexArrayOES(srgb_decoder_vao_); |
| + |
| + glDrawArrays(GL_TRIANGLES, 0, 6); |
| + |
| + // Finally, bind the temporary framebuffer as read framebuffer, |
| + // blit the converted texture in temp fbo to the destination texture |
| + // in destination framebuffer. |
| + glBindFramebufferEXT(GL_READ_FRAMEBUFFER, srgb_decoder_fbo_); |
| + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, dst_framebuffer); |
| + // Note that the source region has been changed in temp framebuffer |
| + glBlitFramebuffer(srcX0 < srcX1 ? 0 : width, |
|
Zhenyao Mo
2016/08/29 23:36:50
This looks incorrect. src region could be clamped
yunchao
2016/08/31 09:18:44
The same as the issue above. blitFramebuffer will
|
| + srcY0 < srcY1 ? 0 : height, |
| + srcX0 < srcX1 ? width : 0, |
| + srcY0 < srcY1 ? height : 0, |
| + dstX0, dstY0, dstX1, dstY1, mask, filter); |
|
Zhenyao Mo
2016/08/29 23:36:50
I think you need to enable SCISSOR_TEST before wri
yunchao
2016/08/31 09:18:45
The linear color format has this problem too. I wi
|
| + |
| + // Restore state |
| + decoder->RestoreAllAttributes(); |
| + decoder->RestoreTextureUnitBindings(0); |
| + decoder->RestoreActiveTexture(); |
| + decoder->RestoreProgramBindings(); |
| + decoder->RestoreBufferBindings(); |
| + decoder->RestoreFramebufferBindings(); |
| + decoder->RestoreGlobalState(); |
| +} |
| + |
| +void SRGBConverter::LinearToSRGB( |
| + const gles2::GLES2Decoder* decoder, |
| + GLint srcX0, |
| + GLint srcY0, |
| + GLint srcX1, |
| + GLint srcY1, |
| + GLint dstX0, |
| + GLint dstY0, |
| + GLint dstX1, |
| + GLint dstY1, |
| + GLbitfield mask, |
| + GLenum filter, |
| + const gfx::Size& framebuffer_size, |
| + GLuint src_framebuffer, |
| + GLenum src_framebuffer_internal_format, |
| + GLuint dst_framebuffer, |
| + GLenum dst_framebuffer_internal_format, |
| + GLenum dst_framebuffer_format, |
| + GLenum dst_framebuffer_type) { |
| + |
| + DCHECK(srgb_encoder_initialized_); |
| + |
| + // Copy the framebuffer to the first srgb encoder texture |
| + glBindFramebufferEXT(GL_FRAMEBUFFER, src_framebuffer); |
| + glActiveTexture(GL_TEXTURE0); |
| + glBindTexture(GL_TEXTURE_2D, srgb_encoder_textures_[0]); |
| + |
| + // We should not copy pixels outside of the read framebuffer. If we read |
| + // these pixels, they would become in-bound during BlitFramebuffer. However, |
| + // Out-of-bounds pixels will be initialized to 0 in CopyTexSubImage. But they |
| + // should read as if the GL_CLAMP_TO_EDGE texture mapping mode were applied |
| + // during BlitFramebuffer when the filter is GL_LINEAR. |
| + GLuint x = srcX1 > srcX0 ? srcX0 : srcX1; |
| + GLuint width = srcX1 > srcX0 ? srcX1 - srcX0 : srcX0 - srcX1; |
| + GLuint y = srcY1 > srcY0 ? srcY0 : srcY1; |
| + GLuint height = srcY1 > srcY0 ? srcY1 - srcY0 : srcY0 - srcY1; |
| + gfx::Rect c(0, 0, framebuffer_size.width(), framebuffer_size.height()); |
| + c.Intersect(gfx::Rect(x, y, width, height)); |
| + glCopyTexImage2D(GL_TEXTURE_2D, 0, src_framebuffer_internal_format, |
| + c.x(), c.y(), c.width(), c.height(), 0); |
| + |
| + // Make a temporary framebuffer using the second srgb encoder texture to |
| + // render the converted (linear to srgb) result to. |
| + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); |
| + |
| + glBindTexture(GL_TEXTURE_2D, srgb_encoder_textures_[1]); |
| + glTexImage2D(GL_TEXTURE_2D, 0, dst_framebuffer_internal_format, |
| + c.width(), c.height(), |
| + 0, dst_framebuffer_format, dst_framebuffer_type, nullptr); |
| + glBindFramebufferEXT(GL_FRAMEBUFFER, srgb_encoder_fbo_); |
| + glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, |
| + srgb_encoder_textures_[1], 0); |
| + |
| + // Render to the second srgb encoder texture, |
| + // sampling from the first srgb encoder texture. |
| + glUseProgram(srgb_encoder_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, srgb_encoder_textures_[0]); |
| + glBindVertexArrayOES(srgb_encoder_vao_); |
| + |
| + glDrawArrays(GL_TRIANGLES, 0, 6); |
| + |
| + // Finally, bind the temporary framebuffer as read framebuffer, |
| + // blit the converted texture in temp fbo to the destination texture |
| + // in destination framebuffer. |
| + glBindFramebufferEXT(GL_READ_FRAMEBUFFER, srgb_encoder_fbo_); |
| + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, dst_framebuffer); |
| + // Note that the source region has been changed in temp framebuffer |
| + glBlitFramebuffer(srcX0 < srcX1 ? 0 : width, |
|
Zhenyao Mo
2016/08/29 23:36:50
I think you need to enable SCISSOR_TEST before wri
yunchao
2016/08/31 09:18:45
Acknowledged.
|
| + srcY0 < srcY1 ? 0 : height, |
| + srcX0 < srcX1 ? width : 0, |
| + srcY0 < srcY1 ? height : 0, |
| + dstX0, dstY0, dstX1, dstY1, mask, filter); |
|
Zhenyao Mo
2016/08/29 23:36:50
Same here, dst region could be wrong if src region
yunchao
2016/08/31 09:18:45
The same as the issue above. blitFramebuffer will
|
| + |
| + // Restore state |
| + decoder->RestoreAllAttributes(); |
| + decoder->RestoreTextureUnitBindings(0); |
| + decoder->RestoreActiveTexture(); |
| + decoder->RestoreProgramBindings(); |
| + decoder->RestoreBufferBindings(); |
| + decoder->RestoreFramebufferBindings(); |
| + decoder->RestoreGlobalState(); |
| +} |
| + |
| +} // namespace gpu |