xref: /aosp_15_r20/external/skia/src/gpu/graphite/Resource.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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