OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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 "content/common/gpu/client/gl_helper_scaling.h" | |
6 | |
7 #include <stddef.h> | |
8 | |
9 #include <deque> | |
10 #include <string> | |
11 #include <vector> | |
12 | |
13 #include "base/bind.h" | |
14 #include "base/lazy_instance.h" | |
15 #include "base/logging.h" | |
16 #include "base/macros.h" | |
17 #include "base/memory/ref_counted.h" | |
18 #include "base/message_loop/message_loop.h" | |
19 #include "base/time/time.h" | |
20 #include "base/trace_event/trace_event.h" | |
21 #include "gpu/command_buffer/client/gles2_interface.h" | |
22 #include "third_party/skia/include/core/SkRegion.h" | |
23 #include "ui/gfx/geometry/rect.h" | |
24 #include "ui/gfx/geometry/size.h" | |
25 | |
26 using gpu::gles2::GLES2Interface; | |
27 | |
28 namespace content { | |
29 | |
30 GLHelperScaling::GLHelperScaling(GLES2Interface* gl, GLHelper* helper) | |
31 : gl_(gl), helper_(helper), vertex_attributes_buffer_(gl_) { | |
32 InitBuffer(); | |
33 } | |
34 | |
35 GLHelperScaling::~GLHelperScaling() {} | |
36 | |
37 // Used to keep track of a generated shader program. The program | |
38 // is passed in as text through Setup and is used by calling | |
39 // UseProgram() with the right parameters. Note that |gl_| | |
40 // and |helper_| are assumed to live longer than this program. | |
41 class ShaderProgram : public base::RefCounted<ShaderProgram> { | |
42 public: | |
43 ShaderProgram(GLES2Interface* gl, GLHelper* helper) | |
44 : gl_(gl), | |
45 helper_(helper), | |
46 program_(gl_->CreateProgram()), | |
47 position_location_(-1), | |
48 texcoord_location_(-1), | |
49 src_subrect_location_(-1), | |
50 src_pixelsize_location_(-1), | |
51 dst_pixelsize_location_(-1), | |
52 scaling_vector_location_(-1), | |
53 color_weights_location_(-1) {} | |
54 | |
55 // Compile shader program. | |
56 void Setup(const GLchar* vertex_shader_text, | |
57 const GLchar* fragment_shader_text); | |
58 | |
59 // UseProgram must be called with GL_TEXTURE_2D bound to the | |
60 // source texture and GL_ARRAY_BUFFER bound to a vertex | |
61 // attribute buffer. | |
62 void UseProgram(const gfx::Size& src_size, | |
63 const gfx::Rect& src_subrect, | |
64 const gfx::Size& dst_size, | |
65 bool scale_x, | |
66 bool flip_y, | |
67 GLfloat color_weights[4]); | |
68 | |
69 bool Initialized() const { return position_location_ != -1; } | |
70 | |
71 private: | |
72 friend class base::RefCounted<ShaderProgram>; | |
73 ~ShaderProgram() { gl_->DeleteProgram(program_); } | |
74 | |
75 GLES2Interface* gl_; | |
76 GLHelper* helper_; | |
77 | |
78 // A program for copying a source texture into a destination texture. | |
79 GLuint program_; | |
80 | |
81 // The location of the position in the program. | |
82 GLint position_location_; | |
83 // The location of the texture coordinate in the program. | |
84 GLint texcoord_location_; | |
85 // The location of the source texture in the program. | |
86 GLint texture_location_; | |
87 // The location of the texture coordinate of | |
88 // the sub-rectangle in the program. | |
89 GLint src_subrect_location_; | |
90 // Location of size of source image in pixels. | |
91 GLint src_pixelsize_location_; | |
92 // Location of size of destination image in pixels. | |
93 GLint dst_pixelsize_location_; | |
94 // Location of vector for scaling direction. | |
95 GLint scaling_vector_location_; | |
96 // Location of color weights. | |
97 GLint color_weights_location_; | |
98 | |
99 DISALLOW_COPY_AND_ASSIGN(ShaderProgram); | |
100 }; | |
101 | |
102 // Implementation of a single stage in a scaler pipeline. If the pipeline has | |
103 // multiple stages, it calls Scale() on the subscaler, then further scales the | |
104 // output. Caches textures and framebuffers to avoid allocating/deleting | |
105 // them once per frame, which can be expensive on some drivers. | |
106 class ScalerImpl : public GLHelper::ScalerInterface, | |
107 public GLHelperScaling::ShaderInterface { | |
108 public: | |
109 // |gl| and |copy_impl| are expected to live longer than this object. | |
110 // |src_size| is the size of the input texture in pixels. | |
111 // |dst_size| is the size of the output texutre in pixels. | |
112 // |src_subrect| is the portion of the src to copy to the output texture. | |
113 // If |scale_x| is true, we are scaling along the X axis, otherwise Y. | |
114 // If we are scaling in both X and Y, |scale_x| is ignored. | |
115 // If |vertically_flip_texture| is true, output will be upside-down. | |
116 // If |swizzle| is true, RGBA will be transformed into BGRA. | |
117 // |color_weights| are only used together with SHADER_PLANAR to specify | |
118 // how to convert RGB colors into a single value. | |
119 ScalerImpl(GLES2Interface* gl, | |
120 GLHelperScaling* scaler_helper, | |
121 const GLHelperScaling::ScalerStage& scaler_stage, | |
122 ScalerImpl* subscaler, | |
123 const float* color_weights) | |
124 : gl_(gl), | |
125 scaler_helper_(scaler_helper), | |
126 spec_(scaler_stage), | |
127 intermediate_texture_(0), | |
128 dst_framebuffer_(gl), | |
129 subscaler_(subscaler) { | |
130 if (color_weights) { | |
131 color_weights_[0] = color_weights[0]; | |
132 color_weights_[1] = color_weights[1]; | |
133 color_weights_[2] = color_weights[2]; | |
134 color_weights_[3] = color_weights[3]; | |
135 } else { | |
136 color_weights_[0] = 0.0; | |
137 color_weights_[1] = 0.0; | |
138 color_weights_[2] = 0.0; | |
139 color_weights_[3] = 0.0; | |
140 } | |
141 shader_program_ = | |
142 scaler_helper_->GetShaderProgram(spec_.shader, spec_.swizzle); | |
143 | |
144 if (subscaler_) { | |
145 intermediate_texture_ = 0u; | |
146 gl_->GenTextures(1, &intermediate_texture_); | |
147 ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, | |
148 intermediate_texture_); | |
149 gl_->TexImage2D(GL_TEXTURE_2D, | |
150 0, | |
151 GL_RGBA, | |
152 spec_.src_size.width(), | |
153 spec_.src_size.height(), | |
154 0, | |
155 GL_RGBA, | |
156 GL_UNSIGNED_BYTE, | |
157 NULL); | |
158 } | |
159 } | |
160 | |
161 ~ScalerImpl() override { | |
162 if (intermediate_texture_) { | |
163 gl_->DeleteTextures(1, &intermediate_texture_); | |
164 } | |
165 } | |
166 | |
167 // GLHelperShader::ShaderInterface implementation. | |
168 void Execute(GLuint source_texture, | |
169 const std::vector<GLuint>& dest_textures) override { | |
170 if (subscaler_) { | |
171 subscaler_->Scale(source_texture, intermediate_texture_); | |
172 source_texture = intermediate_texture_; | |
173 } | |
174 | |
175 ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder( | |
176 gl_, dst_framebuffer_); | |
177 DCHECK_GT(dest_textures.size(), 0U); | |
178 scoped_ptr<GLenum[]> buffers(new GLenum[dest_textures.size()]); | |
179 for (size_t t = 0; t < dest_textures.size(); t++) { | |
180 ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, dest_textures[t]); | |
181 gl_->FramebufferTexture2D(GL_FRAMEBUFFER, | |
182 GL_COLOR_ATTACHMENT0 + t, | |
183 GL_TEXTURE_2D, | |
184 dest_textures[t], | |
185 0); | |
186 buffers[t] = GL_COLOR_ATTACHMENT0 + t; | |
187 } | |
188 ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, source_texture); | |
189 | |
190 gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |
191 gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
192 gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | |
193 gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | |
194 | |
195 ScopedBufferBinder<GL_ARRAY_BUFFER> buffer_binder( | |
196 gl_, scaler_helper_->vertex_attributes_buffer_); | |
197 shader_program_->UseProgram(spec_.src_size, | |
198 spec_.src_subrect, | |
199 spec_.dst_size, | |
200 spec_.scale_x, | |
201 spec_.vertically_flip_texture, | |
202 color_weights_); | |
203 gl_->Viewport(0, 0, spec_.dst_size.width(), spec_.dst_size.height()); | |
204 | |
205 if (dest_textures.size() > 1) { | |
206 DCHECK_LE(static_cast<int>(dest_textures.size()), | |
207 scaler_helper_->helper_->MaxDrawBuffers()); | |
208 gl_->DrawBuffersEXT(dest_textures.size(), buffers.get()); | |
209 } | |
210 // Conduct texture mapping by drawing a quad composed of two triangles. | |
211 gl_->DrawArrays(GL_TRIANGLE_STRIP, 0, 4); | |
212 if (dest_textures.size() > 1) { | |
213 // Set the draw buffers back to not confuse others. | |
214 gl_->DrawBuffersEXT(1, &buffers[0]); | |
215 } | |
216 } | |
217 | |
218 // GLHelper::ScalerInterface implementation. | |
219 void Scale(GLuint source_texture, GLuint dest_texture) override { | |
220 std::vector<GLuint> tmp(1); | |
221 tmp[0] = dest_texture; | |
222 Execute(source_texture, tmp); | |
223 } | |
224 | |
225 const gfx::Size& SrcSize() override { | |
226 if (subscaler_) { | |
227 return subscaler_->SrcSize(); | |
228 } | |
229 return spec_.src_size; | |
230 } | |
231 const gfx::Rect& SrcSubrect() override { | |
232 if (subscaler_) { | |
233 return subscaler_->SrcSubrect(); | |
234 } | |
235 return spec_.src_subrect; | |
236 } | |
237 const gfx::Size& DstSize() override { return spec_.dst_size; } | |
238 | |
239 private: | |
240 GLES2Interface* gl_; | |
241 GLHelperScaling* scaler_helper_; | |
242 GLHelperScaling::ScalerStage spec_; | |
243 GLfloat color_weights_[4]; | |
244 GLuint intermediate_texture_; | |
245 scoped_refptr<ShaderProgram> shader_program_; | |
246 ScopedFramebuffer dst_framebuffer_; | |
247 scoped_ptr<ScalerImpl> subscaler_; | |
248 }; | |
249 | |
250 GLHelperScaling::ScalerStage::ScalerStage(ShaderType shader_, | |
251 gfx::Size src_size_, | |
252 gfx::Rect src_subrect_, | |
253 gfx::Size dst_size_, | |
254 bool scale_x_, | |
255 bool vertically_flip_texture_, | |
256 bool swizzle_) | |
257 : shader(shader_), | |
258 src_size(src_size_), | |
259 src_subrect(src_subrect_), | |
260 dst_size(dst_size_), | |
261 scale_x(scale_x_), | |
262 vertically_flip_texture(vertically_flip_texture_), | |
263 swizzle(swizzle_) {} | |
264 | |
265 GLHelperScaling::ScalerStage::ScalerStage(const ScalerStage& other) = default; | |
266 | |
267 // The important inputs for this function is |x_ops| and | |
268 // |y_ops|. They represent scaling operations to be done | |
269 // on an imag of size |src_size|. If |quality| is SCALER_QUALITY_BEST, | |
270 // then we will interpret these scale operations literally and we'll | |
271 // create one scaler stage for each ScaleOp. However, if |quality| | |
272 // is SCALER_QUALITY_GOOD, then we can do a whole bunch of optimizations | |
273 // by combining two or more ScaleOps in to a single scaler stage. | |
274 // Normally we process ScaleOps from |y_ops| first and |x_ops| after | |
275 // all |y_ops| are processed, but sometimes we can combine one or more | |
276 // operation from both queues essentially for free. This is the reason | |
277 // why |x_ops| and |y_ops| aren't just one single queue. | |
278 void GLHelperScaling::ConvertScalerOpsToScalerStages( | |
279 GLHelper::ScalerQuality quality, | |
280 gfx::Size src_size, | |
281 gfx::Rect src_subrect, | |
282 const gfx::Size& dst_size, | |
283 bool vertically_flip_texture, | |
284 bool swizzle, | |
285 std::deque<GLHelperScaling::ScaleOp>* x_ops, | |
286 std::deque<GLHelperScaling::ScaleOp>* y_ops, | |
287 std::vector<ScalerStage>* scaler_stages) { | |
288 while (!x_ops->empty() || !y_ops->empty()) { | |
289 gfx::Size intermediate_size = src_subrect.size(); | |
290 std::deque<ScaleOp>* current_queue = NULL; | |
291 | |
292 if (!y_ops->empty()) { | |
293 current_queue = y_ops; | |
294 } else { | |
295 current_queue = x_ops; | |
296 } | |
297 | |
298 ShaderType current_shader = SHADER_BILINEAR; | |
299 switch (current_queue->front().scale_factor) { | |
300 case 0: | |
301 if (quality == GLHelper::SCALER_QUALITY_BEST) { | |
302 current_shader = SHADER_BICUBIC_UPSCALE; | |
303 } | |
304 break; | |
305 case 2: | |
306 if (quality == GLHelper::SCALER_QUALITY_BEST) { | |
307 current_shader = SHADER_BICUBIC_HALF_1D; | |
308 } | |
309 break; | |
310 case 3: | |
311 DCHECK(quality != GLHelper::SCALER_QUALITY_BEST); | |
312 current_shader = SHADER_BILINEAR3; | |
313 break; | |
314 default: | |
315 NOTREACHED(); | |
316 } | |
317 bool scale_x = current_queue->front().scale_x; | |
318 current_queue->front().UpdateSize(&intermediate_size); | |
319 current_queue->pop_front(); | |
320 | |
321 // Optimization: Sometimes we can combine 2-4 scaling operations into | |
322 // one operation. | |
323 if (quality == GLHelper::SCALER_QUALITY_GOOD) { | |
324 if (!current_queue->empty() && current_shader == SHADER_BILINEAR) { | |
325 // Combine two steps in the same dimension. | |
326 current_queue->front().UpdateSize(&intermediate_size); | |
327 current_queue->pop_front(); | |
328 current_shader = SHADER_BILINEAR2; | |
329 if (!current_queue->empty()) { | |
330 // Combine three steps in the same dimension. | |
331 current_queue->front().UpdateSize(&intermediate_size); | |
332 current_queue->pop_front(); | |
333 current_shader = SHADER_BILINEAR4; | |
334 } | |
335 } | |
336 // Check if we can combine some steps in the other dimension as well. | |
337 // Since all shaders currently use GL_LINEAR, we can easily scale up | |
338 // or scale down by exactly 2x at the same time as we do another | |
339 // operation. Currently, the following mergers are supported: | |
340 // * 1 bilinear Y-pass with 1 bilinear X-pass (up or down) | |
341 // * 2 bilinear Y-passes with 2 bilinear X-passes | |
342 // * 1 bilinear Y-pass with N bilinear X-pass | |
343 // * N bilinear Y-passes with 1 bilinear X-pass (down only) | |
344 // Measurements indicate that generalizing this for 3x3 and 4x4 | |
345 // makes it slower on some platforms, such as the Pixel. | |
346 if (!scale_x && x_ops->size() > 0 && x_ops->front().scale_factor <= 2) { | |
347 int x_passes = 0; | |
348 if (current_shader == SHADER_BILINEAR2 && x_ops->size() >= 2) { | |
349 // 2y + 2x passes | |
350 x_passes = 2; | |
351 current_shader = SHADER_BILINEAR2X2; | |
352 } else if (current_shader == SHADER_BILINEAR) { | |
353 // 1y + Nx passes | |
354 scale_x = true; | |
355 switch (x_ops->size()) { | |
356 case 0: | |
357 NOTREACHED(); | |
358 case 1: | |
359 if (x_ops->front().scale_factor == 3) { | |
360 current_shader = SHADER_BILINEAR3; | |
361 } | |
362 x_passes = 1; | |
363 break; | |
364 case 2: | |
365 x_passes = 2; | |
366 current_shader = SHADER_BILINEAR2; | |
367 break; | |
368 default: | |
369 x_passes = 3; | |
370 current_shader = SHADER_BILINEAR4; | |
371 break; | |
372 } | |
373 } else if (x_ops->front().scale_factor == 2) { | |
374 // Ny + 1x-downscale | |
375 x_passes = 1; | |
376 } | |
377 | |
378 for (int i = 0; i < x_passes; i++) { | |
379 x_ops->front().UpdateSize(&intermediate_size); | |
380 x_ops->pop_front(); | |
381 } | |
382 } | |
383 } | |
384 | |
385 scaler_stages->push_back(ScalerStage(current_shader, | |
386 src_size, | |
387 src_subrect, | |
388 intermediate_size, | |
389 scale_x, | |
390 vertically_flip_texture, | |
391 swizzle)); | |
392 src_size = intermediate_size; | |
393 src_subrect = gfx::Rect(intermediate_size); | |
394 vertically_flip_texture = false; | |
395 swizzle = false; | |
396 } | |
397 } | |
398 | |
399 void GLHelperScaling::ComputeScalerStages( | |
400 GLHelper::ScalerQuality quality, | |
401 const gfx::Size& src_size, | |
402 const gfx::Rect& src_subrect, | |
403 const gfx::Size& dst_size, | |
404 bool vertically_flip_texture, | |
405 bool swizzle, | |
406 std::vector<ScalerStage>* scaler_stages) { | |
407 if (quality == GLHelper::SCALER_QUALITY_FAST || | |
408 src_subrect.size() == dst_size) { | |
409 scaler_stages->push_back(ScalerStage(SHADER_BILINEAR, | |
410 src_size, | |
411 src_subrect, | |
412 dst_size, | |
413 false, | |
414 vertically_flip_texture, | |
415 swizzle)); | |
416 return; | |
417 } | |
418 | |
419 std::deque<GLHelperScaling::ScaleOp> x_ops, y_ops; | |
420 GLHelperScaling::ScaleOp::AddOps(src_subrect.width(), | |
421 dst_size.width(), | |
422 true, | |
423 quality == GLHelper::SCALER_QUALITY_GOOD, | |
424 &x_ops); | |
425 GLHelperScaling::ScaleOp::AddOps(src_subrect.height(), | |
426 dst_size.height(), | |
427 false, | |
428 quality == GLHelper::SCALER_QUALITY_GOOD, | |
429 &y_ops); | |
430 | |
431 ConvertScalerOpsToScalerStages(quality, | |
432 src_size, | |
433 src_subrect, | |
434 dst_size, | |
435 vertically_flip_texture, | |
436 swizzle, | |
437 &x_ops, | |
438 &y_ops, | |
439 scaler_stages); | |
440 } | |
441 | |
442 GLHelper::ScalerInterface* GLHelperScaling::CreateScaler( | |
443 GLHelper::ScalerQuality quality, | |
444 gfx::Size src_size, | |
445 gfx::Rect src_subrect, | |
446 const gfx::Size& dst_size, | |
447 bool vertically_flip_texture, | |
448 bool swizzle) { | |
449 std::vector<ScalerStage> scaler_stages; | |
450 ComputeScalerStages(quality, | |
451 src_size, | |
452 src_subrect, | |
453 dst_size, | |
454 vertically_flip_texture, | |
455 swizzle, | |
456 &scaler_stages); | |
457 | |
458 ScalerImpl* ret = NULL; | |
459 for (unsigned int i = 0; i < scaler_stages.size(); i++) { | |
460 ret = new ScalerImpl(gl_, this, scaler_stages[i], ret, NULL); | |
461 } | |
462 return ret; | |
463 } | |
464 | |
465 GLHelper::ScalerInterface* GLHelperScaling::CreatePlanarScaler( | |
466 const gfx::Size& src_size, | |
467 const gfx::Rect& src_subrect, | |
468 const gfx::Size& dst_size, | |
469 bool vertically_flip_texture, | |
470 bool swizzle, | |
471 const float color_weights[4]) { | |
472 ScalerStage stage(SHADER_PLANAR, | |
473 src_size, | |
474 src_subrect, | |
475 dst_size, | |
476 true, | |
477 vertically_flip_texture, | |
478 swizzle); | |
479 return new ScalerImpl(gl_, this, stage, NULL, color_weights); | |
480 } | |
481 | |
482 GLHelperScaling::ShaderInterface* GLHelperScaling::CreateYuvMrtShader( | |
483 const gfx::Size& src_size, | |
484 const gfx::Rect& src_subrect, | |
485 const gfx::Size& dst_size, | |
486 bool vertically_flip_texture, | |
487 bool swizzle, | |
488 ShaderType shader) { | |
489 DCHECK(shader == SHADER_YUV_MRT_PASS1 || shader == SHADER_YUV_MRT_PASS2); | |
490 ScalerStage stage(shader, | |
491 src_size, | |
492 src_subrect, | |
493 dst_size, | |
494 true, | |
495 vertically_flip_texture, | |
496 swizzle); | |
497 return new ScalerImpl(gl_, this, stage, NULL, NULL); | |
498 } | |
499 | |
500 const GLfloat GLHelperScaling::kVertexAttributes[] = { | |
501 -1.0f, -1.0f, 0.0f, 0.0f, // vertex 0 | |
502 1.0f, -1.0f, 1.0f, 0.0f, // vertex 1 | |
503 -1.0f, 1.0f, 0.0f, 1.0f, // vertex 2 | |
504 1.0f, 1.0f, 1.0f, 1.0f, }; // vertex 3 | |
505 | |
506 void GLHelperScaling::InitBuffer() { | |
507 ScopedBufferBinder<GL_ARRAY_BUFFER> buffer_binder(gl_, | |
508 vertex_attributes_buffer_); | |
509 gl_->BufferData(GL_ARRAY_BUFFER, | |
510 sizeof(kVertexAttributes), | |
511 kVertexAttributes, | |
512 GL_STATIC_DRAW); | |
513 } | |
514 | |
515 scoped_refptr<ShaderProgram> GLHelperScaling::GetShaderProgram(ShaderType type, | |
516 bool swizzle) { | |
517 ShaderProgramKeyType key(type, swizzle); | |
518 scoped_refptr<ShaderProgram>& cache_entry(shader_programs_[key]); | |
519 if (!cache_entry.get()) { | |
520 cache_entry = new ShaderProgram(gl_, helper_); | |
521 std::basic_string<GLchar> vertex_program; | |
522 std::basic_string<GLchar> fragment_program; | |
523 std::basic_string<GLchar> vertex_header; | |
524 std::basic_string<GLchar> fragment_directives; | |
525 std::basic_string<GLchar> fragment_header; | |
526 std::basic_string<GLchar> shared_variables; | |
527 | |
528 vertex_header.append( | |
529 "precision highp float;\n" | |
530 "attribute vec2 a_position;\n" | |
531 "attribute vec2 a_texcoord;\n" | |
532 "uniform vec4 src_subrect;\n"); | |
533 | |
534 fragment_header.append( | |
535 "precision mediump float;\n" | |
536 "uniform sampler2D s_texture;\n"); | |
537 | |
538 vertex_program.append( | |
539 " gl_Position = vec4(a_position, 0.0, 1.0);\n" | |
540 " vec2 texcoord = src_subrect.xy + a_texcoord * src_subrect.zw;\n"); | |
541 | |
542 switch (type) { | |
543 case SHADER_BILINEAR: | |
544 shared_variables.append("varying vec2 v_texcoord;\n"); | |
545 vertex_program.append(" v_texcoord = texcoord;\n"); | |
546 fragment_program.append( | |
547 " gl_FragColor = texture2D(s_texture, v_texcoord);\n"); | |
548 break; | |
549 | |
550 case SHADER_BILINEAR2: | |
551 // This is equivialent to two passes of the BILINEAR shader above. | |
552 // It can be used to scale an image down 1.0x-2.0x in either dimension, | |
553 // or exactly 4x. | |
554 shared_variables.append( | |
555 "varying vec4 v_texcoords;\n"); // 2 texcoords packed in one quad | |
556 vertex_header.append( | |
557 "uniform vec2 scaling_vector;\n" | |
558 "uniform vec2 dst_pixelsize;\n"); | |
559 vertex_program.append( | |
560 " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n" | |
561 " step /= 4.0;\n" | |
562 " v_texcoords.xy = texcoord + step;\n" | |
563 " v_texcoords.zw = texcoord - step;\n"); | |
564 | |
565 fragment_program.append( | |
566 " gl_FragColor = (texture2D(s_texture, v_texcoords.xy) +\n" | |
567 " texture2D(s_texture, v_texcoords.zw)) / 2.0;\n"); | |
568 break; | |
569 | |
570 case SHADER_BILINEAR3: | |
571 // This is kind of like doing 1.5 passes of the BILINEAR shader. | |
572 // It can be used to scale an image down 1.5x-3.0x, or exactly 6x. | |
573 shared_variables.append( | |
574 "varying vec4 v_texcoords1;\n" // 2 texcoords packed in one quad | |
575 "varying vec2 v_texcoords2;\n"); | |
576 vertex_header.append( | |
577 "uniform vec2 scaling_vector;\n" | |
578 "uniform vec2 dst_pixelsize;\n"); | |
579 vertex_program.append( | |
580 " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n" | |
581 " step /= 3.0;\n" | |
582 " v_texcoords1.xy = texcoord + step;\n" | |
583 " v_texcoords1.zw = texcoord;\n" | |
584 " v_texcoords2 = texcoord - step;\n"); | |
585 fragment_program.append( | |
586 " gl_FragColor = (texture2D(s_texture, v_texcoords1.xy) +\n" | |
587 " texture2D(s_texture, v_texcoords1.zw) +\n" | |
588 " texture2D(s_texture, v_texcoords2)) / 3.0;\n"); | |
589 break; | |
590 | |
591 case SHADER_BILINEAR4: | |
592 // This is equivialent to three passes of the BILINEAR shader above, | |
593 // It can be used to scale an image down 2.0x-4.0x or exactly 8x. | |
594 shared_variables.append("varying vec4 v_texcoords[2];\n"); | |
595 vertex_header.append( | |
596 "uniform vec2 scaling_vector;\n" | |
597 "uniform vec2 dst_pixelsize;\n"); | |
598 vertex_program.append( | |
599 " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n" | |
600 " step /= 8.0;\n" | |
601 " v_texcoords[0].xy = texcoord - step * 3.0;\n" | |
602 " v_texcoords[0].zw = texcoord - step;\n" | |
603 " v_texcoords[1].xy = texcoord + step;\n" | |
604 " v_texcoords[1].zw = texcoord + step * 3.0;\n"); | |
605 fragment_program.append( | |
606 " gl_FragColor = (\n" | |
607 " texture2D(s_texture, v_texcoords[0].xy) +\n" | |
608 " texture2D(s_texture, v_texcoords[0].zw) +\n" | |
609 " texture2D(s_texture, v_texcoords[1].xy) +\n" | |
610 " texture2D(s_texture, v_texcoords[1].zw)) / 4.0;\n"); | |
611 break; | |
612 | |
613 case SHADER_BILINEAR2X2: | |
614 // This is equivialent to four passes of the BILINEAR shader above. | |
615 // Two in each dimension. It can be used to scale an image down | |
616 // 1.0x-2.0x in both X and Y directions. Or, it could be used to | |
617 // scale an image down by exactly 4x in both dimensions. | |
618 shared_variables.append("varying vec4 v_texcoords[2];\n"); | |
619 vertex_header.append("uniform vec2 dst_pixelsize;\n"); | |
620 vertex_program.append( | |
621 " vec2 step = src_subrect.zw / 4.0 / dst_pixelsize;\n" | |
622 " v_texcoords[0].xy = texcoord + vec2(step.x, step.y);\n" | |
623 " v_texcoords[0].zw = texcoord + vec2(step.x, -step.y);\n" | |
624 " v_texcoords[1].xy = texcoord + vec2(-step.x, step.y);\n" | |
625 " v_texcoords[1].zw = texcoord + vec2(-step.x, -step.y);\n"); | |
626 fragment_program.append( | |
627 " gl_FragColor = (\n" | |
628 " texture2D(s_texture, v_texcoords[0].xy) +\n" | |
629 " texture2D(s_texture, v_texcoords[0].zw) +\n" | |
630 " texture2D(s_texture, v_texcoords[1].xy) +\n" | |
631 " texture2D(s_texture, v_texcoords[1].zw)) / 4.0;\n"); | |
632 break; | |
633 | |
634 case SHADER_BICUBIC_HALF_1D: | |
635 // This scales down texture by exactly half in one dimension. | |
636 // directions in one pass. We use bilinear lookup to reduce | |
637 // the number of texture reads from 8 to 4 | |
638 shared_variables.append( | |
639 "const float CenterDist = 99.0 / 140.0;\n" | |
640 "const float LobeDist = 11.0 / 4.0;\n" | |
641 "const float CenterWeight = 35.0 / 64.0;\n" | |
642 "const float LobeWeight = -3.0 / 64.0;\n" | |
643 "varying vec4 v_texcoords[2];\n"); | |
644 vertex_header.append( | |
645 "uniform vec2 scaling_vector;\n" | |
646 "uniform vec2 src_pixelsize;\n"); | |
647 vertex_program.append( | |
648 " vec2 step = src_subrect.zw * scaling_vector / src_pixelsize;\n" | |
649 " v_texcoords[0].xy = texcoord - LobeDist * step;\n" | |
650 " v_texcoords[0].zw = texcoord - CenterDist * step;\n" | |
651 " v_texcoords[1].xy = texcoord + CenterDist * step;\n" | |
652 " v_texcoords[1].zw = texcoord + LobeDist * step;\n"); | |
653 fragment_program.append( | |
654 " gl_FragColor = \n" | |
655 // Lobe pixels | |
656 " (texture2D(s_texture, v_texcoords[0].xy) +\n" | |
657 " texture2D(s_texture, v_texcoords[1].zw)) *\n" | |
658 " LobeWeight +\n" | |
659 // Center pixels | |
660 " (texture2D(s_texture, v_texcoords[0].zw) +\n" | |
661 " texture2D(s_texture, v_texcoords[1].xy)) *\n" | |
662 " CenterWeight;\n"); | |
663 break; | |
664 | |
665 case SHADER_BICUBIC_UPSCALE: | |
666 // When scaling up, we need 4 texture reads, but we can | |
667 // save some instructions because will know in which range of | |
668 // the bicubic function each call call to the bicubic function | |
669 // will be in. | |
670 // Also, when sampling the bicubic function like this, the sum | |
671 // is always exactly one, so we can skip normalization as well. | |
672 shared_variables.append("varying vec2 v_texcoord;\n"); | |
673 vertex_program.append(" v_texcoord = texcoord;\n"); | |
674 fragment_header.append( | |
675 "uniform vec2 src_pixelsize;\n" | |
676 "uniform vec2 scaling_vector;\n" | |
677 "const float a = -0.5;\n" | |
678 // This function is equivialent to calling the bicubic | |
679 // function with x-1, x, 1-x and 2-x | |
680 // (assuming 0 <= x < 1) | |
681 "vec4 filt4(float x) {\n" | |
682 " return vec4(x * x * x, x * x, x, 1) *\n" | |
683 " mat4( a, -2.0 * a, a, 0.0,\n" | |
684 " a + 2.0, -a - 3.0, 0.0, 1.0,\n" | |
685 " -a - 2.0, 3.0 + 2.0 * a, -a, 0.0,\n" | |
686 " -a, a, 0.0, 0.0);\n" | |
687 "}\n" | |
688 "mat4 pixels_x(vec2 pos, vec2 step) {\n" | |
689 " return mat4(\n" | |
690 " texture2D(s_texture, pos - step),\n" | |
691 " texture2D(s_texture, pos),\n" | |
692 " texture2D(s_texture, pos + step),\n" | |
693 " texture2D(s_texture, pos + step * 2.0));\n" | |
694 "}\n"); | |
695 fragment_program.append( | |
696 " vec2 pixel_pos = v_texcoord * src_pixelsize - \n" | |
697 " scaling_vector / 2.0;\n" | |
698 " float frac = fract(dot(pixel_pos, scaling_vector));\n" | |
699 " vec2 base = (floor(pixel_pos) + vec2(0.5)) / src_pixelsize;\n" | |
700 " vec2 step = scaling_vector / src_pixelsize;\n" | |
701 " gl_FragColor = pixels_x(base, step) * filt4(frac);\n"); | |
702 break; | |
703 | |
704 case SHADER_PLANAR: | |
705 // Converts four RGBA pixels into one pixel. Each RGBA | |
706 // pixel will be dot-multiplied with the color weights and | |
707 // then placed into a component of the output. This is used to | |
708 // convert RGBA textures into Y, U and V textures. We do this | |
709 // because single-component textures are not renderable on all | |
710 // architectures. | |
711 shared_variables.append("varying vec4 v_texcoords[2];\n"); | |
712 vertex_header.append( | |
713 "uniform vec2 scaling_vector;\n" | |
714 "uniform vec2 dst_pixelsize;\n"); | |
715 vertex_program.append( | |
716 " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n" | |
717 " step /= 4.0;\n" | |
718 " v_texcoords[0].xy = texcoord - step * 1.5;\n" | |
719 " v_texcoords[0].zw = texcoord - step * 0.5;\n" | |
720 " v_texcoords[1].xy = texcoord + step * 0.5;\n" | |
721 " v_texcoords[1].zw = texcoord + step * 1.5;\n"); | |
722 fragment_header.append("uniform vec4 color_weights;\n"); | |
723 fragment_program.append( | |
724 " gl_FragColor = color_weights * mat4(\n" | |
725 " vec4(texture2D(s_texture, v_texcoords[0].xy).rgb, 1.0),\n" | |
726 " vec4(texture2D(s_texture, v_texcoords[0].zw).rgb, 1.0),\n" | |
727 " vec4(texture2D(s_texture, v_texcoords[1].xy).rgb, 1.0),\n" | |
728 " vec4(texture2D(s_texture, v_texcoords[1].zw).rgb, 1.0));\n"); | |
729 break; | |
730 | |
731 case SHADER_YUV_MRT_PASS1: | |
732 // RGB24 to YV12 in two passes; writing two 8888 targets each pass. | |
733 // | |
734 // YV12 is full-resolution luma and half-resolution blue/red chroma. | |
735 // | |
736 // (original) | |
737 // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX | |
738 // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX | |
739 // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX | |
740 // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX | |
741 // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX | |
742 // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX | |
743 // | | |
744 // | (y plane) (temporary) | |
745 // | YYYY YYYY UUVV UUVV | |
746 // +--> { YYYY YYYY + UUVV UUVV } | |
747 // YYYY YYYY UUVV UUVV | |
748 // First YYYY YYYY UUVV UUVV | |
749 // pass YYYY YYYY UUVV UUVV | |
750 // YYYY YYYY UUVV UUVV | |
751 // | | |
752 // | (u plane) (v plane) | |
753 // Second | UUUU VVVV | |
754 // pass +--> { UUUU + VVVV } | |
755 // UUUU VVVV | |
756 // | |
757 shared_variables.append("varying vec4 v_texcoords[2];\n"); | |
758 vertex_header.append( | |
759 "uniform vec2 scaling_vector;\n" | |
760 "uniform vec2 dst_pixelsize;\n"); | |
761 vertex_program.append( | |
762 " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n" | |
763 " step /= 4.0;\n" | |
764 " v_texcoords[0].xy = texcoord - step * 1.5;\n" | |
765 " v_texcoords[0].zw = texcoord - step * 0.5;\n" | |
766 " v_texcoords[1].xy = texcoord + step * 0.5;\n" | |
767 " v_texcoords[1].zw = texcoord + step * 1.5;\n"); | |
768 fragment_directives.append("#extension GL_EXT_draw_buffers : enable\n"); | |
769 fragment_header.append( | |
770 "const vec3 kRGBtoY = vec3(0.257, 0.504, 0.098);\n" | |
771 "const float kYBias = 0.0625;\n" | |
772 // Divide U and V by two to compensate for averaging below. | |
773 "const vec3 kRGBtoU = vec3(-0.148, -0.291, 0.439) / 2.0;\n" | |
774 "const vec3 kRGBtoV = vec3(0.439, -0.368, -0.071) / 2.0;\n" | |
775 "const float kUVBias = 0.5;\n"); | |
776 fragment_program.append( | |
777 " vec3 pixel1 = texture2D(s_texture, v_texcoords[0].xy).rgb;\n" | |
778 " vec3 pixel2 = texture2D(s_texture, v_texcoords[0].zw).rgb;\n" | |
779 " vec3 pixel3 = texture2D(s_texture, v_texcoords[1].xy).rgb;\n" | |
780 " vec3 pixel4 = texture2D(s_texture, v_texcoords[1].zw).rgb;\n" | |
781 " vec3 pixel12 = pixel1 + pixel2;\n" | |
782 " vec3 pixel34 = pixel3 + pixel4;\n" | |
783 " gl_FragData[0] = vec4(dot(pixel1, kRGBtoY),\n" | |
784 " dot(pixel2, kRGBtoY),\n" | |
785 " dot(pixel3, kRGBtoY),\n" | |
786 " dot(pixel4, kRGBtoY)) + kYBias;\n" | |
787 " gl_FragData[1] = vec4(dot(pixel12, kRGBtoU),\n" | |
788 " dot(pixel34, kRGBtoU),\n" | |
789 " dot(pixel12, kRGBtoV),\n" | |
790 " dot(pixel34, kRGBtoV)) + kUVBias;\n"); | |
791 break; | |
792 | |
793 case SHADER_YUV_MRT_PASS2: | |
794 // We're just sampling two pixels and unswizzling them. There's | |
795 // no need to do vertical scaling with math, since bilinear | |
796 // interpolation in the sampler takes care of that. | |
797 shared_variables.append("varying vec4 v_texcoords;\n"); | |
798 vertex_header.append( | |
799 "uniform vec2 scaling_vector;\n" | |
800 "uniform vec2 dst_pixelsize;\n"); | |
801 vertex_program.append( | |
802 " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n" | |
803 " step /= 2.0;\n" | |
804 " v_texcoords.xy = texcoord - step * 0.5;\n" | |
805 " v_texcoords.zw = texcoord + step * 0.5;\n"); | |
806 fragment_directives.append("#extension GL_EXT_draw_buffers : enable\n"); | |
807 fragment_program.append( | |
808 " vec4 lo_uuvv = texture2D(s_texture, v_texcoords.xy);\n" | |
809 " vec4 hi_uuvv = texture2D(s_texture, v_texcoords.zw);\n" | |
810 " gl_FragData[0] = vec4(lo_uuvv.rg, hi_uuvv.rg);\n" | |
811 " gl_FragData[1] = vec4(lo_uuvv.ba, hi_uuvv.ba);\n"); | |
812 break; | |
813 } | |
814 if (swizzle) { | |
815 switch(type) { | |
816 case SHADER_YUV_MRT_PASS1: | |
817 fragment_program.append(" gl_FragData[0] = gl_FragData[0].bgra;\n"); | |
818 break; | |
819 case SHADER_YUV_MRT_PASS2: | |
820 fragment_program.append(" gl_FragData[0] = gl_FragData[0].bgra;\n"); | |
821 fragment_program.append(" gl_FragData[1] = gl_FragData[1].bgra;\n"); | |
822 break; | |
823 default: | |
824 fragment_program.append(" gl_FragColor = gl_FragColor.bgra;\n"); | |
825 break; | |
826 } | |
827 } | |
828 | |
829 vertex_program = vertex_header + shared_variables + "void main() {\n" + | |
830 vertex_program + "}\n"; | |
831 | |
832 fragment_program = fragment_directives + fragment_header + | |
833 shared_variables + "void main() {\n" + fragment_program + | |
834 "}\n"; | |
835 | |
836 cache_entry->Setup(vertex_program.c_str(), fragment_program.c_str()); | |
837 } | |
838 return cache_entry; | |
839 } | |
840 | |
841 void ShaderProgram::Setup(const GLchar* vertex_shader_text, | |
842 const GLchar* fragment_shader_text) { | |
843 // Shaders to map the source texture to |dst_texture_|. | |
844 GLuint vertex_shader = | |
845 helper_->CompileShaderFromSource(vertex_shader_text, GL_VERTEX_SHADER); | |
846 if (vertex_shader == 0) | |
847 return; | |
848 | |
849 gl_->AttachShader(program_, vertex_shader); | |
850 gl_->DeleteShader(vertex_shader); | |
851 | |
852 GLuint fragment_shader = helper_->CompileShaderFromSource( | |
853 fragment_shader_text, GL_FRAGMENT_SHADER); | |
854 if (fragment_shader == 0) | |
855 return; | |
856 gl_->AttachShader(program_, fragment_shader); | |
857 gl_->DeleteShader(fragment_shader); | |
858 | |
859 gl_->LinkProgram(program_); | |
860 | |
861 GLint link_status = 0; | |
862 gl_->GetProgramiv(program_, GL_LINK_STATUS, &link_status); | |
863 if (!link_status) | |
864 return; | |
865 | |
866 position_location_ = gl_->GetAttribLocation(program_, "a_position"); | |
867 texcoord_location_ = gl_->GetAttribLocation(program_, "a_texcoord"); | |
868 texture_location_ = gl_->GetUniformLocation(program_, "s_texture"); | |
869 src_subrect_location_ = gl_->GetUniformLocation(program_, "src_subrect"); | |
870 src_pixelsize_location_ = gl_->GetUniformLocation(program_, "src_pixelsize"); | |
871 dst_pixelsize_location_ = gl_->GetUniformLocation(program_, "dst_pixelsize"); | |
872 scaling_vector_location_ = | |
873 gl_->GetUniformLocation(program_, "scaling_vector"); | |
874 color_weights_location_ = gl_->GetUniformLocation(program_, "color_weights"); | |
875 // The only reason fetching these attribute locations should fail is | |
876 // if the context was spontaneously lost (i.e., because the GPU | |
877 // process crashed, perhaps deliberately for testing). | |
878 DCHECK(Initialized() || gl_->GetGraphicsResetStatusKHR() != GL_NO_ERROR); | |
879 } | |
880 | |
881 void ShaderProgram::UseProgram(const gfx::Size& src_size, | |
882 const gfx::Rect& src_subrect, | |
883 const gfx::Size& dst_size, | |
884 bool scale_x, | |
885 bool flip_y, | |
886 GLfloat color_weights[4]) { | |
887 gl_->UseProgram(program_); | |
888 | |
889 // OpenGL defines the last parameter to VertexAttribPointer as type | |
890 // "const GLvoid*" even though it is actually an offset into the buffer | |
891 // object's data store and not a pointer to the client's address space. | |
892 const void* offsets[2] = { | |
893 0, reinterpret_cast<const void*>(2 * sizeof(GLfloat)) | |
894 }; | |
895 | |
896 gl_->VertexAttribPointer(position_location_, | |
897 2, | |
898 GL_FLOAT, | |
899 GL_FALSE, | |
900 4 * sizeof(GLfloat), | |
901 offsets[0]); | |
902 gl_->EnableVertexAttribArray(position_location_); | |
903 | |
904 gl_->VertexAttribPointer(texcoord_location_, | |
905 2, | |
906 GL_FLOAT, | |
907 GL_FALSE, | |
908 4 * sizeof(GLfloat), | |
909 offsets[1]); | |
910 gl_->EnableVertexAttribArray(texcoord_location_); | |
911 | |
912 gl_->Uniform1i(texture_location_, 0); | |
913 | |
914 // Convert |src_subrect| to texture coordinates. | |
915 GLfloat src_subrect_texcoord[] = { | |
916 static_cast<float>(src_subrect.x()) / src_size.width(), | |
917 static_cast<float>(src_subrect.y()) / src_size.height(), | |
918 static_cast<float>(src_subrect.width()) / src_size.width(), | |
919 static_cast<float>(src_subrect.height()) / src_size.height(), }; | |
920 if (flip_y) { | |
921 src_subrect_texcoord[1] += src_subrect_texcoord[3]; | |
922 src_subrect_texcoord[3] *= -1.0; | |
923 } | |
924 gl_->Uniform4fv(src_subrect_location_, 1, src_subrect_texcoord); | |
925 | |
926 gl_->Uniform2f(src_pixelsize_location_, src_size.width(), src_size.height()); | |
927 gl_->Uniform2f(dst_pixelsize_location_, | |
928 static_cast<float>(dst_size.width()), | |
929 static_cast<float>(dst_size.height())); | |
930 | |
931 gl_->Uniform2f( | |
932 scaling_vector_location_, scale_x ? 1.0 : 0.0, scale_x ? 0.0 : 1.0); | |
933 gl_->Uniform4fv(color_weights_location_, 1, color_weights); | |
934 } | |
935 | |
936 } // namespace content | |
OLD | NEW |