1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "cc/animation/animation_host.h"
6 
7 #include "base/memory/ptr_util.h"
8 #include "cc/animation/animation_id_provider.h"
9 #include "cc/animation/animation_timeline.h"
10 #include "cc/animation/scroll_timeline.h"
11 #include "cc/animation/worklet_animation.h"
12 #include "cc/test/animation_test_common.h"
13 #include "cc/test/animation_timelines_test_common.h"
14 #include "cc/test/mock_layer_tree_mutator.h"
15 #include "cc/trees/scroll_node.h"
16 #include "cc/trees/transform_node.h"
17 #include "testing/gmock/include/gmock/gmock.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 
20 using ::testing::InvokeWithoutArgs;
21 using ::testing::Mock;
22 using ::testing::NiceMock;
23 using ::testing::Return;
24 using ::testing::_;
25 
26 namespace cc {
27 namespace {
28 
29 class AnimationHostTest : public AnimationTimelinesTest {
30  public:
31   AnimationHostTest() = default;
32   ~AnimationHostTest() override = default;
33 
AttachWorkletAnimation()34   void AttachWorkletAnimation() {
35     client_.RegisterElementId(element_id_, ElementListType::ACTIVE);
36     client_impl_.RegisterElementId(element_id_, ElementListType::PENDING);
37     client_impl_.RegisterElementId(element_id_, ElementListType::ACTIVE);
38 
39     worklet_animation_ = WorkletAnimation::Create(
40         worklet_animation_id_, "test_name", 1, nullptr, nullptr);
41     int cc_id = worklet_animation_->id();
42     worklet_animation_->AttachElement(element_id_);
43     host_->AddAnimationTimeline(timeline_);
44     timeline_->AttachAnimation(worklet_animation_);
45 
46     host_->PushPropertiesTo(host_impl_);
47     timeline_impl_ = host_impl_->GetTimelineById(timeline_id_);
48     worklet_animation_impl_ =
49         ToWorkletAnimation(timeline_impl_->GetAnimationById(cc_id));
50   }
51 
SetOutputState(base::TimeDelta local_time)52   void SetOutputState(base::TimeDelta local_time) {
53     MutatorOutputState::AnimationState state(worklet_animation_id_);
54     state.local_times.push_back(local_time);
55     worklet_animation_impl_->SetOutputState(state);
56   }
57 
58   scoped_refptr<WorkletAnimation> worklet_animation_;
59   scoped_refptr<WorkletAnimation> worklet_animation_impl_;
60   WorkletAnimationId worklet_animation_id_{11, 12};
61 };
62 
63 // See Animation tests on layer registration/unregistration in
64 // animation_unittest.cc.
65 
TEST_F(AnimationHostTest,SyncTimelinesAddRemove)66 TEST_F(AnimationHostTest, SyncTimelinesAddRemove) {
67   std::unique_ptr<AnimationHost> host(
68       AnimationHost::CreateForTesting(ThreadInstance::MAIN));
69   std::unique_ptr<AnimationHost> host_impl(
70       AnimationHost::CreateForTesting(ThreadInstance::IMPL));
71 
72   const int timeline_id = AnimationIdProvider::NextTimelineId();
73   scoped_refptr<AnimationTimeline> timeline(
74       AnimationTimeline::Create(timeline_id));
75   host->AddAnimationTimeline(timeline.get());
76   EXPECT_TRUE(timeline->animation_host());
77 
78   EXPECT_FALSE(host_impl->GetTimelineById(timeline_id));
79 
80   host->PushPropertiesTo(host_impl.get());
81 
82   scoped_refptr<AnimationTimeline> timeline_impl =
83       host_impl->GetTimelineById(timeline_id);
84   EXPECT_TRUE(timeline_impl);
85   EXPECT_EQ(timeline_impl->id(), timeline_id);
86 
87   host->PushPropertiesTo(host_impl.get());
88   EXPECT_EQ(timeline_impl, host_impl->GetTimelineById(timeline_id));
89 
90   host->RemoveAnimationTimeline(timeline.get());
91   EXPECT_FALSE(timeline->animation_host());
92 
93   host->PushPropertiesTo(host_impl.get());
94   EXPECT_FALSE(host_impl->GetTimelineById(timeline_id));
95 
96   EXPECT_FALSE(timeline_impl->animation_host());
97 }
98 
TEST_F(AnimationHostTest,ImplOnlyTimeline)99 TEST_F(AnimationHostTest, ImplOnlyTimeline) {
100   std::unique_ptr<AnimationHost> host(
101       AnimationHost::CreateForTesting(ThreadInstance::MAIN));
102   std::unique_ptr<AnimationHost> host_impl(
103       AnimationHost::CreateForTesting(ThreadInstance::IMPL));
104 
105   const int timeline_id1 = AnimationIdProvider::NextTimelineId();
106   const int timeline_id2 = AnimationIdProvider::NextTimelineId();
107 
108   scoped_refptr<AnimationTimeline> timeline(
109       AnimationTimeline::Create(timeline_id1));
110   scoped_refptr<AnimationTimeline> timeline_impl(
111       AnimationTimeline::Create(timeline_id2));
112   timeline_impl->set_is_impl_only(true);
113 
114   host->AddAnimationTimeline(timeline.get());
115   host_impl->AddAnimationTimeline(timeline_impl.get());
116 
117   host->PushPropertiesTo(host_impl.get());
118 
119   EXPECT_TRUE(host->GetTimelineById(timeline_id1));
120   EXPECT_TRUE(host_impl->GetTimelineById(timeline_id2));
121 }
122 
TEST_F(AnimationHostTest,ImplOnlyScrollAnimationUpdateTargetIfDetached)123 TEST_F(AnimationHostTest, ImplOnlyScrollAnimationUpdateTargetIfDetached) {
124   client_.RegisterElementId(element_id_, ElementListType::ACTIVE);
125   client_impl_.RegisterElementId(element_id_, ElementListType::PENDING);
126 
127   gfx::ScrollOffset target_offset(0., 2.);
128   gfx::ScrollOffset current_offset(0., 1.);
129   host_impl_->ImplOnlyScrollAnimationCreate(element_id_, target_offset,
130                                             current_offset, base::TimeDelta(),
131                                             base::TimeDelta());
132 
133   gfx::Vector2dF scroll_delta(0, 0.5);
134   gfx::ScrollOffset max_scroll_offset(0., 3.);
135 
136   base::TimeTicks time;
137 
138   time += base::TimeDelta::FromSecondsD(0.1);
139   EXPECT_TRUE(host_impl_->ImplOnlyScrollAnimationUpdateTarget(
140       scroll_delta, max_scroll_offset, time, base::TimeDelta()));
141 
142   // Detach all animations from layers and timelines.
143   host_impl_->ClearMutators();
144 
145   time += base::TimeDelta::FromSecondsD(0.1);
146   EXPECT_FALSE(host_impl_->ImplOnlyScrollAnimationUpdateTarget(
147       scroll_delta, max_scroll_offset, time, base::TimeDelta()));
148 }
149 
150 // Tests that verify interaction of AnimationHost with LayerTreeMutator.
151 
TEST_F(AnimationHostTest,FastLayerTreeMutatorUpdateTakesEffectInSameFrame)152 TEST_F(AnimationHostTest, FastLayerTreeMutatorUpdateTakesEffectInSameFrame) {
153   AttachWorkletAnimation();
154 
155   const float start_opacity = .7f;
156   const float end_opacity = .3f;
157   const double duration = 1.;
158 
159   const float expected_opacity =
160       start_opacity + (end_opacity - start_opacity) / 2;
161   AddOpacityTransitionToAnimation(worklet_animation_.get(), duration,
162                                   start_opacity, end_opacity, true);
163 
164   base::TimeDelta local_time = base::TimeDelta::FromSecondsD(duration / 2);
165 
166   MockLayerTreeMutator* mock_mutator = new NiceMock<MockLayerTreeMutator>();
167   host_impl_->SetLayerTreeMutator(
168       base::WrapUnique<LayerTreeMutator>(mock_mutator));
169   ON_CALL(*mock_mutator, HasMutators()).WillByDefault(Return(true));
170   ON_CALL(*mock_mutator, MutateRef(_))
171       .WillByDefault(InvokeWithoutArgs(
172           [this, local_time]() { this->SetOutputState(local_time); }));
173 
174   // Push the opacity animation to the impl thread.
175   host_->PushPropertiesTo(host_impl_);
176   host_impl_->ActivateAnimations(nullptr);
177 
178   // Ticking host should cause layer tree mutator to update output state which
179   // should take effect in the same animation frame.
180   TickAnimationsTransferEvents(base::TimeTicks(), 0u);
181 
182   // Emulate behavior in PrepareToDraw. Animation worklet updates are best
183   // effort, and the animation tick is deferred until draw to allow time for the
184   // updates to arrive.
185   host_impl_->TickWorkletAnimations();
186 
187   TestLayer* layer =
188       client_.FindTestLayer(element_id_, ElementListType::ACTIVE);
189   EXPECT_FALSE(layer->is_property_mutated(TargetProperty::OPACITY));
190   client_impl_.ExpectOpacityPropertyMutated(
191       element_id_, ElementListType::ACTIVE, expected_opacity);
192 }
193 
TEST_F(AnimationHostTest,LayerTreeMutatorsIsMutatedWithCorrectInputState)194 TEST_F(AnimationHostTest, LayerTreeMutatorsIsMutatedWithCorrectInputState) {
195   AttachWorkletAnimation();
196 
197   MockLayerTreeMutator* mock_mutator = new NiceMock<MockLayerTreeMutator>();
198   host_impl_->SetLayerTreeMutator(
199       base::WrapUnique<LayerTreeMutator>(mock_mutator));
200   ON_CALL(*mock_mutator, HasMutators()).WillByDefault(Return(true));
201 
202   const float start_opacity = .7f;
203   const float end_opacity = .3f;
204   const double duration = 1.;
205 
206   AddOpacityTransitionToAnimation(worklet_animation_.get(), duration,
207                                   start_opacity, end_opacity, true);
208 
209   host_->PushPropertiesTo(host_impl_);
210   host_impl_->ActivateAnimations(nullptr);
211 
212   EXPECT_CALL(*mock_mutator, MutateRef(_));
213 
214   base::TimeTicks time;
215   time += base::TimeDelta::FromSecondsD(0.1);
216   TickAnimationsTransferEvents(time, 0u);
217 }
218 
TEST_F(AnimationHostTest,LayerTreeMutatorsIsMutatedOnlyWhenInputChanges)219 TEST_F(AnimationHostTest, LayerTreeMutatorsIsMutatedOnlyWhenInputChanges) {
220   AttachWorkletAnimation();
221 
222   MockLayerTreeMutator* mock_mutator = new NiceMock<MockLayerTreeMutator>();
223   host_impl_->SetLayerTreeMutator(
224       base::WrapUnique<LayerTreeMutator>(mock_mutator));
225   ON_CALL(*mock_mutator, HasMutators()).WillByDefault(Return(true));
226 
227   const float start_opacity = .7f;
228   const float end_opacity = .3f;
229   const double duration = 1.;
230 
231   AddOpacityTransitionToAnimation(worklet_animation_.get(), duration,
232                                   start_opacity, end_opacity, true);
233 
234   host_->PushPropertiesTo(host_impl_);
235   host_impl_->ActivateAnimations(nullptr);
236 
237   EXPECT_CALL(*mock_mutator, MutateRef(_)).Times(1);
238 
239   base::TimeTicks time;
240   time += base::TimeDelta::FromSecondsD(0.1);
241   TickAnimationsTransferEvents(time, 0u);
242 
243   // The time has not changed which means worklet animation input is the same.
244   // Ticking animations again should not result in mutator being asked to
245   // mutate.
246   TickAnimationsTransferEvents(time, 0u);
247 }
248 
249 class MockAnimation : public Animation {
250  public:
MockAnimation(int id)251   explicit MockAnimation(int id) : Animation(id) {}
252   MOCK_METHOD1(Tick, void(base::TimeTicks monotonic_time));
253 
254  private:
~MockAnimation()255   ~MockAnimation() {}
256 };
257 
Animation1TimeEquals20(MutatorInputState * input)258 bool Animation1TimeEquals20(MutatorInputState* input) {
259   std::unique_ptr<AnimationWorkletInput> in = input->TakeWorkletState(333);
260   return in && in->added_and_updated_animations.size() == 1 &&
261          in->added_and_updated_animations[0]
262                  .worklet_animation_id.animation_id == 22 &&
263          in->added_and_updated_animations[0].current_time == 20;
264 }
265 
CreateScrollingNodeForElement(ElementId element_id,PropertyTrees * property_trees)266 void CreateScrollingNodeForElement(ElementId element_id,
267                                    PropertyTrees* property_trees) {
268   // A scrolling node in cc has a corresponding transform node (See
269   // |ScrollNode::transform_id|). This setup here creates both nodes and links
270   // them as they would normally be. This more complete setup is necessary here
271   // because ScrollTimeline depends on both nodes for its calculations.
272   TransformNode transform_node;
273   transform_node.scrolls = true;
274   int transform_node_id =
275       property_trees->transform_tree.Insert(transform_node, 0);
276   property_trees->element_id_to_transform_node_index[element_id] =
277       transform_node_id;
278 
279   ScrollNode scroll_node;
280   scroll_node.scrollable = true;
281   // Setup scroll dimention to be 100x100.
282   scroll_node.bounds = gfx::Size(200, 200);
283   scroll_node.container_bounds = gfx::Size(100, 100);
284   scroll_node.element_id = element_id;
285   scroll_node.transform_id = transform_node_id;
286 
287   int scroll_node_id = property_trees->scroll_tree.Insert(scroll_node, 0);
288   property_trees->element_id_to_scroll_node_index[element_id] = scroll_node_id;
289 }
290 
SetScrollOffset(PropertyTrees * property_trees,ElementId element_id,gfx::ScrollOffset offset)291 void SetScrollOffset(PropertyTrees* property_trees,
292                      ElementId element_id,
293                      gfx::ScrollOffset offset) {
294   // Update both scroll and transform trees
295   property_trees->scroll_tree.SetScrollOffset(element_id, offset);
296   TransformNode* transform_node =
297       property_trees->transform_tree.FindNodeFromElementId(element_id);
298   transform_node->scroll_offset = offset;
299   transform_node->needs_local_transform_update = true;
300 }
301 
TEST_F(AnimationHostTest,LayerTreeMutatorUpdateReflectsScrollAnimations)302 TEST_F(AnimationHostTest, LayerTreeMutatorUpdateReflectsScrollAnimations) {
303   ElementId element_id = element_id_;
304   int animation_id1 = 11;
305   int animation_id2 = 12;
306   WorkletAnimationId worklet_animation_id{333, 22};
307 
308   client_.RegisterElementId(element_id, ElementListType::ACTIVE);
309   client_impl_.RegisterElementId(element_id, ElementListType::PENDING);
310   client_impl_.RegisterElementId(element_id, ElementListType::ACTIVE);
311   host_impl_->AddAnimationTimeline(timeline_);
312 
313   PropertyTrees property_trees;
314   property_trees.is_main_thread = false;
315   property_trees.is_active = true;
316   CreateScrollingNodeForElement(element_id, &property_trees);
317 
318   // Set an initial scroll value.
319   SetScrollOffset(&property_trees, element_id, gfx::ScrollOffset(10, 10));
320 
321   scoped_refptr<MockAnimation> mock_scroll_animation(
322       new MockAnimation(animation_id1));
323   EXPECT_CALL(*mock_scroll_animation, Tick(_))
324       .WillOnce(InvokeWithoutArgs([&]() {
325         // Scroll to 20% of the max value.
326         SetScrollOffset(&property_trees, element_id, gfx::ScrollOffset(20, 20));
327       }));
328 
329   // Ensure scroll animation is ticking.
330   timeline_->AttachAnimation(mock_scroll_animation);
331   host_impl_->AddToTicking(mock_scroll_animation);
332 
333   // Create scroll timeline that links scroll animation and worklet animation
334   // together. Use timerange so that we have 1:1 time & scroll mapping.
335   auto scroll_timeline =
336       ScrollTimeline::Create(element_id, ScrollTimeline::ScrollDown,
337                              base::nullopt, base::nullopt, 100);
338 
339   // Create a worklet animation that is bound to the scroll timeline.
340   scoped_refptr<WorkletAnimation> worklet_animation(
341       new WorkletAnimation(animation_id2, worklet_animation_id, "test_name", 1,
342                            nullptr, nullptr, true));
343   host_impl_->AddAnimationTimeline(scroll_timeline);
344   scroll_timeline->AttachAnimation(worklet_animation);
345 
346   worklet_animation->AttachElement(element_id);
347 
348   AddOpacityTransitionToAnimation(worklet_animation.get(), 1, .7f, .3f, true);
349 
350   MockLayerTreeMutator* mock_mutator = new NiceMock<MockLayerTreeMutator>();
351   host_impl_->SetLayerTreeMutator(
352       base::WrapUnique<LayerTreeMutator>(mock_mutator));
353   ON_CALL(*mock_mutator, HasMutators()).WillByDefault(Return(true));
354   EXPECT_CALL(*mock_mutator,
355               MutateRef(::testing::Truly(Animation1TimeEquals20)))
356       .Times(1);
357 
358   // Ticking host should cause scroll animation to scroll which should also be
359   // reflected in the input of the layer tree mutator in the same animation
360   // frame.
361   host_impl_->TickAnimations(base::TimeTicks(), property_trees.scroll_tree,
362                              false);
363 }
364 
365 }  // namespace
366 }  // namespace cc
367