renderer: introduced paint reference counting

The reference count of the Paint object allows
easy & safe shared ownership and control over its lifetime
among users and the engine.

New APIs:
- uint8_t Paint::ref()
- uint8_t Paint::unref(bool free = true)
- uint8_t Paint::refCnt() const

issue: https://github.com/thorvg/thorvg/issues/1372 https://github.com/thorvg/thorvg/issues/2598
This commit is contained in:
Hermet Park 2024-11-19 16:15:01 +09:00 committed by Hermet Park
parent 511495f6d8
commit 22d94ea629
12 changed files with 113 additions and 61 deletions

View file

@ -447,6 +447,53 @@ public:
*/
MaskMethod mask(const Paint** target) const noexcept;
/**
* @brief Increment the reference count for the Paint instance.
*
* This method increases the reference count of the Paint object, allowing shared ownership and control over its lifetime.
*
* @return The updated reference count after the increment by 1.
*
* @warning Please ensure that each call to ref() is paired with a corresponding call to unref() to prevent a dangling instance.
*
* @see Paint::unref()
* @see Paint::refCnt()
*
* @since 1.0
*/
uint8_t ref() noexcept;
/**
* @brief Decrement the reference count for the Paint instance.
*
* This method decreases the reference count of the Paint object by 1.
* If the reference count reaches zero and the @p free flag is set to true, the Paint instance is automatically deleted.
*
* @param[in] free Flag indicating whether to delete the Paint instance when the reference count reaches zero.
*
* @return The updated reference count after the decrement.
*
* @see Paint::ref()
* @see Paint::refCnt()
*
* @since 1.0
*/
uint8_t unref(bool free = true) noexcept;
/**
* @brief Retrieve the current reference count of the Paint instance.
*
* This method provides the current reference count, allowing the user to check the shared ownership state of the Paint object.
*
* @return The current reference count of the Paint instance.
*
* @see Paint::ref()
* @see Paint::unref()
*
* @since 1.0
*/
uint8_t refCnt() const noexcept;
/**
* @brief Returns the ID value of this class.
*

View file

@ -25,7 +25,6 @@
#include "tvgCommon.h"
#include "tvgInlist.h"
#include "tvgPaint.h"
#include "tvgShape.h"
#include "tvgLottieExpressions.h"
#include "tvgLottieModifier.h"
@ -64,13 +63,13 @@ struct RenderContext
RenderContext(Shape* propagator)
{
P(propagator)->reset();
PP(propagator)->ref();
propagator->ref();
this->propagator = propagator;
}
~RenderContext()
{
PP(propagator)->unref();
propagator->unref(false);
free(transform);
delete(roundness);
delete(offsetPath);
@ -79,7 +78,7 @@ struct RenderContext
RenderContext(const RenderContext& rhs, Shape* propagator, bool mergeable = false)
{
if (mergeable) merging = rhs.merging;
PP(propagator)->ref();
propagator->ref();
this->propagator = propagator;
this->repeaters = rhs.repeaters;
if (rhs.roundness) this->roundness = new LottieRoundnessModifier(rhs.roundness->r);

View file

@ -21,8 +21,6 @@
*/
#include "tvgMath.h"
#include "tvgPaint.h"
#include "tvgFill.h"
#include "tvgTaskScheduler.h"
#include "tvgLottieModel.h"
@ -170,7 +168,7 @@ void LottieImage::prepare()
TaskScheduler::async(true);
picture->size(data.width, data.height);
PP(picture)->ref();
picture->ref();
pooler.push(picture);
}
@ -472,14 +470,14 @@ void LottieLayer::prepare(RGB24* color)
if (type == LottieLayer::Precomp) {
auto clipper = Shape::gen();
clipper->appendRect(0.0f, 0.0f, w, h);
PP(clipper)->ref();
clipper->ref();
statical.pooler.push(clipper);
//prepare solid fill in advance if it is a layer type.
} else if (color && type == LottieLayer::Solid) {
auto solidFill = Shape::gen();
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();
solidFill->ref();
statical.pooler.push(solidFill);
}

View file

