Home | History | Annotate | Download | only in surface
      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.
      4 
      5 #include <d3d9.h>
      6 #include <random>
      7 
      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"
     28 
     29 namespace d3d_utils = ui_surface_d3d9_utils;
     30 
     31 using base::win::ScopedComPtr;
     32 using std::uniform_int_distribution;
     33 
     34 namespace {
     35 
     36 // Debug flag, useful when hacking on tests.
     37 const bool kDumpImagesOnFailure = false;
     38 
     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));
     43 
     44   SkBitmap result;
     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);
     50   result.allocPixels();
     51   result.lockPixels();
     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]);
     58       }
     59     } else {
     60       uint32* row32 = reinterpret_cast<uint32*>(row8);
     61       for (int x = 0; x < size.width(); ++x) {
     62         *result.getAddr32(x, y) = row32[x] | 0xFF000000;
     63       }
     64     }
     65   }
     66   result.unlockPixels();
     67   result.setImmutable();
     68   surface->UnlockRect();
     69   return result;
     70 }
     71 
     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,
     76                                         discard_transparency,
     77                                         &png_data) &&
     78       base::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;
     82   }
     83   return false;
     84 }
     85 
     86 }  // namespace
     87 
     88 // Test fixture for AcceleratedSurfaceTransformer.
     89 //
     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> {
     93  public:
     94   AcceleratedSurfaceTransformerTest() : color_error_tolerance_(0) {};
     95 
     96   IDirect3DDevice9Ex* device() { return device_.get(); }
     97 
     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";
    102         return;
    103       }
    104     }
    105     if (!d3d_utils::CreateDevice(d3d_module_,
    106                                  D3DDEVTYPE_HAL,
    107                                  D3DPRESENT_INTERVAL_IMMEDIATE,
    108                                  device_.Receive())) {
    109       GTEST_FAIL() << "Could not create Direct3D device.";
    110       return;
    111     }
    112 
    113     SeedRandom("default");
    114   }
    115 
    116   virtual void TearDown() {
    117     device_ = NULL;
    118   }
    119 
    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);
    129   }
    130 
    131   void SeedRandom(const char* seed) {
    132     rng_.seed(base::Hash(seed));
    133     random_dword_.reset();
    134   }
    135 
    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));
    142   }
    143 
    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. "
    149                    << GetAdapterInfo();
    150     }
    151   }
    152 
    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
    155   // symmetric.
    156   void FillSymmetricRandomCheckerboard(
    157       IDirect3DSurface9* lockable_surface,
    158       const gfx::Size& size,
    159       int checker_square_size) {
    160 
    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);
    167 
    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;
    181           }
    182         }
    183       }
    184     }
    185 
    186     lockable_surface->UnlockRect();
    187   }
    188 
    189   void FillRandomCheckerboard(
    190       IDirect3DSurface9* lockable_surface,
    191       const gfx::Size& size,
    192       int checker_square_size) {
    193 
    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);
    200 
    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;
    209           }
    210         }
    211       }
    212     }
    213 
    214     lockable_surface->UnlockRect();
    215   }
    216 
    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)
    220       return true;
    221     uint8* a = reinterpret_cast<uint8*>(&color_a);
    222     uint8* b = reinterpret_cast<uint8*>(&color_b);
    223     int max_error = 0;
    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]));
    227 
    228     if (max_error <= color_error_tolerance())
    229       return true;
    230 
    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();
    238 
    239     return false;
    240   }
    241 
    242 bool AssertSameColor(uint8 color_a, uint8 color_b) {
    243     if (color_a == color_b)
    244       return true;
    245     int max_error = std::abs((int) color_a - (int) color_b);
    246     if (max_error <= color_error_tolerance())
    247       return true;
    248     ADD_FAILURE() << "Colors not equal: "
    249                   << base::StringPrintf("0x%x", color_a)
    250                   << " vs. " << base::StringPrintf("0x%x", color_b);
    251     return false;
    252   }
    253 
    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();
    259 
    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 << ")";
    275         }
    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 << ")";
    281         }
    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 << ")";
    287         }
    288       }
    289     }
    290     lockable_surface->UnlockRect();
    291   }
    292 
    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();
    299 
    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 << ")";
    318         }
    319     }
    320     expected->UnlockRect();
    321     actual->UnlockRect();
    322   }
    323 
    324  protected:
    325   DWORD RandomColor() {
    326     return random_dword_(rng_);
    327   }
    328 
    329   void set_color_error_tolerance(int value) {
    330     color_error_tolerance_ = value;
    331   }
    332 
    333   int color_error_tolerance() {
    334     return color_error_tolerance_;
    335   }
    336 
    337   void DoResizeBilinearTest(AcceleratedSurfaceTransformer* gpu_ops,
    338                             const gfx::Size& src_size,
    339                             const gfx::Size& dst_size,
    340                             int checkerboard_size) {
    341 
    342     SCOPED_TRACE(
    343         base::StringPrintf(
    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(),
    347             checkerboard_size));
    348 
    349     set_color_error_tolerance(4);
    350 
    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";
    358 
    359     FillSymmetricRandomCheckerboard(src, src_size, checkerboard_size);
    360 
    361     ASSERT_TRUE(gpu_ops->ResizeBilinear(src, gfx::Rect(src_size), dst,
    362                                         gfx::Rect(dst_size)));
    363 
    364     AssertSymmetry(dst, dst_size);
    365   }
    366 
    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,
    374         reference_surface));
    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));
    381   }
    382 
    383   void AssertSame(int width_in_bytes, int height, uint8* reference,
    384                   IDirect3DSurface9* lockable) {
    385     BeforeLockWorkaround();
    386 
    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 << ")";
    397         }
    398       }
    399     }
    400     lockable->UnlockRect();
    401   }
    402 
    403   void DoCopyInvertedTest(AcceleratedSurfaceTransformer* gpu_ops,
    404                           const gfx::Size& size) {
    405 
    406     SCOPED_TRACE(base::StringPrintf(
    407         "CopyInverted @ %dx%d", size.width(), size.height()));
    408 
    409     set_color_error_tolerance(0);
    410 
    411     base::win::ScopedComPtr<IDirect3DSurface9> dst, reference_pattern;
    412     base::win::ScopedComPtr<IDirect3DTexture9> src;
    413 
    414     CreateRandomCheckerboardTexture(size, 1, &reference_pattern, &src);
    415 
    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);
    421 
    422     ASSERT_TRUE(d3d_utils::CreateOrReuseLockableSurface(device(), alloc_size,
    423         &dst)) << "Could not create dst render target.";
    424 
    425     ASSERT_TRUE(gpu_ops->CopyInverted(src, dst, size));
    426     AssertIsInvertedCopy(size, reference_pattern, dst);
    427   }
    428 
    429 
    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));
    442     }
    443   }
    444 
    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));
    453     }
    454     ASSERT_NO_FATAL_FAILURE(
    455         DoYUVConversionTest(gpu_ops, src_size, dst_size, 4, false));
    456   }
    457 
    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) {
    463     SCOPED_TRACE(
    464         base::StringPrintf(
    465             "YUV Converting %dx%d at checkerboard size of %d; MRT %s",
    466             src_size.width(), src_size.height(),
    467             checkerboard_size,
    468             use_multi_render_targets ? "enabled" : "disabled"));
    469 
    470 
    471     base::win::ScopedComPtr<IDirect3DTexture9> src;
    472     base::win::ScopedComPtr<IDirect3DSurface9> reference;
    473     base::win::ScopedComPtr<IDirect3DSurface9> dst_y, dst_u, dst_v;
    474 
    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,
    482                                     &src);
    483 
    484     gfx::Size packed_y_size, packed_uv_size;
    485 
    486     ASSERT_TRUE(gpu_ops->AllocYUVBuffers(dst_size,
    487                                          &packed_y_size,
    488                                          &packed_uv_size,
    489                                          dst_y.Receive(),
    490                                          dst_u.Receive(),
    491                                          dst_v.Receive()));
    492 
    493     // Actually do the conversion.
    494     if (use_multi_render_targets) {
    495       ASSERT_TRUE(gpu_ops->TransformRGBToYV12_MRT(src,
    496                                                   dst_size,
    497                                                   packed_y_size,
    498                                                   packed_uv_size,
    499                                                   dst_y,
    500                                                   dst_u,
    501                                                   dst_v));
    502     } else {
    503       ASSERT_TRUE(gpu_ops->TransformRGBToYV12_WithoutMRT(src,
    504                                                          dst_size,
    505                                                          packed_y_size,
    506                                                          packed_uv_size,
    507                                                          dst_y,
    508                                                          dst_u,
    509                                                          dst_v));
    510     }
    511 
    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);
    515 
    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;
    521     } else {
    522       // We'll call Copy to do the bilinear scaling if needed.
    523       base::win::ScopedComPtr<IDirect3DSurface9> reference_scaled;
    524       ASSERT_TRUE(
    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);
    530     }
    531 
    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)),
    538         &reference_y[0],
    539         &reference_u[0],
    540         &reference_v[0],
    541         dst_size.width(),
    542         dst_size.height(),
    543         reference_rgb_scaled.rowBytes(),
    544         dst_size.width(),
    545         uv_size.width());
    546     reference_rgb_scaled.unlockPixels();
    547 
    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);
    552 
    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,
    560                    base::FilePath(
    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")));
    568     }
    569   }
    570 
    571   int color_error_tolerance_;
    572   uniform_int_distribution<DWORD> random_dword_;
    573   std::mt19937 rng_;
    574   base::ScopedNativeLibrary d3d_module_;
    575   base::win::ScopedComPtr<IDirect3DDevice9Ex> device_;
    576 };
    577 
    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()));
    583 
    584   WarnOnMissingFeatures(&gpu_ops);
    585 };
    586 
    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());
    593 
    594   SeedRandom("AcceleratedSurfaceTransformerTest.TestConsistentRandom");
    595   ASSERT_EQ(2922058934, RandomColor());
    596   ASSERT_EQ(4050239976, RandomColor());
    597 
    598   SeedRandom("DifferentSeed");
    599   ASSERT_EQ(3904108833, RandomColor());
    600 }
    601 
    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");
    608 
    609   AcceleratedSurfaceTransformer t;
    610   ASSERT_TRUE(t.Init(device()));
    611 
    612   uniform_int_distribution<int> size(1, 512);
    613 
    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;
    618   }
    619 }
    620 
    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");
    627 
    628   AcceleratedSurfaceTransformer t;
    629   ASSERT_TRUE(t.Init(device()));
    630 
    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));
    653 
    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));
    674 }
    675 
    676 // Tests ResizeBilinear with 16K wide/hight src and dst surfaces.
    677 //
    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");
    683 
    684   AcceleratedSurfaceTransformer gpu_ops;
    685   ASSERT_TRUE(gpu_ops.Init(device()));
    686 
    687   D3DCAPS9 caps;
    688   ASSERT_HRESULT_SUCCEEDED(
    689       device()->GetDeviceCaps(&caps));
    690 
    691   SCOPED_TRACE(base::StringPrintf(
    692      "max texture size: %dx%d, max texture aspect: %d",
    693       caps.MaxTextureWidth, caps.MaxTextureHeight, caps.MaxTextureAspectRatio));
    694 
    695   const int w = caps.MaxTextureWidth;
    696   const int h = caps.MaxTextureHeight;
    697   const int lo = 256;
    698 
    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)));
    711 
    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));
    716 
    717 }
    718 
    719 // Exercises ResizeBilinear with random minification cases where the
    720 // aspect ratio does not change.
    721 //
    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");
    728 
    729   AcceleratedSurfaceTransformer gpu_ops;
    730   ASSERT_TRUE(gpu_ops.Init(device()));
    731 
    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);
    736 
    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);
    743 
    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);
    748 
    749     int checkerboard_size = checkerboards[checkerboard(rng_)];
    750 
    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;
    756   }
    757 };
    758 
    759 // Exercises ResizeBilinear with random magnification cases where the
    760 // aspect ratio does not change.
    761 //
    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");
    769 
    770   AcceleratedSurfaceTransformer gpu_ops;
    771   ASSERT_TRUE(gpu_ops.Init(device()));
    772 
    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);
    777 
    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);
    784 
    785     int dst_height = dims[dim(rng_)];
    786     int src_height = static_cast<int>(
    787         static_cast<int64>(src_width) * dst_height / dst_width);
    788 
    789     int checkerboard_size = checkerboards[checkerboard(rng_)];
    790 
    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;
    796   }
    797 };
    798 
    799 TEST_P(AcceleratedSurfaceTransformerTest, RGBtoYUV) {
    800   SeedRandom("RGBtoYUV");
    801 
    802   AcceleratedSurfaceTransformer gpu_ops;
    803   ASSERT_TRUE(gpu_ops.Init(device()));
    804 
    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
    807   // a bug might be.
    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));
    814 
    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));
    820     }
    821   }
    822 
    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));
    829     }
    830   }
    831 
    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));
    845 
    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));
    861 }
    862 
    863 TEST_P(AcceleratedSurfaceTransformerTest, RGBtoYUVScaled) {
    864   SeedRandom("RGBtoYUVScaled");
    865 
    866   AcceleratedSurfaceTransformer gpu_ops;
    867   ASSERT_TRUE(gpu_ops.Init(device()));
    868 
    869   ASSERT_NO_FATAL_FAILURE(
    870       DoYUVConversionScaleTest(&gpu_ops, gfx::Size(32, 32), gfx::Size(64, 64)));
    871 
    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)));
    878 }
    879 
    880 namespace {
    881 
    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());
    887   }
    888   return result;
    889 }
    890 
    891 }  // namespace
    892 
    893 INSTANTIATE_TEST_CASE_P(VistaAndUp,
    894                         AcceleratedSurfaceTransformerTest,
    895                         ::testing::ValuesIn(WindowsVersionIfVistaOrBetter()));
    896