Partially defer SkImage_Gpu

One of SkImageCacherator, GrBitmapTextureMaker, GrImageTextureMaker, GrTextureAdjuster, GrTextureProducer or SkImage has to take the first step. This is probably the least odd of the options.

Change-Id: Ie167034553451f4b3633a5a1548dbd4d75839b3d
Reviewed-on: https://skia-review.googlesource.com/9488
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
diff --git a/gm/image_pict.cpp b/gm/image_pict.cpp
index 1f273dc..a6e0bdf 100644
--- a/gm/image_pict.cpp
+++ b/gm/image_pict.cpp
@@ -360,8 +360,7 @@
             return;
         }
         // No API to draw a GrTexture directly, so we cheat and create a private image subclass
-        sk_sp<SkImage> image(new SkImage_Gpu(cache->info().width(), cache->info().height(),
-                                             cache->uniqueID(), kPremul_SkAlphaType,
+        sk_sp<SkImage> image(new SkImage_Gpu(cache->uniqueID(), kPremul_SkAlphaType,
                                              std::move(texture), std::move(texColorSpace),
                                              SkBudgeted::kNo));
         canvas->drawImage(image.get(), x, y);
diff --git a/include/gpu/GrSurfaceContext.h b/include/gpu/GrSurfaceContext.h
index ed049be..109c4a0 100644
--- a/include/gpu/GrSurfaceContext.h
+++ b/include/gpu/GrSurfaceContext.h
@@ -74,12 +74,12 @@
      *              unsupported pixel config.
      */
     bool readPixels(const SkImageInfo& dstInfo, void* dstBuffer, size_t dstRowBytes,
-                    int x, int y) {
-        return this->onReadPixels(dstInfo, dstBuffer, dstRowBytes, x, y);
+                    int x, int y, uint32_t flags = 0) {
+        return this->onReadPixels(dstInfo, dstBuffer, dstRowBytes, x, y, flags);
     }
 
     /**
-     * Writes a rectangle of pixels [srcInfo, srcBuffer, srcRowbytes] into the 
+     * Writes a rectangle of pixels [srcInfo, srcBuffer, srcRowbytes] into the
      * renderTargetContext at the specified position.
      * @param srcInfo       image info for the source pixels
      * @param srcBuffer     source for the write
@@ -137,7 +137,7 @@
                         const SkIRect& srcRect,
                         const SkIPoint& dstPoint) = 0;
     virtual bool onReadPixels(const SkImageInfo& dstInfo, void* dstBuffer,
-                              size_t dstRowBytes, int x, int y) = 0;
+                              size_t dstRowBytes, int x, int y, uint32_t flags) = 0;
     virtual bool onWritePixels(const SkImageInfo& srcInfo, const void* srcBuffer,
                                size_t srcRowBytes, int x, int y, uint32_t flags) = 0;
 
diff --git a/include/gpu/GrTextureContext.h b/include/gpu/GrTextureContext.h
index 6b2f0e3..d5e1eb9 100644
--- a/include/gpu/GrTextureContext.h
+++ b/include/gpu/GrTextureContext.h
@@ -48,7 +48,7 @@
 
     bool onCopy(GrSurfaceProxy* src, const SkIRect& srcRect, const SkIPoint& dstPoint) override;
     bool onReadPixels(const SkImageInfo& dstInfo, void* dstBuffer,
-                      size_t dstRowBytes, int x, int y) override;
+                      size_t dstRowBytes, int x, int y, uint32_t flags) override;
     bool onWritePixels(const SkImageInfo& srcInfo, const void* srcBuffer,
                        size_t srcRowBytes, int x, int y, uint32_t flags) override;
 
diff --git a/include/private/GrSurfaceProxy.h b/include/private/GrSurfaceProxy.h
index dd06f32..31e0f1d 100644
--- a/include/private/GrSurfaceProxy.h
+++ b/include/private/GrSurfaceProxy.h
@@ -331,7 +331,8 @@
     // For wrapped resources, 'fDesc' will always be filled in from the wrapped resource.
     const GrSurfaceDesc  fDesc;
     const SkBackingFit   fFit;      // always exact for wrapped resources
-    const SkBudgeted     fBudgeted; // set from the backing resource for wrapped resources
+    mutable SkBudgeted   fBudgeted; // set from the backing resource for wrapped resources
+                                    // mutable bc of SkSurface/SkImage wishy-washiness
     const uint32_t       fFlags;
     const UniqueID       fUniqueID; // set from the backing resource for wrapped resources
 
diff --git a/src/core/SkSpecialImage.cpp b/src/core/SkSpecialImage.cpp
index 1940fb9..dc71709 100644
--- a/src/core/SkSpecialImage.cpp
+++ b/src/core/SkSpecialImage.cpp
@@ -353,10 +353,7 @@
         return nullptr;
     }
 
-    // Note that we're explicitly using the GrTexture's width & height here b.c. SkImages
-    // must be tight.
-    return sk_make_sp<SkImage_Gpu>(tex->width(), tex->height(),
-                                   kNeedNewImageUniqueID, alphaType,
+    return sk_make_sp<SkImage_Gpu>(kNeedNewImageUniqueID, alphaType,
                                    sk_ref_sp(tex),
                                    std::move(colorSpace), SkBudgeted::kYes);
 }
@@ -400,8 +397,7 @@
         // 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(tex->width(), tex->height(),
-                                                  this->uniqueID(), fAlphaType,
+        auto img = sk_sp<SkImage>(new SkImage_Gpu(this->uniqueID(), fAlphaType,
                                                   sk_ref_sp(tex),
                                                   fColorSpace, SkBudgeted::kNo));
 
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index 5937054..051af82 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -157,16 +157,16 @@
 
 // TODO: move this (and GrTextureContext::onReadPixels) to GrSurfaceContext?
 bool GrRenderTargetContext::onReadPixels(const SkImageInfo& dstInfo, void* dstBuffer,
-                                         size_t dstRowBytes, int x, int y) {
+                                         size_t dstRowBytes, int x, int y, uint32_t flags) {
     // TODO: teach GrRenderTarget to take ImageInfo directly to specify the src pixels
     GrPixelConfig config = SkImageInfo2GrPixelConfig(dstInfo, *fContext->caps());
     if (kUnknown_GrPixelConfig == config) {
         return false;
     }
 
-    uint32_t flags = 0;
+    // TODO: this seems to duplicate code in SkImage_Gpu::onReadPixels
     if (kUnpremul_SkAlphaType == dstInfo.alphaType()) {
-        flags = GrContext::kUnpremul_PixelOpsFlag;
+        flags |= GrContext::kUnpremul_PixelOpsFlag;
     }
 
     // Deferral of the VRAM resources must end in this instance anyway
diff --git a/src/gpu/GrRenderTargetContext.h b/src/gpu/GrRenderTargetContext.h
index 479fc93..dc4944a 100644
--- a/src/gpu/GrRenderTargetContext.h
+++ b/src/gpu/GrRenderTargetContext.h
@@ -469,7 +469,7 @@
 
     bool onCopy(GrSurfaceProxy* src, const SkIRect& srcRect, const SkIPoint& dstPoint) override;
     bool onReadPixels(const SkImageInfo& dstInfo, void* dstBuffer,
-                      size_t dstRowBytes, int x, int y) override;
+                      size_t dstRowBytes, int x, int y, uint32_t flags) override;
     bool onWritePixels(const SkImageInfo& srcInfo, const void* srcBuffer,
                        size_t srcRowBytes, int x, int y, uint32_t flags) override;
 
diff --git a/src/gpu/GrSurfaceProxy.cpp b/src/gpu/GrSurfaceProxy.cpp
index 0c49750..180a7f0 100644
--- a/src/gpu/GrSurfaceProxy.cpp
+++ b/src/gpu/GrSurfaceProxy.cpp
@@ -6,6 +6,7 @@
  */
 
 #include "GrSurfaceProxy.h"
+#include "GrSurfaceProxyPriv.h"
 
 #include "GrCaps.h"
 #include "GrContext.h"
@@ -302,3 +303,19 @@
 
     return dstContext;
 }
+
+void GrSurfaceProxyPriv::makeBudgeted() {
+    if (fProxy->fTarget) {
+        fProxy->fTarget->resourcePriv().makeBudgeted();
+    }
+
+    fProxy->fBudgeted = SkBudgeted::kYes;
+}
+
+void GrSurfaceProxyPriv::makeUnbudgeted() {
+    if (fProxy->fTarget) {
+        fProxy->fTarget->resourcePriv().makeUnbudgeted();
+    }
+
+    fProxy->fBudgeted = SkBudgeted::kNo;
+}
diff --git a/src/gpu/GrSurfaceProxyPriv.h b/src/gpu/GrSurfaceProxyPriv.h
index 4e43dae..390806f 100644
--- a/src/gpu/GrSurfaceProxyPriv.h
+++ b/src/gpu/GrSurfaceProxyPriv.h
@@ -23,6 +23,18 @@
     // Don't abuse this!!!!!!!
     bool isExact() const { return SkBackingFit::kExact == fProxy->fFit; }
 
+    // These next two are very specialized and wacky - don't use them!
+
+    // In the case where an unbudgeted, deferred SkSurface_Gpu has snapped a budgeted, deferred
+    // SkImage_Gpu, this serves to propagate the budgeting forward in time. For now, and
+    // presumably forever, this will not change any flushing decisions but may make Ganesh
+    // appear to have gone over budget. In the case of non-deferred proxies this will immediately
+    // propagate the budget decision to the resource, which in itself is dubious.
+    void makeBudgeted();
+    // In the case where a budgeted, deferred SkSurface_Gpu has snapped an unbudgeted, deferred
+    // SkImage_Gpu, this serves to propagate the lack of budgeting forward in time.
+    void makeUnbudgeted();
+
 private:
     explicit GrSurfaceProxyPriv(GrSurfaceProxy* proxy) : fProxy(proxy) {}
     GrSurfaceProxyPriv(const GrSurfaceProxyPriv&) {} // unimpl
diff --git a/src/gpu/GrTextureContext.cpp b/src/gpu/GrTextureContext.cpp
index ffcddbc..0fbc951 100644
--- a/src/gpu/GrTextureContext.cpp
+++ b/src/gpu/GrTextureContext.cpp
@@ -109,16 +109,16 @@
 
 // TODO: move this (and GrRenderTargetContext::onReadPixels) to GrSurfaceContext?
 bool GrTextureContext::onReadPixels(const SkImageInfo& dstInfo, void* dstBuffer,
-                                    size_t dstRowBytes, int x, int y) {
+                                    size_t dstRowBytes, int x, int y, uint32_t flags) {
     // TODO: teach GrTexture to take ImageInfo directly to specify the src pixels
     GrPixelConfig config = SkImageInfo2GrPixelConfig(dstInfo, *fContext->caps());
     if (kUnknown_GrPixelConfig == config) {
         return false;
     }
 
-    uint32_t flags = 0;
+    // TODO: this seems to duplicate code in SkImage_Gpu::onReadPixels
     if (kUnpremul_SkAlphaType == dstInfo.alphaType()) {
-        flags = GrContext::kUnpremul_PixelOpsFlag;
+        flags |= GrContext::kUnpremul_PixelOpsFlag;
     }
 
     // Deferral of the VRAM resources must end in this instance anyway
diff --git a/src/image/SkImageShader.cpp b/src/image/SkImageShader.cpp
index 4f739f3..de225cc 100644
--- a/src/image/SkImageShader.cpp
+++ b/src/image/SkImageShader.cpp
@@ -154,25 +154,29 @@
     GrSamplerParams params(tm, textureFilterMode);
     sk_sp<SkColorSpace> texColorSpace;
     SkScalar scaleAdjust[2] = { 1.0f, 1.0f };
-    sk_sp<GrTexture> texture(as_IB(fImage)->asTextureRef(args.fContext, params, args.fDstColorSpace,
-                                                         &texColorSpace, scaleAdjust));
-    if (!texture) {
+    sk_sp<GrTextureProxy> proxy(as_IB(fImage)->asTextureProxyRef(args.fContext, params,
+                                                                 args.fDstColorSpace,
+                                                                 &texColorSpace, scaleAdjust));
+    if (!proxy) {
         return nullptr;
     }
 
+    bool isAlphaOnly = GrPixelConfigIsAlphaOnly(proxy->config());
+
     lmInverse.postScale(scaleAdjust[0], scaleAdjust[1]);
 
     sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(texColorSpace.get(),
                                                                        args.fDstColorSpace);
     sk_sp<GrFragmentProcessor> inner;
     if (doBicubic) {
-        inner = GrBicubicEffect::Make(texture.get(), std::move(colorSpaceXform), lmInverse, tm);
+        inner = GrBicubicEffect::Make(args.fContext, std::move(proxy), std::move(colorSpaceXform),
+                                      lmInverse, tm);
     } else {
-        inner = GrSimpleTextureEffect::Make(texture.get(), std::move(colorSpaceXform),
-                                            lmInverse, params);
+        inner = GrSimpleTextureEffect::Make(args.fContext, std::move(proxy),
+                                            std::move(colorSpaceXform), lmInverse, params);
     }
 
-    if (GrPixelConfigIsAlphaOnly(texture->config())) {
+    if (isAlphaOnly) {
         return inner;
     }
     return sk_sp<GrFragmentProcessor>(GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner)));
diff --git a/src/image/SkImage_Base.h b/src/image/SkImage_Base.h
index cc53358..e08d761 100644
--- a/src/image/SkImage_Base.h
+++ b/src/image/SkImage_Base.h
@@ -50,6 +50,9 @@
     virtual GrTexture* peekTexture() const { return nullptr; }
 #if SK_SUPPORT_GPU
     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; }
 #endif
     virtual SkImageCacherator* peekCacherator() const { return nullptr; }
diff --git a/src/image/SkImage_Generator.cpp b/src/image/SkImage_Generator.cpp
index 14516e4..ae3f9f8 100644
--- a/src/image/SkImage_Generator.cpp
+++ b/src/image/SkImage_Generator.cpp
@@ -26,7 +26,13 @@
         return fCache.info().alphaType();
     }
 
