Rename GrResourceCache2->GrResourceCache

TBR=robertphillips@google.com

Review URL: https://codereview.chromium.org/921453002
diff --git a/src/gpu/GrResourceCache.cpp b/src/gpu/GrResourceCache.cpp
new file mode 100644
index 0000000..2b842a8
--- /dev/null
+++ b/src/gpu/GrResourceCache.cpp
@@ -0,0 +1,503 @@
+
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "GrResourceCache.h"
+#include "GrGpuResource.h"  
+
+#include "SkChecksum.h"
+#include "SkGr.h"
+#include "SkMessageBus.h"
+
+DECLARE_SKMESSAGEBUS_MESSAGE(GrContentKeyInvalidatedMessage);
+
+//////////////////////////////////////////////////////////////////////////////
+
+GrScratchKey::ResourceType GrScratchKey::GenerateResourceType() {
+    static int32_t gType = INHERITED::kInvalidDomain + 1;
+
+    int32_t type = sk_atomic_inc(&gType);
+    if (type > SK_MaxU16) {
+        SkFAIL("Too many Resource Types");
+    }
+
+    return static_cast<ResourceType>(type);
+}
+
+GrContentKey::Domain GrContentKey::GenerateDomain() {
+    static int32_t gDomain = INHERITED::kInvalidDomain + 1;
+
+    int32_t domain = sk_atomic_inc(&gDomain);
+    if (domain > SK_MaxU16) {
+        SkFAIL("Too many Content Key Domains");
+    }
+
+    return static_cast<Domain>(domain);
+}
+uint32_t GrResourceKeyHash(const uint32_t* data, size_t size) {
+    return SkChecksum::Compute(data, size);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+class GrResourceCache::AutoValidate : ::SkNoncopyable {
+public:
+    AutoValidate(GrResourceCache* cache) : fCache(cache) { cache->validate(); }
+    ~AutoValidate() { fCache->validate(); }
+private:
+    GrResourceCache* fCache;
+};
+
+ //////////////////////////////////////////////////////////////////////////////
+
+static const int kDefaultMaxCount = 2 * (1 << 10);
+static const size_t kDefaultMaxSize = 96 * (1 << 20);
+
+GrResourceCache::GrResourceCache()
+    : fMaxCount(kDefaultMaxCount)
+    , fMaxBytes(kDefaultMaxSize)
+#if GR_CACHE_STATS
+    , fHighWaterCount(0)
+    , fHighWaterBytes(0)
+    , fBudgetedHighWaterCount(0)
+    , fBudgetedHighWaterBytes(0)
+#endif
+    , fCount(0)
+    , fBytes(0)
+    , fBudgetedCount(0)
+    , fBudgetedBytes(0)
+    , fPurging(false)
+    , fNewlyPurgeableResourceWhilePurging(false)
+    , fOverBudgetCB(NULL)
+    , fOverBudgetData(NULL) {
+}
+
+GrResourceCache::~GrResourceCache() {
+    this->releaseAll();
+}
+
+void GrResourceCache::setLimits(int count, size_t bytes) {
+    fMaxCount = count;
+    fMaxBytes = bytes;
+    this->purgeAsNeeded();
+}
+
+void GrResourceCache::insertResource(GrGpuResource* resource) {
+    SkASSERT(resource);
+    SkASSERT(!resource->wasDestroyed());
+    SkASSERT(!this->isInCache(resource));
+    SkASSERT(!fPurging);
+    fResources.addToHead(resource);
+
+    size_t size = resource->gpuMemorySize();
+    ++fCount;
+    fBytes += size;
+#if GR_CACHE_STATS
+    fHighWaterCount = SkTMax(fCount, fHighWaterCount);
+    fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes);
+#endif
+    if (resource->cacheAccess().isBudgeted()) {
+        ++fBudgetedCount;
+        fBudgetedBytes += size;
+#if GR_CACHE_STATS
+        fBudgetedHighWaterCount = SkTMax(fBudgetedCount, fBudgetedHighWaterCount);
+        fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
+#endif
+    }
+    if (resource->cacheAccess().getScratchKey().isValid()) {
+        SkASSERT(!resource->cacheAccess().isWrapped());
+        fScratchMap.insert(resource->cacheAccess().getScratchKey(), resource);
+    }
+    
+    this->purgeAsNeeded();
+}
+
+void GrResourceCache::removeResource(GrGpuResource* resource) {
+    SkASSERT(this->isInCache(resource));
+
+    size_t size = resource->gpuMemorySize();
+    --fCount;
+    fBytes -= size;
+    if (resource->cacheAccess().isBudgeted()) {
+        --fBudgetedCount;
+        fBudgetedBytes -= size;
+    }
+
+    fResources.remove(resource);
+    if (resource->cacheAccess().getScratchKey().isValid()) {
+        fScratchMap.remove(resource->cacheAccess().getScratchKey(), resource);
+    }
+    if (resource->getContentKey().isValid()) {
+        fContentHash.remove(resource->getContentKey());
+    }
+    this->validate();
+}
+
+void GrResourceCache::abandonAll() {
+    AutoValidate av(this);
+
+    SkASSERT(!fPurging);
+    while (GrGpuResource* head = fResources.head()) {
+        SkASSERT(!head->wasDestroyed());
+        head->cacheAccess().abandon();
+        // abandon should have already removed this from the list.
+        SkASSERT(head != fResources.head());
+    }
+    SkASSERT(!fScratchMap.count());
+    SkASSERT(!fContentHash.count());
+    SkASSERT(!fCount);
+    SkASSERT(!fBytes);
+    SkASSERT(!fBudgetedCount);
+    SkASSERT(!fBudgetedBytes);
+}
+
+void GrResourceCache::releaseAll() {
+    AutoValidate av(this);
+
+    SkASSERT(!fPurging);
+    while (GrGpuResource* head = fResources.head()) {
+        SkASSERT(!head->wasDestroyed());
+        head->cacheAccess().release();
+        // release should have already removed this from the list.
+        SkASSERT(head != fResources.head());
+    }
+    SkASSERT(!fScratchMap.count());
+    SkASSERT(!fCount);
+    SkASSERT(!fBytes);
+    SkASSERT(!fBudgetedCount);
+    SkASSERT(!fBudgetedBytes);
+}
+
+class GrResourceCache::AvailableForScratchUse {
+public:
+    AvailableForScratchUse(bool rejectPendingIO) : fRejectPendingIO(rejectPendingIO) { }
+
+    bool operator()(const GrGpuResource* resource) const {
+        if (resource->internalHasRef() || !resource->cacheAccess().isScratch()) {
+            return false;
+        }
+        return !fRejectPendingIO || !resource->internalHasPendingIO();
+    }
+
+private:
+    bool fRejectPendingIO;
+};
+
+GrGpuResource* GrResourceCache::findAndRefScratchResource(const GrScratchKey& scratchKey,
+                                                           uint32_t flags) {
+    SkASSERT(!fPurging);
+    SkASSERT(scratchKey.isValid());
+
+    GrGpuResource* resource;
+    if (flags & (kPreferNoPendingIO_ScratchFlag | kRequireNoPendingIO_ScratchFlag)) {
+        resource = fScratchMap.find(scratchKey, AvailableForScratchUse(true));
+        if (resource) {
+            resource->ref();
+            this->makeResourceMRU(resource);
+            this->validate();
+            return resource;
+        } else if (flags & kRequireNoPendingIO_ScratchFlag) {
+            return NULL;
+        }
+        // TODO: fail here when kPrefer is specified, we didn't find a resource without pending io,
+        // but there is still space in our budget for the resource.
+    }
+    resource = fScratchMap.find(scratchKey, AvailableForScratchUse(false));
+    if (resource) {
+        resource->ref();
+        this->makeResourceMRU(resource);
+        this->validate();
+    }
+    return resource;
+}
+
+void GrResourceCache::willRemoveScratchKey(const GrGpuResource* resource) {
+    SkASSERT(resource->cacheAccess().getScratchKey().isValid());
+    fScratchMap.remove(resource->cacheAccess().getScratchKey(), resource);
+}
+
+void GrResourceCache::willRemoveContentKey(const GrGpuResource* resource) {
+    // Someone has a ref to this resource in order to invalidate it. When the ref count reaches
+    // zero we will get a notifyPurgable() and figure out what to do with it.
+    SkASSERT(resource->getContentKey().isValid());
+    fContentHash.remove(resource->getContentKey());
+}
+
+bool GrResourceCache::didSetContentKey(GrGpuResource* resource) {
+    SkASSERT(!fPurging);
+    SkASSERT(resource);
+    SkASSERT(this->isInCache(resource));
+    SkASSERT(resource->getContentKey().isValid());
+
+    GrGpuResource* res = fContentHash.find(resource->getContentKey());
+    if (NULL != res) {
+        return false;
+    }
+
+    fContentHash.add(resource);
+    this->validate();
+    return true;
+}
+
+void GrResourceCache::makeResourceMRU(GrGpuResource* resource) {
+    SkASSERT(!fPurging);
+    SkASSERT(resource);
+    SkASSERT(this->isInCache(resource));
+    fResources.remove(resource);    
+    fResources.addToHead(resource);
+}
+
+void GrResourceCache::notifyPurgeable(GrGpuResource* resource) {
+    SkASSERT(resource);
+    SkASSERT(this->isInCache(resource));
+    SkASSERT(resource->isPurgeable());
+
+    // We can't purge if in the middle of purging because purge is iterating. Instead record
+    // that additional resources became purgeable.
+    if (fPurging) {
+        fNewlyPurgeableResourceWhilePurging = true;
+        return;
+    }
+
+    bool release = false;
+
+    if (resource->cacheAccess().isWrapped()) {
+        release = true;
+    } else if (!resource->cacheAccess().isBudgeted()) {
+        // Check whether this resource could still be used as a scratch resource.
+        if (resource->cacheAccess().getScratchKey().isValid()) {
+            // We won't purge an existing resource to make room for this one.
+            bool underBudget = fBudgetedCount < fMaxCount &&
+                               fBudgetedBytes + resource->gpuMemorySize() <= fMaxBytes;
+            if (underBudget) {
+                resource->cacheAccess().makeBudgeted();
+            } else {
+                release = true;
+            }
+        } else {
+            release = true;
+        }
+    } else {
+        // Purge the resource if we're over budget
+        bool overBudget = fBudgetedCount > fMaxCount || fBudgetedBytes > fMaxBytes;
+
+        // Also purge if the resource has neither a valid scratch key nor a content key.
+        bool noKey = !resource->cacheAccess().getScratchKey().isValid() &&
+                     !resource->getContentKey().isValid();
+        if (overBudget || noKey) {
+            release = true;
+        }
+    }
+
+    if (release) {
+        SkDEBUGCODE(int beforeCount = fCount;)
+        resource->cacheAccess().release();
+        // We should at least free this resource, perhaps dependent resources as well.
+        SkASSERT(fCount < beforeCount);
+    }
+    this->validate();
+}
+
+void GrResourceCache::didChangeGpuMemorySize(const GrGpuResource* resource, size_t oldSize) {
+    // SkASSERT(!fPurging); GrPathRange increases size during flush. :(
+    SkASSERT(resource);
+    SkASSERT(this->isInCache(resource));
+
+    ptrdiff_t delta = resource->gpuMemorySize() - oldSize;
+
+    fBytes += delta;
+#if GR_CACHE_STATS
+    fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes);
+#endif
+    if (resource->cacheAccess().isBudgeted()) {
+        fBudgetedBytes += delta;
+#if GR_CACHE_STATS
+        fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
+#endif
+    }
+
+    this->purgeAsNeeded();
+    this->validate();
+}
+
+void GrResourceCache::didChangeBudgetStatus(GrGpuResource* resource) {
+    SkASSERT(!fPurging);
+    SkASSERT(resource);
+    SkASSERT(this->isInCache(resource));
+
+    size_t size = resource->gpuMemorySize();
+
+    if (resource->cacheAccess().isBudgeted()) {
+        ++fBudgetedCount;
+        fBudgetedBytes += size;
+#if GR_CACHE_STATS
+        fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
+        fBudgetedHighWaterCount = SkTMax(fBudgetedCount, fBudgetedHighWaterCount);
+#endif
+        this->purgeAsNeeded();
+    } else {
+        --fBudgetedCount;
+        fBudgetedBytes -= size;
+    }
+
+    this->validate();
+}
+
+void GrResourceCache::internalPurgeAsNeeded() {
+    SkASSERT(!fPurging);
+    SkASSERT(!fNewlyPurgeableResourceWhilePurging);
+    SkASSERT(fBudgetedCount > fMaxCount || fBudgetedBytes > fMaxBytes);
+
+    fPurging = true;
+
+    bool overBudget = true;
+    do {
+        fNewlyPurgeableResourceWhilePurging = false;
+        ResourceList::Iter resourceIter;
+        GrGpuResource* resource = resourceIter.init(fResources,
+                                                    ResourceList::Iter::kTail_IterStart);
+
+        while (resource) {
+            GrGpuResource* prev = resourceIter.prev();
+            if (resource->isPurgeable()) {
+                resource->cacheAccess().release();
+            }
+            resource = prev;
+            if (fBudgetedCount <= fMaxCount && fBudgetedBytes <= fMaxBytes) {
+                overBudget = false;
+                resource = NULL;
+            }
+        }
+
+        if (!fNewlyPurgeableResourceWhilePurging && overBudget && fOverBudgetCB) {
+            // Despite the purge we're still over budget. Call our over budget callback.
+            (*fOverBudgetCB)(fOverBudgetData);
+        }
+    } while (overBudget && fNewlyPurgeableResourceWhilePurging);
+
+    fNewlyPurgeableResourceWhilePurging = false;
+    fPurging = false;
+    this->validate();
+}
+
+void GrResourceCache::purgeAllUnlocked() {
+    SkASSERT(!fPurging);
+    SkASSERT(!fNewlyPurgeableResourceWhilePurging);
+
+    fPurging = true;
+
+    do {
+        fNewlyPurgeableResourceWhilePurging = false;
+        ResourceList::Iter resourceIter;
+        GrGpuResource* resource =
+            resourceIter.init(fResources, ResourceList::Iter::kTail_IterStart);
+
+        while (resource) {
+            GrGpuResource* prev = resourceIter.prev();
+            if (resource->isPurgeable()) {
+                resource->cacheAccess().release();
+            }
+            resource = prev;
+        }
+
+        if (!fNewlyPurgeableResourceWhilePurging && fCount && fOverBudgetCB) {
+            (*fOverBudgetCB)(fOverBudgetData);
+        }
+    } while (fNewlyPurgeableResourceWhilePurging);
+    fPurging = false;
+    this->validate();
+}
+
+void GrResourceCache::processInvalidContentKeys(
+    const SkTArray<GrContentKeyInvalidatedMessage>& msgs) {
+    for (int i = 0; i < msgs.count(); ++i) {
+        GrGpuResource* resource = this->findAndRefContentResource(msgs[i].key());
+        if (resource) {
+            resource->cacheAccess().removeContentKey();
+            resource->unref(); // will call notifyPurgeable, if it is indeed now purgeable.
+        }
+    }
+}
+
+#ifdef SK_DEBUG
+void GrResourceCache::validate() const {
+    // Reduce the frequency of validations for large resource counts.
+    static SkRandom gRandom;
+    int mask = (SkNextPow2(fCount + 1) >> 5) - 1;
+    if (~mask && (gRandom.nextU() & mask)) {
+        return;
+    }
+
+    size_t bytes = 0;
+    int count = 0;
+    int budgetedCount = 0;
+    size_t budgetedBytes = 0;
+    int locked = 0;
+    int scratch = 0;
+    int couldBeScratch = 0;
+    int content = 0;
+
+    ResourceList::Iter iter;
+    GrGpuResource* resource = iter.init(fResources, ResourceList::Iter::kHead_IterStart);
+    for ( ; resource; resource = iter.next()) {
+        bytes += resource->gpuMemorySize();
+        ++count;
+
+        if (!resource->isPurgeable()) {
+            ++locked;
+        }
+
+        if (resource->cacheAccess().isScratch()) {
+            SkASSERT(!resource->getContentKey().isValid());
+            ++scratch;
+            SkASSERT(fScratchMap.countForKey(resource->cacheAccess().getScratchKey()));
+            SkASSERT(!resource->cacheAccess().isWrapped());
+        } else if (resource->cacheAccess().getScratchKey().isValid()) {
+            SkASSERT(!resource->cacheAccess().isBudgeted() ||
+                     resource->getContentKey().isValid());
+            ++couldBeScratch;
+            SkASSERT(fScratchMap.countForKey(resource->cacheAccess().getScratchKey()));
+            SkASSERT(!resource->cacheAccess().isWrapped());
+        }
+        const GrContentKey& contentKey = resource->getContentKey();
+        if (contentKey.isValid()) {
+            ++content;
+            SkASSERT(fContentHash.find(contentKey) == resource);
+            SkASSERT(!resource->cacheAccess().isWrapped());
+            SkASSERT(resource->cacheAccess().isBudgeted());
+        }
+
+        if (resource->cacheAccess().isBudgeted()) {
+            ++budgetedCount;
+            budgetedBytes += resource->gpuMemorySize();
+        }
+    }
+
+    SkASSERT(fBudgetedCount <= fCount);
+    SkASSERT(fBudgetedBytes <= fBudgetedBytes);
+    SkASSERT(bytes == fBytes);
+    SkASSERT(count == fCount);
+    SkASSERT(budgetedBytes == fBudgetedBytes);
+    SkASSERT(budgetedCount == fBudgetedCount);
+#if GR_CACHE_STATS
+    SkASSERT(fBudgetedHighWaterCount <= fHighWaterCount);
+    SkASSERT(fBudgetedHighWaterBytes <= fHighWaterBytes);
+    SkASSERT(bytes <= fHighWaterBytes);
+    SkASSERT(count <= fHighWaterCount);
+    SkASSERT(budgetedBytes <= fBudgetedHighWaterBytes);
+    SkASSERT(budgetedCount <= fBudgetedHighWaterCount);
+#endif
+    SkASSERT(content == fContentHash.count());
+    SkASSERT(scratch + couldBeScratch == fScratchMap.count());
+
+    // This assertion is not currently valid because we can be in recursive notifyIsPurgeable()
+    // calls. This will be fixed when subresource registration is explicit.
+    // bool overBudget = budgetedBytes > fMaxBytes || budgetedCount > fMaxCount;
+    // SkASSERT(!overBudget || locked == count || fPurging);
+}
+#endif