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 //////////////////////////////////////////////////////////////////////////////
22 
GenerateResourceType()23 GrScratchKey::ResourceType GrScratchKey::GenerateResourceType() {
24     static int32_t gType = INHERITED::kInvalidDomain + 1;
25 
26     int32_t type = sk_atomic_inc(&gType);
27     if (type > SK_MaxU16) {
28         SkFAIL("Too many Resource Types");
29     }
30 
31     return static_cast<ResourceType>(type);
32 }
33 
GenerateDomain()34 GrUniqueKey::Domain GrUniqueKey::GenerateDomain() {
35     static int32_t gDomain = INHERITED::kInvalidDomain + 1;
36 
37     int32_t domain = sk_atomic_inc(&gDomain);
38     if (domain > SK_MaxU16) {
39         SkFAIL("Too many GrUniqueKey Domains");
40     }
41 
42     return static_cast<Domain>(domain);
43 }
44 
GrResourceKeyHash(const uint32_t * data,size_t size)45 uint32_t GrResourceKeyHash(const uint32_t* data, size_t size) {
46     return SkOpts::hash(data, size);
47 }
48 
49 //////////////////////////////////////////////////////////////////////////////
50 
51 class GrResourceCache::AutoValidate : ::SkNoncopyable {
52 public:
AutoValidate(GrResourceCache * cache)53     AutoValidate(GrResourceCache* cache) : fCache(cache) { cache->validate(); }
~AutoValidate()54     ~AutoValidate() { fCache->validate(); }
55 private:
56     GrResourceCache* fCache;
57 };
58 
59  //////////////////////////////////////////////////////////////////////////////
60 
61 
GrResourceCache(const GrCaps * caps)62 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     , fPreferVRAMUseOverFlushes(caps->preferVRAMUseOverFlushes()) {
79     SkDEBUGCODE(fCount = 0;)
80     SkDEBUGCODE(fNewlyPurgeableResourceForValidation = nullptr;)
81 }
82 
~GrResourceCache()83 GrResourceCache::~GrResourceCache() {
84     this->releaseAll();
85 }
86 
setLimits(int count,size_t bytes,int maxUnusedFlushes)87 void GrResourceCache::setLimits(int count, size_t bytes, int maxUnusedFlushes) {
88     fMaxCount = count;
89     fMaxBytes = bytes;
90     fMaxUnusedFlushes = maxUnusedFlushes;
91     this->purgeAsNeeded();
92 }
93 
insertResource(GrGpuResource * resource)94 void GrResourceCache::insertResource(GrGpuResource* resource) {
95     SkASSERT(resource);
96     SkASSERT(!this->isInCache(resource));
97     SkASSERT(!resource->wasDestroyed());
98     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     resource->cacheAccess().setTimestamp(this->getNextTimestamp());
103 
104     this->addToNonpurgeableArray(resource);
105 
106     size_t size = resource->gpuMemorySize();
107     SkDEBUGCODE(++fCount;)
108     fBytes += size;
109 #if GR_CACHE_STATS
110     fHighWaterCount = SkTMax(this->getResourceCount(), fHighWaterCount);
111     fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes);
112 #endif
113     if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
114         ++fBudgetedCount;
115         fBudgetedBytes += size;
116         TRACE_COUNTER2(TRACE_DISABLED_BY_DEFAULT("skia.gpu.cache"), "skia budget", "used",
117                        fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
118 #if GR_CACHE_STATS
119         fBudgetedHighWaterCount = SkTMax(fBudgetedCount, fBudgetedHighWaterCount);
120         fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
121 #endif
122     }
123     if (resource->resourcePriv().getScratchKey().isValid() &&
124         !resource->getUniqueKey().isValid()) {
125         SkASSERT(!resource->resourcePriv().refsWrappedObjects());
126         fScratchMap.insert(resource->resourcePriv().getScratchKey(), resource);
127     }
128 
129     this->purgeAsNeeded();
130 }
131 
removeResource(GrGpuResource * resource)132 void GrResourceCache::removeResource(GrGpuResource* resource) {
133     this->validate();
134     SkASSERT(this->isInCache(resource));
135 
136     if (resource->isPurgeable()) {
137         fPurgeableQueue.remove(resource);
138     } else {
139         this->removeFromNonpurgeableArray(resource);
140     }
141 
142     size_t size = resource->gpuMemorySize();
143     SkDEBUGCODE(--fCount;)
144     fBytes -= size;
145     if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
146         --fBudgetedCount;
147         fBudgetedBytes -= size;
148         TRACE_COUNTER2(TRACE_DISABLED_BY_DEFAULT("skia.gpu.cache"), "skia budget", "used",
149                        fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
150     }
151 
152     if (resource->resourcePriv().getScratchKey().isValid() &&
153         !resource->getUniqueKey().isValid()) {
154         fScratchMap.remove(resource->resourcePriv().getScratchKey(), resource);
155     }
156     if (resource->getUniqueKey().isValid()) {
157         fUniqueHash.remove(resource->getUniqueKey());
158     }
159     this->validate();
160 }
161 
abandonAll()162 void GrResourceCache::abandonAll() {
163     AutoValidate av(this);
164 
165     while (fNonpurgeableResources.count()) {
166         GrGpuResource* back = *(fNonpurgeableResources.end() - 1);
167         SkASSERT(!back->wasDestroyed());
168         back->cacheAccess().abandon();
169     }
170 
171     while (fPurgeableQueue.count()) {
172         GrGpuResource* top = fPurgeableQueue.peek();
173         SkASSERT(!top->wasDestroyed());
174         top->cacheAccess().abandon();
175     }
176 
177     SkASSERT(!fScratchMap.count());
178     SkASSERT(!fUniqueHash.count());
179     SkASSERT(!fCount);
180     SkASSERT(!this->getResourceCount());
181     SkASSERT(!fBytes);
182     SkASSERT(!fBudgetedCount);
183     SkASSERT(!fBudgetedBytes);
184 }
185 
releaseAll()186 void GrResourceCache::releaseAll() {
187     AutoValidate av(this);
188 
189     while(fNonpurgeableResources.count()) {
190         GrGpuResource* back = *(fNonpurgeableResources.end() - 1);
191         SkASSERT(!back->wasDestroyed());
192         back->cacheAccess().release();
193     }
194 
195     while (fPurgeableQueue.count()) {
196         GrGpuResource* top = fPurgeableQueue.peek();
197         SkASSERT(!top->wasDestroyed());
198         top->cacheAccess().release();
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 class GrResourceCache::AvailableForScratchUse {
211 public:
AvailableForScratchUse(bool rejectPendingIO)212     AvailableForScratchUse(bool rejectPendingIO) : fRejectPendingIO(rejectPendingIO) { }
213 
operator ()(const GrGpuResource * resource) const214     bool operator()(const GrGpuResource* resource) const {
215         SkASSERT(!resource->getUniqueKey().isValid() &&
216                  resource->resourcePriv().getScratchKey().isValid());
217         if (resource->internalHasRef() || !resource->cacheAccess().isScratch()) {
218             return false;
219         }
220         return !fRejectPendingIO || !resource->internalHasPendingIO();
221     }
222 
223 private:
224     bool fRejectPendingIO;
225 };
226 
findAndRefScratchResource(const GrScratchKey & scratchKey,size_t resourceSize,uint32_t flags)227 GrGpuResource* GrResourceCache::findAndRefScratchResource(const GrScratchKey& scratchKey,
228                                                           size_t resourceSize,
229                                                           uint32_t flags) {
230     SkASSERT(scratchKey.isValid());
231 
232     GrGpuResource* resource;
233     if (flags & (kPreferNoPendingIO_ScratchFlag | kRequireNoPendingIO_ScratchFlag)) {
234         resource = fScratchMap.find(scratchKey, AvailableForScratchUse(true));
235         if (resource) {
236             this->refAndMakeResourceMRU(resource);
237             this->validate();
238             return resource;
239         } else if (flags & kRequireNoPendingIO_ScratchFlag) {
240             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         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             return nullptr;
249         }
250     }
251     resource = fScratchMap.find(scratchKey, AvailableForScratchUse(false));
252     if (resource) {
253         this->refAndMakeResourceMRU(resource);
254         this->validate();
255     }
256     return resource;
257 }
258 
willRemoveScratchKey(const GrGpuResource * resource)259 void GrResourceCache::willRemoveScratchKey(const GrGpuResource* resource) {
260     SkASSERT(resource->resourcePriv().getScratchKey().isValid());
261     if (!resource->getUniqueKey().isValid()) {
262         fScratchMap.remove(resource->resourcePriv().getScratchKey(), resource);
263     }
264 }
265 
removeUniqueKey(GrGpuResource * resource)266 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     if (resource->getUniqueKey().isValid()) {
270         SkASSERT(resource == fUniqueHash.find(resource->getUniqueKey()));
271         fUniqueHash.remove(resource->getUniqueKey());
272     }
273     resource->cacheAccess().removeUniqueKey();
274 
275     if (resource->resourcePriv().getScratchKey().isValid()) {
276         fScratchMap.insert(resource->resourcePriv().getScratchKey(), resource);
277     }
278 
279     this->validate();
280 }
281 
changeUniqueKey(GrGpuResource * resource,const GrUniqueKey & newKey)282 void GrResourceCache::changeUniqueKey(GrGpuResource* resource, const GrUniqueKey& newKey) {
283     SkASSERT(resource);
284     SkASSERT(this->isInCache(resource));
285 
286     // If another resource has the new key, remove its key then install the key on this resource.
287     if (newKey.isValid()) {
288         // Remove the entry for this resource if it already has a unique key.
289         if (resource->getUniqueKey().isValid()) {
290             SkASSERT(resource == fUniqueHash.find(resource->getUniqueKey()));
291             fUniqueHash.remove(resource->getUniqueKey());
292             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             if (resource->resourcePriv().getScratchKey().isValid()) {
297                 fScratchMap.remove(resource->resourcePriv().getScratchKey(), resource);
298             }
299         }
300 
301         if (GrGpuResource* old = fUniqueHash.find(newKey)) {
302             // If the old resource using the key is purgeable and is unreachable, then remove it.
303             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                 SkDEBUGCODE(resource->cacheAccess().removeUniqueKey();)
307                 old->cacheAccess().release();
308             } else {
309                 this->removeUniqueKey(old);
310             }
311         }
312         SkASSERT(nullptr == fUniqueHash.find(newKey));
313         resource->cacheAccess().setUniqueKey(newKey);
314         fUniqueHash.add(resource);
315     } else {
316         this->removeUniqueKey(resource);
317     }
318 
319     this->validate();
320 }
321 
refAndMakeResourceMRU(GrGpuResource * resource)322 void GrResourceCache::refAndMakeResourceMRU(GrGpuResource* resource) {
323     SkASSERT(resource);
324     SkASSERT(this->isInCache(resource));
325 
326     if (resource->isPurgeable()) {
327         // It's about to become unpurgeable.
328         fPurgeableQueue.remove(resource);
329         this->addToNonpurgeableArray(resource);
330     }
331     resource->ref();
332 
333     resource->cacheAccess().setTimestamp(this->getNextTimestamp());
334     this->validate();
335 }
336 
notifyCntReachedZero(GrGpuResource * resource,uint32_t flags)337 void GrResourceCache::notifyCntReachedZero(GrGpuResource* resource, uint32_t flags) {
338     SkASSERT(resource);
339     SkASSERT(!resource->wasDestroyed());
340     SkASSERT(flags);
341     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     SkASSERT(fNonpurgeableResources[*resource->cacheAccess().accessCacheIndex()] == resource);
345 
346     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         if (resource->isPurgeable()) {
352             fNewlyPurgeableResourceForValidation = resource;
353         }
354 #endif
355         resource->cacheAccess().setTimestamp(this->getNextTimestamp());
356         SkDEBUGCODE(fNewlyPurgeableResourceForValidation = nullptr);
357     }
358 
359     if (!SkToBool(ResourceAccess::kAllCntsReachedZero_RefNotificationFlag & flags)) {
360         SkASSERT(!resource->isPurgeable());
361         return;
362     }
363 
364     SkASSERT(resource->isPurgeable());
365     this->removeFromNonpurgeableArray(resource);
366     fPurgeableQueue.insert(resource);
367     resource->cacheAccess().setFlushCntWhenResourceBecamePurgeable(fExternalFlushCnt);
368     resource->cacheAccess().setTimeWhenResourceBecomePurgeable();
369 
370     if (SkBudgeted::kNo == resource->resourcePriv().isBudgeted()) {
371         // Check whether this resource could still be used as a scratch resource.
372         if (!resource->resourcePriv().refsWrappedObjects() &&
373             resource->resourcePriv().getScratchKey().isValid()) {
374             // We won't purge an existing resource to make room for this one.
375             if (fBudgetedCount < fMaxCount &&
376                 fBudgetedBytes + resource->gpuMemorySize() <= fMaxBytes) {
377                 resource->resourcePriv().makeBudgeted();
378                 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         bool noKey = !resource->resourcePriv().getScratchKey().isValid() &&
385                      !resource->getUniqueKey().isValid();
386         if (!this->overBudget() && !noKey) {
387             return;
388         }
389     }
390 
391     SkDEBUGCODE(int beforeCount = this->getResourceCount();)
392     resource->cacheAccess().release();
393     // We should at least free this resource, perhaps dependent resources as well.
394     SkASSERT(this->getResourceCount() < beforeCount);
395     this->validate();
396 }
397 
didChangeGpuMemorySize(const GrGpuResource * resource,size_t oldSize)398 void GrResourceCache::didChangeGpuMemorySize(const GrGpuResource* resource, size_t oldSize) {
399     // SkASSERT(!fPurging); GrPathRange increases size during flush. :(
400     SkASSERT(resource);
401     SkASSERT(this->isInCache(resource));
402 
403     ptrdiff_t delta = resource->gpuMemorySize() - oldSize;
404 
405     fBytes += delta;
406 #if GR_CACHE_STATS
407     fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes);
408 #endif
409     if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
410         fBudgetedBytes += delta;
411         TRACE_COUNTER2(TRACE_DISABLED_BY_DEFAULT("skia.gpu.cache"), "skia budget", "used",
412                        fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
413 #if GR_CACHE_STATS
414         fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
415 #endif
416     }
417 
418     this->purgeAsNeeded();
419     this->validate();
420 }
421 
didChangeBudgetStatus(GrGpuResource * resource)422 void GrResourceCache::didChangeBudgetStatus(GrGpuResource* resource) {
423     SkASSERT(resource);
424     SkASSERT(this->isInCache(resource));
425 
426     size_t size = resource->gpuMemorySize();
427 
428     if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
429         ++fBudgetedCount;
430         fBudgetedBytes += size;
431 #if GR_CACHE_STATS
432         fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
433         fBudgetedHighWaterCount = SkTMax(fBudgetedCount, fBudgetedHighWaterCount);
434 #endif
435         this->purgeAsNeeded();
436     } else {
437         --fBudgetedCount;
438         fBudgetedBytes -= size;
439     }
440     TRACE_COUNTER2(TRACE_DISABLED_BY_DEFAULT("skia.gpu.cache"), "skia budget", "used",
441                    fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
442 
443     this->validate();
444 }
445 
purgeAsNeeded()446 void GrResourceCache::purgeAsNeeded() {
447     SkTArray<GrUniqueKeyInvalidatedMessage> invalidKeyMsgs;
448     fInvalidUniqueKeyInbox.poll(&invalidKeyMsgs);
449     if (invalidKeyMsgs.count()) {
450         this->processInvalidUniqueKeys(invalidKeyMsgs);
451     }
452 
453     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         uint32_t oldestAllowedFlushCnt = fExternalFlushCnt - fMaxUnusedFlushes - 1;
459         // check for underflow
460         if (oldestAllowedFlushCnt < fExternalFlushCnt) {
461             while (fPurgeableQueue.count()) {
462                 uint32_t flushWhenResourceBecamePurgeable =
463                         fPurgeableQueue.peek()->cacheAccess().flushCntWhenResourceBecamePurgeable();
464                 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                     break;
471                 }
472                 GrGpuResource* resource = fPurgeableQueue.peek();
473                 SkASSERT(resource->isPurgeable());
474                 resource->cacheAccess().release();
475             }
476         }
477     }
478 
479     bool stillOverbudget = this->overBudget();
480     while (stillOverbudget && fPurgeableQueue.count()) {
481         GrGpuResource* resource = fPurgeableQueue.peek();
482         SkASSERT(resource->isPurgeable());
483         resource->cacheAccess().release();
484         stillOverbudget = this->overBudget();
485     }
486 
487     this->validate();
488 
489     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         fRequestFlush = true;
493     }
494 }
495 
purgeAllUnlocked()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 
purgeResourcesNotUsedSince(GrStdSteadyClock::time_point purgeTime)508 void GrResourceCache::purgeResourcesNotUsedSince(GrStdSteadyClock::time_point purgeTime) {
509     while (fPurgeableQueue.count()) {
510         const GrStdSteadyClock::time_point resourceTime =
511                 fPurgeableQueue.peek()->cacheAccess().timeWhenResourceBecamePurgeable();
512         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             break;
519         }
520         GrGpuResource* resource = fPurgeableQueue.peek();
521         SkASSERT(resource->isPurgeable());
522         resource->cacheAccess().release();
523     }
524 }
525 
processInvalidUniqueKeys(const SkTArray<GrUniqueKeyInvalidatedMessage> & msgs)526 void GrResourceCache::processInvalidUniqueKeys(
527     const SkTArray<GrUniqueKeyInvalidatedMessage>& msgs) {
528     for (int i = 0; i < msgs.count(); ++i) {
529         GrGpuResource* resource = this->findAndRefUniqueResource(msgs[i].key());
530         if (resource) {
531             resource->resourcePriv().removeUniqueKey();
532             resource->unref(); // If this resource is now purgeable, the cache will be notified.
533         }
534     }
535 }
536 
addToNonpurgeableArray(GrGpuResource * resource)537 void GrResourceCache::addToNonpurgeableArray(GrGpuResource* resource) {
538     int index = fNonpurgeableResources.count();
539     *fNonpurgeableResources.append() = resource;
540     *resource->cacheAccess().accessCacheIndex() = index;
541 }
542 
removeFromNonpurgeableArray(GrGpuResource * resource)543 void GrResourceCache::removeFromNonpurgeableArray(GrGpuResource* resource) {
544     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     GrGpuResource* tail = *(fNonpurgeableResources.end() - 1);
548     SkASSERT(fNonpurgeableResources[*index] == resource);
549     fNonpurgeableResources[*index] = tail;
550     *tail->cacheAccess().accessCacheIndex() = *index;
551     fNonpurgeableResources.pop();
552     SkDEBUGCODE(*index = -1);
553 }
554 
getNextTimestamp()555 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     if (0 == fTimestamp) {
559         int count = this->getResourceCount();
560         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             SkTDArray<GrGpuResource*> sortedPurgeableResources;
565             sortedPurgeableResources.setReserve(fPurgeableQueue.count());
566 
567             while (fPurgeableQueue.count()) {
568                 *sortedPurgeableResources.append() = fPurgeableQueue.peek();
569                 fPurgeableQueue.pop();
570             }
571 
572             SkTQSort(fNonpurgeableResources.begin(), fNonpurgeableResources.end() - 1,
573                      CompareTimestamp);
574 
575             // Pick resources out of the purgeable and non-purgeable arrays based on lowest
576             // timestamp and assign new timestamps.
577             int currP = 0;
578             int currNP = 0;
579             while (currP < sortedPurgeableResources.count() &&
580                    currNP < fNonpurgeableResources.count()) {
581                 uint32_t tsP = sortedPurgeableResources[currP]->cacheAccess().timestamp();
582                 uint32_t tsNP = fNonpurgeableResources[currNP]->cacheAccess().timestamp();
583                 SkASSERT(tsP != tsNP);
584                 if (tsP < tsNP) {
585                     sortedPurgeableResources[currP++]->cacheAccess().setTimestamp(fTimestamp++);
586                 } else {
587                     // Correct the index in the nonpurgeable array stored on the resource post-sort.
588                     *fNonpurgeableResources[currNP]->cacheAccess().accessCacheIndex() = currNP;
589                     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             while (currP < sortedPurgeableResources.count()) {
595                 sortedPurgeableResources[currP++]->cacheAccess().setTimestamp(fTimestamp++);
596             }
597             while (currNP < fNonpurgeableResources.count()) {
598                 *fNonpurgeableResources[currNP]->cacheAccess().accessCacheIndex() = currNP;
599                 fNonpurgeableResources[currNP++]->cacheAccess().setTimestamp(fTimestamp++);
600             }
601 
602             // Rebuild the queue.
603             for (int i = 0; i < sortedPurgeableResources.count(); ++i) {
604                 fPurgeableQueue.insert(sortedPurgeableResources[i]);
605             }
606 
607             this->validate();
608             SkASSERT(count == this->getResourceCount());
609 
610             // count should be the next timestamp we return.
611             SkASSERT(fTimestamp == SkToU32(count));
612         }
613     }
614     return fTimestamp++;
615 }
616 
notifyFlushOccurred(FlushType type)617 void GrResourceCache::notifyFlushOccurred(FlushType type) {
618     switch (type) {
619         case FlushType::kImmediateMode:
620             break;
621         case FlushType::kCacheRequested:
622             SkASSERT(fRequestFlush);
623             fRequestFlush = false;
624             break;
625         case FlushType::kExternal:
626             ++fExternalFlushCnt;
627             if (0 == fExternalFlushCnt) {
628                 // When this wraps just reset all the purgeable resources' last used flush state.
629                 for (int i = 0; i < fPurgeableQueue.count(); ++i) {
630                     fPurgeableQueue.at(i)->cacheAccess().setFlushCntWhenResourceBecamePurgeable(0);
631                 }
632             }
633             break;
634     }
635     this->purgeAsNeeded();
636 }
637 
dumpMemoryStatistics(SkTraceMemoryDump * traceMemoryDump) const638 void GrResourceCache::dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const {
639     for (int i = 0; i < fNonpurgeableResources.count(); ++i) {
640         fNonpurgeableResources[i]->dumpMemoryStatistics(traceMemoryDump);
641     }
642     for (int i = 0; i < fPurgeableQueue.count(); ++i) {
643         fPurgeableQueue.at(i)->dumpMemoryStatistics(traceMemoryDump);
644     }
645 }
646 
647 #ifdef SK_DEBUG
validate() const648 void GrResourceCache::validate() const {
649     // Reduce the frequency of validations for large resource counts.
650     static SkRandom gRandom;
651     int mask = (SkNextPow2(fCount + 1) >> 5) - 1;
652     if (~mask && (gRandom.nextU() & mask)) {
653         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         Stats(const GrResourceCache* cache) {
668             memset(this, 0, sizeof(*this));
669             fScratchMap = &cache->fScratchMap;
670             fUniqueHash = &cache->fUniqueHash;
671         }
672 
673         void update(GrGpuResource* resource) {
674             fBytes += resource->gpuMemorySize();
675 
676             if (!resource->isPurgeable()) {
677                 ++fLocked;
678             }
679 
680             const GrScratchKey& scratchKey = resource->resourcePriv().getScratchKey();
681             const GrUniqueKey& uniqueKey = resource->getUniqueKey();
682 
683             if (resource->cacheAccess().isScratch()) {
684                 SkASSERT(!uniqueKey.isValid());
685                 ++fScratch;
686                 SkASSERT(fScratchMap->countForKey(scratchKey));
687                 SkASSERT(!resource->resourcePriv().refsWrappedObjects());
688             } else if (scratchKey.isValid()) {
689                 SkASSERT(SkBudgeted::kNo == resource->resourcePriv().isBudgeted() ||
690                          uniqueKey.isValid());
691                 if (!uniqueKey.isValid()) {
692                     ++fCouldBeScratch;
693                     SkASSERT(fScratchMap->countForKey(scratchKey));
694                 }
695                 SkASSERT(!resource->resourcePriv().refsWrappedObjects());
696             }
697             if (uniqueKey.isValid()) {
698                 ++fContent;
699                 SkASSERT(fUniqueHash->find(uniqueKey) == resource);
700                 SkASSERT(!resource->resourcePriv().refsWrappedObjects());
701                 SkASSERT(SkBudgeted::kYes == resource->resourcePriv().isBudgeted());
702 
703                 if (scratchKey.isValid()) {
704                     SkASSERT(!fScratchMap->has(resource, scratchKey));
705                 }
706             }
707 
708             if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
709                 ++fBudgetedCount;
710                 fBudgetedBytes += resource->gpuMemorySize();
711             }
712         }
713     };
714 
715     {
716         ScratchMap::ConstIter iter(&fScratchMap);
717 
718         int count = 0;
719         for ( ; !iter.done(); ++iter) {
720             const GrGpuResource* resource = *iter;
721             SkASSERT(resource->resourcePriv().getScratchKey().isValid());
722             SkASSERT(!resource->getUniqueKey().isValid());
723             count++;
724         }
725         SkASSERT(count == fScratchMap.count()); // ensure the iterator is working correctly
726     }
727 
728     Stats stats(this);
729 
730     for (int i = 0; i < fNonpurgeableResources.count(); ++i) {
731         SkASSERT(!fNonpurgeableResources[i]->isPurgeable() ||
732                  fNewlyPurgeableResourceForValidation == fNonpurgeableResources[i]);
733         SkASSERT(*fNonpurgeableResources[i]->cacheAccess().accessCacheIndex() == i);
734         SkASSERT(!fNonpurgeableResources[i]->wasDestroyed());
735         stats.update(fNonpurgeableResources[i]);
736     }
737     for (int i = 0; i < fPurgeableQueue.count(); ++i) {
738         SkASSERT(fPurgeableQueue.at(i)->isPurgeable());
739         SkASSERT(*fPurgeableQueue.at(i)->cacheAccess().accessCacheIndex() == i);
740         SkASSERT(!fPurgeableQueue.at(i)->wasDestroyed());
741         stats.update(fPurgeableQueue.at(i));
742     }
743 
744     SkASSERT(fCount == this->getResourceCount());
745     SkASSERT(fBudgetedCount <= fCount);
746     SkASSERT(fBudgetedBytes <= fBytes);
747     SkASSERT(stats.fBytes == fBytes);
748     SkASSERT(stats.fBudgetedBytes == fBudgetedBytes);
749     SkASSERT(stats.fBudgetedCount == fBudgetedCount);
750 #if GR_CACHE_STATS
751     SkASSERT(fBudgetedHighWaterCount <= fHighWaterCount);
752     SkASSERT(fBudgetedHighWaterBytes <= fHighWaterBytes);
753     SkASSERT(fBytes <= fHighWaterBytes);
754     SkASSERT(fCount <= fHighWaterCount);
755     SkASSERT(fBudgetedBytes <= fBudgetedHighWaterBytes);
756     SkASSERT(fBudgetedCount <= fBudgetedHighWaterCount);
757 #endif
758     SkASSERT(stats.fContent == fUniqueHash.count());
759     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 
isInCache(const GrGpuResource * resource) const767 bool GrResourceCache::isInCache(const GrGpuResource* resource) const {
768     int index = *resource->cacheAccess().accessCacheIndex();
769     if (index < 0) {
770         return false;
771     }
772     if (index < fPurgeableQueue.count() && fPurgeableQueue.at(index) == resource) {
773         return true;
774     }
775     if (index < fNonpurgeableResources.count() && fNonpurgeableResources[index] == resource) {
776         return true;
777     }
778     SkDEBUGFAIL("Resource index should be -1 or the resource should be in the cache.");
779     return false;
780 }
781 
782 #endif
783