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
190bool 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
204void 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
220void 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
233void 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
257size_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
268void 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
288void 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
307void 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
328void 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
372void SkResourceCache::dump() const {
373    this->validate();
374
375    SkDebugf("SkResourceCache: count=%d bytes=%d %s\n",
376             fCount, fTotalBytesUsed, fDiscardableFactory ? "discardable" : "malloc");
377}
378
379size_t SkResourceCache::setSingleAllocationByteLimit(size_t newLimit) {
380    size_t oldLimit = fSingleAllocationByteLimit;
381    fSingleAllocationByteLimit = newLimit;
382    return oldLimit;
383}
384
385size_t SkResourceCache::getSingleAllocationByteLimit() const {
386    return fSingleAllocationByteLimit;
387}
388
389///////////////////////////////////////////////////////////////////////////////
390
391#include "SkThread.h"
392
393SK_DECLARE_STATIC_MUTEX(gMutex);
394static SkResourceCache* gResourceCache = NULL;
395static 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. */
406static 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
420size_t SkResourceCache::GetTotalBytesUsed() {
421    SkAutoMutexAcquire am(gMutex);
422    return get_cache()->getTotalBytesUsed();
423}
424
425size_t SkResourceCache::GetTotalByteLimit() {
426    SkAutoMutexAcquire am(gMutex);
427    return get_cache()->getTotalByteLimit();
428}
429
430size_t SkResourceCache::SetTotalByteLimit(size_t newLimit) {
431    SkAutoMutexAcquire am(gMutex);
432    return get_cache()->setTotalByteLimit(newLimit);
433}
434
435SkResourceCache::DiscardableFactory SkResourceCache::GetDiscardableFactory() {
436    SkAutoMutexAcquire am(gMutex);
437    return get_cache()->discardableFactory();
438}
439
440SkBitmap::Allocator* SkResourceCache::GetAllocator() {
441    SkAutoMutexAcquire am(gMutex);
442    return get_cache()->allocator();
443}
444
445void SkResourceCache::Dump() {
446    SkAutoMutexAcquire am(gMutex);
447    get_cache()->dump();
448}
449
450size_t SkResourceCache::SetSingleAllocationByteLimit(size_t size) {
451    SkAutoMutexAcquire am(gMutex);
452    return get_cache()->setSingleAllocationByteLimit(size);
453}
454
455size_t SkResourceCache::GetSingleAllocationByteLimit() {
456    SkAutoMutexAcquire am(gMutex);
457    return get_cache()->getSingleAllocationByteLimit();
458}
459
460void SkResourceCache::PurgeAll() {
461    SkAutoMutexAcquire am(gMutex);
462    return get_cache()->purgeAll();
463}
464
465bool SkResourceCache::Find(const Key& key, VisitorProc visitor, void* context) {
466    SkAutoMutexAcquire am(gMutex);
467    return get_cache()->find(key, visitor, context);
468}
469
470void SkResourceCache::Add(Rec* rec) {
471    SkAutoMutexAcquire am(gMutex);
472    get_cache()->add(rec);
473}
474
475///////////////////////////////////////////////////////////////////////////////
476
477#include "SkGraphics.h"
478
479size_t SkGraphics::GetResourceCacheTotalBytesUsed() {
480    return SkResourceCache::GetTotalBytesUsed();
481}
482
483size_t SkGraphics::GetResourceCacheTotalByteLimit() {
484    return SkResourceCache::GetTotalByteLimit();
485}
486
487size_t SkGraphics::SetResourceCacheTotalByteLimit(size_t newLimit) {
488    return SkResourceCache::SetTotalByteLimit(newLimit);
489}
490
491size_t SkGraphics::GetResourceCacheSingleAllocationByteLimit() {
492    return SkResourceCache::GetSingleAllocationByteLimit();
493}
494
495size_t SkGraphics::SetResourceCacheSingleAllocationByteLimit(size_t newLimit) {
496    return SkResourceCache::SetSingleAllocationByteLimit(newLimit);
497}
498
499void SkGraphics::PurgeResourceCache() {
500    return SkResourceCache::PurgeAll();
501}
502
503