Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(430)

Side by Side Diff: ui/surface/accelerated_surface_transformer_win_unittest.cc

Issue 11464017: Separate image processing logic from presentation logic. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Made new deps Windows-only (yuck) Created 8 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <d3d9.h>
6 #include <random>
7
8 #include "base/basictypes.h"
9 #include "base/hash.h"
10 #include "base/scoped_native_library.h"
11 #include "base/stringprintf.h"
12 #include "base/win/scoped_comptr.h"
13 #include "base/win/windows_version.h"
14 #include "testing/gtest/include/gtest/gtest-param-test.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16 #include "ui/gfx/rect.h"
17 #include "ui/surface/accelerated_surface_transformer_win.h"
18 #include "ui/surface/accelerated_surface_win.h"
19 #include "ui/surface/d3d9_utils_win.h"
20
21 namespace d3d_utils = ui_surface_d3d9_utils;
22
23 using base::win::ScopedComPtr;
24 using std::uniform_int_distribution;
25
26 // Provides a reference rasterizer (all rendering done by software emulation)
27 // Direct3D device, for use by unit tests.
28 //
29 // This class is parameterized so that it runs only on Vista+. See
30 // WindowsVersionIfVistaOrBetter() for details on this works.
31 class AcceleratedSurfaceTransformerTest : public testing::TestWithParam<int> {
32 public:
33 AcceleratedSurfaceTransformerTest() {};
34
35 IDirect3DDevice9Ex* device() { return device_.get(); }
36
37 virtual void SetUp() {
38 if (!d3d_module_.is_valid()) {
39 if (!d3d_utils::LoadD3D9(&d3d_module_)) {
40 GTEST_FAIL();
41 return;
42 }
43 }
44 if (!d3d_utils::CreateDevice(d3d_module_,
45 D3DDEVTYPE_HAL,
46 D3DPRESENT_INTERVAL_IMMEDIATE,
47 device_.Receive())) {
48 GTEST_FAIL();
49 return;
50 }
51 const ::testing::TestInfo* const test_info =
52 ::testing::UnitTest::GetInstance()->current_test_info();
53 SeedRandom("default");
54 }
55
56 virtual void TearDown() {
57 device_ = NULL;
58 }
59
60 void SeedRandom(const char* seed) {
61 rng_.seed(base::Hash(seed));
62 random_dword_.reset();
63 }
64
65 // Locks and fills a surface with a checkerboard pattern where the colors
66 // are random but the total image pattern is horizontally and vertically
67 // symmetric.
68 template<typename ColorType>
69 void FillSymmetricRandomCheckerboard(
70 IDirect3DSurface9* lockable_surface,
71 const gfx::Size& size,
72 int checker_square_size) {
73
74 D3DLOCKED_RECT locked_rect;
75 ASSERT_HRESULT_SUCCEEDED(
76 lockable_surface->LockRect(&locked_rect, NULL, D3DLOCK_DISCARD));
77 ColorType* surface = reinterpret_cast<ColorType*>(locked_rect.pBits);
78 ASSERT_EQ(0, locked_rect.Pitch % sizeof(ColorType));
79 int pitch = locked_rect.Pitch / sizeof(ColorType);
80
81 for (int y = 0; y <= size.height() / 2; y += checker_square_size) {
82 for (int x = 0; x <= size.width() / 2; x += checker_square_size) {
83 ColorType color = static_cast<ColorType>(RandomColor());
84 int y_limit = std::min(size.height() / 2, y + checker_square_size - 1);
85 int x_limit = std::min(size.width() / 2, x + checker_square_size - 1);
86 for (int y_lo = y; y_lo <= y_limit; y_lo++) {
87 for (int x_lo = x; x_lo <= x_limit; x_lo++) {
88 int y_hi = size.height() - 1 - y_lo;
89 int x_hi = size.width() - 1 - x_lo;
90 surface[x_lo + y_lo*pitch] = color;
91 surface[x_lo + y_hi*pitch] = color;
92 surface[x_hi + y_lo*pitch] = color;
93 surface[x_hi + y_hi*pitch] = color;
94 }
95 }
96 }
97 }
98
99 lockable_surface->UnlockRect();
100 }
101
102 template<typename ColorType>
103 void FillRandomCheckerboard(
104 IDirect3DSurface9* lockable_surface,
105 const gfx::Size& size,
106 int checker_square_size) {
107
108 D3DLOCKED_RECT locked_rect;
109 ASSERT_HRESULT_SUCCEEDED(
110 lockable_surface->LockRect(&locked_rect, NULL, D3DLOCK_DISCARD));
111 ColorType* surface = reinterpret_cast<ColorType*>(locked_rect.pBits);
112 ASSERT_EQ(0, locked_rect.Pitch % sizeof(ColorType));
113 int pitch = locked_rect.Pitch / sizeof(ColorType);
114
115 for (int y = 0; y <= size.height(); y += checker_square_size) {
116 for (int x = 0; x <= size.width(); x += checker_square_size) {
117 ColorType color = static_cast<ColorType>(RandomColor());
118 int y_limit = std::min(size.height(), y + checker_square_size);
119 int x_limit = std::min(size.width(), x + checker_square_size);
120 for (int square_y = y; square_y < y_limit; square_y++) {
121 for (int square_x = x; square_x < x_limit; square_x++) {
122 surface[square_x + square_y*pitch] = color;
123 }
124 }
125 }
126 }
127
128 lockable_surface->UnlockRect();
129 }
130
131 // Approximate color-equality check. Allows for some rounding error.
132 bool AssertSameColor(DWORD color_a, DWORD color_b) {
133 if (color_a == color_b)
134 return true;
135 uint8* a = reinterpret_cast<uint8*>(&color_a);
136 uint8* b = reinterpret_cast<uint8*>(&color_b);
137 int max_error = 0;
138 for (int i = 0; i < 4; i++)
139 max_error = std::max(max_error,
140 std::abs(static_cast<int>(a[i]) - b[i]));
141
142 if (max_error <= kAbsoluteColorErrorTolerance)
143 return true;
144
145 EXPECT_EQ(StringPrintf("%d %d %d %d", a[0], a[1], a[2], a[3]),
146 StringPrintf("%d %d %d %d", b[0], b[1], b[2], b[3]));
147
148 return false;
149 }
150
151 // Asserts that an image is symmetric with respect to itself: both
152 // horizontally and vertically, within the tolerance of AssertSameColor.
153 template<typename ColorType>
154 void AssertSymmetry(IDirect3DSurface9* lockable_surface,
155 const gfx::Size& size) {
156 D3DLOCKED_RECT locked_rect;
157 ASSERT_HRESULT_SUCCEEDED(
158 lockable_surface->LockRect(&locked_rect, NULL, D3DLOCK_READONLY));
159 ASSERT_EQ(0, locked_rect.Pitch % sizeof(ColorType));
160 int pitch = locked_rect.Pitch / sizeof(ColorType);
161 ColorType* surface = reinterpret_cast<ColorType*>(locked_rect.pBits);
162 for (int y_lo = 0; y_lo < size.height() / 2; y_lo++) {
163 int y_hi = size.height() - 1 - y_lo;
164 for (int x_lo = 0; x_lo < size.width() / 2; x_lo++) {
165 int x_hi = size.width() - 1 - x_lo;
166 if (!AssertSameColor(surface[x_lo + y_lo*pitch],
167 surface[x_hi + y_lo*pitch])) {
168 FAIL() << StringPrintf("Pixels (%d, %d) vs. (%d, %d)",
169 x_lo, y_lo, x_hi, y_lo);
170 }
171 if (!AssertSameColor(surface[x_hi + y_lo*pitch],
172 surface[x_hi + y_hi*pitch])) {
173 FAIL() << StringPrintf("Pixels (%d, %d) vs. (%d, %d)",
174 x_hi, y_lo, x_hi, y_hi);
175 }
176 if (!AssertSameColor(surface[x_hi + y_hi*pitch],
177 surface[x_lo + y_hi*pitch])) {
178 FAIL() << StringPrintf("Pixels (%d, %d) vs. (%d, %d)",
179 x_hi, y_hi, x_lo, y_hi);
180 }
181 }
182 }
183 lockable_surface->UnlockRect();
184 }
185
186 // Asserts that the actual image is a bit-identical, vertically mirrored
187 // copy of the expected image.
188 template<typename ColorType>
189 void AssertIsInvertedCopy(const gfx::Size& size,
190 IDirect3DSurface9* expected,
191 IDirect3DSurface9* actual) {
192
193 D3DLOCKED_RECT locked_expected, locked_actual;
194 ASSERT_HRESULT_SUCCEEDED(
195 expected->LockRect(&locked_expected, NULL, D3DLOCK_READONLY));
196 ASSERT_HRESULT_SUCCEEDED(
197 actual->LockRect(&locked_actual, NULL, D3DLOCK_READONLY));
198 ASSERT_EQ(0, locked_expected.Pitch % sizeof(ColorType));
199 int pitch = locked_expected.Pitch / sizeof(ColorType);
200 ColorType* expected_image =
201 reinterpret_cast<ColorType*>(locked_expected.pBits);
202 ColorType* actual_image =
203 reinterpret_cast<ColorType*>(locked_actual.pBits);
204 for (int y = 0; y < size.height(); y++) {
205 int y_actual = size.height() - 1 - y;
206 ASSERT_EQ(0,
207 memcmp(&expected_image[y*pitch], &actual_image[y_actual*pitch],
208 sizeof(ColorType) * size.width()))
209 << StringPrintf("Rows not equal: %d vs. %d", y, y_actual);
210 }
211 expected->UnlockRect();
212 actual->UnlockRect();
213 }
214
215 protected:
216 static const int kAbsoluteColorErrorTolerance = 4;
217
218 DWORD RandomColor() {
219 return random_dword_(rng_);
220 }
221
222 void DoResizeBilinearTest(AcceleratedSurfaceTransformer* gpu_ops,
223 const gfx::Size& src_size,
224 const gfx::Size& dst_size,
225 int checkerboard_size) {
226
227 SCOPED_TRACE(
228 StringPrintf("Resizing %dx%d -> %dx%d at checkerboard size of %d",
229 src_size.width(), src_size.height(),
230 dst_size.width(), dst_size.height(),
231 checkerboard_size));
232
233 base::win::ScopedComPtr<IDirect3DSurface9> src, dst;
234 ASSERT_TRUE(d3d_utils::CreateTemporaryLockableSurface(
235 device(), src_size, src.Receive()));
236 ASSERT_TRUE(d3d_utils::CreateTemporaryLockableSurface(
237 device(), dst_size, dst.Receive()));
238
239 FillSymmetricRandomCheckerboard<DWORD>(src, src_size, checkerboard_size);
240
241 ASSERT_TRUE(gpu_ops->ResizeBilinear(src, gfx::Rect(src_size), dst));
242
243 AssertSymmetry<DWORD>(dst, dst_size);
244 }
245
246 void DoCopyInvertedTest(AcceleratedSurfaceTransformer* gpu_ops,
247 const gfx::Size& size) {
248
249 SCOPED_TRACE(
250 StringPrintf("CopyInverted @ %dx%d", size.width(), size.height()));
251
252 base::win::ScopedComPtr<IDirect3DSurface9> checkerboard, src, dst;
253 base::win::ScopedComPtr<IDirect3DTexture9> src_texture;
254 ASSERT_TRUE(d3d_utils::CreateTemporaryLockableSurface(device(), size,
255 checkerboard.Receive()));
256 ASSERT_TRUE(d3d_utils::CreateTemporaryRenderTargetTexture(device(), size,
257 src_texture.Receive(), src.Receive()));
258 ASSERT_TRUE(d3d_utils::CreateTemporaryLockableSurface(device(), size,
259 dst.Receive()));
260
261 FillRandomCheckerboard<DWORD>(checkerboard, size, 1);
262 ASSERT_HRESULT_SUCCEEDED(
263 device()->StretchRect(checkerboard, NULL, src, NULL, D3DTEXF_NONE));
264 ASSERT_TRUE(gpu_ops->CopyInverted(src_texture, dst, size));
265 AssertIsInvertedCopy<DWORD>(size, checkerboard, dst);
266 }
267
268 uniform_int_distribution<DWORD> random_dword_;
269 std::mt19937 rng_;
270 base::ScopedNativeLibrary d3d_module_;
271 base::win::ScopedComPtr<IDirect3DDevice9Ex> device_;
272 };
273
274 TEST_P(AcceleratedSurfaceTransformerTest, Init) {
275 AcceleratedSurfaceTransformer gpu_ops;
276 ASSERT_TRUE(gpu_ops.Init(device()));
277 };
278
279 TEST_P(AcceleratedSurfaceTransformerTest, TestConsistentRandom) {
280 // This behavior should be the same for every execution on every machine.
281 // Otherwise tests might be flaky and impossible to debug.
282 SeedRandom("AcceleratedSurfaceTransformerTest.TestConsistentRandom");
283 ASSERT_EQ(2922058934, RandomColor());
284
285 SeedRandom("AcceleratedSurfaceTransformerTest.TestConsistentRandom");
286 ASSERT_EQ(2922058934, RandomColor());
287 ASSERT_EQ(4050239976, RandomColor());
288
289 SeedRandom("DifferentSeed");
290 ASSERT_EQ(3904108833, RandomColor());
291 }
292
293 TEST_P(AcceleratedSurfaceTransformerTest, MixedOperations) {
294 SeedRandom("MixedOperations");
295
296 AcceleratedSurfaceTransformer t;
297 ASSERT_TRUE(t.Init(device()));
298
299 DoResizeBilinearTest(&t, gfx::Size(256, 256), gfx::Size(255, 255), 1);
300 DoResizeBilinearTest(&t, gfx::Size(256, 256), gfx::Size(255, 255), 2);
301 DoCopyInvertedTest(&t, gfx::Size(20, 107));
302 DoResizeBilinearTest(&t, gfx::Size(256, 256), gfx::Size(255, 255), 5);
303 DoResizeBilinearTest(&t, gfx::Size(256, 256), gfx::Size(64, 64), 5);
304 DoResizeBilinearTest(&t, gfx::Size(255, 255), gfx::Size(3, 3), 1);
305 DoCopyInvertedTest(&t, gfx::Size(1412, 124));
306 DoResizeBilinearTest(&t, gfx::Size(255, 255), gfx::Size(257, 257), 1);
307 DoResizeBilinearTest(&t, gfx::Size(255, 255), gfx::Size(257, 257), 2);
308
309 DoCopyInvertedTest(&t, gfx::Size(1512, 7));
310 DoResizeBilinearTest(&t, gfx::Size(255, 255), gfx::Size(257, 257), 5);
311 DoResizeBilinearTest(&t, gfx::Size(150, 256), gfx::Size(126, 256), 8);
312 DoCopyInvertedTest(&t, gfx::Size(1521, 3));
313 DoResizeBilinearTest(&t, gfx::Size(150, 256), gfx::Size(126, 256), 1);
314 DoCopyInvertedTest(&t, gfx::Size(33, 712));
315 DoResizeBilinearTest(&t, gfx::Size(150, 256), gfx::Size(126, 8), 8);
316 DoCopyInvertedTest(&t, gfx::Size(33, 2));
317 DoResizeBilinearTest(&t, gfx::Size(200, 256), gfx::Size(126, 8), 8);
318 }
319
320 // Tests ResizeBilinear with 16K wide/hight src and dst surfaces.
321 TEST_P(AcceleratedSurfaceTransformerTest, LargeSurfaces) {
322 SeedRandom("LargeSurfaces");
323
324 AcceleratedSurfaceTransformer gpu_ops;
325 ASSERT_TRUE(gpu_ops.Init(device()));
326
327 const int lo = 256;
328 const int hi = 16384;
329
330 DoResizeBilinearTest(&gpu_ops, gfx::Size(hi, lo), gfx::Size(lo, lo), 1);
331 DoResizeBilinearTest(&gpu_ops, gfx::Size(lo, hi), gfx::Size(lo, lo), 1);
332 DoResizeBilinearTest(&gpu_ops, gfx::Size(lo, lo), gfx::Size(hi, lo), lo);
333 DoResizeBilinearTest(&gpu_ops, gfx::Size(lo, lo), gfx::Size(lo, hi), lo);
334 DoCopyInvertedTest(&gpu_ops, gfx::Size(hi, lo));
335 DoCopyInvertedTest(&gpu_ops, gfx::Size(lo, hi));
336 }
337
338 // Exercises ResizeBilinear with random minification cases where the
339 // aspect ratio does not change.
340 TEST_P(AcceleratedSurfaceTransformerTest, MinifyUniform) {
341 SeedRandom("MinifyUniform");
342
343 AcceleratedSurfaceTransformer gpu_ops;
344 ASSERT_TRUE(gpu_ops.Init(device()));
345
346 int dims[] = { 21, 63, 64, 65, 99, 127, 128, 129, 192, 255, 256, 257};
347 int checkerboards[] = {1, 2, 3, 9};
348 uniform_int_distribution<int> dim(0, arraysize(dims) - 1);
349 uniform_int_distribution<int> checkerboard(0, arraysize(checkerboards) - 1);
350
351 for (int i = 0; i < 300; i++) {
352 // Widths are picked so that dst is smaller than src.
353 int dst_width = dims[dim(rng_)];
354 int src_width = dims[dim(rng_)];
355 if (src_width < dst_width)
356 std::swap(dst_width, src_width);
357
358 // src_width is picked to preserve aspect ratio.
359 int dst_height = dims[dim(rng_)];
360 int src_height = static_cast<int>(
361 static_cast<int64>(src_width) * dst_height / dst_width);
362
363 int checkerboard_size = checkerboards[checkerboard(rng_)];
364
365 DoResizeBilinearTest(&gpu_ops,
366 gfx::Size(src_width, src_height), // Src size (larger)
367 gfx::Size(dst_width, dst_height), // Dst size (smaller)
368 checkerboard_size);
369 }
370 };
371
372 // Exercises ResizeBilinear with random magnification cases where the
373 // aspect ratio does not change.
374 //
375 // Disabled. This test relies on an assertion that resizing preserves
376 // symmetry in the image, but for the current implementation of ResizeBilinear,
377 // this does not seem to be true.
378 TEST_P(AcceleratedSurfaceTransformerTest, DISABLED_MagnifyUniform) {
379 SeedRandom("MagnifyUniform");
380
381 AcceleratedSurfaceTransformer gpu_ops;
382 ASSERT_TRUE(gpu_ops.Init(device()));
383
384 int dims[] = {63, 64, 65, 99, 127, 128, 129, 192, 255, 256, 257};
385 int checkerboards[] = {1, 2, 3, 9};
386 uniform_int_distribution<int> dim(0, arraysize(dims) - 1);
387 uniform_int_distribution<int> checkerboard(0, arraysize(checkerboards) - 1);
388
389 for (int i = 0; i < 50; i++) {
390 // Widths are picked so that b is smaller than a.
391 int dst_width = dims[dim(rng_)];
392 int src_width = dims[dim(rng_)];
393 if (dst_width < src_width)
394 std::swap(src_width, dst_width);
395
396 int dst_height = dims[dim(rng_)];
397 int src_height = static_cast<int>(
398 static_cast<int64>(src_width) * dst_height / dst_width);
399
400 int checkerboard_size = checkerboards[checkerboard(rng_)];
401
402 DoResizeBilinearTest(&gpu_ops,
403 gfx::Size(src_width, src_height), // Src size (smaller)
404 gfx::Size(dst_width, dst_height), // Dst size (larger)
405 checkerboard_size);
406 }
407 };
408
409 namespace {
410
411 // Used to suppress test on Windows versions prior to Vista.
412 std::vector<int> WindowsVersionIfVistaOrBetter() {
413 std::vector<int> result;
414 if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
415 result.push_back(base::win::GetVersion());
416 }
417 return result;
418 }
419
420 } // namespace
421
422 INSTANTIATE_TEST_CASE_P(VistaAndUp,
423 AcceleratedSurfaceTransformerTest,
424 ::testing::ValuesIn(WindowsVersionIfVistaOrBetter()));
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698