blob: e3d4f0a1008494a076fcffebac7e9016a5ef7c3f [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);
70 return a.fP0 == b.fP0 && a.fP1 == b.fP1 && a.fP2 == b.fP2 &&
71 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 }
85
86private:
87 void finalize(uint32_t privateBits) {
88 fPrivateBits = privateBits;
89 this->computeHashIndex();
90 }
91
92 uint16_t width() const { return fP2 & 0xffff; }
93 uint16_t height() const { return (fP2 >> 16); }
94
95 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 }
104
105 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;
121
122 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 /**
197 * Search for an entry with the same Key. If found, "lock" it and return it.
198 * If not found, return null.
199 */
200 GrTextureEntry* findAndLock(const GrTextureKey&);
201
202 /**
203 * Create a new entry, based on the specified key and texture, and return
204 * its "locked" entry.
205 *
206 * Ownership of the texture is transferred to the Entry, which will unref()
207 * it when we are purged or deleted.
208 */
209 GrTextureEntry* createAndLock(const GrTextureKey&, GrTexture*);
210
211 /**
212 * Detach removes an entry from the cache. This prevents the entry from
213 * being found by a subsequent findAndLock() until it is reattached. The
214 * entry still counts against the cache's budget and should be reattached
215 * when exclusive access is no longer needed.
216 */
217 void detach(GrTextureEntry*);
218
219 /**
220 * Reattaches a texture to the cache and unlocks it. Allows it to be found
221 * by a subsequent findAndLock or be purged (provided its lock count is
222 * now 0.)
223 */
224 void reattachAndUnlock(GrTextureEntry*);
225
226 /**
227 * When done with an entry, call unlock(entry) on it, which returns it to
228 * a purgable state.
229 */
230 void unlock(GrTextureEntry*);
231
232 enum DeleteMode {
233 kFreeTexture_DeleteMode,
234 kAbandonTexture_DeleteMode
235 };
236 void deleteAll(DeleteMode);
237
238#if GR_DEBUG
239 void validate() const;
240#else
241 void validate() const {}
242#endif
243
244private:
245 void internalDetach(GrTextureEntry*, bool);
246 void attachToHead(GrTextureEntry*, bool);
247 void purgeAsNeeded(); // uses kFreeTexture_DeleteMode
248
249 class Key;
250 GrTHashTable<GrTextureEntry, Key, 8> fCache;
251
252 // manage the dlink list
253 GrTextureEntry* fHead;
254 GrTextureEntry* fTail;
255
256 // our budget, used in purgeAsNeeded()
257 const int fMaxCount;
258 const size_t fMaxBytes;
259
260 // our current stats, related to our budget
261 int fEntryCount;
262 size_t fEntryBytes;
263 int fClientDetachedCount;
264 size_t fClientDetachedBytes;
265};
266
267///////////////////////////////////////////////////////////////////////////////
268
269#if GR_DEBUG
270 class GrAutoTextureCacheValidate {
271 public:
272 GrAutoTextureCacheValidate(GrTextureCache* cache) : fCache(cache) {
273 cache->validate();
274 }
275 ~GrAutoTextureCacheValidate() {
276 fCache->validate();
277 }
278 private:
279 GrTextureCache* fCache;
280 };
281#else
282 class GrAutoTextureCacheValidate {
283 public:
284 GrAutoTextureCacheValidate(GrTextureCache*) {}
285 };
286#endif
287
288#endif
289