Add a deferred copy surface

This CL forces all GrSurface copies to go through a GrSurfaceContext (rather than GrContext).

There is a bit of goofiness going on here until read/writePixels is also consolidated in GrSurfaceContext and a proxy-backed SkImage/SkSurface is added.

Change-Id: Iab1867668d8146a766201158a251b9174438ee2b
Reviewed-on: https://skia-review.googlesource.com/5773
Reviewed-by: Brian Osman <brianosman@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
diff --git a/gm/image_pict.cpp b/gm/image_pict.cpp
index 3a84905..aa28249 100644
--- a/gm/image_pict.cpp
+++ b/gm/image_pict.cpp
@@ -14,7 +14,11 @@
 
 #if SK_SUPPORT_GPU
 #include "GrContext.h"
+#include "GrContextPriv.h"
+#include "GrSurfaceContext.h"
+#include "GrSurfaceProxy.h"
 #include "GrTexture.h"
+#include "GrTextureProxy.h"
 #include "../src/image/SkImage_Gpu.h"
 #endif
 
@@ -209,15 +213,15 @@
 public:
     TextureGenerator(GrContext* ctx, const SkImageInfo& info, SkPicture* pic)
         : SkImageGenerator(info)
-        , fCtx(SkRef(ctx))
-    {
-        auto surface(SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info));
+        , fCtx(SkRef(ctx)) {
+
+        sk_sp<SkSurface> surface(SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info));
         if (surface) {
             surface->getCanvas()->clear(0);
             surface->getCanvas()->translate(-100, -100);
             surface->getCanvas()->drawPicture(pic);
             sk_sp<SkImage> image(surface->makeImageSnapshot());
-            fTexture.reset(SkRef(as_IB(image)->peekTexture()));
+            fProxy = GrSurfaceProxy::MakeWrapped(sk_ref_sp(as_IB(image)->peekTexture()));
         }
     }
 protected:
@@ -226,22 +230,41 @@
             SkASSERT(ctx == fCtx.get());
         }
 
-        if (!fTexture) {
+        if (!fProxy) {
             return nullptr;
         }
 
+        if (subset.fLeft == 0 && subset.fTop == 0 &&
+            subset.fRight == fProxy->width() && subset.fBottom == fProxy->height()) {
+            return SkSafeRef(fProxy->instantiate(fCtx->textureProvider())->asTexture());
+        }
+
         // need to copy the subset into a new texture
-        GrSurfaceDesc desc = fTexture->desc();
+        GrSurfaceDesc desc = fProxy->desc();
         desc.fWidth = subset.width();
         desc.fHeight = subset.height();
 
-        GrTexture* dst = fCtx->textureProvider()->createTexture(desc, SkBudgeted::kNo);
-        fCtx->copySurface(dst, fTexture.get(), subset, SkIPoint::Make(0, 0));
-        return dst;
+        sk_sp<GrSurfaceContext> dstContext(fCtx->contextPriv().makeDeferredSurfaceContext(
+                                                                                desc,
+                                                                                SkBudgeted::kNo));
+        if (!dstContext) {
+            return nullptr;
+        }
+
+        if (!dstContext->copy(fProxy.get(), subset, SkIPoint::Make(0, 0))) {
+            return nullptr;
+        }
+
+        GrSurface* dstSurf = dstContext->asDeferredSurface()->instantiate(fCtx->textureProvider());
+        if (!dstSurf) {
+            return nullptr;
+        }
+
+        return SkRef(dstSurf->asTexture());
     }
 private:
-    sk_sp<GrContext> fCtx;
-    sk_sp<GrTexture> fTexture;
+    sk_sp<GrContext>      fCtx;
+    sk_sp<GrSurfaceProxy> fProxy;
 };
 static SkImageGenerator* make_tex_generator(GrContext* ctx, SkPicture* pic) {
     const SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h
index 9ccda23..9e9773c 100644
--- a/include/gpu/GrContext.h
+++ b/include/gpu/GrContext.h
@@ -302,25 +302,6 @@
                             uint32_t pixelOpsFlags = 0);
 
     /**
-     * Copies a rectangle of texels from src to dst.
-     * @param dst           the surface to copy to.
-     * @param src           the surface to copy from.
-     * @param srcRect       the rectangle of the src that should be copied.
-     * @param dstPoint      the translation applied when writing the srcRect's pixels to the dst.
-     */
-    bool copySurface(GrSurface* dst,
-                     GrSurface* src,
-                     const SkIRect& srcRect,
-                     const SkIPoint& dstPoint);
-
-    /** Helper that copies the whole surface but fails when the two surfaces are not identically
-        sized. */
-    bool copySurface(GrSurface* dst, GrSurface* src) {
-        return this->copySurface(dst, src, SkIRect::MakeWH(dst->width(), dst->height()),
-                                 SkIPoint::Make(0,0));
-    }
-
-    /**
      * After this returns any pending writes to the surface will have been issued to the backend 3D API.
      */
     void flushSurfaceWrites(GrSurface* surface);
diff --git a/include/gpu/GrRenderTargetContext.h b/include/gpu/GrRenderTargetContext.h
index ac733cb..f6f8cbf 100644
--- a/include/gpu/GrRenderTargetContext.h
+++ b/include/gpu/GrRenderTargetContext.h
@@ -50,8 +50,6 @@
 public:
     ~GrRenderTargetContext() override;
 
-    bool copySurface(GrSurface* src, const SkIRect& srcRect, const SkIPoint& dstPoint) override;
-
     // TODO: it is odd that we need both the SkPaint in the following 3 methods.
     // We should extract the text parameters from SkPaint and pass them separately
     // akin to GrStyle (GrTextInfo?)
@@ -372,6 +370,7 @@
         return fRenderTargetProxy->instantiate(fContext->textureProvider());
     }
 
+    GrSurfaceProxy* asDeferredSurface() override { return fRenderTargetProxy.get(); }
     GrTextureProxy* asDeferredTexture();
 
     sk_sp<GrTexture> asTexture() {
@@ -467,6 +466,9 @@
                           const SkPath&,
                           const GrStyle&);
 
+    bool onCopy(GrSurfaceProxy* src, const SkIRect& srcRect, const SkIPoint& dstPoint) override;
+
+
     // This entry point allows the GrTextContext-derived classes to add their ops to the GrOpList.
     void addDrawOp(const GrPipelineBuilder&, const GrClip&, GrDrawOp*);
 
@@ -483,6 +485,8 @@
     sk_sp<SkColorSpace>               fColorSpace;
     sk_sp<GrColorSpaceXform>          fColorXformFromSRGB;
     SkSurfaceProps                    fSurfaceProps;
+
+    typedef GrSurfaceContext INHERITED;
 };
 
 #endif
diff --git a/include/gpu/GrSurfaceContext.h b/include/gpu/GrSurfaceContext.h
index a05d37f..e1c799e 100644
--- a/include/gpu/GrSurfaceContext.h
+++ b/include/gpu/GrSurfaceContext.h
@@ -8,6 +8,8 @@
 #ifndef GrSurfaceContext_DEFINED
 #define GrSurfaceContext_DEFINED
 
