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 |
| 36 |
| 37 void SRGBConverter::InitializeSRGBConverterProgram() { |
| 38 if (srgb_converter_program_) { |
| 39 return; |
| 40 } |
| 41 |
| 42 srgb_converter_program_ = glCreateProgram(); |
| 43 |
| 44 // Compile the vertex shader |
| 45 const char* vs_source = |
| 46 "#version 150\n" |
| 47 "out vec2 v_texcoord;\n" |
| 48 "\n" |
| 49 "void main()\n" |
| 50 "{\n" |
| 51 " const vec2 quad_positions[6] = vec2[6]\n" |
| 52 " (\n" |
| 53 " vec2(0.0f, 0.0f),\n" |
| 54 " vec2(0.0f, 1.0f),\n" |
| 55 " vec2(1.0f, 0.0f),\n" |
| 56 "\n" |
| 57 " vec2(0.0f, 1.0f),\n" |
| 58 " vec2(1.0f, 0.0f),\n" |
| 59 " vec2(1.0f, 1.0f)\n" |
| 60 " );\n" |
| 61 "\n" |
| 62 " vec2 xy = vec2((quad_positions[gl_VertexID] * 2.0) - 1.0);\n" |
| 63 " gl_Position = vec4(xy, 0.0, 1.0);\n" |
| 64 " v_texcoord = quad_positions[gl_VertexID];\n" |
| 65 "}\n"; |
| 66 GLuint vs = glCreateShader(GL_VERTEX_SHADER); |
| 67 CompileShader(vs, vs_source); |
| 68 glAttachShader(srgb_converter_program_, vs); |
| 69 glDeleteShader(vs); |
| 70 |
| 71 // Compile the fragment shader |
| 72 |
| 73 // Sampling texels from a srgb texture to a linear image, it will convert |
| 74 // the srgb color space to linear color space automatically as a part of |
| 75 // filtering. See the section <sRGB Texture Color Conversion> in GLES and |
| 76 // OpenGL spec. So during decoding, we don't need to use the equation to |
| 77 // explicitly decode srgb to linear in fragment shader. |
| 78 // Drawing to a srgb image, it will convert linear to srgb automatically. |
| 79 // See the section <sRGB Conversion> in GLES and OpenGL spec. So during |
| 80 // encoding, we don't need to use the equation to explicitly encode linear |
| 81 // to srgb in fragment shader. |
| 82 // As a result, we just use a simple fragment shader to do srgb conversion. |
| 83 const char* fs_source = |
| 84 "#version 150\n" |
| 85 "uniform sampler2D u_source_texture;\n" |
| 86 "in vec2 v_texcoord;\n" |
| 87 "out vec4 output_color;\n" |
| 88 "\n" |
| 89 "void main()\n" |
| 90 "{\n" |
| 91 " vec4 c = texture(u_source_texture, v_texcoord);\n" |
| 92 " output_color = c;\n" |
| 93 "}\n"; |
| 94 |
| 95 GLuint fs = glCreateShader(GL_FRAGMENT_SHADER); |
| 96 CompileShader(fs, fs_source); |
| 97 glAttachShader(srgb_converter_program_, fs); |
| 98 glDeleteShader(fs); |
| 99 |
| 100 glLinkProgram(srgb_converter_program_); |
| 101 #ifndef NDEBUG |
| 102 GLint linked = 0; |
| 103 glGetProgramiv(srgb_converter_program_, GL_LINK_STATUS, &linked); |
| 104 if (!linked) { |
| 105 DLOG(ERROR) << "BlitFramebuffer: program link failure."; |
| 106 } |
| 107 #endif |
| 108 |
| 109 GLuint texture_uniform = |
| 110 glGetUniformLocation(srgb_converter_program_, "u_source_texture"); |
| 111 glUseProgram(srgb_converter_program_); |
| 112 glUniform1i(texture_uniform, 0); |
| 113 } |
| 114 |
| 115 void SRGBConverter::InitializeSRGBConverter( |
| 116 const gles2::GLES2Decoder* decoder) { |
| 117 if (srgb_converter_initialized_) { |
| 118 return; |
| 119 } |
| 120 |
| 121 InitializeSRGBConverterProgram(); |
| 122 |
| 123 glGenTextures( |
| 124 srgb_converter_textures_.size(), srgb_converter_textures_.data()); |
| 125 glActiveTexture(GL_TEXTURE0); |
| 126 for (auto srgb_converter_texture : srgb_converter_textures_) { |
| 127 glBindTexture(GL_TEXTURE_2D, srgb_converter_texture); |
| 128 |
| 129 // Use linear, non-mipmapped sampling with the srgb converter texture |
| 130 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| 131 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| 132 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| 133 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| 134 } |
| 135 |
| 136 glGenFramebuffersEXT(1, &srgb_decoder_fbo_); |
| 137 glGenFramebuffersEXT(1, &srgb_encoder_fbo_); |
| 138 |
| 139 glGenVertexArraysOES(1, &srgb_converter_vao_); |
| 140 |
| 141 decoder->RestoreTextureUnitBindings(0); |
| 142 decoder->RestoreActiveTexture(); |
| 143 decoder->RestoreProgramBindings(); |
| 144 |
| 145 srgb_converter_initialized_ = true; |
| 146 } |
| 147 |
| 148 void SRGBConverter::Destroy() { |
| 149 if (srgb_converter_initialized_) { |
| 150 glDeleteTextures(srgb_converter_textures_.size(), |
| 151 srgb_converter_textures_.data()); |
| 152 srgb_converter_textures_.fill(0); |
| 153 |
| 154 glDeleteFramebuffersEXT(1, &srgb_decoder_fbo_); |
| 155 srgb_decoder_fbo_ = 0; |
| 156 glDeleteFramebuffersEXT(1, &srgb_encoder_fbo_); |
| 157 srgb_encoder_fbo_ = 0; |
| 158 |
| 159 glDeleteVertexArraysOES(1, &srgb_converter_vao_); |
| 160 srgb_converter_vao_ = 0; |
| 161 |
| 162 glDeleteProgram(srgb_converter_program_); |
| 163 srgb_converter_program_ = 0; |
| 164 |
| 165 srgb_converter_initialized_ = false; |
| 166 } |
| 167 } |
| 168 |
| 169 void SRGBConverter::Blit( |
| 170 const gles2::GLES2Decoder* decoder, |
| 171 GLint srcX0, |
| 172 GLint srcY0, |
| 173 GLint srcX1, |
| 174 GLint srcY1, |
| 175 GLint dstX0, |
| 176 GLint dstY0, |
| 177 GLint dstX1, |
| 178 GLint dstY1, |
| 179 GLbitfield mask, |
| 180 GLenum filter, |
| 181 const gfx::Size& framebuffer_size, |
| 182 GLuint src_framebuffer, |
| 183 GLenum src_framebuffer_internal_format, |
| 184 GLenum src_framebuffer_format, |
| 185 GLenum src_framebuffer_type, |
| 186 GLuint dst_framebuffer, |
| 187 bool decode, |
| 188 bool encode) { |
| 189 // This function blits srgb image in src fb to srgb image in dst fb. |
| 190 // The steps are: |
| 191 // 1) Copy and crop pixels from source srgb image to the 1st texture(srgb). |
| 192 // 2) Sampling from the 1st texture and drawing to the 2nd texture(linear). |
| 193 // During this step, color space is converted from srgb to linear. |
| 194 // 3) Blit pixels from the 2nd texture to the 3rd texture(linear). |
| 195 // 4) Sampling from the 3rd texture and drawing to the dst image(srgb). |
| 196 // During this step, color space is converted from linear to srgb. |
| 197 // If we need to blit from linear to srgb or vice versa, some steps will be |
| 198 // skipped. |
| 199 DCHECK(srgb_converter_initialized_); |
| 200 |
| 201 // Set the states |
| 202 glActiveTexture(GL_TEXTURE0); |
| 203 glDisable(GL_SCISSOR_TEST); |
| 204 glDisable(GL_DEPTH_TEST); |
| 205 glDisable(GL_STENCIL_TEST); |
| 206 glDisable(GL_CULL_FACE); |
| 207 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); |
| 208 glDepthMask(GL_FALSE); |
| 209 glDisable(GL_BLEND); |
| 210 glDisable(GL_DITHER); |
| 211 |
| 212 // Copy the image from read buffer to the 1st texture(srgb). |
| 213 // TODO(yunchao) If the read buffer is a fbo texture, we can sample |
| 214 // directly from that texture. In this way, we can save gpu memory. |
| 215 GLuint width_read = 0, height_read = 0, xoffset = 0, yoffset = 0; |
| 216 if (decode) { |
| 217 glBindFramebufferEXT(GL_FRAMEBUFFER, src_framebuffer); |
| 218 glBindTexture(GL_TEXTURE_2D, srgb_converter_textures_[0]); |
| 219 |
| 220 // We should not copy pixels outside of the read framebuffer. If we read |
| 221 // these pixels, they would become in-bound during BlitFramebuffer. However, |
| 222 // Out-of-bounds pixels will be initialized to 0 in CopyTexSubImage. |
| 223 // But they should read as if the GL_CLAMP_TO_EDGE texture mapping mode |
| 224 // were applied during BlitFramebuffer when the filter is GL_LINEAR. |
| 225 GLuint x = srcX1 > srcX0 ? srcX0 : srcX1; |
| 226 GLuint y = srcY1 > srcY0 ? srcY0 : srcY1; |
| 227 width_read = srcX1 > srcX0 ? srcX1 - srcX0 : srcX0 - srcX1; |
| 228 height_read = srcY1 > srcY0 ? srcY1 - srcY0 : srcY0 - srcY1; |
| 229 gfx::Rect c(0, 0, framebuffer_size.width(), framebuffer_size.height()); |
| 230 c.Intersect(gfx::Rect(x, y, width_read, height_read)); |
| 231 xoffset = c.x() - x; |
| 232 yoffset = c.y() - y; |
| 233 glCopyTexImage2D(GL_TEXTURE_2D, 0, src_framebuffer_internal_format, |
| 234 c.x(), c.y(), c.width(), c.height(), 0); |
| 235 |
| 236 // Make a temporary linear texture as the 2nd texture, where we |
| 237 // render the converted (srgb to linear) result to. |
| 238 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); |
| 239 |
| 240 glBindTexture(GL_TEXTURE_2D, srgb_converter_textures_[1]); |
| 241 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, |
| 242 c.width(), c.height(), |
| 243 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| 244 glBindFramebufferEXT(GL_FRAMEBUFFER, srgb_decoder_fbo_); |
| 245 glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, |
| 246 GL_TEXTURE_2D, srgb_converter_textures_[1], 0); |
| 247 |
| 248 // Sampling from the 1st texture(srgb) and drawing to the |
| 249 // 2nd texture(linear), |
| 250 glUseProgram(srgb_converter_program_); |
| 251 glViewport(0, 0, width_read, height_read); |
| 252 |
| 253 glBindTexture(GL_TEXTURE_2D, srgb_converter_textures_[0]); |
| 254 glBindVertexArrayOES(srgb_converter_vao_); |
| 255 |
| 256 glDrawArrays(GL_TRIANGLES, 0, 6); |
| 257 } else { |
| 258 // Set approriate read framebuffer if decoding is skipped. |
| 259 glBindFramebufferEXT(GL_READ_FRAMEBUFFER, src_framebuffer); |
| 260 } |
| 261 |
| 262 // Create the 3rd texture(linear) as encoder_fbo's draw buffer. But we can |
| 263 // reuse the 1st texture and re-allocate the image. Then Blit framebuffer |
| 264 // from the 2nd texture(linear) to the 3rd texture. Filtering is done |
| 265 // during bliting. Note that the src and dst coordinates may be reversed. |
| 266 GLuint width_draw = 0, height_draw = 0; |
| 267 if (encode) { |
| 268 glBindTexture(GL_TEXTURE_2D, srgb_converter_textures_[0]); |
| 269 |
| 270 width_draw = dstX1 > dstX0 ? dstX1 - dstX0 : dstX0 - dstX1; |
| 271 height_draw = dstY1 > dstY0 ? dstY1 - dstY0 : dstY0 - dstY1; |
| 272 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); |
| 273 glTexImage2D( |
| 274 GL_TEXTURE_2D, 0, decode ? GL_RGBA : src_framebuffer_internal_format, |
| 275 width_draw, height_draw, 0, |
| 276 decode ? GL_RGBA : src_framebuffer_format, |
| 277 decode ? GL_UNSIGNED_BYTE : src_framebuffer_type, |
| 278 nullptr); |
| 279 |
| 280 glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, srgb_encoder_fbo_); |
| 281 glFramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, |
| 282 GL_TEXTURE_2D, srgb_converter_textures_[0], 0); |
| 283 } else { |
| 284 // Set approriate draw framebuffer if encoding is skipped. |
| 285 glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, dst_framebuffer); |
| 286 } |
| 287 |
| 288 glBlitFramebuffer( |
| 289 decode ? (srcX0 < srcX1 ? 0 - xoffset : width_read - xoffset) : srcX0, |
| 290 decode ? (srcY0 < srcY1 ? 0 - yoffset : height_read - yoffset) : srcY0, |
| 291 decode ? (srcX0 < srcX1 ? width_read - xoffset : 0 - xoffset) : srcX1, |
| 292 decode ? (srcY0 < srcY1 ? height_read - yoffset : 0 - yoffset) : srcY1, |
| 293 encode ? (dstX0 < dstX1 ? 0 : width_draw) : dstX0, |
| 294 encode ? (dstY0 < dstY1 ? 0 : height_draw) : dstY0, |
| 295 encode ? (dstX0 < dstX1 ? width_draw : 0) : dstX1, |
| 296 encode ? (dstY0 < dstY1 ? height_draw : 0) : dstY1, |
| 297 mask, filter); |
| 298 |
| 299 // Sampling from the 3rd texture(linear) and drawing to the target srgb image. |
| 300 // During this step, color space is converted from linear to srgb. We should |
| 301 // set appropriate viewport to draw to the correct location in target FB. |
| 302 if (encode) { |
| 303 GLuint xstart = dstX0 < dstX1 ? dstX0 : dstX1; |
| 304 GLuint ystart = dstY0 < dstY1 ? dstY0 : dstY1; |
| 305 glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, dst_framebuffer); |
| 306 glUseProgram(srgb_converter_program_); |
| 307 glViewport(xstart, ystart, width_draw, height_draw); |
| 308 |
| 309 glBindTexture(GL_TEXTURE_2D, srgb_converter_textures_[0]); |
| 310 glBindVertexArrayOES(srgb_converter_vao_); |
| 311 |
| 312 glDrawArrays(GL_TRIANGLES, 0, 6); |
| 313 } |
| 314 |
| 315 // Restore state |
| 316 decoder->RestoreAllAttributes(); |
| 317 decoder->RestoreTextureUnitBindings(0); |
| 318 decoder->RestoreActiveTexture(); |
| 319 decoder->RestoreProgramBindings(); |
| 320 decoder->RestoreBufferBindings(); |
| 321 decoder->RestoreFramebufferBindings(); |
| 322 decoder->RestoreGlobalState(); |
| 323 } |
| 324 |
| 325 } // namespace gles2. |
| 326 } // namespace gpu |
OLD | NEW |