Merge branch 'scene-bbox-caching' into 'master'

wlr_scene: Add bounding-box caching

See merge request wlroots/wlroots!5272
This commit is contained in:
Liam Middlebrook 2026-04-10 13:00:56 +00:00
commit 1a5ecb7264
2 changed files with 91 additions and 1 deletions

View file

@ -92,6 +92,12 @@ struct wlr_scene_tree {
struct wlr_scene_node node;
struct wl_list children; // wlr_scene_node.link
struct {
// Cached bounding box for optimized scene traversal
struct wlr_box bbox;
bool bbox_valid;
} WLR_PRIVATE;
};
/** The root scene-graph node. */

View file

@ -1,4 +1,5 @@
#include <assert.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <wlr/backend.h>
@ -65,6 +66,8 @@ struct wlr_scene *scene_node_get_root(struct wlr_scene_node *node) {
return scene;
}
static void scene_tree_invalidate_bbox(struct wlr_scene_tree *tree);
static void scene_node_init(struct wlr_scene_node *node,
enum wlr_scene_node_type type, struct wlr_scene_tree *parent) {
*node = (struct wlr_scene_node){
@ -80,6 +83,7 @@ static void scene_node_init(struct wlr_scene_node *node,
if (parent != NULL) {
wl_list_insert(parent->children.prev, &node->link);
scene_tree_invalidate_bbox(parent);
}
wlr_addon_set_init(&node->addons);
@ -201,6 +205,65 @@ struct wlr_scene_tree *wlr_scene_tree_create(struct wlr_scene_tree *parent) {
return tree;
}
static void scene_tree_invalidate_bbox(struct wlr_scene_tree *tree) {
while (tree) {
if (!tree->bbox_valid) {
break;
}
tree->bbox_valid = false;
tree = tree->node.parent;
}
}
static int max(int a, int b) {
return a > b ? a : b;
}
static int min(int a, int b) {
return a < b ? a : b;
}
// Compute and cache the bounding box for a tree (in local coordinates)
static void scene_tree_update_bbox(struct wlr_scene_tree *tree) {
if (tree->bbox_valid) {
return;
}
int min_x = INT_MAX, min_y = INT_MAX, max_x = INT_MIN, max_y = INT_MIN;
struct wlr_scene_node *child;
wl_list_for_each(child, &tree->children, link) {
if (child->type == WLR_SCENE_NODE_TREE) {
struct wlr_scene_tree *child_tree = wlr_scene_tree_from_node(child);
scene_tree_update_bbox(child_tree);
// Child tree bbox is in child-local coordinates
// Transform to parent coordinates by adding child->x/y
min_x = min(min_x, child->x + child_tree->bbox.x);
min_y = min(min_y, child->y + child_tree->bbox.y);
max_x = max(max_x, child->x + child_tree->bbox.x + child_tree->bbox.width);
max_y = max(max_y, child->y + child_tree->bbox.y + child_tree->bbox.height);
} else {
int child_w = 0, child_h = 0;
scene_node_get_size(child, &child_w, &child_h);
min_x = min(min_x, child->x);
min_y = min(min_y, child->y);
max_x = max(max_x, child->x + child_w);
max_y = max(max_y, child->y + child_h);
}
}
// Only set the bbox as valid when there are valid children are present
if (min_x != INT_MAX && max_x != INT_MIN) {
tree->bbox.x = min_x;
tree->bbox.y = min_y;
// the max clamps negative sizes (i.e., not enabled nodes) to zero
tree->bbox.width = max(0, max_x - min_x);
tree->bbox.height = max(0, max_y - min_y);
tree->bbox_valid = true;
}
}
typedef bool (*scene_node_box_iterator_func_t)(struct wlr_scene_node *node,
int sx, int sy, void *data);
@ -213,6 +276,19 @@ static bool _scene_nodes_in_box(struct wlr_scene_node *node, struct wlr_box *box
switch (node->type) {
case WLR_SCENE_NODE_TREE:;
struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node);
// Check if this subtree's bbox intersects the query box
scene_tree_update_bbox(scene_tree);
struct wlr_box tree_box = {
.x = lx + scene_tree->bbox.x,
.y = ly + scene_tree->bbox.y,
.width = scene_tree->bbox.width,
.height = scene_tree->bbox.height,
};
if (!wlr_box_empty(&tree_box) && !wlr_box_intersection(&tree_box, &tree_box, box)) {
return false;
}
struct wlr_scene_node *child;
wl_list_for_each_reverse(child, &scene_tree->children, link) {
if (_scene_nodes_in_box(child, box, iterator, user_data, lx + child->x, ly + child->y)) {
@ -752,6 +828,7 @@ void wlr_scene_rect_set_size(struct wlr_scene_rect *rect, int width, int height)
rect->width = width;
rect->height = height;
scene_tree_invalidate_bbox(rect->node.parent);
scene_node_update(&rect->node, NULL);
}
@ -1067,6 +1144,7 @@ void wlr_scene_buffer_set_dest_size(struct wlr_scene_buffer *scene_buffer,
assert(width >= 0 && height >= 0);
scene_buffer->dst_width = width;
scene_buffer->dst_height = height;
scene_tree_invalidate_bbox(scene_buffer->node.parent);
scene_node_update(&scene_buffer->node, NULL);
}
@ -1209,7 +1287,7 @@ void wlr_scene_node_set_enabled(struct wlr_scene_node *node, bool enabled) {
}
node->enabled = enabled;
scene_tree_invalidate_bbox(node->parent);
scene_node_update(node, &visible);
}
@ -1220,6 +1298,7 @@ void wlr_scene_node_set_position(struct wlr_scene_node *node, int x, int y) {
node->x = x;
node->y = y;
scene_tree_invalidate_bbox(node->parent);
scene_node_update(node, NULL);
}
@ -1290,9 +1369,14 @@ void wlr_scene_node_reparent(struct wlr_scene_node *node,
scene_node_visibility(node, &visible);
}
scene_tree_invalidate_bbox(node->parent);
wl_list_remove(&node->link);
node->parent = new_parent;
wl_list_insert(new_parent->children.prev, &node->link);
scene_tree_invalidate_bbox(new_parent);
scene_node_update(node, &visible);
}