renderer: ++stabilization

This introduces a managed condition to precisely control the
canvas updates. This prevents a crash when the target is
changed and drawn without any update calls.

issue: https://github.com/thorvg/thorvg/issues/2484
This commit is contained in:
Hermet Park 2024-06-27 12:45:28 +09:00
parent 3019bb81e8
commit 38e2812004
4 changed files with 21 additions and 21 deletions

View file

@ -26,7 +26,7 @@
#include "tvgPaint.h"
enum Status : uint8_t {Synced = 0, Updating, Drawing};
enum Status : uint8_t {Synced = 0, Updating, Drawing, Damanged};
struct Canvas::Impl
{
@ -35,8 +35,6 @@ struct Canvas::Impl
RenderRegion vport = {0, 0, INT32_MAX, INT32_MAX};
Status status = Status::Synced;
bool refresh = false; //if all paints should be updated by force.
Impl(RenderMethod* pRenderer) : renderer(pRenderer)
{
renderer->ref();
@ -87,18 +85,13 @@ struct Canvas::Impl
return Result::Success;
}
void needRefresh()
{
refresh = true;
}
Result update(Paint* paint, bool force)
{
if (paints.empty() || status == Status::Drawing) return Result::InsufficientCondition;
Array<RenderData> clips;
auto flag = RenderUpdateFlag::None;
if (refresh || force) flag = RenderUpdateFlag::All;
if (status == Status::Damanged || force) flag = RenderUpdateFlag::All;
if (paint) {
paint->pImpl->update(renderer, nullptr, clips, 255, flag);
@ -106,7 +99,6 @@ struct Canvas::Impl
for (auto paint : paints) {
paint->pImpl->update(renderer, nullptr, clips, 255, flag);
}
refresh = false;
}
status = Status::Updating;
return Result::Success;
@ -114,6 +106,7 @@ struct Canvas::Impl
Result draw()
{
if (status == Status::Damanged) update(nullptr, false);
if (status == Status::Drawing || paints.empty() || !renderer->preRender()) return Result::InsufficientCondition;
bool rendered = false;
@ -129,7 +122,7 @@ struct Canvas::Impl
Result sync()
{
if (status == Status::Synced) return Result::InsufficientCondition;
if (status == Status::Synced || status == Status::Damanged) return Result::InsufficientCondition;
if (renderer->sync()) {
status = Status::Synced;
@ -141,7 +134,8 @@ struct Canvas::Impl
Result viewport(int32_t x, int32_t y, int32_t w, int32_t h)
{
if (status != Status::Synced) return Result::InsufficientCondition;
if (status != Status::Damanged && status != Status::Synced) return Result::InsufficientCondition;
RenderRegion val = {x, y, w, h};
//intersect if the target buffer is already set.
auto surface = renderer->mainSurface();
@ -151,7 +145,7 @@ struct Canvas::Impl
if (vport == val) return Result::Success;
renderer->viewport(val);
vport = val;
needRefresh();
status = Status::Damanged;
return Result::Success;
}
};

View file

@ -62,7 +62,9 @@ GlCanvas::~GlCanvas()
Result GlCanvas::target(int32_t id, uint32_t w, uint32_t h) noexcept
{
#ifdef THORVG_GL_RASTER_SUPPORT
if (Canvas::pImpl->status != Status::Synced) return Result::InsufficientCondition;
if (Canvas::pImpl->status != Status::Damanged && Canvas::pImpl->status != Status::Synced) {
return Result::InsufficientCondition;
}
//We know renderer type, avoid dynamic_cast for performance.
auto renderer = static_cast<GlRenderer*>(Canvas::pImpl->renderer);
@ -73,7 +75,7 @@ Result GlCanvas::target(int32_t id, uint32_t w, uint32_t h) noexcept
renderer->viewport(Canvas::pImpl->vport);
//Paints must be updated again with this new target.
Canvas::pImpl->needRefresh();
Canvas::pImpl->status = Status::Damanged;
return Result::Success;
#endif

View file

@ -82,7 +82,9 @@ Result SwCanvas::mempool(MempoolPolicy policy) noexcept
Result SwCanvas::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, Colorspace cs) noexcept
{
#ifdef THORVG_SW_RASTER_SUPPORT
if (Canvas::pImpl->status != Status::Synced) return Result::InsufficientCondition;
if (Canvas::pImpl->status != Status::Damanged && Canvas::pImpl->status != Status::Synced) {
return Result::InsufficientCondition;
}
//We know renderer type, avoid dynamic_cast for performance.
auto renderer = static_cast<SwRenderer*>(Canvas::pImpl->renderer);
@ -92,12 +94,12 @@ Result SwCanvas::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t
Canvas::pImpl->vport = {0, 0, (int32_t)w, (int32_t)h};
renderer->viewport(Canvas::pImpl->vport);
//Paints must be updated again with this new target.
Canvas::pImpl->needRefresh();
//FIXME: The value must be associated with an individual canvas instance.
ImageLoader::cs = static_cast<ColorSpace>(cs);
//Paints must be updated again with this new target.
Canvas::pImpl->status = Status::Damanged;
return Result::Success;
#endif
return Result::NonSupport;

View file

@ -57,7 +57,9 @@ WgCanvas::~WgCanvas()
Result WgCanvas::target(void* instance, void* surface, uint32_t w, uint32_t h) noexcept
{
#ifdef THORVG_WG_RASTER_SUPPORT
if (Canvas::pImpl->status != Status::Synced) return Result::InsufficientCondition;
if (Canvas::pImpl->status != Status::Damanged && Canvas::pImpl->status != Status::Synced) {
return Result::InsufficientCondition;
}
if (!instance || !surface || (w == 0) || (h == 0)) return Result::InvalidArguments;
@ -70,7 +72,7 @@ Result WgCanvas::target(void* instance, void* surface, uint32_t w, uint32_t h) n
renderer->viewport(Canvas::pImpl->vport);
//Paints must be updated again with this new target.
Canvas::pImpl->needRefresh();
Canvas::pImpl->status = Status::Damanged;
return Result::Success;
#endif