Bug 539356 - Avoid some causes of unnecessary painting. r=roc
authorMatt Woodrow <mwoodrow@mozilla.com>
Wed, 26 Sep 2012 11:59:56 +1200
changeset 114762 035ed3e2d9d4c031f6e8fdc1ef70b559d94d83bb
parent 114761 937aaddb295c2f98d2207bc8f5f89fa36fb6a2f3
child 114763 d60fa8d54dc4b9134dd93237a15932a3e86928c7
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)
reviewersroc
bugs539356
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 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,