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