Implement buffer copying and clearing on the paint thread for multi tiled layers (bug 1422392, r=nical) draft
authorRyan Hunt <rhunt@eqrion.net>
Wed, 29 Nov 2017 19:00:50 -0500
changeset 708365 dd8d14f469f2a7d4f43c0a41373a6848f57f4b39
parent 708364 f8256b678da683da76edc02769dd4d0ebe234e09
child 708366 b2af9d7976f09178c4c74ed7254d5c4ab323e5bd
push id92378
push userbmo:rhunt@eqrion.net
push dateWed, 06 Dec 2017 20:15:21 +0000
reviewersnical
bugs1422392
milestone59.0a1
Implement buffer copying and clearing on the paint thread for multi tiled layers (bug 1422392, r=nical) This implements recording of buffer preparing commands for MultiTiledContentClient and replaying it on the paint thread. The order of locking for tiling is important and for buffer copying the front buffer needs to be attempted to be locked before the back buffer. This means we can't do this on the paint thread like we do for ContentClient, so instead I've changed the OpenMode::ASYNC to be a flag that can be applied to OpenMode::OPEN_READ as well as WRITE, as it looks like we can apply the same logic for READ as WRITE. MozReview-Commit-ID: ED6eeYx8dUV
gfx/layers/CompositorTypes.h
gfx/layers/PaintThread.cpp
gfx/layers/PaintThread.h
gfx/layers/client/ContentClient.cpp
gfx/layers/client/SingleTiledContentClient.cpp
gfx/layers/client/TextureClient.cpp
gfx/layers/client/TiledContentClient.cpp
gfx/layers/client/TiledContentClient.h
--- a/gfx/layers/CompositorTypes.h
+++ b/gfx/layers/CompositorTypes.h
@@ -247,20 +247,21 @@ struct TextureInfo
  */
 enum class OpenMode : uint8_t {
   OPEN_NONE        = 0,
   OPEN_READ        = 0x1,
   OPEN_WRITE       = 0x2,
   // This is only used in conjunction with OMTP to indicate that the DrawTarget
   // that is being borrowed will be painted asynchronously, and so will outlive
   // the write lock.
-  OPEN_ASYNC_WRITE = 0x04,
+  OPEN_ASYNC = 0x04,
 
   OPEN_READ_WRITE  = OPEN_READ|OPEN_WRITE,
-  OPEN_READ_ASYNC_WRITE  = OPEN_READ|OPEN_WRITE|OPEN_ASYNC_WRITE,
+  OPEN_READ_WRITE_ASYNC  = OPEN_READ|OPEN_WRITE|OPEN_ASYNC,
+  OPEN_READ_ASYNC   = OPEN_READ|OPEN_ASYNC,
   OPEN_READ_ONLY   = OPEN_READ,
   OPEN_WRITE_ONLY  = OPEN_WRITE,
 };
 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(OpenMode)
 
 // The kinds of mask texture a shader can support
 // We rely on the items in this enum being sequential
 enum class MaskType : uint8_t {
--- a/gfx/layers/PaintThread.cpp
+++ b/gfx/layers/PaintThread.cpp
@@ -89,16 +89,59 @@ CapturedBufferState::GetTextureClients(n
       aTextureClients.AppendElement(destination);
     }
     if (TextureClient* destinationOnWhite = mBufferInitialize->mDestination->GetClientOnWhite()) {
       aTextureClients.AppendElement(destinationOnWhite);
     }
   }
 }
 
