Bug 539356 - Avoid some causes of unnecessary painting. r=roc
authorMatt Woodrow <mwoodrow@mozilla.com>
Wed, 26 Sep 2012 11:59:56 +1200
changeset 108573 035ed3e2d9d4c031f6e8fdc1ef70b559d94d83bb
parent 108572 937aaddb295c2f98d2207bc8f5f89fa36fb6a2f3
child 108574 d60fa8d54dc4b9134dd93237a15932a3e86928c7
push id82
push usershu@rfrn.org
push dateFri, 05 Oct 2012 13:20:22 +0000
reviewersroc
bugs539356
milestone18.0a1
Bug 539356 - Avoid some causes of unnecessary painting. r=roc
layout/generic/nsFrame.cpp
layout/generic/nsIFrame.h
layout/generic/nsImageFrame.cpp
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -4636,18 +4636,29 @@ NS_DECLARE_FRAME_PROPERTY(LayerActivityP
 
 void
 LayerActivityTracker::NotifyExpired(LayerActivity* aObject)
 {
   RemoveObject(aObject);
 
   nsIFrame* f = aObject->mFrame;
   aObject->mFrame = nullptr;
+
+  // if there are hints other than transform/opacity, invalidate, since we don't know what else to do.
+  if (aObject->mChangeHint & ~(nsChangeHint_UpdateOpacityLayer|nsChangeHint_UpdateTransformLayer)) {
+    f->InvalidateFrameSubtree();
+  } else {
+    if (aObject->mChangeHint & nsChangeHint_UpdateOpacityLayer) {
+      f->InvalidateFrameSubtree(nsDisplayItem::TYPE_OPACITY);
+    } 
+    if (aObject->mChangeHint & nsChangeHint_UpdateTransformLayer) {
+      f->InvalidateFrameSubtree(nsDisplayItem::TYPE_TRANSFORM);
+    }
+  } 
   f->Properties().Delete(LayerActivityProperty());
-  f->InvalidateFrameSubtree();
 }
 
 void
 nsIFrame::MarkLayersActive(nsChangeHint aChangeHint)
 {
   FrameProperties properties = Properties();
   LayerActivity* layerActivity =
     static_cast<LayerActivity*>(properties.Get(LayerActivityProperty()));
@@ -4747,22 +4758,50 @@ nsIFrame::GetTransformMatrix(const nsIFr
   nsPoint delta = GetOffsetToCrossDoc(*aOutAncestor);
   int32_t scaleFactor = PresContext()->AppUnitsPerDevPixel();
   return gfx3DMatrix().Translation
     (NSAppUnitsToFloatPixels(delta.x, scaleFactor),
      NSAppUnitsToFloatPixels(delta.y, scaleFactor),
      0.0f);
 }
 
+static void InvalidateFrameInternal(nsIFrame *aFrame, bool aHasDisplayItem = true)
+{
+  if (aHasDisplayItem) {
+    aFrame->AddStateBits(NS_FRAME_NEEDS_PAINT);
+  }
+  nsSVGEffects::InvalidateDirectRenderingObservers(aFrame);
+  nsIFrame *parent = nsLayoutUtils::GetCrossDocParentFrame(aFrame);
+  while (parent && !parent->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
+    if (aHasDisplayItem) {
+      parent->AddStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT);
+    }
+    nsSVGEffects::InvalidateDirectRenderingObservers(parent);
+    parent = nsLayoutUtils::GetCrossDocParentFrame(parent);
+  }
+  if (!aHasDisplayItem) {
+    return;
+  }
+  if (!parent) {
+    aFrame->SchedulePaint();
+  }
+  if (aFrame->HasAnyStateBits(NS_FRAME_HAS_INVALID_RECT)) {
+    aFrame->Properties().Delete(nsIFrame::InvalidationRect());
+    aFrame->RemoveStateBits(NS_FRAME_HAS_INVALID_RECT);
+  }
+}
+
 void
-nsIFrame::InvalidateFrameSubtree()
-{
-  InvalidateFrame();
-
-  if (HasAnyStateBits(NS_FRAME_ALL_DESCENDANTS_NEED_PAINT)) {
+nsIFrame::InvalidateFrameSubtree(uint32_t aDisplayItemKey)
+{
+  bool hasDisplayItem = 
+    !aDisplayItemKey || FrameLayerBuilder::HasRetainedDataFor(this, aDisplayItemKey);
+  InvalidateFrameInternal(this, hasDisplayItem);
+
+  if (HasAnyStateBits(NS_FRAME_ALL_DESCENDANTS_NEED_PAINT) || !hasDisplayItem) {
     return;
   }
 
   AddStateBits(NS_FRAME_ALL_DESCENDANTS_NEED_PAINT);
   
   nsAutoTArray<nsIFrame::ChildList,4> childListArray;
   GetCrossDocChildLists(&childListArray);
 
@@ -4791,51 +4830,40 @@ nsIFrame::ClearInvalidationStateBits()
     }
   }
 
   RemoveStateBits(NS_FRAME_NEEDS_PAINT | 
                   NS_FRAME_DESCENDANT_NEEDS_PAINT | 
                   NS_FRAME_ALL_DESCENDANTS_NEED_PAINT);
 }
 
-static void InvalidateFrameInternal(nsIFrame *aFrame)
-{
-  aFrame->AddStateBits(NS_FRAME_NEEDS_PAINT);
-  nsSVGEffects::InvalidateDirectRenderingObservers(aFrame);
-  nsIFrame *parent = nsLayoutUtils::GetCrossDocParentFrame(aFrame);
-  while (parent && !parent->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
-    parent->AddStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT);
-    nsSVGEffects::InvalidateDirectRenderingObservers(parent);
-    parent = nsLayoutUtils::GetCrossDocParentFrame(parent);
-  }
-  if (!parent) {
-    aFrame->SchedulePaint();
-  }
-  if (aFrame->HasAnyStateBits(NS_FRAME_HAS_INVALID_RECT)) {
-    aFrame->Properties().Delete(nsIFrame::InvalidationRect());
-    aFrame->RemoveStateBits(NS_FRAME_HAS_INVALID_RECT);
-  }
+void
+nsIFrame::InvalidateFrame(uint32_t aDisplayItemKey)
+{
+  bool hasDisplayItem = 
+    !aDisplayItemKey || FrameLayerBuilder::HasRetainedDataFor(this, aDisplayItemKey);
+  InvalidateFrameInternal(this, hasDisplayItem);
 }
 
 void
-nsIFrame::InvalidateFrame()
-{
-  InvalidateFrameInternal(this);
-}
-
-void
-nsIFrame::InvalidateFrameWithRect(const nsRect& aRect)
-{
+nsIFrame::InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey)
+{
+  bool hasDisplayItem = 
+    !aDisplayItemKey || FrameLayerBuilder::HasRetainedDataFor(this, aDisplayItemKey);
   bool alreadyInvalid = false;
   if (!HasAnyStateBits(NS_FRAME_NEEDS_PAINT)) {
-    InvalidateFrameInternal(this);
+    InvalidateFrameInternal(this, hasDisplayItem);
   } else {
     alreadyInvalid = true;
   } 
 
+  if (!hasDisplayItem) {
+    return;
+  }
+
   nsRect *rect = static_cast<nsRect*>(Properties().Get(InvalidationRect()));
   if (!rect) {
     if (alreadyInvalid) {
       return;
     }
     rect = new nsRect();
     Properties().Set(InvalidationRect(), rect);
     AddStateBits(NS_FRAME_HAS_INVALID_RECT);
@@ -4889,17 +4917,24 @@ nsIFrame::InvalidateLayer(uint32_t aDisp
   NS_ASSERTION(aDisplayItemKey > 0, "Need a key");
 
   Layer* layer = FrameLayerBuilder::GetDedicatedLayer(this, aDisplayItemKey);
   if (aDamageRect && aDamageRect->IsEmpty()) {
     return layer;
   }
 
   if (!layer) {
-    InvalidateFrame();
+    // Plugins can transition from not rendering anything to rendering,
+    // and still only call this. So always invalidate, with specifying
+    // the display item type just in case.
+    if (aDisplayItemKey == nsDisplayItem::TYPE_PLUGIN) {
+      InvalidateFrame();
+    } else {
+      InvalidateFrame(aDisplayItemKey);
+    }
     return nullptr;
   }
 
   if (aDamageRect) {
     layer->AddInvalidRect(*aDamageRect);
   } else {
     layer->SetInvalidRectToVisibleRegion();
   }
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -2178,40 +2178,52 @@ public:
   bool AreLayersMarkedActive(nsChangeHint aChangeHint);
 
   /**
    * Marks all display items created by this frame as needing a repaint,
    * and calls SchedulePaint() if requested and one is not already pending.
    *
    * This includes all display items created by this frame, including
    * container types.
+   *
+   * @param aDisplayItemKey If specified, only issues an invalidate
+   * if this frame painted a display item of that type during the 
+   * previous paint. SVG rendering observers are always notified.
    */
-  virtual void InvalidateFrame();
+  virtual void InvalidateFrame(uint32_t aDisplayItemKey = 0);
 
   /**
    * Same as InvalidateFrame(), but only mark a fixed rect as needing
    * repainting.
    *
    * @param aRect The rect to invalidate, relative to the TopLeft of the
    * frame's border box.
+   * @param aDisplayItemKey If specified, only issues an invalidate
+   * if this frame painted a display item of that type during the 
+   * previous paint. SVG rendering observers are always notified.
    */
-  virtual void InvalidateFrameWithRect(const nsRect& aRect);
+  virtual void InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey = 0);
   
   /**
    * Calls InvalidateFrame() on all frames descendant frames (including
    * this one).
    * 
    * This function doesn't walk through placeholder frames to invalidate
    * the out-of-flow frames.
+   *
+   * @param aDisplayItemKey If specified, only issues an invalidate
+   * if this frame painted a display item of that type during the 
+   * previous paint. SVG rendering observers are always notified.
    */
-  void InvalidateFrameSubtree();
+  void InvalidateFrameSubtree(uint32_t aDisplayItemKey = 0);
 
   /**
    * Called when a frame is about to be removed and needs to be invalidated.
    * Normally does nothing since DLBI handles removed frames.
+   * 
    */
   virtual void InvalidateFrameForRemoval() {}
   
   /**
    * Checks if a frame has had InvalidateFrame() called on it since the
    * last paint.
    *
    * If true, then the invalid rect is returned in aRect, with an
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -587,20 +587,24 @@ nsImageFrame::OnDataAvailable(imgIReques
   // from
   if (!aCurrentFrame)
     return NS_OK;
   
 #ifdef DEBUG_decode
   printf("Source rect (%d,%d,%d,%d)\n",
          aRect->x, aRect->y, aRect->width, aRect->height);
 #endif
+
   if (aRect->IsEqualInterior(nsIntRect::GetMaxSizedIntRect())) {
-    InvalidateFrame();
+    InvalidateFrame(nsDisplayItem::TYPE_IMAGE);
+    InvalidateFrame(nsDisplayItem::TYPE_ALT_FEEDBACK);
   } else {
-    InvalidateFrameWithRect(SourceRectToDest(*aRect));
+    nsRect invalid = SourceRectToDest(*aRect);
+    InvalidateFrameWithRect(invalid, nsDisplayItem::TYPE_IMAGE);
+    InvalidateFrameWithRect(invalid, nsDisplayItem::TYPE_ALT_FEEDBACK);
   }
   
   return NS_OK;
 }
 
 nsresult
 nsImageFrame::OnStopDecode(imgIRequest *aRequest,
                            nsresult aStatus,