| /* |
| * Copyright 2022 Rive |
| */ |
| |
| #pragma once |
| |
| #include "rive/renderer/gpu.hpp" |
| |
| namespace rive::gpu |
| { |
| // 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 RenderContext |
| // 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; } |
| size_t mapSizeInBytes() const { return m_mapSizeInBytes; } |
| |
| // 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 RenderContext 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::gpu |