blob: 53e7f8811212245b91b33950d9f23b247cbad3a2 [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 resource->ref();
97
bsalomonc8dc1f72014-08-21 13:02:13 -070098 ++fCount;
bsalomon71cb0c22014-11-14 12:10:14 -080099 SkDEBUGCODE(fHighWaterCount = SkTMax(fCount, fHighWaterCount));
100 fBytes += resource->gpuMemorySize();
101 SkDEBUGCODE(fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes));
bsalomon453cf402014-11-11 14:15:57 -0800102 if (!resource->cacheAccess().getScratchKey().isNullScratch()) {
bsalomon8b79d232014-11-10 10:19:06 -0800103 // TODO(bsalomon): Make this assertion possible.
104 // SkASSERT(!resource->isWrapped());
bsalomon453cf402014-11-11 14:15:57 -0800105 fScratchMap.insert(resource->cacheAccess().getScratchKey(), resource);
bsalomon744998e2014-08-28 09:54:34 -0700106 }
bsalomon71cb0c22014-11-14 12:10:14 -0800107
108 this->purgeAsNeeded();
bsalomonc8dc1f72014-08-21 13:02:13 -0700109}
110
111void GrResourceCache2::removeResource(GrGpuResource* resource) {
bsalomon71cb0c22014-11-14 12:10:14 -0800112 AutoValidate av(this);
113
114 --fCount;
115 fBytes -= resource->gpuMemorySize();
bsalomon16961262014-08-26 14:01:07 -0700116 SkASSERT(this->isInCache(resource));
bsalomon744998e2014-08-28 09:54:34 -0700117 fResources.remove(resource);
bsalomon453cf402014-11-11 14:15:57 -0800118 if (!resource->cacheAccess().getScratchKey().isNullScratch()) {
119 fScratchMap.remove(resource->cacheAccess().getScratchKey(), resource);
bsalomon744998e2014-08-28 09:54:34 -0700120 }
bsalomon453cf402014-11-11 14:15:57 -0800121 if (const GrResourceKey* contentKey = resource->cacheAccess().getContentKey()) {
bsalomon8b79d232014-11-10 10:19:06 -0800122 fContentHash.remove(*contentKey);
123 }
bsalomonc8dc1f72014-08-21 13:02:13 -0700124}
125
126void GrResourceCache2::abandonAll() {
bsalomon71cb0c22014-11-14 12:10:14 -0800127 AutoValidate av(this);
128
129 SkASSERT(!fPurging);
bsalomonc8dc1f72014-08-21 13:02:13 -0700130 while (GrGpuResource* head = fResources.head()) {
131 SkASSERT(!head->wasDestroyed());
132 head->abandon();
bsalomon71cb0c22014-11-14 12:10:14 -0800133 head->unref();
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());
148 head->release();
bsalomon71cb0c22014-11-14 12:10:14 -0800149 head->unref();
bsalomonc8dc1f72014-08-21 13:02:13 -0700150 // release should have already removed this from the list.
151 SkASSERT(head != fResources.head());
152 }
bsalomon744998e2014-08-28 09:54:34 -0700153 SkASSERT(!fScratchMap.count());
bsalomonc8dc1f72014-08-21 13:02:13 -0700154 SkASSERT(!fCount);
155}
bsalomonbcf0a522014-10-08 08:40:09 -0700156
157class GrResourceCache2::AvailableForScratchUse {
158public:
bsalomon000f8292014-10-15 19:04:14 -0700159 AvailableForScratchUse(bool rejectPendingIO) : fRejectPendingIO(rejectPendingIO) { }
bsalomonbcf0a522014-10-08 08:40:09 -0700160
161 bool operator()(const GrGpuResource* resource) const {
bsalomon453cf402014-11-11 14:15:57 -0800162 if (!resource->reffedOnlyByCache() || !resource->cacheAccess().isScratch()) {
bsalomon000f8292014-10-15 19:04:14 -0700163 return false;
bsalomonbcf0a522014-10-08 08:40:09 -0700164 }
bsalomon000f8292014-10-15 19:04:14 -0700165
166 return !fRejectPendingIO || !resource->internalHasPendingIO();
bsalomonbcf0a522014-10-08 08:40:09 -0700167 }
bsalomon1e2530b2014-10-09 09:57:18 -0700168
bsalomonbcf0a522014-10-08 08:40:09 -0700169private:
bsalomon000f8292014-10-15 19:04:14 -0700170 bool fRejectPendingIO;
bsalomonbcf0a522014-10-08 08:40:09 -0700171};
172
173GrGpuResource* GrResourceCache2::findAndRefScratchResource(const GrResourceKey& scratchKey,
bsalomon000f8292014-10-15 19:04:14 -0700174 uint32_t flags) {
bsalomon71cb0c22014-11-14 12:10:14 -0800175 AutoValidate av(this);
176
177 SkASSERT(!fPurging);
bsalomonbcf0a522014-10-08 08:40:09 -0700178 SkASSERT(scratchKey.isScratch());
bsalomon000f8292014-10-15 19:04:14 -0700179
bsalomon71cb0c22014-11-14 12:10:14 -0800180 GrGpuResource* resource;
bsalomon000f8292014-10-15 19:04:14 -0700181 if (flags & (kPreferNoPendingIO_ScratchFlag | kRequireNoPendingIO_ScratchFlag)) {
bsalomon71cb0c22014-11-14 12:10:14 -0800182 resource = fScratchMap.find(scratchKey, AvailableForScratchUse(true));
bsalomon000f8292014-10-15 19:04:14 -0700183 if (resource) {
bsalomon71cb0c22014-11-14 12:10:14 -0800184 this->makeResourceMRU(resource);
bsalomon000f8292014-10-15 19:04:14 -0700185 return SkRef(resource);
186 } else if (flags & kRequireNoPendingIO_ScratchFlag) {
187 return NULL;
188 }
189 // TODO: fail here when kPrefer is specified, we didn't find a resource without pending io,
190 // but there is still space in our budget for the resource.
191 }
bsalomon71cb0c22014-11-14 12:10:14 -0800192 resource = fScratchMap.find(scratchKey, AvailableForScratchUse(false));
193 if (resource) {
194 resource->ref();
195 this->makeResourceMRU(resource);
196 }
197 return resource;
bsalomonbcf0a522014-10-08 08:40:09 -0700198}
bsalomon8b79d232014-11-10 10:19:06 -0800199
bsalomon6d4488c2014-11-11 07:27:16 -0800200bool GrResourceCache2::didSetContentKey(GrGpuResource* resource) {
bsalomon71cb0c22014-11-14 12:10:14 -0800201 SkASSERT(!fPurging);
bsalomon8b79d232014-11-10 10:19:06 -0800202 SkASSERT(resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800203 SkASSERT(this->isInCache(resource));
bsalomon453cf402014-11-11 14:15:57 -0800204 SkASSERT(resource->cacheAccess().getContentKey());
205 SkASSERT(!resource->cacheAccess().getContentKey()->isScratch());
bsalomon8b79d232014-11-10 10:19:06 -0800206
bsalomon453cf402014-11-11 14:15:57 -0800207 GrGpuResource* res = fContentHash.find(*resource->cacheAccess().getContentKey());
bsalomon8b79d232014-11-10 10:19:06 -0800208 if (NULL != res) {
209 return false;
210 }
211
212 fContentHash.add(resource);
bsalomon71cb0c22014-11-14 12:10:14 -0800213 this->validate();
bsalomon8b79d232014-11-10 10:19:06 -0800214 return true;
215}
bsalomon71cb0c22014-11-14 12:10:14 -0800216
217void GrResourceCache2::makeResourceMRU(GrGpuResource* resource) {
218 AutoValidate av(this);
219
220 SkASSERT(!fPurging);
221 SkASSERT(resource);
222 SkASSERT(this->isInCache(resource));
223 fResources.remove(resource);
224 fResources.addToHead(resource);
225}
226
227void GrResourceCache2::notifyPurgable(const GrGpuResource* resource) {
228 SkASSERT(resource);
229 SkASSERT(this->isInCache(resource));
230 SkASSERT(resource->isPurgable());
231
232 // We can't purge if in the middle of purging because purge is iterating. Instead record
233 // that additional resources became purgable.
234 if (fPurging) {
235 fNewlyPurgableResourceWhilePurging = true;
236 return;
237 }
238
239 // Purge the resource if we're over budget
240 bool overBudget = fCount > fMaxCount || fBytes > fMaxBytes;
241
242 // We should not be over budget here unless all resources are unpuragble.
243#ifdef SK_DEBUG
244 if (overBudget) {
245 ResourceList::Iter iter;
246 GrGpuResource* r = iter.init(fResources, ResourceList::Iter::kHead_IterStart);
247 for ( ; r; r = iter.next()) {
248 SkASSERT(r == resource || !r->isPurgable());
249 }
250 }
251#endif
252
253 // Also purge if the resource has neither a valid scratch key nor a content key.
254 bool noKey = !resource->cacheAccess().isScratch() &&
255 (NULL == resource->cacheAccess().getContentKey());
256
257 if (overBudget || noKey) {
258 SkDEBUGCODE(int beforeCount = fCount;)
259 resource->unref();
260 // We should at least have freed resource. It may have in turn freed other resources.
261 SkASSERT(fCount < beforeCount);
262 }
263
264 this->validate();
265}
266
267void GrResourceCache2::didChangeGpuMemorySize(const GrGpuResource* resource, size_t oldSize) {
268 // SkASSERT(!fPurging); GrPathRange increases size during flush. :(
269 SkASSERT(resource);
270 SkASSERT(this->isInCache(resource));
271
272 fBytes += resource->gpuMemorySize() - oldSize;
273 SkDEBUGCODE(fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes));
274
275 this->purgeAsNeeded();
276 this->validate();
277}
278
279void GrResourceCache2::internalPurgeAsNeeded() {
280 SkASSERT(!fPurging);
281 SkASSERT(!fNewlyPurgableResourceWhilePurging);
282 SkASSERT(fCount > fMaxCount || fBytes > fMaxBytes);
283
284 fPurging = true;
285
286 AutoValidate av(this); // Put this after setting fPurging so we're allowed to be over budget.
287
288 bool overBudget = true;
289 do {
290 fNewlyPurgableResourceWhilePurging = false;
291 ResourceList::Iter resourceIter;
292 GrGpuResource* resource = resourceIter.init(fResources,
293 ResourceList::Iter::kTail_IterStart);
294
295 while (resource) {
296 GrGpuResource* prev = resourceIter.prev();
297 if (resource->isPurgable()) {
298 resource->unref();
299 }
300 resource = prev;
301 if (fCount <= fMaxCount && fBytes <= fMaxBytes) {
302 overBudget = false;
303 resource = NULL;
304 }
305 }
306
307 if (!fNewlyPurgableResourceWhilePurging && overBudget && fOverBudgetCB) {
308 // Despite the purge we're still over budget. Call our over budget callback.
309 (*fOverBudgetCB)(fOverBudgetData);
310 }
311 } while (overBudget && fNewlyPurgableResourceWhilePurging);
312
313 fNewlyPurgableResourceWhilePurging = false;
314 fPurging = false;
315}
316
317void GrResourceCache2::purgeAllUnlocked() {
318 SkASSERT(!fPurging);
319 SkASSERT(!fNewlyPurgableResourceWhilePurging);
320
321 fPurging = true;
322
323 AutoValidate av(this); // Put this after setting fPurging so we're allowed to be over budget.
324
325 do {
326 fNewlyPurgableResourceWhilePurging = false;
327 ResourceList::Iter resourceIter;
328 GrGpuResource* resource =
329 resourceIter.init(fResources, ResourceList::Iter::kTail_IterStart);
330
331 while (resource) {
332 GrGpuResource* prev = resourceIter.prev();
333 if (resource->isPurgable()) {
334 resource->unref();
335 }
336 resource = prev;
337 }
338
339 if (!fNewlyPurgableResourceWhilePurging && fCount && fOverBudgetCB) {
340 (*fOverBudgetCB)(fOverBudgetData);
341 }
342 } while (fNewlyPurgableResourceWhilePurging);
343 fPurging = false;
344}
345
346#ifdef SK_DEBUG
347void GrResourceCache2::validate() const {
348 size_t bytes = 0;
349 int count = 0;
350 int locked = 0;
351 int scratch = 0;
352 int couldBeScratch = 0;
353 int content = 0;
354
355 ResourceList::Iter iter;
356 GrGpuResource* resource = iter.init(fResources, ResourceList::Iter::kHead_IterStart);
357 for ( ; resource; resource = iter.next()) {
358 bytes += resource->gpuMemorySize();
359 ++count;
360
361 if (!resource->isPurgable()) {
362 ++locked;
363 }
364
365 if (resource->cacheAccess().isScratch()) {
366 SkASSERT(NULL == resource->cacheAccess().getContentKey());
367 ++scratch;
368 SkASSERT(fScratchMap.countForKey(resource->cacheAccess().getScratchKey()));
369 } else if (!resource->cacheAccess().getScratchKey().isNullScratch()) {
370 SkASSERT(NULL != resource->cacheAccess().getContentKey());
371 ++couldBeScratch;
372 SkASSERT(fScratchMap.countForKey(resource->cacheAccess().getScratchKey()));
373 }
374
375 if (const GrResourceKey* contentKey = resource->cacheAccess().getContentKey()) {
376 ++content;
377 SkASSERT(fContentHash.find(*contentKey) == resource);
378 }
379 }
380
381 SkASSERT(bytes == fBytes);
382 SkASSERT(count == fCount);
383#if GR_CACHE_STATS
384 SkASSERT(bytes <= fHighWaterBytes);
385 SkASSERT(count <= fHighWaterCount);
386#endif
387 SkASSERT(content == fContentHash.count());
388 SkASSERT(scratch + couldBeScratch == fScratchMap.count());
389
390 bool overBudget = bytes > fMaxBytes || count > fMaxCount;
391 SkASSERT(!overBudget || locked == count || fPurging);
392}
393#endif
394
395#if GR_CACHE_STATS
396void GrResourceCache2::printStats() const {
397 this->validate();
398
399 int locked = 0;
400 int scratch = 0;
401
402 ResourceList::Iter iter;
403 GrGpuResource* resource = iter.init(fResources, ResourceList::Iter::kHead_IterStart);
404
405 for ( ; resource; resource = iter.next()) {
406 if (!resource->isPurgable()) {
407 ++locked;
408 }
409 if (resource->cacheAccess().isScratch()) {
410 ++scratch;
411 }
412 }
413
414 float countUtilization = (100.f * fCount) / fMaxCount;
415 float byteUtilization = (100.f * fBytes) / fMaxBytes;
416
417 SkDebugf("Budget: %d items %d bytes\n", fMaxCount, fMaxBytes);
418 SkDebugf("\t\tEntry Count: current %d (%d locked, %d scratch %.2g%% full), high %d\n",
419 fCount, locked, scratch, countUtilization, fHighWaterCount);
420 SkDebugf("\t\tEntry Bytes: current %d (%.2g%% full) high %d\n",
421 fBytes, byteUtilization, fHighWaterBytes);
422}
423
424#endif