1 // Copyright 2014 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 "third_party/blink/renderer/core/scroll/scroll_animator_compositor_coordinator.h"
6
7 #include <memory>
8
9 #include "cc/animation/animation_host.h"
10 #include "cc/animation/scroll_offset_animation_curve.h"
11 #include "cc/layers/picture_layer.h"
12 #include "third_party/blink/public/platform/platform.h"
13 #include "third_party/blink/renderer/core/scroll/scrollable_area.h"
14 #include "third_party/blink/renderer/platform/animation/compositor_animation.h"
15 #include "third_party/blink/renderer/platform/animation/compositor_animation_timeline.h"
16 #include "third_party/blink/renderer/platform/animation/compositor_keyframe_model.h"
17 #include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
18 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
19
20 namespace blink {
21
ScrollAnimatorCompositorCoordinator()22 ScrollAnimatorCompositorCoordinator::ScrollAnimatorCompositorCoordinator()
23 : element_id_(),
24 run_state_(RunState::kIdle),
25 impl_only_animation_takeover_(false),
26 compositor_animation_id_(0),
27 compositor_animation_group_id_(0) {
28 compositor_animation_ = CompositorAnimation::Create();
29 DCHECK(compositor_animation_);
30 compositor_animation_->SetAnimationDelegate(this);
31 }
32
33 ScrollAnimatorCompositorCoordinator::~ScrollAnimatorCompositorCoordinator() =
34 default;
35
Dispose()36 void ScrollAnimatorCompositorCoordinator::Dispose() {
37 compositor_animation_->SetAnimationDelegate(nullptr);
38 compositor_animation_.reset();
39 }
40
ResetAnimationState()41 void ScrollAnimatorCompositorCoordinator::ResetAnimationState() {
42 run_state_ = RunState::kIdle;
43 RemoveAnimation();
44 }
45
HasAnimationThatRequiresService() const46 bool ScrollAnimatorCompositorCoordinator::HasAnimationThatRequiresService()
47 const {
48 if (HasImplOnlyAnimationUpdate())
49 return true;
50
51 switch (run_state_) {
52 case RunState::kIdle:
53 case RunState::kRunningOnCompositor:
54 return false;
55 case RunState::kWaitingToCancelOnCompositorButNewScroll:
56 case RunState::kPostAnimationCleanup:
57 case RunState::kWaitingToSendToCompositor:
58 case RunState::kRunningOnMainThread:
59 case RunState::kRunningOnCompositorButNeedsUpdate:
60 case RunState::kRunningOnCompositorButNeedsTakeover:
61 case RunState::kRunningOnCompositorButNeedsAdjustment:
62 case RunState::kWaitingToCancelOnCompositor:
63 return true;
64 }
65 NOTREACHED();
66 return false;
67 }
68
AddAnimation(std::unique_ptr<CompositorKeyframeModel> keyframe_model)69 bool ScrollAnimatorCompositorCoordinator::AddAnimation(
70 std::unique_ptr<CompositorKeyframeModel> keyframe_model) {
71 RemoveAnimation();
72 if (compositor_animation_->IsElementAttached()) {
73 compositor_animation_id_ = keyframe_model->Id();
74 compositor_animation_group_id_ = keyframe_model->Group();
75 compositor_animation_->AddKeyframeModel(std::move(keyframe_model));
76 return true;
77 }
78 return false;
79 }
80
RemoveAnimation()81 void ScrollAnimatorCompositorCoordinator::RemoveAnimation() {
82 if (compositor_animation_id_) {
83 compositor_animation_->RemoveKeyframeModel(compositor_animation_id_);
84 compositor_animation_id_ = 0;
85 compositor_animation_group_id_ = 0;
86 }
87 }
88
AbortAnimation()89 void ScrollAnimatorCompositorCoordinator::AbortAnimation() {
90 if (compositor_animation_id_) {
91 compositor_animation_->AbortKeyframeModel(compositor_animation_id_);
92 compositor_animation_id_ = 0;
93 compositor_animation_group_id_ = 0;
94 }
95 }
96
CancelAnimation()97 void ScrollAnimatorCompositorCoordinator::CancelAnimation() {
98 switch (run_state_) {
99 case RunState::kIdle:
100 case RunState::kWaitingToCancelOnCompositor:
101 case RunState::kPostAnimationCleanup:
102 break;
103 case RunState::kWaitingToSendToCompositor:
104 if (compositor_animation_id_) {
105 // We still have a previous animation running on the compositor.
106 run_state_ = RunState::kWaitingToCancelOnCompositor;
107 } else {
108 ResetAnimationState();
109 }
110 break;
111 case RunState::kRunningOnMainThread:
112 run_state_ = RunState::kPostAnimationCleanup;
113 break;
114 case RunState::kWaitingToCancelOnCompositorButNewScroll:
115 case RunState::kRunningOnCompositorButNeedsAdjustment:
116 case RunState::kRunningOnCompositorButNeedsTakeover:
117 case RunState::kRunningOnCompositorButNeedsUpdate:
118 case RunState::kRunningOnCompositor:
119 run_state_ = RunState::kWaitingToCancelOnCompositor;
120
121 // Get serviced the next time compositor updates are allowed.
122 GetScrollableArea()->RegisterForAnimation();
123 }
124 }
125
TakeOverCompositorAnimation()126 void ScrollAnimatorCompositorCoordinator::TakeOverCompositorAnimation() {
127 switch (run_state_) {
128 case RunState::kIdle:
129 TakeOverImplOnlyScrollOffsetAnimation();
130 break;
131 case RunState::kWaitingToCancelOnCompositor:
132 case RunState::kWaitingToCancelOnCompositorButNewScroll:
133 case RunState::kPostAnimationCleanup:
134 case RunState::kRunningOnCompositorButNeedsTakeover:
135 case RunState::kWaitingToSendToCompositor:
136 case RunState::kRunningOnMainThread:
137 break;
138 case RunState::kRunningOnCompositorButNeedsAdjustment:
139 case RunState::kRunningOnCompositorButNeedsUpdate:
140 case RunState::kRunningOnCompositor:
141 // We call abortAnimation that makes changes to the animation running on
142 // the compositor. Thus, this function should only be called when in
143 // CompositingClean state.
144 AbortAnimation();
145
146 run_state_ = RunState::kRunningOnCompositorButNeedsTakeover;
147
148 // Get serviced the next time compositor updates are allowed.
149 GetScrollableArea()->RegisterForAnimation();
150 }
151 }
152
CompositorAnimationFinished(int group_id)153 void ScrollAnimatorCompositorCoordinator::CompositorAnimationFinished(
154 int group_id) {
155 if (compositor_animation_group_id_ != group_id)
156 return;
157
158 // TODO(crbug.com/992437) We should not need to remove completed animations
159 // however they are sometimes accidentally restarted if we don't explicitly
160 // remove them.
161 RemoveAnimation();
162
163 switch (run_state_) {
164 case RunState::kIdle:
165 case RunState::kPostAnimationCleanup:
166 case RunState::kRunningOnMainThread:
167 NOTREACHED();
168 break;
169 case RunState::kWaitingToSendToCompositor:
170 case RunState::kWaitingToCancelOnCompositorButNewScroll:
171 break;
172 case RunState::kRunningOnCompositor:
173 case RunState::kRunningOnCompositorButNeedsAdjustment:
174 case RunState::kRunningOnCompositorButNeedsUpdate:
175 case RunState::kRunningOnCompositorButNeedsTakeover:
176 case RunState::kWaitingToCancelOnCompositor:
177 run_state_ = RunState::kPostAnimationCleanup;
178 // Get serviced the next time compositor updates are allowed.
179 if (GetScrollableArea())
180 GetScrollableArea()->RegisterForAnimation();
181 else
182 ResetAnimationState();
183 }
184 }
185
ReattachCompositorAnimationIfNeeded(CompositorAnimationTimeline * timeline)186 bool ScrollAnimatorCompositorCoordinator::ReattachCompositorAnimationIfNeeded(
187 CompositorAnimationTimeline* timeline) {
188 bool reattached = false;
189 CompositorElementId element_id = GetScrollElementId();
190 DCHECK(element_id || (RuntimeEnabledFeatures::CompositeAfterPaintEnabled() ||
191 !GetScrollableArea()->LayerForScrolling()));
192
193 if (element_id != element_id_) {
194 if (compositor_animation_ && timeline) {
195 // Detach from old layer (if any).
196 if (element_id_) {
197 if (compositor_animation_->IsElementAttached())
198 compositor_animation_->DetachElement();
199 timeline->AnimationDestroyed(*this);
200 }
201 // Attach to new layer (if any).
202 if (element_id) {
203 DCHECK(!compositor_animation_->IsElementAttached());
204 timeline->AnimationAttached(*this);
205 compositor_animation_->AttachElement(element_id);
206 reattached = true;
207 }
208 element_id_ = element_id;
209 }
210 }
211
212 return reattached;
213 }
214
NotifyAnimationStarted(double monotonic_time,int group)215 void ScrollAnimatorCompositorCoordinator::NotifyAnimationStarted(
216 double monotonic_time,
217 int group) {}
218
NotifyAnimationFinished(double monotonic_time,int group)219 void ScrollAnimatorCompositorCoordinator::NotifyAnimationFinished(
220 double monotonic_time,
221 int group) {
222 NotifyCompositorAnimationFinished(group);
223 }
224
NotifyAnimationAborted(double monotonic_time,int group)225 void ScrollAnimatorCompositorCoordinator::NotifyAnimationAborted(
226 double monotonic_time,
227 int group) {
228 // An animation aborted by the compositor is treated as a finished
229 // animation.
230 NotifyCompositorAnimationFinished(group);
231 }
232
233 CompositorAnimation*
GetCompositorAnimation() const234 ScrollAnimatorCompositorCoordinator::GetCompositorAnimation() const {
235 return compositor_animation_.get();
236 }
237
CompositorOffsetFromBlinkOffset(ScrollOffset offset)238 FloatPoint ScrollAnimatorCompositorCoordinator::CompositorOffsetFromBlinkOffset(
239 ScrollOffset offset) {
240 return GetScrollableArea()->ScrollOffsetToPosition(offset);
241 }
242
243 ScrollOffset
BlinkOffsetFromCompositorOffset(FloatPoint position)244 ScrollAnimatorCompositorCoordinator::BlinkOffsetFromCompositorOffset(
245 FloatPoint position) {
246 return GetScrollableArea()->ScrollPositionToOffset(position);
247 }
248
HasImplOnlyAnimationUpdate() const249 bool ScrollAnimatorCompositorCoordinator::HasImplOnlyAnimationUpdate() const {
250 return !impl_only_animation_adjustment_.IsZero() ||
251 impl_only_animation_takeover_;
252 }
253
GetScrollElementId() const254 CompositorElementId ScrollAnimatorCompositorCoordinator::GetScrollElementId()
255 const {
256 if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
257 return GetScrollableArea()->GetScrollElementId();
258
259 cc::Layer* layer = GetScrollableArea()->LayerForScrolling();
260 return layer ? layer->element_id() : CompositorElementId();
261 }
262
UpdateImplOnlyCompositorAnimations()263 void ScrollAnimatorCompositorCoordinator::UpdateImplOnlyCompositorAnimations() {
264 if (!HasImplOnlyAnimationUpdate())
265 return;
266
267 cc::AnimationHost* host = GetScrollableArea()->GetCompositorAnimationHost();
268 CompositorElementId element_id = GetScrollElementId();
269 if (host && element_id) {
270 if (!impl_only_animation_adjustment_.IsZero()) {
271 host->scroll_offset_animations().AddAdjustmentUpdate(
272 element_id, gfx::Vector2dF(impl_only_animation_adjustment_.Width(),
273 impl_only_animation_adjustment_.Height()));
274 }
275 if (impl_only_animation_takeover_)
276 host->scroll_offset_animations().AddTakeoverUpdate(element_id);
277 }
278 impl_only_animation_adjustment_ = IntSize();
279 impl_only_animation_takeover_ = false;
280 }
281
UpdateCompositorAnimations()282 void ScrollAnimatorCompositorCoordinator::UpdateCompositorAnimations() {
283 if (!GetScrollableArea()->ScrollAnimatorEnabled())
284 return;
285
286 UpdateImplOnlyCompositorAnimations();
287 }
288
ScrollOffsetChanged(const ScrollOffset & offset,mojom::blink::ScrollType scroll_type)289 void ScrollAnimatorCompositorCoordinator::ScrollOffsetChanged(
290 const ScrollOffset& offset,
291 mojom::blink::ScrollType scroll_type) {
292 ScrollOffset clamped_offset = GetScrollableArea()->ClampScrollOffset(offset);
293 GetScrollableArea()->ScrollOffsetChanged(clamped_offset, scroll_type);
294 }
295
AdjustAnimationAndSetScrollOffset(const ScrollOffset & offset,mojom::blink::ScrollType scroll_type)296 void ScrollAnimatorCompositorCoordinator::AdjustAnimationAndSetScrollOffset(
297 const ScrollOffset& offset,
298 mojom::blink::ScrollType scroll_type) {
299 // Subclasses should override this and adjust the animation as necessary.
300 ScrollOffsetChanged(offset, scroll_type);
301 }
302
AdjustImplOnlyScrollOffsetAnimation(const IntSize & adjustment)303 void ScrollAnimatorCompositorCoordinator::AdjustImplOnlyScrollOffsetAnimation(
304 const IntSize& adjustment) {
305 if (!GetScrollableArea()->ScrollAnimatorEnabled())
306 return;
307
308 impl_only_animation_adjustment_.Expand(adjustment.Width(),
309 adjustment.Height());
310
311 GetScrollableArea()->RegisterForAnimation();
312 }
313
314 void ScrollAnimatorCompositorCoordinator::
TakeOverImplOnlyScrollOffsetAnimation()315 TakeOverImplOnlyScrollOffsetAnimation() {
316 if (!GetScrollableArea()->ScrollAnimatorEnabled())
317 return;
318
319 impl_only_animation_takeover_ = true;
320
321 // Update compositor animations right away to avoid skipping a frame.
322 // This imposes the constraint that this function should only be called
323 // from or after DocumentLifecycle::LifecycleState::CompositingClean state.
324 UpdateImplOnlyCompositorAnimations();
325
326 GetScrollableArea()->RegisterForAnimation();
327 }
328
RunStateAsText() const329 String ScrollAnimatorCompositorCoordinator::RunStateAsText() const {
330 switch (run_state_) {
331 case RunState::kIdle:
332 return String("Idle");
333 case RunState::kWaitingToSendToCompositor:
334 return String("WaitingToSendToCompositor");
335 case RunState::kRunningOnCompositor:
336 return String("RunningOnCompositor");
337 case RunState::kRunningOnMainThread:
338 return String("RunningOnMainThread");
339 case RunState::kRunningOnCompositorButNeedsUpdate:
340 return String("RunningOnCompositorButNeedsUpdate");
341 case RunState::kWaitingToCancelOnCompositor:
342 return String("WaitingToCancelOnCompositor");
343 case RunState::kPostAnimationCleanup:
344 return String("PostAnimationCleanup");
345 case RunState::kRunningOnCompositorButNeedsTakeover:
346 return String("RunningOnCompositorButNeedsTakeover");
347 case RunState::kWaitingToCancelOnCompositorButNewScroll:
348 return String("WaitingToCancelOnCompositorButNewScroll");
349 case RunState::kRunningOnCompositorButNeedsAdjustment:
350 return String("RunningOnCompositorButNeedsAdjustment");
351 }
352 NOTREACHED();
353 return String();
354 }
355
356 } // namespace blink
357