SkResourceCache.cpp revision e6cb48382db869f8e194b83aec80fd495be76db7
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
24void 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
34class SkResourceCache::Hash :
35    public SkTDynamicHash<SkResourceCache::Rec, SkResourceCache::Key> {};
36
37
38///////////////////////////////////////////////////////////////////////////////
39
40void 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
56class SkOneShotDiscardablePixelRef : public SkPixelRef {
57public:
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
63protected:
64    virtual bool onNewLockPixels(LockRec*) SK_OVERRIDE;
65    virtual void onUnlockPixels() SK_OVERRIDE;
66    virtual size_t getAllocatedSizeInBytes() const SK_OVERRIDE;
67
68private:
69    SkDiscardableMemory* fDM;
70    size_t               fRB;
71    bool                 fFirstTime;
72
73    typedef SkPixelRef INHERITED;
74};
75
76SkOneShotDiscardablePixelRef::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
87SkOneShotDiscardablePixelRef::~SkOneShotDiscardablePixelRef() {
88    SkDELETE(fDM);
89}
90
91bool 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
111SUCCESS:
112    rec->fPixels = fDM->data();
113    rec->fColorTable = NULL;
114    rec->fRowBytes = fRB;
115    return true;
116}
117
118void SkOneShotDiscardablePixelRef::onUnlockPixels() {
119    SkASSERT(!fFirstTime);
120    fDM->unlock();
121}
122
123size_t SkOneShotDiscardablePixelRef::getAllocatedSizeInBytes() const {
124    return this->info().getSafeSize(fRB);
125}
126
127class SkResourceCacheDiscardableAllocator : public SkBitmap::Allocator {
128public:
129    SkResourceCacheDiscardableAllocator(SkResourceCache::DiscardableFactory factory) {
130        SkASSERT(factory);
131        fFactory = factory;
132    }
133
134    virtual bool allocPixelRef(SkBitmap*, SkColorTable*) SK_OVERRIDE;
135
136private:
137    SkResourceCache::DiscardableFactory fFactory;
138};
139
140bool 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
164SkResourceCache::SkResourceCache(DiscardableFactory factory) {
165    this->init();
166    fDiscardableFactory = factory;
167
168    fAllocator = SkNEW_ARGS(SkResourceCacheDiscardableAllocator, (factory));
169}
170
171SkResourceCache::SkResourceCache(size_t byteLimit) {
172    this->init();
173    fTotalByteLimit = byteLimit;
174}
175
176SkResourceCache::~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
190const SkResourceCache::Rec* SkResourceCache::findAndLock(const Key& key) {
191    Rec* rec = fHash->find(key);
192    if (rec) {
193        this->moveToHead(rec);  // for our LRU
194        rec->fLockCount += 1;
195    }
196    return rec;
197}
198
199const SkResourceCache::Rec* SkResourceCache::addAndLock(Rec* rec) {
200    SkASSERT(rec);
201    // See if we already have this key (racy inserts, etc.)
202    const Rec* existing = this->findAndLock(rec->getKey());
203    if (existing) {
204        SkDELETE(rec);
205        return existing;
206    }
207
208    this->addToHead(rec);
209    SkASSERT(1 == rec->fLockCount);
210    fHash->add(rec);
211    // We may (now) be overbudget, so see if we need to purge something.
212    this->purgeAsNeeded();
213    return rec;
214}
215
216void SkResourceCache::add(Rec* rec) {
217    SkASSERT(rec);
218    // See if we already have this key (racy inserts, etc.)
219    const Rec* existing = this->findAndLock(rec->getKey());
220    if (existing) {
221        SkDELETE(rec);
222        this->unlock(existing);
223        return;
224    }
225
226    this->addToHead(rec);
227    SkASSERT(1 == rec->fLockCount);
228    fHash->add(rec);
229    this->unlock(rec);
230}
231
232void SkResourceCache::unlock(SkResourceCache::ID id) {
233    SkASSERT(id);
234
235#ifdef SK_DEBUG
236    {
237        bool found = false;
238        Rec* rec = fHead;
239        while (rec != NULL) {
240            if (rec == id) {
241                found = true;
242                break;
243            }
244            rec = rec->fNext;
245        }
246        SkASSERT(found);
247    }
248#endif
249    const Rec* rec = id;
250    SkASSERT(rec->fLockCount > 0);
251    // We're under our lock, and we're the only possible mutator, so unconsting is fine.
252    const_cast<Rec*>(rec)->fLockCount -= 1;
253
254    // we may have been over-budget, but now have released something, so check
255    // if we should purge.
256    if (0 == rec->fLockCount) {
257        this->purgeAsNeeded();
258    }
259}
260
261void SkResourceCache::remove(Rec* rec) {
262    SkASSERT(0 == rec->fLockCount);
263
264    size_t used = rec->bytesUsed();
265    SkASSERT(used <= fTotalBytesUsed);
266
267    this->detach(rec);
268    fHash->remove(rec->getKey());
269
270    SkDELETE(rec);
271
272    fTotalBytesUsed -= used;
273    fCount -= 1;
274}
275
276void SkResourceCache::purgeAsNeeded(bool forcePurge) {
277    size_t byteLimit;
278    int    countLimit;
279
280    if (fDiscardableFactory) {
281        countLimit = SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT;
282        byteLimit = SK_MaxU32;  // no limit based on bytes
283    } else {
284        countLimit = SK_MaxS32; // no limit based on count
285        byteLimit = fTotalByteLimit;
286    }
287
288    Rec* rec = fTail;
289    while (rec) {
290        if (!forcePurge && fTotalBytesUsed < byteLimit && fCount < countLimit) {
291            break;
292        }
293
294        Rec* prev = rec->fPrev;
295        if (0 == rec->fLockCount) {
296            this->remove(rec);
297        }
298        rec = prev;
299    }
300}
301
302size_t SkResourceCache::setTotalByteLimit(size_t newLimit) {
303    size_t prevLimit = fTotalByteLimit;
304    fTotalByteLimit = newLimit;
305    if (newLimit < prevLimit) {
306        this->purgeAsNeeded();
307    }
308    return prevLimit;
309}
310
311///////////////////////////////////////////////////////////////////////////////
312
313void SkResourceCache::detach(Rec* rec) {
314    Rec* prev = rec->fPrev;
315    Rec* next = rec->fNext;
316
317    if (!prev) {
318        SkASSERT(fHead == rec);
319        fHead = next;
320    } else {
321        prev->fNext = next;
322    }
323
324    if (!next) {
325        fTail = prev;
326    } else {
327        next->fPrev = prev;
328    }
329
330    rec->fNext = rec->fPrev = NULL;
331}
332
333void SkResourceCache::moveToHead(Rec* rec) {
334    if (fHead == rec) {
335        return;
336    }
337
338    SkASSERT(fHead);
339    SkASSERT(fTail);
340
341    this->validate();
342
343    this->detach(rec);
344
345    fHead->fPrev = rec;
346    rec->fNext = fHead;
347    fHead = rec;
348
349    this->validate();
350}
351
352void SkResourceCache::addToHead(Rec* rec) {
353    this->validate();
354
355    rec->fPrev = NULL;
356    rec->fNext = fHead;
357    if (fHead) {
358        fHead->fPrev = rec;
359    }
360    fHead = rec;
361    if (!fTail) {
362        fTail = rec;
363    }
364    fTotalBytesUsed += rec->bytesUsed();
365    fCount += 1;
366
367    this->validate();
368}
369
370///////////////////////////////////////////////////////////////////////////////
371
372#ifdef SK_DEBUG
373void SkResourceCache::validate() const {
374    if (NULL == fHead) {
375        SkASSERT(NULL == fTail);
376        SkASSERT(0 == fTotalBytesUsed);
377        return;
378    }
379
380    if (fHead == fTail) {
381        SkASSERT(NULL == fHead->fPrev);
382        SkASSERT(NULL == fHead->fNext);
383        SkASSERT(fHead->bytesUsed() == fTotalBytesUsed);
384        return;
385    }
386
387    SkASSERT(NULL == fHead->fPrev);
388    SkASSERT(fHead->fNext);
389    SkASSERT(NULL == fTail->fNext);
390    SkASSERT(fTail->fPrev);
391
392    size_t used = 0;
393    int count = 0;
394    const Rec* rec = fHead;
395    while (rec) {
396        count += 1;
397        used += rec->bytesUsed();
398        SkASSERT(used <= fTotalBytesUsed);
399        rec = rec->fNext;
400    }
401    SkASSERT(fCount == count);
402
403    rec = fTail;
404    while (rec) {
405        SkASSERT(count > 0);
406        count -= 1;
407        SkASSERT(used >= rec->bytesUsed());
408        used -= rec->bytesUsed();
409        rec = rec->fPrev;
410    }
411
412    SkASSERT(0 == count);
413    SkASSERT(0 == used);
414}
415#endif
416
417void SkResourceCache::dump() const {
418    this->validate();
419
420    const Rec* rec = fHead;
421    int locked = 0;
422    while (rec) {
423        locked += rec->fLockCount > 0;
424        rec = rec->fNext;
425    }
426
427    SkDebugf("SkResourceCache: count=%d bytes=%d locked=%d %s\n",
428             fCount, fTotalBytesUsed, locked,
429             fDiscardableFactory ? "discardable" : "malloc");
430}
431
432size_t SkResourceCache::setSingleAllocationByteLimit(size_t newLimit) {
433    size_t oldLimit = fSingleAllocationByteLimit;
434    fSingleAllocationByteLimit = newLimit;
435    return oldLimit;
436}
437
438size_t SkResourceCache::getSingleAllocationByteLimit() const {
439    return fSingleAllocationByteLimit;
440}
441
442///////////////////////////////////////////////////////////////////////////////
443
444#include "SkThread.h"
445
446SK_DECLARE_STATIC_MUTEX(gMutex);
447static SkResourceCache* gResourceCache = NULL;
448static void cleanup_gResourceCache() {
449    // We'll clean this up in our own tests, but disable for clients.
450    // Chrome seems to have funky multi-process things going on in unit tests that
451    // makes this unsafe to delete when the main process atexit()s.
452    // SkLazyPtr does the same sort of thing.
453#if SK_DEVELOPER
454    SkDELETE(gResourceCache);
455#endif
456}
457
458/** Must hold gMutex when calling. */
459static SkResourceCache* get_cache() {
460    // gMutex is always held when this is called, so we don't need to be fancy in here.
461    gMutex.assertHeld();
462    if (NULL == gResourceCache) {
463#ifdef SK_USE_DISCARDABLE_SCALEDIMAGECACHE
464        gResourceCache = SkNEW_ARGS(SkResourceCache, (SkDiscardableMemory::Create));
465#else
466        gResourceCache = SkNEW_ARGS(SkResourceCache, (SK_DEFAULT_IMAGE_CACHE_LIMIT));
467#endif
468        atexit(cleanup_gResourceCache);
469    }
470    return gResourceCache;
471}
472
473void SkResourceCache::Unlock(SkResourceCache::ID id) {
474    SkAutoMutexAcquire am(gMutex);
475    get_cache()->unlock(id);
476
477//    get_cache()->dump();
478}
479
480void SkResourceCache::Remove(SkResourceCache::ID id) {
481    SkAutoMutexAcquire am(gMutex);
482    SkASSERT(id);
483
484#ifdef SK_DEBUG
485    {
486        bool found = false;
487        Rec* rec = get_cache()->fHead;
488        while (rec != NULL) {
489            if (rec == id) {
490                found = true;
491                break;
492            }
493            rec = rec->fNext;
494        }
495        SkASSERT(found);
496    }
497#endif
498    const Rec* rec = id;
499    get_cache()->remove(const_cast<Rec*>(rec));
500}
501
502size_t SkResourceCache::GetTotalBytesUsed() {
503    SkAutoMutexAcquire am(gMutex);
504    return get_cache()->getTotalBytesUsed();
505}
506
507size_t SkResourceCache::GetTotalByteLimit() {
508    SkAutoMutexAcquire am(gMutex);
509    return get_cache()->getTotalByteLimit();
510}
511
512size_t SkResourceCache::SetTotalByteLimit(size_t newLimit) {
513    SkAutoMutexAcquire am(gMutex);
514    return get_cache()->setTotalByteLimit(newLimit);
515}
516
517SkBitmap::Allocator* SkResourceCache::GetAllocator() {
518    SkAutoMutexAcquire am(gMutex);
519    return get_cache()->allocator();
520}
521
522void SkResourceCache::Dump() {
523    SkAutoMutexAcquire am(gMutex);
524    get_cache()->dump();
525}
526
527size_t SkResourceCache::SetSingleAllocationByteLimit(size_t size) {
528    SkAutoMutexAcquire am(gMutex);
529    return get_cache()->setSingleAllocationByteLimit(size);
530}
531
532size_t SkResourceCache::GetSingleAllocationByteLimit() {
533    SkAutoMutexAcquire am(gMutex);
534    return get_cache()->getSingleAllocationByteLimit();
535}
536
537void SkResourceCache::PurgeAll() {
538    SkAutoMutexAcquire am(gMutex);
539    return get_cache()->purgeAll();
540}
541
542const SkResourceCache::Rec* SkResourceCache::FindAndLock(const Key& key) {
543    SkAutoMutexAcquire am(gMutex);
544    return get_cache()->findAndLock(key);
545}
546
547const SkResourceCache::Rec* SkResourceCache::AddAndLock(Rec* rec) {
548    SkAutoMutexAcquire am(gMutex);
549    return get_cache()->addAndLock(rec);
550}
551
552void SkResourceCache::Add(Rec* rec) {
553    SkAutoMutexAcquire am(gMutex);
554    get_cache()->add(rec);
555}
556
557///////////////////////////////////////////////////////////////////////////////
558
559#include "SkGraphics.h"
560
561size_t SkGraphics::GetResourceCacheTotalBytesUsed() {
562    return SkResourceCache::GetTotalBytesUsed();
563}
564
565size_t SkGraphics::GetResourceCacheTotalByteLimit() {
566    return SkResourceCache::GetTotalByteLimit();
567}
568
569size_t SkGraphics::SetResourceCacheTotalByteLimit(size_t newLimit) {
570    return SkResourceCache::SetTotalByteLimit(newLimit);
571}
572
573size_t SkGraphics::GetResourceCacheSingleAllocationByteLimit() {
574    return SkResourceCache::GetSingleAllocationByteLimit();
575}
576
577size_t SkGraphics::SetResourceCacheSingleAllocationByteLimit(size_t newLimit) {
578    return SkResourceCache::SetSingleAllocationByteLimit(newLimit);
579}
580
581void SkGraphics::PurgeResourceCache() {
582    return SkResourceCache::PurgeAll();
583}
584
585