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
}

View file

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

View file

@ -162,7 +162,7 @@ struct SwShapeTask : SwTask
}
curBox = renderBox; //sync
if (!nodirty) dirtyRegion->add(&prvBox, &curBox);
if (!nodirty) dirtyRegion->add(prvBox, curBox);
return;
err:
@ -170,7 +170,7 @@ struct SwShapeTask : SwTask
shapeReset(&shape);
rleReset(shape.strokeRle);
shapeDelOutline(&shape, mpool, tid);
if (!nodirty) dirtyRegion->add(&prvBox, &curBox);
if (!nodirty) dirtyRegion->add(prvBox, curBox);
}
void dispose() override
@ -221,7 +221,7 @@ struct SwImageTask : SwTask
auto clipper = static_cast<SwTask*>(*p);
if (!clipper->clip(image.rle)) goto err;
}
if (!nodirty) dirtyRegion->add(&prvBox, &curBox);
if (!nodirty) dirtyRegion->add(prvBox, curBox);
return;
}
}
@ -232,7 +232,7 @@ struct SwImageTask : SwTask
rleReset(image.rle);
end:
imageDelOutline(&image, mpool, tid);
if (!nodirty) dirtyRegion->add(&prvBox, &curBox);
if (!nodirty) dirtyRegion->add(prvBox, curBox);
}
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;
//partial rendering
void damage(const RenderRegion& region) override;
void damage(RenderData rd, const RenderRegion& region) override;
bool partial(bool disable) override;
static SwRenderer* gen(uint32_t threads);

View file

@ -131,7 +131,12 @@ namespace tvg
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)

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;
auto pvalid = prv ? prv->valid() : false;
auto cvalid = cur ? cur->valid() : false;
if (!pvalid && !cvalid) return;
if (disabled) return false;
for (int idx = 0; idx < PARTITIONING; ++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);
partition.list[partition.current].push(RenderRegion::intersect(*prv, partition.region));
}
if (cvalid && cur->intersected(partition.region)) {
ScopedLock lock(key);
partition.list[partition.current].push(RenderRegion::intersect(*cur, partition.region));
partition.list[partition.current].push(RenderRegion::intersect(bbox, 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);
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], 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,
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) {
auto current = partitions[idx].current;
auto& targets = partitions[idx].list[current];
if (targets.empty()) return;
if (targets.empty()) continue;
current = !current; //swapping buffers
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);
partitions[idx].current = current;

View file

@ -164,7 +164,8 @@ struct RenderRegion
void init(uint32_t w, uint32_t h);
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();
bool deactivate(bool on)
@ -541,7 +542,7 @@ public:
virtual void dispose(RenderEffect* effect) = 0;
//partial rendering
virtual void damage(const RenderRegion& region) = 0;
virtual void damage(RenderData rd, const RenderRegion& region) = 0;
virtual bool partial(bool disable) = 0;
};

View file

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

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
}

View file

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