Bug 1448439 - Extract a FlingPhysics abstraction from GenericFlingAnimation. r=kats draft
authorBotond Ballo <botond@mozilla.com>
Fri, 20 Apr 2018 16:00:11 -0400
changeset 788681 e53f1d7bbe3e0a588f5d5ba530c59e03cdc50d69
parent 788680 2290b72d3026878b34c86f437b0fac093d9fceac
child 788682 11a3ebd03430e0981be156638762987c7c07a458
push id108063
push userbballo@mozilla.com
push dateThu, 26 Apr 2018 20:42:29 +0000
reviewerskats
bugs1448439
milestone61.0a1
Bug 1448439 - Extract a FlingPhysics abstraction from GenericFlingAnimation. r=kats The FlingPhysics controls the shape of the fling curve, including how fast and far it goes, while leaving other logic (related to flywheel, handoff, overscroll, etc.) centralized in GenericFlingAnimation. FlingPhysics is a template parameter of GenericFlingAnimation because we already have a dynamic dispatch on the type of the animation, we don't need another one for the physics. I called the specific class implementing the current physics DesktopFlingPhysics because since the deprecation of B2G and Android switching to StackScrollerFlingAnimation, it's only used in desktop touch scenarios. MozReview-Commit-ID: LhJ9rpPrnXC
gfx/layers/apz/src/AsyncPanZoomController.cpp
gfx/layers/apz/src/AsyncPanZoomController.h
gfx/layers/apz/src/DesktopFlingPhysics.h
gfx/layers/apz/src/GenericFlingAnimation.h
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -13,16 +13,17 @@
 
 #include "APZCTreeManager.h"            // for APZCTreeManager
 #include "AsyncPanZoomAnimation.h"      // for AsyncPanZoomAnimation
 #include "AutoDirWheelDeltaAdjuster.h"  // for APZAutoDirWheelDeltaAdjuster
 #include "AutoscrollAnimation.h"        // for AutoscrollAnimation
 #include "Axis.h"                       // for AxisX, AxisY, Axis, etc
 #include "CheckerboardEvent.h"          // for CheckerboardEvent
 #include "Compositor.h"                 // for Compositor
+#include "DesktopFlingPhysics.h"        // for DesktopFlingPhysics
 #include "FrameMetrics.h"               // for FrameMetrics, etc
 #include "GenericFlingAnimation.h"      // for GenericFlingAnimation
 #include "GestureEventListener.h"       // for GestureEventListener
 #include "HitTestingTreeNode.h"         // for HitTestingTreeNode
 #include "InputData.h"                  // for MultiTouchInput, etc
 #include "InputBlockState.h"            // for InputBlockState, TouchBlockState
 #include "InputQueue.h"                 // for InputQueue
 #include "Overscroll.h"                 // for OverscrollAnimation
