Bug 1405359 - Avoid pushing and popping identical clip stacks for adjacent display items. r=jrmuizel
☠☠ backed out by 79d7006caad1 ☠ ☠
authorKartikaya Gupta <kgupta@mozilla.com>
Tue, 24 Oct 2017 16:15:00 -0400
changeset 685831 988d6a397ea8eb440e7fd521f818202124de8417
parent 685830 154c415885b818ca3de8f21c44639994bddeab5d
child 685832 1593dfc4cf04af2d32060262a5a0e77ff5b2688d
push id86016
push userkgupta@mozilla.com
push dateWed, 25 Oct 2017 01:53:44 +0000
reviewersjrmuizel
bugs1405359
milestone58.0a1
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: GadIp2J8TrA
gfx/layers/wr/ScrollingLayersHelper.cpp
gfx/layers/wr/ScrollingLayersHelper.h
gfx/layers/wr/WebRenderCommandBuilder.cpp
--- 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
@@ -27,19 +27,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,
@@ -70,22 +72,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,