blob: d2eb91c0e86fb6b38d277e0f50e0de22e9db5148 [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"
Robert Phillips1afd4cd2018-01-08 13:40:32 -050013#include "GrProxyProvider.h"
Robert Phillipsae7d3f32017-09-21 08:26:08 -040014#include "GrTexture.h"
15#include "GrTextureProxyCacheAccess.h"
hendrikw876c3132015-03-04 10:33:49 -080016#include "GrTracing.h"
bsalomon71cb0c22014-11-14 12:10:14 -080017#include "SkGr.h"
18#include "SkMessageBus.h"
mtklein4e976072016-08-08 09:06:27 -070019#include "SkOpts.h"
bsalomonddf30e62015-02-19 11:38:44 -080020#include "SkTSort.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);
robertphillips9790a7b2015-01-05 12:29:15 -080032 if (type > SK_MaxU16) {
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);
kkinnunen016dffb2015-01-23 06:43:05 -080043 if (domain > SK_MaxU16) {
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
bsalomon71cb0c22014-11-14 12:10:14 -080066
Brian Osman13dddce2017-05-09 13:19:50 -040067GrResourceCache::GrResourceCache(const GrCaps* caps, uint32_t contextUniqueID)
Robert Phillips1afd4cd2018-01-08 13:40:32 -050068 : fProxyProvider(nullptr)
69 , fTimestamp(0)
bsalomon9f2d1572015-02-17 11:47:40 -080070 , fMaxCount(kDefaultMaxCount)
bsalomon71cb0c22014-11-14 12:10:14 -080071 , fMaxBytes(kDefaultMaxSize)
bsalomon3f324322015-04-08 11:01:54 -070072 , fMaxUnusedFlushes(kDefaultMaxUnusedFlushes)
bsalomon71cb0c22014-11-14 12:10:14 -080073#if GR_CACHE_STATS
74 , fHighWaterCount(0)
75 , fHighWaterBytes(0)
bsalomondace19e2014-11-17 07:34:06 -080076 , fBudgetedHighWaterCount(0)
77 , fBudgetedHighWaterBytes(0)
bsalomon71cb0c22014-11-14 12:10:14 -080078#endif
bsalomon71cb0c22014-11-14 12:10:14 -080079 , fBytes(0)
bsalomondace19e2014-11-17 07:34:06 -080080 , fBudgetedCount(0)
81 , fBudgetedBytes(0)
Derek Sollenbergeree479142017-05-24 11:41:33 -040082 , fPurgeableBytes(0)
robertphillipsee843b22016-10-04 05:30:20 -070083 , fRequestFlush(false)
bsalomone2e87f32016-09-22 12:42:11 -070084 , fExternalFlushCnt(0)
Brian Osman13dddce2017-05-09 13:19:50 -040085 , fContextUniqueID(contextUniqueID)
robertphillips63926682015-08-20 09:39:02 -070086 , fPreferVRAMUseOverFlushes(caps->preferVRAMUseOverFlushes()) {
bsalomonf320e042015-02-17 15:09:34 -080087 SkDEBUGCODE(fCount = 0;)
halcanary96fcdcc2015-08-27 07:41:13 -070088 SkDEBUGCODE(fNewlyPurgeableResourceForValidation = nullptr;)
bsalomon71cb0c22014-11-14 12:10:14 -080089}
90
bsalomon0ea80f42015-02-11 10:49:59 -080091GrResourceCache::~GrResourceCache() {
bsalomonc8dc1f72014-08-21 13:02:13 -070092 this->releaseAll();
93}
94
bsalomon3f324322015-04-08 11:01:54 -070095void GrResourceCache::setLimits(int count, size_t bytes, int maxUnusedFlushes) {
bsalomon71cb0c22014-11-14 12:10:14 -080096 fMaxCount = count;
97 fMaxBytes = bytes;
bsalomon3f324322015-04-08 11:01:54 -070098 fMaxUnusedFlushes = maxUnusedFlushes;
bsalomon71cb0c22014-11-14 12:10:14 -080099 this->purgeAsNeeded();
100}
101
bsalomon0ea80f42015-02-11 10:49:59 -0800102void GrResourceCache::insertResource(GrGpuResource* resource) {
bsalomon49f085d2014-09-05 13:34:00 -0700103 SkASSERT(resource);
bsalomon16961262014-08-26 14:01:07 -0700104 SkASSERT(!this->isInCache(resource));
bsalomonf320e042015-02-17 15:09:34 -0800105 SkASSERT(!resource->wasDestroyed());
106 SkASSERT(!resource->isPurgeable());
bsalomonddf30e62015-02-19 11:38:44 -0800107
108 // We must set the timestamp before adding to the array in case the timestamp wraps and we wind
109 // up iterating over all the resources that already have timestamps.
110 resource->cacheAccess().setTimestamp(this->getNextTimestamp());
111
bsalomonf320e042015-02-17 15:09:34 -0800112 this->addToNonpurgeableArray(resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800113
bsalomondace19e2014-11-17 07:34:06 -0800114 size_t size = resource->gpuMemorySize();
bsalomonf320e042015-02-17 15:09:34 -0800115 SkDEBUGCODE(++fCount;)
bsalomon84c8e622014-11-17 09:33:27 -0800116 fBytes += size;
bsalomon82b1d622014-11-14 13:59:57 -0800117#if GR_CACHE_STATS
bsalomonf320e042015-02-17 15:09:34 -0800118 fHighWaterCount = SkTMax(this->getResourceCount(), fHighWaterCount);
bsalomon82b1d622014-11-14 13:59:57 -0800119 fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes);
120#endif
bsalomon5ec26ae2016-02-25 08:33:02 -0800121 if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
bsalomondace19e2014-11-17 07:34:06 -0800122 ++fBudgetedCount;
123 fBudgetedBytes += size;
Brian Osman39c08ac2017-07-26 09:36:09 -0400124 TRACE_COUNTER2("skia.gpu.cache", "skia budget", "used",
hendrikw876c3132015-03-04 10:33:49 -0800125 fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
bsalomondace19e2014-11-17 07:34:06 -0800126#if GR_CACHE_STATS
127 fBudgetedHighWaterCount = SkTMax(fBudgetedCount, fBudgetedHighWaterCount);
128 fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
129#endif
130 }
robertphillipsc4ed6842016-05-24 14:17:12 -0700131 if (resource->resourcePriv().getScratchKey().isValid() &&
132 !resource->getUniqueKey().isValid()) {
kkinnunen2e6055b2016-04-22 01:48:29 -0700133 SkASSERT(!resource->resourcePriv().refsWrappedObjects());
bsalomon3582d3e2015-02-13 14:20:05 -0800134 fScratchMap.insert(resource->resourcePriv().getScratchKey(), resource);
bsalomon744998e2014-08-28 09:54:34 -0700135 }
bsalomon9f2d1572015-02-17 11:47:40 -0800136
bsalomon71cb0c22014-11-14 12:10:14 -0800137 this->purgeAsNeeded();
bsalomonc8dc1f72014-08-21 13:02:13 -0700138}
139
bsalomon0ea80f42015-02-11 10:49:59 -0800140void GrResourceCache::removeResource(GrGpuResource* resource) {
bsalomon9f2d1572015-02-17 11:47:40 -0800141 this->validate();
bsalomon16961262014-08-26 14:01:07 -0700142 SkASSERT(this->isInCache(resource));
bsalomondace19e2014-11-17 07:34:06 -0800143
Derek Sollenbergeree479142017-05-24 11:41:33 -0400144 size_t size = resource->gpuMemorySize();
bsalomon9f2d1572015-02-17 11:47:40 -0800145 if (resource->isPurgeable()) {
146 fPurgeableQueue.remove(resource);
Derek Sollenbergeree479142017-05-24 11:41:33 -0400147 fPurgeableBytes -= size;
bsalomonf320e042015-02-17 15:09:34 -0800148 } else {
149 this->removeFromNonpurgeableArray(resource);
bsalomon9f2d1572015-02-17 11:47:40 -0800150 }
151
bsalomonf320e042015-02-17 15:09:34 -0800152 SkDEBUGCODE(--fCount;)
bsalomondace19e2014-11-17 07:34:06 -0800153 fBytes -= size;
bsalomon5ec26ae2016-02-25 08:33:02 -0800154 if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
bsalomondace19e2014-11-17 07:34:06 -0800155 --fBudgetedCount;
156 fBudgetedBytes -= size;
Brian Osman39c08ac2017-07-26 09:36:09 -0400157 TRACE_COUNTER2("skia.gpu.cache", "skia budget", "used",
hendrikw876c3132015-03-04 10:33:49 -0800158 fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
bsalomondace19e2014-11-17 07:34:06 -0800159 }
160
robertphillipsc4ed6842016-05-24 14:17:12 -0700161 if (resource->resourcePriv().getScratchKey().isValid() &&
162 !resource->getUniqueKey().isValid()) {
bsalomon3582d3e2015-02-13 14:20:05 -0800163 fScratchMap.remove(resource->resourcePriv().getScratchKey(), resource);
bsalomon744998e2014-08-28 09:54:34 -0700164 }
bsalomon8718aaf2015-02-19 07:24:21 -0800165 if (resource->getUniqueKey().isValid()) {
166 fUniqueHash.remove(resource->getUniqueKey());
bsalomon8b79d232014-11-10 10:19:06 -0800167 }
bsalomonb436ed62014-11-17 12:15:56 -0800168 this->validate();
bsalomonc8dc1f72014-08-21 13:02:13 -0700169}
170
bsalomon0ea80f42015-02-11 10:49:59 -0800171void GrResourceCache::abandonAll() {
bsalomon71cb0c22014-11-14 12:10:14 -0800172 AutoValidate av(this);
173
bsalomonf320e042015-02-17 15:09:34 -0800174 while (fNonpurgeableResources.count()) {
175 GrGpuResource* back = *(fNonpurgeableResources.end() - 1);
176 SkASSERT(!back->wasDestroyed());
177 back->cacheAccess().abandon();
bsalomonc8dc1f72014-08-21 13:02:13 -0700178 }
bsalomonf320e042015-02-17 15:09:34 -0800179
180 while (fPurgeableQueue.count()) {
181 GrGpuResource* top = fPurgeableQueue.peek();
182 SkASSERT(!top->wasDestroyed());
183 top->cacheAccess().abandon();
184 }
185
bsalomon744998e2014-08-28 09:54:34 -0700186 SkASSERT(!fScratchMap.count());
bsalomon8718aaf2015-02-19 07:24:21 -0800187 SkASSERT(!fUniqueHash.count());
bsalomonc8dc1f72014-08-21 13:02:13 -0700188 SkASSERT(!fCount);
bsalomonf320e042015-02-17 15:09:34 -0800189 SkASSERT(!this->getResourceCount());
bsalomondace19e2014-11-17 07:34:06 -0800190 SkASSERT(!fBytes);
191 SkASSERT(!fBudgetedCount);
192 SkASSERT(!fBudgetedBytes);
Derek Sollenbergeree479142017-05-24 11:41:33 -0400193 SkASSERT(!fPurgeableBytes);
bsalomonc8dc1f72014-08-21 13:02:13 -0700194}
195
bsalomon0ea80f42015-02-11 10:49:59 -0800196void GrResourceCache::releaseAll() {
bsalomon71cb0c22014-11-14 12:10:14 -0800197 AutoValidate av(this);
198
Brian Osman13dddce2017-05-09 13:19:50 -0400199 this->processFreedGpuResources();
200
Robert Phillips1afd4cd2018-01-08 13:40:32 -0500201 SkASSERT(fProxyProvider); // better have called setProxyProvider
Robert Phillips3ec95732017-09-29 15:10:39 -0400202 // We must remove the uniqueKeys from the proxies here. While they possess a uniqueKey
203 // they also have a raw pointer back to this class (which is presumably going away)!
Robert Phillips1afd4cd2018-01-08 13:40:32 -0500204 fProxyProvider->removeAllUniqueKeys();
Robert Phillips45a6f142017-09-29 09:49:41 -0400205
bsalomonf320e042015-02-17 15:09:34 -0800206 while(fNonpurgeableResources.count()) {
207 GrGpuResource* back = *(fNonpurgeableResources.end() - 1);
208 SkASSERT(!back->wasDestroyed());
209 back->cacheAccess().release();
bsalomonc8dc1f72014-08-21 13:02:13 -0700210 }
bsalomonf320e042015-02-17 15:09:34 -0800211
212 while (fPurgeableQueue.count()) {
213 GrGpuResource* top = fPurgeableQueue.peek();
214 SkASSERT(!top->wasDestroyed());
215 top->cacheAccess().release();
216 }
217
bsalomon744998e2014-08-28 09:54:34 -0700218 SkASSERT(!fScratchMap.count());
bsalomon8718aaf2015-02-19 07:24:21 -0800219 SkASSERT(!fUniqueHash.count());
bsalomonc8dc1f72014-08-21 13:02:13 -0700220 SkASSERT(!fCount);
bsalomonf320e042015-02-17 15:09:34 -0800221 SkASSERT(!this->getResourceCount());
bsalomondace19e2014-11-17 07:34:06 -0800222 SkASSERT(!fBytes);
223 SkASSERT(!fBudgetedCount);
224 SkASSERT(!fBudgetedBytes);
Derek Sollenbergeree479142017-05-24 11:41:33 -0400225 SkASSERT(!fPurgeableBytes);
bsalomonc8dc1f72014-08-21 13:02:13 -0700226}
bsalomonbcf0a522014-10-08 08:40:09 -0700227
bsalomon0ea80f42015-02-11 10:49:59 -0800228class GrResourceCache::AvailableForScratchUse {
bsalomonbcf0a522014-10-08 08:40:09 -0700229public:
bsalomon000f8292014-10-15 19:04:14 -0700230 AvailableForScratchUse(bool rejectPendingIO) : fRejectPendingIO(rejectPendingIO) { }
bsalomonbcf0a522014-10-08 08:40:09 -0700231
232 bool operator()(const GrGpuResource* resource) const {
robertphillipsc4ed6842016-05-24 14:17:12 -0700233 SkASSERT(!resource->getUniqueKey().isValid() &&
234 resource->resourcePriv().getScratchKey().isValid());
bsalomon12299ab2014-11-14 13:33:09 -0800235 if (resource->internalHasRef() || !resource->cacheAccess().isScratch()) {
bsalomon000f8292014-10-15 19:04:14 -0700236 return false;
bsalomonbcf0a522014-10-08 08:40:09 -0700237 }
bsalomon000f8292014-10-15 19:04:14 -0700238 return !fRejectPendingIO || !resource->internalHasPendingIO();
bsalomonbcf0a522014-10-08 08:40:09 -0700239 }
bsalomon1e2530b2014-10-09 09:57:18 -0700240
bsalomonbcf0a522014-10-08 08:40:09 -0700241private:
bsalomon000f8292014-10-15 19:04:14 -0700242 bool fRejectPendingIO;
bsalomonbcf0a522014-10-08 08:40:09 -0700243};
244
bsalomon0ea80f42015-02-11 10:49:59 -0800245GrGpuResource* GrResourceCache::findAndRefScratchResource(const GrScratchKey& scratchKey,
robertphillips6e83ac72015-08-13 05:19:14 -0700246 size_t resourceSize,
bsalomon9f2d1572015-02-17 11:47:40 -0800247 uint32_t flags) {
bsalomon7775c852014-12-30 12:50:52 -0800248 SkASSERT(scratchKey.isValid());
robertphillipsee843b22016-10-04 05:30:20 -0700249
bsalomon71cb0c22014-11-14 12:10:14 -0800250 GrGpuResource* resource;
bsalomon000f8292014-10-15 19:04:14 -0700251 if (flags & (kPreferNoPendingIO_ScratchFlag | kRequireNoPendingIO_ScratchFlag)) {
bsalomon71cb0c22014-11-14 12:10:14 -0800252 resource = fScratchMap.find(scratchKey, AvailableForScratchUse(true));
bsalomon000f8292014-10-15 19:04:14 -0700253 if (resource) {
bsalomon9f2d1572015-02-17 11:47:40 -0800254 this->refAndMakeResourceMRU(resource);
bsalomonb436ed62014-11-17 12:15:56 -0800255 this->validate();
256 return resource;
bsalomon000f8292014-10-15 19:04:14 -0700257 } else if (flags & kRequireNoPendingIO_ScratchFlag) {
halcanary96fcdcc2015-08-27 07:41:13 -0700258 return nullptr;
bsalomon000f8292014-10-15 19:04:14 -0700259 }
robertphillips63926682015-08-20 09:39:02 -0700260 // We would prefer to consume more available VRAM rather than flushing
261 // immediately, but on ANGLE this can lead to starving of the GPU.
262 if (fPreferVRAMUseOverFlushes && this->wouldFit(resourceSize)) {
robertphillips6e83ac72015-08-13 05:19:14 -0700263 // kPrefer is specified, we didn't find a resource without pending io,
robertphillips63926682015-08-20 09:39:02 -0700264 // but there is still space in our budget for the resource so force
265 // the caller to allocate a new resource.
halcanary96fcdcc2015-08-27 07:41:13 -0700266 return nullptr;
robertphillips6e83ac72015-08-13 05:19:14 -0700267 }
bsalomon000f8292014-10-15 19:04:14 -0700268 }
bsalomon71cb0c22014-11-14 12:10:14 -0800269 resource = fScratchMap.find(scratchKey, AvailableForScratchUse(false));
270 if (resource) {
bsalomon9f2d1572015-02-17 11:47:40 -0800271 this->refAndMakeResourceMRU(resource);
bsalomonb436ed62014-11-17 12:15:56 -0800272 this->validate();
bsalomon71cb0c22014-11-14 12:10:14 -0800273 }
274 return resource;
bsalomonbcf0a522014-10-08 08:40:09 -0700275}
bsalomon8b79d232014-11-10 10:19:06 -0800276
bsalomon0ea80f42015-02-11 10:49:59 -0800277void GrResourceCache::willRemoveScratchKey(const GrGpuResource* resource) {
bsalomon3582d3e2015-02-13 14:20:05 -0800278 SkASSERT(resource->resourcePriv().getScratchKey().isValid());
robertphillipsc4ed6842016-05-24 14:17:12 -0700279 if (!resource->getUniqueKey().isValid()) {
280 fScratchMap.remove(resource->resourcePriv().getScratchKey(), resource);
281 }
bsalomon10e23ca2014-11-25 05:52:06 -0800282}
283
bsalomonf99e9612015-02-19 08:24:16 -0800284void GrResourceCache::removeUniqueKey(GrGpuResource* resource) {
bsalomon3f324322015-04-08 11:01:54 -0700285 // Someone has a ref to this resource in order to have removed the key. When the ref count
286 // reaches zero we will get a ref cnt notification and figure out what to do with it.
bsalomonf99e9612015-02-19 08:24:16 -0800287 if (resource->getUniqueKey().isValid()) {
288 SkASSERT(resource == fUniqueHash.find(resource->getUniqueKey()));
289 fUniqueHash.remove(resource->getUniqueKey());
290 }
291 resource->cacheAccess().removeUniqueKey();
robertphillipsc4ed6842016-05-24 14:17:12 -0700292
293 if (resource->resourcePriv().getScratchKey().isValid()) {
294 fScratchMap.insert(resource->resourcePriv().getScratchKey(), resource);
295 }
296
bsalomonf99e9612015-02-19 08:24:16 -0800297 this->validate();
bsalomon23e619c2015-02-06 11:54:28 -0800298}
299
bsalomonf99e9612015-02-19 08:24:16 -0800300void GrResourceCache::changeUniqueKey(GrGpuResource* resource, const GrUniqueKey& newKey) {
bsalomon8b79d232014-11-10 10:19:06 -0800301 SkASSERT(resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800302 SkASSERT(this->isInCache(resource));
bsalomon8b79d232014-11-10 10:19:06 -0800303
bsalomonf99e9612015-02-19 08:24:16 -0800304 // If another resource has the new key, remove its key then install the key on this resource.
305 if (newKey.isValid()) {
Greg Daniel0d537802017-09-08 11:44:14 -0400306 if (GrGpuResource* old = fUniqueHash.find(newKey)) {
307 // If the old resource using the key is purgeable and is unreachable, then remove it.
308 if (!old->resourcePriv().getScratchKey().isValid() && old->isPurgeable()) {
309 old->cacheAccess().release();
310 } else {
311 this->removeUniqueKey(old);
312 }
313 }
314 SkASSERT(nullptr == fUniqueHash.find(newKey));
315
robertphillipsc4ed6842016-05-24 14:17:12 -0700316 // Remove the entry for this resource if it already has a unique key.
317 if (resource->getUniqueKey().isValid()) {
318 SkASSERT(resource == fUniqueHash.find(resource->getUniqueKey()));
319 fUniqueHash.remove(resource->getUniqueKey());
320 SkASSERT(nullptr == fUniqueHash.find(resource->getUniqueKey()));
321 } else {
322 // 'resource' didn't have a valid unique key before so it is switching sides. Remove it
323 // from the ScratchMap
324 if (resource->resourcePriv().getScratchKey().isValid()) {
325 fScratchMap.remove(resource->resourcePriv().getScratchKey(), resource);
326 }
327 }
328
bsalomonf99e9612015-02-19 08:24:16 -0800329 resource->cacheAccess().setUniqueKey(newKey);
330 fUniqueHash.add(resource);
331 } else {
robertphillipsc4ed6842016-05-24 14:17:12 -0700332 this->removeUniqueKey(resource);
bsalomonf99e9612015-02-19 08:24:16 -0800333 }
334
bsalomon71cb0c22014-11-14 12:10:14 -0800335 this->validate();
bsalomon8b79d232014-11-10 10:19:06 -0800336}
bsalomon71cb0c22014-11-14 12:10:14 -0800337
bsalomon9f2d1572015-02-17 11:47:40 -0800338void GrResourceCache::refAndMakeResourceMRU(GrGpuResource* resource) {
bsalomon71cb0c22014-11-14 12:10:14 -0800339 SkASSERT(resource);
340 SkASSERT(this->isInCache(resource));
bsalomonddf30e62015-02-19 11:38:44 -0800341
bsalomon9f2d1572015-02-17 11:47:40 -0800342 if (resource->isPurgeable()) {
343 // It's about to become unpurgeable.
Derek Sollenbergeree479142017-05-24 11:41:33 -0400344 fPurgeableBytes -= resource->gpuMemorySize();
bsalomon9f2d1572015-02-17 11:47:40 -0800345 fPurgeableQueue.remove(resource);
bsalomonf320e042015-02-17 15:09:34 -0800346 this->addToNonpurgeableArray(resource);
bsalomon9f2d1572015-02-17 11:47:40 -0800347 }
348 resource->ref();
bsalomonddf30e62015-02-19 11:38:44 -0800349
350 resource->cacheAccess().setTimestamp(this->getNextTimestamp());
bsalomonf320e042015-02-17 15:09:34 -0800351 this->validate();
bsalomon71cb0c22014-11-14 12:10:14 -0800352}
353
bsalomon3f324322015-04-08 11:01:54 -0700354void GrResourceCache::notifyCntReachedZero(GrGpuResource* resource, uint32_t flags) {
bsalomon71cb0c22014-11-14 12:10:14 -0800355 SkASSERT(resource);
bsalomon3f324322015-04-08 11:01:54 -0700356 SkASSERT(!resource->wasDestroyed());
357 SkASSERT(flags);
bsalomon71cb0c22014-11-14 12:10:14 -0800358 SkASSERT(this->isInCache(resource));
bsalomon3f324322015-04-08 11:01:54 -0700359 // This resource should always be in the nonpurgeable array when this function is called. It
360 // will be moved to the queue if it is newly purgeable.
361 SkASSERT(fNonpurgeableResources[*resource->cacheAccess().accessCacheIndex()] == resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800362
bsalomon3f324322015-04-08 11:01:54 -0700363 if (SkToBool(ResourceAccess::kRefCntReachedZero_RefNotificationFlag & flags)) {
364#ifdef SK_DEBUG
365 // When the timestamp overflows validate() is called. validate() checks that resources in
366 // the nonpurgeable array are indeed not purgeable. However, the movement from the array to
367 // the purgeable queue happens just below in this function. So we mark it as an exception.
368 if (resource->isPurgeable()) {
369 fNewlyPurgeableResourceForValidation = resource;
370 }
371#endif
372 resource->cacheAccess().setTimestamp(this->getNextTimestamp());
halcanary96fcdcc2015-08-27 07:41:13 -0700373 SkDEBUGCODE(fNewlyPurgeableResourceForValidation = nullptr);
bsalomon3f324322015-04-08 11:01:54 -0700374 }
375
376 if (!SkToBool(ResourceAccess::kAllCntsReachedZero_RefNotificationFlag & flags)) {
377 SkASSERT(!resource->isPurgeable());
378 return;
379 }
380
381 SkASSERT(resource->isPurgeable());
bsalomonf320e042015-02-17 15:09:34 -0800382 this->removeFromNonpurgeableArray(resource);
bsalomon9f2d1572015-02-17 11:47:40 -0800383 fPurgeableQueue.insert(resource);
bsalomone2e87f32016-09-22 12:42:11 -0700384 resource->cacheAccess().setFlushCntWhenResourceBecamePurgeable(fExternalFlushCnt);
Brian Salomon5e150852017-03-22 14:53:13 -0400385 resource->cacheAccess().setTimeWhenResourceBecomePurgeable();
Derek Sollenbergeree479142017-05-24 11:41:33 -0400386 fPurgeableBytes += resource->gpuMemorySize();
bsalomon71cb0c22014-11-14 12:10:14 -0800387
bsalomon5ec26ae2016-02-25 08:33:02 -0800388 if (SkBudgeted::kNo == resource->resourcePriv().isBudgeted()) {
bsalomonc2f35b72015-01-23 07:19:22 -0800389 // Check whether this resource could still be used as a scratch resource.
kkinnunen2e6055b2016-04-22 01:48:29 -0700390 if (!resource->resourcePriv().refsWrappedObjects() &&
bsalomon9f2d1572015-02-17 11:47:40 -0800391 resource->resourcePriv().getScratchKey().isValid()) {
bsalomonc2f35b72015-01-23 07:19:22 -0800392 // We won't purge an existing resource to make room for this one.
bsalomonf320e042015-02-17 15:09:34 -0800393 if (fBudgetedCount < fMaxCount &&
394 fBudgetedBytes + resource->gpuMemorySize() <= fMaxBytes) {
bsalomon3582d3e2015-02-13 14:20:05 -0800395 resource->resourcePriv().makeBudgeted();
bsalomon9f2d1572015-02-17 11:47:40 -0800396 return;
bsalomonc2f35b72015-01-23 07:19:22 -0800397 }
bsalomonc2f35b72015-01-23 07:19:22 -0800398 }
399 } else {
bsalomon9f2d1572015-02-17 11:47:40 -0800400 // Purge the resource immediately if we're over budget
bsalomon8718aaf2015-02-19 07:24:21 -0800401 // Also purge if the resource has neither a valid scratch key nor a unique key.
robertphillipsee843b22016-10-04 05:30:20 -0700402 bool noKey = !resource->resourcePriv().getScratchKey().isValid() &&
403 !resource->getUniqueKey().isValid();
404 if (!this->overBudget() && !noKey) {
bsalomon9f2d1572015-02-17 11:47:40 -0800405 return;
bsalomonc2f35b72015-01-23 07:19:22 -0800406 }
407 }
bsalomondace19e2014-11-17 07:34:06 -0800408
bsalomonf320e042015-02-17 15:09:34 -0800409 SkDEBUGCODE(int beforeCount = this->getResourceCount();)
bsalomon9f2d1572015-02-17 11:47:40 -0800410 resource->cacheAccess().release();
411 // We should at least free this resource, perhaps dependent resources as well.
bsalomonf320e042015-02-17 15:09:34 -0800412 SkASSERT(this->getResourceCount() < beforeCount);
bsalomon71cb0c22014-11-14 12:10:14 -0800413 this->validate();
414}
415
bsalomon0ea80f42015-02-11 10:49:59 -0800416void GrResourceCache::didChangeGpuMemorySize(const GrGpuResource* resource, size_t oldSize) {
bsalomon71cb0c22014-11-14 12:10:14 -0800417 // SkASSERT(!fPurging); GrPathRange increases size during flush. :(
418 SkASSERT(resource);
419 SkASSERT(this->isInCache(resource));
420
bsalomondace19e2014-11-17 07:34:06 -0800421 ptrdiff_t delta = resource->gpuMemorySize() - oldSize;
422
423 fBytes += delta;
bsalomon82b1d622014-11-14 13:59:57 -0800424#if GR_CACHE_STATS
425 fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes);
426#endif
bsalomon5ec26ae2016-02-25 08:33:02 -0800427 if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
bsalomondace19e2014-11-17 07:34:06 -0800428 fBudgetedBytes += delta;
Brian Osman39c08ac2017-07-26 09:36:09 -0400429 TRACE_COUNTER2("skia.gpu.cache", "skia budget", "used",
hendrikw876c3132015-03-04 10:33:49 -0800430 fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
bsalomondace19e2014-11-17 07:34:06 -0800431#if GR_CACHE_STATS
432 fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
433#endif
434 }
bsalomon71cb0c22014-11-14 12:10:14 -0800435
436 this->purgeAsNeeded();
437 this->validate();
438}
439
bsalomon0ea80f42015-02-11 10:49:59 -0800440void GrResourceCache::didChangeBudgetStatus(GrGpuResource* resource) {
bsalomon84c8e622014-11-17 09:33:27 -0800441 SkASSERT(resource);
442 SkASSERT(this->isInCache(resource));
443
444 size_t size = resource->gpuMemorySize();
445
bsalomon5ec26ae2016-02-25 08:33:02 -0800446 if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
bsalomon84c8e622014-11-17 09:33:27 -0800447 ++fBudgetedCount;
448 fBudgetedBytes += size;
bsalomonafe30052015-01-16 07:32:33 -0800449#if GR_CACHE_STATS
450 fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
451 fBudgetedHighWaterCount = SkTMax(fBudgetedCount, fBudgetedHighWaterCount);
452#endif
bsalomon84c8e622014-11-17 09:33:27 -0800453 this->purgeAsNeeded();
454 } else {
455 --fBudgetedCount;
456 fBudgetedBytes -= size;
457 }
Brian Osman39c08ac2017-07-26 09:36:09 -0400458 TRACE_COUNTER2("skia.gpu.cache", "skia budget", "used",
hendrikw876c3132015-03-04 10:33:49 -0800459 fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
bsalomon84c8e622014-11-17 09:33:27 -0800460
461 this->validate();
462}
463
robertphillipsee843b22016-10-04 05:30:20 -0700464void GrResourceCache::purgeAsNeeded() {
bsalomon3f324322015-04-08 11:01:54 -0700465 SkTArray<GrUniqueKeyInvalidatedMessage> invalidKeyMsgs;
466 fInvalidUniqueKeyInbox.poll(&invalidKeyMsgs);
467 if (invalidKeyMsgs.count()) {
468 this->processInvalidUniqueKeys(invalidKeyMsgs);
469 }
bsalomon71cb0c22014-11-14 12:10:14 -0800470
Brian Osman13dddce2017-05-09 13:19:50 -0400471 this->processFreedGpuResources();
472
bsalomone2e87f32016-09-22 12:42:11 -0700473 if (fMaxUnusedFlushes > 0) {
474 // We want to know how many complete flushes have occurred without the resource being used.
475 // If the resource was tagged when fExternalFlushCnt was N then this means it became
476 // purgeable during activity that became the N+1th flush. So when the flush count is N+2
477 // it has sat in the purgeable queue for one entire flush.
478 uint32_t oldestAllowedFlushCnt = fExternalFlushCnt - fMaxUnusedFlushes - 1;
479 // check for underflow
480 if (oldestAllowedFlushCnt < fExternalFlushCnt) {
481 while (fPurgeableQueue.count()) {
482 uint32_t flushWhenResourceBecamePurgeable =
483 fPurgeableQueue.peek()->cacheAccess().flushCntWhenResourceBecamePurgeable();
484 if (oldestAllowedFlushCnt < flushWhenResourceBecamePurgeable) {
485 // Resources were given both LRU timestamps and tagged with a flush cnt when
486 // they first became purgeable. The LRU timestamp won't change again until the
487 // resource is made non-purgeable again. So, at this point all the remaining
488 // resources in the timestamp-sorted queue will have a flush count >= to this
489 // one.
490 break;
491 }
492 GrGpuResource* resource = fPurgeableQueue.peek();
493 SkASSERT(resource->isPurgeable());
494 resource->cacheAccess().release();
bsalomon3f324322015-04-08 11:01:54 -0700495 }
bsalomon3f324322015-04-08 11:01:54 -0700496 }
497 }
498
499 bool stillOverbudget = this->overBudget();
500 while (stillOverbudget && fPurgeableQueue.count()) {
robertphillipsee843b22016-10-04 05:30:20 -0700501 GrGpuResource* resource = fPurgeableQueue.peek();
bsalomon9f2d1572015-02-17 11:47:40 -0800502 SkASSERT(resource->isPurgeable());
503 resource->cacheAccess().release();
bsalomon3f324322015-04-08 11:01:54 -0700504 stillOverbudget = this->overBudget();
bsalomon9f2d1572015-02-17 11:47:40 -0800505 }
bsalomon71cb0c22014-11-14 12:10:14 -0800506
bsalomonb436ed62014-11-17 12:15:56 -0800507 this->validate();
robertphillipsee843b22016-10-04 05:30:20 -0700508
509 if (stillOverbudget) {
510 // Set this so that GrDrawingManager will issue a flush to free up resources with pending
511 // IO that we were unable to purge in this pass.
512 fRequestFlush = true;
513 }
bsalomon71cb0c22014-11-14 12:10:14 -0800514}
515
Robert Phillips6eba0632018-03-28 12:25:42 -0400516void GrResourceCache::purgeUnlockedResources(bool scratchResourcesOnly) {
517 if (!scratchResourcesOnly) {
518 // We could disable maintaining the heap property here, but it would add a lot of
519 // complexity. Moreover, this is rarely called.
520 while (fPurgeableQueue.count()) {
521 GrGpuResource* resource = fPurgeableQueue.peek();
522 SkASSERT(resource->isPurgeable());
523 resource->cacheAccess().release();
524 }
525 } else {
526 // Sort the queue
527 fPurgeableQueue.sort();
528
529 // Make a list of the scratch resources to delete
530 SkTDArray<GrGpuResource*> scratchResources;
531 for (int i = 0; i < fPurgeableQueue.count(); i++) {
532 GrGpuResource* resource = fPurgeableQueue.at(i);
533 SkASSERT(resource->isPurgeable());
534 if (!resource->getUniqueKey().isValid()) {
535 *scratchResources.append() = resource;
536 }
537 }
538
539 // Delete the scratch resources. This must be done as a separate pass
540 // to avoid messing up the sorted order of the queue
541 for (int i = 0; i < scratchResources.count(); i++) {
542 scratchResources.getAt(i)->cacheAccess().release();
543 }
bsalomon9f2d1572015-02-17 11:47:40 -0800544 }
bsalomon71cb0c22014-11-14 12:10:14 -0800545
bsalomonb436ed62014-11-17 12:15:56 -0800546 this->validate();
bsalomon71cb0c22014-11-14 12:10:14 -0800547}
548
Brian Salomon5e150852017-03-22 14:53:13 -0400549void GrResourceCache::purgeResourcesNotUsedSince(GrStdSteadyClock::time_point purgeTime) {
550 while (fPurgeableQueue.count()) {
551 const GrStdSteadyClock::time_point resourceTime =
552 fPurgeableQueue.peek()->cacheAccess().timeWhenResourceBecamePurgeable();
553 if (resourceTime >= purgeTime) {
554 // Resources were given both LRU timestamps and tagged with a frame number when
555 // they first became purgeable. The LRU timestamp won't change again until the
556 // resource is made non-purgeable again. So, at this point all the remaining
557 // resources in the timestamp-sorted queue will have a frame number >= to this
558 // one.
559 break;
560 }
561 GrGpuResource* resource = fPurgeableQueue.peek();
562 SkASSERT(resource->isPurgeable());
563 resource->cacheAccess().release();
564 }
565}
566
Derek Sollenberger5480a182017-05-25 16:43:59 -0400567void GrResourceCache::purgeUnlockedResources(size_t bytesToPurge, bool preferScratchResources) {
568
569 const size_t tmpByteBudget = SkTMax((size_t)0, fBytes - bytesToPurge);
570 bool stillOverbudget = tmpByteBudget < fBytes;
571
572 if (preferScratchResources && bytesToPurge < fPurgeableBytes) {
573 // Sort the queue
574 fPurgeableQueue.sort();
575
576 // Make a list of the scratch resources to delete
577 SkTDArray<GrGpuResource*> scratchResources;
578 size_t scratchByteCount = 0;
579 for (int i = 0; i < fPurgeableQueue.count() && stillOverbudget; i++) {
580 GrGpuResource* resource = fPurgeableQueue.at(i);
581 SkASSERT(resource->isPurgeable());
582 if (!resource->getUniqueKey().isValid()) {
583 *scratchResources.append() = resource;
584 scratchByteCount += resource->gpuMemorySize();
585 stillOverbudget = tmpByteBudget < fBytes - scratchByteCount;
586 }
587 }
588
589 // Delete the scratch resources. This must be done as a separate pass
590 // to avoid messing up the sorted order of the queue
591 for (int i = 0; i < scratchResources.count(); i++) {
592 scratchResources.getAt(i)->cacheAccess().release();
593 }
594 stillOverbudget = tmpByteBudget < fBytes;
595
596 this->validate();
597 }
598
599 // Purge any remaining resources in LRU order
600 if (stillOverbudget) {
601 const size_t cachedByteCount = fMaxBytes;
602 fMaxBytes = tmpByteBudget;
603 this->purgeAsNeeded();
604 fMaxBytes = cachedByteCount;
605 }
606}
607
bsalomon8718aaf2015-02-19 07:24:21 -0800608void GrResourceCache::processInvalidUniqueKeys(
Robert Phillips1afd4cd2018-01-08 13:40:32 -0500609 const SkTArray<GrUniqueKeyInvalidatedMessage>& msgs) {
610 SkASSERT(fProxyProvider); // better have called setProxyProvider
611
bsalomon23e619c2015-02-06 11:54:28 -0800612 for (int i = 0; i < msgs.count(); ++i) {
Robert Phillips1afd4cd2018-01-08 13:40:32 -0500613 fProxyProvider->processInvalidProxyUniqueKey(msgs[i].key());
Robert Phillipsae7d3f32017-09-21 08:26:08 -0400614
bsalomon8718aaf2015-02-19 07:24:21 -0800615 GrGpuResource* resource = this->findAndRefUniqueResource(msgs[i].key());
bsalomon23e619c2015-02-06 11:54:28 -0800616 if (resource) {
bsalomon8718aaf2015-02-19 07:24:21 -0800617 resource->resourcePriv().removeUniqueKey();
bsalomon3f324322015-04-08 11:01:54 -0700618 resource->unref(); // If this resource is now purgeable, the cache will be notified.
bsalomon23e619c2015-02-06 11:54:28 -0800619 }
620 }
621}
622
Brian Osman13dddce2017-05-09 13:19:50 -0400623void GrResourceCache::insertCrossContextGpuResource(GrGpuResource* resource) {
624 resource->ref();
625}
626
627void GrResourceCache::processFreedGpuResources() {
628 SkTArray<GrGpuResourceFreedMessage> msgs;
629 fFreedGpuResourceInbox.poll(&msgs);
630 for (int i = 0; i < msgs.count(); ++i) {
631 if (msgs[i].fOwningUniqueID == fContextUniqueID) {
632 msgs[i].fResource->unref();
633 }
634 }
635}
636
bsalomonf320e042015-02-17 15:09:34 -0800637void GrResourceCache::addToNonpurgeableArray(GrGpuResource* resource) {
638 int index = fNonpurgeableResources.count();
639 *fNonpurgeableResources.append() = resource;
640 *resource->cacheAccess().accessCacheIndex() = index;
641}
642
643void GrResourceCache::removeFromNonpurgeableArray(GrGpuResource* resource) {
644 int* index = resource->cacheAccess().accessCacheIndex();
645 // Fill the whole we will create in the array with the tail object, adjust its index, and
646 // then pop the array
647 GrGpuResource* tail = *(fNonpurgeableResources.end() - 1);
648 SkASSERT(fNonpurgeableResources[*index] == resource);
649 fNonpurgeableResources[*index] = tail;
650 *tail->cacheAccess().accessCacheIndex() = *index;
651 fNonpurgeableResources.pop();
652 SkDEBUGCODE(*index = -1);
653}
654
bsalomonddf30e62015-02-19 11:38:44 -0800655uint32_t GrResourceCache::getNextTimestamp() {
656 // If we wrap then all the existing resources will appear older than any resources that get
657 // a timestamp after the wrap.
658 if (0 == fTimestamp) {
659 int count = this->getResourceCount();
660 if (count) {
661 // Reset all the timestamps. We sort the resources by timestamp and then assign
662 // sequential timestamps beginning with 0. This is O(n*lg(n)) but it should be extremely
663 // rare.
664 SkTDArray<GrGpuResource*> sortedPurgeableResources;
665 sortedPurgeableResources.setReserve(fPurgeableQueue.count());
666
667 while (fPurgeableQueue.count()) {
668 *sortedPurgeableResources.append() = fPurgeableQueue.peek();
669 fPurgeableQueue.pop();
670 }
robertphillipsee843b22016-10-04 05:30:20 -0700671
bsalomone2e87f32016-09-22 12:42:11 -0700672 SkTQSort(fNonpurgeableResources.begin(), fNonpurgeableResources.end() - 1,
673 CompareTimestamp);
bsalomonddf30e62015-02-19 11:38:44 -0800674
675 // Pick resources out of the purgeable and non-purgeable arrays based on lowest
676 // timestamp and assign new timestamps.
677 int currP = 0;
678 int currNP = 0;
679 while (currP < sortedPurgeableResources.count() &&
mtklein56da0252015-11-16 11:16:23 -0800680 currNP < fNonpurgeableResources.count()) {
bsalomonddf30e62015-02-19 11:38:44 -0800681 uint32_t tsP = sortedPurgeableResources[currP]->cacheAccess().timestamp();
682 uint32_t tsNP = fNonpurgeableResources[currNP]->cacheAccess().timestamp();
683 SkASSERT(tsP != tsNP);
684 if (tsP < tsNP) {
685 sortedPurgeableResources[currP++]->cacheAccess().setTimestamp(fTimestamp++);
686 } else {
687 // Correct the index in the nonpurgeable array stored on the resource post-sort.
688 *fNonpurgeableResources[currNP]->cacheAccess().accessCacheIndex() = currNP;
689 fNonpurgeableResources[currNP++]->cacheAccess().setTimestamp(fTimestamp++);
690 }
691 }
692
693 // The above loop ended when we hit the end of one array. Finish the other one.
694 while (currP < sortedPurgeableResources.count()) {
695 sortedPurgeableResources[currP++]->cacheAccess().setTimestamp(fTimestamp++);
696 }
697 while (currNP < fNonpurgeableResources.count()) {
698 *fNonpurgeableResources[currNP]->cacheAccess().accessCacheIndex() = currNP;
699 fNonpurgeableResources[currNP++]->cacheAccess().setTimestamp(fTimestamp++);
700 }
701
702 // Rebuild the queue.
703 for (int i = 0; i < sortedPurgeableResources.count(); ++i) {
704 fPurgeableQueue.insert(sortedPurgeableResources[i]);
705 }
706
707 this->validate();
708 SkASSERT(count == this->getResourceCount());
709
710 // count should be the next timestamp we return.
711 SkASSERT(fTimestamp == SkToU32(count));
mtklein56da0252015-11-16 11:16:23 -0800712 }
bsalomonddf30e62015-02-19 11:38:44 -0800713 }
714 return fTimestamp++;
715}
716
bsalomonb77a9072016-09-07 10:02:04 -0700717void GrResourceCache::notifyFlushOccurred(FlushType type) {
718 switch (type) {
bsalomonb77a9072016-09-07 10:02:04 -0700719 case FlushType::kCacheRequested:
robertphillipsee843b22016-10-04 05:30:20 -0700720 SkASSERT(fRequestFlush);
721 fRequestFlush = false;
bsalomonb77a9072016-09-07 10:02:04 -0700722 break;
robertphillipsee843b22016-10-04 05:30:20 -0700723 case FlushType::kExternal:
bsalomone2e87f32016-09-22 12:42:11 -0700724 ++fExternalFlushCnt;
725 if (0 == fExternalFlushCnt) {
726 // When this wraps just reset all the purgeable resources' last used flush state.
727 for (int i = 0; i < fPurgeableQueue.count(); ++i) {
728 fPurgeableQueue.at(i)->cacheAccess().setFlushCntWhenResourceBecamePurgeable(0);
729 }
bsalomonb77a9072016-09-07 10:02:04 -0700730 }
731 break;
bsalomon3f324322015-04-08 11:01:54 -0700732 }
robertphillipsee843b22016-10-04 05:30:20 -0700733 this->purgeAsNeeded();
bsalomon3f324322015-04-08 11:01:54 -0700734}
735
ericrk0a5fa482015-09-15 14:16:10 -0700736void GrResourceCache::dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const {
737 for (int i = 0; i < fNonpurgeableResources.count(); ++i) {
738 fNonpurgeableResources[i]->dumpMemoryStatistics(traceMemoryDump);
739 }
740 for (int i = 0; i < fPurgeableQueue.count(); ++i) {
741 fPurgeableQueue.at(i)->dumpMemoryStatistics(traceMemoryDump);
742 }
743}
744
bsalomon71cb0c22014-11-14 12:10:14 -0800745#ifdef SK_DEBUG
bsalomon0ea80f42015-02-11 10:49:59 -0800746void GrResourceCache::validate() const {
bsalomonc2f35b72015-01-23 07:19:22 -0800747 // Reduce the frequency of validations for large resource counts.
748 static SkRandom gRandom;
749 int mask = (SkNextPow2(fCount + 1) >> 5) - 1;
750 if (~mask && (gRandom.nextU() & mask)) {
751 return;
752 }
753
bsalomonf320e042015-02-17 15:09:34 -0800754 struct Stats {
755 size_t fBytes;
756 int fBudgetedCount;
757 size_t fBudgetedBytes;
758 int fLocked;
759 int fScratch;
760 int fCouldBeScratch;
761 int fContent;
762 const ScratchMap* fScratchMap;
bsalomon8718aaf2015-02-19 07:24:21 -0800763 const UniqueHash* fUniqueHash;
bsalomon71cb0c22014-11-14 12:10:14 -0800764
bsalomonf320e042015-02-17 15:09:34 -0800765 Stats(const GrResourceCache* cache) {
766 memset(this, 0, sizeof(*this));
767 fScratchMap = &cache->fScratchMap;
bsalomon8718aaf2015-02-19 07:24:21 -0800768 fUniqueHash = &cache->fUniqueHash;
bsalomon71cb0c22014-11-14 12:10:14 -0800769 }
770
bsalomonf320e042015-02-17 15:09:34 -0800771 void update(GrGpuResource* resource) {
772 fBytes += resource->gpuMemorySize();
bsalomondace19e2014-11-17 07:34:06 -0800773
bsalomonf320e042015-02-17 15:09:34 -0800774 if (!resource->isPurgeable()) {
775 ++fLocked;
776 }
bsalomon9f2d1572015-02-17 11:47:40 -0800777
robertphillipsc4ed6842016-05-24 14:17:12 -0700778 const GrScratchKey& scratchKey = resource->resourcePriv().getScratchKey();
779 const GrUniqueKey& uniqueKey = resource->getUniqueKey();
780
bsalomonf320e042015-02-17 15:09:34 -0800781 if (resource->cacheAccess().isScratch()) {
robertphillipsc4ed6842016-05-24 14:17:12 -0700782 SkASSERT(!uniqueKey.isValid());
bsalomonf320e042015-02-17 15:09:34 -0800783 ++fScratch;
robertphillipsc4ed6842016-05-24 14:17:12 -0700784 SkASSERT(fScratchMap->countForKey(scratchKey));
kkinnunen2e6055b2016-04-22 01:48:29 -0700785 SkASSERT(!resource->resourcePriv().refsWrappedObjects());
robertphillipsc4ed6842016-05-24 14:17:12 -0700786 } else if (scratchKey.isValid()) {
bsalomon5ec26ae2016-02-25 08:33:02 -0800787 SkASSERT(SkBudgeted::kNo == resource->resourcePriv().isBudgeted() ||
robertphillipsc4ed6842016-05-24 14:17:12 -0700788 uniqueKey.isValid());
789 if (!uniqueKey.isValid()) {
mtklein4e976072016-08-08 09:06:27 -0700790 ++fCouldBeScratch;
robertphillipsc4ed6842016-05-24 14:17:12 -0700791 SkASSERT(fScratchMap->countForKey(scratchKey));
792 }
kkinnunen2e6055b2016-04-22 01:48:29 -0700793 SkASSERT(!resource->resourcePriv().refsWrappedObjects());
bsalomonf320e042015-02-17 15:09:34 -0800794 }
bsalomon8718aaf2015-02-19 07:24:21 -0800795 if (uniqueKey.isValid()) {
bsalomonf320e042015-02-17 15:09:34 -0800796 ++fContent;
bsalomon8718aaf2015-02-19 07:24:21 -0800797 SkASSERT(fUniqueHash->find(uniqueKey) == resource);
Brian Osman0562eb92017-05-08 11:16:39 -0400798 SkASSERT(SkBudgeted::kYes == resource->resourcePriv().isBudgeted() ||
799 resource->resourcePriv().refsWrappedObjects());
robertphillipsc4ed6842016-05-24 14:17:12 -0700800
801 if (scratchKey.isValid()) {
802 SkASSERT(!fScratchMap->has(resource, scratchKey));
803 }
bsalomonf320e042015-02-17 15:09:34 -0800804 }
805
bsalomon5ec26ae2016-02-25 08:33:02 -0800806 if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
bsalomonf320e042015-02-17 15:09:34 -0800807 ++fBudgetedCount;
808 fBudgetedBytes += resource->gpuMemorySize();
809 }
bsalomon9f2d1572015-02-17 11:47:40 -0800810 }
bsalomonf320e042015-02-17 15:09:34 -0800811 };
812
robertphillipsc4ed6842016-05-24 14:17:12 -0700813 {
814 ScratchMap::ConstIter iter(&fScratchMap);
815
816 int count = 0;
817 for ( ; !iter.done(); ++iter) {
818 const GrGpuResource* resource = *iter;
819 SkASSERT(resource->resourcePriv().getScratchKey().isValid());
820 SkASSERT(!resource->getUniqueKey().isValid());
821 count++;
822 }
823 SkASSERT(count == fScratchMap.count()); // ensure the iterator is working correctly
824 }
825
bsalomonf320e042015-02-17 15:09:34 -0800826 Stats stats(this);
Derek Sollenbergeree479142017-05-24 11:41:33 -0400827 size_t purgeableBytes = 0;
bsalomonf320e042015-02-17 15:09:34 -0800828
829 for (int i = 0; i < fNonpurgeableResources.count(); ++i) {
bsalomon3f324322015-04-08 11:01:54 -0700830 SkASSERT(!fNonpurgeableResources[i]->isPurgeable() ||
831 fNewlyPurgeableResourceForValidation == fNonpurgeableResources[i]);
bsalomonf320e042015-02-17 15:09:34 -0800832 SkASSERT(*fNonpurgeableResources[i]->cacheAccess().accessCacheIndex() == i);
833 SkASSERT(!fNonpurgeableResources[i]->wasDestroyed());
834 stats.update(fNonpurgeableResources[i]);
bsalomon71cb0c22014-11-14 12:10:14 -0800835 }
bsalomon9f2d1572015-02-17 11:47:40 -0800836 for (int i = 0; i < fPurgeableQueue.count(); ++i) {
837 SkASSERT(fPurgeableQueue.at(i)->isPurgeable());
bsalomonf320e042015-02-17 15:09:34 -0800838 SkASSERT(*fPurgeableQueue.at(i)->cacheAccess().accessCacheIndex() == i);
839 SkASSERT(!fPurgeableQueue.at(i)->wasDestroyed());
840 stats.update(fPurgeableQueue.at(i));
Derek Sollenbergeree479142017-05-24 11:41:33 -0400841 purgeableBytes += fPurgeableQueue.at(i)->gpuMemorySize();
bsalomon9f2d1572015-02-17 11:47:40 -0800842 }
843
bsalomonf320e042015-02-17 15:09:34 -0800844 SkASSERT(fCount == this->getResourceCount());
bsalomondace19e2014-11-17 07:34:06 -0800845 SkASSERT(fBudgetedCount <= fCount);
bsalomonf320e042015-02-17 15:09:34 -0800846 SkASSERT(fBudgetedBytes <= fBytes);
847 SkASSERT(stats.fBytes == fBytes);
848 SkASSERT(stats.fBudgetedBytes == fBudgetedBytes);
849 SkASSERT(stats.fBudgetedCount == fBudgetedCount);
Derek Sollenbergeree479142017-05-24 11:41:33 -0400850 SkASSERT(purgeableBytes == fPurgeableBytes);
bsalomon71cb0c22014-11-14 12:10:14 -0800851#if GR_CACHE_STATS
bsalomondace19e2014-11-17 07:34:06 -0800852 SkASSERT(fBudgetedHighWaterCount <= fHighWaterCount);
853 SkASSERT(fBudgetedHighWaterBytes <= fHighWaterBytes);
bsalomonf320e042015-02-17 15:09:34 -0800854 SkASSERT(fBytes <= fHighWaterBytes);
855 SkASSERT(fCount <= fHighWaterCount);
856 SkASSERT(fBudgetedBytes <= fBudgetedHighWaterBytes);
857 SkASSERT(fBudgetedCount <= fBudgetedHighWaterCount);
bsalomon71cb0c22014-11-14 12:10:14 -0800858#endif
bsalomon8718aaf2015-02-19 07:24:21 -0800859 SkASSERT(stats.fContent == fUniqueHash.count());
bsalomonf320e042015-02-17 15:09:34 -0800860 SkASSERT(stats.fScratch + stats.fCouldBeScratch == fScratchMap.count());
bsalomon71cb0c22014-11-14 12:10:14 -0800861
bsalomon3f324322015-04-08 11:01:54 -0700862 // This assertion is not currently valid because we can be in recursive notifyCntReachedZero()
bsalomon12299ab2014-11-14 13:33:09 -0800863 // calls. This will be fixed when subresource registration is explicit.
bsalomondace19e2014-11-17 07:34:06 -0800864 // bool overBudget = budgetedBytes > fMaxBytes || budgetedCount > fMaxCount;
bsalomon12299ab2014-11-14 13:33:09 -0800865 // SkASSERT(!overBudget || locked == count || fPurging);
bsalomon71cb0c22014-11-14 12:10:14 -0800866}
bsalomonf320e042015-02-17 15:09:34 -0800867
868bool GrResourceCache::isInCache(const GrGpuResource* resource) const {
869 int index = *resource->cacheAccess().accessCacheIndex();
870 if (index < 0) {
871 return false;
872 }
873 if (index < fPurgeableQueue.count() && fPurgeableQueue.at(index) == resource) {
874 return true;
875 }
876 if (index < fNonpurgeableResources.count() && fNonpurgeableResources[index] == resource) {
877 return true;
878 }
879 SkDEBUGFAIL("Resource index should be -1 or the resource should be in the cache.");
880 return false;
881}
882
bsalomon71cb0c22014-11-14 12:10:14 -0800883#endif