Revert "Distinguish between "flushed" and "finished" idle state callbacks on GrTexture."

This reverts commit 9ac040700659cefff3c9a7ec6d4ada98948c307c.

Reason for revert: Breaking DDL Win10 skpbench bot

Original change's description:
> Distinguish between "flushed" and "finished" idle state callbacks on GrTexture.
> 
> This is necessary to convert the promise image API to call Release when all
> work is flushed and Done when all work is complete (future work).
> 
> Change-Id: I9745952bb0978ca2aaa79aeed460730b2fea856e
> Bug: skia:8800
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/197163
> Commit-Queue: Brian Salomon <bsalomon@google.com>
> Reviewed-by: Robert Phillips <robertphillips@google.com>

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

# Not skipping CQ checks because original CL landed > 1 day ago.

Bug: skia:8800
Change-Id: I5e6c4ea072beb4fb67a53d2ea2b007a7d201799d
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/198603
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/include/gpu/GrTexture.h b/include/gpu/GrTexture.h
index b5e7ff1..4a7935f 100644
--- a/include/gpu/GrTexture.h
+++ b/include/gpu/GrTexture.h
@@ -49,31 +49,17 @@
     }
 #endif
 
-    /** See addIdleProc. */
-    enum class IdleState {
-        kFlushed,
-        kFinished
-    };
     /**
-     * Installs a proc on this texture. It will be called when the texture becomes "idle". There
-     * are two types of idle states as indicated by IdleState. For managed backends (e.g. GL where
-     * a driver typically handles CPU/GPU synchronization of resource access) there is no difference
-     * between the two. They both mean "all work related to the resource has been flushed to the
-     * backend API and the texture is not owned outside the resource cache".
-     *
-     * If the API is unmanaged (e.g. Vulkan) then kFinished has the additional constraint that the
-     * work flushed to the GPU is finished.
+     * Installs a proc on this texture. It will be called when the texture becomes "idle". Idle is
+     * defined to mean that the texture has no refs or pending IOs and that GPU I/O operations on
+     * the texture are completed if the backend API disallows deletion of a texture before such
+     * operations occur (e.g. Vulkan). After the idle proc is called it is removed. The idle proc
+     * will always be called before the texture is destroyed, even in unusual shutdown scenarios
+     * (e.g. GrContext::abandonContext()).
      */
-    virtual void addIdleProc(sk_sp<GrRefCntedCallback> idleProc, IdleState) {
-        // This is the default implementation for the managed case where the IdleState can be
-        // ignored. Unmanaged backends, e.g. Vulkan, must override this to consider IdleState.
-        fIdleProcs.push_back(std::move(idleProc));
-    }
-    /** Helper version of addIdleProc that creates the ref-counted wrapper. */
-    void addIdleProc(GrRefCntedCallback::Callback callback,
-                     GrRefCntedCallback::Context context,
-                     IdleState state) {
-        this->addIdleProc(sk_make_sp<GrRefCntedCallback>(callback, context), state);
+    virtual void addIdleProc(sk_sp<GrRefCntedCallback> callback) {
+        callback->addChild(std::move(fIdleCallback));
+        fIdleCallback = std::move(callback);
     }
 
     /** Access methods that are only to be used within Skia code. */
@@ -85,11 +71,11 @@
 
     virtual bool onStealBackendTexture(GrBackendTexture*, SkImage::BackendTextureReleaseProc*) = 0;
 
-    SkTArray<sk_sp<GrRefCntedCallback>> fIdleProcs;
+    sk_sp<GrRefCntedCallback> fIdleCallback;
 
     void willRemoveLastRefOrPendingIO() override {
-        // We're about to be idle in the resource cache. Do our part to trigger the idle callbacks.
-        fIdleProcs.reset();
+        // We're about to be idle in the resource cache. Do our part to trigger the idle callback.
+        fIdleCallback.reset();
     }
 
 private:
diff --git a/include/private/GrTypesPriv.h b/include/private/GrTypesPriv.h
index f6c3590f..d6ea362 100644
--- a/include/private/GrTypesPriv.h
+++ b/include/private/GrTypesPriv.h
@@ -1555,7 +1555,8 @@
 }
 
 /**
- * Ref-counted object that calls a callback from its destructor.
+ * Ref-counted object that calls a callback from its destructor. These can be chained together. Any
+ * owner can cancel calling the callback via abandon().
  */
 class GrRefCntedCallback : public SkRefCnt {
 public:
@@ -1567,9 +1568,28 @@
     }
     ~GrRefCntedCallback() override { fReleaseProc ? fReleaseProc(fReleaseCtx) : void(); }
 
