Virtual PLSRenderContextImpl
diff --git a/include/rive/pls/buffer_ring.hpp b/include/rive/pls/buffer_ring.hpp index e099a2e..602cd90 100644 --- a/include/rive/pls/buffer_ring.hpp +++ b/include/rive/pls/buffer_ring.hpp
@@ -27,6 +27,7 @@ size_t capacity() const { return m_capacity; } size_t itemSizeInBytes() const { return m_itemSizeInBytes; } + size_t totalSizeInBytes() const { return m_capacity * m_itemSizeInBytes * kBufferRingSize; } // Maps the next buffer in the ring. void* mapBuffer() @@ -168,117 +169,4 @@ private: std::unique_ptr<char[]> m_cpuBuffers[kBufferRingSize]; }; - -// Wrapper for an abstract BufferRingImpl that supports mapping buffers, writing an array of items -// of the same type, and submitting for rendering. -// -// Intended usage pattern: -// -// * Call ensureMapped() to map the next buffer in the ring. -// * push() all items for rendering. -// * Call submit() to unmap and submit the currently-mapped buffer for rendering, in whatever way -// that is meaningful for the PLSRenderContext implementation. -// -template <typename T> class BufferRing -{ -public: - BufferRing() = default; - BufferRing(std::unique_ptr<BufferRingImpl> impl) { reset(std::move(impl)); } - BufferRing(BufferRing&& other) : m_impl(std::move(other.m_impl)) {} - - void reset(std::unique_ptr<BufferRingImpl> impl) - { - assert(!mapped()); - assert(impl->itemSizeInBytes() == sizeof(T)); - m_impl = std::move(impl); - } - - size_t totalSizeInBytes() const - { - return m_impl ? kBufferRingSize * m_impl->capacity() * m_impl->itemSizeInBytes() : 0; - } - - size_t capacity() const { return m_impl->capacity(); } - - // Maps the next buffer in the ring, if one is not already mapped. - RIVE_ALWAYS_INLINE void ensureMapped() - { - if (!mapped()) - { - m_mappedMemory = m_nextMappedItem = reinterpret_cast<T*>(m_impl->mapBuffer()); - m_mappingEnd = m_mappedMemory + m_impl->capacity(); - } - } - - const BufferRingImpl* impl() const { return m_impl.get(); } - BufferRingImpl* impl() { return m_impl.get(); } - - // Is a buffer not mapped, or, has nothing been pushed yet to the currently-mapped buffer? - size_t empty() const - { - assert(!m_mappedMemory == !m_nextMappedItem); - return m_mappedMemory == m_nextMappedItem; - } - - // How many bytes have been written to the currently-mapped buffer? - // (Returns 0 if no buffer is mapped.) - size_t bytesWritten() const - { - assert(!m_mappedMemory == !m_mappingEnd); - return reinterpret_cast<uintptr_t>(m_nextMappedItem) - - reinterpret_cast<uintptr_t>(m_mappedMemory); - } - - // Is a buffer currently mapped? - bool mapped() const - { - assert(!m_mappedMemory == !m_nextMappedItem && !m_mappedMemory == !m_mappingEnd); - return m_mappedMemory != nullptr; - } - - // Is there room to push() itemCount items to the currently-mapped buffer? - bool hasRoomFor(size_t itemCount) - { - assert(mapped()); - return m_nextMappedItem + itemCount <= m_mappingEnd; - } - - // Append and write a new item to the currently-mapped buffer. In order to enforce the - // write-only requirement of a mapped buffer, this method does not return any pointers to the - // client. - template <typename... Args> RIVE_ALWAYS_INLINE void emplace_back(Args&&... args) - { - push() = {std::forward<Args>(args)...}; - } - template <typename... Args> RIVE_ALWAYS_INLINE void set_back(Args&&... args) - { - push().set(std::forward<Args>(args)...); - } - - // Called after all the data for a frame has been push()-ed to the mapped buffer. Unmaps and - // submits the currently-mapped buffer (if any) for GPU rendering, in whatever way that is - // meaningful for the PLSRenderContext implementation. - void submit() - { - if (mapped()) - { - m_impl->unmapAndSubmitBuffer(bytesWritten()); - m_mappingEnd = m_nextMappedItem = m_mappedMemory = nullptr; - } - assert(!mapped()); - } - -private: - template <typename... Args> RIVE_ALWAYS_INLINE T& push() - { - assert(mapped()); - assert(hasRoomFor(1)); - return *m_nextMappedItem++; - } - - std::unique_ptr<BufferRingImpl> m_impl; - T* m_mappedMemory = nullptr; - T* m_nextMappedItem = nullptr; - const T* m_mappingEnd = nullptr; -}; } // namespace rive::pls
diff --git a/include/rive/pls/d3d/pls_render_context_d3d.hpp b/include/rive/pls/d3d/pls_render_context_d3d_impl.hpp similarity index 91% rename from include/rive/pls/d3d/pls_render_context_d3d.hpp rename to include/rive/pls/d3d/pls_render_context_d3d_impl.hpp index b0c7b1c..ba0f0bf 100644 --- a/include/rive/pls/d3d/pls_render_context_d3d.hpp +++ b/include/rive/pls/d3d/pls_render_context_d3d_impl.hpp
@@ -5,7 +5,7 @@ #pragma once #include "rive/pls/d3d/d3d11.hpp" -#include "rive/pls/pls_render_context.hpp" +#include "rive/pls/pls_render_context_buffer_ring_impl.hpp" #include <map> namespace rive::pls @@ -22,7 +22,7 @@ ID3D11Texture2D* targetTexture() const { return m_targetTexture.Get(); } private: - friend class PLSRenderContextD3D; + friend class PLSRenderContextD3DImpl; PLSRenderTargetD3D(ID3D11Device*, size_t width, size_t height); @@ -38,10 +38,10 @@ }; // D3D backend implementation of PLSRenderContext. -class PLSRenderContextD3D : public PLSRenderContext +class PLSRenderContextD3DImpl : public PLSRenderContextBufferRingImpl { public: - PLSRenderContextD3D(ComPtr<ID3D11Device>, ComPtr<ID3D11DeviceContext>, bool isIntel); + PLSRenderContextD3DImpl(ComPtr<ID3D11Device>, ComPtr<ID3D11DeviceContext>, bool isIntel); rcp<PLSRenderTargetD3D> makeRenderTarget(size_t width, size_t height); @@ -64,7 +64,7 @@ void allocateGradientTexture(size_t height) override; void allocateTessellationTexture(size_t height) override; - void onFlush(const FlushDescriptor&) override; + void flush(const PLSRenderContext::FlushDescriptor&) override; void setPipelineLayoutAndShaders(DrawType, const ShaderFeatures&);
diff --git a/include/rive/pls/gl/pls_render_context_gl.hpp b/include/rive/pls/gl/pls_render_context_gl_impl.hpp similarity index 86% rename from include/rive/pls/gl/pls_render_context_gl.hpp rename to include/rive/pls/gl/pls_render_context_gl_impl.hpp index 4a37f2b..7fb8294 100644 --- a/include/rive/pls/gl/pls_render_context_gl.hpp +++ b/include/rive/pls/gl/pls_render_context_gl_impl.hpp
@@ -9,7 +9,7 @@ #include "rive/pls/gl/gles3.hpp" #include "rive/pls/gl/pls_render_target_gl.hpp" #include "rive/pls/buffer_ring.hpp" -#include "rive/pls/pls_render_context.hpp" +#include "rive/pls/pls_render_context_buffer_ring_impl.hpp" #include <map> namespace rive::pls @@ -19,17 +19,17 @@ class PLSRenderTargetGL; // OpenGL backend implementation of PLSRenderContext. -class PLSRenderContextGL : public PLSRenderContext +class PLSRenderContextGLImpl : public PLSRenderContextBufferRingImpl { public: - static std::unique_ptr<PLSRenderContextGL> Make(); - ~PLSRenderContextGL() override; + static std::unique_ptr<PLSRenderContextGLImpl> Make(); + ~PLSRenderContextGLImpl() override; // Creates a PLSRenderTarget that draws directly into the given GL framebuffer. // Returns null if the framebuffer doesn't support pixel local storage. rcp<PLSRenderTargetGL> wrapGLRenderTarget(GLuint framebufferID, size_t width, size_t height) { - return m_plsImpl->wrapGLRenderTarget(framebufferID, width, height, m_platformFeatures); + return m_plsImpl->wrapGLRenderTarget(framebufferID, width, height, platformFeatures()); } // Creates a PLSRenderTarget that draws to a new, offscreen GL framebuffer. This method is @@ -37,7 +37,7 @@ // results. rcp<PLSRenderTargetGL> makeOffscreenRenderTarget(size_t width, size_t height) { - return m_plsImpl->makeOffscreenRenderTarget(width, height, m_platformFeatures); + return m_plsImpl->makeOffscreenRenderTarget(width, height, platformFeatures()); } private: @@ -55,11 +55,9 @@ size_t height, const PlatformFeatures&) = 0; - virtual void activatePixelLocalStorage(PLSRenderContextGL*, - const PLSRenderTargetGL*, - LoadAction, - bool needsClipBuffer) = 0; - virtual void deactivatePixelLocalStorage(PLSRenderContextGL*) = 0; + virtual void activatePixelLocalStorage(PLSRenderContextGLImpl*, + const PLSRenderContext::FlushDescriptor&) = 0; + virtual void deactivatePixelLocalStorage(PLSRenderContextGLImpl*) = 0; virtual const char* shaderDefineName() const = 0; @@ -105,7 +103,7 @@ public: DrawProgram(const DrawProgram&) = delete; DrawProgram& operator=(const DrawProgram&) = delete; - DrawProgram(PLSRenderContextGL*, DrawType, const ShaderFeatures&); + DrawProgram(PLSRenderContextGLImpl*, DrawType, const ShaderFeatures&); ~DrawProgram(); GLuint id() const { return m_id; } @@ -118,12 +116,7 @@ class DrawShader; - PLSRenderContextGL(const PlatformFeatures&, GLExtensions, std::unique_ptr<PLSImpl>); - - const PLSRenderTargetGL* renderTarget() const - { - return static_cast<const PLSRenderTargetGL*>(frameDescriptor().renderTarget.get()); - } + PLSRenderContextGLImpl(const PlatformFeatures&, GLExtensions, std::unique_ptr<PLSImpl>); std::unique_ptr<BufferRingImpl> makeVertexBufferRing(size_t capacity, size_t itemSizeInBytes) override; @@ -143,7 +136,7 @@ void allocateGradientTexture(size_t height) override; void allocateTessellationTexture(size_t height) override; - void onFlush(const FlushDescriptor&) override; + void flush(const PLSRenderContext::FlushDescriptor&) override; // GL state wrapping. void bindProgram(GLuint);
diff --git a/include/rive/pls/gl/pls_render_target_gl.hpp b/include/rive/pls/gl/pls_render_target_gl.hpp index 17346b6..dc6664d 100644 --- a/include/rive/pls/gl/pls_render_target_gl.hpp +++ b/include/rive/pls/gl/pls_render_target_gl.hpp
@@ -24,7 +24,7 @@ GLuint sideFramebufferID() const { return m_sideFramebufferID; } private: - friend class PLSRenderContextGL; + friend class PLSRenderContextGLImpl; // Creates a render target that draws to an existing GL framebuffer. The caller must also call // allocateCoverageBackingTextures() and attach those textures to the framebuffer if needed.
diff --git a/include/rive/pls/pls.hpp b/include/rive/pls/pls.hpp index 30c6ee1..863f88e 100644 --- a/include/rive/pls/pls.hpp +++ b/include/rive/pls/pls.hpp
@@ -12,6 +12,11 @@ #include "rive/shapes/paint/color.hpp" #include "rive/shapes/paint/stroke_join.hpp" +namespace rive +{ +class GrInnerFanTriangulator; +} + // This header defines constants and data structures for Rive's pixel local storage path rendering // algorithm. // @@ -62,6 +67,7 @@ // In order to support WebGL2, we implement the path data buffer as a texture. constexpr static size_t kPathTextureWidthInItems = 128; constexpr static size_t kPathTexelsPerItem = 3; +constexpr static size_t kPathTextureWidthInTexels = kPathTextureWidthInItems * kPathTexelsPerItem; // Each contour has its own unique ID, which it uses to index a data record containing per-contour // information. This value is currently 16 bit. @@ -72,6 +78,8 @@ // In order to support WebGL2, we implement the contour data buffer as a texture. constexpr static size_t kContourTextureWidthInItems = 256; constexpr static size_t kContourTexelsPerItem = 1; +constexpr static size_t kContourTextureWidthInTexels = + kContourTextureWidthInItems * kContourTexelsPerItem; // Tessellation is performed by rendering vertices into a data texture. These values define the // dimensions of the tessellation data texture. @@ -389,6 +397,56 @@ }; static_assert(sizeof(TriangleVertex) == sizeof(float) * 3); +template <typename T> class WriteOnlyMappedMemory +{ +public: + WriteOnlyMappedMemory() { reset(); } + WriteOnlyMappedMemory(void* ptr, size_t count) { reset(ptr, count); } + + void reset() { reset(nullptr, 0); } + + void reset(void* ptr, size_t count) + { + m_mappedMemory = reinterpret_cast<T*>(ptr); + m_nextMappedItem = m_mappedMemory; + m_mappingEnd = m_mappedMemory + count; + } + + operator bool() const { return m_mappedMemory; } + + // How many bytes have been written to the buffer? + size_t bytesWritten() const + { + return reinterpret_cast<uintptr_t>(m_nextMappedItem) - + reinterpret_cast<uintptr_t>(m_mappedMemory); + } + + // Is there room to push() itemCount items to the buffer? + bool hasRoomFor(size_t itemCount) { return m_nextMappedItem + itemCount <= m_mappingEnd; } + + // Append and write a new item to the buffer. In order to enforce the write-only requirement of + // a mapped buffer, these methods do not return any pointers to the client. + template <typename... Args> RIVE_ALWAYS_INLINE void emplace_back(Args&&... args) + { + push() = {std::forward<Args>(args)...}; + } + template <typename... Args> RIVE_ALWAYS_INLINE void set_back(Args&&... args) + { + push().set(std::forward<Args>(args)...); + } + +private: + template <typename... Args> RIVE_ALWAYS_INLINE T& push() + { + assert(hasRoomFor(1)); + return *m_nextMappedItem++; + } + + T* m_mappedMemory; + T* m_nextMappedItem; + const T* m_mappingEnd; +}; + // Once all curves in a contour have been tessellated, we render the tessellated vertices in // "patches" (aka specific instanced geometry). // @@ -482,6 +540,135 @@ void GeneratePatchBufferData(PatchVertex[kPatchVertexBufferCount], uint16_t indices[kPatchIndexBufferCount]); +enum class DrawType : uint8_t +{ + midpointFanPatches, // Standard paths and/or strokes. + outerCurvePatches, // Just the outer curves of a path; the interior will be triangulated. + interiorTriangulation +}; + +constexpr static uint32_t PatchSegmentSpan(DrawType drawType) +{ + switch (drawType) + { + case DrawType::midpointFanPatches: + return kMidpointFanPatchSegmentSpan; + case DrawType::outerCurvePatches: + return kOuterCurvePatchSegmentSpan; + default: + RIVE_UNREACHABLE(); + } +} + +constexpr static uint32_t PatchIndexCount(DrawType drawType) +{ + switch (drawType) + { + case DrawType::midpointFanPatches: + return kMidpointFanPatchIndexCount; + case DrawType::outerCurvePatches: + return kOuterCurvePatchIndexCount; + default: + RIVE_UNREACHABLE(); + } +} + +constexpr static uintptr_t PatchBaseIndex(DrawType drawType) +{ + switch (drawType) + { + case DrawType::midpointFanPatches: + return kMidpointFanPatchBaseIndex; + case DrawType::outerCurvePatches: + return kOuterCurvePatchBaseIndex; + default: + RIVE_UNREACHABLE(); + } +} + +// Specifies what to do with the render target at the beginning of a flush. +enum class LoadAction : bool +{ + clear, + preserveRenderTarget +}; + +// Indicates how much blendMode support will be needed in the "uber" draw shader. +enum class BlendTier : uint8_t +{ + srcOver, // Every draw uses srcOver. + advanced, // Draws use srcOver *and* advanced blend modes, excluding HSL modes. + advancedHSL, // Draws use srcOver *and* advanced blend modes *and* advanced HSL modes. +}; + +// Used by ShaderFeatures to generate keys and source code. +enum class SourceType : bool +{ + vertexOnly, + wholeProgram +}; + +// Indicates which "uber shader" features to enable in the draw shader. +struct ShaderFeatures +{ + enum PreprocessorDefines : uint32_t + { + ENABLE_ADVANCED_BLEND = 1 << 0, + ENABLE_PATH_CLIPPING = 1 << 1, + ENABLE_EVEN_ODD = 1 << 2, + ENABLE_HSL_BLEND_MODES = 1 << 3, + }; + + // Returns a bitmask of which preprocessor macros must be defined in order to support the + // current feature set. + uint32_t getPreprocessorDefines(SourceType) const; + + struct + { + BlendTier blendTier = BlendTier::srcOver; + bool enablePathClipping = false; + } programFeatures; + + struct + { + bool enableEvenOdd = false; + } fragmentFeatures; +}; + +inline static uint32_t ShaderUniqueKey(SourceType sourceType, + DrawType drawType, + const ShaderFeatures& shaderFeatures) +{ + return (shaderFeatures.getPreprocessorDefines(sourceType) << 1) | + (drawType == DrawType::interiorTriangulation); +} + +// Linked list of draws to be issued by the subclass during onFlush(). +struct Draw +{ + Draw(DrawType drawType_, uint32_t baseVertexOrInstance_) : + drawType(drawType_), baseVertexOrInstance(baseVertexOrInstance_) + {} + const DrawType drawType; + uint32_t baseVertexOrInstance; + uint32_t vertexOrInstanceCount = 0; // Calculated during PLSRenderContext::flush(). + ShaderFeatures shaderFeatures; + GrInnerFanTriangulator* triangulator = nullptr; // Used by "interiorTriangulation" draws. +}; + +// Simple gradients only have 2 texels, so we write them to mapped texture memory from the CPU +// instead of rendering them. +struct TwoTexelRamp +{ + void set(const ColorInt colors[2]) + { + UnpackColorToRGBA8(colors[0], colorData); + UnpackColorToRGBA8(colors[1], colorData + 4); + } + uint8_t colorData[8]; +}; +static_assert(sizeof(TwoTexelRamp) == 8 * sizeof(uint8_t)); + // Returns the smallest number that can be added to 'value', such that 'value % alignment' == 0. template <uint32_t Alignment> RIVE_ALWAYS_INLINE uint32_t PaddingToAlignUp(uint32_t value) {
diff --git a/include/rive/pls/pls_render_context.hpp b/include/rive/pls/pls_render_context.hpp index 065b4cd..7c0e174 100644 --- a/include/rive/pls/pls_render_context.hpp +++ b/include/rive/pls/pls_render_context.hpp
@@ -16,7 +16,6 @@ namespace rive { -class GrInnerFanTriangulator; class RawPath; } // namespace rive @@ -26,6 +25,7 @@ class PLSGradient; class PLSPaint; class PLSPath; +class PLSRenderContextImpl; // Used as a key for complex gradients. class GradientContentKey @@ -86,14 +86,11 @@ class PLSRenderContext { public: - virtual ~PLSRenderContext(); + PLSRenderContext(std::unique_ptr<PLSRenderContextImpl>); + ~PLSRenderContext(); - // Specifies what to do with the render target at the beginning of a flush. - enum class LoadAction : bool - { - clear, - preserveRenderTarget - }; + PLSRenderContextImpl* impl() const { return m_impl.get(); } + const PlatformFeatures& platformFeatures() const { return m_platformFeatures; } // Options for controlling how and where a frame is rendered. struct FrameDescriptor @@ -293,11 +290,11 @@ template <typename T> class PerFlushLinkedList { public: - void reset() { m_tail = m_head = nullptr; } - - bool empty() const; + size_t count() const; + bool empty() const { return count() == 0; } T& tail() const; template <typename... Args> void emplace_back(PLSRenderContext* context, Args... args); + void reset(); struct Node { @@ -306,111 +303,35 @@ Node* next = nullptr; }; - class Iter + template <typename U> class Iter { public: Iter(Node* current) : m_current(current) {} bool operator!=(const Iter& other) const { return m_current != other.m_current; } void operator++() { m_current = m_current->next; } - T& operator*() { return m_current->data; } + U& operator*() { return m_current->data; } private: Node* m_current; }; - Iter begin() { return {m_head}; } - Iter end() { return {nullptr}; } + Iter<T> begin() { return {m_head}; } + Iter<T> end() { return {nullptr}; } + Iter<const T> begin() const { return {m_head}; } + Iter<const T> end() const { return {nullptr}; } private: Node* m_head = nullptr; Node* m_tail = nullptr; + size_t m_count = 0; }; -protected: - PLSRenderContext(const PlatformFeatures&); - - virtual std::unique_ptr<BufferRingImpl> makeVertexBufferRing(size_t capacity, - size_t itemSizeInBytes) = 0; - - virtual std::unique_ptr<TexelBufferRing> makeTexelBufferRing(TexelBufferRing::Format, - size_t widthInItems, - size_t height, - size_t texelsPerItem, - int textureIdx, - TexelBufferRing::Filter) = 0; - - virtual std::unique_ptr<BufferRingImpl> makePixelUnpackBufferRing(size_t capacity, - size_t itemSizeInBytes) = 0; - - virtual std::unique_ptr<BufferRingImpl> makeUniformBufferRing(size_t sizeInBytes) = 0; - - virtual void allocateGradientTexture(size_t height) = 0; - virtual void allocateTessellationTexture(size_t height) = 0; - - const TexelBufferRing* pathBufferRing() - { - return static_cast<const TexelBufferRing*>(m_pathBuffer.impl()); - } - const TexelBufferRing* contourBufferRing() - { - return static_cast<const TexelBufferRing*>(m_contourBuffer.impl()); - } - const BufferRingImpl* simpleColorRampsBufferRing() const - { - return m_simpleColorRampsBuffer.impl(); - } - const BufferRingImpl* gradSpanBufferRing() const { return m_gradSpanBuffer.impl(); } - const BufferRingImpl* tessSpanBufferRing() { return m_tessSpanBuffer.impl(); } - const BufferRingImpl* triangleBufferRing() { return m_triangleBuffer.impl(); } - const BufferRingImpl* uniformBufferRing() const { return m_uniformBuffer.impl(); } - - virtual void onBeginFrame() {} - - // Indicates how much blendMode support will be needed in the "uber" draw shader. - enum class BlendTier : uint8_t - { - srcOver, // Every draw uses srcOver. - advanced, // Draws use srcOver *and* advanced blend modes, excluding HSL modes. - advancedHSL, // Draws use srcOver *and* advanced blend modes *and* advanced HSL modes. - }; - - // Used by ShaderFeatures to generate keys and source code. - enum class SourceType : bool - { - vertexOnly, - wholeProgram - }; - - // Indicates which "uber shader" features to enable in the draw shader. - struct ShaderFeatures - { - enum PreprocessorDefines : uint32_t - { - ENABLE_ADVANCED_BLEND = 1 << 0, - ENABLE_PATH_CLIPPING = 1 << 1, - ENABLE_EVEN_ODD = 1 << 2, - ENABLE_HSL_BLEND_MODES = 1 << 3, - }; - - // Returns a bitmask of which preprocessor macros must be defined in order to support the - // current feature set. - uint32_t getPreprocessorDefines(SourceType) const; - - struct - { - BlendTier blendTier = BlendTier::srcOver; - bool enablePathClipping = false; - } programFeatures; - - struct - { - bool enableEvenOdd = false; - } fragmentFeatures; - }; - + // protected: +public: struct FlushDescriptor { - FlushType flushType; + const PLSRenderTarget* renderTarget; LoadAction loadAction; + ColorInt clearColor = 0; size_t complexGradSpanCount; size_t tessVertexSpanCount; uint16_t simpleGradTexelsWidth; @@ -419,89 +340,23 @@ uint32_t complexGradRowsHeight; uint32_t tessDataHeight; bool needsClipBuffer; + bool hasTriangleVertices; + bool wireframe; + const PerFlushLinkedList<Draw>* drawList; }; - virtual void onFlush(const FlushDescriptor&) = 0; - +private: const PlatformFeatures m_platformFeatures; const size_t m_maxPathID; - - enum class DrawType : uint8_t - { - midpointFanPatches, // Standard paths and/or strokes. - outerCurvePatches, // Just the outer curves of a path; the interior will be triangulated. - interiorTriangulation - }; - - constexpr static uint32_t PatchSegmentSpan(DrawType drawType) - { - switch (drawType) - { - case DrawType::midpointFanPatches: - return kMidpointFanPatchSegmentSpan; - case DrawType::outerCurvePatches: - return kOuterCurvePatchSegmentSpan; - default: - RIVE_UNREACHABLE(); - } - } - - constexpr static uint32_t PatchIndexCount(DrawType drawType) - { - switch (drawType) - { - case DrawType::midpointFanPatches: - return kMidpointFanPatchIndexCount; - case DrawType::outerCurvePatches: - return kOuterCurvePatchIndexCount; - default: - RIVE_UNREACHABLE(); - } - } - - constexpr static uintptr_t PatchBaseIndex(DrawType drawType) - { - switch (drawType) - { - case DrawType::midpointFanPatches: - return kMidpointFanPatchBaseIndex; - case DrawType::outerCurvePatches: - return kOuterCurvePatchBaseIndex; - default: - RIVE_UNREACHABLE(); - } - } - - static uint32_t ShaderUniqueKey(SourceType sourceType, - DrawType drawType, - const ShaderFeatures& shaderFeatures) - { - return (shaderFeatures.getPreprocessorDefines(sourceType) << 1) | - (drawType == DrawType::interiorTriangulation); - } - - // Linked list of draws to be issued by the subclass during onFlush(). - struct Draw - { - Draw(DrawType drawType_, uint32_t baseVertexOrInstance_) : - drawType(drawType_), baseVertexOrInstance(baseVertexOrInstance_) - {} - const DrawType drawType; - uint32_t baseVertexOrInstance; - uint32_t vertexOrInstanceCount = 0; // Calculated during PLSRenderContext::flush(). - ShaderFeatures shaderFeatures; - GrInnerFanTriangulator* triangulator = nullptr; // Used by "interiorTriangulation" draws. - }; + const std::unique_ptr<PLSRenderContextImpl> m_impl; PerFlushLinkedList<Draw> m_drawList; - size_t m_drawListCount = 0; // GrTriangulator provides an upper bound on the number of vertices it will emit. Triangulations // are not writen out until the last minute, during flush(), and this variable provides an upper // bound on the number of vertices that will be written. size_t m_maxTriangleVertexCount = 0; -private: static BlendTier BlendTierForBlendMode(PLSBlendMode); // Allocates a horizontal span of texels in the gradient texture and schedules either a texture @@ -551,7 +406,7 @@ // Resources allocated at flush time (after we already know exactly how big they need to // be). - size_t triangleVertexBufferSize; + size_t triangleVertexBufferCount; size_t gradientTextureHeight; size_t tessellationTextureHeight; @@ -564,8 +419,8 @@ maxComplexGradientSpans = std::max(maxComplexGradientSpans, other.maxComplexGradientSpans); maxTessellationSpans = std::max(maxTessellationSpans, other.maxTessellationSpans); - triangleVertexBufferSize = - std::max(triangleVertexBufferSize, other.triangleVertexBufferSize); + triangleVertexBufferCount = + std::max(triangleVertexBufferCount, other.triangleVertexBufferCount); gradientTextureHeight = std::max(gradientTextureHeight, other.gradientTextureHeight); tessellationTextureHeight = std::max(tessellationTextureHeight, other.tessellationTextureHeight); @@ -589,9 +444,9 @@ if (maxTessellationSpans > threshold.maxTessellationSpans) scaled.maxTessellationSpans = static_cast<double>(maxTessellationSpans) * scaleFactor; - if (triangleVertexBufferSize > threshold.triangleVertexBufferSize) - scaled.triangleVertexBufferSize = - static_cast<double>(triangleVertexBufferSize) * scaleFactor; + if (triangleVertexBufferCount > threshold.triangleVertexBufferCount) + scaled.triangleVertexBufferCount = + static_cast<double>(triangleVertexBufferCount) * scaleFactor; if (gradientTextureHeight > threshold.gradientTextureHeight) scaled.gradientTextureHeight = static_cast<double>(gradientTextureHeight) * scaleFactor; @@ -614,7 +469,7 @@ GPUResourceLimits resetFlushTimeLimits() const { GPUResourceLimits noFlushTimeLimits = *this; - noFlushTimeLimits.triangleVertexBufferSize = 0; + noFlushTimeLimits.triangleVertexBufferCount = 0; noFlushTimeLimits.gradientTextureHeight = 0; noFlushTimeLimits.tessellationTextureHeight = 0; return noFlushTimeLimits; @@ -641,29 +496,18 @@ // whether the buffer needs to be updated at the beginning of a flush. FlushUniforms m_cachedUniformData{0, 0, 0, 0, 0, m_platformFeatures}; - // Simple gradients only have 2 texels, so we write them to mapped texture memory from the CPU - // instead of rendering them. - struct TwoTexelRamp - { - void set(const ColorInt colors[2]) - { - UnpackColorToRGBA8(colors[0], colorData); - UnpackColorToRGBA8(colors[1], colorData + 4); - } - uint8_t colorData[8]; - }; - - BufferRing<PathData> m_pathBuffer; - BufferRing<ContourData> m_contourBuffer; - BufferRing<TwoTexelRamp> m_simpleColorRampsBuffer; // Simple gradients get written by the CPU. - BufferRing<GradientSpan> m_gradSpanBuffer; // Complex gradients get rendered by the GPU. - BufferRing<TessVertexSpan> m_tessSpanBuffer; - BufferRing<TriangleVertex> m_triangleBuffer; - BufferRing<FlushUniforms> m_uniformBuffer; + WriteOnlyMappedMemory<PathData> m_pathData; + WriteOnlyMappedMemory<ContourData> m_contourData; + // Simple gradients get written by the CPU. + WriteOnlyMappedMemory<TwoTexelRamp> m_simpleColorRampsData; + // Complex gradients get rendered by the GPU. + WriteOnlyMappedMemory<GradientSpan> m_gradSpanData; + WriteOnlyMappedMemory<TessVertexSpan> m_tessSpanData; + WriteOnlyMappedMemory<TriangleVertex> m_triangleVertexData; // How many rows of the gradient texture are dedicated to simple (two-texel) ramps? // This is also the y-coordinate at which the complex color ramps begin. - size_t m_reservedGradTextureRowsForSimpleRamps = 0; + size_t m_reservedSimpleGradientRowCount = 0; // Per-frame state. FrameDescriptor m_frameDescriptor; @@ -702,10 +546,11 @@ TrivialBlockAllocator m_trivialPerFlushAllocator{kPerFlushAllocatorInitialBlockSize}; }; -template <typename T> bool PLSRenderContext::PerFlushLinkedList<T>::empty() const +template <typename T> size_t PLSRenderContext::PerFlushLinkedList<T>::count() const { assert(!!m_head == !!m_tail); - return m_tail == nullptr; + assert(!!m_tail == !!m_count); + return m_count; } template <typename T> T& PLSRenderContext::PerFlushLinkedList<T>::tail() const @@ -729,6 +574,13 @@ m_tail->next = node; } m_tail = node; + ++m_count; } +template <typename T> void PLSRenderContext::PerFlushLinkedList<T>::reset() +{ + m_tail = nullptr; + m_head = nullptr; + m_count = 0; +} } // namespace rive::pls
diff --git a/include/rive/pls/pls_render_context_buffer_ring_impl.hpp b/include/rive/pls/pls_render_context_buffer_ring_impl.hpp new file mode 100644 index 0000000..d92c162 --- /dev/null +++ b/include/rive/pls/pls_render_context_buffer_ring_impl.hpp
@@ -0,0 +1,85 @@ +/* + * Copyright 2023 Rive + */ + +#pragma once + +#include "rive/pls/pls_render_context_impl.hpp" +#include "rive/pls/buffer_ring.hpp" + +namespace rive::pls +{ +class PLSRenderContextBufferRingImpl : public PLSRenderContextImpl +{ +public: + PLSRenderContextBufferRingImpl(const PlatformFeatures& platformFeatures) + { + m_platformFeatures = platformFeatures; + } + + void resizePathTexture(size_t width, size_t height) override; + void resizeContourTexture(size_t width, size_t height) override; + void resizeSimpleColorRampsBuffer(size_t sizeInBytes) override; + void resizeGradSpanBuffer(size_t sizeInBytes) override; + void resizeTessVertexSpanBuffer(size_t sizeInBytes) override; + void resizeTriangleVertexBuffer(size_t sizeInBytes) override; + + void resizeGradientTexture(size_t height) override { allocateGradientTexture(height); } + void resizeTessellationTexture(size_t height) override { allocateTessellationTexture(height); } + + void* mapPathTexture() override { return m_pathBuffer->mapBuffer(); } + void* mapContourTexture() override { return m_contourBuffer->mapBuffer(); } + void* mapSimpleColorRampsBuffer() override { return m_simpleColorRampsBuffer->mapBuffer(); } + void* mapGradSpanBuffer() override { return m_gradSpanBuffer->mapBuffer(); } + void* mapTessVertexSpanBuffer() override { return m_tessSpanBuffer->mapBuffer(); } + void* mapTriangleVertexBuffer() override { return m_triangleBuffer->mapBuffer(); } + + void unmapPathTexture(size_t widthWritten, size_t heightWritten) override; + void unmapContourTexture(size_t widthWritten, size_t heightWritten) override; + void unmapSimpleColorRampsBuffer(size_t bytesWritten) override; + void unmapGradSpanBuffer(size_t bytesWritten) override; + void unmapTessVertexSpanBuffer(size_t bytesWritten) override; + void unmapTriangleVertexBuffer(size_t bytesWritten) override; + + void updateFlushUniforms(const FlushUniforms*) override; + +protected: + const TexelBufferRing* pathBufferRing() { return m_pathBuffer.get(); } + const TexelBufferRing* contourBufferRing() { return m_contourBuffer.get(); } + const BufferRingImpl* simpleColorRampsBufferRing() const + { + return m_simpleColorRampsBuffer.get(); + } + const BufferRingImpl* gradSpanBufferRing() const { return m_gradSpanBuffer.get(); } + const BufferRingImpl* tessSpanBufferRing() { return m_tessSpanBuffer.get(); } + const BufferRingImpl* triangleBufferRing() { return m_triangleBuffer.get(); } + const BufferRingImpl* uniformBufferRing() const { return m_uniformBuffer.get(); } + + virtual std::unique_ptr<BufferRingImpl> makeVertexBufferRing(size_t capacity, + size_t itemSizeInBytes) = 0; + + virtual std::unique_ptr<TexelBufferRing> makeTexelBufferRing(TexelBufferRing::Format, + size_t widthInItems, + size_t height, + size_t texelsPerItem, + int textureIdx, + TexelBufferRing::Filter) = 0; + + virtual std::unique_ptr<BufferRingImpl> makePixelUnpackBufferRing(size_t capacity, + size_t itemSizeInBytes) = 0; + + virtual std::unique_ptr<BufferRingImpl> makeUniformBufferRing(size_t sizeInBytes) = 0; + + virtual void allocateGradientTexture(size_t height) = 0; + virtual void allocateTessellationTexture(size_t height) = 0; + +private: + std::unique_ptr<TexelBufferRing> m_pathBuffer; + std::unique_ptr<TexelBufferRing> m_contourBuffer; + std::unique_ptr<BufferRingImpl> m_simpleColorRampsBuffer; + std::unique_ptr<BufferRingImpl> m_gradSpanBuffer; + std::unique_ptr<BufferRingImpl> m_tessSpanBuffer; + std::unique_ptr<BufferRingImpl> m_triangleBuffer; + std::unique_ptr<BufferRingImpl> m_uniformBuffer; +}; +} // namespace rive::pls
diff --git a/include/rive/pls/pls_render_context_impl.hpp b/include/rive/pls/pls_render_context_impl.hpp new file mode 100644 index 0000000..7ef295f --- /dev/null +++ b/include/rive/pls/pls_render_context_impl.hpp
@@ -0,0 +1,50 @@ +/* + * Copyright 2023 Rive + */ + +#pragma once + +#include "rive/pls/pls_render_context.hpp" + +namespace rive::pls +{ +class PLSRenderContextImpl +{ +public: + virtual ~PLSRenderContextImpl() {} + + const PlatformFeatures& platformFeatures() const { return m_platformFeatures; } + + virtual void prepareToMapBuffers() {} + + virtual void resizePathTexture(size_t width, size_t height) = 0; + virtual void resizeContourTexture(size_t width, size_t height) = 0; + virtual void resizeSimpleColorRampsBuffer(size_t sizeInBytes) = 0; + virtual void resizeGradSpanBuffer(size_t sizeInBytes) = 0; + virtual void resizeTessVertexSpanBuffer(size_t sizeInBytes) = 0; + virtual void resizeTriangleVertexBuffer(size_t sizeInBytes) = 0; + virtual void resizeGradientTexture(size_t height) = 0; + virtual void resizeTessellationTexture(size_t height) = 0; + + virtual void* mapPathTexture() = 0; + virtual void* mapContourTexture() = 0; + virtual void* mapSimpleColorRampsBuffer() = 0; + virtual void* mapGradSpanBuffer() = 0; + virtual void* mapTessVertexSpanBuffer() = 0; + virtual void* mapTriangleVertexBuffer() = 0; + + virtual void unmapPathTexture(size_t widthWritten, size_t heightWritten) = 0; + virtual void unmapContourTexture(size_t widthWritten, size_t heightWritten) = 0; + virtual void unmapSimpleColorRampsBuffer(size_t bytesWritten) = 0; + virtual void unmapGradSpanBuffer(size_t bytesWritten) = 0; + virtual void unmapTessVertexSpanBuffer(size_t bytesWritten) = 0; + virtual void unmapTriangleVertexBuffer(size_t bytesWritten) = 0; + + virtual void updateFlushUniforms(const FlushUniforms*) = 0; + + virtual void flush(const PLSRenderContext::FlushDescriptor&) = 0; + +protected: + PlatformFeatures m_platformFeatures; +}; +} // namespace rive::pls
diff --git a/obfuscator/pls_renames.h b/obfuscator/pls_renames.h index 45283ed..d5c0ca3 100644 --- a/obfuscator/pls_renames.h +++ b/obfuscator/pls_renames.h
@@ -57,7 +57,7 @@ #define PLSPaint r_38 #define PLSPath r_39 #define PLSRenderContext r_3a -#define PLSRenderContextGL r_3b +#define PLSRenderContextGLImpl r_3b #define PLSRenderContextMetal r_3c #define PLSRenderTarget r_3d #define PLSRenderTargetGL r_3e
diff --git a/out/premake5_pls_renderer.lua b/out/premake5_pls_renderer.lua index dd3e22b..f24dd44 100644 --- a/out/premake5_pls_renderer.lua +++ b/out/premake5_pls_renderer.lua
@@ -121,7 +121,7 @@ do files {"../renderer/gl/buffer_ring_gl.cpp", "../renderer/gl/gl_utils.cpp", - "../renderer/gl/pls_render_context_gl.cpp", + "../renderer/gl/pls_render_context_gl_impl.cpp", "../renderer/gl/pls_render_target_gl.cpp"} end
diff --git a/path_fiddle/fiddle_context_d3d.cpp b/path_fiddle/fiddle_context_d3d.cpp index 10b6829..60498f8 100644 --- a/path_fiddle/fiddle_context_d3d.cpp +++ b/path_fiddle/fiddle_context_d3d.cpp
@@ -2,7 +2,7 @@ #include "rive/pls/pls_factory.hpp" #include "rive/pls/pls_renderer.hpp" -#include "rive/pls/d3d/pls_render_context_d3d.hpp" +#include "rive/pls/d3d/pls_render_context_d3d_impl.hpp" #include "rive/pls/d3d/d3d11.hpp" #include <array> #include <dxgi1_2.h> @@ -25,7 +25,8 @@ m_d3dFactory(std::move(d3dFactory)), m_gpu(std::move(gpu)), m_gpuContext(std::move(gpuContext)), - m_plsContext(new PLSRenderContextD3D(m_gpu, m_gpuContext, isIntel)) + m_plsContext(new PLSRenderContext( + std::make_unique<PLSRenderContextD3DImpl>(m_gpu, m_gpuContext, isIntel))) {} float dpiScale() const override { return 1; } @@ -51,7 +52,8 @@ NULL, m_swapchain.GetAddressOf())); - m_renderTarget = m_plsContext->makeRenderTarget(width, height); + auto d3dContext = static_cast<PLSRenderContextD3DImpl*>(m_plsContext->impl()); + m_renderTarget = d3dContext->makeRenderTarget(width, height); } void toggleZoomWindow() override {} @@ -91,7 +93,7 @@ ComPtr<ID3D11Device> m_gpu; ComPtr<ID3D11DeviceContext> m_gpuContext; ComPtr<IDXGISwapChain1> m_swapchain; - std::unique_ptr<PLSRenderContextD3D> m_plsContext; + std::unique_ptr<PLSRenderContext> m_plsContext; rcp<PLSRenderTargetD3D> m_renderTarget; };
diff --git a/path_fiddle/fiddle_context_gl.cpp b/path_fiddle/fiddle_context_gl.cpp index 03ebb40..4da4033 100644 --- a/path_fiddle/fiddle_context_gl.cpp +++ b/path_fiddle/fiddle_context_gl.cpp
@@ -3,7 +3,7 @@ #include "rive/pls/gl/gles3.hpp" #include "rive/pls/pls_factory.hpp" #include "rive/pls/pls_renderer.hpp" -#include "rive/pls/gl/pls_render_context_gl.hpp" +#include "rive/pls/gl/pls_render_context_gl_impl.hpp" #include "rive/pls/gl/pls_render_target_gl.hpp" #ifdef RIVE_WASM @@ -274,7 +274,8 @@ void onSizeChanged(int width, int height) override { - m_renderTarget = m_plsContext->makeOffscreenRenderTarget(width, height); + auto glContext = static_cast<PLSRenderContextGLImpl*>(m_plsContext->impl()); + m_renderTarget = glContext->makeOffscreenRenderTarget(width, height); } std::unique_ptr<Renderer> makeRenderer(int width, int height) override @@ -305,7 +306,8 @@ void shrinkGPUResourcesToFit() final { m_plsContext->shrinkGPUResourcesToFit(); } private: - std::unique_ptr<PLSRenderContextGL> m_plsContext = PLSRenderContextGL::Make(); + std::unique_ptr<PLSRenderContext> m_plsContext = + std::make_unique<PLSRenderContext>(PLSRenderContextGLImpl::Make()); rcp<PLSRenderTargetGL> m_renderTarget; };
diff --git a/path_fiddle/fiddle_context_metal.mm b/path_fiddle/fiddle_context_metal.mm index e77ca29..01c107d 100644 --- a/path_fiddle/fiddle_context_metal.mm +++ b/path_fiddle/fiddle_context_metal.mm
@@ -2,7 +2,7 @@ #include "rive/pls/pls_factory.hpp" #include "rive/pls/pls_renderer.hpp" -#include "rive/pls/gl/pls_render_context_gl.hpp" +#include "rive/pls/gl/pls_render_context_gl_impl.hpp" #include "rive/pls/gl/pls_render_target_gl.hpp" #include "rive/pls/metal/pls_render_context_metal.h" #include "rive/pls/metal/pls_render_target_metal.h"
diff --git a/renderer/d3d/pls_render_context_d3d.cpp b/renderer/d3d/pls_render_context_d3d_impl.cpp similarity index 95% rename from renderer/d3d/pls_render_context_d3d.cpp rename to renderer/d3d/pls_render_context_d3d_impl.cpp index 7f1ef56..4cbee67 100644 --- a/renderer/d3d/pls_render_context_d3d.cpp +++ b/renderer/d3d/pls_render_context_d3d_impl.cpp
@@ -2,7 +2,7 @@ * Copyright 2023 Rive */ -#include "rive/pls/d3d/pls_render_context_d3d.hpp" +#include "rive/pls/d3d/pls_render_context_d3d_impl.hpp" #include <D3DCompiler.h> #include <sstream> @@ -151,10 +151,10 @@ return platformFeatures; } -PLSRenderContextD3D::PLSRenderContextD3D(ComPtr<ID3D11Device> gpu, - ComPtr<ID3D11DeviceContext> gpuContext, - bool isIntel) : - PLSRenderContext(platform_features_d3d()), +PLSRenderContextD3DImpl::PLSRenderContextD3DImpl(ComPtr<ID3D11Device> gpu, + ComPtr<ID3D11DeviceContext> gpuContext, + bool isIntel) : + PLSRenderContextBufferRingImpl(platform_features_d3d()), m_isIntel(isIntel), m_gpu(gpu), m_gpuContext(gpuContext) @@ -345,7 +345,6 @@ desc.Usage = D3D11_USAGE_DEFAULT; desc.BindFlags = bindFlags; desc.CPUAccessFlags = 0; - desc.StructureByteStride = itemSizeInBytes; for (size_t i = 0; i < kBufferRingSize; ++i) { @@ -383,8 +382,9 @@ ComPtr<ID3D11Buffer> m_buffers[kBufferRingSize]; }; -std::unique_ptr<BufferRingImpl> PLSRenderContextD3D::makeVertexBufferRing(size_t capacity, - size_t itemSizeInBytes) +std::unique_ptr<BufferRingImpl> PLSRenderContextD3DImpl::makeVertexBufferRing( + size_t capacity, + size_t itemSizeInBytes) { return std::make_unique<BufferRingD3D>(m_gpu.Get(), m_gpuContext, @@ -393,7 +393,7 @@ D3D11_BIND_VERTEX_BUFFER); } -std::unique_ptr<BufferRingImpl> PLSRenderContextD3D::makePixelUnpackBufferRing( +std::unique_ptr<BufferRingImpl> PLSRenderContextD3DImpl::makePixelUnpackBufferRing( size_t capacity, size_t itemSizeInBytes) { @@ -401,7 +401,8 @@ return std::make_unique<CPUOnlyBufferRing>(capacity, itemSizeInBytes); } -std::unique_ptr<BufferRingImpl> PLSRenderContextD3D::makeUniformBufferRing(size_t itemSizeInBytes) +std::unique_ptr<BufferRingImpl> PLSRenderContextD3DImpl::makeUniformBufferRing( + size_t itemSizeInBytes) { return std::make_unique<BufferRingD3D>(m_gpu.Get(), m_gpuContext, @@ -456,7 +457,7 @@ ComPtr<ID3D11ShaderResourceView> m_srvs[kBufferRingSize]; }; -std::unique_ptr<TexelBufferRing> PLSRenderContextD3D::makeTexelBufferRing( +std::unique_ptr<TexelBufferRing> PLSRenderContextD3DImpl::makeTexelBufferRing( TexelBufferRing::Format format, size_t widthInItems, size_t height, @@ -510,12 +511,12 @@ m_targetUAV = make_simple_2d_uav(gpu, m_targetTexture.Get(), DXGI_FORMAT_R8G8B8A8_UNORM); } -rcp<PLSRenderTargetD3D> PLSRenderContextD3D::makeRenderTarget(size_t width, size_t height) +rcp<PLSRenderTargetD3D> PLSRenderContextD3DImpl::makeRenderTarget(size_t width, size_t height) { return rcp(new PLSRenderTargetD3D(m_gpu.Get(), width, height)); } -void PLSRenderContextD3D::allocateGradientTexture(size_t height) +void PLSRenderContextD3DImpl::allocateGradientTexture(size_t height) { m_gradTexture = make_simple_2d_texture(m_gpu.Get(), DXGI_FORMAT_R8G8B8A8_UNORM, @@ -528,7 +529,7 @@ make_simple_2d_rtv(m_gpu.Get(), m_gradTexture.Get(), DXGI_FORMAT_R8G8B8A8_UNORM); } -void PLSRenderContextD3D::allocateTessellationTexture(size_t height) +void PLSRenderContextD3DImpl::allocateTessellationTexture(size_t height) { m_tessTexture = make_simple_2d_texture(m_gpu.Get(), DXGI_FORMAT_R32G32B32A32_UINT, @@ -541,8 +542,8 @@ make_simple_2d_rtv(m_gpu.Get(), m_tessTexture.Get(), DXGI_FORMAT_R32G32B32A32_UINT); } -void PLSRenderContextD3D::setPipelineLayoutAndShaders(DrawType drawType, - const ShaderFeatures& shaderFeatures) +void PLSRenderContextD3DImpl::setPipelineLayoutAndShaders(DrawType drawType, + const ShaderFeatures& shaderFeatures) { uint32_t vertexShaderKey = ShaderUniqueKey(SourceType::vertexOnly, drawType, shaderFeatures); auto vertexEntry = m_drawVertexShaders.find(vertexShaderKey); @@ -670,16 +671,15 @@ return static_cast<const TexelBufferD3D*>(texelBufferRing)->submittedSRV(); } -void PLSRenderContextD3D::onFlush(const FlushDescriptor& desc) +void PLSRenderContextD3DImpl::flush(const PLSRenderContext::FlushDescriptor& desc) { - auto renderTarget = - static_cast<const PLSRenderTargetD3D*>(frameDescriptor().renderTarget.get()); + auto renderTarget = static_cast<const PLSRenderTargetD3D*>(desc.renderTarget); constexpr static UINT kZero[4]{}; if (desc.loadAction == LoadAction::clear) { float clearColor4f[4]; - UnpackColorToRGBA32F(frameDescriptor().clearColor, clearColor4f); + UnpackColorToRGBA32F(desc.clearColor, clearColor4f); m_gpuContext->ClearUnorderedAccessViewFloat(renderTarget->m_targetUAV.Get(), clearColor4f); } m_gpuContext->ClearUnorderedAccessViewUint(renderTarget->m_coverageUAV.Get(), kZero); @@ -803,7 +803,11 @@ static_assert(kTriangleVertexDataSlot == 1); UINT vertexStrides[] = {sizeof(PatchVertex), sizeof(TriangleVertex)}; UINT vertexOffsets[] = {0, 0}; - m_gpuContext->IASetVertexBuffers(0, 2, vertexBuffers, vertexStrides, vertexOffsets); + m_gpuContext->IASetVertexBuffers(0, + desc.hasTriangleVertices ? 2 : 1, + vertexBuffers, + vertexStrides, + vertexOffsets); ID3D11ShaderResourceView* vertexTextureViews[] = {m_tessTextureSRV.Get(), submitted_srv(pathBufferRing()), @@ -820,14 +824,14 @@ 0, 1}; m_gpuContext->RSSetViewports(1, &viewport); - if (frameDescriptor().wireframe) + if (desc.wireframe) { m_gpuContext->RSSetState(m_debugWireframeState.Get()); } m_gpuContext->PSSetShaderResources(kGradTextureIdx, 1, m_gradTextureSRV.GetAddressOf()); - for (const Draw& draw : m_drawList) + for (const Draw& draw : *desc.drawList) { if (draw.vertexOrInstanceCount == 0) { @@ -857,7 +861,7 @@ } } - if (frameDescriptor().wireframe) + if (desc.wireframe) { m_gpuContext->RSSetState(m_rasterState.Get()); }
diff --git a/renderer/gl/pls_impl_ext_native.cpp b/renderer/gl/pls_impl_ext_native.cpp index 94c11d4..6bfde49 100644 --- a/renderer/gl/pls_impl_ext_native.cpp +++ b/renderer/gl/pls_impl_ext_native.cpp
@@ -2,7 +2,7 @@ * Copyright 2023 Rive */ -#include "rive/pls/gl/pls_render_context_gl.hpp" +#include "rive/pls/gl/pls_render_context_gl_impl.hpp" #include "gl/gl_utils.hpp" #include "rive/math/simd.hpp" @@ -86,7 +86,7 @@ GLint m_clearColorUniLocation = -1; }; -class PLSRenderContextGL::PLSImplEXTNative : public PLSRenderContextGL::PLSImpl +class PLSRenderContextGLImpl::PLSImplEXTNative : public PLSRenderContextGLImpl::PLSImpl { public: PLSImplEXTNative(const GLExtensions& extensions) : m_extensions(extensions) @@ -120,30 +120,29 @@ return rcp(new PLSRenderTargetGL(width, height, platformFeatures)); } - void activatePixelLocalStorage(PLSRenderContextGL* context, - const PLSRenderTargetGL* renderTarget, - LoadAction loadAction, - bool needsClipBuffer) override + void activatePixelLocalStorage(PLSRenderContextGLImpl* context, + const PLSRenderContext::FlushDescriptor& desc) override { assert(context->m_extensions.EXT_shader_pixel_local_storage); assert(context->m_extensions.EXT_shader_framebuffer_fetch || context->m_extensions.ARM_shader_framebuffer_fetch); + auto renderTarget = static_cast<const PLSRenderTargetGL*>(desc.renderTarget); glBindFramebuffer(GL_FRAMEBUFFER, renderTarget->drawFramebufferID()); glEnable(GL_SHADER_PIXEL_LOCAL_STORAGE_EXT); uint32_t ops = loadstoreops::kClearCoverage; float clearColor4f[4]; - if (loadAction == LoadAction::clear) + if (desc.loadAction == LoadAction::clear) { - UnpackColorToRGBA32F(context->frameDescriptor().clearColor, clearColor4f); + UnpackColorToRGBA32F(desc.clearColor, clearColor4f); ops |= loadstoreops::kClearColor; } else { ops |= loadstoreops::kLoadColor; } - if (needsClipBuffer) + if (desc.needsClipBuffer) { ops |= loadstoreops::kClearClip; } @@ -171,7 +170,7 @@ } } - void deactivatePixelLocalStorage(PLSRenderContextGL* context) override + void deactivatePixelLocalStorage(PLSRenderContextGLImpl* context) override { // Issue a fullscreen draw that transfers the color information in pixel local storage to // the main framebuffer. @@ -195,7 +194,7 @@ GLuint m_plsLoadStoreVAO = 0; }; -std::unique_ptr<PLSRenderContextGL::PLSImpl> PLSRenderContextGL::MakePLSImplEXTNative( +std::unique_ptr<PLSRenderContextGLImpl::PLSImpl> PLSRenderContextGLImpl::MakePLSImplEXTNative( const GLExtensions& extensions) { return std::make_unique<PLSImplEXTNative>(extensions);
diff --git a/renderer/gl/pls_impl_framebuffer_fetch.cpp b/renderer/gl/pls_impl_framebuffer_fetch.cpp index 7454cd6..79a65f0 100644 --- a/renderer/gl/pls_impl_framebuffer_fetch.cpp +++ b/renderer/gl/pls_impl_framebuffer_fetch.cpp
@@ -2,7 +2,7 @@ * Copyright 2023 Rive */ -#include "rive/pls/gl/pls_render_context_gl.hpp" +#include "rive/pls/gl/pls_render_context_gl_impl.hpp" #include "rive/pls/gl/pls_render_target_gl.hpp" @@ -15,7 +15,7 @@ GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3}; -class PLSRenderContextGL::PLSImplFramebufferFetch : public PLSRenderContextGL::PLSImpl +class PLSRenderContextGLImpl::PLSImplFramebufferFetch : public PLSRenderContextGLImpl::PLSImpl { public: PLSImplFramebufferFetch(const GLExtensions& extensions) : m_extensions(extensions) {} @@ -47,13 +47,12 @@ return renderTarget; } - void activatePixelLocalStorage(PLSRenderContextGL* context, - const PLSRenderTargetGL* renderTarget, - LoadAction loadAction, - bool needsClipBuffer) override + void activatePixelLocalStorage(PLSRenderContextGLImpl* context, + const PLSRenderContext::FlushDescriptor& desc) override { assert(context->m_extensions.EXT_shader_framebuffer_fetch); + auto renderTarget = static_cast<const PLSRenderTargetGL*>(desc.renderTarget); glBindFramebuffer(GL_FRAMEBUFFER, renderTarget->drawFramebufferID()); // Enable multiple render targets, with a draw buffer for each PLS plane. @@ -63,26 +62,26 @@ // exception of the color buffer after an intermediate flush. static_assert(kFramebufferPlaneIdx == 0); glInvalidateFramebuffer(GL_FRAMEBUFFER, - loadAction == LoadAction::clear ? 4 : 3, - loadAction == LoadAction::clear ? kPLSDrawBuffers - : kPLSDrawBuffers + 1); + desc.loadAction == LoadAction::clear ? 4 : 3, + desc.loadAction == LoadAction::clear ? kPLSDrawBuffers + : kPLSDrawBuffers + 1); // Clear the PLS planes. constexpr static uint32_t kZero[4]{}; - if (loadAction == LoadAction::clear) + if (desc.loadAction == LoadAction::clear) { float clearColor4f[4]; - UnpackColorToRGBA32F(context->frameDescriptor().clearColor, clearColor4f); + UnpackColorToRGBA32F(desc.clearColor, clearColor4f); glClearBufferfv(GL_COLOR, kFramebufferPlaneIdx, clearColor4f); } glClearBufferuiv(GL_COLOR, kCoveragePlaneIdx, kZero); - if (needsClipBuffer) + if (desc.needsClipBuffer) { glClearBufferuiv(GL_COLOR, kClipPlaneIdx, kZero); } } - void deactivatePixelLocalStorage(PLSRenderContextGL*) override + void deactivatePixelLocalStorage(PLSRenderContextGLImpl*) override { // Instruct the driver not to flush PLS contents from tiled memory, with the exception of // the color buffer. @@ -124,8 +123,8 @@ const GLExtensions m_extensions; }; -std::unique_ptr<PLSRenderContextGL::PLSImpl> PLSRenderContextGL::MakePLSImplFramebufferFetch( - const GLExtensions& extensions) +std::unique_ptr<PLSRenderContextGLImpl::PLSImpl> PLSRenderContextGLImpl:: + MakePLSImplFramebufferFetch(const GLExtensions& extensions) { return std::make_unique<PLSImplFramebufferFetch>(extensions); }
diff --git a/renderer/gl/pls_impl_rw_texture.cpp b/renderer/gl/pls_impl_rw_texture.cpp index 383d240..353f510 100644 --- a/renderer/gl/pls_impl_rw_texture.cpp +++ b/renderer/gl/pls_impl_rw_texture.cpp
@@ -2,7 +2,7 @@ * Copyright 2023 Rive */ -#include "rive/pls/gl/pls_render_context_gl.hpp" +#include "rive/pls/gl/pls_render_context_gl_impl.hpp" #include "../out/obj/generated/glsl.exports.h" @@ -13,7 +13,7 @@ GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3}; -class PLSRenderContextGL::PLSImplRWTexture : public PLSRenderContextGL::PLSImpl +class PLSRenderContextGLImpl::PLSImplRWTexture : public PLSRenderContextGLImpl::PLSImpl { rcp<PLSRenderTargetGL> wrapGLRenderTarget(GLuint framebufferID, size_t width, @@ -39,22 +39,22 @@ return renderTarget; } - void activatePixelLocalStorage(PLSRenderContextGL* context, - const PLSRenderTargetGL* renderTarget, - LoadAction loadAction, - bool needsClipBuffer) override + void activatePixelLocalStorage(PLSRenderContextGLImpl*, + const PLSRenderContext::FlushDescriptor& desc) override { + auto renderTarget = static_cast<const PLSRenderTargetGL*>(desc.renderTarget); + // Clear the necessary textures. constexpr static GLuint kZero[4]{}; glBindFramebuffer(GL_FRAMEBUFFER, renderTarget->sideFramebufferID()); - if (loadAction == LoadAction::clear) + if (desc.loadAction == LoadAction::clear) { float clearColor4f[4]; - UnpackColorToRGBA32F(context->frameDescriptor().clearColor, clearColor4f); + UnpackColorToRGBA32F(desc.clearColor, clearColor4f); glClearBufferfv(GL_COLOR, kFramebufferPlaneIdx, clearColor4f); } glClearBufferuiv(GL_COLOR, kCoveragePlaneIdx, kZero); - if (needsClipBuffer) + if (desc.needsClipBuffer) { glClearBufferuiv(GL_COLOR, kClipPlaneIdx, kZero); } @@ -81,7 +81,7 @@ 0, GL_READ_WRITE, GL_RGBA8); - if (needsClipBuffer) + if (desc.needsClipBuffer) { glBindImageTexture(kClipPlaneIdx, renderTarget->m_clipTextureID, @@ -96,7 +96,7 @@ glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); } - void deactivatePixelLocalStorage(PLSRenderContextGL*) override + void deactivatePixelLocalStorage(PLSRenderContextGLImpl*) override { glMemoryBarrier(GL_ALL_BARRIER_BITS); } @@ -106,7 +106,7 @@ void onBarrier() override { return glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); } }; -std::unique_ptr<PLSRenderContextGL::PLSImpl> PLSRenderContextGL::MakePLSImplRWTexture() +std::unique_ptr<PLSRenderContextGLImpl::PLSImpl> PLSRenderContextGLImpl::MakePLSImplRWTexture() { return std::make_unique<PLSImplRWTexture>(); }
diff --git a/renderer/gl/pls_impl_webgl.cpp b/renderer/gl/pls_impl_webgl.cpp index 7a91781..512c79d 100644 --- a/renderer/gl/pls_impl_webgl.cpp +++ b/renderer/gl/pls_impl_webgl.cpp
@@ -2,7 +2,7 @@ * Copyright 2022 Rive */ -#include "rive/pls/gl/pls_render_context_gl.hpp" +#include "rive/pls/gl/pls_render_context_gl_impl.hpp" #include <stdio.h> @@ -15,7 +15,7 @@ namespace rive::pls { -class PLSRenderContextGL::PLSImplWebGL : public PLSRenderContextGL::PLSImpl +class PLSRenderContextGLImpl::PLSImplWebGL : public PLSRenderContextGLImpl::PLSImpl { rcp<PLSRenderTargetGL> wrapGLRenderTarget(GLuint framebufferID, size_t width, @@ -53,30 +53,29 @@ return renderTarget; } - void activatePixelLocalStorage(PLSRenderContextGL* context, - const PLSRenderTargetGL* renderTarget, - LoadAction loadAction, - bool needsClipBuffer) override + void activatePixelLocalStorage(PLSRenderContextGLImpl*, + const PLSRenderContext::FlushDescriptor& desc) override { + auto renderTarget = static_cast<const PLSRenderTargetGL*>(desc.renderTarget); glBindFramebuffer(GL_FRAMEBUFFER, renderTarget->drawFramebufferID()); - if (loadAction == LoadAction::clear) + if (desc.loadAction == LoadAction::clear) { float clearColor4f[4]; - UnpackColorToRGBA32F(context->frameDescriptor().clearColor, clearColor4f); + UnpackColorToRGBA32F(desc.clearColor, clearColor4f); glFramebufferPixelLocalClearValuefvWEBGL(kFramebufferPlaneIdx, clearColor4f); } - GLenum loadOps[4] = {(GLenum)(loadAction == LoadAction::clear ? GL_LOAD_OP_CLEAR_WEBGL - : GL_LOAD_OP_LOAD_WEBGL), + GLenum loadOps[4] = {(GLenum)(desc.loadAction == LoadAction::clear ? GL_LOAD_OP_CLEAR_WEBGL + : GL_LOAD_OP_LOAD_WEBGL), GL_LOAD_OP_ZERO_WEBGL, GL_DONT_CARE, - (GLenum)(needsClipBuffer ? GL_LOAD_OP_ZERO_WEBGL : GL_DONT_CARE)}; + (GLenum)(desc.needsClipBuffer ? GL_LOAD_OP_ZERO_WEBGL : GL_DONT_CARE)}; glBeginPixelLocalStorageWEBGL(4, loadOps); } - void deactivatePixelLocalStorage(PLSRenderContextGL*) override + void deactivatePixelLocalStorage(PLSRenderContextGLImpl*) override { constexpr static GLenum kStoreOps[4] = {GL_STORE_OP_STORE_WEBGL, GL_DONT_CARE, @@ -88,7 +87,7 @@ const char* shaderDefineName() const override { return GLSL_PLS_IMPL_WEBGL; } }; -std::unique_ptr<PLSRenderContextGL::PLSImpl> PLSRenderContextGL::MakePLSImplWebGL() +std::unique_ptr<PLSRenderContextGLImpl::PLSImpl> PLSRenderContextGLImpl::MakePLSImplWebGL() { return std::make_unique<PLSImplWebGL>(); }
diff --git a/renderer/gl/pls_render_context_gl.cpp b/renderer/gl/pls_render_context_gl_impl.cpp similarity index 88% rename from renderer/gl/pls_render_context_gl.cpp rename to renderer/gl/pls_render_context_gl_impl.cpp index 48445dd..70c0e01 100644 --- a/renderer/gl/pls_render_context_gl.cpp +++ b/renderer/gl/pls_render_context_gl_impl.cpp
@@ -2,7 +2,7 @@ * Copyright 2022 Rive */ -#include "rive/pls/gl/pls_render_context_gl.hpp" +#include "rive/pls/gl/pls_render_context_gl_impl.hpp" #include "buffer_ring_gl.hpp" #include "gl_utils.hpp" @@ -32,10 +32,12 @@ }); #endif -PLSRenderContextGL::PLSRenderContextGL(const PlatformFeatures& platformFeatures, - GLExtensions extensions, - std::unique_ptr<PLSImpl> plsImpl) : - PLSRenderContext(platformFeatures), m_extensions(extensions), m_plsImpl(std::move(plsImpl)) +PLSRenderContextGLImpl::PLSRenderContextGLImpl(const PlatformFeatures& platformFeatures, + GLExtensions extensions, + std::unique_ptr<PLSImpl> plsImpl) : + PLSRenderContextBufferRingImpl(platformFeatures), + m_extensions(extensions), + m_plsImpl(std::move(plsImpl)) { m_shaderVersionString[kShaderVersionStringBuffSize - 1] = '\0'; @@ -175,7 +177,7 @@ #endif } -PLSRenderContextGL::~PLSRenderContextGL() +PLSRenderContextGLImpl::~PLSRenderContextGLImpl() { glDeleteProgram(m_colorRampProgram); glDeleteVertexArrays(1, &m_colorRampVAO); @@ -192,13 +194,13 @@ glDeleteBuffers(1, &m_patchIndicesBuffer); } -std::unique_ptr<BufferRingImpl> PLSRenderContextGL::makeVertexBufferRing(size_t capacity, - size_t itemSizeInBytes) +std::unique_ptr<BufferRingImpl> PLSRenderContextGLImpl::makeVertexBufferRing(size_t capacity, + size_t itemSizeInBytes) { return std::make_unique<BufferGL>(GL_ARRAY_BUFFER, capacity, itemSizeInBytes); } -std::unique_ptr<TexelBufferRing> PLSRenderContextGL::makeTexelBufferRing( +std::unique_ptr<TexelBufferRing> PLSRenderContextGLImpl::makeTexelBufferRing( TexelBufferRing::Format format, size_t widthInItems, size_t height, @@ -214,19 +216,19 @@ filter); } -std::unique_ptr<BufferRingImpl> PLSRenderContextGL::makePixelUnpackBufferRing( +std::unique_ptr<BufferRingImpl> PLSRenderContextGLImpl::makePixelUnpackBufferRing( size_t capacity, size_t itemSizeInBytes) { return std::make_unique<BufferGL>(GL_PIXEL_UNPACK_BUFFER, capacity, itemSizeInBytes); } -std::unique_ptr<BufferRingImpl> PLSRenderContextGL::makeUniformBufferRing(size_t sizeInBytes) +std::unique_ptr<BufferRingImpl> PLSRenderContextGLImpl::makeUniformBufferRing(size_t sizeInBytes) { return std::make_unique<BufferGL>(GL_UNIFORM_BUFFER, 1, sizeInBytes); } -void PLSRenderContextGL::allocateGradientTexture(size_t height) +void PLSRenderContextGLImpl::allocateGradientTexture(size_t height) { glDeleteTextures(1, &m_gradientTexture); @@ -247,7 +249,7 @@ 0); } -void PLSRenderContextGL::allocateTessellationTexture(size_t height) +void PLSRenderContextGLImpl::allocateTessellationTexture(size_t height) { glDeleteTextures(1, &m_tessVertexTexture); @@ -270,13 +272,13 @@ // Wraps a compiled GL shader of draw.glsl, either vertex or fragment, with a specific set of // features enabled via #define. The set of features to enable is dictated by ShaderFeatures. -class PLSRenderContextGL::DrawShader +class PLSRenderContextGLImpl::DrawShader { public: DrawShader(const DrawShader&) = delete; DrawShader& operator=(const DrawShader&) = delete; - DrawShader(PLSRenderContextGL* context, + DrawShader(PLSRenderContextGLImpl* context, GLenum shaderType, DrawType drawType, const ShaderFeatures& shaderFeatures) @@ -317,7 +319,7 @@ sources.push_back(glsl::advanced_blend); } } - if (context->m_platformFeatures.avoidFlatVaryings) + if (context->platformFeatures().avoidFlatVaryings) { sources.push_back("#define " GLSL_OPTIONALLY_FLAT "\n"); } @@ -343,9 +345,9 @@ GLuint m_id; }; -PLSRenderContextGL::DrawProgram::DrawProgram(PLSRenderContextGL* context, - DrawType drawType, - const ShaderFeatures& shaderFeatures) +PLSRenderContextGLImpl::DrawProgram::DrawProgram(PLSRenderContextGLImpl* context, + DrawType drawType, + const ShaderFeatures& shaderFeatures) { m_id = glCreateProgram(); @@ -378,14 +380,14 @@ } } -PLSRenderContextGL::DrawProgram::~DrawProgram() { glDeleteProgram(m_id); } +PLSRenderContextGLImpl::DrawProgram::~DrawProgram() { glDeleteProgram(m_id); } static GLuint gl_buffer_id(const BufferRingImpl* bufferRing) { return static_cast<const BufferGL*>(bufferRing)->submittedBufferID(); } -void PLSRenderContextGL::onFlush(const FlushDescriptor& desc) +void PLSRenderContextGLImpl::flush(const PLSRenderContext::FlushDescriptor& desc) { // All programs use the same set of per-flush uniforms. glBindBufferBase(GL_UNIFORM_BUFFER, 0, gl_buffer_id(uniformBufferRing())); @@ -450,7 +452,7 @@ // Compile the draw programs before activating pixel local storage. // (ANGLE_shader_pixel_local_storage doesn't allow shader compilation while active.) - for (const Draw& draw : m_drawList) + for (const Draw& draw : *desc.drawList) { // Compile the draw program before activating pixel local storage. // Cache specific compilations of draw.glsl by ShaderFeatures. @@ -460,30 +462,28 @@ } // Bind the currently-submitted buffer in the triangleBufferRing to its vertex array. - if (m_maxTriangleVertexCount > 0) + if (desc.hasTriangleVertices) { bindVAO(m_interiorTrianglesVAO); glBindBuffer(GL_ARRAY_BUFFER, gl_buffer_id(triangleBufferRing())); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr); } - glViewport(0, 0, renderTarget()->width(), renderTarget()->height()); + auto renderTarget = static_cast<const PLSRenderTargetGL*>(desc.renderTarget); + glViewport(0, 0, renderTarget->width(), renderTarget->height()); #ifdef RIVE_DESKTOP_GL - if (m_extensions.ANGLE_polygon_mode && frameDescriptor().wireframe) + if (m_extensions.ANGLE_polygon_mode && desc.wireframe) { glPolygonModeANGLE(GL_FRONT_AND_BACK, GL_LINE_ANGLE); glLineWidth(2); } #endif - m_plsImpl->activatePixelLocalStorage(this, - renderTarget(), - desc.loadAction, - desc.needsClipBuffer); + m_plsImpl->activatePixelLocalStorage(this, desc); // Execute the DrawList. - for (const Draw& draw : m_drawList) + for (const Draw& draw : *desc.drawList) { if (draw.vertexOrInstanceCount == 0) { @@ -538,14 +538,14 @@ m_plsImpl->deactivatePixelLocalStorage(this); #ifdef RIVE_DESKTOP_GL - if (m_extensions.ANGLE_polygon_mode && frameDescriptor().wireframe) + if (m_extensions.ANGLE_polygon_mode && desc.wireframe) { glPolygonModeANGLE(GL_FRONT_AND_BACK, GL_FILL_ANGLE); } #endif } -void PLSRenderContextGL::bindProgram(GLuint programID) +void PLSRenderContextGLImpl::bindProgram(GLuint programID) { if (programID != m_boundProgramID) { @@ -554,7 +554,7 @@ } } -void PLSRenderContextGL::bindVAO(GLuint vao) +void PLSRenderContextGLImpl::bindVAO(GLuint vao) { if (vao != m_boundVAO) { @@ -563,7 +563,7 @@ } } -std::unique_ptr<PLSRenderContextGL> PLSRenderContextGL::Make() +std::unique_ptr<PLSRenderContextGLImpl> PLSRenderContextGLImpl::Make() { GLExtensions extensions{}; GLint extensionCount; @@ -665,30 +665,32 @@ if (extensions.EXT_shader_pixel_local_storage && (extensions.ARM_shader_framebuffer_fetch || extensions.EXT_shader_framebuffer_fetch)) { - return std::unique_ptr<PLSRenderContextGL>( - new PLSRenderContextGL(platformFeatures, extensions, MakePLSImplEXTNative(extensions))); + return std::unique_ptr<PLSRenderContextGLImpl>( + new PLSRenderContextGLImpl(platformFeatures, + extensions, + MakePLSImplEXTNative(extensions))); } if (extensions.EXT_shader_framebuffer_fetch) { - return std::unique_ptr<PLSRenderContextGL>( - new PLSRenderContextGL(platformFeatures, - extensions, - MakePLSImplFramebufferFetch(extensions))); + return std::unique_ptr<PLSRenderContextGLImpl>( + new PLSRenderContextGLImpl(platformFeatures, + extensions, + MakePLSImplFramebufferFetch(extensions))); } #endif #ifdef RIVE_DESKTOP_GL if (extensions.ANGLE_shader_pixel_local_storage_coherent) { - return std::unique_ptr<PLSRenderContextGL>( - new PLSRenderContextGL(platformFeatures, extensions, MakePLSImplWebGL())); + return std::unique_ptr<PLSRenderContextGLImpl>( + new PLSRenderContextGLImpl(platformFeatures, extensions, MakePLSImplWebGL())); } if (extensions.ARB_fragment_shader_interlock || extensions.INTEL_fragment_shader_ordering) { - return std::unique_ptr<PLSRenderContextGL>( - new PLSRenderContextGL(platformFeatures, extensions, MakePLSImplRWTexture())); + return std::unique_ptr<PLSRenderContextGLImpl>( + new PLSRenderContextGLImpl(platformFeatures, extensions, MakePLSImplRWTexture())); } #endif @@ -697,8 +699,8 @@ emscripten_webgl_get_current_context()) && emscripten_webgl_shader_pixel_local_storage_is_coherent()) { - return std::unique_ptr<PLSRenderContextGL>( - new PLSRenderContextGL(platformFeatures, extensions, MakePLSImplWebGL())); + return std::unique_ptr<PLSRenderContextGLImpl>( + new PLSRenderContextGLImpl(platformFeatures, extensions, MakePLSImplWebGL())); } #endif
diff --git a/renderer/gr_inner_fan_triangulator.hpp b/renderer/gr_inner_fan_triangulator.hpp index 32b90fb..1924485 100644 --- a/renderer/gr_inner_fan_triangulator.hpp +++ b/renderer/gr_inner_fan_triangulator.hpp
@@ -48,7 +48,7 @@ void setPathID(uint16_t pathID) { m_pathID = pathID; } uint16_t pathID() const { return m_pathID; } - size_t polysToTriangles(pls::BufferRing<pls::TriangleVertex>* bufferRing) const + size_t polysToTriangles(pls::WriteOnlyMappedMemory<pls::TriangleVertex>* bufferRing) const { if (m_polys == nullptr)
diff --git a/renderer/gr_triangulator.cpp b/renderer/gr_triangulator.cpp index e1ef9e9..59ceb33 100644 --- a/renderer/gr_triangulator.cpp +++ b/renderer/gr_triangulator.cpp
@@ -114,11 +114,11 @@ static inline void emit_vertex(Vertex* v, int winding, uint16_t pathID, - pls::BufferRing<pls::TriangleVertex>* bufferRing) + pls::WriteOnlyMappedMemory<pls::TriangleVertex>* mappedMemory) { // GrTriangulator and pls unfortunately have opposite winding senses. int16_t plsWeight = -winding; - bufferRing->emplace_back(v->fPoint, plsWeight, pathID); + mappedMemory->emplace_back(v->fPoint, plsWeight, pathID); } static void emit_triangle(Vertex* v0, @@ -126,22 +126,22 @@ Vertex* v2, int winding, uint16_t pathID, - pls::BufferRing<pls::TriangleVertex>* bufferRing) + pls::WriteOnlyMappedMemory<pls::TriangleVertex>* mappedMemory) { TESS_LOG("emit_triangle %g (%g, %g) %d\n", v0->fID, v0->fPoint.x, v0->fPoint.y, v0->fAlpha); TESS_LOG(" %g (%g, %g) %d\n", v1->fID, v1->fPoint.x, v1->fPoint.y, v1->fAlpha); TESS_LOG(" %g (%g, %g) %d\n", v2->fID, v2->fPoint.x, v2->fPoint.y, v2->fAlpha); #if TESSELLATOR_WIREFRAME - emit_vertex(v0, winding, pathID, bufferRing); - emit_vertex(v1, winding, pathID, bufferRing); - emit_vertex(v1, winding, pathID, bufferRing); - emit_vertex(v2, winding, pathID, bufferRing); - emit_vertex(v2, winding, pathID, bufferRing); - emit_vertex(v0, winding, pathID, bufferRing); + emit_vertex(v0, winding, pathID, mappedMemory); + emit_vertex(v1, winding, pathID, mappedMemory); + emit_vertex(v1, winding, pathID, mappedMemory); + emit_vertex(v2, winding, pathID, mappedMemory); + emit_vertex(v2, winding, pathID, mappedMemory); + emit_vertex(v0, winding, pathID, mappedMemory); #else - emit_vertex(v0, winding, pathID, bufferRing); - emit_vertex(v1, winding, pathID, bufferRing); - emit_vertex(v2, winding, pathID, bufferRing); + emit_vertex(v0, winding, pathID, mappedMemory); + emit_vertex(v1, winding, pathID, mappedMemory); + emit_vertex(v2, winding, pathID, mappedMemory); #endif } @@ -407,10 +407,11 @@ } } -void GrTriangulator::emitMonotonePoly(const MonotonePoly* monotonePoly, - uint16_t pathID, - bool reverseTriangles, - pls::BufferRing<pls::TriangleVertex>* bufferRing) const +void GrTriangulator::emitMonotonePoly( + const MonotonePoly* monotonePoly, + uint16_t pathID, + bool reverseTriangles, + pls::WriteOnlyMappedMemory<pls::TriangleVertex>* mappedMemory) const { assert(monotonePoly->fWinding != 0); Edge* e = monotonePoly->fFirstEdge; @@ -447,7 +448,7 @@ monotonePoly->fWinding, pathID, reverseTriangles, - bufferRing); + mappedMemory); } double ax = static_cast<double>(curr->fPoint.x) - prev->fPoint.x; double ay = static_cast<double>(curr->fPoint.y) - prev->fPoint.y; @@ -461,7 +462,7 @@ monotonePoly->fWinding, pathID, reverseTriangles, - bufferRing); + mappedMemory); v->fPrev->fNext = v->fNext; v->fNext->fPrev = v->fPrev; count--; @@ -481,19 +482,20 @@ } } -void GrTriangulator::emitTriangle(Vertex* prev, - Vertex* curr, - Vertex* next, - int winding, - uint16_t pathID, - bool reverseTriangles, - pls::BufferRing<pls::TriangleVertex>* bufferRing) const +void GrTriangulator::emitTriangle( + Vertex* prev, + Vertex* curr, + Vertex* next, + int winding, + uint16_t pathID, + bool reverseTriangles, + pls::WriteOnlyMappedMemory<pls::TriangleVertex>* mappedMemory) const { if (reverseTriangles) { std::swap(prev, next); } - return emit_triangle(prev, curr, next, winding, pathID, bufferRing); + return emit_triangle(prev, curr, next, winding, pathID, mappedMemory); } GrTriangulator::Poly::Poly(Vertex* v, int winding) : @@ -577,7 +579,7 @@ void GrTriangulator::emitPoly(const Poly* poly, uint16_t pathID, bool reverseTriangles, - pls::BufferRing<pls::TriangleVertex>* bufferRing) const + pls::WriteOnlyMappedMemory<pls::TriangleVertex>* mappedMemory) const { if (poly->fCount < 3) { @@ -586,7 +588,7 @@ TESS_LOG("emit() %d, size %d\n", poly->fID, poly->fCount); for (MonotonePoly* m = poly->fHead; m != nullptr; m = m->fNext) { - emitMonotonePoly(m, pathID, reverseTriangles, bufferRing); + emitMonotonePoly(m, pathID, reverseTriangles, mappedMemory); } } @@ -2084,17 +2086,18 @@ } // Stage 6: Triangulate the monotone polygons into a vertex buffer. -void GrTriangulator::polysToTriangles(const Poly* polys, - FillRule overrideFillType, - uint16_t pathID, - bool reverseTriangles, - pls::BufferRing<pls::TriangleVertex>* bufferRing) const +void GrTriangulator::polysToTriangles( + const Poly* polys, + FillRule overrideFillType, + uint16_t pathID, + bool reverseTriangles, + pls::WriteOnlyMappedMemory<pls::TriangleVertex>* mappedMemory) const { for (const Poly* poly = polys; poly; poly = poly->fNext) { if (apply_fill_type(overrideFillType, poly)) { - emitPoly(poly, pathID, reverseTriangles, bufferRing); + emitPoly(poly, pathID, reverseTriangles, mappedMemory); } } } @@ -2178,11 +2181,12 @@ return CountPoints(polys, fFillRule); } -size_t GrTriangulator::polysToTriangles(const Poly* polys, - uint64_t maxVertexCount, - uint16_t pathID, - bool reverseTriangles, - pls::BufferRing<pls::TriangleVertex>* bufferRing) const +size_t GrTriangulator::polysToTriangles( + const Poly* polys, + uint64_t maxVertexCount, + uint16_t pathID, + bool reverseTriangles, + pls::WriteOnlyMappedMemory<pls::TriangleVertex>* mappedMemory) const { if (0 == maxVertexCount || maxVertexCount > std::numeric_limits<int32_t>::max()) { @@ -2199,9 +2203,9 @@ } #endif - size_t start = bufferRing->bytesWritten(); - polysToTriangles(polys, fFillRule, pathID, reverseTriangles, bufferRing); - size_t actualCount = (bufferRing->bytesWritten() - start) / vertexStride; + size_t start = mappedMemory->bytesWritten(); + polysToTriangles(polys, fFillRule, pathID, reverseTriangles, mappedMemory); + size_t actualCount = (mappedMemory->bytesWritten() - start) / vertexStride; assert(actualCount <= maxVertexCount * vertexStride); return actualCount; }
diff --git a/renderer/gr_triangulator.hpp b/renderer/gr_triangulator.hpp index 45a4ea3..694cfe6 100644 --- a/renderer/gr_triangulator.hpp +++ b/renderer/gr_triangulator.hpp
@@ -18,7 +18,6 @@ #include "rive/math/vec2d.hpp" #include "rive/math/aabb.hpp" #include "rive/pls/pls.hpp" -#include "rive/pls/buffer_ring.hpp" #include "rive/pls/trivial_block_allocator.hpp" namespace rive @@ -99,7 +98,7 @@ FillRule overrideFillRule, uint16_t pathID, bool reverseTriangles, - pls::BufferRing<pls::TriangleVertex>*) const; + pls::WriteOnlyMappedMemory<pls::TriangleVertex>*) const; // The vertex sorting in step (3) is a merge sort, since it plays well with the linked list // of vertices (and the necessity of inserting new vertices on intersection). @@ -148,18 +147,18 @@ void emitMonotonePoly(const MonotonePoly*, uint16_t pathID, bool reverseTriangles, - pls::BufferRing<pls::TriangleVertex>*) const; + pls::WriteOnlyMappedMemory<pls::TriangleVertex>*) const; void emitTriangle(Vertex* prev, Vertex* curr, Vertex* next, int winding, uint16_t pathID, bool reverseTriangles, - pls::BufferRing<pls::TriangleVertex>*) const; + pls::WriteOnlyMappedMemory<pls::TriangleVertex>*) const; void emitPoly(const Poly*, uint16_t pathID, bool reverseTriangles, - pls::BufferRing<pls::TriangleVertex>*) const; + pls::WriteOnlyMappedMemory<pls::TriangleVertex>*) const; Poly* makePoly(Poly** head, Vertex* v, int winding) const; void appendPointToContour(const Vec2D& p, VertexList* contour) const; @@ -247,7 +246,7 @@ uint64_t maxVertexCount, uint16_t pathID, bool reverseTriangles, - pls::BufferRing<pls::TriangleVertex>*) const; + pls::WriteOnlyMappedMemory<pls::TriangleVertex>*) const; AABB fPathBounds; FillRule fFillRule;
diff --git a/renderer/metal/pls_render_context_metal.mm b/renderer/metal/pls_render_context_metal.mm index cbf5479..240d7ea 100644 --- a/renderer/metal/pls_render_context_metal.mm +++ b/renderer/metal/pls_render_context_metal.mm
@@ -507,13 +507,6 @@ thisFlushLock.unlock(); }]; - if (desc.flushType == FlushType::intermediate) - { - // The frame isn't complete yet. The caller will begin preparing a new flush immediately - // after this method returns, so lock buffers for the next flush now. - lockNextBufferRingIndex(); - } - [commandBuffer commit]; } } // namespace rive::pls
diff --git a/renderer/pls_render_context.cpp b/renderer/pls_render_context.cpp index c7b6456..f93675e 100644 --- a/renderer/pls_render_context.cpp +++ b/renderer/pls_render_context.cpp
@@ -8,6 +8,7 @@ #include "pls_path.hpp" #include "pls_paint.hpp" #include "rive/math/math_types.hpp" +#include "rive/pls/pls_render_context_impl.hpp" #include <string_view> @@ -29,9 +30,9 @@ constexpr size_t kMinTessTextureHeight = 32; constexpr size_t kMaxTessTextureHeight = 2048; // GL_MAX_TEXTURE_SIZE spec minimum. -constexpr size_t kMaxTessellationVertices = kMaxTessTextureHeight * kTessTextureWidth; +constexpr size_t kMaxTessellationVertexCount = kMaxTessTextureHeight * kTessTextureWidth; -uint32_t PLSRenderContext::ShaderFeatures::getPreprocessorDefines(SourceType sourceType) const +uint32_t ShaderFeatures::getPreprocessorDefines(SourceType sourceType) const { uint32_t defines = 0; if (programFeatures.blendTier != BlendTier::srcOver) @@ -56,7 +57,7 @@ return defines; } -PLSRenderContext::BlendTier PLSRenderContext::BlendTierForBlendMode(PLSBlendMode blendMode) +BlendTier PLSRenderContext::BlendTierForBlendMode(PLSBlendMode blendMode) { switch (blendMode) { @@ -120,9 +121,10 @@ return x ^ y; } -PLSRenderContext::PLSRenderContext(const PlatformFeatures& platformFeatures) : - m_platformFeatures(platformFeatures), - m_maxPathID(MaxPathID(m_platformFeatures.pathIDGranularity)) +PLSRenderContext::PLSRenderContext(std::unique_ptr<PLSRenderContextImpl> impl) : + m_platformFeatures(impl->platformFeatures()), + m_maxPathID(MaxPathID(m_platformFeatures.pathIDGranularity)), + m_impl(std::move(impl)) {} PLSRenderContext::~PLSRenderContext() @@ -218,13 +220,6 @@ #define COUNT_RESOURCE_SIZE(SIZE_IN_BYTES) #endif - // One-time allocation of the uniform buffer ring. - if (m_uniformBuffer.impl() == nullptr) - { - m_uniformBuffer.reset(makeUniformBufferRing(sizeof(FlushUniforms))); - } - COUNT_RESOURCE_SIZE(m_uniformBuffer.totalSizeInBytes()); - // Path data texture ring. constexpr size_t kMinPathIDCount = kPathTextureWidthInItems * 32; // 32 texels tall. size_t targetMaxPathID = resource_texture_height<kPathTextureWidthInItems>(targets.maxPathID) * @@ -236,20 +231,15 @@ resource_texture_height<kPathTextureWidthInItems>(m_currentResourceLimits.maxPathID); if (shouldReallocate(targetPathTextureHeight, currentPathTextureHeight)) { - assert(!m_pathBuffer.mapped()); - m_pathBuffer.reset(makeTexelBufferRing(TexelBufferRing::Format::rgba32ui, - kPathTextureWidthInItems, - targetPathTextureHeight, - kPathTexelsPerItem, - kPathTextureIdx, - TexelBufferRing::Filter::nearest)); + assert(!m_pathData); + m_impl->resizePathTexture(kPathTextureWidthInTexels, targetPathTextureHeight); LOG_CHANGED_SIZE("path texture height", currentPathTextureHeight, targetPathTextureHeight, - m_pathBuffer.totalSizeInBytes()); + m_pathBuffer->totalSizeInBytes()); m_currentResourceLimits.maxPathID = targetMaxPathID; } - COUNT_RESOURCE_SIZE(m_pathBuffer.totalSizeInBytes()); + COUNT_RESOURCE_SIZE(m_pathBuffer->totalSizeInBytes()); // Contour data texture ring. constexpr size_t kMinContourIDCount = kContourTextureWidthInItems * 32; // 32 texels tall. @@ -263,109 +253,101 @@ resource_texture_height<kContourTextureWidthInItems>(m_currentResourceLimits.maxContourID); if (shouldReallocate(targetContourTextureHeight, currentContourTextureHeight)) { - assert(!m_contourBuffer.mapped()); - m_contourBuffer.reset(makeTexelBufferRing(TexelBufferRing::Format::rgba32ui, - kContourTextureWidthInItems, - targetContourTextureHeight, - kContourTexelsPerItem, - pls::kContourTextureIdx, - TexelBufferRing::Filter::nearest)); + assert(!m_contourData); + m_impl->resizeContourTexture(kContourTextureWidthInTexels, targetContourTextureHeight); LOG_CHANGED_SIZE("contour texture height", currentContourTextureHeight, targetContourTextureHeight, - m_contourBuffer.totalSizeInBytes()); + m_contourBuffer->totalSizeInBytes()); m_currentResourceLimits.maxContourID = targetMaxContourID; } - COUNT_RESOURCE_SIZE(m_contourBuffer.totalSizeInBytes()); + COUNT_RESOURCE_SIZE(m_contourBuffer->totalSizeInBytes()); // Simple gradient color ramp pixel unpack buffer ring. - size_t targetSimpleGradientRows = + size_t targetSimpleGradientRowCount = resource_texture_height<kGradTextureWidthInSimpleRamps>(targets.maxSimpleGradients); - targetSimpleGradientRows = - std::clamp(targetSimpleGradientRows, kMinSimpleColorRampRows, kMaxSimpleColorRampRows); + targetSimpleGradientRowCount = + std::clamp(targetSimpleGradientRowCount, kMinSimpleColorRampRows, kMaxSimpleColorRampRows); assert(m_currentResourceLimits.maxSimpleGradients % kGradTextureWidthInSimpleRamps == 0); - assert(m_reservedGradTextureRowsForSimpleRamps == + assert(m_reservedSimpleGradientRowCount == resource_texture_height<kGradTextureWidthInSimpleRamps>( m_currentResourceLimits.maxSimpleGradients)); - if (shouldReallocate(targetSimpleGradientRows, m_reservedGradTextureRowsForSimpleRamps)) + if (shouldReallocate(targetSimpleGradientRowCount, m_reservedSimpleGradientRowCount)) { - assert(!m_simpleColorRampsBuffer.mapped()); - m_simpleColorRampsBuffer.reset( - makePixelUnpackBufferRing(targetSimpleGradientRows * kGradTextureWidthInSimpleRamps, - sizeof(TwoTexelRamp))); + assert(!m_simpleColorRampsData); + m_impl->resizeSimpleColorRampsBuffer(targetSimpleGradientRowCount * + kGradTextureWidthInSimpleRamps * sizeof(TwoTexelRamp)); LOG_CHANGED_SIZE("maxSimpleGradients", - m_reservedGradTextureRowsForSimpleRamps * kGradTextureWidthInSimpleRamps, - targetSimpleGradientRows * kGradTextureWidthInSimpleRamps, - m_simpleColorRampsBuffer.totalSizeInBytes()); + m_reservedSimpleGradientRowCount * kGradTextureWidthInSimpleRamps, + targetSimpleGradientRowCount * kGradTextureWidthInSimpleRamps, + m_simpleColorRampsBuffer->totalSizeInBytes()); m_currentResourceLimits.maxSimpleGradients = - targetSimpleGradientRows * kGradTextureWidthInSimpleRamps; - m_reservedGradTextureRowsForSimpleRamps = targetSimpleGradientRows; + targetSimpleGradientRowCount * kGradTextureWidthInSimpleRamps; + m_reservedSimpleGradientRowCount = targetSimpleGradientRowCount; } - COUNT_RESOURCE_SIZE(m_simpleColorRampsBuffer.totalSizeInBytes()); + COUNT_RESOURCE_SIZE(m_simpleColorRampsBuffer->totalSizeInBytes()); // Instance buffer ring for rendering complex gradients. constexpr size_t kMinComplexGradientSpans = kMinComplexGradients * 32; constexpr size_t kMaxComplexGradientSpans = kMaxComplexGradients * 64; - size_t targetComplexGradientSpans = std::clamp(targets.maxComplexGradientSpans, - kMinComplexGradientSpans, - kMaxComplexGradientSpans); - if (shouldReallocate(targetComplexGradientSpans, + size_t targetComplexGradientSpanCount = std::clamp(targets.maxComplexGradientSpans, + kMinComplexGradientSpans, + kMaxComplexGradientSpans); + if (shouldReallocate(targetComplexGradientSpanCount, m_currentResourceLimits.maxComplexGradientSpans)) { - assert(!m_gradSpanBuffer.mapped()); - m_gradSpanBuffer.reset( - makeVertexBufferRing(targetComplexGradientSpans, sizeof(GradientSpan))); + assert(!m_gradSpanData); + m_impl->resizeGradSpanBuffer(targetComplexGradientSpanCount * sizeof(GradientSpan)); LOG_CHANGED_SIZE("maxComplexGradientSpans", m_currentResourceLimits.maxComplexGradientSpans, - targetComplexGradientSpans, - m_gradSpanBuffer.totalSizeInBytes()); - m_currentResourceLimits.maxComplexGradientSpans = targetComplexGradientSpans; + targetComplexGradientSpanCount, + m_gradSpanBuffer->totalSizeInBytes()); + m_currentResourceLimits.maxComplexGradientSpans = targetComplexGradientSpanCount; } - COUNT_RESOURCE_SIZE(m_gradSpanBuffer.totalSizeInBytes()); + COUNT_RESOURCE_SIZE(m_gradSpanBuffer->totalSizeInBytes()); // Instance buffer ring for rendering path tessellation vertices. constexpr size_t kMinTessellationSpans = kMinTessTextureHeight * kTessTextureWidth / 4; const size_t maxTessellationSpans = kMaxTessTextureHeight * kTessTextureWidth / 8; // ~100MiB - size_t targetTessellationSpans = + size_t targetTessellationSpanCount = std::clamp(targets.maxTessellationSpans, kMinTessellationSpans, maxTessellationSpans); - if (shouldReallocate(targetTessellationSpans, m_currentResourceLimits.maxTessellationSpans)) + if (shouldReallocate(targetTessellationSpanCount, m_currentResourceLimits.maxTessellationSpans)) { - assert(!m_tessSpanBuffer.mapped()); - m_tessSpanBuffer.reset( - makeVertexBufferRing(targetTessellationSpans, sizeof(TessVertexSpan))); + assert(!m_tessSpanData); + m_impl->resizeTessVertexSpanBuffer(targetTessellationSpanCount * sizeof(TessVertexSpan)); LOG_CHANGED_SIZE("maxTessellationSpans", m_currentResourceLimits.maxTessellationSpans, - targetTessellationSpans, - m_tessSpanBuffer.totalSizeInBytes()); - m_currentResourceLimits.maxTessellationSpans = targetTessellationSpans; + targetTessellationSpanCount, + m_tessSpanBuffer->totalSizeInBytes()); + m_currentResourceLimits.maxTessellationSpans = targetTessellationSpanCount; } - COUNT_RESOURCE_SIZE(m_tessSpanBuffer.totalSizeInBytes()); + COUNT_RESOURCE_SIZE(m_tessSpanBuffer->totalSizeInBytes()); // Instance buffer ring for literal triangles fed directly by the CPU. - constexpr size_t kMinTriangleVertices = 3072 * 3; // 324 KiB + constexpr size_t kMinTriangleVertexCount = 3072 * 3; // 324 KiB // Triangle vertices don't have a maximum limit; we let the other components be the limiting // factor and allocate whatever buffer size we need at flush time. - size_t targetTriangleVertices = - std::max(targets.triangleVertexBufferSize, kMinTriangleVertices); - if (shouldReallocate(targetTriangleVertices, m_currentResourceLimits.triangleVertexBufferSize)) + size_t targetTriangleVertexCount = + std::max(targets.triangleVertexBufferCount, kMinTriangleVertexCount); + if (shouldReallocate(targetTriangleVertexCount, + m_currentResourceLimits.triangleVertexBufferCount)) { - assert(!m_triangleBuffer.mapped()); - m_triangleBuffer.reset( - makeVertexBufferRing(targetTriangleVertices, sizeof(TriangleVertex))); - LOG_CHANGED_SIZE("triangleVertexBufferSize", - m_currentResourceLimits.triangleVertexBufferSize, - targetTriangleVertices, - m_triangleBuffer.totalSizeInBytes()); - m_currentResourceLimits.triangleVertexBufferSize = targetTriangleVertices; + assert(!m_triangleVertexData); + m_impl->resizeTriangleVertexBuffer(targetTriangleVertexCount * sizeof(TriangleVertex)); + LOG_CHANGED_SIZE("triangleVertexBufferCount", + m_currentResourceLimits.triangleVertexBufferCount, + targetTriangleVertexCount, + m_triangleBuffer->totalSizeInBytes()); + m_currentResourceLimits.triangleVertexBufferCount = targetTriangleVertexCount; } - COUNT_RESOURCE_SIZE(m_triangleBuffer.totalSizeInBytes()); + COUNT_RESOURCE_SIZE(m_triangleBuffer->totalSizeInBytes()); // Gradient color ramp texture. size_t targetGradTextureHeight = std::clamp(targets.gradientTextureHeight, kMinGradTextureHeight, kMaxGradTextureHeight); if (shouldReallocate(targetGradTextureHeight, m_currentResourceLimits.gradientTextureHeight)) { - allocateGradientTexture(targetGradTextureHeight); + m_impl->resizeGradientTexture(targetGradTextureHeight); LOG_CHANGED_SIZE("gradientTextureHeight", m_currentResourceLimits.gradientTextureHeight, targetGradTextureHeight, @@ -381,7 +363,7 @@ if (shouldReallocate(targetTessTextureHeight, m_currentResourceLimits.tessellationTextureHeight)) { - allocateTessellationTexture(targetTessTextureHeight); + m_impl->resizeTessellationTexture(targetTessTextureHeight); LOG_CHANGED_SIZE("tessellationTextureHeight", m_currentResourceLimits.tessellationTextureHeight, targetTessTextureHeight, @@ -402,7 +384,7 @@ growExceededGPUResources(m_maxRecentResourceUsage.resetFlushTimeLimits(), kGPUResourcePadding); m_frameDescriptor = std::move(frameDescriptor); m_isFirstFlushOfFrame = true; - onBeginFrame(); + m_impl->prepareToMapBuffers(); RIVE_DEBUG_CODE(m_didBeginFrame = true); } @@ -461,9 +443,9 @@ newLimits.maxTessellationSpans = maxTessellationSpans; needsRealloc = true; } - assert(!m_pathBuffer.mapped()); - assert(!m_contourBuffer.mapped()); - assert(!m_tessSpanBuffer.mapped()); + assert(!m_pathData); + assert(!m_contourData); + assert(!m_tessSpanData); if (needsRealloc) { // The very first draw of the flush overwhelmed our GPU resources. Since we haven't @@ -472,22 +454,28 @@ } } - m_pathBuffer.ensureMapped(); - m_contourBuffer.ensureMapped(); - m_tessSpanBuffer.ensureMapped(); + if (!m_pathData) + { + assert(!m_contourData); + assert(!m_tessSpanData); + m_pathData.reset(m_impl->mapPathTexture(), m_currentResourceLimits.maxPathID); + m_contourData.reset(m_impl->mapContourTexture(), m_currentResourceLimits.maxContourID); + m_tessSpanData.reset(m_impl->mapTessVertexSpanBuffer(), + m_currentResourceLimits.maxTessellationSpans); + } // Does the path fit in our current buffers? if (m_currentPathID + pathCount <= m_currentResourceLimits.maxPathID && m_currentContourID + contourCount <= m_currentResourceLimits.maxContourID && - m_tessSpanBuffer.hasRoomFor(maxTessellationSpans) && - m_tessVertexCount + maxTessVertexCountWithInternalPadding <= kMaxTessellationVertices) + m_tessSpanData.hasRoomFor(maxTessellationSpans) && + m_tessVertexCount + maxTessVertexCountWithInternalPadding <= kMaxTessellationVertexCount) { - assert(m_pathBuffer.hasRoomFor(pathCount)); - assert(m_contourBuffer.hasRoomFor(contourCount)); + assert(m_pathData.hasRoomFor(pathCount)); + assert(m_contourData.hasRoomFor(contourCount)); RIVE_DEBUG_CODE(m_expectedTessVertexCountAtNextReserve = m_tessVertexCount + tessVertexCounter.totalVertexCountIncludingReflectionsAndPadding()); - assert(m_expectedTessVertexCountAtNextReserve <= kMaxTessellationVertices); + assert(m_expectedTessVertexCountAtNextReserve <= kMaxTessellationVertexCount); return true; } @@ -540,8 +528,12 @@ return false; } rampTexelsIdx = m_simpleGradients.size() * 2; - m_simpleColorRampsBuffer.ensureMapped(); - m_simpleColorRampsBuffer.set_back(colors); + if (!m_simpleColorRampsData) + { + m_simpleColorRampsData.reset(m_impl->mapSimpleColorRampsBuffer(), + m_currentResourceLimits.maxSimpleGradients); + } + m_simpleColorRampsData.set_back(colors); m_simpleGradients.insert({simpleKey, rampTexelsIdx}); } row = rampTexelsIdx / kGradTextureWidth; @@ -572,7 +564,7 @@ // the gradient span buffer hasn't been mapped yet, we have a unique opportunity to grow // it if needed. size_t spanCount = stopCount + 1; - if (!m_gradSpanBuffer.mapped()) + if (!m_gradSpanData) { if (spanCount > m_currentResourceLimits.maxComplexGradientSpans) { @@ -583,10 +575,11 @@ newLimits.maxComplexGradientSpans = spanCount; growExceededGPUResources(newLimits, kGPUResourceIntermediateGrowthFactor); } - m_gradSpanBuffer.ensureMapped(); + m_gradSpanData.reset(m_impl->mapGradSpanBuffer(), + m_currentResourceLimits.maxComplexGradientSpans); } - if (!m_gradSpanBuffer.hasRoomFor(spanCount)) + if (!m_gradSpanData.hasRoomFor(spanCount)) { // We ran out of instances for rendering complex color ramps. The caller needs to // flush and try again. @@ -596,7 +589,7 @@ // Push "GradientSpan" instances that will render each section of the color ramp. ColorInt lastColor = colors[0]; uint32_t lastXFixed = 0; - // The viewport will start at m_reservedGradTextureRowsForSimpleRamps when rendering + // The viewport will start at m_reservedSimpleGradientRowCount when rendering // color ramps. uint32_t y = static_cast<uint32_t>(m_complexGradients.size()); // "stop * w + .5" converts a stop position to an x-coordinate in the gradient texture. @@ -609,13 +602,13 @@ float x = stops[i] * w + .5f; uint32_t xFixed = static_cast<uint32_t>(x * (65536.f / kGradTextureWidth)); assert(lastXFixed <= xFixed && xFixed < 65536); - m_gradSpanBuffer.set_back(lastXFixed, xFixed, y, lastColor, colors[i]); + m_gradSpanData.set_back(lastXFixed, xFixed, y, lastColor, colors[i]); lastColor = colors[i]; lastXFixed = xFixed; } - m_gradSpanBuffer.set_back(lastXFixed, 65535u, y, lastColor, lastColor); + m_gradSpanData.set_back(lastXFixed, 65535u, y, lastColor, lastColor); - row = m_reservedGradTextureRowsForSimpleRamps + m_complexGradients.size(); + row = m_reservedSimpleGradientRowCount + m_complexGradients.size(); m_complexGradients.emplace(std::move(key), row); } } @@ -640,11 +633,11 @@ m_currentPathIsStroked = strokeRadius != 0; m_currentPathNeedsMirroredContours = !m_currentPathIsStroked; - m_pathBuffer.set_back(matrix, strokeRadius, fillRule, paintType, clipID, blendMode, paintData); + m_pathData.set_back(matrix, strokeRadius, fillRule, paintType, clipID, blendMode, paintData); ++m_currentPathID; assert(0 < m_currentPathID && m_currentPathID <= m_maxPathID); - assert(m_currentPathID == m_pathBuffer.bytesWritten() / sizeof(PathData)); + assert(m_currentPathID == m_pathData.bytesWritten() / sizeof(PathData)); auto drawType = patchType == PatchType::midpointFan ? DrawType::midpointFanPatches : DrawType::outerCurvePatches; @@ -686,13 +679,13 @@ RIVE_DEBUG_CODE(m_expectedTessVertexCountAtEndOfPath = m_tessVertexCount + tessVertexCountWithoutPadding); assert(m_expectedTessVertexCountAtEndOfPath <= m_expectedTessVertexCountAtNextReserve); - assert(m_expectedTessVertexCountAtEndOfPath <= kMaxTessellationVertices); + assert(m_expectedTessVertexCountAtEndOfPath <= kMaxTessellationVertexCount); } void PLSRenderContext::pushContour(Vec2D midpoint, bool closed, uint32_t paddingVertexCount) { assert(m_didBeginFrame); - assert(!m_pathBuffer.empty()); + assert(m_pathData.bytesWritten() > 0); assert(m_currentPathIsStroked || closed); assert(m_currentPathID != 0); // pathID can't be zero. @@ -700,12 +693,10 @@ { midpoint.x = closed ? 1 : 0; } - m_contourBuffer.emplace_back(midpoint, - m_currentPathID, - static_cast<uint32_t>(m_tessVertexCount)); + m_contourData.emplace_back(midpoint, m_currentPathID, static_cast<uint32_t>(m_tessVertexCount)); ++m_currentContourID; assert(0 < m_currentContourID && m_currentContourID <= kMaxContourID); - assert(m_currentContourID == m_contourBuffer.bytesWritten() / sizeof(ContourData)); + assert(m_currentContourID == m_contourData.bytesWritten() / sizeof(ContourData)); // The first curve of the contour will be pre-padded with 'paddingVertexCount' tessellation // vertices, colocated at T=0. The caller must use this argument align the end of the contour on @@ -765,7 +756,7 @@ constexpr static uint32_t kInvalidContourID = 0; assert(m_tessVertexCount == m_expectedTessVertexCountAtEndOfPath); RIVE_DEBUG_CODE(m_expectedTessVertexCountAtEndOfPath = m_tessVertexCount + count;) - assert(m_expectedTessVertexCountAtEndOfPath <= kMaxTessellationVertices); + assert(m_expectedTessVertexCountAtEndOfPath <= kMaxTessellationVertexCount); pushTessellationSpans(kEmptyCubic, {0, 0}, count, 0, 0, 1, kInvalidContourID); assert(m_tessVertexCount == m_expectedTessVertexCountAtEndOfPath); } @@ -783,15 +774,15 @@ int32_t x1 = x0 + totalVertexCount; for (;;) { - m_tessSpanBuffer.set_back(pts, - joinTangent, - static_cast<float>(y), - x0, - x1, - parametricSegmentCount, - polarSegmentCount, - joinSegmentCount, - contourIDWithFlags); + m_tessSpanData.set_back(pts, + joinTangent, + static_cast<float>(y), + x0, + x1, + parametricSegmentCount, + polarSegmentCount, + joinSegmentCount, + contourIDWithFlags); if (x1 > static_cast<int32_t>(kTessTextureWidth)) { // The span was too long to fit on the current line. Wrap and draw it again, this @@ -829,18 +820,18 @@ for (;;) { - m_tessSpanBuffer.set_back(pts, - joinTangent, - static_cast<float>(y), - x0, - x1, - static_cast<float>(reflectionY), - reflectionX0, - reflectionX1, - parametricSegmentCount, - polarSegmentCount, - joinSegmentCount, - contourIDWithFlags); + m_tessSpanData.set_back(pts, + joinTangent, + static_cast<float>(y), + x0, + x1, + static_cast<float>(reflectionY), + reflectionX0, + reflectionX1, + parametricSegmentCount, + polarSegmentCount, + joinSegmentCount, + contourIDWithFlags); if (x1 > static_cast<int32_t>(kTessTextureWidth) || reflectionX1 < 0) { // Either the span or its reflection was too long to fit on the current line. Wrap and @@ -891,7 +882,6 @@ if (m_drawList.empty() || m_drawList.tail().drawType != drawType) { m_drawList.emplace_back(this, drawType, baseVertex); - ++m_drawListCount; } ShaderFeatures* shaderFeatures = &m_drawList.tail().shaderFeatures; if (blendMode > PLSBlendMode::srcOver) @@ -934,7 +924,7 @@ // The final vertex of the final patch of each contour crosses over into the next contour. (This // is how we wrap around back to the beginning.) Therefore, the final contour of the flush needs // an out-of-contour vertex to cross into as well, so we emit a padding vertex here at the end. - if (!m_tessSpanBuffer.empty()) + if (m_tessSpanData.bytesWritten() > 0) { pushPaddingVertices(1); } @@ -943,14 +933,12 @@ // when we know exactly how large they need to be. GPUResourceLimits newLimitsForFlushTimeResources{}; bool needsFlushTimeRealloc = false; - assert(m_triangleBuffer.capacity() == m_currentResourceLimits.triangleVertexBufferSize); - if (m_currentResourceLimits.triangleVertexBufferSize < m_maxTriangleVertexCount) + if (m_currentResourceLimits.triangleVertexBufferCount < m_maxTriangleVertexCount) { - newLimitsForFlushTimeResources.triangleVertexBufferSize = m_maxTriangleVertexCount; + newLimitsForFlushTimeResources.triangleVertexBufferCount = m_maxTriangleVertexCount; needsFlushTimeRealloc = true; } - size_t requiredGradTextureHeight = - m_reservedGradTextureRowsForSimpleRamps + m_complexGradients.size(); + size_t requiredGradTextureHeight = m_reservedSimpleGradientRowCount + m_complexGradients.size(); if (m_currentResourceLimits.gradientTextureHeight < requiredGradTextureHeight) { newLimitsForFlushTimeResources.gradientTextureHeight = requiredGradTextureHeight; @@ -969,11 +957,13 @@ } if (m_maxTriangleVertexCount > 0) { - m_triangleBuffer.ensureMapped(); - assert(m_triangleBuffer.hasRoomFor(m_maxTriangleVertexCount)); + assert(!m_triangleVertexData); + m_triangleVertexData.reset(m_impl->mapTriangleVertexBuffer(), + m_currentResourceLimits.triangleVertexBufferCount); + assert(m_triangleVertexData.hasRoomFor(m_maxTriangleVertexCount)); } assert(m_complexGradients.size() <= - m_currentResourceLimits.gradientTextureHeight - m_reservedGradTextureRowsForSimpleRamps); + m_currentResourceLimits.gradientTextureHeight - m_reservedSimpleGradientRowCount); assert(m_tessVertexCount <= m_currentResourceLimits.tessellationTextureHeight * kTessTextureWidth); @@ -995,7 +985,7 @@ size_t actualVertexCount = maxVertexCount; if (maxVertexCount > 0) { - actualVertexCount = draw.triangulator->polysToTriangles(&m_triangleBuffer); + actualVertexCount = draw.triangulator->polysToTriangles(&m_triangleVertexData); } assert(actualVertexCount <= maxVertexCount); draw.baseVertexOrInstance = writtenTriangleVertexCount; @@ -1007,21 +997,51 @@ needsClipBuffer = needsClipBuffer || draw.shaderFeatures.programFeatures.enablePathClipping; RIVE_DEBUG_CODE(++drawIdx;) } - assert(drawIdx == m_drawListCount); + assert(drawIdx == m_drawList.count()); // Determine how much to draw. - size_t simpleColorRampCount = m_simpleColorRampsBuffer.bytesWritten() / sizeof(TwoTexelRamp); - size_t gradSpanCount = m_gradSpanBuffer.bytesWritten() / sizeof(GradientSpan); - size_t tessVertexSpanCount = m_tessSpanBuffer.bytesWritten() / sizeof(TessVertexSpan); + size_t simpleColorRampCount = m_simpleColorRampsData.bytesWritten() / sizeof(TwoTexelRamp); + size_t gradSpanCount = m_gradSpanData.bytesWritten() / sizeof(GradientSpan); + size_t tessVertexSpanCount = m_tessSpanData.bytesWritten() / sizeof(TessVertexSpan); size_t tessDataHeight = resource_texture_height<kTessTextureWidth>(m_tessVertexCount); - // Upload all non-empty buffers before flushing. - m_pathBuffer.submit(); - m_contourBuffer.submit(); - m_simpleColorRampsBuffer.submit(); - m_gradSpanBuffer.submit(); - m_tessSpanBuffer.submit(); - m_triangleBuffer.submit(); + // Unmap all non-empty buffers before flushing. + if (m_pathData) + { + size_t texelsWritten = m_pathData.bytesWritten() / (sizeof(uint32_t) * 4); + size_t widthWritten = std::min(texelsWritten, kPathTextureWidthInTexels); + size_t heightWritten = resource_texture_height<kPathTextureWidthInTexels>(texelsWritten); + m_impl->unmapPathTexture(widthWritten, heightWritten); + m_pathData.reset(); + } + if (m_contourData) + { + size_t texelsWritten = m_contourData.bytesWritten() / (sizeof(uint32_t) * 4); + size_t widthWritten = std::min(texelsWritten, kContourTextureWidthInTexels); + size_t heightWritten = resource_texture_height<kContourTextureWidthInTexels>(texelsWritten); + m_impl->unmapContourTexture(widthWritten, heightWritten); + m_contourData.reset(); + } + if (m_simpleColorRampsData) + { + m_impl->unmapSimpleColorRampsBuffer(m_simpleColorRampsData.bytesWritten()); + m_simpleColorRampsData.reset(); + } + if (m_gradSpanData) + { + m_impl->unmapGradSpanBuffer(m_gradSpanData.bytesWritten()); + m_gradSpanData.reset(); + } + if (m_tessSpanData) + { + m_impl->unmapTessVertexSpanBuffer(m_tessSpanData.bytesWritten()); + m_tessSpanData.reset(); + } + if (m_triangleVertexData) + { + m_impl->unmapTriangleVertexBuffer(m_triangleVertexData.bytesWritten()); + m_triangleVertexData.reset(); + } // Update the uniform buffer for drawing if needed. FlushUniforms uniformData(m_complexGradients.size(), @@ -1032,33 +1052,35 @@ m_platformFeatures); if (!bits_equal(&m_cachedUniformData, &uniformData)) { - m_uniformBuffer.ensureMapped(); - m_uniformBuffer.emplace_back(uniformData); - m_uniformBuffer.submit(); + m_impl->updateFlushUniforms(&uniformData); m_cachedUniformData = uniformData; } FlushDescriptor flushDesc; - flushDesc.flushType = flushType; + flushDesc.renderTarget = frameDescriptor().renderTarget.get(); flushDesc.loadAction = m_isFirstFlushOfFrame ? frameDescriptor().loadAction : LoadAction::preserveRenderTarget; + flushDesc.clearColor = frameDescriptor().clearColor; flushDesc.complexGradSpanCount = gradSpanCount; flushDesc.tessVertexSpanCount = tessVertexSpanCount; flushDesc.simpleGradTexelsWidth = std::min(simpleColorRampCount * 2, kGradTextureWidth); flushDesc.simpleGradTexelsHeight = resource_texture_height<kGradTextureWidthInSimpleRamps>(simpleColorRampCount); - flushDesc.complexGradRowsTop = m_reservedGradTextureRowsForSimpleRamps; + flushDesc.complexGradRowsTop = m_reservedSimpleGradientRowCount; flushDesc.complexGradRowsHeight = m_complexGradients.size(); flushDesc.tessDataHeight = tessDataHeight; flushDesc.needsClipBuffer = needsClipBuffer; - onFlush(flushDesc); + flushDesc.hasTriangleVertices = m_maxTriangleVertexCount > 0; + flushDesc.wireframe = frameDescriptor().wireframe; + flushDesc.drawList = &m_drawList; + m_impl->flush(flushDesc); m_currentFrameResourceUsage.maxPathID += m_currentPathID; m_currentFrameResourceUsage.maxContourID += m_currentContourID; m_currentFrameResourceUsage.maxSimpleGradients += m_simpleGradients.size(); m_currentFrameResourceUsage.maxComplexGradientSpans += gradSpanCount; m_currentFrameResourceUsage.maxTessellationSpans += tessVertexSpanCount; - m_currentFrameResourceUsage.triangleVertexBufferSize += m_maxTriangleVertexCount; + m_currentFrameResourceUsage.triangleVertexBufferCount += m_maxTriangleVertexCount; m_currentFrameResourceUsage.gradientTextureHeight += resource_texture_height<kGradTextureWidthInSimpleRamps>(m_simpleGradients.size()) + m_complexGradients.size(); @@ -1105,9 +1127,15 @@ m_isFirstFlushOfFrame = false; m_drawList.reset(); - m_drawListCount = 0; // Delete all objects that were allocted for this flush using the TrivialBlockAllocator. m_trivialPerFlushAllocator.reset(); + + if (flushType == FlushType::intermediate) + { + // The frame isn't complete yet. The caller will begin preparing a new flush immediately + // after this method returns, so lock buffers for the next flush now. + m_impl->prepareToMapBuffers(); + } } } // namespace rive::pls
diff --git a/renderer/pls_render_context_buffer_ring_impl.cpp b/renderer/pls_render_context_buffer_ring_impl.cpp new file mode 100644 index 0000000..64cfee4 --- /dev/null +++ b/renderer/pls_render_context_buffer_ring_impl.cpp
@@ -0,0 +1,92 @@ +/* + * Copyright 2022 Rive + */ + +#include "rive/pls/pls_render_context_buffer_ring_impl.hpp" + +namespace rive::pls +{ +void PLSRenderContextBufferRingImpl::resizePathTexture(size_t width, size_t height) +{ + m_pathBuffer = makeTexelBufferRing(TexelBufferRing::Format::rgba32ui, + width / kPathTexelsPerItem, + height, + kPathTexelsPerItem, + kPathTextureIdx, + TexelBufferRing::Filter::nearest); +} + +void PLSRenderContextBufferRingImpl::resizeContourTexture(size_t width, size_t height) +{ + m_contourBuffer = makeTexelBufferRing(TexelBufferRing::Format::rgba32ui, + width / kContourTexelsPerItem, + height, + kContourTexelsPerItem, + pls::kContourTextureIdx, + TexelBufferRing::Filter::nearest); +} + +void PLSRenderContextBufferRingImpl::resizeSimpleColorRampsBuffer(size_t sizeInBytes) +{ + m_simpleColorRampsBuffer = + makePixelUnpackBufferRing(sizeInBytes / sizeof(TwoTexelRamp), sizeof(TwoTexelRamp)); +} + +void PLSRenderContextBufferRingImpl::resizeGradSpanBuffer(size_t sizeInBytes) +{ + m_gradSpanBuffer = + makeVertexBufferRing(sizeInBytes / sizeof(GradientSpan), sizeof(GradientSpan)); +} + +void PLSRenderContextBufferRingImpl::resizeTessVertexSpanBuffer(size_t sizeInBytes) +{ + m_tessSpanBuffer = + makeVertexBufferRing(sizeInBytes / sizeof(TessVertexSpan), sizeof(TessVertexSpan)); +} + +void PLSRenderContextBufferRingImpl::resizeTriangleVertexBuffer(size_t sizeInBytes) +{ + m_triangleBuffer = + makeVertexBufferRing(sizeInBytes / sizeof(TriangleVertex), sizeof(TriangleVertex)); +} + +void PLSRenderContextBufferRingImpl::unmapPathTexture(size_t widthWritten, size_t heightWritten) +{ + m_pathBuffer->unmapAndSubmitBuffer(heightWritten * widthWritten * 4 * 4); +} + +void PLSRenderContextBufferRingImpl::unmapContourTexture(size_t widthWritten, size_t heightWritten) +{ + return m_contourBuffer->unmapAndSubmitBuffer(heightWritten * widthWritten * 4 * 4); +} + +void PLSRenderContextBufferRingImpl::unmapSimpleColorRampsBuffer(size_t bytesWritten) +{ + m_simpleColorRampsBuffer->unmapAndSubmitBuffer(bytesWritten); +} + +void PLSRenderContextBufferRingImpl::unmapGradSpanBuffer(size_t bytesWritten) +{ + m_gradSpanBuffer->unmapAndSubmitBuffer(bytesWritten); +} + +void PLSRenderContextBufferRingImpl::unmapTessVertexSpanBuffer(size_t bytesWritten) +{ + m_tessSpanBuffer->unmapAndSubmitBuffer(bytesWritten); +} + +void PLSRenderContextBufferRingImpl::unmapTriangleVertexBuffer(size_t bytesWritten) +{ + m_triangleBuffer->unmapAndSubmitBuffer(bytesWritten); +} + +void PLSRenderContextBufferRingImpl::updateFlushUniforms(const FlushUniforms* uniformData) +{ + if (m_uniformBuffer == nullptr) + { + m_uniformBuffer = makeUniformBufferRing(sizeof(FlushUniforms)); + } + memcpy(m_uniformBuffer->mapBuffer(), uniformData, sizeof(FlushUniforms)); + m_uniformBuffer->unmapAndSubmitBuffer(sizeof(FlushUniforms)); +} +} // namespace rive::pls