Home | History | Annotate | Download | only in test
      1 // Copyright 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 "cc/test/layer_tree_pixel_test.h"
      6 
      7 #include "base/path_service.h"
      8 #include "cc/layers/solid_color_layer.h"
      9 #include "cc/layers/texture_layer.h"
     10 #include "cc/output/copy_output_request.h"
     11 #include "cc/output/copy_output_result.h"
     12 #include "cc/resources/texture_mailbox.h"
     13 #include "cc/test/paths.h"
     14 #include "cc/test/pixel_comparator.h"
     15 #include "cc/test/pixel_test_output_surface.h"
     16 #include "cc/test/pixel_test_software_output_device.h"
     17 #include "cc/test/pixel_test_utils.h"
     18 #include "cc/trees/layer_tree_impl.h"
     19 #include "ui/gl/gl_implementation.h"
     20 #include "webkit/common/gpu/context_provider_in_process.h"
     21 #include "webkit/common/gpu/webgraphicscontext3d_in_process_command_buffer_impl.h"
     22 
     23 namespace cc {
     24 
     25 LayerTreePixelTest::LayerTreePixelTest()
     26     : pixel_comparator_(new ExactPixelComparator(true)),
     27       test_type_(GL_WITH_DEFAULT),
     28       pending_texture_mailbox_callbacks_(0) {}
     29 
     30 LayerTreePixelTest::~LayerTreePixelTest() {}
     31 
     32 scoped_ptr<OutputSurface> LayerTreePixelTest::CreateOutputSurface(
     33     bool fallback) {
     34   gfx::Vector2d viewport_offset(20, 10);
     35   gfx::Size surface_expansion_size(40, 60);
     36   scoped_ptr<PixelTestOutputSurface> output_surface;
     37 
     38   switch (test_type_) {
     39     case SOFTWARE_WITH_DEFAULT:
     40     case SOFTWARE_WITH_BITMAP: {
     41       scoped_ptr<PixelTestSoftwareOutputDevice> software_output_device(
     42           new PixelTestSoftwareOutputDevice);
     43       software_output_device->set_surface_expansion_size(
     44           surface_expansion_size);
     45       output_surface = make_scoped_ptr(
     46           new PixelTestOutputSurface(
     47               software_output_device.PassAs<SoftwareOutputDevice>()));
     48       break;
     49     }
     50 
     51     case GL_WITH_DEFAULT:
     52     case GL_WITH_BITMAP: {
     53       CHECK(gfx::InitializeGLBindings(gfx::kGLImplementationOSMesaGL));
     54 
     55       using WebKit::WebGraphicsContext3D;
     56       using webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl;
     57       scoped_ptr<WebGraphicsContext3D> context(
     58           WebGraphicsContext3DInProcessCommandBufferImpl::
     59               CreateOffscreenContext(WebGraphicsContext3D::Attributes()));
     60       output_surface.reset(new PixelTestOutputSurface(context.Pass()));
     61       break;
     62     }
     63   }
     64 
     65   output_surface->set_viewport_offset(viewport_offset);
     66   output_surface->set_surface_expansion_size(surface_expansion_size);
     67   return output_surface.PassAs<OutputSurface>();
     68 }
     69 
     70 scoped_refptr<cc::ContextProvider>
     71 LayerTreePixelTest::OffscreenContextProviderForMainThread() {
     72   scoped_refptr<webkit::gpu::ContextProviderInProcess> provider =
     73       webkit::gpu::ContextProviderInProcess::CreateOffscreen();
     74   CHECK(provider->BindToCurrentThread());
     75   return provider;
     76 }
     77 
     78 scoped_refptr<cc::ContextProvider>
     79 LayerTreePixelTest::OffscreenContextProviderForCompositorThread() {
     80   scoped_refptr<webkit::gpu::ContextProviderInProcess> provider =
     81       webkit::gpu::ContextProviderInProcess::CreateOffscreen();
     82   CHECK(provider.get());
     83   return provider;
     84 }
     85 
     86 scoped_ptr<CopyOutputRequest> LayerTreePixelTest::CreateCopyOutputRequest() {
     87   return CopyOutputRequest::CreateBitmapRequest(
     88       base::Bind(&LayerTreePixelTest::ReadbackResult, base::Unretained(this)));
     89 }
     90 
     91 void LayerTreePixelTest::ReadbackResult(scoped_ptr<CopyOutputResult> result) {
     92   ASSERT_TRUE(result->HasBitmap());
     93   result_bitmap_ = result->TakeBitmap().Pass();
     94   EndTest();
     95 }
     96 
     97 void LayerTreePixelTest::BeginTest() {
     98   Layer* target = readback_target_ ? readback_target_
     99                                    : layer_tree_host()->root_layer();
    100   target->RequestCopyOfOutput(CreateCopyOutputRequest().Pass());
    101   PostSetNeedsCommitToMainThread();
    102 }
    103 
    104 void LayerTreePixelTest::AfterTest() {
    105   base::FilePath test_data_dir;
    106   EXPECT_TRUE(PathService::Get(cc::DIR_TEST_DATA, &test_data_dir));
    107   base::FilePath ref_file_path = test_data_dir.Append(ref_file_);
    108 
    109   // To rebaseline:
    110   // EXPECT_TRUE(WritePNGFile(*result_bitmap_, ref_file_path, true));
    111 
    112   EXPECT_TRUE(MatchesPNGFile(*result_bitmap_,
    113                              ref_file_path,
    114                              *pixel_comparator_));
    115 }
    116 
    117 scoped_refptr<SolidColorLayer> LayerTreePixelTest::CreateSolidColorLayer(
    118     gfx::Rect rect, SkColor color) {
    119   scoped_refptr<SolidColorLayer> layer = SolidColorLayer::Create();
    120   layer->SetIsDrawable(true);
    121   layer->SetAnchorPoint(gfx::PointF());
    122   layer->SetBounds(rect.size());
    123   layer->SetPosition(rect.origin());
    124   layer->SetBackgroundColor(color);
    125   return layer;
    126 }
    127 
    128 void LayerTreePixelTest::EndTest() {
    129   // Drop TextureMailboxes on the main thread so that they can be cleaned up and
    130   // the pending callbacks will fire.
    131   for (size_t i = 0; i < texture_layers_.size(); ++i)
    132     texture_layers_[i]->SetTextureMailbox(TextureMailbox());
    133 
    134   TryEndTest();
    135 }
    136 
    137 void LayerTreePixelTest::TryEndTest() {
    138   if (!result_bitmap_)
    139     return;
    140   if (pending_texture_mailbox_callbacks_)
    141     return;
    142   LayerTreeTest::EndTest();
    143 }
    144 
    145 scoped_refptr<SolidColorLayer> LayerTreePixelTest::
    146     CreateSolidColorLayerWithBorder(
    147         gfx::Rect rect, SkColor color, int border_width, SkColor border_color) {
    148   scoped_refptr<SolidColorLayer> layer = CreateSolidColorLayer(rect, color);
    149   scoped_refptr<SolidColorLayer> border_top = CreateSolidColorLayer(
    150       gfx::Rect(0, 0, rect.width(), border_width), border_color);
    151   scoped_refptr<SolidColorLayer> border_left = CreateSolidColorLayer(
    152       gfx::Rect(0,
    153                 border_width,
    154                 border_width,
    155                 rect.height() - border_width * 2),
    156       border_color);
    157   scoped_refptr<SolidColorLayer> border_right = CreateSolidColorLayer(
    158       gfx::Rect(rect.width() - border_width,
    159                 border_width,
    160                 border_width,
    161                 rect.height() - border_width * 2),
    162       border_color);
    163   scoped_refptr<SolidColorLayer> border_bottom = CreateSolidColorLayer(
    164       gfx::Rect(0, rect.height() - border_width, rect.width(), border_width),
    165       border_color);
    166   layer->AddChild(border_top);
    167   layer->AddChild(border_left);
    168   layer->AddChild(border_right);
    169   layer->AddChild(border_bottom);
    170   return layer;
    171 }
    172 
    173 scoped_refptr<TextureLayer> LayerTreePixelTest::CreateTextureLayer(
    174     gfx::Rect rect, const SkBitmap& bitmap) {
    175   scoped_refptr<TextureLayer> layer = TextureLayer::CreateForMailbox(NULL);
    176   layer->SetIsDrawable(true);
    177   layer->SetAnchorPoint(gfx::PointF());
    178   layer->SetBounds(rect.size());
    179   layer->SetPosition(rect.origin());
    180   layer->SetTextureMailbox(CopyBitmapToTextureMailboxAsTexture(bitmap));
    181 
    182   texture_layers_.push_back(layer);
    183   pending_texture_mailbox_callbacks_++;
    184   return layer;
    185 }
    186 
    187 void LayerTreePixelTest::RunPixelTest(
    188     PixelTestType test_type,
    189     scoped_refptr<Layer> content_root,
    190     base::FilePath file_name) {
    191   test_type_ = test_type;
    192   content_root_ = content_root;
    193   readback_target_ = NULL;
    194   ref_file_ = file_name;
    195   RunTest(true, false, true);
    196 }
    197 
    198 void LayerTreePixelTest::RunPixelTestWithReadbackTarget(
    199     PixelTestType test_type,
    200     scoped_refptr<Layer> content_root,
    201     Layer* target,
    202     base::FilePath file_name) {
    203   test_type_ = test_type;
    204   content_root_ = content_root;
    205   readback_target_ = target;
    206   ref_file_ = file_name;
    207   RunTest(true, false, true);
    208 }
    209 
    210 void LayerTreePixelTest::SetupTree() {
    211   scoped_refptr<Layer> root = Layer::Create();
    212   root->SetBounds(content_root_->bounds());
    213   root->AddChild(content_root_);
    214   layer_tree_host()->SetRootLayer(root);
    215   LayerTreeTest::SetupTree();
    216 }
    217 
    218 scoped_ptr<SkBitmap> LayerTreePixelTest::CopyTextureMailboxToBitmap(
    219     gfx::Size size,
    220     const TextureMailbox& texture_mailbox) {
    221   DCHECK(texture_mailbox.IsTexture());
    222   if (!texture_mailbox.IsTexture())
    223     return scoped_ptr<SkBitmap>();
    224 
    225   using webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl;
    226   scoped_ptr<WebKit::WebGraphicsContext3D> context3d(
    227       WebGraphicsContext3DInProcessCommandBufferImpl::CreateOffscreenContext(
    228           WebKit::WebGraphicsContext3D::Attributes()));
    229 
    230   EXPECT_TRUE(context3d->makeContextCurrent());
    231 
    232   if (texture_mailbox.sync_point())
    233     context3d->waitSyncPoint(texture_mailbox.sync_point());
    234 
    235   unsigned texture_id = context3d->createTexture();
    236   context3d->bindTexture(GL_TEXTURE_2D, texture_id);
    237   context3d->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    238   context3d->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    239   context3d->texParameteri(
    240       GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    241   context3d->texParameteri(
    242       GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    243   context3d->consumeTextureCHROMIUM(texture_mailbox.target(),
    244                                     texture_mailbox.data());
    245   context3d->bindTexture(GL_TEXTURE_2D, 0);
    246 
    247   unsigned fbo = context3d->createFramebuffer();
    248   context3d->bindFramebuffer(GL_FRAMEBUFFER, fbo);
    249   context3d->framebufferTexture2D(GL_FRAMEBUFFER,
    250                                   GL_COLOR_ATTACHMENT0,
    251                                   GL_TEXTURE_2D,
    252                                   texture_id,
    253                                   0);
    254   EXPECT_EQ(static_cast<unsigned>(GL_FRAMEBUFFER_COMPLETE),
    255             context3d->checkFramebufferStatus(GL_FRAMEBUFFER));
    256 
    257   scoped_ptr<uint8[]> pixels(new uint8[size.GetArea() * 4]);
    258   context3d->readPixels(0,
    259                         0,
    260                         size.width(),
    261                         size.height(),
    262                         GL_RGBA,
    263                         GL_UNSIGNED_BYTE,
    264                         pixels.get());
    265 
    266   context3d->deleteFramebuffer(fbo);
    267   context3d->deleteTexture(texture_id);
    268 
    269   scoped_ptr<SkBitmap> bitmap(new SkBitmap);
    270   bitmap->setConfig(SkBitmap::kARGB_8888_Config,
    271                     size.width(),
    272                     size.height());
    273   bitmap->allocPixels();
    274 
    275   scoped_ptr<SkAutoLockPixels> lock(new SkAutoLockPixels(*bitmap));
    276   uint8* out_pixels = static_cast<uint8*>(bitmap->getPixels());
    277 
    278   size_t row_bytes = size.width() * 4;
    279   size_t total_bytes = size.height() * row_bytes;
    280   for (size_t dest_y = 0; dest_y < total_bytes; dest_y += row_bytes) {
    281     // Flip Y axis.
    282     size_t src_y = total_bytes - dest_y - row_bytes;
    283     // Swizzle OpenGL -> Skia byte order.
    284     for (size_t x = 0; x < row_bytes; x += 4) {
    285       out_pixels[dest_y + x + SK_R32_SHIFT/8] = pixels.get()[src_y + x + 0];
    286       out_pixels[dest_y + x + SK_G32_SHIFT/8] = pixels.get()[src_y + x + 1];
    287       out_pixels[dest_y + x + SK_B32_SHIFT/8] = pixels.get()[src_y + x + 2];
    288       out_pixels[dest_y + x + SK_A32_SHIFT/8] = pixels.get()[src_y + x + 3];
    289     }
    290   }
    291 
    292   return bitmap.Pass();
    293 }
    294 
    295 void LayerTreePixelTest::ReleaseTextureMailbox(
    296     scoped_ptr<WebKit::WebGraphicsContext3D> context3d,
    297     uint32 texture,
    298     uint32 sync_point,
    299     bool lost_resource) {
    300   if (sync_point)
    301     context3d->waitSyncPoint(sync_point);
    302   context3d->deleteTexture(texture);
    303   pending_texture_mailbox_callbacks_--;
    304   TryEndTest();
    305 }
    306 
    307 TextureMailbox LayerTreePixelTest::CopyBitmapToTextureMailboxAsTexture(
    308     const SkBitmap& bitmap) {
    309   DCHECK_GT(bitmap.width(), 0);
    310   DCHECK_GT(bitmap.height(), 0);
    311 
    312   CHECK(gfx::InitializeGLBindings(gfx::kGLImplementationOSMesaGL));
    313 
    314   using webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl;
    315   scoped_ptr<WebKit::WebGraphicsContext3D> context3d(
    316       WebGraphicsContext3DInProcessCommandBufferImpl::CreateOffscreenContext(
    317           WebKit::WebGraphicsContext3D::Attributes()));
    318 
    319   EXPECT_TRUE(context3d->makeContextCurrent());
    320 
    321   unsigned texture_id = context3d->createTexture();
    322   context3d->bindTexture(GL_TEXTURE_2D, texture_id);
    323   context3d->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    324   context3d->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    325   context3d->texParameteri(
    326       GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    327   context3d->texParameteri(
    328       GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    329 
    330   DCHECK_EQ(SkBitmap::kARGB_8888_Config, bitmap.getConfig());
    331 
    332   {
    333     SkAutoLockPixels lock(bitmap);
    334 
    335     size_t row_bytes = bitmap.width() * 4;
    336     size_t total_bytes = bitmap.height() * row_bytes;
    337 
    338     scoped_ptr<uint8[]> gl_pixels(new uint8[total_bytes]);
    339     uint8* bitmap_pixels = static_cast<uint8*>(bitmap.getPixels());
    340 
    341     for (size_t y = 0; y < total_bytes; y += row_bytes) {
    342       // Flip Y axis.
    343       size_t src_y = total_bytes - y - row_bytes;
    344       // Swizzle Skia -> OpenGL byte order.
    345       for (size_t x = 0; x < row_bytes; x += 4) {
    346         gl_pixels.get()[y + x + 0] = bitmap_pixels[src_y + x + SK_R32_SHIFT/8];
    347         gl_pixels.get()[y + x + 1] = bitmap_pixels[src_y + x + SK_G32_SHIFT/8];
    348         gl_pixels.get()[y + x + 2] = bitmap_pixels[src_y + x + SK_B32_SHIFT/8];
    349         gl_pixels.get()[y + x + 3] = bitmap_pixels[src_y + x + SK_A32_SHIFT/8];
    350       }
    351     }
    352 
    353     context3d->texImage2D(GL_TEXTURE_2D,
    354                           0,
    355                           GL_RGBA,
    356                           bitmap.width(),
    357                           bitmap.height(),
    358                           0,
    359                           GL_RGBA,
    360                           GL_UNSIGNED_BYTE,
    361                           gl_pixels.get());
    362   }
    363 
    364   gpu::Mailbox mailbox;
    365   context3d->genMailboxCHROMIUM(mailbox.name);
    366   context3d->produceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
    367   context3d->bindTexture(GL_TEXTURE_2D, 0);
    368   uint32 sync_point = context3d->insertSyncPoint();
    369 
    370   return TextureMailbox(
    371       mailbox,
    372       base::Bind(&LayerTreePixelTest::ReleaseTextureMailbox,
    373                  base::Unretained(this),
    374                  base::Passed(&context3d),
    375                  texture_id),
    376       sync_point);
    377 }
    378 
    379 }  // namespace cc
    380