| Index: src/gpu/GrResourceCache.cpp
|
| diff --git a/src/gpu/GrResourceCache.cpp b/src/gpu/GrResourceCache.cpp
|
| index f8b1a1266e2848d0abddd6a4ddd62ac08fc425ea..f1a51b06c34971b8d4675c1c5aabed829ca45b52 100644
|
| --- a/src/gpu/GrResourceCache.cpp
|
| +++ b/src/gpu/GrResourceCache.cpp
|
| @@ -40,6 +40,7 @@ GrUniqueKey::Domain GrUniqueKey::GenerateDomain() {
|
|
|
| return static_cast<Domain>(domain);
|
| }
|
| +
|
| uint32_t GrResourceKeyHash(const uint32_t* data, size_t size) {
|
| return SkChecksum::Compute(data, size);
|
| }
|
| @@ -56,13 +57,12 @@ private:
|
|
|
| //////////////////////////////////////////////////////////////////////////////
|
|
|
| -static const int kDefaultMaxCount = 2 * (1 << 12);
|
| -static const size_t kDefaultMaxSize = 96 * (1 << 20);
|
|
|
| GrResourceCache::GrResourceCache()
|
| : fTimestamp(0)
|
| , fMaxCount(kDefaultMaxCount)
|
| , fMaxBytes(kDefaultMaxSize)
|
| + , fMaxUnusedFlushes(kDefaultMaxUnusedFlushes)
|
| #if GR_CACHE_STATS
|
| , fHighWaterCount(0)
|
| , fHighWaterBytes(0)
|
| @@ -73,20 +73,49 @@ GrResourceCache::GrResourceCache()
|
| , fBudgetedCount(0)
|
| , fBudgetedBytes(0)
|
| , fOverBudgetCB(NULL)
|
| - , fOverBudgetData(NULL) {
|
| + , fOverBudgetData(NULL)
|
| + , fFlushTimestamps(NULL)
|
| + , fLastFlushTimestampIndex(0){
|
| SkDEBUGCODE(fCount = 0;)
|
| + SkDEBUGCODE(fNewlyPurgeableResourceForValidation = NULL;)
|
| + this->resetFlushTimestamps();
|
| }
|
|
|
| GrResourceCache::~GrResourceCache() {
|
| this->releaseAll();
|
| + SkDELETE(fFlushTimestamps);
|
| }
|
|
|
| -void GrResourceCache::setLimits(int count, size_t bytes) {
|
| +void GrResourceCache::setLimits(int count, size_t bytes, int maxUnusedFlushes) {
|
| fMaxCount = count;
|
| fMaxBytes = bytes;
|
| + fMaxUnusedFlushes = maxUnusedFlushes;
|
| + this->resetFlushTimestamps();
|
| this->purgeAsNeeded();
|
| }
|
|
|
| +void GrResourceCache::resetFlushTimestamps() {
|
| + SkDELETE(fFlushTimestamps);
|
| +
|
| + // We assume this number is a power of two when wrapping indices into the timestamp array.
|
| + fMaxUnusedFlushes = SkNextPow2(fMaxUnusedFlushes);
|
| +
|
| + // Since our implementation is to store the timestamps of the last fMaxUnusedFlushes flush calls
|
| + // we just turn the feature off if that array would be large.
|
| + static const int kMaxSupportedTimestampHistory = 128;
|
| +
|
| + if (fMaxUnusedFlushes > kMaxSupportedTimestampHistory) {
|
| + fFlushTimestamps = NULL;
|
| + return;
|
| + }
|
| +
|
| + fFlushTimestamps = SkNEW_ARRAY(uint32_t, fMaxUnusedFlushes);
|
| + fLastFlushTimestampIndex = 0;
|
| + // Set all the historical flush timestamps to initially be at the beginning of time (timestamp
|
| + // 0).
|
| + sk_bzero(fFlushTimestamps, fMaxUnusedFlushes * sizeof(uint32_t));
|
| +}
|
| +
|
| void GrResourceCache::insertResource(GrGpuResource* resource) {
|
| SkASSERT(resource);
|
| SkASSERT(!this->isInCache(resource));
|
| @@ -247,8 +276,8 @@ void GrResourceCache::willRemoveScratchKey(const GrGpuResource* resource) {
|
| }
|
|
|
| void GrResourceCache::removeUniqueKey(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.
|
| + // Someone has a ref to this resource in order to have removed the key. When the ref count
|
| + // reaches zero we will get a ref cnt notification and figure out what to do with it.
|
| if (resource->getUniqueKey().isValid()) {
|
| SkASSERT(resource == fUniqueHash.find(resource->getUniqueKey()));
|
| fUniqueHash.remove(resource->getUniqueKey());
|
| @@ -307,11 +336,34 @@ void GrResourceCache::refAndMakeResourceMRU(GrGpuResource* resource) {
|
| this->validate();
|
| }
|
|
|
| -void GrResourceCache::notifyPurgeable(GrGpuResource* resource) {
|
| +void GrResourceCache::notifyCntReachedZero(GrGpuResource* resource, uint32_t flags) {
|
| SkASSERT(resource);
|
| + SkASSERT(!resource->wasDestroyed());
|
| + SkASSERT(flags);
|
| SkASSERT(this->isInCache(resource));
|
| - SkASSERT(resource->isPurgeable());
|
| + // This resource should always be in the nonpurgeable array when this function is called. It
|
| + // will be moved to the queue if it is newly purgeable.
|
| + SkASSERT(fNonpurgeableResources[*resource->cacheAccess().accessCacheIndex()] == resource);
|
| +
|
| + if (SkToBool(ResourceAccess::kRefCntReachedZero_RefNotificationFlag & flags)) {
|
| +#ifdef SK_DEBUG
|
| + // When the timestamp overflows validate() is called. validate() checks that resources in
|
| + // the nonpurgeable array are indeed not purgeable. However, the movement from the array to
|
| + // the purgeable queue happens just below in this function. So we mark it as an exception.
|
| + if (resource->isPurgeable()) {
|
| + fNewlyPurgeableResourceForValidation = resource;
|
| + }
|
| +#endif
|
| + resource->cacheAccess().setTimestamp(this->getNextTimestamp());
|
| + SkDEBUGCODE(fNewlyPurgeableResourceForValidation = NULL);
|
| + }
|
|
|
| + if (!SkToBool(ResourceAccess::kAllCntsReachedZero_RefNotificationFlag & flags)) {
|
| + SkASSERT(!resource->isPurgeable());
|
| + return;
|
| + }
|
| +
|
| + SkASSERT(resource->isPurgeable());
|
| this->removeFromNonpurgeableArray(resource);
|
| fPurgeableQueue.insert(resource);
|
|
|
| @@ -391,25 +443,43 @@ void GrResourceCache::didChangeBudgetStatus(GrGpuResource* resource) {
|
| this->validate();
|
| }
|
|
|
| -void GrResourceCache::internalPurgeAsNeeded() {
|
| - SkASSERT(this->overBudget());
|
| +void GrResourceCache::purgeAsNeeded() {
|
| + SkTArray<GrUniqueKeyInvalidatedMessage> invalidKeyMsgs;
|
| + fInvalidUniqueKeyInbox.poll(&invalidKeyMsgs);
|
| + if (invalidKeyMsgs.count()) {
|
| + this->processInvalidUniqueKeys(invalidKeyMsgs);
|
| + }
|
|
|
| - bool stillOverbudget = true;
|
| - while (fPurgeableQueue.count()) {
|
| + if (fFlushTimestamps) {
|
| + // Assuming kNumFlushesToDeleteUnusedResource is a power of 2.
|
| + SkASSERT(SkIsPow2(fMaxUnusedFlushes));
|
| + int oldestFlushIndex = (fLastFlushTimestampIndex + 1) & (fMaxUnusedFlushes - 1);
|
| +
|
| + uint32_t oldestAllowedTimestamp = fFlushTimestamps[oldestFlushIndex];
|
| + while (fPurgeableQueue.count()) {
|
| + uint32_t oldestResourceTimestamp = fPurgeableQueue.peek()->cacheAccess().timestamp();
|
| + if (oldestAllowedTimestamp < oldestResourceTimestamp) {
|
| + break;
|
| + }
|
| + GrGpuResource* resource = fPurgeableQueue.peek();
|
| + SkASSERT(resource->isPurgeable());
|
| + resource->cacheAccess().release();
|
| + }
|
| + }
|
| +
|
| + bool stillOverbudget = this->overBudget();
|
| + while (stillOverbudget && fPurgeableQueue.count()) {
|
| GrGpuResource* resource = fPurgeableQueue.peek();
|
| SkASSERT(resource->isPurgeable());
|
| resource->cacheAccess().release();
|
| - if (!this->overBudget()) {
|
| - stillOverbudget = false;
|
| - break;
|
| - }
|
| + stillOverbudget = this->overBudget();
|
| }
|
|
|
| this->validate();
|
|
|
| if (stillOverbudget) {
|
| // Despite the purge we're still over budget. Call our over budget callback. If this frees
|
| - // any resources then we'll get notifyPurgeable() calls and take appropriate action.
|
| + // any resources then we'll get notified and take appropriate action.
|
| (*fOverBudgetCB)(fOverBudgetData);
|
| this->validate();
|
| }
|
| @@ -433,7 +503,7 @@ void GrResourceCache::processInvalidUniqueKeys(
|
| GrGpuResource* resource = this->findAndRefUniqueResource(msgs[i].key());
|
| if (resource) {
|
| resource->resourcePriv().removeUniqueKey();
|
| - resource->unref(); // will call notifyPurgeable, if it is indeed now purgeable.
|
| + resource->unref(); // If this resource is now purgeable, the cache will be notified.
|
| }
|
| }
|
| }
|
| @@ -518,11 +588,26 @@ uint32_t GrResourceCache::getNextTimestamp() {
|
|
|
| // count should be the next timestamp we return.
|
| SkASSERT(fTimestamp == SkToU32(count));
|
| +
|
| + // The historical timestamps of flushes are now invalid.
|
| + this->resetFlushTimestamps();
|
| }
|
| }
|
| return fTimestamp++;
|
| }
|
|
|
| +void GrResourceCache::notifyFlushOccurred() {
|
| + if (fFlushTimestamps) {
|
| + SkASSERT(SkIsPow2(fMaxUnusedFlushes));
|
| + fLastFlushTimestampIndex = (fLastFlushTimestampIndex + 1) & (fMaxUnusedFlushes - 1);
|
| + // get the timestamp before accessing fFlushTimestamps because getNextTimestamp will
|
| + // reallocate fFlushTimestamps on timestamp overflow.
|
| + uint32_t timestamp = this->getNextTimestamp();
|
| + fFlushTimestamps[fLastFlushTimestampIndex] = timestamp;
|
| + this->purgeAsNeeded();
|
| + }
|
| +}
|
| +
|
| #ifdef SK_DEBUG
|
| void GrResourceCache::validate() const {
|
| // Reduce the frequency of validations for large resource counts.
|
| @@ -586,7 +671,8 @@ void GrResourceCache::validate() const {
|
| Stats stats(this);
|
|
|
| for (int i = 0; i < fNonpurgeableResources.count(); ++i) {
|
| - SkASSERT(!fNonpurgeableResources[i]->isPurgeable());
|
| + SkASSERT(!fNonpurgeableResources[i]->isPurgeable() ||
|
| + fNewlyPurgeableResourceForValidation == fNonpurgeableResources[i]);
|
| SkASSERT(*fNonpurgeableResources[i]->cacheAccess().accessCacheIndex() == i);
|
| SkASSERT(!fNonpurgeableResources[i]->wasDestroyed());
|
| stats.update(fNonpurgeableResources[i]);
|
| @@ -615,7 +701,7 @@ void GrResourceCache::validate() const {
|
| SkASSERT(stats.fContent == fUniqueHash.count());
|
| SkASSERT(stats.fScratch + stats.fCouldBeScratch == fScratchMap.count());
|
|
|
| - // This assertion is not currently valid because we can be in recursive notifyIsPurgeable()
|
| + // This assertion is not currently valid because we can be in recursive notifyCntReachedZero()
|
| // calls. This will be fixed when subresource registration is explicit.
|
| // bool overBudget = budgetedBytes > fMaxBytes || budgetedCount > fMaxCount;
|
| // SkASSERT(!overBudget || locked == count || fPurging);
|
|
|