common: partial rendering stabilization++

There are more corner case issues after the partial rendering
introduction, this covers all-in-one.
This commit is contained in:
Hermet Park 2025-06-26 13:12:41 +09:00 committed by Hermet Park
parent 4fd6be9363
commit f3d7e232ce
10 changed files with 69 additions and 28 deletions

View file

@ -1481,7 +1481,7 @@ bool GlRenderer::postUpdate()
} }
void GlRenderer::damage(const RenderRegion& region) void GlRenderer::damage(TVG_UNUSED RenderData rd, TVG_UNUSED const RenderRegion& region)
{ {
//TODO //TODO
} }

View file

@ -100,7 +100,7 @@ public:
void dispose(RenderEffect* effect) override; void dispose(RenderEffect* effect) override;
//partial rendering //partial rendering
void damage(const RenderRegion& region) override; void damage(RenderData rd, const RenderRegion& region) override;
bool partial(bool disable) override; bool partial(bool disable) override;
static GlRenderer* gen(uint32_t threads); static GlRenderer* gen(uint32_t threads);

View file

@ -162,7 +162,7 @@ struct SwShapeTask : SwTask
} }
curBox = renderBox; //sync curBox = renderBox; //sync
if (!nodirty) dirtyRegion->add(&prvBox, &curBox); if (!nodirty) dirtyRegion->add(prvBox, curBox);
return; return;
err: err:
@ -170,7 +170,7 @@ struct SwShapeTask : SwTask
shapeReset(&shape); shapeReset(&shape);
rleReset(shape.strokeRle); rleReset(shape.strokeRle);
shapeDelOutline(&shape, mpool, tid); shapeDelOutline(&shape, mpool, tid);
if (!nodirty) dirtyRegion->add(&prvBox, &curBox); if (!nodirty) dirtyRegion->add(prvBox, curBox);
} }
void dispose() override void dispose() override
@ -221,7 +221,7 @@ struct SwImageTask : SwTask
auto clipper = static_cast<SwTask*>(*p); auto clipper = static_cast<SwTask*>(*p);
if (!clipper->clip(image.rle)) goto err; if (!clipper->clip(image.rle)) goto err;
} }
if (!nodirty) dirtyRegion->add(&prvBox, &curBox); if (!nodirty) dirtyRegion->add(prvBox, curBox);
return; return;
} }
} }
@ -232,7 +232,7 @@ struct SwImageTask : SwTask
rleReset(image.rle); rleReset(image.rle);
end: end:
imageDelOutline(&image, mpool, tid); imageDelOutline(&image, mpool, tid);
if (!nodirty) dirtyRegion->add(&prvBox, &curBox); if (!nodirty) dirtyRegion->add(prvBox, curBox);
} }
void dispose() override void dispose() override
@ -381,9 +381,11 @@ bool SwRenderer::postRender()
} }
void SwRenderer::damage(const RenderRegion& region) void SwRenderer::damage(RenderData rd, const RenderRegion& region)
{ {
dirtyRegion.add(&region, nullptr); SwTask* task = static_cast<SwTask*>(rd);
if (task && task->opacity == 0) return;
dirtyRegion.add(region);
} }

View file

@ -68,7 +68,7 @@ public:
void dispose(RenderEffect* effect) override; void dispose(RenderEffect* effect) override;
//partial rendering //partial rendering
void damage(const RenderRegion& region) override; void damage(RenderData rd, const RenderRegion& region) override;
bool partial(bool disable) override; bool partial(bool disable) override;
static SwRenderer* gen(uint32_t threads); static SwRenderer* gen(uint32_t threads);

View file

@ -131,7 +131,12 @@ namespace tvg
void damage(const RenderRegion& vport) void damage(const RenderRegion& vport)
{ {
if (renderer) renderer->damage(vport); if (renderer) renderer->damage(rd, vport);
}
void damage()
{
if (renderer) renderer->damage(rd, bounds(renderer));
} }
void mark(CompositionFlag flag) void mark(CompositionFlag flag)

View file

