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