Index: cc/trees/layer_tree_host_unittest_animation_timelines.cc |
diff --git a/cc/trees/layer_tree_host_unittest_animation_timelines.cc b/cc/trees/layer_tree_host_unittest_animation_timelines.cc |
index b3b4a6e799985c54e54428317e6f153e6d40030e..353840d05e18bf72bf183e8eeebbd805ac3768dd 100644 |
--- a/cc/trees/layer_tree_host_unittest_animation_timelines.cc |
+++ b/cc/trees/layer_tree_host_unittest_animation_timelines.cc |
@@ -5,6 +5,9 @@ |
#include "cc/trees/layer_tree_host.h" |
#include "cc/animation/animation_curve.h" |
+#include "cc/animation/animation_host.h" |
+#include "cc/animation/animation_player.h" |
+#include "cc/animation/animation_timeline.h" |
#include "cc/animation/layer_animation_controller.h" |
#include "cc/animation/scroll_offset_animation_curve.h" |
#include "cc/animation/timing_function.h" |
@@ -18,5 +21,723 @@ |
#include "cc/trees/layer_tree_impl.h" |
namespace cc { |
-namespace {} // namespace |
+namespace { |
+ |
+class LayerTreeHostTimelinesTest : public LayerTreeTest { |
+ public: |
+ LayerTreeHostTimelinesTest() |
+ : timeline_id_(1), player_id_(2), player_child_id_(3) { |
+ timeline_ = AnimationTimeline::Create(timeline_id_); |
+ player_ = AnimationPlayer::Create(player_id_); |
+ player_child_ = AnimationPlayer::Create(player_child_id_); |
+ |
+ player_->set_layer_animation_delegate(this); |
+ } |
+ |
+ void InitializeSettings(LayerTreeSettings* settings) override { |
+ settings->use_compositor_animation_timelines = true; |
+ } |
+ |
+ void SetupTree() override { LayerTreeTest::SetupTree(); } |
+ |
+ void AttachPlayersToTimeline() { |
+ layer_tree_host()->animation_host()->AddAnimationTimeline(timeline_.get()); |
+ timeline_->AttachPlayer(player_.get()); |
+ timeline_->AttachPlayer(player_child_.get()); |
+ } |
+ |
+ protected: |
+ scoped_refptr<AnimationTimeline> timeline_; |
+ scoped_refptr<AnimationPlayer> player_; |
+ scoped_refptr<AnimationPlayer> player_child_; |
+ |
+ const int timeline_id_; |
+ const int player_id_; |
+ const int player_child_id_; |
+}; |
+ |
+// Add a layer animation and confirm that |
+// LayerTreeHostImpl::UpdateAnimationState does get called. |
+// Evolved frome LayerTreeHostAnimationTestAddAnimation |
+class LayerTreeHostTimelinesTestAddAnimation |
+ : public LayerTreeHostTimelinesTest { |
+ public: |
+ LayerTreeHostTimelinesTestAddAnimation() |
+ : update_animation_state_was_called_(false) {} |
+ |
+ void BeginTest() override { |
+ AttachPlayersToTimeline(); |
+ player_->AttachLayer(layer_tree_host()->root_layer()->id()); |
+ PostAddInstantAnimationToMainThreadPlayer(player_.get()); |
+ } |
+ |
+ void UpdateAnimationState(LayerTreeHostImpl* host_impl, |
+ bool has_unfinished_animation) override { |
+ EXPECT_FALSE(has_unfinished_animation); |
+ update_animation_state_was_called_ = true; |
+ } |
+ |
+ void NotifyAnimationStarted(base::TimeTicks monotonic_time, |
+ Animation::TargetProperty target_property, |
+ int group) override { |
+ EXPECT_LT(base::TimeTicks(), monotonic_time); |
+ |
+ LayerAnimationController* controller = |
+ player_->layer_animation_controller(); |
+ Animation* animation = controller->GetAnimation(Animation::OPACITY); |
+ if (animation) |
+ player_->RemoveAnimation(animation->id()); |
+ |
+ EndTest(); |
+ } |
+ |
+ void AfterTest() override { EXPECT_TRUE(update_animation_state_was_called_); } |
+ |
+ private: |
+ bool update_animation_state_was_called_; |
+}; |
+ |
+SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTimelinesTestAddAnimation); |
+ |
+// Add a layer animation to a layer, but continually fail to draw. Confirm that |
+// after a while, we do eventually force a draw. |
+// Evolved from LayerTreeHostAnimationTestCheckerboardDoesNotStarveDraws. |
+class LayerTreeHostTimelinesTestCheckerboardDoesNotStarveDraws |
+ : public LayerTreeHostTimelinesTest { |
+ public: |
+ LayerTreeHostTimelinesTestCheckerboardDoesNotStarveDraws() |
+ : started_animating_(false) {} |
+ |
+ void BeginTest() override { |
+ AttachPlayersToTimeline(); |
+ player_->AttachLayer(layer_tree_host()->root_layer()->id()); |
+ PostAddAnimationToMainThreadPlayer(player_.get()); |
+ } |
+ |
+ void AnimateLayers(LayerTreeHostImpl* host_impl, |
+ base::TimeTicks monotonic_time) override { |
+ started_animating_ = true; |
+ } |
+ |
+ void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override { |
+ if (started_animating_) |
+ EndTest(); |
+ } |
+ |
+ DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl, |
+ LayerTreeHostImpl::FrameData* frame, |
+ DrawResult draw_result) override { |
+ return DRAW_ABORTED_CHECKERBOARD_ANIMATIONS; |
+ } |
+ |
+ void AfterTest() override {} |
+ |
+ private: |
+ bool started_animating_; |
+}; |
+ |
+// Starvation can only be an issue with the MT compositor. |
+MULTI_THREAD_TEST_F(LayerTreeHostTimelinesTestCheckerboardDoesNotStarveDraws); |
+ |
+// Ensures that animations eventually get deleted. |
+// Evolved from LayerTreeHostAnimationTestAnimationsGetDeleted. |
+class LayerTreeHostTimelinesTestAnimationsGetDeleted |
+ : public LayerTreeHostTimelinesTest { |
+ public: |
+ LayerTreeHostTimelinesTestAnimationsGetDeleted() |
+ : started_animating_(false) {} |
+ |
+ void BeginTest() override { |
+ AttachPlayersToTimeline(); |
+ player_->AttachLayer(layer_tree_host()->root_layer()->id()); |
+ PostAddAnimationToMainThreadPlayer(player_.get()); |
+ } |
+ |
+ void AnimateLayers(LayerTreeHostImpl* host_impl, |
+ base::TimeTicks monotonic_time) override { |
+ bool have_animations = !host_impl->animation_host() |
+ ->animation_registrar() |
+ ->active_animation_controllers_for_testing() |
+ .empty(); |
+ if (!started_animating_ && have_animations) { |
+ started_animating_ = true; |
+ return; |
+ } |
+ |
+ if (started_animating_ && !have_animations) |
+ EndTest(); |
+ } |
+ |
+ void NotifyAnimationFinished(base::TimeTicks monotonic_time, |
+ Animation::TargetProperty target_property, |
+ int group) override { |
+ // Animations on the impl-side controller only get deleted during a commit, |
+ // so we need to schedule a commit. |
+ layer_tree_host()->SetNeedsCommit(); |
+ } |
+ |
+ void AfterTest() override {} |
+ |
+ private: |
+ bool started_animating_; |
+}; |
+ |
+SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTimelinesTestAnimationsGetDeleted); |
+ |
+// Ensure that an animation's timing function is respected. |
+// Evolved from LayerTreeHostAnimationTestAddAnimationWithTimingFunction. |
+class LayerTreeHostTimelinesTestAddAnimationWithTimingFunction |
+ : public LayerTreeHostTimelinesTest { |
+ public: |
+ LayerTreeHostTimelinesTestAddAnimationWithTimingFunction() {} |
+ |
+ void SetupTree() override { |
+ LayerTreeHostTimelinesTest::SetupTree(); |
+ content_ = FakeContentLayer::Create(&client_); |
+ content_->SetBounds(gfx::Size(4, 4)); |
+ layer_tree_host()->root_layer()->AddChild(content_); |
+ |
+ AttachPlayersToTimeline(); |
+ player_child_->AttachLayer(content_->id()); |
+ } |
+ |
+ void BeginTest() override { |
+ PostAddAnimationToMainThreadPlayer(player_child_.get()); |
+ } |
+ |
+ void AnimateLayers(LayerTreeHostImpl* host_impl, |
+ base::TimeTicks monotonic_time) override { |
+ scoped_refptr<AnimationTimeline> timeline_impl = |
+ host_impl->animation_host()->GetTimelineById(timeline_id_); |
+ scoped_refptr<AnimationPlayer> player_child_impl = |
+ timeline_impl->GetPlayerById(player_child_id_); |
+ |
+ LayerAnimationController* controller_impl = |
+ player_child_impl->layer_animation_controller(); |
+ if (!controller_impl) |
+ return; |
+ |
+ Animation* animation = controller_impl->GetAnimation(Animation::OPACITY); |
+ if (!animation) |
+ return; |
+ |
+ const FloatAnimationCurve* curve = |
+ animation->curve()->ToFloatAnimationCurve(); |
+ float start_opacity = curve->GetValue(base::TimeDelta()); |
+ float end_opacity = curve->GetValue(curve->Duration()); |
+ float linearly_interpolated_opacity = |
+ 0.25f * end_opacity + 0.75f * start_opacity; |
+ base::TimeDelta time = TimeUtil::Scale(curve->Duration(), 0.25f); |
+ // If the linear timing function associated with this animation was not |
+ // picked up, then the linearly interpolated opacity would be different |
+ // because of the default ease timing function. |
+ EXPECT_FLOAT_EQ(linearly_interpolated_opacity, curve->GetValue(time)); |
+ |
+ EndTest(); |
+ } |
+ |
+ void AfterTest() override {} |
+ |
+ FakeContentLayerClient client_; |
+ scoped_refptr<FakeContentLayer> content_; |
+}; |
+ |
+SINGLE_AND_MULTI_THREAD_TEST_F( |
+ LayerTreeHostTimelinesTestAddAnimationWithTimingFunction); |
+ |
+// Ensures that main thread animations have their start times synchronized with |
+// impl thread animations. |
+// Evolved from LayerTreeHostAnimationTestSynchronizeAnimationStartTimes. |
+class LayerTreeHostTimelinesTestSynchronizeAnimationStartTimes |
+ : public LayerTreeHostTimelinesTest { |
+ public: |
+ LayerTreeHostTimelinesTestSynchronizeAnimationStartTimes() {} |
+ |
+ void SetupTree() override { |
+ LayerTreeHostTimelinesTest::SetupTree(); |
+ content_ = FakeContentLayer::Create(&client_); |
+ content_->SetBounds(gfx::Size(4, 4)); |
+ |
+ layer_tree_host()->root_layer()->AddChild(content_); |
+ |
+ AttachPlayersToTimeline(); |
+ player_child_->set_layer_animation_delegate(this); |
+ player_child_->AttachLayer(content_->id()); |
+ } |
+ |
+ void BeginTest() override { |
+ PostAddAnimationToMainThreadPlayer(player_child_.get()); |
+ } |
+ |
+ void NotifyAnimationStarted(base::TimeTicks monotonic_time, |
+ Animation::TargetProperty target_property, |
+ int group) override { |
+ LayerAnimationController* controller = |
+ player_child_->layer_animation_controller(); |
+ Animation* animation = controller->GetAnimation(Animation::OPACITY); |
+ main_start_time_ = animation->start_time(); |
+ controller->RemoveAnimation(animation->id()); |
+ EndTest(); |
+ } |
+ |
+ void UpdateAnimationState(LayerTreeHostImpl* impl_host, |
+ bool has_unfinished_animation) override { |
+ scoped_refptr<AnimationTimeline> timeline_impl = |
+ impl_host->animation_host()->GetTimelineById(timeline_id_); |
+ scoped_refptr<AnimationPlayer> player_child_impl = |
+ timeline_impl->GetPlayerById(player_child_id_); |
+ |
+ LayerAnimationController* controller = |
+ player_child_impl->layer_animation_controller(); |
+ Animation* animation = controller->GetAnimation(Animation::OPACITY); |
+ if (!animation) |
+ return; |
+ |
+ impl_start_time_ = animation->start_time(); |
+ } |
+ |
+ void AfterTest() override { |
+ EXPECT_EQ(impl_start_time_, main_start_time_); |
+ EXPECT_LT(base::TimeTicks(), impl_start_time_); |
+ } |
+ |
+ private: |
+ base::TimeTicks main_start_time_; |
+ base::TimeTicks impl_start_time_; |
+ FakeContentLayerClient client_; |
+ scoped_refptr<FakeContentLayer> content_; |
+}; |
+ |
+SINGLE_AND_MULTI_THREAD_TEST_F( |
+ LayerTreeHostTimelinesTestSynchronizeAnimationStartTimes); |
+ |
+// Ensures that notify animation finished is called. |
+// Evolved from LayerTreeHostAnimationTestAnimationFinishedEvents. |
+class LayerTreeHostTimelinesTestAnimationFinishedEvents |
+ : public LayerTreeHostTimelinesTest { |
+ public: |
+ LayerTreeHostTimelinesTestAnimationFinishedEvents() {} |
+ |
+ void BeginTest() override { |
+ AttachPlayersToTimeline(); |
+ player_->AttachLayer(layer_tree_host()->root_layer()->id()); |
+ PostAddInstantAnimationToMainThreadPlayer(player_.get()); |
+ } |
+ |
+ void NotifyAnimationFinished(base::TimeTicks monotonic_time, |
+ Animation::TargetProperty target_property, |
+ int group) override { |
+ LayerAnimationController* controller = |
+ player_->layer_animation_controller(); |
+ Animation* animation = controller->GetAnimation(Animation::OPACITY); |
+ if (animation) |
+ controller->RemoveAnimation(animation->id()); |
+ EndTest(); |
+ } |
+ |
+ void AfterTest() override {} |
+}; |
+ |
+SINGLE_AND_MULTI_THREAD_TEST_F( |
+ LayerTreeHostTimelinesTestAnimationFinishedEvents); |
+ |
+// Ensures that when opacity is being animated, this value does not cause the |
+// subtree to be skipped. |
+// Evolved from LayerTreeHostAnimationTestDoNotSkipLayersWithAnimatedOpacity. |
+class LayerTreeHostTimelinesTestDoNotSkipLayersWithAnimatedOpacity |
+ : public LayerTreeHostTimelinesTest { |
+ public: |
+ LayerTreeHostTimelinesTestDoNotSkipLayersWithAnimatedOpacity() |
+ : update_check_layer_(FakeContentLayer::Create(&client_)) {} |
+ |
+ void SetupTree() override { |
+ update_check_layer_->SetOpacity(0.f); |
+ layer_tree_host()->SetRootLayer(update_check_layer_); |
+ LayerTreeHostTimelinesTest::SetupTree(); |
+ |
+ AttachPlayersToTimeline(); |
+ player_->AttachLayer(update_check_layer_->id()); |
+ } |
+ |
+ void BeginTest() override { |
+ PostAddAnimationToMainThreadPlayer(player_.get()); |
+ } |
+ |
+ void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override { |
+ scoped_refptr<AnimationTimeline> timeline_impl = |
+ host_impl->animation_host()->GetTimelineById(timeline_id_); |
+ scoped_refptr<AnimationPlayer> player_impl = |
+ timeline_impl->GetPlayerById(player_id_); |
+ |
+ LayerAnimationController* controller_impl = |
+ player_impl->layer_animation_controller(); |
+ Animation* animation_impl = |
+ controller_impl->GetAnimation(Animation::OPACITY); |
+ controller_impl->RemoveAnimation(animation_impl->id()); |
+ EndTest(); |
+ } |
+ |
+ void AfterTest() override { |
+ // Update() should have been called once, proving that the layer was not |
+ // skipped. |
+ EXPECT_EQ(1u, update_check_layer_->update_count()); |
+ |
+ // clear update_check_layer_ so LayerTreeHost dies. |
+ update_check_layer_ = NULL; |
+ } |
+ |
+ private: |
+ FakeContentLayerClient client_; |
+ scoped_refptr<FakeContentLayer> update_check_layer_; |
+}; |
+ |
+SINGLE_AND_MULTI_THREAD_TEST_F( |
+ LayerTreeHostTimelinesTestDoNotSkipLayersWithAnimatedOpacity); |
+ |
+// Layers added to tree with existing active animations should have the |
+// animation correctly recognized. |
+// Evolved from LayerTreeHostAnimationTestLayerAddedWithAnimation. |
+class LayerTreeHostTimelinesTestLayerAddedWithAnimation |
+ : public LayerTreeHostTimelinesTest { |
+ public: |
+ LayerTreeHostTimelinesTestLayerAddedWithAnimation() {} |
+ |
+ void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
+ |
+ void DidCommit() override { |
+ if (layer_tree_host()->source_frame_number() == 1) { |
+ AttachPlayersToTimeline(); |
+ |
+ scoped_refptr<Layer> layer = Layer::Create(); |
+ player_->AttachLayer(layer->id()); |
+ player_->set_layer_animation_delegate(this); |
+ |
+ // Any valid AnimationCurve will do here. |
+ scoped_ptr<AnimationCurve> curve(new FakeFloatAnimationCurve()); |
+ scoped_ptr<Animation> animation( |
+ Animation::Create(curve.Pass(), 1, 1, Animation::OPACITY)); |
+ player_->AddAnimation(animation.Pass()); |
+ |
+ // We add the animation *before* attaching the layer to the tree. |
+ layer_tree_host()->root_layer()->AddChild(layer); |
+ } |
+ } |
+ |
+ void AnimateLayers(LayerTreeHostImpl* impl_host, |
+ base::TimeTicks monotonic_time) override { |
+ EndTest(); |
+ } |
+ |
+ void AfterTest() override {} |
+}; |
+ |
+SINGLE_AND_MULTI_THREAD_TEST_F( |
+ LayerTreeHostTimelinesTestLayerAddedWithAnimation); |
+ |
+// Make sure the main thread can still execute animations when CanDraw() is not |
+// true. |
+// Evolved from LayerTreeHostAnimationTestRunAnimationWhenNotCanDraw |
+class LayerTreeHostTimelinesTestRunAnimationWhenNotCanDraw |
+ : public LayerTreeHostTimelinesTest { |
+ public: |
+ LayerTreeHostTimelinesTestRunAnimationWhenNotCanDraw() : started_times_(0) {} |
+ |
+ void SetupTree() override { |
+ LayerTreeHostTimelinesTest::SetupTree(); |
+ content_ = FakeContentLayer::Create(&client_); |
+ content_->SetBounds(gfx::Size(4, 4)); |
+ layer_tree_host()->root_layer()->AddChild(content_); |
+ |
+ AttachPlayersToTimeline(); |
+ player_child_->AttachLayer(content_->id()); |
+ player_child_->set_layer_animation_delegate(this); |
+ } |
+ |
+ void BeginTest() override { |
+ layer_tree_host()->SetViewportSize(gfx::Size()); |
+ PostAddAnimationToMainThreadPlayer(player_child_.get()); |
+ } |
+ |
+ void NotifyAnimationStarted(base::TimeTicks monotonic_time, |
+ Animation::TargetProperty target_property, |
+ int group) override { |
+ started_times_++; |
+ } |
+ |
+ void NotifyAnimationFinished(base::TimeTicks monotonic_time, |
+ Animation::TargetProperty target_property, |
+ int group) override { |
+ EndTest(); |
+ } |
+ |
+ void AfterTest() override { EXPECT_EQ(1, started_times_); } |
+ |
+ private: |
+ int started_times_; |
+ FakeContentLayerClient client_; |
+ scoped_refptr<FakeContentLayer> content_; |
+}; |
+ |
+SINGLE_AND_MULTI_THREAD_TEST_F( |
+ LayerTreeHostTimelinesTestRunAnimationWhenNotCanDraw); |
+ |
+// Animations should not be started when frames are being skipped due to |
+// checkerboard. |
+// Evolved from LayerTreeHostAnimationTestCheckerboardDoesntStartAnimations. |
+class LayerTreeHostTimelinesTestCheckerboardDoesntStartAnimations |
+ : public LayerTreeHostTimelinesTest { |
+ void SetupTree() override { |
+ LayerTreeHostTimelinesTest::SetupTree(); |
+ content_ = FakeContentLayer::Create(&client_); |
+ content_->SetBounds(gfx::Size(4, 4)); |
+ layer_tree_host()->root_layer()->AddChild(content_); |
+ |
+ AttachPlayersToTimeline(); |
+ player_child_->AttachLayer(content_->id()); |
+ player_child_->set_layer_animation_delegate(this); |
+ } |
+ |
+ void InitializeSettings(LayerTreeSettings* settings) override { |
+ // Make sure that drawing many times doesn't cause a checkerboarded |
+ // animation to start so we avoid flake in this test. |
+ settings->timeout_and_draw_when_animation_checkerboards = false; |
+ LayerTreeHostTimelinesTest::InitializeSettings(settings); |
+ } |
+ |
+ void BeginTest() override { |
+ prevented_draw_ = 0; |
+ added_animations_ = 0; |
+ started_times_ = 0; |
+ |
+ PostSetNeedsCommitToMainThread(); |
+ } |
+ |
+ DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl, |
+ LayerTreeHostImpl::FrameData* frame_data, |
+ DrawResult draw_result) override { |
+ if (added_animations_ < 2) |
+ return draw_result; |
+ if (TestEnded()) |
+ return draw_result; |
+ // Act like there is checkerboard when the second animation wants to draw. |
+ ++prevented_draw_; |
+ if (prevented_draw_ > 2) |
+ EndTest(); |
+ return DRAW_ABORTED_CHECKERBOARD_ANIMATIONS; |
+ } |
+ |
+ void DidCommitAndDrawFrame() override { |
+ switch (layer_tree_host()->source_frame_number()) { |
+ case 1: |
+ // The animation is longer than 1 BeginFrame interval. |
+ AddOpacityTransitionToPlayer(player_child_.get(), 0.1, 0.2f, 0.8f, |
+ false); |
+ added_animations_++; |
+ break; |
+ case 2: |
+ // This second animation will not be drawn so it should not start. |
+ AddAnimatedTransformToPlayer(player_child_.get(), 0.1, 5, 5); |
+ added_animations_++; |
+ break; |
+ } |
+ } |
+ |
+ void NotifyAnimationStarted(base::TimeTicks monotonic_time, |
+ Animation::TargetProperty target_property, |
+ int group) override { |
+ if (TestEnded()) |
+ return; |
+ started_times_++; |
+ } |
+ |
+ void AfterTest() override { |
+ // Make sure we tried to draw the second animation but failed. |
+ EXPECT_LT(0, prevented_draw_); |
+ // The first animation should be started, but the second should not because |
+ // of checkerboard. |
+ EXPECT_EQ(1, started_times_); |
+ } |
+ |
+ int prevented_draw_; |
+ int added_animations_; |
+ int started_times_; |
+ FakeContentLayerClient client_; |
+ scoped_refptr<FakeContentLayer> content_; |
+}; |
+ |
+MULTI_THREAD_TEST_F( |
+ LayerTreeHostTimelinesTestCheckerboardDoesntStartAnimations); |
+ |
+// When animations are simultaneously added to an existing layer and to a new |
+// layer, they should start at the same time, even when there's already a |
+// running animation on the existing layer. |
+// Evolved from LayerTreeHostAnimationTestAnimationsAddedToNewAndExistingLayers. |
+class LayerTreeHostTimelinesTestAnimationsAddedToNewAndExistingLayers |
+ : public LayerTreeHostTimelinesTest { |
+ public: |
+ LayerTreeHostTimelinesTestAnimationsAddedToNewAndExistingLayers() |
+ : frame_count_with_pending_tree_(0) {} |
+ |
+ void BeginTest() override { |
+ AttachPlayersToTimeline(); |
+ PostSetNeedsCommitToMainThread(); |
+ } |
+ |
+ void DidCommit() override { |
+ if (layer_tree_host()->source_frame_number() == 1) { |
+ player_->AttachLayer(layer_tree_host()->root_layer()->id()); |
+ AddAnimatedTransformToPlayer(player_.get(), 4, 1, 1); |
+ } else if (layer_tree_host()->source_frame_number() == 2) { |
+ AddOpacityTransitionToPlayer(player_.get(), 1, 0.f, 0.5f, true); |
+ |
+ scoped_refptr<Layer> layer = Layer::Create(); |
+ layer_tree_host()->root_layer()->AddChild(layer); |
+ layer->set_layer_animation_delegate(this); |
+ layer->SetBounds(gfx::Size(4, 4)); |
+ |
+ player_child_->AttachLayer(layer->id()); |
+ AddOpacityTransitionToPlayer(player_child_.get(), 1, 0.f, 0.5f, true); |
+ } |
+ } |
+ |
+ void BeginCommitOnThread(LayerTreeHostImpl* host_impl) override { |
+ if (host_impl->settings().impl_side_painting) |
+ host_impl->BlockNotifyReadyToActivateForTesting(true); |
+ } |
+ |
+ void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override { |
+ // For the commit that added animations to new and existing layers, keep |
+ // blocking activation. We want to verify that even with activation blocked, |
+ // the animation on the layer that's already in the active tree won't get a |
+ // head start. |
+ if (host_impl->settings().impl_side_painting && |
+ host_impl->pending_tree()->source_frame_number() != 2) { |
+ host_impl->BlockNotifyReadyToActivateForTesting(false); |
+ } |
+ } |
+ |
+ void WillBeginImplFrameOnThread(LayerTreeHostImpl* host_impl, |
+ const BeginFrameArgs& args) override { |
+ if (!host_impl->pending_tree() || |
+ host_impl->pending_tree()->source_frame_number() != 2) |
+ return; |
+ |
+ frame_count_with_pending_tree_++; |
+ if (frame_count_with_pending_tree_ == 2 && |
+ host_impl->settings().impl_side_painting) { |
+ host_impl->BlockNotifyReadyToActivateForTesting(false); |
+ } |
+ } |
+ |
+ void UpdateAnimationState(LayerTreeHostImpl* host_impl, |
+ bool has_unfinished_animation) override { |
+ scoped_refptr<AnimationTimeline> timeline_impl = |
+ host_impl->animation_host()->GetTimelineById(timeline_id_); |
+ scoped_refptr<AnimationPlayer> player_impl = |
+ timeline_impl->GetPlayerById(player_id_); |
+ scoped_refptr<AnimationPlayer> player_child_impl = |
+ timeline_impl->GetPlayerById(player_child_id_); |
+ |
+ // wait for tree activation. |
+ if (!player_impl->layer_animation_controller()) |
+ return; |
+ |
+ LayerAnimationController* root_controller_impl = |
+ player_impl->layer_animation_controller(); |
+ Animation* root_animation = |
+ root_controller_impl->GetAnimation(Animation::OPACITY); |
+ if (!root_animation || root_animation->run_state() != Animation::RUNNING) |
+ return; |
+ |
+ LayerAnimationController* child_controller_impl = |
+ player_child_impl->layer_animation_controller(); |
+ Animation* child_animation = |
+ child_controller_impl->GetAnimation(Animation::OPACITY); |
+ EXPECT_EQ(Animation::RUNNING, child_animation->run_state()); |
+ EXPECT_EQ(root_animation->start_time(), child_animation->start_time()); |
+ root_controller_impl->AbortAnimations(Animation::OPACITY); |
+ root_controller_impl->AbortAnimations(Animation::TRANSFORM); |
+ child_controller_impl->AbortAnimations(Animation::OPACITY); |
+ EndTest(); |
+ } |
+ |
+ void AfterTest() override {} |
+ |
+ private: |
+ int frame_count_with_pending_tree_; |
+}; |
+ |
+SINGLE_AND_MULTI_THREAD_BLOCKNOTIFY_TEST_F( |
+ LayerTreeHostTimelinesTestAnimationsAddedToNewAndExistingLayers); |
+ |
+// Evolved from LayerTreeHostAnimationTestAddAnimationAfterAnimating. |
+class LayerTreeHostTimelinesTestAddAnimationAfterAnimating |
+ : public LayerTreeHostTimelinesTest { |
+ public: |
+ LayerTreeHostTimelinesTestAddAnimationAfterAnimating() |
+ : num_swap_buffers_(0) {} |
+ |
+ void SetupTree() override { |
+ LayerTreeHostTimelinesTest::SetupTree(); |
+ content_ = Layer::Create(); |
+ content_->SetBounds(gfx::Size(4, 4)); |
+ layer_tree_host()->root_layer()->AddChild(content_); |
+ |
+ AttachPlayersToTimeline(); |
+ |
+ player_->AttachLayer(layer_tree_host()->root_layer()->id()); |
+ player_child_->AttachLayer(content_->id()); |
+ } |
+ |
+ void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
+ |
+ void DidCommit() override { |
+ switch (layer_tree_host()->source_frame_number()) { |
+ case 1: |
+ // First frame: add an animation to the root layer. |
+ AddAnimatedTransformToPlayer(player_.get(), 0.1, 5, 5); |
+ break; |
+ case 2: |
+ // Second frame: add an animation to the content layer. The root layer |
+ // animation has caused us to animate already during this frame. |
+ AddOpacityTransitionToPlayer(player_child_.get(), 0.1, 5, 5, false); |
+ break; |
+ } |
+ } |
+ |
+ void SwapBuffersOnThread(LayerTreeHostImpl* host_impl, bool result) override { |
+ // After both animations have started, verify that they have valid |
+ // start times. |
+ num_swap_buffers_++; |
+ AnimationRegistrar::AnimationControllerMap controllers_copy = |
+ host_impl->animation_host() |
+ ->animation_registrar() |
+ ->active_animation_controllers_for_testing(); |
+ if (controllers_copy.size() == 2u) { |
+ EndTest(); |
+ EXPECT_GE(num_swap_buffers_, 3); |
+ for (auto& it : controllers_copy) { |
+ int id = it.first; |
+ if (id == host_impl->RootLayer()->id()) { |
+ Animation* anim = it.second->GetAnimation(Animation::TRANSFORM); |
+ EXPECT_GT((anim->start_time() - base::TimeTicks()).InSecondsF(), 0); |
+ } else if (id == host_impl->RootLayer()->children()[0]->id()) { |
+ Animation* anim = it.second->GetAnimation(Animation::OPACITY); |
+ EXPECT_GT((anim->start_time() - base::TimeTicks()).InSecondsF(), 0); |
+ } |
+ } |
+ } |
+ } |
+ |
+ void AfterTest() override {} |
+ |
+ private: |
+ scoped_refptr<Layer> content_; |
+ int num_swap_buffers_; |
+}; |
+ |
+SINGLE_AND_MULTI_THREAD_TEST_F( |
+ LayerTreeHostTimelinesTestAddAnimationAfterAnimating); |
+ |
+} // namespace |
} // namespace cc |