• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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