Reland "Reintroduce deinstantiate lazy proxy types and use for promise images."

This is a reland of 8b40ac35b2ec31a61c751e610b891681df272283

Original change's description:
> Reintroduce deinstantiate lazy proxy types and use for promise images.
> 
> This reverts a fraction of b2c5dae65df952999f0a12b9df80bc1433ffa19a to
> restore the deinstantiate lazy proxy type, supporting implementation,
> and tests.
> 
> Use them for promise images to avoid thread safety issues for promise
> image resources. Makes promise image instantiation callbacks do a thread
> safe unref of their fulfilled GrTexture in GrResourceCache. The
> GrResourceCache mechanism for receiving unref messages is extended to
> allow multiple pending unrefs. All this is new.
> 
> 
> Bug: skia:8800
> Change-Id: I7b1d4fea13c053b6fbbd39c0c6eaf567b8bf81f1
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/199002
> Reviewed-by: Brian Salomon <bsalomon@google.com>
> Reviewed-by: Robert Phillips <robertphillips@google.com>
> Commit-Queue: Brian Salomon <bsalomon@google.com>

Bug: skia:8800
Change-Id: Ib939fc5c19edf0c6b965c9f6adf0afedd4267703
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/201220
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/gn/gpu.gni b/gn/gpu.gni
index 8c98928..942c400 100644
--- a/gn/gpu.gni
+++ b/gn/gpu.gni
@@ -88,6 +88,8 @@
   "$_src/gpu/GrDefaultGeoProcFactory.h",
   "$_src/gpu/GrDeferredProxyUploader.h",
   "$_src/gpu/GrDeferredUpload.h",
+  "$_src/gpu/GrDeinstantiateProxyTracker.cpp",
+  "$_src/gpu/GrDeinstantiateProxyTracker.h",
   "$_src/gpu/GrDistanceFieldGenFromVector.cpp",
   "$_src/gpu/GrDistanceFieldGenFromVector.h",
   "$_src/gpu/GrDrawingManager.cpp",
diff --git a/include/gpu/GrTexture.h b/include/gpu/GrTexture.h
index 4a7935f..e20ebb0 100644
--- a/include/gpu/GrTexture.h
+++ b/include/gpu/GrTexture.h
@@ -79,7 +79,6 @@
     }
 
 private:
-
     void computeScratchKey(GrScratchKey*) const override;
     size_t onGpuMemorySize() const override;
     void markMipMapsDirty();
