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 "Units.h" 16 #include "OverscrollHandoffState.h" 17 #include "mozilla/Assertions.h" 18 #include "mozilla/Monitor.h" 19 #include "mozilla/RefPtr.h" 20 #include "mozilla/StaticPrefs_apz.h" 21 #include "mozilla/TimeStamp.h" 22 #include "mozilla/ToString.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: GenericFlingAnimation(AsyncPanZoomController & aApzc,const FlingHandoffState & aHandoffState,float aPLPPI)58 GenericFlingAnimation(AsyncPanZoomController& aApzc, 59 const FlingHandoffState& aHandoffState, float aPLPPI) 60 : mApzc(aApzc), 61 mOverscrollHandoffChain(aHandoffState.mChain), 62 mScrolledApzc(aHandoffState.mScrolledApzc) { 63 MOZ_ASSERT(mOverscrollHandoffChain); 64 65 // Drop any velocity on axes where we don't have room to scroll anyways 66 // (in this APZC, or an APZC further in the handoff chain). 67 // This ensures that we don't take the 'overscroll' path in Sample() 68 // on account of one axis which can't scroll having a velocity. 69 if (!mOverscrollHandoffChain->CanScrollInDirection( 70 &mApzc, ScrollDirection::eHorizontal)) { 71 RecursiveMutexAutoLock lock(mApzc.mRecursiveMutex); 72 mApzc.mX.SetVelocity(0); 73 } 74 if (!mOverscrollHandoffChain->CanScrollInDirection( 75 &mApzc, ScrollDirection::eVertical)) { 76 RecursiveMutexAutoLock lock(mApzc.mRecursiveMutex); 77 mApzc.mY.SetVelocity(0); 78 } 79 80 if (aHandoffState.mIsHandoff) { 81 // Only apply acceleration in the APZC that originated the fling, not in 82 // APZCs further down the handoff chain during handoff. 83 mApzc.mFlingAccelerator.Reset(); 84 } 85 86 ParentLayerPoint velocity = 87 mApzc.mFlingAccelerator.GetFlingStartingVelocity( 88 aApzc.GetFrameTime(), mApzc.GetVelocityVector(), aHandoffState); 89 90 mApzc.SetVelocityVector(velocity); 91 92 FlingPhysics::Init(mApzc.GetVelocityVector(), aPLPPI); 93 } 94 95 /** 96 * Advances a fling by an interpolated amount based on the passed in |aDelta|. 97 * This should be called whenever sampling the content transform for this 98 * frame. Returns true if the fling animation should be advanced by one frame, 99 * or false if there is no fling or the fling has ended. 100 */ DoSample(FrameMetrics & aFrameMetrics,const TimeDuration & aDelta)101 virtual bool DoSample(FrameMetrics& aFrameMetrics, 102 const TimeDuration& aDelta) override { 103 ParentLayerPoint velocity; 104 ParentLayerPoint offset; 105 FlingPhysics::Sample(aDelta, &velocity, &offset); 106 107 mApzc.SetVelocityVector(velocity); 108 109 // If we shouldn't continue the fling, let's just stop and repaint. 110 if (IsZero(velocity)) { 111 FLING_LOG("%p ending fling animation. overscrolled=%d\n", &mApzc, 112 mApzc.IsOverscrolled()); 113 // This APZC or an APZC further down the handoff chain may be be 114 // overscrolled. Start a snap-back animation on the overscrolled APZC. 115 // Note: 116 // This needs to be a deferred task even though it can safely run 117 // while holding mRecursiveMutex, because otherwise, if the overscrolled 118 // APZC is this one, then the SetState(NOTHING) in UpdateAnimation will 119 // stomp on the SetState(SNAP_BACK) it does. 120 mDeferredTasks.AppendElement(NewRunnableMethod<AsyncPanZoomController*>( 121 "layers::OverscrollHandoffChain::SnapBackOverscrolledApzc", 122 mOverscrollHandoffChain.get(), 123 &OverscrollHandoffChain::SnapBackOverscrolledApzc, &mApzc)); 124 return false; 125 } 126 127 // Ordinarily we might need to do a ScheduleComposite if either of 128 // the following AdjustDisplacement calls returns true, but this 129 // is already running as part of a FlingAnimation, so we'll be compositing 130 // per frame of animation anyway. 131 ParentLayerPoint overscroll; 132 ParentLayerPoint adjustedOffset; 133 mApzc.mX.AdjustDisplacement(offset.x, adjustedOffset.x, overscroll.x); 134 mApzc.mY.AdjustDisplacement(offset.y, adjustedOffset.y, overscroll.y); 135 if (aFrameMetrics.GetZoom() != CSSToParentLayerScale2D(0, 0)) { 136 mApzc.ScrollBy(adjustedOffset / aFrameMetrics.GetZoom()); 137 } 138 139 // The fling may have caused us to reach the end of our scroll range. 140 if (!IsZero(overscroll)) { 141 // Hand off the fling to the next APZC in the overscroll handoff chain. 142 143 // We may have reached the end of the scroll range along one axis but 144 // not the other. In such a case we only want to hand off the relevant 145 // component of the fling. 146 if (FuzzyEqualsAdditive(overscroll.x, 0.0f, COORDINATE_EPSILON)) { 147 velocity.x = 0; 148 } else if (FuzzyEqualsAdditive(overscroll.y, 0.0f, COORDINATE_EPSILON)) { 149 velocity.y = 0; 150 } 151 152 // To hand off the fling, we attempt to find a target APZC and start a new 153 // fling with the same velocity on that APZC. For simplicity, the actual 154 // overscroll of the current sample is discarded rather than being handed 155 // off. The compositor should sample animations sufficiently frequently 156 // that this is not noticeable. The target APZC is chosen by seeing if 157 // there is an APZC further in the handoff chain which is pannable; if 158 // there isn't, we take the new fling ourselves, entering an overscrolled 159 // state. 160 // Note: APZC is holding mRecursiveMutex, so directly calling 161 // HandleFlingOverscroll() (which acquires the tree lock) would violate 162 // the lock ordering. Instead we schedule HandleFlingOverscroll() to be 163 // called after mRecursiveMutex is released. 164 FLING_LOG("%p fling went into overscroll, handing off with velocity %s\n", 165 &mApzc, ToString(velocity).c_str()); 166 mDeferredTasks.AppendElement( 167 NewRunnableMethod<ParentLayerPoint, SideBits, 168 RefPtr<const OverscrollHandoffChain>, 169 RefPtr<const AsyncPanZoomController>>( 170 "layers::AsyncPanZoomController::HandleFlingOverscroll", &mApzc, 171 &AsyncPanZoomController::HandleFlingOverscroll, velocity, 172 apz::GetOverscrollSideBits(overscroll), mOverscrollHandoffChain, 173 mScrolledApzc)); 174 175 // If there is a remaining velocity on this APZC, continue this fling 176 // as well. (This fling and the handed-off fling will run concurrently.) 177 // Note that AdjustDisplacement() will have zeroed out the velocity 178 // along the axes where we're overscrolled. 179 return !IsZero(mApzc.GetVelocityVector()); 180 } 181 182 return true; 183 } 184 Cancel(CancelAnimationFlags aFlags)185 void Cancel(CancelAnimationFlags aFlags) override { 186 mApzc.mFlingAccelerator.ObserveFlingCanceled(mApzc.GetVelocityVector()); 187 } 188 HandleScrollOffsetUpdate(const Maybe<CSSPoint> & aRelativeDelta)189 virtual bool HandleScrollOffsetUpdate( 190 const Maybe<CSSPoint>& aRelativeDelta) override { 191 return true; 192 } 193 194 private: 195 AsyncPanZoomController& mApzc; 196 RefPtr<const OverscrollHandoffChain> mOverscrollHandoffChain; 197 RefPtr<const AsyncPanZoomController> mScrolledApzc; 198 }; 199 200 } // namespace layers 201 } // namespace mozilla 202 203 #endif // mozilla_layers_GenericFlingAnimation_h_ 204