Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
* License, v. 2.0. If a copy of the MPL was not distributed with this
5
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#ifndef mozilla_layers_GenericFlingAnimation_h_
8
#define mozilla_layers_GenericFlingAnimation_h_
9
10
#include "APZUtils.h"
11
#include "AsyncPanZoomAnimation.h"
12
#include "AsyncPanZoomController.h"
13
#include "FrameMetrics.h"
14
#include "Layers.h"
15
#include "LayersLogging.h"
16
#include "Units.h"
17
#include "OverscrollHandoffState.h"
18
#include "mozilla/Assertions.h"
19
#include "mozilla/Monitor.h"
20
#include "mozilla/RefPtr.h"
21
#include "mozilla/StaticPrefs_apz.h"
22
#include "mozilla/TimeStamp.h"
23
#include "nsThreadUtils.h"
24
25
static mozilla::LazyLogModule sApzFlgLog("apz.fling");
26
#define FLING_LOG(...) MOZ_LOG(sApzFlgLog, LogLevel::Debug, (__VA_ARGS__))
27
28
namespace mozilla {
29
namespace layers {
30
31
/**
32
* The FlingPhysics template parameter determines the physics model
33
* that the fling animation follows. It must have the following methods:
34
*
35
* - Default constructor.
36
*
37
* - Init(const ParentLayerPoint& aStartingVelocity, float aPLPPI).
38
* Called at the beginning of the fling, with the fling's starting velocity,
39
* and the number of ParentLayer pixels per (Screen) inch at the point of
40
* the fling's start in the fling's direction.
41
*
42
* - Sample(const TimeDuration& aDelta,
43
* ParentLayerPoint* aOutVelocity,
44
* ParentLayerPoint* aOutOffset);
45
* Called on each sample of the fling.
46
* |aDelta| is the time elapsed since the last sample.
47
* |aOutVelocity| should be the desired velocity after the current sample,
48
* in ParentLayer pixels per millisecond.
49
* |aOutOffset| should be the desired _delta_ to the scroll offset after
50
* the current sample. |aOutOffset| should _not_ be clamped to the APZC's
51
* scrollable bounds; the caller will do the clamping, and it needs to
52
* know the unclamped value to handle handoff/overscroll correctly.
53
*/
54
template <typename FlingPhysics>
55
class GenericFlingAnimation : public AsyncPanZoomAnimation,
56
public FlingPhysics {
57
public:
58
GenericFlingAnimation(
59
AsyncPanZoomController& aApzc,
60
const RefPtr<const OverscrollHandoffChain>& aOverscrollHandoffChain,
61
bool aFlingIsHandedOff,
62
const RefPtr<const AsyncPanZoomController>& aScrolledApzc, float aPLPPI)
63
: mApzc(aApzc),
64
mOverscrollHandoffChain(aOverscrollHandoffChain),
65
mScrolledApzc(aScrolledApzc) {
66
MOZ_ASSERT(mOverscrollHandoffChain);
67
TimeStamp now = aApzc.GetFrameTime();
68
69
// Drop any velocity on axes where we don't have room to scroll anyways
70
// (in this APZC, or an APZC further in the handoff chain).
71
// This ensures that we don't take the 'overscroll' path in Sample()
72
// on account of one axis which can't scroll having a velocity.
73
if (!mOverscrollHandoffChain->CanScrollInDirection(
74
&mApzc, ScrollDirection::eHorizontal)) {
75
RecursiveMutexAutoLock lock(mApzc.mRecursiveMutex);
76
mApzc.mX.SetVelocity(0);
77
}
78
if (!mOverscrollHandoffChain->CanScrollInDirection(
79
&mApzc, ScrollDirection::eVertical)) {
80
RecursiveMutexAutoLock lock(mApzc.mRecursiveMutex);
81
mApzc.mY.SetVelocity(0);
82
}
83
84
ParentLayerPoint velocity = mApzc.GetVelocityVector();
85
86
// If the last fling was very recent and in the same direction as this one,
87
// boost the velocity to be the sum of the two. Check separate axes
88
// separately because we could have two vertical flings with small
89
// horizontal components on the opposite side of zero, and we still want the
90
// y-fling to get accelerated. Note that the acceleration code is only
91
// applied on the APZC that initiates the fling; the accelerated velocities
92
// are then handed off using the normal DispatchFling codepath. Acceleration
93
// is only applied in the APZC that originated the fling, not in APZCs
94
// further down the handoff chain during handoff.
95
bool applyAcceleration = !aFlingIsHandedOff;
96
if (applyAcceleration && !mApzc.mLastFlingTime.IsNull() &&
97
(now - mApzc.mLastFlingTime).ToMilliseconds() <
98
StaticPrefs::apz_fling_accel_interval_ms() &&
99
velocity.Length() >= StaticPrefs::apz_fling_accel_min_velocity()) {
100
if (velocity.x != 0 &&
101
SameDirection(velocity.x, mApzc.mLastFlingVelocity.x)) {
102
velocity.x = Accelerate(velocity.x, mApzc.mLastFlingVelocity.x);
103
FLING_LOG("%p Applying fling x-acceleration from %f to %f (delta %f)\n",
104
&mApzc, mApzc.mX.GetVelocity(), velocity.x,
105
mApzc.mLastFlingVelocity.x);
106
mApzc.mX.SetVelocity(velocity.x);
107
}
108
if (velocity.y != 0 &&
109
SameDirection(velocity.y, mApzc.mLastFlingVelocity.y)) {
110
velocity.y = Accelerate(velocity.y, mApzc.mLastFlingVelocity.y);
111
FLING_LOG("%p Applying fling y-acceleration from %f to %f (delta %f)\n",
112
&mApzc, mApzc.mY.GetVelocity(), velocity.y,
113
mApzc.mLastFlingVelocity.y);
114
mApzc.mY.SetVelocity(velocity.y);
115
}
116
}
117
118
mApzc.mLastFlingTime = now;
119
mApzc.mLastFlingVelocity = velocity;
120
121
FlingPhysics::Init(mApzc.GetVelocityVector(), aPLPPI);
122
}
123
124
/**
125
* Advances a fling by an interpolated amount based on the passed in |aDelta|.
126
* This should be called whenever sampling the content transform for this
127
* frame. Returns true if the fling animation should be advanced by one frame,
128
* or false if there is no fling or the fling has ended.
129
*/
130
virtual bool DoSample(FrameMetrics& aFrameMetrics,
131
const TimeDuration& aDelta) override {
132
ParentLayerPoint velocity;
133
ParentLayerPoint offset;
134
FlingPhysics::Sample(aDelta, &velocity, &offset);
135
136
mApzc.SetVelocityVector(velocity);
137
138
// If we shouldn't continue the fling, let's just stop and repaint.
139
if (IsZero(velocity)) {
140
FLING_LOG("%p ending fling animation. overscrolled=%d\n", &mApzc,
141
mApzc.IsOverscrolled());
142
// This APZC or an APZC further down the handoff chain may be be
143
// overscrolled. Start a snap-back animation on the overscrolled APZC.
144
// Note:
145
// This needs to be a deferred task even though it can safely run
146
// while holding mRecursiveMutex, because otherwise, if the overscrolled
147
// APZC is this one, then the SetState(NOTHING) in UpdateAnimation will
148
// stomp on the SetState(SNAP_BACK) it does.
149
mDeferredTasks.AppendElement(NewRunnableMethod<AsyncPanZoomController*>(
150
"layers::OverscrollHandoffChain::SnapBackOverscrolledApzc",
151
mOverscrollHandoffChain.get(),
152
&OverscrollHandoffChain::SnapBackOverscrolledApzc, &mApzc));
153
return false;
154
}
155
156
// Ordinarily we might need to do a ScheduleComposite if either of
157
// the following AdjustDisplacement calls returns true, but this
158
// is already running as part of a FlingAnimation, so we'll be compositing
159
// per frame of animation anyway.
160
ParentLayerPoint overscroll;
161
ParentLayerPoint adjustedOffset;
162
mApzc.mX.AdjustDisplacement(offset.x, adjustedOffset.x, overscroll.x);
163
mApzc.mY.AdjustDisplacement(offset.y, adjustedOffset.y, overscroll.y);
164
165
mApzc.ScrollBy(adjustedOffset / aFrameMetrics.GetZoom());
166
167
// The fling may have caused us to reach the end of our scroll range.
168
if (!IsZero(overscroll)) {
169
// Hand off the fling to the next APZC in the overscroll handoff chain.
170
171
// We may have reached the end of the scroll range along one axis but
172
// not the other. In such a case we only want to hand off the relevant
173
// component of the fling.
174
if (FuzzyEqualsAdditive(overscroll.x, 0.0f, COORDINATE_EPSILON)) {
175
velocity.x = 0;
176
} else if (FuzzyEqualsAdditive(overscroll.y, 0.0f, COORDINATE_EPSILON)) {
177
velocity.y = 0;
178
}
179
180
// To hand off the fling, we attempt to find a target APZC and start a new
181
// fling with the same velocity on that APZC. For simplicity, the actual
182
// overscroll of the current sample is discarded rather than being handed
183
// off. The compositor should sample animations sufficiently frequently
184
// that this is not noticeable. The target APZC is chosen by seeing if
185
// there is an APZC further in the handoff chain which is pannable; if
186
// there isn't, we take the new fling ourselves, entering an overscrolled
187
// state.
188
// Note: APZC is holding mRecursiveMutex, so directly calling
189
// HandleFlingOverscroll() (which acquires the tree lock) would violate
190
// the lock ordering. Instead we schedule HandleFlingOverscroll() to be
191
// called after mRecursiveMutex is released.
192
FLING_LOG("%p fling went into overscroll, handing off with velocity %s\n",
193
&mApzc, Stringify(velocity).c_str());
194
mDeferredTasks.AppendElement(
195
NewRunnableMethod<ParentLayerPoint,
196
RefPtr<const OverscrollHandoffChain>,
197
RefPtr<const AsyncPanZoomController>>(
198
"layers::AsyncPanZoomController::HandleFlingOverscroll", &mApzc,
199
&AsyncPanZoomController::HandleFlingOverscroll, velocity,
200
mOverscrollHandoffChain, mScrolledApzc));
201
202
// If there is a remaining velocity on this APZC, continue this fling
203
// as well. (This fling and the handed-off fling will run concurrently.)
204
// Note that AdjustDisplacement() will have zeroed out the velocity
205
// along the axes where we're overscrolled.
206
return !IsZero(mApzc.GetVelocityVector());
207
}
208
209
return true;
210
}
211
212
virtual bool HandleScrollOffsetUpdate(
213
const Maybe<CSSPoint>& aRelativeDelta) override {
214
return true;
215
}
216
217
private:
218
static bool SameDirection(float aVelocity1, float aVelocity2) {
219
return (aVelocity1 == 0.0f) || (aVelocity2 == 0.0f) ||
220
(IsNegative(aVelocity1) == IsNegative(aVelocity2));
221
}
222
223
static float Accelerate(float aBase, float aSupplemental) {
224
return (aBase * StaticPrefs::apz_fling_accel_base_mult()) +
225
(aSupplemental * StaticPrefs::apz_fling_accel_supplemental_mult());
226
}
227
228
AsyncPanZoomController& mApzc;
229
RefPtr<const OverscrollHandoffChain> mOverscrollHandoffChain;
230
RefPtr<const AsyncPanZoomController> mScrolledApzc;
231
};
232
233
} // namespace layers
234
} // namespace mozilla
235
236
#endif // mozilla_layers_GenericFlingAnimation_h_