1 /*
2  * Copyright 2013 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 
8 #include "SkMessageBus.h"
9 #include "SkMipMap.h"
10 #include "SkMutex.h"
11 #include "SkOpts.h"
12 #include "SkPixelRef.h"
13 #include "SkResourceCache.h"
14 #include "SkTraceMemoryDump.h"
15 
16 #include <stddef.h>
17 #include <stdlib.h>
18 
DECLARE_SKMESSAGEBUS_MESSAGE(SkResourceCache::PurgeSharedIDMessage)19 DECLARE_SKMESSAGEBUS_MESSAGE(SkResourceCache::PurgeSharedIDMessage)
20 
21 // This can be defined by the caller's build system
22 //#define SK_USE_DISCARDABLE_SCALEDIMAGECACHE
23 
24 #ifndef SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT
25 #   define SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT   1024
26 #endif
27 
28 #ifndef SK_DEFAULT_IMAGE_CACHE_LIMIT
29     #define SK_DEFAULT_IMAGE_CACHE_LIMIT     (32 * 1024 * 1024)
30 #endif
31 
32 void SkResourceCache::Key::init(void* nameSpace, uint64_t sharedID, size_t dataSize) {
33     SkASSERT(SkAlign4(dataSize) == dataSize);
34 
35     // fCount32 and fHash are not hashed
36     static const int kUnhashedLocal32s = 2; // fCache32 + fHash
37     static const int kSharedIDLocal32s = 2; // fSharedID_lo + fSharedID_hi
38     static const int kHashedLocal32s = kSharedIDLocal32s + (sizeof(fNamespace) >> 2);
39     static const int kLocal32s = kUnhashedLocal32s + kHashedLocal32s;
40 
41     static_assert(sizeof(Key) == (kLocal32s << 2), "unaccounted_key_locals");
42     static_assert(sizeof(Key) == offsetof(Key, fNamespace) + sizeof(fNamespace),
43                  "namespace_field_must_be_last");
44 
45     fCount32 = SkToS32(kLocal32s + (dataSize >> 2));
46     fSharedID_lo = (uint32_t)sharedID;
47     fSharedID_hi = (uint32_t)(sharedID >> 32);
48     fNamespace = nameSpace;
49     // skip unhashed fields when computing the hash
50     fHash = SkOpts::hash(this->as32() + kUnhashedLocal32s,
51                          (fCount32 - kUnhashedLocal32s) << 2);
52 }
53 
54 #include "SkTDynamicHash.h"
55 
56 class SkResourceCache::Hash :
57     public SkTDynamicHash<SkResourceCache::Rec, SkResourceCache::Key> {};
58 
59 
60 ///////////////////////////////////////////////////////////////////////////////
61 
init()62 void SkResourceCache::init() {
63     fHead = nullptr;
64     fTail = nullptr;
65     fHash = new Hash;
66     fTotalBytesUsed = 0;
67     fCount = 0;
68     fSingleAllocationByteLimit = 0;
69     fAllocator = nullptr;
70 
71     // One of these should be explicit set by the caller after we return.
72     fTotalByteLimit = 0;
73     fDiscardableFactory = nullptr;
74 }
75 
76 #include "SkDiscardableMemory.h"
77 
78 class SkOneShotDiscardablePixelRef : public SkPixelRef {
79 public:
80 
81     // Ownership of the discardablememory is transfered to the pixelref
82     // The pixelref will ref() the colortable (if not NULL), and unref() in destructor
83     SkOneShotDiscardablePixelRef(const SkImageInfo&, SkDiscardableMemory*, size_t rowBytes,
84                                  SkColorTable*);
85     ~SkOneShotDiscardablePixelRef();
86 
87 protected:
88     bool onNewLockPixels(LockRec*) override;
89     void onUnlockPixels() override;
90     size_t getAllocatedSizeInBytes() const override;
91 
diagnostic_only_getDiscardable() const92     SkDiscardableMemory* diagnostic_only_getDiscardable() const override { return fDM; }
93 
94 private:
95     SkDiscardableMemory* fDM;
96     size_t               fRB;
97     bool                 fFirstTime;
98     SkColorTable*        fCTable;
99 
100     typedef SkPixelRef INHERITED;
101 };
102 
SkOneShotDiscardablePixelRef(const SkImageInfo & info,SkDiscardableMemory * dm,size_t rowBytes,SkColorTable * ctable)103 SkOneShotDiscardablePixelRef::SkOneShotDiscardablePixelRef(const SkImageInfo& info,
104                                              SkDiscardableMemory* dm,
105                                              size_t rowBytes,
106                                              SkColorTable* ctable)
107     : INHERITED(info)
108     , fDM(dm)
109     , fRB(rowBytes)
110     , fCTable(ctable)
111 {
112     SkASSERT(dm->data());
113     fFirstTime = true;
114     SkSafeRef(ctable);
115 }
116 
~SkOneShotDiscardablePixelRef()117 SkOneShotDiscardablePixelRef::~SkOneShotDiscardablePixelRef() {
118     delete fDM;
119     SkSafeUnref(fCTable);
120 }
121 
onNewLockPixels(LockRec * rec)122 bool SkOneShotDiscardablePixelRef::onNewLockPixels(LockRec* rec) {
123     if (fFirstTime) {
124         // we're already locked
125         SkASSERT(fDM->data());
126         fFirstTime = false;
127         goto SUCCESS;
128     }
129 
130     // A previous call to onUnlock may have deleted our DM, so check for that
131     if (nullptr == fDM) {
132         return false;
133     }
134 
135     if (!fDM->lock()) {
136         // since it failed, we delete it now, to free-up the resource
137         delete fDM;
138         fDM = nullptr;
139         return false;
140     }
141 
142 SUCCESS:
143     rec->fPixels = fDM->data();
144     rec->fColorTable = fCTable;
145     rec->fRowBytes = fRB;
146     return true;
147 }
148 
onUnlockPixels()149 void SkOneShotDiscardablePixelRef::onUnlockPixels() {
150     SkASSERT(!fFirstTime);
151     fDM->unlock();
152 }
153 
getAllocatedSizeInBytes() const154 size_t SkOneShotDiscardablePixelRef::getAllocatedSizeInBytes() const {
155     return this->info().getSafeSize(fRB);
156 }
157 
158 class SkResourceCacheDiscardableAllocator : public SkBitmap::Allocator {
159 public:
SkResourceCacheDiscardableAllocator(SkResourceCache::DiscardableFactory factory)160     SkResourceCacheDiscardableAllocator(SkResourceCache::DiscardableFactory factory) {
161         SkASSERT(factory);
162         fFactory = factory;
163     }
164 
165     bool allocPixelRef(SkBitmap*, SkColorTable*) override;
166 
167 private:
168     SkResourceCache::DiscardableFactory fFactory;
169 };
170 
allocPixelRef(SkBitmap * bitmap,SkColorTable * ctable)171 bool SkResourceCacheDiscardableAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
172     size_t size = bitmap->getSize();
173     uint64_t size64 = bitmap->computeSize64();
174     if (0 == size || size64 > (uint64_t)size) {
175         return false;
176     }
177 
178     if (kIndex_8_SkColorType == bitmap->colorType()) {
179         if (!ctable) {
180             return false;
181         }
182     } else {
183         ctable = nullptr;
184     }
185 
186     SkDiscardableMemory* dm = fFactory(size);
187     if (nullptr == dm) {
188         return false;
189     }
190 
191     SkImageInfo info = bitmap->info();
192     bitmap->setPixelRef(new SkOneShotDiscardablePixelRef(info, dm, bitmap->rowBytes(),
193                                                          ctable))->unref();
194     bitmap->lockPixels();
195     return bitmap->readyToDraw();
196 }
197 
SkResourceCache(DiscardableFactory factory)198 SkResourceCache::SkResourceCache(DiscardableFactory factory) {
199     this->init();
200     fDiscardableFactory = factory;
201 
202     fAllocator = new SkResourceCacheDiscardableAllocator(factory);
203 }
204 
SkResourceCache(size_t byteLimit)205 SkResourceCache::SkResourceCache(size_t byteLimit) {
206     this->init();
207     fTotalByteLimit = byteLimit;
208 }
209 
~SkResourceCache()210 SkResourceCache::~SkResourceCache() {
211     SkSafeUnref(fAllocator);
212 
213     Rec* rec = fHead;
214     while (rec) {
215         Rec* next = rec->fNext;
216         delete rec;
217         rec = next;
218     }
219     delete fHash;
220 }
221 
222 ////////////////////////////////////////////////////////////////////////////////
223 
find(const Key & key,FindVisitor visitor,void * context)224 bool SkResourceCache::find(const Key& key, FindVisitor visitor, void* context) {
225     this->checkMessages();
226 
227     Rec* rec = fHash->find(key);
228     if (rec) {
229         if (visitor(*rec, context)) {
230             this->moveToHead(rec);  // for our LRU
231             return true;
232         } else {
233             this->remove(rec);  // stale
234             return false;
235         }
236     }
237     return false;
238 }
239 
make_size_str(size_t size,SkString * str)240 static void make_size_str(size_t size, SkString* str) {
241     const char suffix[] = { 'b', 'k', 'm', 'g', 't', 0 };
242     int i = 0;
243     while (suffix[i] && (size > 1024)) {
244         i += 1;
245         size >>= 10;
246     }
247     str->printf("%zu%c", size, suffix[i]);
248 }
249 
250 static bool gDumpCacheTransactions;
251 
add(Rec * rec)252 void SkResourceCache::add(Rec* rec) {
253     this->checkMessages();
254 
255     SkASSERT(rec);
256     // See if we already have this key (racy inserts, etc.)
257     Rec* existing = fHash->find(rec->getKey());
258     if (existing) {
259         delete rec;
260         return;
261     }
262 
263     this->addToHead(rec);
264     fHash->add(rec);
265 
266     if (gDumpCacheTransactions) {
267         SkString bytesStr, totalStr;
268         make_size_str(rec->bytesUsed(), &bytesStr);
269         make_size_str(fTotalBytesUsed, &totalStr);
270         SkDebugf("RC:    add %5s %12p key %08x -- total %5s, count %d\n",
271                  bytesStr.c_str(), rec, rec->getHash(), totalStr.c_str(), fCount);
272     }
273 
274     // since the new rec may push us over-budget, we perform a purge check now
275     this->purgeAsNeeded();
276 }
277 
remove(Rec * rec)278 void SkResourceCache::remove(Rec* rec) {
279     size_t used = rec->bytesUsed();
280     SkASSERT(used <= fTotalBytesUsed);
281 
282     this->release(rec);
283     fHash->remove(rec->getKey());
284 
285     fTotalBytesUsed -= used;
286     fCount -= 1;
287 
288     if (gDumpCacheTransactions) {
289         SkString bytesStr, totalStr;
290         make_size_str(used, &bytesStr);
291         make_size_str(fTotalBytesUsed, &totalStr);
292         SkDebugf("RC: remove %5s %12p key %08x -- total %5s, count %d\n",
293                  bytesStr.c_str(), rec, rec->getHash(), totalStr.c_str(), fCount);
294     }
295 
296     delete rec;
297 }
298 
purgeAsNeeded(bool forcePurge)299 void SkResourceCache::purgeAsNeeded(bool forcePurge) {
300     size_t byteLimit;
301     int    countLimit;
302 
303     if (fDiscardableFactory) {
304         countLimit = SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT;
305         byteLimit = SK_MaxU32;  // no limit based on bytes
306     } else {
307         countLimit = SK_MaxS32; // no limit based on count
308         byteLimit = fTotalByteLimit;
309     }
310 
311     Rec* rec = fTail;
312     while (rec) {
313         if (!forcePurge && fTotalBytesUsed < byteLimit && fCount < countLimit) {
314             break;
315         }
316 
317         Rec* prev = rec->fPrev;
318         this->remove(rec);
319         rec = prev;
320     }
321 }
322 
323 //#define SK_TRACK_PURGE_SHAREDID_HITRATE
324 
325 #ifdef SK_TRACK_PURGE_SHAREDID_HITRATE
326 static int gPurgeCallCounter;
327 static int gPurgeHitCounter;
328 #endif
329 
purgeSharedID(uint64_t sharedID)330 void SkResourceCache::purgeSharedID(uint64_t sharedID) {
331     if (0 == sharedID) {
332         return;
333     }
334 
335 #ifdef SK_TRACK_PURGE_SHAREDID_HITRATE
336     gPurgeCallCounter += 1;
337     bool found = false;
338 #endif
339     // go backwards, just like purgeAsNeeded, just to make the code similar.
340     // could iterate either direction and still be correct.
341     Rec* rec = fTail;
342     while (rec) {
343         Rec* prev = rec->fPrev;
344         if (rec->getKey().getSharedID() == sharedID) {
345 //            SkDebugf("purgeSharedID id=%llx rec=%p\n", sharedID, rec);
346             this->remove(rec);
347 #ifdef SK_TRACK_PURGE_SHAREDID_HITRATE
348             found = true;
349 #endif
350         }
351         rec = prev;
352     }
353 
354 #ifdef SK_TRACK_PURGE_SHAREDID_HITRATE
355     if (found) {
356         gPurgeHitCounter += 1;
357     }
358 
359     SkDebugf("PurgeShared calls=%d hits=%d rate=%g\n", gPurgeCallCounter, gPurgeHitCounter,
360              gPurgeHitCounter * 100.0 / gPurgeCallCounter);
361 #endif
362 }
363 
visitAll(Visitor visitor,void * context)364 void SkResourceCache::visitAll(Visitor visitor, void* context) {
365     // go backwards, just like purgeAsNeeded, just to make the code similar.
366     // could iterate either direction and still be correct.
367     Rec* rec = fTail;
368     while (rec) {
369         visitor(*rec, context);
370         rec = rec->fPrev;
371     }
372 }
373 
374 ///////////////////////////////////////////////////////////////////////////////////////////////////
375 
setTotalByteLimit(size_t newLimit)376 size_t SkResourceCache::setTotalByteLimit(size_t newLimit) {
377     size_t prevLimit = fTotalByteLimit;
378     fTotalByteLimit = newLimit;
379     if (newLimit < prevLimit) {
380         this->purgeAsNeeded();
381     }
382     return prevLimit;
383 }
384 
newCachedData(size_t bytes)385 SkCachedData* SkResourceCache::newCachedData(size_t bytes) {
386     this->checkMessages();
387 
388     if (fDiscardableFactory) {
389         SkDiscardableMemory* dm = fDiscardableFactory(bytes);
390         return dm ? new SkCachedData(bytes, dm) : nullptr;
391     } else {
392         return new SkCachedData(sk_malloc_throw(bytes), bytes);
393     }
394 }
395 
396 ///////////////////////////////////////////////////////////////////////////////
397 
release(Rec * rec)398 void SkResourceCache::release(Rec* rec) {
399     Rec* prev = rec->fPrev;
400     Rec* next = rec->fNext;
401 
402     if (!prev) {
403         SkASSERT(fHead == rec);
404         fHead = next;
405     } else {
406         prev->fNext = next;
407     }
408 
409     if (!next) {
410         fTail = prev;
411     } else {
412         next->fPrev = prev;
413     }
414 
415     rec->fNext = rec->fPrev = nullptr;
416 }
417 
moveToHead(Rec * rec)418 void SkResourceCache::moveToHead(Rec* rec) {
419     if (fHead == rec) {
420         return;
421     }
422 
423     SkASSERT(fHead);
424     SkASSERT(fTail);
425 
426     this->validate();
427 
428     this->release(rec);
429 
430     fHead->fPrev = rec;
431     rec->fNext = fHead;
432     fHead = rec;
433 
434     this->validate();
435 }
436 
addToHead(Rec * rec)437 void SkResourceCache::addToHead(Rec* rec) {
438     this->validate();
439 
440     rec->fPrev = nullptr;
441     rec->fNext = fHead;
442     if (fHead) {
443         fHead->fPrev = rec;
444     }
445     fHead = rec;
446     if (!fTail) {
447         fTail = rec;
448     }
449     fTotalBytesUsed += rec->bytesUsed();
450     fCount += 1;
451 
452     this->validate();
453 }
454 
455 ///////////////////////////////////////////////////////////////////////////////
456 
457 #ifdef SK_DEBUG
validate() const458 void SkResourceCache::validate() const {
459     if (nullptr == fHead) {
460         SkASSERT(nullptr == fTail);
461         SkASSERT(0 == fTotalBytesUsed);
462         return;
463     }
464 
465     if (fHead == fTail) {
466         SkASSERT(nullptr == fHead->fPrev);
467         SkASSERT(nullptr == fHead->fNext);
468         SkASSERT(fHead->bytesUsed() == fTotalBytesUsed);
469         return;
470     }
471 
472     SkASSERT(nullptr == fHead->fPrev);
473     SkASSERT(fHead->fNext);
474     SkASSERT(nullptr == fTail->fNext);
475     SkASSERT(fTail->fPrev);
476 
477     size_t used = 0;
478     int count = 0;
479     const Rec* rec = fHead;
480     while (rec) {
481         count += 1;
482         used += rec->bytesUsed();
483         SkASSERT(used <= fTotalBytesUsed);
484         rec = rec->fNext;
485     }
486     SkASSERT(fCount == count);
487 
488     rec = fTail;
489     while (rec) {
490         SkASSERT(count > 0);
491         count -= 1;
492         SkASSERT(used >= rec->bytesUsed());
493         used -= rec->bytesUsed();
494         rec = rec->fPrev;
495     }
496 
497     SkASSERT(0 == count);
498     SkASSERT(0 == used);
499 }
500 #endif
501 
dump() const502 void SkResourceCache::dump() const {
503     this->validate();
504 
505     SkDebugf("SkResourceCache: count=%d bytes=%d %s\n",
506              fCount, fTotalBytesUsed, fDiscardableFactory ? "discardable" : "malloc");
507 }
508 
setSingleAllocationByteLimit(size_t newLimit)509 size_t SkResourceCache::setSingleAllocationByteLimit(size_t newLimit) {
510     size_t oldLimit = fSingleAllocationByteLimit;
511     fSingleAllocationByteLimit = newLimit;
512     return oldLimit;
513 }
514 
getSingleAllocationByteLimit() const515 size_t SkResourceCache::getSingleAllocationByteLimit() const {
516     return fSingleAllocationByteLimit;
517 }
518 
getEffectiveSingleAllocationByteLimit() const519 size_t SkResourceCache::getEffectiveSingleAllocationByteLimit() const {
520     // fSingleAllocationByteLimit == 0 means the caller is asking for our default
521     size_t limit = fSingleAllocationByteLimit;
522 
523     // if we're not discardable (i.e. we are fixed-budget) then cap the single-limit
524     // to our budget.
525     if (nullptr == fDiscardableFactory) {
526         if (0 == limit) {
527             limit = fTotalByteLimit;
528         } else {
529             limit = SkTMin(limit, fTotalByteLimit);
530         }
531     }
532     return limit;
533 }
534 
checkMessages()535 void SkResourceCache::checkMessages() {
536     SkTArray<PurgeSharedIDMessage> msgs;
537     fPurgeSharedIDInbox.poll(&msgs);
538     for (int i = 0; i < msgs.count(); ++i) {
539         this->purgeSharedID(msgs[i].fSharedID);
540     }
541 }
542 
543 ///////////////////////////////////////////////////////////////////////////////
544 
545 SK_DECLARE_STATIC_MUTEX(gMutex);
546 static SkResourceCache* gResourceCache = nullptr;
547 
548 /** Must hold gMutex when calling. */
get_cache()549 static SkResourceCache* get_cache() {
550     // gMutex is always held when this is called, so we don't need to be fancy in here.
551     gMutex.assertHeld();
552     if (nullptr == gResourceCache) {
553 #ifdef SK_USE_DISCARDABLE_SCALEDIMAGECACHE
554         gResourceCache = new SkResourceCache(SkDiscardableMemory::Create);
555 #else
556         gResourceCache = new SkResourceCache(SK_DEFAULT_IMAGE_CACHE_LIMIT);
557 #endif
558     }
559     return gResourceCache;
560 }
561 
GetTotalBytesUsed()562 size_t SkResourceCache::GetTotalBytesUsed() {
563     SkAutoMutexAcquire am(gMutex);
564     return get_cache()->getTotalBytesUsed();
565 }
566 
GetTotalByteLimit()567 size_t SkResourceCache::GetTotalByteLimit() {
568     SkAutoMutexAcquire am(gMutex);
569     return get_cache()->getTotalByteLimit();
570 }
571 
SetTotalByteLimit(size_t newLimit)572 size_t SkResourceCache::SetTotalByteLimit(size_t newLimit) {
573     SkAutoMutexAcquire am(gMutex);
574     return get_cache()->setTotalByteLimit(newLimit);
575 }
576 
GetDiscardableFactory()577 SkResourceCache::DiscardableFactory SkResourceCache::GetDiscardableFactory() {
578     SkAutoMutexAcquire am(gMutex);
579     return get_cache()->discardableFactory();
580 }
581 
GetAllocator()582 SkBitmap::Allocator* SkResourceCache::GetAllocator() {
583     SkAutoMutexAcquire am(gMutex);
584     return get_cache()->allocator();
585 }
586 
NewCachedData(size_t bytes)587 SkCachedData* SkResourceCache::NewCachedData(size_t bytes) {
588     SkAutoMutexAcquire am(gMutex);
589     return get_cache()->newCachedData(bytes);
590 }
591 
Dump()592 void SkResourceCache::Dump() {
593     SkAutoMutexAcquire am(gMutex);
594     get_cache()->dump();
595 }
596 
SetSingleAllocationByteLimit(size_t size)597 size_t SkResourceCache::SetSingleAllocationByteLimit(size_t size) {
598     SkAutoMutexAcquire am(gMutex);
599     return get_cache()->setSingleAllocationByteLimit(size);
600 }
601 
GetSingleAllocationByteLimit()602 size_t SkResourceCache::GetSingleAllocationByteLimit() {
603     SkAutoMutexAcquire am(gMutex);
604     return get_cache()->getSingleAllocationByteLimit();
605 }
606 
GetEffectiveSingleAllocationByteLimit()607 size_t SkResourceCache::GetEffectiveSingleAllocationByteLimit() {
608     SkAutoMutexAcquire am(gMutex);
609     return get_cache()->getEffectiveSingleAllocationByteLimit();
610 }
611 
PurgeAll()612 void SkResourceCache::PurgeAll() {
613     SkAutoMutexAcquire am(gMutex);
614     return get_cache()->purgeAll();
615 }
616 
Find(const Key & key,FindVisitor visitor,void * context)617 bool SkResourceCache::Find(const Key& key, FindVisitor visitor, void* context) {
618     SkAutoMutexAcquire am(gMutex);
619     return get_cache()->find(key, visitor, context);
620 }
621 
Add(Rec * rec)622 void SkResourceCache::Add(Rec* rec) {
623     SkAutoMutexAcquire am(gMutex);
624     get_cache()->add(rec);
625 }
626 
VisitAll(Visitor visitor,void * context)627 void SkResourceCache::VisitAll(Visitor visitor, void* context) {
628     SkAutoMutexAcquire am(gMutex);
629     get_cache()->visitAll(visitor, context);
630 }
631 
PostPurgeSharedID(uint64_t sharedID)632 void SkResourceCache::PostPurgeSharedID(uint64_t sharedID) {
633     if (sharedID) {
634         SkMessageBus<PurgeSharedIDMessage>::Post(PurgeSharedIDMessage(sharedID));
635     }
636 }
637 
638 ///////////////////////////////////////////////////////////////////////////////
639 
640 #include "SkGraphics.h"
641 #include "SkImageFilter.h"
642 
GetResourceCacheTotalBytesUsed()643 size_t SkGraphics::GetResourceCacheTotalBytesUsed() {
644     return SkResourceCache::GetTotalBytesUsed();
645 }
646 
GetResourceCacheTotalByteLimit()647 size_t SkGraphics::GetResourceCacheTotalByteLimit() {
648     return SkResourceCache::GetTotalByteLimit();
649 }
650 
SetResourceCacheTotalByteLimit(size_t newLimit)651 size_t SkGraphics::SetResourceCacheTotalByteLimit(size_t newLimit) {
652     return SkResourceCache::SetTotalByteLimit(newLimit);
653 }
654 
GetResourceCacheSingleAllocationByteLimit()655 size_t SkGraphics::GetResourceCacheSingleAllocationByteLimit() {
656     return SkResourceCache::GetSingleAllocationByteLimit();
657 }
658 
SetResourceCacheSingleAllocationByteLimit(size_t newLimit)659 size_t SkGraphics::SetResourceCacheSingleAllocationByteLimit(size_t newLimit) {
660     return SkResourceCache::SetSingleAllocationByteLimit(newLimit);
661 }
662 
PurgeResourceCache()663 void SkGraphics::PurgeResourceCache() {
664     SkImageFilter::PurgeCache();
665     return SkResourceCache::PurgeAll();
666 }
667 
668 /////////////
669 
dump_visitor(const SkResourceCache::Rec & rec,void *)670 static void dump_visitor(const SkResourceCache::Rec& rec, void*) {
671     SkDebugf("RC: %12s bytes %9lu  discardable %p\n",
672              rec.getCategory(), rec.bytesUsed(), rec.diagnostic_only_getDiscardable());
673 }
674 
TestDumpMemoryStatistics()675 void SkResourceCache::TestDumpMemoryStatistics() {
676     VisitAll(dump_visitor, nullptr);
677 }
678 
sk_trace_dump_visitor(const SkResourceCache::Rec & rec,void * context)679 static void sk_trace_dump_visitor(const SkResourceCache::Rec& rec, void* context) {
680     SkTraceMemoryDump* dump = static_cast<SkTraceMemoryDump*>(context);
681     SkString dumpName = SkStringPrintf("skia/sk_resource_cache/%s_%p", rec.getCategory(), &rec);
682     SkDiscardableMemory* discardable = rec.diagnostic_only_getDiscardable();
683     if (discardable) {
684         dump->setDiscardableMemoryBacking(dumpName.c_str(), *discardable);
685 
686         // The discardable memory size will be calculated by dumper, but we also dump what we think
687         // the size of object in memory is irrespective of whether object is live or dead.
688         dump->dumpNumericValue(dumpName.c_str(), "discardable_size", "bytes", rec.bytesUsed());
689     } else {
690         dump->dumpNumericValue(dumpName.c_str(), "size", "bytes", rec.bytesUsed());
691         dump->setMemoryBacking(dumpName.c_str(), "malloc", nullptr);
692     }
693 }
694 
DumpMemoryStatistics(SkTraceMemoryDump * dump)695 void SkResourceCache::DumpMemoryStatistics(SkTraceMemoryDump* dump) {
696     // Since resource could be backed by malloc or discardable, the cache always dumps detailed
697     // stats to be accurate.
698     VisitAll(sk_trace_dump_visitor, dump);
699 }
700