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/files/file_util.h"
18 #include "base/json/json_reader.h"
19 #include "base/message_loop/message_loop.h"
20 #include "base/run_loop.h"
21 #include "base/strings/stringprintf.h"
22 #include "base/synchronization/waitable_event.h"
23 #include "base/test/launcher/unit_test_launcher.h"
24 #include "base/test/test_suite.h"
25 #include "base/time/time.h"
26 #include "base/trace_event/trace_event.h"
27 #include "content/common/gpu/client/gl_helper.h"
28 #include "content/common/gpu/client/gl_helper_readback_support.h"
29 #include "content/common/gpu/client/gl_helper_scaling.h"
30 #include "content/public/test/unittest_test_suite.h"
31 #include "content/test/content_test_suite.h"
32 #include "gpu/blink/webgraphicscontext3d_in_process_command_buffer_impl.h"
33 #include "media/base/video_frame.h"
34 #include "testing/gtest/include/gtest/gtest.h"
35 #include "third_party/skia/include/core/SkBitmap.h"
36 #include "third_party/skia/include/core/SkTypes.h"
37 #include "ui/gl/gl_implementation.h"
39 #if defined(OS_MACOSX)
40 #include "base/mac/scoped_nsautorelease_pool.h"
46 using blink::WebGraphicsContext3D
;
47 using gpu_blink::WebGraphicsContext3DInProcessCommandBufferImpl
;
49 content::GLHelper::ScalerQuality kQualities
[] = {
50 content::GLHelper::SCALER_QUALITY_BEST
,
51 content::GLHelper::SCALER_QUALITY_GOOD
,
52 content::GLHelper::SCALER_QUALITY_FAST
, };
54 const char* kQualityNames
[] = {"best", "good", "fast", };
56 class GLHelperTest
: public testing::Test
{
58 void SetUp() override
{
59 WebGraphicsContext3D::Attributes attributes
;
60 bool lose_context_when_out_of_memory
= false;
62 WebGraphicsContext3DInProcessCommandBufferImpl::CreateOffscreenContext(
63 attributes
, lose_context_when_out_of_memory
);
64 context_
->InitializeOnCurrentThread();
65 context_support_
= context_
->GetContextSupport();
67 new content::GLHelper(context_
->GetGLInterface(), context_support_
));
68 helper_scaling_
.reset(new content::GLHelperScaling(
69 context_
->GetGLInterface(), helper_
.get()));
72 void TearDown() override
{
73 helper_scaling_
.reset(NULL
);
78 void StartTracing(const std::string
& filter
) {
79 base::trace_event::TraceLog::GetInstance()->SetEnabled(
80 base::trace_event::TraceConfig(filter
,
81 base::trace_event::RECORD_UNTIL_FULL
),
82 base::trace_event::TraceLog::RECORDING_MODE
);
85 static void TraceDataCB(
86 const base::Callback
<void()>& callback
,
88 const scoped_refptr
<base::RefCountedString
>& json_events_str
,
89 bool has_more_events
) {
90 if (output
->size() > 1 && !json_events_str
->data().empty()) {
93 output
->append(json_events_str
->data());
94 if (!has_more_events
) {
99 // End tracing, return tracing data in a simple map
100 // of event name->counts.
101 void EndTracing(std::map
<std::string
, int>* event_counts
) {
102 std::string json_data
= "[";
103 base::trace_event::TraceLog::GetInstance()->SetDisabled();
104 base::RunLoop run_loop
;
105 base::trace_event::TraceLog::GetInstance()->Flush(
106 base::Bind(&GLHelperTest::TraceDataCB
,
107 run_loop
.QuitClosure(),
108 base::Unretained(&json_data
)));
110 json_data
.append("]");
112 std::string error_msg
;
113 scoped_ptr
<base::Value
> trace_data(
114 base::JSONReader::DeprecatedReadAndReturnError(json_data
, 0, NULL
,
117 << "JSON parsing failed (" << error_msg
<< ") JSON data:" << std::endl
120 base::ListValue
* list
;
121 CHECK(trace_data
->GetAsList(&list
));
122 for (size_t i
= 0; i
< list
->GetSize(); i
++) {
123 base::Value
* item
= NULL
;
124 if (list
->Get(i
, &item
)) {
125 base::DictionaryValue
* dict
;
126 CHECK(item
->GetAsDictionary(&dict
));
128 CHECK(dict
->GetString("name", &name
));
129 std::string trace_type
;
130 CHECK(dict
->GetString("ph", &trace_type
));
131 // Count all except END traces, as they come in BEGIN/END pairs.
132 if (trace_type
!= "E")
133 (*event_counts
)[name
]++;
134 VLOG(1) << "trace name: " << name
;
139 // Bicubic filter kernel function.
140 static float Bicubic(float x
) {
141 const float a
= -0.5;
146 return (a
+ 2) * x3
- (a
+ 3) * x2
+ 1;
148 return a
* x3
- 5 * a
* x2
+ 8 * a
* x
- 4 * a
;
154 // Look up a single channel value. Works for 4-channel and single channel
155 // bitmaps. Clamp x/y.
156 int Channel(SkBitmap
* pixels
, int x
, int y
, int c
) {
157 if (pixels
->bytesPerPixel() == 4) {
159 pixels
->getAddr32(std::max(0, std::min(x
, pixels
->width() - 1)),
160 std::max(0, std::min(y
, pixels
->height() - 1)));
161 return (*data
) >> (c
* 8) & 0xff;
163 DCHECK_EQ(pixels
->bytesPerPixel(), 1);
165 return *pixels
->getAddr8(std::max(0, std::min(x
, pixels
->width() - 1)),
166 std::max(0, std::min(y
, pixels
->height() - 1)));
170 // Set a single channel value. Works for 4-channel and single channel
171 // bitmaps. Clamp x/y.
172 void SetChannel(SkBitmap
* pixels
, int x
, int y
, int c
, int v
) {
175 DCHECK_LT(x
, pixels
->width());
176 DCHECK_LT(y
, pixels
->height());
177 if (pixels
->bytesPerPixel() == 4) {
178 uint32
* data
= pixels
->getAddr32(x
, y
);
179 v
= std::max(0, std::min(v
, 255));
180 *data
= (*data
& ~(0xffu
<< (c
* 8))) | (v
<< (c
* 8));
182 DCHECK_EQ(pixels
->bytesPerPixel(), 1);
184 uint8
* data
= pixels
->getAddr8(x
, y
);
185 v
= std::max(0, std::min(v
, 255));
190 // Print all the R, G, B or A values from an SkBitmap in a
191 // human-readable format.
192 void PrintChannel(SkBitmap
* pixels
, int c
) {
193 for (int y
= 0; y
< pixels
->height(); y
++) {
194 std::string formatted
;
195 for (int x
= 0; x
< pixels
->width(); x
++) {
196 formatted
.append(base::StringPrintf("%3d, ", Channel(pixels
, x
, y
, c
)));
198 LOG(ERROR
) << formatted
;
202 // Print out the individual steps of a scaler pipeline.
203 std::string
PrintStages(
204 const std::vector
<GLHelperScaling::ScalerStage
>& scaler_stages
) {
206 for (size_t i
= 0; i
< scaler_stages
.size(); i
++) {
207 ret
.append(base::StringPrintf("%dx%d -> %dx%d ",
208 scaler_stages
[i
].src_size
.width(),
209 scaler_stages
[i
].src_size
.height(),
210 scaler_stages
[i
].dst_size
.width(),
211 scaler_stages
[i
].dst_size
.height()));
212 bool xy_matters
= false;
213 switch (scaler_stages
[i
].shader
) {
214 case GLHelperScaling::SHADER_BILINEAR
:
215 ret
.append("bilinear");
217 case GLHelperScaling::SHADER_BILINEAR2
:
218 ret
.append("bilinear2");
221 case GLHelperScaling::SHADER_BILINEAR3
:
222 ret
.append("bilinear3");
225 case GLHelperScaling::SHADER_BILINEAR4
:
226 ret
.append("bilinear4");
229 case GLHelperScaling::SHADER_BILINEAR2X2
:
230 ret
.append("bilinear2x2");
232 case GLHelperScaling::SHADER_BICUBIC_UPSCALE
:
233 ret
.append("bicubic upscale");
236 case GLHelperScaling::SHADER_BICUBIC_HALF_1D
:
237 ret
.append("bicubic 1/2");
240 case GLHelperScaling::SHADER_PLANAR
:
241 ret
.append("planar");
243 case GLHelperScaling::SHADER_YUV_MRT_PASS1
:
244 ret
.append("rgb2yuv pass 1");
246 case GLHelperScaling::SHADER_YUV_MRT_PASS2
:
247 ret
.append("rgb2yuv pass 2");
252 if (scaler_stages
[i
].scale_x
) {
263 bool CheckScale(double scale
, int samples
, bool already_scaled
) {
264 // 1:1 is valid if there is one sample.
265 if (samples
== 1 && scale
== 1.0) {
268 // Is it an exact down-scale (50%, 25%, etc.?)
269 if (scale
== 2.0 * samples
) {
272 // Upscales, only valid if we haven't already scaled in this dimension.
273 if (!already_scaled
) {
274 // Is it a valid bilinear upscale?
275 if (samples
== 1 && scale
<= 1.0) {
278 // Multi-sample upscale-downscale combination?
279 if (scale
> samples
/ 2.0 && scale
< samples
) {
286 // Make sure that the stages of the scaler pipeline are sane.
287 void ValidateScalerStages(
288 content::GLHelper::ScalerQuality quality
,
289 const std::vector
<GLHelperScaling::ScalerStage
>& scaler_stages
,
290 const gfx::Size
& dst_size
,
291 const std::string
& message
) {
292 bool previous_error
= HasFailure();
293 // First, check that the input size for each stage is equal to
294 // the output size of the previous stage.
295 for (size_t i
= 1; i
< scaler_stages
.size(); i
++) {
296 EXPECT_EQ(scaler_stages
[i
- 1].dst_size
.width(),
297 scaler_stages
[i
].src_size
.width());
298 EXPECT_EQ(scaler_stages
[i
- 1].dst_size
.height(),
299 scaler_stages
[i
].src_size
.height());
300 EXPECT_EQ(scaler_stages
[i
].src_subrect
.x(), 0);
301 EXPECT_EQ(scaler_stages
[i
].src_subrect
.y(), 0);
302 EXPECT_EQ(scaler_stages
[i
].src_subrect
.width(),
303 scaler_stages
[i
].src_size
.width());
304 EXPECT_EQ(scaler_stages
[i
].src_subrect
.height(),
305 scaler_stages
[i
].src_size
.height());
308 // Check the output size matches the destination of the last stage
309 EXPECT_EQ(scaler_stages
[scaler_stages
.size() - 1].dst_size
.width(),
311 EXPECT_EQ(scaler_stages
[scaler_stages
.size() - 1].dst_size
.height(),
314 // Used to verify that up-scales are not attempted after some
316 bool scaled_x
= false;
317 bool scaled_y
= false;
319 for (size_t i
= 0; i
< scaler_stages
.size(); i
++) {
320 // Note: 2.0 means scaling down by 50%
322 static_cast<double>(scaler_stages
[i
].src_subrect
.width()) /
323 static_cast<double>(scaler_stages
[i
].dst_size
.width());
325 static_cast<double>(scaler_stages
[i
].src_subrect
.height()) /
326 static_cast<double>(scaler_stages
[i
].dst_size
.height());
331 // Codify valid scale operations.
332 switch (scaler_stages
[i
].shader
) {
333 case GLHelperScaling::SHADER_PLANAR
:
334 case GLHelperScaling::SHADER_YUV_MRT_PASS1
:
335 case GLHelperScaling::SHADER_YUV_MRT_PASS2
:
336 EXPECT_TRUE(false) << "Invalid shader.";
339 case GLHelperScaling::SHADER_BILINEAR
:
340 if (quality
!= content::GLHelper::SCALER_QUALITY_FAST
) {
345 case GLHelperScaling::SHADER_BILINEAR2
:
349 case GLHelperScaling::SHADER_BILINEAR3
:
353 case GLHelperScaling::SHADER_BILINEAR4
:
357 case GLHelperScaling::SHADER_BILINEAR2X2
:
361 case GLHelperScaling::SHADER_BICUBIC_UPSCALE
:
362 if (scaler_stages
[i
].scale_x
) {
363 EXPECT_LT(x_scale
, 1.0);
364 EXPECT_EQ(y_scale
, 1.0);
366 EXPECT_EQ(x_scale
, 1.0);
367 EXPECT_LT(y_scale
, 1.0);
370 case GLHelperScaling::SHADER_BICUBIC_HALF_1D
:
371 if (scaler_stages
[i
].scale_x
) {
372 EXPECT_EQ(x_scale
, 2.0);
373 EXPECT_EQ(y_scale
, 1.0);
375 EXPECT_EQ(x_scale
, 1.0);
376 EXPECT_EQ(y_scale
, 2.0);
381 if (!scaler_stages
[i
].scale_x
) {
382 std::swap(x_samples
, y_samples
);
386 EXPECT_TRUE(CheckScale(x_scale
, x_samples
, scaled_x
))
387 << "x_scale = " << x_scale
;
390 EXPECT_TRUE(CheckScale(y_scale
, y_samples
, scaled_y
))
391 << "y_scale = " << y_scale
;
394 if (x_scale
!= 1.0) {
397 if (y_scale
!= 1.0) {
402 if (HasFailure() && !previous_error
) {
403 LOG(ERROR
) << "Invalid scaler stages: " << message
;
404 LOG(ERROR
) << "Scaler stages:";
405 LOG(ERROR
) << PrintStages(scaler_stages
);
409 // Compares two bitmaps taking color types into account. Checks whether each
410 // component of each pixel is no more than |maxdiff| apart. If bitmaps are not
411 // similar enough, prints out |truth|, |other|, |source|, |scaler_stages|
413 void Compare(SkBitmap
* truth
,
417 const std::vector
<GLHelperScaling::ScalerStage
>& scaler_stages
,
418 std::string message
) {
419 EXPECT_EQ(truth
->width(), other
->width());
420 EXPECT_EQ(truth
->height(), other
->height());
421 bool swizzle
= (truth
->colorType() == kRGBA_8888_SkColorType
&&
422 other
->colorType() == kBGRA_8888_SkColorType
) ||
423 (truth
->colorType() == kBGRA_8888_SkColorType
&&
424 other
->colorType() == kRGBA_8888_SkColorType
);
425 EXPECT_TRUE(swizzle
|| truth
->colorType() == other
->colorType());
426 int bpp
= truth
->bytesPerPixel();
427 for (int x
= 0; x
< truth
->width(); x
++) {
428 for (int y
= 0; y
< truth
->height(); y
++) {
429 for (int c
= 0; c
< bpp
; c
++) {
430 int a
= Channel(truth
, x
, y
, c
);
431 // swizzle when comparing if needed
432 int b
= swizzle
&& (c
== 0 || c
== 2)
433 ? Channel(other
, x
, y
, (c
+ 2) & 2)
434 : Channel(other
, x
, y
, c
);
435 EXPECT_NEAR(a
, b
, maxdiff
) << " x=" << x
<< " y=" << y
<< " c=" << c
437 if (std::abs(a
- b
) > maxdiff
) {
438 LOG(ERROR
) << "-------expected--------";
439 for (int i
= 0; i
< bpp
; i
++) {
440 LOG(ERROR
) << "Channel " << i
<< ":";
441 PrintChannel(truth
, i
);
443 LOG(ERROR
) << "-------actual--------";
444 for (int i
= 0; i
< bpp
; i
++) {
445 LOG(ERROR
) << "Channel " << i
<< ":";
446 PrintChannel(other
, i
);
449 LOG(ERROR
) << "-------original--------";
450 for (int i
= 0; i
< source
->bytesPerPixel(); i
++) {
451 LOG(ERROR
) << "Channel " << i
<< ":";
452 PrintChannel(source
, i
);
455 LOG(ERROR
) << "-----Scaler stages------";
456 LOG(ERROR
) << PrintStages(scaler_stages
);
464 // Get a single R, G, B or A value as a float.
465 float ChannelAsFloat(SkBitmap
* pixels
, int x
, int y
, int c
) {
466 return Channel(pixels
, x
, y
, c
) / 255.0;
469 // Works like a GL_LINEAR lookup on an SkBitmap.
470 float Bilinear(SkBitmap
* pixels
, float x
, float y
, int c
) {
473 int base_x
= static_cast<int>(floorf(x
));
474 int base_y
= static_cast<int>(floorf(y
));
477 return (ChannelAsFloat(pixels
, base_x
, base_y
, c
) * (1 - x
) * (1 - y
) +
478 ChannelAsFloat(pixels
, base_x
+ 1, base_y
, c
) * x
* (1 - y
) +
479 ChannelAsFloat(pixels
, base_x
, base_y
+ 1, c
) * (1 - x
) * y
+
480 ChannelAsFloat(pixels
, base_x
+ 1, base_y
+ 1, c
) * x
* y
);
483 // Encodes an RGBA bitmap to grayscale.
484 // Reference implementation for
485 // GLHelper::CopyToTextureImpl::EncodeTextureAsGrayscale.
486 void EncodeToGrayscaleSlow(SkBitmap
* input
, SkBitmap
* output
) {
487 const float kRGBtoGrayscaleColorWeights
[3] = {0.213f
, 0.715f
, 0.072f
};
488 CHECK_EQ(kAlpha_8_SkColorType
, output
->colorType());
489 CHECK_EQ(input
->width(), output
->width());
490 CHECK_EQ(input
->height(), output
->height());
491 CHECK_EQ(input
->colorType(), kRGBA_8888_SkColorType
);
493 for (int dst_y
= 0; dst_y
< output
->height(); dst_y
++) {
494 for (int dst_x
= 0; dst_x
< output
->width(); dst_x
++) {
495 float c0
= ChannelAsFloat(input
, dst_x
, dst_y
, 0);
496 float c1
= ChannelAsFloat(input
, dst_x
, dst_y
, 1);
497 float c2
= ChannelAsFloat(input
, dst_x
, dst_y
, 2);
498 float value
= c0
* kRGBtoGrayscaleColorWeights
[0] +
499 c1
* kRGBtoGrayscaleColorWeights
[1] +
500 c2
* kRGBtoGrayscaleColorWeights
[2];
502 output
, dst_x
, dst_y
, 0, static_cast<int>(value
* 255.0f
+ 0.5f
));
507 // Very slow bicubic / bilinear scaler for reference.
508 void ScaleSlow(SkBitmap
* input
,
510 content::GLHelper::ScalerQuality quality
) {
511 float xscale
= static_cast<float>(input
->width()) / output
->width();
512 float yscale
= static_cast<float>(input
->height()) / output
->height();
513 float clamped_xscale
= xscale
< 1.0 ? 1.0 : 1.0 / xscale
;
514 float clamped_yscale
= yscale
< 1.0 ? 1.0 : 1.0 / yscale
;
515 for (int dst_y
= 0; dst_y
< output
->height(); dst_y
++) {
516 for (int dst_x
= 0; dst_x
< output
->width(); dst_x
++) {
517 for (int channel
= 0; channel
< 4; channel
++) {
518 float dst_x_in_src
= (dst_x
+ 0.5f
) * xscale
;
519 float dst_y_in_src
= (dst_y
+ 0.5f
) * yscale
;
524 case content::GLHelper::SCALER_QUALITY_BEST
:
525 for (int src_y
= -10; src_y
< input
->height() + 10; ++src_y
) {
527 Bicubic((src_y
+ 0.5f
- dst_y_in_src
) * clamped_yscale
);
528 if (coeff_y
== 0.0f
) {
531 for (int src_x
= -10; src_x
< input
->width() + 10; ++src_x
) {
534 Bicubic((src_x
+ 0.5f
- dst_x_in_src
) * clamped_xscale
);
539 float c
= ChannelAsFloat(input
, src_x
, src_y
, channel
);
545 case content::GLHelper::SCALER_QUALITY_GOOD
: {
546 int xshift
= 0, yshift
= 0;
547 while ((output
->width() << xshift
) < input
->width()) {
550 while ((output
->height() << yshift
) < input
->height()) {
553 int xmag
= 1 << xshift
;
554 int ymag
= 1 << yshift
;
555 if (xmag
== 4 && output
->width() * 3 >= input
->width()) {
558 if (ymag
== 4 && output
->height() * 3 >= input
->height()) {
561 for (int x
= 0; x
< xmag
; x
++) {
562 for (int y
= 0; y
< ymag
; y
++) {
563 value
+= Bilinear(input
,
564 (dst_x
* xmag
+ x
+ 0.5) * xscale
/ xmag
,
565 (dst_y
* ymag
+ y
+ 0.5) * yscale
/ ymag
,
573 case content::GLHelper::SCALER_QUALITY_FAST
:
574 value
= Bilinear(input
, dst_x_in_src
, dst_y_in_src
, channel
);
582 static_cast<int>(value
* 255.0f
+ 0.5f
));
588 void FlipSKBitmap(SkBitmap
* bitmap
) {
589 int bpp
= bitmap
->bytesPerPixel();
590 DCHECK(bpp
== 4 || bpp
== 1);
592 int bottom_line
= bitmap
->height() - 1;
593 while (top_line
< bottom_line
) {
594 for (int x
= 0; x
< bitmap
->width(); x
++) {
595 bpp
== 4 ? std::swap(*bitmap
->getAddr32(x
, top_line
),
596 *bitmap
->getAddr32(x
, bottom_line
))
597 : std::swap(*bitmap
->getAddr8(x
, top_line
),
598 *bitmap
->getAddr8(x
, bottom_line
));
605 // Swaps red and blue channels in each pixel in a 32-bit bitmap.
606 void SwizzleSKBitmap(SkBitmap
* bitmap
) {
607 int bpp
= bitmap
->bytesPerPixel();
609 for (int y
= 0; y
< bitmap
->height(); y
++) {
610 for (int x
= 0; x
< bitmap
->width(); x
++) {
611 // Swap channels 0 and 2 (red and blue)
612 int c0
= Channel(bitmap
, x
, y
, 0);
613 int c2
= Channel(bitmap
, x
, y
, 2);
614 SetChannel(bitmap
, x
, y
, 2, c0
);
615 SetChannel(bitmap
, x
, y
, 0, c2
);
620 // gl_helper scales recursively, so we'll need to do that
621 // in the reference implementation too.
622 void ScaleSlowRecursive(SkBitmap
* input
,
624 content::GLHelper::ScalerQuality quality
) {
625 if (quality
== content::GLHelper::SCALER_QUALITY_FAST
||
626 quality
== content::GLHelper::SCALER_QUALITY_GOOD
) {
627 ScaleSlow(input
, output
, quality
);
631 float xscale
= static_cast<float>(output
->width()) / input
->width();
633 // This corresponds to all the operations we can do directly.
634 float yscale
= static_cast<float>(output
->height()) / input
->height();
635 if ((xscale
== 1.0f
&& yscale
== 1.0f
) ||
636 (xscale
== 0.5f
&& yscale
== 1.0f
) ||
637 (xscale
== 1.0f
&& yscale
== 0.5f
) ||
638 (xscale
>= 1.0f
&& yscale
== 1.0f
) ||
639 (xscale
== 1.0f
&& yscale
>= 1.0f
)) {
640 ScaleSlow(input
, output
, quality
);
644 // Now we break the problem down into smaller pieces, using the
645 // operations available.
646 int xtmp
= input
->width();
647 int ytmp
= input
->height();
649 if (output
->height() != input
->height()) {
650 ytmp
= output
->height();
651 while (ytmp
< input
->height() && ytmp
* 2 != input
->height()) {
655 xtmp
= output
->width();
656 while (xtmp
< input
->width() && xtmp
* 2 != input
->width()) {
662 tmp
.allocN32Pixels(xtmp
, ytmp
);
664 ScaleSlowRecursive(input
, &tmp
, quality
);
665 ScaleSlowRecursive(&tmp
, output
, quality
);
668 // Creates an RGBA SkBitmap
669 scoped_ptr
<SkBitmap
> CreateTestBitmap(int width
,
672 scoped_ptr
<SkBitmap
> bitmap(new SkBitmap
);
673 bitmap
->allocPixels(SkImageInfo::Make(
674 width
, height
, kRGBA_8888_SkColorType
, kPremul_SkAlphaType
));
676 for (int x
= 0; x
< width
; ++x
) {
677 for (int y
= 0; y
< height
; ++y
) {
678 switch (test_pattern
) {
679 case 0: // Smooth test pattern
680 SetChannel(bitmap
.get(), x
, y
, 0, x
* 10);
681 SetChannel(bitmap
.get(), x
, y
, 0, y
== 0 ? x
* 50 : x
* 10);
682 SetChannel(bitmap
.get(), x
, y
, 1, y
* 10);
683 SetChannel(bitmap
.get(), x
, y
, 2, (x
+ y
) * 10);
684 SetChannel(bitmap
.get(), x
, y
, 3, 255);
686 case 1: // Small blocks
687 SetChannel(bitmap
.get(), x
, y
, 0, x
& 1 ? 255 : 0);
688 SetChannel(bitmap
.get(), x
, y
, 1, y
& 1 ? 255 : 0);
689 SetChannel(bitmap
.get(), x
, y
, 2, (x
+ y
) & 1 ? 255 : 0);
690 SetChannel(bitmap
.get(), x
, y
, 3, 255);
692 case 2: // Medium blocks
693 SetChannel(bitmap
.get(), x
, y
, 0, 10 + x
/ 2 * 50);
694 SetChannel(bitmap
.get(), x
, y
, 1, 10 + y
/ 3 * 50);
695 SetChannel(bitmap
.get(), x
, y
, 2, (x
+ y
) / 5 * 50 + 5);
696 SetChannel(bitmap
.get(), x
, y
, 3, 255);
701 return bitmap
.Pass();
704 // Binds texture and framebuffer and loads the bitmap pixels into the texture.
705 void BindTextureAndFrameBuffer(WebGLId texture
,
710 context_
->bindFramebuffer(GL_FRAMEBUFFER
, framebuffer
);
711 context_
->bindTexture(GL_TEXTURE_2D
, texture
);
712 context_
->texImage2D(GL_TEXTURE_2D
,
720 bitmap
->getPixels());
723 // Create a test image, transform it using
724 // GLHelper::CropScaleReadbackAndCleanTexture and a reference implementation
725 // and compare the results.
726 void TestCropScaleReadbackAndCleanTexture(int xsize
,
731 SkColorType out_color_type
,
733 size_t quality_index
) {
734 DCHECK(out_color_type
== kAlpha_8_SkColorType
||
735 out_color_type
== kRGBA_8888_SkColorType
||
736 out_color_type
== kBGRA_8888_SkColorType
);
737 WebGLId src_texture
= context_
->createTexture();
738 WebGLId framebuffer
= context_
->createFramebuffer();
739 scoped_ptr
<SkBitmap
> input_pixels
=
740 CreateTestBitmap(xsize
, ysize
, test_pattern
).Pass();
741 BindTextureAndFrameBuffer(
742 src_texture
, framebuffer
, input_pixels
.get(), xsize
, ysize
);
744 std::string message
= base::StringPrintf(
746 "output size: %dx%d "
747 "pattern: %d , quality: %s, "
748 "out_color_type: %d",
754 kQualityNames
[quality_index
],
757 // Transform the bitmap using GLHelper::CropScaleReadbackAndCleanTexture.
758 SkBitmap output_pixels
;
759 output_pixels
.allocPixels(SkImageInfo::Make(
760 scaled_xsize
, scaled_ysize
, out_color_type
, kPremul_SkAlphaType
));
761 base::RunLoop run_loop
;
762 gfx::Size encoded_texture_size
;
763 helper_
->CropScaleReadbackAndCleanTexture(
765 gfx::Size(xsize
, ysize
),
766 gfx::Rect(xsize
, ysize
),
767 gfx::Size(scaled_xsize
, scaled_ysize
),
768 static_cast<unsigned char*>(output_pixels
.getPixels()),
770 base::Bind(&callcallback
, run_loop
.QuitClosure()),
771 kQualities
[quality_index
]);
773 // CropScaleReadbackAndCleanTexture flips the pixels. Flip them back.
774 FlipSKBitmap(&output_pixels
);
776 // If the bitmap shouldn't have changed - compare against input.
777 if (xsize
== scaled_xsize
&& ysize
== scaled_ysize
&&
778 out_color_type
!= kAlpha_8_SkColorType
) {
779 const std::vector
<GLHelperScaling::ScalerStage
> dummy_stages
;
780 Compare(input_pixels
.get(),
785 message
+ " comparing against input");
789 // Now transform the bitmap using the reference implementation.
790 SkBitmap scaled_pixels
;
791 scaled_pixels
.allocPixels(SkImageInfo::Make(scaled_xsize
,
793 kRGBA_8888_SkColorType
,
794 kPremul_SkAlphaType
));
795 SkBitmap truth_pixels
;
798 input_pixels
.get(), &scaled_pixels
, kQualities
[quality_index
]);
799 // Step 2: Encode to grayscale if needed.
800 if (out_color_type
== kAlpha_8_SkColorType
) {
801 truth_pixels
.allocPixels(SkImageInfo::Make(
802 scaled_xsize
, scaled_ysize
, out_color_type
, kPremul_SkAlphaType
));
803 EncodeToGrayscaleSlow(&scaled_pixels
, &truth_pixels
);
805 truth_pixels
= scaled_pixels
;
808 // Now compare the results.
809 SkAutoLockPixels
lock_input(truth_pixels
);
810 const std::vector
<GLHelperScaling::ScalerStage
> dummy_stages
;
811 Compare(&truth_pixels
,
816 message
+ " comparing against transformed/scaled");
818 context_
->deleteTexture(src_texture
);
819 context_
->deleteFramebuffer(framebuffer
);
822 // Scaling test: Create a test image, scale it using GLHelperScaling
823 // and a reference implementation and compare the results.
824 void TestScale(int xsize
,
829 size_t quality_index
,
831 WebGLId src_texture
= context_
->createTexture();
832 WebGLId framebuffer
= context_
->createFramebuffer();
833 scoped_ptr
<SkBitmap
> input_pixels
=
834 CreateTestBitmap(xsize
, ysize
, test_pattern
).Pass();
835 BindTextureAndFrameBuffer(
836 src_texture
, framebuffer
, input_pixels
.get(), xsize
, ysize
);
838 std::string message
= base::StringPrintf(
840 "output size: %dx%d "
841 "pattern: %d quality: %s",
847 kQualityNames
[quality_index
]);
849 std::vector
<GLHelperScaling::ScalerStage
> stages
;
850 helper_scaling_
->ComputeScalerStages(kQualities
[quality_index
],
851 gfx::Size(xsize
, ysize
),
852 gfx::Rect(0, 0, xsize
, ysize
),
853 gfx::Size(scaled_xsize
, scaled_ysize
),
857 ValidateScalerStages(kQualities
[quality_index
],
859 gfx::Size(scaled_xsize
, scaled_ysize
),
862 WebGLId dst_texture
=
863 helper_
->CopyAndScaleTexture(src_texture
,
864 gfx::Size(xsize
, ysize
),
865 gfx::Size(scaled_xsize
, scaled_ysize
),
867 kQualities
[quality_index
]);
869 SkBitmap output_pixels
;
870 output_pixels
.allocPixels(SkImageInfo::Make(scaled_xsize
,
872 kRGBA_8888_SkColorType
,
873 kPremul_SkAlphaType
));
875 helper_
->ReadbackTextureSync(
877 gfx::Rect(0, 0, scaled_xsize
, scaled_ysize
),
878 static_cast<unsigned char*>(output_pixels
.getPixels()),
879 kRGBA_8888_SkColorType
);
881 // Flip the pixels back.
882 FlipSKBitmap(&output_pixels
);
885 // If the bitmap shouldn't have changed - compare against input.
886 if (xsize
== scaled_xsize
&& ysize
== scaled_ysize
) {
887 Compare(input_pixels
.get(),
892 message
+ " comparing against input");
896 // Now scale the bitmap using the reference implementation.
897 SkBitmap truth_pixels
;
898 truth_pixels
.allocPixels(SkImageInfo::Make(scaled_xsize
,
900 kRGBA_8888_SkColorType
,
901 kPremul_SkAlphaType
));
903 input_pixels
.get(), &truth_pixels
, kQualities
[quality_index
]);
904 Compare(&truth_pixels
,
909 message
+ " comparing against scaled");
911 context_
->deleteTexture(src_texture
);
912 context_
->deleteTexture(dst_texture
);
913 context_
->deleteFramebuffer(framebuffer
);
916 // Create a scaling pipeline and check that it is made up of
917 // valid scaling operations.
918 void TestScalerPipeline(size_t quality
,
923 std::vector
<GLHelperScaling::ScalerStage
> stages
;
924 helper_scaling_
->ComputeScalerStages(kQualities
[quality
],
925 gfx::Size(xsize
, ysize
),
926 gfx::Rect(0, 0, xsize
, ysize
),
927 gfx::Size(dst_xsize
, dst_ysize
),
931 ValidateScalerStages(kQualities
[quality
],
933 gfx::Size(dst_xsize
, dst_ysize
),
936 "output size: %dx%d "
942 kQualityNames
[quality
]));
945 // Create a scaling pipeline and make sure that the steps
946 // are exactly the steps we expect.
947 void CheckPipeline(content::GLHelper::ScalerQuality quality
,
952 const std::string
& description
) {
953 std::vector
<GLHelperScaling::ScalerStage
> stages
;
954 helper_scaling_
->ComputeScalerStages(quality
,
955 gfx::Size(xsize
, ysize
),
956 gfx::Rect(0, 0, xsize
, ysize
),
957 gfx::Size(dst_xsize
, dst_ysize
),
961 ValidateScalerStages(content::GLHelper::SCALER_QUALITY_GOOD
,
963 gfx::Size(dst_xsize
, dst_ysize
),
965 EXPECT_EQ(PrintStages(stages
), description
);
968 // Note: Left/Right means Top/Bottom when used for Y dimension.
976 static Margin
NextMargin(Margin m
) {
983 return MarginInvalid
;
985 return MarginInvalid
;
989 int compute_margin(int insize
, int outsize
, Margin m
) {
990 int available
= outsize
- insize
;
993 EXPECT_TRUE(false) << "This should not happen.";
998 return (available
/ 2) & ~1;
1004 // Convert 0.0 - 1.0 to 0 - 255
1005 int float_to_byte(float v
) {
1006 int ret
= static_cast<int>(floorf(v
* 255.0f
+ 0.5f
));
1016 static void callcallback(const base::Callback
<void()>& callback
,
1021 void PrintPlane(unsigned char* plane
, int xsize
, int stride
, int ysize
) {
1022 for (int y
= 0; y
< ysize
; y
++) {
1023 std::string formatted
;
1024 for (int x
= 0; x
< xsize
; x
++) {
1025 formatted
.append(base::StringPrintf("%3d, ", plane
[y
* stride
+ x
]));
1027 LOG(ERROR
) << formatted
<< " (" << (plane
+ y
* stride
) << ")";
1031 // Compare two planes make sure that each component of each pixel
1032 // is no more than |maxdiff| apart.
1033 void ComparePlane(unsigned char* truth
,
1035 unsigned char* other
,
1041 std::string message
) {
1042 for (int x
= 0; x
< xsize
; x
++) {
1043 for (int y
= 0; y
< ysize
; y
++) {
1044 int a
= other
[y
* other_stride
+ x
];
1045 int b
= truth
[y
* truth_stride
+ x
];
1046 EXPECT_NEAR(a
, b
, maxdiff
) << " x=" << x
<< " y=" << y
<< " "
1048 if (std::abs(a
- b
) > maxdiff
) {
1049 LOG(ERROR
) << "-------expected--------";
1050 PrintPlane(truth
, xsize
, truth_stride
, ysize
);
1051 LOG(ERROR
) << "-------actual--------";
1052 PrintPlane(other
, xsize
, other_stride
, ysize
);
1054 LOG(ERROR
) << "-------before yuv conversion: red--------";
1055 PrintChannel(source
, 0);
1056 LOG(ERROR
) << "-------before yuv conversion: green------";
1057 PrintChannel(source
, 1);
1058 LOG(ERROR
) << "-------before yuv conversion: blue-------";
1059 PrintChannel(source
, 2);
1067 void DrawGridToBitmap(int w
, int h
,
1068 SkColor background_color
,
1073 ASSERT_GT(grid_pitch
, 0);
1074 ASSERT_GT(grid_width
, 0);
1075 ASSERT_NE(background_color
, grid_color
);
1077 for (int y
= 0; y
< h
; ++y
) {
1078 bool y_on_grid
= ((y
% grid_pitch
) < grid_width
);
1080 for (int x
= 0; x
< w
; ++x
) {
1081 bool on_grid
= (y_on_grid
|| ((x
% grid_pitch
) < grid_width
));
1083 if (bmp
.colorType() == kRGBA_8888_SkColorType
||
1084 bmp
.colorType() == kBGRA_8888_SkColorType
) {
1085 *bmp
.getAddr32(x
, y
) = (on_grid
? grid_color
: background_color
);
1086 } else if (bmp
.colorType() == kRGB_565_SkColorType
) {
1087 *bmp
.getAddr16(x
, y
) = (on_grid
? grid_color
: background_color
);
1093 void DrawCheckerToBitmap(int w
, int h
,
1094 SkColor color1
, SkColor color2
,
1095 int rect_w
, int rect_h
,
1097 ASSERT_GT(rect_w
, 0);
1098 ASSERT_GT(rect_h
, 0);
1099 ASSERT_NE(color1
, color2
);
1101 for (int y
= 0; y
< h
; ++y
) {
1102 bool y_bit
= (((y
/ rect_h
) & 0x1) == 0);
1104 for (int x
= 0; x
< w
; ++x
) {
1105 bool x_bit
= (((x
/ rect_w
) & 0x1) == 0);
1107 bool use_color2
= (x_bit
!= y_bit
); // xor
1108 if (bmp
.colorType() == kRGBA_8888_SkColorType
||
1109 bmp
.colorType() == kBGRA_8888_SkColorType
) {
1110 *bmp
.getAddr32(x
, y
) = (use_color2
? color2
: color1
);
1111 } else if (bmp
.colorType() == kRGB_565_SkColorType
) {
1112 *bmp
.getAddr16(x
, y
) = (use_color2
? color2
: color1
);
1118 bool ColorComponentsClose(SkColor component1
,
1120 SkColorType color_type
) {
1121 int c1
= static_cast<int>(component1
);
1122 int c2
= static_cast<int>(component2
);
1123 bool result
= false;
1124 switch (color_type
) {
1125 case kRGBA_8888_SkColorType
:
1126 case kBGRA_8888_SkColorType
:
1127 result
= (std::abs(c1
- c2
) == 0);
1129 case kRGB_565_SkColorType
:
1130 result
= (std::abs(c1
- c2
) <= 7);
1138 bool ColorsClose(SkColor color1
, SkColor color2
, SkColorType color_type
) {
1139 bool red
= ColorComponentsClose(SkColorGetR(color1
),
1140 SkColorGetR(color2
), color_type
);
1141 bool green
= ColorComponentsClose(SkColorGetG(color1
),
1142 SkColorGetG(color2
), color_type
);
1143 bool blue
= ColorComponentsClose(SkColorGetB(color1
),
1144 SkColorGetB(color2
), color_type
);
1145 bool alpha
= ColorComponentsClose(SkColorGetA(color1
),
1146 SkColorGetA(color2
), color_type
);
1147 if (color_type
== kRGB_565_SkColorType
) {
1148 return red
&& blue
&& green
;
1150 return red
&& blue
&& green
&& alpha
;
1153 bool IsEqual(const SkBitmap
& bmp1
, const SkBitmap
& bmp2
) {
1154 if (bmp1
.isNull() && bmp2
.isNull())
1156 if (bmp1
.width() != bmp2
.width() ||
1157 bmp1
.height() != bmp2
.height()) {
1158 LOG(ERROR
) << "Bitmap geometry check failure";
1161 if (bmp1
.colorType() != bmp2
.colorType())
1164 SkAutoLockPixels
lock1(bmp1
);
1165 SkAutoLockPixels
lock2(bmp2
);
1166 if (!bmp1
.getPixels() || !bmp2
.getPixels()) {
1167 LOG(ERROR
) << "Empty Bitmap!";
1170 for (int y
= 0; y
< bmp1
.height(); ++y
) {
1171 for (int x
= 0; x
< bmp1
.width(); ++x
) {
1172 if (!ColorsClose(bmp1
.getColor(x
,y
),
1174 bmp1
.colorType())) {
1175 LOG(ERROR
) << "Bitmap color comparision failure";
1183 void BindAndAttachTextureWithPixels(GLuint src_texture
,
1184 SkColorType color_type
,
1185 const gfx::Size
& src_size
,
1186 const SkBitmap
& input_pixels
) {
1187 context_
->bindTexture(GL_TEXTURE_2D
, src_texture
);
1189 switch (color_type
) {
1190 case kBGRA_8888_SkColorType
:
1191 format
= GL_BGRA_EXT
;
1193 case kRGBA_8888_SkColorType
:
1196 case kRGB_565_SkColorType
:
1202 GLenum type
= (color_type
== kRGB_565_SkColorType
) ?
1203 GL_UNSIGNED_SHORT_5_6_5
: GL_UNSIGNED_BYTE
;
1204 context_
->texImage2D(GL_TEXTURE_2D
,
1212 input_pixels
.getPixels());
1215 void ReadBackTexture(GLuint src_texture
,
1216 const gfx::Size
& src_size
,
1217 unsigned char* pixels
,
1218 SkColorType color_type
,
1221 base::RunLoop run_loop
;
1222 helper_
->ReadbackTextureAsync(src_texture
,
1226 base::Bind(&callcallback
,
1227 run_loop
.QuitClosure()));
1230 helper_
->ReadbackTextureSync(src_texture
,
1231 gfx::Rect(src_size
),
1236 // Test basic format readback.
1237 bool TestTextureFormatReadback(const gfx::Size
& src_size
,
1238 SkColorType color_type
,
1241 SkImageInfo::Make(src_size
.width(),
1244 kPremul_SkAlphaType
);
1245 if (!helper_
->IsReadbackConfigSupported(color_type
)) {
1246 LOG(INFO
) << "Skipping test format not supported" << color_type
;
1249 WebGLId src_texture
= context_
->createTexture();
1250 SkBitmap input_pixels
;
1251 input_pixels
.allocPixels(info
);
1252 // Test Pattern-1, Fill with Plain color pattern.
1253 // Erase the input bitmap with red color.
1254 input_pixels
.eraseColor(SK_ColorRED
);
1255 BindAndAttachTextureWithPixels(src_texture
,
1259 SkBitmap output_pixels
;
1260 output_pixels
.allocPixels(info
);
1261 // Initialize the output bitmap with Green color.
1262 // When the readback is over output bitmap should have the red color.
1263 output_pixels
.eraseColor(SK_ColorGREEN
);
1264 uint8
* pixels
= static_cast<uint8
*>(output_pixels
.getPixels());
1265 ReadBackTexture(src_texture
, src_size
, pixels
, color_type
, async
);
1266 bool result
= IsEqual(input_pixels
, output_pixels
);
1268 LOG(ERROR
) << "Bitmap comparision failure Pattern-1";
1271 const int rect_w
= 10, rect_h
= 4, src_grid_pitch
= 10, src_grid_width
= 4;
1272 const SkColor color1
= SK_ColorRED
, color2
= SK_ColorBLUE
;
1273 // Test Pattern-2, Fill with Grid Pattern.
1274 DrawGridToBitmap(src_size
.width(), src_size
.height(),
1276 src_grid_pitch
, src_grid_width
,
1278 BindAndAttachTextureWithPixels(src_texture
,
1282 ReadBackTexture(src_texture
, src_size
, pixels
, color_type
, async
);
1283 result
= IsEqual(input_pixels
, output_pixels
);
1285 LOG(ERROR
) << "Bitmap comparision failure Pattern-2";
1288 // Test Pattern-3, Fill with CheckerBoard Pattern.
1289 DrawCheckerToBitmap(src_size
.width(),
1292 color2
, rect_w
, rect_h
, input_pixels
);
1293 BindAndAttachTextureWithPixels(src_texture
,
1297 ReadBackTexture(src_texture
, src_size
, pixels
, color_type
, async
);
1298 result
= IsEqual(input_pixels
, output_pixels
);
1300 LOG(ERROR
) << "Bitmap comparision failure Pattern-3";
1303 context_
->deleteTexture(src_texture
);
1310 // YUV readback test. Create a test pattern, convert to YUV
1311 // with reference implementation and compare to what gl_helper
1313 void TestYUVReadback(int xsize
,
1322 content::GLHelper::ScalerQuality quality
) {
1323 WebGLId src_texture
= context_
->createTexture();
1324 SkBitmap input_pixels
;
1325 input_pixels
.allocN32Pixels(xsize
, ysize
);
1327 for (int x
= 0; x
< xsize
; ++x
) {
1328 for (int y
= 0; y
< ysize
; ++y
) {
1329 switch (test_pattern
) {
1330 case 0: // Smooth test pattern
1331 SetChannel(&input_pixels
, x
, y
, 0, x
* 10);
1332 SetChannel(&input_pixels
, x
, y
, 1, y
* 10);
1333 SetChannel(&input_pixels
, x
, y
, 2, (x
+ y
) * 10);
1334 SetChannel(&input_pixels
, x
, y
, 3, 255);
1336 case 1: // Small blocks
1337 SetChannel(&input_pixels
, x
, y
, 0, x
& 1 ? 255 : 0);
1338 SetChannel(&input_pixels
, x
, y
, 1, y
& 1 ? 255 : 0);
1339 SetChannel(&input_pixels
, x
, y
, 2, (x
+ y
) & 1 ? 255 : 0);
1340 SetChannel(&input_pixels
, x
, y
, 3, 255);
1342 case 2: // Medium blocks
1343 SetChannel(&input_pixels
, x
, y
, 0, 10 + x
/ 2 * 50);
1344 SetChannel(&input_pixels
, x
, y
, 1, 10 + y
/ 3 * 50);
1345 SetChannel(&input_pixels
, x
, y
, 2, (x
+ y
) / 5 * 50 + 5);
1346 SetChannel(&input_pixels
, x
, y
, 3, 255);
1352 context_
->bindTexture(GL_TEXTURE_2D
, src_texture
);
1353 context_
->texImage2D(GL_TEXTURE_2D
,
1361 input_pixels
.getPixels());
1363 gpu::Mailbox mailbox
;
1364 context_
->genMailboxCHROMIUM(mailbox
.name
);
1365 EXPECT_FALSE(mailbox
.IsZero());
1366 context_
->produceTextureCHROMIUM(GL_TEXTURE_2D
, mailbox
.name
);
1367 uint32 sync_point
= context_
->insertSyncPoint();
1369 std::string message
= base::StringPrintf(
1370 "input size: %dx%d "
1371 "output size: %dx%d "
1373 "pattern: %d %s %s",
1381 flip
? "flip" : "noflip",
1382 flip
? "mrt" : "nomrt");
1383 scoped_ptr
<ReadbackYUVInterface
> yuv_reader(
1384 helper_
->CreateReadbackPipelineYUV(
1386 gfx::Size(xsize
, ysize
),
1387 gfx::Rect(0, 0, xsize
, ysize
),
1388 gfx::Size(xsize
, ysize
),
1392 scoped_refptr
<media::VideoFrame
> output_frame
=
1393 media::VideoFrame::CreateFrame(
1394 media::PIXEL_FORMAT_YV12
,
1395 // The coded size of the output frame is rounded up to the next
1396 // 16-byte boundary. This tests that the readback is being
1397 // positioned inside the frame's visible region, and not dependent
1398 // on its coded size.
1399 gfx::Size((output_xsize
+ 15) & ~15, (output_ysize
+ 15) & ~15),
1400 gfx::Rect(0, 0, output_xsize
, output_ysize
),
1401 gfx::Size(output_xsize
, output_ysize
),
1402 base::TimeDelta::FromSeconds(0));
1403 scoped_refptr
<media::VideoFrame
> truth_frame
=
1404 media::VideoFrame::CreateFrame(
1405 media::PIXEL_FORMAT_YV12
, gfx::Size(output_xsize
, output_ysize
),
1406 gfx::Rect(0, 0, output_xsize
, output_ysize
),
1407 gfx::Size(output_xsize
, output_ysize
),
1408 base::TimeDelta::FromSeconds(0));
1410 base::RunLoop run_loop
;
1411 yuv_reader
->ReadbackYUV(mailbox
,
1414 gfx::Point(xmargin
, ymargin
),
1415 base::Bind(&callcallback
, run_loop
.QuitClosure()));
1419 FlipSKBitmap(&input_pixels
);
1422 unsigned char* Y
= truth_frame
->visible_data(media::VideoFrame::kYPlane
);
1423 unsigned char* U
= truth_frame
->visible_data(media::VideoFrame::kUPlane
);
1424 unsigned char* V
= truth_frame
->visible_data(media::VideoFrame::kVPlane
);
1425 int32 y_stride
= truth_frame
->stride(media::VideoFrame::kYPlane
);
1426 int32 u_stride
= truth_frame
->stride(media::VideoFrame::kUPlane
);
1427 int32 v_stride
= truth_frame
->stride(media::VideoFrame::kVPlane
);
1428 memset(Y
, 0x00, y_stride
* output_ysize
);
1429 memset(U
, 0x80, u_stride
* output_ysize
/ 2);
1430 memset(V
, 0x80, v_stride
* output_ysize
/ 2);
1432 const float kRGBtoYColorWeights
[] = {0.257f
, 0.504f
, 0.098f
, 0.0625f
};
1433 const float kRGBtoUColorWeights
[] = {-0.148f
, -0.291f
, 0.439f
, 0.5f
};
1434 const float kRGBtoVColorWeights
[] = {0.439f
, -0.368f
, -0.071f
, 0.5f
};
1436 for (int y
= 0; y
< ysize
; y
++) {
1437 for (int x
= 0; x
< xsize
; x
++) {
1438 Y
[(y
+ ymargin
) * y_stride
+ x
+ xmargin
] = float_to_byte(
1439 ChannelAsFloat(&input_pixels
, x
, y
, 0) * kRGBtoYColorWeights
[0] +
1440 ChannelAsFloat(&input_pixels
, x
, y
, 1) * kRGBtoYColorWeights
[1] +
1441 ChannelAsFloat(&input_pixels
, x
, y
, 2) * kRGBtoYColorWeights
[2] +
1442 kRGBtoYColorWeights
[3]);
1446 for (int y
= 0; y
< ysize
/ 2; y
++) {
1447 for (int x
= 0; x
< xsize
/ 2; x
++) {
1448 U
[(y
+ ymargin
/ 2) * u_stride
+ x
+ xmargin
/ 2] =
1449 float_to_byte(Bilinear(&input_pixels
, x
* 2 + 1.0, y
* 2 + 1.0, 0) *
1450 kRGBtoUColorWeights
[0] +
1451 Bilinear(&input_pixels
, x
* 2 + 1.0, y
* 2 + 1.0, 1) *
1452 kRGBtoUColorWeights
[1] +
1453 Bilinear(&input_pixels
, x
* 2 + 1.0, y
* 2 + 1.0, 2) *
1454 kRGBtoUColorWeights
[2] +
1455 kRGBtoUColorWeights
[3]);
1456 V
[(y
+ ymargin
/ 2) * v_stride
+ x
+ xmargin
/ 2] =
1457 float_to_byte(Bilinear(&input_pixels
, x
* 2 + 1.0, y
* 2 + 1.0, 0) *
1458 kRGBtoVColorWeights
[0] +
1459 Bilinear(&input_pixels
, x
* 2 + 1.0, y
* 2 + 1.0, 1) *
1460 kRGBtoVColorWeights
[1] +
1461 Bilinear(&input_pixels
, x
* 2 + 1.0, y
* 2 + 1.0, 2) *
1462 kRGBtoVColorWeights
[2] +
1463 kRGBtoVColorWeights
[3]);
1469 output_frame
->visible_data(media::VideoFrame::kYPlane
),
1470 output_frame
->stride(media::VideoFrame::kYPlane
),
1475 message
+ " Y plane");
1478 output_frame
->visible_data(media::VideoFrame::kUPlane
),
1479 output_frame
->stride(media::VideoFrame::kUPlane
),
1484 message
+ " U plane");
1487 output_frame
->visible_data(media::VideoFrame::kVPlane
),
1488 output_frame
->stride(media::VideoFrame::kVPlane
),
1493 message
+ " V plane");
1495 context_
->deleteTexture(src_texture
);
1498 void TestAddOps(int src
, int dst
, bool scale_x
, bool allow3
) {
1499 std::deque
<GLHelperScaling::ScaleOp
> ops
;
1500 GLHelperScaling::ScaleOp::AddOps(src
, dst
, scale_x
, allow3
, &ops
);
1501 // Scale factor 3 is a special case.
1502 // It is currently only allowed by itself.
1503 if (allow3
&& dst
* 3 >= src
&& dst
* 2 < src
) {
1504 EXPECT_EQ(ops
[0].scale_factor
, 3);
1505 EXPECT_EQ(ops
.size(), 1U);
1506 EXPECT_EQ(ops
[0].scale_x
, scale_x
);
1507 EXPECT_EQ(ops
[0].scale_size
, dst
);
1511 for (size_t i
= 0; i
< ops
.size(); i
++) {
1512 EXPECT_EQ(ops
[i
].scale_x
, scale_x
);
1514 // Only the first op is allowed to be a scale up.
1515 // (Scaling up *after* scaling down would make it fuzzy.)
1516 EXPECT_TRUE(ops
[0].scale_factor
== 0 || ops
[0].scale_factor
== 2);
1518 // All other operations must be 50% downscales.
1519 EXPECT_EQ(ops
[i
].scale_factor
, 2);
1522 // Check that the scale factors make sense and add up.
1524 for (int i
= static_cast<int>(ops
.size() - 1); i
>= 0; i
--) {
1525 EXPECT_EQ(tmp
, ops
[i
].scale_size
);
1526 if (ops
[i
].scale_factor
== 0) {
1528 EXPECT_GT(tmp
, src
);
1531 tmp
*= ops
[i
].scale_factor
;
1534 EXPECT_EQ(tmp
, src
);
1537 void CheckPipeline2(int xsize
,
1541 const std::string
& description
) {
1542 std::vector
<GLHelperScaling::ScalerStage
> stages
;
1543 helper_scaling_
->ConvertScalerOpsToScalerStages(
1544 content::GLHelper::SCALER_QUALITY_GOOD
,
1545 gfx::Size(xsize
, ysize
),
1546 gfx::Rect(0, 0, xsize
, ysize
),
1547 gfx::Size(dst_xsize
, dst_ysize
),
1553 EXPECT_EQ(x_ops_
.size(), 0U);
1554 EXPECT_EQ(y_ops_
.size(), 0U);
1555 ValidateScalerStages(content::GLHelper::SCALER_QUALITY_GOOD
,
1557 gfx::Size(dst_xsize
, dst_ysize
),
1559 EXPECT_EQ(PrintStages(stages
), description
);
1562 void CheckOptimizationsTest() {
1563 // Basic upscale. X and Y should be combined into one pass.
1564 x_ops_
.push_back(GLHelperScaling::ScaleOp(0, true, 2000));
1565 y_ops_
.push_back(GLHelperScaling::ScaleOp(0, false, 2000));
1566 CheckPipeline2(1024, 768, 2000, 2000, "1024x768 -> 2000x2000 bilinear\n");
1568 // X scaled 1/2, Y upscaled, should still be one pass.
1569 x_ops_
.push_back(GLHelperScaling::ScaleOp(2, true, 512));
1570 y_ops_
.push_back(GLHelperScaling::ScaleOp(0, false, 2000));
1571 CheckPipeline2(1024, 768, 512, 2000, "1024x768 -> 512x2000 bilinear\n");
1573 // X upscaled, Y scaled 1/2, one bilinear pass
1574 x_ops_
.push_back(GLHelperScaling::ScaleOp(0, true, 2000));
1575 y_ops_
.push_back(GLHelperScaling::ScaleOp(2, false, 384));
1576 CheckPipeline2(1024, 768, 2000, 384, "1024x768 -> 2000x384 bilinear\n");
1578 // X scaled 1/2, Y scaled 1/2, one bilinear pass
1579 x_ops_
.push_back(GLHelperScaling::ScaleOp(2, true, 512));
1580 y_ops_
.push_back(GLHelperScaling::ScaleOp(2, false, 384));
1581 CheckPipeline2(1024, 768, 512, 384, "1024x768 -> 512x384 bilinear\n");
1583 // X scaled 1/2, Y scaled to 60%, one bilinear2 pass.
1584 x_ops_
.push_back(GLHelperScaling::ScaleOp(2, true, 50));
1585 y_ops_
.push_back(GLHelperScaling::ScaleOp(0, false, 120));
1586 y_ops_
.push_back(GLHelperScaling::ScaleOp(2, false, 60));
1587 CheckPipeline2(100, 100, 50, 60, "100x100 -> 50x60 bilinear2 Y\n");
1589 // X scaled to 60%, Y scaled 1/2, one bilinear2 pass.
1590 x_ops_
.push_back(GLHelperScaling::ScaleOp(0, true, 120));
1591 x_ops_
.push_back(GLHelperScaling::ScaleOp(2, true, 60));
1592 y_ops_
.push_back(GLHelperScaling::ScaleOp(2, false, 50));
1593 CheckPipeline2(100, 100, 60, 50, "100x100 -> 60x50 bilinear2 X\n");
1595 // X scaled to 60%, Y scaled 60%, one bilinear2x2 pass.
1596 x_ops_
.push_back(GLHelperScaling::ScaleOp(0, true, 120));
1597 x_ops_
.push_back(GLHelperScaling::ScaleOp(2, true, 60));
1598 y_ops_
.push_back(GLHelperScaling::ScaleOp(0, false, 120));
1599 y_ops_
.push_back(GLHelperScaling::ScaleOp(2, false, 60));
1600 CheckPipeline2(100, 100, 60, 60, "100x100 -> 60x60 bilinear2x2\n");
1602 // X scaled to 40%, Y scaled 40%, two bilinear3 passes.
1603 x_ops_
.push_back(GLHelperScaling::ScaleOp(3, true, 40));
1604 y_ops_
.push_back(GLHelperScaling::ScaleOp(3, false, 40));
1609 "100x100 -> 100x40 bilinear3 Y\n"
1610 "100x40 -> 40x40 bilinear3 X\n");
1612 // X scaled to 60%, Y scaled 40%
1613 x_ops_
.push_back(GLHelperScaling::ScaleOp(0, true, 120));
1614 x_ops_
.push_back(GLHelperScaling::ScaleOp(2, true, 60));
1615 y_ops_
.push_back(GLHelperScaling::ScaleOp(3, false, 40));
1620 "100x100 -> 100x40 bilinear3 Y\n"
1621 "100x40 -> 60x40 bilinear2 X\n");
1623 // X scaled to 40%, Y scaled 60%
1624 x_ops_
.push_back(GLHelperScaling::ScaleOp(3, true, 40));
1625 y_ops_
.push_back(GLHelperScaling::ScaleOp(0, false, 120));
1626 y_ops_
.push_back(GLHelperScaling::ScaleOp(2, false, 60));
1631 "100x100 -> 100x60 bilinear2 Y\n"
1632 "100x60 -> 40x60 bilinear3 X\n");
1634 // X scaled to 30%, Y scaled 30%
1635 x_ops_
.push_back(GLHelperScaling::ScaleOp(0, true, 120));
1636 x_ops_
.push_back(GLHelperScaling::ScaleOp(2, true, 60));
1637 x_ops_
.push_back(GLHelperScaling::ScaleOp(2, true, 30));
1638 y_ops_
.push_back(GLHelperScaling::ScaleOp(0, false, 120));
1639 y_ops_
.push_back(GLHelperScaling::ScaleOp(2, false, 60));
1640 y_ops_
.push_back(GLHelperScaling::ScaleOp(2, false, 30));
1645 "100x100 -> 100x30 bilinear4 Y\n"
1646 "100x30 -> 30x30 bilinear4 X\n");
1648 // X scaled to 50%, Y scaled 30%
1649 x_ops_
.push_back(GLHelperScaling::ScaleOp(2, true, 50));
1650 y_ops_
.push_back(GLHelperScaling::ScaleOp(0, false, 120));
1651 y_ops_
.push_back(GLHelperScaling::ScaleOp(2, false, 60));
1652 y_ops_
.push_back(GLHelperScaling::ScaleOp(2, false, 30));
1653 CheckPipeline2(100, 100, 50, 30, "100x100 -> 50x30 bilinear4 Y\n");
1655 // X scaled to 150%, Y scaled 30%
1656 // Note that we avoid combinding X and Y passes
1657 // as that would probably be LESS efficient here.
1658 x_ops_
.push_back(GLHelperScaling::ScaleOp(0, true, 150));
1659 y_ops_
.push_back(GLHelperScaling::ScaleOp(0, false, 120));
1660 y_ops_
.push_back(GLHelperScaling::ScaleOp(2, false, 60));
1661 y_ops_
.push_back(GLHelperScaling::ScaleOp(2, false, 30));
1666 "100x100 -> 100x30 bilinear4 Y\n"
1667 "100x30 -> 150x30 bilinear\n");
1669 // X scaled to 1%, Y scaled 1%
1670 x_ops_
.push_back(GLHelperScaling::ScaleOp(0, true, 128));
1671 x_ops_
.push_back(GLHelperScaling::ScaleOp(2, true, 64));
1672 x_ops_
.push_back(GLHelperScaling::ScaleOp(2, true, 32));
1673 x_ops_
.push_back(GLHelperScaling::ScaleOp(2, true, 16));
1674 x_ops_
.push_back(GLHelperScaling::ScaleOp(2, true, 8));
1675 x_ops_
.push_back(GLHelperScaling::ScaleOp(2, true, 4));
1676 x_ops_
.push_back(GLHelperScaling::ScaleOp(2, true, 2));
1677 x_ops_
.push_back(GLHelperScaling::ScaleOp(2, true, 1));
1678 y_ops_
.push_back(GLHelperScaling::ScaleOp(0, false, 128));
1679 y_ops_
.push_back(GLHelperScaling::ScaleOp(2, false, 64));
1680 y_ops_
.push_back(GLHelperScaling::ScaleOp(2, false, 32));
1681 y_ops_
.push_back(GLHelperScaling::ScaleOp(2, false, 16));
1682 y_ops_
.push_back(GLHelperScaling::ScaleOp(2, false, 8));
1683 y_ops_
.push_back(GLHelperScaling::ScaleOp(2, false, 4));
1684 y_ops_
.push_back(GLHelperScaling::ScaleOp(2, false, 2));
1685 y_ops_
.push_back(GLHelperScaling::ScaleOp(2, false, 1));
1690 "100x100 -> 100x32 bilinear4 Y\n"
1691 "100x32 -> 100x4 bilinear4 Y\n"
1692 "100x4 -> 64x1 bilinear2x2\n"
1693 "64x1 -> 8x1 bilinear4 X\n"
1694 "8x1 -> 1x1 bilinear4 X\n");
1697 scoped_ptr
<WebGraphicsContext3DInProcessCommandBufferImpl
> context_
;
1698 gpu::ContextSupport
* context_support_
;
1699 scoped_ptr
<content::GLHelper
> helper_
;
1700 scoped_ptr
<content::GLHelperScaling
> helper_scaling_
;
1701 std::deque
<GLHelperScaling::ScaleOp
> x_ops_
, y_ops_
;
1704 class GLHelperPixelTest
: public GLHelperTest
{
1706 gfx::DisableNullDrawGLBindings enable_pixel_output_
;
1709 TEST_F(GLHelperTest
, RGBASyncReadbackTest
) {
1710 const int kTestSize
= 64;
1711 bool result
= TestTextureFormatReadback(gfx::Size(kTestSize
,kTestSize
),
1712 kRGBA_8888_SkColorType
,
1714 EXPECT_EQ(result
, true);
1718 TEST_F(GLHelperTest
, BGRASyncReadbackTest
) {
1719 const int kTestSize
= 64;
1720 bool result
= TestTextureFormatReadback(gfx::Size(kTestSize
,kTestSize
),
1721 kBGRA_8888_SkColorType
,
1723 EXPECT_EQ(result
, true);
1726 TEST_F(GLHelperTest
, RGB565SyncReadbackTest
) {
1727 const int kTestSize
= 64;
1728 bool result
= TestTextureFormatReadback(gfx::Size(kTestSize
,kTestSize
),
1729 kRGB_565_SkColorType
,
1731 EXPECT_EQ(result
, true);
1734 TEST_F(GLHelperTest
, RGBAASyncReadbackTest
) {
1735 const int kTestSize
= 64;
1736 bool result
= TestTextureFormatReadback(gfx::Size(kTestSize
,kTestSize
),
1737 kRGBA_8888_SkColorType
,
1739 EXPECT_EQ(result
, true);
1742 TEST_F(GLHelperTest
, BGRAASyncReadbackTest
) {
1743 const int kTestSize
= 64;
1744 bool result
= TestTextureFormatReadback(gfx::Size(kTestSize
,kTestSize
),
1745 kBGRA_8888_SkColorType
,
1747 EXPECT_EQ(result
, true);
1750 TEST_F(GLHelperTest
, RGB565ASyncReadbackTest
) {
1751 const int kTestSize
= 64;
1752 bool result
= TestTextureFormatReadback(gfx::Size(kTestSize
,kTestSize
),
1753 kRGB_565_SkColorType
,
1755 EXPECT_EQ(result
, true);
1758 TEST_F(GLHelperPixelTest
, YUVReadbackOptTest
) {
1759 // This test uses the cb_command tracing events to detect how many
1760 // scaling passes are actually performed by the YUV readback pipeline.
1761 StartTracing(TRACE_DISABLED_BY_DEFAULT("cb_command"));
1763 TestYUVReadback(800,
1772 content::GLHelper::SCALER_QUALITY_FAST
);
1774 std::map
<std::string
, int> event_counts
;
1775 EndTracing(&event_counts
);
1776 int draw_buffer_calls
= event_counts
["kDrawBuffersEXTImmediate"];
1777 int draw_arrays_calls
= event_counts
["kDrawArrays"];
1778 VLOG(1) << "Draw buffer calls: " << draw_buffer_calls
;
1779 VLOG(1) << "DrawArrays calls: " << draw_arrays_calls
;
1781 if (draw_buffer_calls
) {
1782 // When using MRT, the YUV readback code should only
1783 // execute two draw arrays, and scaling should be integrated
1784 // into those two calls since we are using the FAST scalign
1786 EXPECT_EQ(2, draw_arrays_calls
);
1788 // When not using MRT, there are three passes for the YUV,
1789 // and one for the scaling.
1790 EXPECT_EQ(4, draw_arrays_calls
);
1794 TEST_F(GLHelperPixelTest
, YUVReadbackTest
) {
1795 int sizes
[] = {2, 4, 14};
1796 for (int flip
= 0; flip
<= 1; flip
++) {
1797 for (int use_mrt
= 0; use_mrt
<= 1; use_mrt
++) {
1798 for (unsigned int x
= 0; x
< arraysize(sizes
); x
++) {
1799 for (unsigned int y
= 0; y
< arraysize(sizes
); y
++) {
1800 for (unsigned int ox
= x
; ox
< arraysize(sizes
); ox
++) {
1801 for (unsigned int oy
= y
; oy
< arraysize(sizes
); oy
++) {
1802 // If output is a subsection of the destination frame, (letterbox)
1803 // then try different variations of where the subsection goes.
1804 for (Margin xm
= x
< ox
? MarginLeft
: MarginRight
;
1806 xm
= NextMargin(xm
)) {
1807 for (Margin ym
= y
< oy
? MarginLeft
: MarginRight
;
1809 ym
= NextMargin(ym
)) {
1810 for (int pattern
= 0; pattern
< 3; pattern
++) {
1811 TestYUVReadback(sizes
[x
],
1815 compute_margin(sizes
[x
], sizes
[ox
], xm
),
1816 compute_margin(sizes
[y
], sizes
[oy
], ym
),
1820 content::GLHelper::SCALER_QUALITY_GOOD
);
1835 // Per pixel tests, all sizes are small so that we can print
1836 // out the generated bitmaps.
1837 TEST_F(GLHelperPixelTest
, ScaleTest
) {
1838 int sizes
[] = {3, 6, 16};
1839 for (int flip
= 0; flip
<= 1; flip
++) {
1840 for (size_t q_index
= 0; q_index
< arraysize(kQualities
); q_index
++) {
1841 for (int x
= 0; x
< 3; x
++) {
1842 for (int y
= 0; y
< 3; y
++) {
1843 for (int dst_x
= 0; dst_x
< 3; dst_x
++) {
1844 for (int dst_y
= 0; dst_y
< 3; dst_y
++) {
1845 for (int pattern
= 0; pattern
< 3; pattern
++) {
1865 // Per pixel tests, all sizes are small so that we can print
1866 // out the generated bitmaps.
1867 TEST_F(GLHelperPixelTest
, CropScaleReadbackAndCleanTextureTest
) {
1868 const int kSizes
[] = {3, 6, 16};
1869 const SkColorType kColorTypes
[] = {
1870 kAlpha_8_SkColorType
, kRGBA_8888_SkColorType
, kBGRA_8888_SkColorType
};
1871 for (size_t color_type
= 0; color_type
< arraysize(kColorTypes
);
1873 // Test BEST and FAST qualities, skip GOOD
1874 for (size_t q_index
= 0; q_index
< arraysize(kQualities
); q_index
+= 2) {
1875 for (size_t x
= 0; x
< arraysize(kSizes
); x
++) {
1876 for (size_t y
= 0; y
< arraysize(kSizes
); y
++) {
1877 for (size_t dst_x
= 0; dst_x
< arraysize(kSizes
); dst_x
++) {
1878 for (size_t dst_y
= 0; dst_y
< arraysize(kSizes
); dst_y
++) {
1879 for (int pattern
= 0; pattern
< 3; pattern
++) {
1880 TestCropScaleReadbackAndCleanTexture(kSizes
[x
],
1885 kColorTypes
[color_type
],
1899 // Validate that all scaling generates valid pipelines.
1900 TEST_F(GLHelperTest
, ValidateScalerPipelines
) {
1901 int sizes
[] = {7, 99, 128, 256, 512, 719, 720, 721, 1920, 2011, 3217, 4096};
1902 for (size_t q
= 0; q
< arraysize(kQualities
); q
++) {
1903 for (size_t x
= 0; x
< arraysize(sizes
); x
++) {
1904 for (size_t y
= 0; y
< arraysize(sizes
); y
++) {
1905 for (size_t dst_x
= 0; dst_x
< arraysize(sizes
); dst_x
++) {
1906 for (size_t dst_y
= 0; dst_y
< arraysize(sizes
); dst_y
++) {
1908 q
, sizes
[x
], sizes
[y
], sizes
[dst_x
], sizes
[dst_y
]);
1919 // Make sure we don't create overly complicated pipelines
1920 // for a few common use cases.
1921 TEST_F(GLHelperTest
, CheckSpecificPipelines
) {
1922 // Upscale should be single pass.
1923 CheckPipeline(content::GLHelper::SCALER_QUALITY_GOOD
,
1928 "1024x700 -> 1280x720 bilinear\n");
1929 // Slight downscale should use BILINEAR2X2.
1930 CheckPipeline(content::GLHelper::SCALER_QUALITY_GOOD
,
1935 "1280x720 -> 1024x700 bilinear2x2\n");
1936 // Most common tab capture pipeline on the Pixel.
1937 // Should be using two BILINEAR3 passes.
1938 CheckPipeline(content::GLHelper::SCALER_QUALITY_GOOD
,
1943 "2560x1476 -> 2560x720 bilinear3 Y\n"
1944 "2560x720 -> 1249x720 bilinear3 X\n");
1947 TEST_F(GLHelperTest
, ScalerOpTest
) {
1948 for (int allow3
= 0; allow3
<= 1; allow3
++) {
1949 for (int dst
= 1; dst
< 2049; dst
+= 1 + (dst
>> 3)) {
1950 for (int src
= 1; src
< 2049; src
++) {
1951 TestAddOps(src
, dst
, allow3
== 1, (src
& 1) == 1);
1953 LOG(ERROR
) << "Failed for src=" << src
<< " dst=" << dst
1954 << " allow3=" << allow3
;
1962 TEST_F(GLHelperTest
, CheckOptimizations
) {
1963 // Test in baseclass since it is friends with GLHelperScaling
1964 CheckOptimizationsTest();
1967 } // namespace content
1971 int RunHelper(base::TestSuite
* test_suite
) {
1972 content::UnitTestTestSuite
runner(test_suite
);
1973 base::MessageLoopForIO message_loop
;
1974 return runner
.Run();
1979 // These tests needs to run against a proper GL environment, so we
1980 // need to set it up before we can run the tests.
1981 int main(int argc
, char** argv
) {
1982 base::CommandLine::Init(argc
, argv
);
1983 base::TestSuite
* suite
= new content::ContentTestSuite(argc
, argv
);
1984 #if defined(OS_MACOSX)
1985 base::mac::ScopedNSAutoreleasePool pool
;
1988 return base::LaunchUnitTestsSerially(
1991 base::Bind(&RunHelper
, base::Unretained(suite
)));