Bug 621601. Part 1: Change empty transaction API to EndEmptyTransaction. r=bas,tnikkel,a=joe
authorRobert O'Callahan <robert@ocallahan.org>
Wed, 19 Jan 2011 21:27:54 +1300
changeset 60857 a18080aa75d6bea549a80a8647a57c88f19d6f13
parent 60856 0eddf5b448bb93a3f49ef96a0e5b4852d09111e1
child 60858 4fc581f1ff00e7dcf45fb982c0abd579beb32773
push id18137
push userrocallahan@mozilla.com
push dateWed, 19 Jan 2011 08:28:48 +0000
treeherdermozilla-central@710c1a6faf99 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbas, tnikkel, joe
bugs621601
milestone2.0b10pre
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 621601. Part 1: Change empty transaction API to EndEmptyTransaction. r=bas,tnikkel,a=joe
gfx/layers/Layers.h
gfx/layers/basic/BasicLayers.cpp
gfx/layers/basic/BasicLayers.h
layout/base/FrameLayerBuilder.cpp
layout/base/FrameLayerBuilder.h
layout/base/nsDisplayList.cpp
layout/base/nsDisplayList.h
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
layout/base/nsPresShell.cpp
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -289,16 +289,28 @@ public:
    * Start a new transaction. Nested transactions are not allowed so
    * there must be no transaction currently in progress. 
    * This transaction will render the contents of the layer tree to
    * the given target context. The rendering will be complete when
    * EndTransaction returns.
    */
   virtual void BeginTransactionWithTarget(gfxContext* aTarget) = 0;
   /**
+   * Attempts to end an "empty transaction". There must have been no
+   * changes to the layer tree since the BeginTransaction().
+   * It's possible for this to fail; ThebesLayers may need to be updated
+   * due to VRAM data being lost, for example. In such cases this method
+   * returns false, and the caller must proceed with a normal layer tree
+   * update and EndTransaction.
+   */
+  virtual bool EndEmptyTransaction()
+  {
+    return false;
+  }
+  /**
    * Function called to draw the contents of each ThebesLayer.
    * aRegionToDraw contains the region that needs to be drawn.
    * This would normally be a subregion of the visible region.
    * The callee must draw all of aRegionToDraw. Drawing outside
    * aRegionToDraw will be clipped out or ignored.
    * The callee must draw all of aRegionToDraw.
    * This region is relative to 0,0 in the ThebesLayer.
    * 
@@ -329,31 +341,16 @@ public:
    * drawing phase, and end the transaction.
    * 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) = 0;
 
-  /**
-   * Attempts to perform an "empty transaction", i.e., a BeginTransaction()
-   * followed by no changes to the layer tree and an EndTransaction with no
-   * ThebesLayer drawing callback. This will only work if no ThebesLayers
-   * need to be updated (i.e. the visible region of each ThebesLayer is already
-   * fully retained). Since this cannot be predicted in advance,
-   * DoEmptyTransaction is allowed to fail and return false. When
-   * DoEmptyTransaction fails it must be immediately (within the same paint
-   * event) followed by a normal BeginTransaction/EndTransaction pair.
-   */
-  virtual bool DoEmptyTransaction()
-  {
-    return false;
-  }
-
   PRBool IsSnappingEffectiveTransforms() { return mSnapEffectiveTransforms; } 
 
   /**
    * CONSTRUCTION PHASE ONLY
    * Set the root layer.
    */
   virtual void SetRoot(Layer* aLayer) = 0;
   /**
--- a/gfx/layers/basic/BasicLayers.cpp
+++ b/gfx/layers/basic/BasicLayers.cpp
@@ -1271,47 +1271,55 @@ BasicLayerManager::EndTransactionInterna
     nsIntRegion region;
     MarkLeafLayersCoveredByOpaque(mRoot,
                                   mRoot->GetEffectiveVisibleRegion().GetBounds(),
                                   region);
     PaintLayer(mRoot, aCallback, aCallbackData);
 
     // If we're doing manual double-buffering, we need to avoid drawing
     // the results of an incomplete transaction to the destination surface.
+    // If the transaction is incomplete and we're not double-buffering then
+    // either the system is double-buffering our window (in which case the
+    // followup EndTransaction will be drawn over the top of our incomplete
+    // transaction before the system updates the window), or we have no
+    // overlapping or transparent layers in the update region, in which case
+    // our partial transaction drawing will look fine.
     if (useDoubleBuffering && !mTransactionIncomplete) {
       finalTarget->SetOperator(gfxContext::OPERATOR_SOURCE);
       PopGroupWithCachedSurface(finalTarget, cachedSurfaceOffset);
     }
 
     mTarget = nsnull;
   }
 
 #ifdef MOZ_LAYERS_HAVE_LOG
   Log();
   MOZ_LAYERS_LOG(("]----- EndTransaction"));
 #endif
 
 #ifdef DEBUG
-  mPhase = PHASE_NONE;
+  // Go back to the construction phase if the transaction isn't complete.
+  // Layout will update the layer tree and call EndTransaction().
+  mPhase = mTransactionIncomplete ? PHASE_CONSTRUCTION : PHASE_NONE;
 #endif
   mUsingDefaultTarget = PR_FALSE;
 
   NS_ASSERTION(!aCallback || !mTransactionIncomplete,
                "If callback is not null, transaction must be complete");
+
   return !mTransactionIncomplete;
 }
 
 bool
-BasicLayerManager::DoEmptyTransaction()
+BasicLayerManager::EndEmptyTransaction()
 {
   if (!mRoot) {
     return false;
   }
 
-  BeginTransaction();
   return EndTransactionInternal(nsnull, nsnull);
 }
 
 void
 BasicLayerManager::SetRoot(Layer* aLayer)
 {
   NS_ASSERTION(aLayer, "Root can't be null");
   NS_ASSERTION(aLayer->Manager() == this, "Wrong manager");
@@ -2616,42 +2624,37 @@ BasicShadowLayerManager::Mutated(Layer* 
 
 void
 BasicShadowLayerManager::BeginTransactionWithTarget(gfxContext* aTarget)
 {
   NS_ABORT_IF_FALSE(mKeepAlive.IsEmpty(), "uncommitted txn?");
   // If the last transaction was incomplete (a failed DoEmptyTransaction),
   // don't signal a new transaction to ShadowLayerForwarder. Carry on adding
   // to the previous transaction.
-  if (HasShadowManager() && !mTransactionIncomplete) {
+  if (HasShadowManager()) {
     ShadowLayerForwarder::BeginTransaction();
   }
   BasicLayerManager::BeginTransactionWithTarget(aTarget);
 }
 
 void
 BasicShadowLayerManager::EndTransaction(DrawThebesLayerCallback aCallback,
                                         void* aCallbackData)
 {
   BasicLayerManager::EndTransaction(aCallback, aCallbackData);
   ForwardTransaction();
 }
 
 bool
-BasicShadowLayerManager::DoEmptyTransaction()
+BasicShadowLayerManager::EndEmptyTransaction()
 {
-  if (!mRoot) {
-    return false;
-  }
-
-  BasicLayerManager::BeginTransaction();
-  if (!EndTransactionInternal(nsnull, nsnull)) {
+  if (!BasicLayerManager::EndEmptyTransaction()) {
     // Return without calling ForwardTransaction. This leaves the
     // ShadowLayerForwarder transaction open; the following
-    // BeginTransaction/EndTransaction pair will complete it.
+    // EndTransaction will complete it.
     return false;
   }
   ForwardTransaction();
   return true;
 }
 
 void
 BasicShadowLayerManager::ForwardTransaction()
--- a/gfx/layers/basic/BasicLayers.h
+++ b/gfx/layers/basic/BasicLayers.h
@@ -135,19 +135,19 @@ public:
   float XResolution() const { return mXResolution; }
   float YResolution() const { return mYResolution; }
 
   nsIWidget* GetRetainerWidget() { return mWidget; }
   void ClearRetainerWidget() { mWidget = nsnull; }
 
   virtual void BeginTransaction();
   virtual void BeginTransactionWithTarget(gfxContext* aTarget);
+  virtual bool EndEmptyTransaction();
   virtual void EndTransaction(DrawThebesLayerCallback aCallback,
                               void* aCallbackData);
-  virtual bool DoEmptyTransaction();
 
   virtual void SetRoot(Layer* aLayer);
 
   virtual already_AddRefed<ThebesLayer> CreateThebesLayer();
   virtual already_AddRefed<ContainerLayer> CreateContainerLayer();
   virtual already_AddRefed<ImageLayer> CreateImageLayer();
   virtual already_AddRefed<CanvasLayer> CreateCanvasLayer();
   virtual already_AddRefed<ImageContainer> CreateImageContainer();
@@ -236,19 +236,19 @@ class BasicShadowLayerManager : public B
 {
   typedef nsTArray<nsRefPtr<Layer> > LayerRefArray;
 
 public:
   BasicShadowLayerManager(nsIWidget* aWidget);
   virtual ~BasicShadowLayerManager();
 
   virtual void BeginTransactionWithTarget(gfxContext* aTarget);
+  virtual bool EndEmptyTransaction();
   virtual void EndTransaction(DrawThebesLayerCallback aCallback,
                               void* aCallbackData);
-  virtual bool DoEmptyTransaction();
 
   virtual void SetRoot(Layer* aLayer);
 
   virtual void Mutated(Layer* aLayer);
 
   virtual already_AddRefed<ThebesLayer> CreateThebesLayer();
   virtual already_AddRefed<ContainerLayer> CreateContainerLayer();
   virtual already_AddRefed<ImageLayer> CreateImageLayer();
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -457,17 +457,17 @@ FrameLayerBuilder::InternalDestroyDispla
 /* static */ void
 FrameLayerBuilder::DestroyDisplayItemData(nsIFrame* aFrame,
                                           void* aPropertyValue)
 {
   InternalDestroyDisplayItemData(aFrame, aPropertyValue, PR_TRUE);
 }
 
 void
