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