Simplify promise image lazy instantiation callbacks.

Now that we never re-fulfill a promise image we no longer need to deinstantiate
promise image proxies. They now can use kSingleUse callback semantics.

This was the only usage of the kDeinstantiate lazy callback type so it is
removed. The DeinstantiateProxyTracker is also no longer required and is
removed.

The GrTexture idle callback mechanism now uses GrReleaseProcHelper, which has
been extended to support chaining multiple callbacks together and an abandon()
method that aborts calling the callback in the destructor. It has been renamed
GrRefCntedCallback to reflect its more general usage.

Bug: skia:8800
Change-Id: I857c9eec57fdf706631a266ec8bea682d6657a7c
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/196500
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/src/gpu/GrAHardwareBufferImageGenerator.cpp b/src/gpu/GrAHardwareBufferImageGenerator.cpp
index 9220a19..2055955 100644
--- a/src/gpu/GrAHardwareBufferImageGenerator.cpp
+++ b/src/gpu/GrAHardwareBufferImageGenerator.cpp
@@ -179,9 +179,7 @@
                 }
 
                 if (deleteImageProc) {
-                    sk_sp<GrReleaseProcHelper> releaseProcHelper(
-                            new GrReleaseProcHelper(deleteImageProc, deleteImageCtx));
-                    tex->setRelease(releaseProcHelper);
+                    tex->setRelease(deleteImageProc, deleteImageCtx);
                 }
 
                 return tex;
diff --git a/src/gpu/GrBackendTextureImageGenerator.cpp b/src/gpu/GrBackendTextureImageGenerator.cpp
index 0c41cc1..e184e8e 100644
--- a/src/gpu/GrBackendTextureImageGenerator.cpp
+++ b/src/gpu/GrBackendTextureImageGenerator.cpp
@@ -106,7 +106,7 @@
     auto proxyProvider = context->priv().proxyProvider();
 
     fBorrowingMutex.acquire();
-    sk_sp<GrReleaseProcHelper> releaseProcHelper;
+    sk_sp<GrRefCntedCallback> releaseProcHelper;
     if (SK_InvalidGenID != fRefHelper->fBorrowingContextID) {
         if (fRefHelper->fBorrowingContextID != context->priv().contextID()) {
             fBorrowingMutex.release();
@@ -119,10 +119,10 @@
     } else {
         SkASSERT(!fRefHelper->fBorrowingContextReleaseProc);
         // The ref we add to fRefHelper here will be passed into and owned by the
-        // GrReleaseProcHelper.
+        // GrRefCntedCallback.
         fRefHelper->ref();
-        releaseProcHelper.reset(new GrReleaseProcHelper(ReleaseRefHelper_TextureReleaseProc,
-                                                        fRefHelper));
+        releaseProcHelper.reset(
+                new GrRefCntedCallback(ReleaseRefHelper_TextureReleaseProc, fRefHelper));
         fRefHelper->fBorrowingContextReleaseProc = releaseProcHelper.get();
     }
     fRefHelper->fBorrowingContextID = context->priv().contextID();
diff --git a/src/gpu/GrBackendTextureImageGenerator.h b/src/gpu/GrBackendTextureImageGenerator.h
index 74dd3ea..d461176 100644
--- a/src/gpu/GrBackendTextureImageGenerator.h
+++ b/src/gpu/GrBackendTextureImageGenerator.h
@@ -73,7 +73,7 @@
         // texture are finished on the borrowing context before we open this back up to other
         // contexts. In general a ref to this release proc is owned by all proxies and gpu uses of
         // the backend texture.
-        GrReleaseProcHelper* fBorrowingContextReleaseProc;
+        GrRefCntedCallback*  fBorrowingContextReleaseProc;
         uint32_t             fBorrowingContextID;
     };
 
