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 <stdio.h>
      6 #include <cmath>
      7 #include <string>
      8 #include <vector>
      9 
     10 #include <GLES2/gl2.h>
     11 #include <GLES2/gl2ext.h>
     12 #include <GLES2/gl2extchromium.h>
     13 
     14 #include "base/at_exit.h"
     15 #include "base/bind.h"
     16 #include "base/command_line.h"
     17 #include "base/debug/trace_event.h"
     18 #include "base/file_util.h"
     19 #include "base/json/json_reader.h"
     20 #include "base/message_loop/message_loop.h"
     21 #include "base/run_loop.h"
     22 #include "base/strings/stringprintf.h"
     23 #include "base/synchronization/waitable_event.h"
     24 #include "base/time/time.h"
     25 #include "content/common/gpu/client/gl_helper.h"
     26 #include "content/common/gpu/client/gl_helper_scaling.h"
     27 #include "content/public/test/unittest_test_suite.h"
     28 #include "content/test/content_test_suite.h"
     29 #include "gpu/config/gpu_util.h"
     30 #include "media/base/video_frame.h"
     31 #include "testing/gtest/include/gtest/gtest.h"
     32 #include "third_party/skia/include/core/SkBitmap.h"
     33 #include "third_party/skia/include/core/SkTypes.h"
     34 #include "ui/gl/gl_surface.h"
     35 #include "webkit/common/gpu/webgraphicscontext3d_in_process_command_buffer_impl.h"
     36 
     37 #if defined(OS_MACOSX)
     38 #include "base/mac/scoped_nsautorelease_pool.h"
     39 #endif
     40 
     41 #if defined(TOOLKIT_GTK)
     42 #include "ui/gfx/gtk_util.h"
     43 #endif
     44 
     45 namespace content {
     46 
     47 using blink::WebGLId;
     48 using blink::WebGraphicsContext3D;
     49 using webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl;
     50 
     51 content::GLHelper::ScalerQuality kQualities[] = {
     52   content::GLHelper::SCALER_QUALITY_BEST,
     53   content::GLHelper::SCALER_QUALITY_GOOD,
     54   content::GLHelper::SCALER_QUALITY_FAST,
     55 };
     56 
     57 const char *kQualityNames[] = {
     58   "best",
     59   "good",
     60   "fast",
     61 };
     62 
     63 class GLHelperTest : public testing::Test {
     64  protected:
     65   virtual void SetUp() {
     66     WebGraphicsContext3D::Attributes attributes;
     67     context_ = WebGraphicsContext3DInProcessCommandBufferImpl::
     68         CreateOffscreenContext(attributes);
     69     context_->makeContextCurrent();
     70     context_support_ = context_->GetContextSupport();
     71     helper_.reset(new content::GLHelper(context_.get(), context_support_));
     72     helper_scaling_.reset(new content::GLHelperScaling(
     73         context_.get(),
     74         helper_.get()));
     75   }
     76 
     77   virtual void TearDown() {
     78     helper_scaling_.reset(NULL);
     79     helper_.reset(NULL);
     80     context_.reset(NULL);
     81   }
     82 
     83   void StartTracing(const std::string& filter) {
     84     base::debug::TraceLog::GetInstance()->SetEnabled(
     85         base::debug::CategoryFilter(filter),
     86         base::debug::TraceLog::RECORD_UNTIL_FULL);
     87   }
     88 
     89   static void TraceDataCB(
     90       const base::Callback<void()>& callback,
     91       std::string* output,
     92       const scoped_refptr<base::RefCountedString>& json_events_str,
     93       bool has_more_events) {
     94     if (output->size() > 1) {
     95       output->append(",");
     96     }
     97     output->append(json_events_str->data());
     98     if (!has_more_events) {
     99       callback.Run();
    100     }
    101   }
    102 
    103   // End tracing, return tracing data in a simple map
    104   // of event name->counts.
    105   void EndTracing(std::map<std::string, int> *event_counts) {
    106     std::string json_data = "[";
    107     base::debug::TraceLog::GetInstance()->SetDisabled();
    108     base::RunLoop run_loop;
    109     base::debug::TraceLog::GetInstance()->Flush(
    110         base::Bind(&GLHelperTest::TraceDataCB,
    111                    run_loop.QuitClosure(),
    112                    base::Unretained(&json_data)));
    113     run_loop.Run();
    114     json_data.append("]");
    115 
    116     scoped_ptr<Value> trace_data(base::JSONReader::Read(json_data));
    117     ListValue* list;
    118     CHECK(trace_data->GetAsList(&list));
    119     for (size_t i = 0; i < list->GetSize(); i++) {
    120       base::Value *item = NULL;
    121       if (list->Get(i, &item)) {
    122         base::DictionaryValue *dict;
    123         CHECK(item->GetAsDictionary(&dict));
    124         std::string name;
    125         CHECK(dict->GetString("name", &name));
    126         (*event_counts)[name]++;
    127         VLOG(1) << "trace name: " << name;
    128       }
    129     }
    130   }
    131 
    132   // Bicubic filter kernel function.
    133   static float Bicubic(float x) {
    134     const float a = -0.5;
    135     x = std::abs(x);
    136     float x2 = x * x;
    137     float x3 = x2 * x;
    138     if (x <= 1) {
    139       return (a + 2) * x3 - (a + 3) * x2 + 1;
    140     } else if (x < 2) {
    141       return a * x3 - 5 * a * x2 + 8 * a * x - 4 * a;
    142     } else {
    143       return 0.0f;
    144     }
    145   }
    146 
    147   // Look up a single R/G/B/A value.
    148   // Clamp x/y.
    149   int Channel(SkBitmap* pixels, int x, int y, int c) {
    150     uint32 *data = pixels->getAddr32(
    151         std::max(0, std::min(x, pixels->width() - 1)),
    152         std::max(0, std::min(y, pixels->height() - 1)));
    153     return (*data) >> (c * 8) & 0xff;
    154   }
    155 
    156   // Set a single R/G/B/A value.
    157   void SetChannel(SkBitmap* pixels, int x, int y, int c, int v) {
    158     DCHECK_GE(x, 0);
    159     DCHECK_GE(y, 0);
    160     DCHECK_LT(x, pixels->width());
    161     DCHECK_LT(y, pixels->height());
    162     uint32 *data = pixels->getAddr32(x, y);
    163     v = std::max(0, std::min(v, 255));
    164     *data = (*data & ~(0xffu << (c * 8))) | (v << (c * 8));
    165   }
    166 
    167   // Print all the R, G, B or A values from an SkBitmap in a
    168   // human-readable format.
    169   void PrintChannel(SkBitmap* pixels, int c) {
    170     for (int y = 0; y < pixels->height(); y++) {
    171       for (int x = 0; x < pixels->width(); x++) {
    172         printf("%3d, ", Channel(pixels, x, y, c));
    173       }
    174       printf("\n");
    175     }
    176   }
    177 
    178   // Print out the individual steps of a scaler pipeline.
    179   std::string PrintStages(
    180       const std::vector<GLHelperScaling::ScalerStage> &scaler_stages) {
    181     std::string ret;
    182     for (size_t i = 0; i < scaler_stages.size(); i++) {
    183       ret.append(base::StringPrintf("%dx%d -> %dx%d ",
    184                                     scaler_stages[i].src_size.width(),
    185                                     scaler_stages[i].src_size.height(),
    186                                     scaler_stages[i].dst_size.width(),
    187                                     scaler_stages[i].dst_size.height()));
    188       bool xy_matters = false;
    189       switch (scaler_stages[i].shader) {
    190         case GLHelperScaling::SHADER_BILINEAR:
    191           ret.append("bilinear");
    192           break;
    193         case GLHelperScaling::SHADER_BILINEAR2:
    194           ret.append("bilinear2");
    195           xy_matters = true;
    196           break;
    197         case GLHelperScaling::SHADER_BILINEAR3:
    198           ret.append("bilinear3");
    199           xy_matters = true;
    200           break;
    201         case GLHelperScaling::SHADER_BILINEAR4:
    202           ret.append("bilinear4");
    203           xy_matters = true;
    204           break;
    205         case GLHelperScaling::SHADER_BILINEAR2X2:
    206           ret.append("bilinear2x2");
    207           break;
    208         case GLHelperScaling::SHADER_BICUBIC_UPSCALE:
    209           ret.append("bicubic upscale");
    210           xy_matters = true;
    211           break;
    212         case GLHelperScaling::SHADER_BICUBIC_HALF_1D:
    213           ret.append("bicubic 1/2");
    214           xy_matters = true;
    215           break;
    216         case GLHelperScaling::SHADER_PLANAR:
    217           ret.append("planar");
    218           break;
    219         case GLHelperScaling::SHADER_YUV_MRT_PASS1:
    220           ret.append("rgb2yuv pass 1");
    221           break;
    222         case GLHelperScaling::SHADER_YUV_MRT_PASS2:
    223           ret.append("rgb2yuv pass 2");
    224           break;
    225       }
    226 
    227       if (xy_matters) {
    228         if (scaler_stages[i].scale_x) {
    229           ret.append(" X");
    230         } else {
    231           ret.append(" Y");
    232         }
    233       }
    234       ret.append("\n");
    235     }
    236     return ret;
    237   }
    238 
    239   bool CheckScale(double scale, int samples, bool already_scaled) {
    240     // 1:1 is valid if there is one sample.
    241     if (samples == 1 && scale == 1.0) {
    242       return true;
    243     }
    244     // Is it an exact down-scale (50%, 25%, etc.?)
    245     if (scale == 2.0 * samples) {
    246       return true;
    247     }
    248     // Upscales, only valid if we haven't already scaled in this dimension.
    249     if (!already_scaled) {
    250       // Is it a valid bilinear upscale?
    251       if (samples == 1 && scale <= 1.0) {
    252         return true;
    253       }
    254       // Multi-sample upscale-downscale combination?
    255       if (scale > samples / 2.0 && scale < samples) {
    256         return true;
    257       }
    258     }
    259     return false;
    260   }
    261 
    262   // Make sure that the stages of the scaler pipeline are sane.
    263   void ValidateScalerStages(
    264       content::GLHelper::ScalerQuality quality,
    265       const std::vector<GLHelperScaling::ScalerStage> &scaler_stages,
    266       const std::string& message) {
    267     bool previous_error = HasFailure();
    268     // First, check that the input size for each stage is equal to
    269     // the output size of the previous stage.
    270     for (size_t i = 1; i < scaler_stages.size(); i++) {
    271       EXPECT_EQ(scaler_stages[i - 1].dst_size.width(),
    272                 scaler_stages[i].src_size.width());
    273       EXPECT_EQ(scaler_stages[i - 1].dst_size.height(),
    274                 scaler_stages[i].src_size.height());
    275       EXPECT_EQ(scaler_stages[i].src_subrect.x(), 0);
    276       EXPECT_EQ(scaler_stages[i].src_subrect.y(), 0);
    277       EXPECT_EQ(scaler_stages[i].src_subrect.width(),
    278                 scaler_stages[i].src_size.width());
    279       EXPECT_EQ(scaler_stages[i].src_subrect.height(),
    280                 scaler_stages[i].src_size.height());
    281     }
    282 
    283     // Used to verify that up-scales are not attempted after some
    284     // other scale.
    285     bool scaled_x = false;
    286     bool scaled_y = false;
    287 
    288     for (size_t i = 0; i < scaler_stages.size(); i++) {
    289       // Note: 2.0 means scaling down by 50%
    290       double x_scale =
    291           static_cast<double>(scaler_stages[i].src_subrect.width()) /
    292           static_cast<double>(scaler_stages[i].dst_size.width());
    293       double y_scale =
    294           static_cast<double>(scaler_stages[i].src_subrect.height()) /
    295           static_cast<double>(scaler_stages[i].dst_size.height());
    296 
    297       int x_samples = 0;
    298       int y_samples = 0;
    299 
    300       // Codify valid scale operations.
    301       switch (scaler_stages[i].shader) {
    302         case GLHelperScaling::SHADER_PLANAR:
    303         case GLHelperScaling::SHADER_YUV_MRT_PASS1:
    304         case GLHelperScaling::SHADER_YUV_MRT_PASS2:
    305           EXPECT_TRUE(false) << "Invalid shader.";
    306           break;
    307 
    308         case GLHelperScaling::SHADER_BILINEAR:
    309           if (quality != content::GLHelper::SCALER_QUALITY_FAST) {
    310             x_samples = 1;
    311             y_samples = 1;
    312           }
    313           break;
    314         case GLHelperScaling::SHADER_BILINEAR2:
    315           x_samples = 2;
    316           y_samples = 1;
    317           break;
    318         case GLHelperScaling::SHADER_BILINEAR3:
    319           x_samples = 3;
    320           y_samples = 1;
    321           break;
    322         case GLHelperScaling::SHADER_BILINEAR4:
    323           x_samples = 4;
    324           y_samples = 1;
    325           break;
    326         case GLHelperScaling::SHADER_BILINEAR2X2:
    327           x_samples = 2;
    328           y_samples = 2;
    329           break;
    330         case GLHelperScaling::SHADER_BICUBIC_UPSCALE:
    331           if (scaler_stages[i].scale_x) {
    332             EXPECT_LT(x_scale, 1.0);
    333             EXPECT_EQ(y_scale, 1.0);
    334           } else {
    335             EXPECT_EQ(x_scale, 1.0);
    336             EXPECT_LT(y_scale, 1.0);
    337           }
    338           break;
    339         case GLHelperScaling::SHADER_BICUBIC_HALF_1D:
    340           if (scaler_stages[i].scale_x) {
    341             EXPECT_EQ(x_scale, 2.0);
    342             EXPECT_EQ(y_scale, 1.0);
    343           } else {
    344             EXPECT_EQ(x_scale, 1.0);
    345             EXPECT_EQ(y_scale, 2.0);
    346           }
    347           break;
    348       }
    349 
    350       if (!scaler_stages[i].scale_x) {
    351         std::swap(x_samples, y_samples);
    352       }
    353 
    354       if (x_samples) {
    355         EXPECT_TRUE(CheckScale(x_scale, x_samples, scaled_x))
    356             << "x_scale = " << x_scale;
    357       }
    358       if (y_samples) {
    359         EXPECT_TRUE(CheckScale(y_scale, y_samples, scaled_y))
    360             << "y_scale = " << y_scale;
    361       }
    362 
    363       if (x_scale != 1.0) {
    364         scaled_x = true;
    365       }
    366       if (y_scale != 1.0) {
    367         scaled_y = true;
    368       }
    369     }
    370 
    371     if (HasFailure() && !previous_error) {
    372       printf("Invalid scaler stages: %s\n", message.c_str());
    373       printf("Scaler stages:\n%s", PrintStages(scaler_stages).c_str());
    374     }
    375   }
    376 
    377   // Compare two bitmaps, make sure that each component of each pixel
    378   // is no more than |maxdiff| apart. If they are not similar enough,
    379   // prints out |truth|, |other|, |source|, |scaler_stages| and |message|.
    380   void Compare(SkBitmap* truth,
    381                SkBitmap* other,
    382                int maxdiff,
    383                SkBitmap* source,
    384                const std::vector<GLHelperScaling::ScalerStage> &scaler_stages,
    385                std::string message) {
    386     EXPECT_EQ(truth->width(), other->width());
    387     EXPECT_EQ(truth->height(), other->height());
    388     for (int x = 0; x < truth->width(); x++) {
    389       for (int y = 0; y < truth->height(); y++) {
    390         for (int c = 0; c < 4; c++) {
    391           int a = Channel(truth, x, y, c);
    392           int b = Channel(other, x, y, c);
    393           EXPECT_NEAR(a, b, maxdiff)
    394               << " x=" << x
    395               << " y=" << y
    396               << " c=" << c
    397               << " " << message;
    398           if (std::abs(a - b) > maxdiff) {
    399             printf("-------expected--------\n");
    400             PrintChannel(truth, c);
    401             printf("-------actual--------\n");
    402             PrintChannel(other, c);
    403             if (source) {
    404               printf("-------before scaling--------\n");
    405               PrintChannel(source, c);
    406             }
    407             printf("-----Scaler stages------\n%s",
    408                    PrintStages(scaler_stages).c_str());
    409             return;
    410           }
    411         }
    412       }
    413     }
    414   }
    415 
    416   // Get a single R, G, B or A value as a float.
    417   float ChannelAsFloat(SkBitmap* pixels, int x, int y, int c) {
    418     return Channel(pixels, x, y, c) / 255.0;
    419   }
    420 
    421   // Works like a GL_LINEAR lookup on an SkBitmap.
    422   float Bilinear(SkBitmap* pixels, float x, float y, int c) {
    423     x -= 0.5;
    424     y -= 0.5;
    425     int base_x = static_cast<int>(floorf(x));
    426     int base_y = static_cast<int>(floorf(y));
    427     x -= base_x;
    428     y -= base_y;
    429     return (ChannelAsFloat(pixels, base_x, base_y, c) * (1 - x) * (1 - y) +
    430             ChannelAsFloat(pixels, base_x + 1, base_y, c) * x * (1 - y) +
    431             ChannelAsFloat(pixels, base_x, base_y + 1, c) * (1 - x) * y +
    432             ChannelAsFloat(pixels, base_x + 1, base_y + 1, c) * x * y);
    433   }
    434 
    435   // Very slow bicubic / bilinear scaler for reference.
    436   void ScaleSlow(SkBitmap* input,
    437                  SkBitmap* output,
    438                  content::GLHelper::ScalerQuality quality) {
    439     float xscale = static_cast<float>(input->width()) / output->width();
    440     float yscale = static_cast<float>(input->height()) / output->height();
    441     float clamped_xscale = xscale < 1.0 ? 1.0 : 1.0 / xscale;
    442     float clamped_yscale = yscale < 1.0 ? 1.0 : 1.0 / yscale;
    443     for (int dst_y = 0; dst_y < output->height(); dst_y++) {
    444       for (int dst_x = 0; dst_x < output->width(); dst_x++) {
    445         for (int channel = 0; channel < 4; channel++) {
    446           float dst_x_in_src = (dst_x + 0.5f) * xscale;
    447           float dst_y_in_src = (dst_y + 0.5f) * yscale;
    448 
    449           float value = 0.0f;
    450           float sum = 0.0f;
    451           switch (quality) {
    452             case content::GLHelper::SCALER_QUALITY_BEST:
    453               for (int src_y = -10; src_y < input->height() + 10; ++src_y) {
    454                 float coeff_y = Bicubic(
    455                     (src_y + 0.5f - dst_y_in_src) * clamped_yscale);
    456                 if (coeff_y == 0.0f) {
    457                   continue;
    458                 }
    459                 for (int src_x = -10; src_x < input->width() + 10; ++src_x) {
    460                   float coeff = coeff_y * Bicubic(
    461                       (src_x + 0.5f - dst_x_in_src) * clamped_xscale);
    462                   if (coeff == 0.0f) {
    463                     continue;
    464                   }
    465                   sum += coeff;
    466                   float c = ChannelAsFloat(input, src_x, src_y, channel);
    467                   value += c * coeff;
    468                 }
    469               }
    470               break;
    471 
    472             case content::GLHelper::SCALER_QUALITY_GOOD: {
    473               int xshift = 0, yshift = 0;
    474               while ((output->width() << xshift) < input->width()) {
    475                 xshift++;
    476               }
    477               while ((output->height() << yshift) < input->height()) {
    478                 yshift++;
    479               }
    480               int xmag = 1 << xshift;
    481               int ymag = 1 << yshift;
    482               if (xmag == 4 && output->width() * 3 >= input->width()) {
    483                 xmag=3;
    484               }
    485               if (ymag == 4 && output->height() * 3 >= input->height()) {
    486                 ymag=3;
    487               }
    488               for (int x = 0; x < xmag; x++) {
    489                 for (int y = 0; y < ymag; y++) {
    490                   value += Bilinear(input,
    491                                     (dst_x * xmag + x + 0.5) * xscale / xmag,
    492                                     (dst_y * ymag + y + 0.5) * yscale / ymag,
    493                       channel);
    494                   sum += 1.0;
    495                 }
    496               }
    497               break;
    498             }
    499 
    500             case content::GLHelper::SCALER_QUALITY_FAST:
    501               value = Bilinear(input, dst_x_in_src, dst_y_in_src, channel);
    502               sum = 1.0;
    503           }
    504           value /= sum;
    505           SetChannel(output, dst_x, dst_y, channel,
    506                      static_cast<int>(value * 255.0f + 0.5f));
    507         }
    508       }
    509     }
    510   }
    511 
    512   void FlipSKBitmap(SkBitmap* bitmap) {
    513     int top_line = 0;
    514     int bottom_line = bitmap->height() - 1;
    515     while (top_line < bottom_line) {
    516       for (int x = 0; x < bitmap->width(); x++) {
    517         std::swap(*bitmap->getAddr32(x, top_line),
    518                   *bitmap->getAddr32(x, bottom_line));
    519       }
    520       top_line++;
    521       bottom_line--;
    522     }
    523   }
    524 
    525   // gl_helper scales recursively, so we'll need to do that
    526   // in the reference implementation too.
    527   void ScaleSlowRecursive(SkBitmap* input,
    528                           SkBitmap* output,
    529                           content::GLHelper::ScalerQuality quality) {
    530     if (quality == content::GLHelper::SCALER_QUALITY_FAST ||
    531         quality == content::GLHelper::SCALER_QUALITY_GOOD) {
    532       ScaleSlow(input, output, quality);
    533       return;
    534     }
    535 
    536     float xscale = static_cast<float>(output->width()) / input->width();
    537 
    538     // This corresponds to all the operations we can do directly.
    539     float yscale = static_cast<float>(output->height()) / input->height();
    540     if ((xscale == 1.0f && yscale == 1.0f) ||
    541         (xscale == 0.5f && yscale == 1.0f) ||
    542         (xscale == 1.0f && yscale == 0.5f) ||
    543         (xscale >= 1.0f && yscale == 1.0f) ||
    544         (xscale == 1.0f && yscale >= 1.0f)) {
    545       ScaleSlow(input, output, quality);
    546       return;
    547     }
    548 
    549     // Now we break the problem down into smaller pieces, using the
    550     // operations available.
    551     int xtmp = input->width();
    552     int ytmp = input->height();
    553 
    554     if (output->height() != input->height()) {
    555       ytmp = output->height();
    556       while (ytmp < input->height() && ytmp * 2 != input->height()) {
    557         ytmp += ytmp;
    558       }
    559     } else {
    560       xtmp = output->width();
    561       while (xtmp < input->width() && xtmp * 2 != input->width()) {
    562         xtmp += xtmp;
    563       }
    564     }
    565 
    566     SkBitmap tmp;
    567     tmp.setConfig(SkBitmap::kARGB_8888_Config, xtmp, ytmp);
    568     tmp.allocPixels();
    569     SkAutoLockPixels lock(tmp);
    570 
    571     ScaleSlowRecursive(input, &tmp, quality);
    572     ScaleSlowRecursive(&tmp, output, quality);
    573   }
    574 
    575   // Scaling test: Create a test image, scale it using GLHelperScaling
    576   // and a reference implementation and compare the results.
    577   void TestScale(int xsize, int ysize,
    578                  int scaled_xsize, int scaled_ysize,
    579                  int test_pattern,
    580                  size_t quality,
    581                  bool flip) {
    582     WebGLId src_texture = context_->createTexture();
    583     WebGLId framebuffer = context_->createFramebuffer();
    584     SkBitmap input_pixels;
    585     input_pixels.setConfig(SkBitmap::kARGB_8888_Config, xsize, ysize);
    586     input_pixels.allocPixels();
    587     SkAutoLockPixels lock(input_pixels);
    588 
    589     for (int x = 0; x < xsize; ++x) {
    590       for (int y = 0; y < ysize; ++y) {
    591         switch (test_pattern) {
    592           case 0:  // Smooth test pattern
    593             SetChannel(&input_pixels, x, y, 0, x * 10);
    594             SetChannel(&input_pixels, x, y, 1, y * 10);
    595             SetChannel(&input_pixels, x, y, 2, (x + y) * 10);
    596             SetChannel(&input_pixels, x, y, 3, 255);
    597             break;
    598           case 1:  // Small blocks
    599             SetChannel(&input_pixels, x, y, 0, x & 1 ? 255 : 0);
    600             SetChannel(&input_pixels, x, y, 1, y & 1 ? 255 : 0);
    601             SetChannel(&input_pixels, x, y, 2, (x + y) & 1 ? 255 : 0);
    602             SetChannel(&input_pixels, x, y, 3, 255);
    603             break;
    604           case 2:  // Medium blocks
    605             SetChannel(&input_pixels, x, y, 0, 10 + x/2 * 50);
    606             SetChannel(&input_pixels, x, y, 1, 10 + y/3 * 50);
    607             SetChannel(&input_pixels, x, y, 2, (x + y)/5 * 50 + 5);
    608             SetChannel(&input_pixels, x, y, 3, 255);
    609             break;
    610         }
    611       }
    612     }
    613 
    614     context_->bindFramebuffer(GL_FRAMEBUFFER, framebuffer);
    615     context_->bindTexture(GL_TEXTURE_2D, src_texture);
    616     context_->texImage2D(GL_TEXTURE_2D,
    617                          0,
    618                          GL_RGBA,
    619                          xsize,
    620                          ysize,
    621                          0,
    622                          GL_RGBA,
    623                          GL_UNSIGNED_BYTE,
    624                          input_pixels.getPixels());
    625 
    626     std::string message = base::StringPrintf("input size: %dx%d "
    627                                              "output size: %dx%d "
    628                                              "pattern: %d quality: %s",
    629                                              xsize, ysize,
    630                                              scaled_xsize, scaled_ysize,
    631                                              test_pattern,
    632                                              kQualityNames[quality]);
    633 
    634 
    635     std::vector<GLHelperScaling::ScalerStage> stages;
    636     helper_scaling_->ComputeScalerStages(
    637         kQualities[quality],
    638         gfx::Size(xsize, ysize),
    639         gfx::Rect(0, 0, xsize, ysize),
    640         gfx::Size(scaled_xsize, scaled_ysize),
    641         flip,
    642         false,
    643         &stages);
    644     ValidateScalerStages(kQualities[quality], stages, message);
    645 
    646     WebGLId dst_texture = helper_->CopyAndScaleTexture(
    647         src_texture,
    648         gfx::Size(xsize, ysize),
    649         gfx::Size(scaled_xsize, scaled_ysize),
    650         flip,
    651         kQualities[quality]);
    652 
    653     SkBitmap output_pixels;
    654     output_pixels.setConfig(SkBitmap::kARGB_8888_Config,
    655                             scaled_xsize, scaled_ysize);
    656     output_pixels.allocPixels();
    657     SkAutoLockPixels output_lock(output_pixels);
    658 
    659     helper_->ReadbackTextureSync(
    660         dst_texture,
    661         gfx::Rect(0, 0, scaled_xsize, scaled_ysize),
    662         static_cast<unsigned char *>(output_pixels.getPixels()));
    663     if (flip) {
    664       // Flip the pixels back.
    665       FlipSKBitmap(&output_pixels);
    666     }
    667     if (xsize == scaled_xsize && ysize == scaled_ysize) {
    668       Compare(&input_pixels,
    669               &output_pixels,
    670               2,
    671               NULL,
    672               stages,
    673               message + " comparing against input");
    674     }
    675     SkBitmap truth_pixels;
    676     truth_pixels.setConfig(SkBitmap::kARGB_8888_Config,
    677                            scaled_xsize, scaled_ysize);
    678     truth_pixels.allocPixels();
    679     SkAutoLockPixels truth_lock(truth_pixels);
    680 
    681     ScaleSlowRecursive(&input_pixels, &truth_pixels, kQualities[quality]);
    682     Compare(&truth_pixels,
    683             &output_pixels,
    684             2,
    685             &input_pixels,
    686             stages,
    687             message + " comparing against scaled");
    688 
    689     context_->deleteTexture(src_texture);
    690     context_->deleteTexture(dst_texture);
    691     context_->deleteFramebuffer(framebuffer);
    692   }
    693 
    694   // Create a scaling pipeline and check that it is made up of
    695   // valid scaling operations.
    696   void TestScalerPipeline(size_t quality,
    697                           int xsize, int ysize,
    698                           int dst_xsize, int dst_ysize) {
    699     std::vector<GLHelperScaling::ScalerStage> stages;
    700     helper_scaling_->ComputeScalerStages(
    701         kQualities[quality],
    702         gfx::Size(xsize, ysize),
    703         gfx::Rect(0, 0, xsize, ysize),
    704         gfx::Size(dst_xsize, dst_ysize),
    705         false,
    706         false,
    707         &stages);
    708     ValidateScalerStages(kQualities[quality], stages,
    709                          base::StringPrintf("input size: %dx%d "
    710                                             "output size: %dx%d "
    711                                             "quality: %s",
    712                                             xsize, ysize,
    713                                             dst_xsize, dst_ysize,
    714                                             kQualityNames[quality]));
    715   }
    716 
    717   // Create a scaling pipeline and make sure that the steps
    718   // are exactly the steps we expect.
    719   void CheckPipeline(content::GLHelper::ScalerQuality quality,
    720                      int xsize, int ysize,
    721                      int dst_xsize, int dst_ysize,
    722                      const std::string &description) {
    723     std::vector<GLHelperScaling::ScalerStage> stages;
    724     helper_scaling_->ComputeScalerStages(
    725         quality,
    726         gfx::Size(xsize, ysize),
    727         gfx::Rect(0, 0, xsize, ysize),
    728         gfx::Size(dst_xsize, dst_ysize),
    729         false,
    730         false,
    731         &stages);
    732     ValidateScalerStages(
    733         content::GLHelper::SCALER_QUALITY_GOOD,
    734         stages,
    735         "");
    736     EXPECT_EQ(PrintStages(stages), description);
    737   }
    738 
    739   // Note: Left/Right means Top/Bottom when used for Y dimension.
    740   enum Margin {
    741     MarginLeft,
    742     MarginMiddle,
    743     MarginRight,
    744     MarginInvalid,
    745   };
    746 
    747   static Margin NextMargin(Margin m) {
    748     switch (m) {
    749       case MarginLeft:
    750         return MarginMiddle;
    751       case MarginMiddle:
    752         return MarginRight;
    753       case MarginRight:
    754         return MarginInvalid;
    755       default:
    756         return MarginInvalid;
    757     }
    758   }
    759 
    760   int compute_margin(int insize, int outsize, Margin m) {
    761     int available = outsize - insize;
    762     switch (m) {
    763       default:
    764         EXPECT_TRUE(false) << "This should not happen.";
    765         return 0;
    766       case MarginLeft:
    767         return 0;
    768       case MarginMiddle:
    769         return (available / 2) & ~1;
    770       case MarginRight:
    771         return available;
    772     }
    773   }
    774 
    775   // Convert 0.0 - 1.0 to 0 - 255
    776   int float_to_byte(float v) {
    777     int ret = static_cast<int>(floorf(v * 255.0f + 0.5f));
    778     if (ret < 0) {
    779       return 0;
    780     }
    781     if (ret > 255) {
    782       return 255;
    783     }
    784     return ret;
    785   }
    786 
    787   static void callcallback(const base::Callback<void()>& callback,
    788                            bool result) {
    789     callback.Run();
    790   }
    791 
    792   void PrintPlane(unsigned char *plane, int xsize, int stride, int ysize) {
    793     for (int y = 0; y < ysize; y++) {
    794       for (int x = 0; x < xsize ; x++) {
    795         printf("%3d, ", plane[y * stride + x]);
    796       }
    797       printf("   (%p)\n", plane + y * stride);
    798     }
    799   }
    800 
    801   // Compare two planes make sure that each component of each pixel
    802   // is no more than |maxdiff| apart.
    803   void ComparePlane(unsigned char* truth,
    804                     unsigned char* other,
    805                     int maxdiff,
    806                     int xsize,
    807                     int stride,
    808                     int ysize,
    809                     SkBitmap* source,
    810                     std::string message) {
    811     int truth_stride = stride;
    812     for (int x = 0; x < xsize; x++) {
    813       for (int y = 0; y < ysize; y++) {
    814         int a = other[y * stride + x];
    815         int b = truth[y * stride + x];
    816         EXPECT_NEAR(a, b, maxdiff)
    817             << " x=" << x
    818             << " y=" << y
    819             << " " << message;
    820         if (std::abs(a - b) > maxdiff) {
    821           printf("-------expected--------\n");
    822           PrintPlane(truth, xsize, truth_stride, ysize);
    823           printf("-------actual--------\n");
    824           PrintPlane(other, xsize, stride, ysize);
    825           if (source) {
    826             printf("-------before yuv conversion: red--------\n");
    827             PrintChannel(source, 0);
    828             printf("-------before yuv conversion: green------\n");
    829             PrintChannel(source, 1);
    830             printf("-------before yuv conversion: blue-------\n");
    831             PrintChannel(source, 2);
    832           }
    833           return;
    834         }
    835       }
    836     }
    837   }
    838 
    839   // YUV readback test. Create a test pattern, convert to YUV
    840   // with reference implementation and compare to what gl_helper
    841   // returns.
    842   void TestYUVReadback(int xsize,
    843                        int ysize,
    844                        int output_xsize,
    845                        int output_ysize,
    846                        int xmargin,
    847                        int ymargin,
    848                        int test_pattern,
    849                        bool flip,
    850                        bool use_mrt,
    851                        content::GLHelper::ScalerQuality quality) {
    852     WebGLId src_texture = context_->createTexture();
    853     SkBitmap input_pixels;
    854     input_pixels.setConfig(SkBitmap::kARGB_8888_Config, xsize, ysize);
    855     input_pixels.allocPixels();
    856     SkAutoLockPixels lock(input_pixels);
    857 
    858     for (int x = 0; x < xsize; ++x) {
    859       for (int y = 0; y < ysize; ++y) {
    860         switch (test_pattern) {
    861           case 0:  // Smooth test pattern
    862             SetChannel(&input_pixels, x, y, 0, x * 10);
    863             SetChannel(&input_pixels, x, y, 1, y * 10);
    864             SetChannel(&input_pixels, x, y, 2, (x + y) * 10);
    865             SetChannel(&input_pixels, x, y, 3, 255);
    866             break;
    867           case 1:  // Small blocks
    868             SetChannel(&input_pixels, x, y, 0, x & 1 ? 255 : 0);
    869             SetChannel(&input_pixels, x, y, 1, y & 1 ? 255 : 0);
    870             SetChannel(&input_pixels, x, y, 2, (x + y) & 1 ? 255 : 0);
    871             SetChannel(&input_pixels, x, y, 3, 255);
    872             break;
    873           case 2:  // Medium blocks
    874             SetChannel(&input_pixels, x, y, 0, 10 + x/2 * 50);
    875             SetChannel(&input_pixels, x, y, 1, 10 + y/3 * 50);
    876             SetChannel(&input_pixels, x, y, 2, (x + y)/5 * 50 + 5);
    877             SetChannel(&input_pixels, x, y, 3, 255);
    878             break;
    879         }
    880       }
    881     }
    882 
    883     context_->bindTexture(GL_TEXTURE_2D, src_texture);
    884     context_->texImage2D(GL_TEXTURE_2D,
    885                          0,
    886                          GL_RGBA,
    887                          xsize,
    888                          ysize,
    889                          0,
    890                          GL_RGBA,
    891                          GL_UNSIGNED_BYTE,
    892                          input_pixels.getPixels());
    893 
    894     gpu::Mailbox mailbox;
    895     context_->genMailboxCHROMIUM(mailbox.name);
    896     EXPECT_FALSE(mailbox.IsZero());
    897     context_->produceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
    898     uint32 sync_point = context_->insertSyncPoint();
    899 
    900     std::string message = base::StringPrintf("input size: %dx%d "
    901                                              "output size: %dx%d "
    902                                              "margin: %dx%d "
    903                                              "pattern: %d %s %s",
    904                                              xsize, ysize,
    905                                              output_xsize, output_ysize,
    906                                              xmargin, ymargin,
    907                                              test_pattern,
    908                                              flip ? "flip" : "noflip",
    909                                              flip ? "mrt" : "nomrt");
    910     scoped_ptr<ReadbackYUVInterface> yuv_reader(
    911         helper_->CreateReadbackPipelineYUV(
    912             quality,
    913             gfx::Size(xsize, ysize),
    914             gfx::Rect(0, 0, xsize, ysize),
    915             gfx::Size(output_xsize, output_ysize),
    916             gfx::Rect(xmargin, ymargin, xsize, ysize),
    917             flip,
    918             use_mrt));
    919 
    920     scoped_refptr<media::VideoFrame> output_frame =
    921         media::VideoFrame::CreateFrame(
    922             media::VideoFrame::YV12,
    923             gfx::Size(output_xsize, output_ysize),
    924             gfx::Rect(0, 0, output_xsize, output_ysize),
    925             gfx::Size(output_xsize, output_ysize),
    926             base::TimeDelta::FromSeconds(0));
    927     scoped_refptr<media::VideoFrame> truth_frame =
    928         media::VideoFrame::CreateFrame(
    929             media::VideoFrame::YV12,
    930             gfx::Size(output_xsize, output_ysize),
    931             gfx::Rect(0, 0, output_xsize, output_ysize),
    932             gfx::Size(output_xsize, output_ysize),
    933             base::TimeDelta::FromSeconds(0));
    934 
    935     base::RunLoop run_loop;
    936     yuv_reader->ReadbackYUV(
    937         mailbox,
    938         sync_point,
    939         output_frame.get(),
    940         base::Bind(&callcallback, run_loop.QuitClosure()));
    941     run_loop.Run();
    942 
    943     if (flip) {
    944       FlipSKBitmap(&input_pixels);
    945     }
    946 
    947     unsigned char* Y = truth_frame->data(media::VideoFrame::kYPlane);
    948     unsigned char* U = truth_frame->data(media::VideoFrame::kUPlane);
    949     unsigned char* V = truth_frame->data(media::VideoFrame::kVPlane);
    950     int32 y_stride = truth_frame->stride(media::VideoFrame::kYPlane);
    951     int32 u_stride = truth_frame->stride(media::VideoFrame::kUPlane);
    952     int32 v_stride = truth_frame->stride(media::VideoFrame::kVPlane);
    953     memset(Y, 0x00, y_stride * output_ysize);
    954     memset(U, 0x80, u_stride * output_ysize / 2);
    955     memset(V, 0x80, v_stride * output_ysize / 2);
    956 
    957     for (int y = 0; y < ysize; y++) {
    958       for (int x = 0; x < xsize; x++) {
    959         Y[(y + ymargin) * y_stride + x + xmargin] = float_to_byte(
    960             ChannelAsFloat(&input_pixels, x, y, 0) * 0.257 +
    961             ChannelAsFloat(&input_pixels, x, y, 1) * 0.504 +
    962             ChannelAsFloat(&input_pixels, x, y, 2) * 0.098 +
    963             0.0625);
    964       }
    965     }
    966 
    967     for (int y = 0; y < ysize / 2; y++) {
    968       for (int x = 0; x < xsize / 2; x++) {
    969         U[(y + ymargin / 2) * u_stride + x + xmargin / 2] =
    970             float_to_byte(
    971                 Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 0) * -0.148 +
    972                 Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 1) * -0.291 +
    973                 Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 2) * 0.439 +
    974                 0.5);
    975         V[(y + ymargin / 2) * v_stride + x + xmargin / 2] =
    976             float_to_byte(
    977                 Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 0) * 0.439 +
    978                 Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 1) * -0.368 +
    979                 Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 2) * -0.071 +
    980                 0.5);
    981       }
    982     }
    983 
    984     ComparePlane(Y,
    985                  output_frame->data(media::VideoFrame::kYPlane), 2,
    986                  output_xsize,
    987                  y_stride,
    988                  output_ysize,
    989                  &input_pixels,
    990                  message + " Y plane");
    991     ComparePlane(U,
    992                  output_frame->data(media::VideoFrame::kUPlane), 2,
    993                  output_xsize / 2,
    994                  u_stride,
    995                  output_ysize / 2,
    996                  &input_pixels,
    997                  message + " U plane");
    998     ComparePlane(V,
    999                  output_frame->data(media::VideoFrame::kVPlane), 2,
   1000                  output_xsize / 2,
   1001                  v_stride,
   1002                  output_ysize / 2,
   1003                  &input_pixels,
   1004                  message + " V plane");
   1005 
   1006     context_->deleteTexture(src_texture);
   1007   }
   1008 
   1009   void TestAddOps(int src,
   1010                   int dst,
   1011                   bool scale_x,
   1012                   bool allow3) {
   1013     std::deque<GLHelperScaling::ScaleOp> ops;
   1014     GLHelperScaling::ScaleOp::AddOps(src, dst, scale_x, allow3, &ops);
   1015     // Scale factor 3 is a special case.
   1016     // It is currently only allowed by itself.
   1017     if (allow3 && dst * 3 >= src && dst * 2 < src) {
   1018       EXPECT_EQ(ops[0].scale_factor, 3);
   1019       EXPECT_EQ(ops.size(), 1U);
   1020       EXPECT_EQ(ops[0].scale_x, scale_x);
   1021       EXPECT_EQ(ops[0].scale_size, dst);
   1022       return;
   1023     }
   1024 
   1025     for (size_t i = 0; i < ops.size(); i++) {
   1026       EXPECT_EQ(ops[i].scale_x, scale_x);
   1027       if (i == 0) {
   1028         // Only the first op is allowed to be a scale up.
   1029         // (Scaling up *after* scaling down would make it fuzzy.)
   1030         EXPECT_TRUE(ops[0].scale_factor == 0 ||
   1031                     ops[0].scale_factor == 2);
   1032       } else {
   1033         // All other operations must be 50% downscales.
   1034         EXPECT_EQ(ops[i].scale_factor, 2);
   1035       }
   1036     }
   1037     // Check that the scale factors make sense and add up.
   1038     int tmp = dst;
   1039     for (int i = static_cast<int>(ops.size() - 1); i >= 0; i--) {
   1040       EXPECT_EQ(tmp, ops[i].scale_size);
   1041       if (ops[i].scale_factor == 0) {
   1042         EXPECT_EQ(i, 0);
   1043         EXPECT_GT(tmp, src);
   1044         tmp = src;
   1045       } else {
   1046         tmp *= ops[i].scale_factor;
   1047       }
   1048     }
   1049     EXPECT_EQ(tmp, src);
   1050   }
   1051 
   1052   void CheckPipeline2(int xsize, int ysize,
   1053                       int dst_xsize, int dst_ysize,
   1054                       const std::string &description) {
   1055     std::vector<GLHelperScaling::ScalerStage> stages;
   1056     helper_scaling_->ConvertScalerOpsToScalerStages(
   1057         content::GLHelper::SCALER_QUALITY_GOOD,
   1058         gfx::Size(xsize, ysize),
   1059         gfx::Rect(0, 0, xsize, ysize),
   1060         gfx::Size(dst_xsize, dst_ysize),
   1061         false,
   1062         false,
   1063         &x_ops_,
   1064         &y_ops_,
   1065         &stages);
   1066     EXPECT_EQ(x_ops_.size(), 0U);
   1067     EXPECT_EQ(y_ops_.size(), 0U);
   1068     ValidateScalerStages(
   1069         content::GLHelper::SCALER_QUALITY_GOOD,
   1070         stages,
   1071         "");
   1072     EXPECT_EQ(PrintStages(stages), description);
   1073   }
   1074 
   1075   void CheckOptimizationsTest() {
   1076     // Basic upscale. X and Y should be combined into one pass.
   1077     x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 2000));
   1078     y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 2000));
   1079     CheckPipeline2(1024, 768, 2000, 2000,
   1080                    "1024x768 -> 2000x2000 bilinear\n");
   1081 
   1082     // X scaled 1/2, Y upscaled, should still be one pass.
   1083     x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 512));
   1084     y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 2000));
   1085     CheckPipeline2(1024, 768, 512, 2000,
   1086                    "1024x768 -> 512x2000 bilinear\n");
   1087 
   1088     // X upscaled, Y scaled 1/2, one bilinear pass
   1089     x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 2000));
   1090     y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 384));
   1091     CheckPipeline2(1024, 768, 2000, 384,
   1092                    "1024x768 -> 2000x384 bilinear\n");
   1093 
   1094     // X scaled 1/2, Y scaled 1/2, one bilinear pass
   1095     x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 512));
   1096     y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 384));
   1097     CheckPipeline2(1024, 768, 2000, 384,
   1098                    "1024x768 -> 512x384 bilinear\n");
   1099 
   1100     // X scaled 1/2, Y scaled to 60%, one bilinear2 pass.
   1101     x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 50));
   1102     y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 120));
   1103     y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 60));
   1104     CheckPipeline2(100, 100, 50, 60,
   1105                    "100x100 -> 50x60 bilinear2 Y\n");
   1106 
   1107     // X scaled to 60%, Y scaled 1/2, one bilinear2 pass.
   1108     x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 120));
   1109     x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 60));
   1110     y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 50));
   1111     CheckPipeline2(100, 100, 50, 60,
   1112                    "100x100 -> 60x50 bilinear2 X\n");
   1113 
   1114     // X scaled to 60%, Y scaled 60%, one bilinear2x2 pass.
   1115     x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 120));
   1116     x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 60));
   1117     y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 120));
   1118     y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 60));
   1119     CheckPipeline2(100, 100, 60, 60,
   1120                    "100x100 -> 60x60 bilinear2x2\n");
   1121 
   1122     // X scaled to 40%, Y scaled 40%, two bilinear3 passes.
   1123     x_ops_.push_back(GLHelperScaling::ScaleOp(3, true, 40));
   1124     y_ops_.push_back(GLHelperScaling::ScaleOp(3, false, 40));
   1125     CheckPipeline2(100, 100, 40, 40,
   1126                    "100x100 -> 100x40 bilinear3 Y\n"
   1127                    "100x40 -> 40x40 bilinear3 X\n");
   1128 
   1129     // X scaled to 60%, Y scaled 40%
   1130     x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 120));
   1131     x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 60));
   1132     y_ops_.push_back(GLHelperScaling::ScaleOp(3, false, 40));
   1133     CheckPipeline2(100, 100, 60, 40,
   1134                    "100x100 -> 100x40 bilinear3 Y\n"
   1135                    "100x40 -> 60x40 bilinear2 X\n");
   1136 
   1137     // X scaled to 40%, Y scaled 60%
   1138     x_ops_.push_back(GLHelperScaling::ScaleOp(3, true, 40));
   1139     y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 120));
   1140     y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 60));
   1141     CheckPipeline2(100, 100, 40, 60,
   1142                    "100x100 -> 100x60 bilinear2 Y\n"
   1143                    "100x60 -> 40x60 bilinear3 X\n");
   1144 
   1145     // X scaled to 30%, Y scaled 30%
   1146     x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 120));
   1147     x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 60));
   1148     x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 30));
   1149     y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 120));
   1150     y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 60));
   1151     y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 30));
   1152     CheckPipeline2(100, 100, 30, 30,
   1153                    "100x100 -> 100x30 bilinear4 Y\n"
   1154                    "100x30 -> 30x30 bilinear4 X\n");
   1155 
   1156     // X scaled to 50%, Y scaled 30%
   1157     x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 50));
   1158     y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 120));
   1159     y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 60));
   1160     y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 30));
   1161     CheckPipeline2(100, 100, 50, 30,
   1162                    "100x100 -> 50x30 bilinear4 Y\n");
   1163 
   1164     // X scaled to 150%, Y scaled 30%
   1165     // Note that we avoid combinding X and Y passes
   1166     // as that would probably be LESS efficient here.
   1167     x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 150));
   1168     y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 120));
   1169     y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 60));
   1170     y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 30));
   1171     CheckPipeline2(100, 100, 150, 30,
   1172                    "100x100 -> 100x30 bilinear4 Y\n"
   1173                    "100x30 -> 150x30 bilinear\n");
   1174 
   1175     // X scaled to 1%, Y scaled 1%
   1176     x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 128));
   1177     x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 64));
   1178     x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 32));
   1179     x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 16));
   1180     x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 8));
   1181     x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 4));
   1182     x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 2));
   1183     x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 1));
   1184     y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 128));
   1185     y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 64));
   1186     y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 32));
   1187     y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 16));
   1188     y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 8));
   1189     y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 4));
   1190     y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 2));
   1191     y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 1));
   1192     CheckPipeline2(100, 100, 30, 30,
   1193                    "100x100 -> 100x32 bilinear4 Y\n"
   1194                    "100x32 -> 100x4 bilinear4 Y\n"
   1195                    "100x4 -> 64x1 bilinear2x2\n"
   1196                    "64x1 -> 8x1 bilinear4 X\n"
   1197                    "8x1 -> 1x1 bilinear4 X\n");
   1198   }
   1199 
   1200   scoped_ptr<WebGraphicsContext3DInProcessCommandBufferImpl> context_;
   1201   gpu::ContextSupport* context_support_;
   1202   scoped_ptr<content::GLHelper> helper_;
   1203   scoped_ptr<content::GLHelperScaling> helper_scaling_;
   1204   std::deque<GLHelperScaling::ScaleOp> x_ops_, y_ops_;
   1205 };
   1206 
   1207 TEST_F(GLHelperTest, YUVReadbackOptTest) {
   1208   // This test uses the cb_command tracing events to detect how many
   1209   // scaling passes are actually performed by the YUV readback pipeline.
   1210   StartTracing(TRACE_DISABLED_BY_DEFAULT("cb_command"));
   1211 
   1212   TestYUVReadback(
   1213       800, 400,
   1214       800, 400,
   1215       0,  0,
   1216       1,
   1217       false,
   1218       true,
   1219       content::GLHelper::SCALER_QUALITY_FAST);
   1220 
   1221   std::map<std::string, int> event_counts;
   1222   EndTracing(&event_counts);
   1223   int draw_buffer_calls = event_counts["kDrawBuffersEXTImmediate"];
   1224   int draw_arrays_calls = event_counts["kDrawArrays"];
   1225   VLOG(1) << "Draw buffer calls: " << draw_buffer_calls;
   1226   VLOG(1) << "DrawArrays calls: " << draw_arrays_calls;
   1227 
   1228   if (draw_buffer_calls) {
   1229     // When using MRT, the YUV readback code should only
   1230     // execute two draw arrays, and scaling should be integrated
   1231     // into those two calls since we are using the FAST scalign
   1232     // quality.
   1233     EXPECT_EQ(2, draw_arrays_calls);
   1234   } else {
   1235     // When not using MRT, there are three passes for the YUV,
   1236     // and one for the scaling.
   1237     EXPECT_EQ(4, draw_arrays_calls);
   1238   }
   1239 }
   1240 
   1241 TEST_F(GLHelperTest, YUVReadbackTest) {
   1242   int sizes[] = { 2, 4, 14 };
   1243   for (int flip = 0; flip <= 1; flip++) {
   1244     for (int use_mrt = 0; use_mrt <= 1; use_mrt++) {
   1245       for (unsigned int x = 0; x < arraysize(sizes); x++) {
   1246         for (unsigned int y = 0; y < arraysize(sizes); y++) {
   1247           for (unsigned int ox = x; ox < arraysize(sizes); ox++) {
   1248             for (unsigned int oy = y; oy < arraysize(sizes); oy++) {
   1249               // If output is a subsection of the destination frame, (letterbox)
   1250               // then try different variations of where the subsection goes.
   1251               for (Margin xm = x < ox ? MarginLeft : MarginRight;
   1252                    xm <= MarginRight;
   1253                    xm = NextMargin(xm)) {
   1254                 for (Margin ym = y < oy ? MarginLeft : MarginRight;
   1255                      ym <= MarginRight;
   1256                      ym = NextMargin(ym)) {
   1257                   for (int pattern = 0; pattern < 3; pattern++) {
   1258                     TestYUVReadback(
   1259                         sizes[x],
   1260                         sizes[y],
   1261                         sizes[ox],
   1262                         sizes[oy],
   1263                         compute_margin(sizes[x], sizes[ox], xm),
   1264                         compute_margin(sizes[y], sizes[oy], ym),
   1265                         pattern,
   1266                         flip == 1,
   1267                         use_mrt == 1,
   1268                         content::GLHelper::SCALER_QUALITY_GOOD);
   1269                     if (HasFailure()) {
   1270                       return;
   1271                     }
   1272                   }
   1273                 }
   1274               }
   1275             }
   1276           }
   1277         }
   1278       }
   1279     }
   1280   }
   1281 }
   1282 
   1283 // Per pixel tests, all sizes are small so that we can print
   1284 // out the generated bitmaps.
   1285 TEST_F(GLHelperTest, ScaleTest) {
   1286   int sizes[] = {3, 6, 16};
   1287   for (int flip = 0; flip <= 1; flip++) {
   1288     for (size_t q = 0; q < arraysize(kQualities); q++) {
   1289       for (int x = 0; x < 3; x++) {
   1290         for (int y = 0; y < 3; y++) {
   1291           for (int dst_x = 0; dst_x < 3; dst_x++) {
   1292             for (int dst_y = 0; dst_y < 3; dst_y++) {
   1293               for (int pattern = 0; pattern < 3; pattern++) {
   1294                 TestScale(sizes[x],
   1295                           sizes[y],
   1296                           sizes[dst_x],
   1297                           sizes[dst_y],
   1298                           pattern,
   1299                           q,
   1300                           flip == 1);
   1301                 if (HasFailure()) {
   1302                   return;
   1303                 }
   1304               }
   1305             }
   1306           }
   1307         }
   1308       }
   1309     }
   1310   }
   1311 }
   1312 
   1313 // Validate that all scaling generates valid pipelines.
   1314 TEST_F(GLHelperTest, ValidateScalerPipelines) {
   1315   int sizes[] = {7, 99, 128, 256, 512, 719, 720, 721, 1920, 2011, 3217, 4096};
   1316   for (size_t q = 0; q < arraysize(kQualities); q++) {
   1317     for (size_t x = 0; x < arraysize(sizes); x++) {
   1318       for (size_t y = 0; y < arraysize(sizes); y++) {
   1319         for (size_t dst_x = 0; dst_x < arraysize(sizes); dst_x++) {
   1320           for (size_t dst_y = 0; dst_y < arraysize(sizes); dst_y++) {
   1321             TestScalerPipeline(q,
   1322                                sizes[x], sizes[y],
   1323                                sizes[dst_x], sizes[dst_y]);
   1324             if (HasFailure()) {
   1325               return;
   1326             }
   1327           }
   1328         }
   1329       }
   1330     }
   1331   }
   1332 }
   1333 
   1334 // Make sure we don't create overly complicated pipelines
   1335 // for a few common use cases.
   1336 TEST_F(GLHelperTest, CheckSpecificPipelines) {
   1337   // Upscale should be single pass.
   1338   CheckPipeline(content::GLHelper::SCALER_QUALITY_GOOD,
   1339                 1024, 700, 1280, 720,
   1340                 "1024x700 -> 1280x720 bilinear\n");
   1341   // Slight downscale should use BILINEAR2X2.
   1342   CheckPipeline(content::GLHelper::SCALER_QUALITY_GOOD,
   1343                 1280, 720, 1024, 700,
   1344                 "1280x720 -> 1024x700 bilinear2x2\n");
   1345   // Most common tab capture pipeline on the Pixel.
   1346   // Should be using two BILINEAR3 passes.
   1347   CheckPipeline(content::GLHelper::SCALER_QUALITY_GOOD,
   1348                 2560, 1476, 1249, 720,
   1349                 "2560x1476 -> 2560x720 bilinear3 Y\n"
   1350                 "2560x720 -> 1249x720 bilinear3 X\n");
   1351 }
   1352 
   1353 TEST_F(GLHelperTest, ScalerOpTest) {
   1354   for (int allow3 = 0; allow3 <= 1; allow3++) {
   1355     for (int dst = 1; dst < 2049; dst += 1 + (dst >> 3)) {
   1356       for (int src = 1; src < 2049; src++) {
   1357         TestAddOps(src, dst, allow3 == 1, (src & 1) == 1);
   1358         if (HasFailure()) {
   1359           LOG(ERROR) << "Failed for src=" << src
   1360                      << " dst=" << dst
   1361                      << " allow3=" << allow3;
   1362           return;
   1363         }
   1364       }
   1365     }
   1366   }
   1367 }
   1368 
   1369 TEST_F(GLHelperTest, CheckOptimizations) {
   1370   // Test in baseclass since it is friends with GLHelperScaling
   1371   CheckOptimizationsTest();
   1372 }
   1373 
   1374 }  // namespace
   1375 
   1376 // These tests needs to run against a proper GL environment, so we
   1377 // need to set it up before we can run the tests.
   1378 int main(int argc, char** argv) {
   1379   CommandLine::Init(argc, argv);
   1380   base::TestSuite* suite = new content::ContentTestSuite(argc, argv);
   1381 #if defined(OS_MACOSX)
   1382   base::mac::ScopedNSAutoreleasePool pool;
   1383 #endif
   1384 #if defined(TOOLKIT_GTK)
   1385   gfx::GtkInitFromCommandLine(*CommandLine::ForCurrentProcess());
   1386 #endif
   1387   gfx::GLSurface::InitializeOneOff();
   1388   gpu::ApplyGpuDriverBugWorkarounds(CommandLine::ForCurrentProcess());
   1389 
   1390   content::UnitTestTestSuite runner(suite);
   1391   base::MessageLoop message_loop;
   1392   return runner.Run();
   1393 }
   1394