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