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