Bug 1022612. Part 10: Implement merging and flattening in ProcessDisplayItems. r=mattwoodrow
☠☠ backed out by 2bcded4e3b4a ☠ ☠
authorRobert O'Callahan <robert@ocallahan.org>
Mon, 09 Jun 2014 16:48:00 +1200
changeset 216605 58abf5b0e148c18dcd77e1856161714ad93564f3
parent 216604 797058a09ad2bc81b303e299f6b0c25210a69a2c
child 216606 da9152b6ea2983e43a84a6416a6fb4befff57cd5
push id515
push userraliiev@mozilla.com
push dateMon, 06 Oct 2014 12:51:51 +0000
treeherdermozilla-release@267c7a481bef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow
bugs1022612
milestone33.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 1022612. Part 10: Implement merging and flattening in ProcessDisplayItems. r=mattwoodrow BuildContainerLayerFor now has to be able to mutate the passed-in display item list.
layout/base/FrameLayerBuilder.cpp
layout/base/FrameLayerBuilder.h
layout/base/nsDisplayList.cpp
layout/base/nsDisplayList.h
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -18,16 +18,17 @@
 #include "MaskLayerImageCache.h"
 #include "nsIScrollableFrame.h"
 #include "nsPrintfCString.h"
 #include "LayerTreeInvalidation.h"
 #include "nsSVGIntegrationUtils.h"
 #include "ImageContainer.h"
 #include "ActiveLayerTracker.h"
 #include "gfx2DGlue.h"
+#include "mozilla/LookAndFeel.h"
 
 #include "GeckoProfiler.h"
 #include "mozilla/gfx/Tools.h"
 #include "mozilla/gfx/2D.h"
 #include "gfxPrefs.h"
 
 #include <algorithm>
 
@@ -516,17 +517,17 @@ public:
   enum ProcessDisplayItemsFlags {
     NO_COMPONENT_ALPHA = 0x01,
   };
 
   /**
    * This is the method that actually walks a display list and builds
    * the child layers.
    */
-  void ProcessDisplayItems(const nsDisplayList& aList, uint32_t aFlags);
+  void ProcessDisplayItems(nsDisplayList* aList, uint32_t aFlags);
   /**
    * This finalizes all the open ThebesLayers by popping every element off
    * mThebesLayerDataStack, then sets the children of the container layer
    * to be all the layers in mNewChildLayers in that order and removes any
    * layers as children of the container that aren't in mNewChildLayers.
    * @param aTextContentFlags if any child layer has CONTENT_COMPONENT_ALPHA,
    * set *aTextContentFlags to CONTENT_COMPONENT_ALPHA
    */
@@ -2405,53 +2406,113 @@ ContainerState::ChooseAnimatedGeometryRo
     // should result in the least invalidation when scrolling.
     *aAnimatedGeometryRoot =
       nsLayoutUtils::GetAnimatedGeometryRootFor(item, mBuilder);
     return true;
   }
   return false;
 }
 
+/* Checks if aPotentialScrollItem is a scroll layer item and aPotentialScrollbarItem
+ * is an overlay scrollbar item for the same scroll frame.
+ */
+static bool
+IsScrollLayerItemAndOverlayScrollbarForScrollFrame(
+  nsDisplayItem* aPotentialScrollItem, nsDisplayItem* aPotentialScrollbarItem)
+{
+  if (aPotentialScrollItem->GetType() == nsDisplayItem::TYPE_SCROLL_LAYER &&
+      aPotentialScrollbarItem &&
+      aPotentialScrollbarItem->GetType() == nsDisplayItem::TYPE_OWN_LAYER &&
+      LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars)) {
+    nsDisplayScrollLayer* scrollItem =
+      static_cast<nsDisplayScrollLayer*>(aPotentialScrollItem);
+    nsDisplayOwnLayer* layerItem =
+      static_cast<nsDisplayOwnLayer*>(aPotentialScrollbarItem);
+    if ((layerItem->GetFlags() &
+         (nsDisplayOwnLayer::VERTICAL_SCROLLBAR |
+          nsDisplayOwnLayer::HORIZONTAL_SCROLLBAR)) &&
+        layerItem->Frame()->GetParent() == scrollItem->GetScrollFrame()) {
+      return true;
+    }
+  }
+  return false;
+}
+
 /*
  * Iterate through the non-clip items in aList and its descendants.
  * For each item we compute the effective clip rect. Each item is assigned
  * to a layer. We invalidate the areas in ThebesLayers where an item
  * has moved from one ThebesLayer to another. Also,
  * aState->mInvalidThebesContent is invalidated in every ThebesLayer.
  * We set the clip rect for items that generated their own layer, and
  * create a mask layer to do any rounded rect clipping.
  * (ThebesLayers don't need a clip rect on the layer, we clip the items
  * individually when we draw them.)
  * We set the visible rect for all layers, although the actual setting
  * of visible rects for some ThebesLayers is deferred until the calling
  * of ContainerState::Finish.
  */
 void
