blob: a2fde2fef22021d67e2a0ea671175cd5181472ab [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
bsalomonf99e9612015-02-19 08:24:16 -0800240void GrResourceCache::removeUniqueKey(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.
bsalomonf99e9612015-02-19 08:24:16 -0800243 if (resource->getUniqueKey().isValid()) {
244 SkASSERT(resource == fUniqueHash.find(resource->getUniqueKey()));
245 fUniqueHash.remove(resource->getUniqueKey());
246 }
247 resource->cacheAccess().removeUniqueKey();
248 this->validate();
bsalomon23e619c2015-02-06 11:54:28 -0800249}
250
bsalomonf99e9612015-02-19 08:24:16 -0800251void GrResourceCache::changeUniqueKey(GrGpuResource* resource, const GrUniqueKey& newKey) {
bsalomon8b79d232014-11-10 10:19:06 -0800252 SkASSERT(resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800253 SkASSERT(this->isInCache(resource));
bsalomon8b79d232014-11-10 10:19:06 -0800254
bsalomonf99e9612015-02-19 08:24:16 -0800255 // Remove the entry for this resource if it already has a unique key.
256 if (resource->getUniqueKey().isValid()) {
257 SkASSERT(resource == fUniqueHash.find(resource->getUniqueKey()));
258 fUniqueHash.remove(resource->getUniqueKey());
259 SkASSERT(NULL == fUniqueHash.find(resource->getUniqueKey()));
bsalomon8b79d232014-11-10 10:19:06 -0800260 }
261
bsalomonf99e9612015-02-19 08:24:16 -0800262 // If another resource has the new key, remove its key then install the key on this resource.
263 if (newKey.isValid()) {
264 if (GrGpuResource* old = fUniqueHash.find(newKey)) {
265 // If the old resource using the key is purgeable and is unreachable, then remove it.
266 if (!old->resourcePriv().getScratchKey().isValid() && old->isPurgeable()) {
267 // release may call validate() which will assert that resource is in fUniqueHash
268 // if it has a valid key. So in debug reset the key here before we assign it.
269 SkDEBUGCODE(resource->cacheAccess().removeUniqueKey();)
270 old->cacheAccess().release();
271 } else {
272 fUniqueHash.remove(newKey);
273 old->cacheAccess().removeUniqueKey();
274 }
275 }
276 SkASSERT(NULL == fUniqueHash.find(newKey));
277 resource->cacheAccess().setUniqueKey(newKey);
278 fUniqueHash.add(resource);
279 } else {
280 resource->cacheAccess().removeUniqueKey();
281 }
282
bsalomon71cb0c22014-11-14 12:10:14 -0800283 this->validate();
bsalomon8b79d232014-11-10 10:19:06 -0800284}
bsalomon71cb0c22014-11-14 12:10:14 -0800285
bsalomon9f2d1572015-02-17 11:47:40 -0800286void GrResourceCache::refAndMakeResourceMRU(GrGpuResource* resource) {
bsalomon71cb0c22014-11-14 12:10:14 -0800287 SkASSERT(resource);
288 SkASSERT(this->isInCache(resource));
bsalomon9f2d1572015-02-17 11:47:40 -0800289 if (resource->isPurgeable()) {
290 // It's about to become unpurgeable.
291 fPurgeableQueue.remove(resource);
bsalomonf320e042015-02-17 15:09:34 -0800292 this->addToNonpurgeableArray(resource);
bsalomon9f2d1572015-02-17 11:47:40 -0800293 }
294 resource->ref();
295 resource->cacheAccess().setTimestamp(fTimestamp++);
bsalomonf320e042015-02-17 15:09:34 -0800296 this->validate();
bsalomon71cb0c22014-11-14 12:10:14 -0800297}
298
bsalomon0ea80f42015-02-11 10:49:59 -0800299void GrResourceCache::notifyPurgeable(GrGpuResource* resource) {
bsalomon71cb0c22014-11-14 12:10:14 -0800300 SkASSERT(resource);
301 SkASSERT(this->isInCache(resource));
bsalomon63c992f2015-01-23 12:47:59 -0800302 SkASSERT(resource->isPurgeable());
bsalomon71cb0c22014-11-14 12:10:14 -0800303
bsalomonf320e042015-02-17 15:09:34 -0800304 this->removeFromNonpurgeableArray(resource);
bsalomon9f2d1572015-02-17 11:47:40 -0800305 fPurgeableQueue.insert(resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800306
bsalomon9f2d1572015-02-17 11:47:40 -0800307 if (!resource->resourcePriv().isBudgeted()) {
bsalomonc2f35b72015-01-23 07:19:22 -0800308 // Check whether this resource could still be used as a scratch resource.
bsalomon9f2d1572015-02-17 11:47:40 -0800309 if (!resource->cacheAccess().isWrapped() &&
310 resource->resourcePriv().getScratchKey().isValid()) {
bsalomonc2f35b72015-01-23 07:19:22 -0800311 // We won't purge an existing resource to make room for this one.
bsalomonf320e042015-02-17 15:09:34 -0800312 if (fBudgetedCount < fMaxCount &&
313 fBudgetedBytes + resource->gpuMemorySize() <= fMaxBytes) {
bsalomon3582d3e2015-02-13 14:20:05 -0800314 resource->resourcePriv().makeBudgeted();
bsalomon9f2d1572015-02-17 11:47:40 -0800315 return;
bsalomonc2f35b72015-01-23 07:19:22 -0800316 }
bsalomonc2f35b72015-01-23 07:19:22 -0800317 }
318 } else {
bsalomon9f2d1572015-02-17 11:47:40 -0800319 // Purge the resource immediately if we're over budget
bsalomon8718aaf2015-02-19 07:24:21 -0800320 // Also purge if the resource has neither a valid scratch key nor a unique key.
bsalomon3582d3e2015-02-13 14:20:05 -0800321 bool noKey = !resource->resourcePriv().getScratchKey().isValid() &&
bsalomon8718aaf2015-02-19 07:24:21 -0800322 !resource->getUniqueKey().isValid();
bsalomonf320e042015-02-17 15:09:34 -0800323 if (!this->overBudget() && !noKey) {
bsalomon9f2d1572015-02-17 11:47:40 -0800324 return;
bsalomonc2f35b72015-01-23 07:19:22 -0800325 }
326 }
bsalomondace19e2014-11-17 07:34:06 -0800327
bsalomonf320e042015-02-17 15:09:34 -0800328 SkDEBUGCODE(int beforeCount = this->getResourceCount();)
bsalomon9f2d1572015-02-17 11:47:40 -0800329 resource->cacheAccess().release();
330 // We should at least free this resource, perhaps dependent resources as well.
bsalomonf320e042015-02-17 15:09:34 -0800331 SkASSERT(this->getResourceCount() < beforeCount);
bsalomon71cb0c22014-11-14 12:10:14 -0800332 this->validate();
333}
334
bsalomon0ea80f42015-02-11 10:49:59 -0800335void GrResourceCache::didChangeGpuMemorySize(const GrGpuResource* resource, size_t oldSize) {
bsalomon71cb0c22014-11-14 12:10:14 -0800336 // SkASSERT(!fPurging); GrPathRange increases size during flush. :(
337 SkASSERT(resource);
338 SkASSERT(this->isInCache(resource));
339
bsalomondace19e2014-11-17 07:34:06 -0800340 ptrdiff_t delta = resource->gpuMemorySize() - oldSize;
341
342 fBytes += delta;
bsalomon82b1d622014-11-14 13:59:57 -0800343#if GR_CACHE_STATS
344 fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes);
345#endif
bsalomon3582d3e2015-02-13 14:20:05 -0800346 if (resource->resourcePriv().isBudgeted()) {
bsalomondace19e2014-11-17 07:34:06 -0800347 fBudgetedBytes += delta;
348#if GR_CACHE_STATS
349 fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
350#endif
351 }
bsalomon71cb0c22014-11-14 12:10:14 -0800352
353 this->purgeAsNeeded();
354 this->validate();
355}
356
bsalomon0ea80f42015-02-11 10:49:59 -0800357void GrResourceCache::didChangeBudgetStatus(GrGpuResource* resource) {
bsalomon84c8e622014-11-17 09:33:27 -0800358 SkASSERT(resource);
359 SkASSERT(this->isInCache(resource));
360
361 size_t size = resource->gpuMemorySize();
362
bsalomon3582d3e2015-02-13 14:20:05 -0800363 if (resource->resourcePriv().isBudgeted()) {
bsalomon84c8e622014-11-17 09:33:27 -0800364 ++fBudgetedCount;
365 fBudgetedBytes += size;
bsalomonafe30052015-01-16 07:32:33 -0800366#if GR_CACHE_STATS
367 fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
368 fBudgetedHighWaterCount = SkTMax(fBudgetedCount, fBudgetedHighWaterCount);
369#endif
bsalomon84c8e622014-11-17 09:33:27 -0800370 this->purgeAsNeeded();
371 } else {
372 --fBudgetedCount;
373 fBudgetedBytes -= size;
374 }
375
376 this->validate();
377}
378
bsalomon0ea80f42015-02-11 10:49:59 -0800379void GrResourceCache::internalPurgeAsNeeded() {
bsalomonf320e042015-02-17 15:09:34 -0800380 SkASSERT(this->overBudget());
bsalomon71cb0c22014-11-14 12:10:14 -0800381
bsalomon9f2d1572015-02-17 11:47:40 -0800382 bool stillOverbudget = true;
383 while (fPurgeableQueue.count()) {
384 GrGpuResource* resource = fPurgeableQueue.peek();
385 SkASSERT(resource->isPurgeable());
386 resource->cacheAccess().release();
bsalomonf320e042015-02-17 15:09:34 -0800387 if (!this->overBudget()) {
bsalomon9f2d1572015-02-17 11:47:40 -0800388 stillOverbudget = false;
389 break;
bsalomon71cb0c22014-11-14 12:10:14 -0800390 }
bsalomon9f2d1572015-02-17 11:47:40 -0800391 }
bsalomon71cb0c22014-11-14 12:10:14 -0800392
bsalomonb436ed62014-11-17 12:15:56 -0800393 this->validate();
bsalomon9f2d1572015-02-17 11:47:40 -0800394
395 if (stillOverbudget) {
396 // Despite the purge we're still over budget. Call our over budget callback. If this frees
397 // any resources then we'll get notifyPurgeable() calls and take appropriate action.
398 (*fOverBudgetCB)(fOverBudgetData);
399 this->validate();
400 }
bsalomon71cb0c22014-11-14 12:10:14 -0800401}
402
bsalomon0ea80f42015-02-11 10:49:59 -0800403void GrResourceCache::purgeAllUnlocked() {
bsalomon9f2d1572015-02-17 11:47:40 -0800404 // We could disable maintaining the heap property here, but it would add a lot of complexity.
405 // Moreover, this is rarely called.
406 while (fPurgeableQueue.count()) {
407 GrGpuResource* resource = fPurgeableQueue.peek();
408 SkASSERT(resource->isPurgeable());
409 resource->cacheAccess().release();
410 }
bsalomon71cb0c22014-11-14 12:10:14 -0800411
bsalomonb436ed62014-11-17 12:15:56 -0800412 this->validate();
bsalomon71cb0c22014-11-14 12:10:14 -0800413}
414
bsalomon8718aaf2015-02-19 07:24:21 -0800415void GrResourceCache::processInvalidUniqueKeys(
416 const SkTArray<GrUniqueKeyInvalidatedMessage>& msgs) {
bsalomon23e619c2015-02-06 11:54:28 -0800417 for (int i = 0; i < msgs.count(); ++i) {
bsalomon8718aaf2015-02-19 07:24:21 -0800418 GrGpuResource* resource = this->findAndRefUniqueResource(msgs[i].key());
bsalomon23e619c2015-02-06 11:54:28 -0800419 if (resource) {
bsalomon8718aaf2015-02-19 07:24:21 -0800420 resource->resourcePriv().removeUniqueKey();
bsalomon23e619c2015-02-06 11:54:28 -0800421 resource->unref(); // will call notifyPurgeable, if it is indeed now purgeable.
422 }
423 }
424}
425
bsalomonf320e042015-02-17 15:09:34 -0800426void GrResourceCache::addToNonpurgeableArray(GrGpuResource* resource) {
427 int index = fNonpurgeableResources.count();
428 *fNonpurgeableResources.append() = resource;
429 *resource->cacheAccess().accessCacheIndex() = index;
430}
431
432void GrResourceCache::removeFromNonpurgeableArray(GrGpuResource* resource) {
433 int* index = resource->cacheAccess().accessCacheIndex();
434 // Fill the whole we will create in the array with the tail object, adjust its index, and
435 // then pop the array
436 GrGpuResource* tail = *(fNonpurgeableResources.end() - 1);
437 SkASSERT(fNonpurgeableResources[*index] == resource);
438 fNonpurgeableResources[*index] = tail;
439 *tail->cacheAccess().accessCacheIndex() = *index;
440 fNonpurgeableResources.pop();
441 SkDEBUGCODE(*index = -1);
442}
443
bsalomon71cb0c22014-11-14 12:10:14 -0800444#ifdef SK_DEBUG
bsalomon0ea80f42015-02-11 10:49:59 -0800445void GrResourceCache::validate() const {
bsalomonc2f35b72015-01-23 07:19:22 -0800446 // Reduce the frequency of validations for large resource counts.
447 static SkRandom gRandom;
448 int mask = (SkNextPow2(fCount + 1) >> 5) - 1;
449 if (~mask && (gRandom.nextU() & mask)) {
450 return;
451 }
452
bsalomonf320e042015-02-17 15:09:34 -0800453 struct Stats {
454 size_t fBytes;
455 int fBudgetedCount;
456 size_t fBudgetedBytes;
457 int fLocked;
458 int fScratch;
459 int fCouldBeScratch;
460 int fContent;
461 const ScratchMap* fScratchMap;
bsalomon8718aaf2015-02-19 07:24:21 -0800462 const UniqueHash* fUniqueHash;
bsalomon71cb0c22014-11-14 12:10:14 -0800463
bsalomonf320e042015-02-17 15:09:34 -0800464 Stats(const GrResourceCache* cache) {
465 memset(this, 0, sizeof(*this));
466 fScratchMap = &cache->fScratchMap;
bsalomon8718aaf2015-02-19 07:24:21 -0800467 fUniqueHash = &cache->fUniqueHash;
bsalomon71cb0c22014-11-14 12:10:14 -0800468 }
469
bsalomonf320e042015-02-17 15:09:34 -0800470 void update(GrGpuResource* resource) {
471 fBytes += resource->gpuMemorySize();
bsalomondace19e2014-11-17 07:34:06 -0800472
bsalomonf320e042015-02-17 15:09:34 -0800473 if (!resource->isPurgeable()) {
474 ++fLocked;
475 }
bsalomon9f2d1572015-02-17 11:47:40 -0800476
bsalomonf320e042015-02-17 15:09:34 -0800477 if (resource->cacheAccess().isScratch()) {
bsalomon8718aaf2015-02-19 07:24:21 -0800478 SkASSERT(!resource->getUniqueKey().isValid());
bsalomonf320e042015-02-17 15:09:34 -0800479 ++fScratch;
480 SkASSERT(fScratchMap->countForKey(resource->resourcePriv().getScratchKey()));
481 SkASSERT(!resource->cacheAccess().isWrapped());
482 } else if (resource->resourcePriv().getScratchKey().isValid()) {
483 SkASSERT(!resource->resourcePriv().isBudgeted() ||
bsalomon8718aaf2015-02-19 07:24:21 -0800484 resource->getUniqueKey().isValid());
bsalomonf320e042015-02-17 15:09:34 -0800485 ++fCouldBeScratch;
486 SkASSERT(fScratchMap->countForKey(resource->resourcePriv().getScratchKey()));
487 SkASSERT(!resource->cacheAccess().isWrapped());
488 }
bsalomon8718aaf2015-02-19 07:24:21 -0800489 const GrUniqueKey& uniqueKey = resource->getUniqueKey();
490 if (uniqueKey.isValid()) {
bsalomonf320e042015-02-17 15:09:34 -0800491 ++fContent;
bsalomon8718aaf2015-02-19 07:24:21 -0800492 SkASSERT(fUniqueHash->find(uniqueKey) == resource);
bsalomonf320e042015-02-17 15:09:34 -0800493 SkASSERT(!resource->cacheAccess().isWrapped());
494 SkASSERT(resource->resourcePriv().isBudgeted());
495 }
496
497 if (resource->resourcePriv().isBudgeted()) {
498 ++fBudgetedCount;
499 fBudgetedBytes += resource->gpuMemorySize();
500 }
bsalomon9f2d1572015-02-17 11:47:40 -0800501 }
bsalomonf320e042015-02-17 15:09:34 -0800502 };
503
504 Stats stats(this);
505
506 for (int i = 0; i < fNonpurgeableResources.count(); ++i) {
507 SkASSERT(!fNonpurgeableResources[i]->isPurgeable());
508 SkASSERT(*fNonpurgeableResources[i]->cacheAccess().accessCacheIndex() == i);
509 SkASSERT(!fNonpurgeableResources[i]->wasDestroyed());
510 stats.update(fNonpurgeableResources[i]);
bsalomon71cb0c22014-11-14 12:10:14 -0800511 }
bsalomon9f2d1572015-02-17 11:47:40 -0800512 for (int i = 0; i < fPurgeableQueue.count(); ++i) {
513 SkASSERT(fPurgeableQueue.at(i)->isPurgeable());
bsalomonf320e042015-02-17 15:09:34 -0800514 SkASSERT(*fPurgeableQueue.at(i)->cacheAccess().accessCacheIndex() == i);
515 SkASSERT(!fPurgeableQueue.at(i)->wasDestroyed());
516 stats.update(fPurgeableQueue.at(i));
bsalomon9f2d1572015-02-17 11:47:40 -0800517 }
518
bsalomonf320e042015-02-17 15:09:34 -0800519 SkASSERT(fCount == this->getResourceCount());
bsalomondace19e2014-11-17 07:34:06 -0800520 SkASSERT(fBudgetedCount <= fCount);
bsalomonf320e042015-02-17 15:09:34 -0800521 SkASSERT(fBudgetedBytes <= fBytes);
522 SkASSERT(stats.fBytes == fBytes);
523 SkASSERT(stats.fBudgetedBytes == fBudgetedBytes);
524 SkASSERT(stats.fBudgetedCount == fBudgetedCount);
bsalomon71cb0c22014-11-14 12:10:14 -0800525#if GR_CACHE_STATS
bsalomondace19e2014-11-17 07:34:06 -0800526 SkASSERT(fBudgetedHighWaterCount <= fHighWaterCount);
527 SkASSERT(fBudgetedHighWaterBytes <= fHighWaterBytes);
bsalomonf320e042015-02-17 15:09:34 -0800528 SkASSERT(fBytes <= fHighWaterBytes);
529 SkASSERT(fCount <= fHighWaterCount);
530 SkASSERT(fBudgetedBytes <= fBudgetedHighWaterBytes);
531 SkASSERT(fBudgetedCount <= fBudgetedHighWaterCount);
bsalomon71cb0c22014-11-14 12:10:14 -0800532#endif
bsalomon8718aaf2015-02-19 07:24:21 -0800533 SkASSERT(stats.fContent == fUniqueHash.count());
bsalomonf320e042015-02-17 15:09:34 -0800534 SkASSERT(stats.fScratch + stats.fCouldBeScratch == fScratchMap.count());
bsalomon71cb0c22014-11-14 12:10:14 -0800535
bsalomon63c992f2015-01-23 12:47:59 -0800536 // This assertion is not currently valid because we can be in recursive notifyIsPurgeable()
bsalomon12299ab2014-11-14 13:33:09 -0800537 // calls. This will be fixed when subresource registration is explicit.
bsalomondace19e2014-11-17 07:34:06 -0800538 // bool overBudget = budgetedBytes > fMaxBytes || budgetedCount > fMaxCount;
bsalomon12299ab2014-11-14 13:33:09 -0800539 // SkASSERT(!overBudget || locked == count || fPurging);
bsalomon71cb0c22014-11-14 12:10:14 -0800540}
bsalomonf320e042015-02-17 15:09:34 -0800541
542bool GrResourceCache::isInCache(const GrGpuResource* resource) const {
543 int index = *resource->cacheAccess().accessCacheIndex();
544 if (index < 0) {
545 return false;
546 }
547 if (index < fPurgeableQueue.count() && fPurgeableQueue.at(index) == resource) {
548 return true;
549 }
550 if (index < fNonpurgeableResources.count() && fNonpurgeableResources[index] == resource) {
551 return true;
552 }
553 SkDEBUGFAIL("Resource index should be -1 or the resource should be in the cache.");
554 return false;
555}
556
bsalomon71cb0c22014-11-14 12:10:14 -0800557#endif