LCOV - code coverage report
Current view: top level - gfx/skia/skia/src/gpu - GrResourceCache.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 434 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 37 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13