Bug 599359, part 2: When we allocate a new front/back buffer pair, pre-fill the new front buffer with what we painted into the back buffer to stabilize the first Swap(). r=vlad a=blocking-fennec
authorChris Jones <jones.chris.g@gmail.com>
Tue, 28 Sep 2010 17:05:30 -0500
changeset 54702 6a5d2399012c45e517f3c99f56f3979a2f3503db
parent 54701 9c47254208d0547e251b9bb5c7f58f3c3496b99c
child 54703 f94f0477848e450b6df36df0f2b1fff112467bd6
push idunknown
push userunknown
push dateunknown
reviewersvlad, blocking-fennec
bugs599359
milestone2.0b7pre
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 599359, part 2: When we allocate a new front/back buffer pair, pre-fill the new front buffer with what we painted into the back buffer to stabilize the first Swap(). r=vlad a=blocking-fennec
gfx/layers/basic/BasicLayers.cpp
gfx/layers/ipc/PLayers.ipdl
gfx/layers/ipc/ShadowLayers.cpp
gfx/layers/ipc/ShadowLayers.h
gfx/layers/ipc/ShadowLayersParent.cpp
--- a/gfx/layers/basic/BasicLayers.cpp
+++ b/gfx/layers/basic/BasicLayers.cpp
@@ -1427,16 +1427,20 @@ private:
 
   NS_OVERRIDE virtual already_AddRefed<gfxASurface>
   CreateBuffer(Buffer::ContentType aType, const nsIntSize& aSize);
 
   // This describes the gfxASurface we hand to mBuffer.  We keep a
   // copy of the descriptor here so that we can call
   // DestroySharedSurface() on the descriptor.
   SurfaceDescriptor mBackBuffer;
