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 |