blob: db97e35d7d938987351546b05b469c475e51e38c [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"
bsalomon3582d3e2015-02-13 14:20:05 -080010#include "GrGpuResourceCacheAccess.h"
hendrikw876c3132015-03-04 10:33:49 -080011#include "GrTracing.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}
bsalomon3f324322015-04-08 11:01:54 -070042
bsalomon24db3b12015-01-23 04:24:04 -080043uint32_t GrResourceKeyHash(const uint32_t* data, size_t size) {
mtklein56da0252015-11-16 11:16:23 -080044 return SkChecksum::Murmur3(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
bsalomon71cb0c22014-11-14 12:10:14 -080059
robertphillips63926682015-08-20 09:39:02 -070060GrResourceCache::GrResourceCache(const GrCaps* caps)
bsalomon9f2d1572015-02-17 11:47:40 -080061 : fTimestamp(0)
62 , fMaxCount(kDefaultMaxCount)
bsalomon71cb0c22014-11-14 12:10:14 -080063 , fMaxBytes(kDefaultMaxSize)
bsalomon3f324322015-04-08 11:01:54 -070064 , fMaxUnusedFlushes(kDefaultMaxUnusedFlushes)
bsalomon71cb0c22014-11-14 12:10:14 -080065#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)
halcanary96fcdcc2015-08-27 07:41:13 -070074 , fOverBudgetCB(nullptr)
75 , fOverBudgetData(nullptr)
76 , fFlushTimestamps(nullptr)
robertphillips63926682015-08-20 09:39:02 -070077 , fLastFlushTimestampIndex(0)
78 , fPreferVRAMUseOverFlushes(caps->preferVRAMUseOverFlushes()) {
bsalomonf320e042015-02-17 15:09:34 -080079 SkDEBUGCODE(fCount = 0;)
halcanary96fcdcc2015-08-27 07:41:13 -070080 SkDEBUGCODE(fNewlyPurgeableResourceForValidation = nullptr;)
bsalomon3f324322015-04-08 11:01:54 -070081 this->resetFlushTimestamps();
bsalomon71cb0c22014-11-14 12:10:14 -080082}
83
bsalomon0ea80f42015-02-11 10:49:59 -080084GrResourceCache::~GrResourceCache() {
bsalomonc8dc1f72014-08-21 13:02:13 -070085 this->releaseAll();
halcanary385fe4d2015-08-26 13:07:48 -070086 delete[] fFlushTimestamps;
bsalomonc8dc1f72014-08-21 13:02:13 -070087}
88
bsalomon3f324322015-04-08 11:01:54 -070089void GrResourceCache::setLimits(int count, size_t bytes, int maxUnusedFlushes) {
bsalomon71cb0c22014-11-14 12:10:14 -080090 fMaxCount = count;
91 fMaxBytes = bytes;
bsalomon3f324322015-04-08 11:01:54 -070092 fMaxUnusedFlushes = maxUnusedFlushes;
93 this->resetFlushTimestamps();
bsalomon71cb0c22014-11-14 12:10:14 -080094 this->purgeAsNeeded();
95}
96
bsalomon3f324322015-04-08 11:01:54 -070097void GrResourceCache::resetFlushTimestamps() {
halcanary385fe4d2015-08-26 13:07:48 -070098 delete[] fFlushTimestamps;
bsalomon3f324322015-04-08 11:01:54 -070099
100 // We assume this number is a power of two when wrapping indices into the timestamp array.
101 fMaxUnusedFlushes = SkNextPow2(fMaxUnusedFlushes);
102
103 // Since our implementation is to store the timestamps of the last fMaxUnusedFlushes flush calls
104 // we just turn the feature off if that array would be large.
105 static const int kMaxSupportedTimestampHistory = 128;
106
107 if (fMaxUnusedFlushes > kMaxSupportedTimestampHistory) {
halcanary96fcdcc2015-08-27 07:41:13 -0700108 fFlushTimestamps = nullptr;
bsalomon3f324322015-04-08 11:01:54 -0700109 return;
110 }
111
halcanary385fe4d2015-08-26 13:07:48 -0700112 fFlushTimestamps = new uint32_t[fMaxUnusedFlushes];
bsalomon3f324322015-04-08 11:01:54 -0700113 fLastFlushTimestampIndex = 0;
114 // Set all the historical flush timestamps to initially be at the beginning of time (timestamp
115 // 0).
116 sk_bzero(fFlushTimestamps, fMaxUnusedFlushes * sizeof(uint32_t));
117}
118
bsalomon0ea80f42015-02-11 10:49:59 -0800119void GrResourceCache::insertResource(GrGpuResource* resource) {
bsalomon49f085d2014-09-05 13:34:00 -0700120 SkASSERT(resource);
bsalomon16961262014-08-26 14:01:07 -0700121 SkASSERT(!this->isInCache(resource));
bsalomonf320e042015-02-17 15:09:34 -0800122 SkASSERT(!resource->wasDestroyed());
123 SkASSERT(!resource->isPurgeable());
bsalomonddf30e62015-02-19 11:38:44 -0800124
125 // We must set the timestamp before adding to the array in case the timestamp wraps and we wind
126 // up iterating over all the resources that already have timestamps.
127 resource->cacheAccess().setTimestamp(this->getNextTimestamp());
128
bsalomonf320e042015-02-17 15:09:34 -0800129 this->addToNonpurgeableArray(resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800130
bsalomondace19e2014-11-17 07:34:06 -0800131 size_t size = resource->gpuMemorySize();
bsalomonf320e042015-02-17 15:09:34 -0800132 SkDEBUGCODE(++fCount;)
bsalomon84c8e622014-11-17 09:33:27 -0800133 fBytes += size;
bsalomon82b1d622014-11-14 13:59:57 -0800134#if GR_CACHE_STATS
bsalomonf320e042015-02-17 15:09:34 -0800135 fHighWaterCount = SkTMax(this->getResourceCount(), fHighWaterCount);
bsalomon82b1d622014-11-14 13:59:57 -0800136 fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes);
137#endif
bsalomon5ec26ae2016-02-25 08:33:02 -0800138 if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
bsalomondace19e2014-11-17 07:34:06 -0800139 ++fBudgetedCount;
140 fBudgetedBytes += size;
hendrikw876c3132015-03-04 10:33:49 -0800141 TRACE_COUNTER2(TRACE_DISABLED_BY_DEFAULT("skia.gpu.cache"), "skia budget", "used",
142 fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
bsalomondace19e2014-11-17 07:34:06 -0800143#if GR_CACHE_STATS
144 fBudgetedHighWaterCount = SkTMax(fBudgetedCount, fBudgetedHighWaterCount);
145 fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
146#endif
147 }
robertphillipsc4ed6842016-05-24 14:17:12 -0700148 if (resource->resourcePriv().getScratchKey().isValid() &&
149 !resource->getUniqueKey().isValid()) {
kkinnunen2e6055b2016-04-22 01:48:29 -0700150 SkASSERT(!resource->resourcePriv().refsWrappedObjects());
bsalomon3582d3e2015-02-13 14:20:05 -0800151 fScratchMap.insert(resource->resourcePriv().getScratchKey(), resource);
bsalomon744998e2014-08-28 09:54:34 -0700152 }
bsalomon9f2d1572015-02-17 11:47:40 -0800153
bsalomon71cb0c22014-11-14 12:10:14 -0800154 this->purgeAsNeeded();
bsalomonc8dc1f72014-08-21 13:02:13 -0700155}
156
bsalomon0ea80f42015-02-11 10:49:59 -0800157void GrResourceCache::removeResource(GrGpuResource* resource) {
bsalomon9f2d1572015-02-17 11:47:40 -0800158 this->validate();
bsalomon16961262014-08-26 14:01:07 -0700159 SkASSERT(this->isInCache(resource));
bsalomondace19e2014-11-17 07:34:06 -0800160
bsalomon9f2d1572015-02-17 11:47:40 -0800161 if (resource->isPurgeable()) {
162 fPurgeableQueue.remove(resource);
bsalomonf320e042015-02-17 15:09:34 -0800163 } else {
164 this->removeFromNonpurgeableArray(resource);
bsalomon9f2d1572015-02-17 11:47:40 -0800165 }
166
bsalomondace19e2014-11-17 07:34:06 -0800167 size_t size = resource->gpuMemorySize();
bsalomonf320e042015-02-17 15:09:34 -0800168 SkDEBUGCODE(--fCount;)
bsalomondace19e2014-11-17 07:34:06 -0800169 fBytes -= size;
bsalomon5ec26ae2016-02-25 08:33:02 -0800170 if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
bsalomondace19e2014-11-17 07:34:06 -0800171 --fBudgetedCount;
172 fBudgetedBytes -= size;
hendrikw876c3132015-03-04 10:33:49 -0800173 TRACE_COUNTER2(TRACE_DISABLED_BY_DEFAULT("skia.gpu.cache"), "skia budget", "used",
174 fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
bsalomondace19e2014-11-17 07:34:06 -0800175 }
176
robertphillipsc4ed6842016-05-24 14:17:12 -0700177 if (resource->resourcePriv().getScratchKey().isValid() &&
178 !resource->getUniqueKey().isValid()) {
bsalomon3582d3e2015-02-13 14:20:05 -0800179 fScratchMap.remove(resource->resourcePriv().getScratchKey(), resource);
bsalomon744998e2014-08-28 09:54:34 -0700180 }
bsalomon8718aaf2015-02-19 07:24:21 -0800181 if (resource->getUniqueKey().isValid()) {
182 fUniqueHash.remove(resource->getUniqueKey());
bsalomon8b79d232014-11-10 10:19:06 -0800183 }
bsalomonb436ed62014-11-17 12:15:56 -0800184 this->validate();
bsalomonc8dc1f72014-08-21 13:02:13 -0700185}
186
bsalomon0ea80f42015-02-11 10:49:59 -0800187void GrResourceCache::abandonAll() {
bsalomon71cb0c22014-11-14 12:10:14 -0800188 AutoValidate av(this);
189
bsalomonf320e042015-02-17 15:09:34 -0800190 while (fNonpurgeableResources.count()) {
191 GrGpuResource* back = *(fNonpurgeableResources.end() - 1);
192 SkASSERT(!back->wasDestroyed());
193 back->cacheAccess().abandon();
bsalomonc8dc1f72014-08-21 13:02:13 -0700194 }
bsalomonf320e042015-02-17 15:09:34 -0800195
196 while (fPurgeableQueue.count()) {
197 GrGpuResource* top = fPurgeableQueue.peek();
198 SkASSERT(!top->wasDestroyed());
199 top->cacheAccess().abandon();
200 }
201
bsalomon744998e2014-08-28 09:54:34 -0700202 SkASSERT(!fScratchMap.count());
bsalomon8718aaf2015-02-19 07:24:21 -0800203 SkASSERT(!fUniqueHash.count());
bsalomonc8dc1f72014-08-21 13:02:13 -0700204 SkASSERT(!fCount);
bsalomonf320e042015-02-17 15:09:34 -0800205 SkASSERT(!this->getResourceCount());
bsalomondace19e2014-11-17 07:34:06 -0800206 SkASSERT(!fBytes);
207 SkASSERT(!fBudgetedCount);
208 SkASSERT(!fBudgetedBytes);
bsalomonc8dc1f72014-08-21 13:02:13 -0700209}
210
bsalomon0ea80f42015-02-11 10:49:59 -0800211void GrResourceCache::releaseAll() {
bsalomon71cb0c22014-11-14 12:10:14 -0800212 AutoValidate av(this);
213
bsalomonf320e042015-02-17 15:09:34 -0800214 while(fNonpurgeableResources.count()) {
215 GrGpuResource* back = *(fNonpurgeableResources.end() - 1);
216 SkASSERT(!back->wasDestroyed());
217 back->cacheAccess().release();
bsalomonc8dc1f72014-08-21 13:02:13 -0700218 }
bsalomonf320e042015-02-17 15:09:34 -0800219
220 while (fPurgeableQueue.count()) {
221 GrGpuResource* top = fPurgeableQueue.peek();
222 SkASSERT(!top->wasDestroyed());
223 top->cacheAccess().release();
224 }
225
bsalomon744998e2014-08-28 09:54:34 -0700226 SkASSERT(!fScratchMap.count());
bsalomon8718aaf2015-02-19 07:24:21 -0800227 SkASSERT(!fUniqueHash.count());
bsalomonc8dc1f72014-08-21 13:02:13 -0700228 SkASSERT(!fCount);
bsalomonf320e042015-02-17 15:09:34 -0800229 SkASSERT(!this->getResourceCount());
bsalomondace19e2014-11-17 07:34:06 -0800230 SkASSERT(!fBytes);
231 SkASSERT(!fBudgetedCount);
232 SkASSERT(!fBudgetedBytes);
bsalomonc8dc1f72014-08-21 13:02:13 -0700233}
bsalomonbcf0a522014-10-08 08:40:09 -0700234
bsalomon0ea80f42015-02-11 10:49:59 -0800235class GrResourceCache::AvailableForScratchUse {
bsalomonbcf0a522014-10-08 08:40:09 -0700236public:
bsalomon000f8292014-10-15 19:04:14 -0700237 AvailableForScratchUse(bool rejectPendingIO) : fRejectPendingIO(rejectPendingIO) { }
bsalomonbcf0a522014-10-08 08:40:09 -0700238
239 bool operator()(const GrGpuResource* resource) const {
robertphillipsc4ed6842016-05-24 14:17:12 -0700240 SkASSERT(!resource->getUniqueKey().isValid() &&
241 resource->resourcePriv().getScratchKey().isValid());
bsalomon12299ab2014-11-14 13:33:09 -0800242 if (resource->internalHasRef() || !resource->cacheAccess().isScratch()) {
bsalomon000f8292014-10-15 19:04:14 -0700243 return false;
bsalomonbcf0a522014-10-08 08:40:09 -0700244 }
bsalomon000f8292014-10-15 19:04:14 -0700245 return !fRejectPendingIO || !resource->internalHasPendingIO();
bsalomonbcf0a522014-10-08 08:40:09 -0700246 }
bsalomon1e2530b2014-10-09 09:57:18 -0700247
bsalomonbcf0a522014-10-08 08:40:09 -0700248private:
bsalomon000f8292014-10-15 19:04:14 -0700249 bool fRejectPendingIO;
bsalomonbcf0a522014-10-08 08:40:09 -0700250};
251
bsalomon0ea80f42015-02-11 10:49:59 -0800252GrGpuResource* GrResourceCache::findAndRefScratchResource(const GrScratchKey& scratchKey,
robertphillips6e83ac72015-08-13 05:19:14 -0700253 size_t resourceSize,
bsalomon9f2d1572015-02-17 11:47:40 -0800254 uint32_t flags) {
bsalomon7775c852014-12-30 12:50:52 -0800255 SkASSERT(scratchKey.isValid());
bsalomon000f8292014-10-15 19:04:14 -0700256
bsalomon71cb0c22014-11-14 12:10:14 -0800257 GrGpuResource* resource;
bsalomon000f8292014-10-15 19:04:14 -0700258 if (flags & (kPreferNoPendingIO_ScratchFlag | kRequireNoPendingIO_ScratchFlag)) {
bsalomon71cb0c22014-11-14 12:10:14 -0800259 resource = fScratchMap.find(scratchKey, AvailableForScratchUse(true));
bsalomon000f8292014-10-15 19:04:14 -0700260 if (resource) {
bsalomon9f2d1572015-02-17 11:47:40 -0800261 this->refAndMakeResourceMRU(resource);
bsalomonb436ed62014-11-17 12:15:56 -0800262 this->validate();
263 return resource;
bsalomon000f8292014-10-15 19:04:14 -0700264 } else if (flags & kRequireNoPendingIO_ScratchFlag) {
halcanary96fcdcc2015-08-27 07:41:13 -0700265 return nullptr;
bsalomon000f8292014-10-15 19:04:14 -0700266 }
robertphillips63926682015-08-20 09:39:02 -0700267 // We would prefer to consume more available VRAM rather than flushing
268 // immediately, but on ANGLE this can lead to starving of the GPU.
269 if (fPreferVRAMUseOverFlushes && this->wouldFit(resourceSize)) {
robertphillips6e83ac72015-08-13 05:19:14 -0700270 // kPrefer is specified, we didn't find a resource without pending io,
robertphillips63926682015-08-20 09:39:02 -0700271 // but there is still space in our budget for the resource so force
272 // the caller to allocate a new resource.
halcanary96fcdcc2015-08-27 07:41:13 -0700273 return nullptr;
robertphillips6e83ac72015-08-13 05:19:14 -0700274 }
bsalomon000f8292014-10-15 19:04:14 -0700275 }
bsalomon71cb0c22014-11-14 12:10:14 -0800276 resource = fScratchMap.find(scratchKey, AvailableForScratchUse(false));
277 if (resource) {
bsalomon9f2d1572015-02-17 11:47:40 -0800278 this->refAndMakeResourceMRU(resource);
bsalomonb436ed62014-11-17 12:15:56 -0800279 this->validate();
bsalomon71cb0c22014-11-14 12:10:14 -0800280 }
281 return resource;
bsalomonbcf0a522014-10-08 08:40:09 -0700282}
bsalomon8b79d232014-11-10 10:19:06 -0800283
bsalomon0ea80f42015-02-11 10:49:59 -0800284void GrResourceCache::willRemoveScratchKey(const GrGpuResource* resource) {
bsalomon3582d3e2015-02-13 14:20:05 -0800285 SkASSERT(resource->resourcePriv().getScratchKey().isValid());
robertphillipsc4ed6842016-05-24 14:17:12 -0700286 if (!resource->getUniqueKey().isValid()) {
287 fScratchMap.remove(resource->resourcePriv().getScratchKey(), resource);
288 }
bsalomon10e23ca2014-11-25 05:52:06 -0800289}
290
bsalomonf99e9612015-02-19 08:24:16 -0800291void GrResourceCache::removeUniqueKey(GrGpuResource* resource) {
bsalomon3f324322015-04-08 11:01:54 -0700292 // Someone has a ref to this resource in order to have removed the key. When the ref count
293 // reaches zero we will get a ref cnt notification and figure out what to do with it.
bsalomonf99e9612015-02-19 08:24:16 -0800294 if (resource->getUniqueKey().isValid()) {
295 SkASSERT(resource == fUniqueHash.find(resource->getUniqueKey()));
296 fUniqueHash.remove(resource->getUniqueKey());
297 }
298 resource->cacheAccess().removeUniqueKey();
robertphillipsc4ed6842016-05-24 14:17:12 -0700299
300 if (resource->resourcePriv().getScratchKey().isValid()) {
301 fScratchMap.insert(resource->resourcePriv().getScratchKey(), resource);
302 }
303
bsalomonf99e9612015-02-19 08:24:16 -0800304 this->validate();
bsalomon23e619c2015-02-06 11:54:28 -0800305}
306
bsalomonf99e9612015-02-19 08:24:16 -0800307void GrResourceCache::changeUniqueKey(GrGpuResource* resource, const GrUniqueKey& newKey) {
bsalomon8b79d232014-11-10 10:19:06 -0800308 SkASSERT(resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800309 SkASSERT(this->isInCache(resource));
bsalomon8b79d232014-11-10 10:19:06 -0800310
bsalomonf99e9612015-02-19 08:24:16 -0800311 // If another resource has the new key, remove its key then install the key on this resource.
312 if (newKey.isValid()) {
robertphillipsc4ed6842016-05-24 14:17:12 -0700313 // Remove the entry for this resource if it already has a unique key.
314 if (resource->getUniqueKey().isValid()) {
315 SkASSERT(resource == fUniqueHash.find(resource->getUniqueKey()));
316 fUniqueHash.remove(resource->getUniqueKey());
317 SkASSERT(nullptr == fUniqueHash.find(resource->getUniqueKey()));
318 } else {
319 // 'resource' didn't have a valid unique key before so it is switching sides. Remove it
320 // from the ScratchMap
321 if (resource->resourcePriv().getScratchKey().isValid()) {
322 fScratchMap.remove(resource->resourcePriv().getScratchKey(), resource);
323 }
324 }
325
bsalomonf99e9612015-02-19 08:24:16 -0800326 if (GrGpuResource* old = fUniqueHash.find(newKey)) {
327 // If the old resource using the key is purgeable and is unreachable, then remove it.
328 if (!old->resourcePriv().getScratchKey().isValid() && old->isPurgeable()) {
329 // release may call validate() which will assert that resource is in fUniqueHash
330 // if it has a valid key. So in debug reset the key here before we assign it.
331 SkDEBUGCODE(resource->cacheAccess().removeUniqueKey();)
332 old->cacheAccess().release();
333 } else {
robertphillipsc4ed6842016-05-24 14:17:12 -0700334 this->removeUniqueKey(old);
bsalomonf99e9612015-02-19 08:24:16 -0800335 }
336 }
halcanary96fcdcc2015-08-27 07:41:13 -0700337 SkASSERT(nullptr == fUniqueHash.find(newKey));
bsalomonf99e9612015-02-19 08:24:16 -0800338 resource->cacheAccess().setUniqueKey(newKey);
339 fUniqueHash.add(resource);
340 } else {
robertphillipsc4ed6842016-05-24 14:17:12 -0700341 this->removeUniqueKey(resource);
bsalomonf99e9612015-02-19 08:24:16 -0800342 }
343
bsalomon71cb0c22014-11-14 12:10:14 -0800344 this->validate();
bsalomon8b79d232014-11-10 10:19:06 -0800345}
bsalomon71cb0c22014-11-14 12:10:14 -0800346
bsalomon9f2d1572015-02-17 11:47:40 -0800347void GrResourceCache::refAndMakeResourceMRU(GrGpuResource* resource) {
bsalomon71cb0c22014-11-14 12:10:14 -0800348 SkASSERT(resource);
349 SkASSERT(this->isInCache(resource));
bsalomonddf30e62015-02-19 11:38:44 -0800350
bsalomon9f2d1572015-02-17 11:47:40 -0800351 if (resource->isPurgeable()) {
352 // It's about to become unpurgeable.
353 fPurgeableQueue.remove(resource);
bsalomonf320e042015-02-17 15:09:34 -0800354 this->addToNonpurgeableArray(resource);
bsalomon9f2d1572015-02-17 11:47:40 -0800355 }
356 resource->ref();
bsalomonddf30e62015-02-19 11:38:44 -0800357
358 resource->cacheAccess().setTimestamp(this->getNextTimestamp());
bsalomonf320e042015-02-17 15:09:34 -0800359 this->validate();
bsalomon71cb0c22014-11-14 12:10:14 -0800360}
361
bsalomon3f324322015-04-08 11:01:54 -0700362void GrResourceCache::notifyCntReachedZero(GrGpuResource* resource, uint32_t flags) {
bsalomon71cb0c22014-11-14 12:10:14 -0800363 SkASSERT(resource);
bsalomon3f324322015-04-08 11:01:54 -0700364 SkASSERT(!resource->wasDestroyed());
365 SkASSERT(flags);
bsalomon71cb0c22014-11-14 12:10:14 -0800366 SkASSERT(this->isInCache(resource));
bsalomon3f324322015-04-08 11:01:54 -0700367 // This resource should always be in the nonpurgeable array when this function is called. It
368 // will be moved to the queue if it is newly purgeable.
369 SkASSERT(fNonpurgeableResources[*resource->cacheAccess().accessCacheIndex()] == resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800370
bsalomon3f324322015-04-08 11:01:54 -0700371 if (SkToBool(ResourceAccess::kRefCntReachedZero_RefNotificationFlag & flags)) {
372#ifdef SK_DEBUG
373 // When the timestamp overflows validate() is called. validate() checks that resources in
374 // the nonpurgeable array are indeed not purgeable. However, the movement from the array to
375 // the purgeable queue happens just below in this function. So we mark it as an exception.
376 if (resource->isPurgeable()) {
377 fNewlyPurgeableResourceForValidation = resource;
378 }
379#endif
380 resource->cacheAccess().setTimestamp(this->getNextTimestamp());
halcanary96fcdcc2015-08-27 07:41:13 -0700381 SkDEBUGCODE(fNewlyPurgeableResourceForValidation = nullptr);
bsalomon3f324322015-04-08 11:01:54 -0700382 }
383
384 if (!SkToBool(ResourceAccess::kAllCntsReachedZero_RefNotificationFlag & flags)) {
385 SkASSERT(!resource->isPurgeable());
386 return;
387 }
388
389 SkASSERT(resource->isPurgeable());
bsalomonf320e042015-02-17 15:09:34 -0800390 this->removeFromNonpurgeableArray(resource);
bsalomon9f2d1572015-02-17 11:47:40 -0800391 fPurgeableQueue.insert(resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800392
bsalomon5ec26ae2016-02-25 08:33:02 -0800393 if (SkBudgeted::kNo == resource->resourcePriv().isBudgeted()) {
bsalomonc2f35b72015-01-23 07:19:22 -0800394 // Check whether this resource could still be used as a scratch resource.
kkinnunen2e6055b2016-04-22 01:48:29 -0700395 if (!resource->resourcePriv().refsWrappedObjects() &&
bsalomon9f2d1572015-02-17 11:47:40 -0800396 resource->resourcePriv().getScratchKey().isValid()) {
bsalomonc2f35b72015-01-23 07:19:22 -0800397 // We won't purge an existing resource to make room for this one.
bsalomonf320e042015-02-17 15:09:34 -0800398 if (fBudgetedCount < fMaxCount &&
399 fBudgetedBytes + resource->gpuMemorySize() <= fMaxBytes) {
bsalomon3582d3e2015-02-13 14:20:05 -0800400 resource->resourcePriv().makeBudgeted();
bsalomon9f2d1572015-02-17 11:47:40 -0800401 return;
bsalomonc2f35b72015-01-23 07:19:22 -0800402 }
bsalomonc2f35b72015-01-23 07:19:22 -0800403 }
404 } else {
bsalomon9f2d1572015-02-17 11:47:40 -0800405 // Purge the resource immediately if we're over budget
bsalomon8718aaf2015-02-19 07:24:21 -0800406 // Also purge if the resource has neither a valid scratch key nor a unique key.
bsalomon3582d3e2015-02-13 14:20:05 -0800407 bool noKey = !resource->resourcePriv().getScratchKey().isValid() &&
bsalomon8718aaf2015-02-19 07:24:21 -0800408 !resource->getUniqueKey().isValid();
bsalomonf320e042015-02-17 15:09:34 -0800409 if (!this->overBudget() && !noKey) {
bsalomon9f2d1572015-02-17 11:47:40 -0800410 return;
bsalomonc2f35b72015-01-23 07:19:22 -0800411 }
412 }
bsalomondace19e2014-11-17 07:34:06 -0800413
bsalomonf320e042015-02-17 15:09:34 -0800414 SkDEBUGCODE(int beforeCount = this->getResourceCount();)
bsalomon9f2d1572015-02-17 11:47:40 -0800415 resource->cacheAccess().release();
416 // We should at least free this resource, perhaps dependent resources as well.
bsalomonf320e042015-02-17 15:09:34 -0800417 SkASSERT(this->getResourceCount() < beforeCount);
bsalomon71cb0c22014-11-14 12:10:14 -0800418 this->validate();
419}
420
bsalomon0ea80f42015-02-11 10:49:59 -0800421void GrResourceCache::didChangeGpuMemorySize(const GrGpuResource* resource, size_t oldSize) {
bsalomon71cb0c22014-11-14 12:10:14 -0800422 // SkASSERT(!fPurging); GrPathRange increases size during flush. :(
423 SkASSERT(resource);
424 SkASSERT(this->isInCache(resource));
425
bsalomondace19e2014-11-17 07:34:06 -0800426 ptrdiff_t delta = resource->gpuMemorySize() - oldSize;
427
428 fBytes += delta;
bsalomon82b1d622014-11-14 13:59:57 -0800429#if GR_CACHE_STATS
430 fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes);
431#endif
bsalomon5ec26ae2016-02-25 08:33:02 -0800432 if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
bsalomondace19e2014-11-17 07:34:06 -0800433 fBudgetedBytes += delta;
hendrikw876c3132015-03-04 10:33:49 -0800434 TRACE_COUNTER2(TRACE_DISABLED_BY_DEFAULT("skia.gpu.cache"), "skia budget", "used",
435 fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
bsalomondace19e2014-11-17 07:34:06 -0800436#if GR_CACHE_STATS
437 fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
438#endif
439 }
bsalomon71cb0c22014-11-14 12:10:14 -0800440
441 this->purgeAsNeeded();
442 this->validate();
443}
444
bsalomon0ea80f42015-02-11 10:49:59 -0800445void GrResourceCache::didChangeBudgetStatus(GrGpuResource* resource) {
bsalomon84c8e622014-11-17 09:33:27 -0800446 SkASSERT(resource);
447 SkASSERT(this->isInCache(resource));
448
449 size_t size = resource->gpuMemorySize();
450
bsalomon5ec26ae2016-02-25 08:33:02 -0800451 if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
bsalomon84c8e622014-11-17 09:33:27 -0800452 ++fBudgetedCount;
453 fBudgetedBytes += size;
bsalomonafe30052015-01-16 07:32:33 -0800454#if GR_CACHE_STATS
455 fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
456 fBudgetedHighWaterCount = SkTMax(fBudgetedCount, fBudgetedHighWaterCount);
457#endif
bsalomon84c8e622014-11-17 09:33:27 -0800458 this->purgeAsNeeded();
459 } else {
460 --fBudgetedCount;
461 fBudgetedBytes -= size;
462 }
hendrikw876c3132015-03-04 10:33:49 -0800463 TRACE_COUNTER2(TRACE_DISABLED_BY_DEFAULT("skia.gpu.cache"), "skia budget", "used",
464 fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
bsalomon84c8e622014-11-17 09:33:27 -0800465
466 this->validate();
467}
468
bsalomon3f324322015-04-08 11:01:54 -0700469void GrResourceCache::purgeAsNeeded() {
470 SkTArray<GrUniqueKeyInvalidatedMessage> invalidKeyMsgs;
471 fInvalidUniqueKeyInbox.poll(&invalidKeyMsgs);
472 if (invalidKeyMsgs.count()) {
473 this->processInvalidUniqueKeys(invalidKeyMsgs);
474 }
bsalomon71cb0c22014-11-14 12:10:14 -0800475
bsalomon3f324322015-04-08 11:01:54 -0700476 if (fFlushTimestamps) {
477 // Assuming kNumFlushesToDeleteUnusedResource is a power of 2.
478 SkASSERT(SkIsPow2(fMaxUnusedFlushes));
479 int oldestFlushIndex = (fLastFlushTimestampIndex + 1) & (fMaxUnusedFlushes - 1);
480
481 uint32_t oldestAllowedTimestamp = fFlushTimestamps[oldestFlushIndex];
482 while (fPurgeableQueue.count()) {
483 uint32_t oldestResourceTimestamp = fPurgeableQueue.peek()->cacheAccess().timestamp();
484 if (oldestAllowedTimestamp < oldestResourceTimestamp) {
485 break;
486 }
487 GrGpuResource* resource = fPurgeableQueue.peek();
488 SkASSERT(resource->isPurgeable());
489 resource->cacheAccess().release();
490 }
491 }
492
493 bool stillOverbudget = this->overBudget();
494 while (stillOverbudget && fPurgeableQueue.count()) {
bsalomon9f2d1572015-02-17 11:47:40 -0800495 GrGpuResource* resource = fPurgeableQueue.peek();
496 SkASSERT(resource->isPurgeable());
497 resource->cacheAccess().release();
bsalomon3f324322015-04-08 11:01:54 -0700498 stillOverbudget = this->overBudget();
bsalomon9f2d1572015-02-17 11:47:40 -0800499 }
bsalomon71cb0c22014-11-14 12:10:14 -0800500
bsalomonb436ed62014-11-17 12:15:56 -0800501 this->validate();
bsalomon9f2d1572015-02-17 11:47:40 -0800502
503 if (stillOverbudget) {
504 // Despite the purge we're still over budget. Call our over budget callback. If this frees
bsalomon3f324322015-04-08 11:01:54 -0700505 // any resources then we'll get notified and take appropriate action.
bsalomon9f2d1572015-02-17 11:47:40 -0800506 (*fOverBudgetCB)(fOverBudgetData);
507 this->validate();
508 }
bsalomon71cb0c22014-11-14 12:10:14 -0800509}
510
bsalomon0ea80f42015-02-11 10:49:59 -0800511void GrResourceCache::purgeAllUnlocked() {
bsalomon9f2d1572015-02-17 11:47:40 -0800512 // We could disable maintaining the heap property here, but it would add a lot of complexity.
513 // Moreover, this is rarely called.
514 while (fPurgeableQueue.count()) {
515 GrGpuResource* resource = fPurgeableQueue.peek();
516 SkASSERT(resource->isPurgeable());
517 resource->cacheAccess().release();
518 }
bsalomon71cb0c22014-11-14 12:10:14 -0800519
bsalomonb436ed62014-11-17 12:15:56 -0800520 this->validate();
bsalomon71cb0c22014-11-14 12:10:14 -0800521}
522
bsalomon8718aaf2015-02-19 07:24:21 -0800523void GrResourceCache::processInvalidUniqueKeys(
524 const SkTArray<GrUniqueKeyInvalidatedMessage>& msgs) {
bsalomon23e619c2015-02-06 11:54:28 -0800525 for (int i = 0; i < msgs.count(); ++i) {
bsalomon8718aaf2015-02-19 07:24:21 -0800526 GrGpuResource* resource = this->findAndRefUniqueResource(msgs[i].key());
bsalomon23e619c2015-02-06 11:54:28 -0800527 if (resource) {
bsalomon8718aaf2015-02-19 07:24:21 -0800528 resource->resourcePriv().removeUniqueKey();
bsalomon3f324322015-04-08 11:01:54 -0700529 resource->unref(); // If this resource is now purgeable, the cache will be notified.
bsalomon23e619c2015-02-06 11:54:28 -0800530 }
531 }
532}
533
bsalomonf320e042015-02-17 15:09:34 -0800534void GrResourceCache::addToNonpurgeableArray(GrGpuResource* resource) {
535 int index = fNonpurgeableResources.count();
536 *fNonpurgeableResources.append() = resource;
537 *resource->cacheAccess().accessCacheIndex() = index;
538}
539
540void GrResourceCache::removeFromNonpurgeableArray(GrGpuResource* resource) {
541 int* index = resource->cacheAccess().accessCacheIndex();
542 // Fill the whole we will create in the array with the tail object, adjust its index, and
543 // then pop the array
544 GrGpuResource* tail = *(fNonpurgeableResources.end() - 1);
545 SkASSERT(fNonpurgeableResources[*index] == resource);
546 fNonpurgeableResources[*index] = tail;
547 *tail->cacheAccess().accessCacheIndex() = *index;
548 fNonpurgeableResources.pop();
549 SkDEBUGCODE(*index = -1);
550}
551
bsalomonddf30e62015-02-19 11:38:44 -0800552uint32_t GrResourceCache::getNextTimestamp() {
553 // If we wrap then all the existing resources will appear older than any resources that get
554 // a timestamp after the wrap.
555 if (0 == fTimestamp) {
556 int count = this->getResourceCount();
557 if (count) {
558 // Reset all the timestamps. We sort the resources by timestamp and then assign
559 // sequential timestamps beginning with 0. This is O(n*lg(n)) but it should be extremely
560 // rare.
561 SkTDArray<GrGpuResource*> sortedPurgeableResources;
562 sortedPurgeableResources.setReserve(fPurgeableQueue.count());
563
564 while (fPurgeableQueue.count()) {
565 *sortedPurgeableResources.append() = fPurgeableQueue.peek();
566 fPurgeableQueue.pop();
567 }
568
569 struct Less {
570 bool operator()(GrGpuResource* a, GrGpuResource* b) {
571 return CompareTimestamp(a,b);
572 }
573 };
574 Less less;
575 SkTQSort(fNonpurgeableResources.begin(), fNonpurgeableResources.end() - 1, less);
576
577 // Pick resources out of the purgeable and non-purgeable arrays based on lowest
578 // timestamp and assign new timestamps.
579 int currP = 0;
580 int currNP = 0;
581 while (currP < sortedPurgeableResources.count() &&
mtklein56da0252015-11-16 11:16:23 -0800582 currNP < fNonpurgeableResources.count()) {
bsalomonddf30e62015-02-19 11:38:44 -0800583 uint32_t tsP = sortedPurgeableResources[currP]->cacheAccess().timestamp();
584 uint32_t tsNP = fNonpurgeableResources[currNP]->cacheAccess().timestamp();
585 SkASSERT(tsP != tsNP);
586 if (tsP < tsNP) {
587 sortedPurgeableResources[currP++]->cacheAccess().setTimestamp(fTimestamp++);
588 } else {
589 // Correct the index in the nonpurgeable array stored on the resource post-sort.
590 *fNonpurgeableResources[currNP]->cacheAccess().accessCacheIndex() = currNP;
591 fNonpurgeableResources[currNP++]->cacheAccess().setTimestamp(fTimestamp++);
592 }
593 }
594
595 // The above loop ended when we hit the end of one array. Finish the other one.
596 while (currP < sortedPurgeableResources.count()) {
597 sortedPurgeableResources[currP++]->cacheAccess().setTimestamp(fTimestamp++);
598 }
599 while (currNP < fNonpurgeableResources.count()) {
600 *fNonpurgeableResources[currNP]->cacheAccess().accessCacheIndex() = currNP;
601 fNonpurgeableResources[currNP++]->cacheAccess().setTimestamp(fTimestamp++);
602 }
603
604 // Rebuild the queue.
605 for (int i = 0; i < sortedPurgeableResources.count(); ++i) {
606 fPurgeableQueue.insert(sortedPurgeableResources[i]);
607 }
608
609 this->validate();
610 SkASSERT(count == this->getResourceCount());
611
612 // count should be the next timestamp we return.
613 SkASSERT(fTimestamp == SkToU32(count));
mtklein56da0252015-11-16 11:16:23 -0800614
bsalomon3f324322015-04-08 11:01:54 -0700615 // The historical timestamps of flushes are now invalid.
616 this->resetFlushTimestamps();
mtklein56da0252015-11-16 11:16:23 -0800617 }
bsalomonddf30e62015-02-19 11:38:44 -0800618 }
619 return fTimestamp++;
620}
621
bsalomon3f324322015-04-08 11:01:54 -0700622void GrResourceCache::notifyFlushOccurred() {
623 if (fFlushTimestamps) {
624 SkASSERT(SkIsPow2(fMaxUnusedFlushes));
625 fLastFlushTimestampIndex = (fLastFlushTimestampIndex + 1) & (fMaxUnusedFlushes - 1);
626 // get the timestamp before accessing fFlushTimestamps because getNextTimestamp will
627 // reallocate fFlushTimestamps on timestamp overflow.
628 uint32_t timestamp = this->getNextTimestamp();
629 fFlushTimestamps[fLastFlushTimestampIndex] = timestamp;
630 this->purgeAsNeeded();
631 }
632}
633
ericrk0a5fa482015-09-15 14:16:10 -0700634void GrResourceCache::dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const {
635 for (int i = 0; i < fNonpurgeableResources.count(); ++i) {
636 fNonpurgeableResources[i]->dumpMemoryStatistics(traceMemoryDump);
637 }
638 for (int i = 0; i < fPurgeableQueue.count(); ++i) {
639 fPurgeableQueue.at(i)->dumpMemoryStatistics(traceMemoryDump);
640 }
641}
642
bsalomon71cb0c22014-11-14 12:10:14 -0800643#ifdef SK_DEBUG
bsalomon0ea80f42015-02-11 10:49:59 -0800644void GrResourceCache::validate() const {
bsalomonc2f35b72015-01-23 07:19:22 -0800645 // Reduce the frequency of validations for large resource counts.
646 static SkRandom gRandom;
647 int mask = (SkNextPow2(fCount + 1) >> 5) - 1;
648 if (~mask && (gRandom.nextU() & mask)) {
649 return;
650 }
651
bsalomonf320e042015-02-17 15:09:34 -0800652 struct Stats {
653 size_t fBytes;
654 int fBudgetedCount;
655 size_t fBudgetedBytes;
656 int fLocked;
657 int fScratch;
658 int fCouldBeScratch;
659 int fContent;
660 const ScratchMap* fScratchMap;
bsalomon8718aaf2015-02-19 07:24:21 -0800661 const UniqueHash* fUniqueHash;
bsalomon71cb0c22014-11-14 12:10:14 -0800662
bsalomonf320e042015-02-17 15:09:34 -0800663 Stats(const GrResourceCache* cache) {
664 memset(this, 0, sizeof(*this));
665 fScratchMap = &cache->fScratchMap;
bsalomon8718aaf2015-02-19 07:24:21 -0800666 fUniqueHash = &cache->fUniqueHash;
bsalomon71cb0c22014-11-14 12:10:14 -0800667 }
668
bsalomonf320e042015-02-17 15:09:34 -0800669 void update(GrGpuResource* resource) {
670 fBytes += resource->gpuMemorySize();
bsalomondace19e2014-11-17 07:34:06 -0800671
bsalomonf320e042015-02-17 15:09:34 -0800672 if (!resource->isPurgeable()) {
673 ++fLocked;
674 }
bsalomon9f2d1572015-02-17 11:47:40 -0800675
robertphillipsc4ed6842016-05-24 14:17:12 -0700676 const GrScratchKey& scratchKey = resource->resourcePriv().getScratchKey();
677 const GrUniqueKey& uniqueKey = resource->getUniqueKey();
678
bsalomonf320e042015-02-17 15:09:34 -0800679 if (resource->cacheAccess().isScratch()) {
robertphillipsc4ed6842016-05-24 14:17:12 -0700680 SkASSERT(!uniqueKey.isValid());
bsalomonf320e042015-02-17 15:09:34 -0800681 ++fScratch;
robertphillipsc4ed6842016-05-24 14:17:12 -0700682 SkASSERT(fScratchMap->countForKey(scratchKey));
kkinnunen2e6055b2016-04-22 01:48:29 -0700683 SkASSERT(!resource->resourcePriv().refsWrappedObjects());
robertphillipsc4ed6842016-05-24 14:17:12 -0700684 } else if (scratchKey.isValid()) {
bsalomon5ec26ae2016-02-25 08:33:02 -0800685 SkASSERT(SkBudgeted::kNo == resource->resourcePriv().isBudgeted() ||
robertphillipsc4ed6842016-05-24 14:17:12 -0700686 uniqueKey.isValid());
687 if (!uniqueKey.isValid()) {
688 ++fCouldBeScratch;
689 SkASSERT(fScratchMap->countForKey(scratchKey));
690 }
kkinnunen2e6055b2016-04-22 01:48:29 -0700691 SkASSERT(!resource->resourcePriv().refsWrappedObjects());
bsalomonf320e042015-02-17 15:09:34 -0800692 }
bsalomon8718aaf2015-02-19 07:24:21 -0800693 if (uniqueKey.isValid()) {
bsalomonf320e042015-02-17 15:09:34 -0800694 ++fContent;
bsalomon8718aaf2015-02-19 07:24:21 -0800695 SkASSERT(fUniqueHash->find(uniqueKey) == resource);
kkinnunen2e6055b2016-04-22 01:48:29 -0700696 SkASSERT(!resource->resourcePriv().refsWrappedObjects());
bsalomon5ec26ae2016-02-25 08:33:02 -0800697 SkASSERT(SkBudgeted::kYes == resource->resourcePriv().isBudgeted());
robertphillipsc4ed6842016-05-24 14:17:12 -0700698
699 if (scratchKey.isValid()) {
700 SkASSERT(!fScratchMap->has(resource, scratchKey));
701 }
bsalomonf320e042015-02-17 15:09:34 -0800702 }
703
bsalomon5ec26ae2016-02-25 08:33:02 -0800704 if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
bsalomonf320e042015-02-17 15:09:34 -0800705 ++fBudgetedCount;
706 fBudgetedBytes += resource->gpuMemorySize();
707 }
bsalomon9f2d1572015-02-17 11:47:40 -0800708 }
bsalomonf320e042015-02-17 15:09:34 -0800709 };
710
robertphillipsc4ed6842016-05-24 14:17:12 -0700711 {
712 ScratchMap::ConstIter iter(&fScratchMap);
713
714 int count = 0;
715 for ( ; !iter.done(); ++iter) {
716 const GrGpuResource* resource = *iter;
717 SkASSERT(resource->resourcePriv().getScratchKey().isValid());
718 SkASSERT(!resource->getUniqueKey().isValid());
719 count++;
720 }
721 SkASSERT(count == fScratchMap.count()); // ensure the iterator is working correctly
722 }
723
bsalomonf320e042015-02-17 15:09:34 -0800724 Stats stats(this);
725
726 for (int i = 0; i < fNonpurgeableResources.count(); ++i) {
bsalomon3f324322015-04-08 11:01:54 -0700727 SkASSERT(!fNonpurgeableResources[i]->isPurgeable() ||
728 fNewlyPurgeableResourceForValidation == fNonpurgeableResources[i]);
bsalomonf320e042015-02-17 15:09:34 -0800729 SkASSERT(*fNonpurgeableResources[i]->cacheAccess().accessCacheIndex() == i);
730 SkASSERT(!fNonpurgeableResources[i]->wasDestroyed());
731 stats.update(fNonpurgeableResources[i]);
bsalomon71cb0c22014-11-14 12:10:14 -0800732 }
bsalomon9f2d1572015-02-17 11:47:40 -0800733 for (int i = 0; i < fPurgeableQueue.count(); ++i) {
734 SkASSERT(fPurgeableQueue.at(i)->isPurgeable());
bsalomonf320e042015-02-17 15:09:34 -0800735 SkASSERT(*fPurgeableQueue.at(i)->cacheAccess().accessCacheIndex() == i);
736 SkASSERT(!fPurgeableQueue.at(i)->wasDestroyed());
737 stats.update(fPurgeableQueue.at(i));
bsalomon9f2d1572015-02-17 11:47:40 -0800738 }
739
bsalomonf320e042015-02-17 15:09:34 -0800740 SkASSERT(fCount == this->getResourceCount());
bsalomondace19e2014-11-17 07:34:06 -0800741 SkASSERT(fBudgetedCount <= fCount);
bsalomonf320e042015-02-17 15:09:34 -0800742 SkASSERT(fBudgetedBytes <= fBytes);
743 SkASSERT(stats.fBytes == fBytes);
744 SkASSERT(stats.fBudgetedBytes == fBudgetedBytes);
745 SkASSERT(stats.fBudgetedCount == fBudgetedCount);
bsalomon71cb0c22014-11-14 12:10:14 -0800746#if GR_CACHE_STATS
bsalomondace19e2014-11-17 07:34:06 -0800747 SkASSERT(fBudgetedHighWaterCount <= fHighWaterCount);
748 SkASSERT(fBudgetedHighWaterBytes <= fHighWaterBytes);
bsalomonf320e042015-02-17 15:09:34 -0800749 SkASSERT(fBytes <= fHighWaterBytes);
750 SkASSERT(fCount <= fHighWaterCount);
751 SkASSERT(fBudgetedBytes <= fBudgetedHighWaterBytes);
752 SkASSERT(fBudgetedCount <= fBudgetedHighWaterCount);
bsalomon71cb0c22014-11-14 12:10:14 -0800753#endif
bsalomon8718aaf2015-02-19 07:24:21 -0800754 SkASSERT(stats.fContent == fUniqueHash.count());
bsalomonf320e042015-02-17 15:09:34 -0800755 SkASSERT(stats.fScratch + stats.fCouldBeScratch == fScratchMap.count());
bsalomon71cb0c22014-11-14 12:10:14 -0800756
bsalomon3f324322015-04-08 11:01:54 -0700757 // This assertion is not currently valid because we can be in recursive notifyCntReachedZero()
bsalomon12299ab2014-11-14 13:33:09 -0800758 // calls. This will be fixed when subresource registration is explicit.
bsalomondace19e2014-11-17 07:34:06 -0800759 // bool overBudget = budgetedBytes > fMaxBytes || budgetedCount > fMaxCount;
bsalomon12299ab2014-11-14 13:33:09 -0800760 // SkASSERT(!overBudget || locked == count || fPurging);
bsalomon71cb0c22014-11-14 12:10:14 -0800761}
bsalomonf320e042015-02-17 15:09:34 -0800762
763bool GrResourceCache::isInCache(const GrGpuResource* resource) const {
764 int index = *resource->cacheAccess().accessCacheIndex();
765 if (index < 0) {
766 return false;
767 }
768 if (index < fPurgeableQueue.count() && fPurgeableQueue.at(index) == resource) {
769 return true;
770 }
771 if (index < fNonpurgeableResources.count() && fNonpurgeableResources[index] == resource) {
772 return true;
773 }
774 SkDEBUGFAIL("Resource index should be -1 or the resource should be in the cache.");
775 return false;
776}
777
bsalomon71cb0c22014-11-14 12:10:14 -0800778#endif