mirror of
https://github.com/thorvg/thorvg.git
synced 2025-07-24 07:08:58 +00:00
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
This commit is contained in:
parent
89752ff661
commit
d386a5654a
4 changed files with 76 additions and 47 deletions
|
@ -233,73 +233,100 @@ static void _updateStroke(LottieStroke* stroke, float frameNo, RenderContext* ct
|
|||
}
|
||||
|
||||
|
||||
bool LottieBuilder::fragmented(LottieGroup* parent, LottieObject** child, Inlist<RenderContext>& contexts, RenderContext* ctx)
|
||||
bool LottieBuilder::fragmented(LottieGroup* parent, LottieObject** child, Inlist<RenderContext>& 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<Shape*>(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<RenderContext>& contexts, RenderContext* ctx)
|
||||
bool LottieBuilder::updateSolidStroke(LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& contexts, RenderContext* ctx)
|
||||
{
|
||||
if (fragmented(parent, child, contexts, ctx)) return;
|
||||
|
||||
auto stroke = static_cast<LottieSolidStroke*>(*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<LottieStroke*>(stroke), frameNo, ctx, tween, exps);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void LottieBuilder::updateGradientStroke(LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& contexts, RenderContext* ctx)
|
||||
bool LottieBuilder::updateGradientStroke(LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& contexts, RenderContext* ctx)
|
||||
{
|
||||
if (fragmented(parent, child, contexts, ctx)) return;
|
||||
|
||||
auto stroke = static_cast<LottieGradientStroke*>(*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<LottieStroke*>(stroke), frameNo, ctx, tween, exps);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void LottieBuilder::updateSolidFill(LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& contexts, RenderContext* ctx)
|
||||
bool LottieBuilder::updateSolidFill(LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& contexts, RenderContext* ctx)
|
||||
{
|
||||
if (fragmented(parent, child, contexts, ctx)) return;
|
||||
|
||||
auto fill = static_cast<LottieSolidFill*>(*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<RenderContext>& contexts, RenderContext* ctx)
|
||||
bool LottieBuilder::updateGradientFill(LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& contexts, RenderContext* ctx)
|
||||
{
|
||||
if (fragmented(parent, child, contexts, ctx)) return;
|
||||
|
||||
auto fill = static_cast<LottieGradientFill*>(*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, Inlist<Re
|
|||
while (!contexts.empty()) {
|
||||
auto ctx = contexts.front();
|
||||
ctx->reqFragment = 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, Inlist<Re
|
|||
break;
|
||||
}
|
||||
case LottieObject::SolidFill: {
|
||||
updateSolidFill(parent, child, frameNo, contexts, ctx);
|
||||
stop = updateSolidFill(parent, child, frameNo, contexts, ctx);
|
||||
break;
|
||||
}
|
||||
case LottieObject::SolidStroke: {
|
||||
updateSolidStroke(parent, child, frameNo, contexts, ctx);
|
||||
stop = updateSolidStroke(parent, child, frameNo, contexts, ctx);
|
||||
break;
|
||||
}
|
||||
case LottieObject::GradientFill: {
|
||||
updateGradientFill(parent, child, frameNo, contexts, ctx);
|
||||
stop = updateGradientFill(parent, child, frameNo, contexts, ctx);
|
||||
break;
|
||||
}
|
||||
case LottieObject::GradientStroke: {
|
||||
updateGradientStroke(parent, child, frameNo, contexts, ctx);
|
||||
stop = updateGradientStroke(parent, child, frameNo, contexts, ctx);
|
||||
break;
|
||||
}
|
||||
case LottieObject::Rect: {
|
||||
|
@ -832,7 +860,7 @@ void LottieBuilder::updateChildren(LottieGroup* parent, float frameNo, Inlist<Re
|
|||
}
|
||||
|
||||
//stop processing for those invisible contents
|
||||
if (ctx->propagator->opacity() == skip) break;
|
||||
if (stop || ctx->propagator->opacity() == skip) break;
|
||||
}
|
||||
delete(ctx);
|
||||
}
|
||||
|
|
|
@ -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<RenderContext>& contexts, RenderContext* ctx);
|
||||
bool fragmented(LottieGroup* parent, LottieObject** child, Inlist<RenderContext>& 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<RenderContext>& contexts, uint8_t skip);
|
||||
void updateGroup(LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& pcontexts, RenderContext* ctx, uint8_t skip);
|
||||
void updateTransform(LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& contexts, RenderContext* ctx);
|
||||
void updateSolidFill(LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& contexts, RenderContext* ctx);
|
||||
void updateSolidStroke(LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& contexts, RenderContext* ctx);
|
||||
void updateGradientFill(LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& contexts, RenderContext* ctx);
|
||||
void updateGradientStroke(LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& contexts, RenderContext* ctx);
|
||||
bool updateSolidFill(LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& contexts, RenderContext* ctx);
|
||||
bool updateSolidStroke(LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& contexts, RenderContext* ctx);
|
||||
bool updateGradientFill(LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& contexts, RenderContext* ctx);
|
||||
bool updateGradientStroke(LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& contexts, RenderContext* ctx);
|
||||
void updateRect(LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& contexts, RenderContext* ctx);
|
||||
void updateEllipse(LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& contexts, RenderContext* ctx);
|
||||
void updatePath(LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& contexts, RenderContext* ctx);
|
||||
|
|
|
@ -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<LinearGradient*>(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
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue