lottie: introduced LottieRenderPooler

LottieRenderPooler is designed to manages rendering
instances(paints) efficiently through a simple pooling
mechanism to resuse among the animations

This replaces the previous individusal paints implemenations
among solidfill, precomp vierport and pictures,
also fix a potential paints corruption problems.
This commit is contained in:
Hermet Park 2024-07-12 15:13:10 +09:00
parent d13606e14b
commit 1131695ce4
5 changed files with 101 additions and 50 deletions

View file

@ -11,6 +11,7 @@ source_file = [
'tvgLottieParser.h',
'tvgLottieParserHandler.h',
'tvgLottieProperty.h',
'tvgLottieRenderPooler.h',
'tvgLottieAnimation.cpp',
'tvgLottieBuilder.cpp',
'tvgLottieExpressions.cpp',

View file

@ -28,7 +28,6 @@
#include "tvgPaint.h"
#include "tvgShape.h"
#include "tvgInlist.h"
#include "tvgTaskScheduler.h"
#include "tvgLottieModel.h"
#include "tvgLottieBuilder.h"
#include "tvgLottieExpressions.h"
@ -1043,44 +1042,24 @@ static void _updatePrecomp(LottieLayer* precomp, float frameNo, LottieExpression
}
//clip the layer viewport
if (!precomp->clipper) {
precomp->clipper = Shape::gen().release();
precomp->clipper->appendRect(0.0f, 0.0f, precomp->w, precomp->h);
PP(precomp->clipper)->ref();
}
precomp->clipper->transform(precomp->cache.matrix);
precomp->scene->composite(cast(precomp->clipper), CompositeMethod::ClipPath);
auto clipper = precomp->pooling();
clipper->transform(precomp->cache.matrix);
precomp->scene->composite(cast(clipper), CompositeMethod::ClipPath);
}
static void _updateSolid(LottieLayer* layer)
{
layer->solidFill->opacity(layer->cache.opacity);
layer->scene->push(cast(layer->solidFill));
auto solidFill = layer->pooling();
solidFill->opacity(layer->cache.opacity);
layer->scene->push(cast(solidFill));
}
static void _updateImage(LottieGroup* layer)
{
auto image = static_cast<LottieImage*>(layer->children.first());
if (!image->picture) {
image->picture = Picture::gen().release();
//force to load a picture on the same thread
TaskScheduler::async(false);
if (image->size > 0) image->picture->load((const char*)image->b64Data, image->size, image->mimeType, false);
else image->picture->load(image->path);
TaskScheduler::async(true);
PP(image->picture)->ref();
image->picture->size(image->width, image->height);
}
if (image->refCnt == 1) layer->scene->push(tvg::cast(image->picture));
else layer->scene->push(tvg::cast(image->picture->duplicate()));
layer->scene->push(tvg::cast(image->pooling()));
}
@ -1344,7 +1323,6 @@ static void _buildReference(LottieComposition* comp, LottieLayer* layer)
layer->reqFragment = assetLayer->reqFragment;
}
} else if (layer->type == LottieLayer::Image) {
++static_cast<LottieImage*>(*asset)->refCnt;
layer->children.push(*asset);
}
break;

View file

