blob: 8658744d9ab68368b1ebb48c7f205b88f3459c3a [file] [log] [blame]
bsalomonc8dc1f72014-08-21 13:02:13 -07001
2/*
3 * Copyright 2014 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9
bsalomon0ea80f42015-02-11 10:49:59 -080010#include "GrResourceCache.h"
bsalomon3582d3e2015-02-13 14:20:05 -080011#include "GrGpuResourceCacheAccess.h"
bsalomon7775c852014-12-30 12:50:52 -080012#include "SkChecksum.h"
bsalomon71cb0c22014-11-14 12:10:14 -080013#include "SkGr.h"
14#include "SkMessageBus.h"
15
bsalomon8718aaf2015-02-19 07:24:21 -080016DECLARE_SKMESSAGEBUS_MESSAGE(GrUniqueKeyInvalidatedMessage);
bsalomon71cb0c22014-11-14 12:10:14 -080017
18//////////////////////////////////////////////////////////////////////////////
19
bsalomon7775c852014-12-30 12:50:52 -080020GrScratchKey::ResourceType GrScratchKey::GenerateResourceType() {
bsalomon24db3b12015-01-23 04:24:04 -080021 static int32_t gType = INHERITED::kInvalidDomain + 1;
bsalomonfe369ee2014-11-10 11:59:06 -080022
bsalomon7775c852014-12-30 12:50:52 -080023 int32_t type = sk_atomic_inc(&gType);
robertphillips9790a7b2015-01-05 12:29:15 -080024 if (type > SK_MaxU16) {
bsalomon71cb0c22014-11-14 12:10:14 -080025 SkFAIL("Too many Resource Types");
26 }
27
28 return static_cast<ResourceType>(type);
29}
30
bsalomon8718aaf2015-02-19 07:24:21 -080031GrUniqueKey::Domain GrUniqueKey::GenerateDomain() {
bsalomon24db3b12015-01-23 04:24:04 -080032 static int32_t gDomain = INHERITED::kInvalidDomain + 1;
bsalomon7775c852014-12-30 12:50:52 -080033
bsalomon24db3b12015-01-23 04:24:04 -080034 int32_t domain = sk_atomic_inc(&gDomain);
kkinnunen016dffb2015-01-23 06:43:05 -080035 if (domain > SK_MaxU16) {
bsalomon8718aaf2015-02-19 07:24:21 -080036 SkFAIL("Too many GrUniqueKey Domains");
bsalomon7775c852014-12-30 12:50:52 -080037 }
bsalomon24db3b12015-01-23 04:24:04 -080038
39 return static_cast<Domain>(domain);
40}
41uint32_t GrResourceKeyHash(const uint32_t* data, size_t size) {
42 return SkChecksum::Compute(data, size);
bsalomon7775c852014-12-30 12:50:52 -080043}
44
bsalomonfe369ee2014-11-10 11:59:06 -080045//////////////////////////////////////////////////////////////////////////////
46
bsalomon0ea80f42015-02-11 10:49:59 -080047class GrResourceCache::AutoValidate : ::SkNoncopyable {
bsalomon71cb0c22014-11-14 12:10:14 -080048public:
bsalomon0ea80f42015-02-11 10:49:59 -080049 AutoValidate(GrResourceCache* cache) : fCache(cache) { cache->validate(); }
bsalomon71cb0c22014-11-14 12:10:14 -080050 ~AutoValidate() { fCache->validate(); }
51private:
bsalomon0ea80f42015-02-11 10:49:59 -080052 GrResourceCache* fCache;
bsalomon71cb0c22014-11-14 12:10:14 -080053};
54
55 //////////////////////////////////////////////////////////////////////////////
56
57static const int kDefaultMaxCount = 2 * (1 << 10);
58static const size_t kDefaultMaxSize = 96 * (1 << 20);
59
bsalomon0ea80f42015-02-11 10:49:59 -080060GrResourceCache::GrResourceCache()
bsalomon9f2d1572015-02-17 11:47:40 -080061 : fTimestamp(0)
62 , fMaxCount(kDefaultMaxCount)
bsalomon71cb0c22014-11-14 12:10:14 -080063 , fMaxBytes(kDefaultMaxSize)
64#if GR_CACHE_STATS
65 , fHighWaterCount(0)
66 , fHighWaterBytes(0)
bsalomondace19e2014-11-17 07:34:06 -080067 , fBudgetedHighWaterCount(0)
68 , fBudgetedHighWaterBytes(0)
bsalomon71cb0c22014-11-14 12:10:14 -080069#endif
bsalomon71cb0c22014-11-14 12:10:14 -080070 , fBytes(0)
bsalomondace19e2014-11-17 07:34:06 -080071 , fBudgetedCount(0)
72 , fBudgetedBytes(0)
bsalomon71cb0c22014-11-14 12:10:14 -080073 , fOverBudgetCB(NULL)
74 , fOverBudgetData(NULL) {
bsalomonf320e042015-02-17 15:09:34 -080075 SkDEBUGCODE(fCount = 0;)
bsalomon71cb0c22014-11-14 12:10:14 -080076}
77
bsalomon0ea80f42015-02-11 10:49:59 -080078GrResourceCache::~GrResourceCache() {
bsalomonc8dc1f72014-08-21 13:02:13 -070079 this->releaseAll();
80}
81
bsalomon0ea80f42015-02-11 10:49:59 -080082void GrResourceCache::setLimits(int count, size_t bytes) {
bsalomon71cb0c22014-11-14 12:10:14 -080083 fMaxCount = count;
84 fMaxBytes = bytes;
85 this->purgeAsNeeded();
86}
87
bsalomon0ea80f42015-02-11 10:49:59 -080088void GrResourceCache::insertResource(GrGpuResource* resource) {
bsalomon49f085d2014-09-05 13:34:00 -070089 SkASSERT(resource);
bsalomon16961262014-08-26 14:01:07 -070090 SkASSERT(!this->isInCache(resource));
bsalomonf320e042015-02-17 15:09:34 -080091 SkASSERT(!resource->wasDestroyed());
92 SkASSERT(!resource->isPurgeable());
93 this->addToNonpurgeableArray(resource);
bsalomon71cb0c22014-11-14 12:10:14 -080094
bsalomondace19e2014-11-17 07:34:06 -080095 size_t size = resource->gpuMemorySize();
bsalomonf320e042015-02-17 15:09:34 -080096 SkDEBUGCODE(++fCount;)
bsalomon84c8e622014-11-17 09:33:27 -080097 fBytes += size;
bsalomon82b1d622014-11-14 13:59:57 -080098#if GR_CACHE_STATS
bsalomonf320e042015-02-17 15:09:34 -080099 fHighWaterCount = SkTMax(this->getResourceCount(), fHighWaterCount);
bsalomon82b1d622014-11-14 13:59:57 -0800100 fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes);
101#endif
bsalomon3582d3e2015-02-13 14:20:05 -0800102 if (resource->resourcePriv().isBudgeted()) {
bsalomondace19e2014-11-17 07:34:06 -0800103 ++fBudgetedCount;
104 fBudgetedBytes += size;
105#if GR_CACHE_STATS
106 fBudgetedHighWaterCount = SkTMax(fBudgetedCount, fBudgetedHighWaterCount);
107 fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
108#endif
109 }
bsalomon3582d3e2015-02-13 14:20:05 -0800110 if (resource->resourcePriv().getScratchKey().isValid()) {
bsalomon84c8e622014-11-17 09:33:27 -0800111 SkASSERT(!resource->cacheAccess().isWrapped());
bsalomon3582d3e2015-02-13 14:20:05 -0800112 fScratchMap.insert(resource->resourcePriv().getScratchKey(), resource);
bsalomon744998e2014-08-28 09:54:34 -0700113 }
bsalomon9f2d1572015-02-17 11:47:40 -0800114
115 resource->cacheAccess().setTimestamp(fTimestamp++);
116
bsalomon71cb0c22014-11-14 12:10:14 -0800117 this->purgeAsNeeded();
bsalomonc8dc1f72014-08-21 13:02:13 -0700118}
119
bsalomon0ea80f42015-02-11 10:49:59 -0800120void GrResourceCache::removeResource(GrGpuResource* resource) {
bsalomon9f2d1572015-02-17 11:47:40 -0800121 this->validate();
bsalomon16961262014-08-26 14:01:07 -0700122 SkASSERT(this->isInCache(resource));
bsalomondace19e2014-11-17 07:34:06 -0800123
bsalomon9f2d1572015-02-17 11:47:40 -0800124 if (resource->isPurgeable()) {
125 fPurgeableQueue.remove(resource);
bsalomonf320e042015-02-17 15:09:34 -0800126 } else {
127 this->removeFromNonpurgeableArray(resource);
bsalomon9f2d1572015-02-17 11:47:40 -0800128 }
129
bsalomondace19e2014-11-17 07:34:06 -0800130 size_t size = resource->gpuMemorySize();
bsalomonf320e042015-02-17 15:09:34 -0800131 SkDEBUGCODE(--fCount;)
bsalomondace19e2014-11-17 07:34:06 -0800132 fBytes -= size;
bsalomon3582d3e2015-02-13 14:20:05 -0800133 if (resource->resourcePriv().isBudgeted()) {
bsalomondace19e2014-11-17 07:34:06 -0800134 --fBudgetedCount;
135 fBudgetedBytes -= size;
136 }
137
bsalomon3582d3e2015-02-13 14:20:05 -0800138 if (resource->resourcePriv().getScratchKey().isValid()) {
139 fScratchMap.remove(resource->resourcePriv().getScratchKey(), resource);
bsalomon744998e2014-08-28 09:54:34 -0700140 }
bsalomon8718aaf2015-02-19 07:24:21 -0800141 if (resource->getUniqueKey().isValid()) {
142 fUniqueHash.remove(resource->getUniqueKey());
bsalomon8b79d232014-11-10 10:19:06 -0800143 }
bsalomonb436ed62014-11-17 12:15:56 -0800144 this->validate();
bsalomonc8dc1f72014-08-21 13:02:13 -0700145}
146
bsalomon0ea80f42015-02-11 10:49:59 -0800147void GrResourceCache::abandonAll() {
bsalomon71cb0c22014-11-14 12:10:14 -0800148 AutoValidate av(this);
149
bsalomonf320e042015-02-17 15:09:34 -0800150 while (fNonpurgeableResources.count()) {
151 GrGpuResource* back = *(fNonpurgeableResources.end() - 1);
152 SkASSERT(!back->wasDestroyed());
153 back->cacheAccess().abandon();
bsalomonc8dc1f72014-08-21 13:02:13 -0700154 }
bsalomonf320e042015-02-17 15:09:34 -0800155
156 while (fPurgeableQueue.count()) {
157 GrGpuResource* top = fPurgeableQueue.peek();
158 SkASSERT(!top->wasDestroyed());
159 top->cacheAccess().abandon();
160 }
161
bsalomon744998e2014-08-28 09:54:34 -0700162 SkASSERT(!fScratchMap.count());
bsalomon8718aaf2015-02-19 07:24:21 -0800163 SkASSERT(!fUniqueHash.count());
bsalomonc8dc1f72014-08-21 13:02:13 -0700164 SkASSERT(!fCount);
bsalomonf320e042015-02-17 15:09:34 -0800165 SkASSERT(!this->getResourceCount());
bsalomondace19e2014-11-17 07:34:06 -0800166 SkASSERT(!fBytes);
167 SkASSERT(!fBudgetedCount);
168 SkASSERT(!fBudgetedBytes);
bsalomonc8dc1f72014-08-21 13:02:13 -0700169}
170
bsalomon0ea80f42015-02-11 10:49:59 -0800171void GrResourceCache::releaseAll() {
bsalomon71cb0c22014-11-14 12:10:14 -0800172 AutoValidate av(this);
173
bsalomonf320e042015-02-17 15:09:34 -0800174 while(fNonpurgeableResources.count()) {
175 GrGpuResource* back = *(fNonpurgeableResources.end() - 1);
176 SkASSERT(!back->wasDestroyed());
177 back->cacheAccess().release();
bsalomonc8dc1f72014-08-21 13:02:13 -0700178 }
bsalomonf320e042015-02-17 15:09:34 -0800179
180 while (fPurgeableQueue.count()) {
181 GrGpuResource* top = fPurgeableQueue.peek();
182 SkASSERT(!top->wasDestroyed());
183 top->cacheAccess().release();
184 }
185
bsalomon744998e2014-08-28 09:54:34 -0700186 SkASSERT(!fScratchMap.count());
bsalomon8718aaf2015-02-19 07:24:21 -0800187 SkASSERT(!fUniqueHash.count());
bsalomonc8dc1f72014-08-21 13:02:13 -0700188 SkASSERT(!fCount);
bsalomonf320e042015-02-17 15:09:34 -0800189 SkASSERT(!this->getResourceCount());
bsalomondace19e2014-11-17 07:34:06 -0800190 SkASSERT(!fBytes);
191 SkASSERT(!fBudgetedCount);
192 SkASSERT(!fBudgetedBytes);
bsalomonc8dc1f72014-08-21 13:02:13 -0700193}
bsalomonbcf0a522014-10-08 08:40:09 -0700194
bsalomon0ea80f42015-02-11 10:49:59 -0800195class GrResourceCache::AvailableForScratchUse {
bsalomonbcf0a522014-10-08 08:40:09 -0700196public:
bsalomon000f8292014-10-15 19:04:14 -0700197 AvailableForScratchUse(bool rejectPendingIO) : fRejectPendingIO(rejectPendingIO) { }
bsalomonbcf0a522014-10-08 08:40:09 -0700198
199 bool operator()(const GrGpuResource* resource) const {
bsalomon12299ab2014-11-14 13:33:09 -0800200 if (resource->internalHasRef() || !resource->cacheAccess().isScratch()) {
bsalomon000f8292014-10-15 19:04:14 -0700201 return false;
bsalomonbcf0a522014-10-08 08:40:09 -0700202 }
bsalomon000f8292014-10-15 19:04:14 -0700203 return !fRejectPendingIO || !resource->internalHasPendingIO();
bsalomonbcf0a522014-10-08 08:40:09 -0700204 }
bsalomon1e2530b2014-10-09 09:57:18 -0700205
bsalomonbcf0a522014-10-08 08:40:09 -0700206private:
bsalomon000f8292014-10-15 19:04:14 -0700207 bool fRejectPendingIO;
bsalomonbcf0a522014-10-08 08:40:09 -0700208};
209
bsalomon0ea80f42015-02-11 10:49:59 -0800210GrGpuResource* GrResourceCache::findAndRefScratchResource(const GrScratchKey& scratchKey,
bsalomon9f2d1572015-02-17 11:47:40 -0800211 uint32_t flags) {
bsalomon7775c852014-12-30 12:50:52 -0800212 SkASSERT(scratchKey.isValid());
bsalomon000f8292014-10-15 19:04:14 -0700213
bsalomon71cb0c22014-11-14 12:10:14 -0800214 GrGpuResource* resource;
bsalomon000f8292014-10-15 19:04:14 -0700215 if (flags & (kPreferNoPendingIO_ScratchFlag | kRequireNoPendingIO_ScratchFlag)) {
bsalomon71cb0c22014-11-14 12:10:14 -0800216 resource = fScratchMap.find(scratchKey, AvailableForScratchUse(true));
bsalomon000f8292014-10-15 19:04:14 -0700217 if (resource) {
bsalomon9f2d1572015-02-17 11:47:40 -0800218 this->refAndMakeResourceMRU(resource);
bsalomonb436ed62014-11-17 12:15:56 -0800219 this->validate();
220 return resource;
bsalomon000f8292014-10-15 19:04:14 -0700221 } else if (flags & kRequireNoPendingIO_ScratchFlag) {
222 return NULL;
223 }
224 // TODO: fail here when kPrefer is specified, we didn't find a resource without pending io,
225 // but there is still space in our budget for the resource.
226 }
bsalomon71cb0c22014-11-14 12:10:14 -0800227 resource = fScratchMap.find(scratchKey, AvailableForScratchUse(false));
228 if (resource) {
bsalomon9f2d1572015-02-17 11:47:40 -0800229 this->refAndMakeResourceMRU(resource);
bsalomonb436ed62014-11-17 12:15:56 -0800230 this->validate();
bsalomon71cb0c22014-11-14 12:10:14 -0800231 }
232 return resource;
bsalomonbcf0a522014-10-08 08:40:09 -0700233}
bsalomon8b79d232014-11-10 10:19:06 -0800234
bsalomon0ea80f42015-02-11 10:49:59 -0800235void GrResourceCache::willRemoveScratchKey(const GrGpuResource* resource) {
bsalomon3582d3e2015-02-13 14:20:05 -0800236 SkASSERT(resource->resourcePriv().getScratchKey().isValid());
237 fScratchMap.remove(resource->resourcePriv().getScratchKey(), resource);
bsalomon10e23ca2014-11-25 05:52:06 -0800238}
239
bsalomon8718aaf2015-02-19 07:24:21 -0800240void GrResourceCache::willRemoveUniqueKey(const GrGpuResource* resource) {
bsalomon23e619c2015-02-06 11:54:28 -0800241 // Someone has a ref to this resource in order to invalidate it. When the ref count reaches
242 // zero we will get a notifyPurgable() and figure out what to do with it.
bsalomon8718aaf2015-02-19 07:24:21 -0800243 SkASSERT(resource->getUniqueKey().isValid());
244 fUniqueHash.remove(resource->getUniqueKey());
bsalomon23e619c2015-02-06 11:54:28 -0800245}
246
bsalomon8718aaf2015-02-19 07:24:21 -0800247bool GrResourceCache::didSetUniqueKey(GrGpuResource* resource) {
bsalomon8b79d232014-11-10 10:19:06 -0800248 SkASSERT(resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800249 SkASSERT(this->isInCache(resource));
bsalomon8718aaf2015-02-19 07:24:21 -0800250 SkASSERT(resource->getUniqueKey().isValid());
bsalomon8b79d232014-11-10 10:19:06 -0800251
bsalomon8718aaf2015-02-19 07:24:21 -0800252 GrGpuResource* res = fUniqueHash.find(resource->getUniqueKey());
bsalomon8b79d232014-11-10 10:19:06 -0800253 if (NULL != res) {
254 return false;
255 }
256
bsalomon8718aaf2015-02-19 07:24:21 -0800257 fUniqueHash.add(resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800258 this->validate();
bsalomon8b79d232014-11-10 10:19:06 -0800259 return true;
260}
bsalomon71cb0c22014-11-14 12:10:14 -0800261
bsalomon9f2d1572015-02-17 11:47:40 -0800262void GrResourceCache::refAndMakeResourceMRU(GrGpuResource* resource) {
bsalomon71cb0c22014-11-14 12:10:14 -0800263 SkASSERT(resource);
264 SkASSERT(this->isInCache(resource));
bsalomon9f2d1572015-02-17 11:47:40 -0800265 if (resource->isPurgeable()) {
266 // It's about to become unpurgeable.
267 fPurgeableQueue.remove(resource);
bsalomonf320e042015-02-17 15:09:34 -0800268 this->addToNonpurgeableArray(resource);
bsalomon9f2d1572015-02-17 11:47:40 -0800269 }
270 resource->ref();
271 resource->cacheAccess().setTimestamp(fTimestamp++);
bsalomonf320e042015-02-17 15:09:34 -0800272 this->validate();
bsalomon71cb0c22014-11-14 12:10:14 -0800273}
274
bsalomon0ea80f42015-02-11 10:49:59 -0800275void GrResourceCache::notifyPurgeable(GrGpuResource* resource) {
bsalomon71cb0c22014-11-14 12:10:14 -0800276 SkASSERT(resource);
277 SkASSERT(this->isInCache(resource));
bsalomon63c992f2015-01-23 12:47:59 -0800278 SkASSERT(resource->isPurgeable());
bsalomon71cb0c22014-11-14 12:10:14 -0800279
bsalomonf320e042015-02-17 15:09:34 -0800280 this->removeFromNonpurgeableArray(resource);
bsalomon9f2d1572015-02-17 11:47:40 -0800281 fPurgeableQueue.insert(resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800282
bsalomon9f2d1572015-02-17 11:47:40 -0800283 if (!resource->resourcePriv().isBudgeted()) {
bsalomonc2f35b72015-01-23 07:19:22 -0800284 // Check whether this resource could still be used as a scratch resource.
bsalomon9f2d1572015-02-17 11:47:40 -0800285 if (!resource->cacheAccess().isWrapped() &&
286 resource->resourcePriv().getScratchKey().isValid()) {
bsalomonc2f35b72015-01-23 07:19:22 -0800287 // We won't purge an existing resource to make room for this one.
bsalomonf320e042015-02-17 15:09:34 -0800288 if (fBudgetedCount < fMaxCount &&
289 fBudgetedBytes + resource->gpuMemorySize() <= fMaxBytes) {
bsalomon3582d3e2015-02-13 14:20:05 -0800290 resource->resourcePriv().makeBudgeted();
bsalomon9f2d1572015-02-17 11:47:40 -0800291 return;
bsalomonc2f35b72015-01-23 07:19:22 -0800292 }
bsalomonc2f35b72015-01-23 07:19:22 -0800293 }
294 } else {
bsalomon9f2d1572015-02-17 11:47:40 -0800295 // Purge the resource immediately if we're over budget
bsalomon8718aaf2015-02-19 07:24:21 -0800296 // Also purge if the resource has neither a valid scratch key nor a unique key.
bsalomon3582d3e2015-02-13 14:20:05 -0800297 bool noKey = !resource->resourcePriv().getScratchKey().isValid() &&
bsalomon8718aaf2015-02-19 07:24:21 -0800298 !resource->getUniqueKey().isValid();
bsalomonf320e042015-02-17 15:09:34 -0800299 if (!this->overBudget() && !noKey) {
bsalomon9f2d1572015-02-17 11:47:40 -0800300 return;
bsalomonc2f35b72015-01-23 07:19:22 -0800301 }
302 }
bsalomondace19e2014-11-17 07:34:06 -0800303
bsalomonf320e042015-02-17 15:09:34 -0800304 SkDEBUGCODE(int beforeCount = this->getResourceCount();)
bsalomon9f2d1572015-02-17 11:47:40 -0800305 resource->cacheAccess().release();
306 // We should at least free this resource, perhaps dependent resources as well.
bsalomonf320e042015-02-17 15:09:34 -0800307 SkASSERT(this->getResourceCount() < beforeCount);
bsalomon71cb0c22014-11-14 12:10:14 -0800308 this->validate();
309}
310
bsalomon0ea80f42015-02-11 10:49:59 -0800311void GrResourceCache::didChangeGpuMemorySize(const GrGpuResource* resource, size_t oldSize) {
bsalomon71cb0c22014-11-14 12:10:14 -0800312 // SkASSERT(!fPurging); GrPathRange increases size during flush. :(
313 SkASSERT(resource);
314 SkASSERT(this->isInCache(resource));
315
bsalomondace19e2014-11-17 07:34:06 -0800316 ptrdiff_t delta = resource->gpuMemorySize() - oldSize;
317
318 fBytes += delta;
bsalomon82b1d622014-11-14 13:59:57 -0800319#if GR_CACHE_STATS
320 fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes);
321#endif
bsalomon3582d3e2015-02-13 14:20:05 -0800322 if (resource->resourcePriv().isBudgeted()) {
bsalomondace19e2014-11-17 07:34:06 -0800323 fBudgetedBytes += delta;
324#if GR_CACHE_STATS
325 fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
326#endif
327 }
bsalomon71cb0c22014-11-14 12:10:14 -0800328
329 this->purgeAsNeeded();
330 this->validate();
331}
332
bsalomon0ea80f42015-02-11 10:49:59 -0800333void GrResourceCache::didChangeBudgetStatus(GrGpuResource* resource) {
bsalomon84c8e622014-11-17 09:33:27 -0800334 SkASSERT(resource);
335 SkASSERT(this->isInCache(resource));
336
337 size_t size = resource->gpuMemorySize();
338
bsalomon3582d3e2015-02-13 14:20:05 -0800339 if (resource->resourcePriv().isBudgeted()) {
bsalomon84c8e622014-11-17 09:33:27 -0800340 ++fBudgetedCount;
341 fBudgetedBytes += size;
bsalomonafe30052015-01-16 07:32:33 -0800342#if GR_CACHE_STATS
343 fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
344 fBudgetedHighWaterCount = SkTMax(fBudgetedCount, fBudgetedHighWaterCount);
345#endif
bsalomon84c8e622014-11-17 09:33:27 -0800346 this->purgeAsNeeded();
347 } else {
348 --fBudgetedCount;
349 fBudgetedBytes -= size;
350 }
351
352 this->validate();
353}
354
bsalomon0ea80f42015-02-11 10:49:59 -0800355void GrResourceCache::internalPurgeAsNeeded() {
bsalomonf320e042015-02-17 15:09:34 -0800356 SkASSERT(this->overBudget());
bsalomon71cb0c22014-11-14 12:10:14 -0800357
bsalomon9f2d1572015-02-17 11:47:40 -0800358 bool stillOverbudget = true;
359 while (fPurgeableQueue.count()) {
360 GrGpuResource* resource = fPurgeableQueue.peek();
361 SkASSERT(resource->isPurgeable());
362 resource->cacheAccess().release();
bsalomonf320e042015-02-17 15:09:34 -0800363 if (!this->overBudget()) {
bsalomon9f2d1572015-02-17 11:47:40 -0800364 stillOverbudget = false;
365 break;
bsalomon71cb0c22014-11-14 12:10:14 -0800366 }
bsalomon9f2d1572015-02-17 11:47:40 -0800367 }
bsalomon71cb0c22014-11-14 12:10:14 -0800368
bsalomonb436ed62014-11-17 12:15:56 -0800369 this->validate();
bsalomon9f2d1572015-02-17 11:47:40 -0800370
371 if (stillOverbudget) {
372 // Despite the purge we're still over budget. Call our over budget callback. If this frees
373 // any resources then we'll get notifyPurgeable() calls and take appropriate action.
374 (*fOverBudgetCB)(fOverBudgetData);
375 this->validate();
376 }
bsalomon71cb0c22014-11-14 12:10:14 -0800377}
378
bsalomon0ea80f42015-02-11 10:49:59 -0800379void GrResourceCache::purgeAllUnlocked() {
bsalomon9f2d1572015-02-17 11:47:40 -0800380 // We could disable maintaining the heap property here, but it would add a lot of complexity.
381 // Moreover, this is rarely called.
382 while (fPurgeableQueue.count()) {
383 GrGpuResource* resource = fPurgeableQueue.peek();
384 SkASSERT(resource->isPurgeable());
385 resource->cacheAccess().release();
386 }
bsalomon71cb0c22014-11-14 12:10:14 -0800387
bsalomonb436ed62014-11-17 12:15:56 -0800388 this->validate();
bsalomon71cb0c22014-11-14 12:10:14 -0800389}
390
bsalomon8718aaf2015-02-19 07:24:21 -0800391void GrResourceCache::processInvalidUniqueKeys(
392 const SkTArray<GrUniqueKeyInvalidatedMessage>& msgs) {
bsalomon23e619c2015-02-06 11:54:28 -0800393 for (int i = 0; i < msgs.count(); ++i) {
bsalomon8718aaf2015-02-19 07:24:21 -0800394 GrGpuResource* resource = this->findAndRefUniqueResource(msgs[i].key());
bsalomon23e619c2015-02-06 11:54:28 -0800395 if (resource) {
bsalomon8718aaf2015-02-19 07:24:21 -0800396 resource->resourcePriv().removeUniqueKey();
bsalomon23e619c2015-02-06 11:54:28 -0800397 resource->unref(); // will call notifyPurgeable, if it is indeed now purgeable.
398 }
399 }
400}
401
bsalomonf320e042015-02-17 15:09:34 -0800402void GrResourceCache::addToNonpurgeableArray(GrGpuResource* resource) {
403 int index = fNonpurgeableResources.count();
404 *fNonpurgeableResources.append() = resource;
405 *resource->cacheAccess().accessCacheIndex() = index;
406}
407
408void GrResourceCache::removeFromNonpurgeableArray(GrGpuResource* resource) {
409 int* index = resource->cacheAccess().accessCacheIndex();
410 // Fill the whole we will create in the array with the tail object, adjust its index, and
411 // then pop the array
412 GrGpuResource* tail = *(fNonpurgeableResources.end() - 1);
413 SkASSERT(fNonpurgeableResources[*index] == resource);
414 fNonpurgeableResources[*index] = tail;
415 *tail->cacheAccess().accessCacheIndex() = *index;
416 fNonpurgeableResources.pop();
417 SkDEBUGCODE(*index = -1);
418}
419
bsalomon71cb0c22014-11-14 12:10:14 -0800420#ifdef SK_DEBUG
bsalomon0ea80f42015-02-11 10:49:59 -0800421void GrResourceCache::validate() const {
bsalomonc2f35b72015-01-23 07:19:22 -0800422 // Reduce the frequency of validations for large resource counts.
423 static SkRandom gRandom;
424 int mask = (SkNextPow2(fCount + 1) >> 5) - 1;
425 if (~mask && (gRandom.nextU() & mask)) {
426 return;
427 }
428
bsalomonf320e042015-02-17 15:09:34 -0800429 struct Stats {
430 size_t fBytes;
431 int fBudgetedCount;
432 size_t fBudgetedBytes;
433 int fLocked;
434 int fScratch;
435 int fCouldBeScratch;
436 int fContent;
437 const ScratchMap* fScratchMap;
bsalomon8718aaf2015-02-19 07:24:21 -0800438 const UniqueHash* fUniqueHash;
bsalomon71cb0c22014-11-14 12:10:14 -0800439
bsalomonf320e042015-02-17 15:09:34 -0800440 Stats(const GrResourceCache* cache) {
441 memset(this, 0, sizeof(*this));
442 fScratchMap = &cache->fScratchMap;
bsalomon8718aaf2015-02-19 07:24:21 -0800443 fUniqueHash = &cache->fUniqueHash;
bsalomon71cb0c22014-11-14 12:10:14 -0800444 }
445
bsalomonf320e042015-02-17 15:09:34 -0800446 void update(GrGpuResource* resource) {
447 fBytes += resource->gpuMemorySize();
bsalomondace19e2014-11-17 07:34:06 -0800448
bsalomonf320e042015-02-17 15:09:34 -0800449 if (!resource->isPurgeable()) {
450 ++fLocked;
451 }
bsalomon9f2d1572015-02-17 11:47:40 -0800452
bsalomonf320e042015-02-17 15:09:34 -0800453 if (resource->cacheAccess().isScratch()) {
bsalomon8718aaf2015-02-19 07:24:21 -0800454 SkASSERT(!resource->getUniqueKey().isValid());
bsalomonf320e042015-02-17 15:09:34 -0800455 ++fScratch;
456 SkASSERT(fScratchMap->countForKey(resource->resourcePriv().getScratchKey()));
457 SkASSERT(!resource->cacheAccess().isWrapped());
458 } else if (resource->resourcePriv().getScratchKey().isValid()) {
459 SkASSERT(!resource->resourcePriv().isBudgeted() ||
bsalomon8718aaf2015-02-19 07:24:21 -0800460 resource->getUniqueKey().isValid());
bsalomonf320e042015-02-17 15:09:34 -0800461 ++fCouldBeScratch;
462 SkASSERT(fScratchMap->countForKey(resource->resourcePriv().getScratchKey()));
463 SkASSERT(!resource->cacheAccess().isWrapped());
464 }
bsalomon8718aaf2015-02-19 07:24:21 -0800465 const GrUniqueKey& uniqueKey = resource->getUniqueKey();
466 if (uniqueKey.isValid()) {
bsalomonf320e042015-02-17 15:09:34 -0800467 ++fContent;
bsalomon8718aaf2015-02-19 07:24:21 -0800468 SkASSERT(fUniqueHash->find(uniqueKey) == resource);
bsalomonf320e042015-02-17 15:09:34 -0800469 SkASSERT(!resource->cacheAccess().isWrapped());
470 SkASSERT(resource->resourcePriv().isBudgeted());
471 }
472
473 if (resource->resourcePriv().isBudgeted()) {
474 ++fBudgetedCount;
475 fBudgetedBytes += resource->gpuMemorySize();
476 }
bsalomon9f2d1572015-02-17 11:47:40 -0800477 }
bsalomonf320e042015-02-17 15:09:34 -0800478 };
479
480 Stats stats(this);
481
482 for (int i = 0; i < fNonpurgeableResources.count(); ++i) {
483 SkASSERT(!fNonpurgeableResources[i]->isPurgeable());
484 SkASSERT(*fNonpurgeableResources[i]->cacheAccess().accessCacheIndex() == i);
485 SkASSERT(!fNonpurgeableResources[i]->wasDestroyed());
486 stats.update(fNonpurgeableResources[i]);
bsalomon71cb0c22014-11-14 12:10:14 -0800487 }
bsalomon9f2d1572015-02-17 11:47:40 -0800488 for (int i = 0; i < fPurgeableQueue.count(); ++i) {
489 SkASSERT(fPurgeableQueue.at(i)->isPurgeable());
bsalomonf320e042015-02-17 15:09:34 -0800490 SkASSERT(*fPurgeableQueue.at(i)->cacheAccess().accessCacheIndex() == i);
491 SkASSERT(!fPurgeableQueue.at(i)->wasDestroyed());
492 stats.update(fPurgeableQueue.at(i));
bsalomon9f2d1572015-02-17 11:47:40 -0800493 }
494
bsalomonf320e042015-02-17 15:09:34 -0800495 SkASSERT(fCount == this->getResourceCount());
bsalomondace19e2014-11-17 07:34:06 -0800496 SkASSERT(fBudgetedCount <= fCount);
bsalomonf320e042015-02-17 15:09:34 -0800497 SkASSERT(fBudgetedBytes <= fBytes);
498 SkASSERT(stats.fBytes == fBytes);
499 SkASSERT(stats.fBudgetedBytes == fBudgetedBytes);
500 SkASSERT(stats.fBudgetedCount == fBudgetedCount);
bsalomon71cb0c22014-11-14 12:10:14 -0800501#if GR_CACHE_STATS
bsalomondace19e2014-11-17 07:34:06 -0800502 SkASSERT(fBudgetedHighWaterCount <= fHighWaterCount);
503 SkASSERT(fBudgetedHighWaterBytes <= fHighWaterBytes);
bsalomonf320e042015-02-17 15:09:34 -0800504 SkASSERT(fBytes <= fHighWaterBytes);
505 SkASSERT(fCount <= fHighWaterCount);
506 SkASSERT(fBudgetedBytes <= fBudgetedHighWaterBytes);
507 SkASSERT(fBudgetedCount <= fBudgetedHighWaterCount);
bsalomon71cb0c22014-11-14 12:10:14 -0800508#endif
bsalomon8718aaf2015-02-19 07:24:21 -0800509 SkASSERT(stats.fContent == fUniqueHash.count());
bsalomonf320e042015-02-17 15:09:34 -0800510 SkASSERT(stats.fScratch + stats.fCouldBeScratch == fScratchMap.count());
bsalomon71cb0c22014-11-14 12:10:14 -0800511
bsalomon63c992f2015-01-23 12:47:59 -0800512 // This assertion is not currently valid because we can be in recursive notifyIsPurgeable()
bsalomon12299ab2014-11-14 13:33:09 -0800513 // calls. This will be fixed when subresource registration is explicit.
bsalomondace19e2014-11-17 07:34:06 -0800514 // bool overBudget = budgetedBytes > fMaxBytes || budgetedCount > fMaxCount;
bsalomon12299ab2014-11-14 13:33:09 -0800515 // SkASSERT(!overBudget || locked == count || fPurging);
bsalomon71cb0c22014-11-14 12:10:14 -0800516}
bsalomonf320e042015-02-17 15:09:34 -0800517
518bool GrResourceCache::isInCache(const GrGpuResource* resource) const {
519 int index = *resource->cacheAccess().accessCacheIndex();
520 if (index < 0) {
521 return false;
522 }
523 if (index < fPurgeableQueue.count() && fPurgeableQueue.at(index) == resource) {
524 return true;
525 }
526 if (index < fNonpurgeableResources.count() && fNonpurgeableResources[index] == resource) {
527 return true;
528 }
529 SkDEBUGFAIL("Resource index should be -1 or the resource should be in the cache.");
530 return false;
531}
532
bsalomon71cb0c22014-11-14 12:10:14 -0800533#endif