blob: 4e5ae8df20bf2327b385cc853aeee7d28571093d [file] [log] [blame] [edit]
/*
* Copyright 2025 Rive
*/
#include "rive/renderer/gpu_resource.hpp"
namespace rive::gpu
{
GPUResource::~GPUResource() {}
void GPUResource::onRefCntReachedZero() const
{
// GPUResourceManager will hold off on deleting "this" until its safe frame
// number has been reached.
m_manager->onRenderingResourceReleased(const_cast<GPUResource*>(this));
}
GPUResourceManager::~GPUResourceManager()
{
// Call shutdown() before destroying the resource manager.
assert(m_currentFrameNumber == SHUTDOWN_FRAME_NUMBER);
assert(m_safeFrameNumber == SHUTDOWN_FRAME_NUMBER);
assert(m_resourcePurgatory.empty());
}
void GPUResourceManager::advanceFrameNumber(uint64_t nextFrameNumber,
uint64_t safeFrameNumber)
{
assert(nextFrameNumber >= m_currentFrameNumber);
assert(safeFrameNumber >= m_safeFrameNumber);
assert(safeFrameNumber <= nextFrameNumber);
m_currentFrameNumber = nextFrameNumber;
m_safeFrameNumber = safeFrameNumber;
// Delete all resources whose safe frame number has been reached.
while (!m_resourcePurgatory.empty() &&
m_resourcePurgatory.front().lastFrameNumber <= m_safeFrameNumber)
{
assert(m_resourcePurgatory.front().resource->debugging_refcnt() == 0);
m_resourcePurgatory.pop_front();
}
}
void GPUResourceManager::onRenderingResourceReleased(GPUResource* resource)
{
assert(resource->manager() == this);
if (m_currentFrameNumber > m_safeFrameNumber)
{
// Hold this resource until its safe frame number is reached.
assert(resource->debugging_refcnt() == 0);
assert(m_resourcePurgatory.empty() ||
m_currentFrameNumber >=
m_resourcePurgatory.back().lastFrameNumber);
m_resourcePurgatory.emplace_back(resource, m_currentFrameNumber);
}
else
{
// We're in a shutdown cycle. Delete immediately.
delete resource;
}
}
void GPUResourceManager::shutdown()
{
advanceFrameNumber(SHUTDOWN_FRAME_NUMBER, SHUTDOWN_FRAME_NUMBER);
}
rcp<GPUResource> GPUResourcePool::acquire()
{
rcp<GPUResource> resource;
if (!m_pool.empty() &&
m_pool.front().lastFrameNumber <= m_manager->safeFrameNumber())
{
// Recycle the oldest buffer in the pool.
resource = rcp(m_pool.front().resource.release());
m_pool.pop_front();
// Trim the pool in case it's grown out of control (meaning it was
// advanced multiple times in a single frame).
while (m_pool.size() > m_maxPoolCount &&
m_pool.front().lastFrameNumber <= m_manager->safeFrameNumber())
{
m_pool.pop_front();
}
}
assert(resource == nullptr || resource->debugging_refcnt() == 1);
return resource;
}
void GPUResourcePool::recycle(rcp<GPUResource> resource)
{
if (resource != nullptr)
{
// Return the current buffer to the pool.
assert(resource->debugging_refcnt() == 1);
assert(m_pool.empty() || m_manager->currentFrameNumber() >=
m_pool.back().lastFrameNumber);
m_pool.emplace_back(resource.release(),
m_manager->currentFrameNumber());
}
}
} // namespace rive::gpu