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 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 | |
OLD | NEW |