+#include "../private/GrSurfaceProxy.h"
+
 #include "SkRefCnt.h"
 
 class GrAuditTrail;
@@ -24,7 +26,30 @@
 public:
     ~GrSurfaceContext() override {}
 
-    virtual bool copySurface(GrSurface* src, const SkIRect& srcRect, const SkIPoint& dstPoint) = 0;
+    /*
+     * Copy 'src' into the proxy backing this context
+     * @param src       src of pixels
+     * @param srcRect   the subset of 'src' to copy
+     * @param dstPoint  the origin of the 'srcRect' in the destination coordinate space
+     * @return          true if the copy succeeded; false otherwise
+     *
+     * Note: Notionally, 'srcRect' is clipped to 'src's extent with 'dstPoint' being adjusted.
+     *       Then the 'srcRect' offset by 'dstPoint' is clipped against the dst's extent.
+     *       The end result is only valid src pixels and dst pixels will be touched but the copied
+     *       regions will not be shifted.
+     */
+    bool copy(GrSurfaceProxy* src, const SkIRect& srcRect, const SkIPoint& dstPoint) {
+        return this->onCopy(src, srcRect, dstPoint);
+    }
+
+    bool copy(GrSurfaceProxy* src) {
+        return this->onCopy(src,
+                            SkIRect::MakeWH(src->width(), src->height()),
+                            SkIPoint::Make(0, 0));
+    }
+
+    // TODO: this is virtual b.c. this object doesn't have a pointer to the wrapped GrSurfaceProxy?
+    virtual GrSurfaceProxy* asDeferredSurface() = 0;
 
     GrAuditTrail* auditTrail() { return fAuditTrail; }
 
@@ -38,6 +63,13 @@
 
     // In debug builds we guard against improper thread handling
     SkDEBUGCODE(mutable GrSingleOwner* fSingleOwner;)
+
+private:
+    virtual bool onCopy(GrSurfaceProxy* src,
+                        const SkIRect& srcRect,
+                        const SkIPoint& dstPoint) = 0;
+
+    typedef SkRefCnt INHERITED;
 };
 
 #endif
diff --git a/include/gpu/GrTextureContext.h b/include/gpu/GrTextureContext.h
index da71c07..3052f0c 100644
--- a/include/gpu/GrTextureContext.h
+++ b/include/gpu/GrTextureContext.h
@@ -27,7 +27,7 @@
 public:
     ~GrTextureContext() override;
 
-    bool copySurface(GrSurface* src, const SkIRect& srcRect, const SkIPoint& dstPoint) override;
+    GrSurfaceProxy* asDeferredSurface() override { return fTextureProxy.get(); }
 
 protected:
     GrTextureContext(GrContext*, GrDrawingManager*, sk_sp<GrTextureProxy>, GrAuditTrail*,
@@ -40,6 +40,8 @@
 private:
     friend class GrDrawingManager; // for ctor
 
+    bool onCopy(GrSurfaceProxy* src, const SkIRect& srcRect, const SkIPoint& dstPoint) override;
+
     GrTextureOpList* getOpList();
 
     GrDrawingManager*            fDrawingManager;
@@ -48,6 +50,8 @@
     // In MDB-mode the GrOpList can be closed by some other renderTargetContext that has picked
     // it up. For this reason, the GrOpList should only ever be accessed via 'getOpList'.
     GrTextureOpList*             fOpList;
+
+    typedef GrSurfaceContext INHERITED;
 };
 
 #endif
diff --git a/include/private/GrRenderTargetProxy.h b/include/private/GrRenderTargetProxy.h
index 83107da..7f026ba 100644
--- a/include/private/GrRenderTargetProxy.h
+++ b/include/private/GrRenderTargetProxy.h
@@ -53,6 +53,9 @@
 
     GrRenderTarget::Flags testingOnly_getFlags() const;
 
+    // TODO: move this to a priv class!
+    bool refsWrappedObjects() const;
+
 protected:
     friend class GrSurfaceProxy;  // for ctors
 
diff --git a/include/private/GrSurfaceProxy.h b/include/private/GrSurfaceProxy.h
index 731603b..12972c2 100644
--- a/include/private/GrSurfaceProxy.h
+++ b/include/private/GrSurfaceProxy.h
@@ -266,6 +266,20 @@
         return fGpuMemorySize;
     }
 
+    // Helper function that creates a temporary SurfaceContext to perform the copy
+    static sk_sp<GrSurfaceProxy> Copy(GrContext*, GrSurfaceProxy* src,
+                                      SkIRect srcRect, SkBudgeted);
+
+    // Copy the entire 'src'
+    static sk_sp<GrSurfaceProxy> Copy(GrContext* context, GrSurfaceProxy* src,
+                                      SkBudgeted budgeted) {
+        return Copy(context, src, SkIRect::MakeWH(src->width(), src->height()), budgeted);
+    }
+
+    // Test-only entry point - should decrease in use as proxies propagate
+    static sk_sp<GrSurfaceProxy> TestCopy(GrContext* context, const GrSurfaceDesc& dstDesc,
+                                          GrTexture* srcTexture, SkBudgeted budgeted);
+
     bool isWrapped_ForTesting() const;
 
     SkDEBUGCODE(void validate(GrContext*) const;)
diff --git a/src/core/SkSpecialImage.cpp b/src/core/SkSpecialImage.cpp
index 2567bf1..4f48f78 100644
--- a/src/core/SkSpecialImage.cpp
+++ b/src/core/SkSpecialImage.cpp
@@ -17,6 +17,7 @@
 
 #if SK_SUPPORT_GPU
 #include "GrContext.h"
+#include "GrSurfaceContext.h"
 #include "GrTexture.h"
 #include "GrSamplerParams.h"
 #include "GrTextureProxy.h"
@@ -346,6 +347,20 @@
 #include "GrTexture.h"
 #include "SkImage_Gpu.h"
 
+static sk_sp<SkImage> wrap_proxy_in_image(GrContext* context, GrSurfaceProxy* proxy,
+                                          SkAlphaType alphaType, sk_sp<SkColorSpace> colorSpace) {
+    // TODO: add GrTextureProxy-backed SkImage_Gpus
+    GrSurface* surf = proxy->instantiate(context->textureProvider());
+    if (!surf) {
+        return nullptr;
+    }
+
+    return sk_make_sp<SkImage_Gpu>(proxy->width(), proxy->height(),
+                                   kNeedNewImageUniqueID, alphaType,
+                                   sk_ref_sp(surf->asTexture()),
+                                   std::move(colorSpace), SkBudgeted::kYes);
+}
+
 class SkSpecialImage_Gpu : public SkSpecialImage_Base {
 public:
     SkSpecialImage_Gpu(const SkIRect& subset,
@@ -478,34 +493,21 @@
                                                    fAlphaType);
     }
 
