Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set sw=2 ts=8 et 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 "Units.h"
16 : #include "OverscrollHandoffState.h"
17 : #include "gfxPrefs.h"
18 : #include "mozilla/Assertions.h"
19 : #include "mozilla/Monitor.h"
20 : #include "mozilla/RefPtr.h"
21 : #include "mozilla/TimeStamp.h"
22 : #include "nsThreadUtils.h"
23 :
24 : #define FLING_LOG(...)
25 : // #define FLING_LOG(...) printf_stderr("FLING: " __VA_ARGS__)
26 :
27 : namespace mozilla {
28 : namespace layers {
29 :
30 0 : class GenericFlingAnimation: public AsyncPanZoomAnimation {
31 : public:
32 0 : GenericFlingAnimation(AsyncPanZoomController& aApzc,
33 : PlatformSpecificStateBase* aPlatformSpecificState,
34 : const RefPtr<const OverscrollHandoffChain>& aOverscrollHandoffChain,
35 : bool aFlingIsHandedOff,
36 : const RefPtr<const AsyncPanZoomController>& aScrolledApzc)
37 0 : : mApzc(aApzc)
38 : , mOverscrollHandoffChain(aOverscrollHandoffChain)
39 0 : , mScrolledApzc(aScrolledApzc)
40 : {
41 0 : MOZ_ASSERT(mOverscrollHandoffChain);
42 0 : TimeStamp now = aApzc.GetFrameTime();
43 :
44 : // Drop any velocity on axes where we don't have room to scroll anyways
45 : // (in this APZC, or an APZC further in the handoff chain).
46 : // This ensures that we don't take the 'overscroll' path in Sample()
47 : // on account of one axis which can't scroll having a velocity.
48 0 : if (!mOverscrollHandoffChain->CanScrollInDirection(&mApzc, ScrollDirection::HORIZONTAL)) {
49 0 : ReentrantMonitorAutoEnter lock(mApzc.mMonitor);
50 0 : mApzc.mX.SetVelocity(0);
51 : }
52 0 : if (!mOverscrollHandoffChain->CanScrollInDirection(&mApzc, ScrollDirection::VERTICAL)) {
53 0 : ReentrantMonitorAutoEnter lock(mApzc.mMonitor);
54 0 : mApzc.mY.SetVelocity(0);
55 : }
56 :
57 0 : ParentLayerPoint velocity = mApzc.GetVelocityVector();
58 :
59 : // If the last fling was very recent and in the same direction as this one,
60 : // boost the velocity to be the sum of the two. Check separate axes separately
61 : // because we could have two vertical flings with small horizontal components
62 : // on the opposite side of zero, and we still want the y-fling to get accelerated.
63 : // Note that the acceleration code is only applied on the APZC that initiates
64 : // the fling; the accelerated velocities are then handed off using the
65 : // normal DispatchFling codepath.
66 : // Acceleration is only applied in the APZC that originated the fling,
67 : // not in APZCs further down the handoff chain during handoff.
68 0 : bool applyAcceleration = !aFlingIsHandedOff;
69 0 : if (applyAcceleration && !mApzc.mLastFlingTime.IsNull()
70 0 : && (now - mApzc.mLastFlingTime).ToMilliseconds() < gfxPrefs::APZFlingAccelInterval()
71 0 : && velocity.Length() >= gfxPrefs::APZFlingAccelMinVelocity()) {
72 0 : if (SameDirection(velocity.x, mApzc.mLastFlingVelocity.x)) {
73 0 : velocity.x = Accelerate(velocity.x, mApzc.mLastFlingVelocity.x);
74 : FLING_LOG("%p Applying fling x-acceleration from %f to %f (delta %f)\n",
75 : &mApzc, mApzc.mX.GetVelocity(), velocity.x, mApzc.mLastFlingVelocity.x);
76 0 : mApzc.mX.SetVelocity(velocity.x);
77 : }
78 0 : if (SameDirection(velocity.y, mApzc.mLastFlingVelocity.y)) {
79 0 : velocity.y = Accelerate(velocity.y, mApzc.mLastFlingVelocity.y);
80 : FLING_LOG("%p Applying fling y-acceleration from %f to %f (delta %f)\n",
81 : &mApzc, mApzc.mY.GetVelocity(), velocity.y, mApzc.mLastFlingVelocity.y);
82 0 : mApzc.mY.SetVelocity(velocity.y);
83 : }
84 : }
85 :
86 0 : mApzc.mLastFlingTime = now;
87 0 : mApzc.mLastFlingVelocity = velocity;
88 0 : }
89 :
90 : /**
91 : * Advances a fling by an interpolated amount based on the passed in |aDelta|.
92 : * This should be called whenever sampling the content transform for this
93 : * frame. Returns true if the fling animation should be advanced by one frame,
94 : * or false if there is no fling or the fling has ended.
95 : */
96 0 : virtual bool DoSample(FrameMetrics& aFrameMetrics,
97 : const TimeDuration& aDelta) override
98 : {
99 0 : float friction = gfxPrefs::APZFlingFriction();
100 0 : float threshold = gfxPrefs::APZFlingStoppedThreshold();
101 :
102 0 : bool shouldContinueFlingX = mApzc.mX.FlingApplyFrictionOrCancel(aDelta, friction, threshold),
103 0 : shouldContinueFlingY = mApzc.mY.FlingApplyFrictionOrCancel(aDelta, friction, threshold);
104 : // If we shouldn't continue the fling, let's just stop and repaint.
105 0 : if (!shouldContinueFlingX && !shouldContinueFlingY) {
106 : FLING_LOG("%p ending fling animation. overscrolled=%d\n", &mApzc, mApzc.IsOverscrolled());
107 : // This APZC or an APZC further down the handoff chain may be be overscrolled.
108 : // Start a snap-back animation on the overscrolled APZC.
109 : // Note:
110 : // This needs to be a deferred task even though it can safely run
111 : // while holding mMonitor, because otherwise, if the overscrolled APZC
112 : // is this one, then the SetState(NOTHING) in UpdateAnimation will
113 : // stomp on the SetState(SNAP_BACK) it does.
114 0 : mDeferredTasks.AppendElement(NewRunnableMethod<AsyncPanZoomController*>(
115 : "layers::OverscrollHandoffChain::SnapBackOverscrolledApzc",
116 0 : mOverscrollHandoffChain.get(),
117 : &OverscrollHandoffChain::SnapBackOverscrolledApzc,
118 0 : &mApzc));
119 0 : return false;
120 : }
121 :
122 : // AdjustDisplacement() zeroes out the Axis velocity if we're in overscroll.
123 : // Since we need to hand off the velocity to the tree manager in such a case,
124 : // we save it here. Would be ParentLayerVector instead of ParentLayerPoint
125 : // if we had vector classes.
126 0 : ParentLayerPoint velocity = mApzc.GetVelocityVector();
127 :
128 0 : ParentLayerPoint offset = velocity * aDelta.ToMilliseconds();
129 :
130 : // Ordinarily we might need to do a ScheduleComposite if either of
131 : // the following AdjustDisplacement calls returns true, but this
132 : // is already running as part of a FlingAnimation, so we'll be compositing
133 : // per frame of animation anyway.
134 0 : ParentLayerPoint overscroll;
135 0 : ParentLayerPoint adjustedOffset;
136 0 : mApzc.mX.AdjustDisplacement(offset.x, adjustedOffset.x, overscroll.x);
137 0 : mApzc.mY.AdjustDisplacement(offset.y, adjustedOffset.y, overscroll.y);
138 :
139 0 : aFrameMetrics.ScrollBy(adjustedOffset / aFrameMetrics.GetZoom());
140 :
141 : // The fling may have caused us to reach the end of our scroll range.
142 0 : if (!IsZero(overscroll)) {
143 : // Hand off the fling to the next APZC in the overscroll handoff chain.
144 :
145 : // We may have reached the end of the scroll range along one axis but
146 : // not the other. In such a case we only want to hand off the relevant
147 : // component of the fling.
148 0 : if (FuzzyEqualsAdditive(overscroll.x, 0.0f, COORDINATE_EPSILON)) {
149 0 : velocity.x = 0;
150 0 : } else if (FuzzyEqualsAdditive(overscroll.y, 0.0f, COORDINATE_EPSILON)) {
151 0 : velocity.y = 0;
152 : }
153 :
154 : // To hand off the fling, we attempt to find a target APZC and start a new
155 : // fling with the same velocity on that APZC. For simplicity, the actual
156 : // overscroll of the current sample is discarded rather than being handed
157 : // off. The compositor should sample animations sufficiently frequently
158 : // that this is not noticeable. The target APZC is chosen by seeing if
159 : // there is an APZC further in the handoff chain which is pannable; if
160 : // there isn't, we take the new fling ourselves, entering an overscrolled
161 : // state.
162 : // Note: APZC is holding mMonitor, so directly calling
163 : // HandleFlingOverscroll() (which acquires the tree lock) would violate
164 : // the lock ordering. Instead we schedule HandleFlingOverscroll() to be
165 : // called after mMonitor is released.
166 : FLING_LOG("%p fling went into overscroll, handing off with velocity %s\n", &mApzc, Stringify(velocity).c_str());
167 0 : mDeferredTasks.AppendElement(
168 : NewRunnableMethod<ParentLayerPoint,
169 : RefPtr<const OverscrollHandoffChain>,
170 0 : RefPtr<const AsyncPanZoomController>>(
171 : "layers::AsyncPanZoomController::HandleFlingOverscroll",
172 0 : &mApzc,
173 : &AsyncPanZoomController::HandleFlingOverscroll,
174 : velocity,
175 : mOverscrollHandoffChain,
176 0 : mScrolledApzc));
177 :
178 : // If there is a remaining velocity on this APZC, continue this fling
179 : // as well. (This fling and the handed-off fling will run concurrently.)
180 : // Note that AdjustDisplacement() will have zeroed out the velocity
181 : // along the axes where we're overscrolled.
182 0 : return !IsZero(mApzc.GetVelocityVector());
183 : }
184 :
185 0 : return true;
186 : }
187 :
188 : private:
189 0 : static bool SameDirection(float aVelocity1, float aVelocity2)
190 : {
191 : return (aVelocity1 == 0.0f)
192 0 : || (aVelocity2 == 0.0f)
193 0 : || (IsNegative(aVelocity1) == IsNegative(aVelocity2));
194 : }
195 :
196 0 : static float Accelerate(float aBase, float aSupplemental)
197 : {
198 0 : return (aBase * gfxPrefs::APZFlingAccelBaseMultiplier())
199 0 : + (aSupplemental * gfxPrefs::APZFlingAccelSupplementalMultiplier());
200 : }
201 :
202 : AsyncPanZoomController& mApzc;
203 : RefPtr<const OverscrollHandoffChain> mOverscrollHandoffChain;
204 : RefPtr<const AsyncPanZoomController> mScrolledApzc;
205 : };
206 :
207 : } // namespace layers
208 : } // namespace mozilla
209 :
210 : #endif // mozilla_layers_GenericFlingAnimation_h_
|