SkScaledImageCache.cpp revision 2c86fbb0b14a1f674bf56ea5ad6a086cc004a76e
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 "SkScaledImageCache.h"
9#include "SkMipMap.h"
10#include "SkPixelRef.h"
11#include "SkRect.h"
12
13#ifndef SK_DEFAULT_IMAGE_CACHE_LIMIT
14    #define SK_DEFAULT_IMAGE_CACHE_LIMIT     (2 * 1024 * 1024)
15#endif
16
17
18 // Implemented from en.wikipedia.org/wiki/MurmurHash.
19static uint32_t compute_hash(const uint32_t data[], int count) {
20    uint32_t hash = 0;
21
22    for (int i = 0; i < count; ++i) {
23        uint32_t k = data[i];
24        k *= 0xcc9e2d51;
25        k = (k << 15) | (k >> 17);
26        k *= 0x1b873593;
27
28        hash ^= k;
29        hash = (hash << 13) | (hash >> 19);
30        hash *= 5;
31        hash += 0xe6546b64;
32    }
33
34    //    hash ^= size;
35    hash ^= hash >> 16;
36    hash *= 0x85ebca6b;
37    hash ^= hash >> 13;
38    hash *= 0xc2b2ae35;
39    hash ^= hash >> 16;
40
41    return hash;
42}
43
44struct Key {
45    bool init(const SkBitmap& bm, SkScalar scaleX, SkScalar scaleY) {
46        SkPixelRef* pr = bm.pixelRef();
47        if (!pr) {
48            return false;
49        }
50
51        size_t x, y;
52        SkTDivMod(bm.pixelRefOffset(), bm.rowBytes(), &y, &x);
53        x >>= 2;
54
55        fGenID = pr->getGenerationID();
56        fBounds.set(x, y, x + bm.width(), y + bm.height());
57        fScaleX = scaleX;
58        fScaleY = scaleY;
59
60        fHash = compute_hash(&fGenID, 7);
61        return true;
62    }
63
64    bool operator<(const Key& other) const {
65        const uint32_t* a = &fGenID;
66        const uint32_t* b = &other.fGenID;
67        for (int i = 0; i < 7; ++i) {
68            if (a[i] < b[i]) {
69                return true;
70            }
71            if (a[i] > b[i]) {
72                return false;
73            }
74        }
75        return false;
76    }
77
78    bool operator==(const Key& other) const {
79        const uint32_t* a = &fHash;
80        const uint32_t* b = &other.fHash;
81        for (int i = 0; i < 8; ++i) {
82            if (a[i] != b[i]) {
83                return false;
84            }
85        }
86        return true;
87    }
88
89    uint32_t    fHash;
90    uint32_t    fGenID;
91    float       fScaleX;
92    float       fScaleY;
93    SkIRect     fBounds;
94};
95
96struct SkScaledImageCache::Rec {
97    Rec(const Key& key, const SkBitmap& bm) : fKey(key), fBitmap(bm) {
98        fLockCount = 1;
99        fMip = NULL;
100    }
101
102    Rec(const Key& key, const SkMipMap* mip) : fKey(key) {
103        fLockCount = 1;
104        fMip = mip;
105        mip->ref();
106    }
107
108    ~Rec() {
109        SkSafeUnref(fMip);
110    }
111
112    size_t bytesUsed() const {
113        return fMip ? fMip->getSize() : fBitmap.getSize();
114    }
115
116    Rec*    fNext;
117    Rec*    fPrev;
118
119    // this guy wants to be 64bit aligned
120    Key     fKey;
121
122    int32_t fLockCount;
123
124    // we use either fBitmap or fMip, but not both
125    SkBitmap fBitmap;
126    const SkMipMap* fMip;
127};
128
129#include "SkTDynamicHash.h"
130
131namespace { // can't use static functions w/ template parameters
132const Key& key_from_rec(const SkScaledImageCache::Rec& rec) {
133    return rec.fKey;
134}
135
136uint32_t hash_from_key(const Key& key) {
137    return key.fHash;
138}
139
140bool eq_rec_key(const SkScaledImageCache::Rec& rec, const Key& key) {
141    return rec.fKey == key;
142}
143}
144
145class SkScaledImageCache::Hash : public SkTDynamicHash<SkScaledImageCache::Rec,
146                                   Key, key_from_rec, hash_from_key,
147                                   eq_rec_key> {};
148
149///////////////////////////////////////////////////////////////////////////////
150
151// experimental hash to speed things up
152#define USE_HASH
153
154SkScaledImageCache::SkScaledImageCache(size_t byteLimit) {
155    fHead = NULL;
156    fTail = NULL;
157#ifdef USE_HASH
158    fHash = new Hash;
159#else
160    fHash = NULL;
161#endif
162    fBytesUsed = 0;
163    fByteLimit = byteLimit;
164    fCount = 0;
165}
166
167SkScaledImageCache::~SkScaledImageCache() {
168    Rec* rec = fHead;
169    while (rec) {
170        Rec* next = rec->fNext;
171        SkDELETE(rec);
172        rec = next;
173    }
174    delete fHash;
175}
176
177SkScaledImageCache::Rec* SkScaledImageCache::findAndLock(const SkBitmap& orig,
178                                                        SkScalar scaleX,
179                                                        SkScalar scaleY) {
180    Key key;
181    if (!key.init(orig, scaleX, scaleY)) {
182        return NULL;
183    }
184
185#ifdef USE_HASH
186    Rec* rec = fHash->find(key);
187#else
188    Rec* rec = fHead;
189    while (rec != NULL) {
190        if (rec->fKey == key) {
191            break;
192        }
193        rec = rec->fNext;
194    }
195#endif
196
197    if (rec) {
198        this->moveToHead(rec);  // for our LRU
199        rec->fLockCount += 1;
200    }
201    return rec;
202}
203
204SkScaledImageCache::ID* SkScaledImageCache::findAndLock(const SkBitmap& orig,
205                                                        SkScalar scaleX,
206                                                        SkScalar scaleY,
207                                                        SkBitmap* scaled) {
208    if (0 == scaleX || 0 == scaleY) {
209        // degenerate, and the key we use for mipmaps
210        return NULL;
211    }
212
213    Rec* rec = this->findAndLock(orig, scaleX, scaleY);
214    if (rec) {
215        SkASSERT(NULL == rec->fMip);
216        SkASSERT(rec->fBitmap.pixelRef());
217        *scaled = rec->fBitmap;
218    }
219    return (ID*)rec;
220}
221
222SkScaledImageCache::ID* SkScaledImageCache::findAndLockMip(const SkBitmap& orig,
223                                                           SkMipMap const ** mip) {
224    Rec* rec = this->findAndLock(orig, 0, 0);
225    if (rec) {
226        SkASSERT(rec->fMip);
227        SkASSERT(NULL == rec->fBitmap.pixelRef());
228        *mip = rec->fMip;
229    }
230    return (ID*)rec;
231}
232
233SkScaledImageCache::ID* SkScaledImageCache::addAndLock(const SkBitmap& orig,
234                                                       SkScalar scaleX,
235                                                       SkScalar scaleY,
236                                                       const SkBitmap& scaled) {
237    if (0 == scaleX || 0 == scaleY) {
238        // degenerate, and the key we use for mipmaps
239        return NULL;
240    }
241
242    Key key;
243    if (!key.init(orig, scaleX, scaleY)) {
244        return NULL;
245    }
246
247    Rec* rec = SkNEW_ARGS(Rec, (key, scaled));
248    this->addToHead(rec);
249    SkASSERT(1 == rec->fLockCount);
250
251#ifdef USE_HASH
252    fHash->add(rec);
253#endif
254
255    // We may (now) be overbudget, so see if we need to purge something.
256    this->purgeAsNeeded();
257    return (ID*)rec;
258}
259
260SkScaledImageCache::ID* SkScaledImageCache::addAndLockMip(const SkBitmap& orig,
261                                                          const SkMipMap* mip) {
262    Key key;
263    if (!key.init(orig, 0, 0)) {
264        return NULL;
265    }
266
267    Rec* rec = SkNEW_ARGS(Rec, (key, mip));
268    this->addToHead(rec);
269    SkASSERT(1 == rec->fLockCount);
270
271#ifdef USE_HASH
272    fHash->add(rec);
273#endif
274
275    // We may (now) be overbudget, so see if we need to purge something.
276    this->purgeAsNeeded();
277    return (ID*)rec;
278}
279
280void SkScaledImageCache::unlock(SkScaledImageCache::ID* id) {
281    SkASSERT(id);
282
283#ifdef SK_DEBUG
284    {
285        bool found = false;
286        Rec* rec = fHead;
287        while (rec != NULL) {
288            if ((ID*)rec == id) {
289                found = true;
290                break;
291            }
292            rec = rec->fNext;
293        }
294        SkASSERT(found);
295    }
296#endif
297    Rec* rec = (Rec*)id;
298    SkASSERT(rec->fLockCount > 0);
299    rec->fLockCount -= 1;
300
301    // we may have been over-budget, but now have released something, so check
302    // if we should purge.
303    if (0 == rec->fLockCount) {
304        this->purgeAsNeeded();
305    }
306}
307
308void SkScaledImageCache::purgeAsNeeded() {
309    size_t byteLimit = fByteLimit;
310    size_t bytesUsed = fBytesUsed;
311
312    Rec* rec = fTail;
313    while (rec) {
314        if (bytesUsed < byteLimit) {
315            break;
316        }
317        Rec* prev = rec->fPrev;
318        if (0 == rec->fLockCount) {
319            size_t used = rec->bytesUsed();
320            SkASSERT(used <= bytesUsed);
321            bytesUsed -= used;
322            this->detach(rec);
323#ifdef USE_HASH
324            fHash->remove(rec->fKey);
325#endif
326
327            SkDELETE(rec);
328            fCount -= 1;
329        }
330        rec = prev;
331    }
332    fBytesUsed = bytesUsed;
333}
334
335size_t SkScaledImageCache::setByteLimit(size_t newLimit) {
336    size_t prevLimit = fByteLimit;
337    fByteLimit = newLimit;
338    if (newLimit < prevLimit) {
339        this->purgeAsNeeded();
340    }
341    return prevLimit;
342}
343
344///////////////////////////////////////////////////////////////////////////////
345
346void SkScaledImageCache::detach(Rec* rec) {
347    Rec* prev = rec->fPrev;
348    Rec* next = rec->fNext;
349
350    if (!prev) {
351        SkASSERT(fHead == rec);
352        fHead = next;
353    } else {
354        prev->fNext = next;
355    }
356
357    if (!next) {
358        fTail = prev;
359    } else {
360        next->fPrev = prev;
361    }
362
363    rec->fNext = rec->fPrev = NULL;
364}
365
366void SkScaledImageCache::moveToHead(Rec* rec) {
367    if (fHead == rec) {
368        return;
369    }
370
371    SkASSERT(fHead);
372    SkASSERT(fTail);
373
374    this->validate();
375
376    this->detach(rec);
377
378    fHead->fPrev = rec;
379    rec->fNext = fHead;
380    fHead = rec;
381
382    this->validate();
383}
384
385void SkScaledImageCache::addToHead(Rec* rec) {
386    this->validate();
387
388    rec->fPrev = NULL;
389    rec->fNext = fHead;
390    if (fHead) {
391        fHead->fPrev = rec;
392    }
393    fHead = rec;
394    if (!fTail) {
395        fTail = rec;
396    }
397    fBytesUsed += rec->bytesUsed();
398    fCount += 1;
399
400    this->validate();
401}
402
403#ifdef SK_DEBUG
404void SkScaledImageCache::validate() const {
405    if (NULL == fHead) {
406        SkASSERT(NULL == fTail);
407        SkASSERT(0 == fBytesUsed);
408        return;
409    }
410
411    if (fHead == fTail) {
412        SkASSERT(NULL == fHead->fPrev);
413        SkASSERT(NULL == fHead->fNext);
414        SkASSERT(fHead->bytesUsed() == fBytesUsed);
415        return;
416    }
417
418    SkASSERT(NULL == fHead->fPrev);
419    SkASSERT(NULL != fHead->fNext);
420    SkASSERT(NULL == fTail->fNext);
421    SkASSERT(NULL != fTail->fPrev);
422
423    size_t used = 0;
424    int count = 0;
425    const Rec* rec = fHead;
426    while (rec) {
427        count += 1;
428        used += rec->bytesUsed();
429        SkASSERT(used <= fBytesUsed);
430        rec = rec->fNext;
431    }
432    SkASSERT(fCount == count);
433
434    rec = fTail;
435    while (rec) {
436        SkASSERT(count > 0);
437        count -= 1;
438        SkASSERT(used >= rec->bytesUsed());
439        used -= rec->bytesUsed();
440        rec = rec->fPrev;
441    }
442
443    SkASSERT(0 == count);
444    SkASSERT(0 == used);
445}
446#endif
447
448///////////////////////////////////////////////////////////////////////////////
449
450#include "SkThread.h"
451
452SK_DECLARE_STATIC_MUTEX(gMutex);
453
454static SkScaledImageCache* get_cache() {
455    static SkScaledImageCache* gCache;
456    if (!gCache) {
457        gCache = SkNEW_ARGS(SkScaledImageCache, (SK_DEFAULT_IMAGE_CACHE_LIMIT));
458    }
459    return gCache;
460}
461
462SkScaledImageCache::ID* SkScaledImageCache::FindAndLock(const SkBitmap& orig,
463                                                        SkScalar scaleX,
464                                                        SkScalar scaleY,
465                                                        SkBitmap* scaled) {
466    SkAutoMutexAcquire am(gMutex);
467    return get_cache()->findAndLock(orig, scaleX, scaleY, scaled);
468}
469
470SkScaledImageCache::ID* SkScaledImageCache::FindAndLockMip(const SkBitmap& orig,
471                                                       SkMipMap const ** mip) {
472    SkAutoMutexAcquire am(gMutex);
473    return get_cache()->findAndLockMip(orig, mip);
474}
475
476SkScaledImageCache::ID* SkScaledImageCache::AddAndLock(const SkBitmap& orig,
477                                                       SkScalar scaleX,
478                                                       SkScalar scaleY,
479                                                       const SkBitmap& scaled) {
480    SkAutoMutexAcquire am(gMutex);
481    return get_cache()->addAndLock(orig, scaleX, scaleY, scaled);
482}
483
484SkScaledImageCache::ID* SkScaledImageCache::AddAndLockMip(const SkBitmap& orig,
485                                                          const SkMipMap* mip) {
486    SkAutoMutexAcquire am(gMutex);
487    return get_cache()->addAndLockMip(orig, mip);
488}
489
490void SkScaledImageCache::Unlock(SkScaledImageCache::ID* id) {
491    SkAutoMutexAcquire am(gMutex);
492    return get_cache()->unlock(id);
493}
494
495size_t SkScaledImageCache::GetBytesUsed() {
496    SkAutoMutexAcquire am(gMutex);
497    return get_cache()->getBytesUsed();
498}
499
500size_t SkScaledImageCache::GetByteLimit() {
501    SkAutoMutexAcquire am(gMutex);
502    return get_cache()->getByteLimit();
503}
504
505size_t SkScaledImageCache::SetByteLimit(size_t newLimit) {
506    SkAutoMutexAcquire am(gMutex);
507    return get_cache()->setByteLimit(newLimit);
508}
509
510///////////////////////////////////////////////////////////////////////////////
511
512#include "SkGraphics.h"
513
514size_t SkGraphics::GetImageCacheBytesUsed() {
515    return SkScaledImageCache::GetBytesUsed();
516}
517
518size_t SkGraphics::GetImageCacheByteLimit() {
519    return SkScaledImageCache::GetByteLimit();
520}
521
522size_t SkGraphics::SetImageCacheByteLimit(size_t newLimit) {
523    return SkScaledImageCache::SetByteLimit(newLimit);
524}
525