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/GrResourceKey.h"
12 #include "../private/GrTypesPriv.h"
13 #include "../private/SkNoncopyable.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 GrOpList 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 notifyAllCntsAreZero(). 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. sk_sp). 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.
ref()53     void ref() const {
54         this->validate();
55         ++fRefCnt;
56     }
57 
unref()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 
validate()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:
GrIORef()80     GrIORef() : fRefCnt(1), fPendingReads(0), fPendingWrites(0) { }
81 
82     enum CntType {
83         kRef_CntType,
84         kPendingRead_CntType,
85         kPendingWrite_CntType,
86     };
87 
internalHasPendingRead()88     bool internalHasPendingRead() const { return SkToBool(fPendingReads); }
internalHasPendingWrite()89     bool internalHasPendingWrite() const { return SkToBool(fPendingWrites); }
internalHasPendingIO()90     bool internalHasPendingIO() const { return SkToBool(fPendingWrites | fPendingReads); }
91 
internalHasRef()92     bool internalHasRef() const { return SkToBool(fRefCnt); }
internalHasUniqueRef()93     bool internalHasUniqueRef() const { return fRefCnt == 1; }
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 
addPendingRead()101     void addPendingRead() const {
102         this->validate();
103         ++fPendingReads;
104     }
105 
completedRead()106     void completedRead() const {
107         this->validate();
108         --fPendingReads;
109         this->didRemoveRefOrPendingIO(kPendingRead_CntType);
110     }
111 
addPendingWrite()112     void addPendingWrite() const {
113         this->validate();
114         ++fPendingWrites;
115     }
116 
completedWrite()117     void completedWrite() const {
118         this->validate();
119         --fPendingWrites;
120         this->didRemoveRefOrPendingIO(kPendingWrite_CntType);
121     }
122 
123 private:
didRemoveRefOrPendingIO(CntType cntTypeRemoved)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     friend class GrResourceCache; // to check IO ref counts.
135 
136     template <typename, GrIOType> friend class GrPendingIOResource;
137 };
138 
139 /**
140  * Base class for objects that can be kept in the GrResourceCache.
141  */
142 class SK_API GrGpuResource : public GrIORef<GrGpuResource> {
143 public:
144     /**
145      * Tests whether a object has been abandoned or released. All objects will
146      * be in this state after their creating GrContext is destroyed or has
147      * contextLost called. It's up to the client to test wasDestroyed() before
148      * attempting to use an object if it holds refs on objects across
149      * ~GrContext, freeResources with the force flag, or contextLost.
150      *
151      * @return true if the object has been released or abandoned,
152      *         false otherwise.
153      */
wasDestroyed()154     bool wasDestroyed() const { return nullptr == fGpu; }
155 
156     /**
157      * Retrieves the context that owns the object. Note that it is possible for
158      * this to return NULL. When objects have been release()ed or abandon()ed
159      * they no longer have an owning context. Destroying a GrContext
160      * automatically releases all its resources.
161      */
162     const GrContext* getContext() const;
163     GrContext* getContext();
164 
165     /**
166      * Retrieves the amount of GPU memory used by this resource in bytes. It is
167      * approximate since we aren't aware of additional padding or copies made
168      * by the driver.
169      *
170      * @return the amount of GPU memory used in bytes
171      */
gpuMemorySize()172     size_t gpuMemorySize() const {
173         if (kInvalidGpuMemorySize == fGpuMemorySize) {
174             fGpuMemorySize = this->onGpuMemorySize();
175             SkASSERT(kInvalidGpuMemorySize != fGpuMemorySize);
176         }
177         return fGpuMemorySize;
178     }
179 
180     class UniqueID {
181     public:
InvalidID()182         static UniqueID InvalidID() {
183             return UniqueID(uint32_t(SK_InvalidUniqueID));
184         }
185 
UniqueID()186         UniqueID() {}
187 
UniqueID(uint32_t id)188         explicit UniqueID(uint32_t id) : fID(id) {}
189 
asUInt()190         uint32_t asUInt() const { return fID; }
191 
192         bool operator==(const UniqueID& other) const {
193             return fID == other.fID;
194         }
195         bool operator!=(const UniqueID& other) const {
196             return !(*this == other);
197         }
198 
makeInvalid()199         void makeInvalid() { fID = SK_InvalidUniqueID; }
isInvalid()200         bool isInvalid() const { return SK_InvalidUniqueID == fID; }
201 
202     protected:
203         uint32_t fID;
204     };
205 
206     /**
207      * Gets an id that is unique for this GrGpuResource object. It is static in that it does
208      * not change when the content of the GrGpuResource object changes. This will never return
209      * 0.
210      */
uniqueID()211     UniqueID uniqueID() const { return fUniqueID; }
212 
213     /** Returns the current unique key for the resource. It will be invalid if the resource has no
214         associated unique key. */
getUniqueKey()215     const GrUniqueKey& getUniqueKey() const { return fUniqueKey; }
216 
217     /**
218      * Internal-only helper class used for manipulations of the resource by the cache.
219      */
220     class CacheAccess;
221     inline CacheAccess cacheAccess();
222     inline const CacheAccess cacheAccess() const;
223 
224     /**
225      * Internal-only helper class used for manipulations of the resource by internal code.
226      */
227     class ResourcePriv;
228     inline ResourcePriv resourcePriv();
229     inline const ResourcePriv resourcePriv() const;
230 
231     /**
232      * Dumps memory usage information for this GrGpuResource to traceMemoryDump.
233      * Typically, subclasses should not need to override this, and should only
234      * need to override setMemoryBacking.
235      **/
236     virtual void dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const;
237 
238     /**
239      * Describes the type of gpu resource that is represented by the implementing
240      * class (e.g. texture, buffer object, stencil).  This data is used for diagnostic
241      * purposes by dumpMemoryStatistics().
242      *
243      * The value returned is expected to be long lived and will not be copied by the caller.
244      */
245     virtual const char* getResourceType() const = 0;
246 
247     static uint32_t CreateUniqueID();
248 
249 protected:
250     // This must be called by every non-wrapped GrGpuObject. It should be called once the object is
251     // fully initialized (i.e. only from the constructors of the final class).
252     void registerWithCache(SkBudgeted);
253 
254     // This must be called by every GrGpuObject that references any wrapped backend objects. It
255     // should be called once the object is fully initialized (i.e. only from the constructors of the
256     // final class).
257     void registerWithCacheWrapped(GrWrapCacheable);
258 
259     GrGpuResource(GrGpu*);
260     virtual ~GrGpuResource();
261 
getGpu()262     GrGpu* getGpu() const { return fGpu; }
263 
264     /** Overridden to free GPU resources in the backend API. */
onRelease()265     virtual void onRelease() { }
266     /** Overridden to abandon any internal handles, ptrs, etc to backend API resources.
267         This may be called when the underlying 3D context is no longer valid and so no
268         backend API calls should be made. */
onAbandon()269     virtual void onAbandon() { }
270 
271     /**
272      * Allows subclasses to add additional backing information to the SkTraceMemoryDump.
273      **/
setMemoryBacking(SkTraceMemoryDump *,const SkString &)274     virtual void setMemoryBacking(SkTraceMemoryDump*, const SkString&) const {}
275 
276     /**
277      * Returns a string that uniquely identifies this resource.
278      */
279     SkString getResourceName() const;
280 
281     /**
282      * A helper for subclasses that override dumpMemoryStatistics(). This method using a format
283      * consistent with the default implementation of dumpMemoryStatistics() but allows the caller
284      * to customize various inputs.
285      */
286     void dumpMemoryStatisticsPriv(SkTraceMemoryDump* traceMemoryDump, const SkString& resourceName,
287                                   const char* type, size_t size) const;
288 
289 
290 private:
291     bool isPurgeable() const;
292     bool hasRefOrPendingIO() const;
293 
294     /**
295      * Called by the registerWithCache if the resource is available to be used as scratch.
296      * Resource subclasses should override this if the instances should be recycled as scratch
297      * resources and populate the scratchKey with the key.
298      * By default resources are not recycled as scratch.
299      **/
computeScratchKey(GrScratchKey *)300     virtual void computeScratchKey(GrScratchKey*) const {}
301 
302     /**
303      * Removes references to objects in the underlying 3D API without freeing them.
304      * Called by CacheAccess.
305      */
306     void abandon();
307 
308     /**
309      * Frees the object in the underlying 3D API. Called by CacheAccess.
310      */
311     void release();
312 
313     virtual size_t onGpuMemorySize() const = 0;
314 
315     /**
316      * Called by GrResourceCache when a resource loses its last ref or pending IO.
317      */
removedLastRefOrPendingIO()318     virtual void removedLastRefOrPendingIO() {}
319 
320     // See comments in CacheAccess and ResourcePriv.
321     void setUniqueKey(const GrUniqueKey&);
322     void removeUniqueKey();
323     void notifyAllCntsAreZero(CntType) const;
324     bool notifyRefCountIsZero() const;
325     void removeScratchKey();
326     void makeBudgeted();
327     void makeUnbudgeted();
328 
329 #ifdef SK_DEBUG
330     friend class GrGpu;  // for assert in GrGpu to access getGpu
331 #endif
332 
333     // An index into a heap when this resource is purgeable or an array when not. This is maintained
334     // by the cache.
335     int fCacheArrayIndex;
336     // This value reflects how recently this resource was accessed in the cache. This is maintained
337     // by the cache.
338     uint32_t fTimestamp;
339     GrStdSteadyClock::time_point fTimeWhenBecamePurgeable;
340 
341     static const size_t kInvalidGpuMemorySize = ~static_cast<size_t>(0);
342     GrScratchKey fScratchKey;
343     GrUniqueKey fUniqueKey;
344 
345     // This is not ref'ed but abandon() or release() will be called before the GrGpu object
346     // is destroyed. Those calls set will this to NULL.
347     GrGpu* fGpu;
348     mutable size_t fGpuMemorySize = kInvalidGpuMemorySize;
349 
350     GrBudgetedType fBudgetedType = GrBudgetedType::kUnbudgetedUncacheable;
351     bool fRefsWrappedObjects = false;
352     const UniqueID fUniqueID;
353 
354     typedef GrIORef<GrGpuResource> INHERITED;
355     friend class GrIORef<GrGpuResource>; // to access notifyAllCntsAreZero and notifyRefCntIsZero.
356 };
357 
358 #endif
359