OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "gpu/command_buffer/service/gles2_cmd_srgb_converter.h" |
| 6 |
| 7 #include "gpu/command_buffer/service/texture_manager.h" |
| 8 #include "ui/gl/gl_version_info.h" |
| 9 |
| 10 namespace { |
| 11 |
| 12 void CompileShader(GLuint shader, const char* shader_source) { |
| 13 glShaderSource(shader, 1, &shader_source, 0); |
| 14 glCompileShader(shader); |
| 15 #ifndef NDEBUG |
| 16 GLint compile_status; |
| 17 glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_status); |
| 18 if (GL_TRUE != compile_status) |
| 19 DLOG(ERROR) << "CopyTexImage: shader compilation failure."; |
| 20 #endif |
| 21 } |
| 22 |
| 23 } // anonymous namespace |
| 24 |
| 25 namespace gpu { |
| 26 namespace gles2 { |
| 27 |
| 28 SRGBConverter::SRGBConverter( |
| 29 const gles2::FeatureInfo* feature_info) |
| 30 : feature_info_(feature_info) { |
| 31 } |
| 32 |
| 33 SRGBConverter::~SRGBConverter() {} |
| 34 |
| 35 // Vertex shader, shared by both decoder program and encoder program |
| 36 static const char* vs_source = |
| 37 "#version 150\n" |
| 38 "out vec2 v_texcoord;\n" |
| 39 "\n" |
| 40 "void main()\n" |
| 41 "{\n" |
| 42 " const vec2 quad_positions[6] = vec2[6]\n" |
| 43 " (\n" |
| 44 " vec2(0.0f, 0.0f),\n" |
| 45 " vec2(0.0f, 1.0f),\n" |
| 46 " vec2(1.0f, 0.0f),\n" |
| 47 "\n" |
| 48 " vec2(0.0f, 1.0f),\n" |
| 49 " vec2(1.0f, 0.0f),\n" |
| 50 " vec2(1.0f, 1.0f)\n" |
| 51 " );\n" |
| 52 "\n" |
| 53 " vec2 xy = vec2((quad_positions[gl_VertexID] * 2.0) - 1.0);\n" |
| 54 " gl_Position = vec4(xy, 0.0, 1.0);\n" |
| 55 " v_texcoord = quad_positions[gl_VertexID];\n" |
| 56 "}\n"; |
| 57 |
| 58 void SRGBConverter::InitializeSRGBDecoder( |
| 59 const gles2::GLES2Decoder* decoder) { |
| 60 if (srgb_decoder_initialized_) { |
| 61 return; |
| 62 } |
| 63 |
| 64 srgb_decoder_program_ = glCreateProgram(); |
| 65 |
| 66 // Compile the vertex shader |
| 67 GLuint vs = glCreateShader(GL_VERTEX_SHADER); |
| 68 CompileShader(vs, vs_source); |
| 69 glAttachShader(srgb_decoder_program_, vs); |
| 70 glDeleteShader(vs); |
| 71 |
| 72 // Compile the fragment shader |
| 73 |
| 74 // Sampling texels from a srgb texture to a linear image, it will convert |
| 75 // the srgb color space to linear color space automatically as a part of |
| 76 // filtering. See the section <sRGB Texture Color Conversion> in GLES and |
| 77 // OpenGL spec. So in decoder, we don't need to decode again. |
| 78 // However, sampling texels from a linear texture to a srgb image, it will |
| 79 // not convert linear to srgb automatically. Shader should always operates |
| 80 // in linear space. So in encoder, we need to encode explicitly in shader. |
| 81 const char* fs_source = |
| 82 "#version 150\n" |
| 83 "uniform sampler2D u_source_texture;\n" |
| 84 "in vec2 v_texcoord;\n" |
| 85 "out vec4 output_color;\n" |
| 86 "\n" |
| 87 "void main()\n" |
| 88 "{\n" |
| 89 " vec4 c = texture(u_source_texture, v_texcoord);\n" |
| 90 " output_color = c;\n" |
| 91 "}\n"; |
| 92 |
| 93 GLuint fs = glCreateShader(GL_FRAGMENT_SHADER); |
| 94 CompileShader(fs, fs_source); |
| 95 glAttachShader(srgb_decoder_program_, fs); |
| 96 glDeleteShader(fs); |
| 97 |
| 98 glLinkProgram(srgb_decoder_program_); |
| 99 #ifndef NDEBUG |
| 100 GLint linked = 0; |
| 101 glGetProgramiv(srgb_decoder_program_, GL_LINK_STATUS, &linked); |
| 102 if (!linked) { |
| 103 DLOG(ERROR) << "BlitFramebuffer: program link failure."; |
| 104 } |
| 105 #endif |
| 106 |
| 107 GLuint texture_uniform = |
| 108 glGetUniformLocation(srgb_decoder_program_, "u_source_texture"); |
| 109 glUseProgram(srgb_decoder_program_); |
| 110 glUniform1i(texture_uniform, 0); |
| 111 |
| 112 glGenTextures(srgb_decoder_textures_.size(), srgb_decoder_textures_.data()); |
| 113 glActiveTexture(GL_TEXTURE0); |
| 114 for (auto srgb_decoder_texture : srgb_decoder_textures_) { |
| 115 glBindTexture(GL_TEXTURE_2D, srgb_decoder_texture); |
| 116 |
| 117 // Use linear, non-mipmapped sampling with the srgb decoder texture |
| 118 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| 119 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| 120 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| 121 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| 122 } |
| 123 |
| 124 glGenFramebuffersEXT(1, &srgb_decoder_fbo_); |
| 125 glGenVertexArraysOES(1, &srgb_decoder_vao_); |
| 126 |
| 127 decoder->RestoreTextureUnitBindings(0); |
| 128 decoder->RestoreActiveTexture(); |
| 129 decoder->RestoreProgramBindings(); |
| 130 |
| 131 srgb_decoder_initialized_ = true; |
| 132 } |
| 133 |
| 134 void SRGBConverter::InitializeSRGBEncoder( |
| 135 const gles2::GLES2Decoder* decoder) { |
| 136 if (srgb_encoder_initialized_) { |
| 137 return; |
| 138 } |
| 139 |
| 140 srgb_encoder_program_ = glCreateProgram(); |
| 141 |
| 142 // Compile the vertex shader |
| 143 GLuint vs = glCreateShader(GL_VERTEX_SHADER); |
| 144 CompileShader(vs, vs_source); |
| 145 glAttachShader(srgb_encoder_program_, vs); |
| 146 glDeleteShader(vs); |
| 147 |
| 148 // Compile the fragment shader |
| 149 |
| 150 // Sampling texels from a srgb texture to a linear image, it will convert |
| 151 // the srgb color space to linear color space automatically as a part of |
| 152 // filtering. See the section <sRGB Texture Color Conversion> in GLES and |
| 153 // OpenGL spec. So in decoder, we don't need to decode again. |
| 154 // However, sampling texels from a linear texture to a srgb image, it will |
| 155 // not convert linear to srgb automatically. Shader should always operates |
| 156 // in linear space. So in encoder, we need to encode explicitly in shader. |
| 157 const char* fs_source = |
| 158 "#version 150\n" |
| 159 "uniform sampler2D u_source_texture;\n" |
| 160 "in vec2 v_texcoord;\n" |
| 161 "out vec4 output_color;\n" |
| 162 "\n" |
| 163 "float encode(float color)\n" |
| 164 "{\n" |
| 165 " float encoded_color;\n" |
| 166 " if (color <= 0.0) {\n" |
| 167 " return 0.0;\n" |
| 168 " } else if (color < 0.0031308) {\n" |
| 169 " return color * 12.92;\n" |
| 170 " } else if (color < 1) {\n" |
| 171 " return pow(color, 0.41666) * 1.055 - 0.055;\n" |
| 172 " } else {\n" |
| 173 " return 1.0;\n" |
| 174 " }\n" |
| 175 "}\n" |
| 176 "\n" |
| 177 "void main()\n" |
| 178 "{\n" |
| 179 " vec4 c = texture(u_source_texture, v_texcoord);\n" |
| 180 " output_color = vec4(encode(c.r), encode(c.g), encode(c.b), c.a);\n" |
| 181 "}\n"; |
| 182 |
| 183 GLuint fs = glCreateShader(GL_FRAGMENT_SHADER); |
| 184 CompileShader(fs, fs_source); |
| 185 glAttachShader(srgb_encoder_program_, fs); |
| 186 glDeleteShader(fs); |
| 187 |
| 188 glLinkProgram(srgb_encoder_program_); |
| 189 #ifndef NDEBUG |
| 190 GLint linked = 0; |
| 191 glGetProgramiv(srgb_encoder_program_, GL_LINK_STATUS, &linked); |
| 192 if (!linked) { |
| 193 DLOG(ERROR) << "SRGB Encoder for BlitFramebuffer: program link failure."; |
| 194 } |
| 195 #endif |
| 196 |
| 197 GLuint texture_uniform = |
| 198 glGetUniformLocation(srgb_encoder_program_, "u_source_texture"); |
| 199 glUseProgram(srgb_encoder_program_); |
| 200 glUniform1i(texture_uniform, 0); |
| 201 |
| 202 glGenTextures(srgb_encoder_textures_.size(), srgb_encoder_textures_.data()); |
| 203 glActiveTexture(GL_TEXTURE0); |
| 204 for (auto srgb_encoder_texture : srgb_encoder_textures_) { |
| 205 glBindTexture(GL_TEXTURE_2D, srgb_encoder_texture); |
| 206 |
| 207 // Use linear, non-mipmapped sampling with the srgb encoder texture |
| 208 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| 209 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| 210 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| 211 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| 212 } |
| 213 |
| 214 glGenFramebuffersEXT(1, &srgb_encoder_fbo_); |
| 215 glGenVertexArraysOES(1, &srgb_encoder_vao_); |
| 216 |
| 217 decoder->RestoreTextureUnitBindings(0); |
| 218 decoder->RestoreActiveTexture(); |
| 219 decoder->RestoreProgramBindings(); |
| 220 |
| 221 srgb_encoder_initialized_ = true; |
| 222 } |
| 223 |
| 224 void SRGBConverter::Destroy() { |
| 225 if (srgb_decoder_initialized_) { |
| 226 glDeleteProgram(srgb_decoder_program_); |
| 227 srgb_decoder_program_ = 0; |
| 228 |
| 229 glDeleteTextures(srgb_decoder_textures_.size(), |
| 230 srgb_decoder_textures_.data()); |
| 231 srgb_decoder_textures_.fill(0); |
| 232 |
| 233 glDeleteFramebuffersEXT(1, &srgb_decoder_fbo_); |
| 234 srgb_decoder_fbo_ = 0; |
| 235 |
| 236 glDeleteVertexArraysOES(1, &srgb_decoder_vao_); |
| 237 srgb_decoder_vao_ = 0; |
| 238 |
| 239 srgb_decoder_initialized_ = false; |
| 240 } |
| 241 |
| 242 if (srgb_encoder_initialized_) { |
| 243 glDeleteProgram(srgb_encoder_program_); |
| 244 srgb_encoder_program_ = 0; |
| 245 |
| 246 glDeleteTextures(srgb_encoder_textures_.size(), |
| 247 srgb_encoder_textures_.data()); |
| 248 srgb_encoder_textures_.fill(0); |
| 249 |
| 250 glDeleteFramebuffersEXT(1, &srgb_encoder_fbo_); |
| 251 srgb_encoder_fbo_ = 0; |
| 252 |
| 253 glDeleteVertexArraysOES(1, &srgb_encoder_vao_); |
| 254 srgb_encoder_vao_ = 0; |
| 255 |
| 256 srgb_encoder_initialized_ = false; |
| 257 } |
| 258 } |
| 259 |
| 260 void SRGBConverter::SRGBToLinear( |
| 261 const gles2::GLES2Decoder* decoder, |
| 262 GLint srcX0, |
| 263 GLint srcY0, |
| 264 GLint srcX1, |
| 265 GLint srcY1, |
| 266 GLint dstX0, |
| 267 GLint dstY0, |
| 268 GLint dstX1, |
| 269 GLint dstY1, |
| 270 GLbitfield mask, |
| 271 GLenum filter, |
| 272 const gfx::Size& framebuffer_size, |
| 273 GLuint src_framebuffer, |
| 274 GLenum src_framebuffer_internal_format, |
| 275 GLuint dst_framebuffer) { |
| 276 // This function blits srgb image in src fb to linear image in dst fb. |
| 277 // The steps are: |
| 278 // 1) Copy and crop pixels from source srgb image to the 1st texture(srgb). |
| 279 // 2) Sampling from the 1st texture and drawing to the 2nd texture(linear). |
| 280 // During this step, color space is converted from srgb to linear. |
| 281 // 3) Finally, blit pixels from the 2nd texture to the target, which is |
| 282 // also a linear image. |
| 283 DCHECK(srgb_decoder_initialized_); |
| 284 |
| 285 // Copy the image from read buffer to the decoder's 1st texture(srgb). |
| 286 // TODO(yunchao) If the read buffer is a fbo texture, we can sample |
| 287 // directly from that texture. In this way, we can save gpu memory. |
| 288 glBindFramebufferEXT(GL_FRAMEBUFFER, src_framebuffer); |
| 289 glActiveTexture(GL_TEXTURE0); |
| 290 glBindTexture(GL_TEXTURE_2D, srgb_decoder_textures_[0]); |
| 291 |
| 292 // We should not copy pixels outside of the read framebuffer. If we read |
| 293 // these pixels, they would become in-bound during BlitFramebuffer. However, |
| 294 // Out-of-bounds pixels will be initialized to 0 in CopyTexSubImage. But they |
| 295 // should read as if the GL_CLAMP_TO_EDGE texture mapping mode were applied |
| 296 // during BlitFramebuffer when the filter is GL_LINEAR. |
| 297 GLuint x = srcX1 > srcX0 ? srcX0 : srcX1; |
| 298 GLuint y = srcY1 > srcY0 ? srcY0 : srcY1; |
| 299 GLuint width = srcX1 > srcX0 ? srcX1 - srcX0 : srcX0 - srcX1; |
| 300 GLuint height = srcY1 > srcY0 ? srcY1 - srcY0 : srcY0 - srcY1; |
| 301 gfx::Rect c(0, 0, framebuffer_size.width(), framebuffer_size.height()); |
| 302 c.Intersect(gfx::Rect(x, y, width, height)); |
| 303 GLuint xoffset = c.x() - x; |
| 304 GLuint yoffset = c.y() - y; |
| 305 glCopyTexImage2D(GL_TEXTURE_2D, 0, src_framebuffer_internal_format, |
| 306 c.x(), c.y(), c.width(), c.height(), 0); |
| 307 |
| 308 // Make a temporary linear texture as the decoder's 2nd texture, where we |
| 309 // render the converted (srgb to linear) result to. |
| 310 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); |
| 311 |
| 312 glBindTexture(GL_TEXTURE_2D, srgb_decoder_textures_[1]); |
| 313 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, |
| 314 c.width(), c.height(), |
| 315 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| 316 glBindFramebufferEXT(GL_FRAMEBUFFER, srgb_decoder_fbo_); |
| 317 glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, |
| 318 GL_TEXTURE_2D, srgb_decoder_textures_[1], 0); |
| 319 |
| 320 // Sampling from the decoder's first texture(srgb) and drawing to the |
| 321 // decoder's 2nd texture(linear), |
| 322 glUseProgram(srgb_decoder_program_); |
| 323 glViewport(0, 0, width, height); |
| 324 glDisable(GL_SCISSOR_TEST); |
| 325 glDisable(GL_DEPTH_TEST); |
| 326 glDisable(GL_STENCIL_TEST); |
| 327 glDisable(GL_CULL_FACE); |
| 328 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); |
| 329 glDepthMask(GL_FALSE); |
| 330 glDisable(GL_BLEND); |
| 331 glDisable(GL_DITHER); |
| 332 |
| 333 glBindTexture(GL_TEXTURE_2D, srgb_decoder_textures_[0]); |
| 334 glBindVertexArrayOES(srgb_decoder_vao_); |
| 335 |
| 336 glDrawArrays(GL_TRIANGLES, 0, 6); |
| 337 |
| 338 // Finally, bind the decoder framebuffer as read framebuffer, |
| 339 // blit the converted texture in decoder fbo to the destination texture |
| 340 // in destination framebuffer. |
| 341 glBindFramebufferEXT(GL_READ_FRAMEBUFFER, srgb_decoder_fbo_); |
| 342 glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, dst_framebuffer); |
| 343 // Note that the source region has been changed in decoder framebuffer. |
| 344 // The xoffset/yoffset can make bliting clamp to the correct edge if |
| 345 // CLAMP_TO_EDGE is necessary. |
| 346 glBlitFramebuffer(srcX0 < srcX1 ? 0 - xoffset : width - xoffset, |
| 347 srcY0 < srcY1 ? 0 - yoffset : height - yoffset, |
| 348 srcX0 < srcX1 ? width - xoffset : 0 - xoffset, |
| 349 srcY0 < srcY1 ? height - yoffset : 0 - yoffset, |
| 350 dstX0, dstY0, dstX1, dstY1, mask, filter); |
| 351 |
| 352 // Restore state |
| 353 decoder->RestoreAllAttributes(); |
| 354 decoder->RestoreTextureUnitBindings(0); |
| 355 decoder->RestoreActiveTexture(); |
| 356 decoder->RestoreProgramBindings(); |
| 357 decoder->RestoreBufferBindings(); |
| 358 decoder->RestoreFramebufferBindings(); |
| 359 decoder->RestoreGlobalState(); |
| 360 } |
| 361 |
| 362 void SRGBConverter::LinearToSRGB( |
| 363 const gles2::GLES2Decoder* decoder, |
| 364 GLint srcX0, |
| 365 GLint srcY0, |
| 366 GLint srcX1, |
| 367 GLint srcY1, |
| 368 GLint dstX0, |
| 369 GLint dstY0, |
| 370 GLint dstX1, |
| 371 GLint dstY1, |
| 372 GLbitfield mask, |
| 373 GLenum filter, |
| 374 GLuint src_framebuffer, |
| 375 GLenum src_framebuffer_internal_format, |
| 376 GLenum src_framebuffer_format, |
| 377 GLenum src_framebuffer_type, |
| 378 GLuint dst_framebuffer) { |
| 379 // This function blits linear image in src fb to srgb image in dst fb. |
| 380 // The steps are: |
| 381 // 1) BlitFramebuffer from source linear image to a temp linear texture. |
| 382 // 2) Sampling from the temp texture and drawing to the target srgb |
| 383 // image. During this step, color space is converted from linear to srgb. |
| 384 |
| 385 DCHECK(srgb_encoder_initialized_); |
| 386 |
| 387 // Create a temp linear texture as draw buffer. Blit framebuffer from |
| 388 // source linear image to the temp linear texture. Filtering is done |
| 389 // during bliting. Note that the src and dst coordinates may be reversed. |
| 390 glActiveTexture(GL_TEXTURE0); |
| 391 glBindTexture(GL_TEXTURE_2D, srgb_encoder_textures_[0]); |
| 392 |
| 393 GLuint width = dstX1 > dstX0 ? dstX1 - dstX0 : dstX0 - dstX1; |
| 394 GLuint height = dstY1 > dstY0 ? dstY1 - dstY0 : dstY0 - dstY1; |
| 395 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); |
| 396 glTexImage2D(GL_TEXTURE_2D, 0, src_framebuffer_internal_format, |
| 397 width, height, |
| 398 0, src_framebuffer_format, src_framebuffer_type, nullptr); |
| 399 |
| 400 glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, srgb_encoder_fbo_); |
| 401 glFramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, |
| 402 GL_TEXTURE_2D, srgb_encoder_textures_[0], 0); |
| 403 glBindFramebufferEXT(GL_READ_FRAMEBUFFER, src_framebuffer); |
| 404 glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, |
| 405 dstX0 < dstX1 ? 0 : width, |
| 406 dstY0 < dstY1 ? 0 : height, |
| 407 dstX0 < dstX1 ? width : 0, |
| 408 dstY0 < dstY1 ? height : 0, |
| 409 mask, filter); |
| 410 |
| 411 // Sampling from the linear texture and drawing to the target srgb image. |
| 412 // During this step, color space is converted from linear to srgb. We should |
| 413 // set appropriate viewport to draw to the correct location in target FB. |
| 414 GLuint xstart = dstX0 < dstX1 ? dstX0 : dstX1; |
| 415 GLuint ystart = dstY0 < dstY1 ? dstY0 : dstY1; |
| 416 glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, dst_framebuffer); |
| 417 glUseProgram(srgb_encoder_program_); |
| 418 glViewport(xstart, ystart, width, height); |
| 419 glDisable(GL_SCISSOR_TEST); |
| 420 glDisable(GL_DEPTH_TEST); |
| 421 glDisable(GL_STENCIL_TEST); |
| 422 glDisable(GL_CULL_FACE); |
| 423 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); |
| 424 glDepthMask(GL_FALSE); |
| 425 glDisable(GL_BLEND); |
| 426 glDisable(GL_DITHER); |
| 427 |
| 428 glBindTexture(GL_TEXTURE_2D, srgb_encoder_textures_[0]); |
| 429 glBindVertexArrayOES(srgb_encoder_vao_); |
| 430 |
| 431 glDrawArrays(GL_TRIANGLES, 0, 6); |
| 432 |
| 433 // Restore state |
| 434 decoder->RestoreAllAttributes(); |
| 435 decoder->RestoreTextureUnitBindings(0); |
| 436 decoder->RestoreActiveTexture(); |
| 437 decoder->RestoreProgramBindings(); |
| 438 decoder->RestoreBufferBindings(); |
| 439 decoder->RestoreFramebufferBindings(); |
| 440 decoder->RestoreGlobalState(); |
| 441 } |
| 442 |
| 443 void SRGBConverter::SRGBToSRGB( |
| 444 const gles2::GLES2Decoder* decoder, |
| 445 GLint srcX0, |
| 446 GLint srcY0, |
| 447 GLint srcX1, |
| 448 GLint srcY1, |
| 449 GLint dstX0, |
| 450 GLint dstY0, |
| 451 GLint dstX1, |
| 452 GLint dstY1, |
| 453 GLbitfield mask, |
| 454 GLenum filter, |
| 455 const gfx::Size& framebuffer_size, |
| 456 GLuint src_framebuffer, |
| 457 GLenum src_framebuffer_internal_format, |
| 458 GLuint dst_framebuffer) { |
| 459 // This function blits srgb image in src fb to srgb image in dst fb. |
| 460 // It needs to use decoder's resource, as well as encoder's resources, |
| 461 // for instance, decoder's and encoder's fbos, programs and textuers. |
| 462 // The steps are: |
| 463 // 1) Copy and crop pixels from source srgb image to the 1st texture(srgb). |
| 464 // 2) Sampling from the 1st texture and drawing to the 2nd texture(linear). |
| 465 // During this step, color space is converted from srgb to linear. |
| 466 // 3) Blit pixels from the 2nd texture to the 3rd texture(linear). |
| 467 // 4) Sampling from the 3rd texture and drawing to the dst image(srgb). |
| 468 // During this step, color space is converted from linear to srgb. |
| 469 DCHECK(srgb_decoder_initialized_ && srgb_encoder_initialized_); |
| 470 |
| 471 // Copy the image from read buffer to the decoder's 1st texture(srgb). |
| 472 // TODO(yunchao) If the read buffer is a fbo texture, we can sample |
| 473 // directly from that texture. In this way, we can save gpu memory. |
| 474 glBindFramebufferEXT(GL_FRAMEBUFFER, src_framebuffer); |
| 475 glActiveTexture(GL_TEXTURE0); |
| 476 glBindTexture(GL_TEXTURE_2D, srgb_decoder_textures_[0]); |
| 477 |
| 478 // We should not copy pixels outside of the read framebuffer. If we read |
| 479 // these pixels, they would become in-bound during BlitFramebuffer. However, |
| 480 // Out-of-bounds pixels will be initialized to 0 in CopyTexSubImage. But they |
| 481 // should read as if the GL_CLAMP_TO_EDGE texture mapping mode were applied |
| 482 // during BlitFramebuffer when the filter is GL_LINEAR. |
| 483 GLuint x = srcX1 > srcX0 ? srcX0 : srcX1; |
| 484 GLuint y = srcY1 > srcY0 ? srcY0 : srcY1; |
| 485 GLuint width_read = srcX1 > srcX0 ? srcX1 - srcX0 : srcX0 - srcX1; |
| 486 GLuint height_read = srcY1 > srcY0 ? srcY1 - srcY0 : srcY0 - srcY1; |
| 487 gfx::Rect c(0, 0, framebuffer_size.width(), framebuffer_size.height()); |
| 488 c.Intersect(gfx::Rect(x, y, width_read, height_read)); |
| 489 GLuint xoffset = c.x() - x; |
| 490 GLuint yoffset = c.y() - y; |
| 491 glCopyTexImage2D(GL_TEXTURE_2D, 0, src_framebuffer_internal_format, |
| 492 c.x(), c.y(), c.width(), c.height(), 0); |
| 493 |
| 494 // Make a temporary linear texture as the decoder's 2nd texture, where we |
| 495 // render the converted (srgb to linear) result to. |
| 496 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); |
| 497 |
| 498 glBindTexture(GL_TEXTURE_2D, srgb_decoder_textures_[1]); |
| 499 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, |
| 500 c.width(), c.height(), |
| 501 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| 502 glBindFramebufferEXT(GL_FRAMEBUFFER, srgb_decoder_fbo_); |
| 503 glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, |
| 504 GL_TEXTURE_2D, srgb_decoder_textures_[1], 0); |
| 505 |
| 506 // Sampling from the decoder's first texture(srgb) and drawing to the |
| 507 // decoder's 2nd texture(linear), |
| 508 glUseProgram(srgb_decoder_program_); |
| 509 glViewport(0, 0, width_read, height_read); |
| 510 glDisable(GL_SCISSOR_TEST); |
| 511 glDisable(GL_DEPTH_TEST); |
| 512 glDisable(GL_STENCIL_TEST); |
| 513 glDisable(GL_CULL_FACE); |
| 514 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); |
| 515 glDepthMask(GL_FALSE); |
| 516 glDisable(GL_BLEND); |
| 517 glDisable(GL_DITHER); |
| 518 |
| 519 glBindTexture(GL_TEXTURE_2D, srgb_decoder_textures_[0]); |
| 520 glBindVertexArrayOES(srgb_decoder_vao_); |
| 521 |
| 522 glDrawArrays(GL_TRIANGLES, 0, 6); |
| 523 |
| 524 // Create the 3rd texture(linear) as decoder's draw buffer. Blit framebuffer |
| 525 // from the 2nd texture(linear) to the 3rd texture. Filtering is done |
| 526 // during bliting. Note that the src and dst coordinates may be reversed. |
| 527 glBindTexture(GL_TEXTURE_2D, srgb_encoder_textures_[0]); |
| 528 |
| 529 GLuint width_draw = dstX1 > dstX0 ? dstX1 - dstX0 : dstX0 - dstX1; |
| 530 GLuint height_draw = dstY1 > dstY0 ? dstY1 - dstY0 : dstY0 - dstY1; |
| 531 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); |
| 532 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, |
| 533 width_draw, height_draw, |
| 534 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| 535 |
| 536 glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, srgb_encoder_fbo_); |
| 537 glFramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, |
| 538 GL_TEXTURE_2D, srgb_encoder_textures_[0], 0); |
| 539 glBindFramebufferEXT(GL_READ_FRAMEBUFFER, srgb_decoder_fbo_); |
| 540 glBlitFramebuffer(srcX0 < srcX1 ? 0 - xoffset : width_read - xoffset, |
| 541 srcY0 < srcY1 ? 0 - yoffset : height_read - yoffset, |
| 542 srcX0 < srcX1 ? width_read - xoffset : 0 - xoffset, |
| 543 srcY0 < srcY1 ? height_read - yoffset : 0 - yoffset, |
| 544 dstX0 < dstX1 ? 0 : width_draw, |
| 545 dstY0 < dstY1 ? 0 : height_draw, |
| 546 dstX0 < dstX1 ? width_draw : 0, |
| 547 dstY0 < dstY1 ? height_draw : 0, |
| 548 mask, filter); |
| 549 |
| 550 // Sampling from the 3rd texture(linear) and drawing to the target srgb image. |
| 551 // During this step, color space is converted from linear to srgb. We should |
| 552 // set appropriate viewport to draw to the correct location in target FB. |
| 553 GLuint xstart = dstX0 < dstX1 ? dstX0 : dstX1; |
| 554 GLuint ystart = dstY0 < dstY1 ? dstY0 : dstY1; |
| 555 glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, dst_framebuffer); |
| 556 glUseProgram(srgb_encoder_program_); |
| 557 glViewport(xstart, ystart, width_draw, height_draw); |
| 558 |
| 559 glBindTexture(GL_TEXTURE_2D, srgb_encoder_textures_[0]); |
| 560 glBindVertexArrayOES(srgb_encoder_vao_); |
| 561 |
| 562 glDrawArrays(GL_TRIANGLES, 0, 6); |
| 563 |
| 564 // Restore state |
| 565 decoder->RestoreAllAttributes(); |
| 566 decoder->RestoreTextureUnitBindings(0); |
| 567 decoder->RestoreActiveTexture(); |
| 568 decoder->RestoreProgramBindings(); |
| 569 decoder->RestoreBufferBindings(); |
| 570 decoder->RestoreFramebufferBindings(); |
| 571 decoder->RestoreGlobalState(); |
| 572 } |
| 573 |
| 574 } // namespace gles2. |
| 575 } // namespace gpu |
OLD | NEW |