blob: e27272ec250d3ab4c69d4efaf5ed2f4e1a6928f3 [file] [log] [blame]
reed@google.comac10a2d2010-12-22 21:39:39 +00001/*
2 Copyright 2010 Google Inc.
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16
17
18#include "GrTextureCache.h"
19#include "GrTexture.h"
20
21GrTextureEntry::GrTextureEntry(const GrTextureKey& key, GrTexture* texture)
22 : fKey(key), fTexture(texture) {
23 fLockCount = 0;
24 fPrev = fNext = NULL;
25
26 // we assume ownership of the texture, and will unref it when we die
27 GrAssert(texture);
28}
29
30GrTextureEntry::~GrTextureEntry() {
31 fTexture->unref();
32}
33
34#if GR_DEBUG
35void GrTextureEntry::validate() const {
36 GrAssert(fLockCount >= 0);
37 GrAssert(fTexture);
38 fTexture->validate();
39}
40#endif
41
42///////////////////////////////////////////////////////////////////////////////
43
reed@google.com01804b42011-01-18 21:50:41 +000044GrTextureCache::GrTextureCache(int maxCount, size_t maxBytes) :
reed@google.comac10a2d2010-12-22 21:39:39 +000045 fMaxCount(maxCount),
46 fMaxBytes(maxBytes) {
47 fEntryCount = 0;
48 fEntryBytes = 0;
49 fClientDetachedCount = 0;
50 fClientDetachedBytes = 0;
51
52 fHead = fTail = NULL;
53}
54
55GrTextureCache::~GrTextureCache() {
56 GrAutoTextureCacheValidate atcv(this);
reed@google.com01804b42011-01-18 21:50:41 +000057
reed@google.comac10a2d2010-12-22 21:39:39 +000058 this->deleteAll(kFreeTexture_DeleteMode);
59}
60
reed@google.com01804b42011-01-18 21:50:41 +000061void GrTextureCache::getLimits(int* maxTextures, size_t* maxTextureBytes) const{
62 if (maxTextures) {
63 *maxTextures = fMaxCount;
64 }
65 if (maxTextureBytes) {
66 *maxTextureBytes = fMaxBytes;
67 }
68}
69
70void GrTextureCache::setLimits(int maxTextures, size_t maxTextureBytes) {
71 bool smaller = (maxTextures < fMaxCount) || (maxTextureBytes < fMaxBytes);
72
73 fMaxCount = maxTextures;
74 fMaxBytes = maxTextureBytes;
75
76 if (smaller) {
77 this->purgeAsNeeded();
78 }
79}
80
81void GrTextureCache::internalDetach(GrTextureEntry* entry,
reed@google.comac10a2d2010-12-22 21:39:39 +000082 bool clientDetach) {
83 GrTextureEntry* prev = entry->fPrev;
84 GrTextureEntry* next = entry->fNext;
85
86 if (prev) {
87 prev->fNext = next;
88 } else {
89 fHead = next;
90 }
91 if (next) {
92 next->fPrev = prev;
93 } else {
94 fTail = prev;
95 }
96
97 // update our stats
98 if (clientDetach) {
99 fClientDetachedCount += 1;
100 fClientDetachedBytes += entry->texture()->sizeInBytes();
101 } else {
102 fEntryCount -= 1;
103 fEntryBytes -= entry->texture()->sizeInBytes();
104 }
105}
106
reed@google.com01804b42011-01-18 21:50:41 +0000107void GrTextureCache::attachToHead(GrTextureEntry* entry,
reed@google.comac10a2d2010-12-22 21:39:39 +0000108 bool clientReattach) {
109 entry->fPrev = NULL;
110 entry->fNext = fHead;
111 if (fHead) {
112 fHead->fPrev = entry;
113 }
114 fHead = entry;
115 if (NULL == fTail) {
116 fTail = entry;
117 }
118
119 // update our stats
120 if (clientReattach) {
121 fClientDetachedCount -= 1;
122 fClientDetachedBytes -= entry->texture()->sizeInBytes();
123 } else {
124 fEntryCount += 1;
125 fEntryBytes += entry->texture()->sizeInBytes();
126 }
127}
128
129class GrTextureCache::Key {
130 typedef GrTextureEntry T;
131
132 const GrTextureKey& fKey;
133public:
134 Key(const GrTextureKey& key) : fKey(key) {}
135
136 uint32_t getHash() const { return fKey.hashIndex(); }
reed@google.com01804b42011-01-18 21:50:41 +0000137
reed@google.comac10a2d2010-12-22 21:39:39 +0000138 static bool LT(const T& entry, const Key& key) {
139 return entry.key() < key.fKey;
140 }
141 static bool EQ(const T& entry, const Key& key) {
142 return entry.key() == key.fKey;
143 }
144#if GR_DEBUG
145 static uint32_t GetHash(const T& entry) {
146 return entry.key().hashIndex();
147 }
148 static bool LT(const T& a, const T& b) {
149 return a.key() < b.key();
150 }
151 static bool EQ(const T& a, const T& b) {
152 return a.key() == b.key();
153 }
154#endif
155};
156
157GrTextureEntry* GrTextureCache::findAndLock(const GrTextureKey& key) {
158 GrAutoTextureCacheValidate atcv(this);
159
160 GrTextureEntry* entry = fCache.find(key);
161 if (entry) {
162 this->internalDetach(entry, false);
163 this->attachToHead(entry, false);
164 // mark the entry as "busy" so it doesn't get purged
165 entry->lock();
166 }
167 return entry;
168}
169
170GrTextureEntry* GrTextureCache::createAndLock(const GrTextureKey& key,
171 GrTexture* texture) {
172 GrAutoTextureCacheValidate atcv(this);
173
174 GrTextureEntry* entry = new GrTextureEntry(key, texture);
175
176 this->attachToHead(entry, false);
177 fCache.insert(key, entry);
178
179#if GR_DUMP_TEXTURE_UPLOAD
180 GrPrintf("--- add texture to cache %p, count=%d bytes= %d %d\n",
181 entry, fEntryCount, texture->sizeInBytes(), fEntryBytes);
182#endif
183
184 // mark the entry as "busy" so it doesn't get purged
185 entry->lock();
186 this->purgeAsNeeded();
187 return entry;
188}
189
190void GrTextureCache::detach(GrTextureEntry* entry) {
191 internalDetach(entry, true);
192 fCache.remove(entry->fKey, entry);
193}
194
195void GrTextureCache::reattachAndUnlock(GrTextureEntry* entry) {
196 attachToHead(entry, true);
197 fCache.insert(entry->key(), entry);
198 unlock(entry);
199}
200
201void GrTextureCache::unlock(GrTextureEntry* entry) {
202 GrAutoTextureCacheValidate atcv(this);
reed@google.com01804b42011-01-18 21:50:41 +0000203
reed@google.comac10a2d2010-12-22 21:39:39 +0000204 GrAssert(entry);
205 GrAssert(entry->isLocked());
206 GrAssert(fCache.find(entry->key()));
207
208 entry->unlock();
209 this->purgeAsNeeded();
210}
211
212void GrTextureCache::purgeAsNeeded() {
213 GrAutoTextureCacheValidate atcv(this);
reed@google.com01804b42011-01-18 21:50:41 +0000214
reed@google.comac10a2d2010-12-22 21:39:39 +0000215 GrTextureEntry* entry = fTail;
216 while (entry) {
217 if (fEntryCount <= fMaxCount && fEntryBytes <= fMaxBytes) {
218 break;
219 }
220
221 GrTextureEntry* prev = entry->fPrev;
222 if (!entry->isLocked()) {
223 // remove from our cache
224 fCache.remove(entry->fKey, entry);
reed@google.com01804b42011-01-18 21:50:41 +0000225
reed@google.comac10a2d2010-12-22 21:39:39 +0000226 // remove from our llist
227 this->internalDetach(entry, false);
228
229#if GR_DUMP_TEXTURE_UPLOAD
230 GrPrintf("--- ~texture from cache %p [%d %d]\n", entry->texture(),
231 entry->texture()->contentWidth(),
232 entry->texture()->contentHeight());
233#endif
234 delete entry;
235 }
236 entry = prev;
237 }
238}
239
240void GrTextureCache::deleteAll(DeleteMode mode) {
241 GrAssert(!fClientDetachedCount);
242 GrAssert(!fClientDetachedBytes);
reed@google.com01804b42011-01-18 21:50:41 +0000243
reed@google.comac10a2d2010-12-22 21:39:39 +0000244 GrTextureEntry* entry = fHead;
245 while (entry) {
246 GrAssert(!entry->isLocked());
247
248 GrTextureEntry* next = entry->fNext;
249 if (kAbandonTexture_DeleteMode == mode) {
250 entry->texture()->abandon();
251 }
252 delete entry;
253 entry = next;
254 }
255
256 fCache.removeAll();
257 fHead = fTail = NULL;
258 fEntryCount = 0;
259 fEntryBytes = 0;
260}
261
262///////////////////////////////////////////////////////////////////////////////
263
264#if GR_DEBUG
265static int countMatches(const GrTextureEntry* head, const GrTextureEntry* target) {
266 const GrTextureEntry* entry = head;
267 int count = 0;
268 while (entry) {
269 if (target == entry) {
270 count += 1;
271 }
272 entry = entry->next();
273 }
274 return count;
275}
276
reed@google.comb89a6432011-02-07 13:20:30 +0000277#if GR_DEBUG
278static bool both_zero_or_nonzero(int count, size_t bytes) {
279 return (count == 0 && bytes == 0) || (count > 0 && bytes > 0);
280}
281#endif
282
reed@google.comac10a2d2010-12-22 21:39:39 +0000283void GrTextureCache::validate() const {
284 GrAssert(!fHead == !fTail);
reed@google.comb89a6432011-02-07 13:20:30 +0000285 GrAssert(both_zero_or_nonzero(fEntryCount, fEntryBytes));
286 GrAssert(both_zero_or_nonzero(fClientDetachedCount, fClientDetachedBytes));
reed@google.comac10a2d2010-12-22 21:39:39 +0000287 GrAssert(fClientDetachedBytes <= fEntryBytes);
288 GrAssert(fClientDetachedCount <= fEntryCount);
289 GrAssert((fEntryCount - fClientDetachedCount) == fCache.count());
reed@google.com01804b42011-01-18 21:50:41 +0000290
reed@google.comac10a2d2010-12-22 21:39:39 +0000291 fCache.validate();
292
293 GrTextureEntry* entry = fHead;
294 int count = 0;
295 size_t bytes = 0;
296 while (entry) {
297 entry->validate();
298 GrAssert(fCache.find(entry->key()));
299 count += 1;
300 bytes += entry->texture()->sizeInBytes();
301 entry = entry->fNext;
302 }
303 GrAssert(count == fEntryCount - fClientDetachedCount);
304 GrAssert(bytes == fEntryBytes - fClientDetachedBytes);
305
306 count = 0;
307 for (entry = fTail; entry; entry = entry->fPrev) {
308 count += 1;
309 }
310 GrAssert(count == fEntryCount - fClientDetachedCount);
311
312 for (int i = 0; i < count; i++) {
313 int matches = countMatches(fHead, fCache.getArray()[i]);
314 GrAssert(1 == matches);
315 }
316}
317#endif
318
319