Make unref messages to GrResourceCache work with all resource types.

Previously we made them texture-only to fix an upcast that occurred
through a virtual inheritance link on a pointer to a deleted object.

This change changes the mechanism such that the sender of the message
maintains a ref that is moved into the sent message. The upcast happens
during creation of the message and thus before the resource is deleted.

The motivation is to use this for SkCustomMesh vertex/index buffers.

Bug: skia:13036
Change-Id: I0990eaed0d736257e1b19e9819e0dac8a9af8c18
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/537084
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Greg Daniel <egdaniel@google.com>
diff --git a/src/gpu/ganesh/GrBackendTextureImageGenerator.cpp b/src/gpu/ganesh/GrBackendTextureImageGenerator.cpp
index 8effdee..7896518 100644
--- a/src/gpu/ganesh/GrBackendTextureImageGenerator.cpp
+++ b/src/gpu/ganesh/GrBackendTextureImageGenerator.cpp
@@ -24,9 +24,9 @@
 #include "src/gpu/ganesh/gl/GrGLTexture.h"
 
 GrBackendTextureImageGenerator::RefHelper::RefHelper(
-                    GrTexture* texture,
-                    GrDirectContext::DirectContextID owningContextID,
-                    std::unique_ptr<GrSemaphore> semaphore)
+        sk_sp<GrTexture> texture,
+        GrDirectContext::DirectContextID owningContextID,
+        std::unique_ptr<GrSemaphore> semaphore)
         : fOriginalTexture(texture)
         , fOwningContextID(owningContextID)
         , fBorrowingContextReleaseProc(nullptr)
@@ -34,48 +34,43 @@
 
 GrBackendTextureImageGenerator::RefHelper::~RefHelper() {
     SkASSERT(!fBorrowingContextID.isValid());
-
     // Generator has been freed, and no one is borrowing the texture. Notify the original cache
     // that it can free the last ref, so it happens on the correct thread.
-    GrTextureFreedMessage msg { fOriginalTexture, fOwningContextID };
-    SkMessageBus<GrTextureFreedMessage, GrDirectContext::DirectContextID>::Post(msg);
+    GrResourceCache::ReturnResourceFromThread(std::move(fOriginalTexture), fOwningContextID);
 }
 
 std::unique_ptr<SkImageGenerator>
-GrBackendTextureImageGenerator::Make(sk_sp<GrTexture> texture, GrSurfaceOrigin origin,
-                                     std::unique_ptr<GrSemaphore> semaphore, SkColorType colorType,
-                                     SkAlphaType alphaType, sk_sp<SkColorSpace> colorSpace) {
+GrBackendTextureImageGenerator::Make(sk_sp<GrTexture> texture,
+                                     GrSurfaceOrigin origin,
+                                     std::unique_ptr<GrSemaphore> semaphore,
+                                     SkColorType colorType,
+                                     SkAlphaType alphaType,
+                                     sk_sp<SkColorSpace> colorSpace) {
     GrDirectContext* dContext = texture->getContext();
 
-    // Attach our texture to this context's resource cache. This ensures that deletion will happen
-    // in the correct thread/context. This adds the only ref to the texture that will persist from
-    // this point. That ref will be released when the generator's RefHelper is freed.
-    dContext->priv().getResourceCache()->insertDelayedTextureUnref(texture.get());
-
-    GrBackendTexture backendTexture = texture->getBackendTexture();
-
     if (!dContext->priv().caps()->areColorTypeAndFormatCompatible(
-            SkColorTypeToGrColorType(colorType), backendTexture.getBackendFormat())) {
+                SkColorTypeToGrColorType(colorType), texture->backendFormat())) {
         return nullptr;
     }
 
-    SkImageInfo info = SkImageInfo::Make(texture->width(), texture->height(), colorType, alphaType,
-                                         std::move(colorSpace));
+    SkColorInfo info(colorType, alphaType, std::move(colorSpace));
     return std::unique_ptr<SkImageGenerator>(new GrBackendTextureImageGenerator(
-          info, texture.get(), origin, dContext->directContextID(),
-          std::move(semaphore), backendTexture));
+            info,
+            std::move(texture),
+            origin,
+            dContext->directContextID(),
+            std::move(semaphore)));
 }
 
 GrBackendTextureImageGenerator::GrBackendTextureImageGenerator(
-                    const SkImageInfo& info,
-                    GrTexture* texture,
-                    GrSurfaceOrigin origin,
-                    GrDirectContext::DirectContextID owningContextID,
-                    std::unique_ptr<GrSemaphore> semaphore,
-                    const GrBackendTexture& backendTex)
-        : INHERITED(info)
+        const SkColorInfo& info,
+        sk_sp<GrTexture> texture,
+        GrSurfaceOrigin origin,
+        GrDirectContext::DirectContextID owningContextID,
+        std::unique_ptr<GrSemaphore> semaphore)
+        : INHERITED(SkImageInfo::Make(texture->dimensions(), info))
         , fRefHelper(new RefHelper(texture, owningContextID, std::move(semaphore)))
