blob: 596af6d623a3bacf4f45cf69a0e5831e0718a71a [file] [log] [blame]
bsalomonc8dc1f72014-08-21 13:02:13 -07001/*
2 * Copyright 2014 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8
bsalomon0ea80f42015-02-11 10:49:59 -08009#include "GrResourceCache.h"
robertphillips28a838e2016-06-23 14:07:00 -070010
11#include "GrCaps.h"
bsalomon3582d3e2015-02-13 14:20:05 -080012#include "GrGpuResourceCacheAccess.h"
hendrikw876c3132015-03-04 10:33:49 -080013#include "GrTracing.h"
bsalomon71cb0c22014-11-14 12:10:14 -080014#include "SkGr.h"
15#include "SkMessageBus.h"
mtklein4e976072016-08-08 09:06:27 -070016#include "SkOpts.h"
bsalomonddf30e62015-02-19 11:38:44 -080017#include "SkTSort.h"
bsalomon71cb0c22014-11-14 12:10:14 -080018
bsalomon8718aaf2015-02-19 07:24:21 -080019DECLARE_SKMESSAGEBUS_MESSAGE(GrUniqueKeyInvalidatedMessage);
bsalomon71cb0c22014-11-14 12:10:14 -080020
21//////////////////////////////////////////////////////////////////////////////
22
bsalomon7775c852014-12-30 12:50:52 -080023GrScratchKey::ResourceType GrScratchKey::GenerateResourceType() {
bsalomon24db3b12015-01-23 04:24:04 -080024 static int32_t gType = INHERITED::kInvalidDomain + 1;
bsalomonfe369ee2014-11-10 11:59:06 -080025
bsalomon7775c852014-12-30 12:50:52 -080026 int32_t type = sk_atomic_inc(&gType);
robertphillips9790a7b2015-01-05 12:29:15 -080027 if (type > SK_MaxU16) {
bsalomon71cb0c22014-11-14 12:10:14 -080028 SkFAIL("Too many Resource Types");
29 }
30
31 return static_cast<ResourceType>(type);
32}
33
bsalomon8718aaf2015-02-19 07:24:21 -080034GrUniqueKey::Domain GrUniqueKey::GenerateDomain() {
bsalomon24db3b12015-01-23 04:24:04 -080035 static int32_t gDomain = INHERITED::kInvalidDomain + 1;
bsalomon7775c852014-12-30 12:50:52 -080036
bsalomon24db3b12015-01-23 04:24:04 -080037 int32_t domain = sk_atomic_inc(&gDomain);
kkinnunen016dffb2015-01-23 06:43:05 -080038 if (domain > SK_MaxU16) {
bsalomon8718aaf2015-02-19 07:24:21 -080039 SkFAIL("Too many GrUniqueKey Domains");
bsalomon7775c852014-12-30 12:50:52 -080040 }
bsalomon24db3b12015-01-23 04:24:04 -080041
42 return static_cast<Domain>(domain);
43}
bsalomon3f324322015-04-08 11:01:54 -070044
bsalomon24db3b12015-01-23 04:24:04 -080045uint32_t GrResourceKeyHash(const uint32_t* data, size_t size) {
mtklein4e976072016-08-08 09:06:27 -070046 return SkOpts::hash(data, size);
bsalomon7775c852014-12-30 12:50:52 -080047}
48
bsalomonfe369ee2014-11-10 11:59:06 -080049//////////////////////////////////////////////////////////////////////////////
50
bsalomon0ea80f42015-02-11 10:49:59 -080051class GrResourceCache::AutoValidate : ::SkNoncopyable {
bsalomon71cb0c22014-11-14 12:10:14 -080052public:
bsalomon0ea80f42015-02-11 10:49:59 -080053 AutoValidate(GrResourceCache* cache) : fCache(cache) { cache->validate(); }
bsalomon71cb0c22014-11-14 12:10:14 -080054 ~AutoValidate() { fCache->validate(); }
55private:
bsalomon0ea80f42015-02-11 10:49:59 -080056 GrResourceCache* fCache;
bsalomon71cb0c22014-11-14 12:10:14 -080057};
58
59 //////////////////////////////////////////////////////////////////////////////
robertphillipsee843b22016-10-04 05:30:20 -070060
bsalomon71cb0c22014-11-14 12:10:14 -080061
robertphillips63926682015-08-20 09:39:02 -070062GrResourceCache::GrResourceCache(const GrCaps* caps)
bsalomon9f2d1572015-02-17 11:47:40 -080063 : fTimestamp(0)
64 , fMaxCount(kDefaultMaxCount)
bsalomon71cb0c22014-11-14 12:10:14 -080065 , fMaxBytes(kDefaultMaxSize)
bsalomon3f324322015-04-08 11:01:54 -070066 , fMaxUnusedFlushes(kDefaultMaxUnusedFlushes)
bsalomon71cb0c22014-11-14 12:10:14 -080067#if GR_CACHE_STATS
68 , fHighWaterCount(0)
69 , fHighWaterBytes(0)
bsalomondace19e2014-11-17 07:34:06 -080070 , fBudgetedHighWaterCount(0)
71 , fBudgetedHighWaterBytes(0)
bsalomon71cb0c22014-11-14 12:10:14 -080072#endif
bsalomon71cb0c22014-11-14 12:10:14 -080073 , fBytes(0)
bsalomondace19e2014-11-17 07:34:06 -080074 , fBudgetedCount(0)
75 , fBudgetedBytes(0)
robertphillipsee843b22016-10-04 05:30:20 -070076 , fRequestFlush(false)
bsalomone2e87f32016-09-22 12:42:11 -070077 , fExternalFlushCnt(0)
robertphillips63926682015-08-20 09:39:02 -070078 , fPreferVRAMUseOverFlushes(caps->preferVRAMUseOverFlushes()) {
bsalomonf320e042015-02-17 15:09:34 -080079 SkDEBUGCODE(fCount = 0;)
halcanary96fcdcc2015-08-27 07:41:13 -070080 SkDEBUGCODE(fNewlyPurgeableResourceForValidation = nullptr;)
bsalomon71cb0c22014-11-14 12:10:14 -080081}
82
bsalomon0ea80f42015-02-11 10:49:59 -080083GrResourceCache::~GrResourceCache() {
bsalomonc8dc1f72014-08-21 13:02:13 -070084 this->releaseAll();
85}
86
bsalomon3f324322015-04-08 11:01:54 -070087void GrResourceCache::setLimits(int count, size_t bytes, int maxUnusedFlushes) {
bsalomon71cb0c22014-11-14 12:10:14 -080088 fMaxCount = count;
89 fMaxBytes = bytes;
bsalomon3f324322015-04-08 11:01:54 -070090 fMaxUnusedFlushes = maxUnusedFlushes;
bsalomon71cb0c22014-11-14 12:10:14 -080091 this->purgeAsNeeded();
92}
93
bsalomon0ea80f42015-02-11 10:49:59 -080094void GrResourceCache::insertResource(GrGpuResource* resource) {
bsalomon49f085d2014-09-05 13:34:00 -070095 SkASSERT(resource);
bsalomon16961262014-08-26 14:01:07 -070096 SkASSERT(!this->isInCache(resource));
bsalomonf320e042015-02-17 15:09:34 -080097 SkASSERT(!resource->wasDestroyed());
98 SkASSERT(!resource->isPurgeable());
bsalomonddf30e62015-02-19 11:38:44 -080099
100 // We must set the timestamp before adding to the array in case the timestamp wraps and we wind
101 // up iterating over all the resources that already have timestamps.
102 resource->cacheAccess().setTimestamp(this->getNextTimestamp());
103
bsalomonf320e042015-02-17 15:09:34 -0800104 this->addToNonpurgeableArray(resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800105
bsalomondace19e2014-11-17 07:34:06 -0800106 size_t size = resource->gpuMemorySize();
bsalomonf320e042015-02-17 15:09:34 -0800107 SkDEBUGCODE(++fCount;)
bsalomon84c8e622014-11-17 09:33:27 -0800108 fBytes += size;
bsalomon82b1d622014-11-14 13:59:57 -0800109#if GR_CACHE_STATS
bsalomonf320e042015-02-17 15:09:34 -0800110 fHighWaterCount = SkTMax(this->getResourceCount(), fHighWaterCount);
bsalomon82b1d622014-11-14 13:59:57 -0800111 fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes);
112#endif
bsalomon5ec26ae2016-02-25 08:33:02 -0800113 if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
bsalomondace19e2014-11-17 07:34:06 -0800114 ++fBudgetedCount;
115 fBudgetedBytes += size;
hendrikw876c3132015-03-04 10:33:49 -0800116 TRACE_COUNTER2(TRACE_DISABLED_BY_DEFAULT("skia.gpu.cache"), "skia budget", "used",
117 fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
bsalomondace19e2014-11-17 07:34:06 -0800118#if GR_CACHE_STATS
119 fBudgetedHighWaterCount = SkTMax(fBudgetedCount, fBudgetedHighWaterCount);
120 fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
121#endif
122 }
robertphillipsc4ed6842016-05-24 14:17:12 -0700123 if (resource->resourcePriv().getScratchKey().isValid() &&
124 !resource->getUniqueKey().isValid()) {
kkinnunen2e6055b2016-04-22 01:48:29 -0700125 SkASSERT(!resource->resourcePriv().refsWrappedObjects());
bsalomon3582d3e2015-02-13 14:20:05 -0800126 fScratchMap.insert(resource->resourcePriv().getScratchKey(), resource);
bsalomon744998e2014-08-28 09:54:34 -0700127 }
bsalomon9f2d1572015-02-17 11:47:40 -0800128
bsalomon71cb0c22014-11-14 12:10:14 -0800129 this->purgeAsNeeded();
bsalomonc8dc1f72014-08-21 13:02:13 -0700130}
131
bsalomon0ea80f42015-02-11 10:49:59 -0800132void GrResourceCache::removeResource(GrGpuResource* resource) {
bsalomon9f2d1572015-02-17 11:47:40 -0800133 this->validate();
bsalomon16961262014-08-26 14:01:07 -0700134 SkASSERT(this->isInCache(resource));
bsalomondace19e2014-11-17 07:34:06 -0800135
bsalomon9f2d1572015-02-17 11:47:40 -0800136 if (resource->isPurgeable()) {
137 fPurgeableQueue.remove(resource);
bsalomonf320e042015-02-17 15:09:34 -0800138 } else {
139 this->removeFromNonpurgeableArray(resource);
bsalomon9f2d1572015-02-17 11:47:40 -0800140 }
141
bsalomondace19e2014-11-17 07:34:06 -0800142 size_t size = resource->gpuMemorySize();
bsalomonf320e042015-02-17 15:09:34 -0800143 SkDEBUGCODE(--fCount;)
bsalomondace19e2014-11-17 07:34:06 -0800144 fBytes -= size;
bsalomon5ec26ae2016-02-25 08:33:02 -0800145 if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
bsalomondace19e2014-11-17 07:34:06 -0800146 --fBudgetedCount;
147 fBudgetedBytes -= size;
hendrikw876c3132015-03-04 10:33:49 -0800148 TRACE_COUNTER2(TRACE_DISABLED_BY_DEFAULT("skia.gpu.cache"), "skia budget", "used",
149 fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
bsalomondace19e2014-11-17 07:34:06 -0800150 }
151
robertphillipsc4ed6842016-05-24 14:17:12 -0700152 if (resource->resourcePriv().getScratchKey().isValid() &&
153 !resource->getUniqueKey().isValid()) {
bsalomon3582d3e2015-02-13 14:20:05 -0800154 fScratchMap.remove(resource->resourcePriv().getScratchKey(), resource);
bsalomon744998e2014-08-28 09:54:34 -0700155 }
bsalomon8718aaf2015-02-19 07:24:21 -0800156 if (resource->getUniqueKey().isValid()) {
157 fUniqueHash.remove(resource->getUniqueKey());
bsalomon8b79d232014-11-10 10:19:06 -0800158 }
bsalomonb436ed62014-11-17 12:15:56 -0800159 this->validate();
bsalomonc8dc1f72014-08-21 13:02:13 -0700160}
161
bsalomon0ea80f42015-02-11 10:49:59 -0800162void GrResourceCache::abandonAll() {
bsalomon71cb0c22014-11-14 12:10:14 -0800163 AutoValidate av(this);
164
bsalomonf320e042015-02-17 15:09:34 -0800165 while (fNonpurgeableResources.count()) {
166 GrGpuResource* back = *(fNonpurgeableResources.end() - 1);
167 SkASSERT(!back->wasDestroyed());
168 back->cacheAccess().abandon();
bsalomonc8dc1f72014-08-21 13:02:13 -0700169 }
bsalomonf320e042015-02-17 15:09:34 -0800170
171 while (fPurgeableQueue.count()) {
172 GrGpuResource* top = fPurgeableQueue.peek();
173 SkASSERT(!top->wasDestroyed());
174 top->cacheAccess().abandon();
175 }
176
bsalomon744998e2014-08-28 09:54:34 -0700177 SkASSERT(!fScratchMap.count());
bsalomon8718aaf2015-02-19 07:24:21 -0800178 SkASSERT(!fUniqueHash.count());
bsalomonc8dc1f72014-08-21 13:02:13 -0700179 SkASSERT(!fCount);
bsalomonf320e042015-02-17 15:09:34 -0800180 SkASSERT(!this->getResourceCount());
bsalomondace19e2014-11-17 07:34:06 -0800181 SkASSERT(!fBytes);
182 SkASSERT(!fBudgetedCount);
183 SkASSERT(!fBudgetedBytes);
bsalomonc8dc1f72014-08-21 13:02:13 -0700184}
185
bsalomon0ea80f42015-02-11 10:49:59 -0800186void GrResourceCache::releaseAll() {
bsalomon71cb0c22014-11-14 12:10:14 -0800187 AutoValidate av(this);
188
bsalomonf320e042015-02-17 15:09:34 -0800189 while(fNonpurgeableResources.count()) {
190 GrGpuResource* back = *(fNonpurgeableResources.end() - 1);
191 SkASSERT(!back->wasDestroyed());
192 back->cacheAccess().release();
bsalomonc8dc1f72014-08-21 13:02:13 -0700193 }
bsalomonf320e042015-02-17 15:09:34 -0800194
195 while (fPurgeableQueue.count()) {
196 GrGpuResource* top = fPurgeableQueue.peek();
197 SkASSERT(!top->wasDestroyed());
198 top->cacheAccess().release();
199 }
200
bsalomon744998e2014-08-28 09:54:34 -0700201 SkASSERT(!fScratchMap.count());
bsalomon8718aaf2015-02-19 07:24:21 -0800202 SkASSERT(!fUniqueHash.count());
bsalomonc8dc1f72014-08-21 13:02:13 -0700203 SkASSERT(!fCount);
bsalomonf320e042015-02-17 15:09:34 -0800204 SkASSERT(!this->getResourceCount());
bsalomondace19e2014-11-17 07:34:06 -0800205 SkASSERT(!fBytes);
206 SkASSERT(!fBudgetedCount);
207 SkASSERT(!fBudgetedBytes);
bsalomonc8dc1f72014-08-21 13:02:13 -0700208}
bsalomonbcf0a522014-10-08 08:40:09 -0700209
bsalomon0ea80f42015-02-11 10:49:59 -0800210class GrResourceCache::AvailableForScratchUse {
bsalomonbcf0a522014-10-08 08:40:09 -0700211public:
bsalomon000f8292014-10-15 19:04:14 -0700212 AvailableForScratchUse(bool rejectPendingIO) : fRejectPendingIO(rejectPendingIO) { }
bsalomonbcf0a522014-10-08 08:40:09 -0700213
214 bool operator()(const GrGpuResource* resource) const {
robertphillipsc4ed6842016-05-24 14:17:12 -0700215 SkASSERT(!resource->getUniqueKey().isValid() &&
216 resource->resourcePriv().getScratchKey().isValid());
bsalomon12299ab2014-11-14 13:33:09 -0800217 if (resource->internalHasRef() || !resource->cacheAccess().isScratch()) {
bsalomon000f8292014-10-15 19:04:14 -0700218 return false;
bsalomonbcf0a522014-10-08 08:40:09 -0700219 }
bsalomon000f8292014-10-15 19:04:14 -0700220 return !fRejectPendingIO || !resource->internalHasPendingIO();
bsalomonbcf0a522014-10-08 08:40:09 -0700221 }
bsalomon1e2530b2014-10-09 09:57:18 -0700222
bsalomonbcf0a522014-10-08 08:40:09 -0700223private:
bsalomon000f8292014-10-15 19:04:14 -0700224 bool fRejectPendingIO;
bsalomonbcf0a522014-10-08 08:40:09 -0700225};
226
bsalomon0ea80f42015-02-11 10:49:59 -0800227GrGpuResource* GrResourceCache::findAndRefScratchResource(const GrScratchKey& scratchKey,
robertphillips6e83ac72015-08-13 05:19:14 -0700228 size_t resourceSize,
bsalomon9f2d1572015-02-17 11:47:40 -0800229 uint32_t flags) {
bsalomon7775c852014-12-30 12:50:52 -0800230 SkASSERT(scratchKey.isValid());
robertphillipsee843b22016-10-04 05:30:20 -0700231
bsalomon71cb0c22014-11-14 12:10:14 -0800232 GrGpuResource* resource;
bsalomon000f8292014-10-15 19:04:14 -0700233 if (flags & (kPreferNoPendingIO_ScratchFlag | kRequireNoPendingIO_ScratchFlag)) {
bsalomon71cb0c22014-11-14 12:10:14 -0800234 resource = fScratchMap.find(scratchKey, AvailableForScratchUse(true));
bsalomon000f8292014-10-15 19:04:14 -0700235 if (resource) {
bsalomon9f2d1572015-02-17 11:47:40 -0800236 this->refAndMakeResourceMRU(resource);
bsalomonb436ed62014-11-17 12:15:56 -0800237 this->validate();
238 return resource;
bsalomon000f8292014-10-15 19:04:14 -0700239 } else if (flags & kRequireNoPendingIO_ScratchFlag) {
halcanary96fcdcc2015-08-27 07:41:13 -0700240 return nullptr;
bsalomon000f8292014-10-15 19:04:14 -0700241 }
robertphillips63926682015-08-20 09:39:02 -0700242 // We would prefer to consume more available VRAM rather than flushing
243 // immediately, but on ANGLE this can lead to starving of the GPU.
244 if (fPreferVRAMUseOverFlushes && this->wouldFit(resourceSize)) {
robertphillips6e83ac72015-08-13 05:19:14 -0700245 // kPrefer is specified, we didn't find a resource without pending io,
robertphillips63926682015-08-20 09:39:02 -0700246 // but there is still space in our budget for the resource so force
247 // the caller to allocate a new resource.
halcanary96fcdcc2015-08-27 07:41:13 -0700248 return nullptr;
robertphillips6e83ac72015-08-13 05:19:14 -0700249 }
bsalomon000f8292014-10-15 19:04:14 -0700250 }
bsalomon71cb0c22014-11-14 12:10:14 -0800251 resource = fScratchMap.find(scratchKey, AvailableForScratchUse(false));
252 if (resource) {
bsalomon9f2d1572015-02-17 11:47:40 -0800253 this->refAndMakeResourceMRU(resource);
bsalomonb436ed62014-11-17 12:15:56 -0800254 this->validate();
bsalomon71cb0c22014-11-14 12:10:14 -0800255 }
256 return resource;
bsalomonbcf0a522014-10-08 08:40:09 -0700257}
bsalomon8b79d232014-11-10 10:19:06 -0800258
bsalomon0ea80f42015-02-11 10:49:59 -0800259void GrResourceCache::willRemoveScratchKey(const GrGpuResource* resource) {
bsalomon3582d3e2015-02-13 14:20:05 -0800260 SkASSERT(resource->resourcePriv().getScratchKey().isValid());
robertphillipsc4ed6842016-05-24 14:17:12 -0700261 if (!resource->getUniqueKey().isValid()) {
262 fScratchMap.remove(resource->resourcePriv().getScratchKey(), resource);
263 }
bsalomon10e23ca2014-11-25 05:52:06 -0800264}
265
bsalomonf99e9612015-02-19 08:24:16 -0800266void GrResourceCache::removeUniqueKey(GrGpuResource* resource) {
bsalomon3f324322015-04-08 11:01:54 -0700267 // Someone has a ref to this resource in order to have removed the key. When the ref count
268 // reaches zero we will get a ref cnt notification and figure out what to do with it.
bsalomonf99e9612015-02-19 08:24:16 -0800269 if (resource->getUniqueKey().isValid()) {
270 SkASSERT(resource == fUniqueHash.find(resource->getUniqueKey()));
271 fUniqueHash.remove(resource->getUniqueKey());
272 }
273 resource->cacheAccess().removeUniqueKey();
robertphillipsc4ed6842016-05-24 14:17:12 -0700274
275 if (resource->resourcePriv().getScratchKey().isValid()) {
276 fScratchMap.insert(resource->resourcePriv().getScratchKey(), resource);
277 }
278
bsalomonf99e9612015-02-19 08:24:16 -0800279 this->validate();
bsalomon23e619c2015-02-06 11:54:28 -0800280}
281
bsalomonf99e9612015-02-19 08:24:16 -0800282void GrResourceCache::changeUniqueKey(GrGpuResource* resource, const GrUniqueKey& newKey) {
bsalomon8b79d232014-11-10 10:19:06 -0800283 SkASSERT(resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800284 SkASSERT(this->isInCache(resource));
bsalomon8b79d232014-11-10 10:19:06 -0800285
bsalomonf99e9612015-02-19 08:24:16 -0800286 // If another resource has the new key, remove its key then install the key on this resource.
287 if (newKey.isValid()) {
robertphillipsc4ed6842016-05-24 14:17:12 -0700288 // Remove the entry for this resource if it already has a unique key.
289 if (resource->getUniqueKey().isValid()) {
290 SkASSERT(resource == fUniqueHash.find(resource->getUniqueKey()));
291 fUniqueHash.remove(resource->getUniqueKey());
292 SkASSERT(nullptr == fUniqueHash.find(resource->getUniqueKey()));
293 } else {
294 // 'resource' didn't have a valid unique key before so it is switching sides. Remove it
295 // from the ScratchMap
296 if (resource->resourcePriv().getScratchKey().isValid()) {
297 fScratchMap.remove(resource->resourcePriv().getScratchKey(), resource);
298 }
299 }
300
bsalomonf99e9612015-02-19 08:24:16 -0800301 if (GrGpuResource* old = fUniqueHash.find(newKey)) {
302 // If the old resource using the key is purgeable and is unreachable, then remove it.
303 if (!old->resourcePriv().getScratchKey().isValid() && old->isPurgeable()) {
304 // release may call validate() which will assert that resource is in fUniqueHash
305 // if it has a valid key. So in debug reset the key here before we assign it.
306 SkDEBUGCODE(resource->cacheAccess().removeUniqueKey();)
307 old->cacheAccess().release();
308 } else {
robertphillipsc4ed6842016-05-24 14:17:12 -0700309 this->removeUniqueKey(old);
bsalomonf99e9612015-02-19 08:24:16 -0800310 }
311 }
halcanary96fcdcc2015-08-27 07:41:13 -0700312 SkASSERT(nullptr == fUniqueHash.find(newKey));
bsalomonf99e9612015-02-19 08:24:16 -0800313 resource->cacheAccess().setUniqueKey(newKey);
314 fUniqueHash.add(resource);
315 } else {
robertphillipsc4ed6842016-05-24 14:17:12 -0700316 this->removeUniqueKey(resource);
bsalomonf99e9612015-02-19 08:24:16 -0800317 }
318
bsalomon71cb0c22014-11-14 12:10:14 -0800319 this->validate();
bsalomon8b79d232014-11-10 10:19:06 -0800320}
bsalomon71cb0c22014-11-14 12:10:14 -0800321
bsalomon9f2d1572015-02-17 11:47:40 -0800322void GrResourceCache::refAndMakeResourceMRU(GrGpuResource* resource) {
bsalomon71cb0c22014-11-14 12:10:14 -0800323 SkASSERT(resource);
324 SkASSERT(this->isInCache(resource));
bsalomonddf30e62015-02-19 11:38:44 -0800325
bsalomon9f2d1572015-02-17 11:47:40 -0800326 if (resource->isPurgeable()) {
327 // It's about to become unpurgeable.
328 fPurgeableQueue.remove(resource);
bsalomonf320e042015-02-17 15:09:34 -0800329 this->addToNonpurgeableArray(resource);
bsalomon9f2d1572015-02-17 11:47:40 -0800330 }
331 resource->ref();
bsalomonddf30e62015-02-19 11:38:44 -0800332
333 resource->cacheAccess().setTimestamp(this->getNextTimestamp());
bsalomonf320e042015-02-17 15:09:34 -0800334 this->validate();
bsalomon71cb0c22014-11-14 12:10:14 -0800335}
336
bsalomon3f324322015-04-08 11:01:54 -0700337void GrResourceCache::notifyCntReachedZero(GrGpuResource* resource, uint32_t flags) {
bsalomon71cb0c22014-11-14 12:10:14 -0800338 SkASSERT(resource);
bsalomon3f324322015-04-08 11:01:54 -0700339 SkASSERT(!resource->wasDestroyed());
340 SkASSERT(flags);
bsalomon71cb0c22014-11-14 12:10:14 -0800341 SkASSERT(this->isInCache(resource));
bsalomon3f324322015-04-08 11:01:54 -0700342 // This resource should always be in the nonpurgeable array when this function is called. It
343 // will be moved to the queue if it is newly purgeable.
344 SkASSERT(fNonpurgeableResources[*resource->cacheAccess().accessCacheIndex()] == resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800345
bsalomon3f324322015-04-08 11:01:54 -0700346 if (SkToBool(ResourceAccess::kRefCntReachedZero_RefNotificationFlag & flags)) {
347#ifdef SK_DEBUG
348 // When the timestamp overflows validate() is called. validate() checks that resources in
349 // the nonpurgeable array are indeed not purgeable. However, the movement from the array to
350 // the purgeable queue happens just below in this function. So we mark it as an exception.
351 if (resource->isPurgeable()) {
352 fNewlyPurgeableResourceForValidation = resource;
353 }
354#endif
355 resource->cacheAccess().setTimestamp(this->getNextTimestamp());
halcanary96fcdcc2015-08-27 07:41:13 -0700356 SkDEBUGCODE(fNewlyPurgeableResourceForValidation = nullptr);
bsalomon3f324322015-04-08 11:01:54 -0700357 }
358
359 if (!SkToBool(ResourceAccess::kAllCntsReachedZero_RefNotificationFlag & flags)) {
360 SkASSERT(!resource->isPurgeable());
361 return;
362 }
363
364 SkASSERT(resource->isPurgeable());
bsalomonf320e042015-02-17 15:09:34 -0800365 this->removeFromNonpurgeableArray(resource);
bsalomon9f2d1572015-02-17 11:47:40 -0800366 fPurgeableQueue.insert(resource);
bsalomone2e87f32016-09-22 12:42:11 -0700367 resource->cacheAccess().setFlushCntWhenResourceBecamePurgeable(fExternalFlushCnt);
Brian Salomon5e150852017-03-22 14:53:13 -0400368 resource->cacheAccess().setTimeWhenResourceBecomePurgeable();
bsalomon71cb0c22014-11-14 12:10:14 -0800369
bsalomon5ec26ae2016-02-25 08:33:02 -0800370 if (SkBudgeted::kNo == resource->resourcePriv().isBudgeted()) {
bsalomonc2f35b72015-01-23 07:19:22 -0800371 // Check whether this resource could still be used as a scratch resource.
kkinnunen2e6055b2016-04-22 01:48:29 -0700372 if (!resource->resourcePriv().refsWrappedObjects() &&
bsalomon9f2d1572015-02-17 11:47:40 -0800373 resource->resourcePriv().getScratchKey().isValid()) {
bsalomonc2f35b72015-01-23 07:19:22 -0800374 // We won't purge an existing resource to make room for this one.
bsalomonf320e042015-02-17 15:09:34 -0800375 if (fBudgetedCount < fMaxCount &&
376 fBudgetedBytes + resource->gpuMemorySize() <= fMaxBytes) {
bsalomon3582d3e2015-02-13 14:20:05 -0800377 resource->resourcePriv().makeBudgeted();
bsalomon9f2d1572015-02-17 11:47:40 -0800378 return;
bsalomonc2f35b72015-01-23 07:19:22 -0800379 }
bsalomonc2f35b72015-01-23 07:19:22 -0800380 }
381 } else {
bsalomon9f2d1572015-02-17 11:47:40 -0800382 // Purge the resource immediately if we're over budget
bsalomon8718aaf2015-02-19 07:24:21 -0800383 // Also purge if the resource has neither a valid scratch key nor a unique key.
robertphillipsee843b22016-10-04 05:30:20 -0700384 bool noKey = !resource->resourcePriv().getScratchKey().isValid() &&
385 !resource->getUniqueKey().isValid();
386 if (!this->overBudget() && !noKey) {
bsalomon9f2d1572015-02-17 11:47:40 -0800387 return;
bsalomonc2f35b72015-01-23 07:19:22 -0800388 }
389 }
bsalomondace19e2014-11-17 07:34:06 -0800390
bsalomonf320e042015-02-17 15:09:34 -0800391 SkDEBUGCODE(int beforeCount = this->getResourceCount();)
bsalomon9f2d1572015-02-17 11:47:40 -0800392 resource->cacheAccess().release();
393 // We should at least free this resource, perhaps dependent resources as well.
bsalomonf320e042015-02-17 15:09:34 -0800394 SkASSERT(this->getResourceCount() < beforeCount);
bsalomon71cb0c22014-11-14 12:10:14 -0800395 this->validate();
396}
397
bsalomon0ea80f42015-02-11 10:49:59 -0800398void GrResourceCache::didChangeGpuMemorySize(const GrGpuResource* resource, size_t oldSize) {
bsalomon71cb0c22014-11-14 12:10:14 -0800399 // SkASSERT(!fPurging); GrPathRange increases size during flush. :(
400 SkASSERT(resource);
401 SkASSERT(this->isInCache(resource));
402
bsalomondace19e2014-11-17 07:34:06 -0800403 ptrdiff_t delta = resource->gpuMemorySize() - oldSize;
404
405 fBytes += delta;
bsalomon82b1d622014-11-14 13:59:57 -0800406#if GR_CACHE_STATS
407 fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes);
408#endif
bsalomon5ec26ae2016-02-25 08:33:02 -0800409 if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
bsalomondace19e2014-11-17 07:34:06 -0800410 fBudgetedBytes += delta;
hendrikw876c3132015-03-04 10:33:49 -0800411 TRACE_COUNTER2(TRACE_DISABLED_BY_DEFAULT("skia.gpu.cache"), "skia budget", "used",
412 fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
bsalomondace19e2014-11-17 07:34:06 -0800413#if GR_CACHE_STATS
414 fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
415#endif
416 }
bsalomon71cb0c22014-11-14 12:10:14 -0800417
418 this->purgeAsNeeded();
419 this->validate();
420}
421
bsalomon0ea80f42015-02-11 10:49:59 -0800422void GrResourceCache::didChangeBudgetStatus(GrGpuResource* resource) {
bsalomon84c8e622014-11-17 09:33:27 -0800423 SkASSERT(resource);
424 SkASSERT(this->isInCache(resource));
425
426 size_t size = resource->gpuMemorySize();
427
bsalomon5ec26ae2016-02-25 08:33:02 -0800428 if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
bsalomon84c8e622014-11-17 09:33:27 -0800429 ++fBudgetedCount;
430 fBudgetedBytes += size;
bsalomonafe30052015-01-16 07:32:33 -0800431#if GR_CACHE_STATS
432 fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
433 fBudgetedHighWaterCount = SkTMax(fBudgetedCount, fBudgetedHighWaterCount);
434#endif
bsalomon84c8e622014-11-17 09:33:27 -0800435 this->purgeAsNeeded();
436 } else {
437 --fBudgetedCount;
438 fBudgetedBytes -= size;
439 }
hendrikw876c3132015-03-04 10:33:49 -0800440 TRACE_COUNTER2(TRACE_DISABLED_BY_DEFAULT("skia.gpu.cache"), "skia budget", "used",
441 fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
bsalomon84c8e622014-11-17 09:33:27 -0800442
443 this->validate();
444}
445
robertphillipsee843b22016-10-04 05:30:20 -0700446void GrResourceCache::purgeAsNeeded() {
bsalomon3f324322015-04-08 11:01:54 -0700447 SkTArray<GrUniqueKeyInvalidatedMessage> invalidKeyMsgs;
448 fInvalidUniqueKeyInbox.poll(&invalidKeyMsgs);
449 if (invalidKeyMsgs.count()) {
450 this->processInvalidUniqueKeys(invalidKeyMsgs);
451 }
bsalomon71cb0c22014-11-14 12:10:14 -0800452
bsalomone2e87f32016-09-22 12:42:11 -0700453 if (fMaxUnusedFlushes > 0) {
454 // We want to know how many complete flushes have occurred without the resource being used.
455 // If the resource was tagged when fExternalFlushCnt was N then this means it became
456 // purgeable during activity that became the N+1th flush. So when the flush count is N+2
457 // it has sat in the purgeable queue for one entire flush.
458 uint32_t oldestAllowedFlushCnt = fExternalFlushCnt - fMaxUnusedFlushes - 1;
459 // check for underflow
460 if (oldestAllowedFlushCnt < fExternalFlushCnt) {
461 while (fPurgeableQueue.count()) {
462 uint32_t flushWhenResourceBecamePurgeable =
463 fPurgeableQueue.peek()->cacheAccess().flushCntWhenResourceBecamePurgeable();
464 if (oldestAllowedFlushCnt < flushWhenResourceBecamePurgeable) {
465 // Resources were given both LRU timestamps and tagged with a flush cnt when
466 // they first became purgeable. The LRU timestamp won't change again until the
467 // resource is made non-purgeable again. So, at this point all the remaining
468 // resources in the timestamp-sorted queue will have a flush count >= to this
469 // one.
470 break;
471 }
472 GrGpuResource* resource = fPurgeableQueue.peek();
473 SkASSERT(resource->isPurgeable());
474 resource->cacheAccess().release();
bsalomon3f324322015-04-08 11:01:54 -0700475 }
bsalomon3f324322015-04-08 11:01:54 -0700476 }
477 }
478
479 bool stillOverbudget = this->overBudget();
480 while (stillOverbudget && fPurgeableQueue.count()) {
robertphillipsee843b22016-10-04 05:30:20 -0700481 GrGpuResource* resource = fPurgeableQueue.peek();
bsalomon9f2d1572015-02-17 11:47:40 -0800482 SkASSERT(resource->isPurgeable());
483 resource->cacheAccess().release();
bsalomon3f324322015-04-08 11:01:54 -0700484 stillOverbudget = this->overBudget();
bsalomon9f2d1572015-02-17 11:47:40 -0800485 }
bsalomon71cb0c22014-11-14 12:10:14 -0800486
bsalomonb436ed62014-11-17 12:15:56 -0800487 this->validate();
robertphillipsee843b22016-10-04 05:30:20 -0700488
489 if (stillOverbudget) {
490 // Set this so that GrDrawingManager will issue a flush to free up resources with pending
491 // IO that we were unable to purge in this pass.
492 fRequestFlush = true;
493 }
bsalomon71cb0c22014-11-14 12:10:14 -0800494}
495
bsalomon0ea80f42015-02-11 10:49:59 -0800496void GrResourceCache::purgeAllUnlocked() {
bsalomon9f2d1572015-02-17 11:47:40 -0800497 // We could disable maintaining the heap property here, but it would add a lot of complexity.
498 // Moreover, this is rarely called.
499 while (fPurgeableQueue.count()) {
500 GrGpuResource* resource = fPurgeableQueue.peek();
501 SkASSERT(resource->isPurgeable());
502 resource->cacheAccess().release();
503 }
bsalomon71cb0c22014-11-14 12:10:14 -0800504
bsalomonb436ed62014-11-17 12:15:56 -0800505 this->validate();
bsalomon71cb0c22014-11-14 12:10:14 -0800506}
507
Brian Salomon5e150852017-03-22 14:53:13 -0400508void GrResourceCache::purgeResourcesNotUsedSince(GrStdSteadyClock::time_point purgeTime) {
509 while (fPurgeableQueue.count()) {
510 const GrStdSteadyClock::time_point resourceTime =
511 fPurgeableQueue.peek()->cacheAccess().timeWhenResourceBecamePurgeable();
512 if (resourceTime >= purgeTime) {
513 // Resources were given both LRU timestamps and tagged with a frame number when
514 // they first became purgeable. The LRU timestamp won't change again until the
515 // resource is made non-purgeable again. So, at this point all the remaining
516 // resources in the timestamp-sorted queue will have a frame number >= to this
517 // one.
518 break;
519 }
520 GrGpuResource* resource = fPurgeableQueue.peek();
521 SkASSERT(resource->isPurgeable());
522 resource->cacheAccess().release();
523 }
524}
525
bsalomon8718aaf2015-02-19 07:24:21 -0800526void GrResourceCache::processInvalidUniqueKeys(
527 const SkTArray<GrUniqueKeyInvalidatedMessage>& msgs) {
bsalomon23e619c2015-02-06 11:54:28 -0800528 for (int i = 0; i < msgs.count(); ++i) {
bsalomon8718aaf2015-02-19 07:24:21 -0800529 GrGpuResource* resource = this->findAndRefUniqueResource(msgs[i].key());
bsalomon23e619c2015-02-06 11:54:28 -0800530 if (resource) {
bsalomon8718aaf2015-02-19 07:24:21 -0800531 resource->resourcePriv().removeUniqueKey();
bsalomon3f324322015-04-08 11:01:54 -0700532 resource->unref(); // If this resource is now purgeable, the cache will be notified.
bsalomon23e619c2015-02-06 11:54:28 -0800533 }
534 }
535}
536
bsalomonf320e042015-02-17 15:09:34 -0800537void GrResourceCache::addToNonpurgeableArray(GrGpuResource* resource) {
538 int index = fNonpurgeableResources.count();
539 *fNonpurgeableResources.append() = resource;
540 *resource->cacheAccess().accessCacheIndex() = index;
541}
542
543void GrResourceCache::removeFromNonpurgeableArray(GrGpuResource* resource) {
544 int* index = resource->cacheAccess().accessCacheIndex();
545 // Fill the whole we will create in the array with the tail object, adjust its index, and
546 // then pop the array
547 GrGpuResource* tail = *(fNonpurgeableResources.end() - 1);
548 SkASSERT(fNonpurgeableResources[*index] == resource);
549 fNonpurgeableResources[*index] = tail;
550 *tail->cacheAccess().accessCacheIndex() = *index;
551 fNonpurgeableResources.pop();
552 SkDEBUGCODE(*index = -1);
553}
554
bsalomonddf30e62015-02-19 11:38:44 -0800555uint32_t GrResourceCache::getNextTimestamp() {
556 // If we wrap then all the existing resources will appear older than any resources that get
557 // a timestamp after the wrap.
558 if (0 == fTimestamp) {
559 int count = this->getResourceCount();
560 if (count) {
561 // Reset all the timestamps. We sort the resources by timestamp and then assign
562 // sequential timestamps beginning with 0. This is O(n*lg(n)) but it should be extremely
563 // rare.
564 SkTDArray<GrGpuResource*> sortedPurgeableResources;
565 sortedPurgeableResources.setReserve(fPurgeableQueue.count());
566
567 while (fPurgeableQueue.count()) {
568 *sortedPurgeableResources.append() = fPurgeableQueue.peek();
569 fPurgeableQueue.pop();
570 }
robertphillipsee843b22016-10-04 05:30:20 -0700571
bsalomone2e87f32016-09-22 12:42:11 -0700572 SkTQSort(fNonpurgeableResources.begin(), fNonpurgeableResources.end() - 1,
573 CompareTimestamp);
bsalomonddf30e62015-02-19 11:38:44 -0800574
575 // Pick resources out of the purgeable and non-purgeable arrays based on lowest
576 // timestamp and assign new timestamps.
577 int currP = 0;
578 int currNP = 0;
579 while (currP < sortedPurgeableResources.count() &&
mtklein56da0252015-11-16 11:16:23 -0800580 currNP < fNonpurgeableResources.count()) {
bsalomonddf30e62015-02-19 11:38:44 -0800581 uint32_t tsP = sortedPurgeableResources[currP]->cacheAccess().timestamp();
582 uint32_t tsNP = fNonpurgeableResources[currNP]->cacheAccess().timestamp();
583 SkASSERT(tsP != tsNP);
584 if (tsP < tsNP) {
585 sortedPurgeableResources[currP++]->cacheAccess().setTimestamp(fTimestamp++);
586 } else {
587 // Correct the index in the nonpurgeable array stored on the resource post-sort.
588 *fNonpurgeableResources[currNP]->cacheAccess().accessCacheIndex() = currNP;
589 fNonpurgeableResources[currNP++]->cacheAccess().setTimestamp(fTimestamp++);
590 }
591 }
592
593 // The above loop ended when we hit the end of one array. Finish the other one.
594 while (currP < sortedPurgeableResources.count()) {
595 sortedPurgeableResources[currP++]->cacheAccess().setTimestamp(fTimestamp++);
596 }
597 while (currNP < fNonpurgeableResources.count()) {
598 *fNonpurgeableResources[currNP]->cacheAccess().accessCacheIndex() = currNP;
599 fNonpurgeableResources[currNP++]->cacheAccess().setTimestamp(fTimestamp++);
600 }
601
602 // Rebuild the queue.
603 for (int i = 0; i < sortedPurgeableResources.count(); ++i) {
604 fPurgeableQueue.insert(sortedPurgeableResources[i]);
605 }
606
607 this->validate();
608 SkASSERT(count == this->getResourceCount());
609
610 // count should be the next timestamp we return.
611 SkASSERT(fTimestamp == SkToU32(count));
mtklein56da0252015-11-16 11:16:23 -0800612 }
bsalomonddf30e62015-02-19 11:38:44 -0800613 }
614 return fTimestamp++;
615}
616
bsalomonb77a9072016-09-07 10:02:04 -0700617void GrResourceCache::notifyFlushOccurred(FlushType type) {
618 switch (type) {
619 case FlushType::kImmediateMode:
620 break;
621 case FlushType::kCacheRequested:
robertphillipsee843b22016-10-04 05:30:20 -0700622 SkASSERT(fRequestFlush);
623 fRequestFlush = false;
bsalomonb77a9072016-09-07 10:02:04 -0700624 break;
robertphillipsee843b22016-10-04 05:30:20 -0700625 case FlushType::kExternal:
bsalomone2e87f32016-09-22 12:42:11 -0700626 ++fExternalFlushCnt;
627 if (0 == fExternalFlushCnt) {
628 // When this wraps just reset all the purgeable resources' last used flush state.
629 for (int i = 0; i < fPurgeableQueue.count(); ++i) {
630 fPurgeableQueue.at(i)->cacheAccess().setFlushCntWhenResourceBecamePurgeable(0);
631 }
bsalomonb77a9072016-09-07 10:02:04 -0700632 }
633 break;
bsalomon3f324322015-04-08 11:01:54 -0700634 }
robertphillipsee843b22016-10-04 05:30:20 -0700635 this->purgeAsNeeded();
bsalomon3f324322015-04-08 11:01:54 -0700636}
637
ericrk0a5fa482015-09-15 14:16:10 -0700638void GrResourceCache::dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const {
639 for (int i = 0; i < fNonpurgeableResources.count(); ++i) {
640 fNonpurgeableResources[i]->dumpMemoryStatistics(traceMemoryDump);
641 }
642 for (int i = 0; i < fPurgeableQueue.count(); ++i) {
643 fPurgeableQueue.at(i)->dumpMemoryStatistics(traceMemoryDump);
644 }
645}
646
bsalomon71cb0c22014-11-14 12:10:14 -0800647#ifdef SK_DEBUG
bsalomon0ea80f42015-02-11 10:49:59 -0800648void GrResourceCache::validate() const {
bsalomonc2f35b72015-01-23 07:19:22 -0800649 // Reduce the frequency of validations for large resource counts.
650 static SkRandom gRandom;
651 int mask = (SkNextPow2(fCount + 1) >> 5) - 1;
652 if (~mask && (gRandom.nextU() & mask)) {
653 return;
654 }
655
bsalomonf320e042015-02-17 15:09:34 -0800656 struct Stats {
657 size_t fBytes;
658 int fBudgetedCount;
659 size_t fBudgetedBytes;
660 int fLocked;
661 int fScratch;
662 int fCouldBeScratch;
663 int fContent;
664 const ScratchMap* fScratchMap;
bsalomon8718aaf2015-02-19 07:24:21 -0800665 const UniqueHash* fUniqueHash;
bsalomon71cb0c22014-11-14 12:10:14 -0800666
bsalomonf320e042015-02-17 15:09:34 -0800667 Stats(const GrResourceCache* cache) {
668 memset(this, 0, sizeof(*this));
669 fScratchMap = &cache->fScratchMap;
bsalomon8718aaf2015-02-19 07:24:21 -0800670 fUniqueHash = &cache->fUniqueHash;
bsalomon71cb0c22014-11-14 12:10:14 -0800671 }
672
bsalomonf320e042015-02-17 15:09:34 -0800673 void update(GrGpuResource* resource) {
674 fBytes += resource->gpuMemorySize();
bsalomondace19e2014-11-17 07:34:06 -0800675
bsalomonf320e042015-02-17 15:09:34 -0800676 if (!resource->isPurgeable()) {
677 ++fLocked;
678 }
bsalomon9f2d1572015-02-17 11:47:40 -0800679
robertphillipsc4ed6842016-05-24 14:17:12 -0700680 const GrScratchKey& scratchKey = resource->resourcePriv().getScratchKey();
681 const GrUniqueKey& uniqueKey = resource->getUniqueKey();
682
bsalomonf320e042015-02-17 15:09:34 -0800683 if (resource->cacheAccess().isScratch()) {
robertphillipsc4ed6842016-05-24 14:17:12 -0700684 SkASSERT(!uniqueKey.isValid());
bsalomonf320e042015-02-17 15:09:34 -0800685 ++fScratch;
robertphillipsc4ed6842016-05-24 14:17:12 -0700686 SkASSERT(fScratchMap->countForKey(scratchKey));
kkinnunen2e6055b2016-04-22 01:48:29 -0700687 SkASSERT(!resource->resourcePriv().refsWrappedObjects());
robertphillipsc4ed6842016-05-24 14:17:12 -0700688 } else if (scratchKey.isValid()) {
bsalomon5ec26ae2016-02-25 08:33:02 -0800689 SkASSERT(SkBudgeted::kNo == resource->resourcePriv().isBudgeted() ||
robertphillipsc4ed6842016-05-24 14:17:12 -0700690 uniqueKey.isValid());
691 if (!uniqueKey.isValid()) {
mtklein4e976072016-08-08 09:06:27 -0700692 ++fCouldBeScratch;
robertphillipsc4ed6842016-05-24 14:17:12 -0700693 SkASSERT(fScratchMap->countForKey(scratchKey));
694 }
kkinnunen2e6055b2016-04-22 01:48:29 -0700695 SkASSERT(!resource->resourcePriv().refsWrappedObjects());
bsalomonf320e042015-02-17 15:09:34 -0800696 }
bsalomon8718aaf2015-02-19 07:24:21 -0800697 if (uniqueKey.isValid()) {
bsalomonf320e042015-02-17 15:09:34 -0800698 ++fContent;
bsalomon8718aaf2015-02-19 07:24:21 -0800699 SkASSERT(fUniqueHash->find(uniqueKey) == resource);
kkinnunen2e6055b2016-04-22 01:48:29 -0700700 SkASSERT(!resource->resourcePriv().refsWrappedObjects());
bsalomon5ec26ae2016-02-25 08:33:02 -0800701 SkASSERT(SkBudgeted::kYes == resource->resourcePriv().isBudgeted());
robertphillipsc4ed6842016-05-24 14:17:12 -0700702
703 if (scratchKey.isValid()) {
704 SkASSERT(!fScratchMap->has(resource, scratchKey));
705 }
bsalomonf320e042015-02-17 15:09:34 -0800706 }
707
bsalomon5ec26ae2016-02-25 08:33:02 -0800708 if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
bsalomonf320e042015-02-17 15:09:34 -0800709 ++fBudgetedCount;
710 fBudgetedBytes += resource->gpuMemorySize();
711 }
bsalomon9f2d1572015-02-17 11:47:40 -0800712 }
bsalomonf320e042015-02-17 15:09:34 -0800713 };
714
robertphillipsc4ed6842016-05-24 14:17:12 -0700715 {
716 ScratchMap::ConstIter iter(&fScratchMap);
717
718 int count = 0;
719 for ( ; !iter.done(); ++iter) {
720 const GrGpuResource* resource = *iter;
721 SkASSERT(resource->resourcePriv().getScratchKey().isValid());
722 SkASSERT(!resource->getUniqueKey().isValid());
723 count++;
724 }
725 SkASSERT(count == fScratchMap.count()); // ensure the iterator is working correctly
726 }
727
bsalomonf320e042015-02-17 15:09:34 -0800728 Stats stats(this);
729
730 for (int i = 0; i < fNonpurgeableResources.count(); ++i) {
bsalomon3f324322015-04-08 11:01:54 -0700731 SkASSERT(!fNonpurgeableResources[i]->isPurgeable() ||
732 fNewlyPurgeableResourceForValidation == fNonpurgeableResources[i]);
bsalomonf320e042015-02-17 15:09:34 -0800733 SkASSERT(*fNonpurgeableResources[i]->cacheAccess().accessCacheIndex() == i);
734 SkASSERT(!fNonpurgeableResources[i]->wasDestroyed());
735 stats.update(fNonpurgeableResources[i]);
bsalomon71cb0c22014-11-14 12:10:14 -0800736 }
bsalomon9f2d1572015-02-17 11:47:40 -0800737 for (int i = 0; i < fPurgeableQueue.count(); ++i) {
738 SkASSERT(fPurgeableQueue.at(i)->isPurgeable());
bsalomonf320e042015-02-17 15:09:34 -0800739 SkASSERT(*fPurgeableQueue.at(i)->cacheAccess().accessCacheIndex() == i);
740 SkASSERT(!fPurgeableQueue.at(i)->wasDestroyed());
741 stats.update(fPurgeableQueue.at(i));
bsalomon9f2d1572015-02-17 11:47:40 -0800742 }
743
bsalomonf320e042015-02-17 15:09:34 -0800744 SkASSERT(fCount == this->getResourceCount());
bsalomondace19e2014-11-17 07:34:06 -0800745 SkASSERT(fBudgetedCount <= fCount);
bsalomonf320e042015-02-17 15:09:34 -0800746 SkASSERT(fBudgetedBytes <= fBytes);
747 SkASSERT(stats.fBytes == fBytes);
748 SkASSERT(stats.fBudgetedBytes == fBudgetedBytes);
749 SkASSERT(stats.fBudgetedCount == fBudgetedCount);
bsalomon71cb0c22014-11-14 12:10:14 -0800750#if GR_CACHE_STATS
bsalomondace19e2014-11-17 07:34:06 -0800751 SkASSERT(fBudgetedHighWaterCount <= fHighWaterCount);
752 SkASSERT(fBudgetedHighWaterBytes <= fHighWaterBytes);
bsalomonf320e042015-02-17 15:09:34 -0800753 SkASSERT(fBytes <= fHighWaterBytes);
754 SkASSERT(fCount <= fHighWaterCount);
755 SkASSERT(fBudgetedBytes <= fBudgetedHighWaterBytes);
756 SkASSERT(fBudgetedCount <= fBudgetedHighWaterCount);
bsalomon71cb0c22014-11-14 12:10:14 -0800757#endif
bsalomon8718aaf2015-02-19 07:24:21 -0800758 SkASSERT(stats.fContent == fUniqueHash.count());
bsalomonf320e042015-02-17 15:09:34 -0800759 SkASSERT(stats.fScratch + stats.fCouldBeScratch == fScratchMap.count());
bsalomon71cb0c22014-11-14 12:10:14 -0800760
bsalomon3f324322015-04-08 11:01:54 -0700761 // This assertion is not currently valid because we can be in recursive notifyCntReachedZero()
bsalomon12299ab2014-11-14 13:33:09 -0800762 // calls. This will be fixed when subresource registration is explicit.
bsalomondace19e2014-11-17 07:34:06 -0800763 // bool overBudget = budgetedBytes > fMaxBytes || budgetedCount > fMaxCount;
bsalomon12299ab2014-11-14 13:33:09 -0800764 // SkASSERT(!overBudget || locked == count || fPurging);
bsalomon71cb0c22014-11-14 12:10:14 -0800765}
bsalomonf320e042015-02-17 15:09:34 -0800766
767bool GrResourceCache::isInCache(const GrGpuResource* resource) const {
768 int index = *resource->cacheAccess().accessCacheIndex();
769 if (index < 0) {
770 return false;
771 }
772 if (index < fPurgeableQueue.count() && fPurgeableQueue.at(index) == resource) {
773 return true;
774 }
775 if (index < fNonpurgeableResources.count() && fNonpurgeableResources[index] == resource) {
776 return true;
777 }
778 SkDEBUGFAIL("Resource index should be -1 or the resource should be in the cache.");
779 return false;
780}
781
bsalomon71cb0c22014-11-14 12:10:14 -0800782#endif