xref: /aosp_15_r20/external/skia/src/gpu/ganesh/GrManagedResource.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2015 Google Inc.
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 GrManagedResource_DEFINED
9 #define GrManagedResource_DEFINED
10 
11 #include "include/core/SkRefCnt.h"
12 #include "include/private/base/SkAssert.h"
13 #include "include/private/base/SkDebug.h"
14 #include "include/private/base/SkMutex.h"
15 #include "include/private/base/SkNoncopyable.h"
16 #include "include/private/base/SkThreadAnnotations.h"
17 #include "src/core/SkTHash.h"
18 #include "src/gpu/ganesh/GrSurface.h"
19 
20 #include <atomic>
21 #include <cstdint>
22 #include <utility>
23 
24 // uncomment to enable tracing of resource refs
25 #ifdef SK_DEBUG
26 #define SK_TRACE_MANAGED_RESOURCES
27 #endif
28 
29 /** \class GrManagedResource
30 
31   GrManagedResource is the base class for GPU resources that may be shared by
32   multiple objects, in particular objects that are tracked by a command buffer.
33   When an existing owner wants to share a reference, it calls ref().
34   When an owner wants to release its reference, it calls unref(). When the
35   shared object's reference count goes to zero as the result of an unref()
36   call, its (virtual) destructor is called. It is an error for the
37   destructor to be called explicitly (or via the object going out of scope on
38   the stack or calling delete) if getRefCnt() > 1.
39 
40   This is nearly identical to SkRefCntBase. The exceptions are that unref()
41   takes a GrGpu, and any derived classes must implement freeGPUData().
42 */
43 
44 class GrManagedResource : SkNoncopyable {
45 public:
46     // Simple refCount tracing, to ensure that everything ref'ed is unref'ed.
47 #ifdef SK_TRACE_MANAGED_RESOURCES
48     struct Hash {
operatorHash49         uint32_t operator()(const GrManagedResource* const& r) const {
50             SkASSERT(r);
51             return r->fKey;
52         }
53     };
54 
55     class Trace {
56     public:
~Trace()57         ~Trace() {
58             fHashSet.foreach([](const GrManagedResource* r) {
59                 r->dumpInfo();
60             });
61             SkASSERT(0 == fHashSet.count());
62         }
63 
add(const GrManagedResource * r)64         void add(const GrManagedResource* r) {
65             SkAutoMutexExclusive locked(fLock);
66             fHashSet.add(r);
67         }
68 
remove(const GrManagedResource * r)69         void remove(const GrManagedResource* r) {
70             SkAutoMutexExclusive locked(fLock);
71             fHashSet.remove(r);
72         }
73 
74     private:
75         SkMutex fLock;
76         skia_private::THashSet<const GrManagedResource*, GrManagedResource::Hash> fHashSet
77                 SK_GUARDED_BY(fLock);
78     };
79 
80     static std::atomic<uint32_t> fKeyCounter;
81 #endif
82 
83     /** Default construct, initializing the reference count to 1.
84      */
GrManagedResource()85     GrManagedResource() : fRefCnt(1) {
86 #ifdef SK_TRACE_MANAGED_RESOURCES
87         fKey = fKeyCounter.fetch_add(+1, std::memory_order_relaxed);
88         GetTrace()->add(this);
89 #endif
90     }
91 
92     /** Destruct, asserting that the reference count is 1.
93      */
~GrManagedResource()94     virtual ~GrManagedResource() {
95 #ifdef SK_DEBUG
96         auto count = this->getRefCnt();
97         SkASSERTF(count == 1, "fRefCnt was %d", count);
98         fRefCnt.store(0);    // illegal value, to catch us if we reuse after delete
99 #endif
100     }
101 
102 #ifdef SK_DEBUG
103     /** Return the reference count. Use only for debugging. */
getRefCnt()104     int32_t getRefCnt() const { return fRefCnt.load(); }
105 #endif
106 
107     /** May return true if the caller is the only owner.
108      *  Ensures that all previous owner's actions are complete.
109      */
unique()110     bool unique() const {
111         // The acquire barrier is only really needed if we return true.  It
112         // prevents code conditioned on the result of unique() from running
113         // until previous owners are all totally done calling unref().
114         return 1 == fRefCnt.load(std::memory_order_acquire);
115     }
116 
117     /** Increment the reference count.
118         Must be balanced by a call to unref() or unrefAndFreeResources().
119      */
ref()120     void ref() const {
121         // No barrier required.
122         SkDEBUGCODE(int newRefCount = )fRefCnt.fetch_add(+1, std::memory_order_relaxed);
123         SkASSERT(newRefCount >= 1);
124     }
125 
126     /** Decrement the reference count. If the reference count is 1 before the
127         decrement, then delete the object. Note that if this is the case, then
128         the object needs to have been allocated via new, and not on the stack.
129         Any GPU data associated with this resource will be freed before it's deleted.
130      */
unref()131     void unref() const {
132         // A release here acts in place of all releases we "should" have been doing in ref().
133         int newRefCount = fRefCnt.fetch_add(-1, std::memory_order_acq_rel);
134         SkASSERT(newRefCount >= 0);
135         if (newRefCount == 1) {
136             // Like unique(), the acquire is only needed on success, to make sure
137             // code in internal_dispose() doesn't happen before the decrement.
138             this->internal_dispose();
139         }
140     }
141 
142 #ifdef SK_DEBUG
143     // This is used for validating in the vulkan backend when using a main command buffer and temp
144     // command buffer at the same time. We need to validate that no images in the temp command
145     // buffer have been used in the main command buffer.
asVkImageResource()146     virtual const GrManagedResource* asVkImageResource() const { return nullptr; }
147 #endif
148 
149 #ifdef SK_DEBUG
validate()150     void validate() const {
151         SkASSERT(this->getRefCnt() > 0);
152     }
153 #endif
154 
155 #ifdef SK_TRACE_MANAGED_RESOURCES
156     /** Output a human-readable dump of this resource's information
157      */
158     virtual void dumpInfo() const = 0;
159 #endif
160 
161 private:
162 #ifdef SK_TRACE_MANAGED_RESOURCES
GetTrace()163     static Trace* GetTrace() {
164         static Trace kTrace;
165         return &kTrace;
166     }
167 #endif
168 
169     /** Must be implemented by any subclasses.
170      *  Deletes any GPU data associated with this resource
171      */
172     virtual void freeGPUData() const = 0;
173 
174     /**
175      *  Called when the ref count goes to 0. Will free GPU resources.
176      */
internal_dispose()177     void internal_dispose() const {
178         this->freeGPUData();
179 #ifdef SK_TRACE_MANAGED_RESOURCES
180         GetTrace()->remove(this);
181 #endif
182 
183 #ifdef SK_DEBUG
184         SkASSERT(0 == this->getRefCnt());
185         fRefCnt.store(1);
186 #endif
187         delete this;
188     }
189 
190     mutable std::atomic<int32_t> fRefCnt;
191 #ifdef SK_TRACE_MANAGED_RESOURCES
192     uint32_t fKey;
193 #endif
194 
195     using INHERITED = SkNoncopyable;
196 };
197 
198 // This subclass allows for recycling
199 class GrRecycledResource : public GrManagedResource {
200 public:
201     // When recycle is called and there is only one ref left on the resource, we will signal that
202     // the resource can be recycled for reuse. If the subclass (or whoever is managing this resource)
203     // decides not to recycle the objects, it is their responsibility to call unref on the object.
recycle()204     void recycle() const {
205         if (this->unique()) {
206             this->onRecycle();
207         } else {
208             this->unref();
209         }
210     }
211 
212 private:
213     virtual void onRecycle() const = 0;
214 };
215 
216 /** \class GrTextureResource
217 
218   GrTextureResource is the base class for managed texture resources, and implements the
219   basic releaseProc functionality for them.
220 
221 */
222 class GrTextureResource : public GrManagedResource {
223 public:
GrTextureResource()224     GrTextureResource() {}
225 
~GrTextureResource()226     ~GrTextureResource() override {
227         SkASSERT(!fReleaseHelper);
228     }
229 
setRelease(sk_sp<GrSurface::RefCntedReleaseProc> releaseHelper)230     void setRelease(sk_sp<GrSurface::RefCntedReleaseProc> releaseHelper) {
231         fReleaseHelper = std::move(releaseHelper);
232     }
233 
234 protected:
235     mutable sk_sp<GrSurface::RefCntedReleaseProc> fReleaseHelper;
236 
invokeReleaseProc()237     void invokeReleaseProc() const {
238         if (fReleaseHelper) {
239             // Depending on the ref count of fReleaseHelper this may or may not actually trigger
240             // the ReleaseProc to be called.
241             fReleaseHelper.reset();
242         }
243     }
244 
245 private:
246     using INHERITED = GrManagedResource;
247 };
248 
249 #endif
250