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