Revert of Make GrResourceCache dynamically change between LRU and random replacement strategies. (patchset #8 id:140001 of https://codereview.chromium.org/2321563006/ )
Reason for revert: Causing problems on Mac & Windows bots. Original issue's description: > Make GrResourceCache dynamically change between LRU and random replacement strategies. > > Random performs significantly better when each frame exceeds the budget by a small margin whereas LRU has worst case behavior. > > The decision of which to use is made based on the history from a few frames of the ratio of total unique key cache misses to unique key cache misses of resources purged in the last 2 frames. > GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2321563006 > > Committed: https://skia.googlesource.com/skia/+/0f147ac2ae575bbad3515a526f13700bc5c8e9d7 TBR=bsalomon@google.com # Skipping CQ checks because original CL landed less than 1 days ago. NOPRESUBMIT=true NOTREECHECKS=true NOTRY=true Review-Url: https://codereview.chromium.org/2386993004
This commit is contained in:
parent
2ebd0c80a2
commit
ee843b2ae4
@ -58,7 +58,6 @@ public:
|
||||
this->validate();
|
||||
|
||||
if (!(--fRefCnt)) {
|
||||
SkASSERT(fRefCnt >= 0);
|
||||
if (!static_cast<const DERIVED*>(this)->notifyRefCountIsZero()) {
|
||||
return;
|
||||
}
|
||||
@ -118,7 +117,6 @@ private:
|
||||
|
||||
private:
|
||||
void didRemoveRefOrPendingIO(CntType cntTypeRemoved) const {
|
||||
this->validate();
|
||||
if (0 == fPendingReads && 0 == fPendingWrites && 0 == fRefCnt) {
|
||||
static_cast<const DERIVED*>(this)->notifyAllCntsAreZero(cntTypeRemoved);
|
||||
}
|
||||
|
@ -129,17 +129,10 @@ void GrDrawingManager::internalFlush(GrResourceCache::FlushType type) {
|
||||
#endif
|
||||
|
||||
fFlushState.reset();
|
||||
// Avoid notifying the cache about successive client flushes where no rendering occurred between
|
||||
// them.
|
||||
bool skipNotify = false;
|
||||
if (!flushed) {
|
||||
skipNotify = GrResourceCache::kExternal == fLastFlushType &&
|
||||
GrResourceCache::kExternal == type;
|
||||
}
|
||||
if (!skipNotify) {
|
||||
// We always have to notify the cache when it requested a flush so it can reset its state.
|
||||
if (flushed || type == GrResourceCache::FlushType::kCacheRequested) {
|
||||
fContext->getResourceCache()->notifyFlushOccurred(type);
|
||||
}
|
||||
fLastFlushType = type;
|
||||
fFlushing = false;
|
||||
}
|
||||
|
||||
@ -152,9 +145,6 @@ void GrDrawingManager::prepareSurfaceForExternalIO(GrSurface* surface) {
|
||||
|
||||
if (surface->surfacePriv().hasPendingIO()) {
|
||||
this->flush();
|
||||
} else if (GrResourceCache::kExternal != fLastFlushType) {
|
||||
fContext->getResourceCache()->notifyFlushOccurred(GrResourceCache::kExternal);
|
||||
fLastFlushType = GrResourceCache::kExternal;
|
||||
}
|
||||
|
||||
GrRenderTarget* rt = surface->asRenderTarget();
|
||||
|
@ -76,7 +76,6 @@ private:
|
||||
, fSoftwarePathRenderer(nullptr)
|
||||
, fFlushState(context->getGpu(), context->resourceProvider())
|
||||
, fFlushing(false)
|
||||
, fLastFlushType(GrResourceCache::FlushType::kExternal)
|
||||
, fIsImmediateMode(isImmediateMode) {
|
||||
}
|
||||
|
||||
@ -108,7 +107,6 @@ private:
|
||||
|
||||
GrBatchFlushState fFlushState;
|
||||
bool fFlushing;
|
||||
GrResourceCache::FlushType fLastFlushType;
|
||||
|
||||
bool fIsImmediateMode;
|
||||
};
|
||||
|
@ -57,22 +57,13 @@ 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)
|
||||
@ -82,8 +73,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;)
|
||||
@ -91,8 +82,6 @@ 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) {
|
||||
@ -238,10 +227,8 @@ 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));
|
||||
@ -269,25 +256,6 @@ 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()) {
|
||||
@ -412,12 +380,9 @@ 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 hasKey = resource->resourcePriv().getScratchKey().isValid() ||
|
||||
resource->getUniqueKey().isValid();
|
||||
if (hasKey) {
|
||||
if (this->overBudget()) {
|
||||
this->purgeAsNeeded();
|
||||
}
|
||||
bool noKey = !resource->resourcePriv().getScratchKey().isValid() &&
|
||||
!resource->getUniqueKey().isValid();
|
||||
if (!this->overBudget() && !noKey) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -477,36 +442,7 @@ void GrResourceCache::didChangeBudgetStatus(GrGpuResource* resource) {
|
||||
this->validate();
|
||||
}
|
||||
|
||||
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;
|
||||
void GrResourceCache::purgeAsNeeded() {
|
||||
SkTArray<GrUniqueKeyInvalidatedMessage> invalidKeyMsgs;
|
||||
fInvalidUniqueKeyInbox.poll(&invalidKeyMsgs);
|
||||
if (invalidKeyMsgs.count()) {
|
||||
@ -534,31 +470,26 @@ void GrResourceCache::internalPurgeAsNeeded(bool fromFlushNotification) {
|
||||
}
|
||||
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 = this->selectResourceUsingStrategy();
|
||||
GrGpuResource* resource = fPurgeableQueue.peek();
|
||||
SkASSERT(resource->isPurgeable());
|
||||
this->recordPurgedKey(resource);
|
||||
resource->cacheAccess().release();
|
||||
stillOverbudget = this->overBudget();
|
||||
}
|
||||
|
||||
this->validate();
|
||||
fIsPurging = false;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void GrResourceCache::purgeAllUnlocked() {
|
||||
@ -618,6 +549,7 @@ uint32_t GrResourceCache::getNextTimestamp() {
|
||||
*sortedPurgeableResources.append() = fPurgeableQueue.peek();
|
||||
fPurgeableQueue.pop();
|
||||
}
|
||||
|
||||
SkTQSort(fNonpurgeableResources.begin(), fNonpurgeableResources.end() - 1,
|
||||
CompareTimestamp);
|
||||
|
||||
@ -668,25 +600,10 @@ void GrResourceCache::notifyFlushOccurred(FlushType type) {
|
||||
case FlushType::kImmediateMode:
|
||||
break;
|
||||
case FlushType::kCacheRequested:
|
||||
SkASSERT(fRequestFlush);
|
||||
fRequestFlush = false;
|
||||
break;
|
||||
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();
|
||||
case FlushType::kExternal:
|
||||
++fExternalFlushCnt;
|
||||
if (0 == fExternalFlushCnt) {
|
||||
// When this wraps just reset all the purgeable resources' last used flush state.
|
||||
@ -695,9 +612,8 @@ void GrResourceCache::notifyFlushOccurred(FlushType type) {
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
this->internalPurgeAsNeeded(true);
|
||||
this->purgeAsNeeded();
|
||||
}
|
||||
|
||||
void GrResourceCache::dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const {
|
||||
|
@ -13,9 +13,7 @@
|
||||
#include "GrGpuResourcePriv.h"
|
||||
#include "GrResourceCache.h"
|
||||
#include "GrResourceKey.h"
|
||||
#include "SkChunkAlloc.h"
|
||||
#include "SkMessageBus.h"
|
||||
#include "SkRandom.h"
|
||||
#include "SkRefCnt.h"
|
||||
#include "SkTArray.h"
|
||||
#include "SkTDPQueue.h"
|
||||
@ -139,7 +137,13 @@ public:
|
||||
/**
|
||||
* Find a resource that matches a unique key.
|
||||
*/
|
||||
GrGpuResource* findAndRefUniqueResource(const GrUniqueKey& key);
|
||||
GrGpuResource* findAndRefUniqueResource(const GrUniqueKey& key) {
|
||||
GrGpuResource* resource = fUniqueHash.find(key);
|
||||
if (resource) {
|
||||
this->refAndMakeResourceMRU(resource);
|
||||
}
|
||||
return resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query whether a unique key exists in the cache.
|
||||
@ -150,19 +154,14 @@ public:
|
||||
|
||||
/** Purges resources to become under budget and processes resources with invalidated unique
|
||||
keys. */
|
||||
void purgeAsNeeded() { this->internalPurgeAsNeeded(false); }
|
||||
void purgeAsNeeded();
|
||||
|
||||
/** Purges all resources that don't have external owners. */
|
||||
void purgeAllUnlocked();
|
||||
|
||||
/** Returns true if the cache would like a flush to occur in order to make more resources
|
||||
purgeable. */
|
||||
bool requestsFlush() const {
|
||||
// When in random replacement mode we request a flush in order to make as many resources
|
||||
// as possible subject to replacement.
|
||||
return this->overBudget() && (ReplacementStrategy::kRandom == fStrategy ||
|
||||
0 == fPurgeableQueue.count());
|
||||
}
|
||||
bool requestsFlush() const { return fRequestFlush; }
|
||||
|
||||
enum FlushType {
|
||||
kExternal,
|
||||
@ -233,14 +232,10 @@ private:
|
||||
void refAndMakeResourceMRU(GrGpuResource*);
|
||||
/// @}
|
||||
|
||||
void internalPurgeAsNeeded(bool fromFlushNotification);
|
||||
void processInvalidUniqueKeys(const SkTArray<GrUniqueKeyInvalidatedMessage>&);
|
||||
void addToNonpurgeableArray(GrGpuResource*);
|
||||
void removeFromNonpurgeableArray(GrGpuResource*);
|
||||
bool overBudget() const { return fBudgetedBytes > fMaxBytes || fBudgetedCount > fMaxCount; }
|
||||
GrGpuResource* selectResourceUsingStrategy();
|
||||
void recordPurgedKey(GrGpuResource*);
|
||||
void recordKeyMiss(const GrUniqueKey&);
|
||||
|
||||
bool wouldFit(size_t bytes) {
|
||||
return fBudgetedBytes+bytes <= fMaxBytes && fBudgetedCount+1 <= fMaxCount;
|
||||
@ -259,27 +254,22 @@ private:
|
||||
|
||||
class AvailableForScratchUse;
|
||||
|
||||
struct HashTraitsBase {
|
||||
static uint32_t Hash(const GrResourceKey& key) { return key.hash(); }
|
||||
};
|
||||
|
||||
struct ScratchMapTraits : public HashTraitsBase {
|
||||
struct ScratchMapTraits {
|
||||
static const GrScratchKey& GetKey(const GrGpuResource& r) {
|
||||
return r.resourcePriv().getScratchKey();
|
||||
}
|
||||
|
||||
static uint32_t Hash(const GrScratchKey& key) { return key.hash(); }
|
||||
};
|
||||
typedef SkTMultiMap<GrGpuResource, GrScratchKey, ScratchMapTraits> ScratchMap;
|
||||
|
||||
struct UniqueHashTraits : public HashTraitsBase {
|
||||
struct UniqueHashTraits {
|
||||
static const GrUniqueKey& GetKey(const GrGpuResource& r) { return r.getUniqueKey(); }
|
||||
|
||||
static uint32_t Hash(const GrUniqueKey& key) { return key.hash(); }
|
||||
};
|
||||
typedef SkTDynamicHash<GrGpuResource, GrUniqueKey, UniqueHashTraits> UniqueHash;
|
||||
|
||||
struct UniqueSetTraits : public HashTraitsBase {
|
||||
static const GrUniqueKey& GetKey(const GrUniqueKey& key) { return key; }
|
||||
};
|
||||
typedef SkTDynamicHash<GrUniqueKey, GrUniqueKey, UniqueSetTraits> UniqueKeySet;
|
||||
|
||||
static bool CompareTimestamp(GrGpuResource* const& a, GrGpuResource* const& b) {
|
||||
return a->cacheAccess().timestamp() < b->cacheAccess().timestamp();
|
||||
}
|
||||
@ -288,22 +278,6 @@ private:
|
||||
return res->cacheAccess().accessCacheIndex();
|
||||
}
|
||||
|
||||
/**
|
||||
* The resource cache chooses one of these replacement strategies based on a "strategy score"
|
||||
* updated after each external flush based on unique key cache misses.
|
||||
*/
|
||||
enum class ReplacementStrategy {
|
||||
kLRU,
|
||||
kRandom
|
||||
};
|
||||
/**
|
||||
* When the current strategy score is >=0 LRU is chosen, when it is < 0 random is chosen. The
|
||||
* absolute value of the score moves by 1 each flush.
|
||||
*/
|
||||
static constexpr int kStrategyScoreMin = -5;
|
||||
static constexpr int kStrategyScoreMax = 4;
|
||||
static constexpr int kInitialStrategyScore = 2;
|
||||
|
||||
typedef SkMessageBus<GrUniqueKeyInvalidatedMessage>::Inbox InvalidUniqueKeyInbox;
|
||||
typedef SkTDPQueue<GrGpuResource*, CompareTimestamp, AccessResourceIndex> PurgeableQueue;
|
||||
typedef SkTDArray<GrGpuResource*> ResourceArray;
|
||||
@ -325,17 +299,6 @@ private:
|
||||
size_t fMaxBytes;
|
||||
int fMaxUnusedFlushes;
|
||||
|
||||
// Data related to replacement strategy.
|
||||
SkRandom fRandom;
|
||||
ReplacementStrategy fStrategy;
|
||||
int fStrategyScore;
|
||||
int fTotalMissesThisFlush;
|
||||
int fMissesThisFlushPurgedRecently;
|
||||
UniqueKeySet fUniqueKeysPurgedThisFlush[2];
|
||||
// These are pointers to SkChunckAlloc because of gcc bug 63707
|
||||
SkChunkAlloc* fUniqueKeysPurgedThisFlushStorage[2];
|
||||
int fFlushParity;
|
||||
|
||||
#if GR_CACHE_STATS
|
||||
int fHighWaterCount;
|
||||
size_t fHighWaterBytes;
|
||||
@ -351,10 +314,9 @@ private:
|
||||
int fBudgetedCount;
|
||||
size_t fBudgetedBytes;
|
||||
|
||||
bool fRequestFlush;
|
||||
uint32_t fExternalFlushCnt;
|
||||
|
||||
bool fIsPurging;
|
||||
|
||||
InvalidUniqueKeyInbox fInvalidUniqueKeyInbox;
|
||||
|
||||
// This resource is allowed to be in the nonpurgeable array for the sake of validate() because
|
||||
|
Loading…
Reference in New Issue
Block a user