| Index: src/gpu/GrResourceCache.cpp
|
| diff --git a/src/gpu/GrResourceCache.cpp b/src/gpu/GrResourceCache.cpp
|
| index 9462a7384d73d45d3a5a857060ab7755d7082903..e6afa81b79de1104c7328549d05cef0a340f5807 100644
|
| --- a/src/gpu/GrResourceCache.cpp
|
| +++ b/src/gpu/GrResourceCache.cpp
|
| @@ -57,13 +57,22 @@ private:
|
| };
|
|
|
| //////////////////////////////////////////////////////////////////////////////
|
| -
|
| +constexpr int GrResourceCache::kStrategyScoreMin;
|
| +constexpr int GrResourceCache::kStrategyScoreMax;
|
| +constexpr int GrResourceCache::kInitialStrategyScore;
|
|
|
| GrResourceCache::GrResourceCache(const GrCaps* caps)
|
| : fTimestamp(0)
|
| , fMaxCount(kDefaultMaxCount)
|
| , fMaxBytes(kDefaultMaxSize)
|
| , fMaxUnusedFlushes(kDefaultMaxUnusedFlushes)
|
| + , fStrategy(ReplacementStrategy::kLRU)
|
| + , fStrategyScore(kInitialStrategyScore)
|
| + , fTotalMissesThisFlush(0)
|
| + , fMissesThisFlushPurgedRecently(0)
|
| + , fUniqueKeysPurgedThisFlushStorage {new SkChunkAlloc(8*sizeof(GrUniqueKey)),
|
| + new SkChunkAlloc(8*sizeof(GrUniqueKey))}
|
| + , fFlushParity(0)
|
| #if GR_CACHE_STATS
|
| , fHighWaterCount(0)
|
| , fHighWaterBytes(0)
|
| @@ -73,8 +82,8 @@ GrResourceCache::GrResourceCache(const GrCaps* caps)
|
| , fBytes(0)
|
| , fBudgetedCount(0)
|
| , fBudgetedBytes(0)
|
| - , fRequestFlush(false)
|
| , fExternalFlushCnt(0)
|
| + , fIsPurging(false)
|
| , fPreferVRAMUseOverFlushes(caps->preferVRAMUseOverFlushes()) {
|
| SkDEBUGCODE(fCount = 0;)
|
| SkDEBUGCODE(fNewlyPurgeableResourceForValidation = nullptr;)
|
| @@ -82,6 +91,8 @@ GrResourceCache::GrResourceCache(const GrCaps* caps)
|
|
|
| GrResourceCache::~GrResourceCache() {
|
| this->releaseAll();
|
| + delete fUniqueKeysPurgedThisFlushStorage[0];
|
| + delete fUniqueKeysPurgedThisFlushStorage[1];
|
| }
|
|
|
| void GrResourceCache::setLimits(int count, size_t bytes, int maxUnusedFlushes) {
|
| @@ -227,8 +238,10 @@ private:
|
| GrGpuResource* GrResourceCache::findAndRefScratchResource(const GrScratchKey& scratchKey,
|
| size_t resourceSize,
|
| uint32_t flags) {
|
| + // We don't currently track misses for scratch resources for selecting the replacement policy.
|
| + // The reason is that it is common to look for a scratch resource before creating a texture
|
| + // that will immediately become uniquely keyed.
|
| SkASSERT(scratchKey.isValid());
|
| -
|
| GrGpuResource* resource;
|
| if (flags & (kPreferNoPendingIO_ScratchFlag | kRequireNoPendingIO_ScratchFlag)) {
|
| resource = fScratchMap.find(scratchKey, AvailableForScratchUse(true));
|
| @@ -256,6 +269,25 @@ GrGpuResource* GrResourceCache::findAndRefScratchResource(const GrScratchKey& sc
|
| return resource;
|
| }
|
|
|
| +GrGpuResource* GrResourceCache::findAndRefUniqueResource(const GrUniqueKey& key) {
|
| + GrGpuResource* resource = fUniqueHash.find(key);
|
| + if (resource) {
|
| + this->refAndMakeResourceMRU(resource);
|
| + } else {
|
| + this->recordKeyMiss(key);
|
| + }
|
| + return resource;
|
| +}
|
| +
|
| +void GrResourceCache::recordKeyMiss(const GrUniqueKey& key) {
|
| + // If a resource with this key was purged either this flush or the previous flush, consider it
|
| + // a recent purge.
|
| + if (fUniqueKeysPurgedThisFlush[0].find(key) || fUniqueKeysPurgedThisFlush[1].find(key)) {
|
| + ++fMissesThisFlushPurgedRecently;
|
| + }
|
| + ++fTotalMissesThisFlush;
|
| +}
|
| +
|
| void GrResourceCache::willRemoveScratchKey(const GrGpuResource* resource) {
|
| SkASSERT(resource->resourcePriv().getScratchKey().isValid());
|
| if (!resource->getUniqueKey().isValid()) {
|
| @@ -380,9 +412,12 @@ void GrResourceCache::notifyCntReachedZero(GrGpuResource* resource, uint32_t fla
|
| } else {
|
| // Purge the resource immediately if we're over budget
|
| // Also purge if the resource has neither a valid scratch key nor a unique key.
|
| - bool noKey = !resource->resourcePriv().getScratchKey().isValid() &&
|
| - !resource->getUniqueKey().isValid();
|
| - if (!this->overBudget() && !noKey) {
|
| + bool hasKey = resource->resourcePriv().getScratchKey().isValid() ||
|
| + resource->getUniqueKey().isValid();
|
| + if (hasKey) {
|
| + if (this->overBudget()) {
|
| + this->purgeAsNeeded();
|
| + }
|
| return;
|
| }
|
| }
|
| @@ -442,7 +477,36 @@ void GrResourceCache::didChangeBudgetStatus(GrGpuResource* resource) {
|
| this->validate();
|
| }
|
|
|
| -void GrResourceCache::purgeAsNeeded() {
|
| +void GrResourceCache::recordPurgedKey(GrGpuResource* resource) {
|
| + // This maximum exists to avoid allocating too much space for key tracking.
|
| + static constexpr int kMaxTrackedKeys = 256;
|
| + if (fUniqueKeysPurgedThisFlush[fFlushParity].count() >= kMaxTrackedKeys) {
|
| + return;
|
| + }
|
| + if (resource->getUniqueKey().isValid() &&
|
| + !fUniqueKeysPurgedThisFlush[fFlushParity].find(resource->getUniqueKey())) {
|
| + void* p = fUniqueKeysPurgedThisFlushStorage[fFlushParity]->allocThrow(sizeof(GrUniqueKey));
|
| + GrUniqueKey* copy = new (p) GrUniqueKey;
|
| + *copy = resource->getUniqueKey();
|
| + fUniqueKeysPurgedThisFlush[fFlushParity].add(copy);
|
| + }
|
| +}
|
| +
|
| +GrGpuResource* GrResourceCache::selectResourceUsingStrategy() {
|
| + switch (fStrategy) {
|
| + case ReplacementStrategy::kLRU:
|
| + return fPurgeableQueue.peek();
|
| + case ReplacementStrategy::kRandom:
|
| + return fPurgeableQueue.at(fRandom.nextULessThan(fPurgeableQueue.count()));
|
| + }
|
| + return nullptr;
|
| +}
|
| +
|
| +void GrResourceCache::internalPurgeAsNeeded(bool fromFlushNotification) {
|
| + if (fIsPurging) {
|
| + return;
|
| + }
|
| + fIsPurging = true;
|
| SkTArray<GrUniqueKeyInvalidatedMessage> invalidKeyMsgs;
|
| fInvalidUniqueKeyInbox.poll(&invalidKeyMsgs);
|
| if (invalidKeyMsgs.count()) {
|
| @@ -470,26 +534,31 @@ void GrResourceCache::purgeAsNeeded() {
|
| }
|
| GrGpuResource* resource = fPurgeableQueue.peek();
|
| SkASSERT(resource->isPurgeable());
|
| + this->recordPurgedKey(resource);
|
| resource->cacheAccess().release();
|
| }
|
| }
|
| }
|
|
|
| + if (ReplacementStrategy::kRandom == fStrategy && !fromFlushNotification) {
|
| + // Wait until after the requested flush when all the pending IO resources will be eligible
|
| + // for the draft.
|
| + SkASSERT(!this->overBudget() || this->requestsFlush());
|
| + fIsPurging = false;
|
| + return;
|
| + }
|
| +
|
| bool stillOverbudget = this->overBudget();
|
| while (stillOverbudget && fPurgeableQueue.count()) {
|
| - GrGpuResource* resource = fPurgeableQueue.peek();
|
| + GrGpuResource* resource = this->selectResourceUsingStrategy();
|
| SkASSERT(resource->isPurgeable());
|
| + this->recordPurgedKey(resource);
|
| resource->cacheAccess().release();
|
| stillOverbudget = this->overBudget();
|
| }
|
|
|
| this->validate();
|
| -
|
| - if (stillOverbudget) {
|
| - // Set this so that GrDrawingManager will issue a flush to free up resources with pending
|
| - // IO that we were unable to purge in this pass.
|
| - fRequestFlush = true;
|
| - }
|
| + fIsPurging = false;
|
| }
|
|
|
| void GrResourceCache::purgeAllUnlocked() {
|
| @@ -549,7 +618,6 @@ uint32_t GrResourceCache::getNextTimestamp() {
|
| *sortedPurgeableResources.append() = fPurgeableQueue.peek();
|
| fPurgeableQueue.pop();
|
| }
|
| -
|
| SkTQSort(fNonpurgeableResources.begin(), fNonpurgeableResources.end() - 1,
|
| CompareTimestamp);
|
|
|
| @@ -600,10 +668,25 @@ void GrResourceCache::notifyFlushOccurred(FlushType type) {
|
| case FlushType::kImmediateMode:
|
| break;
|
| case FlushType::kCacheRequested:
|
| - SkASSERT(fRequestFlush);
|
| - fRequestFlush = false;
|
| break;
|
| - case FlushType::kExternal:
|
| + case FlushType::kExternal: {
|
| + int scoreDelta = 1;
|
| + if (fMissesThisFlushPurgedRecently) {
|
| + // If > 60% of our cache misses were things we purged in the last two flushes
|
| + // then we move closer towards selecting random replacement.
|
| + if ((float)fMissesThisFlushPurgedRecently / fTotalMissesThisFlush > 0.6f) {
|
| + scoreDelta = -1;
|
| + }
|
| + }
|
| + fStrategyScore = SkTPin(fStrategyScore + scoreDelta, kStrategyScoreMin,
|
| + kStrategyScoreMax);
|
| + fStrategy = fStrategyScore < 0 ? ReplacementStrategy::kRandom
|
| + : ReplacementStrategy::kLRU;
|
| + fMissesThisFlushPurgedRecently = 0;
|
| + fTotalMissesThisFlush = 0;
|
| + fFlushParity = -(fFlushParity - 1);
|
| + fUniqueKeysPurgedThisFlush[fFlushParity].reset();
|
| + fUniqueKeysPurgedThisFlushStorage[fFlushParity]->rewind();
|
| ++fExternalFlushCnt;
|
| if (0 == fExternalFlushCnt) {
|
| // When this wraps just reset all the purgeable resources' last used flush state.
|
| @@ -612,8 +695,9 @@ void GrResourceCache::notifyFlushOccurred(FlushType type) {
|
| }
|
| }
|
| break;
|
| + }
|
| }
|
| - this->purgeAsNeeded();
|
| + this->internalPurgeAsNeeded(true);
|
| }
|
|
|
| void GrResourceCache::dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const {
|
|
|