@@ -513,17 +514,17 @@ static bool IsCloseToVertical(float aAng
 
 // Counter used to give each APZC a unique id
 static uint32_t sAsyncPanZoomControllerCount = 0;
 
 AsyncPanZoomAnimation*
 PlatformSpecificStateBase::CreateFlingAnimation(AsyncPanZoomController& aApzc,
                                                 const FlingHandoffState& aHandoffState)
 {
-  return new GenericFlingAnimation(aApzc,
+  return new GenericFlingAnimation<DesktopFlingPhysics>(aApzc,
       aHandoffState.mChain,
       aHandoffState.mIsHandoff,
       aHandoffState.mScrolledApzc);
 }
 
 TimeStamp
 AsyncPanZoomController::GetFrameTime() const
 {
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -44,17 +44,18 @@ class AsyncDragMetrics;
 class APZCTreeManager;
 struct ScrollableLayerGuid;
 class CompositorController;
 class MetricsSharingController;
 class GestureEventListener;
 struct AsyncTransform;
 class AsyncPanZoomAnimation;
 class StackScrollerFlingAnimation;
-class GenericFlingAnimation;
+template <typename FlingPhysics> class GenericFlingAnimation;
+class DesktopFlingPhysics;
 class InputBlockState;
 struct FlingHandoffState;
 class TouchBlockState;
 class PanGestureBlockState;
 class OverscrollHandoffChain;
 struct OverscrollHandoffState;
 class StateChangeNotificationBlocker;
 class CheckerboardEvent;
@@ -1157,17 +1158,18 @@ public:
    */
   ParentLayerPoint AttemptFling(const FlingHandoffState& aHandoffState);
 
   ParentLayerPoint AdjustHandoffVelocityForOverscrollBehavior(ParentLayerPoint& aHandoffVelocity) const;
 
 private:
   friend class StackScrollerFlingAnimation;
   friend class AutoscrollAnimation;
-  friend class GenericFlingAnimation;
+  template <typename FlingPhysics> friend class GenericFlingAnimation;
+  friend class DesktopFlingPhysics;
   friend class OverscrollAnimation;
   friend class SmoothScrollAnimation;
   friend class GenericScrollAnimation;
   friend class WheelScrollAnimation;
   friend class KeyboardScrollAnimation;
 
   friend class GenericOverscrollEffect;
   friend class WidgetOverscrollEffect;
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/src/DesktopFlingPhysics.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 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_DesktopFlingPhysics_h_
+#define mozilla_layers_DesktopFlingPhysics_h_
+
+#include "AsyncPanZoomController.h"
+#include "Units.h"
+#include "gfxPrefs.h"
+#include "mozilla/Assertions.h"
+
+#define FLING_PHYS_LOG(...)
+// #define FLING_PHYS_LOG(...) printf_stderr("FLING: " __VA_ARGS__)
+
+namespace mozilla {
+namespace layers {
+
+class DesktopFlingPhysics {
+public:
+  void Init(const ParentLayerPoint& aStartingVelocity)
+  {
+    mVelocity = aStartingVelocity;
+  }
+  void Sample(const TimeDuration& aDelta,
+              ParentLayerPoint* aOutVelocity,
+              ParentLayerPoint* aOutOffset)
+  {
+    float friction = gfxPrefs::APZFlingFriction();
+    float threshold = gfxPrefs::APZFlingStoppedThreshold();
+
+    mVelocity = ParentLayerPoint(
+        ApplyFrictionOrCancel(mVelocity.x, aDelta, friction, threshold),
+        ApplyFrictionOrCancel(mVelocity.y, aDelta, friction, threshold));
+
+    *aOutVelocity = mVelocity;
+    *aOutOffset = mVelocity * aDelta.ToMilliseconds();
+  }
+private:
+  /**
+   * Applies friction to the given velocity and returns the result, or
+   * returns zero if the velocity is too low.
+   * |aVelocity| is the incoming velocity.
+   * |aDelta| is the amount of time that has passed since the last time
+   * friction was applied.
+   * |aFriction| is the amount of friction to apply.
+   * |aThreshold| is the velocity below which the fling is cancelled.
+   */
+  static float ApplyFrictionOrCancel(float aVelocity, const TimeDuration& aDelta,
+                                     float aFriction, float aThreshold)
+  {
+    if (fabsf(aVelocity) <= aThreshold) {
+      // If the velocity is very low, just set it to 0 and stop the fling,
+      // otherwise we'll just asymptotically approach 0 and the user won't
+      // actually see any changes.
+      return 0.0f;
+    }
+
+    aVelocity *= pow(1.0f - aFriction, float(aDelta.ToMilliseconds()));
+    return aVelocity;
+  }
+
+  ParentLayerPoint mVelocity;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_DesktopFlingPhysics_h_
--- a/gfx/layers/apz/src/GenericFlingAnimation.h
+++ b/gfx/layers/apz/src/GenericFlingAnimation.h
@@ -22,17 +22,39 @@
 #include "nsThreadUtils.h"
 
 #define FLING_LOG(...)
 // #define FLING_LOG(...) printf_stderr("FLING: " __VA_ARGS__)
 
 namespace mozilla {
 namespace layers {
 
-class GenericFlingAnimation: public AsyncPanZoomAnimation {
+/**
+ * The FlingPhysics template parameter determines the physics model
+ * that the fling animation follows. It must have the following methods:
+ *
+ *   - Default constructor.
+ *
+ *   - Init(const ParentLayerPoint& aStartingVelocity).
+ *     Called at the beginning of the fling, with the fling's starting velocity.
+ *
+ *   - Sample(const TimeDuration& aDelta,
+ *            ParentLayerPoint* aOutVelocity,
+ *            ParentLayerPoint* aOutOffset);
+ *     Called on each sample of the fling.
+ *     |aDelta| is the time elapsed since the last sample.
+ *     |aOutVelocity| should be the desired velocity after the current sample,
+ *                    in ParentLayer pixels per millisecond.
+ *     |aOutOffset| should be the desired _delta_ to the scroll offset after
+ *     the current sample. |aOutOffset| should _not_ be clamped to the APZC's
+ *     scrollable bounds; the caller will do the clamping, and it needs to
+ *     know the unclamped value to handle handoff/overscroll correctly.
+ */
+template <typename FlingPhysics>
+class GenericFlingAnimation: public AsyncPanZoomAnimation, public FlingPhysics {
 public:
   GenericFlingAnimation(AsyncPanZoomController& aApzc,
                         const RefPtr<const OverscrollHandoffChain>& aOverscrollHandoffChain,
                         bool aFlingIsHandedOff,
                         const RefPtr<const AsyncPanZoomController>& aScrolledApzc)
     : mApzc(aApzc)
     , mOverscrollHandoffChain(aOverscrollHandoffChain)
     , mScrolledApzc(aScrolledApzc)
@@ -79,39 +101,32 @@ public:
         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;
+
+    FlingPhysics::Init(mApzc.GetVelocityVector());
   }
 
   /**
    * 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();
-
-    // Save the velocity locally instead of just passing it directly into
-    // SetVelocityVector(), because AdjustDisplacement() (called below)
-    // zeroes out the Axis velocity if we're in overscroll, and we need to
-    // hand off the velocity to the tree manager in such a case.
-    ParentLayerPoint velocity(
-        ApplyFrictionOrCancel(mApzc.mX.GetVelocity(), aDelta, friction, threshold),
-        ApplyFrictionOrCancel(mApzc.mY.GetVelocity(), aDelta, friction, threshold));
-    FLING_LOG("%p reduced velocity to %s due to friction\n",
-      &mApzc, ToString(mApzc.GetVelocityVector()).c_str());
+    ParentLayerPoint velocity;
+    ParentLayerPoint offset;
+    FlingPhysics::Sample(aDelta, &velocity, &offset);
 
     mApzc.SetVelocityVector(velocity);
 
     // If we shouldn't continue the fling, let's just stop and repaint.
     if (IsZero(velocity)) {
       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.
@@ -123,18 +138,16 @@ public:
       mDeferredTasks.AppendElement(NewRunnableMethod<AsyncPanZoomController*>(
         "layers::OverscrollHandoffChain::SnapBackOverscrolledApzc",
         mOverscrollHandoffChain.get(),
         &OverscrollHandoffChain::SnapBackOverscrolledApzc,
         &mApzc));
       return false;
     }
 
-    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);
@@ -197,39 +210,16 @@ private:
   }
 
   static float Accelerate(float aBase, float aSupplemental)
   {
     return (aBase * gfxPrefs::APZFlingAccelBaseMultiplier())
          + (aSupplemental * gfxPrefs::APZFlingAccelSupplementalMultiplier());
   }
 
-  /**
-   * Applies friction to the given velocity and returns the result, or
-   * returns zero if the velocity is too low.
-   * |aVelocity| is the incoming velocity.
-   * |aDelta| is the amount of time that has passed since the last time
-   * friction was applied.
-   * |aFriction| is the amount of friction to apply.
-   * |aThreshold| is the velocity below which the fling is cancelled.
-   */
-  static float ApplyFrictionOrCancel(float aVelocity, const TimeDuration& aDelta,
-                                     float aFriction, float aThreshold)
-  {
-    if (fabsf(aVelocity) <= aThreshold) {
-      // If the velocity is very low, just set it to 0 and stop the fling,
-      // otherwise we'll just asymptotically approach 0 and the user won't
-      // actually see any changes.
-      return 0.0f;
-    }
-
-    aVelocity *= pow(1.0f - aFriction, float(aDelta.ToMilliseconds()));
-    return aVelocity;
-  }
-
   AsyncPanZoomController& mApzc;
   RefPtr<const OverscrollHandoffChain> mOverscrollHandoffChain;
   RefPtr<const AsyncPanZoomController> mScrolledApzc;
 };
 
 } // namespace layers
 } // namespace mozilla