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