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