Bug 1405359 - Avoid pushing and popping identical clip stacks for adjacent display items. r?jrmuizel
Instead of unconditionally pushing and popping clips per display item,
this patch changes things so that for each recursive display list, we
create an ItemClips struct. We push this onto the stack when we enter
the display list, and pop it off at the end. For each display item, we
check to see if the clips would actually change compared to the previous
display item, and only do the pop/repush in that case.
MozReview-Commit-ID: J0MCc2V9hWT
--- a/gfx/layers/wr/ScrollingLayersHelper.cpp
+++ b/gfx/layers/wr/ScrollingLayersHelper.cpp
@@ -33,19 +33,44 @@ void
ScrollingLayersHelper::EndBuild()
{
mBuilder = nullptr;
mCache.clear();
MOZ_ASSERT(mItemClipStack.empty());
}
void
+ScrollingLayersHelper::BeginList()
+{
+ mItemClipStack.emplace_back(nullptr, nullptr);
+}
+
+void
+ScrollingLayersHelper::EndList()
+{
+ MOZ_ASSERT(!mItemClipStack.empty());
+ mItemClipStack.back().Unapply(mBuilder);
+ mItemClipStack.pop_back();
+}
+
+void
ScrollingLayersHelper::BeginItem(nsDisplayItem* aItem,
const StackingContextHelper& aStackingContext)
{
+ ItemClips clips(aItem->GetActiveScrolledRoot(), aItem->GetClipChain());
+ MOZ_ASSERT(!mItemClipStack.empty());
+ if (clips.HasSameInputs(mItemClipStack.back())) {
+ // Early-exit because if the clips are the same then we don't need to do
+ // do the work of popping the old stuff and then pushing it right back on
+ // for the new item.
+ return;
+ }
+ mItemClipStack.back().Unapply(mBuilder);
+ mItemClipStack.pop_back();
+
int32_t auPerDevPixel = aItem->Frame()->PresContext()->AppUnitsPerDevPixel();
// There are two ASR chains here that we need to be fully defined. One is the
// ASR chain pointed to by aItem->GetActiveScrolledRoot(). The other is the
// ASR chain pointed to by aItem->GetClipChain()->mASR. We pick the leafmost
// of these two chains because that one will include the other.
// The leafmost clip is trivially going to be aItem->GetClipChain().
// So we call DefineClipChain with these two leafmost things, and it will
@@ -73,17 +98,16 @@ ScrollingLayersHelper::BeginItem(nsDispl
FrameMetrics::ViewID scrollId = aItem->GetActiveScrolledRoot()
? nsLayoutUtils::ViewIDForASR(aItem->GetActiveScrolledRoot())
: FrameMetrics::NULL_SCROLL_ID;
// If the leafmost ASR is not the same as the item's ASR then we are dealing
// with a case where the item's clip chain is scrolled by something other than
// the item's ASR. So for those cases we need to use the ClipAndScroll API.
bool needClipAndScroll = (leafmostId != scrollId);
- ItemClips clips;
// If we don't need a ClipAndScroll, ensure the item's ASR is at the top of
// the scroll stack
if (!needClipAndScroll && mBuilder->TopmostScrollId() != scrollId) {
MOZ_ASSERT(leafmostId == scrollId); // because !needClipAndScroll
clips.mScrollId = Some(scrollId);
}
// And ensure the leafmost clip, if scrolled by that ASR, is at the top of the
// stack.
@@ -371,32 +395,30 @@ ScrollingLayersHelper::RecurseAndDefineA
mBuilder->DefineScrollLayer(scrollId, ancestorIds.first, ancestorIds.second,
aSc.ToRelativeLayoutRect(contentRect),
aSc.ToRelativeLayoutRect(clipBounds));
ids.first = Some(scrollId);
return ids;
}
-void
-ScrollingLayersHelper::EndItem(nsDisplayItem* aItem)
-{
- MOZ_ASSERT(!mItemClipStack.empty());
- ItemClips& clips = mItemClipStack.back();
- clips.Unapply(mBuilder);
- mItemClipStack.pop_back();
-}
-
ScrollingLayersHelper::~ScrollingLayersHelper()
{
MOZ_ASSERT(!mBuilder);
MOZ_ASSERT(mCache.empty());
MOZ_ASSERT(mItemClipStack.empty());
}
+ScrollingLayersHelper::ItemClips::ItemClips(const ActiveScrolledRoot* aAsr,
+ const DisplayItemClipChain* aChain)
+ : mAsr(aAsr)
+ , mChain(aChain)
+{
+}
+
void
ScrollingLayersHelper::ItemClips::Apply(wr::DisplayListBuilder* aBuilder)
{
if (mScrollId) {
aBuilder->PushScrollLayer(mScrollId.ref());
}
if (mClipId) {
aBuilder->PushClip(mClipId.ref());
@@ -416,10 +438,17 @@ ScrollingLayersHelper::ItemClips::Unappl
if (mClipId) {
aBuilder->PopClip();
}
if (mScrollId) {
aBuilder->PopScrollLayer();
}
}
+bool
+ScrollingLayersHelper::ItemClips::HasSameInputs(const ItemClips& aOther)
+{
+ return mAsr == aOther.mAsr &&
+ mChain == aOther.mChain;
+}
+
} // namespace layers
} // namespace mozilla
--- a/gfx/layers/wr/ScrollingLayersHelper.h
+++ b/gfx/layers/wr/ScrollingLayersHelper.h
@@ -29,19 +29,21 @@ class StackingContextHelper;
class ScrollingLayersHelper
{
public:
ScrollingLayersHelper();
void BeginBuild(wr::DisplayListBuilder& aBuilder);
void EndBuild();
+ void BeginList();
+ void EndList();
+
void BeginItem(nsDisplayItem* aItem,
const StackingContextHelper& aStackingContext);
- void EndItem(nsDisplayItem* aItem);
~ScrollingLayersHelper();
private:
std::pair<Maybe<FrameMetrics::ViewID>, Maybe<wr::WrClipId>>
DefineClipChain(nsDisplayItem* aItem,
const ActiveScrolledRoot* aAsr,
const DisplayItemClipChain* aChain,
int32_t aAppUnitsPerDevPixel,
@@ -72,22 +74,29 @@ private:
// end up creating multiple clips in WR that are effectively identical but
// have separate clip ids. Hopefully this won't happen very often.
typedef std::unordered_map<const DisplayItemClipChain*, wr::WrClipId> ClipIdMap;
wr::DisplayListBuilder* mBuilder;
ClipIdMap mCache;
struct ItemClips {
+ ItemClips(const ActiveScrolledRoot* aAsr,
+ const DisplayItemClipChain* aChain);
+
+ const ActiveScrolledRoot* mAsr;
+ const DisplayItemClipChain* mChain;
+
Maybe<FrameMetrics::ViewID> mScrollId;
Maybe<wr::WrClipId> mClipId;
Maybe<std::pair<FrameMetrics::ViewID, Maybe<wr::WrClipId>>> mClipAndScroll;
void Apply(wr::DisplayListBuilder* aBuilder);
void Unapply(wr::DisplayListBuilder* aBuilder);
+ bool HasSameInputs(const ItemClips& aOther);
};
std::vector<ItemClips> mItemClipStack;
};
} // namespace layers
} // namespace mozilla
--- a/gfx/layers/wr/WebRenderCommandBuilder.cpp
+++ b/gfx/layers/wr/WebRenderCommandBuilder.cpp
@@ -107,16 +107,18 @@ WebRenderCommandBuilder::BuildWebRenderC
void
WebRenderCommandBuilder::CreateWebRenderCommandsFromDisplayList(nsDisplayList* aDisplayList,
nsDisplayListBuilder* aDisplayListBuilder,
const StackingContextHelper& aSc,
wr::DisplayListBuilder& aBuilder,
wr::IpcResourceUpdateQueue& aResources)
{
+ mScrollingHelper.BeginList();
+
bool apzEnabled = mManager->AsyncPanZoomEnabled();
EventRegions eventRegions;
FlattenedDisplayItemIterator iter(aDisplayListBuilder, aDisplayList);
while (nsDisplayItem* i = iter.GetNext()) {
nsDisplayItem* item = i;
DisplayItemType itemType = item->GetType();
@@ -216,17 +218,16 @@ WebRenderCommandBuilder::CreateWebRender
mScrollingHelper.BeginItem(item, aSc);
// Note: this call to CreateWebRenderCommands can recurse back into
// this function if the |item| is a wrapper for a sublist.
if (!item->CreateWebRenderCommands(aBuilder, aResources, aSc, mManager,
aDisplayListBuilder)) {
PushItemAsImage(item, aBuilder, aResources, aSc, aDisplayListBuilder);
}
- mScrollingHelper.EndItem(item);
if (apzEnabled) {
if (forceNewLayerData) {
// Pop the thing we pushed before the recursion, so the topmost item on
// the stack is enclosing display item's ASR (or the stack is empty)
mAsrStack.pop_back();
const ActiveScrolledRoot* stopAtAsr =
mAsrStack.empty() ? nullptr : mAsrStack.back();
@@ -254,16 +255,18 @@ WebRenderCommandBuilder::CreateWebRender
// return. Again, at this point the layer data list must be non-empty, and
// the most recently created layer data will have been created by an item
// with matching ASRs.
if (!eventRegions.IsEmpty()) {
MOZ_ASSERT(apzEnabled);
MOZ_ASSERT(!mLayerScrollData.empty());
mLayerScrollData.back().AddEventRegions(eventRegions);
}
+
+ mScrollingHelper.EndList();
}
Maybe<wr::ImageKey>
WebRenderCommandBuilder::CreateImageKey(nsDisplayItem* aItem,
ImageContainer* aContainer,
mozilla::wr::DisplayListBuilder& aBuilder,
mozilla::wr::IpcResourceUpdateQueue& aResources,
const StackingContextHelper& aSc,