+    // TODO: move all the logic here into the subset-flavor GrSurfaceProxy::copy?
     sk_sp<SkImage> onMakeTightSubset(const SkIRect& subset) const override {
-        // TODO: add GrTextureProxy-backed SkImage_Gpus
-        GrSurface* surf = fSurfaceProxy->instantiate(fContext->textureProvider());
-        if (!surf) {
-            return nullptr;
-        }
-
+        // TODO: this is problematic since the surfaceProxy could be loose
         if (0 == subset.fLeft && 0 == subset.fTop &&
             fSurfaceProxy->width() == subset.width() &&
             fSurfaceProxy->height() == subset.height()) {
             // The existing GrTexture is already tight so reuse it in the SkImage
-            return sk_make_sp<SkImage_Gpu>(surf->width(), surf->height(),
-                                           kNeedNewImageUniqueID, fAlphaType,
-                                           sk_ref_sp(surf->asTexture()),
-                                           fColorSpace, SkBudgeted::kYes);
+            return wrap_proxy_in_image(fContext, fSurfaceProxy.get(),
+                                       fAlphaType, fColorSpace);
         }
 
-        GrSurfaceDesc desc = fSurfaceProxy->desc();
-        desc.fWidth = subset.width();
-        desc.fHeight = subset.height();
+        sk_sp<GrSurfaceProxy> subsetProxy(GrSurfaceProxy::Copy(fContext, fSurfaceProxy.get(),
+                                                               subset, SkBudgeted::kYes));
 
-        sk_sp<GrTexture> subTx(fContext->textureProvider()->createTexture(desc, SkBudgeted::kYes));
-        if (!subTx) {
-            return nullptr;
-        }
-        fContext->copySurface(subTx.get(), surf, subset, SkIPoint::Make(0, 0));
-        return sk_make_sp<SkImage_Gpu>(desc.fWidth, desc.fHeight, kNeedNewImageUniqueID,
-                                       fAlphaType, std::move(subTx), fColorSpace, SkBudgeted::kYes);
+        return wrap_proxy_in_image(fContext, subsetProxy.get(), fAlphaType, fColorSpace);
     }
 
     sk_sp<SkSurface> onMakeTightSurface(const SkImageFilter::OutputProperties& outProps,
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index ff5b026..2e66c0c 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -541,43 +541,6 @@
     fDrawingManager->prepareSurfaceForExternalIO(surface);
 }
 
-bool GrContext::copySurface(GrSurface* dst, GrSurface* src, const SkIRect& srcRect,
-                            const SkIPoint& dstPoint) {
-    ASSERT_SINGLE_OWNER
-    RETURN_FALSE_IF_ABANDONED
-    GR_AUDIT_TRAIL_AUTO_FRAME(&fAuditTrail, "GrContext::copySurface");
-
-    if (!src || !dst) {
-        return false;
-    }
-    ASSERT_OWNED_RESOURCE(src);
-    ASSERT_OWNED_RESOURCE(dst);
-
-    // We don't allow conversion between integer configs and float/fixed configs.
-    if (GrPixelConfigIsSint(dst->config()) != GrPixelConfigIsSint(src->config())) {
-        return false;
-    }
-
-#ifndef ENABLE_MDB
-    // We can't yet fully defer copies to textures, so GrTextureContext::copySurface will
-    // execute the copy immediately. Ensure the data is ready.
-    src->flushWrites();
-#endif
-
-    sk_sp<GrSurfaceContext> surfaceContext(
-        this->contextPriv().makeWrappedSurfaceContext(sk_ref_sp(dst)));
-
-    if (!surfaceContext) {
-        return false;
-    }
-
-    if (!surfaceContext->copySurface(src, srcRect, dstPoint)) {
-        return false;
-    }
-
-    return true;
-}
-
 void GrContext::flushSurfaceWrites(GrSurface* surface) {
     ASSERT_SINGLE_OWNER
     RETURN_IF_ABANDONED
@@ -639,8 +602,36 @@
     }
 }
 
