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.
10 #include <GLES2/gl2.h>
11 #include <GLES2/gl2ext.h>
12 #include <GLES2/gl2extchromium.h>
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/files/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"
37 #if defined(OS_MACOSX)
38 #include "base/mac/scoped_nsautorelease_pool.h"
44 using blink::WebGraphicsContext3D;
45 using webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl;
47 content::GLHelper::ScalerQuality kQualities[] = {
48 content::GLHelper::SCALER_QUALITY_BEST,
49 content::GLHelper::SCALER_QUALITY_GOOD,
50 content::GLHelper::SCALER_QUALITY_FAST, };
52 const char* kQualityNames[] = {"best", "good", "fast", };
54 class GLHelperTest : public testing::Test {
56 virtual void SetUp() {
57 WebGraphicsContext3D::Attributes attributes;
58 bool lose_context_when_out_of_memory = false;
60 WebGraphicsContext3DInProcessCommandBufferImpl::CreateOffscreenContext(
61 attributes, lose_context_when_out_of_memory);
62 context_->InitializeOnCurrentThread();
63 context_support_ = context_->GetContextSupport();
65 new content::GLHelper(context_->GetGLInterface(), context_support_));
66 helper_scaling_.reset(new content::GLHelperScaling(
67 context_->GetGLInterface(), helper_.get()));
70 virtual void TearDown() {
71 helper_scaling_.reset(NULL);
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));
84 static void TraceDataCB(
85 const base::Callback<void()>& callback,
87 const scoped_refptr<base::RefCountedString>& json_events_str,
88 bool has_more_events) {
89 if (output->size() > 1) {
92 output->append(json_events_str->data());
93 if (!has_more_events) {
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)));
109 json_data.append("]");
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));
120 CHECK(dict->GetString("name", &name));
121 std::string trace_type;
122 CHECK(dict->GetString("ph", &trace_type));
123 // Count all except END traces, as they come in BEGIN/END pairs.
124 if (trace_type != "E")
125 (*event_counts)[name]++;
126 VLOG(1) << "trace name: " << name;
131 // Bicubic filter kernel function.
132 static float Bicubic(float x) {
133 const float a = -0.5;
138 return (a + 2) * x3 - (a + 3) * x2 + 1;
140 return a * x3 - 5 * a * x2 + 8 * a * x - 4 * a;
146 // Look up a single channel value. Works for 4-channel and single channel
147 // bitmaps. Clamp x/y.
148 int Channel(SkBitmap* pixels, int x, int y, int c) {
149 if (pixels->bytesPerPixel() == 4) {
151 pixels->getAddr32(std::max(0, std::min(x, pixels->width() - 1)),
152 std::max(0, std::min(y, pixels->height() - 1)));
153 return (*data) >> (c * 8) & 0xff;
155 DCHECK_EQ(pixels->bytesPerPixel(), 1);
157 return *pixels->getAddr8(std::max(0, std::min(x, pixels->width() - 1)),
158 std::max(0, std::min(y, pixels->height() - 1)));
162 // Set a single channel value. Works for 4-channel and single channel
163 // bitmaps. Clamp x/y.
164 void SetChannel(SkBitmap* pixels, int x, int y, int c, int v) {
167 DCHECK_LT(x, pixels->width());
168 DCHECK_LT(y, pixels->height());
169 if (pixels->bytesPerPixel() == 4) {
170 uint32* data = pixels->getAddr32(x, y);
171 v = std::max(0, std::min(v, 255));
172 *data = (*data & ~(0xffu << (c * 8))) | (v << (c * 8));
174 DCHECK_EQ(pixels->bytesPerPixel(), 1);
176 uint8* data = pixels->getAddr8(x, y);
177 v = std::max(0, std::min(v, 255));
182 // Print all the R, G, B or A values from an SkBitmap in a
183 // human-readable format.
184 void PrintChannel(SkBitmap* pixels, int c) {
185 for (int y = 0; y < pixels->height(); y++) {
186 std::string formatted;
187 for (int x = 0; x < pixels->width(); x++) {
188 formatted.append(base::StringPrintf("%3d, ", Channel(pixels, x, y, c)));
190 LOG(ERROR) << formatted;
194 // Print out the individual steps of a scaler pipeline.
195 std::string PrintStages(
196 const std::vector<GLHelperScaling::ScalerStage>& scaler_stages) {
198 for (size_t i = 0; i < scaler_stages.size(); i++) {
199 ret.append(base::StringPrintf("%dx%d -> %dx%d ",
200 scaler_stages[i].src_size.width(),
201 scaler_stages[i].src_size.height(),
202 scaler_stages[i].dst_size.width(),
203 scaler_stages[i].dst_size.height()));
204 bool xy_matters = false;
205 switch (scaler_stages[i].shader) {
206 case GLHelperScaling::SHADER_BILINEAR:
207 ret.append("bilinear");
209 case GLHelperScaling::SHADER_BILINEAR2:
210 ret.append("bilinear2");
213 case GLHelperScaling::SHADER_BILINEAR3:
214 ret.append("bilinear3");
217 case GLHelperScaling::SHADER_BILINEAR4:
218 ret.append("bilinear4");
221 case GLHelperScaling::SHADER_BILINEAR2X2:
222 ret.append("bilinear2x2");
224 case GLHelperScaling::SHADER_BICUBIC_UPSCALE:
225 ret.append("bicubic upscale");
228 case GLHelperScaling::SHADER_BICUBIC_HALF_1D:
229 ret.append("bicubic 1/2");
232 case GLHelperScaling::SHADER_PLANAR:
233 ret.append("planar");
235 case GLHelperScaling::SHADER_YUV_MRT_PASS1:
236 ret.append("rgb2yuv pass 1");
238 case GLHelperScaling::SHADER_YUV_MRT_PASS2:
239 ret.append("rgb2yuv pass 2");
244 if (scaler_stages[i].scale_x) {
255 bool CheckScale(double scale, int samples, bool already_scaled) {
256 // 1:1 is valid if there is one sample.
257 if (samples == 1 && scale == 1.0) {
260 // Is it an exact down-scale (50%, 25%, etc.?)
261 if (scale == 2.0 * samples) {
264 // Upscales, only valid if we haven't already scaled in this dimension.
265 if (!already_scaled) {
266 // Is it a valid bilinear upscale?
267 if (samples == 1 && scale <= 1.0) {
270 // Multi-sample upscale-downscale combination?
271 if (scale > samples / 2.0 && scale < samples) {
278 // Make sure that the stages of the scaler pipeline are sane.
279 void ValidateScalerStages(
280 content::GLHelper::ScalerQuality quality,
281 const std::vector<GLHelperScaling::ScalerStage>& scaler_stages,
282 const gfx::Size& dst_size,
283 const std::string& message) {
284 bool previous_error = HasFailure();
285 // First, check that the input size for each stage is equal to
286 // the output size of the previous stage.
287 for (size_t i = 1; i < scaler_stages.size(); i++) {
288 EXPECT_EQ(scaler_stages[i - 1].dst_size.width(),
289 scaler_stages[i].src_size.width());
290 EXPECT_EQ(scaler_stages[i - 1].dst_size.height(),
291 scaler_stages[i].src_size.height());
292 EXPECT_EQ(scaler_stages[i].src_subrect.x(), 0);
293 EXPECT_EQ(scaler_stages[i].src_subrect.y(), 0);
294 EXPECT_EQ(scaler_stages[i].src_subrect.width(),
295 scaler_stages[i].src_size.width());
296 EXPECT_EQ(scaler_stages[i].src_subrect.height(),
297 scaler_stages[i].src_size.height());
300 // Check the output size matches the destination of the last stage
301 EXPECT_EQ(scaler_stages[scaler_stages.size() - 1].dst_size.width(),
303 EXPECT_EQ(scaler_stages[scaler_stages.size() - 1].dst_size.height(),
306 // Used to verify that up-scales are not attempted after some
308 bool scaled_x = false;
309 bool scaled_y = false;
311 for (size_t i = 0; i < scaler_stages.size(); i++) {
312 // Note: 2.0 means scaling down by 50%
314 static_cast<double>(scaler_stages[i].src_subrect.width()) /
315 static_cast<double>(scaler_stages[i].dst_size.width());
317 static_cast<double>(scaler_stages[i].src_subrect.height()) /
318 static_cast<double>(scaler_stages[i].dst_size.height());
323 // Codify valid scale operations.
324 switch (scaler_stages[i].shader) {
325 case GLHelperScaling::SHADER_PLANAR:
326 case GLHelperScaling::SHADER_YUV_MRT_PASS1:
327 case GLHelperScaling::SHADER_YUV_MRT_PASS2:
328 EXPECT_TRUE(false) << "Invalid shader.";
331 case GLHelperScaling::SHADER_BILINEAR:
332 if (quality != content::GLHelper::SCALER_QUALITY_FAST) {
337 case GLHelperScaling::SHADER_BILINEAR2:
341 case GLHelperScaling::SHADER_BILINEAR3:
345 case GLHelperScaling::SHADER_BILINEAR4:
349 case GLHelperScaling::SHADER_BILINEAR2X2:
353 case GLHelperScaling::SHADER_BICUBIC_UPSCALE:
354 if (scaler_stages[i].scale_x) {
355 EXPECT_LT(x_scale, 1.0);
356 EXPECT_EQ(y_scale, 1.0);
358 EXPECT_EQ(x_scale, 1.0);
359 EXPECT_LT(y_scale, 1.0);
362 case GLHelperScaling::SHADER_BICUBIC_HALF_1D:
363 if (scaler_stages[i].scale_x) {
364 EXPECT_EQ(x_scale, 2.0);
365 EXPECT_EQ(y_scale, 1.0);
367 EXPECT_EQ(x_scale, 1.0);
368 EXPECT_EQ(y_scale, 2.0);
373 if (!scaler_stages[i].scale_x) {
374 std::swap(x_samples, y_samples);
378 EXPECT_TRUE(CheckScale(x_scale, x_samples, scaled_x))
379 << "x_scale = " << x_scale;
382 EXPECT_TRUE(CheckScale(y_scale, y_samples, scaled_y))
383 << "y_scale = " << y_scale;
386 if (x_scale != 1.0) {
389 if (y_scale != 1.0) {
394 if (HasFailure() && !previous_error) {
395 LOG(ERROR) << "Invalid scaler stages: " << message;
396 LOG(ERROR) << "Scaler stages:";
397 LOG(ERROR) << PrintStages(scaler_stages);
401 // Compares two bitmaps taking color types into account. Checks whether each
402 // component of each pixel is no more than |maxdiff| apart. If bitmaps are not
403 // similar enough, prints out |truth|, |other|, |source|, |scaler_stages|
405 void Compare(SkBitmap* truth,
409 const std::vector<GLHelperScaling::ScalerStage>& scaler_stages,
410 std::string message) {
411 EXPECT_EQ(truth->width(), other->width());
412 EXPECT_EQ(truth->height(), other->height());
413 bool swizzle = (truth->colorType() == kRGBA_8888_SkColorType &&
414 other->colorType() == kBGRA_8888_SkColorType) ||
415 (truth->colorType() == kBGRA_8888_SkColorType &&
416 other->colorType() == kRGBA_8888_SkColorType);
417 EXPECT_TRUE(swizzle || truth->colorType() == other->colorType());
418 int bpp = truth->bytesPerPixel();
419 for (int x = 0; x < truth->width(); x++) {
420 for (int y = 0; y < truth->height(); y++) {
421 for (int c = 0; c < bpp; c++) {
422 int a = Channel(truth, x, y, c);
423 // swizzle when comparing if needed
424 int b = swizzle && (c == 0 || c == 2)
425 ? Channel(other, x, y, (c + 2) & 2)
426 : Channel(other, x, y, c);
427 EXPECT_NEAR(a, b, maxdiff) << " x=" << x << " y=" << y << " c=" << c
429 if (std::abs(a - b) > maxdiff) {
430 LOG(ERROR) << "-------expected--------";
431 for (int i = 0; i < bpp; i++) {
432 LOG(ERROR) << "Channel " << i << ":";
433 PrintChannel(truth, i);
435 LOG(ERROR) << "-------actual--------";
436 for (int i = 0; i < bpp; i++) {
437 LOG(ERROR) << "Channel " << i << ":";
438 PrintChannel(other, i);
441 LOG(ERROR) << "-------original--------";
442 for (int i = 0; i < source->bytesPerPixel(); i++) {
443 LOG(ERROR) << "Channel " << i << ":";
444 PrintChannel(source, i);
447 LOG(ERROR) << "-----Scaler stages------";
448 LOG(ERROR) << PrintStages(scaler_stages);
456 // Get a single R, G, B or A value as a float.
457 float ChannelAsFloat(SkBitmap* pixels, int x, int y, int c) {
458 return Channel(pixels, x, y, c) / 255.0;
461 // Works like a GL_LINEAR lookup on an SkBitmap.
462 float Bilinear(SkBitmap* pixels, float x, float y, int c) {
465 int base_x = static_cast<int>(floorf(x));
466 int base_y = static_cast<int>(floorf(y));
469 return (ChannelAsFloat(pixels, base_x, base_y, c) * (1 - x) * (1 - y) +
470 ChannelAsFloat(pixels, base_x + 1, base_y, c) * x * (1 - y) +
471 ChannelAsFloat(pixels, base_x, base_y + 1, c) * (1 - x) * y +
472 ChannelAsFloat(pixels, base_x + 1, base_y + 1, c) * x * y);
475 // Encodes an RGBA bitmap to grayscale.
476 // Reference implementation for
477 // GLHelper::CopyToTextureImpl::EncodeTextureAsGrayscale.
478 void EncodeToGrayscaleSlow(SkBitmap* input, SkBitmap* output) {
479 const float kRGBtoGrayscaleColorWeights[3] = {0.213f, 0.715f, 0.072f};
480 CHECK_EQ(kAlpha_8_SkColorType, output->colorType());
481 CHECK_EQ(input->width(), output->width());
482 CHECK_EQ(input->height(), output->height());
483 CHECK_EQ(input->colorType(), kRGBA_8888_SkColorType);
485 for (int dst_y = 0; dst_y < output->height(); dst_y++) {
486 for (int dst_x = 0; dst_x < output->width(); dst_x++) {
487 float c0 = ChannelAsFloat(input, dst_x, dst_y, 0);
488 float c1 = ChannelAsFloat(input, dst_x, dst_y, 1);
489 float c2 = ChannelAsFloat(input, dst_x, dst_y, 2);
490 float value = c0 * kRGBtoGrayscaleColorWeights[0] +
491 c1 * kRGBtoGrayscaleColorWeights[1] +
492 c2 * kRGBtoGrayscaleColorWeights[2];
494 output, dst_x, dst_y, 0, static_cast<int>(value * 255.0f + 0.5f));
499 // Very slow bicubic / bilinear scaler for reference.
500 void ScaleSlow(SkBitmap* input,
502 content::GLHelper::ScalerQuality quality) {
503 float xscale = static_cast<float>(input->width()) / output->width();
504 float yscale = static_cast<float>(input->height()) / output->height();
505 float clamped_xscale = xscale < 1.0 ? 1.0 : 1.0 / xscale;
506 float clamped_yscale = yscale < 1.0 ? 1.0 : 1.0 / yscale;
507 for (int dst_y = 0; dst_y < output->height(); dst_y++) {
508 for (int dst_x = 0; dst_x < output->width(); dst_x++) {
509 for (int channel = 0; channel < 4; channel++) {
510 float dst_x_in_src = (dst_x + 0.5f) * xscale;
511 float dst_y_in_src = (dst_y + 0.5f) * yscale;
516 case content::GLHelper::SCALER_QUALITY_BEST:
517 for (int src_y = -10; src_y < input->height() + 10; ++src_y) {
519 Bicubic((src_y + 0.5f - dst_y_in_src) * clamped_yscale);
520 if (coeff_y == 0.0f) {
523 for (int src_x = -10; src_x < input->width() + 10; ++src_x) {
526 Bicubic((src_x + 0.5f - dst_x_in_src) * clamped_xscale);
531 float c = ChannelAsFloat(input, src_x, src_y, channel);
537 case content::GLHelper::SCALER_QUALITY_GOOD: {
538 int xshift = 0, yshift = 0;
539 while ((output->width() << xshift) < input->width()) {
542 while ((output->height() << yshift) < input->height()) {
545 int xmag = 1 << xshift;
546 int ymag = 1 << yshift;
547 if (xmag == 4 && output->width() * 3 >= input->width()) {
550 if (ymag == 4 && output->height() * 3 >= input->height()) {
553 for (int x = 0; x < xmag; x++) {
554 for (int y = 0; y < ymag; y++) {
555 value += Bilinear(input,
556 (dst_x * xmag + x + 0.5) * xscale / xmag,
557 (dst_y * ymag + y + 0.5) * yscale / ymag,
565 case content::GLHelper::SCALER_QUALITY_FAST:
566 value = Bilinear(input, dst_x_in_src, dst_y_in_src, channel);
574 static_cast<int>(value * 255.0f + 0.5f));
580 void FlipSKBitmap(SkBitmap* bitmap) {
581 int bpp = bitmap->bytesPerPixel();
582 DCHECK(bpp == 4 || bpp == 1);
584 int bottom_line = bitmap->height() - 1;
585 while (top_line < bottom_line) {
586 for (int x = 0; x < bitmap->width(); x++) {
587 bpp == 4 ? std::swap(*bitmap->getAddr32(x, top_line),
588 *bitmap->getAddr32(x, bottom_line))
589 : std::swap(*bitmap->getAddr8(x, top_line),
590 *bitmap->getAddr8(x, bottom_line));
597 // Swaps red and blue channels in each pixel in a 32-bit bitmap.
598 void SwizzleSKBitmap(SkBitmap* bitmap) {
599 int bpp = bitmap->bytesPerPixel();
601 for (int y = 0; y < bitmap->height(); y++) {
602 for (int x = 0; x < bitmap->width(); x++) {
603 // Swap channels 0 and 2 (red and blue)
604 int c0 = Channel(bitmap, x, y, 0);
605 int c2 = Channel(bitmap, x, y, 2);
606 SetChannel(bitmap, x, y, 2, c0);
607 SetChannel(bitmap, x, y, 0, c2);
612 // gl_helper scales recursively, so we'll need to do that
613 // in the reference implementation too.
614 void ScaleSlowRecursive(SkBitmap* input,
616 content::GLHelper::ScalerQuality quality) {
617 if (quality == content::GLHelper::SCALER_QUALITY_FAST ||
618 quality == content::GLHelper::SCALER_QUALITY_GOOD) {
619 ScaleSlow(input, output, quality);
623 float xscale = static_cast<float>(output->width()) / input->width();
625 // This corresponds to all the operations we can do directly.
626 float yscale = static_cast<float>(output->height()) / input->height();
627 if ((xscale == 1.0f && yscale == 1.0f) ||
628 (xscale == 0.5f && yscale == 1.0f) ||
629 (xscale == 1.0f && yscale == 0.5f) ||
630 (xscale >= 1.0f && yscale == 1.0f) ||
631 (xscale == 1.0f && yscale >= 1.0f)) {
632 ScaleSlow(input, output, quality);
636 // Now we break the problem down into smaller pieces, using the
637 // operations available.
638 int xtmp = input->width();
639 int ytmp = input->height();
641 if (output->height() != input->height()) {
642 ytmp = output->height();
643 while (ytmp < input->height() && ytmp * 2 != input->height()) {
647 xtmp = output->width();
648 while (xtmp < input->width() && xtmp * 2 != input->width()) {
654 tmp.allocN32Pixels(xtmp, ytmp);
656 ScaleSlowRecursive(input, &tmp, quality);
657 ScaleSlowRecursive(&tmp, output, quality);
660 // Creates an RGBA SkBitmap
661 scoped_ptr<SkBitmap> CreateTestBitmap(int width,
664 scoped_ptr<SkBitmap> bitmap(new SkBitmap);
665 bitmap->allocPixels(SkImageInfo::Make(
666 width, height, kRGBA_8888_SkColorType, kPremul_SkAlphaType));
668 for (int x = 0; x < width; ++x) {
669 for (int y = 0; y < height; ++y) {
670 switch (test_pattern) {
671 case 0: // Smooth test pattern
672 SetChannel(bitmap.get(), x, y, 0, x * 10);
673 SetChannel(bitmap.get(), x, y, 0, y == 0 ? x * 50 : x * 10);
674 SetChannel(bitmap.get(), x, y, 1, y * 10);
675 SetChannel(bitmap.get(), x, y, 2, (x + y) * 10);
676 SetChannel(bitmap.get(), x, y, 3, 255);
678 case 1: // Small blocks
679 SetChannel(bitmap.get(), x, y, 0, x & 1 ? 255 : 0);
680 SetChannel(bitmap.get(), x, y, 1, y & 1 ? 255 : 0);
681 SetChannel(bitmap.get(), x, y, 2, (x + y) & 1 ? 255 : 0);
682 SetChannel(bitmap.get(), x, y, 3, 255);
684 case 2: // Medium blocks
685 SetChannel(bitmap.get(), x, y, 0, 10 + x / 2 * 50);
686 SetChannel(bitmap.get(), x, y, 1, 10 + y / 3 * 50);
687 SetChannel(bitmap.get(), x, y, 2, (x + y) / 5 * 50 + 5);
688 SetChannel(bitmap.get(), x, y, 3, 255);
693 return bitmap.Pass();
696 // Binds texture and framebuffer and loads the bitmap pixels into the texture.
697 void BindTextureAndFrameBuffer(WebGLId texture,
702 context_->bindFramebuffer(GL_FRAMEBUFFER, framebuffer);
703 context_->bindTexture(GL_TEXTURE_2D, texture);
704 context_->texImage2D(GL_TEXTURE_2D,
712 bitmap->getPixels());
715 // Create a test image, transform it using
716 // GLHelper::CropScaleReadbackAndCleanTexture and a reference implementation
717 // and compare the results.
718 void TestCropScaleReadbackAndCleanTexture(int xsize,
723 SkColorType out_color_type,
725 size_t quality_index) {
726 DCHECK(out_color_type == kAlpha_8_SkColorType ||
727 out_color_type == kRGBA_8888_SkColorType ||
728 out_color_type == kBGRA_8888_SkColorType);
729 WebGLId src_texture = context_->createTexture();
730 WebGLId framebuffer = context_->createFramebuffer();
731 scoped_ptr<SkBitmap> input_pixels =
732 CreateTestBitmap(xsize, ysize, test_pattern).Pass();
733 BindTextureAndFrameBuffer(
734 src_texture, framebuffer, input_pixels.get(), xsize, ysize);
736 std::string message = base::StringPrintf(
738 "output size: %dx%d "
739 "pattern: %d , quality: %s, "
740 "out_color_type: %d",
746 kQualityNames[quality_index],
749 // Transform the bitmap using GLHelper::CropScaleReadbackAndCleanTexture.
750 SkBitmap output_pixels;
751 output_pixels.allocPixels(SkImageInfo::Make(
752 scaled_xsize, scaled_ysize, out_color_type, kPremul_SkAlphaType));
753 base::RunLoop run_loop;
754 gfx::Size encoded_texture_size;
755 helper_->CropScaleReadbackAndCleanTexture(
757 gfx::Size(xsize, ysize),
758 gfx::Rect(xsize, ysize),
759 gfx::Size(scaled_xsize, scaled_ysize),
760 static_cast<unsigned char*>(output_pixels.getPixels()),
762 base::Bind(&callcallback, run_loop.QuitClosure()),
763 kQualities[quality_index]);
765 // CropScaleReadbackAndCleanTexture flips the pixels. Flip them back.
766 FlipSKBitmap(&output_pixels);
768 // If the bitmap shouldn't have changed - compare against input.
769 if (xsize == scaled_xsize && ysize == scaled_ysize &&
770 out_color_type != kAlpha_8_SkColorType) {
771 const std::vector<GLHelperScaling::ScalerStage> dummy_stages;
772 Compare(input_pixels.get(),
777 message + " comparing against input");
781 // Now transform the bitmap using the reference implementation.
782 SkBitmap scaled_pixels;
783 scaled_pixels.allocPixels(SkImageInfo::Make(scaled_xsize,
785 kRGBA_8888_SkColorType,
786 kPremul_SkAlphaType));
787 SkBitmap truth_pixels;
790 input_pixels.get(), &scaled_pixels, kQualities[quality_index]);
791 // Step 2: Encode to grayscale if needed.
792 if (out_color_type == kAlpha_8_SkColorType) {
793 truth_pixels.allocPixels(SkImageInfo::Make(
794 scaled_xsize, scaled_ysize, out_color_type, kPremul_SkAlphaType));
795 EncodeToGrayscaleSlow(&scaled_pixels, &truth_pixels);
797 truth_pixels = scaled_pixels;
800 // Now compare the results.
801 SkAutoLockPixels lock_input(truth_pixels);
802 const std::vector<GLHelperScaling::ScalerStage> dummy_stages;
803 Compare(&truth_pixels,
808 message + " comparing against transformed/scaled");
810 context_->deleteTexture(src_texture);
811 context_->deleteFramebuffer(framebuffer);
814 // Scaling test: Create a test image, scale it using GLHelperScaling
815 // and a reference implementation and compare the results.
816 void TestScale(int xsize,
821 size_t quality_index,
823 WebGLId src_texture = context_->createTexture();
824 WebGLId framebuffer = context_->createFramebuffer();
825 scoped_ptr<SkBitmap> input_pixels =
826 CreateTestBitmap(xsize, ysize, test_pattern).Pass();
827 BindTextureAndFrameBuffer(
828 src_texture, framebuffer, input_pixels.get(), xsize, ysize);
830 std::string message = base::StringPrintf(
832 "output size: %dx%d "
833 "pattern: %d quality: %s",
839 kQualityNames[quality_index]);
841 std::vector<GLHelperScaling::ScalerStage> stages;
842 helper_scaling_->ComputeScalerStages(kQualities[quality_index],
843 gfx::Size(xsize, ysize),
844 gfx::Rect(0, 0, xsize, ysize),
845 gfx::Size(scaled_xsize, scaled_ysize),
849 ValidateScalerStages(kQualities[quality_index],
851 gfx::Size(scaled_xsize, scaled_ysize),
854 WebGLId dst_texture =
855 helper_->CopyAndScaleTexture(src_texture,
856 gfx::Size(xsize, ysize),
857 gfx::Size(scaled_xsize, scaled_ysize),
859 kQualities[quality_index]);
861 SkBitmap output_pixels;
862 output_pixels.allocPixels(SkImageInfo::Make(scaled_xsize,
864 kRGBA_8888_SkColorType,
865 kPremul_SkAlphaType));
867 helper_->ReadbackTextureSync(
869 gfx::Rect(0, 0, scaled_xsize, scaled_ysize),
870 static_cast<unsigned char*>(output_pixels.getPixels()),
871 kRGBA_8888_SkColorType);
873 // Flip the pixels back.
874 FlipSKBitmap(&output_pixels);
877 // If the bitmap shouldn't have changed - compare against input.
878 if (xsize == scaled_xsize && ysize == scaled_ysize) {
879 Compare(input_pixels.get(),
884 message + " comparing against input");
888 // Now scale the bitmap using the reference implementation.
889 SkBitmap truth_pixels;
890 truth_pixels.allocPixels(SkImageInfo::Make(scaled_xsize,
892 kRGBA_8888_SkColorType,
893 kPremul_SkAlphaType));
895 input_pixels.get(), &truth_pixels, kQualities[quality_index]);
896 Compare(&truth_pixels,
901 message + " comparing against scaled");
903 context_->deleteTexture(src_texture);
904 context_->deleteTexture(dst_texture);
905 context_->deleteFramebuffer(framebuffer);
908 // Create a scaling pipeline and check that it is made up of
909 // valid scaling operations.
910 void TestScalerPipeline(size_t quality,
915 std::vector<GLHelperScaling::ScalerStage> stages;
916 helper_scaling_->ComputeScalerStages(kQualities[quality],
917 gfx::Size(xsize, ysize),
918 gfx::Rect(0, 0, xsize, ysize),
919 gfx::Size(dst_xsize, dst_ysize),
923 ValidateScalerStages(kQualities[quality],
925 gfx::Size(dst_xsize, dst_ysize),
928 "output size: %dx%d "
934 kQualityNames[quality]));
937 // Create a scaling pipeline and make sure that the steps
938 // are exactly the steps we expect.
939 void CheckPipeline(content::GLHelper::ScalerQuality quality,
944 const std::string& description) {
945 std::vector<GLHelperScaling::ScalerStage> stages;
946 helper_scaling_->ComputeScalerStages(quality,
947 gfx::Size(xsize, ysize),
948 gfx::Rect(0, 0, xsize, ysize),
949 gfx::Size(dst_xsize, dst_ysize),
953 ValidateScalerStages(content::GLHelper::SCALER_QUALITY_GOOD,
955 gfx::Size(dst_xsize, dst_ysize),
957 EXPECT_EQ(PrintStages(stages), description);
960 // Note: Left/Right means Top/Bottom when used for Y dimension.
968 static Margin NextMargin(Margin m) {
975 return MarginInvalid;
977 return MarginInvalid;
981 int compute_margin(int insize, int outsize, Margin m) {
982 int available = outsize - insize;
985 EXPECT_TRUE(false) << "This should not happen.";
990 return (available / 2) & ~1;
996 // Convert 0.0 - 1.0 to 0 - 255
997 int float_to_byte(float v) {
998 int ret = static_cast<int>(floorf(v * 255.0f + 0.5f));
1008 static void callcallback(const base::Callback<void()>& callback,
1013 void PrintPlane(unsigned char* plane, int xsize, int stride, int ysize) {
1014 for (int y = 0; y < ysize; y++) {
1015 std::string formatted;
1016 for (int x = 0; x < xsize; x++) {
1017 formatted.append(base::StringPrintf("%3d, ", plane[y * stride + x]));
1019 LOG(ERROR) << formatted << " (" << (plane + y * stride) << ")";
1023 // Compare two planes make sure that each component of each pixel
1024 // is no more than |maxdiff| apart.
1025 void ComparePlane(unsigned char* truth,
1026 unsigned char* other,
1032 std::string message) {
1033 int truth_stride = stride;
1034 for (int x = 0; x < xsize; x++) {
1035 for (int y = 0; y < ysize; y++) {
1036 int a = other[y * stride + x];
1037 int b = truth[y * stride + x];
1038 EXPECT_NEAR(a, b, maxdiff) << " x=" << x << " y=" << y << " "
1040 if (std::abs(a - b) > maxdiff) {
1041 LOG(ERROR) << "-------expected--------";
1042 PrintPlane(truth, xsize, truth_stride, ysize);
1043 LOG(ERROR) << "-------actual--------";
1044 PrintPlane(other, xsize, stride, ysize);
1046 LOG(ERROR) << "-------before yuv conversion: red--------";
1047 PrintChannel(source, 0);
1048 LOG(ERROR) << "-------before yuv conversion: green------";
1049 PrintChannel(source, 1);
1050 LOG(ERROR) << "-------before yuv conversion: blue-------";
1051 PrintChannel(source, 2);
1059 void DrawGridToBitmap(int w, int h,
1060 SkColor background_color,
1065 ASSERT_GT(grid_pitch, 0);
1066 ASSERT_GT(grid_width, 0);
1067 ASSERT_NE(background_color, grid_color);
1069 for (int y = 0; y < h; ++y) {
1070 bool y_on_grid = ((y % grid_pitch) < grid_width);
1072 for (int x = 0; x < w; ++x) {
1073 bool on_grid = (y_on_grid || ((x % grid_pitch) < grid_width));
1075 if (bmp.colorType() == kRGBA_8888_SkColorType ||
1076 bmp.colorType() == kBGRA_8888_SkColorType) {
1077 *bmp.getAddr32(x, y) = (on_grid ? grid_color : background_color);
1078 } else if (bmp.colorType() == kRGB_565_SkColorType) {
1079 *bmp.getAddr16(x, y) = (on_grid ? grid_color : background_color);
1085 void DrawCheckerToBitmap(int w, int h,
1086 SkColor color1, SkColor color2,
1087 int rect_w, int rect_h,
1089 ASSERT_GT(rect_w, 0);
1090 ASSERT_GT(rect_h, 0);
1091 ASSERT_NE(color1, color2);
1093 for (int y = 0; y < h; ++y) {
1094 bool y_bit = (((y / rect_h) & 0x1) == 0);
1096 for (int x = 0; x < w; ++x) {
1097 bool x_bit = (((x / rect_w) & 0x1) == 0);
1099 bool use_color2 = (x_bit != y_bit); // xor
1100 if (bmp.colorType() == kRGBA_8888_SkColorType ||
1101 bmp.colorType() == kBGRA_8888_SkColorType) {
1102 *bmp.getAddr32(x, y) = (use_color2 ? color2 : color1);
1103 } else if (bmp.colorType() == kRGB_565_SkColorType) {
1104 *bmp.getAddr16(x, y) = (use_color2 ? color2 : color1);
1110 bool ColorComponentsClose(SkColor component1,
1112 SkColorType color_type) {
1113 int c1 = static_cast<int>(component1);
1114 int c2 = static_cast<int>(component2);
1115 bool result = false;
1116 switch (color_type) {
1117 case kRGBA_8888_SkColorType:
1118 case kBGRA_8888_SkColorType:
1119 result = (std::abs(c1 - c2) == 0);
1121 case kRGB_565_SkColorType:
1122 result = (std::abs(c1 - c2) <= 7);
1130 bool ColorsClose(SkColor color1, SkColor color2, SkColorType color_type) {
1131 bool red = ColorComponentsClose(SkColorGetR(color1),
1132 SkColorGetR(color2), color_type);
1133 bool green = ColorComponentsClose(SkColorGetG(color1),
1134 SkColorGetG(color2), color_type);
1135 bool blue = ColorComponentsClose(SkColorGetB(color1),
1136 SkColorGetB(color2), color_type);
1137 bool alpha = ColorComponentsClose(SkColorGetA(color1),
1138 SkColorGetA(color2), color_type);
1139 if (color_type == kRGB_565_SkColorType) {
1140 return red && blue && green;
1142 return red && blue && green && alpha;
1145 bool IsEqual(const SkBitmap& bmp1, const SkBitmap& bmp2) {
1146 if (bmp1.isNull() && bmp2.isNull())
1148 if (bmp1.width() != bmp2.width() ||
1149 bmp1.height() != bmp2.height()) {
1150 LOG(ERROR) << "Bitmap geometry check failure";
1153 if (bmp1.colorType() != bmp2.colorType())
1156 SkAutoLockPixels lock1(bmp1);
1157 SkAutoLockPixels lock2(bmp2);
1158 if (!bmp1.getPixels() || !bmp2.getPixels()) {
1159 LOG(ERROR) << "Empty Bitmap!";
1162 for (int y = 0; y < bmp1.height(); ++y) {
1163 for (int x = 0; x < bmp1.width(); ++x) {
1164 if (!ColorsClose(bmp1.getColor(x,y),
1166 bmp1.colorType())) {
1167 LOG(ERROR) << "Bitmap color comparision failure";
1175 void BindAndAttachTextureWithPixels(GLuint src_texture,
1176 SkColorType color_type,
1177 const gfx::Size& src_size,
1178 const SkBitmap& input_pixels) {
1179 context_->bindTexture(GL_TEXTURE_2D, src_texture);
1181 switch (color_type) {
1182 case kBGRA_8888_SkColorType:
1183 format = GL_BGRA_EXT;
1185 case kRGBA_8888_SkColorType:
1188 case kRGB_565_SkColorType:
1194 GLenum type = (color_type == kRGB_565_SkColorType) ?
1195 GL_UNSIGNED_SHORT_5_6_5 : GL_UNSIGNED_BYTE;
1196 context_->texImage2D(GL_TEXTURE_2D,
1204 input_pixels.getPixels());
1207 void ReadBackTexture(GLuint src_texture,
1208 const gfx::Size& src_size,
1209 unsigned char* pixels,
1210 SkColorType color_type,
1213 base::RunLoop run_loop;
1214 helper_->ReadbackTextureAsync(src_texture,
1218 base::Bind(&callcallback,
1219 run_loop.QuitClosure()));
1222 helper_->ReadbackTextureSync(src_texture,
1223 gfx::Rect(src_size),
1228 // Test basic format readback.
1229 bool TestTextureFormatReadback(const gfx::Size& src_size,
1230 SkColorType color_type,
1233 SkImageInfo::Make(src_size.width(),
1236 kPremul_SkAlphaType);
1237 if (!helper_->IsReadbackConfigSupported(color_type)) {
1238 LOG(INFO) << "Skipping test format not supported" << color_type;
1241 WebGLId src_texture = context_->createTexture();
1242 SkBitmap input_pixels;
1243 input_pixels.allocPixels(info);
1244 // Test Pattern-1, Fill with Plain color pattern.
1245 // Erase the input bitmap with red color.
1246 input_pixels.eraseColor(SK_ColorRED);
1247 BindAndAttachTextureWithPixels(src_texture,
1251 SkBitmap output_pixels;
1252 output_pixels.allocPixels(info);
1253 // Initialize the output bitmap with Green color.
1254 // When the readback is over output bitmap should have the red color.
1255 output_pixels.eraseColor(SK_ColorGREEN);
1256 uint8* pixels = static_cast<uint8*>(output_pixels.getPixels());
1257 ReadBackTexture(src_texture, src_size, pixels, color_type, async);
1258 bool result = IsEqual(input_pixels, output_pixels);
1260 LOG(ERROR) << "Bitmap comparision failure Pattern-1";
1263 const int rect_w = 10, rect_h = 4, src_grid_pitch = 10, src_grid_width = 4;
1264 const SkColor color1 = SK_ColorRED, color2 = SK_ColorBLUE;
1265 // Test Pattern-2, Fill with Grid Pattern.
1266 DrawGridToBitmap(src_size.width(), src_size.height(),
1268 src_grid_pitch, src_grid_width,
1270 BindAndAttachTextureWithPixels(src_texture,
1274 ReadBackTexture(src_texture, src_size, pixels, color_type, async);
1275 result = IsEqual(input_pixels, output_pixels);
1277 LOG(ERROR) << "Bitmap comparision failure Pattern-2";
1280 // Test Pattern-3, Fill with CheckerBoard Pattern.
1281 DrawCheckerToBitmap(src_size.width(),
1284 color2, rect_w, rect_h, input_pixels);
1285 BindAndAttachTextureWithPixels(src_texture,
1289 ReadBackTexture(src_texture, src_size, pixels, color_type, async);
1290 result = IsEqual(input_pixels, output_pixels);
1292 LOG(ERROR) << "Bitmap comparision failure Pattern-3";
1295 context_->deleteTexture(src_texture);
1302 // YUV readback test. Create a test pattern, convert to YUV
1303 // with reference implementation and compare to what gl_helper
1305 void TestYUVReadback(int xsize,
1314 content::GLHelper::ScalerQuality quality) {
1315 WebGLId src_texture = context_->createTexture();
1316 SkBitmap input_pixels;
1317 input_pixels.allocN32Pixels(xsize, ysize);
1319 for (int x = 0; x < xsize; ++x) {
1320 for (int y = 0; y < ysize; ++y) {
1321 switch (test_pattern) {
1322 case 0: // Smooth test pattern
1323 SetChannel(&input_pixels, x, y, 0, x * 10);
1324 SetChannel(&input_pixels, x, y, 1, y * 10);
1325 SetChannel(&input_pixels, x, y, 2, (x + y) * 10);
1326 SetChannel(&input_pixels, x, y, 3, 255);
1328 case 1: // Small blocks
1329 SetChannel(&input_pixels, x, y, 0, x & 1 ? 255 : 0);
1330 SetChannel(&input_pixels, x, y, 1, y & 1 ? 255 : 0);
1331 SetChannel(&input_pixels, x, y, 2, (x + y) & 1 ? 255 : 0);
1332 SetChannel(&input_pixels, x, y, 3, 255);
1334 case 2: // Medium blocks
1335 SetChannel(&input_pixels, x, y, 0, 10 + x / 2 * 50);
1336 SetChannel(&input_pixels, x, y, 1, 10 + y / 3 * 50);
1337 SetChannel(&input_pixels, x, y, 2, (x + y) / 5 * 50 + 5);
1338 SetChannel(&input_pixels, x, y, 3, 255);
1344 context_->bindTexture(GL_TEXTURE_2D, src_texture);
1345 context_->texImage2D(GL_TEXTURE_2D,
1353 input_pixels.getPixels());
1355 gpu::Mailbox mailbox;
1356 context_->genMailboxCHROMIUM(mailbox.name);
1357 EXPECT_FALSE(mailbox.IsZero());
1358 context_->produceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
1359 uint32 sync_point = context_->insertSyncPoint();
1361 std::string message = base::StringPrintf(
1362 "input size: %dx%d "
1363 "output size: %dx%d "
1365 "pattern: %d %s %s",
1373 flip ? "flip" : "noflip",
1374 flip ? "mrt" : "nomrt");
1375 scoped_ptr<ReadbackYUVInterface> yuv_reader(
1376 helper_->CreateReadbackPipelineYUV(
1378 gfx::Size(xsize, ysize),
1379 gfx::Rect(0, 0, xsize, ysize),
1380 gfx::Size(output_xsize, output_ysize),
1381 gfx::Rect(xmargin, ymargin, xsize, ysize),
1385 scoped_refptr<media::VideoFrame> output_frame =
1386 media::VideoFrame::CreateFrame(
1387 media::VideoFrame::YV12,
1388 gfx::Size(output_xsize, output_ysize),
1389 gfx::Rect(0, 0, output_xsize, output_ysize),
1390 gfx::Size(output_xsize, output_ysize),
1391 base::TimeDelta::FromSeconds(0));
1392 scoped_refptr<media::VideoFrame> truth_frame =
1393 media::VideoFrame::CreateFrame(
1394 media::VideoFrame::YV12,
1395 gfx::Size(output_xsize, output_ysize),
1396 gfx::Rect(0, 0, output_xsize, output_ysize),
1397 gfx::Size(output_xsize, output_ysize),
1398 base::TimeDelta::FromSeconds(0));
1400 base::RunLoop run_loop;
1401 yuv_reader->ReadbackYUV(mailbox,
1404 base::Bind(&callcallback, run_loop.QuitClosure()));
1408 FlipSKBitmap(&input_pixels);
1411 unsigned char* Y = truth_frame->data(media::VideoFrame::kYPlane);
1412 unsigned char* U = truth_frame->data(media::VideoFrame::kUPlane);
1413 unsigned char* V = truth_frame->data(media::VideoFrame::kVPlane);
1414 int32 y_stride = truth_frame->stride(media::VideoFrame::kYPlane);
1415 int32 u_stride = truth_frame->stride(media::VideoFrame::kUPlane);
1416 int32 v_stride = truth_frame->stride(media::VideoFrame::kVPlane);
1417 memset(Y, 0x00, y_stride * output_ysize);
1418 memset(U, 0x80, u_stride * output_ysize / 2);
1419 memset(V, 0x80, v_stride * output_ysize / 2);
1421 const float kRGBtoYColorWeights[] = {0.257f, 0.504f, 0.098f, 0.0625f};
1422 const float kRGBtoUColorWeights[] = {-0.148f, -0.291f, 0.439f, 0.5f};
1423 const float kRGBtoVColorWeights[] = {0.439f, -0.368f, -0.071f, 0.5f};
1425 for (int y = 0; y < ysize; y++) {
1426 for (int x = 0; x < xsize; x++) {
1427 Y[(y + ymargin) * y_stride + x + xmargin] = float_to_byte(
1428 ChannelAsFloat(&input_pixels, x, y, 0) * kRGBtoYColorWeights[0] +
1429 ChannelAsFloat(&input_pixels, x, y, 1) * kRGBtoYColorWeights[1] +
1430 ChannelAsFloat(&input_pixels, x, y, 2) * kRGBtoYColorWeights[2] +
1431 kRGBtoYColorWeights[3]);
1435 for (int y = 0; y < ysize / 2; y++) {
1436 for (int x = 0; x < xsize / 2; x++) {
1437 U[(y + ymargin / 2) * u_stride + x + xmargin / 2] =
1438 float_to_byte(Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 0) *
1439 kRGBtoUColorWeights[0] +
1440 Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 1) *
1441 kRGBtoUColorWeights[1] +
1442 Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 2) *
1443 kRGBtoUColorWeights[2] +
1444 kRGBtoUColorWeights[3]);
1445 V[(y + ymargin / 2) * v_stride + x + xmargin / 2] =
1446 float_to_byte(Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 0) *
1447 kRGBtoVColorWeights[0] +
1448 Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 1) *
1449 kRGBtoVColorWeights[1] +
1450 Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 2) *
1451 kRGBtoVColorWeights[2] +
1452 kRGBtoVColorWeights[3]);
1457 output_frame->data(media::VideoFrame::kYPlane),
1463 message + " Y plane");
1465 output_frame->data(media::VideoFrame::kUPlane),
1471 message + " U plane");
1473 output_frame->data(media::VideoFrame::kVPlane),
1479 message + " V plane");
1481 context_->deleteTexture(src_texture);
1484 void TestAddOps(int src, int dst, bool scale_x, bool allow3) {
1485 std::deque<GLHelperScaling::ScaleOp> ops;
1486 GLHelperScaling::ScaleOp::AddOps(src, dst, scale_x, allow3, &ops);
1487 // Scale factor 3 is a special case.
1488 // It is currently only allowed by itself.
1489 if (allow3 && dst * 3 >= src && dst * 2 < src) {
1490 EXPECT_EQ(ops[0].scale_factor, 3);
1491 EXPECT_EQ(ops.size(), 1U);
1492 EXPECT_EQ(ops[0].scale_x, scale_x);
1493 EXPECT_EQ(ops[0].scale_size, dst);
1497 for (size_t i = 0; i < ops.size(); i++) {
1498 EXPECT_EQ(ops[i].scale_x, scale_x);
1500 // Only the first op is allowed to be a scale up.
1501 // (Scaling up *after* scaling down would make it fuzzy.)
1502 EXPECT_TRUE(ops[0].scale_factor == 0 || ops[0].scale_factor == 2);
1504 // All other operations must be 50% downscales.
1505 EXPECT_EQ(ops[i].scale_factor, 2);
1508 // Check that the scale factors make sense and add up.
1510 for (int i = static_cast<int>(ops.size() - 1); i >= 0; i--) {
1511 EXPECT_EQ(tmp, ops[i].scale_size);
1512 if (ops[i].scale_factor == 0) {
1514 EXPECT_GT(tmp, src);
1517 tmp *= ops[i].scale_factor;
1520 EXPECT_EQ(tmp, src);
1523 void CheckPipeline2(int xsize,
1527 const std::string& description) {
1528 std::vector<GLHelperScaling::ScalerStage> stages;
1529 helper_scaling_->ConvertScalerOpsToScalerStages(
1530 content::GLHelper::SCALER_QUALITY_GOOD,
1531 gfx::Size(xsize, ysize),
1532 gfx::Rect(0, 0, xsize, ysize),
1533 gfx::Size(dst_xsize, dst_ysize),
1539 EXPECT_EQ(x_ops_.size(), 0U);
1540 EXPECT_EQ(y_ops_.size(), 0U);
1541 ValidateScalerStages(content::GLHelper::SCALER_QUALITY_GOOD,
1543 gfx::Size(dst_xsize, dst_ysize),
1545 EXPECT_EQ(PrintStages(stages), description);
1548 void CheckOptimizationsTest() {
1549 // Basic upscale. X and Y should be combined into one pass.
1550 x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 2000));
1551 y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 2000));
1552 CheckPipeline2(1024, 768, 2000, 2000, "1024x768 -> 2000x2000 bilinear\n");
1554 // X scaled 1/2, Y upscaled, should still be one pass.
1555 x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 512));
1556 y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 2000));
1557 CheckPipeline2(1024, 768, 512, 2000, "1024x768 -> 512x2000 bilinear\n");
1559 // X upscaled, Y scaled 1/2, one bilinear pass
1560 x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 2000));
1561 y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 384));
1562 CheckPipeline2(1024, 768, 2000, 384, "1024x768 -> 2000x384 bilinear\n");
1564 // X scaled 1/2, Y scaled 1/2, one bilinear pass
1565 x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 512));
1566 y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 384));
1567 CheckPipeline2(1024, 768, 512, 384, "1024x768 -> 512x384 bilinear\n");
1569 // X scaled 1/2, Y scaled to 60%, one bilinear2 pass.
1570 x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 50));
1571 y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 120));
1572 y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 60));
1573 CheckPipeline2(100, 100, 50, 60, "100x100 -> 50x60 bilinear2 Y\n");
1575 // X scaled to 60%, Y scaled 1/2, one bilinear2 pass.
1576 x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 120));
1577 x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 60));
1578 y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 50));
1579 CheckPipeline2(100, 100, 60, 50, "100x100 -> 60x50 bilinear2 X\n");
1581 // X scaled to 60%, Y scaled 60%, one bilinear2x2 pass.
1582 x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 120));
1583 x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 60));
1584 y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 120));
1585 y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 60));
1586 CheckPipeline2(100, 100, 60, 60, "100x100 -> 60x60 bilinear2x2\n");
1588 // X scaled to 40%, Y scaled 40%, two bilinear3 passes.
1589 x_ops_.push_back(GLHelperScaling::ScaleOp(3, true, 40));
1590 y_ops_.push_back(GLHelperScaling::ScaleOp(3, false, 40));
1595 "100x100 -> 100x40 bilinear3 Y\n"
1596 "100x40 -> 40x40 bilinear3 X\n");
1598 // X scaled to 60%, Y scaled 40%
1599 x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 120));
1600 x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 60));
1601 y_ops_.push_back(GLHelperScaling::ScaleOp(3, false, 40));
1606 "100x100 -> 100x40 bilinear3 Y\n"
1607 "100x40 -> 60x40 bilinear2 X\n");
1609 // X scaled to 40%, Y scaled 60%
1610 x_ops_.push_back(GLHelperScaling::ScaleOp(3, true, 40));
1611 y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 120));
1612 y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 60));
1617 "100x100 -> 100x60 bilinear2 Y\n"
1618 "100x60 -> 40x60 bilinear3 X\n");
1620 // X scaled to 30%, Y scaled 30%
1621 x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 120));
1622 x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 60));
1623 x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 30));
1624 y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 120));
1625 y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 60));
1626 y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 30));
1631 "100x100 -> 100x30 bilinear4 Y\n"
1632 "100x30 -> 30x30 bilinear4 X\n");
1634 // X scaled to 50%, Y scaled 30%
1635 x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 50));
1636 y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 120));
1637 y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 60));
1638 y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 30));
1639 CheckPipeline2(100, 100, 50, 30, "100x100 -> 50x30 bilinear4 Y\n");
1641 // X scaled to 150%, Y scaled 30%
1642 // Note that we avoid combinding X and Y passes
1643 // as that would probably be LESS efficient here.
1644 x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 150));
1645 y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 120));
1646 y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 60));
1647 y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 30));
1652 "100x100 -> 100x30 bilinear4 Y\n"
1653 "100x30 -> 150x30 bilinear\n");
1655 // X scaled to 1%, Y scaled 1%
1656 x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 128));
1657 x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 64));
1658 x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 32));
1659 x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 16));
1660 x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 8));
1661 x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 4));
1662 x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 2));
1663 x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 1));
1664 y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 128));
1665 y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 64));
1666 y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 32));
1667 y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 16));
1668 y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 8));
1669 y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 4));
1670 y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 2));
1671 y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 1));
1676 "100x100 -> 100x32 bilinear4 Y\n"
1677 "100x32 -> 100x4 bilinear4 Y\n"
1678 "100x4 -> 64x1 bilinear2x2\n"
1679 "64x1 -> 8x1 bilinear4 X\n"
1680 "8x1 -> 1x1 bilinear4 X\n");
1683 scoped_ptr<WebGraphicsContext3DInProcessCommandBufferImpl> context_;
1684 gpu::ContextSupport* context_support_;
1685 scoped_ptr<content::GLHelper> helper_;
1686 scoped_ptr<content::GLHelperScaling> helper_scaling_;
1687 std::deque<GLHelperScaling::ScaleOp> x_ops_, y_ops_;
1690 class GLHelperPixelTest : public GLHelperTest {
1692 gfx::DisableNullDrawGLBindings enable_pixel_output_;
1695 TEST_F(GLHelperTest, RGBASyncReadbackTest) {
1696 const int kTestSize = 64;
1697 bool result = TestTextureFormatReadback(gfx::Size(kTestSize,kTestSize),
1698 kRGBA_8888_SkColorType,
1700 EXPECT_EQ(result, true);
1704 TEST_F(GLHelperTest, BGRASyncReadbackTest) {
1705 const int kTestSize = 64;
1706 bool result = TestTextureFormatReadback(gfx::Size(kTestSize,kTestSize),
1707 kBGRA_8888_SkColorType,
1709 EXPECT_EQ(result, true);
1712 TEST_F(GLHelperTest, RGB565SyncReadbackTest) {
1713 const int kTestSize = 64;
1714 bool result = TestTextureFormatReadback(gfx::Size(kTestSize,kTestSize),
1715 kRGB_565_SkColorType,
1717 EXPECT_EQ(result, true);
1720 TEST_F(GLHelperTest, RGBAASyncReadbackTest) {
1721 const int kTestSize = 64;
1722 bool result = TestTextureFormatReadback(gfx::Size(kTestSize,kTestSize),
1723 kRGBA_8888_SkColorType,
1725 EXPECT_EQ(result, true);
1728 TEST_F(GLHelperTest, BGRAASyncReadbackTest) {
1729 const int kTestSize = 64;
1730 bool result = TestTextureFormatReadback(gfx::Size(kTestSize,kTestSize),
1731 kBGRA_8888_SkColorType,
1733 EXPECT_EQ(result, true);
1736 TEST_F(GLHelperTest, RGB565ASyncReadbackTest) {
1737 const int kTestSize = 64;
1738 bool result = TestTextureFormatReadback(gfx::Size(kTestSize,kTestSize),
1739 kRGB_565_SkColorType,
1741 EXPECT_EQ(result, true);
1744 TEST_F(GLHelperPixelTest, YUVReadbackOptTest) {
1745 // This test uses the cb_command tracing events to detect how many
1746 // scaling passes are actually performed by the YUV readback pipeline.
1747 StartTracing(TRACE_DISABLED_BY_DEFAULT("cb_command"));
1749 TestYUVReadback(800,
1758 content::GLHelper::SCALER_QUALITY_FAST);
1760 std::map<std::string, int> event_counts;
1761 EndTracing(&event_counts);
1762 int draw_buffer_calls = event_counts["kDrawBuffersEXTImmediate"];
1763 int draw_arrays_calls = event_counts["kDrawArrays"];
1764 VLOG(1) << "Draw buffer calls: " << draw_buffer_calls;
1765 VLOG(1) << "DrawArrays calls: " << draw_arrays_calls;
1767 if (draw_buffer_calls) {
1768 // When using MRT, the YUV readback code should only
1769 // execute two draw arrays, and scaling should be integrated
1770 // into those two calls since we are using the FAST scalign
1772 EXPECT_EQ(2, draw_arrays_calls);
1774 // When not using MRT, there are three passes for the YUV,
1775 // and one for the scaling.
1776 EXPECT_EQ(4, draw_arrays_calls);
1780 TEST_F(GLHelperPixelTest, YUVReadbackTest) {
1781 int sizes[] = {2, 4, 14};
1782 for (int flip = 0; flip <= 1; flip++) {
1783 for (int use_mrt = 0; use_mrt <= 1; use_mrt++) {
1784 for (unsigned int x = 0; x < arraysize(sizes); x++) {
1785 for (unsigned int y = 0; y < arraysize(sizes); y++) {
1786 for (unsigned int ox = x; ox < arraysize(sizes); ox++) {
1787 for (unsigned int oy = y; oy < arraysize(sizes); oy++) {
1788 // If output is a subsection of the destination frame, (letterbox)
1789 // then try different variations of where the subsection goes.
1790 for (Margin xm = x < ox ? MarginLeft : MarginRight;
1792 xm = NextMargin(xm)) {
1793 for (Margin ym = y < oy ? MarginLeft : MarginRight;
1795 ym = NextMargin(ym)) {
1796 for (int pattern = 0; pattern < 3; pattern++) {
1797 TestYUVReadback(sizes[x],
1801 compute_margin(sizes[x], sizes[ox], xm),
1802 compute_margin(sizes[y], sizes[oy], ym),
1806 content::GLHelper::SCALER_QUALITY_GOOD);
1821 // Per pixel tests, all sizes are small so that we can print
1822 // out the generated bitmaps.
1823 TEST_F(GLHelperPixelTest, ScaleTest) {
1824 int sizes[] = {3, 6, 16};
1825 for (int flip = 0; flip <= 1; flip++) {
1826 for (size_t q_index = 0; q_index < arraysize(kQualities); q_index++) {
1827 for (int x = 0; x < 3; x++) {
1828 for (int y = 0; y < 3; y++) {
1829 for (int dst_x = 0; dst_x < 3; dst_x++) {
1830 for (int dst_y = 0; dst_y < 3; dst_y++) {
1831 for (int pattern = 0; pattern < 3; pattern++) {
1851 // Per pixel tests, all sizes are small so that we can print
1852 // out the generated bitmaps.
1853 TEST_F(GLHelperPixelTest, CropScaleReadbackAndCleanTextureTest) {
1854 const int kSizes[] = {3, 6, 16};
1855 const SkColorType kColorTypes[] = {
1856 kAlpha_8_SkColorType, kRGBA_8888_SkColorType, kBGRA_8888_SkColorType};
1857 for (size_t color_type = 0; color_type < arraysize(kColorTypes);
1859 // Test BEST and FAST qualities, skip GOOD
1860 for (size_t q_index = 0; q_index < arraysize(kQualities); q_index += 2) {
1861 for (size_t x = 0; x < arraysize(kSizes); x++) {
1862 for (size_t y = 0; y < arraysize(kSizes); y++) {
1863 for (size_t dst_x = 0; dst_x < arraysize(kSizes); dst_x++) {
1864 for (size_t dst_y = 0; dst_y < arraysize(kSizes); dst_y++) {
1865 for (int pattern = 0; pattern < 3; pattern++) {
1866 TestCropScaleReadbackAndCleanTexture(kSizes[x],
1871 kColorTypes[color_type],
1885 // Validate that all scaling generates valid pipelines.
1886 TEST_F(GLHelperTest, ValidateScalerPipelines) {
1887 int sizes[] = {7, 99, 128, 256, 512, 719, 720, 721, 1920, 2011, 3217, 4096};
1888 for (size_t q = 0; q < arraysize(kQualities); q++) {
1889 for (size_t x = 0; x < arraysize(sizes); x++) {
1890 for (size_t y = 0; y < arraysize(sizes); y++) {
1891 for (size_t dst_x = 0; dst_x < arraysize(sizes); dst_x++) {
1892 for (size_t dst_y = 0; dst_y < arraysize(sizes); dst_y++) {
1894 q, sizes[x], sizes[y], sizes[dst_x], sizes[dst_y]);
1905 // Make sure we don't create overly complicated pipelines
1906 // for a few common use cases.
1907 TEST_F(GLHelperTest, CheckSpecificPipelines) {
1908 // Upscale should be single pass.
1909 CheckPipeline(content::GLHelper::SCALER_QUALITY_GOOD,
1914 "1024x700 -> 1280x720 bilinear\n");
1915 // Slight downscale should use BILINEAR2X2.
1916 CheckPipeline(content::GLHelper::SCALER_QUALITY_GOOD,
1921 "1280x720 -> 1024x700 bilinear2x2\n");
1922 // Most common tab capture pipeline on the Pixel.
1923 // Should be using two BILINEAR3 passes.
1924 CheckPipeline(content::GLHelper::SCALER_QUALITY_GOOD,
1929 "2560x1476 -> 2560x720 bilinear3 Y\n"
1930 "2560x720 -> 1249x720 bilinear3 X\n");
1933 TEST_F(GLHelperTest, ScalerOpTest) {
1934 for (int allow3 = 0; allow3 <= 1; allow3++) {
1935 for (int dst = 1; dst < 2049; dst += 1 + (dst >> 3)) {
1936 for (int src = 1; src < 2049; src++) {
1937 TestAddOps(src, dst, allow3 == 1, (src & 1) == 1);
1939 LOG(ERROR) << "Failed for src=" << src << " dst=" << dst
1940 << " allow3=" << allow3;
1948 TEST_F(GLHelperTest, CheckOptimizations) {
1949 // Test in baseclass since it is friends with GLHelperScaling
1950 CheckOptimizationsTest();
1955 // These tests needs to run against a proper GL environment, so we
1956 // need to set it up before we can run the tests.
1957 int main(int argc, char** argv) {
1958 base::CommandLine::Init(argc, argv);
1959 base::TestSuite* suite = new content::ContentTestSuite(argc, argv);
1960 #if defined(OS_MACOSX)
1961 base::mac::ScopedNSAutoreleasePool pool;
1964 content::UnitTestTestSuite runner(suite);
1965 base::MessageLoop message_loop;
1966 return runner.Run();