blob: 40cfefa29b6213095ee78d9b4e647832f1c2e7b3 [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
commit-bot@chromium.orgef284a82013-07-11 22:29:29 +000010SK_DEFINE_INST_COUNT(SkImageCache)
11SK_DEFINE_INST_COUNT(SkLruImageCache)
12
scroggo@google.comf8d7d272013-02-22 21:38:35 +000013static intptr_t NextGenerationID() {
14 static intptr_t gNextID;
15 do {
16 gNextID++;
17 } while (SkImageCache::UNINITIALIZED_ID == gNextID);
18 return gNextID;
19}
20
21class CachedPixels : public SkNoncopyable {
22
23public:
24 CachedPixels(size_t length)
25 : fLength(length)
26 , fID(NextGenerationID())
27 , fLocked(false) {
28 fAddr = sk_malloc_throw(length);
29 }
30
31 ~CachedPixels() {
32 sk_free(fAddr);
33 }
34
35 void* getData() { return fAddr; }
36
37 intptr_t getID() const { return fID; }
38
39 size_t getLength() const { return fLength; }
40
41 void lock() { SkASSERT(!fLocked); fLocked = true; }
42
43 void unlock() { SkASSERT(fLocked); fLocked = false; }
44
45 bool isLocked() const { return fLocked; }
46
47private:
48 void* fAddr;
49 size_t fLength;
50 const intptr_t fID;
51 bool fLocked;
52 SK_DECLARE_INTERNAL_LLIST_INTERFACE(CachedPixels);
53};
54
55////////////////////////////////////////////////////////////////////////////////////
56
57SkLruImageCache::SkLruImageCache(size_t budget)
58 : fRamBudget(budget)
59 , fRamUsed(0) {}
60
61SkLruImageCache::~SkLruImageCache() {
62 // Don't worry about updating pointers. All will be deleted.
63 Iter iter;
64 CachedPixels* pixels = iter.init(fLRU, Iter::kTail_IterStart);
65 while (pixels != NULL) {
66 CachedPixels* prev = iter.prev();
67 SkASSERT(!pixels->isLocked());
robertphillips@google.comffb91da2013-02-25 01:38:01 +000068#ifdef SK_DEBUG
scroggo@google.comf8d7d272013-02-22 21:38:35 +000069 fRamUsed -= pixels->getLength();
70#endif
71 SkDELETE(pixels);
72 pixels = prev;
73 }
robertphillips@google.comffb91da2013-02-25 01:38:01 +000074#ifdef SK_DEBUG
scroggo@google.comf8d7d272013-02-22 21:38:35 +000075 SkASSERT(fRamUsed == 0);
76#endif
77}
78
79#ifdef SK_DEBUG
scroggo@google.combb281f72013-03-18 21:37:39 +000080SkImageCache::MemoryStatus SkLruImageCache::getMemoryStatus(intptr_t ID) const {
81 if (SkImageCache::UNINITIALIZED_ID == ID) {
82 return SkImageCache::kFreed_MemoryStatus;
83 }
scroggo@google.comf8d7d272013-02-22 21:38:35 +000084 SkAutoMutexAcquire ac(&fMutex);
85 CachedPixels* pixels = this->findByID(ID);
86 if (NULL == pixels) {
scroggo@google.combb281f72013-03-18 21:37:39 +000087 return SkImageCache::kFreed_MemoryStatus;
scroggo@google.comf8d7d272013-02-22 21:38:35 +000088 }
89 if (pixels->isLocked()) {
scroggo@google.combb281f72013-03-18 21:37:39 +000090 return SkImageCache::kPinned_MemoryStatus;
scroggo@google.comf8d7d272013-02-22 21:38:35 +000091 }
scroggo@google.combb281f72013-03-18 21:37:39 +000092 return SkImageCache::kUnpinned_MemoryStatus;
93}
94
95void SkLruImageCache::purgeAllUnpinnedCaches() {
96 SkAutoMutexAcquire ac(&fMutex);
97 this->purgeTilAtOrBelow(0);
scroggo@google.comf8d7d272013-02-22 21:38:35 +000098}
99#endif
100
scroggo@google.coma560d00b2013-03-04 21:32:32 +0000101size_t SkLruImageCache::setImageCacheLimit(size_t newLimit) {
102 size_t oldLimit = fRamBudget;
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000103 SkAutoMutexAcquire ac(&fMutex);
scroggo@google.coma560d00b2013-03-04 21:32:32 +0000104 fRamBudget = newLimit;
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000105 this->purgeIfNeeded();
scroggo@google.coma560d00b2013-03-04 21:32:32 +0000106 return oldLimit;
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000107}
108
109void* SkLruImageCache::allocAndPinCache(size_t bytes, intptr_t* ID) {
110 SkAutoMutexAcquire ac(&fMutex);
111 CachedPixels* pixels = SkNEW_ARGS(CachedPixels, (bytes));
112 if (ID != NULL) {
113 *ID = pixels->getID();
114 }
115 pixels->lock();
116 fRamUsed += bytes;
117 fLRU.addToHead(pixels);
118 this->purgeIfNeeded();
119 return pixels->getData();
120}
121
scroggo@google.combb281f72013-03-18 21:37:39 +0000122void* SkLruImageCache::pinCache(intptr_t ID, SkImageCache::DataStatus* status) {
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000123 SkASSERT(ID != SkImageCache::UNINITIALIZED_ID);
124 SkAutoMutexAcquire ac(&fMutex);
125 CachedPixels* pixels = this->findByID(ID);
126 if (NULL == pixels) {
127 return NULL;
128 }
129 if (pixels != fLRU.head()) {
130 fLRU.remove(pixels);
131 fLRU.addToHead(pixels);
132 }
scroggo@google.combb281f72013-03-18 21:37:39 +0000133 SkASSERT(status != NULL);
134 // This cache will never return pinned memory whose data has been overwritten.
135 *status = SkImageCache::kRetained_DataStatus;
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000136 pixels->lock();
137 return pixels->getData();
138}
139
140void SkLruImageCache::releaseCache(intptr_t ID) {
141 SkASSERT(ID != SkImageCache::UNINITIALIZED_ID);
142 SkAutoMutexAcquire ac(&fMutex);
143 CachedPixels* pixels = this->findByID(ID);
144 SkASSERT(pixels != NULL);
145 pixels->unlock();
146 this->purgeIfNeeded();
147}
148
149void SkLruImageCache::throwAwayCache(intptr_t ID) {
scroggo@google.combb281f72013-03-18 21:37:39 +0000150 SkASSERT(ID != SkImageCache::UNINITIALIZED_ID);
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000151 SkAutoMutexAcquire ac(&fMutex);
152 CachedPixels* pixels = this->findByID(ID);
153 if (pixels != NULL) {
154 if (pixels->isLocked()) {
155 pixels->unlock();
156 }
157 this->removePixels(pixels);
158 }
159}
160
161void SkLruImageCache::removePixels(CachedPixels* pixels) {
162 // Mutex is already locked.
163 SkASSERT(!pixels->isLocked());
164 const size_t size = pixels->getLength();
165 SkASSERT(size <= fRamUsed);
166 fLRU.remove(pixels);
167 SkDELETE(pixels);
168 fRamUsed -= size;
169}
170
171CachedPixels* SkLruImageCache::findByID(intptr_t ID) const {
172 // Mutex is already locked.
173 Iter iter;
174 // Start from the head, most recently used.
175 CachedPixels* pixels = iter.init(fLRU, Iter::kHead_IterStart);
176 while (pixels != NULL) {
177 if (pixels->getID() == ID) {
178 return pixels;
179 }
180 pixels = iter.next();
181 }
182 return NULL;
183}
184
185void SkLruImageCache::purgeIfNeeded() {
186 // Mutex is already locked.
scroggo@google.coma560d00b2013-03-04 21:32:32 +0000187 if (fRamBudget > 0) {
188 this->purgeTilAtOrBelow(fRamBudget);
189 }
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000190}
191
192void SkLruImageCache::purgeTilAtOrBelow(size_t limit) {
193 // Mutex is already locked.
194 if (fRamUsed > limit) {
195 Iter iter;
196 // Start from the tail, least recently used.
197 CachedPixels* pixels = iter.init(fLRU, Iter::kTail_IterStart);
198 while (pixels != NULL && fRamUsed > limit) {
199 CachedPixels* prev = iter.prev();
200 if (!pixels->isLocked()) {
201 this->removePixels(pixels);
202 }
203 pixels = prev;
204 }
205 }
206}