diff --git a/include/private/GrSurfaceProxy.h b/include/private/GrSurfaceProxy.h
index 7c572bb..c9eec6c 100644
--- a/include/private/GrSurfaceProxy.h
+++ b/include/private/GrSurfaceProxy.h
@@ -208,6 +208,8 @@
     enum class LazyInstantiationType {
         kSingleUse,      // Instantiation callback is allowed to be called only once.
         kMultipleUse,    // Instantiation callback can be called multiple times.
+        kDeinstantiate,  // Instantiation callback can be called multiple times,
+                         // but we will deinstantiate the proxy after every flush.
     };
 
     enum class LazyState {
diff --git a/src/gpu/GrBackendTextureImageGenerator.cpp b/src/gpu/GrBackendTextureImageGenerator.cpp
index e184e8e..1bba019 100644
--- a/src/gpu/GrBackendTextureImageGenerator.cpp
+++ b/src/gpu/GrBackendTextureImageGenerator.cpp
@@ -42,7 +42,7 @@
     // 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.
-    context->priv().getResourceCache()->insertCrossContextGpuResource(texture.get());
+    context->priv().getResourceCache()->insertDelayedResourceUnref(texture.get());
 
     GrBackendTexture backendTexture = texture->getBackendTexture();
     GrBackendFormat backendFormat = backendTexture.getBackendFormat();
diff --git a/src/gpu/GrDeinstantiateProxyTracker.cpp b/src/gpu/GrDeinstantiateProxyTracker.cpp
new file mode 100644
index 0000000..9870617
--- /dev/null
+++ b/src/gpu/GrDeinstantiateProxyTracker.cpp
@@ -0,0 +1,32 @@
+/*
+ * 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
new file mode 100644
index 0000000..2555ab1
--- /dev/null
+++ b/src/gpu/GrDeinstantiateProxyTracker.h
@@ -0,0 +1,29 @@
+/*
+ * 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 b6d45ff..10da8f9 100644
--- a/src/gpu/GrDrawingManager.cpp
+++ b/src/gpu/GrDrawingManager.cpp
@@ -285,7 +285,7 @@
     bool flushed = false;
 
     {
-        GrResourceAllocator alloc(resourceProvider);
+        GrResourceAllocator alloc(resourceProvider, flushState.deinstantiateProxyTracker());
         for (int i = 0; i < fDAG.numOpLists(); ++i) {
             if (fDAG.opList(i)) {
                 fDAG.opList(i)->gatherProxyIntervals(&alloc);
@@ -336,13 +336,20 @@
     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();
+        flushed = false;
     }
     for (GrOnFlushCallbackObject* onFlushCBObject : fOnFlushCBObjects) {
         onFlushCBObject->postFlush(fTokenTracker.nextTokenToFlush(), fFlushingOpListIDs.begin(),
                                    fFlushingOpListIDs.count());
+        flushed = true;
+    }
+    if (flushed) {
+        resourceCache->purgeAsNeeded();
     }
     fFlushingOpListIDs.reset();
     fFlushing = false;
diff --git a/src/gpu/GrOpFlushState.h b/src/gpu/GrOpFlushState.h
index bc10920..07983d4 100644
--- a/src/gpu/GrOpFlushState.h
+++ b/src/gpu/GrOpFlushState.h
@@ -12,6 +12,7 @@
 #include "GrAppliedClip.h"
 #include "GrBufferAllocPool.h"
 #include "GrDeferredUpload.h"
+#include "GrDeinstantiateProxyTracker.h"
 #include "GrRenderTargetProxy.h"
 #include "SkArenaAlloc.h"
 #include "SkArenaAllocList.h"
@@ -108,6 +109,8 @@
     // permissible).
     GrAtlasManager* atlasManager() const final;
 
+    GrDeinstantiateProxyTracker* deinstantiateProxyTracker() { return &fDeinstantiateProxyTracker; }
+
 private:
     /** GrMeshDrawOp::Target override. */
     SkArenaAlloc* allocator() override { return &fArena; }
@@ -161,6 +164,9 @@
     // 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/GrResourceAllocator.cpp b/src/gpu/GrResourceAllocator.cpp
index 81c2709..0d3c34e 100644
--- a/src/gpu/GrResourceAllocator.cpp
+++ b/src/gpu/GrResourceAllocator.cpp
@@ -7,6 +7,7 @@
 
 #include "GrResourceAllocator.h"
 
+#include "GrDeinstantiateProxyTracker.h"
 #include "GrGpuResourcePriv.h"
 #include "GrOpList.h"
 #include "GrRenderTargetProxy.h"
@@ -106,7 +107,12 @@
     if (proxy->readOnly() || !fResourceProvider->explicitlyAllocateGPUResources()) {
         // FIXME: remove this once we can do the lazy instantiation from assign instead.
         if (GrSurfaceProxy::LazyState::kNot != proxy->lazyInstantiationState()) {
-            proxy->priv().doLazyInstantiation(fResourceProvider);
+            if (proxy->priv().doLazyInstantiation(fResourceProvider)) {
+                if (proxy->priv().lazyInstantiationType() ==
+                    GrSurfaceProxy::LazyInstantiationType::kDeinstantiate) {
+                    fDeinstantiateTracker->addProxy(proxy);
+                }
+            }
         }
     }
 }
@@ -369,6 +375,11 @@
         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 cb366e6..ea1250f 100644
--- a/src/gpu/GrResourceAllocator.h
+++ b/src/gpu/GrResourceAllocator.h
@@ -16,6 +16,7 @@
 #include "SkTDynamicHash.h"
 #include "SkTMultiMap.h"
 
+class GrDeinstantiateProxyTracker;
 class GrResourceProvider;
 
 // Print out explicit allocation information
