From d386a5654a238bde2209a6fb07f94a87baeab256 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Wed, 26 Feb 2025 22:44:44 +0900 Subject: [PATCH] Lottie: optimize rendering performance implemented an aggressive culling strategy to eliminate unnecessary renderings if the rendering visuals were hidden by other overlaid opaque fills or strokes this pretty improves the performance for those scenarios when rendering sequences are fragmented by fills/strokes. Performance has been improved ~7% with those cases --- src/loaders/lottie/tvgLottieBuilder.cpp | 86 ++++++++++++++++--------- src/loaders/lottie/tvgLottieBuilder.h | 18 +++--- src/loaders/lottie/tvgLottieModel.cpp | 14 ++-- src/loaders/lottie/tvgLottieModel.h | 5 +- 4 files changed, 76 insertions(+), 47 deletions(-) diff --git a/src/loaders/lottie/tvgLottieBuilder.cpp b/src/loaders/lottie/tvgLottieBuilder.cpp index 36130379..32993498 100644 --- a/src/loaders/lottie/tvgLottieBuilder.cpp +++ b/src/loaders/lottie/tvgLottieBuilder.cpp @@ -233,73 +233,100 @@ static void _updateStroke(LottieStroke* stroke, float frameNo, RenderContext* ct } -bool LottieBuilder::fragmented(LottieGroup* parent, LottieObject** child, Inlist& contexts, RenderContext* ctx) +bool LottieBuilder::fragmented(LottieGroup* parent, LottieObject** child, Inlist& contexts, RenderContext* ctx, RenderFragment fragment) { - if (!ctx->reqFragment) return false; - if (ctx->fragmenting) return true; + if (ctx->fragment) return true; - contexts.back(new RenderContext(*ctx, static_cast(PAINT(ctx->propagator)->duplicate(parent->pooling())))); - auto fragment = contexts.tail; - fragment->begin = child - 1; - ctx->fragmenting = true; + if (!ctx->reqFragment || ctx->fragment == RenderFragment::ByNone) return false; + + contexts.back(new RenderContext(*ctx, (Shape*)(PAINT(ctx->propagator)->duplicate(parent->pooling())))); + + contexts.tail->begin = child - 1; + ctx->fragment = fragment; return false; } -void LottieBuilder::updateSolidStroke(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx) +bool LottieBuilder::updateSolidStroke(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx) { - if (fragmented(parent, child, contexts, ctx)) return; - auto stroke = static_cast(*child); + auto opacity = stroke->opacity(frameNo, tween, exps); + + //interrupted by fully opaque, stop the current rendering + if (ctx->fragment == RenderFragment::ByStroke && opacity == 255) return true; + + if (fragmented(parent, child, contexts, ctx, RenderFragment::ByStroke)) return false; + if (opacity == 0) return false; ctx->merging = nullptr; auto color = stroke->color(frameNo, tween, exps); - ctx->propagator->strokeFill(color.rgb[0], color.rgb[1], color.rgb[2], stroke->opacity(frameNo, tween, exps)); + ctx->propagator->strokeFill(color.rgb[0], color.rgb[1], color.rgb[2], opacity); _updateStroke(static_cast(stroke), frameNo, ctx, tween, exps); + + return false; } -void LottieBuilder::updateGradientStroke(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx) +bool LottieBuilder::updateGradientStroke(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx) { - if (fragmented(parent, child, contexts, ctx)) return; - auto stroke = static_cast(*child); + auto opacity = stroke->opacity(frameNo, tween, exps); + + //interrupted by fully opaque, stop the current rendering + if (ctx->fragment == RenderFragment::ByStroke && stroke->opaque && opacity == 255) return true; + + if (fragmented(parent, child, contexts, ctx, RenderFragment::ByStroke)) return false; ctx->merging = nullptr; - ctx->propagator->strokeFill(stroke->fill(frameNo, tween, exps)); + if (auto val = stroke->fill(frameNo, opacity, tween, exps)) ctx->propagator->strokeFill(val); _updateStroke(static_cast(stroke), frameNo, ctx, tween, exps); + + return false; } -void LottieBuilder::updateSolidFill(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx) +bool LottieBuilder::updateSolidFill(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx) { - if (fragmented(parent, child, contexts, ctx)) return; - auto fill = static_cast(*child); + auto opacity = fill->opacity(frameNo, tween, exps); + + //interrupted by fully opaque, stop the current rendering + if (ctx->fragment == RenderFragment::ByFill && opacity == 255) return true; + + if (fragmented(parent, child, contexts, ctx, RenderFragment::ByFill)) return false; + if (opacity == 0) return false; ctx->merging = nullptr; auto color = fill->color(frameNo, tween, exps); - ctx->propagator->fill(color.rgb[0], color.rgb[1], color.rgb[2], fill->opacity(frameNo, tween, exps)); + ctx->propagator->fill(color.rgb[0], color.rgb[1], color.rgb[2], opacity); ctx->propagator->fill(fill->rule); if (ctx->propagator->strokeWidth() > 0) ctx->propagator->order(true); + + return false; } -void LottieBuilder::updateGradientFill(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx) +bool LottieBuilder::updateGradientFill(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx) { - if (fragmented(parent, child, contexts, ctx)) return; - auto fill = static_cast(*child); + auto opacity = fill->opacity(frameNo, tween, exps); + + //interrupted by fully opaque, stop the current rendering + if (ctx->fragment == RenderFragment::ByFill && fill->opaque && opacity == 255) return true; + + if (fragmented(parent, child, contexts, ctx, RenderFragment::ByFill)) return false; ctx->merging = nullptr; - //TODO: reuse the fill instance? - ctx->propagator->fill(fill->fill(frameNo, tween, exps)); + + if (auto val = fill->fill(frameNo, opacity, tween, exps)) ctx->propagator->fill(val); ctx->propagator->fill(fill->rule); if (ctx->propagator->strokeWidth() > 0) ctx->propagator->order(true); + + return false; } @@ -769,6 +796,7 @@ void LottieBuilder::updateChildren(LottieGroup* parent, float frameNo, InlistreqFragment = parent->reqFragment; + auto stop = false; for (auto child = ctx->begin; child >= parent->children.data; --child) { //Here switch-case statements are more performant than virtual methods. switch ((*child)->type) { @@ -781,19 +809,19 @@ void LottieBuilder::updateChildren(LottieGroup* parent, float frameNo, Inlistpropagator->opacity() == skip) break; + if (stop || ctx->propagator->opacity() == skip) break; } delete(ctx); } diff --git a/src/loaders/lottie/tvgLottieBuilder.h b/src/loaders/lottie/tvgLottieBuilder.h index 7deb8901..bd995523 100644 --- a/src/loaders/lottie/tvgLottieBuilder.h +++ b/src/loaders/lottie/tvgLottieBuilder.h @@ -46,6 +46,8 @@ struct RenderRepeater bool inorder; }; +enum RenderFragment : uint8_t {ByNone = 0, ByFill, ByStroke}; + struct RenderContext { INLIST_ITEM(RenderContext); @@ -58,7 +60,7 @@ struct RenderContext LottieRoundnessModifier* roundness = nullptr; LottieOffsetModifier* offset = nullptr; LottieModifier* modifier = nullptr; - bool fragmenting = false; //render context has been fragmented by filling + RenderFragment fragment = ByNone; //render context has been fragmented bool reqFragment = false; //requirement to fragment the render context RenderContext(Shape* propagator) @@ -76,12 +78,12 @@ struct RenderContext delete(offset); } - RenderContext(const RenderContext& rhs, Shape* propagator, bool mergeable = false) + RenderContext(const RenderContext& rhs, Shape* propagator, bool mergeable = false) : propagator(propagator) { if (mergeable) merging = rhs.merging; propagator->ref(); - this->propagator = propagator; repeaters = rhs.repeaters; + fragment = rhs.fragment; if (rhs.roundness) { roundness = new LottieRoundnessModifier(rhs.roundness->buffer, rhs.roundness->r); update(roundness); @@ -138,7 +140,7 @@ struct LottieBuilder private: void appendRect(Shape* shape, Point& pos, Point& size, float r, bool clockwise, RenderContext* ctx); - bool fragmented(LottieGroup* parent, LottieObject** child, Inlist& contexts, RenderContext* ctx); + bool fragmented(LottieGroup* parent, LottieObject** child, Inlist& contexts, RenderContext* ctx, RenderFragment fragment); void updateStrokeEffect(LottieLayer* layer, LottieFxStroke* effect, float frameNo); void updateEffect(LottieLayer* layer, float frameNo); @@ -154,10 +156,10 @@ private: void updateChildren(LottieGroup* parent, float frameNo, Inlist& contexts, uint8_t skip); void updateGroup(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& pcontexts, RenderContext* ctx, uint8_t skip); void updateTransform(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx); - void updateSolidFill(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx); - void updateSolidStroke(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx); - void updateGradientFill(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx); - void updateGradientStroke(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx); + bool updateSolidFill(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx); + bool updateSolidStroke(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx); + bool updateGradientFill(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx); + bool updateGradientStroke(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx); void updateRect(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx); void updateEllipse(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx); void updatePath(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx); diff --git a/src/loaders/lottie/tvgLottieModel.cpp b/src/loaders/lottie/tvgLottieModel.cpp index f4e7c666..bca2379e 100644 --- a/src/loaders/lottie/tvgLottieModel.cpp +++ b/src/loaders/lottie/tvgLottieModel.cpp @@ -311,6 +311,7 @@ uint32_t LottieGradient::populate(ColorStop& color, size_t count) } aidx += 2; } + if (cs.a < 255) opaque = false; output.push(cs); } @@ -321,6 +322,7 @@ uint32_t LottieGradient::populate(ColorStop& color, size_t count) cs.g = (uint8_t)nearbyint((*color.input)[cidx + 2] * 255.0f); cs.b = (uint8_t)nearbyint((*color.input)[cidx + 3] * 255.0f); cs.a = (output.count > 0) ? output.last().a : 255; + if (cs.a < 255) opaque = false; output.push(cs); cidx += 4; } @@ -329,6 +331,7 @@ uint32_t LottieGradient::populate(ColorStop& color, size_t count) while (aidx < color.input->count) { cs.offset = (*color.input)[aidx]; cs.a = (uint8_t)nearbyint((*color.input)[aidx + 1] * 255.0f); + if (cs.a < 255) opaque = false; if (output.count > 0) { cs.r = output.last().r; cs.g = output.last().g; @@ -348,12 +351,11 @@ uint32_t LottieGradient::populate(ColorStop& color, size_t count) } -Fill* LottieGradient::fill(float frameNo, Tween& tween, LottieExpressions* exps) +Fill* LottieGradient::fill(float frameNo, uint8_t opacity, Tween& tween, LottieExpressions* exps) { - auto opacity = this->opacity(frameNo, tween, exps); if (opacity == 0) return nullptr; - Fill* fill = nullptr; + Fill* fill; auto s = start(frameNo, tween, exps); auto e = end(frameNo, tween, exps); @@ -361,11 +363,9 @@ Fill* LottieGradient::fill(float frameNo, Tween& tween, LottieExpressions* exps) if (id == 1) { fill = LinearGradient::gen(); static_cast(fill)->linear(s.x, s.y, e.x, e.y); - } //Radial Gradient - if (id == 2) { + } else { fill = RadialGradient::gen(); - auto w = fabsf(e.x - s.x); auto h = fabsf(e.y - s.y); auto r = (w > h) ? (w + 0.375f * h) : (h + 0.375f * w); @@ -384,8 +384,6 @@ Fill* LottieGradient::fill(float frameNo, Tween& tween, LottieExpressions* exps) } } - if (!fill) return nullptr; - colorStops(frameNo, fill, tween, exps); //multiply the current opacity with the fill diff --git a/src/loaders/lottie/tvgLottieModel.h b/src/loaders/lottie/tvgLottieModel.h index e8837aac..f2bea8ea 100644 --- a/src/loaders/lottie/tvgLottieModel.h +++ b/src/loaders/lottie/tvgLottieModel.h @@ -701,7 +701,7 @@ struct LottieGradient : LottieObject } uint32_t populate(ColorStop& color, size_t count); - Fill* fill(float frameNo, Tween& tween, LottieExpressions* exps); + Fill* fill(float frameNo, uint8_t opacity, Tween& tween, LottieExpressions* exps); LottieScalar start = Point{0.0f, 0.0f}; LottieScalar end = Point{0.0f, 0.0f}; @@ -709,7 +709,8 @@ struct LottieGradient : LottieObject LottieFloat angle = 0.0f; LottieOpacity opacity = 255; LottieColorStop colorStops; - uint8_t id = 0; //1: linear, 2: radial + uint8_t id = 0; //1: linear, 2: radial + bool opaque = true; //fully opaque or not };