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