Implement record and replay painting for multi tiled layers (bug 1422392, r=nical) draft
authorRyan Hunt <rhunt@eqrion.net>
Tue, 21 Nov 2017 19:12:14 -0500
changeset 708364 f8256b678da683da76edc02769dd4d0ebe234e09
parent 708363 c3a818618cee0bab7ac3c99a42daf698c063e0c0
child 708365 dd8d14f469f2a7d4f43c0a41373a6848f57f4b39
push id92378
push userbmo:rhunt@eqrion.net
push dateWed, 06 Dec 2017 20:15:21 +0000
reviewersnical
bugs1422392
milestone59.0a1
Implement record and replay painting for multi tiled layers (bug 1422392, r=nical) This commit modifies MultiTiledContentClient to record drawing commands and replay them to the tiled draw target on the paint thread when OMTP is enabled. MozReview-Commit-ID: 22zL3c4NZvu
gfx/2d/DrawTargetCairo.cpp
gfx/2d/FilterNodeD2D1.cpp
gfx/layers/PaintThread.cpp
gfx/layers/PaintThread.h
gfx/layers/client/ClientTiledPaintedLayer.cpp
gfx/layers/client/SingleTiledContentClient.cpp
gfx/layers/client/SingleTiledContentClient.h
gfx/layers/client/TiledContentClient.cpp
gfx/layers/client/TiledContentClient.h
gfx/layers/ipc/CompositorBridgeChild.cpp
gfx/layers/ipc/CompositorBridgeChild.h
gfx/thebes/gfxPlatform.cpp
gfx/thebes/gfxQuartzNativeDrawing.cpp
--- a/gfx/2d/DrawTargetCairo.cpp
+++ b/gfx/2d/DrawTargetCairo.cpp
@@ -2281,17 +2281,18 @@ DrawTargetCairo::GetUserSpaceClip()
   return Rect(clipX1, clipY1, clipX2 - clipX1, clipY2 - clipY1); // Narrowing of doubles to floats
 }
 
 cairo_t*
 BorrowedCairoContext::BorrowCairoContextFromDrawTarget(DrawTarget* aDT)
 {
   if (aDT->GetBackendType() != BackendType::CAIRO ||
       aDT->IsDualDrawTarget() ||
-      aDT->IsTiledDrawTarget()) {
+      aDT->IsTiledDrawTarget() ||
+      aDT->IsCaptureDT()) {
     return nullptr;
   }
   DrawTargetCairo* cairoDT = static_cast<DrawTargetCairo*>(aDT);
 
   cairoDT->WillChange();
 
   // save the state to make it easier for callers to avoid mucking with things
   cairo_save(cairoDT->mContext);
--- a/gfx/2d/FilterNodeD2D1.cpp
+++ b/gfx/2d/FilterNodeD2D1.cpp
@@ -145,17 +145,17 @@ D2D1_CHANNEL_SELECTOR D2DChannelSelector
   }
 
   MOZ_CRASH("GFX: Unknown enum value D2DChannelSelector!");
   return D2D1_CHANNEL_SELECTOR_R;
 }
 
 already_AddRefed<ID2D1Image> GetImageForSourceSurface(DrawTarget *aDT, SourceSurface *aSurface)
 {
-  if (aDT->IsTiledDrawTarget() || aDT->IsDualDrawTarget()) {
+  if (aDT->IsTiledDrawTarget() || aDT->IsDualDrawTarget() || aDT->IsCaptureDT()) {
     gfxDevCrash(LogReason::FilterNodeD2D1Target) << "Incompatible draw target type! " << (int)aDT->IsTiledDrawTarget() << " " << (int)aDT->IsDualDrawTarget();
     return nullptr;
   }
   switch (aDT->GetBackendType()) {
     case BackendType::DIRECT2D1_1:
       return static_cast<DrawTargetD2D1*>(aDT)->GetImageForSurface(aSurface, ExtendMode::CLAMP);
     default:
       gfxDevCrash(LogReason::FilterNodeD2D1Backend) << "Unknown draw target type! " << (int)aDT->GetBackendType();
--- a/gfx/layers/PaintThread.cpp
+++ b/gfx/layers/PaintThread.cpp
@@ -336,16 +336,74 @@ PaintThread::AsyncPaintContents(Composit
     // This should ensure the capture drawtarget, which may hold on to UnscaledFont objects,
     // gets destroyed on the main thread (See bug 1404742). This assumes (unflushed) target
     // DrawTargets do not themselves hold on to UnscaledFonts.
     NS_ReleaseOnMainThreadSystemGroup("CapturePaintState::DrawTargetCapture", aState->mCapture.forget());
   }
 }
 
 void
+PaintThread::PaintTiledContents(CapturedTiledPaintState* aState)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aState);
+
+  RefPtr<CompositorBridgeChild> cbc(CompositorBridgeChild::Get());
+  RefPtr<CapturedTiledPaintState> state(aState);
+
+  cbc->NotifyBeginAsyncTiledPaint(state);
+
+  RefPtr<PaintThread> self = this;
+  RefPtr<Runnable> task = NS_NewRunnableFunction("PaintThread::PaintTiledContents",
+    [self, cbc, state]() -> void
+  {
+    self->AsyncPaintTiledContents(cbc,
+                                  state);
+  });
+
+#ifndef OMTP_FORCE_SYNC
+  sThread->Dispatch(task.forget());
+#else
+  SyncRunnable::DispatchToThread(sThread, task);
+#endif
+}
+
+void
+PaintThread::AsyncPaintTiledContents(CompositorBridgeChild* aBridge,
+                                     CapturedTiledPaintState* aState)
+{
+  MOZ_ASSERT(IsOnPaintThread());
+  MOZ_ASSERT(aState);
+
+  if (!mInAsyncPaintGroup) {
+    mInAsyncPaintGroup = true;
+    PROFILER_TRACING("Paint", "Rasterize", TRACING_INTERVAL_START);
+  }
+
+  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);
+  }
+
+  if (gfxPrefs::LayersOMTPReleaseCaptureOnMainThread()) {
+    // This should ensure the capture drawtarget, which may hold on to UnscaledFont objects,
+    // gets destroyed on the main thread (See bug 1404742). This assumes (unflushed) target
+    // DrawTargets do not themselves hold on to UnscaledFonts.
+    NS_ReleaseOnMainThreadSystemGroup("CapturePaintState::DrawTargetCapture", aState->mCapture.forget());
+  }
+
+  aBridge->NotifyFinishedAsyncTiledPaint(aState);
+}
+
+void
 PaintThread::EndLayer()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   RefPtr<PaintThread> self = this;
   RefPtr<Runnable> task = NS_NewRunnableFunction("PaintThread::AsyncEndLayer",
   [self]() -> void
   {
--- a/gfx/layers/PaintThread.h
+++ b/gfx/layers/PaintThread.h
@@ -107,16 +107,35 @@ public:
   Maybe<Copy> mBufferInitialize;
 
 protected:
   ~CapturedBufferState() {}
 };
 
 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:
+  CapturedTiledPaintState(gfx::DrawTarget* aTargetTiled,
+                          gfx::DrawTargetCapture* aCapture)
+  : mTargetTiled(aTargetTiled)
+  , mCapture(aCapture)
+  {}
+
+  RefPtr<gfx::DrawTarget> mTargetTiled;
+  RefPtr<gfx::DrawTargetCapture> mCapture;
+  std::vector<RefPtr<TextureClient>> mClients;
+
+protected:
+  virtual ~CapturedTiledPaintState() {}
+};
+
 class CompositorBridgeChild;
 
 class PaintThread final
 {
   friend void DestroyPaintThread(UniquePtr<PaintThread>&& aPaintThread);
 
 public:
   static void Start();
@@ -132,16 +151,18 @@ public:
   // or running while this is executing.
   void BeginLayerTransaction();
 
   void PrepareBuffer(CapturedBufferState* aState);
 
   void PaintContents(CapturedPaintState* aState,
                      PrepDrawTargetForPaintingCallback aCallback);
 
+  void PaintTiledContents(CapturedTiledPaintState* aState);
+
   // Must be called on the main thread. Signifies that the current
   // batch of CapturedPaintStates* for PaintContents have been recorded
   // and the main thread is finished recording this layer.
   void EndLayer();
 
   // This allows external users to run code on the paint thread.
   void Dispatch(RefPtr<Runnable>& aRunnable);
 
@@ -167,16 +188,18 @@ private:
   void ShutdownOnPaintThread();
   void InitOnPaintThread();
 
   void AsyncPrepareBuffer(CompositorBridgeChild* aBridge,
                           CapturedBufferState* aState);
   void AsyncPaintContents(CompositorBridgeChild* aBridge,
                           CapturedPaintState* aState,
                           PrepDrawTargetForPaintingCallback aCallback);
+  void AsyncPaintTiledContents(CompositorBridgeChild* aBridge,
+                               CapturedTiledPaintState* aState);
   void AsyncEndLayer();
   void AsyncEndLayerTransaction(CompositorBridgeChild* aBridge,
                                 SyncObjectClient* aSyncObject);
 
   static StaticAutoPtr<PaintThread> sSingleton;
   static StaticRefPtr<nsIThread> sThread;
   static PlatformThreadId sThreadId;
 
--- a/gfx/layers/client/ClientTiledPaintedLayer.cpp
+++ b/gfx/layers/client/ClientTiledPaintedLayer.cpp
@@ -14,16 +14,17 @@
 #include "gfxRect.h"                    // for gfxRect
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/gfx/BaseSize.h"       // for BaseSize
 #include "mozilla/gfx/gfxVars.h"
 #include "mozilla/gfx/Rect.h"           // for Rect, RectTyped
 #include "mozilla/layers/CompositorBridgeChild.h"
 #include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
 #include "mozilla/layers/LayersMessages.h"
+#include "mozilla/layers/PaintThread.h"
 #include "mozilla/mozalloc.h"           // for operator delete, etc
 #include "nsISupportsImpl.h"            // for MOZ_COUNT_CTOR, etc
 #include "LayersLogging.h"
 #include "mozilla/layers/SingleTiledContentClient.h"
 
 namespace mozilla {
 namespace layers {
 
@@ -339,19 +340,23 @@ ClientTiledPaintedLayer::RenderHighPreci
   if (mPaintData.mCriticalDisplayPort) {
     validRegion.AndWith(mPaintData.mCriticalDisplayPort->ToUnknownRect());
   }
   SetValidRegion(validRegion);
 
   TILING_LOG("TILING %p: Non-progressive paint invalid region %s\n", this, Stringify(aInvalidRegion).c_str());
   TILING_LOG("TILING %p: Non-progressive paint new valid region %s\n", this, Stringify(GetValidRegion()).c_str());
 
+  TilePaintFlags flags = PaintThread::Get()
+    ? TilePaintFlags::Async
+    : TilePaintFlags::None;
+
   mContentClient->GetTiledBuffer()->SetFrameResolution(mPaintData.mResolution);
   mContentClient->GetTiledBuffer()->PaintThebes(GetValidRegion(), aInvalidRegion, aInvalidRegion,
-                                                aCallback, aCallbackData);
+                                                aCallback, aCallbackData, flags);
   mPaintData.mPaintFinished = true;
   return true;
 }
 
 bool
 ClientTiledPaintedLayer::RenderLowPrecision(const nsIntRegion& aInvalidRegion,
                                            const nsIntRegion& aVisibleRegion,
                                            LayerManager::DrawPaintedLayerCallback aCallback,
--- a/gfx/layers/client/SingleTiledContentClient.cpp
+++ b/gfx/layers/client/SingleTiledContentClient.cpp
@@ -100,19 +100,19 @@ ClientSingleTiledLayerBuffer::GetTexture
 }
 
 void
 ClientSingleTiledLayerBuffer::PaintThebes(const nsIntRegion& aNewValidRegion,
                                           const nsIntRegion& aPaintRegion,
                                           const nsIntRegion& aDirtyRegion,
                                           LayerManager::DrawPaintedLayerCallback aCallback,
                                           void* aCallbackData,
-                                          bool aIsProgressive)
+                                          TilePaintFlags aFlags)
 {
-  mWasLastPaintProgressive = aIsProgressive;
+  mWasLastPaintProgressive = !!(aFlags & TilePaintFlags::Progressive);
 
   // Compare layer valid region size to current backbuffer size, discard if not matching.
   gfx::IntSize size = aNewValidRegion.GetBounds().Size();
   gfx::IntPoint origin = aNewValidRegion.GetBounds().TopLeft();
   nsIntRegion paintRegion = aPaintRegion;
 
   RefPtr<TextureClient> discardedFrontBuffer = nullptr;
   RefPtr<TextureClient> discardedFrontBufferOnWhite = nullptr;
@@ -145,16 +145,17 @@ ClientSingleTiledLayerBuffer::PaintThebe
 
   nsIntRegion extraPainted;
   RefPtr<TextureClient> backBufferOnWhite;
   RefPtr<TextureClient> backBuffer =
     mTile.GetBackBuffer(mCompositableClient,
                         tileDirtyRegion,
                         content, mode,
                         extraPainted,
+                        TilePaintFlags::None,
                         &backBufferOnWhite);
 
   // 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.
--- a/gfx/layers/client/SingleTiledContentClient.h
+++ b/gfx/layers/client/SingleTiledContentClient.h
@@ -38,17 +38,17 @@ public:
   void ReportClientLost() override {}
 
   // ClientTiledLayerBuffer
   void PaintThebes(const nsIntRegion& aNewValidRegion,
                    const nsIntRegion& aPaintRegion,
                    const nsIntRegion& aDirtyRegion,
                    LayerManager::DrawPaintedLayerCallback aCallback,
                    void* aCallbackData,
-                   bool aIsProgressive = false) override;
+                   TilePaintFlags aFlags = TilePaintFlags::None) override;
  
   bool SupportsProgressiveUpdate() override { return false; }
   bool ProgressiveUpdate(const nsIntRegion& aValidRegion,
                          const nsIntRegion& aInvalidRegion,
                          const nsIntRegion& aOldValidRegion,
                          nsIntRegion& aOutDrawnRegion,
                          BasicTiledLayerPaintData* aPaintData,
                          LayerManager::DrawPaintedLayerCallback aCallback,
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -17,16 +17,17 @@
 #include "mozilla/MathAlgorithms.h"     // for Abs
 #include "mozilla/gfx/Point.h"          // for IntSize
 #include "mozilla/gfx/Rect.h"           // for Rect
 #include "mozilla/gfx/Tools.h"          // for BytesPerPixel
 #include "mozilla/layers/CompositableForwarder.h"
 #include "mozilla/layers/CompositorBridgeChild.h" // for CompositorBridgeChild
 #include "mozilla/layers/LayerMetricsWrapper.h"
 #include "mozilla/layers/ShadowLayers.h"  // for ShadowLayerForwarder
+#include "mozilla/layers/PaintThread.h"  // for PaintThread
 #include "TextureClientPool.h"
 #include "nsDebug.h"                    // for NS_ASSERTION
 #include "nsISupportsImpl.h"            // for gfxContext::AddRef, etc
 #include "nsExpirationTracker.h"        // for nsExpirationTracker
 #include "nsMathUtils.h"               // for NS_lroundf
 #include "LayersLogging.h"
 #include "UnitTransforms.h"             // for TransformTo
 #include "mozilla/UniquePtr.h"
@@ -471,36 +472,42 @@ TileClient::Flip()
   nsIntRegion invalidFront = mInvalidFront;
   mInvalidFront = mInvalidBack;
   mInvalidBack = invalidFront;
 }
 
 static bool
 CopyFrontToBack(TextureClient* aFront,
                 TextureClient* aBack,
-                const gfx::IntRect& aRectToCopy)
+                const gfx::IntRect& aRectToCopy,
+                TilePaintFlags aFlags)
 {
+  bool asyncPaint = !!(aFlags & TilePaintFlags::Async);
+  OpenMode lockMode = asyncPaint ? OpenMode::OPEN_READ_ASYNC_WRITE
+                                 : OpenMode::OPEN_READ_WRITE;
+
   TextureClientAutoLock frontLock(aFront, OpenMode::OPEN_READ);
   if (!frontLock.Succeeded()) {
     return false;
   }
 
-  if (!aBack->Lock(OpenMode::OPEN_READ_WRITE)) {
+  if (!aBack->Lock(lockMode)) {
     gfxCriticalError() << "[Tiling:Client] Failed to lock the tile's back buffer";
     return false;
   }
 
   gfx::IntPoint rectToCopyTopLeft = aRectToCopy.TopLeft();
   aFront->CopyToTextureClient(aBack, &aRectToCopy, &rectToCopyTopLeft);
   return true;
 }
 
 void
 TileClient::ValidateBackBufferFromFront(const nsIntRegion& aDirtyRegion,
-                                        nsIntRegion& aAddPaintedRegion)
+                                        nsIntRegion& aAddPaintedRegion,
+                                        TilePaintFlags aFlags)
 {
   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.
@@ -518,20 +525,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)) {
+      if (CopyFrontToBack(mFrontBuffer, mBackBuffer, gfxRectToCopy, aFlags)) {
         if (mBackBufferOnWhite) {
           MOZ_ASSERT(mFrontBufferOnWhite);
-          if (CopyFrontToBack(mFrontBufferOnWhite, mBackBufferOnWhite, gfxRectToCopy)) {
+          if (CopyFrontToBack(mFrontBufferOnWhite, mBackBufferOnWhite, gfxRectToCopy, aFlags)) {
             mInvalidBack.SetEmpty();
           }
         }
       }
     }
   }
 }
 
