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