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