-    bool onReadPixels(const SkImageInfo&, void*, size_t, int srcX, int srcY, CachingHint) const override;
+    bool onReadPixels(const SkImageInfo&, void*, size_t, int srcX, int srcY,
+                      CachingHint) const override;
+#if SK_SUPPORT_GPU
+    sk_sp<GrTextureProxy> asTextureProxyRef(GrContext*, const GrSamplerParams&,
+                                            SkColorSpace*, sk_sp<SkColorSpace>*,
+                                            SkScalar scaleAdjust[2]) const override;
+#endif
     SkImageCacherator* peekCacherator() const override { return &fCache; }
     SkData* onRefEncoded(GrContext*) const override;
     sk_sp<SkImage> onMakeSubset(const SkIRect&) const override;
@@ -77,6 +83,22 @@
     return fCache.lockAsBitmap(nullptr, bitmap, this, dstColorSpace, chint);
 }
 
+#if SK_SUPPORT_GPU
+sk_sp<GrTextureProxy> SkImage_Generator::asTextureProxyRef(GrContext* context,
+                                                           const GrSamplerParams& params,
+                                                           SkColorSpace* dstColorSpace,
+                                                           sk_sp<SkColorSpace>* texColorSpace,
+                                                           SkScalar scaleAdjust[2]) const {
+    sk_sp<GrTexture> tex(fCache.lockAsTexture(context, params, dstColorSpace,
+                                              texColorSpace, this, scaleAdjust));
+    if (!tex) {
+        return nullptr;
+    }
+
+    return GrSurfaceProxy::MakeWrapped(std::move(tex));
+}
+#endif
+
 GrTexture* SkImage_Generator::asTextureRef(GrContext* ctx, const GrSamplerParams& params,
                                            SkColorSpace* dstColorSpace,
                                            sk_sp<SkColorSpace>* texColorSpace,
diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp
index 9ca790c..a3649ac 100644
--- a/src/image/SkImage_Gpu.cpp
+++ b/src/image/SkImage_Gpu.cpp
@@ -35,17 +35,27 @@
 #include "SkPixelRef.h"
 #include "SkReadPixelsRec.h"
 
-SkImage_Gpu::SkImage_Gpu(int w, int h, uint32_t uniqueID, SkAlphaType at, sk_sp<GrTexture> tex,
+SkImage_Gpu::SkImage_Gpu(uint32_t uniqueID, SkAlphaType at, sk_sp<GrTexture> tex,
                          sk_sp<SkColorSpace> colorSpace, SkBudgeted budgeted)
-    : INHERITED(w, h, uniqueID)
-    , fTexture(std::move(tex))
+    : 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)
-{
-    SkASSERT(fTexture->width() == w);
-    SkASSERT(fTexture->height() == h);
+    , fAddedRasterVersionToCache(false) {
+}
+
+SkImage_Gpu::SkImage_Gpu(GrContext* context, uint32_t uniqueID, SkAlphaType at,
+                         sk_sp<GrTextureProxy> proxy,
+                         sk_sp<SkColorSpace> colorSpace, SkBudgeted budgeted)
+    : INHERITED(proxy->width(), proxy->height(), uniqueID)
+    , fContext(context)
+    , fProxy(std::move(proxy))
+    , fAlphaType(at)
+    , fBudgeted(budgeted)
+    , fColorSpace(std::move(colorSpace))
+    , fAddedRasterVersionToCache(false) {
 }
 
 SkImage_Gpu::~SkImage_Gpu() {
@@ -62,10 +72,10 @@
 
 SkImageInfo SkImage_Gpu::onImageInfo() const {
     SkColorType ct;
-    if (!GrPixelConfigToColorType(fTexture->config(), &ct)) {
+    if (!GrPixelConfigToColorType(fProxy->config(), &ct)) {
         ct = kUnknown_SkColorType;
     }
-    return SkImageInfo::Make(fTexture->width(), fTexture->height(), ct, fAlphaType, fColorSpace);
+    return SkImageInfo::Make(fProxy->width(), fProxy->height(), ct, fAlphaType, fColorSpace);
 }
 
 static SkImageInfo make_info(int w, int h, SkAlphaType at, sk_sp<SkColorSpace> colorSpace) {
@@ -81,12 +91,20 @@
         return true;
     }
 
-    if (!dst->tryAllocPixels(make_info(this->width(), this->height(), this->alphaType(),
-                                       this->fColorSpace))) {
+    SkImageInfo ii = make_info(this->width(), this->height(), this->alphaType(),
+                               sk_ref_sp(dstColorSpace));
+    if (!dst->tryAllocPixels(ii)) {
         return false;
     }
-    if (!fTexture->readPixels(0, 0, dst->width(), dst->height(), kSkia8888_GrPixelConfig,
-                              dst->getPixels(), dst->rowBytes())) {
+
+    sk_sp<GrSurfaceContext> sContext = fContext->contextPriv().makeWrappedSurfaceContext(
+                                                                                    fProxy,
+                                                                                    fColorSpace);
+    if (!sContext) {
+        return false;
+    }
+
+    if (!sContext->readPixels(dst->info(), dst->getPixels(), dst->rowBytes(), 0, 0)) {
         return false;
     }
 
@@ -98,18 +116,35 @@
     return true;
 }
 
-sk_sp<GrTextureProxy> SkImage_Gpu::asTextureProxyRef() const {
-    return GrSurfaceProxy::MakeWrapped(fTexture);
+sk_sp<GrTextureProxy> SkImage_Gpu::asTextureProxyRef(GrContext* context,
+                                                     const GrSamplerParams& params,
+                                                     SkColorSpace* dstColorSpace,
+                                                     sk_sp<SkColorSpace>* texColorSpace,
+                                                     SkScalar scaleAdjust[2]) const {
+    sk_sp<GrTexture> tex(this->asTextureRef(context, params, dstColorSpace,
+                                            texColorSpace, scaleAdjust));
+
+    return GrSurfaceProxy::MakeWrapped(std::move(tex));
 }
 
 GrTexture* SkImage_Gpu::asTextureRef(GrContext* ctx, const GrSamplerParams& params,
                                      SkColorSpace* dstColorSpace,
                                      sk_sp<SkColorSpace>* texColorSpace,
                                      SkScalar scaleAdjust[2]) const {
+    if (ctx != fContext) {
+        SkASSERT(0);
+        return nullptr;
+    }
+
     if (texColorSpace) {
         *texColorSpace = this->fColorSpace;
     }
-    GrTextureAdjuster adjuster(fTexture.get(), this->alphaType(), this->bounds(),
+    GrTexture* texture = fProxy->instantiate(fContext->resourceProvider());
+    if (!texture) {
+        return nullptr;
+    }
+
+    GrTextureAdjuster adjuster(texture, this->alphaType(), this->bounds(),
                                this->uniqueID(), this->fColorSpace.get());
     return adjuster.refTextureSafeForParams(params, nullptr, scaleAdjust);
 }
@@ -137,11 +172,8 @@
 
 bool SkImage_Gpu::onReadYUV8Planes(const SkISize sizes[3], void* const planes[3],
                                    const size_t rowBytes[3], SkYUVColorSpace colorSpace) const {
-    if (sk_sp<GrTextureProxy> proxy = as_IB(this)->asTextureProxyRef()) {
-        if (GrTextureToYUVPlanes(fTexture->getContext(), std::move(proxy), sizes, planes,
-                                 rowBytes, colorSpace)) {
-            return true;
-        }
+    if (GrTextureToYUVPlanes(fContext, fProxy, sizes, planes, rowBytes, colorSpace)) {
+        return true;
     }
 
     return INHERITED::onReadYUV8Planes(sizes, planes, rowBytes, colorSpace);
@@ -158,17 +190,25 @@
         return false;
     }
 
-    GrPixelConfig config = SkImageInfo2GrPixelConfig(rec.fInfo, *fTexture->getContext()->caps());
+    // TODO: this seems to duplicate code in GrTextureContext::onReadPixels and
+    // GrRenderTargetContext::onReadPixels
     uint32_t flags = 0;
     if (kUnpremul_SkAlphaType == rec.fInfo.alphaType() && kPremul_SkAlphaType == fAlphaType) {
         // let the GPU perform this transformation for us
         flags = GrContext::kUnpremul_PixelOpsFlag;
     }
-    if (!fTexture->readPixels(fColorSpace.get(), rec.fX, rec.fY, rec.fInfo.width(),
-                              rec.fInfo.height(), config, rec.fInfo.colorSpace(), rec.fPixels,
-                              rec.fRowBytes, flags)) {
+
+    sk_sp<GrSurfaceContext> sContext = fContext->contextPriv().makeWrappedSurfaceContext(
+                                                                                    fProxy,
+                                                                                    fColorSpace);
+    if (!sContext) {
         return false;
     }
+
+    if (!sContext->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY, flags)) {
+        return false;
+    }
+
     // do we have to manually fix-up the alpha channel?
     //      src         dst
     //      unpremul    premul      fix manually
@@ -184,12 +224,11 @@
 }
 
 sk_sp<SkImage> SkImage_Gpu::onMakeSubset(const SkIRect& subset) const {
-    GrContext* ctx = fTexture->getContext();
-    GrSurfaceDesc desc = fTexture->desc();
+    GrSurfaceDesc desc = fProxy->desc();
     desc.fWidth = subset.width();
     desc.fHeight = subset.height();
 
-    sk_sp<GrSurfaceContext> sContext(ctx->contextPriv().makeDeferredSurfaceContext(
+    sk_sp<GrSurfaceContext> sContext(fContext->contextPriv().makeDeferredSurfaceContext(
                                                                         desc,
                                                                         SkBackingFit::kExact,
                                                                         fBudgeted));
@@ -197,24 +236,13 @@
         return nullptr;
     }
 
-    // TODO: make gpu images be proxy-backed so we don't need to do this
-    sk_sp<GrSurfaceProxy> tmpSrc(GrSurfaceProxy::MakeWrapped(fTexture));
-    if (!tmpSrc) {
+    if (!sContext->copy(fProxy.get(), subset, SkIPoint::Make(0, 0))) {
         return nullptr;
     }
 
-    if (!sContext->copy(tmpSrc.get(), subset, SkIPoint::Make(0, 0))) {
-        return nullptr;
-    }
-
-    // TODO: make gpu images be proxy-backed so we don't need to do this
-    GrSurface* subTx = sContext->asSurfaceProxy()->instantiate(ctx->resourceProvider());
-    if (!subTx) {
-        return nullptr;
-    }
-
-    return sk_make_sp<SkImage_Gpu>(desc.fWidth, desc.fHeight, kNeedNewImageUniqueID,
-                                   fAlphaType, sk_ref_sp(subTx->asTexture()),
+    // MDB: this call is okay bc we know 'sContext' was kExact
+    return sk_make_sp<SkImage_Gpu>(fContext, kNeedNewImageUniqueID,
+                                   fAlphaType, sContext->asTextureProxyRef(),
                                    fColorSpace, fBudgeted);
 }
 
@@ -237,7 +265,7 @@
     }
 
     const SkBudgeted budgeted = SkBudgeted::kNo;
-    return sk_make_sp<SkImage_Gpu>(desc.fWidth, desc.fHeight, kNeedNewImageUniqueID,
+    return sk_make_sp<SkImage_Gpu>(kNeedNewImageUniqueID,
                                    at, std::move(tex), std::move(colorSpace), budgeted);
 }
 
@@ -332,7 +360,7 @@
                                   sk_ref_sp(uProxy->asTextureProxy()),
                                   sk_ref_sp(vProxy->asTextureProxy()), yuvSizes, colorSpace, nv12));
 
-    const SkRect rect = SkRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height));
+    const SkRect rect = SkRect::MakeIWH(width, height);
 
     renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), rect);
 
@@ -340,8 +368,10 @@
         return nullptr;
     }
     ctx->flushSurfaceWrites(renderTargetContext->accessRenderTarget());
-    return sk_make_sp<SkImage_Gpu>(width, height, kNeedNewImageUniqueID,
-                                   kOpaque_SkAlphaType, renderTargetContext->asTexture(),
+
+    // MDB: this call is okay bc we know 'renderTargetContext' was exact
+    return sk_make_sp<SkImage_Gpu>(ctx, kNeedNewImageUniqueID,
+                                   kOpaque_SkAlphaType, renderTargetContext->asTextureProxyRef(),
                                    renderTargetContext->refColorSpace(), budgeted);
 }
 
@@ -370,7 +400,7 @@
     if (!texture) {
         return nullptr;
     }
-    return sk_make_sp<SkImage_Gpu>(texture->width(), texture->height(), id, at, std::move(texture),
+    return sk_make_sp<SkImage_Gpu>(id, at, std::move(texture),
                                    std::move(texColorSpace), SkBudgeted::kNo);
 }
 
@@ -477,7 +507,7 @@
     if (!texture) {
         return nullptr;
     }
-    return sk_make_sp<SkImage_Gpu>(texture->width(), texture->height(), kNeedNewImageUniqueID,
+    return sk_make_sp<SkImage_Gpu>(kNeedNewImageUniqueID,
                                    pixmap.alphaType(), std::move(texture),
                                    sk_ref_sp(pixmap.info().colorSpace()), budgeted);
 }
@@ -808,7 +838,7 @@
         return nullptr;
     }
     texture->texturePriv().setMipColorMode(colorMode);
-    return sk_make_sp<SkImage_Gpu>(texture->width(), texture->height(), kNeedNewImageUniqueID,
+    return sk_make_sp<SkImage_Gpu>(kNeedNewImageUniqueID,
                                    info.alphaType(), std::move(texture),
                                    sk_ref_sp(info.colorSpace()), budgeted);
 }
diff --git a/src/image/SkImage_Gpu.h b/src/image/SkImage_Gpu.h
index 0ad44d3..c70c18a 100644
--- a/src/image/SkImage_Gpu.h
+++ b/src/image/SkImage_Gpu.h
@@ -10,6 +10,7 @@
 
 #include "GrClip.h"
 #include "GrGpuResourcePriv.h"
+#include "GrSurfaceProxyPriv.h"
 #include "GrTexture.h"
 #include "SkAtomics.h"
 #include "SkBitmap.h"
@@ -20,12 +21,9 @@
 
 class SkImage_Gpu : public SkImage_Base {
 public:
-    /**
-     *  An "image" can be a subset/window into a larger texture, so we explicit take the
-     *  width and height.
-     */
-    SkImage_Gpu(int w, int h, uint32_t uniqueID, SkAlphaType, sk_sp<GrTexture>, sk_sp<SkColorSpace>,
-                SkBudgeted);
+    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;
 
     SkImageInfo onImageInfo() const override;
@@ -33,22 +31,29 @@
 
     void applyBudgetDecision() const {
         if (SkBudgeted::kYes == fBudgeted) {
-            fTexture->resourcePriv().makeBudgeted();
+            fProxy->priv().makeBudgeted();
         } else {
-            fTexture->resourcePriv().makeUnbudgeted();
+            fProxy->priv().makeUnbudgeted();
         }
     }
 
     bool getROPixels(SkBitmap*, SkColorSpace* dstColorSpace, CachingHint) const override;
-    GrTexture* asTextureRef(GrContext* ctx, const GrSamplerParams& params, SkColorSpace*,
+    GrTexture* asTextureRef(GrContext*, const GrSamplerParams&, SkColorSpace*,
                             sk_sp<SkColorSpace>*, SkScalar scaleAdjust[2]) const override;
     sk_sp<SkImage> onMakeSubset(const SkIRect&) const override;
 
-    GrTexture* peekTexture() const override { return fTexture.get(); }
-    sk_sp<GrTextureProxy> asTextureProxyRef() const override;
+    GrTexture* peekTexture() const override {
+        return fProxy->instantiate(fContext->resourceProvider());
+    }
+    sk_sp<GrTextureProxy> asTextureProxyRef() const override {
+        return fProxy;
+    }
+    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 fTexture;
+        return sk_ref_sp(this->peekTexture());
     }
 
     bool onReadYUV8Planes(const SkISize sizes[3], void* const planes[3],
@@ -57,11 +62,12 @@
     bool onReadPixels(const SkImageInfo&, void* dstPixels, size_t dstRowBytes,
                       int srcX, int srcY, CachingHint) const override;
 
-    GrContext* context() { return fTexture->getContext(); }
+    GrContext* context() { return fContext; }
     sk_sp<SkColorSpace> refColorSpace() { return fColorSpace; }
 
 private:
-    sk_sp<GrTexture>       fTexture;
+    GrContext*             fContext;
+    sk_sp<GrTextureProxy>  fProxy;
     const SkAlphaType      fAlphaType;
     const SkBudgeted       fBudgeted;
     sk_sp<SkColorSpace>    fColorSpace;
diff --git a/src/image/SkImage_Raster.cpp b/src/image/SkImage_Raster.cpp
index 61be7b7..220cc01 100644
--- a/src/image/SkImage_Raster.cpp
+++ b/src/image/SkImage_Raster.cpp
@@ -86,9 +86,16 @@
     bool onPeekPixels(SkPixmap*) const override;
     const SkBitmap* onPeekBitmap() const override { return &fBitmap; }
 
-    bool getROPixels(SkBitmap*, SkColorSpace* dstColorSpace, CachingHint) const override;
+#if SK_SUPPORT_GPU
+    sk_sp<GrTextureProxy> asTextureProxyRef(GrContext*, const GrSamplerParams&,
+                                            SkColorSpace*, sk_sp<SkColorSpace>*,
+                                            SkScalar scaleAdjust[2]) const override;
+#endif
+
     GrTexture* asTextureRef(GrContext*, const GrSamplerParams&, SkColorSpace*,
                             sk_sp<SkColorSpace>*, SkScalar scaleAdjust[2]) const override;
+
+    bool getROPixels(SkBitmap*, SkColorSpace* dstColorSpace, CachingHint) const override;
     sk_sp<SkImage> onMakeSubset(const SkIRect&) const override;
 
     SkPixelRef* getPixelRef() const { return fBitmap.pixelRef(); }
@@ -170,6 +177,18 @@
     return true;
 }
 
+#if SK_SUPPORT_GPU
+sk_sp<GrTextureProxy> SkImage_Raster::asTextureProxyRef(GrContext* context,
+                                                        const GrSamplerParams& params,
+                                                        SkColorSpace* dstColorSpace,
+                                                        sk_sp<SkColorSpace>* texColorSpace,
+                                                        SkScalar scaleAdjust[2]) const {
+    sk_sp<GrTexture> tex(this->asTextureRef(context, params, dstColorSpace, texColorSpace,
+                                            scaleAdjust));
+    return GrSurfaceProxy::MakeWrapped(std::move(tex));
+}
+#endif
+
 GrTexture* SkImage_Raster::asTextureRef(GrContext* ctx, const GrSamplerParams& params,
                                         SkColorSpace* dstColorSpace,
                                         sk_sp<SkColorSpace>* texColorSpace,
@@ -192,9 +211,9 @@
     }
 
     return GrRefCachedBitmapTexture(ctx, fBitmap, params, scaleAdjust);
-#endif
-
+#else
     return nullptr;
+#endif
 }
 
 #if SK_SUPPORT_GPU
diff --git a/src/image/SkSurface_Gpu.cpp b/src/image/SkSurface_Gpu.cpp
index d1f6d24..0bd34f8 100644
--- a/src/image/SkSurface_Gpu.cpp
+++ b/src/image/SkSurface_Gpu.cpp
@@ -120,7 +120,7 @@
     const SkImageInfo info = fDevice->imageInfo();
     sk_sp<SkImage> image;
     if (tex) {
-        image = sk_make_sp<SkImage_Gpu>(info.width(), info.height(), kNeedNewImageUniqueID,
+        image = sk_make_sp<SkImage_Gpu>(kNeedNewImageUniqueID,
                                         info.alphaType(), sk_ref_sp(tex),
                                         sk_ref_sp(info.colorSpace()), budgeted);
     }
diff --git a/tools/gpu/GrTest.cpp b/tools/gpu/GrTest.cpp
index fa0556d..15faffa 100644
--- a/tools/gpu/GrTest.cpp
+++ b/tools/gpu/GrTest.cpp
@@ -130,15 +130,9 @@
         return nullptr;
     }
 
-    GrTexture* tex = proxy->instantiate(this->resourceProvider());
-    if (!tex) {
-        return nullptr;
-    }
-
-    // MDB TODO: add proxy-backed SkImage_Gpu's
-    sk_sp<SkImage> image(new SkImage_Gpu(tex->width(), tex->height(),
-                                         kNeedNewImageUniqueID, kPremul_SkAlphaType,
-                                         sk_ref_sp(tex), nullptr, SkBudgeted::kNo));
+    SkASSERT(proxy->priv().isExact());
+    sk_sp<SkImage> image(new SkImage_Gpu(this, kNeedNewImageUniqueID, kPremul_SkAlphaType,
+                                         std::move(proxy), nullptr, SkBudgeted::kNo));
     return image;
 }