Kill RotatedContentBuffer (bug 1409871 part 12, r=nical) draft
authorRyan Hunt <rhunt@eqrion.net>
Mon, 16 Oct 2017 19:45:11 -0400
changeset 684134 a83ce18da11a92c7d5892099a94aec059b392702
parent 684133 6e66802c290c371d3752f4a241bca4f145c9cd98
child 684135 b9fcab192e7ac673f059cdfc8ae36a422f4ac8b5
push id85567
push userbmo:rhunt@eqrion.net
push dateFri, 20 Oct 2017 22:13:22 +0000
reviewersnical
bugs1409871
milestone58.0a1
Kill RotatedContentBuffer (bug 1409871 part 12, r=nical) This commit is the guts of the refactoring. This commit removes rotated content buffer, and moves the necessary functionality to content client. All content clients now do not inherit from rotated content buffer, but contain a refptr to a rotated buffer. For the basic content client, this is a DrawTargetRotatedBuffer. For the remote content client, this is a RemoteRotatedBuffer. The double buffered content client contains an additional RemoteRotatedBuffer for the front buffer. Each derived class of content client only needs to implement a method to create its own rotated buffer. The rest is handled by the base content client. The remote content clients still override some additional methods to sync buffers and do IPC. MozReview-Commit-ID: B9vcSi8UYhm
gfx/layers/CompositorTypes.h
gfx/layers/RotatedBuffer.cpp
gfx/layers/RotatedBuffer.h
gfx/layers/basic/BasicPaintedLayer.cpp
gfx/layers/basic/BasicPaintedLayer.h
gfx/layers/client/ClientPaintedLayer.cpp
gfx/layers/client/ClientPaintedLayer.h
gfx/layers/client/ContentClient.cpp
gfx/layers/client/ContentClient.h
gfx/layers/composite/ContentHost.h
--- a/gfx/layers/CompositorTypes.h
+++ b/gfx/layers/CompositorTypes.h
@@ -243,24 +243,25 @@ struct TextureInfo
  * How a SurfaceDescriptor will be opened.
  *
  * See ShadowLayerForwarder::OpenDescriptor for example.
  */
 enum class OpenMode : uint8_t {
   OPEN_NONE        = 0,
   OPEN_READ        = 0x1,
   OPEN_WRITE       = 0x2,
-  OPEN_READ_WRITE  = OPEN_READ|OPEN_WRITE,
-  OPEN_READ_ONLY   = OPEN_READ,
-  OPEN_WRITE_ONLY  = OPEN_WRITE,
-
   // This is only used in conjunction with OMTP to indicate that the DrawTarget
   // that is being borrowed will be painted asynchronously, and so will outlive
   // the write lock.
-  OPEN_ASYNC_WRITE = 0x04
+  OPEN_ASYNC_WRITE = 0x04,
+
+  OPEN_READ_WRITE  = OPEN_READ|OPEN_WRITE,
+  OPEN_READ_ASYNC_WRITE  = OPEN_READ|OPEN_WRITE|OPEN_ASYNC_WRITE,
+  OPEN_READ_ONLY   = OPEN_READ,
+  OPEN_WRITE_ONLY  = OPEN_WRITE,
 };
 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(OpenMode)
 
 // The kinds of mask texture a shader can support
 // We rely on the items in this enum being sequential
 enum class MaskType : uint8_t {
   MaskNone = 0,   // no mask layer
   Mask,           // mask layer
--- a/gfx/layers/RotatedBuffer.cpp
+++ b/gfx/layers/RotatedBuffer.cpp
@@ -284,29 +284,16 @@ WrapRotationAxis(int32_t* aRotationPoint
 {
   if (*aRotationPoint < 0) {
     *aRotationPoint += aSize;
   } else if (*aRotationPoint >= aSize) {
     *aRotationPoint -= aSize;
   }
 }
 
-static IntRect
-ComputeBufferRect(const IntRect& aRequestedRect)
-{
-  IntRect rect(aRequestedRect);
-  // Set a minimum width to guarantee a minimum size of buffers we
-  // allocate (and work around problems on some platforms with smaller
-  // dimensions). 64 used to be the magic number needed to work around
-  // a rendering glitch on b2g (see bug 788411). Now that we don't support
-  // this device anymore we should be fine with 8 pixels as the minimum.
-  rect.SetWidth(std::max(aRequestedRect.Width(), 8));
-  return rect;
-}
-
 bool
 RotatedBuffer::AdjustTo(const gfx::IntRect& aDestBufferRect,
                         const gfx::IntRect& aDrawBounds,
                         bool aCanHaveRotation,
                         bool aCanDrawRotated)
 {
   IntRect keepArea;
   if (keepArea.IntersectRect(aDestBufferRect, mBufferRect)) {
@@ -464,16 +451,28 @@ RotatedBuffer::BorrowDrawTargetForQuadra
     MOZ_ASSERT(aOutMatrix);
     *aOutMatrix = Matrix::Translation(-quadrantRect.x, -quadrantRect.y);
     mSetTransform = false;
   }
 
   return mLoanedDrawTarget;
 }
 
+gfx::SurfaceFormat
+RemoteRotatedBuffer::GetFormat() const
+{
+  return mClient->GetFormat();
+}
+
+bool
+RemoteRotatedBuffer::IsLocked()
+{
+  return mClient->IsLocked();
+}
+
 bool
 RemoteRotatedBuffer::Lock(OpenMode aMode)
 {
   MOZ_ASSERT(!mTarget);
   MOZ_ASSERT(!mTargetOnWhite);
 
   bool locked = mClient->Lock(aMode) &&
                 (!mClientOnWhite || mClientOnWhite->Lock(aMode));
@@ -514,16 +513,25 @@ RemoteRotatedBuffer::Unlock()
     mClient->Unlock();
   }
   if (mClientOnWhite && mClientOnWhite->IsLocked()) {
     mClientOnWhite->Unlock();
   }
 }
 
 void
+RemoteRotatedBuffer::SyncWithObject(SyncObjectClient* aSyncObject)
+{
+  mClient->SyncWithObject(aSyncObject);
+  if (mClientOnWhite) {
+    mClientOnWhite->SyncWithObject(aSyncObject);
+  }
+}
+
+void
 RemoteRotatedBuffer::Clear()
 {
   MOZ_ASSERT(!mTarget && !mTargetOnWhite);
   mClient = nullptr;
   mClientOnWhite = nullptr;
 }
 
 already_AddRefed<gfx::SourceSurface>
@@ -544,16 +552,22 @@ RemoteRotatedBuffer::GetDTBuffer() const
 }
 
 gfx::DrawTarget*
 RemoteRotatedBuffer::GetDTBufferOnWhite() const
 {
   return mTargetOnWhite;
 }
 
