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