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