Bug 770001. When comparing clips, adjust for any change in the ThebesLayer coordinate system. When clips are different, try to accumulate differences intelligently, taking into account that changes in clips outside the bounds of the clipped display item don't matter. r=mattwoodrow
authorRobert O'Callahan <robert@ocallahan.org>
Wed, 29 Aug 2012 17:48:44 +1200
changeset 114753 32c4a08f8e6ec61221fa4816e89d803c2adc4c64
parent 114752 9bf0072dcd72219dc4d8fdbbdc524516c24a25c0
child 114754 5f4c8635e87e4d9637a299600392311196b0a894
push id1708
push userakeybl@mozilla.com
push dateMon, 19 Nov 2012 21:10:21 +0000
treeherdermozilla-beta@27b14fe50103 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow
bugs770001
milestone18.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 770001. When comparing clips, adjust for any change in the ThebesLayer coordinate system. When clips are different, try to accumulate differences intelligently, taking into account that changes in clips outside the bounds of the clipped display item don't matter. r=mattwoodrow
layout/base/FrameLayerBuilder.cpp
layout/base/FrameLayerBuilder.h
layout/base/nsDisplayListInvalidation.h
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -2178,26 +2178,28 @@ ContainerState::InvalidateForLayerChange
       // so it doesn't matter whether we are using the old scale at last paint
       // or a new scale here
 #ifdef DEBUG_INVALIDATIONS
       printf("Display item type %s(%p) changed layers %p to %p!\n", aItem->Name(), f, t, aNewLayer);
 #endif
       ThebesDisplayItemLayerUserData* data =
           static_cast<ThebesDisplayItemLayerUserData*>(t->GetUserData(&gThebesDisplayItemLayerUserData));
       InvalidatePostTransformRegion(t,
-          oldGeometry->ComputeInvalidationRegion().ScaleToOutsidePixels(data->mXScale, data->mYScale, mAppUnitsPerDevPixel),
+          oldGeometry->ComputeInvalidationRegion().
+            ScaleToOutsidePixels(data->mXScale, data->mYScale, mAppUnitsPerDevPixel),
           mLayerBuilder->GetLastPaintOffset(t));
     }
     if (aNewLayer) {
       ThebesLayer* newThebesLayer = aNewLayer->AsThebesLayer();
       if (newThebesLayer) {
         ThebesDisplayItemLayerUserData* data =
             static_cast<ThebesDisplayItemLayerUserData*>(newThebesLayer->GetUserData(&gThebesDisplayItemLayerUserData));
         InvalidatePostTransformRegion(newThebesLayer,
-            geometry->ComputeInvalidationRegion().ScaleToOutsidePixels(data->mXScale, data->mYScale, mAppUnitsPerDevPixel),
+            geometry->ComputeInvalidationRegion().
+              ScaleToOutsidePixels(data->mXScale, data->mYScale, mAppUnitsPerDevPixel),
             GetTranslationForThebesLayer(newThebesLayer));
       }
     }
     return;
   } 
   if (!aNewLayer) {
     return;
   }
@@ -2214,30 +2216,31 @@ ContainerState::InvalidateForLayerChange
   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() || *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.
