// 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, m_layerId).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->layerTreeImpl(), treeImpl); EXPECT_EQ(layer->nonFastScrollableRegion(), layerImpl->nonFastScrollableRegion()); ASSERT_EQ(!!layer->maskLayer(), !!layerImpl->maskLayer()); if (layer->maskLayer()) expectTreesAreIdentical(layer->maskLayer(), layerImpl->maskLayer(), treeImpl); ASSERT_EQ(!!layer->replicaLayer(), !!layerImpl->replicaLayer()); if (layer->replicaLayer()) expectTreesAreIdentical(layer->replicaLayer(), layerImpl->replicaLayer(), 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.activeTree()); 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.activeTree()); expectTreesAreIdentical(layerTreeRoot.get(), layerImplTreeRoot.get(), m_hostImpl.activeTree()); } // 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.activeTree()); expectTreesAreIdentical(layerTreeRoot.get(), layerImplTreeRoot.get(), m_hostImpl.activeTree()); // 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.activeTree()); expectTreesAreIdentical(layerTreeRoot.get(), layerImplTreeRoot.get(), m_hostImpl.activeTree()); 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.activeTree()); expectTreesAreIdentical(layerTreeRoot.get(), layerImplTreeRoot.get(), m_hostImpl.activeTree()); // 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.activeTree()); expectTreesAreIdentical(layerTreeRoot.get(), layerImplTreeRoot.get(), m_hostImpl.activeTree()); 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.activeTree()); expectTreesAreIdentical(layerTreeRoot.get(), layerImplTreeRoot.get(), m_hostImpl.activeTree()); 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.activeTree()); expectTreesAreIdentical(layerTreeRoot.get(), layerImplTreeRoot.get(), m_hostImpl.activeTree()); // 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.activeTree()); expectTreesAreIdentical(layerTreeRoot.get(), layerImplTreeRoot.get(), m_hostImpl.activeTree()); 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.activeTree()); expectTreesAreIdentical(oldLayerTreeRoot.get(), layerImplTreeRoot.get(), m_hostImpl.activeTree()); // 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.activeTree()); expectTreesAreIdentical(newLayerTreeRoot.get(), layerImplTreeRoot.get(), m_hostImpl.activeTree()); 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.activeTree()); expectTreesAreIdentical(layerTreeRoot.get(), layerImplTreeRoot.get(), m_hostImpl.activeTree()); // Remove the mask layer. layerTreeRoot->children()[0]->setMaskLayer(0); layerImplTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), layerImplTreeRoot.Pass(), m_hostImpl.activeTree()); expectTreesAreIdentical(layerTreeRoot.get(), layerImplTreeRoot.get(), m_hostImpl.activeTree()); // Remove the replica layer. layerTreeRoot->children()[1]->setReplicaLayer(0); layerImplTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), layerImplTreeRoot.Pass(), m_hostImpl.activeTree()); expectTreesAreIdentical(layerTreeRoot.get(), layerImplTreeRoot.get(), m_hostImpl.activeTree()); // Remove the replica mask. replicaLayerWithMask->setMaskLayer(0); layerImplTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), layerImplTreeRoot.Pass(), m_hostImpl.activeTree()); expectTreesAreIdentical(layerTreeRoot.get(), layerImplTreeRoot.get(), m_hostImpl.activeTree()); } TEST_F(TreeSynchronizerTest, synchronizeAnimations) { LayerTreeSettings settings; FakeProxy proxy(scoped_ptr(NULL)); DebugScopedSetImplThread impl(&proxy); scoped_ptr hostImpl = LayerTreeHostImpl::create(settings, 0, &proxy); scoped_refptr layerTreeRoot = Layer::create(); layerTreeRoot->setLayerAnimationController(FakeLayerAnimationController::create()); EXPECT_FALSE(static_cast(layerTreeRoot->layerAnimationController())->synchronizedAnimations()); scoped_ptr layerImplTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), scoped_ptr(), m_hostImpl.activeTree()); TreeSynchronizer::pushProperties(layerTreeRoot.get(), layerImplTreeRoot.get()); layerImplTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), layerImplTreeRoot.Pass(), m_hostImpl.activeTree()); EXPECT_TRUE(static_cast(layerTreeRoot->layerAnimationController())->synchronizedAnimations()); } } // namespace } // namespace cc