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::ReadAndReturnError(json_data
, 0, NULL
, &error_msg
);
116 << "JSON parsing failed (" << error_msg
<< ") JSON data:" << std::endl
119 base::ListValue
* list
;
120 CHECK(trace_data
->GetAsList(&list
));
121 for (size_t i
= 0; i
< list
->GetSize(); i
++) {
122 base::Value
* item
= NULL
;
123 if (list
->Get(i
, &item
)) {
124 base::DictionaryValue
* dict
;
125 CHECK(item
->GetAsDictionary(&dict
));
127 CHECK(dict
->GetString("name", &name
));
128 std::string trace_type
;
129 CHECK(dict
->GetString("ph", &trace_type
));
130 // Count all except END traces, as they come in BEGIN/END pairs.
131 if (trace_type
!= "E" && trace_type
!= "e")
132 (*event_counts
)[name
]++;
133 VLOG(1) << "trace name: " << name
;
138 // Bicubic filter kernel function.
139 static float Bicubic(float x
) {
140 const float a
= -0.5;
145 return (a
+ 2) * x3
- (a
+ 3) * x2
+ 1;
147 return a
* x3
- 5 * a
* x2
+ 8 * a
* x
- 4 * a
;
153 // Look up a single channel value. Works for 4-channel and single channel
154 // bitmaps. Clamp x/y.
155 int Channel(SkBitmap
* pixels
, int x
, int y
, int c
) {
156 if (pixels
->bytesPerPixel() == 4) {
158 pixels
->getAddr32(std::max(0, std::min(x
, pixels
->width() - 1)),
159 std::max(0, std::min(y
, pixels
->height() - 1)));
160 return (*data
) >> (c
* 8) & 0xff;
162 DCHECK_EQ(pixels
->bytesPerPixel(), 1);
164 return *pixels
->getAddr8(std::max(0, std::min(x
, pixels
->width() - 1)),
165 std::max(0, std::min(y
, pixels
->height() - 1)));
169 // Set a single channel value. Works for 4-channel and single channel
170 // bitmaps. Clamp x/y.
171 void SetChannel(SkBitmap
* pixels
, int x
, int y
, int c
, int v
) {
174 DCHECK_LT(x
, pixels
->width());
175 DCHECK_LT(y
, pixels
->height());
176 if (pixels
->bytesPerPixel() == 4) {
177 uint32
* data
= pixels
->getAddr32(x
, y
);
178 v
= std::max(0, std::min(v
, 255));
179 *data
= (*data
& ~(0xffu
<< (c
* 8))) | (v
<< (c
* 8));
181 DCHECK_EQ(pixels
->bytesPerPixel(), 1);
183 uint8
* data
= pixels
->getAddr8(x
, y
);
184 v
= std::max(0, std::min(v
, 255));
189 // Print all the R, G, B or A values from an SkBitmap in a
190 // human-readable format.
191 void PrintChannel(SkBitmap
* pixels
, int c
) {
192 for (int y
= 0; y
< pixels
->height(); y
++) {
193 std::string formatted
;
194 for (int x
= 0; x
< pixels
->width(); x
++) {
195 formatted
.append(base::StringPrintf("%3d, ", Channel(pixels
, x
, y
, c
)));
197 LOG(ERROR
) << formatted
;
201 // Print out the individual steps of a scaler pipeline.
202 std::string
PrintStages(
203 const std::vector
<GLHelperScaling::ScalerStage
>& scaler_stages
) {
205 for (size_t i
= 0; i
< scaler_stages
.size(); i
++) {
206 ret
.append(base::StringPrintf("%dx%d -> %dx%d ",
207 scaler_stages
[i
].src_size
.width(),
208 scaler_stages
[i
].src_size
.height(),
209 scaler_stages
[i
].dst_size
.width(),
210 scaler_stages
[i
].dst_size
.height()));
211 bool xy_matters
= false;
212 switch (scaler_stages
[i
].shader
) {
213 case GLHelperScaling::SHADER_BILINEAR
:
214 ret
.append("bilinear");
216 case GLHelperScaling::SHADER_BILINEAR2
:
217 ret
.append("bilinear2");
220 case GLHelperScaling::SHADER_BILINEAR3
:
221 ret
.append("bilinear3");
224 case GLHelperScaling::SHADER_BILINEAR4
:
225 ret
.append("bilinear4");
228 case GLHelperScaling::SHADER_BILINEAR2X2
:
229 ret
.append("bilinear2x2");
231 case GLHelperScaling::SHADER_BICUBIC_UPSCALE
:
232 ret
.append("bicubic upscale");
235 case GLHelperScaling::SHADER_BICUBIC_HALF_1D
:
236 ret
.append("bicubic 1/2");
239 case GLHelperScaling::SHADER_PLANAR
:
240 ret
.append("planar");
242 case GLHelperScaling::SHADER_YUV_MRT_PASS1
:
243 ret
.append("rgb2yuv pass 1");
245 case GLHelperScaling::SHADER_YUV_MRT_PASS2
:
246 ret
.append("rgb2yuv pass 2");
251 if (scaler_stages
[i
].scale_x
) {
262 bool CheckScale(double scale
, int samples
, bool already_scaled
) {
263 // 1:1 is valid if there is one sample.
264 if (samples
== 1 && scale
== 1.0) {
267 // Is it an exact down-scale (50%, 25%, etc.?)
268 if (scale
== 2.0 * samples
) {
271 // Upscales, only valid if we haven't already scaled in this dimension.
272 if (!already_scaled
) {
273 // Is it a valid bilinear upscale?
274 if (samples
== 1 && scale
<= 1.0) {
277 // Multi-sample upscale-downscale combination?
278 if (scale
> samples
/ 2.0 && scale
< samples
) {
285 // Make sure that the stages of the scaler pipeline are sane.
286 void ValidateScalerStages(
287 content::GLHelper::ScalerQuality quality
,
288 const std::vector
<GLHelperScaling::ScalerStage
>& scaler_stages
,
289 const gfx::Size
& dst_size
,
290 const std::string
& message
) {
291 bool previous_error
= HasFailure();
292 // First, check that the input size for each stage is equal to
293 // the output size of the previous stage.
294 for (size_t i
= 1; i
< scaler_stages
.size(); i
++) {
295 EXPECT_EQ(scaler_stages
[i
- 1].dst_size
.width(),
296 scaler_stages
[i
].src_size
.width());
297 EXPECT_EQ(scaler_stages
[i
- 1].dst_size
.height(),
298 scaler_stages
[i
].src_size
.height());
299 EXPECT_EQ(scaler_stages
[i
].src_subrect
.x(), 0);
300 EXPECT_EQ(scaler_stages
[i
].src_subrect
.y(), 0);
301 EXPECT_EQ(scaler_stages
[i
].src_subrect
.width(),
302 scaler_stages
[i
].src_size
.width());
303 EXPECT_EQ(scaler_stages
[i
].src_subrect
.height(),
304 scaler_stages
[i
].src_size
.height());
307 // Check the output size matches the destination of the last stage
308 EXPECT_EQ(scaler_stages
[scaler_stages
.size() - 1].dst_size
.width(),
310 EXPECT_EQ(scaler_stages
[scaler_stages
.size() - 1].dst_size
.height(),
313 // Used to verify that up-scales are not attempted after some
315 bool scaled_x
= false;
316 bool scaled_y
= false;
318 for (size_t i
= 0; i
< scaler_stages
.size(); i
++) {
319 // Note: 2.0 means scaling down by 50%
321 static_cast<double>(scaler_stages
[i
].src_subrect
.width()) /
322 static_cast<double>(scaler_stages
[i
].dst_size
.width());
324 static_cast<double>(scaler_stages
[i
].src_subrect
.height()) /
325 static_cast<double>(scaler_stages
[i
].dst_size
.height());
330 // Codify valid scale operations.
331 switch (scaler_stages
[i
].shader
) {
332 case GLHelperScaling::SHADER_PLANAR
:
333 case GLHelperScaling::SHADER_YUV_MRT_PASS1
:
334 case GLHelperScaling::SHADER_YUV_MRT_PASS2
:
335 EXPECT_TRUE(false) << "Invalid shader.";
338 case GLHelperScaling::SHADER_BILINEAR
:
339 if (quality
!= content::GLHelper::SCALER_QUALITY_FAST
) {
344 case GLHelperScaling::SHADER_BILINEAR2
:
348 case GLHelperScaling::SHADER_BILINEAR3
:
352 case GLHelperScaling::SHADER_BILINEAR4
:
356 case GLHelperScaling::SHADER_BILINEAR2X2
:
360 case GLHelperScaling::SHADER_BICUBIC_UPSCALE
:
361 if (scaler_stages
[i
].scale_x
) {
362 EXPECT_LT(x_scale
, 1.0);
363 EXPECT_EQ(y_scale
, 1.0);
365 EXPECT_EQ(x_scale
, 1.0);
366 EXPECT_LT(y_scale
, 1.0);
369 case GLHelperScaling::SHADER_BICUBIC_HALF_1D
:
370 if (scaler_stages
[i
].scale_x
) {
371 EXPECT_EQ(x_scale
, 2.0);
372 EXPECT_EQ(y_scale
, 1.0);
374 EXPECT_EQ(x_scale
, 1.0);
375 EXPECT_EQ(y_scale
, 2.0);
380 if (!scaler_stages
[i
].scale_x
) {
381 std::swap(x_samples
, y_samples
);
385 EXPECT_TRUE(CheckScale(x_scale
, x_samples
, scaled_x
))
386 << "x_scale = " << x_scale
;
389 EXPECT_TRUE(CheckScale(y_scale
, y_samples
, scaled_y
))
390 << "y_scale = " << y_scale
;
393 if (x_scale
!= 1.0) {
396 if (y_scale
!= 1.0) {
401 if (HasFailure() && !previous_error
) {
402 LOG(ERROR
) << "Invalid scaler stages: " << message
;
403 LOG(ERROR
) << "Scaler stages:";
404 LOG(ERROR
) << PrintStages(scaler_stages
);
408 // Compares two bitmaps taking color types into account. Checks whether each
409 // component of each pixel is no more than |maxdiff| apart. If bitmaps are not
410 // similar enough, prints out |truth|, |other|, |source|, |scaler_stages|
412 void Compare(SkBitmap
* truth
,
416 const std::vector
<GLHelperScaling::ScalerStage
>& scaler_stages
,
417 std::string message
) {
418 EXPECT_EQ(truth
->width(), other
->width());
419 EXPECT_EQ(truth
->height(), other
->height());
420 bool swizzle
= (truth
->colorType() == kRGBA_8888_SkColorType
&&
421 other
->colorType() == kBGRA_8888_SkColorType
) ||
422 (truth
->colorType() == kBGRA_8888_SkColorType
&&
423 other
->colorType() == kRGBA_8888_SkColorType
);
424 EXPECT_TRUE(swizzle
|| truth
->colorType() == other
->colorType());
425 int bpp
= truth
->bytesPerPixel();
426 for (int x
= 0; x
< truth
->width(); x
++) {
427 for (int y
= 0; y
< truth
->height(); y
++) {
428 for (int c
= 0; c
< bpp
; c
++) {
429 int a
= Channel(truth
, x
, y
, c
);
430 // swizzle when comparing if needed
431 int b
= swizzle
&& (c
== 0 || c
== 2)
432 ? Channel(other
, x
, y
, (c
+ 2) & 2)
433 : Channel(other
, x
, y
, c
);
434 EXPECT_NEAR(a
, b
, maxdiff
) << " x=" << x
<< " y=" << y
<< " c=" << c
436 if (std::abs(a
- b
) > maxdiff
) {
437 LOG(ERROR
) << "-------expected--------";
438 for (int i
= 0; i
< bpp
; i
++) {
439 LOG(ERROR
) << "Channel " << i
<< ":";
440 PrintChannel(truth
, i
);
442 LOG(ERROR
) << "-------actual--------";
443 for (int i
= 0; i
< bpp
; i
++) {
444 LOG(ERROR
) << "Channel " << i
<< ":";
445 PrintChannel(other
, i
);
448 LOG(ERROR
) << "-------original--------";
449 for (int i
= 0; i
< source
->bytesPerPixel(); i
++) {
450 LOG(ERROR
) << "Channel " << i
<< ":";
451 PrintChannel(source
, i
);
454 LOG(ERROR
) << "-----Scaler stages------";
455 LOG(ERROR
) << PrintStages(scaler_stages
);
463 // Get a single R, G, B or A value as a float.
464 float ChannelAsFloat(SkBitmap
* pixels
, int x
, int y
, int c
) {
465 return Channel(pixels
, x
, y
, c
) / 255.0;
468 // Works like a GL_LINEAR lookup on an SkBitmap.
469 float Bilinear(SkBitmap
* pixels
, float x
, float y
, int c
) {
472 int base_x
= static_cast<int>(floorf(x
));
473 int base_y
= static_cast<int>(floorf(y
));
476 return (ChannelAsFloat(pixels
, base_x
, base_y
, c
) * (1 - x
) * (1 - y
) +
477 ChannelAsFloat(pixels
, base_x
+ 1, base_y
, c
) * x
* (1 - y
) +
478 ChannelAsFloat(pixels
, base_x
, base_y
+ 1, c
) * (1 - x
) * y
+
479 ChannelAsFloat(pixels
, base_x
+ 1, base_y
+ 1, c
) * x
* y
);
482 // Encodes an RGBA bitmap to grayscale.
483 // Reference implementation for
484 // GLHelper::CopyToTextureImpl::EncodeTextureAsGrayscale.
485 void EncodeToGrayscaleSlow(SkBitmap
* input
, SkBitmap
* output
) {
486 const float kRGBtoGrayscaleColorWeights
[3] = {0.213f
, 0.715f
, 0.072f
};
487 CHECK_EQ(kAlpha_8_SkColorType
, output
->colorType());
488 CHECK_EQ(input
->width(), output
->width());
489 CHECK_EQ(input
->height(), output
->height());
490 CHECK_EQ(input
->colorType(), kRGBA_8888_SkColorType
);
492 for (int dst_y
= 0; dst_y
< output
->height(); dst_y
++) {
493 for (int dst_x
= 0; dst_x
< output
->width(); dst_x
++) {
494 float c0
= ChannelAsFloat(input
, dst_x
, dst_y
, 0);
495 float c1
= ChannelAsFloat(input
, dst_x
, dst_y
, 1);
496 float c2
= ChannelAsFloat(input
, dst_x
, dst_y
, 2);
497 float value
= c0
* kRGBtoGrayscaleColorWeights
[0] +
498 c1
* kRGBtoGrayscaleColorWeights
[1] +
499 c2
* kRGBtoGrayscaleColorWeights
[2];
501 output
, dst_x
, dst_y
, 0, static_cast<int>(value
* 255.0f
+ 0.5f
));
506 // Very slow bicubic / bilinear scaler for reference.
507 void ScaleSlow(SkBitmap
* input
,
509 content::GLHelper::ScalerQuality quality
) {
510 float xscale
= static_cast<float>(input
->width()) / output
->width();
511 float yscale
= static_cast<float>(input
->height()) / output
->height();
512 float clamped_xscale
= xscale
< 1.0 ? 1.0 : 1.0 / xscale
;
513 float clamped_yscale
= yscale
< 1.0 ? 1.0 : 1.0 / yscale
;
514 for (int dst_y
= 0; dst_y
< output
->height(); dst_y
++) {
515 for (int dst_x
= 0; dst_x
< output
->width(); dst_x
++) {
516 for (int channel
= 0; channel
< 4; channel
++) {
517 float dst_x_in_src
= (dst_x
+ 0.5f
) * xscale
;
518 float dst_y_in_src
= (dst_y
+ 0.5f
) * yscale
;
523 case content::GLHelper::SCALER_QUALITY_BEST
:
524 for (int src_y
= -10; src_y
< input
->height() + 10; ++src_y
) {
526 Bicubic((src_y
+ 0.5f
- dst_y_in_src
) * clamped_yscale
);
527 if (coeff_y
== 0.0f
) {
530 for (int src_x
= -10; src_x
< input
->width() + 10; ++src_x
) {
533 Bicubic((src_x
+ 0.5f
- dst_x_in_src
) * clamped_xscale
);
538 float c
= ChannelAsFloat(input
, src_x
, src_y
, channel
);
544 case content::GLHelper::SCALER_QUALITY_GOOD
: {
545 int xshift
= 0, yshift
= 0;
546 while ((output
->width() << xshift
) < input
->width()) {
549 while ((output
->height() << yshift
) < input
->height()) {
552 int xmag
= 1 << xshift
;
553 int ymag
= 1 << yshift
;
554 if (xmag
== 4 && output
->width() * 3 >= input
->width()) {
557 if (ymag
== 4 && output
->height() * 3 >= input
->height()) {
560 for (int x
= 0; x
< xmag
; x
++) {
561 for (int y
= 0; y
< ymag
; y
++) {
562 value
+= Bilinear(input
,
563 (dst_x
* xmag
+ x
+ 0.5) * xscale
/ xmag
,
564 (dst_y
* ymag
+ y
+ 0.5) * yscale
/ ymag
,
572 case content::GLHelper::SCALER_QUALITY_FAST
:
573 value
= Bilinear(input
, dst_x_in_src
, dst_y_in_src
, channel
);
581 static_cast<int>(value
* 255.0f
+ 0.5f
));
587 void FlipSKBitmap(SkBitmap
* bitmap
) {
588 int bpp
= bitmap
->bytesPerPixel();
589 DCHECK(bpp
== 4 || bpp
== 1);
591 int bottom_line
= bitmap
->height() - 1;
592 while (top_line
< bottom_line
) {
593 for (int x
= 0; x
< bitmap
->width(); x
++) {
594 bpp
== 4 ? std::swap(*bitmap
->getAddr32(x
, top_line
),
595 *bitmap
->getAddr32(x
, bottom_line
))
596 : std::swap(*bitmap
->getAddr8(x
, top_line
),
597 *bitmap
->getAddr8(x
, bottom_line
));
604 // Swaps red and blue channels in each pixel in a 32-bit bitmap.
605 void SwizzleSKBitmap(SkBitmap
* bitmap
) {
606 int bpp
= bitmap
->bytesPerPixel();
608 for (int y
= 0; y
< bitmap
->height(); y
++) {
609 for (int x
= 0; x
< bitmap
->width(); x
++) {
610 // Swap channels 0 and 2 (red and blue)
611 int c0
= Channel(bitmap
, x
, y
, 0);
612 int c2
= Channel(bitmap
, x
, y
, 2);
613 SetChannel(bitmap
, x
, y
, 2, c0
);
614 SetChannel(bitmap
, x
, y
, 0, c2
);
619 // gl_helper scales recursively, so we'll need to do that
620 // in the reference implementation too.
621 void ScaleSlowRecursive(SkBitmap
* input
,
623 content::GLHelper::ScalerQuality quality
) {
624 if (quality
== content::GLHelper::SCALER_QUALITY_FAST
||
625 quality
== content::GLHelper::SCALER_QUALITY_GOOD
) {
626 ScaleSlow(input
, output
, quality
);
630 float xscale
= static_cast<float>(output
->width()) / input
->width();
632 // This corresponds to all the operations we can do directly.
633 float yscale
= static_cast<float>(output
->height()) / input
->height();
634 if ((xscale
== 1.0f
&& yscale
== 1.0f
) ||
635 (xscale
== 0.5f
&& yscale
== 1.0f
) ||
636 (xscale
== 1.0f
&& yscale
== 0.5f
) ||
637 (xscale
>= 1.0f
&& yscale
== 1.0f
) ||
638 (xscale
== 1.0f
&& yscale
>= 1.0f
)) {
639 ScaleSlow(input
, output
, quality
);
643 // Now we break the problem down into smaller pieces, using the
644 // operations available.
645 int xtmp
= input
->width();
646 int ytmp
= input
->height();
648 if (output
->height() != input
->height()) {
649 ytmp
= output
->height();
650 while (ytmp
< input
->height() && ytmp
* 2 != input
->height()) {
654 xtmp
= output
->width();
655 while (xtmp
< input
->width() && xtmp
* 2 != input
->width()) {
661 tmp
.allocN32Pixels(xtmp
, ytmp
);
663 ScaleSlowRecursive(input
, &tmp
, quality
);
664 ScaleSlowRecursive(&tmp
, output
, quality
);
667 // Creates an RGBA SkBitmap
668 scoped_ptr
<SkBitmap
> CreateTestBitmap(int width
,
671 scoped_ptr
<SkBitmap
> bitmap(new SkBitmap
);
672 bitmap
->allocPixels(SkImageInfo::Make(
673 width
, height
, kRGBA_8888_SkColorType
, kPremul_SkAlphaType
));
675 for (int x
= 0; x
< width
; ++x
) {
676 for (int y
= 0; y
< height
; ++y
) {
677 switch (test_pattern
) {
678 case 0: // Smooth test pattern
679 SetChannel(bitmap
.get(), x
, y
, 0, x
* 10);
680 SetChannel(bitmap
.get(), x
, y
, 0, y
== 0 ? x
* 50 : x
* 10);
681 SetChannel(bitmap
.get(), x
, y
, 1, y
* 10);
682 SetChannel(bitmap
.get(), x
, y
, 2, (x
+ y
) * 10);
683 SetChannel(bitmap
.get(), x
, y
, 3, 255);
685 case 1: // Small blocks
686 SetChannel(bitmap
.get(), x
, y
, 0, x
& 1 ? 255 : 0);
687 SetChannel(bitmap
.get(), x
, y
, 1, y
& 1 ? 255 : 0);
688 SetChannel(bitmap
.get(), x
, y
, 2, (x
+ y
) & 1 ? 255 : 0);
689 SetChannel(bitmap
.get(), x
, y
, 3, 255);
691 case 2: // Medium blocks
692 SetChannel(bitmap
.get(), x
, y
, 0, 10 + x
/ 2 * 50);
693 SetChannel(bitmap
.get(), x
, y
, 1, 10 + y
/ 3 * 50);
694 SetChannel(bitmap
.get(), x
, y
, 2, (x
+ y
) / 5 * 50 + 5);
695 SetChannel(bitmap
.get(), x
, y
, 3, 255);
700 return bitmap
.Pass();
703 // Binds texture and framebuffer and loads the bitmap pixels into the texture.
704 void BindTextureAndFrameBuffer(WebGLId texture
,
709 context_
->bindFramebuffer(GL_FRAMEBUFFER
, framebuffer
);
710 context_
->bindTexture(GL_TEXTURE_2D
, texture
);
711 context_
->texImage2D(GL_TEXTURE_2D
,
719 bitmap
->getPixels());
722 // Create a test image, transform it using
723 // GLHelper::CropScaleReadbackAndCleanTexture and a reference implementation
724 // and compare the results.
725 void TestCropScaleReadbackAndCleanTexture(int xsize
,
730 SkColorType out_color_type
,
732 size_t quality_index
) {
733 DCHECK(out_color_type
== kAlpha_8_SkColorType
||
734 out_color_type
== kRGBA_8888_SkColorType
||
735 out_color_type
== kBGRA_8888_SkColorType
);
736 WebGLId src_texture
= context_
->createTexture();
737 WebGLId framebuffer
= context_
->createFramebuffer();
738 scoped_ptr
<SkBitmap
> input_pixels
=
739 CreateTestBitmap(xsize
, ysize
, test_pattern
).Pass();
740 BindTextureAndFrameBuffer(
741 src_texture
, framebuffer
, input_pixels
.get(), xsize
, ysize
);
743 std::string message
= base::StringPrintf(
745 "output size: %dx%d "
746 "pattern: %d , quality: %s, "
747 "out_color_type: %d",
753 kQualityNames
[quality_index
],
756 // Transform the bitmap using GLHelper::CropScaleReadbackAndCleanTexture.
757 SkBitmap output_pixels
;
758 output_pixels
.allocPixels(SkImageInfo::Make(
759 scaled_xsize
, scaled_ysize
, out_color_type
, kPremul_SkAlphaType
));
760 base::RunLoop run_loop
;
761 gfx::Size encoded_texture_size
;
762 helper_
->CropScaleReadbackAndCleanTexture(
764 gfx::Size(xsize
, ysize
),
765 gfx::Rect(xsize
, ysize
),
766 gfx::Size(scaled_xsize
, scaled_ysize
),
767 static_cast<unsigned char*>(output_pixels
.getPixels()),
769 base::Bind(&callcallback
, run_loop
.QuitClosure()),
770 kQualities
[quality_index
]);
772 // CropScaleReadbackAndCleanTexture flips the pixels. Flip them back.
773 FlipSKBitmap(&output_pixels
);
775 // If the bitmap shouldn't have changed - compare against input.
776 if (xsize
== scaled_xsize
&& ysize
== scaled_ysize
&&
777 out_color_type
!= kAlpha_8_SkColorType
) {
778 const std::vector
<GLHelperScaling::ScalerStage
> dummy_stages
;
779 Compare(input_pixels
.get(),
784 message
+ " comparing against input");
788 // Now transform the bitmap using the reference implementation.
789 SkBitmap scaled_pixels
;
790 scaled_pixels
.allocPixels(SkImageInfo::Make(scaled_xsize
,
792 kRGBA_8888_SkColorType
,
793 kPremul_SkAlphaType
));
794 SkBitmap truth_pixels
;
797 input_pixels
.get(), &scaled_pixels
, kQualities
[quality_index
]);
798 // Step 2: Encode to grayscale if needed.
799 if (out_color_type
== kAlpha_8_SkColorType
) {
800 truth_pixels
.allocPixels(SkImageInfo::Make(
801 scaled_xsize
, scaled_ysize
, out_color_type
, kPremul_SkAlphaType
));
802 EncodeToGrayscaleSlow(&scaled_pixels
, &truth_pixels
);
804 truth_pixels
= scaled_pixels
;
807 // Now compare the results.
808 SkAutoLockPixels
lock_input(truth_pixels
);
809 const std::vector
<GLHelperScaling::ScalerStage
> dummy_stages
;
810 Compare(&truth_pixels
,
815 message
+ " comparing against transformed/scaled");
817 context_
->deleteTexture(src_texture
);
818 context_
->deleteFramebuffer(framebuffer
);
821 // Scaling test: Create a test image, scale it using GLHelperScaling
822 // and a reference implementation and compare the results.
823 void TestScale(int xsize
,
828 size_t quality_index
,
830 WebGLId src_texture
= context_
->createTexture();
831 WebGLId framebuffer
= context_
->createFramebuffer();
832 scoped_ptr
<SkBitmap
> input_pixels
=
833 CreateTestBitmap(xsize
, ysize
, test_pattern
).Pass();
834 BindTextureAndFrameBuffer(
835 src_texture
, framebuffer
, input_pixels
.get(), xsize
, ysize
);
837 std::string message
= base::StringPrintf(
839 "output size: %dx%d "
840 "pattern: %d quality: %s",
846 kQualityNames
[quality_index
]);
848 std::vector
<GLHelperScaling::ScalerStage
> stages
;
849 helper_scaling_
->ComputeScalerStages(kQualities
[quality_index
],
850 gfx::Size(xsize
, ysize
),
851 gfx::Rect(0, 0, xsize
, ysize
),
852 gfx::Size(scaled_xsize
, scaled_ysize
),
856 ValidateScalerStages(kQualities
[quality_index
],
858 gfx::Size(scaled_xsize
, scaled_ysize
),
861 WebGLId dst_texture
=
862 helper_
->CopyAndScaleTexture(src_texture
,
863 gfx::Size(xsize
, ysize
),
864 gfx::Size(scaled_xsize
, scaled_ysize
),
866 kQualities
[quality_index
]);
868 SkBitmap output_pixels
;
869 output_pixels
.allocPixels(SkImageInfo::Make(scaled_xsize
,
871 kRGBA_8888_SkColorType
,
872 kPremul_SkAlphaType
));
874 helper_
->ReadbackTextureSync(
876 gfx::Rect(0, 0, scaled_xsize
, scaled_ysize
),
877 static_cast<unsigned char*>(output_pixels
.getPixels()),
878 kRGBA_8888_SkColorType
);
880 // Flip the pixels back.
881 FlipSKBitmap(&output_pixels
);
884 // If the bitmap shouldn't have changed - compare against input.
885 if (xsize
== scaled_xsize
&& ysize
== scaled_ysize
) {
886 Compare(input_pixels
.get(),
891 message
+ " comparing against input");
895 // Now scale the bitmap using the reference implementation.
896 SkBitmap truth_pixels
;
897 truth_pixels
.allocPixels(SkImageInfo::Make(scaled_xsize
,
899 kRGBA_8888_SkColorType
,
900 kPremul_SkAlphaType
));
902 input_pixels
.get(), &truth_pixels
, kQualities
[quality_index
]);
903 Compare(&truth_pixels
,
908 message
+ " comparing against scaled");
910 context_
->deleteTexture(src_texture
);
911 context_
->deleteTexture(dst_texture
);
912 context_
->deleteFramebuffer(framebuffer
);
915 // Create a scaling pipeline and check that it is made up of
916 // valid scaling operations.
917 void TestScalerPipeline(size_t quality
,
922 std::vector
<GLHelperScaling::ScalerStage
> stages
;
923 helper_scaling_
->ComputeScalerStages(kQualities
[quality
],
924 gfx::Size(xsize
, ysize
),
925 gfx::Rect(0, 0, xsize
, ysize
),
926 gfx::Size(dst_xsize
, dst_ysize
),
930 ValidateScalerStages(kQualities
[quality
],
932 gfx::Size(dst_xsize
, dst_ysize
),
935 "output size: %dx%d "
941 kQualityNames
[quality
]));
944 // Create a scaling pipeline and make sure that the steps
945 // are exactly the steps we expect.
946 void CheckPipeline(content::GLHelper::ScalerQuality quality
,
951 const std::string
& description
) {
952 std::vector
<GLHelperScaling::ScalerStage
> stages
;
953 helper_scaling_
->ComputeScalerStages(quality
,
954 gfx::Size(xsize
, ysize
),
955 gfx::Rect(0, 0, xsize
, ysize
),
956 gfx::Size(dst_xsize
, dst_ysize
),
960 ValidateScalerStages(content::GLHelper::SCALER_QUALITY_GOOD
,
962 gfx::Size(dst_xsize
, dst_ysize
),
964 EXPECT_EQ(PrintStages(stages
), description
);
967 // Note: Left/Right means Top/Bottom when used for Y dimension.
975 static Margin
NextMargin(Margin m
) {
982 return MarginInvalid
;
984 return MarginInvalid
;
988 int compute_margin(int insize
, int outsize
, Margin m
) {
989 int available
= outsize
- insize
;
992 EXPECT_TRUE(false) << "This should not happen.";
997 return (available
/ 2) & ~1;
1003 // Convert 0.0 - 1.0 to 0 - 255
1004 int float_to_byte(float v
) {
1005 int ret
= static_cast<int>(floorf(v
* 255.0f
+ 0.5f
));
1015 static void callcallback(const base::Callback
<void()>& callback
,
1020 void PrintPlane(unsigned char* plane
, int xsize
, int stride
, int ysize
) {
1021 for (int y
= 0; y
< ysize
; y
++) {
1022 std::string formatted
;
1023 for (int x
= 0; x
< xsize
; x
++) {
1024 formatted
.append(base::StringPrintf("%3d, ", plane
[y
* stride
+ x
]));
1026 LOG(ERROR
) << formatted
<< " (" << (plane
+ y
* stride
) << ")";
1030 // Compare two planes make sure that each component of each pixel
1031 // is no more than |maxdiff| apart.
1032 void ComparePlane(unsigned char* truth
,
1034 unsigned char* other
,
1040 std::string message
) {
1041 for (int x
= 0; x
< xsize
; x
++) {
1042 for (int y
= 0; y
< ysize
; y
++) {
1043 int a
= other
[y
* other_stride
+ x
];
1044 int b
= truth
[y
* truth_stride
+ x
];
1045 EXPECT_NEAR(a
, b
, maxdiff
) << " x=" << x
<< " y=" << y
<< " "
1047 if (std::abs(a
- b
) > maxdiff
) {
1048 LOG(ERROR
) << "-------expected--------";
1049 PrintPlane(truth
, xsize
, truth_stride
, ysize
);
1050 LOG(ERROR
) << "-------actual--------";
1051 PrintPlane(other
, xsize
, other_stride
, ysize
);
1053 LOG(ERROR
) << "-------before yuv conversion: red--------";
1054 PrintChannel(source
, 0);
1055 LOG(ERROR
) << "-------before yuv conversion: green------";
1056 PrintChannel(source
, 1);
1057 LOG(ERROR
) << "-------before yuv conversion: blue-------";
1058 PrintChannel(source
, 2);
1066 void DrawGridToBitmap(int w
, int h
,
1067 SkColor background_color
,
1072 ASSERT_GT(grid_pitch
, 0);
1073 ASSERT_GT(grid_width
, 0);
1074 ASSERT_NE(background_color
, grid_color
);
1076 for (int y
= 0; y
< h
; ++y
) {
1077 bool y_on_grid
= ((y
% grid_pitch
) < grid_width
);
1079 for (int x
= 0; x
< w
; ++x
) {
1080 bool on_grid
= (y_on_grid
|| ((x
% grid_pitch
) < grid_width
));
1082 if (bmp
.colorType() == kRGBA_8888_SkColorType
||
1083 bmp
.colorType() == kBGRA_8888_SkColorType
) {
1084 *bmp
.getAddr32(x
, y
) = (on_grid
? grid_color
: background_color
);
1085 } else if (bmp
.colorType() == kRGB_565_SkColorType
) {
1086 *bmp
.getAddr16(x
, y
) = (on_grid
? grid_color
: background_color
);
1092 void DrawCheckerToBitmap(int w
, int h
,
1093 SkColor color1
, SkColor color2
,
1094 int rect_w
, int rect_h
,
1096 ASSERT_GT(rect_w
, 0);
1097 ASSERT_GT(rect_h
, 0);
1098 ASSERT_NE(color1
, color2
);
1100 for (int y
= 0; y
< h
; ++y
) {
1101 bool y_bit
= (((y
/ rect_h
) & 0x1) == 0);
1103 for (int x
= 0; x
< w
; ++x
) {
1104 bool x_bit
= (((x
/ rect_w
) & 0x1) == 0);
1106 bool use_color2
= (x_bit
!= y_bit
); // xor
1107 if (bmp
.colorType() == kRGBA_8888_SkColorType
||
1108 bmp
.colorType() == kBGRA_8888_SkColorType
) {
1109 *bmp
.getAddr32(x
, y
) = (use_color2
? color2
: color1
);
1110 } else if (bmp
.colorType() == kRGB_565_SkColorType
) {
1111 *bmp
.getAddr16(x
, y
) = (use_color2
? color2
: color1
);
1117 bool ColorComponentsClose(SkColor component1
,
1119 SkColorType color_type
) {
1120 int c1
= static_cast<int>(component1
);
1121 int c2
= static_cast<int>(component2
);
1122 bool result
= false;
1123 switch (color_type
) {
1124 case kRGBA_8888_SkColorType
:
1125 case kBGRA_8888_SkColorType
:
1126 result
= (std::abs(c1
- c2
) == 0);
1128 case kRGB_565_SkColorType
:
1129 result
= (std::abs(c1
- c2
) <= 7);
1137 bool ColorsClose(SkColor color1
, SkColor color2
, SkColorType color_type
) {
1138 bool red
= ColorComponentsClose(SkColorGetR(color1
),
1139 SkColorGetR(color2
), color_type
);
1140 bool green
= ColorComponentsClose(SkColorGetG(color1
),
1141 SkColorGetG(color2
), color_type
);
1142 bool blue
= ColorComponentsClose(SkColorGetB(color1
),
1143 SkColorGetB(color2
), color_type
);
1144 bool alpha
= ColorComponentsClose(SkColorGetA(color1
),
1145 SkColorGetA(color2
), color_type
);
1146 if (color_type
== kRGB_565_SkColorType
) {
1147 return red
&& blue
&& green
;
1149 return red
&& blue
&& green
&& alpha
;
1152 bool IsEqual(const SkBitmap
& bmp1
, const SkBitmap
& bmp2
) {
1153 if (bmp1
.isNull() && bmp2
.isNull())
1155 if (bmp1
.width() != bmp2
.width() ||
1156 bmp1
.height() != bmp2
.height()) {
1157 LOG(ERROR
) << "Bitmap geometry check failure";
1160 if (bmp1
.colorType() != bmp2
.colorType())
1163 SkAutoLockPixels
lock1(bmp1
);
1164 SkAutoLockPixels
lock2(bmp2
);
1165 if (!bmp1
.getPixels() || !bmp2
.getPixels()) {
1166 LOG(ERROR
) << "Empty Bitmap!";
1169 for (int y
= 0; y
< bmp1
.height(); ++y
) {
1170 for (int x
= 0; x
< bmp1
.width(); ++x
) {
1171 if (!ColorsClose(bmp1
.getColor(x
,y
),
1173 bmp1
.colorType())) {
1174 LOG(ERROR
) << "Bitmap color comparision failure";
1182 void BindAndAttachTextureWithPixels(GLuint src_texture
,
1183 SkColorType color_type
,
1184 const gfx::Size
& src_size
,
1185 const SkBitmap
& input_pixels
) {
1186 context_
->bindTexture(GL_TEXTURE_2D
, src_texture
);
1188 switch (color_type
) {
1189 case kBGRA_8888_SkColorType
:
1190 format
= GL_BGRA_EXT
;
1192 case kRGBA_8888_SkColorType
:
1195 case kRGB_565_SkColorType
:
1201 GLenum type
= (color_type
== kRGB_565_SkColorType
) ?
1202 GL_UNSIGNED_SHORT_5_6_5
: GL_UNSIGNED_BYTE
;
1203 context_
->texImage2D(GL_TEXTURE_2D
,
1211 input_pixels
.getPixels());
1214 void ReadBackTexture(GLuint src_texture
,
1215 const gfx::Size
& src_size
,
1216 unsigned char* pixels
,
1217 SkColorType color_type
,
1220 base::RunLoop run_loop
;
1221 helper_
->ReadbackTextureAsync(src_texture
,
1225 base::Bind(&callcallback
,
1226 run_loop
.QuitClosure()));
1229 helper_
->ReadbackTextureSync(src_texture
,
1230 gfx::Rect(src_size
),
1235 // Test basic format readback.
1236 bool TestTextureFormatReadback(const gfx::Size
& src_size
,
1237 SkColorType color_type
,
1240 SkImageInfo::Make(src_size
.width(),
1243 kPremul_SkAlphaType
);
1244 if (!helper_
->IsReadbackConfigSupported(color_type
)) {
1245 LOG(INFO
) << "Skipping test format not supported" << color_type
;
1248 WebGLId src_texture
= context_
->createTexture();
1249 SkBitmap input_pixels
;
1250 input_pixels
.allocPixels(info
);
1251 // Test Pattern-1, Fill with Plain color pattern.
1252 // Erase the input bitmap with red color.
1253 input_pixels
.eraseColor(SK_ColorRED
);
1254 BindAndAttachTextureWithPixels(src_texture
,
1258 SkBitmap output_pixels
;
1259 output_pixels
.allocPixels(info
);
1260 // Initialize the output bitmap with Green color.
1261 // When the readback is over output bitmap should have the red color.
1262 output_pixels
.eraseColor(SK_ColorGREEN
);
1263 uint8
* pixels
= static_cast<uint8
*>(output_pixels
.getPixels());
1264 ReadBackTexture(src_texture
, src_size
, pixels
, color_type
, async
);
1265 bool result
= IsEqual(input_pixels
, output_pixels
);
1267 LOG(ERROR
) << "Bitmap comparision failure Pattern-1";
1270 const int rect_w
= 10, rect_h
= 4, src_grid_pitch
= 10, src_grid_width
= 4;
1271 const SkColor color1
= SK_ColorRED
, color2
= SK_ColorBLUE
;
1272 // Test Pattern-2, Fill with Grid Pattern.
1273 DrawGridToBitmap(src_size
.width(), src_size
.height(),
1275 src_grid_pitch
, src_grid_width
,
1277 BindAndAttachTextureWithPixels(src_texture
,
1281 ReadBackTexture(src_texture
, src_size
, pixels
, color_type
, async
);
1282 result
= IsEqual(input_pixels
, output_pixels
);
1284 LOG(ERROR
) << "Bitmap comparision failure Pattern-2";
1287 // Test Pattern-3, Fill with CheckerBoard Pattern.
1288 DrawCheckerToBitmap(src_size
.width(),
1291 color2
, rect_w
, rect_h
, input_pixels
);
1292 BindAndAttachTextureWithPixels(src_texture
,
1296 ReadBackTexture(src_texture
, src_size
, pixels
, color_type
, async
);
1297 result
= IsEqual(input_pixels
, output_pixels
);
1299 LOG(ERROR
) << "Bitmap comparision failure Pattern-3";
1302 context_
->deleteTexture(src_texture
);
1309 // YUV readback test. Create a test pattern, convert to YUV
1310 // with reference implementation and compare to what gl_helper
1312 void TestYUVReadback(int xsize
,
1321 content::GLHelper::ScalerQuality quality
) {
1322 WebGLId src_texture
= context_
->createTexture();
1323 SkBitmap input_pixels
;
1324 input_pixels
.allocN32Pixels(xsize
, ysize
);
1326 for (int x
= 0; x
< xsize
; ++x
) {
1327 for (int y
= 0; y
< ysize
; ++y
) {
1328 switch (test_pattern
) {
1329 case 0: // Smooth test pattern
1330 SetChannel(&input_pixels
, x
, y
, 0, x
* 10);
1331 SetChannel(&input_pixels
, x
, y
, 1, y
* 10);
1332 SetChannel(&input_pixels
, x
, y
, 2, (x
+ y
) * 10);
1333 SetChannel(&input_pixels
, x
, y
, 3, 255);
1335 case 1: // Small blocks
1336 SetChannel(&input_pixels
, x
, y
, 0, x
& 1 ? 255 : 0);
1337 SetChannel(&input_pixels
, x
, y
, 1, y
& 1 ? 255 : 0);
1338 SetChannel(&input_pixels
, x
, y
, 2, (x
+ y
) & 1 ? 255 : 0);
1339 SetChannel(&input_pixels
, x
, y
, 3, 255);
1341 case 2: // Medium blocks
1342 SetChannel(&input_pixels
, x
, y
, 0, 10 + x
/ 2 * 50);
1343 SetChannel(&input_pixels
, x
, y
, 1, 10 + y
/ 3 * 50);
1344 SetChannel(&input_pixels
, x
, y
, 2, (x
+ y
) / 5 * 50 + 5);
1345 SetChannel(&input_pixels
, x
, y
, 3, 255);
1351 context_
->bindTexture(GL_TEXTURE_2D
, src_texture
);
1352 context_
->texImage2D(GL_TEXTURE_2D
,
1360 input_pixels
.getPixels());
1362 gpu::Mailbox mailbox
;
1363 context_
->genMailboxCHROMIUM(mailbox
.name
);
1364 EXPECT_FALSE(mailbox
.IsZero());
1365 context_
->produceTextureCHROMIUM(GL_TEXTURE_2D
, mailbox
.name
);
1366 uint32 sync_point
= context_
->insertSyncPoint();
1368 std::string message
= base::StringPrintf(
1369 "input size: %dx%d "
1370 "output size: %dx%d "
1372 "pattern: %d %s %s",
1380 flip
? "flip" : "noflip",
1381 flip
? "mrt" : "nomrt");
1382 scoped_ptr
<ReadbackYUVInterface
> yuv_reader(
1383 helper_
->CreateReadbackPipelineYUV(
1385 gfx::Size(xsize
, ysize
),
1386 gfx::Rect(0, 0, xsize
, ysize
),
1387 gfx::Size(xsize
, ysize
),
1391 scoped_refptr
<media::VideoFrame
> output_frame
=
1392 media::VideoFrame::CreateFrame(
1393 media::PIXEL_FORMAT_YV12
,
1394 // The coded size of the output frame is rounded up to the next
1395 // 16-byte boundary. This tests that the readback is being
1396 // positioned inside the frame's visible region, and not dependent
1397 // on its coded size.
1398 gfx::Size((output_xsize
+ 15) & ~15, (output_ysize
+ 15) & ~15),
1399 gfx::Rect(0, 0, output_xsize
, output_ysize
),
1400 gfx::Size(output_xsize
, output_ysize
),
1401 base::TimeDelta::FromSeconds(0));
1402 scoped_refptr
<media::VideoFrame
> truth_frame
=
1403 media::VideoFrame::CreateFrame(
1404 media::PIXEL_FORMAT_YV12
, gfx::Size(output_xsize
, output_ysize
),
1405 gfx::Rect(0, 0, output_xsize
, output_ysize
),
1406 gfx::Size(output_xsize
, output_ysize
),
1407 base::TimeDelta::FromSeconds(0));
1409 base::RunLoop run_loop
;
1410 yuv_reader
->ReadbackYUV(mailbox
,
1413 gfx::Point(xmargin
, ymargin
),
1414 base::Bind(&callcallback
, run_loop
.QuitClosure()));
1418 FlipSKBitmap(&input_pixels
);
1421 unsigned char* Y
= truth_frame
->visible_data(media::VideoFrame::kYPlane
);
1422 unsigned char* U
= truth_frame
->visible_data(media::VideoFrame::kUPlane
);
1423 unsigned char* V
= truth_frame
->visible_data(media::VideoFrame::kVPlane
);
1424 int32 y_stride
= truth_frame
->stride(media::VideoFrame::kYPlane
);
1425 int32 u_stride
= truth_frame
->stride(media::VideoFrame::kUPlane
);
1426 int32 v_stride
= truth_frame
->stride(media::VideoFrame::kVPlane
);
1427 memset(Y
, 0x00, y_stride
* output_ysize
);
1428 memset(U
, 0x80, u_stride
* output_ysize
/ 2);
1429 memset(V
, 0x80, v_stride
* output_ysize
/ 2);
1431 const float kRGBtoYColorWeights
[] = {0.257f
, 0.504f
, 0.098f
, 0.0625f
};
1432 const float kRGBtoUColorWeights
[] = {-0.148f
, -0.291f
, 0.439f
, 0.5f
};
1433 const float kRGBtoVColorWeights
[] = {0.439f
, -0.368f
, -0.071f
, 0.5f
};
1435 for (int y
= 0; y
< ysize
; y
++) {
1436 for (int x
= 0; x
< xsize
; x
++) {
1437 Y
[(y
+ ymargin
) * y_stride
+ x
+ xmargin
] = float_to_byte(
1438 ChannelAsFloat(&input_pixels
, x
, y
, 0) * kRGBtoYColorWeights
[0] +
1439 ChannelAsFloat(&input_pixels
, x
, y
, 1) * kRGBtoYColorWeights
[1] +
1440 ChannelAsFloat(&input_pixels
, x
, y
, 2) * kRGBtoYColorWeights
[2] +
1441 kRGBtoYColorWeights
[3]);
1445 for (int y
= 0; y
< ysize
/ 2; y
++) {
1446 for (int x
= 0; x
< xsize
/ 2; x
++) {
1447 U
[(y
+ ymargin
/ 2) * u_stride
+ x
+ xmargin
/ 2] =
1448 float_to_byte(Bilinear(&input_pixels
, x
* 2 + 1.0, y
* 2 + 1.0, 0) *
1449 kRGBtoUColorWeights
[0] +
1450 Bilinear(&input_pixels
, x
* 2 + 1.0, y
* 2 + 1.0, 1) *
1451 kRGBtoUColorWeights
[1] +
1452 Bilinear(&input_pixels
, x
* 2 + 1.0, y
* 2 + 1.0, 2) *
1453 kRGBtoUColorWeights
[2] +
1454 kRGBtoUColorWeights
[3]);
1455 V
[(y
+ ymargin
/ 2) * v_stride
+ x
+ xmargin
/ 2] =
1456 float_to_byte(Bilinear(&input_pixels
, x
* 2 + 1.0, y
* 2 + 1.0, 0) *
1457 kRGBtoVColorWeights
[0] +
1458 Bilinear(&input_pixels
, x
* 2 + 1.0, y
* 2 + 1.0, 1) *
1459 kRGBtoVColorWeights
[1] +
1460 Bilinear(&input_pixels
, x
* 2 + 1.0, y
* 2 + 1.0, 2) *
1461 kRGBtoVColorWeights
[2] +
1462 kRGBtoVColorWeights
[3]);
1468 output_frame
->visible_data(media::VideoFrame::kYPlane
),
1469 output_frame
->stride(media::VideoFrame::kYPlane
),
1474 message
+ " Y plane");
1477 output_frame
->visible_data(media::VideoFrame::kUPlane
),
1478 output_frame
->stride(media::VideoFrame::kUPlane
),
1483 message
+ " U plane");
1486 output_frame
->visible_data(media::VideoFrame::kVPlane
),
1487 output_frame
->stride(media::VideoFrame::kVPlane
),
1492 message
+ " V plane");
1494 context_
->deleteTexture(src_texture
);
1497 void TestAddOps(int src
, int dst
, bool scale_x
, bool allow3
) {
1498 std::deque
<GLHelperScaling::ScaleOp
> ops
;
1499 GLHelperScaling::ScaleOp::AddOps(src
, dst
, scale_x
, allow3
, &ops
);
1500 // Scale factor 3 is a special case.
1501 // It is currently only allowed by itself.
1502 if (allow3
&& dst
* 3 >= src
&& dst
* 2 < src
) {
1503 EXPECT_EQ(ops
[0].scale_factor
, 3);
1504 EXPECT_EQ(ops
.size(), 1U);
1505 EXPECT_EQ(ops
[0].scale_x
, scale_x
);
1506 EXPECT_EQ(ops
[0].scale_size
, dst
);
1510 for (size_t i
= 0; i
< ops
.size(); i
++) {
1511 EXPECT_EQ(ops
[i
].scale_x
, scale_x
);
1513 // Only the first op is allowed to be a scale up.
1514 // (Scaling up *after* scaling down would make it fuzzy.)
1515 EXPECT_TRUE(ops
[0].scale_factor
== 0 || ops
[0].scale_factor
== 2);
1517 // All other operations must be 50% downscales.
1518 EXPECT_EQ(ops
[i
].scale_factor
, 2);
1521 // Check that the scale factors make sense and add up.
1523 for (int i
= static_cast<int>(ops
.size() - 1); i
>= 0; i
--) {
1524 EXPECT_EQ(tmp
, ops
[i
].scale_size
);
1525 if (ops
[i
].scale_factor
== 0) {
1527 EXPECT_GT(tmp
, src
);
1530 tmp
*= ops
[i
].scale_factor
;
1533 EXPECT_EQ(tmp
, src
);
1536 void CheckPipeline2(int xsize
,
1540 const std::string
& description
) {
1541 std::vector
<GLHelperScaling::ScalerStage
> stages
;
1542 helper_scaling_
->ConvertScalerOpsToScalerStages(
1543 content::GLHelper::SCALER_QUALITY_GOOD
,
1544 gfx::Size(xsize
, ysize
),
1545 gfx::Rect(0, 0, xsize
, ysize
),
1546 gfx::Size(dst_xsize
, dst_ysize
),
1552 EXPECT_EQ(x_ops_
.size(), 0U);
1553 EXPECT_EQ(y_ops_
.size(), 0U);
1554 ValidateScalerStages(content::GLHelper::SCALER_QUALITY_GOOD
,
1556 gfx::Size(dst_xsize
, dst_ysize
),
1558 EXPECT_EQ(PrintStages(stages
), description
);
1561 void CheckOptimizationsTest() {
1562 // Basic upscale. X and Y should be combined into one pass.
1563 x_ops_
.push_back(GLHelperScaling::ScaleOp(0, true, 2000));
1564 y_ops_
.push_back(GLHelperScaling::ScaleOp(0, false, 2000));
1565 CheckPipeline2(1024, 768, 2000, 2000, "1024x768 -> 2000x2000 bilinear\n");
1567 // X scaled 1/2, Y upscaled, should still be one pass.
1568 x_ops_
.push_back(GLHelperScaling::ScaleOp(2, true, 512));
1569 y_ops_
.push_back(GLHelperScaling::ScaleOp(0, false, 2000));
1570 CheckPipeline2(1024, 768, 512, 2000, "1024x768 -> 512x2000 bilinear\n");
1572 // X upscaled, Y scaled 1/2, one bilinear pass
1573 x_ops_
.push_back(GLHelperScaling::ScaleOp(0, true, 2000));
1574 y_ops_
.push_back(GLHelperScaling::ScaleOp(2, false, 384));
1575 CheckPipeline2(1024, 768, 2000, 384, "1024x768 -> 2000x384 bilinear\n");
1577 // X scaled 1/2, Y scaled 1/2, one bilinear pass
1578 x_ops_
.push_back(GLHelperScaling::ScaleOp(2, true, 512));
1579 y_ops_
.push_back(GLHelperScaling::ScaleOp(2, false, 384));
1580 CheckPipeline2(1024, 768, 512, 384, "1024x768 -> 512x384 bilinear\n");
1582 // X scaled 1/2, Y scaled to 60%, one bilinear2 pass.
1583 x_ops_
.push_back(GLHelperScaling::ScaleOp(2, true, 50));
1584 y_ops_
.push_back(GLHelperScaling::ScaleOp(0, false, 120));
1585 y_ops_
.push_back(GLHelperScaling::ScaleOp(2, false, 60));
1586 CheckPipeline2(100, 100, 50, 60, "100x100 -> 50x60 bilinear2 Y\n");
1588 // X scaled to 60%, Y scaled 1/2, one bilinear2 pass.
1589 x_ops_
.push_back(GLHelperScaling::ScaleOp(0, true, 120));
1590 x_ops_
.push_back(GLHelperScaling::ScaleOp(2, true, 60));
1591 y_ops_
.push_back(GLHelperScaling::ScaleOp(2, false, 50));
1592 CheckPipeline2(100, 100, 60, 50, "100x100 -> 60x50 bilinear2 X\n");
1594 // X scaled to 60%, Y scaled 60%, one bilinear2x2 pass.
1595 x_ops_
.push_back(GLHelperScaling::ScaleOp(0, true, 120));
1596 x_ops_
.push_back(GLHelperScaling::ScaleOp(2, true, 60));
1597 y_ops_
.push_back(GLHelperScaling::ScaleOp(0, false, 120));
1598 y_ops_
.push_back(GLHelperScaling::ScaleOp(2, false, 60));
1599 CheckPipeline2(100, 100, 60, 60, "100x100 -> 60x60 bilinear2x2\n");
1601 // X scaled to 40%, Y scaled 40%, two bilinear3 passes.
1602 x_ops_
.push_back(GLHelperScaling::ScaleOp(3, true, 40));
1603 y_ops_
.push_back(GLHelperScaling::ScaleOp(3, false, 40));
1608 "100x100 -> 100x40 bilinear3 Y\n"
1609 "100x40 -> 40x40 bilinear3 X\n");
1611 // X scaled to 60%, Y scaled 40%
1612 x_ops_
.push_back(GLHelperScaling::ScaleOp(0, true, 120));
1613 x_ops_
.push_back(GLHelperScaling::ScaleOp(2, true, 60));
1614 y_ops_
.push_back(GLHelperScaling::ScaleOp(3, false, 40));
1619 "100x100 -> 100x40 bilinear3 Y\n"
1620 "100x40 -> 60x40 bilinear2 X\n");
1622 // X scaled to 40%, Y scaled 60%
1623 x_ops_
.push_back(GLHelperScaling::ScaleOp(3, true, 40));
1624 y_ops_
.push_back(GLHelperScaling::ScaleOp(0, false, 120));
1625 y_ops_
.push_back(GLHelperScaling::ScaleOp(2, false, 60));
1630 "100x100 -> 100x60 bilinear2 Y\n"
1631 "100x60 -> 40x60 bilinear3 X\n");
1633 // X scaled to 30%, Y scaled 30%
1634 x_ops_
.push_back(GLHelperScaling::ScaleOp(0, true, 120));
1635 x_ops_
.push_back(GLHelperScaling::ScaleOp(2, true, 60));
1636 x_ops_
.push_back(GLHelperScaling::ScaleOp(2, true, 30));
1637 y_ops_
.push_back(GLHelperScaling::ScaleOp(0, false, 120));
1638 y_ops_
.push_back(GLHelperScaling::ScaleOp(2, false, 60));
1639 y_ops_
.push_back(GLHelperScaling::ScaleOp(2, false, 30));
1644 "100x100 -> 100x30 bilinear4 Y\n"
1645 "100x30 -> 30x30 bilinear4 X\n");
1647 // X scaled to 50%, Y scaled 30%
1648 x_ops_
.push_back(GLHelperScaling::ScaleOp(2, true, 50));
1649 y_ops_
.push_back(GLHelperScaling::ScaleOp(0, false, 120));
1650 y_ops_
.push_back(GLHelperScaling::ScaleOp(2, false, 60));
1651 y_ops_
.push_back(GLHelperScaling::ScaleOp(2, false, 30));
1652 CheckPipeline2(100, 100, 50, 30, "100x100 -> 50x30 bilinear4 Y\n");
1654 // X scaled to 150%, Y scaled 30%
1655 // Note that we avoid combinding X and Y passes
1656 // as that would probably be LESS efficient here.
1657 x_ops_
.push_back(GLHelperScaling::ScaleOp(0, true, 150));
1658 y_ops_
.push_back(GLHelperScaling::ScaleOp(0, false, 120));
1659 y_ops_
.push_back(GLHelperScaling::ScaleOp(2, false, 60));
1660 y_ops_
.push_back(GLHelperScaling::ScaleOp(2, false, 30));
1665 "100x100 -> 100x30 bilinear4 Y\n"
1666 "100x30 -> 150x30 bilinear\n");
1668 // X scaled to 1%, Y scaled 1%
1669 x_ops_
.push_back(GLHelperScaling::ScaleOp(0, true, 128));
1670 x_ops_
.push_back(GLHelperScaling::ScaleOp(2, true, 64));
1671 x_ops_
.push_back(GLHelperScaling::ScaleOp(2, true, 32));
1672 x_ops_
.push_back(GLHelperScaling::ScaleOp(2, true, 16));
1673 x_ops_
.push_back(GLHelperScaling::ScaleOp(2, true, 8));
1674 x_ops_
.push_back(GLHelperScaling::ScaleOp(2, true, 4));
1675 x_ops_
.push_back(GLHelperScaling::ScaleOp(2, true, 2));
1676 x_ops_
.push_back(GLHelperScaling::ScaleOp(2, true, 1));
1677 y_ops_
.push_back(GLHelperScaling::ScaleOp(0, false, 128));
1678 y_ops_
.push_back(GLHelperScaling::ScaleOp(2, false, 64));
1679 y_ops_
.push_back(GLHelperScaling::ScaleOp(2, false, 32));
1680 y_ops_
.push_back(GLHelperScaling::ScaleOp(2, false, 16));
1681 y_ops_
.push_back(GLHelperScaling::ScaleOp(2, false, 8));
1682 y_ops_
.push_back(GLHelperScaling::ScaleOp(2, false, 4));
1683 y_ops_
.push_back(GLHelperScaling::ScaleOp(2, false, 2));
1684 y_ops_
.push_back(GLHelperScaling::ScaleOp(2, false, 1));
1689 "100x100 -> 100x32 bilinear4 Y\n"
1690 "100x32 -> 100x4 bilinear4 Y\n"
1691 "100x4 -> 64x1 bilinear2x2\n"
1692 "64x1 -> 8x1 bilinear4 X\n"
1693 "8x1 -> 1x1 bilinear4 X\n");
1696 scoped_ptr
<WebGraphicsContext3DInProcessCommandBufferImpl
> context_
;
1697 gpu::ContextSupport
* context_support_
;
1698 scoped_ptr
<content::GLHelper
> helper_
;
1699 scoped_ptr
<content::GLHelperScaling
> helper_scaling_
;
1700 std::deque
<GLHelperScaling::ScaleOp
> x_ops_
, y_ops_
;
1703 class GLHelperPixelTest
: public GLHelperTest
{
1705 gfx::DisableNullDrawGLBindings enable_pixel_output_
;
1708 TEST_F(GLHelperTest
, RGBASyncReadbackTest
) {
1709 const int kTestSize
= 64;
1710 bool result
= TestTextureFormatReadback(gfx::Size(kTestSize
,kTestSize
),
1711 kRGBA_8888_SkColorType
,
1713 EXPECT_EQ(result
, true);
1717 TEST_F(GLHelperTest
, BGRASyncReadbackTest
) {
1718 const int kTestSize
= 64;
1719 bool result
= TestTextureFormatReadback(gfx::Size(kTestSize
,kTestSize
),
1720 kBGRA_8888_SkColorType
,
1722 EXPECT_EQ(result
, true);
1725 TEST_F(GLHelperTest
, RGB565SyncReadbackTest
) {
1726 const int kTestSize
= 64;
1727 bool result
= TestTextureFormatReadback(gfx::Size(kTestSize
,kTestSize
),
1728 kRGB_565_SkColorType
,
1730 EXPECT_EQ(result
, true);
1733 TEST_F(GLHelperTest
, RGBAASyncReadbackTest
) {
1734 const int kTestSize
= 64;
1735 bool result
= TestTextureFormatReadback(gfx::Size(kTestSize
,kTestSize
),
1736 kRGBA_8888_SkColorType
,
1738 EXPECT_EQ(result
, true);
1741 TEST_F(GLHelperTest
, BGRAASyncReadbackTest
) {
1742 const int kTestSize
= 64;
1743 bool result
= TestTextureFormatReadback(gfx::Size(kTestSize
,kTestSize
),
1744 kBGRA_8888_SkColorType
,
1746 EXPECT_EQ(result
, true);
1749 TEST_F(GLHelperTest
, RGB565ASyncReadbackTest
) {
1750 const int kTestSize
= 64;
1751 bool result
= TestTextureFormatReadback(gfx::Size(kTestSize
,kTestSize
),
1752 kRGB_565_SkColorType
,
1754 EXPECT_EQ(result
, true);
1757 TEST_F(GLHelperPixelTest
, YUVReadbackOptTest
) {
1758 // This test uses the gpu.service/gpu_decoder tracing events to detect how
1759 // many scaling passes are actually performed by the YUV readback pipeline.
1760 StartTracing(TRACE_DISABLED_BY_DEFAULT("gpu.service") ","
1761 TRACE_DISABLED_BY_DEFAULT("gpu_decoder"));
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
)));