blob: 26f7ef54838bd8c61fd47178fe7a30eba895e371 [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
scroggo@google.combb281f72013-03-18 21:37:39 +000077SkImageCache::MemoryStatus SkLruImageCache::getMemoryStatus(intptr_t ID) const {
78 if (SkImageCache::UNINITIALIZED_ID == ID) {
79 return SkImageCache::kFreed_MemoryStatus;
80 }
scroggo@google.comf8d7d272013-02-22 21:38:35 +000081 SkAutoMutexAcquire ac(&fMutex);
82 CachedPixels* pixels = this->findByID(ID);
83 if (NULL == pixels) {
scroggo@google.combb281f72013-03-18 21:37:39 +000084 return SkImageCache::kFreed_MemoryStatus;
scroggo@google.comf8d7d272013-02-22 21:38:35 +000085 }
86 if (pixels->isLocked()) {
scroggo@google.combb281f72013-03-18 21:37:39 +000087 return SkImageCache::kPinned_MemoryStatus;
scroggo@google.comf8d7d272013-02-22 21:38:35 +000088 }
scroggo@google.combb281f72013-03-18 21:37:39 +000089 return SkImageCache::kUnpinned_MemoryStatus;
90}
91
92void SkLruImageCache::purgeAllUnpinnedCaches() {
93 SkAutoMutexAcquire ac(&fMutex);
94 this->purgeTilAtOrBelow(0);
scroggo@google.comf8d7d272013-02-22 21:38:35 +000095}
96#endif
97
scroggo@google.coma560d00b2013-03-04 21:32:32 +000098size_t SkLruImageCache::setImageCacheLimit(size_t newLimit) {
99 size_t oldLimit = fRamBudget;
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000100 SkAutoMutexAcquire ac(&fMutex);
scroggo@google.coma560d00b2013-03-04 21:32:32 +0000101 fRamBudget = newLimit;
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000102 this->purgeIfNeeded();
scroggo@google.coma560d00b2013-03-04 21:32:32 +0000103 return oldLimit;
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000104}
105
106void* SkLruImageCache::allocAndPinCache(size_t bytes, intptr_t* ID) {
107 SkAutoMutexAcquire ac(&fMutex);
108 CachedPixels* pixels = SkNEW_ARGS(CachedPixels, (bytes));
109 if (ID != NULL) {
110 *ID = pixels->getID();
111 }
112 pixels->lock();
113 fRamUsed += bytes;
114 fLRU.addToHead(pixels);
115 this->purgeIfNeeded();
116 return pixels->getData();
117}
118
scroggo@google.combb281f72013-03-18 21:37:39 +0000119void* SkLruImageCache::pinCache(intptr_t ID, SkImageCache::DataStatus* status) {
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000120 SkASSERT(ID != SkImageCache::UNINITIALIZED_ID);
121 SkAutoMutexAcquire ac(&fMutex);
122 CachedPixels* pixels = this->findByID(ID);
123 if (NULL == pixels) {
124 return NULL;
125 }
126 if (pixels != fLRU.head()) {
127 fLRU.remove(pixels);
128 fLRU.addToHead(pixels);
129 }
scroggo@google.combb281f72013-03-18 21:37:39 +0000130 SkASSERT(status != NULL);
131 // This cache will never return pinned memory whose data has been overwritten.
132 *status = SkImageCache::kRetained_DataStatus;
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000133 pixels->lock();
134 return pixels->getData();
135}
136
137void SkLruImageCache::releaseCache(intptr_t ID) {
138 SkASSERT(ID != SkImageCache::UNINITIALIZED_ID);
139 SkAutoMutexAcquire ac(&fMutex);
140 CachedPixels* pixels = this->findByID(ID);
141 SkASSERT(pixels != NULL);
142 pixels->unlock();
143 this->purgeIfNeeded();
144}
145
146void SkLruImageCache::throwAwayCache(intptr_t ID) {
scroggo@google.combb281f72013-03-18 21:37:39 +0000147 SkASSERT(ID != SkImageCache::UNINITIALIZED_ID);
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000148 SkAutoMutexAcquire ac(&fMutex);
149 CachedPixels* pixels = this->findByID(ID);
150 if (pixels != NULL) {
151 if (pixels->isLocked()) {
152 pixels->unlock();
153 }
154 this->removePixels(pixels);
155 }
156}
157
158void SkLruImageCache::removePixels(CachedPixels* pixels) {
159 // Mutex is already locked.
160 SkASSERT(!pixels->isLocked());
161 const size_t size = pixels->getLength();
162 SkASSERT(size <= fRamUsed);
163 fLRU.remove(pixels);
164 SkDELETE(pixels);
165 fRamUsed -= size;
166}
167
168CachedPixels* SkLruImageCache::findByID(intptr_t ID) const {
169 // Mutex is already locked.
170 Iter iter;
171 // Start from the head, most recently used.
172 CachedPixels* pixels = iter.init(fLRU, Iter::kHead_IterStart);
173 while (pixels != NULL) {
174 if (pixels->getID() == ID) {
175 return pixels;
176 }
177 pixels = iter.next();
178 }
179 return NULL;
180}
181
182void SkLruImageCache::purgeIfNeeded() {
183 // Mutex is already locked.
scroggo@google.coma560d00b2013-03-04 21:32:32 +0000184 if (fRamBudget > 0) {
185 this->purgeTilAtOrBelow(fRamBudget);
186 }
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000187}
188
189void SkLruImageCache::purgeTilAtOrBelow(size_t limit) {
190 // Mutex is already locked.
191 if (fRamUsed > limit) {
192 Iter iter;
193 // Start from the tail, least recently used.
194 CachedPixels* pixels = iter.init(fLRU, Iter::kTail_IterStart);
195 while (pixels != NULL && fRamUsed > limit) {
196 CachedPixels* prev = iter.prev();
197 if (!pixels->isLocked()) {
198 this->removePixels(pixels);
199 }
200 pixels = prev;
201 }
202 }
203}