Skip to content

Commit

Permalink
Clone draw for cache optimizations
Browse files Browse the repository at this point in the history
Diffs=
4566a1208 Clone draw for cache optimizations (#8176)

Co-authored-by: rivessamr <[email protected]>
  • Loading branch information
rivessamr and rivessamr committed Sep 26, 2024
1 parent 8b50d16 commit f1c22e9
Show file tree
Hide file tree
Showing 9 changed files with 249 additions and 23 deletions.
2 changes: 1 addition & 1 deletion .rive_head
Original file line number Diff line number Diff line change
@@ -1 +1 @@
56511a2858f8e01481eb23d2cca00cbf5c25fd93
4566a1208a50671aede1fd1bf30a1806b757c343
1 change: 1 addition & 0 deletions include/rive/math/mat2d.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ class Mat2D
static Mat2D compose(const TransformComponents&);
float findMaxScale() const;
Mat2D scale(Vec2D) const;
Mat2D translate(Vec2D) const;

static Mat2D multiply(const Mat2D& a, const Mat2D& b);

Expand Down
14 changes: 12 additions & 2 deletions renderer/include/rive/renderer/draw.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class Draw
stencilClipReset,
};

Draw(IAABB pixelBounds, const Mat2D&, BlendMode, rcp<const Texture> imageTexture, Type);
Draw(AABB bounds, const Mat2D&, BlendMode, rcp<const Texture> imageTexture, Type);

const Texture* imageTexture() const { return m_imageTextureRef; }
const IAABB& pixelBounds() const { return m_pixelBounds; }
Expand Down Expand Up @@ -95,6 +95,7 @@ class Draw

protected:
const Texture* const m_imageTextureRef;
const AABB m_bounds;
const IAABB m_pixelBounds;
const Mat2D m_matrix;
const BlendMode m_blendMode;
Expand Down Expand Up @@ -142,14 +143,23 @@ class RiveRenderPathDraw : public Draw
void releaseRefs() override;

public:
RiveRenderPathDraw(IAABB,
RiveRenderPathDraw(AABB,
const Mat2D&,
rcp<const RiveRenderPath>,
FillRule,
const RiveRenderPaint*,
Type,
gpu::InterlockMode);

// Copy constructor
RiveRenderPathDraw(const RiveRenderPathDraw&,
float tx,
float ty,
rcp<const RiveRenderPath>,
FillRule fillRule,
const RiveRenderPaint* paint,
gpu::InterlockMode);

void onPushToRenderContext(RenderContext::LogicalFlush*);

const RiveRenderPath* const m_pathRef;
Expand Down
52 changes: 48 additions & 4 deletions renderer/src/draw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -295,13 +295,14 @@ RIVE_ALWAYS_INLINE uint32_t join_type_flags(StrokeJoin join)
}
} // namespace

