Bug 1167235 - Part 1 - Render canvas2D into TextureClient directly. r=Bas
authorNicolas Silva <nsilva@mozilla.com>
Fri, 01 Jul 2016 10:58:06 +0200
changeset 383202 fff0d7d295e5bb6cb2eab7134bbabedc55d502d8
parent 383201 57cf7cae92f1b234b763240a33f030fd68fc2166
child 383203 53487e6b475af7829d3aa14a9d99ffe9e443774e
push id21963
push userdmitchell@mozilla.com
push dateFri, 01 Jul 2016 19:54:18 +0000
reviewersBas
bugs1167235
milestone50.0a1
Bug 1167235 - Part 1 - Render canvas2D into TextureClient directly. r=Bas
dom/canvas/CanvasRenderingContext2D.cpp
dom/canvas/CanvasRenderingContext2D.h
gfx/2d/Tools.h
gfx/layers/CopyableCanvasLayer.cpp
gfx/layers/Layers.cpp
gfx/layers/PersistentBufferProvider.cpp
gfx/layers/PersistentBufferProvider.h
gfx/layers/client/CanvasClient.cpp
gfx/layers/client/CanvasClient.h
gfx/layers/client/ClientCanvasLayer.cpp
gfx/layers/client/ClientLayerManager.cpp
gfx/layers/client/ClientLayerManager.h
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -80,16 +80,17 @@
 #include "mozilla/dom/ImageBitmap.h"
 #include "mozilla/dom/ImageData.h"
 #include "mozilla/dom/PBrowserParent.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "mozilla/dom/TypedArray.h"
 #include "mozilla/EndianUtils.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/Helpers.h"
+#include "mozilla/gfx/Tools.h"
 #include "mozilla/gfx/PathHelpers.h"
 #include "mozilla/gfx/DataSurfaceHelpers.h"
 #include "mozilla/gfx/PatternHelpers.h"
 #include "mozilla/ipc/DocumentRendererParent.h"
 #include "mozilla/ipc/PDocumentRendererParent.h"
 #include "mozilla/layers/PersistentBufferProvider.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/Preferences.h"
@@ -115,16 +116,17 @@
 #include "nsFilterInstance.h"
 #include "nsSVGLength2.h"
 #include "nsDeviceContext.h"
 #include "nsFontMetrics.h"
 #include "Units.h"
 #include "CanvasUtils.h"
 #include "mozilla/StyleSetHandle.h"
 #include "mozilla/StyleSetHandleInlines.h"
+#include "mozilla/layers/CanvasClient.h"
 
 #undef free // apparently defined by some windows header, clashing with a free()
             // method in SkTypes.h
 #include "SkiaGLGlue.h"
 #ifdef USE_SKIA
 #include "SurfaceTypes.h"
 #include "GLBlitHelper.h"
 #endif
@@ -218,20 +220,42 @@ protected:
   friend class CanvasGeneralPattern;
 
   // Beginning of linear gradient.
   Point mBegin;
   // End of linear gradient.
   Point mEnd;
 };
 
+bool
+CanvasRenderingContext2D::PatternIsOpaque(CanvasRenderingContext2D::Style aStyle) const
+{
+  const ContextState& state = CurrentState();
+  if (state.globalAlpha < 1.0) {
+    return false;
+  }
+
+  if (state.patternStyles[aStyle] && state.patternStyles[aStyle]->mSurface) {
+    return IsOpaqueFormat(state.patternStyles[aStyle]->mSurface->GetFormat());
+  }
+
+  // TODO: for gradient patterns we could check that all stops are opaque
+  // colors.
+
+  if (!state.gradientStyles[aStyle]) {
+    // it's a color pattern.
+    return Color::FromABGR(state.colorStyles[aStyle]).a >= 1.0;
+  }
+
+  return false;
+}
+
 // This class is named 'GeneralCanvasPattern' instead of just
 // 'GeneralPattern' to keep Windows PGO builds from confusing the
 // GeneralPattern class in gfxContext.cpp with this one.
-
 class CanvasGeneralPattern
 {
 public:
   typedef CanvasRenderingContext2D::Style Style;
   typedef CanvasRenderingContext2D::ContextState ContextState;
 
   Pattern& ForStyle(CanvasRenderingContext2D* aCtx,
                     Style aStyle,
@@ -1271,33 +1295,37 @@ bool CanvasRenderingContext2D::SwitchRen
     }
     mCurrentVideoSize.width = 0;
     mCurrentVideoSize.height = 0;
   }
 #endif
 
   RefPtr<SourceSurface> snapshot;
   Matrix transform;
+  RefPtr<PersistentBufferProvider> oldBufferProvider = mBufferProvider;
+  AutoReturnSnapshot autoReturn(nullptr);
 
   if (mTarget) {
     snapshot = mTarget->Snapshot();
     transform = mTarget->GetTransform();
   } else {
     MOZ_ASSERT(mBufferProvider);
     // When mBufferProvider is true but we have no mTarget, our current state's
     // transform is always valid. See ReturnTarget().
     transform = CurrentState().transform;
-    snapshot = mBufferProvider->GetSnapshot();
+    snapshot = mBufferProvider->BorrowSnapshot();
+    autoReturn.mBufferProvider = mBufferProvider;
+    autoReturn.mSnapshot = &snapshot;
   }
   mTarget = nullptr;
   mBufferProvider = nullptr;
   mResetLayer = true;
 
   // Recreate target using the new rendering mode
-  RenderingMode attemptedMode = EnsureTarget(aRenderingMode);
+  RenderingMode attemptedMode = EnsureTarget(nullptr, aRenderingMode);
   if (!IsTargetValid())
     return false;
 
   // We succeeded, so update mRenderingMode to reflect reality
   mRenderingMode = attemptedMode;
 
   // Restore the content from the old DrawTarget
   gfx::Rect r(0, 0, mWidth, mHeight);
@@ -1420,30 +1448,44 @@ CanvasRenderingContext2D::CheckSizeForSk
   double scale = gDefaultScale > 0 ? gDefaultScale : 1.0;
   int32_t threshold = ceil(scale * scale * gScreenPixels);
 
   // screen size acts as max threshold
   return threshold < 0 || (aSize.width * aSize.height) <= threshold;
 }
 
 CanvasRenderingContext2D::RenderingMode
-CanvasRenderingContext2D::EnsureTarget(RenderingMode aRenderingMode)
+CanvasRenderingContext2D::EnsureTarget(const gfx::Rect* aCoveredRect,
+                                       RenderingMode aRenderingMode)
 {
   // This would make no sense, so make sure we don't get ourselves in a mess
   MOZ_ASSERT(mRenderingMode != RenderingMode::DefaultBackendMode);
 
   RenderingMode mode = (aRenderingMode == RenderingMode::DefaultBackendMode) ? mRenderingMode : aRenderingMode;
 
   if (mTarget && mode == mRenderingMode) {
     return mRenderingMode;
   }
 
   if (mBufferProvider && mode == mRenderingMode) {
-    mTarget = mBufferProvider->GetDT(IntRect(IntPoint(), IntSize(mWidth, mHeight)));
+    gfx::Rect rect(0, 0, mWidth, mHeight);
+    if (aCoveredRect && CurrentState().transform.TransformBounds(*aCoveredRect).Contains(rect)) {
+      mTarget = mBufferProvider->BorrowDrawTarget(IntRect());
+    } else {
+      mTarget = mBufferProvider->BorrowDrawTarget(IntRect(0, 0, mWidth, mHeight));
+    }
+
     if (mTarget) {
+      // Restore clip and transform.
+      mTarget->SetTransform(CurrentState().transform);
+      for (uint32_t i = 0; i < mStyleStack.Length(); i++) {
+        for (uint32_t c = 0; c < mStyleStack[i].clipsPushed.Length(); c++) {
+          mTarget->PushClip(mStyleStack[i].clipsPushed[c]);
+        }
+      }
       return mRenderingMode;
     } else {
       mBufferProvider = nullptr;
     }
   }
 
   mIsSkiaGL = false;
 
@@ -1490,17 +1532,17 @@ CanvasRenderingContext2D::EnsureTarget(R
       }
 
       if (!mBufferProvider) {
         mBufferProvider = layerManager->CreatePersistentBufferProvider(size, format);
       }
     }
 
     if (mBufferProvider) {
-      mTarget = mBufferProvider->GetDT(IntRect(IntPoint(), IntSize(mWidth, mHeight)));
+      mTarget = mBufferProvider->BorrowDrawTarget(IntRect(IntPoint(), IntSize(mWidth, mHeight)));
     } else if (!mTarget) {
       mTarget = gfxPlatform::GetPlatform()->CreateOffscreenCanvasDrawTarget(size, format);
       mode = RenderingMode::SoftwareBackendMode;
     }
   }
 
   if (mTarget) {
     static bool registered = false;
@@ -1618,17 +1660,22 @@ CanvasRenderingContext2D::ClearTarget()
   }
 }
 
 void
 CanvasRenderingContext2D::ReturnTarget()
 {
   if (mTarget && mBufferProvider) {
     CurrentState().transform = mTarget->GetTransform();
-    mBufferProvider->ReturnAndUseDT(mTarget.forget());
+    for (uint32_t i = 0; i < mStyleStack.Length(); i++) {
+      for (uint32_t c = 0; c < mStyleStack[i].clipsPushed.Length(); c++) {
+        mTarget->PopClip();
+      }
+    }
+    mBufferProvider->ReturnDrawTarget(mTarget.forget());
   }
 }
 
 NS_IMETHODIMP
 CanvasRenderingContext2D::InitializeWithDrawTarget(nsIDocShell* aShell,
                                                    gfx::DrawTarget* aTarget)
 {
   RemovePostRefreshObserver();
@@ -2593,19 +2640,21 @@ void
 CanvasRenderingContext2D::ClearRect(double aX, double aY, double aW,
                                     double aH)
 {
   // Do not allow zeros - it's a no-op at that point per spec.
   if (!ValidateRect(aX, aY, aW, aH, false)) {
     return;
   }
 
-  EnsureTarget();
-
-  mTarget->ClearRect(gfx::Rect(aX, aY, aW, aH));
+  gfx::Rect clearRect(aX, aY, aW, aH);
+
+  EnsureTarget(&clearRect);
+
+  mTarget->ClearRect(clearRect);
 
   RedrawUser(gfxRect(aX, aY, aW, aH));
 }
 
 void
 CanvasRenderingContext2D::FillRect(double aX, double aY, double aW,
                                    double aH)
 {
@@ -2656,28 +2705,32 @@ CanvasRenderingContext2D::FillRect(doubl
         aH = patternSize.height - aY;
         if (aH < 0) {
           aH = 0;
         }
       }
     }
   }
 
