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 "SkResourceCache.h"
     10 #include "SkMipMap.h"
     11 #include "SkPixelRef.h"
     12 
     13 // This can be defined by the caller's build system
     14 //#define SK_USE_DISCARDABLE_SCALEDIMAGECACHE
     15 
     16 #ifndef SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT
     17 #   define SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT   1024
     18 #endif
     19 
     20 #ifndef SK_DEFAULT_IMAGE_CACHE_LIMIT
     21     #define SK_DEFAULT_IMAGE_CACHE_LIMIT     (2 * 1024 * 1024)
     22 #endif
     23 
     24 void SkResourceCache::Key::init(size_t length) {
     25     SkASSERT(SkAlign4(length) == length);
     26     // 2 is fCount32 and fHash
     27     fCount32 = SkToS32(2 + (length >> 2));
     28     // skip both of our fields whe computing the murmur
     29     fHash = SkChecksum::Murmur3(this->as32() + 2, (fCount32 - 2) << 2);
     30 }
     31 
     32 #include "SkTDynamicHash.h"
     33 
     34 class SkResourceCache::Hash :
     35     public SkTDynamicHash<SkResourceCache::Rec, SkResourceCache::Key> {};
     36 
     37 
     38 ///////////////////////////////////////////////////////////////////////////////
     39 
     40 void SkResourceCache::init() {
     41     fHead = NULL;
     42     fTail = NULL;
     43     fHash = new Hash;
     44     fTotalBytesUsed = 0;
     45     fCount = 0;
     46     fSingleAllocationByteLimit = 0;
     47     fAllocator = NULL;
     48 
     49     // One of these should be explicit set by the caller after we return.
     50     fTotalByteLimit = 0;
     51     fDiscardableFactory = NULL;
     52 }
     53 
     54 #include "SkDiscardableMemory.h"
     55 
     56 class SkOneShotDiscardablePixelRef : public SkPixelRef {
     57 public:
     58     SK_DECLARE_INST_COUNT(SkOneShotDiscardablePixelRef)
     59     // Ownership of the discardablememory is transfered to the pixelref
     60     SkOneShotDiscardablePixelRef(const SkImageInfo&, SkDiscardableMemory*, size_t rowBytes);
     61     ~SkOneShotDiscardablePixelRef();
     62 
     63 protected:
     64     virtual bool onNewLockPixels(LockRec*) SK_OVERRIDE;
     65     virtual void onUnlockPixels() SK_OVERRIDE;
     66     virtual size_t getAllocatedSizeInBytes() const SK_OVERRIDE;
     67 
     68 private:
     69     SkDiscardableMemory* fDM;
     70     size_t               fRB;
     71     bool                 fFirstTime;
     72 
     73     typedef SkPixelRef INHERITED;
     74 };
     75 
     76 SkOneShotDiscardablePixelRef::SkOneShotDiscardablePixelRef(const SkImageInfo& info,
     77                                              SkDiscardableMemory* dm,
     78                                              size_t rowBytes)
     79     : INHERITED(info)
     80     , fDM(dm)
     81     , fRB(rowBytes)
     82 {
     83     SkASSERT(dm->data());
     84     fFirstTime = true;
     85 }
     86 
     87 SkOneShotDiscardablePixelRef::~SkOneShotDiscardablePixelRef() {
     88     SkDELETE(fDM);
     89 }
     90 
     91 bool SkOneShotDiscardablePixelRef::onNewLockPixels(LockRec* rec) {
     92     if (fFirstTime) {
     93         // we're already locked
     94         SkASSERT(fDM->data());
     95         fFirstTime = false;
     96         goto SUCCESS;
     97     }
     98 
     99     // A previous call to onUnlock may have deleted our DM, so check for that
    100     if (NULL == fDM) {
    101         return false;
    102     }
    103 
    104     if (!fDM->lock()) {
    105         // since it failed, we delete it now, to free-up the resource
    106         delete fDM;
    107         fDM = NULL;
    108         return false;
    109     }
    110 
    111 SUCCESS:
    112     rec->fPixels = fDM->data();
    113     rec->fColorTable = NULL;
    114     rec->fRowBytes = fRB;
    115     return true;
    116 }
    117 
    118 void SkOneShotDiscardablePixelRef::onUnlockPixels() {
    119     SkASSERT(!fFirstTime);
    120     fDM->unlock();
    121 }
    122 
    123 size_t SkOneShotDiscardablePixelRef::getAllocatedSizeInBytes() const {
    124     return this->info().getSafeSize(fRB);
    125 }
    126 
    127 class SkResourceCacheDiscardableAllocator : public SkBitmap::Allocator {
    128 public:
    129     SkResourceCacheDiscardableAllocator(SkResourceCache::DiscardableFactory factory) {
    130         SkASSERT(factory);
    131         fFactory = factory;
    132     }
    133 
    134     virtual bool allocPixelRef(SkBitmap*, SkColorTable*) SK_OVERRIDE;
    135 
    136 private:
    137     SkResourceCache::DiscardableFactory fFactory;
    138 };
    139 
    140 bool SkResourceCacheDiscardableAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
    141     size_t size = bitmap->getSize();
    142     uint64_t size64 = bitmap->computeSize64();
    143     if (0 == size || size64 > (uint64_t)size) {
    144         return false;
    145     }
    146 
    147     SkDiscardableMemory* dm = fFactory(size);
    148     if (NULL == dm) {
    149         return false;
    150     }
    151 
    152     // can we relax this?
    153     if (kN32_SkColorType != bitmap->colorType()) {
    154         return false;
    155     }
    156 
    157     SkImageInfo info = bitmap->info();
    158     bitmap->setPixelRef(SkNEW_ARGS(SkOneShotDiscardablePixelRef,
    159                                    (info, dm, bitmap->rowBytes())))->unref();
    160     bitmap->lockPixels();
    161     return bitmap->readyToDraw();
    162 }
    163 
    164 SkResourceCache::SkResourceCache(DiscardableFactory factory) {
    165     this->init();
    166     fDiscardableFactory = factory;
    167 
    168     fAllocator = SkNEW_ARGS(SkResourceCacheDiscardableAllocator, (factory));
    169 }
    170 
    171 SkResourceCache::SkResourceCache(size_t byteLimit) {
    172     this->init();
    173     fTotalByteLimit = byteLimit;
    174 }
    175 
    176 SkResourceCache::~SkResourceCache() {
    177     SkSafeUnref(fAllocator);
    178 
    179     Rec* rec = fHead;
    180     while (rec) {
    181         Rec* next = rec->fNext;
    182         SkDELETE(rec);
    183         rec = next;
    184     }
    185     delete fHash;
    186 }
    187 
    188 ////////////////////////////////////////////////////////////////////////////////
    189 
    190 bool SkResourceCache::find(const Key& key, VisitorProc visitor, void* context) {
    191     Rec* rec = fHash->find(key);
    192     if (rec) {
    193         if (visitor(*rec, context)) {
    194             this->moveToHead(rec);  // for our LRU
    195             return true;
    196         } else {
    197             this->remove(rec);  // stale
    198             return false;
    199         }
    200     }
    201     return false;
    202 }
    203 
    204 void SkResourceCache::add(Rec* rec) {
    205     SkASSERT(rec);
    206     // See if we already have this key (racy inserts, etc.)
    207     Rec* existing = fHash->find(rec->getKey());
    208     if (existing) {
    209         SkDELETE(rec);
    210         return;
    211     }
    212 
    213     this->addToHead(rec);
    214     fHash->add(rec);
    215 
    216     // since the new rec may push us over-budget, we perform a purge check now
    217     this->purgeAsNeeded();
    218 }
    219 
    220 void SkResourceCache::remove(Rec* rec) {
    221     size_t used = rec->bytesUsed();
    222     SkASSERT(used <= fTotalBytesUsed);
    223 
    224     this->detach(rec);
    225     fHash->remove(rec->getKey());
    226 
    227     SkDELETE(rec);
    228 
    229     fTotalBytesUsed -= used;
    230     fCount -= 1;
    231 }
    232 
    233 void SkResourceCache::purgeAsNeeded(bool forcePurge) {
    234     size_t byteLimit;
    235     int    countLimit;
    236 
    237     if (fDiscardableFactory) {
    238         countLimit = SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT;
    239         byteLimit = SK_MaxU32;  // no limit based on bytes
    240     } else {
    241         countLimit = SK_MaxS32; // no limit based on count
    242         byteLimit = fTotalByteLimit;
    243     }
    244 
    245     Rec* rec = fTail;
    246     while (rec) {
    247         if (!forcePurge && fTotalBytesUsed < byteLimit && fCount < countLimit) {
    248             break;
    249         }
    250 
    251         Rec* prev = rec->fPrev;
    252         this->remove(rec);
    253         rec = prev;
    254     }
    255 }
    256 
    257 size_t SkResourceCache::setTotalByteLimit(size_t newLimit) {
    258     size_t prevLimit = fTotalByteLimit;
    259     fTotalByteLimit = newLimit;
    260     if (newLimit < prevLimit) {
    261         this->purgeAsNeeded();
    262     }
    263     return prevLimit;
    264 }
    265 
    266 ///////////////////////////////////////////////////////////////////////////////
    267 
    268 void SkResourceCache::detach(Rec* rec) {
    269     Rec* prev = rec->fPrev;
    270     Rec* next = rec->fNext;
    271 
    272     if (!prev) {
    273         SkASSERT(fHead == rec);
    274         fHead = next;
    275     } else {
    276         prev->fNext = next;
    277     }
    278 
    279     if (!next) {
    280         fTail = prev;
    281     } else {
    282         next->fPrev = prev;
    283     }
    284 
    285     rec->fNext = rec->fPrev = NULL;
    286 }
    287 
    288 void SkResourceCache::moveToHead(Rec* rec) {
    289     if (fHead == rec) {
    290         return;
    291     }
    292 
    293     SkASSERT(fHead);
    294     SkASSERT(fTail);
    295 
    296     this->validate();
    297 
    298     this->detach(rec);
    299 
    300     fHead->fPrev = rec;
    301     rec->fNext = fHead;
    302     fHead = rec;
    303 
    304     this->validate();
    305 }
    306 
    307 void SkResourceCache::addToHead(Rec* rec) {
    308     this->validate();
    309 
    310     rec->fPrev = NULL;
    311     rec->fNext = fHead;
    312     if (fHead) {
    313         fHead->fPrev = rec;
    314     }
    315     fHead = rec;
    316     if (!fTail) {
    317         fTail = rec;
    318     }
    319     fTotalBytesUsed += rec->bytesUsed();
    320     fCount += 1;
    321 
    322     this->validate();
    323 }
    324 
    325 ///////////////////////////////////////////////////////////////////////////////
    326 
    327 #ifdef SK_DEBUG
    328 void SkResourceCache::validate() const {
    329     if (NULL == fHead) {
    330         SkASSERT(NULL == fTail);
    331         SkASSERT(0 == fTotalBytesUsed);
    332         return;
    333     }
    334 
    335     if (fHead == fTail) {
    336         SkASSERT(NULL == fHead->fPrev);
    337         SkASSERT(NULL == fHead->fNext);
    338         SkASSERT(fHead->bytesUsed() == fTotalBytesUsed);
    339         return;
    340     }
    341 
    342     SkASSERT(NULL == fHead->fPrev);
    343     SkASSERT(fHead->fNext);
    344     SkASSERT(NULL == fTail->fNext);
    345     SkASSERT(fTail->fPrev);
    346 
    347     size_t used = 0;
    348     int count = 0;
    349     const Rec* rec = fHead;
    350     while (rec) {
    351         count += 1;
    352         used += rec->bytesUsed();
    353         SkASSERT(used <= fTotalBytesUsed);
    354         rec = rec->fNext;
    355     }
    356     SkASSERT(fCount == count);
    357 
    358     rec = fTail;
    359     while (rec) {
    360         SkASSERT(count > 0);
    361         count -= 1;
    362         SkASSERT(used >= rec->bytesUsed());
    363         used -= rec->bytesUsed();
    364         rec = rec->fPrev;
    365     }
    366 
    367     SkASSERT(0 == count);
    368     SkASSERT(0 == used);
    369 }
    370 #endif
    371 
    372 void SkResourceCache::dump() const {
    373     this->validate();
    374 
    375     SkDebugf("SkResourceCache: count=%d bytes=%d %s\n",
    376              fCount, fTotalBytesUsed, fDiscardableFactory ? "discardable" : "malloc");
    377 }
    378 
    379 size_t SkResourceCache::setSingleAllocationByteLimit(size_t newLimit) {
    380     size_t oldLimit = fSingleAllocationByteLimit;
    381     fSingleAllocationByteLimit = newLimit;
    382     return oldLimit;
    383 }
    384 
    385 size_t SkResourceCache::getSingleAllocationByteLimit() const {
    386     return fSingleAllocationByteLimit;
    387 }
    388 
    389 ///////////////////////////////////////////////////////////////////////////////
    390 
    391 #include "SkThread.h"
    392 
    393 SK_DECLARE_STATIC_MUTEX(gMutex);
    394 static SkResourceCache* gResourceCache = NULL;
    395 static void cleanup_gResourceCache() {
    396     // We'll clean this up in our own tests, but disable for clients.
    397     // Chrome seems to have funky multi-process things going on in unit tests that
    398     // makes this unsafe to delete when the main process atexit()s.
    399     // SkLazyPtr does the same sort of thing.
    400 #if SK_DEVELOPER
    401     SkDELETE(gResourceCache);
    402 #endif
    403 }
    404 
    405 /** Must hold gMutex when calling. */
    406 static SkResourceCache* get_cache() {
    407     // gMutex is always held when this is called, so we don't need to be fancy in here.
    408     gMutex.assertHeld();
    409     if (NULL == gResourceCache) {
    410 #ifdef SK_USE_DISCARDABLE_SCALEDIMAGECACHE
    411         gResourceCache = SkNEW_ARGS(SkResourceCache, (SkDiscardableMemory::Create));
    412 #else
    413         gResourceCache = SkNEW_ARGS(SkResourceCache, (SK_DEFAULT_IMAGE_CACHE_LIMIT));
    414 #endif
    415         atexit(cleanup_gResourceCache);
    416     }
    417     return gResourceCache;
    418 }
    419 
    420 size_t SkResourceCache::GetTotalBytesUsed() {
    421     SkAutoMutexAcquire am(gMutex);
    422     return get_cache()->getTotalBytesUsed();
    423 }
    424 
    425 size_t SkResourceCache::GetTotalByteLimit() {
    426     SkAutoMutexAcquire am(gMutex);
    427     return get_cache()->getTotalByteLimit();
    428 }
    429 
    430 size_t SkResourceCache::SetTotalByteLimit(size_t newLimit) {
    431     SkAutoMutexAcquire am(gMutex);
    432     return get_cache()->setTotalByteLimit(newLimit);
    433 }
    434 
    435 SkResourceCache::DiscardableFactory SkResourceCache::GetDiscardableFactory() {
    436     SkAutoMutexAcquire am(gMutex);
    437     return get_cache()->discardableFactory();
    438 }
    439 
    440 SkBitmap::Allocator* SkResourceCache::GetAllocator() {
    441     SkAutoMutexAcquire am(gMutex);
    442     return get_cache()->allocator();
    443 }
    444 
    445 void SkResourceCache::Dump() {
    446     SkAutoMutexAcquire am(gMutex);
    447     get_cache()->dump();
    448 }
    449 
    450 size_t SkResourceCache::SetSingleAllocationByteLimit(size_t size) {
    451     SkAutoMutexAcquire am(gMutex);
    452     return get_cache()->setSingleAllocationByteLimit(size);
    453 }
    454 
    455 size_t SkResourceCache::GetSingleAllocationByteLimit() {
    456     SkAutoMutexAcquire am(gMutex);
    457     return get_cache()->getSingleAllocationByteLimit();
    458 }
    459 
    460 void SkResourceCache::PurgeAll() {
    461     SkAutoMutexAcquire am(gMutex);
    462     return get_cache()->purgeAll();
    463 }
    464 
    465 bool SkResourceCache::Find(const Key& key, VisitorProc visitor, void* context) {
    466     SkAutoMutexAcquire am(gMutex);
    467     return get_cache()->find(key, visitor, context);
    468 }
    469 
    470 void SkResourceCache::Add(Rec* rec) {
    471     SkAutoMutexAcquire am(gMutex);
    472     get_cache()->add(rec);
    473 }
    474 
    475 ///////////////////////////////////////////////////////////////////////////////
    476 
    477 #include "SkGraphics.h"
    478 
    479 size_t SkGraphics::GetResourceCacheTotalBytesUsed() {
    480     return SkResourceCache::GetTotalBytesUsed();
    481 }
    482 
    483 size_t SkGraphics::GetResourceCacheTotalByteLimit() {
    484     return SkResourceCache::GetTotalByteLimit();
    485 }
    486 
    487 size_t SkGraphics::SetResourceCacheTotalByteLimit(size_t newLimit) {
    488     return SkResourceCache::SetTotalByteLimit(newLimit);
    489 }
    490 
    491 size_t SkGraphics::GetResourceCacheSingleAllocationByteLimit() {
    492     return SkResourceCache::GetSingleAllocationByteLimit();
    493 }
    494 
    495 size_t SkGraphics::SetResourceCacheSingleAllocationByteLimit(size_t newLimit) {
    496     return SkResourceCache::SetSingleAllocationByteLimit(newLimit);
    497 }
    498 
    499 void SkGraphics::PurgeResourceCache() {
    500     return SkResourceCache::PurgeAll();
    501 }
    502 
    503