2 * Copyright 2013 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "SkChecksum.h"
9 #include "SkResourceCache.h"
11 #include "SkPixelRef.h"
15 // This can be defined by the caller's build system
16 //#define SK_USE_DISCARDABLE_SCALEDIMAGECACHE
18 #ifndef SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT
19 # define SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT 1024
22 #ifndef SK_DEFAULT_IMAGE_CACHE_LIMIT
23 #define SK_DEFAULT_IMAGE_CACHE_LIMIT (2 * 1024 * 1024)
26 void SkResourceCache::Key::init(void* nameSpace, size_t length) {
27 SkASSERT(SkAlign4(length) == length);
29 // fCount32 and fHash are not hashed
30 static const int kUnhashedLocal32s = 2;
31 static const int kLocal32s = kUnhashedLocal32s + (sizeof(fNamespace) >> 2);
33 SK_COMPILE_ASSERT(sizeof(Key) == (kLocal32s << 2), unaccounted_key_locals);
34 SK_COMPILE_ASSERT(sizeof(Key) == offsetof(Key, fNamespace) + sizeof(fNamespace),
35 namespace_field_must_be_last);
37 fCount32 = SkToS32(kLocal32s + (length >> 2));
38 fNamespace = nameSpace;
39 // skip unhashed fields when computing the murmur
40 fHash = SkChecksum::Murmur3(this->as32() + kUnhashedLocal32s,
41 (fCount32 - kUnhashedLocal32s) << 2);
44 #include "SkTDynamicHash.h"
46 class SkResourceCache::Hash :
47 public SkTDynamicHash<SkResourceCache::Rec, SkResourceCache::Key> {};
50 ///////////////////////////////////////////////////////////////////////////////
52 void SkResourceCache::init() {
58 fSingleAllocationByteLimit = 0;
61 // One of these should be explicit set by the caller after we return.
63 fDiscardableFactory = NULL;
66 #include "SkDiscardableMemory.h"
68 class SkOneShotDiscardablePixelRef : public SkPixelRef {
70 SK_DECLARE_INST_COUNT(SkOneShotDiscardablePixelRef)
71 // Ownership of the discardablememory is transfered to the pixelref
72 SkOneShotDiscardablePixelRef(const SkImageInfo&, SkDiscardableMemory*, size_t rowBytes);
73 ~SkOneShotDiscardablePixelRef();
76 virtual bool onNewLockPixels(LockRec*) SK_OVERRIDE;
77 virtual void onUnlockPixels() SK_OVERRIDE;
78 virtual size_t getAllocatedSizeInBytes() const SK_OVERRIDE;
81 SkDiscardableMemory* fDM;
85 typedef SkPixelRef INHERITED;
88 SkOneShotDiscardablePixelRef::SkOneShotDiscardablePixelRef(const SkImageInfo& info,
89 SkDiscardableMemory* dm,
99 SkOneShotDiscardablePixelRef::~SkOneShotDiscardablePixelRef() {
103 bool SkOneShotDiscardablePixelRef::onNewLockPixels(LockRec* rec) {
105 // we're already locked
106 SkASSERT(fDM->data());
111 // A previous call to onUnlock may have deleted our DM, so check for that
117 // since it failed, we delete it now, to free-up the resource
124 rec->fPixels = fDM->data();
125 rec->fColorTable = NULL;
126 rec->fRowBytes = fRB;
130 void SkOneShotDiscardablePixelRef::onUnlockPixels() {
131 SkASSERT(!fFirstTime);
135 size_t SkOneShotDiscardablePixelRef::getAllocatedSizeInBytes() const {
136 return this->info().getSafeSize(fRB);
139 class SkResourceCacheDiscardableAllocator : public SkBitmap::Allocator {
141 SkResourceCacheDiscardableAllocator(SkResourceCache::DiscardableFactory factory) {
146 virtual bool allocPixelRef(SkBitmap*, SkColorTable*) SK_OVERRIDE;
149 SkResourceCache::DiscardableFactory fFactory;
152 bool SkResourceCacheDiscardableAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
153 size_t size = bitmap->getSize();
154 uint64_t size64 = bitmap->computeSize64();
155 if (0 == size || size64 > (uint64_t)size) {
159 SkDiscardableMemory* dm = fFactory(size);
164 // can we relax this?
165 if (kN32_SkColorType != bitmap->colorType()) {
169 SkImageInfo info = bitmap->info();
170 bitmap->setPixelRef(SkNEW_ARGS(SkOneShotDiscardablePixelRef,
171 (info, dm, bitmap->rowBytes())))->unref();
172 bitmap->lockPixels();
173 return bitmap->readyToDraw();
176 SkResourceCache::SkResourceCache(DiscardableFactory factory) {
178 fDiscardableFactory = factory;
180 fAllocator = SkNEW_ARGS(SkResourceCacheDiscardableAllocator, (factory));
183 SkResourceCache::SkResourceCache(size_t byteLimit) {
185 fTotalByteLimit = byteLimit;
188 SkResourceCache::~SkResourceCache() {
189 SkSafeUnref(fAllocator);
193 Rec* next = rec->fNext;
200 ////////////////////////////////////////////////////////////////////////////////
202 bool SkResourceCache::find(const Key& key, VisitorProc visitor, void* context) {
203 Rec* rec = fHash->find(key);
205 if (visitor(*rec, context)) {
206 this->moveToHead(rec); // for our LRU
209 this->remove(rec); // stale
216 static void make_size_str(size_t size, SkString* str) {
217 const char suffix[] = { 'b', 'k', 'm', 'g', 't', 0 };
219 while (suffix[i] && (size > 1024)) {
223 str->printf("%zu%c", size, suffix[i]);
226 static bool gDumpCacheTransactions;
228 void SkResourceCache::add(Rec* rec) {
230 // See if we already have this key (racy inserts, etc.)
231 Rec* existing = fHash->find(rec->getKey());
237 this->addToHead(rec);
240 if (gDumpCacheTransactions) {
241 SkString bytesStr, totalStr;
242 make_size_str(rec->bytesUsed(), &bytesStr);
243 make_size_str(fTotalBytesUsed, &totalStr);
244 SkDebugf("RC: add %5s %12p key %08x -- total %5s, count %d\n",
245 bytesStr.c_str(), rec, rec->getHash(), totalStr.c_str(), fCount);
248 // since the new rec may push us over-budget, we perform a purge check now
249 this->purgeAsNeeded();
252 void SkResourceCache::remove(Rec* rec) {
253 size_t used = rec->bytesUsed();
254 SkASSERT(used <= fTotalBytesUsed);
257 fHash->remove(rec->getKey());
259 fTotalBytesUsed -= used;
262 if (gDumpCacheTransactions) {
263 SkString bytesStr, totalStr;
264 make_size_str(used, &bytesStr);
265 make_size_str(fTotalBytesUsed, &totalStr);
266 SkDebugf("RC: remove %5s %12p key %08x -- total %5s, count %d\n",
267 bytesStr.c_str(), rec, rec->getHash(), totalStr.c_str(), fCount);
273 void SkResourceCache::purgeAsNeeded(bool forcePurge) {
277 if (fDiscardableFactory) {
278 countLimit = SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT;
279 byteLimit = SK_MaxU32; // no limit based on bytes
281 countLimit = SK_MaxS32; // no limit based on count
282 byteLimit = fTotalByteLimit;
287 if (!forcePurge && fTotalBytesUsed < byteLimit && fCount < countLimit) {
291 Rec* prev = rec->fPrev;
297 size_t SkResourceCache::setTotalByteLimit(size_t newLimit) {
298 size_t prevLimit = fTotalByteLimit;
299 fTotalByteLimit = newLimit;
300 if (newLimit < prevLimit) {
301 this->purgeAsNeeded();
306 SkCachedData* SkResourceCache::newCachedData(size_t bytes) {
307 if (fDiscardableFactory) {
308 SkDiscardableMemory* dm = fDiscardableFactory(bytes);
309 return dm ? SkNEW_ARGS(SkCachedData, (bytes, dm)) : NULL;
311 return SkNEW_ARGS(SkCachedData, (sk_malloc_throw(bytes), bytes));
315 ///////////////////////////////////////////////////////////////////////////////
317 void SkResourceCache::detach(Rec* rec) {
318 Rec* prev = rec->fPrev;
319 Rec* next = rec->fNext;
322 SkASSERT(fHead == rec);
334 rec->fNext = rec->fPrev = NULL;
337 void SkResourceCache::moveToHead(Rec* rec) {
356 void SkResourceCache::addToHead(Rec* rec) {
368 fTotalBytesUsed += rec->bytesUsed();
374 ///////////////////////////////////////////////////////////////////////////////
377 void SkResourceCache::validate() const {
379 SkASSERT(NULL == fTail);
380 SkASSERT(0 == fTotalBytesUsed);
384 if (fHead == fTail) {
385 SkASSERT(NULL == fHead->fPrev);
386 SkASSERT(NULL == fHead->fNext);
387 SkASSERT(fHead->bytesUsed() == fTotalBytesUsed);
391 SkASSERT(NULL == fHead->fPrev);
392 SkASSERT(fHead->fNext);
393 SkASSERT(NULL == fTail->fNext);
394 SkASSERT(fTail->fPrev);
398 const Rec* rec = fHead;
401 used += rec->bytesUsed();
402 SkASSERT(used <= fTotalBytesUsed);
405 SkASSERT(fCount == count);
411 SkASSERT(used >= rec->bytesUsed());
412 used -= rec->bytesUsed();
416 SkASSERT(0 == count);
421 void SkResourceCache::dump() const {
424 SkDebugf("SkResourceCache: count=%d bytes=%d %s\n",
425 fCount, fTotalBytesUsed, fDiscardableFactory ? "discardable" : "malloc");
428 size_t SkResourceCache::setSingleAllocationByteLimit(size_t newLimit) {
429 size_t oldLimit = fSingleAllocationByteLimit;
430 fSingleAllocationByteLimit = newLimit;
434 size_t SkResourceCache::getSingleAllocationByteLimit() const {
435 return fSingleAllocationByteLimit;
438 ///////////////////////////////////////////////////////////////////////////////
440 #include "SkThread.h"
442 SK_DECLARE_STATIC_MUTEX(gMutex);
443 static SkResourceCache* gResourceCache = NULL;
444 static void cleanup_gResourceCache() {
445 // We'll clean this up in our own tests, but disable for clients.
446 // Chrome seems to have funky multi-process things going on in unit tests that
447 // makes this unsafe to delete when the main process atexit()s.
448 // SkLazyPtr does the same sort of thing.
450 SkDELETE(gResourceCache);
454 /** Must hold gMutex when calling. */
455 static SkResourceCache* get_cache() {
456 // gMutex is always held when this is called, so we don't need to be fancy in here.
458 if (NULL == gResourceCache) {
459 #ifdef SK_USE_DISCARDABLE_SCALEDIMAGECACHE
460 gResourceCache = SkNEW_ARGS(SkResourceCache, (SkDiscardableMemory::Create));
462 gResourceCache = SkNEW_ARGS(SkResourceCache, (SK_DEFAULT_IMAGE_CACHE_LIMIT));
464 atexit(cleanup_gResourceCache);
466 return gResourceCache;
469 size_t SkResourceCache::GetTotalBytesUsed() {
470 SkAutoMutexAcquire am(gMutex);
471 return get_cache()->getTotalBytesUsed();
474 size_t SkResourceCache::GetTotalByteLimit() {
475 SkAutoMutexAcquire am(gMutex);
476 return get_cache()->getTotalByteLimit();
479 size_t SkResourceCache::SetTotalByteLimit(size_t newLimit) {
480 SkAutoMutexAcquire am(gMutex);
481 return get_cache()->setTotalByteLimit(newLimit);
484 SkResourceCache::DiscardableFactory SkResourceCache::GetDiscardableFactory() {
485 SkAutoMutexAcquire am(gMutex);
486 return get_cache()->discardableFactory();
489 SkBitmap::Allocator* SkResourceCache::GetAllocator() {
490 SkAutoMutexAcquire am(gMutex);
491 return get_cache()->allocator();
494 SkCachedData* SkResourceCache::NewCachedData(size_t bytes) {
495 SkAutoMutexAcquire am(gMutex);
496 return get_cache()->newCachedData(bytes);
499 void SkResourceCache::Dump() {
500 SkAutoMutexAcquire am(gMutex);
504 size_t SkResourceCache::SetSingleAllocationByteLimit(size_t size) {
505 SkAutoMutexAcquire am(gMutex);
506 return get_cache()->setSingleAllocationByteLimit(size);
509 size_t SkResourceCache::GetSingleAllocationByteLimit() {
510 SkAutoMutexAcquire am(gMutex);
511 return get_cache()->getSingleAllocationByteLimit();
514 void SkResourceCache::PurgeAll() {
515 SkAutoMutexAcquire am(gMutex);
516 return get_cache()->purgeAll();
519 bool SkResourceCache::Find(const Key& key, VisitorProc visitor, void* context) {
520 SkAutoMutexAcquire am(gMutex);
521 return get_cache()->find(key, visitor, context);
524 void SkResourceCache::Add(Rec* rec) {
525 SkAutoMutexAcquire am(gMutex);
526 get_cache()->add(rec);
529 ///////////////////////////////////////////////////////////////////////////////
531 #include "SkGraphics.h"
533 size_t SkGraphics::GetResourceCacheTotalBytesUsed() {
534 return SkResourceCache::GetTotalBytesUsed();
537 size_t SkGraphics::GetResourceCacheTotalByteLimit() {
538 return SkResourceCache::GetTotalByteLimit();
541 size_t SkGraphics::SetResourceCacheTotalByteLimit(size_t newLimit) {
542 return SkResourceCache::SetTotalByteLimit(newLimit);
545 size_t SkGraphics::GetResourceCacheSingleAllocationByteLimit() {
546 return SkResourceCache::GetSingleAllocationByteLimit();
549 size_t SkGraphics::SetResourceCacheSingleAllocationByteLimit(size_t newLimit) {
550 return SkResourceCache::SetSingleAllocationByteLimit(newLimit);
553 void SkGraphics::PurgeResourceCache() {
554 return SkResourceCache::PurgeAll();