+gfx::SurfaceFormat
+DrawTargetRotatedBuffer::GetFormat() const
+{
+  return mTarget->GetFormat();
+}
+
 already_AddRefed<gfx::SourceSurface>
 DrawTargetRotatedBuffer::GetSourceSurface(ContextSource aSource) const
 {
   if (aSource == ContextSource::BUFFER_BLACK) {
     return mTarget->Snapshot();
   } else {
     MOZ_ASSERT(aSource == ContextSource::BUFFER_WHITE);
     return mTargetOnWhite->Snapshot();
@@ -567,477 +581,32 @@ DrawTargetRotatedBuffer::GetDTBuffer() c
 }
 
 gfx::DrawTarget*
 DrawTargetRotatedBuffer::GetDTBufferOnWhite() const
 {
   return mTargetOnWhite;
 }
 
+gfx::SurfaceFormat
+SourceRotatedBuffer::GetFormat() const
+{
+  return mSource->GetFormat();
+}
+
 already_AddRefed<SourceSurface>
 SourceRotatedBuffer::GetSourceSurface(ContextSource aSource) const
 {
   RefPtr<SourceSurface> surf;
   if (aSource == BUFFER_BLACK) {
     surf = mSource;
   } else {
     MOZ_ASSERT(aSource == BUFFER_WHITE);
     surf = mSourceOnWhite;
   }
 
   MOZ_ASSERT(surf);
   return surf.forget();
 }
 
-RotatedContentBuffer::BufferDecision
-RotatedContentBuffer::CalculateBufferForPaint(PaintedLayer* aLayer,
-                                              uint32_t aFlags)
-{
-  ContentType layerContentType =
-    aLayer->CanUseOpaqueSurface() ? gfxContentType::COLOR :
-                                    gfxContentType::COLOR_ALPHA;
-
-  SurfaceMode mode;
-  ContentType contentType;
-  IntRect destBufferRect;
-  nsIntRegion neededRegion;
-  nsIntRegion validRegion = aLayer->GetValidRegion();
-
-  bool canReuseBuffer = HaveBuffer();
-  bool canKeepBufferContents = true;
-
-  while (true) {
-    mode = aLayer->GetSurfaceMode();
-    neededRegion = aLayer->GetVisibleRegion().ToUnknownRegion();
-    canReuseBuffer &= BufferSizeOkFor(neededRegion.GetBounds().Size());
-    contentType = layerContentType;
-
-    if (canReuseBuffer) {
-      if (mBufferRect.Contains(neededRegion.GetBounds())) {
-        // We don't need to adjust mBufferRect.
-        destBufferRect = mBufferRect;
-      } else if (neededRegion.GetBounds().Size() <= mBufferRect.Size()) {
-        // The buffer's big enough but doesn't contain everything that's
-        // going to be visible. We'll move it.
-        destBufferRect = IntRect(neededRegion.GetBounds().TopLeft(), mBufferRect.Size());
-      } else {
-        destBufferRect = neededRegion.GetBounds();
-      }
-    } else {
-      // We won't be reusing the buffer.  Compute a new rect.
-      destBufferRect = ComputeBufferRect(neededRegion.GetBounds());
-    }
-
-    if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
-#if defined(MOZ_GFX_OPTIMIZE_MOBILE)
-      mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
-#else
-      if (!aLayer->GetParent() ||
-          !aLayer->GetParent()->SupportsComponentAlphaChildren() ||
-          !aLayer->AsShadowableLayer() ||
-          !aLayer->AsShadowableLayer()->HasShadow()) {
-        mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
-      } else {
-        contentType = gfxContentType::COLOR;
-      }
-#endif
-    }
-
-    if ((aFlags & PAINT_WILL_RESAMPLE) &&
-        (!neededRegion.GetBounds().IsEqualInterior(destBufferRect) ||
-         neededRegion.GetNumRects() > 1))
-    {
-      // The area we add to neededRegion might not be painted opaquely.
-      if (mode == SurfaceMode::SURFACE_OPAQUE) {
-        contentType = gfxContentType::COLOR_ALPHA;
-        mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
-      }
-
-      // 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.
-    if (canReuseBuffer &&
-        (contentType != BufferContentType() ||
-        (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) != HaveBufferOnWhite()))
-    {
-      // Restart the decision process; we won't re-enter since we guard on
-      // being able to re-use the buffer.
-      canReuseBuffer = false;
-      canKeepBufferContents = false;
-      validRegion.SetEmpty();
-      continue;
-    }
-
-    break;
-  }
-
-  NS_ASSERTION(destBufferRect.Contains(neededRegion.GetBounds()),
-               "Destination rect doesn't contain what we need to paint");
-
-  BufferDecision dest;
-  dest.mNeededRegion = Move(neededRegion);
-  dest.mValidRegion = Move(validRegion);
-  dest.mBufferRect = destBufferRect;
-  dest.mBufferMode = mode;
-  dest.mBufferContentType = contentType;
-  dest.mCanReuseBuffer = canReuseBuffer;
-  dest.mCanKeepBufferContents = canKeepBufferContents;
-  return dest;
-}
-
-gfxContentType
-RotatedContentBuffer::BufferContentType()
-{
-  if (mBufferProvider || (mDTBuffer && mDTBuffer->IsValid())) {
-    SurfaceFormat format = SurfaceFormat::B8G8R8A8;
-
-    if (mBufferProvider) {
-      format = mBufferProvider->GetFormat();
-    } else if (mDTBuffer && mDTBuffer->IsValid()) {
-      format = mDTBuffer->GetFormat();
-    }
-
-    return ContentForFormat(format);
-  }
-  return gfxContentType::SENTINEL;
-}
-
-bool
-RotatedContentBuffer::BufferSizeOkFor(const IntSize& aSize)
-{
-  return (aSize == mBufferRect.Size() ||
-          (SizedToVisibleBounds != mBufferSizePolicy &&
-           aSize < mBufferRect.Size()));
-}
-
-bool
-RotatedContentBuffer::EnsureBuffer()
-{
-  NS_ASSERTION(!mLoanedDrawTarget, "Loaned draw target must be returned");
-  if (!mDTBuffer || !mDTBuffer->IsValid()) {
-    if (mBufferProvider) {
-      mDTBuffer = mBufferProvider->BorrowDrawTarget();
-    }
-  }
-
-  NS_WARNING_ASSERTION(mDTBuffer && mDTBuffer->IsValid(), "no buffer");
-  return !!mDTBuffer;
-}
-
-bool
-RotatedContentBuffer::EnsureBufferOnWhite()
-{
-  NS_ASSERTION(!mLoanedDrawTarget, "Loaned draw target must be returned");
-  if (!mDTBufferOnWhite) {
-    if (mBufferProviderOnWhite) {
-      mDTBufferOnWhite =
-        mBufferProviderOnWhite->BorrowDrawTarget();
-    }
-  }
-
-  NS_WARNING_ASSERTION(mDTBufferOnWhite, "no buffer");
-  return !!mDTBufferOnWhite;
-}
-
-bool
-RotatedContentBuffer::HaveBuffer() const
-{
-  return mBufferProvider || (mDTBuffer && mDTBuffer->IsValid());
-}
-
-bool
-RotatedContentBuffer::HaveBufferOnWhite() const
-{
-  return mBufferProviderOnWhite || (mDTBufferOnWhite && mDTBufferOnWhite->IsValid());
-}
-
-void
-RotatedContentBuffer::FlushBuffers()
-{
-  if (mDTBuffer) {
-    mDTBuffer->Flush();
-  }
-  if (mDTBufferOnWhite) {
-    mDTBufferOnWhite->Flush();
-  }
-}
-
-RotatedContentBuffer::PaintState
-RotatedContentBuffer::BeginPaint(PaintedLayer* aLayer,
-                                 uint32_t aFlags)
-{
-  PaintState result;
-
-  BufferDecision dest = CalculateBufferForPaint(aLayer, aFlags);
-  result.mContentType = dest.mBufferContentType;
-
-  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 != BufferContentType()) {
-        printf_stderr("Invalidating entire rotated buffer (layer %p): content type changed\n", aLayer);
-      } else if ((dest.mBufferMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) != HaveBufferOnWhite()) {
-        printf_stderr("Invalidating entire rotated buffer (layer %p): component alpha changed\n", aLayer);
-      }
-    }
-#endif
-  }
-
-  result.mRegionToDraw.Sub(dest.mNeededRegion,
-                           dest.mValidRegion);
-
-  if (result.mRegionToDraw.IsEmpty())
-    return result;
-
-  if (HaveBuffer()) {
-    if (LockBuffers()) {
-      // Do not modify result.mRegionToDraw or result.mContentType after this call.
-      // Do not modify mBufferRect, mBufferRotation, or mDidSelfCopy,
-      // or call CreateBuffer before this call.
-      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;
-
-  IntRect drawBounds = result.mRegionToDraw.GetBounds();
-  RefPtr<DrawTarget> destDTBuffer;
-  RefPtr<DrawTarget> destDTBufferOnWhite;
-  uint32_t bufferFlags = 0;
-  if (dest.mBufferMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
-    bufferFlags |= BUFFER_COMPONENT_ALPHA;
-  }
-  if (dest.mCanReuseBuffer) {
-    if (!EnsureBuffer() ||
-        (HaveBufferOnWhite() && !EnsureBufferOnWhite())) {
-      return result;
-    }
-
-    if (!AdjustTo(dest.mBufferRect,
-                  drawBounds,
-                  canHaveRotation,
-                  canDrawRotated)) {
-      dest.mBufferRect = ComputeBufferRect(dest.mNeededRegion.GetBounds());
-      CreateBuffer(result.mContentType, dest.mBufferRect, bufferFlags,
-                   &destDTBuffer, &destDTBufferOnWhite);
-
-      if (!destDTBuffer ||
-          (!destDTBufferOnWhite && (bufferFlags & BUFFER_COMPONENT_ALPHA))) {
-        if (Factory::ReasonableSurfaceSize(IntSize(dest.mBufferRect.Width(), dest.mBufferRect.Height()))) {
-          gfxCriticalNote << "Failed 1 buffer db=" << hexa(destDTBuffer.get())
-                          << " dw=" << hexa(destDTBufferOnWhite.get())
-                          << " for " << dest.mBufferRect.x << ", "
-                          << dest.mBufferRect.y << ", "
-                          << dest.mBufferRect.Width() << ", "
-                          << dest.mBufferRect.Height();
-        }
-        return result;
-      }
-    }
-  } else {
-    // The buffer's not big enough, so allocate a new one
-    CreateBuffer(result.mContentType, dest.mBufferRect, bufferFlags,
-                 &destDTBuffer, &destDTBufferOnWhite);
-    if (!destDTBuffer ||
-        (!destDTBufferOnWhite && (bufferFlags & BUFFER_COMPONENT_ALPHA))) {
-      if (Factory::ReasonableSurfaceSize(IntSize(dest.mBufferRect.Width(), dest.mBufferRect.Height()))) {
-        gfxCriticalNote << "Failed 2 buffer db=" << hexa(destDTBuffer.get())
-                        << " dw=" << hexa(destDTBufferOnWhite.get())
-                        << " 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 (destDTBuffer) {
-    if ((HaveBuffer() && EnsureBuffer()) &&
-        (dest.mBufferMode != SurfaceMode::SURFACE_COMPONENT_ALPHA || (HaveBufferOnWhite() && EnsureBufferOnWhite()))) {
-      DrawTargetRotatedBuffer oldBuffer = DrawTargetRotatedBuffer(mDTBuffer, mDTBufferOnWhite,
-                                                                  mBufferRect, mBufferRotation);
-
-      mDTBuffer = destDTBuffer.forget();
-      mDTBufferOnWhite = destDTBufferOnWhite.forget();
-      mBufferRect = dest.mBufferRect;
-      mBufferRotation = IntPoint(0,0);
-
-      UpdateDestinationFrom(oldBuffer, nsIntRegion(mBufferRect));
-    } else {
-      mDTBuffer = destDTBuffer.forget();
-      mDTBufferOnWhite = destDTBufferOnWhite.forget();
-      mBufferRect = dest.mBufferRect;
-      mBufferRotation = IntPoint(0,0);
-    }
-  }
-  NS_ASSERTION(canHaveRotation || mBufferRotation == IntPoint(0,0),
-               "Rotation disabled, but we have nonzero rotation?");
-
-  nsIntRegion invalidate;
-  invalidate.Sub(aLayer->GetValidRegion(), dest.mBufferRect);
-  result.mRegionToInvalidate.Or(result.mRegionToInvalidate, invalidate);
-
-  result.mClip = DrawRegionClip::DRAW;
-  result.mMode = dest.mBufferMode;
-
-  return result;
-}
-
-RefPtr<CapturedPaintState>
-RotatedContentBuffer::BorrowDrawTargetForRecording(PaintState& aPaintState,
-                                                   DrawIterator* aIter,
-                                                   bool aSetTransform)
-{
-  if (aPaintState.mMode == SurfaceMode::SURFACE_NONE ||
-      !EnsureBuffer() ||
-      (HaveBufferOnWhite() && !EnsureBufferOnWhite())) {
-    return nullptr;
-  }
-
-  Matrix transform;
-  DrawTarget* result = BorrowDrawTargetForQuadrantUpdate(aPaintState.mRegionToDraw.GetBounds(),
-                                                         BUFFER_BOTH, aIter,
-                                                         aSetTransform,
-                                                         &transform);
-  if (!result) {
-    return nullptr;
-  }
-
-  nsIntRegion regionToDraw =
-    ExpandDrawRegion(aPaintState, aIter, result->GetBackendType());
-
-  RefPtr<CapturedPaintState> state =
-    new CapturedPaintState(regionToDraw,
-                           result,
-                           mDTBufferOnWhite,
-                           transform,
-                           aPaintState.mMode,
-                           aPaintState.mContentType);
-  return state;
-}
-
-/*static */ bool
-RotatedContentBuffer::PrepareDrawTargetForPainting(CapturedPaintState* aState)
-{
-  MOZ_ASSERT(aState);
-  RefPtr<DrawTarget> target = aState->mTarget;
-  RefPtr<DrawTarget> whiteTarget = aState->mTargetOnWhite;
-
-  if (aState->mSurfaceMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
-    if (!target || !target->IsValid() ||
-        !whiteTarget || !whiteTarget->IsValid()) {
-      // This can happen in release builds if allocating one of the two buffers
-      // failed. This in turn can happen if unreasonably large textures are
-      // requested.
-      return false;
-    }
-    for (auto iter = aState->mRegionToDraw.RectIter(); !iter.Done(); iter.Next()) {
-      const IntRect& rect = iter.Get();
-      target->FillRect(Rect(rect.x, rect.y, rect.Width(), rect.Height()),
-                            ColorPattern(Color(0.0, 0.0, 0.0, 1.0)));
-      whiteTarget->FillRect(Rect(rect.x, rect.y, rect.Width(), rect.Height()),
-                                 ColorPattern(Color(1.0, 1.0, 1.0, 1.0)));
-    }
-  } else if (aState->mContentType == gfxContentType::COLOR_ALPHA &&
-             target->IsValid()) {
-    // HaveBuffer() => we have an existing buffer that we must clear
-    for (auto iter = aState->mRegionToDraw.RectIter(); !iter.Done(); iter.Next()) {
-      const IntRect& rect = iter.Get();
-      target->ClearRect(Rect(rect.x, rect.y, rect.Width(), rect.Height()));
-    }
-  }
-
-  return true;
-}
-
-nsIntRegion
-RotatedContentBuffer::ExpandDrawRegion(PaintState& aPaintState,
-                                       DrawIterator* aIter,
-                                       BackendType aBackendType)
-{
-  nsIntRegion* drawPtr = &aPaintState.mRegionToDraw;
-  if (aIter) {
-    // The iterators draw region currently only contains the bounds of the region,
-    // this makes it the precise region.
-    aIter->mDrawRegion.And(aIter->mDrawRegion, aPaintState.mRegionToDraw);
-    drawPtr = &aIter->mDrawRegion;
-  }
-  if (aBackendType == BackendType::DIRECT2D ||
-      aBackendType == BackendType::DIRECT2D1_1) {
-    // Simplify the draw region to avoid hitting expensive drawing paths
-    // for complex regions.
-    drawPtr->SimplifyOutwardByArea(100 * 100);
-  }
-  return *drawPtr;
-}
-
-DrawTarget*
-RotatedContentBuffer::BorrowDrawTargetForPainting(PaintState& aPaintState,
-                                                  DrawIterator* aIter /* = nullptr */)
-{
-  RefPtr<CapturedPaintState> capturedState =
-    BorrowDrawTargetForRecording(aPaintState, aIter, true);
-
-  if (!capturedState) {
-    return nullptr;
-  }
-
-  if (!RotatedContentBuffer::PrepareDrawTargetForPainting(capturedState)) {
-    return nullptr;
-  }
-
-  return capturedState->mTarget;
-}
-
-already_AddRefed<SourceSurface>
-RotatedContentBuffer::GetSourceSurface(ContextSource aSource) const
-{
-  if (!mDTBuffer || !mDTBuffer->IsValid()) {
-    gfxCriticalNote << "Invalid buffer in RotatedContentBuffer::GetSourceSurface " << gfx::hexa(mDTBuffer);
-    return nullptr;
-  }
-
-  if (aSource == BUFFER_BLACK) {
-    return mDTBuffer->Snapshot();
-  } else {
-    if (!mDTBufferOnWhite || !mDTBufferOnWhite->IsValid()) {
-    gfxCriticalNote << "Invalid buffer on white in RotatedContentBuffer::GetSourceSurface " << gfx::hexa(mDTBufferOnWhite);
-      return nullptr;
-    }
-    MOZ_ASSERT(aSource == BUFFER_WHITE);
-    return mDTBufferOnWhite->Snapshot();
-  }
-}
-
 } // namespace layers
 } // namespace mozilla
 
--- a/gfx/layers/RotatedBuffer.h
+++ b/gfx/layers/RotatedBuffer.h
@@ -28,19 +28,20 @@ class CapturedPaintState;
 typedef bool (*PrepDrawTargetForPaintingCallback)(CapturedPaintState*);
 
 class PaintedLayer;
 
 // Mixin class for classes which need logic for loaning out a draw target.
 // See comments on BorrowDrawTargetForQuadrantUpdate.
 class BorrowDrawTarget
 {
-protected:
+public:
   void ReturnDrawTarget(gfx::DrawTarget*& aReturned);
 
+protected:
   // The draw target loaned by BorrowDrawTargetForQuadrantUpdate. It should not
   // be used, we just keep a reference to ensure it is kept alive and so we can
   // correctly restore state when it is returned.
   RefPtr<gfx::DrawTarget> mLoanedDrawTarget;
   gfx::Matrix mLoanedTransform;
 
   // This flag denotes whether or not a transform was already applied
   // to mLoanedDrawTarget and thus needs to be reset to mLoanedTransform
@@ -63,16 +64,18 @@ protected:
  * at row H-N on the screen.
  * mBufferRotation.y would be N in this example.
  */
 class RotatedBuffer : public BorrowDrawTarget
 {
 public:
   typedef gfxContentType ContentType;
 
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RotatedBuffer)
+
   RotatedBuffer(const gfx::IntRect& aBufferRect,
                 const gfx::IntPoint& aBufferRotation)
     : mBufferRect(aBufferRect)
     , mBufferRotation(aBufferRotation)
     , mDidSelfCopy(false)
   { }
   RotatedBuffer()
     : mDidSelfCopy(false)
@@ -134,26 +137,64 @@ public:
   /**
    * |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; }
 
+  void SetBufferRect(const gfx::IntRect& aBufferRect) {
+    mBufferRect = aBufferRect;
+  }
+  void SetBufferRotation(const gfx::IntPoint& aBufferRotation) {
+    mBufferRotation = aBufferRotation;
+  }
+
+  bool DidSelfCopy() const { return mDidSelfCopy; }
+  void ClearDidSelfCopy() { mDidSelfCopy = false; }
+
+  virtual gfx::SurfaceFormat GetFormat() const = 0;
+
   virtual bool HaveBuffer() const = 0;
   virtual bool HaveBufferOnWhite() const = 0;
 
+  virtual bool IsLocked() = 0;
+  virtual bool Lock(OpenMode aMode) = 0;
+  virtual void Unlock() = 0;
+
   virtual already_AddRefed<gfx::SourceSurface> GetSourceSurface(ContextSource aSource) const = 0;
 
-protected:
+  /**
+   * Get a draw target at the specified resolution for updating |aBounds|,
+   * which must be contained within a single quadrant.
+   *
+   * The result should only be held temporarily by the caller (it will be kept
+   * alive by this). Once used it should be returned using ReturnDrawTarget.
+   * BorrowDrawTargetForQuadrantUpdate may not be called more than once without
+   * first calling ReturnDrawTarget.
+   *
+   * ReturnDrawTarget will by default restore the transform on the draw target.
+   * But it is the callers responsibility to restore the clip.
+   * The caller should flush the draw target, if necessary.
+   * If aSetTransform is false, the required transform will be set in aOutTransform.
+   */
+  gfx::DrawTarget*
+  BorrowDrawTargetForQuadrantUpdate(const gfx::IntRect& aBounds,
+                                    ContextSource aSource,
+                                    DrawIterator* aIter,
+                                    bool aSetTransform = true,
+                                    gfx::Matrix* aOutTransform = nullptr);
 
   virtual gfx::DrawTarget* GetDTBuffer() const = 0;
   virtual gfx::DrawTarget* GetDTBufferOnWhite() const = 0;
 
+protected:
+  virtual ~RotatedBuffer() {}
+
   enum XSide {
     LEFT, RIGHT
   };
   enum YSide {
     TOP, BOTTOM
   };
   gfx::IntRect GetQuadrantRectangle(XSide aXSide, YSide aYSide) const;
 
@@ -166,37 +207,16 @@ protected:
    */
   void DrawBufferQuadrant(gfx::DrawTarget* aTarget, XSide aXSide, YSide aYSide,
                           ContextSource aSource,
                           float aOpacity,
                           gfx::CompositionOp aOperator,
                           gfx::SourceSurface* aMask,
                           const gfx::Matrix* aMaskTransform) const;
 
-  /**
-   * Get a draw target at the specified resolution for updating |aBounds|,
-   * which must be contained within a single quadrant.
-   *
-   * The result should only be held temporarily by the caller (it will be kept
-   * alive by this). Once used it should be returned using ReturnDrawTarget.
-   * BorrowDrawTargetForQuadrantUpdate may not be called more than once without
-   * first calling ReturnDrawTarget.
-   *
-   * ReturnDrawTarget will by default restore the transform on the draw target.
-   * But it is the callers responsibility to restore the clip.
-   * The caller should flush the draw target, if necessary.
-   * If aSetTransform is false, the required transform will be set in aOutTransform.
-   */
-  gfx::DrawTarget*
-  BorrowDrawTargetForQuadrantUpdate(const gfx::IntRect& aBounds,
-                                    ContextSource aSource,
-                                    DrawIterator* aIter,
-                                    bool aSetTransform = true,
-                                    gfx::Matrix* aOutTransform = nullptr);
-
   /** The area of the PaintedLayer that is covered by the buffer as a whole */
   gfx::IntRect             mBufferRect;
   /**
    * The x and y rotation of the buffer. Conceptually the buffer
    * has its origin translated to mBufferRect.TopLeft() - mBufferRotation,
    * is tiled to fill the plane, and the result is clipped to mBufferRect.
    * So the pixel at mBufferRotation within the buffer is what gets painted at
    * mBufferRect.TopLeft().
@@ -216,24 +236,28 @@ public:
   RemoteRotatedBuffer(TextureClient* aClient, TextureClient* aClientOnWhite,
                       const gfx::IntRect& aBufferRect,
                       const gfx::IntPoint& aBufferRotation)
     : RotatedBuffer(aBufferRect, aBufferRotation)
     , mClient(aClient)
     , mClientOnWhite(aClientOnWhite)
   { }
 
-  bool Lock(OpenMode aMode);
-  void Unlock();
+  virtual bool IsLocked() override;
+  virtual bool Lock(OpenMode aMode) override;
+  virtual void Unlock() override;
 
+  void SyncWithObject(SyncObjectClient* aSyncObject);
   void Clear();
 
   TextureClient* GetClient() const { return mClient; }
   TextureClient* GetClientOnWhite() const { return mClientOnWhite; }
 
+  virtual gfx::SurfaceFormat GetFormat() const override;
+
   virtual bool HaveBuffer() const override { return !!mClient; }
   virtual bool HaveBufferOnWhite() const override { return !!mClientOnWhite; }
 
   virtual already_AddRefed<gfx::SourceSurface> GetSourceSurface(ContextSource aSource) const override;
 
 protected:
   virtual gfx::DrawTarget* GetDTBuffer() const override;
   virtual gfx::DrawTarget* GetDTBufferOnWhite() const override;
@@ -252,16 +276,22 @@ public:
   DrawTargetRotatedBuffer(gfx::DrawTarget* aTarget, gfx::DrawTarget* aTargetOnWhite,
                           const gfx::IntRect& aBufferRect,
                           const gfx::IntPoint& aBufferRotation)
     : RotatedBuffer(aBufferRect, aBufferRotation)
     , mTarget(aTarget)
     , mTargetOnWhite(aTargetOnWhite)
   { }
 
+  virtual bool IsLocked() override { return false; }
+  virtual bool Lock(OpenMode aMode) override { return true; }
+  virtual void Unlock() override {}
+
+  virtual gfx::SurfaceFormat GetFormat() const override;
+
   virtual bool HaveBuffer() const override { return !!mTarget; }
   virtual bool HaveBufferOnWhite() const override { return !!mTargetOnWhite; }
 
   virtual already_AddRefed<gfx::SourceSurface> GetSourceSurface(ContextSource aSource) const override;
 
 protected:
   virtual gfx::DrawTarget* GetDTBuffer() const override;
   virtual gfx::DrawTarget* GetDTBufferOnWhite() const override;
@@ -277,270 +307,32 @@ public:
   SourceRotatedBuffer(gfx::SourceSurface* aSource, gfx::SourceSurface* aSourceOnWhite,
                       const gfx::IntRect& aBufferRect,
                       const gfx::IntPoint& aBufferRotation)
     : RotatedBuffer(aBufferRect, aBufferRotation)
     , mSource(aSource)
     , mSourceOnWhite(aSourceOnWhite)
   { }
 
+  virtual bool IsLocked() override { return false; }
+  virtual bool Lock(OpenMode aMode) override { return false; }
+  virtual void Unlock() override {}
+
   virtual already_AddRefed<gfx::SourceSurface> GetSourceSurface(ContextSource aSource) const;
 
+  virtual gfx::SurfaceFormat GetFormat() const override;
+
   virtual bool HaveBuffer() const { return !!mSource; }
   virtual bool HaveBufferOnWhite() const { return !!mSourceOnWhite; }
 
 protected:
   virtual gfx::DrawTarget* GetDTBuffer() const { return nullptr; }
   virtual gfx::DrawTarget* GetDTBufferOnWhite() const { return nullptr; }
 
 private:
   RefPtr<gfx::SourceSurface> mSource;
   RefPtr<gfx::SourceSurface> mSourceOnWhite;
 };
 
-/**
- * This class encapsulates the buffer used to retain PaintedLayer contents,
- * i.e., the contents of the layer's GetVisibleRegion().
- */
-class RotatedContentBuffer : public RotatedBuffer
-{
-public:
-  typedef gfxContentType ContentType;
-
-  /**
-   * Controls the size of the backing buffer of this.
-   * - SizedToVisibleBounds: the backing buffer is exactly the same
-   *   size as the bounds of PaintedLayer's visible region
-   * - ContainsVisibleBounds: the backing buffer is large enough to
-   *   fit visible bounds.  May be larger.
-   */
-  enum BufferSizePolicy {
-    SizedToVisibleBounds,
-    ContainsVisibleBounds
-  };
-
-  explicit RotatedContentBuffer(BufferSizePolicy aBufferSizePolicy)
-    : mBufferProvider(nullptr)
-    , mBufferProviderOnWhite(nullptr)
-    , mBufferSizePolicy(aBufferSizePolicy)
-  {
-    MOZ_COUNT_CTOR(RotatedContentBuffer);
-  }
-  virtual ~RotatedContentBuffer()
-  {
-    MOZ_COUNT_DTOR(RotatedContentBuffer);
-  }
-
-  /**
-   * Wipe out all retained contents. Call this when the entire
-   * buffer becomes invalid.
-   */
-  void Clear()
-  {
-    UnlockBuffers();
-    mDTBuffer = nullptr;
-    mDTBufferOnWhite = nullptr;
-    mBufferProvider = nullptr;
-    mBufferProviderOnWhite = nullptr;
-    mBufferRect.SetEmpty();
-  }
-
-  /**
-   * This is returned by BeginPaint. The caller should draw into mTarget.
-   * mRegionToDraw must be drawn. mRegionToInvalidate has been invalidated
-   * by RotatedContentBuffer and must be redrawn on the screen.
-   * mRegionToInvalidate is set when the buffer has changed from
-   * opaque to transparent or vice versa, since the details of rendering can
-   * depend on the buffer type.
-   */
-  struct PaintState {
-    PaintState()
-      : mRegionToDraw()
-      , mRegionToInvalidate()
-      , mMode(SurfaceMode::SURFACE_NONE)
-      , mClip(DrawRegionClip::NONE)
-      , mContentType(gfxContentType::SENTINEL)
-    {}
-
-    nsIntRegion mRegionToDraw;
-    nsIntRegion mRegionToInvalidate;
-    SurfaceMode mMode;
-    DrawRegionClip mClip;
-    ContentType mContentType;
-  };
-
-  enum {
-    PAINT_WILL_RESAMPLE = 0x01,
-    PAINT_NO_ROTATION = 0x02,
-    PAINT_CAN_DRAW_ROTATED = 0x04
-  };
-  /**
-   * Start a drawing operation. This returns a PaintState describing what
-   * needs to be drawn to bring the buffer up to date in the visible region.
-   * This queries aLayer to get the currently valid and visible regions.
-   * The returned mTarget may be null if mRegionToDraw is empty.
-   * Otherwise it must not be null.
-   * mRegionToInvalidate will contain mRegionToDraw.
-   * @param aFlags when PAINT_WILL_RESAMPLE is passed, this indicates that
-   * buffer will be resampled when rendering (i.e the effective transform
-   * combined with the scale for the resolution is not just an integer
-   * translation). This will disable buffer rotation (since we don't want
-   * to resample across the rotation boundary) and will ensure that we
-   * make the entire buffer contents valid (since we don't want to sample
-   * invalid pixels outside the visible region, if the visible region doesn't
-   * fill the buffer bounds).
-   * PAINT_CAN_DRAW_ROTATED can be passed if the caller supports drawing
-   * rotated content that crosses the physical buffer boundary. The caller
-   * will need to call BorrowDrawTargetForPainting multiple times to achieve
-   * this.
-   */
-  PaintState BeginPaint(PaintedLayer* aLayer,
-                        uint32_t aFlags);
-
-  /**
-   * Fetch a DrawTarget for rendering. The DrawTarget remains owned by
-   * this. See notes on BorrowDrawTargetForQuadrantUpdate.
-   * May return null. If the return value is non-null, it must be
-   * 'un-borrowed' using ReturnDrawTarget.
-   *
-   * If PAINT_CAN_DRAW_ROTATED was specified for BeginPaint, then the caller
-   * must call this function repeatedly (with an iterator) until it returns
-   * nullptr. The caller should draw the mDrawRegion of the iterator instead
-   * of mRegionToDraw in the PaintState.
-   *
-   * @param aPaintState Paint state data returned by a call to BeginPaint
-   * @param aIter Paint state iterator. Only required if PAINT_CAN_DRAW_ROTATED
-   * was specified to BeginPaint.
-   */
-  gfx::DrawTarget* BorrowDrawTargetForPainting(PaintState& aPaintState,
-                                               DrawIterator* aIter = nullptr);
-
-  /**
-   * Borrow a draw target for recording. The required transform for correct painting
-   * is not applied to the returned DrawTarget by default, BUT it is
-   * required to be whenever drawing does happen.
-   */
-  RefPtr<CapturedPaintState> BorrowDrawTargetForRecording(PaintState& aPaintState,
-                                                          DrawIterator* aIter,
-                                                          bool aSetTransform = false);
-
-  nsIntRegion ExpandDrawRegion(PaintState& aPaintState,
-                               DrawIterator* aIter,
-                               gfx::BackendType aBackendType);
-
-  static bool PrepareDrawTargetForPainting(CapturedPaintState*);
-  enum {
-    BUFFER_COMPONENT_ALPHA = 0x02 // Dual buffers should be created for drawing with
-                                  // component alpha.
-  };
-  /**
-   * Return a new surface of |aSize| and |aType|.
-   *
-   * If the created buffer supports azure content, then the result(s) will
-   * be returned in aBlackDT/aWhiteDT, otherwise aBlackSurface/aWhiteSurface
-   * will be used.
-   */
-  virtual void
-  CreateBuffer(ContentType aType, const gfx::IntRect& aRect, uint32_t aFlags,
-               RefPtr<gfx::DrawTarget>* aBlackDT, RefPtr<gfx::DrawTarget>* aWhiteDT) = 0;
-
-  virtual already_AddRefed<gfx::SourceSurface> GetSourceSurface(ContextSource aSource) const;
-
-protected:
-  // new texture client versions
-  void SetBufferProvider(TextureClient* aClient)
-  {
-    // Only this buffer provider can give us a buffer.  If we
-    // already have one, something has gone wrong.
-    MOZ_ASSERT(!aClient || !mDTBuffer || !mDTBuffer->IsValid());
-
-    mBufferProvider = aClient;
-    if (!mBufferProvider) {
-      mDTBuffer = nullptr;
-    }
-  }
-
-  void SetBufferProviderOnWhite(TextureClient* aClient)
-  {
-    // Only this buffer provider can give us a buffer.  If we
-    // already have one, something has gone wrong.
-    MOZ_ASSERT(!aClient || !mDTBufferOnWhite || !mDTBufferOnWhite->IsValid());
-
-    mBufferProviderOnWhite = aClient;
-    if (!mBufferProviderOnWhite) {
-      mDTBufferOnWhite = nullptr;
-    }
-  }
-
-protected:
-  struct BufferDecision {
-    nsIntRegion mNeededRegion;
-    nsIntRegion mValidRegion;
-    gfx::IntRect mBufferRect;
-    SurfaceMode mBufferMode;
-    ContentType mBufferContentType;
-    bool mCanReuseBuffer;
-    bool mCanKeepBufferContents;
-  };
-
-  BufferDecision CalculateBufferForPaint(PaintedLayer* aLayer,
-                                         uint32_t aFlags);
-
-  /**
-   * Return the buffer's content type.  Requires a valid buffer or
-   * buffer provider.
-   */
-  gfxContentType BufferContentType();
-  bool BufferSizeOkFor(const gfx::IntSize& aSize);
-  /**
-   * If the buffer hasn't been mapped, map it.
-   */
-  bool EnsureBuffer();
-  bool EnsureBufferOnWhite();
-
-  // Flush our buffers if they are mapped.
-  void FlushBuffers();
-
-  /**
-   * Get the underlying buffer, if any. This is useful because we can pass
-   * in the buffer as the default "reference surface" if there is one.
-   * Don't use it for anything else!
-   */
-  virtual gfx::DrawTarget* GetDTBuffer() const { return mDTBuffer; }
-  virtual gfx::DrawTarget* GetDTBufferOnWhite() const { return mDTBufferOnWhite; }
-
-  /**
-   * True if we have a buffer where we can get it (but not necessarily
-   * mapped currently).
-   */
-  virtual bool HaveBuffer() const;
-  virtual bool HaveBufferOnWhite() const;
-
-  /**
-   * Any actions that should be performed at the last moment before we begin
-   * rendering the next frame. I.e., after we calculate what we will draw,
-   * but before we rotate the buffer and possibly create new buffers.
-   * aRegionToDraw is the region which is guaranteed to be overwritten when
-   * drawing the next frame.
-   */
-  virtual void FinalizeFrame(const nsIntRegion& aRegionToDraw) {}
-
-  virtual bool LockBuffers() { return true; }
-  virtual void UnlockBuffers() {}
-
-  RefPtr<gfx::DrawTarget> mDTBuffer;
-  RefPtr<gfx::DrawTarget> mDTBufferOnWhite;
-
-  /**
-   * These members are only set transiently.  They're used to map mDTBuffer
-   * when we're using surfaces that require explicit map/unmap. Only one
-   * may be used at a time.
-   */
-  TextureClient* mBufferProvider;
-  TextureClient* mBufferProviderOnWhite;
-
-  BufferSizePolicy      mBufferSizePolicy;
-};
-
 } // namespace layers
 } // namespace mozilla
 
 #endif /* ROTATEDBUFFER_H_ */