+sk_sp<GrSurfaceContext> GrContextPriv::makeDeferredSurfaceContext(const GrSurfaceDesc& dstDesc,
+                                                                  SkBudgeted isDstBudgeted) {
+
+    sk_sp<GrSurfaceProxy> proxy = GrSurfaceProxy::MakeDeferred(*fContext->caps(), dstDesc,
+                                                               SkBackingFit::kExact, isDstBudgeted);
+
+    if (proxy->asRenderTargetProxy()) {
+        return this->drawingManager()->makeRenderTargetContext(std::move(proxy), nullptr, nullptr);
+    } else {
+        SkASSERT(proxy->asTextureProxy());
+        return this->drawingManager()->makeTextureContext(std::move(proxy));
+    }
+
+    return nullptr;
+}
+
+sk_sp<GrSurfaceContext> GrContextPriv::makeTestSurfaceContext(sk_sp<GrSurfaceProxy> proxy) {
+
+    if (proxy->asRenderTargetProxy()) {
+        return this->drawingManager()->makeRenderTargetContext(std::move(proxy), nullptr, nullptr);
+    } else {
+        SkASSERT(proxy->asTextureProxy());
+        return this->drawingManager()->makeTextureContext(std::move(proxy));
+    }
+
+    return nullptr;
+}
+
 sk_sp<GrRenderTargetContext> GrContextPriv::makeBackendTextureRenderTargetContext(
-                                                                   const GrBackendTextureDesc& desc, 
+                                                                   const GrBackendTextureDesc& desc,
                                                                    sk_sp<SkColorSpace> colorSpace,
                                                                    const SkSurfaceProps* props,
                                                                    GrWrapOwnership ownership) {
diff --git a/src/gpu/GrContextPriv.h b/src/gpu/GrContextPriv.h
index dcf0807..2b2a39e 100644
--- a/src/gpu/GrContextPriv.h
+++ b/src/gpu/GrContextPriv.h
@@ -11,6 +11,8 @@
 #include "GrContext.h"
 #include "GrSurfaceContext.h"
 
+class GrSurfaceProxy;
+
 /** Class that adds methods to GrContext that are only intended for use internal to Skia.
     This class is purely a privileged window into GrContext. It should never have additional
     data members or virtual methods. */
@@ -26,6 +28,11 @@
     // Create a surfaceContext that wraps an existing texture or renderTarget
     sk_sp<GrSurfaceContext> makeWrappedSurfaceContext(sk_sp<GrSurface> tex);
 
+    sk_sp<GrSurfaceContext> makeDeferredSurfaceContext(const GrSurfaceDesc& dstDesc,
+                                                       SkBudgeted isDstBudgeted);
+
+    sk_sp<GrSurfaceContext> makeTestSurfaceContext(sk_sp<GrSurfaceProxy> proxy);
+
     sk_sp<GrRenderTargetContext> makeBackendTextureRenderTargetContext(
                                                          const GrBackendTextureDesc& desc,
                                                          sk_sp<SkColorSpace> colorSpace,
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index 76a116a..0fcd4ca 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -133,12 +133,20 @@
     return fOpList;
 }
 
-bool GrRenderTargetContext::copySurface(GrSurface* src, const SkIRect& srcRect,
-                                        const SkIPoint& dstPoint) {
+// TODO: move this (and GrTextContext::copy) to GrSurfaceContext?
+bool GrRenderTargetContext::onCopy(GrSurfaceProxy* srcProxy,
+                                   const SkIRect& srcRect,
+                                   const SkIPoint& dstPoint) {
     ASSERT_SINGLE_OWNER
     RETURN_FALSE_IF_ABANDONED
     SkDEBUGCODE(this->validate();)
-    GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrRenderTargetContext::copySurface");
+    GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrRenderTargetContext::copy");
+
+    // TODO: defer instantiation until flush time
+    sk_sp<GrSurface> src(sk_ref_sp(srcProxy->instantiate(fContext->textureProvider())));
+    if (!src) {
+        return false;
+    }
 
     // TODO: this needs to be fixed up since it ends the deferrable of the GrRenderTarget
     sk_sp<GrRenderTarget> rt(
@@ -147,7 +155,7 @@
         return false;
     }
 
-    return this->getOpList()->copySurface(rt.get(), src, srcRect, dstPoint);
+    return this->getOpList()->copySurface(rt.get(), src.get(), srcRect, dstPoint);
 }
 
 void GrRenderTargetContext::drawText(const GrClip& clip, const GrPaint& grPaint,
diff --git a/src/gpu/GrRenderTargetContextPriv.h b/src/gpu/GrRenderTargetContextPriv.h
index b3bd72a..f1548cc 100644
--- a/src/gpu/GrRenderTargetContextPriv.h
+++ b/src/gpu/GrRenderTargetContextPriv.h
@@ -114,6 +114,10 @@
                                const GrUserStencilSettings* = nullptr,
                                bool snapToCenters = false);
 
+    bool refsWrappedObjects() const {
+        return fRenderTargetContext->fRenderTargetProxy->refsWrappedObjects();
+    }
+
 private:
     explicit GrRenderTargetContextPriv(GrRenderTargetContext* renderTargetContext)
         : fRenderTargetContext(renderTargetContext) {}
diff --git a/src/gpu/GrRenderTargetProxy.cpp b/src/gpu/GrRenderTargetProxy.cpp
index 03637cf..882acf6 100644
--- a/src/gpu/GrRenderTargetProxy.cpp
+++ b/src/gpu/GrRenderTargetProxy.cpp
@@ -8,6 +8,7 @@
 #include "GrRenderTargetProxy.h"
 
 #include "GrCaps.h"
+#include "GrGpuResourcePriv.h"
 #include "GrRenderTargetOpList.h"
 #include "GrRenderTargetPriv.h"
 #include "GrTextureProvider.h"
@@ -63,3 +64,10 @@
     return GrSurface::ComputeSize(fDesc, fDesc.fSampleCnt+1, false, SkBackingFit::kApprox == fFit);
 }
 
+bool GrRenderTargetProxy::refsWrappedObjects() const {
+    if (!fTarget) {
+        return false;
+    }
+
+    return fTarget->resourcePriv().refsWrappedObjects();
+}
diff --git a/src/gpu/GrSurfaceProxy.cpp b/src/gpu/GrSurfaceProxy.cpp
index 4a0c348..24c7cfe 100644
--- a/src/gpu/GrSurfaceProxy.cpp
+++ b/src/gpu/GrSurfaceProxy.cpp
@@ -8,8 +8,11 @@
 #include "GrSurfaceProxy.h"
 
 #include "GrCaps.h"
+#include "GrContext.h"
+#include "GrContextPriv.h"
 #include "GrGpuResourcePriv.h"
 #include "GrOpList.h"
+#include "GrSurfaceContext.h"
 #include "GrTextureProvider.h"
 #include "GrTextureRenderTargetProxy.h"
 
@@ -161,3 +164,50 @@
     INHERITED::validate();
 }
 #endif
+
+sk_sp<GrSurfaceProxy> GrSurfaceProxy::Copy(GrContext* context,
+                                           GrSurfaceProxy* src,
+                                           SkIRect srcRect,
+                                           SkBudgeted budgeted) {
+    if (!srcRect.intersect(SkIRect::MakeWH(src->width(), src->height()))) {
+        return nullptr;
+    }
+
+    GrSurfaceDesc dstDesc = src->desc();
+    dstDesc.fWidth = srcRect.width();
+    dstDesc.fHeight = srcRect.height();
+
+    sk_sp<GrSurfaceContext> dstContext(context->contextPriv().makeDeferredSurfaceContext(dstDesc,
+                                                                                         budgeted));
+    if (!dstContext) {
+        return nullptr;
+    }
+
+    if (!dstContext->copy(src, srcRect, SkIPoint::Make(0, 0))) {
+        return nullptr;
+    }
+
+    return sk_ref_sp(dstContext->asDeferredSurface());
+}
+
+sk_sp<GrSurfaceProxy> GrSurfaceProxy::TestCopy(GrContext* context, const GrSurfaceDesc& dstDesc,
+                                               GrTexture* srcTexture, SkBudgeted budgeted) {
+
+    sk_sp<GrSurfaceContext> dstContext(context->contextPriv().makeDeferredSurfaceContext(
+                                                                                dstDesc,
+                                                                                budgeted));
+    if (!dstContext) {
+        return nullptr;
+    }
+
+    sk_sp<GrSurfaceProxy> srcProxy(GrSurfaceProxy::MakeWrapped(sk_ref_sp(srcTexture)));
+    if (!srcProxy) {
+        return nullptr;
+    }
+
+    if (!dstContext->copy(srcProxy.get())) {
+        return nullptr;
+    }
+
+    return sk_ref_sp(dstContext->asDeferredSurface());
+}
diff --git a/src/gpu/GrTextureContext.cpp b/src/gpu/GrTextureContext.cpp
index 22620fe..28240f8 100644
--- a/src/gpu/GrTextureContext.cpp
+++ b/src/gpu/GrTextureContext.cpp
@@ -24,8 +24,7 @@
     : GrSurfaceContext(context, auditTrail, singleOwner)
     , fDrawingManager(drawingMgr)
     , fTextureProxy(std::move(textureProxy))
-    , fOpList(SkSafeRef(fTextureProxy->getLastTextureOpList()))
-{
+    , fOpList(SkSafeRef(fTextureProxy->getLastTextureOpList())) {
     SkDEBUGCODE(this->validate();)
 }
 
@@ -56,12 +55,26 @@
     return fOpList;
 }
 
