Bug 1257959 - Dynamic toolbar transition seems to slow down flings r=kats
authorRandall Barker <rbarker@mozilla.com>
Tue, 05 Apr 2016 15:12:48 -0700
changeset 291936 cce22ba996a65e6be45fcf4cdf0187dbbcfac645
parent 291935 9e574b87617bba8747e714145253e9d5a98e126b
child 291937 67f70018cbb583f57e35380b77b1e57efae5fd0b
push id74736
push userrbarker@mozilla.com
push dateWed, 06 Apr 2016 21:42:06 +0000
treeherdermozilla-inbound@cce22ba996a6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskats
bugs1257959
milestone48.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 1257959 - Dynamic toolbar transition seems to slow down flings r=kats
gfx/layers/apz/src/APZCTreeManager.cpp
gfx/layers/apz/src/APZCTreeManager.h
gfx/layers/apz/src/AsyncPanZoomController.cpp
gfx/layers/apz/src/AsyncPanZoomController.h
gfx/layers/apz/src/Axis.cpp
gfx/layers/apz/src/Axis.h
mobile/android/base/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java
mobile/android/base/java/org/mozilla/gecko/gfx/JavaPanZoomController.java
mobile/android/base/java/org/mozilla/gecko/gfx/LayerView.java
mobile/android/base/java/org/mozilla/gecko/gfx/NativePanZoomController.java
mobile/android/base/java/org/mozilla/gecko/gfx/PanZoomController.java
widget/android/GeneratedJNINatives.h
widget/android/GeneratedJNIWrappers.cpp
widget/android/GeneratedJNIWrappers.h
widget/android/nsWindow.cpp
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -1072,16 +1072,24 @@ APZCTreeManager::ProcessMouseEvent(Widge
   nsEventStatus status = ReceiveInputEvent(input, aOutTargetGuid, aOutInputBlockId);
 
   aEvent.refPoint.x = input.mOrigin.x;
   aEvent.refPoint.y = input.mOrigin.y;
   aEvent.mFlags.mHandledByAPZ = true;
   return status;
 }
 
