blob: e77d9acd62ff37f0fd0eb1b05b1250da3d35a920 [file] [log] [blame]
bsalomonc8dc1f72014-08-21 13:02:13 -07001
2/*
3 * Copyright 2014 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9
bsalomon0ea80f42015-02-11 10:49:59 -080010#include "GrResourceCache.h"
bsalomon3582d3e2015-02-13 14:20:05 -080011#include "GrGpuResourceCacheAccess.h"
hendrikw876c3132015-03-04 10:33:49 -080012#include "GrTracing.h"
bsalomon7775c852014-12-30 12:50:52 -080013#include "SkChecksum.h"
bsalomon71cb0c22014-11-14 12:10:14 -080014#include "SkGr.h"
15#include "SkMessageBus.h"
bsalomonddf30e62015-02-19 11:38:44 -080016#include "SkTSort.h"
bsalomon71cb0c22014-11-14 12:10:14 -080017
bsalomon8718aaf2015-02-19 07:24:21 -080018DECLARE_SKMESSAGEBUS_MESSAGE(GrUniqueKeyInvalidatedMessage);
bsalomon71cb0c22014-11-14 12:10:14 -080019
20//////////////////////////////////////////////////////////////////////////////
21
bsalomon7775c852014-12-30 12:50:52 -080022GrScratchKey::ResourceType GrScratchKey::GenerateResourceType() {
bsalomon24db3b12015-01-23 04:24:04 -080023 static int32_t gType = INHERITED::kInvalidDomain + 1;
bsalomonfe369ee2014-11-10 11:59:06 -080024
bsalomon7775c852014-12-30 12:50:52 -080025 int32_t type = sk_atomic_inc(&gType);
robertphillips9790a7b2015-01-05 12:29:15 -080026 if (type > SK_MaxU16) {
bsalomon71cb0c22014-11-14 12:10:14 -080027 SkFAIL("Too many Resource Types");
28 }
29
30 return static_cast<ResourceType>(type);
31}
32
bsalomon8718aaf2015-02-19 07:24:21 -080033GrUniqueKey::Domain GrUniqueKey::GenerateDomain() {
bsalomon24db3b12015-01-23 04:24:04 -080034 static int32_t gDomain = INHERITED::kInvalidDomain + 1;
bsalomon7775c852014-12-30 12:50:52 -080035
bsalomon24db3b12015-01-23 04:24:04 -080036 int32_t domain = sk_atomic_inc(&gDomain);
kkinnunen016dffb2015-01-23 06:43:05 -080037 if (domain > SK_MaxU16) {
bsalomon8718aaf2015-02-19 07:24:21 -080038 SkFAIL("Too many GrUniqueKey Domains");
bsalomon7775c852014-12-30 12:50:52 -080039 }
bsalomon24db3b12015-01-23 04:24:04 -080040
41 return static_cast<Domain>(domain);
42}
bsalomon3f324322015-04-08 11:01:54 -070043
bsalomon24db3b12015-01-23 04:24:04 -080044uint32_t GrResourceKeyHash(const uint32_t* data, size_t size) {
45 return SkChecksum::Compute(data, size);
bsalomon7775c852014-12-30 12:50:52 -080046}
47
bsalomonfe369ee2014-11-10 11:59:06 -080048//////////////////////////////////////////////////////////////////////////////
49
bsalomon0ea80f42015-02-11 10:49:59 -080050class GrResourceCache::AutoValidate : ::SkNoncopyable {
bsalomon71cb0c22014-11-14 12:10:14 -080051public:
bsalomon0ea80f42015-02-11 10:49:59 -080052 AutoValidate(GrResourceCache* cache) : fCache(cache) { cache->validate(); }
bsalomon71cb0c22014-11-14 12:10:14 -080053 ~AutoValidate() { fCache->validate(); }
54private:
bsalomon0ea80f42015-02-11 10:49:59 -080055 GrResourceCache* fCache;
bsalomon71cb0c22014-11-14 12:10:14 -080056};
57
58 //////////////////////////////////////////////////////////////////////////////
59
bsalomon71cb0c22014-11-14 12:10:14 -080060
robertphillips63926682015-08-20 09:39:02 -070061GrResourceCache::GrResourceCache(const GrCaps* caps)
bsalomon9f2d1572015-02-17 11:47:40 -080062 : fTimestamp(0)
63 , fMaxCount(kDefaultMaxCount)
bsalomon71cb0c22014-11-14 12:10:14 -080064 , fMaxBytes(kDefaultMaxSize)
bsalomon3f324322015-04-08 11:01:54 -070065 , fMaxUnusedFlushes(kDefaultMaxUnusedFlushes)
bsalomon71cb0c22014-11-14 12:10:14 -080066#if GR_CACHE_STATS
67 , fHighWaterCount(0)
68 , fHighWaterBytes(0)
bsalomondace19e2014-11-17 07:34:06 -080069 , fBudgetedHighWaterCount(0)
70 , fBudgetedHighWaterBytes(0)
bsalomon71cb0c22014-11-14 12:10:14 -080071#endif
bsalomon71cb0c22014-11-14 12:10:14 -080072 , fBytes(0)
bsalomondace19e2014-11-17 07:34:06 -080073 , fBudgetedCount(0)
74 , fBudgetedBytes(0)
bsalomon71cb0c22014-11-14 12:10:14 -080075 , fOverBudgetCB(NULL)
bsalomon3f324322015-04-08 11:01:54 -070076 , fOverBudgetData(NULL)
77 , fFlushTimestamps(NULL)
robertphillips63926682015-08-20 09:39:02 -070078 , fLastFlushTimestampIndex(0)
79 , fPreferVRAMUseOverFlushes(caps->preferVRAMUseOverFlushes()) {
bsalomonf320e042015-02-17 15:09:34 -080080 SkDEBUGCODE(fCount = 0;)
bsalomon3f324322015-04-08 11:01:54 -070081 SkDEBUGCODE(fNewlyPurgeableResourceForValidation = NULL;)
82 this->resetFlushTimestamps();
bsalomon71cb0c22014-11-14 12:10:14 -080083}
84
bsalomon0ea80f42015-02-11 10:49:59 -080085GrResourceCache::~GrResourceCache() {
bsalomonc8dc1f72014-08-21 13:02:13 -070086 this->releaseAll();
mtkleind9f88622015-04-09 03:20:04 -070087 SkDELETE_ARRAY(fFlushTimestamps);
bsalomonc8dc1f72014-08-21 13:02:13 -070088}
89
bsalomon3f324322015-04-08 11:01:54 -070090void GrResourceCache::setLimits(int count, size_t bytes, int maxUnusedFlushes) {
bsalomon71cb0c22014-11-14 12:10:14 -080091 fMaxCount = count;
92 fMaxBytes = bytes;
bsalomon3f324322015-04-08 11:01:54 -070093 fMaxUnusedFlushes = maxUnusedFlushes;
94 this->resetFlushTimestamps();
bsalomon71cb0c22014-11-14 12:10:14 -080095 this->purgeAsNeeded();
96}
97
bsalomon3f324322015-04-08 11:01:54 -070098void GrResourceCache::resetFlushTimestamps() {
mtkleind9f88622015-04-09 03:20:04 -070099 SkDELETE_ARRAY(fFlushTimestamps);
bsalomon3f324322015-04-08 11:01:54 -0700100
101 // We assume this number is a power of two when wrapping indices into the timestamp array.
102 fMaxUnusedFlushes = SkNextPow2(fMaxUnusedFlushes);
103
104 // Since our implementation is to store the timestamps of the last fMaxUnusedFlushes flush calls
105 // we just turn the feature off if that array would be large.
106 static const int kMaxSupportedTimestampHistory = 128;
107
108 if (fMaxUnusedFlushes > kMaxSupportedTimestampHistory) {
109 fFlushTimestamps = NULL;
110 return;
111 }
112
113 fFlushTimestamps = SkNEW_ARRAY(uint32_t, fMaxUnusedFlushes);
114 fLastFlushTimestampIndex = 0;
115 // Set all the historical flush timestamps to initially be at the beginning of time (timestamp
116 // 0).
117 sk_bzero(fFlushTimestamps, fMaxUnusedFlushes * sizeof(uint32_t));
118}
119
bsalomon0ea80f42015-02-11 10:49:59 -0800120void GrResourceCache::insertResource(GrGpuResource* resource) {
bsalomon49f085d2014-09-05 13:34:00 -0700121 SkASSERT(resource);
bsalomon16961262014-08-26 14:01:07 -0700122 SkASSERT(!this->isInCache(resource));
bsalomonf320e042015-02-17 15:09:34 -0800123 SkASSERT(!resource->wasDestroyed());
124 SkASSERT(!resource->isPurgeable());
bsalomonddf30e62015-02-19 11:38:44 -0800125
126 // We must set the timestamp before adding to the array in case the timestamp wraps and we wind
127 // up iterating over all the resources that already have timestamps.
128 resource->cacheAccess().setTimestamp(this->getNextTimestamp());
129
bsalomonf320e042015-02-17 15:09:34 -0800130 this->addToNonpurgeableArray(resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800131
bsalomondace19e2014-11-17 07:34:06 -0800132 size_t size = resource->gpuMemorySize();
bsalomonf320e042015-02-17 15:09:34 -0800133 SkDEBUGCODE(++fCount;)
bsalomon84c8e622014-11-17 09:33:27 -0800134 fBytes += size;
bsalomon82b1d622014-11-14 13:59:57 -0800135#if GR_CACHE_STATS
bsalomonf320e042015-02-17 15:09:34 -0800136 fHighWaterCount = SkTMax(this->getResourceCount(), fHighWaterCount);
bsalomon82b1d622014-11-14 13:59:57 -0800137 fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes);
138#endif
bsalomon3582d3e2015-02-13 14:20:05 -0800139 if (resource->resourcePriv().isBudgeted()) {
bsalomondace19e2014-11-17 07:34:06 -0800140 ++fBudgetedCount;
141 fBudgetedBytes += size;
hendrikw876c3132015-03-04 10:33:49 -0800142 TRACE_COUNTER2(TRACE_DISABLED_BY_DEFAULT("skia.gpu.cache"), "skia budget", "used",
143 fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
bsalomondace19e2014-11-17 07:34:06 -0800144#if GR_CACHE_STATS
145 fBudgetedHighWaterCount = SkTMax(fBudgetedCount, fBudgetedHighWaterCount);
146 fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
147#endif
148 }
bsalomon3582d3e2015-02-13 14:20:05 -0800149 if (resource->resourcePriv().getScratchKey().isValid()) {
bsalomon6dc6f5f2015-06-18 09:12:16 -0700150 SkASSERT(!resource->cacheAccess().isExternal());
bsalomon3582d3e2015-02-13 14:20:05 -0800151 fScratchMap.insert(resource->resourcePriv().getScratchKey(), resource);
bsalomon744998e2014-08-28 09:54:34 -0700152 }
bsalomon9f2d1572015-02-17 11:47:40 -0800153
bsalomon71cb0c22014-11-14 12:10:14 -0800154 this->purgeAsNeeded();
bsalomonc8dc1f72014-08-21 13:02:13 -0700155}
156
bsalomon0ea80f42015-02-11 10:49:59 -0800157void GrResourceCache::removeResource(GrGpuResource* resource) {
bsalomon9f2d1572015-02-17 11:47:40 -0800158 this->validate();
bsalomon16961262014-08-26 14:01:07 -0700159 SkASSERT(this->isInCache(resource));
bsalomondace19e2014-11-17 07:34:06 -0800160
bsalomon9f2d1572015-02-17 11:47:40 -0800161 if (resource->isPurgeable()) {
162 fPurgeableQueue.remove(resource);
bsalomonf320e042015-02-17 15:09:34 -0800163 } else {
164 this->removeFromNonpurgeableArray(resource);
bsalomon9f2d1572015-02-17 11:47:40 -0800165 }
166
bsalomondace19e2014-11-17 07:34:06 -0800167 size_t size = resource->gpuMemorySize();
bsalomonf320e042015-02-17 15:09:34 -0800168 SkDEBUGCODE(--fCount;)
bsalomondace19e2014-11-17 07:34:06 -0800169 fBytes -= size;
bsalomon3582d3e2015-02-13 14:20:05 -0800170 if (resource->resourcePriv().isBudgeted()) {
bsalomondace19e2014-11-17 07:34:06 -0800171 --fBudgetedCount;
172 fBudgetedBytes -= size;
hendrikw876c3132015-03-04 10:33:49 -0800173 TRACE_COUNTER2(TRACE_DISABLED_BY_DEFAULT("skia.gpu.cache"), "skia budget", "used",
174 fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
bsalomondace19e2014-11-17 07:34:06 -0800175 }
176
bsalomon3582d3e2015-02-13 14:20:05 -0800177 if (resource->resourcePriv().getScratchKey().isValid()) {
178 fScratchMap.remove(resource->resourcePriv().getScratchKey(), resource);
bsalomon744998e2014-08-28 09:54:34 -0700179 }
bsalomon8718aaf2015-02-19 07:24:21 -0800180 if (resource->getUniqueKey().isValid()) {
181 fUniqueHash.remove(resource->getUniqueKey());
bsalomon8b79d232014-11-10 10:19:06 -0800182 }
bsalomonb436ed62014-11-17 12:15:56 -0800183 this->validate();
bsalomonc8dc1f72014-08-21 13:02:13 -0700184}
185
bsalomon0ea80f42015-02-11 10:49:59 -0800186void GrResourceCache::abandonAll() {
bsalomon71cb0c22014-11-14 12:10:14 -0800187 AutoValidate av(this);
188
bsalomonf320e042015-02-17 15:09:34 -0800189 while (fNonpurgeableResources.count()) {
190 GrGpuResource* back = *(fNonpurgeableResources.end() - 1);
191 SkASSERT(!back->wasDestroyed());
192 back->cacheAccess().abandon();
bsalomonc8dc1f72014-08-21 13:02:13 -0700193 }
bsalomonf320e042015-02-17 15:09:34 -0800194
195 while (fPurgeableQueue.count()) {
196 GrGpuResource* top = fPurgeableQueue.peek();
197 SkASSERT(!top->wasDestroyed());
198 top->cacheAccess().abandon();
199 }
200
bsalomon744998e2014-08-28 09:54:34 -0700201 SkASSERT(!fScratchMap.count());
bsalomon8718aaf2015-02-19 07:24:21 -0800202 SkASSERT(!fUniqueHash.count());
bsalomonc8dc1f72014-08-21 13:02:13 -0700203 SkASSERT(!fCount);
bsalomonf320e042015-02-17 15:09:34 -0800204 SkASSERT(!this->getResourceCount());
bsalomondace19e2014-11-17 07:34:06 -0800205 SkASSERT(!fBytes);
206 SkASSERT(!fBudgetedCount);
207 SkASSERT(!fBudgetedBytes);
bsalomonc8dc1f72014-08-21 13:02:13 -0700208}
209
bsalomon0ea80f42015-02-11 10:49:59 -0800210void GrResourceCache::releaseAll() {
bsalomon71cb0c22014-11-14 12:10:14 -0800211 AutoValidate av(this);
212
bsalomonf320e042015-02-17 15:09:34 -0800213 while(fNonpurgeableResources.count()) {
214 GrGpuResource* back = *(fNonpurgeableResources.end() - 1);
215 SkASSERT(!back->wasDestroyed());
216 back->cacheAccess().release();
bsalomonc8dc1f72014-08-21 13:02:13 -0700217 }
bsalomonf320e042015-02-17 15:09:34 -0800218
219 while (fPurgeableQueue.count()) {
220 GrGpuResource* top = fPurgeableQueue.peek();
221 SkASSERT(!top->wasDestroyed());
222 top->cacheAccess().release();
223 }
224
bsalomon744998e2014-08-28 09:54:34 -0700225 SkASSERT(!fScratchMap.count());
bsalomon8718aaf2015-02-19 07:24:21 -0800226 SkASSERT(!fUniqueHash.count());
bsalomonc8dc1f72014-08-21 13:02:13 -0700227 SkASSERT(!fCount);
bsalomonf320e042015-02-17 15:09:34 -0800228 SkASSERT(!this->getResourceCount());
bsalomondace19e2014-11-17 07:34:06 -0800229 SkASSERT(!fBytes);
230 SkASSERT(!fBudgetedCount);
231 SkASSERT(!fBudgetedBytes);
bsalomonc8dc1f72014-08-21 13:02:13 -0700232}
bsalomonbcf0a522014-10-08 08:40:09 -0700233
bsalomon0ea80f42015-02-11 10:49:59 -0800234class GrResourceCache::AvailableForScratchUse {
bsalomonbcf0a522014-10-08 08:40:09 -0700235public:
bsalomon000f8292014-10-15 19:04:14 -0700236 AvailableForScratchUse(bool rejectPendingIO) : fRejectPendingIO(rejectPendingIO) { }
bsalomonbcf0a522014-10-08 08:40:09 -0700237
238 bool operator()(const GrGpuResource* resource) const {
bsalomon12299ab2014-11-14 13:33:09 -0800239 if (resource->internalHasRef() || !resource->cacheAccess().isScratch()) {
bsalomon000f8292014-10-15 19:04:14 -0700240 return false;
bsalomonbcf0a522014-10-08 08:40:09 -0700241 }
bsalomon000f8292014-10-15 19:04:14 -0700242 return !fRejectPendingIO || !resource->internalHasPendingIO();
bsalomonbcf0a522014-10-08 08:40:09 -0700243 }
bsalomon1e2530b2014-10-09 09:57:18 -0700244
bsalomonbcf0a522014-10-08 08:40:09 -0700245private:
bsalomon000f8292014-10-15 19:04:14 -0700246 bool fRejectPendingIO;
bsalomonbcf0a522014-10-08 08:40:09 -0700247};
248
bsalomon0ea80f42015-02-11 10:49:59 -0800249GrGpuResource* GrResourceCache::findAndRefScratchResource(const GrScratchKey& scratchKey,
robertphillips6e83ac72015-08-13 05:19:14 -0700250 size_t resourceSize,
bsalomon9f2d1572015-02-17 11:47:40 -0800251 uint32_t flags) {
bsalomon7775c852014-12-30 12:50:52 -0800252 SkASSERT(scratchKey.isValid());
bsalomon000f8292014-10-15 19:04:14 -0700253
bsalomon71cb0c22014-11-14 12:10:14 -0800254 GrGpuResource* resource;
bsalomon000f8292014-10-15 19:04:14 -0700255 if (flags & (kPreferNoPendingIO_ScratchFlag | kRequireNoPendingIO_ScratchFlag)) {
bsalomon71cb0c22014-11-14 12:10:14 -0800256 resource = fScratchMap.find(scratchKey, AvailableForScratchUse(true));
bsalomon000f8292014-10-15 19:04:14 -0700257 if (resource) {
bsalomon9f2d1572015-02-17 11:47:40 -0800258 this->refAndMakeResourceMRU(resource);
bsalomonb436ed62014-11-17 12:15:56 -0800259 this->validate();
260 return resource;
bsalomon000f8292014-10-15 19:04:14 -0700261 } else if (flags & kRequireNoPendingIO_ScratchFlag) {
262 return NULL;
263 }
robertphillips63926682015-08-20 09:39:02 -0700264 // We would prefer to consume more available VRAM rather than flushing
265 // immediately, but on ANGLE this can lead to starving of the GPU.
266 if (fPreferVRAMUseOverFlushes && this->wouldFit(resourceSize)) {
robertphillips6e83ac72015-08-13 05:19:14 -0700267 // kPrefer is specified, we didn't find a resource without pending io,
robertphillips63926682015-08-20 09:39:02 -0700268 // but there is still space in our budget for the resource so force
269 // the caller to allocate a new resource.
robertphillips6e83ac72015-08-13 05:19:14 -0700270 return NULL;
271 }
bsalomon000f8292014-10-15 19:04:14 -0700272 }
bsalomon71cb0c22014-11-14 12:10:14 -0800273 resource = fScratchMap.find(scratchKey, AvailableForScratchUse(false));
274 if (resource) {
bsalomon9f2d1572015-02-17 11:47:40 -0800275 this->refAndMakeResourceMRU(resource);
bsalomonb436ed62014-11-17 12:15:56 -0800276 this->validate();
bsalomon71cb0c22014-11-14 12:10:14 -0800277 }
278 return resource;
bsalomonbcf0a522014-10-08 08:40:09 -0700279}
bsalomon8b79d232014-11-10 10:19:06 -0800280
bsalomon0ea80f42015-02-11 10:49:59 -0800281void GrResourceCache::willRemoveScratchKey(const GrGpuResource* resource) {
bsalomon3582d3e2015-02-13 14:20:05 -0800282 SkASSERT(resource->resourcePriv().getScratchKey().isValid());
283 fScratchMap.remove(resource->resourcePriv().getScratchKey(), resource);
bsalomon10e23ca2014-11-25 05:52:06 -0800284}
285
bsalomonf99e9612015-02-19 08:24:16 -0800286void GrResourceCache::removeUniqueKey(GrGpuResource* resource) {
bsalomon3f324322015-04-08 11:01:54 -0700287 // Someone has a ref to this resource in order to have removed the key. When the ref count
288 // reaches zero we will get a ref cnt notification and figure out what to do with it.
bsalomonf99e9612015-02-19 08:24:16 -0800289 if (resource->getUniqueKey().isValid()) {
290 SkASSERT(resource == fUniqueHash.find(resource->getUniqueKey()));
291 fUniqueHash.remove(resource->getUniqueKey());
292 }
293 resource->cacheAccess().removeUniqueKey();
294 this->validate();
bsalomon23e619c2015-02-06 11:54:28 -0800295}
296
bsalomonf99e9612015-02-19 08:24:16 -0800297void GrResourceCache::changeUniqueKey(GrGpuResource* resource, const GrUniqueKey& newKey) {
bsalomon8b79d232014-11-10 10:19:06 -0800298 SkASSERT(resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800299 SkASSERT(this->isInCache(resource));
bsalomon8b79d232014-11-10 10:19:06 -0800300
bsalomonf99e9612015-02-19 08:24:16 -0800301 // Remove the entry for this resource if it already has a unique key.
302 if (resource->getUniqueKey().isValid()) {
303 SkASSERT(resource == fUniqueHash.find(resource->getUniqueKey()));
304 fUniqueHash.remove(resource->getUniqueKey());
305 SkASSERT(NULL == fUniqueHash.find(resource->getUniqueKey()));
bsalomon8b79d232014-11-10 10:19:06 -0800306 }
307
bsalomonf99e9612015-02-19 08:24:16 -0800308 // If another resource has the new key, remove its key then install the key on this resource.
309 if (newKey.isValid()) {
310 if (GrGpuResource* old = fUniqueHash.find(newKey)) {
311 // If the old resource using the key is purgeable and is unreachable, then remove it.
312 if (!old->resourcePriv().getScratchKey().isValid() && old->isPurgeable()) {
313 // release may call validate() which will assert that resource is in fUniqueHash
314 // if it has a valid key. So in debug reset the key here before we assign it.
315 SkDEBUGCODE(resource->cacheAccess().removeUniqueKey();)
316 old->cacheAccess().release();
317 } else {
318 fUniqueHash.remove(newKey);
319 old->cacheAccess().removeUniqueKey();
320 }
321 }
322 SkASSERT(NULL == fUniqueHash.find(newKey));
323 resource->cacheAccess().setUniqueKey(newKey);
324 fUniqueHash.add(resource);
325 } else {
326 resource->cacheAccess().removeUniqueKey();
327 }
328
bsalomon71cb0c22014-11-14 12:10:14 -0800329 this->validate();
bsalomon8b79d232014-11-10 10:19:06 -0800330}
bsalomon71cb0c22014-11-14 12:10:14 -0800331
bsalomon9f2d1572015-02-17 11:47:40 -0800332void GrResourceCache::refAndMakeResourceMRU(GrGpuResource* resource) {
bsalomon71cb0c22014-11-14 12:10:14 -0800333 SkASSERT(resource);
334 SkASSERT(this->isInCache(resource));
bsalomonddf30e62015-02-19 11:38:44 -0800335
bsalomon9f2d1572015-02-17 11:47:40 -0800336 if (resource->isPurgeable()) {
337 // It's about to become unpurgeable.
338 fPurgeableQueue.remove(resource);
bsalomonf320e042015-02-17 15:09:34 -0800339 this->addToNonpurgeableArray(resource);
bsalomon9f2d1572015-02-17 11:47:40 -0800340 }
341 resource->ref();
bsalomonddf30e62015-02-19 11:38:44 -0800342
343 resource->cacheAccess().setTimestamp(this->getNextTimestamp());
bsalomonf320e042015-02-17 15:09:34 -0800344 this->validate();
bsalomon71cb0c22014-11-14 12:10:14 -0800345}
346
bsalomon3f324322015-04-08 11:01:54 -0700347void GrResourceCache::notifyCntReachedZero(GrGpuResource* resource, uint32_t flags) {
bsalomon71cb0c22014-11-14 12:10:14 -0800348 SkASSERT(resource);
bsalomon3f324322015-04-08 11:01:54 -0700349 SkASSERT(!resource->wasDestroyed());
350 SkASSERT(flags);
bsalomon71cb0c22014-11-14 12:10:14 -0800351 SkASSERT(this->isInCache(resource));
bsalomon3f324322015-04-08 11:01:54 -0700352 // This resource should always be in the nonpurgeable array when this function is called. It
353 // will be moved to the queue if it is newly purgeable.
354 SkASSERT(fNonpurgeableResources[*resource->cacheAccess().accessCacheIndex()] == resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800355
bsalomon3f324322015-04-08 11:01:54 -0700356 if (SkToBool(ResourceAccess::kRefCntReachedZero_RefNotificationFlag & flags)) {
357#ifdef SK_DEBUG
358 // When the timestamp overflows validate() is called. validate() checks that resources in
359 // the nonpurgeable array are indeed not purgeable. However, the movement from the array to
360 // the purgeable queue happens just below in this function. So we mark it as an exception.
361 if (resource->isPurgeable()) {
362 fNewlyPurgeableResourceForValidation = resource;
363 }
364#endif
365 resource->cacheAccess().setTimestamp(this->getNextTimestamp());
366 SkDEBUGCODE(fNewlyPurgeableResourceForValidation = NULL);
367 }
368
369 if (!SkToBool(ResourceAccess::kAllCntsReachedZero_RefNotificationFlag & flags)) {
370 SkASSERT(!resource->isPurgeable());
371 return;
372 }
373
374 SkASSERT(resource->isPurgeable());
bsalomonf320e042015-02-17 15:09:34 -0800375 this->removeFromNonpurgeableArray(resource);
bsalomon9f2d1572015-02-17 11:47:40 -0800376 fPurgeableQueue.insert(resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800377
bsalomon9f2d1572015-02-17 11:47:40 -0800378 if (!resource->resourcePriv().isBudgeted()) {
bsalomonc2f35b72015-01-23 07:19:22 -0800379 // Check whether this resource could still be used as a scratch resource.
bsalomon6dc6f5f2015-06-18 09:12:16 -0700380 if (!resource->cacheAccess().isExternal() &&
bsalomon9f2d1572015-02-17 11:47:40 -0800381 resource->resourcePriv().getScratchKey().isValid()) {
bsalomonc2f35b72015-01-23 07:19:22 -0800382 // We won't purge an existing resource to make room for this one.
bsalomonf320e042015-02-17 15:09:34 -0800383 if (fBudgetedCount < fMaxCount &&
384 fBudgetedBytes + resource->gpuMemorySize() <= fMaxBytes) {
bsalomon3582d3e2015-02-13 14:20:05 -0800385 resource->resourcePriv().makeBudgeted();
bsalomon9f2d1572015-02-17 11:47:40 -0800386 return;
bsalomonc2f35b72015-01-23 07:19:22 -0800387 }
bsalomonc2f35b72015-01-23 07:19:22 -0800388 }
389 } else {
bsalomon9f2d1572015-02-17 11:47:40 -0800390 // Purge the resource immediately if we're over budget
bsalomon8718aaf2015-02-19 07:24:21 -0800391 // Also purge if the resource has neither a valid scratch key nor a unique key.
bsalomon3582d3e2015-02-13 14:20:05 -0800392 bool noKey = !resource->resourcePriv().getScratchKey().isValid() &&
bsalomon8718aaf2015-02-19 07:24:21 -0800393 !resource->getUniqueKey().isValid();
bsalomonf320e042015-02-17 15:09:34 -0800394 if (!this->overBudget() && !noKey) {
bsalomon9f2d1572015-02-17 11:47:40 -0800395 return;
bsalomonc2f35b72015-01-23 07:19:22 -0800396 }
397 }
bsalomondace19e2014-11-17 07:34:06 -0800398
bsalomonf320e042015-02-17 15:09:34 -0800399 SkDEBUGCODE(int beforeCount = this->getResourceCount();)
bsalomon9f2d1572015-02-17 11:47:40 -0800400 resource->cacheAccess().release();
401 // We should at least free this resource, perhaps dependent resources as well.
bsalomonf320e042015-02-17 15:09:34 -0800402 SkASSERT(this->getResourceCount() < beforeCount);
bsalomon71cb0c22014-11-14 12:10:14 -0800403 this->validate();
404}
405
bsalomon0ea80f42015-02-11 10:49:59 -0800406void GrResourceCache::didChangeGpuMemorySize(const GrGpuResource* resource, size_t oldSize) {
bsalomon71cb0c22014-11-14 12:10:14 -0800407 // SkASSERT(!fPurging); GrPathRange increases size during flush. :(
408 SkASSERT(resource);
409 SkASSERT(this->isInCache(resource));
410
bsalomondace19e2014-11-17 07:34:06 -0800411 ptrdiff_t delta = resource->gpuMemorySize() - oldSize;
412
413 fBytes += delta;
bsalomon82b1d622014-11-14 13:59:57 -0800414#if GR_CACHE_STATS
415 fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes);
416#endif
bsalomon3582d3e2015-02-13 14:20:05 -0800417 if (resource->resourcePriv().isBudgeted()) {
bsalomondace19e2014-11-17 07:34:06 -0800418 fBudgetedBytes += delta;
hendrikw876c3132015-03-04 10:33:49 -0800419 TRACE_COUNTER2(TRACE_DISABLED_BY_DEFAULT("skia.gpu.cache"), "skia budget", "used",
420 fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
bsalomondace19e2014-11-17 07:34:06 -0800421#if GR_CACHE_STATS
422 fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
423#endif
424 }
bsalomon71cb0c22014-11-14 12:10:14 -0800425
426 this->purgeAsNeeded();
427 this->validate();
428}
429
bsalomon0ea80f42015-02-11 10:49:59 -0800430void GrResourceCache::didChangeBudgetStatus(GrGpuResource* resource) {
bsalomon84c8e622014-11-17 09:33:27 -0800431 SkASSERT(resource);
432 SkASSERT(this->isInCache(resource));
433
434 size_t size = resource->gpuMemorySize();
435
bsalomon3582d3e2015-02-13 14:20:05 -0800436 if (resource->resourcePriv().isBudgeted()) {
bsalomon84c8e622014-11-17 09:33:27 -0800437 ++fBudgetedCount;
438 fBudgetedBytes += size;
bsalomonafe30052015-01-16 07:32:33 -0800439#if GR_CACHE_STATS
440 fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
441 fBudgetedHighWaterCount = SkTMax(fBudgetedCount, fBudgetedHighWaterCount);
442#endif
bsalomon84c8e622014-11-17 09:33:27 -0800443 this->purgeAsNeeded();
444 } else {
445 --fBudgetedCount;
446 fBudgetedBytes -= size;
447 }
hendrikw876c3132015-03-04 10:33:49 -0800448 TRACE_COUNTER2(TRACE_DISABLED_BY_DEFAULT("skia.gpu.cache"), "skia budget", "used",
449 fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
bsalomon84c8e622014-11-17 09:33:27 -0800450
451 this->validate();
452}
453
bsalomon3f324322015-04-08 11:01:54 -0700454void GrResourceCache::purgeAsNeeded() {
455 SkTArray<GrUniqueKeyInvalidatedMessage> invalidKeyMsgs;
456 fInvalidUniqueKeyInbox.poll(&invalidKeyMsgs);
457 if (invalidKeyMsgs.count()) {
458 this->processInvalidUniqueKeys(invalidKeyMsgs);
459 }
bsalomon71cb0c22014-11-14 12:10:14 -0800460
bsalomon3f324322015-04-08 11:01:54 -0700461 if (fFlushTimestamps) {
462 // Assuming kNumFlushesToDeleteUnusedResource is a power of 2.
463 SkASSERT(SkIsPow2(fMaxUnusedFlushes));
464 int oldestFlushIndex = (fLastFlushTimestampIndex + 1) & (fMaxUnusedFlushes - 1);
465
466 uint32_t oldestAllowedTimestamp = fFlushTimestamps[oldestFlushIndex];
467 while (fPurgeableQueue.count()) {
468 uint32_t oldestResourceTimestamp = fPurgeableQueue.peek()->cacheAccess().timestamp();
469 if (oldestAllowedTimestamp < oldestResourceTimestamp) {
470 break;
471 }
472 GrGpuResource* resource = fPurgeableQueue.peek();
473 SkASSERT(resource->isPurgeable());
474 resource->cacheAccess().release();
475 }
476 }
477
478 bool stillOverbudget = this->overBudget();
479 while (stillOverbudget && fPurgeableQueue.count()) {
bsalomon9f2d1572015-02-17 11:47:40 -0800480 GrGpuResource* resource = fPurgeableQueue.peek();
481 SkASSERT(resource->isPurgeable());
482 resource->cacheAccess().release();
bsalomon3f324322015-04-08 11:01:54 -0700483 stillOverbudget = this->overBudget();
bsalomon9f2d1572015-02-17 11:47:40 -0800484 }
bsalomon71cb0c22014-11-14 12:10:14 -0800485
bsalomonb436ed62014-11-17 12:15:56 -0800486 this->validate();
bsalomon9f2d1572015-02-17 11:47:40 -0800487
488 if (stillOverbudget) {
489 // Despite the purge we're still over budget. Call our over budget callback. If this frees
bsalomon3f324322015-04-08 11:01:54 -0700490 // any resources then we'll get notified and take appropriate action.
bsalomon9f2d1572015-02-17 11:47:40 -0800491 (*fOverBudgetCB)(fOverBudgetData);
492 this->validate();
493 }
bsalomon71cb0c22014-11-14 12:10:14 -0800494}
495
bsalomon0ea80f42015-02-11 10:49:59 -0800496void GrResourceCache::purgeAllUnlocked() {
bsalomon9f2d1572015-02-17 11:47:40 -0800497 // We could disable maintaining the heap property here, but it would add a lot of complexity.
498 // Moreover, this is rarely called.
499 while (fPurgeableQueue.count()) {
500 GrGpuResource* resource = fPurgeableQueue.peek();
501 SkASSERT(resource->isPurgeable());
502 resource->cacheAccess().release();
503 }
bsalomon71cb0c22014-11-14 12:10:14 -0800504
bsalomonb436ed62014-11-17 12:15:56 -0800505 this->validate();
bsalomon71cb0c22014-11-14 12:10:14 -0800506}
507
bsalomon8718aaf2015-02-19 07:24:21 -0800508void GrResourceCache::processInvalidUniqueKeys(
509 const SkTArray<GrUniqueKeyInvalidatedMessage>& msgs) {
bsalomon23e619c2015-02-06 11:54:28 -0800510 for (int i = 0; i < msgs.count(); ++i) {
bsalomon8718aaf2015-02-19 07:24:21 -0800511 GrGpuResource* resource = this->findAndRefUniqueResource(msgs[i].key());
bsalomon23e619c2015-02-06 11:54:28 -0800512 if (resource) {
bsalomon8718aaf2015-02-19 07:24:21 -0800513 resource->resourcePriv().removeUniqueKey();
bsalomon3f324322015-04-08 11:01:54 -0700514 resource->unref(); // If this resource is now purgeable, the cache will be notified.
bsalomon23e619c2015-02-06 11:54:28 -0800515 }
516 }
517}
518
bsalomonf320e042015-02-17 15:09:34 -0800519void GrResourceCache::addToNonpurgeableArray(GrGpuResource* resource) {
520 int index = fNonpurgeableResources.count();
521 *fNonpurgeableResources.append() = resource;
522 *resource->cacheAccess().accessCacheIndex() = index;
523}
524
525void GrResourceCache::removeFromNonpurgeableArray(GrGpuResource* resource) {
526 int* index = resource->cacheAccess().accessCacheIndex();
527 // Fill the whole we will create in the array with the tail object, adjust its index, and
528 // then pop the array
529 GrGpuResource* tail = *(fNonpurgeableResources.end() - 1);
530 SkASSERT(fNonpurgeableResources[*index] == resource);
531 fNonpurgeableResources[*index] = tail;
532 *tail->cacheAccess().accessCacheIndex() = *index;
533 fNonpurgeableResources.pop();
534 SkDEBUGCODE(*index = -1);
535}
536
bsalomonddf30e62015-02-19 11:38:44 -0800537uint32_t GrResourceCache::getNextTimestamp() {
538 // If we wrap then all the existing resources will appear older than any resources that get
539 // a timestamp after the wrap.
540 if (0 == fTimestamp) {
541 int count = this->getResourceCount();
542 if (count) {
543 // Reset all the timestamps. We sort the resources by timestamp and then assign
544 // sequential timestamps beginning with 0. This is O(n*lg(n)) but it should be extremely
545 // rare.
546 SkTDArray<GrGpuResource*> sortedPurgeableResources;
547 sortedPurgeableResources.setReserve(fPurgeableQueue.count());
548
549 while (fPurgeableQueue.count()) {
550 *sortedPurgeableResources.append() = fPurgeableQueue.peek();
551 fPurgeableQueue.pop();
552 }
553
554 struct Less {
555 bool operator()(GrGpuResource* a, GrGpuResource* b) {
556 return CompareTimestamp(a,b);
557 }
558 };
559 Less less;
560 SkTQSort(fNonpurgeableResources.begin(), fNonpurgeableResources.end() - 1, less);
561
562 // Pick resources out of the purgeable and non-purgeable arrays based on lowest
563 // timestamp and assign new timestamps.
564 int currP = 0;
565 int currNP = 0;
566 while (currP < sortedPurgeableResources.count() &&
567 currNP < fNonpurgeableResources.count()) {
568 uint32_t tsP = sortedPurgeableResources[currP]->cacheAccess().timestamp();
569 uint32_t tsNP = fNonpurgeableResources[currNP]->cacheAccess().timestamp();
570 SkASSERT(tsP != tsNP);
571 if (tsP < tsNP) {
572 sortedPurgeableResources[currP++]->cacheAccess().setTimestamp(fTimestamp++);
573 } else {
574 // Correct the index in the nonpurgeable array stored on the resource post-sort.
575 *fNonpurgeableResources[currNP]->cacheAccess().accessCacheIndex() = currNP;
576 fNonpurgeableResources[currNP++]->cacheAccess().setTimestamp(fTimestamp++);
577 }
578 }
579
580 // The above loop ended when we hit the end of one array. Finish the other one.
581 while (currP < sortedPurgeableResources.count()) {
582 sortedPurgeableResources[currP++]->cacheAccess().setTimestamp(fTimestamp++);
583 }
584 while (currNP < fNonpurgeableResources.count()) {
585 *fNonpurgeableResources[currNP]->cacheAccess().accessCacheIndex() = currNP;
586 fNonpurgeableResources[currNP++]->cacheAccess().setTimestamp(fTimestamp++);
587 }
588
589 // Rebuild the queue.
590 for (int i = 0; i < sortedPurgeableResources.count(); ++i) {
591 fPurgeableQueue.insert(sortedPurgeableResources[i]);
592 }
593
594 this->validate();
595 SkASSERT(count == this->getResourceCount());
596
597 // count should be the next timestamp we return.
598 SkASSERT(fTimestamp == SkToU32(count));
bsalomon3f324322015-04-08 11:01:54 -0700599
600 // The historical timestamps of flushes are now invalid.
601 this->resetFlushTimestamps();
bsalomonddf30e62015-02-19 11:38:44 -0800602 }
603 }
604 return fTimestamp++;
605}
606
bsalomon3f324322015-04-08 11:01:54 -0700607void GrResourceCache::notifyFlushOccurred() {
608 if (fFlushTimestamps) {
609 SkASSERT(SkIsPow2(fMaxUnusedFlushes));
610 fLastFlushTimestampIndex = (fLastFlushTimestampIndex + 1) & (fMaxUnusedFlushes - 1);
611 // get the timestamp before accessing fFlushTimestamps because getNextTimestamp will
612 // reallocate fFlushTimestamps on timestamp overflow.
613 uint32_t timestamp = this->getNextTimestamp();
614 fFlushTimestamps[fLastFlushTimestampIndex] = timestamp;
615 this->purgeAsNeeded();
616 }
617}
618
bsalomon71cb0c22014-11-14 12:10:14 -0800619#ifdef SK_DEBUG
bsalomon0ea80f42015-02-11 10:49:59 -0800620void GrResourceCache::validate() const {
bsalomonc2f35b72015-01-23 07:19:22 -0800621 // Reduce the frequency of validations for large resource counts.
622 static SkRandom gRandom;
623 int mask = (SkNextPow2(fCount + 1) >> 5) - 1;
624 if (~mask && (gRandom.nextU() & mask)) {
625 return;
626 }
627
bsalomonf320e042015-02-17 15:09:34 -0800628 struct Stats {
629 size_t fBytes;
630 int fBudgetedCount;
631 size_t fBudgetedBytes;
632 int fLocked;
633 int fScratch;
634 int fCouldBeScratch;
635 int fContent;
636 const ScratchMap* fScratchMap;
bsalomon8718aaf2015-02-19 07:24:21 -0800637 const UniqueHash* fUniqueHash;
bsalomon71cb0c22014-11-14 12:10:14 -0800638
bsalomonf320e042015-02-17 15:09:34 -0800639 Stats(const GrResourceCache* cache) {
640 memset(this, 0, sizeof(*this));
641 fScratchMap = &cache->fScratchMap;
bsalomon8718aaf2015-02-19 07:24:21 -0800642 fUniqueHash = &cache->fUniqueHash;
bsalomon71cb0c22014-11-14 12:10:14 -0800643 }
644
bsalomonf320e042015-02-17 15:09:34 -0800645 void update(GrGpuResource* resource) {
646 fBytes += resource->gpuMemorySize();
bsalomondace19e2014-11-17 07:34:06 -0800647
bsalomonf320e042015-02-17 15:09:34 -0800648 if (!resource->isPurgeable()) {
649 ++fLocked;
650 }
bsalomon9f2d1572015-02-17 11:47:40 -0800651
bsalomonf320e042015-02-17 15:09:34 -0800652 if (resource->cacheAccess().isScratch()) {
bsalomon8718aaf2015-02-19 07:24:21 -0800653 SkASSERT(!resource->getUniqueKey().isValid());
bsalomonf320e042015-02-17 15:09:34 -0800654 ++fScratch;
655 SkASSERT(fScratchMap->countForKey(resource->resourcePriv().getScratchKey()));
bsalomon6dc6f5f2015-06-18 09:12:16 -0700656 SkASSERT(!resource->cacheAccess().isExternal());
bsalomonf320e042015-02-17 15:09:34 -0800657 } else if (resource->resourcePriv().getScratchKey().isValid()) {
658 SkASSERT(!resource->resourcePriv().isBudgeted() ||
bsalomon8718aaf2015-02-19 07:24:21 -0800659 resource->getUniqueKey().isValid());
bsalomonf320e042015-02-17 15:09:34 -0800660 ++fCouldBeScratch;
661 SkASSERT(fScratchMap->countForKey(resource->resourcePriv().getScratchKey()));
bsalomon6dc6f5f2015-06-18 09:12:16 -0700662 SkASSERT(!resource->cacheAccess().isExternal());
bsalomonf320e042015-02-17 15:09:34 -0800663 }
bsalomon8718aaf2015-02-19 07:24:21 -0800664 const GrUniqueKey& uniqueKey = resource->getUniqueKey();
665 if (uniqueKey.isValid()) {
bsalomonf320e042015-02-17 15:09:34 -0800666 ++fContent;
bsalomon8718aaf2015-02-19 07:24:21 -0800667 SkASSERT(fUniqueHash->find(uniqueKey) == resource);
bsalomon6dc6f5f2015-06-18 09:12:16 -0700668 SkASSERT(!resource->cacheAccess().isExternal());
bsalomonf320e042015-02-17 15:09:34 -0800669 SkASSERT(resource->resourcePriv().isBudgeted());
670 }
671
672 if (resource->resourcePriv().isBudgeted()) {
673 ++fBudgetedCount;
674 fBudgetedBytes += resource->gpuMemorySize();
675 }
bsalomon9f2d1572015-02-17 11:47:40 -0800676 }
bsalomonf320e042015-02-17 15:09:34 -0800677 };
678
679 Stats stats(this);
680
681 for (int i = 0; i < fNonpurgeableResources.count(); ++i) {
bsalomon3f324322015-04-08 11:01:54 -0700682 SkASSERT(!fNonpurgeableResources[i]->isPurgeable() ||
683 fNewlyPurgeableResourceForValidation == fNonpurgeableResources[i]);
bsalomonf320e042015-02-17 15:09:34 -0800684 SkASSERT(*fNonpurgeableResources[i]->cacheAccess().accessCacheIndex() == i);
685 SkASSERT(!fNonpurgeableResources[i]->wasDestroyed());
686 stats.update(fNonpurgeableResources[i]);
bsalomon71cb0c22014-11-14 12:10:14 -0800687 }
bsalomon9f2d1572015-02-17 11:47:40 -0800688 for (int i = 0; i < fPurgeableQueue.count(); ++i) {
689 SkASSERT(fPurgeableQueue.at(i)->isPurgeable());
bsalomonf320e042015-02-17 15:09:34 -0800690 SkASSERT(*fPurgeableQueue.at(i)->cacheAccess().accessCacheIndex() == i);
691 SkASSERT(!fPurgeableQueue.at(i)->wasDestroyed());
692 stats.update(fPurgeableQueue.at(i));
bsalomon9f2d1572015-02-17 11:47:40 -0800693 }
694
bsalomonf320e042015-02-17 15:09:34 -0800695 SkASSERT(fCount == this->getResourceCount());
bsalomondace19e2014-11-17 07:34:06 -0800696 SkASSERT(fBudgetedCount <= fCount);
bsalomonf320e042015-02-17 15:09:34 -0800697 SkASSERT(fBudgetedBytes <= fBytes);
698 SkASSERT(stats.fBytes == fBytes);
699 SkASSERT(stats.fBudgetedBytes == fBudgetedBytes);
700 SkASSERT(stats.fBudgetedCount == fBudgetedCount);
bsalomon71cb0c22014-11-14 12:10:14 -0800701#if GR_CACHE_STATS
bsalomondace19e2014-11-17 07:34:06 -0800702 SkASSERT(fBudgetedHighWaterCount <= fHighWaterCount);
703 SkASSERT(fBudgetedHighWaterBytes <= fHighWaterBytes);
bsalomonf320e042015-02-17 15:09:34 -0800704 SkASSERT(fBytes <= fHighWaterBytes);
705 SkASSERT(fCount <= fHighWaterCount);
706 SkASSERT(fBudgetedBytes <= fBudgetedHighWaterBytes);
707 SkASSERT(fBudgetedCount <= fBudgetedHighWaterCount);
bsalomon71cb0c22014-11-14 12:10:14 -0800708#endif
bsalomon8718aaf2015-02-19 07:24:21 -0800709 SkASSERT(stats.fContent == fUniqueHash.count());
bsalomonf320e042015-02-17 15:09:34 -0800710 SkASSERT(stats.fScratch + stats.fCouldBeScratch == fScratchMap.count());
bsalomon71cb0c22014-11-14 12:10:14 -0800711
bsalomon3f324322015-04-08 11:01:54 -0700712 // This assertion is not currently valid because we can be in recursive notifyCntReachedZero()
bsalomon12299ab2014-11-14 13:33:09 -0800713 // calls. This will be fixed when subresource registration is explicit.
bsalomondace19e2014-11-17 07:34:06 -0800714 // bool overBudget = budgetedBytes > fMaxBytes || budgetedCount > fMaxCount;
bsalomon12299ab2014-11-14 13:33:09 -0800715 // SkASSERT(!overBudget || locked == count || fPurging);
bsalomon71cb0c22014-11-14 12:10:14 -0800716}
bsalomonf320e042015-02-17 15:09:34 -0800717
718bool GrResourceCache::isInCache(const GrGpuResource* resource) const {
719 int index = *resource->cacheAccess().accessCacheIndex();
720 if (index < 0) {
721 return false;
722 }
723 if (index < fPurgeableQueue.count() && fPurgeableQueue.at(index) == resource) {
724 return true;
725 }
726 if (index < fNonpurgeableResources.count() && fNonpurgeableResources[index] == resource) {
727 return true;
728 }
729 SkDEBUGFAIL("Resource index should be -1 or the resource should be in the cache.");
730 return false;
731}
732
bsalomon71cb0c22014-11-14 12:10:14 -0800733#endif