Bug 805343. Identify DisplayItemDatas that are completely hidden by other opaque content in their ThebesLayer, and ignore them when we check to see whether there's an image we might need to invalidate. r=mattwoodrow
authorRobert O'Callahan <robert@ocallahan.org>
Mon, 10 Dec 2012 18:47:33 +1300
changeset 124583 e8f5182d94c57218bdb26a19f74350dbe540686b
parent 124582 a4c7e07abd65e69904191cf4d33a66b588077ecc
child 124584 3ba90f85653a9d69fa167adbe5dade6aaecb9023
push id2151
push userlsblakk@mozilla.com
push dateTue, 19 Feb 2013 18:06:57 +0000
treeherdermozilla-beta@4952e88741ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow
bugs805343
milestone20.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 805343. Identify DisplayItemDatas that are completely hidden by other opaque content in their ThebesLayer, and ignore them when we check to see whether there's an image we might need to invalidate. r=mattwoodrow
layout/base/FrameLayerBuilder.cpp
layout/base/FrameLayerBuilder.h
layout/style/ImageLoader.cpp
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -44,16 +44,17 @@ FrameLayerBuilder::DisplayItemData::Disp
 
   : mParent(aParent)
   , mLayer(aLayer)
   , mDisplayItemKey(aKey)
   , mContainerLayerGeneration(aGeneration)
   , mLayerState(aLayerState)
   , mUsed(true)
   , mIsInvalid(false)
+  , mIsVisible(true)
 {
 }
 
 FrameLayerBuilder::DisplayItemData::DisplayItemData(DisplayItemData &toCopy)
 {
   // This isn't actually a copy-constructor; notice that it steals toCopy's
   // mGeometry pointer.  Be careful.
   mParent = toCopy.mParent;
@@ -61,16 +62,17 @@ FrameLayerBuilder::DisplayItemData::Disp
   mInactiveManager = toCopy.mInactiveManager;
   mFrameList = toCopy.mFrameList;
   mGeometry = toCopy.mGeometry;
   mDisplayItemKey = toCopy.mDisplayItemKey;
   mClip = toCopy.mClip;
   mContainerLayerGeneration = toCopy.mContainerLayerGeneration;
   mLayerState = toCopy.mLayerState;
   mUsed = toCopy.mUsed;
+  mIsVisible = toCopy.mIsVisible;
 }
 
 void
 FrameLayerBuilder::DisplayItemData::AddFrame(nsIFrame* aFrame)
 {
   mFrameList.AppendElement(aFrame);
 
   nsTArray<DisplayItemData*> *array = 
@@ -1110,27 +1112,28 @@ FrameLayerBuilder::HasRetainedDataFor(ns
         return true;
       }
     }
   }
   return false;
 }
 
 void
-FrameLayerBuilder::IterateRetainedDataFor(nsIFrame* aFrame, DisplayItemDataCallback aCallback)
+FrameLayerBuilder::IterateVisibleRetainedDataFor(nsIFrame* aFrame, DisplayItemDataCallback aCallback)
 {
   nsTArray<DisplayItemData*> *array = 
     reinterpret_cast<nsTArray<DisplayItemData*>*>(aFrame->Properties().Get(LayerManagerDataProperty()));
   if (!array) {
     return;
   }
   
   for (uint32_t i = 0; i < array->Length(); i++) {
     DisplayItemData* data = array->ElementAt(i);
-    if (data->mDisplayItemKey != nsDisplayItem::TYPE_ZERO) {
+    if (data->mDisplayItemKey != nsDisplayItem::TYPE_ZERO &&
+        data->IsVisibleInLayer()) {
       aCallback(aFrame, data);
     }
   }
 }
 
 FrameLayerBuilder::DisplayItemData*
 FrameLayerBuilder::GetOldLayerForFrame(nsIFrame* aFrame, uint32_t aDisplayItemKey)
 {
@@ -1836,17 +1839,17 @@ ContainerState::ThebesLayerData::Accumul
   }
   
   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.ApproximateIntersect(*r));
