Home | History | Annotate | Download | only in vk
      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 abandonGPUData().
     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 {
     42         uint32_t operator()(const GrVkResource* const& r) const {
     43             SkASSERT(r);
     44             return r->fKey;
     45         }
     46     };
     47 
     48     class Trace {
     49     public:
     50         ~Trace() {
     51             fHashSet.foreach([](const GrVkResource* r) {
     52                 r->dumpInfo();
     53             });
     54             SkASSERT(0 == fHashSet.count());
     55         }
     56         void add(const GrVkResource* r) { fHashSet.add(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      */
     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      */
     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. */
     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      */
     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      */
    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      */
    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 */
    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
    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     /**
    157      * Called from unrefAndAbandon. Resources should do any necessary cleanup without freeing
    158      * underlying Vk objects. This must be overridden by subclasses that themselves store
    159      * GrVkResources since those resource will need to be unrefed.
    160      */
    161     virtual void abandonGPUData() const {}
    162 
    163     /**
    164      *  Called when the ref count goes to 0. Will free Vk resources.
    165      */
    166     void internal_dispose(const GrVkGpu* gpu) const {
    167         this->freeGPUData(gpu);
    168 #ifdef SK_TRACE_VK_RESOURCES
    169         fTrace.remove(this);
    170 #endif
    171         SkASSERT(0 == fRefCnt);
    172         fRefCnt = 1;
    173         delete this;
    174     }
    175 
    176     /**
    177      *  Internal_dispose without freeing Vk resources. Used when we've lost context.
    178      */
    179     void internal_dispose() const {
    180         this->abandonGPUData();
    181 #ifdef SK_TRACE_VK_RESOURCES
    182         fTrace.remove(this);
    183 #endif
    184         SkASSERT(0 == fRefCnt);
    185         fRefCnt = 1;
    186         delete this;
    187     }
    188 
    189     mutable int32_t fRefCnt;
    190 #ifdef SK_TRACE_VK_RESOURCES
    191     uint32_t fKey;
    192 #endif
    193 
    194     typedef SkNoncopyable INHERITED;
    195 };
    196 
    197 // This subclass allows for recycling
    198 class GrVkRecycledResource : public GrVkResource {
    199 public:
    200     // When recycle is called and there is only one ref left on the resource, we will signal that
    201     // the resource can be recycled for reuse. If the sublass (or whoever is managing this resource)
    202     // decides not to recycle the objects, it is their responsibility to call unref on the object.
    203     void recycle(GrVkGpu* gpu) const {
    204         if (this->unique()) {
    205             this->onRecycle(gpu);
    206         } else {
    207             this->unref(gpu);
    208         }
    209     }
    210 
    211 private:
    212     virtual void onRecycle(GrVkGpu* gpu) const = 0;
    213 };
    214 
    215 #endif
    216