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