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)
|
||||
{
|
||||
if (currentPass()->isEmpty()) return {};
|
||||
|
|
|
@ -80,6 +80,7 @@ public:
|
|||
bool postRender() override;
|
||||
void dispose(RenderData data) override;;
|
||||
RenderRegion region(RenderData data) override;
|
||||
void damage(const RenderRegion& region) override;
|
||||
RenderRegion viewport() override;
|
||||
bool viewport(const RenderRegion& vp) 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)
|
||||
{
|
||||
return static_cast<SwTask*>(data)->bounds();
|
||||
|
|
|
@ -46,6 +46,7 @@ public:
|
|||
bool postRender() override;
|
||||
void dispose(RenderData data) override;
|
||||
RenderRegion region(RenderData data) override;
|
||||
void damage(const RenderRegion& region) override;
|
||||
RenderRegion viewport() override;
|
||||
bool viewport(const RenderRegion& vp) override;
|
||||
bool blend(BlendMethod method) override;
|
||||
|
|
|
@ -119,7 +119,6 @@ namespace tvg
|
|||
uint8_t unrefx(bool free)
|
||||
{
|
||||
if (refCnt > 0) --refCnt;
|
||||
else TVGERR("RENDERER", "Corrupted Reference Count!");
|
||||
|
||||
if (free && refCnt == 0) {
|
||||
delete(paint);
|
||||
|
@ -129,6 +128,11 @@ namespace tvg
|
|||
return refCnt;
|
||||
}
|
||||
|
||||
void damage()
|
||||
{
|
||||
if (renderer) renderer->damage(bounds(renderer));
|
||||
}
|
||||
|
||||
void mark(RenderUpdateFlag flag)
|
||||
{
|
||||
renderFlag |= flag;
|
||||
|
|
|
@ -50,7 +50,6 @@ static inline RenderUpdateFlag operator|(const RenderUpdateFlag a, const RenderU
|
|||
return RenderUpdateFlag(uint16_t(a) | uint16_t(b));
|
||||
}
|
||||
|
||||
|
||||
struct RenderSurface
|
||||
{
|
||||
union {
|
||||
|
@ -141,6 +140,70 @@ struct RenderRegion
|
|||
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
|
||||
{
|
||||
Array<PathCommand> cmds;
|
||||
|
@ -437,6 +500,7 @@ public:
|
|||
virtual bool renderImage(RenderData data) = 0;
|
||||
virtual bool postRender() = 0;
|
||||
virtual void dispose(RenderData data) = 0;
|
||||
virtual void damage(const RenderRegion& region) = 0;
|
||||
virtual RenderRegion region(RenderData data) = 0;
|
||||
virtual RenderRegion viewport() = 0;
|
||||
virtual bool viewport(const RenderRegion& vp) = 0;
|
||||
|
|
|
@ -128,6 +128,9 @@ struct SceneImpl : Scene
|
|||
vport = renderer->viewport();
|
||||
vdirty = true;
|
||||
|
||||
//bounds(renderer) here hinders parallelization.
|
||||
if (effects) renderer->damage(vport);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -254,7 +257,10 @@ struct SceneImpl : Scene
|
|||
{
|
||||
auto itr = paints.begin();
|
||||
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++);
|
||||
}
|
||||
return Result::Success;
|
||||
|
@ -263,6 +269,8 @@ struct SceneImpl : Scene
|
|||
Result remove(Paint* paint)
|
||||
{
|
||||
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();
|
||||
paints.remove(paint);
|
||||
return Result::Success;
|
||||
|
@ -307,6 +315,7 @@ struct SceneImpl : Scene
|
|||
}
|
||||
delete(effects);
|
||||
effects = nullptr;
|
||||
impl.renderer->damage(vport);
|
||||
}
|
||||
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)
|
||||
{
|
||||
auto renderData = (WgRenderDataPaint*)data;
|
||||
|
|
|
@ -38,6 +38,7 @@ public:
|
|||
bool postRender() override;
|
||||
void dispose(RenderData data) override;
|
||||
RenderRegion region(RenderData data) override;
|
||||
void damage(const RenderRegion& region) override;
|
||||
RenderRegion viewport() override;
|
||||
bool viewport(const RenderRegion& vp) override;
|
||||
bool blend(BlendMethod method) override;
|
||||
|
|
Loading…
Add table
Reference in a new issue