blob: 69ff21faaaa35a562bccd0a8e14b60878369370a [file] [log] [blame]
/*
* Copyright 2022 Rive
*/
#pragma once
#include "rive/math/raw_path.hpp"
#include "rive/renderer.hpp"
#include "rive/pls/aligned_buffer.hpp"
#include "rive/pls/fixed_queue.hpp"
#include "rive/pls/pls.hpp"
#include "rive/pls/pls_render_context.hpp"
#include <vector>
namespace rive
{
class GrInnerFanTriangulator;
};
namespace rive::pls
{
class PLSPath;
class PLSPaint;
class PLSRenderContext;
// Renderer implementation for Rive's pixel local storage renderer.
class PLSRenderer : public Renderer
{
public:
PLSRenderer(PLSRenderContext*);
~PLSRenderer() override;
void save() override;
void restore() override;
void transform(const Mat2D& matrix) override;
void drawPath(RenderPath*, RenderPaint*) override;
void clipPath(RenderPath*) override;
void drawImage(const RenderImage*, BlendMode, float opacity) override;
void drawImageMesh(const RenderImage*,
rcp<RenderBuffer> vertices_f32,
rcp<RenderBuffer> uvCoords_f32,
rcp<RenderBuffer> indices_u16,
BlendMode,
float opacity) override;
// Most likely temporary. Determines if a path is an axis-aligned rectangle that can be
// represented by the struct rive::AABB. Used to detect artboard clip candidates.
static bool IsAABB(const RawPath&);
private:
class InteriorTriangulationHelper;
// Pushes any necessary clip updates to m_pathBatch and writes back the clipID the next path
// should be drawn with.
// Returns false if the operation failed, at which point the caller should flush and try again.
[[nodiscard]] bool applyClip(uint32_t* clipID);
// Pushes all paths in m_pathBatch to the GPU. All paths in the batch are assumed to be clip
// updates except the final one, which is drawn with 'finalPathPaint'.
[[nodiscard]] bool pushInternalPathBatch(PLSPaint* finalPathPaint);
struct ContourData
{
ContourData(const RawPath::Iter& endOfContour_,
size_t endLineIdx_,
size_t endCurveIdx_,
size_t endRotationIdx_,
Vec2D midpoint_,
bool closed_,
size_t strokeJoinCount_) :
endOfContour(endOfContour_),
endLineIdx(endLineIdx_),
endCurveIdx(endCurveIdx_),
endRotationIdx(endRotationIdx_),
midpoint(midpoint_),
closed(closed_),
strokeJoinCount(strokeJoinCount_)
{}
RawPath::Iter endOfContour;
size_t endLineIdx;
size_t endCurveIdx;
size_t endRotationIdx; // We measure rotations on both curves and round joins.
Vec2D midpoint;
bool closed;
size_t strokeJoinCount;
uint32_t strokeCapSegmentCount = 0;
uint32_t paddingVertexCount = 0;
RIVE_DEBUG_CODE(uint32_t tessVertexCount = 0;)
};
void pushContour(RawPath::Iter iter,
const ContourData&,
size_t curveIdx,
size_t rotationIdx,
float matrixMaxScale,
const PLSPaint* strokePaint);
// Emulates a stroke cap before the given cubic by pushing a copy of the cubic, reversed, with 0
// tessellation segments leading up to the join section, and a 180-degree join that looks like
// the desired stroke cap.
void pushEmulatedStrokeCapAsJoinBeforeCubic(const Vec2D cubic[],
uint32_t emulatedCapAsJoinFlags,
uint32_t strokeCapSegmentCount);
// Called when we ran out of room in GPU buffers. Does an intermediate flush of all currently
// queued GPU work.
void intermediateFlush();
struct RenderState
{
RenderState(const Mat2D& m, size_t h) : matrix(m), clipStackHeight(h) {}
Mat2D matrix;
size_t clipStackHeight;
};
std::vector<RenderState> m_stack{{Mat2D(), 0}};
struct ClipElement
{
Mat2D matrix;
RawPath path;
AABB pathBounds;
FillRule fillRule;
uint32_t clipID;
};
std::vector<ClipElement> m_clipStack;
bool m_hasArtboardClipCandidate = false;
PLSRenderContext* const m_context;
// Internal list of path draws that get pushed to the GPU in a single batch.
struct PathDraw
{
PathDraw(const Mat2D* matrix_,
const RawPath* rawPath_,
const AABB& pathBounds_,
FillRule fillRule_,
uint32_t clipID_) :
matrix(matrix_),
rawPath(rawPath_),
pathBounds(pathBounds_),
fillRule(fillRule_),
clipID(clipID_)
{}
const Mat2D* matrix;
const RawPath* rawPath;
AABB pathBounds;
FillRule fillRule;
uint32_t clipID;
GrInnerFanTriangulator* triangulator = nullptr; // Non-null if using interior triangulation.
size_t firstContourIdx = 0;
size_t contourCount = 0;
uint32_t tessVertexCount = 0;
uint32_t paddingVertexCount = 0;
};
std::vector<PathDraw> m_pathBatch;
// Temporary storage used during drawPath. Stored as a persistent class member to avoid
// redundant mallocs.
std::vector<ContourData> m_contourBatch;
AlignedBuffer<4, float> m_parametricSegmentCounts_pow4;
FixedQueue<uint8_t> m_numChops;
FixedQueue<Vec2D> m_chops;
AlignedBuffer<1, std::array<Vec2D, 2>> m_tangentPairs;
AlignedBuffer<4, uint32_t> m_parametricSegmentCounts;
AlignedBuffer<4, uint32_t> m_polarSegmentCounts;
// Used to build coarse path interiors for the "interior triangulation" algorithm.
RawPath m_scratchPath;
// Consistency checks for pushContour.
RIVE_DEBUG_CODE(size_t m_pushedLineCount;)
RIVE_DEBUG_CODE(size_t m_pushedCurveCount;)
RIVE_DEBUG_CODE(size_t m_pushedRotationCount;)
RIVE_DEBUG_CODE(size_t m_pushedStrokeJoinCount;)
RIVE_DEBUG_CODE(size_t m_pushedStrokeCapCount;)
// Counts how many additional curves were pushed by pushEmulatedStrokeCapAsJoinBeforeCubic().
RIVE_DEBUG_CODE(size_t m_pushedEmptyStrokeCountForCaps;)
};
} // namespace rive::pls