-bool GrTextureContext::copySurface(GrSurface* src, const SkIRect& srcRect,
-                                   const SkIPoint& dstPoint) {
+// TODO: move this (and GrRenderTargetContext::copy) to GrSurfaceContext?
+bool GrTextureContext::onCopy(GrSurfaceProxy* srcProxy,
+                              const SkIRect& srcRect,
+                              const SkIPoint& dstPoint) {
     ASSERT_SINGLE_OWNER
     RETURN_FALSE_IF_ABANDONED
     SkDEBUGCODE(this->validate();)
-    GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrTextureContext::copySurface");
+    GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrTextureContext::copy");
+
+    // TODO: defer instantiation until flush time
+    sk_sp<GrSurface> src(sk_ref_sp(srcProxy->instantiate(fContext->textureProvider())));
+    if (!src) {
+        return false;
+    }
+

+#ifndef ENABLE_MDB

+    // We can't yet fully defer copies to textures, so GrTextureContext::copySurface will

+    // execute the copy immediately. Ensure the data is ready.

+    src->flushWrites();

+#endif
 
     // TODO: this needs to be fixed up since it ends the deferrable of the GrTexture
     sk_sp<GrTexture> tex(sk_ref_sp(fTextureProxy->instantiate(fContext->textureProvider())));
@@ -70,7 +83,7 @@
     }
 
     GrTextureOpList* opList = this->getOpList();
-    bool result = opList->copySurface(tex.get(), src, srcRect, dstPoint);
+    bool result = opList->copySurface(tex.get(), src.get(), srcRect, dstPoint);
 
 #ifndef ENABLE_MDB
     GrOpFlushState flushState(fContext->getGpu(), nullptr);
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 3cdc34e..5738764 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -15,6 +15,7 @@
 #include "GrRenderTargetContextPriv.h"
 #include "GrStyle.h"
 #include "GrTextureAdjuster.h"
+#include "GrTextureProxy.h"
 #include "GrTracing.h"
 
 #include "SkCanvasPriv.h"
@@ -262,9 +263,7 @@
         if (fRenderTargetContext->wasAbandoned()) {
             return;
         }
-        newRTC->copySurface(fRenderTargetContext->asTexture().get(),
-                            SkIRect::MakeWH(this->width(), this->height()),
-                            SkIPoint::Make(0, 0));
+        newRTC->copy(fRenderTargetContext->asDeferredSurface());
     }
 
     fRenderTargetContext = newRTC;
