blob: fb1af962f4a9ffdcad09d77d91cca22d3f67a92d [file] [log] [blame]
/*
* Copyright 2022 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "src/gpu/graphite/Resource.h"
#include "include/core/SkTraceMemoryDump.h"
#include "src/gpu/graphite/ResourceCache.h"
namespace skgpu::graphite {
namespace {
uint32_t create_unique_id() {
static std::atomic<uint32_t> nextID{1};
uint32_t id;
do {
id = nextID.fetch_add(1, std::memory_order_relaxed);
} while (id == SK_InvalidUniqueID);
return id;
}
} // namespace anonymous
Resource::Resource(const SharedContext* sharedContext,
Ownership ownership,
skgpu::Budgeted budgeted,
size_t gpuMemorySize,
bool commandBufferRefsAsUsageRefs)
: fSharedContext(sharedContext)
, fUsageRefCnt(1)
, fCommandBufferRefCnt(0)
, fCacheRefCnt(0)
, fCommandBufferRefsAsUsageRefs(commandBufferRefsAsUsageRefs)
, fOwnership(ownership)
, fGpuMemorySize(gpuMemorySize)
, fBudgeted(budgeted)
, fUniqueID(create_unique_id()) {
// If we don't own the resource that must mean its wrapped in a client object. Thus we should
// not be budgeted
SkASSERT(fOwnership == Ownership::kOwned || fBudgeted == skgpu::Budgeted::kNo);
}
Resource::~Resource() {
// The cache should have released or destroyed this resource.
SkASSERT(this->wasDestroyed());
}
void Resource::registerWithCache(sk_sp<ResourceCache> returnCache) {
SkASSERT(!fReturnCache);
SkASSERT(returnCache);
fReturnCache = std::move(returnCache);
}
bool Resource::notifyARefIsZero(LastRemovedRef removedRef) const {
// No resource should have been destroyed if there was still any sort of ref on it.
SkASSERT(!this->wasDestroyed());
Resource* mutableThis = const_cast<Resource*>(this);
// TODO: We have not switched all resources to use the ResourceCache yet. Once we do we should
// be able to assert that we have an fCacheReturn.
// SkASSERT(fReturnCache);
if (removedRef != LastRemovedRef::kCache &&
fReturnCache &&
fReturnCache->returnResource(mutableThis, removedRef)) {
return false;
}
if (!this->hasAnyRefs()) {
return true;
}
return false;
}
void Resource::internalDispose() {
SkASSERT(fSharedContext);
this->invokeReleaseProc();
this->freeGpuData();
fSharedContext = nullptr;
// TODO: If we ever support freeing all the backend objects without deleting the object, we'll
// need to add a hasAnyRefs() check here.
delete this;
}
bool Resource::isPurgeable() const {
// For being purgeable we don't care if there are cacheRefs on the object since the cacheRef
// will always be greater than 1 since we add one on insert and don't remove that ref until
// the Resource is removed from the cache.
return !(this->hasUsageRef() || this->hasCommandBufferRef());
}
void Resource::dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const {
if (this->ownership() == Ownership::kWrapped && !traceMemoryDump->shouldDumpWrappedObjects()) {
return;
}
if (this->budgeted() == skgpu::Budgeted::kNo &&
!traceMemoryDump->shouldDumpUnbudgetedObjects()) {
return;
}
size_t size = this->gpuMemorySize();
// Avoid dumping objects without a size (e.g. Samplers, pipelines, etc).
// TODO: Would a client ever actually want to see all of this? Wouldn't be hard to add it as an
// option.
if (size == 0) {
return;
}
SkString resourceName("skia/gpu_resources/resource_");
resourceName.appendU32(this->uniqueID().asUInt());
traceMemoryDump->dumpNumericValue(resourceName.c_str(), "size", "bytes", size);
traceMemoryDump->dumpStringValue(resourceName.c_str(), "type", this->getResourceType());
traceMemoryDump->dumpStringValue(resourceName.c_str(), "label", this->getLabel().c_str());
if (this->isPurgeable()) {
traceMemoryDump->dumpNumericValue(resourceName.c_str(), "purgeable_size", "bytes", size);
}
if (traceMemoryDump->shouldDumpWrappedObjects()) {
traceMemoryDump->dumpWrappedState(resourceName.c_str(),
this->ownership() == Ownership::kWrapped);
}
if (traceMemoryDump->shouldDumpUnbudgetedObjects()) {
traceMemoryDump->dumpBudgetedState(resourceName.c_str(),
this->budgeted() == skgpu::Budgeted::kYes);
}
this->onDumpMemoryStatistics(traceMemoryDump, resourceName.c_str());
// TODO: implement this to report real gpu id backing the resource. Will be virtual implemented
// by backend specific resource subclasses.
//this->setMemoryBacking(traceMemoryDump, resourceName);
}
} // namespace skgpu::graphite