mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-08 05:33:36 +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)
|
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;
|
||||||
|
|
|
@ -477,6 +477,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;
|
||||||
|
|
|
@ -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 {
|
||||||
|
@ -137,6 +136,68 @@ 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()) {
|
||||||
|
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
|
struct RenderPath
|
||||||
{
|
{
|
||||||
Array<PathCommand> cmds;
|
Array<PathCommand> cmds;
|
||||||
|
@ -433,6 +494,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;
|
||||||
|
|
|
@ -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)
|
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)) {
|
if (needComposition(opacity)) {
|
||||||
/* Overriding opacity value. If this scene is half-translucent,
|
/* 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;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +137,7 @@ struct SceneImpl : Scene
|
||||||
renderer->blend(impl.blendMethod);
|
renderer->blend(impl.blendMethod);
|
||||||
|
|
||||||
if (compFlag) {
|
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);
|
renderer->beginComposite(cmp, MaskMethod::None, opacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,7 +160,7 @@ struct SceneImpl : Scene
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderRegion bounds(RenderMethod* renderer) const
|
RenderRegion bounds(RenderMethod* renderer)
|
||||||
{
|
{
|
||||||
if (paints.empty()) return {};
|
if (paints.empty()) return {};
|
||||||
|
|
||||||
|
@ -185,8 +188,8 @@ struct SceneImpl : Scene
|
||||||
pRegion.max.x += eRegion.max.x;
|
pRegion.max.x += eRegion.max.x;
|
||||||
pRegion.max.y += eRegion.max.y;
|
pRegion.max.y += eRegion.max.y;
|
||||||
|
|
||||||
pRegion.intersect(this->vport);
|
vport = RenderRegion::intersect(renderer->viewport(), pRegion);
|
||||||
return pRegion;
|
return vport;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result bounds(Point* pt4, Matrix& m, bool obb, bool stroking)
|
Result bounds(Point* pt4, Matrix& m, bool obb, bool stroking)
|
||||||
|
@ -298,6 +301,7 @@ struct SceneImpl : Scene
|
||||||
}
|
}
|
||||||
delete(effects);
|
delete(effects);
|
||||||
effects = nullptr;
|
effects = nullptr;
|
||||||
|
impl.renderer->damage(vport);
|
||||||
}
|
}
|
||||||
return Result::Success;
|
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)
|
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