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