+      opaqueClipped.Or(opaqueClipped, aClip.ApproximateIntersectInner(*r));
     }
 
     nsIntRegion opaquePixels = aState->ScaleRegionToInsidePixels(opaqueClipped, snap);
 
     nsIntRegionRectIterator iter2(opaquePixels);
     for (const nsIntRect* r = iter2.Next(); r; r = iter2.Next()) {
       // We don't use SimplifyInward here since it's not defined exactly
       // what it will discard. For our purposes the most important case
@@ -2451,17 +2454,18 @@ FrameLayerBuilder::AddThebesDisplayItem(
 
     if (hasClip) {
       intClip = clip.GetBounds().ScaleToOutsidePixels(thebesData->mXScale, 
                                                       thebesData->mYScale, 
                                                       thebesData->mAppUnitsPerDevPixel);
     }
   }
 
-  AddLayerDisplayItem(aLayer, aItem, aClip, aLayerState, aTopLeft, tempManager, aGeometry);
+  DisplayItemData* displayItemData =
+    AddLayerDisplayItem(aLayer, aItem, aClip, aLayerState, aTopLeft, tempManager, aGeometry);
 
   ThebesLayerItemsEntry* entry = mThebesLayerItems.PutEntry(aLayer);
   if (entry) {
     entry->mContainerLayerFrame = aContainerLayerFrame;
     if (entry->mContainerLayerGeneration == 0) {
       entry->mContainerLayerGeneration = mContainerLayerGeneration;
     }
     NS_ASSERTION(aItem->GetUnderlyingFrame(), "Must have frame");
@@ -2518,17 +2522,17 @@ FrameLayerBuilder::AddThebesDisplayItem(
         }
 
         invalid.ScaleRoundOut(thebesData->mXScale, thebesData->mYScale);
         InvalidatePostTransformRegion(aLayer, invalid,
                                       GetTranslationForThebesLayer(aLayer));
       }
     }
     ClippedDisplayItem* cdi =
-      entry->mItems.AppendElement(ClippedDisplayItem(aItem, aClip,
+      entry->mItems.AppendElement(ClippedDisplayItem(aItem, displayItemData, aClip,
                                                      mContainerLayerGeneration));
     cdi->mInactiveLayerManager = tempManager;
   }
 }
 
 FrameLayerBuilder::DisplayItemData*
 FrameLayerBuilder::StoreDataForFrame(nsDisplayItem* aItem, Layer* aLayer, LayerState aState)
 {
@@ -2593,35 +2597,36 @@ FrameLayerBuilder::ClippedDisplayItem::~
     BasicLayerManager* basic = static_cast<BasicLayerManager*>(mInactiveLayerManager.get());
     if (basic->InTransaction()) {
       basic->EndTransaction(nullptr, nullptr);
     }
     basic->SetUserData(&gLayerManagerLayerBuilder, nullptr);
   }
 }
 
