blob: 1238b6a8016e1998a4fcd40a01b81d126078d1fd [file] [log] [blame] [edit]
/*
* Copyright 2022 Rive
*/
#pragma once
#include "rive/pls/pls.hpp"
namespace rive::pls
{
// API-agnostic implementation of an abstract buffer ring. We use rings to ensure the GPU can render
// one frame in parallel while the CPU prepares the next frame.
//
// Calling mapBuffer() maps the next buffer in the ring.
//
// Calling unmapAndSubmitBuffer() submits the currently-mapped buffer for GPU rendering, in whatever
// way that is meaningful for the PLSRenderContext implementation.
//
// This class is meant to only be used through BufferRing<>.
class BufferRing
{
public:
BufferRing(size_t capacityInBytes) : m_capacityInBytes(capacityInBytes) {}
virtual ~BufferRing() {}
size_t capacityInBytes() const { return m_capacityInBytes; }
bool isMapped() const { return m_mapSizeInBytes != 0; }
// Maps the next buffer in the ring.
void* mapBuffer(size_t mapSizeInBytes)
{
assert(!isMapped());
assert(mapSizeInBytes > 0);
assert(mapSizeInBytes <= m_capacityInBytes);
m_submittedBufferIdx = (m_submittedBufferIdx + 1) % kBufferRingSize;
m_mapSizeInBytes = mapSizeInBytes;
return onMapBuffer(m_submittedBufferIdx, m_mapSizeInBytes);
}
// Submits the currently-mapped buffer for GPU rendering, in whatever way that is meaningful for
// the PLSRenderContext implementation.
void unmapAndSubmitBuffer()
{
assert(isMapped());
onUnmapAndSubmitBuffer(m_submittedBufferIdx, m_mapSizeInBytes);
m_mapSizeInBytes = 0;
}
protected:
int submittedBufferIdx() const
{
assert(!isMapped());
return m_submittedBufferIdx;
}
virtual void* onMapBuffer(int bufferIdx, size_t mapSizeInBytes) = 0;
virtual void onUnmapAndSubmitBuffer(int bufferIdx, size_t mapSizeInBytes) = 0;
uint8_t* shadowBuffer() const
{
if (m_shadowBuffer == nullptr && m_capacityInBytes > 0)
{
m_shadowBuffer.reset(new uint8_t[m_capacityInBytes]);
}
return m_shadowBuffer.get();
}
private:
size_t m_capacityInBytes;
size_t m_mapSizeInBytes = 0;
int m_submittedBufferIdx = 0;
// Lazy-allocated CPU buffer for when buffer mapping isn't supported by the API.
mutable std::unique_ptr<uint8_t[]> m_shadowBuffer;
};
// BufferRing that resides solely in CPU memory, and therefore doesn't require a ring.
class HeapBufferRing : public BufferRing
{
public:
HeapBufferRing(size_t capacityInBytes) : BufferRing(capacityInBytes) {}
uint8_t* contents() const { return shadowBuffer(); }
protected:
void* onMapBuffer(int bufferIdx, size_t mapSizeInBytes) override { return shadowBuffer(); }
void onUnmapAndSubmitBuffer(int bufferIdx, size_t mapSizeInBytes) override {}
};
} // namespace rive::pls