Home | History | Annotate | Download | only in gpu
      1 /*
      2  * Copyright 2014 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 GrGpuResource_DEFINED
      9 #define GrGpuResource_DEFINED
     10 
     11 #include "../private/GrTypesPriv.h"
     12 #include "GrResourceKey.h"
     13 
     14 class GrContext;
     15 class GrGpu;
     16 class GrResourceCache;
     17 class SkTraceMemoryDump;
     18 
     19 /**
     20  * Base class for GrGpuResource. Handles the various types of refs we need. Separated out as a base
     21  * class to isolate the ref-cnting behavior and provide friendship without exposing all of
     22  * GrGpuResource.
     23  *
     24  * Gpu resources can have three types of refs:
     25  *   1) Normal ref (+ by ref(), - by unref()): These are used by code that is issuing draw calls
     26  *      that read and write the resource via GrOpList and by any object that must own a
     27  *      GrGpuResource and is itself owned (directly or indirectly) by Skia-client code.
     28  *   2) Pending read (+ by addPendingRead(), - by completedRead()): GrContext has scheduled a read
     29  *      of the resource by the GPU as a result of a skia API call but hasn't executed it yet.
     30  *   3) Pending write (+ by addPendingWrite(), - by completedWrite()): GrContext has scheduled a
     31  *      write to the resource by the GPU as a result of a skia API call but hasn't executed it yet.
     32  *
     33  * The latter two ref types are private and intended only for Gr core code.
     34  *
     35  * When all the ref/io counts reach zero DERIVED::notifyAllCntsAreZero() will be called (static poly
     36  * morphism using CRTP). Similarly when the ref (but not necessarily pending read/write) count
     37  * reaches 0 DERIVED::notifyRefCountIsZero() will be called. In the case when an unref() causes both
     38  * the ref cnt to reach zero and the other counts are zero, notifyRefCountIsZero() will be called
     39  * before notifyIsPurgeable(). Moreover, if notifyRefCountIsZero() returns false then
     40  * notifyAllRefCntsAreZero() won't be called at all. notifyRefCountIsZero() must return false if the
     41  * object may be deleted after notifyRefCntIsZero() returns.
     42  *
     43  * GrIORef and GrGpuResource are separate classes for organizational reasons and to be
     44  * able to give access via friendship to only the functions related to pending IO operations.
     45  */
     46 template <typename DERIVED> class GrIORef : public SkNoncopyable {
     47 public:
     48     // Some of the signatures are written to mirror SkRefCnt so that GrGpuResource can work with
     49     // templated helper classes (e.g. sk_sp). However, we have different categories of
     50     // refs (e.g. pending reads). We also don't require thread safety as GrCacheable objects are
     51     // not intended to cross thread boundaries.
     52     void ref() const {
     53         this->validate();
     54         ++fRefCnt;
     55     }
     56 
     57     void unref() const {
     58         this->validate();
     59 
     60         if (!(--fRefCnt)) {
     61             if (!static_cast<const DERIVED*>(this)->notifyRefCountIsZero()) {
     62                 return;
     63             }
     64         }
     65 
     66         this->didRemoveRefOrPendingIO(kRef_CntType);
     67     }
     68 
     69     void validate() const {
     70 #ifdef SK_DEBUG
     71         SkASSERT(fRefCnt >= 0);
     72         SkASSERT(fPendingReads >= 0);
     73         SkASSERT(fPendingWrites >= 0);
     74         SkASSERT(fRefCnt + fPendingReads + fPendingWrites >= 0);
     75 #endif
     76     }
     77 
     78 protected:
     79     GrIORef() : fRefCnt(1), fPendingReads(0), fPendingWrites(0) { }
     80 
     81     enum CntType {
     82         kRef_CntType,
     83         kPendingRead_CntType,
     84         kPendingWrite_CntType,
     85     };
     86 
     87     bool isPurgeable() const { return !this->internalHasRef() && !this->internalHasPendingIO(); }
     88 
     89     bool internalHasPendingRead() const { return SkToBool(fPendingReads); }
     90     bool internalHasPendingWrite() const { return SkToBool(fPendingWrites); }
     91     bool internalHasPendingIO() const { return SkToBool(fPendingWrites | fPendingReads); }
     92 
     93     bool internalHasRef() const { return SkToBool(fRefCnt); }
     94 
     95 private:
     96     friend class GrIORefProxy; // needs to forward on wrapped IO calls
     97     // This is for a unit test.
     98     template <typename T>
     99     friend void testingOnly_getIORefCnts(const T*, int* refCnt, int* readCnt, int* writeCnt);
    100 
    101     void addPendingRead() const {
    102         this->validate();
    103         ++fPendingReads;
    104     }
    105 
    106     void completedRead() const {
    107         this->validate();
    108         --fPendingReads;
    109         this->didRemoveRefOrPendingIO(kPendingRead_CntType);
    110     }
    111 
    112     void addPendingWrite() const {
    113         this->validate();
    114         ++fPendingWrites;
    115     }
    116 
    117     void completedWrite() const {
    118         this->validate();
    119         --fPendingWrites;
    120         this->didRemoveRefOrPendingIO(kPendingWrite_CntType);
    121     }
    122 
    123 private:
    124     void didRemoveRefOrPendingIO(CntType cntTypeRemoved) const {
    125         if (0 == fPendingReads && 0 == fPendingWrites && 0 == fRefCnt) {
    126             static_cast<const DERIVED*>(this)->notifyAllCntsAreZero(cntTypeRemoved);
    127         }
    128     }
    129 
    130     mutable int32_t fRefCnt;
    131     mutable int32_t fPendingReads;
    132     mutable int32_t fPendingWrites;
    133 
    134     // This class is used to manage conversion of refs to pending reads/writes.
    135     friend class GrGpuResourceRef;
    136     friend class GrResourceCache; // to check IO ref counts.
    137 
    138     template <typename, GrIOType> friend class GrPendingIOResource;
    139 };
    140 
    141 /**
    142  * Base class for objects that can be kept in the GrResourceCache.
    143  */
    144 class SK_API GrGpuResource : public GrIORef<GrGpuResource> {
    145 public:
    146 
    147     /**
    148      * Tests whether a object has been abandoned or released. All objects will
    149      * be in this state after their creating GrContext is destroyed or has
    150      * contextLost called. It's up to the client to test wasDestroyed() before
    151      * attempting to use an object if it holds refs on objects across
    152      * ~GrContext, freeResources with the force flag, or contextLost.
    153      *
    154      * @return true if the object has been released or abandoned,
    155      *         false otherwise.
    156      */
    157     bool wasDestroyed() const { return NULL == fGpu; }
    158 
    159     /**
    160      * Retrieves the context that owns the object. Note that it is possible for
    161      * this to return NULL. When objects have been release()ed or abandon()ed
    162      * they no longer have an owning context. Destroying a GrContext
    163      * automatically releases all its resources.
    164      */
    165     const GrContext* getContext() const;
    166     GrContext* getContext();
    167 
    168     /**
    169      * Retrieves the amount of GPU memory used by this resource in bytes. It is
    170      * approximate since we aren't aware of additional padding or copies made
    171      * by the driver.
    172      *
    173      * @return the amount of GPU memory used in bytes
    174      */
    175     size_t gpuMemorySize() const {
    176         if (kInvalidGpuMemorySize == fGpuMemorySize) {
    177             fGpuMemorySize = this->onGpuMemorySize();
    178             SkASSERT(kInvalidGpuMemorySize != fGpuMemorySize);
    179         }
    180         return fGpuMemorySize;
    181     }
    182 
    183     class UniqueID {
    184     public:
    185         static UniqueID InvalidID() {
    186             return UniqueID(uint32_t(SK_InvalidUniqueID));
    187         }
    188 
    189         UniqueID() {}
    190 
    191         explicit UniqueID(uint32_t id) : fID(id) {}
    192 
    193         uint32_t asUInt() const { return fID; }
    194 
    195         bool operator==(const UniqueID& other) const {
    196             return fID == other.fID;
    197         }
    198         bool operator!=(const UniqueID& other) const {
    199             return !(*this == other);
    200         }
    201 
    202         void makeInvalid() { fID = SK_InvalidUniqueID; }
    203         bool isInvalid() const { return SK_InvalidUniqueID == fID; }
    204 
    205     protected:
    206         uint32_t fID;
    207     };
    208 
    209     /**
    210      * Gets an id that is unique for this GrGpuResource object. It is static in that it does
    211      * not change when the content of the GrGpuResource object changes. This will never return
    212      * 0.
    213      */
    214     UniqueID uniqueID() const { return fUniqueID; }
    215 
    216     /** Returns the current unique key for the resource. It will be invalid if the resource has no
    217         associated unique key. */
    218     const GrUniqueKey& getUniqueKey() const { return fUniqueKey; }
    219 
    220     /**
    221      * Internal-only helper class used for manipulations of the resource by the cache.
    222      */
    223     class CacheAccess;
    224     inline CacheAccess cacheAccess();
    225     inline const CacheAccess cacheAccess() const;
    226 
    227     /**
    228      * Internal-only helper class used for manipulations of the resource by internal code.
    229      */
    230     class ResourcePriv;
    231     inline ResourcePriv resourcePriv();
    232     inline const ResourcePriv resourcePriv() const;
    233 
    234     /**
    235      * Removes references to objects in the underlying 3D API without freeing them.
    236      * Called by CacheAccess.
    237      * In general this method should not be called outside of skia. It was
    238      * made by public for a special case where it needs to be called in Blink
    239      * when a texture becomes unsafe to use after having been shared through
    240      * a texture mailbox.
    241      */
    242     void abandon();
    243 
    244     /**
    245      * Dumps memory usage information for this GrGpuResource to traceMemoryDump.
    246      * Typically, subclasses should not need to override this, and should only
    247      * need to override setMemoryBacking.
    248      **/
    249     virtual void dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const;
    250 
    251     static uint32_t CreateUniqueID();
    252 
    253 protected:
    254     // This must be called by every non-wrapped GrGpuObject. It should be called once the object is
    255     // fully initialized (i.e. only from the constructors of the final class).
    256     void registerWithCache(SkBudgeted);
    257 
    258     // This must be called by every GrGpuObject that references any wrapped backend objects. It
    259     // should be called once the object is fully initialized (i.e. only from the constructors of the
    260     // final class).
    261     void registerWithCacheWrapped();
    262 
    263     GrGpuResource(GrGpu*);
    264     virtual ~GrGpuResource();
    265 
    266     GrGpu* getGpu() const { return fGpu; }
    267 
    268     /** Overridden to free GPU resources in the backend API. */
    269     virtual void onRelease() { }
    270     /** Overridden to abandon any internal handles, ptrs, etc to backend API resources.
    271         This may be called when the underlying 3D context is no longer valid and so no
    272         backend API calls should be made. */
    273     virtual void onAbandon() { }
    274 
    275     /**
    276      * This entry point should be called whenever gpuMemorySize() should report a different size.
    277      * The cache will call gpuMemorySize() to update the current size of the resource.
    278      */
    279     void didChangeGpuMemorySize() const;
    280 
    281     /**
    282      * Allows subclasses to add additional backing information to the SkTraceMemoryDump. Called by
    283      * onMemoryDump. The default implementation adds no backing information.
    284      **/
    285     virtual void setMemoryBacking(SkTraceMemoryDump*, const SkString&) const {}
    286 
    287 private:
    288     /**
    289      * Called by the registerWithCache if the resource is available to be used as scratch.
    290      * Resource subclasses should override this if the instances should be recycled as scratch
    291      * resources and populate the scratchKey with the key.
    292      * By default resources are not recycled as scratch.
    293      **/
    294     virtual void computeScratchKey(GrScratchKey*) const { }
    295 
    296     /**
    297      * Frees the object in the underlying 3D API. Called by CacheAccess.
    298      */
    299     void release();
    300 
    301     virtual size_t onGpuMemorySize() const = 0;
    302 
    303     // See comments in CacheAccess and ResourcePriv.
    304     void setUniqueKey(const GrUniqueKey&);
    305     void removeUniqueKey();
    306     void notifyAllCntsAreZero(CntType) const;
    307     bool notifyRefCountIsZero() const;
    308     void removeScratchKey();
    309     void makeBudgeted();
    310     void makeUnbudgeted();
    311 
    312 #ifdef SK_DEBUG
    313     friend class GrGpu;  // for assert in GrGpu to access getGpu
    314 #endif
    315 
    316     // An index into a heap when this resource is purgeable or an array when not. This is maintained
    317     // by the cache.
    318     int fCacheArrayIndex;
    319     // This value reflects how recently this resource was accessed in the cache. This is maintained
    320     // by the cache.
    321     uint32_t fTimestamp;
    322     uint32_t fExternalFlushCntWhenBecamePurgeable;
    323     GrStdSteadyClock::time_point fTimeWhenBecamePurgeable;
    324 
    325     static const size_t kInvalidGpuMemorySize = ~static_cast<size_t>(0);
    326     GrScratchKey fScratchKey;
    327     GrUniqueKey fUniqueKey;
    328 
    329     // This is not ref'ed but abandon() or release() will be called before the GrGpu object
    330     // is destroyed. Those calls set will this to NULL.
    331     GrGpu* fGpu;
    332     mutable size_t fGpuMemorySize;
    333 
    334     SkBudgeted fBudgeted;
    335     bool fRefsWrappedObjects;
    336     const UniqueID fUniqueID;
    337 
    338     typedef GrIORef<GrGpuResource> INHERITED;
    339     friend class GrIORef<GrGpuResource>; // to access notifyAllCntsAreZero and notifyRefCntIsZero.
    340 };
    341 
    342 #endif
    343