+void
+APZCTreeManager::ProcessTouchVelocity(uint32_t aTimestampMs, float aSpeedY)
+{
+  if (mApzcForInputBlock) {
+    mApzcForInputBlock->HandleTouchVelocity(aTimestampMs, aSpeedY);
+  }
+}
+
 nsEventStatus
 APZCTreeManager::ProcessWheelEvent(WidgetWheelEvent& aEvent,
                                    ScrollableLayerGuid* aOutTargetGuid,
                                    uint64_t* aOutInputBlockId)
 {
   ScrollWheelInput::ScrollMode scrollMode = ScrollWheelInput::SCROLLMODE_INSTANT;
   if (gfxPrefs::SmoothScrollEnabled() &&
       ((aEvent.mDeltaMode == nsIDOMWheelEvent::DOM_DELTA_LINE &&
--- a/gfx/layers/apz/src/APZCTreeManager.h
+++ b/gfx/layers/apz/src/APZCTreeManager.h
@@ -437,16 +437,24 @@ public:
      used by other production code.
   */
   RefPtr<HitTestingTreeNode> GetRootNode() const;
   already_AddRefed<AsyncPanZoomController> GetTargetAPZC(const ScreenPoint& aPoint,
                                                          HitTestResult* aOutHitResult,
                                                          bool* aOutHitScrollbar = nullptr);
   ScreenToParentLayerMatrix4x4 GetScreenToApzcTransform(const AsyncPanZoomController *aApzc) const;
   ParentLayerToScreenMatrix4x4 GetApzcToGeckoTransform(const AsyncPanZoomController *aApzc) const;
+
+  /**
+   * Process touch velocity.
+   * Sometimes the touch move event will have a velocity even though no scrolling
+   * is occurring such as when the toolbar is being hidden/shown in Fennec.
+   * This function can be called to have the y axis' velocity queue updated.
+   */
+  void ProcessTouchVelocity(uint32_t aTimestampMs, float aSpeedY);
 private:
   typedef bool (*GuidComparator)(const ScrollableLayerGuid&, const ScrollableLayerGuid&);
 
   /* Helpers */
   void AttachNodeToTree(HitTestingTreeNode* aNode,
                         HitTestingTreeNode* aParent,
                         HitTestingTreeNode* aNextSibling);
   already_AddRefed<AsyncPanZoomController> GetTargetAPZC(const ScrollableLayerGuid& aGuid);
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -1231,16 +1231,21 @@ nsEventStatus AsyncPanZoomController::Ha
     break;
   }
   default: NS_WARNING("Unhandled input event"); break;
   }
 
   return rv;
 }
 
+void AsyncPanZoomController::HandleTouchVelocity(uint32_t aTimesampMs, float aSpeedY)
+{
+  mY.HandleTouchVelocity(aTimesampMs, aSpeedY);
+}
+
 nsEventStatus AsyncPanZoomController::OnTouchStart(const MultiTouchInput& aEvent) {
   APZC_LOG("%p got a touch-start in state %d\n", this, mState);
   mPanDirRestricted = false;
   ParentLayerPoint point = GetFirstTouchPoint(aEvent);
 
   switch (mState) {
     case FLING:
     case ANIMATING_ZOOM:
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -259,16 +259,24 @@ public:
    * Currently some gestures are detected in GestureEventListener that calls
    * APZC back through this handler in order to avoid recursive calls to
    * APZC::HandleInputEvent() which is supposed to do the work for
    * ReceiveInputEvent().
    */
   nsEventStatus HandleGestureEvent(const InputData& aEvent);
 
   /**
+   * Handler for touch velocity.
+   * Sometimes the touch move event will have a velocity even though no scrolling
+   * is occurring such as when the toolbar is being hidden/shown in Fennec.
+   * This function can be called to have the y axis' velocity queue updated.
+   */
+  void HandleTouchVelocity(uint32_t aTimesampMs, float aSpeedY);
+
+  /**
    * Populates the provided object (if non-null) with the scrollable guid of this apzc.
    */
   void GetGuid(ScrollableLayerGuid* aGuidOut) const;
 
   /**
    * Returns the scrollable guid of this apzc.
    */
   ScrollableLayerGuid GetGuid() const;
--- a/gfx/layers/apz/src/Axis.cpp
+++ b/gfx/layers/apz/src/Axis.cpp
@@ -84,16 +84,31 @@ void Axis::UpdateWithTouchAtDevicePoint(
     // 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)(mVelocitySamplePos - aPos + aAdditionalDelta) / (float)(aTimestampMs - mVelocitySampleTimeMs);
+
+  newVelocity = ApplyFlingCurveToVelocity(newVelocity);
+
+  AXIS_LOG("%p|%s updating velocity to %f with touch\n",
+    mAsyncPanZoomController, Name(), newVelocity);
+  mVelocity = newVelocity;
+  mPos = aPos;
+  mVelocitySampleTimeMs = aTimestampMs;
+  mVelocitySamplePos = aPos;
+
+  AddVelocityToQueue(aTimestampMs, mVelocity);
+}
+
+float Axis::ApplyFlingCurveToVelocity(float aVelocity) const {
+  float newVelocity = aVelocity;
   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()) {
@@ -112,30 +127,36 @@ void Axis::UpdateWithTouchAtDevicePoint(
       }
     }
 
     if (velocityIsNegative) {
       newVelocity = -newVelocity;
     }
   }
 
-  AXIS_LOG("%p|%s updating velocity to %f with touch\n",
-    mAsyncPanZoomController, Name(), newVelocity);
-  mVelocity = newVelocity;
-  mPos = aPos;
-  mVelocitySampleTimeMs = aTimestampMs;
-  mVelocitySamplePos = aPos;
+  return newVelocity;
+}
 
-  // Limit queue size pased on pref
-  mVelocityQueue.AppendElement(std::make_pair(aTimestampMs, mVelocity));
+void Axis::AddVelocityToQueue(uint32_t aTimestampMs, float aVelocity) {
+  mVelocityQueue.AppendElement(std::make_pair(aTimestampMs, aVelocity));
   if (mVelocityQueue.Length() > gfxPrefs::APZMaxVelocityQueueSize()) {
     mVelocityQueue.RemoveElementAt(0);
   }
 }
 
+void Axis::HandleTouchVelocity(uint32_t aTimestampMs, float aSpeed) {
+  // mVelocityQueue is controller-thread only
+  APZThreadUtils::AssertOnControllerThread();
+
+  mVelocity = ApplyFlingCurveToVelocity(aSpeed);
+  mVelocitySampleTimeMs = aTimestampMs;
+
+  AddVelocityToQueue(aTimestampMs, mVelocity);
+}
+
 void Axis::StartTouch(ParentLayerCoord aPos, uint32_t aTimestampMs) {
   mStartPos = aPos;
   mPos = aPos;
   mVelocitySampleTimeMs = aTimestampMs;
   mVelocitySamplePos = aPos;
   mAxisLocked = false;
 }
 
--- a/gfx/layers/apz/src/Axis.h
+++ b/gfx/layers/apz/src/Axis.h
@@ -44,16 +44,23 @@ public:
    * Notify this Axis that a new touch has been received, including a timestamp
    * for when the touch was received. This triggers a recalculation of velocity.
    * This can also used for pan gesture events. For those events, the "touch"
    * location is stationary and the scroll displacement is passed in as
    * aAdditionalDelta.
    */
   void UpdateWithTouchAtDevicePoint(ParentLayerCoord aPos, ParentLayerCoord aAdditionalDelta, uint32_t aTimestampMs);
 
+protected:
+  float ApplyFlingCurveToVelocity(float aVelocity) const;
+  void AddVelocityToQueue(uint32_t aTimestampMs, float aVelocity);
+
+public:
+  void HandleTouchVelocity(uint32_t aTimestampMs, float aSpeed);
+
   /**
    * Notify this Axis that a touch has begun, i.e. the user has put their finger
    * on the screen but has not yet tried to pan.
    */
   void StartTouch(ParentLayerCoord aPos, uint32_t aTimestampMs);
 
   /**
    * Notify this Axis that a touch has ended gracefully. This may perform
--- a/mobile/android/base/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java
+++ b/mobile/android/base/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java
@@ -373,16 +373,21 @@ public class DynamicToolbarAnimator {
             if (inBetween || reachedThreshold || (atTopOfPage && isToolbarTranslated)) {
                 return translation;
             }
         }
 
         return 0;
     }
 
+    // Timestamp of the start of the touch event used to calculate toolbar velocity
+    private long mLastEventTime;
+    // Current velocity of the toolbar. Used to populate the velocity queue in C++APZ.
+    private float mVelocity;
+
     boolean onInterceptTouchEvent(MotionEvent event) {
         if (isPinned()) {
             return false;
         }
 
         // Animations should never co-exist with the user touching the screen.
         if (mAnimationTask != null) {
             mTarget.getView().removeRenderTask(mAnimationTask);
@@ -393,16 +398,17 @@ public class DynamicToolbarAnimator {
         // should reset and cause us to start over.
         if (event.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE ||
             event.getActionMasked() != MotionEvent.ACTION_MOVE ||
             event.getPointerCount() != 1)
         {
             if (mTouchStart != null) {
                 Log.v(LOGTAG, "Resetting touch sequence due to non-move");
                 mTouchStart = null;
+                mVelocity = 0.0f;
             }
 
             if (event.getActionMasked() == MotionEvent.ACTION_UP) {
                 // We need to do this even if the toolbar is already fully
                 // visible or fully hidden, because this is what triggers the
                 // viewport resize in content and updates the viewport metrics.
                 boolean toolbarMostlyVisible = mToolbarTranslation < (mMaxTranslation / 2);
                 Log.v(LOGTAG, "All fingers lifted, completing " + (toolbarMostlyVisible ? "show" : "hide"));
@@ -413,26 +419,36 @@ public class DynamicToolbarAnimator {
 
         if (mTouchStart != null) {
             float prevDir = mLastTouch - mTouchStart.y;
             float newDir = event.getRawY() - mLastTouch;
             if (prevDir != 0 && newDir != 0 && ((prevDir < 0) != (newDir < 0))) {
                 // If the direction of movement changed, reset the travel
                 // distance properties.
                 mTouchStart = null;
+                mVelocity = 0.0f;
             }
         }
 
         if (mTouchStart == null) {
             mTouchStart = new PointF(event.getRawX(), event.getRawY());
             mLastTouch = event.getRawY();
+            mLastEventTime = event.getEventTime();
             return false;
         }
 
         float deltaY = event.getRawY() - mLastTouch;
+        long currentTime = event.getEventTime();
+        float deltaTime = (float)(currentTime - mLastEventTime);
+        mLastEventTime = currentTime;
+        if (deltaTime > 0.0f) {
+            mVelocity = -deltaY / deltaTime;
+        } else {
+            mVelocity = 0.0f;
+        }
         mLastTouch = event.getRawY();
         float travelDistance = event.getRawY() - mTouchStart.y;
 
         ImmutableViewportMetrics metrics = mTarget.getViewportMetrics();
 
         if (metrics.getPageHeight() <= mTarget.getView().getHeight() &&
             mToolbarTranslation == 0) {
             // If the page is short and the toolbar is already visible, don't
@@ -460,16 +476,21 @@ public class DynamicToolbarAnimator {
             shiftLayerView(0);
         }
 
         fireListeners();
         mTarget.getView().requestRender();
         return true;
     }
 
+    // Get the current velocity of the toolbar.
+    float getVelocity() {
+        return mVelocity;
+    }
+
     public PointF getVisibleEndOfLayerView() {
         return new PointF(mTarget.getView().getWidth(),
             mTarget.getView().getHeight() - mMaxTranslation + mLayerViewTranslation);
     }
 
     private float bottomOfCssViewport(ImmutableViewportMetrics aMetrics) {
         return (isResizing() ? mHeightDuringResize : aMetrics.getHeight())
                 + mMaxTranslation - mLayerViewTranslation;
--- a/mobile/android/base/java/org/mozilla/gecko/gfx/JavaPanZoomController.java
+++ b/mobile/android/base/java/org/mozilla/gecko/gfx/JavaPanZoomController.java
@@ -317,16 +317,19 @@ class JavaPanZoomController
                 return animatedScale(0.2f);
             case KeyEvent.KEYCODE_ZOOM_OUT:
                 return animatedScale(-0.2f);
             }
         }
         return false;
     }
 
+    // Ignore MontionEvent velocity. Needed for C++APZ
+    public void onMotionEventVelocity(final long aEventTime, final float aSpeedY) {}
+
     /** This function MUST be called on the UI thread */
     @Override
     public boolean onMotionEvent(MotionEvent event) {
         if (Versions.preHCMR1) {
             return false;
         }
 
         switch (event.getSource() & InputDevice.SOURCE_CLASS_MASK) {
--- a/mobile/android/base/java/org/mozilla/gecko/gfx/LayerView.java
+++ b/mobile/android/base/java/org/mozilla/gecko/gfx/LayerView.java
@@ -220,16 +220,19 @@ public class LayerView extends ScrollVie
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
             requestFocus();
         }
         event.offsetLocation(0, -mSurfaceTranslation);
 
         if (mToolbarAnimator != null && mToolbarAnimator.onInterceptTouchEvent(event)) {
+            if (mPanZoomController != null) {
+                mPanZoomController.onMotionEventVelocity(event.getEventTime(), mToolbarAnimator.getVelocity());
+            }
             return true;
         }
         if (AppConstants.MOZ_ANDROID_APZ && !mLayerClient.isGeckoReady()) {
             // If gecko isn't loaded yet, don't try sending events to the
             // native code because it's just going to crash
             return true;
         }
         if (mPanZoomController != null && mPanZoomController.onTouchEvent(event)) {
--- a/mobile/android/base/java/org/mozilla/gecko/gfx/NativePanZoomController.java
+++ b/mobile/android/base/java/org/mozilla/gecko/gfx/NativePanZoomController.java
@@ -46,16 +46,19 @@ class NativePanZoomController extends JN
             float x, float y,
             float hScroll, float vScroll);
 
     @WrapForJNI
     private native boolean handleMouseEvent(
             int action, long time, int metaState,
             float x, float y, int buttons);
 
+    @WrapForJNI
+    private native void handleMotionEventVelocity(long time, float ySpeed);
+
     private boolean handleMotionEvent(MotionEvent event) {
         if (mDestroyed) {
             return false;
         }
 
         final int action = event.getActionMasked();
         final int count = event.getPointerCount();
 
@@ -190,16 +193,21 @@ class NativePanZoomController extends JN
 
     @Override
     public boolean onKeyEvent(KeyEvent event) {
         // FIXME implement this
         return false;
     }
 
     @Override
+    public void onMotionEventVelocity(final long aEventTime, final float aSpeedY) {
+        handleMotionEventVelocity(aEventTime, aSpeedY);
+    }
+
+    @Override
     public PointF getVelocityVector() {
         // FIXME implement this
         return new PointF(0, 0);
     }
 
     @Override
     public void pageRectUpdated() {
         // no-op in APZC, I think
--- a/mobile/android/base/java/org/mozilla/gecko/gfx/PanZoomController.java
+++ b/mobile/android/base/java/org/mozilla/gecko/gfx/PanZoomController.java
@@ -31,16 +31,17 @@ public interface PanZoomController {
         }
     }
 
     public void destroy();
 
     public boolean onTouchEvent(MotionEvent event);
     public boolean onMotionEvent(MotionEvent event);
     public boolean onKeyEvent(KeyEvent event);
+    public void onMotionEventVelocity(final long aEventTime, final float aSpeedY);
     public void notifyDefaultActionPrevented(boolean prevented);
 
     public boolean getRedrawHint();
     public PointF getVelocityVector();
 
     public void pageRectUpdated();
     public void abortPanning();
     public void abortAnimation();
--- a/widget/android/GeneratedJNINatives.h
+++ b/widget/android/GeneratedJNINatives.h
@@ -324,16 +324,20 @@ public:
         mozilla::jni::MakeNativeMethod<NativePanZoomController::DisposeNative_t>(
                 mozilla::jni::NativeStub<NativePanZoomController::DisposeNative_t, Impl>
                 ::template Wrap<&Impl::DisposeNative>),
 
         mozilla::jni::MakeNativeMethod<NativePanZoomController::HandleMotionEvent_t>(
                 mozilla::jni::NativeStub<NativePanZoomController::HandleMotionEvent_t, Impl>
                 ::template Wrap<&Impl::HandleMotionEvent>),
 
+        mozilla::jni::MakeNativeMethod<NativePanZoomController::HandleMotionEventVelocity_t>(
+                mozilla::jni::NativeStub<NativePanZoomController::HandleMotionEventVelocity_t, Impl>
+                ::template Wrap<&Impl::HandleMotionEventVelocity>),
+
         mozilla::jni::MakeNativeMethod<NativePanZoomController::HandleMouseEvent_t>(
                 mozilla::jni::NativeStub<NativePanZoomController::HandleMouseEvent_t, Impl>
                 ::template Wrap<&Impl::HandleMouseEvent>),
 
         mozilla::jni::MakeNativeMethod<NativePanZoomController::HandleScrollEvent_t>(
                 mozilla::jni::NativeStub<NativePanZoomController::HandleScrollEvent_t, Impl>
                 ::template Wrap<&Impl::HandleScrollEvent>),
 
--- a/widget/android/GeneratedJNIWrappers.cpp
+++ b/widget/android/GeneratedJNIWrappers.cpp
@@ -1524,16 +1524,19 @@ auto NativePanZoomController::Destroy() 
 }
 
 constexpr char NativePanZoomController::DisposeNative_t::name[];
 constexpr char NativePanZoomController::DisposeNative_t::signature[];
 
 constexpr char NativePanZoomController::HandleMotionEvent_t::name[];
 constexpr char NativePanZoomController::HandleMotionEvent_t::signature[];
 
+constexpr char NativePanZoomController::HandleMotionEventVelocity_t::name[];
+constexpr char NativePanZoomController::HandleMotionEventVelocity_t::signature[];
+
 constexpr char NativePanZoomController::HandleMouseEvent_t::name[];
 constexpr char NativePanZoomController::HandleMouseEvent_t::signature[];
 
 constexpr char NativePanZoomController::HandleScrollEvent_t::name[];
 constexpr char NativePanZoomController::HandleScrollEvent_t::signature[];
 
 constexpr char NativePanZoomController::AbortAnimation_t::name[];
 constexpr char NativePanZoomController::AbortAnimation_t::signature[];
--- a/widget/android/GeneratedJNIWrappers.h
+++ b/widget/android/GeneratedJNIWrappers.h
@@ -3658,16 +3658,31 @@ public:
         static constexpr char name[] = "handleMotionEvent";
         static constexpr char signature[] =
                 "(IIJI[I[F[F[F[F[F[F)Z";
         static const bool isStatic = false;
         static const mozilla::jni::ExceptionMode exceptionMode =
                 mozilla::jni::ExceptionMode::ABORT;
     };
 
+    struct HandleMotionEventVelocity_t {
+        typedef NativePanZoomController Owner;
+        typedef void ReturnType;
+        typedef void SetterType;
+        typedef mozilla::jni::Args<
+                int64_t,
+                float> Args;
+        static constexpr char name[] = "handleMotionEventVelocity";
+        static constexpr char signature[] =
+                "(JF)V";
+        static const bool isStatic = false;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+    };
+
     struct HandleMouseEvent_t {
         typedef NativePanZoomController Owner;
         typedef bool ReturnType;
         typedef bool SetterType;
         typedef mozilla::jni::Args<
                 int32_t,
                 int64_t,
                 int32_t,
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -802,16 +802,24 @@ public:
             WidgetTouchEvent touchEvent = input.ToWidgetTouchEvent(window);
             window->ProcessUntransformedAPZEvent(&touchEvent, guid,
                                                  blockId, status);
             window->DispatchHitTest(touchEvent);
         });
         return true;
     }
 
+    void HandleMotionEventVelocity(int64_t aTime, float aSpeedY)
+    {
+        RefPtr<APZCTreeManager> controller = mWindow->mAPZC;
+        if (controller) {
+            controller->ProcessTouchVelocity((uint32_t)aTime, aSpeedY);
+        }
+    }
+
     void UpdateOverscrollVelocity(const float x, const float y)
     {
         mNPZC->UpdateOverscrollVelocity(x, y);
     }
 
     void UpdateOverscrollOffset(const float x, const float y)
     {
         mNPZC->UpdateOverscrollOffset(x, y);