lottie: ++scene rendering optimization

Apply LottieRenderPooler to path/rect/ellipse/polystar.

This enhances the animation performance: ~10%
This commit is contained in:
Hermet Park 2024-07-13 17:33:11 +09:00
parent d0210a1a72
commit cf253dd873
10 changed files with 138 additions and 83 deletions

View file

@ -99,7 +99,7 @@ struct RenderContext
static void _updateChildren(LottieGroup* parent, float frameNo, Inlist<RenderContext>& contexts, LottieExpressions* exps); static void _updateChildren(LottieGroup* parent, float frameNo, Inlist<RenderContext>& contexts, LottieExpressions* exps);
static void _updateLayer(LottieLayer* root, LottieLayer* layer, float frameNo, LottieExpressions* exps); static void _updateLayer(LottieLayer* root, LottieLayer* layer, float frameNo, LottieExpressions* exps);
static bool _buildComposition(LottieComposition* comp, LottieLayer* parent); 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) 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; group->reqFragment |= ctx->reqFragment;
//generate a merging shape to consolidate partial shapes into a single entity //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<RenderContext> contexts; Inlist<RenderContext> contexts;
contexts.back(new RenderContext(*ctx, group->mergeable())); 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; if (ctx->merging) return false;
auto shape = cast<Shape>(ctx->propagator->duplicate()); if (shape) {
ctx->merging = shape.get(); ctx->merging = shape->pooling();
parent->scene->push(std::move(shape)); PP(ctx->propagator)->duplicate(ctx->merging);
} else {
ctx->merging = static_cast<Shape*>(ctx->propagator->duplicate());
}
parent->scene->push(cast(ctx->merging));
return true; return true;
} }
//OPTIMIZE: path? static void _repeat(LottieGroup* parent, Shape* path, RenderContext* ctx)
static void _repeat(LottieGroup* parent, unique_ptr<Shape> path, RenderContext* ctx)
{ {
Array<Shape*> propagators; Array<Shape*> propagators;
propagators.push(ctx->propagator); propagators.push(ctx->propagator);
@ -401,7 +405,7 @@ static void _repeat(LottieGroup* parent, unique_ptr<Shape> path, RenderContext*
for (auto propagator = propagators.begin(); propagator < propagators.end(); ++propagator) { for (auto propagator = propagators.begin(); propagator < propagators.end(); ++propagator) {
auto shape = static_cast<Shape*>((*propagator)->duplicate()); auto shape = static_cast<Shape*>((*propagator)->duplicate());
P(shape)->rs.path = P(path.get())->rs.path; P(shape)->rs.path = P(path)->rs.path;
auto opacity = repeater->interpOpacity ? mathLerp<uint8_t>(repeater->startOpacity, repeater->endOpacity, static_cast<float>(i + 1) / repeater->cnt) : repeater->startOpacity; auto opacity = repeater->interpOpacity ? mathLerp<uint8_t>(repeater->startOpacity, repeater->endOpacity, static_cast<float>(i + 1) / repeater->cnt) : repeater->startOpacity;
shape->opacity(opacity); shape->opacity(opacity);
@ -542,11 +546,12 @@ static void _updateRect(LottieGroup* parent, LottieObject** child, float frameNo
} }
if (!ctx->repeaters.empty()) { if (!ctx->repeaters.empty()) {
auto path = Shape::gen(); auto shape = rect->pooling();
_appendRect(path.get(), position.x - size.x * 0.5f, position.y - size.y * 0.5f, size.x, size.y, roundness, ctx->transform, rect->clockwise); shape->reset();
_repeat(parent, std::move(path), ctx); _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 { } 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); _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); auto size = ellipse->size(frameNo, exps);
if (!ctx->repeaters.empty()) { if (!ctx->repeaters.empty()) {
auto path = Shape::gen(); auto shape = ellipse->pooling();
_appendCircle(path.get(), position.x, position.y, size.x * 0.5f, size.y * 0.5f, ctx->transform, ellipse->clockwise); shape->reset();
_repeat(parent, std::move(path), ctx); _appendCircle(shape, position.x, position.y, size.x * 0.5f, size.y * 0.5f, ctx->transform, ellipse->clockwise);
_repeat(parent, shape, ctx);
} else { } 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); _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<LottiePath*>(*child); auto path = static_cast<LottiePath*>(*child);
if (!ctx->repeaters.empty()) { if (!ctx->repeaters.empty()) {
auto p = Shape::gen(); auto shape = path->pooling();
path->pathset(frameNo, P(p)->rs.path.cmds, P(p)->rs.path.pts, ctx->transform, ctx->roundness, exps); shape->reset();
_repeat(parent, std::move(p), ctx); path->pathset(frameNo, P(shape)->rs.path.cmds, P(shape)->rs.path.pts, ctx->transform, ctx->roundness, exps);
_repeat(parent, shape, ctx);
} else { } 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)) { 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); 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 direction = star->clockwise ? 1.0f : -1.0f;
auto hasRoundness = false; auto hasRoundness = false;
bool roundedCorner = (roundness > ROUNDNESS_EPSILON) && (mathZero(innerRoundness) || mathZero(outerRoundness)); bool roundedCorner = (roundness > ROUNDNESS_EPSILON) && (mathZero(innerRoundness) || mathZero(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; float x, y;
@ -798,10 +811,7 @@ static void _updateStar(LottieGroup* parent, LottiePolyStar* star, Matrix* trans
} }
shape->close(); shape->close();
if (roundedCorner) { if (roundedCorner) _applyRoundedCorner(shape, merging, outerRoundness, roundness, hasRoundness);
_applyRoundedCorner(shape, merging, outerRoundness, roundness, hasRoundness);
delete(shape);
}
} }
@ -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<RenderContext>& contexts, RenderContext* ctx, LottieExpressions* exps) static void _updatePolystar(LottieGroup* parent, LottieObject** child, float frameNo, TVG_UNUSED Inlist<RenderContext>& contexts, RenderContext* ctx, LottieExpressions* exps)
{ {
auto star= static_cast<LottiePolyStar*>(*child); auto star = static_cast<LottiePolyStar*>(*child);
//Optimize: Can we skip the individual coords transform? //Optimize: Can we skip the individual coords transform?
Matrix matrix; Matrix matrix;
@ -890,12 +900,13 @@ static void _updatePolystar(LottieGroup* parent, LottieObject** child, float fra
auto identity = mathIdentity((const Matrix*)&matrix); auto identity = mathIdentity((const Matrix*)&matrix);
if (!ctx->repeaters.empty()) { if (!ctx->repeaters.empty()) {
auto p = Shape::gen(); auto shape = star->pooling();
if (star->type == LottiePolyStar::Star) _updateStar(parent, star, identity ? nullptr : &matrix, ctx->roundness, frameNo, p.get(), exps); shape->reset();
else _updatePolygon(parent, star, identity ? nullptr : &matrix, frameNo, p.get(), exps); if (star->type == LottiePolyStar::Star) _updateStar(parent, star, identity ? nullptr : &matrix, ctx->roundness, frameNo, shape, exps);
_repeat(parent, std::move(p), ctx); else _updatePolygon(parent, star, identity ? nullptr : &matrix, frameNo, shape, exps);
_repeat(parent, shape, ctx);
} else { } 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); 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); else _updatePolygon(parent, star, identity ? nullptr : &matrix, frameNo, ctx->merging, exps);
P(ctx->merging)->update(RenderUpdateFlag::Path); P(ctx->merging)->update(RenderUpdateFlag::Path);

View file

@ -37,6 +37,16 @@
/* External Class Implementation */ /* 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() void LottieSlot::reset()
{ {
if (!overriden) return; if (!overriden) return;

View file

@ -266,15 +266,18 @@ struct LottieTrimpath : LottieObject
}; };
struct LottieShape : LottieObject struct LottieShape : LottieObject, LottieRenderPooler<tvg::Shape>
{ {
virtual ~LottieShape() {}
bool clockwise = true; //clockwise or counter-clockwise bool clockwise = true; //clockwise or counter-clockwise
virtual ~LottieShape() {}
bool mergeable() override bool mergeable() override
{ {
return true; return true;
} }
void prepare(LottieObject::Type type);
}; };
@ -292,7 +295,7 @@ struct LottiePath : LottieShape
{ {
void prepare() void prepare()
{ {
LottieObject::type = LottieObject::Path; LottieShape::prepare(LottieObject::Path);
} }
LottiePathSet pathset; LottiePathSet pathset;
@ -303,7 +306,7 @@ struct LottieRect : LottieShape
{ {
void prepare() void prepare()
{ {
LottieObject::type = LottieObject::Rect; LottieShape::prepare(LottieObject::Rect);
} }
LottiePosition position = Point{0.0f, 0.0f}; LottiePosition position = Point{0.0f, 0.0f};
@ -318,7 +321,7 @@ struct LottiePolyStar : LottieShape
void prepare() void prepare()
{ {
LottieObject::type = LottieObject::Polystar; LottieShape::prepare(LottieObject::Polystar);
} }
LottiePosition position = Point{0.0f, 0.0f}; LottiePosition position = Point{0.0f, 0.0f};
@ -336,7 +339,7 @@ struct LottieEllipse : LottieShape
{ {
void prepare() void prepare()
{ {
LottieObject::type = LottieObject::Ellipse; LottieShape::prepare(LottieObject::Ellipse);
} }
LottiePosition position = Point{0.0f, 0.0f}; LottiePosition position = Point{0.0f, 0.0f};
@ -552,7 +555,7 @@ struct LottieGroup : LottieObject
return nullptr; return nullptr;
} }
Scene* scene = nullptr; //tvg render data Scene* scene = nullptr;
Array<LottieObject*> children; Array<LottieObject*> children;
bool reqFragment = false; //requirment to fragment the render context bool reqFragment = false; //requirment to fragment the render context

View file

@ -158,16 +158,22 @@ Iterator* Paint::Impl::iterator()
} }
Paint* Paint::Impl::duplicate() Paint* Paint::Impl::duplicate(Paint* ret)
{ {
Paint* ret; if (ret) ret->composite(nullptr, CompositeMethod::None);
PAINT_METHOD(ret, duplicate());
PAINT_METHOD(ret, duplicate(ret));
//duplicate Transform //duplicate Transform
if (rTransform) { if (rTransform) {
ret->pImpl->rTransform = new RenderTransform(); if (!ret->pImpl->rTransform) {
ret->pImpl->rTransform = new RenderTransform();
}
*ret->pImpl->rTransform = *rTransform; *ret->pImpl->rTransform = *rTransform;
ret->pImpl->renderFlag |= RenderUpdateFlag::Transform; ret->pImpl->renderFlag |= RenderUpdateFlag::Transform;
} else {
delete(ret->pImpl->rTransform);
ret->pImpl->rTransform = nullptr;
} }
ret->pImpl->opacity = opacity; ret->pImpl->opacity = opacity;

View file

@ -138,7 +138,7 @@ namespace tvg
bool bounds(float* x, float* y, float* w, float* h, bool transformed, bool stroking); bool bounds(float* x, float* y, float* w, float* h, bool transformed, bool stroking);
RenderData update(RenderMethod* renderer, const RenderTransform* pTransform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper = false); RenderData update(RenderMethod* renderer, const RenderTransform* pTransform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper = false);
bool render(RenderMethod* renderer); bool render(RenderMethod* renderer);
Paint* duplicate(); Paint* duplicate(Paint* ret = nullptr);
}; };
} }

View file

@ -189,19 +189,21 @@ struct Picture::Impl
} }
} }
Paint* duplicate() Paint* duplicate(Paint* ret)
{ {
if (ret) TVGERR("RENDERER", "TODO: duplicate()");
load(); load();
auto ret = Picture::gen().release(); auto picture = Picture::gen().release();
auto dup = ret->pImpl; auto dup = picture->pImpl;
if (paint) dup->paint = paint->duplicate(); if (paint) dup->paint = paint->duplicate();
if (loader) { if (loader) {
dup->loader = loader; dup->loader = loader;
++dup->loader->sharing; ++dup->loader->sharing;
PP(ret)->renderFlag |= RenderUpdateFlag::Image; PP(picture)->renderFlag |= RenderUpdateFlag::Image;
} }
dup->surface = surface; dup->surface = surface;
@ -215,7 +217,7 @@ struct Picture::Impl
memcpy(dup->rm.triangles, rm.triangles, sizeof(Polygon) * rm.triangleCnt); memcpy(dup->rm.triangles, rm.triangles, sizeof(Polygon) * rm.triangleCnt);
} }
return ret; return picture;
} }
Iterator* iterator() Iterator* iterator()

View file

@ -147,6 +147,31 @@ struct RenderStroke
bool simultaneous = true; bool simultaneous = true;
} trim; } 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<float*>(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() ~RenderStroke()
{ {
free(dashPattern); free(dashPattern);

View file

@ -198,10 +198,12 @@ struct Scene::Impl
return true; return true;
} }
Paint* duplicate() Paint* duplicate(Paint* ret)
{ {
auto ret = Scene::gen().release(); if (ret) TVGERR("RENDERER", "TODO: duplicate()");
auto dup = ret->pImpl;
auto scene = Scene::gen().release();
auto dup = scene->pImpl;
for (auto paint : paints) { for (auto paint : paints) {
auto cdup = paint->duplicate(); auto cdup = paint->duplicate();
@ -209,7 +211,7 @@ struct Scene::Impl
dup->paints.push_back(cdup); dup->paints.push_back(cdup);
} }
return ret; return scene;
} }
void clear(bool free) void clear(bool free)

View file

@ -34,6 +34,7 @@ struct Shape::Impl
RenderData rd = nullptr; //engine data RenderData rd = nullptr; //engine data
Shape* shape; Shape* shape;
uint8_t flag = RenderUpdateFlag::None; uint8_t flag = RenderUpdateFlag::None;
uint8_t opacity; //for composition uint8_t opacity; //for composition
bool needComp = false; //composite or not bool needComp = false; //composite or not
@ -359,47 +360,40 @@ struct Shape::Impl
this->flag |= flag; this->flag |= flag;
} }
Paint* duplicate() Paint* duplicate(Paint* ret)
{ {
auto ret = Shape::gen().release(); auto shape = static_cast<Shape*>(ret);
auto dup = ret->pImpl; 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; dup->rs.rule = rs.rule;
//Color //Color
memcpy(dup->rs.color, rs.color, sizeof(rs.color)); memcpy(dup->rs.color, rs.color, sizeof(rs.color));
dup->flag = RenderUpdateFlag::Color;
//Path //Path
if (rs.path.cmds.count > 0 && rs.path.pts.count > 0) { dup->rs.path.cmds.push(rs.path.cmds);
dup->rs.path.cmds = rs.path.cmds; dup->rs.path.pts.push(rs.path.pts);
dup->rs.path.pts = rs.path.pts;
dup->flag |= RenderUpdateFlag::Path;
}
//Stroke //Stroke
if (rs.stroke) { if (rs.stroke) {
dup->rs.stroke = new RenderStroke(); if (!dup->rs.stroke) dup->rs.stroke = new RenderStroke;
*dup->rs.stroke = *rs.stroke; *dup->rs.stroke = *rs.stroke;
memcpy(dup->rs.stroke->color, rs.stroke->color, sizeof(rs.stroke->color)); } else {
if (rs.stroke->dashCnt > 0) { delete(dup->rs.stroke);
dup->rs.stroke->dashPattern = static_cast<float*>(malloc(sizeof(float) * rs.stroke->dashCnt)); dup->rs.stroke = nullptr;
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;
} }
//Fill //Fill
if (rs.fill) { if (rs.fill) dup->rs.fill = rs.fill->duplicate();
dup->rs.fill = rs.fill->duplicate(); else dup->rs.fill = nullptr;
dup->flag |= RenderUpdateFlag::Gradient;
}
return ret; return shape;
} }
Iterator* iterator() Iterator* iterator()

View file

@ -152,12 +152,14 @@ struct Text::Impl
return true; return true;
} }
Paint* duplicate() Paint* duplicate(Paint* ret)
{ {
if (ret) TVGERR("RENDERER", "TODO: duplicate()");
load(); load();
auto ret = Text::gen().release(); auto text = Text::gen().release();
auto dup = ret->pImpl; auto dup = text->pImpl;
if (paint) dup->paint = static_cast<Shape*>(paint->duplicate()); if (paint) dup->paint = static_cast<Shape*>(paint->duplicate());
if (loader) { if (loader) {
@ -169,7 +171,7 @@ struct Text::Impl
dup->italic = italic; dup->italic = italic;
dup->fontSize = fontSize; dup->fontSize = fontSize;
return ret; return text;
} }
Iterator* iterator() Iterator* iterator()