Home | History | Annotate | Download | only in gpu
      1 
      2 /*
      3  * Copyright 2014 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 #ifndef GrResourceCache_DEFINED
     10 #define GrResourceCache_DEFINED
     11 
     12 #include "GrGpuResource.h"
     13 #include "GrGpuResourceCacheAccess.h"
     14 #include "GrGpuResourcePriv.h"
     15 #include "GrResourceKey.h"
     16 #include "SkMessageBus.h"
     17 #include "SkRefCnt.h"
     18 #include "SkTArray.h"
     19 #include "SkTDPQueue.h"
     20 #include "SkTInternalLList.h"
     21 #include "SkTMultiMap.h"
     22 
     23 class SkString;
     24 
     25 /**
     26  * Manages the lifetime of all GrGpuResource instances.
     27  *
     28  * Resources may have optionally have two types of keys:
     29  *      1) A scratch key. This is for resources whose allocations are cached but not their contents.
     30  *         Multiple resources can share the same scratch key. This is so a caller can have two
     31  *         resource instances with the same properties (e.g. multipass rendering that ping-pongs
     32  *         between two temporary surfaces). The scratch key is set at resource creation time and
     33  *         should never change. Resources need not have a scratch key.
     34  *      2) A unique key. This key's meaning is specific to the domain that created the key. Only one
     35  *         resource may have a given unique key. The unique key can be set, cleared, or changed
     36  *         anytime after resource creation.
     37  *
     38  * A unique key always takes precedence over a scratch key when a resource has both types of keys.
     39  * If a resource has neither key type then it will be deleted as soon as the last reference to it
     40  * is dropped.
     41  *
     42  * When proactive purging is enabled, on every flush, the timestamp of that flush is stored in a
     43  * n-sized ring buffer. When purging occurs each purgeable resource's timestamp is compared to the
     44  * timestamp of the n-th prior flush. If the resource's last use timestamp is older than the old
     45  * flush then the resource is proactively purged even when the cache is under budget. By default
     46  * this feature is disabled, though it can be enabled by calling GrResourceCache::setLimits.
     47  */
     48 class GrResourceCache {
     49 public:
     50     GrResourceCache();
     51     ~GrResourceCache();
     52 
     53     // Default maximum number of budgeted resources in the cache.
     54     static const int    kDefaultMaxCount            = 2 * (1 << 12);
     55     // Default maximum number of bytes of gpu memory of budgeted resources in the cache.
     56     static const size_t kDefaultMaxSize             = 96 * (1 << 20);
     57     // Default number of flushes a budgeted resources can go unused in the cache before it is
     58     // purged. Large values disable the feature (as the ring buffer of flush timestamps would be
     59     // large). This is currently the default until we decide to enable this feature
     60     // of the cache by default.
     61     static const int    kDefaultMaxUnusedFlushes    = 1024;
     62 
     63     /** Used to access functionality needed by GrGpuResource for lifetime management. */
     64     class ResourceAccess;
     65     ResourceAccess resourceAccess();
     66 
     67     /**
     68      * Sets the cache limits in terms of number of resources, max gpu memory byte size, and number
     69      * of GrContext flushes that a resource can be unused before it is evicted. The latter value is
     70      * a suggestion and there is no promise that a resource will be purged immediately after it
     71      * hasn't been used in maxUnusedFlushes flushes.
     72      */
     73     void setLimits(int count, size_t bytes, int maxUnusedFlushes = kDefaultMaxUnusedFlushes);
     74 
     75     /**
     76      * Returns the number of resources.
     77      */
     78     int getResourceCount() const {
     79         return fPurgeableQueue.count() + fNonpurgeableResources.count();
     80     }
     81 
     82     /**
     83      * Returns the number of resources that count against the budget.
     84      */
     85     int getBudgetedResourceCount() const { return fBudgetedCount; }
     86 
     87     /**
     88      * Returns the number of bytes consumed by resources.
     89      */
     90     size_t getResourceBytes() const { return fBytes; }
     91 
     92     /**
     93      * Returns the number of bytes consumed by budgeted resources.
     94      */
     95     size_t getBudgetedResourceBytes() const { return fBudgetedBytes; }
     96 
     97     /**
     98      * Returns the cached resources count budget.
     99      */
    100     int getMaxResourceCount() const { return fMaxCount; }
    101 
    102     /**
    103      * Returns the number of bytes consumed by cached resources.
    104      */
    105     size_t getMaxResourceBytes() const { return fMaxBytes; }
    106 
    107     /**
    108      * Abandons the backend API resources owned by all GrGpuResource objects and removes them from
    109      * the cache.
    110      */
    111     void abandonAll();
    112 
    113     /**
    114      * Releases the backend API resources owned by all GrGpuResource objects and removes them from
    115      * the cache.
    116      */
    117     void releaseAll();
    118 
    119     enum {
    120         /** Preferentially returns scratch resources with no pending IO. */
    121         kPreferNoPendingIO_ScratchFlag = 0x1,
    122         /** Will not return any resources that match but have pending IO. */
    123         kRequireNoPendingIO_ScratchFlag = 0x2,
    124     };
    125 
    126     /**
    127      * Find a resource that matches a scratch key.
    128      */
    129     GrGpuResource* findAndRefScratchResource(const GrScratchKey& scratchKey, uint32_t flags = 0);
    130 
    131 #ifdef SK_DEBUG
    132     // This is not particularly fast and only used for validation, so debug only.
    133     int countScratchEntriesForKey(const GrScratchKey& scratchKey) const {
    134         return fScratchMap.countForKey(scratchKey);
    135     }
    136 #endif
    137 
    138     /**
    139      * Find a resource that matches a unique key.
    140      */
    141     GrGpuResource* findAndRefUniqueResource(const GrUniqueKey& key) {
    142         GrGpuResource* resource = fUniqueHash.find(key);
    143         if (resource) {
    144             this->refAndMakeResourceMRU(resource);
    145         }
    146         return resource;
    147     }
    148 
    149     /**
    150      * Query whether a unique key exists in the cache.
    151      */
    152     bool hasUniqueKey(const GrUniqueKey& key) const {
    153         return SkToBool(fUniqueHash.find(key));
    154     }
    155 
    156     /** Purges resources to become under budget and processes resources with invalidated unique
    157         keys. */
    158     void purgeAsNeeded();
    159 
    160     /** Purges all resources that don't have external owners. */
    161     void purgeAllUnlocked();
    162 
    163     /**
    164      * The callback function used by the cache when it is still over budget after a purge. The
    165      * passed in 'data' is the same 'data' handed to setOverbudgetCallback.
    166      */
    167     typedef void (*PFOverBudgetCB)(void* data);
    168 
    169     /**
    170      * Set the callback the cache should use when it is still over budget after a purge. The 'data'
    171      * provided here will be passed back to the callback. Note that the cache will attempt to purge
    172      * any resources newly freed by the callback.
    173      */
    174     void setOverBudgetCallback(PFOverBudgetCB overBudgetCB, void* data) {
    175         fOverBudgetCB = overBudgetCB;
    176         fOverBudgetData = data;
    177     }
    178 
    179     void notifyFlushOccurred();
    180 
    181 #if GR_GPU_STATS
    182     void dumpStats(SkString*) const;
    183 #endif
    184 
    185     // This function is for unit testing and is only defined in test tools.
    186     void changeTimestamp(uint32_t newTimestamp);
    187 
    188 private:
    189     ///////////////////////////////////////////////////////////////////////////
    190     /// @name Methods accessible via ResourceAccess
    191     ////
    192     void insertResource(GrGpuResource*);
    193     void removeResource(GrGpuResource*);
    194     void notifyCntReachedZero(GrGpuResource*, uint32_t flags);
    195     void didChangeGpuMemorySize(const GrGpuResource*, size_t oldSize);
    196     void changeUniqueKey(GrGpuResource*, const GrUniqueKey&);
    197     void removeUniqueKey(GrGpuResource*);
    198     void willRemoveScratchKey(const GrGpuResource*);
    199     void didChangeBudgetStatus(GrGpuResource*);
    200     void refAndMakeResourceMRU(GrGpuResource*);
    201     /// @}
    202 
    203     void resetFlushTimestamps();
    204     void processInvalidUniqueKeys(const SkTArray<GrUniqueKeyInvalidatedMessage>&);
    205     void addToNonpurgeableArray(GrGpuResource*);
    206     void removeFromNonpurgeableArray(GrGpuResource*);
    207     bool overBudget() const { return fBudgetedBytes > fMaxBytes || fBudgetedCount > fMaxCount; }
    208 
    209     uint32_t getNextTimestamp();
    210 
    211 #ifdef SK_DEBUG
    212     bool isInCache(const GrGpuResource* r) const;
    213     void validate() const;
    214 #else
    215     void validate() const {}
    216 #endif
    217 
    218     class AutoValidate;
    219 
    220     class AvailableForScratchUse;
    221 
    222     struct ScratchMapTraits {
    223         static const GrScratchKey& GetKey(const GrGpuResource& r) {
    224             return r.resourcePriv().getScratchKey();
    225         }
    226 
    227         static uint32_t Hash(const GrScratchKey& key) { return key.hash(); }
    228     };
    229     typedef SkTMultiMap<GrGpuResource, GrScratchKey, ScratchMapTraits> ScratchMap;
    230 
    231     struct UniqueHashTraits {
    232         static const GrUniqueKey& GetKey(const GrGpuResource& r) { return r.getUniqueKey(); }
    233 
    234         static uint32_t Hash(const GrUniqueKey& key) { return key.hash(); }
    235     };
    236     typedef SkTDynamicHash<GrGpuResource, GrUniqueKey, UniqueHashTraits> UniqueHash;
    237 
    238     static bool CompareTimestamp(GrGpuResource* const& a, GrGpuResource* const& b) {
    239         return a->cacheAccess().timestamp() < b->cacheAccess().timestamp();
    240     }
    241 
    242     static int* AccessResourceIndex(GrGpuResource* const& res) {
    243         return res->cacheAccess().accessCacheIndex();
    244     }
    245 
    246     typedef SkMessageBus<GrUniqueKeyInvalidatedMessage>::Inbox InvalidUniqueKeyInbox;
    247     typedef SkTDPQueue<GrGpuResource*, CompareTimestamp, AccessResourceIndex> PurgeableQueue;
    248     typedef SkTDArray<GrGpuResource*> ResourceArray;
    249 
    250     // Whenever a resource is added to the cache or the result of a cache lookup, fTimestamp is
    251     // assigned as the resource's timestamp and then incremented. fPurgeableQueue orders the
    252     // purgeable resources by this value, and thus is used to purge resources in LRU order.
    253     uint32_t                            fTimestamp;
    254     PurgeableQueue                      fPurgeableQueue;
    255     ResourceArray                       fNonpurgeableResources;
    256 
    257     // This map holds all resources that can be used as scratch resources.
    258     ScratchMap                          fScratchMap;
    259     // This holds all resources that have unique keys.
    260     UniqueHash                          fUniqueHash;
    261 
    262     // our budget, used in purgeAsNeeded()
    263     int                                 fMaxCount;
    264     size_t                              fMaxBytes;
    265     int                                 fMaxUnusedFlushes;
    266 
    267 #if GR_CACHE_STATS
    268     int                                 fHighWaterCount;
    269     size_t                              fHighWaterBytes;
    270     int                                 fBudgetedHighWaterCount;
    271     size_t                              fBudgetedHighWaterBytes;
    272 #endif
    273 
    274     // our current stats for all resources
    275     SkDEBUGCODE(int                     fCount;)
    276     size_t                              fBytes;
    277 
    278     // our current stats for resources that count against the budget
    279     int                                 fBudgetedCount;
    280     size_t                              fBudgetedBytes;
    281 
    282     PFOverBudgetCB                      fOverBudgetCB;
    283     void*                               fOverBudgetData;
    284 
    285     // We keep track of the "timestamps" of the last n flushes. If a resource hasn't been used in
    286     // that time then we well preemptively purge it to reduce memory usage.
    287     uint32_t*                           fFlushTimestamps;
    288     int                                 fLastFlushTimestampIndex;
    289 
    290     InvalidUniqueKeyInbox               fInvalidUniqueKeyInbox;
    291 
    292     // This resource is allowed to be in the nonpurgeable array for the sake of validate() because
    293     // we're in the midst of converting it to purgeable status.
    294     SkDEBUGCODE(GrGpuResource*          fNewlyPurgeableResourceForValidation;)
    295 };
    296 
    297 class GrResourceCache::ResourceAccess {
    298 private:
    299     ResourceAccess(GrResourceCache* cache) : fCache(cache) { }
    300     ResourceAccess(const ResourceAccess& that) : fCache(that.fCache) { }
    301     ResourceAccess& operator=(const ResourceAccess&); // unimpl
    302 
    303     /**
    304      * Insert a resource into the cache.
    305      */
    306     void insertResource(GrGpuResource* resource) { fCache->insertResource(resource); }
    307 
    308     /**
    309      * Removes a resource from the cache.
    310      */
    311     void removeResource(GrGpuResource* resource) { fCache->removeResource(resource); }
    312 
    313     /**
    314      * Notifications that should be sent to the cache when the ref/io cnt status of resources
    315      * changes.
    316      */
    317     enum RefNotificationFlags {
    318         /** All types of refs on the resource have reached zero. */
    319         kAllCntsReachedZero_RefNotificationFlag = 0x1,
    320         /** The normal (not pending IO type) ref cnt has reached zero. */
    321         kRefCntReachedZero_RefNotificationFlag  = 0x2,
    322     };
    323     /**
    324      * Called by GrGpuResources when they detect that their ref/io cnts have reached zero. When the
    325      * normal ref cnt reaches zero the flags that are set should be:
    326      *     a) kRefCntReachedZero if a pending IO cnt is still non-zero.
    327      *     b) (kRefCntReachedZero | kAllCntsReachedZero) when all pending IO cnts are also zero.
    328      * kAllCntsReachedZero is set by itself if a pending IO cnt is decremented to zero and all the
    329      * the other cnts are already zero.
    330      */
    331     void notifyCntReachedZero(GrGpuResource* resource, uint32_t flags) {
    332         fCache->notifyCntReachedZero(resource, flags);
    333     }
    334 
    335     /**
    336      * Called by GrGpuResources when their sizes change.
    337      */
    338     void didChangeGpuMemorySize(const GrGpuResource* resource, size_t oldSize) {
    339         fCache->didChangeGpuMemorySize(resource, oldSize);
    340     }
    341 
    342     /**
    343      * Called by GrGpuResources to change their unique keys.
    344      */
    345     void changeUniqueKey(GrGpuResource* resource, const GrUniqueKey& newKey) {
    346          fCache->changeUniqueKey(resource, newKey);
    347     }
    348 
    349     /**
    350      * Called by a GrGpuResource to remove its unique key.
    351      */
    352     void removeUniqueKey(GrGpuResource* resource) { fCache->removeUniqueKey(resource); }
    353 
    354     /**
    355      * Called by a GrGpuResource when it removes its scratch key.
    356      */
    357     void willRemoveScratchKey(const GrGpuResource* resource) {
    358         fCache->willRemoveScratchKey(resource);
    359     }
    360 
    361     /**
    362      * Called by GrGpuResources when they change from budgeted to unbudgeted or vice versa.
    363      */
    364     void didChangeBudgetStatus(GrGpuResource* resource) { fCache->didChangeBudgetStatus(resource); }
    365 
    366     // No taking addresses of this type.
    367     const ResourceAccess* operator&() const;
    368     ResourceAccess* operator&();
    369 
    370     GrResourceCache* fCache;
    371 
    372     friend class GrGpuResource; // To access all the proxy inline methods.
    373     friend class GrResourceCache; // To create this type.
    374 };
    375 
    376 inline GrResourceCache::ResourceAccess GrResourceCache::resourceAccess() {
    377     return ResourceAccess(this);
    378 }
    379 
    380 #endif
    381