-        , fBackendTexture(backendTex)
+        , fBackendTexture(texture->getBackendTexture())
         , fSurfaceOrigin(origin) {}
 
 GrBackendTextureImageGenerator::~GrBackendTextureImageGenerator() {
diff --git a/src/gpu/ganesh/GrBackendTextureImageGenerator.h b/src/gpu/ganesh/GrBackendTextureImageGenerator.h
index 30ea050..6da01c5 100644
--- a/src/gpu/ganesh/GrBackendTextureImageGenerator.h
+++ b/src/gpu/ganesh/GrBackendTextureImageGenerator.h
@@ -48,24 +48,23 @@
                                          GrMipmapped mipmapped, GrImageTexGenPolicy) override;
 
 private:
-    GrBackendTextureImageGenerator(const SkImageInfo& info,
-                                   GrTexture*,
+    GrBackendTextureImageGenerator(const SkColorInfo&,
+                                   sk_sp<GrTexture>,
                                    GrSurfaceOrigin,
                                    GrDirectContext::DirectContextID owningContextID,
-                                   std::unique_ptr<GrSemaphore>,
-                                   const GrBackendTexture&);
+                                   std::unique_ptr<GrSemaphore>);
 
     static void ReleaseRefHelper_TextureReleaseProc(void* ctx);
 
     class RefHelper : public SkNVRefCnt<RefHelper> {
     public:
-        RefHelper(GrTexture*,
+        RefHelper(sk_sp<GrTexture>,
                   GrDirectContext::DirectContextID owningContextID,
                   std::unique_ptr<GrSemaphore>);
 
         ~RefHelper();
 
-        GrTexture*                       fOriginalTexture;
+        sk_sp<GrTexture>                 fOriginalTexture;
         GrDirectContext::DirectContextID fOwningContextID;
 
         // We use this key so that we don't rewrap the GrBackendTexture in a GrTexture for each
diff --git a/src/gpu/ganesh/GrResourceCache.cpp b/src/gpu/ganesh/GrResourceCache.cpp
index 989c365..d837c1e 100644
--- a/src/gpu/ganesh/GrResourceCache.cpp
+++ b/src/gpu/ganesh/GrResourceCache.cpp
@@ -28,7 +28,9 @@
 
 DECLARE_SKMESSAGEBUS_MESSAGE(skgpu::UniqueKeyInvalidatedMessage, uint32_t, true);
 
-DECLARE_SKMESSAGEBUS_MESSAGE(GrTextureFreedMessage, GrDirectContext::DirectContextID, true);
+DECLARE_SKMESSAGEBUS_MESSAGE(GrResourceCache::UnrefResourceMessage,
+                             GrDirectContext::DirectContextID,
+                             /*AllowCopyableMessage=*/false);
 
 #define ASSERT_SINGLE_OWNER SKGPU_ASSERT_SINGLE_OWNER(fSingleOwner)
 
@@ -44,48 +46,11 @@
 
 //////////////////////////////////////////////////////////////////////////////
 
-inline GrResourceCache::TextureAwaitingUnref::TextureAwaitingUnref() = default;
-
-inline GrResourceCache::TextureAwaitingUnref::TextureAwaitingUnref(GrTexture* texture)
-        : fTexture(texture), fNumUnrefs(1) {}
-
-inline GrResourceCache::TextureAwaitingUnref::TextureAwaitingUnref(TextureAwaitingUnref&& that) {
-    fTexture = std::exchange(that.fTexture, nullptr);
-    fNumUnrefs = std::exchange(that.fNumUnrefs, 0);
-}
-
-inline GrResourceCache::TextureAwaitingUnref& GrResourceCache::TextureAwaitingUnref::operator=(
-        TextureAwaitingUnref&& that) {
-    fTexture = std::exchange(that.fTexture, nullptr);
-    fNumUnrefs = std::exchange(that.fNumUnrefs, 0);
-    return *this;
-}
-
-inline GrResourceCache::TextureAwaitingUnref::~TextureAwaitingUnref() {
-    if (fTexture) {
-        for (int i = 0; i < fNumUnrefs; ++i) {
-            fTexture->unref();
-        }
-    }
-}
-
-inline void GrResourceCache::TextureAwaitingUnref::TextureAwaitingUnref::addRef() { ++fNumUnrefs; }
-
-inline void GrResourceCache::TextureAwaitingUnref::unref() {
-    SkASSERT(fNumUnrefs > 0);
-    fTexture->unref();
-    --fNumUnrefs;
-}
-
-inline bool GrResourceCache::TextureAwaitingUnref::finished() { return !fNumUnrefs; }
-
-//////////////////////////////////////////////////////////////////////////////
-
 GrResourceCache::GrResourceCache(skgpu::SingleOwner* singleOwner,
                                  GrDirectContext::DirectContextID owningContextID,
                                  uint32_t familyID)
         : fInvalidUniqueKeyInbox(familyID)
-        , fFreedTextureInbox(owningContextID)
+        , fUnrefResourceInbox(owningContextID)
         , fOwningContextID(owningContextID)
         , fContextUniqueID(familyID)
         , fSingleOwner(singleOwner) {
@@ -170,10 +135,6 @@
 void GrResourceCache::abandonAll() {
     AutoValidate av(this);
 
-    // We need to make sure to free any resources that were waiting on a free message but never
-    // received one.
-    fTexturesAwaitingUnref.reset();
-
     while (fNonpurgeableResources.count()) {
         GrGpuResource* back = *(fNonpurgeableResources.end() - 1);
         SkASSERT(!back->wasDestroyed());
@@ -196,7 +157,6 @@
     SkASSERT(!fBudgetedCount);
     SkASSERT(!fBudgetedBytes);
     SkASSERT(!fPurgeableBytes);
-    SkASSERT(!fTexturesAwaitingUnref.count());
 }
 
 void GrResourceCache::releaseAll() {
@@ -206,10 +166,6 @@
 
     this->processFreedGpuResources();
 
-    // We need to make sure to free any resources that were waiting on a free message but never
-    // received one.
-    fTexturesAwaitingUnref.reset();
-
     SkASSERT(fProxyProvider); // better have called setProxyProvider
     SkASSERT(fThreadSafeCache); // better have called setThreadSafeCache too
 
@@ -237,7 +193,6 @@
     SkASSERT(!fBudgetedCount);
     SkASSERT(!fBudgetedBytes);
     SkASSERT(!fPurgeableBytes);
-    SkASSERT(!fTexturesAwaitingUnref.count());
 }
 
 void GrResourceCache::refResource(GrGpuResource* resource) {
@@ -678,36 +633,10 @@
            fNumBudgetedResourcesFlushWillMakePurgeable > 0;
 }
 
-void GrResourceCache::insertDelayedTextureUnref(GrTexture* texture) {
-    texture->ref();
-    uint32_t id = texture->uniqueID().asUInt();
-    if (auto* data = fTexturesAwaitingUnref.find(id)) {
-        data->addRef();
-    } else {
-        fTexturesAwaitingUnref.set(id, {texture});
-    }
-}
-
 void GrResourceCache::processFreedGpuResources() {
-    if (!fTexturesAwaitingUnref.count()) {
-        return;
-    }
-
-    SkTArray<GrTextureFreedMessage> msgs;
-    fFreedTextureInbox.poll(&msgs);
-    for (int i = 0; i < msgs.count(); ++i) {
-        SkASSERT(msgs[i].fIntendedRecipient == fOwningContextID);
-        uint32_t id = msgs[i].fTexture->uniqueID().asUInt();
-        TextureAwaitingUnref* info = fTexturesAwaitingUnref.find(id);
-        // If the GrContext was released or abandoned then fTexturesAwaitingUnref should have been
-        // empty and we would have returned early above. Thus, any texture from a message should be
-        // in the list of fTexturesAwaitingUnref.
-        SkASSERT(info);
-        info->unref();
-        if (info->finished()) {
-            fTexturesAwaitingUnref.remove(id);
-        }
-    }
+    SkTArray<UnrefResourceMessage> msgs;
+    fUnrefResourceInbox.poll(&msgs);
+    // We don't need to do anything other than let the messages delete themselves and call unref.
 }
 
 void GrResourceCache::addToNonpurgeableArray(GrGpuResource* resource) {
diff --git a/src/gpu/ganesh/GrResourceCache.h b/src/gpu/ganesh/GrResourceCache.h
index 12ab4a9..fd1e013 100644
--- a/src/gpu/ganesh/GrResourceCache.h
+++ b/src/gpu/ganesh/GrResourceCache.h
@@ -32,16 +32,6 @@
 class SingleOwner;
 }
 
-struct GrTextureFreedMessage {
-    GrTexture* fTexture;
-    GrDirectContext::DirectContextID fIntendedRecipient;
-};
-
-static inline bool SkShouldPostMessageToBus(
-        const GrTextureFreedMessage& msg, GrDirectContext::DirectContextID potentialRecipient) {
-    return potentialRecipient == msg.fIntendedRecipient;
-}
-
 /**
  * Manages the lifetime of all GrGpuResource instances.
  *
@@ -66,6 +56,25 @@
                     uint32_t familyID);
     ~GrResourceCache();
 
+    /**
+     * This is used to safely return a resource to the cache when the owner may be on another
+     * thread from GrDirectContext. If the context still exists then using this method ensures that
+     * the resource is received by the cache for processing (recycling or destruction) on the
+     * context's thread.
+     *
+     * This is templated as it is rather than simply taking sk_sp<GrGpuResource> in order to enforce
+     * that the caller passes an rvalue. If the caller doesn't move its ref into this function
+     * then it will retain ownership, defeating the purpose. (Note that sk_sp<GrGpuResource>&&
+     * doesn't work either because calling with sk_sp<GrSpecificResource> will create a temporary
+     * sk_sp<GrGpuResource> which is an rvalue).
+     */
+    template<typename T>
+    static std::enable_if_t<std::is_base_of_v<GrGpuResource, T>, void>
+    ReturnResourceFromThread(sk_sp<T>&& resource, GrDirectContext::DirectContextID id) {
+        UnrefResourceMessage msg(std::move(resource), id);
+        UnrefResourceMessage::Bus::Post(std::move(msg));
+    }
+
     // Default maximum number of bytes of gpu memory of budgeted resources in the cache.
     static const size_t kDefaultMaxSize             = 256 * (1 << 20);
 
@@ -195,9 +204,6 @@
         purgeable. */
     bool requestsFlush() const;
 
-    /** Maintain a ref to this texture until we receive a GrTextureFreedMessage. */
-    void insertDelayedTextureUnref(GrTexture*);
-
 #if GR_CACHE_STATS
     struct Stats {
         int fTotal;
@@ -256,6 +262,33 @@
         fThreadSafeCache = threadSafeCache;
     }
 
+    // It'd be nice if this could be private but SkMessageBus relies on macros to define types that
+    // require this to be public.
+    class UnrefResourceMessage {
+    public:
+        GrDirectContext::DirectContextID recipient() const { return fRecipient; }
+
+        UnrefResourceMessage(UnrefResourceMessage&&) = default;
+        UnrefResourceMessage& operator=(UnrefResourceMessage&&) = default;
+
+    private:
+        friend class GrResourceCache;
+
+        using Bus = SkMessageBus<UnrefResourceMessage,
+                                 GrDirectContext::DirectContextID,
+                                 /*AllowCopyableMessage=*/false>;
+
+        UnrefResourceMessage(sk_sp<GrGpuResource>&& resource,
+                             GrDirectContext::DirectContextID recipient)
+                : fResource(std::move(resource)), fRecipient(recipient) {}
+
+        UnrefResourceMessage(const UnrefResourceMessage&) = delete;
+        UnrefResourceMessage& operator=(const UnrefResourceMessage&) = delete;
+
+        sk_sp<GrGpuResource> fResource;
+        GrDirectContext::DirectContextID fRecipient;
+    };
+
 private:
     ///////////////////////////////////////////////////////////////////////////
     /// @name Methods accessible via ResourceAccess
@@ -308,25 +341,6 @@
     };
     typedef SkTDynamicHash<GrGpuResource, skgpu::UniqueKey, UniqueHashTraits> UniqueHash;
 
-    class TextureAwaitingUnref {
-    public:
-        TextureAwaitingUnref();
-        TextureAwaitingUnref(GrTexture* texture);
-        TextureAwaitingUnref(const TextureAwaitingUnref&) = delete;
-        TextureAwaitingUnref& operator=(const TextureAwaitingUnref&) = delete;
-        TextureAwaitingUnref(TextureAwaitingUnref&&);
-        TextureAwaitingUnref& operator=(TextureAwaitingUnref&&);
-        ~TextureAwaitingUnref();
-        void addRef();
-        void unref();
-        bool finished();
-
-    private:
-        GrTexture* fTexture = nullptr;
-        int fNumUnrefs = 0;
-    };
-    using TexturesAwaitingUnref = SkTHashMap<uint32_t, TextureAwaitingUnref>;
-
     static bool CompareTimestamp(GrGpuResource* const& a, GrGpuResource* const& b) {
         return a->cacheAccess().timestamp() < b->cacheAccess().timestamp();
     }
@@ -335,9 +349,6 @@
         return res->cacheAccess().accessCacheIndex();
     }
 
-    using TextureFreedMessageBus = SkMessageBus<GrTextureFreedMessage,
-                                                GrDirectContext::DirectContextID>;
-
     typedef SkMessageBus<skgpu::UniqueKeyInvalidatedMessage, uint32_t>::Inbox InvalidUniqueKeyInbox;
     typedef SkTDPQueue<GrGpuResource*, CompareTimestamp, AccessResourceIndex> PurgeableQueue;
     typedef SkTDArray<GrGpuResource*> ResourceArray;
@@ -378,8 +389,7 @@
     int                                 fNumBudgetedResourcesFlushWillMakePurgeable = 0;
 
     InvalidUniqueKeyInbox               fInvalidUniqueKeyInbox;
-    TextureFreedMessageBus::Inbox       fFreedTextureInbox;
-    TexturesAwaitingUnref               fTexturesAwaitingUnref;
+    UnrefResourceMessage::Bus::Inbox    fUnrefResourceInbox;
 
     GrDirectContext::DirectContextID    fOwningContextID;
     uint32_t                            fContextUniqueID = SK_InvalidUniqueID;
@@ -465,6 +475,11 @@
     friend class GrResourceCache; // To create this type.
 };
 
+static inline bool SkShouldPostMessageToBus(const GrResourceCache::UnrefResourceMessage& msg,
+                                            GrDirectContext::DirectContextID potentialRecipient) {
+    return potentialRecipient == msg.recipient();
+}
+
 inline GrResourceCache::ResourceAccess GrResourceCache::resourceAccess() {
     return ResourceAccess(this);
 }
diff --git a/src/image/SkImage_GpuBase.cpp b/src/image/SkImage_GpuBase.cpp
index eb1a73c..8e892ea 100644
--- a/src/image/SkImage_GpuBase.cpp
+++ b/src/image/SkImage_GpuBase.cpp
@@ -223,10 +223,6 @@
      * multiple SkImages. The created GrTexture is given a key based on a unique ID associated with
      * the SkPromiseImageTexture.
      *
-     * The GrTexutre idle proc mechanism is used to call the Release and Done procs. We use this
-     * instead of the GrSurface release proc because the GrTexture is cached and therefore may
-     * outlive the proxy into which this callback is installed.
-     *
      * A key invalidation message is installed on the SkPromiseImageTexture so that the GrTexture
      * is deleted once it can no longer be used to instantiate a proxy.
      */
@@ -249,16 +245,8 @@
 
         ~PromiseLazyInstantiateCallback() {
             // Our destructor can run on any thread. We trigger the unref of fTexture by message.
-            // This unreffed texture pointer is a real problem! When the context has been
-            // abandoned, the GrTexture pointed to by this pointer is deleted! Due to virtual
-            // inheritance any manipulation of this pointer at that point will cause a crash.
-            // For now we "work around" the problem by just passing it, untouched, into the
-            // message bus but this very fragile.
-            // In the future the GrSurface class hierarchy refactoring should eliminate this
-            // difficulty by removing the virtual inheritance.
             if (fTexture) {
-                GrTextureFreedMessage msg { fTexture, fTextureContextID };
-                SkMessageBus<GrTextureFreedMessage, GrDirectContext::DirectContextID>::Post(msg);
+                GrResourceCache::ReturnResourceFromThread(std::move(fTexture), fTextureContextID);
             }
         }
 
@@ -277,7 +265,7 @@
             // Our proxy is getting instantiated for the second+ time. We are only allowed to call
             // Fulfill once. So return our cached result.
             if (fTexture) {
-                return {sk_ref_sp(fTexture), kReleaseCallbackOnInstantiation, kKeySyncMode};
+                return {fTexture, kReleaseCallbackOnInstantiation, kKeySyncMode};
             } else if (fFulfillProcFailed) {
                 // We've already called fulfill and it failed. Our contract says that we should only
                 // call each callback once.
@@ -297,29 +285,23 @@
                 return {};
             }
 
-            sk_sp<GrTexture> tex = resourceProvider->wrapBackendTexture(backendTexture,
-                                                                        kBorrow_GrWrapOwnership,
-                                                                        GrWrapCacheable::kNo,
-                                                                        kRead_GrIOType);
-            if (!tex) {
+            fTexture = resourceProvider->wrapBackendTexture(backendTexture,
+                                                            kBorrow_GrWrapOwnership,
+                                                            GrWrapCacheable::kNo,
+                                                            kRead_GrIOType);
+            if (!fTexture) {
                 return {};
             }
-            tex->setRelease(fReleaseHelper);
-            fTexture = tex.get();
-            // We need to hold on to the GrTexture in case our proxy gets reinstantiated. However,
-            // we can't unref in our destructor because we may be on another thread then. So we
-            // let the cache know it is waiting on an unref message. We will send that message from
-            // our destructor.
+            fTexture->setRelease(fReleaseHelper);
             auto dContext = fTexture->getContext();
-            dContext->priv().getResourceCache()->insertDelayedTextureUnref(fTexture);
             fTextureContextID = dContext->directContextID();
-            return {std::move(tex), kReleaseCallbackOnInstantiation, kKeySyncMode};
+            return {fTexture, kReleaseCallbackOnInstantiation, kKeySyncMode};
         }
 
     private:
         PromiseImageTextureFulfillProc fFulfillProc;
         sk_sp<skgpu::RefCntedCallback> fReleaseHelper;
-        GrTexture* fTexture = nullptr;
+        sk_sp<GrTexture> fTexture;
         GrDirectContext::DirectContextID fTextureContextID;
         bool fFulfillProcFailed = false;
     } callback(fulfillProc, std::move(releaseHelper));
