| 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..b437eea04d919b2453034b7591b0f559eacc5de5
|
| --- /dev/null
|
| +++ b/gpu/command_buffer/service/gles2_cmd_srgb_converter.cc
|
| @@ -0,0 +1,575 @@
|
| +// 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 {
|
| +namespace gles2 {
|
| +
|
| +SRGBConverter::SRGBConverter(
|
| + const gles2::FeatureInfo* feature_info)
|
| + : feature_info_(feature_info) {
|
| +}
|
| +
|
| +SRGBConverter::~SRGBConverter() {}
|
| +
|
| +// Vertex shader, shared by both decoder program and encoder program
|
| +static 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"
|
| + " vec2 xy = vec2((quad_positions[gl_VertexID] * 2.0) - 1.0);\n"
|
| + " gl_Position = vec4(xy, 0.0, 1.0);\n"
|
| + " v_texcoord = quad_positions[gl_VertexID];\n"
|
| + "}\n";
|
| +
|
| +void SRGBConverter::InitializeSRGBDecoder(
|
| + const gles2::GLES2Decoder* decoder) {
|
| + if (srgb_decoder_initialized_) {
|
| + return;
|
| + }
|
| +
|
| + srgb_decoder_program_ = glCreateProgram();
|
| +
|
| + // Compile the vertex shader
|
| + GLuint vs = glCreateShader(GL_VERTEX_SHADER);
|
| + CompileShader(vs, vs_source);
|
| + glAttachShader(srgb_decoder_program_, vs);
|
| + glDeleteShader(vs);
|
| +
|
| + // Compile the fragment shader
|
| +
|
| + // Sampling texels from a srgb texture to a linear image, it will convert
|
| + // the srgb color space to linear color space automatically as a part of
|
| + // filtering. See the section <sRGB Texture Color Conversion> in GLES and
|
| + // OpenGL spec. So in decoder, we don't need to decode again.
|
| + // However, sampling texels from a linear texture to a srgb image, it will
|
| + // not convert linear to srgb automatically. Shader should always operates
|
| + // in linear space. So in encoder, we need to encode explicitly in 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"
|
| + " vec4 c = texture(u_source_texture, v_texcoord);\n"
|
| + " 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 linear, non-mipmapped sampling with the srgb decoder texture
|
| + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
| + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
| + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
| + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
| + }
|
| +
|
| + 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
|
| + GLuint vs = glCreateShader(GL_VERTEX_SHADER);
|
| + CompileShader(vs, vs_source);
|
| + glAttachShader(srgb_encoder_program_, vs);
|
| + glDeleteShader(vs);
|
| +
|
| + // Compile the fragment shader
|
| +
|
| + // Sampling texels from a srgb texture to a linear image, it will convert
|
| + // the srgb color space to linear color space automatically as a part of
|
| + // filtering. See the section <sRGB Texture Color Conversion> in GLES and
|
| + // OpenGL spec. So in decoder, we don't need to decode again.
|
| + // However, sampling texels from a linear texture to a srgb image, it will
|
| + // not convert linear to srgb automatically. Shader should always operates
|
| + // in linear space. So in encoder, we need to encode explicitly in 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.0) {\n"
|
| + " return 0.0;\n"
|
| + " } else if (color < 0.0031308) {\n"
|
| + " return color * 12.92;\n"
|
| + " } else if (color < 1) {\n"
|
| + " return pow(color, 0.41666) * 1.055 - 0.055;\n"
|
| + " } else {\n"
|
| + " return 1.0;\n"
|
| + " }\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 linear, non-mipmapped sampling with the srgb encoder texture
|
| + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
| + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
| + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
| + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
| + }
|
| +
|
| + 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) {
|
| + // This function blits srgb image in src fb to linear image in dst fb.
|
| + // The steps are:
|
| + // 1) Copy and crop pixels from source srgb image to the 1st texture(srgb).
|
| + // 2) Sampling from the 1st texture and drawing to the 2nd texture(linear).
|
| + // During this step, color space is converted from srgb to linear.
|
| + // 3) Finally, blit pixels from the 2nd texture to the target, which is
|
| + // also a linear image.
|
| + DCHECK(srgb_decoder_initialized_);
|
| +
|
| + // Copy the image from read buffer to the decoder's 1st texture(srgb).
|
| + // TODO(yunchao) If the read buffer is a fbo texture, we can sample
|
| + // directly from that texture. In this way, we can save gpu memory.
|
| + 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 y = srcY1 > srcY0 ? srcY0 : srcY1;
|
| + GLuint width = srcX1 > srcX0 ? srcX1 - srcX0 : srcX0 - srcX1;
|
| + 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));
|
| + GLuint xoffset = c.x() - x;
|
| + GLuint yoffset = c.y() - y;
|
| + glCopyTexImage2D(GL_TEXTURE_2D, 0, src_framebuffer_internal_format,
|
| + c.x(), c.y(), c.width(), c.height(), 0);
|
| +
|
| + // Make a temporary linear texture as the decoder's 2nd texture, where we
|
| + // render the converted (srgb to linear) result to.
|
| + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
| +
|
| + glBindTexture(GL_TEXTURE_2D, srgb_decoder_textures_[1]);
|
| + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
|
| + c.width(), c.height(),
|
| + 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
| + glBindFramebufferEXT(GL_FRAMEBUFFER, srgb_decoder_fbo_);
|
| + glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
| + GL_TEXTURE_2D, srgb_decoder_textures_[1], 0);
|
| +
|
| + // Sampling from the decoder's first texture(srgb) and drawing to the
|
| + // decoder's 2nd texture(linear),
|
| + 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 decoder framebuffer as read framebuffer,
|
| + // blit the converted texture in decoder 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 decoder framebuffer.
|
| + // The xoffset/yoffset can make bliting clamp to the correct edge if
|
| + // CLAMP_TO_EDGE is necessary.
|
| + glBlitFramebuffer(srcX0 < srcX1 ? 0 - xoffset : width - xoffset,
|
| + srcY0 < srcY1 ? 0 - yoffset : height - yoffset,
|
| + srcX0 < srcX1 ? width - xoffset : 0 - xoffset,
|
| + srcY0 < srcY1 ? height - yoffset : 0 - yoffset,
|
| + dstX0, dstY0, dstX1, dstY1, mask, filter);
|
| +
|
| + // 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,
|
| + GLuint src_framebuffer,
|
| + GLenum src_framebuffer_internal_format,
|
| + GLenum src_framebuffer_format,
|
| + GLenum src_framebuffer_type,
|
| + GLuint dst_framebuffer) {
|
| + // This function blits linear image in src fb to srgb image in dst fb.
|
| + // The steps are:
|
| + // 1) BlitFramebuffer from source linear image to a temp linear texture.
|
| + // 2) Sampling from the temp texture and drawing to the target srgb
|
| + // image. During this step, color space is converted from linear to srgb.
|
| +
|
| + DCHECK(srgb_encoder_initialized_);
|
| +
|
| + // Create a temp linear texture as draw buffer. Blit framebuffer from
|
| + // source linear image to the temp linear texture. Filtering is done
|
| + // during bliting. Note that the src and dst coordinates may be reversed.
|
| + glActiveTexture(GL_TEXTURE0);
|
| + glBindTexture(GL_TEXTURE_2D, srgb_encoder_textures_[0]);
|
| +
|
| + GLuint width = dstX1 > dstX0 ? dstX1 - dstX0 : dstX0 - dstX1;
|
| + GLuint height = dstY1 > dstY0 ? dstY1 - dstY0 : dstY0 - dstY1;
|
| + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
| + glTexImage2D(GL_TEXTURE_2D, 0, src_framebuffer_internal_format,
|
| + width, height,
|
| + 0, src_framebuffer_format, src_framebuffer_type, nullptr);
|
| +
|
| + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, srgb_encoder_fbo_);
|
| + glFramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
| + GL_TEXTURE_2D, srgb_encoder_textures_[0], 0);
|
| + glBindFramebufferEXT(GL_READ_FRAMEBUFFER, src_framebuffer);
|
| + glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1,
|
| + dstX0 < dstX1 ? 0 : width,
|
| + dstY0 < dstY1 ? 0 : height,
|
| + dstX0 < dstX1 ? width : 0,
|
| + dstY0 < dstY1 ? height : 0,
|
| + mask, filter);
|
| +
|
| + // Sampling from the linear texture and drawing to the target srgb image.
|
| + // During this step, color space is converted from linear to srgb. We should
|
| + // set appropriate viewport to draw to the correct location in target FB.
|
| + GLuint xstart = dstX0 < dstX1 ? dstX0 : dstX1;
|
| + GLuint ystart = dstY0 < dstY1 ? dstY0 : dstY1;
|
| + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, dst_framebuffer);
|
| + glUseProgram(srgb_encoder_program_);
|
| + glViewport(xstart, ystart, 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);
|
| +
|
| + // Restore state
|
| + decoder->RestoreAllAttributes();
|
| + decoder->RestoreTextureUnitBindings(0);
|
| + decoder->RestoreActiveTexture();
|
| + decoder->RestoreProgramBindings();
|
| + decoder->RestoreBufferBindings();
|
| + decoder->RestoreFramebufferBindings();
|
| + decoder->RestoreGlobalState();
|
| +}
|
| +
|
| +void SRGBConverter::SRGBToSRGB(
|
| + 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) {
|
| + // This function blits srgb image in src fb to srgb image in dst fb.
|
| + // It needs to use decoder's resource, as well as encoder's resources,
|
| + // for instance, decoder's and encoder's fbos, programs and textuers.
|
| + // The steps are:
|
| + // 1) Copy and crop pixels from source srgb image to the 1st texture(srgb).
|
| + // 2) Sampling from the 1st texture and drawing to the 2nd texture(linear).
|
| + // During this step, color space is converted from srgb to linear.
|
| + // 3) Blit pixels from the 2nd texture to the 3rd texture(linear).
|
| + // 4) Sampling from the 3rd texture and drawing to the dst image(srgb).
|
| + // During this step, color space is converted from linear to srgb.
|
| + DCHECK(srgb_decoder_initialized_ && srgb_encoder_initialized_);
|
| +
|
| + // Copy the image from read buffer to the decoder's 1st texture(srgb).
|
| + // TODO(yunchao) If the read buffer is a fbo texture, we can sample
|
| + // directly from that texture. In this way, we can save gpu memory.
|
| + 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 y = srcY1 > srcY0 ? srcY0 : srcY1;
|
| + GLuint width_read = srcX1 > srcX0 ? srcX1 - srcX0 : srcX0 - srcX1;
|
| + GLuint height_read = srcY1 > srcY0 ? srcY1 - srcY0 : srcY0 - srcY1;
|
| + gfx::Rect c(0, 0, framebuffer_size.width(), framebuffer_size.height());
|
| + c.Intersect(gfx::Rect(x, y, width_read, height_read));
|
| + GLuint xoffset = c.x() - x;
|
| + GLuint yoffset = c.y() - y;
|
| + glCopyTexImage2D(GL_TEXTURE_2D, 0, src_framebuffer_internal_format,
|
| + c.x(), c.y(), c.width(), c.height(), 0);
|
| +
|
| + // Make a temporary linear texture as the decoder's 2nd texture, where we
|
| + // render the converted (srgb to linear) result to.
|
| + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
| +
|
| + glBindTexture(GL_TEXTURE_2D, srgb_decoder_textures_[1]);
|
| + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
|
| + c.width(), c.height(),
|
| + 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
| + glBindFramebufferEXT(GL_FRAMEBUFFER, srgb_decoder_fbo_);
|
| + glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
| + GL_TEXTURE_2D, srgb_decoder_textures_[1], 0);
|
| +
|
| + // Sampling from the decoder's first texture(srgb) and drawing to the
|
| + // decoder's 2nd texture(linear),
|
| + glUseProgram(srgb_decoder_program_);
|
| + glViewport(0, 0, width_read, height_read);
|
| + 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);
|
| +
|
| + // Create the 3rd texture(linear) as decoder's draw buffer. Blit framebuffer
|
| + // from the 2nd texture(linear) to the 3rd texture. Filtering is done
|
| + // during bliting. Note that the src and dst coordinates may be reversed.
|
| + glBindTexture(GL_TEXTURE_2D, srgb_encoder_textures_[0]);
|
| +
|
| + GLuint width_draw = dstX1 > dstX0 ? dstX1 - dstX0 : dstX0 - dstX1;
|
| + GLuint height_draw = dstY1 > dstY0 ? dstY1 - dstY0 : dstY0 - dstY1;
|
| + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
| + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
|
| + width_draw, height_draw,
|
| + 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
| +
|
| + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, srgb_encoder_fbo_);
|
| + glFramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
| + GL_TEXTURE_2D, srgb_encoder_textures_[0], 0);
|
| + glBindFramebufferEXT(GL_READ_FRAMEBUFFER, srgb_decoder_fbo_);
|
| + glBlitFramebuffer(srcX0 < srcX1 ? 0 - xoffset : width_read - xoffset,
|
| + srcY0 < srcY1 ? 0 - yoffset : height_read - yoffset,
|
| + srcX0 < srcX1 ? width_read - xoffset : 0 - xoffset,
|
| + srcY0 < srcY1 ? height_read - yoffset : 0 - yoffset,
|
| + dstX0 < dstX1 ? 0 : width_draw,
|
| + dstY0 < dstY1 ? 0 : height_draw,
|
| + dstX0 < dstX1 ? width_draw : 0,
|
| + dstY0 < dstY1 ? height_draw : 0,
|
| + mask, filter);
|
| +
|
| + // Sampling from the 3rd texture(linear) and drawing to the target srgb image.
|
| + // During this step, color space is converted from linear to srgb. We should
|
| + // set appropriate viewport to draw to the correct location in target FB.
|
| + GLuint xstart = dstX0 < dstX1 ? dstX0 : dstX1;
|
| + GLuint ystart = dstY0 < dstY1 ? dstY0 : dstY1;
|
| + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, dst_framebuffer);
|
| + glUseProgram(srgb_encoder_program_);
|
| + glViewport(xstart, ystart, width_draw, height_draw);
|
| +
|
| + glBindTexture(GL_TEXTURE_2D, srgb_encoder_textures_[0]);
|
| + glBindVertexArrayOES(srgb_encoder_vao_);
|
| +
|
| + glDrawArrays(GL_TRIANGLES, 0, 6);
|
| +
|
| + // Restore state
|
| + decoder->RestoreAllAttributes();
|
| + decoder->RestoreTextureUnitBindings(0);
|
| + decoder->RestoreActiveTexture();
|
| + decoder->RestoreProgramBindings();
|
| + decoder->RestoreBufferBindings();
|
| + decoder->RestoreFramebufferBindings();
|
| + decoder->RestoreGlobalState();
|
| +}
|
| +
|
| +} // namespace gles2.
|
| +} // namespace gpu
|
|
|