Home | History | Annotate | Download | only in client
      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 
     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 
     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:
     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>;
     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.
    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 
    159   virtual ~ScalerImpl() {
    160     if (intermediate_texture_) {
    161       context_->deleteTexture(intermediate_texture_);
    162     }
    163   }
    164 
    165   // GLHelperShader::ShaderInterface implementation.
    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.
    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 
    234   virtual const gfx::Size& SrcSize() OVERRIDE {
    235     if (subscaler_) {
    236       return subscaler_->SrcSize();
    237     }
    238     return spec_.src_size;
    239   }
    240   virtual const gfx::Rect& SrcSubrect() OVERRIDE {
    241     if (subscaler_) {
    242       return subscaler_->SrcSubrect();
    243     }
    244     return spec_.src_subrect;
    245   }
    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 
    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.
    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 
    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*
    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*
    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*
    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 
    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>
    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 
    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 
    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