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
This commit is contained in:
Hermet Park 2023-10-25 18:55:05 +09:00 committed by Hermet Park
parent 978f85c3ea
commit d37c500262
4 changed files with 45 additions and 16 deletions

View file

@ -84,7 +84,7 @@ struct RenderContext
static void _updateChildren(LottieGroup* parent, float frameNo, queue<RenderContext>& 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, queue<RenderCont
}
static void _updatePrecomp(LottieLayer* precomp, float frameNo)
static void _updatePrecomp(LottieLayer* precomp, float frameNo, bool caching)
{
if (precomp->children.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<LottieLayer*>(*child), frameNo);
_updateLayer(precomp, static_cast<LottieLayer*>(*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<LottieGroup*>(*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<LottieLayer*>(*child), frameNo);
_updateLayer(root, static_cast<LottieLayer*>(*child), frameNo, false);
}
return true;
}

View file

@ -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. */

View file

@ -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<LottieObject*> 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;

View file

@ -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<LottieLayer*>(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();