+  CompositionOp op = UsedOperation();
+  bool discardContent = PatternIsOpaque(Style::FILL)
+    && (op == CompositionOp::OP_OVER || op == CompositionOp::OP_DEST_OUT);
+
+  const gfx::Rect fillRect(aX, aY, aW, aH);
+  EnsureTarget(discardContent ? &fillRect : nullptr);
+
   gfx::Rect bounds;
-
-  EnsureTarget();
   if (NeedToCalculateBounds()) {
-    bounds = gfx::Rect(aX, aY, aW, aH);
-    bounds = mTarget->GetTransform().TransformBounds(bounds);
+    bounds = mTarget->GetTransform().TransformBounds(fillRect);
   }
 
   AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
     FillRect(gfx::Rect(aX, aY, aW, aH),
              CanvasGeneralPattern().ForStyle(this, Style::FILL, mTarget),
-             DrawOptions(state.globalAlpha, UsedOperation()));
+             DrawOptions(state.globalAlpha, op));
 
   RedrawUser(gfxRect(aX, aY, aW, aH));
 }
 
 void
 CanvasRenderingContext2D::StrokeRect(double aX, double aY, double aW,
                                      double aH)
 {
@@ -5641,17 +5694,24 @@ CanvasRenderingContext2D::GetBufferProvi
   if (mBufferProvider) {
     return mBufferProvider;
   }
 
   if (!mTarget) {
     return nullptr;
   }
 
-  mBufferProvider = new PersistentBufferProviderBasic(mTarget);
+  if (aManager) {
+    mBufferProvider = aManager->CreatePersistentBufferProvider(gfx::IntSize(mWidth, mHeight),
+                                                               GetSurfaceFormat());
+  }
+
+  if (!mBufferProvider) {
+    mBufferProvider = new PersistentBufferProviderBasic(mTarget);
+  }
 
   return mBufferProvider;
 }
 
 already_AddRefed<Layer>
 CanvasRenderingContext2D::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
                                          Layer *aOldLayer,
                                          LayerManager *aManager)
--- a/dom/canvas/CanvasRenderingContext2D.h
+++ b/dom/canvas/CanvasRenderingContext2D.h
@@ -630,17 +630,18 @@ protected:
    /**
    * Create the backing surfacing, if it doesn't exist. If there is an error
    * in creating the target then it will put sErrorTarget in place. If there
    * is in turn an error in creating the sErrorTarget then they would both
    * be null so IsTargetValid() would still return null.
    *
    * Returns the actual rendering mode being used by the created target.
    */
-  RenderingMode EnsureTarget(RenderingMode aRenderMode = RenderingMode::DefaultBackendMode);
+  RenderingMode EnsureTarget(const gfx::Rect* aCoveredRect = nullptr,
+                             RenderingMode aRenderMode = RenderingMode::DefaultBackendMode);
 
   /**
    * Disposes an old target and prepares to lazily create a new target.
    */
   void ClearTarget();
 
   /*
    * Returns the target to the buffer provider. i.e. this will queue a frame for
@@ -657,16 +658,22 @@ protected:
 
   /**
     * Returns the surface format this canvas should be allocated using. Takes
     * into account mOpaque, platform requirements, etc.
     */
   mozilla::gfx::SurfaceFormat GetSurfaceFormat() const;
 
   /**
+   * Returns true if we know for sure that the pattern for a given style is opaque.
+   * Usefull to know if we can discard the content below in certain situations.
+   */
+  bool PatternIsOpaque(Style aStyle) const;
+
+  /**
    * Update CurrentState().filter with the filter description for
    * CurrentState().filterChain.
    */
   void UpdateFilter();
 
   nsLayoutUtils::SurfaceFromElementResult
     CachedSurfaceFromElement(Element* aElement);
 
--- a/gfx/2d/Tools.h
+++ b/gfx/2d/Tools.h
@@ -93,16 +93,32 @@ BytesPerPixel(SurfaceFormat aFormat)
     return 3 * sizeof(float);
   case SurfaceFormat::Depth:
     return sizeof(uint16_t);
   default:
     return 4;
   }
 }
 
