Bug 1405359 - Avoid pushing and popping identical clip stacks for adjacent display items. r=jrmuizel
authorKartikaya Gupta <kgupta@mozilla.com>
Tue, 24 Oct 2017 18:47:17 -0400
changeset 388057 d3d823602aa14b79688f969fca7f1b5514b479b8
parent 388056 2cbf78901a3d029fc683e1ec6d00065820a902b7
child 388058 edc52ff233c42294140b0e22381f7f37cd30c345
push id32739
push useracraciun@mozilla.com
push dateWed, 25 Oct 2017 09:29:21 +0000
treeherdermozilla-central@252a8528c5ab [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs1405359
milestone58.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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,