@ -36,7 +36,7 @@ struct LottieRenderPooler
~LottieRenderPooler()
{
for (auto p = pooler.begin(); p < pooler.end(); ++p) {
if (PP(*p)->unref() == 0) delete(*p);
(*p)->unref();
}
}
@ -44,12 +44,12 @@ struct LottieRenderPooler
{
//return available one.
for (auto p = pooler.begin(); p < pooler.end(); ++p) {
if (PP(*p)->refCnt == 1) return *p;
if ((*p)->refCnt() == 1) return *p;
}
//no empty, generate a new one.
auto p = copy ? static_cast<T*>(pooler[0]->duplicate()) : T::gen();
PP(p)->ref();
p->ref();
pooler.push(p);
return p;
}

View file

@ -24,7 +24,6 @@
#define _TVG_ANIMATION_H_
#include "tvgCommon.h"
#include "tvgPaint.h"
#include "tvgPicture.h"
struct Animation::Impl
@ -34,14 +33,12 @@ struct Animation::Impl
Impl()
{
picture = Picture::gen();
PP(picture)->ref();
picture->ref();
}
~Impl()
{
if (PP(picture)->unref() == 0) {
delete(picture);
}
picture->unref();
}
};

View file

@ -53,7 +53,7 @@ struct Canvas::Impl
void clearPaints()
{
for (auto paint : paints) {
if (P(paint)->unref() == 0) delete(paint);
paint->unref();
}
paints.clear();
}
@ -64,7 +64,7 @@ struct Canvas::Impl
if (status == Status::Drawing) return Result::InsufficientCondition;
if (!paint) return Result::MemoryCorruption;
PP(paint)->ref();
paint->ref();
paints.push_back(paint);
return update(paint, true);
@ -72,16 +72,18 @@ struct Canvas::Impl
Result clear(bool paints, bool buffer)
{
auto ret = Result::Success;
if (status == Status::Drawing) return Result::InsufficientCondition;
//Clear render target
if (buffer) {
if (!renderer->clear()) return Result::InsufficientCondition;
if (buffer && !renderer->clear()) {
ret = Result::InsufficientCondition;
}
if (paints) clearPaints();
return Result::Success;
return ret;
}
Result update(Paint* paint, bool force)

View file

@ -362,7 +362,7 @@ void Paint::Impl::reset()
}
if (maskData) {
if (P(maskData->target)->unref() == 0) delete(maskData->target);
maskData->target->unref();
free(maskData);
maskData = nullptr;
}
@ -503,3 +503,33 @@ Result Paint::blend(BlendMethod method) noexcept
return Result::Success;
}
uint8_t Paint::ref() noexcept
{
if (pImpl->refCnt == UINT8_MAX) TVGERR("RENDERER", "Reference Count Overflow!");
else ++pImpl->refCnt;
return pImpl->refCnt;
}
uint8_t Paint::unref(bool free) noexcept
{
if (pImpl->refCnt > 0) --pImpl->refCnt;
else TVGERR("RENDERER", "Corrupted Reference Count!");
if (free && pImpl->refCnt == 0) {
//TODO: use the global dismiss function?
delete(this);
return 0;
}
return pImpl->refCnt;
}
uint8_t Paint::refCnt() const noexcept
{
return pImpl->refCnt;
}

View file

