blob: aed31b73d2af4ff3cacedd358d9497a3cd0fb7f6 [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"
bsalomonddf30e62015-02-19 11:38:44 -080015#include "SkTSort.h"
bsalomon71cb0c22014-11-14 12:10:14 -080016
bsalomon8718aaf2015-02-19 07:24:21 -080017DECLARE_SKMESSAGEBUS_MESSAGE(GrUniqueKeyInvalidatedMessage);
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
bsalomon8718aaf2015-02-19 07:24:21 -080032GrUniqueKey::Domain GrUniqueKey::GenerateDomain() {
bsalomon24db3b12015-01-23 04:24:04 -080033 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) {
bsalomon8718aaf2015-02-19 07:24:21 -080037 SkFAIL("Too many GrUniqueKey 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()
bsalomon9f2d1572015-02-17 11:47:40 -080062 : fTimestamp(0)
63 , fMaxCount(kDefaultMaxCount)
bsalomon71cb0c22014-11-14 12:10:14 -080064 , fMaxBytes(kDefaultMaxSize)
65#if GR_CACHE_STATS
66 , fHighWaterCount(0)
67 , fHighWaterBytes(0)
bsalomondace19e2014-11-17 07:34:06 -080068 , fBudgetedHighWaterCount(0)
69 , fBudgetedHighWaterBytes(0)
bsalomon71cb0c22014-11-14 12:10:14 -080070#endif
bsalomon71cb0c22014-11-14 12:10:14 -080071 , fBytes(0)
bsalomondace19e2014-11-17 07:34:06 -080072 , fBudgetedCount(0)
73 , fBudgetedBytes(0)
bsalomon71cb0c22014-11-14 12:10:14 -080074 , fOverBudgetCB(NULL)
75 , fOverBudgetData(NULL) {
bsalomonf320e042015-02-17 15:09:34 -080076 SkDEBUGCODE(fCount = 0;)
bsalomon71cb0c22014-11-14 12:10:14 -080077}
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);
bsalomon16961262014-08-26 14:01:07 -070091 SkASSERT(!this->isInCache(resource));
bsalomonf320e042015-02-17 15:09:34 -080092 SkASSERT(!resource->wasDestroyed());
93 SkASSERT(!resource->isPurgeable());
bsalomonddf30e62015-02-19 11:38:44 -080094
95 // We must set the timestamp before adding to the array in case the timestamp wraps and we wind
96 // up iterating over all the resources that already have timestamps.
97 resource->cacheAccess().setTimestamp(this->getNextTimestamp());
98
bsalomonf320e042015-02-17 15:09:34 -080099 this->addToNonpurgeableArray(resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800100
bsalomondace19e2014-11-17 07:34:06 -0800101 size_t size = resource->gpuMemorySize();
bsalomonf320e042015-02-17 15:09:34 -0800102 SkDEBUGCODE(++fCount;)
bsalomon84c8e622014-11-17 09:33:27 -0800103 fBytes += size;
bsalomon82b1d622014-11-14 13:59:57 -0800104#if GR_CACHE_STATS
bsalomonf320e042015-02-17 15:09:34 -0800105 fHighWaterCount = SkTMax(this->getResourceCount(), fHighWaterCount);
bsalomon82b1d622014-11-14 13:59:57 -0800106 fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes);
107#endif
bsalomon3582d3e2015-02-13 14:20:05 -0800108 if (resource->resourcePriv().isBudgeted()) {
bsalomondace19e2014-11-17 07:34:06 -0800109 ++fBudgetedCount;
110 fBudgetedBytes += size;
111#if GR_CACHE_STATS
112 fBudgetedHighWaterCount = SkTMax(fBudgetedCount, fBudgetedHighWaterCount);
113 fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
114#endif
115 }
bsalomon3582d3e2015-02-13 14:20:05 -0800116 if (resource->resourcePriv().getScratchKey().isValid()) {
bsalomon84c8e622014-11-17 09:33:27 -0800117 SkASSERT(!resource->cacheAccess().isWrapped());
bsalomon3582d3e2015-02-13 14:20:05 -0800118 fScratchMap.insert(resource->resourcePriv().getScratchKey(), resource);
bsalomon744998e2014-08-28 09:54:34 -0700119 }
bsalomon9f2d1572015-02-17 11:47:40 -0800120
bsalomon71cb0c22014-11-14 12:10:14 -0800121 this->purgeAsNeeded();
bsalomonc8dc1f72014-08-21 13:02:13 -0700122}
123
bsalomon0ea80f42015-02-11 10:49:59 -0800124void GrResourceCache::removeResource(GrGpuResource* resource) {
bsalomon9f2d1572015-02-17 11:47:40 -0800125 this->validate();
bsalomon16961262014-08-26 14:01:07 -0700126 SkASSERT(this->isInCache(resource));
bsalomondace19e2014-11-17 07:34:06 -0800127
bsalomon9f2d1572015-02-17 11:47:40 -0800128 if (resource->isPurgeable()) {
129 fPurgeableQueue.remove(resource);
bsalomonf320e042015-02-17 15:09:34 -0800130 } else {
131 this->removeFromNonpurgeableArray(resource);
bsalomon9f2d1572015-02-17 11:47:40 -0800132 }
133
bsalomondace19e2014-11-17 07:34:06 -0800134 size_t size = resource->gpuMemorySize();
bsalomonf320e042015-02-17 15:09:34 -0800135 SkDEBUGCODE(--fCount;)
bsalomondace19e2014-11-17 07:34:06 -0800136 fBytes -= size;
bsalomon3582d3e2015-02-13 14:20:05 -0800137 if (resource->resourcePriv().isBudgeted()) {
bsalomondace19e2014-11-17 07:34:06 -0800138 --fBudgetedCount;
139 fBudgetedBytes -= size;
140 }
141
bsalomon3582d3e2015-02-13 14:20:05 -0800142 if (resource->resourcePriv().getScratchKey().isValid()) {
143 fScratchMap.remove(resource->resourcePriv().getScratchKey(), resource);
bsalomon744998e2014-08-28 09:54:34 -0700144 }
bsalomon8718aaf2015-02-19 07:24:21 -0800145 if (resource->getUniqueKey().isValid()) {
146 fUniqueHash.remove(resource->getUniqueKey());
bsalomon8b79d232014-11-10 10:19:06 -0800147 }
bsalomonb436ed62014-11-17 12:15:56 -0800148 this->validate();
bsalomonc8dc1f72014-08-21 13:02:13 -0700149}
150
bsalomon0ea80f42015-02-11 10:49:59 -0800151void GrResourceCache::abandonAll() {
bsalomon71cb0c22014-11-14 12:10:14 -0800152 AutoValidate av(this);
153
bsalomonf320e042015-02-17 15:09:34 -0800154 while (fNonpurgeableResources.count()) {
155 GrGpuResource* back = *(fNonpurgeableResources.end() - 1);
156 SkASSERT(!back->wasDestroyed());
157 back->cacheAccess().abandon();
bsalomonc8dc1f72014-08-21 13:02:13 -0700158 }
bsalomonf320e042015-02-17 15:09:34 -0800159
160 while (fPurgeableQueue.count()) {
161 GrGpuResource* top = fPurgeableQueue.peek();
162 SkASSERT(!top->wasDestroyed());
163 top->cacheAccess().abandon();
164 }
165
bsalomon744998e2014-08-28 09:54:34 -0700166 SkASSERT(!fScratchMap.count());
bsalomon8718aaf2015-02-19 07:24:21 -0800167 SkASSERT(!fUniqueHash.count());
bsalomonc8dc1f72014-08-21 13:02:13 -0700168 SkASSERT(!fCount);
bsalomonf320e042015-02-17 15:09:34 -0800169 SkASSERT(!this->getResourceCount());
bsalomondace19e2014-11-17 07:34:06 -0800170 SkASSERT(!fBytes);
171 SkASSERT(!fBudgetedCount);
172 SkASSERT(!fBudgetedBytes);
bsalomonc8dc1f72014-08-21 13:02:13 -0700173}
174
bsalomon0ea80f42015-02-11 10:49:59 -0800175void GrResourceCache::releaseAll() {
bsalomon71cb0c22014-11-14 12:10:14 -0800176 AutoValidate av(this);
177
bsalomonf320e042015-02-17 15:09:34 -0800178 while(fNonpurgeableResources.count()) {
179 GrGpuResource* back = *(fNonpurgeableResources.end() - 1);
180 SkASSERT(!back->wasDestroyed());
181 back->cacheAccess().release();
bsalomonc8dc1f72014-08-21 13:02:13 -0700182 }
bsalomonf320e042015-02-17 15:09:34 -0800183
184 while (fPurgeableQueue.count()) {
185 GrGpuResource* top = fPurgeableQueue.peek();
186 SkASSERT(!top->wasDestroyed());
187 top->cacheAccess().release();
188 }
189
bsalomon744998e2014-08-28 09:54:34 -0700190 SkASSERT(!fScratchMap.count());
bsalomon8718aaf2015-02-19 07:24:21 -0800191 SkASSERT(!fUniqueHash.count());
bsalomonc8dc1f72014-08-21 13:02:13 -0700192 SkASSERT(!fCount);
bsalomonf320e042015-02-17 15:09:34 -0800193 SkASSERT(!this->getResourceCount());
bsalomondace19e2014-11-17 07:34:06 -0800194 SkASSERT(!fBytes);
195 SkASSERT(!fBudgetedCount);
196 SkASSERT(!fBudgetedBytes);
bsalomonc8dc1f72014-08-21 13:02:13 -0700197}
bsalomonbcf0a522014-10-08 08:40:09 -0700198
bsalomon0ea80f42015-02-11 10:49:59 -0800199class GrResourceCache::AvailableForScratchUse {
bsalomonbcf0a522014-10-08 08:40:09 -0700200public:
bsalomon000f8292014-10-15 19:04:14 -0700201 AvailableForScratchUse(bool rejectPendingIO) : fRejectPendingIO(rejectPendingIO) { }
bsalomonbcf0a522014-10-08 08:40:09 -0700202
203 bool operator()(const GrGpuResource* resource) const {
bsalomon12299ab2014-11-14 13:33:09 -0800204 if (resource->internalHasRef() || !resource->cacheAccess().isScratch()) {
bsalomon000f8292014-10-15 19:04:14 -0700205 return false;
bsalomonbcf0a522014-10-08 08:40:09 -0700206 }
bsalomon000f8292014-10-15 19:04:14 -0700207 return !fRejectPendingIO || !resource->internalHasPendingIO();
bsalomonbcf0a522014-10-08 08:40:09 -0700208 }
bsalomon1e2530b2014-10-09 09:57:18 -0700209
bsalomonbcf0a522014-10-08 08:40:09 -0700210private:
bsalomon000f8292014-10-15 19:04:14 -0700211 bool fRejectPendingIO;
bsalomonbcf0a522014-10-08 08:40:09 -0700212};
213
bsalomon0ea80f42015-02-11 10:49:59 -0800214GrGpuResource* GrResourceCache::findAndRefScratchResource(const GrScratchKey& scratchKey,
bsalomon9f2d1572015-02-17 11:47:40 -0800215 uint32_t flags) {
bsalomon7775c852014-12-30 12:50:52 -0800216 SkASSERT(scratchKey.isValid());
bsalomon000f8292014-10-15 19:04:14 -0700217
bsalomon71cb0c22014-11-14 12:10:14 -0800218 GrGpuResource* resource;
bsalomon000f8292014-10-15 19:04:14 -0700219 if (flags & (kPreferNoPendingIO_ScratchFlag | kRequireNoPendingIO_ScratchFlag)) {
bsalomon71cb0c22014-11-14 12:10:14 -0800220 resource = fScratchMap.find(scratchKey, AvailableForScratchUse(true));
bsalomon000f8292014-10-15 19:04:14 -0700221 if (resource) {
bsalomon9f2d1572015-02-17 11:47:40 -0800222 this->refAndMakeResourceMRU(resource);
bsalomonb436ed62014-11-17 12:15:56 -0800223 this->validate();
224 return resource;
bsalomon000f8292014-10-15 19:04:14 -0700225 } else if (flags & kRequireNoPendingIO_ScratchFlag) {
226 return NULL;
227 }
228 // TODO: fail here when kPrefer is specified, we didn't find a resource without pending io,
229 // but there is still space in our budget for the resource.
230 }
bsalomon71cb0c22014-11-14 12:10:14 -0800231 resource = fScratchMap.find(scratchKey, AvailableForScratchUse(false));
232 if (resource) {
bsalomon9f2d1572015-02-17 11:47:40 -0800233 this->refAndMakeResourceMRU(resource);
bsalomonb436ed62014-11-17 12:15:56 -0800234 this->validate();
bsalomon71cb0c22014-11-14 12:10:14 -0800235 }
236 return resource;
bsalomonbcf0a522014-10-08 08:40:09 -0700237}
bsalomon8b79d232014-11-10 10:19:06 -0800238
bsalomon0ea80f42015-02-11 10:49:59 -0800239void GrResourceCache::willRemoveScratchKey(const GrGpuResource* resource) {
bsalomon3582d3e2015-02-13 14:20:05 -0800240 SkASSERT(resource->resourcePriv().getScratchKey().isValid());
241 fScratchMap.remove(resource->resourcePriv().getScratchKey(), resource);
bsalomon10e23ca2014-11-25 05:52:06 -0800242}
243
bsalomonf99e9612015-02-19 08:24:16 -0800244void GrResourceCache::removeUniqueKey(GrGpuResource* resource) {
bsalomon23e619c2015-02-06 11:54:28 -0800245 // Someone has a ref to this resource in order to invalidate it. When the ref count reaches
246 // zero we will get a notifyPurgable() and figure out what to do with it.
bsalomonf99e9612015-02-19 08:24:16 -0800247 if (resource->getUniqueKey().isValid()) {
248 SkASSERT(resource == fUniqueHash.find(resource->getUniqueKey()));
249 fUniqueHash.remove(resource->getUniqueKey());
250 }
251 resource->cacheAccess().removeUniqueKey();
252 this->validate();
bsalomon23e619c2015-02-06 11:54:28 -0800253}
254
bsalomonf99e9612015-02-19 08:24:16 -0800255void GrResourceCache::changeUniqueKey(GrGpuResource* resource, const GrUniqueKey& newKey) {
bsalomon8b79d232014-11-10 10:19:06 -0800256 SkASSERT(resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800257 SkASSERT(this->isInCache(resource));
bsalomon8b79d232014-11-10 10:19:06 -0800258
bsalomonf99e9612015-02-19 08:24:16 -0800259 // Remove the entry for this resource if it already has a unique key.
260 if (resource->getUniqueKey().isValid()) {
261 SkASSERT(resource == fUniqueHash.find(resource->getUniqueKey()));
262 fUniqueHash.remove(resource->getUniqueKey());
263 SkASSERT(NULL == fUniqueHash.find(resource->getUniqueKey()));
bsalomon8b79d232014-11-10 10:19:06 -0800264 }
265
bsalomonf99e9612015-02-19 08:24:16 -0800266 // If another resource has the new key, remove its key then install the key on this resource.
267 if (newKey.isValid()) {
268 if (GrGpuResource* old = fUniqueHash.find(newKey)) {
269 // If the old resource using the key is purgeable and is unreachable, then remove it.
270 if (!old->resourcePriv().getScratchKey().isValid() && old->isPurgeable()) {
271 // release may call validate() which will assert that resource is in fUniqueHash
272 // if it has a valid key. So in debug reset the key here before we assign it.
273 SkDEBUGCODE(resource->cacheAccess().removeUniqueKey();)
274 old->cacheAccess().release();
275 } else {
276 fUniqueHash.remove(newKey);
277 old->cacheAccess().removeUniqueKey();
278 }
279 }
280 SkASSERT(NULL == fUniqueHash.find(newKey));
281 resource->cacheAccess().setUniqueKey(newKey);
282 fUniqueHash.add(resource);
283 } else {
284 resource->cacheAccess().removeUniqueKey();
285 }
286
bsalomon71cb0c22014-11-14 12:10:14 -0800287 this->validate();
bsalomon8b79d232014-11-10 10:19:06 -0800288}
bsalomon71cb0c22014-11-14 12:10:14 -0800289
bsalomon9f2d1572015-02-17 11:47:40 -0800290void GrResourceCache::refAndMakeResourceMRU(GrGpuResource* resource) {
bsalomon71cb0c22014-11-14 12:10:14 -0800291 SkASSERT(resource);
292 SkASSERT(this->isInCache(resource));
bsalomonddf30e62015-02-19 11:38:44 -0800293
bsalomon9f2d1572015-02-17 11:47:40 -0800294 if (resource->isPurgeable()) {
295 // It's about to become unpurgeable.
296 fPurgeableQueue.remove(resource);
bsalomonf320e042015-02-17 15:09:34 -0800297 this->addToNonpurgeableArray(resource);
bsalomon9f2d1572015-02-17 11:47:40 -0800298 }
299 resource->ref();
bsalomonddf30e62015-02-19 11:38:44 -0800300
301 resource->cacheAccess().setTimestamp(this->getNextTimestamp());
bsalomonf320e042015-02-17 15:09:34 -0800302 this->validate();
bsalomon71cb0c22014-11-14 12:10:14 -0800303}
304
bsalomon0ea80f42015-02-11 10:49:59 -0800305void GrResourceCache::notifyPurgeable(GrGpuResource* resource) {
bsalomon71cb0c22014-11-14 12:10:14 -0800306 SkASSERT(resource);
307 SkASSERT(this->isInCache(resource));
bsalomon63c992f2015-01-23 12:47:59 -0800308 SkASSERT(resource->isPurgeable());
bsalomon71cb0c22014-11-14 12:10:14 -0800309
bsalomonf320e042015-02-17 15:09:34 -0800310 this->removeFromNonpurgeableArray(resource);
bsalomon9f2d1572015-02-17 11:47:40 -0800311 fPurgeableQueue.insert(resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800312
bsalomon9f2d1572015-02-17 11:47:40 -0800313 if (!resource->resourcePriv().isBudgeted()) {
bsalomonc2f35b72015-01-23 07:19:22 -0800314 // Check whether this resource could still be used as a scratch resource.
bsalomon9f2d1572015-02-17 11:47:40 -0800315 if (!resource->cacheAccess().isWrapped() &&
316 resource->resourcePriv().getScratchKey().isValid()) {
bsalomonc2f35b72015-01-23 07:19:22 -0800317 // We won't purge an existing resource to make room for this one.
bsalomonf320e042015-02-17 15:09:34 -0800318 if (fBudgetedCount < fMaxCount &&
319 fBudgetedBytes + resource->gpuMemorySize() <= fMaxBytes) {
bsalomon3582d3e2015-02-13 14:20:05 -0800320 resource->resourcePriv().makeBudgeted();
bsalomon9f2d1572015-02-17 11:47:40 -0800321 return;
bsalomonc2f35b72015-01-23 07:19:22 -0800322 }
bsalomonc2f35b72015-01-23 07:19:22 -0800323 }
324 } else {
bsalomon9f2d1572015-02-17 11:47:40 -0800325 // Purge the resource immediately if we're over budget
bsalomon8718aaf2015-02-19 07:24:21 -0800326 // Also purge if the resource has neither a valid scratch key nor a unique key.
bsalomon3582d3e2015-02-13 14:20:05 -0800327 bool noKey = !resource->resourcePriv().getScratchKey().isValid() &&
bsalomon8718aaf2015-02-19 07:24:21 -0800328 !resource->getUniqueKey().isValid();
bsalomonf320e042015-02-17 15:09:34 -0800329 if (!this->overBudget() && !noKey) {
bsalomon9f2d1572015-02-17 11:47:40 -0800330 return;
bsalomonc2f35b72015-01-23 07:19:22 -0800331 }
332 }
bsalomondace19e2014-11-17 07:34:06 -0800333
bsalomonf320e042015-02-17 15:09:34 -0800334 SkDEBUGCODE(int beforeCount = this->getResourceCount();)
bsalomon9f2d1572015-02-17 11:47:40 -0800335 resource->cacheAccess().release();
336 // We should at least free this resource, perhaps dependent resources as well.
bsalomonf320e042015-02-17 15:09:34 -0800337 SkASSERT(this->getResourceCount() < beforeCount);
bsalomon71cb0c22014-11-14 12:10:14 -0800338 this->validate();
339}
340
bsalomon0ea80f42015-02-11 10:49:59 -0800341void GrResourceCache::didChangeGpuMemorySize(const GrGpuResource* resource, size_t oldSize) {
bsalomon71cb0c22014-11-14 12:10:14 -0800342 // SkASSERT(!fPurging); GrPathRange increases size during flush. :(
343 SkASSERT(resource);
344 SkASSERT(this->isInCache(resource));
345
bsalomondace19e2014-11-17 07:34:06 -0800346 ptrdiff_t delta = resource->gpuMemorySize() - oldSize;
347
348 fBytes += delta;
bsalomon82b1d622014-11-14 13:59:57 -0800349#if GR_CACHE_STATS
350 fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes);
351#endif
bsalomon3582d3e2015-02-13 14:20:05 -0800352 if (resource->resourcePriv().isBudgeted()) {
bsalomondace19e2014-11-17 07:34:06 -0800353 fBudgetedBytes += delta;
354#if GR_CACHE_STATS
355 fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
356#endif
357 }
bsalomon71cb0c22014-11-14 12:10:14 -0800358
359 this->purgeAsNeeded();
360 this->validate();
361}
362
bsalomon0ea80f42015-02-11 10:49:59 -0800363void GrResourceCache::didChangeBudgetStatus(GrGpuResource* resource) {
bsalomon84c8e622014-11-17 09:33:27 -0800364 SkASSERT(resource);
365 SkASSERT(this->isInCache(resource));
366
367 size_t size = resource->gpuMemorySize();
368
bsalomon3582d3e2015-02-13 14:20:05 -0800369 if (resource->resourcePriv().isBudgeted()) {
bsalomon84c8e622014-11-17 09:33:27 -0800370 ++fBudgetedCount;
371 fBudgetedBytes += size;
bsalomonafe30052015-01-16 07:32:33 -0800372#if GR_CACHE_STATS
373 fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
374 fBudgetedHighWaterCount = SkTMax(fBudgetedCount, fBudgetedHighWaterCount);
375#endif
bsalomon84c8e622014-11-17 09:33:27 -0800376 this->purgeAsNeeded();
377 } else {
378 --fBudgetedCount;
379 fBudgetedBytes -= size;
380 }
381
382 this->validate();
383}
384
bsalomon0ea80f42015-02-11 10:49:59 -0800385void GrResourceCache::internalPurgeAsNeeded() {
bsalomonf320e042015-02-17 15:09:34 -0800386 SkASSERT(this->overBudget());
bsalomon71cb0c22014-11-14 12:10:14 -0800387
bsalomon9f2d1572015-02-17 11:47:40 -0800388 bool stillOverbudget = true;
389 while (fPurgeableQueue.count()) {
390 GrGpuResource* resource = fPurgeableQueue.peek();
391 SkASSERT(resource->isPurgeable());
392 resource->cacheAccess().release();
bsalomonf320e042015-02-17 15:09:34 -0800393 if (!this->overBudget()) {
bsalomon9f2d1572015-02-17 11:47:40 -0800394 stillOverbudget = false;
395 break;
bsalomon71cb0c22014-11-14 12:10:14 -0800396 }
bsalomon9f2d1572015-02-17 11:47:40 -0800397 }
bsalomon71cb0c22014-11-14 12:10:14 -0800398
bsalomonb436ed62014-11-17 12:15:56 -0800399 this->validate();
bsalomon9f2d1572015-02-17 11:47:40 -0800400
401 if (stillOverbudget) {
402 // Despite the purge we're still over budget. Call our over budget callback. If this frees
403 // any resources then we'll get notifyPurgeable() calls and take appropriate action.
404 (*fOverBudgetCB)(fOverBudgetData);
405 this->validate();
406 }
bsalomon71cb0c22014-11-14 12:10:14 -0800407}
408
bsalomon0ea80f42015-02-11 10:49:59 -0800409void GrResourceCache::purgeAllUnlocked() {
bsalomon9f2d1572015-02-17 11:47:40 -0800410 // We could disable maintaining the heap property here, but it would add a lot of complexity.
411 // Moreover, this is rarely called.
412 while (fPurgeableQueue.count()) {
413 GrGpuResource* resource = fPurgeableQueue.peek();
414 SkASSERT(resource->isPurgeable());
415 resource->cacheAccess().release();
416 }
bsalomon71cb0c22014-11-14 12:10:14 -0800417
bsalomonb436ed62014-11-17 12:15:56 -0800418 this->validate();
bsalomon71cb0c22014-11-14 12:10:14 -0800419}
420
bsalomon8718aaf2015-02-19 07:24:21 -0800421void GrResourceCache::processInvalidUniqueKeys(
422 const SkTArray<GrUniqueKeyInvalidatedMessage>& msgs) {
bsalomon23e619c2015-02-06 11:54:28 -0800423 for (int i = 0; i < msgs.count(); ++i) {
bsalomon8718aaf2015-02-19 07:24:21 -0800424 GrGpuResource* resource = this->findAndRefUniqueResource(msgs[i].key());
bsalomon23e619c2015-02-06 11:54:28 -0800425 if (resource) {
bsalomon8718aaf2015-02-19 07:24:21 -0800426 resource->resourcePriv().removeUniqueKey();
bsalomon23e619c2015-02-06 11:54:28 -0800427 resource->unref(); // will call notifyPurgeable, if it is indeed now purgeable.
428 }
429 }
430}
431
bsalomonf320e042015-02-17 15:09:34 -0800432void GrResourceCache::addToNonpurgeableArray(GrGpuResource* resource) {
433 int index = fNonpurgeableResources.count();
434 *fNonpurgeableResources.append() = resource;
435 *resource->cacheAccess().accessCacheIndex() = index;
436}
437
438void GrResourceCache::removeFromNonpurgeableArray(GrGpuResource* resource) {
439 int* index = resource->cacheAccess().accessCacheIndex();
440 // Fill the whole we will create in the array with the tail object, adjust its index, and
441 // then pop the array
442 GrGpuResource* tail = *(fNonpurgeableResources.end() - 1);
443 SkASSERT(fNonpurgeableResources[*index] == resource);
444 fNonpurgeableResources[*index] = tail;
445 *tail->cacheAccess().accessCacheIndex() = *index;
446 fNonpurgeableResources.pop();
447 SkDEBUGCODE(*index = -1);
448}
449
bsalomonddf30e62015-02-19 11:38:44 -0800450uint32_t GrResourceCache::getNextTimestamp() {
451 // If we wrap then all the existing resources will appear older than any resources that get
452 // a timestamp after the wrap.
453 if (0 == fTimestamp) {
454 int count = this->getResourceCount();
455 if (count) {
456 // Reset all the timestamps. We sort the resources by timestamp and then assign
457 // sequential timestamps beginning with 0. This is O(n*lg(n)) but it should be extremely
458 // rare.
459 SkTDArray<GrGpuResource*> sortedPurgeableResources;
460 sortedPurgeableResources.setReserve(fPurgeableQueue.count());
461
462 while (fPurgeableQueue.count()) {
463 *sortedPurgeableResources.append() = fPurgeableQueue.peek();
464 fPurgeableQueue.pop();
465 }
466
467 struct Less {
468 bool operator()(GrGpuResource* a, GrGpuResource* b) {
469 return CompareTimestamp(a,b);
470 }
471 };
472 Less less;
473 SkTQSort(fNonpurgeableResources.begin(), fNonpurgeableResources.end() - 1, less);
474
475 // Pick resources out of the purgeable and non-purgeable arrays based on lowest
476 // timestamp and assign new timestamps.
477 int currP = 0;
478 int currNP = 0;
479 while (currP < sortedPurgeableResources.count() &&
480 currNP < fNonpurgeableResources.count()) {
481 uint32_t tsP = sortedPurgeableResources[currP]->cacheAccess().timestamp();
482 uint32_t tsNP = fNonpurgeableResources[currNP]->cacheAccess().timestamp();
483 SkASSERT(tsP != tsNP);
484 if (tsP < tsNP) {
485 sortedPurgeableResources[currP++]->cacheAccess().setTimestamp(fTimestamp++);
486 } else {
487 // Correct the index in the nonpurgeable array stored on the resource post-sort.
488 *fNonpurgeableResources[currNP]->cacheAccess().accessCacheIndex() = currNP;
489 fNonpurgeableResources[currNP++]->cacheAccess().setTimestamp(fTimestamp++);
490 }
491 }
492
493 // The above loop ended when we hit the end of one array. Finish the other one.
494 while (currP < sortedPurgeableResources.count()) {
495 sortedPurgeableResources[currP++]->cacheAccess().setTimestamp(fTimestamp++);
496 }
497 while (currNP < fNonpurgeableResources.count()) {
498 *fNonpurgeableResources[currNP]->cacheAccess().accessCacheIndex() = currNP;
499 fNonpurgeableResources[currNP++]->cacheAccess().setTimestamp(fTimestamp++);
500 }
501
502 // Rebuild the queue.
503 for (int i = 0; i < sortedPurgeableResources.count(); ++i) {
504 fPurgeableQueue.insert(sortedPurgeableResources[i]);
505 }
506
507 this->validate();
508 SkASSERT(count == this->getResourceCount());
509
510 // count should be the next timestamp we return.
511 SkASSERT(fTimestamp == SkToU32(count));
512 }
513 }
514 return fTimestamp++;
515}
516
bsalomon71cb0c22014-11-14 12:10:14 -0800517#ifdef SK_DEBUG
bsalomon0ea80f42015-02-11 10:49:59 -0800518void GrResourceCache::validate() const {
bsalomonc2f35b72015-01-23 07:19:22 -0800519 // Reduce the frequency of validations for large resource counts.
520 static SkRandom gRandom;
521 int mask = (SkNextPow2(fCount + 1) >> 5) - 1;
522 if (~mask && (gRandom.nextU() & mask)) {
523 return;
524 }
525
bsalomonf320e042015-02-17 15:09:34 -0800526 struct Stats {
527 size_t fBytes;
528 int fBudgetedCount;
529 size_t fBudgetedBytes;
530 int fLocked;
531 int fScratch;
532 int fCouldBeScratch;
533 int fContent;
534 const ScratchMap* fScratchMap;
bsalomon8718aaf2015-02-19 07:24:21 -0800535 const UniqueHash* fUniqueHash;
bsalomon71cb0c22014-11-14 12:10:14 -0800536
bsalomonf320e042015-02-17 15:09:34 -0800537 Stats(const GrResourceCache* cache) {
538 memset(this, 0, sizeof(*this));
539 fScratchMap = &cache->fScratchMap;
bsalomon8718aaf2015-02-19 07:24:21 -0800540 fUniqueHash = &cache->fUniqueHash;
bsalomon71cb0c22014-11-14 12:10:14 -0800541 }
542
bsalomonf320e042015-02-17 15:09:34 -0800543 void update(GrGpuResource* resource) {
544 fBytes += resource->gpuMemorySize();
bsalomondace19e2014-11-17 07:34:06 -0800545
bsalomonf320e042015-02-17 15:09:34 -0800546 if (!resource->isPurgeable()) {
547 ++fLocked;
548 }
bsalomon9f2d1572015-02-17 11:47:40 -0800549
bsalomonf320e042015-02-17 15:09:34 -0800550 if (resource->cacheAccess().isScratch()) {
bsalomon8718aaf2015-02-19 07:24:21 -0800551 SkASSERT(!resource->getUniqueKey().isValid());
bsalomonf320e042015-02-17 15:09:34 -0800552 ++fScratch;
553 SkASSERT(fScratchMap->countForKey(resource->resourcePriv().getScratchKey()));
554 SkASSERT(!resource->cacheAccess().isWrapped());
555 } else if (resource->resourcePriv().getScratchKey().isValid()) {
556 SkASSERT(!resource->resourcePriv().isBudgeted() ||
bsalomon8718aaf2015-02-19 07:24:21 -0800557 resource->getUniqueKey().isValid());
bsalomonf320e042015-02-17 15:09:34 -0800558 ++fCouldBeScratch;
559 SkASSERT(fScratchMap->countForKey(resource->resourcePriv().getScratchKey()));
560 SkASSERT(!resource->cacheAccess().isWrapped());
561 }
bsalomon8718aaf2015-02-19 07:24:21 -0800562 const GrUniqueKey& uniqueKey = resource->getUniqueKey();
563 if (uniqueKey.isValid()) {
bsalomonf320e042015-02-17 15:09:34 -0800564 ++fContent;
bsalomon8718aaf2015-02-19 07:24:21 -0800565 SkASSERT(fUniqueHash->find(uniqueKey) == resource);
bsalomonf320e042015-02-17 15:09:34 -0800566 SkASSERT(!resource->cacheAccess().isWrapped());
567 SkASSERT(resource->resourcePriv().isBudgeted());
568 }
569
570 if (resource->resourcePriv().isBudgeted()) {
571 ++fBudgetedCount;
572 fBudgetedBytes += resource->gpuMemorySize();
573 }
bsalomon9f2d1572015-02-17 11:47:40 -0800574 }
bsalomonf320e042015-02-17 15:09:34 -0800575 };
576
577 Stats stats(this);
578
579 for (int i = 0; i < fNonpurgeableResources.count(); ++i) {
580 SkASSERT(!fNonpurgeableResources[i]->isPurgeable());
581 SkASSERT(*fNonpurgeableResources[i]->cacheAccess().accessCacheIndex() == i);
582 SkASSERT(!fNonpurgeableResources[i]->wasDestroyed());
583 stats.update(fNonpurgeableResources[i]);
bsalomon71cb0c22014-11-14 12:10:14 -0800584 }
bsalomon9f2d1572015-02-17 11:47:40 -0800585 for (int i = 0; i < fPurgeableQueue.count(); ++i) {
586 SkASSERT(fPurgeableQueue.at(i)->isPurgeable());
bsalomonf320e042015-02-17 15:09:34 -0800587 SkASSERT(*fPurgeableQueue.at(i)->cacheAccess().accessCacheIndex() == i);
588 SkASSERT(!fPurgeableQueue.at(i)->wasDestroyed());
589 stats.update(fPurgeableQueue.at(i));
bsalomon9f2d1572015-02-17 11:47:40 -0800590 }
591
bsalomonf320e042015-02-17 15:09:34 -0800592 SkASSERT(fCount == this->getResourceCount());
bsalomondace19e2014-11-17 07:34:06 -0800593 SkASSERT(fBudgetedCount <= fCount);
bsalomonf320e042015-02-17 15:09:34 -0800594 SkASSERT(fBudgetedBytes <= fBytes);
595 SkASSERT(stats.fBytes == fBytes);
596 SkASSERT(stats.fBudgetedBytes == fBudgetedBytes);
597 SkASSERT(stats.fBudgetedCount == fBudgetedCount);
bsalomon71cb0c22014-11-14 12:10:14 -0800598#if GR_CACHE_STATS
bsalomondace19e2014-11-17 07:34:06 -0800599 SkASSERT(fBudgetedHighWaterCount <= fHighWaterCount);
600 SkASSERT(fBudgetedHighWaterBytes <= fHighWaterBytes);
bsalomonf320e042015-02-17 15:09:34 -0800601 SkASSERT(fBytes <= fHighWaterBytes);
602 SkASSERT(fCount <= fHighWaterCount);
603 SkASSERT(fBudgetedBytes <= fBudgetedHighWaterBytes);
604 SkASSERT(fBudgetedCount <= fBudgetedHighWaterCount);
bsalomon71cb0c22014-11-14 12:10:14 -0800605#endif
bsalomon8718aaf2015-02-19 07:24:21 -0800606 SkASSERT(stats.fContent == fUniqueHash.count());
bsalomonf320e042015-02-17 15:09:34 -0800607 SkASSERT(stats.fScratch + stats.fCouldBeScratch == fScratchMap.count());
bsalomon71cb0c22014-11-14 12:10:14 -0800608
bsalomon63c992f2015-01-23 12:47:59 -0800609 // This assertion is not currently valid because we can be in recursive notifyIsPurgeable()
bsalomon12299ab2014-11-14 13:33:09 -0800610 // calls. This will be fixed when subresource registration is explicit.
bsalomondace19e2014-11-17 07:34:06 -0800611 // bool overBudget = budgetedBytes > fMaxBytes || budgetedCount > fMaxCount;
bsalomon12299ab2014-11-14 13:33:09 -0800612 // SkASSERT(!overBudget || locked == count || fPurging);
bsalomon71cb0c22014-11-14 12:10:14 -0800613}
bsalomonf320e042015-02-17 15:09:34 -0800614
615bool GrResourceCache::isInCache(const GrGpuResource* resource) const {
616 int index = *resource->cacheAccess().accessCacheIndex();
617 if (index < 0) {
618 return false;
619 }
620 if (index < fPurgeableQueue.count() && fPurgeableQueue.at(index) == resource) {
621 return true;
622 }
623 if (index < fNonpurgeableResources.count() && fNonpurgeableResources[index] == resource) {
624 return true;
625 }
626 SkDEBUGFAIL("Resource index should be -1 or the resource should be in the cache.");
627 return false;
628}
629
bsalomon71cb0c22014-11-14 12:10:14 -0800630#endif