diff --git a/tests/ResourceCacheTest.cpp b/tests/ResourceCacheTest.cpp
index 44e3e3b..0924d85 100644
--- a/tests/ResourceCacheTest.cpp
+++ b/tests/ResourceCacheTest.cpp
@@ -1567,7 +1567,7 @@
     GrGpu* gpu = mock.gpu();
 
     GrBackendTexture backends[3];
-    GrTexture* wrapped[3];
+    sk_sp<GrTexture> wrapped[3];
     int freed[3] = { 0, 0, 0 };
 
     auto releaseProc = [](void* ctx) {
@@ -1581,26 +1581,10 @@
         wrapped[i] = gpu->wrapBackendTexture(backends[i],
                                              GrWrapOwnership::kBorrow_GrWrapOwnership,
                                              (i < 2) ? GrWrapCacheable::kYes : GrWrapCacheable::kNo,
-                                             GrIOType::kRead_GrIOType)
-                             .release();
+                                             GrIOType::kRead_GrIOType);
         wrapped[i]->setRelease(releaseProc, &freed[i]);
     }
 
-    cache->insertDelayedTextureUnref(wrapped[0]);
-    cache->insertDelayedTextureUnref(wrapped[1]);
-
-    // An uncacheable cross-context should not be purged as soon as we drop our ref. This
-    // is because inserting it as a cross-context resource actually holds a ref until the
-    // message is received.
-    cache->insertDelayedTextureUnref(wrapped[2]);
-
-    REPORTER_ASSERT(reporter, 0 == (freed[0] + freed[1] + freed[2]));
-
-    // Have only ref waiting on message.
-    wrapped[0]->unref();
-    wrapped[1]->unref();
-    wrapped[2]->unref();
-
     REPORTER_ASSERT(reporter, 0 == (freed[0] + freed[1] + freed[2]));
 
     // This should free nothing since no messages were sent.
