1/*
2 * Copyright 2011 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#include "GrGpuResource.h"
9#include "GrContext.h"
10#include "GrContextPriv.h"
11#include "GrResourceCache.h"
12#include "GrGpu.h"
13#include "GrGpuResourcePriv.h"
14#include "SkTraceMemoryDump.h"
15
16static inline GrResourceCache* get_resource_cache(GrGpu* gpu) {
17    SkASSERT(gpu);
18    SkASSERT(gpu->getContext());
19    SkASSERT(gpu->getContext()->contextPriv().getResourceCache());
20    return gpu->getContext()->contextPriv().getResourceCache();
21}
22
23GrGpuResource::GrGpuResource(GrGpu* gpu)
24    : fExternalFlushCntWhenBecamePurgeable(0)
25    , fGpu(gpu)
26    , fGpuMemorySize(kInvalidGpuMemorySize)
27    , fBudgeted(SkBudgeted::kNo)
28    , fRefsWrappedObjects(false)
29    , fUniqueID(CreateUniqueID()) {
30    SkDEBUGCODE(fCacheArrayIndex = -1);
31}
32
33void GrGpuResource::registerWithCache(SkBudgeted budgeted) {
34    SkASSERT(fBudgeted == SkBudgeted::kNo);
35    fBudgeted = budgeted;
36    this->computeScratchKey(&fScratchKey);
37    get_resource_cache(fGpu)->resourceAccess().insertResource(this);
38}
39
40void GrGpuResource::registerWithCacheWrapped() {
41    SkASSERT(fBudgeted == SkBudgeted::kNo);
42    // Currently resources referencing wrapped objects are not budgeted.
43    fRefsWrappedObjects = true;
44    get_resource_cache(fGpu)->resourceAccess().insertResource(this);
45}
46
47GrGpuResource::~GrGpuResource() {
48    // The cache should have released or destroyed this resource.
49    SkASSERT(this->wasDestroyed());
50}
51
52void GrGpuResource::release() {
53    SkASSERT(fGpu);
54    this->onRelease();
55    get_resource_cache(fGpu)->resourceAccess().removeResource(this);
56    fGpu = nullptr;
57    fGpuMemorySize = 0;
58}
59
60void GrGpuResource::abandon() {
61    if (this->wasDestroyed()) {
62        return;
63    }
64    SkASSERT(fGpu);
65    this->onAbandon();
66    get_resource_cache(fGpu)->resourceAccess().removeResource(this);
67    fGpu = nullptr;
68    fGpuMemorySize = 0;
69}
70
71void GrGpuResource::dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const {
72    if (this->fRefsWrappedObjects && !traceMemoryDump->shouldDumpWrappedObjects()) {
73        return;
74    }
75
76    this->dumpMemoryStatisticsPriv(traceMemoryDump, this->getResourceName(),
77                                   this->getResourceType(), this->gpuMemorySize());
78}
79
80void GrGpuResource::dumpMemoryStatisticsPriv(SkTraceMemoryDump* traceMemoryDump,
81                                             const SkString& resourceName,
82                                             const char* type, size_t size) const {
83    const char* tag = "Scratch";
84    if (fUniqueKey.isValid()) {
85        tag = (fUniqueKey.tag() != nullptr) ? fUniqueKey.tag() : "Other";
86    }
87
88    traceMemoryDump->dumpNumericValue(resourceName.c_str(), "size", "bytes", size);
89    traceMemoryDump->dumpStringValue(resourceName.c_str(), "type", type);
90    traceMemoryDump->dumpStringValue(resourceName.c_str(), "category", tag);
91    if (this->isPurgeable()) {
92        traceMemoryDump->dumpNumericValue(resourceName.c_str(), "purgeable_size", "bytes", size);
93    }
94
95    this->setMemoryBacking(traceMemoryDump, resourceName);
96}
97
98SkString GrGpuResource::getResourceName() const {
99    // Dump resource as "skia/gpu_resources/resource_#".
100    SkString resourceName("skia/gpu_resources/resource_");
101    resourceName.appendU32(this->uniqueID().asUInt());
102    return resourceName;
103}
104
105const GrContext* GrGpuResource::getContext() const {
106    if (fGpu) {
107        return fGpu->getContext();
108    } else {
109        return nullptr;
110    }
111}
112
113GrContext* GrGpuResource::getContext() {
114    if (fGpu) {
115        return fGpu->getContext();
116    } else {
117        return nullptr;
118    }
119}
120
121void GrGpuResource::didChangeGpuMemorySize() const {
122    if (this->wasDestroyed()) {
123        return;
124    }
125
126    size_t oldSize = fGpuMemorySize;
127    SkASSERT(kInvalidGpuMemorySize != oldSize);
128    fGpuMemorySize = kInvalidGpuMemorySize;
129    get_resource_cache(fGpu)->resourceAccess().didChangeGpuMemorySize(this, oldSize);
130}
131
132void GrGpuResource::removeUniqueKey() {
133    if (this->wasDestroyed()) {
134        return;
135    }
136    SkASSERT(fUniqueKey.isValid());
137    get_resource_cache(fGpu)->resourceAccess().removeUniqueKey(this);
138}
139
140void GrGpuResource::setUniqueKey(const GrUniqueKey& key) {
141    SkASSERT(this->internalHasRef());
142    SkASSERT(key.isValid());
143
144    // Uncached resources can never have a unique key, unless they're wrapped resources. Wrapped
145    // resources are a special case: the unique keys give us a weak ref so that we can reuse the
146    // same resource (rather than re-wrapping). When a wrapped resource is no longer referenced,
147    // it will always be released - it is never converted to a scratch resource.
148    if (SkBudgeted::kNo == this->resourcePriv().isBudgeted() && !this->fRefsWrappedObjects) {
149        return;
150    }
151
152    if (this->wasDestroyed()) {
153        return;
154    }
155
156    get_resource_cache(fGpu)->resourceAccess().changeUniqueKey(this, key);
157}
158
159void GrGpuResource::notifyAllCntsAreZero(CntType lastCntTypeToReachZero) const {
160    if (this->wasDestroyed()) {
161        // We've already been removed from the cache. Goodbye cruel world!
162        delete this;
163        return;
164    }
165
166    // We should have already handled this fully in notifyRefCntIsZero().
167    SkASSERT(kRef_CntType != lastCntTypeToReachZero);
168
169    GrGpuResource* mutableThis = const_cast<GrGpuResource*>(this);
170    static const uint32_t kFlag =
171        GrResourceCache::ResourceAccess::kAllCntsReachedZero_RefNotificationFlag;
172    get_resource_cache(fGpu)->resourceAccess().notifyCntReachedZero(mutableThis, kFlag);
173}
174
175bool GrGpuResource::notifyRefCountIsZero() const {
176    if (this->wasDestroyed()) {
177        // handle this in notifyAllCntsAreZero().
178        return true;
179    }
180
181    GrGpuResource* mutableThis = const_cast<GrGpuResource*>(this);
182    uint32_t flags = GrResourceCache::ResourceAccess::kRefCntReachedZero_RefNotificationFlag;
183    if (!this->internalHasPendingIO()) {
184        flags |= GrResourceCache::ResourceAccess::kAllCntsReachedZero_RefNotificationFlag;
185    }
186    get_resource_cache(fGpu)->resourceAccess().notifyCntReachedZero(mutableThis, flags);
187
188    // There is no need to call our notifyAllCntsAreZero function at this point since we already
189    // told the cache about the state of cnts.
190    return false;
191}
192
193void GrGpuResource::removeScratchKey() {
194    if (!this->wasDestroyed() && fScratchKey.isValid()) {
195        get_resource_cache(fGpu)->resourceAccess().willRemoveScratchKey(this);
196        fScratchKey.reset();
197    }
198}
199
200void GrGpuResource::makeBudgeted() {
201    if (!this->wasDestroyed() && SkBudgeted::kNo == fBudgeted) {
202        // Currently resources referencing wrapped objects are not budgeted.
203        SkASSERT(!fRefsWrappedObjects);
204        fBudgeted = SkBudgeted::kYes;
205        get_resource_cache(fGpu)->resourceAccess().didChangeBudgetStatus(this);
206    }
207}
208
209void GrGpuResource::makeUnbudgeted() {
210    if (!this->wasDestroyed() && SkBudgeted::kYes == fBudgeted &&
211        !fUniqueKey.isValid()) {
212        fBudgeted = SkBudgeted::kNo;
213        get_resource_cache(fGpu)->resourceAccess().didChangeBudgetStatus(this);
214    }
215}
216
217uint32_t GrGpuResource::CreateUniqueID() {
218    static int32_t gUniqueID = SK_InvalidUniqueID;
219    uint32_t id;
220    do {
221        id = static_cast<uint32_t>(sk_atomic_inc(&gUniqueID) + 1);
222    } while (id == SK_InvalidUniqueID);
223    return id;
224}
225