blob: 53dfe9b58df140eea07b067ee82b5b74683a5af8 [file] [log] [blame]
bsalomonc8dc1f72014-08-21 13:02:13 -07001/*
2 * Copyright 2014 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
bsalomon0ea80f42015-02-11 10:49:59 -08008#include "GrResourceCache.h"
Brian Salomon614c1a82018-12-19 15:42:06 -05009#include <atomic>
robertphillips28a838e2016-06-23 14:07:00 -070010#include "GrCaps.h"
bsalomon3582d3e2015-02-13 14:20:05 -080011#include "GrGpuResourceCacheAccess.h"
Robert Phillips1afd4cd2018-01-08 13:40:32 -050012#include "GrProxyProvider.h"
Brian Salomon614c1a82018-12-19 15:42:06 -050013#include "GrSingleOwner.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"
Brian Osmane3f543c2018-11-02 12:52:10 -040020#include "SkRandom.h"
Brian Salomon614c1a82018-12-19 15:42:06 -050021#include "SkScopeExit.h"
bsalomonddf30e62015-02-19 11:38:44 -080022#include "SkTSort.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -040023#include "SkTo.h"
bsalomon71cb0c22014-11-14 12:10:14 -080024
bsalomon8718aaf2015-02-19 07:24:21 -080025DECLARE_SKMESSAGEBUS_MESSAGE(GrUniqueKeyInvalidatedMessage);
bsalomon71cb0c22014-11-14 12:10:14 -080026
Brian Osman13dddce2017-05-09 13:19:50 -040027DECLARE_SKMESSAGEBUS_MESSAGE(GrGpuResourceFreedMessage);
28
Brian Salomon8f8995a2018-10-15 14:32:15 -040029#define ASSERT_SINGLE_OWNER \
30 SkDEBUGCODE(GrSingleOwner::AutoEnforce debug_SingleOwner(fSingleOwner);)
31
bsalomon71cb0c22014-11-14 12:10:14 -080032//////////////////////////////////////////////////////////////////////////////
33
bsalomon7775c852014-12-30 12:50:52 -080034GrScratchKey::ResourceType GrScratchKey::GenerateResourceType() {
Mike Klein0ec1c572018-12-04 11:52:51 -050035 static std::atomic<int32_t> nextType{INHERITED::kInvalidDomain + 1};
bsalomonfe369ee2014-11-10 11:59:06 -080036
Mike Klein0ec1c572018-12-04 11:52:51 -050037 int32_t type = nextType++;
Ben Wagner9bc36fd2018-06-15 14:23:36 -040038 if (type > SkTo<int32_t>(UINT16_MAX)) {
Ben Wagnerb4aab9a2017-08-16 10:53:04 -040039 SK_ABORT("Too many Resource Types");
bsalomon71cb0c22014-11-14 12:10:14 -080040 }
41
42 return static_cast<ResourceType>(type);
43}
44
bsalomon8718aaf2015-02-19 07:24:21 -080045GrUniqueKey::Domain GrUniqueKey::GenerateDomain() {
Mike Klein0ec1c572018-12-04 11:52:51 -050046 static std::atomic<int32_t> nextDomain{INHERITED::kInvalidDomain + 1};
bsalomon7775c852014-12-30 12:50:52 -080047
Mike Klein0ec1c572018-12-04 11:52:51 -050048 int32_t domain = nextDomain++;
Ben Wagner397ee0e2018-06-15 15:13:26 -040049 if (domain > SkTo<int32_t>(UINT16_MAX)) {
Ben Wagnerb4aab9a2017-08-16 10:53:04 -040050 SK_ABORT("Too many GrUniqueKey Domains");
bsalomon7775c852014-12-30 12:50:52 -080051 }
bsalomon24db3b12015-01-23 04:24:04 -080052
53 return static_cast<Domain>(domain);
54}
bsalomon3f324322015-04-08 11:01:54 -070055
bsalomon24db3b12015-01-23 04:24:04 -080056uint32_t GrResourceKeyHash(const uint32_t* data, size_t size) {
mtklein4e976072016-08-08 09:06:27 -070057 return SkOpts::hash(data, size);
bsalomon7775c852014-12-30 12:50:52 -080058}
59
bsalomonfe369ee2014-11-10 11:59:06 -080060//////////////////////////////////////////////////////////////////////////////
61
bsalomon0ea80f42015-02-11 10:49:59 -080062class GrResourceCache::AutoValidate : ::SkNoncopyable {
bsalomon71cb0c22014-11-14 12:10:14 -080063public:
bsalomon0ea80f42015-02-11 10:49:59 -080064 AutoValidate(GrResourceCache* cache) : fCache(cache) { cache->validate(); }
bsalomon71cb0c22014-11-14 12:10:14 -080065 ~AutoValidate() { fCache->validate(); }
66private:
bsalomon0ea80f42015-02-11 10:49:59 -080067 GrResourceCache* fCache;
bsalomon71cb0c22014-11-14 12:10:14 -080068};
69
70 //////////////////////////////////////////////////////////////////////////////
robertphillipsee843b22016-10-04 05:30:20 -070071
Brian Salomon8f8995a2018-10-15 14:32:15 -040072GrResourceCache::GrResourceCache(const GrCaps* caps, GrSingleOwner* singleOwner,
73 uint32_t contextUniqueID)
Brian Salomon238069b2018-07-11 15:58:57 -040074 : fProxyProvider(nullptr)
75 , fTimestamp(0)
76 , fMaxCount(kDefaultMaxCount)
77 , fMaxBytes(kDefaultMaxSize)
bsalomon71cb0c22014-11-14 12:10:14 -080078#if GR_CACHE_STATS
Brian Salomon238069b2018-07-11 15:58:57 -040079 , fHighWaterCount(0)
80 , fHighWaterBytes(0)
81 , fBudgetedHighWaterCount(0)
82 , fBudgetedHighWaterBytes(0)
bsalomon71cb0c22014-11-14 12:10:14 -080083#endif
Brian Salomon238069b2018-07-11 15:58:57 -040084 , fBytes(0)
85 , fBudgetedCount(0)
86 , fBudgetedBytes(0)
87 , fPurgeableBytes(0)
Brian Salomon238069b2018-07-11 15:58:57 -040088 , fInvalidUniqueKeyInbox(contextUniqueID)
89 , fFreedGpuResourceInbox(contextUniqueID)
90 , fContextUniqueID(contextUniqueID)
Brian Salomon8f8995a2018-10-15 14:32:15 -040091 , fSingleOwner(singleOwner)
Brian Salomon238069b2018-07-11 15:58:57 -040092 , fPreferVRAMUseOverFlushes(caps->preferVRAMUseOverFlushes()) {
93 SkASSERT(contextUniqueID != SK_InvalidUniqueID);
bsalomonf320e042015-02-17 15:09:34 -080094 SkDEBUGCODE(fCount = 0;)
halcanary96fcdcc2015-08-27 07:41:13 -070095 SkDEBUGCODE(fNewlyPurgeableResourceForValidation = nullptr;)
bsalomon71cb0c22014-11-14 12:10:14 -080096}
97
bsalomon0ea80f42015-02-11 10:49:59 -080098GrResourceCache::~GrResourceCache() {
bsalomonc8dc1f72014-08-21 13:02:13 -070099 this->releaseAll();
100}
101
Brian Salomon43b882b2018-09-07 16:29:14 -0400102void GrResourceCache::setLimits(int count, size_t bytes) {
bsalomon71cb0c22014-11-14 12:10:14 -0800103 fMaxCount = count;
104 fMaxBytes = bytes;
105 this->purgeAsNeeded();
106}
107
bsalomon0ea80f42015-02-11 10:49:59 -0800108void GrResourceCache::insertResource(GrGpuResource* resource) {
Brian Salomon8f8995a2018-10-15 14:32:15 -0400109 ASSERT_SINGLE_OWNER
bsalomon49f085d2014-09-05 13:34:00 -0700110 SkASSERT(resource);
bsalomon16961262014-08-26 14:01:07 -0700111 SkASSERT(!this->isInCache(resource));
bsalomonf320e042015-02-17 15:09:34 -0800112 SkASSERT(!resource->wasDestroyed());
Brian Salomon614c1a82018-12-19 15:42:06 -0500113 SkASSERT(!resource->resourcePriv().isPurgeable());
bsalomonddf30e62015-02-19 11:38:44 -0800114
115 // We must set the timestamp before adding to the array in case the timestamp wraps and we wind
116 // up iterating over all the resources that already have timestamps.
117 resource->cacheAccess().setTimestamp(this->getNextTimestamp());
118
bsalomonf320e042015-02-17 15:09:34 -0800119 this->addToNonpurgeableArray(resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800120
bsalomondace19e2014-11-17 07:34:06 -0800121 size_t size = resource->gpuMemorySize();
bsalomonf320e042015-02-17 15:09:34 -0800122 SkDEBUGCODE(++fCount;)
bsalomon84c8e622014-11-17 09:33:27 -0800123 fBytes += size;
bsalomon82b1d622014-11-14 13:59:57 -0800124#if GR_CACHE_STATS
bsalomonf320e042015-02-17 15:09:34 -0800125 fHighWaterCount = SkTMax(this->getResourceCount(), fHighWaterCount);
bsalomon82b1d622014-11-14 13:59:57 -0800126 fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes);
127#endif
bsalomon5ec26ae2016-02-25 08:33:02 -0800128 if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
bsalomondace19e2014-11-17 07:34:06 -0800129 ++fBudgetedCount;
130 fBudgetedBytes += size;
Brian Osman39c08ac2017-07-26 09:36:09 -0400131 TRACE_COUNTER2("skia.gpu.cache", "skia budget", "used",
hendrikw876c3132015-03-04 10:33:49 -0800132 fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
bsalomondace19e2014-11-17 07:34:06 -0800133#if GR_CACHE_STATS
134 fBudgetedHighWaterCount = SkTMax(fBudgetedCount, fBudgetedHighWaterCount);
135 fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
136#endif
137 }
robertphillipsc4ed6842016-05-24 14:17:12 -0700138 if (resource->resourcePriv().getScratchKey().isValid() &&
139 !resource->getUniqueKey().isValid()) {
kkinnunen2e6055b2016-04-22 01:48:29 -0700140 SkASSERT(!resource->resourcePriv().refsWrappedObjects());
bsalomon3582d3e2015-02-13 14:20:05 -0800141 fScratchMap.insert(resource->resourcePriv().getScratchKey(), resource);
bsalomon744998e2014-08-28 09:54:34 -0700142 }
bsalomon9f2d1572015-02-17 11:47:40 -0800143
bsalomon71cb0c22014-11-14 12:10:14 -0800144 this->purgeAsNeeded();
bsalomonc8dc1f72014-08-21 13:02:13 -0700145}
146
bsalomon0ea80f42015-02-11 10:49:59 -0800147void GrResourceCache::removeResource(GrGpuResource* resource) {
Brian Salomon8f8995a2018-10-15 14:32:15 -0400148 ASSERT_SINGLE_OWNER
bsalomon9f2d1572015-02-17 11:47:40 -0800149 this->validate();
bsalomon16961262014-08-26 14:01:07 -0700150 SkASSERT(this->isInCache(resource));
bsalomondace19e2014-11-17 07:34:06 -0800151
Derek Sollenbergeree479142017-05-24 11:41:33 -0400152 size_t size = resource->gpuMemorySize();
Brian Salomon614c1a82018-12-19 15:42:06 -0500153 if (resource->resourcePriv().isPurgeable()) {
bsalomon9f2d1572015-02-17 11:47:40 -0800154 fPurgeableQueue.remove(resource);
Derek Sollenbergeree479142017-05-24 11:41:33 -0400155 fPurgeableBytes -= size;
bsalomonf320e042015-02-17 15:09:34 -0800156 } else {
157 this->removeFromNonpurgeableArray(resource);
bsalomon9f2d1572015-02-17 11:47:40 -0800158 }
159
bsalomonf320e042015-02-17 15:09:34 -0800160 SkDEBUGCODE(--fCount;)
bsalomondace19e2014-11-17 07:34:06 -0800161 fBytes -= size;
bsalomon5ec26ae2016-02-25 08:33:02 -0800162 if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
bsalomondace19e2014-11-17 07:34:06 -0800163 --fBudgetedCount;
164 fBudgetedBytes -= size;
Brian Osman39c08ac2017-07-26 09:36:09 -0400165 TRACE_COUNTER2("skia.gpu.cache", "skia budget", "used",
hendrikw876c3132015-03-04 10:33:49 -0800166 fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
bsalomondace19e2014-11-17 07:34:06 -0800167 }
168
robertphillipsc4ed6842016-05-24 14:17:12 -0700169 if (resource->resourcePriv().getScratchKey().isValid() &&
170 !resource->getUniqueKey().isValid()) {
bsalomon3582d3e2015-02-13 14:20:05 -0800171 fScratchMap.remove(resource->resourcePriv().getScratchKey(), resource);
bsalomon744998e2014-08-28 09:54:34 -0700172 }
bsalomon8718aaf2015-02-19 07:24:21 -0800173 if (resource->getUniqueKey().isValid()) {
174 fUniqueHash.remove(resource->getUniqueKey());
bsalomon8b79d232014-11-10 10:19:06 -0800175 }
bsalomonb436ed62014-11-17 12:15:56 -0800176 this->validate();
bsalomonc8dc1f72014-08-21 13:02:13 -0700177}
178
bsalomon0ea80f42015-02-11 10:49:59 -0800179void GrResourceCache::abandonAll() {
bsalomon71cb0c22014-11-14 12:10:14 -0800180 AutoValidate av(this);
181
Greg Danielb2acf0a2018-09-12 09:17:11 -0400182 for (int i = 0; i < fResourcesWaitingForFreeMsg.count(); ++i) {
183 fResourcesWaitingForFreeMsg[i]->abandon();
184 }
185 fResourcesWaitingForFreeMsg.reset();
186
bsalomonf320e042015-02-17 15:09:34 -0800187 while (fNonpurgeableResources.count()) {
188 GrGpuResource* back = *(fNonpurgeableResources.end() - 1);
189 SkASSERT(!back->wasDestroyed());
Brian Salomon614c1a82018-12-19 15:42:06 -0500190 // If these resources we're relying on a purgeable notification to release something, notify
191 // them now. They aren't in the purgeable queue but they're getting purged anyway.
192 back->cacheAccess().becamePurgeable();
bsalomonf320e042015-02-17 15:09:34 -0800193 back->cacheAccess().abandon();
bsalomonc8dc1f72014-08-21 13:02:13 -0700194 }
bsalomonf320e042015-02-17 15:09:34 -0800195
196 while (fPurgeableQueue.count()) {
197 GrGpuResource* top = fPurgeableQueue.peek();
198 SkASSERT(!top->wasDestroyed());
199 top->cacheAccess().abandon();
200 }
201
bsalomon744998e2014-08-28 09:54:34 -0700202 SkASSERT(!fScratchMap.count());
bsalomon8718aaf2015-02-19 07:24:21 -0800203 SkASSERT(!fUniqueHash.count());
bsalomonc8dc1f72014-08-21 13:02:13 -0700204 SkASSERT(!fCount);
bsalomonf320e042015-02-17 15:09:34 -0800205 SkASSERT(!this->getResourceCount());
bsalomondace19e2014-11-17 07:34:06 -0800206 SkASSERT(!fBytes);
207 SkASSERT(!fBudgetedCount);
208 SkASSERT(!fBudgetedBytes);
Derek Sollenbergeree479142017-05-24 11:41:33 -0400209 SkASSERT(!fPurgeableBytes);
Greg Danielb2acf0a2018-09-12 09:17:11 -0400210 SkASSERT(!fResourcesWaitingForFreeMsg.count());
bsalomonc8dc1f72014-08-21 13:02:13 -0700211}
212
bsalomon0ea80f42015-02-11 10:49:59 -0800213void GrResourceCache::releaseAll() {
bsalomon71cb0c22014-11-14 12:10:14 -0800214 AutoValidate av(this);
215
Brian Osman13dddce2017-05-09 13:19:50 -0400216 this->processFreedGpuResources();
217
Greg Danielc27eb722018-08-10 09:48:08 -0400218 // We need to make sure to free any resources that were waiting on a free message but never
219 // received one.
220 for (int i = 0; i < fResourcesWaitingForFreeMsg.count(); ++i) {
221 fResourcesWaitingForFreeMsg[i]->unref();
222 }
Greg Danielb2acf0a2018-09-12 09:17:11 -0400223 fResourcesWaitingForFreeMsg.reset();
Greg Danielc27eb722018-08-10 09:48:08 -0400224
Robert Phillips1afd4cd2018-01-08 13:40:32 -0500225 SkASSERT(fProxyProvider); // better have called setProxyProvider
Robert Phillips3ec95732017-09-29 15:10:39 -0400226 // We must remove the uniqueKeys from the proxies here. While they possess a uniqueKey
227 // they also have a raw pointer back to this class (which is presumably going away)!
Robert Phillips1afd4cd2018-01-08 13:40:32 -0500228 fProxyProvider->removeAllUniqueKeys();
Robert Phillips45a6f142017-09-29 09:49:41 -0400229
Brian Salomon614c1a82018-12-19 15:42:06 -0500230 while (fNonpurgeableResources.count()) {
bsalomonf320e042015-02-17 15:09:34 -0800231 GrGpuResource* back = *(fNonpurgeableResources.end() - 1);
232 SkASSERT(!back->wasDestroyed());
Brian Salomon614c1a82018-12-19 15:42:06 -0500233 // If these resources we're relying on a purgeable notification to release something, notify
234 // them now. They aren't in the purgeable queue but they're getting purged anyway.
235 back->cacheAccess().becamePurgeable();
bsalomonf320e042015-02-17 15:09:34 -0800236 back->cacheAccess().release();
bsalomonc8dc1f72014-08-21 13:02:13 -0700237 }
bsalomonf320e042015-02-17 15:09:34 -0800238
239 while (fPurgeableQueue.count()) {
240 GrGpuResource* top = fPurgeableQueue.peek();
241 SkASSERT(!top->wasDestroyed());
242 top->cacheAccess().release();
243 }
244
bsalomon744998e2014-08-28 09:54:34 -0700245 SkASSERT(!fScratchMap.count());
bsalomon8718aaf2015-02-19 07:24:21 -0800246 SkASSERT(!fUniqueHash.count());
bsalomonc8dc1f72014-08-21 13:02:13 -0700247 SkASSERT(!fCount);
bsalomonf320e042015-02-17 15:09:34 -0800248 SkASSERT(!this->getResourceCount());
bsalomondace19e2014-11-17 07:34:06 -0800249 SkASSERT(!fBytes);
250 SkASSERT(!fBudgetedCount);
251 SkASSERT(!fBudgetedBytes);
Derek Sollenbergeree479142017-05-24 11:41:33 -0400252 SkASSERT(!fPurgeableBytes);
Greg Danielb2acf0a2018-09-12 09:17:11 -0400253 SkASSERT(!fResourcesWaitingForFreeMsg.count());
bsalomonc8dc1f72014-08-21 13:02:13 -0700254}
bsalomonbcf0a522014-10-08 08:40:09 -0700255
bsalomon0ea80f42015-02-11 10:49:59 -0800256class GrResourceCache::AvailableForScratchUse {
bsalomonbcf0a522014-10-08 08:40:09 -0700257public:
bsalomon000f8292014-10-15 19:04:14 -0700258 AvailableForScratchUse(bool rejectPendingIO) : fRejectPendingIO(rejectPendingIO) { }
bsalomonbcf0a522014-10-08 08:40:09 -0700259
260 bool operator()(const GrGpuResource* resource) const {
robertphillipsc4ed6842016-05-24 14:17:12 -0700261 SkASSERT(!resource->getUniqueKey().isValid() &&
262 resource->resourcePriv().getScratchKey().isValid());
bsalomon12299ab2014-11-14 13:33:09 -0800263 if (resource->internalHasRef() || !resource->cacheAccess().isScratch()) {
bsalomon000f8292014-10-15 19:04:14 -0700264 return false;
bsalomonbcf0a522014-10-08 08:40:09 -0700265 }
bsalomon000f8292014-10-15 19:04:14 -0700266 return !fRejectPendingIO || !resource->internalHasPendingIO();
bsalomonbcf0a522014-10-08 08:40:09 -0700267 }
bsalomon1e2530b2014-10-09 09:57:18 -0700268
bsalomonbcf0a522014-10-08 08:40:09 -0700269private:
bsalomon000f8292014-10-15 19:04:14 -0700270 bool fRejectPendingIO;
bsalomonbcf0a522014-10-08 08:40:09 -0700271};
272
bsalomon0ea80f42015-02-11 10:49:59 -0800273GrGpuResource* GrResourceCache::findAndRefScratchResource(const GrScratchKey& scratchKey,
robertphillips6e83ac72015-08-13 05:19:14 -0700274 size_t resourceSize,
Chris Daltond004e0b2018-09-27 09:28:03 -0600275 ScratchFlags flags) {
bsalomon7775c852014-12-30 12:50:52 -0800276 SkASSERT(scratchKey.isValid());
robertphillipsee843b22016-10-04 05:30:20 -0700277
bsalomon71cb0c22014-11-14 12:10:14 -0800278 GrGpuResource* resource;
Chris Daltond004e0b2018-09-27 09:28:03 -0600279 if (flags & (ScratchFlags::kPreferNoPendingIO | ScratchFlags::kRequireNoPendingIO)) {
bsalomon71cb0c22014-11-14 12:10:14 -0800280 resource = fScratchMap.find(scratchKey, AvailableForScratchUse(true));
bsalomon000f8292014-10-15 19:04:14 -0700281 if (resource) {
bsalomon9f2d1572015-02-17 11:47:40 -0800282 this->refAndMakeResourceMRU(resource);
bsalomonb436ed62014-11-17 12:15:56 -0800283 this->validate();
284 return resource;
Chris Daltond004e0b2018-09-27 09:28:03 -0600285 } else if (flags & ScratchFlags::kRequireNoPendingIO) {
halcanary96fcdcc2015-08-27 07:41:13 -0700286 return nullptr;
bsalomon000f8292014-10-15 19:04:14 -0700287 }
robertphillips63926682015-08-20 09:39:02 -0700288 // We would prefer to consume more available VRAM rather than flushing
289 // immediately, but on ANGLE this can lead to starving of the GPU.
290 if (fPreferVRAMUseOverFlushes && this->wouldFit(resourceSize)) {
robertphillips6e83ac72015-08-13 05:19:14 -0700291 // kPrefer is specified, we didn't find a resource without pending io,
robertphillips63926682015-08-20 09:39:02 -0700292 // but there is still space in our budget for the resource so force
293 // the caller to allocate a new resource.
halcanary96fcdcc2015-08-27 07:41:13 -0700294 return nullptr;
robertphillips6e83ac72015-08-13 05:19:14 -0700295 }
bsalomon000f8292014-10-15 19:04:14 -0700296 }
bsalomon71cb0c22014-11-14 12:10:14 -0800297 resource = fScratchMap.find(scratchKey, AvailableForScratchUse(false));
298 if (resource) {
bsalomon9f2d1572015-02-17 11:47:40 -0800299 this->refAndMakeResourceMRU(resource);
bsalomonb436ed62014-11-17 12:15:56 -0800300 this->validate();
bsalomon71cb0c22014-11-14 12:10:14 -0800301 }
302 return resource;
bsalomonbcf0a522014-10-08 08:40:09 -0700303}
bsalomon8b79d232014-11-10 10:19:06 -0800304
bsalomon0ea80f42015-02-11 10:49:59 -0800305void GrResourceCache::willRemoveScratchKey(const GrGpuResource* resource) {
Brian Salomon8f8995a2018-10-15 14:32:15 -0400306 ASSERT_SINGLE_OWNER
bsalomon3582d3e2015-02-13 14:20:05 -0800307 SkASSERT(resource->resourcePriv().getScratchKey().isValid());
robertphillipsc4ed6842016-05-24 14:17:12 -0700308 if (!resource->getUniqueKey().isValid()) {
309 fScratchMap.remove(resource->resourcePriv().getScratchKey(), resource);
310 }
bsalomon10e23ca2014-11-25 05:52:06 -0800311}
312
bsalomonf99e9612015-02-19 08:24:16 -0800313void GrResourceCache::removeUniqueKey(GrGpuResource* resource) {
Brian Salomon8f8995a2018-10-15 14:32:15 -0400314 ASSERT_SINGLE_OWNER
bsalomon3f324322015-04-08 11:01:54 -0700315 // Someone has a ref to this resource in order to have removed the key. When the ref count
316 // reaches zero we will get a ref cnt notification and figure out what to do with it.
bsalomonf99e9612015-02-19 08:24:16 -0800317 if (resource->getUniqueKey().isValid()) {
318 SkASSERT(resource == fUniqueHash.find(resource->getUniqueKey()));
319 fUniqueHash.remove(resource->getUniqueKey());
320 }
321 resource->cacheAccess().removeUniqueKey();
robertphillipsc4ed6842016-05-24 14:17:12 -0700322
323 if (resource->resourcePriv().getScratchKey().isValid()) {
324 fScratchMap.insert(resource->resourcePriv().getScratchKey(), resource);
325 }
326
bsalomonf99e9612015-02-19 08:24:16 -0800327 this->validate();
bsalomon23e619c2015-02-06 11:54:28 -0800328}
329
bsalomonf99e9612015-02-19 08:24:16 -0800330void GrResourceCache::changeUniqueKey(GrGpuResource* resource, const GrUniqueKey& newKey) {
Brian Salomon8f8995a2018-10-15 14:32:15 -0400331 ASSERT_SINGLE_OWNER
bsalomon8b79d232014-11-10 10:19:06 -0800332 SkASSERT(resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800333 SkASSERT(this->isInCache(resource));
bsalomon8b79d232014-11-10 10:19:06 -0800334
bsalomonf99e9612015-02-19 08:24:16 -0800335 // If another resource has the new key, remove its key then install the key on this resource.
336 if (newKey.isValid()) {
Greg Daniel0d537802017-09-08 11:44:14 -0400337 if (GrGpuResource* old = fUniqueHash.find(newKey)) {
338 // If the old resource using the key is purgeable and is unreachable, then remove it.
Brian Salomon614c1a82018-12-19 15:42:06 -0500339 if (!old->resourcePriv().getScratchKey().isValid() &&
340 old->resourcePriv().isPurgeable()) {
Greg Daniel0d537802017-09-08 11:44:14 -0400341 old->cacheAccess().release();
342 } else {
343 this->removeUniqueKey(old);
344 }
345 }
346 SkASSERT(nullptr == fUniqueHash.find(newKey));
347
robertphillipsc4ed6842016-05-24 14:17:12 -0700348 // Remove the entry for this resource if it already has a unique key.
349 if (resource->getUniqueKey().isValid()) {
350 SkASSERT(resource == fUniqueHash.find(resource->getUniqueKey()));
351 fUniqueHash.remove(resource->getUniqueKey());
352 SkASSERT(nullptr == fUniqueHash.find(resource->getUniqueKey()));
353 } else {
354 // 'resource' didn't have a valid unique key before so it is switching sides. Remove it
355 // from the ScratchMap
356 if (resource->resourcePriv().getScratchKey().isValid()) {
357 fScratchMap.remove(resource->resourcePriv().getScratchKey(), resource);
358 }
359 }
360
bsalomonf99e9612015-02-19 08:24:16 -0800361 resource->cacheAccess().setUniqueKey(newKey);
362 fUniqueHash.add(resource);
363 } else {
robertphillipsc4ed6842016-05-24 14:17:12 -0700364 this->removeUniqueKey(resource);
bsalomonf99e9612015-02-19 08:24:16 -0800365 }
366
bsalomon71cb0c22014-11-14 12:10:14 -0800367 this->validate();
bsalomon8b79d232014-11-10 10:19:06 -0800368}
bsalomon71cb0c22014-11-14 12:10:14 -0800369
bsalomon9f2d1572015-02-17 11:47:40 -0800370void GrResourceCache::refAndMakeResourceMRU(GrGpuResource* resource) {
Brian Salomon8f8995a2018-10-15 14:32:15 -0400371 ASSERT_SINGLE_OWNER
bsalomon71cb0c22014-11-14 12:10:14 -0800372 SkASSERT(resource);
373 SkASSERT(this->isInCache(resource));
bsalomonddf30e62015-02-19 11:38:44 -0800374
Brian Salomon614c1a82018-12-19 15:42:06 -0500375 if (resource->resourcePriv().isPurgeable()) {
bsalomon9f2d1572015-02-17 11:47:40 -0800376 // It's about to become unpurgeable.
Derek Sollenbergeree479142017-05-24 11:41:33 -0400377 fPurgeableBytes -= resource->gpuMemorySize();
bsalomon9f2d1572015-02-17 11:47:40 -0800378 fPurgeableQueue.remove(resource);
bsalomonf320e042015-02-17 15:09:34 -0800379 this->addToNonpurgeableArray(resource);
bsalomon9f2d1572015-02-17 11:47:40 -0800380 }
381 resource->ref();
bsalomonddf30e62015-02-19 11:38:44 -0800382
383 resource->cacheAccess().setTimestamp(this->getNextTimestamp());
bsalomonf320e042015-02-17 15:09:34 -0800384 this->validate();
bsalomon71cb0c22014-11-14 12:10:14 -0800385}
386
bsalomon3f324322015-04-08 11:01:54 -0700387void GrResourceCache::notifyCntReachedZero(GrGpuResource* resource, uint32_t flags) {
Brian Salomon8f8995a2018-10-15 14:32:15 -0400388 ASSERT_SINGLE_OWNER
bsalomon71cb0c22014-11-14 12:10:14 -0800389 SkASSERT(resource);
bsalomon3f324322015-04-08 11:01:54 -0700390 SkASSERT(!resource->wasDestroyed());
391 SkASSERT(flags);
bsalomon71cb0c22014-11-14 12:10:14 -0800392 SkASSERT(this->isInCache(resource));
bsalomon3f324322015-04-08 11:01:54 -0700393 // This resource should always be in the nonpurgeable array when this function is called. It
394 // will be moved to the queue if it is newly purgeable.
395 SkASSERT(fNonpurgeableResources[*resource->cacheAccess().accessCacheIndex()] == resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800396
bsalomon3f324322015-04-08 11:01:54 -0700397 if (SkToBool(ResourceAccess::kRefCntReachedZero_RefNotificationFlag & flags)) {
398#ifdef SK_DEBUG
399 // When the timestamp overflows validate() is called. validate() checks that resources in
400 // the nonpurgeable array are indeed not purgeable. However, the movement from the array to
401 // the purgeable queue happens just below in this function. So we mark it as an exception.
Brian Salomon614c1a82018-12-19 15:42:06 -0500402 if (resource->resourcePriv().isPurgeable()) {
bsalomon3f324322015-04-08 11:01:54 -0700403 fNewlyPurgeableResourceForValidation = resource;
404 }
405#endif
406 resource->cacheAccess().setTimestamp(this->getNextTimestamp());
halcanary96fcdcc2015-08-27 07:41:13 -0700407 SkDEBUGCODE(fNewlyPurgeableResourceForValidation = nullptr);
bsalomon3f324322015-04-08 11:01:54 -0700408 }
409
410 if (!SkToBool(ResourceAccess::kAllCntsReachedZero_RefNotificationFlag & flags)) {
Brian Salomon614c1a82018-12-19 15:42:06 -0500411 SkASSERT(!resource->resourcePriv().isPurgeable());
bsalomon3f324322015-04-08 11:01:54 -0700412 return;
413 }
414
Brian Salomon614c1a82018-12-19 15:42:06 -0500415 SkASSERT(resource->resourcePriv().isPurgeable());
bsalomonf320e042015-02-17 15:09:34 -0800416 this->removeFromNonpurgeableArray(resource);
bsalomon9f2d1572015-02-17 11:47:40 -0800417 fPurgeableQueue.insert(resource);
Brian Salomon5e150852017-03-22 14:53:13 -0400418 resource->cacheAccess().setTimeWhenResourceBecomePurgeable();
Derek Sollenbergeree479142017-05-24 11:41:33 -0400419 fPurgeableBytes += resource->gpuMemorySize();
bsalomon71cb0c22014-11-14 12:10:14 -0800420
Greg Daniel303e83e2018-09-10 14:10:19 -0400421 bool hasUniqueKey = resource->getUniqueKey().isValid();
422
Brian Salomon614c1a82018-12-19 15:42:06 -0500423 {
424 SkScopeExit notifyPurgeable([resource] { resource->cacheAccess().becamePurgeable(); });
425
426 if (SkBudgeted::kNo == resource->resourcePriv().isBudgeted()) {
427 // We keep unbudgeted resources with a unique key in the purgable queue of the cache so
428 // they can be reused again by the image connected to the unique key.
429 if (hasUniqueKey && !resource->cacheAccess().shouldPurgeImmediately()) {
430 return;
431 }
432 // Check whether this resource could still be used as a scratch resource.
433 if (!resource->resourcePriv().refsWrappedObjects() &&
434 resource->resourcePriv().getScratchKey().isValid()) {
435 // We won't purge an existing resource to make room for this one.
436 if (fBudgetedCount < fMaxCount &&
437 fBudgetedBytes + resource->gpuMemorySize() <= fMaxBytes) {
438 resource->resourcePriv().makeBudgeted();
439 return;
440 }
441 }
442 } else {
443 // Purge the resource immediately if we're over budget
444 // Also purge if the resource has neither a valid scratch key nor a unique key.
445 bool hasKey = resource->resourcePriv().getScratchKey().isValid() || hasUniqueKey;
446 if (!this->overBudget() && hasKey) {
bsalomon9f2d1572015-02-17 11:47:40 -0800447 return;
bsalomonc2f35b72015-01-23 07:19:22 -0800448 }
bsalomonc2f35b72015-01-23 07:19:22 -0800449 }
bsalomonc2f35b72015-01-23 07:19:22 -0800450 }
bsalomonf320e042015-02-17 15:09:34 -0800451 SkDEBUGCODE(int beforeCount = this->getResourceCount();)
bsalomon9f2d1572015-02-17 11:47:40 -0800452 resource->cacheAccess().release();
453 // We should at least free this resource, perhaps dependent resources as well.
bsalomonf320e042015-02-17 15:09:34 -0800454 SkASSERT(this->getResourceCount() < beforeCount);
bsalomon71cb0c22014-11-14 12:10:14 -0800455 this->validate();
456}
457
bsalomon0ea80f42015-02-11 10:49:59 -0800458void GrResourceCache::didChangeBudgetStatus(GrGpuResource* resource) {
Brian Salomon8f8995a2018-10-15 14:32:15 -0400459 ASSERT_SINGLE_OWNER
bsalomon84c8e622014-11-17 09:33:27 -0800460 SkASSERT(resource);
461 SkASSERT(this->isInCache(resource));
462
463 size_t size = resource->gpuMemorySize();
464
bsalomon5ec26ae2016-02-25 08:33:02 -0800465 if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
bsalomon84c8e622014-11-17 09:33:27 -0800466 ++fBudgetedCount;
467 fBudgetedBytes += size;
bsalomonafe30052015-01-16 07:32:33 -0800468#if GR_CACHE_STATS
469 fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
470 fBudgetedHighWaterCount = SkTMax(fBudgetedCount, fBudgetedHighWaterCount);
471#endif
bsalomon84c8e622014-11-17 09:33:27 -0800472 this->purgeAsNeeded();
473 } else {
474 --fBudgetedCount;
475 fBudgetedBytes -= size;
476 }
Brian Osman39c08ac2017-07-26 09:36:09 -0400477 TRACE_COUNTER2("skia.gpu.cache", "skia budget", "used",
hendrikw876c3132015-03-04 10:33:49 -0800478 fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
bsalomon84c8e622014-11-17 09:33:27 -0800479
480 this->validate();
481}
482
robertphillipsee843b22016-10-04 05:30:20 -0700483void GrResourceCache::purgeAsNeeded() {
bsalomon3f324322015-04-08 11:01:54 -0700484 SkTArray<GrUniqueKeyInvalidatedMessage> invalidKeyMsgs;
485 fInvalidUniqueKeyInbox.poll(&invalidKeyMsgs);
486 if (invalidKeyMsgs.count()) {
Robert Phillips427966a2018-12-20 17:20:43 -0500487 SkASSERT(fProxyProvider);
488
489 for (int i = 0; i < invalidKeyMsgs.count(); ++i) {
490 fProxyProvider->processInvalidUniqueKey(invalidKeyMsgs[i].key(), nullptr,
491 GrProxyProvider::InvalidateGPUResource::kYes);
492 SkASSERT(!this->findAndRefUniqueResource(invalidKeyMsgs[i].key()));
493 }
bsalomon3f324322015-04-08 11:01:54 -0700494 }
bsalomon71cb0c22014-11-14 12:10:14 -0800495
Brian Osman13dddce2017-05-09 13:19:50 -0400496 this->processFreedGpuResources();
497
bsalomon3f324322015-04-08 11:01:54 -0700498 bool stillOverbudget = this->overBudget();
499 while (stillOverbudget && fPurgeableQueue.count()) {
robertphillipsee843b22016-10-04 05:30:20 -0700500 GrGpuResource* resource = fPurgeableQueue.peek();
Brian Salomon614c1a82018-12-19 15:42:06 -0500501 SkASSERT(resource->resourcePriv().isPurgeable());
bsalomon9f2d1572015-02-17 11:47:40 -0800502 resource->cacheAccess().release();
bsalomon3f324322015-04-08 11:01:54 -0700503 stillOverbudget = this->overBudget();
bsalomon9f2d1572015-02-17 11:47:40 -0800504 }
bsalomon71cb0c22014-11-14 12:10:14 -0800505
bsalomonb436ed62014-11-17 12:15:56 -0800506 this->validate();
bsalomon71cb0c22014-11-14 12:10:14 -0800507}
508
Robert Phillips6eba0632018-03-28 12:25:42 -0400509void GrResourceCache::purgeUnlockedResources(bool scratchResourcesOnly) {
510 if (!scratchResourcesOnly) {
511 // We could disable maintaining the heap property here, but it would add a lot of
512 // complexity. Moreover, this is rarely called.
513 while (fPurgeableQueue.count()) {
514 GrGpuResource* resource = fPurgeableQueue.peek();
Brian Salomon614c1a82018-12-19 15:42:06 -0500515 SkASSERT(resource->resourcePriv().isPurgeable());
Robert Phillips6eba0632018-03-28 12:25:42 -0400516 resource->cacheAccess().release();
517 }
518 } else {
519 // Sort the queue
520 fPurgeableQueue.sort();
521
522 // Make a list of the scratch resources to delete
523 SkTDArray<GrGpuResource*> scratchResources;
524 for (int i = 0; i < fPurgeableQueue.count(); i++) {
525 GrGpuResource* resource = fPurgeableQueue.at(i);
Brian Salomon614c1a82018-12-19 15:42:06 -0500526 SkASSERT(resource->resourcePriv().isPurgeable());
Robert Phillips6eba0632018-03-28 12:25:42 -0400527 if (!resource->getUniqueKey().isValid()) {
528 *scratchResources.append() = resource;
529 }
530 }
531
532 // Delete the scratch resources. This must be done as a separate pass
533 // to avoid messing up the sorted order of the queue
534 for (int i = 0; i < scratchResources.count(); i++) {
535 scratchResources.getAt(i)->cacheAccess().release();
536 }
bsalomon9f2d1572015-02-17 11:47:40 -0800537 }
bsalomon71cb0c22014-11-14 12:10:14 -0800538
bsalomonb436ed62014-11-17 12:15:56 -0800539 this->validate();
bsalomon71cb0c22014-11-14 12:10:14 -0800540}
541
Brian Salomon5e150852017-03-22 14:53:13 -0400542void GrResourceCache::purgeResourcesNotUsedSince(GrStdSteadyClock::time_point purgeTime) {
543 while (fPurgeableQueue.count()) {
544 const GrStdSteadyClock::time_point resourceTime =
545 fPurgeableQueue.peek()->cacheAccess().timeWhenResourceBecamePurgeable();
546 if (resourceTime >= purgeTime) {
547 // Resources were given both LRU timestamps and tagged with a frame number when
548 // they first became purgeable. The LRU timestamp won't change again until the
549 // resource is made non-purgeable again. So, at this point all the remaining
550 // resources in the timestamp-sorted queue will have a frame number >= to this
551 // one.
552 break;
553 }
554 GrGpuResource* resource = fPurgeableQueue.peek();
Brian Salomon614c1a82018-12-19 15:42:06 -0500555 SkASSERT(resource->resourcePriv().isPurgeable());
Brian Salomon5e150852017-03-22 14:53:13 -0400556 resource->cacheAccess().release();
557 }
558}
559
Derek Sollenberger5480a182017-05-25 16:43:59 -0400560void GrResourceCache::purgeUnlockedResources(size_t bytesToPurge, bool preferScratchResources) {
561
562 const size_t tmpByteBudget = SkTMax((size_t)0, fBytes - bytesToPurge);
563 bool stillOverbudget = tmpByteBudget < fBytes;
564
565 if (preferScratchResources && bytesToPurge < fPurgeableBytes) {
566 // Sort the queue
567 fPurgeableQueue.sort();
568
569 // Make a list of the scratch resources to delete
570 SkTDArray<GrGpuResource*> scratchResources;
571 size_t scratchByteCount = 0;
572 for (int i = 0; i < fPurgeableQueue.count() && stillOverbudget; i++) {
573 GrGpuResource* resource = fPurgeableQueue.at(i);
Brian Salomon614c1a82018-12-19 15:42:06 -0500574 SkASSERT(resource->resourcePriv().isPurgeable());
Derek Sollenberger5480a182017-05-25 16:43:59 -0400575 if (!resource->getUniqueKey().isValid()) {
576 *scratchResources.append() = resource;
577 scratchByteCount += resource->gpuMemorySize();
578 stillOverbudget = tmpByteBudget < fBytes - scratchByteCount;
579 }
580 }
581
582 // Delete the scratch resources. This must be done as a separate pass
583 // to avoid messing up the sorted order of the queue
584 for (int i = 0; i < scratchResources.count(); i++) {
585 scratchResources.getAt(i)->cacheAccess().release();
586 }
587 stillOverbudget = tmpByteBudget < fBytes;
588
589 this->validate();
590 }
591
592 // Purge any remaining resources in LRU order
593 if (stillOverbudget) {
594 const size_t cachedByteCount = fMaxBytes;
595 fMaxBytes = tmpByteBudget;
596 this->purgeAsNeeded();
597 fMaxBytes = cachedByteCount;
598 }
599}
600
Brian Osman13dddce2017-05-09 13:19:50 -0400601void GrResourceCache::insertCrossContextGpuResource(GrGpuResource* resource) {
602 resource->ref();
Greg Danielc27eb722018-08-10 09:48:08 -0400603 SkASSERT(!fResourcesWaitingForFreeMsg.contains(resource));
604 fResourcesWaitingForFreeMsg.push_back(resource);
Brian Osman13dddce2017-05-09 13:19:50 -0400605}
606
607void GrResourceCache::processFreedGpuResources() {
608 SkTArray<GrGpuResourceFreedMessage> msgs;
609 fFreedGpuResourceInbox.poll(&msgs);
610 for (int i = 0; i < msgs.count(); ++i) {
Brian Salomon238069b2018-07-11 15:58:57 -0400611 SkASSERT(msgs[i].fOwningUniqueID == fContextUniqueID);
Greg Danielc27eb722018-08-10 09:48:08 -0400612 int index = fResourcesWaitingForFreeMsg.find(msgs[i].fResource);
Greg Danielb2acf0a2018-09-12 09:17:11 -0400613 // If we called release or abandon on the GrContext we will have already released our ref on
614 // the GrGpuResource. If then the message arrives before the actual GrContext gets destroyed
615 // we will try to process the message when we destroy the GrContext. This protects us from
616 // trying to unref the resource twice.
617 if (index != -1) {
618 fResourcesWaitingForFreeMsg.removeShuffle(index);
619 msgs[i].fResource->unref();
620 }
Brian Osman13dddce2017-05-09 13:19:50 -0400621 }
622}
623
bsalomonf320e042015-02-17 15:09:34 -0800624void GrResourceCache::addToNonpurgeableArray(GrGpuResource* resource) {
625 int index = fNonpurgeableResources.count();
626 *fNonpurgeableResources.append() = resource;
627 *resource->cacheAccess().accessCacheIndex() = index;
628}
629
630void GrResourceCache::removeFromNonpurgeableArray(GrGpuResource* resource) {
631 int* index = resource->cacheAccess().accessCacheIndex();
632 // Fill the whole we will create in the array with the tail object, adjust its index, and
633 // then pop the array
634 GrGpuResource* tail = *(fNonpurgeableResources.end() - 1);
635 SkASSERT(fNonpurgeableResources[*index] == resource);
636 fNonpurgeableResources[*index] = tail;
637 *tail->cacheAccess().accessCacheIndex() = *index;
638 fNonpurgeableResources.pop();
639 SkDEBUGCODE(*index = -1);
640}
641
bsalomonddf30e62015-02-19 11:38:44 -0800642uint32_t GrResourceCache::getNextTimestamp() {
643 // If we wrap then all the existing resources will appear older than any resources that get
644 // a timestamp after the wrap.
645 if (0 == fTimestamp) {
646 int count = this->getResourceCount();
647 if (count) {
648 // Reset all the timestamps. We sort the resources by timestamp and then assign
649 // sequential timestamps beginning with 0. This is O(n*lg(n)) but it should be extremely
650 // rare.
651 SkTDArray<GrGpuResource*> sortedPurgeableResources;
652 sortedPurgeableResources.setReserve(fPurgeableQueue.count());
653
654 while (fPurgeableQueue.count()) {
655 *sortedPurgeableResources.append() = fPurgeableQueue.peek();
656 fPurgeableQueue.pop();
657 }
robertphillipsee843b22016-10-04 05:30:20 -0700658
bsalomone2e87f32016-09-22 12:42:11 -0700659 SkTQSort(fNonpurgeableResources.begin(), fNonpurgeableResources.end() - 1,
660 CompareTimestamp);
bsalomonddf30e62015-02-19 11:38:44 -0800661
662 // Pick resources out of the purgeable and non-purgeable arrays based on lowest
663 // timestamp and assign new timestamps.
664 int currP = 0;
665 int currNP = 0;
666 while (currP < sortedPurgeableResources.count() &&
mtklein56da0252015-11-16 11:16:23 -0800667 currNP < fNonpurgeableResources.count()) {
bsalomonddf30e62015-02-19 11:38:44 -0800668 uint32_t tsP = sortedPurgeableResources[currP]->cacheAccess().timestamp();
669 uint32_t tsNP = fNonpurgeableResources[currNP]->cacheAccess().timestamp();
670 SkASSERT(tsP != tsNP);
671 if (tsP < tsNP) {
672 sortedPurgeableResources[currP++]->cacheAccess().setTimestamp(fTimestamp++);
673 } else {
674 // Correct the index in the nonpurgeable array stored on the resource post-sort.
675 *fNonpurgeableResources[currNP]->cacheAccess().accessCacheIndex() = currNP;
676 fNonpurgeableResources[currNP++]->cacheAccess().setTimestamp(fTimestamp++);
677 }
678 }
679
680 // The above loop ended when we hit the end of one array. Finish the other one.
681 while (currP < sortedPurgeableResources.count()) {
682 sortedPurgeableResources[currP++]->cacheAccess().setTimestamp(fTimestamp++);
683 }
684 while (currNP < fNonpurgeableResources.count()) {
685 *fNonpurgeableResources[currNP]->cacheAccess().accessCacheIndex() = currNP;
686 fNonpurgeableResources[currNP++]->cacheAccess().setTimestamp(fTimestamp++);
687 }
688
689 // Rebuild the queue.
690 for (int i = 0; i < sortedPurgeableResources.count(); ++i) {
691 fPurgeableQueue.insert(sortedPurgeableResources[i]);
692 }
693
694 this->validate();
695 SkASSERT(count == this->getResourceCount());
696
697 // count should be the next timestamp we return.
698 SkASSERT(fTimestamp == SkToU32(count));
mtklein56da0252015-11-16 11:16:23 -0800699 }
bsalomonddf30e62015-02-19 11:38:44 -0800700 }
701 return fTimestamp++;
702}
703
ericrk0a5fa482015-09-15 14:16:10 -0700704void GrResourceCache::dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const {
705 for (int i = 0; i < fNonpurgeableResources.count(); ++i) {
706 fNonpurgeableResources[i]->dumpMemoryStatistics(traceMemoryDump);
707 }
708 for (int i = 0; i < fPurgeableQueue.count(); ++i) {
709 fPurgeableQueue.at(i)->dumpMemoryStatistics(traceMemoryDump);
710 }
711}
712
bsalomon71cb0c22014-11-14 12:10:14 -0800713#ifdef SK_DEBUG
bsalomon0ea80f42015-02-11 10:49:59 -0800714void GrResourceCache::validate() const {
bsalomonc2f35b72015-01-23 07:19:22 -0800715 // Reduce the frequency of validations for large resource counts.
716 static SkRandom gRandom;
717 int mask = (SkNextPow2(fCount + 1) >> 5) - 1;
718 if (~mask && (gRandom.nextU() & mask)) {
719 return;
720 }
721
bsalomonf320e042015-02-17 15:09:34 -0800722 struct Stats {
723 size_t fBytes;
724 int fBudgetedCount;
725 size_t fBudgetedBytes;
726 int fLocked;
727 int fScratch;
728 int fCouldBeScratch;
729 int fContent;
730 const ScratchMap* fScratchMap;
bsalomon8718aaf2015-02-19 07:24:21 -0800731 const UniqueHash* fUniqueHash;
bsalomon71cb0c22014-11-14 12:10:14 -0800732
bsalomonf320e042015-02-17 15:09:34 -0800733 Stats(const GrResourceCache* cache) {
734 memset(this, 0, sizeof(*this));
735 fScratchMap = &cache->fScratchMap;
bsalomon8718aaf2015-02-19 07:24:21 -0800736 fUniqueHash = &cache->fUniqueHash;
bsalomon71cb0c22014-11-14 12:10:14 -0800737 }
738
bsalomonf320e042015-02-17 15:09:34 -0800739 void update(GrGpuResource* resource) {
740 fBytes += resource->gpuMemorySize();
bsalomondace19e2014-11-17 07:34:06 -0800741
Brian Salomon614c1a82018-12-19 15:42:06 -0500742 if (!resource->resourcePriv().isPurgeable()) {
bsalomonf320e042015-02-17 15:09:34 -0800743 ++fLocked;
744 }
bsalomon9f2d1572015-02-17 11:47:40 -0800745
robertphillipsc4ed6842016-05-24 14:17:12 -0700746 const GrScratchKey& scratchKey = resource->resourcePriv().getScratchKey();
747 const GrUniqueKey& uniqueKey = resource->getUniqueKey();
748
bsalomonf320e042015-02-17 15:09:34 -0800749 if (resource->cacheAccess().isScratch()) {
robertphillipsc4ed6842016-05-24 14:17:12 -0700750 SkASSERT(!uniqueKey.isValid());
bsalomonf320e042015-02-17 15:09:34 -0800751 ++fScratch;
robertphillipsc4ed6842016-05-24 14:17:12 -0700752 SkASSERT(fScratchMap->countForKey(scratchKey));
kkinnunen2e6055b2016-04-22 01:48:29 -0700753 SkASSERT(!resource->resourcePriv().refsWrappedObjects());
robertphillipsc4ed6842016-05-24 14:17:12 -0700754 } else if (scratchKey.isValid()) {
bsalomon5ec26ae2016-02-25 08:33:02 -0800755 SkASSERT(SkBudgeted::kNo == resource->resourcePriv().isBudgeted() ||
robertphillipsc4ed6842016-05-24 14:17:12 -0700756 uniqueKey.isValid());
757 if (!uniqueKey.isValid()) {
mtklein4e976072016-08-08 09:06:27 -0700758 ++fCouldBeScratch;
robertphillipsc4ed6842016-05-24 14:17:12 -0700759 SkASSERT(fScratchMap->countForKey(scratchKey));
760 }
kkinnunen2e6055b2016-04-22 01:48:29 -0700761 SkASSERT(!resource->resourcePriv().refsWrappedObjects());
bsalomonf320e042015-02-17 15:09:34 -0800762 }
bsalomon8718aaf2015-02-19 07:24:21 -0800763 if (uniqueKey.isValid()) {
bsalomonf320e042015-02-17 15:09:34 -0800764 ++fContent;
bsalomon8718aaf2015-02-19 07:24:21 -0800765 SkASSERT(fUniqueHash->find(uniqueKey) == resource);
Brian Osman0562eb92017-05-08 11:16:39 -0400766 SkASSERT(SkBudgeted::kYes == resource->resourcePriv().isBudgeted() ||
767 resource->resourcePriv().refsWrappedObjects());
robertphillipsc4ed6842016-05-24 14:17:12 -0700768
769 if (scratchKey.isValid()) {
770 SkASSERT(!fScratchMap->has(resource, scratchKey));
771 }
bsalomonf320e042015-02-17 15:09:34 -0800772 }
773
bsalomon5ec26ae2016-02-25 08:33:02 -0800774 if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
bsalomonf320e042015-02-17 15:09:34 -0800775 ++fBudgetedCount;
776 fBudgetedBytes += resource->gpuMemorySize();
777 }
bsalomon9f2d1572015-02-17 11:47:40 -0800778 }
bsalomonf320e042015-02-17 15:09:34 -0800779 };
780
robertphillipsc4ed6842016-05-24 14:17:12 -0700781 {
782 ScratchMap::ConstIter iter(&fScratchMap);
783
784 int count = 0;
785 for ( ; !iter.done(); ++iter) {
786 const GrGpuResource* resource = *iter;
787 SkASSERT(resource->resourcePriv().getScratchKey().isValid());
788 SkASSERT(!resource->getUniqueKey().isValid());
789 count++;
790 }
791 SkASSERT(count == fScratchMap.count()); // ensure the iterator is working correctly
792 }
793
bsalomonf320e042015-02-17 15:09:34 -0800794 Stats stats(this);
Derek Sollenbergeree479142017-05-24 11:41:33 -0400795 size_t purgeableBytes = 0;
bsalomonf320e042015-02-17 15:09:34 -0800796
797 for (int i = 0; i < fNonpurgeableResources.count(); ++i) {
Brian Salomon614c1a82018-12-19 15:42:06 -0500798 SkASSERT(!fNonpurgeableResources[i]->resourcePriv().isPurgeable() ||
bsalomon3f324322015-04-08 11:01:54 -0700799 fNewlyPurgeableResourceForValidation == fNonpurgeableResources[i]);
bsalomonf320e042015-02-17 15:09:34 -0800800 SkASSERT(*fNonpurgeableResources[i]->cacheAccess().accessCacheIndex() == i);
801 SkASSERT(!fNonpurgeableResources[i]->wasDestroyed());
802 stats.update(fNonpurgeableResources[i]);
bsalomon71cb0c22014-11-14 12:10:14 -0800803 }
bsalomon9f2d1572015-02-17 11:47:40 -0800804 for (int i = 0; i < fPurgeableQueue.count(); ++i) {
Brian Salomon614c1a82018-12-19 15:42:06 -0500805 SkASSERT(fPurgeableQueue.at(i)->resourcePriv().isPurgeable());
bsalomonf320e042015-02-17 15:09:34 -0800806 SkASSERT(*fPurgeableQueue.at(i)->cacheAccess().accessCacheIndex() == i);
807 SkASSERT(!fPurgeableQueue.at(i)->wasDestroyed());
808 stats.update(fPurgeableQueue.at(i));
Derek Sollenbergeree479142017-05-24 11:41:33 -0400809 purgeableBytes += fPurgeableQueue.at(i)->gpuMemorySize();
bsalomon9f2d1572015-02-17 11:47:40 -0800810 }
811
bsalomonf320e042015-02-17 15:09:34 -0800812 SkASSERT(fCount == this->getResourceCount());
bsalomondace19e2014-11-17 07:34:06 -0800813 SkASSERT(fBudgetedCount <= fCount);
bsalomonf320e042015-02-17 15:09:34 -0800814 SkASSERT(fBudgetedBytes <= fBytes);
815 SkASSERT(stats.fBytes == fBytes);
816 SkASSERT(stats.fBudgetedBytes == fBudgetedBytes);
817 SkASSERT(stats.fBudgetedCount == fBudgetedCount);
Derek Sollenbergeree479142017-05-24 11:41:33 -0400818 SkASSERT(purgeableBytes == fPurgeableBytes);
bsalomon71cb0c22014-11-14 12:10:14 -0800819#if GR_CACHE_STATS
bsalomondace19e2014-11-17 07:34:06 -0800820 SkASSERT(fBudgetedHighWaterCount <= fHighWaterCount);
821 SkASSERT(fBudgetedHighWaterBytes <= fHighWaterBytes);
bsalomonf320e042015-02-17 15:09:34 -0800822 SkASSERT(fBytes <= fHighWaterBytes);
823 SkASSERT(fCount <= fHighWaterCount);
824 SkASSERT(fBudgetedBytes <= fBudgetedHighWaterBytes);
825 SkASSERT(fBudgetedCount <= fBudgetedHighWaterCount);
bsalomon71cb0c22014-11-14 12:10:14 -0800826#endif
bsalomon8718aaf2015-02-19 07:24:21 -0800827 SkASSERT(stats.fContent == fUniqueHash.count());
bsalomonf320e042015-02-17 15:09:34 -0800828 SkASSERT(stats.fScratch + stats.fCouldBeScratch == fScratchMap.count());
bsalomon71cb0c22014-11-14 12:10:14 -0800829
bsalomon3f324322015-04-08 11:01:54 -0700830 // This assertion is not currently valid because we can be in recursive notifyCntReachedZero()
bsalomon12299ab2014-11-14 13:33:09 -0800831 // calls. This will be fixed when subresource registration is explicit.
bsalomondace19e2014-11-17 07:34:06 -0800832 // bool overBudget = budgetedBytes > fMaxBytes || budgetedCount > fMaxCount;
bsalomon12299ab2014-11-14 13:33:09 -0800833 // SkASSERT(!overBudget || locked == count || fPurging);
bsalomon71cb0c22014-11-14 12:10:14 -0800834}
bsalomonf320e042015-02-17 15:09:34 -0800835
836bool GrResourceCache::isInCache(const GrGpuResource* resource) const {
837 int index = *resource->cacheAccess().accessCacheIndex();
838 if (index < 0) {
839 return false;
840 }
841 if (index < fPurgeableQueue.count() && fPurgeableQueue.at(index) == resource) {
842 return true;
843 }
844 if (index < fNonpurgeableResources.count() && fNonpurgeableResources[index] == resource) {
845 return true;
846 }
847 SkDEBUGFAIL("Resource index should be -1 or the resource should be in the cache.");
848 return false;
849}
850
bsalomon71cb0c22014-11-14 12:10:14 -0800851#endif