+  // When we allocate a new front buffer, we keep a temporary record
+  // of it until reach PaintBuffer().  Then we pre-fill back->front
+  // and destroy our record.
+  SurfaceDescriptor mNewFrontBuffer;
   nsIntSize mBufferSize;
 };
  
 void
 BasicShadowableThebesLayer::PaintBuffer(gfxContext* aContext,
                                         const nsIntRegion& aRegionToDraw,
                                         const nsIntRegion& aRegionToInvalidate,
                                         LayerManager::DrawThebesLayerCallback aCallback,
@@ -1444,18 +1448,49 @@ BasicShadowableThebesLayer::PaintBuffer(
 {
   Base::PaintBuffer(aContext, aRegionToDraw, aRegionToInvalidate,
                     aCallback, aCallbackData);
 
   if (HasShadow()) {
     NS_ABORT_IF_FALSE(IsSurfaceDescriptorValid(mBackBuffer),
                       "should have a back buffer by now");
 
+    nsIntRegion updatedRegion = aRegionToDraw;
+    if (IsSurfaceDescriptorValid(mNewFrontBuffer)) {
+      // We just allocated a new buffer pair.  We want to "pre-fill"
+      // the new front buffer by copying to it what we just painted
+      // into the back buffer.  This starts off our Swap()s from a
+      // stable base: the first swap will return the same valid region
+      // as our new back buffer.  Thereafter, we only need to
+      // invalidate what was painted into the back buffer.
+      nsRefPtr<gfxASurface> frontBuffer =
+        BasicManager()->OpenDescriptor(mNewFrontBuffer);
+      nsRefPtr<gfxASurface> backBuffer =
+        BasicManager()->OpenDescriptor(mBackBuffer);
+
+      nsRefPtr<gfxContext> ctx = new gfxContext(frontBuffer);
+      ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
+      ctx->DrawSurface(backBuffer, backBuffer->GetSize());
+
+      BasicManager()->CreatedThebesBuffer(BasicManager()->Hold(this),
+                                          mValidRegion,
+                                          mXResolution,
+                                          mYResolution,
+                                          mBuffer.BufferRect(),
+                                          mNewFrontBuffer);
+
+      // Clear temporary record of new front buffer
+      mNewFrontBuffer = SurfaceDescriptor();
+      // And pretend that we didn't update anything, in order to
+      // stabilize the first swap.
+      updatedRegion.SetEmpty();
+    }
+
     BasicManager()->PaintedThebesBuffer(BasicManager()->Hold(this),
-                                        aRegionToDraw,
+                                        updatedRegion,
                                         mBuffer.BufferRect(),
                                         mBuffer.BufferRotation(),
                                         mBackBuffer);
   }
 }
 
 already_AddRefed<gfxASurface>
 BasicShadowableThebesLayer::CreateBuffer(Buffer::ContentType aType,
@@ -1466,29 +1501,25 @@ BasicShadowableThebesLayer::CreateBuffer
   }
 
   if (IsSurfaceDescriptorValid(mBackBuffer)) {
     BasicManager()->DestroyedThebesBuffer(BasicManager()->Hold(this),
                                           mBackBuffer);
     mBackBuffer = SurfaceDescriptor();
   }
 
-  SurfaceDescriptor tmpFront;
   // XXX error handling
+  NS_ABORT_IF_FALSE(!IsSurfaceDescriptorValid(mNewFrontBuffer),
+                    "Bad!  Did we create a buffer twice without painting?");
   if (!BasicManager()->AllocDoubleBuffer(gfxIntSize(aSize.width, aSize.height),
                                          aType,
-                                         &tmpFront,
+                                         &mNewFrontBuffer,
                                          &mBackBuffer))
     NS_RUNTIMEABORT("creating ThebesLayer 'back buffer' failed!");
   mBufferSize = aSize;
-
-  nsIntRect bufRect = mVisibleRegion.GetBounds();
-  BasicManager()->CreatedThebesBuffer(BasicManager()->Hold(this),
-                                      bufRect,
-                                      tmpFront);
   return BasicManager()->OpenDescriptor(mBackBuffer);
 }
 
 
 class BasicShadowableImageLayer : public BasicImageLayer,
                                   public BasicShadowableLayer
 {
 public:
@@ -1764,16 +1795,20 @@ public:
   virtual ~BasicShadowThebesLayer()
   {
     // If Disconnect() wasn't called on us, then we assume that the
     // remote side shut down and IPC is disconnected, so we let IPDL
     // clean up our front surface Shmem.
     MOZ_COUNT_DTOR(BasicShadowThebesLayer);
   }
 
+  virtual void SetFrontBuffer(const ThebesBuffer& aNewFront,
+                              const nsIntRegion& aValidRegion,
+                              float aXResolution, float aYResolution);
+
   virtual void SetValidRegion(const nsIntRegion& aRegion)
   {
     mOldValidRegion = mValidRegion;
     ShadowThebesLayer::SetValidRegion(aRegion);
   }
 
   virtual void SetResolution(float aXResolution, float aYResolution)
   {
@@ -1825,16 +1860,35 @@ private:
   // Then when we Swap() back/front buffers, we can return these
   // parameters to our partner (adjusted as needed).
   nsIntRegion mOldValidRegion;
   float mOldXResolution;
   float mOldYResolution;
 };
 
 void
+BasicShadowThebesLayer::SetFrontBuffer(const ThebesBuffer& aNewFront,
+                                       const nsIntRegion& aValidRegion,
+                                       float aXResolution, float aYResolution)
+{
+  mValidRegion = mOldValidRegion = aValidRegion;
+  mXResolution = mOldXResolution = aXResolution;
+  mYResolution = mOldYResolution = aYResolution;
+  nsRefPtr<gfxASurface> newFrontBuffer =
+    BasicManager()->OpenDescriptor(aNewFront.buffer());
+
+  nsRefPtr<gfxASurface> unused;
+  nsIntRect unusedRect;
+  nsIntPoint unusedRotation;
+  mFrontBuffer.Swap(newFrontBuffer, aNewFront.rect(), aNewFront.rotation(),
+                    getter_AddRefs(unused), &unusedRect, &unusedRotation);
+  mFrontBufferDescriptor = aNewFront.buffer();
+}
+
+void
 BasicShadowThebesLayer::Swap(const ThebesBuffer& aNewFront,
                              const nsIntRegion& aUpdatedRegion,
                              ThebesBuffer* aNewBack,
                              nsIntRegion* aNewBackValidRegion,
                              float* aNewXResolution, float* aNewYResolution)
 {
   // This code relies on Swap() arriving *after* attribute mutations.
   aNewBack->buffer() = mFrontBufferDescriptor;
--- a/gfx/layers/ipc/PLayers.ipdl
+++ b/gfx/layers/ipc/PLayers.ipdl
@@ -77,20 +77,28 @@ struct OpCreateCanvasLayer     { PLayer 
 // buffer that only contains (transparent) black pixels just so that
 // we can swap it back after the first OpPaint without a special case.
 
 union SurfaceDescriptor {
   Shmem;
   SurfaceDescriptorX11;
 };
 
+struct ThebesBuffer {
+  SurfaceDescriptor buffer;
+  nsIntRect rect;
+  nsIntPoint rotation; 
+};
+
 struct OpCreateThebesBuffer {
   PLayer layer;
-  nsIntRect bufferRect;
-  SurfaceDescriptor initialFront;
+  ThebesBuffer initialFront;
+  nsIntRegion frontValidRegion;
+  float xResolution;
+  float yResolution;
 };
 struct OpDestroyThebesFrontBuffer { PLayer layer; };
 
 struct OpCreateCanvasBuffer {
   PLayer layer;
   nsIntSize size;
   Shmem initialFront;
 };
@@ -147,21 +155,16 @@ struct OpSetLayerAttributes {
 // Monkey with the tree structure
 struct OpSetRoot      { PLayer root; };
 struct OpInsertAfter  { PLayer container; PLayer childLayer; PLayer after; };
 struct OpAppendChild  { PLayer container; PLayer childLayer; };
 struct OpRemoveChild  { PLayer container; PLayer childLayer; };
 
 
 // Paint (buffer update)
-struct ThebesBuffer {
-  SurfaceDescriptor buffer;
-  nsIntRect rect;
-  nsIntPoint rotation; 
-};
 struct OpPaintThebesBuffer {
   PLayer layer;
   ThebesBuffer newFrontBuffer;
   nsIntRegion updatedRegion;
 };
 
 struct OpPaintCanvas  {
   PLayer layer;
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -178,22 +178,29 @@ ShadowLayerForwarder::CreatedColorLayer(
 void
 ShadowLayerForwarder::CreatedCanvasLayer(ShadowableLayer* aCanvas)
 {
   CreatedLayer<OpCreateCanvasLayer>(mTxn, aCanvas);
 }
 
 void
 ShadowLayerForwarder::CreatedThebesBuffer(ShadowableLayer* aThebes,
-                                          nsIntRect aBufferRect,
+                                          const nsIntRegion& aFrontValidRegion,
+                                          float aXResolution,
+                                          float aYResolution,
+                                          const nsIntRect& aBufferRect,
                                           const SurfaceDescriptor& aTempFrontBuffer)
 {
   mTxn->AddEdit(OpCreateThebesBuffer(NULL, Shadow(aThebes),
-                                     aBufferRect,
-                                     aTempFrontBuffer));
+                                     ThebesBuffer(aTempFrontBuffer,
+                                                  aBufferRect,
+                                                  nsIntPoint(0, 0)),
+                                     aFrontValidRegion,
+                                     aXResolution,
+                                     aYResolution));
 }
 
 void
 ShadowLayerForwarder::CreatedImageBuffer(ShadowableLayer* aImage,
                                          nsIntSize aSize,
                                          gfxSharedImageSurface* aTempFrontSurface)
 {
   mTxn->AddEdit(OpCreateImageBuffer(NULL, Shadow(aImage),
--- a/gfx/layers/ipc/ShadowLayers.h
+++ b/gfx/layers/ipc/ShadowLayers.h
@@ -138,23 +138,26 @@ public:
    * specificed layer.  |aInitialFrontSurface| is one of the newly
    * created, transparent black buffers for the layer; the "real"
    * layer holds on to the other as its back buffer.  We send it
    * across on buffer creation to avoid special cases in the buffer
    * swapping logic for Painted*() operations.
    *
    * It is expected that Created*Buffer() will be followed by a
    * Painted*Buffer() in the same transaction, so that
-   * |aInitialFrontBuffer| is never actually drawn to screen.
+   * |aInitialFrontBuffer| is never actually drawn to screen.  It is
+   * OK if it is drawn though.
    */
   /**
    * |aBufferRect| is the screen rect covered by |aInitialFrontBuffer|.
    */
   void CreatedThebesBuffer(ShadowableLayer* aThebes,
-                           nsIntRect aBufferRect,
+                           const nsIntRegion& aFrontValidRegion,
+                           float aXResolution, float aYResolution,
+                           const nsIntRect& aBufferRect,
                            const SurfaceDescriptor& aInitialFrontBuffer);
   /**
    * For the next two methods, |aSize| is the size of
    * |aInitialFrontSurface|.
    */
   void CreatedImageBuffer(ShadowableLayer* aImage,
                           nsIntSize aSize,
                           gfxSharedImageSurface* aInitialFrontSurface);
@@ -389,16 +392,26 @@ public:
    * CONSTRUCTION PHASE ONLY
    */
   void SetParent(PLayersParent* aParent)
   {
     NS_ABORT_IF_FALSE(!mAllocator, "Stomping parent?");
     mAllocator = aParent;
   }
 
+  /**
+   * CONSTRUCTION PHASE ONLY
+   *
+   * Override the front buffer and its valid region with the specified
+   * values.  This is called when a new buffer has been created.
+   */
+  virtual void SetFrontBuffer(const ThebesBuffer& aNewFront,
+                              const nsIntRegion& aValidRegion,
+                              float aXResolution, float aYResolution) = 0;
+
   virtual void InvalidateRegion(const nsIntRegion& aRegion)
   {
     NS_RUNTIMEABORT("ShadowThebesLayers can't fill invalidated regions");
   }
 
   /**
    * CONSTRUCTION PHASE ONLY
    */
--- a/gfx/layers/ipc/ShadowLayersParent.cpp
+++ b/gfx/layers/ipc/ShadowLayersParent.cpp
@@ -190,22 +190,18 @@ ShadowLayersParent::RecvUpdate(const nsT
     }
     case Edit::TOpCreateThebesBuffer: {
       MOZ_LAYERS_LOG(("[ParentSide] CreateThebesBuffer"));
 
       const OpCreateThebesBuffer& otb = edit.get_OpCreateThebesBuffer();
       ShadowThebesLayer* thebes = static_cast<ShadowThebesLayer*>(
         AsShadowLayer(otb)->AsLayer());
 
-      ThebesBuffer unusedBuffer;
-      nsIntRegion unusedRegion; float unusedXRes, unusedYRes;
-      thebes->Swap(
-        ThebesBuffer(otb.initialFront(), otb.bufferRect(), nsIntPoint(0, 0)),
-        unusedRegion,
-        &unusedBuffer, &unusedRegion, &unusedXRes, &unusedYRes);
+      thebes->SetFrontBuffer(otb.initialFront(), otb.frontValidRegion(),
+                             otb.xResolution(), otb.yResolution());
 
       break;
     }
     case Edit::TOpCreateCanvasBuffer: {
       MOZ_LAYERS_LOG(("[ParentSide] CreateCanvasBuffer"));
 
       const OpCreateCanvasBuffer& ocb = edit.get_OpCreateCanvasBuffer();
       ShadowCanvasLayer* canvas = static_cast<ShadowCanvasLayer*>(