Home | History | Annotate | Download | only in gpu
      1 /*
      2  * Copyright 2014 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 
      9 #include "GrResourceCache.h"
     10 
     11 #include "GrCaps.h"
     12 #include "GrGpuResourceCacheAccess.h"
     13 #include "GrTracing.h"
     14 #include "SkGr.h"
     15 #include "SkMessageBus.h"
     16 #include "SkOpts.h"
     17 #include "SkTSort.h"
     18 
     19 DECLARE_SKMESSAGEBUS_MESSAGE(GrUniqueKeyInvalidatedMessage);
     20 
     21 DECLARE_SKMESSAGEBUS_MESSAGE(GrGpuResourceFreedMessage);
     22 
     23 //////////////////////////////////////////////////////////////////////////////
     24 
     25 GrScratchKey::ResourceType GrScratchKey::GenerateResourceType() {
     26     static int32_t gType = INHERITED::kInvalidDomain + 1;
     27 
     28     int32_t type = sk_atomic_inc(&gType);
     29     if (type > SK_MaxU16) {
     30         SkFAIL("Too many Resource Types");
     31     }
     32 
     33     return static_cast<ResourceType>(type);
     34 }
     35 
     36 GrUniqueKey::Domain GrUniqueKey::GenerateDomain() {
     37     static int32_t gDomain = INHERITED::kInvalidDomain + 1;
     38 
     39     int32_t domain = sk_atomic_inc(&gDomain);
     40     if (domain > SK_MaxU16) {
     41         SkFAIL("Too many GrUniqueKey Domains");
     42     }
     43 
     44     return static_cast<Domain>(domain);
     45 }
     46 
     47 uint32_t GrResourceKeyHash(const uint32_t* data, size_t size) {
     48     return SkOpts::hash(data, size);
     49 }
     50 
     51 //////////////////////////////////////////////////////////////////////////////
     52 
     53 class GrResourceCache::AutoValidate : ::SkNoncopyable {
     54 public:
     55     AutoValidate(GrResourceCache* cache) : fCache(cache) { cache->validate(); }
     56     ~AutoValidate() { fCache->validate(); }
     57 private:
     58     GrResourceCache* fCache;
     59 };
     60 
     61  //////////////////////////////////////////////////////////////////////////////
     62 
     63 
     64 GrResourceCache::GrResourceCache(const GrCaps* caps, uint32_t contextUniqueID)
     65     : fTimestamp(0)
     66     , fMaxCount(kDefaultMaxCount)
     67     , fMaxBytes(kDefaultMaxSize)
     68     , fMaxUnusedFlushes(kDefaultMaxUnusedFlushes)
     69 #if GR_CACHE_STATS
     70     , fHighWaterCount(0)
     71     , fHighWaterBytes(0)
     72     , fBudgetedHighWaterCount(0)
     73     , fBudgetedHighWaterBytes(0)
     74 #endif
     75     , fBytes(0)
     76     , fBudgetedCount(0)
     77     , fBudgetedBytes(0)
     78     , fPurgeableBytes(0)
     79     , fRequestFlush(false)
     80     , fExternalFlushCnt(0)
     81     , fContextUniqueID(contextUniqueID)
     82     , fPreferVRAMUseOverFlushes(caps->preferVRAMUseOverFlushes()) {
     83     SkDEBUGCODE(fCount = 0;)
     84     SkDEBUGCODE(fNewlyPurgeableResourceForValidation = nullptr;)
     85 }
     86 
     87 GrResourceCache::~GrResourceCache() {
     88     this->releaseAll();
     89 }
     90 
     91 void GrResourceCache::setLimits(int count, size_t bytes, int maxUnusedFlushes) {
     92     fMaxCount = count;
     93     fMaxBytes = bytes;
     94     fMaxUnusedFlushes = maxUnusedFlushes;
     95     this->purgeAsNeeded();
     96 }
     97 
     98 void GrResourceCache::insertResource(GrGpuResource* resource) {
     99     SkASSERT(resource);
    100     SkASSERT(!this->isInCache(resource));
    101     SkASSERT(!resource->wasDestroyed());
    102     SkASSERT(!resource->isPurgeable());
    103 
    104     // We must set the timestamp before adding to the array in case the timestamp wraps and we wind
    105     // up iterating over all the resources that already have timestamps.
    106     resource->cacheAccess().setTimestamp(this->getNextTimestamp());
    107 
    108     this->addToNonpurgeableArray(resource);
    109 
    110     size_t size = resource->gpuMemorySize();
    111     SkDEBUGCODE(++fCount;)
    112     fBytes += size;
    113 #if GR_CACHE_STATS
    114     fHighWaterCount = SkTMax(this->getResourceCount(), fHighWaterCount);
    115     fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes);
    116 #endif
    117     if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
    118         ++fBudgetedCount;
    119         fBudgetedBytes += size;
    120         TRACE_COUNTER2(TRACE_DISABLED_BY_DEFAULT("skia.gpu.cache"), "skia budget", "used",
    121                        fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
    122 #if GR_CACHE_STATS
    123         fBudgetedHighWaterCount = SkTMax(fBudgetedCount, fBudgetedHighWaterCount);
    124         fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
    125 #endif
    126     }
    127     if (resource->resourcePriv().getScratchKey().isValid() &&
    128         !resource->getUniqueKey().isValid()) {
    129         SkASSERT(!resource->resourcePriv().refsWrappedObjects());
    130         fScratchMap.insert(resource->resourcePriv().getScratchKey(), resource);
    131     }
    132 
    133     this->purgeAsNeeded();
    134 }
    135 
    136 void GrResourceCache::removeResource(GrGpuResource* resource) {
    137     this->validate();
    138     SkASSERT(this->isInCache(resource));
    139 
    140     size_t size = resource->gpuMemorySize();
    141     if (resource->isPurgeable()) {
    142         fPurgeableQueue.remove(resource);
    143         fPurgeableBytes -= size;
    144     } else {
    145         this->removeFromNonpurgeableArray(resource);
    146     }
    147 
    148     SkDEBUGCODE(--fCount;)
    149     fBytes -= size;
    150     if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
    151         --fBudgetedCount;
    152         fBudgetedBytes -= size;
    153         TRACE_COUNTER2(TRACE_DISABLED_BY_DEFAULT("skia.gpu.cache"), "skia budget", "used",
    154                        fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
    155     }
    156 
    157     if (resource->resourcePriv().getScratchKey().isValid() &&
    158         !resource->getUniqueKey().isValid()) {
    159         fScratchMap.remove(resource->resourcePriv().getScratchKey(), resource);
    160     }
    161     if (resource->getUniqueKey().isValid()) {
    162         fUniqueHash.remove(resource->getUniqueKey());
    163     }
    164     this->validate();
    165 }
    166 
    167 void GrResourceCache::abandonAll() {
    168     AutoValidate av(this);
    169 
    170     while (fNonpurgeableResources.count()) {
    171         GrGpuResource* back = *(fNonpurgeableResources.end() - 1);
    172         SkASSERT(!back->wasDestroyed());
    173         back->cacheAccess().abandon();
    174     }
    175 
    176     while (fPurgeableQueue.count()) {
    177         GrGpuResource* top = fPurgeableQueue.peek();
    178         SkASSERT(!top->wasDestroyed());
    179         top->cacheAccess().abandon();
    180     }
    181 
    182     SkASSERT(!fScratchMap.count());
    183     SkASSERT(!fUniqueHash.count());
    184     SkASSERT(!fCount);
    185     SkASSERT(!this->getResourceCount());
    186     SkASSERT(!fBytes);
    187     SkASSERT(!fBudgetedCount);
    188     SkASSERT(!fBudgetedBytes);
    189     SkASSERT(!fPurgeableBytes);
    190 }
    191 
    192 void GrResourceCache::releaseAll() {
    193     AutoValidate av(this);
    194 
    195     this->processFreedGpuResources();
    196 
    197     while(fNonpurgeableResources.count()) {
    198         GrGpuResource* back = *(fNonpurgeableResources.end() - 1);
    199         SkASSERT(!back->wasDestroyed());
    200         back->cacheAccess().release();
    201     }
    202 
    203     while (fPurgeableQueue.count()) {
    204         GrGpuResource* top = fPurgeableQueue.peek();
    205         SkASSERT(!top->wasDestroyed());
    206         top->cacheAccess().release();
    207     }
    208 
    209     SkASSERT(!fScratchMap.count());
    210     SkASSERT(!fUniqueHash.count());
    211     SkASSERT(!fCount);
    212     SkASSERT(!this->getResourceCount());
    213     SkASSERT(!fBytes);
    214     SkASSERT(!fBudgetedCount);
    215     SkASSERT(!fBudgetedBytes);
    216     SkASSERT(!fPurgeableBytes);
    217 }
    218 
    219 class GrResourceCache::AvailableForScratchUse {
    220 public:
    221     AvailableForScratchUse(bool rejectPendingIO) : fRejectPendingIO(rejectPendingIO) { }
    222 
    223     bool operator()(const GrGpuResource* resource) const {
    224         SkASSERT(!resource->getUniqueKey().isValid() &&
    225                  resource->resourcePriv().getScratchKey().isValid());
    226         if (resource->internalHasRef() || !resource->cacheAccess().isScratch()) {
    227             return false;
    228         }
    229         return !fRejectPendingIO || !resource->internalHasPendingIO();
    230     }
    231 
    232 private:
    233     bool fRejectPendingIO;
    234 };
    235 
    236 GrGpuResource* GrResourceCache::findAndRefScratchResource(const GrScratchKey& scratchKey,
    237                                                           size_t resourceSize,
    238                                                           uint32_t flags) {
    239     SkASSERT(scratchKey.isValid());
    240 
    241     GrGpuResource* resource;
    242     if (flags & (kPreferNoPendingIO_ScratchFlag | kRequireNoPendingIO_ScratchFlag)) {
    243         resource = fScratchMap.find(scratchKey, AvailableForScratchUse(true));
    244         if (resource) {
    245             this->refAndMakeResourceMRU(resource);
    246             this->validate();
    247             return resource;
    248         } else if (flags & kRequireNoPendingIO_ScratchFlag) {
    249             return nullptr;
    250         }
    251         // We would prefer to consume more available VRAM rather than flushing
    252         // immediately, but on ANGLE this can lead to starving of the GPU.
    253         if (fPreferVRAMUseOverFlushes && this->wouldFit(resourceSize)) {
    254             // kPrefer is specified, we didn't find a resource without pending io,
    255             // but there is still space in our budget for the resource so force
    256             // the caller to allocate a new resource.
    257             return nullptr;
    258         }
    259     }
    260     resource = fScratchMap.find(scratchKey, AvailableForScratchUse(false));
    261     if (resource) {
    262         this->refAndMakeResourceMRU(resource);
    263         this->validate();
    264     }
    265     return resource;
    266 }
    267 
    268 void GrResourceCache::willRemoveScratchKey(const GrGpuResource* resource) {
    269     SkASSERT(resource->resourcePriv().getScratchKey().isValid());
    270     if (!resource->getUniqueKey().isValid()) {
    271         fScratchMap.remove(resource->resourcePriv().getScratchKey(), resource);
    272     }
    273 }
    274 
    275 void GrResourceCache::removeUniqueKey(GrGpuResource* resource) {
    276     // Someone has a ref to this resource in order to have removed the key. When the ref count
    277     // reaches zero we will get a ref cnt notification and figure out what to do with it.
    278     if (resource->getUniqueKey().isValid()) {
    279         SkASSERT(resource == fUniqueHash.find(resource->getUniqueKey()));
    280         fUniqueHash.remove(resource->getUniqueKey());
    281     }
    282     resource->cacheAccess().removeUniqueKey();
    283 
    284     if (resource->resourcePriv().getScratchKey().isValid()) {
    285         fScratchMap.insert(resource->resourcePriv().getScratchKey(), resource);
    286     }
    287 
    288     this->validate();
    289 }
    290 
    291 void GrResourceCache::changeUniqueKey(GrGpuResource* resource, const GrUniqueKey& newKey) {
    292     SkASSERT(resource);
    293     SkASSERT(this->isInCache(resource));
    294 
    295     // If another resource has the new key, remove its key then install the key on this resource.
    296     if (newKey.isValid()) {
    297         // Remove the entry for this resource if it already has a unique key.
    298         if (resource->getUniqueKey().isValid()) {
    299             SkASSERT(resource == fUniqueHash.find(resource->getUniqueKey()));
    300             fUniqueHash.remove(resource->getUniqueKey());
    301             SkASSERT(nullptr == fUniqueHash.find(resource->getUniqueKey()));
    302         } else {
    303             // 'resource' didn't have a valid unique key before so it is switching sides. Remove it
    304             // from the ScratchMap
    305             if (resource->resourcePriv().getScratchKey().isValid()) {
    306                 fScratchMap.remove(resource->resourcePriv().getScratchKey(), resource);
    307             }
    308         }
    309 
    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                 this->removeUniqueKey(old);
    319             }
    320         }
    321         SkASSERT(nullptr == fUniqueHash.find(newKey));
    322         resource->cacheAccess().setUniqueKey(newKey);
    323         fUniqueHash.add(resource);
    324     } else {
    325         this->removeUniqueKey(resource);
    326     }
    327 
    328     this->validate();
    329 }
    330 
    331 void GrResourceCache::refAndMakeResourceMRU(GrGpuResource* resource) {
    332     SkASSERT(resource);
    333     SkASSERT(this->isInCache(resource));
    334 
    335     if (resource->isPurgeable()) {
    336         // It's about to become unpurgeable.
    337         fPurgeableBytes -= resource->gpuMemorySize();
    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     resource->cacheAccess().setFlushCntWhenResourceBecamePurgeable(fExternalFlushCnt);
    378     resource->cacheAccess().setTimeWhenResourceBecomePurgeable();
    379     fPurgeableBytes += resource->gpuMemorySize();
    380 
    381     if (SkBudgeted::kNo == resource->resourcePriv().isBudgeted()) {
    382         // Check whether this resource could still be used as a scratch resource.
    383         if (!resource->resourcePriv().refsWrappedObjects() &&
    384             resource->resourcePriv().getScratchKey().isValid()) {
    385             // We won't purge an existing resource to make room for this one.
    386             if (fBudgetedCount < fMaxCount &&
    387                 fBudgetedBytes + resource->gpuMemorySize() <= fMaxBytes) {
    388                 resource->resourcePriv().makeBudgeted();
    389                 return;
    390             }
    391         }
    392     } else {
    393         // Purge the resource immediately if we're over budget
    394         // Also purge if the resource has neither a valid scratch key nor a unique key.
    395         bool noKey = !resource->resourcePriv().getScratchKey().isValid() &&
    396                      !resource->getUniqueKey().isValid();
    397         if (!this->overBudget() && !noKey) {
    398             return;
    399         }
    400     }
    401 
    402     SkDEBUGCODE(int beforeCount = this->getResourceCount();)
    403     resource->cacheAccess().release();
    404     // We should at least free this resource, perhaps dependent resources as well.
    405     SkASSERT(this->getResourceCount() < beforeCount);
    406     this->validate();
    407 }
    408 
    409 void GrResourceCache::didChangeGpuMemorySize(const GrGpuResource* resource, size_t oldSize) {
    410     // SkASSERT(!fPurging); GrPathRange increases size during flush. :(
    411     SkASSERT(resource);
    412     SkASSERT(this->isInCache(resource));
    413 
    414     ptrdiff_t delta = resource->gpuMemorySize() - oldSize;
    415 
    416     fBytes += delta;
    417 #if GR_CACHE_STATS
    418     fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes);
    419 #endif
    420     if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
    421         fBudgetedBytes += delta;
    422         TRACE_COUNTER2(TRACE_DISABLED_BY_DEFAULT("skia.gpu.cache"), "skia budget", "used",
    423                        fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
    424 #if GR_CACHE_STATS
    425         fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
    426 #endif
    427     }
    428 
    429     this->purgeAsNeeded();
    430     this->validate();
    431 }
    432 
    433 void GrResourceCache::didChangeBudgetStatus(GrGpuResource* resource) {
    434     SkASSERT(resource);
    435     SkASSERT(this->isInCache(resource));
    436 
    437     size_t size = resource->gpuMemorySize();
    438 
    439     if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
    440         ++fBudgetedCount;
    441         fBudgetedBytes += size;
    442 #if GR_CACHE_STATS
    443         fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
    444         fBudgetedHighWaterCount = SkTMax(fBudgetedCount, fBudgetedHighWaterCount);
    445 #endif
    446         this->purgeAsNeeded();
    447     } else {
    448         --fBudgetedCount;
    449         fBudgetedBytes -= size;
    450     }
    451     TRACE_COUNTER2(TRACE_DISABLED_BY_DEFAULT("skia.gpu.cache"), "skia budget", "used",
    452                    fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
    453 
    454     this->validate();
    455 }
    456 
    457 void GrResourceCache::purgeAsNeeded() {
    458     SkTArray<GrUniqueKeyInvalidatedMessage> invalidKeyMsgs;
    459     fInvalidUniqueKeyInbox.poll(&invalidKeyMsgs);
    460     if (invalidKeyMsgs.count()) {
    461         this->processInvalidUniqueKeys(invalidKeyMsgs);
    462     }
    463 
    464     this->processFreedGpuResources();
    465 
    466     if (fMaxUnusedFlushes > 0) {
    467         // We want to know how many complete flushes have occurred without the resource being used.
    468         // If the resource was tagged when fExternalFlushCnt was N then this means it became
    469         // purgeable during activity that became the N+1th flush. So when the flush count is N+2
    470         // it has sat in the purgeable queue for one entire flush.
    471         uint32_t oldestAllowedFlushCnt = fExternalFlushCnt - fMaxUnusedFlushes - 1;
    472         // check for underflow
    473         if (oldestAllowedFlushCnt < fExternalFlushCnt) {
    474             while (fPurgeableQueue.count()) {
    475                 uint32_t flushWhenResourceBecamePurgeable =
    476                         fPurgeableQueue.peek()->cacheAccess().flushCntWhenResourceBecamePurgeable();
    477                 if (oldestAllowedFlushCnt < flushWhenResourceBecamePurgeable) {
    478                     // Resources were given both LRU timestamps and tagged with a flush cnt when
    479                     // they first became purgeable. The LRU timestamp won't change again until the
    480                     // resource is made non-purgeable again. So, at this point all the remaining
    481                     // resources in the timestamp-sorted queue will have a flush count >= to this
    482                     // one.
    483                     break;
    484                 }
    485                 GrGpuResource* resource = fPurgeableQueue.peek();
    486                 SkASSERT(resource->isPurgeable());
    487                 resource->cacheAccess().release();
    488             }
    489         }
    490     }
    491 
    492     bool stillOverbudget = this->overBudget();
    493     while (stillOverbudget && fPurgeableQueue.count()) {
    494         GrGpuResource* resource = fPurgeableQueue.peek();
    495         SkASSERT(resource->isPurgeable());
    496         resource->cacheAccess().release();
    497         stillOverbudget = this->overBudget();
    498     }
    499 
    500     this->validate();
    501 
    502     if (stillOverbudget) {
    503         // Set this so that GrDrawingManager will issue a flush to free up resources with pending
    504         // IO that we were unable to purge in this pass.
    505         fRequestFlush = true;
    506     }
    507 }
    508 
    509 void GrResourceCache::purgeAllUnlocked() {
    510     // We could disable maintaining the heap property here, but it would add a lot of complexity.
    511     // Moreover, this is rarely called.
    512     while (fPurgeableQueue.count()) {
    513         GrGpuResource* resource = fPurgeableQueue.peek();
    514         SkASSERT(resource->isPurgeable());
    515         resource->cacheAccess().release();
    516     }
    517 
    518     this->validate();
    519 }
    520 
    521 void GrResourceCache::purgeResourcesNotUsedSince(GrStdSteadyClock::time_point purgeTime) {
    522     while (fPurgeableQueue.count()) {
    523         const GrStdSteadyClock::time_point resourceTime =
    524                 fPurgeableQueue.peek()->cacheAccess().timeWhenResourceBecamePurgeable();
    525         if (resourceTime >= purgeTime) {
    526             // Resources were given both LRU timestamps and tagged with a frame number when
    527             // they first became purgeable. The LRU timestamp won't change again until the
    528             // resource is made non-purgeable again. So, at this point all the remaining
    529             // resources in the timestamp-sorted queue will have a frame number >= to this
    530             // one.
    531             break;
    532         }
    533         GrGpuResource* resource = fPurgeableQueue.peek();
    534         SkASSERT(resource->isPurgeable());
    535         resource->cacheAccess().release();
    536     }
    537 }
    538 
    539 void GrResourceCache::purgeUnlockedResources(size_t bytesToPurge, bool preferScratchResources) {
    540 
    541     const size_t tmpByteBudget = SkTMax((size_t)0, fBytes - bytesToPurge);
    542     bool stillOverbudget = tmpByteBudget < fBytes;
    543 
    544     if (preferScratchResources && bytesToPurge < fPurgeableBytes) {
    545         // Sort the queue
    546         fPurgeableQueue.sort();
    547 
    548         // Make a list of the scratch resources to delete
    549         SkTDArray<GrGpuResource*> scratchResources;
    550         size_t scratchByteCount = 0;
    551         for (int i = 0; i < fPurgeableQueue.count() && stillOverbudget; i++) {
    552             GrGpuResource* resource = fPurgeableQueue.at(i);
    553             SkASSERT(resource->isPurgeable());
    554             if (!resource->getUniqueKey().isValid()) {
    555                 *scratchResources.append() = resource;
    556                 scratchByteCount += resource->gpuMemorySize();
    557                 stillOverbudget = tmpByteBudget < fBytes - scratchByteCount;
    558             }
    559         }
    560 
    561         // Delete the scratch resources. This must be done as a separate pass
    562         // to avoid messing up the sorted order of the queue
    563         for (int i = 0; i < scratchResources.count(); i++) {
    564             scratchResources.getAt(i)->cacheAccess().release();
    565         }
    566         stillOverbudget = tmpByteBudget < fBytes;
    567 
    568         this->validate();
    569     }
    570 
    571     // Purge any remaining resources in LRU order
    572     if (stillOverbudget) {
    573         const size_t cachedByteCount = fMaxBytes;
    574         fMaxBytes = tmpByteBudget;
    575         this->purgeAsNeeded();
    576         fMaxBytes = cachedByteCount;
    577     }
    578 }
    579 
    580 void GrResourceCache::processInvalidUniqueKeys(
    581     const SkTArray<GrUniqueKeyInvalidatedMessage>& msgs) {
    582     for (int i = 0; i < msgs.count(); ++i) {
    583         GrGpuResource* resource = this->findAndRefUniqueResource(msgs[i].key());
    584         if (resource) {
    585             resource->resourcePriv().removeUniqueKey();
    586             resource->unref(); // If this resource is now purgeable, the cache will be notified.
    587         }
    588     }
    589 }
    590 
    591 void GrResourceCache::insertCrossContextGpuResource(GrGpuResource* resource) {
    592     resource->ref();
    593 }
    594 
    595 void GrResourceCache::processFreedGpuResources() {
    596     SkTArray<GrGpuResourceFreedMessage> msgs;
    597     fFreedGpuResourceInbox.poll(&msgs);
    598     for (int i = 0; i < msgs.count(); ++i) {
    599         if (msgs[i].fOwningUniqueID == fContextUniqueID) {
    600             msgs[i].fResource->unref();
    601         }
    602     }
    603 }
    604 
    605 void GrResourceCache::addToNonpurgeableArray(GrGpuResource* resource) {
    606     int index = fNonpurgeableResources.count();
    607     *fNonpurgeableResources.append() = resource;
    608     *resource->cacheAccess().accessCacheIndex() = index;
    609 }
    610 
    611 void GrResourceCache::removeFromNonpurgeableArray(GrGpuResource* resource) {
    612     int* index = resource->cacheAccess().accessCacheIndex();
    613     // Fill the whole we will create in the array with the tail object, adjust its index, and
    614     // then pop the array
    615     GrGpuResource* tail = *(fNonpurgeableResources.end() - 1);
    616     SkASSERT(fNonpurgeableResources[*index] == resource);
    617     fNonpurgeableResources[*index] = tail;
    618     *tail->cacheAccess().accessCacheIndex() = *index;
    619     fNonpurgeableResources.pop();
    620     SkDEBUGCODE(*index = -1);
    621 }
    622 
    623 uint32_t GrResourceCache::getNextTimestamp() {
    624     // If we wrap then all the existing resources will appear older than any resources that get
    625     // a timestamp after the wrap.
    626     if (0 == fTimestamp) {
    627         int count = this->getResourceCount();
    628         if (count) {
    629             // Reset all the timestamps. We sort the resources by timestamp and then assign
    630             // sequential timestamps beginning with 0. This is O(n*lg(n)) but it should be extremely
    631             // rare.
    632             SkTDArray<GrGpuResource*> sortedPurgeableResources;
    633             sortedPurgeableResources.setReserve(fPurgeableQueue.count());
    634 
    635             while (fPurgeableQueue.count()) {
    636                 *sortedPurgeableResources.append() = fPurgeableQueue.peek();
    637                 fPurgeableQueue.pop();
    638             }
    639 
    640             SkTQSort(fNonpurgeableResources.begin(), fNonpurgeableResources.end() - 1,
    641                      CompareTimestamp);
    642 
    643             // Pick resources out of the purgeable and non-purgeable arrays based on lowest
    644             // timestamp and assign new timestamps.
    645             int currP = 0;
    646             int currNP = 0;
    647             while (currP < sortedPurgeableResources.count() &&
    648                    currNP < fNonpurgeableResources.count()) {
    649                 uint32_t tsP = sortedPurgeableResources[currP]->cacheAccess().timestamp();
    650                 uint32_t tsNP = fNonpurgeableResources[currNP]->cacheAccess().timestamp();
    651                 SkASSERT(tsP != tsNP);
    652                 if (tsP < tsNP) {
    653                     sortedPurgeableResources[currP++]->cacheAccess().setTimestamp(fTimestamp++);
    654                 } else {
    655                     // Correct the index in the nonpurgeable array stored on the resource post-sort.
    656                     *fNonpurgeableResources[currNP]->cacheAccess().accessCacheIndex() = currNP;
    657                     fNonpurgeableResources[currNP++]->cacheAccess().setTimestamp(fTimestamp++);
    658                 }
    659             }
    660 
    661             // The above loop ended when we hit the end of one array. Finish the other one.
    662             while (currP < sortedPurgeableResources.count()) {
    663                 sortedPurgeableResources[currP++]->cacheAccess().setTimestamp(fTimestamp++);
    664             }
    665             while (currNP < fNonpurgeableResources.count()) {
    666                 *fNonpurgeableResources[currNP]->cacheAccess().accessCacheIndex() = currNP;
    667                 fNonpurgeableResources[currNP++]->cacheAccess().setTimestamp(fTimestamp++);
    668             }
    669 
    670             // Rebuild the queue.
    671             for (int i = 0; i < sortedPurgeableResources.count(); ++i) {
    672                 fPurgeableQueue.insert(sortedPurgeableResources[i]);
    673             }
    674 
    675             this->validate();
    676             SkASSERT(count == this->getResourceCount());
    677 
    678             // count should be the next timestamp we return.
    679             SkASSERT(fTimestamp == SkToU32(count));
    680         }
    681     }
    682     return fTimestamp++;
    683 }
    684 
    685 void GrResourceCache::notifyFlushOccurred(FlushType type) {
    686     switch (type) {
    687         case FlushType::kCacheRequested:
    688             SkASSERT(fRequestFlush);
    689             fRequestFlush = false;
    690             break;
    691         case FlushType::kExternal:
    692             ++fExternalFlushCnt;
    693             if (0 == fExternalFlushCnt) {
    694                 // When this wraps just reset all the purgeable resources' last used flush state.
    695                 for (int i = 0; i < fPurgeableQueue.count(); ++i) {
    696                     fPurgeableQueue.at(i)->cacheAccess().setFlushCntWhenResourceBecamePurgeable(0);
    697                 }
    698             }
    699             break;
    700     }
    701     this->purgeAsNeeded();
    702 }
    703 
    704 void GrResourceCache::dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const {
    705     for (int i = 0; i < fNonpurgeableResources.count(); ++i) {
    706         fNonpurgeableResources[i]->dumpMemoryStatistics(traceMemoryDump);
    707     }
    708     for (int i = 0; i < fPurgeableQueue.count(); ++i) {
    709         fPurgeableQueue.at(i)->dumpMemoryStatistics(traceMemoryDump);
    710     }
    711 }
    712 
    713 #ifdef SK_DEBUG
    714 void GrResourceCache::validate() const {
    715     // Reduce the frequency of validations for large resource counts.
    716     static SkRandom gRandom;
    717     int mask = (SkNextPow2(fCount + 1) >> 5) - 1;
    718     if (~mask && (gRandom.nextU() & mask)) {
    719         return;
    720     }
    721 
    722     struct Stats {
    723         size_t fBytes;
    724         int fBudgetedCount;
    725         size_t fBudgetedBytes;
    726         int fLocked;
    727         int fScratch;
    728         int fCouldBeScratch;
    729         int fContent;
    730         const ScratchMap* fScratchMap;
    731         const UniqueHash* fUniqueHash;
    732 
    733         Stats(const GrResourceCache* cache) {
    734             memset(this, 0, sizeof(*this));
    735             fScratchMap = &cache->fScratchMap;
    736             fUniqueHash = &cache->fUniqueHash;
    737         }
    738 
    739         void update(GrGpuResource* resource) {
    740             fBytes += resource->gpuMemorySize();
    741 
    742             if (!resource->isPurgeable()) {
    743                 ++fLocked;
    744             }
    745 
    746             const GrScratchKey& scratchKey = resource->resourcePriv().getScratchKey();
    747             const GrUniqueKey& uniqueKey = resource->getUniqueKey();
    748 
    749             if (resource->cacheAccess().isScratch()) {
    750                 SkASSERT(!uniqueKey.isValid());
    751                 ++fScratch;
    752                 SkASSERT(fScratchMap->countForKey(scratchKey));
    753                 SkASSERT(!resource->resourcePriv().refsWrappedObjects());
    754             } else if (scratchKey.isValid()) {
    755                 SkASSERT(SkBudgeted::kNo == resource->resourcePriv().isBudgeted() ||
    756                          uniqueKey.isValid());
    757                 if (!uniqueKey.isValid()) {
    758                     ++fCouldBeScratch;
    759                     SkASSERT(fScratchMap->countForKey(scratchKey));
    760                 }
    761                 SkASSERT(!resource->resourcePriv().refsWrappedObjects());
    762             }
    763             if (uniqueKey.isValid()) {
    764                 ++fContent;
    765                 SkASSERT(fUniqueHash->find(uniqueKey) == resource);
    766                 SkASSERT(SkBudgeted::kYes == resource->resourcePriv().isBudgeted() ||
    767                          resource->resourcePriv().refsWrappedObjects());
    768 
    769                 if (scratchKey.isValid()) {
    770                     SkASSERT(!fScratchMap->has(resource, scratchKey));
    771                 }
    772             }
    773 
    774             if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
    775                 ++fBudgetedCount;
    776                 fBudgetedBytes += resource->gpuMemorySize();
    777             }
    778         }
    779     };
    780 
    781     {
    782         ScratchMap::ConstIter iter(&fScratchMap);
    783 
    784         int count = 0;
    785         for ( ; !iter.done(); ++iter) {
    786             const GrGpuResource* resource = *iter;
    787             SkASSERT(resource->resourcePriv().getScratchKey().isValid());
    788             SkASSERT(!resource->getUniqueKey().isValid());
    789             count++;
    790         }
    791         SkASSERT(count == fScratchMap.count()); // ensure the iterator is working correctly
    792     }
    793 
    794     Stats stats(this);
    795     size_t purgeableBytes = 0;
    796 
    797     for (int i = 0; i < fNonpurgeableResources.count(); ++i) {
    798         SkASSERT(!fNonpurgeableResources[i]->isPurgeable() ||
    799                  fNewlyPurgeableResourceForValidation == fNonpurgeableResources[i]);
    800         SkASSERT(*fNonpurgeableResources[i]->cacheAccess().accessCacheIndex() == i);
    801         SkASSERT(!fNonpurgeableResources[i]->wasDestroyed());
    802         stats.update(fNonpurgeableResources[i]);
    803     }
    804     for (int i = 0; i < fPurgeableQueue.count(); ++i) {
    805         SkASSERT(fPurgeableQueue.at(i)->isPurgeable());
    806         SkASSERT(*fPurgeableQueue.at(i)->cacheAccess().accessCacheIndex() == i);
    807         SkASSERT(!fPurgeableQueue.at(i)->wasDestroyed());
    808         stats.update(fPurgeableQueue.at(i));
    809         purgeableBytes += fPurgeableQueue.at(i)->gpuMemorySize();
    810     }
    811 
    812     SkASSERT(fCount == this->getResourceCount());
    813     SkASSERT(fBudgetedCount <= fCount);
    814     SkASSERT(fBudgetedBytes <= fBytes);
    815     SkASSERT(stats.fBytes == fBytes);
    816     SkASSERT(stats.fBudgetedBytes == fBudgetedBytes);
    817     SkASSERT(stats.fBudgetedCount == fBudgetedCount);
    818     SkASSERT(purgeableBytes == fPurgeableBytes);
    819 #if GR_CACHE_STATS
    820     SkASSERT(fBudgetedHighWaterCount <= fHighWaterCount);
    821     SkASSERT(fBudgetedHighWaterBytes <= fHighWaterBytes);
    822     SkASSERT(fBytes <= fHighWaterBytes);
    823     SkASSERT(fCount <= fHighWaterCount);
    824     SkASSERT(fBudgetedBytes <= fBudgetedHighWaterBytes);
    825     SkASSERT(fBudgetedCount <= fBudgetedHighWaterCount);
    826 #endif
    827     SkASSERT(stats.fContent == fUniqueHash.count());
    828     SkASSERT(stats.fScratch + stats.fCouldBeScratch == fScratchMap.count());
    829 
    830     // This assertion is not currently valid because we can be in recursive notifyCntReachedZero()
    831     // calls. This will be fixed when subresource registration is explicit.
    832     // bool overBudget = budgetedBytes > fMaxBytes || budgetedCount > fMaxCount;
    833     // SkASSERT(!overBudget || locked == count || fPurging);
    834 }
    835 
    836 bool GrResourceCache::isInCache(const GrGpuResource* resource) const {
    837     int index = *resource->cacheAccess().accessCacheIndex();
    838     if (index < 0) {
    839         return false;
    840     }
    841     if (index < fPurgeableQueue.count() && fPurgeableQueue.at(index) == resource) {
    842         return true;
    843     }
    844     if (index < fNonpurgeableResources.count() && fNonpurgeableResources[index] == resource) {
    845         return true;
    846     }
    847     SkDEBUGFAIL("Resource index should be -1 or the resource should be in the cache.");
    848     return false;
    849 }
    850 
    851 #endif
    852