Reland "Reland "Remove support for copyAsDraw in gpu copySurface.""

This reverts commit 4c6f9b767034c6812d868108516c2580dce3cb56.

Reason for revert: Landing with neuxs 7 and androind one fixes

Original change's description:
> Revert "Reland "Remove support for copyAsDraw in gpu copySurface.""
>
> This reverts commit 84ea04949cabc87a88d06c5c6f6aeb944a745911.
>
> Reason for revert: nexus 7 and android one broken
>
> Original change's description:
> > Reland "Remove support for copyAsDraw in gpu copySurface."
> >
> > This reverts commit c5167c053bd58e6afbad83fe493c0231df3f9704.
> >
> > Reason for revert: fixed
> >
> > Original change's description:
> > > Revert "Remove support for copyAsDraw in gpu copySurface."
> > >
> > > This reverts commit 6565506463db042d3d543a1707f473cdf1ef4e9e.
> > >
> > > Reason for revert: seems to break things?
> > >
> > > Original change's description:
> > > > Remove support for copyAsDraw in gpu copySurface.
> > > >
> > > > The major changes on a higher lever are:
> > > > 1) The majority of all copies now go through GrSurfaceProxy::Copy which
> > > > takes in a proxy and returns a new one with the data copied to it. This
> > > > is the most common use case within Ganesh.
> > > >
> > > > 2) The backend copy calls no longer do draws, require origins to be the
> > > > same, and won't do any swizzling or adjustment of subrects. They are
> > > > all implemented to be dumb copy this data to this other spot.
> > > >
> > > > 3) The GrSurfaceContext copy call has now been moved to priv and renamed
> > > > copyNoDraw, and a new priv copyAsDraw was added to GrRenderTargetContext.
> > > >
> > > > 4) WritePixels and ReplaceRenderTarget both need to specifiy the destination
> > > > of copies. They are the only users (besides the GrSurfaceProxy::Copy) which
> > > > call the priv methods on GrSurfaceContext.
> > > >
> > > > Change-Id: Iaf1eb3a73ccaf39a75af77e281dae594f809186f
> > > > Reviewed-on: https://skia-review.googlesource.com/c/skia/+/217459
> > > > Reviewed-by: Brian Salomon <bsalomon@google.com>
> > > > Commit-Queue: Greg Daniel <egdaniel@google.com>
> > >
> > > TBR=egdaniel@google.com,bsalomon@google.com,robertphillips@google.com
> > >
> > > Change-Id: Id43aa8aa1451e794342e930441d9975b90e6b59f
> > > No-Presubmit: true
> > > No-Tree-Checks: true
> > > No-Try: true
> > > Reviewed-on: https://skia-review.googlesource.com/c/skia/+/218549
> > > Reviewed-by: Greg Daniel <egdaniel@google.com>
> > > Commit-Queue: Greg Daniel <egdaniel@google.com>
> >
> > TBR=egdaniel@google.com,bsalomon@google.com,robertphillips@google.com
> >
> > Change-Id: I1a96f85ae2ff7622a6b57406755d478e7fbcf56e
> > No-Presubmit: true
> > No-Tree-Checks: true
> > Reviewed-on: https://skia-review.googlesource.com/c/skia/+/218797
> > Reviewed-by: Brian Salomon <bsalomon@google.com>
> > Commit-Queue: Greg Daniel <egdaniel@google.com>
>
> TBR=egdaniel@google.com,bsalomon@google.com,robertphillips@google.com
>
> Change-Id: I310930a9df30535f45a065263a40239141e15562
> No-Presubmit: true
> No-Tree-Checks: true
> No-Try: true
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/219384
> Reviewed-by: Greg Daniel <egdaniel@google.com>
> Commit-Queue: Greg Daniel <egdaniel@google.com>

TBR=egdaniel@google.com,bsalomon@google.com,robertphillips@google.com

Change-Id: I88df4f19aa26ed77b5af4e25d138387cbabd1934
No-Presubmit: true
No-Tree-Checks: true
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/219386
Commit-Queue: Greg Daniel <egdaniel@google.com>
Reviewed-by: Greg Daniel <egdaniel@google.com>
diff --git a/gm/image_pict.cpp b/gm/image_pict.cpp
index 1d8f469..c47fb28 100644
--- a/gm/image_pict.cpp
+++ b/gm/image_pict.cpp
@@ -196,29 +196,11 @@
             return fProxy;
         }
 
-        // need to copy the subset into a new texture
-        GrSurfaceDesc desc;
-        desc.fWidth = info.width();
-        desc.fHeight = info.height();
-        desc.fConfig = fProxy->config();
-
         GrMipMapped mipMapped = willBeMipped ? GrMipMapped::kYes : GrMipMapped::kNo;
 
-        sk_sp<GrSurfaceContext> dstContext(fCtx->priv().makeDeferredSurfaceContext(
-                fProxy->backendFormat(), desc, fProxy->origin(), mipMapped, SkBackingFit::kExact,
-                SkBudgeted::kYes));
-        if (!dstContext) {
-            return nullptr;
-        }
-
-        if (!dstContext->copy(
-                            fProxy.get(),
-                            SkIRect::MakeXYWH(origin.x(), origin.y(), info.width(), info.height()),
-                            SkIPoint::Make(0, 0))) {
-            return nullptr;
-        }
-
-        return dstContext->asTextureProxyRef();
+        return GrSurfaceProxy::Copy(fCtx.get(), fProxy.get(), mipMapped,
+                SkIRect::MakeXYWH(origin.x(), origin.y(), info.width(), info.height()),
+                SkBackingFit::kExact, SkBudgeted::kYes);
     }
 
 private:
diff --git a/gn/gpu.gni b/gn/gpu.gni
index 463bc28..ab7da4e 100644
--- a/gn/gpu.gni
+++ b/gn/gpu.gni
@@ -632,10 +632,6 @@
   "$_src/gpu/vk/GrVkCommandBuffer.h",
   "$_src/gpu/vk/GrVkCommandPool.cpp",
   "$_src/gpu/vk/GrVkCommandPool.h",
-  "$_src/gpu/vk/GrVkCopyManager.cpp",
-  "$_src/gpu/vk/GrVkCopyManager.h",
-  "$_src/gpu/vk/GrVkCopyPipeline.cpp",
-  "$_src/gpu/vk/GrVkCopyPipeline.h",
   "$_src/gpu/vk/GrVkDescriptorPool.cpp",
   "$_src/gpu/vk/GrVkDescriptorPool.h",
   "$_src/gpu/vk/GrVkDescriptorSet.cpp",
@@ -715,11 +711,7 @@
   "$_src/gpu/mtl/GrMtlCaps.mm",
   "$_src/gpu/mtl/GrMtlCommandBuffer.h",
   "$_src/gpu/mtl/GrMtlCommandBuffer.mm",
-  "$_src/gpu/mtl/GrMtlCopyManager.h",
-  "$_src/gpu/mtl/GrMtlCopyManager.mm",
   "$_src/gpu/mtl/GrMtlCppUtil.h",
-  "$_src/gpu/mtl/GrMtlCopyPipelineState.h",
-  "$_src/gpu/mtl/GrMtlCopyPipelineState.mm",
   "$_src/gpu/mtl/GrMtlDepthStencil.h",
   "$_src/gpu/mtl/GrMtlDepthStencil.mm",
   "$_src/gpu/mtl/GrMtlGpu.h",
diff --git a/gn/tests.gni b/gn/tests.gni
index baa77f5..e16a550 100644
--- a/gn/tests.gni
+++ b/gn/tests.gni
@@ -297,7 +297,6 @@
   "$_tests/VkBackendSurfaceTest.cpp",
   "$_tests/VkDrawableTest.cpp",
   "$_tests/VkHardwareBufferTest.cpp",
-  "$_tests/VkMakeCopyPipelineTest.cpp",
   "$_tests/VkPriorityExtensionTest.cpp",
   "$_tests/VkWrapTests.cpp",
   "$_tests/VptrTest.cpp",
diff --git a/include/private/GrSurfaceProxy.h b/include/private/GrSurfaceProxy.h
index f193010..e5bed41 100644
--- a/include/private/GrSurfaceProxy.h
+++ b/include/private/GrSurfaceProxy.h
@@ -437,20 +437,21 @@
         return fGpuMemorySize;
     }
 
+    enum class RectsMustMatch : bool {
+        kNo = false,
+        kYes = true
+    };
+
     // Helper function that creates a temporary SurfaceContext to perform the copy
     // The copy is is not a render target and not multisampled.
     static sk_sp<GrTextureProxy> Copy(GrRecordingContext*, GrSurfaceProxy* src, GrMipMapped,
-                                      SkIRect srcRect, SkBackingFit, SkBudgeted);
+                                      SkIRect srcRect, SkBackingFit, SkBudgeted,
+                                      RectsMustMatch = RectsMustMatch::kNo);
 
     // Copy the entire 'src'
     static sk_sp<GrTextureProxy> Copy(GrRecordingContext*, GrSurfaceProxy* src, GrMipMapped,
                                       SkBackingFit, SkBudgeted);
 
-    // Test-only entry point - should decrease in use as proxies propagate
-    static sk_sp<GrSurfaceContext> TestCopy(GrRecordingContext* context,
-                                            const GrSurfaceDesc& dstDesc,
-                                            GrSurfaceOrigin, GrSurfaceProxy* srcProxy);
-
     bool isWrapped_ForTesting() const;
 
     SkDEBUGCODE(void validate(GrContext_Base*) const;)
diff --git a/src/gpu/GrBackendTextureImageGenerator.cpp b/src/gpu/GrBackendTextureImageGenerator.cpp
index 4df0bd4..6320809 100644
--- a/src/gpu/GrBackendTextureImageGenerator.cpp
+++ b/src/gpu/GrBackendTextureImageGenerator.cpp
@@ -208,28 +208,10 @@
         // because Vulkan will want to do the copy as a draw. All other copies would require a
         // layout change in Vulkan and we do not change the layout of borrowed images.
         GrMipMapped mipMapped = willNeedMipMaps ? GrMipMapped::kYes : GrMipMapped::kNo;
-
-        GrBackendFormat format = proxy->backendFormat().makeTexture2D();
-        if (!format.isValid()) {
-            return nullptr;
-        }
-
-        sk_sp<GrRenderTargetContext> rtContext(
-            context->priv().makeDeferredRenderTargetContext(
-                format, SkBackingFit::kExact, info.width(), info.height(),
-                proxy->config(), nullptr, 1, mipMapped, proxy->origin(), nullptr,
-                SkBudgeted::kYes));
-
-        if (!rtContext) {
-            return nullptr;
-        }
-
         SkIRect subset = SkIRect::MakeXYWH(origin.fX, origin.fY, info.width(), info.height());
-        if (!rtContext->copy(proxy.get(), subset, SkIPoint::Make(0, 0))) {
-            return nullptr;
-        }
 
-        return rtContext->asTextureProxyRef();
+        return GrSurfaceProxy::Copy(context, proxy.get(), mipMapped, subset, SkBackingFit::kExact,
+                                    SkBudgeted::kYes);
     }
 }
 
diff --git a/src/gpu/GrCaps.h b/src/gpu/GrCaps.h
index 3c9884e..42d6e76 100644
--- a/src/gpu/GrCaps.h
+++ b/src/gpu/GrCaps.h
@@ -310,8 +310,7 @@
      * copy rect must equal src's bounds.
      */
     virtual bool initDescForDstCopy(const GrRenderTargetProxy* src, GrSurfaceDesc* desc,
-                                    GrSurfaceOrigin* origin, bool* rectsMustMatch,
-                                    bool* disallowSubrect) const = 0;
+                                    bool* rectsMustMatch, bool* disallowSubrect) const = 0;
 
     bool validateSurfaceDesc(const GrSurfaceDesc&, GrMipMapped) const;
 
diff --git a/src/gpu/GrContextPriv.cpp b/src/gpu/GrContextPriv.cpp
index 7945192..093acbf 100644
--- a/src/gpu/GrContextPriv.cpp
+++ b/src/gpu/GrContextPriv.cpp
@@ -15,6 +15,7 @@
 #include "src/gpu/GrGpu.h"
 #include "src/gpu/GrMemoryPool.h"
 #include "src/gpu/GrRenderTargetContext.h"
+#include "src/gpu/GrSurfaceContextPriv.h"
 #include "src/gpu/GrSurfacePriv.h"
 #include "src/gpu/GrTextureContext.h"
 #include "src/gpu/SkGr.h"
diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp
index ce151b1..7bbedfc 100644
--- a/src/gpu/GrGpu.cpp
+++ b/src/gpu/GrGpu.cpp
@@ -222,10 +222,8 @@
     return buffer;
 }
 
