| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "cc/trees/layer_tree_host.h" |
| |
| #include "cc/test/fake_content_layer_client.h" |
| #include "cc/test/fake_picture_layer.h" |
| #include "cc/test/fake_picture_layer_impl.h" |
| #include "cc/test/layer_tree_test.h" |
| #include "cc/trees/layer_tree_impl.h" |
| |
| namespace cc { |
| namespace { |
| |
| // These tests deal with picture layers. |
| class LayerTreeHostPictureTest : public LayerTreeTest { |
| protected: |
| void SetupTreeWithSinglePictureLayer(const gfx::Size& size) { |
| scoped_refptr<Layer> root = Layer::Create(); |
| root->SetBounds(size); |
| |
| root_picture_layer_ = FakePictureLayer::Create(&client_); |
| root_picture_layer_->SetBounds(size); |
| root->AddChild(root_picture_layer_); |
| |
| layer_tree_host()->SetRootLayer(root); |
| client_.set_bounds(size); |
| } |
| |
| scoped_refptr<FakePictureLayer> root_picture_layer_; |
| FakeContentLayerClient client_; |
| }; |
| |
| class LayerTreeHostPictureTestTwinLayer |
| : public LayerTreeHostPictureTest { |
| void SetupTree() override { |
| SetupTreeWithSinglePictureLayer(gfx::Size(1, 1)); |
| picture_id1_ = root_picture_layer_->id(); |
| picture_id2_ = -1; |
| } |
| |
| void BeginTest() override { |
| activates_ = 0; |
| PostSetNeedsCommitToMainThread(); |
| } |
| |
| void DidCommit() override { |
| switch (layer_tree_host()->source_frame_number()) { |
| case 1: |
| // Activate while there are pending and active twins in place. |
| layer_tree_host()->SetNeedsCommit(); |
| break; |
| case 2: |
| // Drop the picture layer from the tree so the activate will have an |
| // active layer without a pending twin. |
| root_picture_layer_->RemoveFromParent(); |
| break; |
| case 3: { |
| // Add a new picture layer so the activate will have a pending layer |
| // without an active twin. |
| scoped_refptr<FakePictureLayer> picture = |
| FakePictureLayer::Create(&client_); |
| picture_id2_ = picture->id(); |
| layer_tree_host()->root_layer()->AddChild(picture); |
| break; |
| } |
| case 4: |
| // Active while there are pending and active twins again. |
| layer_tree_host()->SetNeedsCommit(); |
| break; |
| case 5: |
| EndTest(); |
| break; |
| } |
| } |
| |
| void WillActivateTreeOnThread(LayerTreeHostImpl* impl) override { |
| LayerImpl* active_root_impl = impl->active_tree()->root_layer_for_testing(); |
| int picture_id = impl->active_tree()->source_frame_number() < 2 |
| ? picture_id1_ |
| : picture_id2_; |
| |
| if (!impl->pending_tree()->LayerById(picture_id)) { |
| EXPECT_EQ(2, activates_); |
| return; |
| } |
| |
| FakePictureLayerImpl* pending_picture_impl = |
| static_cast<FakePictureLayerImpl*>( |
| impl->pending_tree()->LayerById(picture_id)); |
| |
| if (!active_root_impl) { |
| EXPECT_EQ(0, activates_); |
| EXPECT_EQ(nullptr, pending_picture_impl->GetPendingOrActiveTwinLayer()); |
| return; |
| } |
| |
| if (!impl->active_tree()->LayerById(picture_id)) { |
| EXPECT_EQ(3, activates_); |
| EXPECT_EQ(nullptr, pending_picture_impl->GetPendingOrActiveTwinLayer()); |
| return; |
| } |
| |
| FakePictureLayerImpl* active_picture_impl = |
| static_cast<FakePictureLayerImpl*>( |
| impl->active_tree()->LayerById(picture_id)); |
| |
| // After the first activation, when we commit again, we'll have a pending |
| // and active layer. Then we recreate a picture layer in the 4th activate |
| // and the next commit will have a pending and active twin again. |
| EXPECT_TRUE(activates_ == 1 || activates_ == 4) << activates_; |
| |
| EXPECT_EQ(pending_picture_impl, |
| active_picture_impl->GetPendingOrActiveTwinLayer()); |
| EXPECT_EQ(active_picture_impl, |
| pending_picture_impl->GetPendingOrActiveTwinLayer()); |
| } |
| |
| void DidActivateTreeOnThread(LayerTreeHostImpl* impl) override { |
| int picture_id = impl->active_tree()->source_frame_number() < 3 |
| ? picture_id1_ |
| : picture_id2_; |
| if (!impl->active_tree()->LayerById(picture_id)) { |
| EXPECT_EQ(2, activates_); |
| } else { |
| FakePictureLayerImpl* active_picture_impl = |
| static_cast<FakePictureLayerImpl*>( |
| impl->active_tree()->LayerById(picture_id)); |
| EXPECT_EQ(nullptr, active_picture_impl->GetPendingOrActiveTwinLayer()); |
| } |
| |
| ++activates_; |
| } |
| |
| void AfterTest() override { EXPECT_EQ(5, activates_); } |
| |
| int activates_; |
| int picture_id1_; |
| int picture_id2_; |
| }; |
| |
| // There is no pending layers in single thread mode. |
| MULTI_THREAD_TEST_F(LayerTreeHostPictureTestTwinLayer); |
| |
| class LayerTreeHostPictureTestResizeViewportWithGpuRaster |
| : public LayerTreeHostPictureTest { |
| void InitializeSettings(LayerTreeSettings* settings) override { |
| settings->gpu_rasterization_forced = true; |
| } |
| |
| void SetupTree() override { |
| scoped_refptr<Layer> root = Layer::Create(); |
| root->SetBounds(gfx::Size(768, 960)); |
| client_.set_bounds(root->bounds()); |
| client_.set_fill_with_nonsolid_color(true); |
| picture_ = FakePictureLayer::Create(&client_); |
| picture_->SetBounds(gfx::Size(768, 960)); |
| root->AddChild(picture_); |
| |
| layer_tree_host()->SetRootLayer(root); |
| LayerTreeHostPictureTest::SetupTree(); |
| } |
| |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void CommitCompleteOnThread(LayerTreeHostImpl* impl) override { |
| LayerImpl* child = impl->sync_tree()->LayerById(picture_->id()); |
| FakePictureLayerImpl* picture_impl = |
| static_cast<FakePictureLayerImpl*>(child); |
| gfx::Size tile_size = |
| picture_impl->HighResTiling()->TileAt(0, 0)->content_rect().size(); |
| |
| switch (impl->sync_tree()->source_frame_number()) { |
| case 0: |
| tile_size_ = tile_size; |
| // GPU Raster picks a tile size based on the viewport size. |
| EXPECT_EQ(gfx::Size(768, 256), tile_size); |
| break; |
| case 1: |
| // When the viewport changed size, the new frame's tiles should change |
| // along with it. |
| EXPECT_NE(gfx::Size(768, 256), tile_size); |
| } |
| } |
| |
| void DidCommit() override { |
| switch (layer_tree_host()->source_frame_number()) { |
| case 1: |
| // Change the picture layer's size along with the viewport, so it will |
| // consider picking a new tile size. |
| picture_->SetBounds(gfx::Size(768, 1056)); |
| layer_tree_host()->SetViewportSize(gfx::Size(768, 1056)); |
| break; |
| case 2: |
| EndTest(); |
| } |
| } |
| |
| void AfterTest() override {} |
| |
| gfx::Size tile_size_; |
| FakeContentLayerClient client_; |
| scoped_refptr<FakePictureLayer> picture_; |
| }; |
| |
| SINGLE_AND_MULTI_THREAD_TEST_F( |
| LayerTreeHostPictureTestResizeViewportWithGpuRaster); |
| |
| class LayerTreeHostPictureTestChangeLiveTilesRectWithRecycleTree |
| : public LayerTreeHostPictureTest { |
| void SetupTree() override { |
| frame_ = 0; |
| did_post_commit_ = false; |
| |
| scoped_refptr<Layer> root = Layer::Create(); |
| root->SetBounds(gfx::Size(100, 100)); |
| // The layer is big enough that the live tiles rect won't cover the full |
| // layer. |
| client_.set_fill_with_nonsolid_color(true); |
| picture_ = FakePictureLayer::Create(&client_); |
| picture_->SetBounds(gfx::Size(100, 100000)); |
| root->AddChild(picture_); |
| |
| // picture_'s transform is going to be changing on the compositor thread, so |
| // force it to have a transform node by making it scrollable. |
| picture_->SetScrollClipLayerId(root->id()); |
| |
| layer_tree_host()->SetRootLayer(root); |
| LayerTreeHostPictureTest::SetupTree(); |
| client_.set_bounds(picture_->bounds()); |
| } |
| |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void DrawLayersOnThread(LayerTreeHostImpl* impl) override { |
| LayerImpl* child = impl->active_tree()->LayerById(picture_->id()); |
| FakePictureLayerImpl* picture_impl = |
| static_cast<FakePictureLayerImpl*>(child); |
| switch (++frame_) { |
| case 1: { |
| PictureLayerTiling* tiling = picture_impl->HighResTiling(); |
| int num_tiles_y = tiling->TilingDataForTesting().num_tiles_y(); |
| |
| // There should be tiles at the top of the picture layer but not at the |
| // bottom. |
| EXPECT_TRUE(tiling->TileAt(0, 0)); |
| EXPECT_FALSE(tiling->TileAt(0, num_tiles_y)); |
| |
| // Make the bottom of the layer visible. |
| gfx::Transform transform; |
| transform.Translate(0.f, -100000.f + 100.f); |
| picture_impl->OnTransformAnimated(transform); |
| impl->SetNeedsRedraw(); |
| break; |
| } |
| case 2: { |
| PictureLayerTiling* tiling = picture_impl->HighResTiling(); |
| |
| // There not be tiles at the top of the layer now. |
| EXPECT_FALSE(tiling->TileAt(0, 0)); |
| |
| // Make the top of the layer visible again. |
| picture_impl->OnTransformAnimated(gfx::Transform()); |
| impl->SetNeedsRedraw(); |
| break; |
| } |
| case 3: { |
| PictureLayerTiling* tiling = picture_impl->HighResTiling(); |
| int num_tiles_y = tiling->TilingDataForTesting().num_tiles_y(); |
| |
| // There should be tiles at the top of the picture layer again. |
| EXPECT_TRUE(tiling->TileAt(0, 0)); |
| EXPECT_FALSE(tiling->TileAt(0, num_tiles_y)); |
| |
| // Make a new main frame without changing the picture layer at all, so |
| // it won't need to update or push properties. |
| did_post_commit_ = true; |
| PostSetNeedsCommitToMainThread(); |
| break; |
| } |
| } |
| } |
| |
| void WillActivateTreeOnThread(LayerTreeHostImpl* impl) override { |
| LayerImpl* child = impl->sync_tree()->LayerById(picture_->id()); |
| FakePictureLayerImpl* picture_impl = |
| static_cast<FakePictureLayerImpl*>(child); |
| PictureLayerTiling* tiling = picture_impl->HighResTiling(); |
| int num_tiles_y = tiling->TilingDataForTesting().num_tiles_y(); |
| |
| if (!impl->active_tree()->root_layer_for_testing()) { |
| // If active tree doesn't have the layer, then pending tree should have |
| // all needed tiles. |
| EXPECT_TRUE(tiling->TileAt(0, 0)); |
| } else { |
| // Since there was no invalidation, the pending tree shouldn't have any |
| // tiles. |
| EXPECT_FALSE(tiling->TileAt(0, 0)); |
| } |
| EXPECT_FALSE(tiling->TileAt(0, num_tiles_y)); |
| |
| if (did_post_commit_) |
| EndTest(); |
| } |
| |
| void AfterTest() override {} |
| |
| int frame_; |
| bool did_post_commit_; |
| FakeContentLayerClient client_; |
| scoped_refptr<FakePictureLayer> picture_; |
| }; |
| |
| // Multi-thread only since there is no recycle tree in single thread. |
| MULTI_THREAD_TEST_F(LayerTreeHostPictureTestChangeLiveTilesRectWithRecycleTree); |
| |
| class LayerTreeHostPictureTestRSLLMembership : public LayerTreeHostPictureTest { |
| void SetupTree() override { |
| scoped_refptr<Layer> root = Layer::Create(); |
| root->SetBounds(gfx::Size(100, 100)); |
| client_.set_bounds(root->bounds()); |
| |
| child_ = Layer::Create(); |
| root->AddChild(child_); |
| |
| // Don't be solid color so the layer has tilings/tiles. |
| client_.set_fill_with_nonsolid_color(true); |
| picture_ = FakePictureLayer::Create(&client_); |
| picture_->SetBounds(gfx::Size(100, 100)); |
| child_->AddChild(picture_); |
| |
| layer_tree_host()->SetRootLayer(root); |
| LayerTreeHostPictureTest::SetupTree(); |
| } |
| |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void CommitCompleteOnThread(LayerTreeHostImpl* impl) override { |
| LayerImpl* gchild = impl->sync_tree()->LayerById(picture_->id()); |
| FakePictureLayerImpl* picture = static_cast<FakePictureLayerImpl*>(gchild); |
| |
| switch (impl->sync_tree()->source_frame_number()) { |
| case 0: |
| // On 1st commit the layer has tilings. |
| EXPECT_GT(picture->tilings()->num_tilings(), 0u); |
| break; |
| case 1: |
| // On 2nd commit, the layer is transparent, but its tilings are left |
| // there. |
| EXPECT_GT(picture->tilings()->num_tilings(), 0u); |
| break; |
| case 2: |
| // On 3rd commit, the layer is visible again, so has tilings. |
| EXPECT_GT(picture->tilings()->num_tilings(), 0u); |
| } |
| } |
| |
| void DidActivateTreeOnThread(LayerTreeHostImpl* impl) override { |
| LayerImpl* gchild = impl->sync_tree()->LayerById(picture_->id()); |
| FakePictureLayerImpl* picture = static_cast<FakePictureLayerImpl*>(gchild); |
| |
| switch (impl->active_tree()->source_frame_number()) { |
| case 0: |
| // On 1st commit the layer has tilings. |
| EXPECT_GT(picture->tilings()->num_tilings(), 0u); |
| break; |
| case 1: |
| // On 2nd commit, the layer is transparent, but its tilings are left |
| // there. |
| EXPECT_GT(picture->tilings()->num_tilings(), 0u); |
| break; |
| case 2: |
| // On 3rd commit, the layer is visible again, so has tilings. |
| EXPECT_GT(picture->tilings()->num_tilings(), 0u); |
| EndTest(); |
| } |
| } |
| |
| void DidCommit() override { |
| switch (layer_tree_host()->source_frame_number()) { |
| case 1: |
| // For the 2nd commit, change opacity to 0 so that the layer will not be |
| // part of the visible frame. |
| child_->SetOpacity(0.f); |
| break; |
| case 2: |
| // For the 3rd commit, change opacity to 1 so that the layer will again |
| // be part of the visible frame. |
| child_->SetOpacity(1.f); |
| } |
| } |
| |
| void AfterTest() override {} |
| |
| FakeContentLayerClient client_; |
| scoped_refptr<Layer> child_; |
| scoped_refptr<FakePictureLayer> picture_; |
| }; |
| |
| SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostPictureTestRSLLMembership); |
| |
| class LayerTreeHostPictureTestRSLLMembershipWithScale |
| : public LayerTreeHostPictureTest { |
| void SetupTree() override { |
| scoped_refptr<Layer> root_clip = Layer::Create(); |
| root_clip->SetBounds(gfx::Size(100, 100)); |
| scoped_refptr<Layer> page_scale_layer = Layer::Create(); |
| page_scale_layer->SetBounds(gfx::Size(100, 100)); |
| |
| pinch_ = Layer::Create(); |
| pinch_->SetBounds(gfx::Size(500, 500)); |
| pinch_->SetScrollClipLayerId(root_clip->id()); |
| pinch_->SetIsContainerForFixedPositionLayers(true); |
| page_scale_layer->AddChild(pinch_); |
| root_clip->AddChild(page_scale_layer); |
| |
| // Don't be solid color so the layer has tilings/tiles. |
| client_.set_fill_with_nonsolid_color(true); |
| picture_ = FakePictureLayer::Create(&client_); |
| picture_->SetBounds(gfx::Size(100, 100)); |
| pinch_->AddChild(picture_); |
| |
| layer_tree_host()->RegisterViewportLayers(NULL, page_scale_layer, pinch_, |
| nullptr); |
| layer_tree_host()->SetPageScaleFactorAndLimits(1.f, 1.f, 4.f); |
| layer_tree_host()->SetRootLayer(root_clip); |
| LayerTreeHostPictureTest::SetupTree(); |
| client_.set_bounds(picture_->bounds()); |
| } |
| |
| void InitializeSettings(LayerTreeSettings* settings) override { |
| settings->layer_transforms_should_scale_layer_contents = true; |
| } |
| |
| void BeginTest() override { |
| frame_ = 0; |
| draws_in_frame_ = 0; |
| last_frame_drawn_ = -1; |
| ready_to_draw_ = false; |
| PostSetNeedsCommitToMainThread(); |
| } |
| |
| void WillActivateTreeOnThread(LayerTreeHostImpl* impl) override { |
| LayerImpl* gchild = impl->sync_tree()->LayerById(picture_->id()); |
| FakePictureLayerImpl* picture = static_cast<FakePictureLayerImpl*>(gchild); |
| ready_to_draw_ = false; |
| |
| switch (frame_) { |
| case 0: |
| // On 1st commit the pending layer has tilings. |
| ASSERT_EQ(1u, picture->tilings()->num_tilings()); |
| EXPECT_EQ(1.f, picture->tilings()->tiling_at(0)->contents_scale()); |
| break; |
| case 1: |
| // On 2nd commit, the pending layer is transparent, so has a stale |
| // value. |
| ASSERT_EQ(1u, picture->tilings()->num_tilings()); |
| EXPECT_EQ(1.f, picture->tilings()->tiling_at(0)->contents_scale()); |
| break; |
| case 2: |
| // On 3rd commit, the pending layer is visible again, so has tilings and |
| // is updated for the pinch. |
| ASSERT_EQ(1u, picture->tilings()->num_tilings()); |
| EXPECT_EQ(2.f, picture->tilings()->tiling_at(0)->contents_scale()); |
| } |
| } |
| |
| void DrawLayersOnThread(LayerTreeHostImpl* impl) override { |
| LayerImpl* gchild = impl->active_tree()->LayerById(picture_->id()); |
| FakePictureLayerImpl* picture = static_cast<FakePictureLayerImpl*>(gchild); |
| |
| if (frame_ != last_frame_drawn_) |
| draws_in_frame_ = 0; |
| ++draws_in_frame_; |
| last_frame_drawn_ = frame_; |
| |
| switch (frame_) { |
| case 0: |
| if (draws_in_frame_ == 1) { |
| // On 1st commit the layer has tilings. |
| EXPECT_GT(picture->tilings()->num_tilings(), 0u); |
| EXPECT_EQ(1.f, picture->HighResTiling()->contents_scale()); |
| |
| // Pinch zoom in to change the scale on the active tree. |
| impl->PinchGestureBegin(); |
| impl->PinchGestureUpdate(2.f, gfx::Point(1, 1)); |
| impl->PinchGestureEnd(); |
| } else if (picture->tilings()->num_tilings() == 1) { |
| // If the pinch gesture caused a commit we could get here with a |
| // pending tree. |
| EXPECT_FALSE(impl->pending_tree()); |
| EXPECT_EQ(2.f, picture->HighResTiling()->contents_scale()); |
| |
| // Need to wait for ready to draw here so that the pinch is |
| // entirely complete, otherwise another draw might come in before |
| // the commit occurs. |
| if (ready_to_draw_) { |
| ++frame_; |
| MainThreadTaskRunner()->PostTask( |
| FROM_HERE, |
| base::Bind( |
| &LayerTreeHostPictureTestRSLLMembershipWithScale::NextStep, |
| base::Unretained(this))); |
| } |
| } |
| break; |
| case 1: |
| EXPECT_EQ(1, draws_in_frame_); |
| // On 2nd commit, this active layer is transparent, so does not update |
| // tilings. It has the high res scale=2 from the previous frame, and |
| // also a scale=1 copied from the pending layer's stale value during |
| // activation. |
| EXPECT_EQ(2u, picture->picture_layer_tiling_set()->num_tilings()); |
| |
| ++frame_; |
| MainThreadTaskRunner()->PostTask( |
| FROM_HERE, |
| base::Bind( |
| &LayerTreeHostPictureTestRSLLMembershipWithScale::NextStep, |
| base::Unretained(this))); |
| break; |
| case 2: |
| EXPECT_EQ(1, draws_in_frame_); |
| // On 3rd commit, the layer is visible again, so has tilings. |
| EXPECT_GT(picture->tilings()->num_tilings(), 0u); |
| EndTest(); |
| } |
| } |
| |
| void NextStep() { |
| switch (frame_) { |
| case 1: |
| // For the 2nd commit, change opacity to 0 so that the layer will not be |
| // part of the visible frame. |
| pinch_->SetOpacity(0.f); |
| break; |
| case 2: |
| // For the 3rd commit, change opacity to 1 so that the layer will again |
| // be part of the visible frame. |
| pinch_->SetOpacity(1.f); |
| break; |
| } |
| } |
| |
| void NotifyReadyToDrawOnThread(LayerTreeHostImpl* impl) override { |
| ready_to_draw_ = true; |
| if (frame_ == 0) { |
| // The ready to draw can race with a draw in which everything is |
| // actually ready. Therefore, just issue one more extra draw |
| // here to force notify->draw ordering. |
| impl->SetNeedsRedraw(); |
| } |
| } |
| |
| void AfterTest() override {} |
| |
| FakeContentLayerClient client_; |
| scoped_refptr<Layer> pinch_; |
| scoped_refptr<FakePictureLayer> picture_; |
| int frame_; |
| int draws_in_frame_; |
| int last_frame_drawn_; |
| bool ready_to_draw_; |
| }; |
| |
| // Multi-thread only because in single thread you can't pinch zoom on the |
| // compositor thread. |
| MULTI_THREAD_TEST_F(LayerTreeHostPictureTestRSLLMembershipWithScale); |
| |
| } // namespace |
| } // namespace cc |