1 /*
2 * Copyright 2022 Google LLC
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 "src/gpu/graphite/Resource.h"
9
10 #include "include/core/SkTraceMemoryDump.h"
11 #include "src/gpu/graphite/ResourceCache.h"
12
13 namespace skgpu::graphite {
14
15 namespace {
create_unique_id()16 uint32_t create_unique_id() {
17 static std::atomic<uint32_t> nextID{1};
18 uint32_t id;
19 do {
20 id = nextID.fetch_add(1, std::memory_order_relaxed);
21 } while (id == SK_InvalidUniqueID);
22 return id;
23 }
24 } // namespace anonymous
25
Resource(const SharedContext * sharedContext,Ownership ownership,skgpu::Budgeted budgeted,size_t gpuMemorySize,bool commandBufferRefsAsUsageRefs)26 Resource::Resource(const SharedContext* sharedContext,
27 Ownership ownership,
28 skgpu::Budgeted budgeted,
29 size_t gpuMemorySize,
30 bool commandBufferRefsAsUsageRefs)
31 : fSharedContext(sharedContext)
32 , fUsageRefCnt(1)
33 , fCommandBufferRefCnt(0)
34 , fCacheRefCnt(0)
35 , fCommandBufferRefsAsUsageRefs(commandBufferRefsAsUsageRefs)
36 , fOwnership(ownership)
37 , fGpuMemorySize(gpuMemorySize)
38 , fBudgeted(budgeted)
39 , fUniqueID(create_unique_id()) {
40 // If we don't own the resource that must mean its wrapped in a client object. Thus we should
41 // not be budgeted
42 SkASSERT(fOwnership == Ownership::kOwned || fBudgeted == skgpu::Budgeted::kNo);
43 }
44
~Resource()45 Resource::~Resource() {
46 // The cache should have released or destroyed this resource.
47 SkASSERT(this->wasDestroyed());
48 }
49
registerWithCache(sk_sp<ResourceCache> returnCache)50 void Resource::registerWithCache(sk_sp<ResourceCache> returnCache) {
51 SkASSERT(!fReturnCache);
52 SkASSERT(returnCache);
53
54 fReturnCache = std::move(returnCache);
55 }
56
notifyARefIsZero(LastRemovedRef removedRef) const57 bool Resource::notifyARefIsZero(LastRemovedRef removedRef) const {
58 // No resource should have been destroyed if there was still any sort of ref on it.
59 SkASSERT(!this->wasDestroyed());
60
61 Resource* mutableThis = const_cast<Resource*>(this);
62
63 // TODO: We have not switched all resources to use the ResourceCache yet. Once we do we should
64 // be able to assert that we have an fCacheReturn.
65 // SkASSERT(fReturnCache);
66 if (removedRef != LastRemovedRef::kCache &&
67 fReturnCache &&
68 fReturnCache->returnResource(mutableThis, removedRef)) {
69 return false;
70 }
71
72 if (!this->hasAnyRefs()) {
73 return true;
74 }
75 return false;
76 }
77
internalDispose()78 void Resource::internalDispose() {
79 SkASSERT(fSharedContext);
80 this->invokeReleaseProc();
81 this->freeGpuData();
82 fSharedContext = nullptr;
83 // TODO: If we ever support freeing all the backend objects without deleting the object, we'll
84 // need to add a hasAnyRefs() check here.
85 delete this;
86 }
87
isPurgeable() const88 bool Resource::isPurgeable() const {
89 // For being purgeable we don't care if there are cacheRefs on the object since the cacheRef
90 // will always be greater than 1 since we add one on insert and don't remove that ref until
91 // the Resource is removed from the cache.
92 return !(this->hasUsageRef() || this->hasCommandBufferRef());
93 }
94
dumpMemoryStatistics(SkTraceMemoryDump * traceMemoryDump) const95 void Resource::dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const {
96 if (this->ownership() == Ownership::kWrapped && !traceMemoryDump->shouldDumpWrappedObjects()) {
97 return;
98 }
99
100 if (this->budgeted() == skgpu::Budgeted::kNo &&
101 !traceMemoryDump->shouldDumpUnbudgetedObjects()) {
102 return;
103 }
104
105 size_t size = this->gpuMemorySize();
106
107 // Avoid dumping zero-sized objects (e.g. Samplers, pipelines, etc) except memoryless textures.
108 // TODO: Would a client ever actually want to see all of this? Wouldn't be hard to add it as an
109 // option.
110 if (size == 0 && this->asTexture() == nullptr) {
111 return;
112 }
113
114 SkString resourceName("skia/gpu_resources/resource_");
115 resourceName.appendU32(this->uniqueID().asUInt());
116
117 traceMemoryDump->dumpNumericValue(resourceName.c_str(), "size", "bytes", size);
118 traceMemoryDump->dumpStringValue(resourceName.c_str(), "type", this->getResourceType());
119 traceMemoryDump->dumpStringValue(resourceName.c_str(), "label", this->getLabel().c_str());
120 if (this->isPurgeable()) {
121 traceMemoryDump->dumpNumericValue(resourceName.c_str(), "purgeable_size", "bytes", size);
122 }
123 if (traceMemoryDump->shouldDumpWrappedObjects()) {
124 traceMemoryDump->dumpWrappedState(resourceName.c_str(),
125 this->ownership() == Ownership::kWrapped);
126 }
127 if (traceMemoryDump->shouldDumpUnbudgetedObjects()) {
128 traceMemoryDump->dumpBudgetedState(resourceName.c_str(),
129 this->budgeted() == skgpu::Budgeted::kYes);
130 }
131
132 this->onDumpMemoryStatistics(traceMemoryDump, resourceName.c_str());
133
134 // TODO: implement this to report real gpu id backing the resource. Will be virtual implemented
135 // by backend specific resource subclasses.
136 //this->setMemoryBacking(traceMemoryDump, resourceName);
137 }
138
139 } // namespace skgpu::graphite
140
141