Bug 1402498 - Add ScrollAnimationMSDPhysics, can be enabled using general.smoothScroll.msdPhysics.enabled. r=rhunt
authorMarkus Stange <mstange@themasta.com>
Tue, 26 Sep 2017 20:55:35 -0400
changeset 434615 9c91a596243be462ad80d6f5e9c83833d59bf5f5
parent 434614 884587d3b510a8268ab420adc469f45b8b568dac
child 434616 febc67e303a2101bce0704ba31f5c8e74c755b45
push id8114
push userjlorenzo@mozilla.com
push dateThu, 02 Nov 2017 16:33:21 +0000
treeherdermozilla-beta@73e0d89a540f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrhunt
bugs1402498
milestone58.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 1402498 - Add ScrollAnimationMSDPhysics, can be enabled using general.smoothScroll.msdPhysics.enabled. r=rhunt MozReview-Commit-ID: fr8Q9iod5k
gfx/layers/apz/src/GenericScrollAnimation.cpp
gfx/thebes/gfxPrefs.h
layout/generic/ScrollAnimationMSDPhysics.cpp
layout/generic/ScrollAnimationMSDPhysics.h
layout/generic/moz.build
layout/generic/nsGfxScrollFrame.cpp
modules/libpref/init/all.js
--- a/gfx/layers/apz/src/GenericScrollAnimation.cpp
+++ b/gfx/layers/apz/src/GenericScrollAnimation.cpp
@@ -6,28 +6,33 @@
 
 #include "GenericScrollAnimation.h"
 
 #include "AsyncPanZoomController.h"
 #include "gfxPrefs.h"
 #include "nsPoint.h"
 #include "ScrollAnimationPhysics.h"
 #include "ScrollAnimationBezierPhysics.h"