--- a/gfx/layers/basic/BasicPaintedLayer.cpp
+++ b/gfx/layers/basic/BasicPaintedLayer.cpp
@@ -145,26 +145,26 @@ BasicPaintedLayer::Validate(LayerManager
   nsTArray<ReadbackProcessor::Update> readbackUpdates;
   if (aReadback && UsedForReadback()) {
     aReadback->GetPaintedLayerUpdates(this, &readbackUpdates);
   }
 
   uint32_t flags = 0;
 #ifndef MOZ_WIDGET_ANDROID
   if (BasicManager()->CompositorMightResample()) {
-    flags |= RotatedContentBuffer::PAINT_WILL_RESAMPLE;
+    flags |= ContentClient::PAINT_WILL_RESAMPLE;
   }
-  if (!(flags & RotatedContentBuffer::PAINT_WILL_RESAMPLE)) {
+  if (!(flags & ContentClient::PAINT_WILL_RESAMPLE)) {
     if (MayResample()) {
-      flags |= RotatedContentBuffer::PAINT_WILL_RESAMPLE;
+      flags |= ContentClient::PAINT_WILL_RESAMPLE;
     }
   }
 #endif
   if (mDrawAtomically) {
-    flags |= RotatedContentBuffer::PAINT_NO_ROTATION;
+    flags |= ContentClient::PAINT_NO_ROTATION;
   }
   PaintState state =
     mContentClient->BeginPaintBuffer(this, flags);
   SubtractFromValidRegion(state.mRegionToInvalidate);
 
   DrawTarget* target = mContentClient->BorrowDrawTargetForPainting(state);
   if (target && target->IsValid()) {
     // The area that became invalid and is visible needs to be repainted
--- a/gfx/layers/basic/BasicPaintedLayer.h
+++ b/gfx/layers/basic/BasicPaintedLayer.h
@@ -22,18 +22,18 @@ class gfxContext;
 
 namespace mozilla {
 namespace layers {
 
 class ReadbackProcessor;
 
 class BasicPaintedLayer : public PaintedLayer, public BasicImplData {
 public:
-  typedef RotatedContentBuffer::PaintState PaintState;
-  typedef RotatedContentBuffer::ContentType ContentType;
+  typedef ContentClient::PaintState PaintState;
+  typedef ContentClient::ContentType ContentType;
 
   explicit BasicPaintedLayer(BasicLayerManager* aLayerManager, gfx::BackendType aBackend) :
     PaintedLayer(aLayerManager, static_cast<BasicImplData*>(this)),
     mContentClient(nullptr)
     , mBackend(aBackend)
   {
     MOZ_COUNT_CTOR(BasicPaintedLayer);
   }
--- a/gfx/layers/client/ClientPaintedLayer.cpp
+++ b/gfx/layers/client/ClientPaintedLayer.cpp
@@ -111,24 +111,24 @@ ClientPaintedLayer::UpdatePaintRegion(Pa
   aState.mRegionToInvalidate.And(aState.mRegionToInvalidate,
                                  GetLocalVisibleRegion().ToUnknownRegion());
   return true;
 }
 
 uint32_t
 ClientPaintedLayer::GetPaintFlags()
 {
-  uint32_t flags = RotatedContentBuffer::PAINT_CAN_DRAW_ROTATED;
+  uint32_t flags = ContentClient::PAINT_CAN_DRAW_ROTATED;
   #ifndef MOZ_IGNORE_PAINT_WILL_RESAMPLE
    if (ClientManager()->CompositorMightResample()) {
-     flags |= RotatedContentBuffer::PAINT_WILL_RESAMPLE;
+     flags |= ContentClient::PAINT_WILL_RESAMPLE;
    }
-   if (!(flags & RotatedContentBuffer::PAINT_WILL_RESAMPLE)) {
+   if (!(flags & ContentClient::PAINT_WILL_RESAMPLE)) {
      if (MayResample()) {
-       flags |= RotatedContentBuffer::PAINT_WILL_RESAMPLE;
+       flags |= ContentClient::PAINT_WILL_RESAMPLE;
      }
    }
   #endif
   return flags;
 }
 
 void
 ClientPaintedLayer::PaintThebes(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates)
@@ -143,17 +143,17 @@ ClientPaintedLayer::PaintThebes(nsTArray
   uint32_t flags = GetPaintFlags();
 
   PaintState state = mContentClient->BeginPaintBuffer(this, flags);
   if (!UpdatePaintRegion(state)) {
     return;
   }
 
   bool didUpdate = false;
-  RotatedContentBuffer::DrawIterator iter;
+  RotatedBuffer::DrawIterator iter;
   while (DrawTarget* target = mContentClient->BorrowDrawTargetForPainting(state, &iter)) {
     if (!target || !target->IsValid()) {
       if (target) {
         mContentClient->ReturnDrawTargetToBuffer(target);
       }
       continue;
     }
 
@@ -213,17 +213,17 @@ ClientPaintedLayer::PaintOffMainThread()
   uint32_t flags = GetPaintFlags();
 
   PaintState state = mContentClient->BeginPaintBuffer(this, flags);
   if (!UpdatePaintRegion(state)) {
     return false;
   }
 
   bool didUpdate = false;
-  RotatedContentBuffer::DrawIterator iter;
+  RotatedBuffer::DrawIterator iter;
 
   // Debug Protip: Change to BorrowDrawTargetForPainting if using sync OMTP.
   while (RefPtr<CapturedPaintState> captureState =
           mContentClient->BorrowDrawTargetForRecording(state, &iter))
   {
     DrawTarget* target = captureState->mTarget;
     if (!target || !target->IsValid()) {
       if (target) {
@@ -250,17 +250,17 @@ ClientPaintedLayer::PaintOffMainThread()
                                               state.mClip,
                                               state.mRegionToInvalidate,
                                               ClientManager()->GetPaintedLayerCallbackData());
 
     ctx = nullptr;
 
     captureState->mCapture = captureDT.forget();
     PaintThread::Get()->PaintContents(captureState,
-                                      RotatedContentBuffer::PrepareDrawTargetForPainting);
+                                      ContentClient::PrepareDrawTargetForPainting);
 
     mContentClient->ReturnDrawTargetToBuffer(target);
 
     didUpdate = true;
   }
 
   PaintThread::Get()->EndLayer();
   mContentClient->EndPaint(nullptr);
--- a/gfx/layers/client/ClientPaintedLayer.h
+++ b/gfx/layers/client/ClientPaintedLayer.h
@@ -27,18 +27,18 @@ class DrawTargetCapture;
 namespace layers {
 class CompositableClient;
 class ShadowableLayer;
 class SpecificLayerAttributes;
 
 class ClientPaintedLayer : public PaintedLayer,
                            public ClientLayer {
 public:
-  typedef RotatedContentBuffer::PaintState PaintState;
-  typedef RotatedContentBuffer::ContentType ContentType;
+  typedef ContentClient::PaintState PaintState;
+  typedef ContentClient::ContentType ContentType;
 
   explicit ClientPaintedLayer(ClientLayerManager* aLayerManager,
                              LayerManager::PaintedLayerCreationHint aCreationHint = LayerManager::NONE) :
     PaintedLayer(aLayerManager, static_cast<ClientLayer*>(this), aCreationHint),
     mContentClient(nullptr)
   {
     MOZ_COUNT_CTOR(ClientPaintedLayer);
   }
--- a/gfx/layers/client/ContentClient.cpp
+++ b/gfx/layers/client/ContentClient.cpp
@@ -8,17 +8,16 @@
 #include "gfxContext.h"                 // for gfxContext, etc
 #include "gfxPlatform.h"                // for gfxPlatform
 #include "gfxEnv.h"                     // for gfxEnv
 #include "gfxPrefs.h"                   // for gfxPrefs
 #include "gfxPoint.h"                   // for IntSize, gfxPoint
 #include "gfxUtils.h"                   // for gfxUtils
 #include "ipc/ShadowLayers.h"           // for ShadowLayerForwarder
 #include "mozilla/ArrayUtils.h"         // for ArrayLength
-#include "mozilla/Maybe.h"
 #include "mozilla/gfx/2D.h"             // for DrawTarget, Factory
 #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
@@ -30,37 +29,51 @@
 #ifdef XP_WIN
 #include "gfxWindowsPlatform.h"
 #endif
 #ifdef MOZ_WIDGET_GTK
 #include "gfxPlatformGtk.h"
 #endif
 #include "ReadbackLayer.h"
 
+#include <utility>
 #include <vector>
 
 using namespace std;
 
 namespace mozilla {
 
 using namespace gfx;
 
 namespace layers {
 
-static TextureFlags TextureFlagsForRotatedContentBufferFlags(uint32_t aBufferFlags)
+static TextureFlags TextureFlagsForContentClientFlags(uint32_t aBufferFlags)
 {
   TextureFlags result = TextureFlags::NO_FLAGS;
 
-  if (aBufferFlags & RotatedContentBuffer::BUFFER_COMPONENT_ALPHA) {
+  if (aBufferFlags & ContentClient::BUFFER_COMPONENT_ALPHA) {
     result |= TextureFlags::COMPONENT_ALPHA;
   }
 
   return result;
 }
 
+static IntRect
+ComputeBufferRect(const IntRect& aRequestedRect)
+{
+  IntRect rect(aRequestedRect);
+  // Set a minimum width to guarantee a minimum size of buffers we
+  // allocate (and work around problems on some platforms with smaller
+  // dimensions). 64 used to be the magic number needed to work around
+  // a rendering glitch on b2g (see bug 788411). Now that we don't support
+  // this device anymore we should be fine with 8 pixels as the minimum.
+  rect.SetWidth(std::max(aRequestedRect.Width(), 8));
+  return rect;
+}
+
 /* static */ already_AddRefed<ContentClient>
 ContentClient::CreateContentClient(CompositableForwarder* aForwarder)
 {
   LayersBackend backend = aForwarder->GetCompositorBackendType();
   if (backend != LayersBackend::LAYERS_OPENGL &&
       backend != LayersBackend::LAYERS_D3D11 &&
       backend != LayersBackend::LAYERS_WR &&
       backend != LayersBackend::LAYERS_BASIC) {
@@ -87,16 +100,388 @@ ContentClient::CreateContentClient(Compo
 
   if (useDoubleBuffering || gfxEnv::ForceDoubleBuffering()) {
     return MakeAndAddRef<ContentClientDoubleBuffered>(aForwarder);
   }
   return MakeAndAddRef<ContentClientSingleBuffered>(aForwarder);
 }
 
 void
+ContentClient::Clear()
+{
+  mBuffer = nullptr;
+}
+
+ContentClient::PaintState
+ContentClient::BeginPaintBuffer(PaintedLayer* aLayer,
+                                uint32_t aFlags)
+{
+  PaintState result;
+
+  BufferDecision dest = CalculateBufferForPaint(aLayer, aFlags);
+  result.mContentType = dest.mBufferContentType;
+
+  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 != 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
+  }
+
+  result.mRegionToDraw.Sub(dest.mNeededRegion,
+                           dest.mValidRegion);
+
+  if (result.mRegionToDraw.IsEmpty())
+    return result;
+
+  if (mBuffer) {
+    if (mBuffer->Lock(LockMode())) {
+      // Do not modify result.mRegionToDraw or result.mContentType after this call.
+      // Do not modify mBufferRect, mBufferRotation, or mDidSelfCopy,
+      // or call CreateBuffer before this call.
+      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;
+
+  IntRect drawBounds = result.mRegionToDraw.GetBounds();
+  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 (!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();
+        }
+        return result;
+      }
+    }
+  } 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 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 (mBuffer) {
+      newBuffer->UpdateDestinationFrom(*mBuffer, nsIntRegion(newBuffer->BufferRect()));
+    }
+
+    // Ensure our reference to the front buffer is released
+    // 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?");
+
+  nsIntRegion invalidate;
+  invalidate.Sub(aLayer->GetValidRegion(), dest.mBufferRect);
+  result.mRegionToInvalidate.Or(result.mRegionToInvalidate, invalidate);
+
+  result.mClip = DrawRegionClip::DRAW;
+  result.mMode = dest.mBufferMode;
+
+  return result;
+}
+
+DrawTarget*
+ContentClient::BorrowDrawTargetForPainting(ContentClient::PaintState& aPaintState,
+                                           RotatedBuffer::DrawIterator* aIter /* = nullptr */)
+{
+  RefPtr<CapturedPaintState> capturedState =
+    BorrowDrawTargetForRecording(aPaintState, aIter, true);
+
+  if (!capturedState) {
+    return nullptr;
+  }
+
+  if (!ContentClient::PrepareDrawTargetForPainting(capturedState)) {
+    return nullptr;
+  }
+
+  return capturedState->mTarget;
+}
+
+RefPtr<CapturedPaintState>
+ContentClient::BorrowDrawTargetForRecording(ContentClient::PaintState& aPaintState,
+                                            RotatedBuffer::DrawIterator* aIter,
+                                            bool aSetTransform)
+{
+  if (aPaintState.mMode == SurfaceMode::SURFACE_NONE ||
+      !mBuffer || !mBuffer->IsLocked()) {
+    return nullptr;
+  }
+
+  Matrix transform;
+  DrawTarget* result = mBuffer->BorrowDrawTargetForQuadrantUpdate(
+                                  aPaintState.mRegionToDraw.GetBounds(),
+                                  RotatedBuffer::BUFFER_BOTH, aIter,
+                                  aSetTransform,
+                                  &transform);
+  if (!result) {
+    return nullptr;
+  }
+
+  nsIntRegion regionToDraw =
+    ExpandDrawRegion(aPaintState, aIter, result->GetBackendType());
+
+  RefPtr<CapturedPaintState> state =
+    new CapturedPaintState(regionToDraw,
+                           result,
+                           mBuffer->GetDTBufferOnWhite(),
+                           transform,
+                           aPaintState.mMode,
+                           aPaintState.mContentType);
+  return state;
+}
+
+void
+ContentClient::ReturnDrawTargetToBuffer(gfx::DrawTarget*& aReturned)
+{
+  mBuffer->ReturnDrawTarget(aReturned);
+}
+
+nsIntRegion
+ContentClient::ExpandDrawRegion(ContentClient::PaintState& aPaintState,
+                                RotatedBuffer::DrawIterator* aIter,
+                                BackendType aBackendType)
+{
+  nsIntRegion* drawPtr = &aPaintState.mRegionToDraw;
+  if (aIter) {
+    // The iterators draw region currently only contains the bounds of the region,
+    // this makes it the precise region.
+    aIter->mDrawRegion.And(aIter->mDrawRegion, aPaintState.mRegionToDraw);
+    drawPtr = &aIter->mDrawRegion;
+  }
+  if (aBackendType == BackendType::DIRECT2D ||
+      aBackendType == BackendType::DIRECT2D1_1) {
+    // Simplify the draw region to avoid hitting expensive drawing paths
+    // for complex regions.
+    drawPtr->SimplifyOutwardByArea(100 * 100);
+  }
+  return *drawPtr;
+}
+
+/*static */ bool
+ContentClient::PrepareDrawTargetForPainting(CapturedPaintState* aState)
+{
+  MOZ_ASSERT(aState);
+  RefPtr<DrawTarget> target = aState->mTarget;
+  RefPtr<DrawTarget> whiteTarget = aState->mTargetOnWhite;
+
+  if (aState->mSurfaceMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
+    if (!target || !target->IsValid() ||
+        !whiteTarget || !whiteTarget->IsValid()) {
+      // This can happen in release builds if allocating one of the two buffers
+      // failed. This in turn can happen if unreasonably large textures are
+      // requested.
+      return false;
+    }
+    for (auto iter = aState->mRegionToDraw.RectIter(); !iter.Done(); iter.Next()) {
+      const IntRect& rect = iter.Get();
+      target->FillRect(Rect(rect.x, rect.y, rect.Width(), rect.Height()),
+                            ColorPattern(Color(0.0, 0.0, 0.0, 1.0)));
+      whiteTarget->FillRect(Rect(rect.x, rect.y, rect.Width(), rect.Height()),
+                                 ColorPattern(Color(1.0, 1.0, 1.0, 1.0)));
+    }
+  } else if (aState->mContentType == gfxContentType::COLOR_ALPHA &&
+             target->IsValid()) {
+    // HaveBuffer() => we have an existing buffer that we must clear
+    for (auto iter = aState->mRegionToDraw.RectIter(); !iter.Done(); iter.Next()) {
+      const IntRect& rect = iter.Get();
+      target->ClearRect(Rect(rect.x, rect.y, rect.Width(), rect.Height()));
+    }
+  }
+
+  return true;
+}
+
+ContentClient::BufferDecision
+ContentClient::CalculateBufferForPaint(PaintedLayer* aLayer,
+                                       uint32_t aFlags)
+{
+  gfxContentType layerContentType =
+    aLayer->CanUseOpaqueSurface() ? gfxContentType::COLOR :
+                                    gfxContentType::COLOR_ALPHA;
+
+  SurfaceMode mode;
+  gfxContentType contentType;
+  IntRect destBufferRect;
+  nsIntRegion neededRegion;
+  nsIntRegion validRegion = aLayer->GetValidRegion();
+
+  bool canReuseBuffer = !!mBuffer;
+  bool canKeepBufferContents = true;
+
+  while (true) {
+    mode = aLayer->GetSurfaceMode();
+    neededRegion = aLayer->GetVisibleRegion().ToUnknownRegion();
+    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
+        // going to be visible. We'll move it.
+        destBufferRect = IntRect(neededRegion.GetBounds().TopLeft(), mBuffer->BufferRect().Size());
+      } else {
+        destBufferRect = neededRegion.GetBounds();
+      }
+    } else {
+      // We won't be reusing the buffer.  Compute a new rect.
+      destBufferRect = ComputeBufferRect(neededRegion.GetBounds());
+    }
+
+    if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
+#if defined(MOZ_GFX_OPTIMIZE_MOBILE)
+      mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
+#else
+      if (!aLayer->GetParent() ||
+          !aLayer->GetParent()->SupportsComponentAlphaChildren() ||
+          !aLayer->AsShadowableLayer() ||
+          !aLayer->AsShadowableLayer()->HasShadow()) {
+        mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
+      } else {
+        contentType = gfxContentType::COLOR;
+      }
+#endif
+    }
+
+    if ((aFlags & PAINT_WILL_RESAMPLE) &&
+        (!neededRegion.GetBounds().IsEqualInterior(destBufferRect) ||
+         neededRegion.GetNumRects() > 1))
+    {
+      // The area we add to neededRegion might not be painted opaquely.
+      if (mode == SurfaceMode::SURFACE_OPAQUE) {
+        contentType = gfxContentType::COLOR_ALPHA;
+        mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
+      }
+
+      // 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.
+    if (canReuseBuffer &&
+        (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 re-use the buffer.
+      canReuseBuffer = false;
+      canKeepBufferContents = false;
+      validRegion.SetEmpty();
+      continue;
+    }
+
+    break;
+  }
+
+  NS_ASSERTION(destBufferRect.Contains(neededRegion.GetBounds()),
+               "Destination rect doesn't contain what we need to paint");
+
+  BufferDecision dest;
+  dest.mNeededRegion = Move(neededRegion);
+  dest.mValidRegion = Move(validRegion);
+  dest.mBufferRect = destBufferRect;
+  dest.mBufferMode = mode;
+  dest.mBufferContentType = contentType;
+  dest.mCanReuseBuffer = canReuseBuffer;
+  dest.mCanKeepBufferContents = canKeepBufferContents;
+  return dest;
+}
+
+gfxContentType
+ContentClient::BufferContentType()
+{
+  if (mBuffer) {
+    return ContentForFormat(mBuffer->GetFormat());
+  }
+  return gfxContentType::SENTINEL;
+}
+
+bool
+ContentClient::BufferSizeOkFor(const IntSize& aSize)
+{
+  MOZ_ASSERT(mBuffer);
+  return (aSize == mBuffer->BufferRect().Size() ||
+          (SizedToVisibleBounds != mBufferSizePolicy &&
+           aSize < mBuffer->BufferRect().Size()));
+}
+
+OpenMode
+ContentClient::LockMode() const
+{
+  return mInAsyncPaint ? OpenMode::OPEN_READ_ASYNC_WRITE
+                       : OpenMode::OPEN_READ_WRITE;
+}
+
+void
 ContentClient::BeginAsyncPaint()
 {
   mInAsyncPaint = true;
 }
 
 void
 ContentClient::EndPaint(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates)
 {
@@ -108,105 +493,90 @@ ContentClient::PrintInfo(std::stringstre
 {
   aStream << aPrefix;
   aStream << nsPrintfCString("ContentClient (0x%p)", this).get();
 }
 
 // We pass a null pointer for the ContentClient Forwarder argument, which means
 // this client will not have a ContentHost on the other side.
 ContentClientBasic::ContentClientBasic(gfx::BackendType aBackend)
-  : ContentClient(nullptr)
-  , RotatedContentBuffer(ContainsVisibleBounds)
+  : ContentClient(nullptr, ContainsVisibleBounds)
   , mBackend(aBackend)
 {}
 
 void
 ContentClientBasic::DrawTo(PaintedLayer* aLayer,
                            gfx::DrawTarget* aTarget,
                            float aOpacity,
                            gfx::CompositionOp aOp,
                            gfx::SourceSurface* aMask,
                            const gfx::Matrix* aMaskTransform)
 {
-  if (!EnsureBuffer()) {
+  if (!mBuffer) {
     return;
   }
 
-  RotatedContentBuffer::DrawTo(aLayer, aTarget, aOpacity, aOp,
-                               aMask, aMaskTransform);
+  mBuffer->DrawTo(aLayer, aTarget, aOpacity, aOp,
+                  aMask, aMaskTransform);
 }
 
-void
-ContentClientBasic::CreateBuffer(ContentType aType,
+RefPtr<RotatedBuffer>
+ContentClientBasic::CreateBuffer(gfxContentType aType,
                                  const IntRect& aRect,
-                                 uint32_t aFlags,
-                                 RefPtr<gfx::DrawTarget>* aBlackDT,
-                                 RefPtr<gfx::DrawTarget>* aWhiteDT)
+                                 uint32_t aFlags)
 {
   MOZ_ASSERT(!(aFlags & BUFFER_COMPONENT_ALPHA));
   if (aFlags & BUFFER_COMPONENT_ALPHA) {
     gfxDevCrash(LogReason::AlphaWithBasicClient) << "Asking basic content client for component alpha";
   }
 
   IntSize size(aRect.Width(), aRect.Height());
+  RefPtr<gfx::DrawTarget> drawTarget;
+
 #ifdef XP_WIN
   if (mBackend == BackendType::CAIRO && 
       (aType == gfxContentType::COLOR || aType == gfxContentType::COLOR_ALPHA)) {
     RefPtr<gfxASurface> surf =
       new gfxWindowsSurface(size, aType == gfxContentType::COLOR ? gfxImageFormat::X8R8G8B8_UINT32 :
                                                                    gfxImageFormat::A8R8G8B8_UINT32);
-    *aBlackDT = gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(surf, size);
-
-    if (*aBlackDT) {
-      return;
-    }
+    drawTarget = gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(surf, size);
   }
 #endif
 
-  *aBlackDT = gfxPlatform::GetPlatform()->CreateDrawTargetForBackend(
-    mBackend, size,
-    gfxPlatform::GetPlatform()->Optimal2DFormatForContent(aType));
+  if (!drawTarget) {
+    drawTarget = gfxPlatform::GetPlatform()->CreateDrawTargetForBackend(
+      mBackend, size,
+      gfxPlatform::GetPlatform()->Optimal2DFormatForContent(aType));
+  }
+
+  return new DrawTargetRotatedBuffer(drawTarget, nullptr, aRect, IntPoint(0,0));
 }
 
 RefPtr<CapturedPaintState>
-ContentClientBasic::BorrowDrawTargetForRecording(PaintState& aPaintState,
-                                                 RotatedContentBuffer::DrawIterator* aIter)
+ContentClientBasic::BorrowDrawTargetForRecording(ContentClient::PaintState& aPaintState,
+                                                 RotatedBuffer::DrawIterator* aIter,
+                                                 bool aSetTransform)
 {
   // BasicLayers does not yet support OMTP.
   return nullptr;
 }
 
-void
-ContentClientRemoteBuffer::DestroyBuffers()
+RefPtr<CapturedPaintState>
+ContentClientRemoteBuffer::BorrowDrawTargetForRecording(ContentClient::PaintState& aPaintState,
+                                                        RotatedBuffer::DrawIterator* aIter,
+                                                        bool aSetTransform)
 {
-  if (!mTextureClient) {
-    return;
-  }
-
-  mOldTextures.AppendElement(mTextureClient);
-  mTextureClient = nullptr;
-  if (mTextureClientOnWhite) {
-    mOldTextures.AppendElement(mTextureClientOnWhite);
-    mTextureClientOnWhite = nullptr;
-  }
-
-  DestroyFrontBuffer();
-}
-
-RefPtr<CapturedPaintState>
-ContentClientRemoteBuffer::BorrowDrawTargetForRecording(PaintState& aPaintState,
-                                                        RotatedContentBuffer::DrawIterator* aIter)
-{
-  RefPtr<CapturedPaintState> cps = RotatedContentBuffer::BorrowDrawTargetForRecording(aPaintState, aIter);
+  RefPtr<CapturedPaintState> cps = ContentClient::BorrowDrawTargetForRecording(aPaintState, aIter, aSetTransform);
   if (!cps) {
     return nullptr;
   }
 
-  cps->mTextureClient = mTextureClient;
-  cps->mTextureClientOnWhite = mTextureClientOnWhite;
+  RemoteRotatedBuffer* remoteBuffer = GetRemoteBuffer();
+  cps->mTextureClient = remoteBuffer->GetClient();
+  cps->mTextureClientOnWhite = remoteBuffer->GetClientOnWhite();
   return cps.forget();
 }
 
 class RemoteBufferReadbackProcessor : public TextureReadbackSink
 {
 public:
   RemoteBufferReadbackProcessor(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates,
                                 const IntRect& aBufferRect, const nsIntPoint& aBufferRotation)
@@ -260,352 +630,262 @@ private:
   IntRect mBufferRect;
   nsIntPoint mBufferRotation;
 };
 
 void
 ContentClientRemoteBuffer::BeginPaint()
 {
   EnsureBackBufferIfFrontBuffer();
-
-  // XXX: So we might not have a TextureClient yet.. because it will
-  // only be created by CreateBuffer.. which will deliver a locked surface!.
-  if (mTextureClient) {
-    SetBufferProvider(mTextureClient);
-  }
-  if (mTextureClientOnWhite) {
-    SetBufferProviderOnWhite(mTextureClientOnWhite);
-  }
 }
 
 void
 ContentClientRemoteBuffer::BeginAsyncPaint()
 {
   BeginPaint();
   mInAsyncPaint = true;
 }
 
 void
 ContentClientRemoteBuffer::EndPaint(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates)
 {
-  MOZ_ASSERT(!mTextureClientOnWhite || !aReadbackUpdates || aReadbackUpdates->Length() == 0);
+  MOZ_ASSERT(!mBuffer || !mBuffer->HaveBufferOnWhite() ||
+             !aReadbackUpdates || aReadbackUpdates->Length() == 0);
+
+  RemoteRotatedBuffer* remoteBuffer = GetRemoteBuffer();
 
-  // XXX: We might still not have a texture client if PaintThebes
-  // decided we didn't need one yet because the region to draw was empty.
-  SetBufferProvider(nullptr);
-  SetBufferProviderOnWhite(nullptr);
-  for (unsigned i = 0; i< mOldTextures.Length(); ++i) {
-    if (mOldTextures[i]->IsLocked()) {
-      mOldTextures[i]->Unlock();
-    }
-  }
-  mOldTextures.Clear();
+  if (remoteBuffer && remoteBuffer->IsLocked()) {
+    if (aReadbackUpdates && aReadbackUpdates->Length() > 0) {
+      RefPtr<TextureReadbackSink> readbackSink = new RemoteBufferReadbackProcessor(aReadbackUpdates,
+                                                                                   remoteBuffer->BufferRect(),
+                                                                                   remoteBuffer->BufferRotation());
 
-  if (mTextureClient && mTextureClient->IsLocked()) {
-    if (aReadbackUpdates && aReadbackUpdates->Length() > 0) {
-      RefPtr<TextureReadbackSink> readbackSink = new RemoteBufferReadbackProcessor(aReadbackUpdates, mBufferRect, mBufferRotation);
-
-      mTextureClient->SetReadbackSink(readbackSink);
+      remoteBuffer->GetClient()->SetReadbackSink(readbackSink);
     }
 
-    mTextureClient->Unlock();
-    mTextureClient->SyncWithObject(mForwarder->GetSyncObject());
-  }
-  if (mTextureClientOnWhite && mTextureClientOnWhite->IsLocked()) {
-    mTextureClientOnWhite->Unlock();
-    mTextureClientOnWhite->SyncWithObject(mForwarder->GetSyncObject());
+    remoteBuffer->Unlock();
+    remoteBuffer->SyncWithObject(mForwarder->GetSyncObject());
   }
 
   ContentClient::EndPaint(aReadbackUpdates);
 }
 
-void
-ContentClientRemoteBuffer::BuildTextureClients(SurfaceFormat aFormat,
-                                               const IntRect& aRect,
-                                               uint32_t aFlags)
+RefPtr<RotatedBuffer>
+ContentClientRemoteBuffer::CreateBuffer(gfxContentType aType,
+                                        const IntRect& aRect,
+                                        uint32_t aFlags)
 {
   // If we hit this assertion, then it might be due to an empty transaction
   // followed by a real transaction. Our buffers should be created (but not
   // painted in the empty transaction) and then painted (but not created) in the
   // real transaction. That is kind of fragile, and this assert will catch
   // circumstances where we screw that up, e.g., by unnecessarily recreating our
   // buffers.
   MOZ_ASSERT(!mIsNewBuffer,
              "Bad! Did we create a buffer twice without painting?");
 
-  mIsNewBuffer = true;
-
-  DestroyBuffers();
+  gfx::SurfaceFormat format = gfxPlatform::GetPlatform()->Optimal2DFormatForContent(aType);
 
-  mSurfaceFormat = aFormat;
-  mSize = IntSize(aRect.Width(), aRect.Height());
-  mTextureFlags = TextureFlagsForRotatedContentBufferFlags(aFlags);
+  TextureFlags textureFlags = TextureFlagsForContentClientFlags(aFlags);
+  if (aFlags & BUFFER_COMPONENT_ALPHA) {
+    textureFlags |= TextureFlags::COMPONENT_ALPHA;
+  }
 
-  if (aFlags & BUFFER_COMPONENT_ALPHA) {
-    mTextureFlags |= TextureFlags::COMPONENT_ALPHA;
+  RefPtr<RotatedBuffer> buffer = CreateBufferInternal(aRect, format, textureFlags);
+
+  if (!buffer) {
+    return nullptr;
   }
 
-  CreateBackBuffer(mBufferRect);
+  DebugOnly<bool> locked = buffer->Lock(LockMode());
+  MOZ_ASSERT(locked, "Could not lock the RemoteRotatedBuffer");
+
+  mIsNewBuffer = true;
+  mTextureFlags = textureFlags;
+
+  return buffer;
 }
 
-void
-ContentClientRemoteBuffer::CreateBackBuffer(const IntRect& aBufferRect)
+RefPtr<RotatedBuffer>
+ContentClientRemoteBuffer::CreateBufferInternal(const gfx::IntRect& aRect,
+                                                gfx::SurfaceFormat aFormat,
+                                                TextureFlags aFlags)
 {
-  // gfx::BackendType::NONE means fallback to the content backend
   TextureAllocationFlags textureAllocFlags
-                         = (mTextureFlags & TextureFlags::COMPONENT_ALPHA) ?
+                         = (aFlags & TextureFlags::COMPONENT_ALPHA) ?
                             TextureAllocationFlags::ALLOC_CLEAR_BUFFER_BLACK :
                             TextureAllocationFlags::ALLOC_CLEAR_BUFFER;
 
-  mTextureClient = CreateTextureClientForDrawing(
-    mSurfaceFormat, mSize, BackendSelector::Content,
-    mTextureFlags | ExtraTextureFlags(),
+  RefPtr<TextureClient> textureClient = CreateTextureClientForDrawing(
+    aFormat, aRect.Size(), BackendSelector::Content,
+    aFlags | ExtraTextureFlags(),
     textureAllocFlags
   );
-  if (!mTextureClient || !AddTextureClient(mTextureClient)) {
-    AbortTextureClientCreation();
-    return;
+
+  if (!textureClient || !AddTextureClient(textureClient)) {
+    return nullptr;
   }
-  mTextureClient->EnableBlockingReadLock();
+  textureClient->EnableBlockingReadLock();
 
-  if (mTextureFlags & TextureFlags::COMPONENT_ALPHA) {
-    mTextureClientOnWhite = mTextureClient->CreateSimilar(
+  RefPtr<TextureClient> textureClientOnWhite;
+  if (aFlags & TextureFlags::COMPONENT_ALPHA) {
+    textureClientOnWhite = textureClient->CreateSimilar(
       mForwarder->GetCompositorBackendType(),
-      mTextureFlags | ExtraTextureFlags(),
+      aFlags | ExtraTextureFlags(),
       TextureAllocationFlags::ALLOC_CLEAR_BUFFER_WHITE
     );
-    if (!mTextureClientOnWhite || !AddTextureClient(mTextureClientOnWhite)) {
-      AbortTextureClientCreation();
-      return;
+    if (!textureClientOnWhite || !AddTextureClient(textureClientOnWhite)) {
+      return nullptr;
     }
     // We don't enable the readlock for the white buffer since we always
     // use them together and waiting on the lock for the black
     // should be sufficient.
   }
-}
 
-void
-ContentClientRemoteBuffer::CreateBuffer(ContentType aType,
-                                        const IntRect& aRect,
-                                        uint32_t aFlags,
-                                        RefPtr<gfx::DrawTarget>* aBlackDT,
-                                        RefPtr<gfx::DrawTarget>* aWhiteDT)
-{
-  BuildTextureClients(gfxPlatform::GetPlatform()->Optimal2DFormatForContent(aType), aRect, aFlags);
-  if (!mTextureClient) {
-    return;
-  }
-
-  OpenMode mode = OpenMode::OPEN_READ_WRITE;
-  if (mInAsyncPaint) {
-    mode |= OpenMode::OPEN_ASYNC_WRITE;
-  }
-
-  // We just created the textures and we are about to get their draw targets
-  // so we have to lock them here.
-  DebugOnly<bool> locked = mTextureClient->Lock(mode);
-  MOZ_ASSERT(locked, "Could not lock the TextureClient");
-
-  *aBlackDT = mTextureClient->BorrowDrawTarget();
-  if (aFlags & BUFFER_COMPONENT_ALPHA) {
-    locked = mTextureClientOnWhite->Lock(mode);
-    MOZ_ASSERT(locked, "Could not lock the second TextureClient for component alpha");
-
-    *aWhiteDT = mTextureClientOnWhite->BorrowDrawTarget();
-  }
+  return new RemoteRotatedBuffer(textureClient,
+                                 textureClientOnWhite,
+                                 aRect,
+                                 IntPoint(0,0));
 }
 
 nsIntRegion
 ContentClientRemoteBuffer::GetUpdatedRegion(const nsIntRegion& aRegionToDraw,
                                             const nsIntRegion& aVisibleRegion)
 {
   nsIntRegion updatedRegion;
-  if (mIsNewBuffer || mDidSelfCopy) {
+  if (mIsNewBuffer || mBuffer->DidSelfCopy()) {
     // A buffer reallocation clears both buffers. The front buffer has all the
     // content by now, but the back buffer is still clear. Here, in effect, we
     // are saying to copy all of the pixels of the front buffer to the back.
     // Also when we self-copied in the buffer, the buffer space
     // changes and some changed buffer content isn't reflected in the
     // draw or invalidate region (on purpose!).  When this happens, we
     // need to read back the entire buffer too.
     updatedRegion = aVisibleRegion.GetBounds();
     mIsNewBuffer = false;
   } else {
     updatedRegion = aRegionToDraw;
   }
 
-  NS_ASSERTION(BufferRect().Contains(aRegionToDraw.GetBounds()),
+  MOZ_ASSERT(mBuffer, "should have a back buffer by now");
+  NS_ASSERTION(mBuffer->BufferRect().Contains(aRegionToDraw.GetBounds()),
                "Update outside of buffer rect!");
-  MOZ_ASSERT(mTextureClient, "should have a back buffer by now");
 
   return updatedRegion;
 }
 
 void
 ContentClientRemoteBuffer::Updated(const nsIntRegion& aRegionToDraw,
                                    const nsIntRegion& aVisibleRegion)
 {
   nsIntRegion updatedRegion = GetUpdatedRegion(aRegionToDraw,
                                                aVisibleRegion);
 
-  MOZ_ASSERT(mTextureClient);
-  if (mTextureClientOnWhite) {
-    mForwarder->UseComponentAlphaTextures(this, mTextureClient,
-                                          mTextureClientOnWhite);
+  RemoteRotatedBuffer* remoteBuffer = GetRemoteBuffer();
+
+  MOZ_ASSERT(remoteBuffer && remoteBuffer->GetClient());
+  if (remoteBuffer->HaveBufferOnWhite()) {
+    mForwarder->UseComponentAlphaTextures(this,
+                                          remoteBuffer->GetClient(),
+                                          remoteBuffer->GetClientOnWhite());
   } else {
     AutoTArray<CompositableForwarder::TimedTextureClient,1> textures;
     CompositableForwarder::TimedTextureClient* t = textures.AppendElement();
-    t->mTextureClient = mTextureClient;
-    IntSize size = mTextureClient->GetSize();
+    t->mTextureClient = remoteBuffer->GetClient();
+    IntSize size = remoteBuffer->GetClient()->GetSize();
     t->mPictureRect = nsIntRect(0, 0, size.width, size.height);
     GetForwarder()->UseTextures(this, textures);
   }
+
   // This forces a synchronous transaction, so we can swap buffers now
   // and know that we'll have sole ownership of the old front buffer
   // by the time we paint next.
   mForwarder->UpdateTextureRegion(this,
-                                  ThebesBufferData(BufferRect(),
-                                                   BufferRotation()),
+                                  ThebesBufferData(remoteBuffer->BufferRect(),
+                                                   remoteBuffer->BufferRotation()),
                                   updatedRegion);
   SwapBuffers(updatedRegion);
 }
 
-bool
-ContentClientRemoteBuffer::LockBuffers()
-{
-  OpenMode mode = OpenMode::OPEN_READ_WRITE;
-  if (mInAsyncPaint) {
-    mode |= OpenMode::OPEN_ASYNC_WRITE;
-  }
-  if (mTextureClient) {
-    bool locked = mTextureClient->Lock(mode);
-    if (!locked) {
-      return false;
-    }
-  }
-  if (mTextureClientOnWhite) {
-    bool locked = mTextureClientOnWhite->Lock(mode);
-    if (!locked) {
-      UnlockBuffers();
-      return false;
-    }
-  }
-  return true;
-}
-
-void
-ContentClientRemoteBuffer::UnlockBuffers()
-{
-  if (mTextureClient && mTextureClient->IsLocked()) {
-    mTextureClient->Unlock();
-  }
-  if (mTextureClientOnWhite && mTextureClientOnWhite->IsLocked()) {
-    mTextureClientOnWhite->Unlock();
-  }
-}
-
 void
 ContentClientRemoteBuffer::Dump(std::stringstream& aStream,
                                 const char* aPrefix,
                                 bool aDumpHtml, TextureDumpMode aCompress)
 {
+  RemoteRotatedBuffer* remoteBuffer = GetRemoteBuffer();
+
   // TODO We should combine the OnWhite/OnBlack here an just output a single image.
   if (!aDumpHtml) {
     aStream << "\n" << aPrefix << "Surface: ";
   }
-  CompositableClient::DumpTextureClient(aStream, mTextureClient, aCompress);
+  CompositableClient::DumpTextureClient(aStream,
+                                        remoteBuffer ? remoteBuffer->GetClient() : nullptr,
+                                        aCompress);
 }
 
 void
 ContentClientDoubleBuffered::Dump(std::stringstream& aStream,
                                   const char* aPrefix,
                                   bool aDumpHtml, TextureDumpMode aCompress)
 {
   // TODO We should combine the OnWhite/OnBlack here an just output a single image.
   if (!aDumpHtml) {
     aStream << "\n" << aPrefix << "Surface: ";
   }
-  if (mFrontBuffer) {
-    CompositableClient::DumpTextureClient(aStream, mFrontBuffer->GetClient(), aCompress);
-  }
-}
-
-void
-ContentClientDoubleBuffered::DestroyFrontBuffer()
-{
-  if (mFrontBuffer) {
-    RefPtr<TextureClient> client = mFrontBuffer->GetClient();
-    RefPtr<TextureClient> clientOnWhite = mFrontBuffer->GetClientOnWhite();
-
-    if (client) {
-      mOldTextures.AppendElement(client);
-    }
-    if (clientOnWhite) {
-      mOldTextures.AppendElement(clientOnWhite);
-    }
-
-    mFrontBuffer = Nothing();
-  }
+  CompositableClient::DumpTextureClient(aStream,
+                                        mFrontBuffer ? mFrontBuffer->GetClient() : nullptr,
+                                        aCompress);
 }
 
 void
 ContentClientDoubleBuffered::SwapBuffers(const nsIntRegion& aFrontUpdatedRegion)
 {
   mFrontUpdatedRegion = aFrontUpdatedRegion;
 
-  RefPtr<TextureClient> newBack;
-  RefPtr<TextureClient> newBackOnWhite;
-  IntRect newBackBufferRect;
-  nsIntPoint newBackBufferRotation;
+  RefPtr<RemoteRotatedBuffer> frontBuffer = mFrontBuffer;
+  RefPtr<RemoteRotatedBuffer> backBuffer = GetRemoteBuffer();
 
-  if (mFrontBuffer) {
-    newBack = mFrontBuffer->GetClient();
-    newBackOnWhite = mFrontBuffer->GetClientOnWhite();
-    newBackBufferRect = mFrontBuffer->BufferRect();
-    newBackBufferRotation = mFrontBuffer->BufferRotation();
-  }
+  std::swap(frontBuffer, backBuffer);
 
-  mFrontBuffer = Some(RemoteRotatedBuffer(mTextureClient, mTextureClientOnWhite,
-                                          mBufferRect, mBufferRotation));
-
-  mTextureClient = newBack;
-  mTextureClientOnWhite = newBackOnWhite;
-  mBufferRect = newBackBufferRect;
-  mBufferRotation = newBackBufferRotation;
+  mFrontBuffer = frontBuffer;
+  mBuffer = backBuffer;
 
   mFrontAndBackBufferDiffer = true;
 }
 
 void
 ContentClientDoubleBuffered::BeginPaint()
 {
   ContentClientRemoteBuffer::BeginPaint();
 
   mIsNewBuffer = false;
 
   if (!mFrontAndBackBufferDiffer) {
     return;
   }
 
-  if (!mFrontBuffer) {
+  if (!mFrontBuffer || !mBuffer) {
     mFrontAndBackBufferDiffer = false;
     return;
   }
 
-  if (mDidSelfCopy) {
+  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.
-    mBufferRect.MoveTo(mFrontBuffer->BufferRect().TopLeft());
-    mBufferRotation = nsIntPoint();
+    gfx::IntRect backBufferRect = mBuffer->BufferRect();
+    backBufferRect.MoveTo(mFrontBuffer->BufferRect().TopLeft());
+
+    mBuffer->SetBufferRect(backBufferRect);
+    mBuffer->SetBufferRotation(IntPoint(0,0));
     return;
   }
-  mBufferRect = mFrontBuffer->BufferRect();
-  mBufferRotation = mFrontBuffer->BufferRotation();
+  mBuffer->SetBufferRect(mFrontBuffer->BufferRect());
+  mBuffer->SetBufferRotation(mFrontBuffer->BufferRotation());
 }
 
 void
 ContentClientDoubleBuffered::BeginAsyncPaint()
 {
   BeginPaint();
   mInAsyncPaint = true;
 }
@@ -613,17 +893,17 @@ ContentClientDoubleBuffered::BeginAsyncP
 // 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)
 {
   if (!mFrontAndBackBufferDiffer) {
-    MOZ_ASSERT(!mDidSelfCopy, "If we have to copy the world, then our buffers are different, right?");
+    MOZ_ASSERT(!mFrontBuffer->DidSelfCopy(), "If we have to copy the world, then our buffers are different, right?");
     return;
   }
   MOZ_ASSERT(mFrontBuffer);
   if (!mFrontBuffer) {
     return;
   }
 
   MOZ_LAYERS_LOG(("BasicShadowableThebes(%p): reading back <x=%d,y=%d,w=%d,h=%d>",
@@ -631,44 +911,43 @@ ContentClientDoubleBuffered::FinalizeFra
                   mFrontUpdatedRegion.GetBounds().x,
                   mFrontUpdatedRegion.GetBounds().y,
                   mFrontUpdatedRegion.GetBounds().Width(),
                   mFrontUpdatedRegion.GetBounds().Height()));
 
   mFrontAndBackBufferDiffer = false;
 
   nsIntRegion updateRegion = mFrontUpdatedRegion;
-  if (mDidSelfCopy) {
-    mDidSelfCopy = false;
-    updateRegion = mBufferRect;
+  if (mFrontBuffer->DidSelfCopy()) {
+    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;
   }
 
-  if (!EnsureBuffer() ||
-      (HaveBufferOnWhite() && !EnsureBufferOnWhite())) {
+  if (!mBuffer) {
     return;
   }
 
   if (mFrontBuffer->Lock(OpenMode::OPEN_READ_ONLY)) {
-    UpdateDestinationFrom(*mFrontBuffer, updateRegion);
+    mBuffer->UpdateDestinationFrom(*mFrontBuffer, updateRegion);
     mFrontBuffer->Unlock();
   }
 }
 
 void
 ContentClientDoubleBuffered::EnsureBackBufferIfFrontBuffer()
 {
-  if (!mTextureClient && mFrontBuffer) {
-    CreateBackBuffer(mFrontBuffer->BufferRect());
-
-    mBufferRect = mFrontBuffer->BufferRect();
-    mBufferRotation = mFrontBuffer->BufferRotation();
+  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
@@ -71,100 +71,222 @@ class CapturedPaintState;
  * Then return that DrawTarget using ReturnDrawTarget.
  * Call EndPaint on the content client;
  *
  * SwapBuffers is called in response to the transaction reply from the compositor.
  */
 class ContentClient : public CompositableClient
 {
 public:
+  typedef gfxContentType ContentType;
+
   /**
    * Creates, configures, and returns a new content client. If necessary, a
    * message will be sent to the compositor to create a corresponding content
    * host.
    */
   static already_AddRefed<ContentClient> CreateContentClient(CompositableForwarder* aFwd);
 
-  explicit ContentClient(CompositableForwarder* aForwarder)
-  : CompositableClient(aForwarder),
-    mInAsyncPaint(false)
+  /**
+   * Controls the size of the backing buffer of this.
+   * - SizedToVisibleBounds: the backing buffer is exactly the same
+   *   size as the bounds of PaintedLayer's visible region
+   * - ContainsVisibleBounds: the backing buffer is large enough to
+   *   fit visible bounds.  May be larger.
+   */
+  enum BufferSizePolicy {
+    SizedToVisibleBounds,
+    ContainsVisibleBounds
+  };
+
+  explicit ContentClient(CompositableForwarder* aForwarder,
+                         BufferSizePolicy aBufferSizePolicy)
+  : CompositableClient(aForwarder)
+  , mBufferSizePolicy(aBufferSizePolicy)
+  , mInAsyncPaint(false)
   {}
   virtual ~ContentClient()
   {}
 
   virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
 
-  virtual void Clear() = 0;
-  virtual RotatedContentBuffer::PaintState BeginPaintBuffer(PaintedLayer* aLayer,
-                                                            uint32_t aFlags) = 0;
-  virtual gfx::DrawTarget* BorrowDrawTargetForPainting(RotatedContentBuffer::PaintState& aPaintState,
-                                                       RotatedContentBuffer::DrawIterator* aIter = nullptr) = 0;
-  virtual void ReturnDrawTargetToBuffer(gfx::DrawTarget*& aReturned) = 0;
+  virtual void Clear();
+
+  /**
+   * This is returned by BeginPaint. The caller should draw into mTarget.
+   * mRegionToDraw must be drawn. mRegionToInvalidate has been invalidated
+   * by RotatedContentBuffer and must be redrawn on the screen.
+   * mRegionToInvalidate is set when the buffer has changed from
+   * opaque to transparent or vice versa, since the details of rendering can
+   * depend on the buffer type.
+   */
+  struct PaintState {
+    PaintState()
+      : mRegionToDraw()
+      , mRegionToInvalidate()
+      , mMode(SurfaceMode::SURFACE_NONE)
+      , mClip(DrawRegionClip::NONE)
+      , mContentType(gfxContentType::SENTINEL)
+    {}
+
+    nsIntRegion mRegionToDraw;
+    nsIntRegion mRegionToInvalidate;
+    SurfaceMode mMode;
+    DrawRegionClip mClip;
+    gfxContentType mContentType;
+  };
+
+  enum {
+    PAINT_WILL_RESAMPLE = 0x01,
+    PAINT_NO_ROTATION = 0x02,
+    PAINT_CAN_DRAW_ROTATED = 0x04
+  };
+
+  /**
+   * Start a drawing operation. This returns a PaintState describing what
+   * needs to be drawn to bring the buffer up to date in the visible region.
+   * This queries aLayer to get the currently valid and visible regions.
+   * The returned mTarget may be null if mRegionToDraw is empty.
+   * Otherwise it must not be null.
+   * mRegionToInvalidate will contain mRegionToDraw.
+   * @param aFlags when PAINT_WILL_RESAMPLE is passed, this indicates that
+   * buffer will be resampled when rendering (i.e the effective transform
+   * combined with the scale for the resolution is not just an integer
+   * translation). This will disable buffer rotation (since we don't want
+   * to resample across the rotation boundary) and will ensure that we
+   * make the entire buffer contents valid (since we don't want to sample
+   * invalid pixels outside the visible region, if the visible region doesn't
+   * fill the buffer bounds).
+   * PAINT_CAN_DRAW_ROTATED can be passed if the caller supports drawing
+   * rotated content that crosses the physical buffer boundary. The caller
+   * will need to call BorrowDrawTargetForPainting multiple times to achieve
+   * this.
+   */
+  PaintState BeginPaintBuffer(PaintedLayer* aLayer, uint32_t aFlags);
+
+  /**
+   * Fetch a DrawTarget for rendering. The DrawTarget remains owned by
+   * this. See notes on BorrowDrawTargetForQuadrantUpdate.
+   * May return null. If the return value is non-null, it must be
+   * 'un-borrowed' using ReturnDrawTarget.
+   *
+   * If PAINT_CAN_DRAW_ROTATED was specified for BeginPaint, then the caller
+   * must call this function repeatedly (with an iterator) until it returns
+   * nullptr. The caller should draw the mDrawRegion of the iterator instead
+   * of mRegionToDraw in the PaintState.
+   *
+   * @param aPaintState Paint state data returned by a call to BeginPaint
+   * @param aIter Paint state iterator. Only required if PAINT_CAN_DRAW_ROTATED
+   * was specified to BeginPaint.
+   */
+  virtual gfx::DrawTarget* BorrowDrawTargetForPainting(
+    PaintState& aPaintState,
+    RotatedBuffer::DrawIterator* aIter = nullptr);
+
+  /**
+   * Borrow a draw target for recording. The required transform for correct painting
+   * is not applied to the returned DrawTarget by default, BUT it is
+   * required to be whenever drawing does happen.
+   */
   virtual RefPtr<CapturedPaintState> BorrowDrawTargetForRecording(
-    RotatedContentBuffer::PaintState& aPaintState,
-    RotatedContentBuffer::DrawIterator* aIter) = 0;
+    PaintState& aPaintState,
+    RotatedBuffer::DrawIterator* aIter,
+    bool aSetTransform = false);
+
+  virtual void ReturnDrawTargetToBuffer(gfx::DrawTarget*& aReturned);
 
   // Called as part of the layers transation reply. Conveys data about our
   // buffer(s) from the compositor. If appropriate we should swap references
   // to our buffers.
   virtual void SwapBuffers(const nsIntRegion& aFrontUpdatedRegion) {}
 
-  // call before and after painting into this content client
+  // Call before and after painting into this content client
   virtual void BeginPaint() {}
   virtual void BeginAsyncPaint();
   virtual void EndPaint(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates = nullptr);
 
+  nsIntRegion ExpandDrawRegion(PaintState& aPaintState,
+                               RotatedBuffer::DrawIterator* aIter,
+                               gfx::BackendType aBackendType);
+
+  static bool PrepareDrawTargetForPainting(CapturedPaintState*);
+
+  enum {
+    BUFFER_COMPONENT_ALPHA = 0x02 // Dual buffers should be created for drawing with
+                                  // component alpha.
+  };
+
 protected:
+  struct BufferDecision {
+    nsIntRegion mNeededRegion;
+    nsIntRegion mValidRegion;
+    gfx::IntRect mBufferRect;
+    SurfaceMode mBufferMode;
+    gfxContentType mBufferContentType;
+    bool mCanReuseBuffer;
+    bool mCanKeepBufferContents;
+  };
+
+  BufferDecision CalculateBufferForPaint(PaintedLayer* aLayer,
+                                         uint32_t aFlags);
+
+  /**
+   * Return the buffer's content type.  Requires a valid buffer or
+   * buffer provider.
+   */
+  gfxContentType BufferContentType();
+  bool BufferSizeOkFor(const gfx::IntSize& aSize);
+
+  OpenMode LockMode() const;
+
+  /**
+   * Any actions that should be performed at the last moment before we begin
+   * rendering the next frame. I.e., after we calculate what we will draw,
+   * but before we rotate the buffer and possibly create new buffers.
+   * aRegionToDraw is the region which is guaranteed to be overwritten when
+   * drawing the next frame.
+   */
+  virtual void FinalizeFrame(const nsIntRegion& aRegionToDraw) {}
+
+  virtual RefPtr<RotatedBuffer> CreateBuffer(gfxContentType aType,
+                                             const gfx::IntRect& aRect,
+                                             uint32_t aFlags) = 0;
+
+  RefPtr<RotatedBuffer> mBuffer;
+  BufferSizePolicy      mBufferSizePolicy;
   bool mInAsyncPaint;
 };
 
-// thin wrapper around RotatedContentBuffer, for on-mtc
+// Thin wrapper around DrawTargetRotatedBuffer, for on-mtc
 class ContentClientBasic final : public ContentClient
-                               , protected RotatedContentBuffer
 {
 public:
   explicit ContentClientBasic(gfx::BackendType aBackend);
 
-  typedef RotatedContentBuffer::PaintState PaintState;
-  typedef RotatedContentBuffer::ContentType ContentType;
-
-  virtual void Clear() override { RotatedContentBuffer::Clear(); }
-  virtual PaintState BeginPaintBuffer(PaintedLayer* aLayer,
-                                      uint32_t aFlags) override
-  {
-    return RotatedContentBuffer::BeginPaint(aLayer, aFlags);
-  }
-  virtual gfx::DrawTarget* BorrowDrawTargetForPainting(PaintState& aPaintState,
-                                                       RotatedContentBuffer::DrawIterator* aIter = nullptr) override
-  {
-    return RotatedContentBuffer::BorrowDrawTargetForPainting(aPaintState, aIter);
-  }
   virtual RefPtr<CapturedPaintState> BorrowDrawTargetForRecording(PaintState& aPaintState,
-                                                                  RotatedContentBuffer::DrawIterator* aIter) override;
-
-  virtual void ReturnDrawTargetToBuffer(gfx::DrawTarget*& aReturned) override
-  {
-    BorrowDrawTarget::ReturnDrawTarget(aReturned);
-  }
+                                                                  RotatedBuffer::DrawIterator* aIter,
+                                                                  bool aSetTransform) override;
 
   void DrawTo(PaintedLayer* aLayer,
               gfx::DrawTarget* aTarget,
               float aOpacity,
               gfx::CompositionOp aOp,
               gfx::SourceSurface* aMask,
               const gfx::Matrix* aMaskTransform);
 
-  virtual void CreateBuffer(ContentType aType, const gfx::IntRect& aRect, uint32_t aFlags,
-                            RefPtr<gfx::DrawTarget>* aBlackDT, RefPtr<gfx::DrawTarget>* aWhiteDT) override;
-
   virtual TextureInfo GetTextureInfo() const override
   {
     MOZ_CRASH("GFX: Should not be called on non-remote ContentClient");
   }
 
+protected:
+  virtual RefPtr<RotatedBuffer> CreateBuffer(gfxContentType aType,
+                                             const gfx::IntRect& aRect,
+                                             uint32_t aFlags) override;
+
 private:
   gfx::BackendType mBackend;
 };
 
 /**
  * A ContentClient backed by a RotatedContentBuffer.
  *
  * When using a ContentClientRemoteBuffer, SurfaceDescriptors are created on
@@ -176,134 +298,74 @@ private:
  * compositor should assign the corresponding TextureHosts to our corresponding
  * ContentHost.
  *
  * If the size or type of our buffer(s) change(s), then we simply destroy and
  * create them.
  */
 // Version using new texture clients
 class ContentClientRemoteBuffer : public ContentClient
-                                , protected RotatedContentBuffer
 {
-  using RotatedContentBuffer::BufferRect;
-  using RotatedContentBuffer::BufferRotation;
 public:
   explicit ContentClientRemoteBuffer(CompositableForwarder* aForwarder)
-    : ContentClient(aForwarder)
-    , RotatedContentBuffer(ContainsVisibleBounds)
+    : ContentClient(aForwarder, ContainsVisibleBounds)
     , mIsNewBuffer(false)
-    , mSurfaceFormat(gfx::SurfaceFormat::B8G8R8A8)
   {}
 
-  typedef RotatedContentBuffer::PaintState PaintState;
-  typedef RotatedContentBuffer::ContentType ContentType;
-
-  virtual void Clear() override
-  {
-    RotatedContentBuffer::Clear();
-    mTextureClient = nullptr;
-    mTextureClientOnWhite = nullptr;
-  }
-
   virtual void Dump(std::stringstream& aStream,
                     const char* aPrefix="",
                     bool aDumpHtml=false,
                     TextureDumpMode aCompress=TextureDumpMode::Compress) override;
 
-  virtual PaintState BeginPaintBuffer(PaintedLayer* aLayer,
-                                      uint32_t aFlags) override
-  {
-    return RotatedContentBuffer::BeginPaint(aLayer, aFlags);
-  }
-  virtual gfx::DrawTarget* BorrowDrawTargetForPainting(PaintState& aPaintState,
-                                                       RotatedContentBuffer::DrawIterator* aIter = nullptr) override
-  {
-    return RotatedContentBuffer::BorrowDrawTargetForPainting(aPaintState, aIter);
-  }
   virtual RefPtr<CapturedPaintState> BorrowDrawTargetForRecording(PaintState& aPaintState,
-                                                                  RotatedContentBuffer::DrawIterator* aIter) override;
-
-  virtual void ReturnDrawTargetToBuffer(gfx::DrawTarget*& aReturned) override
-  {
-    BorrowDrawTarget::ReturnDrawTarget(aReturned);
-  }
+                                                                  RotatedBuffer::DrawIterator* aIter,
+                                                                  bool aSetTransform) override;
 
   /**
    * Begin/End Paint map a gfxASurface from the texture client
    * into the buffer of RotatedBuffer. The surface is only
    * valid when the texture client is locked, so is mapped out
    * of RotatedContentBuffer when we are done painting.
    * None of the underlying buffer attributes (rect, rotation)
    * are affected by mapping/unmapping.
    */
   virtual void BeginPaint() override;
   virtual void BeginAsyncPaint() override;
   virtual void EndPaint(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates = nullptr) override;
 
   virtual void Updated(const nsIntRegion& aRegionToDraw,
                        const nsIntRegion& aVisibleRegion);
 
-  // Expose these protected methods from the superclass.
-  virtual const gfx::IntRect& BufferRect() const
-  {
-    return RotatedContentBuffer::BufferRect();
-  }
-  virtual const nsIntPoint& BufferRotation() const
-  {
-    return RotatedContentBuffer::BufferRotation();
-  }
-
-  virtual void CreateBuffer(ContentType aType, const gfx::IntRect& aRect, uint32_t aFlags,
-                            RefPtr<gfx::DrawTarget>* aBlackDT, RefPtr<gfx::DrawTarget>* aWhiteDT) override;
-
   virtual TextureFlags ExtraTextureFlags() const
   {
     return TextureFlags::IMMEDIATE_UPLOAD;
   }
 
 protected:
-  void DestroyBuffers();
-
   virtual nsIntRegion GetUpdatedRegion(const nsIntRegion& aRegionToDraw,
                                        const nsIntRegion& aVisibleRegion);
 
-  void BuildTextureClients(gfx::SurfaceFormat aFormat,
-                           const gfx::IntRect& aRect,
-                           uint32_t aFlags);
-
-  void CreateBackBuffer(const gfx::IntRect& aBufferRect);
-
   // Ensure we have a valid back buffer if we have a valid front buffer (i.e.
   // if a backbuffer has been created.)
   virtual void EnsureBackBufferIfFrontBuffer() {}
 
-  // Create the front buffer for the ContentClient/Host pair if necessary
-  // and notify the compositor that we have created the buffer(s).
-  virtual void DestroyFrontBuffer() {}
+  virtual RefPtr<RotatedBuffer> CreateBuffer(gfxContentType aType,
+                                             const gfx::IntRect& aRect,
+                                             uint32_t aFlags) override;
 
-  virtual void AbortTextureClientCreation()
+  RefPtr<RotatedBuffer> CreateBufferInternal(const gfx::IntRect& aRect,
+                                             gfx::SurfaceFormat aFormat,
+                                             TextureFlags aFlags);
+
+  RemoteRotatedBuffer* GetRemoteBuffer() const
   {
-    mTextureClient = nullptr;
-    mTextureClientOnWhite = nullptr;
-    mIsNewBuffer = false;
+    return static_cast<RemoteRotatedBuffer*>(mBuffer.get());
   }
 
-  virtual bool LockBuffers() override;
-  virtual void UnlockBuffers() override;
-
-  RefPtr<TextureClient> mTextureClient;
-  RefPtr<TextureClient> mTextureClientOnWhite;
-  // keep a record of texture clients we have created and need to keep around
-  // (for RotatedBuffer to access), then unlock and remove them when we are done
-  // painting.
-  nsTArray<RefPtr<TextureClient> > mOldTextures;
-
   bool mIsNewBuffer;
-  gfx::IntSize mSize;
-  gfx::SurfaceFormat mSurfaceFormat;
 };
 
 /**
  * A double buffered ContentClient. mTextureClient is the back buffer, which
  * we draw into. mFrontClient is the front buffer which we may read from, but
  * not write to, when the compositor does not have the 'soft' lock. We can write
  * into mTextureClient at any time.
  *
@@ -317,53 +379,43 @@ class ContentClientDoubleBuffered : publ
 public:
   explicit ContentClientDoubleBuffered(CompositableForwarder* aFwd)
     : ContentClientRemoteBuffer(aFwd)
     , mFrontAndBackBufferDiffer(false)
   {}
 
   virtual ~ContentClientDoubleBuffered() {}
 
+  virtual void Dump(std::stringstream& aStream,
+                    const char* aPrefix="",
+                    bool aDumpHtml=false,
+                    TextureDumpMode aCompress=TextureDumpMode::Compress) override;
+
   virtual void Clear() override
   {
-    ContentClientRemoteBuffer::Clear();
-    mFrontBuffer = Nothing();
+    ContentClient::Clear();
+    mFrontBuffer = nullptr;
   }
 
   virtual void SwapBuffers(const nsIntRegion& aFrontUpdatedRegion) override;
 
   virtual void BeginPaint() override;
   virtual void BeginAsyncPaint() override;
 
   virtual void FinalizeFrame(const nsIntRegion& aRegionToDraw) override;
 
   virtual void EnsureBackBufferIfFrontBuffer() override;
 
   virtual TextureInfo GetTextureInfo() const override
   {
     return TextureInfo(CompositableType::CONTENT_DOUBLE, mTextureFlags);
   }
 
-  virtual void Dump(std::stringstream& aStream,
-                    const char* aPrefix="",
-                    bool aDumpHtml=false,
-                    TextureDumpMode aCompress=TextureDumpMode::Compress) override;
-protected:
-  virtual void DestroyFrontBuffer() override;
-
 private:
-
-  virtual void AbortTextureClientCreation() override
-  {
-    mTextureClient = nullptr;
-    mTextureClientOnWhite = nullptr;
-    mFrontBuffer = Nothing();
-  }
-
-  Maybe<RemoteRotatedBuffer> mFrontBuffer;
+  RefPtr<RemoteRotatedBuffer> mFrontBuffer;
   nsIntRegion mFrontUpdatedRegion;
   bool mFrontAndBackBufferDiffer;
 };
 
 /**
  * A single buffered ContentClient. We have a single TextureClient/Host
  * which we update and then send a message to the compositor that we are
  * done updating. It is not safe for the compositor to use the corresponding
--- a/gfx/layers/composite/ContentHost.h
+++ b/gfx/layers/composite/ContentHost.h
@@ -15,16 +15,17 @@
 #include "mozilla/RefPtr.h"             // for RefPtr
 #include "mozilla/gfx/BasePoint.h"      // for BasePoint
 #include "mozilla/gfx/MatrixFwd.h"      // for Matrix4x4
 #include "mozilla/gfx/Point.h"          // for Point
 #include "mozilla/gfx/Polygon.h"        // for Polygon
 #include "mozilla/gfx/Rect.h"           // for Rect
 #include "mozilla/gfx/Types.h"          // for SamplingFilter
 #include "mozilla/layers/CompositorTypes.h"  // for TextureInfo, etc
+#include "mozilla/layers/ContentClient.h"  // for ContentClient
 #include "mozilla/layers/ISurfaceAllocator.h"  // for ISurfaceAllocator
 #include "mozilla/layers/LayersSurfaces.h"  // for SurfaceDescriptor
 #include "mozilla/layers/LayersTypes.h"  // for etc
 #include "mozilla/layers/TextureHost.h"  // for TextureHost
 #include "mozilla/mozalloc.h"           // for operator delete
 #include "mozilla/UniquePtr.h"          // for UniquePtr
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsDebug.h"                    // for NS_RUNTIMEABORT
@@ -86,18 +87,18 @@ protected:
  * texture hosts (for single and double buffering) to the ContentHost.
  *
  * It is the responsibility of the ContentHost to destroy its resources when
  * they are recreated or the ContentHost dies.
  */
 class ContentHostBase : public ContentHost
 {
 public:
-  typedef RotatedContentBuffer::ContentType ContentType;
-  typedef RotatedContentBuffer::PaintState PaintState;
+  typedef ContentClient::ContentType ContentType;
+  typedef ContentClient::PaintState PaintState;
 
   explicit ContentHostBase(const TextureInfo& aTextureInfo);
   virtual ~ContentHostBase();
 
   virtual gfx::IntRect GetBufferRect() override { return mBufferRect; }
 
   virtual nsIntPoint GetOriginOffset()
   {