Bug 539356 - Part 8b - Move painting of retained layers to the view manager flush, and only composite on the paint event. r=roc
authorMatt Woodrow <mwoodrow@mozilla.com>
Mon, 11 Jun 2012 16:44:09 +1200
changeset 101169 0cd288a3808594737dbf48ffc57608f5c20e384d
parent 101168 f1d43208825c9068efee4bd4ac9a7a13eba0bbe7
child 101170 0adc41ff56dcddc7533f52730b3ec2296ff52430
push id1316
push userakeybl@mozilla.com
push dateMon, 27 Aug 2012 22:37:00 +0000
treeherdermozilla-beta@db4b09302ee2 [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 8b - Move painting of retained layers to the view manager flush, and only composite on the paint event. r=roc
gfx/layers/Layers.h
gfx/layers/basic/BasicLayers.h
gfx/layers/d3d10/LayerManagerD3D10.cpp
layout/base/nsDisplayList.cpp
layout/base/nsDisplayList.h
layout/base/nsIPresShell.h
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
layout/base/nsPresShell.cpp
layout/base/nsPresShell.h
layout/base/nsRefreshDriver.cpp
view/src/nsViewManager.cpp
view/src/nsViewManager.h
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -304,16 +304,19 @@ public:
    * During the drawing phase, all ThebesLayers in the tree are
    * drawn in tree order, exactly once each, except for those layers
    * where it is known that the visible region is empty.
    */
   virtual void EndTransaction(DrawThebesLayerCallback aCallback,
                               void* aCallbackData,
                               EndTransactionFlags aFlags = END_DEFAULT) = 0;
 
+  virtual bool HasShadowManagerInternal() const { return false; }
+  bool HasShadowManager() const { return HasShadowManagerInternal(); }
+
   bool IsSnappingEffectiveTransforms() { return mSnapEffectiveTransforms; } 
 
   /**
    * CONSTRUCTION PHASE ONLY
    * Set the root layer. The root layer is initially null. If there is
    * no root layer, EndTransaction won't draw anything.
    */
   virtual void SetRoot(Layer* aLayer) = 0;
--- a/gfx/layers/basic/BasicLayers.h
+++ b/gfx/layers/basic/BasicLayers.h
@@ -143,18 +143,16 @@ public:
   already_AddRefed<gfxContext> PushGroupForLayer(gfxContext* aContext, Layer* aLayer,
                                                  const nsIntRegion& aRegion,
                                                  bool* aNeedsClipToVisibleRegion);
   already_AddRefed<gfxContext> PushGroupWithCachedSurface(gfxContext *aTarget,
                                                           gfxASurface::gfxContentType aContent);
   void PopGroupToSourceWithCachedSurface(gfxContext *aTarget, gfxContext *aPushed);
 
   virtual bool IsCompositingCheap() { return false; }
-  virtual bool HasShadowManagerInternal() const { return false; }
-  bool HasShadowManager() const { return HasShadowManagerInternal(); }
   virtual PRInt32 GetMaxTextureSize() const { return PR_INT32_MAX; }
 
 protected:
   enum TransactionPhase {
     PHASE_NONE, PHASE_CONSTRUCTION, PHASE_DRAWING, PHASE_FORWARD
   };
   TransactionPhase mPhase;
 
--- a/gfx/layers/d3d10/LayerManagerD3D10.cpp
+++ b/gfx/layers/d3d10/LayerManagerD3D10.cpp
@@ -194,17 +194,17 @@ LayerManagerD3D10::Initialize(bool force
 
     attachments->mVertexBuffer = mVertexBuffer;
   } else {
     mEffect = attachments->mEffect;
     mVertexBuffer = attachments->mVertexBuffer;
     mInputLayout = attachments->mInputLayout;
   }
 
-  if (HasShadowManager()) {
+  if (LayerManager::HasShadowManager()) {
     reporter.SetSuccessful();
     return true;
   }
 
   nsRefPtr<IDXGIDevice> dxgiDevice;
   nsRefPtr<IDXGIAdapter> dxgiAdapter;
   nsRefPtr<IDXGIFactory> dxgiFactory;
 
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -643,17 +643,17 @@ void nsDisplayList::PaintForFrame(nsDisp
     // See bug 693938, attachment 567017
     NS_WARNING("We don't support transparent content with displayports, force it to be opqaue");
     root->SetContentFlags(Layer::CONTENT_OPAQUE);
   }
 
   layerManager->SetRoot(root);
   layerBuilder->WillEndTransaction(layerManager);
   layerManager->EndTransaction(FrameLayerBuilder::DrawThebesLayer,
-                               aBuilder);
+                               aBuilder, (aFlags & PAINT_NO_COMPOSITE) ? LayerManager::END_NO_COMPOSITE : LayerManager::END_DEFAULT);
   layerBuilder->DidEndTransaction(layerManager);
 
   if (aFlags & PAINT_FLUSH_LAYERS) {
     FrameLayerBuilder::InvalidateAllLayers(layerManager);
   }
 
   nsCSSRendering::DidPaint();
   layerManager->RemoveUserData(&gLayerManagerLayerBuilder);
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -20,16 +20,17 @@
 #include "nsISelection.h"
 #include "nsCaret.h"
 #include "plarena.h"
 #include "Layers.h"
 #include "nsRegion.h"
 #include "FrameLayerBuilder.h"
 #include "nsThemeConstants.h"
 #include "ImageLayers.h"
+#include "nsLayoutUtils.h"
 
 #include "mozilla/StandardInteger.h"
 
 #include <stdlib.h>
 
 class nsIPresShell;
 class nsIContent;
 class nsRenderingContext;
@@ -1150,17 +1151,18 @@ public:
    * 
    * This must only be called on the root display list of the display list
    * tree.
    */
   enum {
     PAINT_DEFAULT = 0,
     PAINT_USE_WIDGET_LAYERS = 0x01,
     PAINT_FLUSH_LAYERS = 0x02,
-    PAINT_EXISTING_TRANSACTION = 0x04
+    PAINT_EXISTING_TRANSACTION = 0x04,
+    PAINT_NO_COMPOSITE = 0x08
   };
   void PaintRoot(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx,
                  PRUint32 aFlags) const;
   /**
    * Like PaintRoot, but used for internal display sublists.
    * aForFrame is the frame that the list is associated with.
    */
   void PaintForFrame(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx,
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -1202,19 +1202,22 @@ public:
 
   /**
    * Dispatch a mouse move event based on the most recent mouse position if
    * this PresShell is visible. This is used when the contents of the page
    * moved (aFromScroll is false) or scrolled (aFromScroll is true).
    */
   virtual void SynthesizeMouseMove(bool aFromScroll) = 0;
 
-  virtual void Paint(nsIView* aViewToPaint, nsIWidget* aWidget,
-                     const nsRegion& aDirtyRegion, const nsIntRegion& aIntDirtyRegion,
-                     bool aWillSendDidPaint) = 0;
+  enum PaintType {
+    PaintType_Composite,
+    PaintType_NoComposite
+  };
+  virtual void Paint(nsIView* aViewToPaint, const nsRegion& aDirtyRegion,
+                     PaintType aType, bool aWillSendDidPaint) = 0;
   virtual nsresult HandleEvent(nsIFrame*       aFrame,
                                nsGUIEvent*     aEvent,
                                bool            aDontRetargetEvents,
                                nsEventStatus*  aEventStatus) = 0;
   virtual bool ShouldIgnoreInvalidation() = 0;
   /**
    * Notify that the NS_WILL_PAINT event was received. Fires on every
    * visible presshell in the document tree.
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -1778,16 +1778,19 @@ nsLayoutUtils::PaintFrame(nsRenderingCon
         // pres shell, notify the widget about any toolbars we've encountered.
         widget->UpdateThemeGeometries(builder.GetThemeGeometries());
       }
     }
   }
   if (aFlags & PAINT_EXISTING_TRANSACTION) {
     flags |= nsDisplayList::PAINT_EXISTING_TRANSACTION;
   }
+  if (aFlags & PAINT_NO_COMPOSITE) {
+    flags |= nsDisplayList::PAINT_NO_COMPOSITE;
+  }
 
   list.PaintRoot(&builder, aRenderingContext, flags);
 
   // Update the widget's opaque region information. This sets
   // glass boundaries on Windows.
   if ((aFlags & PAINT_WIDGET_LAYERS) &&
       !willFlushRetainedLayers &&
       !(aFlags & PAINT_DOCUMENT_RELATIVE)) {
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -591,17 +591,18 @@ public:
     PAINT_IN_TRANSFORM = 0x01,
     PAINT_SYNC_DECODE_IMAGES = 0x02,
     PAINT_WIDGET_LAYERS = 0x04,
     PAINT_IGNORE_SUPPRESSION = 0x08,
     PAINT_DOCUMENT_RELATIVE = 0x10,
     PAINT_HIDE_CARET = 0x20,
     PAINT_ALL_CONTINUATIONS = 0x40,
     PAINT_TO_WINDOW = 0x80,
-    PAINT_EXISTING_TRANSACTION = 0x100
+    PAINT_EXISTING_TRANSACTION = 0x100,
+    PAINT_NO_COMPOSITE = 0x200
   };
 
   /**
    * Given aFrame, the root frame of a stacking context, paint it and its
    * descendants to aRenderingContext.
    * @param aRenderingContext a rendering context translated so that (0,0)
    * is the origin of aFrame; for best results, (0,0) should transform
    * to pixel-aligned coordinates. This can be null, in which case
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -5197,108 +5197,120 @@ public:
   }
 
 private:
   bool mWillSendDidPaint;
 };
 
 void
 PresShell::Paint(nsIView*           aViewToPaint,
-                 nsIWidget*         aWidgetToPaint,
                  const nsRegion&    aDirtyRegion,
-                 const nsIntRegion& aIntDirtyRegion,
+                 PaintType          aType,
                  bool               aWillSendDidPaint)
 {
 #ifdef NS_FUNCTION_TIMER
   NS_TIME_FUNCTION_DECLARE_DOCURL;
   const nsRect& bounds__ = aDirtyRegion.GetBounds();
   NS_TIME_FUNCTION_MIN_FMT(1.0, "%s (line %d) (document: %s, dirty rect: (<%f, %f>, <%f, %f>)",
                            MOZ_FUNCTION_NAME, __LINE__, docURL__.get(),
                            NSCoordToFloat(bounds__.x),
                            NSCoordToFloat(bounds__.y),
                            NSCoordToFloat(bounds__.XMost()),
                            NSCoordToFloat(bounds__.YMost()));
 #endif
 
   SAMPLE_LABEL("Paint", "PresShell::Paint");
   NS_ASSERTION(!mIsDestroying, "painting a destroyed PresShell");
   NS_ASSERTION(aViewToPaint, "null view");
-  NS_ASSERTION(aWidgetToPaint, "Can't paint without a widget");
 
   nsAutoNotifyDidPaint notifyDidPaint(aWillSendDidPaint);
 
   nsPresContext* presContext = GetPresContext();
   AUTO_LAYOUT_PHASE_ENTRY_POINT(presContext, Paint);
 
   nsIFrame* frame = aViewToPaint->GetFrame();
 
   bool isRetainingManager;
   LayerManager* layerManager =
-    aWidgetToPaint->GetLayerManager(&isRetainingManager);
+    aViewToPaint->GetWidget()->GetLayerManager(&isRetainingManager);
   NS_ASSERTION(layerManager, "Must be in paint event");
 
   if (mIsFirstPaint) {
     layerManager->SetIsFirstPaint();
     mIsFirstPaint = false;
   }
-  layerManager->BeginTransaction();
 
   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,
     // that will cause us to forget to update the real layer manager!
-    if (!(frame->GetStateBits() & NS_FRAME_UPDATE_LAYER_TREE)) {
-      if (layerManager->EndEmptyTransaction()) {
-        frame->UpdatePaintCountForPaintedPresShells();
-        presContext->NotifyDidPaintForSubtree();
+    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);
+  } else {
+    layerManager->BeginTransaction();
   }
   if (frame) {
     frame->ClearPresShellsFromLastPaint();
   }
 
   nscolor bgcolor = ComputeBackstopColor(aViewToPaint);
+  PRUint32 flags = nsLayoutUtils::PAINT_WIDGET_LAYERS | nsLayoutUtils::PAINT_EXISTING_TRANSACTION;
+  if (aType == PaintType_NoComposite) {
+    flags |= nsLayoutUtils::PAINT_NO_COMPOSITE;
+  }
 
   if (frame) {
     // Defer invalidates that are triggered during painting, and discard
     // invalidates of areas that are already being repainted.
     // The layer system can trigger invalidates during painting
     // (see FrameLayerBuilder).
     frame->BeginDeferringInvalidatesForDisplayRoot(aDirtyRegion);
 
     // We can paint directly into the widget using its layer manager.
-    nsLayoutUtils::PaintFrame(nsnull, frame, aDirtyRegion, bgcolor,
-                              nsLayoutUtils::PAINT_WIDGET_LAYERS |
-                              nsLayoutUtils::PAINT_EXISTING_TRANSACTION);
+    nsLayoutUtils::PaintFrame(nsnull, frame, aDirtyRegion, bgcolor, flags);
 
     frame->EndDeferringInvalidatesForDisplayRoot();
-    presContext->NotifyDidPaintForSubtree();
+    if (aType != PaintType_Composite) {
+      presContext->NotifyDidPaintForSubtree();
+    }
     return;
   }
 
   nsRefPtr<ColorLayer> root = layerManager->CreateColorLayer();
   if (root) {
     nsPresContext* pc = GetPresContext();
     nsIntRect bounds =
       pc->GetVisibleArea().ToOutsidePixels(pc->AppUnitsPerDevPixel());
     bgcolor = NS_ComposeColors(bgcolor, mCanvasBackgroundColor);
     root->SetColor(bgcolor);
     root->SetVisibleRegion(bounds);
     layerManager->SetRoot(root);
   }
-  layerManager->EndTransaction(NULL, NULL);
-
-  presContext->NotifyDidPaintForSubtree();
+  layerManager->EndTransaction(NULL, NULL, aType == PaintType_NoComposite ?
+                                             LayerManager::END_NO_COMPOSITE :
+                                             LayerManager::END_DEFAULT);
+
+  if (aType != PaintType_Composite) {
+    presContext->NotifyDidPaintForSubtree();
+  }
 }
 
 // static
 void
 nsIPresShell::SetCapturingContent(nsIContent* aContent, PRUint8 aFlags)
 {
   // If capture was set for pointer lock, don't unlock unless we are coming
   // out of pointer lock explicitly.
--- a/layout/base/nsPresShell.h
+++ b/layout/base/nsPresShell.h
@@ -178,19 +178,18 @@ public:
   virtual void SetIgnoreViewportScrolling(bool aIgnore);
 
   virtual void SetDisplayPort(const nsRect& aDisplayPort);
 
   virtual nsresult SetResolution(float aXResolution, float aYResolution);
 
   //nsIViewObserver interface
 
-  virtual void Paint(nsIView* aViewToPaint, nsIWidget* aWidget,
-                     const nsRegion& aDirtyRegion, const nsIntRegion& aIntDirtyRegion,
-                     bool aWillSendDidPaint);
+  virtual void Paint(nsIView* aViewToPaint, const nsRegion& aDirtyRegion,
+                     PaintType aType, bool aWillSendDidPaint);
   virtual nsresult HandleEvent(nsIFrame*       aFrame,
                                nsGUIEvent*     aEvent,
                                bool            aDontRetargetEvents,
                                nsEventStatus*  aEventStatus);
   virtual NS_HIDDEN_(nsresult) HandleDOMEventWithTarget(nsIContent* aTargetContent,
                                                         nsEvent* aEvent,
                                                         nsEventStatus* aStatus);
   virtual NS_HIDDEN_(nsresult) HandleDOMEventWithTarget(nsIContent* aTargetContent,
--- a/layout/base/nsRefreshDriver.cpp
+++ b/layout/base/nsRefreshDriver.cpp
@@ -406,18 +406,24 @@ nsRefreshDriver::Notify(nsITimer *aTimer
 
   ImageRequestParameters parms = {mMostRecentRefresh};
   if (mRequests.Count()) {
     mRequests.EnumerateEntries(nsRefreshDriver::ImageRequestEnumerator, &parms);
     EnsureTimerStarted(false);
   }
 
   if (mViewManagerFlushIsPending) {
+#ifdef DEBUG_INVALIDATIONS
+    printf("Starting ProcessPendingUpdates\n");
+#endif
     mViewManagerFlushIsPending = false;
     mPresContext->GetPresShell()->GetViewManager()->ProcessPendingUpdates();
+#ifdef DEBUG_INVALIDATIONS
+    printf("Ending ProcessPendingUpdates\n");
+#endif
   }
 
   if (mThrottled ||
       (mTimerIsPrecise !=
        (GetRefreshTimerType() == nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP))) {
     // Stop the timer now and restart it here.  Stopping is in the mThrottled
     // case ok because either it's already one-shot, and it just fired, and all
     // we need to do is null it out, or it's repeating and we need to reset it
--- a/view/src/nsViewManager.cpp
+++ b/view/src/nsViewManager.cpp
@@ -295,21 +295,19 @@ nsIView* nsIViewManager::GetDisplayRootF
   }
 }
 
 /**
    aRegion is given in device coordinates!!
    aContext may be null, in which case layers should be used for
    rendering.
 */
-void nsViewManager::Refresh(nsView *aView, nsIWidget *aWidget,
-                            const nsIntRegion& aRegion,
+void nsViewManager::Refresh(nsView *aView, const nsIntRegion& aRegion,
                             bool aWillSendDidPaint)
 {
-  NS_ASSERTION(aView == nsView::GetViewFor(aWidget), "view widget mismatch");
   NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
 
   // damageRegion is the damaged area, in twips, relative to the view origin
   nsRegion damageRegion = aRegion.ToAppUnits(AppUnitsPerDevPixel());
   // move region from widget coordinates into view coordinates
   damageRegion.MoveBy(-aView->ViewToWidgetOffset());
 
   if (damageRegion.IsEmpty()) {
@@ -332,18 +330,24 @@ void nsViewManager::Refresh(nsView *aVie
   {
     nsAutoScriptBlocker scriptBlocker;
     SetPainting(true);
 
     NS_ASSERTION(GetDisplayRootFor(aView) == aView,
                  "Widgets that we paint must all be display roots");
 
     if (mPresShell) {
-      mPresShell->Paint(aView, aWidget, damageRegion, aRegion,
+#ifdef DEBUG_INVALIDATIONS
+      printf("--COMPOSITE-- %p\n", mPresShell);
+#endif
+      mPresShell->Paint(aView, damageRegion, nsIPresShell::PaintType_Composite,
                         aWillSendDidPaint);
+#ifdef DEBUG_INVALIDATIONS
+      printf("--ENDCOMPOSITE--\n");
+#endif
       mozilla::StartupTimeline::RecordOnce(mozilla::StartupTimeline::FIRST_PAINT);
     }
 
     SetPainting(false);
   }
 
   if (RootViewManager()->mRecursiveRefreshPending) {
     RootViewManager()->mRecursiveRefreshPending = false;
@@ -369,16 +373,27 @@ void nsViewManager::ProcessPendingUpdate
   for (nsView* childView = aView->GetFirstChild(); childView;
        childView = childView->GetNextSibling()) {
     ProcessPendingUpdatesForView(childView, aFlushDirtyRegion);
   }
 
   // Push out updates after we've processed the children; ensures that
   // damage is applied based on the final widget geometry
   if (aFlushDirtyRegion) {
+    nsIWidget *widget = aView->GetWidget();
+    if (widget) {
+#ifdef DEBUG_INVALIDATIONS
+      printf("---- PAINT START ----PresShell(%p), nsView(%p), nsIWidget(%p)\n", mPresShell, aView, widget);
+#endif
+      nsAutoScriptBlocker scriptBlocker;
+      mPresShell->Paint(aView, nsRegion(), nsIPresShell::PaintType_NoComposite, false);
+#ifdef DEBUG_INVALIDATIONS
+      printf("---- PAINT END ----\n");
+#endif
+    }
     FlushDirtyRegionToWidget(aView);
   }
 }
 
 void nsViewManager::FlushDirtyRegionToWidget(nsView* aView)
 {
   if (!aView->HasNonEmptyDirtyRegion())
     return;
@@ -762,17 +777,17 @@ NS_IMETHODIMP nsViewManager::DispatchEve
           // destroyed it during CallWillPaintOnObservers (bug 378273).
           view = nsView::GetViewFor(event->widget);
         }
 
         if (!view || event->region.IsEmpty())
           break;
 
         // Paint.
-        Refresh(view, event->widget, event->region, event->willSendDidPaint);
+        Refresh(view, event->region, event->willSendDidPaint);
 
         break;
       }
 
     case NS_DID_PAINT: {
       nsRefPtr<nsViewManager> rootVM = RootViewManager();
       rootVM->CallDidPaintOnObserver();
       break;
--- a/view/src/nsViewManager.h
+++ b/view/src/nsViewManager.h
@@ -124,18 +124,17 @@ private:
   void CallDidPaintOnObserver();
   void ReparentChildWidgets(nsIView* aView, nsIWidget *aNewWidget);
   void ReparentWidgets(nsIView* aView, nsIView *aParent);
   void InvalidateWidgetArea(nsView *aWidgetView, const nsRegion &aDamagedRegion);
 
   void InvalidateViews(nsView *aView);
 
   // aView is the view for aWidget and aRegion is relative to aWidget.
-  void Refresh(nsView *aView, nsIWidget *aWidget, const nsIntRegion& aRegion,
-               bool aWillSendDidPaint);
+  void Refresh(nsView *aView, const nsIntRegion& aRegion, bool aWillSendDidPaint);
 
   void InvalidateRectDifference(nsView *aView, const nsRect& aRect, const nsRect& aCutOut);
   void InvalidateHorizontalBandDifference(nsView *aView, const nsRect& aRect, const nsRect& aCutOut,
                                           nscoord aY1, nscoord aY2, bool aInCutOut);
 
   // Utilities
 
   bool IsViewInserted(nsView *aView);