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 class GenericFlingAnimation: public AsyncPanZoomAnimation { 31 public: GenericFlingAnimation(AsyncPanZoomController & aApzc,PlatformSpecificStateBase * aPlatformSpecificState,const RefPtr<const OverscrollHandoffChain> & aOverscrollHandoffChain,bool aFlingIsHandedOff,const RefPtr<const AsyncPanZoomController> & aScrolledApzc)32 GenericFlingAnimation(AsyncPanZoomController& aApzc, 33 PlatformSpecificStateBase* aPlatformSpecificState, 34 const RefPtr<const OverscrollHandoffChain>& aOverscrollHandoffChain, 35 bool aFlingIsHandedOff, 36 const RefPtr<const AsyncPanZoomController>& aScrolledApzc) 37 : mApzc(aApzc) 38 , mOverscrollHandoffChain(aOverscrollHandoffChain) 39 , mScrolledApzc(aScrolledApzc) 40 { 41 MOZ_ASSERT(mOverscrollHandoffChain); 42 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 if (!mOverscrollHandoffChain->CanScrollInDirection(&mApzc, Layer::HORIZONTAL)) { 49 ReentrantMonitorAutoEnter lock(mApzc.mMonitor); 50 mApzc.mX.SetVelocity(0); 51 } 52 if (!mOverscrollHandoffChain->CanScrollInDirection(&mApzc, Layer::VERTICAL)) { 53 ReentrantMonitorAutoEnter lock(mApzc.mMonitor); 54 mApzc.mY.SetVelocity(0); 55 } 56 57 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 bool applyAcceleration = !aFlingIsHandedOff; 69 if (applyAcceleration && !mApzc.mLastFlingTime.IsNull() 70 && (now - mApzc.mLastFlingTime).ToMilliseconds() < gfxPrefs::APZFlingAccelInterval() 71 && velocity.Length() >= gfxPrefs::APZFlingAccelMinVelocity()) { 72 if (SameDirection(velocity.x, mApzc.mLastFlingVelocity.x)) { 73 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 mApzc.mX.SetVelocity(velocity.x); 77 } 78 if (SameDirection(velocity.y, mApzc.mLastFlingVelocity.y)) { 79 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 mApzc.mY.SetVelocity(velocity.y); 83 } 84 } 85 86 mApzc.mLastFlingTime = now; 87 mApzc.mLastFlingVelocity = velocity; 88 } 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 */ DoSample(FrameMetrics & aFrameMetrics,const TimeDuration & aDelta)96 virtual bool DoSample(FrameMetrics& aFrameMetrics, 97 const TimeDuration& aDelta) override 98 { 99 float friction = gfxPrefs::APZFlingFriction(); 100 float threshold = gfxPrefs::APZFlingStoppedThreshold(); 101 102 bool shouldContinueFlingX = mApzc.mX.FlingApplyFrictionOrCancel(aDelta, friction, threshold), 103 shouldContinueFlingY = mApzc.mY.FlingApplyFrictionOrCancel(aDelta, friction, threshold); 104 // If we shouldn't continue the fling, let's just stop and repaint. 105 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 mDeferredTasks.AppendElement( 115 NewRunnableMethod<AsyncPanZoomController*>(mOverscrollHandoffChain.get(), 116 &OverscrollHandoffChain::SnapBackOverscrolledApzc, 117 &mApzc)); 118 return false; 119 } 120 121 // AdjustDisplacement() zeroes out the Axis velocity if we're in overscroll. 122 // Since we need to hand off the velocity to the tree manager in such a case, 123 // we save it here. Would be ParentLayerVector instead of ParentLayerPoint 124 // if we had vector classes. 125 ParentLayerPoint velocity = mApzc.GetVelocityVector(); 126 127 ParentLayerPoint offset = velocity * aDelta.ToMilliseconds(); 128 129 // Ordinarily we might need to do a ScheduleComposite if either of 130 // the following AdjustDisplacement calls returns true, but this 131 // is already running as part of a FlingAnimation, so we'll be compositing 132 // per frame of animation anyway. 133 ParentLayerPoint overscroll; 134 ParentLayerPoint adjustedOffset; 135 mApzc.mX.AdjustDisplacement(offset.x, adjustedOffset.x, overscroll.x); 136 mApzc.mY.AdjustDisplacement(offset.y, adjustedOffset.y, overscroll.y); 137 138 aFrameMetrics.ScrollBy(adjustedOffset / aFrameMetrics.GetZoom()); 139 140 // The fling may have caused us to reach the end of our scroll range. 141 if (!IsZero(overscroll)) { 142 // Hand off the fling to the next APZC in the overscroll handoff chain. 143 144 // We may have reached the end of the scroll range along one axis but 145 // not the other. In such a case we only want to hand off the relevant 146 // component of the fling. 147 if (FuzzyEqualsAdditive(overscroll.x, 0.0f, COORDINATE_EPSILON)) { 148 velocity.x = 0; 149 } else if (FuzzyEqualsAdditive(overscroll.y, 0.0f, COORDINATE_EPSILON)) { 150 velocity.y = 0; 151 } 152 153 // To hand off the fling, we attempt to find a target APZC and start a new 154 // fling with the same velocity on that APZC. For simplicity, the actual 155 // overscroll of the current sample is discarded rather than being handed 156 // off. The compositor should sample animations sufficiently frequently 157 // that this is not noticeable. The target APZC is chosen by seeing if 158 // there is an APZC further in the handoff chain which is pannable; if 159 // there isn't, we take the new fling ourselves, entering an overscrolled 160 // state. 161 // Note: APZC is holding mMonitor, so directly calling 162 // HandleFlingOverscroll() (which acquires the tree lock) would violate 163 // the lock ordering. Instead we schedule HandleFlingOverscroll() to be 164 // called after mMonitor is released. 165 FLING_LOG("%p fling went into overscroll, handing off with velocity %s\n", &mApzc, Stringify(velocity).c_str()); 166 mDeferredTasks.AppendElement( 167 NewRunnableMethod<ParentLayerPoint, 168 RefPtr<const OverscrollHandoffChain>, 169 RefPtr<const AsyncPanZoomController>>(&mApzc, 170 &AsyncPanZoomController::HandleFlingOverscroll, 171 velocity, 172 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 185 private: SameDirection(float aVelocity1,float aVelocity2)186 static bool SameDirection(float aVelocity1, float aVelocity2) 187 { 188 return (aVelocity1 == 0.0f) 189 || (aVelocity2 == 0.0f) 190 || (IsNegative(aVelocity1) == IsNegative(aVelocity2)); 191 } 192 Accelerate(float aBase,float aSupplemental)193 static float Accelerate(float aBase, float aSupplemental) 194 { 195 return (aBase * gfxPrefs::APZFlingAccelBaseMultiplier()) 196 + (aSupplemental * gfxPrefs::APZFlingAccelSupplementalMultiplier()); 197 } 198 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_ 208