+    /**
+     * After abandon is called the release proc will no longer be called in the destructor. This
+     * does not recurse on child release procs or unref them.
+     */
+    void abandon() {
+        fReleaseProc = nullptr;
+        fReleaseCtx = nullptr;
+    }
+
+    /** Adds another GrRefCntedCallback that this will unref in its destructor. */
+    void addChild(sk_sp<GrRefCntedCallback> next) {
+        if (!fNext) {
+            fNext = std::move(next);
+            return;
+        }
+        fNext->addChild(std::move(next));
+    }
+
     Context context() const { return fReleaseCtx; }
 
 private:
+    sk_sp<GrRefCntedCallback> fNext;
     Callback fReleaseProc;
     Context fReleaseCtx;
 };
diff --git a/src/gpu/vk/GrVkImage.cpp b/src/gpu/vk/GrVkImage.cpp
index d90873b..524c33e 100644
--- a/src/gpu/vk/GrVkImage.cpp
+++ b/src/gpu/vk/GrVkImage.cpp
@@ -270,37 +270,28 @@
     GrVkMemory::FreeImageMemory(gpu, isLinear, fAlloc);
 }
 
-void GrVkImage::Resource::addIdleProc(GrVkTexture* owningTexture,
-                                      sk_sp<GrRefCntedCallback> idleProc) const {
-    SkASSERT(!fOwningTexture || fOwningTexture == owningTexture);
-    fOwningTexture = owningTexture;
-    fIdleProcs.push_back(std::move(idleProc));
+void GrVkImage::Resource::replaceIdleProc(
+        GrVkTexture* owner, sk_sp<GrRefCntedCallback> idleCallback) const {
+    fOwningTexture = owner;
+    fIdleCallback = std::move(idleCallback);
 }
 
-int GrVkImage::Resource::idleProcCnt() const { return fIdleProcs.count(); }
-
-sk_sp<GrRefCntedCallback> GrVkImage::Resource::idleProc(int i) const { return fIdleProcs[i]; }
-
-void GrVkImage::Resource::resetIdleProcs() const { fIdleProcs.reset(); }
-
 void GrVkImage::Resource::removeOwningTexture() const { fOwningTexture = nullptr; }
 
 void GrVkImage::Resource::notifyAddedToCommandBuffer() const { ++fNumCommandBufferOwners; }
 
 void GrVkImage::Resource::notifyRemovedFromCommandBuffer() const {
     SkASSERT(fNumCommandBufferOwners);
-    if (--fNumCommandBufferOwners || !fIdleProcs.count()) {
+    if (--fNumCommandBufferOwners || !fIdleCallback) {
         return;
     }
     if (fOwningTexture) {
         if (fOwningTexture->resourcePriv().hasRefOrPendingIO()) {
-            // Wait for the texture to become idle in the cache to call the procs.
             return;
         }
-        fOwningTexture->callIdleProcsOnBehalfOfResource();
-    } else {
-        fIdleProcs.reset();
+        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 ec3c15d..2da325d 100644
--- a/src/gpu/vk/GrVkImage.h
+++ b/src/gpu/vk/GrVkImage.h
@@ -187,15 +187,12 @@
         }
 
         /**
-         * These are used to coordinate calling the "finished" idle procs between the GrVkTexture
-         * and the Resource. If the GrVkTexture becomes purgeable and if there are no command
-         * buffers referring to the Resource then it calls the procs. Otherwise, the Resource calls
-         * them when the last command buffer reference goes away and the GrVkTexture is purgeable.
+         * These are used to coordinate calling the idle proc between the GrVkTexture and the
+         * Resource. If the GrVkTexture becomes purgeable and if there are no command buffers
+         * 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 addIdleProc(GrVkTexture*, sk_sp<GrRefCntedCallback>) const;
-        int idleProcCnt() const;
-        sk_sp<GrRefCntedCallback> idleProc(int) const;
-        void resetIdleProcs() const;
+        void replaceIdleProc(GrVkTexture* owner, sk_sp<GrRefCntedCallback>) const;
         void removeOwningTexture() const;
 
         /**
@@ -228,7 +225,7 @@
         GrVkAlloc      fAlloc;
         VkImageTiling  fImageTiling;
         mutable int fNumCommandBufferOwners = 0;
-        mutable SkTArray<sk_sp<GrRefCntedCallback>> fIdleProcs;
+        mutable sk_sp<GrRefCntedCallback> fIdleCallback;
         mutable GrVkTexture* fOwningTexture = nullptr;
 
         typedef GrVkResource INHERITED;
diff --git a/src/gpu/vk/GrVkTexture.cpp b/src/gpu/vk/GrVkTexture.cpp
index 8610a86..74f9b15 100644
--- a/src/gpu/vk/GrVkTexture.cpp
+++ b/src/gpu/vk/GrVkTexture.cpp
@@ -120,11 +120,11 @@
 }
 
 void GrVkTexture::onRelease() {
-    // We're about to be severed from our GrVkResource. If there are "finish" idle procs we have to
-    // decide who will handle them. If the resource is still tied to a command buffer we let it
-    // handle them. Otherwise, we handle them.
+    // We're about to be severed from our GrVkResource. If there is an idle proc we have to decide
+    // 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()) {
-        this->removeFinishIdleProcs();
+        fIdleCallback.reset();
     }
 
     // we create this and don't hand it off, so we should always destroy it
@@ -139,11 +139,11 @@
 }
 
 void GrVkTexture::onAbandon() {
-    // We're about to be severed from our GrVkResource. If there are "finish" idle procs we have to
-    // decide who will handle them. If the resource is still tied to a command buffer we let it
-    // handle them. Otherwise, we handle them.
+    // We're about to be severed from our GrVkResource. If there is an idle proc we have to decide
+    // 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()) {
-        this->removeFinishIdleProcs();
+        fIdleCallback.reset();
     }
 
     // we create this and don't hand it off, so we should always destroy it
@@ -169,70 +169,25 @@
     return fTextureView;
 }
 
-void GrVkTexture::addIdleProc(sk_sp<GrRefCntedCallback> idleProc, IdleState type) {
-    INHERITED::addIdleProc(idleProc, type);
-    if (type == IdleState::kFinished) {
-        if (auto* resource = this->resource()) {
-            resource->addIdleProc(this, std::move(idleProc));
-        }
+void GrVkTexture::addIdleProc(sk_sp<GrRefCntedCallback> callback) {
+    INHERITED::addIdleProc(callback);
+    if (auto* resource = this->resource()) {
+        resource->replaceIdleProc(this, fIdleCallback);
     }
 }
 
-void GrVkTexture::callIdleProcsOnBehalfOfResource() {
-    // If we got here then the resource is being removed from its last command buffer and the
-    // texture is idle in the cache. Any kFlush idle procs should already have been called. So
-    // the texture and resource should have the same set of procs.
-    SkASSERT(this->resource());
-    SkASSERT(this->resource()->idleProcCnt() == fIdleProcs.count());
-#ifdef SK_DEBUG
-    for (int i = 0; i < fIdleProcs.count(); ++i) {
-        SkASSERT(fIdleProcs[i] == this->resource()->idleProc(i));
-    }
-#endif
-    fIdleProcs.reset();
-    this->resource()->resetIdleProcs();
-}
-
 void GrVkTexture::willRemoveLastRefOrPendingIO() {
-    if (!fIdleProcs.count()) {
+    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;
-    bool callFinishProcs = !resource || !resource->isOwnedByCommandBuffer();
-    if (callFinishProcs) {
-        // Everything must go!
-        fIdleProcs.reset();
-        resource->resetIdleProcs();
-    } else {
-        // The procs that should be called on flush but not finish are those that are owned
-        // by the GrVkTexture and not the Resource. We do this by copying the resource's array
-        // and thereby dropping refs to procs we own but the resource does not.
-        SkASSERT(resource);
-        fIdleProcs.reset(resource->idleProcCnt());
-        for (int i = 0; i < fIdleProcs.count(); ++i) {
-            fIdleProcs[i] = resource->idleProc(i);
+    if (resource) {
+        if (resource->isOwnedByCommandBuffer()) {
+            return;
         }
+        resource->replaceIdleProc(this, nullptr);
     }
-}
-
-void GrVkTexture::removeFinishIdleProcs() {
-    // This should only be called by onRelease/onAbandon when we have already checked for a
-    // resource.
-    const auto* resource = this->resource();
-    SkASSERT(resource);
-    SkSTArray<4, sk_sp<GrRefCntedCallback>> procsToKeep;
-    int resourceIdx = 0;
-    // The idle procs that are common between the GrVkTexture and its Resource should be found in
-    // the same order.
-    for (int i = 0; i < fIdleProcs.count(); ++i) {
-        if (fIdleProcs[i] == resource->idleProc(resourceIdx)) {
-            ++resourceIdx;
-        } else {
-            procsToKeep.push_back(fIdleProcs[i]);
-        }
-    }
-    SkASSERT(resourceIdx == resource->idleProcCnt());
-    fIdleProcs = procsToKeep;
+    fIdleCallback.reset();
 }
diff --git a/src/gpu/vk/GrVkTexture.h b/src/gpu/vk/GrVkTexture.h
index 50d4cae..ffd4a3f 100644
--- a/src/gpu/vk/GrVkTexture.h
+++ b/src/gpu/vk/GrVkTexture.h
@@ -38,8 +38,8 @@
 
     const GrVkImageView* textureView();
 
-    void addIdleProc(sk_sp<GrRefCntedCallback>, IdleState) override;
-    void callIdleProcsOnBehalfOfResource();
+    void addIdleProc(sk_sp<GrRefCntedCallback>) override;
+    void removeIdleProc() { fIdleCallback.reset(); }
 
 protected:
     GrVkTexture(GrVkGpu*, const GrSurfaceDesc&, const GrVkImageInfo&, sk_sp<GrVkImageLayout>,
@@ -71,8 +71,6 @@
         this->setResourceRelease(std::move(releaseHelper));
     }
 
-    void removeFinishIdleProcs();
-
     const GrVkImageView* fTextureView;
 
     typedef GrTexture INHERITED;
diff --git a/src/image/SkImage_GpuBase.cpp b/src/image/SkImage_GpuBase.cpp
index 2618dd4..ec2e651 100644
--- a/src/image/SkImage_GpuBase.cpp
+++ b/src/image/SkImage_GpuBase.cpp
@@ -426,8 +426,10 @@
                                        PromiseImageTextureDoneProc doneProc,
                                        PromiseImageTextureContext context,
                                        GrPixelConfig config)
-                : fFulfillProc(fulfillProc), fReleaseProc(releaseProc), fConfig(config) {
-            fDoneCallback = sk_make_sp<GrRefCntedCallback>(doneProc, context);
+                : fFulfillProc(fulfillProc), fConfig(config) {
+            auto doneHelper = sk_make_sp<GrRefCntedCallback>(doneProc, context);
+            fIdleCallback = sk_make_sp<GrRefCntedCallback>(releaseProc, context);
+            fIdleCallback->addChild(std::move(doneHelper));
         }
         PromiseLazyInstantiateCallback(PromiseLazyInstantiateCallback&&) = default;
         PromiseLazyInstantiateCallback(const PromiseLazyInstantiateCallback&) {
@@ -441,20 +443,30 @@
             return *this;
         }
 
+        ~PromiseLazyInstantiateCallback() {
+            if (fIdleCallback) {
+                // We were never fulfilled. Pass false so done proc is still called.
+                fIdleCallback->abandon();
+            }
+        }
+
         sk_sp<GrSurface> operator()(GrResourceProvider* resourceProvider) {
-            SkASSERT(fDoneCallback);
-            PromiseImageTextureContext textureContext = fDoneCallback->context();
+            SkASSERT(fIdleCallback);
+            PromiseImageTextureContext textureContext = fIdleCallback->context();
             sk_sp<SkPromiseImageTexture> promiseTexture = fFulfillProc(textureContext);
             // From here on out our contract is that the release proc must be called, even if
             // the return from fulfill was invalid or we fail for some other reason.
-            auto releaseCallback = sk_make_sp<GrRefCntedCallback>(fReleaseProc, textureContext);
             if (!promiseTexture) {
+                // Make sure we explicitly reset this because our destructor assumes a non-null
+                // fIdleCallback means fulfill was never called.
+                fIdleCallback.reset();
                 return sk_sp<GrTexture>();
             }
 
             auto backendTexture = promiseTexture->backendTexture();
             backendTexture.fConfig = fConfig;
             if (!backendTexture.isValid()) {
+                fIdleCallback.reset();
                 return sk_sp<GrTexture>();
             }
 
@@ -477,19 +489,18 @@
                              kRead_GrIOType))) {
                     tex->resourcePriv().setUniqueKey(key);
                 } else {
+                    fIdleCallback.reset();
                     return sk_sp<GrTexture>();
                 }
             }
-            tex->addIdleProc(std::move(releaseCallback), GrTexture::IdleState::kFinished);
-            tex->addIdleProc(std::move(fDoneCallback), GrTexture::IdleState::kFinished);
+            tex->addIdleProc(std::move(fIdleCallback));
             promiseTexture->addKeyToInvalidate(tex->getContext()->priv().contextID(), key);
             return std::move(tex);
         }
 
     private:
+        sk_sp<GrRefCntedCallback> fIdleCallback;
         PromiseImageTextureFulfillProc fFulfillProc;
-        PromiseImageTextureReleaseProc fReleaseProc;
-        sk_sp<GrRefCntedCallback> fDoneCallback;
         GrPixelConfig fConfig;
     } callback(fulfillProc, releaseProc, doneProc, textureContext, config);
 
diff --git a/tests/GrSurfaceTest.cpp b/tests/GrSurfaceTest.cpp
index 745f1de..9035cd7 100644
--- a/tests/GrSurfaceTest.cpp
+++ b/tests/GrSurfaceTest.cpp
@@ -423,8 +423,8 @@
                 // Makes a texture, possibly adds a key, and sets the callback.
                 auto make = [&m, &keyAdder, &proc, &idleIDs](GrContext* context, int num) {
                     sk_sp<GrTexture> texture = m(context);
-                    texture->addIdleProc(proc, new Context{&idleIDs, num},
-                                         GrTexture::IdleState::kFinished);
+                    texture->addIdleProc(
+                            sk_make_sp<GrRefCntedCallback>(proc, new Context{&idleIDs, num}));
                     keyAdder(texture.get());
                     return texture;
                 };
@@ -585,15 +585,12 @@
 
     for (const auto& idleMaker : {make_wrapped_texture, make_normal_texture}) {
         for (const auto& otherMaker : {make_wrapped_texture, make_normal_texture}) {
-            for (auto idleState :
-                 {GrTexture::IdleState::kFlushed, GrTexture::IdleState::kFinished}) {
-                auto idleTexture = idleMaker(context, false);
-                auto otherTexture = otherMaker(context, false);
-                otherTexture->ref();
-                idleTexture->addIdleProc(idleProc, otherTexture.get(), idleState);
-                otherTexture.reset();
-                idleTexture.reset();
-            }
+            auto idleTexture = idleMaker(context, false);
+            auto otherTexture = otherMaker(context, false);
+            otherTexture->ref();
+            idleTexture->addIdleProc(sk_make_sp<GrRefCntedCallback>(idleProc, otherTexture.get()));
+            otherTexture.reset();
+            idleTexture.reset();
         }
     }
 }
@@ -607,27 +604,25 @@
     auto idleProc = [](void* context) { reinterpret_cast<GrContext*>(context)->flush(); };
 
     for (const auto& idleMaker : {make_wrapped_texture, make_normal_texture}) {
-        for (auto idleState : {GrTexture::IdleState::kFlushed, GrTexture::IdleState::kFinished}) {
-            auto idleTexture = idleMaker(context, false);
-            idleTexture->addIdleProc(idleProc, context, idleState);
-            auto info = SkImageInfo::Make(10, 10, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
-            auto surf = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info, 1, nullptr);
-            // We'll draw two images to the canvas. One is a normal texture-backed image. The other
-            // is a wrapped-texture backed image.
-            surf->getCanvas()->clear(SK_ColorWHITE);
-            auto img1 = surf->makeImageSnapshot();
-            auto gpu = context->priv().getGpu();
-            std::unique_ptr<uint32_t[]> pixels(new uint32_t[info.width() * info.height()]);
-            auto backendTexture = gpu->createTestingOnlyBackendTexture(
-                    pixels.get(), info.width(), info.height(), kRGBA_8888_SkColorType, false,
-                    GrMipMapped::kNo);
-            auto img2 = SkImage::MakeFromTexture(context, backendTexture, kTopLeft_GrSurfaceOrigin,
-                                                 info.colorType(), info.alphaType(), nullptr);
-            surf->getCanvas()->drawImage(std::move(img1), 0, 0);
-            surf->getCanvas()->drawImage(std::move(img2), 1, 1);
-            idleTexture.reset();
-            gpu->deleteTestingOnlyBackendTexture(backendTexture);
-        }
+        auto idleTexture = idleMaker(context, false);
+        idleTexture->addIdleProc(sk_make_sp<GrRefCntedCallback>(idleProc, context));
+        auto info = SkImageInfo::Make(10, 10, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+        auto surf = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info, 1, nullptr);
+        // We'll draw two images to the canvas. One is a normal texture-backed image. The other is
+        // a wrapped-texture backed image.
+        surf->getCanvas()->clear(SK_ColorWHITE);
+        auto img1 = surf->makeImageSnapshot();
+        auto gpu = context->priv().getGpu();
+        std::unique_ptr<uint32_t[]> pixels(new uint32_t[info.width() * info.height()]);
+        auto backendTexture = gpu->createTestingOnlyBackendTexture(
+                pixels.get(), info.width(), info.height(), kRGBA_8888_SkColorType, false,
+                GrMipMapped::kNo);
+        auto img2 = SkImage::MakeFromTexture(context, backendTexture, kTopLeft_GrSurfaceOrigin,
+                                             info.colorType(), info.alphaType(), nullptr);
+        surf->getCanvas()->drawImage(std::move(img1), 0, 0);
+        surf->getCanvas()->drawImage(std::move(img2), 1, 1);
+        idleTexture.reset();
+        gpu->deleteTestingOnlyBackendTexture(backendTexture);
     }
 }
 
@@ -637,59 +632,17 @@
     auto idleProc = [](void* texture) { reinterpret_cast<GrTexture*>(texture)->ref(); };
     // release proc to check whether the texture was released or not.
     auto releaseProc = [](void* isReleased) { *reinterpret_cast<bool*>(isReleased) = true; };
-    for (auto idleState : {GrTexture::IdleState::kFlushed, GrTexture::IdleState::kFinished}) {
-        bool isReleased = false;
-        auto idleTexture = make_normal_texture(context, false);
-        // This test assumes the texture won't be cached (or else the release proc doesn't get
-        // called).
-        idleTexture->resourcePriv().removeScratchKey();
-        context->flush();
-        idleTexture->addIdleProc(idleProc, idleTexture.get(), idleState);
-        idleTexture->setRelease(releaseProc, &isReleased);
-        auto* raw = idleTexture.get();
-        idleTexture.reset();
-        REPORTER_ASSERT(reporter, !isReleased);
-        raw->unref();
-        REPORTER_ASSERT(reporter, isReleased);
-    }
-}
-
-DEF_GPUTEST_FOR_ALL_CONTEXTS(TextureIdleStateTest, reporter, contextInfo) {
-    GrContext* context = contextInfo.grContext();
-    for (const auto& idleMaker : {make_wrapped_texture, make_normal_texture}) {
-        auto idleTexture = idleMaker(context, false);
-
-        uint32_t flags = 0;
-        static constexpr uint32_t kFlushFlag = 0x1;
-        static constexpr uint32_t kFinishFlag = 0x2;
-        auto flushProc = [](void* flags) { *static_cast<uint32_t*>(flags) |= kFlushFlag; };
-        auto finishProc = [](void* flags) { *static_cast<uint32_t*>(flags) |= kFinishFlag; };
-        idleTexture->addIdleProc(flushProc, &flags, GrTexture::IdleState::kFlushed);
-        idleTexture->addIdleProc(finishProc, &flags, GrTexture::IdleState::kFinished);
-
-        // Insert a copy from idleTexture to another texture so that we have some queued IO on
-        // idleTexture.
-        auto proxy = context->priv().proxyProvider()->testingOnly_createWrapped(
-                std::move(idleTexture), kTopLeft_GrSurfaceOrigin);
-        SkImageInfo info = SkImageInfo::Make(proxy->width(), proxy->height(),
-                                             kRGBA_8888_SkColorType, kPremul_SkAlphaType);
-        auto rt = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info, 0, nullptr);
-        auto rtc = rt->getCanvas()->internal_private_accessTopLayerRenderTargetContext();
-        context->flush();
-        rtc->copy(proxy.get());
-        proxy.reset();
-        REPORTER_ASSERT(reporter, flags == 0);
-
-        // After a flush we expect idleTexture to have reached the kFlushed state on all backends.
-        // On "managed" backends we expect it to reach kFinished as well. On Vulkan, the only
-        // current "unmanaged" backend, we *may* need a sync to reach kFinished.
-        context->flush();
-        if (contextInfo.backend() == kVulkan_GrBackend) {
-            REPORTER_ASSERT(reporter, flags & kFlushFlag);
-        } else {
-            REPORTER_ASSERT(reporter, flags == (kFlushFlag | kFinishFlag));
-        }
-        context->priv().getGpu()->testingOnly_flushGpuAndSync();
-        REPORTER_ASSERT(reporter, flags == (kFlushFlag | kFinishFlag));
-    }
+    bool isReleased = false;
+    auto idleTexture = make_normal_texture(context, false);
+    // This test assumes the texture won't be cached (or else the release proc doesn't get
+    // called).
+    idleTexture->resourcePriv().removeScratchKey();
+    context->flush();
+    idleTexture->addIdleProc(sk_make_sp<GrRefCntedCallback>(idleProc, idleTexture.get()));
+    idleTexture->setRelease(releaseProc, &isReleased);
+    auto* raw = idleTexture.get();
+    idleTexture.reset();
+    REPORTER_ASSERT(reporter, !isReleased);
+    raw->unref();
+    REPORTER_ASSERT(reporter, isReleased);
 }