pin as texture api

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2241353002

Review-Url: https://codereview.chromium.org/2241353002
diff --git a/src/core/SkImagePriv.h b/src/core/SkImagePriv.h
index 3077f34..0377762 100644
--- a/src/core/SkImagePriv.h
+++ b/src/core/SkImagePriv.h
@@ -77,4 +77,29 @@
 
 GrTexture* GrDeepCopyTexture(GrTexture* src, SkBudgeted);
 
+/**
+ *  Will attempt to upload and lock the contents of the image as a texture, so that subsequent
+ *  draws to a gpu-target will come from that texture (and not by looking at the original image
+ *  src). In particular this is intended to use the texture even if the image's original content
+ *  changes subsequent to this call (i.e. the src is mutable!).
+ *
+ *  This must be balanced by an equal number of calls to SkImage_unpinAsTexture() -- calls can be
+ *  nested.
+ *
+ *  Once in this "pinned" state, the image has all of the same thread restrictions that exist
+ *  for a natively created gpu image (e.g. SkImage::MakeFromTexture)
+ *  - all drawing, pinning, unpinning must happen in the same thread as the GrContext.
+ */
+void SkImage_pinAsTexture(const SkImage*, GrContext*);
+
+/**
+ *  The balancing call to SkImage_pinAsTexture. When a balanced number of calls have been made, then
+ *  the "pinned" texture is free to be purged, etc. This also means that a subsequent "pin" call
+ *  will look at the original content again, and if its uniqueID/generationID has changed, then
+ *  a newer texture will be uploaded/pinned.
+ *
+ *  The context passed to unpin must match the one passed to pin.
+ */
+void SkImage_unpinAsTexture(const SkImage*, GrContext*);
+
 #endif
diff --git a/src/gpu/GrImageIDTextureAdjuster.cpp b/src/gpu/GrImageIDTextureAdjuster.cpp
index 2bfa21c..f9aaae1 100644
--- a/src/gpu/GrImageIDTextureAdjuster.cpp
+++ b/src/gpu/GrImageIDTextureAdjuster.cpp
@@ -17,17 +17,6 @@
 
 static bool bmp_is_alpha_only(const SkBitmap& bm) { return kAlpha_8_SkColorType == bm.colorType(); }
 
