wlroots/util/rect_union.c
Kenny Levinsen 57441ded02 util/rect_union: Limit rect_union_add to 1024 rects
If a very large number of clip rects are accumulated in rect_union_add,
rect_union_evaluate can end up being disproportionately expensive, and
as an extreme numbers of clip rects are not beneficial for drawing, this
is without any benefit.

Limit the number of rects to 1024 in rect_union_add, switching over to
bounding box mode instead when the limit is exceeded. This leads to a
small 70% reduction in CPU time in the Vulkan renderer on the
stacked/clip200/1024 benchmarks.

Signed-off-by: Kenny Levinsen <kl@kl.wtf>
2026-05-01 12:50:04 +00:00

99 lines
2.3 KiB
C

#include <limits.h>
#include "util/rect_union.h"
static void box_union(pixman_box32_t *dst, const pixman_box32_t *box) {
dst->x1 = dst->x1 < box->x1 ? dst->x1 : box->x1;
dst->y1 = dst->y1 < box->y1 ? dst->y1 : box->y1;
dst->x2 = dst->x2 > box->x2 ? dst->x2 : box->x2;
dst->y2 = dst->y2 > box->y2 ? dst->y2 : box->y2;
}
static bool box_empty_or_invalid(const pixman_box32_t *box) {
return box->x1 >= box->x2 || box->y1 >= box->y2;
}
void rect_union_init(struct rect_union *ru) {
*ru = (struct rect_union) {
.alloc_failure = false,
.bounding_box = (pixman_box32_t) {
.x1 = INT_MAX,
.x2 = INT_MIN,
.y1 = INT_MAX,
.y2 = INT_MIN,
}
};
pixman_region32_init(&ru->region);
wl_array_init(&ru->unsorted);
};
void rect_union_finish(struct rect_union *ru) {
pixman_region32_fini(&ru->region);
wl_array_release(&ru->unsorted);
}
static void handle_alloc_failure(struct rect_union *ru) {
ru->alloc_failure = true;
wl_array_release(&ru->unsorted);
wl_array_init(&ru->unsorted);
}
void rect_union_add(struct rect_union *ru, const pixman_box32_t *box) {
if (box_empty_or_invalid(box)) {
return;
}
box_union(&ru->bounding_box, box);
if (ru->alloc_failure) {
return;
}
int nrects = (int)(ru->unsorted.size / sizeof(pixman_box32_t));
if (nrects >= 1024) {
handle_alloc_failure(ru);
return;
}
pixman_box32_t *entry = wl_array_add(&ru->unsorted, sizeof(*entry));
if (entry) {
*entry = *box;
} else {
handle_alloc_failure(ru);
}
}
const pixman_region32_t *rect_union_evaluate(struct rect_union *ru) {
if (ru->alloc_failure) {
goto bounding_box;
}
int nrects = (int)(ru->unsorted.size / sizeof(pixman_box32_t));
pixman_region32_t reg;
bool ok = pixman_region32_init_rects(&reg, ru->unsorted.data, nrects);
if (!ok) {
handle_alloc_failure(ru);
goto bounding_box;
}
ok = pixman_region32_union(&reg, &reg, &ru->region);
if (!ok) {
pixman_region32_fini(&reg);
handle_alloc_failure(ru);
goto bounding_box;
}
pixman_region32_fini(&ru->region);
// pixman_region32_t is safe to move
ru->region = reg;
wl_array_release(&ru->unsorted);
wl_array_init(&ru->unsorted);
return &ru->region;
bounding_box:
pixman_region32_fini(&ru->region);
if (box_empty_or_invalid(&ru->bounding_box)) {
pixman_region32_init(&ru->region);
} else {
pixman_region32_init_with_extents(&ru->region, &ru->bounding_box);
}
return &ru->region;
}