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