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 committed by Hermet Park
parent 23688dfb78
commit d1067ad080
4 changed files with 21 additions and 21 deletions

View file

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

View file

@ -62,7 +62,9 @@ GlCanvas::~GlCanvas()
Result GlCanvas::target(int32_t id, uint32_t w, uint32_t h) noexcept Result GlCanvas::target(int32_t id, uint32_t w, uint32_t h) noexcept
{ {
#ifdef THORVG_GL_RASTER_SUPPORT #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. //We know renderer type, avoid dynamic_cast for performance.
auto renderer = static_cast<GlRenderer*>(Canvas::pImpl->renderer); 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); renderer->viewport(Canvas::pImpl->vport);
//Paints must be updated again with this new target. //Paints must be updated again with this new target.
Canvas::pImpl->needRefresh(); Canvas::pImpl->status = Status::Damanged;
return Result::Success; return Result::Success;
#endif #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 Result SwCanvas::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, Colorspace cs) noexcept
{ {
#ifdef THORVG_SW_RASTER_SUPPORT #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. //We know renderer type, avoid dynamic_cast for performance.
auto renderer = static_cast<SwRenderer*>(Canvas::pImpl->renderer); 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}; Canvas::pImpl->vport = {0, 0, (int32_t)w, (int32_t)h};
renderer->viewport(Canvas::pImpl->vport); 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. //FIXME: The value must be associated with an individual canvas instance.
ImageLoader::cs = static_cast<ColorSpace>(cs); ImageLoader::cs = static_cast<ColorSpace>(cs);
//Paints must be updated again with this new target.
Canvas::pImpl->status = Status::Damanged;
return Result::Success; return Result::Success;
#endif #endif
return Result::NonSupport; 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 Result WgCanvas::target(void* instance, void* surface, uint32_t w, uint32_t h) noexcept
{ {
#ifdef THORVG_WG_RASTER_SUPPORT #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; 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); renderer->viewport(Canvas::pImpl->vport);
//Paints must be updated again with this new target. //Paints must be updated again with this new target.
Canvas::pImpl->needRefresh(); Canvas::pImpl->status = Status::Damanged;
return Result::Success; return Result::Success;
#endif #endif