blob: 54f26fb5dcbf8263e143b12ce9efe846b5f1da38 [file] [log] [blame]
scroggo@google.comf8d7d272013-02-22 21:38:35 +00001/*
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 "SkLruImageCache.h"
9
10static intptr_t NextGenerationID() {
11 static intptr_t gNextID;
12 do {
13 gNextID++;
14 } while (SkImageCache::UNINITIALIZED_ID == gNextID);
15 return gNextID;
16}
17
18class CachedPixels : public SkNoncopyable {
19
20public:
21 CachedPixels(size_t length)
22 : fLength(length)
23 , fID(NextGenerationID())
24 , fLocked(false) {
25 fAddr = sk_malloc_throw(length);
26 }
27
28 ~CachedPixels() {
29 sk_free(fAddr);
30 }
31
32 void* getData() { return fAddr; }
33
34 intptr_t getID() const { return fID; }
35
36 size_t getLength() const { return fLength; }
37
38 void lock() { SkASSERT(!fLocked); fLocked = true; }
39
40 void unlock() { SkASSERT(fLocked); fLocked = false; }
41
42 bool isLocked() const { return fLocked; }
43
44private:
45 void* fAddr;
46 size_t fLength;
47 const intptr_t fID;
48 bool fLocked;
49 SK_DECLARE_INTERNAL_LLIST_INTERFACE(CachedPixels);
50};
51
52////////////////////////////////////////////////////////////////////////////////////
53
54SkLruImageCache::SkLruImageCache(size_t budget)
55 : fRamBudget(budget)
56 , fRamUsed(0) {}
57
58SkLruImageCache::~SkLruImageCache() {
59 // Don't worry about updating pointers. All will be deleted.
60 Iter iter;
61 CachedPixels* pixels = iter.init(fLRU, Iter::kTail_IterStart);
62 while (pixels != NULL) {
63 CachedPixels* prev = iter.prev();
64 SkASSERT(!pixels->isLocked());
robertphillips@google.comffb91da2013-02-25 01:38:01 +000065#ifdef SK_DEBUG
scroggo@google.comf8d7d272013-02-22 21:38:35 +000066 fRamUsed -= pixels->getLength();
67#endif
68 SkDELETE(pixels);
69 pixels = prev;
70 }
robertphillips@google.comffb91da2013-02-25 01:38:01 +000071#ifdef SK_DEBUG
scroggo@google.comf8d7d272013-02-22 21:38:35 +000072 SkASSERT(fRamUsed == 0);
73#endif
74}
75
76#ifdef SK_DEBUG
77SkImageCache::CacheStatus SkLruImageCache::getCacheStatus(intptr_t ID) const {
78 SkAutoMutexAcquire ac(&fMutex);
79 CachedPixels* pixels = this->findByID(ID);
80 if (NULL == pixels) {
81 return SkImageCache::kThrownAway_CacheStatus;
82 }
83 if (pixels->isLocked()) {
84 return SkImageCache::kPinned_CacheStatus;
85 }
86 return SkImageCache::kUnpinned_CacheStatus;
87}
88#endif
89
scroggo@google.coma560d00b2013-03-04 21:32:32 +000090size_t SkLruImageCache::setImageCacheLimit(size_t newLimit) {
91 size_t oldLimit = fRamBudget;
scroggo@google.comf8d7d272013-02-22 21:38:35 +000092 SkAutoMutexAcquire ac(&fMutex);
scroggo@google.coma560d00b2013-03-04 21:32:32 +000093 fRamBudget = newLimit;
scroggo@google.comf8d7d272013-02-22 21:38:35 +000094 this->purgeIfNeeded();
scroggo@google.coma560d00b2013-03-04 21:32:32 +000095 return oldLimit;
scroggo@google.comf8d7d272013-02-22 21:38:35 +000096}
97
98void* SkLruImageCache::allocAndPinCache(size_t bytes, intptr_t* ID) {
99 SkAutoMutexAcquire ac(&fMutex);
100 CachedPixels* pixels = SkNEW_ARGS(CachedPixels, (bytes));
101 if (ID != NULL) {
102 *ID = pixels->getID();
103 }
104 pixels->lock();
105 fRamUsed += bytes;
106 fLRU.addToHead(pixels);
107 this->purgeIfNeeded();
108 return pixels->getData();
109}
110
111void* SkLruImageCache::pinCache(intptr_t ID) {
112 SkASSERT(ID != SkImageCache::UNINITIALIZED_ID);
113 SkAutoMutexAcquire ac(&fMutex);
114 CachedPixels* pixels = this->findByID(ID);
115 if (NULL == pixels) {
116 return NULL;
117 }
118 if (pixels != fLRU.head()) {
119 fLRU.remove(pixels);
120 fLRU.addToHead(pixels);
121 }
122 pixels->lock();
123 return pixels->getData();
124}
125
126void SkLruImageCache::releaseCache(intptr_t ID) {
127 SkASSERT(ID != SkImageCache::UNINITIALIZED_ID);
128 SkAutoMutexAcquire ac(&fMutex);
129 CachedPixels* pixels = this->findByID(ID);
130 SkASSERT(pixels != NULL);
131 pixels->unlock();
132 this->purgeIfNeeded();
133}
134
135void SkLruImageCache::throwAwayCache(intptr_t ID) {
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000136 SkAutoMutexAcquire ac(&fMutex);
137 CachedPixels* pixels = this->findByID(ID);
138 if (pixels != NULL) {
139 if (pixels->isLocked()) {
140 pixels->unlock();
141 }
142 this->removePixels(pixels);
143 }
144}
145
146void SkLruImageCache::removePixels(CachedPixels* pixels) {
147 // Mutex is already locked.
148 SkASSERT(!pixels->isLocked());
149 const size_t size = pixels->getLength();
150 SkASSERT(size <= fRamUsed);
151 fLRU.remove(pixels);
152 SkDELETE(pixels);
153 fRamUsed -= size;
154}
155
156CachedPixels* SkLruImageCache::findByID(intptr_t ID) const {
157 // Mutex is already locked.
scroggo@google.comc75764e2013-03-04 21:38:50 +0000158 if (SkImageCache::UNINITIALIZED_ID == ID) {
159 return NULL;
160 }
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000161 Iter iter;
162 // Start from the head, most recently used.
163 CachedPixels* pixels = iter.init(fLRU, Iter::kHead_IterStart);
164 while (pixels != NULL) {
165 if (pixels->getID() == ID) {
166 return pixels;
167 }
168 pixels = iter.next();
169 }
170 return NULL;
171}
172
173void SkLruImageCache::purgeIfNeeded() {
174 // Mutex is already locked.
scroggo@google.coma560d00b2013-03-04 21:32:32 +0000175 if (fRamBudget > 0) {
176 this->purgeTilAtOrBelow(fRamBudget);
177 }
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000178}
179
180void SkLruImageCache::purgeTilAtOrBelow(size_t limit) {
181 // Mutex is already locked.
182 if (fRamUsed > limit) {
183 Iter iter;
184 // Start from the tail, least recently used.
185 CachedPixels* pixels = iter.init(fLRU, Iter::kTail_IterStart);
186 while (pixels != NULL && fRamUsed > limit) {
187 CachedPixels* prev = iter.prev();
188 if (!pixels->isLocked()) {
189 this->removePixels(pixels);
190 }
191 pixels = prev;
192 }
193 }
194}