Bug 1448439 - Use an accurate pixels per pinch value in AndroidFlingPhysics. r=kats draft
authorBotond Ballo <botond@mozilla.com>
Thu, 26 Apr 2018 16:42:05 -0400
changeset 788685 89f751e48e376812c49fc2d7665a43049d9de121
parent 788684 9acdec2fff2f95057cf2fc4da9fc8e86de4912f0
push id108063
push userbballo@mozilla.com
push dateThu, 26 Apr 2018 20:42:29 +0000
reviewerskats
bugs1448439
milestone61.0a1
Bug 1448439 - Use an accurate pixels per pinch value in AndroidFlingPhysics. r=kats MozReview-Commit-ID: I0yZKTvlHWV
gfx/layers/apz/src/AndroidAPZ.cpp
gfx/layers/apz/src/AndroidAPZ.h
gfx/layers/apz/src/AndroidFlingPhysics.cpp
gfx/layers/apz/src/AndroidFlingPhysics.h
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/AndroidAPZ.cpp
+++ b/gfx/layers/apz/src/AndroidAPZ.cpp
@@ -42,22 +42,24 @@ AndroidSpecificState::AndroidSpecificSta
     ANDROID_APZ_LOG("%p Failed to create Android StackScroller\n", this);
     return;
   }
   mOverScroller = scroller;
 }
 
 AsyncPanZoomAnimation*
 AndroidSpecificState::CreateFlingAnimation(AsyncPanZoomController& aApzc,
-                                           const FlingHandoffState& aHandoffState) {
+                                           const FlingHandoffState& aHandoffState,
+                                           float aPLPPI) {
   if (gfxPrefs::APZUseChromeFlingPhysics()) {
     return new GenericFlingAnimation<AndroidFlingPhysics>(aApzc,
             aHandoffState.mChain,
             aHandoffState.mIsHandoff,
-            aHandoffState.mScrolledApzc);
+            aHandoffState.mScrolledApzc,
+            aPLPPI);
   } else {
     return new StackScrollerFlingAnimation(aApzc,
         this,
         aHandoffState.mChain,
         aHandoffState.mIsHandoff,
         aHandoffState.mScrolledApzc);
   }
 }
