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