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/Resource.h" 19 #include "src/gpu/graphite/ResourceTypes.h" 20 21 #if defined(GPU_TEST_UTILS) 22 #include <functional> 23 #endif 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 36 #if defined(GPU_TEST_UTILS) 37 class Texture; 38 #endif 39 40 class ResourceCache : public SkRefCnt { 41 public: 42 static sk_sp<ResourceCache> Make(SingleOwner*, uint32_t recorderID, size_t maxBytes); 43 ~ResourceCache() override; 44 45 ResourceCache(const ResourceCache&) = delete; 46 ResourceCache(ResourceCache&&) = delete; 47 ResourceCache& operator=(const ResourceCache&) = delete; 48 ResourceCache& operator=(ResourceCache&&) = delete; 49 50 using ScratchResourceSet = skia_private::THashSet<const Resource*>; 51 // Find a resource that matches a key. If Shareable == kScratch, then `unavailable` must be 52 // non-null and is used to filter the scratch resources that can fulfill this request. 53 Resource* findAndRefResource(const GraphiteResourceKey& key, 54 Budgeted, Shareable, 55 const ScratchResourceSet* unavailable=nullptr); 56 57 // Purge resources not used since the passed point in time. Resources that have a gpu memory 58 // size of zero will not be purged. 59 // TODO: Should we add an optional flag to also allow purging of zero sized resources? Would we 60 // want to be able to differentiate between things like Pipelines (probably never want to purge) 61 // and things like descriptor sets. 62 void purgeResourcesNotUsedSince(StdSteadyClock::time_point purgeTime); 63 64 // Purge any unlocked resources. Resources that have a gpu memory size of zero will not be 65 // purged. 66 void purgeResources(); 67 68 // Called by the ResourceProvider when it is dropping its ref to the ResourceCache. After this 69 // is called no more Resources can be returned to the ResourceCache (besides those already in 70 // the return queue). Also no new Resources can be retrieved from the ResourceCache. 71 void shutdown(); 72 proxyCache()73 ProxyCache* proxyCache() { return fProxyCache.get(); } 74 getResourceCount()75 int getResourceCount() const { return fPurgeableQueue.count() + fNonpurgeableResources.size(); } 76 getMaxBudget()77 size_t getMaxBudget() const { return fMaxBytes; } 78 void setMaxBudget(size_t bytes); 79 currentBudgetedBytes()80 size_t currentBudgetedBytes() const { return fBudgetedBytes; } 81 currentPurgeableBytes()82 size_t currentPurgeableBytes() const { return fPurgeableBytes; } 83 84 void dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const; 85 86 void forceProcessReturnedResources(); 87 88 #if defined(GPU_TEST_UTILS) forcePurgeAsNeeded()89 void forcePurgeAsNeeded() { this->purgeAsNeeded(); } 90 91 // Returns the numbers of Resources that can currently be found in the cache. This includes all 92 // shared Resources and all non-shareable resources that have been returned to the cache. 93 int numFindableResources() const; 94 95 Resource* topOfPurgeableQueue(); 96 testingInPurgeableQueue(Resource * resource)97 bool testingInPurgeableQueue(Resource* resource) { return this->inPurgeableQueue(resource); } 98 testingInReturnQueue(Resource * resource)99 bool testingInReturnQueue(Resource* resource) { return resource->inReturnQueue(); } 100 101 void visitTextures(const std::function<void(const Texture*, bool purgeable)>&) const; 102 #endif 103 104 // This is a thread safe call and is a no-op if the cache has been shut down already. This 105 // should only be called by Resource. Returns true if the resource was successfully added to 106 // the return queue. 107 bool returnResource(Resource*); 108 109 // Registers the Resource with the cache; can only be called at the time of creation. 110 void insertResource(Resource*, const GraphiteResourceKey&, Budgeted, Shareable); 111 112 private: 113 ResourceCache(SingleOwner*, uint32_t recorderID, size_t maxBytes); 114 115 // All these private functions are not meant to be thread safe. We don't check for is single 116 // owner in them as we assume that has already been checked by the public api calls. 117 void refAndMakeResourceMRU(Resource*); 118 void addToNonpurgeableArray(Resource* resource); 119 void removeFromNonpurgeableArray(Resource* resource); 120 void removeFromPurgeableQueue(Resource* resource); 121 // Resources in the resource map are reusable (can be returned from findAndRef), but are not 122 // necessarily purgeable. 123 void addToResourceMap(Resource* resource); 124 void removeFromResourceMap(Resource* resource); 125 126 // This will return true if any resources were actually returned to the cache 127 bool processReturnedResources(Resource* queueHead=nullptr); 128 // Returns the next resource in the linked list of the return queue 129 Resource* processReturnedResource(Resource*); 130 131 uint32_t getNextUseToken(); 132 void setResourceUseToken(Resource*, uint32_t token); 133 134 overbudget()135 bool overbudget() const { return fBudgetedBytes > fMaxBytes; } 136 void purgeAsNeeded(); 137 void purgeResource(Resource*); 138 // Passing in a nullptr for purgeTime will trigger us to try and free all unlocked resources. 139 void purgeResources(const StdSteadyClock::time_point* purgeTime); 140 141 bool inPurgeableQueue(const Resource*) const; 142 143 #if defined(SK_DEBUG) 144 bool isInCache(const Resource* r) const; 145 void validate() const; 146 147 bool inNonpurgeableArray(const Resource*) const; 148 #else validate()149 void validate() const {} 150 #endif 151 152 struct MapTraits { GetKeyMapTraits153 static const GraphiteResourceKey& GetKey(const Resource& r) { return r.key(); } 154 HashMapTraits155 static uint32_t Hash(const GraphiteResourceKey& key) { return key.hash(); } OnFreeMapTraits156 static void OnFree(Resource*) {} 157 }; 158 using ResourceMap = SkTMultiMap<Resource, GraphiteResourceKey, MapTraits>; 159 CompareUseToken(Resource * const & a,Resource * const & b)160 static bool CompareUseToken(Resource* const& a, Resource* const& b) { 161 return a->lastUseToken() < b->lastUseToken(); 162 } AccessResourceIndex(Resource * const & res)163 static int* AccessResourceIndex(Resource* const& res) { return res->accessCacheIndex(); } 164 165 using PurgeableQueue = SkTDPQueue<Resource*, CompareUseToken, AccessResourceIndex>; 166 using ResourceArray = SkTDArray<Resource*>; 167 168 // NOTE: every Resource held by ResourceMap, ResourceArray, and PurgeableQueue will have a cache 169 // ref keeping them alive until after their pointer has been removed. 170 PurgeableQueue fPurgeableQueue; 171 ResourceArray fNonpurgeableResources; 172 ResourceMap fResourceMap; 173 174 std::unique_ptr<ProxyCache> fProxyCache; 175 176 // Our budget 177 size_t fMaxBytes; 178 size_t fBudgetedBytes = 0; 179 size_t fPurgeableBytes = 0; 180 181 // Whenever a resource is added to the cache or the result of a cache lookup, fUseToken is 182 // assigned as the resource's last use token and then incremented. fPurgeableQueue orders the 183 // purgeable resources by this value, and thus is used to purge resources in LRU order. 184 // Resources with a size of zero are set to have max uint32_t value. This will also put them at 185 // the end of the LRU priority queue. This will allow us to not purge these resources even when 186 // we are over budget. 187 uint32_t fUseToken = 0; 188 189 // The head of the return queue if the pointer is non-null, or null if there are no returned 190 // resources to process. A special sentinel address is used to encode when the cache is shut 191 // down. Once set to that address, this will never change value. 192 std::atomic<Resource*> fReturnQueue = nullptr; 193 194 SingleOwner* fSingleOwner = nullptr; 195 SkDEBUGCODE(int fCount = 0;) 196 }; 197 198 } // namespace skgpu::graphite 199 200 #endif // skgpu_graphite_ResourceCache_DEFINED 201