xref: /aosp_15_r20/external/skia/src/gpu/graphite/ResourceCache.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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