@@ -41,8 +42,8 @@
  */
 class GrResourceAllocator {
 public:
-    GrResourceAllocator(GrResourceProvider* resourceProvider)
-            : fResourceProvider(resourceProvider) {}
+    GrResourceAllocator(GrResourceProvider* resourceProvider, GrDeinstantiateProxyTracker* tracker)
+            : fResourceProvider(resourceProvider), fDeinstantiateTracker(tracker) {}
 
     ~GrResourceAllocator();
 
@@ -211,6 +212,7 @@
     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/GrResourceCache.cpp b/src/gpu/GrResourceCache.cpp
index 9b17d92..c36634c 100644
--- a/src/gpu/GrResourceCache.cpp
+++ b/src/gpu/GrResourceCache.cpp
@@ -14,6 +14,7 @@
 #include "GrTexture.h"
 #include "GrTextureProxyCacheAccess.h"
 #include "GrTracing.h"
+#include "SkExchange.h"
 #include "SkGr.h"
 #include "SkMessageBus.h"
 #include "SkOpts.h"
@@ -67,7 +68,44 @@
     GrResourceCache* fCache;
 };
 
- //////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+inline GrResourceCache::ResourceAwaitingUnref::ResourceAwaitingUnref() = default;
+
+inline GrResourceCache::ResourceAwaitingUnref::ResourceAwaitingUnref(GrGpuResource* resource)
+        : fResource(resource), fNumUnrefs(1) {}
+
+inline GrResourceCache::ResourceAwaitingUnref::ResourceAwaitingUnref(ResourceAwaitingUnref&& that) {
+    fResource = skstd::exchange(that.fResource, nullptr);
+    fNumUnrefs = skstd::exchange(that.fNumUnrefs, 0);
+}
+
+inline GrResourceCache::ResourceAwaitingUnref& GrResourceCache::ResourceAwaitingUnref::operator=(
+        ResourceAwaitingUnref&& that) {
+    fResource = skstd::exchange(that.fResource, nullptr);
+    fNumUnrefs = skstd::exchange(that.fNumUnrefs, 0);
+    return *this;
+}
+
+inline GrResourceCache::ResourceAwaitingUnref::~ResourceAwaitingUnref() {
+    if (fResource) {
+        for (int i = 0; i < fNumUnrefs; ++i) {
+            fResource->unref();
+        }
+    }
+}
+
+inline void GrResourceCache::ResourceAwaitingUnref::addRef() { ++fNumUnrefs; }
+
+inline void GrResourceCache::ResourceAwaitingUnref::unref() {
+    SkASSERT(fNumUnrefs > 0);
+    fResource->unref();
+    --fNumUnrefs;
+}
+
+inline bool GrResourceCache::ResourceAwaitingUnref::finished() { return !fNumUnrefs; }
+
+//////////////////////////////////////////////////////////////////////////////
 
 GrResourceCache::GrResourceCache(const GrCaps* caps, GrSingleOwner* singleOwner,
                                  uint32_t contextUniqueID)
@@ -179,10 +217,9 @@
 void GrResourceCache::abandonAll() {
     AutoValidate av(this);
 
-    for (int i = 0; i < fResourcesWaitingForFreeMsg.count(); ++i) {
-        fResourcesWaitingForFreeMsg[i]->cacheAccess().abandon();
-    }
-    fResourcesWaitingForFreeMsg.reset();
+    // We need to make sure to free any resources that were waiting on a free message but never
+    // received one.
+    fResourcesAwaitingUnref.reset();
 
     while (fNonpurgeableResources.count()) {
         GrGpuResource* back = *(fNonpurgeableResources.end() - 1);
@@ -204,7 +241,7 @@
     SkASSERT(!fBudgetedCount);
     SkASSERT(!fBudgetedBytes);
     SkASSERT(!fPurgeableBytes);
-    SkASSERT(!fResourcesWaitingForFreeMsg.count());
+    SkASSERT(!fResourcesAwaitingUnref.count());
 }
 
 void GrResourceCache::releaseAll() {
@@ -214,10 +251,7 @@
 
     // We need to make sure to free any resources that were waiting on a free message but never
     // received one.
-    for (int i = 0; i < fResourcesWaitingForFreeMsg.count(); ++i) {
-        fResourcesWaitingForFreeMsg[i]->unref();
-    }
-    fResourcesWaitingForFreeMsg.reset();
+    fResourcesAwaitingUnref.reset();
 
     SkASSERT(fProxyProvider); // better have called setProxyProvider
     // We must remove the uniqueKeys from the proxies here. While they possess a uniqueKey
@@ -244,7 +278,7 @@
     SkASSERT(!fBudgetedCount);
     SkASSERT(!fBudgetedBytes);
     SkASSERT(!fPurgeableBytes);
-    SkASSERT(!fResourcesWaitingForFreeMsg.count());
+    SkASSERT(!fResourcesAwaitingUnref.count());
 }
 
 class GrResourceCache::AvailableForScratchUse {
@@ -605,10 +639,14 @@
     }
 }
 