Draw::Draw(IAABB pixelBounds,
Draw::Draw(AABB bounds,
const Mat2D& matrix,
BlendMode blendMode,
rcp<const Texture> imageTexture,
Type type) :
m_imageTextureRef(imageTexture.release()),
m_pixelBounds(pixelBounds),
m_bounds(bounds),
m_pixelBounds(bounds.roundOut()),
m_matrix(matrix),
m_blendMode(blendMode),
m_type(type)
Expand Down Expand Up @@ -426,14 +427,14 @@ DrawUniquePtr RiveRenderPathDraw::Make(RenderContext* context,
return DrawUniquePtr(draw);
}

RiveRenderPathDraw::RiveRenderPathDraw(IAABB pixelBounds,
RiveRenderPathDraw::RiveRenderPathDraw(AABB bounds,
const Mat2D& matrix,
rcp<const RiveRenderPath> path,
FillRule fillRule,
const RiveRenderPaint* paint,
Type type,
gpu::InterlockMode frameInterlockMode) :
Draw(pixelBounds, matrix, paint->getBlendMode(), ref_rcp(paint->getImageTexture()), type),
Draw(bounds, matrix, paint->getBlendMode(), ref_rcp(paint->getImageTexture()), type),
m_pathRef(path.release()),
m_fillRule(paint->getIsStroked() ? FillRule::nonZero : fillRule),
m_paintType(paint->getType())
Expand Down Expand Up @@ -500,6 +501,48 @@ RiveRenderPathDraw::RiveRenderPathDraw(IAABB pixelBounds,
assert(isStroked() == (strokeRadius() > 0));
}

RiveRenderPathDraw::RiveRenderPathDraw(const RiveRenderPathDraw& from,
float tx,
float ty,
rcp<const RiveRenderPath> path,
FillRule fillRule,
const RiveRenderPaint* paint,
gpu::InterlockMode frameInterlockMode) :
RiveRenderPathDraw(
from.m_bounds.offset(tx - from.m_matrix.tx(), ty - from.m_matrix.ty()),
from.m_matrix.translate(Vec2D(tx - from.m_matrix.tx(), ty - from.m_matrix.ty())),
path,
fillRule,
paint,
from.m_type,
frameInterlockMode)

{
m_resourceCounts = from.m_resourceCounts;
m_strokeMatrixMaxScale = from.m_strokeMatrixMaxScale;

if (isStroked())
{
m_strokeMatrixMaxScale = from.m_strokeMatrixMaxScale;
m_strokeJoin = from.m_strokeJoin;
m_strokeCap = from.m_strokeCap;
}
m_contours = from.m_contours;
m_numChops = from.m_numChops;
m_chopVertices = from.m_chopVertices;
m_tangentPairs = from.m_tangentPairs;
m_polarSegmentCounts = from.m_polarSegmentCounts;
m_parametricSegmentCounts = from.m_parametricSegmentCounts;
m_triangulator = from.m_triangulator;

RIVE_DEBUG_CODE(m_pendingLineCount = from.m_pendingLineCount;)
RIVE_DEBUG_CODE(m_pendingCurveCount = from.m_pendingCurveCount;)
RIVE_DEBUG_CODE(m_pendingRotationCount = from.m_pendingRotationCount;)
RIVE_DEBUG_CODE(m_pendingStrokeJoinCount = from.m_pendingStrokeJoinCount;)
RIVE_DEBUG_CODE(m_pendingStrokeCapCount = from.m_pendingStrokeCapCount;)
RIVE_DEBUG_CODE(m_pendingEmptyStrokeCountForCaps = from.m_pendingEmptyStrokeCountForCaps;)
}

void RiveRenderPathDraw::pushToRenderContext(RenderContext::LogicalFlush* flush)
{
// Make sure the rawPath in our path reference hasn't changed since we began holding!
Expand All @@ -523,6 +566,7 @@ void RiveRenderPathDraw::pushToRenderContext(RenderContext::LogicalFlush* flush)

void RiveRenderPathDraw::releaseRefs()
{
m_pathRef->invalidateDrawCache();
Draw::releaseRefs();
RIVE_DEBUG_CODE(m_pathRef->unlockRawPathMutations();)
m_pathRef->unref();
Expand Down
1 change: 0 additions & 1 deletion renderer/src/render_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -925,7 +925,6 @@ void RenderContext::LogicalFlush::writeResources()
for (size_t i = 0; i < m_draws.size(); ++i)
{
Draw* draw = m_draws[i].get();

int4 drawBounds = simd::load4i(&m_draws[i]->pixelBounds());

// Add one extra pixel of padding to the draw bounds to make absolutely certain we get
Expand Down
70 changes: 70 additions & 0 deletions renderer/src/rive_render_path.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

namespace rive
{

RiveRenderPath::RiveRenderPath(FillRule fillRule, RawPath& rawPath)
{
m_rawPath.swap(rawPath);
Expand Down Expand Up @@ -163,4 +164,73 @@ uint64_t RiveRenderPath::getRawPathMutationID() const
}
return m_rawPathMutationID;
}

void RiveRenderPath::setDrawCache(gpu::RiveRenderPathDraw* drawCache,
const Mat2D& mat,
rive::RiveRenderPaint* riveRenderPaint) const
{
CacheElements& cache =
m_cachedElements[riveRenderPaint->getIsStroked() ? CACHE_STROKED : CACHE_FILLED];

cache.draw = drawCache;

cache.xx = mat.xx();
cache.xy = mat.xy();
cache.yx = mat.yx();
cache.yy = mat.yy();

if (riveRenderPaint->getIsStroked())
{
m_cachedThickness = riveRenderPaint->getThickness();
m_cachedJoin = riveRenderPaint->getJoin();
m_cachedCap = riveRenderPaint->getCap();
}
}

gpu::DrawUniquePtr RiveRenderPath::getDrawCache(const Mat2D& matrix,
const RiveRenderPaint* paint,
FillRule fillRule,
TrivialBlockAllocator* allocator,
gpu::InterlockMode interlockMode) const
{
const CacheElements& cache =
m_cachedElements[paint->getIsStroked() ? CACHE_STROKED : CACHE_FILLED];

if (cache.draw == nullptr)
{
return nullptr;
}

if (paint->getIsStroked())
{
if (m_cachedThickness != paint->getThickness())
{
return nullptr;
}

if (m_cachedJoin != paint->getJoin())
{
return nullptr;
}

if (m_cachedCap != paint->getCap())
{
return nullptr;
}
}

if (matrix.xx() != cache.xx || matrix.xy() != cache.xy || matrix.yx() != cache.yx ||
matrix.yy() != cache.yy)
{
return nullptr;
}

return gpu::DrawUniquePtr(allocator->make<gpu::RiveRenderPathDraw>(*cache.draw,
matrix.tx(),
matrix.ty(),
ref_rcp(this),
fillRule,
paint,
interlockMode));
}
} // namespace rive
58 changes: 57 additions & 1 deletion renderer/src/rive_render_path.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

#include "rive/math/raw_path.hpp"
#include "rive/renderer.hpp"
#include "rive/renderer/draw.hpp"
#include "rive_render_paint.hpp"
#include "../renderer/src/rive_render_path.hpp"

namespace rive
{
Expand All @@ -17,7 +20,21 @@ class RiveRenderPath : public lite_rtti_override<RenderPath, RiveRenderPath>
RiveRenderPath(FillRule fillRule, RawPath& rawPath);

void rewind() override;
void fillRule(FillRule rule) override { m_fillRule = rule; }
void fillRule(FillRule rule) override
{
if (m_fillRule == rule)
{
return;
}
m_fillRule = rule;
// Most cached draws can be used interchangeably with any fill rule, but if there is a
// triangulator, it needs to be invalidated when the fill rule changes.
if (m_cachedElements[CACHE_FILLED].draw != nullptr &&
m_cachedElements[CACHE_FILLED].draw->triangulator() != nullptr)
{
invalidateDrawCache(CACHE_FILLED);
}
}

void moveTo(float x, float y) override;
void lineTo(float x, float y) override;
Expand Down Expand Up @@ -64,5 +81,44 @@ class RiveRenderPath : public lite_rtti_override<RenderPath, RiveRenderPath>

mutable uint32_t m_dirt = kAllDirt;
RIVE_DEBUG_CODE(mutable int m_rawPathMutationLockCount = 0;)

public:
void invalidateDrawCache() const
{
invalidateDrawCache(CACHE_STROKED);
invalidateDrawCache(CACHE_FILLED);
}

void invalidateDrawCache(int index) const { m_cachedElements[index].draw = nullptr; }

void setDrawCache(gpu::RiveRenderPathDraw* drawCache,
const Mat2D& mat,
rive::RiveRenderPaint* riveRenderPaint) const;

gpu::DrawUniquePtr getDrawCache(const Mat2D& matrix,
const RiveRenderPaint* paint,
FillRule fillRule,
TrivialBlockAllocator* allocator,
gpu::InterlockMode interlockMode) const;

private:
enum
{
CACHE_STROKED,
CACHE_FILLED,
NUM_CACHES,
};
struct CacheElements
{
gpu::RiveRenderPathDraw* draw = nullptr;
float xx;
float xy;
float yx;
float yy;
};
mutable CacheElements m_cachedElements[NUM_CACHES];
mutable float m_cachedThickness;
mutable StrokeJoin m_cachedJoin;
mutable StrokeCap m_cachedCap;
};
} // namespace rive
62 changes: 48 additions & 14 deletions renderer/src/rive_renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,12 +129,30 @@ void RiveRenderer::drawPath(RenderPath* renderPath, RenderPaint* renderPaint)
return;
}

clipAndPushDraw(gpu::RiveRenderPathDraw::Make(m_context,
m_stack.back().matrix,
ref_rcp(path),
path->getFillRule(),
paint,
&m_scratchPath));
gpu::DrawUniquePtr cacheDraw = path->getDrawCache(m_stack.back().matrix,
paint,
path->getFillRule(),
&m_context->perFrameAllocator(),
m_context->frameInterlockMode());

if (cacheDraw != nullptr)
{
clipAndPushDraw(std::move(cacheDraw));
return;
}

auto draw = gpu::RiveRenderPathDraw::Make(m_context,
m_stack.back().matrix,
ref_rcp(path),
path->getFillRule(),
paint,
&m_scratchPath);

path->setDrawCache(static_cast<gpu::RiveRenderPathDraw*>(draw.get()),
m_stack.back().matrix,
paint);

clipAndPushDraw(std::move(draw));
}

void RiveRenderer::clipPath(RenderPath* renderPath)
Expand Down Expand Up @@ -469,16 +487,32 @@ RiveRenderer::ApplyClipResult RiveRenderer::applyClip(gpu::Draw* draw)
{
RiveRenderPaint clipUpdatePaint;
clipUpdatePaint.clipUpdate(/*clip THIS clipDraw against:*/ lastClipID);
auto clipDraw = gpu::RiveRenderPathDraw::Make(m_context,
clip.matrix,
clip.path,
clip.fillRule,
&clipUpdatePaint,
&m_scratchPath);
if (clipDraw.get() == nullptr)

gpu::DrawUniquePtr clipDraw = clip.path->getDrawCache(clip.matrix,
&clipUpdatePaint,
clip.fillRule,
&m_context->perFrameAllocator(),
m_context->frameInterlockMode());

if (clipDraw == nullptr)
{
return ApplyClipResult::clipEmpty;
clipDraw = gpu::RiveRenderPathDraw::Make(m_context,
clip.matrix,
clip.path,
clip.fillRule,
&clipUpdatePaint,
&m_scratchPath);

if (clipDraw == nullptr)
{
return ApplyClipResult::clipEmpty;
}

clip.path->setDrawCache(static_cast<gpu::RiveRenderPathDraw*>(clipDraw.get()),
clip.matrix,
&clipUpdatePaint);
}

clipDrawBounds = clipDraw->pixelBounds();
// Generate a new clipID every time we (re-)render an element to the clip buffer.
// (Each embodiment of the element needs its own separate readBounds.)
Expand Down
Loading

0 comments on commit f1c22e9

Please sign in to comment.