Bug 1022612. Part 11: Set opaque flag on nsDisplayList if we find an opaque item that covers the whole list. r=mattwoodrow
authorRobert O'Callahan <robert@ocallahan.org>
Mon, 09 Jun 2014 16:48:00 +1200
changeset 217056 a0e6a1d488a8d29e270de6f06d8f2191b0b673cd
parent 217055 9bf91c8ca6fcb3b5a6c5f680e7547a6cb35eb267
child 217057 bb02e31b35a988f1feec2fcd97af4ea8d34c8876
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 11: Set opaque flag on nsDisplayList if we find an opaque item that covers the whole list. r=mattwoodrow This is less general than what nsDisplayItem::ComputeVisibility does. This means if multiple opaque items together cover the list bounds, but not individually, we won't mark the list as opaque. I think that should be OK.
layout/base/FrameLayerBuilder.cpp
layout/base/nsDisplayList.h
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -258,16 +258,17 @@ public:
    * We pass in a separate opaque rect because the opaque rect can be
    * bigger than the visible rect, and we want to have the biggest
    * opaque rect that we can.
    * @param aSolidColor if non-null, the visible area of the item is
    * a constant color given by *aSolidColor
    */
   void Accumulate(ContainerState* aState,
                   nsDisplayItem* aItem,
+                  const nsIntRegion& aClippedOpaqueRegion,
                   const nsIntRect& aVisibleRect,
                   const nsIntRect& aDrawRect,
                   const DisplayItemClip& aClip);
   const nsIFrame* GetAnimatedGeometryRoot() { return mAnimatedGeometryRoot; }
 
   /**
    * Add aHitRegion, aMaybeHitRegion, and aDispatchToContentHitRegion to the
    * hit regions for this ThebesLayer.
@@ -484,22 +485,24 @@ private:
  */
 class ContainerState {
 public:
   ContainerState(nsDisplayListBuilder* aBuilder,
                  LayerManager* aManager,
                  FrameLayerBuilder* aLayerBuilder,
                  nsIFrame* aContainerFrame,
                  nsDisplayItem* aContainerItem,
+                 const nsRect& aContainerBounds,
                  ContainerLayer* aContainerLayer,
                  const ContainerLayerParameters& aParameters) :
     mBuilder(aBuilder), mManager(aManager),
     mLayerBuilder(aLayerBuilder),
     mContainerFrame(aContainerFrame),
     mContainerLayer(aContainerLayer),
+    mContainerBounds(aContainerBounds),
     mParameters(aParameters),
     mNextFreeRecycledThebesLayer(0)
   {
     nsPresContext* presContext = aContainerFrame->PresContext();
     mAppUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
     mContainerReferenceFrame =
       const_cast<nsIFrame*>(aContainerItem ? aContainerItem->ReferenceFrameForChildren() :
                                              mBuilder->FindReferenceFrameFor(mContainerFrame));
@@ -528,18 +531,16 @@ public:
    * 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
    */
   void Finish(uint32_t *aTextContentFlags, LayerManagerData* aData, bool& aHasComponentAlphaChildren);
 
-  nsRect GetChildrenBounds() { return mBounds; }
-
   nscoord GetAppUnitsPerDevPixel() { return mAppUnitsPerDevPixel; }
 
   nsIntRect ScaleToNearestPixels(const nsRect& aRect)
   {
     return aRect.ScaleToNearestPixels(mParameters.mXScale, mParameters.mYScale,
                                       mAppUnitsPerDevPixel);
   }
   nsIntRegion ScaleRegionToNearestPixels(const nsRegion& aRegion)
@@ -708,23 +709,24 @@ protected:
 
   nsDisplayListBuilder*            mBuilder;
   LayerManager*                    mManager;
   FrameLayerBuilder*               mLayerBuilder;
   nsIFrame*                        mContainerFrame;
   nsIFrame*                        mContainerReferenceFrame;
   const nsIFrame*                  mContainerAnimatedGeometryRoot;
   ContainerLayer*                  mContainerLayer;
+  nsRect                           mContainerBounds;
+  DebugOnly<nsRect>                mAccumulatedChildBounds;
   ContainerLayerParameters         mParameters;
   /**
    * The region of ThebesLayers that should be invalidated every time
    * we recycle one.
    */
   nsIntRegion                      mInvalidThebesContent;
-  nsRect                           mBounds;
   nsAutoTArray<nsAutoPtr<ThebesLayerData>,1>  mThebesLayerDataStack;
   /**
    * We collect the list of children in here. During ProcessDisplayItems,
    * the layers in this array either have mContainerLayer as their parent,
    * or no parent.
    */
   typedef nsAutoTArray<nsRefPtr<Layer>,1> AutoLayersArray;
   AutoLayersArray                  mNewChildLayers;
@@ -2103,16 +2105,17 @@ WindowHasTransparency(nsDisplayListBuild
 {
   const nsRegion* windowTransparentRegion = aBuilder->GetFinalTransparentRegion();
   return windowTransparentRegion && !windowTransparentRegion->IsEmpty();
 }
 
 void
 ThebesLayerData::Accumulate(ContainerState* aState,
                             nsDisplayItem* aItem,
+                            const nsIntRegion& aClippedOpaqueRegion,
                             const nsIntRect& aVisibleRect,
                             const nsIntRect& aDrawRect,
                             const DisplayItemClip& aClip)
 {
   if (aState->mBuilder->NeedToForceTransparentSurfaceForItem(aItem)) {
     mForceTransparentSurface = true;
   }
   if (aState->mParameters.mDisableSubpixelAntialiasingInDescendants) {
@@ -2183,29 +2186,19 @@ ThebesLayerData::Accumulate(ContainerSta
     }
 
     mVisibleRegion.Or(mVisibleRegion, aVisibleRect);
     mVisibleRegion.SimplifyOutward(4);
     mDrawRegion.Or(mDrawRegion, aDrawRect);
     mDrawRegion.SimplifyOutward(4);
   }
 
-  bool snap;
-  nsRegion opaque = aItem->GetOpaqueRegion(aState->mBuilder, &snap);
-  if (!opaque.IsEmpty()) {
-    nsRegion opaqueClipped;
-    nsRegionRectIterator iter(opaque);
-    for (const nsRect* r = iter.Next(); r; r = iter.Next()) {
-      opaqueClipped.Or(opaqueClipped, aClip.ApproximateIntersectInward(*r));
-    }
-
-    nsIntRegion opaquePixels = aState->ScaleRegionToInsidePixels(opaqueClipped, snap);
-
-    nsIntRegionRectIterator iter2(opaquePixels);
-    for (const nsIntRect* r = iter2.Next(); r; r = iter2.Next()) {
+  if (!aClippedOpaqueRegion.IsEmpty()) {
+    nsIntRegionRectIterator iter(aClippedOpaqueRegion);
+    for (const nsIntRect* r = iter.Next(); r; r = iter.Next()) {
       // We don't use SimplifyInward here since it's not defined exactly
       // what it will discard. For our purposes the most important case
       // is a large opaque background at the bottom of z-order (e.g.,
       // a canvas background), so we need to make sure that the first rect
       // we see doesn't get discarded.
       nsIntRegion tmp;
       tmp.Or(mOpaqueRegion, *r);
        // Opaque display items in chrome documents whose window is partially
@@ -2520,18 +2513,21 @@ ContainerState::ProcessDisplayItems(nsDi
     nsIntRect clipRect;
     const DisplayItemClip& itemClip = item->GetClip();
     if (itemClip.HasClip()) {
       itemContent.IntersectRect(itemContent, itemClip.GetClipRect());
       clipRect = ScaleToNearestPixels(itemClip.GetClipRect());
       itemDrawRect.IntersectRect(itemDrawRect, clipRect);
       clipRect.MoveBy(mParameters.mOffset);
     }
-    mBounds.UnionRect(mBounds, itemContent);
+#ifdef DEBUG
+    ((nsRect&)mAccumulatedChildBounds).UnionRect(mAccumulatedChildBounds, itemContent);
+#endif
     itemVisibleRect.IntersectRect(itemVisibleRect, itemDrawRect);
+    ThebesLayerData* accumulateIntoThebesLayerData = nullptr;
 
     LayerState layerState = item->GetLayerState(mBuilder, mManager, mParameters);
     if (layerState == LAYER_INACTIVE &&
         nsDisplayItem::ForceActiveLayers()) {
       layerState = LAYER_ACTIVE;
     }
 
     bool forceInactive;
@@ -2697,41 +2693,60 @@ ContainerState::ProcessDisplayItems(nsDi
        * part of a ThebesLayer.
        */
       nsAutoPtr<nsDisplayItemGeometry> dummy;
       mLayerBuilder->AddLayerDisplayItem(ownLayer, item,
                                          itemClip, layerState,
                                          topLeft, nullptr,
                                          dummy);
     } else {
-      ThebesLayerData* data =
+      ThebesLayerData* thebesLayerData =
         FindThebesLayerFor(item, itemVisibleRect, animatedGeometryRoot, topLeft,
                            shouldFixToViewport);
 
       if (itemType == nsDisplayItem::TYPE_LAYER_EVENT_REGIONS) {
         nsDisplayLayerEventRegions* eventRegions =
             static_cast<nsDisplayLayerEventRegions*>(item);
-        data->AccumulateEventRegions(eventRegions->HitRegion(),
-                                     eventRegions->MaybeHitRegion(),
-                                     eventRegions->DispatchToContentHitRegion());
+        thebesLayerData->AccumulateEventRegions(eventRegions->HitRegion(),
+                                                eventRegions->MaybeHitRegion(),
+                                                eventRegions->DispatchToContentHitRegion());
       } else {
         // check to see if the new item has rounded rect clips in common with
         // other items in the layer
-        data->UpdateCommonClipCount(itemClip);
-        data->Accumulate(this, item, itemVisibleRect, itemDrawRect, itemClip);
+        thebesLayerData->UpdateCommonClipCount(itemClip);
 
         nsAutoPtr<nsDisplayItemGeometry> geometry(item->AllocateGeometry(mBuilder));
-        InvalidateForLayerChange(item, data->mLayer, itemClip, topLeft, geometry);
-
-        mLayerBuilder->AddThebesDisplayItem(data, item, itemClip,
+        InvalidateForLayerChange(item, thebesLayerData->mLayer, itemClip, topLeft, geometry);
+
+        mLayerBuilder->AddThebesDisplayItem(thebesLayerData, item, itemClip,
                                             mContainerFrame,
                                             layerState, topLeft,
                                             geometry);
+        accumulateIntoThebesLayerData = thebesLayerData;
       }
     }
+
+    bool snapOpaque;
+    nsRegion opaque = item->GetOpaqueRegion(mBuilder, &snapOpaque);
+    nsRegion opaqueClipped;
+    if (!opaque.IsEmpty()) {
+      nsRegionRectIterator iter(opaque);
+      for (const nsRect* r = iter.Next(); r; r = iter.Next()) {
+        opaqueClipped.Or(opaqueClipped, itemClip.ApproximateIntersectInward(*r));
+      }
+      if (opaqueClipped.Contains(mContainerBounds)) {
+        aList->SetIsOpaque();
+      }
+    }
+    if (accumulateIntoThebesLayerData) {
+      nsIntRegion opaquePixels =
+          ScaleRegionToInsidePixels(opaqueClipped, snapOpaque);
+      accumulateIntoThebesLayerData->Accumulate(this, item,
+          opaquePixels, itemVisibleRect, itemDrawRect, itemClip);
+    }
   }
 
   aList->AppendToTop(&savedItems);
 }
 
 void
 ContainerState::InvalidateForLayerChange(nsDisplayItem* aItem,
                                          Layer* aNewLayer,
@@ -3129,16 +3144,19 @@ ContainerState::CollectOldLayers()
 
 void
 ContainerState::Finish(uint32_t* aTextContentFlags, LayerManagerData* aData, bool& aHasComponentAlphaChildren)
 {
   while (!mThebesLayerDataStack.IsEmpty()) {
     PopThebesLayerData();
   }
 
+  NS_ASSERTION(mContainerBounds.IsEqualInterior(mAccumulatedChildBounds),
+               "Bounds computation mismatch");
+
   uint32_t textContentFlags = 0;
 
   // Make sure that current/existing layers are added to the parent and are
   // in the correct order.
   Layer* layer = nullptr;
   for (uint32_t i = 0; i < mNewChildLayers.Length(); ++i) {
     Layer* prevChild = i == 0 ? nullptr : mNewChildLayers[i - 1].get();
     layer = mNewChildLayers[i];
@@ -3472,38 +3490,38 @@ FrameLayerBuilder::BuildContainerLayerFo
     } else {
       StoreDataForFrame(aContainerFrame, containerDisplayItemKey, containerLayer, LAYER_ACTIVE);
     }
   }
 
   LayerManagerData* data = static_cast<LayerManagerData*>
     (aManager->GetUserData(&gLayerManagerUserData));
 
-  nsRect bounds;
+  nsRect bounds = aChildren->GetBounds(aBuilder);
   nsIntRect pixBounds;
   int32_t appUnitsPerDevPixel;
   uint32_t stateFlags = 0;
   if ((aContainerFrame->GetStateBits() & NS_FRAME_NO_COMPONENT_ALPHA) &&
       mRetainingManager && mRetainingManager->ShouldAvoidComponentAlphaLayers()) {
     stateFlags = ContainerState::NO_COMPONENT_ALPHA;
   }
   uint32_t flags;
   while (true) {
     ContainerState state(aBuilder, aManager, aManager->GetLayerBuilder(),
-                         aContainerFrame, aContainerItem,
+                         aContainerFrame, aContainerItem, bounds,
                          containerLayer, scaleParameters);
 
     state.ProcessDisplayItems(aChildren, stateFlags);
 
     // Set CONTENT_COMPONENT_ALPHA if any of our children have it.
     // This is suboptimal ... a child could have text that's over transparent
     // pixels in its own layer, but over opaque parts of previous siblings.
     bool hasComponentAlphaChildren = false;
     state.Finish(&flags, data, hasComponentAlphaChildren);
-    bounds = state.GetChildrenBounds();
+
     pixBounds = state.ScaleToOutsidePixels(bounds, false);
     appUnitsPerDevPixel = state.GetAppUnitsPerDevPixel();
 
     if (hasComponentAlphaChildren &&
         mRetainingManager &&
         mRetainingManager->ShouldAvoidComponentAlphaLayers() &&
         containerLayer->HasMultipleChildren() &&
         !stateFlags) {
@@ -3517,17 +3535,16 @@ FrameLayerBuilder::BuildContainerLayerFo
       mThebesLayerItems.EnumerateEntries(RestoreThebesLayerItemEntries,
                                          &mContainerLayerGeneration);
       aContainerFrame->AddStateBits(NS_FRAME_NO_COMPONENT_ALPHA);
       continue;
     }
     break;
   }
 
-  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);
   }
 
@@ -3537,17 +3554,17 @@ FrameLayerBuilder::BuildContainerLayerFo
   // when the visible region is a simple rect), so we propogate
   // CONTENT_COMPONENT_ALPHA_DESCENDANT all the way to the root.
   if (flags & Layer::CONTENT_COMPONENT_ALPHA) {
     flags |= Layer::CONTENT_COMPONENT_ALPHA_DESCENDANT;
   }
 
   // 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 and add CONTENT_OPAQUE instead.
       flags &= ~Layer::CONTENT_COMPONENT_ALPHA;
       flags |= Layer::CONTENT_OPAQUE;
     }
   }
   containerLayer->SetContentFlags(flags);
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -1699,16 +1699,20 @@ public:
   void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
                nsDisplayItem::HitTestState* aState,
                nsTArray<nsIFrame*> *aOutFrames) const;
 
 #if defined(DEBUG) || defined(MOZ_DUMP_PAINTING)
   bool DidComputeVisibility() const { return mDidComputeVisibility; }
 #endif
 
+  void SetIsOpaque()
+  {
+    mIsOpaque = true;
+  }
   nsRect GetVisibleRect() const { return mVisibleRect; }
 
 private:
   // This class is only used on stack, so we don't have to worry about leaking
   // it.  Don't let us be heap-allocated!
   void* operator new(size_t sz) CPP_THROW_NEW;
   
   // Utility function used to massage the list during ComputeVisibility.