diff --git a/src/gpu/GrDeinstantiateProxyTracker.cpp b/src/gpu/GrDeinstantiateProxyTracker.cpp
deleted file mode 100644
index 9870617..0000000
--- a/src/gpu/GrDeinstantiateProxyTracker.cpp
+++ /dev/null
@@ -1,32 +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 "GrDeinstantiateProxyTracker.h"
-
-#include "GrSurfaceProxy.h"
-#include "GrSurfaceProxyPriv.h"
-
-void GrDeinstantiateProxyTracker::addProxy(GrSurfaceProxy* proxy) {
-#ifdef SK_DEBUG
-    using LazyType = GrSurfaceProxy::LazyInstantiationType;
-    SkASSERT(LazyType::kDeinstantiate == proxy->priv().lazyInstantiationType());
-    for (int i = 0; i < fProxies.count(); ++i) {
-        SkASSERT(proxy != fProxies[i].get());
-    }
-#endif
-    fProxies.push_back(sk_ref_sp(proxy));
-}
-
-void GrDeinstantiateProxyTracker::deinstantiateAllProxies() {
-    for (int i = 0; i < fProxies.count(); ++i) {
-        GrSurfaceProxy* proxy = fProxies[i].get();
-        SkASSERT(proxy->priv().isSafeToDeinstantiate());
-        proxy->deinstantiate();
-    }
-
-    fProxies.reset();
-}
diff --git a/src/gpu/GrDeinstantiateProxyTracker.h b/src/gpu/GrDeinstantiateProxyTracker.h
deleted file mode 100644
index 2555ab1..0000000
--- a/src/gpu/GrDeinstantiateProxyTracker.h
+++ /dev/null
@@ -1,29 +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 GrDeinstantiateProxyTracker_DEFINED
-#define GrDeinstantiateProxyTracker_DEFINED
-
-#include "GrSurfaceProxy.h"
-#include "SkTArray.h"
-
-class GrDeinstantiateProxyTracker {
-public:
-    GrDeinstantiateProxyTracker() {}
-
-    // Adds a proxy which will be deinstantiated at the end of flush. The same proxy may not be
-    // added multiple times.
-    void addProxy(GrSurfaceProxy* proxy);
-
-    // Loops through all tracked proxies and deinstantiates them.
-    void deinstantiateAllProxies();
-
-private:
-    SkTArray<sk_sp<GrSurfaceProxy>> fProxies;
-};
-
-#endif
diff --git a/src/gpu/GrDrawingManager.cpp b/src/gpu/GrDrawingManager.cpp
index aa22cef..ae49d0a 100644
--- a/src/gpu/GrDrawingManager.cpp
+++ b/src/gpu/GrDrawingManager.cpp
@@ -292,7 +292,7 @@
     bool flushed = false;
 
     {
-        GrResourceAllocator alloc(resourceProvider, flushState.deinstantiateProxyTracker());
+        GrResourceAllocator alloc(resourceProvider);
         for (int i = 0; i < fDAG.numOpLists(); ++i) {
             if (fDAG.opList(i)) {
                 fDAG.opList(i)->gatherProxyIntervals(&alloc);
@@ -343,8 +343,6 @@
     GrSemaphoresSubmitted result = gpu->finishFlush(proxy, access, flags, numSemaphores,
                                                     backendSemaphores);
 
-    flushState.deinstantiateProxyTracker()->deinstantiateAllProxies();
-
     // Give the cache a chance to purge resources that become purgeable due to flushing.
     if (flushed) {
         resourceCache->purgeAsNeeded();
diff --git a/src/gpu/GrOpFlushState.h b/src/gpu/GrOpFlushState.h
index 07983d4..bc10920 100644
--- a/src/gpu/GrOpFlushState.h
+++ b/src/gpu/GrOpFlushState.h
@@ -12,7 +12,6 @@
 #include "GrAppliedClip.h"
 #include "GrBufferAllocPool.h"
 #include "GrDeferredUpload.h"
-#include "GrDeinstantiateProxyTracker.h"
 #include "GrRenderTargetProxy.h"
 #include "SkArenaAlloc.h"
 #include "SkArenaAllocList.h"
@@ -109,8 +108,6 @@
     // permissible).
     GrAtlasManager* atlasManager() const final;
 
-    GrDeinstantiateProxyTracker* deinstantiateProxyTracker() { return &fDeinstantiateProxyTracker; }
-
 private:
     /** GrMeshDrawOp::Target override. */
     SkArenaAlloc* allocator() override { return &fArena; }
@@ -164,9 +161,6 @@
     // Variables that are used to track where we are in lists as ops are executed
     SkArenaAllocList<Draw>::Iter fCurrDraw;
     SkArenaAllocList<InlineUpload>::Iter fCurrUpload;
-
-    // Used to track the proxies that need to be deinstantiated after we finish a flush
-    GrDeinstantiateProxyTracker fDeinstantiateProxyTracker;
 };
 
 #endif
diff --git a/src/gpu/GrProxyProvider.cpp b/src/gpu/GrProxyProvider.cpp
index 64b3e95..2540cbb 100644
--- a/src/gpu/GrProxyProvider.cpp
+++ b/src/gpu/GrProxyProvider.cpp
@@ -488,11 +488,8 @@
         return nullptr;
     }
 
-    sk_sp<GrReleaseProcHelper> releaseHelper;
     if (releaseProc) {
-        releaseHelper.reset(new GrReleaseProcHelper(releaseProc, releaseCtx));
-        // This gives the texture a ref on the releaseHelper
-        tex->setRelease(std::move(releaseHelper));
+        tex->setRelease(releaseProc, releaseCtx);
     }
 
     SkASSERT(!tex->asRenderTarget());  // Strictly a GrTexture
@@ -529,11 +526,8 @@
         return nullptr;
     }
 
-    sk_sp<GrReleaseProcHelper> releaseHelper;
     if (releaseProc) {
-        releaseHelper.reset(new GrReleaseProcHelper(releaseProc, releaseCtx));
-        // This gives the texture a ref on the releaseHelper
-        tex->setRelease(std::move(releaseHelper));
+        tex->setRelease(releaseProc, releaseCtx);
     }
 
     SkASSERT(tex->asRenderTarget());  // A GrTextureRenderTarget
@@ -563,9 +557,9 @@
         return nullptr;
     }
 
