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