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(GPU_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(GPU_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 currentPurgeableBytes()85 size_t currentPurgeableBytes() const { return fPurgeableBytes; } 86 87 void dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const; 88 89 #if defined(GPU_TEST_UTILS) forceProcessReturnedResources()90 void forceProcessReturnedResources() { this->processReturnedResources(); } 91 forcePurgeAsNeeded()92 void forcePurgeAsNeeded() { this->purgeAsNeeded(); } 93 94 // Returns the numbers of Resources that can currently be found in the cache. This includes all 95 // shared Resources and all non-shareable resources that have been returned to the cache. 96 int numFindableResources() const; 97 98 // This will probably end up being a public function to change the current budget size, but for 99 // now just making this a testing only function. 100 void setMaxBudget(size_t bytes); 101 102 Resource* topOfPurgeableQueue(); 103 testingInPurgeableQueue(Resource * resource)104 bool testingInPurgeableQueue(Resource* resource) { return this->inPurgeableQueue(resource); } 105 106 void visitTextures(const std::function<void(const Texture*, bool purgeable)>&) const; 107 #endif 108 proxyCache()109 ProxyCache* proxyCache() { return fProxyCache.get(); } 110 111 private: 112 ResourceCache(SingleOwner*, uint32_t recorderID, size_t maxBytes); 113 114 // All these private functions are not meant to be thread safe. We don't check for is single 115 // owner in them as we assume that has already been checked by the public api calls. 116 void refAndMakeResourceMRU(Resource*); 117 void addToNonpurgeableArray(Resource* resource); 118 void removeFromNonpurgeableArray(Resource* resource); 119 void removeFromPurgeableQueue(Resource* resource); 120 121 // This will return true if any resources were actually returned to the cache 122 bool processReturnedResources(); 123 void returnResourceToCache(Resource*, LastRemovedRef); 124 125 uint32_t getNextTimestamp(); 126 void setResourceTimestamp(Resource*, uint32_t timestamp); 127 128 bool inPurgeableQueue(Resource*) const; 129 overbudget()130 bool overbudget() const { return fBudgetedBytes > fMaxBytes; } 131 void purgeAsNeeded(); 132 void purgeResource(Resource*); 133 // Passing in a nullptr for purgeTime will trigger us to try and free all unlocked resources. 134 void purgeResources(const StdSteadyClock::time_point* purgeTime); 135 136 #ifdef SK_DEBUG 137 bool isInCache(const Resource* r) const; 138 void validate() const; 139 #else validate()140 void validate() const {} 141 #endif 142 143 struct MapTraits { 144 static const GraphiteResourceKey& GetKey(const Resource& r); 145 146 static uint32_t Hash(const GraphiteResourceKey& key); OnFreeMapTraits147 static void OnFree(Resource*) {} 148 }; 149 typedef SkTMultiMap<Resource, GraphiteResourceKey, MapTraits> ResourceMap; 150 151 static bool CompareTimestamp(Resource* const& a, Resource* const& b); 152 static int* AccessResourceIndex(Resource* const& res); 153 154 using PurgeableQueue = SkTDPQueue<Resource*, CompareTimestamp, AccessResourceIndex>; 155 using ResourceArray = SkTDArray<Resource*>; 156 157 // Whenever a resource is added to the cache or the result of a cache lookup, fTimestamp is 158 // assigned as the resource's timestamp and then incremented. fPurgeableQueue orders the 159 // purgeable resources by this value, and thus is used to purge resources in LRU order. 160 // Resources with a size of zero are set to have max uint32_t value. This will also put them at 161 // the end of the LRU priority queue. This will allow us to not purge these resources even when 162 // we are over budget. 163 uint32_t fTimestamp = 0; 164 static const uint32_t kMaxTimestamp = 0xFFFFFFFF; 165 166 PurgeableQueue fPurgeableQueue; 167 ResourceArray fNonpurgeableResources; 168 std::unique_ptr<ProxyCache> fProxyCache; 169 170 SkDEBUGCODE(int fCount = 0;) 171 172 ResourceMap fResourceMap; 173 174 // Our budget 175 size_t fMaxBytes; 176 size_t fBudgetedBytes = 0; 177 178 size_t fPurgeableBytes = 0; 179 180 SingleOwner* fSingleOwner = nullptr; 181 182 bool fIsShutdown = false; 183 184 SkMutex fReturnMutex; 185 using ReturnQueue = std::vector<std::pair<Resource*, LastRemovedRef>>; 186 ReturnQueue fReturnQueue SK_GUARDED_BY(fReturnMutex); 187 }; 188 189 } // namespace skgpu::graphite 190 191 #endif // skgpu_graphite_ResourceCache_DEFINED 192