Bug 564991. Part 13: Optimize invalidation to avoid repainting ThebesLayer contents sometimes. r=mats,sr=dbaron
authorRobert O'Callahan <robert@ocallahan.org>
Fri, 16 Jul 2010 09:07:53 +1200
changeset 47740 d98f8a21727e27081c58a5db8c98dd8024bf45fe
parent 47739 209d286abd0c35395a18a09d9c71a79d01044cbf
child 47741 34d7e0561e5bde247abf8af510a1f85a7cf8a189
push id14413
push userrocallahan@mozilla.com
push dateThu, 15 Jul 2010 21:12:02 +0000
treeherderautoland@e1d7fd5255fd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmats, dbaron
bugs564991
milestone2.0b2pre
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 564991. Part 13: Optimize invalidation to avoid repainting ThebesLayer contents sometimes. r=mats,sr=dbaron
content/html/content/src/nsHTMLCanvasElement.cpp
content/media/nsMediaDecoder.cpp
layout/base/FrameLayerBuilder.cpp
layout/base/FrameLayerBuilder.h
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsChangeHint.h
layout/generic/nsFrame.cpp
layout/generic/nsIFrame.h
layout/style/nsStyleContext.cpp
layout/style/nsStyleStruct.cpp
--- a/content/html/content/src/nsHTMLCanvasElement.cpp
+++ b/content/html/content/src/nsHTMLCanvasElement.cpp
@@ -41,16 +41,17 @@
 #include "nsNetUtil.h"
 #include "prmem.h"
 
 #include "nsIScriptSecurityManager.h"
 #include "nsIXPConnect.h"
 #include "jsapi.h"
 
 #include "nsFrameManager.h"
+#include "nsDisplayList.h"
 #include "ImageLayers.h"
 #include "BasicLayers.h"
 
 #define DEFAULT_CANVAS_WIDTH 300
 #define DEFAULT_CANVAS_HEIGHT 150
 
 using namespace mozilla;
 using namespace mozilla::layers;
