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/GrGpuResource.cpp b/src/gpu/GrGpuResource.cpp
index c9224be..02cc617 100644
--- a/src/gpu/GrGpuResource.cpp
+++ b/src/gpu/GrGpuResource.cpp
@@ -93,6 +93,17 @@
     this->setMemoryBacking(traceMemoryDump, resourceName);
 }
 
+bool GrGpuResource::isPurgeable() const {
+    // Resources in the kUnbudgetedCacheable state are never purgeable when they have a unique
+    // key. The key must be removed/invalidated to make them purgeable.
+    return !this->hasRefOrPendingIO() &&
+           !(fBudgetedType == GrBudgetedType::kUnbudgetedCacheable && fUniqueKey.isValid());
+}
+
+bool GrGpuResource::hasRefOrPendingIO() const {
+    return this->internalHasRef() || this->internalHasPendingIO();
+}
+
 SkString GrGpuResource::getResourceName() const {
     // Dump resource as "skia/gpu_resources/resource_#".
     SkString resourceName("skia/gpu_resources/resource_");
@@ -145,6 +156,8 @@
 }
 
 void GrGpuResource::notifyAllCntsAreZero(CntType lastCntTypeToReachZero) const {
+    GrGpuResource* mutableThis = const_cast<GrGpuResource*>(this);
+    mutableThis->removedLastRefOrPendingIO();
     if (this->wasDestroyed()) {
         // We've already been removed from the cache. Goodbye cruel world!
         delete this;
@@ -154,7 +167,6 @@
     // We should have already handled this fully in notifyRefCntIsZero().
     SkASSERT(kRef_CntType != lastCntTypeToReachZero);
 
-    GrGpuResource* mutableThis = const_cast<GrGpuResource*>(this);
     static const uint32_t kFlag =
         GrResourceCache::ResourceAccess::kAllCntsReachedZero_RefNotificationFlag;
     get_resource_cache(fGpu)->resourceAccess().notifyCntReachedZero(mutableThis, kFlag);
@@ -170,6 +182,7 @@
     uint32_t flags = GrResourceCache::ResourceAccess::kRefCntReachedZero_RefNotificationFlag;
     if (!this->internalHasPendingIO()) {
         flags |= GrResourceCache::ResourceAccess::kAllCntsReachedZero_RefNotificationFlag;
+        mutableThis->removedLastRefOrPendingIO();
     }
     get_resource_cache(fGpu)->resourceAccess().notifyCntReachedZero(mutableThis, flags);