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