Revert to using EnsureBackBufferIfFrontBuffer (bug 1416921, r=bas) draft
authorRyan Hunt <rhunt@eqrion.net>
Fri, 17 Nov 2017 01:37:01 -0500
changeset 700425 b23201008943bf70b1705047dde59f24bf0d0502
parent 699635 010374bce60670cf1348150fe493d0193318d4f6
child 740856 9443d029c455638fdcbbd3a102bdd067dc8d78c6
push id89818
push userbmo:rhunt@eqrion.net
push dateMon, 20 Nov 2017 04:32:25 +0000
reviewersbas
bugs1416921
milestone59.0a1
Revert to using EnsureBackBufferIfFrontBuffer (bug 1416921, r=bas) This commit reverts DoubleBufferedContentClient to creating a back buffer if there is an existing front buffer. This reverts back to a sequence of a finalize frame, unrotate, and initialize new back buffer. When we are not async painting we do each buffer operation immediately. If we are async painting, then we collect all of the buffer operations and perform them on the paint thread. Note on locking: The destination for buffer copies must always be opened with the async write flag, and the source must be unlocked. MozReview-Commit-ID: Gu4EcblY7Sg
gfx/layers/PaintThread.cpp
gfx/layers/PaintThread.h
gfx/layers/client/ContentClient.cpp
gfx/layers/client/ContentClient.h
--- a/gfx/layers/PaintThread.cpp
+++ b/gfx/layers/PaintThread.cpp
@@ -36,46 +36,62 @@ bool
 CapturedBufferState::Unrotate::UnrotateBuffer()
 {
   return mBuffer->UnrotateBufferTo(mParameters);
 }
 
 bool
 CapturedBufferState::PrepareBuffer()
 {
-  return (!mBufferCopy || mBufferCopy->CopyBuffer()) &&
-         (!mBufferUnrotate || mBufferUnrotate->UnrotateBuffer());
+  return (!mBufferFinalize || mBufferFinalize->CopyBuffer()) &&
+         (!mBufferUnrotate || mBufferUnrotate->UnrotateBuffer()) &&
+         (!mBufferInitialize || mBufferInitialize->CopyBuffer());
 }
 
 void
 CapturedBufferState::GetTextureClients(nsTArray<RefPtr<TextureClient>>& aTextureClients)
 {
-  if (mBufferCopy) {
-    if (TextureClient* source = mBufferCopy->mSource->GetClient()) {
+  if (mBufferFinalize) {
+    if (TextureClient* source = mBufferFinalize->mSource->GetClient()) {
       aTextureClients.AppendElement(source);
     }
-    if (TextureClient* sourceOnWhite = mBufferCopy->mSource->GetClientOnWhite()) {
+    if (TextureClient* sourceOnWhite = mBufferFinalize->mSource->GetClientOnWhite()) {
       aTextureClients.AppendElement(sourceOnWhite);
     }
-    if (TextureClient* destination = mBufferCopy->mDestination->GetClient()) {
+    if (TextureClient* destination = mBufferFinalize->mDestination->GetClient()) {
       aTextureClients.AppendElement(destination);
     }
-    if (TextureClient* destinationOnWhite = mBufferCopy->mDestination->GetClientOnWhite()) {
+    if (TextureClient* destinationOnWhite = mBufferFinalize->mDestination->GetClientOnWhite()) {
       aTextureClients.AppendElement(destinationOnWhite);
     }
   }
 
   if (mBufferUnrotate) {
     if (TextureClient* client = mBufferUnrotate->mBuffer->GetClient()) {
       aTextureClients.AppendElement(client);
     }
     if (TextureClient* clientOnWhite = mBufferUnrotate->mBuffer->GetClientOnWhite()) {
       aTextureClients.AppendElement(clientOnWhite);
     }
   }
+
+  if (mBufferInitialize) {
+    if (TextureClient* source = mBufferInitialize->mSource->GetClient()) {
+      aTextureClients.AppendElement(source);
+    }
+    if (TextureClient* sourceOnWhite = mBufferInitialize->mSource->GetClientOnWhite()) {
+      aTextureClients.AppendElement(sourceOnWhite);
+    }
+    if (TextureClient* destination = mBufferInitialize->mDestination->GetClient()) {
+      aTextureClients.AppendElement(destination);
+    }
+    if (TextureClient* destinationOnWhite = mBufferInitialize->mDestination->GetClientOnWhite()) {
+      aTextureClients.AppendElement(destinationOnWhite);
+    }
+  }
 }
 
 StaticAutoPtr<PaintThread> PaintThread::sSingleton;
 StaticRefPtr<nsIThread> PaintThread::sThread;
 PlatformThreadId PaintThread::sThreadId;
 
 // RAII make sure we clean up and restore our draw targets
 // when we paint async.
--- a/gfx/layers/PaintThread.h
+++ b/gfx/layers/PaintThread.h
@@ -97,18 +97,19 @@ public:
    * Prepares the rotated buffers for painting by copying a previous frame
    * into the buffer and/or unrotating the pixels and returns whether the
    * operations were successful. If this fails a new buffer should be created
    * for the frame.
    */
   bool PrepareBuffer();
   void GetTextureClients(nsTArray<RefPtr<TextureClient>>& aTextureClients);
 
-  Maybe<Copy> mBufferCopy;
+  Maybe<Copy> mBufferFinalize;
   Maybe<Unrotate> mBufferUnrotate;
+  Maybe<Copy> mBufferInitialize;
 
 protected:
   ~CapturedBufferState() {}
 };
 
 typedef bool (*PrepDrawTargetForPaintingCallback)(CapturedPaintState* aPaintState);
 
 class CompositorBridgeChild;
--- a/gfx/layers/client/ContentClient.cpp
+++ b/gfx/layers/client/ContentClient.cpp
@@ -155,72 +155,84 @@ ContentClient::BeginPaint(PaintedLayer* 
                          !(aLayer->Manager()->AsWebRenderLayerManager());
   bool canDrawRotated = aFlags & PAINT_CAN_DRAW_ROTATED;
   bool asyncPaint = (aFlags & PAINT_ASYNC);
 
   IntRect drawBounds = result.mRegionToDraw.GetBounds();
   OpenMode lockMode = asyncPaint ? OpenMode::OPEN_READ_ASYNC_WRITE
                                  : OpenMode::OPEN_READ_WRITE;
 
+  if (asyncPaint) {
+    result.mBufferState = new CapturedBufferState();
+  }
+
+  if (mBuffer) {
+    if (mBuffer->Lock(lockMode)) {
+      // Do not modify result.mRegionToDraw or result.mContentType after this call.
+      Maybe<CapturedBufferState::Copy> bufferFinalize =
+        FinalizeFrame(result.mRegionToDraw);
+
+      if (asyncPaint) {
+        result.mBufferState->mBufferFinalize = Move(bufferFinalize);
+      } else if (bufferFinalize) {
+        bufferFinalize->CopyBuffer();
+      }
+    } else {
+      result.mRegionToDraw = dest.mNeededRegion;
+      dest.mCanReuseBuffer = false;
+      Clear();
+    }
+  }
+
   if (dest.mCanReuseBuffer) {
     MOZ_ASSERT(mBuffer);
 
     bool canReuseBuffer = false;
 
-    if (mBuffer->Lock(lockMode)) {
-      RefPtr<CapturedBufferState> bufferState = new CapturedBufferState();
-
-      // Do not modify result.mRegionToDraw or result.mContentType after this call.
-      FinalizeFrame(result.mRegionToDraw, bufferState);
-
-      auto newParameters = mBuffer->AdjustedParameters(dest.mBufferRect);
+    auto newParameters = mBuffer->AdjustedParameters(dest.mBufferRect);
+    Maybe<CapturedBufferState::Unrotate> bufferUnrotate = Nothing();
 
-      if ((!canHaveRotation && newParameters.IsRotated()) ||
-          (!canDrawRotated && newParameters.RectWrapsBuffer(drawBounds))) {
-        bufferState->mBufferUnrotate = Some(CapturedBufferState::Unrotate {
-          newParameters,
-          mBuffer->ShallowCopy(),
-        });
-      }
+    if ((!canHaveRotation && newParameters.IsRotated()) ||
+        (!canDrawRotated && newParameters.RectWrapsBuffer(drawBounds))) {
+      bufferUnrotate = Some(CapturedBufferState::Unrotate {
+        newParameters,
+        mBuffer->ShallowCopy(),
+      });
+    }
 
-      // If we're async painting then return the buffer state to
-      // be dispatched to the paint thread, otherwise do it now
-      if (asyncPaint) {
-        // We cannot do a buffer unrotate if the buffer is already rotated
-        // and we're async painting as that may fail
-        if (!bufferState->mBufferUnrotate ||
-            mBuffer->BufferRotation() == IntPoint(0,0)) {
-          result.mBufferState = bufferState;
+    // If we're async painting then return the buffer state to
+    // be dispatched to the paint thread, otherwise do it now
+    if (asyncPaint) {
+      // We cannot do a buffer unrotate if the buffer is already rotated
+      // and we're async painting as that may fail
+      if (!bufferUnrotate ||
+          mBuffer->BufferRotation() == IntPoint(0,0)) {
+        result.mBufferState->mBufferUnrotate = Move(bufferUnrotate);
 
-          // We can then assume that preparing the buffer will always
-          // succeed and update our parameters unconditionally
-          if (bufferState->mBufferUnrotate) {
-            newParameters.SetUnrotated();
-          }
-          mBuffer->SetParameters(newParameters);
-          canReuseBuffer = true;
+        // We can then assume that preparing the buffer will always
+        // succeed and update our parameters unconditionally
+        if (result.mBufferState->mBufferUnrotate) {
+          newParameters.SetUnrotated();
         }
-      } else {
-        if (bufferState->PrepareBuffer()) {
-          if (bufferState->mBufferUnrotate) {
-            newParameters.SetUnrotated();
-          }
-          mBuffer->SetParameters(newParameters);
-          canReuseBuffer = true;
+        mBuffer->SetParameters(newParameters);
+        canReuseBuffer = true;
+      }
+    } else {
+      if (!bufferUnrotate || bufferUnrotate->UnrotateBuffer()) {
+        if (bufferUnrotate) {
+          newParameters.SetUnrotated();
         }
+        mBuffer->SetParameters(newParameters);
+        canReuseBuffer = true;
       }
     }
 
     if (!canReuseBuffer) {
-      if (mBuffer->IsLocked()) {
-        mBuffer->Unlock();
-      }
       dest.mBufferRect = ComputeBufferRect(dest.mNeededRegion.GetBounds());
       dest.mCanReuseBuffer = false;
-      dest.mMustRemoveFrontBuffer = true;
     }
   }
 
   NS_ASSERTION(!(aFlags & PAINT_WILL_RESAMPLE) || dest.mBufferRect == dest.mNeededRegion.GetBounds(),
                "If we're resampling, we need to validate the entire buffer");
 
   // We never had a buffer, the buffer wasn't big enough, the content changed
   // types, or we failed to unrotate the buffer when requested. In any case,
@@ -238,56 +250,56 @@ ContentClient::BeginPaint(PaintedLayer* 
     if (!newBuffer) {
       if (Factory::ReasonableSurfaceSize(IntSize(dest.mBufferRect.Width(), dest.mBufferRect.Height()))) {
         gfxCriticalNote << "Failed buffer for "
                         << dest.mBufferRect.x << ", "
                         << dest.mBufferRect.y << ", "
                         << dest.mBufferRect.Width() << ", "
                         << dest.mBufferRect.Height();
       }
+      Clear();
       return result;
     }
 
     if (!newBuffer->Lock(lockMode)) {
       gfxCriticalNote << "Failed to lock new back buffer.";
+      Clear();
       return result;
     }
 
     // If we have an existing front buffer, copy it into the new back buffer
-    if (RefPtr<RotatedBuffer> frontBuffer = GetFrontBuffer()) {
+    if (mBuffer) {
+      if (mBuffer->IsLocked()) {
+        mBuffer->Unlock();
+      }
+
       nsIntRegion updateRegion = newBuffer->BufferRect();
       updateRegion.Sub(updateRegion, result.mRegionToDraw);
 
       if (!updateRegion.IsEmpty()) {
-        RefPtr<CapturedBufferState> bufferState = new CapturedBufferState();
-
-        bufferState->mBufferCopy = Some(CapturedBufferState::Copy {
-          frontBuffer->ShallowCopy(),
+        auto bufferInitialize = CapturedBufferState::Copy {
+          mBuffer->ShallowCopy(),
           newBuffer->ShallowCopy(),
           updateRegion.GetBounds(),
-        });
+        };
 
         // If we're async painting then return the buffer state to
         // be dispatched to the paint thread, otherwise do it now
         if (asyncPaint) {
-          MOZ_ASSERT(!result.mBufferState);
-          result.mBufferState = bufferState;
+          result.mBufferState->mBufferInitialize = Some(Move(bufferInitialize));
         } else {
-          if (!bufferState->PrepareBuffer()) {
+          if (!bufferInitialize.CopyBuffer()) {
             gfxCriticalNote << "Failed to copy front buffer to back buffer.";
             return result;
           }
         }
       }
-
-      if (dest.mMustRemoveFrontBuffer) {
-        Clear();
-      }
     }
 
+    Clear();
     mBuffer = newBuffer;
   }
 
   NS_ASSERTION(canHaveRotation || mBuffer->BufferRotation() == IntPoint(0,0),
                "Rotation disabled, but we have nonzero rotation?");
 
   nsIntRegion invalidate;
   invalidate.Sub(aLayer->GetValidRegion(), dest.mBufferRect);
@@ -414,27 +426,24 @@ ContentClient::PrepareDrawTargetForPaint
 ContentClient::BufferDecision
 ContentClient::CalculateBufferForPaint(PaintedLayer* aLayer,
                                        uint32_t aFlags)
 {
   gfxContentType layerContentType =
     aLayer->CanUseOpaqueSurface() ? gfxContentType::COLOR :
                                     gfxContentType::COLOR_ALPHA;
 
-  RefPtr<RotatedBuffer> frontBuffer = GetFrontBuffer();
-
   SurfaceMode mode;
   gfxContentType contentType;
   IntRect destBufferRect;
   nsIntRegion neededRegion;
   nsIntRegion validRegion = aLayer->GetValidRegion();
 
   bool canReuseBuffer = !!mBuffer;
   bool canKeepBufferContents = true;
-  bool mustRemoveFrontBuffer = false;
 
   while (true) {
     mode = aLayer->GetSurfaceMode();
     neededRegion = aLayer->GetVisibleRegion().ToUnknownRegion();
     canReuseBuffer = canReuseBuffer && ValidBufferSize(mBufferSizePolicy,
                                                        mBuffer->BufferRect().Size(),
                                                        neededRegion.GetBounds().Size());
     contentType = layerContentType;
@@ -446,32 +455,17 @@ ContentClient::CalculateBufferForPaint(P
       } else if (neededRegion.GetBounds().Size() <= mBuffer->BufferRect().Size()) {
         // The buffer's big enough but doesn't contain everything that's
         // going to be visible. We'll move it.
         destBufferRect = IntRect(neededRegion.GetBounds().TopLeft(), mBuffer->BufferRect().Size());
       } else {
         destBufferRect = neededRegion.GetBounds();
       }
     } else {
-      // We won't be reusing the buffer. Compute a new rect.
-      if (frontBuffer) {
-        // We must create a buffer that is the same size as the front buffer,
-        // or else we need to remove the front buffer
-        if (ValidBufferSize(mBufferSizePolicy,
-                            frontBuffer->BufferRect().Size(),
-                            neededRegion.GetBounds().Size())) {
-          destBufferRect = frontBuffer->BufferRect();
-          destBufferRect.MoveTo(neededRegion.GetBounds().TopLeft());
-        } else {
-          destBufferRect = ComputeBufferRect(neededRegion.GetBounds());
-          mustRemoveFrontBuffer = true;
-        }
-      } else {
-        destBufferRect = ComputeBufferRect(neededRegion.GetBounds());
-      }
+      destBufferRect = ComputeBufferRect(neededRegion.GetBounds());
     }
 
     if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
 #if defined(MOZ_GFX_OPTIMIZE_MOBILE)
       mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
 #else
       if (!aLayer->GetParent() ||
           !aLayer->GetParent()->SupportsComponentAlphaChildren() ||
@@ -501,67 +495,49 @@ ContentClient::CalculateBufferForPaint(P
 
     // If we have an existing buffer, but the content type has changed or we
     // have transitioned into/out of component alpha, then we need to recreate it.
     bool needsComponentAlpha = (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA);
     bool backBufferChangedSurface = mBuffer &&
                                     (contentType != mBuffer->GetContentType() ||
                                      needsComponentAlpha != mBuffer->HaveBufferOnWhite());
     if (canKeepBufferContents && backBufferChangedSurface) {
-      // We cannot reuse the back buffer if the surface type or content type
-      // changed. We may have to also invalidate, but only if the front buffer
-      // also changed.
-      canReuseBuffer = false;
-    }
-    bool frontBufferChangedSurface = frontBuffer &&
-                                     (contentType != frontBuffer->GetContentType() ||
-                                      needsComponentAlpha != frontBuffer->HaveBufferOnWhite());
-    if (canKeepBufferContents && frontBufferChangedSurface) {
       // Restart the decision process; we won't re-enter since we guard on
       // being able to keep the buffer contents.
       canReuseBuffer = false;
       canKeepBufferContents = false;
       validRegion.SetEmpty();
-      continue;
     }
-
     break;
   }
 
   NS_ASSERTION(destBufferRect.Contains(neededRegion.GetBounds()),
                "Destination rect doesn't contain what we need to paint");
 
   BufferDecision dest;
   dest.mNeededRegion = Move(neededRegion);
   dest.mValidRegion = Move(validRegion);
   dest.mBufferRect = destBufferRect;
   dest.mBufferMode = mode;
   dest.mBufferContentType = contentType;
   dest.mCanReuseBuffer = canReuseBuffer;
   dest.mCanKeepBufferContents = canKeepBufferContents;
-  dest.mMustRemoveFrontBuffer = mustRemoveFrontBuffer;
   return dest;
 }
 
 bool
 ContentClient::ValidBufferSize(BufferSizePolicy aPolicy,
                                const gfx::IntSize& aBufferSize,
                                const gfx::IntSize& aVisibleBoundsSize)
 {
   return (aVisibleBoundsSize == aBufferSize ||
           (SizedToVisibleBounds != aPolicy &&
            aVisibleBoundsSize < aBufferSize));
 }
 
-RefPtr<RotatedBuffer>
-ContentClient::GetFrontBuffer() const
-{
-  return mBuffer;
-}
-
 void
 ContentClient::PrintInfo(std::stringstream& aStream, const char* aPrefix)
 {
   aStream << aPrefix;
   aStream << nsPrintfCString("ContentClient (0x%p)", this).get();
 }
 
 // We pass a null pointer for the ContentClient Forwarder argument, which means
@@ -914,85 +890,96 @@ ContentClientDoubleBuffered::SwapBuffers
 
   mFrontAndBackBufferDiffer = true;
 }
 
 ContentClient::PaintState
 ContentClientDoubleBuffered::BeginPaint(PaintedLayer* aLayer,
                                         uint32_t aFlags)
 {
+  EnsureBackBufferIfFrontBuffer();
+
   mIsNewBuffer = false;
+
   if (!mFrontBuffer || !mBuffer) {
     mFrontAndBackBufferDiffer = false;
   }
 
-  return ContentClient::BeginPaint(aLayer, aFlags);
-}
+  if (mFrontAndBackBufferDiffer) {
+    if (mFrontBuffer->DidSelfCopy()) {
+      // We can't easily draw our front buffer into us, since we're going to be
+      // copying stuff around anyway it's easiest if we just move our situation
+      // to non-rotated while we're at it. If this situation occurs we'll have
+      // hit a self-copy path in PaintThebes before as well anyway.
+      gfx::IntRect backBufferRect = mBuffer->BufferRect();
+      backBufferRect.MoveTo(mFrontBuffer->BufferRect().TopLeft());
 
-RefPtr<RotatedBuffer>
-ContentClientDoubleBuffered::GetFrontBuffer() const
-{
-  return mFrontBuffer;
+      mBuffer->SetBufferRect(backBufferRect);
+      mBuffer->SetBufferRotation(IntPoint(0,0));
+    } else {
+      mBuffer->SetBufferRect(mFrontBuffer->BufferRect());
+      mBuffer->SetBufferRotation(mFrontBuffer->BufferRotation());
+    }
+  }
+
+  return ContentClient::BeginPaint(aLayer, aFlags);
 }
 
 // Sync front/back buffers content
 // After executing, the new back buffer has the same (interesting) pixels as
 // the new front buffer, and mValidRegion et al. are correct wrt the new
 // back buffer (i.e. as they were for the old back buffer)
-void
-ContentClientDoubleBuffered::FinalizeFrame(const nsIntRegion& aRegionToDraw,
-                                           CapturedBufferState* aPrepareState)
+Maybe<CapturedBufferState::Copy>
+ContentClientDoubleBuffered::FinalizeFrame(const nsIntRegion& aRegionToDraw)
 {
   if (!mFrontAndBackBufferDiffer) {
     MOZ_ASSERT(!mFrontBuffer || !mFrontBuffer->DidSelfCopy(),
                "If the front buffer did a self copy then our front and back buffer must be different.");
-    return;
+    return Nothing();
   }
 
   MOZ_ASSERT(mFrontBuffer && mBuffer);
   if (!mFrontBuffer || !mBuffer) {
-    return;
+    return Nothing();
   }
 
   MOZ_LAYERS_LOG(("BasicShadowableThebes(%p): reading back <x=%d,y=%d,w=%d,h=%d>",
                   this,
                   mFrontUpdatedRegion.GetBounds().x,
                   mFrontUpdatedRegion.GetBounds().y,
                   mFrontUpdatedRegion.GetBounds().Width(),
                   mFrontUpdatedRegion.GetBounds().Height()));
 
   mFrontAndBackBufferDiffer = false;
 
-  // Move the back buffer rect and rotation to the front buffer rect and rotation
-  // so that we can update the pixels that changed between frames
-  gfx::IntRect backBufferRect = mBuffer->BufferRect();
-  backBufferRect.MoveTo(mFrontBuffer->BufferRect().TopLeft());
-  mBuffer->SetBufferRect(backBufferRect);
-  mBuffer->SetBufferRotation(mFrontBuffer->BufferRotation());
-
-  // Calculate the region to update
   nsIntRegion updateRegion = mFrontUpdatedRegion;
   if (mFrontBuffer->DidSelfCopy()) {
-    // If we did an unrotate operation on the front buffer we might as well
-    // unrotate as well because we will be reading back the whole front buffer
-    mBuffer->SetBufferRotation(IntPoint(0,0));
-
     mFrontBuffer->ClearDidSelfCopy();
     updateRegion = mBuffer->BufferRect();
   }
 
   // No point in sync'ing what we are going to draw over anyway. And if there is
   // nothing to sync at all, there is nothing to do and we can go home early.
   updateRegion.Sub(updateRegion, aRegionToDraw);
   if (updateRegion.IsEmpty()) {
-    return;
+    return Nothing();
   }
 
-  MOZ_ASSERT(!aPrepareState->mBufferCopy);
-  aPrepareState->mBufferCopy = Some(CapturedBufferState::Copy {
+  return Some(CapturedBufferState::Copy {
     mFrontBuffer->ShallowCopy(),
     mBuffer->ShallowCopy(),
     updateRegion.GetBounds(),
   });
 }
 
+void
+ContentClientDoubleBuffered::EnsureBackBufferIfFrontBuffer()
+{
+  if (!mBuffer && mFrontBuffer) {
+    mBuffer = CreateBufferInternal(mFrontBuffer->BufferRect(),
+                                   mFrontBuffer->GetFormat(),
+                                   mTextureFlags);
+    MOZ_ASSERT(mBuffer);
+  }
+}
+
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/client/ContentClient.h
+++ b/gfx/layers/client/ContentClient.h
@@ -193,42 +193,40 @@ protected:
   struct BufferDecision {
     nsIntRegion mNeededRegion;
     nsIntRegion mValidRegion;
     gfx::IntRect mBufferRect;
     SurfaceMode mBufferMode;
     gfxContentType mBufferContentType;
     bool mCanReuseBuffer;
     bool mCanKeepBufferContents;
-    bool mMustRemoveFrontBuffer;
   };
 
   /**
    * Decide whether we can keep our current buffer and its contents,
    * and return a struct containing the regions to paint, invalidate,
    * the new buffer rect, surface mode, and content type.
    */
   BufferDecision CalculateBufferForPaint(PaintedLayer* aLayer,
                                          uint32_t aFlags);
 
   static bool ValidBufferSize(BufferSizePolicy aPolicy,
                               const gfx::IntSize& aBufferSize,
                               const gfx::IntSize& aVisibleBoundsSize);
 
-  virtual RefPtr<RotatedBuffer> GetFrontBuffer() const;
-
   /**
    * Any actions that should be performed at the last moment before we begin
    * rendering the next frame. I.e., after we calculate what we will draw,
    * but before we rotate the buffer and possibly create new buffers.
    * aRegionToDraw is the region which is guaranteed to be overwritten when
    * drawing the next frame.
    */
-  virtual void FinalizeFrame(const nsIntRegion& aRegionToDraw,
-                             CapturedBufferState* aState) {}
+  virtual Maybe<CapturedBufferState::Copy> FinalizeFrame(const nsIntRegion& aRegionToDraw) {
+    return Nothing();
+  }
 
   /**
    * Create a new rotated buffer for the specified content type, buffer rect,
    * and buffer flags.
    */
   virtual RefPtr<RotatedBuffer> CreateBuffer(gfxContentType aType,
                                              const gfx::IntRect& aRect,
                                              uint32_t aFlags) = 0;
@@ -361,27 +359,26 @@ public:
                     TextureDumpMode aCompress=TextureDumpMode::Compress) override;
 
   virtual void Clear() override;
 
   virtual void SwapBuffers(const nsIntRegion& aFrontUpdatedRegion) override;
 
   virtual PaintState BeginPaint(PaintedLayer* aLayer, uint32_t aFlags) override;
 
-  virtual RefPtr<RotatedBuffer> GetFrontBuffer() const override;
-
-  virtual void FinalizeFrame(const nsIntRegion& aRegionToDraw,
-                             CapturedBufferState* aState) override;
+  virtual Maybe<CapturedBufferState::Copy> FinalizeFrame(const nsIntRegion& aRegionToDraw) override;
 
   virtual TextureInfo GetTextureInfo() const override
   {
     return TextureInfo(CompositableType::CONTENT_DOUBLE, mTextureFlags);
   }
 
 private:
+  void EnsureBackBufferIfFrontBuffer();
+
   RefPtr<RemoteRotatedBuffer> mFrontBuffer;
   nsIntRegion mFrontUpdatedRegion;
   bool mFrontAndBackBufferDiffer;
 };
 
 /**
  * A single buffered ContentClientRemoteBuffer. We have a single
  * TextureClient/Host which we update and then send a message to the