@@ -1609,20 +1593,20 @@
     REPORTER_ASSERT(reporter, 0 == (freed[0] + freed[1] + freed[2]));
 
     // Send message to free the first resource
-    GrTextureFreedMessage msg1{wrapped[0], dContext->directContextID()};
-    SkMessageBus<GrTextureFreedMessage, GrDirectContext::DirectContextID>::Post(msg1);
+    GrResourceCache::ReturnResourceFromThread(std::move(wrapped[0]), dContext->directContextID());
     cache->purgeAsNeeded();
 
     REPORTER_ASSERT(reporter, 1 == (freed[0] + freed[1] + freed[2]));
     REPORTER_ASSERT(reporter, 1 == freed[0]);
 
-    GrTextureFreedMessage msg2{wrapped[2], dContext->directContextID()};
-    SkMessageBus<GrTextureFreedMessage, GrDirectContext::DirectContextID>::Post(msg2);
+    GrResourceCache::ReturnResourceFromThread(std::move(wrapped[2]), dContext->directContextID());
     cache->purgeAsNeeded();
 
     REPORTER_ASSERT(reporter, 2 == (freed[0] + freed[1] + freed[2]));
     REPORTER_ASSERT(reporter, 0 == freed[1]);
 
+    wrapped[1].reset();
+
     mock.reset();
 
     REPORTER_ASSERT(reporter, 3 == (freed[0] + freed[1] + freed[2]));