-ContainerState::ProcessDisplayItems(const nsDisplayList& aList,
+ContainerState::ProcessDisplayItems(nsDisplayList* aList,
                                     uint32_t aFlags)
 {
   PROFILER_LABEL("ContainerState", "ProcessDisplayItems",
     js::ProfileEntry::Category::GRAPHICS);
 
   const nsIFrame* lastAnimatedGeometryRoot = mContainerReferenceFrame;
   nsPoint topLeft(0,0);
 
   // When NO_COMPONENT_ALPHA is set, items will be flattened into a single
   // layer, so we need to choose which active scrolled root to use for all
   // items.
   if (aFlags & NO_COMPONENT_ALPHA) {
-    if (ChooseAnimatedGeometryRoot(aList, &lastAnimatedGeometryRoot)) {
+    if (ChooseAnimatedGeometryRoot(*aList, &lastAnimatedGeometryRoot)) {
       topLeft = lastAnimatedGeometryRoot->GetOffsetToCrossDoc(mContainerReferenceFrame);
     }
   }
 
   int32_t maxLayers = nsDisplayItem::MaxActiveLayers();
   int layerCount = 0;
 
-  for (nsDisplayItem* item = aList.GetBottom(); item; item = item->GetAbove()) {
+  nsDisplayList savedItems;
+  nsDisplayItem* item;
+  while ((item = aList->RemoveBottom()) != nullptr) {
+    // Peek ahead to the next item and try merging with it or swapping with it
+    // if necessary.
+    nsDisplayItem* aboveItem;
+    while ((aboveItem = aList->GetBottom()) != nullptr) {
+      if (aboveItem->TryMerge(mBuilder, item)) {
+        aList->RemoveBottom();
+        item->~nsDisplayItem();
+        item = aboveItem;
+      } else if (IsScrollLayerItemAndOverlayScrollbarForScrollFrame(aboveItem, item)) {
+        // If an overlay scrollbar item is between a scroll layer item and the
+        // other scroll layer items that we need to merge with just move the
+        // scrollbar item up, that way it will be on top of the scrolled content
+        // and we can try to merge all the scroll layer items.
+        aList->RemoveBottom();
+        aList->AppendToBottom(item);
+        item = aboveItem;
+      } else {
+        break;
+      }
+    }
+
+    nsDisplayList* itemSameCoordinateSystemChildren
+      = item->GetSameCoordinateSystemChildren();
+    if (itemSameCoordinateSystemChildren) {
+      if (item->ShouldFlattenAway(mBuilder)) {
+        aList->AppendToBottom(itemSameCoordinateSystemChildren);
+        item->~nsDisplayItem();
+        continue;
+      }
+    }
+
+    savedItems.AppendToTop(item);
+
     NS_ASSERTION(mAppUnitsPerDevPixel == AppUnitsPerDevPixel(item),
       "items in a container layer should all have the same app units per dev pixel");
 
     nsIntRect itemVisibleRect =
       ScaleToOutsidePixels(item->GetVisibleRect(), false);
     bool snap;
     nsRect itemContent = item->GetBounds(mBuilder, &snap);
     nsIntRect itemDrawRect = ScaleToOutsidePixels(itemContent, snap);
@@ -2662,16 +2723,18 @@ ContainerState::ProcessDisplayItems(cons
 
         mLayerBuilder->AddThebesDisplayItem(data, item, itemClip,
                                             mContainerFrame,
                                             layerState, topLeft,
                                             geometry);
       }
     }
   }
+
+  aList->AppendToTop(&savedItems);
 }
 
 void
 ContainerState::InvalidateForLayerChange(nsDisplayItem* aItem,
                                          Layer* aNewLayer,
                                          const DisplayItemClip& aClip,
                                          const nsPoint& aTopLeft,
                                          nsDisplayItemGeometry *aGeometry)
@@ -3308,17 +3371,17 @@ FrameLayerBuilder::RestoreThebesLayerIte
   return PL_DHASH_NEXT;
 }
 
 already_AddRefed<ContainerLayer>
 FrameLayerBuilder::BuildContainerLayerFor(nsDisplayListBuilder* aBuilder,
                                           LayerManager* aManager,
                                           nsIFrame* aContainerFrame,
                                           nsDisplayItem* aContainerItem,
-                                          const nsDisplayList& aChildren,
+                                          nsDisplayList* aChildren,
                                           const ContainerLayerParameters& aParameters,
                                           const gfx3DMatrix* aTransform,
                                           uint32_t aFlags)
 {
   uint32_t containerDisplayItemKey =
     aContainerItem ? aContainerItem->GetPerFrameKey() : nsDisplayItem::TYPE_ZERO;
   NS_ASSERTION(aContainerFrame, "Container display items here should have a frame");
   NS_ASSERTION(!aContainerItem ||
@@ -3372,17 +3435,17 @@ FrameLayerBuilder::BuildContainerLayerFo
     state = LAYER_ACTIVE;
   }
 
   if (aContainerItem && state == LAYER_ACTIVE_EMPTY) {
     // Empty layers only have metadata and should never have display items. We
     // early exit because later, invalidation will walk up the frame tree to
     // determine which thebes layer gets invalidated. Since an empty layer
     // should never have anything to paint, it should never be invalidated.
-    NS_ASSERTION(aChildren.IsEmpty(), "Should have no children");
+    NS_ASSERTION(aChildren->IsEmpty(), "Should have no children");
     return containerLayer.forget();
   }
 
   ContainerLayerParameters scaleParameters;
   if (!ChooseScaleAndSetTransform(this, aBuilder, aContainerFrame, aChildren.GetVisibleRect(),
                                   aTransform, aParameters, containerLayer, state, scaleParameters)) {
     return nullptr;
   }
@@ -3440,27 +3503,27 @@ FrameLayerBuilder::BuildContainerLayerFo
       mThebesLayerItems.EnumerateEntries(RestoreThebesLayerItemEntries,
                                          &mContainerLayerGeneration);
       aContainerFrame->AddStateBits(NS_FRAME_NO_COMPONENT_ALPHA);
       continue;
     }
     break;
   }
 