+static inline bool
+IsOpaqueFormat(SurfaceFormat aFormat) {
+  switch (aFormat) {
+    case SurfaceFormat::B8G8R8X8:
+    case SurfaceFormat::R8G8B8X8:
+    case SurfaceFormat::X8R8G8B8:
+    case SurfaceFormat::YUV:
+    case SurfaceFormat::NV12:
+    case SurfaceFormat::YUV422:
+    case SurfaceFormat::R5G6B5_UINT16:
+      return true;
+    default:
+      return false;
+  }
+}
+
 template<typename T, int alignment = 16>
 struct AlignedArray
 {
   typedef T value_type;
 
   AlignedArray()
     : mPtr(nullptr)
     , mStorage(nullptr)
--- a/gfx/layers/CopyableCanvasLayer.cpp
+++ b/gfx/layers/CopyableCanvasLayer.cpp
@@ -80,30 +80,42 @@ bool
 CopyableCanvasLayer::IsDataValid(const Data& aData)
 {
   return mGLContext == aData.mGLContext;
 }
 
 void
 CopyableCanvasLayer::UpdateTarget(DrawTarget* aDestTarget)
 {
+  AutoReturnSnapshot autoReturn;
+
   if (mAsyncRenderer) {
     mSurface = mAsyncRenderer->GetSurface();
   } else if (!mGLFrontbuffer && mBufferProvider) {
-    mSurface = mBufferProvider->GetSnapshot();
+    mSurface = mBufferProvider->BorrowSnapshot();
+    if (aDestTarget) {
+      // If !aDestTarget we'll end up painting using mSurface later,
+      // so we can't return it to the provider (note that this will trigger a
+      // copy of the snapshot behind the scenes when the provider is unlocked).
+      autoReturn.mSnapshot = &mSurface;
+    }
+    // Either way we need to call ReturnSnapshot because ther may be an
+    // underlying TextureClient that has to be unlocked.
+    autoReturn.mBufferProvider = mBufferProvider;
   }
 
   if (!mGLContext && aDestTarget) {
     NS_ASSERTION(mSurface, "Must have surface to draw!");
     if (mSurface) {
       aDestTarget->CopySurface(mSurface,
                                IntRect(0, 0, mBounds.width, mBounds.height),
                                IntPoint(0, 0));
       mSurface = nullptr;
     }
+
     return;
   }
 
   if ((!mGLFrontbuffer && mBufferProvider) || mAsyncRenderer) {
     return;
   }
 
   MOZ_ASSERT(mGLContext);
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -172,27 +172,22 @@ LayerManager::CreateDrawTarget(const Int
     CreateOffscreenCanvasDrawTarget(aSize, aFormat);
 }
 
 already_AddRefed<PersistentBufferProvider>
 LayerManager::CreatePersistentBufferProvider(const mozilla::gfx::IntSize &aSize,
                                              mozilla::gfx::SurfaceFormat aFormat)
 {
   RefPtr<PersistentBufferProviderBasic> bufferProvider =
-    new PersistentBufferProviderBasic(aSize, aFormat,
-                                      gfxPlatform::GetPlatform()->GetPreferredCanvasBackend());
+    PersistentBufferProviderBasic::Create(aSize, aFormat,
+      gfxPlatform::GetPlatform()->GetPreferredCanvasBackend());
 
-  if (!bufferProvider->IsValid()) {
-    bufferProvider =
-      new PersistentBufferProviderBasic(aSize, aFormat,
-                                        gfxPlatform::GetPlatform()->GetFallbackCanvasBackend());
-  }
-
-  if (!bufferProvider->IsValid()) {
-    return nullptr;
+  if (!bufferProvider) {
+    bufferProvider = PersistentBufferProviderBasic::Create(aSize, aFormat,
+      gfxPlatform::GetPlatform()->GetFallbackCanvasBackend());
   }
 
   return bufferProvider.forget();
 }
 
 #ifdef DEBUG
 void
 LayerManager::Mutated(Layer* aLayer)
--- a/gfx/layers/PersistentBufferProvider.cpp
+++ b/gfx/layers/PersistentBufferProvider.cpp
@@ -11,16 +11,221 @@
 #include "gfxPlatform.h"
 
 namespace mozilla {
 
 using namespace gfx;
 
 namespace layers {
 
-PersistentBufferProviderBasic::PersistentBufferProviderBasic(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
-                                                             gfx::BackendType aBackend)
+PersistentBufferProviderBasic::PersistentBufferProviderBasic(DrawTarget* aDt)
+: mDrawTarget(aDt)
+{
+  MOZ_COUNT_CTOR(PersistentBufferProviderBasic);
+}
+
+PersistentBufferProviderBasic::~PersistentBufferProviderBasic()
+{
+  MOZ_COUNT_DTOR(PersistentBufferProviderBasic);
+}
+
+already_AddRefed<gfx::DrawTarget>
+PersistentBufferProviderBasic::BorrowDrawTarget(const gfx::IntRect& aPersistedRect)
+{
+  MOZ_ASSERT(!mSnapshot);
+  RefPtr<gfx::DrawTarget> dt(mDrawTarget);
+  return dt.forget();
+}
+
+bool
+PersistentBufferProviderBasic::ReturnDrawTarget(already_AddRefed<gfx::DrawTarget> aDT)
+{
+  RefPtr<gfx::DrawTarget> dt(aDT);
+  MOZ_ASSERT(mDrawTarget == dt);
+  return true;
+}
+
+already_AddRefed<gfx::SourceSurface>
+PersistentBufferProviderBasic::BorrowSnapshot()
+{
+  mSnapshot = mDrawTarget->Snapshot();
+  RefPtr<SourceSurface> snapshot = mSnapshot;
+  return snapshot.forget();
+}
+
+void
+PersistentBufferProviderBasic::ReturnSnapshot(already_AddRefed<gfx::SourceSurface> aSnapshot)
+{
+  RefPtr<SourceSurface> snapshot = aSnapshot;
+  MOZ_ASSERT(!snapshot || snapshot == mSnapshot);
+  mSnapshot = nullptr;
+}
+
+//static
+already_AddRefed<PersistentBufferProviderBasic>
+PersistentBufferProviderBasic::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+                                      gfx::BackendType aBackend)
+{
+  RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()->CreateDrawTargetForBackend(aBackend, aSize, aFormat);
+
+  if (!dt) {
+    return nullptr;
+  }
+
+  RefPtr<PersistentBufferProviderBasic> provider =
+    new PersistentBufferProviderBasic(dt);
+
+  return provider.forget();
+}
+
+
+//static
+already_AddRefed<PersistentBufferProviderShared>
+PersistentBufferProviderShared::Create(gfx::IntSize aSize,
+                                       gfx::SurfaceFormat aFormat,
+                                       CompositableForwarder* aFwd)
+{
+  if (!aFwd || !aFwd->IPCOpen()) {
+    return nullptr;
+  }
+
+  RefPtr<TextureClient> texture = TextureClient::CreateForDrawing(
+    aFwd, aFormat, aSize,
+    BackendSelector::Canvas,
+    TextureFlags::IMMEDIATE_UPLOAD,
+    TextureAllocationFlags::ALLOC_DEFAULT
+  );
+
+  if (!texture) {
+    return nullptr;
+  }
+
+  RefPtr<PersistentBufferProviderShared> provider =
+    new PersistentBufferProviderShared(aSize, aFormat, aFwd, texture);
+  return provider.forget();
+}
+
+PersistentBufferProviderShared::PersistentBufferProviderShared(gfx::IntSize aSize,
+                                                               gfx::SurfaceFormat aFormat,
+                                                               CompositableForwarder* aFwd,
+                                                               RefPtr<TextureClient>& aTexture)
+
+: mSize(aSize)
+, mFormat(aFormat)
+, mFwd(aFwd)
+, mBack(aTexture.forget())
+{
+  MOZ_COUNT_CTOR(PersistentBufferProviderShared);
+}
+
+PersistentBufferProviderShared::~PersistentBufferProviderShared()
 {
-  mDrawTarget = gfxPlatform::GetPlatform()->CreateDrawTargetForBackend(aBackend, aSize, aFormat);
+  MOZ_COUNT_DTOR(PersistentBufferProviderShared);
+
+  mDrawTarget = nullptr;
+  if (mBack && mBack->IsLocked()) {
+    mBack->Unlock();
+  }
+  if (mFront && mFront->IsLocked()) {
+    mFront->Unlock();
+  }
+}
+
+already_AddRefed<gfx::DrawTarget>
+PersistentBufferProviderShared::BorrowDrawTarget(const gfx::IntRect& aPersistedRect)
+{
+  if (!mFwd->IPCOpen()) {
+    return nullptr;
+  }
+
+  if (!mDrawTarget) {
+    if (!mBack) {
+      mBack = TextureClient::CreateForDrawing(
+        mFwd, mFormat, mSize,
+        BackendSelector::Canvas,
+        TextureFlags::IMMEDIATE_UPLOAD,
+        TextureAllocationFlags::ALLOC_DEFAULT
+      );
+    }
+
+    if (!mBack) {
+      return nullptr;
+    }
+
+    if (!mBack->Lock(OpenMode::OPEN_READ_WRITE)) {
+      return nullptr;
+    }
+    if (!aPersistedRect.IsEmpty() && mFront
+        && mFront->Lock(OpenMode::OPEN_READ)) {
+
+      DebugOnly<bool> success = mFront->CopyToTextureClient(mBack, &aPersistedRect, nullptr);
+      MOZ_ASSERT(success);
+
+      mFront->Unlock();
+    }
+
+    mDrawTarget = mBack->BorrowDrawTarget();
+    if (!mDrawTarget) {
+      return nullptr;
+    }
+  }
+
+  RefPtr<gfx::DrawTarget> dt(mDrawTarget);
+  return dt.forget();
+}
+
+bool
+PersistentBufferProviderShared::ReturnDrawTarget(already_AddRefed<gfx::DrawTarget> aDT)
+{
+  RefPtr<gfx::DrawTarget> dt(aDT);
+  MOZ_ASSERT(mDrawTarget == dt);
+
+  mDrawTarget = nullptr;
+  dt = nullptr;
+
+  mBack->Unlock();
+
+  mFront = mBack;
+  mBack = nullptr;
+
+  return true;
+}
+
+already_AddRefed<gfx::SourceSurface>
+PersistentBufferProviderShared::BorrowSnapshot()
+{
+  if (!mFront || mFront->IsLocked()) {
+    MOZ_ASSERT(false);
+    return nullptr;
+  }
+
+  if (!mFront->Lock(OpenMode::OPEN_READ)) {
+    return nullptr;
+  }
+
+  mDrawTarget = mFront->BorrowDrawTarget();
+
+  if (!mDrawTarget) {
+    mFront->Unlock();
+  }
+
+  mSnapshot = mDrawTarget->Snapshot();
+
+  RefPtr<SourceSurface> snapshot = mSnapshot;
+  return snapshot.forget();
+}
+
+void
+PersistentBufferProviderShared::ReturnSnapshot(already_AddRefed<gfx::SourceSurface> aSnapshot)
+{
+  RefPtr<SourceSurface> snapshot = aSnapshot;
+  MOZ_ASSERT(!snapshot || snapshot == mSnapshot);
+
+  mSnapshot = nullptr;
+  snapshot = nullptr;
+
+  mDrawTarget = nullptr;
+
+  mFront->Unlock();
 }
 
 } // namespace layers
 } // namespace mozilla
\ No newline at end of file
--- a/gfx/layers/PersistentBufferProvider.h
+++ b/gfx/layers/PersistentBufferProvider.h
@@ -8,16 +8,22 @@
 
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/RefPtr.h"             // for RefPtr, already_AddRefed, etc
 #include "mozilla/layers/LayersTypes.h"
 #include "mozilla/layers/CompositableForwarder.h"
 #include "mozilla/gfx/Types.h"
 
 namespace mozilla {
+
+namespace gfx {
+  class SourceSurface;
+  class DrawTarget;
+}
+
 namespace layers {
 
 class CopyableCanvasLayer;
 
 /**
  * A PersistentBufferProvider is for users which require the temporary use of
  * a DrawTarget to draw into. When they're done drawing they return the
  * DrawTarget, when they later need to continue drawing they get a DrawTarget
@@ -33,51 +39,120 @@ public:
 
   virtual LayersBackend GetType() { return LayersBackend::LAYERS_NONE; }
 
   /**
    * Get a DrawTarget from the PersistentBufferProvider.
    *
    * \param aPersistedRect This indicates the area of the DrawTarget that needs
    *                       to have remained the same since the call to
-   *                       ReturnAndUseDT.
+   *                       ReturnDrawTarget.
    */
-  virtual already_AddRefed<gfx::DrawTarget> GetDT(const gfx::IntRect& aPersistedRect) = 0;
+  virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget(const gfx::IntRect& aPersistedRect) = 0;
+
   /**
    * Return a DrawTarget to the PersistentBufferProvider and indicate the
    * contents of this DrawTarget is to be considered current by the
    * BufferProvider. The caller should forget any references to the DrawTarget.
    */
-  virtual bool ReturnAndUseDT(already_AddRefed<gfx::DrawTarget> aDT) = 0;
+  virtual bool ReturnDrawTarget(already_AddRefed<gfx::DrawTarget> aDT) = 0;
+
+  virtual already_AddRefed<gfx::SourceSurface> BorrowSnapshot() = 0;
 
-  virtual already_AddRefed<gfx::SourceSurface> GetSnapshot() = 0;
-protected:
+  virtual void ReturnSnapshot(already_AddRefed<gfx::SourceSurface> aSnapshot) = 0;
+
+  virtual TextureClient* GetTextureClient() { return nullptr; }
 };
 
+
 class PersistentBufferProviderBasic : public PersistentBufferProvider
 {
 public:
-  MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PersistentBufferProviderBasic)
+  MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PersistentBufferProviderBasic, override)
+
+  static already_AddRefed<PersistentBufferProviderBasic>
+  Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat, gfx::BackendType aBackend);
+
+  explicit PersistentBufferProviderBasic(gfx::DrawTarget* aTarget);
+
+  virtual LayersBackend GetType() override { return LayersBackend::LAYERS_BASIC; }
+
+  virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget(const gfx::IntRect& aPersistedRect) override;
+
+  virtual bool ReturnDrawTarget(already_AddRefed<gfx::DrawTarget> aDT) override;
+
+  virtual already_AddRefed<gfx::SourceSurface> BorrowSnapshot() override;
+
+  virtual void ReturnSnapshot(already_AddRefed<gfx::SourceSurface> aSnapshot) override;
+
+private:
+  ~PersistentBufferProviderBasic();
 
-  PersistentBufferProviderBasic(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
-                                gfx::BackendType aBackend);
-  explicit PersistentBufferProviderBasic(gfx::DrawTarget* aTarget) : mDrawTarget(aTarget) {}
+  RefPtr<gfx::DrawTarget> mDrawTarget;
+  RefPtr<gfx::SourceSurface> mSnapshot;
+};
+
+
+/**
+ * Provides access to a buffer which can be sent to the compositor without
+ * requiring a copy.
+ */
+class PersistentBufferProviderShared : public PersistentBufferProvider
+{
+public:
+  MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PersistentBufferProviderShared, override)
+
+  static already_AddRefed<PersistentBufferProviderShared>
+  Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+         CompositableForwarder* aFwd);
+
+  virtual LayersBackend GetType() override { return LayersBackend::LAYERS_CLIENT; }
+
+  virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget(const gfx::IntRect& aPersistedRect) override;
 