+  } else if (aItem->IsInvalid()) {
+    // Either layout marked item as needing repainting, invalidate the entire old and new areas.
     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
+    // Let the display item check for geometry changes and decide what needs to be
     // repainted.
     nsPoint shift = aTopLeft - data->mLastActiveScrolledRootOrigin;
     oldGeometry->MoveBy(shift);
     aItem->ComputeInvalidationRegion(mBuilder, oldGeometry, &combined);
+    oldClip->AddOffsetAndComputeDifference(shift, oldGeometry->ComputeInvalidationRegion(),
+                                           aClip, geometry->ComputeInvalidationRegion(),
+                                           &combined);
 #ifdef DEBUG_INVALIDATIONS
     if (!combined.IsEmpty()) {
       printf("Display item type %s(%p) (in layer %p) changed geometry!\n", aItem->Name(), f, aNewLayer);
     }
 #endif
   }
   if (!combined.IsEmpty()) {
     InvalidatePostTransformRegion(newThebesLayer,
@@ -3443,16 +3446,53 @@ FrameLayerBuilder::Clip::RemoveRoundedCo
 {
   if (mRoundedClipRects.IsEmpty())
     return;
 
   mClipRect = NonRoundedIntersection();
   mRoundedClipRects.Clear();
 }
 
+static void
+AccumulateRectDifference(const nsRect& aR1, const nsRect& aR2, nsRegion* aOut)
+{
+  if (aR1.IsEqualInterior(aR2))
+    return;
+  nsRegion r;
+  r.Xor(aR1, aR2);
+  aOut->Or(*aOut, r);
+}
+
+void
+FrameLayerBuilder::Clip::AddOffsetAndComputeDifference(const nsPoint& aOffset,
+                                                       const nsRect& aBounds,
+                                                       const Clip& aOther,
+                                                       const nsRect& aOtherBounds,
+                                                       nsRegion* aDifference)
+{
+  if (mHaveClipRect != aOther.mHaveClipRect ||
+      mRoundedClipRects.Length() != aOther.mRoundedClipRects.Length()) {
+    aDifference->Or(*aDifference, aBounds);
+    aDifference->Or(*aDifference, aOtherBounds);
+    return;
+  }
+  if (mHaveClipRect) {
+    AccumulateRectDifference((mClipRect + aOffset).Intersect(aBounds),
+                             aOther.mClipRect.Intersect(aOtherBounds),
+                             aDifference);
+  }
+  for (uint32_t i = 0; i < mRoundedClipRects.Length(); ++i) {
+    if (mRoundedClipRects[i] + aOffset != aOther.mRoundedClipRects[i]) {
+      // The corners make it tricky so we'll just add both rects here.
+      aDifference->Or(*aDifference, mRoundedClipRects[i].mRect.Intersect(aBounds));
+      aDifference->Or(*aDifference, aOther.mRoundedClipRects[i].mRect.Intersect(aOtherBounds));
+    }
+  }
+}
+
 gfxRect
 CalculateBounds(const nsTArray<FrameLayerBuilder::Clip::RoundedRect>& aRects, int32_t A2D)
 {
   nsRect bounds = aRects[0].mRect;
   for (uint32_t i = 1; i < aRects.Length(); ++i) {
     bounds.UnionRect(bounds, aRects[i].mRect);
    }
  
--- a/layout/base/FrameLayerBuilder.h
+++ b/layout/base/FrameLayerBuilder.h
@@ -408,16 +408,21 @@ public:
    * list of rounded rectangles.
    */
   struct Clip {
     struct RoundedRect {
       nsRect mRect;
       // Indices into mRadii are the NS_CORNER_* constants in nsStyleConsts.h
       nscoord mRadii[8];
 
+      RoundedRect operator+(const nsPoint& aOffset) const {
+        RoundedRect r = *this;
+        r.mRect += aOffset;
+        return r;
+      }
       bool operator==(const RoundedRect& aOther) const {
         if (!mRect.IsEqualInterior(aOther.mRect)) {
           return false;
         }
 
         NS_FOR_CSS_HALF_CORNERS(corner) {
           if (mRadii[corner] != aOther.mRadii[corner]) {
             return false;
@@ -470,16 +475,22 @@ public:
     bool IsRectClippedByRoundedCorner(const nsRect& aRect) const;
 
     // Intersection of all rects in this clip ignoring any rounded corners.
     nsRect NonRoundedIntersection() const;
 
     // Gets rid of any rounded corners in this clip.
     void RemoveRoundedCorners();
 
+    // Adds the difference between Intersect(*this + aPoint, aBounds) and
+    // Intersect(aOther, aOtherBounds) to aDifference.
+    void AddOffsetAndComputeDifference(const nsPoint& aPoint, const nsRect& aBounds,
+                                       const Clip& aOther, const nsRect& aOtherBounds,
+                                       nsRegion* aDifference);
+
     bool operator==(const Clip& aOther) const {
       return mHaveClipRect == aOther.mHaveClipRect &&
              (!mHaveClipRect || mClipRect.IsEqualInterior(aOther.mClipRect)) &&
              mRoundedClipRects == aOther.mRoundedClipRects;
     }
     bool operator!=(const Clip& aOther) const {
       return !(*this == aOther);
     }
--- a/layout/base/nsDisplayListInvalidation.h
+++ b/layout/base/nsDisplayListInvalidation.h
@@ -23,17 +23,17 @@ class nsDisplayItemGeometry
 public:
   nsDisplayItemGeometry(nsDisplayItem* aItem, nsDisplayListBuilder* aBuilder);
   virtual ~nsDisplayItemGeometry();
   
   /**
    * Compute the area required to be invalidated if this
    * display item is removed.
    */
-  nsRegion ComputeInvalidationRegion() { return mBounds; }
+  const nsRect& ComputeInvalidationRegion() { return mBounds; }
   
   /**
    * Shifts all retained areas of the nsDisplayItemGeometry by the given offset.
    * 
    * This is used to compensate for scrolling, since the destination buffer
    * can scroll without requiring a full repaint.
    *
    * @param aOffset Offset to shift by.