-// By construction this texture adjuster always represents an entire SkImage, so use the
-// image's dimensions for the key's rectangle.
-GrImageTextureAdjuster::GrImageTextureAdjuster(const SkImage_Base* img)
-    : GrTextureAdjuster(img->peekTexture(), SkIRect::MakeSize(img->dimensions()), img->uniqueID(),
-                        img->onImageInfo().colorSpace())
-{
-    SkASSERT(img->peekTexture());
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
 GrBitmapTextureMaker::GrBitmapTextureMaker(GrContext* context, const SkBitmap& bitmap)
     : INHERITED(context, bitmap.width(), bitmap.height(), bmp_is_alpha_only(bitmap))
     , fBitmap(bitmap)
diff --git a/src/gpu/GrImageIDTextureAdjuster.h b/src/gpu/GrImageIDTextureAdjuster.h
index 327bf8f..25b866b 100644
--- a/src/gpu/GrImageIDTextureAdjuster.h
+++ b/src/gpu/GrImageIDTextureAdjuster.h
@@ -15,11 +15,6 @@
 class SkImage_Base;
 class SkImageCacherator;
 
-class GrImageTextureAdjuster : public GrTextureAdjuster {
-public:
-    explicit GrImageTextureAdjuster(const SkImage_Base* img);
-};
-
 /** This class manages the conversion of SW-backed bitmaps to GrTextures. If the input bitmap is
     non-volatile the texture is cached using a key created from the pixels' image id and the
     subset of the pixelref specified by the bitmap. */
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index e9ab62d..44d4354 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -1315,9 +1315,11 @@
     ASSERT_SINGLE_OWNER
     SkMatrix viewMatrix = *draw.fMatrix;
     viewMatrix.preTranslate(x, y);
-    if (as_IB(image)->peekTexture()) {
+    uint32_t pinnedUniqueID;
+    if (sk_sp<GrTexture> tex = as_IB(image)->refPinnedTexture(&pinnedUniqueID)) {
         CHECK_SHOULD_DRAW(draw);
-        GrImageTextureAdjuster adjuster(as_IB(image));
+        GrTextureAdjuster adjuster(tex.get(), image->bounds(), pinnedUniqueID,
+                                   as_IB(image)->onImageInfo().colorSpace());
         this->drawTextureProducer(&adjuster, nullptr, nullptr, SkCanvas::kFast_SrcRectConstraint,
                                   viewMatrix, fClip, paint);
         return;
@@ -1345,9 +1347,11 @@
                                 const SkRect& dst, const SkPaint& paint,
                                 SkCanvas::SrcRectConstraint constraint) {
     ASSERT_SINGLE_OWNER
-    if (as_IB(image)->peekTexture()) {
+    uint32_t pinnedUniqueID;
+    if (sk_sp<GrTexture> tex = as_IB(image)->refPinnedTexture(&pinnedUniqueID)) {
         CHECK_SHOULD_DRAW(draw);
-        GrImageTextureAdjuster adjuster(as_IB(image));
+        GrTextureAdjuster adjuster(tex.get(), image->bounds(), pinnedUniqueID,
+                                   as_IB(image)->onImageInfo().colorSpace());
         this->drawTextureProducer(&adjuster, src, &dst, constraint, *draw.fMatrix, fClip, paint);
         return;
     }
@@ -1414,8 +1418,11 @@
 void SkGpuDevice::drawImageNine(const SkDraw& draw, const SkImage* image,
                                 const SkIRect& center, const SkRect& dst, const SkPaint& paint) {
     ASSERT_SINGLE_OWNER
-    if (as_IB(image)->peekTexture()) {
-        GrImageTextureAdjuster adjuster(as_IB(image));
+    uint32_t pinnedUniqueID;
+    if (sk_sp<GrTexture> tex = as_IB(image)->refPinnedTexture(&pinnedUniqueID)) {
+        CHECK_SHOULD_DRAW(draw);
+        GrTextureAdjuster adjuster(tex.get(), image->bounds(), pinnedUniqueID,
+                                   as_IB(image)->onImageInfo().colorSpace());
         this->drawProducerNine(draw, &adjuster, center, dst, paint);
     } else {
         SkBitmap bm;
diff --git a/src/image/SkImage.cpp b/src/image/SkImage.cpp
index 9febeaa..b8c8964 100644
--- a/src/image/SkImage.cpp
+++ b/src/image/SkImage.cpp
@@ -511,3 +511,17 @@
                                                    const SkIRect* subset) {
     return SkImage::MakeFromEncoded(SkData::MakeWithCopy(data, length), subset);
 }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+void SkImage_pinAsTexture(const SkImage* image, GrContext* ctx) {
+    SkASSERT(image);
+    SkASSERT(ctx);
+    as_IB(image)->onPinAsTexture(ctx);
+}
+
+void SkImage_unpinAsTexture(const SkImage* image, GrContext* ctx) {
+    SkASSERT(image);
+    SkASSERT(ctx);
+    as_IB(image)->onUnpinAsTexture(ctx);
+}
diff --git a/src/image/SkImage_Base.h b/src/image/SkImage_Base.h
index 1cfb7da..09971c1 100644
--- a/src/image/SkImage_Base.h
+++ b/src/image/SkImage_Base.h
@@ -8,6 +8,7 @@
 #ifndef SkImage_Base_DEFINED
 #define SkImage_Base_DEFINED
 
+#include "GrTexture.h"
 #include "SkAtomics.h"
 #include "SkImage.h"
 #include "SkSurface.h"
@@ -40,6 +41,7 @@
                               int srcX, int srcY, CachingHint) const;
 
     virtual GrTexture* peekTexture() const { return nullptr; }
+    virtual sk_sp<GrTexture> refPinnedTexture(uint32_t* uniqueID) const { return nullptr; }
     virtual SkImageCacherator* peekCacherator() const { return nullptr; }
 
     // return a read-only copy of the pixels. We promise to not modify them,
@@ -69,6 +71,9 @@
         fAddedToCache.store(true);
     }
 
+    virtual void onPinAsTexture(GrContext*) const {}
+    virtual void onUnpinAsTexture(GrContext*) const {}
+
 private:
     // Set true by caches when they cache content that's derived from the current pixels.
     mutable SkAtomic<bool> fAddedToCache;
diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp
index 44bb71c..76f19bc 100644
--- a/src/image/SkImage_Gpu.cpp
+++ b/src/image/SkImage_Gpu.cpp
@@ -83,8 +83,9 @@
 
 GrTexture* SkImage_Gpu::asTextureRef(GrContext* ctx, const GrTextureParams& params,
                                      SkSourceGammaTreatment gammaTreatment) const {
-    return GrImageTextureAdjuster(as_IB(this)).refTextureSafeForParams(params, gammaTreatment,
-                                                                       nullptr);
+    GrTextureAdjuster adjuster(this->peekTexture(), this->bounds(), this->uniqueID(),
+                               this->onImageInfo().colorSpace());
+    return adjuster.refTextureSafeForParams(params, gammaTreatment, nullptr);
 }
 
 bool SkImage_Gpu::isOpaque() const {
diff --git a/src/image/SkImage_Gpu.h b/src/image/SkImage_Gpu.h
index 5faaa75..44949a1 100644
--- a/src/image/SkImage_Gpu.h
+++ b/src/image/SkImage_Gpu.h
@@ -43,6 +43,10 @@
     sk_sp<SkImage> onMakeSubset(const SkIRect&) const override;
 
     GrTexture* peekTexture() const override { return fTexture; }
+    sk_sp<GrTexture> refPinnedTexture(uint32_t* uniqueID) const override {
+        *uniqueID = this->uniqueID();
+        return sk_ref_sp(fTexture.get());
+    }
     bool isOpaque() const override;
     bool onReadPixels(const SkImageInfo&, void* dstPixels, size_t dstRowBytes,
                       int srcX, int srcY, CachingHint) const override;
diff --git a/src/image/SkImage_Raster.cpp b/src/image/SkImage_Raster.cpp
index 67d521e..abdecb9 100644
--- a/src/image/SkImage_Raster.cpp
+++ b/src/image/SkImage_Raster.cpp
@@ -115,9 +115,21 @@
         return fBitmap.pixelRef() && fBitmap.pixelRef()->isLazyGenerated();
     }
 
+#if SK_SUPPORT_GPU
+    sk_sp<GrTexture> refPinnedTexture(uint32_t* uniqueID) const override;
+    void onPinAsTexture(GrContext*) const override;
+    void onUnpinAsTexture(GrContext*) const override;
+#endif
+
 private:
     SkBitmap fBitmap;
 
+#if SK_SUPPORT_GPU
+    mutable sk_sp<GrTexture> fPinnedTexture;
+    mutable int32_t fPinnedCount = 0;
+    mutable uint32_t fPinnedUniqueID = 0;
+#endif
+
     typedef SkImage_Base INHERITED;
 };
 
@@ -149,7 +161,11 @@
     SkASSERT(fBitmap.isImmutable());
 }
 
-SkImage_Raster::~SkImage_Raster() {}
+SkImage_Raster::~SkImage_Raster() {
+#if SK_SUPPORT_GPU
+    SkASSERT(nullptr == fPinnedTexture.get());  // want the caller to have manually unpinned
+#endif
+}
 
 bool SkImage_Raster::onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes,
                                   int srcX, int srcY, CachingHint) const {
@@ -178,6 +194,8 @@
     return true;
 }
 
+#include "GrImageIDTextureAdjuster.h"
+
 GrTexture* SkImage_Raster::asTextureRef(GrContext* ctx, const GrTextureParams& params,
                                         SkSourceGammaTreatment gammaTreatment) const {
 #if SK_SUPPORT_GPU
@@ -185,12 +203,64 @@
         return nullptr;
     }
 
+    uint32_t uniqueID;
+    sk_sp<GrTexture> tex = this->refPinnedTexture(&uniqueID);
+    if (tex) {
+        GrTextureAdjuster adjuster(fPinnedTexture.get(), fBitmap.bounds(),
+                                   fPinnedUniqueID, fBitmap.colorSpace());
+        return adjuster.refTextureSafeForParams(params, gammaTreatment, nullptr);
+    }
+
     return GrRefCachedBitmapTexture(ctx, fBitmap, params, gammaTreatment);
 #endif
 
     return nullptr;
 }
 