-  bool IsValid() { return !!mDrawTarget; }
-  virtual LayersBackend GetType() { return LayersBackend::LAYERS_BASIC; }
-  already_AddRefed<gfx::DrawTarget> GetDT(const gfx::IntRect& aPersistedRect) {
-    RefPtr<gfx::DrawTarget> dt(mDrawTarget);
-    return dt.forget();
+  virtual bool ReturnDrawTarget(already_AddRefed<gfx::DrawTarget> aDT) override;
+
+  virtual already_AddRefed<gfx::SourceSurface> BorrowSnapshot() override;
+
+  virtual void ReturnSnapshot(already_AddRefed<gfx::SourceSurface> aSnapshot) override;
+
+  TextureClient* GetTextureClient() override {
+    return mFront;
   }
-  bool ReturnAndUseDT(already_AddRefed<gfx::DrawTarget> aDT) {
-    RefPtr<gfx::DrawTarget> dt(aDT);
-    MOZ_ASSERT(mDrawTarget == dt);
-    return true;
+
+protected:
+  PersistentBufferProviderShared(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+                                 CompositableForwarder* aFwd,
+                                 RefPtr<TextureClient>& aTexture);
+
+  ~PersistentBufferProviderShared();
+
+  gfx::IntSize mSize;
+  gfx::SurfaceFormat mFormat;
+  RefPtr<CompositableForwarder> mFwd;
+  RefPtr<TextureClient> mFront;
+  RefPtr<TextureClient> mBack;
+  RefPtr<gfx::DrawTarget> mDrawTarget;
+  RefPtr<gfx::SourceSurface > mSnapshot;
+};
+
+struct AutoReturnSnapshot
+{
+  PersistentBufferProvider* mBufferProvider;
+  RefPtr<gfx::SourceSurface>* mSnapshot;
+
+  explicit AutoReturnSnapshot(PersistentBufferProvider* aProvider = nullptr)
+  : mBufferProvider(aProvider)
+  , mSnapshot(nullptr)
+  {}
+
+  ~AutoReturnSnapshot()
+  {
+    if (mBufferProvider) {
+      mBufferProvider->ReturnSnapshot(mSnapshot ? mSnapshot->forget() : nullptr);
+    }
   }
-  virtual already_AddRefed<gfx::SourceSurface> GetSnapshot() { return mDrawTarget->Snapshot(); }
-private:
-  RefPtr<gfx::DrawTarget> mDrawTarget;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif
--- a/gfx/layers/client/CanvasClient.cpp
+++ b/gfx/layers/client/CanvasClient.cpp
@@ -62,16 +62,40 @@ CanvasClientBridge::UpdateAsync(AsyncCan
   }
 
   static_cast<ShadowLayerForwarder*>(GetForwarder())
     ->AttachAsyncCompositable(asyncID, mLayer);
   mAsyncID = asyncID;
 }
 
 void
+CanvasClient2D::UpdateFromTexture(TextureClient* aTexture)
+{
+  MOZ_ASSERT(aTexture);
+
+  if (!aTexture->IsSharedWithCompositor()) {
+    if (!AddTextureClient(aTexture)) {
+      return;
+    }
+  }
+
+  mBackBuffer = aTexture;
+
+  AutoTArray<CompositableForwarder::TimedTextureClient,1> textures;
+  CompositableForwarder::TimedTextureClient* t = textures.AppendElement();
+  t->mTextureClient = mBackBuffer;
+  t->mPictureRect = nsIntRect(nsIntPoint(0, 0), aTexture->GetSize());
+  t->mFrameID = mFrameID;
+  t->mInputFrameID = VRManagerChild::Get()->GetInputFrameID();
+
+  GetForwarder()->UseTextures(this, textures);
+  aTexture->SyncWithObject(GetForwarder()->GetSyncObject());
+}
+
+void
 CanvasClient2D::Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer)
 {
   AutoRemoveTexture autoRemove(this);
   if (mBackBuffer &&
       (mBackBuffer->IsImmutable() || mBackBuffer->GetSize() != aSize)) {
     autoRemove.mTexture = mBackBuffer;
     mBackBuffer = nullptr;
   }
--- a/gfx/layers/client/CanvasClient.h
+++ b/gfx/layers/client/CanvasClient.h
@@ -8,16 +8,17 @@
 
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/Attributes.h"         // for override
 #include "mozilla/RefPtr.h"             // for RefPtr, already_AddRefed
 #include "mozilla/layers/CompositableClient.h"  // for CompositableClient
 #include "mozilla/layers/CompositorTypes.h"  // for TextureInfo, etc
 #include "mozilla/layers/LayersSurfaces.h"  // for SurfaceDescriptor
 #include "mozilla/layers/TextureClient.h"  // for TextureClient, etc
+#include "mozilla/layers/PersistentBufferProvider.h"
 
 // Fix X11 header brain damage that conflicts with MaybeOneOf::None
 #undef None
 #include "mozilla/MaybeOneOf.h"
 
 #include "mozilla/mozalloc.h"           // for operator delete
 
 #include "mozilla/gfx/Point.h"          // for IntSize
@@ -71,16 +72,18 @@ public:
   virtual bool AddTextureClient(TextureClient* aTexture) override
   {
     ++mFrameID;
     return CompositableClient::AddTextureClient(aTexture);
   }
 
   virtual void UpdateAsync(AsyncCanvasRenderer* aRenderer) {}
 
+  virtual void UpdateFromTexture(TextureClient* aTexture) {}
+
   virtual void Updated() { }
 
 protected:
   int32_t mFrameID;
 };
 
 // Used for 2D canvases and WebGL canvas on non-GL systems where readback is requried.
 class CanvasClient2D : public CanvasClient