-  NS_ASSERTION(bounds.IsEqualInterior(aChildren.GetBounds(aBuilder)), "Wrong bounds");
+  NS_ASSERTION(bounds.IsEqualInterior(aChildren->GetBounds(aBuilder)), "Wrong bounds");
   pixBounds.MoveBy(nsIntPoint(scaleParameters.mOffset.x, scaleParameters.mOffset.y));
   if (aParameters.mAncestorClipRect && !(aFlags & CONTAINER_NOT_CLIPPED_BY_ANCESTORS)) {
     SetVisibleRegionForLayer(containerLayer, nsIntRegion(pixBounds),
                              *aParameters.mAncestorClipRect);
   } else {
     containerLayer->SetVisibleRegion(pixBounds);
   }
   // Make sure that rounding the visible region out didn't add any area
   // we won't paint
-  if (aChildren.IsOpaque() && !aChildren.NeedsTransparentSurface()) {
+  if (aChildren->IsOpaque() && !aChildren->NeedsTransparentSurface()) {
     bounds.ScaleRoundIn(scaleParameters.mXScale, scaleParameters.mYScale);
     if (bounds.Contains(pixBounds.ToAppUnits(appUnitsPerDevPixel))) {
       // Clear CONTENT_COMPONENT_ALPHA
       flags = Layer::CONTENT_OPAQUE;
     }
   }
   containerLayer->SetContentFlags(flags);
 
--- a/layout/base/FrameLayerBuilder.h
+++ b/layout/base/FrameLayerBuilder.h
@@ -202,23 +202,24 @@ public:
    * items in the display list, making items with their own layers
    * children of the new container, and assigning all other items to
    * ThebesLayer children created and managed by the FrameLayerBuilder.
    * Returns a layer with clip rect cleared; it is the
    * caller's responsibility to add any clip rect. The visible region
    * is set based on what's in the layer.
    * The container layer is transformed by aTransform (if non-null), and
    * the result is transformed by the scale factors in aContainerParameters.
+   * aChildren is modified due to display item merging and flattening.
    */
   already_AddRefed<ContainerLayer>
   BuildContainerLayerFor(nsDisplayListBuilder* aBuilder,
                          LayerManager* aManager,
                          nsIFrame* aContainerFrame,
                          nsDisplayItem* aContainerItem,
-                         const nsDisplayList& aChildren,
+                         nsDisplayList* aChildren,
                          const ContainerLayerParameters& aContainerParameters,
                          const gfx3DMatrix* aTransform,
                          uint32_t aFlags = 0);
 
   /**
    * Get a retained layer for a display item that needs to create its own
    * layer for rendering (i.e. under nsDisplayItem::BuildLayer). Returns
    * null if no retained layer is available, which usually means that this
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -1227,32 +1227,31 @@ nsDisplayList::ComputeVisibilityForSubli
 #if defined(DEBUG) || defined(MOZ_DUMP_PAINTING)
   mDidComputeVisibility = true;
 #endif
   return anyVisible;
 }
 
 void nsDisplayList::PaintRoot(nsDisplayListBuilder* aBuilder,
                               nsRenderingContext* aCtx,
-                              uint32_t aFlags) const {
+                              uint32_t aFlags) {
   PROFILER_LABEL("nsDisplayList", "PaintRoot",
     js::ProfileEntry::Category::GRAPHICS);
-
   PaintForFrame(aBuilder, aCtx, aBuilder->RootReferenceFrame(), aFlags);
 }
 
 /**
  * We paint by executing a layer manager transaction, constructing a
  * single layer representing the display list, and then making it the
  * root of the layer manager, drawing into the ThebesLayers.
  */
 void nsDisplayList::PaintForFrame(nsDisplayListBuilder* aBuilder,
                                   nsRenderingContext* aCtx,
                                   nsIFrame* aForFrame,
-                                  uint32_t aFlags) const {
+                                  uint32_t aFlags) {
   NS_ASSERTION(mDidComputeVisibility,
                "Must call ComputeVisibility before calling Paint");
 
   nsRefPtr<LayerManager> layerManager;
   bool widgetTransaction = false;
   bool allowRetaining = false;
   bool doBeginTransaction = true;
   nsView *view = nullptr;
@@ -1314,17 +1313,17 @@ void nsDisplayList::PaintForFrame(nsDisp
 
   nsAutoPtr<LayerProperties> props(computeInvalidRect ? 
                                      LayerProperties::CloneFrom(layerManager->GetRoot()) : 
                                      nullptr);
 
   ContainerLayerParameters containerParameters
     (presShell->GetXResolution(), presShell->GetYResolution());
   nsRefPtr<ContainerLayer> root = layerBuilder->
-    BuildContainerLayerFor(aBuilder, layerManager, aForFrame, nullptr, *this,
+    BuildContainerLayerFor(aBuilder, layerManager, aForFrame, nullptr, this,
                            containerParameters, nullptr);
 
   nsIDocument* document = nullptr;
   if (presShell) {
     document = presShell->GetDocument();
   }
 
   if (widgetTransaction ||
@@ -3283,17 +3282,17 @@ already_AddRefed<Layer>
 nsDisplayOpacity::BuildLayer(nsDisplayListBuilder* aBuilder,
                              LayerManager* aManager,
                              const ContainerLayerParameters& aContainerParameters) {
   if (mOpacity == 0 && mFrame->GetContent() &&
       !nsLayoutUtils::HasAnimations(mFrame->GetContent(), eCSSProperty_opacity)) {
     return nullptr;
   }
   nsRefPtr<Layer> container = aManager->GetLayerBuilder()->
-    BuildContainerLayerFor(aBuilder, aManager, mFrame, this, mList,
+    BuildContainerLayerFor(aBuilder, aManager, mFrame, this, &mList,
                            aContainerParameters, nullptr);
   if (!container)
     return nullptr;
 
   container->SetOpacity(mOpacity);
   nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(container, aBuilder,
                                                            this, mFrame,
                                                            eCSSProperty_opacity);
@@ -3446,17 +3445,17 @@ nsDisplayMixBlendMode::GetLayerState(nsD
 already_AddRefed<Layer>
 nsDisplayMixBlendMode::BuildLayer(nsDisplayListBuilder* aBuilder,
                                   LayerManager* aManager,
                                   const ContainerLayerParameters& aContainerParameters) {
   ContainerLayerParameters newContainerParameters = aContainerParameters;
   newContainerParameters.mDisableSubpixelAntialiasingInDescendants = true;
 
   nsRefPtr<Layer> container = aManager->GetLayerBuilder()->
-  BuildContainerLayerFor(aBuilder, aManager, mFrame, this, mList,
+  BuildContainerLayerFor(aBuilder, aManager, mFrame, this, &mList,
                          newContainerParameters, nullptr);
   if (!container) {
     return nullptr;
   }
 
   container->DeprecatedSetMixBlendMode(nsCSSRendering::GetGFXBlendMode(mFrame->StyleDisplay()->mMixBlendMode));
 
   return container.forget();
@@ -3523,17 +3522,17 @@ nsDisplayBlendContainer::BuildLayer(nsDi
                                     LayerManager* aManager,
                                     const ContainerLayerParameters& aContainerParameters) {
   // turn off anti-aliasing in the parent stacking context because it changes
   // how the group is initialized.
   ContainerLayerParameters newContainerParameters = aContainerParameters;
   newContainerParameters.mDisableSubpixelAntialiasingInDescendants = true;
 
   nsRefPtr<Layer> container = aManager->GetLayerBuilder()->
-  BuildContainerLayerFor(aBuilder, aManager, mFrame, this, mList,
+  BuildContainerLayerFor(aBuilder, aManager, mFrame, this, &mList,
                          newContainerParameters, nullptr);
   if (!container) {
     return nullptr;
   }
   
   container->SetForceIsolatedGroup(true);
   return container.forget();
 }
@@ -3568,17 +3567,17 @@ nsDisplayOwnLayer::~nsDisplayOwnLayer() 
 #endif
 
 // nsDisplayOpacity uses layers for rendering
 already_AddRefed<Layer>
 nsDisplayOwnLayer::BuildLayer(nsDisplayListBuilder* aBuilder,
                               LayerManager* aManager,
                               const ContainerLayerParameters& aContainerParameters) {
   nsRefPtr<ContainerLayer> layer = aManager->GetLayerBuilder()->
-    BuildContainerLayerFor(aBuilder, aManager, mFrame, this, mList,
+    BuildContainerLayerFor(aBuilder, aManager, mFrame, this, &mList,
                            aContainerParameters, nullptr);
   if (mFlags & VERTICAL_SCROLLBAR) {
     layer->SetScrollbarData(mScrollTarget, Layer::ScrollDirection::VERTICAL);
   }
   if (mFlags & HORIZONTAL_SCROLLBAR) {
     layer->SetScrollbarData(mScrollTarget, Layer::ScrollDirection::HORIZONTAL);
   }
 
@@ -3891,17 +3890,17 @@ nsDisplayScrollLayer::GetBounds(nsDispla
   return nsDisplayWrapList::GetBounds(aBuilder, aSnap);
 }
 
 already_AddRefed<Layer>
 nsDisplayScrollLayer::BuildLayer(nsDisplayListBuilder* aBuilder,
                                  LayerManager* aManager,
                                  const ContainerLayerParameters& aContainerParameters) {
   nsRefPtr<ContainerLayer> layer = aManager->GetLayerBuilder()->
-    BuildContainerLayerFor(aBuilder, aManager, mFrame, this, mList,
+    BuildContainerLayerFor(aBuilder, aManager, mFrame, this, &mList,
                            aContainerParameters, nullptr);
 
   nsRect viewport = mScrollFrame->GetRect() -
                     mScrollFrame->GetPosition() +
                     mScrollFrame->GetOffsetToCrossDoc(ReferenceFrame());
 
   layer->SetScrollHandoffParentId(mScrollParentId);
   RecordFrameMetrics(mScrolledFrame, mScrollFrame, ReferenceFrame(), layer,
@@ -4795,17 +4794,17 @@ already_AddRefed<Layer> nsDisplayTransfo
   if (mFrame->StyleDisplay()->mBackfaceVisibility == NS_STYLE_BACKFACE_VISIBILITY_HIDDEN &&
       newTransformMatrix.IsBackfaceVisible()) {
     return nullptr;
   }
 
   uint32_t flags = ShouldPrerenderTransformedContent(aBuilder, mFrame, false) ?
     FrameLayerBuilder::CONTAINER_NOT_CLIPPED_BY_ANCESTORS : 0;
   nsRefPtr<ContainerLayer> container = aManager->GetLayerBuilder()->
-    BuildContainerLayerFor(aBuilder, aManager, mFrame, this, *mStoredList.GetChildren(),
+    BuildContainerLayerFor(aBuilder, aManager, mFrame, this, mStoredList.GetChildren(),
                            aContainerParameters, &newTransformMatrix, flags);
 
   if (!container) {
     return nullptr;
   }
 
   // Add the preserve-3d flag for this layer, BuildContainerLayerFor clears all flags,
   // so we never need to explicitely unset this flag.
@@ -5319,17 +5318,17 @@ nsDisplaySVGEffects::BuildLayer(nsDispla
   }
 
   ContainerLayerParameters newContainerParameters = aContainerParameters;
   if (effectProperties.HasValidFilter()) {
     newContainerParameters.mDisableSubpixelAntialiasingInDescendants = true;
   }
 
   nsRefPtr<ContainerLayer> container = aManager->GetLayerBuilder()->
-    BuildContainerLayerFor(aBuilder, aManager, mFrame, this, mList,
+    BuildContainerLayerFor(aBuilder, aManager, mFrame, this, &mList,
                            newContainerParameters, nullptr);
 
   return container.forget();
 }
 
 bool nsDisplaySVGEffects::ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                               nsRegion* aVisibleRegion,
                                               const nsRect& aAllowVisibleRegionExpansion) {
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -1676,23 +1676,23 @@ public:
     PAINT_DEFAULT = 0,
     PAINT_USE_WIDGET_LAYERS = 0x01,
     PAINT_FLUSH_LAYERS = 0x02,
     PAINT_EXISTING_TRANSACTION = 0x04,
     PAINT_NO_COMPOSITE = 0x08,
     PAINT_COMPRESSED = 0x10
   };
   void PaintRoot(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx,
-                 uint32_t aFlags) const;
+                 uint32_t aFlags);
   /**
    * Like PaintRoot, but used for internal display sublists.
    * aForFrame is the frame that the list is associated with.
    */
   void PaintForFrame(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx,
-                     nsIFrame* aForFrame, uint32_t aFlags) const;
+                     nsIFrame* aForFrame, uint32_t aFlags);
   /**
    * Get the bounds. Takes the union of the bounds of all children.
    */
   nsRect GetBounds(nsDisplayListBuilder* aBuilder) const;
   /**
    * Find the topmost display item that returns a non-null frame, and return
    * the frame.
    */