+#if SK_SUPPORT_GPU
+
+sk_sp<GrTexture> SkImage_Raster::refPinnedTexture(uint32_t* uniqueID) const {
+    if (fPinnedTexture) {
+        SkASSERT(fPinnedCount > 0);
+        SkASSERT(fPinnedUniqueID != 0);
+        *uniqueID = fPinnedUniqueID;
+        return fPinnedTexture;
+    }
+    return nullptr;
+}
+
+void SkImage_Raster::onPinAsTexture(GrContext* ctx) const {
+    if (fPinnedTexture) {
+        SkASSERT(fPinnedCount > 0);
+        SkASSERT(fPinnedUniqueID != 0);
+        SkASSERT(fPinnedTexture->getContext() == ctx);
+    } else {
+        SkASSERT(fPinnedCount == 0);
+        SkASSERT(fPinnedUniqueID == 0);
+        fPinnedTexture.reset(GrRefCachedBitmapTexture(ctx, fBitmap,
+                                                      GrTextureParams::ClampNoFilter(),
+                                                      SkSourceGammaTreatment::kRespect));
+        fPinnedUniqueID = fBitmap.getGenerationID();
+    }
+    // Note: we always increment, even if we failed to create the pinned texture
+    ++fPinnedCount;
+}
+
+void SkImage_Raster::onUnpinAsTexture(GrContext* ctx) const {
+    // Note: we always decrement, even if fPinnedTexture is null
+    SkASSERT(fPinnedCount > 0);
+    SkASSERT(fPinnedUniqueID != 0);
+    if (fPinnedTexture) {
+        SkASSERT(fPinnedTexture->getContext() == ctx);
+    }
+
+    if (0 == --fPinnedCount) {
+        fPinnedTexture.reset(nullptr);
+        fPinnedUniqueID = 0;
+    }
+}
+#endif
+
 sk_sp<SkImage> SkImage_Raster::onMakeSubset(const SkIRect& subset) const {
     // TODO : could consider heurist of sharing pixels, if subset is pretty close to complete