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 "include/private/GrResourceKey.h"
12 #include "include/private/GrTypesPriv.h"
13 #include "include/private/SkNoncopyable.h"
14
15 class GrGpu;
16 class GrResourceCache;
17 class SkTraceMemoryDump;
18
19 /**
20 * Base class for GrGpuResource. Provides the hooks for resources to interact with the cache.
21 * Separated out as a base class to isolate the ref-cnting behavior and provide friendship without
22 * exposing all of GrGpuResource.
23 *
24 * PRIOR to the last ref being removed DERIVED::notifyARefCntWillBeZero() will be called
25 * (static poly morphism using CRTP). It is legal for additional ref's to be added
26 * during this time. AFTER the ref count reaches zero DERIVED::notifyARefCntIsZero() will be
27 * called.
28 */
29 template <typename DERIVED> class GrIORef : public SkNoncopyable {
30 public:
unique()31 bool unique() const { return fRefCnt == 1; }
32
ref()33 void ref() const {
34 // Only the cache should be able to add the first ref to a resource.
35 SkASSERT(this->getRefCnt() > 0);
36 // No barrier required.
37 (void)fRefCnt.fetch_add(+1, std::memory_order_relaxed);
38 }
39
40 // This enum is used to notify the GrResourceCache which type of ref just dropped to zero.
41 enum class LastRemovedRef {
42 kMainRef, // This refers to fRefCnt
43 kCommandBufferUsage, // This refers to fCommandBufferUsageCnt
44 };
45
unref()46 void unref() const {
47 SkASSERT(this->getRefCnt() > 0);
48 if (1 == fRefCnt.fetch_add(-1, std::memory_order_acq_rel)) {
49 this->notifyWillBeZero(LastRemovedRef::kMainRef);
50 }
51 }
52
addCommandBufferUsage()53 void addCommandBufferUsage() const {
54 // No barrier required.
55 (void)fCommandBufferUsageCnt.fetch_add(+1, std::memory_order_relaxed);
56 }
57
removeCommandBufferUsage()58 void removeCommandBufferUsage() const {
59 SkASSERT(!this->hasNoCommandBufferUsages());
60 if (1 == fCommandBufferUsageCnt.fetch_add(-1, std::memory_order_acq_rel)) {
61 this->notifyWillBeZero(LastRemovedRef::kCommandBufferUsage);
62 }
63 }
64
65 #if GR_TEST_UTILS
testingOnly_getRefCnt()66 int32_t testingOnly_getRefCnt() const { return this->getRefCnt(); }
67 #endif
68
69 protected:
GrIORef()70 GrIORef() : fRefCnt(1), fCommandBufferUsageCnt(0) {}
71
internalHasRef()72 bool internalHasRef() const { return SkToBool(this->getRefCnt()); }
internalHasNoCommandBufferUsages()73 bool internalHasNoCommandBufferUsages() const {
74 return SkToBool(this->hasNoCommandBufferUsages());
75 }
76
77 // Privileged method that allows going from ref count = 0 to ref count = 1.
addInitialRef()78 void addInitialRef() const {
79 SkASSERT(fRefCnt >= 0);
80 // No barrier required.
81 (void)fRefCnt.fetch_add(+1, std::memory_order_relaxed);
82 }
83
84 private:
notifyWillBeZero(LastRemovedRef removedRef)85 void notifyWillBeZero(LastRemovedRef removedRef) const {
86 static_cast<const DERIVED*>(this)->notifyARefCntIsZero(removedRef);
87 }
88
getRefCnt()89 int32_t getRefCnt() const { return fRefCnt.load(std::memory_order_relaxed); }
90
hasNoCommandBufferUsages()91 bool hasNoCommandBufferUsages() const {
92 if (0 == fCommandBufferUsageCnt.load(std::memory_order_acquire)) {
93 // The acquire barrier is only really needed if we return true. It
94 // prevents code conditioned on the result of hasNoCommandBufferUsages() from running
95 // until previous owners are all totally done calling removeCommandBufferUsage().
96 return true;
97 }
98 return false;
99 }
100
101 mutable std::atomic<int32_t> fRefCnt;
102 mutable std::atomic<int32_t> fCommandBufferUsageCnt;
103
104 using INHERITED = SkNoncopyable;
105 };
106
107 /**
108 * Base class for objects that can be kept in the GrResourceCache.
109 */
110 class GrGpuResource : public GrIORef<GrGpuResource> {
111 public:
112 /**
113 * Tests whether a object has been abandoned or released. All objects will
114 * be in this state after their creating GrContext is destroyed or has
115 * contextLost called. It's up to the client to test wasDestroyed() before
116 * attempting to use an object if it holds refs on objects across
117 * ~GrContext, freeResources with the force flag, or contextLost.
118 *
119 * @return true if the object has been released or abandoned,
120 * false otherwise.
121 */
wasDestroyed()122 bool wasDestroyed() const { return nullptr == fGpu; }
123
124 /**
125 * Retrieves the context that owns the object. Note that it is possible for
126 * this to return NULL. When objects have been release()ed or abandon()ed
127 * they no longer have an owning context. Destroying a GrDirectContext
128 * automatically releases all its resources.
129 */
130 const GrDirectContext* getContext() const;
131 GrDirectContext* getContext();
132
133 /**
134 * Retrieves the amount of GPU memory used by this resource in bytes. It is
135 * approximate since we aren't aware of additional padding or copies made
136 * by the driver.
137 *
138 * @return the amount of GPU memory used in bytes
139 */
gpuMemorySize()140 size_t gpuMemorySize() const {
141 if (kInvalidGpuMemorySize == fGpuMemorySize) {
142 fGpuMemorySize = this->onGpuMemorySize();
143 SkASSERT(kInvalidGpuMemorySize != fGpuMemorySize);
144 }
145 return fGpuMemorySize;
146 }
147
148 class UniqueID {
149 public:
150 UniqueID() = default;
151
UniqueID(uint32_t id)152 explicit UniqueID(uint32_t id) : fID(id) {}
153
asUInt()154 uint32_t asUInt() const { return fID; }
155
156 bool operator==(const UniqueID& other) const { return fID == other.fID; }
157 bool operator!=(const UniqueID& other) const { return !(*this == other); }
158
makeInvalid()159 void makeInvalid() { fID = SK_InvalidUniqueID; }
isInvalid()160 bool isInvalid() const { return fID == SK_InvalidUniqueID; }
161
162 protected:
163 uint32_t fID = SK_InvalidUniqueID;
164 };
165
166 /**
167 * Gets an id that is unique for this GrGpuResource object. It is static in that it does
168 * not change when the content of the GrGpuResource object changes. This will never return
169 * 0.
170 */
uniqueID()171 UniqueID uniqueID() const { return fUniqueID; }
172
173 /** Returns the current unique key for the resource. It will be invalid if the resource has no
174 associated unique key. */
getUniqueKey()175 const GrUniqueKey& getUniqueKey() const { return fUniqueKey; }
176
177 /**
178 * Internal-only helper class used for manipulations of the resource by the cache.
179 */
180 class CacheAccess;
181 inline CacheAccess cacheAccess();
182 inline const CacheAccess cacheAccess() const; // NOLINT(readability-const-return-type)
183
184 /**
185 * Internal-only helper class used for manipulations of the resource by GrSurfaceProxy.
186 */
187 class ProxyAccess;
188 inline ProxyAccess proxyAccess();
189
190 /**
191 * Internal-only helper class used for manipulations of the resource by internal code.
192 */
193 class ResourcePriv;
194 inline ResourcePriv resourcePriv();
195 inline const ResourcePriv resourcePriv() const; // NOLINT(readability-const-return-type)
196
197 /**
198 * Dumps memory usage information for this GrGpuResource to traceMemoryDump.
199 * Typically, subclasses should not need to override this, and should only
200 * need to override setMemoryBacking.
201 **/
202 virtual void dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const;
203
204 /**
205 * Describes the type of gpu resource that is represented by the implementing
206 * class (e.g. texture, buffer object, stencil). This data is used for diagnostic
207 * purposes by dumpMemoryStatistics().
208 *
209 * The value returned is expected to be long lived and will not be copied by the caller.
210 */
211 virtual const char* getResourceType() const = 0;
212
213 static uint32_t CreateUniqueID();
214
215 protected:
216 // This must be called by every non-wrapped GrGpuObject. It should be called once the object is
217 // fully initialized (i.e. only from the constructors of the final class).
218 void registerWithCache(SkBudgeted);
219
220 // This must be called by every GrGpuObject that references any wrapped backend objects. It
221 // should be called once the object is fully initialized (i.e. only from the constructors of the
222 // final class).
223 void registerWithCacheWrapped(GrWrapCacheable);
224
225 GrGpuResource(GrGpu*);
226 virtual ~GrGpuResource();
227
getGpu()228 GrGpu* getGpu() const { return fGpu; }
229
230 /** Overridden to free GPU resources in the backend API. */
onRelease()231 virtual void onRelease() { }
232 /** Overridden to abandon any internal handles, ptrs, etc to backend API resources.
233 This may be called when the underlying 3D context is no longer valid and so no
234 backend API calls should be made. */
onAbandon()235 virtual void onAbandon() { }
236
237 /**
238 * Allows subclasses to add additional backing information to the SkTraceMemoryDump.
239 **/
setMemoryBacking(SkTraceMemoryDump *,const SkString &)240 virtual void setMemoryBacking(SkTraceMemoryDump*, const SkString&) const {}
241
242 /**
243 * Returns a string that uniquely identifies this resource.
244 */
245 SkString getResourceName() const;
246
247 /**
248 * A helper for subclasses that override dumpMemoryStatistics(). This method using a format
249 * consistent with the default implementation of dumpMemoryStatistics() but allows the caller
250 * to customize various inputs.
251 */
252 void dumpMemoryStatisticsPriv(SkTraceMemoryDump* traceMemoryDump, const SkString& resourceName,
253 const char* type, size_t size) const;
254
255
256 private:
257 bool isPurgeable() const;
258 bool hasRef() const;
259 bool hasNoCommandBufferUsages() const;
260
261 /**
262 * Called by the registerWithCache if the resource is available to be used as scratch.
263 * Resource subclasses should override this if the instances should be recycled as scratch
264 * resources and populate the scratchKey with the key.
265 * By default resources are not recycled as scratch.
266 **/
computeScratchKey(GrScratchKey *)267 virtual void computeScratchKey(GrScratchKey*) const {}
268
269 /**
270 * Removes references to objects in the underlying 3D API without freeing them.
271 * Called by CacheAccess.
272 */
273 void abandon();
274
275 /**
276 * Frees the object in the underlying 3D API. Called by CacheAccess.
277 */
278 void release();
279
280 virtual size_t onGpuMemorySize() const = 0;
281
282 // See comments in CacheAccess and ResourcePriv.
283 void setUniqueKey(const GrUniqueKey&);
284 void removeUniqueKey();
285 void notifyARefCntIsZero(LastRemovedRef removedRef) const;
286 void removeScratchKey();
287 void makeBudgeted();
288 void makeUnbudgeted();
289
290 #ifdef SK_DEBUG
291 friend class GrGpu; // for assert in GrGpu to access getGpu
292 #endif
293
294 // An index into a heap when this resource is purgeable or an array when not. This is maintained
295 // by the cache.
296 int fCacheArrayIndex;
297 // This value reflects how recently this resource was accessed in the cache. This is maintained
298 // by the cache.
299 uint32_t fTimestamp;
300 GrStdSteadyClock::time_point fTimeWhenBecamePurgeable;
301
302 static const size_t kInvalidGpuMemorySize = ~static_cast<size_t>(0);
303 GrScratchKey fScratchKey;
304 GrUniqueKey fUniqueKey;
305
306 // This is not ref'ed but abandon() or release() will be called before the GrGpu object
307 // is destroyed. Those calls set will this to NULL.
308 GrGpu* fGpu;
309 mutable size_t fGpuMemorySize = kInvalidGpuMemorySize;
310
311 GrBudgetedType fBudgetedType = GrBudgetedType::kUnbudgetedUncacheable;
312 bool fRefsWrappedObjects = false;
313 const UniqueID fUniqueID;
314
315 using INHERITED = GrIORef<GrGpuResource>;
316 friend class GrIORef<GrGpuResource>; // to access notifyRefCntWillBeZero and
317 // notifyARefCntIsZero.
318 };
319
320 class GrGpuResource::ProxyAccess {
321 private:
ProxyAccess(GrGpuResource * resource)322 ProxyAccess(GrGpuResource* resource) : fResource(resource) {}
323
324 /** Proxies are allowed to take a resource from no refs to one ref. */
325 void ref(GrResourceCache* cache);
326
327 // No taking addresses of this type.
328 const CacheAccess* operator&() const = delete;
329 CacheAccess* operator&() = delete;
330
331 GrGpuResource* fResource;
332
333 friend class GrGpuResource;
334 friend class GrSurfaceProxy;
335 };
336
proxyAccess()337 inline GrGpuResource::ProxyAccess GrGpuResource::proxyAccess() { return ProxyAccess(this); }
338
339 #endif
340