@@ -621,16 +628,17 @@ CreateBackBufferTexture(TextureClient* a
 }
 
 TextureClient*
 TileClient::GetBackBuffer(CompositableClient& aCompositable,
                           const nsIntRegion& aDirtyRegion,
                           gfxContentType aContent,
                           SurfaceMode aMode,
                           nsIntRegion& aAddPaintedRegion,
+                          TilePaintFlags aFlags,
                           RefPtr<TextureClient>* aBackBufferOnWhite)
 {
   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
@@ -676,30 +684,33 @@ TileClient::GetBackBuffer(CompositableCl
       if (!mBackBufferOnWhite) {
         DiscardBackBuffer();
         DiscardFrontBuffer();
         return nullptr;
       }
       mInvalidBack = IntRect(IntPoint(), mBackBufferOnWhite->GetSize());
     }
 
-    ValidateBackBufferFromFront(aDirtyRegion, aAddPaintedRegion);
+    ValidateBackBufferFromFront(aDirtyRegion, aAddPaintedRegion, aFlags);
   }
 
+  OpenMode lockMode = aFlags & TilePaintFlags::Async ? OpenMode::OPEN_READ_ASYNC_WRITE
+                                                     : OpenMode::OPEN_READ_WRITE;
+
   if (!mBackBuffer->IsLocked()) {
-    if (!mBackBuffer->Lock(OpenMode::OPEN_READ_WRITE)) {
+    if (!mBackBuffer->Lock(lockMode)) {
       gfxCriticalError() << "[Tiling:Client] Failed to lock a tile (B)";
       DiscardBackBuffer();
       DiscardFrontBuffer();
       return nullptr;
     }
   }
 
   if (mBackBufferOnWhite && !mBackBufferOnWhite->IsLocked()) {
-    if (!mBackBufferOnWhite->Lock(OpenMode::OPEN_READ_WRITE)) {
+    if (!mBackBufferOnWhite->Lock(lockMode)) {
       gfxCriticalError() << "[Tiling:Client] Failed to lock a tile (W)";
       DiscardBackBuffer();
       DiscardFrontBuffer();
       return nullptr;
     }
   }
 
   *aBackBufferOnWhite = mBackBufferOnWhite;
@@ -761,24 +772,24 @@ ClientMultiTiledLayerBuffer::GetSurfaceD
 }
 
 void
 ClientMultiTiledLayerBuffer::PaintThebes(const nsIntRegion& aNewValidRegion,
                                          const nsIntRegion& aPaintRegion,
                                          const nsIntRegion& aDirtyRegion,
                                          LayerManager::DrawPaintedLayerCallback aCallback,
                                          void* aCallbackData,
-                                         bool aIsProgressive)
+                                         TilePaintFlags aFlags)
 {
   TILING_LOG("TILING %p: PaintThebes painting region %s\n", &mPaintedLayer, Stringify(aPaintRegion).c_str());
   TILING_LOG("TILING %p: PaintThebes new valid region %s\n", &mPaintedLayer, Stringify(aNewValidRegion).c_str());
 
   mCallback = aCallback;
   mCallbackData = aCallbackData;
-  mWasLastPaintProgressive = aIsProgressive;
+  mWasLastPaintProgressive = !!(aFlags & TilePaintFlags::Progressive);
 
 #ifdef GFX_TILEDLAYER_PREF_WARNINGS
   long start = PR_IntervalNow();
 #endif
 
 #ifdef GFX_TILEDLAYER_PREF_WARNINGS
   if (PR_IntervalNow() - start > 30) {
     const IntRect bounds = aPaintRegion.GetBounds();
@@ -793,17 +804,17 @@ ClientMultiTiledLayerBuffer::PaintThebes
     }
   }
   start = PR_IntervalNow();
 #endif
 
   AUTO_PROFILER_LABEL("ClientMultiTiledLayerBuffer::PaintThebes", GRAPHICS);
 
   mNewValidRegion = aNewValidRegion;
