Home | History | Annotate | Download | only in gpu
      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 
     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 
     52     GrResourceKey(uint32_t v[4]) {
     53         memcpy(fP, v, 4 * sizeof(uint32_t));
     54         this->computeHashIndex();
     55     }
     56 
     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
     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 
     86     uint32_t getValue32(int i) const {
     87         GrAssert(i >=0 && i < 4);
     88         return fP[i];
     89     }
     90 private:
     91 
     92     static uint32_t rol(uint32_t x) {
     93         return (x >> 24) | (x << 8);
     94     }
     95     static uint32_t ror(uint32_t x) {
     96         return (x >> 8) | (x << 24);
     97     }
     98     static uint32_t rohalf(uint32_t x) {
     99         return (x >> 16) | (x << 16);
    100     }
    101 
    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:
    123     GrResource* resource() const { return fResource; }
    124     const GrResourceKey& key() const { return fKey; }
    125 
    126 #if GR_DEBUG
    127     GrResourceEntry* next() const { return fNext; }
    128     GrResourceEntry* prev() const { return fPrev; }
    129 #endif
    130 
    131 #if GR_DEBUG
    132     void validate() const;
    133 #else
    134     void validate() const {}
    135 #endif
    136 
    137 private:
    138     GrResourceEntry(const GrResourceKey& key, GrResource* resource);
    139     ~GrResourceEntry();
    140 
    141     bool isLocked() const { return fLockCount != 0; }
    142     void lock() { ++fLockCount; }
    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      */
    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
    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:
    306         GrAutoResourceCacheValidate(GrResourceCache* cache) : fCache(cache) {
    307             cache->validate();
    308         }
    309         ~GrAutoResourceCacheValidate() {
    310             fCache->validate();
    311         }
    312     private:
    313         GrResourceCache* fCache;
    314     };
    315 #else
    316     class GrAutoResourceCacheValidate {
    317     public:
    318         GrAutoResourceCacheValidate(GrResourceCache*) {}
    319     };
    320 #endif
    321 
    322 #endif
    323