Backed out 7 changesets (bug 1399692) failing on browser chrome browser/base/content/test/performance/browser_urlbar_search_reflows.js
authorNARCIS BELEUZU <nbeleuzu@mozilla.com>
Fri, 03 Nov 2017 14:32:46 +0200
changeset 443284 28647c01f828ad709b1c9ec09f20b66eb1983404
parent 443283 b2375c59ecd9768a469ce59bc3cf84819dc14faa
child 443285 4ada8f0d5cc011af4bc0f4fadbb25ea3e1e62bfc
push id1618
push userCallek@gmail.com
push dateThu, 11 Jan 2018 17:45:48 +0000
treeherdermozilla-release@882ca853e05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1399692
milestone58.0a1
backs oute9349ad2f1f8fec862b1d2271d0d8f25ad0814d4
cb6507b560aaad188fc83a47b664aaa5692a0acd
3d0da65640964e5a0565b5e7b7646cf719ce1449
926af2eca400cf8a16777813ceb586b1d3be7d68
b57a3f0d08478f094328b5a50c57eed35798fdf0
9fcb661df79e51c80f5ffb24717dba655dc97d73
744c8fabaa1c3e6b7322cd2b4c6672b8509c643c
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
Backed out 7 changesets (bug 1399692) failing on browser chrome browser/base/content/test/performance/browser_urlbar_search_reflows.js Backed out changeset e9349ad2f1f8 (bug 1399692) Backed out changeset cb6507b560aa (bug 1399692) Backed out changeset 3d0da6564096 (bug 1399692) Backed out changeset 926af2eca400 (bug 1399692) Backed out changeset b57a3f0d0847 (bug 1399692) Backed out changeset 9fcb661df79e (bug 1399692) Backed out changeset 744c8fabaa1c (bug 1399692)
gfx/layers/PaintThread.cpp
gfx/layers/PaintThread.h
gfx/layers/RotatedBuffer.cpp
gfx/layers/RotatedBuffer.h
gfx/layers/client/ClientPaintedLayer.cpp
gfx/layers/client/ContentClient.cpp
gfx/layers/client/ContentClient.h
gfx/layers/client/TextureClient.cpp
gfx/layers/d3d11/TextureD3D11.cpp
gfx/layers/ipc/CompositorBridgeChild.cpp
gfx/layers/ipc/CompositorBridgeChild.h
gfx/layers/moz.build
--- a/gfx/layers/PaintThread.cpp
+++ b/gfx/layers/PaintThread.cpp
@@ -16,68 +16,16 @@
 #include "mozilla/Preferences.h"
 #include "mozilla/SyncRunnable.h"
 
 namespace mozilla {
 namespace layers {
 
 using namespace gfx;
 
-bool
-CapturedBufferState::Copy::CopyBuffer()
-{
-  if (mSource->Lock(OpenMode::OPEN_READ_ONLY)) {
-    mDestination->UpdateDestinationFrom(*mSource, mBounds);
-    mSource->Unlock();
-    return true;
-  }
-  return false;
-}
-
-bool
-CapturedBufferState::Unrotate::UnrotateBuffer()
-{
-  return mBuffer->UnrotateBufferTo(mParameters);
-}
-
-bool
-CapturedBufferState::PrepareBuffer()
-{
-  return (!mBufferCopy || mBufferCopy->CopyBuffer()) &&
-         (!mBufferUnrotate || mBufferUnrotate->UnrotateBuffer());
-}
-
-void
-CapturedBufferState::GetTextureClients(nsTArray<RefPtr<TextureClient>>& aTextureClients)
-{
-  if (mBufferCopy) {
-    if (TextureClient* source = mBufferCopy->mSource->GetClient()) {
-      aTextureClients.AppendElement(source);
-    }
-    if (TextureClient* sourceOnWhite = mBufferCopy->mSource->GetClientOnWhite()) {
-      aTextureClients.AppendElement(sourceOnWhite);
-    }
-    if (TextureClient* destination = mBufferCopy->mDestination->GetClient()) {
-      aTextureClients.AppendElement(destination);
-    }
-    if (TextureClient* destinationOnWhite = mBufferCopy->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);
-    }
-  }
-}
-
 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
 {
@@ -206,80 +154,36 @@ void
 PaintThread::BeginLayerTransaction()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   MOZ_ASSERT(!mInAsyncPaintGroup);
 }
 
 void
-PaintThread::PrepareBuffer(CapturedBufferState* aState)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(aState);
-
-  // If painting asynchronously, we need to acquire the compositor bridge which
-  // owns the underlying MessageChannel. Otherwise we leave it null and use
-  // synchronous dispatch.
-  RefPtr<CompositorBridgeChild> cbc;
-  if (!gfxPrefs::LayersOMTPForceSync()) {
-    cbc = CompositorBridgeChild::Get();
-    cbc->NotifyBeginAsyncPrepareBuffer(aState);
-  }
-  RefPtr<CapturedBufferState> state(aState);
-
-  RefPtr<PaintThread> self = this;
-  RefPtr<Runnable> task = NS_NewRunnableFunction("PaintThread::PrepareBuffer",
-    [self, cbc, state]() -> void
-  {
-    self->AsyncPrepareBuffer(cbc,
-                             state);
-  });
-
-  if (cbc) {
-    sThread->Dispatch(task.forget());
-  } else {
-    SyncRunnable::DispatchToThread(sThread, task);
-  }
-}
-
-void
-PaintThread::AsyncPrepareBuffer(CompositorBridgeChild* aBridge,
-                                CapturedBufferState* aState)
-{
-  MOZ_ASSERT(IsOnPaintThread());
-  MOZ_ASSERT(aState);
-
-  if (!aState->PrepareBuffer()) {
-    gfxCriticalNote << "Failed to prepare buffers on the paint thread.";
-  }
-
-  aBridge->NotifyFinishedAsyncPrepareBuffer(aState);
-}
-
-void
 PaintThread::PaintContents(CapturedPaintState* aState,
                            PrepDrawTargetForPaintingCallback aCallback)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aState);
 
   // If painting asynchronously, we need to acquire the compositor bridge which
   // owns the underlying MessageChannel. Otherwise we leave it null and use
   // synchronous dispatch.
   RefPtr<CompositorBridgeChild> cbc;
   if (!gfxPrefs::LayersOMTPForceSync()) {
     cbc = CompositorBridgeChild::Get();
     cbc->NotifyBeginAsyncPaint(aState);
   }
   RefPtr<CapturedPaintState> state(aState);
