blob: c3a61ac2e7c53f135eb016526644d6e29b3399aa [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
bsalomon@google.com8fe72472011-03-30 21:26:44 +000058 this->removeAll();
reed@google.comac10a2d2010-12-22 21:39:39 +000059}
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(),
bsalomon@google.comc6cf7232011-02-17 16:43:10 +0000231 entry->texture()->width(),
232 entry->texture()->height());
reed@google.comac10a2d2010-12-22 21:39:39 +0000233#endif
234 delete entry;
235 }
236 entry = prev;
237 }
238}
239
bsalomon@google.com8fe72472011-03-30 21:26:44 +0000240void GrTextureCache::removeAll() {
reed@google.comac10a2d2010-12-22 21:39:39 +0000241 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;
reed@google.comac10a2d2010-12-22 21:39:39 +0000249 delete entry;
250 entry = next;
251 }
252
253 fCache.removeAll();
254 fHead = fTail = NULL;
255 fEntryCount = 0;
256 fEntryBytes = 0;
257}
258
259///////////////////////////////////////////////////////////////////////////////
260
261#if GR_DEBUG
262static int countMatches(const GrTextureEntry* head, const GrTextureEntry* target) {
263 const GrTextureEntry* entry = head;
264 int count = 0;
265 while (entry) {
266 if (target == entry) {
267 count += 1;
268 }
269 entry = entry->next();
270 }
271 return count;
272}
273
reed@google.comb89a6432011-02-07 13:20:30 +0000274#if GR_DEBUG
275static bool both_zero_or_nonzero(int count, size_t bytes) {
276 return (count == 0 && bytes == 0) || (count > 0 && bytes > 0);
277}
278#endif
279
reed@google.comac10a2d2010-12-22 21:39:39 +0000280void GrTextureCache::validate() const {
281 GrAssert(!fHead == !fTail);
reed@google.comb89a6432011-02-07 13:20:30 +0000282 GrAssert(both_zero_or_nonzero(fEntryCount, fEntryBytes));
283 GrAssert(both_zero_or_nonzero(fClientDetachedCount, fClientDetachedBytes));
reed@google.comac10a2d2010-12-22 21:39:39 +0000284 GrAssert(fClientDetachedBytes <= fEntryBytes);
285 GrAssert(fClientDetachedCount <= fEntryCount);
286 GrAssert((fEntryCount - fClientDetachedCount) == fCache.count());
reed@google.com01804b42011-01-18 21:50:41 +0000287
reed@google.comac10a2d2010-12-22 21:39:39 +0000288 fCache.validate();
289
290 GrTextureEntry* entry = fHead;
291 int count = 0;
292 size_t bytes = 0;
293 while (entry) {
294 entry->validate();
295 GrAssert(fCache.find(entry->key()));
296 count += 1;
297 bytes += entry->texture()->sizeInBytes();
298 entry = entry->fNext;
299 }
300 GrAssert(count == fEntryCount - fClientDetachedCount);
301 GrAssert(bytes == fEntryBytes - fClientDetachedBytes);
302
303 count = 0;
304 for (entry = fTail; entry; entry = entry->fPrev) {
305 count += 1;
306 }
307 GrAssert(count == fEntryCount - fClientDetachedCount);
308
309 for (int i = 0; i < count; i++) {
310 int matches = countMatches(fHead, fCache.getArray()[i]);
311 GrAssert(1 == matches);
312 }
313}
314#endif
315
316