-  Update(aNewValidRegion, aPaintRegion, aDirtyRegion);
+  Update(aNewValidRegion, aPaintRegion, aDirtyRegion, aFlags);
 
 #ifdef GFX_TILEDLAYER_PREF_WARNINGS
   if (PR_IntervalNow() - start > 10) {
     const IntRect bounds = aPaintRegion.GetBounds();
     printf_stderr("Time to tile %i: %i, %i, %i, %i\n", PR_IntervalNow() - start, bounds.x, bounds.y, bounds.width, bounds.height);
   }
 #endif
 
@@ -909,17 +920,18 @@ ClientTiledLayerBuffer::UnlockTile(TileC
   }
   if (aTile.mBackBufferOnWhite && aTile.mBackBufferOnWhite->IsLocked()) {
     aTile.mBackBufferOnWhite->Unlock();
   }
 }
 
 void ClientMultiTiledLayerBuffer::Update(const nsIntRegion& newValidRegion,
                                          const nsIntRegion& aPaintRegion,
-                                         const nsIntRegion& aDirtyRegion)
+                                         const nsIntRegion& aDirtyRegion,
+                                         TilePaintFlags aFlags)
 {
   const IntSize scaledTileSize = GetScaledTileSize();
   const gfx::IntRect newBounds = newValidRegion.GetBounds();
 
   const TilesPlacement oldTiles = mTiles;
   const TilesPlacement newTiles(floor_div(newBounds.x, scaledTileSize.width),
                           floor_div(newBounds.y, scaledTileSize.height),
                           floor_div(GetTileStart(newBounds.x, scaledTileSize.width)
@@ -960,48 +972,79 @@ void ClientMultiTiledLayerBuffer::Update
       nsIntRegion tileDrawRegion = IntRect(tileOffset, scaledTileSize);
       tileDrawRegion.AndWith(paintRegion);
 
       if (tileDrawRegion.IsEmpty()) {
         continue;
       }
 
       TileClient& tile = mRetainedTiles[i];
-      if (!ValidateTile(tile, GetTileOffset(tilePosition), tileDrawRegion)) {
+      if (!ValidateTile(tile, GetTileOffset(tilePosition), tileDrawRegion, aFlags)) {
         gfxCriticalError() << "ValidateTile failed";
       }
 
       // Validating the tile may have required more to be painted.
       paintRegion.OrWith(tileDrawRegion);
       dirtyRegion.OrWith(tileDrawRegion);
     }
 
-    if (!mMoz2DTiles.empty()) {
+    if (!mPaintTiles.empty()) {
+      // Create a tiled draw target
       gfx::TileSet tileset;
-      for (size_t i = 0; i < mMoz2DTiles.size(); ++i) {
-        mMoz2DTiles[i].mTileOrigin -= mTilingOrigin;
+      for (size_t i = 0; i < mPaintTiles.size(); ++i) {
+        mPaintTiles[i].mTileOrigin -= mTilingOrigin;
       }
-      tileset.mTiles = &mMoz2DTiles[0];
-      tileset.mTileCount = mMoz2DTiles.size();
+      tileset.mTiles = &mPaintTiles[0];
+      tileset.mTileCount = mPaintTiles.size();
       RefPtr<DrawTarget> drawTarget = gfx::Factory::CreateTiledDrawTarget(tileset);
       if (!drawTarget || !drawTarget->IsValid()) {
         gfxDevCrash(LogReason::InvalidContext) << "Invalid tiled draw target";
         return;
       }
       drawTarget->SetTransform(Matrix());
 
-      RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(drawTarget);
-      MOZ_ASSERT(ctx); // already checked the draw target above
-      ctx->SetMatrix(
-        ctx->CurrentMatrix().PreScale(mResolution, mResolution).PreTranslate(-mTilingOrigin));
+      if (aFlags & TilePaintFlags::Async) {
+        // Create a capture draw target
+        RefPtr<DrawTargetCapture> captureDT =
+          Factory::CreateCaptureDrawTarget(drawTarget->GetBackendType(),
+                                           drawTarget->GetSize(),
+                                           drawTarget->GetFormat());
+
+        // Draw into the capture target
+        RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(captureDT);
+        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);
+        ctx = nullptr;
 
-      mCallback(&mPaintedLayer, ctx, paintRegion, dirtyRegion,
-                DrawRegionClip::DRAW, nsIntRegion(), mCallbackData);
-      mMoz2DTiles.clear();
-      // Reset:
+        // Replay on the paint thread
+        RefPtr<CapturedTiledPaintState> capturedState =
+          new CapturedTiledPaintState(drawTarget,
+                                      captureDT);
+        capturedState->mClients = std::move(mPaintTilesTextureClients);
+
+        PaintThread::Get()->PaintTiledContents(capturedState);
+        mManager->SetQueuedAsyncPaints();
+      } else {
+        // 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);
+      }
+
+      // Reset
+      mPaintTiles.clear();
+      mPaintTilesTextureClients.clear();
       mTilingOrigin = IntPoint(std::numeric_limits<int32_t>::max(),
                                std::numeric_limits<int32_t>::max());
     }
 
     bool edgePaddingEnabled = gfxPrefs::TileEdgePaddingEnabled();
 
     for (uint32_t i = 0; i < mRetainedTiles.Length(); ++i) {
       TileClient& tile = mRetainedTiles[i];
@@ -1041,17 +1084,18 @@ void ClientMultiTiledLayerBuffer::Update
   mTiles = newTiles;
   mValidRegion = newValidRegion;
   mPaintedRegion.OrWith(paintRegion);
 }
 
 bool
 ClientMultiTiledLayerBuffer::ValidateTile(TileClient& aTile,
                                           const nsIntPoint& aTileOrigin,
-                                          nsIntRegion& aDirtyRegion)
+                                          nsIntRegion& aDirtyRegion,
+                                          TilePaintFlags aFlags)
 {
   AUTO_PROFILER_LABEL("ClientMultiTiledLayerBuffer::ValidateTile", GRAPHICS);
 
 #ifdef GFX_TILEDLAYER_PREF_WARNINGS
   if (aDirtyRegion.IsComplex()) {
     printf_stderr("Complex region\n");
   }
 #endif
@@ -1072,16 +1116,17 @@ ClientMultiTiledLayerBuffer::ValidateTil
 
   nsIntRegion extraPainted;
   RefPtr<TextureClient> backBufferOnWhite;
   RefPtr<TextureClient> backBuffer =
     aTile.GetBackBuffer(mCompositableClient,
                         offsetScaledDirtyRegion,
                         content, mode,
                         extraPainted,
+                        aFlags,
                         &backBufferOnWhite);
 
   // 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.
@@ -1097,34 +1142,41 @@ ClientMultiTiledLayerBuffer::ValidateTil
   extraPainted.MoveBy(aTileOrigin);
   extraPainted.And(extraPainted, mNewValidRegion);
   mPaintedRegion.Or(mPaintedRegion, extraPainted);
 
   if (!backBuffer) {
     return false;
   }
 
-  gfx::Tile moz2DTile;
+  gfx::Tile paintTile;
   RefPtr<DrawTarget> dt = backBuffer->BorrowDrawTarget();
   RefPtr<DrawTarget> dtOnWhite;
   if (backBufferOnWhite) {
     dtOnWhite = backBufferOnWhite->BorrowDrawTarget();
-    moz2DTile.mDrawTarget = Factory::CreateDualDrawTarget(dt, dtOnWhite);
+    paintTile.mDrawTarget = Factory::CreateDualDrawTarget(dt, dtOnWhite);
   } else {
-    moz2DTile.mDrawTarget = dt;
+    paintTile.mDrawTarget = dt;
   }
-  moz2DTile.mTileOrigin = gfx::IntPoint(aTileOrigin.x, aTileOrigin.y);
+  paintTile.mTileOrigin = gfx::IntPoint(aTileOrigin.x, aTileOrigin.y);
   if (!dt || (backBufferOnWhite && !dtOnWhite)) {
     aTile.DiscardBuffers();
     return false;
   }
 
-  mMoz2DTiles.push_back(moz2DTile);
-  mTilingOrigin.x = std::min(mTilingOrigin.x, moz2DTile.mTileOrigin.x);
-  mTilingOrigin.y = std::min(mTilingOrigin.y, moz2DTile.mTileOrigin.y);
+  mPaintTiles.push_back(paintTile);
+  mTilingOrigin.x = std::min(mTilingOrigin.x, paintTile.mTileOrigin.x);
+  mTilingOrigin.y = std::min(mTilingOrigin.y, paintTile.mTileOrigin.y);
+
+  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);
 
     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)));
@@ -1389,17 +1441,17 @@ ClientMultiTiledLayerBuffer::Progressive
     // aValidRegion may have been altered by InvalidateRegion, but we still
     // want to display stale content until it gets progressively updated.
     // Create a region that includes stale content.
     nsIntRegion validOrStale;
     validOrStale.Or(updatedValidRegion, aOldValidRegion);
 
     // Paint the computed region and subtract it from the invalid region.
     PaintThebes(validOrStale, regionToPaint, remainingInvalidRegion,
-                aCallback, aCallbackData, true);
+                aCallback, aCallbackData, TilePaintFlags::Progressive);
     remainingInvalidRegion.SubOut(regionToPaint);
   } while (repeat);
 
   TILING_LOG("TILING %p: Progressive update final valid region %s buffer changed %d\n", &mPaintedLayer, Stringify(updatedValidRegion).c_str(), isBufferChanged);
   TILING_LOG("TILING %p: Progressive update final invalid region %s\n", &mPaintedLayer, Stringify(remainingInvalidRegion).c_str());
 
   // Return false if nothing has been drawn, or give what has been drawn
   // to the shadow layer to upload.
--- a/gfx/layers/client/TiledContentClient.h
+++ b/gfx/layers/client/TiledContentClient.h
@@ -37,16 +37,23 @@
 #include "mozilla/layers/ISurfaceAllocator.h"
 
 namespace mozilla {
 namespace layers {
 
 class ClientTiledPaintedLayer;
 class ClientLayerManager;
 
+enum class TilePaintFlags : uint8_t {
+  None = 0x0,
+  Async = 0x1,
+  Progressive = 0x2,
+};
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(TilePaintFlags)
+
 /**
  * Represent a single tile in tiled buffer. The buffer keeps tiles,
  * each tile keeps a reference to a texture client and a read-lock. This
  * read-lock is used to help implement a copy-on-write mechanism. The tile
  * should be locked before being sent to the compositor. The compositor should
  * unlock the read-lock as soon as it has finished with the buffer in the
  * TextureHost to prevent more textures being created than is necessary.
  * Ideal place to store per tile debug information.
@@ -117,16 +124,17 @@ struct TileClient
   * knows to upload it.
   *
   * If nullptr is returned, aTextureClientOnWhite is undefined.
   */
   TextureClient* GetBackBuffer(CompositableClient&,
                                const nsIntRegion& aDirtyRegion,
                                gfxContentType aContent, SurfaceMode aMode,
                                nsIntRegion& aAddPaintedRegion,
+                               TilePaintFlags aFlags,
                                RefPtr<TextureClient>* aTextureClientOnWhite);
 
   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
@@ -154,17 +162,18 @@ struct TileClient
 #endif
   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);
+                                   nsIntRegion& aAddPaintedRegion,
+                                   TilePaintFlags aFlags);
 };
 
 /**
  * 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 {
   /*
@@ -289,17 +298,17 @@ public:
     , mWasLastPaintProgressive(false)
   {}
 
   virtual void PaintThebes(const nsIntRegion& aNewValidRegion,
                    const nsIntRegion& aPaintRegion,
                    const nsIntRegion& aDirtyRegion,
                    LayerManager::DrawPaintedLayerCallback aCallback,
                    void* aCallbackData,
-                   bool aIsProgressive = false) = 0;
+                   TilePaintFlags aFlags) = 0;
 
   virtual bool SupportsProgressiveUpdate() = 0;
   virtual bool ProgressiveUpdate(const nsIntRegion& aValidRegion,
                          const nsIntRegion& aInvalidRegion,
                          const nsIntRegion& aOldValidRegion,
                          nsIntRegion& aOutDrawnRegion,
                          BasicTiledLayerPaintData* aPaintData,
                          LayerManager::DrawPaintedLayerCallback aCallback,
@@ -345,17 +354,17 @@ public:
                               ClientLayerManager* aManager,
                               SharedFrameMetricsHelper* aHelper);
 
   void PaintThebes(const nsIntRegion& aNewValidRegion,
                    const nsIntRegion& aPaintRegion,
                    const nsIntRegion& aDirtyRegion,
                    LayerManager::DrawPaintedLayerCallback aCallback,
                    void* aCallbackData,
-                   bool aIsProgressive = false) override;
+                   TilePaintFlags aFlags = TilePaintFlags::None) override;
 
   virtual bool SupportsProgressiveUpdate() override { return true; }
   /**
    * Performs a progressive update of a given tiled buffer.
    * See ComputeProgressiveUpdateRegion below for parameter documentation.
    * aOutDrawnRegion is an outparameter that contains the region that was
    * drawn, and which can now be added to the layer's valid region.
    */
@@ -400,43 +409,49 @@ public:
 
   SurfaceDescriptorTiles GetSurfaceDescriptorTiles();
 
   void SetResolution(float aResolution) {
     if (mResolution == aResolution) {
       return;
     }
 
-    Update(nsIntRegion(), nsIntRegion(), nsIntRegion());
+    Update(nsIntRegion(), nsIntRegion(), nsIntRegion(), TilePaintFlags::None);
     mResolution = aResolution;
   }
 
 protected:
   bool ValidateTile(TileClient& aTile,
                     const nsIntPoint& aTileRect,
-                    nsIntRegion& aDirtyRegion);
+                    nsIntRegion& aDirtyRegion,
+                    TilePaintFlags aFlags);
 
   void Update(const nsIntRegion& aNewValidRegion,
               const nsIntRegion& aPaintRegion,
-              const nsIntRegion& aDirtyRegion);
+              const nsIntRegion& aDirtyRegion,
+              TilePaintFlags aFlags);
 
   TileClient GetPlaceholderTile() const { return TileClient(); }
 
 private:
   RefPtr<ClientLayerManager> mManager;
   LayerManager::DrawPaintedLayerCallback mCallback;
   void* mCallbackData;
 
   // The region that will be made valid during Update(). Once Update() is
   // completed then this is identical to mValidRegion.
   nsIntRegion mNewValidRegion;
 
   SharedFrameMetricsHelper*  mSharedFrameMetricsHelper;
-  // When using Moz2D's CreateTiledDrawTarget we maintain a list of gfx::Tiles
-  std::vector<gfx::Tile> mMoz2DTiles;
+
+  // 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;
+
   /**
    * 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
    * position, otherwise its GetSize() methods will be broken.
--- a/gfx/layers/ipc/CompositorBridgeChild.cpp
+++ b/gfx/layers/ipc/CompositorBridgeChild.cpp
@@ -1271,16 +1271,50 @@ CompositorBridgeChild::NotifyFinishedAsy
   aState->mTextureClient = nullptr;
   if (aState->mTextureClientOnWhite) {
     aState->mTextureClientOnWhite->DropPaintThreadRef();
     aState->mTextureClientOnWhite = nullptr;
   }
 }
 
 void
+CompositorBridgeChild::NotifyBeginAsyncTiledPaint(CapturedTiledPaintState* aState)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  MonitorAutoLock lock(mPaintLock);
+
+  // We must not be waiting for paints to complete yet. This would imply we
+  // started a new paint without waiting for a previous one, which could lead to
+  // incorrect rendering or IPDL deadlocks.
+  MOZ_ASSERT(!mIsDelayingForAsyncPaints);
+
+  mOutstandingAsyncPaints++;
+
+  // Mark texture clients that they are being used for async painting, and
+  // make sure we hold them alive on the main thread.
+  for (auto& client : aState->mClients) {
+    mTextureClientsForAsyncPaint.AppendElement(client);
+  }
+  aState->mClients.clear();
+}
+
+void
+CompositorBridgeChild::NotifyFinishedAsyncTiledPaint(CapturedTiledPaintState* aState)
+{
+  MOZ_ASSERT(PaintThread::IsOnPaintThread());
+
+  MonitorAutoLock lock(mPaintLock);
+
+  mOutstandingAsyncPaints--;
+
+  aState->mTargetTiled = nullptr;
+}
+
+void
 CompositorBridgeChild::NotifyBeginAsyncEndLayerTransaction()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MonitorAutoLock lock(mPaintLock);
 
   MOZ_ASSERT(!mOutstandingAsyncEndTransaction);
   mOutstandingAsyncEndTransaction = true;
 }
--- a/gfx/layers/ipc/CompositorBridgeChild.h
+++ b/gfx/layers/ipc/CompositorBridgeChild.h
@@ -42,16 +42,17 @@ class APZCTreeManagerChild;
 class ClientLayerManager;
 class CompositorBridgeParent;
 class CompositorManagerChild;
 class CompositorOptions;
 class TextureClient;
 class TextureClientPool;
 class CapturedBufferState;
 class CapturedPaintState;
+class CapturedTiledPaintState;
 struct FrameMetrics;
 
 class CompositorBridgeChild final : public PCompositorBridgeChild,
                                     public TextureForwarder
 {
   typedef InfallibleTArray<AsyncParentMessageData> AsyncParentMessageArray;
 
 public:
@@ -236,16 +237,24 @@ public:
   // that the paint thread is going to begin painting asynchronously.
   void NotifyBeginAsyncPaint(CapturedPaintState* aState);
 
   // Must only be called from the paint thread. Notifies the CompositorBridge
   // that the paint thread has finished an asynchronous paint request.
   void NotifyFinishedAsyncPaint(CapturedPaintState* aState);
 
   // Must only be called from the main thread. Notifies the CompositorBridge
+  // that the paint thread is going to begin painting asynchronously.
+  void NotifyBeginAsyncTiledPaint(CapturedTiledPaintState* aState);
+
+  // Must only be called from the paint thread. Notifies the CompositorBridge
+  // that the paint thread has finished an asynchronous paint request.
+  void NotifyFinishedAsyncTiledPaint(CapturedTiledPaintState* aState);
+
+  // Must only be called from the main thread. Notifies the CompositorBridge
   // that the paint thread is going to perform texture synchronization at the
   // end of async painting, and should postpone messages if needed until
   // finished.
   void NotifyBeginAsyncEndLayerTransaction();
 
   // Must only be called from the paint thread. Notifies the CompositorBridge
   // that the paint thread has finished all async paints and texture syncs from
   // a given transaction and may resume sending messages.
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -2597,18 +2597,18 @@ gfxPlatform::InitOMTPConfig()
   if (mContentBackend == BackendType::CAIRO) {
     omtp.ForceDisable(FeatureStatus::Broken, "OMTP is not supported when using cairo",
       NS_LITERAL_CSTRING("FEATURE_FAILURE_COMP_PREF"));
   }
 
   if (InSafeMode()) {
     omtp.ForceDisable(FeatureStatus::Blocked, "OMTP blocked by safe-mode",
                       NS_LITERAL_CSTRING("FEATURE_FAILURE_COMP_SAFEMODE"));
-  } else if (gfxPrefs::LayersTilesEnabled()) {
-    omtp.ForceDisable(FeatureStatus::Blocked, "OMTP does not yet support tiling",
+  } else if (gfxPrefs::TileEdgePaddingEnabled()) {
+    omtp.ForceDisable(FeatureStatus::Blocked, "OMTP does not yet support tiling with edge padding",
                       NS_LITERAL_CSTRING("FEATURE_FAILURE_OMTP_TILING"));
   }
 
   if (omtp.IsEnabled()) {
     gfxVars::SetUseOMTP(true);
     reporter.SetSuccessful();
   }
 }
--- a/gfx/thebes/gfxQuartzNativeDrawing.cpp
+++ b/gfx/thebes/gfxQuartzNativeDrawing.cpp
@@ -19,17 +19,17 @@ gfxQuartzNativeDrawing::gfxQuartzNativeD
 }
 
 CGContextRef
 gfxQuartzNativeDrawing::BeginNativeDrawing()
 {
   NS_ASSERTION(!mCGContext, "BeginNativeDrawing called when drawing already in progress");
 
   DrawTarget *dt = mDrawTarget;
-  if (dt->IsDualDrawTarget() || dt->IsTiledDrawTarget() ||
+  if (dt->IsDualDrawTarget() || dt->IsTiledDrawTarget() || dt->IsCaptureDT() ||
       dt->GetBackendType() != BackendType::SKIA || dt->IsRecording()) {
     // We need a DrawTarget that we can get a CGContextRef from:
     Matrix transform = dt->GetTransform();
 
     mNativeRect = transform.TransformBounds(mNativeRect);
     mNativeRect.RoundOut();
     if (mNativeRect.IsEmpty()) {
       return nullptr;