blob: 3f7498c325c2a3044da30ec20878d4f9d66ed6e0 [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 SkDEBUGCODE(fHighWaterCount = SkTMax(fCount, fHighWaterCount));
99 fBytes += resource->gpuMemorySize();
100 SkDEBUGCODE(fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes));
bsalomon453cf402014-11-11 14:15:57 -0800101 if (!resource->cacheAccess().getScratchKey().isNullScratch()) {
bsalomon8b79d232014-11-10 10:19:06 -0800102 // TODO(bsalomon): Make this assertion possible.
103 // SkASSERT(!resource->isWrapped());
bsalomon453cf402014-11-11 14:15:57 -0800104 fScratchMap.insert(resource->cacheAccess().getScratchKey(), resource);
bsalomon744998e2014-08-28 09:54:34 -0700105 }
bsalomon71cb0c22014-11-14 12:10:14 -0800106
107 this->purgeAsNeeded();
bsalomonc8dc1f72014-08-21 13:02:13 -0700108}
109
110void GrResourceCache2::removeResource(GrGpuResource* resource) {
bsalomon71cb0c22014-11-14 12:10:14 -0800111 AutoValidate av(this);
112
113 --fCount;
114 fBytes -= resource->gpuMemorySize();
bsalomon16961262014-08-26 14:01:07 -0700115 SkASSERT(this->isInCache(resource));
bsalomon744998e2014-08-28 09:54:34 -0700116 fResources.remove(resource);
bsalomon453cf402014-11-11 14:15:57 -0800117 if (!resource->cacheAccess().getScratchKey().isNullScratch()) {
118 fScratchMap.remove(resource->cacheAccess().getScratchKey(), resource);
bsalomon744998e2014-08-28 09:54:34 -0700119 }
bsalomon453cf402014-11-11 14:15:57 -0800120 if (const GrResourceKey* contentKey = resource->cacheAccess().getContentKey()) {
bsalomon8b79d232014-11-10 10:19:06 -0800121 fContentHash.remove(*contentKey);
122 }
bsalomonc8dc1f72014-08-21 13:02:13 -0700123}
124
125void GrResourceCache2::abandonAll() {
bsalomon71cb0c22014-11-14 12:10:14 -0800126 AutoValidate av(this);
127
128 SkASSERT(!fPurging);
bsalomonc8dc1f72014-08-21 13:02:13 -0700129 while (GrGpuResource* head = fResources.head()) {
130 SkASSERT(!head->wasDestroyed());
bsalomon12299ab2014-11-14 13:33:09 -0800131 head->cacheAccess().abandon();
bsalomonc8dc1f72014-08-21 13:02:13 -0700132 // abandon should have already removed this from the list.
133 SkASSERT(head != fResources.head());
134 }
bsalomon744998e2014-08-28 09:54:34 -0700135 SkASSERT(!fScratchMap.count());
bsalomon8b79d232014-11-10 10:19:06 -0800136 SkASSERT(!fContentHash.count());
bsalomonc8dc1f72014-08-21 13:02:13 -0700137 SkASSERT(!fCount);
138}
139
140void GrResourceCache2::releaseAll() {
bsalomon71cb0c22014-11-14 12:10:14 -0800141 AutoValidate av(this);
142
143 SkASSERT(!fPurging);
bsalomonc8dc1f72014-08-21 13:02:13 -0700144 while (GrGpuResource* head = fResources.head()) {
145 SkASSERT(!head->wasDestroyed());
bsalomon12299ab2014-11-14 13:33:09 -0800146 head->cacheAccess().release();
bsalomonc8dc1f72014-08-21 13:02:13 -0700147 // release should have already removed this from the list.
148 SkASSERT(head != fResources.head());
149 }
bsalomon744998e2014-08-28 09:54:34 -0700150 SkASSERT(!fScratchMap.count());
bsalomonc8dc1f72014-08-21 13:02:13 -0700151 SkASSERT(!fCount);
152}
bsalomonbcf0a522014-10-08 08:40:09 -0700153
154class GrResourceCache2::AvailableForScratchUse {
155public:
bsalomon000f8292014-10-15 19:04:14 -0700156 AvailableForScratchUse(bool rejectPendingIO) : fRejectPendingIO(rejectPendingIO) { }
bsalomonbcf0a522014-10-08 08:40:09 -0700157
158 bool operator()(const GrGpuResource* resource) const {
bsalomon12299ab2014-11-14 13:33:09 -0800159 if (resource->internalHasRef() || !resource->cacheAccess().isScratch()) {
bsalomon000f8292014-10-15 19:04:14 -0700160 return false;
bsalomonbcf0a522014-10-08 08:40:09 -0700161 }
bsalomon000f8292014-10-15 19:04:14 -0700162
163 return !fRejectPendingIO || !resource->internalHasPendingIO();
bsalomonbcf0a522014-10-08 08:40:09 -0700164 }
bsalomon1e2530b2014-10-09 09:57:18 -0700165
bsalomonbcf0a522014-10-08 08:40:09 -0700166private:
bsalomon000f8292014-10-15 19:04:14 -0700167 bool fRejectPendingIO;
bsalomonbcf0a522014-10-08 08:40:09 -0700168};
169
170GrGpuResource* GrResourceCache2::findAndRefScratchResource(const GrResourceKey& scratchKey,
bsalomon000f8292014-10-15 19:04:14 -0700171 uint32_t flags) {
bsalomon71cb0c22014-11-14 12:10:14 -0800172 AutoValidate av(this);
173
174 SkASSERT(!fPurging);
bsalomonbcf0a522014-10-08 08:40:09 -0700175 SkASSERT(scratchKey.isScratch());
bsalomon000f8292014-10-15 19:04:14 -0700176
bsalomon71cb0c22014-11-14 12:10:14 -0800177 GrGpuResource* resource;
bsalomon000f8292014-10-15 19:04:14 -0700178 if (flags & (kPreferNoPendingIO_ScratchFlag | kRequireNoPendingIO_ScratchFlag)) {
bsalomon71cb0c22014-11-14 12:10:14 -0800179 resource = fScratchMap.find(scratchKey, AvailableForScratchUse(true));
bsalomon000f8292014-10-15 19:04:14 -0700180 if (resource) {
bsalomon71cb0c22014-11-14 12:10:14 -0800181 this->makeResourceMRU(resource);
bsalomon000f8292014-10-15 19:04:14 -0700182 return SkRef(resource);
183 } else if (flags & kRequireNoPendingIO_ScratchFlag) {
184 return NULL;
185 }
186 // TODO: fail here when kPrefer is specified, we didn't find a resource without pending io,
187 // but there is still space in our budget for the resource.
188 }
bsalomon71cb0c22014-11-14 12:10:14 -0800189 resource = fScratchMap.find(scratchKey, AvailableForScratchUse(false));
190 if (resource) {
191 resource->ref();
192 this->makeResourceMRU(resource);
193 }
194 return resource;
bsalomonbcf0a522014-10-08 08:40:09 -0700195}
bsalomon8b79d232014-11-10 10:19:06 -0800196
bsalomon6d4488c2014-11-11 07:27:16 -0800197bool GrResourceCache2::didSetContentKey(GrGpuResource* resource) {
bsalomon71cb0c22014-11-14 12:10:14 -0800198 SkASSERT(!fPurging);
bsalomon8b79d232014-11-10 10:19:06 -0800199 SkASSERT(resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800200 SkASSERT(this->isInCache(resource));
bsalomon453cf402014-11-11 14:15:57 -0800201 SkASSERT(resource->cacheAccess().getContentKey());
202 SkASSERT(!resource->cacheAccess().getContentKey()->isScratch());
bsalomon8b79d232014-11-10 10:19:06 -0800203
bsalomon453cf402014-11-11 14:15:57 -0800204 GrGpuResource* res = fContentHash.find(*resource->cacheAccess().getContentKey());
bsalomon8b79d232014-11-10 10:19:06 -0800205 if (NULL != res) {
206 return false;
207 }
208
209 fContentHash.add(resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800210 this->validate();
bsalomon8b79d232014-11-10 10:19:06 -0800211 return true;
212}
bsalomon71cb0c22014-11-14 12:10:14 -0800213
214void GrResourceCache2::makeResourceMRU(GrGpuResource* resource) {
215 AutoValidate av(this);
216
217 SkASSERT(!fPurging);
218 SkASSERT(resource);
219 SkASSERT(this->isInCache(resource));
220 fResources.remove(resource);
221 fResources.addToHead(resource);
222}
223
bsalomon12299ab2014-11-14 13:33:09 -0800224void GrResourceCache2::notifyPurgable(GrGpuResource* resource) {
bsalomon71cb0c22014-11-14 12:10:14 -0800225 SkASSERT(resource);
226 SkASSERT(this->isInCache(resource));
227 SkASSERT(resource->isPurgable());
228
229 // We can't purge if in the middle of purging because purge is iterating. Instead record
230 // that additional resources became purgable.
231 if (fPurging) {
232 fNewlyPurgableResourceWhilePurging = true;
233 return;
234 }
235
236 // Purge the resource if we're over budget
237 bool overBudget = fCount > fMaxCount || fBytes > fMaxBytes;
238
bsalomon71cb0c22014-11-14 12:10:14 -0800239 // Also purge if the resource has neither a valid scratch key nor a content key.
240 bool noKey = !resource->cacheAccess().isScratch() &&
241 (NULL == resource->cacheAccess().getContentKey());
242
243 if (overBudget || noKey) {
244 SkDEBUGCODE(int beforeCount = fCount;)
bsalomon12299ab2014-11-14 13:33:09 -0800245 resource->cacheAccess().release();
246 // We should at least free this resource, perhaps dependent resources as well.
bsalomon71cb0c22014-11-14 12:10:14 -0800247 SkASSERT(fCount < beforeCount);
248 }
249
250 this->validate();
251}
252
253void GrResourceCache2::didChangeGpuMemorySize(const GrGpuResource* resource, size_t oldSize) {
254 // SkASSERT(!fPurging); GrPathRange increases size during flush. :(
255 SkASSERT(resource);
256 SkASSERT(this->isInCache(resource));
257
258 fBytes += resource->gpuMemorySize() - oldSize;
259 SkDEBUGCODE(fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes));
260
261 this->purgeAsNeeded();
262 this->validate();
263}
264
265void GrResourceCache2::internalPurgeAsNeeded() {
266 SkASSERT(!fPurging);
267 SkASSERT(!fNewlyPurgableResourceWhilePurging);
268 SkASSERT(fCount > fMaxCount || fBytes > fMaxBytes);
269
270 fPurging = true;
271
272 AutoValidate av(this); // Put this after setting fPurging so we're allowed to be over budget.
273
274 bool overBudget = true;
275 do {
276 fNewlyPurgableResourceWhilePurging = false;
277 ResourceList::Iter resourceIter;
278 GrGpuResource* resource = resourceIter.init(fResources,
279 ResourceList::Iter::kTail_IterStart);
280
281 while (resource) {
282 GrGpuResource* prev = resourceIter.prev();
283 if (resource->isPurgable()) {
bsalomon12299ab2014-11-14 13:33:09 -0800284 resource->cacheAccess().release();
bsalomon71cb0c22014-11-14 12:10:14 -0800285 }
286 resource = prev;
287 if (fCount <= fMaxCount && fBytes <= fMaxBytes) {
288 overBudget = false;
289 resource = NULL;
290 }
291 }
292
293 if (!fNewlyPurgableResourceWhilePurging && overBudget && fOverBudgetCB) {
294 // Despite the purge we're still over budget. Call our over budget callback.
295 (*fOverBudgetCB)(fOverBudgetData);
296 }
297 } while (overBudget && fNewlyPurgableResourceWhilePurging);
298
299 fNewlyPurgableResourceWhilePurging = false;
300 fPurging = false;
301}
302
303void GrResourceCache2::purgeAllUnlocked() {
304 SkASSERT(!fPurging);
305 SkASSERT(!fNewlyPurgableResourceWhilePurging);
306
307 fPurging = true;
308
309 AutoValidate av(this); // Put this after setting fPurging so we're allowed to be over budget.
310
311 do {
312 fNewlyPurgableResourceWhilePurging = false;
313 ResourceList::Iter resourceIter;
314 GrGpuResource* resource =
315 resourceIter.init(fResources, ResourceList::Iter::kTail_IterStart);
316
317 while (resource) {
318 GrGpuResource* prev = resourceIter.prev();
319 if (resource->isPurgable()) {
bsalomon12299ab2014-11-14 13:33:09 -0800320 resource->cacheAccess().release();
321 }
bsalomon71cb0c22014-11-14 12:10:14 -0800322 resource = prev;
323 }
324
325 if (!fNewlyPurgableResourceWhilePurging && fCount && fOverBudgetCB) {
326 (*fOverBudgetCB)(fOverBudgetData);
327 }
328 } while (fNewlyPurgableResourceWhilePurging);
329 fPurging = false;
330}
331
332#ifdef SK_DEBUG
333void GrResourceCache2::validate() const {
334 size_t bytes = 0;
335 int count = 0;
336 int locked = 0;
337 int scratch = 0;
338 int couldBeScratch = 0;
339 int content = 0;
340
341 ResourceList::Iter iter;
342 GrGpuResource* resource = iter.init(fResources, ResourceList::Iter::kHead_IterStart);
343 for ( ; resource; resource = iter.next()) {
344 bytes += resource->gpuMemorySize();
345 ++count;
346
347 if (!resource->isPurgable()) {
348 ++locked;
349 }
350
351 if (resource->cacheAccess().isScratch()) {
352 SkASSERT(NULL == resource->cacheAccess().getContentKey());
353 ++scratch;
354 SkASSERT(fScratchMap.countForKey(resource->cacheAccess().getScratchKey()));
355 } else if (!resource->cacheAccess().getScratchKey().isNullScratch()) {
356 SkASSERT(NULL != resource->cacheAccess().getContentKey());
357 ++couldBeScratch;
358 SkASSERT(fScratchMap.countForKey(resource->cacheAccess().getScratchKey()));
359 }
360
361 if (const GrResourceKey* contentKey = resource->cacheAccess().getContentKey()) {
362 ++content;
363 SkASSERT(fContentHash.find(*contentKey) == resource);
364 }
365 }
366
367 SkASSERT(bytes == fBytes);
368 SkASSERT(count == fCount);
369#if GR_CACHE_STATS
370 SkASSERT(bytes <= fHighWaterBytes);
371 SkASSERT(count <= fHighWaterCount);
372#endif
373 SkASSERT(content == fContentHash.count());
374 SkASSERT(scratch + couldBeScratch == fScratchMap.count());
375
bsalomon12299ab2014-11-14 13:33:09 -0800376 // This assertion is not currently valid because we can be in recursive notifyIsPurgable()
377 // calls. This will be fixed when subresource registration is explicit.
378 // bool overBudget = bytes > fMaxBytes || count > fMaxCount;
379 // SkASSERT(!overBudget || locked == count || fPurging);
bsalomon71cb0c22014-11-14 12:10:14 -0800380}
381#endif
382
383#if GR_CACHE_STATS
384void GrResourceCache2::printStats() const {
385 this->validate();
386
387 int locked = 0;
388 int scratch = 0;
389
390 ResourceList::Iter iter;
391 GrGpuResource* resource = iter.init(fResources, ResourceList::Iter::kHead_IterStart);
392
393 for ( ; resource; resource = iter.next()) {
394 if (!resource->isPurgable()) {
395 ++locked;
396 }
397 if (resource->cacheAccess().isScratch()) {
398 ++scratch;
399 }
400 }
401
402 float countUtilization = (100.f * fCount) / fMaxCount;
403 float byteUtilization = (100.f * fBytes) / fMaxBytes;
404
405 SkDebugf("Budget: %d items %d bytes\n", fMaxCount, fMaxBytes);
406 SkDebugf("\t\tEntry Count: current %d (%d locked, %d scratch %.2g%% full), high %d\n",
407 fCount, locked, scratch, countUtilization, fHighWaterCount);
408 SkDebugf("\t\tEntry Bytes: current %d (%.2g%% full) high %d\n",
409 fBytes, byteUtilization, fHighWaterBytes);
410}
411
412#endif