Bug 1389138 - Push scroll layers in layers-free WR. r=mstange draft
authorKartikaya Gupta <kgupta@mozilla.com>
Thu, 17 Aug 2017 11:07:03 -0400
changeset 648485 623b116c293e86d063e5acfa4a01f5104719f560
parent 648484 677a92f918481a73877f551b9ac32975e0110be9
child 726835 1cdb772f4d6852d3d0f5680a138ff0c013c52eeb
push id74768
push userkgupta@mozilla.com
push dateThu, 17 Aug 2017 20:14:33 +0000
reviewersmstange
bugs1389138
milestone57.0a1
Bug 1389138 - Push scroll layers in layers-free WR. r=mstange This patch takes the existing code (which allows recursively pushing the necessary part of a clip chain) and folds it into another recursive function that operates on the ASR chain. Now the ASR recursion is the primary one, and clips are pushed for each ASR as needed. This code was partly modelled after the code in ContainerState::SetupScrollingMetadata, which also iterates over the ASRs and clips. MozReview-Commit-ID: 1qaFPY7Ja7
gfx/layers/wr/ScrollingLayersHelper.cpp
gfx/layers/wr/ScrollingLayersHelper.h
--- a/gfx/layers/wr/ScrollingLayersHelper.cpp
+++ b/gfx/layers/wr/ScrollingLayersHelper.cpp
@@ -16,16 +16,17 @@ namespace mozilla {
 namespace layers {
 
 ScrollingLayersHelper::ScrollingLayersHelper(WebRenderLayer* aLayer,
                                              wr::DisplayListBuilder& aBuilder,
                                              const StackingContextHelper& aStackingContext)
   : mLayer(aLayer)
   , mBuilder(&aBuilder)
   , mPushedLayerLocalClip(false)
+  , mPushedClipAndScroll(false)
 {
   if (!mLayer->WrManager()->AsyncPanZoomEnabled()) {
     // If APZ is disabled then we don't need to push the scrolling clips. We
     // still want to push the layer's local clip though.
     PushLayerLocalClip(aStackingContext);
     return;
   }
 
@@ -87,19 +88,106 @@ ScrollingLayersHelper::ScrollingLayersHe
 
 ScrollingLayersHelper::ScrollingLayersHelper(nsDisplayItem* aItem,
                                              wr::DisplayListBuilder& aBuilder,
                                              const StackingContextHelper& aStackingContext,
                                              WebRenderLayerManager::ClipIdMap& aCache)
   : mLayer(nullptr)
   , mBuilder(&aBuilder)
   , mPushedLayerLocalClip(false)
+  , mPushedClipAndScroll(false)
 {
+  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. And then we
+  // call DefineAndPushScrollLayers with it, which will recursively push all
+  // the necessary clips and scroll layer items for that ASR chain.
+  const ActiveScrolledRoot* leafmostASR = aItem->GetActiveScrolledRoot();
+  if (aItem->GetClipChain()) {
+    leafmostASR = ActiveScrolledRoot::PickDescendant(leafmostASR,
+        aItem->GetClipChain()->mASR);
+  }
+  DefineAndPushScrollLayers(aItem, leafmostASR,
+      aItem->GetClipChain(), aBuilder, auPerDevPixel, aStackingContext, aCache);
+
+  // Next, we push the leaf part of the clip chain that is scrolled by the
+  // leafmost ASR. All the clips outside the leafmost ASR were already pushed
+  // in the above call. This call may be a no-op if the item's ASR got picked
+  // as the leaftmostASR previously, because that means these clips were pushed
+  // already as being "outside" leafmostASR.
   DefineAndPushChain(aItem->GetClipChain(), aBuilder, aStackingContext,
-      aItem->Frame()->PresContext()->AppUnitsPerDevPixel(), aCache);
+      auPerDevPixel, aCache);
+
+  // Finally, if clip chain's ASR was the leafmost ASR, then the top of the
+  // scroll id stack right now will point to that, rather than the item's ASR
+  // which is what we want. So we override that by doing a PushClipAndScrollInfo
+  // call. This should generally only happen for fixed-pos type items, but we
+  // use code generic enough to handle other cases.
+  FrameMetrics::ViewID scrollId = aItem->GetActiveScrolledRoot()
+      ? nsLayoutUtils::ViewIDForASR(aItem->GetActiveScrolledRoot())
+      : FrameMetrics::NULL_SCROLL_ID;
+  if (aBuilder.TopmostScrollId() != Some(scrollId)) {
+    Maybe<wr::WrClipId> clipId = mBuilder->TopmostClipId();
+    mBuilder->PushClipAndScrollInfo(scrollId, clipId.ptrOr(nullptr));
+    mPushedClipAndScroll = true;
+  }
+}
+
+void
+ScrollingLayersHelper::DefineAndPushScrollLayers(nsDisplayItem* aItem,
+                                                 const ActiveScrolledRoot* aAsr,
+                                                 const DisplayItemClipChain* aChain,
+                                                 wr::DisplayListBuilder& aBuilder,
+                                                 int32_t aAppUnitsPerDevPixel,
+                                                 const StackingContextHelper& aStackingContext,
+                                                 WebRenderLayerManager::ClipIdMap& aCache)
+{
+  if (!aAsr) {
+    return;
+  }
+  Maybe<ScrollMetadata> metadata = aAsr->mScrollableFrame->ComputeScrollMetadata(
+      nullptr, aItem->ReferenceFrame(), ContainerLayerParameters(), nullptr);
+  MOZ_ASSERT(metadata);
+  FrameMetrics::ViewID scrollId = metadata->GetMetrics().GetScrollId();
+  if (aBuilder.TopmostScrollId() == Some(scrollId)) {
+    // it's already been pushed, so we don't need to recurse any further.
+    return;
+  }
+
+  // Find the first clip up the chain that's "outside" aAsr. Any clips
+  // that are "inside" aAsr (i.e. that are scrolled by aAsr) will need to be
+  // pushed onto the stack after aAsr has been pushed. On the recursive call
+  // we need to skip up the clip chain past these clips.
+  const DisplayItemClipChain* asrClippedBy = aChain;
+  while (asrClippedBy &&
+         ActiveScrolledRoot::PickAncestor(asrClippedBy->mASR, aAsr) == aAsr) {
+    asrClippedBy = asrClippedBy->mParent;
+  }
+
+  // Recurse up the ASR chain to make sure all ancestor scroll layers and their
+  // enclosing clips are defined and pushed onto the WR stack.
+  DefineAndPushScrollLayers(aItem, aAsr->mParent, asrClippedBy, aBuilder,
+      aAppUnitsPerDevPixel, aStackingContext, aCache);
+
+  // Once the ancestors are dealt with, we want to make sure all the clips
+  // enclosing aAsr are pushed. All the clips enclosing aAsr->mParent were
+  // already taken care of in the recursive call, so DefineAndPushChain will
+  // push exactly what we want.
+  DefineAndPushChain(asrClippedBy, aBuilder, aStackingContext,
+      aAppUnitsPerDevPixel, aCache);
+  // Finally, push the ASR itself as a scroll layer. Note that the
+  // implementation of wr_push_scroll_layer in bindings.rs makes sure the
+  // scroll layer doesn't get defined multiple times so we don't need to worry
+  // about that here.
+  if (PushScrollLayer(metadata->GetMetrics(), aStackingContext)) {
+    mPushedClips.push_back(wr::ScrollOrClipId(scrollId));
+  }
 }
 
 void
 ScrollingLayersHelper::DefineAndPushChain(const DisplayItemClipChain* aChain,
                                           wr::DisplayListBuilder& aBuilder,
                                           const StackingContextHelper& aStackingContext,
                                           int32_t aAppUnitsPerDevPixel,
                                           WebRenderLayerManager::ClipIdMap& aCache)
@@ -207,20 +295,27 @@ ScrollingLayersHelper::PushLayerClip(con
   mBuilder->PushClip(mBuilder->DefineClip(
       aSc.ToRelativeLayoutRect(clipRect), nullptr, mask.ptrOr(nullptr)));
 }
 
 ScrollingLayersHelper::~ScrollingLayersHelper()
 {
   if (!mLayer) {
     // For layers-free mode.
+    if (mPushedClipAndScroll) {
+      mBuilder->PopClipAndScrollInfo();
+    }
     while (!mPushedClips.empty()) {
       wr::ScrollOrClipId id = mPushedClips.back();
-      MOZ_ASSERT(id.is<wr::WrClipId>());
-      mBuilder->PopClip();
+      if (id.is<wr::WrClipId>()) {
+        mBuilder->PopClip();
+      } else {
+        MOZ_ASSERT(id.is<FrameMetrics::ViewID>());
+        mBuilder->PopScrollLayer();
+      }
       mPushedClips.pop_back();
     }
     return;
   }
 
   Layer* layer = mLayer->GetLayer();
   if (!mLayer->WrManager()->AsyncPanZoomEnabled()) {
     if (mPushedLayerLocalClip) {
--- a/gfx/layers/wr/ScrollingLayersHelper.h
+++ b/gfx/layers/wr/ScrollingLayersHelper.h
@@ -32,29 +32,37 @@ public:
                         const StackingContextHelper& aSc);
   ScrollingLayersHelper(nsDisplayItem* aItem,
                         wr::DisplayListBuilder& aBuilder,
                         const StackingContextHelper& aStackingContext,
                         WebRenderLayerManager::ClipIdMap& aCache);
   ~ScrollingLayersHelper();
 
 private:
+  void DefineAndPushScrollLayers(nsDisplayItem* aItem,
+                                 const ActiveScrolledRoot* aAsr,
+                                 const DisplayItemClipChain* aChain,
+                                 wr::DisplayListBuilder& aBuilder,
+                                 int32_t aAppUnitsPerDevPixel,
+                                 const StackingContextHelper& aStackingContext,
+                                 WebRenderLayerManager::ClipIdMap& aCache);
   void DefineAndPushChain(const DisplayItemClipChain* aChain,
                           wr::DisplayListBuilder& aBuilder,
                           const StackingContextHelper& aStackingContext,
                           int32_t aAppUnitsPerDevPixel,
                           WebRenderLayerManager::ClipIdMap& aCache);
   bool PushScrollLayer(const FrameMetrics& aMetrics,
                        const StackingContextHelper& aStackingContext);
   void PushLayerLocalClip(const StackingContextHelper& aStackingContext);
   void PushLayerClip(const LayerClip& aClip,
                      const StackingContextHelper& aSc);
 
   WebRenderLayer* mLayer;
   wr::DisplayListBuilder* mBuilder;
   bool mPushedLayerLocalClip;
+  bool mPushedClipAndScroll;
   std::vector<wr::ScrollOrClipId> mPushedClips;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif