blob: 3ba333945c8bd8c77b4be1f4a82e3fc3c7cfd3de [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
44GrTextureCache::GrTextureCache(int maxCount, size_t maxBytes) :
45 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);
57
58 this->deleteAll(kFreeTexture_DeleteMode);
59}
60
61void GrTextureCache::internalDetach(GrTextureEntry* entry,
62 bool clientDetach) {
63 GrTextureEntry* prev = entry->fPrev;
64 GrTextureEntry* next = entry->fNext;
65
66 if (prev) {
67 prev->fNext = next;
68 } else {
69 fHead = next;
70 }
71 if (next) {
72 next->fPrev = prev;
73 } else {
74 fTail = prev;
75 }
76
77 // update our stats
78 if (clientDetach) {
79 fClientDetachedCount += 1;
80 fClientDetachedBytes += entry->texture()->sizeInBytes();
81 } else {
82 fEntryCount -= 1;
83 fEntryBytes -= entry->texture()->sizeInBytes();
84 }
85}
86
87void GrTextureCache::attachToHead(GrTextureEntry* entry,
88 bool clientReattach) {
89 entry->fPrev = NULL;
90 entry->fNext = fHead;
91 if (fHead) {
92 fHead->fPrev = entry;
93 }
94 fHead = entry;
95 if (NULL == fTail) {
96 fTail = entry;
97 }
98
99 // update our stats
100 if (clientReattach) {
101 fClientDetachedCount -= 1;
102 fClientDetachedBytes -= entry->texture()->sizeInBytes();
103 } else {
104 fEntryCount += 1;
105 fEntryBytes += entry->texture()->sizeInBytes();
106 }
107}
108
109class GrTextureCache::Key {
110 typedef GrTextureEntry T;
111
112 const GrTextureKey& fKey;
113public:
114 Key(const GrTextureKey& key) : fKey(key) {}
115
116 uint32_t getHash() const { return fKey.hashIndex(); }
117
118 static bool LT(const T& entry, const Key& key) {
119 return entry.key() < key.fKey;
120 }
121 static bool EQ(const T& entry, const Key& key) {
122 return entry.key() == key.fKey;
123 }
124#if GR_DEBUG
125 static uint32_t GetHash(const T& entry) {
126 return entry.key().hashIndex();
127 }
128 static bool LT(const T& a, const T& b) {
129 return a.key() < b.key();
130 }
131 static bool EQ(const T& a, const T& b) {
132 return a.key() == b.key();
133 }
134#endif
135};
136
137GrTextureEntry* GrTextureCache::findAndLock(const GrTextureKey& key) {
138 GrAutoTextureCacheValidate atcv(this);
139
140 GrTextureEntry* entry = fCache.find(key);
141 if (entry) {
142 this->internalDetach(entry, false);
143 this->attachToHead(entry, false);
144 // mark the entry as "busy" so it doesn't get purged
145 entry->lock();
146 }
147 return entry;
148}
149
150GrTextureEntry* GrTextureCache::createAndLock(const GrTextureKey& key,
151 GrTexture* texture) {
152 GrAutoTextureCacheValidate atcv(this);
153
154 GrTextureEntry* entry = new GrTextureEntry(key, texture);
155
156 this->attachToHead(entry, false);
157 fCache.insert(key, entry);
158
159#if GR_DUMP_TEXTURE_UPLOAD
160 GrPrintf("--- add texture to cache %p, count=%d bytes= %d %d\n",
161 entry, fEntryCount, texture->sizeInBytes(), fEntryBytes);
162#endif
163
164 // mark the entry as "busy" so it doesn't get purged
165 entry->lock();
166 this->purgeAsNeeded();
167 return entry;
168}
169
170void GrTextureCache::detach(GrTextureEntry* entry) {
171 internalDetach(entry, true);
172 fCache.remove(entry->fKey, entry);
173}
174
175void GrTextureCache::reattachAndUnlock(GrTextureEntry* entry) {
176 attachToHead(entry, true);
177 fCache.insert(entry->key(), entry);
178 unlock(entry);
179}
180
181void GrTextureCache::unlock(GrTextureEntry* entry) {
182 GrAutoTextureCacheValidate atcv(this);
183
184 GrAssert(entry);
185 GrAssert(entry->isLocked());
186 GrAssert(fCache.find(entry->key()));
187
188 entry->unlock();
189 this->purgeAsNeeded();
190}
191
192void GrTextureCache::purgeAsNeeded() {
193 GrAutoTextureCacheValidate atcv(this);
194
195 GrTextureEntry* entry = fTail;
196 while (entry) {
197 if (fEntryCount <= fMaxCount && fEntryBytes <= fMaxBytes) {
198 break;
199 }
200
201 GrTextureEntry* prev = entry->fPrev;
202 if (!entry->isLocked()) {
203 // remove from our cache
204 fCache.remove(entry->fKey, entry);
205
206 // remove from our llist
207 this->internalDetach(entry, false);
208
209#if GR_DUMP_TEXTURE_UPLOAD
210 GrPrintf("--- ~texture from cache %p [%d %d]\n", entry->texture(),
211 entry->texture()->contentWidth(),
212 entry->texture()->contentHeight());
213#endif
214 delete entry;
215 }
216 entry = prev;
217 }
218}
219
220void GrTextureCache::deleteAll(DeleteMode mode) {
221 GrAssert(!fClientDetachedCount);
222 GrAssert(!fClientDetachedBytes);
223
224 GrTextureEntry* entry = fHead;
225 while (entry) {
226 GrAssert(!entry->isLocked());
227
228 GrTextureEntry* next = entry->fNext;
229 if (kAbandonTexture_DeleteMode == mode) {
230 entry->texture()->abandon();
231 }
232 delete entry;
233 entry = next;
234 }
235
236 fCache.removeAll();
237 fHead = fTail = NULL;
238 fEntryCount = 0;
239 fEntryBytes = 0;
240}
241
242///////////////////////////////////////////////////////////////////////////////
243
244#if GR_DEBUG
245static int countMatches(const GrTextureEntry* head, const GrTextureEntry* target) {
246 const GrTextureEntry* entry = head;
247 int count = 0;
248 while (entry) {
249 if (target == entry) {
250 count += 1;
251 }
252 entry = entry->next();
253 }
254 return count;
255}
256
257void GrTextureCache::validate() const {
258 GrAssert(!fHead == !fTail);
259 GrAssert(!fEntryCount == !fEntryBytes);
260 GrAssert(!fClientDetachedBytes == !fClientDetachedBytes);
261 GrAssert(fClientDetachedBytes <= fEntryBytes);
262 GrAssert(fClientDetachedCount <= fEntryCount);
263 GrAssert((fEntryCount - fClientDetachedCount) == fCache.count());
264 GrAssert(fEntryBytes >= 0);
265 GrAssert(fEntryCount >= 0);
266 GrAssert(fClientDetachedCount >= 0);
267 GrAssert(fClientDetachedBytes >= 0);
268
269 fCache.validate();
270
271 GrTextureEntry* entry = fHead;
272 int count = 0;
273 size_t bytes = 0;
274 while (entry) {
275 entry->validate();
276 GrAssert(fCache.find(entry->key()));
277 count += 1;
278 bytes += entry->texture()->sizeInBytes();
279 entry = entry->fNext;
280 }
281 GrAssert(count == fEntryCount - fClientDetachedCount);
282 GrAssert(bytes == fEntryBytes - fClientDetachedBytes);
283
284 count = 0;
285 for (entry = fTail; entry; entry = entry->fPrev) {
286 count += 1;
287 }
288 GrAssert(count == fEntryCount - fClientDetachedCount);
289
290 for (int i = 0; i < count; i++) {
291 int matches = countMatches(fHead, fCache.getArray()[i]);
292 GrAssert(1 == matches);
293 }
294}
295#endif
296
297