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 void SRGBConverter::InitializeSRGBDecoder( | |
36 const gles2::GLES2Decoder* decoder) { | |
37 if (srgb_decoder_initialized_) { | |
38 return; | |
39 } | |
40 | |
41 srgb_decoder_program_ = glCreateProgram(); | |
42 | |
43 // Compile the vertex shader | |
44 const char* vs_source = | |
45 "#version 150\n" | |
46 "out vec2 v_texcoord;\n" | |
47 "\n" | |
48 "void main()\n" | |
49 "{\n" | |
50 " const vec2 quad_positions[6] = vec2[6]\n" | |
51 " (\n" | |
52 " vec2(0.0f, 0.0f),\n" | |
53 " vec2(0.0f, 1.0f),\n" | |
54 " vec2(1.0f, 0.0f),\n" | |
55 "\n" | |
56 " vec2(0.0f, 1.0f),\n" | |
57 " vec2(1.0f, 0.0f),\n" | |
58 " vec2(1.0f, 1.0f)\n" | |
59 " );\n" | |
60 "\n" | |
61 " vec2 xy = vec2((quad_positions[gl_VertexID] * 2.0) - 1.0);\n" | |
62 " gl_Position = vec4(xy, 0.0, 1.0);\n" | |
63 " v_texcoord = quad_positions[gl_VertexID];\n" | |
64 "}\n"; | |
65 | |
66 GLuint vs = glCreateShader(GL_VERTEX_SHADER); | |
67 CompileShader(vs, vs_source); | |
68 glAttachShader(srgb_decoder_program_, vs); | |
69 glDeleteShader(vs); | |
70 | |
71 // Compile the fragment shader | |
72 // Sampling from a srgb texture and drawing to a linear color buffer, it will | |
73 // convert the srgb color space to linear color space automatically. | |
74 // However, sampling from a linear texture and drawing to a srgb color buffer, | |
75 // it will not convert linear to srgb automatically. | |
76 const char* fs_source = | |
77 "#version 150\n" | |
78 "uniform sampler2D u_source_texture;\n" | |
79 "in vec2 v_texcoord;\n" | |
80 "out vec4 output_color;\n" | |
81 "\n" | |
82 /* "float decode(float color)\n" | |
83 "{\n" | |
84 " float decoded_color;\n" | |
85 " if (color <= 0.04045) {\n" | |
86 " decoded_color = color / 12.92;\n" | |
87 " } else {\n" | |
88 " decoded_color = (color + 0.055) / 1.055;\n" | |
89 " decoded_color = pow(decoded_color, 2.4);\n" | |
90 " }\n" | |
91 " return decoded_color;\n" | |
92 "}\n" | |
93 "\n" */ | |
94 "void main()\n" | |
95 "{\n" | |
96 " vec4 c = texture(u_source_texture, v_texcoord);\n" | |
97 //" output_color = vec4(decode(c.r), decode(c.g), decode(c.b), c.a);\n" | |
98 " output_color = c;\n" | |
99 "}\n"; | |
100 | |
101 GLuint fs = glCreateShader(GL_FRAGMENT_SHADER); | |
102 CompileShader(fs, fs_source); | |
103 glAttachShader(srgb_decoder_program_, fs); | |
104 glDeleteShader(fs); | |
105 | |
106 glLinkProgram(srgb_decoder_program_); | |
107 #ifndef NDEBUG | |
108 GLint linked = 0; | |
109 glGetProgramiv(srgb_decoder_program_, GL_LINK_STATUS, &linked); | |
110 if (!linked) { | |
111 DLOG(ERROR) << "BlitFramebuffer: program link failure."; | |
112 } | |
113 #endif | |
114 | |
115 GLuint texture_uniform = | |
116 glGetUniformLocation(srgb_decoder_program_, "u_source_texture"); | |
117 glUseProgram(srgb_decoder_program_); | |
118 glUniform1i(texture_uniform, 0); | |
119 | |
120 glGenTextures(srgb_decoder_textures_.size(), srgb_decoder_textures_.data()); | |
121 glActiveTexture(GL_TEXTURE0); | |
122 for (auto srgb_decoder_texture : srgb_decoder_textures_) { | |
123 glBindTexture(GL_TEXTURE_2D, srgb_decoder_texture); | |
124 | |
125 // Use nearest, non-mipmapped sampling with the srgb decoder texture | |
126 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
127 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |
128 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | |
129 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | |
130 } | |
131 | |
132 glGenFramebuffersEXT(1, &srgb_decoder_fbo_); | |
133 glGenVertexArraysOES(1, &srgb_decoder_vao_); | |
134 | |
135 decoder->RestoreTextureUnitBindings(0); | |
136 decoder->RestoreActiveTexture(); | |
137 decoder->RestoreProgramBindings(); | |
138 | |
139 srgb_decoder_initialized_ = true; | |
140 } | |
141 | |
142 void SRGBConverter::InitializeSRGBEncoder( | |
143 const gles2::GLES2Decoder* decoder) { | |
144 if (srgb_encoder_initialized_) { | |
145 return; | |
146 } | |
147 | |
148 srgb_encoder_program_ = glCreateProgram(); | |
149 | |
150 // Compile the vertex shader | |
151 const char* vs_source = | |
152 "#version 150\n" | |
153 "out vec2 v_texcoord;\n" | |
154 "\n" | |
155 "void main()\n" | |
156 "{\n" | |
157 " const vec2 quad_positions[6] = vec2[6]\n" | |
158 " (\n" | |
159 " vec2(0.0f, 0.0f),\n" | |
160 " vec2(0.0f, 1.0f),\n" | |
161 " vec2(1.0f, 0.0f),\n" | |
162 "\n" | |
163 " vec2(0.0f, 1.0f),\n" | |
164 " vec2(1.0f, 0.0f),\n" | |
165 " vec2(1.0f, 1.0f)\n" | |
166 " );\n" | |
167 "\n" | |
168 " vec2 xy = vec2((quad_positions[gl_VertexID] * 2.0) - 1.0);\n" | |
169 " gl_Position = vec4(xy, 0.0, 1.0);\n" | |
170 " v_texcoord = quad_positions[gl_VertexID];\n" | |
171 "}\n"; | |
172 | |
173 GLuint vs = glCreateShader(GL_VERTEX_SHADER); | |
174 CompileShader(vs, vs_source); | |
175 glAttachShader(srgb_encoder_program_, vs); | |
176 glDeleteShader(vs); | |
177 | |
178 // Compile the fragment shader | |
179 const char* fs_source = | |
180 "#version 150\n" | |
181 "uniform sampler2D u_source_texture;\n" | |
182 "in vec2 v_texcoord;\n" | |
183 "out vec4 output_color;\n" | |
184 "\n" | |
185 "float encode(float color)\n" | |
186 "{\n" | |
187 " float encoded_color;\n" | |
188 " if (color <= 0.0) {\n" | |
189 " return 0.0;\n" | |
190 " } else if (color < 0.0031308) {\n" | |
191 " return color * 12.92;\n" | |
192 " } else if (color < 1) {\n" | |
193 " return pow(color, 0.41666) * 1.055 - 0.055;\n" | |
194 " } else {\n" | |
195 " return 1.0;\n" | |
196 " }\n" | |
197 "}\n" | |
198 "\n" | |
199 "void main()\n" | |
200 "{\n" | |
201 " vec4 c = texture(u_source_texture, v_texcoord);\n" | |
202 " output_color = vec4(encode(c.r), encode(c.g), encode(c.b), c.a);\n" | |
203 "}\n"; | |
204 | |
205 GLuint fs = glCreateShader(GL_FRAGMENT_SHADER); | |
206 CompileShader(fs, fs_source); | |
207 glAttachShader(srgb_encoder_program_, fs); | |
208 glDeleteShader(fs); | |
209 | |
210 glLinkProgram(srgb_encoder_program_); | |
211 #ifndef NDEBUG | |
212 GLint linked = 0; | |
213 glGetProgramiv(srgb_encoder_program_, GL_LINK_STATUS, &linked); | |
214 if (!linked) { | |
215 DLOG(ERROR) << "SRGB Encoder for BlitFramebuffer: program link failure."; | |
216 } | |
217 #endif | |
218 | |
219 GLuint texture_uniform = | |
220 glGetUniformLocation(srgb_encoder_program_, "u_source_texture"); | |
221 glUseProgram(srgb_encoder_program_); | |
222 glUniform1i(texture_uniform, 0); | |
223 | |
224 glGenTextures(srgb_encoder_textures_.size(), srgb_encoder_textures_.data()); | |
225 glActiveTexture(GL_TEXTURE0); | |
226 for (auto srgb_encoder_texture : srgb_encoder_textures_) { | |
227 glBindTexture(GL_TEXTURE_2D, srgb_encoder_texture); | |
228 | |
229 // Use nearest, non-mipmapped sampling with the srgb encoder texture | |
230 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
231 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |
232 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | |
233 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | |
234 } | |
235 | |
236 glGenFramebuffersEXT(1, &srgb_encoder_fbo_); | |
237 glGenVertexArraysOES(1, &srgb_encoder_vao_); | |
238 | |
239 decoder->RestoreTextureUnitBindings(0); | |
240 decoder->RestoreActiveTexture(); | |
241 decoder->RestoreProgramBindings(); | |
242 | |
243 srgb_encoder_initialized_ = true; | |
244 } | |
245 | |
246 void SRGBConverter::Destroy() { | |
247 if (srgb_decoder_initialized_) { | |
248 glDeleteProgram(srgb_decoder_program_); | |
249 srgb_decoder_program_ = 0; | |
250 | |
251 glDeleteTextures(srgb_decoder_textures_.size(), | |
252 srgb_decoder_textures_.data()); | |
253 srgb_decoder_textures_.fill(0); | |
254 | |
255 glDeleteFramebuffersEXT(1, &srgb_decoder_fbo_); | |
256 srgb_decoder_fbo_ = 0; | |
257 | |
258 glDeleteVertexArraysOES(1, &srgb_decoder_vao_); | |
259 srgb_decoder_vao_ = 0; | |
260 | |
261 srgb_decoder_initialized_ = false; | |
262 } | |
263 | |
264 if (srgb_encoder_initialized_) { | |
265 glDeleteProgram(srgb_encoder_program_); | |
266 srgb_encoder_program_ = 0; | |
267 | |
268 glDeleteTextures(srgb_encoder_textures_.size(), | |
269 srgb_encoder_textures_.data()); | |
270 srgb_encoder_textures_.fill(0); | |
271 | |
272 glDeleteFramebuffersEXT(1, &srgb_encoder_fbo_); | |
273 srgb_encoder_fbo_ = 0; | |
274 | |
275 glDeleteVertexArraysOES(1, &srgb_encoder_vao_); | |
276 srgb_encoder_vao_ = 0; | |
277 | |
278 srgb_encoder_initialized_ = false; | |
279 } | |
280 } | |
281 | |
282 void SRGBConverter::SRGBToLinear( | |
283 const gles2::GLES2Decoder* decoder, | |
284 GLint srcX0, | |
285 GLint srcY0, | |
286 GLint srcX1, | |
287 GLint srcY1, | |
288 GLint dstX0, | |
289 GLint dstY0, | |
290 GLint dstX1, | |
291 GLint dstY1, | |
292 GLbitfield mask, | |
293 GLenum filter, | |
294 const gfx::Size& framebuffer_size, | |
295 GLuint src_framebuffer, | |
296 GLenum src_framebuffer_internal_format, | |
297 GLuint dst_framebuffer, | |
298 GLenum dst_framebuffer_internal_format, | |
299 GLenum dst_framebuffer_format, | |
300 GLenum dst_framebuffer_type) { | |
301 | |
302 DCHECK(srgb_decoder_initialized_); | |
303 | |
304 // Copy the image from framebuffer to the first srgb decoder texture | |
305 // TODO(yunchao) If the read buffer is a fbo texture, we can sample | |
306 // directly from that texture. In this way, we can save gpu memory. | |
307 glBindFramebufferEXT(GL_FRAMEBUFFER, src_framebuffer); | |
308 glActiveTexture(GL_TEXTURE0); | |
309 glBindTexture(GL_TEXTURE_2D, srgb_decoder_textures_[0]); | |
310 | |
311 // We should not copy pixels outside of the read framebuffer. If we read | |
312 // these pixels, they would become in-bound during BlitFramebuffer. However, | |
313 // Out-of-bounds pixels will be initialized to 0 in CopyTexSubImage. But they | |
314 // should read as if the GL_CLAMP_TO_EDGE texture mapping mode were applied | |
315 // during BlitFramebuffer when the filter is GL_LINEAR. | |
316 GLuint x = srcX1 > srcX0 ? srcX0 : srcX1; | |
317 GLuint width = srcX1 > srcX0 ? srcX1 - srcX0 : srcX0 - srcX1; | |
318 GLuint y = srcY1 > srcY0 ? srcY0 : srcY1; | |
319 GLuint height = srcY1 > srcY0 ? srcY1 - srcY0 : srcY0 - srcY1; | |
320 gfx::Rect c(0, 0, framebuffer_size.width(), framebuffer_size.height()); | |
321 c.Intersect(gfx::Rect(x, y, width, height)); | |
322 glCopyTexImage2D(GL_TEXTURE_2D, 0, src_framebuffer_internal_format, | |
323 c.x(), c.y(), c.width(), c.height(), 0); | |
324 | |
325 // Make a temporary framebuffer using the second srgb decoder texture to | |
326 // render the converted (linear to srgb) result to. | |
327 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); | |
328 | |
329 glBindTexture(GL_TEXTURE_2D, srgb_decoder_textures_[1]); | |
330 glTexImage2D(GL_TEXTURE_2D, 0, dst_framebuffer_internal_format, | |
331 c.width(), c.height(), | |
332 0, dst_framebuffer_format, dst_framebuffer_type, nullptr); | |
333 glBindFramebufferEXT(GL_FRAMEBUFFER, srgb_decoder_fbo_); | |
334 glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, | |
335 srgb_decoder_textures_[1], 0); | |
336 | |
337 // Render to the second srgb decoder texture, | |
338 // sampling from the first srgb decoder texture. | |
339 glUseProgram(srgb_decoder_program_); | |
340 glViewport(0, 0, width, height); | |
341 glDisable(GL_SCISSOR_TEST); | |
342 glDisable(GL_DEPTH_TEST); | |
343 glDisable(GL_STENCIL_TEST); | |
344 glDisable(GL_CULL_FACE); | |
345 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); | |
346 glDepthMask(GL_FALSE); | |
347 glDisable(GL_BLEND); | |
348 glDisable(GL_DITHER); | |
349 | |
350 glBindTexture(GL_TEXTURE_2D, srgb_decoder_textures_[0]); | |
351 glBindVertexArrayOES(srgb_decoder_vao_); | |
352 | |
353 glDrawArrays(GL_TRIANGLES, 0, 6); | |
354 | |
355 // Finally, bind the temporary framebuffer as read framebuffer, | |
356 // blit the converted texture in temp fbo to the destination texture | |
357 // in destination framebuffer. | |
358 glBindFramebufferEXT(GL_READ_FRAMEBUFFER, srgb_decoder_fbo_); | |
359 glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, dst_framebuffer); | |
360 // Note that the source region has been changed in temp framebuffer | |
361 glBlitFramebuffer(srcX0 < srcX1 ? 0 : width, | |
362 srcY0 < srcY1 ? 0 : height, | |
363 srcX0 < srcX1 ? width : 0, | |
364 srcY0 < srcY1 ? height : 0, | |
365 dstX0, dstY0, dstX1, dstY1, mask, filter); | |
Zhenyao Mo
2016/09/01 19:28:33
I still think this is incorrect.
At glCopyTexImag
yunchao
2016/09/02 14:11:16
Got it. You are correct. This algorithm would make
| |
366 | |
367 // Restore state | |
368 decoder->RestoreAllAttributes(); | |
369 decoder->RestoreTextureUnitBindings(0); | |
370 decoder->RestoreActiveTexture(); | |
371 decoder->RestoreProgramBindings(); | |
372 decoder->RestoreBufferBindings(); | |
373 decoder->RestoreFramebufferBindings(); | |
374 decoder->RestoreGlobalState(); | |
375 } | |
376 | |
377 void SRGBConverter::LinearToSRGB( | |
378 const gles2::GLES2Decoder* decoder, | |
379 GLint srcX0, | |
380 GLint srcY0, | |
381 GLint srcX1, | |
382 GLint srcY1, | |
383 GLint dstX0, | |
384 GLint dstY0, | |
385 GLint dstX1, | |
386 GLint dstY1, | |
387 GLbitfield mask, | |
388 GLenum filter, | |
389 const gfx::Size& framebuffer_size, | |
390 GLuint src_framebuffer, | |
391 GLenum src_framebuffer_internal_format, | |
392 GLuint dst_framebuffer, | |
393 GLenum dst_framebuffer_internal_format, | |
394 GLenum dst_framebuffer_format, | |
395 GLenum dst_framebuffer_type) { | |
396 | |
397 DCHECK(srgb_encoder_initialized_); | |
398 | |
399 // Copy the framebuffer to the first srgb encoder texture | |
400 glBindFramebufferEXT(GL_FRAMEBUFFER, src_framebuffer); | |
401 glActiveTexture(GL_TEXTURE0); | |
402 glBindTexture(GL_TEXTURE_2D, srgb_encoder_textures_[0]); | |
403 | |
404 // We should not copy pixels outside of the read framebuffer. If we read | |
405 // these pixels, they would become in-bound during BlitFramebuffer. However, | |
406 // Out-of-bounds pixels will be initialized to 0 in CopyTexSubImage. But they | |
407 // should read as if the GL_CLAMP_TO_EDGE texture mapping mode were applied | |
408 // during BlitFramebuffer when the filter is GL_LINEAR. | |
409 GLuint x = srcX1 > srcX0 ? srcX0 : srcX1; | |
410 GLuint width = srcX1 > srcX0 ? srcX1 - srcX0 : srcX0 - srcX1; | |
411 GLuint y = srcY1 > srcY0 ? srcY0 : srcY1; | |
412 GLuint height = srcY1 > srcY0 ? srcY1 - srcY0 : srcY0 - srcY1; | |
413 gfx::Rect c(0, 0, framebuffer_size.width(), framebuffer_size.height()); | |
414 c.Intersect(gfx::Rect(x, y, width, height)); | |
415 glCopyTexImage2D(GL_TEXTURE_2D, 0, src_framebuffer_internal_format, | |
416 c.x(), c.y(), c.width(), c.height(), 0); | |
417 | |
418 // Make a temporary framebuffer using the second srgb encoder texture to | |
419 // render the converted (linear to srgb) result to. | |
420 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); | |
421 | |
422 glBindTexture(GL_TEXTURE_2D, srgb_encoder_textures_[1]); | |
423 glTexImage2D(GL_TEXTURE_2D, 0, dst_framebuffer_internal_format, | |
424 c.width(), c.height(), | |
425 0, dst_framebuffer_format, dst_framebuffer_type, nullptr); | |
426 glBindFramebufferEXT(GL_FRAMEBUFFER, srgb_encoder_fbo_); | |
427 glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, | |
428 srgb_encoder_textures_[1], 0); | |
429 | |
430 // Render to the second srgb encoder texture, | |
431 // sampling from the first srgb encoder texture. | |
432 glUseProgram(srgb_encoder_program_); | |
433 glViewport(0, 0, width, height); | |
434 glDisable(GL_SCISSOR_TEST); | |
435 glDisable(GL_DEPTH_TEST); | |
436 glDisable(GL_STENCIL_TEST); | |
437 glDisable(GL_CULL_FACE); | |
438 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); | |
439 glDepthMask(GL_FALSE); | |
440 glDisable(GL_BLEND); | |
441 glDisable(GL_DITHER); | |
442 | |
443 glBindTexture(GL_TEXTURE_2D, srgb_encoder_textures_[0]); | |
444 glBindVertexArrayOES(srgb_encoder_vao_); | |
445 | |
446 glDrawArrays(GL_TRIANGLES, 0, 6); | |
447 | |
448 // Finally, bind the temporary framebuffer as read framebuffer, | |
449 // blit the converted texture in temp fbo to the destination texture | |
450 // in destination framebuffer. | |
451 glBindFramebufferEXT(GL_READ_FRAMEBUFFER, srgb_encoder_fbo_); | |
452 glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, dst_framebuffer); | |
453 // Note that the source region has been changed in temp framebuffer | |
454 glBlitFramebuffer(srcX0 < srcX1 ? 0 : width, | |
455 srcY0 < srcY1 ? 0 : height, | |
456 srcX0 < srcX1 ? width : 0, | |
457 srcY0 < srcY1 ? height : 0, | |
458 dstX0, dstY0, dstX1, dstY1, mask, filter); | |
Zhenyao Mo
2016/09/01 19:28:33
Same situation here.
yunchao
2016/09/02 14:11:16
Done.
| |
459 | |
460 // Restore state | |
461 decoder->RestoreAllAttributes(); | |
462 decoder->RestoreTextureUnitBindings(0); | |
463 decoder->RestoreActiveTexture(); | |
464 decoder->RestoreProgramBindings(); | |
465 decoder->RestoreBufferBindings(); | |
466 decoder->RestoreFramebufferBindings(); | |
467 decoder->RestoreGlobalState(); | |
468 } | |
469 | |
470 } // namespace gles2. | |
471 } // namespace gpu | |
OLD | NEW |