Bug 539356 - Part 11 - Reimplement empty transactions. r=roc
authorMatt Woodrow <mwoodrow@mozilla.com>
Sat, 30 Jun 2012 15:06:12 +1200
changeset 98012 ba840bf34511473c2278236237e7a3340e3b6aca
parent 98011 e04abde1b3238f60e250b208cc7ecfc47c440b32
child 98013 90ab708bab8b14ea75d49dee9db3b32c3bbbfbf5
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 11 - Reimplement empty transactions. r=roc
layout/base/nsIPresShell.h
layout/base/nsPresShell.cpp
layout/base/nsPresShell.h
layout/generic/nsFrame.cpp
layout/generic/nsIFrame.h
view/src/nsViewManager.cpp
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -1224,17 +1224,26 @@ public:
    * visible presshell in the document tree.
    */
   virtual void WillPaint(bool aWillSendDidPaint) = 0;
   /**
    * Notify that the NS_DID_PAINT event was received. Only fires on the
    * root pres shell.
    */
   virtual void DidPaint() = 0;
-  virtual void ScheduleViewManagerFlush() = 0;
+
+  /**
+   * Ensures that the refresh driver is running, and schedules a view 
+   * manager flush on the next tick.
+   *
+   * @param aFlags nsIFrame::PAINT_COMPOSITE_ONLY : No changes have 
+   * been made that require a layer tree update, so only schedule a 
+   * layer tree composite.
+   */
+  virtual void ScheduleViewManagerFlush(PRUint32 aFlags = 0) = 0;
   virtual void ClearMouseCaptureOnView(nsIView* aView) = 0;
   virtual bool IsVisible() = 0;
   virtual void DispatchSynthMouseMove(nsGUIEvent *aEvent, bool aFlushOnHoverChange) = 0;
 
   virtual void SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf,
                                    nsArenaMemoryStats *aArenaObjectsSize,
                                    size_t *aPresShellSize,
                                    size_t *aStyleSetsSize,
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -165,16 +165,17 @@
 #include "gfxPlatform.h"
 
 #include "mozilla/FunctionTimer.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
 #include "sampler.h"
 
 #include "Layers.h"
+#include "LayerTreeInvalidation.h"
 #include "nsAsyncDOMEvent.h"
 
 #ifdef NS_FUNCTION_TIMER
 #define NS_TIME_FUNCTION_DECLARE_DOCURL                \
   nsCAutoString docURL__("N/A");                       \
   nsIURI *uri__ = mDocument->GetDocumentURI();         \
   if (uri__) uri__->GetSpec(docURL__);
 #define NS_TIME_FUNCTION_WITH_DOCURL                   \
@@ -3369,21 +3370,24 @@ PresShell::GetRectVisibility(nsIFrame* a
     return nsRectVisibility_kLeftOfViewport;
   if (r.x >= insetRect.XMost())
     return nsRectVisibility_kRightOfViewport;
 
   return nsRectVisibility_kVisible;
 }
 
 void
