| /* | |
| * 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. With no manager (the Metal Ore backend keeps | |
| // native objects alive via ARC and command-buffer retention rather than a | |
| // manager) there is no deferred reclaim, so delete now instead of leaking. | |
| if (m_manager) | |
| m_manager->onRenderingResourceReleased(const_cast<GPUResource*>(this)); | |
| else | |
| delete 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; | |
| m_didAdvanceFrameNumber = true; | |
| // 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); | |
| // Hold the resource in purgatory (rather than freeing it now) only if GPU | |
| // work that might reference it could still be in flight: | |
| // * during normal rendering, while frames are unfinished | |
| // (m_currentFrameNumber > m_safeFrameNumber); or | |
| // * before the first frame (!m_didAdvanceFrameNumber), where a one-shot | |
| // upload may have submitted work referencing a just-released staging | |
| // buffer while m_currentFrameNumber == m_safeFrameNumber == 0. | |
| // Otherwise -- the GPU has caught up to the current frame during rendering, | |
| // or we're in a shutdown cycle (which implies m_didAdvanceFrameNumber) -- | |
| // delete immediately. | |
| if (m_currentFrameNumber > m_safeFrameNumber || !m_didAdvanceFrameNumber) | |
| { | |
| // 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 | |
| { | |
| 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 |