blob: 5fc3c651ae90a11938e7adca74f319296706013d [file] [log] [blame]
/*
* Copyright 2025 Rive
*/
#pragma once
#include "rive/refcnt.hpp"
#include <deque>
namespace rive::gpu
{
class GPUResourceManager;
// Base class for a GPU resource that needs to be kept alive until any in-flight
// command buffers that reference it have completed.
class GPUResource : public RefCnt<GPUResource>
{
public:
virtual ~GPUResource();
// GPUResource classes should default to non-copyable (they're typically
// going to contain handles that would get double-freed if copied)
GPUResource(const GPUResource&) = delete;
GPUResource& operator=(const GPUResource&) = delete;
GPUResourceManager* manager() const { return m_manager.get(); }
protected:
GPUResource(rcp<GPUResourceManager> manager) : m_manager(std::move(manager))
{}
const rcp<GPUResourceManager> m_manager;
private:
friend class RefCnt<GPUResource>;
// Don't delete GPUResources immediately when their ref count reaches zero;
// instead wait until their safe frame number is reached.
void onRefCntReachedZero() const;
};
// A GPUResource that has been fully released, but whose underlying native
// resource may still be referenced by an in-flight command buffer.
struct ZombieResource
{
ZombieResource(GPUResource* resource_, uint64_t lastFrameNumber_) :
resource(resource_), lastFrameNumber(lastFrameNumber_)
{}
std::unique_ptr<GPUResource> resource;
const uint64_t lastFrameNumber; // Frame number at which the underlying
// native resource was last used.
};
// Manages the lifecycle of GPUResources. When the refcount reaches zero, rather
// than deleting it immediately, this class places it in "purgatory" until its
// safe frame number is reached.
class GPUResourceManager : public RefCnt<GPUResourceManager>
{
public:
virtual ~GPUResourceManager();
// Resource lifetime counters. Resources last used on or before
// 'safeFrameNumber' are safe to be released or recycled.
uint64_t currentFrameNumber() const { return m_currentFrameNumber; }
uint64_t safeFrameNumber() const { return m_safeFrameNumber; }
// Purges released resources whose lastFrameNumber is on or before
// safeFrameNumber, and updates the monotonically increasing
// m_currentFrameNumber.
void advanceFrameNumber(uint64_t nextFrameNumber, uint64_t safeFrameNumber);
// Called when a GPUResource has been fully released (refcount reaches 0).
// The underlying native resource won't actually be deleted until the
// resource's safe frame number is reached.
void onRenderingResourceReleased(GPUResource*);
// Called prior to the client beginning its shutdown cycle, and after all
// command buffers from all frames have finished executing. After shutting
// down, we delete GPUResources immediately instead of going
// through m_resourcePurgatory.
void shutdown();
private:
// An m_currentFrameNumber of this value indicates we're in a shutdown cycle
// and resources should be deleted immediately upon release instead of going
// through m_resourcePurgatory.
constexpr static uint64_t SHUTDOWN_FRAME_NUMBER = 0xffffffffffffffff;
// Resource lifetime counters. Resources last used on or before
// 'safeFrameNumber' are safe to be released or recycled.
uint64_t m_currentFrameNumber = 0;
uint64_t m_safeFrameNumber = 0;
// Temporary container for GPUResource instances that have been fully
// released, but need to persist until their safe frame number is reached.
std::deque<ZombieResource> m_resourcePurgatory;
};
// Manual GPUResource lifecycle manager. Rather than allowing GPUResourceManager
// to vaccuum up resources, they may be placed in a pool instead, which will
// recycle them once their safeFrameNumber is reached.
class GPUResourcePool : public GPUResource
{
public:
GPUResourcePool(rcp<GPUResourceManager> manager, size_t maxPoolSize) :
GPUResource(std::move(manager)), m_maxPoolCount(maxPoolSize)
{}
// Returns a previously-recycled resource whose safe frame number has been
// reached, or null of no such resource exists.
rcp<GPUResource> acquire();
// Places the given resouce back in the pool, where it waits until its safe
// frame number is reached. Refcount must be 1.
void recycle(rcp<GPUResource>);
private:
const size_t m_maxPoolCount;
std::deque<ZombieResource> m_pool;
};
} // namespace rive::gpu