-PresShell::ScheduleViewManagerFlush()
+PresShell::ScheduleViewManagerFlush(PRUint32 aFlags)
 {
   nsPresContext* presContext = GetPresContext();
   if (presContext) {
     presContext->RefreshDriver()->ScheduleViewManagerFlush();
+    if (!(aFlags & nsIFrame::PAINT_COMPOSITE_ONLY)) {
+      mPaintRequired = true;
+    }
   }
   if (mDocument) {
     mDocument->SetNeedLayoutFlush();
   }
 }
 
 void
 PresShell::DispatchSynthMouseMove(nsGUIEvent *aEvent,
@@ -5233,32 +5237,65 @@ PresShell::Paint(nsIView*           aVie
     mIsFirstPaint = false;
   }
 
   if (frame && isRetainingManager) {
     // Try to do an empty transaction, if the frame tree does not
     // need to be updated. Do not try to do an empty transaction on
     // a non-retained layer manager (like the BasicLayerManager that
     // draws the window title bar on Mac), because a) it won't work
-    // and b) below we don't want to clear NS_FRAME_UPDATE_LAYER_TREE,
+    // and b) below we don't want to clear mPaintRequired,
     // that will cause us to forget to update the real layer manager!
     if (aType == PaintType_Composite) {
       if (layerManager->HasShadowManager()) {
         return;
       }
       layerManager->BeginTransaction();
       if (layerManager->EndEmptyTransaction()) {
         return;
       }
       NS_WARNING("Must complete empty transaction when compositing!");
     } else {
       layerManager->BeginTransaction();
     }
 
-    frame->RemoveStateBits(NS_FRAME_UPDATE_LAYER_TREE);
+    if (!mPaintRequired) {
+      NotifySubDocInvalidationFunc computeInvalidFunc =
+        presContext->MayHavePaintEventListenerInSubDocument() ? nsPresContext::NotifySubDocInvalidation : 0;
+      bool computeInvalidRect = computeInvalidFunc ||
+                                (layerManager->GetBackendType() == LayerManager::LAYERS_BASIC);
+
+      nsAutoPtr<LayerProperties> props(computeInvalidRect ? 
+                                         LayerProperties::CloneFrom(layerManager->GetRoot()) : 
+                                         nsnull);
+
+      if (layerManager->EndEmptyTransaction()) {
+        nsIntRect invalid;
+        if (props) {
+          invalid = props->ComputeDifferences(layerManager->GetRoot(), computeInvalidFunc);
+        }
+        if (!invalid.IsEmpty()) {
+          if (props) {
+            nsRect rect(presContext->DevPixelsToAppUnits(invalid.x),
+                        presContext->DevPixelsToAppUnits(invalid.y),
+                        presContext->DevPixelsToAppUnits(invalid.width),
+                        presContext->DevPixelsToAppUnits(invalid.height));
+            aViewToPaint->GetViewManager()->InvalidateViewNoSuppression(aViewToPaint, rect);
+            presContext->NotifyInvalidation(invalid, 0);
+          } else {
+            aViewToPaint->GetViewManager()->InvalidateView(aViewToPaint);
+          }
+        }
+    
+        frame->UpdatePaintCountForPaintedPresShells();
+        presContext->NotifyDidPaintForSubtree();
+        return;
+      }
+    }
+    mPaintRequired = false;
   } else {
     layerManager->BeginTransaction();
   }
   if (frame) {
     frame->ClearPresShellsFromLastPaint();
   }
 
   nscolor bgcolor = ComputeBackstopColor(aViewToPaint);
--- a/layout/base/nsPresShell.h
+++ b/layout/base/nsPresShell.h
@@ -194,17 +194,17 @@ public:
                                                         nsEvent* aEvent,
                                                         nsEventStatus* aStatus);
   virtual NS_HIDDEN_(nsresult) HandleDOMEventWithTarget(nsIContent* aTargetContent,
                                                         nsIDOMEvent* aEvent,
                                                         nsEventStatus* aStatus);
   virtual bool ShouldIgnoreInvalidation();
   virtual void WillPaint(bool aWillSendDidPaint);
   virtual void DidPaint();
-  virtual void ScheduleViewManagerFlush();
+  virtual void ScheduleViewManagerFlush(PRUint32 aFlags = 0);
   virtual void DispatchSynthMouseMove(nsGUIEvent *aEvent, bool aFlushOnHoverChange);
   virtual void ClearMouseCaptureOnView(nsIView* aView);
   virtual bool IsVisible();
 
   // caret handling
   virtual NS_HIDDEN_(already_AddRefed<nsCaret>) GetCaret() const;
   virtual NS_HIDDEN_(void) MaybeInvalidateCaretPosition();
   NS_IMETHOD SetCaretEnabled(bool aInEnable);
@@ -763,16 +763,22 @@ protected:
   bool                      mDocumentLoading : 1;
   bool                      mIgnoreFrameDestruction : 1;
   bool                      mHaveShutDown : 1;
   bool                      mViewportOverridden : 1;
   bool                      mLastRootReflowHadUnconstrainedHeight : 1;
   bool                      mNoDelayedMouseEvents : 1;
   bool                      mNoDelayedKeyEvents : 1;
 
