Skip to content

Commit

Permalink
Add RenderPath::addRawPath
Browse files Browse the repository at this point in the history
Addresses this issue: https://2dimensions.slack.com/archives/C07VD44ASE5/p1739456802968219

Rive's RenderPath steals the memory for the RawPath the runtime builds, which is usually fine apart from cases where we expect to use the RawPath after drawing with it. For example, with inner feather we want to be able to compute the bounds of the RawPath after it has been drawn. If during animation a user turns on inner feathering on a path which has not changed recently, which need to be able to compute its bounds, we can't if the RawPath is now gone.

This also can happen with trim paths and dash paths which are re-trimmed/dashed after having being rendered a few frames. Right now we force rebuild the whole path to re-trim it because we lose the RawPath.

This PR allows the ShapePaintPath to hold on to its RawPath by adding RenderPath::addRawPath. This is optimized memory usage but not having the runtime need to re-alloc the RawPath whenever it animates, but it means we're further from making RenderPaths immutable.

For the future: It'd be good to chat about a long term solution that can accommodate all goals of allocating less memory (prior to this change we were re-allocating RawPath memory every frame when animating), not rebuilding paths as often, and still allow for immutability.

Diffs=
9058a3fdad Add RenderPath::addRawPath (#9038)

Co-authored-by: Luigi Rosso <[email protected]>
  • Loading branch information
luigi-rosso and luigi-rosso committed Feb 14, 2025
1 parent e5ede9c commit 7f971fb
Show file tree
Hide file tree
Showing 16 changed files with 82 additions and 17 deletions.
2 changes: 1 addition & 1 deletion .rive_head
Original file line number Diff line number Diff line change
@@ -1 +1 @@
7a6019fb974ed415d8531980b0b1516ff9f095c8
9058a3fdad2444ca9eab3ef06bc49376eadba9de
9 changes: 9 additions & 0 deletions cg_renderer/src/cg_factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,11 @@ class CGRenderPath : public LITE_RTTI_OVERRIDE(RenderPath, CGRenderPath)
CGRenderPath(Span<const Vec2D> pts, Span<const PathVerb> vbs, FillRule rule)
{
m_fillMode = convert(rule);
addRawPathCommands(pts, vbs);
}

void addRawPathCommands(Span<const Vec2D> pts, Span<const PathVerb> vbs)
{
auto p = pts.data();
for (auto v : vbs)
{
Expand Down Expand Up @@ -135,6 +139,11 @@ class CGRenderPath : public LITE_RTTI_OVERRIDE(RenderPath, CGRenderPath)
assert(p == pts.end());
}

void addRawPath(const RawPath& path) override
{
addRawPathCommands(path.points(), path.verbs());
}

CGPathRef path() const { return m_path.get(); }
CGPathDrawingMode drawingMode(bool isStroke) const
{
Expand Down
4 changes: 3 additions & 1 deletion include/rive/renderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
#include "rive/shapes/paint/stroke_cap.hpp"
#include "rive/shapes/paint/stroke_join.hpp"
#include "utils/lite_rtti.hpp"

#include "rive/math/raw_path.hpp"
#include <stdio.h>
#include <cstdint>

Expand Down Expand Up @@ -192,6 +192,8 @@ class RenderPath : public CommandPath, public ENABLE_LITE_RTTI(RenderPath)
{
// No-op on non rive renderer.
}

virtual void addRawPath(const RawPath& path) = 0;
};

class Renderer
Expand Down
1 change: 0 additions & 1 deletion include/rive/shapes/shape_paint_container.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ class ShapePaintContainer
std::vector<ShapePaint*> m_ShapePaints;
void addPaint(ShapePaint* paint);

// TODO: void draw(Renderer* renderer, PathComposer& composer);
public:
static ShapePaintContainer* from(Component* component);

Expand Down
6 changes: 5 additions & 1 deletion include/rive/shapes/shape_paint_path.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@ class ShapePaintPath
m_rawPath.addRect(aabb, dir);
}

const bool hasRenderPath() const { return m_renderPath != nullptr; }
const bool hasRenderPath() const
{
return m_renderPath != nullptr && !m_isRenderPathDirty;
}

#ifdef TESTING
size_t numContours()
Expand All @@ -68,6 +71,7 @@ class ShapePaintPath
}
#endif
private:
bool m_isRenderPathDirty = true;
rcp<RenderPath> m_renderPath;
RawPath m_rawPath;
bool m_isLocal;
Expand Down
12 changes: 10 additions & 2 deletions renderer/src/rive_render_path.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,12 @@ void RiveRenderPath::addRenderPath(RenderPath* path, const Mat2D& matrix)
{
assert(m_rawPathMutationLockCount == 0);
RiveRenderPath* riveRenderPath = static_cast<RiveRenderPath*>(path);

bool isIdentity = matrix == Mat2D();
RawPath::Iter transformedPathIter =
m_rawPath.addPath(riveRenderPath->m_rawPath, &matrix);
if (matrix != Mat2D())
m_rawPath.addPath(riveRenderPath->m_rawPath,
isIdentity ? nullptr : &matrix);
if (!isIdentity)
{
// Prune any segments that became empty after the transform.
m_rawPath.pruneEmptySegments(transformedPathIter);
Expand All @@ -106,6 +109,11 @@ void RiveRenderPath::addRenderPathBackwards(RenderPath* path,
m_dirt = kAllDirt;
}

void RiveRenderPath::addRawPath(const RawPath& path)
{
m_rawPath.addPath(path, nullptr);
}

const AABB& RiveRenderPath::getBounds() const
{
if (m_dirt & kPathBoundsDirt)
Expand Down
2 changes: 1 addition & 1 deletion renderer/src/rive_render_path.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class RiveRenderPath : public LITE_RTTI_OVERRIDE(RenderPath, RiveRenderPath)
void addRenderPath(RenderPath* path, const Mat2D& matrix) override;
void addRenderPathBackwards(RenderPath* path,
const Mat2D& transform) override;

void addRawPath(const RawPath& path) override;
const RawPath& getRawPath() const { return m_rawPath; }
FillRule getFillRule() const { return m_fillRule; }

Expand Down
18 changes: 18 additions & 0 deletions skia/renderer/src/skia_factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class SkiaRenderPath : public LITE_RTTI_OVERRIDE(RenderPath, SkiaRenderPath)

void rewind() override;
void addRenderPath(RenderPath* path, const Mat2D& transform) override;
void addRawPath(const RawPath& path) override;
void fillRule(FillRule value) override;
void moveTo(float x, float y) override;
void lineTo(float x, float y) override;
Expand Down Expand Up @@ -132,6 +133,23 @@ void SkiaRenderPath::addRenderPath(RenderPath* path, const Mat2D& transform)
m_Path.addPath(skPath->m_Path, ToSkia::convert(transform));
}

void SkiaRenderPath::addRawPath(const RawPath& path)
{
const bool isVolatile = false;
const SkScalar* conicWeights = nullptr;
const int conicWeightCount = 0;
auto skPath =
SkPath::Make(reinterpret_cast<const SkPoint*>(path.points().data()),
path.points().size(),
(uint8_t*)path.verbs().data(),
path.verbs().size(),
conicWeights,
conicWeightCount,
m_Path.getFillType(),
isVolatile);
m_Path.addPath(skPath);
}

void SkiaRenderPath::moveTo(float x, float y) { m_Path.moveTo(x, y); }
void SkiaRenderPath::lineTo(float x, float y) { m_Path.lineTo(x, y); }
void SkiaRenderPath::cubicTo(float ox,
Expand Down
25 changes: 15 additions & 10 deletions src/shapes/paint/shape_paint_path.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ ShapePaintPath::ShapePaintPath(bool isLocal, FillRule fillRule) :
void ShapePaintPath::rewind()
{
m_rawPath.rewind();
m_renderPath = nullptr;
m_isRenderPathDirty = true;
}

void ShapePaintPath::addPath(const RawPath& rawPath, const Mat2D* transform)
{
auto iter = m_rawPath.addPath(rawPath, transform);
m_rawPath.pruneEmptySegments(iter);
m_renderPath = nullptr;
m_isRenderPathDirty = true;
}

void ShapePaintPath::addPathClockwise(const RawPath& rawPath,
Expand All @@ -46,25 +46,30 @@ void ShapePaintPath::addPathBackwards(const RawPath& rawPath,
{
auto iter = m_rawPath.addPathBackwards(rawPath, transform);
m_rawPath.pruneEmptySegments(iter);
m_renderPath = nullptr;
m_isRenderPathDirty = true;
}

RenderPath* ShapePaintPath::renderPath(const Component* component)
{
assert(component != nullptr);
if (!m_renderPath)
{
auto factory = component->artboard()->factory();
m_renderPath = factory->makeRenderPath(m_rawPath, m_fillRule);
}
return m_renderPath.get();
return renderPath(component->artboard()->factory());
}

RenderPath* ShapePaintPath::renderPath(Factory* factory)
{
if (!m_renderPath)
{
m_renderPath = factory->makeRenderPath(m_rawPath, m_fillRule);
m_renderPath = factory->makeEmptyRenderPath();
m_renderPath->addRawPath(m_rawPath);
m_renderPath->fillRule(m_fillRule);
m_isRenderPathDirty = false;
}
else if (m_isRenderPathDirty)
{
m_renderPath->rewind();
m_renderPath->addRawPath(m_rawPath);
m_isRenderPathDirty = false;
}

return m_renderPath.get();
}
1 change: 1 addition & 0 deletions tess/include/rive/tess/tess_render_path.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ class TessRenderPath : public LITE_RTTI_OVERRIDE(RenderPath, TessRenderPath)
override;
void close() override;
void addRenderPath(RenderPath* path, const Mat2D& transform) override;
void addRawPath(const RawPath& path) override;

const SegmentedContour& segmentedContour() const;
bool triangulate();
Expand Down
1 change: 1 addition & 0 deletions tess/src/sokol/sokol_factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class NoOpRenderPath : public LITE_RTTI_OVERRIDE(RenderPath, NoOpRenderPath)
void fillRule(FillRule value) override {}
void addPath(CommandPath* path, const Mat2D& transform) override {}
void addRenderPath(RenderPath* path, const Mat2D& transform) override {}
void addRawPath(const RawPath& path) override {}

void moveTo(float x, float y) override {}
void lineTo(float x, float y) override {}
Expand Down
5 changes: 5 additions & 0 deletions tess/src/tess_render_path.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ void TessRenderPath::addRenderPath(RenderPath* path, const Mat2D& transform)
m_subPaths.emplace_back(SubPath(path, transform));
}

void TessRenderPath::addRawPath(const RawPath& path)
{
m_rawPath.addPath(path, nullptr);
}

const SegmentedContour& TessRenderPath::segmentedContour() const
{
return m_segmentedContour;
Expand Down
5 changes: 5 additions & 0 deletions tests/gm/feather.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,11 @@ class FeatherShapesGM : public FeatherGM
m_begin = shape->m_begin;
}

void addRawPath(const RawPath& path) override
{
m_path->addRawPath(path);
}

protected:
Path m_path;
Vec2D m_pen = {0, 0};
Expand Down
4 changes: 4 additions & 0 deletions tests/unit_tests/runtime/clip_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ class ClipTestRenderPath : public rive::RenderPath
void addRenderPath(rive::RenderPath* path,
const rive::Mat2D& transform) override
{}
void addRawPath(const rive::RawPath& path) override
{
rawPath.addPath(path, nullptr);
}

void moveTo(float x, float y) override {}
void lineTo(float x, float y) override {}
Expand Down
2 changes: 2 additions & 0 deletions tests/unit_tests/runtime/path_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ class TestRenderPath : public rive::RenderPath
{
public:
std::vector<TestPathCommand> commands;
void addRawPath(const rive::RawPath& path) override { path.addTo(this); }

void rewind() override
{
commands.emplace_back(TestPathCommand{TestPathCommandType::Reset,
Expand Down
2 changes: 2 additions & 0 deletions utils/no_op_factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ class NoOpRenderPath : public RenderPath
override
{}
void close() override {}

void addRawPath(const RawPath& path) override {}
};
} // namespace

Expand Down

0 comments on commit 7f971fb

Please sign in to comment.