From a01f110229f9d200ecf8c2a3a34a70233ea700d3 Mon Sep 17 00:00:00 2001 From: Botond Ballo Date: Wed, 5 Jul 2017 19:17:14 -0400 Subject: [PATCH] Bug 1375949 - Delay application of async scroll offset by one composite, to give content a chance to remain in sync. r=kats With this in place, scroll-linked effects will remain in sync with async scrolling if they can be processed and painted within the frame budget. This change is currently behind a pref that's off by default. MozReview-Commit-ID: 6GEJTKZh6ON --HG-- extra : rebase_source : 534bf15ef1c5ca26e1dc0d7eb298063b80aa9dd3 --- gfx/layers/apz/src/AsyncPanZoomController.cpp | 64 +++++++++++++++++-- gfx/layers/apz/src/AsyncPanZoomController.h | 25 ++++++++ gfx/thebes/gfxPrefs.h | 1 + modules/libpref/init/all.js | 1 + 4 files changed, 84 insertions(+), 7 deletions(-) diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp index 1424b8836ff4..0db3d5a70235 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.cpp +++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp @@ -273,6 +273,13 @@ typedef GenericFlingAnimation FlingAnimation; * NOTE: Should not be set to anything * other than 0.0 for Android except for tests to disable flings. * + * \li\b apz.frame_delay.enabled + * If this is set to true, changes to the async scroll offset and async zoom + * will not be immediately reflected in GetCurrentAsyncTransform() when called + * with |AsyncTransformConsumer::eForCompositing|. Rather, the transform will + * reflect the value of the async scroll offset and async zoom at the last time + * SampleCompositedAsyncTransform() was called. + * * \li\b apz.max_velocity_inches_per_ms * Maximum velocity. Velocity will be capped at this value if a faster fling * occurs. Negative values indicate unlimited velocity.\n @@ -3218,6 +3225,12 @@ bool AsyncPanZoomController::UpdateAnimation(const TimeStamp& aSampleTime, if (mLastSampleTime == aSampleTime) { return false; } + + // Sample the composited async transform once per composite. Note that we + // call this after the |mLastSampleTime == aSampleTime| check, to ensure + // it's only called once per APZC on each composite. + SampleCompositedAsyncTransform(); + TimeDuration sampleTimeDelta = aSampleTime - mLastSampleTime; mLastSampleTime = aSampleTime; @@ -3319,8 +3332,8 @@ AsyncPanZoomController::GetCurrentAsyncScrollOffset(AsyncTransformConsumer aMode return mLastContentPaintMetrics.GetScrollOffset() * mLastContentPaintMetrics.GetZoom(); } - return (mFrameMetrics.GetScrollOffset() + mTestAsyncScrollOffset) - * mFrameMetrics.GetZoom() * mTestAsyncZoom.scale; + return (GetEffectiveScrollOffset(aMode) + mTestAsyncScrollOffset) + * GetEffectiveZoom(aMode) * mTestAsyncZoom.scale; } CSSPoint @@ -3331,7 +3344,7 @@ AsyncPanZoomController::GetCurrentAsyncScrollOffsetInCssPixels(AsyncTransformCon return mLastContentPaintMetrics.GetScrollOffset(); } - return mFrameMetrics.GetScrollOffset() + mTestAsyncScrollOffset; + return GetEffectiveScrollOffset(aMode) + mTestAsyncScrollOffset; } AsyncTransform @@ -3348,7 +3361,7 @@ AsyncPanZoomController::GetCurrentAsyncTransform(AsyncTransformConsumer aMode) c lastPaintScrollOffset = mLastContentPaintMetrics.GetScrollOffset(); } - CSSPoint currentScrollOffset = mFrameMetrics.GetScrollOffset() + + CSSPoint currentScrollOffset = GetEffectiveScrollOffset(aMode) + mTestAsyncScrollOffset; // If checkerboarding has been disallowed, clamp the scroll position to stay @@ -3369,14 +3382,44 @@ AsyncPanZoomController::GetCurrentAsyncTransform(AsyncTransformConsumer aMode) c } } - ParentLayerPoint translation = (currentScrollOffset - lastPaintScrollOffset) - * mFrameMetrics.GetZoom() * mTestAsyncZoom.scale; + CSSToParentLayerScale2D effectiveZoom = GetEffectiveZoom(aMode); + ParentLayerPoint translation = (currentScrollOffset - lastPaintScrollOffset) + * effectiveZoom * mTestAsyncZoom.scale; + + LayerToParentLayerScale compositedAsyncZoom = + (effectiveZoom / mFrameMetrics.LayersPixelsPerCSSPixel()).ToScaleFactor(); return AsyncTransform( - LayerToParentLayerScale(mFrameMetrics.GetAsyncZoom().scale * mTestAsyncZoom.scale), + LayerToParentLayerScale(compositedAsyncZoom.scale * mTestAsyncZoom.scale), -translation); } +CSSPoint +AsyncPanZoomController::GetEffectiveScrollOffset(AsyncTransformConsumer aMode) const +{ + if (gfxPrefs::APZFrameDelayEnabled() && aMode == eForCompositing) { + return mCompositedScrollOffset; + } + return mFrameMetrics.GetScrollOffset(); +} + +CSSToParentLayerScale2D +AsyncPanZoomController::GetEffectiveZoom(AsyncTransformConsumer aMode) const +{ + if (gfxPrefs::APZFrameDelayEnabled() && aMode == eForCompositing) { + return mCompositedZoom; + } + return mFrameMetrics.GetZoom(); +} + +void +AsyncPanZoomController::SampleCompositedAsyncTransform() +{ + ReentrantMonitorAutoEnter lock(mMonitor); + mCompositedScrollOffset = mFrameMetrics.GetScrollOffset(); + mCompositedZoom = mFrameMetrics.GetZoom(); +} + AsyncTransformComponentMatrix AsyncPanZoomController::GetCurrentAsyncTransformWithOverscroll(AsyncTransformConsumer aMode) const { @@ -3628,6 +3671,9 @@ void AsyncPanZoomController::NotifyLayersUpdated(const ScrollMetadata& aScrollMe mExpectedGeckoMetrics = aLayerMetrics; ShareCompositorFrameMetrics(); + mCompositedScrollOffset = mFrameMetrics.GetScrollOffset(); + mCompositedZoom = mFrameMetrics.GetZoom(); + if (mFrameMetrics.GetDisplayPortMargins() != ScreenMargin()) { // A non-zero display port margin here indicates a displayport has // been set by a previous APZC for the content at this guid. The @@ -3659,11 +3705,14 @@ void AsyncPanZoomController::NotifyLayersUpdated(const ScrollMetadata& aScrollMe needContentRepaint = true; } mFrameMetrics.ZoomBy(totalResolutionChange / presShellResolutionChange); + mCompositedZoom.xScale *= (totalResolutionChange / presShellResolutionChange).width; + mCompositedZoom.yScale *= (totalResolutionChange / presShellResolutionChange).height; } else { // Take the new zoom as either device scale or composition width or // viewport size got changed (e.g. due to orientation change, or content // changing the meta-viewport tag). mFrameMetrics.SetZoom(aLayerMetrics.GetZoom()); + mCompositedZoom = aLayerMetrics.GetZoom(); mFrameMetrics.SetDevPixelsPerCSSPixel(aLayerMetrics.GetDevPixelsPerCSSPixel()); } bool scrollableRectChanged = false; @@ -3702,6 +3751,7 @@ void AsyncPanZoomController::NotifyLayersUpdated(const ScrollMetadata& aScrollMe // correct this we need to update mExpectedGeckoMetrics to be the // last thing we know was painted by Gecko. mFrameMetrics.CopyScrollInfoFrom(aLayerMetrics); + mCompositedScrollOffset = mFrameMetrics.GetScrollOffset(); mExpectedGeckoMetrics = aLayerMetrics; // Cancel the animation (which might also trigger a repaint request) diff --git a/gfx/layers/apz/src/AsyncPanZoomController.h b/gfx/layers/apz/src/AsyncPanZoomController.h index 5e5962940e19..808719f0e388 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.h +++ b/gfx/layers/apz/src/AsyncPanZoomController.h @@ -731,6 +731,11 @@ private: // This allows us to transform events into Gecko's coordinate space. FrameMetrics mExpectedGeckoMetrics; + // These variables cache the scroll offset and zoom stored in |mFrameMetrics| + // the last time SampleCompositedAsyncTransform() was called. + CSSPoint mCompositedScrollOffset; + CSSToParentLayerScale2D mCompositedZoom; + AxisX mX; AxisY mY; @@ -839,6 +844,26 @@ public: */ AsyncTransformComponentMatrix GetCurrentAsyncTransformWithOverscroll(AsyncTransformConsumer aMode) const; +private: + /** + * Samples the composited async transform, making the result of + * |GetCurrentAsyncTransform(eForCompositing)| and similar functions reflect + * the async scroll offset and zoom stored in |mFrameMetrics|. + * + * (This is only relevant when |gfxPrefs::APZFrameDelayEnabled() == true|. + * Otherwise, GetCurrentAsyncTransform() always reflects what's stored in + * |mFrameMetrics| immediately, without any delay.) + */ + void SampleCompositedAsyncTransform(); + + /* + * Helper functions to query the async scroll offset and zoom either + * directly from |mFrameMetrics|, or from cached variables that store + * the scroll offset and zoom from the last time it was sampled by + * calling SampleCompositedAsyncTransform(), depending on who is asking. + */ + CSSPoint GetEffectiveScrollOffset(AsyncTransformConsumer aMode) const; + CSSToParentLayerScale2D GetEffectiveZoom(AsyncTransformConsumer aMode) const; /* =================================================================== * The functions and members in this section are used to manage diff --git a/gfx/thebes/gfxPrefs.h b/gfx/thebes/gfxPrefs.h index e99d61e1b51b..5643a9fb22c7 100644 --- a/gfx/thebes/gfxPrefs.h +++ b/gfx/thebes/gfxPrefs.h @@ -311,6 +311,7 @@ private: DECL_GFX_PREF(Live, "apz.fling_min_velocity_threshold", APZFlingMinVelocityThreshold, float, 0.5f); DECL_GFX_PREF(Live, "apz.fling_stop_on_tap_threshold", APZFlingStopOnTapThreshold, float, 0.05f); DECL_GFX_PREF(Live, "apz.fling_stopped_threshold", APZFlingStoppedThreshold, float, 0.01f); + DECL_GFX_PREF(Live, "apz.frame_delay.enabled", APZFrameDelayEnabled, bool, false); DECL_GFX_PREF(Live, "apz.highlight_checkerboarded_areas", APZHighlightCheckerboardedAreas, bool, false); DECL_GFX_PREF(Once, "apz.keyboard.enabled", APZKeyboardEnabled, bool, false); DECL_GFX_PREF(Live, "apz.max_velocity_inches_per_ms", APZMaxVelocity, float, -1.0f); diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 265e47590e38..2ff68d8fb2f6 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -695,6 +695,7 @@ pref("apz.fling_friction", "0.002"); pref("apz.fling_min_velocity_threshold", "0.5"); pref("apz.fling_stop_on_tap_threshold", "0.05"); pref("apz.fling_stopped_threshold", "0.01"); +pref("apz.frame_delay.enabled", false); pref("apz.highlight_checkerboarded_areas", false); pref("apz.keyboard.enabled", false); pref("apz.max_velocity_inches_per_ms", "-1.0");