GrVkResource.h 7.4 KB

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