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