• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 /*
3  * Copyright 2011 Google Inc.
4  *
5  * Use of this source code is governed by a BSD-style license that can be
6  * found in the LICENSE file.
7  */
8 
9 
10 
11 #ifndef GrResourceCache_DEFINED
12 #define GrResourceCache_DEFINED
13 
14 #include "GrTypes.h"
15 #include "GrTHashCache.h"
16 
17 class GrResource;
18 
19 // return true if a<b, or false if b<a
20 //
21 #define RET_IF_LT_OR_GT(a, b)   \
22     do {                        \
23         if ((a) < (b)) {        \
24             return true;        \
25         }                       \
26         if ((b) < (a)) {        \
27             return false;       \
28         }                       \
29     } while (0)
30 
31 /**
32  *  Helper class for GrResourceCache, the Key is used to identify src data for
33  *  a resource. It is identified by 2 32bit data fields which can hold any
34  *  data (uninterpreted by the cache) and a width/height.
35  */
36 class GrResourceKey {
37 public:
38     enum {
39         kHashBits   = 7,
40         kHashCount  = 1 << kHashBits,
41         kHashMask   = kHashCount - 1
42     };
43 
GrResourceKey(uint32_t p0,uint32_t p1,uint32_t p2,uint32_t p3)44     GrResourceKey(uint32_t p0, uint32_t p1, uint32_t p2, uint32_t p3) {
45         fP[0] = p0;
46         fP[1] = p1;
47         fP[2] = p2;
48         fP[3] = p3;
49         this->computeHashIndex();
50     }
51 
GrResourceKey(uint32_t v[4])52     GrResourceKey(uint32_t v[4]) {
53         memcpy(fP, v, 4 * sizeof(uint32_t));
54         this->computeHashIndex();
55     }
56 
GrResourceKey(const GrResourceKey & src)57     GrResourceKey(const GrResourceKey& src) {
58         memcpy(fP, src.fP, 4 * sizeof(uint32_t));
59 #if GR_DEBUG
60         this->computeHashIndex();
61         GrAssert(fHashIndex == src.fHashIndex);
62 #endif
63         fHashIndex = src.fHashIndex;
64     }
65 
66     //!< returns hash value [0..kHashMask] for the key
hashIndex()67     int hashIndex() const { return fHashIndex; }
68 
69     friend bool operator==(const GrResourceKey& a, const GrResourceKey& b) {
70         GR_DEBUGASSERT(-1 != a.fHashIndex && -1 != b.fHashIndex);
71         return 0 == memcmp(a.fP, b.fP, 4 * sizeof(uint32_t));
72     }
73 
74     friend bool operator!=(const GrResourceKey& a, const GrResourceKey& b) {
75         GR_DEBUGASSERT(-1 != a.fHashIndex && -1 != b.fHashIndex);
76         return !(a == b);
77     }
78 
79     friend bool operator<(const GrResourceKey& a, const GrResourceKey& b) {
80         RET_IF_LT_OR_GT(a.fP[0], b.fP[0]);
81         RET_IF_LT_OR_GT(a.fP[1], b.fP[1]);
82         RET_IF_LT_OR_GT(a.fP[2], b.fP[2]);
83         return a.fP[3] < b.fP[3];
84     }
85 
getValue32(int i)86     uint32_t getValue32(int i) const {
87         GrAssert(i >=0 && i < 4);
88         return fP[i];
89     }
90 private:
91 
rol(uint32_t x)92     static uint32_t rol(uint32_t x) {
93         return (x >> 24) | (x << 8);
94     }
ror(uint32_t x)95     static uint32_t ror(uint32_t x) {
96         return (x >> 8) | (x << 24);
97     }
rohalf(uint32_t x)98     static uint32_t rohalf(uint32_t x) {
99         return (x >> 16) | (x << 16);
100     }
101 
computeHashIndex()102     void computeHashIndex() {
103         uint32_t hash = fP[0] ^ rol(fP[1]) ^ ror(fP[2]) ^ rohalf(fP[3]);
104         // this way to mix and reduce hash to its index may have to change
105         // depending on how many bits we allocate to the index
106         hash ^= hash >> 16;
107         hash ^= hash >> 8;
108         fHashIndex = hash & kHashMask;
109     }
110 
111     uint32_t    fP[4];
112 
113     // this is computed from the fP... fields
114     int         fHashIndex;
115 
116     friend class GrContext;
117 };
118 
119 ///////////////////////////////////////////////////////////////////////////////
120 
121 class GrResourceEntry {
122 public:
resource()123     GrResource* resource() const { return fResource; }
key()124     const GrResourceKey& key() const { return fKey; }
125 
126 #if GR_DEBUG
next()127     GrResourceEntry* next() const { return fNext; }
prev()128     GrResourceEntry* prev() const { return fPrev; }
129 #endif
130 
131 #if GR_DEBUG
132     void validate() const;
133 #else
validate()134     void validate() const {}
135 #endif
136 
137 private:
138     GrResourceEntry(const GrResourceKey& key, GrResource* resource);
139     ~GrResourceEntry();
140 
isLocked()141     bool isLocked() const { return fLockCount != 0; }
lock()142     void lock() { ++fLockCount; }
unlock()143     void unlock() {
144         GrAssert(fLockCount > 0);
145         --fLockCount;
146     }
147 
148     GrResourceKey    fKey;
149     GrResource*      fResource;
150 
151     // track if we're in use, used when we need to purge
152     // we only purge unlocked entries
153     int fLockCount;
154 
155     // we're a dlinklist
156     GrResourceEntry* fPrev;
157     GrResourceEntry* fNext;
158 
159     friend class GrResourceCache;
160 };
161 
162 ///////////////////////////////////////////////////////////////////////////////
163 
164 #include "GrTHashCache.h"
165 
166 /**
167  *  Cache of GrResource objects.
168  *
169  *  These have a corresponding GrResourceKey, built from 128bits identifying the
170  *  resource.
171  *
172  *  The cache stores the entries in a double-linked list, which is its LRU.
173  *  When an entry is "locked" (i.e. given to the caller), it is moved to the
174  *  head of the list. If/when we must purge some of the entries, we walk the
175  *  list backwards from the tail, since those are the least recently used.
176  *
177  *  For fast searches, we maintain a sorted array (based on the GrResourceKey)
178  *  which we can bsearch. When a new entry is added, it is inserted into this
179  *  array.
180  *
181  *  For even faster searches, a hash is computed from the Key. If there is
182  *  a collision between two keys with the same hash, we fall back on the
183  *  bsearch, and update the hash to reflect the most recent Key requested.
184  */
185 class GrResourceCache {
186 public:
187     GrResourceCache(int maxCount, size_t maxBytes);
188     ~GrResourceCache();
189 
190     /**
191      *  Return the current resource cache limits.
192      *
193      *  @param maxResource If non-null, returns maximum number of resources
194      *                     that can be held in the cache.
195      *  @param maxBytes    If non-null, returns maximum number of bytes of
196      *                         gpu memory that can be held in the cache.
197      */
198     void getLimits(int* maxResources, size_t* maxBytes) const;
199 
200     /**
201      *  Specify the resource cache limits. If the current cache exceeds either
202      *  of these, it will be purged (LRU) to keep the cache within these limits.
203      *
204      *  @param maxResources The maximum number of resources that can be held in
205      *                      the cache.
206      *  @param maxBytes     The maximum number of bytes of resource memory that
207      *                      can be held in the cache.
208      */
209     void setLimits(int maxResource, size_t maxResourceBytes);
210 
211     /**
212      * Returns the number of bytes consumed by cached resources.
213      */
getCachedResourceBytes()214     size_t getCachedResourceBytes() const { return fEntryBytes; }
215 
216     /**
217      * Controls whether locks should be nestable or not.
218      */
219     enum LockType {
220         kNested_LockType,
221         kSingle_LockType,
222     };
223 
224     /**
225      *  Search for an entry with the same Key. If found, "lock" it and return it.
226      *  If not found, return null.
227      */
228     GrResourceEntry* findAndLock(const GrResourceKey&, LockType style);
229 
230     /**
231      *  Create a new entry, based on the specified key and resource, and return
232      *  its "locked" entry.
233      *
234      *  Ownership of the resource is transferred to the Entry, which will unref()
235      *  it when we are purged or deleted.
236      */
237     GrResourceEntry* createAndLock(const GrResourceKey&, GrResource*);
238 
239     /**
240      * Determines if the cache contains an entry matching a key. If a matching
241      * entry exists but was detached then it will not be found.
242      */
243     bool hasKey(const GrResourceKey& key) const;
244 
245     /**
246      * Detach removes an entry from the cache. This prevents the entry from
247      * being found by a subsequent findAndLock() until it is reattached. The
248      * entry still counts against the cache's budget and should be reattached
249      * when exclusive access is no longer needed.
250      */
251     void detach(GrResourceEntry*);
252 
253     /**
254      * Reattaches a resource to the cache and unlocks it. Allows it to be found
255      * by a subsequent findAndLock or be purged (provided its lock count is
256      * now 0.)
257      */
258     void reattachAndUnlock(GrResourceEntry*);
259 
260     /**
261      *  When done with an entry, call unlock(entry) on it, which returns it to
262      *  a purgable state.
263      */
264     void unlock(GrResourceEntry*);
265 
266     void removeAll();
267 
268 #if GR_DEBUG
269     void validate() const;
270 #else
validate()271     void validate() const {}
272 #endif
273 
274 private:
275     void internalDetach(GrResourceEntry*, bool);
276     void attachToHead(GrResourceEntry*, bool);
277     void purgeAsNeeded();
278 
279     class Key;
280     GrTHashTable<GrResourceEntry, Key, 8> fCache;
281 
282     // manage the dlink list
283     GrResourceEntry* fHead;
284     GrResourceEntry* fTail;
285 
286     // our budget, used in purgeAsNeeded()
287     int fMaxCount;
288     size_t fMaxBytes;
289 
290     // our current stats, related to our budget
291     int fEntryCount;
292     int fUnlockedEntryCount;
293     size_t fEntryBytes;
294     int fClientDetachedCount;
295     size_t fClientDetachedBytes;
296 
297     // prevents recursive purging
298     bool fPurging;
299 };
300 
301 ///////////////////////////////////////////////////////////////////////////////
302 
303 #if GR_DEBUG
304     class GrAutoResourceCacheValidate {
305     public:
GrAutoResourceCacheValidate(GrResourceCache * cache)306         GrAutoResourceCacheValidate(GrResourceCache* cache) : fCache(cache) {
307             cache->validate();
308         }
~GrAutoResourceCacheValidate()309         ~GrAutoResourceCacheValidate() {
310             fCache->validate();
311         }
312     private:
313         GrResourceCache* fCache;
314     };
315 #else
316     class GrAutoResourceCacheValidate {
317     public:
GrAutoResourceCacheValidate(GrResourceCache *)318         GrAutoResourceCacheValidate(GrResourceCache*) {}
319     };
320 #endif
321 
322 #endif
323