xref: /third_party/skia/src/gpu/GrGpuResource.h (revision cb93a386)
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#include <mutex>
15
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::notifyARefCntWillBeZero() 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::notifyARefCntIsZero() 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    // This enum is used to notify the GrResourceCache which type of ref just dropped to zero.
42    enum class LastRemovedRef {
43        kMainRef,            // This refers to fRefCnt
44        kCommandBufferUsage, // This refers to fCommandBufferUsageCnt
45    };
46
47    void unref() const {
48        SkASSERT(this->getRefCnt() > 0);
49        if (1 == fRefCnt.fetch_add(-1, std::memory_order_acq_rel)) {
50            this->notifyWillBeZero(LastRemovedRef::kMainRef);
51        }
52    }
53
54    void addCommandBufferUsage() const {
55        // No barrier required.
56        (void)fCommandBufferUsageCnt.fetch_add(+1, std::memory_order_relaxed);
57    }
58
59    void removeCommandBufferUsage() const {
60        SkASSERT(!this->hasNoCommandBufferUsages());
61        if (1 == fCommandBufferUsageCnt.fetch_add(-1, std::memory_order_acq_rel)) {
62            this->notifyWillBeZero(LastRemovedRef::kCommandBufferUsage);
63        }
64    }
65
66#if GR_TEST_UTILS
67    int32_t testingOnly_getRefCnt() const { return this->getRefCnt(); }
68#endif
69
70protected:
71    GrIORef() : fRefCnt(1), fCommandBufferUsageCnt(0) {}
72
73    bool internalHasRef() const { return SkToBool(this->getRefCnt()); }
74    bool internalHasNoCommandBufferUsages() const {
75        return SkToBool(this->hasNoCommandBufferUsages());
76    }
77
78    // Privileged method that allows going from ref count = 0 to ref count = 1.
79    void addInitialRef() const {
80        SkASSERT(fRefCnt >= 0);
81        // No barrier required.
82        (void)fRefCnt.fetch_add(+1, std::memory_order_relaxed);
83    }
84
85private:
86    void notifyWillBeZero(LastRemovedRef removedRef) const {
87        static_cast<const DERIVED*>(this)->notifyARefCntIsZero(removedRef);
88    }
89
90    int32_t getRefCnt() const { return fRefCnt.load(std::memory_order_relaxed); }
91
92    bool hasNoCommandBufferUsages() const {
93        if (0 == fCommandBufferUsageCnt.load(std::memory_order_acquire)) {
94            // The acquire barrier is only really needed if we return true.  It
95            // prevents code conditioned on the result of hasNoCommandBufferUsages() from running
96            // until previous owners are all totally done calling removeCommandBufferUsage().
97            return true;
98        }
99        return false;
100    }
101
102    mutable std::atomic<int32_t> fRefCnt;
103    mutable std::atomic<int32_t> fCommandBufferUsageCnt;
104
105    using INHERITED = SkNoncopyable;
106};
107
108struct GrGpuResourceTag {
109    GrGpuResourceTag() : fPid(0), fTid(0), fWid(0), fFid(0)
110    {
111        isGrGpuResourceTagValid = false;
112    }
113
114    GrGpuResourceTag(uint32_t pid, uint32_t tid, uint32_t wid, uint32_t fid, const std::string& name)
115        : fPid(pid), fTid(tid), fWid(wid), fFid(fid), fName(name)
116    {
117        isGrGpuResourceTagValid = fPid || fTid || fWid || fFid;
118    }
119
120    bool operator< (const GrGpuResourceTag& tag) const {
121        if (fPid != tag.fPid) {
122            return fPid < tag.fPid;
123        }
124        if (fTid != tag.fTid) {
125            return fTid < tag.fTid;
126        }
127        if (fWid != tag.fWid) {
128            return fWid < tag.fWid;
129        }
130        if (fFid != tag.fFid) {
131            return fFid < tag.fFid;
132        }
133        return false;
134    }
135
136    bool operator== (const GrGpuResourceTag& tag) const {
137        return (fPid == tag.fPid) && (fTid == tag.fTid) && (fWid == tag.fWid) && (fFid == tag.fFid);
138    }
139
140    std::string toString() const {
141        return "[" + std::to_string(fPid) + "," + std::to_string(fTid) + ","
142            + std::to_string(fWid) + "," + std::to_string(fFid) + "]";
143    }
144
145    bool isGrTagValid() const {
146        return isGrGpuResourceTagValid;
147    }
148
149    bool filter(GrGpuResourceTag& tag) const {
150        if (!isGrTagValid()) {
151            return !tag.isGrTagValid();
152        }
153        if (fPid && fPid != tag.fPid) {
154            return false;
155        }
156        if (fTid && fTid != tag.fTid) {
157            return false;
158        }
159        if (fWid && fWid != tag.fWid) {
160            return false;
161        }
162        if (fFid && fFid != tag.fFid) {
163            return false;
164        }
165        return true;
166    }
167
168    bool filter(GrGpuResourceTag&& tag) const {
169        if (!isGrTagValid()) {
170            return !tag.isGrTagValid();
171        }
172        if (fPid && fPid != tag.fPid) {
173            return false;
174        }
175        if (fTid && fTid != tag.fTid) {
176            return false;
177        }
178        if (fWid && fWid != tag.fWid) {
179            return false;
180        }
181        if (fFid && fFid != tag.fFid) {
182            return false;
183        }
184        return true;
185    }
186    uint32_t fPid;
187    uint32_t fTid;
188    uint32_t fWid;
189    uint32_t fFid;
190    std::string fName;
191    bool isGrGpuResourceTagValid;
192};
193
194/**
195 * Base class for objects that can be kept in the GrResourceCache.
196 */
197class SK_API GrGpuResource : public GrIORef<GrGpuResource> {
198public:
199    /**
200     * Tests whether a object has been abandoned or released. All objects will
201     * be in this state after their creating GrContext is destroyed or has
202     * contextLost called. It's up to the client to test wasDestroyed() before
203     * attempting to use an object if it holds refs on objects across
204     * ~GrContext, freeResources with the force flag, or contextLost.
205     *
206     * @return true if the object has been released or abandoned,
207     *         false otherwise.
208     */
209    bool wasDestroyed() const { return nullptr == fGpu; }
210
211    void setRealAlloc(bool realAlloc) { fRealAlloc = realAlloc; } // OH ISSUE: set real alloc flag
212    bool isRealAlloc() { return fRealAlloc; } // OH ISSUE: get real alloc flag
213    void setRealAllocSize(size_t realAllocSize) { fRealAllocSize = realAllocSize; } // OH ISSUE: set real alloc size
214    size_t getRealAllocSize() { return fRealAllocSize; } // OH ISSUE: get real alloc size
215
216    /**
217     * Retrieves the context that owns the object. Note that it is possible for
218     * this to return NULL. When objects have been release()ed or abandon()ed
219     * they no longer have an owning context. Destroying a GrDirectContext
220     * automatically releases all its resources.
221     */
222    const GrDirectContext* getContext() const;
223    GrDirectContext* getContext();
224
225    /**
226     * Retrieves the amount of GPU memory used by this resource in bytes. It is
227     * approximate since we aren't aware of additional padding or copies made
228     * by the driver.
229     *
230     * @return the amount of GPU memory used in bytes
231     */
232    size_t gpuMemorySize() const {
233        if (kInvalidGpuMemorySize == fGpuMemorySize) {
234            fGpuMemorySize = this->onGpuMemorySize();
235            SkASSERT(kInvalidGpuMemorySize != fGpuMemorySize);
236        }
237        return fGpuMemorySize;
238    }
239
240    class UniqueID {
241    public:
242        UniqueID() = default;
243
244        explicit UniqueID(uint32_t id) : fID(id) {}
245
246        uint32_t asUInt() const { return fID; }
247
248        bool operator==(const UniqueID& other) const { return fID == other.fID; }
249        bool operator!=(const UniqueID& other) const { return !(*this == other); }
250
251        void makeInvalid() { fID = SK_InvalidUniqueID; }
252        bool isInvalid() const { return  fID == SK_InvalidUniqueID; }
253
254    protected:
255        uint32_t fID = SK_InvalidUniqueID;
256    };
257
258    /**
259     * Gets an id that is unique for this GrGpuResource object. It is static in that it does
260     * not change when the content of the GrGpuResource object changes. This will never return
261     * 0.
262     */
263    UniqueID uniqueID() const { return fUniqueID; }
264
265    /** Returns the current unique key for the resource. It will be invalid if the resource has no
266        associated unique key. */
267    const GrUniqueKey& getUniqueKey() const { return fUniqueKey; }
268
269    /**
270     * Internal-only helper class used for manipulations of the resource by the cache.
271     */
272    class CacheAccess;
273    inline CacheAccess cacheAccess();
274    inline const CacheAccess cacheAccess() const;  // NOLINT(readability-const-return-type)
275
276    /**
277     * Internal-only helper class used for manipulations of the resource by GrSurfaceProxy.
278     */
279    class ProxyAccess;
280    inline ProxyAccess proxyAccess();
281
282    /**
283     * Internal-only helper class used for manipulations of the resource by internal code.
284     */
285    class ResourcePriv;
286    inline ResourcePriv resourcePriv();
287    inline const ResourcePriv resourcePriv() const;  // NOLINT(readability-const-return-type)
288
289    /**
290     * Dumps memory usage information for this GrGpuResource to traceMemoryDump.
291     * Typically, subclasses should not need to override this, and should only
292     * need to override setMemoryBacking.
293     **/
294    virtual void dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const;
295
296    /**
297     * Describes the type of gpu resource that is represented by the implementing
298     * class (e.g. texture, buffer object, stencil).  This data is used for diagnostic
299     * purposes by dumpMemoryStatistics().
300     *
301     * The value returned is expected to be long lived and will not be copied by the caller.
302     */
303    virtual const char* getResourceType() const = 0;
304
305    static uint32_t CreateUniqueID();
306
307    /**
308     * Set the resource tag.
309     */
310    void setResourceTag(const GrGpuResourceTag tag);
311
312    /**
313     * Get the resource tag.
314     *
315     * @return all GrGpuResourceTags.
316     */
317    GrGpuResourceTag getResourceTag() const { return fGrResourceTag; }
318
319protected:
320    // This must be called by every non-wrapped GrGpuObject. It should be called once the object is
321    // fully initialized (i.e. only from the constructors of the final class).
322    void registerWithCache(SkBudgeted);
323
324    // This must be called by every GrGpuObject that references any wrapped backend objects. It
325    // should be called once the object is fully initialized (i.e. only from the constructors of the
326    // final class).
327    void registerWithCacheWrapped(GrWrapCacheable);
328
329    GrGpuResource(GrGpu*);
330    virtual ~GrGpuResource();
331
332    GrGpu* getGpu() const { return fGpu; }
333
334    /** Overridden to free GPU resources in the backend API. */
335    virtual void onRelease() { }
336    /** Overridden to abandon any internal handles, ptrs, etc to backend API resources.
337        This may be called when the underlying 3D context is no longer valid and so no
338        backend API calls should be made. */
339    virtual void onAbandon() { }
340
341    /**
342     * Allows subclasses to add additional backing information to the SkTraceMemoryDump.
343     **/
344    virtual void setMemoryBacking(SkTraceMemoryDump*, const SkString&) const {}
345
346    /**
347     * Returns a string that uniquely identifies this resource.
348     */
349    SkString getResourceName() const;
350
351    /**
352     * A helper for subclasses that override dumpMemoryStatistics(). This method using a format
353     * consistent with the default implementation of dumpMemoryStatistics() but allows the caller
354     * to customize various inputs.
355     */
356    void dumpMemoryStatisticsPriv(SkTraceMemoryDump* traceMemoryDump, const SkString& resourceName,
357                                  const char* type, size_t size) const;
358
359
360private:
361    bool isPurgeable() const;
362    bool hasRef() const;
363    bool hasNoCommandBufferUsages() const;
364
365    /**
366     * Called by the registerWithCache if the resource is available to be used as scratch.
367     * Resource subclasses should override this if the instances should be recycled as scratch
368     * resources and populate the scratchKey with the key.
369     * By default resources are not recycled as scratch.
370     **/
371    virtual void computeScratchKey(GrScratchKey*) const {}
372
373    /**
374     * Removes references to objects in the underlying 3D API without freeing them.
375     * Called by CacheAccess.
376     */
377    void abandon();
378
379    /**
380     * Frees the object in the underlying 3D API. Called by CacheAccess.
381     */
382    void release();
383
384    virtual size_t onGpuMemorySize() const = 0;
385
386    // See comments in CacheAccess and ResourcePriv.
387    void setUniqueKey(const GrUniqueKey&);
388    void removeUniqueKey();
389    void notifyARefCntIsZero(LastRemovedRef removedRef) const;
390    void removeScratchKey();
391    void makeBudgeted();
392    void makeUnbudgeted();
393    void userRegisterResource();
394
395#ifdef SK_DEBUG
396    friend class GrGpu;  // for assert in GrGpu to access getGpu
397#endif
398
399    // An index into a heap when this resource is purgeable or an array when not. This is maintained
400    // by the cache.
401    int fCacheArrayIndex = -1;
402    // This value reflects how recently this resource was accessed in the cache. This is maintained
403    // by the cache.
404    uint32_t fTimestamp;
405    GrStdSteadyClock::time_point fTimeWhenBecamePurgeable;
406
407    static const size_t kInvalidGpuMemorySize = ~static_cast<size_t>(0);
408    GrScratchKey fScratchKey;
409    GrUniqueKey fUniqueKey;
410
411    // This is not ref'ed but abandon() or release() will be called before the GrGpu object
412    // is destroyed. Those calls set will this to NULL.
413    GrGpu* fGpu;
414    mutable size_t fGpuMemorySize = kInvalidGpuMemorySize;
415
416    GrBudgetedType fBudgetedType = GrBudgetedType::kUnbudgetedUncacheable;
417    bool fRefsWrappedObjects = false;
418    const UniqueID fUniqueID;
419    GrGpuResourceTag fGrResourceTag;
420
421    using INHERITED = GrIORef<GrGpuResource>;
422    friend class GrIORef<GrGpuResource>; // to access notifyRefCntWillBeZero and
423                                         // notifyARefCntIsZero.
424    std::mutex mutex_; // The gpu cache is released abnormally due to multi threads.
425
426    bool fRealAlloc = false; // OH ISSUE: real alloc flag
427    size_t fRealAllocSize = 0; // OH ISSUE: real alloc size
428};
429
430class GrGpuResource::ProxyAccess {
431private:
432    ProxyAccess(GrGpuResource* resource) : fResource(resource) {}
433
434    /** Proxies are allowed to take a resource from no refs to one ref. */
435    void ref(GrResourceCache* cache);
436
437    // No taking addresses of this type.
438    const CacheAccess* operator&() const = delete;
439    CacheAccess* operator&() = delete;
440
441    GrGpuResource* fResource;
442
443    friend class GrGpuResource;
444    friend class GrSurfaceProxy;
445};
446
447inline GrGpuResource::ProxyAccess GrGpuResource::proxyAccess() { return ProxyAccess(this); }
448
449#endif
450