-    sk_sp<GrReleaseProcHelper> releaseHelper;
+    sk_sp<GrRefCntedCallback> releaseHelper;
     if (releaseProc) {
-        releaseHelper.reset(new GrReleaseProcHelper(releaseProc, releaseCtx));
+        releaseHelper.reset(new GrRefCntedCallback(releaseProc, releaseCtx));
         // This gives the render target a ref on the releaseHelper
         rt->setRelease(std::move(releaseHelper));
     }
diff --git a/src/gpu/GrResourceAllocator.cpp b/src/gpu/GrResourceAllocator.cpp
index 0d3c34e..81c2709 100644
--- a/src/gpu/GrResourceAllocator.cpp
+++ b/src/gpu/GrResourceAllocator.cpp
@@ -7,7 +7,6 @@
 
 #include "GrResourceAllocator.h"
 
-#include "GrDeinstantiateProxyTracker.h"
 #include "GrGpuResourcePriv.h"
 #include "GrOpList.h"
 #include "GrRenderTargetProxy.h"
@@ -107,12 +106,7 @@
     if (proxy->readOnly() || !fResourceProvider->explicitlyAllocateGPUResources()) {
         // FIXME: remove this once we can do the lazy instantiation from assign instead.
         if (GrSurfaceProxy::LazyState::kNot != proxy->lazyInstantiationState()) {
-            if (proxy->priv().doLazyInstantiation(fResourceProvider)) {
-                if (proxy->priv().lazyInstantiationType() ==
-                    GrSurfaceProxy::LazyInstantiationType::kDeinstantiate) {
-                    fDeinstantiateTracker->addProxy(proxy);
-                }
-            }
+            proxy->priv().doLazyInstantiation(fResourceProvider);
         }
     }
 }
