diff options
-rw-r--r-- | include/gpu/GrGpuResource.h | 1 | ||||
-rw-r--r-- | src/gpu/GrGpuResource.cpp | 3 | ||||
-rw-r--r-- | src/gpu/GrGpuResourceCacheAccess.h | 14 | ||||
-rw-r--r-- | src/gpu/GrResourceCache.cpp | 89 | ||||
-rw-r--r-- | src/gpu/GrResourceCache.h | 30 | ||||
-rw-r--r-- | tests/ResourceCacheTest.cpp | 13 |
6 files changed, 65 insertions, 85 deletions
diff --git a/include/gpu/GrGpuResource.h b/include/gpu/GrGpuResource.h index 9305c16758..46e48fc4ee 100644 --- a/include/gpu/GrGpuResource.h +++ b/include/gpu/GrGpuResource.h @@ -287,6 +287,7 @@ private: // This value reflects how recently this resource was accessed in the cache. This is maintained // by the cache. uint32_t fTimestamp; + uint32_t fExternalFlushCntWhenBecamePurgeable; static const size_t kInvalidGpuMemorySize = ~static_cast<size_t>(0); GrScratchKey fScratchKey; diff --git a/src/gpu/GrGpuResource.cpp b/src/gpu/GrGpuResource.cpp index e64339bacb..c1578b50ab 100644 --- a/src/gpu/GrGpuResource.cpp +++ b/src/gpu/GrGpuResource.cpp @@ -20,7 +20,8 @@ static inline GrResourceCache* get_resource_cache(GrGpu* gpu) { } GrGpuResource::GrGpuResource(GrGpu* gpu) - : fGpu(gpu) + : fExternalFlushCntWhenBecamePurgeable(0) + , fGpu(gpu) , fGpuMemorySize(kInvalidGpuMemorySize) , fBudgeted(SkBudgeted::kNo) , fRefsWrappedObjects(false) diff --git a/src/gpu/GrGpuResourceCacheAccess.h b/src/gpu/GrGpuResourceCacheAccess.h index b09dcc1872..e91f899cf2 100644 --- a/src/gpu/GrGpuResourceCacheAccess.h +++ b/src/gpu/GrGpuResourceCacheAccess.h @@ -58,6 +58,20 @@ private: uint32_t timestamp() const { return fResource->fTimestamp; } void setTimestamp(uint32_t ts) { fResource->fTimestamp = ts; } + /** Called by the cache to record when this became purgeable. */ + void setFlushCntWhenResourceBecamePurgeable(uint32_t cnt) { + SkASSERT(fResource->isPurgeable()); + fResource->fExternalFlushCntWhenBecamePurgeable = cnt; + } + /** + * Called by the cache to determine whether this resource has been puregable for more than + * a threshold number of external flushes. + */ + uint32_t flushCntWhenResourceBecamePurgeable() { + SkASSERT(fResource->isPurgeable()); + return fResource->fExternalFlushCntWhenBecamePurgeable; + } + int* accessCacheIndex() const { return &fResource->fCacheArrayIndex; } CacheAccess(GrGpuResource* resource) : fResource(resource) {} diff --git a/src/gpu/GrResourceCache.cpp b/src/gpu/GrResourceCache.cpp index 529f87c124..9462a7384d 100644 --- a/src/gpu/GrResourceCache.cpp +++ b/src/gpu/GrResourceCache.cpp @@ -74,49 +74,23 @@ GrResourceCache::GrResourceCache(const GrCaps* caps) , fBudgetedCount(0) , fBudgetedBytes(0) , fRequestFlush(false) - , fFlushTimestamps(nullptr) - , fLastFlushTimestampIndex(0) + , fExternalFlushCnt(0) , fPreferVRAMUseOverFlushes(caps->preferVRAMUseOverFlushes()) { SkDEBUGCODE(fCount = 0;) SkDEBUGCODE(fNewlyPurgeableResourceForValidation = nullptr;) - this->resetFlushTimestamps(); } GrResourceCache::~GrResourceCache() { this->releaseAll(); - delete[] fFlushTimestamps; } void GrResourceCache::setLimits(int count, size_t bytes, int maxUnusedFlushes) { fMaxCount = count; fMaxBytes = bytes; fMaxUnusedFlushes = maxUnusedFlushes; - this->resetFlushTimestamps(); this->purgeAsNeeded(); } -void GrResourceCache::resetFlushTimestamps() { - delete[] 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 = nullptr; - return; - } - - fFlushTimestamps = new 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)); @@ -390,6 +364,7 @@ void GrResourceCache::notifyCntReachedZero(GrGpuResource* resource, uint32_t fla SkASSERT(resource->isPurgeable()); this->removeFromNonpurgeableArray(resource); fPurgeableQueue.insert(resource); + resource->cacheAccess().setFlushCntWhenResourceBecamePurgeable(fExternalFlushCnt); if (SkBudgeted::kNo == resource->resourcePriv().isBudgeted()) { // Check whether this resource could still be used as a scratch resource. @@ -474,20 +449,29 @@ void GrResourceCache::purgeAsNeeded() { this->processInvalidUniqueKeys(invalidKeyMsgs); } - 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; + if (fMaxUnusedFlushes > 0) { + // We want to know how many complete flushes have occurred without the resource being used. + // If the resource was tagged when fExternalFlushCnt was N then this means it became + // purgeable during activity that became the N+1th flush. So when the flush count is N+2 + // it has sat in the purgeable queue for one entire flush. + uint32_t oldestAllowedFlushCnt = fExternalFlushCnt - fMaxUnusedFlushes - 1; + // check for underflow + if (oldestAllowedFlushCnt < fExternalFlushCnt) { + while (fPurgeableQueue.count()) { + uint32_t flushWhenResourceBecamePurgeable = + fPurgeableQueue.peek()->cacheAccess().flushCntWhenResourceBecamePurgeable(); + if (oldestAllowedFlushCnt < flushWhenResourceBecamePurgeable) { + // Resources were given both LRU timestamps and tagged with a flush cnt when + // they first became purgeable. The LRU timestamp won't change again until the + // resource is made non-purgeable again. So, at this point all the remaining + // resources in the timestamp-sorted queue will have a flush count >= to this + // one. + break; + } + GrGpuResource* resource = fPurgeableQueue.peek(); + SkASSERT(resource->isPurgeable()); + resource->cacheAccess().release(); } - GrGpuResource* resource = fPurgeableQueue.peek(); - SkASSERT(resource->isPurgeable()); - resource->cacheAccess().release(); } } @@ -566,13 +550,8 @@ uint32_t GrResourceCache::getNextTimestamp() { fPurgeableQueue.pop(); } - struct Less { - bool operator()(GrGpuResource* a, GrGpuResource* b) { - return CompareTimestamp(a,b); - } - }; - Less less; - SkTQSort(fNonpurgeableResources.begin(), fNonpurgeableResources.end() - 1, less); + SkTQSort(fNonpurgeableResources.begin(), fNonpurgeableResources.end() - 1, + CompareTimestamp); // Pick resources out of the purgeable and non-purgeable arrays based on lowest // timestamp and assign new timestamps. @@ -611,9 +590,6 @@ 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++; @@ -628,13 +604,12 @@ void GrResourceCache::notifyFlushOccurred(FlushType type) { fRequestFlush = false; break; case FlushType::kExternal: - 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; + ++fExternalFlushCnt; + if (0 == fExternalFlushCnt) { + // When this wraps just reset all the purgeable resources' last used flush state. + for (int i = 0; i < fPurgeableQueue.count(); ++i) { + fPurgeableQueue.at(i)->cacheAccess().setFlushCntWhenResourceBecamePurgeable(0); + } } break; } diff --git a/src/gpu/GrResourceCache.h b/src/gpu/GrResourceCache.h index bf7b237006..ae9a4e7ee7 100644 --- a/src/gpu/GrResourceCache.h +++ b/src/gpu/GrResourceCache.h @@ -40,12 +40,6 @@ class SkTraceMemoryDump; * A unique key always takes precedence over a scratch key when a resource has both types of keys. * If a resource has neither key type then it will be deleted as soon as the last reference to it * is dropped. - * - * When proactive purging is enabled, on every flush, the timestamp of that flush is stored in a - * n-sized ring buffer. When purging occurs each purgeable resource's timestamp is compared to the - * timestamp of the n-th prior flush. If the resource's last use timestamp is older than the old - * flush then the resource is proactively purged even when the cache is under budget. By default - * this feature is disabled, though it can be enabled by calling GrResourceCache::setLimits. */ class GrResourceCache { public: @@ -56,11 +50,12 @@ public: static const int kDefaultMaxCount = 2 * (1 << 12); // Default maximum number of bytes of gpu memory of budgeted resources in the cache. static const size_t kDefaultMaxSize = 96 * (1 << 20); - // Default number of flushes a budgeted resources can go unused in the cache before it is - // purged. Large values disable the feature (as the ring buffer of flush timestamps would be - // large). This is currently the default until we decide to enable this feature - // of the cache by default. - static const int kDefaultMaxUnusedFlushes = 64; + // Default number of external flushes a budgeted resources can go unused in the cache before it + // is purged. Using a value <= 0 disables this feature. + static const int kDefaultMaxUnusedFlushes = + 1 * /* flushes per frame */ + 60 * /* fps */ + 30; /* seconds */ /** Used to access functionality needed by GrGpuResource for lifetime management. */ class ResourceAccess; @@ -68,9 +63,9 @@ public: /** * Sets the cache limits in terms of number of resources, max gpu memory byte size, and number - * of GrContext flushes that a resource can be unused before it is evicted. The latter value is - * a suggestion and there is no promise that a resource will be purged immediately after it - * hasn't been used in maxUnusedFlushes flushes. + * of external GrContext flushes that a resource can be unused before it is evicted. The latter + * value is a suggestion and there is no promise that a resource will be purged immediately + * after it hasn't been used in maxUnusedFlushes flushes. */ void setLimits(int count, size_t bytes, int maxUnusedFlushes = kDefaultMaxUnusedFlushes); @@ -237,7 +232,6 @@ private: void refAndMakeResourceMRU(GrGpuResource*); /// @} - void resetFlushTimestamps(); void processInvalidUniqueKeys(const SkTArray<GrUniqueKeyInvalidatedMessage>&); void addToNonpurgeableArray(GrGpuResource*); void removeFromNonpurgeableArray(GrGpuResource*); @@ -321,11 +315,7 @@ private: size_t fBudgetedBytes; bool fRequestFlush; - - // We keep track of the "timestamps" of the last n flushes. If a resource hasn't been used in - // that time then we well preemptively purge it to reduce memory usage. - uint32_t* fFlushTimestamps; - int fLastFlushTimestampIndex; + uint32_t fExternalFlushCnt; InvalidUniqueKeyInbox fInvalidUniqueKeyInbox; diff --git a/tests/ResourceCacheTest.cpp b/tests/ResourceCacheTest.cpp index b568485e4b..317636644f 100644 --- a/tests/ResourceCacheTest.cpp +++ b/tests/ResourceCacheTest.cpp @@ -1137,8 +1137,8 @@ static void test_flush(skiatest::Reporter* reporter) { } // 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. + for (int i = 0; i < kFlushCount; ++i) { + cache->notifyFlushOccurred(GrResourceCache::kExternal); REPORTER_ASSERT(reporter, kFlushCount - i - 1 == cache->getResourceCount()); for (int j = 0; j < i; ++j) { GrUniqueKey k; @@ -1147,7 +1147,6 @@ static void test_flush(skiatest::Reporter* reporter) { REPORTER_ASSERT(reporter, !SkToBool(r)); SkSafeUnref(r); } - cache->notifyFlushOccurred(GrResourceCache::kExternal); } REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); @@ -1174,8 +1173,8 @@ static void test_flush(skiatest::Reporter* reporter) { 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(GrResourceCache::kExternal); + REPORTER_ASSERT(reporter, kFlushCount - i/2 - 1 == cache->getResourceCount()); } // Unref all the resources that we kept refs on in the first loop. @@ -1183,9 +1182,9 @@ static void test_flush(skiatest::Reporter* reporter) { 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) { + // After kFlushCount + 1 flushes they all will have sat in the purgeable queue for + // kFlushCount full flushes. + for (int i = 0; i < kFlushCount + 1; ++i) { REPORTER_ASSERT(reporter, kFlushCount >> 1 == cache->getResourceCount()); cache->notifyFlushOccurred(GrResourceCache::kExternal); } |