Bug 1405359 - Avoid pushing and popping identical clip stacks for adjacent display items. r?jrmuizel draft
authorKartikaya Gupta <kgupta@mozilla.com>
Tue, 24 Oct 2017 18:47:17 -0400
changeset 685849 ee406b85224d8d5bbc19a8e00edf14e76e90bf77
parent 685848 afacbf2bff2d3160ee38a9b3769c485953d297ba
child 685850 3c0c540cb1be66a21feb6445a84000aa8118e954
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: J0MCc2V9hWT
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
@@ -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,