Adds a mechanism for GrCacheable objects to notify the resource cache
when their size has changed. GrResourceCacheEntry now holds a
reference to the cache, and a cached value of the resource's most
recent size.

Also utilizes this new functionality for mipmaps, and adds a test for
changing resource sizes.

R=bsalomon@google.com, robertphillips@google.com

Author: cdalton@nvidia.com

Review URL: https://codereview.chromium.org/257093002

git-svn-id: http://skia.googlecode.com/svn/trunk@14576 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/tests/ResourceCacheTest.cpp b/tests/ResourceCacheTest.cpp
index 7262b43..f6c8b1d 100644
--- a/tests/ResourceCacheTest.cpp
+++ b/tests/ResourceCacheTest.cpp
@@ -57,11 +57,14 @@
 }
 
 class TestResource : public GrCacheable {
+    static const size_t kDefaultSize = 100;
+
 public:
     SK_DECLARE_INST_COUNT(TestResource);
-    TestResource()
+    TestResource(size_t size = kDefaultSize)
         : fCache(NULL)
-        , fToDelete(NULL) {
+        , fToDelete(NULL)
+        , fSize(size) {
         ++fAlive;
     }
 
@@ -74,7 +77,12 @@
         }
     }
 
-    size_t gpuMemorySize() const SK_OVERRIDE { return 100; }
+    void setSize(size_t size) {
+        fSize = size;
+        this->didChangeGpuMemorySize();
+    }
+
+    size_t gpuMemorySize() const SK_OVERRIDE { return fSize; }
 
     bool isValidOnGpu() const SK_OVERRIDE { return true; }
 
@@ -88,6 +96,7 @@
 private:
     GrResourceCache* fCache;
     TestResource* fToDelete;
+    size_t fSize;
     static int fAlive;
 
     typedef GrCacheable INHERITED;
@@ -174,6 +183,99 @@
     }
 }
 
+static void test_resource_size_changed(skiatest::Reporter* reporter,
+                                       GrContext* context) {
+    GrCacheID::Domain domain = GrCacheID::GenerateDomain();
+    GrResourceKey::ResourceType t = GrResourceKey::GenerateResourceType();
+
+    GrCacheID::Key key1Data;
+    key1Data.fData64[0] = 0;
+    key1Data.fData64[1] = 0;
+    GrResourceKey key1(GrCacheID(domain, key1Data), t, 0);
+
+    GrCacheID::Key key2Data;
+    key2Data.fData64[0] = 1;
+    key2Data.fData64[1] = 0;
+    GrResourceKey key2(GrCacheID(domain, key2Data), t, 0);
+
+    // Test changing resources sizes (both increase & decrease).
+    {
+        GrResourceCache cache(2, 300);
+
+        TestResource* a = new TestResource(0);
+        a->setSize(100); // Test didChangeGpuMemorySize() when not in the cache.
+        cache.addResource(key1, a);
+        a->unref();
+
+        TestResource* b = new TestResource(0);
+        b->setSize(100);
+        cache.addResource(key2, b);
+        b->unref();
+
+        REPORTER_ASSERT(reporter, 200 == cache.getCachedResourceBytes());
+        REPORTER_ASSERT(reporter, 2 == cache.getCachedResourceCount());
+
+        static_cast<TestResource*>(cache.find(key2))->setSize(200);
+        static_cast<TestResource*>(cache.find(key1))->setSize(50);
+
+        REPORTER_ASSERT(reporter, 250 == cache.getCachedResourceBytes());
+        REPORTER_ASSERT(reporter, 2 == cache.getCachedResourceCount());
+    }
+
+    // Test increasing a resources size beyond the cache budget.
+    {
+        GrResourceCache cache(2, 300);
+
+        TestResource* a = new TestResource(100);
+        cache.addResource(key1, a);
+        a->unref();
+
+        TestResource* b = new TestResource(100);
+        cache.addResource(key2, b);
+        b->unref();
+
+        REPORTER_ASSERT(reporter, 200 == cache.getCachedResourceBytes());
+        REPORTER_ASSERT(reporter, 2 == cache.getCachedResourceCount());
+
+        static_cast<TestResource*>(cache.find(key2))->setSize(201);
+        REPORTER_ASSERT(reporter, NULL == cache.find(key1));
+
+        REPORTER_ASSERT(reporter, 201 == cache.getCachedResourceBytes());
+        REPORTER_ASSERT(reporter, 1 == cache.getCachedResourceCount());
+    }
+
+    // Test changing the size of an exclusively-held resource.
+    {
+        GrResourceCache cache(2, 300);
+
+        TestResource* a = new TestResource(100);
+        cache.addResource(key1, a);
+        cache.makeExclusive(a->getCacheEntry());
+
+        TestResource* b = new TestResource(100);
+        cache.addResource(key2, b);
+        b->unref();
+
+        REPORTER_ASSERT(reporter, 200 == cache.getCachedResourceBytes());
+        REPORTER_ASSERT(reporter, 2 == cache.getCachedResourceCount());
+        REPORTER_ASSERT(reporter, NULL == cache.find(key1));
+
+        a->setSize(200);
+
+        REPORTER_ASSERT(reporter, 300 == cache.getCachedResourceBytes());
+        REPORTER_ASSERT(reporter, 2 == cache.getCachedResourceCount());
+        // Internal resource cache validation will test the detached size (debug mode only).
+
+        cache.makeNonExclusive(a->getCacheEntry());
+        a->unref();
+
+        REPORTER_ASSERT(reporter, 300 == cache.getCachedResourceBytes());
+        REPORTER_ASSERT(reporter, 2 == cache.getCachedResourceCount());
+        REPORTER_ASSERT(reporter, NULL != cache.find(key1));
+        // Internal resource cache validation will test the detached size (debug mode only).
+    }
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 DEF_GPUTEST(ResourceCache, reporter, factory) {
     for (int type = 0; type < GrContextFactory::kLastGLContextType; ++type) {
@@ -199,6 +301,7 @@
         test_cache(reporter, context, &canvas);
         test_purge_invalidated(reporter, context);
         test_cache_delete_on_destruction(reporter, context);
+        test_resource_size_changed(reporter, context);
     }
 }