Add scratch-only version of performDeferredCleanup

Change-Id: I5707d1da1b69ab1ffaa77d7a391a187ac3e8eba1
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/417267
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/src/gpu/GrDirectContext.cpp b/src/gpu/GrDirectContext.cpp
index 3a2ae7e..36b5c52 100644
--- a/src/gpu/GrDirectContext.cpp
+++ b/src/gpu/GrDirectContext.cpp
@@ -193,7 +193,7 @@
 
     this->drawingManager()->freeGpuResources();
 
-    fResourceCache->purgeAllUnlocked();
+    fResourceCache->purgeUnlockedResources();
 }
 
 bool GrDirectContext::init() {
@@ -317,7 +317,8 @@
     fGpu->releaseUnlockedBackendObjects();
 }
 
-void GrDirectContext::performDeferredCleanup(std::chrono::milliseconds msNotUsed) {
+void GrDirectContext::performDeferredCleanup(std::chrono::milliseconds msNotUsed,
+                                             bool scratchResourcesOnly) {
     TRACE_EVENT0("skia.gpu", TRACE_FUNC);
 
     ASSERT_SINGLE_OWNER
@@ -331,7 +332,7 @@
     auto purgeTime = GrStdSteadyClock::now() - msNotUsed;
 
     fResourceCache->purgeAsNeeded();
-    fResourceCache->purgeResourcesNotUsedSince(purgeTime);
+    fResourceCache->purgeResourcesNotUsedSince(purgeTime, scratchResourcesOnly);
 
     // The textBlob Cache doesn't actually hold any GPU resource but this is a convenient
     // place to purge stale blobs
diff --git a/src/gpu/GrDirectContextPriv.cpp b/src/gpu/GrDirectContextPriv.cpp
index 66917a1..b00a1a2 100644
--- a/src/gpu/GrDirectContextPriv.cpp
+++ b/src/gpu/GrDirectContextPriv.cpp
@@ -185,10 +185,6 @@
                                    SkColorInfo(colorType, kPremul_SkAlphaType, nullptr));
 }
 
-void GrDirectContextPriv::testingOnly_purgeAllUnlockedResources() {
-    fContext->fResourceCache->purgeAllUnlocked();
-}
-
 void GrDirectContextPriv::testingOnly_flushAndRemoveOnFlushCallbackObject(
         GrOnFlushCallbackObject* cb) {
     fContext->flushAndSubmit();
diff --git a/src/gpu/GrDirectContextPriv.h b/src/gpu/GrDirectContextPriv.h
index fd205d0..fd8f9af 100644
--- a/src/gpu/GrDirectContextPriv.h
+++ b/src/gpu/GrDirectContextPriv.h
@@ -187,13 +187,6 @@
         if it gets cached or used more generally. */
     sk_sp<SkImage> testingOnly_getFontAtlasImage(GrMaskFormat format, unsigned int index = 0);
 
-    /**
-     * Purge all the unlocked resources from the cache.
-     * This entry point is mainly meant for timing texture uploads
-     * and is not defined in normal builds of Skia.
-     */
-    void testingOnly_purgeAllUnlockedResources();
-
     void testingOnly_flushAndRemoveOnFlushCallbackObject(GrOnFlushCallbackObject*);
 #endif
 
diff --git a/src/gpu/GrResourceCache.cpp b/src/gpu/GrResourceCache.cpp
index 64d470c..2a96973 100644
--- a/src/gpu/GrResourceCache.cpp
+++ b/src/gpu/GrResourceCache.cpp
@@ -564,19 +564,43 @@
     this->validate();
 }
 
