From 699063625b3a2666b7f7ec6644b1a389421cd3ce Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Sat, 13 Jul 2024 17:33:11 +0900 Subject: [PATCH] lottie: ++scene rendering optimization Apply LottieRenderPooler to path/rect/ellipse/polystar. This enhances the animation performance: ~10% --- src/loaders/lottie/tvgLottieBuilder.cpp | 77 ++++++++++++++----------- src/loaders/lottie/tvgLottieModel.cpp | 10 ++++ src/loaders/lottie/tvgLottieModel.h | 17 +++--- src/renderer/tvgPaint.cpp | 14 +++-- src/renderer/tvgPaint.h | 2 +- src/renderer/tvgPicture.h | 12 ++-- src/renderer/tvgRender.h | 25 ++++++++ src/renderer/tvgScene.h | 10 ++-- src/renderer/tvgShape.h | 44 ++++++-------- src/renderer/tvgText.h | 10 ++-- 10 files changed, 138 insertions(+), 83 deletions(-) diff --git a/src/loaders/lottie/tvgLottieBuilder.cpp b/src/loaders/lottie/tvgLottieBuilder.cpp index ba250ee3..be753ce7 100644 --- a/src/loaders/lottie/tvgLottieBuilder.cpp +++ b/src/loaders/lottie/tvgLottieBuilder.cpp @@ -99,7 +99,7 @@ struct RenderContext static void _updateChildren(LottieGroup* parent, float frameNo, Inlist& contexts, LottieExpressions* exps); static void _updateLayer(LottieLayer* root, LottieLayer* layer, float frameNo, LottieExpressions* exps); static bool _buildComposition(LottieComposition* comp, LottieLayer* parent); -static bool _draw(LottieGroup* parent, RenderContext* ctx); +static bool _draw(LottieGroup* parent, LottieShape* shape, RenderContext* ctx); static void _rotationXYZ(Matrix* m, float degreeX, float degreeY, float degreeZ) @@ -275,7 +275,7 @@ static void _updateGroup(LottieGroup* parent, LottieObject** child, float frameN group->reqFragment |= ctx->reqFragment; //generate a merging shape to consolidate partial shapes into a single entity - if (group->mergeable()) _draw(parent, ctx); + if (group->mergeable()) _draw(parent, nullptr, ctx); Inlist contexts; contexts.back(new RenderContext(*ctx, group->mergeable())); @@ -374,20 +374,24 @@ static void _updateGradientFill(TVG_UNUSED LottieGroup* parent, LottieObject** c } -static bool _draw(LottieGroup* parent, RenderContext* ctx) +static bool _draw(LottieGroup* parent, LottieShape* shape, RenderContext* ctx) { if (ctx->merging) return false; - auto shape = cast(ctx->propagator->duplicate()); - ctx->merging = shape.get(); - parent->scene->push(std::move(shape)); + if (shape) { + ctx->merging = shape->pooling(); + PP(ctx->propagator)->duplicate(ctx->merging); + } else { + ctx->merging = static_cast(ctx->propagator->duplicate()); + } + + parent->scene->push(cast(ctx->merging)); return true; } -//OPTIMIZE: path? -static void _repeat(LottieGroup* parent, unique_ptr path, RenderContext* ctx) +static void _repeat(LottieGroup* parent, Shape* path, RenderContext* ctx) { Array propagators; propagators.push(ctx->propagator); @@ -401,7 +405,7 @@ static void _repeat(LottieGroup* parent, unique_ptr path, RenderContext* for (auto propagator = propagators.begin(); propagator < propagators.end(); ++propagator) { auto shape = static_cast((*propagator)->duplicate()); - P(shape)->rs.path = P(path.get())->rs.path; + P(shape)->rs.path = P(path)->rs.path; auto opacity = repeater->interpOpacity ? lerp(repeater->startOpacity, repeater->endOpacity, static_cast(i + 1) / repeater->cnt) : repeater->startOpacity; shape->opacity(opacity); @@ -542,11 +546,12 @@ static void _updateRect(LottieGroup* parent, LottieObject** child, float frameNo } if (!ctx->repeaters.empty()) { - auto path = Shape::gen(); - _appendRect(path.get(), position.x - size.x * 0.5f, position.y - size.y * 0.5f, size.x, size.y, roundness, ctx->transform, rect->clockwise); - _repeat(parent, std::move(path), ctx); + auto shape = rect->pooling(); + shape->reset(); + _appendRect(shape, position.x - size.x * 0.5f, position.y - size.y * 0.5f, size.x, size.y, roundness, ctx->transform, rect->clockwise); + _repeat(parent, shape, ctx); } else { - _draw(parent, ctx); + _draw(parent, rect, ctx); _appendRect(ctx->merging, position.x - size.x * 0.5f, position.y - size.y * 0.5f, size.x, size.y, roundness, ctx->transform, rect->clockwise); } } @@ -598,11 +603,12 @@ static void _updateEllipse(LottieGroup* parent, LottieObject** child, float fram auto size = ellipse->size(frameNo, exps); if (!ctx->repeaters.empty()) { - auto path = Shape::gen(); - _appendCircle(path.get(), position.x, position.y, size.x * 0.5f, size.y * 0.5f, ctx->transform, ellipse->clockwise); - _repeat(parent, std::move(path), ctx); + auto shape = ellipse->pooling(); + shape->reset(); + _appendCircle(shape, position.x, position.y, size.x * 0.5f, size.y * 0.5f, ctx->transform, ellipse->clockwise); + _repeat(parent, shape, ctx); } else { - _draw(parent, ctx); + _draw(parent, ellipse, ctx); _appendCircle(ctx->merging, position.x, position.y, size.x * 0.5f, size.y * 0.5f, ctx->transform, ellipse->clockwise); } } @@ -613,11 +619,12 @@ static void _updatePath(LottieGroup* parent, LottieObject** child, float frameNo auto path = static_cast(*child); if (!ctx->repeaters.empty()) { - auto p = Shape::gen(); - path->pathset(frameNo, P(p)->rs.path.cmds, P(p)->rs.path.pts, ctx->transform, ctx->roundness, exps); - _repeat(parent, std::move(p), ctx); + auto shape = path->pooling(); + shape->reset(); + path->pathset(frameNo, P(shape)->rs.path.cmds, P(shape)->rs.path.pts, ctx->transform, ctx->roundness, exps); + _repeat(parent, shape, ctx); } else { - _draw(parent, ctx); + _draw(parent, path, ctx); if (path->pathset(frameNo, P(ctx->merging)->rs.path.cmds, P(ctx->merging)->rs.path.pts, ctx->transform, ctx->roundness, exps)) { P(ctx->merging)->update(RenderUpdateFlag::Path); } @@ -708,8 +715,14 @@ static void _updateStar(LottieGroup* parent, LottiePolyStar* star, Matrix* trans auto direction = star->clockwise ? 1.0f : -1.0f; auto hasRoundness = false; bool roundedCorner = (roundness > ROUNDNESS_EPSILON) && (tvg::zero(innerRoundness) || tvg::zero(outerRoundness)); - //TODO: we can use PathCommand / PathCoord directly. - auto shape = roundedCorner ? Shape::gen().release() : merging; + + Shape* shape; + if (roundedCorner) { + shape = star->pooling(); + shape->reset(); + } else { + shape = merging; + } float x, y; @@ -798,10 +811,7 @@ static void _updateStar(LottieGroup* parent, LottiePolyStar* star, Matrix* trans } shape->close(); - if (roundedCorner) { - _applyRoundedCorner(shape, merging, outerRoundness, roundness, hasRoundness); - delete(shape); - } + if (roundedCorner) _applyRoundedCorner(shape, merging, outerRoundness, roundness, hasRoundness); } @@ -876,7 +886,7 @@ static void _updatePolygon(LottieGroup* parent, LottiePolyStar* star, Matrix* tr static void _updatePolystar(LottieGroup* parent, LottieObject** child, float frameNo, TVG_UNUSED Inlist& contexts, RenderContext* ctx, LottieExpressions* exps) { - auto star= static_cast(*child); + auto star = static_cast(*child); //Optimize: Can we skip the individual coords transform? Matrix matrix; @@ -890,12 +900,13 @@ static void _updatePolystar(LottieGroup* parent, LottieObject** child, float fra auto identity = tvg::identity((const Matrix*)&matrix); if (!ctx->repeaters.empty()) { - auto p = Shape::gen(); - if (star->type == LottiePolyStar::Star) _updateStar(parent, star, identity ? nullptr : &matrix, ctx->roundness, frameNo, p.get(), exps); - else _updatePolygon(parent, star, identity ? nullptr : &matrix, frameNo, p.get(), exps); - _repeat(parent, std::move(p), ctx); + auto shape = star->pooling(); + shape->reset(); + if (star->type == LottiePolyStar::Star) _updateStar(parent, star, identity ? nullptr : &matrix, ctx->roundness, frameNo, shape, exps); + else _updatePolygon(parent, star, identity ? nullptr : &matrix, frameNo, shape, exps); + _repeat(parent, shape, ctx); } else { - _draw(parent, ctx); + _draw(parent, star, ctx); if (star->type == LottiePolyStar::Star) _updateStar(parent, star, identity ? nullptr : &matrix, ctx->roundness, frameNo, ctx->merging, exps); else _updatePolygon(parent, star, identity ? nullptr : &matrix, frameNo, ctx->merging, exps); P(ctx->merging)->update(RenderUpdateFlag::Path); diff --git a/src/loaders/lottie/tvgLottieModel.cpp b/src/loaders/lottie/tvgLottieModel.cpp index 086264cc..1238d098 100644 --- a/src/loaders/lottie/tvgLottieModel.cpp +++ b/src/loaders/lottie/tvgLottieModel.cpp @@ -37,6 +37,16 @@ /* External Class Implementation */ /************************************************************************/ +void LottieShape::prepare(LottieObject::Type type) +{ + LottieObject::type = type; + + auto shape = tvg::Shape::gen().release(); + PP(shape)->ref(); + pooler.push(shape); +} + + void LottieSlot::reset() { if (!overridden) return; diff --git a/src/loaders/lottie/tvgLottieModel.h b/src/loaders/lottie/tvgLottieModel.h index 52508b04..3f91bb93 100644 --- a/src/loaders/lottie/tvgLottieModel.h +++ b/src/loaders/lottie/tvgLottieModel.h @@ -266,15 +266,18 @@ struct LottieTrimpath : LottieObject }; -struct LottieShape : LottieObject +struct LottieShape : LottieObject, LottieRenderPooler { - virtual ~LottieShape() {} bool clockwise = true; //clockwise or counter-clockwise + virtual ~LottieShape() {} + bool mergeable() override { return true; } + + void prepare(LottieObject::Type type); }; @@ -292,7 +295,7 @@ struct LottiePath : LottieShape { void prepare() { - LottieObject::type = LottieObject::Path; + LottieShape::prepare(LottieObject::Path); } LottiePathSet pathset; @@ -303,7 +306,7 @@ struct LottieRect : LottieShape { void prepare() { - LottieObject::type = LottieObject::Rect; + LottieShape::prepare(LottieObject::Rect); } LottiePosition position = Point{0.0f, 0.0f}; @@ -318,7 +321,7 @@ struct LottiePolyStar : LottieShape void prepare() { - LottieObject::type = LottieObject::Polystar; + LottieShape::prepare(LottieObject::Polystar); } LottiePosition position = Point{0.0f, 0.0f}; @@ -336,7 +339,7 @@ struct LottieEllipse : LottieShape { void prepare() { - LottieObject::type = LottieObject::Ellipse; + LottieShape::prepare(LottieObject::Ellipse); } LottiePosition position = Point{0.0f, 0.0f}; @@ -552,7 +555,7 @@ struct LottieGroup : LottieObject return nullptr; } - Scene* scene = nullptr; //tvg render data + Scene* scene = nullptr; Array children; bool reqFragment = false; //requirement to fragment the render context diff --git a/src/renderer/tvgPaint.cpp b/src/renderer/tvgPaint.cpp index 747eaa6e..95e68ed9 100644 --- a/src/renderer/tvgPaint.cpp +++ b/src/renderer/tvgPaint.cpp @@ -158,16 +158,22 @@ Iterator* Paint::Impl::iterator() } -Paint* Paint::Impl::duplicate() +Paint* Paint::Impl::duplicate(Paint* ret) { - Paint* ret; - PAINT_METHOD(ret, duplicate()); + if (ret) ret->composite(nullptr, CompositeMethod::None); + + PAINT_METHOD(ret, duplicate(ret)); //duplicate Transform if (rTransform) { - ret->pImpl->rTransform = new RenderTransform(); + if (!ret->pImpl->rTransform) { + ret->pImpl->rTransform = new RenderTransform(); + } *ret->pImpl->rTransform = *rTransform; ret->pImpl->renderFlag |= RenderUpdateFlag::Transform; + } else { + delete(ret->pImpl->rTransform); + ret->pImpl->rTransform = nullptr; } ret->pImpl->opacity = opacity; diff --git a/src/renderer/tvgPaint.h b/src/renderer/tvgPaint.h index 42abdb1c..273b479b 100644 --- a/src/renderer/tvgPaint.h +++ b/src/renderer/tvgPaint.h @@ -137,7 +137,7 @@ namespace tvg bool bounds(float* x, float* y, float* w, float* h, bool transformed, bool stroking); RenderData update(RenderMethod* renderer, const RenderTransform* pTransform, Array& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper = false); bool render(RenderMethod* renderer); - Paint* duplicate(); + Paint* duplicate(Paint* ret = nullptr); }; } diff --git a/src/renderer/tvgPicture.h b/src/renderer/tvgPicture.h index bd702121..d751fcf2 100644 --- a/src/renderer/tvgPicture.h +++ b/src/renderer/tvgPicture.h @@ -189,19 +189,21 @@ struct Picture::Impl } } - Paint* duplicate() + Paint* duplicate(Paint* ret) { + if (ret) TVGERR("RENDERER", "TODO: duplicate()"); + load(); - auto ret = Picture::gen().release(); - auto dup = ret->pImpl; + auto picture = Picture::gen().release(); + auto dup = picture->pImpl; if (paint) dup->paint = paint->duplicate(); if (loader) { dup->loader = loader; ++dup->loader->sharing; - PP(ret)->renderFlag |= RenderUpdateFlag::Image; + PP(picture)->renderFlag |= RenderUpdateFlag::Image; } dup->surface = surface; @@ -215,7 +217,7 @@ struct Picture::Impl memcpy(dup->rm.triangles, rm.triangles, sizeof(Polygon) * rm.triangleCnt); } - return ret; + return picture; } Iterator* iterator() diff --git a/src/renderer/tvgRender.h b/src/renderer/tvgRender.h index ff557480..0919d90e 100644 --- a/src/renderer/tvgRender.h +++ b/src/renderer/tvgRender.h @@ -147,6 +147,31 @@ struct RenderStroke bool simultaneous = true; } trim; + void operator=(const RenderStroke& rhs) + { + width = rhs.width; + + memcpy(color, rhs.color, sizeof(color)); + + delete(fill); + if (rhs.fill) fill = rhs.fill->duplicate(); + else fill = nullptr; + + free(dashPattern); + if (rhs.dashCnt > 0) { + dashPattern = static_cast(malloc(sizeof(float) * rhs.dashCnt)); + memcpy(dashPattern, rhs.dashPattern, sizeof(float) * rhs.dashCnt); + } else { + dashPattern = nullptr; + } + dashCnt = rhs.dashCnt; + miterlimit = rhs.miterlimit; + cap = rhs.cap; + join = rhs.join; + strokeFirst = rhs.strokeFirst; + trim = rhs.trim; + } + ~RenderStroke() { free(dashPattern); diff --git a/src/renderer/tvgScene.h b/src/renderer/tvgScene.h index 248df7ca..0da94692 100644 --- a/src/renderer/tvgScene.h +++ b/src/renderer/tvgScene.h @@ -198,10 +198,12 @@ struct Scene::Impl return true; } - Paint* duplicate() + Paint* duplicate(Paint* ret) { - auto ret = Scene::gen().release(); - auto dup = ret->pImpl; + if (ret) TVGERR("RENDERER", "TODO: duplicate()"); + + auto scene = Scene::gen().release(); + auto dup = scene->pImpl; for (auto paint : paints) { auto cdup = paint->duplicate(); @@ -209,7 +211,7 @@ struct Scene::Impl dup->paints.push_back(cdup); } - return ret; + return scene; } void clear(bool free) diff --git a/src/renderer/tvgShape.h b/src/renderer/tvgShape.h index 02d21991..bed62557 100644 --- a/src/renderer/tvgShape.h +++ b/src/renderer/tvgShape.h @@ -34,6 +34,7 @@ struct Shape::Impl RenderData rd = nullptr; //engine data Shape* shape; uint8_t flag = RenderUpdateFlag::None; + uint8_t opacity; //for composition bool needComp = false; //composite or not @@ -359,47 +360,40 @@ struct Shape::Impl this->flag |= flag; } - Paint* duplicate() + Paint* duplicate(Paint* ret) { - auto ret = Shape::gen().release(); - auto dup = ret->pImpl; + auto shape = static_cast(ret); + if (shape) shape->reset(); + else shape = Shape::gen().release(); + auto dup = shape->pImpl; + delete(dup->rs.fill); + + //Default Properties + dup->flag = RenderUpdateFlag::All; dup->rs.rule = rs.rule; //Color memcpy(dup->rs.color, rs.color, sizeof(rs.color)); - dup->flag = RenderUpdateFlag::Color; //Path - if (rs.path.cmds.count > 0 && rs.path.pts.count > 0) { - dup->rs.path.cmds = rs.path.cmds; - dup->rs.path.pts = rs.path.pts; - dup->flag |= RenderUpdateFlag::Path; - } + dup->rs.path.cmds.push(rs.path.cmds); + dup->rs.path.pts.push(rs.path.pts); //Stroke if (rs.stroke) { - dup->rs.stroke = new RenderStroke(); + if (!dup->rs.stroke) dup->rs.stroke = new RenderStroke; *dup->rs.stroke = *rs.stroke; - memcpy(dup->rs.stroke->color, rs.stroke->color, sizeof(rs.stroke->color)); - if (rs.stroke->dashCnt > 0) { - dup->rs.stroke->dashPattern = static_cast(malloc(sizeof(float) * rs.stroke->dashCnt)); - memcpy(dup->rs.stroke->dashPattern, rs.stroke->dashPattern, sizeof(float) * rs.stroke->dashCnt); - } - if (rs.stroke->fill) { - dup->rs.stroke->fill = rs.stroke->fill->duplicate(); - dup->flag |= RenderUpdateFlag::GradientStroke; - } - dup->flag |= RenderUpdateFlag::Stroke; + } else { + delete(dup->rs.stroke); + dup->rs.stroke = nullptr; } //Fill - if (rs.fill) { - dup->rs.fill = rs.fill->duplicate(); - dup->flag |= RenderUpdateFlag::Gradient; - } + if (rs.fill) dup->rs.fill = rs.fill->duplicate(); + else dup->rs.fill = nullptr; - return ret; + return shape; } Iterator* iterator() diff --git a/src/renderer/tvgText.h b/src/renderer/tvgText.h index 4cfc18e7..8af4fcf1 100644 --- a/src/renderer/tvgText.h +++ b/src/renderer/tvgText.h @@ -152,12 +152,14 @@ struct Text::Impl return true; } - Paint* duplicate() + Paint* duplicate(Paint* ret) { + if (ret) TVGERR("RENDERER", "TODO: duplicate()"); + load(); - auto ret = Text::gen().release(); - auto dup = ret->pImpl; + auto text = Text::gen().release(); + auto dup = text->pImpl; if (paint) dup->paint = static_cast(paint->duplicate()); if (loader) { @@ -169,7 +171,7 @@ struct Text::Impl dup->italic = italic; dup->fontSize = fontSize; - return ret; + return text; } Iterator* iterator()