blob: 68e1daab0492d9d33d9020c9159f3cc2dce2fac7 [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#ifndef GrTextureCache_DEFINED
19#define GrTextureCache_DEFINED
20
21#include "GrTypes.h"
22#include "GrTHashCache.h"
23
24class GrTexture;
25
26// return true if a<b, or false if b<a
27//
28#define RET_IF_LT_OR_GT(a, b) \
29 do { \
30 if ((a) < (b)) { \
31 return true; \
32 } \
33 if ((b) < (a)) { \
34 return false; \
35 } \
36 } while (0)
37
38/**
39 * Helper class for GrTextureCache, the Key is used to identify src data for
40 * a texture. It is identified by 2 32bit data fields which can hold any
41 * data (uninterpreted by the cache) and a width/height.
42 */
43class GrTextureKey {
44public:
45 enum {
46 kHashBits = 7,
47 kHashCount = 1 << kHashBits,
48 kHashMask = kHashCount - 1
49 };
50
51 GrTextureKey(uint32_t p0, uint32_t p1, uint16_t width, uint16_t height) {
52 fP0 = p0;
53 fP1 = p1;
54 fP2 = width | (height << 16);
55 GR_DEBUGCODE(fHashIndex = -1);
56 }
57
58 GrTextureKey(const GrTextureKey& src) {
59 fP0 = src.fP0;
60 fP1 = src.fP1;
61 fP2 = src.fP2;
62 finalize(src.fPrivateBits);
63 }
64
65 //!< returns hash value [0..kHashMask] for the key
66 int hashIndex() const { return fHashIndex; }
67
68 friend bool operator==(const GrTextureKey& a, const GrTextureKey& b) {
69 GR_DEBUGASSERT(-1 != a.fHashIndex && -1 != b.fHashIndex);
reed@google.com01804b42011-01-18 21:50:41 +000070 return a.fP0 == b.fP0 && a.fP1 == b.fP1 && a.fP2 == b.fP2 &&
reed@google.comac10a2d2010-12-22 21:39:39 +000071 a.fPrivateBits == b.fPrivateBits;
72 }
73
74 friend bool operator!=(const GrTextureKey& a, const GrTextureKey& b) {
75 GR_DEBUGASSERT(-1 != a.fHashIndex && -1 != b.fHashIndex);
76 return !(a == b);
77 }
78
79 friend bool operator<(const GrTextureKey& a, const GrTextureKey& b) {
80 RET_IF_LT_OR_GT(a.fP0, b.fP0);
81 RET_IF_LT_OR_GT(a.fP1, b.fP1);
82 RET_IF_LT_OR_GT(a.fP2, b.fP2);
83 return a.fPrivateBits < b.fPrivateBits;
84 }
reed@google.com01804b42011-01-18 21:50:41 +000085
reed@google.comac10a2d2010-12-22 21:39:39 +000086private:
reed@google.com01804b42011-01-18 21:50:41 +000087 void finalize(uint32_t privateBits) {
reed@google.comac10a2d2010-12-22 21:39:39 +000088 fPrivateBits = privateBits;
89 this->computeHashIndex();
90 }
reed@google.com01804b42011-01-18 21:50:41 +000091
reed@google.comac10a2d2010-12-22 21:39:39 +000092 uint16_t width() const { return fP2 & 0xffff; }
93 uint16_t height() const { return (fP2 >> 16); }
reed@google.com01804b42011-01-18 21:50:41 +000094
reed@google.comac10a2d2010-12-22 21:39:39 +000095 static uint32_t rol(uint32_t x) {
96 return (x >> 24) | (x << 8);
97 }
98 static uint32_t ror(uint32_t x) {
99 return (x >> 8) | (x << 24);
100 }
101 static uint32_t rohalf(uint32_t x) {
102 return (x >> 16) | (x << 16);
103 }
reed@google.com01804b42011-01-18 21:50:41 +0000104
reed@google.comac10a2d2010-12-22 21:39:39 +0000105 void computeHashIndex() {
106 uint32_t hash = fP0 ^ rol(fP1) ^ ror(fP2) ^ rohalf(fPrivateBits);
107 // this way to mix and reduce hash to its index may have to change
108 // depending on how many bits we allocate to the index
109 hash ^= hash >> 16;
110 hash ^= hash >> 8;
111 fHashIndex = hash & kHashMask;
112 }
113
114 uint32_t fP0;
115 uint32_t fP1;
116 uint32_t fP2;
117 uint32_t fPrivateBits;
118
119 // this is computed from the fP... fields
120 int fHashIndex;
reed@google.com01804b42011-01-18 21:50:41 +0000121
reed@google.comac10a2d2010-12-22 21:39:39 +0000122 friend class GrContext;
123};
124
125///////////////////////////////////////////////////////////////////////////////
126
127class GrTextureEntry {
128public:
129 GrTexture* texture() const { return fTexture; }
130 const GrTextureKey& key() const { return fKey; }
131
132#if GR_DEBUG
133 GrTextureEntry* next() const { return fNext; }
134 GrTextureEntry* prev() const { return fPrev; }
135#endif
136
137#if GR_DEBUG
138 void validate() const;
139#else
140 void validate() const {}
141#endif
142
143private:
144 GrTextureEntry(const GrTextureKey& key, GrTexture* texture);
145 ~GrTextureEntry();
146
147 bool isLocked() const { return fLockCount != 0; }
148 void lock() { ++fLockCount; }
149 void unlock() {
150 GrAssert(fLockCount > 0);
151 --fLockCount;
152 }
153
154 GrTextureKey fKey;
155 GrTexture* fTexture;
156
157 // track if we're in use, used when we need to purge
158 // we only purge unlocked entries
159 int fLockCount;
160
161 // we're a dlinklist
162 GrTextureEntry* fPrev;
163 GrTextureEntry* fNext;
164
165 friend class GrTextureCache;
166};
167
168///////////////////////////////////////////////////////////////////////////////
169
170#include "GrTHashCache.h"
171
172/**
173 * Cache of GrTexture objects.
174 *
175 * These have a corresponding GrTextureKey, built from 96bits identifying the
176 * texture/bitmap.
177 *
178 * The cache stores the entries in a double-linked list, which is its LRU.
179 * When an entry is "locked" (i.e. given to the caller), it is moved to the
180 * head of the list. If/when we must purge some of the entries, we walk the
181 * list backwards from the tail, since those are the least recently used.
182 *
183 * For fast searches, we maintain a sorted array (based on the GrTextureKey)
184 * which we can bsearch. When a new entry is added, it is inserted into this
185 * array.
186 *
187 * For even faster searches, a hash is computed from the Key. If there is
188 * a collision between two keys with the same hash, we fall back on the
189 * bsearch, and update the hash to reflect the most recent Key requested.
190 */
191class GrTextureCache {
192public:
193 GrTextureCache(int maxCount, size_t maxBytes);
194 ~GrTextureCache(); // uses kFreeTexture_DeleteMode
195
196 /**
reed@google.com01804b42011-01-18 21:50:41 +0000197 * Return the current texture cache limits.
198 *
199 * @param maxTextures If non-null, returns maximum number of textures that
200 * can be held in the cache.
201 * @param maxTextureBytes If non-null, returns maximum number of bytes of
202 * texture memory that can be held in the cache.
203 */
204 void getLimits(int* maxTextures, size_t* maxTextureBytes) const;
205
206 /**
207 * Specify the texture cache limits. If the current cache exceeds either
208 * of these, it will be purged (LRU) to keep the cache within these limits.
209 *
210 * @param maxTextures The maximum number of textures that can be held in
211 * the cache.
212 * @param maxTextureBytes The maximum number of bytes of texture memory
213 * that can be held in the cache.
214 */
215 void setLimits(int maxTextures, size_t maxTextureBytes);
216
217 /**
reed@google.comac10a2d2010-12-22 21:39:39 +0000218 * Search for an entry with the same Key. If found, "lock" it and return it.
219 * If not found, return null.
220 */
221 GrTextureEntry* findAndLock(const GrTextureKey&);
222
223 /**
224 * Create a new entry, based on the specified key and texture, and return
225 * its "locked" entry.
226 *
227 * Ownership of the texture is transferred to the Entry, which will unref()
228 * it when we are purged or deleted.
229 */
230 GrTextureEntry* createAndLock(const GrTextureKey&, GrTexture*);
reed@google.com01804b42011-01-18 21:50:41 +0000231
reed@google.comac10a2d2010-12-22 21:39:39 +0000232 /**
reed@google.com01804b42011-01-18 21:50:41 +0000233 * Detach removes an entry from the cache. This prevents the entry from
234 * being found by a subsequent findAndLock() until it is reattached. The
reed@google.comac10a2d2010-12-22 21:39:39 +0000235 * entry still counts against the cache's budget and should be reattached
236 * when exclusive access is no longer needed.
237 */
238 void detach(GrTextureEntry*);
reed@google.com01804b42011-01-18 21:50:41 +0000239
reed@google.comac10a2d2010-12-22 21:39:39 +0000240 /**
reed@google.com01804b42011-01-18 21:50:41 +0000241 * Reattaches a texture to the cache and unlocks it. Allows it to be found
242 * by a subsequent findAndLock or be purged (provided its lock count is
reed@google.comac10a2d2010-12-22 21:39:39 +0000243 * now 0.)
244 */
245 void reattachAndUnlock(GrTextureEntry*);
246
247 /**
248 * When done with an entry, call unlock(entry) on it, which returns it to
249 * a purgable state.
250 */
251 void unlock(GrTextureEntry*);
252
253 enum DeleteMode {
254 kFreeTexture_DeleteMode,
255 kAbandonTexture_DeleteMode
256 };
257 void deleteAll(DeleteMode);
258
259#if GR_DEBUG
260 void validate() const;
261#else
262 void validate() const {}
263#endif
264
265private:
266 void internalDetach(GrTextureEntry*, bool);
267 void attachToHead(GrTextureEntry*, bool);
268 void purgeAsNeeded(); // uses kFreeTexture_DeleteMode
269
270 class Key;
271 GrTHashTable<GrTextureEntry, Key, 8> fCache;
272
273 // manage the dlink list
274 GrTextureEntry* fHead;
275 GrTextureEntry* fTail;
276
277 // our budget, used in purgeAsNeeded()
reed@google.com01804b42011-01-18 21:50:41 +0000278 int fMaxCount;
279 size_t fMaxBytes;
reed@google.comac10a2d2010-12-22 21:39:39 +0000280
281 // our current stats, related to our budget
282 int fEntryCount;
283 size_t fEntryBytes;
284 int fClientDetachedCount;
285 size_t fClientDetachedBytes;
286};
287
288///////////////////////////////////////////////////////////////////////////////
289
290#if GR_DEBUG
291 class GrAutoTextureCacheValidate {
292 public:
293 GrAutoTextureCacheValidate(GrTextureCache* cache) : fCache(cache) {
294 cache->validate();
295 }
296 ~GrAutoTextureCacheValidate() {
297 fCache->validate();
298 }
299 private:
300 GrTextureCache* fCache;
301 };
302#else
303 class GrAutoTextureCacheValidate {
304 public:
305 GrAutoTextureCacheValidate(GrTextureCache*) {}
306 };
307#endif
308
309#endif
310