--- a/gfx/layers/apz/src/AndroidAPZ.h
+++ b/gfx/layers/apz/src/AndroidAPZ.h
@@ -18,17 +18,18 @@ class AndroidSpecificState : public Plat
 public:
   AndroidSpecificState();
 
   virtual AndroidSpecificState* AsAndroidSpecificState() override {
     return this;
   }
 
   virtual AsyncPanZoomAnimation* CreateFlingAnimation(AsyncPanZoomController& aApzc,
-                                                      const FlingHandoffState& aHandoffState) override;
+                                                      const FlingHandoffState& aHandoffState,
+                                                      float aPLPPI) override;
 
   static void InitializeGlobalState();
 
   java::StackScroller::GlobalRef mOverScroller;
   TimeStamp mLastFling;
 };
 
 class StackScrollerFlingAnimation: public AsyncPanZoomAnimation {
--- a/gfx/layers/apz/src/AndroidFlingPhysics.cpp
+++ b/gfx/layers/apz/src/AndroidFlingPhysics.cpp
@@ -13,23 +13,24 @@
 
 namespace mozilla {
 namespace layers {
 
 // The fling physics calculations implemented here are adapted from
 // Chrome's implementation of fling physics on Android:
 // https://cs.chromium.org/chromium/src/ui/events/android/scroller.cc?rcl=3ae3aaff927038a5c644926842cb0c31dea60c79
 
-static double ComputeDeceleration(float aFriction)
+static double ComputeDeceleration(float aDPI)
 {
+  const float kFriction = 0.84f;
   const float kGravityEarth = 9.80665f;
   return kGravityEarth  // g (m/s^2)
          * 39.37f       // inch/meter
-         * 160.f        // pixels/inch
-         * aFriction;
+         * aDPI         // pixels/inch
+         * kFriction;
 }
 
 // == std::log(0.78f) / std::log(0.9f)
 const float kDecelerationRate = 2.3582018f;
 
 // Default friction constant in android.view.ViewConfiguration.
 static float GetFlingFriction()
 {
@@ -54,35 +55,33 @@ static float GetInflexion()
 
 // Fling scroll is stopped when the scroll position is |kThresholdForFlingEnd|
 // pixels or closer from the end.
 static float GetThresholdForFlingEnd()
 {
   return gfxPrefs::APZChromeFlingPhysicsStopThreshold();
 }
 
-const float kTuningCoeff = ComputeDeceleration(0.84f);
-
-static double ComputeSplineDeceleration(ParentLayerCoord aVelocity)
+static double ComputeSplineDeceleration(ParentLayerCoord aVelocity, double aTuningCoeff)
 {
   float velocityPerSec = aVelocity * 1000.0f;
-  return std::log(GetInflexion() * velocityPerSec / (GetFlingFriction() * kTuningCoeff));
+  return std::log(GetInflexion() * velocityPerSec / (GetFlingFriction() * aTuningCoeff));
 }
 
-static TimeDuration ComputeFlingDuration(ParentLayerCoord aVelocity)
+static TimeDuration ComputeFlingDuration(ParentLayerCoord aVelocity, double aTuningCoeff)
 {
-  const double splineDecel = ComputeSplineDeceleration(aVelocity);
+  const double splineDecel = ComputeSplineDeceleration(aVelocity, aTuningCoeff);
   const double timeSeconds = std::exp(splineDecel / (kDecelerationRate - 1.0));
   return TimeDuration::FromSeconds(timeSeconds);
 }
 
-static ParentLayerCoord ComputeFlingDistance(ParentLayerCoord aVelocity)
+static ParentLayerCoord ComputeFlingDistance(ParentLayerCoord aVelocity, double aTuningCoeff)
 {
-  const double splineDecel = ComputeSplineDeceleration(aVelocity);
-  return GetFlingFriction() * kTuningCoeff *
+  const double splineDecel = ComputeSplineDeceleration(aVelocity, aTuningCoeff);
+  return GetFlingFriction() * aTuningCoeff *
       std::exp(kDecelerationRate / (kDecelerationRate - 1.0) * splineDecel);
 }
 
 struct SplineConstants {
 public:
   SplineConstants() {
     const float kStartTension = 0.5f;
     const float kEndTension = 1.0f;
@@ -143,27 +142,29 @@ private:
 StaticAutoPtr<SplineConstants> gSplineConstants;
 
 /* static */ void AndroidFlingPhysics::InitializeGlobalState()
 {
   gSplineConstants = new SplineConstants();
   ClearOnShutdown(&gSplineConstants);
 }
 
-void AndroidFlingPhysics::Init(const ParentLayerPoint& aStartingVelocity)
+void AndroidFlingPhysics::Init(const ParentLayerPoint& aStartingVelocity,
+                               float aPLPPI)
 {
   mVelocity = aStartingVelocity.Length();
-  mTargetDuration = ComputeFlingDuration(mVelocity);
+  const double tuningCoeff = ComputeDeceleration(aPLPPI);
+  mTargetDuration = ComputeFlingDuration(mVelocity, tuningCoeff);
   MOZ_ASSERT(!mTargetDuration.IsZero());
   mDurationSoFar = TimeDuration();
   mLastPos = ParentLayerPoint();
   mCurrentPos = ParentLayerPoint();
   float coeffX = mVelocity == 0 ? 1.0f : aStartingVelocity.x / mVelocity;
   float coeffY = mVelocity == 0 ? 1.0f : aStartingVelocity.y / mVelocity;
-  mTargetDistance = ComputeFlingDistance(mVelocity);
+  mTargetDistance = ComputeFlingDistance(mVelocity, tuningCoeff);
   mTargetPos = ParentLayerPoint(mTargetDistance * coeffX,
                                 mTargetDistance * coeffY);
   const float hyp = mTargetPos.Length();
   if (FuzzyEqualsAdditive(hyp, 0.0f)) {
     mDeltaNorm = ParentLayerPoint(1, 1);
   } else {
     mDeltaNorm = ParentLayerPoint(mTargetPos.x / hyp, mTargetPos.y / hyp);
   }
--- a/gfx/layers/apz/src/AndroidFlingPhysics.h
+++ b/gfx/layers/apz/src/AndroidFlingPhysics.h
@@ -12,17 +12,17 @@
 #include "gfxPrefs.h"
 #include "mozilla/Assertions.h"
 
 namespace mozilla {
 namespace layers {
 
 class AndroidFlingPhysics {
 public:
-  void Init(const ParentLayerPoint& aVelocity);
+  void Init(const ParentLayerPoint& aVelocity, float aPLPPI);
   void Sample(const TimeDuration& aDelta,
               ParentLayerPoint* aOutVelocity,
               ParentLayerPoint* aOutOffset);
 
   static void InitializeGlobalState();
 private:
   // Returns false if the animation should end.
   bool SampleImpl(const TimeDuration& aDelta, float* aOutVelocity);
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -531,22 +531,24 @@ static bool IsCloseToVertical(float aAng
   return (fabs(aAngle - (M_PI / 2)) < aThreshold);
 }
 
 // Counter used to give each APZC a unique id
 static uint32_t sAsyncPanZoomControllerCount = 0;
 
 AsyncPanZoomAnimation*
 PlatformSpecificStateBase::CreateFlingAnimation(AsyncPanZoomController& aApzc,
-                                                const FlingHandoffState& aHandoffState)
+                                                const FlingHandoffState& aHandoffState,
+                                                float aPLPPI)
 {
   return new GenericFlingAnimation<DesktopFlingPhysics>(aApzc,
       aHandoffState.mChain,
       aHandoffState.mIsHandoff,
-      aHandoffState.mScrolledApzc);
+      aHandoffState.mScrolledApzc,
+      aPLPPI);
 }
 
 TimeStamp
 AsyncPanZoomController::GetFrameTime() const
 {
   APZCTreeManager* treeManagerLocal = GetApzcTreeManager();
   return treeManagerLocal ? treeManagerLocal->GetFrameTime() : TimeStamp::Now();
 }
@@ -3010,16 +3012,21 @@ RefPtr<const OverscrollHandoffChain> Asy
   // This APZC IsDestroyed(). To avoid callers having to special-case this
   // scenario, just build a 1-element chain containing ourselves.
   OverscrollHandoffChain* result = new OverscrollHandoffChain;
   result->Add(this);
   return result;
 }
 
 ParentLayerPoint AsyncPanZoomController::AttemptFling(const FlingHandoffState& aHandoffState) {
+  // The PLPPI computation acquires the tree lock, so it needs to be performed
+  // on the controller thread, and before the APZC lock is acquired.
+  APZThreadUtils::AssertOnControllerThread();
+  float PLPPI = ComputePLPPI(PanStart(), aHandoffState.mVelocity);
+
   RecursiveMutexAutoLock lock(mRecursiveMutex);
 
   if (!IsPannable()) {
     return aHandoffState.mVelocity;
   }
 
   // We may have a pre-existing velocity for whatever reason (for example,
   // a previously handed off fling). We don't want to clobber that.
@@ -3036,23 +3043,39 @@ ParentLayerPoint AsyncPanZoomController:
   }
 
   // If there's a scroll snap point near the predicted fling destination,
   // scroll there using a smooth scroll animation. Otherwise, start a
   // fling animation.
   ScrollSnapToDestination();
   if (mState != SMOOTH_SCROLL) {
     SetState(FLING);
-    AsyncPanZoomAnimation* fling = GetPlatformSpecificState()->CreateFlingAnimation(*this, aHandoffState);
+    AsyncPanZoomAnimation* fling = GetPlatformSpecificState()->CreateFlingAnimation(
+        *this, aHandoffState, PLPPI);
     StartAnimation(fling);
   }
 
   return residualVelocity;
 }
 
+float AsyncPanZoomController::ComputePLPPI(ParentLayerPoint aPoint, ParentLayerPoint aDirection) const
+{
+  // Convert |aDirection| into a unit vector.
+  aDirection = aDirection / aDirection.Length();
+
+  // Place the vector at |aPoint| and convert to screen coordinates.
+  // The length of the resulting vector is the number of Screen coordinates
+  // that equal 1 ParentLayer coordinate in the given direction.
+  float screenPerParent = ToScreenCoordinates(aDirection, aPoint).Length();
+
+  // Finally, factor in the DPI scale.
+  return GetDPI() / screenPerParent;
+}
+
+
 ParentLayerPoint AsyncPanZoomController::AdjustHandoffVelocityForOverscrollBehavior(ParentLayerPoint& aHandoffVelocity) const
 {
   RecursiveMutexAutoLock lock(mRecursiveMutex);
   ParentLayerPoint residualVelocity;
   if (!mX.OverscrollBehaviorAllowsHandoff()) {
     residualVelocity.x = aHandoffVelocity.x;
     aHandoffVelocity.x = 0;
   }
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -66,18 +66,20 @@ class GenericOverscrollEffect;
 class AndroidSpecificState;
 struct KeyboardScrollAction;
 
 // Base class for grouping platform-specific APZC state variables.
 class PlatformSpecificStateBase {
 public:
   virtual ~PlatformSpecificStateBase() = default;
   virtual AndroidSpecificState* AsAndroidSpecificState() { return nullptr; }
+  // PLPPI = "ParentLayer pixels per (Screen) inch"
   virtual AsyncPanZoomAnimation* CreateFlingAnimation(AsyncPanZoomController& aApzc,
-                                                      const FlingHandoffState& aHandoffState);
+                                                      const FlingHandoffState& aHandoffState,
+                                                      float aPLPPI);
 
   static void InitializeGlobalState() {}
 };
 
 /*
  * Represents a transform from the ParentLayer coordinate space of an APZC
  * to the ParentLayer coordinate space of its parent APZC.
  * Each layer along the way contributes to the transform. We track
@@ -1202,16 +1204,20 @@ private:
   void SmoothScrollTo(const CSSPoint& aDestination);
 
   // Returns whether overscroll is allowed during an event.
   bool AllowScrollHandoffInCurrentBlock() const;
 
   // Invoked by the pinch repaint timer.
   void DoDelayedRequestContentRepaint();
 
+  // Compute the number of ParentLayer pixels per (Screen) inch at the given
+  // point and in the given direction.
+  float ComputePLPPI(ParentLayerPoint aPoint, ParentLayerPoint aDirection) const;
+
   /* ===================================================================
    * The functions and members in this section are used to make ancestor chains
    * out of APZC instances. These chains can only be walked or manipulated
    * while holding the lock in the associated APZCTreeManager instance.
    */
 public:
   void SetParent(AsyncPanZoomController* aParent) {
     mParent = aParent;
--- a/gfx/layers/apz/src/DesktopFlingPhysics.h
+++ b/gfx/layers/apz/src/DesktopFlingPhysics.h
@@ -15,17 +15,17 @@
 #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)
+  void Init(const ParentLayerPoint& aStartingVelocity, float aPLPPI /* unused */)
   {
     mVelocity = aStartingVelocity;
   }
   void Sample(const TimeDuration& aDelta,
               ParentLayerPoint* aOutVelocity,
               ParentLayerPoint* aOutOffset)
   {
     float friction = gfxPrefs::APZFlingFriction();
--- a/gfx/layers/apz/src/GenericFlingAnimation.h
+++ b/gfx/layers/apz/src/GenericFlingAnimation.h
@@ -28,18 +28,20 @@ namespace mozilla {
 namespace layers {
 
 /**
  * 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.
+ *   - Init(const ParentLayerPoint& aStartingVelocity, float aPLPPI).
+ *     Called at the beginning of the fling, with the fling's starting velocity,
+ *     and the number of ParentLayer pixels per (Screen) inch at the point of
+ *     the fling's start in the fling's direction.
  *
  *   - 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.
@@ -49,17 +51,18 @@ namespace layers {
  *     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)
+                        const RefPtr<const AsyncPanZoomController>& aScrolledApzc,
+                        float aPLPPI)
     : 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
@@ -102,17 +105,17 @@ public:
                   &mApzc, mApzc.mY.GetVelocity(), velocity.y, mApzc.mLastFlingVelocity.y);
         mApzc.mY.SetVelocity(velocity.y);
       }
     }
 
     mApzc.mLastFlingTime = now;
     mApzc.mLastFlingVelocity = velocity;
 
-    FlingPhysics::Init(mApzc.GetVelocityVector());
+    FlingPhysics::Init(mApzc.GetVelocityVector(), aPLPPI);
   }
 
   /**
    * 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.
    */