// Copyright 2011 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/tree_synchronizer.h" #include #include "cc/layer.h" #include "cc/layer_animation_controller.h" #include "cc/layer_impl.h" #include "cc/proxy.h" #include "cc/single_thread_proxy.h" #include "cc/test/animation_test_common.h" #include "cc/test/fake_impl_proxy.h" #include "cc/test/fake_layer_tree_host_impl.h" #include "testing/gtest/include/gtest/gtest.h" namespace cc { namespace { class MockLayerImpl : public LayerImpl { public: static scoped_ptr Create(LayerTreeImpl* treeImpl, int layerId) { return make_scoped_ptr(new MockLayerImpl(treeImpl, layerId)); } virtual ~MockLayerImpl() { if (m_layerImplDestructionList) m_layerImplDestructionList->push_back(id()); } void setLayerImplDestructionList(std::vector* list) { m_layerImplDestructionList = list; } private: MockLayerImpl(LayerTreeImpl* treeImpl, int layerId) : LayerImpl(treeImpl, layerId) , m_layerImplDestructionList(0) { } std::vector* m_layerImplDestructionList; }; class MockLayer : public Layer { public: static scoped_refptr Create(std::vector* layerImplDestructionList) { return make_scoped_refptr(new MockLayer(layerImplDestructionList)); } virtual scoped_ptr CreateLayerImpl(LayerTreeImpl* treeImpl) OVERRIDE { return MockLayerImpl::Create(treeImpl, layer_id_).PassAs(); } virtual void PushPropertiesTo(LayerImpl* layerImpl) OVERRIDE { Layer::PushPropertiesTo(layerImpl); MockLayerImpl* mockLayerImpl = static_cast(layerImpl); mockLayerImpl->setLayerImplDestructionList(m_layerImplDestructionList); } private: MockLayer(std::vector* layerImplDestructionList) : Layer() , m_layerImplDestructionList(layerImplDestructionList) { } virtual ~MockLayer() { } std::vector* m_layerImplDestructionList; }; class FakeLayerAnimationController : public LayerAnimationController { public: static scoped_refptr Create() { return static_cast(new FakeLayerAnimationController); } bool synchronizedAnimations() const { return m_synchronizedAnimations; } private: FakeLayerAnimationController() : LayerAnimationController(1) , m_synchronizedAnimations(false) { } virtual ~FakeLayerAnimationController() { } virtual void PushAnimationUpdatesTo(LayerAnimationController* controllerImpl) OVERRIDE { LayerAnimationController::PushAnimationUpdatesTo(controllerImpl); m_synchronizedAnimations = true; } bool m_synchronizedAnimations; }; void expectTreesAreIdentical(Layer* layer, LayerImpl* layerImpl, LayerTreeImpl* treeImpl) { ASSERT_TRUE(layer); ASSERT_TRUE(layerImpl); EXPECT_EQ(layer->id(), layerImpl->id()); EXPECT_EQ(layerImpl->layer_tree_impl(), treeImpl); EXPECT_EQ(layer->non_fast_scrollable_region(), layerImpl->non_fast_scrollable_region()); ASSERT_EQ(!!layer->mask_layer(), !!layerImpl->mask_layer()); if (layer->mask_layer()) expectTreesAreIdentical(layer->mask_layer(), layerImpl->mask_layer(), treeImpl); ASSERT_EQ(!!layer->replica_layer(), !!layerImpl->replica_layer()); if (layer->replica_layer()) expectTreesAreIdentical(layer->replica_layer(), layerImpl->replica_layer(), treeImpl); const std::vector >& layerChildren = layer->children(); const ScopedPtrVector& layerImplChildren = layerImpl->children(); ASSERT_EQ(layerChildren.size(), layerImplChildren.size()); for (size_t i = 0; i < layerChildren.size(); ++i) expectTreesAreIdentical(layerChildren[i].get(), layerImplChildren[i], treeImpl); } class TreeSynchronizerTest : public testing::Test { public: TreeSynchronizerTest() : m_hostImpl(&m_proxy) { } protected: FakeImplProxy m_proxy; FakeLayerTreeHostImpl m_hostImpl; }; // Attempts to synchronizes a null tree. This should not crash, and should // return a null tree. TEST_F(TreeSynchronizerTest, syncNullTree) { scoped_ptr layerImplTreeRoot = TreeSynchronizer::synchronizeTrees(static_cast(NULL), scoped_ptr(), m_hostImpl.active_tree()); EXPECT_TRUE(!layerImplTreeRoot.get()); } // Constructs a very simple tree and synchronizes it without trying to reuse any preexisting layers. TEST_F(TreeSynchronizerTest, syncSimpleTreeFromEmpty) { scoped_refptr layerTreeRoot = Layer::Create(); layerTreeRoot->AddChild(Layer::Create()); layerTreeRoot->AddChild(Layer::Create()); scoped_ptr layerImplTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), scoped_ptr(), m_hostImpl.active_tree()); expectTreesAreIdentical(layerTreeRoot.get(), layerImplTreeRoot.get(), m_hostImpl.active_tree()); } // Constructs a very simple tree and synchronizes it attempting to reuse some layers TEST_F(TreeSynchronizerTest, syncSimpleTreeReusingLayers) { std::vector layerImplDestructionList; scoped_refptr layerTreeRoot = MockLayer::Create(&layerImplDestructionList); layerTreeRoot->AddChild(MockLayer::Create(&layerImplDestructionList)); layerTreeRoot->AddChild(MockLayer::Create(&layerImplDestructionList)); scoped_ptr layerImplTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), scoped_ptr(), m_hostImpl.active_tree()); expectTreesAreIdentical(layerTreeRoot.get(), layerImplTreeRoot.get(), m_hostImpl.active_tree()); // We have to push properties to pick up the destruction list pointer. TreeSynchronizer::pushProperties(layerTreeRoot.get(), layerImplTreeRoot.get()); // Add a new layer to the Layer side layerTreeRoot->children()[0]->AddChild(MockLayer::Create(&layerImplDestructionList)); // Remove one. layerTreeRoot->children()[1]->RemoveFromParent(); int secondLayerImplId = layerImplTreeRoot->children()[1]->id(); // Synchronize again. After the sync the trees should be equivalent and we should have created and destroyed one LayerImpl. layerImplTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), layerImplTreeRoot.Pass(), m_hostImpl.active_tree()); expectTreesAreIdentical(layerTreeRoot.get(), layerImplTreeRoot.get(), m_hostImpl.active_tree()); ASSERT_EQ(1u, layerImplDestructionList.size()); EXPECT_EQ(secondLayerImplId, layerImplDestructionList[0]); } // Constructs a very simple tree and checks that a stacking-order change is tracked properly. TEST_F(TreeSynchronizerTest, syncSimpleTreeAndTrackStackingOrderChange) { std::vector layerImplDestructionList; // Set up the tree and sync once. child2 needs to be synced here, too, even though we // remove it to set up the intended scenario. scoped_refptr layerTreeRoot = MockLayer::Create(&layerImplDestructionList); scoped_refptr child2 = MockLayer::Create(&layerImplDestructionList); layerTreeRoot->AddChild(MockLayer::Create(&layerImplDestructionList)); layerTreeRoot->AddChild(child2); scoped_ptr layerImplTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), scoped_ptr(), m_hostImpl.active_tree()); expectTreesAreIdentical(layerTreeRoot.get(), layerImplTreeRoot.get(), m_hostImpl.active_tree()); // We have to push properties to pick up the destruction list pointer. TreeSynchronizer::pushProperties(layerTreeRoot.get(), layerImplTreeRoot.get()); layerImplTreeRoot->ResetAllChangeTrackingForSubtree(); // re-insert the layer and sync again. child2->RemoveFromParent(); layerTreeRoot->AddChild(child2); layerImplTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), layerImplTreeRoot.Pass(), m_hostImpl.active_tree()); expectTreesAreIdentical(layerTreeRoot.get(), layerImplTreeRoot.get(), m_hostImpl.active_tree()); TreeSynchronizer::pushProperties(layerTreeRoot.get(), layerImplTreeRoot.get()); // Check that the impl thread properly tracked the change. EXPECT_FALSE(layerImplTreeRoot->LayerPropertyChanged()); EXPECT_FALSE(layerImplTreeRoot->children()[0]->LayerPropertyChanged()); EXPECT_TRUE(layerImplTreeRoot->children()[1]->LayerPropertyChanged()); } TEST_F(TreeSynchronizerTest, syncSimpleTreeAndProperties) { scoped_refptr layerTreeRoot = Layer::Create(); layerTreeRoot->AddChild(Layer::Create()); layerTreeRoot->AddChild(Layer::Create()); // Pick some random properties to set. The values are not important, we're just testing that at least some properties are making it through. gfx::PointF rootPosition = gfx::PointF(2.3f, 7.4f); layerTreeRoot->SetPosition(rootPosition); float firstChildOpacity = 0.25f; layerTreeRoot->children()[0]->SetOpacity(firstChildOpacity); gfx::Size secondChildBounds = gfx::Size(25, 53); layerTreeRoot->children()[1]->SetBounds(secondChildBounds); scoped_ptr layerImplTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), scoped_ptr(), m_hostImpl.active_tree()); expectTreesAreIdentical(layerTreeRoot.get(), layerImplTreeRoot.get(), m_hostImpl.active_tree()); TreeSynchronizer::pushProperties(layerTreeRoot.get(), layerImplTreeRoot.get()); // Check that the property values we set on the Layer tree are reflected in the LayerImpl tree. gfx::PointF rootLayerImplPosition = layerImplTreeRoot->position(); EXPECT_EQ(rootPosition.x(), rootLayerImplPosition.x()); EXPECT_EQ(rootPosition.y(), rootLayerImplPosition.y()); EXPECT_EQ(firstChildOpacity, layerImplTreeRoot->children()[0]->opacity()); gfx::Size secondLayerImplChildBounds = layerImplTreeRoot->children()[1]->bounds(); EXPECT_EQ(secondChildBounds.width(), secondLayerImplChildBounds.width()); EXPECT_EQ(secondChildBounds.height(), secondLayerImplChildBounds.height()); } TEST_F(TreeSynchronizerTest, reuseLayerImplsAfterStructuralChange) { std::vector layerImplDestructionList; // Set up a tree with this sort of structure: // root --- A --- B ---+--- C // | // +--- D scoped_refptr layerTreeRoot = MockLayer::Create(&layerImplDestructionList); layerTreeRoot->AddChild(MockLayer::Create(&layerImplDestructionList)); scoped_refptr layerA = layerTreeRoot->children()[0].get(); layerA->AddChild(MockLayer::Create(&layerImplDestructionList)); scoped_refptr layerB = layerA->children()[0].get(); layerB->AddChild(MockLayer::Create(&layerImplDestructionList)); scoped_refptr layerC = layerB->children()[0].get(); layerB->AddChild(MockLayer::Create(&layerImplDestructionList)); scoped_refptr layerD = layerB->children()[1].get(); scoped_ptr layerImplTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), scoped_ptr(), m_hostImpl.active_tree()); expectTreesAreIdentical(layerTreeRoot.get(), layerImplTreeRoot.get(), m_hostImpl.active_tree()); // We have to push properties to pick up the destruction list pointer. TreeSynchronizer::pushProperties(layerTreeRoot.get(), layerImplTreeRoot.get()); // Now restructure the tree to look like this: // root --- D ---+--- A // | // +--- C --- B layerTreeRoot->RemoveAllChildren(); layerD->RemoveAllChildren(); layerTreeRoot->AddChild(layerD); layerA->RemoveAllChildren(); layerD->AddChild(layerA); layerC->RemoveAllChildren(); layerD->AddChild(layerC); layerB->RemoveAllChildren(); layerC->AddChild(layerB); // After another synchronize our trees should match and we should not have destroyed any LayerImpls layerImplTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), layerImplTreeRoot.Pass(), m_hostImpl.active_tree()); expectTreesAreIdentical(layerTreeRoot.get(), layerImplTreeRoot.get(), m_hostImpl.active_tree()); EXPECT_EQ(0u, layerImplDestructionList.size()); } // Constructs a very simple tree, synchronizes it, then synchronizes to a totally new tree. All layers from the old tree should be deleted. TEST_F(TreeSynchronizerTest, syncSimpleTreeThenDestroy) { std::vector layerImplDestructionList; scoped_refptr oldLayerTreeRoot = MockLayer::Create(&layerImplDestructionList); oldLayerTreeRoot->AddChild(MockLayer::Create(&layerImplDestructionList)); oldLayerTreeRoot->AddChild(MockLayer::Create(&layerImplDestructionList)); int oldTreeRootLayerId = oldLayerTreeRoot->id(); int oldTreeFirstChildLayerId = oldLayerTreeRoot->children()[0]->id(); int oldTreeSecondChildLayerId = oldLayerTreeRoot->children()[1]->id(); scoped_ptr layerImplTreeRoot = TreeSynchronizer::synchronizeTrees(oldLayerTreeRoot.get(), scoped_ptr(), m_hostImpl.active_tree()); expectTreesAreIdentical(oldLayerTreeRoot.get(), layerImplTreeRoot.get(), m_hostImpl.active_tree()); // We have to push properties to pick up the destruction list pointer. TreeSynchronizer::pushProperties(oldLayerTreeRoot.get(), layerImplTreeRoot.get()); // Remove all children on the Layer side. oldLayerTreeRoot->RemoveAllChildren(); // Synchronize again. After the sync all LayerImpls from the old tree should be deleted. scoped_refptr newLayerTreeRoot = Layer::Create(); layerImplTreeRoot = TreeSynchronizer::synchronizeTrees(newLayerTreeRoot.get(), layerImplTreeRoot.Pass(), m_hostImpl.active_tree()); expectTreesAreIdentical(newLayerTreeRoot.get(), layerImplTreeRoot.get(), m_hostImpl.active_tree()); ASSERT_EQ(3u, layerImplDestructionList.size()); EXPECT_TRUE(std::find(layerImplDestructionList.begin(), layerImplDestructionList.end(), oldTreeRootLayerId) != layerImplDestructionList.end()); EXPECT_TRUE(std::find(layerImplDestructionList.begin(), layerImplDestructionList.end(), oldTreeFirstChildLayerId) != layerImplDestructionList.end()); EXPECT_TRUE(std::find(layerImplDestructionList.begin(), layerImplDestructionList.end(), oldTreeSecondChildLayerId) != layerImplDestructionList.end()); } // Constructs+syncs a tree with mask, replica, and replica mask layers. TEST_F(TreeSynchronizerTest, syncMaskReplicaAndReplicaMaskLayers) { scoped_refptr layerTreeRoot = Layer::Create(); layerTreeRoot->AddChild(Layer::Create()); layerTreeRoot->AddChild(Layer::Create()); layerTreeRoot->AddChild(Layer::Create()); // First child gets a mask layer. scoped_refptr maskLayer = Layer::Create(); layerTreeRoot->children()[0]->SetMaskLayer(maskLayer.get()); // Second child gets a replica layer. scoped_refptr replicaLayer = Layer::Create(); layerTreeRoot->children()[1]->SetReplicaLayer(replicaLayer.get()); // Third child gets a replica layer with a mask layer. scoped_refptr replicaLayerWithMask = Layer::Create(); scoped_refptr replicaMaskLayer = Layer::Create(); replicaLayerWithMask->SetMaskLayer(replicaMaskLayer.get()); layerTreeRoot->children()[2]->SetReplicaLayer(replicaLayerWithMask.get()); scoped_ptr layerImplTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), scoped_ptr(), m_hostImpl.active_tree()); expectTreesAreIdentical(layerTreeRoot.get(), layerImplTreeRoot.get(), m_hostImpl.active_tree()); // Remove the mask layer. layerTreeRoot->children()[0]->SetMaskLayer(0); layerImplTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), layerImplTreeRoot.Pass(), m_hostImpl.active_tree()); expectTreesAreIdentical(layerTreeRoot.get(), layerImplTreeRoot.get(), m_hostImpl.active_tree()); // Remove the replica layer. layerTreeRoot->children()[1]->SetReplicaLayer(0); layerImplTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), layerImplTreeRoot.Pass(), m_hostImpl.active_tree()); expectTreesAreIdentical(layerTreeRoot.get(), layerImplTreeRoot.get(), m_hostImpl.active_tree()); // Remove the replica mask. replicaLayerWithMask->SetMaskLayer(0); layerImplTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), layerImplTreeRoot.Pass(), m_hostImpl.active_tree()); expectTreesAreIdentical(layerTreeRoot.get(), layerImplTreeRoot.get(), m_hostImpl.active_tree()); } TEST_F(TreeSynchronizerTest, synchronizeAnimations) { LayerTreeSettings settings; FakeProxy proxy(scoped_ptr(NULL)); DebugScopedSetImplThread impl(&proxy); scoped_ptr hostImpl = LayerTreeHostImpl::Create(settings, NULL, &proxy); scoped_refptr layerTreeRoot = Layer::Create(); layerTreeRoot->SetLayerAnimationController(FakeLayerAnimationController::Create()); EXPECT_FALSE(static_cast(layerTreeRoot->layer_animation_controller())->synchronizedAnimations()); scoped_ptr layerImplTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), scoped_ptr(), m_hostImpl.active_tree()); TreeSynchronizer::pushProperties(layerTreeRoot.get(), layerImplTreeRoot.get()); layerImplTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), layerImplTreeRoot.Pass(), m_hostImpl.active_tree()); EXPECT_TRUE(static_cast(layerTreeRoot->layer_animation_controller())->synchronizedAnimations()); } } // namespace } // namespace cc