Chromium Code Reviews| 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" | |
|
Ken Russell (switch to Gerrit)
2016/09/16 22:32:29
Embedding the vertex positions in the vertex shade
piman
2016/09/16 22:53:29
I'm actually ok with this. That's also done in the
Ken Russell (switch to Gerrit)
2016/09/16 23:19:42
OK, fine by me then.
| |
| 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 } | |
| 258 | |
| 259 // Create the 3rd texture(linear) as encoder_fbo's draw buffer. But we can | |
| 260 // reuse the 1st texture and re-allocate the image. Then Blit framebuffer | |
| 261 // from the 2nd texture(linear) to the 3rd texture. Filtering is done | |
| 262 // during bliting. Note that the src and dst coordinates may be reversed. | |
| 263 GLuint width_draw = 0, height_draw = 0; | |
| 264 if (encode) { | |
| 265 glBindTexture(GL_TEXTURE_2D, srgb_converter_textures_[0]); | |
| 266 | |
| 267 width_draw = dstX1 > dstX0 ? dstX1 - dstX0 : dstX0 - dstX1; | |
| 268 height_draw = dstY1 > dstY0 ? dstY1 - dstY0 : dstY0 - dstY1; | |
| 269 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); | |
| 270 glTexImage2D( | |
| 271 GL_TEXTURE_2D, 0, decode ? GL_RGBA : src_framebuffer_internal_format, | |
| 272 width_draw, height_draw, 0, | |
| 273 decode ? GL_RGBA : src_framebuffer_format, | |
| 274 decode ? GL_UNSIGNED_BYTE : src_framebuffer_type, | |
| 275 nullptr); | |
| 276 | |
| 277 glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, srgb_encoder_fbo_); | |
| 278 glFramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, | |
| 279 GL_TEXTURE_2D, srgb_converter_textures_[0], 0); | |
| 280 // Set approriate read/draw framebuffer if decoding or encoding is skipped. | |
| 281 } else { | |
| 282 glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, dst_framebuffer); | |
| 283 } | |
| 284 | |
| 285 if (decode) { | |
| 286 glBindFramebufferEXT(GL_READ_FRAMEBUFFER, srgb_decoder_fbo_); | |
|
piman
2016/09/16 20:49:27
nit: this is already done in the decode path (srgb
yunchao
2016/09/17 15:14:13
Done.
| |
| 287 } else { | |
| 288 glBindFramebufferEXT(GL_READ_FRAMEBUFFER, src_framebuffer); | |
| 289 } | |
| 290 | |
| 291 glBlitFramebuffer( | |
| 292 decode ? (srcX0 < srcX1 ? 0 - xoffset : width_read - xoffset) : srcX0, | |
| 293 decode ? (srcY0 < srcY1 ? 0 - yoffset : height_read - yoffset) : srcY0, | |
| 294 decode ? (srcX0 < srcX1 ? width_read - xoffset : 0 - xoffset) : srcX1, | |
| 295 decode ? (srcY0 < srcY1 ? height_read - yoffset : 0 - yoffset) : srcY1, | |
| 296 encode ? (dstX0 < dstX1 ? 0 : width_draw) : dstX0, | |
| 297 encode ? (dstY0 < dstY1 ? 0 : height_draw) : dstY0, | |
| 298 encode ? (dstX0 < dstX1 ? width_draw : 0) : dstX1, | |
| 299 encode ? (dstY0 < dstY1 ? height_draw : 0) : dstY1, | |
| 300 mask, filter); | |
| 301 | |
| 302 // Sampling from the 3rd texture(linear) and drawing to the target srgb image. | |
| 303 // During this step, color space is converted from linear to srgb. We should | |
| 304 // set appropriate viewport to draw to the correct location in target FB. | |
| 305 if (encode) { | |
| 306 GLuint xstart = dstX0 < dstX1 ? dstX0 : dstX1; | |
| 307 GLuint ystart = dstY0 < dstY1 ? dstY0 : dstY1; | |
| 308 glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, dst_framebuffer); | |
| 309 glUseProgram(srgb_converter_program_); | |
| 310 glViewport(xstart, ystart, width_draw, height_draw); | |
| 311 | |
| 312 glBindTexture(GL_TEXTURE_2D, srgb_converter_textures_[0]); | |
| 313 glBindVertexArrayOES(srgb_converter_vao_); | |
| 314 | |
| 315 glDrawArrays(GL_TRIANGLES, 0, 6); | |
| 316 } | |
| 317 | |
| 318 // Restore state | |
| 319 decoder->RestoreAllAttributes(); | |
| 320 decoder->RestoreTextureUnitBindings(0); | |
| 321 decoder->RestoreActiveTexture(); | |
| 322 decoder->RestoreProgramBindings(); | |
| 323 decoder->RestoreBufferBindings(); | |
| 324 decoder->RestoreFramebufferBindings(); | |
| 325 decoder->RestoreGlobalState(); | |
| 326 } | |
| 327 | |
| 328 } // namespace gles2. | |
| 329 } // namespace gpu | |
| OLD | NEW |