-void GrResourceCache::insertCrossContextGpuResource(GrGpuResource* resource) {
+void GrResourceCache::insertDelayedResourceUnref(GrGpuResource* resource) {
     resource->ref();
-    SkASSERT(!fResourcesWaitingForFreeMsg.contains(resource));
-    fResourcesWaitingForFreeMsg.push_back(resource);
+    uint32_t id = resource->uniqueID().asUInt();
+    if (auto* data = fResourcesAwaitingUnref.find(id)) {
+        data->addRef();
+    } else {
+        fResourcesAwaitingUnref.set(id, {resource});
+    }
 }
 
 void GrResourceCache::processFreedGpuResources() {
@@ -616,14 +654,17 @@
     fFreedGpuResourceInbox.poll(&msgs);
     for (int i = 0; i < msgs.count(); ++i) {
         SkASSERT(msgs[i].fOwningUniqueID == fContextUniqueID);
-        int index = fResourcesWaitingForFreeMsg.find(msgs[i].fResource);
+        uint32_t id = msgs[i].fResource->uniqueID().asUInt();
+        ResourceAwaitingUnref* info = fResourcesAwaitingUnref.find(id);
         // If we called release or abandon on the GrContext we will have already released our ref on
         // the GrGpuResource. If then the message arrives before the actual GrContext gets destroyed
         // we will try to process the message when we destroy the GrContext. This protects us from
         // trying to unref the resource twice.
-        if (index != -1) {
-            fResourcesWaitingForFreeMsg.removeShuffle(index);
-            msgs[i].fResource->unref();
+        if (info) {
+            info->unref();
+            if (info->finished()) {
+                fResourcesAwaitingUnref.remove(id);
+            }
         }
     }
 }
diff --git a/src/gpu/GrResourceCache.h b/src/gpu/GrResourceCache.h
index c948864..84378e6 100644
--- a/src/gpu/GrResourceCache.h
+++ b/src/gpu/GrResourceCache.h
@@ -16,6 +16,7 @@
 #include "SkRefCnt.h"
 #include "SkTArray.h"
 #include "SkTDPQueue.h"
+#include "SkTHash.h"
 #include "SkTInternalLList.h"
 #include "SkTMultiMap.h"
 
@@ -195,7 +196,7 @@
     bool requestsFlush() const { return this->overBudget() && !fPurgeableQueue.count(); }
 
     /** Maintain a ref to this resource until we receive a GrGpuResourceFreedMessage. */
-    void insertCrossContextGpuResource(GrGpuResource* resource);
+    void insertDelayedResourceUnref(GrGpuResource* resource);
 
 #if GR_CACHE_STATS
     struct Stats {
@@ -305,6 +306,25 @@
     };
     typedef SkTDynamicHash<GrGpuResource, GrUniqueKey, UniqueHashTraits> UniqueHash;
 
+    class ResourceAwaitingUnref {
+    public:
+        ResourceAwaitingUnref();
+        ResourceAwaitingUnref(GrGpuResource* resource);
+        ResourceAwaitingUnref(const ResourceAwaitingUnref&) = delete;
+        ResourceAwaitingUnref& operator=(const ResourceAwaitingUnref&) = delete;
+        ResourceAwaitingUnref(ResourceAwaitingUnref&&);
+        ResourceAwaitingUnref& operator=(ResourceAwaitingUnref&&);
+        ~ResourceAwaitingUnref();
+        void addRef();
+        void unref();
+        bool finished();
+
+    private:
+        GrGpuResource* fResource = nullptr;
+        int fNumUnrefs = 0;
+    };
+    using ReourcesAwaitingUnref = SkTHashMap<uint32_t, ResourceAwaitingUnref>;
+
     static bool CompareTimestamp(GrGpuResource* const& a, GrGpuResource* const& b) {
         return a->cacheAccess().timestamp() < b->cacheAccess().timestamp();
     }
@@ -353,8 +373,7 @@
 
     InvalidUniqueKeyInbox               fInvalidUniqueKeyInbox;
     FreedGpuResourceInbox               fFreedGpuResourceInbox;
-
-    SkTDArray<GrGpuResource*>           fResourcesWaitingForFreeMsg;
+    ReourcesAwaitingUnref               fResourcesAwaitingUnref;
 
     uint32_t                            fContextUniqueID;
     GrSingleOwner*                      fSingleOwner;
diff --git a/src/gpu/GrSurfaceProxyPriv.h b/src/gpu/GrSurfaceProxyPriv.h
index 8fe7f83..3b383a5 100644
--- a/src/gpu/GrSurfaceProxyPriv.h
+++ b/src/gpu/GrSurfaceProxyPriv.h
@@ -60,6 +60,11 @@
         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/image/SkImage_GpuBase.cpp b/src/image/SkImage_GpuBase.cpp
index ec2e651..908be47 100644
--- a/src/image/SkImage_GpuBase.cpp
+++ b/src/image/SkImage_GpuBase.cpp
@@ -445,12 +445,23 @@
 
         ~PromiseLazyInstantiateCallback() {
             if (fIdleCallback) {
+                SkASSERT(!fTexture);
                 // We were never fulfilled. Pass false so done proc is still called.
                 fIdleCallback->abandon();
             }
+            // Our destructor can run on any thread. We trigger the unref of fTexture by message.
+            if (fTexture) {
+                SkASSERT(!fIdleCallback);
+                SkMessageBus<GrGpuResourceFreedMessage>::Post({fTexture, fTextureContextID});
+            }
         }
 
         sk_sp<GrSurface> operator()(GrResourceProvider* resourceProvider) {
+            // 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);
+            }
             SkASSERT(fIdleCallback);
             PromiseImageTextureContext textureContext = fIdleCallback->context();
             sk_sp<SkPromiseImageTexture> promiseTexture = fFulfillProc(textureContext);
@@ -495,10 +506,20 @@
             }
             tex->addIdleProc(std::move(fIdleCallback));
             promiseTexture->addKeyToInvalidate(tex->getContext()->priv().contextID(), key);
+            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.
+            GrContext* context = fTexture->getContext();
+            context->priv().getResourceCache()->insertDelayedResourceUnref(fTexture);
+            fTextureContextID = context->priv().contextID();
             return std::move(tex);
         }
 
     private:
