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