+bool
+CapturedTiledPaintState::Copy::CopyBuffer()
+{
+  RefPtr<gfx::SourceSurface> source = mSource->Snapshot();
+
+  // This operation requires the destination draw target to be untranslated,
+  // but the destination will have a transform from being part of a tiled draw
+  // target. However in this case, CopySurface ignores transforms so we don't
+  // need to do anything.
+  mDestination->CopySurface(source,
+                            mBounds,
+                            mBounds.TopLeft());
+  return true;
+}
+
+void
+CapturedTiledPaintState::Clear::ClearBuffer()
+{
+  // See the comment in CopyBuffer for why we need to temporarily reset
+  // the transform of the draw target.
+  Matrix oldTransform = mTarget->GetTransform();
+  mTarget->SetTransform(Matrix());
+
+  if (mTargetOnWhite) {
+    mTargetOnWhite->SetTransform(Matrix());
+    for (auto iter = mDirtyRegion.RectIter(); !iter.Done(); iter.Next()) {
+      const gfx::Rect drawRect(iter.Get().x, iter.Get().y,
+                               iter.Get().width, iter.Get().height);
+      mTarget->FillRect(drawRect, ColorPattern(Color(0.0, 0.0, 0.0, 1.0)));
+      mTargetOnWhite->FillRect(drawRect, ColorPattern(Color(1.0, 1.0, 1.0, 1.0)));
+    }
+    mTargetOnWhite->SetTransform(oldTransform);
+  } else {
+    for (auto iter = mDirtyRegion.RectIter(); !iter.Done(); iter.Next()) {
+      const gfx::Rect drawRect(iter.Get().x, iter.Get().y,
+                               iter.Get().width, iter.Get().height);
+      mTarget->ClearRect(drawRect);
+    }
+  }
+
+  mTarget->SetTransform(oldTransform);
+}
+
 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.
 struct MOZ_STACK_CLASS AutoCapturedPaintSetup
 {
@@ -373,16 +416,24 @@ PaintThread::AsyncPaintTiledContents(Com
   MOZ_ASSERT(IsOnPaintThread());
   MOZ_ASSERT(aState);
 
   if (!mInAsyncPaintGroup) {
     mInAsyncPaintGroup = true;
     PROFILER_TRACING("Paint", "Rasterize", TRACING_INTERVAL_START);
   }
 
+  for (auto& copy : aState->mCopies) {
+    copy.CopyBuffer();
+  }
+
+  for (auto& clear : aState->mClears) {
+    clear.ClearBuffer();
+  }
+
   DrawTarget* target = aState->mTargetTiled;
   DrawTargetCapture* capture = aState->mCapture;
 
   // Draw all the things into the actual dest target.
   target->DrawCapturedDT(capture, Matrix());
 
   if (!mDrawTargetsToFlush.Contains(target)) {
     mDrawTargetsToFlush.AppendElement(target);
--- a/gfx/layers/PaintThread.h
+++ b/gfx/layers/PaintThread.h
@@ -112,24 +112,59 @@ protected:
 
 typedef bool (*PrepDrawTargetForPaintingCallback)(CapturedPaintState* aPaintState);
 
 // Holds the key operations needed to update a tiled content client on the
 // paint thread.
 class CapturedTiledPaintState {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CapturedPaintState)
 public:
+  struct Copy {
+    Copy(RefPtr<gfx::DrawTarget> aSource,
+         RefPtr<gfx::DrawTarget> aDestination,
+         gfx::IntRect aBounds)
+      : mSource(aSource)
+      , mDestination(aDestination)
+      , mBounds(aBounds)
+    {}
+
+    bool CopyBuffer();
+
+    RefPtr<gfx::DrawTarget> mSource;
+    RefPtr<gfx::DrawTarget> mDestination;
+    gfx::IntRect mBounds;
+  };
+
+  struct Clear {
+    Clear(RefPtr<gfx::DrawTarget> aTarget,
+            RefPtr<gfx::DrawTarget> aTargetOnWhite,
+            nsIntRegion aDirtyRegion)
+      : mTarget(aTarget)
+      , mTargetOnWhite(aTargetOnWhite)
+      , mDirtyRegion(aDirtyRegion)
+    {}
+
+    void ClearBuffer();
+
+    RefPtr<gfx::DrawTarget> mTarget;
+    RefPtr<gfx::DrawTarget> mTargetOnWhite;
+    nsIntRegion mDirtyRegion;
+  };
+
   CapturedTiledPaintState(gfx::DrawTarget* aTargetTiled,
                           gfx::DrawTargetCapture* aCapture)
   : mTargetTiled(aTargetTiled)
   , mCapture(aCapture)
   {}
 
   RefPtr<gfx::DrawTarget> mTargetTiled;
   RefPtr<gfx::DrawTargetCapture> mCapture;
+  std::vector<Copy> mCopies;
+  std::vector<Clear> mClears;
+
   std::vector<RefPtr<TextureClient>> mClients;
 
 protected:
   virtual ~CapturedTiledPaintState() {}
 };
 
 class CompositorBridgeChild;
 
--- a/gfx/layers/client/ContentClient.cpp
+++ b/gfx/layers/client/ContentClient.cpp
@@ -152,17 +152,17 @@ ContentClient::BeginPaint(PaintedLayer* 
   // Also disable buffer rotation when using webrender.
   bool canHaveRotation = gfxPlatform::BufferRotationEnabled() &&
                          !(aFlags & (PAINT_WILL_RESAMPLE | PAINT_NO_ROTATION)) &&
                          !(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 lockMode = asyncPaint ? OpenMode::OPEN_READ_WRITE_ASYNC
                                  : OpenMode::OPEN_READ_WRITE;
 
   if (asyncPaint) {
     result.mBufferState = new CapturedBufferState();
   }
 
   if (mBuffer) {
     if (mBuffer->Lock(lockMode)) {
--- a/gfx/layers/client/SingleTiledContentClient.cpp
+++ b/gfx/layers/client/SingleTiledContentClient.cpp
@@ -146,17 +146,18 @@ ClientSingleTiledLayerBuffer::PaintThebe
   nsIntRegion extraPainted;
   RefPtr<TextureClient> backBufferOnWhite;
   RefPtr<TextureClient> backBuffer =
     mTile.GetBackBuffer(mCompositableClient,
                         tileDirtyRegion,
                         content, mode,
                         extraPainted,
                         TilePaintFlags::None,
-                        &backBufferOnWhite);
+                        &backBufferOnWhite,
+                        nullptr);
 
   // Mark the area we need to paint in the back buffer as invalid in the
   // front buffer as they will become out of sync.
   mTile.mInvalidFront.OrWith(tileDirtyRegion);
 
   // Add backbuffer's invalid region to the dirty region to be painted.
   // This will be empty if we were able to copy from the front in to the back.
   paintRegion.OrWith(mTile.mInvalidBack.MovedBy(mTilingOrigin));
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -546,17 +546,17 @@ TextureClient::Unlock()
 {
   MOZ_ASSERT(IsValid());
   MOZ_ASSERT(mIsLocked);
   if (!IsValid() || !mIsLocked) {
     return;
   }
 
   if (mBorrowedDrawTarget) {
-    if (!(mOpenMode & OpenMode::OPEN_ASYNC_WRITE)) {
+    if (!(mOpenMode & OpenMode::OPEN_ASYNC)) {
       if (mOpenMode & OpenMode::OPEN_WRITE) {
         mBorrowedDrawTarget->Flush();
         if (mReadbackSink && !mData->ReadBack(mReadbackSink)) {
           // Fallback implementation for reading back, because mData does not
           // have a backend-specific implementation and returned false.
           RefPtr<SourceSurface> snapshot = mBorrowedDrawTarget->Snapshot();
           RefPtr<DataSourceSurface> dataSurf = snapshot->GetDataSurface();
           mReadbackSink->ProcessReadback(dataSurf);
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -473,41 +473,61 @@ TileClient::Flip()
   mInvalidFront = mInvalidBack;
   mInvalidBack = invalidFront;
 }
 
 static bool
 CopyFrontToBack(TextureClient* aFront,
                 TextureClient* aBack,
                 const gfx::IntRect& aRectToCopy,
-                TilePaintFlags aFlags)
+                TilePaintFlags aFlags,
+                std::vector<CapturedTiledPaintState::Copy>* aCopies)
 {
   bool asyncPaint = !!(aFlags & TilePaintFlags::Async);
-  OpenMode lockMode = asyncPaint ? OpenMode::OPEN_READ_ASYNC_WRITE
-                                 : OpenMode::OPEN_READ_WRITE;
+  OpenMode asyncFlags = asyncPaint ? OpenMode::OPEN_ASYNC : OpenMode::OPEN_NONE;
 
-  TextureClientAutoLock frontLock(aFront, OpenMode::OPEN_READ);
+  TextureClientAutoLock frontLock(aFront, OpenMode::OPEN_READ | asyncFlags);
   if (!frontLock.Succeeded()) {
     return false;
   }
 
-  if (!aBack->Lock(lockMode)) {
+  if (!aBack->Lock(OpenMode::OPEN_READ_WRITE | asyncFlags)) {
     gfxCriticalError() << "[Tiling:Client] Failed to lock the tile's back buffer";
     return false;
   }
 
-  gfx::IntPoint rectToCopyTopLeft = aRectToCopy.TopLeft();
-  aFront->CopyToTextureClient(aBack, &aRectToCopy, &rectToCopyTopLeft);
+  RefPtr<gfx::DrawTarget> backBuffer = aBack->BorrowDrawTarget();
+  if (!backBuffer) {
+    gfxWarning() << "[Tiling:Client] Failed to aquire the back buffer's draw target";
+    return false;
+  }
+
+  RefPtr<gfx::DrawTarget> frontBuffer = aFront->BorrowDrawTarget();
+  if (!frontBuffer) {
+    gfxWarning() << "[Tiling:Client] Failed to aquire the front buffer's draw target";
+    return false;
+  }
+
+  auto copy = CapturedTiledPaintState::Copy{
+    frontBuffer, backBuffer, aRectToCopy
+  };
+
+  if (asyncPaint) {
+    aCopies->push_back(copy);
+  } else {
+    copy.CopyBuffer();
+  }
   return true;
 }
 
 void
 TileClient::ValidateBackBufferFromFront(const nsIntRegion& aDirtyRegion,
                                         nsIntRegion& aAddPaintedRegion,
-                                        TilePaintFlags aFlags)
+                                        TilePaintFlags aFlags,
+                                        std::vector<CapturedTiledPaintState::Copy>* aCopies)
 {
   if (mBackBuffer && mFrontBuffer) {
     gfx::IntSize tileSize = mFrontBuffer->GetSize();
     const IntRect tileRect = IntRect(0, 0, tileSize.width, tileSize.height);
 
     if (aDirtyRegion.Contains(tileRect)) {
       // The dirty region means that we no longer need the front buffer, so
       // discard it.
@@ -525,20 +545,20 @@ TileClient::ValidateBackBufferFromFront(
         return;
       }
 
       // Copy the bounding rect of regionToCopy. As tiles are quite small, it
       // is unlikely that we'd save much by copying each individual rect of the
       // region, but we can reevaluate this if it becomes an issue.
       const IntRect rectToCopy = regionToCopy.GetBounds();
       gfx::IntRect gfxRectToCopy(rectToCopy.x, rectToCopy.y, rectToCopy.Width(), rectToCopy.Height());
-      if (CopyFrontToBack(mFrontBuffer, mBackBuffer, gfxRectToCopy, aFlags)) {
+      if (CopyFrontToBack(mFrontBuffer, mBackBuffer, gfxRectToCopy, aFlags, aCopies)) {
         if (mBackBufferOnWhite) {
           MOZ_ASSERT(mFrontBufferOnWhite);
-          if (CopyFrontToBack(mFrontBufferOnWhite, mBackBufferOnWhite, gfxRectToCopy, aFlags)) {
+          if (CopyFrontToBack(mFrontBufferOnWhite, mBackBufferOnWhite, gfxRectToCopy, aFlags, aCopies)) {
             mInvalidBack.SetEmpty();
           }
         }
       }
     }
   }
 }
 
@@ -629,17 +649,18 @@ CreateBackBufferTexture(TextureClient* a
 
 TextureClient*
 TileClient::GetBackBuffer(CompositableClient& aCompositable,
                           const nsIntRegion& aDirtyRegion,
                           gfxContentType aContent,
                           SurfaceMode aMode,
                           nsIntRegion& aAddPaintedRegion,
                           TilePaintFlags aFlags,
-                          RefPtr<TextureClient>* aBackBufferOnWhite)
+                          RefPtr<TextureClient>* aBackBufferOnWhite,
+                          std::vector<CapturedTiledPaintState::Copy>* aCopies)
 {
   if (!mAllocator) {
     gfxCriticalError() << "[TileClient] Missing TextureClientAllocator.";
     return nullptr;
   }
   if (aMode != SurfaceMode::SURFACE_COMPONENT_ALPHA) {
     // It can happen that a component-alpha layer stops being on component alpha
     // on the next frame, just drop the buffers on white if that happens.
@@ -684,20 +705,20 @@ TileClient::GetBackBuffer(CompositableCl
       if (!mBackBufferOnWhite) {
         DiscardBackBuffer();
         DiscardFrontBuffer();
         return nullptr;
       }
       mInvalidBack = IntRect(IntPoint(), mBackBufferOnWhite->GetSize());
     }
 
-    ValidateBackBufferFromFront(aDirtyRegion, aAddPaintedRegion, aFlags);
+    ValidateBackBufferFromFront(aDirtyRegion, aAddPaintedRegion, aFlags, aCopies);
   }
 
-  OpenMode lockMode = aFlags & TilePaintFlags::Async ? OpenMode::OPEN_READ_ASYNC_WRITE
+  OpenMode lockMode = aFlags & TilePaintFlags::Async ? OpenMode::OPEN_READ_WRITE_ASYNC
                                                      : OpenMode::OPEN_READ_WRITE;
 
   if (!mBackBuffer->IsLocked()) {
     if (!mBackBuffer->Lock(lockMode)) {
       gfxCriticalError() << "[Tiling:Client] Failed to lock a tile (B)";
       DiscardBackBuffer();
       DiscardFrontBuffer();
       return nullptr;
@@ -960,16 +981,18 @@ void ClientMultiTiledLayerBuffer::Update
     }
   }
 
   oldRetainedTiles.Clear();
 
   nsIntRegion paintRegion = aPaintRegion;
   nsIntRegion dirtyRegion = aDirtyRegion;
   if (!paintRegion.IsEmpty()) {
+    MOZ_ASSERT(mPaintClears.size() == 0);
+    MOZ_ASSERT(mPaintCopies.size() == 0);
     for (size_t i = 0; i < newTileCount; ++i) {
       const TileIntPoint tilePosition = newTiles.TilePosition(i);
 
       IntPoint tileOffset = GetTileOffset(tilePosition);
       nsIntRegion tileDrawRegion = IntRect(tileOffset, scaledTileSize);
       tileDrawRegion.AndWith(paintRegion);
 
       if (tileDrawRegion.IsEmpty()) {
@@ -1018,20 +1041,25 @@ void ClientMultiTiledLayerBuffer::Update
                   DrawRegionClip::DRAW, nsIntRegion(), mCallbackData);
         ctx = nullptr;
 
         // Replay on the paint thread
         RefPtr<CapturedTiledPaintState> capturedState =
           new CapturedTiledPaintState(drawTarget,
                                       captureDT);
         capturedState->mClients = std::move(mPaintTilesTextureClients);
+        capturedState->mCopies = std::move(mPaintCopies);
+        capturedState->mClears = std::move(mPaintClears);
 
         PaintThread::Get()->PaintTiledContents(capturedState);
         mManager->SetQueuedAsyncPaints();
       } else {
+        MOZ_ASSERT(mPaintCopies.size() == 0);
+        MOZ_ASSERT(mPaintClears.size() == 0);
+
         // Draw into the tiled draw target
         RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(drawTarget);
         MOZ_ASSERT(ctx); // already checked the draw target above
         ctx->SetMatrix(
           ctx->CurrentMatrix().PreScale(mResolution, mResolution).PreTranslate(-mTilingOrigin));
 
         mCallback(&mPaintedLayer, ctx, paintRegion, dirtyRegion,
                   DrawRegionClip::DRAW, nsIntRegion(), mCallbackData);
@@ -1117,17 +1145,18 @@ ClientMultiTiledLayerBuffer::ValidateTil
   nsIntRegion extraPainted;
   RefPtr<TextureClient> backBufferOnWhite;
   RefPtr<TextureClient> backBuffer =
     aTile.GetBackBuffer(mCompositableClient,
                         offsetScaledDirtyRegion,
                         content, mode,
                         extraPainted,
                         aFlags,
-                        &backBufferOnWhite);
+                        &backBufferOnWhite,
+                        &mPaintCopies);
 
   // Mark the area we need to paint in the back buffer as invalid in the
   // front buffer as they will become out of sync.
   aTile.mInvalidFront.OrWith(offsetScaledDirtyRegion);
 
   // Add backbuffer's invalid region to the dirty region to be painted.
   // This will be empty if we were able to copy from the front in to the back.
   nsIntRegion invalidBack = aTile.mInvalidBack;
@@ -1168,26 +1197,26 @@ ClientMultiTiledLayerBuffer::ValidateTil
 
   if (aFlags & TilePaintFlags::Async) {
     mPaintTilesTextureClients.push_back(backBuffer);
     if (backBufferOnWhite) {
       mPaintTilesTextureClients.push_back(backBufferOnWhite);
     }
   }
 
-  for (auto iter = offsetScaledDirtyRegion.RectIter(); !iter.Done(); iter.Next()) {
-    const gfx::Rect drawRect(iter.Get().x, iter.Get().y,
-                             iter.Get().width, iter.Get().height);
+  auto clear = CapturedTiledPaintState::Clear{
+    dt,
+    dtOnWhite,
+    offsetScaledDirtyRegion
+  };
 
-    if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
-      dt->FillRect(drawRect, ColorPattern(Color(0.0, 0.0, 0.0, 1.0)));
-      dtOnWhite->FillRect(drawRect, ColorPattern(Color(1.0, 1.0, 1.0, 1.0)));
-    } else if (content == gfxContentType::COLOR_ALPHA) {
-      dt->ClearRect(drawRect);
-    }
+  if (aFlags & TilePaintFlags::Async) {
+    mPaintClears.push_back(clear);
+  } else {
+    clear.ClearBuffer();
   }
 
   // The new buffer is now validated, remove the dirty region from it.
   aTile.mInvalidBack.SubOut(offsetScaledDirtyRegion);
 
   aTile.Flip();
 
   return true;
--- a/gfx/layers/client/TiledContentClient.h
+++ b/gfx/layers/client/TiledContentClient.h
@@ -19,16 +19,17 @@
 #include "mozilla/RefPtr.h"             // for RefPtr
 #include "mozilla/ipc/Shmem.h"          // for Shmem
 #include "mozilla/ipc/SharedMemory.h"   // for SharedMemory
 #include "mozilla/layers/AsyncCompositionManager.h"  // for ViewTransform
 #include "mozilla/layers/CompositableClient.h"  // for CompositableClient
 #include "mozilla/layers/CompositorTypes.h"  // for TextureInfo, etc
 #include "mozilla/layers/LayersMessages.h" // for TileDescriptor
 #include "mozilla/layers/LayersTypes.h" // for TextureDumpMode
+#include "mozilla/layers/PaintThread.h" // for CapturedTiledPaintState
 #include "mozilla/layers/TextureClient.h"
 #include "mozilla/layers/TextureClientPool.h"
 #include "ClientLayerManager.h"
 #include "mozilla/mozalloc.h"           // for operator delete
 #include "nsISupportsImpl.h"            // for MOZ_COUNT_DTOR
 #include "nsPoint.h"                    // for nsIntPoint
 #include "nsRect.h"                     // for mozilla::gfx::IntRect
 #include "nsRegion.h"                   // for nsIntRegion
@@ -125,17 +126,18 @@ struct TileClient
   *
   * If nullptr is returned, aTextureClientOnWhite is undefined.
   */
   TextureClient* GetBackBuffer(CompositableClient&,
                                const nsIntRegion& aDirtyRegion,
                                gfxContentType aContent, SurfaceMode aMode,
                                nsIntRegion& aAddPaintedRegion,
                                TilePaintFlags aFlags,
-                               RefPtr<TextureClient>* aTextureClientOnWhite);
+                               RefPtr<TextureClient>* aTextureClientOnWhite,
+                               std::vector<CapturedTiledPaintState::Copy>* aCopies);
 
   void DiscardFrontBuffer();
 
   void DiscardBackBuffer();
 
   /* We wrap the back buffer in a class that disallows assignment
    * so that we can track when ever it changes so that we can update
    * the expiry tracker for expiring the back buffers */
@@ -163,17 +165,18 @@ struct TileClient
   nsIntRegion mInvalidFront;
   nsIntRegion mInvalidBack;
   nsExpirationState mExpirationState;
 private:
   // Copies dirty pixels from the front buffer into the back buffer,
   // and records the copied region in aAddPaintedRegion.
   void ValidateBackBufferFromFront(const nsIntRegion &aDirtyRegion,
                                    nsIntRegion& aAddPaintedRegion,
-                                   TilePaintFlags aFlags);
+                                   TilePaintFlags aFlags,
+                                   std::vector<CapturedTiledPaintState::Copy>* aCopies);
 };
 
 /**
  * This struct stores all the data necessary to perform a paint so that it
  * doesn't need to be recalculated on every repeated transaction.
  */
 struct BasicTiledLayerPaintData {
   /*
@@ -441,16 +444,18 @@ private:
   nsIntRegion mNewValidRegion;
 
   SharedFrameMetricsHelper*  mSharedFrameMetricsHelper;
 
   // Parameters that are collected during Update for a paint before they
   // are either executed or replayed on the paint thread.
   std::vector<gfx::Tile> mPaintTiles;
   std::vector<RefPtr<TextureClient>> mPaintTilesTextureClients;
+  std::vector<CapturedTiledPaintState::Copy> mPaintCopies;
+  std::vector<CapturedTiledPaintState::Clear> mPaintClears;
 
   /**
    * While we're adding tiles, this is used to keep track of the position of
    * the top-left of the top-left-most tile.  When we come to wrap the tiles in
    * TiledDrawTarget we subtract the value of this member from each tile's
    * offset so that all the tiles have a positive offset, then add a
    * translation to the TiledDrawTarget to compensate.  This is important so
    * that the mRect of the TiledDrawTarget is always at a positive x/y