Bug 1448439 - Expose some of the tunable parameters in AndroidFlingPhysics as prefs. r=kats draft
authorBotond Ballo <botond@mozilla.com>
Fri, 20 Apr 2018 18:55:08 -0400
changeset 788684 9acdec2fff2f95057cf2fc4da9fc8e86de4912f0
parent 788683 c454a6769ccd0ca9951c41ef2ea9990c24208373
child 788685 89f751e48e376812c49fc2d7665a43049d9de121
push id108063
push userbballo@mozilla.com
push dateThu, 26 Apr 2018 20:42:29 +0000
reviewerskats
bugs1448439
milestone61.0a1
Bug 1448439 - Expose some of the tunable parameters in AndroidFlingPhysics as prefs. r=kats MozReview-Commit-ID: J4QRmMdGE0l
gfx/layers/apz/src/AndroidFlingPhysics.cpp
gfx/layers/apz/src/AsyncPanZoomController.cpp
gfx/thebes/gfxPrefs.h
modules/libpref/init/all.js
--- a/gfx/layers/apz/src/AndroidFlingPhysics.cpp
+++ b/gfx/layers/apz/src/AndroidFlingPhysics.cpp
@@ -26,62 +26,87 @@ static double ComputeDeceleration(float 
          * 160.f        // pixels/inch
          * aFriction;
 }
 
 // == std::log(0.78f) / std::log(0.9f)
 const float kDecelerationRate = 2.3582018f;
 
 // Default friction constant in android.view.ViewConfiguration.
-const float kFlingFriction = 0.015f;
+static float GetFlingFriction()
+{
+  return gfxPrefs::APZChromeFlingPhysicsFriction();
+}
 
-// Tension lines cross at (kInflexion, 1).
-const float kInflexion = 0.35f;
+// Tension lines cross at (GetInflexion(), 1).
+static float GetInflexion()
+{
+  // Clamp the inflexion to the range [0,1]. Values outside of this range
+  // do not make sense in the physics model, and for negative values the
+  // approximation used to compute the spline curve does not converge.
+  const float inflexion = gfxPrefs::APZChromeFlingPhysicsInflexion();
+  if (inflexion < 0.0f) {
+    return 0.0f;
+  }
+  if (inflexion > 1.0f) {
+    return 1.0f;
+  }
+  return inflexion;
+}
 
 // Fling scroll is stopped when the scroll position is |kThresholdForFlingEnd|
 // pixels or closer from the end.