+#include "ScrollAnimationMSDPhysics.h"
 
 namespace mozilla {
 namespace layers {
 
 GenericScrollAnimation::GenericScrollAnimation(AsyncPanZoomController& aApzc,
                                                const nsPoint& aInitialPosition,
                                                const ScrollAnimationBezierPhysicsSettings& aSettings)
   : mApzc(aApzc)
-  , mAnimationPhysics(MakeUnique<ScrollAnimationBezierPhysics>(aInitialPosition, aSettings))
   , mFinalDestination(aInitialPosition)
   , mForceVerticalOverscroll(false)
 {
+  if (gfxPrefs::SmoothScrollMSDPhysicsEnabled()) {
+    mAnimationPhysics = MakeUnique<ScrollAnimationMSDPhysics>(aInitialPosition);
+  } else {
+    mAnimationPhysics = MakeUnique<ScrollAnimationBezierPhysics>(aInitialPosition, aSettings);
+  }
 }
 
 void
 GenericScrollAnimation::UpdateDelta(TimeStamp aTime, nsPoint aDelta, const nsSize& aCurrentVelocity)
 {
   mFinalDestination += aDelta;
 
   Update(aTime, aCurrentVelocity);
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -399,16 +399,31 @@ private:
   DECL_GFX_PREF(Live, "general.smoothScroll.pixels",           PixelSmoothScrollEnabled, bool, true);
   DECL_GFX_PREF(Live, "general.smoothScroll.pixels.durationMaxMS",
                 PixelSmoothScrollMaxDurationMs, int32_t, 150);
   DECL_GFX_PREF(Live, "general.smoothScroll.pixels.durationMinMS",
                 PixelSmoothScrollMinDurationMs, int32_t, 150);
   DECL_GFX_PREF(Live, "general.smoothScroll.stopDecelerationWeighting",
                 SmoothScrollStopDecelerationWeighting, float, 0.4f);
 
+  DECL_GFX_PREF(Live, "general.smoothScroll.msdPhysics.enabled",
+                SmoothScrollMSDPhysicsEnabled, bool, false);
+  DECL_GFX_PREF(Live, "general.smoothScroll.msdPhysics.continuousMotionMaxDeltaMS",
+                SmoothScrollMSDPhysicsContinuousMotionMaxDeltaMS, int32_t, 120);
+  DECL_GFX_PREF(Live, "general.smoothScroll.msdPhysics.motionBeginSpringConstant",
+                SmoothScrollMSDPhysicsMotionBeginSpringConstant, int32_t, 1250);
+  DECL_GFX_PREF(Live, "general.smoothScroll.msdPhysics.slowdownMinDeltaMS",
+                SmoothScrollMSDPhysicsSlowdownMinDeltaMS, int32_t, 12);
+  DECL_GFX_PREF(Live, "general.smoothScroll.msdPhysics.slowdownMinDeltaRatio",
+                SmoothScrollMSDPhysicsSlowdownMinDeltaRatio, float, 1.3f);
+  DECL_GFX_PREF(Live, "general.smoothScroll.msdPhysics.slowdownSpringConstant",
+                SmoothScrollMSDPhysicsSlowdownSpringConstant, int32_t, 2000);
+  DECL_GFX_PREF(Live, "general.smoothScroll.msdPhysics.regularSpringConstant",
+                SmoothScrollMSDPhysicsRegularSpringConstant, int32_t, 1000);
+
   DECL_GFX_PREF(Once, "gfx.android.rgb16.force",               AndroidRGB16Force, bool, false);
 #if defined(ANDROID)
   DECL_GFX_PREF(Once, "gfx.apitrace.enabled",                  UseApitrace, bool, false);
 #endif
 #if defined(RELEASE_OR_BETA)
   // "Skip" means this is locked to the default value in beta and release.
   DECL_GFX_PREF(Skip, "gfx.blocklist.all",                     BlocklistAll, int32_t, 0);
 #else
new file mode 100644
--- /dev/null
+++ b/layout/generic/ScrollAnimationMSDPhysics.cpp
@@ -0,0 +1,109 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#include "ScrollAnimationMSDPhysics.h"
+#include "gfxPrefs.h"
+
+using namespace mozilla;
+
+ScrollAnimationMSDPhysics::ScrollAnimationMSDPhysics(const nsPoint& aStartPos)
+ : mStartPos(aStartPos)
+ , mModelX(0, 0, 0, gfxPrefs::SmoothScrollMSDPhysicsRegularSpringConstant(), 1)
+ , mModelY(0, 0, 0, gfxPrefs::SmoothScrollMSDPhysicsRegularSpringConstant(), 1)
+ , mIsFirstIteration(true)
+{
+}
+
+void
+ScrollAnimationMSDPhysics::Update(const TimeStamp& aTime,
+                                  const nsPoint& aDestination,
+                                  const nsSize& aCurrentVelocity)
+{
+  double springConstant = ComputeSpringConstant(aTime);
+
+  // mLastSimulatedTime is the most recent time that this animation has been
+  // "observed" at. We don't want to update back to a state in the past, so we
+  // set mStartTime to the more recent of mLastSimulatedTime and aTime.
+  // aTime can be in the past if we're processing an input event whose internal
+  // timestamp is in the past.
+  if (mLastSimulatedTime && aTime < mLastSimulatedTime) {
+    mStartTime = mLastSimulatedTime;
+  } else {
+    mStartTime = aTime;
+  }
+
+  if (!mIsFirstIteration) {
+    mStartPos = PositionAt(mStartTime);
+  }
+
+  mLastSimulatedTime = mStartTime;
+  mDestination = aDestination;
+  mModelX = AxisPhysicsMSDModel(mStartPos.x, aDestination.x,
+                                aCurrentVelocity.width, springConstant, 1);
+  mModelY = AxisPhysicsMSDModel(mStartPos.y, aDestination.y,
+                                aCurrentVelocity.height, springConstant, 1);
+  mIsFirstIteration = false;
+}
+
+double
+ScrollAnimationMSDPhysics::ComputeSpringConstant(const TimeStamp& aTime)
+{
+  if (!mPreviousEventTime) {
+    mPreviousEventTime = aTime;
+    mPreviousDelta = TimeDuration();
+    return gfxPrefs::SmoothScrollMSDPhysicsMotionBeginSpringConstant();
+  }
+
+  TimeDuration delta = aTime - mPreviousEventTime;
+  TimeDuration previousDelta = mPreviousDelta;
+
+  mPreviousEventTime = aTime;
+  mPreviousDelta = delta;
+
+  double deltaMS = delta.ToMilliseconds();
+  if (deltaMS >= gfxPrefs::SmoothScrollMSDPhysicsContinuousMotionMaxDeltaMS()) {
+    return gfxPrefs::SmoothScrollMSDPhysicsMotionBeginSpringConstant();
+  }
+
+  if (previousDelta &&
+      deltaMS >= gfxPrefs::SmoothScrollMSDPhysicsSlowdownMinDeltaMS() &&
+      deltaMS >= previousDelta.ToMilliseconds() * gfxPrefs::SmoothScrollMSDPhysicsSlowdownMinDeltaRatio()) {
+    // The rate of events has slowed (the time delta between events has
+    // increased) enough that we think that the current scroll motion is coming
+    // to a stop. Use a stiffer spring in order to reach the destination more
+    // quickly.
+    return gfxPrefs::SmoothScrollMSDPhysicsSlowdownSpringConstant();
+  }
+
+  return gfxPrefs::SmoothScrollMSDPhysicsRegularSpringConstant();
+}
+
+void
+ScrollAnimationMSDPhysics::SimulateUntil(const TimeStamp& aTime)
+{
+  if (!mLastSimulatedTime || aTime < mLastSimulatedTime) {
+    return;
+  }
+  TimeDuration delta = aTime - mLastSimulatedTime;
+  mModelX.Simulate(delta);
+  mModelY.Simulate(delta);
+  mLastSimulatedTime = aTime;
+}
+
+nsPoint
+ScrollAnimationMSDPhysics::PositionAt(const TimeStamp& aTime)
+{
+  SimulateUntil(aTime);
+  return nsPoint(NSToCoordRound(mModelX.GetPosition()),
+                 NSToCoordRound(mModelY.GetPosition()));
+}
+
+nsSize
+ScrollAnimationMSDPhysics::VelocityAt(const TimeStamp& aTime)
+{
+  SimulateUntil(aTime);
+  return nsSize(NSToCoordRound(mModelX.GetVelocity()),
+                NSToCoordRound(mModelY.GetVelocity()));
+}
new file mode 100644
--- /dev/null
+++ b/layout/generic/ScrollAnimationMSDPhysics.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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_layout_ScrollAnimationMSDPhysics_h_
+#define mozilla_layout_ScrollAnimationMSDPhysics_h_
+
+#include "ScrollAnimationPhysics.h"
+#include "mozilla/layers/AxisPhysicsMSDModel.h"
+
+namespace mozilla {
+
+// This class implements a cubic MSD timing function and automatically
+// adapts the animation duration based on the scrolling rate.
+class ScrollAnimationMSDPhysics : public ScrollAnimationPhysics
+{
+public:
+  typedef mozilla::layers::AxisPhysicsMSDModel AxisPhysicsMSDModel;
+
+  explicit ScrollAnimationMSDPhysics(const nsPoint& aStartPos);
+
+  void Update(const TimeStamp& aTime,
+              const nsPoint& aDestination,
+              const nsSize& aCurrentVelocity) override;
+
+  // Get the velocity at a point in time in nscoords/sec.
+  nsSize VelocityAt(const TimeStamp& aTime) override;
+
+  // Returns the expected scroll position at a given point in time, in app
+  // units, relative to the scroll frame.
+  nsPoint PositionAt(const TimeStamp& aTime) override;
+
+  bool IsFinished(const TimeStamp& aTime) override {
+    SimulateUntil(aTime);
+    return mModelX.IsFinished(1) && mModelY.IsFinished(1);
+  }
+
+protected:
+  double ComputeSpringConstant(const TimeStamp& aTime);
+  void SimulateUntil(const TimeStamp& aTime);
+
+  TimeStamp mPreviousEventTime;
+  TimeDuration mPreviousDelta;
+
+  TimeStamp mStartTime;
+
+  nsPoint mStartPos;
+  nsPoint mDestination;
+  TimeStamp mLastSimulatedTime;
+  AxisPhysicsMSDModel mModelX;
+  AxisPhysicsMSDModel mModelY;
+  bool mIsFirstIteration;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_layout_ScrollAnimationMSDPhysics_h_
--- a/layout/generic/moz.build
+++ b/layout/generic/moz.build
@@ -96,16 +96,17 @@ EXPORTS += [
     'nsRubyTextContainerFrame.h',
     'nsRubyTextFrame.h',
     'nsSplittableFrame.h',
     'nsSubDocumentFrame.h',
     'nsTextFrameUtils.h',
     'nsTextRunTransformations.h',
     'RubyUtils.h',
     'ScrollAnimationBezierPhysics.h',
+    'ScrollAnimationMSDPhysics.h',
     'ScrollAnimationPhysics.h',
     'ScrollbarActivity.h',
     'ScrollSnap.h',
     'TextDrawTarget.h',
     'Visibility.h',
 ]
 
 EXPORTS.mozilla += [
@@ -173,16 +174,17 @@ UNIFIED_SOURCES += [
     'nsTextFrame.cpp',
     'nsTextFrameUtils.cpp',
     'nsTextRunTransformations.cpp',
     'nsVideoFrame.cpp',
     'ReflowInput.cpp',
     'ReflowOutput.cpp',
     'RubyUtils.cpp',
     'ScrollAnimationBezierPhysics.cpp',
+    'ScrollAnimationMSDPhysics.cpp',
     'ScrollbarActivity.cpp',
     'ScrollSnap.cpp',
     'ScrollVelocityQueue.cpp',
     'StickyScrollContainer.cpp',
     'TextOverflow.cpp',
     'ViewportFrame.cpp',
 ]
 
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -54,16 +54,17 @@
 #include "nsSVGIntegrationUtils.h"
 #include "nsIScrollPositionListener.h"
 #include "StickyScrollContainer.h"
 #include "nsIFrameInlines.h"
 #include "gfxPlatform.h"
 #include "gfxPrefs.h"
 #include "ScrollAnimationPhysics.h"
 #include "ScrollAnimationBezierPhysics.h"
+#include "ScrollAnimationMSDPhysics.h"
 #include "ScrollSnap.h"
 #include "UnitTransforms.h"
 #include "nsPluginFrame.h"
 #include "nsSliderFrame.h"
 #include "mozilla/layers/APZCCallbackHelper.h"
 #include <mozilla/layers/AxisPhysicsModel.h>
 #include <mozilla/layers/AxisPhysicsMSDModel.h>
 #include "mozilla/layers/LayerTransactionChild.h"
@@ -1907,17 +1908,17 @@ private:
   }
 };
 
 /*
  * Calculate duration, possibly dynamically according to events rate and event origin.
  * (also maintain previous timestamps - which are only used here).
  */
 static ScrollAnimationBezierPhysicsSettings
-ComputeAnimationSettingsForOrigin(nsIAtom *aOrigin)
+ComputeBezierAnimationSettingsForOrigin(nsIAtom *aOrigin)
 {
   int32_t minMS = 0;
   int32_t maxMS = 0;
   bool isOriginSmoothnessEnabled = false;
   double intervalRatio = 1;
 
   // Default values for all preferences are defined in all.js
   static const int32_t kDefaultMinMS = 150, kDefaultMaxMS = 150;
@@ -1967,19 +1968,24 @@ ScrollFrameHelper::AsyncScroll::InitSmoo
   }
   // Likewise we should never get APZ-triggered scrolls here, and if that changes
   // something is likely broken somewhere.
   MOZ_ASSERT(aOrigin != nsGkAtoms::apz);
 
   // Read preferences only on first iteration or for a different event origin.
   if (!mAnimationPhysics || aOrigin != mOrigin) {
     mOrigin = aOrigin;
-    ScrollAnimationBezierPhysicsSettings settings = ComputeAnimationSettingsForOrigin(mOrigin);
-    mAnimationPhysics =
-      MakeUnique<ScrollAnimationBezierPhysics>(aInitialPosition, settings);
+    if (gfxPrefs::SmoothScrollMSDPhysicsEnabled()) {
+      mAnimationPhysics = MakeUnique<ScrollAnimationMSDPhysics>(aInitialPosition);
+    } else {
+      ScrollAnimationBezierPhysicsSettings settings =
+        ComputeBezierAnimationSettingsForOrigin(mOrigin);
+      mAnimationPhysics =
+        MakeUnique<ScrollAnimationBezierPhysics>(aInitialPosition, settings);
+    }
   }
 
   mRange = aRange;
 
   mAnimationPhysics->Update(aTime, aDestination, aCurrentVelocity);
 }
 
 bool
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -2801,16 +2801,24 @@ pref("general.smoothScroll.other", true)
 // should be longer than scroll events intervals (or else the scroll will stop
 // before the next event arrives - we're guessing next interval by averaging recent
 // intervals).
 // This defines how longer is the duration compared to events interval (percentage)
 pref("general.smoothScroll.durationToIntervalRatio", 200);
 // These two prefs determine the timing function.
 pref("general.smoothScroll.currentVelocityWeighting", "0.25");
 pref("general.smoothScroll.stopDecelerationWeighting", "0.4");
+// Alternative smooth scroll physics ("MSD" = Mass-Spring-Damper)
+pref("general.smoothScroll.msdPhysics.enabled", false);
+pref("general.smoothScroll.msdPhysics.continuousMotionMaxDeltaMS", 120);
+pref("general.smoothScroll.msdPhysics.motionBeginSpringConstant", 1250);
+pref("general.smoothScroll.msdPhysics.slowdownMinDeltaMS", 12);
+pref("general.smoothScroll.msdPhysics.slowdownMinDeltaRatio", "1.3");
+pref("general.smoothScroll.msdPhysics.slowdownSpringConstant", 2000);
+pref("general.smoothScroll.msdPhysics.regularSpringConstant", 1000);
 
 pref("profile.confirm_automigration",true);
 // profile.migration_behavior determines how the profiles root is set
 // 0 - use NS_APP_USER_PROFILES_ROOT_DIR
 // 1 - create one based on the NS4.x profile root
 // 2 - use, if not empty, profile.migration_directory otherwise same as 0
 pref("profile.migration_behavior",0);
 pref("profile.migration_directory", "");