@@ -1656,16 +1640,14 @@
 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ResourceMessagesAfterAbandon, reporter, ctxInfo) {
     auto dContext = ctxInfo.directContext();
     GrGpu* gpu = dContext->priv().getGpu();
-    GrResourceCache* cache = dContext->priv().getResourceCache();
 
     GrBackendTexture backend = dContext->createBackendTexture(16, 16,
                                                               SkColorType::kRGBA_8888_SkColorType,
                                                               GrMipmapped::kNo, GrRenderable::kNo);
-    GrTexture* tex = gpu->wrapBackendTexture(backend,
-                                             GrWrapOwnership::kBorrow_GrWrapOwnership,
-                                             GrWrapCacheable::kYes,
-                                             GrIOType::kRead_GrIOType)
-                             .release();
+    sk_sp<GrTexture> tex = gpu->wrapBackendTexture(backend,
+                                                   GrWrapOwnership::kBorrow_GrWrapOwnership,
+                                                   GrWrapCacheable::kYes,
+                                                   GrIOType::kRead_GrIOType);
 
     auto releaseProc = [](void* ctx) {
         int* index = (int*) ctx;
@@ -1676,11 +1658,6 @@
 
     tex->setRelease(releaseProc, &freed);
 
-    cache->insertDelayedTextureUnref(tex);
-
-    // Now only the cache is holding a ref to this texture
-    tex->unref();
-
     REPORTER_ASSERT(reporter, 0 == freed);
 
     // We must delete the backend texture before abandoning the context in vulkan. We just do it
@@ -1691,9 +1668,9 @@
     REPORTER_ASSERT(reporter, 1 == freed);
 
     // In the past, creating this message could cause an exception due to
-    // an un-safe downcast from GrTexture to GrGpuResource
-    GrTextureFreedMessage msg{tex, dContext->directContextID()};
-    SkMessageBus<GrTextureFreedMessage, GrDirectContext::DirectContextID>::Post(msg);
+    // an un-safe pointer upcast from GrTexture* to GrGpuResource* through virtual inheritance
+    // after deletion of tex.
+    GrResourceCache::ReturnResourceFromThread(std::move(tex), dContext->directContextID());
 
     // This doesn't actually do anything but it does trigger us to read messages
     dContext->purgeUnlockedResources(false);