@@ -375,11 +369,6 @@
         if (GrSurfaceProxy::LazyState::kNot != cur->proxy()->lazyInstantiationState()) {
             if (!cur->proxy()->priv().doLazyInstantiation(fResourceProvider)) {
                 *outError = AssignError::kFailedProxyInstantiation;
-            } else {
-                if (GrSurfaceProxy::LazyInstantiationType::kDeinstantiate ==
-                    cur->proxy()->priv().lazyInstantiationType()) {
-                    fDeinstantiateTracker->addProxy(cur->proxy());
-                }
             }
         } else if (sk_sp<GrSurface> surface = this->findSurfaceFor(cur->proxy(), needsStencil)) {
             // TODO: make getUniqueKey virtual on GrSurfaceProxy
diff --git a/src/gpu/GrResourceAllocator.h b/src/gpu/GrResourceAllocator.h
index ea1250f..cb366e6 100644
--- a/src/gpu/GrResourceAllocator.h
+++ b/src/gpu/GrResourceAllocator.h
@@ -16,7 +16,6 @@
 #include "SkTDynamicHash.h"
 #include "SkTMultiMap.h"
 
-class GrDeinstantiateProxyTracker;
 class GrResourceProvider;
 
 // Print out explicit allocation information
@@ -42,8 +41,8 @@
  */
 class GrResourceAllocator {
 public:
-    GrResourceAllocator(GrResourceProvider* resourceProvider, GrDeinstantiateProxyTracker* tracker)
-            : fResourceProvider(resourceProvider), fDeinstantiateTracker(tracker) {}
+    GrResourceAllocator(GrResourceProvider* resourceProvider)
+            : fResourceProvider(resourceProvider) {}
 
     ~GrResourceAllocator();
 
@@ -212,7 +211,6 @@
     static const int kInitialArenaSize = 128 * sizeof(Interval);
 
     GrResourceProvider*          fResourceProvider;
-    GrDeinstantiateProxyTracker* fDeinstantiateTracker;
     FreePoolMultiMap             fFreePool;          // Recently created/used GrSurfaces
     IntvlHash                    fIntvlHash;         // All the intervals, hashed by proxyID
 
diff --git a/src/gpu/GrSurfaceProxyPriv.h b/src/gpu/GrSurfaceProxyPriv.h
index a9076b8..8fe7f83 100644
--- a/src/gpu/GrSurfaceProxyPriv.h
+++ b/src/gpu/GrSurfaceProxyPriv.h
@@ -60,12 +60,6 @@
         return fProxy->fLazyInstantiationType;
     }
 
-    bool isSafeToDeinstantiate() const {
-        return SkToBool(fProxy->fTarget) &&
-               SkToBool(fProxy->fLazyInstantiateCallback) &&
-               GrSurfaceProxy::LazyInstantiationType::kDeinstantiate == lazyInstantiationType();
-    }
-
     static bool SK_WARN_UNUSED_RESULT AttachStencilIfNeeded(GrResourceProvider*, GrSurface*,
                                                             bool needsStencil);
 
diff --git a/src/gpu/gl/GrGLRenderTarget.h b/src/gpu/gl/GrGLRenderTarget.h
index caedf08..1e7c477 100644
--- a/src/gpu/gl/GrGLRenderTarget.h
+++ b/src/gpu/gl/GrGLRenderTarget.h
@@ -95,7 +95,7 @@
 
     size_t onGpuMemorySize() const override;
 
-    void onSetRelease(sk_sp<GrReleaseProcHelper> releaseHelper) override {}
+    void onSetRelease(sk_sp<GrRefCntedCallback> releaseHelper) override {}
 
     int msaaSamples() const;
     // The number total number of samples, including both MSAA and resolve texture samples.
diff --git a/src/gpu/gl/GrGLTexture.h b/src/gpu/gl/GrGLTexture.h
index 6c22b4d..423a763 100644
--- a/src/gpu/gl/GrGLTexture.h
+++ b/src/gpu/gl/GrGLTexture.h
@@ -75,12 +75,6 @@
         fNonSamplerParams.invalidate();
     }
 
-    void setIdleProc(IdleProc proc, void* context) override {
-        fIdleProc = proc;
-        fIdleProcContext = context;
-    }
-    void* idleContext() const override { return fIdleProcContext; }
-
     // These functions are used to track the texture parameters associated with the texture.
     GrGpu::ResetTimestamp getCachedParamsTimestamp() const { return fParamsTimestamp; }
     const SamplerParams& getCachedSamplerParams() const { return fSamplerParams; }
@@ -124,21 +118,11 @@
     bool onStealBackendTexture(GrBackendTexture*, SkImage::BackendTextureReleaseProc*) override;
 
 private:
-    void onSetRelease(sk_sp<GrReleaseProcHelper> releaseHelper) override {}
-
-    void willRemoveLastRefOrPendingIO() override {
-        if (fIdleProc) {
-            fIdleProc(fIdleProcContext);
-            fIdleProc = nullptr;
-            fIdleProcContext = nullptr;
-        }
-    }
+    void onSetRelease(sk_sp<GrRefCntedCallback> releaseHelper) override {}
 
     SamplerParams fSamplerParams;
     NonSamplerParams fNonSamplerParams;
     GrGpu::ResetTimestamp fParamsTimestamp;
-    IdleProc* fIdleProc = nullptr;
-    void* fIdleProcContext = nullptr;
     GrGLuint fID;
     GrGLenum fFormat;
     GrBackendObjectOwnership fTextureIDOwnership;
diff --git a/src/gpu/gl/GrGLTextureRenderTarget.h b/src/gpu/gl/GrGLTextureRenderTarget.h
index 12a8ea0..a3b1841 100644
--- a/src/gpu/gl/GrGLTextureRenderTarget.h
+++ b/src/gpu/gl/GrGLTextureRenderTarget.h
@@ -67,7 +67,7 @@
 
     size_t onGpuMemorySize() const override;
 
