Bug 539356 - Part 25 - Invalidate display items that have a changed clip. r=roc
authorMatt Woodrow <mwoodrow@mozilla.com>
Sat, 30 Jun 2012 15:06:13 +1200
changeset 98027 f7599b247eef46459e794f3a9661dec6fbbf7acc
parent 98026 6e8c5c011767c7d7e5072dc27cb192750ce347c0
child 98028 d14ec506f28f79cbf3b68578e814db1f1fbd2add
push id23017
push userryanvm@gmail.com
push dateSat, 30 Jun 2012 19:29:24 +0000
treeherdermozilla-central@4c2ddc60f360 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs539356
milestone16.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 539356 - Part 25 - Invalidate display items that have a changed clip. r=roc
layout/base/FrameLayerBuilder.cpp
layout/base/FrameLayerBuilder.h
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -328,17 +328,17 @@ protected:
    * available for recycling.
    */
   void CollectOldLayers();
   /**
    * If aItem used to belong to a ThebesLayer, invalidates the area of
    * aItem in that layer. If aNewLayer is a ThebesLayer, invalidates the area of
    * aItem in that layer.
    */
-  void InvalidateForLayerChange(nsDisplayItem* aItem, Layer* aNewLayer);
+  void InvalidateForLayerChange(nsDisplayItem* aItem, Layer* aNewLayer, const FrameLayerBuilder::Clip& aClip);
   /**
    * Try to determine whether the ThebesLayer at aThebesLayerIndex
    * has a single opaque color behind it, over the entire bounds of its visible
    * region.
    * If successful, return that color, otherwise return NS_RGBA(0,0,0,0).
    */
   nscolor FindOpaqueBackgroundColorFor(PRInt32 aThebesLayerIndex);
   /**
@@ -945,17 +945,18 @@ FrameLayerBuilder::HasRetainedLayerFor(n
       // All layer managers with our user data are retained layer managers
       return true;
     }
   }
   return false;
 }
 
 Layer*
-FrameLayerBuilder::GetOldLayerFor(nsIFrame* aFrame, PRUint32 aDisplayItemKey, nsDisplayItemGeometry** aOldGeometry)
+FrameLayerBuilder::GetOldLayerFor(nsIFrame* aFrame, PRUint32 aDisplayItemKey, 
+                                  nsDisplayItemGeometry** aOldGeometry, Clip** aOldClip)
 {
   // If we need to build a new layer tree, then just refuse to recycle
   // anything.
   if (!mRetainingManager || mInvalidateAllLayers)
     return nsnull;
 
   nsTArray<DisplayItemData> *array = GetDisplayItemDataArrayForFrame(aFrame);
   if (!array)
@@ -963,16 +964,19 @@ FrameLayerBuilder::GetOldLayerFor(nsIFra
 
   for (PRUint32 i = 0; i < array->Length(); ++i) {
     if (array->ElementAt(i).mDisplayItemKey == aDisplayItemKey) {
       Layer* layer = array->ElementAt(i).mLayer;
       if (layer->Manager() == mRetainingManager) {
         if (aOldGeometry) {
           *aOldGeometry = array->ElementAt(i).mGeometry.get();
         }
+        if (aOldClip) {
+          *aOldClip = &array->ElementAt(i).mClip;
+        }
         return layer;
       }
     }
   }
   return nsnull;
 }
 
 /* static */ Layer*
@@ -1861,24 +1865,24 @@ ContainerState::ProcessDisplayItems(cons
                    itemVisibleRect.IsEmpty(),
                    "State is LAYER_ACTIVE_EMPTY but visible rect is not.");
 
       // If the item would have its own layer but is invisible, just hide it.
       // Note that items without their own layers can't be skipped this
       // way, since their ThebesLayer may decide it wants to draw them
       // into its buffer even if they're currently covered.
       if (itemVisibleRect.IsEmpty() && layerState != LAYER_ACTIVE_EMPTY) {
-        InvalidateForLayerChange(item, nsnull);
+        InvalidateForLayerChange(item, nsnull, aClip);
         continue;
       }
 
       // Just use its layer.
       nsRefPtr<Layer> ownLayer = item->BuildLayer(mBuilder, mManager, mParameters);
       if (!ownLayer) {
-        InvalidateForLayerChange(item, ownLayer);
+        InvalidateForLayerChange(item, ownLayer, aClip);
         continue;
       }
 
       if (item->IsInvalid()) {
         ownLayer->SetInvalidRectToVisibleRegion();
       }
 
       // If it's not a ContainerLayer, we need to apply the scale transform
