| /* | |
| * 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 |