Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
Resource.h
Go to the documentation of this file.
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_Resource_DEFINED
9#define skgpu_graphite_Resource_DEFINED
10
16
17#include <atomic>
18#include <functional>
19#include <string>
20#include <string_view>
21
22class SkMutex;
24
25namespace skgpu::graphite {
26
27class ResourceCache;
28class SharedContext;
29
30#if defined(GRAPHITE_TEST_UTILS)
31class Texture;
32#endif
33
34/**
35 * Base class for objects that can be kept in the ResourceCache.
36 */
37class Resource {
38public:
39 Resource(const Resource&) = delete;
40 Resource(Resource&&) = delete;
41 Resource& operator=(const Resource&) = delete;
43
44 // Adds a usage ref to the resource. Named ref so we can easily manage usage refs with sk_sp.
45 void ref() const {
46 // Only the cache should be able to add the first usage ref to a resource.
47 SkASSERT(this->hasUsageRef());
48 // No barrier required.
49 (void)fUsageRefCnt.fetch_add(+1, std::memory_order_relaxed);
50 }
51
52 // Removes a usage ref from the resource
53 void unref() const {
54 bool shouldFree = false;
55 {
56 SkAutoMutexExclusive locked(fUnrefMutex);
57 SkASSERT(this->hasUsageRef());
58 // A release here acts in place of all releases we "should" have been doing in ref().
59 if (1 == fUsageRefCnt.fetch_add(-1, std::memory_order_acq_rel)) {
60 shouldFree = this->notifyARefIsZero(LastRemovedRef::kUsage);
61 }
62 }
63 if (shouldFree) {
64 Resource* mutableThis = const_cast<Resource*>(this);
65 mutableThis->internalDispose();
66 }
67 }
68
69 // Adds a command buffer ref to the resource
70 void refCommandBuffer() const {
71 if (fCommandBufferRefsAsUsageRefs) {
72 return this->ref();
73 }
74 // No barrier required.
75 (void)fCommandBufferRefCnt.fetch_add(+1, std::memory_order_relaxed);
76 }
77
78 // Removes a command buffer ref from the resource
79 void unrefCommandBuffer() const {
80 if (fCommandBufferRefsAsUsageRefs) {
81 return this->unref();
82 }
83 bool shouldFree = false;
84 {
85 SkAutoMutexExclusive locked(fUnrefMutex);
86 SkASSERT(this->hasCommandBufferRef());
87 // A release here acts in place of all releases we "should" have been doing in ref().
88 if (1 == fCommandBufferRefCnt.fetch_add(-1, std::memory_order_acq_rel)) {
89 shouldFree = this->notifyARefIsZero(LastRemovedRef::kCommandBuffer);
90 }
91 }
92 if (shouldFree) {
93 Resource* mutableThis = const_cast<Resource*>(this);
94 mutableThis->internalDispose();
95 }
96 }
97
98 Ownership ownership() const { return fOwnership; }
99
100 skgpu::Budgeted budgeted() const { return fBudgeted; }
101
102 // Retrieves the amount of GPU memory used by this resource in bytes. It is approximate since we
103 // aren't aware of additional padding or copies made by the driver.
104 size_t gpuMemorySize() const { return fGpuMemorySize; }
105
106 class UniqueID {
107 public:
108 UniqueID() = default;
109
110 explicit UniqueID(uint32_t id) : fID(id) {}
111
112 uint32_t asUInt() const { return fID; }
113
114 bool operator==(const UniqueID& other) const { return fID == other.fID; }
115 bool operator!=(const UniqueID& other) const { return !(*this == other); }
116
117 private:
118 uint32_t fID = SK_InvalidUniqueID;
119 };
120
121 // Gets an id that is unique for this Resource object. It is static in that it does not change
122 // when the content of the Resource object changes. This will never return 0.
123 UniqueID uniqueID() const { return fUniqueID; }
124
125 // Describes the type of gpu resource that is represented by the implementing
126 // class (e.g. texture, buffer, etc). This data is used for diagnostic
127 // purposes by dumpMemoryStatistics().
128 //
129 // The value returned is expected to be long lived and will not be copied by the caller.
130 virtual const char* getResourceType() const = 0;
131
132 std::string getLabel() const { return fLabel; }
133
134 // We allow the label on a Resource to change when used for a different function. For example
135 // when reusing a scratch Texture we can change the label to match callers current use.
136 void setLabel(std::string_view label) {
137 fLabel = label;
138 // TODO: call into subclasses to allow them to set the label on actual GPU objects if they
139 // want to.
140 }
141
142 // Tests whether a object has been abandoned or released. All objects will be in this state
143 // after their creating Context is destroyed or abandoned.
144 //
145 // @return true if the object has been released or abandoned,
146 // false otherwise.
147 // TODO: As of now this function isn't really needed because in freeGpuData we are always
148 // deleting this object. However, I want to implement all the purging logic first to make sure
149 // we don't have a use case for calling internalDispose but not wanting to delete the actual
150 // object yet.
151 bool wasDestroyed() const { return fSharedContext == nullptr; }
152
153 const GraphiteResourceKey& key() const { return fKey; }
154 // This should only ever be called by the ResourceProvider
156 SkASSERT(key.shareable() == Shareable::kNo || this->budgeted() == skgpu::Budgeted::kYes);
157 fKey = key;
158 }
159
160 // Dumps memory usage information for this Resource to traceMemoryDump.
161 void dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const;
162
163 /**
164 * If the resource has a non-shareable key then this gives the resource subclass an opportunity
165 * to prepare itself to re-enter the cache. The ResourceCache extends its privilege to take the
166 * first UsageRef to this function via takeRef. If takeRef is called this resource will not
167 * immediately enter the cache but will be re-reprocessed with the Usage Ref count again reaches
168 * zero.
169 */
170 virtual void prepareForReturnToCache(const std::function<void()>& takeRef) {}
171
172#if defined(GRAPHITE_TEST_UTILS)
173 bool testingShouldDeleteASAP() const { return fDeleteASAP == DeleteASAP::kYes; }
174
175 virtual const Texture* asTexture() const { return nullptr; }
176#endif
177
178protected:
179 Resource(const SharedContext*,
180 Ownership,
182 size_t gpuMemorySize,
183 std::string_view label,
184 bool commandBufferRefsAsUsageRefs = false);
185 virtual ~Resource();
186
187 const SharedContext* sharedContext() const { return fSharedContext; }
188
189 // Overridden to free GPU resources in the backend API.
190 virtual void freeGpuData() = 0;
191
192 // Overridden to call any release callbacks, if necessary
193 virtual void invokeReleaseProc() {}
194
195 // Overridden to add extra information to the memory dump.
196 virtual void onDumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump,
197 const char* dumpName) const {}
198
199#ifdef SK_DEBUG
200 bool debugHasCommandBufferRef() const {
201 return hasCommandBufferRef();
202 }
203#endif
204
205 void setDeleteASAP() { fDeleteASAP = DeleteASAP::kYes; }
206
207private:
208 friend class ProxyCache; // for setDeleteASAP and updateAccessTime
209
210 enum class DeleteASAP : bool {
211 kNo = false,
212 kYes = true,
213 };
214
215 DeleteASAP shouldDeleteASAP() const { return fDeleteASAP; }
216
217 // In the ResourceCache this is called whenever a Resource is moved into the purgeableQueue. It
218 // may also be called by the ProxyCache to track the time on Resources it is holding on to.
219 void updateAccessTime() {
220 fLastAccess = skgpu::StdSteadyClock::now();
221 }
222 skgpu::StdSteadyClock::time_point lastAccessTime() const {
223 return fLastAccess;
224 }
225
226 ////////////////////////////////////////////////////////////////////////////
227 // The following set of functions are only meant to be called by the ResourceCache. We don't
228 // want them public general users of a Resource, but they also aren't purely internal calls.
229 ////////////////////////////////////////////////////////////////////////////
230 friend ResourceCache;
231
232 void makeBudgeted() { fBudgeted = skgpu::Budgeted::kYes; }
233 void makeUnbudgeted() { fBudgeted = skgpu::Budgeted::kNo; }
234
235 // This version of ref allows adding a ref when the usage count is 0. This should only be called
236 // from the ResourceCache.
237 void initialUsageRef() const {
238 // Only the cache should be able to add the first usage ref to a resource.
239 SkASSERT(fUsageRefCnt >= 0);
240 // No barrier required.
241 (void)fUsageRefCnt.fetch_add(+1, std::memory_order_relaxed);
242 }
243
244 bool isPurgeable() const;
245 int* accessReturnIndex() const { return &fReturnIndex; }
246 int* accessCacheIndex() const { return &fCacheArrayIndex; }
247
248 uint32_t timestamp() const { return fTimestamp; }
249 void setTimestamp(uint32_t ts) { fTimestamp = ts; }
250
251 void registerWithCache(sk_sp<ResourceCache>);
252
253 // Adds a cache ref to the resource. This is only called by ResourceCache. A Resource will only
254 // ever add a ref when the Resource is part of the cache (i.e. when insertResource is called)
255 // and while the Resource is in the ResourceCache::ReturnQueue.
256 void refCache() const {
257 // No barrier required.
258 (void)fCacheRefCnt.fetch_add(+1, std::memory_order_relaxed);
259 }
260
261 // Removes a cache ref from the resource. The unref here should only ever be called from the
262 // ResourceCache and only in the Recorder thread the ResourceCache is part of.
263 void unrefCache() const {
264 bool shouldFree = false;
265 {
266 SkAutoMutexExclusive locked(fUnrefMutex);
267 SkASSERT(this->hasCacheRef());
268 // A release here acts in place of all releases we "should" have been doing in ref().
269 if (1 == fCacheRefCnt.fetch_add(-1, std::memory_order_acq_rel)) {
270 shouldFree = this->notifyARefIsZero(LastRemovedRef::kCache);
271 }
272 }
273 if (shouldFree) {
274 Resource* mutableThis = const_cast<Resource*>(this);
275 mutableThis->internalDispose();
276 }
277 }
278
279#ifdef SK_DEBUG
280 bool isUsableAsScratch() const {
281 return fKey.shareable() == Shareable::kNo && !this->hasUsageRef() && fNonShareableInCache;
282 }
283#endif
284
285 ////////////////////////////////////////////////////////////////////////////
286 // The remaining calls are meant to be truely private
287 ////////////////////////////////////////////////////////////////////////////
288 bool hasUsageRef() const {
289 if (0 == fUsageRefCnt.load(std::memory_order_acquire)) {
290 // The acquire barrier is only really needed if we return true. It
291 // prevents code conditioned on the result of hasUsageRef() from running until previous
292 // owners are all totally done calling unref().
293 return false;
294 }
295 return true;
296 }
297
298 bool hasCommandBufferRef() const {
299 // Note that we don't check here for fCommandBufferRefsAsUsageRefs. This should always
300 // report zero if that value is true.
301 if (0 == fCommandBufferRefCnt.load(std::memory_order_acquire)) {
302 // The acquire barrier is only really needed if we return true. It
303 // prevents code conditioned on the result of hasCommandBufferRef() from running
304 // until previous owners are all totally done calling unrefCommandBuffer().
305 return false;
306 }
307 SkASSERT(!fCommandBufferRefsAsUsageRefs);
308 return true;
309 }
310
311 bool hasCacheRef() const {
312 if (0 == fCacheRefCnt.load(std::memory_order_acquire)) {
313 // The acquire barrier is only really needed if we return true. It
314 // prevents code conditioned on the result of hasUsageRef() from running until previous
315 // owners are all totally done calling unref().
316 return false;
317 }
318 return true;
319 }
320
321 bool hasAnyRefs() const {
322 return this->hasUsageRef() || this->hasCommandBufferRef() || this->hasCacheRef();
323 }
324
325 bool notifyARefIsZero(LastRemovedRef removedRef) const;
326
327 // Frees the object in the underlying 3D API.
328 void internalDispose();
329
330 // We need to guard calling unref on the usage and command buffer refs since they each could be
331 // unreffed on different threads. This can lead to calling notifyARefIsZero twice with each
332 // instance thinking there are no more refs left and both trying to delete the object.
333 mutable SkMutex fUnrefMutex;
334
335 SkDEBUGCODE(mutable bool fCalledRemovedFromCache = false;)
336
337 // This is not ref'ed but internalDispose() will be called before the Gpu object is destroyed.
338 // That call will set this to nullptr.
339 const SharedContext* fSharedContext;
340
341 mutable std::atomic<int32_t> fUsageRefCnt;
342 mutable std::atomic<int32_t> fCommandBufferRefCnt;
343 mutable std::atomic<int32_t> fCacheRefCnt;
344 // Indicates that CommandBufferRefs should be rerouted to UsageRefs.
345 const bool fCommandBufferRefsAsUsageRefs = false;
346
347 GraphiteResourceKey fKey;
348
349 sk_sp<ResourceCache> fReturnCache;
350 // An index into the return cache so we know whether or not the resource is already waiting to
351 // be returned or not.
352 mutable int fReturnIndex = -1;
353
354 Ownership fOwnership;
355
356 static const size_t kInvalidGpuMemorySize = ~static_cast<size_t>(0);
357 mutable size_t fGpuMemorySize = kInvalidGpuMemorySize;
358
359 // All resource created internally by Graphite and held in the ResourceCache as a shared
360 // resource or available scratch resource are considered budgeted. Resources that back client
361 // owned objects (e.g. SkSurface or SkImage) are not budgeted and do not count against cache
362 // limits.
363 skgpu::Budgeted fBudgeted;
364
365 // This is only used by ProxyCache::purgeProxiesNotUsedSince which is called from
366 // ResourceCache::purgeResourcesNotUsedSince. When kYes, this signals that the Resource
367 // should've been purged based on its timestamp at some point regardless of what its
368 // current timestamp may indicate (since the timestamp will be updated when the Resource
369 // is returned to the ResourceCache).
370 DeleteASAP fDeleteASAP = DeleteASAP::kNo;
371
372 // An index into a heap when this resource is purgeable or an array when not. This is maintained
373 // by the cache.
374 mutable int fCacheArrayIndex = -1;
375 // This value reflects how recently this resource was accessed in the cache. This is maintained
376 // by the cache.
377 uint32_t fTimestamp;
378 skgpu::StdSteadyClock::time_point fLastAccess;
379
380 const UniqueID fUniqueID;
381
382 // String used to describe the current use of this Resource.
383 std::string fLabel;
384
385 // This is only used during validation checking. Lots of the validation code depends on a
386 // resource being purgeable or not. However, purgeable itself just means having no refs. The
387 // refs can be removed before a Resource is returned to the cache (or even added to the
388 // ReturnQueue).
389 SkDEBUGCODE(mutable bool fNonShareableInCache = false;)
390};
391
392} // namespace skgpu::graphite
393
394#endif // skgpu_graphite_Resource_DEFINED
#define SkASSERT(cond)
Definition SkAssert.h:116
#define SkDEBUGCODE(...)
Definition SkDebug.h:23
@ kYes
Do pre-clip the geometry before applying the (perspective) matrix.
@ kNo
Don't pre-clip the geometry before applying the (perspective) matrix.
static constexpr uint32_t SK_InvalidUniqueID
Definition SkTypes.h:196
bool operator!=(const UniqueID &other) const
Definition Resource.h:115
bool operator==(const UniqueID &other) const
Definition Resource.h:114
bool wasDestroyed() const
Definition Resource.h:151
skgpu::Budgeted budgeted() const
Definition Resource.h:100
virtual void prepareForReturnToCache(const std::function< void()> &takeRef)
Definition Resource.h:170
const GraphiteResourceKey & key() const
Definition Resource.h:153
virtual void invokeReleaseProc()
Definition Resource.h:193
void refCommandBuffer() const
Definition Resource.h:70
Resource(const Resource &)=delete
virtual void onDumpMemoryStatistics(SkTraceMemoryDump *traceMemoryDump, const char *dumpName) const
Definition Resource.h:196
void unrefCommandBuffer() const
Definition Resource.h:79
size_t gpuMemorySize() const
Definition Resource.h:104
Resource & operator=(const Resource &)=delete
UniqueID uniqueID() const
Definition Resource.h:123
const SharedContext * sharedContext() const
Definition Resource.h:187
void setKey(const GraphiteResourceKey &key)
Definition Resource.h:155
void setLabel(std::string_view label)
Definition Resource.h:136
std::string getLabel() const
Definition Resource.h:132
Resource(Resource &&)=delete
Ownership ownership() const
Definition Resource.h:98
Resource & operator=(Resource &&)=delete
virtual const char * getResourceType() const =0
virtual void freeGpuData()=0
void dumpMemoryStatistics(SkTraceMemoryDump *traceMemoryDump) const
Definition Resource.cpp:98
Budgeted
Definition GpuTypes.h:35
const uintptr_t id