+        GrTexture* fTexture = nullptr;
+        uint32_t fTextureContextID = SK_InvalidUniqueID;
         sk_sp<GrRefCntedCallback> fIdleCallback;
         PromiseImageTextureFulfillProc fFulfillProc;
         GrPixelConfig fConfig;
@@ -515,5 +536,5 @@
     return proxyProvider->createLazyProxy(std::move(callback), backendFormat, desc, origin,
                                           mipMapped, GrInternalSurfaceFlags::kReadOnly,
                                           SkBackingFit::kExact, SkBudgeted::kNo,
-                                          GrSurfaceProxy::LazyInstantiationType::kSingleUse);
+                                          GrSurfaceProxy::LazyInstantiationType::kDeinstantiate);
 }
diff --git a/tests/GrSurfaceTest.cpp b/tests/GrSurfaceTest.cpp
index ce12da6..375d34c 100644
--- a/tests/GrSurfaceTest.cpp
+++ b/tests/GrSurfaceTest.cpp
@@ -488,6 +488,28 @@
                 // Now that the draw is fully consumed by the GPU, the texture should be idle.
                 REPORTER_ASSERT(reporter, idleIDs.find(2) != idleIDs.end());
 
+                // Make a proxy that should deinstantiate even if we keep a ref on it.
+                auto deinstantiateLazyCB = [&make, &context](GrResourceProvider* rp) {
+                    return make(context, 3);
+                };
+                proxy = context->priv().proxyProvider()->createLazyProxy(
+                        deinstantiateLazyCB, backendFormat, desc,
+                        GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin, GrMipMapped::kNo,
+                        GrInternalSurfaceFlags ::kNone, SkBackingFit::kExact, budgeted,
+                        GrSurfaceProxy::LazyInstantiationType::kDeinstantiate);
+                rtc->drawTexture(GrNoClip(), std::move(proxy), GrSamplerState::Filter::kNearest,
+                                 SkBlendMode::kSrcOver, SkPMColor4f(), SkRect::MakeWH(w, h),
+                                 SkRect::MakeWH(w, h), GrAA::kNo, GrQuadAAFlags::kNone,
+                                 SkCanvas::kFast_SrcRectConstraint, SkMatrix::I(), nullptr);
+                // At this point the proxy shouldn't even be instantiated, there is no texture with
+                // id 3.
+                REPORTER_ASSERT(reporter, idleIDs.find(3) == idleIDs.end());
+                context->flush();
+                context->priv().getGpu()->testingOnly_flushGpuAndSync();
+                // Now that the draw is fully consumed, we should have deinstantiated the proxy and
+                // the texture it made should be idle.
+                REPORTER_ASSERT(reporter, idleIDs.find(3) != idleIDs.end());
+
                 // Make sure we make the call during various shutdown scenarios where the texture
                 // might persist after context is destroyed, abandoned, etc. We test three
                 // variations of each scenario. One where the texture is just created. Another,
