Add mechanism to proactively purge old resources in GrResourceCache.

This change leaves the feature turned off by default.

Review URL: https://codereview.chromium.org/1032873002
diff --git a/tests/ResourceCacheTest.cpp b/tests/ResourceCacheTest.cpp
index 737d743..f92ba87 100644
--- a/tests/ResourceCacheTest.cpp
+++ b/tests/ResourceCacheTest.cpp
@@ -5,6 +5,9 @@
  * found in the LICENSE file.
  */
 
+// Include here to ensure SK_SUPPORT_GPU is set correctly before it is examined.
+#include "SkTypes.h"
+
 #if SK_SUPPORT_GPU
 
 #include "GrContext.h"
@@ -1023,6 +1026,88 @@
     }
 }
 
+static void test_flush(skiatest::Reporter* reporter) {
+    Mock mock(1000000, 1000000);
+    GrContext* context = mock.context();
+    GrResourceCache* cache = mock.cache();
+
+    // The current cache impl will round the max flush count to the next power of 2. So we choose a
+    // power of two here to keep things simpler.
+    static const int kFlushCount = 16;
+    cache->setLimits(1000000, 1000000, kFlushCount);
+
+    {
+        // Insert a resource and send a flush notification kFlushCount times.
+        for (int i = 0; i < kFlushCount; ++i) {
+            TestResource* r = SkNEW_ARGS(TestResource, (context->getGpu()));
+            GrUniqueKey k;
+            make_unique_key<1>(&k, i);
+            r->resourcePriv().setUniqueKey(k);
+            r->unref();
+            cache->notifyFlushOccurred();
+        }
+
+        // Send flush notifications to the cache. Each flush should purge the oldest resource.
+        for (int i = 0; i < kFlushCount - 1; ++i) {
+            // The first resource was purged after the last flush in the initial loop, hence the -1.
+            REPORTER_ASSERT(reporter, kFlushCount - i - 1 == cache->getResourceCount());
+            for (int j = 0; j < i; ++j) {
+                GrUniqueKey k;
+                make_unique_key<1>(&k, j);
+                GrGpuResource* r = cache->findAndRefUniqueResource(k);
+                REPORTER_ASSERT(reporter, !SkToBool(r));
+                SkSafeUnref(r);
+            }
+            cache->notifyFlushOccurred();
+        }
+
+        REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
+        cache->purgeAllUnlocked();
+    }
+
+    // Do a similar test but where we leave refs on some resources to prevent them from being
+    // purged.
+    {
+        GrGpuResource* refedResources[kFlushCount >> 1];
+        for (int i = 0; i < kFlushCount; ++i) {
+            TestResource* r = SkNEW_ARGS(TestResource, (context->getGpu()));
+            GrUniqueKey k;
+            make_unique_key<1>(&k, i);
+            r->resourcePriv().setUniqueKey(k);
+            // Leave a ref on every other resource, beginning with the first.
+            if (SkToBool(i & 0x1)) {
+                refedResources[i/2] = r;
+            } else {
+                r->unref();
+            }
+            cache->notifyFlushOccurred();
+        }
+
+        for (int i = 0; i < kFlushCount; ++i) {
+            // Should get a resource purged every other flush.
+            REPORTER_ASSERT(reporter, kFlushCount - i/2 - 1 == cache->getResourceCount());
+            cache->notifyFlushOccurred();
+        }
+
+        // Unref all the resources that we kept refs on in the first loop.
+        for (int i = 0; i < kFlushCount >> 1; ++i) {
+            refedResources[i]->unref();
+        }
+
+        // When we unref'ed them their timestamps got updated. So nothing should be purged until we
+        // get kFlushCount additional flushes. Then everything should be purged.
+        for (int i = 0; i < kFlushCount; ++i) {
+            REPORTER_ASSERT(reporter, kFlushCount >> 1 == cache->getResourceCount());
+            cache->notifyFlushOccurred();
+        }
+        REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
+
+        cache->purgeAllUnlocked();
+    }
+
+    REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
+}
+
 static void test_large_resource_count(skiatest::Reporter* reporter) {
     // Set the cache size to double the resource count because we're going to create 2x that number
     // resources, using two different key domains. Add a little slop to the bytes because we resize
@@ -1118,6 +1203,7 @@
     test_cache_chained_purge(reporter);
     test_resource_size_changed(reporter);
     test_timestamp_wrap(reporter);
+    test_flush(reporter);
     test_large_resource_count(reporter);
 }