blob: 4593b82a7b57915fb2c2260d6062ef441f7972c9 [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
10#include "GrResourceCache2.h"
bsalomonbcf0a522014-10-08 08:40:09 -070011#include "GrGpuResource.h"
bsalomonc8dc1f72014-08-21 13:02:13 -070012
bsalomon71cb0c22014-11-14 12:10:14 -080013#include "SkGr.h"
14#include "SkMessageBus.h"
15
16DECLARE_SKMESSAGEBUS_MESSAGE(GrResourceInvalidatedMessage);
17
18//////////////////////////////////////////////////////////////////////////////
19
bsalomonfe369ee2014-11-10 11:59:06 -080020GrResourceKey& GrResourceKey::NullScratchKey() {
21 static const GrCacheID::Key kBogusKey = { { {0} } };
22 static GrCacheID kBogusID(ScratchDomain(), kBogusKey);
23 static GrResourceKey kNullScratchKey(kBogusID, NoneResourceType(), 0);
24 return kNullScratchKey;
25}
26
27GrResourceKey::ResourceType GrResourceKey::NoneResourceType() {
28 static const ResourceType gNoneResourceType = GenerateResourceType();
29 return gNoneResourceType;
30}
31
32GrCacheID::Domain GrResourceKey::ScratchDomain() {
33 static const GrCacheID::Domain gDomain = GrCacheID::GenerateDomain();
34 return gDomain;
35}
36
bsalomon71cb0c22014-11-14 12:10:14 -080037GrResourceKey::ResourceType GrResourceKey::GenerateResourceType() {
38 static int32_t gNextType = 0;
39
40 int32_t type = sk_atomic_inc(&gNextType);
41 if (type >= (1 << 8 * sizeof(ResourceType))) {
42 SkFAIL("Too many Resource Types");
43 }
44
45 return static_cast<ResourceType>(type);
46}
47
bsalomonfe369ee2014-11-10 11:59:06 -080048//////////////////////////////////////////////////////////////////////////////
49
bsalomon71cb0c22014-11-14 12:10:14 -080050class GrResourceCache2::AutoValidate : ::SkNoncopyable {
51public:
52 AutoValidate(GrResourceCache2* cache) : fCache(cache) { cache->validate(); }
53 ~AutoValidate() { fCache->validate(); }
54private:
55 GrResourceCache2* fCache;
56};
57
58 //////////////////////////////////////////////////////////////////////////////
59
60static const int kDefaultMaxCount = 2 * (1 << 10);
61static const size_t kDefaultMaxSize = 96 * (1 << 20);
62
63GrResourceCache2::GrResourceCache2()
64 : fMaxCount(kDefaultMaxCount)
65 , fMaxBytes(kDefaultMaxSize)
66#if GR_CACHE_STATS
67 , fHighWaterCount(0)
68 , fHighWaterBytes(0)
69#endif
70 , fCount(0)
71 , fBytes(0)
72 , fPurging(false)
73 , fNewlyPurgableResourceWhilePurging(false)
74 , fOverBudgetCB(NULL)
75 , fOverBudgetData(NULL) {
76}
77
bsalomonc8dc1f72014-08-21 13:02:13 -070078GrResourceCache2::~GrResourceCache2() {
79 this->releaseAll();
80}
81
bsalomon71cb0c22014-11-14 12:10:14 -080082void GrResourceCache2::setLimits(int count, size_t bytes) {
83 fMaxCount = count;
84 fMaxBytes = bytes;
85 this->purgeAsNeeded();
86}
87
bsalomonc8dc1f72014-08-21 13:02:13 -070088void GrResourceCache2::insertResource(GrGpuResource* resource) {
bsalomon71cb0c22014-11-14 12:10:14 -080089 AutoValidate av(this);
90
bsalomon49f085d2014-09-05 13:34:00 -070091 SkASSERT(resource);
bsalomonc8dc1f72014-08-21 13:02:13 -070092 SkASSERT(!resource->wasDestroyed());
bsalomon16961262014-08-26 14:01:07 -070093 SkASSERT(!this->isInCache(resource));
bsalomon71cb0c22014-11-14 12:10:14 -080094 SkASSERT(!fPurging);
bsalomonc8dc1f72014-08-21 13:02:13 -070095 fResources.addToHead(resource);
bsalomon71cb0c22014-11-14 12:10:14 -080096
bsalomonc8dc1f72014-08-21 13:02:13 -070097 ++fCount;
bsalomon71cb0c22014-11-14 12:10:14 -080098 fBytes += resource->gpuMemorySize();
bsalomon82b1d622014-11-14 13:59:57 -080099#if GR_CACHE_STATS
100 fHighWaterCount = SkTMax(fCount, fHighWaterCount);
101 fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes);
102#endif
bsalomon453cf402014-11-11 14:15:57 -0800103 if (!resource->cacheAccess().getScratchKey().isNullScratch()) {
bsalomon8b79d232014-11-10 10:19:06 -0800104 // TODO(bsalomon): Make this assertion possible.
105 // SkASSERT(!resource->isWrapped());
bsalomon453cf402014-11-11 14:15:57 -0800106 fScratchMap.insert(resource->cacheAccess().getScratchKey(), resource);
bsalomon744998e2014-08-28 09:54:34 -0700107 }
bsalomon71cb0c22014-11-14 12:10:14 -0800108
109 this->purgeAsNeeded();
bsalomonc8dc1f72014-08-21 13:02:13 -0700110}
111
112void GrResourceCache2::removeResource(GrGpuResource* resource) {
bsalomon71cb0c22014-11-14 12:10:14 -0800113 AutoValidate av(this);
114
115 --fCount;
116 fBytes -= resource->gpuMemorySize();
bsalomon16961262014-08-26 14:01:07 -0700117 SkASSERT(this->isInCache(resource));
bsalomon744998e2014-08-28 09:54:34 -0700118 fResources.remove(resource);
bsalomon453cf402014-11-11 14:15:57 -0800119 if (!resource->cacheAccess().getScratchKey().isNullScratch()) {
120 fScratchMap.remove(resource->cacheAccess().getScratchKey(), resource);
bsalomon744998e2014-08-28 09:54:34 -0700121 }
bsalomon453cf402014-11-11 14:15:57 -0800122 if (const GrResourceKey* contentKey = resource->cacheAccess().getContentKey()) {
bsalomon8b79d232014-11-10 10:19:06 -0800123 fContentHash.remove(*contentKey);
124 }
bsalomonc8dc1f72014-08-21 13:02:13 -0700125}
126
127void GrResourceCache2::abandonAll() {
bsalomon71cb0c22014-11-14 12:10:14 -0800128 AutoValidate av(this);
129
130 SkASSERT(!fPurging);
bsalomonc8dc1f72014-08-21 13:02:13 -0700131 while (GrGpuResource* head = fResources.head()) {
132 SkASSERT(!head->wasDestroyed());
bsalomon12299ab2014-11-14 13:33:09 -0800133 head->cacheAccess().abandon();
bsalomonc8dc1f72014-08-21 13:02:13 -0700134 // abandon should have already removed this from the list.
135 SkASSERT(head != fResources.head());
136 }
bsalomon744998e2014-08-28 09:54:34 -0700137 SkASSERT(!fScratchMap.count());
bsalomon8b79d232014-11-10 10:19:06 -0800138 SkASSERT(!fContentHash.count());
bsalomonc8dc1f72014-08-21 13:02:13 -0700139 SkASSERT(!fCount);
140}
141
142void GrResourceCache2::releaseAll() {
bsalomon71cb0c22014-11-14 12:10:14 -0800143 AutoValidate av(this);
144
145 SkASSERT(!fPurging);
bsalomonc8dc1f72014-08-21 13:02:13 -0700146 while (GrGpuResource* head = fResources.head()) {
147 SkASSERT(!head->wasDestroyed());
bsalomon12299ab2014-11-14 13:33:09 -0800148 head->cacheAccess().release();
bsalomonc8dc1f72014-08-21 13:02:13 -0700149 // release should have already removed this from the list.
150 SkASSERT(head != fResources.head());
151 }
bsalomon744998e2014-08-28 09:54:34 -0700152 SkASSERT(!fScratchMap.count());
bsalomonc8dc1f72014-08-21 13:02:13 -0700153 SkASSERT(!fCount);
154}
bsalomonbcf0a522014-10-08 08:40:09 -0700155
156class GrResourceCache2::AvailableForScratchUse {
157public:
bsalomon000f8292014-10-15 19:04:14 -0700158 AvailableForScratchUse(bool rejectPendingIO) : fRejectPendingIO(rejectPendingIO) { }
bsalomonbcf0a522014-10-08 08:40:09 -0700159
160 bool operator()(const GrGpuResource* resource) const {
bsalomon12299ab2014-11-14 13:33:09 -0800161 if (resource->internalHasRef() || !resource->cacheAccess().isScratch()) {
bsalomon000f8292014-10-15 19:04:14 -0700162 return false;
bsalomonbcf0a522014-10-08 08:40:09 -0700163 }
bsalomon000f8292014-10-15 19:04:14 -0700164
165 return !fRejectPendingIO || !resource->internalHasPendingIO();
bsalomonbcf0a522014-10-08 08:40:09 -0700166 }
bsalomon1e2530b2014-10-09 09:57:18 -0700167
bsalomonbcf0a522014-10-08 08:40:09 -0700168private:
bsalomon000f8292014-10-15 19:04:14 -0700169 bool fRejectPendingIO;
bsalomonbcf0a522014-10-08 08:40:09 -0700170};
171
172GrGpuResource* GrResourceCache2::findAndRefScratchResource(const GrResourceKey& scratchKey,
bsalomon000f8292014-10-15 19:04:14 -0700173 uint32_t flags) {
bsalomon71cb0c22014-11-14 12:10:14 -0800174 AutoValidate av(this);
175
176 SkASSERT(!fPurging);
bsalomonbcf0a522014-10-08 08:40:09 -0700177 SkASSERT(scratchKey.isScratch());
bsalomon000f8292014-10-15 19:04:14 -0700178
bsalomon71cb0c22014-11-14 12:10:14 -0800179 GrGpuResource* resource;
bsalomon000f8292014-10-15 19:04:14 -0700180 if (flags & (kPreferNoPendingIO_ScratchFlag | kRequireNoPendingIO_ScratchFlag)) {
bsalomon71cb0c22014-11-14 12:10:14 -0800181 resource = fScratchMap.find(scratchKey, AvailableForScratchUse(true));
bsalomon000f8292014-10-15 19:04:14 -0700182 if (resource) {
bsalomon71cb0c22014-11-14 12:10:14 -0800183 this->makeResourceMRU(resource);
bsalomon000f8292014-10-15 19:04:14 -0700184 return SkRef(resource);
185 } else if (flags & kRequireNoPendingIO_ScratchFlag) {
186 return NULL;
187 }
188 // TODO: fail here when kPrefer is specified, we didn't find a resource without pending io,
189 // but there is still space in our budget for the resource.
190 }
bsalomon71cb0c22014-11-14 12:10:14 -0800191 resource = fScratchMap.find(scratchKey, AvailableForScratchUse(false));
192 if (resource) {
193 resource->ref();
194 this->makeResourceMRU(resource);
195 }
196 return resource;
bsalomonbcf0a522014-10-08 08:40:09 -0700197}
bsalomon8b79d232014-11-10 10:19:06 -0800198
bsalomon6d4488c2014-11-11 07:27:16 -0800199bool GrResourceCache2::didSetContentKey(GrGpuResource* resource) {
bsalomon71cb0c22014-11-14 12:10:14 -0800200 SkASSERT(!fPurging);
bsalomon8b79d232014-11-10 10:19:06 -0800201 SkASSERT(resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800202 SkASSERT(this->isInCache(resource));
bsalomon453cf402014-11-11 14:15:57 -0800203 SkASSERT(resource->cacheAccess().getContentKey());
204 SkASSERT(!resource->cacheAccess().getContentKey()->isScratch());
bsalomon8b79d232014-11-10 10:19:06 -0800205
bsalomon453cf402014-11-11 14:15:57 -0800206 GrGpuResource* res = fContentHash.find(*resource->cacheAccess().getContentKey());
bsalomon8b79d232014-11-10 10:19:06 -0800207 if (NULL != res) {
208 return false;
209 }
210
211 fContentHash.add(resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800212 this->validate();
bsalomon8b79d232014-11-10 10:19:06 -0800213 return true;
214}
bsalomon71cb0c22014-11-14 12:10:14 -0800215
216void GrResourceCache2::makeResourceMRU(GrGpuResource* resource) {
217 AutoValidate av(this);
218
219 SkASSERT(!fPurging);
220 SkASSERT(resource);
221 SkASSERT(this->isInCache(resource));
222 fResources.remove(resource);
223 fResources.addToHead(resource);
224}
225
bsalomon12299ab2014-11-14 13:33:09 -0800226void GrResourceCache2::notifyPurgable(GrGpuResource* resource) {
bsalomon71cb0c22014-11-14 12:10:14 -0800227 SkASSERT(resource);
228 SkASSERT(this->isInCache(resource));
229 SkASSERT(resource->isPurgable());
230
231 // We can't purge if in the middle of purging because purge is iterating. Instead record
232 // that additional resources became purgable.
233 if (fPurging) {
234 fNewlyPurgableResourceWhilePurging = true;
235 return;
236 }
237
238 // Purge the resource if we're over budget
239 bool overBudget = fCount > fMaxCount || fBytes > fMaxBytes;
240
bsalomon71cb0c22014-11-14 12:10:14 -0800241 // Also purge if the resource has neither a valid scratch key nor a content key.
242 bool noKey = !resource->cacheAccess().isScratch() &&
243 (NULL == resource->cacheAccess().getContentKey());
244
245 if (overBudget || noKey) {
246 SkDEBUGCODE(int beforeCount = fCount;)
bsalomon12299ab2014-11-14 13:33:09 -0800247 resource->cacheAccess().release();
248 // We should at least free this resource, perhaps dependent resources as well.
bsalomon71cb0c22014-11-14 12:10:14 -0800249 SkASSERT(fCount < beforeCount);
250 }
251
252 this->validate();
253}
254
255void GrResourceCache2::didChangeGpuMemorySize(const GrGpuResource* resource, size_t oldSize) {
256 // SkASSERT(!fPurging); GrPathRange increases size during flush. :(
257 SkASSERT(resource);
258 SkASSERT(this->isInCache(resource));
259
260 fBytes += resource->gpuMemorySize() - oldSize;
bsalomon82b1d622014-11-14 13:59:57 -0800261#if GR_CACHE_STATS
262 fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes);
263#endif
bsalomon71cb0c22014-11-14 12:10:14 -0800264
265 this->purgeAsNeeded();
266 this->validate();
267}
268
269void GrResourceCache2::internalPurgeAsNeeded() {
270 SkASSERT(!fPurging);
271 SkASSERT(!fNewlyPurgableResourceWhilePurging);
272 SkASSERT(fCount > fMaxCount || fBytes > fMaxBytes);
273
274 fPurging = true;
275
276 AutoValidate av(this); // Put this after setting fPurging so we're allowed to be over budget.
277
278 bool overBudget = true;
279 do {
280 fNewlyPurgableResourceWhilePurging = false;
281 ResourceList::Iter resourceIter;
282 GrGpuResource* resource = resourceIter.init(fResources,
283 ResourceList::Iter::kTail_IterStart);
284
285 while (resource) {
286 GrGpuResource* prev = resourceIter.prev();
287 if (resource->isPurgable()) {
bsalomon12299ab2014-11-14 13:33:09 -0800288 resource->cacheAccess().release();
bsalomon71cb0c22014-11-14 12:10:14 -0800289 }
290 resource = prev;
291 if (fCount <= fMaxCount && fBytes <= fMaxBytes) {
292 overBudget = false;
293 resource = NULL;
294 }
295 }
296
297 if (!fNewlyPurgableResourceWhilePurging && overBudget && fOverBudgetCB) {
298 // Despite the purge we're still over budget. Call our over budget callback.
299 (*fOverBudgetCB)(fOverBudgetData);
300 }
301 } while (overBudget && fNewlyPurgableResourceWhilePurging);
302
303 fNewlyPurgableResourceWhilePurging = false;
304 fPurging = false;
305}
306
307void GrResourceCache2::purgeAllUnlocked() {
308 SkASSERT(!fPurging);
309 SkASSERT(!fNewlyPurgableResourceWhilePurging);
310
311 fPurging = true;
312
313 AutoValidate av(this); // Put this after setting fPurging so we're allowed to be over budget.
314
315 do {
316 fNewlyPurgableResourceWhilePurging = false;
317 ResourceList::Iter resourceIter;
318 GrGpuResource* resource =
319 resourceIter.init(fResources, ResourceList::Iter::kTail_IterStart);
320
321 while (resource) {
322 GrGpuResource* prev = resourceIter.prev();
323 if (resource->isPurgable()) {
bsalomon12299ab2014-11-14 13:33:09 -0800324 resource->cacheAccess().release();
325 }
bsalomon71cb0c22014-11-14 12:10:14 -0800326 resource = prev;
327 }
328
329 if (!fNewlyPurgableResourceWhilePurging && fCount && fOverBudgetCB) {
330 (*fOverBudgetCB)(fOverBudgetData);
331 }
332 } while (fNewlyPurgableResourceWhilePurging);
333 fPurging = false;
334}
335
336#ifdef SK_DEBUG
337void GrResourceCache2::validate() const {
338 size_t bytes = 0;
339 int count = 0;
340 int locked = 0;
341 int scratch = 0;
342 int couldBeScratch = 0;
343 int content = 0;
344
345 ResourceList::Iter iter;
346 GrGpuResource* resource = iter.init(fResources, ResourceList::Iter::kHead_IterStart);
347 for ( ; resource; resource = iter.next()) {
348 bytes += resource->gpuMemorySize();
349 ++count;
350
351 if (!resource->isPurgable()) {
352 ++locked;
353 }
354
355 if (resource->cacheAccess().isScratch()) {
356 SkASSERT(NULL == resource->cacheAccess().getContentKey());
357 ++scratch;
358 SkASSERT(fScratchMap.countForKey(resource->cacheAccess().getScratchKey()));
359 } else if (!resource->cacheAccess().getScratchKey().isNullScratch()) {
360 SkASSERT(NULL != resource->cacheAccess().getContentKey());
361 ++couldBeScratch;
362 SkASSERT(fScratchMap.countForKey(resource->cacheAccess().getScratchKey()));
363 }
364
365 if (const GrResourceKey* contentKey = resource->cacheAccess().getContentKey()) {
366 ++content;
367 SkASSERT(fContentHash.find(*contentKey) == resource);
368 }
369 }
370
371 SkASSERT(bytes == fBytes);
372 SkASSERT(count == fCount);
373#if GR_CACHE_STATS
374 SkASSERT(bytes <= fHighWaterBytes);
375 SkASSERT(count <= fHighWaterCount);
376#endif
377 SkASSERT(content == fContentHash.count());
378 SkASSERT(scratch + couldBeScratch == fScratchMap.count());
379
bsalomon12299ab2014-11-14 13:33:09 -0800380 // This assertion is not currently valid because we can be in recursive notifyIsPurgable()
381 // calls. This will be fixed when subresource registration is explicit.
382 // bool overBudget = bytes > fMaxBytes || count > fMaxCount;
383 // SkASSERT(!overBudget || locked == count || fPurging);
bsalomon71cb0c22014-11-14 12:10:14 -0800384}
385#endif
386
387#if GR_CACHE_STATS
388void GrResourceCache2::printStats() const {
389 this->validate();
390
391 int locked = 0;
392 int scratch = 0;
393
394 ResourceList::Iter iter;
395 GrGpuResource* resource = iter.init(fResources, ResourceList::Iter::kHead_IterStart);
396
397 for ( ; resource; resource = iter.next()) {
398 if (!resource->isPurgable()) {
399 ++locked;
400 }
401 if (resource->cacheAccess().isScratch()) {
402 ++scratch;
403 }
404 }
405
406 float countUtilization = (100.f * fCount) / fMaxCount;
407 float byteUtilization = (100.f * fBytes) / fMaxBytes;
408
409 SkDebugf("Budget: %d items %d bytes\n", fMaxCount, fMaxBytes);
410 SkDebugf("\t\tEntry Count: current %d (%d locked, %d scratch %.2g%% full), high %d\n",
411 fCount, locked, scratch, countUtilization, fHighWaterCount);
412 SkDebugf("\t\tEntry Bytes: current %d (%.2g%% full) high %d\n",
413 fBytes, byteUtilization, fHighWaterBytes);
414}
415
416#endif