@@ -497,20 +498,20 @@ nsHTMLCanvasElement::InvalidateFrame(con
 
     // then make it a nsRect
     nsRect invalRect(realRect.X(), realRect.Y(),
                      realRect.Width(), realRect.Height());
 
     // account for border/padding
     invalRect.MoveBy(contentArea.TopLeft() - frame->GetPosition());
 
-    frame->Invalidate(invalRect);
+    frame->InvalidateLayer(invalRect, nsDisplayItem::TYPE_CANVAS);
   } else {
     nsRect r(frame->GetContentRect() - frame->GetPosition());
-    frame->Invalidate(r);
+    frame->InvalidateLayer(r, nsDisplayItem::TYPE_CANVAS);
   }
 }
 
 PRInt32
 nsHTMLCanvasElement::CountContexts()
 {
   if (mCurrentContext)
     return 1;
--- a/content/media/nsMediaDecoder.cpp
+++ b/content/media/nsMediaDecoder.cpp
@@ -47,16 +47,17 @@
 #include "nsNetUtil.h"
 #include "nsHTMLMediaElement.h"
 #include "nsAutoLock.h"
 #include "nsIRenderingContext.h"
 #include "gfxContext.h"
 #include "gfxImageSurface.h"
 #include "nsPresContext.h"
 #include "nsDOMError.h"
+#include "nsDisplayList.h"
 
 #if defined(XP_MACOSX)
 #include "gfxQuartzImageSurface.h"
 #endif
 
 // Number of milliseconds between progress events as defined by spec
 #define PROGRESS_MS 350
 
@@ -144,18 +145,19 @@ void nsMediaDecoder::Invalidate()
         presShell->FrameNeedsReflow(frame,
                                     nsIPresShell::eStyleChange,
                                     NS_FRAME_IS_DIRTY);
       }
     }
   }
 
   if (frame) {
-    nsRect r(nsPoint(0,0), frame->GetSize());
-    frame->Invalidate(r);
+    nsRect contentRect = frame->GetContentRect() - frame->GetPosition();
+    // Only the layer needs to be updated here
+    frame->InvalidateLayer(contentRect, nsDisplayItem::TYPE_VIDEO);
   }
 }
 
 static void ProgressCallback(nsITimer* aTimer, void* aClosure)
 {
   nsMediaDecoder* decoder = static_cast<nsMediaDecoder*>(aClosure);
   decoder->Progress(PR_TRUE);
 }
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -999,16 +999,37 @@ FrameLayerBuilder::InvalidateAllLayers(L
 {
   LayerManagerData* data = static_cast<LayerManagerData*>
     (aManager->GetUserData());
   if (data) {
     data->mInvalidateAllLayers = PR_TRUE;
   }
 }
 
+/* static */
+PRBool
+FrameLayerBuilder::HasDedicatedLayer(nsIFrame* aFrame, PRUint32 aDisplayItemKey)
+{
+  void* propValue = aFrame->Properties().Get(DisplayItemDataProperty());
+  if (!propValue)
+    return PR_FALSE;
+
+  nsTArray<DisplayItemData>* array =
+    (reinterpret_cast<nsTArray<DisplayItemData>*>(&propValue));
+  for (PRUint32 i = 0; i < array->Length(); ++i) {
+    if (array->ElementAt(i).mDisplayItemKey == aDisplayItemKey) {
+      void* layerUserData = array->ElementAt(i).mLayer->GetUserData();
+      if (layerUserData != &gColorLayerUserData &&
+          layerUserData != &gThebesDisplayItemLayerUserData)
+        return PR_TRUE;
+    }
+  }
+  return PR_FALSE;
+}
+
 /* static */ void
 FrameLayerBuilder::DrawThebesLayer(ThebesLayer* aLayer,
                                    gfxContext* aContext,
                                    const nsIntRegion& aRegionToDraw,
                                    const nsIntRegion& aRegionToInvalidate,
                                    void* aCallbackData)
 {
   nsDisplayListBuilder* builder = static_cast<nsDisplayListBuilder*>
--- a/layout/base/FrameLayerBuilder.h
+++ b/layout/base/FrameLayerBuilder.h
@@ -165,17 +165,23 @@ public:
    */
   static void InvalidateAllThebesLayerContents(LayerManager* aManager);
 
   /**
    * Call this to force all retained layers to be discarded and recreated at
    * the next paint.
    */
   static void InvalidateAllLayers(LayerManager* aManager);
-  
+
+  /**
+   * Call this to determine if a frame has a dedicated (non-Thebes) layer
+   * for the given display item key.
+   */
+  static PRBool HasDedicatedLayer(nsIFrame* aFrame, PRUint32 aDisplayItemKey);
+
   /**
    * This callback must be provided to EndTransaction. The callback data
    * must be the nsDisplayListBuilder containing this FrameLayerBuilder.
    */
   static void DrawThebesLayer(ThebesLayer* aLayer,
                               gfxContext* aContext,
                               const nsIntRegion& aRegionToDraw,
                               const nsIntRegion& aRegionToInvalidate,
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -7665,16 +7665,20 @@ DoApplyRenderingChangeToTree(nsIFrame* a
             outerSVGFrame->UpdateAndInvalidateCoveredRegion(aFrame);
           }
         }
 #endif
       } else {
         aFrame->InvalidateOverflowRect();
       }
     }