-const float kThresholdForFlingEnd = 0.1;
+static float GetThresholdForFlingEnd()
+{
+  return gfxPrefs::APZChromeFlingPhysicsStopThreshold();
+}
 
 const float kTuningCoeff = ComputeDeceleration(0.84f);
 
 static double ComputeSplineDeceleration(ParentLayerCoord aVelocity)
 {
   float velocityPerSec = aVelocity * 1000.0f;
-  return std::log(kInflexion * velocityPerSec / (kFlingFriction * kTuningCoeff));
+  return std::log(GetInflexion() * velocityPerSec / (GetFlingFriction() * kTuningCoeff));
 }
 
 static TimeDuration ComputeFlingDuration(ParentLayerCoord aVelocity)
 {
   const double splineDecel = ComputeSplineDeceleration(aVelocity);
   const double timeSeconds = std::exp(splineDecel / (kDecelerationRate - 1.0));
   return TimeDuration::FromSeconds(timeSeconds);
 }
 
 static ParentLayerCoord ComputeFlingDistance(ParentLayerCoord aVelocity)
 {
   const double splineDecel = ComputeSplineDeceleration(aVelocity);
-  return kFlingFriction * kTuningCoeff *
+  return GetFlingFriction() * kTuningCoeff *
       std::exp(kDecelerationRate / (kDecelerationRate - 1.0) * splineDecel);
 }
 
 struct SplineConstants {
 public:
   SplineConstants() {
     const float kStartTension = 0.5f;
     const float kEndTension = 1.0f;
-    const float kP1 = kStartTension * kInflexion;
-    const float kP2 = 1.0f - kEndTension * (1.0f - kInflexion);
+    const float kP1 = kStartTension * GetInflexion();
+    const float kP2 = 1.0f - kEndTension * (1.0f - GetInflexion());
 
     float xMin = 0.0f;
     for (int i = 0; i < kNumSamples; i++) {
       const float alpha = static_cast<float>(i) / kNumSamples;
 
       float xMax = 1.0f;
       float x, tx, coef;
-      while (true) {
+      // While the inflexion can be overridden by the user, it's clamped to
+      // [0,1]. For values in this range, the approximation algorithm below
+      // should converge in < 20 iterations. For good measure, we impose an
+      // iteration limit as well.
+      static const int sIterationLimit = 100;
+      int iterations = 0;
+      while (iterations++ < sIterationLimit) {
         x = xMin + (xMax - xMin) / 2.0f;
         coef = 3.0f * x * (1.0f - x);
         tx = coef * ((1.0f - x) * kP1 + x * kP2) + x * x * x;
         if (FuzzyEqualsAdditive(tx, alpha)) {
           break;
         }
         if (tx > alpha) {
           xMax = x;
@@ -173,17 +198,18 @@ bool AndroidFlingPhysics::SampleImpl(con
   gSplineConstants->CalculateCoefficients(timeRatio, &distanceCoef, &velocityCoef);
 
   // The caller expects the velocity in pixels per _millisecond_.
   *aOutVelocity = velocityCoef * mTargetDistance / mTargetDuration.ToMilliseconds();
 
   mCurrentPos = mTargetPos * distanceCoef;
 
   ParentLayerPoint remainder = mTargetPos - mCurrentPos;
-  if (fabsf(remainder.x) < kThresholdForFlingEnd && fabsf(remainder.y) < kThresholdForFlingEnd) {
+  const float threshold = GetThresholdForFlingEnd();
+  if (fabsf(remainder.x) < threshold && fabsf(remainder.y) < threshold) {
     return false;
   }
 
   return true;
 }
 
 
 } // namespace layers
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -137,16 +137,31 @@ typedef PlatformSpecificStateBase Platfo
  * If set to true, scroll can be handed off from one APZC to another within
  * a single input block. If set to false, a single input block can only
  * scroll one APZC.
  *
  * \li\b apz.android.chrome_fling_physics.enabled
  * If set to true, APZ uses a fling physical model similar to Chrome's
  * on Android, rather than Android's StackScroller.
  *
+ * \li\b apz.android.chrome_fling_physics.friction
+ * A tunable parameter for Chrome fling physics on Android that governs
+ * how quickly a fling animation slows down due to friction (and therefore
+ * also how far it reaches). Should be in the range [0-1].
+ *
+ * \li\b apz.android.chrome_fling_physics.inflexion
+ * A tunable parameter for Chrome fling physics on Android that governs
+ * the shape of the fling curve. Should be in the range [0-1].
+ *
+ * \li\b apz.android.chrome_fling_physics.stop_threshold
+ * A tunable parameter for Chrome fling physics on Android that governs
+ * how close the fling animation has to get to its target destination
+ * before it stops.
+ * Units: ParentLayer pixels
+ *
  * \li\b apz.autoscroll.enabled
  * If set to true, autoscrolling is driven by APZ rather than the content
  * process main thread.
  *
  * \li\b apz.axis_lock.mode
  * The preferred axis locking style. See AxisLockMode for possible values.
  *
  * \li\b apz.axis_lock.lock_angle
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -286,16 +286,19 @@ private:
 
   DECL_GFX_PREF(Live, "accessibility.browsewithcaret", AccessibilityBrowseWithCaret, bool, false);
 
   // The apz prefs are explained in AsyncPanZoomController.cpp
   DECL_GFX_PREF(Live, "apz.allow_checkerboarding",             APZAllowCheckerboarding, bool, true);
   DECL_GFX_PREF(Live, "apz.allow_immediate_handoff",           APZAllowImmediateHandoff, bool, true);
   DECL_GFX_PREF(Live, "apz.allow_zooming",                     APZAllowZooming, bool, false);
   DECL_GFX_PREF(Live, "apz.android.chrome_fling_physics.enabled", APZUseChromeFlingPhysics, bool, false);
+  DECL_GFX_PREF(Live, "apz.android.chrome_fling_physics.friction", APZChromeFlingPhysicsFriction, float, 0.015f);
+  DECL_GFX_PREF(Live, "apz.android.chrome_fling_physics.inflexion", APZChromeFlingPhysicsInflexion, float, 0.35f);
+  DECL_GFX_PREF(Live, "apz.android.chrome_fling_physics.stop_threshold", APZChromeFlingPhysicsStopThreshold, float, 0.1f);
   DECL_GFX_PREF(Live, "apz.autoscroll.enabled",                APZAutoscrollEnabled, bool, false);
   DECL_GFX_PREF(Live, "apz.axis_lock.breakout_angle",          APZAxisBreakoutAngle, float, float(M_PI / 8.0) /* 22.5 degrees */);
   DECL_GFX_PREF(Live, "apz.axis_lock.breakout_threshold",      APZAxisBreakoutThreshold, float, 1.0f / 32.0f);
   DECL_GFX_PREF(Live, "apz.axis_lock.direct_pan_angle",        APZAllowedDirectPanAngle, float, float(M_PI / 3.0) /* 60 degrees */);
   DECL_GFX_PREF(Live, "apz.axis_lock.lock_angle",              APZAxisLockAngle, float, float(M_PI / 6.0) /* 30 degrees */);
   DECL_GFX_PREF(Live, "apz.axis_lock.mode",                    APZAxisLockMode, int32_t, 0);
   DECL_GFX_PREF(Live, "apz.content_response_timeout",          APZContentResponseTimeout, int32_t, 400);
   DECL_GFX_PREF(Live, "apz.danger_zone_x",                     APZDangerZoneX, int32_t, 50);
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -622,16 +622,19 @@ pref("layers.geometry.basic.enabled", tr
 pref("layers.geometry.d3d11.enabled", true);
 
 // APZ preferences. For documentation/details on what these prefs do, check
 // gfx/layers/apz/src/AsyncPanZoomController.cpp.
 pref("apz.allow_checkerboarding", true);
 pref("apz.allow_immediate_handoff", true);
 pref("apz.allow_zooming", false);
 pref("apz.android.chrome_fling_physics.enabled", false);
+pref("apz.android.chrome_fling_physics.friction", "0.015");
+pref("apz.android.chrome_fling_physics.inflexion", "0.35");
+pref("apz.android.chrome_fling_physics.stop_threshold", "0.1");
 pref("apz.autoscroll.enabled", true);
 
 // Whether to lock touch scrolling to one axis at a time
 // 0 = FREE (No locking at all)
 // 1 = STANDARD (Once locked, remain locked until scrolling ends)
 // 2 = STICKY (Allow lock to be broken, with hysteresis)
 pref("apz.axis_lock.mode", 0);
 pref("apz.axis_lock.lock_angle", "0.5235987");        // PI / 6 (30 degrees)