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:
robertphillips 2016-10-04 05:30:20 -07:00 committed by Commit bot
parent 2ebd0c80a2
commit ee843b2ae4
5 changed files with 37 additions and 173 deletions

View File

@ -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);
}

View File

@ -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();

View File

@ -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;
};

View File

@ -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 {

View File

@ -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