Home | History | Annotate | Download | only in gpu
      1 
      2 /*
      3  * Copyright 2014 Google Inc.
      4  *
      5  * Use of this source code is governed by a BSD-style license that can be
      6  * found in the LICENSE file.
      7  */
      8 
      9 
     10 #include "GrResourceCache.h"
     11 #include "GrGpuResourceCacheAccess.h"
     12 #include "GrTracing.h"
     13 #include "SkChecksum.h"
     14 #include "SkGr.h"
     15 #include "SkMessageBus.h"
     16 #include "SkTSort.h"
     17 
     18 DECLARE_SKMESSAGEBUS_MESSAGE(GrUniqueKeyInvalidatedMessage);
     19 
     20 //////////////////////////////////////////////////////////////////////////////
     21 
     22 GrScratchKey::ResourceType GrScratchKey::GenerateResourceType() {
     23     static int32_t gType = INHERITED::kInvalidDomain + 1;
     24 
     25     int32_t type = sk_atomic_inc(&gType);
     26     if (type > SK_MaxU16) {
     27         SkFAIL("Too many Resource Types");
     28     }
     29 
     30     return static_cast<ResourceType>(type);
     31 }
     32 
     33 GrUniqueKey::Domain GrUniqueKey::GenerateDomain() {
     34     static int32_t gDomain = INHERITED::kInvalidDomain + 1;
     35 
     36     int32_t domain = sk_atomic_inc(&gDomain);
     37     if (domain > SK_MaxU16) {
     38         SkFAIL("Too many GrUniqueKey Domains");
     39     }
     40 
     41     return static_cast<Domain>(domain);
     42 }
     43 
     44 uint32_t GrResourceKeyHash(const uint32_t* data, size_t size) {
     45     return SkChecksum::Murmur3(data, size);
     46 }
     47 
     48 //////////////////////////////////////////////////////////////////////////////
     49 
     50 class GrResourceCache::AutoValidate : ::SkNoncopyable {
     51 public:
     52     AutoValidate(GrResourceCache* cache) : fCache(cache) { cache->validate(); }
     53     ~AutoValidate() { fCache->validate(); }
     54 private:
     55     GrResourceCache* fCache;
     56 };
     57 
     58  //////////////////////////////////////////////////////////////////////////////
     59 
     60 
     61 GrResourceCache::GrResourceCache(const GrCaps* caps)
     62     : fTimestamp(0)
     63     , fMaxCount(kDefaultMaxCount)
     64     , fMaxBytes(kDefaultMaxSize)
     65     , fMaxUnusedFlushes(kDefaultMaxUnusedFlushes)
     66 #if GR_CACHE_STATS
     67     , fHighWaterCount(0)
     68     , fHighWaterBytes(0)
     69     , fBudgetedHighWaterCount(0)
     70     , fBudgetedHighWaterBytes(0)
     71 #endif
     72     , fBytes(0)
     73     , fBudgetedCount(0)
     74     , fBudgetedBytes(0)
     75     , fOverBudgetCB(nullptr)
     76     , fOverBudgetData(nullptr)
     77     , fFlushTimestamps(nullptr)
     78     , fLastFlushTimestampIndex(0)
     79     , fPreferVRAMUseOverFlushes(caps->preferVRAMUseOverFlushes()) {
     80     SkDEBUGCODE(fCount = 0;)
     81     SkDEBUGCODE(fNewlyPurgeableResourceForValidation = nullptr;)
     82     this->resetFlushTimestamps();
     83 }
     84 
     85 GrResourceCache::~GrResourceCache() {
     86     this->releaseAll();
     87     delete[] fFlushTimestamps;
     88 }
     89 
     90 void GrResourceCache::setLimits(int count, size_t bytes, int maxUnusedFlushes) {
     91     fMaxCount = count;
     92     fMaxBytes = bytes;
     93     fMaxUnusedFlushes = maxUnusedFlushes;
     94     this->resetFlushTimestamps();
     95     this->purgeAsNeeded();
     96 }
     97 
     98 void GrResourceCache::resetFlushTimestamps() {
     99     delete[] fFlushTimestamps;
    100 
    101     // We assume this number is a power of two when wrapping indices into the timestamp array.
    102     fMaxUnusedFlushes = SkNextPow2(fMaxUnusedFlushes);
    103 
    104     // Since our implementation is to store the timestamps of the last fMaxUnusedFlushes flush calls
    105     // we just turn the feature off if that array would be large.
    106     static const int kMaxSupportedTimestampHistory = 128;
    107 
    108     if (fMaxUnusedFlushes > kMaxSupportedTimestampHistory) {
    109         fFlushTimestamps = nullptr;
    110         return;
    111     }
    112 
    113     fFlushTimestamps = new uint32_t[fMaxUnusedFlushes];
    114     fLastFlushTimestampIndex = 0;
    115     // Set all the historical flush timestamps to initially be at the beginning of time (timestamp
    116     // 0).
    117     sk_bzero(fFlushTimestamps, fMaxUnusedFlushes * sizeof(uint32_t));
    118 }
    119 
    120 void GrResourceCache::insertResource(GrGpuResource* resource) {
    121     SkASSERT(resource);
    122     SkASSERT(!this->isInCache(resource));
    123     SkASSERT(!resource->wasDestroyed());
    124     SkASSERT(!resource->isPurgeable());
    125 
    126     // We must set the timestamp before adding to the array in case the timestamp wraps and we wind
    127     // up iterating over all the resources that already have timestamps.
    128     resource->cacheAccess().setTimestamp(this->getNextTimestamp());
    129 
    130     this->addToNonpurgeableArray(resource);
    131 
    132     size_t size = resource->gpuMemorySize();
    133     SkDEBUGCODE(++fCount;)
    134     fBytes += size;
    135 #if GR_CACHE_STATS
    136     fHighWaterCount = SkTMax(this->getResourceCount(), fHighWaterCount);
    137     fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes);
    138 #endif
    139     if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
    140         ++fBudgetedCount;
    141         fBudgetedBytes += size;
    142         TRACE_COUNTER2(TRACE_DISABLED_BY_DEFAULT("skia.gpu.cache"), "skia budget", "used",
    143                        fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
    144 #if GR_CACHE_STATS
    145         fBudgetedHighWaterCount = SkTMax(fBudgetedCount, fBudgetedHighWaterCount);
    146         fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
    147 #endif
    148     }
    149     if (resource->resourcePriv().getScratchKey().isValid()) {
    150         SkASSERT(!resource->cacheAccess().isExternal());
    151         fScratchMap.insert(resource->resourcePriv().getScratchKey(), resource);
    152     }
    153 
    154     this->purgeAsNeeded();
    155 }
    156 
    157 void GrResourceCache::removeResource(GrGpuResource* resource) {
    158     this->validate();
    159     SkASSERT(this->isInCache(resource));
    160 
    161     if (resource->isPurgeable()) {
    162         fPurgeableQueue.remove(resource);
    163     } else {
    164         this->removeFromNonpurgeableArray(resource);
    165     }
    166 
    167     size_t size = resource->gpuMemorySize();
    168     SkDEBUGCODE(--fCount;)
    169     fBytes -= size;
    170     if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
    171         --fBudgetedCount;
    172         fBudgetedBytes -= size;
    173         TRACE_COUNTER2(TRACE_DISABLED_BY_DEFAULT("skia.gpu.cache"), "skia budget", "used",
    174                        fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
    175     }
    176 
    177     if (resource->resourcePriv().getScratchKey().isValid()) {
    178         fScratchMap.remove(resource->resourcePriv().getScratchKey(), resource);
    179     }
    180     if (resource->getUniqueKey().isValid()) {
    181         fUniqueHash.remove(resource->getUniqueKey());
    182     }
    183     this->validate();
    184 }
    185 
    186 void GrResourceCache::abandonAll() {
    187     AutoValidate av(this);
    188 
    189     while (fNonpurgeableResources.count()) {
    190         GrGpuResource* back = *(fNonpurgeableResources.end() - 1);
    191         SkASSERT(!back->wasDestroyed());
    192         back->cacheAccess().abandon();
    193     }
    194 
    195     while (fPurgeableQueue.count()) {
    196         GrGpuResource* top = fPurgeableQueue.peek();
    197         SkASSERT(!top->wasDestroyed());
    198         top->cacheAccess().abandon();
    199     }
    200 
    201     SkASSERT(!fScratchMap.count());
    202     SkASSERT(!fUniqueHash.count());
    203     SkASSERT(!fCount);
    204     SkASSERT(!this->getResourceCount());
    205     SkASSERT(!fBytes);
    206     SkASSERT(!fBudgetedCount);
    207     SkASSERT(!fBudgetedBytes);
    208 }
    209 
    210 void GrResourceCache::releaseAll() {
    211     AutoValidate av(this);
    212 
    213     while(fNonpurgeableResources.count()) {
    214         GrGpuResource* back = *(fNonpurgeableResources.end() - 1);
    215         SkASSERT(!back->wasDestroyed());
    216         back->cacheAccess().release();
    217     }
    218 
    219     while (fPurgeableQueue.count()) {
    220         GrGpuResource* top = fPurgeableQueue.peek();
    221         SkASSERT(!top->wasDestroyed());
    222         top->cacheAccess().release();
    223     }
    224 
    225     SkASSERT(!fScratchMap.count());
    226     SkASSERT(!fUniqueHash.count());
    227     SkASSERT(!fCount);
    228     SkASSERT(!this->getResourceCount());
    229     SkASSERT(!fBytes);
    230     SkASSERT(!fBudgetedCount);
    231     SkASSERT(!fBudgetedBytes);
    232 }
    233 
    234 class GrResourceCache::AvailableForScratchUse {
    235 public:
    236     AvailableForScratchUse(bool rejectPendingIO) : fRejectPendingIO(rejectPendingIO) { }
    237 
    238     bool operator()(const GrGpuResource* resource) const {
    239         if (resource->internalHasRef() || !resource->cacheAccess().isScratch()) {
    240             return false;
    241         }
    242         return !fRejectPendingIO || !resource->internalHasPendingIO();
    243     }
    244 
    245 private:
    246     bool fRejectPendingIO;
    247 };
    248 
    249 GrGpuResource* GrResourceCache::findAndRefScratchResource(const GrScratchKey& scratchKey,
    250                                                           size_t resourceSize,
    251                                                           uint32_t flags) {
    252     SkASSERT(scratchKey.isValid());
    253 
    254     GrGpuResource* resource;
    255     if (flags & (kPreferNoPendingIO_ScratchFlag | kRequireNoPendingIO_ScratchFlag)) {
    256         resource = fScratchMap.find(scratchKey, AvailableForScratchUse(true));
    257         if (resource) {
    258             this->refAndMakeResourceMRU(resource);
    259             this->validate();
    260             return resource;
    261         } else if (flags & kRequireNoPendingIO_ScratchFlag) {
    262             return nullptr;
    263         }
    264         // We would prefer to consume more available VRAM rather than flushing
    265         // immediately, but on ANGLE this can lead to starving of the GPU.
    266         if (fPreferVRAMUseOverFlushes && this->wouldFit(resourceSize)) {
    267             // kPrefer is specified, we didn't find a resource without pending io,
    268             // but there is still space in our budget for the resource so force
    269             // the caller to allocate a new resource.
    270             return nullptr;
    271         }
    272     }
    273     resource = fScratchMap.find(scratchKey, AvailableForScratchUse(false));
    274     if (resource) {
    275         this->refAndMakeResourceMRU(resource);
    276         this->validate();
    277     }
    278     return resource;
    279 }
    280 
    281 void GrResourceCache::willRemoveScratchKey(const GrGpuResource* resource) {
    282     SkASSERT(resource->resourcePriv().getScratchKey().isValid());
    283     fScratchMap.remove(resource->resourcePriv().getScratchKey(), resource);
    284 }
    285 
    286 void GrResourceCache::removeUniqueKey(GrGpuResource* resource) {
    287     // Someone has a ref to this resource in order to have removed the key. When the ref count
    288     // reaches zero we will get a ref cnt notification and figure out what to do with it.
    289     if (resource->getUniqueKey().isValid()) {
    290         SkASSERT(resource == fUniqueHash.find(resource->getUniqueKey()));
    291         fUniqueHash.remove(resource->getUniqueKey());
    292     }
    293     resource->cacheAccess().removeUniqueKey();
    294     this->validate();
    295 }
    296 
    297 void GrResourceCache::changeUniqueKey(GrGpuResource* resource, const GrUniqueKey& newKey) {
    298     SkASSERT(resource);
    299     SkASSERT(this->isInCache(resource));
    300 
    301     // Remove the entry for this resource if it already has a unique key.
    302     if (resource->getUniqueKey().isValid()) {
    303         SkASSERT(resource == fUniqueHash.find(resource->getUniqueKey()));
    304         fUniqueHash.remove(resource->getUniqueKey());
    305         SkASSERT(nullptr == fUniqueHash.find(resource->getUniqueKey()));
    306     }
    307 
    308     // If another resource has the new key, remove its key then install the key on this resource.
    309     if (newKey.isValid()) {
    310         if (GrGpuResource* old = fUniqueHash.find(newKey)) {
    311             // If the old resource using the key is purgeable and is unreachable, then remove it.
    312             if (!old->resourcePriv().getScratchKey().isValid() && old->isPurgeable()) {
    313                 // release may call validate() which will assert that resource is in fUniqueHash
    314                 // if it has a valid key. So in debug reset the key here before we assign it.
    315                 SkDEBUGCODE(resource->cacheAccess().removeUniqueKey();)
    316                 old->cacheAccess().release();
    317             } else {
    318                 fUniqueHash.remove(newKey);
    319                 old->cacheAccess().removeUniqueKey();
    320             }
    321         }
    322         SkASSERT(nullptr == fUniqueHash.find(newKey));
    323         resource->cacheAccess().setUniqueKey(newKey);
    324         fUniqueHash.add(resource);
    325     } else {
    326         resource->cacheAccess().removeUniqueKey();
    327     }
    328 
    329     this->validate();
    330 }
    331 
    332 void GrResourceCache::refAndMakeResourceMRU(GrGpuResource* resource) {
    333     SkASSERT(resource);
    334     SkASSERT(this->isInCache(resource));
    335 
    336     if (resource->isPurgeable()) {
    337         // It's about to become unpurgeable.
    338         fPurgeableQueue.remove(resource);
    339         this->addToNonpurgeableArray(resource);
    340     }
    341     resource->ref();
    342 
    343     resource->cacheAccess().setTimestamp(this->getNextTimestamp());
    344     this->validate();
    345 }
    346 
    347 void GrResourceCache::notifyCntReachedZero(GrGpuResource* resource, uint32_t flags) {
    348     SkASSERT(resource);
    349     SkASSERT(!resource->wasDestroyed());
    350     SkASSERT(flags);
    351     SkASSERT(this->isInCache(resource));
    352     // This resource should always be in the nonpurgeable array when this function is called. It
    353     // will be moved to the queue if it is newly purgeable.
    354     SkASSERT(fNonpurgeableResources[*resource->cacheAccess().accessCacheIndex()] == resource);
    355 
    356     if (SkToBool(ResourceAccess::kRefCntReachedZero_RefNotificationFlag & flags)) {
    357 #ifdef SK_DEBUG
    358         // When the timestamp overflows validate() is called. validate() checks that resources in
    359         // the nonpurgeable array are indeed not purgeable. However, the movement from the array to
    360         // the purgeable queue happens just below in this function. So we mark it as an exception.
    361         if (resource->isPurgeable()) {
    362             fNewlyPurgeableResourceForValidation = resource;
    363         }
    364 #endif
    365         resource->cacheAccess().setTimestamp(this->getNextTimestamp());
    366         SkDEBUGCODE(fNewlyPurgeableResourceForValidation = nullptr);
    367     }
    368 
    369     if (!SkToBool(ResourceAccess::kAllCntsReachedZero_RefNotificationFlag & flags)) {
    370         SkASSERT(!resource->isPurgeable());
    371         return;
    372     }
    373 
    374     SkASSERT(resource->isPurgeable());
    375     this->removeFromNonpurgeableArray(resource);
    376     fPurgeableQueue.insert(resource);
    377 
    378     if (SkBudgeted::kNo == resource->resourcePriv().isBudgeted()) {
    379         // Check whether this resource could still be used as a scratch resource.
    380         if (!resource->cacheAccess().isExternal() &&
    381             resource->resourcePriv().getScratchKey().isValid()) {
    382             // We won't purge an existing resource to make room for this one.
    383             if (fBudgetedCount < fMaxCount &&
    384                 fBudgetedBytes + resource->gpuMemorySize() <= fMaxBytes) {
    385                 resource->resourcePriv().makeBudgeted();
    386                 return;
    387             }
    388         }
    389     } else {
    390         // Purge the resource immediately if we're over budget
    391         // Also purge if the resource has neither a valid scratch key nor a unique key.
    392         bool noKey = !resource->resourcePriv().getScratchKey().isValid() &&
    393                      !resource->getUniqueKey().isValid();
    394         if (!this->overBudget() && !noKey) {
    395             return;
    396         }
    397     }
    398 
    399     SkDEBUGCODE(int beforeCount = this->getResourceCount();)
    400     resource->cacheAccess().release();
    401     // We should at least free this resource, perhaps dependent resources as well.
    402     SkASSERT(this->getResourceCount() < beforeCount);
    403     this->validate();
    404 }
    405 
    406 void GrResourceCache::didChangeGpuMemorySize(const GrGpuResource* resource, size_t oldSize) {
    407     // SkASSERT(!fPurging); GrPathRange increases size during flush. :(
    408     SkASSERT(resource);
    409     SkASSERT(this->isInCache(resource));
    410 
    411     ptrdiff_t delta = resource->gpuMemorySize() - oldSize;
    412 
    413     fBytes += delta;
    414 #if GR_CACHE_STATS
    415     fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes);
    416 #endif
    417     if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
    418         fBudgetedBytes += delta;
    419         TRACE_COUNTER2(TRACE_DISABLED_BY_DEFAULT("skia.gpu.cache"), "skia budget", "used",
    420                        fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
    421 #if GR_CACHE_STATS
    422         fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
    423 #endif
    424     }
    425 
    426     this->purgeAsNeeded();
    427     this->validate();
    428 }
    429 
    430 void GrResourceCache::didChangeBudgetStatus(GrGpuResource* resource) {
    431     SkASSERT(resource);
    432     SkASSERT(this->isInCache(resource));
    433 
    434     size_t size = resource->gpuMemorySize();
    435 
    436     if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
    437         ++fBudgetedCount;
    438         fBudgetedBytes += size;
    439 #if GR_CACHE_STATS
    440         fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
    441         fBudgetedHighWaterCount = SkTMax(fBudgetedCount, fBudgetedHighWaterCount);
    442 #endif
    443         this->purgeAsNeeded();
    444     } else {
    445         --fBudgetedCount;
    446         fBudgetedBytes -= size;
    447     }
    448     TRACE_COUNTER2(TRACE_DISABLED_BY_DEFAULT("skia.gpu.cache"), "skia budget", "used",
    449                    fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
    450 
    451     this->validate();
    452 }
    453 
    454 void GrResourceCache::purgeAsNeeded() {
    455     SkTArray<GrUniqueKeyInvalidatedMessage> invalidKeyMsgs;
    456     fInvalidUniqueKeyInbox.poll(&invalidKeyMsgs);
    457     if (invalidKeyMsgs.count()) {
    458         this->processInvalidUniqueKeys(invalidKeyMsgs);
    459     }
    460 
    461     if (fFlushTimestamps) {
    462         // Assuming kNumFlushesToDeleteUnusedResource is a power of 2.
    463         SkASSERT(SkIsPow2(fMaxUnusedFlushes));
    464         int oldestFlushIndex = (fLastFlushTimestampIndex + 1) & (fMaxUnusedFlushes - 1);
    465 
    466         uint32_t oldestAllowedTimestamp = fFlushTimestamps[oldestFlushIndex];
    467         while (fPurgeableQueue.count()) {
    468             uint32_t oldestResourceTimestamp = fPurgeableQueue.peek()->cacheAccess().timestamp();
    469             if (oldestAllowedTimestamp < oldestResourceTimestamp) {
    470                 break;
    471             }
    472             GrGpuResource* resource = fPurgeableQueue.peek();
    473             SkASSERT(resource->isPurgeable());
    474             resource->cacheAccess().release();
    475         }
    476     }
    477 
    478     bool stillOverbudget = this->overBudget();
    479     while (stillOverbudget && fPurgeableQueue.count()) {
    480         GrGpuResource* resource = fPurgeableQueue.peek();
    481         SkASSERT(resource->isPurgeable());
    482         resource->cacheAccess().release();
    483         stillOverbudget = this->overBudget();
    484     }
    485 
    486     this->validate();
    487 
    488     if (stillOverbudget) {
    489         // Despite the purge we're still over budget. Call our over budget callback. If this frees
    490         // any resources then we'll get notified and take appropriate action.
    491         (*fOverBudgetCB)(fOverBudgetData);
    492         this->validate();
    493     }
    494 }
    495 
    496 void GrResourceCache::purgeAllUnlocked() {
    497     // We could disable maintaining the heap property here, but it would add a lot of complexity.
    498     // Moreover, this is rarely called.
    499     while (fPurgeableQueue.count()) {
    500         GrGpuResource* resource = fPurgeableQueue.peek();
    501         SkASSERT(resource->isPurgeable());
    502         resource->cacheAccess().release();
    503     }
    504 
    505     this->validate();
    506 }
    507 
    508 void GrResourceCache::processInvalidUniqueKeys(
    509     const SkTArray<GrUniqueKeyInvalidatedMessage>& msgs) {
    510     for (int i = 0; i < msgs.count(); ++i) {
    511         GrGpuResource* resource = this->findAndRefUniqueResource(msgs[i].key());
    512         if (resource) {
    513             resource->resourcePriv().removeUniqueKey();
    514             resource->unref(); // If this resource is now purgeable, the cache will be notified.
    515         }
    516     }
    517 }
    518 
    519 void GrResourceCache::addToNonpurgeableArray(GrGpuResource* resource) {
    520     int index = fNonpurgeableResources.count();
    521     *fNonpurgeableResources.append() = resource;
    522     *resource->cacheAccess().accessCacheIndex() = index;
    523 }
    524 
    525 void GrResourceCache::removeFromNonpurgeableArray(GrGpuResource* resource) {
    526     int* index = resource->cacheAccess().accessCacheIndex();
    527     // Fill the whole we will create in the array with the tail object, adjust its index, and
    528     // then pop the array
    529     GrGpuResource* tail = *(fNonpurgeableResources.end() - 1);
    530     SkASSERT(fNonpurgeableResources[*index] == resource);
    531     fNonpurgeableResources[*index] = tail;
    532     *tail->cacheAccess().accessCacheIndex() = *index;
    533     fNonpurgeableResources.pop();
    534     SkDEBUGCODE(*index = -1);
    535 }
    536 
    537 uint32_t GrResourceCache::getNextTimestamp() {
    538     // If we wrap then all the existing resources will appear older than any resources that get
    539     // a timestamp after the wrap.
    540     if (0 == fTimestamp) {
    541         int count = this->getResourceCount();
    542         if (count) {
    543             // Reset all the timestamps. We sort the resources by timestamp and then assign
    544             // sequential timestamps beginning with 0. This is O(n*lg(n)) but it should be extremely
    545             // rare.
    546             SkTDArray<GrGpuResource*> sortedPurgeableResources;
    547             sortedPurgeableResources.setReserve(fPurgeableQueue.count());
    548 
    549             while (fPurgeableQueue.count()) {
    550                 *sortedPurgeableResources.append() = fPurgeableQueue.peek();
    551                 fPurgeableQueue.pop();
    552             }
    553 
    554             struct Less {
    555                 bool operator()(GrGpuResource* a, GrGpuResource* b) {
    556                     return CompareTimestamp(a,b);
    557                 }
    558             };
    559             Less less;
    560             SkTQSort(fNonpurgeableResources.begin(), fNonpurgeableResources.end() - 1, less);
    561 
    562             // Pick resources out of the purgeable and non-purgeable arrays based on lowest
    563             // timestamp and assign new timestamps.
    564             int currP = 0;
    565             int currNP = 0;
    566             while (currP < sortedPurgeableResources.count() &&
    567                    currNP < fNonpurgeableResources.count()) {
    568                 uint32_t tsP = sortedPurgeableResources[currP]->cacheAccess().timestamp();
    569                 uint32_t tsNP = fNonpurgeableResources[currNP]->cacheAccess().timestamp();
    570                 SkASSERT(tsP != tsNP);
    571                 if (tsP < tsNP) {
    572                     sortedPurgeableResources[currP++]->cacheAccess().setTimestamp(fTimestamp++);
    573                 } else {
    574                     // Correct the index in the nonpurgeable array stored on the resource post-sort.
    575                     *fNonpurgeableResources[currNP]->cacheAccess().accessCacheIndex() = currNP;
    576                     fNonpurgeableResources[currNP++]->cacheAccess().setTimestamp(fTimestamp++);
    577                 }
    578             }
    579 
    580             // The above loop ended when we hit the end of one array. Finish the other one.
    581             while (currP < sortedPurgeableResources.count()) {
    582                 sortedPurgeableResources[currP++]->cacheAccess().setTimestamp(fTimestamp++);
    583             }
    584             while (currNP < fNonpurgeableResources.count()) {
    585                 *fNonpurgeableResources[currNP]->cacheAccess().accessCacheIndex() = currNP;
    586                 fNonpurgeableResources[currNP++]->cacheAccess().setTimestamp(fTimestamp++);
    587             }
    588 
    589             // Rebuild the queue.
    590             for (int i = 0; i < sortedPurgeableResources.count(); ++i) {
    591                 fPurgeableQueue.insert(sortedPurgeableResources[i]);
    592             }
    593 
    594             this->validate();
    595             SkASSERT(count == this->getResourceCount());
    596 
    597             // count should be the next timestamp we return.
    598             SkASSERT(fTimestamp == SkToU32(count));
    599 
    600             // The historical timestamps of flushes are now invalid.
    601             this->resetFlushTimestamps();
    602         }
    603     }
    604     return fTimestamp++;
    605 }
    606 
    607 void GrResourceCache::notifyFlushOccurred() {
    608     if (fFlushTimestamps) {
    609         SkASSERT(SkIsPow2(fMaxUnusedFlushes));
    610         fLastFlushTimestampIndex = (fLastFlushTimestampIndex + 1) & (fMaxUnusedFlushes - 1);
    611         // get the timestamp before accessing fFlushTimestamps because getNextTimestamp will
    612         // reallocate fFlushTimestamps on timestamp overflow.
    613         uint32_t timestamp = this->getNextTimestamp();
    614         fFlushTimestamps[fLastFlushTimestampIndex] = timestamp;
    615         this->purgeAsNeeded();
    616     }
    617 }
    618 
    619 void GrResourceCache::dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const {
    620     for (int i = 0; i < fNonpurgeableResources.count(); ++i) {
    621         fNonpurgeableResources[i]->dumpMemoryStatistics(traceMemoryDump);
    622     }
    623     for (int i = 0; i < fPurgeableQueue.count(); ++i) {
    624         fPurgeableQueue.at(i)->dumpMemoryStatistics(traceMemoryDump);
    625     }
    626 }
    627 
    628 #ifdef SK_DEBUG
    629 void GrResourceCache::validate() const {
    630     // Reduce the frequency of validations for large resource counts.
    631     static SkRandom gRandom;
    632     int mask = (SkNextPow2(fCount + 1) >> 5) - 1;
    633     if (~mask && (gRandom.nextU() & mask)) {
    634         return;
    635     }
    636 
    637     struct Stats {
    638         size_t fBytes;
    639         int fBudgetedCount;
    640         size_t fBudgetedBytes;
    641         int fLocked;
    642         int fScratch;
    643         int fCouldBeScratch;
    644         int fContent;
    645         const ScratchMap* fScratchMap;
    646         const UniqueHash* fUniqueHash;
    647 
    648         Stats(const GrResourceCache* cache) {
    649             memset(this, 0, sizeof(*this));
    650             fScratchMap = &cache->fScratchMap;
    651             fUniqueHash = &cache->fUniqueHash;
    652         }
    653 
    654         void update(GrGpuResource* resource) {
    655             fBytes += resource->gpuMemorySize();
    656 
    657             if (!resource->isPurgeable()) {
    658                 ++fLocked;
    659             }
    660 
    661             if (resource->cacheAccess().isScratch()) {
    662                 SkASSERT(!resource->getUniqueKey().isValid());
    663                 ++fScratch;
    664                 SkASSERT(fScratchMap->countForKey(resource->resourcePriv().getScratchKey()));
    665                 SkASSERT(!resource->cacheAccess().isExternal());
    666             } else if (resource->resourcePriv().getScratchKey().isValid()) {
    667                 SkASSERT(SkBudgeted::kNo == resource->resourcePriv().isBudgeted() ||
    668                          resource->getUniqueKey().isValid());
    669                 ++fCouldBeScratch;
    670                 SkASSERT(fScratchMap->countForKey(resource->resourcePriv().getScratchKey()));
    671                 SkASSERT(!resource->cacheAccess().isExternal());
    672             }
    673             const GrUniqueKey& uniqueKey = resource->getUniqueKey();
    674             if (uniqueKey.isValid()) {
    675                 ++fContent;
    676                 SkASSERT(fUniqueHash->find(uniqueKey) == resource);
    677                 SkASSERT(!resource->cacheAccess().isExternal());
    678                 SkASSERT(SkBudgeted::kYes == resource->resourcePriv().isBudgeted());
    679             }
    680 
    681             if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
    682                 ++fBudgetedCount;
    683                 fBudgetedBytes += resource->gpuMemorySize();
    684             }
    685         }
    686     };
    687 
    688     Stats stats(this);
    689 
    690     for (int i = 0; i < fNonpurgeableResources.count(); ++i) {
    691         SkASSERT(!fNonpurgeableResources[i]->isPurgeable() ||
    692                  fNewlyPurgeableResourceForValidation == fNonpurgeableResources[i]);
    693         SkASSERT(*fNonpurgeableResources[i]->cacheAccess().accessCacheIndex() == i);
    694         SkASSERT(!fNonpurgeableResources[i]->wasDestroyed());
    695         stats.update(fNonpurgeableResources[i]);
    696     }
    697     for (int i = 0; i < fPurgeableQueue.count(); ++i) {
    698         SkASSERT(fPurgeableQueue.at(i)->isPurgeable());
    699         SkASSERT(*fPurgeableQueue.at(i)->cacheAccess().accessCacheIndex() == i);
    700         SkASSERT(!fPurgeableQueue.at(i)->wasDestroyed());
    701         stats.update(fPurgeableQueue.at(i));
    702     }
    703 
    704     SkASSERT(fCount == this->getResourceCount());
    705     SkASSERT(fBudgetedCount <= fCount);
    706     SkASSERT(fBudgetedBytes <= fBytes);
    707     SkASSERT(stats.fBytes == fBytes);
    708     SkASSERT(stats.fBudgetedBytes == fBudgetedBytes);
    709     SkASSERT(stats.fBudgetedCount == fBudgetedCount);
    710 #if GR_CACHE_STATS
    711     SkASSERT(fBudgetedHighWaterCount <= fHighWaterCount);
    712     SkASSERT(fBudgetedHighWaterBytes <= fHighWaterBytes);
    713     SkASSERT(fBytes <= fHighWaterBytes);
    714     SkASSERT(fCount <= fHighWaterCount);
    715     SkASSERT(fBudgetedBytes <= fBudgetedHighWaterBytes);
    716     SkASSERT(fBudgetedCount <= fBudgetedHighWaterCount);
    717 #endif
    718     SkASSERT(stats.fContent == fUniqueHash.count());
    719     SkASSERT(stats.fScratch + stats.fCouldBeScratch == fScratchMap.count());
    720 
    721     // This assertion is not currently valid because we can be in recursive notifyCntReachedZero()
    722     // calls. This will be fixed when subresource registration is explicit.
    723     // bool overBudget = budgetedBytes > fMaxBytes || budgetedCount > fMaxCount;
    724     // SkASSERT(!overBudget || locked == count || fPurging);
    725 }
    726 
    727 bool GrResourceCache::isInCache(const GrGpuResource* resource) const {
    728     int index = *resource->cacheAccess().accessCacheIndex();
    729     if (index < 0) {
    730         return false;
    731     }
    732     if (index < fPurgeableQueue.count() && fPurgeableQueue.at(index) == resource) {
    733         return true;
    734     }
    735     if (index < fNonpurgeableResources.count() && fNonpurgeableResources[index] == resource) {
    736         return true;
    737     }
    738     SkDEBUGFAIL("Resource index should be -1 or the resource should be in the cache.");
    739     return false;
    740 }
    741 
    742 #endif
    743