blob: 20acf547f6fb539698fe1e83a536a1b32630cd57 [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 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"
bsalomon71cb0c22014-11-14 12:10:14 -080020
bsalomon8718aaf2015-02-19 07:24:21 -080021DECLARE_SKMESSAGEBUS_MESSAGE(GrUniqueKeyInvalidatedMessage);
bsalomon71cb0c22014-11-14 12:10:14 -080022
Brian Osman13dddce2017-05-09 13:19:50 -040023DECLARE_SKMESSAGEBUS_MESSAGE(GrGpuResourceFreedMessage);
24
bsalomon71cb0c22014-11-14 12:10:14 -080025//////////////////////////////////////////////////////////////////////////////
26
bsalomon7775c852014-12-30 12:50:52 -080027GrScratchKey::ResourceType GrScratchKey::GenerateResourceType() {
bsalomon24db3b12015-01-23 04:24:04 -080028 static int32_t gType = INHERITED::kInvalidDomain + 1;
bsalomonfe369ee2014-11-10 11:59:06 -080029
bsalomon7775c852014-12-30 12:50:52 -080030 int32_t type = sk_atomic_inc(&gType);
robertphillips9790a7b2015-01-05 12:29:15 -080031 if (type > SK_MaxU16) {
Ben Wagnerb4aab9a2017-08-16 10:53:04 -040032 SK_ABORT("Too many Resource Types");
bsalomon71cb0c22014-11-14 12:10:14 -080033 }
34
35 return static_cast<ResourceType>(type);
36}
37
bsalomon8718aaf2015-02-19 07:24:21 -080038GrUniqueKey::Domain GrUniqueKey::GenerateDomain() {
bsalomon24db3b12015-01-23 04:24:04 -080039 static int32_t gDomain = INHERITED::kInvalidDomain + 1;
bsalomon7775c852014-12-30 12:50:52 -080040
bsalomon24db3b12015-01-23 04:24:04 -080041 int32_t domain = sk_atomic_inc(&gDomain);
kkinnunen016dffb2015-01-23 06:43:05 -080042 if (domain > SK_MaxU16) {
Ben Wagnerb4aab9a2017-08-16 10:53:04 -040043 SK_ABORT("Too many GrUniqueKey Domains");
bsalomon7775c852014-12-30 12:50:52 -080044 }
bsalomon24db3b12015-01-23 04:24:04 -080045
46 return static_cast<Domain>(domain);
47}
bsalomon3f324322015-04-08 11:01:54 -070048
bsalomon24db3b12015-01-23 04:24:04 -080049uint32_t GrResourceKeyHash(const uint32_t* data, size_t size) {
mtklein4e976072016-08-08 09:06:27 -070050 return SkOpts::hash(data, size);
bsalomon7775c852014-12-30 12:50:52 -080051}
52
bsalomonfe369ee2014-11-10 11:59:06 -080053//////////////////////////////////////////////////////////////////////////////
54
bsalomon0ea80f42015-02-11 10:49:59 -080055class GrResourceCache::AutoValidate : ::SkNoncopyable {
bsalomon71cb0c22014-11-14 12:10:14 -080056public:
bsalomon0ea80f42015-02-11 10:49:59 -080057 AutoValidate(GrResourceCache* cache) : fCache(cache) { cache->validate(); }
bsalomon71cb0c22014-11-14 12:10:14 -080058 ~AutoValidate() { fCache->validate(); }
59private:
bsalomon0ea80f42015-02-11 10:49:59 -080060 GrResourceCache* fCache;
bsalomon71cb0c22014-11-14 12:10:14 -080061};
62
63 //////////////////////////////////////////////////////////////////////////////
robertphillipsee843b22016-10-04 05:30:20 -070064
bsalomon71cb0c22014-11-14 12:10:14 -080065
Brian Osman13dddce2017-05-09 13:19:50 -040066GrResourceCache::GrResourceCache(const GrCaps* caps, uint32_t contextUniqueID)
bsalomon9f2d1572015-02-17 11:47:40 -080067 : fTimestamp(0)
68 , fMaxCount(kDefaultMaxCount)
bsalomon71cb0c22014-11-14 12:10:14 -080069 , fMaxBytes(kDefaultMaxSize)
bsalomon3f324322015-04-08 11:01:54 -070070 , fMaxUnusedFlushes(kDefaultMaxUnusedFlushes)
bsalomon71cb0c22014-11-14 12:10:14 -080071#if GR_CACHE_STATS
72 , fHighWaterCount(0)
73 , fHighWaterBytes(0)
bsalomondace19e2014-11-17 07:34:06 -080074 , fBudgetedHighWaterCount(0)
75 , fBudgetedHighWaterBytes(0)
bsalomon71cb0c22014-11-14 12:10:14 -080076#endif
bsalomon71cb0c22014-11-14 12:10:14 -080077 , fBytes(0)
bsalomondace19e2014-11-17 07:34:06 -080078 , fBudgetedCount(0)
79 , fBudgetedBytes(0)
Derek Sollenbergeree479142017-05-24 11:41:33 -040080 , fPurgeableBytes(0)
robertphillipsee843b22016-10-04 05:30:20 -070081 , fRequestFlush(false)
bsalomone2e87f32016-09-22 12:42:11 -070082 , fExternalFlushCnt(0)
Brian Osman13dddce2017-05-09 13:19:50 -040083 , fContextUniqueID(contextUniqueID)
robertphillips63926682015-08-20 09:39:02 -070084 , fPreferVRAMUseOverFlushes(caps->preferVRAMUseOverFlushes()) {
bsalomonf320e042015-02-17 15:09:34 -080085 SkDEBUGCODE(fCount = 0;)
halcanary96fcdcc2015-08-27 07:41:13 -070086 SkDEBUGCODE(fNewlyPurgeableResourceForValidation = nullptr;)
bsalomon71cb0c22014-11-14 12:10:14 -080087}
88
bsalomon0ea80f42015-02-11 10:49:59 -080089GrResourceCache::~GrResourceCache() {
bsalomonc8dc1f72014-08-21 13:02:13 -070090 this->releaseAll();
91}
92
bsalomon3f324322015-04-08 11:01:54 -070093void GrResourceCache::setLimits(int count, size_t bytes, int maxUnusedFlushes) {
bsalomon71cb0c22014-11-14 12:10:14 -080094 fMaxCount = count;
95 fMaxBytes = bytes;
bsalomon3f324322015-04-08 11:01:54 -070096 fMaxUnusedFlushes = maxUnusedFlushes;
bsalomon71cb0c22014-11-14 12:10:14 -080097 this->purgeAsNeeded();
98}
99
bsalomon0ea80f42015-02-11 10:49:59 -0800100void GrResourceCache::insertResource(GrGpuResource* resource) {
bsalomon49f085d2014-09-05 13:34:00 -0700101 SkASSERT(resource);
bsalomon16961262014-08-26 14:01:07 -0700102 SkASSERT(!this->isInCache(resource));
bsalomonf320e042015-02-17 15:09:34 -0800103 SkASSERT(!resource->wasDestroyed());
104 SkASSERT(!resource->isPurgeable());
bsalomonddf30e62015-02-19 11:38:44 -0800105
106 // We must set the timestamp before adding to the array in case the timestamp wraps and we wind
107 // up iterating over all the resources that already have timestamps.
108 resource->cacheAccess().setTimestamp(this->getNextTimestamp());
109
bsalomonf320e042015-02-17 15:09:34 -0800110 this->addToNonpurgeableArray(resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800111
bsalomondace19e2014-11-17 07:34:06 -0800112 size_t size = resource->gpuMemorySize();
bsalomonf320e042015-02-17 15:09:34 -0800113 SkDEBUGCODE(++fCount;)
bsalomon84c8e622014-11-17 09:33:27 -0800114 fBytes += size;
bsalomon82b1d622014-11-14 13:59:57 -0800115#if GR_CACHE_STATS
bsalomonf320e042015-02-17 15:09:34 -0800116 fHighWaterCount = SkTMax(this->getResourceCount(), fHighWaterCount);
bsalomon82b1d622014-11-14 13:59:57 -0800117 fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes);
118#endif
bsalomon5ec26ae2016-02-25 08:33:02 -0800119 if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
bsalomondace19e2014-11-17 07:34:06 -0800120 ++fBudgetedCount;
121 fBudgetedBytes += size;
Brian Osman39c08ac2017-07-26 09:36:09 -0400122 TRACE_COUNTER2("skia.gpu.cache", "skia budget", "used",
hendrikw876c3132015-03-04 10:33:49 -0800123 fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
bsalomondace19e2014-11-17 07:34:06 -0800124#if GR_CACHE_STATS
125 fBudgetedHighWaterCount = SkTMax(fBudgetedCount, fBudgetedHighWaterCount);
126 fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
127#endif
128 }
robertphillipsc4ed6842016-05-24 14:17:12 -0700129 if (resource->resourcePriv().getScratchKey().isValid() &&
130 !resource->getUniqueKey().isValid()) {
kkinnunen2e6055b2016-04-22 01:48:29 -0700131 SkASSERT(!resource->resourcePriv().refsWrappedObjects());
bsalomon3582d3e2015-02-13 14:20:05 -0800132 fScratchMap.insert(resource->resourcePriv().getScratchKey(), resource);
bsalomon744998e2014-08-28 09:54:34 -0700133 }
bsalomon9f2d1572015-02-17 11:47:40 -0800134
bsalomon71cb0c22014-11-14 12:10:14 -0800135 this->purgeAsNeeded();
bsalomonc8dc1f72014-08-21 13:02:13 -0700136}
137
bsalomon0ea80f42015-02-11 10:49:59 -0800138void GrResourceCache::removeResource(GrGpuResource* resource) {
bsalomon9f2d1572015-02-17 11:47:40 -0800139 this->validate();
bsalomon16961262014-08-26 14:01:07 -0700140 SkASSERT(this->isInCache(resource));
bsalomondace19e2014-11-17 07:34:06 -0800141
Derek Sollenbergeree479142017-05-24 11:41:33 -0400142 size_t size = resource->gpuMemorySize();
bsalomon9f2d1572015-02-17 11:47:40 -0800143 if (resource->isPurgeable()) {
144 fPurgeableQueue.remove(resource);
Derek Sollenbergeree479142017-05-24 11:41:33 -0400145 fPurgeableBytes -= size;
bsalomonf320e042015-02-17 15:09:34 -0800146 } else {
147 this->removeFromNonpurgeableArray(resource);
bsalomon9f2d1572015-02-17 11:47:40 -0800148 }
149
bsalomonf320e042015-02-17 15:09:34 -0800150 SkDEBUGCODE(--fCount;)
bsalomondace19e2014-11-17 07:34:06 -0800151 fBytes -= size;
bsalomon5ec26ae2016-02-25 08:33:02 -0800152 if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
bsalomondace19e2014-11-17 07:34:06 -0800153 --fBudgetedCount;
154 fBudgetedBytes -= size;
Brian Osman39c08ac2017-07-26 09:36:09 -0400155 TRACE_COUNTER2("skia.gpu.cache", "skia budget", "used",
hendrikw876c3132015-03-04 10:33:49 -0800156 fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
bsalomondace19e2014-11-17 07:34:06 -0800157 }
158
robertphillipsc4ed6842016-05-24 14:17:12 -0700159 if (resource->resourcePriv().getScratchKey().isValid() &&
160 !resource->getUniqueKey().isValid()) {
bsalomon3582d3e2015-02-13 14:20:05 -0800161 fScratchMap.remove(resource->resourcePriv().getScratchKey(), resource);
bsalomon744998e2014-08-28 09:54:34 -0700162 }
bsalomon8718aaf2015-02-19 07:24:21 -0800163 if (resource->getUniqueKey().isValid()) {
164 fUniqueHash.remove(resource->getUniqueKey());
bsalomon8b79d232014-11-10 10:19:06 -0800165 }
bsalomonb436ed62014-11-17 12:15:56 -0800166 this->validate();
bsalomonc8dc1f72014-08-21 13:02:13 -0700167}
168
bsalomon0ea80f42015-02-11 10:49:59 -0800169void GrResourceCache::abandonAll() {
bsalomon71cb0c22014-11-14 12:10:14 -0800170 AutoValidate av(this);
171
bsalomonf320e042015-02-17 15:09:34 -0800172 while (fNonpurgeableResources.count()) {
173 GrGpuResource* back = *(fNonpurgeableResources.end() - 1);
174 SkASSERT(!back->wasDestroyed());
175 back->cacheAccess().abandon();
bsalomonc8dc1f72014-08-21 13:02:13 -0700176 }
bsalomonf320e042015-02-17 15:09:34 -0800177
178 while (fPurgeableQueue.count()) {
179 GrGpuResource* top = fPurgeableQueue.peek();
180 SkASSERT(!top->wasDestroyed());
181 top->cacheAccess().abandon();
182 }
183
bsalomon744998e2014-08-28 09:54:34 -0700184 SkASSERT(!fScratchMap.count());
bsalomon8718aaf2015-02-19 07:24:21 -0800185 SkASSERT(!fUniqueHash.count());
bsalomonc8dc1f72014-08-21 13:02:13 -0700186 SkASSERT(!fCount);
bsalomonf320e042015-02-17 15:09:34 -0800187 SkASSERT(!this->getResourceCount());
bsalomondace19e2014-11-17 07:34:06 -0800188 SkASSERT(!fBytes);
189 SkASSERT(!fBudgetedCount);
190 SkASSERT(!fBudgetedBytes);
Derek Sollenbergeree479142017-05-24 11:41:33 -0400191 SkASSERT(!fPurgeableBytes);
bsalomonc8dc1f72014-08-21 13:02:13 -0700192}
193
bsalomon0ea80f42015-02-11 10:49:59 -0800194void GrResourceCache::releaseAll() {
bsalomon71cb0c22014-11-14 12:10:14 -0800195 AutoValidate av(this);
196
Brian Osman13dddce2017-05-09 13:19:50 -0400197 this->processFreedGpuResources();
198
Robert Phillips3ec95732017-09-29 15:10:39 -0400199 // We must remove the uniqueKeys from the proxies here. While they possess a uniqueKey
200 // they also have a raw pointer back to this class (which is presumably going away)!
Robert Phillips45a6f142017-09-29 09:49:41 -0400201 UniquelyKeyedProxyHash::Iter iter(&fUniquelyKeyedProxies);
Robert Phillips3ec95732017-09-29 15:10:39 -0400202 for (UniquelyKeyedProxyHash::Iter iter(&fUniquelyKeyedProxies); !iter.done(); ++iter) {
Robert Phillips45a6f142017-09-29 09:49:41 -0400203 GrTextureProxy& tmp = *iter;
204
205 this->processInvalidProxyUniqueKey(tmp.getUniqueKey(), &tmp, false);
206 }
Robert Phillips3ec95732017-09-29 15:10:39 -0400207 SkASSERT(!fUniquelyKeyedProxies.count());
Robert Phillips45a6f142017-09-29 09:49:41 -0400208
bsalomonf320e042015-02-17 15:09:34 -0800209 while(fNonpurgeableResources.count()) {
210 GrGpuResource* back = *(fNonpurgeableResources.end() - 1);
211 SkASSERT(!back->wasDestroyed());
212 back->cacheAccess().release();
bsalomonc8dc1f72014-08-21 13:02:13 -0700213 }
bsalomonf320e042015-02-17 15:09:34 -0800214
215 while (fPurgeableQueue.count()) {
216 GrGpuResource* top = fPurgeableQueue.peek();
217 SkASSERT(!top->wasDestroyed());
218 top->cacheAccess().release();
219 }
220
bsalomon744998e2014-08-28 09:54:34 -0700221 SkASSERT(!fScratchMap.count());
bsalomon8718aaf2015-02-19 07:24:21 -0800222 SkASSERT(!fUniqueHash.count());
bsalomonc8dc1f72014-08-21 13:02:13 -0700223 SkASSERT(!fCount);
bsalomonf320e042015-02-17 15:09:34 -0800224 SkASSERT(!this->getResourceCount());
bsalomondace19e2014-11-17 07:34:06 -0800225 SkASSERT(!fBytes);
226 SkASSERT(!fBudgetedCount);
227 SkASSERT(!fBudgetedBytes);
Derek Sollenbergeree479142017-05-24 11:41:33 -0400228 SkASSERT(!fPurgeableBytes);
bsalomonc8dc1f72014-08-21 13:02:13 -0700229}
bsalomonbcf0a522014-10-08 08:40:09 -0700230
bsalomon0ea80f42015-02-11 10:49:59 -0800231class GrResourceCache::AvailableForScratchUse {
bsalomonbcf0a522014-10-08 08:40:09 -0700232public:
bsalomon000f8292014-10-15 19:04:14 -0700233 AvailableForScratchUse(bool rejectPendingIO) : fRejectPendingIO(rejectPendingIO) { }
bsalomonbcf0a522014-10-08 08:40:09 -0700234
235 bool operator()(const GrGpuResource* resource) const {
robertphillipsc4ed6842016-05-24 14:17:12 -0700236 SkASSERT(!resource->getUniqueKey().isValid() &&
237 resource->resourcePriv().getScratchKey().isValid());
bsalomon12299ab2014-11-14 13:33:09 -0800238 if (resource->internalHasRef() || !resource->cacheAccess().isScratch()) {
bsalomon000f8292014-10-15 19:04:14 -0700239 return false;
bsalomonbcf0a522014-10-08 08:40:09 -0700240 }
bsalomon000f8292014-10-15 19:04:14 -0700241 return !fRejectPendingIO || !resource->internalHasPendingIO();
bsalomonbcf0a522014-10-08 08:40:09 -0700242 }
bsalomon1e2530b2014-10-09 09:57:18 -0700243
bsalomonbcf0a522014-10-08 08:40:09 -0700244private:
bsalomon000f8292014-10-15 19:04:14 -0700245 bool fRejectPendingIO;
bsalomonbcf0a522014-10-08 08:40:09 -0700246};
247
bsalomon0ea80f42015-02-11 10:49:59 -0800248GrGpuResource* GrResourceCache::findAndRefScratchResource(const GrScratchKey& scratchKey,
robertphillips6e83ac72015-08-13 05:19:14 -0700249 size_t resourceSize,
bsalomon9f2d1572015-02-17 11:47:40 -0800250 uint32_t flags) {
bsalomon7775c852014-12-30 12:50:52 -0800251 SkASSERT(scratchKey.isValid());
robertphillipsee843b22016-10-04 05:30:20 -0700252
bsalomon71cb0c22014-11-14 12:10:14 -0800253 GrGpuResource* resource;
bsalomon000f8292014-10-15 19:04:14 -0700254 if (flags & (kPreferNoPendingIO_ScratchFlag | kRequireNoPendingIO_ScratchFlag)) {
bsalomon71cb0c22014-11-14 12:10:14 -0800255 resource = fScratchMap.find(scratchKey, AvailableForScratchUse(true));
bsalomon000f8292014-10-15 19:04:14 -0700256 if (resource) {
bsalomon9f2d1572015-02-17 11:47:40 -0800257 this->refAndMakeResourceMRU(resource);
bsalomonb436ed62014-11-17 12:15:56 -0800258 this->validate();
259 return resource;
bsalomon000f8292014-10-15 19:04:14 -0700260 } else if (flags & kRequireNoPendingIO_ScratchFlag) {
halcanary96fcdcc2015-08-27 07:41:13 -0700261 return nullptr;
bsalomon000f8292014-10-15 19:04:14 -0700262 }
robertphillips63926682015-08-20 09:39:02 -0700263 // We would prefer to consume more available VRAM rather than flushing
264 // immediately, but on ANGLE this can lead to starving of the GPU.
265 if (fPreferVRAMUseOverFlushes && this->wouldFit(resourceSize)) {
robertphillips6e83ac72015-08-13 05:19:14 -0700266 // kPrefer is specified, we didn't find a resource without pending io,
robertphillips63926682015-08-20 09:39:02 -0700267 // but there is still space in our budget for the resource so force
268 // the caller to allocate a new resource.
halcanary96fcdcc2015-08-27 07:41:13 -0700269 return nullptr;
robertphillips6e83ac72015-08-13 05:19:14 -0700270 }
bsalomon000f8292014-10-15 19:04:14 -0700271 }
bsalomon71cb0c22014-11-14 12:10:14 -0800272 resource = fScratchMap.find(scratchKey, AvailableForScratchUse(false));
273 if (resource) {
bsalomon9f2d1572015-02-17 11:47:40 -0800274 this->refAndMakeResourceMRU(resource);
bsalomonb436ed62014-11-17 12:15:56 -0800275 this->validate();
bsalomon71cb0c22014-11-14 12:10:14 -0800276 }
277 return resource;
bsalomonbcf0a522014-10-08 08:40:09 -0700278}
bsalomon8b79d232014-11-10 10:19:06 -0800279
bsalomon0ea80f42015-02-11 10:49:59 -0800280void GrResourceCache::willRemoveScratchKey(const GrGpuResource* resource) {
bsalomon3582d3e2015-02-13 14:20:05 -0800281 SkASSERT(resource->resourcePriv().getScratchKey().isValid());
robertphillipsc4ed6842016-05-24 14:17:12 -0700282 if (!resource->getUniqueKey().isValid()) {
283 fScratchMap.remove(resource->resourcePriv().getScratchKey(), resource);
284 }
bsalomon10e23ca2014-11-25 05:52:06 -0800285}
286
bsalomonf99e9612015-02-19 08:24:16 -0800287void GrResourceCache::removeUniqueKey(GrGpuResource* resource) {
bsalomon3f324322015-04-08 11:01:54 -0700288 // Someone has a ref to this resource in order to have removed the key. When the ref count
289 // reaches zero we will get a ref cnt notification and figure out what to do with it.
bsalomonf99e9612015-02-19 08:24:16 -0800290 if (resource->getUniqueKey().isValid()) {
291 SkASSERT(resource == fUniqueHash.find(resource->getUniqueKey()));
292 fUniqueHash.remove(resource->getUniqueKey());
293 }
294 resource->cacheAccess().removeUniqueKey();
robertphillipsc4ed6842016-05-24 14:17:12 -0700295
296 if (resource->resourcePriv().getScratchKey().isValid()) {
297 fScratchMap.insert(resource->resourcePriv().getScratchKey(), resource);
298 }
299
bsalomonf99e9612015-02-19 08:24:16 -0800300 this->validate();
bsalomon23e619c2015-02-06 11:54:28 -0800301}
302
bsalomonf99e9612015-02-19 08:24:16 -0800303void GrResourceCache::changeUniqueKey(GrGpuResource* resource, const GrUniqueKey& newKey) {
bsalomon8b79d232014-11-10 10:19:06 -0800304 SkASSERT(resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800305 SkASSERT(this->isInCache(resource));
bsalomon8b79d232014-11-10 10:19:06 -0800306
bsalomonf99e9612015-02-19 08:24:16 -0800307 // If another resource has the new key, remove its key then install the key on this resource.
308 if (newKey.isValid()) {
Greg Daniel0d537802017-09-08 11:44:14 -0400309 if (GrGpuResource* old = fUniqueHash.find(newKey)) {
310 // If the old resource using the key is purgeable and is unreachable, then remove it.
311 if (!old->resourcePriv().getScratchKey().isValid() && old->isPurgeable()) {
312 old->cacheAccess().release();
313 } else {
314 this->removeUniqueKey(old);
315 }
316 }
317 SkASSERT(nullptr == fUniqueHash.find(newKey));
318
robertphillipsc4ed6842016-05-24 14:17:12 -0700319 // Remove the entry for this resource if it already has a unique key.
320 if (resource->getUniqueKey().isValid()) {
321 SkASSERT(resource == fUniqueHash.find(resource->getUniqueKey()));
322 fUniqueHash.remove(resource->getUniqueKey());
323 SkASSERT(nullptr == fUniqueHash.find(resource->getUniqueKey()));
324 } else {
325 // 'resource' didn't have a valid unique key before so it is switching sides. Remove it
326 // from the ScratchMap
327 if (resource->resourcePriv().getScratchKey().isValid()) {
328 fScratchMap.remove(resource->resourcePriv().getScratchKey(), resource);
329 }
330 }
331
bsalomonf99e9612015-02-19 08:24:16 -0800332 resource->cacheAccess().setUniqueKey(newKey);
333 fUniqueHash.add(resource);
334 } else {
robertphillipsc4ed6842016-05-24 14:17:12 -0700335 this->removeUniqueKey(resource);
bsalomonf99e9612015-02-19 08:24:16 -0800336 }
337
bsalomon71cb0c22014-11-14 12:10:14 -0800338 this->validate();
bsalomon8b79d232014-11-10 10:19:06 -0800339}
bsalomon71cb0c22014-11-14 12:10:14 -0800340
bsalomon9f2d1572015-02-17 11:47:40 -0800341void GrResourceCache::refAndMakeResourceMRU(GrGpuResource* resource) {
bsalomon71cb0c22014-11-14 12:10:14 -0800342 SkASSERT(resource);
343 SkASSERT(this->isInCache(resource));
bsalomonddf30e62015-02-19 11:38:44 -0800344
bsalomon9f2d1572015-02-17 11:47:40 -0800345 if (resource->isPurgeable()) {
346 // It's about to become unpurgeable.
Derek Sollenbergeree479142017-05-24 11:41:33 -0400347 fPurgeableBytes -= resource->gpuMemorySize();
bsalomon9f2d1572015-02-17 11:47:40 -0800348 fPurgeableQueue.remove(resource);
bsalomonf320e042015-02-17 15:09:34 -0800349 this->addToNonpurgeableArray(resource);
bsalomon9f2d1572015-02-17 11:47:40 -0800350 }
351 resource->ref();
bsalomonddf30e62015-02-19 11:38:44 -0800352
353 resource->cacheAccess().setTimestamp(this->getNextTimestamp());
bsalomonf320e042015-02-17 15:09:34 -0800354 this->validate();
bsalomon71cb0c22014-11-14 12:10:14 -0800355}
356
bsalomon3f324322015-04-08 11:01:54 -0700357void GrResourceCache::notifyCntReachedZero(GrGpuResource* resource, uint32_t flags) {
bsalomon71cb0c22014-11-14 12:10:14 -0800358 SkASSERT(resource);
bsalomon3f324322015-04-08 11:01:54 -0700359 SkASSERT(!resource->wasDestroyed());
360 SkASSERT(flags);
bsalomon71cb0c22014-11-14 12:10:14 -0800361 SkASSERT(this->isInCache(resource));
bsalomon3f324322015-04-08 11:01:54 -0700362 // This resource should always be in the nonpurgeable array when this function is called. It
363 // will be moved to the queue if it is newly purgeable.
364 SkASSERT(fNonpurgeableResources[*resource->cacheAccess().accessCacheIndex()] == resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800365
bsalomon3f324322015-04-08 11:01:54 -0700366 if (SkToBool(ResourceAccess::kRefCntReachedZero_RefNotificationFlag & flags)) {
367#ifdef SK_DEBUG
368 // When the timestamp overflows validate() is called. validate() checks that resources in
369 // the nonpurgeable array are indeed not purgeable. However, the movement from the array to
370 // the purgeable queue happens just below in this function. So we mark it as an exception.
371 if (resource->isPurgeable()) {
372 fNewlyPurgeableResourceForValidation = resource;
373 }
374#endif
375 resource->cacheAccess().setTimestamp(this->getNextTimestamp());
halcanary96fcdcc2015-08-27 07:41:13 -0700376 SkDEBUGCODE(fNewlyPurgeableResourceForValidation = nullptr);
bsalomon3f324322015-04-08 11:01:54 -0700377 }
378
379 if (!SkToBool(ResourceAccess::kAllCntsReachedZero_RefNotificationFlag & flags)) {
380 SkASSERT(!resource->isPurgeable());
381 return;
382 }
383
384 SkASSERT(resource->isPurgeable());
bsalomonf320e042015-02-17 15:09:34 -0800385 this->removeFromNonpurgeableArray(resource);
bsalomon9f2d1572015-02-17 11:47:40 -0800386 fPurgeableQueue.insert(resource);
bsalomone2e87f32016-09-22 12:42:11 -0700387 resource->cacheAccess().setFlushCntWhenResourceBecamePurgeable(fExternalFlushCnt);
Brian Salomon5e150852017-03-22 14:53:13 -0400388 resource->cacheAccess().setTimeWhenResourceBecomePurgeable();
Derek Sollenbergeree479142017-05-24 11:41:33 -0400389 fPurgeableBytes += resource->gpuMemorySize();
bsalomon71cb0c22014-11-14 12:10:14 -0800390
bsalomon5ec26ae2016-02-25 08:33:02 -0800391 if (SkBudgeted::kNo == resource->resourcePriv().isBudgeted()) {
bsalomonc2f35b72015-01-23 07:19:22 -0800392 // Check whether this resource could still be used as a scratch resource.
kkinnunen2e6055b2016-04-22 01:48:29 -0700393 if (!resource->resourcePriv().refsWrappedObjects() &&
bsalomon9f2d1572015-02-17 11:47:40 -0800394 resource->resourcePriv().getScratchKey().isValid()) {
bsalomonc2f35b72015-01-23 07:19:22 -0800395 // We won't purge an existing resource to make room for this one.
bsalomonf320e042015-02-17 15:09:34 -0800396 if (fBudgetedCount < fMaxCount &&
397 fBudgetedBytes + resource->gpuMemorySize() <= fMaxBytes) {
bsalomon3582d3e2015-02-13 14:20:05 -0800398 resource->resourcePriv().makeBudgeted();
bsalomon9f2d1572015-02-17 11:47:40 -0800399 return;
bsalomonc2f35b72015-01-23 07:19:22 -0800400 }
bsalomonc2f35b72015-01-23 07:19:22 -0800401 }
402 } else {
bsalomon9f2d1572015-02-17 11:47:40 -0800403 // Purge the resource immediately if we're over budget
bsalomon8718aaf2015-02-19 07:24:21 -0800404 // Also purge if the resource has neither a valid scratch key nor a unique key.
robertphillipsee843b22016-10-04 05:30:20 -0700405 bool noKey = !resource->resourcePriv().getScratchKey().isValid() &&
406 !resource->getUniqueKey().isValid();
407 if (!this->overBudget() && !noKey) {
bsalomon9f2d1572015-02-17 11:47:40 -0800408 return;
bsalomonc2f35b72015-01-23 07:19:22 -0800409 }
410 }
bsalomondace19e2014-11-17 07:34:06 -0800411
bsalomonf320e042015-02-17 15:09:34 -0800412 SkDEBUGCODE(int beforeCount = this->getResourceCount();)
bsalomon9f2d1572015-02-17 11:47:40 -0800413 resource->cacheAccess().release();
414 // We should at least free this resource, perhaps dependent resources as well.
bsalomonf320e042015-02-17 15:09:34 -0800415 SkASSERT(this->getResourceCount() < beforeCount);
bsalomon71cb0c22014-11-14 12:10:14 -0800416 this->validate();
417}
418
bsalomon0ea80f42015-02-11 10:49:59 -0800419void GrResourceCache::didChangeGpuMemorySize(const GrGpuResource* resource, size_t oldSize) {
bsalomon71cb0c22014-11-14 12:10:14 -0800420 // SkASSERT(!fPurging); GrPathRange increases size during flush. :(
421 SkASSERT(resource);
422 SkASSERT(this->isInCache(resource));
423
bsalomondace19e2014-11-17 07:34:06 -0800424 ptrdiff_t delta = resource->gpuMemorySize() - oldSize;
425
426 fBytes += delta;
bsalomon82b1d622014-11-14 13:59:57 -0800427#if GR_CACHE_STATS
428 fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes);
429#endif
bsalomon5ec26ae2016-02-25 08:33:02 -0800430 if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
bsalomondace19e2014-11-17 07:34:06 -0800431 fBudgetedBytes += delta;
Brian Osman39c08ac2017-07-26 09:36:09 -0400432 TRACE_COUNTER2("skia.gpu.cache", "skia budget", "used",
hendrikw876c3132015-03-04 10:33:49 -0800433 fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
bsalomondace19e2014-11-17 07:34:06 -0800434#if GR_CACHE_STATS
435 fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
436#endif
437 }
bsalomon71cb0c22014-11-14 12:10:14 -0800438
439 this->purgeAsNeeded();
440 this->validate();
441}
442
bsalomon0ea80f42015-02-11 10:49:59 -0800443void GrResourceCache::didChangeBudgetStatus(GrGpuResource* resource) {
bsalomon84c8e622014-11-17 09:33:27 -0800444 SkASSERT(resource);
445 SkASSERT(this->isInCache(resource));
446
447 size_t size = resource->gpuMemorySize();
448
bsalomon5ec26ae2016-02-25 08:33:02 -0800449 if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
bsalomon84c8e622014-11-17 09:33:27 -0800450 ++fBudgetedCount;
451 fBudgetedBytes += size;
bsalomonafe30052015-01-16 07:32:33 -0800452#if GR_CACHE_STATS
453 fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
454 fBudgetedHighWaterCount = SkTMax(fBudgetedCount, fBudgetedHighWaterCount);
455#endif
bsalomon84c8e622014-11-17 09:33:27 -0800456 this->purgeAsNeeded();
457 } else {
458 --fBudgetedCount;
459 fBudgetedBytes -= size;
460 }
Brian Osman39c08ac2017-07-26 09:36:09 -0400461 TRACE_COUNTER2("skia.gpu.cache", "skia budget", "used",
hendrikw876c3132015-03-04 10:33:49 -0800462 fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
bsalomon84c8e622014-11-17 09:33:27 -0800463
464 this->validate();
465}
466
robertphillipsee843b22016-10-04 05:30:20 -0700467void GrResourceCache::purgeAsNeeded() {
bsalomon3f324322015-04-08 11:01:54 -0700468 SkTArray<GrUniqueKeyInvalidatedMessage> invalidKeyMsgs;
469 fInvalidUniqueKeyInbox.poll(&invalidKeyMsgs);
470 if (invalidKeyMsgs.count()) {
471 this->processInvalidUniqueKeys(invalidKeyMsgs);
472 }
bsalomon71cb0c22014-11-14 12:10:14 -0800473
Brian Osman13dddce2017-05-09 13:19:50 -0400474 this->processFreedGpuResources();
475
bsalomone2e87f32016-09-22 12:42:11 -0700476 if (fMaxUnusedFlushes > 0) {
477 // We want to know how many complete flushes have occurred without the resource being used.
478 // If the resource was tagged when fExternalFlushCnt was N then this means it became
479 // purgeable during activity that became the N+1th flush. So when the flush count is N+2
480 // it has sat in the purgeable queue for one entire flush.
481 uint32_t oldestAllowedFlushCnt = fExternalFlushCnt - fMaxUnusedFlushes - 1;
482 // check for underflow
483 if (oldestAllowedFlushCnt < fExternalFlushCnt) {
484 while (fPurgeableQueue.count()) {
485 uint32_t flushWhenResourceBecamePurgeable =
486 fPurgeableQueue.peek()->cacheAccess().flushCntWhenResourceBecamePurgeable();
487 if (oldestAllowedFlushCnt < flushWhenResourceBecamePurgeable) {
488 // Resources were given both LRU timestamps and tagged with a flush cnt when
489 // they first became purgeable. The LRU timestamp won't change again until the
490 // resource is made non-purgeable again. So, at this point all the remaining
491 // resources in the timestamp-sorted queue will have a flush count >= to this
492 // one.
493 break;
494 }
495 GrGpuResource* resource = fPurgeableQueue.peek();
496 SkASSERT(resource->isPurgeable());
497 resource->cacheAccess().release();
bsalomon3f324322015-04-08 11:01:54 -0700498 }
bsalomon3f324322015-04-08 11:01:54 -0700499 }
500 }
501
502 bool stillOverbudget = this->overBudget();
503 while (stillOverbudget && fPurgeableQueue.count()) {
robertphillipsee843b22016-10-04 05:30:20 -0700504 GrGpuResource* resource = fPurgeableQueue.peek();
bsalomon9f2d1572015-02-17 11:47:40 -0800505 SkASSERT(resource->isPurgeable());
506 resource->cacheAccess().release();
bsalomon3f324322015-04-08 11:01:54 -0700507 stillOverbudget = this->overBudget();
bsalomon9f2d1572015-02-17 11:47:40 -0800508 }
bsalomon71cb0c22014-11-14 12:10:14 -0800509
bsalomonb436ed62014-11-17 12:15:56 -0800510 this->validate();
robertphillipsee843b22016-10-04 05:30:20 -0700511
512 if (stillOverbudget) {
513 // Set this so that GrDrawingManager will issue a flush to free up resources with pending
514 // IO that we were unable to purge in this pass.
515 fRequestFlush = true;
516 }
bsalomon71cb0c22014-11-14 12:10:14 -0800517}
518
bsalomon0ea80f42015-02-11 10:49:59 -0800519void GrResourceCache::purgeAllUnlocked() {
bsalomon9f2d1572015-02-17 11:47:40 -0800520 // We could disable maintaining the heap property here, but it would add a lot of complexity.
521 // Moreover, this is rarely called.
522 while (fPurgeableQueue.count()) {
523 GrGpuResource* resource = fPurgeableQueue.peek();
524 SkASSERT(resource->isPurgeable());
525 resource->cacheAccess().release();
526 }
bsalomon71cb0c22014-11-14 12:10:14 -0800527
bsalomonb436ed62014-11-17 12:15:56 -0800528 this->validate();
bsalomon71cb0c22014-11-14 12:10:14 -0800529}
530
Brian Salomon5e150852017-03-22 14:53:13 -0400531void GrResourceCache::purgeResourcesNotUsedSince(GrStdSteadyClock::time_point purgeTime) {
532 while (fPurgeableQueue.count()) {
533 const GrStdSteadyClock::time_point resourceTime =
534 fPurgeableQueue.peek()->cacheAccess().timeWhenResourceBecamePurgeable();
535 if (resourceTime >= purgeTime) {
536 // Resources were given both LRU timestamps and tagged with a frame number when
537 // they first became purgeable. The LRU timestamp won't change again until the
538 // resource is made non-purgeable again. So, at this point all the remaining
539 // resources in the timestamp-sorted queue will have a frame number >= to this
540 // one.
541 break;
542 }
543 GrGpuResource* resource = fPurgeableQueue.peek();
544 SkASSERT(resource->isPurgeable());
545 resource->cacheAccess().release();
546 }
547}
548
Derek Sollenberger5480a182017-05-25 16:43:59 -0400549void GrResourceCache::purgeUnlockedResources(size_t bytesToPurge, bool preferScratchResources) {
550
551 const size_t tmpByteBudget = SkTMax((size_t)0, fBytes - bytesToPurge);
552 bool stillOverbudget = tmpByteBudget < fBytes;
553
554 if (preferScratchResources && bytesToPurge < fPurgeableBytes) {
555 // Sort the queue
556 fPurgeableQueue.sort();
557
558 // Make a list of the scratch resources to delete
559 SkTDArray<GrGpuResource*> scratchResources;
560 size_t scratchByteCount = 0;
561 for (int i = 0; i < fPurgeableQueue.count() && stillOverbudget; i++) {
562 GrGpuResource* resource = fPurgeableQueue.at(i);
563 SkASSERT(resource->isPurgeable());
564 if (!resource->getUniqueKey().isValid()) {
565 *scratchResources.append() = resource;
566 scratchByteCount += resource->gpuMemorySize();
567 stillOverbudget = tmpByteBudget < fBytes - scratchByteCount;
568 }
569 }
570
571 // Delete the scratch resources. This must be done as a separate pass
572 // to avoid messing up the sorted order of the queue
573 for (int i = 0; i < scratchResources.count(); i++) {
574 scratchResources.getAt(i)->cacheAccess().release();
575 }
576 stillOverbudget = tmpByteBudget < fBytes;
577
578 this->validate();
579 }
580
581 // Purge any remaining resources in LRU order
582 if (stillOverbudget) {
583 const size_t cachedByteCount = fMaxBytes;
584 fMaxBytes = tmpByteBudget;
585 this->purgeAsNeeded();
586 fMaxBytes = cachedByteCount;
587 }
588}
589
bsalomon8718aaf2015-02-19 07:24:21 -0800590void GrResourceCache::processInvalidUniqueKeys(
591 const SkTArray<GrUniqueKeyInvalidatedMessage>& msgs) {
bsalomon23e619c2015-02-06 11:54:28 -0800592 for (int i = 0; i < msgs.count(); ++i) {
Robert Phillipsae7d3f32017-09-21 08:26:08 -0400593 this->processInvalidProxyUniqueKey(msgs[i].key());
594
bsalomon8718aaf2015-02-19 07:24:21 -0800595 GrGpuResource* resource = this->findAndRefUniqueResource(msgs[i].key());
bsalomon23e619c2015-02-06 11:54:28 -0800596 if (resource) {
bsalomon8718aaf2015-02-19 07:24:21 -0800597 resource->resourcePriv().removeUniqueKey();
bsalomon3f324322015-04-08 11:01:54 -0700598 resource->unref(); // If this resource is now purgeable, the cache will be notified.
bsalomon23e619c2015-02-06 11:54:28 -0800599 }
600 }
601}
602
Brian Osman13dddce2017-05-09 13:19:50 -0400603void GrResourceCache::insertCrossContextGpuResource(GrGpuResource* resource) {
604 resource->ref();
605}
606
607void GrResourceCache::processFreedGpuResources() {
608 SkTArray<GrGpuResourceFreedMessage> msgs;
609 fFreedGpuResourceInbox.poll(&msgs);
610 for (int i = 0; i < msgs.count(); ++i) {
611 if (msgs[i].fOwningUniqueID == fContextUniqueID) {
612 msgs[i].fResource->unref();
613 }
614 }
615}
616
bsalomonf320e042015-02-17 15:09:34 -0800617void GrResourceCache::addToNonpurgeableArray(GrGpuResource* resource) {
618 int index = fNonpurgeableResources.count();
619 *fNonpurgeableResources.append() = resource;
620 *resource->cacheAccess().accessCacheIndex() = index;
621}
622
623void GrResourceCache::removeFromNonpurgeableArray(GrGpuResource* resource) {
624 int* index = resource->cacheAccess().accessCacheIndex();
625 // Fill the whole we will create in the array with the tail object, adjust its index, and
626 // then pop the array
627 GrGpuResource* tail = *(fNonpurgeableResources.end() - 1);
628 SkASSERT(fNonpurgeableResources[*index] == resource);
629 fNonpurgeableResources[*index] = tail;
630 *tail->cacheAccess().accessCacheIndex() = *index;
631 fNonpurgeableResources.pop();
632 SkDEBUGCODE(*index = -1);
633}
634
bsalomonddf30e62015-02-19 11:38:44 -0800635uint32_t GrResourceCache::getNextTimestamp() {
636 // If we wrap then all the existing resources will appear older than any resources that get
637 // a timestamp after the wrap.
638 if (0 == fTimestamp) {
639 int count = this->getResourceCount();
640 if (count) {
641 // Reset all the timestamps. We sort the resources by timestamp and then assign
642 // sequential timestamps beginning with 0. This is O(n*lg(n)) but it should be extremely
643 // rare.
644 SkTDArray<GrGpuResource*> sortedPurgeableResources;
645 sortedPurgeableResources.setReserve(fPurgeableQueue.count());
646
647 while (fPurgeableQueue.count()) {
648 *sortedPurgeableResources.append() = fPurgeableQueue.peek();
649 fPurgeableQueue.pop();
650 }
robertphillipsee843b22016-10-04 05:30:20 -0700651
bsalomone2e87f32016-09-22 12:42:11 -0700652 SkTQSort(fNonpurgeableResources.begin(), fNonpurgeableResources.end() - 1,
653 CompareTimestamp);
bsalomonddf30e62015-02-19 11:38:44 -0800654
655 // Pick resources out of the purgeable and non-purgeable arrays based on lowest
656 // timestamp and assign new timestamps.
657 int currP = 0;
658 int currNP = 0;
659 while (currP < sortedPurgeableResources.count() &&
mtklein56da0252015-11-16 11:16:23 -0800660 currNP < fNonpurgeableResources.count()) {
bsalomonddf30e62015-02-19 11:38:44 -0800661 uint32_t tsP = sortedPurgeableResources[currP]->cacheAccess().timestamp();
662 uint32_t tsNP = fNonpurgeableResources[currNP]->cacheAccess().timestamp();
663 SkASSERT(tsP != tsNP);
664 if (tsP < tsNP) {
665 sortedPurgeableResources[currP++]->cacheAccess().setTimestamp(fTimestamp++);
666 } else {
667 // Correct the index in the nonpurgeable array stored on the resource post-sort.
668 *fNonpurgeableResources[currNP]->cacheAccess().accessCacheIndex() = currNP;
669 fNonpurgeableResources[currNP++]->cacheAccess().setTimestamp(fTimestamp++);
670 }
671 }
672
673 // The above loop ended when we hit the end of one array. Finish the other one.
674 while (currP < sortedPurgeableResources.count()) {
675 sortedPurgeableResources[currP++]->cacheAccess().setTimestamp(fTimestamp++);
676 }
677 while (currNP < fNonpurgeableResources.count()) {
678 *fNonpurgeableResources[currNP]->cacheAccess().accessCacheIndex() = currNP;
679 fNonpurgeableResources[currNP++]->cacheAccess().setTimestamp(fTimestamp++);
680 }
681
682 // Rebuild the queue.
683 for (int i = 0; i < sortedPurgeableResources.count(); ++i) {
684 fPurgeableQueue.insert(sortedPurgeableResources[i]);
685 }
686
687 this->validate();
688 SkASSERT(count == this->getResourceCount());
689
690 // count should be the next timestamp we return.
691 SkASSERT(fTimestamp == SkToU32(count));
mtklein56da0252015-11-16 11:16:23 -0800692 }
bsalomonddf30e62015-02-19 11:38:44 -0800693 }
694 return fTimestamp++;
695}
696
bsalomonb77a9072016-09-07 10:02:04 -0700697void GrResourceCache::notifyFlushOccurred(FlushType type) {
698 switch (type) {
bsalomonb77a9072016-09-07 10:02:04 -0700699 case FlushType::kCacheRequested:
robertphillipsee843b22016-10-04 05:30:20 -0700700 SkASSERT(fRequestFlush);
701 fRequestFlush = false;
bsalomonb77a9072016-09-07 10:02:04 -0700702 break;
robertphillipsee843b22016-10-04 05:30:20 -0700703 case FlushType::kExternal:
bsalomone2e87f32016-09-22 12:42:11 -0700704 ++fExternalFlushCnt;
705 if (0 == fExternalFlushCnt) {
706 // When this wraps just reset all the purgeable resources' last used flush state.
707 for (int i = 0; i < fPurgeableQueue.count(); ++i) {
708 fPurgeableQueue.at(i)->cacheAccess().setFlushCntWhenResourceBecamePurgeable(0);
709 }
bsalomonb77a9072016-09-07 10:02:04 -0700710 }
711 break;
bsalomon3f324322015-04-08 11:01:54 -0700712 }
robertphillipsee843b22016-10-04 05:30:20 -0700713 this->purgeAsNeeded();
bsalomon3f324322015-04-08 11:01:54 -0700714}
715
ericrk0a5fa482015-09-15 14:16:10 -0700716void GrResourceCache::dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const {
717 for (int i = 0; i < fNonpurgeableResources.count(); ++i) {
718 fNonpurgeableResources[i]->dumpMemoryStatistics(traceMemoryDump);
719 }
720 for (int i = 0; i < fPurgeableQueue.count(); ++i) {
721 fPurgeableQueue.at(i)->dumpMemoryStatistics(traceMemoryDump);
722 }
723}
724
bsalomon71cb0c22014-11-14 12:10:14 -0800725#ifdef SK_DEBUG
bsalomon0ea80f42015-02-11 10:49:59 -0800726void GrResourceCache::validate() const {
bsalomonc2f35b72015-01-23 07:19:22 -0800727 // Reduce the frequency of validations for large resource counts.
728 static SkRandom gRandom;
729 int mask = (SkNextPow2(fCount + 1) >> 5) - 1;
730 if (~mask && (gRandom.nextU() & mask)) {
731 return;
732 }
733
bsalomonf320e042015-02-17 15:09:34 -0800734 struct Stats {
735 size_t fBytes;
736 int fBudgetedCount;
737 size_t fBudgetedBytes;
738 int fLocked;
739 int fScratch;
740 int fCouldBeScratch;
741 int fContent;
742 const ScratchMap* fScratchMap;
bsalomon8718aaf2015-02-19 07:24:21 -0800743 const UniqueHash* fUniqueHash;
bsalomon71cb0c22014-11-14 12:10:14 -0800744
bsalomonf320e042015-02-17 15:09:34 -0800745 Stats(const GrResourceCache* cache) {
746 memset(this, 0, sizeof(*this));
747 fScratchMap = &cache->fScratchMap;
bsalomon8718aaf2015-02-19 07:24:21 -0800748 fUniqueHash = &cache->fUniqueHash;
bsalomon71cb0c22014-11-14 12:10:14 -0800749 }
750
bsalomonf320e042015-02-17 15:09:34 -0800751 void update(GrGpuResource* resource) {
752 fBytes += resource->gpuMemorySize();
bsalomondace19e2014-11-17 07:34:06 -0800753
bsalomonf320e042015-02-17 15:09:34 -0800754 if (!resource->isPurgeable()) {
755 ++fLocked;
756 }
bsalomon9f2d1572015-02-17 11:47:40 -0800757
robertphillipsc4ed6842016-05-24 14:17:12 -0700758 const GrScratchKey& scratchKey = resource->resourcePriv().getScratchKey();
759 const GrUniqueKey& uniqueKey = resource->getUniqueKey();
760
bsalomonf320e042015-02-17 15:09:34 -0800761 if (resource->cacheAccess().isScratch()) {
robertphillipsc4ed6842016-05-24 14:17:12 -0700762 SkASSERT(!uniqueKey.isValid());
bsalomonf320e042015-02-17 15:09:34 -0800763 ++fScratch;
robertphillipsc4ed6842016-05-24 14:17:12 -0700764 SkASSERT(fScratchMap->countForKey(scratchKey));
kkinnunen2e6055b2016-04-22 01:48:29 -0700765 SkASSERT(!resource->resourcePriv().refsWrappedObjects());
robertphillipsc4ed6842016-05-24 14:17:12 -0700766 } else if (scratchKey.isValid()) {
bsalomon5ec26ae2016-02-25 08:33:02 -0800767 SkASSERT(SkBudgeted::kNo == resource->resourcePriv().isBudgeted() ||
robertphillipsc4ed6842016-05-24 14:17:12 -0700768 uniqueKey.isValid());
769 if (!uniqueKey.isValid()) {
mtklein4e976072016-08-08 09:06:27 -0700770 ++fCouldBeScratch;
robertphillipsc4ed6842016-05-24 14:17:12 -0700771 SkASSERT(fScratchMap->countForKey(scratchKey));
772 }
kkinnunen2e6055b2016-04-22 01:48:29 -0700773 SkASSERT(!resource->resourcePriv().refsWrappedObjects());
bsalomonf320e042015-02-17 15:09:34 -0800774 }
bsalomon8718aaf2015-02-19 07:24:21 -0800775 if (uniqueKey.isValid()) {
bsalomonf320e042015-02-17 15:09:34 -0800776 ++fContent;
bsalomon8718aaf2015-02-19 07:24:21 -0800777 SkASSERT(fUniqueHash->find(uniqueKey) == resource);
Brian Osman0562eb92017-05-08 11:16:39 -0400778 SkASSERT(SkBudgeted::kYes == resource->resourcePriv().isBudgeted() ||
779 resource->resourcePriv().refsWrappedObjects());
robertphillipsc4ed6842016-05-24 14:17:12 -0700780
781 if (scratchKey.isValid()) {
782 SkASSERT(!fScratchMap->has(resource, scratchKey));
783 }
bsalomonf320e042015-02-17 15:09:34 -0800784 }
785
bsalomon5ec26ae2016-02-25 08:33:02 -0800786 if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
bsalomonf320e042015-02-17 15:09:34 -0800787 ++fBudgetedCount;
788 fBudgetedBytes += resource->gpuMemorySize();
789 }
bsalomon9f2d1572015-02-17 11:47:40 -0800790 }
bsalomonf320e042015-02-17 15:09:34 -0800791 };
792
robertphillipsc4ed6842016-05-24 14:17:12 -0700793 {
794 ScratchMap::ConstIter iter(&fScratchMap);
795
796 int count = 0;
797 for ( ; !iter.done(); ++iter) {
798 const GrGpuResource* resource = *iter;
799 SkASSERT(resource->resourcePriv().getScratchKey().isValid());
800 SkASSERT(!resource->getUniqueKey().isValid());
801 count++;
802 }
803 SkASSERT(count == fScratchMap.count()); // ensure the iterator is working correctly
804 }
805
bsalomonf320e042015-02-17 15:09:34 -0800806 Stats stats(this);
Derek Sollenbergeree479142017-05-24 11:41:33 -0400807 size_t purgeableBytes = 0;
bsalomonf320e042015-02-17 15:09:34 -0800808
809 for (int i = 0; i < fNonpurgeableResources.count(); ++i) {
bsalomon3f324322015-04-08 11:01:54 -0700810 SkASSERT(!fNonpurgeableResources[i]->isPurgeable() ||
811 fNewlyPurgeableResourceForValidation == fNonpurgeableResources[i]);
bsalomonf320e042015-02-17 15:09:34 -0800812 SkASSERT(*fNonpurgeableResources[i]->cacheAccess().accessCacheIndex() == i);
813 SkASSERT(!fNonpurgeableResources[i]->wasDestroyed());
814 stats.update(fNonpurgeableResources[i]);
bsalomon71cb0c22014-11-14 12:10:14 -0800815 }
bsalomon9f2d1572015-02-17 11:47:40 -0800816 for (int i = 0; i < fPurgeableQueue.count(); ++i) {
817 SkASSERT(fPurgeableQueue.at(i)->isPurgeable());
bsalomonf320e042015-02-17 15:09:34 -0800818 SkASSERT(*fPurgeableQueue.at(i)->cacheAccess().accessCacheIndex() == i);
819 SkASSERT(!fPurgeableQueue.at(i)->wasDestroyed());
820 stats.update(fPurgeableQueue.at(i));
Derek Sollenbergeree479142017-05-24 11:41:33 -0400821 purgeableBytes += fPurgeableQueue.at(i)->gpuMemorySize();
bsalomon9f2d1572015-02-17 11:47:40 -0800822 }
823
bsalomonf320e042015-02-17 15:09:34 -0800824 SkASSERT(fCount == this->getResourceCount());
bsalomondace19e2014-11-17 07:34:06 -0800825 SkASSERT(fBudgetedCount <= fCount);
bsalomonf320e042015-02-17 15:09:34 -0800826 SkASSERT(fBudgetedBytes <= fBytes);
827 SkASSERT(stats.fBytes == fBytes);
828 SkASSERT(stats.fBudgetedBytes == fBudgetedBytes);
829 SkASSERT(stats.fBudgetedCount == fBudgetedCount);
Derek Sollenbergeree479142017-05-24 11:41:33 -0400830 SkASSERT(purgeableBytes == fPurgeableBytes);
bsalomon71cb0c22014-11-14 12:10:14 -0800831#if GR_CACHE_STATS
bsalomondace19e2014-11-17 07:34:06 -0800832 SkASSERT(fBudgetedHighWaterCount <= fHighWaterCount);
833 SkASSERT(fBudgetedHighWaterBytes <= fHighWaterBytes);
bsalomonf320e042015-02-17 15:09:34 -0800834 SkASSERT(fBytes <= fHighWaterBytes);
835 SkASSERT(fCount <= fHighWaterCount);
836 SkASSERT(fBudgetedBytes <= fBudgetedHighWaterBytes);
837 SkASSERT(fBudgetedCount <= fBudgetedHighWaterCount);
bsalomon71cb0c22014-11-14 12:10:14 -0800838#endif
bsalomon8718aaf2015-02-19 07:24:21 -0800839 SkASSERT(stats.fContent == fUniqueHash.count());
bsalomonf320e042015-02-17 15:09:34 -0800840 SkASSERT(stats.fScratch + stats.fCouldBeScratch == fScratchMap.count());
bsalomon71cb0c22014-11-14 12:10:14 -0800841
bsalomon3f324322015-04-08 11:01:54 -0700842 // This assertion is not currently valid because we can be in recursive notifyCntReachedZero()
bsalomon12299ab2014-11-14 13:33:09 -0800843 // calls. This will be fixed when subresource registration is explicit.
bsalomondace19e2014-11-17 07:34:06 -0800844 // bool overBudget = budgetedBytes > fMaxBytes || budgetedCount > fMaxCount;
bsalomon12299ab2014-11-14 13:33:09 -0800845 // SkASSERT(!overBudget || locked == count || fPurging);
bsalomon71cb0c22014-11-14 12:10:14 -0800846}
bsalomonf320e042015-02-17 15:09:34 -0800847
848bool GrResourceCache::isInCache(const GrGpuResource* resource) const {
849 int index = *resource->cacheAccess().accessCacheIndex();
850 if (index < 0) {
851 return false;
852 }
853 if (index < fPurgeableQueue.count() && fPurgeableQueue.at(index) == resource) {
854 return true;
855 }
856 if (index < fNonpurgeableResources.count() && fNonpurgeableResources[index] == resource) {
857 return true;
858 }
859 SkDEBUGFAIL("Resource index should be -1 or the resource should be in the cache.");
860 return false;
861}
862
bsalomon71cb0c22014-11-14 12:10:14 -0800863#endif
Robert Phillipsae7d3f32017-09-21 08:26:08 -0400864
Greg Danielcd871402017-09-26 12:49:26 -0400865void GrResourceCache::adoptUniqueKeyFromSurface(GrTextureProxy* proxy, const GrSurface* surf) {
866 SkASSERT(surf->getUniqueKey().isValid());
867 proxy->cacheAccess().setUniqueKey(this, surf->getUniqueKey());
868 SkASSERT(proxy->getUniqueKey() == surf->getUniqueKey());
869 // multiple proxies can't get the same key
870 SkASSERT(!fUniquelyKeyedProxies.find(surf->getUniqueKey()));
871 fUniquelyKeyedProxies.add(proxy);
872}
873
Robert Phillipsae7d3f32017-09-21 08:26:08 -0400874void GrResourceCache::assignUniqueKeyToProxy(const GrUniqueKey& key, GrTextureProxy* proxy) {
875 SkASSERT(key.isValid());
876 SkASSERT(proxy);
877
878 // If there is already a GrResource with this key then the caller has violated the normal
879 // usage pattern of uniquely keyed resources (e.g., they have created one w/o first seeing
880 // if it already existed in the cache).
881 SkASSERT(!this->findAndRefUniqueResource(key));
882
883 // Uncached resources can never have a unique key, unless they're wrapped resources. Wrapped
884 // resources are a special case: the unique keys give us a weak ref so that we can reuse the
885 // same resource (rather than re-wrapping). When a wrapped resource is no longer referenced,
886 // it will always be released - it is never converted to a scratch resource.
887 if (SkBudgeted::kNo == proxy->isBudgeted() &&
888 (!proxy->priv().isInstantiated() ||
889 !proxy->priv().peekSurface()->resourcePriv().refsWrappedObjects())) {
890 return;
891 }
892
893 SkASSERT(!fUniquelyKeyedProxies.find(key)); // multiple proxies can't get the same key
894
895 proxy->cacheAccess().setUniqueKey(this, key);
896 SkASSERT(proxy->getUniqueKey() == key);
897 fUniquelyKeyedProxies.add(proxy);
898}
899
900sk_sp<GrTextureProxy> GrResourceCache::findProxyByUniqueKey(const GrUniqueKey& key,
901 GrSurfaceOrigin origin) {
902
903 sk_sp<GrTextureProxy> result = sk_ref_sp(fUniquelyKeyedProxies.find(key));
904 if (result) {
905 SkASSERT(result->origin() == origin);
Greg Danielcd871402017-09-26 12:49:26 -0400906 }
907 return result;
908}
909
910sk_sp<GrTextureProxy> GrResourceCache::findOrCreateProxyByUniqueKey(const GrUniqueKey& key,
911 GrSurfaceOrigin origin) {
912 sk_sp<GrTextureProxy> result = this->findProxyByUniqueKey(key, origin);
913 if (result) {
Robert Phillipsae7d3f32017-09-21 08:26:08 -0400914 return result;
915 }
916
917 GrGpuResource* resource = findAndRefUniqueResource(key);
918 if (!resource) {
919 return nullptr;
920 }
921
922 sk_sp<GrTexture> texture(static_cast<GrSurface*>(resource)->asTexture());
923 SkASSERT(texture);
924
925 result = GrSurfaceProxy::MakeWrapped(std::move(texture), origin);
926 SkASSERT(result->getUniqueKey() == key);
Greg Danielcd871402017-09-26 12:49:26 -0400927 // MakeWrapped should've added this for us
928 SkASSERT(fUniquelyKeyedProxies.find(key));
Robert Phillipsae7d3f32017-09-21 08:26:08 -0400929 return result;
930}
931
932void GrResourceCache::processInvalidProxyUniqueKey(const GrUniqueKey& key) {
933 // Note: this method is called for the whole variety of GrGpuResources so often 'key'
934 // will not be in 'fUniquelyKeyedProxies'.
935 GrTextureProxy* proxy = fUniquelyKeyedProxies.find(key);
936 if (proxy) {
Greg Danielcd871402017-09-26 12:49:26 -0400937 this->processInvalidProxyUniqueKey(key, proxy, false);
938 }
939}
940
941void GrResourceCache::processInvalidProxyUniqueKey(const GrUniqueKey& key, GrTextureProxy* proxy,
942 bool invalidateSurface) {
943 SkASSERT(proxy);
944 SkASSERT(proxy->getUniqueKey().isValid());
945 SkASSERT(proxy->getUniqueKey() == key);
946
947 fUniquelyKeyedProxies.remove(key);
948 proxy->cacheAccess().clearUniqueKey();
949
950 if (invalidateSurface && proxy->priv().isInstantiated()) {
951 GrSurface* surface = proxy->priv().peekSurface();
952 if (surface) {
953 surface->resourcePriv().removeUniqueKey();
954 }
Robert Phillipsae7d3f32017-09-21 08:26:08 -0400955 }
956}
957