blob: 2b842a8f08b686e80a2d2ec4943c74c0e75d9e32 [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"
bsalomonbcf0a522014-10-08 08:40:09 -070011#include "GrGpuResource.h"
bsalomonc8dc1f72014-08-21 13:02:13 -070012
bsalomon7775c852014-12-30 12:50:52 -080013#include "SkChecksum.h"
bsalomon71cb0c22014-11-14 12:10:14 -080014#include "SkGr.h"
15#include "SkMessageBus.h"
16
bsalomon23e619c2015-02-06 11:54:28 -080017DECLARE_SKMESSAGEBUS_MESSAGE(GrContentKeyInvalidatedMessage);
bsalomon71cb0c22014-11-14 12:10:14 -080018
19//////////////////////////////////////////////////////////////////////////////
20
bsalomon7775c852014-12-30 12:50:52 -080021GrScratchKey::ResourceType GrScratchKey::GenerateResourceType() {
bsalomon24db3b12015-01-23 04:24:04 -080022 static int32_t gType = INHERITED::kInvalidDomain + 1;
bsalomonfe369ee2014-11-10 11:59:06 -080023
bsalomon7775c852014-12-30 12:50:52 -080024 int32_t type = sk_atomic_inc(&gType);
robertphillips9790a7b2015-01-05 12:29:15 -080025 if (type > SK_MaxU16) {
bsalomon71cb0c22014-11-14 12:10:14 -080026 SkFAIL("Too many Resource Types");
27 }
28
29 return static_cast<ResourceType>(type);
30}
31
bsalomon24db3b12015-01-23 04:24:04 -080032GrContentKey::Domain GrContentKey::GenerateDomain() {
33 static int32_t gDomain = INHERITED::kInvalidDomain + 1;
bsalomon7775c852014-12-30 12:50:52 -080034
bsalomon24db3b12015-01-23 04:24:04 -080035 int32_t domain = sk_atomic_inc(&gDomain);
kkinnunen016dffb2015-01-23 06:43:05 -080036 if (domain > SK_MaxU16) {
bsalomon24db3b12015-01-23 04:24:04 -080037 SkFAIL("Too many Content Key Domains");
bsalomon7775c852014-12-30 12:50:52 -080038 }
bsalomon24db3b12015-01-23 04:24:04 -080039
40 return static_cast<Domain>(domain);
41}
42uint32_t GrResourceKeyHash(const uint32_t* data, size_t size) {
43 return SkChecksum::Compute(data, size);
bsalomon7775c852014-12-30 12:50:52 -080044}
45
bsalomonfe369ee2014-11-10 11:59:06 -080046//////////////////////////////////////////////////////////////////////////////
47
bsalomon0ea80f42015-02-11 10:49:59 -080048class GrResourceCache::AutoValidate : ::SkNoncopyable {
bsalomon71cb0c22014-11-14 12:10:14 -080049public:
bsalomon0ea80f42015-02-11 10:49:59 -080050 AutoValidate(GrResourceCache* cache) : fCache(cache) { cache->validate(); }
bsalomon71cb0c22014-11-14 12:10:14 -080051 ~AutoValidate() { fCache->validate(); }
52private:
bsalomon0ea80f42015-02-11 10:49:59 -080053 GrResourceCache* fCache;
bsalomon71cb0c22014-11-14 12:10:14 -080054};
55
56 //////////////////////////////////////////////////////////////////////////////
57
58static const int kDefaultMaxCount = 2 * (1 << 10);
59static const size_t kDefaultMaxSize = 96 * (1 << 20);
60
bsalomon0ea80f42015-02-11 10:49:59 -080061GrResourceCache::GrResourceCache()
bsalomon71cb0c22014-11-14 12:10:14 -080062 : fMaxCount(kDefaultMaxCount)
63 , 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
70 , fCount(0)
71 , fBytes(0)
bsalomondace19e2014-11-17 07:34:06 -080072 , fBudgetedCount(0)
73 , fBudgetedBytes(0)
bsalomon71cb0c22014-11-14 12:10:14 -080074 , fPurging(false)
bsalomon63c992f2015-01-23 12:47:59 -080075 , fNewlyPurgeableResourceWhilePurging(false)
bsalomon71cb0c22014-11-14 12:10:14 -080076 , fOverBudgetCB(NULL)
77 , fOverBudgetData(NULL) {
78}
79
bsalomon0ea80f42015-02-11 10:49:59 -080080GrResourceCache::~GrResourceCache() {
bsalomonc8dc1f72014-08-21 13:02:13 -070081 this->releaseAll();
82}
83
bsalomon0ea80f42015-02-11 10:49:59 -080084void GrResourceCache::setLimits(int count, size_t bytes) {
bsalomon71cb0c22014-11-14 12:10:14 -080085 fMaxCount = count;
86 fMaxBytes = bytes;
87 this->purgeAsNeeded();
88}
89
bsalomon0ea80f42015-02-11 10:49:59 -080090void GrResourceCache::insertResource(GrGpuResource* resource) {
bsalomon49f085d2014-09-05 13:34:00 -070091 SkASSERT(resource);
bsalomonc8dc1f72014-08-21 13:02:13 -070092 SkASSERT(!resource->wasDestroyed());
bsalomon16961262014-08-26 14:01:07 -070093 SkASSERT(!this->isInCache(resource));
bsalomon71cb0c22014-11-14 12:10:14 -080094 SkASSERT(!fPurging);
bsalomonc8dc1f72014-08-21 13:02:13 -070095 fResources.addToHead(resource);
bsalomon71cb0c22014-11-14 12:10:14 -080096
bsalomondace19e2014-11-17 07:34:06 -080097 size_t size = resource->gpuMemorySize();
bsalomonc8dc1f72014-08-21 13:02:13 -070098 ++fCount;
bsalomon84c8e622014-11-17 09:33:27 -080099 fBytes += size;
bsalomon82b1d622014-11-14 13:59:57 -0800100#if GR_CACHE_STATS
101 fHighWaterCount = SkTMax(fCount, fHighWaterCount);
102 fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes);
103#endif
bsalomon84c8e622014-11-17 09:33:27 -0800104 if (resource->cacheAccess().isBudgeted()) {
bsalomondace19e2014-11-17 07:34:06 -0800105 ++fBudgetedCount;
106 fBudgetedBytes += size;
107#if GR_CACHE_STATS
108 fBudgetedHighWaterCount = SkTMax(fBudgetedCount, fBudgetedHighWaterCount);
109 fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
110#endif
111 }
bsalomon7775c852014-12-30 12:50:52 -0800112 if (resource->cacheAccess().getScratchKey().isValid()) {
bsalomon84c8e622014-11-17 09:33:27 -0800113 SkASSERT(!resource->cacheAccess().isWrapped());
bsalomon453cf402014-11-11 14:15:57 -0800114 fScratchMap.insert(resource->cacheAccess().getScratchKey(), resource);
bsalomon744998e2014-08-28 09:54:34 -0700115 }
bsalomon71cb0c22014-11-14 12:10:14 -0800116
117 this->purgeAsNeeded();
bsalomonc8dc1f72014-08-21 13:02:13 -0700118}
119
bsalomon0ea80f42015-02-11 10:49:59 -0800120void GrResourceCache::removeResource(GrGpuResource* resource) {
bsalomon16961262014-08-26 14:01:07 -0700121 SkASSERT(this->isInCache(resource));
bsalomondace19e2014-11-17 07:34:06 -0800122
123 size_t size = resource->gpuMemorySize();
124 --fCount;
125 fBytes -= size;
bsalomon84c8e622014-11-17 09:33:27 -0800126 if (resource->cacheAccess().isBudgeted()) {
bsalomondace19e2014-11-17 07:34:06 -0800127 --fBudgetedCount;
128 fBudgetedBytes -= size;
129 }
130
131 fResources.remove(resource);
bsalomon7775c852014-12-30 12:50:52 -0800132 if (resource->cacheAccess().getScratchKey().isValid()) {
bsalomon453cf402014-11-11 14:15:57 -0800133 fScratchMap.remove(resource->cacheAccess().getScratchKey(), resource);
bsalomon744998e2014-08-28 09:54:34 -0700134 }
bsalomon563ff602015-02-02 17:25:26 -0800135 if (resource->getContentKey().isValid()) {
136 fContentHash.remove(resource->getContentKey());
bsalomon8b79d232014-11-10 10:19:06 -0800137 }
bsalomonb436ed62014-11-17 12:15:56 -0800138 this->validate();
bsalomonc8dc1f72014-08-21 13:02:13 -0700139}
140
bsalomon0ea80f42015-02-11 10:49:59 -0800141void GrResourceCache::abandonAll() {
bsalomon71cb0c22014-11-14 12:10:14 -0800142 AutoValidate av(this);
143
144 SkASSERT(!fPurging);
bsalomonc8dc1f72014-08-21 13:02:13 -0700145 while (GrGpuResource* head = fResources.head()) {
146 SkASSERT(!head->wasDestroyed());
bsalomon12299ab2014-11-14 13:33:09 -0800147 head->cacheAccess().abandon();
bsalomonc8dc1f72014-08-21 13:02:13 -0700148 // abandon should have already removed this from the list.
149 SkASSERT(head != fResources.head());
150 }
bsalomon744998e2014-08-28 09:54:34 -0700151 SkASSERT(!fScratchMap.count());
bsalomon8b79d232014-11-10 10:19:06 -0800152 SkASSERT(!fContentHash.count());
bsalomonc8dc1f72014-08-21 13:02:13 -0700153 SkASSERT(!fCount);
bsalomondace19e2014-11-17 07:34:06 -0800154 SkASSERT(!fBytes);
155 SkASSERT(!fBudgetedCount);
156 SkASSERT(!fBudgetedBytes);
bsalomonc8dc1f72014-08-21 13:02:13 -0700157}
158
bsalomon0ea80f42015-02-11 10:49:59 -0800159void GrResourceCache::releaseAll() {
bsalomon71cb0c22014-11-14 12:10:14 -0800160 AutoValidate av(this);
161
162 SkASSERT(!fPurging);
bsalomonc8dc1f72014-08-21 13:02:13 -0700163 while (GrGpuResource* head = fResources.head()) {
164 SkASSERT(!head->wasDestroyed());
bsalomon12299ab2014-11-14 13:33:09 -0800165 head->cacheAccess().release();
bsalomonc8dc1f72014-08-21 13:02:13 -0700166 // release should have already removed this from the list.
167 SkASSERT(head != fResources.head());
168 }
bsalomon744998e2014-08-28 09:54:34 -0700169 SkASSERT(!fScratchMap.count());
bsalomonc8dc1f72014-08-21 13:02:13 -0700170 SkASSERT(!fCount);
bsalomondace19e2014-11-17 07:34:06 -0800171 SkASSERT(!fBytes);
172 SkASSERT(!fBudgetedCount);
173 SkASSERT(!fBudgetedBytes);
bsalomonc8dc1f72014-08-21 13:02:13 -0700174}
bsalomonbcf0a522014-10-08 08:40:09 -0700175
bsalomon0ea80f42015-02-11 10:49:59 -0800176class GrResourceCache::AvailableForScratchUse {
bsalomonbcf0a522014-10-08 08:40:09 -0700177public:
bsalomon000f8292014-10-15 19:04:14 -0700178 AvailableForScratchUse(bool rejectPendingIO) : fRejectPendingIO(rejectPendingIO) { }
bsalomonbcf0a522014-10-08 08:40:09 -0700179
180 bool operator()(const GrGpuResource* resource) const {
bsalomon12299ab2014-11-14 13:33:09 -0800181 if (resource->internalHasRef() || !resource->cacheAccess().isScratch()) {
bsalomon000f8292014-10-15 19:04:14 -0700182 return false;
bsalomonbcf0a522014-10-08 08:40:09 -0700183 }
bsalomon000f8292014-10-15 19:04:14 -0700184 return !fRejectPendingIO || !resource->internalHasPendingIO();
bsalomonbcf0a522014-10-08 08:40:09 -0700185 }
bsalomon1e2530b2014-10-09 09:57:18 -0700186
bsalomonbcf0a522014-10-08 08:40:09 -0700187private:
bsalomon000f8292014-10-15 19:04:14 -0700188 bool fRejectPendingIO;
bsalomonbcf0a522014-10-08 08:40:09 -0700189};
190
bsalomon0ea80f42015-02-11 10:49:59 -0800191GrGpuResource* GrResourceCache::findAndRefScratchResource(const GrScratchKey& scratchKey,
bsalomon000f8292014-10-15 19:04:14 -0700192 uint32_t flags) {
bsalomon71cb0c22014-11-14 12:10:14 -0800193 SkASSERT(!fPurging);
bsalomon7775c852014-12-30 12:50:52 -0800194 SkASSERT(scratchKey.isValid());
bsalomon000f8292014-10-15 19:04:14 -0700195
bsalomon71cb0c22014-11-14 12:10:14 -0800196 GrGpuResource* resource;
bsalomon000f8292014-10-15 19:04:14 -0700197 if (flags & (kPreferNoPendingIO_ScratchFlag | kRequireNoPendingIO_ScratchFlag)) {
bsalomon71cb0c22014-11-14 12:10:14 -0800198 resource = fScratchMap.find(scratchKey, AvailableForScratchUse(true));
bsalomon000f8292014-10-15 19:04:14 -0700199 if (resource) {
bsalomonb436ed62014-11-17 12:15:56 -0800200 resource->ref();
bsalomon71cb0c22014-11-14 12:10:14 -0800201 this->makeResourceMRU(resource);
bsalomonb436ed62014-11-17 12:15:56 -0800202 this->validate();
203 return resource;
bsalomon000f8292014-10-15 19:04:14 -0700204 } else if (flags & kRequireNoPendingIO_ScratchFlag) {
205 return NULL;
206 }
207 // TODO: fail here when kPrefer is specified, we didn't find a resource without pending io,
208 // but there is still space in our budget for the resource.
209 }
bsalomon71cb0c22014-11-14 12:10:14 -0800210 resource = fScratchMap.find(scratchKey, AvailableForScratchUse(false));
211 if (resource) {
212 resource->ref();
213 this->makeResourceMRU(resource);
bsalomonb436ed62014-11-17 12:15:56 -0800214 this->validate();
bsalomon71cb0c22014-11-14 12:10:14 -0800215 }
216 return resource;
bsalomonbcf0a522014-10-08 08:40:09 -0700217}
bsalomon8b79d232014-11-10 10:19:06 -0800218
bsalomon0ea80f42015-02-11 10:49:59 -0800219void GrResourceCache::willRemoveScratchKey(const GrGpuResource* resource) {
bsalomone167f962015-01-27 09:56:04 -0800220 SkASSERT(resource->cacheAccess().getScratchKey().isValid());
bsalomon10e23ca2014-11-25 05:52:06 -0800221 fScratchMap.remove(resource->cacheAccess().getScratchKey(), resource);
222}
223
bsalomon0ea80f42015-02-11 10:49:59 -0800224void GrResourceCache::willRemoveContentKey(const GrGpuResource* resource) {
bsalomon23e619c2015-02-06 11:54:28 -0800225 // Someone has a ref to this resource in order to invalidate it. When the ref count reaches
226 // zero we will get a notifyPurgable() and figure out what to do with it.
227 SkASSERT(resource->getContentKey().isValid());
228 fContentHash.remove(resource->getContentKey());
229}
230
bsalomon0ea80f42015-02-11 10:49:59 -0800231bool GrResourceCache::didSetContentKey(GrGpuResource* resource) {
bsalomon71cb0c22014-11-14 12:10:14 -0800232 SkASSERT(!fPurging);
bsalomon8b79d232014-11-10 10:19:06 -0800233 SkASSERT(resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800234 SkASSERT(this->isInCache(resource));
bsalomon563ff602015-02-02 17:25:26 -0800235 SkASSERT(resource->getContentKey().isValid());
bsalomon8b79d232014-11-10 10:19:06 -0800236
bsalomon563ff602015-02-02 17:25:26 -0800237 GrGpuResource* res = fContentHash.find(resource->getContentKey());
bsalomon8b79d232014-11-10 10:19:06 -0800238 if (NULL != res) {
239 return false;
240 }
241
242 fContentHash.add(resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800243 this->validate();
bsalomon8b79d232014-11-10 10:19:06 -0800244 return true;
245}
bsalomon71cb0c22014-11-14 12:10:14 -0800246
bsalomon0ea80f42015-02-11 10:49:59 -0800247void GrResourceCache::makeResourceMRU(GrGpuResource* resource) {
bsalomon71cb0c22014-11-14 12:10:14 -0800248 SkASSERT(!fPurging);
249 SkASSERT(resource);
250 SkASSERT(this->isInCache(resource));
251 fResources.remove(resource);
bsalomonb436ed62014-11-17 12:15:56 -0800252 fResources.addToHead(resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800253}
254
bsalomon0ea80f42015-02-11 10:49:59 -0800255void GrResourceCache::notifyPurgeable(GrGpuResource* resource) {
bsalomon71cb0c22014-11-14 12:10:14 -0800256 SkASSERT(resource);
257 SkASSERT(this->isInCache(resource));
bsalomon63c992f2015-01-23 12:47:59 -0800258 SkASSERT(resource->isPurgeable());
bsalomon71cb0c22014-11-14 12:10:14 -0800259
260 // We can't purge if in the middle of purging because purge is iterating. Instead record
bsalomon63c992f2015-01-23 12:47:59 -0800261 // that additional resources became purgeable.
bsalomon71cb0c22014-11-14 12:10:14 -0800262 if (fPurging) {
bsalomon63c992f2015-01-23 12:47:59 -0800263 fNewlyPurgeableResourceWhilePurging = true;
bsalomon71cb0c22014-11-14 12:10:14 -0800264 return;
265 }
266
bsalomonc2f35b72015-01-23 07:19:22 -0800267 bool release = false;
bsalomon71cb0c22014-11-14 12:10:14 -0800268
bsalomonc2f35b72015-01-23 07:19:22 -0800269 if (resource->cacheAccess().isWrapped()) {
270 release = true;
271 } else if (!resource->cacheAccess().isBudgeted()) {
272 // Check whether this resource could still be used as a scratch resource.
273 if (resource->cacheAccess().getScratchKey().isValid()) {
274 // We won't purge an existing resource to make room for this one.
275 bool underBudget = fBudgetedCount < fMaxCount &&
276 fBudgetedBytes + resource->gpuMemorySize() <= fMaxBytes;
277 if (underBudget) {
278 resource->cacheAccess().makeBudgeted();
279 } else {
280 release = true;
281 }
282 } else {
283 release = true;
284 }
285 } else {
286 // Purge the resource if we're over budget
287 bool overBudget = fBudgetedCount > fMaxCount || fBudgetedBytes > fMaxBytes;
bsalomon71cb0c22014-11-14 12:10:14 -0800288
bsalomonc2f35b72015-01-23 07:19:22 -0800289 // Also purge if the resource has neither a valid scratch key nor a content key.
290 bool noKey = !resource->cacheAccess().getScratchKey().isValid() &&
bsalomon563ff602015-02-02 17:25:26 -0800291 !resource->getContentKey().isValid();
bsalomonc2f35b72015-01-23 07:19:22 -0800292 if (overBudget || noKey) {
293 release = true;
294 }
295 }
bsalomondace19e2014-11-17 07:34:06 -0800296
bsalomonc2f35b72015-01-23 07:19:22 -0800297 if (release) {
bsalomon71cb0c22014-11-14 12:10:14 -0800298 SkDEBUGCODE(int beforeCount = fCount;)
bsalomon12299ab2014-11-14 13:33:09 -0800299 resource->cacheAccess().release();
300 // We should at least free this resource, perhaps dependent resources as well.
bsalomon71cb0c22014-11-14 12:10:14 -0800301 SkASSERT(fCount < beforeCount);
302 }
bsalomon71cb0c22014-11-14 12:10:14 -0800303 this->validate();
304}
305
bsalomon0ea80f42015-02-11 10:49:59 -0800306void GrResourceCache::didChangeGpuMemorySize(const GrGpuResource* resource, size_t oldSize) {
bsalomon71cb0c22014-11-14 12:10:14 -0800307 // SkASSERT(!fPurging); GrPathRange increases size during flush. :(
308 SkASSERT(resource);
309 SkASSERT(this->isInCache(resource));
310
bsalomondace19e2014-11-17 07:34:06 -0800311 ptrdiff_t delta = resource->gpuMemorySize() - oldSize;
312
313 fBytes += delta;
bsalomon82b1d622014-11-14 13:59:57 -0800314#if GR_CACHE_STATS
315 fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes);
316#endif
bsalomon84c8e622014-11-17 09:33:27 -0800317 if (resource->cacheAccess().isBudgeted()) {
bsalomondace19e2014-11-17 07:34:06 -0800318 fBudgetedBytes += delta;
319#if GR_CACHE_STATS
320 fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
321#endif
322 }
bsalomon71cb0c22014-11-14 12:10:14 -0800323
324 this->purgeAsNeeded();
325 this->validate();
326}
327
bsalomon0ea80f42015-02-11 10:49:59 -0800328void GrResourceCache::didChangeBudgetStatus(GrGpuResource* resource) {
bsalomon84c8e622014-11-17 09:33:27 -0800329 SkASSERT(!fPurging);
330 SkASSERT(resource);
331 SkASSERT(this->isInCache(resource));
332
333 size_t size = resource->gpuMemorySize();
334
335 if (resource->cacheAccess().isBudgeted()) {
336 ++fBudgetedCount;
337 fBudgetedBytes += size;
bsalomonafe30052015-01-16 07:32:33 -0800338#if GR_CACHE_STATS
339 fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
340 fBudgetedHighWaterCount = SkTMax(fBudgetedCount, fBudgetedHighWaterCount);
341#endif
bsalomon84c8e622014-11-17 09:33:27 -0800342 this->purgeAsNeeded();
343 } else {
344 --fBudgetedCount;
345 fBudgetedBytes -= size;
346 }
347
348 this->validate();
349}
350
bsalomon0ea80f42015-02-11 10:49:59 -0800351void GrResourceCache::internalPurgeAsNeeded() {
bsalomon71cb0c22014-11-14 12:10:14 -0800352 SkASSERT(!fPurging);
bsalomon63c992f2015-01-23 12:47:59 -0800353 SkASSERT(!fNewlyPurgeableResourceWhilePurging);
bsalomondace19e2014-11-17 07:34:06 -0800354 SkASSERT(fBudgetedCount > fMaxCount || fBudgetedBytes > fMaxBytes);
bsalomon71cb0c22014-11-14 12:10:14 -0800355
356 fPurging = true;
357
bsalomon71cb0c22014-11-14 12:10:14 -0800358 bool overBudget = true;
359 do {
bsalomon63c992f2015-01-23 12:47:59 -0800360 fNewlyPurgeableResourceWhilePurging = false;
bsalomon71cb0c22014-11-14 12:10:14 -0800361 ResourceList::Iter resourceIter;
362 GrGpuResource* resource = resourceIter.init(fResources,
363 ResourceList::Iter::kTail_IterStart);
364
365 while (resource) {
366 GrGpuResource* prev = resourceIter.prev();
bsalomon63c992f2015-01-23 12:47:59 -0800367 if (resource->isPurgeable()) {
bsalomon12299ab2014-11-14 13:33:09 -0800368 resource->cacheAccess().release();
bsalomon71cb0c22014-11-14 12:10:14 -0800369 }
370 resource = prev;
bsalomondace19e2014-11-17 07:34:06 -0800371 if (fBudgetedCount <= fMaxCount && fBudgetedBytes <= fMaxBytes) {
bsalomon71cb0c22014-11-14 12:10:14 -0800372 overBudget = false;
373 resource = NULL;
374 }
375 }
376
bsalomon63c992f2015-01-23 12:47:59 -0800377 if (!fNewlyPurgeableResourceWhilePurging && overBudget && fOverBudgetCB) {
bsalomon71cb0c22014-11-14 12:10:14 -0800378 // Despite the purge we're still over budget. Call our over budget callback.
379 (*fOverBudgetCB)(fOverBudgetData);
380 }
bsalomon63c992f2015-01-23 12:47:59 -0800381 } while (overBudget && fNewlyPurgeableResourceWhilePurging);
bsalomon71cb0c22014-11-14 12:10:14 -0800382
bsalomon63c992f2015-01-23 12:47:59 -0800383 fNewlyPurgeableResourceWhilePurging = false;
bsalomon71cb0c22014-11-14 12:10:14 -0800384 fPurging = false;
bsalomonb436ed62014-11-17 12:15:56 -0800385 this->validate();
bsalomon71cb0c22014-11-14 12:10:14 -0800386}
387
bsalomon0ea80f42015-02-11 10:49:59 -0800388void GrResourceCache::purgeAllUnlocked() {
bsalomon71cb0c22014-11-14 12:10:14 -0800389 SkASSERT(!fPurging);
bsalomon63c992f2015-01-23 12:47:59 -0800390 SkASSERT(!fNewlyPurgeableResourceWhilePurging);
bsalomon71cb0c22014-11-14 12:10:14 -0800391
392 fPurging = true;
393
bsalomon71cb0c22014-11-14 12:10:14 -0800394 do {
bsalomon63c992f2015-01-23 12:47:59 -0800395 fNewlyPurgeableResourceWhilePurging = false;
bsalomon71cb0c22014-11-14 12:10:14 -0800396 ResourceList::Iter resourceIter;
397 GrGpuResource* resource =
398 resourceIter.init(fResources, ResourceList::Iter::kTail_IterStart);
399
400 while (resource) {
401 GrGpuResource* prev = resourceIter.prev();
bsalomon63c992f2015-01-23 12:47:59 -0800402 if (resource->isPurgeable()) {
bsalomon12299ab2014-11-14 13:33:09 -0800403 resource->cacheAccess().release();
404 }
bsalomon71cb0c22014-11-14 12:10:14 -0800405 resource = prev;
406 }
407
bsalomon63c992f2015-01-23 12:47:59 -0800408 if (!fNewlyPurgeableResourceWhilePurging && fCount && fOverBudgetCB) {
bsalomon71cb0c22014-11-14 12:10:14 -0800409 (*fOverBudgetCB)(fOverBudgetData);
410 }
bsalomon63c992f2015-01-23 12:47:59 -0800411 } while (fNewlyPurgeableResourceWhilePurging);
bsalomon71cb0c22014-11-14 12:10:14 -0800412 fPurging = false;
bsalomonb436ed62014-11-17 12:15:56 -0800413 this->validate();
bsalomon71cb0c22014-11-14 12:10:14 -0800414}
415
bsalomon0ea80f42015-02-11 10:49:59 -0800416void GrResourceCache::processInvalidContentKeys(
bsalomon23e619c2015-02-06 11:54:28 -0800417 const SkTArray<GrContentKeyInvalidatedMessage>& msgs) {
418 for (int i = 0; i < msgs.count(); ++i) {
419 GrGpuResource* resource = this->findAndRefContentResource(msgs[i].key());
420 if (resource) {
421 resource->cacheAccess().removeContentKey();
422 resource->unref(); // will call notifyPurgeable, if it is indeed now purgeable.
423 }
424 }
425}
426
bsalomon71cb0c22014-11-14 12:10:14 -0800427#ifdef SK_DEBUG
bsalomon0ea80f42015-02-11 10:49:59 -0800428void GrResourceCache::validate() const {
bsalomonc2f35b72015-01-23 07:19:22 -0800429 // Reduce the frequency of validations for large resource counts.
430 static SkRandom gRandom;
431 int mask = (SkNextPow2(fCount + 1) >> 5) - 1;
432 if (~mask && (gRandom.nextU() & mask)) {
433 return;
434 }
435
bsalomon71cb0c22014-11-14 12:10:14 -0800436 size_t bytes = 0;
437 int count = 0;
bsalomondace19e2014-11-17 07:34:06 -0800438 int budgetedCount = 0;
439 size_t budgetedBytes = 0;
bsalomon71cb0c22014-11-14 12:10:14 -0800440 int locked = 0;
441 int scratch = 0;
442 int couldBeScratch = 0;
443 int content = 0;
444
445 ResourceList::Iter iter;
446 GrGpuResource* resource = iter.init(fResources, ResourceList::Iter::kHead_IterStart);
447 for ( ; resource; resource = iter.next()) {
448 bytes += resource->gpuMemorySize();
449 ++count;
450
bsalomon63c992f2015-01-23 12:47:59 -0800451 if (!resource->isPurgeable()) {
bsalomon71cb0c22014-11-14 12:10:14 -0800452 ++locked;
453 }
454
455 if (resource->cacheAccess().isScratch()) {
bsalomon563ff602015-02-02 17:25:26 -0800456 SkASSERT(!resource->getContentKey().isValid());
bsalomon71cb0c22014-11-14 12:10:14 -0800457 ++scratch;
458 SkASSERT(fScratchMap.countForKey(resource->cacheAccess().getScratchKey()));
bsalomondace19e2014-11-17 07:34:06 -0800459 SkASSERT(!resource->cacheAccess().isWrapped());
bsalomon7775c852014-12-30 12:50:52 -0800460 } else if (resource->cacheAccess().getScratchKey().isValid()) {
bsalomonc2f35b72015-01-23 07:19:22 -0800461 SkASSERT(!resource->cacheAccess().isBudgeted() ||
bsalomon563ff602015-02-02 17:25:26 -0800462 resource->getContentKey().isValid());
bsalomon71cb0c22014-11-14 12:10:14 -0800463 ++couldBeScratch;
464 SkASSERT(fScratchMap.countForKey(resource->cacheAccess().getScratchKey()));
bsalomondace19e2014-11-17 07:34:06 -0800465 SkASSERT(!resource->cacheAccess().isWrapped());
bsalomon71cb0c22014-11-14 12:10:14 -0800466 }
bsalomon563ff602015-02-02 17:25:26 -0800467 const GrContentKey& contentKey = resource->getContentKey();
bsalomon24db3b12015-01-23 04:24:04 -0800468 if (contentKey.isValid()) {
bsalomon71cb0c22014-11-14 12:10:14 -0800469 ++content;
bsalomon24db3b12015-01-23 04:24:04 -0800470 SkASSERT(fContentHash.find(contentKey) == resource);
bsalomondace19e2014-11-17 07:34:06 -0800471 SkASSERT(!resource->cacheAccess().isWrapped());
bsalomonc2f35b72015-01-23 07:19:22 -0800472 SkASSERT(resource->cacheAccess().isBudgeted());
bsalomondace19e2014-11-17 07:34:06 -0800473 }
474
bsalomon84c8e622014-11-17 09:33:27 -0800475 if (resource->cacheAccess().isBudgeted()) {
bsalomondace19e2014-11-17 07:34:06 -0800476 ++budgetedCount;
477 budgetedBytes += resource->gpuMemorySize();
bsalomon71cb0c22014-11-14 12:10:14 -0800478 }
479 }
480
bsalomondace19e2014-11-17 07:34:06 -0800481 SkASSERT(fBudgetedCount <= fCount);
482 SkASSERT(fBudgetedBytes <= fBudgetedBytes);
bsalomon71cb0c22014-11-14 12:10:14 -0800483 SkASSERT(bytes == fBytes);
484 SkASSERT(count == fCount);
bsalomondace19e2014-11-17 07:34:06 -0800485 SkASSERT(budgetedBytes == fBudgetedBytes);
486 SkASSERT(budgetedCount == fBudgetedCount);
bsalomon71cb0c22014-11-14 12:10:14 -0800487#if GR_CACHE_STATS
bsalomondace19e2014-11-17 07:34:06 -0800488 SkASSERT(fBudgetedHighWaterCount <= fHighWaterCount);
489 SkASSERT(fBudgetedHighWaterBytes <= fHighWaterBytes);
bsalomon71cb0c22014-11-14 12:10:14 -0800490 SkASSERT(bytes <= fHighWaterBytes);
491 SkASSERT(count <= fHighWaterCount);
bsalomondace19e2014-11-17 07:34:06 -0800492 SkASSERT(budgetedBytes <= fBudgetedHighWaterBytes);
493 SkASSERT(budgetedCount <= fBudgetedHighWaterCount);
bsalomon71cb0c22014-11-14 12:10:14 -0800494#endif
495 SkASSERT(content == fContentHash.count());
496 SkASSERT(scratch + couldBeScratch == fScratchMap.count());
497
bsalomon63c992f2015-01-23 12:47:59 -0800498 // This assertion is not currently valid because we can be in recursive notifyIsPurgeable()
bsalomon12299ab2014-11-14 13:33:09 -0800499 // calls. This will be fixed when subresource registration is explicit.
bsalomondace19e2014-11-17 07:34:06 -0800500 // bool overBudget = budgetedBytes > fMaxBytes || budgetedCount > fMaxCount;
bsalomon12299ab2014-11-14 13:33:09 -0800501 // SkASSERT(!overBudget || locked == count || fPurging);
bsalomon71cb0c22014-11-14 12:10:14 -0800502}
503#endif