+  RefPtr<DrawTargetCapture> capture(aState->mCapture);
 
   RefPtr<PaintThread> self = this;
   RefPtr<Runnable> task = NS_NewRunnableFunction("PaintThread::PaintContents",
-    [self, cbc, state, aCallback]() -> void
+    [self, cbc, capture, state, aCallback]() -> void
   {
     self->AsyncPaintContents(cbc,
                              state,
                              aCallback);
   });
 
   if (cbc) {
     sThread->Dispatch(task.forget());
--- a/gfx/layers/PaintThread.h
+++ b/gfx/layers/PaintThread.h
@@ -7,17 +7,16 @@
 #ifndef MOZILLA_LAYERS_PAINTTHREAD_H
 #define MOZILLA_LAYERS_PAINTTHREAD_H
 
 #include "base/platform_thread.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/layers/TextureClient.h"
-#include "RotatedBuffer.h"
 #include "nsThreadUtils.h"
 
 namespace mozilla {
 namespace gfx {
 class DrawTarget;
 class DrawTargetCapture;
 };
 
@@ -54,66 +53,16 @@ public:
   gfx::Matrix mTargetTransform;
   SurfaceMode mSurfaceMode;
   gfxContentType mContentType;
 
 protected:
   virtual ~CapturedPaintState() {}
 };
 
-// Holds the key operations for a ContentClient to prepare
-// its buffers for painting
-class CapturedBufferState final {
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CapturedBufferState)
-public:
-  struct Copy {
-    Copy(RefPtr<RotatedBuffer> aSource,
-         RefPtr<RotatedBuffer> aDestination,
-         gfx::IntRect aBounds)
-      : mSource(aSource)
-      , mDestination(aDestination)
-      , mBounds(aBounds)
-    {}
-
-    bool CopyBuffer();
-
-    RefPtr<RotatedBuffer> mSource;
-    RefPtr<RotatedBuffer> mDestination;
-    gfx::IntRect mBounds;
-  };
-
-  struct Unrotate {
-    Unrotate(RotatedBuffer::Parameters aParameters,
-             RefPtr<RotatedBuffer> aBuffer)
-      : mParameters(aParameters)
-      , mBuffer(aBuffer)
-    {}
-
-    bool UnrotateBuffer();
-
-    RotatedBuffer::Parameters mParameters;
-    RefPtr<RotatedBuffer> mBuffer;
-  };
-
-  /**
-   * 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<Unrotate> mBufferUnrotate;
-
-protected:
-  ~CapturedBufferState() {}
-};
-
 typedef bool (*PrepDrawTargetForPaintingCallback)(CapturedPaintState* aPaintState);
 
 class CompositorBridgeChild;
 
 class PaintThread final
 {
   friend void DestroyPaintThread(UniquePtr<PaintThread>&& aPaintThread);
 
@@ -126,18 +75,16 @@ public:
   static bool IsOnPaintThread();
 
   // Must be called on the main thread. Signifies that a new layer transaction
   // is beginning. This must be called immediately after FlushAsyncPaints, and
   // before any new painting occurs, as there can't be any async paints queued
   // or running while this is executing.
   void BeginLayerTransaction();
 
-  void PrepareBuffer(CapturedBufferState* aState);
-
   void PaintContents(CapturedPaintState* aState,
                      PrepDrawTargetForPaintingCallback aCallback);
 
   // 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();
 
@@ -158,18 +105,16 @@ public:
 
 private:
   PaintThread();
 
   bool Init();
   void ShutdownOnPaintThread();
   void InitOnPaintThread();
 
-  void AsyncPrepareBuffer(CompositorBridgeChild* aBridge,
-                          CapturedBufferState* aState);
   void AsyncPaintContents(CompositorBridgeChild* aBridge,
                           CapturedPaintState* aState,
                           PrepDrawTargetForPaintingCallback aCallback);
   void AsyncEndLayer();
   void AsyncEndLayerTransaction(CompositorBridgeChild* aBridge,
                                 SyncObjectClient* aSyncObject);
 
   static StaticAutoPtr<PaintThread> sSingleton;
--- a/gfx/layers/RotatedBuffer.cpp
+++ b/gfx/layers/RotatedBuffer.cpp
@@ -286,126 +286,114 @@ WrapRotationAxis(int32_t* aRotationPoint
   if (*aRotationPoint < 0) {
     *aRotationPoint += aSize;
   } else if (*aRotationPoint >= aSize) {
     *aRotationPoint -= aSize;
   }
 }
 
 bool
-RotatedBuffer::Parameters::IsRotated() const
-{
-  return mBufferRotation != IntPoint(0,0);
-}
-
-bool
-RotatedBuffer::Parameters::RectWrapsBuffer(const gfx::IntRect& aRect) const
-{
-  int32_t xBoundary = mBufferRect.XMost() - mBufferRotation.x;
-  int32_t yBoundary = mBufferRect.YMost() - mBufferRotation.y;
-  return (aRect.x < xBoundary && xBoundary < aRect.XMost()) ||
-         (aRect.y < yBoundary && yBoundary < aRect.YMost());
-}
-
-void
-RotatedBuffer::Parameters::SetUnrotated()
-{
-  mBufferRotation = IntPoint(0,0);
-  mDidSelfCopy = true;
-}
-
-RotatedBuffer::Parameters
-RotatedBuffer::AdjustedParameters(const gfx::IntRect& aDestBufferRect) const
+RotatedBuffer::AdjustTo(const gfx::IntRect& aDestBufferRect,
+                        const gfx::IntRect& aDrawBounds,
+                        bool aCanHaveRotation,
+                        bool aCanDrawRotated)
 {
   IntRect keepArea;
   if (keepArea.IntersectRect(aDestBufferRect, mBufferRect)) {
     // Set mBufferRotation so that the pixels currently in mDTBuffer
     // will still be rendered in the right place when mBufferRect
     // changes to aDestBufferRect.
     IntPoint newRotation = mBufferRotation +
       (aDestBufferRect.TopLeft() - mBufferRect.TopLeft());
     WrapRotationAxis(&newRotation.x, mBufferRect.Width());
     WrapRotationAxis(&newRotation.y, mBufferRect.Height());
     NS_ASSERTION(gfx::IntRect(gfx::IntPoint(0,0), mBufferRect.Size()).Contains(newRotation),
                  "newRotation out of bounds");
 
-    return Parameters{aDestBufferRect, newRotation};
-  }
+    int32_t xBoundary = aDestBufferRect.XMost() - newRotation.x;
+    int32_t yBoundary = aDestBufferRect.YMost() - newRotation.y;
+    bool drawWrapsBuffer = (aDrawBounds.x < xBoundary && xBoundary < aDrawBounds.XMost()) ||
+                           (aDrawBounds.y < yBoundary && yBoundary < aDrawBounds.YMost());
+
+    if ((drawWrapsBuffer && !aCanDrawRotated) ||
+        (newRotation != IntPoint(0,0) && !aCanHaveRotation)) {
+      // The stuff we need to redraw will wrap around an edge of the
+      // buffer (and the caller doesn't know how to support that), so
+      // move the pixels we can keep into a position that lets us
+      // redraw in just one quadrant.
+      RefPtr<gfx::DrawTarget> dtBuffer = GetDTBuffer();
+      RefPtr<gfx::DrawTarget> dtBufferOnWhite = GetDTBufferOnWhite();
 
-  // No pixels are going to be kept. The whole visible region
-  // will be redrawn, so we don't need to copy anything, so we don't
-  // set destBuffer.
-  return Parameters{aDestBufferRect, IntPoint(0,0)};
-}
+      if (mBufferRotation == IntPoint(0,0)) {
+        IntRect srcRect(IntPoint(0, 0), mBufferRect.Size());
+        IntPoint dest = mBufferRect.TopLeft() - aDestBufferRect.TopLeft();
+
+        MOZ_ASSERT(dtBuffer && dtBuffer->IsValid());
+        dtBuffer->CopyRect(srcRect, dest);
+        if (HaveBufferOnWhite()) {
+          MOZ_ASSERT(dtBufferOnWhite && dtBufferOnWhite->IsValid());
+          dtBufferOnWhite->CopyRect(srcRect, dest);
+        }
+
+        mDidSelfCopy = true;
+        mBufferRect = aDestBufferRect;
+      } else {
+        // With azure and a data surface perform an buffer unrotate
+        // (SelfCopy).
+        unsigned char* data;
+        IntSize size;
+        int32_t stride;
+        SurfaceFormat format;
 
-bool
-RotatedBuffer::UnrotateBufferTo(const Parameters& aParameters)
-{
-  RefPtr<gfx::DrawTarget> dtBuffer = GetDTBuffer();
-  RefPtr<gfx::DrawTarget> dtBufferOnWhite = GetDTBufferOnWhite();
+        if (dtBuffer->LockBits(&data, &size, &stride, &format)) {
+          uint8_t bytesPerPixel = BytesPerPixel(format);
+          BufferUnrotate(data,
+                         size.width * bytesPerPixel,
+                         size.height, stride,
+                         newRotation.x * bytesPerPixel, newRotation.y);
+          dtBuffer->ReleaseBits(data);
 
-  if (mBufferRotation == IntPoint(0,0)) {
-    IntRect srcRect(IntPoint(0, 0), mBufferRect.Size());
-    IntPoint dest = mBufferRect.TopLeft() - aParameters.mBufferRect.TopLeft();
+          if (HaveBufferOnWhite()) {
+            MOZ_ASSERT(dtBufferOnWhite && dtBufferOnWhite->IsValid());
+            dtBufferOnWhite->LockBits(&data, &size, &stride, &format);
+            uint8_t bytesPerPixel = BytesPerPixel(format);
+            BufferUnrotate(data,
+                           size.width * bytesPerPixel,
+                           size.height, stride,
+                           newRotation.x * bytesPerPixel, newRotation.y);
+            dtBufferOnWhite->ReleaseBits(data);
+          }
 
-    MOZ_ASSERT(dtBuffer && dtBuffer->IsValid());
-    dtBuffer->CopyRect(srcRect, dest);
-    if (HaveBufferOnWhite()) {
-      MOZ_ASSERT(dtBufferOnWhite && dtBufferOnWhite->IsValid());
-      dtBufferOnWhite->CopyRect(srcRect, dest);
+          // Buffer unrotate moves all the pixels
+          mDidSelfCopy = true;
+          mBufferRect = aDestBufferRect;
+          mBufferRotation = IntPoint(0, 0);
+        }
+
+        if (!mDidSelfCopy) {
+          // We couldn't unrotate the buffer, so we need to create a
+          // new one and start from scratch
+          return false;
+        }
+      }
+    } else {
+      mBufferRect = aDestBufferRect;
+      mBufferRotation = newRotation;
     }
   } else {
-    // With azure and a data surface perform an buffer unrotate
-    // (SelfCopy).
-    unsigned char* data;
-    IntSize size;
-    int32_t stride;
-    SurfaceFormat format;
-
-    if (dtBuffer->LockBits(&data, &size, &stride, &format)) {
-      uint8_t bytesPerPixel = BytesPerPixel(format);
-      BufferUnrotate(data,
-                     size.width * bytesPerPixel,
-                     size.height, stride,
-                     aParameters.mBufferRotation.x * bytesPerPixel,
-                     aParameters.mBufferRotation.y);
-      dtBuffer->ReleaseBits(data);
+    // No pixels are going to be kept. The whole visible region
+    // will be redrawn, so we don't need to copy anything, so we don't
+    // set destBuffer.
+    mBufferRect = aDestBufferRect;
+    mBufferRotation = IntPoint(0,0);
+  }
 
-      if (HaveBufferOnWhite()) {
-        MOZ_ASSERT(dtBufferOnWhite && dtBufferOnWhite->IsValid());
-        dtBufferOnWhite->LockBits(&data, &size, &stride, &format);
-        uint8_t bytesPerPixel = BytesPerPixel(format);
-        BufferUnrotate(data,
-                       size.width * bytesPerPixel,
-                       size.height, stride,
-                       aParameters.mBufferRotation.x * bytesPerPixel,
-                       aParameters.mBufferRotation.y);
-        dtBufferOnWhite->ReleaseBits(data);
-      }
-    } else {
-      return false;
-    }
-  }
   return true;
 }
 
-void
-RotatedBuffer::SetParameters(const RotatedBuffer::Parameters& aParameters)
-{
-  mBufferRect = aParameters.mBufferRect;
-  mBufferRotation = aParameters.mBufferRotation;
-  mDidSelfCopy = aParameters.mDidSelfCopy;
-}
-
-RotatedBuffer::ContentType
-RotatedBuffer::GetContentType() const
-{
-  return ContentForFormat(GetFormat());
-}
-
 DrawTarget*
 RotatedBuffer::BorrowDrawTargetForQuadrantUpdate(const IntRect& aBounds,
                                                  ContextSource aSource,
                                                  DrawIterator* aIter,
                                                  bool aSetTransform,
                                                  Matrix* aOutMatrix)
 {
   IntRect bounds = aBounds;
@@ -492,27 +480,27 @@ RemoteRotatedBuffer::Lock(OpenMode aMode
   if (!locked) {
     Unlock();
     return false;
   }
 
   mTarget = mClient->BorrowDrawTarget();
   if (!mTarget || !mTarget->IsValid()) {
     gfxCriticalNote << "Invalid draw target " << hexa(mTarget)
-                    << " in RemoteRotatedBuffer::Lock";
+                    << "in RemoteRotatedBuffer::Lock";
     Unlock();
     return false;
   }
 
   if (mClientOnWhite) {
     mTargetOnWhite = mClientOnWhite->BorrowDrawTarget();
     if (!mTargetOnWhite || !mTargetOnWhite->IsValid()) {
       gfxCriticalNote << "Invalid draw target(s) " << hexa(mTarget)
                       << " and " << hexa(mTargetOnWhite)
-                      << " in RemoteRotatedBuffer::Lock";
+                      << "in RemoteRotatedBuffer::Lock";
       Unlock();
       return false;
     }
   }
 
   return true;
 }
 
--- a/gfx/layers/RotatedBuffer.h
+++ b/gfx/layers/RotatedBuffer.h
@@ -19,19 +19,21 @@
 #include "nsDebug.h"                    // for NS_RUNTIMEABORT
 #include "nsISupportsImpl.h"            // for MOZ_COUNT_CTOR, etc
 #include "nsRegion.h"                   // for nsIntRegion
 #include "LayersTypes.h"
 
 namespace mozilla {
 namespace layers {
 
+class CapturedPaintState;
+
+typedef bool (*PrepDrawTargetForPaintingCallback)(CapturedPaintState*);
+
 class PaintedLayer;
-class CapturedBufferState;
-class ContentClient;
 
 // Mixin class for classes which need logic for loaning out a draw target.
 // See comments on BorrowDrawTargetForQuadrantUpdate.
 class BorrowDrawTarget
 {
 public:
   void ReturnDrawTarget(gfx::DrawTarget*& aReturned);
 
@@ -151,48 +153,25 @@ public:
    */
   gfx::DrawTarget*
   BorrowDrawTargetForQuadrantUpdate(const gfx::IntRect& aBounds,
                                     ContextSource aSource,
                                     DrawIterator* aIter,
                                     bool aSetTransform = true,
                                     gfx::Matrix* aOutTransform = nullptr);
 
-  struct Parameters {
-    Parameters(const gfx::IntRect& aBufferRect,
-               const gfx::IntPoint& aBufferRotation)
-      : mBufferRect(aBufferRect)
-      , mBufferRotation(aBufferRotation)
-      , mDidSelfCopy(false)
-    {
-    }
-
-    bool IsRotated() const;
-    bool RectWrapsBuffer(const gfx::IntRect& aRect) const;
-
-    void SetUnrotated();
-
-    gfx::IntRect  mBufferRect;
-    gfx::IntPoint mBufferRotation;
-    bool mDidSelfCopy;
-  };
-
   /**
-   * Returns the new buffer parameters for rotating to a
-   * destination buffer rect.
+   * Adjusts the buffer to be centered on the destination buffer rect,
+   * and ready to draw the specified bounds. Returns whether a new buffer
+   * needs to be created.
    */
-  Parameters AdjustedParameters(const gfx::IntRect& aDestBufferRect) const;
-
-  /**
-   * Unrotates the pixels of the rotated buffer for the specified
-   * new buffer parameters.
-   */
-  bool UnrotateBufferTo(const Parameters& aParameters);
-
-  void SetParameters(const Parameters& aParameters);
+  bool AdjustTo(const gfx::IntRect& aDestBufferRect,
+                const gfx::IntRect& aDrawBounds,
+                bool aCanHaveRotation,
+                bool aCanDrawRotated);
 
   /**
    * |BufferRect()| is the rect of device pixels that this
    * RotatedBuffer covers.  That is what DrawBufferWithRotation()
    * will paint when it's called.
    */
   const gfx::IntRect& BufferRect() const { return mBufferRect; }
   const gfx::IntPoint& BufferRotation() const { return mBufferRotation; }
@@ -220,49 +199,30 @@ public:
    */
   bool DidSelfCopy() const { return mDidSelfCopy; }
 
   /**
    * Clears the self copy flag.
    */
   void ClearDidSelfCopy() { mDidSelfCopy = false; }
 
-  /**
-   * Gets the content type for this buffer.
-   */
-  ContentType GetContentType() const;
-
   virtual bool IsLocked() = 0;
   virtual bool Lock(OpenMode aMode) = 0;
   virtual void Unlock() = 0;
 
   virtual bool HaveBuffer() const = 0;
   virtual bool HaveBufferOnWhite() const = 0;
 
   virtual gfx::SurfaceFormat GetFormat() const = 0;
 
   virtual already_AddRefed<gfx::SourceSurface> GetSourceSurface(ContextSource aSource) const = 0;
 
   virtual gfx::DrawTarget* GetDTBuffer() const = 0;
   virtual gfx::DrawTarget* GetDTBufferOnWhite() const = 0;
 
-  virtual TextureClient* GetClient() const {
-    return nullptr;
-  }
-  virtual TextureClient* GetClientOnWhite() const {
-    return nullptr;
-  }
-
-  /**
-   * Creates a shallow copy of the rotated buffer with the same underlying
-   * texture clients and draw targets. Rotated buffers are not thread safe,
-   * so a copy needs to be sent for off main thread painting.
-   */
-  virtual RefPtr<RotatedBuffer> ShallowCopy() const = 0;
-
 protected:
   virtual ~RotatedBuffer() {}
 
   enum XSide {
     LEFT, RIGHT
   };
   enum YSide {
     TOP, BOTTOM
@@ -330,42 +290,23 @@ public:
 
   virtual gfx::SurfaceFormat GetFormat() const override;
 
   virtual already_AddRefed<gfx::SourceSurface> GetSourceSurface(ContextSource aSource) const override;
 
   virtual gfx::DrawTarget* GetDTBuffer() const override;
   virtual gfx::DrawTarget* GetDTBufferOnWhite() const override;
 
-  virtual TextureClient* GetClient() const override { return mClient; }
-  virtual TextureClient* GetClientOnWhite() const override { return mClientOnWhite; }
-
-  virtual RefPtr<RotatedBuffer> ShallowCopy() const {
-    return new RemoteRotatedBuffer {
-      mClient, mClientOnWhite,
-      mTarget, mTargetOnWhite,
-      mBufferRect, mBufferRotation
-    };
-  }
+  TextureClient* GetClient() const { return mClient; }
+  TextureClient* GetClientOnWhite() const { return mClientOnWhite; }
 
   void SyncWithObject(SyncObjectClient* aSyncObject);
   void Clear();
 
 private:
-  RemoteRotatedBuffer(TextureClient* aClient, TextureClient* aClientOnWhite,
-                      gfx::DrawTarget* aTarget, gfx::DrawTarget* aTargetOnWhite,
-                      const gfx::IntRect& aBufferRect,
-                      const gfx::IntPoint& aBufferRotation)
-    : RotatedBuffer(aBufferRect, aBufferRotation)
-    , mClient(aClient)
-    , mClientOnWhite(aClientOnWhite)
-    , mTarget(aTarget)
-    , mTargetOnWhite(aTargetOnWhite)
-  { }
-
   RefPtr<TextureClient> mClient;
   RefPtr<TextureClient> mClientOnWhite;
 
   RefPtr<gfx::DrawTarget> mTarget;
   RefPtr<gfx::DrawTarget> mTargetOnWhite;
 };
 
 /**
@@ -392,23 +333,16 @@ public:
 
   virtual gfx::SurfaceFormat GetFormat() const override;
 
   virtual already_AddRefed<gfx::SourceSurface> GetSourceSurface(ContextSource aSource) const override;
 
   virtual gfx::DrawTarget* GetDTBuffer() const override;
   virtual gfx::DrawTarget* GetDTBufferOnWhite() const override;
 
-  virtual RefPtr<RotatedBuffer> ShallowCopy() const {
-    return new DrawTargetRotatedBuffer {
-        mTarget, mTargetOnWhite,
-        mBufferRect, mBufferRotation
-      };
-  }
-
 private:
   RefPtr<gfx::DrawTarget> mTarget;
   RefPtr<gfx::DrawTarget> mTargetOnWhite;
 };
 
 /**
  * SourceRotatedBuffer is a rotated buffer that is backed by source surfaces,
  * and may only be used to draw into other buffers or be read directly.
@@ -433,20 +367,16 @@ public:
   virtual gfx::SurfaceFormat GetFormat() const override;
 
   virtual bool HaveBuffer() const override { return !!mSource; }
   virtual bool HaveBufferOnWhite() const override { return !!mSourceOnWhite; }
 
   virtual gfx::DrawTarget* GetDTBuffer() const override { return nullptr; }
   virtual gfx::DrawTarget* GetDTBufferOnWhite() const override { return nullptr; }
 
-  virtual RefPtr<RotatedBuffer> ShallowCopy() const {
-    return nullptr;
-  }
-
 private:
   RefPtr<gfx::SourceSurface> mSource;
   RefPtr<gfx::SourceSurface> mSourceOnWhite;
 };
 
 } // namespace layers
 } // namespace mozilla
 
--- a/gfx/layers/client/ClientPaintedLayer.cpp
+++ b/gfx/layers/client/ClientPaintedLayer.cpp
@@ -205,27 +205,21 @@ ClientPaintedLayer::PaintThebes(nsTArray
  *     the main thread. Sync OMTP is only meant to be used as a debugging tool.
  */
 bool
 ClientPaintedLayer::PaintOffMainThread()
 {
   uint32_t flags = GetPaintFlags();
 
   PaintState state = mContentClient->BeginPaint(this, flags | ContentClient::PAINT_ASYNC);
-  bool didUpdate = false;
-
-  if (state.mBufferState) {
-    PaintThread::Get()->PrepareBuffer(state.mBufferState);
-    didUpdate = true;
-  }
-
   if (!UpdatePaintRegion(state)) {
     return false;
   }
 
+  bool didUpdate = false;
   RotatedBuffer::DrawIterator iter;
 
   // Debug Protip: Change to BorrowDrawTargetForPainting if using sync OMTP.
   while (RefPtr<CapturedPaintState> captureState =
           mContentClient->BorrowDrawTargetForRecording(state, &iter))
   {
     DrawTarget* target = captureState->mTargetDual;
     if (!target || !target->IsValid()) {
--- a/gfx/layers/client/ContentClient.cpp
+++ b/gfx/layers/client/ContentClient.cpp
@@ -18,17 +18,16 @@
 #include "mozilla/gfx/BasePoint.h"      // for BasePoint
 #include "mozilla/gfx/BaseSize.h"       // for BaseSize
 #include "mozilla/gfx/Rect.h"           // for Rect
 #include "mozilla/gfx/Types.h"
 #include "mozilla/layers/CompositorBridgeChild.h" // for CompositorBridgeChild
 #include "mozilla/layers/LayerManagerComposite.h"
 #include "mozilla/layers/LayersMessages.h"  // for ThebesBufferData
 #include "mozilla/layers/LayersTypes.h"
-#include "mozilla/layers/PaintThread.h"
 #include "nsDebug.h"                    // for NS_ASSERTION, NS_WARNING, etc
 #include "nsISupportsImpl.h"            // for gfxContext::Release, etc
 #include "nsIWidget.h"                  // for nsIWidget
 #include "nsLayoutUtils.h"
 #ifdef XP_WIN
 #include "gfxWindowsPlatform.h"
 #endif
 #ifdef MOZ_WIDGET_GTK
@@ -123,163 +122,123 @@ ContentClient::BeginPaint(PaintedLayer* 
 
   if (!dest.mCanKeepBufferContents) {
     // We're effectively clearing the valid region, so we need to draw
     // the entire needed region now.
     MOZ_ASSERT(!dest.mCanReuseBuffer);
     MOZ_ASSERT(dest.mValidRegion.IsEmpty());
 
     result.mRegionToInvalidate = aLayer->GetValidRegion();
+    Clear();
 
 #if defined(MOZ_DUMP_PAINTING)
     if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
-      if (result.mContentType != mBuffer->GetContentType()) {
+      if (result.mContentType != BufferContentType()) {
         printf_stderr("Invalidating entire rotated buffer (layer %p): content type changed\n", aLayer);
       } else if ((dest.mBufferMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) != mBuffer->HaveBufferOnWhite()) {
         printf_stderr("Invalidating entire rotated buffer (layer %p): component alpha changed\n", aLayer);
       }
     }
 #endif
-    Clear();
   }
 
   result.mRegionToDraw.Sub(dest.mNeededRegion,
                            dest.mValidRegion);
 
   if (result.mRegionToDraw.IsEmpty())
     return result;
 
+  OpenMode lockMode = aFlags & PAINT_ASYNC ? OpenMode::OPEN_READ_ASYNC_WRITE
+                                           : OpenMode::OPEN_READ_WRITE;
+
+  if (mBuffer) {
+    if (mBuffer->Lock(lockMode)) {
+      // Do not modify result.mRegionToDraw or result.mContentType after this call.
+      // Do not modify the back buffer's bufferRect, bufferRotation, or didSelfCopy.
+      FinalizeFrame(result.mRegionToDraw);
+    } else {
+      // Abandon everything and redraw it all. Ideally we'd reallocate and copy
+      // the old to the new and then call FinalizeFrame on the new buffer so that
+      // we only need to draw the latest bits, but we need a big refactor to support
+      // that ordering.
+      result.mRegionToDraw = dest.mNeededRegion;
+      dest.mCanReuseBuffer = false;
+      Clear();
+    }
+  }
+
   // We need to disable rotation if we're going to be resampled when
   // drawing, because we might sample across the rotation boundary.
   // 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::OPEN_READ_WRITE;
-
-  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);
-
-      if ((!canHaveRotation && newParameters.IsRotated()) ||
-          (!canDrawRotated && newParameters.RectWrapsBuffer(drawBounds))) {
-        bufferState->mBufferUnrotate = Some(CapturedBufferState::Unrotate {
-          newParameters,
-          mBuffer->ShallowCopy(),
-        });
-      }
+  RefPtr<RotatedBuffer> newBuffer;
+  uint32_t bufferFlags = 0;
+  if (dest.mBufferMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
+    bufferFlags |= BUFFER_COMPONENT_ALPHA;
+  }
+  if (dest.mCanReuseBuffer && mBuffer) {
+    if (!mBuffer->AdjustTo(dest.mBufferRect,
+                           drawBounds,
+                           canHaveRotation,
+                           canDrawRotated)) {
+      dest.mBufferRect = ComputeBufferRect(dest.mNeededRegion.GetBounds());
+      newBuffer = CreateBuffer(result.mContentType, dest.mBufferRect, bufferFlags);
 
-      // 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;
-
-          // 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;
+      if (!newBuffer) {
+        if (Factory::ReasonableSurfaceSize(IntSize(dest.mBufferRect.Width(), dest.mBufferRect.Height()))) {
+          gfxCriticalNote << "Failed 1 buffer for "
+                          << dest.mBufferRect.x << ", "
+                          << dest.mBufferRect.y << ", "
+                          << dest.mBufferRect.Width() << ", "
+                          << dest.mBufferRect.Height();
         }
-      } else {
-        if (bufferState->PrepareBuffer()) {
-          if (bufferState->mBufferUnrotate) {
-            newParameters.SetUnrotated();
-          }
-          mBuffer->SetParameters(newParameters);
-          canReuseBuffer = true;
-        }
+        return result;
       }
     }
-
-    if (!canReuseBuffer) {
-      if (mBuffer->IsLocked()) {
-        mBuffer->Unlock();
-      }
-      dest.mBufferRect = ComputeBufferRect(dest.mNeededRegion.GetBounds());
-      dest.mCanReuseBuffer = false;
-    }
-  }
-
-  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,
-  // we need to allocate a new one and prepare it for drawing.
-  if (!dest.mCanReuseBuffer) {
-    uint32_t bufferFlags = 0;
-    if (dest.mBufferMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
-      bufferFlags |= BUFFER_COMPONENT_ALPHA;
-    }
-
-    RefPtr<RotatedBuffer> newBuffer = CreateBuffer(result.mContentType,
-                                                   dest.mBufferRect,
-                                                   bufferFlags);
-
+  } else {
+    // The buffer's not big enough, so allocate a new one
+    newBuffer = CreateBuffer(result.mContentType, dest.mBufferRect, bufferFlags);
     if (!newBuffer) {
       if (Factory::ReasonableSurfaceSize(IntSize(dest.mBufferRect.Width(), dest.mBufferRect.Height()))) {
-        gfxCriticalNote << "Failed buffer for "
+        gfxCriticalNote << "Failed 2 buffer for "
                         << dest.mBufferRect.x << ", "
                         << dest.mBufferRect.y << ", "
                         << dest.mBufferRect.Width() << ", "
                         << dest.mBufferRect.Height();
       }
       return result;
     }
+  }
 
+  NS_ASSERTION(!(aFlags & PAINT_WILL_RESAMPLE) || dest.mBufferRect == dest.mNeededRegion.GetBounds(),
+               "If we're resampling, we need to validate the entire buffer");
+
+  // If needed, copy the old buffer over to the new one
+  if (newBuffer) {
     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()) {
-      RefPtr<CapturedBufferState> bufferState = new CapturedBufferState();
-
-      bufferState->mBufferCopy = Some(CapturedBufferState::Copy {
-        frontBuffer->ShallowCopy(),
-        newBuffer->ShallowCopy(),
-        newBuffer->BufferRect(),
-      });
+    if (mBuffer) {
+      newBuffer->UpdateDestinationFrom(*mBuffer, newBuffer->BufferRect());
 
-      // 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;
-      } else {
-        if (!bufferState->PrepareBuffer()) {
-          gfxCriticalNote << "Failed to copy front buffer to back buffer.";
-          return result;
-        }
-      }
+      // We are done with the old back buffer now and it is about to be
+      // destroyed, so unlock it.
+      mBuffer->Unlock();
     }
 
     // Ensure our reference to the front buffer is released
-    // as well as the old back buffer
+    // as well as the old back buffer.
     Clear();
 
     mBuffer = newBuffer;
   }
 
   NS_ASSERTION(canHaveRotation || mBuffer->BufferRotation() == IntPoint(0,0),
                "Rotation disabled, but we have nonzero rotation?");
 
@@ -420,19 +379,17 @@ ContentClient::CalculateBufferForPaint(P
   nsIntRegion validRegion = aLayer->GetValidRegion();
 
   bool canReuseBuffer = !!mBuffer;
   bool canKeepBufferContents = true;
 
   while (true) {
     mode = aLayer->GetSurfaceMode();
     neededRegion = aLayer->GetVisibleRegion().ToUnknownRegion();
-    canReuseBuffer = canReuseBuffer && ValidBufferSize(mBufferSizePolicy,
-                                                       mBuffer->BufferRect().Size(),
-                                                       neededRegion.GetBounds().Size());
+    canReuseBuffer = canReuseBuffer && BufferSizeOkFor(neededRegion.GetBounds().Size());
     contentType = layerContentType;
 
     if (canReuseBuffer) {
       if (mBuffer->BufferRect().Contains(neededRegion.GetBounds())) {
         // We don't need to adjust mBufferRect.
         destBufferRect = mBuffer->BufferRect();
       } else if (neededRegion.GetBounds().Size() <= mBuffer->BufferRect().Size()) {
         // The buffer's big enough but doesn't contain everything that's
@@ -473,22 +430,21 @@ ContentClient::CalculateBufferForPaint(P
 
       // We need to validate the entire buffer, to make sure that only valid
       // pixels are sampled.
       neededRegion = destBufferRect;
     }
 
     // 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.
-    RefPtr<RotatedBuffer> frontBuffer = GetFrontBuffer();
-    bool needsComponentAlpha = (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA);
-    bool changedSurfaceOrContent = frontBuffer &&
-                                   (contentType != frontBuffer->GetContentType() ||
-                                    needsComponentAlpha != frontBuffer->HaveBufferOnWhite());
-    if (canKeepBufferContents && changedSurfaceOrContent) {
+    if (canKeepBufferContents &&
+        mBuffer &&
+        (contentType != BufferContentType() ||
+        (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) != mBuffer->HaveBufferOnWhite()))
+    {
       // 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;
     }
 
@@ -504,30 +460,32 @@ ContentClient::CalculateBufferForPaint(P
   dest.mBufferRect = destBufferRect;
   dest.mBufferMode = mode;
   dest.mBufferContentType = contentType;
   dest.mCanReuseBuffer = canReuseBuffer;
   dest.mCanKeepBufferContents = canKeepBufferContents;
   return dest;
 }
 
-bool
-ContentClient::ValidBufferSize(BufferSizePolicy aPolicy,
-                               const gfx::IntSize& aBufferSize,
-                               const gfx::IntSize& aVisibleBoundsSize)
+gfxContentType
+ContentClient::BufferContentType()
 {
-  return (aVisibleBoundsSize == aBufferSize ||
-          (SizedToVisibleBounds != aPolicy &&
-           aVisibleBoundsSize < aBufferSize));
+  if (mBuffer) {
+    return ContentForFormat(mBuffer->GetFormat());
+  }
+  return gfxContentType::SENTINEL;
 }
 
-RefPtr<RotatedBuffer>
-ContentClient::GetFrontBuffer() const
+bool
+ContentClient::BufferSizeOkFor(const IntSize& aSize)
 {
-  return mBuffer;
+  MOZ_ASSERT(mBuffer);
+  return (aSize == mBuffer->BufferRect().Size() ||
+          (SizedToVisibleBounds != mBufferSizePolicy &&
+           aSize < mBuffer->BufferRect().Size()));
 }
 
 void
 ContentClient::PrintInfo(std::stringstream& aStream, const char* aPrefix)
 {
   aStream << aPrefix;
   aStream << nsPrintfCString("ContentClient (0x%p)", this).get();
 }
@@ -878,85 +836,97 @@ 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)
+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.");
+    MOZ_ASSERT(!mFrontBuffer->DidSelfCopy(), "If we have to copy the world, then our buffers are different, right?");
     return;
   }
-
-  MOZ_ASSERT(mFrontBuffer && mBuffer);
-  if (!mFrontBuffer || !mBuffer) {
+  MOZ_ASSERT(mFrontBuffer);
+  if (!mFrontBuffer) {
     return;
   }
 
   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(mBuffer->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;
   }
 
-  MOZ_ASSERT(!aPrepareState->mBufferCopy);
-  aPrepareState->mBufferCopy = Some(CapturedBufferState::Copy {
-    mFrontBuffer->ShallowCopy(),
-    mBuffer->ShallowCopy(),
-    updateRegion.GetBounds(),
-  });
+  if (!mBuffer) {
+    return;
+  }
+
+  if (mFrontBuffer->Lock(OpenMode::OPEN_READ_ONLY)) {
+    mBuffer->UpdateDestinationFrom(*mFrontBuffer, updateRegion.GetBounds());
+    mFrontBuffer->Unlock();
+  }
+}
+
+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
@@ -17,17 +17,16 @@
 #include "mozilla/gfx/Point.h"          // for IntSize
 #include "mozilla/layers/CompositableClient.h"  // for CompositableClient
 #include "mozilla/layers/CompositableForwarder.h"
 #include "mozilla/layers/CompositorTypes.h"  // for TextureInfo, etc
 #include "mozilla/layers/ISurfaceAllocator.h"
 #include "mozilla/layers/LayersSurfaces.h"  // for SurfaceDescriptor
 #include "mozilla/layers/LayersTypes.h"  // for TextureDumpMode
 #include "mozilla/layers/TextureClient.h"  // for TextureClient
-#include "mozilla/layers/PaintThread.h"  // for CapturedBufferState
 #include "mozilla/Maybe.h"              // for Maybe
 #include "mozilla/mozalloc.h"           // for operator delete
 #include "ReadbackProcessor.h"          // For ReadbackProcessor::Update
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsPoint.h"                    // for nsIntPoint
 #include "nsRect.h"                     // for mozilla::gfx::IntRect
 #include "nsRegion.h"                   // for nsIntRegion
 #include "nsTArray.h"                   // for nsTArray
@@ -36,19 +35,16 @@ namespace mozilla {
 namespace gfx {
 class DrawTarget;
 } // namespace gfx
 
 namespace layers {
 
 class PaintedLayer;
 class CapturedPaintState;
-class CapturedBufferState;
-
-typedef bool (*PrepDrawTargetForPaintingCallback)(CapturedPaintState*);
 
 /**
  * A compositable client for PaintedLayers. These are different to Image/Canvas
  * clients due to sending a valid region across IPC and because we do a lot more
  * optimisation work, encapsulated in RotatedBuffers.
  *
  * We use content clients for OMTC and non-OMTC, basic rendering so that
  * BasicPaintedLayer has only one interface to deal with. We support single and
@@ -113,17 +109,16 @@ public:
       , mContentType(gfxContentType::SENTINEL)
     {}
 
     nsIntRegion mRegionToDraw;
     nsIntRegion mRegionToInvalidate;
     SurfaceMode mMode;
     DrawRegionClip mClip;
     gfxContentType mContentType;
-    RefPtr<CapturedBufferState> mBufferState;
   };
 
   enum {
     PAINT_WILL_RESAMPLE = 0x01,
     PAINT_NO_ROTATION = 0x02,
     PAINT_CAN_DRAW_ROTATED = 0x04,
     PAINT_ASYNC = 0x08,
   };
@@ -203,31 +198,34 @@ protected:
   /**
    * 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;
+  /**
+   * Return the buffer's content type.  Requires a valid buffer.
+   */
+  gfxContentType BufferContentType();
+  /**
+   * Returns whether the specified size is adequate for the current
+   * buffer and buffer size policy.
+   */
+  bool BufferSizeOkFor(const gfx::IntSize& aSize);
 
   /**
    * 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 void FinalizeFrame(const nsIntRegion& aRegionToDraw) {}
 
   /**
    * 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;
@@ -360,27 +358,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 void 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
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -687,16 +687,20 @@ TextureClient::BorrowDrawTarget()
   // the DrawTarget, just to get a snapshot, which is legit in term of OpenMode
   // but we should have a way to get a SourceSurface directly instead.
   //MOZ_ASSERT(mOpenMode & OpenMode::OPEN_WRITE);
 
   if (!IsValid() || !mIsLocked) {
     return nullptr;
   }
 
+  if (!NS_IsMainThread()) {
+    return nullptr;
+  }
+
   if (!mBorrowedDrawTarget) {
     mBorrowedDrawTarget = mData->BorrowDrawTarget();
 #ifdef DEBUG
     mExpectedDtRefs = mBorrowedDrawTarget ? mBorrowedDrawTarget->refCount() : 0;
 #endif
   }
 
   return mBorrowedDrawTarget;
--- a/gfx/layers/d3d11/TextureD3D11.cpp
+++ b/gfx/layers/d3d11/TextureD3D11.cpp
@@ -759,17 +759,17 @@ CreateTextureHostD3D11(const SurfaceDesc
   }
   return result.forget();
 }
 
 
 already_AddRefed<DrawTarget>
 D3D11TextureData::BorrowDrawTarget()
 {
-  MOZ_ASSERT(NS_IsMainThread() || PaintThread::IsOnPaintThread());
+  MOZ_ASSERT(NS_IsMainThread());
 
   if (!mDrawTarget && mTexture) {
     // This may return a null DrawTarget
     mDrawTarget = Factory::CreateDrawTargetForD3D11Texture(mTexture, mFormat);
     if (!mDrawTarget) {
       gfxCriticalNote << "Could not borrow DrawTarget (D3D11) " << (int)mFormat;
     }
   }
--- a/gfx/layers/ipc/CompositorBridgeChild.cpp
+++ b/gfx/layers/ipc/CompositorBridgeChild.cpp
@@ -1189,44 +1189,16 @@ CompositorBridgeChild::FlushAsyncPaints(
 
     double ratio = double(mSlowFlushCount) / double(mTotalFlushCount);
     Telemetry::ScalarSet(Telemetry::ScalarID::GFX_OMTP_PAINT_WAIT_RATIO,
                          uint32_t(ratio * 100 * 100));
   }
 }
 
 void
-CompositorBridgeChild::NotifyBeginAsyncPrepareBuffer(CapturedBufferState* aState)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  MonitorAutoLock lock(mPaintLock);
-
-  // We must not be waiting for paints (or buffer copying) 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.
-  aState->GetTextureClients(mTextureClientsForAsyncPaint);
-}
-
-void
-CompositorBridgeChild::NotifyFinishedAsyncPrepareBuffer(CapturedBufferState* aState)
-{
-  MOZ_ASSERT(PaintThread::IsOnPaintThread());
-
-  MonitorAutoLock lock(mPaintLock);
-  mOutstandingAsyncPaints--;
-}
-
-void
 CompositorBridgeChild::NotifyBeginAsyncPaint(CapturedPaintState* 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
--- a/gfx/layers/ipc/CompositorBridgeChild.h
+++ b/gfx/layers/ipc/CompositorBridgeChild.h
@@ -40,17 +40,16 @@ using mozilla::dom::TabChild;
 class IAPZCTreeManager;
 class APZCTreeManagerChild;
 class ClientLayerManager;
 class CompositorBridgeParent;
 class CompositorManagerChild;
 class CompositorOptions;
 class TextureClient;
 class TextureClientPool;
-class CapturedBufferState;
 class CapturedPaintState;
 struct FrameMetrics;
 
 class CompositorBridgeChild final : public PCompositorBridgeChild,
                                     public TextureForwarder
 {
   typedef InfallibleTArray<AsyncParentMessageData> AsyncParentMessageArray;
 
@@ -224,24 +223,16 @@ public:
   wr::PipelineId GetNextPipelineId();
 
   // Must only be called from the main thread. Ensures that any paints from
   // previous frames have been flushed. The main thread blocks until the
   // operation completes.
   void FlushAsyncPaints();
 
   // Must only be called from the main thread. Notifies the CompositorBridge
-  // that the paint thread is going to begin preparing a buffer asynchronously.
-  void NotifyBeginAsyncPrepareBuffer(CapturedBufferState* aState);
-
-  // Must only be called from the paint thread. Notifies the CompositorBridge
-  // that the paint thread has finished an asynchronous buffer prepare.
-  void NotifyFinishedAsyncPrepareBuffer(CapturedBufferState* aState);
-
-  // Must only be called from the main thread. Notifies the CompositorBridge
   // 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
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -218,17 +218,16 @@ EXPORTS.mozilla.layers += [
     'opengl/CompositorOGL.h',
     'opengl/MacIOSurfaceTextureClientOGL.h',
     'opengl/MacIOSurfaceTextureHostOGL.h',
     'opengl/TextureClientOGL.h',
     'opengl/TextureHostOGL.h',
     'PaintThread.h',
     'PersistentBufferProvider.h',
     'RenderTrace.h',
-    'RotatedBuffer.h',
     'ShareableCanvasRenderer.h',
     'SourceSurfaceSharedData.h',
     'SourceSurfaceVolatileData.h',
     'SyncObject.h',
     'TextureSourceProvider.h',
     'TextureWrapperImage.h',
     'TransactionIdAllocator.h',
     'UpdateImageHelper.h',