Bug 1236046 - Don't use a stupidly small time delta to compute a velocity. r=botond
authorKartikaya Gupta <kgupta@mozilla.com>
Wed, 03 Feb 2016 14:07:45 -0500
changeset 305905 17a70b41806c3dc2a10d056b86403c89724f3b42
parent 305904 9c4d77b43082b668b2c437917707264bea86a2e8
child 305906 80bf314a61f6304b62ae956106ef24661b34fbb6
push id9214
push userraliiev@mozilla.com
push dateMon, 07 Mar 2016 14:25:21 +0000
treeherdermozilla-aurora@8849dd1a4a79 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbotond
bugs1236046
milestone47.0a1
Bug 1236046 - Don't use a stupidly small time delta to compute a velocity. r=botond
gfx/layers/apz/src/Axis.cpp
gfx/layers/apz/src/Axis.h
--- a/gfx/layers/apz/src/Axis.cpp
+++ b/gfx/layers/apz/src/Axis.cpp
@@ -25,27 +25,35 @@
 #include "gfxPrefs.h"                   // for the preferences
 
 #define AXIS_LOG(...)
 // #define AXIS_LOG(...) printf_stderr("AXIS: " __VA_ARGS__)
 
 namespace mozilla {
 namespace layers {
 
+// When we compute the velocity we do so by taking two input events and
+// dividing the distance delta over the time delta. In some cases the time
+// delta can be really small, which can make the velocity computation very
+// volatile. To avoid this we impose a minimum time delta below which we do
+// not recompute the velocity.
+const uint32_t MIN_VELOCITY_SAMPLE_TIME_MS = 5;
+
 bool FuzzyEqualsCoordinate(float aValue1, float aValue2)
 {
   return FuzzyEqualsAdditive(aValue1, aValue2, COORDINATE_EPSILON)
       || FuzzyEqualsMultiplicative(aValue1, aValue2);
 }
 
 extern StaticAutoPtr<ComputedTimingFunction> gVelocityCurveFunction;
 
 Axis::Axis(AsyncPanZoomController* aAsyncPanZoomController)
   : mPos(0),
-    mPosTimeMs(0),
+    mVelocitySampleTimeMs(0),
+    mVelocitySamplePos(0),
     mVelocity(0.0f),
     mAxisLocked(false),
     mAsyncPanZoomController(aAsyncPanZoomController),
     mOverscroll(0),
     mFirstOverscrollAnimationSample(0),
     mLastOverscrollPeak(0),
     mOverscrollScale(1.0f)
 {
@@ -62,26 +70,30 @@ float Axis::ToLocalVelocity(float aVeloc
       mAsyncPanZoomController->ToParentLayerCoordinates(velocity, panStart);
   return localVelocity.Length();
 }
 
 void Axis::UpdateWithTouchAtDevicePoint(ParentLayerCoord aPos, ParentLayerCoord aAdditionalDelta, uint32_t aTimestampMs) {
   // mVelocityQueue is controller-thread only
   APZThreadUtils::AssertOnControllerThread();
 
-  if (aTimestampMs == mPosTimeMs) {
-    // This could be a duplicate event, or it could be a legitimate event
-    // on some platforms that generate events really fast. As a compromise
-    // update mPos so we don't run into problems like bug 1042734, even though
-    // that means the velocity will be stale. Better than doing a divide-by-zero.
+  if (aTimestampMs <= mVelocitySampleTimeMs + MIN_VELOCITY_SAMPLE_TIME_MS) {
+    // See also the comment on MIN_VELOCITY_SAMPLE_TIME_MS.
+    // We still update mPos so that the positioning is correct (and we don't run
+    // into problems like bug 1042734) but the velocity will remain where it was.
+    // In particular we don't update either mVelocitySampleTimeMs or
+    // mVelocitySamplePos so that eventually when we do get an event with the
+    // required time delta we use the corresponding distance delta as well.
+    AXIS_LOG("%p|%s skipping velocity computation for small time delta %dms\n",
+        mAsyncPanZoomController, Name(), (aTimestampMs - mVelocitySampleTimeMs));
     mPos = aPos;
     return;
   }
 
-  float newVelocity = mAxisLocked ? 0.0f : (float)(mPos - aPos + aAdditionalDelta) / (float)(aTimestampMs - mPosTimeMs);
+  float newVelocity = mAxisLocked ? 0.0f : (float)(mVelocitySamplePos - aPos + aAdditionalDelta) / (float)(aTimestampMs - mVelocitySampleTimeMs);
   if (gfxPrefs::APZMaxVelocity() > 0.0f) {
     bool velocityIsNegative = (newVelocity < 0);
     newVelocity = fabs(newVelocity);
 
     float maxVelocity = ToLocalVelocity(gfxPrefs::APZMaxVelocity());
     newVelocity = std::min(newVelocity, maxVelocity);
 
     if (gfxPrefs::APZCurveThreshold() > 0.0f && gfxPrefs::APZCurveThreshold() < gfxPrefs::APZMaxVelocity()) {
@@ -102,29 +114,31 @@ void Axis::UpdateWithTouchAtDevicePoint(
       newVelocity = -newVelocity;
     }
   }
 
   AXIS_LOG("%p|%s updating velocity to %f with touch\n",
     mAsyncPanZoomController, Name(), newVelocity);
   mVelocity = newVelocity;
   mPos = aPos;
-  mPosTimeMs = aTimestampMs;
+  mVelocitySampleTimeMs = aTimestampMs;
+  mVelocitySamplePos = aPos;
 
   // Limit queue size pased on pref
   mVelocityQueue.AppendElement(std::make_pair(aTimestampMs, mVelocity));
   if (mVelocityQueue.Length() > gfxPrefs::APZMaxVelocityQueueSize()) {
     mVelocityQueue.RemoveElementAt(0);
   }
 }
 
 void Axis::StartTouch(ParentLayerCoord aPos, uint32_t aTimestampMs) {
   mStartPos = aPos;
   mPos = aPos;
-  mPosTimeMs = aTimestampMs;
+  mVelocitySampleTimeMs = aTimestampMs;
+  mVelocitySamplePos = aPos;
   mAxisLocked = false;
 }
 
 bool Axis::AdjustDisplacement(ParentLayerCoord aDisplacement,
                               /* ParentLayerCoord */ float& aDisplacementOut,
                               /* ParentLayerCoord */ float& aOverscrollAmountOut,
                               bool aForceOverscroll /* = false */)
 {
--- a/gfx/layers/apz/src/Axis.h
+++ b/gfx/layers/apz/src/Axis.h
@@ -246,17 +246,24 @@ public:
   virtual CSSToParentLayerScale GetScaleForAxis(const CSSToParentLayerScale2D& aScale) const = 0;
 
   virtual ScreenPoint MakePoint(ScreenCoord aCoord) const = 0;
 
   virtual const char* Name() const = 0;
 
 protected:
   ParentLayerCoord mPos;
-  uint32_t mPosTimeMs;
+
+  // mVelocitySampleTimeMs and mVelocitySamplePos are the time and position
+  // used in the last velocity sampling. They get updated when a new sample is
+  // taken (which may not happen on every input event, if the time delta is too
+  // small).
+  uint32_t mVelocitySampleTimeMs;
+  ParentLayerCoord mVelocitySamplePos;
+
   ParentLayerCoord mStartPos;
   float mVelocity;      // Units: ParentLayerCoords per millisecond
   bool mAxisLocked;     // Whether movement on this axis is locked.
   AsyncPanZoomController* mAsyncPanZoomController;
 
   // mOverscroll is the displacement of an oscillating spring from its resting
   // state. The resting state moves as the overscroll animation progresses.
   ParentLayerCoord mOverscroll;