@ -23,6 +23,7 @@
#include "tvgMath.h"
#include "tvgPaint.h"
#include "tvgFill.h"
#include "tvgTaskScheduler.h"
#include "tvgLottieModel.h"
@ -111,14 +112,32 @@ void LottieSlot::assign(LottieObject* target)
LottieImage::~LottieImage()
{
if (picture && PP(picture)->unref() == 0) {
delete(picture);
}
free(b64Data);
free(mimeType);
}
void LottieImage::prepare()
{
LottieObject::type = LottieObject::Image;
auto picture = Picture::gen().release();
//force to load a picture on the same thread
TaskScheduler::async(false);
if (size > 0) picture->load((const char*)b64Data, size, mimeType, false);
else picture->load(path);
TaskScheduler::async(true);
picture->size(width, height);
PP(picture)->ref();
pooler.push(picture);
}
void LottieTrimpath::segment(float frameNo, float& start, float& end, LottieExpressions* exps)
{
start = this->start(frameNo, exps) * 0.01f;
@ -331,10 +350,6 @@ LottieLayer::~LottieLayer()
delete(*m);
}
//Remove tvg render paints
if (solidFill && PP(solidFill)->unref() == 0) delete(solidFill);
if (clipper && PP(clipper)->unref() == 0) delete(clipper);
delete(transform);
free(name);
}
@ -351,12 +366,19 @@ void LottieLayer::prepare(RGB24* color)
return;
}
//prepare the viewport clipper
if (type == LottieLayer::Precomp) {
auto clipper = Shape::gen().release();
clipper->appendRect(0.0f, 0.0f, w, h);
PP(clipper)->ref();
pooler.push(clipper);
//prepare solid fill in advance if it is a layer type.
if (color && type == LottieLayer::Solid) {
solidFill = Shape::gen().release();
} else if (color && type == LottieLayer::Solid) {
auto solidFill = Shape::gen().release();
solidFill->appendRect(0, 0, static_cast<float>(w), static_cast<float>(h));
solidFill->fill(color->rgb[0], color->rgb[1], color->rgb[2]);
PP(solidFill)->ref();
pooler.push(solidFill);
}
LottieGroup::prepare(LottieObject::Layer);

View file

@ -28,6 +28,7 @@
#include "tvgCommon.h"
#include "tvgRender.h"
#include "tvgLottieProperty.h"
#include "tvgLottieRenderPooler.h"
struct LottieComposition;
@ -490,25 +491,19 @@ struct LottieGradientStroke : LottieGradient, LottieStroke
};
struct LottieImage : LottieObject
struct LottieImage : LottieObject, LottieRenderPooler<tvg::Picture>
{
union {
char* b64Data = nullptr;
char* path;
};
Picture* picture = nullptr; //tvg render data
char* mimeType = nullptr;
uint32_t size = 0;
uint16_t refCnt = 0; //refernce count
float width = 0.0f;
float height = 0.0f;
~LottieImage();
void prepare()
{
LottieObject::type = LottieObject::Image;
}
void prepare();
};
@ -567,7 +562,7 @@ struct LottieGroup : LottieObject
};
struct LottieLayer : LottieGroup
struct LottieLayer : LottieGroup, LottieRenderPooler<tvg::Shape>
{
enum Type : uint8_t {Precomp = 0, Solid, Image, Null, Shape, Text};
@ -593,9 +588,6 @@ struct LottieLayer : LottieGroup
Array<LottieMask*> masks;
LottieLayer* matteTarget = nullptr;
tvg::Shape* solidFill = nullptr;
tvg::Shape* clipper = nullptr;
float timeStretch = 1.0f;
float w = 0.0f, h = 0.0f;
float inFrame = 0.0f;

View file

@ -0,0 +1,58 @@
/*
* Copyright (c) 2024 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _TVG_LOTTIE_RENDER_POOLER_H_
#define _TVG_LOTTIE_RENDER_POOLER_H_
#include "tvgCommon.h"
#include "tvgArray.h"
#include "tvgPaint.h"
template<typename T>
struct LottieRenderPooler
{
Array<T*> pooler;
~LottieRenderPooler()
{
for (auto p = pooler.begin(); p < pooler.end(); ++p) {
if (PP(*p)->unref() == 0) delete(*p);
}
}
T* pooling()
{
//return available one.
for (auto p = pooler.begin(); p < pooler.end(); ++p) {
if (PP(*p)->refCnt == 1) return *p;
}
//no empty, generate a new one.
auto p = static_cast<T*>(pooler[0]->duplicate());
PP(p)->ref();
pooler.push(p);
return p;
}
};
#endif //_TVG_LOTTIE_RENDER_POOLER_H_