| // Copyright 2012 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 <stdint.h> |
| |
| #include "cc/animation/animation_curve.h" |
| #include "cc/animation/animation_host.h" |
| #include "cc/animation/animation_id_provider.h" |
| #include "cc/animation/animation_player.h" |
| #include "cc/animation/animation_timeline.h" |
| #include "cc/animation/element_animations.h" |
| #include "cc/animation/scroll_offset_animation_curve.h" |
| #include "cc/animation/scroll_offset_animations.h" |
| #include "cc/animation/timing_function.h" |
| #include "cc/animation/transform_operations.h" |
| #include "cc/base/completion_event.h" |
| #include "cc/base/time_util.h" |
| #include "cc/layers/layer.h" |
| #include "cc/layers/layer_impl.h" |
| #include "cc/test/animation_test_common.h" |
| #include "cc/test/fake_content_layer_client.h" |
| #include "cc/test/fake_picture_layer.h" |
| #include "cc/test/layer_tree_test.h" |
| #include "cc/trees/layer_tree_impl.h" |
| |
| namespace cc { |
| namespace { |
| |
| class LayerTreeHostAnimationTest : public LayerTreeTest { |
| public: |
| LayerTreeHostAnimationTest() |
| : timeline_id_(AnimationIdProvider::NextTimelineId()), |
| player_id_(AnimationIdProvider::NextPlayerId()), |
| player_child_id_(AnimationIdProvider::NextPlayerId()) { |
| timeline_ = AnimationTimeline::Create(timeline_id_); |
| player_ = AnimationPlayer::Create(player_id_); |
| player_child_ = AnimationPlayer::Create(player_child_id_); |
| |
| player_->set_animation_delegate(this); |
| } |
| |
| void AttachPlayersToTimeline() { |
| layer_tree_host()->animation_host()->AddAnimationTimeline(timeline_.get()); |
| layer_tree_host()->SetElementIdsForTesting(); |
| 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_; |
| }; |
| |
| // Makes sure that SetNeedsAnimate does not cause the CommitRequested() state to |
| // be set. |
| class LayerTreeHostAnimationTestSetNeedsAnimateShouldNotSetCommitRequested |
| : public LayerTreeHostAnimationTest { |
| public: |
| LayerTreeHostAnimationTestSetNeedsAnimateShouldNotSetCommitRequested() |
| : num_commits_(0) {} |
| |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void BeginMainFrame(const BeginFrameArgs& args) override { |
| // We skip the first commit because its the commit that populates the |
| // impl thread with a tree. After the second commit, the test is done. |
| if (num_commits_ != 1) |
| return; |
| |
| layer_tree_host()->SetNeedsAnimate(); |
| // Right now, CommitRequested is going to be true, because during |
| // BeginFrame, we force CommitRequested to true to prevent requests from |
| // hitting the impl thread. But, when the next DidCommit happens, we should |
| // verify that CommitRequested has gone back to false. |
| } |
| |
| void DidCommit() override { |
| if (!num_commits_) { |
| EXPECT_FALSE(layer_tree_host()->CommitRequested()); |
| layer_tree_host()->SetNeedsAnimate(); |
| EXPECT_FALSE(layer_tree_host()->CommitRequested()); |
| } |
| |
| // Verifies that the SetNeedsAnimate we made in ::Animate did not |
| // trigger CommitRequested. |
| EXPECT_FALSE(layer_tree_host()->CommitRequested()); |
| EndTest(); |
| num_commits_++; |
| } |
| |
| void AfterTest() override {} |
| |
| private: |
| int num_commits_; |
| }; |
| |
| MULTI_THREAD_TEST_F( |
| LayerTreeHostAnimationTestSetNeedsAnimateShouldNotSetCommitRequested); |
| |
| // Trigger a frame with SetNeedsCommit. Then, inside the resulting animate |
| // callback, request another frame using SetNeedsAnimate. End the test when |
| // animate gets called yet-again, indicating that the proxy is correctly |
| // handling the case where SetNeedsAnimate() is called inside the BeginFrame |
| // flow. |
| class LayerTreeHostAnimationTestSetNeedsAnimateInsideAnimationCallback |
| : public LayerTreeHostAnimationTest { |
| public: |
| LayerTreeHostAnimationTestSetNeedsAnimateInsideAnimationCallback() |
| : num_begin_frames_(0) {} |
| |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void BeginMainFrame(const BeginFrameArgs& args) override { |
| if (!num_begin_frames_) { |
| layer_tree_host()->SetNeedsAnimate(); |
| num_begin_frames_++; |
| return; |
| } |
| EndTest(); |
| } |
| |
| void AfterTest() override {} |
| |
| private: |
| int num_begin_frames_; |
| }; |
| |
| SINGLE_AND_MULTI_THREAD_TEST_F( |
| LayerTreeHostAnimationTestSetNeedsAnimateInsideAnimationCallback); |
| |
| // Add a layer animation and confirm that |
| // LayerTreeHostImpl::UpdateAnimationState does get called. |
| class LayerTreeHostAnimationTestAddAnimation |
| : public LayerTreeHostAnimationTest { |
| public: |
| LayerTreeHostAnimationTestAddAnimation() |
| : update_animation_state_was_called_(false) {} |
| |
| void BeginTest() override { |
| AttachPlayersToTimeline(); |
| player_->AttachElement(layer_tree_host()->root_layer()->element_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, |
| TargetProperty::Type target_property, |
| int group) override { |
| EXPECT_LT(base::TimeTicks(), monotonic_time); |
| |
| Animation* animation = |
| player_->element_animations()->GetAnimation(TargetProperty::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(LayerTreeHostAnimationTestAddAnimation); |
| |
| // Add a layer animation to a layer, but continually fail to draw. Confirm that |
| // after a while, we do eventually force a draw. |
| class LayerTreeHostAnimationTestCheckerboardDoesNotStarveDraws |
| : public LayerTreeHostAnimationTest { |
| public: |
| LayerTreeHostAnimationTestCheckerboardDoesNotStarveDraws() |
| : started_animating_(false) {} |
| |
| void BeginTest() override { |
| AttachPlayersToTimeline(); |
| player_->AttachElement(layer_tree_host()->root_layer()->element_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(LayerTreeHostAnimationTestCheckerboardDoesNotStarveDraws); |
| |
| // Ensures that animations eventually get deleted. |
| class LayerTreeHostAnimationTestAnimationsGetDeleted |
| : public LayerTreeHostAnimationTest { |
| public: |
| LayerTreeHostAnimationTestAnimationsGetDeleted() |
| : started_animating_(false) {} |
| |
| void BeginTest() override { |
| AttachPlayersToTimeline(); |
| player_->AttachElement(layer_tree_host()->root_layer()->element_id()); |
| PostAddAnimationToMainThreadPlayer(player_.get()); |
| } |
| |
| void AnimateLayers(LayerTreeHostImpl* host_impl, |
| base::TimeTicks monotonic_time) override { |
| bool have_animations = !host_impl->animation_host() |
| ->active_element_animations_for_testing() |
| .empty(); |
| if (!started_animating_ && have_animations) { |
| started_animating_ = true; |
| return; |
| } |
| |
| if (started_animating_ && !have_animations) |
| EndTest(); |
| } |
| |
| void NotifyAnimationFinished(base::TimeTicks monotonic_time, |
| TargetProperty::Type target_property, |
| int group) override { |
| // Animations on the impl-side ElementAnimations 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(LayerTreeHostAnimationTestAnimationsGetDeleted); |
| |
| // Ensure that an animation's timing function is respected. |
| class LayerTreeHostAnimationTestAddAnimationWithTimingFunction |
| : public LayerTreeHostAnimationTest { |
| public: |
| void SetupTree() override { |
| LayerTreeHostAnimationTest::SetupTree(); |
| picture_ = FakePictureLayer::Create(&client_); |
| picture_->SetBounds(gfx::Size(4, 4)); |
| client_.set_bounds(picture_->bounds()); |
| layer_tree_host()->root_layer()->AddChild(picture_); |
| |
| AttachPlayersToTimeline(); |
| player_child_->AttachElement(picture_->element_id()); |
| } |
| |
| void BeginTest() override { |
| PostAddAnimationToMainThreadPlayer(player_child_.get()); |
| } |
| |
| void AnimateLayers(LayerTreeHostImpl* host_impl, |
| base::TimeTicks monotonic_time) override { |
| // TODO(ajuma): This test only checks the active tree. Add checks for |
| // pending tree too. |
| if (!host_impl->active_tree()->root_layer_for_testing()) |
| return; |
| |
| // Wait for the commit with the animation to happen. |
| if (host_impl->sync_tree()->source_frame_number() != 0) |
| return; |
| |
| scoped_refptr<AnimationTimeline> timeline_impl = |
| host_impl->animation_host()->GetTimelineById(timeline_id_); |
| scoped_refptr<AnimationPlayer> player_child_impl = |
| timeline_impl->GetPlayerById(player_child_id_); |
| |
| Animation* animation = |
| player_child_impl->element_animations()->GetAnimation( |
| TargetProperty::OPACITY); |
| |
| 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<FakePictureLayer> picture_; |
| }; |
| |
| SINGLE_AND_MULTI_THREAD_TEST_F( |
| LayerTreeHostAnimationTestAddAnimationWithTimingFunction); |
| |
| // Ensures that main thread animations have their start times synchronized with |
| // impl thread animations. |
| class LayerTreeHostAnimationTestSynchronizeAnimationStartTimes |
| : public LayerTreeHostAnimationTest { |
| public: |
| void SetupTree() override { |
| LayerTreeHostAnimationTest::SetupTree(); |
| picture_ = FakePictureLayer::Create(&client_); |
| picture_->SetBounds(gfx::Size(4, 4)); |
| client_.set_bounds(picture_->bounds()); |
| |
| layer_tree_host()->root_layer()->AddChild(picture_); |
| |
| AttachPlayersToTimeline(); |
| player_child_->set_animation_delegate(this); |
| player_child_->AttachElement(picture_->element_id()); |
| } |
| |
| void BeginTest() override { |
| PostAddAnimationToMainThreadPlayer(player_child_.get()); |
| } |
| |
| void NotifyAnimationStarted(base::TimeTicks monotonic_time, |
| TargetProperty::Type target_property, |
| int group) override { |
| Animation* animation = player_child_->element_animations()->GetAnimation( |
| TargetProperty::OPACITY); |
| main_start_time_ = animation->start_time(); |
| player_child_->element_animations()->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_); |
| |
| Animation* animation = |
| player_child_impl->element_animations()->GetAnimation( |
| TargetProperty::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<FakePictureLayer> picture_; |
| }; |
| |
| SINGLE_AND_MULTI_THREAD_TEST_F( |
| LayerTreeHostAnimationTestSynchronizeAnimationStartTimes); |
| |
| // Ensures that notify animation finished is called. |
| class LayerTreeHostAnimationTestAnimationFinishedEvents |
| : public LayerTreeHostAnimationTest { |
| public: |
| void BeginTest() override { |
| AttachPlayersToTimeline(); |
| player_->AttachElement(layer_tree_host()->root_layer()->element_id()); |
| PostAddInstantAnimationToMainThreadPlayer(player_.get()); |
| } |
| |
| void NotifyAnimationFinished(base::TimeTicks monotonic_time, |
| TargetProperty::Type target_property, |
| int group) override { |
| Animation* animation = |
| player_->element_animations()->GetAnimation(TargetProperty::OPACITY); |
| if (animation) |
| player_->element_animations()->RemoveAnimation(animation->id()); |
| EndTest(); |
| } |
| |
| void AfterTest() override {} |
| }; |
| |
| SINGLE_AND_MULTI_THREAD_TEST_F( |
| LayerTreeHostAnimationTestAnimationFinishedEvents); |
| |
| // Ensures that when opacity is being animated, this value does not cause the |
| // subtree to be skipped. |
| class LayerTreeHostAnimationTestDoNotSkipLayersWithAnimatedOpacity |
| : public LayerTreeHostAnimationTest { |
| public: |
| LayerTreeHostAnimationTestDoNotSkipLayersWithAnimatedOpacity() |
| : update_check_layer_() {} |
| |
| void SetupTree() override { |
| update_check_layer_ = FakePictureLayer::Create(&client_); |
| update_check_layer_->SetOpacity(0.f); |
| layer_tree_host()->SetRootLayer(update_check_layer_); |
| client_.set_bounds(update_check_layer_->bounds()); |
| LayerTreeHostAnimationTest::SetupTree(); |
| |
| AttachPlayersToTimeline(); |
| player_->AttachElement(update_check_layer_->element_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_); |
| |
| Animation* animation_impl = player_impl->element_animations()->GetAnimation( |
| TargetProperty::OPACITY); |
| player_impl->element_animations()->RemoveAnimation(animation_impl->id()); |
| EndTest(); |
| } |
| |
| void AfterTest() override { |
| // Update() should have been called once, proving that the layer was not |
| // skipped. |
| EXPECT_EQ(1, update_check_layer_->update_count()); |
| |
| // clear update_check_layer_ so LayerTreeHost dies. |
| update_check_layer_ = NULL; |
| } |
| |
| private: |
| FakeContentLayerClient client_; |
| scoped_refptr<FakePictureLayer> update_check_layer_; |
| }; |
| |
| SINGLE_AND_MULTI_THREAD_TEST_F( |
| LayerTreeHostAnimationTestDoNotSkipLayersWithAnimatedOpacity); |
| |
| // Layers added to tree with existing active animations should have the |
| // animation correctly recognized. |
| class LayerTreeHostAnimationTestLayerAddedWithAnimation |
| : public LayerTreeHostAnimationTest { |
| public: |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void DidCommit() override { |
| if (layer_tree_host()->source_frame_number() == 1) { |
| AttachPlayersToTimeline(); |
| |
| scoped_refptr<Layer> layer = Layer::Create(); |
| layer->SetElementId(ElementId(42, 0)); |
| player_->AttachElement(layer->element_id()); |
| player_->set_animation_delegate(this); |
| |
| // Any valid AnimationCurve will do here. |
| std::unique_ptr<AnimationCurve> curve(new FakeFloatAnimationCurve()); |
| std::unique_ptr<Animation> animation( |
| Animation::Create(std::move(curve), 1, 1, TargetProperty::OPACITY)); |
| player_->AddAnimation(std::move(animation)); |
| |
| // 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( |
| LayerTreeHostAnimationTestLayerAddedWithAnimation); |
| |
| class LayerTreeHostAnimationTestCancelAnimateCommit |
| : public LayerTreeHostAnimationTest { |
| public: |
| LayerTreeHostAnimationTestCancelAnimateCommit() |
| : num_begin_frames_(0), num_commit_calls_(0), num_draw_calls_(0) {} |
| |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void BeginMainFrame(const BeginFrameArgs& args) override { |
| num_begin_frames_++; |
| // No-op animate will cancel the commit. |
| if (layer_tree_host()->source_frame_number() == 1) { |
| EndTest(); |
| return; |
| } |
| layer_tree_host()->SetNeedsAnimate(); |
| } |
| |
| void CommitCompleteOnThread(LayerTreeHostImpl* impl) override { |
| num_commit_calls_++; |
| if (impl->active_tree()->source_frame_number() > 1) |
| FAIL() << "Commit should have been canceled."; |
| } |
| |
| void DrawLayersOnThread(LayerTreeHostImpl* impl) override { |
| num_draw_calls_++; |
| if (impl->active_tree()->source_frame_number() > 1) |
| FAIL() << "Draw should have been canceled."; |
| } |
| |
| void AfterTest() override { |
| EXPECT_EQ(2, num_begin_frames_); |
| EXPECT_EQ(1, num_commit_calls_); |
| EXPECT_EQ(1, num_draw_calls_); |
| } |
| |
| private: |
| int num_begin_frames_; |
| int num_commit_calls_; |
| int num_draw_calls_; |
| }; |
| |
| MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestCancelAnimateCommit); |
| |
| class LayerTreeHostAnimationTestForceRedraw |
| : public LayerTreeHostAnimationTest { |
| public: |
| LayerTreeHostAnimationTestForceRedraw() |
| : num_animate_(0), num_draw_layers_(0) {} |
| |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void BeginMainFrame(const BeginFrameArgs& args) override { |
| if (++num_animate_ < 2) |
| layer_tree_host()->SetNeedsAnimate(); |
| } |
| |
| void UpdateLayerTreeHost() override { |
| layer_tree_host()->SetNextCommitForcesRedraw(); |
| } |
| |
| void DrawLayersOnThread(LayerTreeHostImpl* impl) override { |
| if (++num_draw_layers_ == 2) |
| EndTest(); |
| } |
| |
| void AfterTest() override { |
| // The first commit will always draw; make sure the second draw triggered |
| // by the animation was not cancelled. |
| EXPECT_EQ(2, num_draw_layers_); |
| EXPECT_EQ(2, num_animate_); |
| } |
| |
| private: |
| int num_animate_; |
| int num_draw_layers_; |
| }; |
| |
| MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestForceRedraw); |
| |
| class LayerTreeHostAnimationTestAnimateAfterSetNeedsCommit |
| : public LayerTreeHostAnimationTest { |
| public: |
| LayerTreeHostAnimationTestAnimateAfterSetNeedsCommit() |
| : num_animate_(0), num_draw_layers_(0) {} |
| |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void BeginMainFrame(const BeginFrameArgs& args) override { |
| if (++num_animate_ <= 2) { |
| layer_tree_host()->SetNeedsCommit(); |
| layer_tree_host()->SetNeedsAnimate(); |
| } |
| } |
| |
| void DrawLayersOnThread(LayerTreeHostImpl* impl) override { |
| if (++num_draw_layers_ == 2) |
| EndTest(); |
| } |
| |
| void AfterTest() override { |
| // The first commit will always draw; make sure the second draw triggered |
| // by the SetNeedsCommit was not cancelled. |
| EXPECT_EQ(2, num_draw_layers_); |
| EXPECT_GE(num_animate_, 2); |
| } |
| |
| private: |
| int num_animate_; |
| int num_draw_layers_; |
| }; |
| |
| MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestAnimateAfterSetNeedsCommit); |
| |
| // Animations should not be started when frames are being skipped due to |
| // checkerboard. |
| class LayerTreeHostAnimationTestCheckerboardDoesntStartAnimations |
| : public LayerTreeHostAnimationTest { |
| void SetupTree() override { |
| LayerTreeHostAnimationTest::SetupTree(); |
| picture_ = FakePictureLayer::Create(&client_); |
| picture_->SetBounds(gfx::Size(4, 4)); |
| client_.set_bounds(picture_->bounds()); |
| layer_tree_host()->root_layer()->AddChild(picture_); |
| |
| AttachPlayersToTimeline(); |
| player_child_->AttachElement(picture_->element_id()); |
| player_child_->set_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; |
| LayerTreeHostAnimationTest::InitializeSettings(settings); |
| } |
| |
| void BeginTest() override { |
| prevented_draw_ = 0; |
| started_times_ = 0; |
| |
| PostSetNeedsCommitToMainThread(); |
| } |
| |
| DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl, |
| LayerTreeHostImpl::FrameData* frame_data, |
| DrawResult draw_result) override { |
| // Don't checkerboard when the first animation wants to start. |
| if (host_impl->active_tree()->source_frame_number() < 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); |
| break; |
| case 2: |
| // This second animation will not be drawn so it should not start. |
| AddAnimatedTransformToPlayer(player_child_.get(), 0.1, 5, 5); |
| break; |
| } |
| } |
| |
| void NotifyAnimationStarted(base::TimeTicks monotonic_time, |
| TargetProperty::Type 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 started_times_; |
| FakeContentLayerClient client_; |
| scoped_refptr<FakePictureLayer> picture_; |
| }; |
| |
| MULTI_THREAD_TEST_F( |
| LayerTreeHostAnimationTestCheckerboardDoesntStartAnimations); |
| |
| // Verifies that scroll offset animations are only accepted when impl-scrolling |
| // is supported, and that when scroll offset animations are accepted, |
| // scroll offset updates are sent back to the main thread. |
| class LayerTreeHostAnimationTestScrollOffsetChangesArePropagated |
| : public LayerTreeHostAnimationTest { |
| public: |
| void SetupTree() override { |
| LayerTreeHostAnimationTest::SetupTree(); |
| |
| scroll_layer_ = FakePictureLayer::Create(&client_); |
| scroll_layer_->SetScrollClipLayerId(layer_tree_host()->root_layer()->id()); |
| scroll_layer_->SetBounds(gfx::Size(1000, 1000)); |
| client_.set_bounds(scroll_layer_->bounds()); |
| scroll_layer_->SetScrollOffset(gfx::ScrollOffset(10, 20)); |
| layer_tree_host()->root_layer()->AddChild(scroll_layer_); |
| |
| AttachPlayersToTimeline(); |
| player_child_->AttachElement(scroll_layer_->element_id()); |
| } |
| |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void DidCommit() override { |
| switch (layer_tree_host()->source_frame_number()) { |
| case 1: { |
| std::unique_ptr<ScrollOffsetAnimationCurve> curve( |
| ScrollOffsetAnimationCurve::Create( |
| gfx::ScrollOffset(500.f, 550.f), |
| CubicBezierTimingFunction::CreatePreset( |
| CubicBezierTimingFunction::EaseType::EASE_IN_OUT))); |
| std::unique_ptr<Animation> animation(Animation::Create( |
| std::move(curve), 1, 0, TargetProperty::SCROLL_OFFSET)); |
| animation->set_needs_synchronized_start_time(true); |
| bool impl_scrolling_supported = |
| layer_tree_host()->proxy()->SupportsImplScrolling(); |
| if (impl_scrolling_supported) |
| player_child_->AddAnimation(std::move(animation)); |
| else |
| EndTest(); |
| break; |
| } |
| default: |
| if (scroll_layer_->scroll_offset().x() > 10 && |
| scroll_layer_->scroll_offset().y() > 20) |
| EndTest(); |
| } |
| } |
| |
| void AfterTest() override {} |
| |
| private: |
| FakeContentLayerClient client_; |
| scoped_refptr<FakePictureLayer> scroll_layer_; |
| }; |
| |
| SINGLE_AND_MULTI_THREAD_TEST_F( |
| LayerTreeHostAnimationTestScrollOffsetChangesArePropagated); |
| |
| // Verifies that when a main thread scrolling reason gets added, the |
| // notification to takover the animation on the main thread gets sent. |
| class LayerTreeHostAnimationTestScrollOffsetAnimationTakeover |
| : public LayerTreeHostAnimationTest { |
| public: |
| LayerTreeHostAnimationTestScrollOffsetAnimationTakeover() {} |
| |
| void SetupTree() override { |
| LayerTreeHostAnimationTest::SetupTree(); |
| |
| scroll_layer_ = FakePictureLayer::Create(&client_); |
| scroll_layer_->SetBounds(gfx::Size(10000, 10000)); |
| client_.set_bounds(scroll_layer_->bounds()); |
| scroll_layer_->SetScrollOffset(gfx::ScrollOffset(10, 20)); |
| layer_tree_host()->root_layer()->AddChild(scroll_layer_); |
| |
| AttachPlayersToTimeline(); |
| player_child_->AttachElement(scroll_layer_->element_id()); |
| // Allows NotifyAnimationTakeover to get called. |
| player_child_->set_animation_delegate(this); |
| } |
| |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void DidCommit() override { |
| if (layer_tree_host()->source_frame_number() == 1) { |
| // Add an update after the first commit to trigger the animation takeover |
| // path. |
| layer_tree_host() |
| ->animation_host() |
| ->scroll_offset_animations() |
| .AddTakeoverUpdate(scroll_layer_->element_id()); |
| EXPECT_TRUE(layer_tree_host() |
| ->animation_host() |
| ->scroll_offset_animations() |
| .HasUpdatesForTesting()); |
| } |
| } |
| |
| void WillCommitCompleteOnThread(LayerTreeHostImpl* host_impl) override { |
| if (host_impl->sync_tree()->source_frame_number() == 0) { |
| host_impl->animation_host()->ImplOnlyScrollAnimationCreate( |
| scroll_layer_->element_id(), gfx::ScrollOffset(650.f, 750.f), |
| gfx::ScrollOffset(10, 20)); |
| } |
| } |
| |
| void NotifyAnimationTakeover(base::TimeTicks monotonic_time, |
| TargetProperty::Type target_property, |
| double animation_start_time, |
| std::unique_ptr<AnimationCurve> curve) override { |
| EndTest(); |
| } |
| |
| void AfterTest() override {} |
| |
| private: |
| FakeContentLayerClient client_; |
| scoped_refptr<FakePictureLayer> scroll_layer_; |
| }; |
| |
| MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestScrollOffsetAnimationTakeover); |
| |
| // Verifies that an impl-only scroll offset animation gets updated when the |
| // scroll offset is adjusted on the main thread. |
| class LayerTreeHostAnimationTestScrollOffsetAnimationAdjusted |
| : public LayerTreeHostAnimationTest { |
| public: |
| LayerTreeHostAnimationTestScrollOffsetAnimationAdjusted() {} |
| |
| void SetupTree() override { |
| LayerTreeHostAnimationTest::SetupTree(); |
| |
| scroll_layer_ = FakePictureLayer::Create(&client_); |
| scroll_layer_->SetBounds(gfx::Size(10000, 10000)); |
| client_.set_bounds(scroll_layer_->bounds()); |
| scroll_layer_->SetScrollOffset(gfx::ScrollOffset(10, 20)); |
| layer_tree_host()->root_layer()->AddChild(scroll_layer_); |
| |
| AttachPlayersToTimeline(); |
| player_child_->AttachElement(scroll_layer_->element_id()); |
| } |
| |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void DidCommit() override { |
| if (layer_tree_host()->source_frame_number() == 1) { |
| // Add an update after the first commit to trigger the animation update |
| // path. |
| layer_tree_host() |
| ->animation_host() |
| ->scroll_offset_animations() |
| .AddAdjustmentUpdate(scroll_layer_->element_id(), |
| gfx::Vector2dF(100.f, 100.f)); |
| EXPECT_TRUE(layer_tree_host() |
| ->animation_host() |
| ->scroll_offset_animations() |
| .HasUpdatesForTesting()); |
| } else if (layer_tree_host()->source_frame_number() == 2) { |
| // Verify that the update queue is cleared after the update is applied. |
| EXPECT_FALSE(layer_tree_host() |
| ->animation_host() |
| ->scroll_offset_animations() |
| .HasUpdatesForTesting()); |
| } |
| } |
| |
| void BeginCommitOnThread(LayerTreeHostImpl* host_impl) override { |
| // Note that the frame number gets incremented after BeginCommitOnThread but |
| // before WillCommitCompleteOnThread and CommitCompleteOnThread. |
| if (host_impl->sync_tree()->source_frame_number() == 0) { |
| // This happens after the impl-only animation is added in |
| // WillCommitCompleteOnThread. |
| Animation* animation = |
| host_impl->animation_host() |
| ->GetElementAnimationsForElementId(scroll_layer_->element_id()) |
| ->GetAnimation(TargetProperty::SCROLL_OFFSET); |
| ScrollOffsetAnimationCurve* curve = |
| animation->curve()->ToScrollOffsetAnimationCurve(); |
| |
| // Verifiy the initial and target position before the scroll offset |
| // update from MT. |
| EXPECT_EQ(Animation::RunState::RUNNING, animation->run_state()); |
| EXPECT_EQ(gfx::ScrollOffset(10.f, 20.f), |
| curve->GetValue(base::TimeDelta())); |
| EXPECT_EQ(gfx::ScrollOffset(650.f, 750.f), curve->target_value()); |
| } |
| } |
| |
| void WillCommitCompleteOnThread(LayerTreeHostImpl* host_impl) override { |
| if (host_impl->sync_tree()->source_frame_number() == 0) { |
| host_impl->animation_host()->ImplOnlyScrollAnimationCreate( |
| scroll_layer_->element_id(), gfx::ScrollOffset(650.f, 750.f), |
| gfx::ScrollOffset(10, 20)); |
| } |
| } |
| |
| void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override { |
| if (host_impl->sync_tree()->source_frame_number() == 1) { |
| Animation* animation = |
| host_impl->animation_host() |
| ->GetElementAnimationsForElementId(scroll_layer_->element_id()) |
| ->GetAnimation(TargetProperty::SCROLL_OFFSET); |
| ScrollOffsetAnimationCurve* curve = |
| animation->curve()->ToScrollOffsetAnimationCurve(); |
| // Verifiy the initial and target position after the scroll offset |
| // update from MT |
| EXPECT_EQ(Animation::RunState::STARTING, animation->run_state()); |
| EXPECT_EQ(gfx::ScrollOffset(110.f, 120.f), |
| curve->GetValue(base::TimeDelta())); |
| EXPECT_EQ(gfx::ScrollOffset(750.f, 850.f), curve->target_value()); |
| |
| EndTest(); |
| } |
| } |
| |
| void AfterTest() override {} |
| |
| private: |
| FakeContentLayerClient client_; |
| scoped_refptr<FakePictureLayer> scroll_layer_; |
| }; |
| |
| MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestScrollOffsetAnimationAdjusted); |
| |
| // Verifies that when the main thread removes a scroll animation and sets a new |
| // scroll position, the active tree takes on exactly this new scroll position |
| // after activation, and the main thread doesn't receive a spurious scroll |
| // delta. |
| class LayerTreeHostAnimationTestScrollOffsetAnimationRemoval |
| : public LayerTreeHostAnimationTest { |
| public: |
| LayerTreeHostAnimationTestScrollOffsetAnimationRemoval() |
| : final_postion_(50.0, 100.0) {} |
| |
| void SetupTree() override { |
| LayerTreeHostAnimationTest::SetupTree(); |
| |
| scroll_layer_ = FakePictureLayer::Create(&client_); |
| scroll_layer_->SetScrollClipLayerId(layer_tree_host()->root_layer()->id()); |
| scroll_layer_->SetBounds(gfx::Size(10000, 10000)); |
| client_.set_bounds(scroll_layer_->bounds()); |
| scroll_layer_->SetScrollOffset(gfx::ScrollOffset(100.0, 200.0)); |
| layer_tree_host()->root_layer()->AddChild(scroll_layer_); |
| |
| std::unique_ptr<ScrollOffsetAnimationCurve> curve( |
| ScrollOffsetAnimationCurve::Create( |
| gfx::ScrollOffset(6500.f, 7500.f), |
| CubicBezierTimingFunction::CreatePreset( |
| CubicBezierTimingFunction::EaseType::EASE_IN_OUT))); |
| std::unique_ptr<Animation> animation(Animation::Create( |
| std::move(curve), 1, 0, TargetProperty::SCROLL_OFFSET)); |
| animation->set_needs_synchronized_start_time(true); |
| |
| AttachPlayersToTimeline(); |
| player_child_->AttachElement(scroll_layer_->element_id()); |
| player_child_->AddAnimation(std::move(animation)); |
| } |
| |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void BeginMainFrame(const BeginFrameArgs& args) override { |
| switch (layer_tree_host()->source_frame_number()) { |
| case 0: |
| break; |
| case 1: { |
| Animation* animation = |
| player_child_->element_animations() |
| ->GetAnimation(TargetProperty::SCROLL_OFFSET); |
| player_child_->RemoveAnimation(animation->id()); |
| scroll_layer_->SetScrollOffset(final_postion_); |
| break; |
| } |
| default: |
| EXPECT_EQ(final_postion_, scroll_layer_->scroll_offset()); |
| } |
| } |
| |
| void BeginCommitOnThread(LayerTreeHostImpl* host_impl) override { |
| host_impl->BlockNotifyReadyToActivateForTesting( |
| ShouldBlockActivation(host_impl)); |
| } |
| |
| void WillBeginImplFrameOnThread(LayerTreeHostImpl* host_impl, |
| const BeginFrameArgs& args) override { |
| host_impl->BlockNotifyReadyToActivateForTesting( |
| ShouldBlockActivation(host_impl)); |
| } |
| |
| void WillActivateTreeOnThread(LayerTreeHostImpl* host_impl) override { |
| if (host_impl->pending_tree()->source_frame_number() != 1) |
| return; |
| LayerImpl* scroll_layer_impl = |
| host_impl->pending_tree()->LayerById(scroll_layer_->id()); |
| EXPECT_EQ(final_postion_, scroll_layer_impl->CurrentScrollOffset()); |
| } |
| |
| void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override { |
| if (host_impl->active_tree()->source_frame_number() != 1) |
| return; |
| LayerImpl* scroll_layer_impl = |
| host_impl->active_tree()->LayerById(scroll_layer_->id()); |
| EXPECT_EQ(final_postion_, scroll_layer_impl->CurrentScrollOffset()); |
| EndTest(); |
| } |
| |
| void AfterTest() override { |
| EXPECT_EQ(final_postion_, scroll_layer_->scroll_offset()); |
| } |
| |
| private: |
| bool ShouldBlockActivation(LayerTreeHostImpl* host_impl) { |
| if (!host_impl->pending_tree()) |
| return false; |
| |
| if (!host_impl->active_tree()->root_layer_for_testing()) |
| return false; |
| |
| scoped_refptr<AnimationTimeline> timeline_impl = |
| host_impl->animation_host()->GetTimelineById(timeline_id_); |
| scoped_refptr<AnimationPlayer> player_impl = |
| timeline_impl->GetPlayerById(player_child_id_); |
| |
| LayerImpl* scroll_layer_impl = |
| host_impl->active_tree()->LayerById(scroll_layer_->id()); |
| Animation* animation = player_impl->element_animations()->GetAnimation( |
| TargetProperty::SCROLL_OFFSET); |
| |
| if (!animation || animation->run_state() != Animation::RUNNING) |
| return false; |
| |
| // Block activation until the running animation has a chance to produce a |
| // scroll delta. |
| gfx::Vector2dF scroll_delta = ScrollDelta(scroll_layer_impl); |
| if (scroll_delta.x() > 0.f || scroll_delta.y() > 0.f) |
| return false; |
| |
| return true; |
| } |
| |
| FakeContentLayerClient client_; |
| scoped_refptr<FakePictureLayer> scroll_layer_; |
| const gfx::ScrollOffset final_postion_; |
| }; |
| |
| MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestScrollOffsetAnimationRemoval); |
| |
| // 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. |
| class LayerTreeHostAnimationTestAnimationsAddedToNewAndExistingLayers |
| : public LayerTreeHostAnimationTest { |
| public: |
| LayerTreeHostAnimationTestAnimationsAddedToNewAndExistingLayers() |
| : frame_count_with_pending_tree_(0) {} |
| |
| void BeginTest() override { |
| AttachPlayersToTimeline(); |
| PostSetNeedsCommitToMainThread(); |
| } |
| |
| void DidCommit() override { |
| if (layer_tree_host()->source_frame_number() == 1) { |
| player_->AttachElement(layer_tree_host()->root_layer()->element_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_tree_host()->SetElementIdsForTesting(); |
| layer->SetBounds(gfx::Size(4, 4)); |
| |
| player_child_->AttachElement(layer->element_id()); |
| player_child_->set_animation_delegate(this); |
| AddOpacityTransitionToPlayer(player_child_.get(), 1, 0.f, 0.5f, true); |
| } |
| } |
| |
| void BeginCommitOnThread(LayerTreeHostImpl* host_impl) override { |
| 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->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->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->element_animations()) |
| return; |
| |
| Animation* root_animation = player_impl->element_animations()->GetAnimation( |
| TargetProperty::OPACITY); |
| if (!root_animation || root_animation->run_state() != Animation::RUNNING) |
| return; |
| |
| Animation* child_animation = |
| player_child_impl->element_animations()->GetAnimation( |
| TargetProperty::OPACITY); |
| EXPECT_EQ(Animation::RUNNING, child_animation->run_state()); |
| EXPECT_EQ(root_animation->start_time(), child_animation->start_time()); |
| player_impl->element_animations()->AbortAnimations(TargetProperty::OPACITY); |
| player_impl->element_animations()->AbortAnimations( |
| TargetProperty::TRANSFORM); |
| player_child_impl->element_animations()->AbortAnimations( |
| TargetProperty::OPACITY); |
| EndTest(); |
| } |
| |
| void AfterTest() override {} |
| |
| private: |
| int frame_count_with_pending_tree_; |
| }; |
| |
| // This test blocks activation which is not supported for single thread mode. |
| MULTI_THREAD_BLOCKNOTIFY_TEST_F( |
| LayerTreeHostAnimationTestAnimationsAddedToNewAndExistingLayers); |
| |
| class LayerTreeHostAnimationTestPendingTreeAnimatesFirstCommit |
| : public LayerTreeHostAnimationTest { |
| public: |
| void SetupTree() override { |
| LayerTreeHostAnimationTest::SetupTree(); |
| |
| layer_ = FakePictureLayer::Create(&client_); |
| layer_->SetBounds(gfx::Size(2, 2)); |
| client_.set_bounds(layer_->bounds()); |
| // Transform the layer to 4,4 to start. |
| gfx::Transform start_transform; |
| start_transform.Translate(4.0, 4.0); |
| layer_->SetTransform(start_transform); |
| |
| layer_tree_host()->root_layer()->AddChild(layer_); |
| layer_tree_host()->SetElementIdsForTesting(); |
| |
| player_->AttachElement(layer_->element_id()); |
| |
| AttachPlayersToTimeline(); |
| } |
| |
| void BeginTest() override { |
| // Add a translate from 6,7 to 8,9. |
| TransformOperations start; |
| start.AppendTranslate(6.f, 7.f, 0.f); |
| TransformOperations end; |
| end.AppendTranslate(8.f, 9.f, 0.f); |
| AddAnimatedTransformToPlayer(player_.get(), 4.0, start, end); |
| |
| PostSetNeedsCommitToMainThread(); |
| } |
| |
| void WillPrepareTiles(LayerTreeHostImpl* host_impl) override { |
| if (host_impl->sync_tree()->source_frame_number() != 0) |
| return; |
| |
| // After checking this on the sync tree, we will activate, which will cause |
| // PrepareTiles to happen again (which races with the test exiting). |
| if (TestEnded()) |
| return; |
| |
| scoped_refptr<AnimationTimeline> timeline_impl = |
| host_impl->animation_host()->GetTimelineById(timeline_id_); |
| scoped_refptr<AnimationPlayer> player_impl = |
| timeline_impl->GetPlayerById(player_id_); |
| |
| LayerImpl* child = host_impl->sync_tree()->LayerById(layer_->id()); |
| Animation* animation = player_impl->element_animations()->GetAnimation( |
| TargetProperty::TRANSFORM); |
| |
| // The animation should be starting for the first frame. |
| EXPECT_EQ(Animation::STARTING, animation->run_state()); |
| |
| // And the transform should be propogated to the sync tree layer, at its |
| // starting state which is 6,7. |
| gfx::Transform expected_transform; |
| expected_transform.Translate(6.0, 7.0); |
| EXPECT_TRANSFORMATION_MATRIX_EQ(expected_transform, child->DrawTransform()); |
| // And the sync tree layer should know it is animating. |
| EXPECT_TRUE(child->screen_space_transform_is_animating()); |
| |
| player_impl->element_animations()->AbortAnimations( |
| TargetProperty::TRANSFORM); |
| EndTest(); |
| } |
| |
| void AfterTest() override {} |
| |
| FakeContentLayerClient client_; |
| scoped_refptr<Layer> layer_; |
| }; |
| |
| SINGLE_AND_MULTI_THREAD_TEST_F( |
| LayerTreeHostAnimationTestPendingTreeAnimatesFirstCommit); |
| |
| // When a layer with an animation is removed from the tree and later re-added, |
| // the animation should resume. |
| class LayerTreeHostAnimationTestAnimatedLayerRemovedAndAdded |
| : public LayerTreeHostAnimationTest { |
| public: |
| void SetupTree() override { |
| LayerTreeHostAnimationTest::SetupTree(); |
| layer_ = Layer::Create(); |
| layer_->SetBounds(gfx::Size(4, 4)); |
| layer_tree_host()->root_layer()->AddChild(layer_); |
| |
| layer_tree_host()->SetElementIdsForTesting(); |
| |
| layer_tree_host()->animation_host()->AddAnimationTimeline(timeline_.get()); |
| timeline_->AttachPlayer(player_.get()); |
| player_->AttachElement(layer_->element_id()); |
| DCHECK(player_->element_animations()); |
| |
| AddOpacityTransitionToPlayer(player_.get(), 10000.0, 0.1f, 0.9f, true); |
| } |
| |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void DidCommit() override { |
| switch (layer_tree_host()->source_frame_number()) { |
| case 0: |
| EXPECT_TRUE( |
| player_->element_animations()->has_element_in_active_list()); |
| EXPECT_FALSE( |
| player_->element_animations()->has_element_in_pending_list()); |
| EXPECT_TRUE(layer_tree_host()->animation_host()->NeedsAnimateLayers()); |
| break; |
| case 1: |
| layer_->RemoveFromParent(); |
| EXPECT_FALSE( |
| player_->element_animations()->has_element_in_active_list()); |
| EXPECT_FALSE( |
| player_->element_animations()->has_element_in_pending_list()); |
| EXPECT_FALSE(layer_tree_host()->animation_host()->NeedsAnimateLayers()); |
| break; |
| case 2: |
| layer_tree_host()->root_layer()->AddChild(layer_); |
| EXPECT_TRUE( |
| player_->element_animations()->has_element_in_active_list()); |
| EXPECT_FALSE( |
| player_->element_animations()->has_element_in_pending_list()); |
| EXPECT_TRUE(layer_tree_host()->animation_host()->NeedsAnimateLayers()); |
| break; |
| } |
| } |
| |
| 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_); |
| |
| switch (host_impl->active_tree()->source_frame_number()) { |
| case 0: |
| EXPECT_TRUE( |
| player_impl->element_animations()->has_element_in_active_list()); |
| EXPECT_TRUE(host_impl->animation_host()->NeedsAnimateLayers()); |
| break; |
| case 1: |
| EXPECT_FALSE( |
| player_impl->element_animations()->has_element_in_active_list()); |
| EXPECT_FALSE(host_impl->animation_host()->NeedsAnimateLayers()); |
| break; |
| case 2: |
| EXPECT_TRUE( |
| player_impl->element_animations()->has_element_in_active_list()); |
| EXPECT_TRUE(host_impl->animation_host()->NeedsAnimateLayers()); |
| EndTest(); |
| break; |
| } |
| } |
| |
| void AfterTest() override {} |
| |
| private: |
| scoped_refptr<Layer> layer_; |
| }; |
| |
| SINGLE_AND_MULTI_THREAD_TEST_F( |
| LayerTreeHostAnimationTestAnimatedLayerRemovedAndAdded); |
| |
| class LayerTreeHostAnimationTestAddAnimationAfterAnimating |
| : public LayerTreeHostAnimationTest { |
| public: |
| void SetupTree() override { |
| LayerTreeHostAnimationTest::SetupTree(); |
| layer_ = Layer::Create(); |
| layer_->SetBounds(gfx::Size(4, 4)); |
| layer_tree_host()->root_layer()->AddChild(layer_); |
| |
| AttachPlayersToTimeline(); |
| |
| player_->AttachElement(layer_tree_host()->root_layer()->element_id()); |
| player_child_->AttachElement(layer_->element_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. |
| if (host_impl->active_tree()->source_frame_number() < 2) |
| return; |
| AnimationHost::ElementToAnimationsMap element_animations_copy = |
| host_impl->animation_host()->active_element_animations_for_testing(); |
| EXPECT_EQ(2u, element_animations_copy.size()); |
| for (auto& it : element_animations_copy) { |
| ElementId id = it.first; |
| if (id == |
| host_impl->active_tree()->root_layer_for_testing()->element_id()) { |
| Animation* anim = it.second->GetAnimation(TargetProperty::TRANSFORM); |
| EXPECT_GT((anim->start_time() - base::TimeTicks()).InSecondsF(), 0); |
| } else if (id == layer_->element_id()) { |
| Animation* anim = it.second->GetAnimation(TargetProperty::OPACITY); |
| EXPECT_GT((anim->start_time() - base::TimeTicks()).InSecondsF(), 0); |
| } |
| EndTest(); |
| } |
| } |
| |
| void AfterTest() override {} |
| |
| private: |
| scoped_refptr<Layer> layer_; |
| }; |
| |
| SINGLE_AND_MULTI_THREAD_TEST_F( |
| LayerTreeHostAnimationTestAddAnimationAfterAnimating); |
| |
| class LayerTreeHostAnimationTestRemoveAnimation |
| : public LayerTreeHostAnimationTest { |
| public: |
| void SetupTree() override { |
| LayerTreeHostAnimationTest::SetupTree(); |
| layer_ = FakePictureLayer::Create(&client_); |
| layer_->SetBounds(gfx::Size(4, 4)); |
| client_.set_bounds(layer_->bounds()); |
| layer_tree_host()->root_layer()->AddChild(layer_); |
| |
| AttachPlayersToTimeline(); |
| |
| player_->AttachElement(layer_tree_host()->root_layer()->element_id()); |
| player_child_->AttachElement(layer_->element_id()); |
| } |
| |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void DidCommit() override { |
| switch (layer_tree_host()->source_frame_number()) { |
| case 1: |
| AddAnimatedTransformToPlayer(player_child_.get(), 1.0, 5, 5); |
| break; |
| case 2: |
| Animation* animation = |
| player_child_->element_animations()->GetAnimation( |
| TargetProperty::TRANSFORM); |
| player_child_->RemoveAnimation(animation->id()); |
| gfx::Transform transform; |
| transform.Translate(10.f, 10.f); |
| layer_->SetTransform(transform); |
| |
| // Do something that causes property trees to get rebuilt. This is |
| // intended to simulate the conditions that caused the bug whose fix |
| // this is testing (the test will pass without it but won't test what |
| // we want it to). We were updating the wrong transform node at the end |
| // of an animation (we were assuming the layer with the finished |
| // animation still had its own transform node). But nodes can only get |
| // added/deleted when something triggers a rebuild. Adding a layer |
| // triggers a rebuild, and since the layer that had an animation before |
| // no longer has one, it doesn't get a transform node in the rebuild. |
| layer_->AddChild(Layer::Create()); |
| break; |
| } |
| } |
| |
| void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override { |
| LayerImpl* child = host_impl->active_tree()->LayerById(layer_->id()); |
| switch (host_impl->active_tree()->source_frame_number()) { |
| case 0: |
| // No animation yet. |
| break; |
| case 1: |
| // Animation is started. |
| EXPECT_TRUE(child->screen_space_transform_is_animating()); |
| break; |
| case 2: { |
| // The animation is removed, the transform that was set afterward is |
| // applied. |
| gfx::Transform expected_transform; |
| expected_transform.Translate(10.f, 10.f); |
| EXPECT_TRANSFORMATION_MATRIX_EQ(expected_transform, |
| child->DrawTransform()); |
| EXPECT_FALSE(child->screen_space_transform_is_animating()); |
| EndTest(); |
| break; |
| } |
| default: |
| NOTREACHED(); |
| } |
| } |
| |
| void AfterTest() override {} |
| |
| private: |
| scoped_refptr<Layer> layer_; |
| FakeContentLayerClient client_; |
| }; |
| |
| SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestRemoveAnimation); |
| |
| class LayerTreeHostAnimationTestIsAnimating |
| : public LayerTreeHostAnimationTest { |
| public: |
| void SetupTree() override { |
| LayerTreeHostAnimationTest::SetupTree(); |
| layer_ = FakePictureLayer::Create(&client_); |
| layer_->SetBounds(gfx::Size(4, 4)); |
| client_.set_bounds(layer_->bounds()); |
| layer_tree_host()->root_layer()->AddChild(layer_); |
| |
| AttachPlayersToTimeline(); |
| player_->AttachElement(layer_->element_id()); |
| } |
| |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void DidCommit() override { |
| switch (layer_tree_host()->source_frame_number()) { |
| case 1: |
| AddAnimatedTransformToPlayer(player_.get(), 1.0, 5, 5); |
| break; |
| case 2: |
| Animation* animation = player_->element_animations()->GetAnimation( |
| TargetProperty::TRANSFORM); |
| player_->RemoveAnimation(animation->id()); |
| break; |
| } |
| } |
| |
| void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override { |
| LayerImpl* child = host_impl->sync_tree()->LayerById(layer_->id()); |
| switch (host_impl->sync_tree()->source_frame_number()) { |
| case 0: |
| // No animation yet. |
| break; |
| case 1: |
| // Animation is started. |
| EXPECT_TRUE(child->screen_space_transform_is_animating()); |
| break; |
| case 2: |
| // The animation is removed/stopped. |
| EXPECT_FALSE(child->screen_space_transform_is_animating()); |
| EndTest(); |
| break; |
| default: |
| NOTREACHED(); |
| } |
| } |
| |
| void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override { |
| LayerImpl* child = host_impl->active_tree()->LayerById(layer_->id()); |
| switch (host_impl->active_tree()->source_frame_number()) { |
| case 0: |
| // No animation yet. |
| break; |
| case 1: |
| // Animation is started. |
| EXPECT_TRUE(child->screen_space_transform_is_animating()); |
| break; |
| case 2: |
| // The animation is removed/stopped. |
| EXPECT_FALSE(child->screen_space_transform_is_animating()); |
| EndTest(); |
| break; |
| default: |
| NOTREACHED(); |
| } |
| } |
| |
| void AfterTest() override {} |
| |
| private: |
| scoped_refptr<Layer> layer_; |
| FakeContentLayerClient client_; |
| }; |
| |
| SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestIsAnimating); |
| |
| class LayerTreeHostAnimationTestAnimationFinishesDuringCommit |
| : public LayerTreeHostAnimationTest { |
| public: |
| LayerTreeHostAnimationTestAnimationFinishesDuringCommit() |
| : signalled_(false) {} |
| |
| void SetupTree() override { |
| LayerTreeHostAnimationTest::SetupTree(); |
| layer_ = FakePictureLayer::Create(&client_); |
| layer_->SetBounds(gfx::Size(4, 4)); |
| client_.set_bounds(layer_->bounds()); |
| layer_tree_host()->root_layer()->AddChild(layer_); |
| |
| AttachPlayersToTimeline(); |
| |
| player_->AttachElement(layer_tree_host()->root_layer()->element_id()); |
| player_child_->AttachElement(layer_->element_id()); |
| } |
| |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void DidCommit() override { |
| if (layer_tree_host()->source_frame_number() == 1) |
| AddAnimatedTransformToPlayer(player_child_.get(), 0.04, 5, 5); |
| } |
| |
| void WillCommit() override { |
| if (layer_tree_host()->source_frame_number() == 2) { |
| // Block until the animation finishes on the compositor thread. Since |
| // animations have already been ticked on the main thread, when the commit |
| // happens the state on the main thread will be consistent with having a |
| // running animation but the state on the compositor thread will be |
| // consistent with having only a finished animation. |
| completion_.Wait(); |
| } |
| } |
| |
| void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override { |
| switch (host_impl->sync_tree()->source_frame_number()) { |
| case 1: |
| PostSetNeedsCommitToMainThread(); |
| break; |
| case 2: |
| gfx::Transform expected_transform; |
| expected_transform.Translate(5.f, 5.f); |
| LayerImpl* layer_impl = host_impl->sync_tree()->LayerById(layer_->id()); |
| EXPECT_TRANSFORMATION_MATRIX_EQ(expected_transform, |
| layer_impl->DrawTransform()); |
| EndTest(); |
| break; |
| } |
| } |
| |
| void UpdateAnimationState(LayerTreeHostImpl* host_impl, |
| bool has_unfinished_animation) override { |
| if (host_impl->active_tree()->source_frame_number() == 1 && |
| !has_unfinished_animation && !signalled_) { |
| // The animation has finished, so allow the main thread to commit. |
| completion_.Signal(); |
| signalled_ = true; |
| } |
| } |
| |
| void AfterTest() override {} |
| |
| private: |
| scoped_refptr<Layer> layer_; |
| FakeContentLayerClient client_; |
| CompletionEvent completion_; |
| bool signalled_; |
| }; |
| |
| // An animation finishing during commit can only happen when we have a separate |
| // compositor thread. |
| MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestAnimationFinishesDuringCommit); |
| |
| class LayerTreeHostAnimationTestNotifyAnimationFinished |
| : public LayerTreeHostAnimationTest { |
| public: |
| LayerTreeHostAnimationTestNotifyAnimationFinished() |
| : called_animation_started_(false), called_animation_finished_(false) {} |
| |
| void SetupTree() override { |
| LayerTreeHostAnimationTest::SetupTree(); |
| picture_ = FakePictureLayer::Create(&client_); |
| picture_->SetBounds(gfx::Size(4, 4)); |
| client_.set_bounds(picture_->bounds()); |
| layer_tree_host()->root_layer()->AddChild(picture_); |
| |
| AttachPlayersToTimeline(); |
| player_->AttachElement(picture_->element_id()); |
| player_->set_animation_delegate(this); |
| } |
| |
| void BeginTest() override { |
| PostAddLongAnimationToMainThreadPlayer(player_.get()); |
| } |
| |
| void NotifyAnimationStarted(base::TimeTicks monotonic_time, |
| TargetProperty::Type target_property, |
| int group) override { |
| called_animation_started_ = true; |
| layer_tree_host()->AnimateLayers(base::TimeTicks::FromInternalValue( |
| std::numeric_limits<int64_t>::max())); |
| PostSetNeedsCommitToMainThread(); |
| } |
| |
| void NotifyAnimationFinished(base::TimeTicks monotonic_time, |
| TargetProperty::Type target_property, |
| int group) override { |
| called_animation_finished_ = true; |
| EndTest(); |
| } |
| |
| void AfterTest() override { |
| EXPECT_TRUE(called_animation_started_); |
| EXPECT_TRUE(called_animation_finished_); |
| } |
| |
| private: |
| bool called_animation_started_; |
| bool called_animation_finished_; |
| FakeContentLayerClient client_; |
| scoped_refptr<FakePictureLayer> picture_; |
| }; |
| |
| SINGLE_AND_MULTI_THREAD_TEST_F( |
| LayerTreeHostAnimationTestNotifyAnimationFinished); |
| |
| // Check that transform sync happens correctly at commit when we remove and add |
| // a different animation player to an element. |
| class LayerTreeHostAnimationTestChangeAnimationPlayer |
| : public LayerTreeHostAnimationTest { |
| public: |
| void SetupTree() override { |
| LayerTreeHostAnimationTest::SetupTree(); |
| AttachPlayersToTimeline(); |
| timeline_->DetachPlayer(player_child_.get()); |
| player_->AttachElement(layer_tree_host()->root_layer()->element_id()); |
| |
| TransformOperations start; |
| start.AppendTranslate(5.f, 5.f, 0.f); |
| TransformOperations end; |
| end.AppendTranslate(5.f, 5.f, 0.f); |
| AddAnimatedTransformToPlayer(player_.get(), 1.0, start, end); |
| } |
| |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override { |
| PropertyTrees* property_trees = host_impl->sync_tree()->property_trees(); |
| TransformNode* node = |
| property_trees->transform_tree.Node(host_impl->sync_tree() |
| ->root_layer_for_testing() |
| ->transform_tree_index()); |
| gfx::Transform translate; |
| translate.Translate(5, 5); |
| switch (host_impl->sync_tree()->source_frame_number()) { |
| case 2: |
| EXPECT_TRANSFORMATION_MATRIX_EQ(node->data.local, translate); |
| EndTest(); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| void DidCommit() override { PostSetNeedsCommitToMainThread(); } |
| |
| void WillBeginMainFrame() override { |
| if (layer_tree_host()->source_frame_number() == 2) { |
| // Destroy player. |
| timeline_->DetachPlayer(player_.get()); |
| player_ = nullptr; |
| timeline_->AttachPlayer(player_child_.get()); |
| player_child_->AttachElement( |
| layer_tree_host()->root_layer()->element_id()); |
| AddAnimatedTransformToPlayer(player_child_.get(), 1.0, 10, 10); |
| Animation* animation = player_child_->element_animations()->GetAnimation( |
| TargetProperty::TRANSFORM); |
| animation->set_start_time(base::TimeTicks::Now() + |
| base::TimeDelta::FromSecondsD(1000)); |
| animation->set_fill_mode(Animation::FillMode::NONE); |
| } |
| } |
| |
| void AfterTest() override {} |
| }; |
| |
| SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestChangeAnimationPlayer); |
| |
| // Check that SetTransformIsPotentiallyAnimatingChanged is called |
| // if we destroy ElementAnimations. |
| class LayerTreeHostAnimationTestSetPotentiallyAnimatingOnLacDestruction |
| : public LayerTreeHostAnimationTest { |
| public: |
| void SetupTree() override { |
| prev_screen_space_transform_is_animating_ = true; |
| screen_space_transform_animation_stopped_ = false; |
| |
| LayerTreeHostAnimationTest::SetupTree(); |
| AttachPlayersToTimeline(); |
| player_->AttachElement(layer_tree_host()->root_layer()->element_id()); |
| AddAnimatedTransformToPlayer(player_.get(), 1.0, 5, 5); |
| } |
| |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override { |
| if (host_impl->pending_tree()->source_frame_number() <= 1) { |
| EXPECT_TRUE(host_impl->pending_tree() |
| ->root_layer_for_testing() |
| ->screen_space_transform_is_animating()); |
| } else { |
| EXPECT_FALSE(host_impl->pending_tree() |
| ->root_layer_for_testing() |
| ->screen_space_transform_is_animating()); |
| } |
| } |
| |
| void DidCommit() override { PostSetNeedsCommitToMainThread(); } |
| |
| void UpdateLayerTreeHost() override { |
| if (layer_tree_host()->source_frame_number() == 2) { |
| // Destroy player. |
| timeline_->DetachPlayer(player_.get()); |
| player_ = nullptr; |
| } |
| } |
| |
| DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl, |
| LayerTreeHostImpl::FrameData* frame_data, |
| DrawResult draw_result) override { |
| const bool screen_space_transform_is_animating = |
| host_impl->active_tree() |
| ->root_layer_for_testing() |
| ->screen_space_transform_is_animating(); |
| |
| // Check that screen_space_transform_is_animating changes only once. |
| if (screen_space_transform_is_animating && |
| prev_screen_space_transform_is_animating_) |
| EXPECT_FALSE(screen_space_transform_animation_stopped_); |
| if (!screen_space_transform_is_animating && |
| prev_screen_space_transform_is_animating_) { |
| EXPECT_FALSE(screen_space_transform_animation_stopped_); |
| screen_space_transform_animation_stopped_ = true; |
| } |
| if (!screen_space_transform_is_animating && |
| !prev_screen_space_transform_is_animating_) |
| EXPECT_TRUE(screen_space_transform_animation_stopped_); |
| |
| prev_screen_space_transform_is_animating_ = |
| screen_space_transform_is_animating; |
| |
| return draw_result; |
| } |
| |
| void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override { |
| if (host_impl->active_tree()->source_frame_number() >= 2) |
| EndTest(); |
| } |
| |
| void AfterTest() override { |
| EXPECT_TRUE(screen_space_transform_animation_stopped_); |
| } |
| |
| bool prev_screen_space_transform_is_animating_; |
| bool screen_space_transform_animation_stopped_; |
| }; |
| |
| MULTI_THREAD_TEST_F( |
| LayerTreeHostAnimationTestSetPotentiallyAnimatingOnLacDestruction); |
| |
| // Check that we invalidate property trees on AnimationPlayer::SetNeedsCommit. |
| class LayerTreeHostAnimationTestRebuildPropertyTreesOnAnimationSetNeedsCommit |
| : public LayerTreeHostAnimationTest { |
| public: |
| void SetupTree() override { |
| LayerTreeHostAnimationTest::SetupTree(); |
| layer_ = FakePictureLayer::Create(&client_); |
| layer_->SetBounds(gfx::Size(4, 4)); |
| client_.set_bounds(layer_->bounds()); |
| layer_tree_host()->root_layer()->AddChild(layer_); |
| |
| AttachPlayersToTimeline(); |
| |
| player_->AttachElement(layer_tree_host()->root_layer()->element_id()); |
| player_child_->AttachElement(layer_->element_id()); |
| } |
| |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void DidCommit() override { |
| if (layer_tree_host()->source_frame_number() == 1 || |
| layer_tree_host()->source_frame_number() == 2) |
| PostSetNeedsCommitToMainThread(); |
| } |
| |
| void UpdateLayerTreeHost() override { |
| if (layer_tree_host()->source_frame_number() == 1) { |
| EXPECT_FALSE(layer_tree_host()->property_trees()->needs_rebuild); |
| AddAnimatedTransformToPlayer(player_child_.get(), 1.0, 5, 5); |
| } |
| |
| EXPECT_TRUE(layer_tree_host()->property_trees()->needs_rebuild); |
| } |
| |
| void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override { |
| if (host_impl->active_tree()->source_frame_number() >= 2) |
| EndTest(); |
| } |
| |
| void AfterTest() override {} |
| |
| private: |
| scoped_refptr<Layer> layer_; |
| FakeContentLayerClient client_; |
| }; |
| |
| MULTI_THREAD_TEST_F( |
| LayerTreeHostAnimationTestRebuildPropertyTreesOnAnimationSetNeedsCommit); |
| |
| } // namespace |
| } // namespace cc |