Home | History | Annotate | Download | only in gpu
      1 /*
      2  * Copyright 2014 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #ifndef GrResourceCache_DEFINED
      9 #define GrResourceCache_DEFINED
     10 
     11 #include "GrGpuResource.h"
     12 #include "GrGpuResourceCacheAccess.h"
     13 #include "GrGpuResourcePriv.h"
     14 #include "GrResourceCache.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 GrCaps;
     24 class SkString;
     25 class SkTraceMemoryDump;
     26 
     27 /**
     28  * Manages the lifetime of all GrGpuResource instances.
     29  *
     30  * Resources may have optionally have two types of keys:
     31  *      1) A scratch key. This is for resources whose allocations are cached but not their contents.
     32  *         Multiple resources can share the same scratch key. This is so a caller can have two
     33  *         resource instances with the same properties (e.g. multipass rendering that ping-pongs
     34  *         between two temporary surfaces). The scratch key is set at resource creation time and
     35  *         should never change. Resources need not have a scratch key.
     36  *      2) A unique key. This key's meaning is specific to the domain that created the key. Only one
     37  *         resource may have a given unique key. The unique key can be set, cleared, or changed
     38  *         anytime after resource creation.
     39  *
     40  * A unique key always takes precedence over a scratch key when a resource has both types of keys.
     41  * If a resource has neither key type then it will be deleted as soon as the last reference to it
     42  * is dropped.
     43  */
     44 class GrResourceCache {
     45 public:
     46     GrResourceCache(const GrCaps* caps);
     47     ~GrResourceCache();
     48 
     49     // Default maximum number of budgeted resources in the cache.
     50     static const int    kDefaultMaxCount            = 2 * (1 << 12);
     51     // Default maximum number of bytes of gpu memory of budgeted resources in the cache.
     52     static const size_t kDefaultMaxSize             = 96 * (1 << 20);
     53     // Default number of external flushes a budgeted resources can go unused in the cache before it
     54     // is purged. Using a value <= 0 disables this feature. This will be removed once Chrome
     55     // starts using time-based purging.
     56     static const int    kDefaultMaxUnusedFlushes =
     57             1  * /* flushes per frame */
     58             60 * /* fps */
     59             30;  /* seconds */
     60 
     61     /** Used to access functionality needed by GrGpuResource for lifetime management. */
     62     class ResourceAccess;
     63     ResourceAccess resourceAccess();
     64 
     65     /**
     66      * Sets the cache limits in terms of number of resources, max gpu memory byte size, and number
     67      * of external GrContext flushes that a resource can be unused before it is evicted. The latter
     68      * value is a suggestion and there is no promise that a resource will be purged immediately
     69      * after it hasn't been used in maxUnusedFlushes flushes.
     70      */
     71     void setLimits(int count, size_t bytes, int maxUnusedFlushes = kDefaultMaxUnusedFlushes);
     72 
     73     /**
     74      * Returns the number of resources.
     75      */
     76     int getResourceCount() const {
     77         return fPurgeableQueue.count() + fNonpurgeableResources.count();
     78     }
     79 
     80     /**
     81      * Returns the number of resources that count against the budget.
     82      */
     83     int getBudgetedResourceCount() const { return fBudgetedCount; }
     84 
     85     /**
     86      * Returns the number of bytes consumed by resources.
     87      */
     88     size_t getResourceBytes() const { return fBytes; }
     89 
     90     /**
     91      * Returns the number of bytes consumed by budgeted resources.
     92      */
     93     size_t getBudgetedResourceBytes() const { return fBudgetedBytes; }
     94 
     95     /**
     96      * Returns the cached resources count budget.
     97      */
     98     int getMaxResourceCount() const { return fMaxCount; }
     99 
    100     /**
    101      * Returns the number of bytes consumed by cached resources.
    102      */
    103     size_t getMaxResourceBytes() const { return fMaxBytes; }
    104 
    105     /**
    106      * Abandons the backend API resources owned by all GrGpuResource objects and removes them from
    107      * the cache.
    108      */
    109     void abandonAll();
    110 
    111     /**
    112      * Releases the backend API resources owned by all GrGpuResource objects and removes them from
    113      * the cache.
    114      */
    115     void releaseAll();
    116 
    117     enum {
    118         /** Preferentially returns scratch resources with no pending IO. */
    119         kPreferNoPendingIO_ScratchFlag = 0x1,
    120         /** Will not return any resources that match but have pending IO. */
    121         kRequireNoPendingIO_ScratchFlag = 0x2,
    122     };
    123 
    124     /**
    125      * Find a resource that matches a scratch key.
    126      */
    127     GrGpuResource* findAndRefScratchResource(const GrScratchKey& scratchKey,
    128                                              size_t resourceSize,
    129                                              uint32_t flags);
    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     /** Purge all resources not used since the passed in time. */
    164     void purgeResourcesNotUsedSince(GrStdSteadyClock::time_point);
    165 
    166     /** Returns true if the cache would like a flush to occur in order to make more resources
    167         purgeable. */
    168     bool requestsFlush() const { return fRequestFlush; }
    169 
    170     enum FlushType {
    171         kExternal,
    172         kImmediateMode,
    173         kCacheRequested,
    174     };
    175     void notifyFlushOccurred(FlushType);
    176 
    177 #if GR_CACHE_STATS
    178     struct Stats {
    179         int fTotal;
    180         int fNumPurgeable;
    181         int fNumNonPurgeable;
    182 
    183         int fScratch;
    184         int fWrapped;
    185         size_t fUnbudgetedSize;
    186 
    187         Stats() { this->reset(); }
    188 
    189         void reset() {
    190             fTotal = 0;
    191             fNumPurgeable = 0;
    192             fNumNonPurgeable = 0;
    193             fScratch = 0;
    194             fWrapped = 0;
    195             fUnbudgetedSize = 0;
    196         }
    197 
    198         void update(GrGpuResource* resource) {
    199             if (resource->cacheAccess().isScratch()) {
    200                 ++fScratch;
    201             }
    202             if (resource->resourcePriv().refsWrappedObjects()) {
    203                 ++fWrapped;
    204             }
    205             if (SkBudgeted::kNo  == resource->resourcePriv().isBudgeted()) {
    206                 fUnbudgetedSize += resource->gpuMemorySize();
    207             }
    208         }
    209     };
    210 
    211     void getStats(Stats*) const;
    212 
    213     void dumpStats(SkString*) const;
    214 
    215     void dumpStatsKeyValuePairs(SkTArray<SkString>* keys, SkTArray<double>* value) const;
    216 #endif
    217 
    218 #ifdef SK_DEBUG
    219     int countUniqueKeysWithTag(const char* tag) const;
    220 #endif
    221 
    222     // This function is for unit testing and is only defined in test tools.
    223     void changeTimestamp(uint32_t newTimestamp);
    224 
    225     // Enumerates all cached resources and dumps their details to traceMemoryDump.
    226     void dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const;
    227 
    228 private:
    229     ///////////////////////////////////////////////////////////////////////////
    230     /// @name Methods accessible via ResourceAccess
    231     ////
    232     void insertResource(GrGpuResource*);
    233     void removeResource(GrGpuResource*);
    234     void notifyCntReachedZero(GrGpuResource*, uint32_t flags);
    235     void didChangeGpuMemorySize(const GrGpuResource*, size_t oldSize);
    236     void changeUniqueKey(GrGpuResource*, const GrUniqueKey&);
    237     void removeUniqueKey(GrGpuResource*);
    238     void willRemoveScratchKey(const GrGpuResource*);
    239     void didChangeBudgetStatus(GrGpuResource*);
    240     void refAndMakeResourceMRU(GrGpuResource*);
    241     /// @}
    242 
    243     void processInvalidUniqueKeys(const SkTArray<GrUniqueKeyInvalidatedMessage>&);
    244     void addToNonpurgeableArray(GrGpuResource*);
    245     void removeFromNonpurgeableArray(GrGpuResource*);
    246     bool overBudget() const { return fBudgetedBytes > fMaxBytes || fBudgetedCount > fMaxCount; }
    247 
    248     bool wouldFit(size_t bytes) {
    249         return fBudgetedBytes+bytes <= fMaxBytes && fBudgetedCount+1 <= fMaxCount;
    250     }
    251 
    252     uint32_t getNextTimestamp();
    253 
    254 #ifdef SK_DEBUG
    255     bool isInCache(const GrGpuResource* r) const;
    256     void validate() const;
    257 #else
    258     void validate() const {}
    259 #endif
    260 
    261     class AutoValidate;
    262 
    263     class AvailableForScratchUse;
    264 
    265     struct ScratchMapTraits {
    266         static const GrScratchKey& GetKey(const GrGpuResource& r) {
    267             return r.resourcePriv().getScratchKey();
    268         }
    269 
    270         static uint32_t Hash(const GrScratchKey& key) { return key.hash(); }
    271     };
    272     typedef SkTMultiMap<GrGpuResource, GrScratchKey, ScratchMapTraits> ScratchMap;
    273 
    274     struct UniqueHashTraits {
    275         static const GrUniqueKey& GetKey(const GrGpuResource& r) { return r.getUniqueKey(); }
    276 
    277         static uint32_t Hash(const GrUniqueKey& key) { return key.hash(); }
    278     };
    279     typedef SkTDynamicHash<GrGpuResource, GrUniqueKey, UniqueHashTraits> UniqueHash;
    280 
    281     static bool CompareTimestamp(GrGpuResource* const& a, GrGpuResource* const& b) {
    282         return a->cacheAccess().timestamp() < b->cacheAccess().timestamp();
    283     }
    284 
    285     static int* AccessResourceIndex(GrGpuResource* const& res) {
    286         return res->cacheAccess().accessCacheIndex();
    287     }
    288 
    289     typedef SkMessageBus<GrUniqueKeyInvalidatedMessage>::Inbox InvalidUniqueKeyInbox;
    290     typedef SkTDPQueue<GrGpuResource*, CompareTimestamp, AccessResourceIndex> PurgeableQueue;
    291     typedef SkTDArray<GrGpuResource*> ResourceArray;
    292 
    293     // Whenever a resource is added to the cache or the result of a cache lookup, fTimestamp is
    294     // assigned as the resource's timestamp and then incremented. fPurgeableQueue orders the
    295     // purgeable resources by this value, and thus is used to purge resources in LRU order.
    296     uint32_t                            fTimestamp;
    297     PurgeableQueue                      fPurgeableQueue;
    298     ResourceArray                       fNonpurgeableResources;
    299 
    300     // This map holds all resources that can be used as scratch resources.
    301     ScratchMap                          fScratchMap;
    302     // This holds all resources that have unique keys.
    303     UniqueHash                          fUniqueHash;
    304 
    305     // our budget, used in purgeAsNeeded()
    306     int                                 fMaxCount;
    307     size_t                              fMaxBytes;
    308     int                                 fMaxUnusedFlushes;
    309 
    310 #if GR_CACHE_STATS
    311     int                                 fHighWaterCount;
    312     size_t                              fHighWaterBytes;
    313     int                                 fBudgetedHighWaterCount;
    314     size_t                              fBudgetedHighWaterBytes;
    315 #endif
    316 
    317     // our current stats for all resources
    318     SkDEBUGCODE(int                     fCount;)
    319     size_t                              fBytes;
    320 
    321     // our current stats for resources that count against the budget
    322     int                                 fBudgetedCount;
    323     size_t                              fBudgetedBytes;
    324 
    325     bool                                fRequestFlush;
    326     uint32_t                            fExternalFlushCnt;
    327 
    328     InvalidUniqueKeyInbox               fInvalidUniqueKeyInbox;
    329 
    330     // This resource is allowed to be in the nonpurgeable array for the sake of validate() because
    331     // we're in the midst of converting it to purgeable status.
    332     SkDEBUGCODE(GrGpuResource*          fNewlyPurgeableResourceForValidation;)
    333 
    334     bool                                fPreferVRAMUseOverFlushes;
    335 };
    336 
    337 class GrResourceCache::ResourceAccess {
    338 private:
    339     ResourceAccess(GrResourceCache* cache) : fCache(cache) { }
    340     ResourceAccess(const ResourceAccess& that) : fCache(that.fCache) { }
    341     ResourceAccess& operator=(const ResourceAccess&); // unimpl
    342 
    343     /**
    344      * Insert a resource into the cache.
    345      */
    346     void insertResource(GrGpuResource* resource) { fCache->insertResource(resource); }
    347 
    348     /**
    349      * Removes a resource from the cache.
    350      */
    351     void removeResource(GrGpuResource* resource) { fCache->removeResource(resource); }
    352 
    353     /**
    354      * Notifications that should be sent to the cache when the ref/io cnt status of resources
    355      * changes.
    356      */
    357     enum RefNotificationFlags {
    358         /** All types of refs on the resource have reached zero. */
    359         kAllCntsReachedZero_RefNotificationFlag = 0x1,
    360         /** The normal (not pending IO type) ref cnt has reached zero. */
    361         kRefCntReachedZero_RefNotificationFlag  = 0x2,
    362     };
    363     /**
    364      * Called by GrGpuResources when they detect that their ref/io cnts have reached zero. When the
    365      * normal ref cnt reaches zero the flags that are set should be:
    366      *     a) kRefCntReachedZero if a pending IO cnt is still non-zero.
    367      *     b) (kRefCntReachedZero | kAllCntsReachedZero) when all pending IO cnts are also zero.
    368      * kAllCntsReachedZero is set by itself if a pending IO cnt is decremented to zero and all the
    369      * the other cnts are already zero.
    370      */
    371     void notifyCntReachedZero(GrGpuResource* resource, uint32_t flags) {
    372         fCache->notifyCntReachedZero(resource, flags);
    373     }
    374 
    375     /**
    376      * Called by GrGpuResources when their sizes change.
    377      */
    378     void didChangeGpuMemorySize(const GrGpuResource* resource, size_t oldSize) {
    379         fCache->didChangeGpuMemorySize(resource, oldSize);
    380     }
    381 
    382     /**
    383      * Called by GrGpuResources to change their unique keys.
    384      */
    385     void changeUniqueKey(GrGpuResource* resource, const GrUniqueKey& newKey) {
    386          fCache->changeUniqueKey(resource, newKey);
    387     }
    388 
    389     /**
    390      * Called by a GrGpuResource to remove its unique key.
    391      */
    392     void removeUniqueKey(GrGpuResource* resource) { fCache->removeUniqueKey(resource); }
    393 
    394     /**
    395      * Called by a GrGpuResource when it removes its scratch key.
    396      */
    397     void willRemoveScratchKey(const GrGpuResource* resource) {
    398         fCache->willRemoveScratchKey(resource);
    399     }
    400 
    401     /**
    402      * Called by GrGpuResources when they change from budgeted to unbudgeted or vice versa.
    403      */
    404     void didChangeBudgetStatus(GrGpuResource* resource) { fCache->didChangeBudgetStatus(resource); }
    405 
    406     // No taking addresses of this type.
    407     const ResourceAccess* operator&() const;
    408     ResourceAccess* operator&();
    409 
    410     GrResourceCache* fCache;
    411 
    412     friend class GrGpuResource; // To access all the proxy inline methods.
    413     friend class GrResourceCache; // To create this type.
    414 };
    415 
    416 inline GrResourceCache::ResourceAccess GrResourceCache::resourceAccess() {
    417     return ResourceAccess(this);
    418 }
    419 
    420 #endif
    421