-    void onSetRelease(sk_sp<GrReleaseProcHelper> releaseHelper) override {}
+    void onSetRelease(sk_sp<GrRefCntedCallback> releaseHelper) override {}
 };
 
 #ifdef SK_BUILD_FOR_WIN
diff --git a/src/gpu/mock/GrMockTexture.h b/src/gpu/mock/GrMockTexture.h
index f1d977c..7beb66c 100644
--- a/src/gpu/mock/GrMockTexture.h
+++ b/src/gpu/mock/GrMockTexture.h
@@ -44,12 +44,6 @@
 
     void textureParamsModified() override {}
 
-    void setIdleProc(IdleProc proc, void* context) override {
-        fIdleProc = proc;
-        fIdleProcContext = context;
-    }
-    void* idleContext() const override { return fIdleProcContext; }
-
 protected:
     // constructor for subclasses
     GrMockTexture(GrMockGpu* gpu, const GrSurfaceDesc& desc, GrMipMapsStatus mipMapsStatus,
@@ -70,23 +64,10 @@
         return false;
     }
 
-    // protected so that GrMockTextureRenderTarget can call this to avoid "inheritance via
-    // dominance" warning.
-    void willRemoveLastRefOrPendingIO() override {
-        if (fIdleProc) {
-            fIdleProc(fIdleProcContext);
-            fIdleProc = nullptr;
-            fIdleProcContext = nullptr;
-        }
-    }
-
 private:
-    void onSetRelease(sk_sp<GrReleaseProcHelper> releaseHelper) override {}
+    void onSetRelease(sk_sp<GrRefCntedCallback> releaseHelper) override {}
 
     GrMockTextureInfo fInfo;
-    sk_sp<GrReleaseProcHelper> fReleaseHelper;
-    IdleProc* fIdleProc = nullptr;
-    void* fIdleProcContext = nullptr;
 
     typedef GrTexture INHERITED;
 };
@@ -139,7 +120,7 @@
             : GrSurface(gpu, desc), INHERITED(gpu, desc), fInfo(info) {}
 
 private:
-    void onSetRelease(sk_sp<GrReleaseProcHelper> releaseHelper) override {}
+    void onSetRelease(sk_sp<GrRefCntedCallback> releaseHelper) override {}
 
     GrMockRenderTargetInfo fInfo;
 
@@ -177,8 +158,12 @@
         return GrMockTexture::backendFormat();
     }
 
+protected:
+    // This avoids an inherits via dominance warning on MSVC.
+    void willRemoveLastRefOrPendingIO() override { GrTexture::willRemoveLastRefOrPendingIO(); }
+
 private:
-    void onSetRelease(sk_sp<GrReleaseProcHelper> releaseHelper) override {}
+    void onSetRelease(sk_sp<GrRefCntedCallback> releaseHelper) override {}
 
     void onAbandon() override {
         GrRenderTarget::onAbandon();
@@ -190,9 +175,6 @@
         GrMockTexture::onRelease();
     }
 
-    // We implement this to avoid the inheritance via dominance warning.
-    void willRemoveLastRefOrPendingIO() override { GrMockTexture::willRemoveLastRefOrPendingIO(); }
-
     size_t onGpuMemorySize() const override {
         int numColorSamples = this->numColorSamples();
         if (numColorSamples > 1) {
diff --git a/src/gpu/mtl/GrMtlRenderTarget.h b/src/gpu/mtl/GrMtlRenderTarget.h
index c6a13aa..282a443 100644
--- a/src/gpu/mtl/GrMtlRenderTarget.h
+++ b/src/gpu/mtl/GrMtlRenderTarget.h
@@ -81,7 +81,7 @@
 
     bool completeStencilAttachment() override;
 
-    void onSetRelease(sk_sp<GrReleaseProcHelper> releaseHelper) override {}
+    void onSetRelease(sk_sp<GrRefCntedCallback> releaseHelper) override {}
 
     typedef GrRenderTarget INHERITED;
 };
diff --git a/src/gpu/mtl/GrMtlTexture.h b/src/gpu/mtl/GrMtlTexture.h
index ae283bd..9134e7d 100644
--- a/src/gpu/mtl/GrMtlTexture.h
+++ b/src/gpu/mtl/GrMtlTexture.h
@@ -36,12 +36,6 @@
 
     bool reallocForMipmap(GrMtlGpu* gpu, uint32_t mipLevels);
 
-    void setIdleProc(IdleProc proc, void* context) override {
-        fIdleProc = proc;
-        fIdleProcContext = context;
-    }
-    void* idleContext() const override { return fIdleProcContext; }
-
 protected:
     GrMtlTexture(GrMtlGpu*, const GrSurfaceDesc&, id<MTLTexture>, GrMipMapsStatus);
 
@@ -66,15 +60,7 @@
     // Since all MTLResources are inherently ref counted, we can call the Release proc when we
     // delete the GrMtlTexture without worry of the MTLTexture getting deleted before it is done on
     // the GPU. Thus we do nothing special here with the releaseHelper.
-    void onSetRelease(sk_sp<GrReleaseProcHelper> releaseHelper) override {}
-
-    void willRemoveLastRefOrPendingIO() override {
-        if (fIdleProc) {
-            fIdleProc(fIdleProcContext);
-            fIdleProc = nullptr;
-            fIdleProcContext = nullptr;
-        }
-    }
+    void onSetRelease(sk_sp<GrRefCntedCallback> releaseHelper) override {}
 
     GrMtlTexture(GrMtlGpu*, SkBudgeted, const GrSurfaceDesc&, id<MTLTexture>,
                  GrMipMapsStatus);
@@ -83,9 +69,6 @@
                  GrWrapCacheable, GrIOType);
 
     id<MTLTexture> fTexture;