@@ -1927,52 +1931,53 @@ ContainerState::ProcessDisplayItems(cons
 
       ContainerLayer* oldContainer = ownLayer->GetParent();
       if (oldContainer && oldContainer != mContainerLayer) {
         oldContainer->RemoveChild(ownLayer);
       }
       NS_ASSERTION(!mNewChildLayers.Contains(ownLayer),
                    "Layer already in list???");
 
-      InvalidateForLayerChange(item, ownLayer);
+      InvalidateForLayerChange(item, ownLayer, aClip);
 
       mNewChildLayers.AppendElement(ownLayer);
-      mLayerBuilder->AddLayerDisplayItem(ownLayer, item, layerState, nsnull);
+      mLayerBuilder->AddLayerDisplayItem(ownLayer, item, aClip, layerState, nsnull);
     } else {
       ThebesLayerData* data =
         FindThebesLayerFor(item, itemVisibleRect, itemDrawRect, aClip,
                            activeScrolledRoot);
 
       data->mLayer->SetIsFixedPosition(
         !nsLayoutUtils::IsScrolledByRootContentDocumentDisplayportScrolling(
                                        activeScrolledRoot, mBuilder));
 
-      InvalidateForLayerChange(item, data->mLayer);
+      InvalidateForLayerChange(item, data->mLayer, aClip);
 
       mLayerBuilder->AddThebesDisplayItem(data->mLayer, item, aClip,
                                           mContainerFrame,
                                           layerState);
 
       // check to see if the new item has rounded rect clips in common with
       // other items in the layer
       data->UpdateCommonClipCount(aClip);
     }
   }
 }
 
 void
