mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-18 05:54:51 +00:00
renderer: add partial rendering support
Partial Rendering refers to a rendering technique where only a portion of the scene or screen is updated, rather than redrawing the entire output. It is commonly used as a performance optimization strategy, focusing on redrawing only the regions that have changed, often called dirty regions. This introduces RenderDirtyRegion, which assists in collecting a compact dirty region from render tasks. Each backend can utilize this class to support efficient partial rendering. This is implemented using a Line Sweep and Subdivision Merging O(NlogN). The basic per-frame workflow is as follows: 1. RenderDirtyRegion::prepare() //Call this in Renderer::preRender(). 2. RenderDirtyRegion::add() //Add all dirty paints for the frame before rendering. 3. RenderDirtyRegion::commit() //Generate the partial rendering region list before rendering. 4. RenderDirtyRegion::get() //Retrieve the current dirty region list and use it when drawing paints. 5. RenderDirtyRegion::clear() //Reset the state. issue: https://github.com/thorvg/thorvg/issues/1747
This commit is contained in:
parent
b22ceaae7c
commit
6ce477854d
9 changed files with 101 additions and 3 deletions
|
@ -862,6 +862,12 @@ bool GlRenderer::sync()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void GlRenderer::damage(TVG_UNUSED const RenderRegion& region)
|
||||||
|
{
|
||||||
|
//TODO:
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
RenderRegion GlRenderer::region(RenderData data)
|
RenderRegion GlRenderer::region(RenderData data)
|
||||||
{
|
{
|
||||||
if (currentPass()->isEmpty()) return {};
|
if (currentPass()->isEmpty()) return {};
|
||||||
|
|
|
@ -80,6 +80,7 @@ public:
|
||||||
bool postRender() override;
|
bool postRender() override;
|
||||||
void dispose(RenderData data) override;;
|
void dispose(RenderData data) override;;
|
||||||
RenderRegion region(RenderData data) override;
|
RenderRegion region(RenderData data) override;
|
||||||
|
void damage(const RenderRegion& region) override;
|
||||||
RenderRegion viewport() override;
|
RenderRegion viewport() override;
|
||||||
bool viewport(const RenderRegion& vp) override;
|
bool viewport(const RenderRegion& vp) override;
|
||||||
bool blend(BlendMethod method) override;
|
bool blend(BlendMethod method) override;
|
||||||
|
|
|
@ -495,6 +495,12 @@ bool SwRenderer::blend(BlendMethod method)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SwRenderer::damage(TVG_UNUSED const RenderRegion& region)
|
||||||
|
{
|
||||||
|
//TODO:
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
RenderRegion SwRenderer::region(RenderData data)
|
RenderRegion SwRenderer::region(RenderData data)
|
||||||
{
|
{
|
||||||
return static_cast<SwTask*>(data)->bounds();
|
return static_cast<SwTask*>(data)->bounds();
|
||||||
|
|
|
@ -46,6 +46,7 @@ public:
|
||||||
bool postRender() override;
|
bool postRender() override;
|
||||||
void dispose(RenderData data) override;
|
void dispose(RenderData data) override;
|
||||||
RenderRegion region(RenderData data) override;
|
RenderRegion region(RenderData data) override;
|
||||||
|
void damage(const RenderRegion& region) override;
|
||||||
RenderRegion viewport() override;
|
RenderRegion viewport() override;
|
||||||
bool viewport(const RenderRegion& vp) override;
|
bool viewport(const RenderRegion& vp) override;
|
||||||
bool blend(BlendMethod method) override;
|
bool blend(BlendMethod method) override;
|
||||||
|
|
|
@ -119,7 +119,6 @@ namespace tvg
|
||||||
uint8_t unrefx(bool free)
|
uint8_t unrefx(bool free)
|
||||||
{
|
{
|
||||||
if (refCnt > 0) --refCnt;
|
if (refCnt > 0) --refCnt;
|
||||||
else TVGERR("RENDERER", "Corrupted Reference Count!");
|
|
||||||
|
|
||||||
if (free && refCnt == 0) {
|
if (free && refCnt == 0) {
|
||||||
delete(paint);
|
delete(paint);
|
||||||
|
@ -129,6 +128,11 @@ namespace tvg
|
||||||
return refCnt;
|
return refCnt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void damage()
|
||||||
|
{
|
||||||
|
if (renderer) renderer->damage(bounds(renderer));
|
||||||
|
}
|
||||||
|
|
||||||
void mark(RenderUpdateFlag flag)
|
void mark(RenderUpdateFlag flag)
|
||||||
{
|
{
|
||||||
renderFlag |= flag;
|
renderFlag |= flag;
|
||||||
|
|
|
@ -50,7 +50,6 @@ static inline RenderUpdateFlag operator|(const RenderUpdateFlag a, const RenderU
|
||||||
return RenderUpdateFlag(uint16_t(a) | uint16_t(b));
|
return RenderUpdateFlag(uint16_t(a) | uint16_t(b));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct RenderSurface
|
struct RenderSurface
|
||||||
{
|
{
|
||||||
union {
|
union {
|
||||||
|
@ -141,6 +140,70 @@ struct RenderRegion
|
||||||
uint32_t h() const { return (uint32_t) sh(); }
|
uint32_t h() const { return (uint32_t) sh(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct RenderDirtyRegion
|
||||||
|
{
|
||||||
|
void add(const RenderRegion& region)
|
||||||
|
{
|
||||||
|
if (!disabled && region.valid()) {
|
||||||
|
ScopedLock lock(key);
|
||||||
|
list[current].push(region);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool prepare(uint32_t count = 0)
|
||||||
|
{
|
||||||
|
if (disabled) return false;
|
||||||
|
|
||||||
|
if (count > THRESHOLD) {
|
||||||
|
skip = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
count *= 120; //FIXME: enough?
|
||||||
|
|
||||||
|
list[0].reserve(count);
|
||||||
|
list[1].reserve(count);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool deactivated()
|
||||||
|
{
|
||||||
|
if (disabled || skip) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
list[0].clear();
|
||||||
|
list[1].clear();
|
||||||
|
skip = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Array<RenderRegion>& get()
|
||||||
|
{
|
||||||
|
return list[current];
|
||||||
|
}
|
||||||
|
|
||||||
|
void commit();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void subdivide(Array<RenderRegion>& targets, uint32_t idx, RenderRegion& lhs, RenderRegion& rhs);
|
||||||
|
|
||||||
|
/* We deactivate partial rendering if there are more than N moving elements.
|
||||||
|
Imagine thousands of moving objects covering the entire screen, That case partial rendering will lose any benefits.
|
||||||
|
Even if they don't, the overhead of subdividing and merging partial regions
|
||||||
|
could be more expensive than simply rendering the full screen.
|
||||||
|
The number is experimentally confirmed and we are open to improve this. */
|
||||||
|
static constexpr const uint32_t THRESHOLD = 5000;
|
||||||
|
|
||||||
|
Array<RenderRegion> list[2]; //double buffer swapping
|
||||||
|
Key key;
|
||||||
|
uint8_t current = 0; //list index. 0 or 1
|
||||||
|
bool disabled = false;
|
||||||
|
bool skip = false;
|
||||||
|
};
|
||||||
|
|
||||||
struct RenderPath
|
struct RenderPath
|
||||||
{
|
{
|
||||||
Array<PathCommand> cmds;
|
Array<PathCommand> cmds;
|
||||||
|
@ -437,6 +500,7 @@ public:
|
||||||
virtual bool renderImage(RenderData data) = 0;
|
virtual bool renderImage(RenderData data) = 0;
|
||||||
virtual bool postRender() = 0;
|
virtual bool postRender() = 0;
|
||||||
virtual void dispose(RenderData data) = 0;
|
virtual void dispose(RenderData data) = 0;
|
||||||
|
virtual void damage(const RenderRegion& region) = 0;
|
||||||
virtual RenderRegion region(RenderData data) = 0;
|
virtual RenderRegion region(RenderData data) = 0;
|
||||||
virtual RenderRegion viewport() = 0;
|
virtual RenderRegion viewport() = 0;
|
||||||
virtual bool viewport(const RenderRegion& vp) = 0;
|
virtual bool viewport(const RenderRegion& vp) = 0;
|
||||||
|
|
|
@ -128,6 +128,9 @@ struct SceneImpl : Scene
|
||||||
vport = renderer->viewport();
|
vport = renderer->viewport();
|
||||||
vdirty = true;
|
vdirty = true;
|
||||||
|
|
||||||
|
//bounds(renderer) here hinders parallelization.
|
||||||
|
if (effects) renderer->damage(vport);
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,7 +257,10 @@ struct SceneImpl : Scene
|
||||||
{
|
{
|
||||||
auto itr = paints.begin();
|
auto itr = paints.begin();
|
||||||
while (itr != paints.end()) {
|
while (itr != paints.end()) {
|
||||||
PAINT((*itr))->unref();
|
auto paint = PAINT((*itr));
|
||||||
|
//when the paint is destroyed damage will be triggered
|
||||||
|
if (paint->refCnt > 1) paint->damage();
|
||||||
|
paint->unref();
|
||||||
paints.erase(itr++);
|
paints.erase(itr++);
|
||||||
}
|
}
|
||||||
return Result::Success;
|
return Result::Success;
|
||||||
|
@ -263,6 +269,8 @@ struct SceneImpl : Scene
|
||||||
Result remove(Paint* paint)
|
Result remove(Paint* paint)
|
||||||
{
|
{
|
||||||
if (PAINT(paint)->parent != this) return Result::InsufficientCondition;
|
if (PAINT(paint)->parent != this) return Result::InsufficientCondition;
|
||||||
|
//when the paint is destroyed damage will be triggered
|
||||||
|
if (PAINT(paint)->refCnt > 1) PAINT(paint)->damage();
|
||||||
PAINT(paint)->unref();
|
PAINT(paint)->unref();
|
||||||
paints.remove(paint);
|
paints.remove(paint);
|
||||||
return Result::Success;
|
return Result::Success;
|
||||||
|
@ -307,6 +315,7 @@ struct SceneImpl : Scene
|
||||||
}
|
}
|
||||||
delete(effects);
|
delete(effects);
|
||||||
effects = nullptr;
|
effects = nullptr;
|
||||||
|
impl.renderer->damage(vport);
|
||||||
}
|
}
|
||||||
return Result::Success;
|
return Result::Success;
|
||||||
}
|
}
|
||||||
|
|
|
@ -273,6 +273,12 @@ void WgRenderer::dispose(RenderData data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void WgRenderer::damage(TVG_UNUSED const RenderRegion& region)
|
||||||
|
{
|
||||||
|
//TODO:
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
RenderRegion WgRenderer::region(RenderData data)
|
RenderRegion WgRenderer::region(RenderData data)
|
||||||
{
|
{
|
||||||
auto renderData = (WgRenderDataPaint*)data;
|
auto renderData = (WgRenderDataPaint*)data;
|
||||||
|
|
|
@ -38,6 +38,7 @@ public:
|
||||||
bool postRender() override;
|
bool postRender() override;
|
||||||
void dispose(RenderData data) override;
|
void dispose(RenderData data) override;
|
||||||
RenderRegion region(RenderData data) override;
|
RenderRegion region(RenderData data) override;
|
||||||
|
void damage(const RenderRegion& region) override;
|
||||||
RenderRegion viewport() override;
|
RenderRegion viewport() override;
|
||||||
bool viewport(const RenderRegion& vp) override;
|
bool viewport(const RenderRegion& vp) override;
|
||||||
bool blend(BlendMethod method) override;
|
bool blend(BlendMethod method) override;
|
||||||
|
|
Loading…
Add table
Reference in a new issue