• 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 "GrConfig.h"
15 #include "GrTypes.h"
16 #include "GrTHashCache.h"
17 #include "GrBinHashKey.h"
18 #include "SkTInternalLList.h"
19 
20 class GrResource;
21 class GrResourceEntry;
22 
23 class GrResourceKey {
24 public:
25     enum {
26         kHashBits   = 7,
27         kHashCount  = 1 << kHashBits,
28         kHashMask   = kHashCount - 1
29     };
30 
ScratchDomain()31     static GrCacheID::Domain ScratchDomain() {
32         static const GrCacheID::Domain gDomain = GrCacheID::GenerateDomain();
33         return gDomain;
34     }
35 
36     /** Uniquely identifies the GrResource subclass in the key to avoid collisions
37         across resource types. */
38     typedef uint8_t ResourceType;
39 
40     /** Flags set by the GrResource subclass. */
41     typedef uint8_t ResourceFlags;
42 
43     /** Generate a unique ResourceType */
44     static ResourceType GenerateResourceType();
45 
46     /** Creates a key for resource */
GrResourceKey(const GrCacheID & id,ResourceType type,ResourceFlags flags)47     GrResourceKey(const GrCacheID& id, ResourceType type, ResourceFlags flags) {
48         this->init(id.getDomain(), id.getKey(), type, flags);
49     };
50 
GrResourceKey(const GrResourceKey & src)51     GrResourceKey(const GrResourceKey& src) {
52         fKey = src.fKey;
53     }
54 
GrResourceKey()55     GrResourceKey() {
56         fKey.fHashedKey.reset();
57     }
58 
reset(const GrCacheID & id,ResourceType type,ResourceFlags flags)59     void reset(const GrCacheID& id, ResourceType type, ResourceFlags flags) {
60         this->init(id.getDomain(), id.getKey(), type, flags);
61     }
62 
63     //!< returns hash value [0..kHashMask] for the key
getHash()64     int getHash() const {
65         return fKey.fHashedKey.getHash() & kHashMask;
66     }
67 
isScratch()68     bool isScratch() const {
69         return ScratchDomain() ==
70             *reinterpret_cast<const GrCacheID::Domain*>(fKey.fHashedKey.getData() +
71                                                         kCacheIDDomainOffset);
72     }
73 
getResourceType()74     ResourceType getResourceType() const {
75         return *reinterpret_cast<const ResourceType*>(fKey.fHashedKey.getData() +
76                                                       kResourceTypeOffset);
77     }
78 
getResourceFlags()79     ResourceFlags getResourceFlags() const {
80         return *reinterpret_cast<const ResourceFlags*>(fKey.fHashedKey.getData() +
81                                                        kResourceFlagsOffset);
82     }
83 
compare(const GrResourceKey & other)84     int compare(const GrResourceKey& other) const {
85         return fKey.fHashedKey.compare(other.fKey.fHashedKey);
86     }
87 
LT(const GrResourceKey & a,const GrResourceKey & b)88     static bool LT(const GrResourceKey& a, const GrResourceKey& b) {
89         return a.compare(b) < 0;
90     }
91 
EQ(const GrResourceKey & a,const GrResourceKey & b)92     static bool EQ(const GrResourceKey& a, const GrResourceKey& b) {
93         return 0 == a.compare(b);
94     }
95 
96     inline static bool LT(const GrResourceEntry& entry, const GrResourceKey& key);
97     inline static bool EQ(const GrResourceEntry& entry, const GrResourceKey& key);
98     inline static bool LT(const GrResourceEntry& a, const GrResourceEntry& b);
99     inline static bool EQ(const GrResourceEntry& a, const GrResourceEntry& b);
100 
101 private:
102     enum {
103         kCacheIDKeyOffset = 0,
104         kCacheIDDomainOffset = kCacheIDKeyOffset + sizeof(GrCacheID::Key),
105         kResourceTypeOffset = kCacheIDDomainOffset + sizeof(GrCacheID::Domain),
106         kResourceFlagsOffset = kResourceTypeOffset + sizeof(ResourceType),
107         kPadOffset = kResourceFlagsOffset + sizeof(ResourceFlags),
108         kKeySize = SkAlign4(kPadOffset),
109         kPadSize = kKeySize - kPadOffset
110     };
111 
init(const GrCacheID::Domain domain,const GrCacheID::Key & key,ResourceType type,ResourceFlags flags)112     void init(const GrCacheID::Domain domain,
113               const GrCacheID::Key& key,
114               ResourceType type,
115               ResourceFlags flags) {
116         union {
117             uint8_t  fKey8[kKeySize];
118             uint32_t fKey32[kKeySize / 4];
119         } keyData;
120 
121         uint8_t* k = keyData.fKey8;
122         memcpy(k + kCacheIDKeyOffset, key.fData8, sizeof(GrCacheID::Key));
123         memcpy(k + kCacheIDDomainOffset, &domain, sizeof(GrCacheID::Domain));
124         memcpy(k + kResourceTypeOffset, &type, sizeof(ResourceType));
125         memcpy(k + kResourceFlagsOffset, &flags, sizeof(ResourceFlags));
126         memset(k + kPadOffset, 0, kPadSize);
127         fKey.fHashedKey.setKeyData(keyData.fKey32);
128     }
129 
130     struct Key;
131     typedef GrTBinHashKey<Key, kKeySize> HashedKey;
132 
133     struct Key {
compareKey134         int compare(const HashedKey& hashedKey) const {
135             return fHashedKey.compare(fHashedKey);
136         }
137 
138         HashedKey fHashedKey;
139     };
140 
141     Key fKey;
142 };
143 
144 ///////////////////////////////////////////////////////////////////////////////
145 
146 class GrResourceEntry {
147 public:
resource()148     GrResource* resource() const { return fResource; }
key()149     const GrResourceKey& key() const { return fKey; }
150 
151 #if GR_DEBUG
152     void validate() const;
153 #else
validate()154     void validate() const {}
155 #endif
156 
157 private:
158     GrResourceEntry(const GrResourceKey& key, GrResource* resource);
159     ~GrResourceEntry();
160 
161     GrResourceKey    fKey;
162     GrResource*      fResource;
163 
164     // we're a linked list
165     SK_DECLARE_INTERNAL_LLIST_INTERFACE(GrResourceEntry);
166 
167     friend class GrResourceCache;
168     friend class GrDLinkedList;
169 };
170 
LT(const GrResourceEntry & entry,const GrResourceKey & key)171 bool GrResourceKey::LT(const GrResourceEntry& entry, const GrResourceKey& key) {
172     return LT(entry.key(), key);
173 }
174 
EQ(const GrResourceEntry & entry,const GrResourceKey & key)175 bool GrResourceKey::EQ(const GrResourceEntry& entry, const GrResourceKey& key) {
176     return EQ(entry.key(), key);
177 }
178 
LT(const GrResourceEntry & a,const GrResourceEntry & b)179 bool GrResourceKey::LT(const GrResourceEntry& a, const GrResourceEntry& b) {
180     return LT(a.key(), b.key());
181 }
182 
EQ(const GrResourceEntry & a,const GrResourceEntry & b)183 bool GrResourceKey::EQ(const GrResourceEntry& a, const GrResourceEntry& b) {
184     return EQ(a.key(), b.key());
185 }
186 
187 ///////////////////////////////////////////////////////////////////////////////
188 
189 #include "GrTHashCache.h"
190 
191 /**
192  *  Cache of GrResource objects.
193  *
194  *  These have a corresponding GrResourceKey, built from 128bits identifying the
195  *  resource.
196  *
197  *  The cache stores the entries in a double-linked list, which is its LRU.
198  *  When an entry is "locked" (i.e. given to the caller), it is moved to the
199  *  head of the list. If/when we must purge some of the entries, we walk the
200  *  list backwards from the tail, since those are the least recently used.
201  *
202  *  For fast searches, we maintain a sorted array (based on the GrResourceKey)
203  *  which we can bsearch. When a new entry is added, it is inserted into this
204  *  array.
205  *
206  *  For even faster searches, a hash is computed from the Key. If there is
207  *  a collision between two keys with the same hash, we fall back on the
208  *  bsearch, and update the hash to reflect the most recent Key requested.
209  */
210 class GrResourceCache {
211 public:
212     GrResourceCache(int maxCount, size_t maxBytes);
213     ~GrResourceCache();
214 
215     /**
216      *  Return the current resource cache limits.
217      *
218      *  @param maxResource If non-null, returns maximum number of resources
219      *                     that can be held in the cache.
220      *  @param maxBytes    If non-null, returns maximum number of bytes of
221      *                         gpu memory that can be held in the cache.
222      */
223     void getLimits(int* maxResources, size_t* maxBytes) const;
224 
225     /**
226      *  Specify the resource cache limits. If the current cache exceeds either
227      *  of these, it will be purged (LRU) to keep the cache within these limits.
228      *
229      *  @param maxResources The maximum number of resources that can be held in
230      *                      the cache.
231      *  @param maxBytes     The maximum number of bytes of resource memory that
232      *                      can be held in the cache.
233      */
234     void setLimits(int maxResource, size_t maxResourceBytes);
235 
236     /**
237      * Returns the number of bytes consumed by cached resources.
238      */
getCachedResourceBytes()239     size_t getCachedResourceBytes() const { return fEntryBytes; }
240 
241     // For a found or added resource to be completely exclusive to the caller
242     // both the kNoOtherOwners and kHide flags need to be specified
243     enum OwnershipFlags {
244         kNoOtherOwners_OwnershipFlag = 0x1, // found/added resource has no other owners
245         kHide_OwnershipFlag = 0x2  // found/added resource is hidden from future 'find's
246     };
247 
248     /**
249      *  Search for an entry with the same Key. If found, return it.
250      *  If not found, return null.
251      *  If ownershipFlags includes kNoOtherOwners and a resource is returned
252      *  then that resource has no other refs to it.
253      *  If ownershipFlags includes kHide and a resource is returned then that
254      *  resource will not be returned from future 'find' calls until it is
255      *  'freed' (and recycled) or makeNonExclusive is called.
256      *  For a resource to be completely exclusive to a caller both kNoOtherOwners
257      *  and kHide must be specified.
258      */
259     GrResource* find(const GrResourceKey& key,
260                      uint32_t ownershipFlags = 0);
261 
262     /**
263      *  Add the new resource to the cache (by creating a new cache entry based
264      *  on the provided key and resource).
265      *
266      *  Ownership of the resource is transferred to the resource cache,
267      *  which will unref() it when it is purged or deleted.
268      *
269      *  If ownershipFlags includes kHide, subsequent calls to 'find' will not
270      *  return 'resource' until it is 'freed' (and recycled) or makeNonExclusive
271      *  is called.
272      */
273     void addResource(const GrResourceKey& key,
274                      GrResource* resource,
275                      uint32_t ownershipFlags = 0);
276 
277     /**
278      * Determines if the cache contains an entry matching a key. If a matching
279      * entry exists but was detached then it will not be found.
280      */
281     bool hasKey(const GrResourceKey& key) const;
282 
283     /**
284      * Hide 'entry' so that future searches will not find it. Such
285      * hidden entries will not be purged. The entry still counts against
286      * the cache's budget and should be made non-exclusive when exclusive access
287      * is no longer needed.
288      */
289     void makeExclusive(GrResourceEntry* entry);
290 
291     /**
292      * Restore 'entry' so that it can be found by future searches. 'entry'
293      * will also be purgeable (provided its lock count is now 0.)
294      */
295     void makeNonExclusive(GrResourceEntry* entry);
296 
297     /**
298      * Removes every resource in the cache that isn't locked.
299      */
300     void purgeAllUnlocked();
301 
302     /**
303      * Allow cache to purge unused resources to obey resource limitations
304      * Note: this entry point will be hidden (again) once totally ref-driven
305      * cache maintenance is implemented
306      */
307     void purgeAsNeeded();
308 
309 #if GR_DEBUG
310     void validate() const;
311 #else
validate()312     void validate() const {}
313 #endif
314 
315 #if GR_CACHE_STATS
316     void printStats();
317 #endif
318 
319 private:
320     enum BudgetBehaviors {
321         kAccountFor_BudgetBehavior,
322         kIgnore_BudgetBehavior
323     };
324 
325     void internalDetach(GrResourceEntry*, BudgetBehaviors behavior = kAccountFor_BudgetBehavior);
326     void attachToHead(GrResourceEntry*, BudgetBehaviors behavior = kAccountFor_BudgetBehavior);
327 
328     void removeInvalidResource(GrResourceEntry* entry);
329 
330     GrTHashTable<GrResourceEntry, GrResourceKey, 8> fCache;
331 
332     // We're an internal doubly linked list
333     typedef SkTInternalLList<GrResourceEntry> EntryList;
334     EntryList    fList;
335 
336 #if GR_DEBUG
337     // These objects cannot be returned by a search
338     EntryList    fExclusiveList;
339 #endif
340 
341     // our budget, used in purgeAsNeeded()
342     int fMaxCount;
343     size_t fMaxBytes;
344 
345     // our current stats, related to our budget
346 #if GR_CACHE_STATS
347     int fHighWaterEntryCount;
348     size_t fHighWaterEntryBytes;
349     int fHighWaterClientDetachedCount;
350     size_t fHighWaterClientDetachedBytes;
351 #endif
352 
353     int fEntryCount;
354     size_t fEntryBytes;
355     int fClientDetachedCount;
356     size_t fClientDetachedBytes;
357 
358     // prevents recursive purging
359     bool fPurging;
360 
361 #if GR_DEBUG
362     static size_t countBytes(const SkTInternalLList<GrResourceEntry>& list);
363 #endif
364 };
365 
366 ///////////////////////////////////////////////////////////////////////////////
367 
368 #if GR_DEBUG
369     class GrAutoResourceCacheValidate {
370     public:
GrAutoResourceCacheValidate(GrResourceCache * cache)371         GrAutoResourceCacheValidate(GrResourceCache* cache) : fCache(cache) {
372             cache->validate();
373         }
~GrAutoResourceCacheValidate()374         ~GrAutoResourceCacheValidate() {
375             fCache->validate();
376         }
377     private:
378         GrResourceCache* fCache;
379     };
380 #else
381     class GrAutoResourceCacheValidate {
382     public:
GrAutoResourceCacheValidate(GrResourceCache *)383         GrAutoResourceCacheValidate(GrResourceCache*) {}
384     };
385 #endif
386 
387 #endif
388