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