blob: 5cb1dd2a94f0b2933503500a0c8b93e9ee35aaea [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
bsalomon23e619c2015-02-06 11:54:28 -080016DECLARE_SKMESSAGEBUS_MESSAGE(GrContentKeyInvalidatedMessage);
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
bsalomon24db3b12015-01-23 04:24:04 -080031GrContentKey::Domain GrContentKey::GenerateDomain() {
32 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) {
bsalomon24db3b12015-01-23 04:24:04 -080036 SkFAIL("Too many Content Key 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()
bsalomon71cb0c22014-11-14 12:10:14 -080061 : fMaxCount(kDefaultMaxCount)
62 , fMaxBytes(kDefaultMaxSize)
63#if GR_CACHE_STATS
64 , fHighWaterCount(0)
65 , fHighWaterBytes(0)
bsalomondace19e2014-11-17 07:34:06 -080066 , fBudgetedHighWaterCount(0)
67 , fBudgetedHighWaterBytes(0)
bsalomon71cb0c22014-11-14 12:10:14 -080068#endif
69 , fCount(0)
70 , fBytes(0)
bsalomondace19e2014-11-17 07:34:06 -080071 , fBudgetedCount(0)
72 , fBudgetedBytes(0)
bsalomon71cb0c22014-11-14 12:10:14 -080073 , fPurging(false)
bsalomon63c992f2015-01-23 12:47:59 -080074 , fNewlyPurgeableResourceWhilePurging(false)
bsalomon71cb0c22014-11-14 12:10:14 -080075 , fOverBudgetCB(NULL)
76 , fOverBudgetData(NULL) {
77}
78
bsalomon0ea80f42015-02-11 10:49:59 -080079GrResourceCache::~GrResourceCache() {
bsalomonc8dc1f72014-08-21 13:02:13 -070080 this->releaseAll();
81}
82
bsalomon0ea80f42015-02-11 10:49:59 -080083void GrResourceCache::setLimits(int count, size_t bytes) {
bsalomon71cb0c22014-11-14 12:10:14 -080084 fMaxCount = count;
85 fMaxBytes = bytes;
86 this->purgeAsNeeded();
87}
88
bsalomon0ea80f42015-02-11 10:49:59 -080089void GrResourceCache::insertResource(GrGpuResource* resource) {
bsalomon49f085d2014-09-05 13:34:00 -070090 SkASSERT(resource);
bsalomonc8dc1f72014-08-21 13:02:13 -070091 SkASSERT(!resource->wasDestroyed());
bsalomon16961262014-08-26 14:01:07 -070092 SkASSERT(!this->isInCache(resource));
bsalomon71cb0c22014-11-14 12:10:14 -080093 SkASSERT(!fPurging);
bsalomonc8dc1f72014-08-21 13:02:13 -070094 fResources.addToHead(resource);
bsalomon71cb0c22014-11-14 12:10:14 -080095
bsalomondace19e2014-11-17 07:34:06 -080096 size_t size = resource->gpuMemorySize();
bsalomonc8dc1f72014-08-21 13:02:13 -070097 ++fCount;
bsalomon84c8e622014-11-17 09:33:27 -080098 fBytes += size;
bsalomon82b1d622014-11-14 13:59:57 -080099#if GR_CACHE_STATS
100 fHighWaterCount = SkTMax(fCount, fHighWaterCount);
101 fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes);
102#endif
bsalomon3582d3e2015-02-13 14:20:05 -0800103 if (resource->resourcePriv().isBudgeted()) {
bsalomondace19e2014-11-17 07:34:06 -0800104 ++fBudgetedCount;
105 fBudgetedBytes += size;
106#if GR_CACHE_STATS
107 fBudgetedHighWaterCount = SkTMax(fBudgetedCount, fBudgetedHighWaterCount);
108 fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
109#endif
110 }
bsalomon3582d3e2015-02-13 14:20:05 -0800111 if (resource->resourcePriv().getScratchKey().isValid()) {
bsalomon84c8e622014-11-17 09:33:27 -0800112 SkASSERT(!resource->cacheAccess().isWrapped());
bsalomon3582d3e2015-02-13 14:20:05 -0800113 fScratchMap.insert(resource->resourcePriv().getScratchKey(), resource);
bsalomon744998e2014-08-28 09:54:34 -0700114 }
bsalomon71cb0c22014-11-14 12:10:14 -0800115
116 this->purgeAsNeeded();
bsalomonc8dc1f72014-08-21 13:02:13 -0700117}
118
bsalomon0ea80f42015-02-11 10:49:59 -0800119void GrResourceCache::removeResource(GrGpuResource* resource) {
bsalomon16961262014-08-26 14:01:07 -0700120 SkASSERT(this->isInCache(resource));
bsalomondace19e2014-11-17 07:34:06 -0800121
122 size_t size = resource->gpuMemorySize();
123 --fCount;
124 fBytes -= size;
bsalomon3582d3e2015-02-13 14:20:05 -0800125 if (resource->resourcePriv().isBudgeted()) {
bsalomondace19e2014-11-17 07:34:06 -0800126 --fBudgetedCount;
127 fBudgetedBytes -= size;
128 }
129
130 fResources.remove(resource);
bsalomon3582d3e2015-02-13 14:20:05 -0800131 if (resource->resourcePriv().getScratchKey().isValid()) {
132 fScratchMap.remove(resource->resourcePriv().getScratchKey(), resource);
bsalomon744998e2014-08-28 09:54:34 -0700133 }
bsalomon563ff602015-02-02 17:25:26 -0800134 if (resource->getContentKey().isValid()) {
135 fContentHash.remove(resource->getContentKey());
bsalomon8b79d232014-11-10 10:19:06 -0800136 }
bsalomonb436ed62014-11-17 12:15:56 -0800137 this->validate();
bsalomonc8dc1f72014-08-21 13:02:13 -0700138}
139
bsalomon0ea80f42015-02-11 10:49:59 -0800140void GrResourceCache::abandonAll() {
bsalomon71cb0c22014-11-14 12:10:14 -0800141 AutoValidate av(this);
142
143 SkASSERT(!fPurging);
bsalomonc8dc1f72014-08-21 13:02:13 -0700144 while (GrGpuResource* head = fResources.head()) {
145 SkASSERT(!head->wasDestroyed());
bsalomon12299ab2014-11-14 13:33:09 -0800146 head->cacheAccess().abandon();
bsalomonc8dc1f72014-08-21 13:02:13 -0700147 // abandon should have already removed this from the list.
148 SkASSERT(head != fResources.head());
149 }
bsalomon744998e2014-08-28 09:54:34 -0700150 SkASSERT(!fScratchMap.count());
bsalomon8b79d232014-11-10 10:19:06 -0800151 SkASSERT(!fContentHash.count());
bsalomonc8dc1f72014-08-21 13:02:13 -0700152 SkASSERT(!fCount);
bsalomondace19e2014-11-17 07:34:06 -0800153 SkASSERT(!fBytes);
154 SkASSERT(!fBudgetedCount);
155 SkASSERT(!fBudgetedBytes);
bsalomonc8dc1f72014-08-21 13:02:13 -0700156}
157
bsalomon0ea80f42015-02-11 10:49:59 -0800158void GrResourceCache::releaseAll() {
bsalomon71cb0c22014-11-14 12:10:14 -0800159 AutoValidate av(this);
160
161 SkASSERT(!fPurging);
bsalomonc8dc1f72014-08-21 13:02:13 -0700162 while (GrGpuResource* head = fResources.head()) {
163 SkASSERT(!head->wasDestroyed());
bsalomon12299ab2014-11-14 13:33:09 -0800164 head->cacheAccess().release();
bsalomonc8dc1f72014-08-21 13:02:13 -0700165 // release should have already removed this from the list.
166 SkASSERT(head != fResources.head());
167 }
bsalomon744998e2014-08-28 09:54:34 -0700168 SkASSERT(!fScratchMap.count());
bsalomonc8dc1f72014-08-21 13:02:13 -0700169 SkASSERT(!fCount);
bsalomondace19e2014-11-17 07:34:06 -0800170 SkASSERT(!fBytes);
171 SkASSERT(!fBudgetedCount);
172 SkASSERT(!fBudgetedBytes);
bsalomonc8dc1f72014-08-21 13:02:13 -0700173}
bsalomonbcf0a522014-10-08 08:40:09 -0700174
bsalomon0ea80f42015-02-11 10:49:59 -0800175class GrResourceCache::AvailableForScratchUse {
bsalomonbcf0a522014-10-08 08:40:09 -0700176public:
bsalomon000f8292014-10-15 19:04:14 -0700177 AvailableForScratchUse(bool rejectPendingIO) : fRejectPendingIO(rejectPendingIO) { }
bsalomonbcf0a522014-10-08 08:40:09 -0700178
179 bool operator()(const GrGpuResource* resource) const {
bsalomon12299ab2014-11-14 13:33:09 -0800180 if (resource->internalHasRef() || !resource->cacheAccess().isScratch()) {
bsalomon000f8292014-10-15 19:04:14 -0700181 return false;
bsalomonbcf0a522014-10-08 08:40:09 -0700182 }
bsalomon000f8292014-10-15 19:04:14 -0700183 return !fRejectPendingIO || !resource->internalHasPendingIO();
bsalomonbcf0a522014-10-08 08:40:09 -0700184 }
bsalomon1e2530b2014-10-09 09:57:18 -0700185
bsalomonbcf0a522014-10-08 08:40:09 -0700186private:
bsalomon000f8292014-10-15 19:04:14 -0700187 bool fRejectPendingIO;
bsalomonbcf0a522014-10-08 08:40:09 -0700188};
189
bsalomon0ea80f42015-02-11 10:49:59 -0800190GrGpuResource* GrResourceCache::findAndRefScratchResource(const GrScratchKey& scratchKey,
bsalomon000f8292014-10-15 19:04:14 -0700191 uint32_t flags) {
bsalomon71cb0c22014-11-14 12:10:14 -0800192 SkASSERT(!fPurging);
bsalomon7775c852014-12-30 12:50:52 -0800193 SkASSERT(scratchKey.isValid());
bsalomon000f8292014-10-15 19:04:14 -0700194
bsalomon71cb0c22014-11-14 12:10:14 -0800195 GrGpuResource* resource;
bsalomon000f8292014-10-15 19:04:14 -0700196 if (flags & (kPreferNoPendingIO_ScratchFlag | kRequireNoPendingIO_ScratchFlag)) {
bsalomon71cb0c22014-11-14 12:10:14 -0800197 resource = fScratchMap.find(scratchKey, AvailableForScratchUse(true));
bsalomon000f8292014-10-15 19:04:14 -0700198 if (resource) {
bsalomonb436ed62014-11-17 12:15:56 -0800199 resource->ref();
bsalomon71cb0c22014-11-14 12:10:14 -0800200 this->makeResourceMRU(resource);
bsalomonb436ed62014-11-17 12:15:56 -0800201 this->validate();
202 return resource;
bsalomon000f8292014-10-15 19:04:14 -0700203 } else if (flags & kRequireNoPendingIO_ScratchFlag) {
204 return NULL;
205 }
206 // TODO: fail here when kPrefer is specified, we didn't find a resource without pending io,
207 // but there is still space in our budget for the resource.
208 }
bsalomon71cb0c22014-11-14 12:10:14 -0800209 resource = fScratchMap.find(scratchKey, AvailableForScratchUse(false));
210 if (resource) {
211 resource->ref();
212 this->makeResourceMRU(resource);
bsalomonb436ed62014-11-17 12:15:56 -0800213 this->validate();
bsalomon71cb0c22014-11-14 12:10:14 -0800214 }
215 return resource;
bsalomonbcf0a522014-10-08 08:40:09 -0700216}
bsalomon8b79d232014-11-10 10:19:06 -0800217
bsalomon0ea80f42015-02-11 10:49:59 -0800218void GrResourceCache::willRemoveScratchKey(const GrGpuResource* resource) {
bsalomon3582d3e2015-02-13 14:20:05 -0800219 SkASSERT(resource->resourcePriv().getScratchKey().isValid());
220 fScratchMap.remove(resource->resourcePriv().getScratchKey(), resource);
bsalomon10e23ca2014-11-25 05:52:06 -0800221}
222
bsalomon0ea80f42015-02-11 10:49:59 -0800223void GrResourceCache::willRemoveContentKey(const GrGpuResource* resource) {
bsalomon23e619c2015-02-06 11:54:28 -0800224 // Someone has a ref to this resource in order to invalidate it. When the ref count reaches
225 // zero we will get a notifyPurgable() and figure out what to do with it.
226 SkASSERT(resource->getContentKey().isValid());
227 fContentHash.remove(resource->getContentKey());
228}
229
bsalomon0ea80f42015-02-11 10:49:59 -0800230bool GrResourceCache::didSetContentKey(GrGpuResource* resource) {
bsalomon71cb0c22014-11-14 12:10:14 -0800231 SkASSERT(!fPurging);
bsalomon8b79d232014-11-10 10:19:06 -0800232 SkASSERT(resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800233 SkASSERT(this->isInCache(resource));
bsalomon563ff602015-02-02 17:25:26 -0800234 SkASSERT(resource->getContentKey().isValid());
bsalomon8b79d232014-11-10 10:19:06 -0800235
bsalomon563ff602015-02-02 17:25:26 -0800236 GrGpuResource* res = fContentHash.find(resource->getContentKey());
bsalomon8b79d232014-11-10 10:19:06 -0800237 if (NULL != res) {
238 return false;
239 }
240
241 fContentHash.add(resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800242 this->validate();
bsalomon8b79d232014-11-10 10:19:06 -0800243 return true;
244}
bsalomon71cb0c22014-11-14 12:10:14 -0800245
bsalomon0ea80f42015-02-11 10:49:59 -0800246void GrResourceCache::makeResourceMRU(GrGpuResource* resource) {
bsalomon71cb0c22014-11-14 12:10:14 -0800247 SkASSERT(!fPurging);
248 SkASSERT(resource);
249 SkASSERT(this->isInCache(resource));
250 fResources.remove(resource);
bsalomonb436ed62014-11-17 12:15:56 -0800251 fResources.addToHead(resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800252}
253
bsalomon0ea80f42015-02-11 10:49:59 -0800254void GrResourceCache::notifyPurgeable(GrGpuResource* resource) {
bsalomon71cb0c22014-11-14 12:10:14 -0800255 SkASSERT(resource);
256 SkASSERT(this->isInCache(resource));
bsalomon63c992f2015-01-23 12:47:59 -0800257 SkASSERT(resource->isPurgeable());
bsalomon71cb0c22014-11-14 12:10:14 -0800258
259 // We can't purge if in the middle of purging because purge is iterating. Instead record
bsalomon63c992f2015-01-23 12:47:59 -0800260 // that additional resources became purgeable.
bsalomon71cb0c22014-11-14 12:10:14 -0800261 if (fPurging) {
bsalomon63c992f2015-01-23 12:47:59 -0800262 fNewlyPurgeableResourceWhilePurging = true;
bsalomon71cb0c22014-11-14 12:10:14 -0800263 return;
264 }
265
bsalomonc2f35b72015-01-23 07:19:22 -0800266 bool release = false;
bsalomon71cb0c22014-11-14 12:10:14 -0800267
bsalomonc2f35b72015-01-23 07:19:22 -0800268 if (resource->cacheAccess().isWrapped()) {
269 release = true;
bsalomon3582d3e2015-02-13 14:20:05 -0800270 } else if (!resource->resourcePriv().isBudgeted()) {
bsalomonc2f35b72015-01-23 07:19:22 -0800271 // Check whether this resource could still be used as a scratch resource.
bsalomon3582d3e2015-02-13 14:20:05 -0800272 if (resource->resourcePriv().getScratchKey().isValid()) {
bsalomonc2f35b72015-01-23 07:19:22 -0800273 // We won't purge an existing resource to make room for this one.
274 bool underBudget = fBudgetedCount < fMaxCount &&
275 fBudgetedBytes + resource->gpuMemorySize() <= fMaxBytes;
276 if (underBudget) {
bsalomon3582d3e2015-02-13 14:20:05 -0800277 resource->resourcePriv().makeBudgeted();
bsalomonc2f35b72015-01-23 07:19:22 -0800278 } else {
279 release = true;
280 }
281 } else {
282 release = true;
283 }
284 } else {
285 // Purge the resource if we're over budget
286 bool overBudget = fBudgetedCount > fMaxCount || fBudgetedBytes > fMaxBytes;
bsalomon71cb0c22014-11-14 12:10:14 -0800287
bsalomonc2f35b72015-01-23 07:19:22 -0800288 // Also purge if the resource has neither a valid scratch key nor a content key.
bsalomon3582d3e2015-02-13 14:20:05 -0800289 bool noKey = !resource->resourcePriv().getScratchKey().isValid() &&
bsalomon563ff602015-02-02 17:25:26 -0800290 !resource->getContentKey().isValid();
bsalomonc2f35b72015-01-23 07:19:22 -0800291 if (overBudget || noKey) {
292 release = true;
293 }
294 }
bsalomondace19e2014-11-17 07:34:06 -0800295
bsalomonc2f35b72015-01-23 07:19:22 -0800296 if (release) {
bsalomon71cb0c22014-11-14 12:10:14 -0800297 SkDEBUGCODE(int beforeCount = fCount;)
bsalomon12299ab2014-11-14 13:33:09 -0800298 resource->cacheAccess().release();
299 // We should at least free this resource, perhaps dependent resources as well.
bsalomon71cb0c22014-11-14 12:10:14 -0800300 SkASSERT(fCount < beforeCount);
301 }
bsalomon71cb0c22014-11-14 12:10:14 -0800302 this->validate();
303}
304
bsalomon0ea80f42015-02-11 10:49:59 -0800305void GrResourceCache::didChangeGpuMemorySize(const GrGpuResource* resource, size_t oldSize) {
bsalomon71cb0c22014-11-14 12:10:14 -0800306 // SkASSERT(!fPurging); GrPathRange increases size during flush. :(
307 SkASSERT(resource);
308 SkASSERT(this->isInCache(resource));
309
bsalomondace19e2014-11-17 07:34:06 -0800310 ptrdiff_t delta = resource->gpuMemorySize() - oldSize;
311
312 fBytes += delta;
bsalomon82b1d622014-11-14 13:59:57 -0800313#if GR_CACHE_STATS
314 fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes);
315#endif
bsalomon3582d3e2015-02-13 14:20:05 -0800316 if (resource->resourcePriv().isBudgeted()) {
bsalomondace19e2014-11-17 07:34:06 -0800317 fBudgetedBytes += delta;
318#if GR_CACHE_STATS
319 fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
320#endif
321 }
bsalomon71cb0c22014-11-14 12:10:14 -0800322
323 this->purgeAsNeeded();
324 this->validate();
325}
326
bsalomon0ea80f42015-02-11 10:49:59 -0800327void GrResourceCache::didChangeBudgetStatus(GrGpuResource* resource) {
bsalomon84c8e622014-11-17 09:33:27 -0800328 SkASSERT(!fPurging);
329 SkASSERT(resource);
330 SkASSERT(this->isInCache(resource));
331
332 size_t size = resource->gpuMemorySize();
333
bsalomon3582d3e2015-02-13 14:20:05 -0800334 if (resource->resourcePriv().isBudgeted()) {
bsalomon84c8e622014-11-17 09:33:27 -0800335 ++fBudgetedCount;
336 fBudgetedBytes += size;
bsalomonafe30052015-01-16 07:32:33 -0800337#if GR_CACHE_STATS
338 fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
339 fBudgetedHighWaterCount = SkTMax(fBudgetedCount, fBudgetedHighWaterCount);
340#endif
bsalomon84c8e622014-11-17 09:33:27 -0800341 this->purgeAsNeeded();
342 } else {
343 --fBudgetedCount;
344 fBudgetedBytes -= size;
345 }
346
347 this->validate();
348}
349
bsalomon0ea80f42015-02-11 10:49:59 -0800350void GrResourceCache::internalPurgeAsNeeded() {
bsalomon71cb0c22014-11-14 12:10:14 -0800351 SkASSERT(!fPurging);
bsalomon63c992f2015-01-23 12:47:59 -0800352 SkASSERT(!fNewlyPurgeableResourceWhilePurging);
bsalomondace19e2014-11-17 07:34:06 -0800353 SkASSERT(fBudgetedCount > fMaxCount || fBudgetedBytes > fMaxBytes);
bsalomon71cb0c22014-11-14 12:10:14 -0800354
355 fPurging = true;
356
bsalomon71cb0c22014-11-14 12:10:14 -0800357 bool overBudget = true;
358 do {
bsalomon63c992f2015-01-23 12:47:59 -0800359 fNewlyPurgeableResourceWhilePurging = false;
bsalomon71cb0c22014-11-14 12:10:14 -0800360 ResourceList::Iter resourceIter;
361 GrGpuResource* resource = resourceIter.init(fResources,
362 ResourceList::Iter::kTail_IterStart);
363
364 while (resource) {
365 GrGpuResource* prev = resourceIter.prev();
bsalomon63c992f2015-01-23 12:47:59 -0800366 if (resource->isPurgeable()) {
bsalomon12299ab2014-11-14 13:33:09 -0800367 resource->cacheAccess().release();
bsalomon71cb0c22014-11-14 12:10:14 -0800368 }
369 resource = prev;
bsalomondace19e2014-11-17 07:34:06 -0800370 if (fBudgetedCount <= fMaxCount && fBudgetedBytes <= fMaxBytes) {
bsalomon71cb0c22014-11-14 12:10:14 -0800371 overBudget = false;
372 resource = NULL;
373 }
374 }
375
bsalomon63c992f2015-01-23 12:47:59 -0800376 if (!fNewlyPurgeableResourceWhilePurging && overBudget && fOverBudgetCB) {
bsalomon71cb0c22014-11-14 12:10:14 -0800377 // Despite the purge we're still over budget. Call our over budget callback.
378 (*fOverBudgetCB)(fOverBudgetData);
379 }
bsalomon63c992f2015-01-23 12:47:59 -0800380 } while (overBudget && fNewlyPurgeableResourceWhilePurging);
bsalomon71cb0c22014-11-14 12:10:14 -0800381
bsalomon63c992f2015-01-23 12:47:59 -0800382 fNewlyPurgeableResourceWhilePurging = false;
bsalomon71cb0c22014-11-14 12:10:14 -0800383 fPurging = false;
bsalomonb436ed62014-11-17 12:15:56 -0800384 this->validate();
bsalomon71cb0c22014-11-14 12:10:14 -0800385}
386
bsalomon0ea80f42015-02-11 10:49:59 -0800387void GrResourceCache::purgeAllUnlocked() {
bsalomon71cb0c22014-11-14 12:10:14 -0800388 SkASSERT(!fPurging);
bsalomon63c992f2015-01-23 12:47:59 -0800389 SkASSERT(!fNewlyPurgeableResourceWhilePurging);
bsalomon71cb0c22014-11-14 12:10:14 -0800390
391 fPurging = true;
392
bsalomon71cb0c22014-11-14 12:10:14 -0800393 do {
bsalomon63c992f2015-01-23 12:47:59 -0800394 fNewlyPurgeableResourceWhilePurging = false;
bsalomon71cb0c22014-11-14 12:10:14 -0800395 ResourceList::Iter resourceIter;
396 GrGpuResource* resource =
397 resourceIter.init(fResources, ResourceList::Iter::kTail_IterStart);
398
399 while (resource) {
400 GrGpuResource* prev = resourceIter.prev();
bsalomon63c992f2015-01-23 12:47:59 -0800401 if (resource->isPurgeable()) {
bsalomon12299ab2014-11-14 13:33:09 -0800402 resource->cacheAccess().release();
403 }
bsalomon71cb0c22014-11-14 12:10:14 -0800404 resource = prev;
405 }
406
bsalomon63c992f2015-01-23 12:47:59 -0800407 if (!fNewlyPurgeableResourceWhilePurging && fCount && fOverBudgetCB) {
bsalomon71cb0c22014-11-14 12:10:14 -0800408 (*fOverBudgetCB)(fOverBudgetData);
409 }
bsalomon63c992f2015-01-23 12:47:59 -0800410 } while (fNewlyPurgeableResourceWhilePurging);
bsalomon71cb0c22014-11-14 12:10:14 -0800411 fPurging = false;
bsalomonb436ed62014-11-17 12:15:56 -0800412 this->validate();
bsalomon71cb0c22014-11-14 12:10:14 -0800413}
414
bsalomon0ea80f42015-02-11 10:49:59 -0800415void GrResourceCache::processInvalidContentKeys(
bsalomon23e619c2015-02-06 11:54:28 -0800416 const SkTArray<GrContentKeyInvalidatedMessage>& msgs) {
417 for (int i = 0; i < msgs.count(); ++i) {
418 GrGpuResource* resource = this->findAndRefContentResource(msgs[i].key());
419 if (resource) {
bsalomon3582d3e2015-02-13 14:20:05 -0800420 resource->resourcePriv().removeContentKey();
bsalomon23e619c2015-02-06 11:54:28 -0800421 resource->unref(); // will call notifyPurgeable, if it is indeed now purgeable.
422 }
423 }
424}
425
bsalomon71cb0c22014-11-14 12:10:14 -0800426#ifdef SK_DEBUG
bsalomon0ea80f42015-02-11 10:49:59 -0800427void GrResourceCache::validate() const {
bsalomonc2f35b72015-01-23 07:19:22 -0800428 // Reduce the frequency of validations for large resource counts.
429 static SkRandom gRandom;
430 int mask = (SkNextPow2(fCount + 1) >> 5) - 1;
431 if (~mask && (gRandom.nextU() & mask)) {
432 return;
433 }
434
bsalomon71cb0c22014-11-14 12:10:14 -0800435 size_t bytes = 0;
436 int count = 0;
bsalomondace19e2014-11-17 07:34:06 -0800437 int budgetedCount = 0;
438 size_t budgetedBytes = 0;
bsalomon71cb0c22014-11-14 12:10:14 -0800439 int locked = 0;
440 int scratch = 0;
441 int couldBeScratch = 0;
442 int content = 0;
443
444 ResourceList::Iter iter;
445 GrGpuResource* resource = iter.init(fResources, ResourceList::Iter::kHead_IterStart);
446 for ( ; resource; resource = iter.next()) {
447 bytes += resource->gpuMemorySize();
448 ++count;
449
bsalomon63c992f2015-01-23 12:47:59 -0800450 if (!resource->isPurgeable()) {
bsalomon71cb0c22014-11-14 12:10:14 -0800451 ++locked;
452 }
453
454 if (resource->cacheAccess().isScratch()) {
bsalomon563ff602015-02-02 17:25:26 -0800455 SkASSERT(!resource->getContentKey().isValid());
bsalomon71cb0c22014-11-14 12:10:14 -0800456 ++scratch;
bsalomon3582d3e2015-02-13 14:20:05 -0800457 SkASSERT(fScratchMap.countForKey(resource->resourcePriv().getScratchKey()));
bsalomondace19e2014-11-17 07:34:06 -0800458 SkASSERT(!resource->cacheAccess().isWrapped());
bsalomon3582d3e2015-02-13 14:20:05 -0800459 } else if (resource->resourcePriv().getScratchKey().isValid()) {
460 SkASSERT(!resource->resourcePriv().isBudgeted() ||
bsalomon563ff602015-02-02 17:25:26 -0800461 resource->getContentKey().isValid());
bsalomon71cb0c22014-11-14 12:10:14 -0800462 ++couldBeScratch;
bsalomon3582d3e2015-02-13 14:20:05 -0800463 SkASSERT(fScratchMap.countForKey(resource->resourcePriv().getScratchKey()));
bsalomondace19e2014-11-17 07:34:06 -0800464 SkASSERT(!resource->cacheAccess().isWrapped());
bsalomon71cb0c22014-11-14 12:10:14 -0800465 }
bsalomon563ff602015-02-02 17:25:26 -0800466 const GrContentKey& contentKey = resource->getContentKey();
bsalomon24db3b12015-01-23 04:24:04 -0800467 if (contentKey.isValid()) {
bsalomon71cb0c22014-11-14 12:10:14 -0800468 ++content;
bsalomon24db3b12015-01-23 04:24:04 -0800469 SkASSERT(fContentHash.find(contentKey) == resource);
bsalomondace19e2014-11-17 07:34:06 -0800470 SkASSERT(!resource->cacheAccess().isWrapped());
bsalomon3582d3e2015-02-13 14:20:05 -0800471 SkASSERT(resource->resourcePriv().isBudgeted());
bsalomondace19e2014-11-17 07:34:06 -0800472 }
473
bsalomon3582d3e2015-02-13 14:20:05 -0800474 if (resource->resourcePriv().isBudgeted()) {
bsalomondace19e2014-11-17 07:34:06 -0800475 ++budgetedCount;
476 budgetedBytes += resource->gpuMemorySize();
bsalomon71cb0c22014-11-14 12:10:14 -0800477 }
478 }
479
bsalomondace19e2014-11-17 07:34:06 -0800480 SkASSERT(fBudgetedCount <= fCount);
481 SkASSERT(fBudgetedBytes <= fBudgetedBytes);
bsalomon71cb0c22014-11-14 12:10:14 -0800482 SkASSERT(bytes == fBytes);
483 SkASSERT(count == fCount);
bsalomondace19e2014-11-17 07:34:06 -0800484 SkASSERT(budgetedBytes == fBudgetedBytes);
485 SkASSERT(budgetedCount == fBudgetedCount);
bsalomon71cb0c22014-11-14 12:10:14 -0800486#if GR_CACHE_STATS
bsalomondace19e2014-11-17 07:34:06 -0800487 SkASSERT(fBudgetedHighWaterCount <= fHighWaterCount);
488 SkASSERT(fBudgetedHighWaterBytes <= fHighWaterBytes);
bsalomon71cb0c22014-11-14 12:10:14 -0800489 SkASSERT(bytes <= fHighWaterBytes);
490 SkASSERT(count <= fHighWaterCount);
bsalomondace19e2014-11-17 07:34:06 -0800491 SkASSERT(budgetedBytes <= fBudgetedHighWaterBytes);
492 SkASSERT(budgetedCount <= fBudgetedHighWaterCount);
bsalomon71cb0c22014-11-14 12:10:14 -0800493#endif
494 SkASSERT(content == fContentHash.count());
495 SkASSERT(scratch + couldBeScratch == fScratchMap.count());
496
bsalomon63c992f2015-01-23 12:47:59 -0800497 // This assertion is not currently valid because we can be in recursive notifyIsPurgeable()
bsalomon12299ab2014-11-14 13:33:09 -0800498 // calls. This will be fixed when subresource registration is explicit.
bsalomondace19e2014-11-17 07:34:06 -0800499 // bool overBudget = budgetedBytes > fMaxBytes || budgetedCount > fMaxCount;
bsalomon12299ab2014-11-14 13:33:09 -0800500 // SkASSERT(!overBudget || locked == count || fPurging);
bsalomon71cb0c22014-11-14 12:10:14 -0800501}
502#endif