+  // False if calls to Paint with the retaining manager can be handled
+  // with an empty transaction, True if we require painting and a layer
+  // tree update.
+  bool                      mPaintRequired : 1;
+
+
   // We've been disconnected from the document.  We will refuse to paint the
   // document until either our timer fires or all frames are constructed.
   bool                      mIsDocumentGone : 1;
 
   // Indicates that it is safe to unlock painting once all pending reflows
   // have been processed.
   bool                      mShouldUnsuppressPainting : 1;
 
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -4646,28 +4646,28 @@ nsIFrame::InvalidateFrame(PRUint32 aFlag
   
 bool 
 nsIFrame::IsInvalid() 
 {
   return HasAnyStateBits(NS_FRAME_NEEDS_PAINT);
 }
 
 void
-nsIFrame::SchedulePaint()
+nsIFrame::SchedulePaint(PRUint32 aFlags)
 {
   nsPresContext *pres = PresContext()->GetRootPresContext();
   if (HasAnyStateBits(NS_FRAME_IN_POPUP) || !pres) {
     nsIFrame *displayRoot = nsLayoutUtils::GetDisplayRootFrame(this);
     NS_ASSERTION(displayRoot, "Need a display root to schedule a paint!");
     if (!displayRoot) {
       return;
     }
     pres = displayRoot->PresContext();
   }
-  pres->PresShell()->ScheduleViewManagerFlush();
+  pres->PresShell()->ScheduleViewManagerFlush(aFlags);
 }
 
 Layer*
 nsIFrame::InvalidateLayer(PRUint32 aDisplayItemKey, const nsIntRect* aDamageRect)
 {
   NS_ASSERTION(aDisplayItemKey > 0, "Need a key");
 
   Layer* layer = FrameLayerBuilder::GetDedicatedLayer(this, aDisplayItemKey);
@@ -4681,17 +4681,17 @@ nsIFrame::InvalidateLayer(PRUint32 aDisp
   }
 
   if (aDamageRect) {
     layer->AddInvalidRect(*aDamageRect);
   } else {
     layer->SetInvalidRectToVisibleRegion();
   }
 
-  SchedulePaint();
+  SchedulePaint(PAINT_COMPOSITE_ONLY);
   return layer;
 }
 
 NS_DECLARE_FRAME_PROPERTY(DeferInvalidatesProperty, nsIFrame::DestroyRegion)
 
 void
 nsIFrame::BeginDeferringInvalidatesForDisplayRoot(const nsRegion& aExcludeRegion)
 {
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -237,22 +237,16 @@ typedef PRUint64 nsFrameState;
 #define NS_FRAME_IS_PUSHED_FLOAT                    NS_FRAME_STATE_BIT(32)
 
 // This bit acts as a loop flag for recursive paint server drawing.
 #define NS_FRAME_DRAWING_AS_PAINTSERVER             NS_FRAME_STATE_BIT(33)
 
 // Frame's overflow area was clipped by the 'clip' property.
 #define NS_FRAME_HAS_CLIP                           NS_FRAME_STATE_BIT(35)
 
-// Frame is a display root and the retained layer tree needs to be updated
-// at the next paint via display list construction.
-// Only meaningful for display roots, so we don't really need a global state
-// bit; we could free up this bit with a little extra complexity.
-#define NS_FRAME_UPDATE_LAYER_TREE                  NS_FRAME_STATE_BIT(36)
-
 // Frame can accept absolutely positioned children.
 #define NS_FRAME_HAS_ABSPOS_CHILDREN                NS_FRAME_STATE_BIT(37)
 
 // A display item for this frame has been painted as part of a ThebesLayer.
 #define NS_FRAME_PAINTED_THEBES                     NS_FRAME_STATE_BIT(38)
 
 // Frame is or is a descendant of something with a fixed height, unless that
 // ancestor is a body or html element, and has no closer ancestor that is
@@ -2154,18 +2148,25 @@ public:
 
   /**
    * Ensures that the refresh driver is running, and schedules a view 
    * manager flush on the next tick.
    *
    * The view manager flush will update the layer tree, repaint any 
    * invalid areas in the layer tree and schedule a layer tree
    * composite operation to display the layer tree.
+   *
+   * @param aFlags PAINT_COMPOSITE_ONLY : No changes have been made
+   * that require a layer tree update, so only schedule a layer
+   * tree composite.
    */
-  void SchedulePaint();
+  enum {
+    PAINT_COMPOSITE_ONLY
+  };
+  void SchedulePaint(PRUint32 aFlags = 0);
 
   /**
    * Checks if the layer tree includes a dedicated layer for this 
    * frame/display item key pair, and invalidates at least aDamageRect
    * area within that layer.
    *
    * If no layer is found, calls InvalidateFrame() instead.
    *
--- a/view/src/nsViewManager.cpp
+++ b/view/src/nsViewManager.cpp
@@ -428,17 +428,17 @@ AddDirtyRegion(nsView *aView, const nsRe
 }
 
 void
 nsViewManager::PostPendingUpdate()
 {
   nsViewManager* rootVM = RootViewManager();
   rootVM->mHasPendingWidgetGeometryChanges = true;
   if (rootVM->mPresShell) {
-    rootVM->mPresShell->ScheduleViewManagerFlush();
+    rootVM->mPresShell->ScheduleViewManagerFlush(nsIFrame::PAINT_COMPOSITE_ONLY);
   }
 }
 
 /**
  * @param aDamagedRegion this region, relative to aWidgetView, is invalidated in
  * every widget child of aWidgetView, plus aWidgetView's own widget
  */
 void