@@ -501,7 +523,7 @@
                 if (api == GrBackendApi::kVulkan) {
                     continue;
                 }
-                int id = 3;
+                int id = 4;
                 enum class DrawType {
                     kNoDraw,
                     kDraw,
diff --git a/tests/LazyProxyTest.cpp b/tests/LazyProxyTest.cpp
index f3b90e9..91f7335 100644
--- a/tests/LazyProxyTest.cpp
+++ b/tests/LazyProxyTest.cpp
@@ -249,8 +249,9 @@
 
     using LazyInstantiationType = GrSurfaceProxy::LazyInstantiationType;
     for (bool doInstantiate : {true, false}) {
-        for (auto lazyType :
-             {LazyInstantiationType::kSingleUse, LazyInstantiationType::kMultipleUse}) {
+        for (auto lazyType : {LazyInstantiationType::kSingleUse,
+                              LazyInstantiationType::kMultipleUse,
+                              LazyInstantiationType::kDeinstantiate}) {
             int testCount = 0;
             // Sets an integer to 1 when the callback is called and -1 when it is deleted.
             class TestCallback {
@@ -454,7 +455,7 @@
                 ctx->priv().caps()->getBackendFormatFromColorType(kRGBA_8888_SkColorType);
 
     using LazyType = GrSurfaceProxy::LazyInstantiationType;
-    for (auto lazyType : {LazyType::kSingleUse, LazyType::kMultipleUse}) {
+    for (auto lazyType : {LazyType::kSingleUse, LazyType::kMultipleUse, LazyType::kDeinstantiate}) {
         sk_sp<GrRenderTargetContext> rtc = ctx->priv().makeDeferredRenderTargetContext(
                 format, SkBackingFit::kExact, 100, 100,
                 kRGBA_8888_GrPixelConfig, nullptr);
@@ -497,7 +498,11 @@
         ctx->flush();
 
         REPORTER_ASSERT(reporter, 1 == instantiateTestValue);
-        REPORTER_ASSERT(reporter, 0 == releaseTestValue);
+        if (LazyType::kDeinstantiate == lazyType) {
+            REPORTER_ASSERT(reporter, 1 == releaseTestValue);
+        } else {
+            REPORTER_ASSERT(reporter, 0 == releaseTestValue);
+        }
 
         // This should cause the uninstantiate proxies to be instantiated again but have no effect
         // on the others
@@ -506,11 +511,20 @@
         rtc->priv().testingOnly_addDrawOp(LazyDeinstantiateTestOp::Make(ctx.get(), lazyProxy));
         ctx->flush();
 
-        REPORTER_ASSERT(reporter, 1 == instantiateTestValue);
-        REPORTER_ASSERT(reporter, 0 == releaseTestValue);
+        if (LazyType::kDeinstantiate == lazyType) {
+            REPORTER_ASSERT(reporter, 2 == instantiateTestValue);
+            REPORTER_ASSERT(reporter, 2 == releaseTestValue);
+        } else {
+            REPORTER_ASSERT(reporter, 1 == instantiateTestValue);
+            REPORTER_ASSERT(reporter, 0 == releaseTestValue);
+        }
 
         lazyProxy.reset();
-        REPORTER_ASSERT(reporter, 1 == releaseTestValue);
+        if (LazyType::kDeinstantiate == lazyType) {
+            REPORTER_ASSERT(reporter, 2 == releaseTestValue);
+        } else {
+            REPORTER_ASSERT(reporter, 1 == releaseTestValue);
+        }
 
         gpu->deleteTestingOnlyBackendTexture(backendTex);
     }
diff --git a/tests/PromiseImageTest.cpp b/tests/PromiseImageTest.cpp
index 93a09ca..789cb24 100644
--- a/tests/PromiseImageTest.cpp
+++ b/tests/PromiseImageTest.cpp
@@ -331,6 +331,13 @@
         auto surf = ctx->priv().resourceProvider()->findByUniqueKey<GrSurface>(key);
         REPORTER_ASSERT(reporter, !surf);
     }
+
+    // Must do this to ensure all callbacks occur before the PromiseImageChecker goes out of scope.
+    alphaImg.reset();
+    grayImg.reset();
+    ctx->flush();
+    ctx->priv().getGpu()->testingOnly_flushGpuAndSync();
+
     gpu->deleteTestingOnlyBackendTexture(backendTex1);
 }
 
@@ -461,7 +468,10 @@
     surface->flush();
     canvas->drawImage(image, 5, 0);
     surface->flush();
-    // Must call this to ensure that all callbacks are performed before the checker is destroyed.
+    // Must call these to ensure that all callbacks are performed before the checker is destroyed.
+    image.reset();
+    ctx->flush();
     gpu->testingOnly_flushGpuAndSync();
+
     gpu->deleteTestingOnlyBackendTexture(backendTex);
 }
diff --git a/tests/ResourceAllocatorTest.cpp b/tests/ResourceAllocatorTest.cpp
index 69118fc..a71221d 100644
--- a/tests/ResourceAllocatorTest.cpp
+++ b/tests/ResourceAllocatorTest.cpp
@@ -10,6 +10,7 @@
 #include "Test.h"
 
 #include "GrContextPriv.h"
+#include "GrDeinstantiateProxyTracker.h"
 #include "GrGpu.h"
 #include "GrProxyProvider.h"
 #include "GrResourceAllocator.h"
@@ -89,7 +90,8 @@
 // assigned different GrSurfaces.
 static void overlap_test(skiatest::Reporter* reporter, GrResourceProvider* resourceProvider,
                          GrSurfaceProxy* p1, GrSurfaceProxy* p2, bool expectedResult) {
-    GrResourceAllocator alloc(resourceProvider);
+    GrDeinstantiateProxyTracker deinstantiateTracker;
+    GrResourceAllocator alloc(resourceProvider, &deinstantiateTracker);
 
     alloc.addInterval(p1, 0, 4);
     alloc.addInterval(p2, 1, 2);
@@ -111,7 +113,8 @@
 static void non_overlap_test(skiatest::Reporter* reporter, GrResourceProvider* resourceProvider,
                              GrSurfaceProxy* p1, GrSurfaceProxy* p2,
                              bool expectedResult) {
-    GrResourceAllocator alloc(resourceProvider);
+    GrDeinstantiateProxyTracker deinstantiateTracker;
+    GrResourceAllocator alloc(resourceProvider, &deinstantiateTracker);
 
     alloc.addInterval(p1, 0, 2);
     alloc.addInterval(p2, 3, 5);
@@ -285,7 +288,7 @@
 }
 
 sk_sp<GrSurfaceProxy> make_lazy(GrProxyProvider* proxyProvider, const GrCaps* caps,
-                                const ProxyParams& p) {
+                                const ProxyParams& p, bool deinstantiate) {
     GrColorType grCT = SkColorTypeToGrColorType(p.fColorType);
     GrPixelConfig config = GrColorTypeToPixelConfig(grCT, GrSRGBEncoded::kNo);
 
@@ -308,7 +311,8 @@
         }
     };
     const GrBackendFormat format = caps->getBackendFormatFromColorType(p.fColorType);
-    auto lazyType = GrSurfaceProxy::LazyInstantiationType ::kSingleUse;
+    auto lazyType = deinstantiate ? GrSurfaceProxy::LazyInstantiationType ::kDeinstantiate
+                                  : GrSurfaceProxy::LazyInstantiationType ::kSingleUse;
     GrInternalSurfaceFlags flags = GrInternalSurfaceFlags::kNone;
     return proxyProvider->createLazyProxy(callback, format, desc, p.fOrigin, GrMipMapped::kNo,
                                           flags, p.fFit, SkBudgeted::kNo, lazyType);
@@ -330,20 +334,28 @@
         rtParams.fIsRT = true;
         auto proxyProvider = context->priv().proxyProvider();
         auto caps = context->priv().caps();
-        auto p0 = make_lazy(proxyProvider, caps, texParams);
+        auto p0 = make_lazy(proxyProvider, caps, texParams, true);
+        auto p1 = make_lazy(proxyProvider, caps, texParams, false);
         texParams.fFit = rtParams.fFit = SkBackingFit::kApprox;
-        auto p1 = make_lazy(proxyProvider, caps, rtParams);
+        auto p2 = make_lazy(proxyProvider, caps, rtParams, true);
+        auto p3 = make_lazy(proxyProvider, caps, rtParams, false);
 
+        GrDeinstantiateProxyTracker deinstantiateTracker;
         {
-            GrResourceAllocator alloc(resourceProvider);
+            GrResourceAllocator alloc(resourceProvider, &deinstantiateTracker);
             alloc.addInterval(p0.get(), 0, 1);
             alloc.addInterval(p1.get(), 0, 1);
+            alloc.addInterval(p2.get(), 0, 1);
+            alloc.addInterval(p3.get(), 0, 1);
             alloc.markEndOfOpList(0);
             int startIndex, stopIndex;
             GrResourceAllocator::AssignError error;
             alloc.assign(&startIndex, &stopIndex, &error);
         }
-        REPORTER_ASSERT(reporter, p0->isInstantiated());
+        deinstantiateTracker.deinstantiateAllProxies();
+        REPORTER_ASSERT(reporter, !p0->isInstantiated());
         REPORTER_ASSERT(reporter, p1->isInstantiated());
+        REPORTER_ASSERT(reporter, !p2->isInstantiated());
+        REPORTER_ASSERT(reporter, p3->isInstantiated());
     }
 }
diff --git a/tests/ResourceCacheTest.cpp b/tests/ResourceCacheTest.cpp
index 9bd5415..a1af2b3 100644
--- a/tests/ResourceCacheTest.cpp
+++ b/tests/ResourceCacheTest.cpp
@@ -1545,18 +1545,18 @@
     GrGpu* gpu = context->priv().getGpu();
 
     TestResource* wrapped1 = TestResource::CreateWrapped(gpu, GrWrapCacheable::kYes);
-    cache->insertCrossContextGpuResource(wrapped1);
+    cache->insertDelayedResourceUnref(wrapped1);
 
     REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive());
 
     TestResource* wrapped2 = TestResource::CreateWrapped(gpu, GrWrapCacheable::kYes);
-    cache->insertCrossContextGpuResource(wrapped2);
+    cache->insertDelayedResourceUnref(wrapped2);
 
     // 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.
     TestResource* wrapped3 = TestResource::CreateWrapped(gpu, GrWrapCacheable::kNo);
-    cache->insertCrossContextGpuResource(wrapped3);
+    cache->insertDelayedResourceUnref(wrapped3);
 
     REPORTER_ASSERT(reporter, 3 == TestResource::NumAlive());