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 WebKit::WebGLId;
     25 using WebKit::WebGraphicsContext3D;
     26 
     27 namespace content {
     28 
     29 GLHelperScaling::GLHelperScaling(WebKit::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 WebKit::WGC3Dchar* vertex_shader_text,
     55              const WebKit::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   WebKit::WGC3Dint position_location_;
     79   // The location of the texture coordinate in the program.
     80   WebKit::WGC3Dint texcoord_location_;
     81   // The location of the source texture in the program.
     82   WebKit::WGC3Dint texture_location_;
     83   // The location of the texture coordinate of
     84   // the sub-rectangle in the program.
     85   WebKit::WGC3Dint src_subrect_location_;
     86   // Location of size of source image in pixels.
     87   WebKit::WGC3Dint src_pixelsize_location_;
     88   // Location of size of destination image in pixels.
     89   WebKit::WGC3Dint dst_pixelsize_location_;
     90   // Location of vector for scaling direction.
     91   WebKit::WGC3Dint scaling_vector_location_;
     92   // Location of color weights.
     93   WebKit::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       WebKit::WebGLId source_texture,
    168       const std::vector<WebKit::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<WebKit::WGC3Denum[]> buffers(
    179         new WebKit::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(WebKit::WebGLId source_texture,
    228                      WebKit::WebGLId dest_texture) OVERRIDE {
    229     std::vector<WebKit::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   WebKit::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 WebKit::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<WebKit::WGC3Dchar> vertex_program;
    537     std::basic_string<WebKit::WGC3Dchar> fragment_program;
    538     std::basic_string<WebKit::WGC3Dchar> vertex_header;
    539     std::basic_string<WebKit::WGC3Dchar> fragment_directives;
    540     std::basic_string<WebKit::WGC3Dchar> fragment_header;
    541     std::basic_string<WebKit::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 
    548     fragment_header.append(
    549         "precision mediump float;\n"
    550         "uniform sampler2D s_texture;\n");
    551 
    552     shared_variables.append(
    553         "uniform vec4 src_subrect;\n"
    554         "uniform vec2 src_pixelsize;\n"
    555         "uniform vec2 dst_pixelsize;\n"
    556         "uniform vec2 scaling_vector;\n");
    557 
    558     vertex_program.append(
    559         "  gl_Position = vec4(a_position, 0.0, 1.0);\n"
    560         "  vec2 texcoord = src_subrect.xy + a_texcoord * src_subrect.zw;\n");
    561 
    562     switch (type) {
    563       case SHADER_BILINEAR:
    564         shared_variables.append("varying vec2 v_texcoord;\n");
    565         vertex_program.append("  v_texcoord = texcoord;\n");
    566         fragment_program.append(
    567             "  gl_FragColor = texture2D(s_texture, v_texcoord);\n");
    568         break;
    569 
    570       case SHADER_BILINEAR2:
    571         // This is equivialent to two passes of the BILINEAR shader above.
    572         // It can be used to scale an image down 1.0x-2.0x in either dimension,
    573         // or exactly 4x.
    574         shared_variables.append(
    575             "varying vec4 v_texcoords;\n");  // 2 texcoords packed in one quad
    576         vertex_program.append(
    577             "  vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n"
    578             "  step /= 4.0;\n"
    579             "  v_texcoords.xy = texcoord + step;\n"
    580             "  v_texcoords.zw = texcoord - step;\n");
    581 
    582         fragment_program.append(
    583             "  gl_FragColor = (texture2D(s_texture, v_texcoords.xy) +\n"
    584             "                  texture2D(s_texture, v_texcoords.zw)) / 2.0;\n");
    585          break;
    586 
    587       case SHADER_BILINEAR3:
    588         // This is kind of like doing 1.5 passes of the BILINEAR shader.
    589         // It can be used to scale an image down 1.5x-3.0x, or exactly 6x.
    590         shared_variables.append(
    591             "varying vec4 v_texcoords1;\n"  // 2 texcoords packed in one quad
    592             "varying vec2 v_texcoords2;\n");
    593         vertex_program.append(
    594             "  vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n"
    595             "  step /= 3.0;\n"
    596             "  v_texcoords1.xy = texcoord + step;\n"
    597             "  v_texcoords1.zw = texcoord;\n"
    598             "  v_texcoords2 = texcoord - step;\n");
    599         fragment_program.append(
    600             "  gl_FragColor = (texture2D(s_texture, v_texcoords1.xy) +\n"
    601             "                  texture2D(s_texture, v_texcoords1.zw) +\n"
    602             "                  texture2D(s_texture, v_texcoords2)) / 3.0;\n");
    603          break;
    604 
    605       case SHADER_BILINEAR4:
    606         // This is equivialent to three passes of the BILINEAR shader above,
    607         // It can be used to scale an image down 2.0x-4.0x or exactly 8x.
    608         shared_variables.append(
    609             "varying vec4 v_texcoords[2];\n");
    610         vertex_program.append(
    611             "  vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n"
    612             "  step /= 8.0;\n"
    613             "  v_texcoords[0].xy = texcoord - step * 3.0;\n"
    614             "  v_texcoords[0].zw = texcoord - step;\n"
    615             "  v_texcoords[1].xy = texcoord + step;\n"
    616             "  v_texcoords[1].zw = texcoord + step * 3.0;\n");
    617         fragment_program.append(
    618             "  gl_FragColor = (\n"
    619             "      texture2D(s_texture, v_texcoords[0].xy) +\n"
    620             "      texture2D(s_texture, v_texcoords[0].zw) +\n"
    621             "      texture2D(s_texture, v_texcoords[1].xy) +\n"
    622             "      texture2D(s_texture, v_texcoords[1].zw)) / 4.0;\n");
    623          break;
    624 
    625       case SHADER_BILINEAR2X2:
    626         // This is equivialent to four passes of the BILINEAR shader above.
    627         // Two in each dimension. It can be used to scale an image down
    628         // 1.0x-2.0x in both X and Y directions. Or, it could be used to
    629         // scale an image down by exactly 4x in both dimensions.
    630         shared_variables.append(
    631             "varying vec4 v_texcoords[2];\n");
    632         vertex_program.append(
    633             "  vec2 step = src_subrect.zw / 4.0 / dst_pixelsize;\n"
    634             "  v_texcoords[0].xy = texcoord + vec2(step.x, step.y);\n"
    635             "  v_texcoords[0].zw = texcoord + vec2(step.x, -step.y);\n"
    636             "  v_texcoords[1].xy = texcoord + vec2(-step.x, step.y);\n"
    637             "  v_texcoords[1].zw = texcoord + vec2(-step.x, -step.y);\n");
    638         fragment_program.append(
    639             "  gl_FragColor = (\n"
    640             "      texture2D(s_texture, v_texcoords[0].xy) +\n"
    641             "      texture2D(s_texture, v_texcoords[0].zw) +\n"
    642             "      texture2D(s_texture, v_texcoords[1].xy) +\n"
    643             "      texture2D(s_texture, v_texcoords[1].zw)) / 4.0;\n");
    644          break;
    645 
    646       case SHADER_BICUBIC_HALF_1D:
    647         // This scales down texture by exactly half in one dimension.
    648         // directions in one pass. We use bilinear lookup to reduce
    649         // the number of texture reads from 8 to 4
    650         shared_variables.append(
    651             "const float CenterDist = 99.0 / 140.0;\n"
    652             "const float LobeDist = 11.0 / 4.0;\n"
    653             "const float CenterWeight = 35.0 / 64.0;\n"
    654             "const float LobeWeight = -3.0 / 64.0;\n"
    655             "varying vec4 v_texcoords[2];\n");
    656         vertex_program.append(
    657             "  vec2 step = src_subrect.zw * scaling_vector / src_pixelsize;\n"
    658             "  v_texcoords[0].xy = texcoord - LobeDist * step;\n"
    659             "  v_texcoords[0].zw = texcoord - CenterDist * step;\n"
    660             "  v_texcoords[1].xy = texcoord + CenterDist * step;\n"
    661             "  v_texcoords[1].zw = texcoord + LobeDist * step;\n");
    662         fragment_program.append(
    663             "  gl_FragColor = \n"
    664             // Lobe pixels
    665             "      (texture2D(s_texture, v_texcoords[0].xy) +\n"
    666             "       texture2D(s_texture, v_texcoords[1].zw)) *\n"
    667             "          LobeWeight +\n"
    668             // Center pixels
    669             "      (texture2D(s_texture, v_texcoords[0].zw) +\n"
    670             "       texture2D(s_texture, v_texcoords[1].xy)) *\n"
    671             "          CenterWeight;\n");
    672          break;
    673 
    674       case SHADER_BICUBIC_UPSCALE:
    675         // When scaling up, we need 4 texture reads, but we can
    676         // save some instructions because will know in which range of
    677         // the bicubic function each call call to the bicubic function
    678         // will be in.
    679         // Also, when sampling the bicubic function like this, the sum
    680         // is always exactly one, so we can skip normalization as well.
    681         shared_variables.append(
    682             "varying vec2 v_texcoord;\n");
    683         vertex_program.append(
    684             "  v_texcoord = texcoord;\n");
    685         fragment_header.append(
    686             "const float a = -0.5;\n"
    687             // This function is equivialent to calling the bicubic
    688             // function with x-1, x, 1-x and 2-x
    689             // (assuming 0 <= x < 1)
    690             "vec4 filt4(float x) {\n"
    691             "  return vec4(x * x * x, x * x, x, 1) *\n"
    692             "         mat4(       a,      -2.0 * a,   a, 0.0,\n"
    693             "               a + 2.0,      -a - 3.0, 0.0, 1.0,\n"
    694             "              -a - 2.0, 3.0 + 2.0 * a,  -a, 0.0,\n"
    695             "                    -a,             a, 0.0, 0.0);\n"
    696             "}\n"
    697             "mat4 pixels_x(vec2 pos, vec2 step) {\n"
    698             "  return mat4(\n"
    699             "      texture2D(s_texture, pos - step),\n"
    700             "      texture2D(s_texture, pos),\n"
    701             "      texture2D(s_texture, pos + step),\n"
    702             "      texture2D(s_texture, pos + step * 2.0));\n"
    703             "}\n");
    704         fragment_program.append(
    705             "  vec2 pixel_pos = v_texcoord * src_pixelsize - \n"
    706             "      scaling_vector / 2.0;\n"
    707             "  float frac = fract(dot(pixel_pos, scaling_vector));\n"
    708             "  vec2 base = (floor(pixel_pos) + vec2(0.5)) / src_pixelsize;\n"
    709             "  vec2 step = scaling_vector / src_pixelsize;\n"
    710             "  gl_FragColor = pixels_x(base, step) * filt4(frac);\n");
    711         break;
    712 
    713       case SHADER_PLANAR:
    714         // Converts four RGBA pixels into one pixel. Each RGBA
    715         // pixel will be dot-multiplied with the color weights and
    716         // then placed into a component of the output. This is used to
    717         // convert RGBA textures into Y, U and V textures. We do this
    718         // because single-component textures are not renderable on all
    719         // architectures.
    720         shared_variables.append(
    721             "varying vec4 v_texcoords[2];\n"
    722             "uniform vec4 color_weights;\n");
    723         vertex_program.append(
    724             "  vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n"
    725             "  step /= 4.0;\n"
    726             "  v_texcoords[0].xy = texcoord - step * 1.5;\n"
    727             "  v_texcoords[0].zw = texcoord - step * 0.5;\n"
    728             "  v_texcoords[1].xy = texcoord + step * 0.5;\n"
    729             "  v_texcoords[1].zw = texcoord + step * 1.5;\n");
    730         fragment_program.append(
    731             "  gl_FragColor = color_weights * mat4(\n"
    732             "    vec4(texture2D(s_texture, v_texcoords[0].xy).rgb, 1.0),\n"
    733             "    vec4(texture2D(s_texture, v_texcoords[0].zw).rgb, 1.0),\n"
    734             "    vec4(texture2D(s_texture, v_texcoords[1].xy).rgb, 1.0),\n"
    735             "    vec4(texture2D(s_texture, v_texcoords[1].zw).rgb, 1.0));\n");
    736         // Swizzle makes no sense for this shader.
    737         DCHECK(!swizzle);
    738         break;
    739 
    740       case SHADER_YUV_MRT_PASS1:
    741         // RGB24 to YV12 in two passes; writing two 8888 targets each pass.
    742         //
    743         // YV12 is full-resolution luma and half-resolution blue/red chroma.
    744         //
    745         //                  (original)
    746         //    RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX
    747         //    RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX
    748         //    RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX
    749         //    RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX
    750         //    RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX
    751         //    RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX
    752         //      |
    753         //      |      (y plane)    (temporary)
    754         //      |      YYYY YYYY     UUVV UUVV
    755         //      +--> { YYYY YYYY  +  UUVV UUVV }
    756         //             YYYY YYYY     UUVV UUVV
    757         //   First     YYYY YYYY     UUVV UUVV
    758         //    pass     YYYY YYYY     UUVV UUVV
    759         //             YYYY YYYY     UUVV UUVV
    760         //                              |
    761         //                              |  (u plane) (v plane)
    762         //   Second                     |      UUUU   VVVV
    763         //     pass                     +--> { UUUU + VVVV }
    764         //                                     UUUU   VVVV
    765         //
    766         shared_variables.append(
    767             "varying vec4 v_texcoords[2];\n");
    768         vertex_program.append(
    769             "  vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n"
    770             "  step /= 4.0;\n"
    771             "  v_texcoords[0].xy = texcoord - step * 1.5;\n"
    772             "  v_texcoords[0].zw = texcoord - step * 0.5;\n"
    773             "  v_texcoords[1].xy = texcoord + step * 0.5;\n"
    774             "  v_texcoords[1].zw = texcoord + step * 1.5;\n");
    775         fragment_directives.append(
    776             "#extension GL_EXT_draw_buffers : enable\n");
    777         fragment_header.append(
    778             "const vec3 kRGBtoY = vec3(0.257, 0.504, 0.098);\n"
    779             "const float kYBias = 0.0625;\n"
    780             // Divide U and V by two to compensate for averaging below.
    781             "const vec3 kRGBtoU = vec3(-0.148, -0.291, 0.439) / 2.0;\n"
    782             "const vec3 kRGBtoV = vec3(0.439, -0.368, -0.071) / 2.0;\n"
    783             "const float kUVBias = 0.5;\n");
    784         fragment_program.append(
    785             "  vec3 pixel1 = texture2D(s_texture, v_texcoords[0].xy).rgb;\n"
    786             "  vec3 pixel2 = texture2D(s_texture, v_texcoords[0].zw).rgb;\n"
    787             "  vec3 pixel3 = texture2D(s_texture, v_texcoords[1].xy).rgb;\n"
    788             "  vec3 pixel4 = texture2D(s_texture, v_texcoords[1].zw).rgb;\n"
    789             "  vec3 pixel12 = pixel1 + pixel2;\n"
    790             "  vec3 pixel34 = pixel3 + pixel4;\n"
    791             "  gl_FragData[0] = vec4(dot(pixel1, kRGBtoY),\n"
    792             "                        dot(pixel2, kRGBtoY),\n"
    793             "                        dot(pixel3, kRGBtoY),\n"
    794             "                        dot(pixel4, kRGBtoY)) + kYBias;\n"
    795             "  gl_FragData[1] = vec4(dot(pixel12, kRGBtoU),\n"
    796             "                        dot(pixel34, kRGBtoU),\n"
    797             "                        dot(pixel12, kRGBtoV),\n"
    798             "                        dot(pixel34, kRGBtoV)) + kUVBias;\n");
    799         // Swizzle makes no sense for this shader.
    800         DCHECK(!swizzle);
    801         break;
    802 
    803       case SHADER_YUV_MRT_PASS2:
    804         // We're just sampling two pixels and unswizzling them.  There's
    805         // no need to do vertical scaling with math, since bilinear
    806         // interpolation in the sampler takes care of that.
    807         shared_variables.append(
    808             "varying vec4 v_texcoords;\n");
    809         vertex_program.append(
    810             "  vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n"
    811             "  step /= 2.0;\n"
    812             "  v_texcoords.xy = texcoord - step * 0.5;\n"
    813             "  v_texcoords.zw = texcoord + step * 0.5;\n");
    814         fragment_directives.append(
    815             "#extension GL_EXT_draw_buffers : enable\n");
    816         fragment_program.append(
    817             "  vec4 lo_uuvv = texture2D(s_texture, v_texcoords.xy);\n"
    818             "  vec4 hi_uuvv = texture2D(s_texture, v_texcoords.zw);\n"
    819             "  gl_FragData[0] = vec4(lo_uuvv.rg, hi_uuvv.rg);\n"
    820             "  gl_FragData[1] = vec4(lo_uuvv.ba, hi_uuvv.ba);\n");
    821         // Swizzle makes no sense for this shader.
    822         DCHECK(!swizzle);
    823         break;
    824     }
    825     if (swizzle) {
    826       fragment_program.append("  gl_FragColor = gl_FragColor.bgra;\n");
    827     }
    828 
    829     vertex_program =
    830         vertex_header +
    831         shared_variables +
    832         "void main() {\n" +
    833         vertex_program +
    834         "}\n";
    835 
    836     fragment_program =
    837         fragment_directives +
    838         fragment_header +
    839         shared_variables +
    840         "void main() {\n" +
    841         fragment_program +
    842         "}\n";
    843 
    844     bool result = cache_entry->Setup(vertex_program.c_str(),
    845                                      fragment_program.c_str());
    846     DCHECK(result || context_->isContextLost())
    847         << "vertex_program =\n" << vertex_program
    848         << "fragment_program =\n" << fragment_program;
    849   }
    850   return cache_entry;
    851 }
    852 
    853 bool ShaderProgram::Setup(const WebKit::WGC3Dchar* vertex_shader_text,
    854                           const WebKit::WGC3Dchar* fragment_shader_text) {
    855   // Shaders to map the source texture to |dst_texture_|.
    856   ScopedShader vertex_shader(context_, helper_->CompileShaderFromSource(
    857       vertex_shader_text, GL_VERTEX_SHADER));
    858   if (vertex_shader.id() == 0) {
    859     return false;
    860   }
    861   context_->attachShader(program_, vertex_shader);
    862   ScopedShader fragment_shader(context_, helper_->CompileShaderFromSource(
    863       fragment_shader_text, GL_FRAGMENT_SHADER));
    864   if (fragment_shader.id() == 0) {
    865     return false;
    866   }
    867   context_->attachShader(program_, fragment_shader);
    868   context_->linkProgram(program_);
    869 
    870   WebKit::WGC3Dint link_status = 0;
    871   context_->getProgramiv(program_, GL_LINK_STATUS, &link_status);
    872   if (!link_status) {
    873     LOG(ERROR) << std::string(context_->getProgramInfoLog(program_).utf8());
    874     return false;
    875   }
    876   position_location_ = context_->getAttribLocation(program_, "a_position");
    877   texcoord_location_ = context_->getAttribLocation(program_, "a_texcoord");
    878   texture_location_ = context_->getUniformLocation(program_, "s_texture");
    879   src_subrect_location_ = context_->getUniformLocation(program_, "src_subrect");
    880   src_pixelsize_location_ = context_->getUniformLocation(program_,
    881                                                          "src_pixelsize");
    882   dst_pixelsize_location_ = context_->getUniformLocation(program_,
    883                                                          "dst_pixelsize");
    884   scaling_vector_location_ = context_->getUniformLocation(program_,
    885                                                           "scaling_vector");
    886   color_weights_location_ = context_->getUniformLocation(program_,
    887                                                          "color_weights");
    888   return true;
    889 }
    890 
    891 void ShaderProgram::UseProgram(
    892     const gfx::Size& src_size,
    893     const gfx::Rect& src_subrect,
    894     const gfx::Size& dst_size,
    895     bool scale_x,
    896     bool flip_y,
    897     GLfloat color_weights[4]) {
    898   context_->useProgram(program_);
    899 
    900   WebKit::WGC3Dintptr offset = 0;
    901   context_->vertexAttribPointer(position_location_,
    902                                 2,
    903                                 GL_FLOAT,
    904                                 GL_FALSE,
    905                                 4 * sizeof(WebKit::WGC3Dfloat),
    906                                 offset);
    907   context_->enableVertexAttribArray(position_location_);
    908 
    909   offset += 2 * sizeof(WebKit::WGC3Dfloat);
    910   context_->vertexAttribPointer(texcoord_location_,
    911                                 2,
    912                                 GL_FLOAT,
    913                                 GL_FALSE,
    914                                 4 * sizeof(WebKit::WGC3Dfloat),
    915                                 offset);
    916   context_->enableVertexAttribArray(texcoord_location_);
    917 
    918   context_->uniform1i(texture_location_, 0);
    919 
    920   // Convert |src_subrect| to texture coordinates.
    921   GLfloat src_subrect_texcoord[] = {
    922     static_cast<float>(src_subrect.x()) / src_size.width(),
    923     static_cast<float>(src_subrect.y()) / src_size.height(),
    924     static_cast<float>(src_subrect.width()) / src_size.width(),
    925     static_cast<float>(src_subrect.height()) / src_size.height(),
    926   };
    927   if (flip_y) {
    928     src_subrect_texcoord[1] += src_subrect_texcoord[3];
    929     src_subrect_texcoord[3] *= -1.0;
    930   }
    931   context_->uniform4fv(src_subrect_location_, 1, src_subrect_texcoord);
    932 
    933   context_->uniform2f(src_pixelsize_location_,
    934                       src_size.width(),
    935                       src_size.height());
    936   context_->uniform2f(dst_pixelsize_location_,
    937                       static_cast<float>(dst_size.width()),
    938                       static_cast<float>(dst_size.height()));
    939 
    940   context_->uniform2f(scaling_vector_location_,
    941                       scale_x ? 1.0 : 0.0,
    942                       scale_x ? 0.0 : 1.0);
    943   context_->uniform4fv(color_weights_location_, 1, color_weights);
    944 }
    945 
    946 }  // namespace content
    947