Bug 1018255 - Use spring physics for snap-back animation. r=kats
authorBotond Ballo <botond@mozilla.com>
Tue, 03 Jun 2014 13:59:35 -0400
changeset 207625 77d99630aa73cd53c7b40ab1b9eae6db7f9a1487
parent 207624 05ce4d3cf94e8f03e81569340337122802d096c0
child 207626 eb8a567c8a121efbf43f52b7822acf78d8eb27d4
push id494
push userraliiev@mozilla.com
push dateMon, 25 Aug 2014 18:42:16 +0000
treeherdermozilla-release@a3cc3e46b571 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskats
bugs1018255
milestone32.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 1018255 - Use spring physics for snap-back animation. r=kats
b2g/app/b2g.js
gfx/layers/apz/src/AsyncPanZoomController.cpp
gfx/layers/apz/src/Axis.cpp
gfx/layers/apz/src/Axis.h
gfx/thebes/gfxPrefs.h
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -955,18 +955,19 @@ pref("apz.axis_lock_mode", 2);
 pref("apz.subframe.enabled", true);
 
 // Overscroll-related settings
 pref("apz.overscroll.enabled", false);
 pref("apz.overscroll.fling_friction", "0.02");
 pref("apz.overscroll.fling_stopped_threshold", "0.4");
 pref("apz.overscroll.clamping", "0.5");
 pref("apz.overscroll.z_effect", "0.2");
-pref("apz.overscroll.snap_back_accel", "0.003");
-pref("apz.overscroll.snap_back_init_vel", "1");
+pref("apz.overscroll.snap_back.spring_stiffness", "0.6");
+pref("apz.overscroll.snap_back.spring_friction", "0.1");
+pref("apz.overscroll.snap_back.mass", "1000");
 
 // This preference allows FirefoxOS apps (and content, I think) to force
 // the use of software (instead of hardware accelerated) 2D canvases by
 // creating a context like this:
 //
 //   canvas.getContext('2d', { willReadFrequently: true })
 //
 // Using a software canvas can save memory when JS calls getImageData()
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -230,23 +230,27 @@ typedef GeckoContentController::APZState
  *
  * "apz.overscroll.z_effect"
  * The fraction of "apz.overscroll.clamping" which can become blank as a result
  * of overscroll, along the axis opposite to the axis of overscroll. Called
  * "z_effect" because the shrinking that brings about the blank space on the
  * opposite axis creates the effect of the page moving away from you along a
  * "z" axis.
  *
- * "apz.overscroll.snap_back_accel"
- * Amount of acceleration applied during the snap-back animation.
+ * "apz.overscroll.snap_back.spring_stiffness"
+ * The stiffness of the spring used in the physics model for the overscroll
+ * snap-back animation.
  *
- * "apz.overscroll.snap_back_init_vel"
- * Initial velocity of a snap-back animation along one axis.
- * Units: screen pixels per millisecond
- * requests.
+ * "apz.overscroll.snap_back.spring_damping"
+ * The friction of the spring used in the physics model for the overscroll
+ * snap-back animation.
+ *
+ * "apz.overscroll.snap_back.mass"
+ * The mass of the page in the physics model for the overscroll snap-back
+ * animation.
  *
  * "apz.pan_repaint_interval"
  * Maximum amount of time while panning before sending a viewport change. This
  * will asynchronously repaint the page. It is also forced when panning stops.
  *
  * "apz.test.logging_enabled"
  * Enable logging of APZ test data (see bug 961289).
  *
