blob: 65894d33141213c614491072acac28eb215a56e6 [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
10#include "GrResourceCache2.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
17DECLARE_SKMESSAGEBUS_MESSAGE(GrResourceInvalidatedMessage);
18
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
bsalomon71cb0c22014-11-14 12:10:14 -080048class GrResourceCache2::AutoValidate : ::SkNoncopyable {
49public:
50 AutoValidate(GrResourceCache2* cache) : fCache(cache) { cache->validate(); }
51 ~AutoValidate() { fCache->validate(); }
52private:
53 GrResourceCache2* fCache;
54};
55
56 //////////////////////////////////////////////////////////////////////////////
57
58static const int kDefaultMaxCount = 2 * (1 << 10);
59static const size_t kDefaultMaxSize = 96 * (1 << 20);
60
61GrResourceCache2::GrResourceCache2()
62 : 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
bsalomonc8dc1f72014-08-21 13:02:13 -070080GrResourceCache2::~GrResourceCache2() {
81 this->releaseAll();
82}
83
bsalomon71cb0c22014-11-14 12:10:14 -080084void GrResourceCache2::setLimits(int count, size_t bytes) {
85 fMaxCount = count;
86 fMaxBytes = bytes;
87 this->purgeAsNeeded();
88}
89
bsalomonc8dc1f72014-08-21 13:02:13 -070090void GrResourceCache2::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
120void GrResourceCache2::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 }
stephana9b440262015-02-02 14:52:11 -0800135 if (resource->cacheAccess().getContentKey().isValid()) {
136 fContentHash.remove(resource->cacheAccess().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
141void GrResourceCache2::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
159void GrResourceCache2::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
176class GrResourceCache2::AvailableForScratchUse {
177public:
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
bsalomon7775c852014-12-30 12:50:52 -0800191GrGpuResource* GrResourceCache2::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
bsalomon10e23ca2014-11-25 05:52:06 -0800219void GrResourceCache2::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
bsalomon6d4488c2014-11-11 07:27:16 -0800224bool GrResourceCache2::didSetContentKey(GrGpuResource* resource) {
bsalomon71cb0c22014-11-14 12:10:14 -0800225 SkASSERT(!fPurging);
bsalomon8b79d232014-11-10 10:19:06 -0800226 SkASSERT(resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800227 SkASSERT(this->isInCache(resource));
stephana9b440262015-02-02 14:52:11 -0800228 SkASSERT(resource->cacheAccess().getContentKey().isValid());
bsalomon8b79d232014-11-10 10:19:06 -0800229
stephana9b440262015-02-02 14:52:11 -0800230 GrGpuResource* res = fContentHash.find(resource->cacheAccess().getContentKey());
bsalomon8b79d232014-11-10 10:19:06 -0800231 if (NULL != res) {
232 return false;
233 }
234
235 fContentHash.add(resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800236 this->validate();
bsalomon8b79d232014-11-10 10:19:06 -0800237 return true;
238}
bsalomon71cb0c22014-11-14 12:10:14 -0800239
240void GrResourceCache2::makeResourceMRU(GrGpuResource* resource) {
bsalomon71cb0c22014-11-14 12:10:14 -0800241 SkASSERT(!fPurging);
242 SkASSERT(resource);
243 SkASSERT(this->isInCache(resource));
244 fResources.remove(resource);
bsalomonb436ed62014-11-17 12:15:56 -0800245 fResources.addToHead(resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800246}
247
bsalomon63c992f2015-01-23 12:47:59 -0800248void GrResourceCache2::notifyPurgeable(GrGpuResource* resource) {
bsalomon71cb0c22014-11-14 12:10:14 -0800249 SkASSERT(resource);
250 SkASSERT(this->isInCache(resource));
bsalomon63c992f2015-01-23 12:47:59 -0800251 SkASSERT(resource->isPurgeable());
bsalomon71cb0c22014-11-14 12:10:14 -0800252
253 // We can't purge if in the middle of purging because purge is iterating. Instead record
bsalomon63c992f2015-01-23 12:47:59 -0800254 // that additional resources became purgeable.
bsalomon71cb0c22014-11-14 12:10:14 -0800255 if (fPurging) {
bsalomon63c992f2015-01-23 12:47:59 -0800256 fNewlyPurgeableResourceWhilePurging = true;
bsalomon71cb0c22014-11-14 12:10:14 -0800257 return;
258 }
259
bsalomonc2f35b72015-01-23 07:19:22 -0800260 bool release = false;
bsalomon71cb0c22014-11-14 12:10:14 -0800261
bsalomonc2f35b72015-01-23 07:19:22 -0800262 if (resource->cacheAccess().isWrapped()) {
263 release = true;
264 } else if (!resource->cacheAccess().isBudgeted()) {
265 // Check whether this resource could still be used as a scratch resource.
266 if (resource->cacheAccess().getScratchKey().isValid()) {
267 // We won't purge an existing resource to make room for this one.
268 bool underBudget = fBudgetedCount < fMaxCount &&
269 fBudgetedBytes + resource->gpuMemorySize() <= fMaxBytes;
270 if (underBudget) {
271 resource->cacheAccess().makeBudgeted();
272 } else {
273 release = true;
274 }
275 } else {
276 release = true;
277 }
278 } else {
279 // Purge the resource if we're over budget
280 bool overBudget = fBudgetedCount > fMaxCount || fBudgetedBytes > fMaxBytes;
bsalomon71cb0c22014-11-14 12:10:14 -0800281
bsalomonc2f35b72015-01-23 07:19:22 -0800282 // Also purge if the resource has neither a valid scratch key nor a content key.
283 bool noKey = !resource->cacheAccess().getScratchKey().isValid() &&
stephana9b440262015-02-02 14:52:11 -0800284 !resource->cacheAccess().getContentKey().isValid();
bsalomonc2f35b72015-01-23 07:19:22 -0800285 if (overBudget || noKey) {
286 release = true;
287 }
288 }
bsalomondace19e2014-11-17 07:34:06 -0800289
bsalomonc2f35b72015-01-23 07:19:22 -0800290 if (release) {
bsalomon71cb0c22014-11-14 12:10:14 -0800291 SkDEBUGCODE(int beforeCount = fCount;)
bsalomon12299ab2014-11-14 13:33:09 -0800292 resource->cacheAccess().release();
293 // We should at least free this resource, perhaps dependent resources as well.
bsalomon71cb0c22014-11-14 12:10:14 -0800294 SkASSERT(fCount < beforeCount);
295 }
bsalomon71cb0c22014-11-14 12:10:14 -0800296 this->validate();
297}
298
299void GrResourceCache2::didChangeGpuMemorySize(const GrGpuResource* resource, size_t oldSize) {
300 // SkASSERT(!fPurging); GrPathRange increases size during flush. :(
301 SkASSERT(resource);
302 SkASSERT(this->isInCache(resource));
303
bsalomondace19e2014-11-17 07:34:06 -0800304 ptrdiff_t delta = resource->gpuMemorySize() - oldSize;
305
306 fBytes += delta;
bsalomon82b1d622014-11-14 13:59:57 -0800307#if GR_CACHE_STATS
308 fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes);
309#endif
bsalomon84c8e622014-11-17 09:33:27 -0800310 if (resource->cacheAccess().isBudgeted()) {
bsalomondace19e2014-11-17 07:34:06 -0800311 fBudgetedBytes += delta;
312#if GR_CACHE_STATS
313 fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
314#endif
315 }
bsalomon71cb0c22014-11-14 12:10:14 -0800316
317 this->purgeAsNeeded();
318 this->validate();
319}
320
bsalomon84c8e622014-11-17 09:33:27 -0800321void GrResourceCache2::didChangeBudgetStatus(GrGpuResource* resource) {
322 SkASSERT(!fPurging);
323 SkASSERT(resource);
324 SkASSERT(this->isInCache(resource));
325
326 size_t size = resource->gpuMemorySize();
327
328 if (resource->cacheAccess().isBudgeted()) {
329 ++fBudgetedCount;
330 fBudgetedBytes += size;
bsalomonafe30052015-01-16 07:32:33 -0800331#if GR_CACHE_STATS
332 fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
333 fBudgetedHighWaterCount = SkTMax(fBudgetedCount, fBudgetedHighWaterCount);
334#endif
bsalomon84c8e622014-11-17 09:33:27 -0800335 this->purgeAsNeeded();
336 } else {
337 --fBudgetedCount;
338 fBudgetedBytes -= size;
339 }
340
341 this->validate();
342}
343
bsalomon71cb0c22014-11-14 12:10:14 -0800344void GrResourceCache2::internalPurgeAsNeeded() {
345 SkASSERT(!fPurging);
bsalomon63c992f2015-01-23 12:47:59 -0800346 SkASSERT(!fNewlyPurgeableResourceWhilePurging);
bsalomondace19e2014-11-17 07:34:06 -0800347 SkASSERT(fBudgetedCount > fMaxCount || fBudgetedBytes > fMaxBytes);
bsalomon71cb0c22014-11-14 12:10:14 -0800348
349 fPurging = true;
350
bsalomon71cb0c22014-11-14 12:10:14 -0800351 bool overBudget = true;
352 do {
bsalomon63c992f2015-01-23 12:47:59 -0800353 fNewlyPurgeableResourceWhilePurging = false;
bsalomon71cb0c22014-11-14 12:10:14 -0800354 ResourceList::Iter resourceIter;
355 GrGpuResource* resource = resourceIter.init(fResources,
356 ResourceList::Iter::kTail_IterStart);
357
358 while (resource) {
359 GrGpuResource* prev = resourceIter.prev();
bsalomon63c992f2015-01-23 12:47:59 -0800360 if (resource->isPurgeable()) {
bsalomon12299ab2014-11-14 13:33:09 -0800361 resource->cacheAccess().release();
bsalomon71cb0c22014-11-14 12:10:14 -0800362 }
363 resource = prev;
bsalomondace19e2014-11-17 07:34:06 -0800364 if (fBudgetedCount <= fMaxCount && fBudgetedBytes <= fMaxBytes) {
bsalomon71cb0c22014-11-14 12:10:14 -0800365 overBudget = false;
366 resource = NULL;
367 }
368 }
369
bsalomon63c992f2015-01-23 12:47:59 -0800370 if (!fNewlyPurgeableResourceWhilePurging && overBudget && fOverBudgetCB) {
bsalomon71cb0c22014-11-14 12:10:14 -0800371 // Despite the purge we're still over budget. Call our over budget callback.
372 (*fOverBudgetCB)(fOverBudgetData);
373 }
bsalomon63c992f2015-01-23 12:47:59 -0800374 } while (overBudget && fNewlyPurgeableResourceWhilePurging);
bsalomon71cb0c22014-11-14 12:10:14 -0800375
bsalomon63c992f2015-01-23 12:47:59 -0800376 fNewlyPurgeableResourceWhilePurging = false;
bsalomon71cb0c22014-11-14 12:10:14 -0800377 fPurging = false;
bsalomonb436ed62014-11-17 12:15:56 -0800378 this->validate();
bsalomon71cb0c22014-11-14 12:10:14 -0800379}
380
381void GrResourceCache2::purgeAllUnlocked() {
382 SkASSERT(!fPurging);
bsalomon63c992f2015-01-23 12:47:59 -0800383 SkASSERT(!fNewlyPurgeableResourceWhilePurging);
bsalomon71cb0c22014-11-14 12:10:14 -0800384
385 fPurging = true;
386
bsalomon71cb0c22014-11-14 12:10:14 -0800387 do {
bsalomon63c992f2015-01-23 12:47:59 -0800388 fNewlyPurgeableResourceWhilePurging = false;
bsalomon71cb0c22014-11-14 12:10:14 -0800389 ResourceList::Iter resourceIter;
390 GrGpuResource* resource =
391 resourceIter.init(fResources, ResourceList::Iter::kTail_IterStart);
392
393 while (resource) {
394 GrGpuResource* prev = resourceIter.prev();
bsalomon63c992f2015-01-23 12:47:59 -0800395 if (resource->isPurgeable()) {
bsalomon12299ab2014-11-14 13:33:09 -0800396 resource->cacheAccess().release();
397 }
bsalomon71cb0c22014-11-14 12:10:14 -0800398 resource = prev;
399 }
400
bsalomon63c992f2015-01-23 12:47:59 -0800401 if (!fNewlyPurgeableResourceWhilePurging && fCount && fOverBudgetCB) {
bsalomon71cb0c22014-11-14 12:10:14 -0800402 (*fOverBudgetCB)(fOverBudgetData);
403 }
bsalomon63c992f2015-01-23 12:47:59 -0800404 } while (fNewlyPurgeableResourceWhilePurging);
bsalomon71cb0c22014-11-14 12:10:14 -0800405 fPurging = false;
bsalomonb436ed62014-11-17 12:15:56 -0800406 this->validate();
bsalomon71cb0c22014-11-14 12:10:14 -0800407}
408
409#ifdef SK_DEBUG
410void GrResourceCache2::validate() const {
bsalomonc2f35b72015-01-23 07:19:22 -0800411 // Reduce the frequency of validations for large resource counts.
412 static SkRandom gRandom;
413 int mask = (SkNextPow2(fCount + 1) >> 5) - 1;
414 if (~mask && (gRandom.nextU() & mask)) {
415 return;
416 }
417
bsalomon71cb0c22014-11-14 12:10:14 -0800418 size_t bytes = 0;
419 int count = 0;
bsalomondace19e2014-11-17 07:34:06 -0800420 int budgetedCount = 0;
421 size_t budgetedBytes = 0;
bsalomon71cb0c22014-11-14 12:10:14 -0800422 int locked = 0;
423 int scratch = 0;
424 int couldBeScratch = 0;
425 int content = 0;
426
427 ResourceList::Iter iter;
428 GrGpuResource* resource = iter.init(fResources, ResourceList::Iter::kHead_IterStart);
429 for ( ; resource; resource = iter.next()) {
430 bytes += resource->gpuMemorySize();
431 ++count;
432
bsalomon63c992f2015-01-23 12:47:59 -0800433 if (!resource->isPurgeable()) {
bsalomon71cb0c22014-11-14 12:10:14 -0800434 ++locked;
435 }
436
437 if (resource->cacheAccess().isScratch()) {
stephana9b440262015-02-02 14:52:11 -0800438 SkASSERT(!resource->cacheAccess().getContentKey().isValid());
bsalomon71cb0c22014-11-14 12:10:14 -0800439 ++scratch;
440 SkASSERT(fScratchMap.countForKey(resource->cacheAccess().getScratchKey()));
bsalomondace19e2014-11-17 07:34:06 -0800441 SkASSERT(!resource->cacheAccess().isWrapped());
bsalomon7775c852014-12-30 12:50:52 -0800442 } else if (resource->cacheAccess().getScratchKey().isValid()) {
bsalomonc2f35b72015-01-23 07:19:22 -0800443 SkASSERT(!resource->cacheAccess().isBudgeted() ||
stephana9b440262015-02-02 14:52:11 -0800444 resource->cacheAccess().getContentKey().isValid());
bsalomon71cb0c22014-11-14 12:10:14 -0800445 ++couldBeScratch;
446 SkASSERT(fScratchMap.countForKey(resource->cacheAccess().getScratchKey()));
bsalomondace19e2014-11-17 07:34:06 -0800447 SkASSERT(!resource->cacheAccess().isWrapped());
bsalomon71cb0c22014-11-14 12:10:14 -0800448 }
stephana9b440262015-02-02 14:52:11 -0800449 const GrContentKey& contentKey = resource->cacheAccess().getContentKey();
bsalomon24db3b12015-01-23 04:24:04 -0800450 if (contentKey.isValid()) {
bsalomon71cb0c22014-11-14 12:10:14 -0800451 ++content;
bsalomon24db3b12015-01-23 04:24:04 -0800452 SkASSERT(fContentHash.find(contentKey) == resource);
bsalomondace19e2014-11-17 07:34:06 -0800453 SkASSERT(!resource->cacheAccess().isWrapped());
bsalomonc2f35b72015-01-23 07:19:22 -0800454 SkASSERT(resource->cacheAccess().isBudgeted());
bsalomondace19e2014-11-17 07:34:06 -0800455 }
456
bsalomon84c8e622014-11-17 09:33:27 -0800457 if (resource->cacheAccess().isBudgeted()) {
bsalomondace19e2014-11-17 07:34:06 -0800458 ++budgetedCount;
459 budgetedBytes += resource->gpuMemorySize();
bsalomon71cb0c22014-11-14 12:10:14 -0800460 }
461 }
462
bsalomondace19e2014-11-17 07:34:06 -0800463 SkASSERT(fBudgetedCount <= fCount);
464 SkASSERT(fBudgetedBytes <= fBudgetedBytes);
bsalomon71cb0c22014-11-14 12:10:14 -0800465 SkASSERT(bytes == fBytes);
466 SkASSERT(count == fCount);
bsalomondace19e2014-11-17 07:34:06 -0800467 SkASSERT(budgetedBytes == fBudgetedBytes);
468 SkASSERT(budgetedCount == fBudgetedCount);
bsalomon71cb0c22014-11-14 12:10:14 -0800469#if GR_CACHE_STATS
bsalomondace19e2014-11-17 07:34:06 -0800470 SkASSERT(fBudgetedHighWaterCount <= fHighWaterCount);
471 SkASSERT(fBudgetedHighWaterBytes <= fHighWaterBytes);
bsalomon71cb0c22014-11-14 12:10:14 -0800472 SkASSERT(bytes <= fHighWaterBytes);
473 SkASSERT(count <= fHighWaterCount);
bsalomondace19e2014-11-17 07:34:06 -0800474 SkASSERT(budgetedBytes <= fBudgetedHighWaterBytes);
475 SkASSERT(budgetedCount <= fBudgetedHighWaterCount);
bsalomon71cb0c22014-11-14 12:10:14 -0800476#endif
477 SkASSERT(content == fContentHash.count());
478 SkASSERT(scratch + couldBeScratch == fScratchMap.count());
479
bsalomon63c992f2015-01-23 12:47:59 -0800480 // This assertion is not currently valid because we can be in recursive notifyIsPurgeable()
bsalomon12299ab2014-11-14 13:33:09 -0800481 // calls. This will be fixed when subresource registration is explicit.
bsalomondace19e2014-11-17 07:34:06 -0800482 // bool overBudget = budgetedBytes > fMaxBytes || budgetedCount > fMaxCount;
bsalomon12299ab2014-11-14 13:33:09 -0800483 // SkASSERT(!overBudget || locked == count || fPurging);
bsalomon71cb0c22014-11-14 12:10:14 -0800484}
485#endif
486
487#if GR_CACHE_STATS
488void GrResourceCache2::printStats() const {
489 this->validate();
490
491 int locked = 0;
492 int scratch = 0;
bsalomon84c8e622014-11-17 09:33:27 -0800493 int wrapped = 0;
494 size_t unbudgetedSize = 0;
bsalomon71cb0c22014-11-14 12:10:14 -0800495
496 ResourceList::Iter iter;
497 GrGpuResource* resource = iter.init(fResources, ResourceList::Iter::kHead_IterStart);
498
499 for ( ; resource; resource = iter.next()) {
bsalomon63c992f2015-01-23 12:47:59 -0800500 if (!resource->isPurgeable()) {
bsalomon71cb0c22014-11-14 12:10:14 -0800501 ++locked;
502 }
503 if (resource->cacheAccess().isScratch()) {
504 ++scratch;
505 }
bsalomon84c8e622014-11-17 09:33:27 -0800506 if (resource->cacheAccess().isWrapped()) {
507 ++wrapped;
508 }
509 if (!resource->cacheAccess().isBudgeted()) {
510 unbudgetedSize += resource->gpuMemorySize();
511 }
bsalomon71cb0c22014-11-14 12:10:14 -0800512 }
513
bsalomondace19e2014-11-17 07:34:06 -0800514 float countUtilization = (100.f * fBudgetedCount) / fMaxCount;
515 float byteUtilization = (100.f * fBudgetedBytes) / fMaxBytes;
bsalomon71cb0c22014-11-14 12:10:14 -0800516
517 SkDebugf("Budget: %d items %d bytes\n", fMaxCount, fMaxBytes);
bsalomon84c8e622014-11-17 09:33:27 -0800518 SkDebugf("\t\tEntry Count: current %d"
519 " (%d budgeted, %d wrapped, %d locked, %d scratch %.2g%% full), high %d\n",
520 fCount, fBudgetedCount, wrapped, locked, scratch, countUtilization, fHighWaterCount);
521 SkDebugf("\t\tEntry Bytes: current %d (budgeted %d, %.2g%% full, %d unbudgeted) high %d\n",
522 fBytes, fBudgetedBytes, byteUtilization, unbudgetedSize, fHighWaterBytes);
bsalomon71cb0c22014-11-14 12:10:14 -0800523}
524
525#endif