Make SkImage_Gpu be deferred

This CL removes the GrTexture-based ctor forcing everyone to create deferred SkImage_Gpus.

split out into:
https://skia-review.googlesource.com/c/9106/ (Remove atlas creation from GrResourceProvider)

Change-Id: I266bbe089c242fe54d5b7adcc7895aa5a39440a0
Reviewed-on: https://skia-review.googlesource.com/6680
Commit-Queue: Robert Phillips <robertphillips@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
diff --git a/gm/image_pict.cpp b/gm/image_pict.cpp
index a6e0bdf..883fd34 100644
--- a/gm/image_pict.cpp
+++ b/gm/image_pict.cpp
@@ -344,6 +344,7 @@
     static void draw_as_tex(SkCanvas* canvas, SkImageCacherator* cache, SkScalar x, SkScalar y) {
 #if SK_SUPPORT_GPU
         sk_sp<SkColorSpace> texColorSpace;
+        // MDB TODO: this should be lockAsTextureRef
         sk_sp<GrTexture> texture(
             cache->lockAsTexture(canvas->getGrContext(), GrSamplerParams::ClampBilerp(),
                                  canvas->imageInfo().colorSpace(), &texColorSpace,
@@ -359,9 +360,13 @@
             canvas->drawLine(r.left(), r.bottom(), r.right(), r.top(), paint);
             return;
         }
+
+        sk_sp<GrTextureProxy> proxy = GrSurfaceProxy::MakeWrapped(std::move(texture));
+
         // No API to draw a GrTexture directly, so we cheat and create a private image subclass
-        sk_sp<SkImage> image(new SkImage_Gpu(cache->uniqueID(), kPremul_SkAlphaType,
-                                             std::move(texture), std::move(texColorSpace),
+        sk_sp<SkImage> image(new SkImage_Gpu(canvas->getGrContext(),
+                                             cache->uniqueID(), kPremul_SkAlphaType,
+                                             std::move(proxy), std::move(texColorSpace),
                                              SkBudgeted::kNo));
         canvas->drawImage(image.get(), x, y);
 #endif
diff --git a/include/private/GrSurfaceProxy.h b/include/private/GrSurfaceProxy.h
index 454916e..d7aa279 100644
--- a/include/private/GrSurfaceProxy.h
+++ b/include/private/GrSurfaceProxy.h
@@ -281,10 +281,13 @@
     }
 
     // Helper function that creates a temporary SurfaceContext to perform the copy
+    // It always returns a kExact-backed proxy bc it is used when converting an SkSpecialImage
+    // to an SkImage.
     static sk_sp<GrTextureProxy> Copy(GrContext*, GrSurfaceProxy* src,
                                       SkIRect srcRect, SkBudgeted);
 
     // Copy the entire 'src'
+    // It always returns a kExact-backed proxy bc it is used in SkGpuDevice::snapSpecial
     static sk_sp<GrTextureProxy> Copy(GrContext* context, GrSurfaceProxy* src,
                                       SkBudgeted budgeted);
 
diff --git a/src/core/SkSpecialImage.cpp b/src/core/SkSpecialImage.cpp
index 411b0ea..4c7e961 100644
--- a/src/core/SkSpecialImage.cpp
+++ b/src/core/SkSpecialImage.cpp
@@ -346,17 +346,10 @@
 ///////////////////////////////////////////////////////////////////////////////
 #include "GrTexture.h"
 
-static sk_sp<SkImage> wrap_proxy_in_image(GrContext* context, GrTextureProxy* proxy,
+static sk_sp<SkImage> wrap_proxy_in_image(GrContext* context, sk_sp<GrTextureProxy> proxy,
                                           SkAlphaType alphaType, sk_sp<SkColorSpace> colorSpace) {
-    // TODO: add GrTextureProxy-backed SkImage_Gpus
-    GrTexture* tex = proxy->instantiate(context->resourceProvider());
-    if (!tex) {
-        return nullptr;
-    }
-
-    return sk_make_sp<SkImage_Gpu>(kNeedNewImageUniqueID, alphaType,
-                                   sk_ref_sp(tex),
-                                   std::move(colorSpace), SkBudgeted::kYes);
+    return sk_make_sp<SkImage_Gpu>(context, kNeedNewImageUniqueID, alphaType,
+                                   std::move(proxy), std::move(colorSpace), SkBudgeted::kYes);
 }
 
 class SkSpecialImage_Gpu : public SkSpecialImage_Base {
@@ -386,21 +379,16 @@
         SkRect dst = SkRect::MakeXYWH(x, y,
                                       this->subset().width(), this->subset().height());
 
-        // TODO: add GrTextureProxy-backed SkImage_Gpus
-        GrTexture* tex = fTextureProxy->instantiate(fContext->resourceProvider());
-        if (!tex) {
-            return;
-        }
-
         // TODO: In this instance we know we're going to draw a sub-portion of the backing
         // texture into the canvas so it is okay to wrap it in an SkImage. This poses
         // some problems for full deferral however in that when the deferred SkImage_Gpu
         // instantiates itself it is going to have to either be okay with having a larger
         // than expected backing texture (unlikely) or the 'fit' of the SurfaceProxy needs
         // to be tightened (if it is deferred).
-        auto img = sk_sp<SkImage>(new SkImage_Gpu(this->uniqueID(), fAlphaType,
-                                                  sk_ref_sp(tex),
-                                                  fColorSpace, SkBudgeted::kNo));
+        sk_sp<SkImage> img = sk_sp<SkImage>(new SkImage_Gpu(canvas->getGrContext(),
+                                                            this->uniqueID(), fAlphaType,
+                                                            fTextureProxy,
+                                                            fColorSpace, SkBudgeted::kNo));
 
         canvas->drawImageRect(img, this->subset(),
                               dst, paint, SkCanvas::kStrict_SrcRectConstraint);
@@ -480,16 +468,22 @@
                 fTextureProxy->width() == subset->width() &&
                 fTextureProxy->height() == subset->height()) {
                 // The existing GrTexture is already tight so reuse it in the SkImage
-                return wrap_proxy_in_image(fContext, fTextureProxy.get(), fAlphaType, fColorSpace);
+                return wrap_proxy_in_image(fContext, fTextureProxy, fAlphaType, fColorSpace);
             }
 
             sk_sp<GrTextureProxy> subsetProxy(GrSurfaceProxy::Copy(fContext, fTextureProxy.get(),
                                                                    *subset, SkBudgeted::kYes));
+            if (!subsetProxy) {
+                return nullptr;
+            }
 
-            return wrap_proxy_in_image(fContext, subsetProxy.get(), fAlphaType, fColorSpace);
+            SkASSERT(subsetProxy->priv().isExact());
+            // MDB: this is acceptable (wrapping subsetProxy in an SkImage) bc Copy will
+            // return a kExact-backed proxy
+            return wrap_proxy_in_image(fContext, std::move(subsetProxy), fAlphaType, fColorSpace);
         }
 
-        return wrap_proxy_in_image(fContext, fTextureProxy.get(), fAlphaType, fColorSpace);
+        return wrap_proxy_in_image(fContext, fTextureProxy, fAlphaType, fColorSpace);
     }
 
     sk_sp<SkSurface> onMakeTightSurface(const SkImageFilter::OutputProperties& outProps,
diff --git a/src/gpu/GrDrawOpAtlas.h b/src/gpu/GrDrawOpAtlas.h
index c857d07..1119e3d 100644
--- a/src/gpu/GrDrawOpAtlas.h
+++ b/src/gpu/GrDrawOpAtlas.h
@@ -8,7 +8,6 @@
 #ifndef GrDrawOpAtlas_DEFINED
 #define GrDrawOpAtlas_DEFINED
 
-#include "GrTexture.h"
 #include "SkPoint.h"
 #include "SkTDArray.h"
 #include "SkTInternalLList.h"
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 676dc2b..8a6d973 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -14,6 +14,7 @@
 #include "GrImageTextureMaker.h"
 #include "GrRenderTargetContextPriv.h"
 #include "GrStyle.h"
+#include "GrSurfaceProxyPriv.h"
 #include "GrTextureAdjuster.h"
 #include "GrTextureProxy.h"
 #include "GrTracing.h"
@@ -165,7 +166,9 @@
     }
 
     GrPixelConfig config = SkImageInfo2GrPixelConfig(origInfo, *context->caps());
-    return context->makeRenderTargetContext(SkBackingFit::kExact,               // Why exact?
+    // This method is used to create SkGpuDevice's for SkSurface_Gpus. In this case
+    // they need to be exact.
+    return context->makeRenderTargetContext(SkBackingFit::kExact,
                                     origInfo.width(), origInfo.height(),
                                     config, origInfo.refColorSpace(), sampleCount,
                                     origin, surfaceProps, budgeted);
@@ -245,16 +248,19 @@
 
     SkBudgeted budgeted = fRenderTargetContext->priv().isBudgeted();
 
+    // This entry point is used by SkSurface_Gpu::onCopyOnWrite so it must create a
+    // kExact-backed render target context.
     sk_sp<GrRenderTargetContext> newRTC(MakeRenderTargetContext(
                                                             this->context(),
                                                             budgeted,
                                                             this->imageInfo(),
-                                                            fRenderTargetContext->numColorSamples(), 
-                                                            fRenderTargetContext->origin(), 
+                                                            fRenderTargetContext->numColorSamples(),
+                                                            fRenderTargetContext->origin(),
                                                             &this->surfaceProps()));
     if (!newRTC) {
         return;
     }
+    SkASSERT(newRTC->asSurfaceProxy()->priv().isExact());
 
     if (shouldRetainContent) {
         if (fRenderTargetContext->wasAbandoned()) {
@@ -1326,6 +1332,7 @@
     const SkImageInfo ii = this->imageInfo();
     const SkIRect srcRect = SkIRect::MakeWH(ii.width(), ii.height());
 
+    SkASSERT(proxy->priv().isExact());
     return SkSpecialImage::MakeDeferredFromGpu(fContext.get(),
                                                srcRect,
                                                kNeedNewImageUniqueID_SpecialImage,
diff --git a/src/gpu/SkGpuDevice.h b/src/gpu/SkGpuDevice.h
index 8c2bf04..b40eadd 100644
--- a/src/gpu/SkGpuDevice.h
+++ b/src/gpu/SkGpuDevice.h
@@ -48,9 +48,11 @@
      * New device that will create an offscreen renderTarget based on the ImageInfo and
      * sampleCount. The Budgeted param controls whether the device's backing store counts against
      * the resource cache budget. On failure, returns nullptr.
+     * This entry point creates a kExact backing store. It is used when creating SkGpuDevices
+     * for SkSurfaces.
      */
     static sk_sp<SkGpuDevice> Make(GrContext*, SkBudgeted, const SkImageInfo&,
-                                   int sampleCount, GrSurfaceOrigin, 
+                                   int sampleCount, GrSurfaceOrigin,
                                    const SkSurfaceProps*, InitContents);
 
     ~SkGpuDevice() override {}
diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp
index 18f2abb..0b9b8aa 100644
--- a/src/gpu/SkGr.cpp
+++ b/src/gpu/SkGr.cpp
@@ -301,6 +301,15 @@
                                                            mipLevelCount, 0, colorMode);
 }
 
+sk_sp<GrTextureProxy> GrUploadMipMapToTextureProxy(GrContext* ctx, const SkImageInfo& info,
+                                                   const GrMipLevel* texels,
+                                                   int mipLevelCount,
+                                                   SkDestinationSurfaceColorMode colorMode) {
+    sk_sp<GrTexture> tex(GrUploadMipMapToTexture(ctx, info, texels, mipLevelCount, colorMode));
+
+    return GrSurfaceProxy::MakeWrapped(std::move(tex));
+}
+
 GrTexture* GrRefCachedBitmapTexture(GrContext* ctx, const SkBitmap& bitmap,
                                     const GrSamplerParams& params, SkScalar scaleAdjust[2]) {
     // Caller doesn't care about the texture's color space (they can always get it from the bitmap)
diff --git a/src/gpu/SkGr.h b/src/gpu/SkGr.h
index 993bf83..b9fa0e8 100644
--- a/src/gpu/SkGr.h
+++ b/src/gpu/SkGr.h
@@ -236,6 +236,11 @@
 GrTexture* GrUploadMipMapToTexture(GrContext*, const SkImageInfo&, const GrMipLevel* texels,
                                    int mipLevelCount, SkDestinationSurfaceColorMode colorMode);
 
+sk_sp<GrTextureProxy> GrUploadMipMapToTextureProxy(GrContext*, const SkImageInfo&,
+                                                   const GrMipLevel* texels,
+                                                   int mipLevelCount,
+                                                   SkDestinationSurfaceColorMode colorMode);
+
 sk_sp<GrTexture> GrMakeCachedBitmapTexture(GrContext*, const SkBitmap&,
                                            const GrSamplerParams&, SkScalar scaleAdjust[2]);
 
diff --git a/src/image/SkImage.cpp b/src/image/SkImage.cpp
index 6217f4e..fe6369e 100644
--- a/src/image/SkImage.cpp
+++ b/src/image/SkImage.cpp
@@ -169,25 +169,14 @@
 #if SK_SUPPORT_GPU
 
 GrTexture* SkImage::getTexture() const {
-    return as_IB(this)->peekTexture();
+    return as_IB(this)->onGetTexture();
 }
 
-bool SkImage::isTextureBacked() const { return SkToBool(as_IB(this)->peekTexture()); }
+bool SkImage::isTextureBacked() const { return SkToBool(as_IB(this)->peekProxy()); }
 
 GrBackendObject SkImage::getTextureHandle(bool flushPendingGrContextIO,
                                           GrSurfaceOrigin* origin) const {
-    GrTexture* texture = as_IB(this)->peekTexture();
-    if (texture) {
-        GrContext* context = texture->getContext();
-        if (context && flushPendingGrContextIO) {
-            context->prepareSurfaceForExternalIO(texture);
-        }
-        if (origin) {
-            *origin = texture->origin();
-        }
-        return texture->getTextureHandle();
-    }
-    return 0;
+    return as_IB(this)->onGetTextureHandle(flushPendingGrContextIO, origin);
 }
 
 #else
diff --git a/src/image/SkImage_Base.h b/src/image/SkImage_Base.h
index 284af0d..e0319dd 100644
--- a/src/image/SkImage_Base.h
+++ b/src/image/SkImage_Base.h
@@ -47,13 +47,20 @@
     virtual bool onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes,
                               int srcX, int srcY, CachingHint) const = 0;
 
+    // MDB TODO: this entry point needs to go away
     virtual GrTexture* peekTexture() const { return nullptr; }
 #if SK_SUPPORT_GPU
+    virtual GrTextureProxy* peekProxy() const { return nullptr; }
     virtual sk_sp<GrTextureProxy> asTextureProxyRef() const { return nullptr; }
     virtual sk_sp<GrTextureProxy> asTextureProxyRef(GrContext*, const GrSamplerParams&,
                                                     SkColorSpace*, sk_sp<SkColorSpace>*,
                                                     SkScalar scaleAdjust[2]) const = 0;
     virtual sk_sp<GrTexture> refPinnedTexture(uint32_t* uniqueID) const { return nullptr; }
+    virtual GrBackendObject onGetTextureHandle(bool flushPendingGrContextIO,
+                                               GrSurfaceOrigin* origin) const {
+        return 0;
+    }
+    virtual GrTexture* onGetTexture() const { return nullptr; }
 #endif
     virtual SkImageCacherator* peekCacherator() const { return nullptr; }
 
diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp
index bcd7275..c9bc2ee 100644
--- a/src/image/SkImage_Gpu.cpp
+++ b/src/image/SkImage_Gpu.cpp
@@ -36,17 +36,6 @@
 #include "SkPixelRef.h"
 #include "SkReadPixelsRec.h"
 
-SkImage_Gpu::SkImage_Gpu(uint32_t uniqueID, SkAlphaType at, sk_sp<GrTexture> tex,
-                         sk_sp<SkColorSpace> colorSpace, SkBudgeted budgeted)
-    : INHERITED(tex->width(), tex->height(), uniqueID)
-    , fContext(tex->getContext())
-    , fProxy(GrSurfaceProxy::MakeWrapped(std::move(tex)))
-    , fAlphaType(at)
-    , fBudgeted(budgeted)
-    , fColorSpace(std::move(colorSpace))
-    , fAddedRasterVersionToCache(false) {
-}
-
 SkImage_Gpu::SkImage_Gpu(GrContext* context, uint32_t uniqueID, SkAlphaType at,
                          sk_sp<GrTextureProxy> proxy,
                          sk_sp<SkColorSpace> colorSpace, SkBudgeted budgeted)
@@ -165,6 +154,33 @@
     }
 }
 
+GrBackendObject SkImage_Gpu::onGetTextureHandle(bool flushPendingGrContextIO,
+                                                GrSurfaceOrigin* origin) const {
+    GrTextureProxy* proxy = this->peekProxy();
+    SkASSERT(proxy);
+
+    GrSurface* surface = proxy->instantiate(fContext->resourceProvider());
+    if (surface && surface->asTexture()) {
+        if (flushPendingGrContextIO) {
+            fContext->prepareSurfaceForExternalIO(surface);
+        }
+        if (origin) {
+            *origin = surface->origin();
+        }
+        return surface->asTexture()->getTextureHandle();
+    }
+    return 0;
+}
+
+GrTexture* SkImage_Gpu::onGetTexture() const {
+    GrTextureProxy* proxy = this->peekProxy();
+    if (!proxy) {
+        return nullptr;
+    }
+
+    return proxy->instantiate(fContext->resourceProvider());
+}
+
 bool SkImage_Gpu::onReadYUV8Planes(const SkISize sizes[3], void* const planes[3],
                                    const size_t rowBytes[3], SkYUVColorSpace colorSpace) const {
     if (GrTextureToYUVPlanes(fContext, fProxy, sizes, planes, rowBytes, colorSpace)) {
@@ -251,6 +267,7 @@
     if (desc.fWidth <= 0 || desc.fHeight <= 0) {
         return nullptr;
     }
+
     sk_sp<GrTexture> tex = ctx->resourceProvider()->wrapBackendTexture(desc, ownership);
     if (!tex) {
         return nullptr;
@@ -261,8 +278,9 @@
 
     const SkBudgeted budgeted = (kAdoptAndCache_GrWrapOwnership == ownership)
             ? SkBudgeted::kYes : SkBudgeted::kNo;
-    return sk_make_sp<SkImage_Gpu>(kNeedNewImageUniqueID,
-                                   at, std::move(tex), std::move(colorSpace), budgeted);
+    sk_sp<GrTextureProxy> proxy(GrSurfaceProxy::MakeWrapped(std::move(tex)));
+    return sk_make_sp<SkImage_Gpu>(ctx, kNeedNewImageUniqueID,
+                                   at, std::move(proxy), std::move(colorSpace), budgeted);
 }
 
 sk_sp<SkImage> SkImage::MakeFromTexture(GrContext* ctx, const GrBackendTextureDesc& desc,
@@ -388,7 +406,8 @@
                                        std::move(imageColorSpace));
 }
 
-static sk_sp<SkImage> create_image_from_maker(GrTextureMaker* maker, SkAlphaType at, uint32_t id,
+static sk_sp<SkImage> create_image_from_maker(GrContext* context,
+                                              GrTextureMaker* maker, SkAlphaType at, uint32_t id,
                                               SkColorSpace* dstColorSpace) {
     sk_sp<SkColorSpace> texColorSpace;
     sk_sp<GrTexture> texture(maker->refTextureForParams(GrSamplerParams::ClampNoFilter(),
@@ -396,7 +415,8 @@
     if (!texture) {
         return nullptr;
     }
-    return sk_make_sp<SkImage_Gpu>(id, at, std::move(texture),
+    sk_sp<GrTextureProxy> proxy = GrSurfaceProxy::MakeWrapped(std::move(texture));
+    return sk_make_sp<SkImage_Gpu>(context, id, at, std::move(proxy),
                                    std::move(texColorSpace), SkBudgeted::kNo);
 }
 
@@ -410,12 +430,14 @@
 
     if (SkImageCacherator* cacher = as_IB(this)->peekCacherator()) {
         GrImageTextureMaker maker(context, cacher, this, kDisallow_CachingHint);
-        return create_image_from_maker(&maker, this->alphaType(), this->uniqueID(), dstColorSpace);
+        return create_image_from_maker(context, &maker, this->alphaType(),
+                                       this->uniqueID(), dstColorSpace);
     }
 
     if (const SkBitmap* bmp = as_IB(this)->onPeekBitmap()) {
         GrBitmapTextureMaker maker(context, *bmp);
-        return create_image_from_maker(&maker, this->alphaType(), this->uniqueID(), dstColorSpace);
+        return create_image_from_maker(context, &maker, this->alphaType(),
+                                       this->uniqueID(), dstColorSpace);
     }
     return nullptr;
 }
@@ -824,13 +846,16 @@
     if (!ctx) {
         return nullptr;
     }
-    sk_sp<GrTexture> texture(GrUploadMipMapToTexture(ctx, info, texels, mipLevelCount, colorMode));
-    if (!texture) {
+    sk_sp<GrTextureProxy> proxy(GrUploadMipMapToTextureProxy(ctx, info, texels, mipLevelCount,
+                                                             colorMode));
+    if (!proxy) {
         return nullptr;
     }
-    return sk_make_sp<SkImage_Gpu>(kNeedNewImageUniqueID,
-                                   info.alphaType(), std::move(texture),
-                                   sk_ref_sp(info.colorSpace()), budgeted);
+
+    SkASSERT(proxy->priv().isExact());
+    return sk_make_sp<SkImage_Gpu>(ctx, kNeedNewImageUniqueID,
+                                   info.alphaType(), std::move(proxy),
+                                   info.refColorSpace(), budgeted);
 }
 
 sk_sp<SkImage> SkImage_Gpu::onMakeColorSpace(sk_sp<SkColorSpace> colorSpace) const {
diff --git a/src/image/SkImage_Gpu.h b/src/image/SkImage_Gpu.h
index 53c38dc..8584191 100644
--- a/src/image/SkImage_Gpu.h
+++ b/src/image/SkImage_Gpu.h
@@ -21,7 +21,6 @@
 
 class SkImage_Gpu : public SkImage_Base {
 public:
-    SkImage_Gpu(uint32_t uniqueID, SkAlphaType, sk_sp<GrTexture>, sk_sp<SkColorSpace>, SkBudgeted);
     SkImage_Gpu(GrContext*, uint32_t uniqueID, SkAlphaType, sk_sp<GrTextureProxy>,
                 sk_sp<SkColorSpace>, SkBudgeted);
     ~SkImage_Gpu() override;
@@ -34,6 +33,9 @@
                             sk_sp<SkColorSpace>*, SkScalar scaleAdjust[2]) const override;
     sk_sp<SkImage> onMakeSubset(const SkIRect&) const override;
 
+    GrTextureProxy* peekProxy() const override {
+        return fProxy.get();
+    }
     GrTexture* peekTexture() const override {
         return fProxy->instantiate(fContext->resourceProvider());
     }
@@ -43,10 +45,14 @@
     sk_sp<GrTextureProxy> asTextureProxyRef(GrContext*, const GrSamplerParams&, SkColorSpace*,
                                             sk_sp<SkColorSpace>*,
                                             SkScalar scaleAdjust[2]) const override;
+
     sk_sp<GrTexture> refPinnedTexture(uint32_t* uniqueID) const override {
         *uniqueID = this->uniqueID();
         return sk_ref_sp(this->peekTexture());
     }
+    GrBackendObject onGetTextureHandle(bool flushPendingGrContextIO,
+                                       GrSurfaceOrigin* origin) const override;
+    GrTexture* onGetTexture() const override;
 
     bool onReadYUV8Planes(const SkISize sizes[3], void* const planes[3],
                           const size_t rowBytes[3], SkYUVColorSpace colorSpace) const override;
diff --git a/src/image/SkSurface_Gpu.cpp b/src/image/SkSurface_Gpu.cpp
index b610c9c..fdbbf82 100644
--- a/src/image/SkSurface_Gpu.cpp
+++ b/src/image/SkSurface_Gpu.cpp
@@ -8,14 +8,15 @@
 #include "SkSurface_Gpu.h"
 
 #include "GrContextPriv.h"
+#include "GrRenderTargetContextPriv.h"
 #include "GrResourceProvider.h"
+
 #include "SkCanvas.h"
 #include "SkColorSpace_Base.h"
 #include "SkGpuDevice.h"
 #include "SkImage_Base.h"
 #include "SkImage_Gpu.h"
 #include "SkImagePriv.h"
-#include "GrRenderTargetContextPriv.h"
 #include "SkSurface_Base.h"
 
 #if SK_SUPPORT_GPU
@@ -23,6 +24,7 @@
 SkSurface_Gpu::SkSurface_Gpu(sk_sp<SkGpuDevice> device)
     : INHERITED(device->width(), device->height(), &device->surfaceProps())
     , fDevice(std::move(device)) {
+    SkASSERT(fDevice->accessRenderTargetContext()->asSurfaceProxy()->priv().isExact());
 }
 
 SkSurface_Gpu::~SkSurface_Gpu() {
@@ -91,38 +93,45 @@
 
     GrContext* ctx = fDevice->context();
 
-    GrSurfaceProxy* srcProxy = rtc->asSurfaceProxy();
-    sk_sp<GrSurfaceContext> copyCtx;
+    if (!rtc->asSurfaceProxy()) {
+        return nullptr;
+    }
+
+    SkBudgeted budgeted = rtc->asSurfaceProxy()->isBudgeted();
+
+    sk_sp<GrTextureProxy> srcProxy = rtc->asTextureProxyRef();
     // If the original render target is a buffer originally created by the client, then we don't
     // want to ever retarget the SkSurface at another buffer we create. Force a copy now to avoid
     // copy-on-write.
     if (!srcProxy || rtc->priv().refsWrappedObjects()) {
+        // MDB TODO: replace this with GrSurfaceProxy::Copy?
         GrSurfaceDesc desc = rtc->desc();
         desc.fFlags = desc.fFlags & ~kRenderTarget_GrSurfaceFlag;
 
-        copyCtx = ctx->contextPriv().makeDeferredSurfaceContext(desc,
+        sk_sp<GrSurfaceContext> copyCtx = ctx->contextPriv().makeDeferredSurfaceContext(
+                                                                desc,
                                                                 SkBackingFit::kExact,
-                                                                srcProxy->isBudgeted());
+                                                                budgeted);
         if (!copyCtx) {
             return nullptr;
         }
 
-        if (!copyCtx->copy(srcProxy)) {
+        if (!copyCtx->copy(rtc->asSurfaceProxy())) {
             return nullptr;
         }
 
-        srcProxy = copyCtx->asSurfaceProxy();
+        srcProxy = copyCtx->asTextureProxyRef();
     }
 
-    // TODO: add proxy-backed SkImage_Gpu
-    GrTexture* tex = srcProxy->instantiate(ctx->resourceProvider())->asTexture();
-
     const SkImageInfo info = fDevice->imageInfo();
     sk_sp<SkImage> image;
-    if (tex) {
-        image = sk_make_sp<SkImage_Gpu>(kNeedNewImageUniqueID,
-                                        info.alphaType(), sk_ref_sp(tex),
-                                        sk_ref_sp(info.colorSpace()), srcProxy->isBudgeted());
+    if (srcProxy) {
+        // The renderTargetContext coming out of SkGpuDevice should always be exact and the
+        // above copy creates a kExact surfaceContext.
+        SkASSERT(srcProxy->priv().isExact());
+        image = sk_make_sp<SkImage_Gpu>(ctx, kNeedNewImageUniqueID,
+                                        info.alphaType(), std::move(srcProxy),
+                                        info.refColorSpace(), budgeted);
     }
     return image;
 }
diff --git a/tests/SpecialImageTest.cpp b/tests/SpecialImageTest.cpp
index 6c1f1bc..70d6fd4 100644
--- a/tests/SpecialImageTest.cpp
+++ b/tests/SpecialImageTest.cpp
@@ -120,7 +120,7 @@
 
         REPORTER_ASSERT(reporter, tightImg->width() == subset.width());
         REPORTER_ASSERT(reporter, tightImg->height() == subset.height());
-        REPORTER_ASSERT(reporter, isGPUBacked == !!tightImg->isTextureBacked());
+        REPORTER_ASSERT(reporter, isGPUBacked == tightImg->isTextureBacked());
         SkPixmap tmpPixmap;
         REPORTER_ASSERT(reporter, isGPUBacked != !!tightImg->peekPixels(&tmpPixmap));
     }
diff --git a/tests/SurfaceTest.cpp b/tests/SurfaceTest.cpp
index 6264196..f620cff 100644
--- a/tests/SurfaceTest.cpp
+++ b/tests/SurfaceTest.cpp
@@ -451,7 +451,7 @@
 }
 
 static SkBudgeted is_budgeted(SkImage* image) {
-    return ((SkImage_Gpu*)image)->peekTexture()->resourcePriv().isBudgeted();
+    return ((SkImage_Gpu*)image)->peekProxy()->isBudgeted();
 }
 
 static SkBudgeted is_budgeted(const sk_sp<SkImage> image) {