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