blob: 23c8e7c99690c8edbd686b4e92ff9cc60ad8c3a6 [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
bsalomon0ea80f42015-02-11 10:49:59 -08008#include "GrResourceCache.h"
robertphillips28a838e2016-06-23 14:07:00 -07009
10#include "GrCaps.h"
bsalomon3582d3e2015-02-13 14:20:05 -080011#include "GrGpuResourceCacheAccess.h"
Robert Phillips1afd4cd2018-01-08 13:40:32 -050012#include "GrProxyProvider.h"
Robert Phillipsae7d3f32017-09-21 08:26:08 -040013#include "GrTexture.h"
14#include "GrTextureProxyCacheAccess.h"
hendrikw876c3132015-03-04 10:33:49 -080015#include "GrTracing.h"
bsalomon71cb0c22014-11-14 12:10:14 -080016#include "SkGr.h"
17#include "SkMessageBus.h"
mtklein4e976072016-08-08 09:06:27 -070018#include "SkOpts.h"
bsalomonddf30e62015-02-19 11:38:44 -080019#include "SkTSort.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -040020#include "SkTo.h"
bsalomon71cb0c22014-11-14 12:10:14 -080021
bsalomon8718aaf2015-02-19 07:24:21 -080022DECLARE_SKMESSAGEBUS_MESSAGE(GrUniqueKeyInvalidatedMessage);
bsalomon71cb0c22014-11-14 12:10:14 -080023
Brian Osman13dddce2017-05-09 13:19:50 -040024DECLARE_SKMESSAGEBUS_MESSAGE(GrGpuResourceFreedMessage);
25
bsalomon71cb0c22014-11-14 12:10:14 -080026//////////////////////////////////////////////////////////////////////////////
27
bsalomon7775c852014-12-30 12:50:52 -080028GrScratchKey::ResourceType GrScratchKey::GenerateResourceType() {
bsalomon24db3b12015-01-23 04:24:04 -080029 static int32_t gType = INHERITED::kInvalidDomain + 1;
bsalomonfe369ee2014-11-10 11:59:06 -080030
bsalomon7775c852014-12-30 12:50:52 -080031 int32_t type = sk_atomic_inc(&gType);
Ben Wagner9bc36fd2018-06-15 14:23:36 -040032 if (type > SkTo<int32_t>(UINT16_MAX)) {
Ben Wagnerb4aab9a2017-08-16 10:53:04 -040033 SK_ABORT("Too many Resource Types");
bsalomon71cb0c22014-11-14 12:10:14 -080034 }
35
36 return static_cast<ResourceType>(type);
37}
38
bsalomon8718aaf2015-02-19 07:24:21 -080039GrUniqueKey::Domain GrUniqueKey::GenerateDomain() {
bsalomon24db3b12015-01-23 04:24:04 -080040 static int32_t gDomain = INHERITED::kInvalidDomain + 1;
bsalomon7775c852014-12-30 12:50:52 -080041
bsalomon24db3b12015-01-23 04:24:04 -080042 int32_t domain = sk_atomic_inc(&gDomain);
Ben Wagner397ee0e2018-06-15 15:13:26 -040043 if (domain > SkTo<int32_t>(UINT16_MAX)) {
Ben Wagnerb4aab9a2017-08-16 10:53:04 -040044 SK_ABORT("Too many GrUniqueKey Domains");
bsalomon7775c852014-12-30 12:50:52 -080045 }
bsalomon24db3b12015-01-23 04:24:04 -080046
47 return static_cast<Domain>(domain);
48}
bsalomon3f324322015-04-08 11:01:54 -070049
bsalomon24db3b12015-01-23 04:24:04 -080050uint32_t GrResourceKeyHash(const uint32_t* data, size_t size) {
mtklein4e976072016-08-08 09:06:27 -070051 return SkOpts::hash(data, size);
bsalomon7775c852014-12-30 12:50:52 -080052}
53
bsalomonfe369ee2014-11-10 11:59:06 -080054//////////////////////////////////////////////////////////////////////////////
55
bsalomon0ea80f42015-02-11 10:49:59 -080056class GrResourceCache::AutoValidate : ::SkNoncopyable {
bsalomon71cb0c22014-11-14 12:10:14 -080057public:
bsalomon0ea80f42015-02-11 10:49:59 -080058 AutoValidate(GrResourceCache* cache) : fCache(cache) { cache->validate(); }
bsalomon71cb0c22014-11-14 12:10:14 -080059 ~AutoValidate() { fCache->validate(); }
60private:
bsalomon0ea80f42015-02-11 10:49:59 -080061 GrResourceCache* fCache;
bsalomon71cb0c22014-11-14 12:10:14 -080062};
63
64 //////////////////////////////////////////////////////////////////////////////
robertphillipsee843b22016-10-04 05:30:20 -070065
Brian Osman13dddce2017-05-09 13:19:50 -040066GrResourceCache::GrResourceCache(const GrCaps* caps, uint32_t contextUniqueID)
Brian Salomon238069b2018-07-11 15:58:57 -040067 : fProxyProvider(nullptr)
68 , fTimestamp(0)
69 , fMaxCount(kDefaultMaxCount)
70 , fMaxBytes(kDefaultMaxSize)
71 , fMaxUnusedFlushes(kDefaultMaxUnusedFlushes)
bsalomon71cb0c22014-11-14 12:10:14 -080072#if GR_CACHE_STATS
Brian Salomon238069b2018-07-11 15:58:57 -040073 , fHighWaterCount(0)
74 , fHighWaterBytes(0)
75 , fBudgetedHighWaterCount(0)
76 , fBudgetedHighWaterBytes(0)
bsalomon71cb0c22014-11-14 12:10:14 -080077#endif
Brian Salomon238069b2018-07-11 15:58:57 -040078 , fBytes(0)
79 , fBudgetedCount(0)
80 , fBudgetedBytes(0)
81 , fPurgeableBytes(0)
82 , fRequestFlush(false)
83 , fExternalFlushCnt(0)
84 , fInvalidUniqueKeyInbox(contextUniqueID)
85 , fFreedGpuResourceInbox(contextUniqueID)
86 , fContextUniqueID(contextUniqueID)
87 , fPreferVRAMUseOverFlushes(caps->preferVRAMUseOverFlushes()) {
88 SkASSERT(contextUniqueID != SK_InvalidUniqueID);
bsalomonf320e042015-02-17 15:09:34 -080089 SkDEBUGCODE(fCount = 0;)
halcanary96fcdcc2015-08-27 07:41:13 -070090 SkDEBUGCODE(fNewlyPurgeableResourceForValidation = nullptr;)
bsalomon71cb0c22014-11-14 12:10:14 -080091}
92
bsalomon0ea80f42015-02-11 10:49:59 -080093GrResourceCache::~GrResourceCache() {
bsalomonc8dc1f72014-08-21 13:02:13 -070094 this->releaseAll();
95}
96
bsalomon3f324322015-04-08 11:01:54 -070097void GrResourceCache::setLimits(int count, size_t bytes, int maxUnusedFlushes) {
bsalomon71cb0c22014-11-14 12:10:14 -080098 fMaxCount = count;
99 fMaxBytes = bytes;
bsalomon3f324322015-04-08 11:01:54 -0700100 fMaxUnusedFlushes = maxUnusedFlushes;
bsalomon71cb0c22014-11-14 12:10:14 -0800101 this->purgeAsNeeded();
102}
103
bsalomon0ea80f42015-02-11 10:49:59 -0800104void GrResourceCache::insertResource(GrGpuResource* resource) {
bsalomon49f085d2014-09-05 13:34:00 -0700105 SkASSERT(resource);
bsalomon16961262014-08-26 14:01:07 -0700106 SkASSERT(!this->isInCache(resource));
bsalomonf320e042015-02-17 15:09:34 -0800107 SkASSERT(!resource->wasDestroyed());
108 SkASSERT(!resource->isPurgeable());
bsalomonddf30e62015-02-19 11:38:44 -0800109
110 // We must set the timestamp before adding to the array in case the timestamp wraps and we wind
111 // up iterating over all the resources that already have timestamps.
112 resource->cacheAccess().setTimestamp(this->getNextTimestamp());
113
bsalomonf320e042015-02-17 15:09:34 -0800114 this->addToNonpurgeableArray(resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800115
bsalomondace19e2014-11-17 07:34:06 -0800116 size_t size = resource->gpuMemorySize();
bsalomonf320e042015-02-17 15:09:34 -0800117 SkDEBUGCODE(++fCount;)
bsalomon84c8e622014-11-17 09:33:27 -0800118 fBytes += size;
bsalomon82b1d622014-11-14 13:59:57 -0800119#if GR_CACHE_STATS
bsalomonf320e042015-02-17 15:09:34 -0800120 fHighWaterCount = SkTMax(this->getResourceCount(), fHighWaterCount);
bsalomon82b1d622014-11-14 13:59:57 -0800121 fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes);
122#endif
bsalomon5ec26ae2016-02-25 08:33:02 -0800123 if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
bsalomondace19e2014-11-17 07:34:06 -0800124 ++fBudgetedCount;
125 fBudgetedBytes += size;
Brian Osman39c08ac2017-07-26 09:36:09 -0400126 TRACE_COUNTER2("skia.gpu.cache", "skia budget", "used",
hendrikw876c3132015-03-04 10:33:49 -0800127 fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
bsalomondace19e2014-11-17 07:34:06 -0800128#if GR_CACHE_STATS
129 fBudgetedHighWaterCount = SkTMax(fBudgetedCount, fBudgetedHighWaterCount);
130 fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
131#endif
132 }
robertphillipsc4ed6842016-05-24 14:17:12 -0700133 if (resource->resourcePriv().getScratchKey().isValid() &&
134 !resource->getUniqueKey().isValid()) {
kkinnunen2e6055b2016-04-22 01:48:29 -0700135 SkASSERT(!resource->resourcePriv().refsWrappedObjects());
bsalomon3582d3e2015-02-13 14:20:05 -0800136 fScratchMap.insert(resource->resourcePriv().getScratchKey(), resource);
bsalomon744998e2014-08-28 09:54:34 -0700137 }
bsalomon9f2d1572015-02-17 11:47:40 -0800138
bsalomon71cb0c22014-11-14 12:10:14 -0800139 this->purgeAsNeeded();
bsalomonc8dc1f72014-08-21 13:02:13 -0700140}
141
bsalomon0ea80f42015-02-11 10:49:59 -0800142void GrResourceCache::removeResource(GrGpuResource* resource) {
bsalomon9f2d1572015-02-17 11:47:40 -0800143 this->validate();
bsalomon16961262014-08-26 14:01:07 -0700144 SkASSERT(this->isInCache(resource));
bsalomondace19e2014-11-17 07:34:06 -0800145
Derek Sollenbergeree479142017-05-24 11:41:33 -0400146 size_t size = resource->gpuMemorySize();
bsalomon9f2d1572015-02-17 11:47:40 -0800147 if (resource->isPurgeable()) {
148 fPurgeableQueue.remove(resource);
Derek Sollenbergeree479142017-05-24 11:41:33 -0400149 fPurgeableBytes -= size;
bsalomonf320e042015-02-17 15:09:34 -0800150 } else {
151 this->removeFromNonpurgeableArray(resource);
bsalomon9f2d1572015-02-17 11:47:40 -0800152 }
153
bsalomonf320e042015-02-17 15:09:34 -0800154 SkDEBUGCODE(--fCount;)
bsalomondace19e2014-11-17 07:34:06 -0800155 fBytes -= size;
bsalomon5ec26ae2016-02-25 08:33:02 -0800156 if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
bsalomondace19e2014-11-17 07:34:06 -0800157 --fBudgetedCount;
158 fBudgetedBytes -= size;
Brian Osman39c08ac2017-07-26 09:36:09 -0400159 TRACE_COUNTER2("skia.gpu.cache", "skia budget", "used",
hendrikw876c3132015-03-04 10:33:49 -0800160 fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
bsalomondace19e2014-11-17 07:34:06 -0800161 }
162
robertphillipsc4ed6842016-05-24 14:17:12 -0700163 if (resource->resourcePriv().getScratchKey().isValid() &&
164 !resource->getUniqueKey().isValid()) {
bsalomon3582d3e2015-02-13 14:20:05 -0800165 fScratchMap.remove(resource->resourcePriv().getScratchKey(), resource);
bsalomon744998e2014-08-28 09:54:34 -0700166 }
bsalomon8718aaf2015-02-19 07:24:21 -0800167 if (resource->getUniqueKey().isValid()) {
168 fUniqueHash.remove(resource->getUniqueKey());
bsalomon8b79d232014-11-10 10:19:06 -0800169 }
bsalomonb436ed62014-11-17 12:15:56 -0800170 this->validate();
bsalomonc8dc1f72014-08-21 13:02:13 -0700171}
172
bsalomon0ea80f42015-02-11 10:49:59 -0800173void GrResourceCache::abandonAll() {
bsalomon71cb0c22014-11-14 12:10:14 -0800174 AutoValidate av(this);
175
bsalomonf320e042015-02-17 15:09:34 -0800176 while (fNonpurgeableResources.count()) {
177 GrGpuResource* back = *(fNonpurgeableResources.end() - 1);
178 SkASSERT(!back->wasDestroyed());
179 back->cacheAccess().abandon();
bsalomonc8dc1f72014-08-21 13:02:13 -0700180 }
bsalomonf320e042015-02-17 15:09:34 -0800181
182 while (fPurgeableQueue.count()) {
183 GrGpuResource* top = fPurgeableQueue.peek();
184 SkASSERT(!top->wasDestroyed());
185 top->cacheAccess().abandon();
186 }
187
bsalomon744998e2014-08-28 09:54:34 -0700188 SkASSERT(!fScratchMap.count());
bsalomon8718aaf2015-02-19 07:24:21 -0800189 SkASSERT(!fUniqueHash.count());
bsalomonc8dc1f72014-08-21 13:02:13 -0700190 SkASSERT(!fCount);
bsalomonf320e042015-02-17 15:09:34 -0800191 SkASSERT(!this->getResourceCount());
bsalomondace19e2014-11-17 07:34:06 -0800192 SkASSERT(!fBytes);
193 SkASSERT(!fBudgetedCount);
194 SkASSERT(!fBudgetedBytes);
Derek Sollenbergeree479142017-05-24 11:41:33 -0400195 SkASSERT(!fPurgeableBytes);
bsalomonc8dc1f72014-08-21 13:02:13 -0700196}
197
bsalomon0ea80f42015-02-11 10:49:59 -0800198void GrResourceCache::releaseAll() {
bsalomon71cb0c22014-11-14 12:10:14 -0800199 AutoValidate av(this);
200
Brian Osman13dddce2017-05-09 13:19:50 -0400201 this->processFreedGpuResources();
202
Robert Phillips1afd4cd2018-01-08 13:40:32 -0500203 SkASSERT(fProxyProvider); // better have called setProxyProvider
Robert Phillips3ec95732017-09-29 15:10:39 -0400204 // We must remove the uniqueKeys from the proxies here. While they possess a uniqueKey
205 // they also have a raw pointer back to this class (which is presumably going away)!
Robert Phillips1afd4cd2018-01-08 13:40:32 -0500206 fProxyProvider->removeAllUniqueKeys();
Robert Phillips45a6f142017-09-29 09:49:41 -0400207
bsalomonf320e042015-02-17 15:09:34 -0800208 while(fNonpurgeableResources.count()) {
209 GrGpuResource* back = *(fNonpurgeableResources.end() - 1);
210 SkASSERT(!back->wasDestroyed());
211 back->cacheAccess().release();
bsalomonc8dc1f72014-08-21 13:02:13 -0700212 }
bsalomonf320e042015-02-17 15:09:34 -0800213
214 while (fPurgeableQueue.count()) {
215 GrGpuResource* top = fPurgeableQueue.peek();
216 SkASSERT(!top->wasDestroyed());
217 top->cacheAccess().release();
218 }
219
bsalomon744998e2014-08-28 09:54:34 -0700220 SkASSERT(!fScratchMap.count());
bsalomon8718aaf2015-02-19 07:24:21 -0800221 SkASSERT(!fUniqueHash.count());
bsalomonc8dc1f72014-08-21 13:02:13 -0700222 SkASSERT(!fCount);
bsalomonf320e042015-02-17 15:09:34 -0800223 SkASSERT(!this->getResourceCount());
bsalomondace19e2014-11-17 07:34:06 -0800224 SkASSERT(!fBytes);
225 SkASSERT(!fBudgetedCount);
226 SkASSERT(!fBudgetedBytes);
Derek Sollenbergeree479142017-05-24 11:41:33 -0400227 SkASSERT(!fPurgeableBytes);
bsalomonc8dc1f72014-08-21 13:02:13 -0700228}
bsalomonbcf0a522014-10-08 08:40:09 -0700229
bsalomon0ea80f42015-02-11 10:49:59 -0800230class GrResourceCache::AvailableForScratchUse {
bsalomonbcf0a522014-10-08 08:40:09 -0700231public:
bsalomon000f8292014-10-15 19:04:14 -0700232 AvailableForScratchUse(bool rejectPendingIO) : fRejectPendingIO(rejectPendingIO) { }
bsalomonbcf0a522014-10-08 08:40:09 -0700233
234 bool operator()(const GrGpuResource* resource) const {
robertphillipsc4ed6842016-05-24 14:17:12 -0700235 SkASSERT(!resource->getUniqueKey().isValid() &&
236 resource->resourcePriv().getScratchKey().isValid());
bsalomon12299ab2014-11-14 13:33:09 -0800237 if (resource->internalHasRef() || !resource->cacheAccess().isScratch()) {
bsalomon000f8292014-10-15 19:04:14 -0700238 return false;
bsalomonbcf0a522014-10-08 08:40:09 -0700239 }
bsalomon000f8292014-10-15 19:04:14 -0700240 return !fRejectPendingIO || !resource->internalHasPendingIO();
bsalomonbcf0a522014-10-08 08:40:09 -0700241 }
bsalomon1e2530b2014-10-09 09:57:18 -0700242
bsalomonbcf0a522014-10-08 08:40:09 -0700243private:
bsalomon000f8292014-10-15 19:04:14 -0700244 bool fRejectPendingIO;
bsalomonbcf0a522014-10-08 08:40:09 -0700245};
246
bsalomon0ea80f42015-02-11 10:49:59 -0800247GrGpuResource* GrResourceCache::findAndRefScratchResource(const GrScratchKey& scratchKey,
robertphillips6e83ac72015-08-13 05:19:14 -0700248 size_t resourceSize,
bsalomon9f2d1572015-02-17 11:47:40 -0800249 uint32_t flags) {
bsalomon7775c852014-12-30 12:50:52 -0800250 SkASSERT(scratchKey.isValid());
robertphillipsee843b22016-10-04 05:30:20 -0700251
bsalomon71cb0c22014-11-14 12:10:14 -0800252 GrGpuResource* resource;
bsalomon000f8292014-10-15 19:04:14 -0700253 if (flags & (kPreferNoPendingIO_ScratchFlag | kRequireNoPendingIO_ScratchFlag)) {
bsalomon71cb0c22014-11-14 12:10:14 -0800254 resource = fScratchMap.find(scratchKey, AvailableForScratchUse(true));
bsalomon000f8292014-10-15 19:04:14 -0700255 if (resource) {
bsalomon9f2d1572015-02-17 11:47:40 -0800256 this->refAndMakeResourceMRU(resource);
bsalomonb436ed62014-11-17 12:15:56 -0800257 this->validate();
258 return resource;
bsalomon000f8292014-10-15 19:04:14 -0700259 } else if (flags & kRequireNoPendingIO_ScratchFlag) {
halcanary96fcdcc2015-08-27 07:41:13 -0700260 return nullptr;
bsalomon000f8292014-10-15 19:04:14 -0700261 }
robertphillips63926682015-08-20 09:39:02 -0700262 // We would prefer to consume more available VRAM rather than flushing
263 // immediately, but on ANGLE this can lead to starving of the GPU.
264 if (fPreferVRAMUseOverFlushes && this->wouldFit(resourceSize)) {
robertphillips6e83ac72015-08-13 05:19:14 -0700265 // kPrefer is specified, we didn't find a resource without pending io,
robertphillips63926682015-08-20 09:39:02 -0700266 // but there is still space in our budget for the resource so force
267 // the caller to allocate a new resource.
halcanary96fcdcc2015-08-27 07:41:13 -0700268 return nullptr;
robertphillips6e83ac72015-08-13 05:19:14 -0700269 }
bsalomon000f8292014-10-15 19:04:14 -0700270 }
bsalomon71cb0c22014-11-14 12:10:14 -0800271 resource = fScratchMap.find(scratchKey, AvailableForScratchUse(false));
272 if (resource) {
bsalomon9f2d1572015-02-17 11:47:40 -0800273 this->refAndMakeResourceMRU(resource);
bsalomonb436ed62014-11-17 12:15:56 -0800274 this->validate();
bsalomon71cb0c22014-11-14 12:10:14 -0800275 }
276 return resource;
bsalomonbcf0a522014-10-08 08:40:09 -0700277}
bsalomon8b79d232014-11-10 10:19:06 -0800278
bsalomon0ea80f42015-02-11 10:49:59 -0800279void GrResourceCache::willRemoveScratchKey(const GrGpuResource* resource) {
bsalomon3582d3e2015-02-13 14:20:05 -0800280 SkASSERT(resource->resourcePriv().getScratchKey().isValid());
robertphillipsc4ed6842016-05-24 14:17:12 -0700281 if (!resource->getUniqueKey().isValid()) {
282 fScratchMap.remove(resource->resourcePriv().getScratchKey(), resource);
283 }
bsalomon10e23ca2014-11-25 05:52:06 -0800284}
285
bsalomonf99e9612015-02-19 08:24:16 -0800286void GrResourceCache::removeUniqueKey(GrGpuResource* resource) {
bsalomon3f324322015-04-08 11:01:54 -0700287 // Someone has a ref to this resource in order to have removed the key. When the ref count
288 // reaches zero we will get a ref cnt notification and figure out what to do with it.
bsalomonf99e9612015-02-19 08:24:16 -0800289 if (resource->getUniqueKey().isValid()) {
290 SkASSERT(resource == fUniqueHash.find(resource->getUniqueKey()));
291 fUniqueHash.remove(resource->getUniqueKey());
292 }
293 resource->cacheAccess().removeUniqueKey();
robertphillipsc4ed6842016-05-24 14:17:12 -0700294
295 if (resource->resourcePriv().getScratchKey().isValid()) {
296 fScratchMap.insert(resource->resourcePriv().getScratchKey(), resource);
297 }
298
bsalomonf99e9612015-02-19 08:24:16 -0800299 this->validate();
bsalomon23e619c2015-02-06 11:54:28 -0800300}
301
bsalomonf99e9612015-02-19 08:24:16 -0800302void GrResourceCache::changeUniqueKey(GrGpuResource* resource, const GrUniqueKey& newKey) {
bsalomon8b79d232014-11-10 10:19:06 -0800303 SkASSERT(resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800304 SkASSERT(this->isInCache(resource));
bsalomon8b79d232014-11-10 10:19:06 -0800305
bsalomonf99e9612015-02-19 08:24:16 -0800306 // If another resource has the new key, remove its key then install the key on this resource.
307 if (newKey.isValid()) {
Greg Daniel0d537802017-09-08 11:44:14 -0400308 if (GrGpuResource* old = fUniqueHash.find(newKey)) {
309 // If the old resource using the key is purgeable and is unreachable, then remove it.
310 if (!old->resourcePriv().getScratchKey().isValid() && old->isPurgeable()) {
311 old->cacheAccess().release();
312 } else {
313 this->removeUniqueKey(old);
314 }
315 }
316 SkASSERT(nullptr == fUniqueHash.find(newKey));
317
robertphillipsc4ed6842016-05-24 14:17:12 -0700318 // Remove the entry for this resource if it already has a unique key.
319 if (resource->getUniqueKey().isValid()) {
320 SkASSERT(resource == fUniqueHash.find(resource->getUniqueKey()));
321 fUniqueHash.remove(resource->getUniqueKey());
322 SkASSERT(nullptr == fUniqueHash.find(resource->getUniqueKey()));
323 } else {
324 // 'resource' didn't have a valid unique key before so it is switching sides. Remove it
325 // from the ScratchMap
326 if (resource->resourcePriv().getScratchKey().isValid()) {
327 fScratchMap.remove(resource->resourcePriv().getScratchKey(), resource);
328 }
329 }
330
bsalomonf99e9612015-02-19 08:24:16 -0800331 resource->cacheAccess().setUniqueKey(newKey);
332 fUniqueHash.add(resource);
333 } else {
robertphillipsc4ed6842016-05-24 14:17:12 -0700334 this->removeUniqueKey(resource);
bsalomonf99e9612015-02-19 08:24:16 -0800335 }
336
bsalomon71cb0c22014-11-14 12:10:14 -0800337 this->validate();
bsalomon8b79d232014-11-10 10:19:06 -0800338}
bsalomon71cb0c22014-11-14 12:10:14 -0800339
bsalomon9f2d1572015-02-17 11:47:40 -0800340void GrResourceCache::refAndMakeResourceMRU(GrGpuResource* resource) {
bsalomon71cb0c22014-11-14 12:10:14 -0800341 SkASSERT(resource);
342 SkASSERT(this->isInCache(resource));
bsalomonddf30e62015-02-19 11:38:44 -0800343
bsalomon9f2d1572015-02-17 11:47:40 -0800344 if (resource->isPurgeable()) {
345 // It's about to become unpurgeable.
Derek Sollenbergeree479142017-05-24 11:41:33 -0400346 fPurgeableBytes -= resource->gpuMemorySize();
bsalomon9f2d1572015-02-17 11:47:40 -0800347 fPurgeableQueue.remove(resource);
bsalomonf320e042015-02-17 15:09:34 -0800348 this->addToNonpurgeableArray(resource);
bsalomon9f2d1572015-02-17 11:47:40 -0800349 }
350 resource->ref();
bsalomonddf30e62015-02-19 11:38:44 -0800351
352 resource->cacheAccess().setTimestamp(this->getNextTimestamp());
bsalomonf320e042015-02-17 15:09:34 -0800353 this->validate();
bsalomon71cb0c22014-11-14 12:10:14 -0800354}
355
bsalomon3f324322015-04-08 11:01:54 -0700356void GrResourceCache::notifyCntReachedZero(GrGpuResource* resource, uint32_t flags) {
bsalomon71cb0c22014-11-14 12:10:14 -0800357 SkASSERT(resource);
bsalomon3f324322015-04-08 11:01:54 -0700358 SkASSERT(!resource->wasDestroyed());
359 SkASSERT(flags);
bsalomon71cb0c22014-11-14 12:10:14 -0800360 SkASSERT(this->isInCache(resource));
bsalomon3f324322015-04-08 11:01:54 -0700361 // This resource should always be in the nonpurgeable array when this function is called. It
362 // will be moved to the queue if it is newly purgeable.
363 SkASSERT(fNonpurgeableResources[*resource->cacheAccess().accessCacheIndex()] == resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800364
bsalomon3f324322015-04-08 11:01:54 -0700365 if (SkToBool(ResourceAccess::kRefCntReachedZero_RefNotificationFlag & flags)) {
366#ifdef SK_DEBUG
367 // When the timestamp overflows validate() is called. validate() checks that resources in
368 // the nonpurgeable array are indeed not purgeable. However, the movement from the array to
369 // the purgeable queue happens just below in this function. So we mark it as an exception.
370 if (resource->isPurgeable()) {
371 fNewlyPurgeableResourceForValidation = resource;
372 }
373#endif
374 resource->cacheAccess().setTimestamp(this->getNextTimestamp());
halcanary96fcdcc2015-08-27 07:41:13 -0700375 SkDEBUGCODE(fNewlyPurgeableResourceForValidation = nullptr);
bsalomon3f324322015-04-08 11:01:54 -0700376 }
377
378 if (!SkToBool(ResourceAccess::kAllCntsReachedZero_RefNotificationFlag & flags)) {
379 SkASSERT(!resource->isPurgeable());
380 return;
381 }
382
383 SkASSERT(resource->isPurgeable());
bsalomonf320e042015-02-17 15:09:34 -0800384 this->removeFromNonpurgeableArray(resource);
bsalomon9f2d1572015-02-17 11:47:40 -0800385 fPurgeableQueue.insert(resource);
bsalomone2e87f32016-09-22 12:42:11 -0700386 resource->cacheAccess().setFlushCntWhenResourceBecamePurgeable(fExternalFlushCnt);
Brian Salomon5e150852017-03-22 14:53:13 -0400387 resource->cacheAccess().setTimeWhenResourceBecomePurgeable();
Derek Sollenbergeree479142017-05-24 11:41:33 -0400388 fPurgeableBytes += resource->gpuMemorySize();
bsalomon71cb0c22014-11-14 12:10:14 -0800389
bsalomon5ec26ae2016-02-25 08:33:02 -0800390 if (SkBudgeted::kNo == resource->resourcePriv().isBudgeted()) {
bsalomonc2f35b72015-01-23 07:19:22 -0800391 // Check whether this resource could still be used as a scratch resource.
kkinnunen2e6055b2016-04-22 01:48:29 -0700392 if (!resource->resourcePriv().refsWrappedObjects() &&
bsalomon9f2d1572015-02-17 11:47:40 -0800393 resource->resourcePriv().getScratchKey().isValid()) {
bsalomonc2f35b72015-01-23 07:19:22 -0800394 // We won't purge an existing resource to make room for this one.
bsalomonf320e042015-02-17 15:09:34 -0800395 if (fBudgetedCount < fMaxCount &&
396 fBudgetedBytes + resource->gpuMemorySize() <= fMaxBytes) {
bsalomon3582d3e2015-02-13 14:20:05 -0800397 resource->resourcePriv().makeBudgeted();
bsalomon9f2d1572015-02-17 11:47:40 -0800398 return;
bsalomonc2f35b72015-01-23 07:19:22 -0800399 }
bsalomonc2f35b72015-01-23 07:19:22 -0800400 }
401 } else {
bsalomon9f2d1572015-02-17 11:47:40 -0800402 // Purge the resource immediately if we're over budget
bsalomon8718aaf2015-02-19 07:24:21 -0800403 // Also purge if the resource has neither a valid scratch key nor a unique key.
robertphillipsee843b22016-10-04 05:30:20 -0700404 bool noKey = !resource->resourcePriv().getScratchKey().isValid() &&
405 !resource->getUniqueKey().isValid();
406 if (!this->overBudget() && !noKey) {
bsalomon9f2d1572015-02-17 11:47:40 -0800407 return;
bsalomonc2f35b72015-01-23 07:19:22 -0800408 }
409 }
bsalomondace19e2014-11-17 07:34:06 -0800410
bsalomonf320e042015-02-17 15:09:34 -0800411 SkDEBUGCODE(int beforeCount = this->getResourceCount();)
bsalomon9f2d1572015-02-17 11:47:40 -0800412 resource->cacheAccess().release();
413 // We should at least free this resource, perhaps dependent resources as well.
bsalomonf320e042015-02-17 15:09:34 -0800414 SkASSERT(this->getResourceCount() < beforeCount);
bsalomon71cb0c22014-11-14 12:10:14 -0800415 this->validate();
416}
417
bsalomon0ea80f42015-02-11 10:49:59 -0800418void GrResourceCache::didChangeBudgetStatus(GrGpuResource* resource) {
bsalomon84c8e622014-11-17 09:33:27 -0800419 SkASSERT(resource);
420 SkASSERT(this->isInCache(resource));
421
422 size_t size = resource->gpuMemorySize();
423
bsalomon5ec26ae2016-02-25 08:33:02 -0800424 if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
bsalomon84c8e622014-11-17 09:33:27 -0800425 ++fBudgetedCount;
426 fBudgetedBytes += size;
bsalomonafe30052015-01-16 07:32:33 -0800427#if GR_CACHE_STATS
428 fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
429 fBudgetedHighWaterCount = SkTMax(fBudgetedCount, fBudgetedHighWaterCount);
430#endif
bsalomon84c8e622014-11-17 09:33:27 -0800431 this->purgeAsNeeded();
432 } else {
433 --fBudgetedCount;
434 fBudgetedBytes -= size;
435 }
Brian Osman39c08ac2017-07-26 09:36:09 -0400436 TRACE_COUNTER2("skia.gpu.cache", "skia budget", "used",
hendrikw876c3132015-03-04 10:33:49 -0800437 fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
bsalomon84c8e622014-11-17 09:33:27 -0800438
439 this->validate();
440}
441
robertphillipsee843b22016-10-04 05:30:20 -0700442void GrResourceCache::purgeAsNeeded() {
bsalomon3f324322015-04-08 11:01:54 -0700443 SkTArray<GrUniqueKeyInvalidatedMessage> invalidKeyMsgs;
444 fInvalidUniqueKeyInbox.poll(&invalidKeyMsgs);
445 if (invalidKeyMsgs.count()) {
446 this->processInvalidUniqueKeys(invalidKeyMsgs);
447 }
bsalomon71cb0c22014-11-14 12:10:14 -0800448
Brian Osman13dddce2017-05-09 13:19:50 -0400449 this->processFreedGpuResources();
450
bsalomone2e87f32016-09-22 12:42:11 -0700451 if (fMaxUnusedFlushes > 0) {
452 // We want to know how many complete flushes have occurred without the resource being used.
453 // If the resource was tagged when fExternalFlushCnt was N then this means it became
454 // purgeable during activity that became the N+1th flush. So when the flush count is N+2
455 // it has sat in the purgeable queue for one entire flush.
456 uint32_t oldestAllowedFlushCnt = fExternalFlushCnt - fMaxUnusedFlushes - 1;
457 // check for underflow
458 if (oldestAllowedFlushCnt < fExternalFlushCnt) {
459 while (fPurgeableQueue.count()) {
460 uint32_t flushWhenResourceBecamePurgeable =
461 fPurgeableQueue.peek()->cacheAccess().flushCntWhenResourceBecamePurgeable();
462 if (oldestAllowedFlushCnt < flushWhenResourceBecamePurgeable) {
463 // Resources were given both LRU timestamps and tagged with a flush cnt when
464 // they first became purgeable. The LRU timestamp won't change again until the
465 // resource is made non-purgeable again. So, at this point all the remaining
466 // resources in the timestamp-sorted queue will have a flush count >= to this
467 // one.
468 break;
469 }
470 GrGpuResource* resource = fPurgeableQueue.peek();
471 SkASSERT(resource->isPurgeable());
472 resource->cacheAccess().release();
bsalomon3f324322015-04-08 11:01:54 -0700473 }
bsalomon3f324322015-04-08 11:01:54 -0700474 }
475 }
476
477 bool stillOverbudget = this->overBudget();
478 while (stillOverbudget && fPurgeableQueue.count()) {
robertphillipsee843b22016-10-04 05:30:20 -0700479 GrGpuResource* resource = fPurgeableQueue.peek();
bsalomon9f2d1572015-02-17 11:47:40 -0800480 SkASSERT(resource->isPurgeable());
481 resource->cacheAccess().release();
bsalomon3f324322015-04-08 11:01:54 -0700482 stillOverbudget = this->overBudget();
bsalomon9f2d1572015-02-17 11:47:40 -0800483 }
bsalomon71cb0c22014-11-14 12:10:14 -0800484
bsalomonb436ed62014-11-17 12:15:56 -0800485 this->validate();
robertphillipsee843b22016-10-04 05:30:20 -0700486
487 if (stillOverbudget) {
488 // Set this so that GrDrawingManager will issue a flush to free up resources with pending
489 // IO that we were unable to purge in this pass.
490 fRequestFlush = true;
491 }
bsalomon71cb0c22014-11-14 12:10:14 -0800492}
493
Robert Phillips6eba0632018-03-28 12:25:42 -0400494void GrResourceCache::purgeUnlockedResources(bool scratchResourcesOnly) {
495 if (!scratchResourcesOnly) {
496 // We could disable maintaining the heap property here, but it would add a lot of
497 // complexity. Moreover, this is rarely called.
498 while (fPurgeableQueue.count()) {
499 GrGpuResource* resource = fPurgeableQueue.peek();
500 SkASSERT(resource->isPurgeable());
501 resource->cacheAccess().release();
502 }
503 } else {
504 // Sort the queue
505 fPurgeableQueue.sort();
506
507 // Make a list of the scratch resources to delete
508 SkTDArray<GrGpuResource*> scratchResources;
509 for (int i = 0; i < fPurgeableQueue.count(); i++) {
510 GrGpuResource* resource = fPurgeableQueue.at(i);
511 SkASSERT(resource->isPurgeable());
512 if (!resource->getUniqueKey().isValid()) {
513 *scratchResources.append() = resource;
514 }
515 }
516
517 // Delete the scratch resources. This must be done as a separate pass
518 // to avoid messing up the sorted order of the queue
519 for (int i = 0; i < scratchResources.count(); i++) {
520 scratchResources.getAt(i)->cacheAccess().release();
521 }
bsalomon9f2d1572015-02-17 11:47:40 -0800522 }
bsalomon71cb0c22014-11-14 12:10:14 -0800523
bsalomonb436ed62014-11-17 12:15:56 -0800524 this->validate();
bsalomon71cb0c22014-11-14 12:10:14 -0800525}
526
Brian Salomon5e150852017-03-22 14:53:13 -0400527void GrResourceCache::purgeResourcesNotUsedSince(GrStdSteadyClock::time_point purgeTime) {
528 while (fPurgeableQueue.count()) {
529 const GrStdSteadyClock::time_point resourceTime =
530 fPurgeableQueue.peek()->cacheAccess().timeWhenResourceBecamePurgeable();
531 if (resourceTime >= purgeTime) {
532 // Resources were given both LRU timestamps and tagged with a frame number when
533 // they first became purgeable. The LRU timestamp won't change again until the
534 // resource is made non-purgeable again. So, at this point all the remaining
535 // resources in the timestamp-sorted queue will have a frame number >= to this
536 // one.
537 break;
538 }
539 GrGpuResource* resource = fPurgeableQueue.peek();
540 SkASSERT(resource->isPurgeable());
541 resource->cacheAccess().release();
542 }
543}
544
Derek Sollenberger5480a182017-05-25 16:43:59 -0400545void GrResourceCache::purgeUnlockedResources(size_t bytesToPurge, bool preferScratchResources) {
546
547 const size_t tmpByteBudget = SkTMax((size_t)0, fBytes - bytesToPurge);
548 bool stillOverbudget = tmpByteBudget < fBytes;
549
550 if (preferScratchResources && bytesToPurge < fPurgeableBytes) {
551 // Sort the queue
552 fPurgeableQueue.sort();
553
554 // Make a list of the scratch resources to delete
555 SkTDArray<GrGpuResource*> scratchResources;
556 size_t scratchByteCount = 0;
557 for (int i = 0; i < fPurgeableQueue.count() && stillOverbudget; i++) {
558 GrGpuResource* resource = fPurgeableQueue.at(i);
559 SkASSERT(resource->isPurgeable());
560 if (!resource->getUniqueKey().isValid()) {
561 *scratchResources.append() = resource;
562 scratchByteCount += resource->gpuMemorySize();
563 stillOverbudget = tmpByteBudget < fBytes - scratchByteCount;
564 }
565 }
566
567 // Delete the scratch resources. This must be done as a separate pass
568 // to avoid messing up the sorted order of the queue
569 for (int i = 0; i < scratchResources.count(); i++) {
570 scratchResources.getAt(i)->cacheAccess().release();
571 }
572 stillOverbudget = tmpByteBudget < fBytes;
573
574 this->validate();
575 }
576
577 // Purge any remaining resources in LRU order
578 if (stillOverbudget) {
579 const size_t cachedByteCount = fMaxBytes;
580 fMaxBytes = tmpByteBudget;
581 this->purgeAsNeeded();
582 fMaxBytes = cachedByteCount;
583 }
584}
585
bsalomon8718aaf2015-02-19 07:24:21 -0800586void GrResourceCache::processInvalidUniqueKeys(
Robert Phillips1afd4cd2018-01-08 13:40:32 -0500587 const SkTArray<GrUniqueKeyInvalidatedMessage>& msgs) {
588 SkASSERT(fProxyProvider); // better have called setProxyProvider
589
bsalomon23e619c2015-02-06 11:54:28 -0800590 for (int i = 0; i < msgs.count(); ++i) {
Robert Phillips1afd4cd2018-01-08 13:40:32 -0500591 fProxyProvider->processInvalidProxyUniqueKey(msgs[i].key());
Robert Phillipsae7d3f32017-09-21 08:26:08 -0400592
bsalomon8718aaf2015-02-19 07:24:21 -0800593 GrGpuResource* resource = this->findAndRefUniqueResource(msgs[i].key());
bsalomon23e619c2015-02-06 11:54:28 -0800594 if (resource) {
bsalomon8718aaf2015-02-19 07:24:21 -0800595 resource->resourcePriv().removeUniqueKey();
bsalomon3f324322015-04-08 11:01:54 -0700596 resource->unref(); // If this resource is now purgeable, the cache will be notified.
bsalomon23e619c2015-02-06 11:54:28 -0800597 }
598 }
599}
600
Brian Osman13dddce2017-05-09 13:19:50 -0400601void GrResourceCache::insertCrossContextGpuResource(GrGpuResource* resource) {
602 resource->ref();
603}
604
605void GrResourceCache::processFreedGpuResources() {
606 SkTArray<GrGpuResourceFreedMessage> msgs;
607 fFreedGpuResourceInbox.poll(&msgs);
608 for (int i = 0; i < msgs.count(); ++i) {
Brian Salomon238069b2018-07-11 15:58:57 -0400609 SkASSERT(msgs[i].fOwningUniqueID == fContextUniqueID);
610 msgs[i].fResource->unref();
Brian Osman13dddce2017-05-09 13:19:50 -0400611 }
612}
613
bsalomonf320e042015-02-17 15:09:34 -0800614void GrResourceCache::addToNonpurgeableArray(GrGpuResource* resource) {
615 int index = fNonpurgeableResources.count();
616 *fNonpurgeableResources.append() = resource;
617 *resource->cacheAccess().accessCacheIndex() = index;
618}
619
620void GrResourceCache::removeFromNonpurgeableArray(GrGpuResource* resource) {
621 int* index = resource->cacheAccess().accessCacheIndex();
622 // Fill the whole we will create in the array with the tail object, adjust its index, and
623 // then pop the array
624 GrGpuResource* tail = *(fNonpurgeableResources.end() - 1);
625 SkASSERT(fNonpurgeableResources[*index] == resource);
626 fNonpurgeableResources[*index] = tail;
627 *tail->cacheAccess().accessCacheIndex() = *index;
628 fNonpurgeableResources.pop();
629 SkDEBUGCODE(*index = -1);
630}
631
bsalomonddf30e62015-02-19 11:38:44 -0800632uint32_t GrResourceCache::getNextTimestamp() {
633 // If we wrap then all the existing resources will appear older than any resources that get
634 // a timestamp after the wrap.
635 if (0 == fTimestamp) {
636 int count = this->getResourceCount();
637 if (count) {
638 // Reset all the timestamps. We sort the resources by timestamp and then assign
639 // sequential timestamps beginning with 0. This is O(n*lg(n)) but it should be extremely
640 // rare.
641 SkTDArray<GrGpuResource*> sortedPurgeableResources;
642 sortedPurgeableResources.setReserve(fPurgeableQueue.count());
643
644 while (fPurgeableQueue.count()) {
645 *sortedPurgeableResources.append() = fPurgeableQueue.peek();
646 fPurgeableQueue.pop();
647 }
robertphillipsee843b22016-10-04 05:30:20 -0700648
bsalomone2e87f32016-09-22 12:42:11 -0700649 SkTQSort(fNonpurgeableResources.begin(), fNonpurgeableResources.end() - 1,
650 CompareTimestamp);
bsalomonddf30e62015-02-19 11:38:44 -0800651
652 // Pick resources out of the purgeable and non-purgeable arrays based on lowest
653 // timestamp and assign new timestamps.
654 int currP = 0;
655 int currNP = 0;
656 while (currP < sortedPurgeableResources.count() &&
mtklein56da0252015-11-16 11:16:23 -0800657 currNP < fNonpurgeableResources.count()) {
bsalomonddf30e62015-02-19 11:38:44 -0800658 uint32_t tsP = sortedPurgeableResources[currP]->cacheAccess().timestamp();
659 uint32_t tsNP = fNonpurgeableResources[currNP]->cacheAccess().timestamp();
660 SkASSERT(tsP != tsNP);
661 if (tsP < tsNP) {
662 sortedPurgeableResources[currP++]->cacheAccess().setTimestamp(fTimestamp++);
663 } else {
664 // Correct the index in the nonpurgeable array stored on the resource post-sort.
665 *fNonpurgeableResources[currNP]->cacheAccess().accessCacheIndex() = currNP;
666 fNonpurgeableResources[currNP++]->cacheAccess().setTimestamp(fTimestamp++);
667 }
668 }
669
670 // The above loop ended when we hit the end of one array. Finish the other one.
671 while (currP < sortedPurgeableResources.count()) {
672 sortedPurgeableResources[currP++]->cacheAccess().setTimestamp(fTimestamp++);
673 }
674 while (currNP < fNonpurgeableResources.count()) {
675 *fNonpurgeableResources[currNP]->cacheAccess().accessCacheIndex() = currNP;
676 fNonpurgeableResources[currNP++]->cacheAccess().setTimestamp(fTimestamp++);
677 }
678
679 // Rebuild the queue.
680 for (int i = 0; i < sortedPurgeableResources.count(); ++i) {
681 fPurgeableQueue.insert(sortedPurgeableResources[i]);
682 }
683
684 this->validate();
685 SkASSERT(count == this->getResourceCount());
686
687 // count should be the next timestamp we return.
688 SkASSERT(fTimestamp == SkToU32(count));
mtklein56da0252015-11-16 11:16:23 -0800689 }
bsalomonddf30e62015-02-19 11:38:44 -0800690 }
691 return fTimestamp++;
692}
693
bsalomonb77a9072016-09-07 10:02:04 -0700694void GrResourceCache::notifyFlushOccurred(FlushType type) {
695 switch (type) {
bsalomonb77a9072016-09-07 10:02:04 -0700696 case FlushType::kCacheRequested:
robertphillipsee843b22016-10-04 05:30:20 -0700697 SkASSERT(fRequestFlush);
698 fRequestFlush = false;
bsalomonb77a9072016-09-07 10:02:04 -0700699 break;
robertphillipsee843b22016-10-04 05:30:20 -0700700 case FlushType::kExternal:
bsalomone2e87f32016-09-22 12:42:11 -0700701 ++fExternalFlushCnt;
702 if (0 == fExternalFlushCnt) {
703 // When this wraps just reset all the purgeable resources' last used flush state.
704 for (int i = 0; i < fPurgeableQueue.count(); ++i) {
705 fPurgeableQueue.at(i)->cacheAccess().setFlushCntWhenResourceBecamePurgeable(0);
706 }
bsalomonb77a9072016-09-07 10:02:04 -0700707 }
708 break;
bsalomon3f324322015-04-08 11:01:54 -0700709 }
robertphillipsee843b22016-10-04 05:30:20 -0700710 this->purgeAsNeeded();
bsalomon3f324322015-04-08 11:01:54 -0700711}
712
ericrk0a5fa482015-09-15 14:16:10 -0700713void GrResourceCache::dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const {
714 for (int i = 0; i < fNonpurgeableResources.count(); ++i) {
715 fNonpurgeableResources[i]->dumpMemoryStatistics(traceMemoryDump);
716 }
717 for (int i = 0; i < fPurgeableQueue.count(); ++i) {
718 fPurgeableQueue.at(i)->dumpMemoryStatistics(traceMemoryDump);
719 }
720}
721
bsalomon71cb0c22014-11-14 12:10:14 -0800722#ifdef SK_DEBUG
bsalomon0ea80f42015-02-11 10:49:59 -0800723void GrResourceCache::validate() const {
bsalomonc2f35b72015-01-23 07:19:22 -0800724 // Reduce the frequency of validations for large resource counts.
725 static SkRandom gRandom;
726 int mask = (SkNextPow2(fCount + 1) >> 5) - 1;
727 if (~mask && (gRandom.nextU() & mask)) {
728 return;
729 }
730
bsalomonf320e042015-02-17 15:09:34 -0800731 struct Stats {
732 size_t fBytes;
733 int fBudgetedCount;
734 size_t fBudgetedBytes;
735 int fLocked;
736 int fScratch;
737 int fCouldBeScratch;
738 int fContent;
739 const ScratchMap* fScratchMap;
bsalomon8718aaf2015-02-19 07:24:21 -0800740 const UniqueHash* fUniqueHash;
bsalomon71cb0c22014-11-14 12:10:14 -0800741
bsalomonf320e042015-02-17 15:09:34 -0800742 Stats(const GrResourceCache* cache) {
743 memset(this, 0, sizeof(*this));
744 fScratchMap = &cache->fScratchMap;
bsalomon8718aaf2015-02-19 07:24:21 -0800745 fUniqueHash = &cache->fUniqueHash;
bsalomon71cb0c22014-11-14 12:10:14 -0800746 }
747
bsalomonf320e042015-02-17 15:09:34 -0800748 void update(GrGpuResource* resource) {
749 fBytes += resource->gpuMemorySize();
bsalomondace19e2014-11-17 07:34:06 -0800750
bsalomonf320e042015-02-17 15:09:34 -0800751 if (!resource->isPurgeable()) {
752 ++fLocked;
753 }
bsalomon9f2d1572015-02-17 11:47:40 -0800754
robertphillipsc4ed6842016-05-24 14:17:12 -0700755 const GrScratchKey& scratchKey = resource->resourcePriv().getScratchKey();
756 const GrUniqueKey& uniqueKey = resource->getUniqueKey();
757
bsalomonf320e042015-02-17 15:09:34 -0800758 if (resource->cacheAccess().isScratch()) {
robertphillipsc4ed6842016-05-24 14:17:12 -0700759 SkASSERT(!uniqueKey.isValid());
bsalomonf320e042015-02-17 15:09:34 -0800760 ++fScratch;
robertphillipsc4ed6842016-05-24 14:17:12 -0700761 SkASSERT(fScratchMap->countForKey(scratchKey));
kkinnunen2e6055b2016-04-22 01:48:29 -0700762 SkASSERT(!resource->resourcePriv().refsWrappedObjects());
robertphillipsc4ed6842016-05-24 14:17:12 -0700763 } else if (scratchKey.isValid()) {
bsalomon5ec26ae2016-02-25 08:33:02 -0800764 SkASSERT(SkBudgeted::kNo == resource->resourcePriv().isBudgeted() ||
robertphillipsc4ed6842016-05-24 14:17:12 -0700765 uniqueKey.isValid());
766 if (!uniqueKey.isValid()) {
mtklein4e976072016-08-08 09:06:27 -0700767 ++fCouldBeScratch;
robertphillipsc4ed6842016-05-24 14:17:12 -0700768 SkASSERT(fScratchMap->countForKey(scratchKey));
769 }
kkinnunen2e6055b2016-04-22 01:48:29 -0700770 SkASSERT(!resource->resourcePriv().refsWrappedObjects());
bsalomonf320e042015-02-17 15:09:34 -0800771 }
bsalomon8718aaf2015-02-19 07:24:21 -0800772 if (uniqueKey.isValid()) {
bsalomonf320e042015-02-17 15:09:34 -0800773 ++fContent;
bsalomon8718aaf2015-02-19 07:24:21 -0800774 SkASSERT(fUniqueHash->find(uniqueKey) == resource);
Brian Osman0562eb92017-05-08 11:16:39 -0400775 SkASSERT(SkBudgeted::kYes == resource->resourcePriv().isBudgeted() ||
776 resource->resourcePriv().refsWrappedObjects());
robertphillipsc4ed6842016-05-24 14:17:12 -0700777
778 if (scratchKey.isValid()) {
779 SkASSERT(!fScratchMap->has(resource, scratchKey));
780 }
bsalomonf320e042015-02-17 15:09:34 -0800781 }
782
bsalomon5ec26ae2016-02-25 08:33:02 -0800783 if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
bsalomonf320e042015-02-17 15:09:34 -0800784 ++fBudgetedCount;
785 fBudgetedBytes += resource->gpuMemorySize();
786 }
bsalomon9f2d1572015-02-17 11:47:40 -0800787 }
bsalomonf320e042015-02-17 15:09:34 -0800788 };
789
robertphillipsc4ed6842016-05-24 14:17:12 -0700790 {
791 ScratchMap::ConstIter iter(&fScratchMap);
792
793 int count = 0;
794 for ( ; !iter.done(); ++iter) {
795 const GrGpuResource* resource = *iter;
796 SkASSERT(resource->resourcePriv().getScratchKey().isValid());
797 SkASSERT(!resource->getUniqueKey().isValid());
798 count++;
799 }
800 SkASSERT(count == fScratchMap.count()); // ensure the iterator is working correctly
801 }
802
bsalomonf320e042015-02-17 15:09:34 -0800803 Stats stats(this);
Derek Sollenbergeree479142017-05-24 11:41:33 -0400804 size_t purgeableBytes = 0;
bsalomonf320e042015-02-17 15:09:34 -0800805
806 for (int i = 0; i < fNonpurgeableResources.count(); ++i) {
bsalomon3f324322015-04-08 11:01:54 -0700807 SkASSERT(!fNonpurgeableResources[i]->isPurgeable() ||
808 fNewlyPurgeableResourceForValidation == fNonpurgeableResources[i]);
bsalomonf320e042015-02-17 15:09:34 -0800809 SkASSERT(*fNonpurgeableResources[i]->cacheAccess().accessCacheIndex() == i);
810 SkASSERT(!fNonpurgeableResources[i]->wasDestroyed());
811 stats.update(fNonpurgeableResources[i]);
bsalomon71cb0c22014-11-14 12:10:14 -0800812 }
bsalomon9f2d1572015-02-17 11:47:40 -0800813 for (int i = 0; i < fPurgeableQueue.count(); ++i) {
814 SkASSERT(fPurgeableQueue.at(i)->isPurgeable());
bsalomonf320e042015-02-17 15:09:34 -0800815 SkASSERT(*fPurgeableQueue.at(i)->cacheAccess().accessCacheIndex() == i);
816 SkASSERT(!fPurgeableQueue.at(i)->wasDestroyed());
817 stats.update(fPurgeableQueue.at(i));
Derek Sollenbergeree479142017-05-24 11:41:33 -0400818 purgeableBytes += fPurgeableQueue.at(i)->gpuMemorySize();
bsalomon9f2d1572015-02-17 11:47:40 -0800819 }
820
bsalomonf320e042015-02-17 15:09:34 -0800821 SkASSERT(fCount == this->getResourceCount());
bsalomondace19e2014-11-17 07:34:06 -0800822 SkASSERT(fBudgetedCount <= fCount);
bsalomonf320e042015-02-17 15:09:34 -0800823 SkASSERT(fBudgetedBytes <= fBytes);
824 SkASSERT(stats.fBytes == fBytes);
825 SkASSERT(stats.fBudgetedBytes == fBudgetedBytes);
826 SkASSERT(stats.fBudgetedCount == fBudgetedCount);
Derek Sollenbergeree479142017-05-24 11:41:33 -0400827 SkASSERT(purgeableBytes == fPurgeableBytes);
bsalomon71cb0c22014-11-14 12:10:14 -0800828#if GR_CACHE_STATS
bsalomondace19e2014-11-17 07:34:06 -0800829 SkASSERT(fBudgetedHighWaterCount <= fHighWaterCount);
830 SkASSERT(fBudgetedHighWaterBytes <= fHighWaterBytes);
bsalomonf320e042015-02-17 15:09:34 -0800831 SkASSERT(fBytes <= fHighWaterBytes);
832 SkASSERT(fCount <= fHighWaterCount);
833 SkASSERT(fBudgetedBytes <= fBudgetedHighWaterBytes);
834 SkASSERT(fBudgetedCount <= fBudgetedHighWaterCount);
bsalomon71cb0c22014-11-14 12:10:14 -0800835#endif
bsalomon8718aaf2015-02-19 07:24:21 -0800836 SkASSERT(stats.fContent == fUniqueHash.count());
bsalomonf320e042015-02-17 15:09:34 -0800837 SkASSERT(stats.fScratch + stats.fCouldBeScratch == fScratchMap.count());
bsalomon71cb0c22014-11-14 12:10:14 -0800838
bsalomon3f324322015-04-08 11:01:54 -0700839 // This assertion is not currently valid because we can be in recursive notifyCntReachedZero()
bsalomon12299ab2014-11-14 13:33:09 -0800840 // calls. This will be fixed when subresource registration is explicit.
bsalomondace19e2014-11-17 07:34:06 -0800841 // bool overBudget = budgetedBytes > fMaxBytes || budgetedCount > fMaxCount;
bsalomon12299ab2014-11-14 13:33:09 -0800842 // SkASSERT(!overBudget || locked == count || fPurging);
bsalomon71cb0c22014-11-14 12:10:14 -0800843}
bsalomonf320e042015-02-17 15:09:34 -0800844
845bool GrResourceCache::isInCache(const GrGpuResource* resource) const {
846 int index = *resource->cacheAccess().accessCacheIndex();
847 if (index < 0) {
848 return false;
849 }
850 if (index < fPurgeableQueue.count() && fPurgeableQueue.at(index) == resource) {
851 return true;
852 }
853 if (index < fNonpurgeableResources.count() && fNonpurgeableResources[index] == resource) {
854 return true;
855 }
856 SkDEBUGFAIL("Resource index should be -1 or the resource should be in the cache.");
857 return false;
858}
859
bsalomon71cb0c22014-11-14 12:10:14 -0800860#endif