@@ -1332,19 +1331,15 @@
 }
 
 sk_sp<SkSpecialImage> SkGpuDevice::snapSpecial() {
-    sk_sp<GrTexture> texture(this->accessRenderTargetContext()->asTexture());
-    if (!texture) {
+    sk_sp<GrSurfaceProxy> sProxy(sk_ref_sp(this->accessRenderTargetContext()->asDeferredTexture()));
+    if (!sProxy) {
         // When the device doesn't have a texture, we create a temporary texture.
         // TODO: we should actually only copy the portion of the source needed to apply the image
         // filter
-        texture.reset(fContext->textureProvider()->createTexture(
-            this->accessRenderTargetContext()->desc(), SkBudgeted::kYes));
-        if (!texture) {
-            return nullptr;
-        }
-
-        if (!fContext->copySurface(texture.get(),
-                                   this->accessRenderTargetContext()->accessRenderTarget())) {
+        sProxy = GrSurfaceProxy::Copy(fContext.get(),
+                                      this->accessRenderTargetContext()->asDeferredSurface(),
+                                      SkBudgeted::kYes);
+        if (!sProxy) {
             return nullptr;
         }
     }
@@ -1352,11 +1347,12 @@
     const SkImageInfo ii = this->imageInfo();
     const SkIRect srcRect = SkIRect::MakeWH(ii.width(), ii.height());
 
-    return SkSpecialImage::MakeFromGpu(srcRect,
-                                       kNeedNewImageUniqueID_SpecialImage,
-                                       std::move(texture),
-                                       sk_ref_sp(ii.colorSpace()),
-                                       &this->surfaceProps());
+    return SkSpecialImage::MakeDeferredFromGpu(fContext.get(),
+                                               srcRect,
+                                               kNeedNewImageUniqueID_SpecialImage,
+                                               sProxy,
+                                               sk_ref_sp(ii.colorSpace()),
+                                               &this->surfaceProps());
 }
 
 void SkGpuDevice::drawDevice(const SkDraw& draw, SkBaseDevice* device,
diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp
index cc11f6d..a290ce5 100644
--- a/src/image/SkImage_Gpu.cpp
+++ b/src/image/SkImage_Gpu.cpp
@@ -13,6 +13,7 @@
 #include "GrBitmapTextureMaker.h"
 #include "GrCaps.h"
 #include "GrContext.h"
+#include "GrContextPriv.h"
 #include "GrImageTextureMaker.h"
 #include "GrRenderTargetContext.h"
 #include "GrTextureAdjuster.h"
@@ -153,13 +154,31 @@
     desc.fWidth = subset.width();
     desc.fHeight = subset.height();
 
-    sk_sp<GrTexture> subTx(ctx->textureProvider()->createTexture(desc, fBudgeted));
+    sk_sp<GrSurfaceContext> sContext(ctx->contextPriv().makeDeferredSurfaceContext(desc,
+                                                                                   fBudgeted));
+    if (!sContext) {
+        return nullptr;
+    }
+
+    // TODO: make gpu images be proxy-backed to we don't need to do this
+    sk_sp<GrSurfaceProxy> tmpSrc(GrSurfaceProxy::MakeWrapped(fTexture));
+    if (!tmpSrc) {
+        return nullptr;
+    }
+
+    if (!sContext->copy(tmpSrc.get(), subset, SkIPoint::Make(0, 0))) {
+        return nullptr;
+    }
+
+    // TODO: make gpu images be proxy-backed to we don't need to do this
+    GrSurface* subTx = sContext->asDeferredSurface()->instantiate(ctx->textureProvider());
     if (!subTx) {
         return nullptr;
     }
-    ctx->copySurface(subTx.get(), fTexture.get(), subset, SkIPoint::Make(0, 0));
+
     return sk_make_sp<SkImage_Gpu>(desc.fWidth, desc.fHeight, kNeedNewImageUniqueID,
-                                   fAlphaType, std::move(subTx), fColorSpace, fBudgeted);
+                                   fAlphaType, sk_ref_sp(subTx->asTexture()),
+                                   fColorSpace, fBudgeted);
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/image/SkSurface_Gpu.cpp b/src/image/SkSurface_Gpu.cpp
index f99d3c2..55b216b 100644
--- a/src/image/SkSurface_Gpu.cpp
+++ b/src/image/SkSurface_Gpu.cpp
@@ -14,6 +14,7 @@
 #include "SkImage_Base.h"
 #include "SkImage_Gpu.h"
 #include "SkImagePriv.h"
+#include "GrRenderTargetContextPriv.h"
 #include "SkSurface_Base.h"
 
 #if SK_SUPPORT_GPU
@@ -82,29 +83,37 @@
 }
 
 sk_sp<SkImage> SkSurface_Gpu::onNewImageSnapshot(SkBudgeted budgeted, SkCopyPixelsMode cpm) {
-    GrRenderTarget* rt = fDevice->accessRenderTargetContext()->accessRenderTarget();
-    if (!rt) {
+    GrRenderTargetContext* rtc = fDevice->accessRenderTargetContext();
+    if (!rtc) {
         return nullptr;
     }
 
-    GrTexture* tex = rt->asTexture();
-    sk_sp<GrTexture> copy;
+    GrContext* ctx = fDevice->context();
+
+    GrSurfaceProxy* srcProxy = rtc->asDeferredSurface();
+    sk_sp<GrSurfaceContext> copyCtx;
     // If the original render target is a buffer originally created by the client, then we don't
     // want to ever retarget the SkSurface at another buffer we create. Force a copy now to avoid
     // copy-on-write.
-    if (kAlways_SkCopyPixelsMode == cpm || !tex || rt->resourcePriv().refsWrappedObjects()) {
-        GrSurfaceDesc desc = fDevice->accessRenderTargetContext()->desc();
-        GrContext* ctx = fDevice->context();
+    if (kAlways_SkCopyPixelsMode == cpm || !srcProxy || rtc->priv().refsWrappedObjects()) {
+        GrSurfaceDesc desc = rtc->desc();
         desc.fFlags = desc.fFlags & ~kRenderTarget_GrSurfaceFlag;
-        copy.reset(ctx->textureProvider()->createTexture(desc, budgeted));
-        if (!copy) {
+
+        copyCtx = ctx->contextPriv().makeDeferredSurfaceContext(desc, budgeted);
+        if (!copyCtx) {
             return nullptr;
         }
-        if (!ctx->copySurface(copy.get(), rt)) {
+
+        if (!copyCtx->copy(srcProxy)) {
             return nullptr;
         }
-        tex = copy.get();
+
+        srcProxy = copyCtx->asDeferredSurface();
     }
+
+    // TODO: add proxy-backed SkImage_Gpu
+    GrTexture* tex = srcProxy->instantiate(ctx->textureProvider())->asTexture();
+
     const SkImageInfo info = fDevice->imageInfo();
     sk_sp<SkImage> image;
     if (tex) {
diff --git a/tests/CopySurfaceTest.cpp b/tests/CopySurfaceTest.cpp
index 053b456..68113e7 100644
--- a/tests/CopySurfaceTest.cpp
+++ b/tests/CopySurfaceTest.cpp
@@ -10,6 +10,9 @@
 
 #if SK_SUPPORT_GPU
 #include "GrContext.h"
+#include "GrContextPriv.h"
+#include "GrSurfaceContext.h"
+#include "GrSurfaceProxy.h"
 #include "GrTexture.h"
 #include "GrTextureProvider.h"
 
@@ -68,22 +71,29 @@
                             dstDesc.fOrigin = dOrigin;
                             dstDesc.fFlags = dFlags;
 
-                            sk_sp<GrTexture> src(
-                                context->textureProvider()->createTexture(srcDesc, SkBudgeted::kNo,
-                                                                          srcPixels.get(),
-                                                                          kRowBytes));
-                            sk_sp<GrTexture> dst(
-                                context->textureProvider()->createTexture(dstDesc, SkBudgeted::kNo,
-                                                                          dstPixels.get(),
-                                                                          kRowBytes));
+                            sk_sp<GrSurfaceProxy> src(GrSurfaceProxy::MakeDeferred(
+                                                                    *context->caps(),
+                                                                    context->textureProvider(),
+                                                                    srcDesc, SkBudgeted::kNo,
+                                                                    srcPixels.get(),
+                                                                    kRowBytes));
+
+                            sk_sp<GrSurfaceProxy> dst(GrSurfaceProxy::MakeDeferred(
+                                                                    *context->caps(),
+                                                                    context->textureProvider(),
+                                                                    dstDesc, SkBudgeted::kNo,
+                                                                    dstPixels.get(),
+                                                                    kRowBytes));
                             if (!src || !dst) {
                                 ERRORF(reporter,
                                        "Could not create surfaces for copy surface test.");
                                 continue;
                             }
 
-                            bool result
-                                    = context->copySurface(dst.get(), src.get(), srcRect, dstPoint);
+                            sk_sp<GrSurfaceContext> sContext =
+                                                context->contextPriv().makeTestSurfaceContext(dst);
+
+                            bool result = sContext->copy(src.get(), srcRect, dstPoint);
 
                             bool expectedResult = true;
                             SkIPoint dstOffset = { dstPoint.fX - srcRect.fLeft,
@@ -120,9 +130,11 @@
                                 continue;
                             }
 
+                            GrSurface* dstSurf = dst->instantiate(context->textureProvider());
+
                             sk_memset32(read.get(), 0, kW * kH);
-                            if (!dst->readPixels(0, 0, kW, kH, baseDesc.fConfig, read.get(),
-                                                 kRowBytes)) {
+                            if (!dstSurf->readPixels(0, 0, kW, kH, baseDesc.fConfig, read.get(),
+                                                     kRowBytes)) {
                                 ERRORF(reporter, "Error calling readPixels");
                                 continue;
                             }
diff --git a/tests/EGLImageTest.cpp b/tests/EGLImageTest.cpp
index f78a0ea..0dcacf9 100644
--- a/tests/EGLImageTest.cpp
+++ b/tests/EGLImageTest.cpp
@@ -10,6 +10,7 @@
 #include "GrContext.h"
 #include "GrContextFactory.h"
 #include "GrShaderCaps.h"
+#include "GrSurfaceContext.h"
 #include "gl/GrGLGpu.h"
 #include "gl/GrGLUtil.h"
 #include "gl/GLTestContext.h"
@@ -40,7 +41,7 @@
 }
 
 static void test_read_pixels(skiatest::Reporter* reporter, GrContext* context,
-                             GrTexture* externalTexture, uint32_t expectedPixelValues[]) {
+                             GrSurface* externalTexture, uint32_t expectedPixelValues[]) {
     int pixelCnt = externalTexture->width() * externalTexture->height();
     SkAutoTMalloc<uint32_t> pixels(pixelCnt);
     memset(pixels.get(), 0, sizeof(uint32_t)*pixelCnt);
@@ -76,9 +77,13 @@
     copyDesc.fWidth = externalTexture->width();
     copyDesc.fHeight = externalTexture->height();
     copyDesc.fFlags = kRenderTarget_GrSurfaceFlag;
-    sk_sp<GrTexture> copy(context->textureProvider()->createTexture(copyDesc, SkBudgeted::kYes));
-    context->copySurface(copy.get(), externalTexture);
-    test_read_pixels(reporter, context, copy.get(), expectedPixelValues);
+
+    sk_sp<GrSurfaceProxy> copy(GrSurfaceProxy::TestCopy(context, copyDesc,
+                                                        externalTexture, SkBudgeted::kYes));
+
+    GrSurface* copySurf = copy->instantiate(context->textureProvider());
+
+    test_read_pixels(reporter, context, copySurf, expectedPixelValues);
 }
 
 DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(EGLImageTest, reporter, ctxInfo) {
diff --git a/tests/GrTextureStripAtlasTest.cpp b/tests/GrTextureStripAtlasTest.cpp
index 60b8a6d..86d3cfc 100644
--- a/tests/GrTextureStripAtlasTest.cpp
+++ b/tests/GrTextureStripAtlasTest.cpp
@@ -9,6 +9,7 @@
 #if SK_SUPPORT_GPU
 
 #include "GrContext.h"
+#include "GrContextPriv.h"
 #include "GrGpu.h"
 #include "GrTextureStripAtlas.h"
 #include "GrTypes.h"
@@ -21,51 +22,97 @@
     desc.fWidth = 32;
     desc.fHeight = 32;
     desc.fConfig = kRGBA_8888_GrPixelConfig;
-    GrTexture* texture = context->textureProvider()->createTexture(desc, SkBudgeted::kYes,
-                                                                   nullptr, 0);
 
-    GrSurfaceDesc targetDesc = desc;
-    targetDesc.fFlags = kRenderTarget_GrSurfaceFlag;
-    GrTexture* target = context->textureProvider()->createTexture(targetDesc, SkBudgeted::kYes,
-                                                                  nullptr, 0);
+    sk_sp<GrSurfaceProxy> srcProxy;
+    GrSurfaceProxy* srcPtr = nullptr;
 
-    SkAutoTMalloc<uint32_t> pixels(desc.fWidth * desc.fHeight);
-    memset(pixels.get(), 0xFF, sizeof(uint32_t) * desc.fWidth * desc.fHeight);
-    texture->writePixels(0, 0, desc.fWidth, desc.fHeight, kRGBA_8888_GrPixelConfig, pixels.get());
+    {
+        SkAutoTMalloc<uint32_t> pixels(desc.fWidth * desc.fHeight);
+        memset(pixels.get(), 0xFF, sizeof(uint32_t) * desc.fWidth * desc.fHeight);
 
-    // Add a pending read to the texture, and then make it available for reuse.
-    context->copySurface(target, texture);
-    texture->unref();
+        srcProxy = GrSurfaceProxy::MakeDeferred(*context->caps(), context->textureProvider(),
+                                                desc, SkBudgeted::kYes,
+                                                pixels.get(), 0);
+    }
+
+    // Add a pending read to the src texture, and then make it available for reuse.
+    sk_sp<GrSurfaceProxy> targetProxy;
+
+    {
+        GrSurfaceDesc targetDesc = desc;
+        targetDesc.fFlags = kRenderTarget_GrSurfaceFlag;
+
+        // We can't use GrSurfaceProxy::Copy bc we may be changing the dst proxy type
+        sk_sp<GrSurfaceContext> dstContext(context->contextPriv().makeDeferredSurfaceContext(
+                                                                                targetDesc,
+                                                                                SkBudgeted::kYes));
+        REPORTER_ASSERT(reporter, dstContext);
+
+        if (!dstContext->copy(srcProxy.get())) {
+            return;
+        }
+
+        targetProxy = sk_ref_sp(dstContext->asDeferredSurface());
+        srcPtr = srcProxy.release();
+    }
 
     // Create an atlas with parameters that allow it to reuse the texture.
-    GrTextureStripAtlas::Desc atlasDesc;
-    atlasDesc.fContext = context;
-    atlasDesc.fConfig = desc.fConfig;
-    atlasDesc.fWidth = desc.fWidth;
-    atlasDesc.fHeight = desc.fHeight;
-    atlasDesc.fRowHeight = 1;
-    GrTextureStripAtlas* atlas = GrTextureStripAtlas::GetAtlas(atlasDesc);
+    GrTextureStripAtlas* atlas;
+
+    {
+        GrTextureStripAtlas::Desc atlasDesc;
+        atlasDesc.fContext = context;
+        atlasDesc.fConfig = desc.fConfig;
+        atlasDesc.fWidth = desc.fWidth;
+        atlasDesc.fHeight = desc.fHeight;
+        atlasDesc.fRowHeight = 1;
+        atlas = GrTextureStripAtlas::GetAtlas(atlasDesc);
+    }
 
     // Write to the atlas' texture.
-    SkImageInfo info = SkImageInfo::MakeN32(desc.fWidth, desc.fHeight, kPremul_SkAlphaType);
-    size_t rowBytes = desc.fWidth * GrBytesPerPixel(desc.fConfig);
-    SkBitmap bitmap;
-    bitmap.allocPixels(info, rowBytes);
-    memset(bitmap.getPixels(), 1, rowBytes * desc.fHeight);
-    int row = atlas->lockRow(bitmap);
-    if (!context->caps()->preferVRAMUseOverFlushes())
-        REPORTER_ASSERT(reporter, texture == atlas->getTexture());
+    int lockedRow;
+
+    {
+        SkImageInfo info = SkImageInfo::MakeN32(desc.fWidth, desc.fHeight, kPremul_SkAlphaType);
+        size_t rowBytes = desc.fWidth * GrBytesPerPixel(desc.fConfig);
+        SkBitmap bitmap;
+        bitmap.allocPixels(info, rowBytes);
+        memset(bitmap.getPixels(), 1, rowBytes * desc.fHeight);
+        lockedRow = atlas->lockRow(bitmap);
+    }
 
     // The atlas' use of its texture shouldn't change which pixels got copied to the target.
-    SkAutoTMalloc<uint32_t> actualPixels(desc.fWidth * desc.fHeight);
-    bool success = target->readPixels(0, 0, desc.fWidth, desc.fHeight, kRGBA_8888_GrPixelConfig,
-                                      actualPixels.get());
-    REPORTER_ASSERT(reporter, success);
-    REPORTER_ASSERT(reporter,
-                    !memcmp(pixels.get(), actualPixels.get(),
-                            sizeof(uint32_t) * desc.fWidth * desc.fHeight));
-    target->unref();
-    atlas->unlockRow(row);
+    {
+        SkAutoTMalloc<uint8_t> actualPixels(sizeof(uint32_t) * desc.fWidth * desc.fHeight);
+
+        // TODO: move readPixels to GrSurfaceProxy?
+        GrSurface* surf = targetProxy->instantiate(context->textureProvider());
+
+        bool success = surf->readPixels(0, 0, desc.fWidth, desc.fHeight,
+                                        kRGBA_8888_GrPixelConfig, actualPixels.get());
+        REPORTER_ASSERT(reporter, success);
+
+        bool good = true;
+
+        const uint8_t* bytes = actualPixels.get();
+        for (size_t i = 0; i < sizeof(uint32_t) * desc.fWidth * desc.fHeight; ++i, ++bytes) {
+            if (0xFF != *bytes) {
+                good = false;
+                break;
+            }
+        }
+
+        REPORTER_ASSERT(reporter, good);
+    }
+
+    if (!context->caps()->preferVRAMUseOverFlushes()) {
+        // This is kindof dodgy since we released it!
+        GrSurface* srcSurface = srcPtr->instantiate(context->textureProvider());
+
+        REPORTER_ASSERT(reporter, srcSurface == atlas->getTexture());
+    }
+
+    atlas->unlockRow(lockedRow);
 }
 
 #endif
diff --git a/tests/IntTextureTest.cpp b/tests/IntTextureTest.cpp
index a6eaf98..5e3fde2 100644
--- a/tests/IntTextureTest.cpp
+++ b/tests/IntTextureTest.cpp
@@ -95,40 +95,48 @@
     REPORTER_ASSERT(reporter, !success);
 
     // Test that copying from one integer texture to another succeeds.
-    sk_sp<GrTexture> copy(context->textureProvider()->createTexture(desc, SkBudgeted::kYes));
-    REPORTER_ASSERT(reporter, copy);
-    if (!copy) {
-        return;
-    }
-    success = context->copySurface(copy.get(), texture.get());
-    REPORTER_ASSERT(reporter, success);
-    if (!success) {
-        return;
-    }
-    sk_bzero(readData.get(), sizeof(int32_t) * kS * kS);
-    success = texture->readPixels(0, 0, kS, kS, kRGBA_8888_sint_GrPixelConfig, readData.get());
-    REPORTER_ASSERT(reporter, success);
-    if (success) {
-        check_pixels(reporter, kS, kS, testData.get(), readData.get());
+    {
+        sk_sp<GrSurfaceProxy> copy(GrSurfaceProxy::TestCopy(context, desc,
+                                                            texture.get(), SkBudgeted::kYes));
+        REPORTER_ASSERT(reporter, copy);
+        if (!copy) {
+            return;
+        }
+
+        GrSurface* copySurface = copy->instantiate(context->textureProvider());
+        REPORTER_ASSERT(reporter, copySurface);
+        if (!copySurface) {
+            return;
+        }
+
+        sk_bzero(readData.get(), sizeof(int32_t) * kS * kS);
+        success = copySurface->readPixels(0, 0, kS, kS,
+                                          kRGBA_8888_sint_GrPixelConfig, readData.get());
+        REPORTER_ASSERT(reporter, success);
+        if (success) {
+            check_pixels(reporter, kS, kS, testData.get(), readData.get());
+        }
     }
 
-    // Test that copying to a non-integer texture fails.
-    GrSurfaceDesc nonIntDesc = desc;
-    nonIntDesc.fConfig = kRGBA_8888_GrPixelConfig;
-    copy.reset(context->textureProvider()->createTexture(nonIntDesc, SkBudgeted::kYes));
-    REPORTER_ASSERT(reporter, copy);
-    if (!copy) {
-        return;
+
+    // Test that copying to a non-integer (8888) texture fails.
+    {
+        GrSurfaceDesc nonIntDesc = desc;
+        nonIntDesc.fConfig = kRGBA_8888_GrPixelConfig;
+
+        sk_sp<GrSurfaceProxy> copy(GrSurfaceProxy::TestCopy(context, nonIntDesc,
+                                                            texture.get(), SkBudgeted::kYes));
+        REPORTER_ASSERT(reporter, !copy);
     }
-    success = context->copySurface(copy.get(), texture.get());
-    REPORTER_ASSERT(reporter, !success);
-    nonIntDesc.fConfig = kRGBA_half_GrPixelConfig;
-    copy.reset(context->textureProvider()->createTexture(nonIntDesc, SkBudgeted::kYes));
-    REPORTER_ASSERT(reporter, copy ||
-                    !context->caps()->isConfigTexturable(kRGBA_half_GrPixelConfig));
-    if (copy) {
-        success = context->copySurface(copy.get(), texture.get());
-        REPORTER_ASSERT(reporter, !success);
+
+    // Test that copying to a non-integer (RGBA_half) texture fails.
+    if (context->caps()->isConfigTexturable(kRGBA_half_GrPixelConfig)) {
+        GrSurfaceDesc nonIntDesc = desc;
+        nonIntDesc.fConfig = kRGBA_half_GrPixelConfig;
+
+        sk_sp<GrSurfaceProxy> copy(GrSurfaceProxy::TestCopy(context, nonIntDesc,
+                                                            texture.get(), SkBudgeted::kYes));
+        REPORTER_ASSERT(reporter, !copy);
     }
 
     // We overwrite the top left quarter of the texture with the bottom right quarter of the
diff --git a/tests/RectangleTextureTest.cpp b/tests/RectangleTextureTest.cpp
index cc0c4d1..5871604 100644
--- a/tests/RectangleTextureTest.cpp
+++ b/tests/RectangleTextureTest.cpp
@@ -15,7 +15,7 @@
 #include "gl/GLTestContext.h"
 
 static void test_read_pixels(skiatest::Reporter* reporter, GrContext* context,
-                             GrTexture* texture, uint32_t expectedPixelValues[]) {
+                             GrSurface* texture, uint32_t expectedPixelValues[]) {
     int pixelCnt = texture->width() * texture->height();
     SkAutoTMalloc<uint32_t> pixels(pixelCnt);
     memset(pixels.get(), 0, sizeof(uint32_t)*pixelCnt);
@@ -52,22 +52,30 @@
 }
 
 static void test_copy_surface_src(skiatest::Reporter* reporter, GrContext* context,
-                                  GrTexture* rectangleTexture, uint32_t expectedPixelValues[]) {
+                                  GrTexture* rectTexture, uint32_t expectedPixelValues[]) {
+    GrSurfaceDesc copyDstDesc;
+    copyDstDesc.fConfig = kRGBA_8888_GrPixelConfig;
+    copyDstDesc.fWidth = rectTexture->width();
+    copyDstDesc.fHeight = rectTexture->height();
+
     for (auto flags : {kNone_GrSurfaceFlags, kRenderTarget_GrSurfaceFlag}) {
-        GrSurfaceDesc copyDstDesc;
-        copyDstDesc.fConfig = kRGBA_8888_GrPixelConfig;
-        copyDstDesc.fWidth = rectangleTexture->width();
-        copyDstDesc.fHeight = rectangleTexture->height();
         copyDstDesc.fFlags = flags;
-        sk_sp<GrTexture> dst(
-                context->textureProvider()->createTexture(copyDstDesc, SkBudgeted::kYes));
-        context->copySurface(dst.get(), rectangleTexture);
-        test_read_pixels(reporter, context, dst.get(), expectedPixelValues);
+
+        sk_sp<GrSurfaceProxy> dst(GrSurfaceProxy::TestCopy(context, copyDstDesc,
+                                                           rectTexture, SkBudgeted::kYes));
+
+        GrSurface* dstSurf = dst->instantiate(context->textureProvider());
+
+        test_read_pixels(reporter, context, dstSurf, expectedPixelValues);
     }
 }
 
 static void test_copy_surface_dst(skiatest::Reporter* reporter, GrContext* context,
                                   GrTexture* rectangleTexture) {
+
+    sk_sp<GrSurfaceContext> sContext(context->contextPriv().makeWrappedSurfaceContext(
+                                                                    sk_ref_sp(rectangleTexture)));
+
     int pixelCnt = rectangleTexture->width() * rectangleTexture->height();
     SkAutoTMalloc<uint32_t> pixels(pixelCnt);
     for (int y = 0; y < rectangleTexture->width(); ++y) {
@@ -81,10 +89,13 @@
         copySrcDesc.fWidth = rectangleTexture->width();
         copySrcDesc.fHeight = rectangleTexture->height();
         copySrcDesc.fFlags = flags;
-        sk_sp<GrTexture> src(context->textureProvider()->createTexture(
-                copySrcDesc, SkBudgeted::kYes, pixels.get(), 0));
 
-        context->copySurface(rectangleTexture, src.get());
+        sk_sp<GrSurfaceProxy> src(GrSurfaceProxy::MakeDeferred(*context->caps(),
+                                                               context->textureProvider(),
+                                                               copySrcDesc,
+                                                               SkBudgeted::kYes, pixels.get(), 0));
+        sContext->copy(src.get());
+
         test_read_pixels(reporter, context, rectangleTexture, pixels.get());
     }
 }