mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-07 21:23:32 +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
535ea16b95
commit
afeb7c024a
8 changed files with 93 additions and 6 deletions
|
@ -859,6 +859,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;
|
||||
|
|
|
@ -477,6 +477,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;
|
||||
|
|
|
@ -50,7 +50,6 @@ static inline RenderUpdateFlag operator|(const RenderUpdateFlag a, const RenderU
|
|||
return RenderUpdateFlag(uint16_t(a) | uint16_t(b));
|
||||
}
|
||||
|
||||
|
||||
struct RenderSurface
|
||||
{
|
||||
union {
|
||||
|
@ -137,6 +136,68 @@ struct RenderRegion
|
|||
uint32_t h() const { return (uint32_t) sh(); }
|
||||
};
|
||||
|
||||
struct RenderDirtyRegion
|
||||
{
|
||||
void add(const RenderRegion& region)
|
||||
{
|
||||
if (!disabled && region.valid()) {
|
||||
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
|
||||
uint8_t current = 0; //list index. 0 or 1
|
||||
bool disabled = false;
|
||||
bool skip = false;
|
||||
};
|
||||
|
||||
struct RenderPath
|
||||
{
|
||||
Array<PathCommand> cmds;
|
||||
|
@ -433,6 +494,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;
|
||||
|
|
|
@ -105,7 +105,7 @@ struct SceneImpl : Scene
|
|||
|
||||
RenderData update(RenderMethod* renderer, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flag, TVG_UNUSED bool clipper)
|
||||
{
|
||||
this->vport = renderer->viewport();
|
||||
vport = renderer->viewport();
|
||||
|
||||
if (needComposition(opacity)) {
|
||||
/* Overriding opacity value. If this scene is half-translucent,
|
||||
|
@ -123,6 +123,9 @@ struct SceneImpl : Scene
|
|||
}
|
||||
}
|
||||
|
||||
if (compFlag) vport = bounds(renderer);
|
||||
if (effects) renderer->damage(vport);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -134,7 +137,7 @@ struct SceneImpl : Scene
|
|||
renderer->blend(impl.blendMethod);
|
||||
|
||||
if (compFlag) {
|
||||
cmp = renderer->target(bounds(renderer), renderer->colorSpace(), static_cast<CompositionFlag>(compFlag));
|
||||
cmp = renderer->target(vport, renderer->colorSpace(), static_cast<CompositionFlag>(compFlag));
|
||||
renderer->beginComposite(cmp, MaskMethod::None, opacity);
|
||||
}
|
||||
|
||||
|
@ -157,7 +160,7 @@ struct SceneImpl : Scene
|
|||
return ret;
|
||||
}
|
||||
|
||||
RenderRegion bounds(RenderMethod* renderer) const
|
||||
RenderRegion bounds(RenderMethod* renderer)
|
||||
{
|
||||
if (paints.empty()) return {};
|
||||
|
||||
|
@ -185,8 +188,8 @@ struct SceneImpl : Scene
|
|||
pRegion.max.x += eRegion.max.x;
|
||||
pRegion.max.y += eRegion.max.y;
|
||||
|
||||
pRegion.intersect(this->vport);
|
||||
return pRegion;
|
||||
vport = RenderRegion::intersect(renderer->viewport(), pRegion);
|
||||
return vport;
|
||||
}
|
||||
|
||||
Result bounds(Point* pt4, Matrix& m, bool obb, bool stroking)
|
||||
|
@ -298,6 +301,7 @@ struct SceneImpl : Scene
|
|||
}
|
||||
delete(effects);
|
||||
effects = nullptr;
|
||||
impl.renderer->damage(vport);
|
||||
}
|
||||
return Result::Success;
|
||||
}
|
||||
|
|
|
@ -274,6 +274,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