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