1 /*
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
GrTextureEntry(const GrTextureKey & key,GrTexture * texture)21 GrTextureEntry::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
~GrTextureEntry()30 GrTextureEntry::~GrTextureEntry() {
31 fTexture->unref();
32 }
33
34 #if GR_DEBUG
validate() const35 void GrTextureEntry::validate() const {
36 GrAssert(fLockCount >= 0);
37 GrAssert(fTexture);
38 fTexture->validate();
39 }
40 #endif
41
42 ///////////////////////////////////////////////////////////////////////////////
43
GrTextureCache(int maxCount,size_t maxBytes)44 GrTextureCache::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
~GrTextureCache()55 GrTextureCache::~GrTextureCache() {
56 GrAutoTextureCacheValidate atcv(this);
57
58 this->removeAll();
59 }
60
getLimits(int * maxTextures,size_t * maxTextureBytes) const61 void GrTextureCache::getLimits(int* maxTextures, size_t* maxTextureBytes) const{
62 if (maxTextures) {
63 *maxTextures = fMaxCount;
64 }
65 if (maxTextureBytes) {
66 *maxTextureBytes = fMaxBytes;
67 }
68 }
69
setLimits(int maxTextures,size_t maxTextureBytes)70 void 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
internalDetach(GrTextureEntry * entry,bool clientDetach)81 void GrTextureCache::internalDetach(GrTextureEntry* entry,
82 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
attachToHead(GrTextureEntry * entry,bool clientReattach)107 void GrTextureCache::attachToHead(GrTextureEntry* entry,
108 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
129 class GrTextureCache::Key {
130 typedef GrTextureEntry T;
131
132 const GrTextureKey& fKey;
133 public:
Key(const GrTextureKey & key)134 Key(const GrTextureKey& key) : fKey(key) {}
135
getHash() const136 uint32_t getHash() const { return fKey.hashIndex(); }
137
LT(const T & entry,const Key & key)138 static bool LT(const T& entry, const Key& key) {
139 return entry.key() < key.fKey;
140 }
EQ(const T & entry,const Key & key)141 static bool EQ(const T& entry, const Key& key) {
142 return entry.key() == key.fKey;
143 }
144 #if GR_DEBUG
GetHash(const T & entry)145 static uint32_t GetHash(const T& entry) {
146 return entry.key().hashIndex();
147 }
LT(const T & a,const T & b)148 static bool LT(const T& a, const T& b) {
149 return a.key() < b.key();
150 }
EQ(const T & a,const T & b)151 static bool EQ(const T& a, const T& b) {
152 return a.key() == b.key();
153 }
154 #endif
155 };
156
findAndLock(const GrTextureKey & key)157 GrTextureEntry* 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
createAndLock(const GrTextureKey & key,GrTexture * texture)170 GrTextureEntry* 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
detach(GrTextureEntry * entry)190 void GrTextureCache::detach(GrTextureEntry* entry) {
191 internalDetach(entry, true);
192 fCache.remove(entry->fKey, entry);
193 }
194
reattachAndUnlock(GrTextureEntry * entry)195 void GrTextureCache::reattachAndUnlock(GrTextureEntry* entry) {
196 attachToHead(entry, true);
197 fCache.insert(entry->key(), entry);
198 unlock(entry);
199 }
200
unlock(GrTextureEntry * entry)201 void GrTextureCache::unlock(GrTextureEntry* entry) {
202 GrAutoTextureCacheValidate atcv(this);
203
204 GrAssert(entry);
205 GrAssert(entry->isLocked());
206 GrAssert(fCache.find(entry->key()));
207
208 entry->unlock();
209 this->purgeAsNeeded();
210 }
211
purgeAsNeeded()212 void GrTextureCache::purgeAsNeeded() {
213 GrAutoTextureCacheValidate atcv(this);
214
215 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);
225
226 // 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()->width(),
232 entry->texture()->height());
233 #endif
234 delete entry;
235 }
236 entry = prev;
237 }
238 }
239
removeAll()240 void GrTextureCache::removeAll() {
241 GrAssert(!fClientDetachedCount);
242 GrAssert(!fClientDetachedBytes);
243
244 GrTextureEntry* entry = fHead;
245 while (entry) {
246 GrAssert(!entry->isLocked());
247
248 GrTextureEntry* next = entry->fNext;
249 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
countMatches(const GrTextureEntry * head,const GrTextureEntry * target)262 static 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
274 #if GR_DEBUG
both_zero_or_nonzero(int count,size_t bytes)275 static bool both_zero_or_nonzero(int count, size_t bytes) {
276 return (count == 0 && bytes == 0) || (count > 0 && bytes > 0);
277 }
278 #endif
279
validate() const280 void GrTextureCache::validate() const {
281 GrAssert(!fHead == !fTail);
282 GrAssert(both_zero_or_nonzero(fEntryCount, fEntryBytes));
283 GrAssert(both_zero_or_nonzero(fClientDetachedCount, fClientDetachedBytes));
284 GrAssert(fClientDetachedBytes <= fEntryBytes);
285 GrAssert(fClientDetachedCount <= fEntryCount);
286 GrAssert((fEntryCount - fClientDetachedCount) == fCache.count());
287
288 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
317