• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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