@ -86,25 +86,13 @@ namespace tvg
~Impl()
{
if (maskData) {
if (P(maskData->target)->unref() == 0) delete(maskData->target);
maskData->target->unref();
free(maskData);
}
if (clipper && P(clipper)->unref() == 0) delete(clipper);
if (clipper) clipper->unref();
if (renderer && (renderer->unref() == 0)) delete(renderer);
}
uint8_t ref()
{
if (refCnt == 255) TVGERR("RENDERER", "Corrupted reference count!");
return ++refCnt;
}
uint8_t unref()
{
if (refCnt == 0) TVGERR("RENDERER", "Corrupted reference count!");
return --refCnt;
}
bool transform(const Matrix& m)
{
if (&tr.m != &m) tr.m = m;
@ -124,16 +112,11 @@ namespace tvg
void clip(Paint* clp)
{
if (this->clipper) {
P(this->clipper)->unref();
if (this->clipper != clp && P(this->clipper)->refCnt == 0) {
delete(this->clipper);
}
}
this->clipper = clp;
if (clipper) clipper->unref(clipper != clp);
clipper = clp;
if (!clp) return;
P(clipper)->ref();
clipper->ref();
}
bool mask(Paint* source, Paint* target, MaskMethod method)
@ -142,10 +125,7 @@ namespace tvg
if ((!target && method != MaskMethod::None) || (target && method == MaskMethod::None)) return false;
if (maskData) {
P(maskData->target)->unref();
if ((maskData->target != target) && P(maskData->target)->refCnt == 0) {
delete(maskData->target);
}
maskData->target->unref(maskData->target != target);
//Reset scenario
if (!target && method == MaskMethod::None) {
free(maskData);
@ -156,7 +136,7 @@ namespace tvg
if (!target && method == MaskMethod::None) return true;
maskData = static_cast<Mask*>(malloc(sizeof(Mask)));
}
P(target)->ref();
target->ref();
maskData->target = target;
maskData->source = source;
maskData->method = method;

View file

@ -23,7 +23,6 @@
#include "tvgCommon.h"
#include "tvgStr.h"
#include "tvgSaveModule.h"
#include "tvgPaint.h"
#ifdef THORVG_GIF_SAVER_SUPPORT
#include "tvgGifSaver.h"
@ -107,7 +106,7 @@ Result Saver::save(Paint* paint, const char* filename, uint32_t quality) noexcep
//Already on saving another resource.
if (pImpl->saveModule) {
if (P(paint)->refCnt == 0) delete(paint);
if (paint->refCnt() == 0) delete(paint);
return Result::InsufficientCondition;
}
@ -116,12 +115,12 @@ Result Saver::save(Paint* paint, const char* filename, uint32_t quality) noexcep
pImpl->saveModule = saveModule;
return Result::Success;
} else {
if (P(paint)->refCnt == 0) delete(paint);
if (paint->refCnt() == 0) delete(paint);
delete(saveModule);
return Result::Unknown;
}
}
if (P(paint)->refCnt == 0) delete(paint);
if (paint->refCnt() == 0) delete(paint);
return Result::NonSupport;
}
@ -140,7 +139,7 @@ Result Saver::save(Animation* animation, const char* filename, uint32_t quality,
if (!animation) return Result::MemoryCorruption;
//animation holds the picture, it must be 1 at the bottom.
auto remove = PP(animation->picture())->refCnt <= 1 ? true : false;
auto remove = animation->picture()->refCnt() <= 1 ? true : false;
if (tvg::zero(animation->totalFrame())) {
if (remove) delete(animation);

View file

@ -70,7 +70,7 @@ Type Scene::type() const noexcept
Result Scene::push(Paint* paint) noexcept
{
if (!paint) return Result::MemoryCorruption;
PP(paint)->ref();
paint->ref();
pImpl->paints.push_back(paint);
return Result::Success;

View file

@ -74,7 +74,7 @@ struct Scene::Impl
resetEffects();
for (auto paint : paints) {
if (P(paint)->unref() == 0) delete(paint);
paint->unref();
}
if (auto renderer = PP(scene)->renderer) {
@ -230,7 +230,7 @@ struct Scene::Impl
for (auto paint : paints) {
auto cdup = paint->duplicate();
P(cdup)->ref();
cdup->ref();
dup->paints.push_back(cdup);
}
@ -242,7 +242,7 @@ struct Scene::Impl
void clear(bool free)
{
for (auto paint : paints) {
if (P(paint)->unref() == 0 && free) delete(paint);
paint->unref(free);
}
paints.clear();
}

View file

@ -100,7 +100,7 @@ bool GifSaver::close()
bg = nullptr;
//animation holds the picture, it must be 1 at the bottom.
if (animation && PP(animation->picture())->refCnt <= 1) delete(animation);
if (animation && animation->picture()->refCnt() <= 1) delete(animation);
animation = nullptr;
free(path);