Move UpdateDestinationFrom to RotatedBuffer. (bug 1409871 part 2, r=nical) draft
authorRyan Hunt <rhunt@eqrion.net>
Wed, 11 Oct 2017 13:40:16 -0400
changeset 684124 8025b2a5620a4e4aaf437e6816078821f63e82e8
parent 684123 672583eb655d89fa231f83617b9aae32c496cc6f
child 684125 452a8c19e7a8b931638935af492b395cc73830cc
push id85567
push userbmo:rhunt@eqrion.net
push dateFri, 20 Oct 2017 22:13:22 +0000
reviewersnical
bugs1409871
milestone58.0a1
Move UpdateDestinationFrom to RotatedBuffer. (bug 1409871 part 2, r=nical) UpdateDestinationFrom is a useful method for any rotated buffer. This will be needed when content clients are no longer rotated buffers. Note: EnsureBuffer and EnsureBufferOnWhite were moved out of UpdateDestinationFrom because I'd like to kill those methods, and so the calls were moved to all users of UpdateDestinationFrom. MozReview-Commit-ID: 2e3HhUsZ6iw
gfx/layers/RotatedBuffer.cpp
gfx/layers/RotatedBuffer.h
gfx/layers/client/ContentClient.cpp
gfx/layers/client/ContentClient.h
--- a/gfx/layers/RotatedBuffer.cpp
+++ b/gfx/layers/RotatedBuffer.cpp
@@ -30,16 +30,30 @@
 #include "PaintThread.h"
 
 namespace mozilla {
 
 using namespace gfx;
 
 namespace layers {
 
+void
+BorrowDrawTarget::ReturnDrawTarget(gfx::DrawTarget*& aReturned)
+{
+  MOZ_ASSERT(mLoanedDrawTarget);
+  MOZ_ASSERT(aReturned == mLoanedDrawTarget);
+  if (mLoanedDrawTarget) {
+    if (mSetTransform) {
+      mLoanedDrawTarget->SetTransform(mLoanedTransform);
+    }
+    mLoanedDrawTarget = nullptr;
+  }
+  aReturned = nullptr;
+}
+
 IntRect
 RotatedBuffer::GetQuadrantRectangle(XSide aXSide, YSide aYSide) const
 {
   // quadrantTranslation is the amount we translate the top-left
   // of the quadrant by to get coordinates relative to the layer
   IntPoint quadrantTranslation = -mBufferRotation;
   quadrantTranslation.x += aXSide == LEFT ? mBufferRect.Width() : 0;
   quadrantTranslation.y += aYSide == TOP ? mBufferRect.Height() : 0;
@@ -177,40 +191,149 @@ RotatedBuffer::DrawBufferWithRotation(gf
   // though! Particularly on D2D Repeat should be a lot faster, need to look
   // into that. TODO[Bas]
   DrawBufferQuadrant(aTarget, LEFT, TOP, aSource, aOpacity, aOperator, aMask, aMaskTransform);
   DrawBufferQuadrant(aTarget, RIGHT, TOP, aSource, aOpacity, aOperator, aMask, aMaskTransform);
   DrawBufferQuadrant(aTarget, LEFT, BOTTOM, aSource, aOpacity, aOperator, aMask, aMaskTransform);
   DrawBufferQuadrant(aTarget, RIGHT, BOTTOM, aSource, aOpacity, aOperator,aMask, aMaskTransform);
 }
 
+bool IsClippingCheap(gfx::DrawTarget* aTarget, const nsIntRegion& aRegion)
+{
+  // Assume clipping is cheap if the draw target just has an integer
+  // translation, and the visible region is simple.
+  return !aTarget->GetTransform().HasNonIntegerTranslation() &&
+         aRegion.GetNumRects() <= 1;
+}
+
+void
+RotatedBuffer::UpdateDestinationFrom(const RotatedBuffer& aSource,
+                                     const nsIntRegion& aUpdateRegion)
+{
+  DrawIterator iter;
+  while (DrawTarget* destDT =
+    BorrowDrawTargetForQuadrantUpdate(aUpdateRegion.GetBounds(), BUFFER_BLACK, &iter)) {
+    bool isClippingCheap = IsClippingCheap(destDT, iter.mDrawRegion);
+    if (isClippingCheap) {
+      gfxUtils::ClipToRegion(destDT, iter.mDrawRegion);
+    }
+
+    aSource.DrawBufferWithRotation(destDT, BUFFER_BLACK, 1.0, CompositionOp::OP_SOURCE);
+    if (isClippingCheap) {
+      destDT->PopClip();
+    }
+    // Flush the destination before the sources become inaccessible (Unlock).
+    destDT->Flush();
+    ReturnDrawTarget(destDT);
+  }
+
+  if (aSource.HaveBufferOnWhite()) {
+    MOZ_ASSERT(HaveBufferOnWhite());
+    DrawIterator whiteIter;
+    while (DrawTarget* destDT =
+      BorrowDrawTargetForQuadrantUpdate(aUpdateRegion.GetBounds(), BUFFER_WHITE, &whiteIter)) {
+      bool isClippingCheap = IsClippingCheap(destDT, whiteIter.mDrawRegion);
+      if (isClippingCheap) {
+        gfxUtils::ClipToRegion(destDT, whiteIter.mDrawRegion);
+      }
+
+      aSource.DrawBufferWithRotation(destDT, BUFFER_WHITE, 1.0, CompositionOp::OP_SOURCE);
+      if (isClippingCheap) {
+        destDT->PopClip();
+      }
+      // Flush the destination before the sources become inaccessible (Unlock).
+      destDT->Flush();
+      ReturnDrawTarget(destDT);
+    }
+  }
+}
+
+DrawTarget*
+RotatedBuffer::BorrowDrawTargetForQuadrantUpdate(const IntRect& aBounds,
+                                                 ContextSource aSource,
+                                                 DrawIterator* aIter,
+                                                 bool aSetTransform,
+                                                 Matrix* aOutMatrix)
+{
+  IntRect bounds = aBounds;
+  if (aIter) {
+    // If an iterator was provided, then BeginPaint must have been run with
+    // PAINT_CAN_DRAW_ROTATED, and the draw region might cover multiple quadrants.
+    // Iterate over each of them, and return an appropriate buffer each time we find
+    // one that intersects the draw region. The iterator mCount value tracks which
+    // quadrants we have considered across multiple calls to this function.
+    aIter->mDrawRegion.SetEmpty();
+    while (aIter->mCount < 4) {
+      IntRect quadrant = GetQuadrantRectangle((aIter->mCount & 1) ? LEFT : RIGHT,
+        (aIter->mCount & 2) ? TOP : BOTTOM);
+      aIter->mDrawRegion.And(aBounds, quadrant);
+      aIter->mCount++;
+      if (!aIter->mDrawRegion.IsEmpty()) {
+        break;
+      }
+    }
+    if (aIter->mDrawRegion.IsEmpty()) {
+      return nullptr;
+    }
+    bounds = aIter->mDrawRegion.GetBounds();
+  }
+
+  gfx::DrawTarget* dtBuffer = GetDTBuffer();
+  gfx::DrawTarget* dtBufferOnWhite = GetDTBufferOnWhite();
+
+  MOZ_ASSERT(!mLoanedDrawTarget, "draw target has been borrowed and not returned");
+  if (aSource == BUFFER_BOTH && HaveBufferOnWhite()) {
+    MOZ_ASSERT(dtBuffer && dtBuffer->IsValid() && dtBufferOnWhite && dtBufferOnWhite->IsValid());
+    mLoanedDrawTarget = Factory::CreateDualDrawTarget(dtBuffer, dtBufferOnWhite);
+  } else if (aSource == BUFFER_WHITE) {
+    mLoanedDrawTarget = dtBufferOnWhite;
+  } else {
+    // BUFFER_BLACK, or BUFFER_BOTH with a single buffer.
+    mLoanedDrawTarget = dtBuffer;
+  }
+
+  // Figure out which quadrant to draw in
+  int32_t xBoundary = mBufferRect.XMost() - mBufferRotation.x;
+  int32_t yBoundary = mBufferRect.YMost() - mBufferRotation.y;
+  XSide sideX = bounds.XMost() <= xBoundary ? RIGHT : LEFT;
+  YSide sideY = bounds.YMost() <= yBoundary ? BOTTOM : TOP;
+  IntRect quadrantRect = GetQuadrantRectangle(sideX, sideY);
+  NS_ASSERTION(quadrantRect.Contains(bounds), "Messed up quadrants");
+
+  if (aSetTransform) {
+    mLoanedTransform = mLoanedDrawTarget->GetTransform();
+    Matrix transform = Matrix(mLoanedTransform)
+                            .PreTranslate(-quadrantRect.x,
+                                          -quadrantRect.y);
+    mLoanedDrawTarget->SetTransform(transform);
+    mSetTransform = true;
+  } else {
+    MOZ_ASSERT(aOutMatrix);
+    *aOutMatrix = Matrix::Translation(-quadrantRect.x, -quadrantRect.y);
+    mSetTransform = false;
+  }
+
+  return mLoanedDrawTarget;
+}
+
 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();
 }
 
-/* static */ bool
-RotatedContentBuffer::IsClippingCheap(DrawTarget* aTarget, const nsIntRegion& aRegion)
-{
-  // Assume clipping is cheap if the draw target just has an integer
-  // translation, and the visible region is simple.
-  return !aTarget->GetTransform().HasNonIntegerTranslation() &&
-         aRegion.GetNumRects() <= 1;
-}
-
 void
 RotatedContentBuffer::DrawTo(PaintedLayer* aLayer,
                              DrawTarget* aTarget,
                              float aOpacity,
                              CompositionOp aOp,
                              SourceSurface* aMask,
                              const Matrix* aMaskTransform)
 {
@@ -238,105 +361,16 @@ RotatedContentBuffer::DrawTo(PaintedLaye
   }
 
   DrawBufferWithRotation(aTarget, BUFFER_BLACK, aOpacity, aOp, aMask, aMaskTransform);
   if (clipped) {
     aTarget->PopClip();
   }
 }
 
-DrawTarget*
-RotatedContentBuffer::BorrowDrawTargetForQuadrantUpdate(const IntRect& aBounds,
-                                                        ContextSource aSource,
-                                                        DrawIterator* aIter,
-                                                        bool aSetTransform,
-                                                        Matrix* aOutMatrix)
-{
-  IntRect bounds = aBounds;
-  if (aIter) {
-    // If an iterator was provided, then BeginPaint must have been run with
-    // PAINT_CAN_DRAW_ROTATED, and the draw region might cover multiple quadrants.
-    // Iterate over each of them, and return an appropriate buffer each time we find
-    // one that intersects the draw region. The iterator mCount value tracks which
-    // quadrants we have considered across multiple calls to this function.
-    aIter->mDrawRegion.SetEmpty();
-    while (aIter->mCount < 4) {
-      IntRect quadrant = GetQuadrantRectangle((aIter->mCount & 1) ? LEFT : RIGHT,
-        (aIter->mCount & 2) ? TOP : BOTTOM);
-      aIter->mDrawRegion.And(aBounds, quadrant);
-      aIter->mCount++;
-      if (!aIter->mDrawRegion.IsEmpty()) {
-        break;
-      }
-    }
-    if (aIter->mDrawRegion.IsEmpty()) {
-      return nullptr;
-    }
-    bounds = aIter->mDrawRegion.GetBounds();
-  }
-
-  if (!EnsureBuffer()) {
-    return nullptr;
-  }
-
-  MOZ_ASSERT(!mLoanedDrawTarget, "draw target has been borrowed and not returned");
-  if (aSource == BUFFER_BOTH && HaveBufferOnWhite()) {
-    if (!EnsureBufferOnWhite()) {
-      return nullptr;
-    }
-    MOZ_ASSERT(mDTBuffer && mDTBuffer->IsValid() && mDTBufferOnWhite && mDTBufferOnWhite->IsValid());
-    mLoanedDrawTarget = Factory::CreateDualDrawTarget(mDTBuffer, mDTBufferOnWhite);
-  } else if (aSource == BUFFER_WHITE) {
-    if (!EnsureBufferOnWhite()) {
-      return nullptr;
-    }
-    mLoanedDrawTarget = mDTBufferOnWhite;
-  } else {
-    // BUFFER_BLACK, or BUFFER_BOTH with a single buffer.
-    mLoanedDrawTarget = mDTBuffer;
-  }
-
-  // Figure out which quadrant to draw in
-  int32_t xBoundary = mBufferRect.XMost() - mBufferRotation.x;
-  int32_t yBoundary = mBufferRect.YMost() - mBufferRotation.y;
-  XSide sideX = bounds.XMost() <= xBoundary ? RIGHT : LEFT;
-  YSide sideY = bounds.YMost() <= yBoundary ? BOTTOM : TOP;
-  IntRect quadrantRect = GetQuadrantRectangle(sideX, sideY);
-  NS_ASSERTION(quadrantRect.Contains(bounds), "Messed up quadrants");
-
-  if (aSetTransform) {
-    mLoanedTransform = mLoanedDrawTarget->GetTransform();
-    Matrix transform = Matrix(mLoanedTransform)
-                            .PreTranslate(-quadrantRect.x,
-                                          -quadrantRect.y);
-    mLoanedDrawTarget->SetTransform(transform);
-    mSetTransform = true;
-  } else {
-    MOZ_ASSERT(aOutMatrix);
-    *aOutMatrix = Matrix::Translation(-quadrantRect.x, -quadrantRect.y);
-    mSetTransform = false;
-  }
-
-  return mLoanedDrawTarget;
-}
-
-void
-BorrowDrawTarget::ReturnDrawTarget(gfx::DrawTarget*& aReturned)
-{
-  MOZ_ASSERT(mLoanedDrawTarget);
-  MOZ_ASSERT(aReturned == mLoanedDrawTarget);
-  if (mLoanedDrawTarget) {
-    if (mSetTransform) {
-      mLoanedDrawTarget->SetTransform(mLoanedTransform);
-    }
-    mLoanedDrawTarget = nullptr;
-  }
-  aReturned = nullptr;
-}
-
 gfxContentType
 RotatedContentBuffer::BufferContentType()
 {
   if (mBufferProvider || (mDTBuffer && mDTBuffer->IsValid())) {
     SurfaceFormat format = SurfaceFormat::B8G8R8A8;
 
     if (mBufferProvider) {
       format = mBufferProvider->GetFormat();
@@ -743,17 +777,19 @@ RotatedContentBuffer::BeginPaint(Painted
   return result;
 }
 
 RefPtr<CapturedPaintState>
 RotatedContentBuffer::BorrowDrawTargetForRecording(PaintState& aPaintState,
                                                    DrawIterator* aIter,
                                                    bool aSetTransform)
 {
-  if (aPaintState.mMode == SurfaceMode::SURFACE_NONE) {
+  if (aPaintState.mMode == SurfaceMode::SURFACE_NONE ||
+      !EnsureBuffer() ||
+      (HaveBufferOnWhite() && !EnsureBufferOnWhite())) {
     return nullptr;
   }
 
   Matrix transform;
   DrawTarget* result = BorrowDrawTargetForQuadrantUpdate(aPaintState.mRegionToDraw.GetBounds(),
                                                          BUFFER_BOTH, aIter,
                                                          aSetTransform,
                                                          &transform);
--- a/gfx/layers/RotatedBuffer.h
+++ b/gfx/layers/RotatedBuffer.h
@@ -24,76 +24,114 @@ namespace layers {
 
 class CapturedPaintState;
 
 typedef bool (*PrepDrawTargetForPaintingCallback)(CapturedPaintState*);
 
 class TextureClient;
 class PaintedLayer;
 
+// Mixin class for classes which need logic for loaning out a draw target.
+// See comments on BorrowDrawTargetForQuadrantUpdate.
+class BorrowDrawTarget
+{
+protected:
+  void ReturnDrawTarget(gfx::DrawTarget*& aReturned);
+
+  // 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
+  // upon returning the drawtarget.
+  bool mSetTransform;
+};
+
 /**
  * This is a cairo/Thebes surface, but with a literal twist. Scrolling
  * causes the layer's visible region to move. We want to keep
  * reusing the same surface if the region size hasn't changed, but we don't
  * want to keep moving the contents of the surface around in memory. So
  * we use a trick.
  * Consider just the vertical case, and suppose the buffer is H pixels
  * high and we're scrolling down by N pixels. Instead of copying the
  * buffer contents up by N pixels, we leave the buffer contents in place,
  * and paint content rows H to H+N-1 into rows 0 to N-1 of the buffer.
  * Then we can refresh the screen by painting rows N to H-1 of the buffer
  * at row 0 on the screen, and then painting rows 0 to N-1 of the buffer
  * at row H-N on the screen.
  * mBufferRotation.y would be N in this example.
  */
-class RotatedBuffer {
+class RotatedBuffer : public BorrowDrawTarget
+{
 public:
   typedef gfxContentType ContentType;
 
   RotatedBuffer(const gfx::IntRect& aBufferRect,
                 const gfx::IntPoint& aBufferRotation)
     : mBufferRect(aBufferRect)
     , mBufferRotation(aBufferRotation)
     , mDidSelfCopy(false)
   { }
   RotatedBuffer()
     : mDidSelfCopy(false)
   { }
 
+  struct DrawIterator {
+    friend class RotatedBuffer;
+    DrawIterator()
+      : mCount(0)
+    {}
+
+    nsIntRegion mDrawRegion;
+
+  private:
+    uint32_t mCount;
+  };
+
   /*
    * Which buffer should be drawn to/read from.
    */
   enum ContextSource {
     BUFFER_BLACK, // The normal buffer, or buffer with black background when using component alpha.
     BUFFER_WHITE, // The buffer with white background, only valid with component alpha.
     BUFFER_BOTH // The combined black/white buffers, only valid for writing operations, not reading.
   };
   // It is the callers repsonsibility to ensure aTarget is flushed after calling
   // this method.
   void DrawBufferWithRotation(gfx::DrawTarget* aTarget, ContextSource aSource,
                               float aOpacity = 1.0,
                               gfx::CompositionOp aOperator = gfx::CompositionOp::OP_OVER,
                               gfx::SourceSurface* aMask = nullptr,
                               const gfx::Matrix* aMaskTransform = nullptr) const;
 
+  void UpdateDestinationFrom(const RotatedBuffer& aSource,
+                             const nsIntRegion& aUpdateRegion);
+
   /**
    * |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; }
 
   virtual bool HaveBuffer() const = 0;
   virtual bool HaveBufferOnWhite() const = 0;
 
   virtual already_AddRefed<gfx::SourceSurface> GetSourceSurface(ContextSource aSource) const = 0;
 
 protected:
 
+  virtual gfx::DrawTarget* GetDTBuffer() const = 0;
+  virtual gfx::DrawTarget* GetDTBufferOnWhite() const = 0;
+
   enum XSide {
     LEFT, RIGHT
   };
   enum YSide {
     TOP, BOTTOM
   };
   gfx::IntRect GetQuadrantRectangle(XSide aXSide, YSide aYSide) const;
 
@@ -106,16 +144,37 @@ 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().
@@ -140,46 +199,30 @@ public:
     , mSourceOnWhite(aSourceOnWhite)
   { }
 
   virtual already_AddRefed<gfx::SourceSurface> GetSourceSurface(ContextSource aSource) const;
 
   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;
 };
 
-// Mixin class for classes which need logic for loaning out a draw target.
-// See comments on BorrowDrawTargetForQuadrantUpdate.
-class BorrowDrawTarget
-{
-protected:
-  void ReturnDrawTarget(gfx::DrawTarget*& aReturned);
-
-  // 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
-  // upon returning the drawtarget.
-  bool mSetTransform;
-};
-
 /**
  * This class encapsulates the buffer used to retain PaintedLayer contents,
  * i.e., the contents of the layer's GetVisibleRegion().
  */
 class RotatedContentBuffer : public RotatedBuffer
-                           , public BorrowDrawTarget
 {
 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
@@ -267,28 +310,16 @@ public:
    * 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);
 
-  struct DrawIterator {
-    friend class RotatedContentBuffer;
-    DrawIterator()
-      : mCount(0)
-    {}
-
-    nsIntRegion mDrawRegion;
-
-  private:
-    uint32_t mCount;
-  };
-
   /**
    * 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
@@ -326,24 +357,16 @@ public:
    * 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;
 
-  /**
-   * 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!
-   */
-  gfx::DrawTarget* GetDTBuffer() { return mDTBuffer; }
-  gfx::DrawTarget* GetDTBufferOnWhite() { return mDTBufferOnWhite; }
-
   virtual already_AddRefed<gfx::SourceSurface> GetSourceSurface(ContextSource aSource) const;
 
   /**
    * Complete the drawing operation. The region to draw must have been
    * drawn before this is called. The contents of the buffer are drawn
    * to aTarget.
    */
   void DrawTo(PaintedLayer* aLayer,
@@ -374,39 +397,16 @@ protected:
     MOZ_ASSERT(!aClient || !mDTBufferOnWhite || !mDTBufferOnWhite->IsValid());
 
     mBufferProviderOnWhite = aClient;
     if (!mBufferProviderOnWhite) {
       mDTBufferOnWhite = nullptr;
     }
   }
 
-  /**
-   * 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);
-
-  static bool IsClippingCheap(gfx::DrawTarget* aTarget, const nsIntRegion& aRegion);
-
 protected:
   /**
    * Return the buffer's content type.  Requires a valid buffer or
    * buffer provider.
    */
   gfxContentType BufferContentType();
   bool BufferSizeOkFor(const gfx::IntSize& aSize);
   /**
@@ -414,16 +414,24 @@ protected:
    */
   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
--- a/gfx/layers/client/ContentClient.cpp
+++ b/gfx/layers/client/ContentClient.cpp
@@ -631,16 +631,21 @@ ContentClientDoubleBuffered::FinalizeFra
 
   // 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())) {
+    return;
+  }
+
   // We need to ensure that we lock these two buffers in the same
   // order as the compositor to prevent deadlocks.
   TextureClientAutoLock frontLock(mFrontClient, OpenMode::OPEN_READ_ONLY);
   if (!frontLock.Succeeded()) {
     return;
   }
   Maybe<TextureClientAutoLock> frontOnWhiteLock;
   if (mFrontClientOnWhite) {
@@ -676,52 +681,10 @@ ContentClientDoubleBuffered::EnsureBackB
   if (!mTextureClient && mFrontClient) {
     CreateBackBuffer(mFrontBufferRect);
 
     mBufferRect = mFrontBufferRect;
     mBufferRotation = mFrontBufferRotation;
   }
 }
 
-void
-ContentClientDoubleBuffered::UpdateDestinationFrom(const RotatedBuffer& aSource,
-                                                   const nsIntRegion& aUpdateRegion)
-{
-  DrawIterator iter;
-  while (DrawTarget* destDT =
-    BorrowDrawTargetForQuadrantUpdate(aUpdateRegion.GetBounds(), BUFFER_BLACK, &iter)) {
-    bool isClippingCheap = IsClippingCheap(destDT, iter.mDrawRegion);
-    if (isClippingCheap) {
-      gfxUtils::ClipToRegion(destDT, iter.mDrawRegion);
-    }
-
-    aSource.DrawBufferWithRotation(destDT, BUFFER_BLACK, 1.0, CompositionOp::OP_SOURCE);
-    if (isClippingCheap) {
-      destDT->PopClip();
-    }
-    // Flush the destination before the sources become inaccessible (Unlock).
-    destDT->Flush();
-    ReturnDrawTargetToBuffer(destDT);
-  }
-
-  if (aSource.HaveBufferOnWhite()) {
-    MOZ_ASSERT(HaveBufferOnWhite());
-    DrawIterator whiteIter;
-    while (DrawTarget* destDT =
-      BorrowDrawTargetForQuadrantUpdate(aUpdateRegion.GetBounds(), BUFFER_WHITE, &whiteIter)) {
-      bool isClippingCheap = IsClippingCheap(destDT, whiteIter.mDrawRegion);
-      if (isClippingCheap) {
-        gfxUtils::ClipToRegion(destDT, whiteIter.mDrawRegion);
-      }
-
-      aSource.DrawBufferWithRotation(destDT, BUFFER_WHITE, 1.0, CompositionOp::OP_SOURCE);
-      if (isClippingCheap) {
-        destDT->PopClip();
-      }
-      // Flush the destination before the sources become inaccessible (Unlock).
-      destDT->Flush();
-      ReturnDrawTargetToBuffer(destDT);
-    }
-  }
-}
-
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/client/ContentClient.h
+++ b/gfx/layers/client/ContentClient.h
@@ -358,18 +358,16 @@ public:
   virtual void Dump(std::stringstream& aStream,
                     const char* aPrefix="",
                     bool aDumpHtml=false,
                     TextureDumpMode aCompress=TextureDumpMode::Compress) override;
 protected:
   virtual void DestroyFrontBuffer() override;
 
 private:
-  void UpdateDestinationFrom(const RotatedBuffer& aSource,
-                             const nsIntRegion& aUpdateRegion);
 
   virtual void AbortTextureClientCreation() override
   {
     mTextureClient = nullptr;
     mTextureClientOnWhite = nullptr;
     mFrontClient = nullptr;
     mFrontClientOnWhite = nullptr;
   }