• 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(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