-void GrResourceCache::purgeUnlockedResources(bool scratchResourcesOnly) {
+void GrResourceCache::purgeUnlockedResources(const GrStdSteadyClock::time_point* purgeTime,
+                                             bool scratchResourcesOnly) {
 
     if (!scratchResourcesOnly) {
-        fThreadSafeCache->dropUniqueRefs(nullptr);
+        if (purgeTime) {
+            fThreadSafeCache->dropUniqueRefsOlderThan(*purgeTime);
+        } else {
+            fThreadSafeCache->dropUniqueRefs(nullptr);
+        }
 
         // We could disable maintaining the heap property here, but it would add a lot of
         // complexity. Moreover, this is rarely called.
         while (fPurgeableQueue.count()) {
             GrGpuResource* resource = fPurgeableQueue.peek();
+
+            const GrStdSteadyClock::time_point resourceTime =
+                    resource->cacheAccess().timeWhenResourceBecamePurgeable();
+            if (purgeTime && resourceTime >= *purgeTime) {
+                // Resources were given both LRU timestamps and tagged with a frame number when
+                // they first became purgeable. The LRU timestamp won't change again until the
+                // resource is made non-purgeable again. So, at this point all the remaining
+                // resources in the timestamp-sorted queue will have a frame number >= to this
+                // one.
+                break;
+            }
+
             SkASSERT(resource->resourcePriv().isPurgeable());
             resource->cacheAccess().release();
         }
     } else {
+        // Early out if the very first item is too new to purge to avoid sorting the queue when
+        // nothing will be deleted.
+        if (purgeTime && fPurgeableQueue.count() &&
+            fPurgeableQueue.peek()->cacheAccess().timeWhenResourceBecamePurgeable() >= *purgeTime) {
+            return;
+        }
+
         // Sort the queue
         fPurgeableQueue.sort();
 
@@ -584,6 +608,13 @@
         SkTDArray<GrGpuResource*> scratchResources;
         for (int i = 0; i < fPurgeableQueue.count(); i++) {
             GrGpuResource* resource = fPurgeableQueue.at(i);
+
+            const GrStdSteadyClock::time_point resourceTime =
+                    resource->cacheAccess().timeWhenResourceBecamePurgeable();
+            if (purgeTime && resourceTime >= *purgeTime) {
+                // scratch or not, all later iterations will be too recently used to purge.
+                break;
+            }
             SkASSERT(resource->resourcePriv().isPurgeable());
             if (!resource->getUniqueKey().isValid()) {
                 *scratchResources.append() = resource;
@@ -600,26 +631,6 @@
     this->validate();
 }
 
-void GrResourceCache::purgeResourcesNotUsedSince(GrStdSteadyClock::time_point purgeTime) {
-    fThreadSafeCache->dropUniqueRefsOlderThan(purgeTime);
-
-    while (fPurgeableQueue.count()) {
-        const GrStdSteadyClock::time_point resourceTime =
-                fPurgeableQueue.peek()->cacheAccess().timeWhenResourceBecamePurgeable();
-        if (resourceTime >= purgeTime) {
-            // Resources were given both LRU timestamps and tagged with a frame number when
-            // they first became purgeable. The LRU timestamp won't change again until the
-            // resource is made non-purgeable again. So, at this point all the remaining
-            // resources in the timestamp-sorted queue will have a frame number >= to this
-            // one.
-            break;
-        }
-        GrGpuResource* resource = fPurgeableQueue.peek();
-        SkASSERT(resource->resourcePriv().isPurgeable());
-        resource->cacheAccess().release();
-    }
-}
-
 bool GrResourceCache::purgeToMakeHeadroom(size_t desiredHeadroomBytes) {
     AutoValidate av(this);
     if (desiredHeadroomBytes > fMaxBytes) {
diff --git a/src/gpu/GrResourceCache.h b/src/gpu/GrResourceCache.h
index eb52ca2..198ace2 100644
--- a/src/gpu/GrResourceCache.h
+++ b/src/gpu/GrResourceCache.h
@@ -154,16 +154,20 @@
         keys. */
     void purgeAsNeeded();
 
-    /** Purges all resources that don't have external owners. */
-    void purgeAllUnlocked() { this->purgeUnlockedResources(false); }
-
     // Purge unlocked resources. If 'scratchResourcesOnly' is true the purgeable resources
     // containing persistent data are spared. If it is false then all purgeable resources will
     // be deleted.
-    void purgeUnlockedResources(bool scratchResourcesOnly);
+    void purgeUnlockedResources(bool scratchResourcesOnly=false) {
+        this->purgeUnlockedResources(/*purgeTime=*/nullptr, scratchResourcesOnly);
+    }
 
-    /** Purge all resources not used since the passed in time. */
-    void purgeResourcesNotUsedSince(GrStdSteadyClock::time_point);
+    // Purge unlocked resources not used since the passed point in time. If 'scratchResourcesOnly'
+    // is true the purgeable resources containing persistent data are spared. If it is false then
+    // all purgeable resources older than 'purgeTime' will be deleted.
+    void purgeResourcesNotUsedSince(GrStdSteadyClock::time_point purgeTime,
+                                    bool scratchResourcesOnly=false) {
+        this->purgeUnlockedResources(&purgeTime, scratchResourcesOnly);
+    }
 
     /** If it's possible to purge enough resources to get the provided amount of budget
         headroom, do so and return true. If it's not possible, do nothing and return false.
@@ -273,6 +277,9 @@
 
     uint32_t getNextTimestamp();
 
+    void purgeUnlockedResources(const GrStdSteadyClock::time_point* purgeTime,
+                                bool scratchResourcesOnly);
+
 #ifdef SK_DEBUG
     bool isInCache(const GrGpuResource* r) const;
     void validate() const;