Upstream version 5.34.92.0
[platform/framework/web/crosswalk.git] / src / content / common / gpu / client / gl_helper_scaling.cc
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 <deque>
8 #include <string>
9 #include <vector>
10
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"
22
23 using gpu::gles2::GLES2Interface;
24
25 namespace content {
26
27 GLHelperScaling::GLHelperScaling(GLES2Interface* gl, GLHelper* helper)
28     : gl_(gl), helper_(helper), vertex_attributes_buffer_(gl_) {
29   InitBuffer();
30 }
31
32 GLHelperScaling::~GLHelperScaling() {}
33
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) {}
51
52   // Compile shader program.
53   void Setup(const GLchar* vertex_shader_text,
54              const GLchar* fragment_shader_text);
55
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]);
65
66   bool Initialized() const { return position_location_ != -1; }
67
68  private:
69   friend class base::RefCounted<ShaderProgram>;
70   ~ShaderProgram() { gl_->DeleteProgram(program_); }
71
72   GLES2Interface* gl_;
73   GLHelper* helper_;
74
75   // A program for copying a source texture into a destination texture.
76   GLuint program_;
77
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_;
95
96   DISALLOW_COPY_AND_ASSIGN(ShaderProgram);
97 };
98
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;
137     }
138     shader_program_ =
139         scaler_helper_->GetShaderProgram(spec_.shader, spec_.swizzle);
140
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,
147                       0,
148                       GL_RGBA,
149                       spec_.src_size.width(),
150                       spec_.src_size.height(),
151                       0,
152                       GL_RGBA,
153                       GL_UNSIGNED_BYTE,
154                       NULL);
155     }
156   }
157
158   virtual ~ScalerImpl() {
159     if (intermediate_texture_) {
160       gl_->DeleteTextures(1, &intermediate_texture_);
161     }
162   }
163
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_;
170     }
171
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],
182                                 0);
183       buffers[t] = GL_COLOR_ATTACHMENT0 + t;
184     }
185     ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, source_texture);
186
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);
191
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());
202
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());
207     }
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]);
213     }
214   }
215
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);
221   }
222
223   virtual const gfx::Size& SrcSize() OVERRIDE {
224     if (subscaler_) {
225       return subscaler_->SrcSize();
226     }
227     return spec_.src_size;
228   }
229   virtual const gfx::Rect& SrcSubrect() OVERRIDE {
230     if (subscaler_) {
231       return subscaler_->SrcSubrect();
232     }
233     return spec_.src_subrect;
234   }
235   virtual const gfx::Size& DstSize() OVERRIDE { return spec_.dst_size; }
236
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_;
246 };
247
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_) {}
262
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;
287
288     if (!y_ops->empty()) {
289       current_queue = y_ops;
290     } else {
291       current_queue = x_ops;
292     }
293
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;
299         }
300         break;
301       case 2:
302         if (quality == GLHelper::SCALER_QUALITY_BEST) {
303           current_shader = SHADER_BICUBIC_HALF_1D;
304         }
305         break;
306       case 3:
307         DCHECK(quality != GLHelper::SCALER_QUALITY_BEST);
308         current_shader = SHADER_BILINEAR3;
309         break;
310       default:
311         NOTREACHED();
312     }
313     bool scale_x = current_queue->front().scale_x;
314     current_queue->front().UpdateSize(&intermediate_size);
315     current_queue->pop_front();
316
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;
330         }
331       }
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;
357               }
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;
368           }
369         } else if (x_ops->front().scale_factor == 2) {
370           // Ny + 1x-downscale
371           x_passes = 1;
372         }
373
374         for (int i = 0; i < x_passes; i++) {
375           x_ops->front().UpdateSize(&intermediate_size);
376           x_ops->pop_front();
377         }
378       }
379     }
380
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;
392   }
393 }
394
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;
413   }
414
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);
426
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);
436 }
437
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);
453
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);
457   }
458   return ret;
459 }
460
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     const float color_weights[4]) {
467   ScalerStage stage(SHADER_PLANAR,
468                     src_size,
469                     src_subrect,
470                     dst_size,
471                     true,
472                     vertically_flip_texture,
473                     false);
474   return new ScalerImpl(gl_, this, stage, NULL, color_weights);
475 }
476
477 GLHelperScaling::ShaderInterface* GLHelperScaling::CreateYuvMrtShader(
478     const gfx::Size& src_size,
479     const gfx::Rect& src_subrect,
480     const gfx::Size& dst_size,
481     bool vertically_flip_texture,
482     ShaderType shader) {
483   DCHECK(shader == SHADER_YUV_MRT_PASS1 || shader == SHADER_YUV_MRT_PASS2);
484   ScalerStage stage(shader,
485                     src_size,
486                     src_subrect,
487                     dst_size,
488                     true,
489                     vertically_flip_texture,
490                     false);
491   return new ScalerImpl(gl_, this, stage, NULL, NULL);
492 }
493
494 const GLfloat GLHelperScaling::kVertexAttributes[] = {
495     -1.0f, -1.0f, 0.0f, 0.0f,     // vertex 0
496     1.0f,  -1.0f, 1.0f, 0.0f,     // vertex 1
497     -1.0f, 1.0f,  0.0f, 1.0f,     // vertex 2
498     1.0f,  1.0f,  1.0f, 1.0f, };  // vertex 3
499
500 void GLHelperScaling::InitBuffer() {
501   ScopedBufferBinder<GL_ARRAY_BUFFER> buffer_binder(gl_,
502                                                     vertex_attributes_buffer_);
503   gl_->BufferData(GL_ARRAY_BUFFER,
504                   sizeof(kVertexAttributes),
505                   kVertexAttributes,
506                   GL_STATIC_DRAW);
507 }
508
509 scoped_refptr<ShaderProgram> GLHelperScaling::GetShaderProgram(ShaderType type,
510                                                                bool swizzle) {
511   ShaderProgramKeyType key(type, swizzle);
512   scoped_refptr<ShaderProgram>& cache_entry(shader_programs_[key]);
513   if (!cache_entry.get()) {
514     cache_entry = new ShaderProgram(gl_, helper_);
515     std::basic_string<GLchar> vertex_program;
516     std::basic_string<GLchar> fragment_program;
517     std::basic_string<GLchar> vertex_header;
518     std::basic_string<GLchar> fragment_directives;
519     std::basic_string<GLchar> fragment_header;
520     std::basic_string<GLchar> shared_variables;
521
522     vertex_header.append(
523         "precision highp float;\n"
524         "attribute vec2 a_position;\n"
525         "attribute vec2 a_texcoord;\n"
526         "uniform vec4 src_subrect;\n");
527
528     fragment_header.append(
529         "precision mediump float;\n"
530         "uniform sampler2D s_texture;\n");
531
532     vertex_program.append(
533         "  gl_Position = vec4(a_position, 0.0, 1.0);\n"
534         "  vec2 texcoord = src_subrect.xy + a_texcoord * src_subrect.zw;\n");
535
536     switch (type) {
537       case SHADER_BILINEAR:
538         shared_variables.append("varying vec2 v_texcoord;\n");
539         vertex_program.append("  v_texcoord = texcoord;\n");
540         fragment_program.append(
541             "  gl_FragColor = texture2D(s_texture, v_texcoord);\n");
542         break;
543
544       case SHADER_BILINEAR2:
545         // This is equivialent to two passes of the BILINEAR shader above.
546         // It can be used to scale an image down 1.0x-2.0x in either dimension,
547         // or exactly 4x.
548         shared_variables.append(
549             "varying vec4 v_texcoords;\n");  // 2 texcoords packed in one quad
550         vertex_header.append(
551             "uniform vec2 scaling_vector;\n"
552             "uniform vec2 dst_pixelsize;\n");
553         vertex_program.append(
554             "  vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n"
555             "  step /= 4.0;\n"
556             "  v_texcoords.xy = texcoord + step;\n"
557             "  v_texcoords.zw = texcoord - step;\n");
558
559         fragment_program.append(
560             "  gl_FragColor = (texture2D(s_texture, v_texcoords.xy) +\n"
561             "                  texture2D(s_texture, v_texcoords.zw)) / 2.0;\n");
562         break;
563
564       case SHADER_BILINEAR3:
565         // This is kind of like doing 1.5 passes of the BILINEAR shader.
566         // It can be used to scale an image down 1.5x-3.0x, or exactly 6x.
567         shared_variables.append(
568             "varying vec4 v_texcoords1;\n"  // 2 texcoords packed in one quad
569             "varying vec2 v_texcoords2;\n");
570         vertex_header.append(
571             "uniform vec2 scaling_vector;\n"
572             "uniform vec2 dst_pixelsize;\n");
573         vertex_program.append(
574             "  vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n"
575             "  step /= 3.0;\n"
576             "  v_texcoords1.xy = texcoord + step;\n"
577             "  v_texcoords1.zw = texcoord;\n"
578             "  v_texcoords2 = texcoord - step;\n");
579         fragment_program.append(
580             "  gl_FragColor = (texture2D(s_texture, v_texcoords1.xy) +\n"
581             "                  texture2D(s_texture, v_texcoords1.zw) +\n"
582             "                  texture2D(s_texture, v_texcoords2)) / 3.0;\n");
583         break;
584
585       case SHADER_BILINEAR4:
586         // This is equivialent to three passes of the BILINEAR shader above,
587         // It can be used to scale an image down 2.0x-4.0x or exactly 8x.
588         shared_variables.append("varying vec4 v_texcoords[2];\n");
589         vertex_header.append(
590             "uniform vec2 scaling_vector;\n"
591             "uniform vec2 dst_pixelsize;\n");
592         vertex_program.append(
593             "  vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n"
594             "  step /= 8.0;\n"
595             "  v_texcoords[0].xy = texcoord - step * 3.0;\n"
596             "  v_texcoords[0].zw = texcoord - step;\n"
597             "  v_texcoords[1].xy = texcoord + step;\n"
598             "  v_texcoords[1].zw = texcoord + step * 3.0;\n");
599         fragment_program.append(
600             "  gl_FragColor = (\n"
601             "      texture2D(s_texture, v_texcoords[0].xy) +\n"
602             "      texture2D(s_texture, v_texcoords[0].zw) +\n"
603             "      texture2D(s_texture, v_texcoords[1].xy) +\n"
604             "      texture2D(s_texture, v_texcoords[1].zw)) / 4.0;\n");
605         break;
606
607       case SHADER_BILINEAR2X2:
608         // This is equivialent to four passes of the BILINEAR shader above.
609         // Two in each dimension. It can be used to scale an image down
610         // 1.0x-2.0x in both X and Y directions. Or, it could be used to
611         // scale an image down by exactly 4x in both dimensions.
612         shared_variables.append("varying vec4 v_texcoords[2];\n");
613         vertex_header.append("uniform vec2 dst_pixelsize;\n");
614         vertex_program.append(
615             "  vec2 step = src_subrect.zw / 4.0 / dst_pixelsize;\n"
616             "  v_texcoords[0].xy = texcoord + vec2(step.x, step.y);\n"
617             "  v_texcoords[0].zw = texcoord + vec2(step.x, -step.y);\n"
618             "  v_texcoords[1].xy = texcoord + vec2(-step.x, step.y);\n"
619             "  v_texcoords[1].zw = texcoord + vec2(-step.x, -step.y);\n");
620         fragment_program.append(
621             "  gl_FragColor = (\n"
622             "      texture2D(s_texture, v_texcoords[0].xy) +\n"
623             "      texture2D(s_texture, v_texcoords[0].zw) +\n"
624             "      texture2D(s_texture, v_texcoords[1].xy) +\n"
625             "      texture2D(s_texture, v_texcoords[1].zw)) / 4.0;\n");
626         break;
627
628       case SHADER_BICUBIC_HALF_1D:
629         // This scales down texture by exactly half in one dimension.
630         // directions in one pass. We use bilinear lookup to reduce
631         // the number of texture reads from 8 to 4
632         shared_variables.append(
633             "const float CenterDist = 99.0 / 140.0;\n"
634             "const float LobeDist = 11.0 / 4.0;\n"
635             "const float CenterWeight = 35.0 / 64.0;\n"
636             "const float LobeWeight = -3.0 / 64.0;\n"
637             "varying vec4 v_texcoords[2];\n");
638         vertex_header.append(
639             "uniform vec2 scaling_vector;\n"
640             "uniform vec2 src_pixelsize;\n");
641         vertex_program.append(
642             "  vec2 step = src_subrect.zw * scaling_vector / src_pixelsize;\n"
643             "  v_texcoords[0].xy = texcoord - LobeDist * step;\n"
644             "  v_texcoords[0].zw = texcoord - CenterDist * step;\n"
645             "  v_texcoords[1].xy = texcoord + CenterDist * step;\n"
646             "  v_texcoords[1].zw = texcoord + LobeDist * step;\n");
647         fragment_program.append(
648             "  gl_FragColor = \n"
649             // Lobe pixels
650             "      (texture2D(s_texture, v_texcoords[0].xy) +\n"
651             "       texture2D(s_texture, v_texcoords[1].zw)) *\n"
652             "          LobeWeight +\n"
653             // Center pixels
654             "      (texture2D(s_texture, v_texcoords[0].zw) +\n"
655             "       texture2D(s_texture, v_texcoords[1].xy)) *\n"
656             "          CenterWeight;\n");
657         break;
658
659       case SHADER_BICUBIC_UPSCALE:
660         // When scaling up, we need 4 texture reads, but we can
661         // save some instructions because will know in which range of
662         // the bicubic function each call call to the bicubic function
663         // will be in.
664         // Also, when sampling the bicubic function like this, the sum
665         // is always exactly one, so we can skip normalization as well.
666         shared_variables.append("varying vec2 v_texcoord;\n");
667         vertex_program.append("  v_texcoord = texcoord;\n");
668         fragment_header.append(
669             "uniform vec2 src_pixelsize;\n"
670             "uniform vec2 scaling_vector;\n"
671             "const float a = -0.5;\n"
672             // This function is equivialent to calling the bicubic
673             // function with x-1, x, 1-x and 2-x
674             // (assuming 0 <= x < 1)
675             "vec4 filt4(float x) {\n"
676             "  return vec4(x * x * x, x * x, x, 1) *\n"
677             "         mat4(       a,      -2.0 * a,   a, 0.0,\n"
678             "               a + 2.0,      -a - 3.0, 0.0, 1.0,\n"
679             "              -a - 2.0, 3.0 + 2.0 * a,  -a, 0.0,\n"
680             "                    -a,             a, 0.0, 0.0);\n"
681             "}\n"
682             "mat4 pixels_x(vec2 pos, vec2 step) {\n"
683             "  return mat4(\n"
684             "      texture2D(s_texture, pos - step),\n"
685             "      texture2D(s_texture, pos),\n"
686             "      texture2D(s_texture, pos + step),\n"
687             "      texture2D(s_texture, pos + step * 2.0));\n"
688             "}\n");
689         fragment_program.append(
690             "  vec2 pixel_pos = v_texcoord * src_pixelsize - \n"
691             "      scaling_vector / 2.0;\n"
692             "  float frac = fract(dot(pixel_pos, scaling_vector));\n"
693             "  vec2 base = (floor(pixel_pos) + vec2(0.5)) / src_pixelsize;\n"
694             "  vec2 step = scaling_vector / src_pixelsize;\n"
695             "  gl_FragColor = pixels_x(base, step) * filt4(frac);\n");
696         break;
697
698       case SHADER_PLANAR:
699         // Converts four RGBA pixels into one pixel. Each RGBA
700         // pixel will be dot-multiplied with the color weights and
701         // then placed into a component of the output. This is used to
702         // convert RGBA textures into Y, U and V textures. We do this
703         // because single-component textures are not renderable on all
704         // architectures.
705         shared_variables.append("varying vec4 v_texcoords[2];\n");
706         vertex_header.append(
707             "uniform vec2 scaling_vector;\n"
708             "uniform vec2 dst_pixelsize;\n");
709         vertex_program.append(
710             "  vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n"
711             "  step /= 4.0;\n"
712             "  v_texcoords[0].xy = texcoord - step * 1.5;\n"
713             "  v_texcoords[0].zw = texcoord - step * 0.5;\n"
714             "  v_texcoords[1].xy = texcoord + step * 0.5;\n"
715             "  v_texcoords[1].zw = texcoord + step * 1.5;\n");
716         fragment_header.append("uniform vec4 color_weights;\n");
717         fragment_program.append(
718             "  gl_FragColor = color_weights * mat4(\n"
719             "    vec4(texture2D(s_texture, v_texcoords[0].xy).rgb, 1.0),\n"
720             "    vec4(texture2D(s_texture, v_texcoords[0].zw).rgb, 1.0),\n"
721             "    vec4(texture2D(s_texture, v_texcoords[1].xy).rgb, 1.0),\n"
722             "    vec4(texture2D(s_texture, v_texcoords[1].zw).rgb, 1.0));\n");
723         // Swizzle makes no sense for this shader.
724         DCHECK(!swizzle);
725         break;
726
727       case SHADER_YUV_MRT_PASS1:
728         // RGB24 to YV12 in two passes; writing two 8888 targets each pass.
729         //
730         // YV12 is full-resolution luma and half-resolution blue/red chroma.
731         //
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
752         //
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         // Swizzle makes no sense for this shader.
788         DCHECK(!swizzle);
789         break;
790
791       case SHADER_YUV_MRT_PASS2:
792         // We're just sampling two pixels and unswizzling them.  There's
793         // no need to do vertical scaling with math, since bilinear
794         // interpolation in the sampler takes care of that.
795         shared_variables.append("varying vec4 v_texcoords;\n");
796         vertex_header.append(
797             "uniform vec2 scaling_vector;\n"
798             "uniform vec2 dst_pixelsize;\n");
799         vertex_program.append(
800             "  vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n"
801             "  step /= 2.0;\n"
802             "  v_texcoords.xy = texcoord - step * 0.5;\n"
803             "  v_texcoords.zw = texcoord + step * 0.5;\n");
804         fragment_directives.append("#extension GL_EXT_draw_buffers : enable\n");
805         fragment_program.append(
806             "  vec4 lo_uuvv = texture2D(s_texture, v_texcoords.xy);\n"
807             "  vec4 hi_uuvv = texture2D(s_texture, v_texcoords.zw);\n"
808             "  gl_FragData[0] = vec4(lo_uuvv.rg, hi_uuvv.rg);\n"
809             "  gl_FragData[1] = vec4(lo_uuvv.ba, hi_uuvv.ba);\n");
810         // Swizzle makes no sense for this shader.
811         DCHECK(!swizzle);
812         break;
813     }
814     if (swizzle) {
815       fragment_program.append("  gl_FragColor = gl_FragColor.bgra;\n");
816     }
817
818     vertex_program = vertex_header + shared_variables + "void main() {\n" +
819                      vertex_program + "}\n";
820
821     fragment_program = fragment_directives + fragment_header +
822                        shared_variables + "void main() {\n" + fragment_program +
823                        "}\n";
824
825     cache_entry->Setup(vertex_program.c_str(), fragment_program.c_str());
826   }
827   return cache_entry;
828 }
829
830 void ShaderProgram::Setup(const GLchar* vertex_shader_text,
831                           const GLchar* fragment_shader_text) {
832   // Shaders to map the source texture to |dst_texture_|.
833   GLuint vertex_shader =
834       helper_->CompileShaderFromSource(vertex_shader_text, GL_VERTEX_SHADER);
835   if (vertex_shader == 0)
836     return;
837
838   gl_->AttachShader(program_, vertex_shader);
839   gl_->DeleteShader(vertex_shader);
840
841   GLuint fragment_shader = helper_->CompileShaderFromSource(
842       fragment_shader_text, GL_FRAGMENT_SHADER);
843   if (fragment_shader == 0)
844     return;
845   gl_->AttachShader(program_, fragment_shader);
846   gl_->DeleteShader(fragment_shader);
847
848   gl_->LinkProgram(program_);
849
850   GLint link_status = 0;
851   gl_->GetProgramiv(program_, GL_LINK_STATUS, &link_status);
852   if (!link_status)
853     return;
854
855   position_location_ = gl_->GetAttribLocation(program_, "a_position");
856   texcoord_location_ = gl_->GetAttribLocation(program_, "a_texcoord");
857   texture_location_ = gl_->GetUniformLocation(program_, "s_texture");
858   src_subrect_location_ = gl_->GetUniformLocation(program_, "src_subrect");
859   src_pixelsize_location_ = gl_->GetUniformLocation(program_, "src_pixelsize");
860   dst_pixelsize_location_ = gl_->GetUniformLocation(program_, "dst_pixelsize");
861   scaling_vector_location_ =
862       gl_->GetUniformLocation(program_, "scaling_vector");
863   color_weights_location_ = gl_->GetUniformLocation(program_, "color_weights");
864   return;
865 }
866
867 void ShaderProgram::UseProgram(const gfx::Size& src_size,
868                                const gfx::Rect& src_subrect,
869                                const gfx::Size& dst_size,
870                                bool scale_x,
871                                bool flip_y,
872                                GLfloat color_weights[4]) {
873   gl_->UseProgram(program_);
874
875   // OpenGL defines the last parameter to VertexAttribPointer as type
876   // "const GLvoid*" even though it is actually an offset into the buffer
877   // object's data store and not a pointer to the client's address space.
878   const void* offsets[2] = {
879       0, reinterpret_cast<const void*>(2 * sizeof(GLfloat))
880   };
881
882   gl_->VertexAttribPointer(position_location_,
883                            2,
884                            GL_FLOAT,
885                            GL_FALSE,
886                            4 * sizeof(GLfloat),
887                            offsets[0]);
888   gl_->EnableVertexAttribArray(position_location_);
889
890   gl_->VertexAttribPointer(texcoord_location_,
891                            2,
892                            GL_FLOAT,
893                            GL_FALSE,
894                            4 * sizeof(GLfloat),
895                            offsets[1]);
896   gl_->EnableVertexAttribArray(texcoord_location_);
897
898   gl_->Uniform1i(texture_location_, 0);
899
900   // Convert |src_subrect| to texture coordinates.
901   GLfloat src_subrect_texcoord[] = {
902       static_cast<float>(src_subrect.x()) / src_size.width(),
903       static_cast<float>(src_subrect.y()) / src_size.height(),
904       static_cast<float>(src_subrect.width()) / src_size.width(),
905       static_cast<float>(src_subrect.height()) / src_size.height(), };
906   if (flip_y) {
907     src_subrect_texcoord[1] += src_subrect_texcoord[3];
908     src_subrect_texcoord[3] *= -1.0;
909   }
910   gl_->Uniform4fv(src_subrect_location_, 1, src_subrect_texcoord);
911
912   gl_->Uniform2f(src_pixelsize_location_, src_size.width(), src_size.height());
913   gl_->Uniform2f(dst_pixelsize_location_,
914                  static_cast<float>(dst_size.width()),
915                  static_cast<float>(dst_size.height()));
916
917   gl_->Uniform2f(
918       scaling_vector_location_, scale_x ? 1.0 : 0.0, scale_x ? 0.0 : 1.0);
919   gl_->Uniform4fv(color_weights_location_, 1, color_weights);
920 }
921
922 }  // namespace content