From d37c5002623a44d0d16581f6cbeee91c928fb535 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Wed, 25 Oct 2023 18:55:05 +0900 Subject: [PATCH] lottie: introduced static layer cache. lottie builder doesn't need to rebuild the layer object if it has no any animation frame data. That case, we can cache the layer scene in order to reuse it. Tested on local machine (single thread): - Lottie: appx. 2ms enhanced. - Binary: +204 --- src/loaders/lottie/tvgLottieBuilder.cpp | 49 +++++++++++++++++++------ src/loaders/lottie/tvgLottieModel.cpp | 1 + src/loaders/lottie/tvgLottieModel.h | 5 ++- src/loaders/lottie/tvgLottieParser.cpp | 6 +-- 4 files changed, 45 insertions(+), 16 deletions(-) diff --git a/src/loaders/lottie/tvgLottieBuilder.cpp b/src/loaders/lottie/tvgLottieBuilder.cpp index 302d03ae..4dbccba3 100644 --- a/src/loaders/lottie/tvgLottieBuilder.cpp +++ b/src/loaders/lottie/tvgLottieBuilder.cpp @@ -84,7 +84,7 @@ struct RenderContext static void _updateChildren(LottieGroup* parent, float frameNo, queue& contexts); -static void _updateLayer(LottieLayer* root, LottieLayer* layer, float frameNo); +static void _updateLayer(LottieLayer* root, LottieLayer* layer, float frameNo, bool caching); static bool _buildComposition(LottieComposition* comp, LottieGroup* parent); static void _rotateX(Matrix* m, float degree) @@ -803,15 +803,14 @@ static void _updateChildren(LottieGroup* parent, float frameNo, queuechildren.count == 0) return; frameNo = precomp->remap(frameNo); - //TODO: skip if the layer is static. for (auto child = precomp->children.end() - 1; child >= precomp->children.data; --child) { - _updateLayer(precomp, static_cast(*child), frameNo); + _updateLayer(precomp, static_cast(*child), frameNo, caching); } //clip the layer viewport @@ -875,12 +874,18 @@ static void _updateMaskings(LottieLayer* layer, float frameNo) } -static bool _updateMatte(LottieLayer* root, LottieLayer* layer, float frameNo) +static bool _updateMatte(LottieLayer* root, LottieLayer* layer, float frameNo, bool caching) { auto target = layer->matte.target; if (!target) return true; - _updateLayer(root, target, frameNo); + if (target->cache.scene) { + //TODO: remove duplicate, share the scene. + layer->scene->composite(cast(target->cache.scene->duplicate()), layer->matte.type); + return true; + } + + _updateLayer(root, target, frameNo, caching); if (target->scene) { layer->scene->composite(cast(target->scene), layer->matte.type); @@ -894,18 +899,31 @@ static bool _updateMatte(LottieLayer* root, LottieLayer* layer, float frameNo) } -static void _updateLayer(LottieLayer* root, LottieLayer* layer, float frameNo) +static void _updateLayer(LottieLayer* root, LottieLayer* layer, float frameNo, bool caching) { layer->scene = nullptr; //visibility if (frameNo < layer->inFrame || frameNo >= layer->outFrame) return; + //static layer, no need to update it again. use a cache. + if (layer->cache.scene) { + //TODO: remove duplicate, share the scene. + root->scene->push(cast(layer->cache.scene->duplicate())); + return; + } + _updateTransform(layer, frameNo); //full transparent scene. no need to perform if (layer->type != LottieLayer::Null && layer->cache.opacity == 0) return; + //figure out this scene is static, reusable. + auto cache = false; + if (layer->statical && !layer->cache.scene && !caching) { + cache = caching = true; + } + //Prepare render data layer->scene = Scene::gen().release(); @@ -916,14 +934,14 @@ static void _updateLayer(LottieLayer* root, LottieLayer* layer, float frameNo) if (layer->matte.target && layer->masks.count > 0) TVGERR("LOTTIE", "FIXME: Matte + Masking??"); - if (!_updateMatte(root, layer, frameNo)) return; + if (!_updateMatte(root, layer, frameNo, caching)) return; _updateMaskings(layer, frameNo); switch (layer->type) { case LottieLayer::Precomp: { if (!layer->children.empty()) { - _updatePrecomp(layer, frameNo); + _updatePrecomp(layer, frameNo, caching); } break; } @@ -943,6 +961,12 @@ static void _updateLayer(LottieLayer* root, LottieLayer* layer, float frameNo) //the given matte source was composited by the target earlier. if (!layer->matteSrc) root->scene->push(cast(layer->scene)); + + //cache this static layer scene + if (cache) { + //TODO: remove duplicate, share the scene. + layer->cache.scene = layer->scene->duplicate(); + } } @@ -1037,10 +1061,14 @@ static bool _buildComposition(LottieComposition* comp, LottieGroup* parent) _bulidHierarchy(parent, child->matte.target); //precomp referencing if (child->matte.target->refId) _buildReference(comp, child->matte.target); + child->statical &= child->matte.target->statical; } _bulidHierarchy(parent, child); _checkFragment(static_cast(*c)); + + child->statical &= parent->statical; + parent->statical &= child->statical; } return true; } @@ -1069,9 +1097,8 @@ bool LottieBuilder::update(LottieComposition* comp, float frameNo) } //update children layers - //TODO: skip if the layer is static. for (auto child = root->children.end() - 1; child >= root->children.data; --child) { - _updateLayer(root, static_cast(*child), frameNo); + _updateLayer(root, static_cast(*child), frameNo, false); } return true; } diff --git a/src/loaders/lottie/tvgLottieModel.cpp b/src/loaders/lottie/tvgLottieModel.cpp index 493c0b10..662b4418 100644 --- a/src/loaders/lottie/tvgLottieModel.cpp +++ b/src/loaders/lottie/tvgLottieModel.cpp @@ -145,6 +145,7 @@ void LottieGroup::prepare(LottieObject::Type type) void LottieLayer::prepare() { if (transform) statical &= transform->statical; + if (timeRemap.frames) statical = false; /* if layer is hidden, only useful data is its transform matrix. so force it to be a Null Layer and release all resource. */ diff --git a/src/loaders/lottie/tvgLottieModel.h b/src/loaders/lottie/tvgLottieModel.h index 97dcf3e5..36255d7b 100644 --- a/src/loaders/lottie/tvgLottieModel.h +++ b/src/loaders/lottie/tvgLottieModel.h @@ -173,7 +173,7 @@ struct LottieGradient } else { colorStops.count = populate(colorStops.value); } - if (start.frames || end.frames || height.frames || angle.frames || colorStops.frames) return true; + if (start.frames || end.frames || height.frames || angle.frames || opacity.frames || colorStops.frames) return true; return false; } @@ -475,7 +475,6 @@ struct LottieGroup : LottieObject void prepare(LottieObject::Type type = LottieObject::Group); Scene* scene = nullptr; //tvg render data - Array children; bool reqFragment = false; //requirment to fragment the render context @@ -500,6 +499,7 @@ struct LottieLayer : LottieGroup delete(matte.target); delete(transform); + delete(cache.scene); } uint8_t opacity(float frameNo) @@ -539,6 +539,7 @@ struct LottieLayer : LottieGroup float frameNo = -1.0f; Matrix matrix; uint8_t opacity; + Paint* scene = nullptr; //tvg statc render ddata } cache; Type type = Null; diff --git a/src/loaders/lottie/tvgLottieParser.cpp b/src/loaders/lottie/tvgLottieParser.cpp index 3a78a43c..d7931015 100644 --- a/src/loaders/lottie/tvgLottieParser.cpp +++ b/src/loaders/lottie/tvgLottieParser.cpp @@ -971,7 +971,9 @@ void LottieParser::parseMasks(LottieLayer* layer) { enterArray(); while (nextArrayValue()) { - layer->masks.push(parseMask()); + auto mask = parseMask(); + if (mask->dynamic()) layer->statical = false; + layer->masks.push(mask); } } @@ -1053,13 +1055,11 @@ LottieLayer* LottieParser::parseLayers() auto matte = static_cast(root->children.last()); if (matte->matteSrc) { layer->matte.target = matte; - layer->statical &= layer->matte.target->statical; } else { TVGLOG("LOTTIE", "Matte Source(%s) is not designated?", matte->name); } root->children.last() = layer; } - root->statical &= layer->statical; } } root->prepare();