@@ -99,16 +102,18 @@ public:
 
   virtual void Clear() override
   {
     mBackBuffer = mFrontBuffer = nullptr;
   }
 
   virtual void Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer) override;
 
+  virtual void UpdateFromTexture(TextureClient* aBuffer) override;
+
   virtual bool AddTextureClient(TextureClient* aTexture) override
   {
     MOZ_ASSERT((mTextureFlags & aTexture->GetFlags()) == mTextureFlags);
     return CanvasClient::AddTextureClient(aTexture);
   }
 
   virtual void OnDetach() override
   {
--- a/gfx/layers/client/ClientCanvasLayer.cpp
+++ b/gfx/layers/client/ClientCanvasLayer.cpp
@@ -125,17 +125,21 @@ ClientCanvasLayer::RenderLayer()
   }
 
   if (!IsDirty()) {
     return;
   }
   Painted();
 
   FirePreTransactionCallback();
-  mCanvasClient->Update(gfx::IntSize(mBounds.width, mBounds.height), this);
+  if (mBufferProvider && mBufferProvider->GetTextureClient()) {
+    mCanvasClient->UpdateFromTexture(mBufferProvider->GetTextureClient());
+  } else {
+    mCanvasClient->Update(gfx::IntSize(mBounds.width, mBounds.height), this);
+  }
 
   FireDidTransactionCallback();
 
   ClientManager()->Hold(this);
   mCanvasClient->Updated();
 }
 
 CanvasClient::CanvasClientType
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -843,16 +843,25 @@ ClientLayerManager::RemoveDidCompositeOb
 }
 
 bool
 ClientLayerManager::DependsOnStaleDevice() const
 {
   return gfxPlatform::GetPlatform()->GetDeviceCounter() != mDeviceCounter;
 }
 