@ -158,25 +158,39 @@ void RenderDirtyRegion::init(uint32_t w, uint32_t h)
} }
void RenderDirtyRegion::add(const RenderRegion* prv, const RenderRegion* cur) bool RenderDirtyRegion::add(const RenderRegion& bbox)
{ {
if (disabled) return; if (disabled) return false;
auto pvalid = prv ? prv->valid() : false;
auto cvalid = cur ? cur->valid() : false;
if (!pvalid && !cvalid) return;
for (int idx = 0; idx < PARTITIONING; ++idx) { for (int idx = 0; idx < PARTITIONING; ++idx) {
auto& partition = partitions[idx]; auto& partition = partitions[idx];
if (pvalid && prv->intersected(partition.region)) { if (bbox.max.y <= partition.region.min.y) break;
if (bbox.intersected(partition.region)) {
ScopedLock lock(key); ScopedLock lock(key);
partition.list[partition.current].push(RenderRegion::intersect(*prv, partition.region)); partition.list[partition.current].push(RenderRegion::intersect(bbox, partition.region));
} }
if (cvalid && cur->intersected(partition.region)) { }
return true;
}
bool RenderDirtyRegion::add(const RenderRegion& prv, const RenderRegion& cur)
{
if (disabled) return false;
if (prv == cur) return add(prv);
for (int idx = 0; idx < PARTITIONING; ++idx) {
auto& partition = partitions[idx];
if (prv.intersected(partition.region)) {
ScopedLock lock(key); ScopedLock lock(key);
partition.list[partition.current].push(RenderRegion::intersect(*cur, partition.region)); partition.list[partition.current].push(RenderRegion::intersect(prv, partition.region));
}
if (cur.intersected(partition.region)) {
ScopedLock lock(key);
partition.list[partition.current].push(RenderRegion::intersect(cur, partition.region));
} }
} }
return true;
} }
@ -222,6 +236,12 @@ void RenderDirtyRegion::subdivide(Array<RenderRegion>& targets, uint32_t idx, Re
subtract(temp[0], lhs); subtract(temp[0], lhs);
subtract(temp[0], rhs); subtract(temp[0], rhs);
//Please reserve memory enough with targets.reserve()
if (targets.count + cnt - 1 > targets.reserved) {
TVGERR("RENDERER", "reserved(%d), required(%d)", targets.reserved, targets.count + cnt - 1);
return;
}
/* Considered using a list to avoid memory shifting, /* Considered using a list to avoid memory shifting,
but ultimately, the array outperformed the list due to better cache locality. */ but ultimately, the array outperformed the list due to better cache locality. */
@ -246,12 +266,12 @@ void RenderDirtyRegion::commit()
for (int idx = 0; idx < PARTITIONING; ++idx) { for (int idx = 0; idx < PARTITIONING; ++idx) {
auto current = partitions[idx].current; auto current = partitions[idx].current;
auto& targets = partitions[idx].list[current]; auto& targets = partitions[idx].list[current];
if (targets.empty()) return; if (targets.empty()) continue;
current = !current; //swapping buffers current = !current; //swapping buffers
auto& output = partitions[idx].list[current]; auto& output = partitions[idx].list[current];
targets.reserve(targets.count * 5); //one intersection can be divided up to 5 targets.reserve(targets.count * 10); //one intersection can be divided up to 5
output.reserve(targets.count); output.reserve(targets.count);
partitions[idx].current = current; partitions[idx].current = current;

View file

@ -164,7 +164,8 @@ struct RenderRegion
void init(uint32_t w, uint32_t h); void init(uint32_t w, uint32_t h);
void commit(); void commit();
void add(const RenderRegion* prv, const RenderRegion* cur); //collect the old and new dirty regions together bool add(const RenderRegion& bbox);
bool add(const RenderRegion& prv, const RenderRegion& cur); //collect the old and new dirty regions together
void clear(); void clear();
bool deactivate(bool on) bool deactivate(bool on)
@ -541,7 +542,7 @@ public:
virtual void dispose(RenderEffect* effect) = 0; virtual void dispose(RenderEffect* effect) = 0;
//partial rendering //partial rendering
virtual void damage(const RenderRegion& region) = 0; virtual void damage(RenderData rd, const RenderRegion& region) = 0;
virtual bool partial(bool disable) = 0; virtual bool partial(bool disable) = 0;
}; };

View file

@ -280,19 +280,32 @@ struct SceneImpl : Scene
Result clearPaints() Result clearPaints()
{ {
if (paints.empty()) return Result::Success;
//Don't need to damage for children
auto recover = (fixed && impl.renderer) ? impl.renderer->partial(true) : false;
auto partialDmg = !(effects || fixed || recover);
auto itr = paints.begin(); auto itr = paints.begin();
while (itr != paints.end()) { while (itr != paints.end()) {
auto paint = PAINT((*itr)); auto paint = PAINT((*itr));
//when the paint is destroyed damage will be triggered
if (paint->refCnt > 1 && partialDmg) paint->damage();
paint->unref(); paint->unref();
paints.erase(itr++); paints.erase(itr++);
} }
if (effects || fixed) impl.damage(vport); //redraw scene full region
if (fixed && impl.renderer) impl.renderer->partial(recover);
return Result::Success; return Result::Success;
} }
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;

View file

@ -581,7 +581,7 @@ bool WgRenderer::postUpdate()
} }
void WgRenderer::damage(const RenderRegion& region) void WgRenderer::damage(TVG_UNUSED RenderData rd, TVG_UNUSED const RenderRegion& region)
{ {
//TODO //TODO
} }

View file

@ -58,7 +58,7 @@ public:
void dispose(RenderEffect* effect) override; void dispose(RenderEffect* effect) override;
//partial rendering //partial rendering
void damage(const RenderRegion& region) override; void damage(RenderData rd, const RenderRegion& region) override;
bool partial(bool disable) override; bool partial(bool disable) override;
static WgRenderer* gen(uint32_t threads); static WgRenderer* gen(uint32_t threads);