Bug 1298218 - Add DisplayItemClipChain. r=mattwoodrow,tnikkel
authorMarkus Stange <mstange@themasta.com>
Fri, 27 Jan 2017 12:57:44 +0100
changeset 378691 d7b10f6c7982b619c8d877a9f81a7fc32f4c3fda
parent 378690 071bde29b52f3e2268bf2c4092835f9c03ad4e22
child 378692 a97b00d08fbd0705812120c3b2c35ded3812a16d
push id7198
push userjlorenzo@mozilla.com
push dateTue, 18 Apr 2017 12:07:49 +0000
treeherdermozilla-beta@d57aa49c3948 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow, tnikkel
bugs1298218
milestone54.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 1298218 - Add DisplayItemClipChain. r=mattwoodrow,tnikkel MozReview-Commit-ID: K9mg86VgK10
layout/painting/DisplayItemClipChain.cpp
layout/painting/DisplayItemClipChain.h
layout/painting/DisplayListClipState.h
layout/painting/moz.build
layout/painting/nsDisplayList.cpp
layout/painting/nsDisplayList.h
new file mode 100644
--- /dev/null
+++ b/layout/painting/DisplayItemClipChain.cpp
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DisplayItemClipChain.h"
+
+namespace mozilla {
+
+/* static */ const DisplayItemClip*
+DisplayItemClipChain::ClipForASR(const DisplayItemClipChain* aClipChain, const ActiveScrolledRoot* aASR)
+{
+  while (aClipChain && !ActiveScrolledRoot::IsAncestor(aClipChain->mASR, aASR)) {
+    aClipChain = aClipChain->mParent;
+  }
+  return (aClipChain && aClipChain->mASR == aASR) ? &aClipChain->mClip : nullptr;
+}
+
+bool
+DisplayItemClipChain::Equal(const DisplayItemClipChain* aClip1, const DisplayItemClipChain* aClip2)
+{
+  if (aClip1 == aClip2) {
+    return true;
+  }
+
+  if (!aClip1 || !aClip2) {
+    return false;
+  }
+
+  return aClip1->mASR == aClip2->mASR &&
+         aClip1->mClip == aClip2->mClip &&
+         Equal(aClip1->mParent, aClip2->mParent);
+}
+
+/* static */ nsCString
+DisplayItemClipChain::ToString(const DisplayItemClipChain* aClipChain)
+{
+  nsAutoCString str;
+  for (auto* sc = aClipChain; sc; sc = sc->mParent) {
+    if (sc->mASR) {
+      str.AppendPrintf("<%s> [0x%p]", sc->mClip.ToString().get(), sc->mASR->mScrollableFrame);
+
+    } else {
+      str.AppendPrintf("<%s> [root asr]", sc->mClip.ToString().get());
+    }
+    if (sc->mParent) {
+      str.Append(", ");
+    }
+  }
+  return str;
+}
+
+bool
+DisplayItemClipChain::HasRoundedCorners() const
+{
+  return mClip.GetRoundedRectCount() > 0 || (mParent && mParent->HasRoundedCorners());
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/layout/painting/DisplayItemClipChain.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef DISPLAYITEMCLIPCHAIN_H_
+#define DISPLAYITEMCLIPCHAIN_H_
+
+#include "mozilla/Assertions.h"
+#include "DisplayItemClip.h"
+#include "nsString.h"
+
+class nsIScrollableFrame;
+
+namespace mozilla {
+
+struct ActiveScrolledRoot;
+
+/**
+ * A DisplayItemClipChain is a linked list of DisplayItemClips where each clip
+ * is associated with an active scrolled root that describes what the clip
+ * moves with.
+ * We use a chain instead of just one intersected clip due to async scrolling:
+ * A clip that moves along with a display item can be fused to the item's
+ * contents when drawing the layer contents, but all other clips in the chain
+ * need to be kept separate so that they can be applied at composition time,
+ * after any async scroll offsets have been applied.
+ * The clip chain is created during display list construction by the builder's
+ * DisplayListClipState.
+ * The clip chain order is determined by the active scrolled root order.
+ * For every DisplayItemClipChain object |clipChain|, the following holds:
+ * !clipChain->mParent || ActiveScrolledRoot::IsAncestor(clipChain->mParent->mASR, clipChain->mASR).
+ * The clip chain can skip over active scrolled roots. That just means that
+ * there is no clip that moves with the skipped ASR in this chain.
+ */
+struct DisplayItemClipChain {
+
+  /**
+   * Get the display item clip in this chain that moves with aASR, or nullptr
+   * if no such clip exists. aClipChain can be null.
+   */
+  static const DisplayItemClip* ClipForASR(const DisplayItemClipChain* aClipChain,
+                                           const ActiveScrolledRoot* aASR);
+
+  static bool Equal(const DisplayItemClipChain* aClip1, const DisplayItemClipChain* aClip2);
+
+  static nsCString ToString(const DisplayItemClipChain* aClipChain);
+
+  bool HasRoundedCorners() const;
+
+  DisplayItemClip mClip;
+  const ActiveScrolledRoot* mASR;
+  const DisplayItemClipChain* mParent;
+};
+
+} // namespace mozilla
+
+#endif /* DISPLAYITEMCLIPCHAIN_H_ */
--- a/layout/painting/DisplayListClipState.h
+++ b/layout/painting/DisplayListClipState.h
@@ -2,16 +2,17 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef DISPLAYLISTCLIPSTATE_H_
 #define DISPLAYLISTCLIPSTATE_H_
 
 #include "DisplayItemClip.h"
+#include "DisplayItemClipChain.h"
 #include "DisplayItemScrollClip.h"
 
 #include "mozilla/DebugOnly.h"
 
 class nsIFrame;
 class nsIScrollableFrame;
 class nsDisplayListBuilder;
 
--- a/layout/painting/moz.build
+++ b/layout/painting/moz.build
@@ -5,16 +5,17 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 with Files('**'):
     BUG_COMPONENT = ('Core', 'Layout: View Rendering')
 
 EXPORTS += [
     'ActiveLayerTracker.h',
     'DisplayItemClip.h',
+    'DisplayItemClipChain.h',
     'DisplayItemScrollClip.h',
     'DisplayListClipState.h',
     'FrameLayerBuilder.h',
     'LayerState.h',
     'nsDisplayItemTypes.h',
     'nsDisplayItemTypesList.h',
     'nsDisplayList.h',
     'nsDisplayListInvalidation.h',
@@ -23,16 +24,17 @@ EXPORTS += [
 EXPORTS.mozilla += [
     'PaintTracker.h',
 ]
 
 UNIFIED_SOURCES += [
     'ActiveLayerTracker.cpp',
     'DashedCornerFinder.cpp',
     'DisplayItemClip.cpp',
+    'DisplayItemClipChain.cpp',
     'DisplayItemScrollClip.cpp',
     'DisplayListClipState.cpp',
     'DottedCornerFinder.cpp',
     'FrameLayerBuilder.cpp',
     'MaskLayerImageCache.cpp',
     'nsCSSRendering.cpp',
     'nsCSSRenderingBorders.cpp',
     'nsDisplayList.cpp',
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -1071,16 +1071,19 @@ nsDisplayListBuilder::~nsDisplayListBuil
     c->DisplayItemClip::~DisplayItemClip();
   }
   for (DisplayItemScrollClip* c : mScrollClipsToDestroy) {
     c->DisplayItemScrollClip::~DisplayItemScrollClip();
   }
   for (ActiveScrolledRoot* asr : mActiveScrolledRoots) {
     asr->ActiveScrolledRoot::~ActiveScrolledRoot();
   }
+  for (DisplayItemClipChain* c : mClipChainsToDestroy) {
+    c->DisplayItemClipChain::~DisplayItemClipChain();
+  }
 
   PL_FinishArenaPool(&mPool);
   MOZ_COUNT_DTOR(nsDisplayListBuilder);
 }
 
 uint32_t
 nsDisplayListBuilder::GetBackgroundPaintFlags() {
   uint32_t flags = 0;
@@ -1297,16 +1300,88 @@ nsDisplayListBuilder::AllocateActiveScro
 {
   void* p = Allocate(sizeof(ActiveScrolledRoot));
   ActiveScrolledRoot* asr =
     new (KnownNotNull, p) ActiveScrolledRoot(aParent, aScrollableFrame);
   mActiveScrolledRoots.AppendElement(asr);
   return asr;
 }
 
+const DisplayItemClipChain*
+nsDisplayListBuilder::AllocateDisplayItemClipChain(const DisplayItemClip& aClip,
+                                                   const ActiveScrolledRoot* aASR,
+                                                   const DisplayItemClipChain* aParent)
+{
+  void* p = Allocate(sizeof(DisplayItemClipChain));
+  DisplayItemClipChain* c = new (KnownNotNull, p) DisplayItemClipChain{ aClip, aASR, aParent };
+  mClipChainsToDestroy.AppendElement(c);
+  return c;
+}
+
+struct ClipChainItem {
+  DisplayItemClip clip;
+  const ActiveScrolledRoot* asr;
+};
+
+const DisplayItemClipChain*
+nsDisplayListBuilder::CreateClipChainIntersection(const DisplayItemClipChain* aAncestor,
+                                                  const DisplayItemClipChain* aLeafClip1,
+                                                  const DisplayItemClipChain* aLeafClip2)
+{
+  AutoTArray<ClipChainItem,8> intersectedClips;
+
+  const DisplayItemClipChain* clip1 = aLeafClip1;
+  const DisplayItemClipChain* clip2 = aLeafClip2;
+
+  const ActiveScrolledRoot* asr =
+    ActiveScrolledRoot::PickDescendant(clip1 ? clip1->mASR : nullptr,
+                                       clip2 ? clip2->mASR : nullptr);
+
+  // Build up the intersection from the leaf to the root and put it into
+  // intersectedClips. The loop below will convert intersectedClips into an
+  // actual DisplayItemClipChain.
+  // (We need to do this in two passes because we need the parent clip in order
+  // to create the DisplayItemClipChain object, but the parent clip has not
+  // been created at that point.)
+  while (!aAncestor || asr != aAncestor->mASR) {
+    if (clip1 && clip1->mASR == asr) {
+      if (clip2 && clip2->mASR == asr) {
+        DisplayItemClip intersection = clip1->mClip;
+        intersection.IntersectWith(clip2->mClip);
+        intersectedClips.AppendElement(ClipChainItem{ intersection, asr });
+        clip2 = clip2->mParent;
+      } else {
+        intersectedClips.AppendElement(ClipChainItem{ clip1->mClip, asr });
+      }
+      clip1 = clip1->mParent;
+    } else if (clip2 && clip2->mASR == asr) {
+      intersectedClips.AppendElement(ClipChainItem{ clip2->mClip, asr });
+      clip2 = clip2->mParent;
+    }
+    if (!asr) {
+      MOZ_ASSERT(!aAncestor, "We should have exited this loop earlier");
+      break;
+    }
+    asr = asr->mParent;
+  }
+
+  // Convert intersectedClips into a DisplayItemClipChain.
+  const DisplayItemClipChain* parentSC = aAncestor;
+  for (auto& sc : Reversed(intersectedClips)) {
+    parentSC = AllocateDisplayItemClipChain(sc.clip, sc.asr, parentSC);
+  }
+  return parentSC;
+}
+
+const DisplayItemClipChain*
+nsDisplayListBuilder::CopyWholeChain(const DisplayItemClipChain* aClipChain)
+{
+  return CreateClipChainIntersection(nullptr, aClipChain, nullptr);
+}
+
 const DisplayItemClip*
 nsDisplayListBuilder::AllocateDisplayItemClip(const DisplayItemClip& aOriginal)
 {
   void* p = Allocate(sizeof(DisplayItemClip));
   if (!aOriginal.GetRoundedRectCount()) {
     memcpy(p, &aOriginal, sizeof(DisplayItemClip));
     return static_cast<DisplayItemClip*>(p);
   }
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -274,16 +274,17 @@ class nsDisplayListBuilder {
     // How far this frame is from the root of the current 3d context.
     int mAccumulatedRectLevels;
     nsRect mDirtyRect;
   };
 
 public:
   typedef mozilla::FrameLayerBuilder FrameLayerBuilder;
   typedef mozilla::DisplayItemClip DisplayItemClip;
+  typedef mozilla::DisplayItemClipChain DisplayItemClipChain;
   typedef mozilla::DisplayListClipState DisplayListClipState;
   typedef mozilla::DisplayItemScrollClip DisplayItemScrollClip;
   typedef mozilla::ActiveScrolledRoot ActiveScrolledRoot;
   typedef nsIWidget::ThemeGeometry ThemeGeometry;
   typedef mozilla::layers::Layer Layer;
   typedef mozilla::layers::FrameMetrics FrameMetrics;
   typedef mozilla::layers::FrameMetrics::ViewID ViewID;
   typedef mozilla::gfx::Matrix4x4 Matrix4x4;
@@ -729,16 +730,42 @@ public:
   /**
    * Allocate a new ActiveScrolledRoot in the arena. Will be cleaned up
    * automatically when the arena goes away.
    */
   ActiveScrolledRoot* AllocateActiveScrolledRoot(const ActiveScrolledRoot* aParent,
                                                  nsIScrollableFrame* aScrollableFrame);
 
   /**
+   * Allocate a new DisplayItemClipChain object in the arena. Will be cleaned
+   * up automatically when the arena goes away.
+   */
+  const DisplayItemClipChain* AllocateDisplayItemClipChain(const DisplayItemClip& aClip,
+                                                           const ActiveScrolledRoot* aASR,
+                                                           const DisplayItemClipChain* aParent);
+
+  /**
+   * Intersect two clip chains, allocating the new clip chain items in this
+   * builder's arena. The result is parented to aAncestor, and no intersections
+   * happen past aAncestor's ASR.
+   * That means aAncestor has to be living in this builder's arena already.
+   * aLeafClip1 and aLeafClip2 only need to outlive the call to this function,
+   * their values are copied into the newly-allocated intersected clip chain
+   * and this function does not hold on to any pointers to them.
+   */
+  const DisplayItemClipChain* CreateClipChainIntersection(const DisplayItemClipChain* aAncestor,
+                                                          const DisplayItemClipChain* aLeafClip1,
+                                                          const DisplayItemClipChain* aLeafClip2);
+
+  /**
+   * Clone the supplied clip chain's chain items into this builder's arena.
+   */
+  const DisplayItemClipChain* CopyWholeChain(const DisplayItemClipChain* aClipChain);
+
+  /**
    * Allocate a new DisplayItemClip in the arena. Will be cleaned up
    * automatically when the arena goes away.
    */
   const DisplayItemClip* AllocateDisplayItemClip(const DisplayItemClip& aOriginal);
 
   /**
    * Allocate a new DisplayItemScrollClip in the arena. Will be cleaned up
    * automatically when the arena goes away.
@@ -1409,16 +1436,17 @@ private:
   // display items for the contents of frames with SVG effects.
   // Only non-null when ShouldBuildScrollInfoItemsForHoisting() is true.
   // This is a pointer and not a real nsDisplayList value because the
   // nsDisplayList class is defined below this class, so we can't use it here.
   nsDisplayList*                 mScrollInfoItemsForHoisting;
   nsTArray<DisplayItemScrollClip*> mScrollClipsToDestroy;
   nsTArray<DisplayItemClip*>     mDisplayItemClipsToDestroy;
   nsTArray<ActiveScrolledRoot*>  mActiveScrolledRoots;
+  nsTArray<DisplayItemClipChain*> mClipChainsToDestroy;
   nsDisplayListBuilderMode       mMode;
   ViewID                         mCurrentScrollParentId;
   ViewID                         mCurrentScrollbarTarget;
   uint32_t                       mCurrentScrollbarFlags;
   Preserves3DContext             mPreserves3DCtx;
   uint32_t                       mPerspectiveItemIndex;
   int32_t                        mSVGEffectsBuildingDepth;
   bool                           mContainsBlendMode;
@@ -1486,16 +1514,17 @@ protected:
  * Display items belong to a list at all times (except temporarily as they
  * move from one list to another).
  */
 class nsDisplayItem : public nsDisplayItemLink {
 public:
   typedef mozilla::ContainerLayerParameters ContainerLayerParameters;
   typedef mozilla::DisplayItemClip DisplayItemClip;
   typedef mozilla::DisplayItemScrollClip DisplayItemScrollClip;
+  typedef mozilla::DisplayItemClipChain DisplayItemClipChain;
   typedef mozilla::ActiveScrolledRoot ActiveScrolledRoot;
   typedef mozilla::layers::FrameMetrics FrameMetrics;
   typedef mozilla::layers::ScrollMetadata ScrollMetadata;
   typedef mozilla::layers::FrameMetrics::ViewID ViewID;
   typedef mozilla::layers::Layer Layer;
   typedef mozilla::layers::LayerManager LayerManager;
   typedef mozilla::LayerState LayerState;