Bug 1448439 - Expose some of the tunable parameters in AndroidFlingPhysics as prefs. r=kats
authorBotond Ballo <botond@mozilla.com>
Fri, 20 Apr 2018 18:55:08 -0400
changeset 472038 68f4c6d260436e8a7d18f3f77de2cb094acc77ff
parent 472037 9ee2b45eeb3abb0ee922dddfd2adbd47fa4dd261
child 472039 8c7c5f3460062a19cc709a772c7586977af3d32b
push id1728
push userjlund@mozilla.com
push dateMon, 18 Jun 2018 21:12:27 +0000
treeherdermozilla-release@c296fde26f5f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskats
bugs1448439
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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)