summaryrefslogtreecommitdiffstats
path: root/gfx/layers/apz/src/GenericFlingAnimation.h
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/layers/apz/src/GenericFlingAnimation.h')
-rw-r--r--gfx/layers/apz/src/GenericFlingAnimation.h207
1 files changed, 207 insertions, 0 deletions
diff --git a/gfx/layers/apz/src/GenericFlingAnimation.h b/gfx/layers/apz/src/GenericFlingAnimation.h
new file mode 100644
index 000000000..deec37b47
--- /dev/null
+++ b/gfx/layers/apz/src/GenericFlingAnimation.h
@@ -0,0 +1,207 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_GenericFlingAnimation_h_
+#define mozilla_layers_GenericFlingAnimation_h_
+
+#include "APZUtils.h"
+#include "AsyncPanZoomAnimation.h"
+#include "AsyncPanZoomController.h"
+#include "FrameMetrics.h"
+#include "Layers.h"
+#include "Units.h"
+#include "OverscrollHandoffState.h"
+#include "gfxPrefs.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/TimeStamp.h"
+#include "nsThreadUtils.h"
+
+#define FLING_LOG(...)
+// #define FLING_LOG(...) printf_stderr("FLING: " __VA_ARGS__)
+
+namespace mozilla {
+namespace layers {
+
+class GenericFlingAnimation: public AsyncPanZoomAnimation {
+public:
+ GenericFlingAnimation(AsyncPanZoomController& aApzc,
+ PlatformSpecificStateBase* aPlatformSpecificState,
+ const RefPtr<const OverscrollHandoffChain>& aOverscrollHandoffChain,
+ bool aFlingIsHandedOff,
+ const RefPtr<const AsyncPanZoomController>& aScrolledApzc)
+ : mApzc(aApzc)
+ , mOverscrollHandoffChain(aOverscrollHandoffChain)
+ , mScrolledApzc(aScrolledApzc)
+ {
+ MOZ_ASSERT(mOverscrollHandoffChain);
+ TimeStamp now = aApzc.GetFrameTime();
+
+ // Drop any velocity on axes where we don't have room to scroll anyways
+ // (in this APZC, or an APZC further in the handoff chain).
+ // This ensures that we don't take the 'overscroll' path in Sample()
+ // on account of one axis which can't scroll having a velocity.
+ if (!mOverscrollHandoffChain->CanScrollInDirection(&mApzc, Layer::HORIZONTAL)) {
+ ReentrantMonitorAutoEnter lock(mApzc.mMonitor);
+ mApzc.mX.SetVelocity(0);
+ }
+ if (!mOverscrollHandoffChain->CanScrollInDirection(&mApzc, Layer::VERTICAL)) {
+ ReentrantMonitorAutoEnter lock(mApzc.mMonitor);
+ mApzc.mY.SetVelocity(0);
+ }
+
+ ParentLayerPoint velocity = mApzc.GetVelocityVector();
+
+ // If the last fling was very recent and in the same direction as this one,
+ // boost the velocity to be the sum of the two. Check separate axes separately
+ // because we could have two vertical flings with small horizontal components
+ // on the opposite side of zero, and we still want the y-fling to get accelerated.
+ // Note that the acceleration code is only applied on the APZC that initiates
+ // the fling; the accelerated velocities are then handed off using the
+ // normal DispatchFling codepath.
+ // Acceleration is only applied in the APZC that originated the fling,
+ // not in APZCs further down the handoff chain during handoff.
+ bool applyAcceleration = !aFlingIsHandedOff;
+ if (applyAcceleration && !mApzc.mLastFlingTime.IsNull()
+ && (now - mApzc.mLastFlingTime).ToMilliseconds() < gfxPrefs::APZFlingAccelInterval()
+ && velocity.Length() >= gfxPrefs::APZFlingAccelMinVelocity()) {
+ if (SameDirection(velocity.x, mApzc.mLastFlingVelocity.x)) {
+ velocity.x = Accelerate(velocity.x, mApzc.mLastFlingVelocity.x);
+ FLING_LOG("%p Applying fling x-acceleration from %f to %f (delta %f)\n",
+ &mApzc, mApzc.mX.GetVelocity(), velocity.x, mApzc.mLastFlingVelocity.x);
+ mApzc.mX.SetVelocity(velocity.x);
+ }
+ if (SameDirection(velocity.y, mApzc.mLastFlingVelocity.y)) {
+ velocity.y = Accelerate(velocity.y, mApzc.mLastFlingVelocity.y);
+ FLING_LOG("%p Applying fling y-acceleration from %f to %f (delta %f)\n",
+ &mApzc, mApzc.mY.GetVelocity(), velocity.y, mApzc.mLastFlingVelocity.y);
+ mApzc.mY.SetVelocity(velocity.y);
+ }
+ }
+
+ mApzc.mLastFlingTime = now;
+ mApzc.mLastFlingVelocity = velocity;
+ }
+
+ /**
+ * Advances a fling by an interpolated amount based on the passed in |aDelta|.
+ * This should be called whenever sampling the content transform for this
+ * frame. Returns true if the fling animation should be advanced by one frame,
+ * or false if there is no fling or the fling has ended.
+ */
+ virtual bool DoSample(FrameMetrics& aFrameMetrics,
+ const TimeDuration& aDelta) override
+ {
+ float friction = gfxPrefs::APZFlingFriction();
+ float threshold = gfxPrefs::APZFlingStoppedThreshold();
+
+ bool shouldContinueFlingX = mApzc.mX.FlingApplyFrictionOrCancel(aDelta, friction, threshold),
+ shouldContinueFlingY = mApzc.mY.FlingApplyFrictionOrCancel(aDelta, friction, threshold);
+ // If we shouldn't continue the fling, let's just stop and repaint.
+ if (!shouldContinueFlingX && !shouldContinueFlingY) {
+ FLING_LOG("%p ending fling animation. overscrolled=%d\n", &mApzc, mApzc.IsOverscrolled());
+ // This APZC or an APZC further down the handoff chain may be be overscrolled.
+ // Start a snap-back animation on the overscrolled APZC.
+ // Note:
+ // This needs to be a deferred task even though it can safely run
+ // while holding mMonitor, because otherwise, if the overscrolled APZC
+ // is this one, then the SetState(NOTHING) in UpdateAnimation will
+ // stomp on the SetState(SNAP_BACK) it does.
+ mDeferredTasks.AppendElement(
+ NewRunnableMethod<AsyncPanZoomController*>(mOverscrollHandoffChain.get(),
+ &OverscrollHandoffChain::SnapBackOverscrolledApzc,
+ &mApzc));
+ return false;
+ }
+
+ // AdjustDisplacement() zeroes out the Axis velocity if we're in overscroll.
+ // Since we need to hand off the velocity to the tree manager in such a case,
+ // we save it here. Would be ParentLayerVector instead of ParentLayerPoint
+ // if we had vector classes.
+ ParentLayerPoint velocity = mApzc.GetVelocityVector();
+
+ ParentLayerPoint offset = velocity * aDelta.ToMilliseconds();
+
+ // Ordinarily we might need to do a ScheduleComposite if either of
+ // the following AdjustDisplacement calls returns true, but this
+ // is already running as part of a FlingAnimation, so we'll be compositing
+ // per frame of animation anyway.
+ ParentLayerPoint overscroll;
+ ParentLayerPoint adjustedOffset;
+ mApzc.mX.AdjustDisplacement(offset.x, adjustedOffset.x, overscroll.x);
+ mApzc.mY.AdjustDisplacement(offset.y, adjustedOffset.y, overscroll.y);
+
+ aFrameMetrics.ScrollBy(adjustedOffset / aFrameMetrics.GetZoom());
+
+ // The fling may have caused us to reach the end of our scroll range.
+ if (!IsZero(overscroll)) {
+ // Hand off the fling to the next APZC in the overscroll handoff chain.
+
+ // We may have reached the end of the scroll range along one axis but
+ // not the other. In such a case we only want to hand off the relevant
+ // component of the fling.
+ if (FuzzyEqualsAdditive(overscroll.x, 0.0f, COORDINATE_EPSILON)) {
+ velocity.x = 0;
+ } else if (FuzzyEqualsAdditive(overscroll.y, 0.0f, COORDINATE_EPSILON)) {
+ velocity.y = 0;
+ }
+
+ // To hand off the fling, we attempt to find a target APZC and start a new
+ // fling with the same velocity on that APZC. For simplicity, the actual
+ // overscroll of the current sample is discarded rather than being handed
+ // off. The compositor should sample animations sufficiently frequently
+ // that this is not noticeable. The target APZC is chosen by seeing if
+ // there is an APZC further in the handoff chain which is pannable; if
+ // there isn't, we take the new fling ourselves, entering an overscrolled
+ // state.
+ // Note: APZC is holding mMonitor, so directly calling
+ // HandleFlingOverscroll() (which acquires the tree lock) would violate
+ // the lock ordering. Instead we schedule HandleFlingOverscroll() to be
+ // called after mMonitor is released.
+ FLING_LOG("%p fling went into overscroll, handing off with velocity %s\n", &mApzc, Stringify(velocity).c_str());
+ mDeferredTasks.AppendElement(
+ NewRunnableMethod<ParentLayerPoint,
+ RefPtr<const OverscrollHandoffChain>,
+ RefPtr<const AsyncPanZoomController>>(&mApzc,
+ &AsyncPanZoomController::HandleFlingOverscroll,
+ velocity,
+ mOverscrollHandoffChain,
+ mScrolledApzc));
+
+ // If there is a remaining velocity on this APZC, continue this fling
+ // as well. (This fling and the handed-off fling will run concurrently.)
+ // Note that AdjustDisplacement() will have zeroed out the velocity
+ // along the axes where we're overscrolled.
+ return !IsZero(mApzc.GetVelocityVector());
+ }
+
+ return true;
+ }
+
+private:
+ static bool SameDirection(float aVelocity1, float aVelocity2)
+ {
+ return (aVelocity1 == 0.0f)
+ || (aVelocity2 == 0.0f)
+ || (IsNegative(aVelocity1) == IsNegative(aVelocity2));
+ }
+
+ static float Accelerate(float aBase, float aSupplemental)
+ {
+ return (aBase * gfxPrefs::APZFlingAccelBaseMultiplier())
+ + (aSupplemental * gfxPrefs::APZFlingAccelSupplementalMultiplier());
+ }
+
+ AsyncPanZoomController& mApzc;
+ RefPtr<const OverscrollHandoffChain> mOverscrollHandoffChain;
+ RefPtr<const AsyncPanZoomController> mScrolledApzc;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_GenericFlingAnimation_h_