-bool GrGpu::copySurface(GrSurface* dst, GrSurfaceOrigin dstOrigin,
-                        GrSurface* src, GrSurfaceOrigin srcOrigin,
-                        const SkIRect& srcRect, const SkIPoint& dstPoint,
-                        bool canDiscardOutsideDstRect) {
+bool GrGpu::copySurface(GrSurface* dst, GrSurface* src, const SkIRect& srcRect,
+                        const SkIPoint& dstPoint, bool canDiscardOutsideDstRect) {
     GR_CREATE_TRACE_MARKER_CONTEXT("GrGpu", "copySurface", fContext);
     SkASSERT(dst && src);
 
@@ -235,8 +233,7 @@
 
     this->handleDirtyContext();
 
-    return this->onCopySurface(dst, dstOrigin, src, srcOrigin, srcRect, dstPoint,
-                               canDiscardOutsideDstRect);
+    return this->onCopySurface(dst, src, srcRect, dstPoint, canDiscardOutsideDstRect);
 }
 
 bool GrGpu::readPixels(GrSurface* surface, int left, int top, int width, int height,
diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h
index 467e1db..62d435e 100644
--- a/src/gpu/GrGpu.h
+++ b/src/gpu/GrGpu.h
@@ -253,13 +253,11 @@
     // Called to perform a surface to surface copy. Fallbacks to issuing a draw from the src to dst
     // take place at the GrOpList level and this function implement faster copy paths. The rect
     // and point are pre-clipped. The src rect and implied dst rect are guaranteed to be within the
-    // src/dst bounds and non-empty. If canDiscardOutsideDstRect is set to true then we don't need
-    // to preserve any data on the dst surface outside of the copy.
-    bool copySurface(GrSurface* dst, GrSurfaceOrigin dstOrigin,
-                     GrSurface* src, GrSurfaceOrigin srcOrigin,
-                     const SkIRect& srcRect,
-                     const SkIPoint& dstPoint,
-                     bool canDiscardOutsideDstRect = false);
+    // src/dst bounds and non-empty. They must also be in their exact device space coords, including
+    // already being transformed for origin if need be. If canDiscardOutsideDstRect is set to true
+    // then we don't need to preserve any data on the dst surface outside of the copy.
+    bool copySurface(GrSurface* dst, GrSurface* src, const SkIRect& srcRect,
+                     const SkIPoint& dstPoint, bool canDiscardOutsideDstRect = false);
 
     // Queries the per-pixel HW sample locations for the given render target, and then finds or
     // assigns a key that uniquely identifies the sample pattern. The actual sample locations can be
@@ -537,10 +535,8 @@
     virtual bool onRegenerateMipMapLevels(GrTexture*) = 0;
 
     // overridden by backend specific derived class to perform the copy surface
-    virtual bool onCopySurface(GrSurface* dst, GrSurfaceOrigin dstOrigin,
-                               GrSurface* src, GrSurfaceOrigin srcOrigin,
-                               const SkIRect& srcRect, const SkIPoint& dstPoint,
-                               bool canDiscardOutsideDstRect) = 0;
+    virtual bool onCopySurface(GrSurface* dst, GrSurface* src, const SkIRect& srcRect,
+                               const SkIPoint& dstPoint, bool canDiscardOutsideDstRect) = 0;
 
     virtual void onFinishFlush(GrSurfaceProxy*[], int n, SkSurface::BackendSurfaceAccess access,
                                const GrFlushInfo&, const GrPrepareForExternalIORequests&) = 0;
diff --git a/src/gpu/GrGpuCommandBuffer.h b/src/gpu/GrGpuCommandBuffer.h
index abb3c4d..e96c610 100644
--- a/src/gpu/GrGpuCommandBuffer.h
+++ b/src/gpu/GrGpuCommandBuffer.h
@@ -30,9 +30,9 @@
     virtual ~GrGpuCommandBuffer() {}
 
     // Copy src into current surface owned by either a GrGpuTextureCommandBuffer or
-    // GrGpuRenderTargetCommandBuffer.
-    virtual void copy(GrSurface* src, GrSurfaceOrigin srcOrigin,
-                      const SkIRect& srcRect, const SkIPoint& dstPoint) = 0;
+    // GrGpuRenderTargetCommandBuffer. The srcRect and dstPoint must be in dst coords and have
+    // already been adjusted for any origin flips.
+    virtual void copy(GrSurface* src, const SkIRect& srcRect, const SkIPoint& dstPoint) = 0;
     // Initiates a transfer from the surface owned by the command buffer to the GrGpuBuffer.
     virtual void transferFrom(const SkIRect& srcRect, GrColorType bufferColorType,
                               GrGpuBuffer* transferBuffer, size_t offset) = 0;
@@ -47,19 +47,14 @@
     void set(GrTexture* texture, GrSurfaceOrigin origin) {
         SkASSERT(!fTexture);
 
-        fOrigin = origin;
         fTexture = texture;
     }
 
 protected:
-    GrGpuTextureCommandBuffer() : fOrigin(kTopLeft_GrSurfaceOrigin), fTexture(nullptr) {}
+    GrGpuTextureCommandBuffer() : fTexture(nullptr) {}
 
-    GrGpuTextureCommandBuffer(GrTexture* texture, GrSurfaceOrigin origin)
-            : fOrigin(origin)
-            , fTexture(texture) {
-    }
+    GrGpuTextureCommandBuffer(GrTexture* texture, GrSurfaceOrigin origin) : fTexture(texture) {}
 
-    GrSurfaceOrigin fOrigin;
     GrTexture*      fTexture;
 
 private:
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index bf2f87b..4c89936 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -1785,27 +1785,17 @@
     int srcH = srcRect.height();
     int srcX = srcRect.fLeft;
     int srcY = srcRect.fTop;
-    sk_sp<GrSurfaceContext> srcContext = sk_ref_sp(this);
+    sk_sp<GrTextureProxy> texProxy = sk_ref_sp(fRenderTargetProxy->asTextureProxy());
     SkCanvas::SrcRectConstraint constraint = SkCanvas::kStrict_SrcRectConstraint;
-    if (!this->asTextureProxy()) {
-        GrSurfaceDesc desc;
-        desc.fWidth = srcW;
-        desc.fHeight = srcH;
-        desc.fConfig = fRenderTargetProxy->config();
-        auto sContext = direct->priv().makeDeferredSurfaceContext(
-                fRenderTargetProxy->backendFormat().makeTexture2D(), desc, this->origin(),
-                GrMipMapped::kNo, SkBackingFit::kApprox, SkBudgeted::kNo,
-                this->colorSpaceInfo().refColorSpace());
-        if (!sContext) {
-            return nullptr;
-        }
-        if (!sContext->copy(fRenderTargetProxy.get(), srcRect, {0, 0})) {
+    if (!texProxy) {
+        texProxy = GrSurfaceProxy::Copy(fContext, fRenderTargetProxy.get(), GrMipMapped::kNo,
+                                        srcRect, SkBackingFit::kApprox, SkBudgeted::kNo);
+        if (!texProxy) {
             return nullptr;
         }
         srcX = 0;
         srcY = 0;
         constraint = SkCanvas::kFast_SrcRectConstraint;
-        srcContext = std::move(sContext);
     }
 
     float sx = (float)info.width() / srcW;
@@ -1824,33 +1814,35 @@
         stepsY = sy != 1.f;
     }
     SkASSERT(stepsX || stepsY);
+    auto currentColorSpace = this->colorSpaceInfo().refColorSpace();
     // Assume we should ignore the rescale linear request if the surface has no color space since
     // it's unclear how we'd linearize from an unknown color space.
     if (rescaleGamma == SkSurface::RescaleGamma::kLinear &&
-        srcContext->colorSpaceInfo().colorSpace() &&
-        !srcContext->colorSpaceInfo().colorSpace()->gammaIsLinear()) {
-        auto cs = srcContext->colorSpaceInfo().colorSpace()->makeLinearGamma();
+        currentColorSpace.get() && !currentColorSpace->gammaIsLinear()) {
+        auto cs = currentColorSpace->makeLinearGamma();
         auto backendFormat = this->caps()->getBackendFormatFromGrColorType(GrColorType::kRGBA_F16,
                                                                            GrSRGBEncoded::kNo);
-        auto xform = GrColorSpaceXform::Make(srcContext->colorSpaceInfo().colorSpace(),
-                                             kPremul_SkAlphaType, cs.get(), kPremul_SkAlphaType);
+        auto xform = GrColorSpaceXform::Make(currentColorSpace.get(), kPremul_SkAlphaType, cs.get(),
+                                             kPremul_SkAlphaType);
         // We'll fall back to kRGBA_8888 if half float not supported.
         auto linearRTC = fContext->priv().makeDeferredRenderTargetContextWithFallback(
-                backendFormat, SkBackingFit::kExact, srcW, srcH, kRGBA_half_GrPixelConfig,
-                std::move(cs), 1, GrMipMapped::kNo, kTopLeft_GrSurfaceOrigin);
+                backendFormat, SkBackingFit::kExact, srcW, srcH, kRGBA_half_GrPixelConfig, cs, 1,
+                GrMipMapped::kNo, kTopLeft_GrSurfaceOrigin);
         if (!linearRTC) {
             return nullptr;
         }
-        linearRTC->drawTexture(GrNoClip(), srcContext->asTextureProxyRef(),
+        linearRTC->drawTexture(GrNoClip(), texProxy,
                                GrSamplerState::Filter::kNearest, SkBlendMode::kSrc,
                                SK_PMColor4fWHITE, SkRect::Make(srcRect), SkRect::MakeWH(srcW, srcH),
                                GrAA::kNo, GrQuadAAFlags::kNone, constraint, SkMatrix::I(),
                                std::move(xform));
-        srcContext = std::move(linearRTC);
+        texProxy = linearRTC->asTextureProxyRef();
+        currentColorSpace = std::move(cs);
         srcX = 0;
         srcY = 0;
         constraint = SkCanvas::kFast_SrcRectConstraint;
     }
+    sk_sp<GrRenderTargetContext> currRTC;
     while (stepsX || stepsY) {
         int nextW = info.width();
         int nextH = info.height();
@@ -1872,23 +1864,22 @@
             }
             --stepsY;
         }
-        GrBackendFormat backendFormat =
-                srcContext->asSurfaceProxy()->backendFormat().makeTexture2D();
-        GrPixelConfig config = srcContext->asSurfaceProxy()->config();
-        auto cs = srcContext->colorSpaceInfo().refColorSpace();
+        GrBackendFormat backendFormat = texProxy->backendFormat().makeTexture2D();
+        GrPixelConfig config = texProxy->config();
+        auto cs = currentColorSpace;
         sk_sp<GrColorSpaceXform> xform;
         if (!stepsX && !stepsY) {
             // Might as well fold conversion to final info in the last step.
             backendFormat = this->caps()->getBackendFormatFromColorType(info.colorType());
             config = this->caps()->getConfigFromBackendFormat(backendFormat, info.colorType());
             cs = info.refColorSpace();
-            xform = GrColorSpaceXform::Make(srcContext->colorSpaceInfo().colorSpace(),
+            xform = GrColorSpaceXform::Make(this->colorSpaceInfo().colorSpace(),
                                             kPremul_SkAlphaType, cs.get(), info.alphaType());
         }
-        auto nextRTC = fContext->priv().makeDeferredRenderTargetContextWithFallback(
+        currRTC = fContext->priv().makeDeferredRenderTargetContextWithFallback(
                 backendFormat, SkBackingFit::kExact, nextW, nextH, config, std::move(cs), 1,
                 GrMipMapped::kNo, kTopLeft_GrSurfaceOrigin);
-        if (!nextRTC) {
+        if (!currRTC) {
             return nullptr;
         }
         auto dstRect = SkRect::MakeWH(nextW, nextH);
@@ -1902,14 +1893,12 @@
             } else if (nextH == srcH) {
                 dir = GrBicubicEffect::Direction::kX;
             }
-            if (srcW != srcContext->width() || srcH != srcContext->height()) {
+            if (srcW != texProxy->width() || srcH != texProxy->height()) {
                 auto domain = GrTextureDomain::MakeTexelDomain(
                         SkIRect::MakeXYWH(srcX, srcY, srcW, srcH), GrTextureDomain::kClamp_Mode);
-                fp = GrBicubicEffect::Make(srcContext->asTextureProxyRef(), matrix, domain, dir,
-                                           kPremul_SkAlphaType);
+                fp = GrBicubicEffect::Make(texProxy, matrix, domain, dir, kPremul_SkAlphaType);
             } else {
-                fp = GrBicubicEffect::Make(srcContext->asTextureProxyRef(), matrix, dir,
-                                           kPremul_SkAlphaType);
+                fp = GrBicubicEffect::Make(texProxy, matrix, dir, kPremul_SkAlphaType);
             }
             if (xform) {
                 fp = GrColorSpaceXformEffect::Make(std::move(fp), std::move(xform));
@@ -1917,26 +1906,24 @@
             GrPaint paint;
             paint.addColorFragmentProcessor(std::move(fp));
             paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
-            nextRTC->drawFilledRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(),
+            currRTC->drawFilledRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(),
                                     dstRect);
         } else {
             auto filter = rescaleQuality == kNone_SkFilterQuality ? GrSamplerState::Filter::kNearest
                                                                   : GrSamplerState::Filter::kBilerp;
             auto srcSubset = SkRect::MakeXYWH(srcX, srcY, srcW, srcH);
-            nextRTC->drawTexture(GrNoClip(), srcContext->asTextureProxyRef(), filter,
-                                 SkBlendMode::kSrc, SK_PMColor4fWHITE, srcSubset, dstRect,
-                                 GrAA::kNo, GrQuadAAFlags::kNone, constraint, SkMatrix::I(),
-                                 std::move(xform));
+            currRTC->drawTexture(GrNoClip(), texProxy, filter, SkBlendMode::kSrc, SK_PMColor4fWHITE,
+                                 srcSubset, dstRect, GrAA::kNo, GrQuadAAFlags::kNone, constraint,
+                                 SkMatrix::I(), std::move(xform));
         }
-        srcContext = std::move(nextRTC);
+        texProxy = currRTC->asTextureProxyRef();
         srcX = srcY = 0;
         srcW = nextW;
         srcH = nextH;
         constraint = SkCanvas::kFast_SrcRectConstraint;
     }
-    auto result = sk_ref_sp(srcContext->asRenderTargetContext());
-    SkASSERT(result);
-    return result;
+    SkASSERT(currRTC);
+    return currRTC;
 }
 
 void GrRenderTargetContext::asyncRescaleAndReadPixels(
@@ -2001,24 +1988,13 @@
             SkRect srcRectToDraw = SkRect::Make(srcRect);
             // If the src is not texturable first try to make a copy to a texture.
             if (!texProxy) {
-                GrSurfaceDesc desc;
-                desc.fWidth = srcRect.width();
-                desc.fHeight = srcRect.height();
-                desc.fConfig = fRenderTargetProxy->config();
-                auto sContext = direct->priv().makeDeferredSurfaceContext(
-                        backendFormat, desc, this->origin(), GrMipMapped::kNo,
-                        SkBackingFit::kApprox, SkBudgeted::kNo,
-                        this->colorSpaceInfo().refColorSpace());
-                if (!sContext) {
+                texProxy = GrSurfaceProxy::Copy(fContext, fRenderTargetProxy.get(),
+                                                GrMipMapped::kNo, srcRect, SkBackingFit::kApprox,
+                                                SkBudgeted::kNo);
+                if (!texProxy) {
                     callback(context, nullptr, 0);
                     return;
                 }
-                if (!sContext->copy(fRenderTargetProxy.get(), srcRect, {0, 0})) {
-                    callback(context, nullptr, 0);
-                    return;
-                }
-                texProxy = sk_ref_sp(sContext->asTextureProxy());
-                SkASSERT(texProxy);
                 srcRectToDraw = SkRect::MakeWH(srcRect.width(), srcRect.height());
             }
             rtc = direct->priv().makeDeferredRenderTargetContext(
@@ -2568,12 +2544,10 @@
     GrSurfaceDesc desc;
     bool rectsMustMatch = false;
     bool disallowSubrect = false;
-    GrSurfaceOrigin origin;
-    if (!this->caps()->initDescForDstCopy(rtProxy, &desc, &origin, &rectsMustMatch,
+    if (!this->caps()->initDescForDstCopy(rtProxy, &desc, &rectsMustMatch,
                                           &disallowSubrect)) {
         desc.fFlags = kRenderTarget_GrSurfaceFlag;
         desc.fConfig = rtProxy->config();
-        origin = rtProxy->origin();
     }
 
     if (!disallowSubrect) {
@@ -2582,36 +2556,56 @@
 
     SkIPoint dstPoint, dstOffset;
     SkBackingFit fit;
+    GrSurfaceProxy::RectsMustMatch matchRects;
     if (rectsMustMatch) {
         desc.fWidth = rtProxy->width();
         desc.fHeight = rtProxy->height();
         dstPoint = {copyRect.fLeft, copyRect.fTop};
         dstOffset = {0, 0};
         fit = SkBackingFit::kExact;
+        matchRects = GrSurfaceProxy::RectsMustMatch::kYes;
     } else {
         desc.fWidth = copyRect.width();
         desc.fHeight = copyRect.height();
         dstPoint = {0, 0};
         dstOffset = {copyRect.fLeft, copyRect.fTop};
         fit = SkBackingFit::kApprox;
+        matchRects = GrSurfaceProxy::RectsMustMatch::kNo;
     }
 
-    SkASSERT(rtProxy->backendFormat().textureType() == GrTextureType::k2D);
-    const GrBackendFormat& format = rtProxy->backendFormat();
-    sk_sp<GrSurfaceContext> sContext = fContext->priv().makeDeferredSurfaceContext(
-            format, desc, origin, GrMipMapped::kNo, fit, SkBudgeted::kYes,
-            sk_ref_sp(this->colorSpaceInfo().colorSpace()));
-    if (!sContext) {
-        SkDebugf("setupDstTexture: surfaceContext creation failed.\n");
-        return false;
-    }
+    sk_sp<GrTextureProxy> newProxy = GrSurfaceProxy::Copy(fContext, rtProxy, GrMipMapped::kNo,
+                                                          copyRect, fit, SkBudgeted::kYes,
+                                                          matchRects);
+    SkASSERT(newProxy);
 
-    if (!sContext->copy(rtProxy, copyRect, dstPoint)) {
-        SkDebugf("setupDstTexture: copy failed.\n");
-        return false;
-    }
-
-    dstProxy->setProxy(sContext->asTextureProxyRef());
+    dstProxy->setProxy(std::move(newProxy));
     dstProxy->setOffset(dstOffset);
     return true;
 }
+
+bool GrRenderTargetContext::blitTexture(GrTextureProxy* src, const SkIRect& srcRect,
+                                        const SkIPoint& dstPoint) {
+    SkIRect clippedSrcRect;
+    SkIPoint clippedDstPoint;
+    if (!GrClipSrcRectAndDstPoint(this->asSurfaceProxy()->isize(), src->isize(), srcRect, dstPoint,
+                                  &clippedSrcRect, &clippedDstPoint)) {
+        return false;
+    }
+
+    GrPaint paint;
+    paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
+    auto fp = GrSimpleTextureEffect::Make(sk_ref_sp(src->asTextureProxy()),
+                                          SkMatrix::I());
+    if (!fp) {
+        return false;
+    }
+    paint.addColorFragmentProcessor(std::move(fp));
+
+    this->fillRectToRect(
+            GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(),
+            SkRect::MakeXYWH(clippedDstPoint.fX, clippedDstPoint.fY, clippedSrcRect.width(),
+                             clippedSrcRect.height()),
+            SkRect::Make(clippedSrcRect));
+    return true;
+}
+
diff --git a/src/gpu/GrRenderTargetContext.h b/src/gpu/GrRenderTargetContext.h
index 528a670..5e68e8d 100644
--- a/src/gpu/GrRenderTargetContext.h
+++ b/src/gpu/GrRenderTargetContext.h
@@ -397,6 +397,13 @@
                           const SkRect& dst);
 
     /**
+     * Draws the src texture with no matrix. The dstRect is the dstPoint with the width and height
+     * of the srcRect. The srcRect and dstRect are clipped to the bounds of the src and dst surfaces
+     * respectively.
+     */
+    bool blitTexture(GrTextureProxy* src, const SkIRect& srcRect, const SkIPoint& dstPoint);
+
+    /**
      * Adds the necessary signal and wait semaphores and adds the passed in SkDrawable to the
      * command stream.
      */
diff --git a/src/gpu/GrSurfaceContext.cpp b/src/gpu/GrSurfaceContext.cpp
index d716d39..ac19e91 100644
--- a/src/gpu/GrSurfaceContext.cpp
+++ b/src/gpu/GrSurfaceContext.cpp
@@ -5,6 +5,8 @@
  * found in the LICENSE file.
  */
 
+#include "src/gpu/GrSurfaceContext.h"
+
 #include "include/private/GrAuditTrail.h"
 #include "include/private/GrOpList.h"
 #include "include/private/GrRecordingContext.h"
@@ -15,7 +17,7 @@
 #include "src/gpu/GrGpu.h"
 #include "src/gpu/GrRecordingContextPriv.h"
 #include "src/gpu/GrRenderTargetContext.h"
-#include "src/gpu/GrSurfaceContext.h"
+#include "src/gpu/GrSurfaceContextPriv.h"
 #include "src/gpu/GrSurfacePriv.h"
 #include "src/gpu/GrTextureContext.h"
 #include "src/gpu/SkGr.h"
@@ -377,7 +379,7 @@
         }
 
         auto tempProxy = direct->priv().proxyProvider()->createProxy(
-                format, desc, kTopLeft_GrSurfaceOrigin, SkBackingFit::kApprox, SkBudgeted::kYes);
+                format, desc, dstProxy->origin(), SkBackingFit::kApprox, SkBudgeted::kYes);
         if (!tempProxy) {
             return false;
         }
@@ -399,7 +401,7 @@
         }
 
         if (this->asRenderTargetContext()) {
-        std::unique_ptr<GrFragmentProcessor> fp;
+            std::unique_ptr<GrFragmentProcessor> fp;
             if (canvas2DFastPath) {
                 fp = direct->priv().createUPMToPMEffect(
                         GrSimpleTextureEffect::Make(std::move(tempProxy), SkMatrix::I()));
@@ -421,10 +423,9 @@
         } else {
             SkIRect srcRect = SkIRect::MakeWH(width, height);
             SkIPoint dstPoint = SkIPoint::Make(left, top);
-            if (!caps->canCopySurface(this->asSurfaceProxy(), tempProxy.get(), srcRect, dstPoint)) {
+            if (!this->copy(tempProxy.get(), srcRect, dstPoint)) {
                 return false;
             }
-            SkAssertResult(this->copy(tempProxy.get(), srcRect, dstPoint));
         }
         return true;
     }
@@ -531,13 +532,17 @@
     ASSERT_SINGLE_OWNER
     RETURN_FALSE_IF_ABANDONED
     SkDEBUGCODE(this->validate();)
-    GR_AUDIT_TRAIL_AUTO_FRAME(this->auditTrail(), "GrSurfaceContext::copy");
+    GR_AUDIT_TRAIL_AUTO_FRAME(this->auditTrail(), "GrSurfaceContextPriv::copy");
 
-    if (!fContext->priv().caps()->canCopySurface(this->asSurfaceProxy(), src, srcRect,
-                                                        dstPoint)) {
+    SkASSERT(src->backendFormat().textureType() != GrTextureType::kExternal);
+    SkASSERT(src->origin() == this->asSurfaceProxy()->origin());
+
+    GrSurfaceProxy* dst = this->asSurfaceProxy();
+
+    if (!fContext->priv().caps()->canCopySurface(dst, src, srcRect, dstPoint)) {
         return false;
     }
 
-    return this->getOpList()->copySurface(fContext, this->asSurfaceProxy(),
-                                          src, srcRect, dstPoint);
+    return this->getOpList()->copySurface(fContext, dst, src, srcRect, dstPoint);
 }
+
diff --git a/src/gpu/GrSurfaceContext.h b/src/gpu/GrSurfaceContext.h
index 3cbb838..e72364c 100644
--- a/src/gpu/GrSurfaceContext.h
+++ b/src/gpu/GrSurfaceContext.h
@@ -39,26 +39,6 @@
     int width() const { return this->asSurfaceProxy()->width(); }
     int height() const { return this->asSurfaceProxy()->height(); }
 
-    /*
-     * 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);
-
-    bool copy(GrSurfaceProxy* src) {
-        return this->copy(src,
-                          SkIRect::MakeWH(src->width(), src->height()),
-                          SkIPoint::Make(0, 0));
-    }
-
    /**
     * These flags can be used with the read/write pixels functions below.
     */
@@ -134,6 +114,17 @@
     GrSurfaceContextPriv surfPriv();
     const GrSurfaceContextPriv surfPriv() const;
 
+#if GR_TEST_UTILS
+    bool testCopy(GrSurfaceProxy* src, const SkIRect& srcRect, const SkIPoint& dstPoint) {
+        return this->copy(src, srcRect, dstPoint);
+    }
+
+    bool testCopy(GrSurfaceProxy* src) {
+        return this->copy(src);
+    }
+#endif
+
+
 protected:
     friend class GrSurfaceContextPriv;
 
@@ -150,6 +141,29 @@
     GrRecordingContext* fContext;
 
 private:
+    friend class GrSurfaceProxy; // for copy
+
+    /**
+     * Copy 'src' into the proxy backing this context. This call will not do any draw fallback.
+     * Currently only writePixels and replaceRenderTarget call this directly. All other copies
+     * should go through GrSurfaceProxy::Copy.
+     * @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. The 'src' must have the same origin as the backing proxy
+     *       of fSurfaceContext.
+     */
+    bool copy(GrSurfaceProxy* src, const SkIRect& srcRect, const SkIPoint& dstPoint);
+
+    bool copy(GrSurfaceProxy* src) {
+        return this->copy(src, SkIRect::MakeWH(src->width(), src->height()), SkIPoint::Make(0, 0));
+    }
+
     bool writePixelsImpl(GrContext* direct, int left, int top, int width, int height,
                          GrColorType srcColorType, SkColorSpace* srcColorSpace,
                          const void* srcBuffer, size_t srcRowBytes, uint32_t pixelOpsFlags);
diff --git a/src/gpu/GrSurfaceProxy.cpp b/src/gpu/GrSurfaceProxy.cpp
index f459e02..ccf2e55 100644
--- a/src/gpu/GrSurfaceProxy.cpp
+++ b/src/gpu/GrSurfaceProxy.cpp
@@ -12,11 +12,13 @@
 #include "include/private/GrOpList.h"
 #include "include/private/GrRecordingContext.h"
 #include "src/gpu/GrCaps.h"
+#include "src/gpu/GrClip.h"
 #include "src/gpu/GrContextPriv.h"
 #include "src/gpu/GrGpuResourcePriv.h"
 #include "src/gpu/GrProxyProvider.h"
 #include "src/gpu/GrRecordingContextPriv.h"
 #include "src/gpu/GrSurfaceContext.h"
+#include "src/gpu/GrSurfaceContextPriv.h"
 #include "src/gpu/GrSurfacePriv.h"
 #include "src/gpu/GrTexturePriv.h"
 #include "src/gpu/GrTextureRenderTargetProxy.h"
@@ -335,33 +337,54 @@
                                            GrMipMapped mipMapped,
                                            SkIRect srcRect,
                                            SkBackingFit fit,
-                                           SkBudgeted budgeted) {
+                                           SkBudgeted budgeted,
+                                           RectsMustMatch rectsMustMatch) {
     SkASSERT(LazyState::kFully != src->lazyInstantiationState());
+    GrSurfaceDesc dstDesc;
+    dstDesc.fConfig = src->config();
+
+    SkIPoint dstPoint;
+    if (rectsMustMatch == RectsMustMatch::kYes) {
+        dstDesc.fWidth = src->width();
+        dstDesc.fHeight = src->height();
+        dstPoint = {srcRect.fLeft, srcRect.fTop};
+    } else {
+        dstDesc.fWidth = srcRect.width();
+        dstDesc.fHeight = srcRect.height();
+        dstPoint = {0, 0};
+    }
+
     if (!srcRect.intersect(SkIRect::MakeWH(src->width(), src->height()))) {
         return nullptr;
     }
 
-    GrSurfaceDesc dstDesc;
-    dstDesc.fWidth = srcRect.width();
-    dstDesc.fHeight = srcRect.height();
-    dstDesc.fConfig = src->config();
-
-    GrBackendFormat format = src->backendFormat().makeTexture2D();
-    if (!format.isValid()) {
-        return nullptr;
+    if (src->backendFormat().textureType() != GrTextureType::kExternal) {
+        sk_sp<GrSurfaceContext> dstContext(context->priv().makeDeferredSurfaceContext(
+                src->backendFormat().makeTexture2D(), dstDesc, src->origin(), mipMapped, fit,
+                budgeted));
+        if (!dstContext) {
+            return nullptr;
+        }
+        if (dstContext->copy(src, srcRect, dstPoint)) {
+            return dstContext->asTextureProxyRef();
+        }
     }
+    if (src->asTextureProxy()) {
+        GrBackendFormat format = src->backendFormat().makeTexture2D();
+        if (!format.isValid()) {
+            return nullptr;
+        }
 
-    sk_sp<GrSurfaceContext> dstContext(context->priv().makeDeferredSurfaceContext(
-            format, dstDesc, src->origin(), mipMapped, fit, budgeted));
-    if (!dstContext) {
-        return nullptr;
+        sk_sp<GrRenderTargetContext> dstContext = context->priv().makeDeferredRenderTargetContext(
+                format, fit, dstDesc.fWidth, dstDesc.fHeight, dstDesc.fConfig, nullptr, 1,
+                mipMapped, src->origin(), nullptr, budgeted);
+
+        if (dstContext && dstContext->blitTexture(src->asTextureProxy(), srcRect, dstPoint)) {
+            return dstContext->asTextureProxyRef();
+        }
     }
-
-    if (!dstContext->copy(src, srcRect, SkIPoint::Make(0, 0))) {
-        return nullptr;
-    }
-
-    return dstContext->asTextureProxyRef();
+    // Can't use backend copies or draws.
+    return nullptr;
 }
 
 sk_sp<GrTextureProxy> GrSurfaceProxy::Copy(GrRecordingContext* context, GrSurfaceProxy* src,
@@ -372,29 +395,6 @@
                 budgeted);
 }
 
-sk_sp<GrSurfaceContext> GrSurfaceProxy::TestCopy(GrRecordingContext* context,
-                                                 const GrSurfaceDesc& dstDesc,
-                                                 GrSurfaceOrigin origin, GrSurfaceProxy* srcProxy) {
-    SkASSERT(LazyState::kFully != srcProxy->lazyInstantiationState());
-
-    GrBackendFormat format = srcProxy->backendFormat().makeTexture2D();
-    if (!format.isValid()) {
-        return nullptr;
-    }
-
-    sk_sp<GrSurfaceContext> dstContext(context->priv().makeDeferredSurfaceContext(
-            format, dstDesc, origin, GrMipMapped::kNo, SkBackingFit::kExact, SkBudgeted::kYes));
-    if (!dstContext) {
-        return nullptr;
-    }
-
-    if (!dstContext->copy(srcProxy)) {
-        return nullptr;
-    }
-
-    return dstContext;
-}
-
 void GrSurfaceProxyPriv::exactify() {
     SkASSERT(GrSurfaceProxy::LazyState::kFully != fProxy->lazyInstantiationState());
     if (this->isExact()) {
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index c10b696..a2b45fd 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -254,7 +254,11 @@
         if (this->context()->abandoned()) {
             return;
         }
-        rtc->copy(fRenderTargetContext->asSurfaceProxy());
+
+        SkASSERT(fRenderTargetContext->asTextureProxy());
+        SkAssertResult(rtc->blitTexture(fRenderTargetContext->asTextureProxy(),
+                                        SkIRect::MakeWH(this->width(), this->height()),
+                                        SkIPoint::Make(0,0)));
     }
 
     fRenderTargetContext = std::move(rtc);
diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp
index b144d01..911262b 100644
--- a/src/gpu/SkGr.cpp
+++ b/src/gpu/SkGr.cpp
@@ -133,31 +133,8 @@
         return nullptr;
     }
 
-    GrProxyProvider* proxyProvider = ctx->priv().proxyProvider();
-    GrSurfaceDesc desc;
-    desc.fFlags = kNone_GrSurfaceFlags;
-    desc.fWidth = baseProxy->width();
-    desc.fHeight = baseProxy->height();
-    desc.fConfig = baseProxy->config();
-    desc.fSampleCnt = 1;
-
-    GrBackendFormat format = baseProxy->backendFormat().makeTexture2D();
-    if (!format.isValid()) {
-        return nullptr;
-    }
-
-    sk_sp<GrTextureProxy> proxy =
-            proxyProvider->createMipMapProxy(format, desc, baseProxy->origin(), SkBudgeted::kYes);
-    if (!proxy) {
-        return nullptr;
-    }
-
-    // Copy the base layer to our proxy
-    sk_sp<GrSurfaceContext> sContext = ctx->priv().makeWrappedSurfaceContext(proxy);
-    SkASSERT(sContext);
-    SkAssertResult(sContext->copy(baseProxy));
-
-    return proxy;
+    return GrSurfaceProxy::Copy(ctx, baseProxy, GrMipMapped::kYes, SkBackingFit::kExact,
+                                SkBudgeted::kYes);
 }
 
 sk_sp<GrTextureProxy> GrRefCachedBitmapTextureProxy(GrRecordingContext* ctx,
diff --git a/src/gpu/geometry/GrRect.h b/src/gpu/geometry/GrRect.h
index 064d539..2a3e72b 100644
--- a/src/gpu/geometry/GrRect.h
+++ b/src/gpu/geometry/GrRect.h
@@ -82,4 +82,60 @@
     SkMatrix rectTransform = SkMatrix::MakeRectToRect(inRect, outRect, SkMatrix::kFill_ScaleToFit);
     rectTransform.mapPoints(outPts, inPts, ptCount);
 }
+
+/**
+ * Clips the srcRect and the dstPoint to the bounds of the srcSize and dstSize respectively. Returns
+ * true if the srcRect and dstRect intersect the srcRect and dst rect (dstPoint with srcRect
+ * width/height). Returns false otherwise. The clipped values are returned in clippedSrcRect and
+ * clippedDstPoint.
+ */
+static inline bool GrClipSrcRectAndDstPoint(const SkISize& dstSize,
+                                            const SkISize& srcSize,
+                                            const SkIRect& srcRect,
+                                            const SkIPoint& dstPoint,
+                                            SkIRect* clippedSrcRect,
+                                            SkIPoint* clippedDstPoint) {
+    *clippedSrcRect = srcRect;
+    *clippedDstPoint = dstPoint;
+
+    // clip the left edge to src and dst bounds, adjusting dstPoint if necessary
+    if (clippedSrcRect->fLeft < 0) {
+        clippedDstPoint->fX -= clippedSrcRect->fLeft;
+        clippedSrcRect->fLeft = 0;
+    }
+    if (clippedDstPoint->fX < 0) {
+        clippedSrcRect->fLeft -= clippedDstPoint->fX;
+        clippedDstPoint->fX = 0;
+    }
+
+    // clip the top edge to src and dst bounds, adjusting dstPoint if necessary
+    if (clippedSrcRect->fTop < 0) {
+        clippedDstPoint->fY -= clippedSrcRect->fTop;
+        clippedSrcRect->fTop = 0;
+    }
+    if (clippedDstPoint->fY < 0) {
+        clippedSrcRect->fTop -= clippedDstPoint->fY;
+        clippedDstPoint->fY = 0;
+    }
+
+    // clip the right edge to the src and dst bounds.
+    if (clippedSrcRect->fRight > srcSize.width()) {
+        clippedSrcRect->fRight = srcSize.width();
+    }
+    if (clippedDstPoint->fX + clippedSrcRect->width() > dstSize.width()) {
+        clippedSrcRect->fRight = clippedSrcRect->fLeft + dstSize.width() - clippedDstPoint->fX;
+    }
+
+    // clip the bottom edge to the src and dst bounds.
+    if (clippedSrcRect->fBottom > srcSize.height()) {
+        clippedSrcRect->fBottom = srcSize.height();
+    }
+    if (clippedDstPoint->fY + clippedSrcRect->height() > dstSize.height()) {
+        clippedSrcRect->fBottom = clippedSrcRect->fTop + dstSize.height() - clippedDstPoint->fY;
+    }
+
+    // The above clipping steps may have inverted the rect if it didn't intersect either the src or
+    // dst bounds.
+    return !clippedSrcRect->isEmpty();
+}
 #endif
diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp
index 48d3f16..b24d96e 100644
--- a/src/gpu/gl/GrGLCaps.cpp
+++ b/src/gpu/gl/GrGLCaps.cpp
@@ -2358,11 +2358,9 @@
 }
 
 bool GrGLCaps::canCopyTexSubImage(GrPixelConfig dstConfig, bool dstHasMSAARenderBuffer,
-                                  bool dstIsTextureable, bool dstIsGLTexture2D,
-                                  GrSurfaceOrigin dstOrigin,
+                                  const GrTextureType* dstTypeIfTexture,
                                   GrPixelConfig srcConfig, bool srcHasMSAARenderBuffer,
-                                  bool srcIsTextureable, bool srcIsGLTexture2D,
-                                  GrSurfaceOrigin srcOrigin) const {
+                                  const GrTextureType* srcTypeIfTexture) const {
     // Table 3.9 of the ES2 spec indicates the supported formats with CopyTexSubImage
     // and BGRA isn't in the spec. There doesn't appear to be any extension that adds it. Perhaps
     // many drivers would allow it to work, but ANGLE does not.
@@ -2378,16 +2376,15 @@
 
     // CopyTex(Sub)Image writes to a texture and we have no way of dynamically wrapping a RT in a
     // texture.
-    if (!dstIsTextureable) {
+    if (!dstTypeIfTexture) {
         return false;
     }
 
-    // Check that we could wrap the source in an FBO, that the dst is TEXTURE_2D, that no mirroring
-    // is required
+    // Check that we could wrap the source in an FBO, that the dst is not TEXTURE_EXTERNAL, that no
+    // mirroring is required
     if (this->canConfigBeFBOColorAttachment(srcConfig) &&
-        (!srcIsTextureable || srcIsGLTexture2D) &&
-        dstIsGLTexture2D &&
-        dstOrigin == srcOrigin) {
+        (!srcTypeIfTexture || *srcTypeIfTexture != GrTextureType::kExternal) &&
+        *dstTypeIfTexture != GrTextureType::kExternal) {
         return true;
     } else {
         return false;
@@ -2395,11 +2392,10 @@
 }
 
 bool GrGLCaps::canCopyAsBlit(GrPixelConfig dstConfig, int dstSampleCnt,
-                             bool dstIsTextureable, bool dstIsGLTexture2D,
-                             GrSurfaceOrigin dstOrigin,
+                             const GrTextureType* dstTypeIfTexture,
                              GrPixelConfig srcConfig, int srcSampleCnt,
-                             bool srcIsTextureable, bool srcIsGLTexture2D,
-                             GrSurfaceOrigin srcOrigin, const SkRect& srcBounds,
+                             const GrTextureType* srcTypeIfTexture,
+                             const SkRect& srcBounds, bool srcBoundsExact,
                              const SkIRect& srcRect, const SkIPoint& dstPoint) const {
     auto blitFramebufferFlags = this->blitFramebufferSupportFlags();
     if (!this->canConfigBeFBOColorAttachment(dstConfig) ||
@@ -2407,30 +2403,23 @@
         return false;
     }
 
-    if (dstIsTextureable && !dstIsGLTexture2D) {
+    if (dstTypeIfTexture && *dstTypeIfTexture == GrTextureType::kExternal) {
         return false;
     }
-    if (srcIsTextureable && !srcIsGLTexture2D) {
+    if (srcTypeIfTexture && *srcTypeIfTexture == GrTextureType::kExternal) {
         return false;
     }
 
     if (GrGLCaps::kNoSupport_BlitFramebufferFlag & blitFramebufferFlags) {
         return false;
     }
-    if (GrGLCaps::kNoScalingOrMirroring_BlitFramebufferFlag & blitFramebufferFlags) {
-        // We would mirror to compensate for origin changes. Note that copySurface is
-        // specified such that the src and dst rects are the same.
-        if (dstOrigin != srcOrigin) {
-            return false;
-        }
-    }
 
     if (GrGLCaps::kResolveMustBeFull_BlitFrambufferFlag & blitFramebufferFlags) {
         if (srcSampleCnt > 1) {
             if (1 == dstSampleCnt) {
                 return false;
             }
-            if (SkRect::Make(srcRect) != srcBounds) {
+            if (SkRect::Make(srcRect) != srcBounds || !srcBoundsExact) {
                 return false;
             }
         }
@@ -2457,9 +2446,6 @@
             if (dstPoint.fX != srcRect.fLeft || dstPoint.fY != srcRect.fTop) {
                 return false;
             }
-            if (dstOrigin != srcOrigin) {
-                return false;
-            }
         }
     }
     return true;
@@ -2485,9 +2471,6 @@
 
 bool GrGLCaps::onCanCopySurface(const GrSurfaceProxy* dst, const GrSurfaceProxy* src,
                                 const SkIRect& srcRect, const SkIPoint& dstPoint) const {
-    GrSurfaceOrigin dstOrigin = dst->origin();
-    GrSurfaceOrigin srcOrigin = src->origin();
-
     GrPixelConfig dstConfig = dst->config();
     GrPixelConfig srcConfig = src->config();
 
@@ -2502,8 +2485,7 @@
     SkASSERT((dstSampleCnt > 0) == SkToBool(dst->asRenderTargetProxy()));
     SkASSERT((srcSampleCnt > 0) == SkToBool(src->asRenderTargetProxy()));
 
-    // None of our copy methods can handle a swizzle. TODO: Make copySurfaceAsDraw handle the
-    // swizzle.
+    // None of our copy methods can handle a swizzle.
     if (this->shaderCaps()->configOutputSwizzle(src->config()) !=
         this->shaderCaps()->configOutputSwizzle(dst->config())) {
         return false;
@@ -2512,37 +2494,29 @@
     const GrTextureProxy* dstTex = dst->asTextureProxy();
     const GrTextureProxy* srcTex = src->asTextureProxy();
 
-    bool dstIsTex2D = dstTex ? (dstTex->textureType() == GrTextureType::k2D) : false;
-    bool srcIsTex2D = srcTex ? (srcTex->textureType() == GrTextureType::k2D) : false;
-
-    // One of the possible requirements for copy as blit is that the srcRect must match the bounds
-    // of the src surface. If we have a approx fit surface we can't know for sure what the src
-    // bounds will be at this time. Thus we assert that if we say we can copy as blit and the src is
-    // approx that we also can copy as draw. Therefore when it comes time to do the copy we will
-    // know we will at least be able to do it as a draw.
-#ifdef SK_DEBUG
-    if (this->canCopyAsBlit(dstConfig, dstSampleCnt, SkToBool(dstTex),
-                            dstIsTex2D, dstOrigin, srcConfig, srcSampleCnt, SkToBool(srcTex),
-                            srcIsTex2D, srcOrigin, src->getBoundsRect(), srcRect, dstPoint) &&
-        !src->priv().isExact()) {
-        SkASSERT(this->canCopyAsDraw(dstConfig, SkToBool(srcTex)));
+    GrTextureType dstTexType;
+    GrTextureType* dstTexTypePtr = nullptr;
+    GrTextureType srcTexType;
+    GrTextureType* srcTexTypePtr = nullptr;
+    if (dstTex) {
+        dstTexType = dstTex->textureType();
+        dstTexTypePtr = &dstTexType;
     }
-#endif
+    if (srcTex) {
+        srcTexType = srcTex->textureType();
+        srcTexTypePtr = &srcTexType;
+    }
 
-    return this->canCopyTexSubImage(dstConfig, has_msaa_render_buffer(dst, *this),
-                                    SkToBool(dstTex), dstIsTex2D, dstOrigin,
-                                    srcConfig, has_msaa_render_buffer(src, *this),
-                                    SkToBool(srcTex), srcIsTex2D, srcOrigin) ||
-           this->canCopyAsBlit(dstConfig, dstSampleCnt, SkToBool(dstTex),
-                               dstIsTex2D, dstOrigin, srcConfig, srcSampleCnt, SkToBool(srcTex),
-                               srcIsTex2D, srcOrigin, src->getBoundsRect(), srcRect,
-                               dstPoint) ||
+    return this->canCopyTexSubImage(dstConfig, has_msaa_render_buffer(dst, *this), dstTexTypePtr,
+                                    srcConfig, has_msaa_render_buffer(src, *this), srcTexTypePtr) ||
+           this->canCopyAsBlit(dstConfig, dstSampleCnt, dstTexTypePtr, srcConfig, srcSampleCnt,
+                               srcTexTypePtr, src->getBoundsRect(), src->priv().isExact(),
+                               srcRect, dstPoint) ||
            this->canCopyAsDraw(dstConfig, SkToBool(srcTex));
 }
 
 bool GrGLCaps::initDescForDstCopy(const GrRenderTargetProxy* src, GrSurfaceDesc* desc,
-                                  GrSurfaceOrigin* origin, bool* rectsMustMatch,
-                                  bool* disallowSubrect) const {
+                                  bool* rectsMustMatch, bool* disallowSubrect) const {
     // By default, we don't require rects to match.
     *rectsMustMatch = false;
 
@@ -2552,7 +2526,6 @@
     // If the src is a texture, we can implement the blit as a draw assuming the config is
     // renderable.
     if (src->asTextureProxy() && !this->isConfigRenderable(src->config())) {
-        *origin = kBottomLeft_GrSurfaceOrigin;
         desc->fFlags = kRenderTarget_GrSurfaceFlag;
         desc->fConfig = src->config();
         return true;
@@ -2573,7 +2546,6 @@
     // possible and we return false to fallback to creating a render target dst for render-to-
     // texture. This code prefers CopyTexSubImage to fbo blit and avoids triggering temporary fbo
     // creation. It isn't clear that avoiding temporary fbo creation is actually optimal.
-    GrSurfaceOrigin originForBlitFramebuffer = kTopLeft_GrSurfaceOrigin;
     bool rectsMustMatchForBlitFramebuffer = false;
     bool disallowSubrectForBlitFramebuffer = false;
     if (src->numColorSamples() > 1 &&
@@ -2581,14 +2553,9 @@
         rectsMustMatchForBlitFramebuffer = true;
         disallowSubrectForBlitFramebuffer = true;
         // Mirroring causes rects to mismatch later, don't allow it.
-        originForBlitFramebuffer = src->origin();
     } else if (src->numColorSamples() > 1 && (this->blitFramebufferSupportFlags() &
                                               kRectsMustMatchForMSAASrc_BlitFramebufferFlag)) {
         rectsMustMatchForBlitFramebuffer = true;
-        // Mirroring causes rects to mismatch later, don't allow it.
-        originForBlitFramebuffer = src->origin();
-    } else if (this->blitFramebufferSupportFlags() & kNoScalingOrMirroring_BlitFramebufferFlag) {
-        originForBlitFramebuffer = src->origin();
     }
 
     // Check for format issues with glCopyTexSubImage2D
@@ -2596,7 +2563,6 @@
         // glCopyTexSubImage2D doesn't work with this config. If the bgra can be used with fbo blit
         // then we set up for that, otherwise fail.
         if (this->canConfigBeFBOColorAttachment(kBGRA_8888_GrPixelConfig)) {
-            *origin = originForBlitFramebuffer;
             desc->fConfig = kBGRA_8888_GrPixelConfig;
             *rectsMustMatch = rectsMustMatchForBlitFramebuffer;
             *disallowSubrect = disallowSubrectForBlitFramebuffer;
@@ -2612,7 +2578,6 @@
             // It's illegal to call CopyTexSubImage2D on a MSAA renderbuffer. Set up for FBO
             // blit or fail.
             if (this->canConfigBeFBOColorAttachment(src->config())) {
-                *origin = originForBlitFramebuffer;
                 desc->fConfig = src->config();
                 *rectsMustMatch = rectsMustMatchForBlitFramebuffer;
                 *disallowSubrect = disallowSubrectForBlitFramebuffer;
@@ -2623,7 +2588,6 @@
     }
 
     // We'll do a CopyTexSubImage. Make the dst a plain old texture.
-    *origin = src->origin();
     desc->fConfig = src->config();
     desc->fFlags = kNone_GrSurfaceFlags;
     return true;
diff --git a/src/gpu/gl/GrGLCaps.h b/src/gpu/gl/GrGLCaps.h
index a2e2948..fe1a9f2 100644
--- a/src/gpu/gl/GrGLCaps.h
+++ b/src/gpu/gl/GrGLCaps.h
@@ -388,21 +388,18 @@
     }
 
     bool canCopyTexSubImage(GrPixelConfig dstConfig, bool dstHasMSAARenderBuffer,
-                            bool dstIsTextureable, bool dstIsGLTexture2D,
-                            GrSurfaceOrigin dstOrigin,
+                            const GrTextureType* dstTypeIfTexture,
                             GrPixelConfig srcConfig, bool srcHasMSAARenderBuffer,
-                            bool srcIsTextureable, bool srcIsGLTexture2D,
-                            GrSurfaceOrigin srcOrigin) const;
+                            const GrTextureType* srcTypeIfTexture) const;
     bool canCopyAsBlit(GrPixelConfig dstConfig, int dstSampleCnt,
-                       bool dstIsTextureable, bool dstIsGLTexture2D,
-                       GrSurfaceOrigin dstOrigin,
+                       const GrTextureType* dstTypeIfTexture,
                        GrPixelConfig srcConfig, int srcSampleCnt,
-                       bool srcIsTextureable, bool srcIsGLTexture2D,
-                       GrSurfaceOrigin srcOrigin, const SkRect& srcBounds,
+                        const GrTextureType* srcTypeIfTexture,
+                       const SkRect& srcBounds, bool srcBoundsExact,
                        const SkIRect& srcRect, const SkIPoint& dstPoint) const;
     bool canCopyAsDraw(GrPixelConfig dstConfig, bool srcIsTextureable) const;
 
-    bool initDescForDstCopy(const GrRenderTargetProxy* src, GrSurfaceDesc* desc, GrSurfaceOrigin*,
+    bool initDescForDstCopy(const GrRenderTargetProxy* src, GrSurfaceDesc* desc,
                             bool* rectsMustMatch, bool* disallowSubrect) const override;
 
     bool programBinarySupport() const { return fProgramBinarySupport; }
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index 98a3743..f344e2e 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -3230,12 +3230,11 @@
 }
 
 // Determines whether glBlitFramebuffer could be used between src and dst by onCopySurface.
-static inline bool can_blit_framebuffer_for_copy_surface(
-                                                const GrSurface* dst, GrSurfaceOrigin dstOrigin,
-                                                const GrSurface* src, GrSurfaceOrigin srcOrigin,
-                                                const SkIRect& srcRect,
-                                                const SkIPoint& dstPoint,
-                                                const GrGLCaps& caps) {
+static inline bool can_blit_framebuffer_for_copy_surface(const GrSurface* dst,
+                                                         const GrSurface* src,
+                                                         const SkIRect& srcRect,
+                                                         const SkIPoint& dstPoint,
+                                                         const GrGLCaps& caps) {
     int dstSampleCnt = 0;
     int srcSampleCnt = 0;
     if (const GrRenderTarget* rt = dst->asRenderTarget()) {
@@ -3250,12 +3249,22 @@
     const GrGLTexture* dstTex = static_cast<const GrGLTexture*>(dst->asTexture());
     const GrGLTexture* srcTex = static_cast<const GrGLTexture*>(src->asTexture());
 
-    bool dstIsGLTexture2D = dstTex ? GR_GL_TEXTURE_2D == dstTex->target() : false;
-    bool srcIsGLTexture2D = srcTex ? GR_GL_TEXTURE_2D == srcTex->target() : false;
+    GrTextureType dstTexType;
+    GrTextureType* dstTexTypePtr = nullptr;
+    GrTextureType srcTexType;
+    GrTextureType* srcTexTypePtr = nullptr;
+    if (dstTex) {
+        dstTexType = dstTex->texturePriv().textureType();
+        dstTexTypePtr = &dstTexType;
+    }
+    if (srcTex) {
+        srcTexType = srcTex->texturePriv().textureType();
+        srcTexTypePtr = &srcTexType;
+    }
 
-    return caps.canCopyAsBlit(dst->config(), dstSampleCnt, SkToBool(dstTex), dstIsGLTexture2D,
-                              dstOrigin, src->config(), srcSampleCnt, SkToBool(srcTex),
-                              srcIsGLTexture2D, srcOrigin, src->getBoundsRect(), srcRect, dstPoint);
+    return caps.canCopyAsBlit(dst->config(), dstSampleCnt, dstTexTypePtr,
+                              src->config(), srcSampleCnt, srcTexTypePtr,
+                              src->getBoundsRect(), true, srcRect, dstPoint);
 }
 
 static bool rt_has_msaa_render_buffer(const GrGLRenderTarget* rt, const GrGLCaps& glCaps) {
@@ -3266,8 +3275,7 @@
     return rt->numColorSamples() > 1 && glCaps.usesMSAARenderBuffers() && rt->renderFBOID() != 0;
 }
 
-static inline bool can_copy_texsubimage(const GrSurface* dst, GrSurfaceOrigin dstOrigin,
-                                        const GrSurface* src, GrSurfaceOrigin srcOrigin,
+static inline bool can_copy_texsubimage(const GrSurface* dst, const GrSurface* src,
                                         const GrGLCaps& caps) {
 
     const GrGLRenderTarget* dstRT = static_cast<const GrGLRenderTarget*>(dst->asRenderTarget());
@@ -3278,13 +3286,21 @@
     bool dstHasMSAARenderBuffer = dstRT ? rt_has_msaa_render_buffer(dstRT, caps) : false;
     bool srcHasMSAARenderBuffer = srcRT ? rt_has_msaa_render_buffer(srcRT, caps) : false;
 
-    bool dstIsGLTexture2D = dstTex ? GR_GL_TEXTURE_2D == dstTex->target() : false;
-    bool srcIsGLTexture2D = srcTex ? GR_GL_TEXTURE_2D == srcTex->target() : false;
+    GrTextureType dstTexType;
+    GrTextureType* dstTexTypePtr = nullptr;
+    GrTextureType srcTexType;
+    GrTextureType* srcTexTypePtr = nullptr;
+    if (dstTex) {
+        dstTexType = dstTex->texturePriv().textureType();
+        dstTexTypePtr = &dstTexType;
+    }
+    if (srcTex) {
+        srcTexType = srcTex->texturePriv().textureType();
+        srcTexTypePtr = &srcTexType;
+    }
 
-    return caps.canCopyTexSubImage(dst->config(), dstHasMSAARenderBuffer, SkToBool(dstTex),
-                                   dstIsGLTexture2D, dstOrigin, src->config(),
-                                   srcHasMSAARenderBuffer, SkToBool(srcTex), srcIsGLTexture2D,
-                                   srcOrigin);
+    return caps.canCopyTexSubImage(dst->config(), dstHasMSAARenderBuffer, dstTexTypePtr,
+                                   src->config(), srcHasMSAARenderBuffer, srcTexTypePtr);
 }
 
 // If a temporary FBO was created, its non-zero ID is returned.
@@ -3375,38 +3391,34 @@
     }
 }
 
-bool GrGLGpu::onCopySurface(GrSurface* dst, GrSurfaceOrigin dstOrigin,
-                            GrSurface* src, GrSurfaceOrigin srcOrigin,
-                            const SkIRect& srcRect, const SkIPoint& dstPoint,
-                            bool canDiscardOutsideDstRect) {
-    // None of our copy methods can handle a swizzle. TODO: Make copySurfaceAsDraw handle the
-    // swizzle.
+bool GrGLGpu::onCopySurface(GrSurface* dst, GrSurface* src, const SkIRect& srcRect,
+                            const SkIPoint& dstPoint, bool canDiscardOutsideDstRect) {
+    // None of our copy methods can handle a swizzle.
     if (this->caps()->shaderCaps()->configOutputSwizzle(src->config()) !=
         this->caps()->shaderCaps()->configOutputSwizzle(dst->config())) {
         return false;
     }
+
     // Don't prefer copying as a draw if the dst doesn't already have a FBO object.
     // This implicitly handles this->glCaps().useDrawInsteadOfAllRenderTargetWrites().
     bool preferCopy = SkToBool(dst->asRenderTarget());
     if (preferCopy && this->glCaps().canCopyAsDraw(dst->config(), SkToBool(src->asTexture()))) {
-        if (this->copySurfaceAsDraw(dst, dstOrigin, src, srcOrigin, srcRect, dstPoint)) {
+        if (this->copySurfaceAsDraw(dst, src, srcRect, dstPoint)) {
             return true;
         }
     }
 
-    if (can_copy_texsubimage(dst, dstOrigin, src, srcOrigin, this->glCaps())) {
-        this->copySurfaceAsCopyTexSubImage(dst, dstOrigin, src, srcOrigin, srcRect, dstPoint);
+    if (can_copy_texsubimage(dst, src, this->glCaps())) {
+        this->copySurfaceAsCopyTexSubImage(dst, src, srcRect, dstPoint);
         return true;
     }
 
-    if (can_blit_framebuffer_for_copy_surface(dst, dstOrigin, src, srcOrigin,
-                                              srcRect, dstPoint, this->glCaps())) {
-        return this->copySurfaceAsBlitFramebuffer(dst, dstOrigin, src, srcOrigin,
-                                                  srcRect, dstPoint);
+    if (can_blit_framebuffer_for_copy_surface(dst, src, srcRect, dstPoint, this->glCaps())) {
+        return this->copySurfaceAsBlitFramebuffer(dst, src, srcRect, dstPoint);
     }
 
     if (!preferCopy && this->glCaps().canCopyAsDraw(dst->config(), SkToBool(src->asTexture()))) {
-        if (this->copySurfaceAsDraw(dst, dstOrigin, src, srcOrigin, srcRect, dstPoint)) {
+        if (this->copySurfaceAsDraw(dst, src, srcRect, dstPoint)) {
             return true;
         }
     }
@@ -3682,44 +3694,32 @@
     return true;
 }
 
-bool GrGLGpu::copySurfaceAsDraw(GrSurface* dst, GrSurfaceOrigin dstOrigin,
-                                GrSurface* src, GrSurfaceOrigin srcOrigin,
-                                const SkIRect& srcRect,
+bool GrGLGpu::copySurfaceAsDraw(GrSurface* dst, GrSurface* src, const SkIRect& srcRect,
                                 const SkIPoint& dstPoint) {
     GrGLTexture* srcTex = static_cast<GrGLTexture*>(src->asTexture());
     int progIdx = TextureToCopyProgramIdx(srcTex);
-
     if (!this->glCaps().canConfigBeFBOColorAttachment(dst->config())) {
         return false;
     }
-
     if (!fCopyPrograms[progIdx].fProgram) {
         if (!this->createCopyProgram(srcTex)) {
             SkDebugf("Failed to create copy program.\n");
             return false;
         }
     }
-
     int w = srcRect.width();
     int h = srcRect.height();
-
     this->bindTexture(0, GrSamplerState::ClampNearest(), srcTex);
-
     this->bindSurfaceFBOForPixelOps(dst, GR_GL_FRAMEBUFFER, kDst_TempFBOTarget);
     this->flushViewport(dst->width(), dst->height());
     fHWBoundRenderTargetUniqueID.makeInvalid();
-
     SkIRect dstRect = SkIRect::MakeXYWH(dstPoint.fX, dstPoint.fY, w, h);
-
     this->flushProgram(fCopyPrograms[progIdx].fProgram);
-
     fHWVertexArrayState.setVertexArrayID(this, 0);
-
     GrGLAttribArrayState* attribs = fHWVertexArrayState.bindInternalVertexArray(this);
     attribs->enableVertexArrays(this, 1);
     attribs->set(this, 0, fCopyProgramArrayBuffer.get(), kFloat2_GrVertexAttribType,
                  kFloat2_GrSLType, 2 * sizeof(GrGLfloat), 0);
-
     // dst rect edges in NDC (-1 to 1)
     int dw = dst->width();
     int dh = dst->height();
@@ -3727,21 +3727,12 @@
     GrGLfloat dx1 = 2.f * (dstPoint.fX + w) / dw - 1.f;
     GrGLfloat dy0 = 2.f * dstPoint.fY / dh - 1.f;
     GrGLfloat dy1 = 2.f * (dstPoint.fY + h) / dh - 1.f;
-    if (kBottomLeft_GrSurfaceOrigin == dstOrigin) {
-        dy0 = -dy0;
-        dy1 = -dy1;
-    }
-
     GrGLfloat sx0 = (GrGLfloat)srcRect.fLeft;
     GrGLfloat sx1 = (GrGLfloat)(srcRect.fLeft + w);
     GrGLfloat sy0 = (GrGLfloat)srcRect.fTop;
     GrGLfloat sy1 = (GrGLfloat)(srcRect.fTop + h);
     int sw = src->width();
     int sh = src->height();
-    if (kBottomLeft_GrSurfaceOrigin == srcOrigin) {
-        sy0 = sh - sy0;
-        sy1 = sh - sy1;
-    }
     if (srcTex->texturePriv().textureType() != GrTextureType::kRectangle) {
         // src rect edges in normalized texture space (0 to 1)
         sx0 /= sw;
@@ -3749,12 +3740,10 @@
         sy0 /= sh;
         sy1 /= sh;
     }
-
     GL_CALL(Uniform4f(fCopyPrograms[progIdx].fPosXformUniform, dx1 - dx0, dy1 - dy0, dx0, dy0));
     GL_CALL(Uniform4f(fCopyPrograms[progIdx].fTexCoordXformUniform,
                       sx1 - sx0, sy1 - sy0, sx0, sy0));
     GL_CALL(Uniform1i(fCopyPrograms[progIdx].fTextureUniform, 0));
-
     GrXferProcessor::BlendInfo blendInfo;
     blendInfo.reset();
     this->flushBlend(blendInfo, GrSwizzle::RGBA());
@@ -3766,50 +3755,37 @@
     if (this->glCaps().srgbWriteControl()) {
         this->flushFramebufferSRGB(true);
     }
-
     GL_CALL(DrawArrays(GR_GL_TRIANGLE_STRIP, 0, 4));
     this->unbindTextureFBOForPixelOps(GR_GL_FRAMEBUFFER, dst);
-    this->didWriteToSurface(dst, dstOrigin, &dstRect);
-
+    // The rect is already in device space so we pass in kTopLeft so no flip is done.
+    this->didWriteToSurface(dst, kTopLeft_GrSurfaceOrigin, &dstRect);
     return true;
 }
 
-void GrGLGpu::copySurfaceAsCopyTexSubImage(GrSurface* dst, GrSurfaceOrigin dstOrigin,
-                                           GrSurface* src, GrSurfaceOrigin srcOrigin,
-                                           const SkIRect& srcRect,
+void GrGLGpu::copySurfaceAsCopyTexSubImage(GrSurface* dst, GrSurface* src, const SkIRect& srcRect,
                                            const SkIPoint& dstPoint) {
-    SkASSERT(can_copy_texsubimage(dst, dstOrigin, src, srcOrigin, this->glCaps()));
+    SkASSERT(can_copy_texsubimage(dst, src, this->glCaps()));
     this->bindSurfaceFBOForPixelOps(src, GR_GL_FRAMEBUFFER, kSrc_TempFBOTarget);
     GrGLTexture* dstTex = static_cast<GrGLTexture *>(dst->asTexture());
     SkASSERT(dstTex);
     // We modified the bound FBO
     fHWBoundRenderTargetUniqueID.makeInvalid();
-    GrGLIRect srcGLRect;
-    srcGLRect.setRelativeTo(src->height(), srcRect, srcOrigin);
 
     this->bindTextureToScratchUnit(dstTex->target(), dstTex->textureID());
-    GrGLint dstY;
-    if (kBottomLeft_GrSurfaceOrigin == dstOrigin) {
-        dstY = dst->height() - (dstPoint.fY + srcGLRect.fHeight);
-    } else {
-        dstY = dstPoint.fY;
-    }
     GL_CALL(CopyTexSubImage2D(dstTex->target(), 0,
-                              dstPoint.fX, dstY,
-                              srcGLRect.fLeft, srcGLRect.fBottom,
-                              srcGLRect.fWidth, srcGLRect.fHeight));
+                              dstPoint.fX, dstPoint.fY,
+                              srcRect.fLeft, srcRect.fTop,
+                              srcRect.width(), srcRect.height()));
     this->unbindTextureFBOForPixelOps(GR_GL_FRAMEBUFFER, src);
     SkIRect dstRect = SkIRect::MakeXYWH(dstPoint.fX, dstPoint.fY,
                                         srcRect.width(), srcRect.height());
-    this->didWriteToSurface(dst, dstOrigin, &dstRect);
+    // The rect is already in device space so we pass in kTopLeft so no flip is done.
+    this->didWriteToSurface(dst, kTopLeft_GrSurfaceOrigin, &dstRect);
 }
 
-bool GrGLGpu::copySurfaceAsBlitFramebuffer(GrSurface* dst, GrSurfaceOrigin dstOrigin,
-                                           GrSurface* src, GrSurfaceOrigin srcOrigin,
-                                           const SkIRect& srcRect,
+bool GrGLGpu::copySurfaceAsBlitFramebuffer(GrSurface* dst, GrSurface* src, const SkIRect& srcRect,
                                            const SkIPoint& dstPoint) {
-    SkASSERT(can_blit_framebuffer_for_copy_surface(dst, dstOrigin, src, srcOrigin,
-                                                   srcRect, dstPoint, this->glCaps()));
+    SkASSERT(can_blit_framebuffer_for_copy_surface(dst, src, srcRect, dstPoint, this->glCaps()));
     SkIRect dstRect = SkIRect::MakeXYWH(dstPoint.fX, dstPoint.fY,
                                         srcRect.width(), srcRect.height());
     if (dst == src) {
@@ -3822,37 +3798,25 @@
     this->bindSurfaceFBOForPixelOps(src, GR_GL_READ_FRAMEBUFFER, kSrc_TempFBOTarget);
     // We modified the bound FBO
     fHWBoundRenderTargetUniqueID.makeInvalid();
-    GrGLIRect srcGLRect;
-    GrGLIRect dstGLRect;
-    srcGLRect.setRelativeTo(src->height(), srcRect, srcOrigin);
-    dstGLRect.setRelativeTo(dst->height(), dstRect, dstOrigin);
 
     // BlitFrameBuffer respects the scissor, so disable it.
     this->disableScissor();
     this->disableWindowRectangles();
 
-    GrGLint srcY0;
-    GrGLint srcY1;
-    // Does the blit need to y-mirror or not?
-    if (srcOrigin == dstOrigin) {
-        srcY0 = srcGLRect.fBottom;
-        srcY1 = srcGLRect.fBottom + srcGLRect.fHeight;
-    } else {
-        srcY0 = srcGLRect.fBottom + srcGLRect.fHeight;
-        srcY1 = srcGLRect.fBottom;
-    }
-    GL_CALL(BlitFramebuffer(srcGLRect.fLeft,
-                            srcY0,
-                            srcGLRect.fLeft + srcGLRect.fWidth,
-                            srcY1,
-                            dstGLRect.fLeft,
-                            dstGLRect.fBottom,
-                            dstGLRect.fLeft + dstGLRect.fWidth,
-                            dstGLRect.fBottom + dstGLRect.fHeight,
+    GL_CALL(BlitFramebuffer(srcRect.fLeft,
+                            srcRect.fTop,
+                            srcRect.fRight,
+                            srcRect.fBottom,
+                            dstRect.fLeft,
+                            dstRect.fTop,
+                            dstRect.fRight,
+                            dstRect.fBottom,
                             GR_GL_COLOR_BUFFER_BIT, GR_GL_NEAREST));
     this->unbindTextureFBOForPixelOps(GR_GL_DRAW_FRAMEBUFFER, dst);
     this->unbindTextureFBOForPixelOps(GR_GL_READ_FRAMEBUFFER, src);
-    this->didWriteToSurface(dst, dstOrigin, &dstRect);
+
+    // The rect is already in device space so we pass in kTopLeft so no flip is done.
+    this->didWriteToSurface(dst, kTopLeft_GrSurfaceOrigin, &dstRect);
     return true;
 }
 
diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h
index 5a6c6d8..ac9bf9a 100644
--- a/src/gpu/gl/GrGLGpu.h
+++ b/src/gpu/gl/GrGLGpu.h
@@ -255,10 +255,8 @@
 
     bool onRegenerateMipMapLevels(GrTexture*) override;
 
-    bool onCopySurface(GrSurface* dst, GrSurfaceOrigin dstOrigin,
-                       GrSurface* src, GrSurfaceOrigin srcOrigin,
-                       const SkIRect& srcRect, const SkIPoint& dstPoint,
-                       bool canDiscardOutsideDstRect) override;
+    bool onCopySurface(GrSurface* dst, GrSurface* src, const SkIRect& srcRect,
+                       const SkIPoint& dstPoint, bool canDiscardOutsideDstRect) override;
 
     // binds texture unit in GL
     void setTextureUnit(int unitIdx);
@@ -301,15 +299,12 @@
 
     bool waitSync(GrGLsync, uint64_t timeout, bool flush);
 
-    bool copySurfaceAsDraw(GrSurface* dst, GrSurfaceOrigin dstOrigin,
-                           GrSurface* src, GrSurfaceOrigin srcOrigin,
-                           const SkIRect& srcRect, const SkIPoint& dstPoint);
-    void copySurfaceAsCopyTexSubImage(GrSurface* dst, GrSurfaceOrigin dstOrigin,
-                                      GrSurface* src, GrSurfaceOrigin srcOrigin,
-                                      const SkIRect& srcRect, const SkIPoint& dstPoint);
-    bool copySurfaceAsBlitFramebuffer(GrSurface* dst, GrSurfaceOrigin dstOrigin,
-                                      GrSurface* src, GrSurfaceOrigin srcOrigin,
-                                      const SkIRect& srcRect, const SkIPoint& dstPoint);
+    bool copySurfaceAsDraw(GrSurface* dst, GrSurface* src, const SkIRect& srcRect,
+                           const SkIPoint& dstPoint);
+    void copySurfaceAsCopyTexSubImage(GrSurface* dst, GrSurface* src, const SkIRect& srcRect,
+                                      const SkIPoint& dstPoint);
+    bool copySurfaceAsBlitFramebuffer(GrSurface* dst, GrSurface* src, const SkIRect& srcRect,
+                                      const SkIPoint& dstPoint);
 
     static bool BlendCoeffReferencesConstant(GrBlendCoeff coeff);
 
diff --git a/src/gpu/gl/GrGLGpuCommandBuffer.h b/src/gpu/gl/GrGLGpuCommandBuffer.h
index 0f9370f..0ec79c6 100644
--- a/src/gpu/gl/GrGLGpuCommandBuffer.h
+++ b/src/gpu/gl/GrGLGpuCommandBuffer.h
@@ -21,9 +21,8 @@
 public:
     GrGLGpuTextureCommandBuffer(GrGLGpu* gpu) : fGpu(gpu) {}
 
-    void copy(GrSurface* src, GrSurfaceOrigin srcOrigin, const SkIRect& srcRect,
-              const SkIPoint& dstPoint) override {
-        fGpu->copySurface(fTexture, fOrigin, src, srcOrigin, srcRect, dstPoint);
+    void copy(GrSurface* src, const SkIRect& srcRect, const SkIPoint& dstPoint) override {
+        fGpu->copySurface(fTexture, src, srcRect, dstPoint);
     }
 
     void transferFrom(const SkIRect& srcRect, GrColorType bufferColorType,
@@ -68,9 +67,8 @@
         state->doUpload(upload);
     }
 
-    void copy(GrSurface* src, GrSurfaceOrigin srcOrigin, const SkIRect& srcRect,
-              const SkIPoint& dstPoint) override {
-        fGpu->copySurface(fRenderTarget, fOrigin, src, srcOrigin, srcRect, dstPoint);
+    void copy(GrSurface* src, const SkIRect& srcRect, const SkIPoint& dstPoint) override {
+        fGpu->copySurface(fRenderTarget, src,srcRect, dstPoint);
     }
 
     void transferFrom(const SkIRect& srcRect, GrColorType bufferColorType,
diff --git a/src/gpu/mock/GrMockCaps.h b/src/gpu/mock/GrMockCaps.h
index b8059d3..c3806d1 100644
--- a/src/gpu/mock/GrMockCaps.h
+++ b/src/gpu/mock/GrMockCaps.h
@@ -70,7 +70,7 @@
 
     bool surfaceSupportsReadPixels(const GrSurface*) const override { return true; }
 
-    bool initDescForDstCopy(const GrRenderTargetProxy* src, GrSurfaceDesc* desc, GrSurfaceOrigin*,
+    bool initDescForDstCopy(const GrRenderTargetProxy* src, GrSurfaceDesc* desc,
                             bool* rectsMustMatch, bool* disallowSubrect) const override {
         return false;
     }
diff --git a/src/gpu/mock/GrMockGpu.h b/src/gpu/mock/GrMockGpu.h
index cf49972..de3af71 100644
--- a/src/gpu/mock/GrMockGpu.h
+++ b/src/gpu/mock/GrMockGpu.h
@@ -101,8 +101,7 @@
                               GrColorType, GrGpuBuffer* transferBuffer, size_t offset) override {
         return true;
     }
-    bool onCopySurface(GrSurface* dst, GrSurfaceOrigin dstOrigin, GrSurface* src,
-                       GrSurfaceOrigin srcOrigin, const SkIRect& srcRect,
+    bool onCopySurface(GrSurface* dst, GrSurface* src, const SkIRect& srcRect,
                        const SkIPoint& dstPoint, bool canDiscardOutsideDstRect) override {
         return true;
     }
diff --git a/src/gpu/mock/GrMockGpuCommandBuffer.h b/src/gpu/mock/GrMockGpuCommandBuffer.h
index 9e2905e..7532bdc 100644
--- a/src/gpu/mock/GrMockGpuCommandBuffer.h
+++ b/src/gpu/mock/GrMockGpuCommandBuffer.h
@@ -19,8 +19,7 @@
 
     ~GrMockGpuTextureCommandBuffer() override {}
 
-    void copy(GrSurface* src, GrSurfaceOrigin srcOrigin, const SkIRect& srcRect,
-              const SkIPoint& dstPoint) override {}
+    void copy(GrSurface* src, const SkIRect& srcRect, const SkIPoint& dstPoint) override {}
     void transferFrom(const SkIRect& srcRect, GrColorType bufferColorType,
                       GrGpuBuffer* transferBuffer, size_t offset) override {}
     void insertEventMarker(const char*) override {}
@@ -42,8 +41,7 @@
     void insertEventMarker(const char*) override {}
     void begin() override {}
     void end() override {}
-    void copy(GrSurface* src, GrSurfaceOrigin srcOrigin, const SkIRect& srcRect,
-              const SkIPoint& dstPoint) override {}
+    void copy(GrSurface* src, const SkIRect& srcRect, const SkIPoint& dstPoint) override {}
     void transferFrom(const SkIRect& srcRect, GrColorType bufferColorType,
                       GrGpuBuffer* transferBuffer, size_t offset) override {}
 
diff --git a/src/gpu/mtl/GrMtlCaps.h b/src/gpu/mtl/GrMtlCaps.h
index 7939dc2..2ee1d88 100644
--- a/src/gpu/mtl/GrMtlCaps.h
+++ b/src/gpu/mtl/GrMtlCaps.h
@@ -46,18 +46,11 @@
         return fPreferredStencilFormat;
     }
 
-    bool canCopyAsBlit(GrPixelConfig dstConfig, int dstSampleCount, GrSurfaceOrigin dstOrigin,
-                       GrPixelConfig srcConfig, int srcSampleCount, GrSurfaceOrigin srcOrigin,
-                       const SkIRect& srcRect, const SkIPoint& dstPoint,
+    bool canCopyAsBlit(GrPixelConfig dstConfig, int dstSampleCount, GrPixelConfig srcConfig,
+                       int srcSampleCount, const SkIRect& srcRect, const SkIPoint& dstPoint,
                        bool areDstSrcSameObj) const;
 
-    bool canCopyAsDraw(GrPixelConfig dstConfig, bool dstIsRenderable,
-                       GrPixelConfig srcConfig, bool srcIsTextureable) const;
-
-    bool canCopyAsDrawThenBlit(GrPixelConfig dstConfig, GrPixelConfig srcConfig,
-                               bool srcIsTextureable) const;
-
-    bool initDescForDstCopy(const GrRenderTargetProxy* src, GrSurfaceDesc* desc, GrSurfaceOrigin*,
+    bool initDescForDstCopy(const GrRenderTargetProxy* src, GrSurfaceDesc* desc,
                             bool* rectsMustMatch, bool* disallowSubrect) const override {
         return false;
     }
diff --git a/src/gpu/mtl/GrMtlCaps.mm b/src/gpu/mtl/GrMtlCaps.mm
index 25cd522..696350b 100644
--- a/src/gpu/mtl/GrMtlCaps.mm
+++ b/src/gpu/mtl/GrMtlCaps.mm
@@ -119,9 +119,7 @@
 }
 
 bool GrMtlCaps::canCopyAsBlit(GrPixelConfig dstConfig, int dstSampleCount,
-                              GrSurfaceOrigin dstOrigin,
                               GrPixelConfig srcConfig, int srcSampleCount,
-                              GrSurfaceOrigin srcOrigin,
                               const SkIRect& srcRect, const SkIPoint& dstPoint,
                               bool areDstSrcSameObj) const {
     if (dstConfig != srcConfig) {
@@ -130,9 +128,6 @@
     if ((dstSampleCount > 1 || srcSampleCount > 1) && (dstSampleCount != srcSampleCount)) {
         return false;
     }
-    if (dstOrigin != srcOrigin) {
-        return false;
-    }
     if (areDstSrcSameObj) {
         SkIRect dstRect = SkIRect::MakeXYWH(dstPoint.x(), dstPoint.y(),
                                             srcRect.width(), srcRect.height());
@@ -143,38 +138,8 @@
     return true;
 }
 
-bool GrMtlCaps::canCopyAsDraw(GrPixelConfig dstConfig, bool dstIsRenderable,
-                              GrPixelConfig srcConfig, bool srcIsTextureable) const {
-    // TODO: Make copySurfaceAsDraw handle the swizzle
-    if (this->shaderCaps()->configOutputSwizzle(srcConfig) !=
-        this->shaderCaps()->configOutputSwizzle(dstConfig)) {
-        return false;
-    }
-
-    if (!dstIsRenderable || !srcIsTextureable) {
-        return false;
-    }
-    return true;
-}
-
-bool GrMtlCaps::canCopyAsDrawThenBlit(GrPixelConfig dstConfig, GrPixelConfig srcConfig,
-                                      bool srcIsTextureable) const {
-    // TODO: Make copySurfaceAsDraw handle the swizzle
-    if (this->shaderCaps()->configOutputSwizzle(srcConfig) !=
-        this->shaderCaps()->configOutputSwizzle(dstConfig)) {
-        return false;
-    }
-    if (!srcIsTextureable) {
-        return false;
-    }
-    return true;
-}
-
 bool GrMtlCaps::onCanCopySurface(const GrSurfaceProxy* dst, const GrSurfaceProxy* src,
                                  const SkIRect& srcRect, const SkIPoint& dstPoint) const {
-    GrSurfaceOrigin dstOrigin = dst->origin();
-    GrSurfaceOrigin srcOrigin = src->origin();
-
     int dstSampleCnt = 0;
     int srcSampleCnt = 0;
     if (const GrRenderTargetProxy* rtProxy = dst->asRenderTargetProxy()) {
@@ -186,13 +151,8 @@
     SkASSERT((dstSampleCnt > 0) == SkToBool(dst->asRenderTargetProxy()));
     SkASSERT((srcSampleCnt > 0) == SkToBool(src->asRenderTargetProxy()));
 
-    return this->canCopyAsBlit(dst->config(), dstSampleCnt, dstOrigin,
-                               src->config(), srcSampleCnt, srcOrigin,
-                               srcRect, dstPoint, dst == src) ||
-           this->canCopyAsDraw(dst->config(), SkToBool(dst->asRenderTargetProxy()),
-                               src->config(), SkToBool(src->asTextureProxy())) ||
-           this->canCopyAsDrawThenBlit(dst->config(), src->config(),
-                                       SkToBool(src->asTextureProxy()));
+    return this->canCopyAsBlit(dst->config(), dstSampleCnt, src->config(), srcSampleCnt, srcRect,
+                               dstPoint, dst == src);
 }
 
 void GrMtlCaps::initGrCaps(const id<MTLDevice> device) {
diff --git a/src/gpu/mtl/GrMtlCopyManager.h b/src/gpu/mtl/GrMtlCopyManager.h
deleted file mode 100644
index 68d1a22..0000000
--- a/src/gpu/mtl/GrMtlCopyManager.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright 2018 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
-*/
-
-#ifndef GrMtlCopyManager_DEFINED
-#define GrMtlCopyManager_DEFINED
-
-#include "include/gpu/GrTypes.h"
-
-#import <metal/metal.h>
-
-class GrMtlCopyPipelineState;
-class GrMtlGpu;
-class GrSurface;
-struct SkIPoint;
-struct SkIRect;
-
-class GrMtlCopyManager {
-public:
-    GrMtlCopyManager(GrMtlGpu* gpu) : fGpu(gpu) {}
-
-    bool copySurfaceAsDraw(GrSurface* dst, GrSurfaceOrigin dstOrigin,
-                           GrSurface* src, GrSurfaceOrigin srcOrigin,
-                           const SkIRect& srcRect, const SkIPoint& dstPoint,
-                           bool canDiscardOutsideDstRect);
-
-    static bool IsCompatible(const GrMtlCopyPipelineState*, MTLPixelFormat dstPixelFormat);
-
-    void destroyResources();
-
-private:
-    enum BufferIndex {
-        kUniform_BufferIndex,
-        kAttribute_BufferIndex,
-    };
-
-    void createCopyProgramBuffer();
-    void createCopyProgramShaders();
-    void createCopyProgramVertexDescriptor();
-
-    void createCopyProgram();
-
-    id<MTLSamplerState>  fSamplerState;
-    id<MTLBuffer>        fVertexAttributeBuffer;
-    id<MTLFunction>      fVertexFunction;
-    id<MTLFunction>      fFragmentFunction;
-    MTLVertexDescriptor* fVertexDescriptor;
-
-    GrMtlGpu* fGpu;
-};
-
-#endif
diff --git a/src/gpu/mtl/GrMtlCopyManager.mm b/src/gpu/mtl/GrMtlCopyManager.mm
deleted file mode 100644
index 0cc7e80..0000000
--- a/src/gpu/mtl/GrMtlCopyManager.mm
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
- * Copyright 2018 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "src/gpu/mtl/GrMtlCopyManager.h"
-
-#include "include/gpu/GrSurface.h"
-
-#include "src/gpu/mtl/GrMtlBuffer.h"
-#include "src/gpu/mtl/GrMtlCommandBuffer.h"
-#include "src/gpu/mtl/GrMtlCopyPipelineState.h"
-#include "src/gpu/mtl/GrMtlGpu.h"
-#include "src/gpu/mtl/GrMtlResourceProvider.h"
-#include "src/gpu/mtl/GrMtlUtil.h"
-
-#include "include/core/SkPoint.h"
-#include "include/core/SkRect.h"
-#include "src/core/SkTraceEvent.h"
-
-#import <simd/simd.h>
-
-#if !__has_feature(objc_arc)
-#error This file must be compiled with Arc. Use -fobjc-arc flag
-#endif
-
-void GrMtlCopyManager::createCopyProgramBuffer() {
-    // Create per vertex attribute data for copy as draw
-    static const simd::float2 vdata[4] = {
-        {0, 0},
-        {0, 1},
-        {1, 0},
-        {1, 1},
-    };
-    sk_sp<GrMtlBuffer> mtlBuffer = GrMtlBuffer::Make(fGpu, sizeof(vdata), GrGpuBufferType::kVertex,
-                                                     kStatic_GrAccessPattern, vdata);
-    fVertexAttributeBuffer = mtlBuffer->mtlBuffer();
-}
-
-void GrMtlCopyManager::createCopyProgramShaders() {
-     // Create shaders required by pipeline state
-    SkString vertShaderText;
-    vertShaderText.appendf(
-        "#extension GL_ARB_separate_shader_objects : enable\n"
-        "#extension GL_ARB_shading_language_420pack : enable\n"
-        "layout(set = %d"/*kUniform_BufferIndex*/", binding = 0) uniform vertexUniformBuffer {"
-            "float4 uPosXform;"
-            "float4 uTexCoordXform;"
-        "};"
-        "layout(location = 0) in float2 inPosition;"
-        "layout(location = 1) out float2 vTexCoord;"
-
-        "// Copy Program VS\n"
-        "void main() {"
-            "vTexCoord = inPosition * uTexCoordXform.xy + uTexCoordXform.zw;"
-            "sk_Position.xy = inPosition * uPosXform.xy + uPosXform.zw;"
-            "sk_Position.zw = float2(0, 1);"
-        "}",
-        kUniform_BufferIndex
-    );
-
-    SkString fragShaderText;
-    fragShaderText.append(
-        "#extension GL_ARB_separate_shader_objects : enable\n"
-        "#extension GL_ARB_shading_language_420pack : enable\n"
-
-        "layout(set = 1, binding = 0) uniform sampler2D uTexture;"
-        "layout(location = 1) in float2 vTexCoord;"
-
-        "// Copy Program FS\n"
-        "void main() {"
-            "sk_FragColor = texture(uTexture, vTexCoord);"
-        "}"
-    );
-
-    SkSL::Program::Settings settings;
-    SkSL::Program::Inputs inputs;
-    id<MTLLibrary> vertexLibrary = GrCompileMtlShaderLibrary(fGpu, vertShaderText.c_str(),
-                                                             SkSL::Program::kVertex_Kind,
-                                                             settings, &inputs);
-    SkASSERT(inputs.isEmpty());
-    SkASSERT(vertexLibrary);
-
-    id<MTLLibrary> fragmentLibrary = GrCompileMtlShaderLibrary(fGpu, fragShaderText.c_str(),
-                                                               SkSL::Program::kFragment_Kind,
-                                                               settings, &inputs);
-    SkASSERT(inputs.isEmpty());
-    SkASSERT(fragmentLibrary);
-
-    id<MTLFunction> vertexFunction = [vertexLibrary newFunctionWithName: @"vertexMain"];
-    id<MTLFunction> fragmentFunction = [fragmentLibrary newFunctionWithName: @"fragmentMain"];
-    SkASSERT(vertexFunction);
-    SkASSERT(fragmentFunction);
-
-    fVertexFunction = vertexFunction;
-    fFragmentFunction = fragmentFunction;
-}
-
-void GrMtlCopyManager::createCopyProgramVertexDescriptor() {
-    // Create vertex descriptor for pipeline state
-    // Expected [[stage_in]] (vertex attribute) MSL format for copies:
-    //
-    // struct Input {
-    //     float2 inPosition [[attribute(0)]];
-    // };
-    MTLVertexDescriptor* vertexDescriptor = [[MTLVertexDescriptor alloc] init];
-    vertexDescriptor.attributes[0].format = MTLVertexFormatFloat2;
-    vertexDescriptor.attributes[0].offset = 0;
-    vertexDescriptor.attributes[0].bufferIndex = kAttribute_BufferIndex;
-
-    vertexDescriptor.layouts[kAttribute_BufferIndex].stepFunction = MTLVertexStepFunctionPerVertex;
-    vertexDescriptor.layouts[kAttribute_BufferIndex].stepRate = 1;
-    vertexDescriptor.layouts[kAttribute_BufferIndex].stride = sizeof(simd::float2);
-
-    fVertexDescriptor = vertexDescriptor;
-}
-
-void GrMtlCopyManager::createCopyProgram() {
-    TRACE_EVENT0("skia", TRACE_FUNC);
-
-    MTLSamplerDescriptor* samplerDescriptor = [[MTLSamplerDescriptor alloc] init];
-    fSamplerState = [fGpu->device() newSamplerStateWithDescriptor: samplerDescriptor];
-
-    this->createCopyProgramBuffer();
-    this->createCopyProgramShaders();
-    this->createCopyProgramVertexDescriptor();
-}
-
-bool GrMtlCopyManager::copySurfaceAsDraw(GrSurface* dst, GrSurfaceOrigin dstOrigin,
-                                         GrSurface* src, GrSurfaceOrigin srcOrigin,
-                                         const SkIRect& srcRect, const SkIPoint& dstPoint,
-                                         bool canDiscardOutsideDstRect) {
-    SkASSERT(fGpu->mtlCaps().canCopyAsDraw(dst->config(), SkToBool(dst->asRenderTarget()),
-                                           src->config(), SkToBool(src->asTexture())));
-
-    id<MTLTexture> dstTex = GrGetMTLTextureFromSurface(dst, false);
-    id<MTLTexture> srcTex = GrGetMTLTextureFromSurface(src, false);
-    SkASSERT(srcTex != dstTex);
-
-    if (fSamplerState == nil) {
-        SkASSERT(fVertexAttributeBuffer == nil);
-        SkASSERT(fVertexFunction == nil);
-        SkASSERT(fFragmentFunction == nil);
-        SkASSERT(fVertexDescriptor == nil);
-
-        this->createCopyProgram();
-    }
-
-    if (!(fSamplerState && fVertexAttributeBuffer && fVertexFunction &&
-          fFragmentFunction && fVertexDescriptor)) {
-        SkASSERT(false);
-        return false;
-    }
-
-    // UPDATE UNIFORM DESCRIPTOR SET
-    int w = srcRect.width();
-    int h = srcRect.height();
-
-    // dst rect edges in NDC (-1 to 1)
-    int dw = dstTex.width;
-    int dh = dstTex.height;
-    float dx0 = 2.f * dstPoint.fX / dw - 1.f;
-    float dx1 = 2.f * (dstPoint.fX + w) / dw - 1.f;
-    float dy0 = 2.f * dstPoint.fY / dh - 1.f;
-    float dy1 = 2.f * (dstPoint.fY + h) / dh - 1.f;
-    if (kBottomLeft_GrSurfaceOrigin == dstOrigin) {
-        dy0 = -dy0;
-        dy1 = -dy1;
-    }
-
-    float sx0 = (float)srcRect.fLeft;
-    float sx1 = (float)(srcRect.fLeft + w);
-    float sy0 = (float)srcRect.fTop;
-    float sy1 = (float)(srcRect.fTop + h);
-    int sh = srcTex.height;
-    if (kBottomLeft_GrSurfaceOrigin == srcOrigin) {
-        sy0 = sh - sy0;
-        sy1 = sh - sy1;
-    }
-
-    // src rect edges in normalized texture space (0 to 1).
-    int sw = srcTex.width;
-    sx0 /= sw;
-    sx1 /= sw;
-    sy0 /= sh;
-    sy1 /= sh;
-
-    const simd::float4 vertexUniformBuffer[2] = {
-        {dx1 - dx0, dy1 - dy0, dx0, dy0}, // posXform
-        {sx1 - sx0, sy1 - sy0, sx0, sy0}, // texCoordXform
-    };
-
-    MTLRenderPassDescriptor* renderPassDesc = [MTLRenderPassDescriptor renderPassDescriptor];
-    renderPassDesc.colorAttachments[0].texture = dstTex;
-    renderPassDesc.colorAttachments[0].slice = 0;
-    renderPassDesc.colorAttachments[0].level = 0;
-    renderPassDesc.colorAttachments[0].loadAction = canDiscardOutsideDstRect ? MTLLoadActionDontCare
-                                                                             : MTLLoadActionLoad;
-    renderPassDesc.colorAttachments[0].storeAction = MTLStoreActionStore;
-
-    id<MTLRenderCommandEncoder> renderCmdEncoder =
-            fGpu->commandBuffer()->getRenderCommandEncoder(renderPassDesc, nullptr);
-    GrMtlCopyPipelineState* copyPipelineState =
-            fGpu->resourceProvider().findOrCreateCopyPipelineState(dstTex.pixelFormat,
-                                                                   fVertexFunction,
-                                                                   fFragmentFunction,
-                                                                   fVertexDescriptor);
-    [renderCmdEncoder setRenderPipelineState: copyPipelineState->mtlCopyPipelineState()];
-    [renderCmdEncoder setVertexBuffer: fVertexAttributeBuffer
-                               offset: 0
-                              atIndex: kAttribute_BufferIndex];
-    [renderCmdEncoder setVertexBytes: vertexUniformBuffer
-                              length: sizeof(vertexUniformBuffer)
-                             atIndex: kUniform_BufferIndex];
-    [renderCmdEncoder setFragmentTexture: srcTex
-                                 atIndex: 0];
-    [renderCmdEncoder setFragmentSamplerState: fSamplerState
-                                      atIndex: 0];
-    [renderCmdEncoder drawPrimitives: MTLPrimitiveTypeTriangleStrip
-                         vertexStart: 0
-                         vertexCount: 4];
-    return true;
-}
-
-bool GrMtlCopyManager::IsCompatible(const GrMtlCopyPipelineState* pipelineState,
-                                    MTLPixelFormat dstPixelFormat) {
-    return pipelineState->fPixelFormat == dstPixelFormat;
-}
-
-void GrMtlCopyManager::destroyResources() {
-    fSamplerState = nil;
-    fVertexAttributeBuffer = nil;
-    fVertexFunction = nil;
-    fFragmentFunction = nil;
-    fVertexDescriptor = nil;
-}
diff --git a/src/gpu/mtl/GrMtlCopyPipelineState.h b/src/gpu/mtl/GrMtlCopyPipelineState.h
deleted file mode 100644
index ce4cbb2..0000000
--- a/src/gpu/mtl/GrMtlCopyPipelineState.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright 2018 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrMtlCopyPipelineState_DEFINED
-#define GrMtlCopyPipelineState_DEFINED
-
-#import <metal/metal.h>
-
-class GrMtlGpu;
-
-class GrMtlCopyPipelineState {
-public:
-    static GrMtlCopyPipelineState* CreateCopyPipelineState(GrMtlGpu* gpu,
-                                                           MTLPixelFormat dstPixelFormat,
-                                                           id<MTLFunction> vertexFunction,
-                                                           id<MTLFunction> fragmentFunction,
-                                                           MTLVertexDescriptor* vertexDescriptor);
-
-    id<MTLRenderPipelineState> mtlCopyPipelineState() { return fPipelineState; }
-
-private:
-    GrMtlCopyPipelineState(id<MTLRenderPipelineState> pipelineState,
-                       MTLPixelFormat pixelFormat)
-            : fPipelineState(pipelineState)
-            , fPixelFormat(pixelFormat) {}
-
-    id<MTLRenderPipelineState> fPipelineState;
-    MTLPixelFormat fPixelFormat;
-
-    friend class GrMtlCopyManager;
-};
-
-#endif
diff --git a/src/gpu/mtl/GrMtlCopyPipelineState.mm b/src/gpu/mtl/GrMtlCopyPipelineState.mm
deleted file mode 100644
index 43db99d..0000000
--- a/src/gpu/mtl/GrMtlCopyPipelineState.mm
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright 2018 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "src/gpu/mtl/GrMtlCopyPipelineState.h"
-#include "src/gpu/mtl/GrMtlGpu.h"
-
-#if !__has_feature(objc_arc)
-#error This file must be compiled with Arc. Use -fobjc-arc flag
-#endif
-
-GrMtlCopyPipelineState* GrMtlCopyPipelineState::CreateCopyPipelineState(
-        GrMtlGpu* gpu,
-        MTLPixelFormat dstPixelFormat,
-        id<MTLFunction> vertexFunction,
-        id<MTLFunction> fragmentFunction,
-        MTLVertexDescriptor* vertexDescriptor) {
-
-    // Create pipeline state for copy as draw
-    MTLRenderPipelineDescriptor* pipelineDescriptor = [MTLRenderPipelineDescriptor new];
-    pipelineDescriptor.vertexFunction = vertexFunction;
-    pipelineDescriptor.fragmentFunction = fragmentFunction;
-    pipelineDescriptor.vertexDescriptor = vertexDescriptor;
-    pipelineDescriptor.colorAttachments[0].pixelFormat = dstPixelFormat;
-
-    NSError* error = nil;
-    id<MTLRenderPipelineState> pipelineState =
-            [gpu->device() newRenderPipelineStateWithDescriptor: pipelineDescriptor
-                                                          error: &error];
-    if (error) {
-        SkDebugf("Error creating pipeline: %s\n",
-                 [[error localizedDescription] cStringUsingEncoding: NSASCIIStringEncoding]);
-        return nil;
-    }
-
-    SkASSERT(pipelineState);
-    return new GrMtlCopyPipelineState(pipelineState, dstPixelFormat);
-}
diff --git a/src/gpu/mtl/GrMtlGpu.h b/src/gpu/mtl/GrMtlGpu.h
index 7288daf..32c22fc 100644
--- a/src/gpu/mtl/GrMtlGpu.h
+++ b/src/gpu/mtl/GrMtlGpu.h
@@ -14,7 +14,6 @@
 #include "src/gpu/GrSemaphore.h"
 
 #include "src/gpu/mtl/GrMtlCaps.h"
-#include "src/gpu/mtl/GrMtlCopyManager.h"
 #include "src/gpu/mtl/GrMtlResourceProvider.h"
 #include "src/gpu/mtl/GrMtlStencilAttachment.h"
 
@@ -72,22 +71,11 @@
     void testingOnly_flushGpuAndSync() override;
 #endif
 
-    bool copySurfaceAsBlit(GrSurface* dst, GrSurfaceOrigin dstOrigin,
-                           GrSurface* src, GrSurfaceOrigin srcOrigin,
-                           const SkIRect& srcRect, const SkIPoint& dstPoint);
+    bool copySurfaceAsBlit(GrSurface* dst, GrSurface* src, const SkIRect& srcRect,
+                           const SkIPoint& dstPoint);
 
-    // This function is needed when we want to copy between two surfaces with different origins and
-    // the destination surface is not a render target. We will first draw to a temporary render
-    // target to adjust for the different origins and then blit from there to the destination.
-    bool copySurfaceAsDrawThenBlit(GrSurface* dst, GrSurfaceOrigin dstOrigin,
-                                   GrSurface* src, GrSurfaceOrigin srcOrigin,
-                                   const SkIRect& srcRect, const SkIPoint& dstPoint);
-
-    bool onCopySurface(GrSurface* dst, GrSurfaceOrigin dstOrigin,
-                       GrSurface* src, GrSurfaceOrigin srcOrigin,
-                       const SkIRect& srcRect,
-                       const SkIPoint& dstPoint,
-                       bool canDiscardOutsideDstRect) override;
+    bool onCopySurface(GrSurface* dst, GrSurface* src, const SkIRect& srcRect,
+                       const SkIPoint& dstPoint, bool canDiscardOutsideDstRect) override;
 
     GrGpuRTCommandBuffer* getCommandBuffer(
                                     GrRenderTarget*, GrSurfaceOrigin, const SkRect& bounds,
@@ -222,7 +210,6 @@
 
     std::unique_ptr<SkSL::Compiler> fCompiler;
 
-    GrMtlCopyManager      fCopyManager;
     GrMtlResourceProvider fResourceProvider;
 
     bool fDisconnected;
diff --git a/src/gpu/mtl/GrMtlGpu.mm b/src/gpu/mtl/GrMtlGpu.mm
index 957a5b2..d281524 100644
--- a/src/gpu/mtl/GrMtlGpu.mm
+++ b/src/gpu/mtl/GrMtlGpu.mm
@@ -99,7 +99,6 @@
         , fQueue(queue)
         , fCmdBuffer(nullptr)
         , fCompiler(new SkSL::Compiler())
-        , fCopyManager(this)
         , fResourceProvider(this)
         , fDisconnected(false) {
     fMtlCaps.reset(new GrMtlCaps(options, fDevice, featureSet));
@@ -121,8 +120,6 @@
         delete fCmdBuffer;
         fCmdBuffer = nullptr;
 
-        // We don't need to distinguish between abandon and destroy for these subsystems
-        fCopyManager.destroyResources();
         fResourceProvider.destroyResources();
 
         fQueue = nil;
@@ -135,8 +132,6 @@
 void GrMtlGpu::destroyResources() {
     // Will implicitly delete the command buffer
     this->submitCommandBuffer(SyncQueue::kForce_SyncQueue);
-
-    fCopyManager.destroyResources();
     fResourceProvider.destroyResources();
 
     fQueue = nil;
@@ -871,126 +866,33 @@
     return 0;
 }
 
-bool GrMtlGpu::copySurfaceAsBlit(GrSurface* dst, GrSurfaceOrigin dstOrigin,
-                                 GrSurface* src, GrSurfaceOrigin srcOrigin,
-                                 const SkIRect& srcRect, const SkIPoint& dstPoint) {
+bool GrMtlGpu::copySurfaceAsBlit(GrSurface* dst, GrSurface* src, const SkIRect& srcRect,
+                                 const SkIPoint& dstPoint) {
 #ifdef SK_DEBUG
     int dstSampleCnt = get_surface_sample_cnt(dst);
     int srcSampleCnt = get_surface_sample_cnt(src);
-    SkASSERT(this->mtlCaps().canCopyAsBlit(dst->config(), dstSampleCnt, dstOrigin,
-                                           src->config(), srcSampleCnt, srcOrigin,
+    SkASSERT(this->mtlCaps().canCopyAsBlit(dst->config(), dstSampleCnt, src->config(), srcSampleCnt,
                                            srcRect, dstPoint, dst == src));
 #endif
     id<MTLTexture> dstTex = GrGetMTLTextureFromSurface(dst, false);
     id<MTLTexture> srcTex = GrGetMTLTextureFromSurface(src, false);
 
-    // Flip rect if necessary
-    SkIRect srcMtlRect;
-    srcMtlRect.fLeft = srcRect.fLeft;
-    srcMtlRect.fRight = srcRect.fRight;
-    SkIRect dstRect;
-    dstRect.fLeft = dstPoint.fX;
-    dstRect.fRight = dstPoint.fX + srcRect.width();
-
-    if (kBottomLeft_GrSurfaceOrigin == srcOrigin) {
-        srcMtlRect.fTop = srcTex.height - srcRect.fBottom;
-        srcMtlRect.fBottom = srcTex.height - srcRect.fTop;
-    } else {
-        srcMtlRect.fTop = srcRect.fTop;
-        srcMtlRect.fBottom = srcRect.fBottom;
-    }
-
-    if (kBottomLeft_GrSurfaceOrigin == dstOrigin) {
-        dstRect.fTop = dstTex.height - dstPoint.fY - srcMtlRect.height();
-    } else {
-        dstRect.fTop = dstPoint.fY;
-    }
-    dstRect.fBottom = dstRect.fTop + srcMtlRect.height();
-
     id<MTLBlitCommandEncoder> blitCmdEncoder = this->commandBuffer()->getBlitCommandEncoder();
     [blitCmdEncoder copyFromTexture: srcTex
                         sourceSlice: 0
                         sourceLevel: 0
-                       sourceOrigin: MTLOriginMake(srcMtlRect.x(), srcMtlRect.y(), 0)
-                         sourceSize: MTLSizeMake(srcMtlRect.width(), srcMtlRect.height(), 1)
+                       sourceOrigin: MTLOriginMake(srcRect.x(), srcRect.y(), 0)
+                         sourceSize: MTLSizeMake(srcRect.width(), srcRect.height(), 1)
                           toTexture: dstTex
                    destinationSlice: 0
                    destinationLevel: 0
-                  destinationOrigin: MTLOriginMake(dstRect.x(), dstRect.y(), 0)];
+                  destinationOrigin: MTLOriginMake(dstPoint.fX, dstPoint.fY, 0)];
 
     return true;
 }
 
-bool GrMtlGpu::copySurfaceAsDrawThenBlit(GrSurface* dst, GrSurfaceOrigin dstOrigin,
-                                         GrSurface* src, GrSurfaceOrigin srcOrigin,
-                                         const SkIRect& srcRect, const SkIPoint& dstPoint) {
-#ifdef SK_DEBUG
-    int dstSampleCnt = get_surface_sample_cnt(dst);
-    int srcSampleCnt = get_surface_sample_cnt(src);
-    SkASSERT(dstSampleCnt == 0); // dst shouldn't be a render target
-    SkASSERT(!this->mtlCaps().canCopyAsBlit(dst->config(), dstSampleCnt, dstOrigin,
-                                            src->config(), srcSampleCnt, srcOrigin,
-                                            srcRect, dstPoint, dst == src));
-    SkASSERT(!this->mtlCaps().canCopyAsDraw(dst->config(), SkToBool(dst->asRenderTarget()),
-                                            src->config(), SkToBool(src->asTexture())));
-    SkASSERT(this->mtlCaps().canCopyAsDrawThenBlit(dst->config(),src->config(),
-                                                   SkToBool(src->asTexture())));
-#endif
-    GrSurfaceDesc surfDesc;
-    surfDesc.fFlags = kRenderTarget_GrSurfaceFlag;
-    surfDesc.fWidth = srcRect.width();
-    surfDesc.fHeight = srcRect.height();
-    surfDesc.fConfig = dst->config();
-    surfDesc.fSampleCnt = 1;
-
-    id<MTLTexture> dstTex = GrGetMTLTextureFromSurface(dst, false);
-    MTLTextureDescriptor* textureDesc = GrGetMTLTextureDescriptor(dstTex);
-    textureDesc.width = srcRect.width();
-    textureDesc.height = srcRect.height();
-    textureDesc.mipmapLevelCount = 1;
-    textureDesc.usage |= MTLTextureUsageRenderTarget;
-
-    sk_sp<GrMtlTexture> transferTexture =
-            GrMtlTextureRenderTarget::CreateNewTextureRenderTarget(this,
-                                                                   SkBudgeted::kYes,
-                                                                   surfDesc,
-                                                                   textureDesc,
-                                                                   GrMipMapsStatus::kNotAllocated);
-
-    GrSurfaceOrigin transferOrigin = dstOrigin;
-    SkASSERT(this->mtlCaps().canCopyAsDraw(transferTexture->config(),
-                                           SkToBool(transferTexture->asRenderTarget()),
-                                           src->config(),
-                                           SkToBool(src->asTexture())));
-    // TODO: Eventually we will need to handle resolves either in this function or make a separate
-    // copySurfaceAsResolveThenBlit().
-    if (!this->copySurface(transferTexture.get(), transferOrigin,
-                           src, srcOrigin,
-                           srcRect, SkIPoint::Make(0, 0))) {
-        return false;
-    }
-
-    SkIRect transferRect = SkIRect::MakeXYWH(0, 0, srcRect.width(), srcRect.height());
-    SkASSERT(this->mtlCaps().canCopyAsBlit(dst->config(),
-                                           get_surface_sample_cnt(dst),
-                                           dstOrigin,
-                                           transferTexture->config(),
-                                           get_surface_sample_cnt(transferTexture.get()),
-                                           transferOrigin,
-                                           transferRect, dstPoint, false));
-    if (!this->copySurface(dst, dstOrigin,
-                           transferTexture.get(), transferOrigin,
-                           transferRect, dstPoint)) {
-        return false;
-    }
-    return true;
-}
-
-bool GrMtlGpu::onCopySurface(GrSurface* dst, GrSurfaceOrigin dstOrigin,
-                             GrSurface* src, GrSurfaceOrigin srcOrigin,
-                             const SkIRect& srcRect,
-                             const SkIPoint& dstPoint,
-                             bool canDiscardOutsideDstRect) {
+bool GrMtlGpu::onCopySurface(GrSurface* dst, GrSurface* src, const SkIRect& srcRect,
+                             const SkIPoint& dstPoint, bool canDiscardOutsideDstRect) {
 
     GrPixelConfig dstConfig = dst->config();
     GrPixelConfig srcConfig = src->config();
@@ -1004,23 +906,15 @@
     }
 
     bool success = false;
-    if (this->mtlCaps().canCopyAsDraw(dst->config(), SkToBool(dst->asRenderTarget()),
-                                      src->config(), SkToBool(src->asTexture()))) {
-        success = fCopyManager.copySurfaceAsDraw(dst, dstOrigin, src, srcOrigin, srcRect, dstPoint,
-                                                 canDiscardOutsideDstRect);
-    } else if (this->mtlCaps().canCopyAsBlit(dstConfig, dstSampleCnt, dstOrigin,
-                                             srcConfig, srcSampleCnt, srcOrigin,
-                                             srcRect, dstPoint, dst == src)) {
-        success = this->copySurfaceAsBlit(dst, dstOrigin, src, srcOrigin, srcRect, dstPoint);
-    } else if (this->mtlCaps().canCopyAsDrawThenBlit(dst->config(), src->config(),
-                                                     SkToBool(src->asTexture()))) {
-        success = this->copySurfaceAsDrawThenBlit(dst, dstOrigin, src, srcOrigin,
-                                                  srcRect, dstPoint);
+    if (this->mtlCaps().canCopyAsBlit(dstConfig, dstSampleCnt, srcConfig, srcSampleCnt, srcRect,
+                                      dstPoint, dst == src)) {
+        success = this->copySurfaceAsBlit(dst, src, srcRect, dstPoint);
     }
     if (success) {
         SkIRect dstRect = SkIRect::MakeXYWH(dstPoint.x(), dstPoint.y(),
                                             srcRect.width(), srcRect.height());
-        this->didWriteToSurface(dst, dstOrigin, &dstRect);
+        // The rect is already in device space so we pass in kTopLeft so no flip is done.
+        this->didWriteToSurface(dst, kTopLeft_GrSurfaceOrigin, &dstRect);
     }
     return success;
 }
diff --git a/src/gpu/mtl/GrMtlGpuCommandBuffer.h b/src/gpu/mtl/GrMtlGpuCommandBuffer.h
index f7a0eb9..062b228 100644
--- a/src/gpu/mtl/GrMtlGpuCommandBuffer.h
+++ b/src/gpu/mtl/GrMtlGpuCommandBuffer.h
@@ -29,9 +29,8 @@
 
     ~GrMtlGpuTextureCommandBuffer() override {}
 
-    void copy(GrSurface* src, GrSurfaceOrigin srcOrigin, const SkIRect& srcRect,
-              const SkIPoint& dstPoint) override {
-        fGpu->copySurface(fTexture, fOrigin, src, srcOrigin, srcRect, dstPoint);
+    void copy(GrSurface* src, const SkIRect& srcRect, const SkIPoint& dstPoint) override {
+        fGpu->copySurface(fTexture, src, srcRect, dstPoint);
     }
     void transferFrom(const SkIRect& srcRect, GrColorType bufferColorType,
                       GrGpuBuffer* transferBuffer, size_t offset) override {
@@ -68,8 +67,7 @@
     }
     void transferFrom(const SkIRect& srcRect, GrColorType bufferColorType,
                       GrGpuBuffer* transferBuffer, size_t offset) override;
-    void copy(GrSurface* src, GrSurfaceOrigin srcOrigin, const SkIRect& srcRect,
-              const SkIPoint& dstPoint) override;
+    void copy(GrSurface* src, const SkIRect& srcRect, const SkIPoint& dstPoint) override;
 
     void submit();
 
diff --git a/src/gpu/mtl/GrMtlGpuCommandBuffer.mm b/src/gpu/mtl/GrMtlGpuCommandBuffer.mm
index 7354fbe..8dc4c70 100644
--- a/src/gpu/mtl/GrMtlGpuCommandBuffer.mm
+++ b/src/gpu/mtl/GrMtlGpuCommandBuffer.mm
@@ -92,12 +92,12 @@
     fGpu->submitIndirectCommandBuffer(fRenderTarget, fOrigin, &iBounds);
 }
 
-void GrMtlGpuRTCommandBuffer::copy(GrSurface* src, GrSurfaceOrigin srcOrigin,
-                                   const SkIRect& srcRect, const SkIPoint& dstPoint) {
+void GrMtlGpuRTCommandBuffer::copy(GrSurface* src, const SkIRect& srcRect,
+const SkIPoint& dstPoint) {
     // We cannot have an active encoder when we call copy since it requires its own
     // command encoder.
     SkASSERT(nil == fActiveRenderCmdEncoder);
-    fGpu->copySurface(fRenderTarget, fOrigin, src, srcOrigin, srcRect, dstPoint);
+    fGpu->copySurface(fRenderTarget, src, srcRect, dstPoint);
 }
 
 void GrMtlGpuRTCommandBuffer::transferFrom(const SkIRect& srcRect, GrColorType bufferColorType,
diff --git a/src/gpu/mtl/GrMtlResourceProvider.h b/src/gpu/mtl/GrMtlResourceProvider.h
index ed20e59..cf19c89 100644
--- a/src/gpu/mtl/GrMtlResourceProvider.h
+++ b/src/gpu/mtl/GrMtlResourceProvider.h
@@ -10,7 +10,6 @@
 
 #include "include/private/SkTArray.h"
 #include "src/core/SkLRUCache.h"
-#include "src/gpu/mtl/GrMtlCopyPipelineState.h"
 #include "src/gpu/mtl/GrMtlDepthStencil.h"
 #include "src/gpu/mtl/GrMtlPipelineStateBuilder.h"
 #include "src/gpu/mtl/GrMtlSampler.h"
@@ -23,11 +22,6 @@
 public:
     GrMtlResourceProvider(GrMtlGpu* gpu);
 
-    GrMtlCopyPipelineState* findOrCreateCopyPipelineState(MTLPixelFormat dstPixelFormat,
-                                                          id<MTLFunction> vertexFunction,
-                                                          id<MTLFunction> fragmentFunction,
-                                                          MTLVertexDescriptor* vertexDescriptor);
-
     GrMtlPipelineState* findOrCreateCompatiblePipelineState(
         GrRenderTarget*, GrSurfaceOrigin,
         const GrPipeline&,
@@ -89,8 +83,6 @@
 #endif
     };
 
-    SkTArray<std::unique_ptr<GrMtlCopyPipelineState>> fCopyPipelineStateCache;
-
     GrMtlGpu* fGpu;
 
     // Cache of GrMtlPipelineStates
diff --git a/src/gpu/mtl/GrMtlResourceProvider.mm b/src/gpu/mtl/GrMtlResourceProvider.mm
index e3a63c7..f447984 100644
--- a/src/gpu/mtl/GrMtlResourceProvider.mm
+++ b/src/gpu/mtl/GrMtlResourceProvider.mm
@@ -8,7 +8,6 @@
 #include "src/gpu/mtl/GrMtlResourceProvider.h"
 
 #include "src/gpu/mtl/GrMtlCommandBuffer.h"
-#include "src/gpu/mtl/GrMtlCopyManager.h"
 #include "src/gpu/mtl/GrMtlGpu.h"
 #include "src/gpu/mtl/GrMtlPipelineState.h"
 #include "src/gpu/mtl/GrMtlUtil.h"
@@ -25,23 +24,6 @@
     fPipelineStateCache.reset(new PipelineStateCache(gpu));
 }
 
-GrMtlCopyPipelineState* GrMtlResourceProvider::findOrCreateCopyPipelineState(
-        MTLPixelFormat dstPixelFormat,
-        id<MTLFunction> vertexFunction,
-        id<MTLFunction> fragmentFunction,
-        MTLVertexDescriptor* vertexDescriptor) {
-
-    for (const auto& copyPipelineState: fCopyPipelineStateCache) {
-        if (GrMtlCopyManager::IsCompatible(copyPipelineState.get(), dstPixelFormat)) {
-            return copyPipelineState.get();
-        }
-    }
-
-    fCopyPipelineStateCache.emplace_back(GrMtlCopyPipelineState::CreateCopyPipelineState(
-             fGpu, dstPixelFormat, vertexFunction, fragmentFunction, vertexDescriptor));
-    return fCopyPipelineStateCache.back().get();
-}
-
 GrMtlPipelineState* GrMtlResourceProvider::findOrCreateCompatiblePipelineState(
         GrRenderTarget* renderTarget, GrSurfaceOrigin origin,
         const GrPipeline& pipeline, const GrPrimitiveProcessor& proc,
diff --git a/src/gpu/ops/GrCopySurfaceOp.cpp b/src/gpu/ops/GrCopySurfaceOp.cpp
index df64f3a..0dfcc29 100644
--- a/src/gpu/ops/GrCopySurfaceOp.cpp
+++ b/src/gpu/ops/GrCopySurfaceOp.cpp
@@ -11,57 +11,7 @@
 #include "src/gpu/GrGpu.h"
 #include "src/gpu/GrMemoryPool.h"
 #include "src/gpu/GrRecordingContextPriv.h"
-
-// returns true if the read/written rect intersects the src/dst and false if not.
-static bool clip_src_rect_and_dst_point(const GrSurfaceProxy* dst,
-                                        const GrSurfaceProxy* src,
-                                        const SkIRect& srcRect,
-                                        const SkIPoint& dstPoint,
-                                        SkIRect* clippedSrcRect,
-                                        SkIPoint* clippedDstPoint) {
-    *clippedSrcRect = srcRect;
-    *clippedDstPoint = dstPoint;
-
-    // clip the left edge to src and dst bounds, adjusting dstPoint if necessary
-    if (clippedSrcRect->fLeft < 0) {
-        clippedDstPoint->fX -= clippedSrcRect->fLeft;
-        clippedSrcRect->fLeft = 0;
-    }
-    if (clippedDstPoint->fX < 0) {
-        clippedSrcRect->fLeft -= clippedDstPoint->fX;
-        clippedDstPoint->fX = 0;
-    }
-
-    // clip the top edge to src and dst bounds, adjusting dstPoint if necessary
-    if (clippedSrcRect->fTop < 0) {
-        clippedDstPoint->fY -= clippedSrcRect->fTop;
-        clippedSrcRect->fTop = 0;
-    }
-    if (clippedDstPoint->fY < 0) {
-        clippedSrcRect->fTop -= clippedDstPoint->fY;
-        clippedDstPoint->fY = 0;
-    }
-
-    // clip the right edge to the src and dst bounds.
-    if (clippedSrcRect->fRight > src->width()) {
-        clippedSrcRect->fRight = src->width();
-    }
-    if (clippedDstPoint->fX + clippedSrcRect->width() > dst->width()) {
-        clippedSrcRect->fRight = clippedSrcRect->fLeft + dst->width() - clippedDstPoint->fX;
-    }
-
-    // clip the bottom edge to the src and dst bounds.
-    if (clippedSrcRect->fBottom > src->height()) {
-        clippedSrcRect->fBottom = src->height();
-    }
-    if (clippedDstPoint->fY + clippedSrcRect->height() > dst->height()) {
-        clippedSrcRect->fBottom = clippedSrcRect->fTop + dst->height() - clippedDstPoint->fY;
-    }
-
-    // The above clipping steps may have inverted the rect if it didn't intersect either the src or
-    // dst bounds.
-    return !clippedSrcRect->isEmpty();
-}
+#include "src/gpu/geometry/GrRect.h"
 
 std::unique_ptr<GrOp> GrCopySurfaceOp::Make(GrRecordingContext* context,
                                             GrSurfaceProxy* dstProxy,
@@ -73,22 +23,54 @@
     SkIRect clippedSrcRect;
     SkIPoint clippedDstPoint;
     // If the rect is outside the srcProxy or dstProxy then we've already succeeded.
-    if (!clip_src_rect_and_dst_point(dstProxy, srcProxy, srcRect, dstPoint,
-                                     &clippedSrcRect, &clippedDstPoint)) {
+    if (!GrClipSrcRectAndDstPoint(dstProxy->isize(), srcProxy->isize(), srcRect, dstPoint,
+                                  &clippedSrcRect, &clippedDstPoint)) {
         return nullptr;
     }
     if (GrPixelConfigIsCompressed(dstProxy->config())) {
         return nullptr;
     }
 
+    SkASSERT(dstProxy->origin() == srcProxy->origin());
+    SkIRect adjSrcRect;
+    adjSrcRect.fLeft = clippedSrcRect.fLeft;
+    adjSrcRect.fRight = clippedSrcRect.fRight;
+    SkIPoint adjDstPoint;
+    adjDstPoint.fX = clippedDstPoint.fX;
+
+    // If it is bottom left origin we must flip the rects.
+    SkASSERT(dstProxy->origin() == srcProxy->origin());
+    if (kBottomLeft_GrSurfaceOrigin == srcProxy->origin()) {
+        adjSrcRect.fTop = srcProxy->height() - clippedSrcRect.fBottom;
+        adjSrcRect.fBottom = srcProxy->height() - clippedSrcRect.fTop;
+        adjDstPoint.fY = dstProxy->height() - clippedDstPoint.fY - clippedSrcRect.height();
+    } else {
+        adjSrcRect.fTop = clippedSrcRect.fTop;
+        adjSrcRect.fBottom = clippedSrcRect.fBottom;
+        adjDstPoint.fY = clippedDstPoint.fY;
+    }
+
     GrOpMemoryPool* pool = context->priv().opMemoryPool();
 
-    return pool->allocate<GrCopySurfaceOp>(srcProxy, clippedSrcRect, clippedDstPoint);
+    return pool->allocate<GrCopySurfaceOp>(srcProxy, dstProxy, adjSrcRect, adjDstPoint);
 }
 
 void GrCopySurfaceOp::onExecute(GrOpFlushState* state, const SkRect& chainBounds) {
     SkASSERT(fSrc.get()->isInstantiated());
 
-    state->commandBuffer()->copy(fSrc.get()->peekSurface(), fSrc.get()->origin(), fSrcRect,
-                                 fDstPoint);
+    // If we are using approx surfaces we may need to adjust our srcRect or dstPoint if the origin
+    // is bottom left.
+    GrSurfaceProxy* src = fSrc.get();
+    if (src->origin() == kBottomLeft_GrSurfaceOrigin) {
+        GrSurfaceProxy* dst = fDst.get();
+        SkASSERT(dst->isInstantiated());
+        if (src->height() != src->peekSurface()->height()) {
+            fSrcRect.offset(0, src->peekSurface()->height() - src->height());
+        }
+        if (dst->height() != dst->peekSurface()->height()) {
+            fDstPoint.fY = fDstPoint.fY + (dst->peekSurface()->height() - dst->height());
+        }
+    }
+
+    state->commandBuffer()->copy(fSrc.get()->peekSurface(), fSrcRect, fDstPoint);
 }
diff --git a/src/gpu/ops/GrCopySurfaceOp.h b/src/gpu/ops/GrCopySurfaceOp.h
index 794a7f5..58e196b 100644
--- a/src/gpu/ops/GrCopySurfaceOp.h
+++ b/src/gpu/ops/GrCopySurfaceOp.h
@@ -45,9 +45,11 @@
 private:
     friend class GrOpMemoryPool; // for ctor
 
-    GrCopySurfaceOp(GrSurfaceProxy* src, const SkIRect& srcRect, const SkIPoint& dstPoint)
+    GrCopySurfaceOp(GrSurfaceProxy* src, GrSurfaceProxy* dst, const SkIRect& srcRect,
+                    const SkIPoint& dstPoint)
             : INHERITED(ClassID())
             , fSrc(src)
+            , fDst(dst)
             , fSrcRect(srcRect)
             , fDstPoint(dstPoint) {
         SkRect bounds =
@@ -61,6 +63,7 @@
     void onExecute(GrOpFlushState*, const SkRect& chainBounds) override;
 
     GrPendingIOResource<GrSurfaceProxy, kRead_GrIOType>  fSrc;
+    GrPendingIOResource<GrSurfaceProxy, kWrite_GrIOType> fDst;
     SkIRect                                              fSrcRect;
     SkIPoint                                             fDstPoint;
 
diff --git a/src/gpu/vk/GrVkCaps.cpp b/src/gpu/vk/GrVkCaps.cpp
index 230ad8e..8a1cc60 100644
--- a/src/gpu/vk/GrVkCaps.cpp
+++ b/src/gpu/vk/GrVkCaps.cpp
@@ -54,8 +54,7 @@
 }
 
 bool GrVkCaps::initDescForDstCopy(const GrRenderTargetProxy* src, GrSurfaceDesc* desc,
-                                  GrSurfaceOrigin* origin, bool* rectsMustMatch,
-                                  bool* disallowSubrect) const {
+                                  bool* rectsMustMatch, bool* disallowSubrect) const {
     // Vk doesn't use rectsMustMatch or disallowSubrect. Always return false.
     *rectsMustMatch = false;
     *disallowSubrect = false;
@@ -63,7 +62,6 @@
     // We can always succeed here with either a CopyImage (none msaa src) or ResolveImage (msaa).
     // For CopyImage we can make a simple texture, for ResolveImage we require the dst to be a
     // render target as well.
-    *origin = src->origin();
     desc->fConfig = src->config();
     if (src->numColorSamples() > 1 || src->asTextureProxy()) {
         desc->fFlags = kRenderTarget_GrSurfaceFlag;
@@ -120,9 +118,8 @@
     return 0;
 }
 
-bool GrVkCaps::canCopyImage(GrPixelConfig dstConfig, int dstSampleCnt, GrSurfaceOrigin dstOrigin,
-                            bool dstHasYcbcr, GrPixelConfig srcConfig, int srcSampleCnt,
-                            GrSurfaceOrigin srcOrigin, bool srcHasYcbcr) const {
+bool GrVkCaps::canCopyImage(GrPixelConfig dstConfig, int dstSampleCnt, bool dstHasYcbcr,
+                            GrPixelConfig srcConfig, int srcSampleCnt, bool srcHasYcbcr) const {
     if ((dstSampleCnt > 1 || srcSampleCnt > 1) && dstSampleCnt != srcSampleCnt) {
         return false;
     }
@@ -133,8 +130,7 @@
 
     // We require that all vulkan GrSurfaces have been created with transfer_dst and transfer_src
     // as image usage flags.
-    if (srcOrigin != dstOrigin ||
-        get_compatible_format_class(srcConfig) != get_compatible_format_class(dstConfig)) {
+    if (get_compatible_format_class(srcConfig) != get_compatible_format_class(dstConfig)) {
         return false;
     }
 
@@ -179,10 +175,8 @@
     return true;
 }
 
-bool GrVkCaps::canCopyAsResolve(GrPixelConfig dstConfig, int dstSampleCnt,
-                                GrSurfaceOrigin dstOrigin, bool dstHasYcbcr,
-                                GrPixelConfig srcConfig, int srcSampleCnt,
-                                GrSurfaceOrigin srcOrigin, bool srcHasYcbcr) const {
+bool GrVkCaps::canCopyAsResolve(GrPixelConfig dstConfig, int dstSampleCnt, bool dstHasYcbcr,
+                                GrPixelConfig srcConfig, int srcSampleCnt, bool srcHasYcbcr) const {
     // The src surface must be multisampled.
     if (srcSampleCnt <= 1) {
         return false;
@@ -198,11 +192,6 @@
         return false;
     }
 
-    // Surfaces must have the same origin.
-    if (srcOrigin != dstOrigin) {
-        return false;
-    }
-
     if (dstHasYcbcr || srcHasYcbcr) {
         return false;
     }
@@ -210,32 +199,8 @@
     return true;
 }
 
-bool GrVkCaps::canCopyAsDraw(GrPixelConfig dstConfig, bool dstIsRenderable, bool dstHasYcbcr,
-                             GrPixelConfig srcConfig, bool srcIsTextureable,
-                             bool srcHasYcbcr) const {
-    // TODO: Make copySurfaceAsDraw handle the swizzle
-    if (this->shaderCaps()->configOutputSwizzle(srcConfig) !=
-        this->shaderCaps()->configOutputSwizzle(dstConfig)) {
-        return false;
-    }
-
-    // Make sure the dst is a render target and the src is a texture.
-    if (!dstIsRenderable || !srcIsTextureable) {
-        return false;
-    }
-
-    if (dstHasYcbcr) {
-        return false;
-    }
-
-    return true;
-}
-
 bool GrVkCaps::onCanCopySurface(const GrSurfaceProxy* dst, const GrSurfaceProxy* src,
                                 const SkIRect& srcRect, const SkIPoint& dstPoint) const {
-    GrSurfaceOrigin dstOrigin = dst->origin();
-    GrSurfaceOrigin srcOrigin = src->origin();
-
     GrPixelConfig dstConfig = dst->config();
     GrPixelConfig srcConfig = src->config();
 
@@ -283,14 +248,12 @@
         }
     }
 
-    return this->canCopyImage(dstConfig, dstSampleCnt, dstOrigin, dstHasYcbcr,
-                              srcConfig, srcSampleCnt, srcOrigin, srcHasYcbcr) ||
+    return this->canCopyImage(dstConfig, dstSampleCnt, dstHasYcbcr,
+                              srcConfig, srcSampleCnt, srcHasYcbcr) ||
            this->canCopyAsBlit(dstConfig, dstSampleCnt, dstIsLinear, dstHasYcbcr,
                                srcConfig, srcSampleCnt, srcIsLinear, srcHasYcbcr) ||
-           this->canCopyAsResolve(dstConfig, dstSampleCnt, dstOrigin, dstHasYcbcr,
-                                  srcConfig, srcSampleCnt, srcOrigin, srcHasYcbcr) ||
-           this->canCopyAsDraw(dstConfig, dstSampleCnt > 0, dstHasYcbcr,
-                               srcConfig, SkToBool(src->asTextureProxy()), srcHasYcbcr);
+           this->canCopyAsResolve(dstConfig, dstSampleCnt, dstHasYcbcr,
+                                  srcConfig, srcSampleCnt, srcHasYcbcr);
 }
 
 template<typename T> T* get_extension_feature_struct(const VkPhysicalDeviceFeatures2& features,
diff --git a/src/gpu/vk/GrVkCaps.h b/src/gpu/vk/GrVkCaps.h
index 6dd6b0b..3f9af35 100644
--- a/src/gpu/vk/GrVkCaps.h
+++ b/src/gpu/vk/GrVkCaps.h
@@ -139,22 +139,17 @@
      * the surface is not a render target, otherwise it is the number of samples in the render
      * target.
      */
-    bool canCopyImage(GrPixelConfig dstConfig, int dstSampleCnt, GrSurfaceOrigin dstOrigin,
-                      bool dstHasYcbcr, GrPixelConfig srcConfig, int srcSamplecnt,
-                      GrSurfaceOrigin srcOrigin, bool srcHasYcbcr) const;
+    bool canCopyImage(GrPixelConfig dstConfig, int dstSampleCnt, bool dstHasYcbcr,
+                      GrPixelConfig srcConfig, int srcSamplecnt, bool srcHasYcbcr) const;
 
     bool canCopyAsBlit(GrPixelConfig dstConfig, int dstSampleCnt, bool dstIsLinear,
                        bool dstHasYcbcr, GrPixelConfig srcConfig, int srcSampleCnt,
                        bool srcIsLinear, bool srcHasYcbcr) const;
 
-    bool canCopyAsResolve(GrPixelConfig dstConfig, int dstSampleCnt, GrSurfaceOrigin dstOrigin,
-                          bool dstHasYcbcr, GrPixelConfig srcConfig, int srcSamplecnt,
-                          GrSurfaceOrigin srcOrigin, bool srcHasYcbcr) const;
+    bool canCopyAsResolve(GrPixelConfig dstConfig, int dstSampleCnt, bool dstHasYcbcr,
+                          GrPixelConfig srcConfig, int srcSamplecnt, bool srcHasYcbcr) const;
 
-    bool canCopyAsDraw(GrPixelConfig dstConfig, bool dstIsRenderable, bool dstHasYcbcr,
-                       GrPixelConfig srcConfig, bool srcIsTextureable, bool srcHasYcbcr) const;
-
-    bool initDescForDstCopy(const GrRenderTargetProxy* src, GrSurfaceDesc* desc, GrSurfaceOrigin*,
+    bool initDescForDstCopy(const GrRenderTargetProxy* src, GrSurfaceDesc* desc,
                             bool* rectsMustMatch, bool* disallowSubrect) const override;
 
     GrPixelConfig validateBackendRenderTarget(const GrBackendRenderTarget&,
diff --git a/src/gpu/vk/GrVkCopyManager.cpp b/src/gpu/vk/GrVkCopyManager.cpp
deleted file mode 100644
index 0467db8..0000000
--- a/src/gpu/vk/GrVkCopyManager.cpp
+++ /dev/null
@@ -1,458 +0,0 @@
-/*
- * Copyright 2016 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
-*/
-
-#include "src/gpu/vk/GrVkCopyManager.h"
-
-#include "include/core/SkPoint.h"
-#include "include/core/SkRect.h"
-#include "include/gpu/GrSamplerState.h"
-#include "include/gpu/GrSurface.h"
-#include "src/core/SkTraceEvent.h"
-#include "src/gpu/GrRenderTargetPriv.h"
-#include "src/gpu/GrShaderCaps.h"
-#include "src/gpu/GrTexturePriv.h"
-#include "src/gpu/vk/GrVkCommandBuffer.h"
-#include "src/gpu/vk/GrVkCommandPool.h"
-#include "src/gpu/vk/GrVkCopyPipeline.h"
-#include "src/gpu/vk/GrVkDescriptorSet.h"
-#include "src/gpu/vk/GrVkGpu.h"
-#include "src/gpu/vk/GrVkImageView.h"
-#include "src/gpu/vk/GrVkPipelineLayout.h"
-#include "src/gpu/vk/GrVkRenderTarget.h"
-#include "src/gpu/vk/GrVkResourceProvider.h"
-#include "src/gpu/vk/GrVkSampler.h"
-#include "src/gpu/vk/GrVkTexture.h"
-#include "src/gpu/vk/GrVkUniformBuffer.h"
-#include "src/gpu/vk/GrVkVertexBuffer.h"
-
-GrVkCopyManager::GrVkCopyManager()
-    : fVertShaderModule(VK_NULL_HANDLE)
-    , fFragShaderModule(VK_NULL_HANDLE)
-    , fPipelineLayout(nullptr) {}
-
-GrVkCopyManager::~GrVkCopyManager() {}
-
-bool GrVkCopyManager::createCopyProgram(GrVkGpu* gpu) {
-    TRACE_EVENT0("skia", TRACE_FUNC);
-
-    SkSL::String vertShaderText;
-    vertShaderText.append(
-        "#extension GL_ARB_separate_shader_objects : enable\n"
-        "#extension GL_ARB_shading_language_420pack : enable\n"
-
-        "layout(set = 0, binding = 0) uniform vertexUniformBuffer {"
-            "half4 uPosXform;"
-            "half4 uTexCoordXform;"
-        "};"
-        "layout(location = 0) in float2 inPosition;"
-        "layout(location = 1) out half2 vTexCoord;"
-
-        "// Copy Program VS\n"
-        "void main() {"
-            "vTexCoord = half2(inPosition * uTexCoordXform.xy + uTexCoordXform.zw);"
-            "sk_Position.xy = inPosition * uPosXform.xy + uPosXform.zw;"
-            "sk_Position.zw = half2(0, 1);"
-        "}"
-    );
-
-    SkSL::String fragShaderText;
-    fragShaderText.append(
-        "#extension GL_ARB_separate_shader_objects : enable\n"
-        "#extension GL_ARB_shading_language_420pack : enable\n"
-
-        "layout(set = 1, binding = 0) uniform sampler2D uTextureSampler;"
-        "layout(location = 1) in half2 vTexCoord;"
-
-        "// Copy Program FS\n"
-        "void main() {"
-            "sk_FragColor = texture(uTextureSampler, vTexCoord);"
-        "}"
-    );
-
-    SkSL::Program::Settings settings;
-    SkSL::String spirv;
-    SkSL::Program::Inputs inputs;
-    if (!GrCompileVkShaderModule(gpu, vertShaderText, VK_SHADER_STAGE_VERTEX_BIT,
-                                 &fVertShaderModule, &fShaderStageInfo[0], settings, &spirv,
-                                 &inputs)) {
-        this->destroyResources(gpu);
-        return false;
-    }
-    SkASSERT(inputs.isEmpty());
-
-    if (!GrCompileVkShaderModule(gpu, fragShaderText, VK_SHADER_STAGE_FRAGMENT_BIT,
-                                 &fFragShaderModule, &fShaderStageInfo[1], settings, &spirv,
-                                 &inputs)) {
-        this->destroyResources(gpu);
-        return false;
-    }
-    SkASSERT(inputs.isEmpty());
-
-    VkDescriptorSetLayout dsLayout[2];
-
-    GrVkResourceProvider& resourceProvider = gpu->resourceProvider();
-
-    dsLayout[GrVkUniformHandler::kUniformBufferDescSet] = resourceProvider.getUniformDSLayout();
-
-    uint32_t samplerVisibility = kFragment_GrShaderFlag;
-    SkTArray<uint32_t> visibilityArray(&samplerVisibility, 1);
-
-    resourceProvider.getSamplerDescriptorSetHandle(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
-                                                   visibilityArray, &fSamplerDSHandle);
-    dsLayout[GrVkUniformHandler::kSamplerDescSet] =
-        resourceProvider.getSamplerDSLayout(fSamplerDSHandle);
-
-    // Create the VkPipelineLayout
-    VkPipelineLayoutCreateInfo layoutCreateInfo;
-    memset(&layoutCreateInfo, 0, sizeof(VkPipelineLayoutCreateFlags));
-    layoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
-    layoutCreateInfo.pNext = 0;
-    layoutCreateInfo.flags = 0;
-    layoutCreateInfo.setLayoutCount = 2;
-    layoutCreateInfo.pSetLayouts = dsLayout;
-    layoutCreateInfo.pushConstantRangeCount = 0;
-    layoutCreateInfo.pPushConstantRanges = nullptr;
-
-    VkPipelineLayout pipelineLayout;
-    VkResult err = GR_VK_CALL(gpu->vkInterface(), CreatePipelineLayout(gpu->device(),
-                                                                       &layoutCreateInfo,
-                                                                       nullptr,
-                                                                       &pipelineLayout));
-    if (err) {
-        this->destroyResources(gpu);
-        return false;
-    }
-
-    fPipelineLayout = new GrVkPipelineLayout(pipelineLayout);
-
-    static const float vdata[] = {
-        0, 0,
-        0, 1,
-        1, 0,
-        1, 1
-    };
-    fVertexBuffer = GrVkVertexBuffer::Make(gpu, sizeof(vdata), false);
-    SkASSERT(fVertexBuffer.get());
-    fVertexBuffer->updateData(vdata, sizeof(vdata));
-
-    // We use 2 float4's for uniforms
-    fUniformBuffer.reset(GrVkUniformBuffer::Create(gpu, 8 * sizeof(float)));
-    SkASSERT(fUniformBuffer.get());
-
-    return true;
-}
-
-bool GrVkCopyManager::copySurfaceAsDraw(GrVkGpu* gpu,
-                                        GrSurface* dst, GrSurfaceOrigin dstOrigin,
-                                        GrSurface* src, GrSurfaceOrigin srcOrigin,
-                                        const SkIRect& srcRect, const SkIPoint& dstPoint,
-                                        bool canDiscardOutsideDstRect) {
-    // None of our copy methods can handle a swizzle. TODO: Make copySurfaceAsDraw handle the
-    // swizzle.
-    if (gpu->caps()->shaderCaps()->configOutputSwizzle(src->config()) !=
-        gpu->caps()->shaderCaps()->configOutputSwizzle(dst->config())) {
-        return false;
-    }
-
-    GrVkRenderTarget* rt = static_cast<GrVkRenderTarget*>(dst->asRenderTarget());
-    if (!rt) {
-        return false;
-    }
-
-    GrVkTexture* srcTex = static_cast<GrVkTexture*>(src->asTexture());
-    if (!srcTex) {
-        return false;
-    }
-
-    if (VK_NULL_HANDLE == fVertShaderModule) {
-        SkASSERT(VK_NULL_HANDLE == fFragShaderModule &&
-                 nullptr == fPipelineLayout &&
-                 nullptr == fVertexBuffer.get() &&
-                 nullptr == fUniformBuffer.get());
-        if (!this->createCopyProgram(gpu)) {
-            SkDebugf("Failed to create copy program.\n");
-            return false;
-        }
-    }
-    SkASSERT(fPipelineLayout);
-
-    GrVkResourceProvider& resourceProv = gpu->resourceProvider();
-
-    GrVkCopyPipeline* pipeline = resourceProv.findOrCreateCopyPipeline(rt,
-                                                                       fShaderStageInfo,
-                                                                       fPipelineLayout->layout());
-    if (!pipeline) {
-        return false;
-    }
-
-    // UPDATE UNIFORM DESCRIPTOR SET
-    int w = srcRect.width();
-    int h = srcRect.height();
-
-    // dst rect edges in NDC (-1 to 1)
-    int dw = dst->width();
-    int dh = dst->height();
-    float dx0 = 2.f * dstPoint.fX / dw - 1.f;
-    float dx1 = 2.f * (dstPoint.fX + w) / dw - 1.f;
-    float dy0 = 2.f * dstPoint.fY / dh - 1.f;
-    float dy1 = 2.f * (dstPoint.fY + h) / dh - 1.f;
-    if (kBottomLeft_GrSurfaceOrigin == dstOrigin) {
-        dy0 = -dy0;
-        dy1 = -dy1;
-    }
-
-
-    float sx0 = (float)srcRect.fLeft;
-    float sx1 = (float)(srcRect.fLeft + w);
-    float sy0 = (float)srcRect.fTop;
-    float sy1 = (float)(srcRect.fTop + h);
-    int sh = src->height();
-    if (kBottomLeft_GrSurfaceOrigin == srcOrigin) {
-        sy0 = sh - sy0;
-        sy1 = sh - sy1;
-    }
-    // src rect edges in normalized texture space (0 to 1).
-    int sw = src->width();
-    sx0 /= sw;
-    sx1 /= sw;
-    sy0 /= sh;
-    sy1 /= sh;
-
-    float uniData[] = { dx1 - dx0, dy1 - dy0, dx0, dy0,    // posXform
-                        sx1 - sx0, sy1 - sy0, sx0, sy0 };  // texCoordXform
-
-    fUniformBuffer->updateData(gpu, uniData, sizeof(uniData), nullptr);
-
-    const GrVkDescriptorSet* uniformDS = resourceProv.getUniformDescriptorSet();
-    SkASSERT(uniformDS);
-
-    VkDescriptorBufferInfo uniBufferInfo;
-    uniBufferInfo.buffer = fUniformBuffer->buffer();
-    uniBufferInfo.offset = fUniformBuffer->offset();
-    uniBufferInfo.range = fUniformBuffer->size();
-
-    VkWriteDescriptorSet descriptorWrites;
-    descriptorWrites.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
-    descriptorWrites.pNext = nullptr;
-    descriptorWrites.dstSet = uniformDS->descriptorSet();
-    descriptorWrites.dstBinding = GrVkUniformHandler::kGeometryBinding;
-    descriptorWrites.dstArrayElement = 0;
-    descriptorWrites.descriptorCount = 1;
-    descriptorWrites.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
-    descriptorWrites.pImageInfo = nullptr;
-    descriptorWrites.pBufferInfo = &uniBufferInfo;
-    descriptorWrites.pTexelBufferView = nullptr;
-
-    GR_VK_CALL(gpu->vkInterface(), UpdateDescriptorSets(gpu->device(),
-                                                        1,
-                                                        &descriptorWrites,
-                                                        0, nullptr));
-
-    // UPDATE SAMPLER DESCRIPTOR SET
-    const GrVkDescriptorSet* samplerDS =
-        gpu->resourceProvider().getSamplerDescriptorSet(fSamplerDSHandle);
-
-    GrSamplerState samplerState = GrSamplerState::ClampNearest();
-
-    GrVkSampler* sampler = resourceProv.findOrCreateCompatibleSampler(
-            samplerState, GrVkYcbcrConversionInfo());
-
-    VkDescriptorImageInfo imageInfo;
-    memset(&imageInfo, 0, sizeof(VkDescriptorImageInfo));
-    imageInfo.sampler = sampler->sampler();
-    imageInfo.imageView = srcTex->textureView()->imageView();
-    imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
-
-    VkWriteDescriptorSet writeInfo;
-    memset(&writeInfo, 0, sizeof(VkWriteDescriptorSet));
-    writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
-    writeInfo.pNext = nullptr;
-    writeInfo.dstSet = samplerDS->descriptorSet();
-    writeInfo.dstBinding = 0;
-    writeInfo.dstArrayElement = 0;
-    writeInfo.descriptorCount = 1;
-    writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
-    writeInfo.pImageInfo = &imageInfo;
-    writeInfo.pBufferInfo = nullptr;
-    writeInfo.pTexelBufferView = nullptr;
-
-    GR_VK_CALL(gpu->vkInterface(), UpdateDescriptorSets(gpu->device(),
-                                                        1,
-                                                        &writeInfo,
-                                                        0, nullptr));
-
-    VkDescriptorSet vkDescSets[] = { uniformDS->descriptorSet(), samplerDS->descriptorSet() };
-
-    GrVkRenderTarget* texRT = static_cast<GrVkRenderTarget*>(srcTex->asRenderTarget());
-    if (texRT) {
-        gpu->resolveRenderTargetNoFlush(texRT);
-    }
-
-    // TODO: Make tighter bounds and then adjust bounds for origin and granularity if we see
-    //       any perf issues with using the whole bounds
-    SkIRect bounds = SkIRect::MakeWH(rt->width(), rt->height());
-
-    // Change layouts of rt and texture. We aren't blending so we don't need color attachment read
-    // access for blending.
-    GrVkImage* targetImage = rt->msaaImage() ? rt->msaaImage() : rt;
-    VkAccessFlags dstAccessFlags = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
-    if (!canDiscardOutsideDstRect) {
-        // We need to load the color attachment so need to be able to read it.
-        dstAccessFlags |= VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
-    }
-    targetImage->setImageLayout(gpu,
-                                VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
-                                dstAccessFlags,
-                                VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
-                                false);
-
-    srcTex->setImageLayout(gpu,
-                           VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
-                           VK_ACCESS_SHADER_READ_BIT,
-                           VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
-                           false);
-
-    GrStencilAttachment* stencil = rt->renderTargetPriv().getStencilAttachment();
-    if (stencil) {
-        GrVkStencilAttachment* vkStencil = (GrVkStencilAttachment*)stencil;
-        // We aren't actually using the stencil but we still load and store it so we need
-        // appropriate barriers.
-        // TODO: Once we refactor surface and how we conntect stencil to RTs, we should not even
-        // have the stencil on this render pass if possible.
-        vkStencil->setImageLayout(gpu,
-                                  VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
-                                  VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
-                                  VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
-                                  VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT,
-                                  false);
-    }
-
-    VkAttachmentLoadOp loadOp = canDiscardOutsideDstRect ? VK_ATTACHMENT_LOAD_OP_DONT_CARE
-                                                         : VK_ATTACHMENT_LOAD_OP_LOAD;
-    GrVkRenderPass::LoadStoreOps vkColorOps(loadOp, VK_ATTACHMENT_STORE_OP_STORE);
-    GrVkRenderPass::LoadStoreOps vkStencilOps(VK_ATTACHMENT_LOAD_OP_LOAD,
-                                              VK_ATTACHMENT_STORE_OP_STORE);
-    const GrVkRenderPass* renderPass;
-    const GrVkResourceProvider::CompatibleRPHandle& rpHandle = rt->compatibleRenderPassHandle();
-    if (rpHandle.isValid()) {
-        renderPass = gpu->resourceProvider().findRenderPass(rpHandle,
-                                                            vkColorOps,
-                                                            vkStencilOps);
-    } else {
-        renderPass = gpu->resourceProvider().findRenderPass(*rt,
-                                                            vkColorOps,
-                                                            vkStencilOps);
-    }
-
-    SkASSERT(renderPass->isCompatible(*rt->simpleRenderPass()));
-
-    GrVkPrimaryCommandBuffer* cmdBuffer = gpu->currentCommandBuffer();
-    cmdBuffer->beginRenderPass(gpu, renderPass, nullptr, *rt, bounds, true);
-
-    GrVkSecondaryCommandBuffer* secondary = gpu->cmdPool()->findOrCreateSecondaryCommandBuffer(gpu);
-    if (!secondary) {
-        return false;
-    }
-    secondary->begin(gpu, rt->framebuffer(), renderPass);
-
-    secondary->bindPipeline(gpu, pipeline);
-
-    // Uniform DescriptorSet, Sampler DescriptorSet, and vertex shader uniformBuffer
-    SkSTArray<3, const GrVkRecycledResource*> descriptorRecycledResources;
-    descriptorRecycledResources.push_back(uniformDS);
-    descriptorRecycledResources.push_back(samplerDS);
-    descriptorRecycledResources.push_back(fUniformBuffer->resource());
-
-    // One sampler, texture view, and texture
-    SkSTArray<3, const GrVkResource*> descriptorResources;
-    descriptorResources.push_back(sampler);
-    descriptorResources.push_back(srcTex->textureView());
-    descriptorResources.push_back(srcTex->resource());
-
-    secondary->bindDescriptorSets(gpu,
-                                  descriptorRecycledResources,
-                                  descriptorResources,
-                                  fPipelineLayout,
-                                  0,
-                                  2,
-                                  vkDescSets,
-                                  0,
-                                  nullptr);
-
-    // Set Dynamic viewport and stencil
-    // We always use one viewport the size of the RT
-    VkViewport viewport;
-    viewport.x = 0.0f;
-    viewport.y = 0.0f;
-    viewport.width = SkIntToScalar(rt->width());
-    viewport.height = SkIntToScalar(rt->height());
-    viewport.minDepth = 0.0f;
-    viewport.maxDepth = 1.0f;
-    secondary->setViewport(gpu, 0, 1, &viewport);
-
-    // We assume the scissor is not enabled so just set it to the whole RT
-    VkRect2D scissor;
-    scissor.extent.width = rt->width();
-    scissor.extent.height = rt->height();
-    scissor.offset.x = 0;
-    scissor.offset.y = 0;
-    secondary->setScissor(gpu, 0, 1, &scissor);
-
-    secondary->bindInputBuffer(gpu, 0, fVertexBuffer.get());
-    secondary->draw(gpu, 4, 1, 0, 0);
-    secondary->end(gpu);
-    cmdBuffer->executeCommands(gpu, secondary);
-    cmdBuffer->endRenderPass(gpu);
-    secondary->unref(gpu);
-
-    // Release all temp resources which should now be reffed by the cmd buffer
-    pipeline->unref(gpu);
-    uniformDS->unref(gpu);
-    samplerDS->unref(gpu);
-    sampler->unref(gpu);
-    renderPass->unref(gpu);
-
-    return true;
-}
-
-void GrVkCopyManager::destroyResources(GrVkGpu* gpu) {
-    if (VK_NULL_HANDLE != fVertShaderModule) {
-        GR_VK_CALL(gpu->vkInterface(), DestroyShaderModule(gpu->device(), fVertShaderModule,
-                                                           nullptr));
-        fVertShaderModule = VK_NULL_HANDLE;
-    }
-
-    if (VK_NULL_HANDLE != fFragShaderModule) {
-        GR_VK_CALL(gpu->vkInterface(), DestroyShaderModule(gpu->device(), fFragShaderModule,
-                                                           nullptr));
-        fFragShaderModule = VK_NULL_HANDLE;
-    }
-
-    if (fPipelineLayout) {
-        fPipelineLayout->unref(gpu);
-        fPipelineLayout = nullptr;
-    }
-
-    if (fUniformBuffer) {
-        fUniformBuffer->release(gpu);
-        fUniformBuffer.reset();
-    }
-}
-
-void GrVkCopyManager::abandonResources() {
-    fVertShaderModule = VK_NULL_HANDLE;
-    fFragShaderModule = VK_NULL_HANDLE;
-    if (fPipelineLayout) {
-        fPipelineLayout->unrefAndAbandon();
-        fPipelineLayout = nullptr;
-    }
-
-    if (fUniformBuffer) {
-        fUniformBuffer->abandon();
-        fUniformBuffer.reset();
-    }
-}
diff --git a/src/gpu/vk/GrVkCopyManager.h b/src/gpu/vk/GrVkCopyManager.h
deleted file mode 100644
index e4f02b6..0000000
--- a/src/gpu/vk/GrVkCopyManager.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2016 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
-*/
-
-#ifndef GrVkCopyManager_DEFINED
-#define GrVkCopyManager_DEFINED
-
-#include "include/gpu/GrTypes.h"
-#include "include/gpu/vk/GrVkTypes.h"
-#include "src/gpu/vk/GrVkDescriptorSetManager.h"
-
-class GrSurface;
-class GrVkCopyPipeline;
-class GrVkGpu;
-class GrVkPipelineLayout;
-class GrVkUniformBuffer;
-class GrVkVertexBuffer;
-struct SkIPoint;
-struct SkIRect;
-
-class GrVkCopyManager {
-public:
-    GrVkCopyManager();
-
-    ~GrVkCopyManager();
-
-    bool copySurfaceAsDraw(GrVkGpu* gpu,
-                           GrSurface* dst, GrSurfaceOrigin dstOrigin,
-                           GrSurface* src, GrSurfaceOrigin srcOrigin,
-                           const SkIRect& srcRect, const SkIPoint& dstPoint,
-                           bool canDiscardOutsideDstRect);
-
-    void destroyResources(GrVkGpu* gpu);
-    void abandonResources();
-
-private:
-    bool createCopyProgram(GrVkGpu* gpu);
-
-    // Everything below is only created once and shared by all copy draws/pipelines
-    VkShaderModule fVertShaderModule;
-    VkShaderModule fFragShaderModule;
-    VkPipelineShaderStageCreateInfo fShaderStageInfo[2];
-
-    GrVkDescriptorSetManager::Handle fSamplerDSHandle;
-    GrVkPipelineLayout* fPipelineLayout;
-
-    sk_sp<GrVkVertexBuffer> fVertexBuffer;
-    std::unique_ptr<GrVkUniformBuffer> fUniformBuffer;
-};
-
-#endif
diff --git a/src/gpu/vk/GrVkCopyPipeline.cpp b/src/gpu/vk/GrVkCopyPipeline.cpp
deleted file mode 100644
index 3b44f61..0000000
--- a/src/gpu/vk/GrVkCopyPipeline.cpp
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Copyright 2016 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "src/gpu/vk/GrVkCopyPipeline.h"
-
-#include "include/private/SkOnce.h"
-#include "src/gpu/vk/GrVkGpu.h"
-#include "src/gpu/vk/GrVkUtil.h"
-
-#if defined(SK_ENABLE_SCOPED_LSAN_SUPPRESSIONS)
-#include <sanitizer/lsan_interface.h>
-#endif
-
-static void setup_multisample_state(int numSamples,
-                                    VkPipelineMultisampleStateCreateInfo* multisampleInfo) {
-    memset(multisampleInfo, 0, sizeof(VkPipelineMultisampleStateCreateInfo));
-    multisampleInfo->sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
-    multisampleInfo->pNext = nullptr;
-    multisampleInfo->flags = 0;
-    SkAssertResult(GrSampleCountToVkSampleCount(numSamples,
-                                                &multisampleInfo->rasterizationSamples));
-    multisampleInfo->sampleShadingEnable = VK_FALSE;
-    multisampleInfo->minSampleShading = 0.0f;
-    multisampleInfo->pSampleMask = nullptr;
-    multisampleInfo->alphaToCoverageEnable = VK_FALSE;
-    multisampleInfo->alphaToOneEnable = VK_FALSE;
-}
-
-GrVkCopyPipeline* GrVkCopyPipeline::Create(GrVkGpu* gpu,
-                                           VkPipelineShaderStageCreateInfo* shaderStageInfo,
-                                           VkPipelineLayout pipelineLayout,
-                                           int numSamples,
-                                           const GrVkRenderPass& renderPass,
-                                           VkPipelineCache cache) {
-
-    static const VkVertexInputAttributeDescription attributeDesc = {
-        0,                        // location
-        0,                        // binding
-        VK_FORMAT_R32G32_SFLOAT,  // format
-        0,                        // offset
-    };
-
-    static const VkVertexInputBindingDescription bindingDesc = {
-        0,                           // binding
-        2 * sizeof(float),           // stride
-        VK_VERTEX_INPUT_RATE_VERTEX  // inputRate
-    };
-
-    static const VkPipelineVertexInputStateCreateInfo vertexInputInfo = {
-        VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,  // sType
-        nullptr,                                                    // pNext
-        0,                                                          // flags
-        1,                                                          // vertexBindingDescriptionCount
-        &bindingDesc,                                               // pVertexBindingDescriptions
-        1,                                                          // vertexAttributeDescriptionCnt
-        &attributeDesc,                                             // pVertexAttributeDescriptions
-    };
-
-    static const VkPipelineInputAssemblyStateCreateInfo inputAssemblyInfo = {
-        VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,  // sType
-        nullptr,                                                      // pNext
-        0,                                                            // flags
-        VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,                         // topology
-        VK_FALSE                                                      // primitiveRestartEnable
-    };
-
-    static const VkStencilOpState dummyStencilState = {
-        VK_STENCIL_OP_KEEP,   // failOp
-        VK_STENCIL_OP_KEEP,   // passOp
-        VK_STENCIL_OP_KEEP,   // depthFailOp
-        VK_COMPARE_OP_NEVER,  // compareOp
-        0,                    // compareMask
-        0,                    // writeMask
-        0                     // reference
-    };
-
-    static const VkPipelineDepthStencilStateCreateInfo stencilInfo = {
-        VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,  // sType
-        nullptr,                                                     // pNext
-        0,                                                           // flags
-        VK_FALSE,                                                    // depthTestEnable
-        VK_FALSE,                                                    // depthWriteEnable
-        VK_COMPARE_OP_ALWAYS,                                        // depthCompareOp
-        VK_FALSE,                                                    // depthBoundsTestEnable
-        VK_FALSE,                                                    // stencilTestEnable
-        dummyStencilState,                                           // front
-        dummyStencilState,                                           // bakc
-        0.0f,                                                        // minDepthBounds
-        1.0f                                                         // maxDepthBounds
-    };
-
-    static const VkPipelineViewportStateCreateInfo viewportInfo = {
-        VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,  // sType
-        nullptr,                                                // pNext
-        0,                                                      // flags
-        1,                                                      // viewportCount
-        nullptr,                                                // pViewports
-        1,                                                      // scissorCount
-        nullptr                                                 // pScissors
-    };
-
-    static const VkPipelineColorBlendAttachmentState attachmentState = {
-        VK_FALSE,                                             // blendEnable
-        VK_BLEND_FACTOR_ONE,                                  // srcColorBlendFactor
-        VK_BLEND_FACTOR_ZERO,                                 // dstColorBlendFactor
-        VK_BLEND_OP_ADD,                                      // colorBlendOp
-        VK_BLEND_FACTOR_ONE,                                  // srcAlphaBlendFactor
-        VK_BLEND_FACTOR_ZERO,                                 // dstAlphaBlendFactor
-        VK_BLEND_OP_ADD,                                      // alphaBlendOp
-        VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | // colorWriteMask
-        VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT   // colorWriteMask
-    };
-
-    static const VkPipelineColorBlendStateCreateInfo colorBlendInfo = {
-        VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,  // sType
-        nullptr,                                                   // pNext
-        0,                                                         // flags
-        VK_FALSE,                                                  // logicOpEnable
-        VK_LOGIC_OP_CLEAR,                                         // logicOp
-        1,                                                         // attachmentCount
-        &attachmentState,                                          // pAttachments
-        { 0.f, 0.f, 0.f, 0.f }                                       // blendConstants[4]
-    };
-
-    static const VkPipelineRasterizationStateCreateInfo rasterInfo = {
-        VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,  // sType
-        nullptr,                                                     // pNext
-        0,                                                           // flags
-        VK_FALSE,                                                    // depthClampEnable
-        VK_FALSE,                                                    // rasterizerDiscardEnabled
-        VK_POLYGON_MODE_FILL,                                        // polygonMode
-        VK_CULL_MODE_NONE,                                           // cullMode
-        VK_FRONT_FACE_COUNTER_CLOCKWISE,                             // frontFace
-        VK_FALSE,                                                    // depthBiasEnable
-        0.0f,                                                        // depthBiasConstantFactor
-        0.0f,                                                        // depthBiasClamp
-        0.0f,                                                        // depthBiasSlopeFactor
-        1.0f                                                         // lineWidth
-    };
-
-    static const VkDynamicState dynamicStates[2] = { VK_DYNAMIC_STATE_VIEWPORT,
-        VK_DYNAMIC_STATE_SCISSOR };
-    static const VkPipelineDynamicStateCreateInfo dynamicInfo = {
-        VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,  // sType
-        nullptr,                                               // pNext
-        0,                                                     // flags
-        2,                                                     // dynamicStateCount
-        dynamicStates                                          // pDynamicStates
-    };
-
-    VkPipelineMultisampleStateCreateInfo multisampleInfo;
-    setup_multisample_state(numSamples, &multisampleInfo);
-
-    VkGraphicsPipelineCreateInfo pipelineCreateInfo;
-    memset(&pipelineCreateInfo, 0, sizeof(VkGraphicsPipelineCreateInfo));
-    pipelineCreateInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
-    pipelineCreateInfo.pNext = nullptr;
-    pipelineCreateInfo.flags = 0;
-    pipelineCreateInfo.stageCount = 2;
-    pipelineCreateInfo.pStages = shaderStageInfo;
-    pipelineCreateInfo.pVertexInputState = &vertexInputInfo;
-    pipelineCreateInfo.pInputAssemblyState = &inputAssemblyInfo;
-    pipelineCreateInfo.pTessellationState = nullptr;
-    pipelineCreateInfo.pViewportState = &viewportInfo;
-    pipelineCreateInfo.pRasterizationState = &rasterInfo;
-    pipelineCreateInfo.pMultisampleState = &multisampleInfo;
-    pipelineCreateInfo.pDepthStencilState = &stencilInfo;
-    pipelineCreateInfo.pColorBlendState = &colorBlendInfo;
-    pipelineCreateInfo.pDynamicState = &dynamicInfo;
-    pipelineCreateInfo.layout = pipelineLayout;
-    pipelineCreateInfo.renderPass = renderPass.vkRenderPass();
-    pipelineCreateInfo.subpass = 0;
-    pipelineCreateInfo.basePipelineHandle = VK_NULL_HANDLE;
-    pipelineCreateInfo.basePipelineIndex = -1;
-
-    VkPipeline vkPipeline;
-    VkResult err;
-    {
-#if defined(SK_ENABLE_SCOPED_LSAN_SUPPRESSIONS)
-        // skia:8712
-        __lsan::ScopedDisabler lsanDisabler;
-#endif
-        err = GR_VK_CALL(gpu->vkInterface(), CreateGraphicsPipelines(gpu->device(),
-                                                                     cache, 1,
-                                                                     &pipelineCreateInfo,
-                                                                     nullptr, &vkPipeline));
-    }
-    if (err) {
-        SkDebugf("Failed to create copy pipeline. Error: %d\n", err);
-        return nullptr;
-    }
-
-    return new GrVkCopyPipeline(vkPipeline, &renderPass);
-}
-
-bool GrVkCopyPipeline::isCompatible(const GrVkRenderPass& rp) const {
-    return rp.isCompatible(*fRenderPass);
-}
diff --git a/src/gpu/vk/GrVkCopyPipeline.h b/src/gpu/vk/GrVkCopyPipeline.h
deleted file mode 100644
index 9b8d2d2..0000000
--- a/src/gpu/vk/GrVkCopyPipeline.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2016 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrVkCopyPipeline_DEFINED
-#define GrVkCopyPipeline_DEFINED
-
-#include "src/gpu/vk/GrVkPipeline.h"
-
-class GrVkCopyPipeline : public GrVkPipeline {
-public:
-    // We expect the passed in renderPass to be stored on the GrVkResourceProvider and not a local
-    // object of the client.
-    static GrVkCopyPipeline* Create(GrVkGpu* gpu,
-                                    VkPipelineShaderStageCreateInfo* shaderStageInfo,
-                                    VkPipelineLayout pipelineLayout,
-                                    int numSamples,
-                                    const GrVkRenderPass& renderPass,
-                                    VkPipelineCache cache);
-
-    bool isCompatible(const GrVkRenderPass& rp) const;
-
-#ifdef SK_TRACE_VK_RESOURCES
-    void dumpInfo() const override {
-        SkDebugf("GrVkCopyPipeline: %d (%d refs)\n", fPipeline, this->getRefCnt());
-    }
-#endif
-
-private:
-    GrVkCopyPipeline(VkPipeline pipeline, const GrVkRenderPass* renderPass)
-        : INHERITED(pipeline)
-        , fRenderPass(renderPass) {
-    }
-
-    const GrVkRenderPass* fRenderPass;
-
-    typedef GrVkPipeline INHERITED;
-};
-
-#endif
diff --git a/src/gpu/vk/GrVkGpu.cpp b/src/gpu/vk/GrVkGpu.cpp
index 83ce19e..eec68dd 100644
--- a/src/gpu/vk/GrVkGpu.cpp
+++ b/src/gpu/vk/GrVkGpu.cpp
@@ -262,9 +262,6 @@
     }
     fSemaphoresToSignal.reset();
 
-
-    fCopyManager.destroyResources(this);
-
     // must call this just before we destroy the command pool and VkDevice
     fResourceProvider.destroyResources(VK_ERROR_DEVICE_LOST == res);
 
@@ -299,7 +296,6 @@
             for (int i = 0; i < fSemaphoresToSignal.count(); ++i) {
                 fSemaphoresToSignal[i]->unrefAndAbandon();
             }
-            fCopyManager.abandonResources();
 
             // must call this just before we destroy the command pool and VkDevice
             fResourceProvider.abandonResources();
@@ -783,6 +779,11 @@
     // R8G8B8A8_UNORM image and then copy it.
     sk_sp<GrVkTexture> copyTexture;
     if (dataColorType == GrColorType::kRGB_888x && tex->imageFormat() == VK_FORMAT_R8G8B8_UNORM) {
+        bool dstHasYcbcr = tex->ycbcrConversionInfo().isValid();
+        if (!this->vkCaps().canCopyAsBlit(tex->config(), 1, false, dstHasYcbcr,
+                                          kRGBA_8888_GrPixelConfig, 1, false, false)) {
+            return false;
+        }
         GrSurfaceDesc surfDesc;
         surfDesc.fFlags = kRenderTarget_GrSurfaceFlag;
         surfDesc.fWidth = width;
@@ -811,16 +812,6 @@
             return false;
         }
 
-        bool dstHasYcbcr = tex->ycbcrConversionInfo().isValid();
-        if (!this->vkCaps().canCopyAsBlit(tex->config(), 1, false, dstHasYcbcr,
-                                          copyTexture->config(), 1, false,
-                                          false) &&
-            !this->vkCaps().canCopyAsDraw(tex->config(), SkToBool(tex->asRenderTarget()),
-                                          dstHasYcbcr,
-                                          copyTexture->config(), true, false)) {
-            return false;
-        }
-
         uploadTexture = copyTexture.get();
         uploadLeft = 0;
         uploadTop = 0;
@@ -881,10 +872,8 @@
     // now.
     if (copyTexture.get()) {
         SkASSERT(dataColorType == GrColorType::kRGB_888x);
-        static const GrSurfaceOrigin kOrigin = kTopLeft_GrSurfaceOrigin;
-        SkAssertResult(this->copySurface(tex, kOrigin, copyTexture.get(), kOrigin,
-                                         SkIRect::MakeWH(width, height), SkIPoint::Make(left, top),
-                                         false));
+        SkAssertResult(this->copySurface(tex, copyTexture.get(), SkIRect::MakeWH(width, height),
+                                         SkIPoint::Make(left, top), false));
     }
     if (1 == mipLevelCount) {
         tex->texturePriv().markMipMapsDirty();
@@ -2102,19 +2091,16 @@
     return 0;
 }
 
-void GrVkGpu::copySurfaceAsCopyImage(GrSurface* dst, GrSurfaceOrigin dstOrigin,
-                                     GrSurface* src, GrSurfaceOrigin srcOrigin,
-                                     GrVkImage* dstImage,
-                                     GrVkImage* srcImage,
-                                     const SkIRect& srcRect,
+void GrVkGpu::copySurfaceAsCopyImage(GrSurface* dst, GrSurface* src, GrVkImage* dstImage,
+                                     GrVkImage* srcImage, const SkIRect& srcRect,
                                      const SkIPoint& dstPoint) {
 #ifdef SK_DEBUG
     int dstSampleCnt = get_surface_sample_cnt(dst);
     int srcSampleCnt = get_surface_sample_cnt(src);
     bool dstHasYcbcr = dstImage->ycbcrConversionInfo().isValid();
     bool srcHasYcbcr = srcImage->ycbcrConversionInfo().isValid();
-    SkASSERT(this->vkCaps().canCopyImage(dst->config(), dstSampleCnt, dstOrigin, dstHasYcbcr,
-                                         src->config(), srcSampleCnt, srcOrigin, srcHasYcbcr));
+    SkASSERT(this->vkCaps().canCopyImage(dst->config(), dstSampleCnt, dstHasYcbcr,
+                                         src->config(), srcSampleCnt, srcHasYcbcr));
 
 #endif
 
@@ -2132,24 +2118,13 @@
                              VK_PIPELINE_STAGE_TRANSFER_BIT,
                              false);
 
-    // Flip rect if necessary
-    SkIRect srcVkRect = srcRect;
-    int32_t dstY = dstPoint.fY;
-
-    if (kBottomLeft_GrSurfaceOrigin == srcOrigin) {
-        SkASSERT(kBottomLeft_GrSurfaceOrigin == dstOrigin);
-        srcVkRect.fTop = src->height() - srcRect.fBottom;
-        srcVkRect.fBottom =  src->height() - srcRect.fTop;
-        dstY = dst->height() - dstPoint.fY - srcVkRect.height();
-    }
-
     VkImageCopy copyRegion;
     memset(&copyRegion, 0, sizeof(VkImageCopy));
     copyRegion.srcSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 };
-    copyRegion.srcOffset = { srcVkRect.fLeft, srcVkRect.fTop, 0 };
+    copyRegion.srcOffset = { srcRect.fLeft, srcRect.fTop, 0 };
     copyRegion.dstSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 };
-    copyRegion.dstOffset = { dstPoint.fX, dstY, 0 };
-    copyRegion.extent = { (uint32_t)srcVkRect.width(), (uint32_t)srcVkRect.height(), 1 };
+    copyRegion.dstOffset = { dstPoint.fX, dstPoint.fY, 0 };
+    copyRegion.extent = { (uint32_t)srcRect.width(), (uint32_t)srcRect.height(), 1 };
 
     fCurrentCmdBuffer->copyImage(this,
                                  srcImage,
@@ -2161,14 +2136,12 @@
 
     SkIRect dstRect = SkIRect::MakeXYWH(dstPoint.fX, dstPoint.fY,
                                         srcRect.width(), srcRect.height());
-    this->didWriteToSurface(dst, dstOrigin, &dstRect);
+    // The rect is already in device space so we pass in kTopLeft so no flip is done.
+    this->didWriteToSurface(dst, kTopLeft_GrSurfaceOrigin, &dstRect);
 }
 
-void GrVkGpu::copySurfaceAsBlit(GrSurface* dst, GrSurfaceOrigin dstOrigin,
-                                GrSurface* src, GrSurfaceOrigin srcOrigin,
-                                GrVkImage* dstImage,
-                                GrVkImage* srcImage,
-                                const SkIRect& srcRect,
+void GrVkGpu::copySurfaceAsBlit(GrSurface* dst, GrSurface* src, GrVkImage* dstImage,
+                                GrVkImage* srcImage, const SkIRect& srcRect,
                                 const SkIPoint& dstPoint) {
 #ifdef SK_DEBUG
     int dstSampleCnt = get_surface_sample_cnt(dst);
@@ -2193,40 +2166,14 @@
                              false);
 
     // Flip rect if necessary
-    SkIRect srcVkRect;
-    srcVkRect.fLeft = srcRect.fLeft;
-    srcVkRect.fRight = srcRect.fRight;
-    SkIRect dstRect;
-    dstRect.fLeft = dstPoint.fX;
-    dstRect.fRight = dstPoint.fX + srcRect.width();
-
-    if (kBottomLeft_GrSurfaceOrigin == srcOrigin) {
-        srcVkRect.fTop = src->height() - srcRect.fBottom;
-        srcVkRect.fBottom = src->height() - srcRect.fTop;
-    } else {
-        srcVkRect.fTop = srcRect.fTop;
-        srcVkRect.fBottom = srcRect.fBottom;
-    }
-
-    if (kBottomLeft_GrSurfaceOrigin == dstOrigin) {
-        dstRect.fTop = dst->height() - dstPoint.fY - srcVkRect.height();
-    } else {
-        dstRect.fTop = dstPoint.fY;
-    }
-    dstRect.fBottom = dstRect.fTop + srcVkRect.height();
-
-    // If we have different origins, we need to flip the top and bottom of the dst rect so that we
-    // get the correct origintation of the copied data.
-    if (srcOrigin != dstOrigin) {
-        using std::swap;
-        swap(dstRect.fTop, dstRect.fBottom);
-    }
+    SkIRect dstRect = SkIRect::MakeXYWH(dstPoint.fX, dstPoint.fY, srcRect.width(),
+                                        srcRect.height());
 
     VkImageBlit blitRegion;
     memset(&blitRegion, 0, sizeof(VkImageBlit));
     blitRegion.srcSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 };
-    blitRegion.srcOffsets[0] = { srcVkRect.fLeft, srcVkRect.fTop, 0 };
-    blitRegion.srcOffsets[1] = { srcVkRect.fRight, srcVkRect.fBottom, 1 };
+    blitRegion.srcOffsets[0] = { srcRect.fLeft, srcRect.fTop, 0 };
+    blitRegion.srcOffsets[1] = { srcRect.fRight, srcRect.fBottom, 1 };
     blitRegion.dstSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 };
     blitRegion.dstOffsets[0] = { dstRect.fLeft, dstRect.fTop, 0 };
     blitRegion.dstOffsets[1] = { dstRect.fRight, dstRect.fBottom, 1 };
@@ -2238,32 +2185,22 @@
                                  &blitRegion,
                                  VK_FILTER_NEAREST); // We never scale so any filter works here
 
-    dstRect = SkIRect::MakeXYWH(dstPoint.fX, dstPoint.fY, srcRect.width(), srcRect.height());
-    this->didWriteToSurface(dst, dstOrigin, &dstRect);
+    // The rect is already in device space so we pass in kTopLeft so no flip is done.
+    this->didWriteToSurface(dst, kTopLeft_GrSurfaceOrigin, &dstRect);
 }
 
-void GrVkGpu::copySurfaceAsResolve(GrSurface* dst, GrSurfaceOrigin dstOrigin, GrSurface* src,
-                                   GrSurfaceOrigin srcOrigin, const SkIRect& origSrcRect,
-                                   const SkIPoint& origDstPoint) {
+void GrVkGpu::copySurfaceAsResolve(GrSurface* dst, GrSurface* src, const SkIRect& srcRect,
+                                   const SkIPoint& dstPoint) {
     GrVkRenderTarget* srcRT = static_cast<GrVkRenderTarget*>(src->asRenderTarget());
-    SkIRect srcRect = origSrcRect;
-    SkIPoint dstPoint = origDstPoint;
-    if (kBottomLeft_GrSurfaceOrigin == srcOrigin) {
-        SkASSERT(kBottomLeft_GrSurfaceOrigin == dstOrigin);
-        srcRect = {origSrcRect.fLeft, src->height() - origSrcRect.fBottom,
-                   origSrcRect.fRight, src->height() - origSrcRect.fTop};
-        dstPoint.fY = dst->height() - dstPoint.fY - srcRect.height();
-    }
     this->resolveImage(dst, srcRT, srcRect, dstPoint);
-    SkIRect dstRect = SkIRect::MakeXYWH(origDstPoint.fX, origDstPoint.fY,
+    SkIRect dstRect = SkIRect::MakeXYWH(dstPoint.fX, dstPoint.fY,
                                         srcRect.width(), srcRect.height());
-    this->didWriteToSurface(dst, dstOrigin, &dstRect);
+    // The rect is already in device space so we pass in kTopLeft so no flip is done.
+    this->didWriteToSurface(dst, kTopLeft_GrSurfaceOrigin, &dstRect);
 }
 
-bool GrVkGpu::onCopySurface(GrSurface* dst, GrSurfaceOrigin dstOrigin,
-                            GrSurface* src, GrSurfaceOrigin srcOrigin,
-                            const SkIRect& srcRect, const SkIPoint& dstPoint,
-                            bool canDiscardOutsideDstRect) {
+bool GrVkGpu::onCopySurface(GrSurface* dst, GrSurface* src, const SkIRect& srcRect,
+                            const SkIPoint& dstPoint, bool canDiscardOutsideDstRect) {
 #ifdef SK_DEBUG
     if (GrVkRenderTarget* srcRT = static_cast<GrVkRenderTarget*>(src->asRenderTarget())) {
         SkASSERT(!srcRT->wrapsSecondaryCommandBuffer());
@@ -2304,33 +2241,22 @@
     bool dstHasYcbcr = dstImage->ycbcrConversionInfo().isValid();
     bool srcHasYcbcr = srcImage->ycbcrConversionInfo().isValid();
 
-    if (this->vkCaps().canCopyAsResolve(dstConfig, dstSampleCnt, dstOrigin, dstHasYcbcr,
-                                        srcConfig, srcSampleCnt, srcOrigin, srcHasYcbcr)) {
-        this->copySurfaceAsResolve(dst, dstOrigin, src, srcOrigin, srcRect, dstPoint);
+    if (this->vkCaps().canCopyAsResolve(dstConfig, dstSampleCnt, dstHasYcbcr,
+                                        srcConfig, srcSampleCnt, srcHasYcbcr)) {
+        this->copySurfaceAsResolve(dst, src, srcRect, dstPoint);
         return true;
     }
 
-    if (this->vkCaps().canCopyAsDraw(dstConfig, SkToBool(dst->asRenderTarget()), dstHasYcbcr,
-                                     srcConfig, SkToBool(src->asTexture()), srcHasYcbcr)) {
-        SkAssertResult(fCopyManager.copySurfaceAsDraw(this, dst, dstOrigin, src, srcOrigin, srcRect,
-                                                      dstPoint, canDiscardOutsideDstRect));
-        auto dstRect = srcRect.makeOffset(dstPoint.fX, dstPoint.fY);
-        this->didWriteToSurface(dst, dstOrigin, &dstRect);
-        return true;
-    }
-
-    if (this->vkCaps().canCopyImage(dstConfig, dstSampleCnt, dstOrigin, dstHasYcbcr,
-                                    srcConfig, srcSampleCnt, srcOrigin, srcHasYcbcr)) {
-        this->copySurfaceAsCopyImage(dst, dstOrigin, src, srcOrigin, dstImage, srcImage,
-                                     srcRect, dstPoint);
+    if (this->vkCaps().canCopyImage(dstConfig, dstSampleCnt, dstHasYcbcr,
+                                    srcConfig, srcSampleCnt, srcHasYcbcr)) {
+        this->copySurfaceAsCopyImage(dst, src, dstImage, srcImage, srcRect, dstPoint);
         return true;
     }
 
     if (this->vkCaps().canCopyAsBlit(dstConfig, dstSampleCnt, dstImage->isLinearTiled(),
                                      dstHasYcbcr, srcConfig, srcSampleCnt,
                                      srcImage->isLinearTiled(), srcHasYcbcr)) {
-        this->copySurfaceAsBlit(dst, dstOrigin, src, srcOrigin, dstImage, srcImage,
-                                srcRect, dstPoint);
+        this->copySurfaceAsBlit(dst, src, dstImage, srcImage, srcRect, dstPoint);
         return true;
     }
 
@@ -2380,6 +2306,17 @@
     if (dstColorType == GrColorType::kRGB_888x && image->imageFormat() == VK_FORMAT_R8G8B8_UNORM) {
         SkASSERT(surface->config() == kRGB_888_GrPixelConfig);
 
+        int srcSampleCount = 0;
+        if (rt) {
+            srcSampleCount = rt->numColorSamples();
+        }
+        bool srcHasYcbcr = image->ycbcrConversionInfo().isValid();
+        if (!this->vkCaps().canCopyAsBlit(kRGBA_8888_GrPixelConfig, 1, false, false,
+                                          surface->config(), srcSampleCount, image->isLinearTiled(),
+                                          srcHasYcbcr)) {
+            return false;
+        }
+
         // Make a new surface that is RGBA to copy the RGB surface into.
         GrSurfaceDesc surfDesc;
         surfDesc.fFlags = kRenderTarget_GrSurfaceFlag;
@@ -2410,25 +2347,9 @@
             return false;
         }
 
-        int srcSampleCount = 0;
-        if (rt) {
-            srcSampleCount = rt->numColorSamples();
-        }
-        bool srcHasYcbcr = image->ycbcrConversionInfo().isValid();
-        if (!this->vkCaps().canCopyAsBlit(copySurface->config(), 1, false, false,
-                                          surface->config(), srcSampleCount, image->isLinearTiled(),
-                                          srcHasYcbcr) &&
-            !this->vkCaps().canCopyAsDraw(copySurface->config(), false, false,
-                                          surface->config(), SkToBool(surface->asTexture()),
-                                          srcHasYcbcr)) {
-            return false;
-        }
         SkIRect srcRect = SkIRect::MakeXYWH(left, top, width, height);
-        static const GrSurfaceOrigin kOrigin = kTopLeft_GrSurfaceOrigin;
-        if (!this->copySurface(copySurface.get(), kOrigin, surface, kOrigin,
-                               srcRect, SkIPoint::Make(0,0))) {
-            return false;
-        }
+        SkAssertResult(this->copySurface(copySurface.get(), surface, srcRect, SkIPoint::Make(0,0)));
+
         top = 0;
         left = 0;
         dstColorType = GrColorType::kRGBA_8888;
diff --git a/src/gpu/vk/GrVkGpu.h b/src/gpu/vk/GrVkGpu.h
index 820d801..d1b2d98 100644
--- a/src/gpu/vk/GrVkGpu.h
+++ b/src/gpu/vk/GrVkGpu.h
@@ -12,7 +12,6 @@
 #include "include/gpu/vk/GrVkTypes.h"
 #include "src/gpu/GrGpu.h"
 #include "src/gpu/vk/GrVkCaps.h"
-#include "src/gpu/vk/GrVkCopyManager.h"
 #include "src/gpu/vk/GrVkIndexBuffer.h"
 #include "src/gpu/vk/GrVkMemory.h"
 #include "src/gpu/vk/GrVkResourceProvider.h"
@@ -222,8 +221,7 @@
     bool onTransferPixelsFrom(GrSurface* surface, int left, int top, int width, int height,
                               GrColorType, GrGpuBuffer* transferBuffer, size_t offset) override;
 
-    bool onCopySurface(GrSurface* dst, GrSurfaceOrigin dstOrigin, GrSurface* src,
-                       GrSurfaceOrigin srcOrigin, const SkIRect& srcRect,
+    bool onCopySurface(GrSurface* dst, GrSurface* src, const SkIRect& srcRect,
                        const SkIPoint& dstPoint, bool canDiscardOutsideDstRect) override;
 
     void onFinishFlush(GrSurfaceProxy*[], int, SkSurface::BackendSurfaceAccess access,
@@ -240,21 +238,14 @@
 
     void internalResolveRenderTarget(GrRenderTarget*, bool requiresSubmit);
 
-    void copySurfaceAsCopyImage(GrSurface* dst, GrSurfaceOrigin dstOrigin,
-                                GrSurface* src, GrSurfaceOrigin srcOrigin,
-                                GrVkImage* dstImage, GrVkImage* srcImage,
-                                const SkIRect& srcRect,
+    void copySurfaceAsCopyImage(GrSurface* dst, GrSurface* src, GrVkImage* dstImage,
+                                GrVkImage* srcImage, const SkIRect& srcRect,
                                 const SkIPoint& dstPoint);
 
-    void copySurfaceAsBlit(GrSurface* dst, GrSurfaceOrigin dstOrigin,
-                           GrSurface* src, GrSurfaceOrigin srcOrigin,
-                           GrVkImage* dstImage, GrVkImage* srcImage,
-                           const SkIRect& srcRect,
-                           const SkIPoint& dstPoint);
+    void copySurfaceAsBlit(GrSurface* dst, GrSurface* src, GrVkImage* dstImage, GrVkImage* srcImage,
+                           const SkIRect& srcRect, const SkIPoint& dstPoint);
 
-    void copySurfaceAsResolve(GrSurface* dst, GrSurfaceOrigin dstOrigin,
-                              GrSurface* src, GrSurfaceOrigin srcOrigin,
-                              const SkIRect& srcRect,
+    void copySurfaceAsResolve(GrSurface* dst, GrSurface* src, const SkIRect& srcRect,
                               const SkIPoint& dstPoint);
 
     // helpers for onCreateTexture and writeTexturePixels
@@ -298,8 +289,6 @@
     VkPhysicalDeviceProperties                            fPhysDevProps;
     VkPhysicalDeviceMemoryProperties                      fPhysDevMemProps;
 
-    GrVkCopyManager                                       fCopyManager;
-
     // compiler used for compiling sksl into spirv. We only want to create the compiler once since
     // there is significant overhead to the first compile of any compiler.
     SkSL::Compiler*                                       fCompiler;
diff --git a/src/gpu/vk/GrVkGpuCommandBuffer.cpp b/src/gpu/vk/GrVkGpuCommandBuffer.cpp
index 26de1f0..10682c6 100644
--- a/src/gpu/vk/GrVkGpuCommandBuffer.cpp
+++ b/src/gpu/vk/GrVkGpuCommandBuffer.cpp
@@ -46,23 +46,19 @@
 
 class Copy : public GrVkPrimaryCommandBufferTask {
 public:
-    Copy(GrSurface* src, GrSurfaceOrigin srcOrigin, const SkIRect& srcRect,
-         const SkIPoint& dstPoint, bool shouldDiscardDst)
+    Copy(GrSurface* src, const SkIRect& srcRect, const SkIPoint& dstPoint, bool shouldDiscardDst)
             : fSrc(src)
-            , fSrcOrigin(srcOrigin)
             , fSrcRect(srcRect)
             , fDstPoint(dstPoint)
             , fShouldDiscardDst(shouldDiscardDst) {}
 
     void execute(const Args& args) override {
-        args.fGpu->copySurface(args.fSurface, args.fOrigin, fSrc.get(), fSrcOrigin, fSrcRect,
-                               fDstPoint, fShouldDiscardDst);
+        args.fGpu->copySurface(args.fSurface, fSrc.get(), fSrcRect, fDstPoint, fShouldDiscardDst);
     }
 
 private:
     using Src = GrPendingIOResource<GrSurface, kRead_GrIOType>;
     Src fSrc;
-    GrSurfaceOrigin fSrcOrigin;
     SkIRect fSrcRect;
     SkIPoint fDstPoint;
     bool fShouldDiscardDst;
@@ -94,9 +90,9 @@
 
 /////////////////////////////////////////////////////////////////////////////
 
-void GrVkGpuTextureCommandBuffer::copy(GrSurface* src, GrSurfaceOrigin srcOrigin,
-                                       const SkIRect& srcRect, const SkIPoint& dstPoint) {
-    fTasks.emplace<Copy>(src, srcOrigin, srcRect, dstPoint, false);
+void GrVkGpuTextureCommandBuffer::copy(GrSurface* src, const SkIRect& srcRect,
+                                       const SkIPoint& dstPoint) {
+    fTasks.emplace<Copy>(src, srcRect, dstPoint, false);
 }
 
 void GrVkGpuTextureCommandBuffer::transferFrom(const SkIRect& srcRect, GrColorType bufferColorType,
@@ -109,7 +105,7 @@
 }
 
 void GrVkGpuTextureCommandBuffer::submit() {
-    GrVkPrimaryCommandBufferTask::Args taskArgs{fGpu, fTexture, fOrigin};
+    GrVkPrimaryCommandBufferTask::Args taskArgs{fGpu, fTexture};
     for (auto& task : fTasks) {
         task.execute(taskArgs);
     }
@@ -231,7 +227,7 @@
     GrStencilAttachment* stencil = fRenderTarget->renderTargetPriv().getStencilAttachment();
     auto currPreCmd = fPreCommandBufferTasks.begin();
 
-    GrVkPrimaryCommandBufferTask::Args taskArgs{fGpu, fRenderTarget, fOrigin};
+    GrVkPrimaryCommandBufferTask::Args taskArgs{fGpu, fRenderTarget};
     for (int i = 0; i < fCommandBufferInfos.count(); ++i) {
         CommandBufferInfo& cbInfo = fCommandBufferInfos[i];
 
@@ -612,7 +608,7 @@
     ++fCommandBufferInfos[fCurrentCmdInfo].fNumPreCmds;
 }
 
-void GrVkGpuRTCommandBuffer::copy(GrSurface* src, GrSurfaceOrigin srcOrigin, const SkIRect& srcRect,
+void GrVkGpuRTCommandBuffer::copy(GrSurface* src, const SkIRect& srcRect,
                                   const SkIPoint& dstPoint) {
     CommandBufferInfo& cbInfo = fCommandBufferInfos[fCurrentCmdInfo];
     if (!cbInfo.fIsEmpty || LoadStoreState::kStartsWithClear == cbInfo.fLoadStoreState) {
@@ -620,8 +616,7 @@
     }
 
     fPreCommandBufferTasks.emplace<Copy>(
-            src, srcOrigin, srcRect, dstPoint,
-            LoadStoreState::kStartsWithDiscard == cbInfo.fLoadStoreState);
+            src, srcRect, dstPoint, LoadStoreState::kStartsWithDiscard == cbInfo.fLoadStoreState);
     ++fCommandBufferInfos[fCurrentCmdInfo].fNumPreCmds;
 
     if (LoadStoreState::kLoadAndStore != cbInfo.fLoadStoreState) {
diff --git a/src/gpu/vk/GrVkGpuCommandBuffer.h b/src/gpu/vk/GrVkGpuCommandBuffer.h
index d5bed2b..d3eb668 100644
--- a/src/gpu/vk/GrVkGpuCommandBuffer.h
+++ b/src/gpu/vk/GrVkGpuCommandBuffer.h
@@ -31,7 +31,6 @@
     struct Args {
         GrGpu* fGpu;
         GrSurface* fSurface;
-        GrSurfaceOrigin fOrigin;
     };
 
     virtual void execute(const Args& args) = 0;
@@ -46,8 +45,7 @@
 public:
     GrVkGpuTextureCommandBuffer(GrVkGpu* gpu) : fGpu(gpu) {}
 
-    void copy(GrSurface* src, GrSurfaceOrigin srcOrigin, const SkIRect& srcRect,
-              const SkIPoint& dstPoint) override;
+    void copy(GrSurface* src, const SkIRect& srcRect, const SkIPoint& dstPoint) override;
     void transferFrom(const SkIRect& srcRect, GrColorType bufferColorType,
                       GrGpuBuffer* transferBuffer, size_t offset) override;
 
@@ -105,8 +103,7 @@
 
     void inlineUpload(GrOpFlushState* state, GrDeferredTextureUploadFn& upload) override;
 
-    void copy(GrSurface* src, GrSurfaceOrigin srcOrigin, const SkIRect& srcRect,
-              const SkIPoint& dstPoint) override;
+    void copy(GrSurface* src, const SkIRect& srcRect, const SkIPoint& dstPoint) override;
     void transferFrom(const SkIRect& srcRect, GrColorType bufferColorType,
                       GrGpuBuffer* transferBuffer, size_t offset) override;
 
diff --git a/src/gpu/vk/GrVkResourceProvider.cpp b/src/gpu/vk/GrVkResourceProvider.cpp
index 87f653e..81cd3a6 100644
--- a/src/gpu/vk/GrVkResourceProvider.cpp
+++ b/src/gpu/vk/GrVkResourceProvider.cpp
@@ -12,7 +12,6 @@
 #include "src/gpu/GrContextPriv.h"
 #include "src/gpu/vk/GrVkCommandBuffer.h"
 #include "src/gpu/vk/GrVkCommandPool.h"
-#include "src/gpu/vk/GrVkCopyPipeline.h"
 #include "src/gpu/vk/GrVkGpu.h"
 #include "src/gpu/vk/GrVkPipeline.h"
 #include "src/gpu/vk/GrVkRenderTarget.h"
@@ -107,33 +106,6 @@
             shaderStageCount, primitiveType, compatibleRenderPass, layout, this->pipelineCache());
 }
 
-GrVkCopyPipeline* GrVkResourceProvider::findOrCreateCopyPipeline(
-        const GrVkRenderTarget* dst,
-        VkPipelineShaderStageCreateInfo* shaderStageInfo,
-        VkPipelineLayout pipelineLayout) {
-    // Find or Create a compatible pipeline
-    GrVkCopyPipeline* pipeline = nullptr;
-    for (int i = 0; i < fCopyPipelines.count() && !pipeline; ++i) {
-        if (fCopyPipelines[i]->isCompatible(*dst->simpleRenderPass())) {
-            pipeline = fCopyPipelines[i];
-        }
-    }
-    if (!pipeline) {
-        pipeline = GrVkCopyPipeline::Create(fGpu, shaderStageInfo,
-                                            pipelineLayout,
-                                            dst->numColorSamples(),
-                                            *dst->simpleRenderPass(),
-                                            this->pipelineCache());
-        if (!pipeline) {
-            return nullptr;
-        }
-        fCopyPipelines.push_back(pipeline);
-    }
-    SkASSERT(pipeline);
-    pipeline->ref();
-    return pipeline;
-}
-
 // To create framebuffers, we first need to create a simple RenderPass that is
 // only used for framebuffer creation. When we actually render we will create
 // RenderPasses as needed that are compatible with the framebuffer.
@@ -404,11 +376,6 @@
         taskGroup->wait();
     }
 
-    // Release all copy pipelines
-    for (int i = 0; i < fCopyPipelines.count(); ++i) {
-        fCopyPipelines[i]->unref(fGpu);
-    }
-
     // loop over all render pass sets to make sure we destroy all the internal VkRenderPasses
     for (int i = 0; i < fRenderPassArray.count(); ++i) {
         fRenderPassArray[i].releaseResources(fGpu);
@@ -477,11 +444,6 @@
     }
     fAvailableCommandPools.reset();
 
-    // Abandon all copy pipelines
-    for (int i = 0; i < fCopyPipelines.count(); ++i) {
-        fCopyPipelines[i]->unrefAndAbandon();
-    }
-
     // loop over all render pass sets to make sure we destroy all the internal VkRenderPasses
     for (int i = 0; i < fRenderPassArray.count(); ++i) {
         fRenderPassArray[i].abandonResources();
diff --git a/src/gpu/vk/GrVkResourceProvider.h b/src/gpu/vk/GrVkResourceProvider.h
index 3df1a4d..649d461 100644
--- a/src/gpu/vk/GrVkResourceProvider.h
+++ b/src/gpu/vk/GrVkResourceProvider.h
@@ -27,7 +27,6 @@
 #include <thread>
 
 class GrVkCommandPool;
-class GrVkCopyPipeline;
 class GrVkGpu;
 class GrVkPipeline;
 class GrVkPipelineState;
@@ -55,10 +54,6 @@
                                  VkRenderPass compatibleRenderPass,
                                  VkPipelineLayout layout);
 
-    GrVkCopyPipeline* findOrCreateCopyPipeline(const GrVkRenderTarget* dst,
-                                               VkPipelineShaderStageCreateInfo*,
-                                               VkPipelineLayout);
-
     GR_DEFINE_RESOURCE_HANDLE_CLASS(CompatibleRPHandle);
 
     // Finds or creates a simple render pass that matches the target, increments the refcount,
@@ -269,9 +264,6 @@
     // Central cache for creating pipelines
     VkPipelineCache fPipelineCache;
 
-    // Cache of previously created copy pipelines
-    SkTArray<GrVkCopyPipeline*> fCopyPipelines;
-
     SkSTArray<4, CompatibleRenderPassSet> fRenderPassArray;
 
     SkTArray<const GrVkRenderPass*> fExternalRenderPasses;
diff --git a/src/image/SkImage_GpuBase.cpp b/src/image/SkImage_GpuBase.cpp
index 6cfc3e4..133e491 100644
--- a/src/image/SkImage_GpuBase.cpp
+++ b/src/image/SkImage_GpuBase.cpp
@@ -112,31 +112,17 @@
 
     sk_sp<GrSurfaceProxy> proxy = this->asTextureProxyRef(context);
 
-    GrSurfaceDesc desc;
-    desc.fWidth = subset.width();
-    desc.fHeight = subset.height();
-    desc.fConfig = proxy->config();
+    sk_sp<GrTextureProxy> copyProxy = GrSurfaceProxy::Copy(
+            context, proxy.get(), GrMipMapped::kNo, subset, SkBackingFit::kExact,
+            proxy->isBudgeted());
 
-    GrBackendFormat format = proxy->backendFormat().makeTexture2D();
-    if (!format.isValid()) {
-        return nullptr;
-    }
-
-    // TODO: Should this inherit our proxy's budgeted status?
-    sk_sp<GrSurfaceContext> sContext(context->priv().makeDeferredSurfaceContext(
-            format, desc, proxy->origin(), GrMipMapped::kNo, SkBackingFit::kExact,
-            proxy->isBudgeted()));
-    if (!sContext) {
-        return nullptr;
-    }
-
-    if (!sContext->copy(proxy.get(), subset, SkIPoint::Make(0, 0))) {
+    if (!copyProxy) {
         return nullptr;
     }
 
     // MDB: this call is okay bc we know 'sContext' was kExact
     return sk_make_sp<SkImage_Gpu>(fContext, kNeedNewImageUniqueID, this->alphaType(),
-                                   sContext->asTextureProxyRef(), this->refColorSpace());
+                                   std::move(copyProxy), this->refColorSpace());
 }
 
 static void apply_premul(const SkImageInfo& info, void* pixels, size_t rowBytes) {
diff --git a/tests/CopySurfaceTest.cpp b/tests/CopySurfaceTest.cpp
index e1a841e..3c9e48c 100644
--- a/tests/CopySurfaceTest.cpp
+++ b/tests/CopySurfaceTest.cpp
@@ -19,6 +19,7 @@
 #include "src/core/SkUtils.h"
 #include "src/gpu/GrCaps.h"
 #include "src/gpu/GrContextPriv.h"
+#include "src/gpu/GrRenderTargetContext.h"
 #include "src/gpu/GrSurfaceContext.h"
 #include "src/gpu/SkGr.h"
 #include "tests/Test.h"
@@ -106,7 +107,14 @@
                                 sk_sp<GrSurfaceContext> dstContext =
                                         context->priv().makeWrappedSurfaceContext(std::move(dst));
 
-                                bool result = dstContext->copy(src.get(), srcRect, dstPoint);
+                                bool result = false;
+                                if (sOrigin == dOrigin) {
+                                    result = dstContext->testCopy(src.get(), srcRect, dstPoint);
+                                } else if (dRenderable == GrRenderable::kYes) {
+                                    SkASSERT(dstContext->asRenderTargetContext());
+                                    result = dstContext->asRenderTargetContext()->blitTexture(
+                                            src.get(), srcRect, dstPoint);
+                                }
 
                                 bool expectedResult = true;
                                 SkIPoint dstOffset = { dstPoint.fX - srcRect.fLeft,
@@ -132,6 +140,10 @@
                                     !copiedDstRect.intersect(SkIRect::MakeWH(kW, kH))) {
                                     expectedResult = false;
                                 }
+                                if (sOrigin != dOrigin && dRenderable == GrRenderable::kNo) {
+                                    expectedResult = false;
+                                }
+
                                 // To make the copied src rect correct we would apply any dst
                                 // clipping back to the src rect, but we don't use it again so
                                 // don't bother.
diff --git a/tests/EGLImageTest.cpp b/tests/EGLImageTest.cpp
index 96483a5..7014c1f 100644
--- a/tests/EGLImageTest.cpp
+++ b/tests/EGLImageTest.cpp
@@ -194,7 +194,7 @@
     // Only test RT-config
     // TODO: why do we always need to draw to copy from an external texture?
     test_copy_from_surface(reporter, context0, surfaceContext->asSurfaceProxy(),
-                           pixels.get(), true, "EGLImageTest-copy");
+                           pixels.get(), "EGLImageTest-copy");
 
     cleanup(glCtx0, externalTexture.fID, glCtx1.get(), context1, &backendTexture1, image);
 }
diff --git a/tests/GrSurfaceTest.cpp b/tests/GrSurfaceTest.cpp
index f0c92ae..a6db616 100644
--- a/tests/GrSurfaceTest.cpp
+++ b/tests/GrSurfaceTest.cpp
@@ -323,13 +323,13 @@
                 SkImage::MakeFromRaster(write, nullptr, nullptr), kNone_GrSurfaceFlags, 1,
                 SkBudgeted::kYes, SkBackingFit::kExact);
         REPORTER_ASSERT(reporter, copySrc);
-        auto copyResult = surfContext->copy(copySrc.get());
+        auto copyResult = surfContext->testCopy(copySrc.get());
         REPORTER_ASSERT(reporter, copyResult == (ioType == kRW_GrIOType));
         // Try the low level copy.
         context->flush();
         auto gpuCopyResult = context->priv().getGpu()->copySurface(
-                proxy->peekTexture(), kTopLeft_GrSurfaceOrigin, copySrc->peekTexture(),
-                kTopLeft_GrSurfaceOrigin, SkIRect::MakeWH(kSize, kSize), {0, 0});
+                proxy->peekTexture(), copySrc->peekTexture(), SkIRect::MakeWH(kSize, kSize),
+                {0, 0});
         REPORTER_ASSERT(reporter, gpuCopyResult == (ioType == kRW_GrIOType));
 
         // Mip regen should not work with a read only texture.
@@ -351,9 +351,12 @@
     }
 }
 
+static const int kSurfSize = 10;
+
 static sk_sp<GrTexture> make_wrapped_texture(GrContext* context, GrRenderable renderable) {
     auto backendTexture = context->createBackendTexture(
-            10, 10, kRGBA_8888_SkColorType, SkColors::kTransparent, GrMipMapped::kNo, renderable);
+            kSurfSize, kSurfSize, kRGBA_8888_SkColorType, SkColors::kTransparent, GrMipMapped::kNo,
+            renderable);
     sk_sp<GrTexture> texture;
     if (GrRenderable::kYes == renderable) {
         texture = context->priv().resourceProvider()->wrapRenderableBackendTexture(
@@ -380,7 +383,7 @@
 static sk_sp<GrTexture> make_normal_texture(GrContext* context, GrRenderable renderable) {
     GrSurfaceDesc desc;
     desc.fConfig = kRGBA_8888_GrPixelConfig;
-    desc.fWidth = desc.fHeight = 10;
+    desc.fWidth = desc.fHeight = kSurfSize;
     desc.fFlags = GrRenderable::kYes == renderable ? kRenderTarget_GrSurfaceFlag
                                                    : kNone_GrSurfaceFlags;
     return context->priv().resourceProvider()->createTexture(
@@ -725,14 +728,14 @@
 
         // Insert a copy from idleTexture to another texture so that we have some queued IO on
         // idleTexture.
-        auto proxy = context->priv().proxyProvider()->testingOnly_createWrapped(
-                std::move(idleTexture), kTopLeft_GrSurfaceOrigin);
-        SkImageInfo info = SkImageInfo::Make(proxy->width(), proxy->height(),
-                                             kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+        SkImageInfo info = SkImageInfo::Make(kSurfSize, kSurfSize, kRGBA_8888_SkColorType,
+                                             kPremul_SkAlphaType);
         auto rt = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info, 0, nullptr);
         auto rtc = rt->getCanvas()->internal_private_accessTopLayerRenderTargetContext();
+        auto proxy = context->priv().proxyProvider()->testingOnly_createWrapped(
+                std::move(idleTexture), rtc->asSurfaceProxy()->origin());
         context->flush();
-        rtc->copy(proxy.get());
+        SkAssertResult(rtc->testCopy(proxy.get()));
         proxy.reset();
         REPORTER_ASSERT(reporter, flags == 0);
 
diff --git a/tests/RectangleTextureTest.cpp b/tests/RectangleTextureTest.cpp
index 37190a3..5214a5d 100644
--- a/tests/RectangleTextureTest.cpp
+++ b/tests/RectangleTextureTest.cpp
@@ -13,9 +13,9 @@
 #include "src/gpu/GrContextPriv.h"
 #include "src/gpu/GrProxyProvider.h"
 #include "src/gpu/GrRenderTargetContext.h"
+#include "src/gpu/GrSurfaceContextPriv.h"
 #include "src/gpu/GrSurfacePriv.h"
 #include "src/gpu/GrTexturePriv.h"
-#include "src/gpu/GrTextureProxyPriv.h"
 #include "src/gpu/SkGr.h"
 #include "src/gpu/gl/GrGLGpu.h"
 #include "src/gpu/gl/GrGLUtil.h"
@@ -93,12 +93,38 @@
     }
 }
 
+static void test_copy_to_surface(skiatest::Reporter* reporter,
+                                 GrContext* context,
+                                 GrSurfaceContext* dstContext,
+                                 const char* testName) {
+
+    int pixelCnt = dstContext->width() * dstContext->height();
+    SkAutoTMalloc<uint32_t> pixels(pixelCnt);
+    for (int y = 0; y < dstContext->width(); ++y) {
+        for (int x = 0; x < dstContext->height(); ++x) {
+            pixels.get()[y * dstContext->width() + x] =
+                SkColorToPremulGrColor(SkColorSetARGB(2*y, y, x, x * y));
+        }
+    }
+
+    for (auto renderable : {GrRenderable::kNo, GrRenderable::kYes}) {
+        auto origin = dstContext->asSurfaceProxy()->origin();
+        auto src = sk_gpu_test::MakeTextureProxyFromData(
+                context, renderable, dstContext->width(),
+                dstContext->height(), kRGBA_8888_SkColorType, origin, pixels.get(), 0);
+        // If this assert ever fails we can add a fallback to do copy as draw, but until then we can
+        // be more restrictive.
+        SkAssertResult(dstContext->testCopy(src.get()));
+        test_read_pixels(reporter, dstContext, pixels.get(), testName);
+    }
+}
+
 DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(RectangleTexture, reporter, ctxInfo) {
     GrContext* context = ctxInfo.grContext();
     GrProxyProvider* proxyProvider = context->priv().proxyProvider();
     sk_gpu_test::GLTestContext* glContext = ctxInfo.glContext();
-    static const int kWidth = 13;
-    static const int kHeight = 13;
+    static const int kWidth = 16;
+    static const int kHeight = 16;
 
     GrColor pixels[kWidth * kHeight];
     for (int y = 0; y < kHeight; ++y) {
@@ -159,7 +185,7 @@
 
         // Test copy to both a texture and RT
         test_copy_from_surface(reporter, context, rectProxy.get(), refPixels,
-                               false, "RectangleTexture-copy-from");
+                               "RectangleTexture-copy-from");
 
         sk_sp<GrSurfaceContext> rectContext = context->priv().makeWrappedSurfaceContext(
                                                                             std::move(rectProxy));
diff --git a/tests/TestUtils.cpp b/tests/TestUtils.cpp
index 34edd72..a63e656 100644
--- a/tests/TestUtils.cpp
+++ b/tests/TestUtils.cpp
@@ -13,11 +13,11 @@
 #include "include/utils/SkBase64.h"
 #include "src/core/SkUtils.h"
 #include "src/gpu/GrContextPriv.h"
+#include "src/gpu/GrDrawingManager.h"
 #include "src/gpu/GrGpu.h"
 #include "src/gpu/GrSurfaceContext.h"
-#include "src/gpu/GrSurfaceContextPriv.h"
+#include "src/gpu/GrTextureContext.h"
 #include "src/gpu/SkGr.h"
-#include "tools/gpu/ProxyUtils.h"
 
 void test_read_pixels(skiatest::Reporter* reporter,
                       GrSurfaceContext* srcContext, uint32_t expectedPixelValues[],
@@ -74,51 +74,16 @@
 
 void test_copy_from_surface(skiatest::Reporter* reporter, GrContext* context,
                             GrSurfaceProxy* proxy, uint32_t expectedPixelValues[],
-                            bool onlyTestRTConfig, const char* testName) {
-    GrSurfaceDesc copyDstDesc;
-    copyDstDesc.fWidth = proxy->width();
-    copyDstDesc.fHeight = proxy->height();
-    copyDstDesc.fConfig = kRGBA_8888_GrPixelConfig;
+                            const char* testName) {
+    sk_sp<GrTextureProxy> dstProxy = GrSurfaceProxy::Copy(context,  proxy, GrMipMapped::kNo,
+                                                          SkBackingFit::kExact, SkBudgeted::kYes);
+    SkASSERT(dstProxy);
 
-    for (auto flags : { kNone_GrSurfaceFlags, kRenderTarget_GrSurfaceFlag }) {
-        if (kNone_GrSurfaceFlags == flags && onlyTestRTConfig) {
-            continue;
-        }
+    sk_sp<GrSurfaceContext> dstContext =
+            context->priv().makeWrappedSurfaceContext(std::move(dstProxy));
+    SkASSERT(dstContext.get());
 
-        copyDstDesc.fFlags = flags;
-        auto origin = (kNone_GrSurfaceFlags == flags) ? kTopLeft_GrSurfaceOrigin
-                                                      : kBottomLeft_GrSurfaceOrigin;
-
-        sk_sp<GrSurfaceContext> dstContext(
-                GrSurfaceProxy::TestCopy(context, copyDstDesc, origin, proxy));
-
-        test_read_pixels(reporter, dstContext.get(), expectedPixelValues, testName);
-    }
-}
-
-void test_copy_to_surface(skiatest::Reporter* reporter,
-                          GrContext* context,
-                          GrSurfaceContext* dstContext,
-                          const char* testName) {
-
-    int pixelCnt = dstContext->width() * dstContext->height();
-    SkAutoTMalloc<uint32_t> pixels(pixelCnt);
-    for (int y = 0; y < dstContext->width(); ++y) {
-        for (int x = 0; x < dstContext->height(); ++x) {
-            pixels.get()[y * dstContext->width() + x] =
-                SkColorToPremulGrColor(SkColorSetARGB(2*y, y, x, x * y));
-        }
-    }
-
-    for (auto renderable : {GrRenderable::kNo, GrRenderable::kYes}) {
-        for (auto origin : {kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin}) {
-            auto src = sk_gpu_test::MakeTextureProxyFromData(
-                    context, renderable, dstContext->width(),
-                    dstContext->height(), kRGBA_8888_SkColorType, origin, pixels.get(), 0);
-            dstContext->copy(src.get());
-            test_read_pixels(reporter, dstContext, pixels.get(), testName);
-        }
-    }
+    test_read_pixels(reporter, dstContext.get(), expectedPixelValues, testName);
 }
 
 void fill_pixel_data(int width, int height, GrColor* data) {
diff --git a/tests/TestUtils.h b/tests/TestUtils.h
index ec4d835..7efbe94 100644
--- a/tests/TestUtils.h
+++ b/tests/TestUtils.h
@@ -26,11 +26,7 @@
 // texture-backed and rendertarget-backed).
 void test_copy_from_surface(skiatest::Reporter*, GrContext*,
                             GrSurfaceProxy* proxy, uint32_t expectedPixelValues[],
-                            bool onlyTestRTConfig, const char* testName);
-
-// Ensure that RGBA 8888 pixels can be copied into 'dstContext'
-void test_copy_to_surface(skiatest::Reporter*, GrContext*,
-                          GrSurfaceContext* dstContext, const char* testName);
+                            const char* testName);
 
 // Fills data with a red-green gradient
 void fill_pixel_data(int width, int height, GrColor* data);
diff --git a/tests/VkMakeCopyPipelineTest.cpp b/tests/VkMakeCopyPipelineTest.cpp
deleted file mode 100644
index 1977286..0000000
--- a/tests/VkMakeCopyPipelineTest.cpp
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Copyright 2018 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-// This is a GPU-backend specific test. It relies on static intializers to work
-
-#include "include/core/SkTypes.h"
-
-#if defined(SK_VULKAN)
-
-#include "include/gpu/vk/GrVkVulkan.h"
-
-#include "include/gpu/GrBackendSurface.h"
-#include "include/gpu/GrTexture.h"
-#include "src/gpu/GrContextPriv.h"
-#include "src/gpu/vk/GrVkCopyPipeline.h"
-#include "src/gpu/vk/GrVkGpu.h"
-#include "src/gpu/vk/GrVkRenderTarget.h"
-#include "src/gpu/vk/GrVkUtil.h"
-#include "tests/Test.h"
-#include "tools/gpu/GrContextFactory.h"
-
-using sk_gpu_test::GrContextFactory;
-
-class TestVkCopyProgram {
-public:
-    TestVkCopyProgram()
-            : fVertShaderModule(VK_NULL_HANDLE)
-            , fFragShaderModule(VK_NULL_HANDLE)
-            , fPipelineLayout(VK_NULL_HANDLE) {}
-
-    void test(GrVkGpu* gpu, skiatest::Reporter* reporter) {
-        const char vertShaderText[] =
-            "#extension GL_ARB_separate_shader_objects : enable\n"
-            "#extension GL_ARB_shading_language_420pack : enable\n"
-
-            "layout(set = 0, binding = 0) uniform vertexUniformBuffer {"
-            "half4 uPosXform;"
-            "half4 uTexCoordXform;"
-            "};"
-            "layout(location = 0) in float2 inPosition;"
-            "layout(location = 1) out half2 vTexCoord;"
-
-            "// Copy Program VS\n"
-            "void main() {"
-            "vTexCoord = half2(inPosition * uTexCoordXform.xy + uTexCoordXform.zw);"
-            "sk_Position.xy = inPosition * uPosXform.xy + uPosXform.zw;"
-            "sk_Position.zw = half2(0, 1);"
-            "}";
-
-        const char fragShaderText[] =
-            "#extension GL_ARB_separate_shader_objects : enable\n"
-            "#extension GL_ARB_shading_language_420pack : enable\n"
-
-            "layout(set = 1, binding = 0) uniform sampler2D uTextureSampler;"
-            "layout(location = 1) in half2 vTexCoord;"
-
-            "// Copy Program FS\n"
-            "void main() {"
-            "sk_FragColor = texture(uTextureSampler, vTexCoord);"
-            "}";
-
-        SkSL::Program::Settings settings;
-        SkSL::String spirv;
-        SkSL::Program::Inputs inputs;
-        if (!GrCompileVkShaderModule(gpu, vertShaderText, VK_SHADER_STAGE_VERTEX_BIT,
-                                     &fVertShaderModule, &fShaderStageInfo[0], settings,
-                                     &spirv, &inputs)) {
-            this->destroyResources(gpu);
-            REPORTER_ASSERT(reporter, false);
-            return;
-        }
-        SkASSERT(inputs.isEmpty());
-
-        if (!GrCompileVkShaderModule(gpu, fragShaderText, VK_SHADER_STAGE_FRAGMENT_BIT,
-                                     &fFragShaderModule, &fShaderStageInfo[1], settings,
-                                     &spirv, &inputs)) {
-            this->destroyResources(gpu);
-            REPORTER_ASSERT(reporter, false);
-            return;
-        }
-
-        VkDescriptorSetLayout dsLayout[2];
-
-        GrVkResourceProvider& resourceProvider = gpu->resourceProvider();
-
-        dsLayout[GrVkUniformHandler::kUniformBufferDescSet] = resourceProvider.getUniformDSLayout();
-
-        uint32_t samplerVisibility = kFragment_GrShaderFlag;
-        SkTArray<uint32_t> visibilityArray(&samplerVisibility, 1);
-
-        resourceProvider.getSamplerDescriptorSetHandle(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
-                                                       visibilityArray, &fSamplerDSHandle);
-        dsLayout[GrVkUniformHandler::kSamplerDescSet] =
-                resourceProvider.getSamplerDSLayout(fSamplerDSHandle);
-
-        // Create the VkPipelineLayout
-        VkPipelineLayoutCreateInfo layoutCreateInfo;
-        memset(&layoutCreateInfo, 0, sizeof(VkPipelineLayoutCreateFlags));
-        layoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
-        layoutCreateInfo.pNext = 0;
-        layoutCreateInfo.flags = 0;
-        layoutCreateInfo.setLayoutCount = 2;
-        layoutCreateInfo.pSetLayouts = dsLayout;
-        layoutCreateInfo.pushConstantRangeCount = 0;
-        layoutCreateInfo.pPushConstantRanges = nullptr;
-
-        VkResult err = GR_VK_CALL(gpu->vkInterface(), CreatePipelineLayout(gpu->device(),
-                                                                           &layoutCreateInfo,
-                                                                           nullptr,
-                                                                           &fPipelineLayout));
-        if (err) {
-            this->destroyResources(gpu);
-            REPORTER_ASSERT(reporter, false);
-            return;
-        }
-
-        GrSurfaceDesc surfDesc;
-        surfDesc.fFlags = kRenderTarget_GrSurfaceFlag;
-        surfDesc.fWidth = 16;
-        surfDesc.fHeight = 16;
-        surfDesc.fConfig = kRGBA_8888_GrPixelConfig;
-        surfDesc.fSampleCnt = 1;
-        sk_sp<GrTexture> tex = gpu->createTexture(surfDesc, SkBudgeted::kNo);
-        if (!tex) {
-            this->destroyResources(gpu);
-            REPORTER_ASSERT(reporter, tex.get());
-            return;
-
-        }
-        GrRenderTarget* rt = tex->asRenderTarget();
-        REPORTER_ASSERT(reporter, rt);
-        GrVkRenderTarget* vkRT = static_cast<GrVkRenderTarget*>(rt);
-
-        GrVkCopyPipeline* copyPipeline = GrVkCopyPipeline::Create(gpu,
-                                                                  fShaderStageInfo,
-                                                                  fPipelineLayout,
-                                                                  1,
-                                                                  *vkRT->simpleRenderPass(),
-                                                                  VK_NULL_HANDLE);
-
-        REPORTER_ASSERT(reporter, copyPipeline);
-        if (copyPipeline) {
-            copyPipeline->unref(gpu);
-        }
-
-        this->destroyResources(gpu);
-    }
-
-    void destroyResources(GrVkGpu* gpu) {
-        if (VK_NULL_HANDLE != fVertShaderModule) {
-            GR_VK_CALL(gpu->vkInterface(), DestroyShaderModule(gpu->device(), fVertShaderModule,
-                                                               nullptr));
-            fVertShaderModule = VK_NULL_HANDLE;
-        }
-
-        if (VK_NULL_HANDLE != fFragShaderModule) {
-            GR_VK_CALL(gpu->vkInterface(), DestroyShaderModule(gpu->device(), fFragShaderModule,
-                                                               nullptr));
-            fFragShaderModule = VK_NULL_HANDLE;
-        }
-
-        if (VK_NULL_HANDLE != fPipelineLayout) {
-            GR_VK_CALL(gpu->vkInterface(), DestroyPipelineLayout(gpu->device(), fPipelineLayout,
-                                                                 nullptr));
-            fPipelineLayout = VK_NULL_HANDLE;
-        }
-    }
-
-    VkShaderModule fVertShaderModule;
-    VkShaderModule fFragShaderModule;
-    VkPipelineShaderStageCreateInfo fShaderStageInfo[2];
-
-    GrVkDescriptorSetManager::Handle fSamplerDSHandle;
-    VkPipelineLayout fPipelineLayout;
-
-};
-
-DEF_GPUTEST_FOR_VULKAN_CONTEXT(VkMakeCopyPipelineTest, reporter, ctxInfo) {
-    GrContext* context = ctxInfo.grContext();
-    GrVkGpu* gpu = static_cast<GrVkGpu*>(context->priv().getGpu());
-
-    TestVkCopyProgram copyProgram;
-    copyProgram.test(gpu, reporter);
-}
-
-#endif