Bug 1257641 - Use empty transactions to carry scroll offset updates to APZ that don't require a repaint. r?mattwoodrow,mstange,botond
MozReview-Commit-ID: 7XgQMUleR2j
--- a/gfx/layers/FrameMetrics.h
+++ b/gfx/layers/FrameMetrics.h
@@ -22,16 +22,25 @@
namespace IPC {
template <typename T> struct ParamTraits;
} // namespace IPC
namespace mozilla {
namespace layers {
/**
+ * Helper struct to hold a couple of fields that can be updated as part of
+ * an empty transaction.
+ */
+struct ScrollUpdateInfo {
+ uint32_t mScrollGeneration;
+ CSSPoint mScrollOffset;
+};
+
+/**
* The viewport and displayport metrics for the painted frame at the
* time of a layer-tree transaction. These metrics are especially
* useful for shadow layers, because the metrics values are updated
* atomically with new pixels.
*/
struct FrameMetrics {
friend struct IPC::ParamTraits<mozilla::layers::FrameMetrics>;
public:
@@ -39,16 +48,19 @@ public:
typedef uint64_t ViewID;
static const ViewID NULL_SCROLL_ID; // This container layer does not scroll.
static const ViewID START_SCROLL_ID = 2; // This is the ID that scrolling subframes
// will begin at.
enum ScrollOffsetUpdateType : uint8_t {
eNone, // The default; the scroll offset was not updated
eMainThread, // The scroll offset was updated by the main thread.
+ ePending, // The scroll offset was updated on the main thread, but not
+ // painted, so the layer texture data is still at the old
+ // offset.
eSentinel // For IPC use only
};
FrameMetrics()
: mScrollId(NULL_SCROLL_ID)
, mScrollParentId(NULL_SCROLL_ID)
, mPresShellResolution(1)
@@ -236,16 +248,23 @@ public:
void CopySmoothScrollInfoFrom(const FrameMetrics& aOther)
{
mSmoothScrollOffset = aOther.mSmoothScrollOffset;
mScrollGeneration = aOther.mScrollGeneration;
mDoSmoothScroll = aOther.mDoSmoothScroll;
}
+ void UpdatePendingScrollInfo(const ScrollUpdateInfo& aInfo)
+ {
+ mScrollOffset = aInfo.mScrollOffset;
+ mScrollGeneration = aInfo.mScrollGeneration;
+ mScrollUpdateType = ePending;
+ }
+
void UpdateScrollInfo(uint32_t aScrollGeneration, const CSSPoint& aScrollOffset)
{
mScrollOffset = aScrollOffset;
mScrollGeneration = aScrollGeneration;
}
// Make a copy of this FrameMetrics object which does not have any pointers
// to heap-allocated memory (i.e. is Plain Old Data, or 'POD'), and is
@@ -375,16 +394,21 @@ public:
}
void SetSmoothScrollOffsetUpdated(int32_t aScrollGeneration)
{
mDoSmoothScroll = true;
mScrollGeneration = aScrollGeneration;
}
+ ScrollOffsetUpdateType GetScrollUpdateType() const
+ {
+ return mScrollUpdateType;
+ }
+
bool GetScrollOffsetUpdated() const
{
return mScrollUpdateType != eNone;
}
bool GetDoSmoothScroll() const
{
return mDoSmoothScroll;
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -562,16 +562,21 @@ Layer::ScrollMetadataChanged()
void
Layer::ApplyPendingUpdatesToSubtree()
{
ApplyPendingUpdatesForThisTransaction();
for (Layer* child = GetFirstChild(); child; child = child->GetNextSibling()) {
child->ApplyPendingUpdatesToSubtree();
}
+ if (!GetParent()) {
+ // Once we're done recursing through the whole tree, clear the pending
+ // updates from the manager.
+ Manager()->ClearPendingScrollInfoUpdate();
+ }
}
bool
Layer::IsOpaqueForVisibility()
{
return GetLocalOpacity() == 1.0f &&
GetEffectiveMixBlendMode() == CompositionOp::OP_OVER;
}
@@ -941,16 +946,25 @@ Layer::ApplyPendingUpdatesForThisTransac
mPendingTransform = nullptr;
if (mPendingAnimations) {
MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) PendingUpdatesForThisTransaction", this));
mPendingAnimations->SwapElements(mAnimations);
mPendingAnimations = nullptr;
Mutated();
}
+
+ for (size_t i = 0; i < mScrollMetadata.Length(); i++) {
+ FrameMetrics& fm = mScrollMetadata[i].GetMetrics();
+ Maybe<ScrollUpdateInfo> update = Manager()->GetPendingScrollInfoUpdate(fm.GetScrollId());
+ if (update) {
+ fm.UpdatePendingScrollInfo(update.value());
+ Mutated();
+ }
+ }
}
float
Layer::GetLocalOpacity()
{
float opacity = mOpacity;
if (LayerComposite* shadow = AsLayerComposite())
opacity = shadow->GetShadowOpacity();
@@ -2442,16 +2456,39 @@ LayerManager::DumpPacket(layerscope::Lay
/*static*/ bool
LayerManager::IsLogEnabled()
{
return MOZ_LOG_TEST(GetLog(), LogLevel::Debug);
}
void
+LayerManager::SetPendingScrollUpdateForNextTransaction(FrameMetrics::ViewID aScrollId,
+ const ScrollUpdateInfo& aUpdateInfo)
+{
+ mPendingScrollUpdates[aScrollId] = aUpdateInfo;
+}
+
+Maybe<ScrollUpdateInfo>
+LayerManager::GetPendingScrollInfoUpdate(FrameMetrics::ViewID aScrollId)
+{
+ auto it = mPendingScrollUpdates.find(aScrollId);
+ if (it != mPendingScrollUpdates.end()) {
+ return Some(it->second);
+ }
+ return Nothing();
+}
+
+void
+LayerManager::ClearPendingScrollInfoUpdate()
+{
+ mPendingScrollUpdates.clear();
+}
+
+void
PrintInfo(std::stringstream& aStream, LayerComposite* aLayerComposite)
{
if (!aLayerComposite) {
return;
}
if (const Maybe<ParentLayerIntRect>& clipRect = aLayerComposite->GetShadowClipRect()) {
AppendToString(aStream, *clipRect, " [shadow-clip=", "]");
}
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -1,16 +1,17 @@
/* -*- 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 GFX_LAYERS_H
#define GFX_LAYERS_H
+#include <map>
#include <stdint.h> // for uint32_t, uint64_t, uint8_t
#include <stdio.h> // for FILE
#include <sys/types.h> // for int32_t, int64_t
#include "FrameMetrics.h" // for FrameMetrics
#include "Units.h" // for LayerMargin, LayerPoint, ParentLayerIntRect
#include "gfxContext.h"
#include "gfxTypes.h"
#include "gfxPoint.h" // for gfxPoint
@@ -693,16 +694,29 @@ private:
uint32_t mNextIndex;
uint32_t mLatestStartIndex;
uint32_t mCurrentRunStartIndex;
bool mIsPaused;
};
FramesTimingRecording mRecording;
TimeStamp mTabSwitchStart;
+
+public:
+ /*
+ * Methods to store/get/clear a "pending scroll info update" object on a
+ * per-scrollid basis. This is used for empty transactions that push over
+ * scroll position updates to the APZ code.
+ */
+ void SetPendingScrollUpdateForNextTransaction(FrameMetrics::ViewID aScrollId,
+ const ScrollUpdateInfo& aUpdateInfo);
+ Maybe<ScrollUpdateInfo> GetPendingScrollInfoUpdate(FrameMetrics::ViewID aScrollId);
+ void ClearPendingScrollInfoUpdate();
+private:
+ std::map<FrameMetrics::ViewID,ScrollUpdateInfo> mPendingScrollUpdates;
};
typedef InfallibleTArray<Animation> AnimationArray;
struct AnimData {
InfallibleTArray<mozilla::StyleAnimationValue> mStartValues;
InfallibleTArray<mozilla::StyleAnimationValue> mEndValues;
InfallibleTArray<Maybe<mozilla::ComputedTimingFunction>> mFunctions;
@@ -852,16 +866,17 @@ public:
* Set the (sub)document metrics used to render the Layer subtree
* rooted at this. Note that a layer may have multiple FrameMetrics
* objects; calling this function will remove all of them and replace
* them with the provided FrameMetrics. See the documentation for
* SetFrameMetrics(const nsTArray<FrameMetrics>&) for more details.
*/
void SetScrollMetadata(const ScrollMetadata& aScrollMetadata)
{
+ Manager()->ClearPendingScrollInfoUpdate();
if (mScrollMetadata.Length() != 1 || mScrollMetadata[0] != aScrollMetadata) {
MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) FrameMetrics", this));
mScrollMetadata.ReplaceElementsAt(0, mScrollMetadata.Length(), aScrollMetadata);
ScrollMetadataChanged();
Mutated();
}
}
@@ -879,16 +894,17 @@ public:
* Note also that there is actually a many-to-many relationship between
* Layers and ScrollMetadata, because multiple Layers may have identical
* ScrollMetadata objects. This happens when those layers belong to the
* same scrolling subdocument and therefore end up with the same async
* transform when they are scrolled by the APZ code.
*/
void SetScrollMetadata(const nsTArray<ScrollMetadata>& aMetadataArray)
{
+ Manager()->ClearPendingScrollInfoUpdate();
if (mScrollMetadata != aMetadataArray) {
MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) FrameMetrics", this));
mScrollMetadata = aMetadataArray;
ScrollMetadataChanged();
Mutated();
}
}
--- a/gfx/layers/LayersLogging.cpp
+++ b/gfx/layers/LayersLogging.cpp
@@ -186,17 +186,17 @@ AppendToString(std::stringstream& aStrea
AppendToString(aStream, m.GetViewport(), "] [v=");
aStream << nsPrintfCString("] [z=(ld=%.3f r=%.3f",
m.GetDevPixelsPerCSSPixel().scale,
m.GetPresShellResolution()).get();
AppendToString(aStream, m.GetCumulativeResolution(), " cr=");
AppendToString(aStream, m.GetZoom(), " z=");
AppendToString(aStream, m.GetExtraResolution(), " er=");
aStream << nsPrintfCString(")] [u=(%d %d %lu)",
- m.GetScrollOffsetUpdated(), m.GetDoSmoothScroll(),
+ m.GetScrollUpdateType(), m.GetDoSmoothScroll(),
m.GetScrollGeneration()).get();
AppendToString(aStream, m.GetScrollParentId(), "] [p=");
aStream << nsPrintfCString("] [i=(%ld %lld %d)] }",
m.GetPresShellId(), m.GetScrollId(), m.IsRootContent()).get();
}
aStream << sfx;
}
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -3349,17 +3349,19 @@ void AsyncPanZoomController::NotifyLayer
// No new information here, skip it. Note that this is not just an
// optimization; it's correctness too. In the case where we get one of these
// stale aLayerMetrics *after* a call to NotifyScrollUpdated, processing the
// stale aLayerMetrics would clobber the more up-to-date information from
// NotifyScrollUpdated.
APZC_LOG("%p NotifyLayersUpdated short-circuit\n", this);
return;
}
- mLastContentPaintMetrics = aLayerMetrics;
+ if (aLayerMetrics.GetScrollUpdateType() != FrameMetrics::ScrollOffsetUpdateType::ePending) {
+ mLastContentPaintMetrics = aLayerMetrics;
+ }
mFrameMetrics.SetScrollParentId(aLayerMetrics.GetScrollParentId());
APZC_LOG_FM(aLayerMetrics, "%p got a NotifyLayersUpdated with aIsFirstPaint=%d, aThisLayerTreeUpdated=%d",
this, aIsFirstPaint, aThisLayerTreeUpdated);
{ // scope lock
MutexAutoLock lock(mCheckerboardEventLock);
if (mCheckerboardEvent && mCheckerboardEvent->IsRecordingTrace()) {
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -61,17 +61,16 @@
#include "AsyncScrollBase.h"
#include "ScrollSnap.h"
#include "UnitTransforms.h"
#include "nsPluginFrame.h"
#include <mozilla/layers/AxisPhysicsModel.h>
#include <mozilla/layers/AxisPhysicsMSDModel.h>
#include "mozilla/layers/LayerTransactionChild.h"
#include "mozilla/layers/ScrollLinkedEffectDetector.h"
-#include "mozilla/layers/ShadowLayers.h"
#include "mozilla/unused.h"
#include "LayersLogging.h" // for Stringify
#include <algorithm>
#include <cstdlib> // for std::abs(int/long)
#include <cmath> // for std::abs(float/double)
#define PAINT_SKIP_LOG(...)
// #define PAINT_SKIP_LOG(...) printf_stderr("PSKIP: " __VA_ARGS__)
@@ -2747,24 +2746,28 @@ ScrollFrameHelper::ScrollToImpl(nsPoint
if (usingDisplayPort && displayPort.IsEqualEdges(oldDisplayPort)) {
if (LastScrollOrigin() == nsGkAtoms::apz) {
schedulePaint = false;
PAINT_SKIP_LOG("Skipping due to APZ scroll\n");
} else if (mScrollableByAPZ && !HasPluginFrames() &&
!content->GetComposedDoc()->HasScrollLinkedEffect()) {
nsIWidget* widget = presContext->GetNearestWidget();
LayerManager* manager = widget ? widget->GetLayerManager() : nullptr;
- ShadowLayerForwarder* forwarder = manager ? manager->AsShadowForwarder() : nullptr;
- if (forwarder && forwarder->HasShadowManager()) {
+ if (manager) {
mozilla::layers::FrameMetrics::ViewID id;
DebugOnly<bool> success = nsLayoutUtils::FindIDFor(content, &id);
MOZ_ASSERT(success); // we have a displayport, we better have an ID
- forwarder->GetShadowManager()->SendUpdateScrollOffset(id,
- mScrollGeneration, CSSPoint::FromAppUnits(GetScrollPosition()));
+
+ // Schedule an empty transaction to carry over the scroll offset update,
+ // instead of a full transaction. This empty transaction might still get
+ // squashed into a full transaction if something happens to trigger one.
schedulePaint = false;
+ manager->SetPendingScrollUpdateForNextTransaction(id,
+ { mScrollGeneration, CSSPoint::FromAppUnits(GetScrollPosition()) });
+ mOuter->SchedulePaint(nsIFrame::PAINT_COMPOSITE_ONLY);
PAINT_SKIP_LOG("Skipping due to APZ-forwarded main-thread scroll\n");
}
}
}
}
if (schedulePaint) {
mOuter->SchedulePaint();