Bug 1242609 - Use PeekMessages to get the most recent DisplayPort request. r?kats
MozReview-Commit-ID: K5PTxUqU9E6
--- a/gfx/layers/apz/util/APZCCallbackHelper.cpp
+++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp
@@ -33,19 +33,20 @@
namespace mozilla {
namespace layers {
using dom::TabParent;
uint64_t APZCCallbackHelper::sLastTargetAPZCNotificationInputBlock = uint64_t(-1);
-static void
-AdjustDisplayPortForScrollDelta(mozilla::layers::FrameMetrics& aFrameMetrics,
- const CSSPoint& aActualScrollOffset)
+void
+APZCCallbackHelper::AdjustDisplayPortForScrollDelta(
+ mozilla::layers::FrameMetrics& aFrameMetrics,
+ const CSSPoint& aActualScrollOffset)
{
// Correct the display-port by the difference between the requested scroll
// offset and the resulting scroll offset after setting the requested value.
ScreenPoint shift =
(aFrameMetrics.GetScrollOffset() - aActualScrollOffset) *
aFrameMetrics.DisplayportPixelsPerCSSPixel();
ScreenMargin margins = aFrameMetrics.GetDisplayPortMargins();
margins.left -= shift.x;
@@ -90,19 +91,17 @@ ScrollFrameTo(nsIScrollableFrame* aFrame
targetScrollPosition.x = geckoScrollPosition.x;
}
// If the scrollable frame is currently in the middle of an async or smooth
// scroll then we don't want to interrupt it (see bug 961280).
// Also if the scrollable frame got a scroll request from a higher priority origin
// since the last layers update, then we don't want to push our scroll request
// because we'll clobber that one, which is bad.
- bool scrollInProgress = aFrame->IsProcessingAsyncScroll()
- || nsLayoutUtils::CanScrollOriginClobberApz(aFrame->LastScrollOrigin())
- || aFrame->LastSmoothScrollOrigin();
+ bool scrollInProgress = APZCCallbackHelper::IsScrollInProgress(aFrame);
if (!scrollInProgress) {
aFrame->ScrollToCSSPixelsApproximate(targetScrollPosition, nsGkAtoms::apz);
geckoScrollPosition = CSSPoint::FromAppUnits(aFrame->GetScrollPosition());
aSuccessOut = true;
}
// Return the final scroll position after setting it so that anything that relies
// on it can have an accurate value. Note that even if we set it above re-querying it
// is a good idea because it may have gotten clamped or rounded.
@@ -140,17 +139,17 @@ ScrollFrame(nsIContent* aContent,
// layer is a scroll info layer, we leave the margins as-is and they will
// be interpreted as relative to the content scroll offset.
if (nsIFrame* frame = aContent->GetPrimaryFrame()) {
frame->SchedulePaint();
}
} else {
// Correct the display port due to the difference between mScrollOffset and the
// actual scroll offset.
- AdjustDisplayPortForScrollDelta(aMetrics, actualScrollOffset);
+ APZCCallbackHelper::AdjustDisplayPortForScrollDelta(aMetrics, actualScrollOffset);
}
} else {
// For whatever reason we couldn't update the scroll offset on the scroll frame,
// which means the data APZ used for its displayport calculation is stale. Fall
// back to a sane default behaviour. Note that we don't tile-align the recentered
// displayport because tile-alignment depends on the scroll position, and the
// scroll position here is out of our control. See bug 966507 comment 21 for a
// more detailed explanation.
@@ -939,11 +938,19 @@ APZCCallbackHelper::SuppressDisplayport(
}
bool
APZCCallbackHelper::IsDisplayportSuppressed()
{
return sActiveSuppressDisplayport > 0;
}
+/* static */ bool
+APZCCallbackHelper::IsScrollInProgress(nsIScrollableFrame* aFrame)
+{
+ return aFrame->IsProcessingAsyncScroll()
+ || nsLayoutUtils::CanScrollOriginClobberApz(aFrame->LastScrollOrigin())
+ || aFrame->LastSmoothScrollOrigin();
+}
+
} // namespace layers
} // namespace mozilla
--- a/gfx/layers/apz/util/APZCCallbackHelper.h
+++ b/gfx/layers/apz/util/APZCCallbackHelper.h
@@ -10,16 +10,17 @@
#include "mozilla/EventForwards.h"
#include "mozilla/Function.h"
#include "mozilla/layers/APZUtils.h"
#include "nsIDOMWindowUtils.h"
class nsIContent;
class nsIDocument;
class nsIPresShell;
+class nsIScrollableFrame;
class nsIWidget;
template<class T> struct already_AddRefed;
template<class T> class nsCOMPtr;
namespace mozilla {
namespace layers {
typedef function<void(uint64_t, const nsTArray<TouchBehaviorFlags>&)>
@@ -168,16 +169,27 @@ public:
* use it to trigger a repaint once suppression is disabled. Without that
* the displayport may get left at the suppressed size for an extended
* period of time and result in unnecessary checkerboarding (see bug
* 1255054). */
static void SuppressDisplayport(const bool& aEnabled,
const nsCOMPtr<nsIPresShell>& aShell);
static bool IsDisplayportSuppressed();
+ static void
+ AdjustDisplayPortForScrollDelta(mozilla::layers::FrameMetrics& aFrameMetrics,
+ const CSSPoint& aActualScrollOffset);
+
+ /*
+ * Check if the scrollable frame is currently in the middle of an async
+ * or smooth scroll. We want to discard certain scroll input if this is
+ * true to prevent clobbering higher priority origins.
+ */
+ static bool
+ IsScrollInProgress(nsIScrollableFrame* aFrame);
private:
static uint64_t sLastTargetAPZCNotificationInputBlock;
};
} // namespace layers
} // namespace mozilla
#endif /* mozilla_layers_APZCCallbackHelper_h */
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -170,16 +170,17 @@ private:
DECL_GFX_PREF(Live, "apz.overscroll.enabled", APZOverscrollEnabled, bool, false);
DECL_GFX_PREF(Live, "apz.overscroll.min_pan_distance_ratio", APZMinPanDistanceRatio, float, 1.0f);
DECL_GFX_PREF(Live, "apz.overscroll.spring_friction", APZOverscrollSpringFriction, float, 0.015f);
DECL_GFX_PREF(Live, "apz.overscroll.spring_stiffness", APZOverscrollSpringStiffness, float, 0.001f);
DECL_GFX_PREF(Live, "apz.overscroll.stop_distance_threshold", APZOverscrollStopDistanceThreshold, float, 5.0f);
DECL_GFX_PREF(Live, "apz.overscroll.stop_velocity_threshold", APZOverscrollStopVelocityThreshold, float, 0.01f);
DECL_GFX_PREF(Live, "apz.overscroll.stretch_factor", APZOverscrollStretchFactor, float, 0.5f);
DECL_GFX_PREF(Live, "apz.paint_skipping.enabled", APZPaintSkipping, bool, true);
+ DECL_GFX_PREF(Live, "apz.peek_messages.enabled", APZPeekMessages, bool, true);
DECL_GFX_PREF(Live, "apz.printtree", APZPrintTree, bool, false);
DECL_GFX_PREF(Live, "apz.record_checkerboarding", APZRecordCheckerboarding, bool, false);
DECL_GFX_PREF(Live, "apz.test.logging_enabled", APZTestLoggingEnabled, bool, false);
DECL_GFX_PREF(Live, "apz.touch_move_tolerance", APZTouchMoveTolerance, float, 0.0);
DECL_GFX_PREF(Live, "apz.touch_start_tolerance", APZTouchStartTolerance, float, 1.0f/4.5f);
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_highmem_adjust", APZXSkateHighMemAdjust, float, 0.0f);
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -9,20 +9,22 @@
#include "mozilla/ArrayUtils.h"
#include "mozilla/BasicEvents.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/EffectCompositor.h"
#include "mozilla/EffectSet.h"
#include "mozilla/EventDispatcher.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/gfx/PathHelpers.h"
+#include "mozilla/layers/PAPZ.h"
#include "mozilla/Likely.h"
#include "mozilla/Maybe.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/dom/ContentChild.h"
#include "mozilla/unused.h"
-#include "mozilla/MemoryReporting.h"
#include "nsCharTraits.h"
#include "nsFontMetrics.h"
#include "nsPresContext.h"
#include "nsIContent.h"
#include "nsIDOMHTMLDocument.h"
#include "nsIDOMHTMLElement.h"
#include "nsFrameList.h"
#include "nsGkAtoms.h"
@@ -9026,8 +9028,85 @@ nsLayoutUtils::GetBoundingContentRect(co
subFrame->GetRectRelativeToSelf(),
relativeTo));
result = subFrameRect.Intersect(result);
}
}
return result;
}
+
+static already_AddRefed<nsIPresShell>
+GetPresShell(const nsIContent* aContent)
+{
+ nsCOMPtr<nsIPresShell> result;
+ if (nsIDocument* doc = aContent->GetComposedDoc()) {
+ result = doc->GetShell();
+ }
+ return result.forget();
+}
+
+static void UpdateDisplayPortMarginsForPendingMetrics(FrameMetrics& aMetrics) {
+ nsIContent* content = nsLayoutUtils::FindContentFor(aMetrics.GetScrollId());
+ if (!content) {
+ return;
+ }
+
+ nsCOMPtr<nsIPresShell> shell = GetPresShell(content);
+ if (!shell) {
+ return;
+ }
+
+ MOZ_ASSERT(aMetrics.GetUseDisplayPortMargins());
+
+ if (gfxPrefs::APZAllowZooming() && aMetrics.IsRootContent()) {
+ // See APZCCallbackHelper::UpdateRootFrame for details.
+ float presShellResolution = shell->GetResolution();
+ if (presShellResolution != aMetrics.GetPresShellResolution()) {
+ return;
+ }
+ }
+
+ nsIScrollableFrame* frame = nsLayoutUtils::FindScrollableFrameFor(aMetrics.GetScrollId());
+
+ if (!frame) {
+ return;
+ }
+
+ if (APZCCallbackHelper::IsScrollInProgress(frame)) {
+ // If these conditions are true, then the UpdateFrame
+ // message may be ignored by the main-thread, so we
+ // shouldn't update the displayport based on it.
+ return;
+ }
+
+ DisplayPortMarginsPropertyData* currentData =
+ static_cast<DisplayPortMarginsPropertyData*>(content->GetProperty(nsGkAtoms::DisplayPortMargins));
+ if (!currentData || currentData->mPriority > 0) {
+ return;
+ }
+
+ CSSPoint frameScrollOffset = CSSPoint::FromAppUnits(frame->GetScrollPosition());
+ APZCCallbackHelper::AdjustDisplayPortForScrollDelta(aMetrics, frameScrollOffset);
+
+ content->SetProperty(nsGkAtoms::DisplayPortMargins,
+ new DisplayPortMarginsPropertyData(aMetrics.GetDisplayPortMargins(), 0),
+ nsINode::DeleteProperty<DisplayPortMarginsPropertyData>);
+}
+
+/* static */ void
+nsLayoutUtils::UpdateDisplayPortMarginsFromPendingMessages() {
+ if (mozilla::dom::ContentChild::GetSingleton() &&
+ mozilla::dom::ContentChild::GetSingleton()->GetIPCChannel()) {
+ mozilla::dom::ContentChild::GetSingleton()->GetIPCChannel()->PeekMessages(
+ mozilla::layers::PAPZ::Msg_UpdateFrame__ID,
+ [](const IPC::Message& aMsg) {
+ void* iter = nullptr;
+ FrameMetrics frame;
+ if (!IPC::ReadParam(&aMsg, &iter, &frame)) {
+ MOZ_ASSERT(false);
+ return;
+ }
+
+ UpdateDisplayPortMarginsForPendingMetrics(frame);
+ });
+ }
+}
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -180,16 +180,26 @@ public:
static bool GetDisplayPort(nsIContent* aContent, nsRect *aResult,
RelativeTo aRelativeTo = RelativeTo::ScrollPort);
/**
* Check whether the given element has a displayport.
*/
static bool HasDisplayPort(nsIContent* aContent);
+
+ /**
+ * Go through the IPC Channel and update displayport margins for content
+ * elements based on UpdateFrame messages. The messages are left in the
+ * queue and will be fully processed when dequeued. The aim is to paint
+ * the most up-to-date displayport without waiting for these message to
+ * go through the message queue.
+ */
+ static void UpdateDisplayPortMarginsFromPendingMessages();
+
/**
* @return the display port for the given element which should be used for
* visibility testing purposes.
*
* If low-precision buffers are enabled, this is the critical display port;
* otherwise, it's the same display port returned by GetDisplayPort().
*/
static bool GetDisplayPortForVisibilityTesting(
--- a/layout/base/nsRefreshDriver.cpp
+++ b/layout/base/nsRefreshDriver.cpp
@@ -1673,16 +1673,23 @@ nsRefreshDriver::Tick(int64_t aNowEpoch,
AutoRestore<bool> restoreInRefresh(mInRefresh);
mInRefresh = true;
AutoRestore<TimeStamp> restoreTickStart(mTickStart);
mTickStart = TimeStamp::Now();
gfxPlatform::GetPlatform()->SchedulePaintIfDeviceReset();
+ // We want to process any pending APZ metrics ahead of their positions
+ // in the queue. This will prevent us from spending precious time
+ // painting a stale displayport.
+ if (gfxPrefs::APZPeekMessages()) {
+ nsLayoutUtils::UpdateDisplayPortMarginsFromPendingMessages();
+ }
+
/*
* The timer holds a reference to |this| while calling |Notify|.
* However, implementations of |WillRefresh| are permitted to destroy
* the pres context, which will cause our |mPresContext| to become
* null. If this happens, we must stop notifying observers.
*/
for (uint32_t i = 0; i < ArrayLength(mObservers); ++i) {
ObserverArray::EndLimitedIterator etor(mObservers[i]);
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -588,16 +588,18 @@ pref("apz.minimap.enabled", false);
pref("apz.overscroll.enabled", false);
pref("apz.overscroll.min_pan_distance_ratio", "1.0");
pref("apz.overscroll.spring_friction", "0.015");
pref("apz.overscroll.spring_stiffness", "0.0018");
pref("apz.overscroll.stop_distance_threshold", "5.0");
pref("apz.overscroll.stop_velocity_threshold", "0.01");
pref("apz.overscroll.stretch_factor", "0.35");
pref("apz.paint_skipping.enabled", true);
+// Fetch displayport updates early from the message queue
+pref("apz.peek_messages.enabled", true);
// Whether to print the APZC tree for debugging
pref("apz.printtree", false);
#ifdef NIGHTLY_BUILD
pref("apz.record_checkerboarding", true);
#else
pref("apz.record_checkerboarding", false);