Change the meaning of GrBudgetedType::kUnbudgetedUncacheable.

kUnbudgetedCacheable now means that the resource is never purged
until its unique key is removed.

This fixes an issue where a cached texture for a promise image
might get purged by cache pressure. This in turn could cause
Skia to call the promise image's Fulfill proc multiple times with
no intervening Release calls. The balancing Release calls would
occur, but the policy is that each Fulfill should be balanced by
Release *before* another Fulfill.

Update/add unit tests.

Bug: chromium:922851
Change-Id: I6411e413b3104721ca4bb6e7f07b3b73d14cbcf9
Reviewed-on: https://skia-review.googlesource.com/c/186361
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/src/gpu/vk/GrVkTexture.cpp b/src/gpu/vk/GrVkTexture.cpp
index bdd8db4..6e8fe67 100644
--- a/src/gpu/vk/GrVkTexture.cpp
+++ b/src/gpu/vk/GrVkTexture.cpp
@@ -120,10 +120,13 @@
 }
 
 void GrVkTexture::onRelease() {
-    // When there is an idle proc, the Resource will call the proc in releaseImage() so
-    // we clear it here.
-    fIdleProc = nullptr;
-    fIdleProcContext = nullptr;
+    // 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()) {
+        fIdleProc = nullptr;
+        fIdleProcContext = nullptr;
+    }
 
     // we create this and don't hand it off, so we should always destroy it
     if (fTextureView) {
@@ -137,10 +140,14 @@
 }
 
 void GrVkTexture::onAbandon() {
-    // When there is an idle proc, the Resource will call the proc in abandonImage() so
-    // we clear it here.
-    fIdleProc = nullptr;
-    fIdleProcContext = nullptr;
+    // 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()) {
+        fIdleProc = nullptr;
+        fIdleProcContext = nullptr;
+    }
+
     // we create this and don't hand it off, so we should always destroy it
     if (fTextureView) {
         fTextureView->unrefAndAbandon();
@@ -172,19 +179,20 @@
     }
 }
 
-void GrVkTexture::becamePurgeable() {
+void GrVkTexture::removedLastRefOrPendingIO() {
     if (!fIdleProc) {
         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->resource();
-    SkASSERT(resource);
-    if (resource->isOwnedByCommandBuffer()) {
+    auto* resource = this->hasResource() ? this->resource() : nullptr;
+    if (resource && resource->isOwnedByCommandBuffer()) {
         return;
     }
     fIdleProc(fIdleProcContext);
     fIdleProc = nullptr;
     fIdleProcContext = nullptr;
-    resource->setIdleProc(nullptr, nullptr, nullptr);
+    if (resource) {
+        resource->setIdleProc(nullptr, nullptr, nullptr);
+    }
 }