+
+already_AddRefed<PersistentBufferProvider>
+ClientLayerManager::CreatePersistentBufferProvider(const gfx::IntSize& aSize,
+                                                   gfx::SurfaceFormat aFormat)
+{
+  return PersistentBufferProviderShared::Create(aSize, aFormat, AsShadowForwarder());
+}
+
+
 ClientLayer::~ClientLayer()
 {
   if (HasShadow()) {
     PLayerChild::Send__delete__(GetShadow());
   }
   MOZ_COUNT_DTOR(ClientLayer);
 }
 
--- a/gfx/layers/client/ClientLayerManager.h
+++ b/gfx/layers/client/ClientLayerManager.h
@@ -241,16 +241,19 @@ public:
   class DidCompositeObserver {
   public:
     virtual void DidComposite() = 0;
   };
 
   void AddDidCompositeObserver(DidCompositeObserver* aObserver);
   void RemoveDidCompositeObserver(DidCompositeObserver* aObserver);
 
+  virtual already_AddRefed<PersistentBufferProvider>
+  CreatePersistentBufferProvider(const gfx::IntSize& aSize, gfx::SurfaceFormat aFormat) override;
+
 protected:
   enum TransactionPhase {
     PHASE_NONE, PHASE_CONSTRUCTION, PHASE_DRAWING, PHASE_FORWARD
   };
   TransactionPhase mPhase;
 
 private:
   // Listen memory-pressure event for ClientLayerManager