Add GrResourceCache::purgeToMakeHeadroom

This function will be used by the resource allocator to figure out
whether an allocation plan is feasible without committing to it.

Bug: skia:10877
Change-Id: I135b7b80d53d9c3541d2fa0313d91d14a1d54eb2
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/394156
Commit-Queue: Adlai Holler <adlai@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
Auto-Submit: Adlai Holler <adlai@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/src/gpu/GrGpuResource.h b/src/gpu/GrGpuResource.h
index 015f036..3a5c064 100644
--- a/src/gpu/GrGpuResource.h
+++ b/src/gpu/GrGpuResource.h
@@ -124,7 +124,7 @@
     /**
      * Retrieves the context that owns the object. Note that it is possible for
      * this to return NULL. When objects have been release()ed or abandon()ed
-     * they no longer have an owning context. Destroying a GrContext
+     * they no longer have an owning context. Destroying a GrDirectContext
      * automatically releases all its resources.
      */
     const GrDirectContext* getContext() const;
diff --git a/src/gpu/GrResourceCache.cpp b/src/gpu/GrResourceCache.cpp
index 4c5cb85..94211f2 100644
--- a/src/gpu/GrResourceCache.cpp
+++ b/src/gpu/GrResourceCache.cpp
@@ -7,6 +7,7 @@
 
 #include "src/gpu/GrResourceCache.h"
 #include <atomic>
+#include <vector>
 #include "include/gpu/GrDirectContext.h"
 #include "include/private/GrSingleOwner.h"
 #include "include/private/SkTo.h"
@@ -619,6 +620,40 @@
     }
 }
 
+bool GrResourceCache::purgeToMakeHeadroom(size_t desiredHeadroomBytes) {
+    AutoValidate av(this);
+    if (this->wouldFit(desiredHeadroomBytes)) {
+        return true;
+    }
+    fPurgeableQueue.sort();
+
+    size_t headroom = this->overBudget() ? 0 : fMaxBytes - fBudgetedBytes;
+    int purgeCnt = 0;
+    for (int i = 0; i < fPurgeableQueue.count(); i++) {
+        GrGpuResource* resource = fPurgeableQueue.at(i);
+        headroom += resource->gpuMemorySize();
+        if (headroom >= desiredHeadroomBytes) {
+            purgeCnt = i + 1;
+            break;
+        }
+    }
+    if (headroom < desiredHeadroomBytes) {
+        return false;
+    }
+
+    // Success! Release the resources.
+    // Copy to array first so we don't mess with the queue.
+    std::vector<GrGpuResource*> resources;
+    resources.reserve(purgeCnt);
+    for (int i = 0; i < purgeCnt; i++) {
+        resources.push_back(fPurgeableQueue.at(i));
+    }
+    for (GrGpuResource* resource : resources) {
+        resource->cacheAccess().release();
+    }
+    return true;
+}
+
 void GrResourceCache::purgeUnlockedResources(size_t bytesToPurge, bool preferScratchResources) {
 
     const size_t tmpByteBudget = std::max((size_t)0, fBytes - bytesToPurge);
diff --git a/src/gpu/GrResourceCache.h b/src/gpu/GrResourceCache.h
index 4a634ad..eb52ca2 100644
--- a/src/gpu/GrResourceCache.h
+++ b/src/gpu/GrResourceCache.h
@@ -165,6 +165,11 @@
     /** Purge all resources not used since the passed in time. */
     void purgeResourcesNotUsedSince(GrStdSteadyClock::time_point);
 
+    /** 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.
+     */
+    bool purgeToMakeHeadroom(size_t desiredHeadroomBytes);
+
     bool overBudget() const { return fBudgetedBytes > fMaxBytes; }
 
     /**