-FrameLayerBuilder::WillBeginRetainedLayerTransaction(LayerManager* aManager)
+FrameLayerBuilder::DidBeginRetainedLayerTransaction(LayerManager* aManager)
 {
   mRetainingManager = aManager;
   LayerManagerData* data = static_cast<LayerManagerData*>
     (aManager->GetUserData(&gLayerManagerUserData));
   if (data) {
     mInvalidateAllThebesContent = data->mInvalidateAllThebesContent;
     mInvalidateAllLayers = data->mInvalidateAllLayers;
   }
--- a/layout/base/FrameLayerBuilder.h
+++ b/layout/base/FrameLayerBuilder.h
@@ -104,20 +104,20 @@ public:
   {
     mNewDisplayItemData.Init();
     mThebesLayerItems.Init();
   }
 
   void Init(nsDisplayListBuilder* aBuilder);
 
   /**
-   * Call this to notify that we are about to start a transaction on the
+   * Call this to notify that we have just started a transaction on the
    * retained layer manager aManager.
    */
-  void WillBeginRetainedLayerTransaction(LayerManager* aManager);
+  void DidBeginRetainedLayerTransaction(LayerManager* aManager);
 
   /**
    * Call this just before we end a transaction on aManager. If aManager
    * is not the retained layer manager then it must be a temporary layer
    * manager that will not be used again.
    */
   void WillEndTransaction(LayerManager* aManager);
 
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -449,26 +449,27 @@ void nsDisplayList::PaintRoot(nsDisplayL
 void nsDisplayList::PaintForFrame(nsDisplayListBuilder* aBuilder,
                                   nsIRenderingContext* aCtx,
                                   nsIFrame* aForFrame,
                                   PRUint32 aFlags) const {
   NS_ASSERTION(mDidComputeVisibility,
                "Must call ComputeVisibility before calling Paint");
 
   nsRefPtr<LayerManager> layerManager;
+  bool allowRetaining = false;
+  bool doBeginTransaction = true;
   if (aFlags & PAINT_USE_WIDGET_LAYERS) {
     nsIFrame* referenceFrame = aBuilder->ReferenceFrame();
     NS_ASSERTION(referenceFrame == nsLayoutUtils::GetDisplayRootFrame(referenceFrame),
                  "Reference frame must be a display root for us to use the layer manager");
     nsIWidget* window = referenceFrame->GetNearestWidget();
     if (window) {
-      bool allowRetaining = true;
       layerManager = window->GetLayerManager(&allowRetaining);
-      if (layerManager && allowRetaining) {
-        aBuilder->LayerBuilder()->WillBeginRetainedLayerTransaction(layerManager);
+      if (layerManager) {
+        doBeginTransaction = !(aFlags & PAINT_EXISTING_TRANSACTION);
       }
     }
   }
   if (!layerManager) {
     if (!aCtx) {
       NS_WARNING("Nowhere to paint into");
       return;
     }
@@ -476,20 +477,25 @@ void nsDisplayList::PaintForFrame(nsDisp
     if (!layerManager)
       return;
   }
 
   if (aFlags & PAINT_FLUSH_LAYERS) {
     FrameLayerBuilder::InvalidateAllLayers(layerManager);
   }
 
-  if (aCtx) {
-    layerManager->BeginTransactionWithTarget(aCtx->ThebesContext());
-  } else {
-    layerManager->BeginTransaction();
+  if (doBeginTransaction) {
+    if (aCtx) {
+      layerManager->BeginTransactionWithTarget(aCtx->ThebesContext());
+    } else {
+      layerManager->BeginTransaction();
+    }
+  }
+  if (allowRetaining) {
+    aBuilder->LayerBuilder()->DidBeginRetainedLayerTransaction(layerManager);
   }
 
   nsRefPtr<ContainerLayer> root = aBuilder->LayerBuilder()->
     BuildContainerLayerFor(aBuilder, layerManager, aForFrame, nsnull, *this);
   if (!root)
     return;
 
   nsPresContext* presContext = aForFrame->PresContext();
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -1025,25 +1025,30 @@ public:
    * ShouldUseWidgetLayerManager() is set, then we will paint using
    * the reference frame's widget's layer manager (and ctx may be null),
    * otherwise we will use a temporary BasicLayerManager and ctx must
    * not be null.
    * 
    * If PAINT_FLUSH_LAYERS is set, we'll force a completely new layer
    * tree to be created for this paint *and* the next paint.
    * 
+   * If PAINT_EXISTING_TRANSACTION is set, the reference frame's widget's
+   * layer manager has already had BeginTransaction() called on it and
+   * we should not call it again.
+   *
    * ComputeVisibility must be called before Paint.
    * 
    * 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_FLUSH_LAYERS = 0x02,
+    PAINT_EXISTING_TRANSACTION = 0x04
   };
   void PaintRoot(nsDisplayListBuilder* aBuilder, nsIRenderingContext* 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, nsIRenderingContext* aCtx,
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -1548,16 +1548,19 @@ nsLayoutUtils::PaintFrame(nsIRenderingCo
 
       // If we're finished building display list items for painting of the outermost
       // pres shell, notify the widget about any toolbars we've encountered.
       nsIWidget_MOZILLA_2_0_BRANCH* widget2 =
         static_cast<nsIWidget_MOZILLA_2_0_BRANCH*>(widget);
       widget2->UpdateThemeGeometries(builder.GetThemeGeometries());
     }
   }
+  if (aFlags & PAINT_EXISTING_TRANSACTION) {
+    flags |= nsDisplayList::PAINT_EXISTING_TRANSACTION;
+  }
 
   list.PaintRoot(&builder, aRenderingContext, flags);
 
 #ifdef DEBUG
   if (gDumpPaintList) {
     fprintf(stderr, "Painting --- after optimization:\n");
     nsFrame::PrintDisplayList(&builder, list);
 
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -555,17 +555,18 @@ public:
   enum {
     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_TO_WINDOW = 0x80,
+    PAINT_EXISTING_TRANSACTION = 0x100
   };
 
   /**
    * 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
@@ -583,16 +584,19 @@ public:
    * and we will use the frame's widget's layer manager to paint
    * even if aRenderingContext is non-null. This is useful if you want
    * to force rendering to use the widget's layer manager for testing
    * or speed. PAINT_WIDGET_LAYERS must be set if aRenderingContext is null.
    * If PAINT_DOCUMENT_RELATIVE is used, the visible region is interpreted
    * as being relative to the document.  (Normally it's relative to the CSS
    * viewport.) PAINT_TO_WINDOW sets painting to window to true on the display
    * list builder even if we can't tell that we are painting to the window.
+   * If PAINT_EXISTING_TRANSACTION is set, then BeginTransaction() has already
+   * been called on aFrame's widget's layer manager and should not be
+   * called again.
    *
    * So there are three possible behaviours:
    * 1) PAINT_WIDGET_LAYERS is set and aRenderingContext is null; we paint
    * by calling BeginTransaction on the widget's layer manager
    * 2) PAINT_WIDGET_LAYERS is set and aRenderingContext is non-null; we
    * paint by calling BeginTransactionWithTarget on the widget's layer
    * maanger
    * 3) PAINT_WIDGET_LAYERS is not set and aRenderingContext is non-null;
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -6111,20 +6111,21 @@ PresShell::Paint(nsIView*           aDis
   nsPresContext* presContext = GetPresContext();
   AUTO_LAYOUT_PHASE_ENTRY_POINT(presContext, Paint);
 
   nsIFrame* frame = aPaintDefaultBackground
       ? nsnull : static_cast<nsIFrame*>(aDisplayRoot->GetClientData());
 
   LayerManager* layerManager = aWidgetToPaint->GetLayerManager();
   NS_ASSERTION(layerManager, "Must be in paint event");
+  layerManager->BeginTransaction();
 
   if (frame) {
     if (!(frame->GetStateBits() & NS_FRAME_UPDATE_LAYER_TREE)) {
-      if (layerManager->DoEmptyTransaction())
+      if (layerManager->EndEmptyTransaction())
         return NS_OK;
     }
     frame->RemoveStateBits(NS_FRAME_UPDATE_LAYER_TREE);
   }
 
   nscolor bgcolor = ComputeBackstopColor(aDisplayRoot);
 
   if (frame && aViewToPaint == aDisplayRoot) {
@@ -6134,30 +6135,30 @@ PresShell::Paint(nsIView*           aDis
     // (see FrameLayerBuilder).
     frame->BeginDeferringInvalidatesForDisplayRoot(aDirtyRegion);
 
     // We can paint directly into the widget using its layer manager.
     // When we get rid of child widgets, this will be the only path we
     // need. (aPaintDefaultBackground will never be needed since the
     // chrome can always paint a default background.)
     nsLayoutUtils::PaintFrame(nsnull, frame, aDirtyRegion, bgcolor,
-                              nsLayoutUtils::PAINT_WIDGET_LAYERS);
+                              nsLayoutUtils::PAINT_WIDGET_LAYERS |
+                              nsLayoutUtils::PAINT_EXISTING_TRANSACTION);
 
     frame->EndDeferringInvalidatesForDisplayRoot();
     presContext->NotifyDidPaintForSubtree();
     return NS_OK;
   }
 
   if (frame) {
     // Defer invalidates that are triggered during painting, and discard
     // invalidates of areas that are already being repainted.
     frame->BeginDeferringInvalidatesForDisplayRoot(aDirtyRegion);
   }
 
-  layerManager->BeginTransaction();
   nsRefPtr<ThebesLayer> root = layerManager->CreateThebesLayer();
   if (root) {
     root->SetVisibleRegion(aIntDirtyRegion);
     layerManager->SetRoot(root);
   }
   if (!frame) {
     bgcolor = NS_ComposeColors(bgcolor, mCanvasBackgroundColor);
   }