-ContainerState::InvalidateForLayerChange(nsDisplayItem* aItem, Layer* aNewLayer)
+ContainerState::InvalidateForLayerChange(nsDisplayItem* aItem, Layer* aNewLayer, const FrameLayerBuilder::Clip& aClip)
 {
   nsIFrame* f = aItem->GetUnderlyingFrame();
   NS_ASSERTION(f, "Display items that render using Thebes must have a frame");
   PRUint32 key = aItem->GetPerFrameKey();
   NS_ASSERTION(key, "Display items that render using Thebes must have a key");
   nsDisplayItemGeometry *oldGeometry = NULL;
+  FrameLayerBuilder::Clip* oldClip = NULL;
   nsAutoPtr<nsDisplayItemGeometry> geometry(aItem->AllocateGeometry(mBuilder));
-  Layer* oldLayer = mLayerBuilder->GetOldLayerFor(f, key, &oldGeometry);
+  Layer* oldLayer = mLayerBuilder->GetOldLayerFor(f, key, &oldGeometry, &oldClip);
   if (aNewLayer != oldLayer && oldLayer) {
     // The item has changed layers.
     // Invalidate the bounds in the old layer and new layer.
     // The bounds might have changed, but we assume that any difference
     // in the bounds will have been invalidated for all Thebes layers
     // in the container via regular frame invalidation.
     ThebesLayer* t = oldLayer->AsThebesLayer();
     if (t) {
@@ -2010,27 +2015,33 @@ ContainerState::InvalidateForLayerChange
   }
 
   ThebesDisplayItemLayerUserData* data =
     static_cast<ThebesDisplayItemLayerUserData*>(newThebesLayer->GetUserData(&gThebesDisplayItemLayerUserData));
   // If the frame is marked as invalidated then we want to invalidate both the old and new bounds,
   // otherwise we only want to invalidate the changed areas.
   nsRegion combined;
   if (!oldLayer) {
+    // This item is being added for the first time, invalidate its entire area.
     //TODO: We call GetGeometry again in AddThebesDisplayItem, we should reuse this.
     combined = geometry->ComputeInvalidationRegion();
 #ifdef DEBUG_INVALIDATIONS
     printf("Display item type %s(%p) added to layer %p!\n", aItem->Name(), f, aNewLayer);
 #endif
-  } else if (aItem->IsInvalid()) {
+  } else if (aItem->IsInvalid() || *oldClip != aClip) {
+    // Either layout marked item as needing repainting, or the clip on it changed, invalidate
+    // the entire old and new areas.
+    // TODO: We could be smarter about handling clip changes here instead of repainting everything.
     combined.Or(geometry->ComputeInvalidationRegion(), oldGeometry->ComputeInvalidationRegion());
 #ifdef DEBUG_INVALIDATIONS
     printf("Display item type %s(%p) (in layer %p) belongs to an invalidated frame!\n", aItem->Name(), f, aNewLayer);
 #endif
   } else {
+    // No obvious differences, so let the display item check for geometry changes and decide what needs to be
+    // repainted.
     ThebesDisplayItemLayerUserData* data =
         static_cast<ThebesDisplayItemLayerUserData*>(newThebesLayer->GetUserData(&gThebesDisplayItemLayerUserData));
     nsIntPoint paintOffset = GetTranslationForThebesLayer(newThebesLayer);
     nsPoint offset((paintOffset.x + data->mActiveScrolledRootPosition.x) * mAppUnitsPerDevPixel / data->mXScale,
                    (paintOffset.y + data->mActiveScrolledRootPosition.y) * mAppUnitsPerDevPixel / data->mYScale);
     nsPoint prevOffset((oldGeometry->mPaintOffset.x + oldGeometry->mActiveScrolledRootPosition.x) * oldGeometry->mAppUnitsPerDevPixel / data->mXScale,
                        (oldGeometry->mPaintOffset.y + oldGeometry->mActiveScrolledRootPosition.y) * oldGeometry->mAppUnitsPerDevPixel / data->mYScale);
     nsPoint shift = offset - prevOffset;
@@ -2086,17 +2097,17 @@ FrameLayerBuilder::AddThebesDisplayItem(
     if (data) {
       tempManager = data->mInactiveManager;
     }
     if (!tempManager) {
       tempManager = new BasicLayerManager();
     }
   }
 
-  AddLayerDisplayItem(aLayer, aItem, aLayerState, tempManager);
+  AddLayerDisplayItem(aLayer, aItem, aClip, aLayerState, tempManager);
 
   ThebesLayerItemsEntry* entry = mThebesLayerItems.PutEntry(aLayer);
   if (entry) {
     entry->mContainerLayerFrame = aContainerLayerFrame;
     entry->mContainerLayerGeneration = mContainerLayerGeneration;
     NS_ASSERTION(aItem->GetUnderlyingFrame(), "Must have frame");
     if (tempManager) {
       FrameLayerBuilder* layerBuilder = new FrameLayerBuilder();
@@ -2168,16 +2179,17 @@ FrameLayerBuilder::ClippedDisplayItem::~
     }
     basic->SetUserData(&gLayerManagerLayerBuilder, nsnull);
   }
 }
 
 void
 FrameLayerBuilder::AddLayerDisplayItem(Layer* aLayer,
                                        nsDisplayItem* aItem,
+                                       const Clip& aClip,
                                        LayerState aLayerState,
                                        LayerManager* aManager)
 {
   if (aLayer->Manager() != mRetainingManager)
     return;
 
   nsIFrame* f = aItem->GetUnderlyingFrame();
   DisplayItemDataEntry* entry = mNewDisplayItemData.PutEntry(f);
@@ -2194,16 +2206,17 @@ FrameLayerBuilder::AddLayerDisplayItem(L
     entry->mContainerLayerGeneration = mContainerLayerGeneration;
     DisplayItemData* data = entry->mData.AppendElement();
     DisplayItemData did(aLayer, aItem->GetPerFrameKey(), aLayerState, mContainerLayerGeneration);
     *data = did;
 
     ThebesLayer *t = aLayer->AsThebesLayer();
     if (t) {
       data->mGeometry = aItem->AllocateGeometry(mDisplayListBuilder);
+      data->mClip = aClip;
       data->mGeometry->mAppUnitsPerDevPixel = AppUnitsPerDevPixel(aItem);
       ThebesDisplayItemLayerUserData* userData =
                     static_cast<ThebesDisplayItemLayerUserData*>(t->GetUserData(&gThebesDisplayItemLayerUserData));
       data->mGeometry->mPaintOffset = GetTranslationForThebesLayer(t);
       data->mGeometry->mActiveScrolledRootPosition = userData->mActiveScrolledRootPosition;
     }
     data->mInactiveManager = aManager;
 
--- a/layout/base/FrameLayerBuilder.h
+++ b/layout/base/FrameLayerBuilder.h
@@ -244,28 +244,29 @@ public:
    * Record aItem as a display item that is rendered by aLayer.
    *
    * @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 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,
                            LayerManager* aManager = nsnull);
 
   /**
    * 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.
    */
-  struct Clip;
   void AddThebesDisplayItem(ThebesLayer* aLayer,
                             nsDisplayItem* aItem,
                             const Clip& aClip,
                             nsIFrame* aContainerLayerFrame,
                             LayerState aLayerState);
 
   /**
    * Set the current top-level LayerManager for the widget being
@@ -310,17 +311,18 @@ public:
 
   /**
    * Given a frame and a display item key that uniquely identifies a
    * display item for the frame, find the layer that was last used to
    * render that display item. Returns null if there is no such layer.
    * This could be a dedicated layer for the display item, or a ThebesLayer
    * that renders many display items.
    */
-  Layer* GetOldLayerFor(nsIFrame* aFrame, PRUint32 aDisplayItemKey, nsDisplayItemGeometry** aOldGeometry = nsnull);
+  Layer* GetOldLayerFor(nsIFrame* aFrame, PRUint32 aDisplayItemKey, 
+                        nsDisplayItemGeometry** aOldGeometry = nsnull, Clip** aOldClip = nsnull);
 
   static Layer* GetDebugOldLayerFor(nsIFrame* aFrame, PRUint32 aDisplayItemKey);
 
   /**
    * If the display item was previously drawn as an inactive layer,
    * then return the layer manager used for the inactive transaction.
    * Returns nsnull if no manager could be found.
    */
@@ -504,16 +506,17 @@ protected:
       mContainerLayerGeneration = toCopy.mContainerLayerGeneration;
       mLayerState = toCopy.mLayerState;
       mUsed = toCopy.mUsed;
     }
 
     nsRefPtr<Layer> mLayer;
     nsRefPtr<LayerManager> mInactiveManager;
     nsAutoPtr<nsDisplayItemGeometry> mGeometry;
+    Clip            mClip;
     PRUint32        mDisplayItemKey;
     PRUint32        mContainerLayerGeneration;
     LayerState      mLayerState;
 
     /**
      * Used to track if data currently stored in mFramesWithLayers (from an existing
      * paint) is also used in the current paint and has an equivalent data object
      * in mNewDisplayItemData.