1*c8dee2aaSAndroid Build Coastguard Worker /* 2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2022 Google LLC 3*c8dee2aaSAndroid Build Coastguard Worker * 4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be 5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file. 6*c8dee2aaSAndroid Build Coastguard Worker */ 7*c8dee2aaSAndroid Build Coastguard Worker 8*c8dee2aaSAndroid Build Coastguard Worker #ifndef skgpu_graphite_ResourceCache_DEFINED 9*c8dee2aaSAndroid Build Coastguard Worker #define skgpu_graphite_ResourceCache_DEFINED 10*c8dee2aaSAndroid Build Coastguard Worker 11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRefCnt.h" 12*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkMutex.h" 13*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTArray.h" 14*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkTDPQueue.h" 15*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkTHash.h" 16*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkTMultiMap.h" 17*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/GpuTypesPriv.h" 18*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/ResourceTypes.h" 19*c8dee2aaSAndroid Build Coastguard Worker 20*c8dee2aaSAndroid Build Coastguard Worker #if defined(GPU_TEST_UTILS) 21*c8dee2aaSAndroid Build Coastguard Worker #include <functional> 22*c8dee2aaSAndroid Build Coastguard Worker #endif 23*c8dee2aaSAndroid Build Coastguard Worker #include <vector> 24*c8dee2aaSAndroid Build Coastguard Worker 25*c8dee2aaSAndroid Build Coastguard Worker class SkTraceMemoryDump; 26*c8dee2aaSAndroid Build Coastguard Worker 27*c8dee2aaSAndroid Build Coastguard Worker namespace skgpu { 28*c8dee2aaSAndroid Build Coastguard Worker class SingleOwner; 29*c8dee2aaSAndroid Build Coastguard Worker } 30*c8dee2aaSAndroid Build Coastguard Worker 31*c8dee2aaSAndroid Build Coastguard Worker namespace skgpu::graphite { 32*c8dee2aaSAndroid Build Coastguard Worker 33*c8dee2aaSAndroid Build Coastguard Worker class GraphiteResourceKey; 34*c8dee2aaSAndroid Build Coastguard Worker class ProxyCache; 35*c8dee2aaSAndroid Build Coastguard Worker class Resource; 36*c8dee2aaSAndroid Build Coastguard Worker 37*c8dee2aaSAndroid Build Coastguard Worker #if defined(GPU_TEST_UTILS) 38*c8dee2aaSAndroid Build Coastguard Worker class Texture; 39*c8dee2aaSAndroid Build Coastguard Worker #endif 40*c8dee2aaSAndroid Build Coastguard Worker 41*c8dee2aaSAndroid Build Coastguard Worker class ResourceCache : public SkRefCnt { 42*c8dee2aaSAndroid Build Coastguard Worker public: 43*c8dee2aaSAndroid Build Coastguard Worker static sk_sp<ResourceCache> Make(SingleOwner*, uint32_t recorderID, size_t maxBytes); 44*c8dee2aaSAndroid Build Coastguard Worker ~ResourceCache() override; 45*c8dee2aaSAndroid Build Coastguard Worker 46*c8dee2aaSAndroid Build Coastguard Worker ResourceCache(const ResourceCache&) = delete; 47*c8dee2aaSAndroid Build Coastguard Worker ResourceCache(ResourceCache&&) = delete; 48*c8dee2aaSAndroid Build Coastguard Worker ResourceCache& operator=(const ResourceCache&) = delete; 49*c8dee2aaSAndroid Build Coastguard Worker ResourceCache& operator=(ResourceCache&&) = delete; 50*c8dee2aaSAndroid Build Coastguard Worker 51*c8dee2aaSAndroid Build Coastguard Worker // Returns the number of resources. getResourceCount()52*c8dee2aaSAndroid Build Coastguard Worker int getResourceCount() const { 53*c8dee2aaSAndroid Build Coastguard Worker return fPurgeableQueue.count() + fNonpurgeableResources.size(); 54*c8dee2aaSAndroid Build Coastguard Worker } 55*c8dee2aaSAndroid Build Coastguard Worker 56*c8dee2aaSAndroid Build Coastguard Worker void insertResource(Resource*); 57*c8dee2aaSAndroid Build Coastguard Worker 58*c8dee2aaSAndroid Build Coastguard Worker // Find a resource that matches a key. 59*c8dee2aaSAndroid Build Coastguard Worker Resource* findAndRefResource(const GraphiteResourceKey& key, skgpu::Budgeted); 60*c8dee2aaSAndroid Build Coastguard Worker 61*c8dee2aaSAndroid Build Coastguard Worker // This is a thread safe call. If it fails the ResourceCache is no longer valid and the 62*c8dee2aaSAndroid Build Coastguard Worker // Resource should clean itself up if it is the last ref. 63*c8dee2aaSAndroid Build Coastguard Worker bool returnResource(Resource*, LastRemovedRef); 64*c8dee2aaSAndroid Build Coastguard Worker 65*c8dee2aaSAndroid Build Coastguard Worker // Purge resources not used since the passed point in time. Resources that have a gpu memory 66*c8dee2aaSAndroid Build Coastguard Worker // size of zero will not be purged. 67*c8dee2aaSAndroid Build Coastguard Worker // TODO: Should we add an optional flag to also allow purging of zero sized resources? Would we 68*c8dee2aaSAndroid Build Coastguard Worker // want to be able to differentiate between things like Pipelines (probably never want to purge) 69*c8dee2aaSAndroid Build Coastguard Worker // and things like descriptor sets. 70*c8dee2aaSAndroid Build Coastguard Worker void purgeResourcesNotUsedSince(StdSteadyClock::time_point purgeTime); 71*c8dee2aaSAndroid Build Coastguard Worker 72*c8dee2aaSAndroid Build Coastguard Worker // Purge any unlocked resources. Resources that have a gpu memory size of zero will not be 73*c8dee2aaSAndroid Build Coastguard Worker // purged. 74*c8dee2aaSAndroid Build Coastguard Worker void purgeResources(); 75*c8dee2aaSAndroid Build Coastguard Worker 76*c8dee2aaSAndroid Build Coastguard Worker // Called by the ResourceProvider when it is dropping its ref to the ResourceCache. After this 77*c8dee2aaSAndroid Build Coastguard Worker // is called no more Resources can be returned to the ResourceCache (besides those already in 78*c8dee2aaSAndroid Build Coastguard Worker // the return queue). Also no new Resources can be retrieved from the ResourceCache. 79*c8dee2aaSAndroid Build Coastguard Worker void shutdown(); 80*c8dee2aaSAndroid Build Coastguard Worker getMaxBudget()81*c8dee2aaSAndroid Build Coastguard Worker size_t getMaxBudget() const { return fMaxBytes; } 82*c8dee2aaSAndroid Build Coastguard Worker currentBudgetedBytes()83*c8dee2aaSAndroid Build Coastguard Worker size_t currentBudgetedBytes() const { return fBudgetedBytes; } 84*c8dee2aaSAndroid Build Coastguard Worker currentPurgeableBytes()85*c8dee2aaSAndroid Build Coastguard Worker size_t currentPurgeableBytes() const { return fPurgeableBytes; } 86*c8dee2aaSAndroid Build Coastguard Worker 87*c8dee2aaSAndroid Build Coastguard Worker void dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const; 88*c8dee2aaSAndroid Build Coastguard Worker 89*c8dee2aaSAndroid Build Coastguard Worker #if defined(GPU_TEST_UTILS) forceProcessReturnedResources()90*c8dee2aaSAndroid Build Coastguard Worker void forceProcessReturnedResources() { this->processReturnedResources(); } 91*c8dee2aaSAndroid Build Coastguard Worker forcePurgeAsNeeded()92*c8dee2aaSAndroid Build Coastguard Worker void forcePurgeAsNeeded() { this->purgeAsNeeded(); } 93*c8dee2aaSAndroid Build Coastguard Worker 94*c8dee2aaSAndroid Build Coastguard Worker // Returns the numbers of Resources that can currently be found in the cache. This includes all 95*c8dee2aaSAndroid Build Coastguard Worker // shared Resources and all non-shareable resources that have been returned to the cache. 96*c8dee2aaSAndroid Build Coastguard Worker int numFindableResources() const; 97*c8dee2aaSAndroid Build Coastguard Worker 98*c8dee2aaSAndroid Build Coastguard Worker // This will probably end up being a public function to change the current budget size, but for 99*c8dee2aaSAndroid Build Coastguard Worker // now just making this a testing only function. 100*c8dee2aaSAndroid Build Coastguard Worker void setMaxBudget(size_t bytes); 101*c8dee2aaSAndroid Build Coastguard Worker 102*c8dee2aaSAndroid Build Coastguard Worker Resource* topOfPurgeableQueue(); 103*c8dee2aaSAndroid Build Coastguard Worker testingInPurgeableQueue(Resource * resource)104*c8dee2aaSAndroid Build Coastguard Worker bool testingInPurgeableQueue(Resource* resource) { return this->inPurgeableQueue(resource); } 105*c8dee2aaSAndroid Build Coastguard Worker 106*c8dee2aaSAndroid Build Coastguard Worker void visitTextures(const std::function<void(const Texture*, bool purgeable)>&) const; 107*c8dee2aaSAndroid Build Coastguard Worker #endif 108*c8dee2aaSAndroid Build Coastguard Worker proxyCache()109*c8dee2aaSAndroid Build Coastguard Worker ProxyCache* proxyCache() { return fProxyCache.get(); } 110*c8dee2aaSAndroid Build Coastguard Worker 111*c8dee2aaSAndroid Build Coastguard Worker private: 112*c8dee2aaSAndroid Build Coastguard Worker ResourceCache(SingleOwner*, uint32_t recorderID, size_t maxBytes); 113*c8dee2aaSAndroid Build Coastguard Worker 114*c8dee2aaSAndroid Build Coastguard Worker // All these private functions are not meant to be thread safe. We don't check for is single 115*c8dee2aaSAndroid Build Coastguard Worker // owner in them as we assume that has already been checked by the public api calls. 116*c8dee2aaSAndroid Build Coastguard Worker void refAndMakeResourceMRU(Resource*); 117*c8dee2aaSAndroid Build Coastguard Worker void addToNonpurgeableArray(Resource* resource); 118*c8dee2aaSAndroid Build Coastguard Worker void removeFromNonpurgeableArray(Resource* resource); 119*c8dee2aaSAndroid Build Coastguard Worker void removeFromPurgeableQueue(Resource* resource); 120*c8dee2aaSAndroid Build Coastguard Worker 121*c8dee2aaSAndroid Build Coastguard Worker // This will return true if any resources were actually returned to the cache 122*c8dee2aaSAndroid Build Coastguard Worker bool processReturnedResources(); 123*c8dee2aaSAndroid Build Coastguard Worker void returnResourceToCache(Resource*, LastRemovedRef); 124*c8dee2aaSAndroid Build Coastguard Worker 125*c8dee2aaSAndroid Build Coastguard Worker uint32_t getNextTimestamp(); 126*c8dee2aaSAndroid Build Coastguard Worker void setResourceTimestamp(Resource*, uint32_t timestamp); 127*c8dee2aaSAndroid Build Coastguard Worker 128*c8dee2aaSAndroid Build Coastguard Worker bool inPurgeableQueue(Resource*) const; 129*c8dee2aaSAndroid Build Coastguard Worker overbudget()130*c8dee2aaSAndroid Build Coastguard Worker bool overbudget() const { return fBudgetedBytes > fMaxBytes; } 131*c8dee2aaSAndroid Build Coastguard Worker void purgeAsNeeded(); 132*c8dee2aaSAndroid Build Coastguard Worker void purgeResource(Resource*); 133*c8dee2aaSAndroid Build Coastguard Worker // Passing in a nullptr for purgeTime will trigger us to try and free all unlocked resources. 134*c8dee2aaSAndroid Build Coastguard Worker void purgeResources(const StdSteadyClock::time_point* purgeTime); 135*c8dee2aaSAndroid Build Coastguard Worker 136*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_DEBUG 137*c8dee2aaSAndroid Build Coastguard Worker bool isInCache(const Resource* r) const; 138*c8dee2aaSAndroid Build Coastguard Worker void validate() const; 139*c8dee2aaSAndroid Build Coastguard Worker #else validate()140*c8dee2aaSAndroid Build Coastguard Worker void validate() const {} 141*c8dee2aaSAndroid Build Coastguard Worker #endif 142*c8dee2aaSAndroid Build Coastguard Worker 143*c8dee2aaSAndroid Build Coastguard Worker struct MapTraits { 144*c8dee2aaSAndroid Build Coastguard Worker static const GraphiteResourceKey& GetKey(const Resource& r); 145*c8dee2aaSAndroid Build Coastguard Worker 146*c8dee2aaSAndroid Build Coastguard Worker static uint32_t Hash(const GraphiteResourceKey& key); OnFreeMapTraits147*c8dee2aaSAndroid Build Coastguard Worker static void OnFree(Resource*) {} 148*c8dee2aaSAndroid Build Coastguard Worker }; 149*c8dee2aaSAndroid Build Coastguard Worker typedef SkTMultiMap<Resource, GraphiteResourceKey, MapTraits> ResourceMap; 150*c8dee2aaSAndroid Build Coastguard Worker 151*c8dee2aaSAndroid Build Coastguard Worker static bool CompareTimestamp(Resource* const& a, Resource* const& b); 152*c8dee2aaSAndroid Build Coastguard Worker static int* AccessResourceIndex(Resource* const& res); 153*c8dee2aaSAndroid Build Coastguard Worker 154*c8dee2aaSAndroid Build Coastguard Worker using PurgeableQueue = SkTDPQueue<Resource*, CompareTimestamp, AccessResourceIndex>; 155*c8dee2aaSAndroid Build Coastguard Worker using ResourceArray = SkTDArray<Resource*>; 156*c8dee2aaSAndroid Build Coastguard Worker 157*c8dee2aaSAndroid Build Coastguard Worker // Whenever a resource is added to the cache or the result of a cache lookup, fTimestamp is 158*c8dee2aaSAndroid Build Coastguard Worker // assigned as the resource's timestamp and then incremented. fPurgeableQueue orders the 159*c8dee2aaSAndroid Build Coastguard Worker // purgeable resources by this value, and thus is used to purge resources in LRU order. 160*c8dee2aaSAndroid Build Coastguard Worker // Resources with a size of zero are set to have max uint32_t value. This will also put them at 161*c8dee2aaSAndroid Build Coastguard Worker // the end of the LRU priority queue. This will allow us to not purge these resources even when 162*c8dee2aaSAndroid Build Coastguard Worker // we are over budget. 163*c8dee2aaSAndroid Build Coastguard Worker uint32_t fTimestamp = 0; 164*c8dee2aaSAndroid Build Coastguard Worker static const uint32_t kMaxTimestamp = 0xFFFFFFFF; 165*c8dee2aaSAndroid Build Coastguard Worker 166*c8dee2aaSAndroid Build Coastguard Worker PurgeableQueue fPurgeableQueue; 167*c8dee2aaSAndroid Build Coastguard Worker ResourceArray fNonpurgeableResources; 168*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<ProxyCache> fProxyCache; 169*c8dee2aaSAndroid Build Coastguard Worker 170*c8dee2aaSAndroid Build Coastguard Worker SkDEBUGCODE(int fCount = 0;) 171*c8dee2aaSAndroid Build Coastguard Worker 172*c8dee2aaSAndroid Build Coastguard Worker ResourceMap fResourceMap; 173*c8dee2aaSAndroid Build Coastguard Worker 174*c8dee2aaSAndroid Build Coastguard Worker // Our budget 175*c8dee2aaSAndroid Build Coastguard Worker size_t fMaxBytes; 176*c8dee2aaSAndroid Build Coastguard Worker size_t fBudgetedBytes = 0; 177*c8dee2aaSAndroid Build Coastguard Worker 178*c8dee2aaSAndroid Build Coastguard Worker size_t fPurgeableBytes = 0; 179*c8dee2aaSAndroid Build Coastguard Worker 180*c8dee2aaSAndroid Build Coastguard Worker SingleOwner* fSingleOwner = nullptr; 181*c8dee2aaSAndroid Build Coastguard Worker 182*c8dee2aaSAndroid Build Coastguard Worker bool fIsShutdown = false; 183*c8dee2aaSAndroid Build Coastguard Worker 184*c8dee2aaSAndroid Build Coastguard Worker SkMutex fReturnMutex; 185*c8dee2aaSAndroid Build Coastguard Worker using ReturnQueue = std::vector<std::pair<Resource*, LastRemovedRef>>; 186*c8dee2aaSAndroid Build Coastguard Worker ReturnQueue fReturnQueue SK_GUARDED_BY(fReturnMutex); 187*c8dee2aaSAndroid Build Coastguard Worker }; 188*c8dee2aaSAndroid Build Coastguard Worker 189*c8dee2aaSAndroid Build Coastguard Worker } // namespace skgpu::graphite 190*c8dee2aaSAndroid Build Coastguard Worker 191*c8dee2aaSAndroid Build Coastguard Worker #endif // skgpu_graphite_ResourceCache_DEFINED 192