-void
+FrameLayerBuilder::DisplayItemData*
 FrameLayerBuilder::AddLayerDisplayItem(Layer* aLayer,
                                        nsDisplayItem* aItem,
                                        const Clip& aClip,
                                        LayerState aLayerState,
                                        const nsPoint& aTopLeft,
                                        LayerManager* aManager,
                                        nsAutoPtr<nsDisplayItemGeometry> aGeometry)
 {
   if (aLayer->Manager() != mRetainingManager)
-    return;
+    return nullptr;
 
   DisplayItemData *data = StoreDataForFrame(aItem, aLayer, aLayerState);
   ThebesLayer *t = aLayer->AsThebesLayer();
   if (t) {
     data->mGeometry = aGeometry;
     data->mClip = aClip;
   }
   data->mInactiveManager = aManager;
+  return data;
 }
 
 nsIntPoint
 FrameLayerBuilder::GetLastPaintOffset(ThebesLayer* aLayer)
 {
   ThebesLayerItemsEntry* entry = mThebesLayerItems.PutEntry(aLayer);
   if (entry) {
     if (entry->mContainerLayerGeneration == 0) {
@@ -3260,77 +3265,95 @@ FrameLayerBuilder::DrawThebesLayer(Thebe
   // See above for why this is OK.
   aContext->Translate(aLayer->GetResidualTranslation() - gfxPoint(offset.x, offset.y));
   aContext->Scale(userData->mXScale, userData->mYScale);
 
   nsPresContext* presContext = containerLayerFrame->PresContext();
   int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
 
   uint32_t i;
-  // Update visible regions. We need perform visibility analysis again
-  // because we may be asked to draw into part of a ThebesLayer that
-  // isn't actually visible in the window (e.g., because a ThebesLayer
-  // expanded its visible region to a rectangle internally), in which
-  // case the mVisibleRect stored in the display item may be wrong.
-  nsRegion visible = aRegionToDraw.ToAppUnits(appUnitsPerDevPixel);
+  // Update visible rects in display items to reflect visibility
+  // just considering items in this ThebesLayer. This is different from the
+  // original visible rects, which describe which part of each item
+  // is visible in the window. These can be larger than the original
+  // visible rect, because we may be asked to draw into part of a
+  // ThebesLayer that isn't actually visible in the window (e.g.,
+  // because a ThebesLayer expanded its visible region to a rectangle
+  // internally, or because we're caching prerendered content).
+  // We also compute the intersection of those visible rects with
+  // aRegionToDraw. These are the rectangles of each display item
+  // that actually need to be drawn now.
+  // Treat as visible everything this layer already contains or will
+  // contain.
+  nsIntRegion layerRegion;
+  layerRegion.Or(aLayer->GetValidRegion(), aRegionToDraw);
+  nsRegion visible = layerRegion.ToAppUnits(appUnitsPerDevPixel);
   visible.MoveBy(NSIntPixelsToAppUnits(offset.x, appUnitsPerDevPixel),
                  NSIntPixelsToAppUnits(offset.y, appUnitsPerDevPixel));
   visible.ScaleInverseRoundOut(userData->mXScale, userData->mYScale);
+  nsRegion toDraw = aRegionToDraw.ToAppUnits(appUnitsPerDevPixel);
+  toDraw.MoveBy(NSIntPixelsToAppUnits(offset.x, appUnitsPerDevPixel),
+                NSIntPixelsToAppUnits(offset.y, appUnitsPerDevPixel));
+  toDraw.ScaleInverseRoundOut(userData->mXScale, userData->mYScale);
 
   for (i = items.Length(); i > 0; --i) {
     ClippedDisplayItem* cdi = &items[i - 1];
 
     NS_ASSERTION(AppUnitsPerDevPixel(cdi->mItem) == appUnitsPerDevPixel,
                  "a thebes layer should contain items only at the same zoom");
 
     NS_ABORT_IF_FALSE(cdi->mClip.mHaveClipRect ||
                       cdi->mClip.mRoundedClipRects.IsEmpty(),
                       "If we have rounded rects, we must have a clip rect");
 
     if (!cdi->mClip.mHaveClipRect ||
         (cdi->mClip.mRoundedClipRects.IsEmpty() &&
          cdi->mClip.mClipRect.Contains(visible.GetBounds()))) {
       cdi->mItem->RecomputeVisibility(builder, &visible);
-      continue;
-    }
-
-    // Do a little dance to account for the fact that we're clipping
-    // to cdi->mClipRect
-    nsRegion clipped;
-    clipped.And(visible, cdi->mClip.mClipRect);
-    nsRegion finalClipped = clipped;
-    cdi->mItem->RecomputeVisibility(builder, &finalClipped);
-    // If we have rounded clip rects, don't subtract from the visible
-    // region since we aren't displaying everything inside the rect.
-    if (cdi->mClip.mRoundedClipRects.IsEmpty()) {
-      nsRegion removed;
-      removed.Sub(clipped, finalClipped);
-      nsRegion newVisible;
-      newVisible.Sub(visible, removed);
-      // Don't let the visible region get too complex.
-      if (newVisible.GetNumRects() <= 15) {
-        visible = newVisible;
+    } else {
+      // Do a little dance to account for the fact that we're clipping
+      // to cdi->mClipRect
+      nsRegion clipped;
+      clipped.And(visible, cdi->mClip.mClipRect);
+      nsRegion finalClipped = clipped;
+      cdi->mItem->RecomputeVisibility(builder, &finalClipped);
+      // If we have rounded clip rects, don't subtract from the visible
+      // region since we aren't displaying everything inside the rect.
+      if (cdi->mClip.mRoundedClipRects.IsEmpty()) {
+        nsRegion removed;
+        removed.Sub(clipped, finalClipped);
+        nsRegion newVisible;
+        newVisible.Sub(visible, removed);
+        // Don't let the visible region get too complex.
+        if (newVisible.GetNumRects() <= 15) {
+          visible = newVisible;
+        }
       }
-    }
-    if (!cdi->mClip.IsRectClippedByRoundedCorner(cdi->mItem->GetVisibleRect())) {
-      cdi->mClip.RemoveRoundedCorners();
+      if (!cdi->mClip.IsRectClippedByRoundedCorner(cdi->mItem->GetVisibleRect())) {
+        cdi->mClip.RemoveRoundedCorners();
+      }
     }
   }
 
   nsRefPtr<nsRenderingContext> rc = new nsRenderingContext();
   rc->Init(presContext->DeviceContext(), aContext);
 
   Clip currentClip;
   bool setClipRect = false;
 
   for (i = 0; i < items.Length(); ++i) {
     ClippedDisplayItem* cdi = &items[i];
 
-    if (cdi->mItem->GetVisibleRect().IsEmpty())
+    if (cdi->mData) {
+      cdi->mData->SetIsVisibleInLayer(!cdi->mItem->GetVisibleRect().IsEmpty());
+    }
+
+    if (!toDraw.Intersects(cdi->mItem->GetVisibleRect())) {
       continue;
+    }
 
     // If the new desired clip state is different from the current state,
     // update the clip.
     if (setClipRect != cdi->mClip.mHaveClipRect ||
         (cdi->mClip.mHaveClipRect && cdi->mClip != currentClip)) {
       if (setClipRect) {
         aContext->Restore();
       }
@@ -3498,17 +3521,17 @@ FrameLayerBuilder::Clip::AddRoundedRectP
   clip.Round();
   clip.Condition();
 
   aContext->NewPath();
   aContext->RoundedRectangle(clip, pixelRadii);
 }
 
 nsRect
-FrameLayerBuilder::Clip::ApproximateIntersect(const nsRect& aRect) const
+FrameLayerBuilder::Clip::ApproximateIntersectInner(const nsRect& aRect) const
 {
   nsRect r = aRect;
   if (mHaveClipRect) {
     r.IntersectRect(r, mClipRect);
   }
   for (uint32_t i = 0, iEnd = mRoundedClipRects.Length();
        i < iEnd; ++i) {
     const Clip::RoundedRect &rr = mRoundedClipRects[i];
--- a/layout/base/FrameLayerBuilder.h
+++ b/layout/base/FrameLayerBuilder.h
@@ -91,16 +91,17 @@ public:
  */
 class FrameLayerBuilder : public layers::LayerUserData {
 public:
   typedef layers::ContainerLayer ContainerLayer;
   typedef layers::Layer Layer;
   typedef layers::ThebesLayer ThebesLayer;
   typedef layers::ImageLayer ImageLayer;
   typedef layers::LayerManager LayerManager;
+  class DisplayItemData;
 
   FrameLayerBuilder() :
     mRetainingManager(nullptr),
     mDetectedDOMModification(false),
     mInvalidateAllLayers(false),
     mContainerLayerGeneration(0),
     mMaxContainerLayerGeneration(0)
   {
@@ -258,23 +259,23 @@ public:
    * @param aLayer Layer that the display item will be rendered into
    * @param aItem Display item to be drawn.
    * @param aLayerState What LayerState the item is using.
    * @param aTopLeft offset from active scrolled root to reference frame
    * @param aManager If the layer is in the LAYER_INACTIVE state,
    * then this is the temporary layer manager to draw with.
    */
   struct Clip;
-  void AddLayerDisplayItem(Layer* aLayer,
-                           nsDisplayItem* aItem,
-                           const Clip& aClip,
-                           LayerState aLayerState,
-                           const nsPoint& aTopLeft,
-                           LayerManager* aManager,
-                           nsAutoPtr<nsDisplayItemGeometry> aGeometry);
+  DisplayItemData* AddLayerDisplayItem(Layer* aLayer,
+                                       nsDisplayItem* aItem,
+                                       const Clip& aClip,
+                                       LayerState aLayerState,
+                                       const nsPoint& aTopLeft,
+                                       LayerManager* aManager,
+                                       nsAutoPtr<nsDisplayItemGeometry> aGeometry);
 
   /**
    * Record aItem as a display item that is rendered by the ThebesLayer
    * aLayer, with aClipRect, where aContainerLayerFrame is the frame
    * for the container layer this ThebesItem belongs to.
    * aItem must have an underlying frame.
    * @param aTopLeft offset from active scrolled root to reference frame
    */
@@ -314,20 +315,19 @@ public:
   LayerManager* GetRetainingLayerManager() { return mRetainingManager; }
 
   /**
    * Returns true if the given display item was rendered during the previous
    * paint. Returns false otherwise.
    */
   static bool HasRetainedDataFor(nsIFrame* aFrame, uint32_t aDisplayItemKey);
 
-  class DisplayItemData;
   typedef void (*DisplayItemDataCallback)(nsIFrame *aFrame, DisplayItemData* aItem);
 
-  static void IterateRetainedDataFor(nsIFrame* aFrame, DisplayItemDataCallback aCallback);
+  static void IterateVisibleRetainedDataFor(nsIFrame* aFrame, DisplayItemDataCallback aCallback);
 
   /**
    * Save transform that was in aLayer when we last painted, and the position
    * of the active scrolled root frame. It must be an integer
    * translation.
    */
   void SaveLastPaintOffset(ThebesLayer* aLayer);
   /**
@@ -412,17 +412,17 @@ public:
                             uint32_t aBegin, uint32_t aEnd) const;
     // 'Draw' (create as a path, does not stroke or fill) aRoundRect to aContext
     void AddRoundedRectPathTo(gfxContext* aContext, int32_t A2D,
                               const RoundedRect &aRoundRect) const;
 
     // Return a rectangle contained in the intersection of aRect with this
     // clip region. Tries to return the largest possible rectangle, but may
     // not succeed.
-    nsRect ApproximateIntersect(const nsRect& aRect) const;
+    nsRect ApproximateIntersectInner(const nsRect& aRect) const;
 
     // Returns false if aRect is definitely not clipped by a rounded corner in
     // this clip. Returns true if aRect is clipped by a rounded corner in this
     // clip or it can not be quickly determined that it is not clipped by a
     // rounded corner in this clip.
     bool IsRectClippedByRoundedCorner(const nsRect& aRect) const;
 
     // Intersection of all rects in this clip ignoring any rounded corners.
@@ -472,16 +472,19 @@ public:
    */
   class DisplayItemData {
   public:
     friend class FrameLayerBuilder;
 
     uint32_t GetDisplayItemKey() { return mDisplayItemKey; }
     Layer* GetLayer() { return mLayer; }
     void Invalidate() { mIsInvalid = true; }
+    bool IsVisibleInLayer() { return mIsVisible; }
+    void SetIsVisibleInLayer(bool aIsVisible) { mIsVisible = aIsVisible; }
+
   protected:
 
     DisplayItemData(LayerManagerData* aParent, uint32_t aKey, Layer* aLayer, LayerState aLayerState, uint32_t aGeneration);
     DisplayItemData(DisplayItemData &toCopy);
 
     /**
      * Removes any references to this object from frames
      * in mFrameList.
@@ -520,17 +523,25 @@ public:
     uint32_t        mContainerLayerGeneration;
     LayerState      mLayerState;
 
     /**
      * Used to track if data currently stored in mFramesWithLayers (from an existing
      * paint) has been updated in the current paint.
      */
     bool            mUsed;
+    /**
+     * True if the entire display item needs to be invalidated.
+     */
     bool            mIsInvalid;
+    /**
+     * True if the display item is visible in its layer, otherwise
+     * it's completely covered by opaque content in its ThebesLayer.
+     */
+    bool            mIsVisible;
   };
 
 protected:
 
   friend class LayerManagerData;
 
   static void RemoveFrameFromLayerManager(nsIFrame* aFrame, void* aPropertyValue);
 
@@ -587,24 +598,27 @@ protected:
    * of ClippedDisplayItems. (ThebesLayerItemsEntry is the hash entry
    * for that hashtable.)
    * These are only stored during the paint process, so that the
    * DrawThebesLayer callback can figure out which items to draw for the
    * ThebesLayer.
    * mItem always has an underlying frame.
    */
   struct ClippedDisplayItem {
-    ClippedDisplayItem(nsDisplayItem* aItem, const Clip& aClip, uint32_t aGeneration)
-      : mItem(aItem), mClip(aClip), mContainerLayerGeneration(aGeneration)
+    ClippedDisplayItem(nsDisplayItem* aItem, DisplayItemData* aData,
+                       const Clip& aClip, uint32_t aGeneration)
+      : mItem(aItem), mData(aData), mClip(aClip),
+        mContainerLayerGeneration(aGeneration)
     {
     }
 
     ~ClippedDisplayItem();
 
     nsDisplayItem* mItem;
+    DisplayItemData* mData;
 
     /**
      * If the display item is being rendered as an inactive
      * layer, then this stores the layer manager being
      * used for the inactive transaction.
      */
     nsRefPtr<LayerManager> mInactiveLayerManager;
 
--- a/layout/style/ImageLoader.cpp
+++ b/layout/style/ImageLoader.cpp
@@ -336,17 +336,17 @@ ImageLoader::DoRedraw(FrameSet* aFrameSe
   NS_ASSERTION(aFrameSet, "Must have a frame set");
   NS_ASSERTION(mDocument, "Should have returned earlier!");
 
   FrameSet::size_type length = aFrameSet->Length();
   for (FrameSet::size_type i = 0; i < length; i++) {
     nsIFrame* frame = aFrameSet->ElementAt(i);
 
     if (frame->GetStyleVisibility()->IsVisible()) {
-      FrameLayerBuilder::IterateRetainedDataFor(frame, InvalidateImagesCallback);
+      FrameLayerBuilder::IterateVisibleRetainedDataFor(frame, InvalidateImagesCallback);
     }
   }
 }
 
 NS_IMPL_ADDREF(ImageLoader)
 NS_IMPL_RELEASE(ImageLoader)
 
 NS_INTERFACE_MAP_BEGIN(ImageLoader)