-    sk_sp<GrReleaseProcHelper> fReleaseHelper;
-    IdleProc* fIdleProc = nullptr;
-    void* fIdleProcContext = nullptr;
 
     typedef GrTexture INHERITED;
 };
diff --git a/src/gpu/mtl/GrMtlTextureRenderTarget.h b/src/gpu/mtl/GrMtlTextureRenderTarget.h
index b15d289..f622330 100644
--- a/src/gpu/mtl/GrMtlTextureRenderTarget.h
+++ b/src/gpu/mtl/GrMtlTextureRenderTarget.h
@@ -76,7 +76,7 @@
                                       numColorSamples, GrMipMapped::kNo, false);
     }
 
-    void onSetRelease(sk_sp<GrReleaseProcHelper> releaseHelper) override {}
+    void onSetRelease(sk_sp<GrRefCntedCallback> releaseHelper) override {}
 };
 
 #endif
diff --git a/src/gpu/vk/GrVkImage.cpp b/src/gpu/vk/GrVkImage.cpp
index 99140f6..524c33e 100644
--- a/src/gpu/vk/GrVkImage.cpp
+++ b/src/gpu/vk/GrVkImage.cpp
@@ -257,7 +257,7 @@
     }
 }
 
-void GrVkImage::setResourceRelease(sk_sp<GrReleaseProcHelper> releaseHelper) {
+void GrVkImage::setResourceRelease(sk_sp<GrRefCntedCallback> releaseHelper) {
     SkASSERT(fResource);
     // Forward the release proc on to GrVkImage::Resource
     fResource->setRelease(std::move(releaseHelper));
@@ -270,11 +270,10 @@
     GrVkMemory::FreeImageMemory(gpu, isLinear, fAlloc);
 }
 
-void GrVkImage::Resource::setIdleProc(GrVkTexture* owner, GrTexture::IdleProc proc,
-                                      void* context) const {
+void GrVkImage::Resource::replaceIdleProc(
+        GrVkTexture* owner, sk_sp<GrRefCntedCallback> idleCallback) const {
     fOwningTexture = owner;
-    fIdleProc = proc;
-    fIdleProcContext = context;
+    fIdleCallback = std::move(idleCallback);
 }
 
 void GrVkImage::Resource::removeOwningTexture() const { fOwningTexture = nullptr; }
@@ -283,22 +282,16 @@
 
 void GrVkImage::Resource::notifyRemovedFromCommandBuffer() const {
     SkASSERT(fNumCommandBufferOwners);
-    if (--fNumCommandBufferOwners || !fIdleProc) {
+    if (--fNumCommandBufferOwners || !fIdleCallback) {
         return;
     }
-    if (fOwningTexture && fOwningTexture->resourcePriv().hasRefOrPendingIO()) {
-        return;
-    }
-    fIdleProc(fIdleProcContext);
     if (fOwningTexture) {
-        fOwningTexture->setIdleProc(nullptr, nullptr);
-        // Changing the texture's proc should change ours.
-        SkASSERT(!fIdleProc);
-        SkASSERT(!fIdleProc);
-    } else {
-        fIdleProc = nullptr;
-        fIdleProcContext = nullptr;
+        if (fOwningTexture->resourcePriv().hasRefOrPendingIO()) {
+            return;
+        }
+        fOwningTexture->removeIdleProc();
     }
+    fIdleCallback.reset();
 }
 
 void GrVkImage::BorrowedResource::freeGPUData(GrVkGpu* gpu) const {
diff --git a/src/gpu/vk/GrVkImage.h b/src/gpu/vk/GrVkImage.h
index 994924d..2da325d 100644
--- a/src/gpu/vk/GrVkImage.h
+++ b/src/gpu/vk/GrVkImage.h
@@ -139,7 +139,7 @@
     typedef void* ReleaseCtx;
     typedef void (*ReleaseProc)(ReleaseCtx);
 
-    void setResourceRelease(sk_sp<GrReleaseProcHelper> releaseHelper);
+    void setResourceRelease(sk_sp<GrRefCntedCallback> releaseHelper);
 
     // Helpers to use for setting the layout of the VkImage
     static VkPipelineStageFlags LayoutToPipelineSrcStageFlags(const VkImageLayout layout);
@@ -182,7 +182,7 @@
             SkDebugf("GrVkImage: %d (%d refs)\n", fImage, this->getRefCnt());
         }
 #endif
-        void setRelease(sk_sp<GrReleaseProcHelper> releaseHelper) {
+        void setRelease(sk_sp<GrRefCntedCallback> releaseHelper) {
             fReleaseHelper = std::move(releaseHelper);
         }
 
@@ -192,7 +192,7 @@
          * referring to the Resource then it calls the proc. Otherwise, the Resource calls it
          * when the last command buffer reference goes away and the GrVkTexture is purgeable.
          */
-        void setIdleProc(GrVkTexture* owner, GrTexture::IdleProc, void* context) const;
+        void replaceIdleProc(GrVkTexture* owner, sk_sp<GrRefCntedCallback>) const;
         void removeOwningTexture() const;
 
         /**
@@ -204,7 +204,7 @@
         bool isOwnedByCommandBuffer() const { return fNumCommandBufferOwners > 0; }
 
     protected:
-        mutable sk_sp<GrReleaseProcHelper> fReleaseHelper;
+        mutable sk_sp<GrRefCntedCallback> fReleaseHelper;
 
         void invokeReleaseProc() const {
             if (fReleaseHelper) {
@@ -225,8 +225,7 @@
         GrVkAlloc      fAlloc;
         VkImageTiling  fImageTiling;
         mutable int fNumCommandBufferOwners = 0;
-        mutable GrTexture::IdleProc* fIdleProc = nullptr;
-        mutable void* fIdleProcContext = nullptr;
+        mutable sk_sp<GrRefCntedCallback> fIdleCallback;
         mutable GrVkTexture* fOwningTexture = nullptr;
 
         typedef GrVkResource INHERITED;
diff --git a/src/gpu/vk/GrVkRenderTarget.h b/src/gpu/vk/GrVkRenderTarget.h
index 393d54d..bcfa823 100644
--- a/src/gpu/vk/GrVkRenderTarget.h
+++ b/src/gpu/vk/GrVkRenderTarget.h
@@ -162,7 +162,7 @@
 
     // In Vulkan we call the release proc after we are finished with the underlying
     // GrVkImage::Resource object (which occurs after the GPU has finished all work on it).
-    void onSetRelease(sk_sp<GrReleaseProcHelper> releaseHelper) override {
+    void onSetRelease(sk_sp<GrRefCntedCallback> releaseHelper) override {
         // Forward the release proc on to GrVkImage
         this->setResourceRelease(std::move(releaseHelper));
     }
diff --git a/src/gpu/vk/GrVkTexture.cpp b/src/gpu/vk/GrVkTexture.cpp
index cdb9351..74f9b15 100644
--- a/src/gpu/vk/GrVkTexture.cpp
+++ b/src/gpu/vk/GrVkTexture.cpp
@@ -124,8 +124,7 @@
     // who will handle it. If the resource is still tied to a command buffer we let it handle it.
     // Otherwise, we handle it.
     if (this->hasResource() && this->resource()->isOwnedByCommandBuffer()) {
-        fIdleProc = nullptr;
-        fIdleProcContext = nullptr;
+        fIdleCallback.reset();
     }
 
     // we create this and don't hand it off, so we should always destroy it
@@ -144,8 +143,7 @@
     // who will handle it. If the resource is still tied to a command buffer we let it handle it.
     // Otherwise, we handle it.
     if (this->hasResource() && this->resource()->isOwnedByCommandBuffer()) {
-        fIdleProc = nullptr;
-        fIdleProcContext = nullptr;
+        fIdleCallback.reset();
     }
 
     // we create this and don't hand it off, so we should always destroy it
@@ -171,28 +169,25 @@
     return fTextureView;
 }
 
-void GrVkTexture::setIdleProc(IdleProc proc, void* context) {
-    fIdleProc = proc;
-    fIdleProcContext = context;
+void GrVkTexture::addIdleProc(sk_sp<GrRefCntedCallback> callback) {
+    INHERITED::addIdleProc(callback);
     if (auto* resource = this->resource()) {
-        resource->setIdleProc(proc ? this : nullptr, proc, context);
+        resource->replaceIdleProc(this, fIdleCallback);
     }
 }
 
 void GrVkTexture::willRemoveLastRefOrPendingIO() {
-    if (!fIdleProc) {
+    if (!fIdleCallback) {
         return;
     }
     // This is called when the GrTexture is purgeable. However, we need to check whether the
     // Resource is still owned by any command buffers. If it is then it will call the proc.
     auto* resource = this->hasResource() ? this->resource() : nullptr;
-    if (resource && resource->isOwnedByCommandBuffer()) {
-        return;
-    }
-    fIdleProc(fIdleProcContext);
-    fIdleProc = nullptr;
-    fIdleProcContext = nullptr;
     if (resource) {
-        resource->setIdleProc(nullptr, nullptr, nullptr);
+        if (resource->isOwnedByCommandBuffer()) {
+            return;
+        }
+        resource->replaceIdleProc(this, nullptr);
     }
+    fIdleCallback.reset();
 }
diff --git a/src/gpu/vk/GrVkTexture.h b/src/gpu/vk/GrVkTexture.h
index 0cfce86..ffd4a3f 100644
--- a/src/gpu/vk/GrVkTexture.h
+++ b/src/gpu/vk/GrVkTexture.h
@@ -38,8 +38,8 @@
 
     const GrVkImageView* textureView();
 
-    void setIdleProc(IdleProc, void* context) override;
-    void* idleContext() const override { return fIdleProcContext; }
+    void addIdleProc(sk_sp<GrRefCntedCallback>) override;
+    void removeIdleProc() { fIdleCallback.reset(); }
 
 protected:
     GrVkTexture(GrVkGpu*, const GrSurfaceDesc&, const GrVkImageInfo&, sk_sp<GrVkImageLayout>,
@@ -54,6 +54,8 @@
         return false;
     }
 
+    void willRemoveLastRefOrPendingIO() override;
+
 private:
     GrVkTexture(GrVkGpu*, SkBudgeted, const GrSurfaceDesc&, const GrVkImageInfo&,
                 sk_sp<GrVkImageLayout> layout, const GrVkImageView* imageView,
@@ -64,16 +66,12 @@
 
     // In Vulkan we call the release proc after we are finished with the underlying
     // GrVkImage::Resource object (which occurs after the GPU has finished all work on it).
-    void onSetRelease(sk_sp<GrReleaseProcHelper> releaseHelper) override {
+    void onSetRelease(sk_sp<GrRefCntedCallback> releaseHelper) override {
         // Forward the release proc on to GrVkImage
         this->setResourceRelease(std::move(releaseHelper));
     }
 
-    void willRemoveLastRefOrPendingIO() override;
-
     const GrVkImageView* fTextureView;
-    GrTexture::IdleProc* fIdleProc = nullptr;
-    void* fIdleProcContext = nullptr;
 
     typedef GrTexture INHERITED;
 };
diff --git a/src/gpu/vk/GrVkTextureRenderTarget.h b/src/gpu/vk/GrVkTextureRenderTarget.h
index 5e90aa0..8093bac 100644
--- a/src/gpu/vk/GrVkTextureRenderTarget.h
+++ b/src/gpu/vk/GrVkTextureRenderTarget.h
@@ -107,7 +107,7 @@
 
     // In Vulkan we call the release proc after we are finished with the underlying
     // GrVkImage::Resource object (which occurs after the GPU has finished all work on it).
-    void onSetRelease(sk_sp<GrReleaseProcHelper> releaseHelper) override {
+    void onSetRelease(sk_sp<GrRefCntedCallback> releaseHelper) override {
         // Forward the release proc on to GrVkImage
         this->setResourceRelease(std::move(releaseHelper));
     }