1 // Copyright (c) 2013 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.
8 #include "base/basictypes.h"
9 #include "base/file_util.h"
10 #include "base/hash.h"
11 #include "base/scoped_native_library.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/time/time.h"
14 #include "base/win/scoped_comptr.h"
15 #include "base/win/windows_version.h"
16 #include "media/base/simd/convert_rgb_to_yuv.h"
17 #include "media/base/yuv_convert.h"
18 #include "skia/ext/image_operations.h"
19 #include "testing/gtest/include/gtest/gtest-param-test.h"
20 #include "testing/gtest/include/gtest/gtest.h"
21 #include "third_party/skia/include/core/SkBitmap.h"
22 #include "third_party/skia/include/core/SkColor.h"
23 #include "ui/gfx/codec/png_codec.h"
24 #include "ui/gfx/rect.h"
25 #include "ui/surface/accelerated_surface_transformer_win.h"
26 #include "ui/surface/accelerated_surface_win.h"
27 #include "ui/surface/d3d9_utils_win.h"
29 namespace d3d_utils
= ui_surface_d3d9_utils
;
31 using base::win::ScopedComPtr
;
32 using std::uniform_int_distribution
;
36 // Debug flag, useful when hacking on tests.
37 const bool kDumpImagesOnFailure
= false;
39 SkBitmap
ToSkBitmap(IDirect3DSurface9
* surface
, bool is_single_channel
) {
40 D3DLOCKED_RECT locked_rect
;
41 EXPECT_HRESULT_SUCCEEDED(
42 surface
->LockRect(&locked_rect
, NULL
, D3DLOCK_READONLY
));
45 gfx::Size size
= d3d_utils::GetSize(surface
);
46 if (is_single_channel
)
47 size
= gfx::Size(size
.width() * 4, size
.height());
48 result
.setConfig(SkBitmap::kARGB_8888_Config
, size
.width(), size
.height(),
49 0, kOpaque_SkAlphaType
);
52 for (int y
= 0; y
< size
.height(); ++y
) {
53 uint8
* row8
= reinterpret_cast<uint8
*>(locked_rect
.pBits
) +
54 (y
* locked_rect
.Pitch
);
55 if (is_single_channel
) {
56 for (int x
= 0; x
< size
.width(); ++x
) {
57 *result
.getAddr32(x
, y
) = SkColorSetRGB(row8
[x
], row8
[x
], row8
[x
]);
60 uint32
* row32
= reinterpret_cast<uint32
*>(row8
);
61 for (int x
= 0; x
< size
.width(); ++x
) {
62 *result
.getAddr32(x
, y
) = row32
[x
] | 0xFF000000;
66 result
.unlockPixels();
67 result
.setImmutable();
68 surface
->UnlockRect();
72 bool WritePNGFile(const SkBitmap
& bitmap
, const base::FilePath
& file_path
) {
73 std::vector
<unsigned char> png_data
;
74 const bool discard_transparency
= true;
75 if (gfx::PNGCodec::EncodeBGRASkBitmap(bitmap
,
78 file_util::CreateDirectory(file_path
.DirName())) {
79 char* data
= reinterpret_cast<char*>(&png_data
[0]);
80 int size
= static_cast<int>(png_data
.size());
81 return file_util::WriteFile(file_path
, data
, size
) == size
;
88 // Test fixture for AcceleratedSurfaceTransformer.
90 // This class is parameterized so that it runs only on Vista+. See
91 // WindowsVersionIfVistaOrBetter() for details on this works.
92 class AcceleratedSurfaceTransformerTest
: public testing::TestWithParam
<int> {
94 AcceleratedSurfaceTransformerTest() : color_error_tolerance_(0) {};
96 IDirect3DDevice9Ex
* device() { return device_
.get(); }
98 virtual void SetUp() {
99 if (!d3d_module_
.is_valid()) {
100 if (!d3d_utils::LoadD3D9(&d3d_module_
)) {
101 GTEST_FAIL() << "Could not load d3d9.dll";
105 if (!d3d_utils::CreateDevice(d3d_module_
,
107 D3DPRESENT_INTERVAL_IMMEDIATE
,
108 device_
.Receive())) {
109 GTEST_FAIL() << "Could not create Direct3D device.";
113 SeedRandom("default");
116 virtual void TearDown() {
120 // Gets a human-readable identifier of the graphics hardware being used,
121 // intended for use inside of SCOPED_TRACE().
122 std::string
GetAdapterInfo() {
123 ScopedComPtr
<IDirect3D9
> d3d
;
124 EXPECT_HRESULT_SUCCEEDED(device()->GetDirect3D(d3d
.Receive()));
125 D3DADAPTER_IDENTIFIER9 info
;
126 EXPECT_HRESULT_SUCCEEDED(d3d
->GetAdapterIdentifier(0, 0, &info
));
127 return base::StringPrintf(
128 "Running on graphics hardware: %s", info
.Description
);
131 void SeedRandom(const char* seed
) {
132 rng_
.seed(base::Hash(seed
));
133 random_dword_
.reset();
136 // Driver workaround: on an Intel GPU (Mobile Intel 965 Express), it seems
137 // necessary to flush between drawing and locking, for the synchronization
138 // to behave properly.
139 void BeforeLockWorkaround() {
140 EXPECT_HRESULT_SUCCEEDED(
141 device()->Present(0, 0, 0, 0));
144 void WarnOnMissingFeatures(AcceleratedSurfaceTransformer
* gpu_ops
) {
145 // Prints a single warning line if some tests are feature-dependent
146 // and the feature is not supported by the current GPU.
147 if (!gpu_ops
->device_supports_multiple_render_targets()) {
148 LOG(WARNING
) << "MRT not supported, some tests will be skipped. "
153 // Locks and fills a surface with a checkerboard pattern where the colors
154 // are random but the total image pattern is horizontally and vertically
156 void FillSymmetricRandomCheckerboard(
157 IDirect3DSurface9
* lockable_surface
,
158 const gfx::Size
& size
,
159 int checker_square_size
) {
161 D3DLOCKED_RECT locked_rect
;
162 ASSERT_HRESULT_SUCCEEDED(
163 lockable_surface
->LockRect(&locked_rect
, NULL
, D3DLOCK_DISCARD
));
164 DWORD
* surface
= reinterpret_cast<DWORD
*>(locked_rect
.pBits
);
165 ASSERT_EQ(0, locked_rect
.Pitch
% sizeof(DWORD
));
166 int pitch
= locked_rect
.Pitch
/ sizeof(DWORD
);
168 for (int y
= 0; y
< (size
.height() + 1) / 2; y
+= checker_square_size
) {
169 for (int x
= 0; x
< (size
.width() + 1) / 2; x
+= checker_square_size
) {
170 DWORD color
= RandomColor();
171 int y_limit
= std::min(size
.height() / 2, y
+ checker_square_size
- 1);
172 int x_limit
= std::min(size
.width() / 2, x
+ checker_square_size
- 1);
173 for (int y_lo
= y
; y_lo
<= y_limit
; y_lo
++) {
174 for (int x_lo
= x
; x_lo
<= x_limit
; x_lo
++) {
175 int y_hi
= size
.height() - 1 - y_lo
;
176 int x_hi
= size
.width() - 1 - x_lo
;
177 surface
[x_lo
+ y_lo
*pitch
] = color
;
178 surface
[x_lo
+ y_hi
*pitch
] = color
;
179 surface
[x_hi
+ y_lo
*pitch
] = color
;
180 surface
[x_hi
+ y_hi
*pitch
] = color
;
186 lockable_surface
->UnlockRect();
189 void FillRandomCheckerboard(
190 IDirect3DSurface9
* lockable_surface
,
191 const gfx::Size
& size
,
192 int checker_square_size
) {
194 D3DLOCKED_RECT locked_rect
;
195 ASSERT_HRESULT_SUCCEEDED(
196 lockable_surface
->LockRect(&locked_rect
, NULL
, D3DLOCK_DISCARD
));
197 DWORD
* surface
= reinterpret_cast<DWORD
*>(locked_rect
.pBits
);
198 ASSERT_EQ(0, locked_rect
.Pitch
% sizeof(DWORD
));
199 int pitch
= locked_rect
.Pitch
/ sizeof(DWORD
);
201 for (int y
= 0; y
<= size
.height(); y
+= checker_square_size
) {
202 for (int x
= 0; x
<= size
.width(); x
+= checker_square_size
) {
203 DWORD color
= RandomColor();
204 int y_limit
= std::min(size
.height(), y
+ checker_square_size
);
205 int x_limit
= std::min(size
.width(), x
+ checker_square_size
);
206 for (int square_y
= y
; square_y
< y_limit
; square_y
++) {
207 for (int square_x
= x
; square_x
< x_limit
; square_x
++) {
208 surface
[square_x
+ square_y
*pitch
] = color
;
214 lockable_surface
->UnlockRect();
217 // Approximate color-equality check. Allows for some rounding error.
218 bool AssertSameColor(DWORD color_a
, DWORD color_b
) {
219 if (color_a
== color_b
)
221 uint8
* a
= reinterpret_cast<uint8
*>(&color_a
);
222 uint8
* b
= reinterpret_cast<uint8
*>(&color_b
);
224 for (int i
= 0; i
< 4; i
++)
225 max_error
= std::max(max_error
,
226 std::abs(static_cast<int>(a
[i
]) - b
[i
]));
228 if (max_error
<= color_error_tolerance())
231 std::string expected_color
=
232 base::StringPrintf("%3d, %3d, %3d, %3d", a
[0], a
[1], a
[2], a
[3]);
233 std::string actual_color
=
234 base::StringPrintf("%3d, %3d, %3d, %3d", b
[0], b
[1], b
[2], b
[3]);
235 EXPECT_EQ(expected_color
, actual_color
)
236 << "Componentwise color difference was "
237 << max_error
<< "; max allowed is " << color_error_tolerance();
242 bool AssertSameColor(uint8 color_a
, uint8 color_b
) {
243 if (color_a
== color_b
)
245 int max_error
= std::abs((int) color_a
- (int) color_b
);
246 if (max_error
<= color_error_tolerance())
248 ADD_FAILURE() << "Colors not equal: "
249 << base::StringPrintf("0x%x", color_a
)
250 << " vs. " << base::StringPrintf("0x%x", color_b
);
254 // Asserts that an image is symmetric with respect to itself: both
255 // horizontally and vertically, within the tolerance of AssertSameColor.
256 void AssertSymmetry(IDirect3DSurface9
* lockable_surface
,
257 const gfx::Size
& size
) {
258 BeforeLockWorkaround();
260 D3DLOCKED_RECT locked_rect
;
261 ASSERT_HRESULT_SUCCEEDED(
262 lockable_surface
->LockRect(&locked_rect
, NULL
, D3DLOCK_READONLY
));
263 ASSERT_EQ(0, locked_rect
.Pitch
% sizeof(DWORD
));
264 int pitch
= locked_rect
.Pitch
/ sizeof(DWORD
);
265 DWORD
* surface
= reinterpret_cast<DWORD
*>(locked_rect
.pBits
);
266 for (int y_lo
= 0; y_lo
< size
.height() / 2; y_lo
++) {
267 int y_hi
= size
.height() - 1 - y_lo
;
268 for (int x_lo
= 0; x_lo
< size
.width() / 2; x_lo
++) {
269 int x_hi
= size
.width() - 1 - x_lo
;
270 if (!AssertSameColor(surface
[x_lo
+ y_lo
*pitch
],
271 surface
[x_hi
+ y_lo
*pitch
])) {
272 lockable_surface
->UnlockRect();
273 GTEST_FAIL() << "Pixels (" << x_lo
<< ", " << y_lo
<< ") vs. "
274 << "(" << x_hi
<< ", " << y_lo
<< ")";
276 if (!AssertSameColor(surface
[x_hi
+ y_lo
*pitch
],
277 surface
[x_hi
+ y_hi
*pitch
])) {
278 lockable_surface
->UnlockRect();
279 GTEST_FAIL() << "Pixels (" << x_hi
<< ", " << y_lo
<< ") vs. "
280 << "(" << x_hi
<< ", " << y_hi
<< ")";
282 if (!AssertSameColor(surface
[x_hi
+ y_hi
*pitch
],
283 surface
[x_lo
+ y_hi
*pitch
])) {
284 lockable_surface
->UnlockRect();
285 GTEST_FAIL() << "Pixels (" << x_hi
<< ", " << y_hi
<< ") vs. "
286 << "(" << x_lo
<< ", " << y_hi
<< ")";
290 lockable_surface
->UnlockRect();
293 // Asserts that the actual image is a bit-identical, vertically mirrored
294 // copy of the expected image.
295 void AssertIsInvertedCopy(const gfx::Size
& size
,
296 IDirect3DSurface9
* expected
,
297 IDirect3DSurface9
* actual
) {
298 BeforeLockWorkaround();
300 D3DLOCKED_RECT locked_expected
, locked_actual
;
301 ASSERT_HRESULT_SUCCEEDED(
302 expected
->LockRect(&locked_expected
, NULL
, D3DLOCK_READONLY
));
303 ASSERT_HRESULT_SUCCEEDED(
304 actual
->LockRect(&locked_actual
, NULL
, D3DLOCK_READONLY
));
305 ASSERT_EQ(0, locked_expected
.Pitch
% sizeof(DWORD
));
306 int pitch
= locked_expected
.Pitch
/ sizeof(DWORD
);
307 DWORD
* expected_image
= reinterpret_cast<DWORD
*>(locked_expected
.pBits
);
308 DWORD
* actual_image
= reinterpret_cast<DWORD
*>(locked_actual
.pBits
);
309 for (int y
= 0; y
< size
.height(); y
++) {
310 int y_actual
= size
.height() - 1 - y
;
311 for (int x
= 0; x
< size
.width(); ++x
)
312 if (!AssertSameColor(expected_image
[y
*pitch
+ x
],
313 actual_image
[y_actual
*pitch
+ x
])) {
314 expected
->UnlockRect();
315 actual
->UnlockRect();
316 GTEST_FAIL() << "Pixels (" << x
<< ", " << y
<< ") vs. "
317 << "(" << x
<< ", " << y_actual
<< ")";
320 expected
->UnlockRect();
321 actual
->UnlockRect();
325 DWORD
RandomColor() {
326 return random_dword_(rng_
);
329 void set_color_error_tolerance(int value
) {
330 color_error_tolerance_
= value
;
333 int color_error_tolerance() {
334 return color_error_tolerance_
;
337 void DoResizeBilinearTest(AcceleratedSurfaceTransformer
* gpu_ops
,
338 const gfx::Size
& src_size
,
339 const gfx::Size
& dst_size
,
340 int checkerboard_size
) {
344 "Resizing %dx%d -> %dx%d at checkerboard size of %d",
345 src_size
.width(), src_size
.height(),
346 dst_size
.width(), dst_size
.height(),
349 set_color_error_tolerance(4);
351 base::win::ScopedComPtr
<IDirect3DSurface9
> src
, dst
;
352 ASSERT_TRUE(d3d_utils::CreateOrReuseLockableSurface(
353 device(), src_size
, &src
))
354 << "Could not create src render target";
355 ASSERT_TRUE(d3d_utils::CreateOrReuseLockableSurface(
356 device(), dst_size
, &dst
))
357 << "Could not create dst render target";
359 FillSymmetricRandomCheckerboard(src
, src_size
, checkerboard_size
);
361 ASSERT_TRUE(gpu_ops
->ResizeBilinear(src
, gfx::Rect(src_size
), dst
,
362 gfx::Rect(dst_size
)));
364 AssertSymmetry(dst
, dst_size
);
367 void CreateRandomCheckerboardTexture(
368 const gfx::Size
& size
,
369 int checkerboard_size
,
370 base::win::ScopedComPtr
<IDirect3DSurface9
>* reference_surface
,
371 base::win::ScopedComPtr
<IDirect3DTexture9
>* result
) {
372 base::win::ScopedComPtr
<IDirect3DSurface9
> dst
;
373 ASSERT_TRUE(d3d_utils::CreateOrReuseLockableSurface(device(), size
,
375 ASSERT_TRUE(d3d_utils::CreateOrReuseRenderTargetTexture(device(), size
,
376 result
, dst
.Receive()));
377 FillRandomCheckerboard(*reference_surface
, size
, checkerboard_size
);
378 ASSERT_HRESULT_SUCCEEDED(
379 device()->StretchRect(
380 *reference_surface
, NULL
, dst
, NULL
, D3DTEXF_NONE
));
383 void AssertSame(int width_in_bytes
, int height
, uint8
* reference
,
384 IDirect3DSurface9
* lockable
) {
385 BeforeLockWorkaround();
387 D3DLOCKED_RECT locked_rect
;
388 ASSERT_HRESULT_SUCCEEDED(
389 lockable
->LockRect(&locked_rect
, NULL
, D3DLOCK_READONLY
));
390 uint8
* actual
= reinterpret_cast<uint8
*>(locked_rect
.pBits
);
391 for (int y
= 0; y
< height
; ++y
) {
392 for (int x
= 0; x
< width_in_bytes
; ++x
) {
393 if (!AssertSameColor(reference
[y
* width_in_bytes
+ x
],
394 actual
[y
* locked_rect
.Pitch
+ x
])) {
395 lockable
->UnlockRect();
396 GTEST_FAIL() << "At pixel (" << x
<< ", " << y
<< ")";
400 lockable
->UnlockRect();
403 void DoCopyInvertedTest(AcceleratedSurfaceTransformer
* gpu_ops
,
404 const gfx::Size
& size
) {
406 SCOPED_TRACE(base::StringPrintf(
407 "CopyInverted @ %dx%d", size
.width(), size
.height()));
409 set_color_error_tolerance(0);
411 base::win::ScopedComPtr
<IDirect3DSurface9
> dst
, reference_pattern
;
412 base::win::ScopedComPtr
<IDirect3DTexture9
> src
;
414 CreateRandomCheckerboardTexture(size
, 1, &reference_pattern
, &src
);
416 // Alloc a slightly larger image 75% of the time, to test that the
417 // viewport is set properly.
418 const int kAlign
= 4;
419 gfx::Size
alloc_size((size
.width() + kAlign
- 1) / kAlign
* kAlign
,
420 (size
.height() + kAlign
- 1) / kAlign
* kAlign
);
422 ASSERT_TRUE(d3d_utils::CreateOrReuseLockableSurface(device(), alloc_size
,
423 &dst
)) << "Could not create dst render target.";
425 ASSERT_TRUE(gpu_ops
->CopyInverted(src
, dst
, size
));
426 AssertIsInvertedCopy(size
, reference_pattern
, dst
);
430 void DoYUVConversionTest(AcceleratedSurfaceTransformer
* gpu_ops
,
431 const gfx::Size
& src_size
,
432 int checkerboard_size
) {
433 // Test the non-MRT implementation, and the MRT implementation as well
434 // (if supported by the device).
435 ASSERT_NO_FATAL_FAILURE(
436 DoYUVConversionTest(gpu_ops
, src_size
, src_size
,
437 checkerboard_size
, false));
438 if (gpu_ops
->device_supports_multiple_render_targets()) {
439 ASSERT_NO_FATAL_FAILURE(
440 DoYUVConversionTest(gpu_ops
, src_size
, src_size
,
441 checkerboard_size
, true));
445 void DoYUVConversionScaleTest(AcceleratedSurfaceTransformer
* gpu_ops
,
446 const gfx::Size
& src_size
,
447 const gfx::Size
& dst_size
) {
448 // Test the non-MRT implementation, and the MRT implementation as well
449 // (if supported by the device).
450 if (gpu_ops
->device_supports_multiple_render_targets()) {
451 ASSERT_NO_FATAL_FAILURE(
452 DoYUVConversionTest(gpu_ops
, src_size
, dst_size
, 4, true));
454 ASSERT_NO_FATAL_FAILURE(
455 DoYUVConversionTest(gpu_ops
, src_size
, dst_size
, 4, false));
458 void DoYUVConversionTest(AcceleratedSurfaceTransformer
* gpu_ops
,
459 const gfx::Size
& src_size
,
460 const gfx::Size
& dst_size
,
461 int checkerboard_size
,
462 boolean use_multi_render_targets
) {
465 "YUV Converting %dx%d at checkerboard size of %d; MRT %s",
466 src_size
.width(), src_size
.height(),
468 use_multi_render_targets
? "enabled" : "disabled"));
471 base::win::ScopedComPtr
<IDirect3DTexture9
> src
;
472 base::win::ScopedComPtr
<IDirect3DSurface9
> reference
;
473 base::win::ScopedComPtr
<IDirect3DSurface9
> dst_y
, dst_u
, dst_v
;
475 // TODO(ncarter): Use a better error metric that measures aggregate error
476 // rather than simply max error. There seems to be slightly more error at
477 // higher resolutions, maybe due to precision issues during rasterization
478 // (or maybe more pixels = more test trials). Results are usually to an
479 // error of 1, but we must use a tolerance of 3.
480 set_color_error_tolerance(3);
481 CreateRandomCheckerboardTexture(src_size
, checkerboard_size
, &reference
,
484 gfx::Size packed_y_size
, packed_uv_size
;
486 ASSERT_TRUE(gpu_ops
->AllocYUVBuffers(dst_size
,
493 // Actually do the conversion.
494 if (use_multi_render_targets
) {
495 ASSERT_TRUE(gpu_ops
->TransformRGBToYV12_MRT(src
,
503 ASSERT_TRUE(gpu_ops
->TransformRGBToYV12_WithoutMRT(src
,
512 // UV size (in bytes/samples) is half, rounded up.
513 gfx::Size
uv_size((dst_size
.width() + 1) / 2,
514 (dst_size
.height() + 1) / 2);
516 // Generate a reference bitmap by calling a software implementation.
517 SkBitmap reference_rgb
= ToSkBitmap(reference
, false);
518 SkBitmap reference_rgb_scaled
;
519 if (dst_size
== src_size
) {
520 reference_rgb_scaled
= reference_rgb
;
522 // We'll call Copy to do the bilinear scaling if needed.
523 base::win::ScopedComPtr
<IDirect3DSurface9
> reference_scaled
;
525 d3d_utils::CreateOrReuseLockableSurface(
526 device(), dst_size
, &reference_scaled
));
527 ASSERT_TRUE(gpu_ops
->Copy(src
, reference_scaled
, dst_size
));
528 BeforeLockWorkaround();
529 reference_rgb_scaled
= ToSkBitmap(reference_scaled
, false);
532 scoped_ptr
<uint8
[]> reference_y(new uint8
[dst_size
.GetArea()]);
533 scoped_ptr
<uint8
[]> reference_u(new uint8
[uv_size
.GetArea()]);
534 scoped_ptr
<uint8
[]> reference_v(new uint8
[uv_size
.GetArea()]);
535 reference_rgb_scaled
.lockPixels();
536 media::ConvertRGB32ToYUV_SSE2_Reference(
537 reinterpret_cast<uint8
*>(reference_rgb_scaled
.getAddr32(0, 0)),
543 reference_rgb_scaled
.rowBytes(),
546 reference_rgb_scaled
.unlockPixels();
548 // Check for equality of the reference and the actual.
549 AssertSame(dst_size
.width(), dst_size
.height(), &reference_y
[0], dst_y
);
550 AssertSame(uv_size
.width(), uv_size
.height(), &reference_u
[0], dst_u
);
551 AssertSame(uv_size
.width(), uv_size
.height(), &reference_v
[0], dst_v
);
553 if (kDumpImagesOnFailure
&& HasFatalFailure()) {
554 // Note that this will dump the full u and v buffers, including
555 // extra columns added due to packing. That means up to 7 extra
556 // columns for uv, and up to 3 extra columns for y.
557 WritePNGFile(reference_rgb
,
558 base::FilePath(FILE_PATH_LITERAL("test_fail_src.png")));
559 WritePNGFile(reference_rgb_scaled
,
561 FILE_PATH_LITERAL("test_fail_src_scaled.png")));
562 WritePNGFile(ToSkBitmap(dst_y
, true),
563 base::FilePath(FILE_PATH_LITERAL("test_fail_y.png")));
564 WritePNGFile(ToSkBitmap(dst_u
, true),
565 base::FilePath(FILE_PATH_LITERAL("test_fail_u.png")));
566 WritePNGFile(ToSkBitmap(dst_v
, true),
567 base::FilePath(FILE_PATH_LITERAL("test_fail_v.png")));
571 int color_error_tolerance_
;
572 uniform_int_distribution
<DWORD
> random_dword_
;
574 base::ScopedNativeLibrary d3d_module_
;
575 base::win::ScopedComPtr
<IDirect3DDevice9Ex
> device_
;
578 // Fails on some bots because Direct3D isn't allowed.
579 TEST_P(AcceleratedSurfaceTransformerTest
, Init
) {
580 SCOPED_TRACE(GetAdapterInfo());
581 AcceleratedSurfaceTransformer gpu_ops
;
582 ASSERT_TRUE(gpu_ops
.Init(device()));
584 WarnOnMissingFeatures(&gpu_ops
);
587 // Fails on some bots because Direct3D isn't allowed.
588 TEST_P(AcceleratedSurfaceTransformerTest
, TestConsistentRandom
) {
589 // This behavior should be the same for every execution on every machine.
590 // Otherwise tests might be flaky and impossible to debug.
591 SeedRandom("AcceleratedSurfaceTransformerTest.TestConsistentRandom");
592 ASSERT_EQ(2922058934, RandomColor());
594 SeedRandom("AcceleratedSurfaceTransformerTest.TestConsistentRandom");
595 ASSERT_EQ(2922058934, RandomColor());
596 ASSERT_EQ(4050239976, RandomColor());
598 SeedRandom("DifferentSeed");
599 ASSERT_EQ(3904108833, RandomColor());
602 // Fails on some bots because Direct3D isn't allowed.
603 TEST_P(AcceleratedSurfaceTransformerTest
, CopyInverted
) {
604 // This behavior should be the same for every execution on every machine.
605 // Otherwise tests might be flaky and impossible to debug.
606 SCOPED_TRACE(GetAdapterInfo());
607 SeedRandom("CopyInverted");
609 AcceleratedSurfaceTransformer t
;
610 ASSERT_TRUE(t
.Init(device()));
612 uniform_int_distribution
<int> size(1, 512);
614 for (int i
= 0; i
< 100; ++i
) {
615 ASSERT_NO_FATAL_FAILURE(
616 DoCopyInvertedTest(&t
, gfx::Size(size(rng_
), size(rng_
))))
617 << "At iteration " << i
;
621 // Fails on some bots because Direct3D isn't allowed.
622 // Fails on other bots because of ResizeBilinear symmetry failures.
623 // Should pass, at least, on NVIDIA Quadro 600.
624 TEST_P(AcceleratedSurfaceTransformerTest
, MixedOperations
) {
625 SCOPED_TRACE(GetAdapterInfo());
626 SeedRandom("MixedOperations");
628 AcceleratedSurfaceTransformer t
;
629 ASSERT_TRUE(t
.Init(device()));
631 ASSERT_NO_FATAL_FAILURE(
632 DoResizeBilinearTest(&t
, gfx::Size(256, 256), gfx::Size(255, 255), 1));
633 ASSERT_NO_FATAL_FAILURE(
634 DoResizeBilinearTest(&t
, gfx::Size(256, 256), gfx::Size(255, 255), 2));
635 ASSERT_NO_FATAL_FAILURE(
636 DoCopyInvertedTest(&t
, gfx::Size(20, 107)));
637 ASSERT_NO_FATAL_FAILURE(
638 DoResizeBilinearTest(&t
, gfx::Size(256, 256), gfx::Size(255, 255), 5));
639 ASSERT_NO_FATAL_FAILURE(
640 DoResizeBilinearTest(&t
, gfx::Size(256, 256), gfx::Size(64, 64), 5));
641 ASSERT_NO_FATAL_FAILURE(
642 DoYUVConversionTest(&t
, gfx::Size(128, 128), 1));
643 ASSERT_NO_FATAL_FAILURE(
644 DoResizeBilinearTest(&t
, gfx::Size(255, 255), gfx::Size(3, 3), 1));
645 ASSERT_NO_FATAL_FAILURE(
646 DoCopyInvertedTest(&t
, gfx::Size(1412, 124)));
647 ASSERT_NO_FATAL_FAILURE(
648 DoYUVConversionTest(&t
, gfx::Size(100, 200), 1));
649 ASSERT_NO_FATAL_FAILURE(
650 DoResizeBilinearTest(&t
, gfx::Size(255, 255), gfx::Size(257, 257), 1));
651 ASSERT_NO_FATAL_FAILURE(
652 DoResizeBilinearTest(&t
, gfx::Size(255, 255), gfx::Size(257, 257), 2));
654 ASSERT_NO_FATAL_FAILURE(
655 DoCopyInvertedTest(&t
, gfx::Size(1512, 7)));
656 ASSERT_NO_FATAL_FAILURE(
657 DoResizeBilinearTest(&t
, gfx::Size(255, 255), gfx::Size(257, 257), 5));
658 ASSERT_NO_FATAL_FAILURE(
659 DoResizeBilinearTest(&t
, gfx::Size(150, 256), gfx::Size(126, 256), 8));
660 ASSERT_NO_FATAL_FAILURE(
661 DoCopyInvertedTest(&t
, gfx::Size(1521, 3)));
662 ASSERT_NO_FATAL_FAILURE(
663 DoYUVConversionTest(&t
, gfx::Size(140, 181), 1));
664 ASSERT_NO_FATAL_FAILURE(
665 DoResizeBilinearTest(&t
, gfx::Size(150, 256), gfx::Size(126, 256), 1));
666 ASSERT_NO_FATAL_FAILURE(
667 DoCopyInvertedTest(&t
, gfx::Size(33, 712)));
668 ASSERT_NO_FATAL_FAILURE(
669 DoResizeBilinearTest(&t
, gfx::Size(150, 256), gfx::Size(126, 8), 8));
670 ASSERT_NO_FATAL_FAILURE(
671 DoCopyInvertedTest(&t
, gfx::Size(33, 2)));
672 ASSERT_NO_FATAL_FAILURE(
673 DoResizeBilinearTest(&t
, gfx::Size(200, 256), gfx::Size(126, 8), 8));
676 // Tests ResizeBilinear with 16K wide/hight src and dst surfaces.
678 // Fails on some bots because Direct3D isn't allowed.
679 // Should pass, at least, on NVIDIA Quadro 600.
680 TEST_P(AcceleratedSurfaceTransformerTest
, LargeSurfaces
) {
681 SCOPED_TRACE(GetAdapterInfo());
682 SeedRandom("LargeSurfaces");
684 AcceleratedSurfaceTransformer gpu_ops
;
685 ASSERT_TRUE(gpu_ops
.Init(device()));
688 ASSERT_HRESULT_SUCCEEDED(
689 device()->GetDeviceCaps(&caps
));
691 SCOPED_TRACE(base::StringPrintf(
692 "max texture size: %dx%d, max texture aspect: %d",
693 caps
.MaxTextureWidth
, caps
.MaxTextureHeight
, caps
.MaxTextureAspectRatio
));
695 const int w
= caps
.MaxTextureWidth
;
696 const int h
= caps
.MaxTextureHeight
;
699 ASSERT_NO_FATAL_FAILURE(
700 DoResizeBilinearTest(&gpu_ops
, gfx::Size(w
, lo
), gfx::Size(lo
, lo
), 1));
701 ASSERT_NO_FATAL_FAILURE(
702 DoResizeBilinearTest(&gpu_ops
, gfx::Size(lo
, h
), gfx::Size(lo
, lo
), 1));
703 ASSERT_NO_FATAL_FAILURE(
704 DoResizeBilinearTest(&gpu_ops
, gfx::Size(lo
, lo
), gfx::Size(w
, lo
), lo
));
705 ASSERT_NO_FATAL_FAILURE(
706 DoResizeBilinearTest(&gpu_ops
, gfx::Size(lo
, lo
), gfx::Size(lo
, h
), lo
));
707 ASSERT_NO_FATAL_FAILURE(
708 DoCopyInvertedTest(&gpu_ops
, gfx::Size(w
, lo
)));
709 ASSERT_NO_FATAL_FAILURE(
710 DoCopyInvertedTest(&gpu_ops
, gfx::Size(lo
, h
)));
712 ASSERT_NO_FATAL_FAILURE(
713 DoYUVConversionTest(&gpu_ops
, gfx::Size(w
, lo
), 1));
714 ASSERT_NO_FATAL_FAILURE(
715 DoYUVConversionTest(&gpu_ops
, gfx::Size(lo
, h
), 1));
719 // Exercises ResizeBilinear with random minification cases where the
720 // aspect ratio does not change.
722 // Fails on some bots because Direct3D isn't allowed.
723 // Fails on other bots because of StretchRect symmetry failures.
724 // Should pass, at least, on NVIDIA Quadro 600.
725 TEST_P(AcceleratedSurfaceTransformerTest
, MinifyUniform
) {
726 SCOPED_TRACE(GetAdapterInfo());
727 SeedRandom("MinifyUniform");
729 AcceleratedSurfaceTransformer gpu_ops
;
730 ASSERT_TRUE(gpu_ops
.Init(device()));
732 const int dims
[] = {21, 63, 64, 65, 99, 127, 128, 129, 192, 255, 256, 257};
733 const int checkerboards
[] = {1, 2, 3, 9};
734 uniform_int_distribution
<int> dim(0, arraysize(dims
) - 1);
735 uniform_int_distribution
<int> checkerboard(0, arraysize(checkerboards
) - 1);
737 for (int i
= 0; i
< 300; i
++) {
738 // Widths are picked so that dst is smaller than src.
739 int dst_width
= dims
[dim(rng_
)];
740 int src_width
= dims
[dim(rng_
)];
741 if (src_width
< dst_width
)
742 std::swap(dst_width
, src_width
);
744 // src_height is picked to preserve aspect ratio.
745 int dst_height
= dims
[dim(rng_
)];
746 int src_height
= static_cast<int>(
747 static_cast<int64
>(src_width
) * dst_height
/ dst_width
);
749 int checkerboard_size
= checkerboards
[checkerboard(rng_
)];
751 ASSERT_NO_FATAL_FAILURE(
752 DoResizeBilinearTest(&gpu_ops
,
753 gfx::Size(src_width
, src_height
), // Src size (larger)
754 gfx::Size(dst_width
, dst_height
), // Dst size (smaller)
755 checkerboard_size
)) << "Failed on iteration " << i
;
759 // Exercises ResizeBilinear with random magnification cases where the
760 // aspect ratio does not change.
762 // This test relies on an assertion that resizing preserves symmetry in the
763 // image, but for the current implementation of ResizeBilinear, this does not
764 // seem to be true (fails on NVIDIA Quadro 600; passes on
765 // Intel Mobile 965 Express)
766 TEST_P(AcceleratedSurfaceTransformerTest
, DISABLED_MagnifyUniform
) {
767 SCOPED_TRACE(GetAdapterInfo());
768 SeedRandom("MagnifyUniform");
770 AcceleratedSurfaceTransformer gpu_ops
;
771 ASSERT_TRUE(gpu_ops
.Init(device()));
773 const int dims
[] = {63, 64, 65, 99, 127, 128, 129, 192, 255, 256, 257};
774 const int checkerboards
[] = {1, 2, 3, 9};
775 uniform_int_distribution
<int> dim(0, arraysize(dims
) - 1);
776 uniform_int_distribution
<int> checkerboard(0, arraysize(checkerboards
) - 1);
778 for (int i
= 0; i
< 50; i
++) {
779 // Widths are picked so that src is smaller than dst.
780 int dst_width
= dims
[dim(rng_
)];
781 int src_width
= dims
[dim(rng_
)];
782 if (dst_width
< src_width
)
783 std::swap(src_width
, dst_width
);
785 int dst_height
= dims
[dim(rng_
)];
786 int src_height
= static_cast<int>(
787 static_cast<int64
>(src_width
) * dst_height
/ dst_width
);
789 int checkerboard_size
= checkerboards
[checkerboard(rng_
)];
791 ASSERT_NO_FATAL_FAILURE(
792 DoResizeBilinearTest(&gpu_ops
,
793 gfx::Size(src_width
, src_height
), // Src size (smaller)
794 gfx::Size(dst_width
, dst_height
), // Dst size (larger)
795 checkerboard_size
)) << "Failed on iteration " << i
;
799 TEST_P(AcceleratedSurfaceTransformerTest
, RGBtoYUV
) {
800 SeedRandom("RGBtoYUV");
802 AcceleratedSurfaceTransformer gpu_ops
;
803 ASSERT_TRUE(gpu_ops
.Init(device()));
805 // Start with some easy-to-debug cases. A checkerboard size of 1 is the
806 // best test, but larger checkerboard sizes give more insight into where
808 ASSERT_NO_FATAL_FAILURE(
809 DoYUVConversionTest(&gpu_ops
, gfx::Size(32, 32), 4));
810 ASSERT_NO_FATAL_FAILURE(
811 DoYUVConversionTest(&gpu_ops
, gfx::Size(32, 32), 2));
812 ASSERT_NO_FATAL_FAILURE(
813 DoYUVConversionTest(&gpu_ops
, gfx::Size(32, 32), 3));
815 // All cases of width (mod 8) and height (mod 8), using 1x1 checkerboard.
816 for (int w
= 32; w
< 40; ++w
) {
817 for (int h
= 32; h
< 40; ++h
) {
818 ASSERT_NO_FATAL_FAILURE(
819 DoYUVConversionTest(&gpu_ops
, gfx::Size(w
, h
), 1));
823 // All the very small sizes which require the most shifting in the
824 // texture coordinates when doing alignment.
825 for (int w
= 1; w
<= 9; ++w
) {
826 for (int h
= 1; h
<= 9; ++h
) {
827 ASSERT_NO_FATAL_FAILURE(
828 DoYUVConversionTest(&gpu_ops
, gfx::Size(w
, h
), 1));
832 // Random medium dimensions.
833 ASSERT_NO_FATAL_FAILURE(
834 DoYUVConversionTest(&gpu_ops
, gfx::Size(10, 142), 1));
835 ASSERT_NO_FATAL_FAILURE(
836 DoYUVConversionTest(&gpu_ops
, gfx::Size(124, 333), 1));
837 ASSERT_NO_FATAL_FAILURE(
838 DoYUVConversionTest(&gpu_ops
, gfx::Size(853, 225), 1));
839 ASSERT_NO_FATAL_FAILURE(
840 DoYUVConversionTest(&gpu_ops
, gfx::Size(231, 412), 1));
841 ASSERT_NO_FATAL_FAILURE(
842 DoYUVConversionTest(&gpu_ops
, gfx::Size(512, 128), 1));
843 ASSERT_NO_FATAL_FAILURE(
844 DoYUVConversionTest(&gpu_ops
, gfx::Size(1024, 768), 1));
846 // Common video/monitor resolutions
847 ASSERT_NO_FATAL_FAILURE(
848 DoYUVConversionTest(&gpu_ops
, gfx::Size(800, 768), 1));
849 ASSERT_NO_FATAL_FAILURE(
850 DoYUVConversionTest(&gpu_ops
, gfx::Size(1024, 768), 1));
851 ASSERT_NO_FATAL_FAILURE(
852 DoYUVConversionTest(&gpu_ops
, gfx::Size(1280, 720), 1));
853 ASSERT_NO_FATAL_FAILURE(
854 DoYUVConversionTest(&gpu_ops
, gfx::Size(1280, 720), 2));
855 ASSERT_NO_FATAL_FAILURE(
856 DoYUVConversionTest(&gpu_ops
, gfx::Size(1920, 1080), 1));
857 ASSERT_NO_FATAL_FAILURE(
858 DoYUVConversionTest(&gpu_ops
, gfx::Size(1920, 1080), 2));
859 ASSERT_NO_FATAL_FAILURE(
860 DoYUVConversionTest(&gpu_ops
, gfx::Size(2048, 1536), 1));
863 TEST_P(AcceleratedSurfaceTransformerTest
, RGBtoYUVScaled
) {
864 SeedRandom("RGBtoYUVScaled");
866 AcceleratedSurfaceTransformer gpu_ops
;
867 ASSERT_TRUE(gpu_ops
.Init(device()));
869 ASSERT_NO_FATAL_FAILURE(
870 DoYUVConversionScaleTest(&gpu_ops
, gfx::Size(32, 32), gfx::Size(64, 64)));
872 ASSERT_NO_FATAL_FAILURE(
873 DoYUVConversionScaleTest(&gpu_ops
, gfx::Size(32, 32), gfx::Size(16, 16)));
874 ASSERT_NO_FATAL_FAILURE(
875 DoYUVConversionScaleTest(&gpu_ops
, gfx::Size(32, 32), gfx::Size(24, 24)));
876 ASSERT_NO_FATAL_FAILURE(
877 DoYUVConversionScaleTest(&gpu_ops
, gfx::Size(32, 32), gfx::Size(48, 48)));
882 // Used to suppress test on Windows versions prior to Vista.
883 std::vector
<int> WindowsVersionIfVistaOrBetter() {
884 std::vector
<int> result
;
885 if (base::win::GetVersion() >= base::win::VERSION_VISTA
) {
886 result
.push_back(base::win::GetVersion());
893 INSTANTIATE_TEST_CASE_P(VistaAndUp
,
894 AcceleratedSurfaceTransformerTest
,
895 ::testing::ValuesIn(WindowsVersionIfVistaOrBetter()));