@@ -485,21 +489,17 @@ private:
   // |mResolution| fields on this.
   CSSPoint mEndOffset;
   CSSToScreenScale mEndZoom;
 };
 
 class OverscrollSnapBackAnimation: public AsyncPanZoomAnimation {
 public:
   OverscrollSnapBackAnimation(AsyncPanZoomController& aApzc)
-    : mApzc(aApzc)
-  {
-    mApzc.mX.StartSnapBack();
-    mApzc.mY.StartSnapBack();
-  }
+    : mApzc(aApzc) {}
 
   virtual bool Sample(FrameMetrics& aFrameMetrics,
                       const TimeDuration& aDelta) MOZ_OVERRIDE
   {
     // Can't inline these variables due to short-circuit evaluation.
     bool continueX = mApzc.mX.SampleSnapBack(aDelta);
     bool continueY = mApzc.mY.SampleSnapBack(aDelta);
     return continueX || continueY;
--- a/gfx/layers/apz/src/Axis.cpp
+++ b/gfx/layers/apz/src/Axis.cpp
@@ -116,31 +116,37 @@ void Axis::OverscrollBy(float aOverscrol
   }
   mOverscroll += aOverscroll;
 }
 
 float Axis::GetOverscroll() const {
   return mOverscroll;
 }
 
-void Axis::StartSnapBack() {
-  float initialSnapBackVelocity = gfxPrefs::APZSnapBackInitialVelocity();
-  if (mOverscroll > 0) {
-    mVelocity = -initialSnapBackVelocity;
-  } else {
-    mVelocity = initialSnapBackVelocity;
-  }
-}
-
 bool Axis::SampleSnapBack(const TimeDuration& aDelta) {
-  // Accelerate the snap-back as time goes on.
-  // Note: this method of acceleration isn't perfectly smooth, as it assumes
+  // Apply spring physics to the snap-back as time goes on.
+  // Note: this method of sampling isn't perfectly smooth, as it assumes
   // a constant velocity over 'aDelta', instead of an accelerating velocity.
   // (The way we applying friction to flings has the same issue.)
-  mVelocity *= pow(1.0f + gfxPrefs::APZSnapBackAcceleration(), float(aDelta.ToMilliseconds()));
+  // Hooke's law with damping:
+  //   F = -kx - bv
+  // where
+  //   k is a constant related to the stiffness of the spring
+  //     The larger the constant, the stiffer the spring.
+  //   x is the displacement of the end of the spring from its equilibrium
+  //     In our scenario, it's the amount of overscroll on the axis.
+  //   b is a constant that provides damping (friction)
+  //   v is the velocity of the point at the end of the spring
+  // See http://gafferongames.com/game-physics/spring-physics/
+  const float kSpringStiffness = gfxPrefs::APZOverscrollSnapBackSpringStiffness();
+  const float kSpringFriction = gfxPrefs::APZOverscrollSnapBackSpringFriction();
+  const float kMass = gfxPrefs::APZOverscrollSnapBackMass();
+  float force = -1 * kSpringStiffness * mOverscroll - kSpringFriction * mVelocity;
+  float acceleration = force / kMass;
+  mVelocity += acceleration * aDelta.ToMilliseconds();
   float screenDisplacement = mVelocity * aDelta.ToMilliseconds();
   float cssDisplacement = screenDisplacement / GetFrameMetrics().GetZoom().scale;
   if (mOverscroll > 0) {
     if (cssDisplacement > 0) {
       NS_WARNING("Overscroll snap-back animation is moving in the wrong direction!");
       return false;
     }
     mOverscroll = std::max(mOverscroll + cssDisplacement, 0.0f);
--- a/gfx/layers/apz/src/Axis.h
+++ b/gfx/layers/apz/src/Axis.h
@@ -94,21 +94,16 @@ public:
   void OverscrollBy(float aOverscroll);
 
   /**
    * Return the amount of overscroll on this axis, in CSS pixels.
    */
   float GetOverscroll() const;
 
   /**
-   * Start a snap-back animation to relieve overscroll.
-   */
-  void StartSnapBack();
-
-  /**
    * Sample the snap-back animation to relieve overscroll.
    * |aDelta| is the time since the last sample.
    */
   bool SampleSnapBack(const TimeDuration& aDelta);
 
   /**
    * Return whether this axis is overscrolled in either direction.
    */
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -121,18 +121,19 @@ private:
   DECL_GFX_PREF(Once, "apz.max_velocity_queue_size",           APZMaxVelocityQueueSize, uint32_t, 5);
   DECL_GFX_PREF(Live, "apz.min_skate_speed",                   APZMinSkateSpeed, float, 1.0f);
   DECL_GFX_PREF(Live, "apz.num_paint_duration_samples",        APZNumPaintDurationSamples, int32_t, 3);
   DECL_GFX_PREF(Live, "apz.overscroll.enabled",                APZOverscrollEnabled, bool, false);
   DECL_GFX_PREF(Live, "apz.overscroll.fling_friction",         APZOverscrollFlingFriction, float, 0.02f);
   DECL_GFX_PREF(Live, "apz.overscroll.fling_stopped_threshold", APZOverscrollFlingStoppedThreshold, float, 0.4f);
   DECL_GFX_PREF(Live, "apz.overscroll.clamping",               APZOverscrollClamping, float, 0.5f);
   DECL_GFX_PREF(Live, "apz.overscroll.z_effect",               APZOverscrollZEffect, float, 0.2f);
-  DECL_GFX_PREF(Once, "apz.overscroll.snap_back_accel",        APZSnapBackAcceleration, float, 0.002f);
-  DECL_GFX_PREF(Live, "apz.overscroll.snap_back_init_vel",     APZSnapBackInitialVelocity, float, 1.0f);
+  DECL_GFX_PREF(Live, "apz.overscroll.snap_back.spring_stiffness", APZOverscrollSnapBackSpringStiffness, float, 0.6f);
+  DECL_GFX_PREF(Live, "apz.overscroll.snap_back.spring_friction", APZOverscrollSnapBackSpringFriction, float, 0.1f);
+  DECL_GFX_PREF(Live, "apz.overscroll.snap_back.mass",         APZOverscrollSnapBackMass, float, 1000.0f);
   DECL_GFX_PREF(Live, "apz.pan_repaint_interval",              APZPanRepaintInterval, int32_t, 250);
   DECL_GFX_PREF(Live, "apz.subframe.enabled",                  APZSubframeEnabled, bool, false);
   DECL_GFX_PREF(Once, "apz.test.logging_enabled",              APZTestLoggingEnabled, bool, false);
   DECL_GFX_PREF(Live, "apz.touch_start_tolerance",             APZTouchStartTolerance, float, 1.0f/4.5f);
   DECL_GFX_PREF(Live, "apz.use_paint_duration",                APZUsePaintDuration, bool, true);
   DECL_GFX_PREF(Live, "apz.velocity_bias",                     APZVelocityBias, float, 1.0f);
   DECL_GFX_PREF(Live, "apz.velocity_relevance_time_ms",        APZVelocityRelevanceTime, uint32_t, 150);
   DECL_GFX_PREF(Live, "apz.x_skate_size_multiplier",           APZXSkateSizeMultiplier, float, 1.5f);