blob: 62e29535966e8674acbe3cae3999dfeb8adca68a [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
90void SkLruImageCache::setBudget(size_t newBudget) {
91 SkAutoMutexAcquire ac(&fMutex);
92 fRamBudget = newBudget;
93 this->purgeIfNeeded();
94}
95
96void* SkLruImageCache::allocAndPinCache(size_t bytes, intptr_t* ID) {
97 SkAutoMutexAcquire ac(&fMutex);
98 CachedPixels* pixels = SkNEW_ARGS(CachedPixels, (bytes));
99 if (ID != NULL) {
100 *ID = pixels->getID();
101 }
102 pixels->lock();
103 fRamUsed += bytes;
104 fLRU.addToHead(pixels);
105 this->purgeIfNeeded();
106 return pixels->getData();
107}
108
109void* SkLruImageCache::pinCache(intptr_t ID) {
110 SkASSERT(ID != SkImageCache::UNINITIALIZED_ID);
111 SkAutoMutexAcquire ac(&fMutex);
112 CachedPixels* pixels = this->findByID(ID);
113 if (NULL == pixels) {
114 return NULL;
115 }
116 if (pixels != fLRU.head()) {
117 fLRU.remove(pixels);
118 fLRU.addToHead(pixels);
119 }
120 pixels->lock();
121 return pixels->getData();
122}
123
124void SkLruImageCache::releaseCache(intptr_t ID) {
125 SkASSERT(ID != SkImageCache::UNINITIALIZED_ID);
126 SkAutoMutexAcquire ac(&fMutex);
127 CachedPixels* pixels = this->findByID(ID);
128 SkASSERT(pixels != NULL);
129 pixels->unlock();
130 this->purgeIfNeeded();
131}
132
133void SkLruImageCache::throwAwayCache(intptr_t ID) {
134 SkASSERT(ID != SkImageCache::UNINITIALIZED_ID);
135 SkAutoMutexAcquire ac(&fMutex);
136 CachedPixels* pixels = this->findByID(ID);
137 if (pixels != NULL) {
138 if (pixels->isLocked()) {
139 pixels->unlock();
140 }
141 this->removePixels(pixels);
142 }
143}
144
145void SkLruImageCache::removePixels(CachedPixels* pixels) {
146 // Mutex is already locked.
147 SkASSERT(!pixels->isLocked());
148 const size_t size = pixels->getLength();
149 SkASSERT(size <= fRamUsed);
150 fLRU.remove(pixels);
151 SkDELETE(pixels);
152 fRamUsed -= size;
153}
154
155CachedPixels* SkLruImageCache::findByID(intptr_t ID) const {
156 // Mutex is already locked.
157 Iter iter;
158 // Start from the head, most recently used.
159 CachedPixels* pixels = iter.init(fLRU, Iter::kHead_IterStart);
160 while (pixels != NULL) {
161 if (pixels->getID() == ID) {
162 return pixels;
163 }
164 pixels = iter.next();
165 }
166 return NULL;
167}
168
169void SkLruImageCache::purgeIfNeeded() {
170 // Mutex is already locked.
171 this->purgeTilAtOrBelow(fRamBudget);
172}
173
174void SkLruImageCache::purgeTilAtOrBelow(size_t limit) {
175 // Mutex is already locked.
176 if (fRamUsed > limit) {
177 Iter iter;
178 // Start from the tail, least recently used.
179 CachedPixels* pixels = iter.init(fLRU, Iter::kTail_IterStart);
180 while (pixels != NULL && fRamUsed > limit) {
181 CachedPixels* prev = iter.prev();
182 if (!pixels->isLocked()) {
183 this->removePixels(pixels);
184 }
185 pixels = prev;
186 }
187 }
188}