+    if (aChange & nsChangeHint_UpdateOpacityLayer) {
+      aFrame->InvalidateLayer(aFrame->GetOverflowRectRelativeToSelf(),
+                              nsDisplayItem::TYPE_OPACITY);
+    }
   }
 }
 
 static void
 ApplyRenderingChangeToTree(nsPresContext* aPresContext,
                            nsIFrame* aFrame,
                            nsChangeHint aChange)
 {
@@ -7947,17 +7951,18 @@ nsCSSFrameConstructor::ProcessRestyledFr
       if (hint & nsChangeHint_UpdateEffects) {
         nsSVGEffects::UpdateEffects(frame);
       }
 #endif
       if (hint & nsChangeHint_NeedReflow) {
         StyleChangeReflow(frame, hint);
         didReflow = PR_TRUE;
       }
-      if (hint & (nsChangeHint_RepaintFrame | nsChangeHint_SyncFrameView)) {
+      if (hint & (nsChangeHint_RepaintFrame | nsChangeHint_SyncFrameView |
+                  nsChangeHint_UpdateOpacityLayer)) {
         ApplyRenderingChangeToTree(presContext, frame, hint);
         didInvalidate = PR_TRUE;
       }
       if (hint & nsChangeHint_UpdateCursor) {
         nsIViewManager* viewMgr = mPresShell->GetViewManager();
         if (viewMgr)
           viewMgr->SynthesizeMouseMove(PR_FALSE);
       }
--- a/layout/base/nsChangeHint.h
+++ b/layout/base/nsChangeHint.h
@@ -77,19 +77,25 @@ enum nsChangeHint {
    * out cached nsSVGPropertyBase and subclasses which hold a reference to
    * the element referenced by the URI, and a mutation observer for
    * the DOM subtree rooted at that element. Also, for filters they store a
    * bounding-box for the filter result so that if the filter changes we can
    * invalidate the old covered area.
    */
   nsChangeHint_UpdateEffects = 0x80,
 
+  /**
+   * Visual change only, but the change can be handled entirely by
+   * updating the layer(s) for the frame.
+   */
+  nsChangeHint_UpdateOpacityLayer = 0x100,
+
   // change requires frame change (e.g., display:).
   // This subsumes all the above.
-  nsChangeHint_ReconstructFrame = 0x100
+  nsChangeHint_ReconstructFrame = 0x200
 };
 
 // Redefine these operators to return nothing. This will catch any use
 // of these operators on hints. We should not be using these operators
 // on nsChangeHints
 inline void operator<(nsChangeHint s1, nsChangeHint s2) {}
 inline void operator>(nsChangeHint s1, nsChangeHint s2) {}
 inline void operator!=(nsChangeHint s1, nsChangeHint s2) {}
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -3675,16 +3675,29 @@ nsFrame::GetType() const
 
 PRBool
 nsIFrame::IsLeaf() const
 {
   return PR_TRUE;
 }
 
 void
+nsIFrame::InvalidateLayer(const nsRect& aDamageRect, PRUint32 aDisplayItemKey)
+{
+  NS_ASSERTION(aDisplayItemKey > 0, "Need a key");
+
+  if (!FrameLayerBuilder::HasDedicatedLayer(this, aDisplayItemKey)) {
+    Invalidate(aDamageRect);
+    return;
+  }
+
+  InvalidateWithFlags(aDamageRect, INVALIDATE_NO_THEBES_LAYERS);
+}
+
+void
 nsIFrame::InvalidateWithFlags(const nsRect& aDamageRect, PRUint32 aFlags)
 {
   if (aDamageRect.IsEmpty()) {
     return;
   }
 
   // Don't allow invalidates to do anything when
   // painting is suppressed.
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -1831,16 +1831,25 @@ public:
    * need to be repainted.
    *
    * @param aDamageRect is in the frame's local coordinate space
    */
   void Invalidate(const nsRect& aDamageRect)
   { return InvalidateWithFlags(aDamageRect, 0); }
 
   /**
+   * As Invalidate above, except that this should be called when the
+   * rendering that has changed is performed using layers so we can avoid
+   * updating the contents of ThebesLayers.
+   * @param aDisplayItemKey must not be zero; indicates the kind of display
+   * item that is being invalidated.
+   */
+  void InvalidateLayer(const nsRect& aDamageRect, PRUint32 aDisplayItemKey);
+
+  /**
    * Helper function that can be overridden by frame classes. The rectangle
    * (plus aOffsetX/aOffsetY) is relative to this frame.
    * 
    * The offset is given as two coords rather than as an nsPoint because
    * gcc optimizes it better that way, in particular in the default
    * implementation that passes the area to the parent frame becomes a tail
    * call.
    *
--- a/layout/style/nsStyleContext.cpp
+++ b/layout/style/nsStyleContext.cpp
@@ -407,19 +407,16 @@ nsStyleContext::CalcStyleDifference(nsSt
 
   // If our rule nodes are the same, then we are looking at the same
   // style data.  We know this because CalcStyleDifference is always
   // called on two style contexts that point to the same element, so we
   // know that our position in the style context tree is the same and
   // our position in the rule node tree is also the same.
   PRBool compare = mRuleNode != aOther->mRuleNode;
 
-  nsChangeHint maxHint = nsChangeHint(NS_STYLE_HINT_FRAMECHANGE |
-      nsChangeHint_UpdateCursor);
-  
 #define DO_STRUCT_DIFFERENCE(struct_)                                         \
   PR_BEGIN_MACRO                                                              \
     NS_ASSERTION(NS_IsHintSubset(nsStyle##struct_::MaxDifference(), maxHint), \
                  "Struct placed in the wrong maxHint section");               \
     const nsStyle##struct_* this##struct_ = PeekStyle##struct_();             \
     if (this##struct_) {                                                      \
       const nsStyle##struct_* other##struct_ = aOther->GetStyle##struct_();   \
       if ((compare || nsStyle##struct_::ForceCompare()) &&                    \
@@ -433,17 +430,22 @@ nsStyleContext::CalcStyleDifference(nsSt
       }                                                                       \
     }                                                                         \
   PR_END_MACRO
 
   // We begin by examining those style structs that are capable of
   // causing the maximal difference, a FRAMECHANGE.
   // FRAMECHANGE Structs: Display, XUL, Content, UserInterface,
   // Visibility, Outline, TableBorder, Table, Text, UIReset, Quotes
+  nsChangeHint maxHint = nsChangeHint(NS_STYLE_HINT_FRAMECHANGE |
+      nsChangeHint_UpdateOpacityLayer);
   DO_STRUCT_DIFFERENCE(Display);
+
+  maxHint = nsChangeHint(NS_STYLE_HINT_FRAMECHANGE |
+      nsChangeHint_UpdateCursor);
   DO_STRUCT_DIFFERENCE(XUL);
   DO_STRUCT_DIFFERENCE(Column);
   DO_STRUCT_DIFFERENCE(Content);
   DO_STRUCT_DIFFERENCE(UserInterface);
   DO_STRUCT_DIFFERENCE(Visibility);
   DO_STRUCT_DIFFERENCE(Outline);
   DO_STRUCT_DIFFERENCE(TableBorder);
   DO_STRUCT_DIFFERENCE(Table);
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -1885,18 +1885,19 @@ nsChangeHint nsStyleDisplay::CalcDiffere
   // XXX the following is conservative, for now: changing float breaking shouldn't
   // necessarily require a repaint, reflow should suffice.
   if (mBreakType != aOther.mBreakType
       || mBreakBefore != aOther.mBreakBefore
       || mBreakAfter != aOther.mBreakAfter
       || mAppearance != aOther.mAppearance)
     NS_UpdateHint(hint, NS_CombineHint(nsChangeHint_ReflowFrame, nsChangeHint_RepaintFrame));
 
-  if (mOpacity != aOther.mOpacity)
-    NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
+  if (mOpacity != aOther.mOpacity) {
+    NS_UpdateHint(hint, nsChangeHint_UpdateOpacityLayer);
+  }
 
   /* If we've added or removed the transform property, we need to reconstruct the frame to add
    * or remove the view object, and also to handle abs-pos and fixed-pos containers.
    */
   if (HasTransform() != aOther.HasTransform()) {
     NS_UpdateHint(hint, nsChangeHint_ReconstructFrame);
   }
   else if (HasTransform()) {
@@ -1931,17 +1932,18 @@ nsChangeHint nsStyleDisplay::CalcDiffere
   return hint;
 }
 
 #ifdef DEBUG
 /* static */
 nsChangeHint nsStyleDisplay::MaxDifference()
 {
   // All the parts of FRAMECHANGE are present above in CalcDifference.
-  return NS_STYLE_HINT_FRAMECHANGE;
+  return nsChangeHint(NS_STYLE_HINT_FRAMECHANGE |
+                      nsChangeHint_UpdateOpacityLayer);
 }
 #endif
 
 // --------------------
 // nsStyleVisibility
 //
 
 nsStyleVisibility::nsStyleVisibility(nsPresContext* aPresContext)