Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(870)

Side by Side Diff: gpu/command_buffer/service/gles2_cmd_srgb_converter.cc

Issue 2286593002: [Command Buffer] emulate SRGB color format for BlitFramebuffer in OpenGL (Closed)
Patch Set: addressed piman@'s feedbacks, and rebased code Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « gpu/command_buffer/service/gles2_cmd_srgb_converter.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 const char* vs_source =
piman 2016/09/08 19:39:18 nit: static, as to not pollute the namespace.
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.
78 // However, sampling texels from a linear texture to a srgb image, it will
79 // not convert linear to srgb automatically. There is no similar words in
80 // <sRGB Texture Color Conversion> or other sections in GLES/OGL spec.
81 // So we don't need to do manual decoding in decoder program, but we need
82 // to do encoder in encoder program.
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 /* "float decode(float color)\n"
90 "{\n"
91 " float decoded_color;\n"
92 " if (color <= 0.04045) {\n"
93 " decoded_color = color / 12.92;\n"
94 " } else {\n"
95 " decoded_color = (color + 0.055) / 1.055;\n"
96 " decoded_color = pow(decoded_color, 2.4);\n"
97 " }\n"
98 " return decoded_color;\n"
99 "}\n"
100 "\n" */
101 "void main()\n"
102 "{\n"
103 " vec4 c = texture(u_source_texture, v_texcoord);\n"
104 //" output_color = vec4(decode(c.r), decode(c.g), decode(c.b), c.a);\n"
105 " output_color = c;\n"
106 "}\n";
107
108 GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
109 CompileShader(fs, fs_source);
110 glAttachShader(srgb_decoder_program_, fs);
111 glDeleteShader(fs);
112
113 glLinkProgram(srgb_decoder_program_);
114 #ifndef NDEBUG
115 GLint linked = 0;
116 glGetProgramiv(srgb_decoder_program_, GL_LINK_STATUS, &linked);
117 if (!linked) {
118 DLOG(ERROR) << "BlitFramebuffer: program link failure.";
119 }
120 #endif
121
122 GLuint texture_uniform =
123 glGetUniformLocation(srgb_decoder_program_, "u_source_texture");
124 glUseProgram(srgb_decoder_program_);
125 glUniform1i(texture_uniform, 0);
126
127 glGenTextures(srgb_decoder_textures_.size(), srgb_decoder_textures_.data());
128 glActiveTexture(GL_TEXTURE0);
129 for (auto srgb_decoder_texture : srgb_decoder_textures_) {
130 glBindTexture(GL_TEXTURE_2D, srgb_decoder_texture);
131
132 // Use linear, non-mipmapped sampling with the srgb decoder texture
133 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
134 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
135 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
136 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
137 }
138
139 glGenFramebuffersEXT(1, &srgb_decoder_fbo_);
140 glGenVertexArraysOES(1, &srgb_decoder_vao_);
141
142 decoder->RestoreTextureUnitBindings(0);
143 decoder->RestoreActiveTexture();
144 decoder->RestoreProgramBindings();
145
146 srgb_decoder_initialized_ = true;
147 }
148
149 void SRGBConverter::InitializeSRGBEncoder(
150 const gles2::GLES2Decoder* decoder) {
151 if (srgb_encoder_initialized_) {
152 return;
153 }
154
155 srgb_encoder_program_ = glCreateProgram();
156
157 // Compile the vertex shader
158 GLuint vs = glCreateShader(GL_VERTEX_SHADER);
159 CompileShader(vs, vs_source);
160 glAttachShader(srgb_encoder_program_, vs);
161 glDeleteShader(vs);
162
163 // Compile the fragment shader
164
165 // Sampling texels from a srgb texture to a linear image, it will convert
166 // the srgb color space to linear color space automatically as a part of
167 // filtering. See the section <sRGB Texture Color Conversion> in GLES and
168 // OpenGL spec.
169 // However, sampling texels from a linear texture to a srgb image, it will
170 // not convert linear to srgb automatically. There is no similar words in
171 // <sRGB Texture Color Conversion> or other sections in GLES/OGL spec.
172 // So we don't need to do manual decoding in decoder program, but we need
173 const char* fs_source =
174 "#version 150\n"
175 "uniform sampler2D u_source_texture;\n"
176 "in vec2 v_texcoord;\n"
177 "out vec4 output_color;\n"
178 "\n"
179 "float encode(float color)\n"
180 "{\n"
181 " float encoded_color;\n"
182 " if (color <= 0.0) {\n"
183 " return 0.0;\n"
184 " } else if (color < 0.0031308) {\n"
185 " return color * 12.92;\n"
186 " } else if (color < 1) {\n"
187 " return pow(color, 0.41666) * 1.055 - 0.055;\n"
188 " } else {\n"
189 " return 1.0;\n"
190 " }\n"
191 "}\n"
192 "\n"
193 "void main()\n"
194 "{\n"
195 " vec4 c = texture(u_source_texture, v_texcoord);\n"
196 " output_color = vec4(encode(c.r), encode(c.g), encode(c.b), c.a);\n"
197 "}\n";
198
199 GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
200 CompileShader(fs, fs_source);
201 glAttachShader(srgb_encoder_program_, fs);
202 glDeleteShader(fs);
203
204 glLinkProgram(srgb_encoder_program_);
205 #ifndef NDEBUG
206 GLint linked = 0;
207 glGetProgramiv(srgb_encoder_program_, GL_LINK_STATUS, &linked);
208 if (!linked) {
209 DLOG(ERROR) << "SRGB Encoder for BlitFramebuffer: program link failure.";
210 }
211 #endif
212
213 GLuint texture_uniform =
214 glGetUniformLocation(srgb_encoder_program_, "u_source_texture");
215 glUseProgram(srgb_encoder_program_);
216 glUniform1i(texture_uniform, 0);
217
218 glGenTextures(srgb_encoder_textures_.size(), srgb_encoder_textures_.data());
219 glActiveTexture(GL_TEXTURE0);
220 for (auto srgb_encoder_texture : srgb_encoder_textures_) {
221 glBindTexture(GL_TEXTURE_2D, srgb_encoder_texture);
222
223 // Use linear, non-mipmapped sampling with the srgb encoder texture
224 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
225 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
226 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
227 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
228 }
229
230 glGenFramebuffersEXT(1, &srgb_encoder_fbo_);
231 glGenVertexArraysOES(1, &srgb_encoder_vao_);
232
233 decoder->RestoreTextureUnitBindings(0);
234 decoder->RestoreActiveTexture();
235 decoder->RestoreProgramBindings();
236
237 srgb_encoder_initialized_ = true;
238 }
239
240 void SRGBConverter::Destroy() {
241 if (srgb_decoder_initialized_) {
242 glDeleteProgram(srgb_decoder_program_);
243 srgb_decoder_program_ = 0;
244
245 glDeleteTextures(srgb_decoder_textures_.size(),
246 srgb_decoder_textures_.data());
247 srgb_decoder_textures_.fill(0);
248
249 glDeleteFramebuffersEXT(1, &srgb_decoder_fbo_);
250 srgb_decoder_fbo_ = 0;
251
252 glDeleteVertexArraysOES(1, &srgb_decoder_vao_);
253 srgb_decoder_vao_ = 0;
254
255 srgb_decoder_initialized_ = false;
256 }
257
258 if (srgb_encoder_initialized_) {
259 glDeleteProgram(srgb_encoder_program_);
260 srgb_encoder_program_ = 0;
261
262 glDeleteTextures(srgb_encoder_textures_.size(),
263 srgb_encoder_textures_.data());
264 srgb_encoder_textures_.fill(0);
265
266 glDeleteFramebuffersEXT(1, &srgb_encoder_fbo_);
267 srgb_encoder_fbo_ = 0;
268
269 glDeleteVertexArraysOES(1, &srgb_encoder_vao_);
270 srgb_encoder_vao_ = 0;
271
272 srgb_encoder_initialized_ = false;
273 }
274 }
275
276 void SRGBConverter::SRGBToLinear(
277 const gles2::GLES2Decoder* decoder,
278 GLint srcX0,
279 GLint srcY0,
280 GLint srcX1,
281 GLint srcY1,
282 GLint dstX0,
283 GLint dstY0,
284 GLint dstX1,
285 GLint dstY1,
286 GLbitfield mask,
287 GLenum filter,
288 const gfx::Size& framebuffer_size,
289 GLuint src_framebuffer,
290 GLenum src_framebuffer_internal_format,
291 GLuint dst_framebuffer) {
292 // This function convert srgb image in src fb to linear image in dst fb,
293 // The steps are:
294 // 1) Copy and crop the pixels from source srgb image to a temp srgb texture.
295 // 2) Sampling from the srgb texture and drawing to a temp linear texture.
296 // Durint this step, color space is converted from srgb to linear.
297 // 3) Finally, blit pixels from the linear texture to the target, which is
298 // also a linear image.
299 DCHECK(srgb_decoder_initialized_);
300
301 // Copy the image from framebuffer to the first srgb decoder texture
302 // TODO(yunchao) If the read buffer is a fbo texture, we can sample
303 // directly from that texture. In this way, we can save gpu memory.
304 glBindFramebufferEXT(GL_FRAMEBUFFER, src_framebuffer);
305 glActiveTexture(GL_TEXTURE0);
306 glBindTexture(GL_TEXTURE_2D, srgb_decoder_textures_[0]);
307
308 // We should not copy pixels outside of the read framebuffer. If we read
309 // these pixels, they would become in-bound during BlitFramebuffer. However,
310 // Out-of-bounds pixels will be initialized to 0 in CopyTexSubImage. But they
311 // should read as if the GL_CLAMP_TO_EDGE texture mapping mode were applied
312 // during BlitFramebuffer when the filter is GL_LINEAR.
313 GLuint x = srcX1 > srcX0 ? srcX0 : srcX1;
314 GLuint y = srcY1 > srcY0 ? srcY0 : srcY1;
315 GLuint width = srcX1 > srcX0 ? srcX1 - srcX0 : srcX0 - srcX1;
316 GLuint height = srcY1 > srcY0 ? srcY1 - srcY0 : srcY0 - srcY1;
317 gfx::Rect c(0, 0, framebuffer_size.width(), framebuffer_size.height());
318 c.Intersect(gfx::Rect(x, y, width, height));
319 GLuint xoffset = c.x() - x;
320 GLuint yoffset = c.y() - y;
321 glCopyTexImage2D(GL_TEXTURE_2D, 0, src_framebuffer_internal_format,
322 c.x(), c.y(), c.width(), c.height(), 0);
323
324 // Make a temporary framebuffer using the second srgb decoder texture to
325 // render the converted (srgb to linear) result to.
326 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
327
328 glBindTexture(GL_TEXTURE_2D, srgb_decoder_textures_[1]);
329 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
330 c.width(), c.height(),
331 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
332 glBindFramebufferEXT(GL_FRAMEBUFFER, srgb_decoder_fbo_);
333 glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
334 GL_TEXTURE_2D, srgb_decoder_textures_[1], 0);
335
336 // Render to the second srgb decoder texture,
337 // sampling from the first srgb decoder texture.
338 glUseProgram(srgb_decoder_program_);
339 glViewport(0, 0, width, height);
340 glDisable(GL_SCISSOR_TEST);
341 glDisable(GL_DEPTH_TEST);
342 glDisable(GL_STENCIL_TEST);
343 glDisable(GL_CULL_FACE);
344 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
345 glDepthMask(GL_FALSE);
346 glDisable(GL_BLEND);
347 glDisable(GL_DITHER);
348
349 glBindTexture(GL_TEXTURE_2D, srgb_decoder_textures_[0]);
350 glBindVertexArrayOES(srgb_decoder_vao_);
351
352 glDrawArrays(GL_TRIANGLES, 0, 6);
353
354 // Finally, bind the temporary framebuffer as read framebuffer,
355 // blit the converted texture in temp fbo to the destination texture
356 // in destination framebuffer.
357 glBindFramebufferEXT(GL_READ_FRAMEBUFFER, srgb_decoder_fbo_);
358 glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, dst_framebuffer);
359 // Note that the source region has been changed in temp framebuffer.
360 // The xoffset/yoffset can make bliting clamp to the correct edge if
361 // CLAMP_TO_EDGE is necessary.
362 glBlitFramebuffer(srcX0 < srcX1 ? 0 - xoffset : width - xoffset,
363 srcY0 < srcY1 ? 0 - yoffset : height - yoffset,
364 srcX0 < srcX1 ? width - xoffset : 0 - xoffset,
365 srcY0 < srcY1 ? height - yoffset : 0 - yoffset,
366 dstX0, dstY0, dstX1, dstY1, mask, filter);
367
368 // Restore state
369 decoder->RestoreAllAttributes();
370 decoder->RestoreTextureUnitBindings(0);
371 decoder->RestoreActiveTexture();
372 decoder->RestoreProgramBindings();
373 decoder->RestoreBufferBindings();
374 decoder->RestoreFramebufferBindings();
375 decoder->RestoreGlobalState();
376 }
377
378 void SRGBConverter::LinearToSRGB(
379 const gles2::GLES2Decoder* decoder,
380 GLint srcX0,
381 GLint srcY0,
382 GLint srcX1,
383 GLint srcY1,
384 GLint dstX0,
385 GLint dstY0,
386 GLint dstX1,
387 GLint dstY1,
388 GLbitfield mask,
389 GLenum filter,
390 GLuint src_framebuffer,
391 GLenum src_framebuffer_internal_format,
392 GLenum src_framebuffer_format,
393 GLenum src_framebuffer_type,
394 GLuint dst_framebuffer) {
395 // This function convert linear image in src fb to srgb image in dst fb.
396 // The steps are:
397 // 1) BlitFramebuffer from source linear image to a temp linear texture.
398 // 2) Sampling from the linear texture and drawing to the target srgb
399 // image. During this step, color space is converted from linear to srgb.
400
401 DCHECK(srgb_encoder_initialized_);
402
403 // Create a temp linear texture as draw buffer. BlitFramebuffer from
404 // source linear image to the temp linear texture. Filtering is done
405 // during bliting. Note that the src and dst coordinates may be reversed.
406 glActiveTexture(GL_TEXTURE0);
407 glBindTexture(GL_TEXTURE_2D, srgb_encoder_textures_[0]);
408
409 GLuint width = dstX1 > dstX0 ? dstX1 - dstX0 : dstX0 - dstX1;
410 GLuint height = dstY1 > dstY0 ? dstY1 - dstY0 : dstY0 - dstY1;
411 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
412 glTexImage2D(GL_TEXTURE_2D, 0, src_framebuffer_internal_format,
413 width, height,
414 0, src_framebuffer_format, src_framebuffer_type, nullptr);
415
416 glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, srgb_encoder_fbo_);
417 glFramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
418 GL_TEXTURE_2D, srgb_encoder_textures_[0], 0);
419 glBindFramebufferEXT(GL_READ_FRAMEBUFFER, src_framebuffer);
420 glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1,
421 dstX0 < dstX1 ? 0 : width,
422 dstY0 < dstY1 ? 0 : height,
423 dstX0 < dstX1 ? width : 0,
424 dstY0 < dstY1 ? height : 0,
425 mask, filter);
426
427 // Sampling from the linear texture and drawing to the target srgb image.
428 // During this step, color space is converted from linear to srgb. We should
429 // set appropriate viewport to draw to the correct location in target FB.
430 GLuint xoffset = dstX0 < dstX1 ? dstX0 : dstX1;
431 GLuint yoffset = dstY0 < dstY1 ? dstY0 : dstY1;
432 glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, dst_framebuffer);
433 glUseProgram(srgb_encoder_program_);
434 glViewport(xoffset, yoffset, width, height);
435 glDisable(GL_SCISSOR_TEST);
436 glDisable(GL_DEPTH_TEST);
437 glDisable(GL_STENCIL_TEST);
438 glDisable(GL_CULL_FACE);
439 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
440 glDepthMask(GL_FALSE);
441 glDisable(GL_BLEND);
442 glDisable(GL_DITHER);
443
444 glBindTexture(GL_TEXTURE_2D, srgb_encoder_textures_[0]);
445 glBindVertexArrayOES(srgb_encoder_vao_);
446
447 glDrawArrays(GL_TRIANGLES, 0, 6);
448
449 // Restore state
450 decoder->RestoreAllAttributes();
451 decoder->RestoreTextureUnitBindings(0);
452 decoder->RestoreActiveTexture();
453 decoder->RestoreProgramBindings();
454 decoder->RestoreBufferBindings();
455 decoder->RestoreFramebufferBindings();
456 decoder->RestoreGlobalState();
457 }
458
459 } // namespace gles2.
460 } // namespace gpu
OLDNEW
« no previous file with comments | « gpu/command_buffer/service/gles2_cmd_srgb_converter.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698