blob: 8b445683385528983c48be4bf4b91beefd5a8ca9 [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"
hendrikw876c3132015-03-04 10:33:49 -080012#include "GrTracing.h"
bsalomon7775c852014-12-30 12:50:52 -080013#include "SkChecksum.h"
bsalomon71cb0c22014-11-14 12:10:14 -080014#include "SkGr.h"
15#include "SkMessageBus.h"
bsalomonddf30e62015-02-19 11:38:44 -080016#include "SkTSort.h"
bsalomon71cb0c22014-11-14 12:10:14 -080017
bsalomon8718aaf2015-02-19 07:24:21 -080018DECLARE_SKMESSAGEBUS_MESSAGE(GrUniqueKeyInvalidatedMessage);
bsalomon71cb0c22014-11-14 12:10:14 -080019
20//////////////////////////////////////////////////////////////////////////////
21
bsalomon7775c852014-12-30 12:50:52 -080022GrScratchKey::ResourceType GrScratchKey::GenerateResourceType() {
bsalomon24db3b12015-01-23 04:24:04 -080023 static int32_t gType = INHERITED::kInvalidDomain + 1;
bsalomonfe369ee2014-11-10 11:59:06 -080024
bsalomon7775c852014-12-30 12:50:52 -080025 int32_t type = sk_atomic_inc(&gType);
robertphillips9790a7b2015-01-05 12:29:15 -080026 if (type > SK_MaxU16) {
bsalomon71cb0c22014-11-14 12:10:14 -080027 SkFAIL("Too many Resource Types");
28 }
29
30 return static_cast<ResourceType>(type);
31}
32
bsalomon8718aaf2015-02-19 07:24:21 -080033GrUniqueKey::Domain GrUniqueKey::GenerateDomain() {
bsalomon24db3b12015-01-23 04:24:04 -080034 static int32_t gDomain = INHERITED::kInvalidDomain + 1;
bsalomon7775c852014-12-30 12:50:52 -080035
bsalomon24db3b12015-01-23 04:24:04 -080036 int32_t domain = sk_atomic_inc(&gDomain);
kkinnunen016dffb2015-01-23 06:43:05 -080037 if (domain > SK_MaxU16) {
bsalomon8718aaf2015-02-19 07:24:21 -080038 SkFAIL("Too many GrUniqueKey Domains");
bsalomon7775c852014-12-30 12:50:52 -080039 }
bsalomon24db3b12015-01-23 04:24:04 -080040
41 return static_cast<Domain>(domain);
42}
43uint32_t GrResourceKeyHash(const uint32_t* data, size_t size) {
44 return SkChecksum::Compute(data, size);
bsalomon7775c852014-12-30 12:50:52 -080045}
46
bsalomonfe369ee2014-11-10 11:59:06 -080047//////////////////////////////////////////////////////////////////////////////
48
bsalomon0ea80f42015-02-11 10:49:59 -080049class GrResourceCache::AutoValidate : ::SkNoncopyable {
bsalomon71cb0c22014-11-14 12:10:14 -080050public:
bsalomon0ea80f42015-02-11 10:49:59 -080051 AutoValidate(GrResourceCache* cache) : fCache(cache) { cache->validate(); }
bsalomon71cb0c22014-11-14 12:10:14 -080052 ~AutoValidate() { fCache->validate(); }
53private:
bsalomon0ea80f42015-02-11 10:49:59 -080054 GrResourceCache* fCache;
bsalomon71cb0c22014-11-14 12:10:14 -080055};
56
57 //////////////////////////////////////////////////////////////////////////////
58
59static const int kDefaultMaxCount = 2 * (1 << 10);
60static const size_t kDefaultMaxSize = 96 * (1 << 20);
61
bsalomon0ea80f42015-02-11 10:49:59 -080062GrResourceCache::GrResourceCache()
bsalomon9f2d1572015-02-17 11:47:40 -080063 : fTimestamp(0)
64 , fMaxCount(kDefaultMaxCount)
bsalomon71cb0c22014-11-14 12:10:14 -080065 , fMaxBytes(kDefaultMaxSize)
66#if GR_CACHE_STATS
67 , fHighWaterCount(0)
68 , fHighWaterBytes(0)
bsalomondace19e2014-11-17 07:34:06 -080069 , fBudgetedHighWaterCount(0)
70 , fBudgetedHighWaterBytes(0)
bsalomon71cb0c22014-11-14 12:10:14 -080071#endif
bsalomon71cb0c22014-11-14 12:10:14 -080072 , fBytes(0)
bsalomondace19e2014-11-17 07:34:06 -080073 , fBudgetedCount(0)
74 , fBudgetedBytes(0)
bsalomon71cb0c22014-11-14 12:10:14 -080075 , fOverBudgetCB(NULL)
76 , fOverBudgetData(NULL) {
bsalomonf320e042015-02-17 15:09:34 -080077 SkDEBUGCODE(fCount = 0;)
bsalomon71cb0c22014-11-14 12:10:14 -080078}
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);
bsalomon16961262014-08-26 14:01:07 -070092 SkASSERT(!this->isInCache(resource));
bsalomonf320e042015-02-17 15:09:34 -080093 SkASSERT(!resource->wasDestroyed());
94 SkASSERT(!resource->isPurgeable());
bsalomonddf30e62015-02-19 11:38:44 -080095
96 // We must set the timestamp before adding to the array in case the timestamp wraps and we wind
97 // up iterating over all the resources that already have timestamps.
98 resource->cacheAccess().setTimestamp(this->getNextTimestamp());
99
bsalomonf320e042015-02-17 15:09:34 -0800100 this->addToNonpurgeableArray(resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800101
bsalomondace19e2014-11-17 07:34:06 -0800102 size_t size = resource->gpuMemorySize();
bsalomonf320e042015-02-17 15:09:34 -0800103 SkDEBUGCODE(++fCount;)
bsalomon84c8e622014-11-17 09:33:27 -0800104 fBytes += size;
bsalomon82b1d622014-11-14 13:59:57 -0800105#if GR_CACHE_STATS
bsalomonf320e042015-02-17 15:09:34 -0800106 fHighWaterCount = SkTMax(this->getResourceCount(), fHighWaterCount);
bsalomon82b1d622014-11-14 13:59:57 -0800107 fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes);
108#endif
bsalomon3582d3e2015-02-13 14:20:05 -0800109 if (resource->resourcePriv().isBudgeted()) {
bsalomondace19e2014-11-17 07:34:06 -0800110 ++fBudgetedCount;
111 fBudgetedBytes += size;
hendrikw876c3132015-03-04 10:33:49 -0800112 TRACE_COUNTER2(TRACE_DISABLED_BY_DEFAULT("skia.gpu.cache"), "skia budget", "used",
113 fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
bsalomondace19e2014-11-17 07:34:06 -0800114#if GR_CACHE_STATS
115 fBudgetedHighWaterCount = SkTMax(fBudgetedCount, fBudgetedHighWaterCount);
116 fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
117#endif
118 }
bsalomon3582d3e2015-02-13 14:20:05 -0800119 if (resource->resourcePriv().getScratchKey().isValid()) {
bsalomon84c8e622014-11-17 09:33:27 -0800120 SkASSERT(!resource->cacheAccess().isWrapped());
bsalomon3582d3e2015-02-13 14:20:05 -0800121 fScratchMap.insert(resource->resourcePriv().getScratchKey(), resource);
bsalomon744998e2014-08-28 09:54:34 -0700122 }
bsalomon9f2d1572015-02-17 11:47:40 -0800123
bsalomon71cb0c22014-11-14 12:10:14 -0800124 this->purgeAsNeeded();
bsalomonc8dc1f72014-08-21 13:02:13 -0700125}
126
bsalomon0ea80f42015-02-11 10:49:59 -0800127void GrResourceCache::removeResource(GrGpuResource* resource) {
bsalomon9f2d1572015-02-17 11:47:40 -0800128 this->validate();
bsalomon16961262014-08-26 14:01:07 -0700129 SkASSERT(this->isInCache(resource));
bsalomondace19e2014-11-17 07:34:06 -0800130
bsalomon9f2d1572015-02-17 11:47:40 -0800131 if (resource->isPurgeable()) {
132 fPurgeableQueue.remove(resource);
bsalomonf320e042015-02-17 15:09:34 -0800133 } else {
134 this->removeFromNonpurgeableArray(resource);
bsalomon9f2d1572015-02-17 11:47:40 -0800135 }
136
bsalomondace19e2014-11-17 07:34:06 -0800137 size_t size = resource->gpuMemorySize();
bsalomonf320e042015-02-17 15:09:34 -0800138 SkDEBUGCODE(--fCount;)
bsalomondace19e2014-11-17 07:34:06 -0800139 fBytes -= size;
bsalomon3582d3e2015-02-13 14:20:05 -0800140 if (resource->resourcePriv().isBudgeted()) {
bsalomondace19e2014-11-17 07:34:06 -0800141 --fBudgetedCount;
142 fBudgetedBytes -= size;
hendrikw876c3132015-03-04 10:33:49 -0800143 TRACE_COUNTER2(TRACE_DISABLED_BY_DEFAULT("skia.gpu.cache"), "skia budget", "used",
144 fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
bsalomondace19e2014-11-17 07:34:06 -0800145 }
146
bsalomon3582d3e2015-02-13 14:20:05 -0800147 if (resource->resourcePriv().getScratchKey().isValid()) {
148 fScratchMap.remove(resource->resourcePriv().getScratchKey(), resource);
bsalomon744998e2014-08-28 09:54:34 -0700149 }
bsalomon8718aaf2015-02-19 07:24:21 -0800150 if (resource->getUniqueKey().isValid()) {
151 fUniqueHash.remove(resource->getUniqueKey());
bsalomon8b79d232014-11-10 10:19:06 -0800152 }
bsalomonb436ed62014-11-17 12:15:56 -0800153 this->validate();
bsalomonc8dc1f72014-08-21 13:02:13 -0700154}
155
bsalomon0ea80f42015-02-11 10:49:59 -0800156void GrResourceCache::abandonAll() {
bsalomon71cb0c22014-11-14 12:10:14 -0800157 AutoValidate av(this);
158
bsalomonf320e042015-02-17 15:09:34 -0800159 while (fNonpurgeableResources.count()) {
160 GrGpuResource* back = *(fNonpurgeableResources.end() - 1);
161 SkASSERT(!back->wasDestroyed());
162 back->cacheAccess().abandon();
bsalomonc8dc1f72014-08-21 13:02:13 -0700163 }
bsalomonf320e042015-02-17 15:09:34 -0800164
165 while (fPurgeableQueue.count()) {
166 GrGpuResource* top = fPurgeableQueue.peek();
167 SkASSERT(!top->wasDestroyed());
168 top->cacheAccess().abandon();
169 }
170
bsalomon744998e2014-08-28 09:54:34 -0700171 SkASSERT(!fScratchMap.count());
bsalomon8718aaf2015-02-19 07:24:21 -0800172 SkASSERT(!fUniqueHash.count());
bsalomonc8dc1f72014-08-21 13:02:13 -0700173 SkASSERT(!fCount);
bsalomonf320e042015-02-17 15:09:34 -0800174 SkASSERT(!this->getResourceCount());
bsalomondace19e2014-11-17 07:34:06 -0800175 SkASSERT(!fBytes);
176 SkASSERT(!fBudgetedCount);
177 SkASSERT(!fBudgetedBytes);
bsalomonc8dc1f72014-08-21 13:02:13 -0700178}
179
bsalomon0ea80f42015-02-11 10:49:59 -0800180void GrResourceCache::releaseAll() {
bsalomon71cb0c22014-11-14 12:10:14 -0800181 AutoValidate av(this);
182
bsalomonf320e042015-02-17 15:09:34 -0800183 while(fNonpurgeableResources.count()) {
184 GrGpuResource* back = *(fNonpurgeableResources.end() - 1);
185 SkASSERT(!back->wasDestroyed());
186 back->cacheAccess().release();
bsalomonc8dc1f72014-08-21 13:02:13 -0700187 }
bsalomonf320e042015-02-17 15:09:34 -0800188
189 while (fPurgeableQueue.count()) {
190 GrGpuResource* top = fPurgeableQueue.peek();
191 SkASSERT(!top->wasDestroyed());
192 top->cacheAccess().release();
193 }
194
bsalomon744998e2014-08-28 09:54:34 -0700195 SkASSERT(!fScratchMap.count());
bsalomon8718aaf2015-02-19 07:24:21 -0800196 SkASSERT(!fUniqueHash.count());
bsalomonc8dc1f72014-08-21 13:02:13 -0700197 SkASSERT(!fCount);
bsalomonf320e042015-02-17 15:09:34 -0800198 SkASSERT(!this->getResourceCount());
bsalomondace19e2014-11-17 07:34:06 -0800199 SkASSERT(!fBytes);
200 SkASSERT(!fBudgetedCount);
201 SkASSERT(!fBudgetedBytes);
bsalomonc8dc1f72014-08-21 13:02:13 -0700202}
bsalomonbcf0a522014-10-08 08:40:09 -0700203
bsalomon0ea80f42015-02-11 10:49:59 -0800204class GrResourceCache::AvailableForScratchUse {
bsalomonbcf0a522014-10-08 08:40:09 -0700205public:
bsalomon000f8292014-10-15 19:04:14 -0700206 AvailableForScratchUse(bool rejectPendingIO) : fRejectPendingIO(rejectPendingIO) { }
bsalomonbcf0a522014-10-08 08:40:09 -0700207
208 bool operator()(const GrGpuResource* resource) const {
bsalomon12299ab2014-11-14 13:33:09 -0800209 if (resource->internalHasRef() || !resource->cacheAccess().isScratch()) {
bsalomon000f8292014-10-15 19:04:14 -0700210 return false;
bsalomonbcf0a522014-10-08 08:40:09 -0700211 }
bsalomon000f8292014-10-15 19:04:14 -0700212 return !fRejectPendingIO || !resource->internalHasPendingIO();
bsalomonbcf0a522014-10-08 08:40:09 -0700213 }
bsalomon1e2530b2014-10-09 09:57:18 -0700214
bsalomonbcf0a522014-10-08 08:40:09 -0700215private:
bsalomon000f8292014-10-15 19:04:14 -0700216 bool fRejectPendingIO;
bsalomonbcf0a522014-10-08 08:40:09 -0700217};
218
bsalomon0ea80f42015-02-11 10:49:59 -0800219GrGpuResource* GrResourceCache::findAndRefScratchResource(const GrScratchKey& scratchKey,
bsalomon9f2d1572015-02-17 11:47:40 -0800220 uint32_t flags) {
bsalomon7775c852014-12-30 12:50:52 -0800221 SkASSERT(scratchKey.isValid());
bsalomon000f8292014-10-15 19:04:14 -0700222
bsalomon71cb0c22014-11-14 12:10:14 -0800223 GrGpuResource* resource;
bsalomon000f8292014-10-15 19:04:14 -0700224 if (flags & (kPreferNoPendingIO_ScratchFlag | kRequireNoPendingIO_ScratchFlag)) {
bsalomon71cb0c22014-11-14 12:10:14 -0800225 resource = fScratchMap.find(scratchKey, AvailableForScratchUse(true));
bsalomon000f8292014-10-15 19:04:14 -0700226 if (resource) {
bsalomon9f2d1572015-02-17 11:47:40 -0800227 this->refAndMakeResourceMRU(resource);
bsalomonb436ed62014-11-17 12:15:56 -0800228 this->validate();
229 return resource;
bsalomon000f8292014-10-15 19:04:14 -0700230 } else if (flags & kRequireNoPendingIO_ScratchFlag) {
231 return NULL;
232 }
233 // TODO: fail here when kPrefer is specified, we didn't find a resource without pending io,
234 // but there is still space in our budget for the resource.
235 }
bsalomon71cb0c22014-11-14 12:10:14 -0800236 resource = fScratchMap.find(scratchKey, AvailableForScratchUse(false));
237 if (resource) {
bsalomon9f2d1572015-02-17 11:47:40 -0800238 this->refAndMakeResourceMRU(resource);
bsalomonb436ed62014-11-17 12:15:56 -0800239 this->validate();
bsalomon71cb0c22014-11-14 12:10:14 -0800240 }
241 return resource;
bsalomonbcf0a522014-10-08 08:40:09 -0700242}
bsalomon8b79d232014-11-10 10:19:06 -0800243
bsalomon0ea80f42015-02-11 10:49:59 -0800244void GrResourceCache::willRemoveScratchKey(const GrGpuResource* resource) {
bsalomon3582d3e2015-02-13 14:20:05 -0800245 SkASSERT(resource->resourcePriv().getScratchKey().isValid());
246 fScratchMap.remove(resource->resourcePriv().getScratchKey(), resource);
bsalomon10e23ca2014-11-25 05:52:06 -0800247}
248
bsalomonf99e9612015-02-19 08:24:16 -0800249void GrResourceCache::removeUniqueKey(GrGpuResource* resource) {
bsalomon23e619c2015-02-06 11:54:28 -0800250 // Someone has a ref to this resource in order to invalidate it. When the ref count reaches
251 // zero we will get a notifyPurgable() and figure out what to do with it.
bsalomonf99e9612015-02-19 08:24:16 -0800252 if (resource->getUniqueKey().isValid()) {
253 SkASSERT(resource == fUniqueHash.find(resource->getUniqueKey()));
254 fUniqueHash.remove(resource->getUniqueKey());
255 }
256 resource->cacheAccess().removeUniqueKey();
257 this->validate();
bsalomon23e619c2015-02-06 11:54:28 -0800258}
259
bsalomonf99e9612015-02-19 08:24:16 -0800260void GrResourceCache::changeUniqueKey(GrGpuResource* resource, const GrUniqueKey& newKey) {
bsalomon8b79d232014-11-10 10:19:06 -0800261 SkASSERT(resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800262 SkASSERT(this->isInCache(resource));
bsalomon8b79d232014-11-10 10:19:06 -0800263
bsalomonf99e9612015-02-19 08:24:16 -0800264 // Remove the entry for this resource if it already has a unique key.
265 if (resource->getUniqueKey().isValid()) {
266 SkASSERT(resource == fUniqueHash.find(resource->getUniqueKey()));
267 fUniqueHash.remove(resource->getUniqueKey());
268 SkASSERT(NULL == fUniqueHash.find(resource->getUniqueKey()));
bsalomon8b79d232014-11-10 10:19:06 -0800269 }
270
bsalomonf99e9612015-02-19 08:24:16 -0800271 // If another resource has the new key, remove its key then install the key on this resource.
272 if (newKey.isValid()) {
273 if (GrGpuResource* old = fUniqueHash.find(newKey)) {
274 // If the old resource using the key is purgeable and is unreachable, then remove it.
275 if (!old->resourcePriv().getScratchKey().isValid() && old->isPurgeable()) {
276 // release may call validate() which will assert that resource is in fUniqueHash
277 // if it has a valid key. So in debug reset the key here before we assign it.
278 SkDEBUGCODE(resource->cacheAccess().removeUniqueKey();)
279 old->cacheAccess().release();
280 } else {
281 fUniqueHash.remove(newKey);
282 old->cacheAccess().removeUniqueKey();
283 }
284 }
285 SkASSERT(NULL == fUniqueHash.find(newKey));
286 resource->cacheAccess().setUniqueKey(newKey);
287 fUniqueHash.add(resource);
288 } else {
289 resource->cacheAccess().removeUniqueKey();
290 }
291
bsalomon71cb0c22014-11-14 12:10:14 -0800292 this->validate();
bsalomon8b79d232014-11-10 10:19:06 -0800293}
bsalomon71cb0c22014-11-14 12:10:14 -0800294
bsalomon9f2d1572015-02-17 11:47:40 -0800295void GrResourceCache::refAndMakeResourceMRU(GrGpuResource* resource) {
bsalomon71cb0c22014-11-14 12:10:14 -0800296 SkASSERT(resource);
297 SkASSERT(this->isInCache(resource));
bsalomonddf30e62015-02-19 11:38:44 -0800298
bsalomon9f2d1572015-02-17 11:47:40 -0800299 if (resource->isPurgeable()) {
300 // It's about to become unpurgeable.
301 fPurgeableQueue.remove(resource);
bsalomonf320e042015-02-17 15:09:34 -0800302 this->addToNonpurgeableArray(resource);
bsalomon9f2d1572015-02-17 11:47:40 -0800303 }
304 resource->ref();
bsalomonddf30e62015-02-19 11:38:44 -0800305
306 resource->cacheAccess().setTimestamp(this->getNextTimestamp());
bsalomonf320e042015-02-17 15:09:34 -0800307 this->validate();
bsalomon71cb0c22014-11-14 12:10:14 -0800308}
309
bsalomon0ea80f42015-02-11 10:49:59 -0800310void GrResourceCache::notifyPurgeable(GrGpuResource* resource) {
bsalomon71cb0c22014-11-14 12:10:14 -0800311 SkASSERT(resource);
312 SkASSERT(this->isInCache(resource));
bsalomon63c992f2015-01-23 12:47:59 -0800313 SkASSERT(resource->isPurgeable());
bsalomon71cb0c22014-11-14 12:10:14 -0800314
bsalomonf320e042015-02-17 15:09:34 -0800315 this->removeFromNonpurgeableArray(resource);
bsalomon9f2d1572015-02-17 11:47:40 -0800316 fPurgeableQueue.insert(resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800317
bsalomon9f2d1572015-02-17 11:47:40 -0800318 if (!resource->resourcePriv().isBudgeted()) {
bsalomonc2f35b72015-01-23 07:19:22 -0800319 // Check whether this resource could still be used as a scratch resource.
bsalomon9f2d1572015-02-17 11:47:40 -0800320 if (!resource->cacheAccess().isWrapped() &&
321 resource->resourcePriv().getScratchKey().isValid()) {
bsalomonc2f35b72015-01-23 07:19:22 -0800322 // We won't purge an existing resource to make room for this one.
bsalomonf320e042015-02-17 15:09:34 -0800323 if (fBudgetedCount < fMaxCount &&
324 fBudgetedBytes + resource->gpuMemorySize() <= fMaxBytes) {
bsalomon3582d3e2015-02-13 14:20:05 -0800325 resource->resourcePriv().makeBudgeted();
bsalomon9f2d1572015-02-17 11:47:40 -0800326 return;
bsalomonc2f35b72015-01-23 07:19:22 -0800327 }
bsalomonc2f35b72015-01-23 07:19:22 -0800328 }
329 } else {
bsalomon9f2d1572015-02-17 11:47:40 -0800330 // Purge the resource immediately if we're over budget
bsalomon8718aaf2015-02-19 07:24:21 -0800331 // Also purge if the resource has neither a valid scratch key nor a unique key.
bsalomon3582d3e2015-02-13 14:20:05 -0800332 bool noKey = !resource->resourcePriv().getScratchKey().isValid() &&
bsalomon8718aaf2015-02-19 07:24:21 -0800333 !resource->getUniqueKey().isValid();
bsalomonf320e042015-02-17 15:09:34 -0800334 if (!this->overBudget() && !noKey) {
bsalomon9f2d1572015-02-17 11:47:40 -0800335 return;
bsalomonc2f35b72015-01-23 07:19:22 -0800336 }
337 }
bsalomondace19e2014-11-17 07:34:06 -0800338
bsalomonf320e042015-02-17 15:09:34 -0800339 SkDEBUGCODE(int beforeCount = this->getResourceCount();)
bsalomon9f2d1572015-02-17 11:47:40 -0800340 resource->cacheAccess().release();
341 // We should at least free this resource, perhaps dependent resources as well.
bsalomonf320e042015-02-17 15:09:34 -0800342 SkASSERT(this->getResourceCount() < beforeCount);
bsalomon71cb0c22014-11-14 12:10:14 -0800343 this->validate();
344}
345
bsalomon0ea80f42015-02-11 10:49:59 -0800346void GrResourceCache::didChangeGpuMemorySize(const GrGpuResource* resource, size_t oldSize) {
bsalomon71cb0c22014-11-14 12:10:14 -0800347 // SkASSERT(!fPurging); GrPathRange increases size during flush. :(
348 SkASSERT(resource);
349 SkASSERT(this->isInCache(resource));
350
bsalomondace19e2014-11-17 07:34:06 -0800351 ptrdiff_t delta = resource->gpuMemorySize() - oldSize;
352
353 fBytes += delta;
bsalomon82b1d622014-11-14 13:59:57 -0800354#if GR_CACHE_STATS
355 fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes);
356#endif
bsalomon3582d3e2015-02-13 14:20:05 -0800357 if (resource->resourcePriv().isBudgeted()) {
bsalomondace19e2014-11-17 07:34:06 -0800358 fBudgetedBytes += delta;
hendrikw876c3132015-03-04 10:33:49 -0800359 TRACE_COUNTER2(TRACE_DISABLED_BY_DEFAULT("skia.gpu.cache"), "skia budget", "used",
360 fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
bsalomondace19e2014-11-17 07:34:06 -0800361#if GR_CACHE_STATS
362 fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
363#endif
364 }
bsalomon71cb0c22014-11-14 12:10:14 -0800365
366 this->purgeAsNeeded();
367 this->validate();
368}
369
bsalomon0ea80f42015-02-11 10:49:59 -0800370void GrResourceCache::didChangeBudgetStatus(GrGpuResource* resource) {
bsalomon84c8e622014-11-17 09:33:27 -0800371 SkASSERT(resource);
372 SkASSERT(this->isInCache(resource));
373
374 size_t size = resource->gpuMemorySize();
375
bsalomon3582d3e2015-02-13 14:20:05 -0800376 if (resource->resourcePriv().isBudgeted()) {
bsalomon84c8e622014-11-17 09:33:27 -0800377 ++fBudgetedCount;
378 fBudgetedBytes += size;
bsalomonafe30052015-01-16 07:32:33 -0800379#if GR_CACHE_STATS
380 fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
381 fBudgetedHighWaterCount = SkTMax(fBudgetedCount, fBudgetedHighWaterCount);
382#endif
bsalomon84c8e622014-11-17 09:33:27 -0800383 this->purgeAsNeeded();
384 } else {
385 --fBudgetedCount;
386 fBudgetedBytes -= size;
387 }
hendrikw876c3132015-03-04 10:33:49 -0800388 TRACE_COUNTER2(TRACE_DISABLED_BY_DEFAULT("skia.gpu.cache"), "skia budget", "used",
389 fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
bsalomon84c8e622014-11-17 09:33:27 -0800390
391 this->validate();
392}
393
bsalomon0ea80f42015-02-11 10:49:59 -0800394void GrResourceCache::internalPurgeAsNeeded() {
bsalomonf320e042015-02-17 15:09:34 -0800395 SkASSERT(this->overBudget());
bsalomon71cb0c22014-11-14 12:10:14 -0800396
bsalomon9f2d1572015-02-17 11:47:40 -0800397 bool stillOverbudget = true;
398 while (fPurgeableQueue.count()) {
399 GrGpuResource* resource = fPurgeableQueue.peek();
400 SkASSERT(resource->isPurgeable());
401 resource->cacheAccess().release();
bsalomonf320e042015-02-17 15:09:34 -0800402 if (!this->overBudget()) {
bsalomon9f2d1572015-02-17 11:47:40 -0800403 stillOverbudget = false;
404 break;
bsalomon71cb0c22014-11-14 12:10:14 -0800405 }
bsalomon9f2d1572015-02-17 11:47:40 -0800406 }
bsalomon71cb0c22014-11-14 12:10:14 -0800407
bsalomonb436ed62014-11-17 12:15:56 -0800408 this->validate();
bsalomon9f2d1572015-02-17 11:47:40 -0800409
410 if (stillOverbudget) {
411 // Despite the purge we're still over budget. Call our over budget callback. If this frees
412 // any resources then we'll get notifyPurgeable() calls and take appropriate action.
413 (*fOverBudgetCB)(fOverBudgetData);
414 this->validate();
415 }
bsalomon71cb0c22014-11-14 12:10:14 -0800416}
417
bsalomon0ea80f42015-02-11 10:49:59 -0800418void GrResourceCache::purgeAllUnlocked() {
bsalomon9f2d1572015-02-17 11:47:40 -0800419 // We could disable maintaining the heap property here, but it would add a lot of complexity.
420 // Moreover, this is rarely called.
421 while (fPurgeableQueue.count()) {
422 GrGpuResource* resource = fPurgeableQueue.peek();
423 SkASSERT(resource->isPurgeable());
424 resource->cacheAccess().release();
425 }
bsalomon71cb0c22014-11-14 12:10:14 -0800426
bsalomonb436ed62014-11-17 12:15:56 -0800427 this->validate();
bsalomon71cb0c22014-11-14 12:10:14 -0800428}
429
bsalomon8718aaf2015-02-19 07:24:21 -0800430void GrResourceCache::processInvalidUniqueKeys(
431 const SkTArray<GrUniqueKeyInvalidatedMessage>& msgs) {
bsalomon23e619c2015-02-06 11:54:28 -0800432 for (int i = 0; i < msgs.count(); ++i) {
bsalomon8718aaf2015-02-19 07:24:21 -0800433 GrGpuResource* resource = this->findAndRefUniqueResource(msgs[i].key());
bsalomon23e619c2015-02-06 11:54:28 -0800434 if (resource) {
bsalomon8718aaf2015-02-19 07:24:21 -0800435 resource->resourcePriv().removeUniqueKey();
bsalomon23e619c2015-02-06 11:54:28 -0800436 resource->unref(); // will call notifyPurgeable, if it is indeed now purgeable.
437 }
438 }
439}
440
bsalomonf320e042015-02-17 15:09:34 -0800441void GrResourceCache::addToNonpurgeableArray(GrGpuResource* resource) {
442 int index = fNonpurgeableResources.count();
443 *fNonpurgeableResources.append() = resource;
444 *resource->cacheAccess().accessCacheIndex() = index;
445}
446
447void GrResourceCache::removeFromNonpurgeableArray(GrGpuResource* resource) {
448 int* index = resource->cacheAccess().accessCacheIndex();
449 // Fill the whole we will create in the array with the tail object, adjust its index, and
450 // then pop the array
451 GrGpuResource* tail = *(fNonpurgeableResources.end() - 1);
452 SkASSERT(fNonpurgeableResources[*index] == resource);
453 fNonpurgeableResources[*index] = tail;
454 *tail->cacheAccess().accessCacheIndex() = *index;
455 fNonpurgeableResources.pop();
456 SkDEBUGCODE(*index = -1);
457}
458
bsalomonddf30e62015-02-19 11:38:44 -0800459uint32_t GrResourceCache::getNextTimestamp() {
460 // If we wrap then all the existing resources will appear older than any resources that get
461 // a timestamp after the wrap.
462 if (0 == fTimestamp) {
463 int count = this->getResourceCount();
464 if (count) {
465 // Reset all the timestamps. We sort the resources by timestamp and then assign
466 // sequential timestamps beginning with 0. This is O(n*lg(n)) but it should be extremely
467 // rare.
468 SkTDArray<GrGpuResource*> sortedPurgeableResources;
469 sortedPurgeableResources.setReserve(fPurgeableQueue.count());
470
471 while (fPurgeableQueue.count()) {
472 *sortedPurgeableResources.append() = fPurgeableQueue.peek();
473 fPurgeableQueue.pop();
474 }
475
476 struct Less {
477 bool operator()(GrGpuResource* a, GrGpuResource* b) {
478 return CompareTimestamp(a,b);
479 }
480 };
481 Less less;
482 SkTQSort(fNonpurgeableResources.begin(), fNonpurgeableResources.end() - 1, less);
483
484 // Pick resources out of the purgeable and non-purgeable arrays based on lowest
485 // timestamp and assign new timestamps.
486 int currP = 0;
487 int currNP = 0;
488 while (currP < sortedPurgeableResources.count() &&
489 currNP < fNonpurgeableResources.count()) {
490 uint32_t tsP = sortedPurgeableResources[currP]->cacheAccess().timestamp();
491 uint32_t tsNP = fNonpurgeableResources[currNP]->cacheAccess().timestamp();
492 SkASSERT(tsP != tsNP);
493 if (tsP < tsNP) {
494 sortedPurgeableResources[currP++]->cacheAccess().setTimestamp(fTimestamp++);
495 } else {
496 // Correct the index in the nonpurgeable array stored on the resource post-sort.
497 *fNonpurgeableResources[currNP]->cacheAccess().accessCacheIndex() = currNP;
498 fNonpurgeableResources[currNP++]->cacheAccess().setTimestamp(fTimestamp++);
499 }
500 }
501
502 // The above loop ended when we hit the end of one array. Finish the other one.
503 while (currP < sortedPurgeableResources.count()) {
504 sortedPurgeableResources[currP++]->cacheAccess().setTimestamp(fTimestamp++);
505 }
506 while (currNP < fNonpurgeableResources.count()) {
507 *fNonpurgeableResources[currNP]->cacheAccess().accessCacheIndex() = currNP;
508 fNonpurgeableResources[currNP++]->cacheAccess().setTimestamp(fTimestamp++);
509 }
510
511 // Rebuild the queue.
512 for (int i = 0; i < sortedPurgeableResources.count(); ++i) {
513 fPurgeableQueue.insert(sortedPurgeableResources[i]);
514 }
515
516 this->validate();
517 SkASSERT(count == this->getResourceCount());
518
519 // count should be the next timestamp we return.
520 SkASSERT(fTimestamp == SkToU32(count));
521 }
522 }
523 return fTimestamp++;
524}
525
bsalomon71cb0c22014-11-14 12:10:14 -0800526#ifdef SK_DEBUG
bsalomon0ea80f42015-02-11 10:49:59 -0800527void GrResourceCache::validate() const {
bsalomonc2f35b72015-01-23 07:19:22 -0800528 // Reduce the frequency of validations for large resource counts.
529 static SkRandom gRandom;
530 int mask = (SkNextPow2(fCount + 1) >> 5) - 1;
531 if (~mask && (gRandom.nextU() & mask)) {
532 return;
533 }
534
bsalomonf320e042015-02-17 15:09:34 -0800535 struct Stats {
536 size_t fBytes;
537 int fBudgetedCount;
538 size_t fBudgetedBytes;
539 int fLocked;
540 int fScratch;
541 int fCouldBeScratch;
542 int fContent;
543 const ScratchMap* fScratchMap;
bsalomon8718aaf2015-02-19 07:24:21 -0800544 const UniqueHash* fUniqueHash;
bsalomon71cb0c22014-11-14 12:10:14 -0800545
bsalomonf320e042015-02-17 15:09:34 -0800546 Stats(const GrResourceCache* cache) {
547 memset(this, 0, sizeof(*this));
548 fScratchMap = &cache->fScratchMap;
bsalomon8718aaf2015-02-19 07:24:21 -0800549 fUniqueHash = &cache->fUniqueHash;
bsalomon71cb0c22014-11-14 12:10:14 -0800550 }
551
bsalomonf320e042015-02-17 15:09:34 -0800552 void update(GrGpuResource* resource) {
553 fBytes += resource->gpuMemorySize();
bsalomondace19e2014-11-17 07:34:06 -0800554
bsalomonf320e042015-02-17 15:09:34 -0800555 if (!resource->isPurgeable()) {
556 ++fLocked;
557 }
bsalomon9f2d1572015-02-17 11:47:40 -0800558
bsalomonf320e042015-02-17 15:09:34 -0800559 if (resource->cacheAccess().isScratch()) {
bsalomon8718aaf2015-02-19 07:24:21 -0800560 SkASSERT(!resource->getUniqueKey().isValid());
bsalomonf320e042015-02-17 15:09:34 -0800561 ++fScratch;
562 SkASSERT(fScratchMap->countForKey(resource->resourcePriv().getScratchKey()));
563 SkASSERT(!resource->cacheAccess().isWrapped());
564 } else if (resource->resourcePriv().getScratchKey().isValid()) {
565 SkASSERT(!resource->resourcePriv().isBudgeted() ||
bsalomon8718aaf2015-02-19 07:24:21 -0800566 resource->getUniqueKey().isValid());
bsalomonf320e042015-02-17 15:09:34 -0800567 ++fCouldBeScratch;
568 SkASSERT(fScratchMap->countForKey(resource->resourcePriv().getScratchKey()));
569 SkASSERT(!resource->cacheAccess().isWrapped());
570 }
bsalomon8718aaf2015-02-19 07:24:21 -0800571 const GrUniqueKey& uniqueKey = resource->getUniqueKey();
572 if (uniqueKey.isValid()) {
bsalomonf320e042015-02-17 15:09:34 -0800573 ++fContent;
bsalomon8718aaf2015-02-19 07:24:21 -0800574 SkASSERT(fUniqueHash->find(uniqueKey) == resource);
bsalomonf320e042015-02-17 15:09:34 -0800575 SkASSERT(!resource->cacheAccess().isWrapped());
576 SkASSERT(resource->resourcePriv().isBudgeted());
577 }
578
579 if (resource->resourcePriv().isBudgeted()) {
580 ++fBudgetedCount;
581 fBudgetedBytes += resource->gpuMemorySize();
582 }
bsalomon9f2d1572015-02-17 11:47:40 -0800583 }
bsalomonf320e042015-02-17 15:09:34 -0800584 };
585
586 Stats stats(this);
587
588 for (int i = 0; i < fNonpurgeableResources.count(); ++i) {
589 SkASSERT(!fNonpurgeableResources[i]->isPurgeable());
590 SkASSERT(*fNonpurgeableResources[i]->cacheAccess().accessCacheIndex() == i);
591 SkASSERT(!fNonpurgeableResources[i]->wasDestroyed());
592 stats.update(fNonpurgeableResources[i]);
bsalomon71cb0c22014-11-14 12:10:14 -0800593 }
bsalomon9f2d1572015-02-17 11:47:40 -0800594 for (int i = 0; i < fPurgeableQueue.count(); ++i) {
595 SkASSERT(fPurgeableQueue.at(i)->isPurgeable());
bsalomonf320e042015-02-17 15:09:34 -0800596 SkASSERT(*fPurgeableQueue.at(i)->cacheAccess().accessCacheIndex() == i);
597 SkASSERT(!fPurgeableQueue.at(i)->wasDestroyed());
598 stats.update(fPurgeableQueue.at(i));
bsalomon9f2d1572015-02-17 11:47:40 -0800599 }
600
bsalomonf320e042015-02-17 15:09:34 -0800601 SkASSERT(fCount == this->getResourceCount());
bsalomondace19e2014-11-17 07:34:06 -0800602 SkASSERT(fBudgetedCount <= fCount);
bsalomonf320e042015-02-17 15:09:34 -0800603 SkASSERT(fBudgetedBytes <= fBytes);
604 SkASSERT(stats.fBytes == fBytes);
605 SkASSERT(stats.fBudgetedBytes == fBudgetedBytes);
606 SkASSERT(stats.fBudgetedCount == fBudgetedCount);
bsalomon71cb0c22014-11-14 12:10:14 -0800607#if GR_CACHE_STATS
bsalomondace19e2014-11-17 07:34:06 -0800608 SkASSERT(fBudgetedHighWaterCount <= fHighWaterCount);
609 SkASSERT(fBudgetedHighWaterBytes <= fHighWaterBytes);
bsalomonf320e042015-02-17 15:09:34 -0800610 SkASSERT(fBytes <= fHighWaterBytes);
611 SkASSERT(fCount <= fHighWaterCount);
612 SkASSERT(fBudgetedBytes <= fBudgetedHighWaterBytes);
613 SkASSERT(fBudgetedCount <= fBudgetedHighWaterCount);
bsalomon71cb0c22014-11-14 12:10:14 -0800614#endif
bsalomon8718aaf2015-02-19 07:24:21 -0800615 SkASSERT(stats.fContent == fUniqueHash.count());
bsalomonf320e042015-02-17 15:09:34 -0800616 SkASSERT(stats.fScratch + stats.fCouldBeScratch == fScratchMap.count());
bsalomon71cb0c22014-11-14 12:10:14 -0800617
bsalomon63c992f2015-01-23 12:47:59 -0800618 // This assertion is not currently valid because we can be in recursive notifyIsPurgeable()
bsalomon12299ab2014-11-14 13:33:09 -0800619 // calls. This will be fixed when subresource registration is explicit.
bsalomondace19e2014-11-17 07:34:06 -0800620 // bool overBudget = budgetedBytes > fMaxBytes || budgetedCount > fMaxCount;
bsalomon12299ab2014-11-14 13:33:09 -0800621 // SkASSERT(!overBudget || locked == count || fPurging);
bsalomon71cb0c22014-11-14 12:10:14 -0800622}
bsalomonf320e042015-02-17 15:09:34 -0800623
624bool GrResourceCache::isInCache(const GrGpuResource* resource) const {
625 int index = *resource->cacheAccess().accessCacheIndex();
626 if (index < 0) {
627 return false;
628 }
629 if (index < fPurgeableQueue.count() && fPurgeableQueue.at(index) == resource) {
630 return true;
631 }
632 if (index < fNonpurgeableResources.count() && fNonpurgeableResources[index] == resource) {
633 return true;
634 }
635 SkDEBUGFAIL("Resource index should be -1 or the resource should be in the cache.");
636 return false;
637}
638
bsalomon71cb0c22014-11-14 12:10:14 -0800639#endif