git.lucas.co / cce-compositor
Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git

commitcb6826c041a192ed84bf897c3035bec656f7e506
parent092b91e9e5
authorLucas Galante <[email protected]>
date2026-06-30 19:50
fix(scenefx): mark optimized blur dirty when underlying nodes update

 scenefx/types/scene/wlr_scene.c | 67 +++++++++++++++++++++++++++++++++++++++--
 1 file changed, 64 insertions(+), 3 deletions(-)

diff --git a/scenefx/types/scene/wlr_scene.c b/scenefx/types/scene/wlr_scene.c
index aa3e1cd..641d93f 100644
--- a/scenefx/types/scene/wlr_scene.c
+++ b/scenefx/types/scene/wlr_scene.c
@@ -399,6 +399,7 @@ static void scene_node_opaque_region(struct wlr_scene_node *node, int x, int y,
 }
 
 struct scene_update_data {
+	struct wlr_scene_node *updated_node;
 	pixman_region32_t *visible;
 	const pixman_region32_t *update_region;
 	struct wlr_box update_box;
@@ -680,12 +681,70 @@ static void restack_xwayland_surface(struct wlr_scene_node *node,
 }
 #endif
 
+static bool scene_node_is_below(struct wlr_scene_node *a, struct wlr_scene_node *b) {
+	if (a == b) {
+		return false;
+	}
+
+	struct wlr_scene_node *chain_a[64];
+	struct wlr_scene_node *chain_b[64];
+	int len_a = 0;
+	int len_b = 0;
+
+	struct wlr_scene_node *curr = a;
+	while (curr && len_a < 64) {
+		chain_a[len_a++] = curr;
+		curr = curr->parent ? &curr->parent->node : NULL;
+	}
+
+	curr = b;
+	while (curr && len_b < 64) {
+		chain_b[len_b++] = curr;
+		curr = curr->parent ? &curr->parent->node : NULL;
+	}
+
+	int idx_a = len_a - 1;
+	int idx_b = len_b - 1;
+
+	if (chain_a[idx_a] != chain_b[idx_b]) {
+		return false;
+	}
+
+	while (idx_a >= 0 && idx_b >= 0 && chain_a[idx_a] == chain_b[idx_b]) {
+		idx_a--;
+		idx_b--;
+	}
+
+	if (idx_a < 0) {
+		return true;
+	}
+	if (idx_b < 0) {
+		return false;
+	}
+
+	struct wlr_scene_tree *lca = wlr_scene_tree_from_node(chain_a[idx_a + 1]);
+	struct wlr_scene_node *child;
+	wl_list_for_each(child, &lca->children, link) {
+		if (child == chain_a[idx_a]) {
+			return true;
+		}
+		if (child == chain_b[idx_b]) {
+			return false;
+		}
+	}
+
+	return false;
+}
+
 static bool scene_node_update_iterator(struct wlr_scene_node *node,
 		int lx, int ly, void *_data) {
 	struct scene_update_data *data = _data;
 
 	if (node->type == WLR_SCENE_NODE_OPTIMIZED_BLUR) {
 		struct wlr_scene_optimized_blur *scene_blur = wlr_scene_optimized_blur_from_node(node);
+		if (data->updated_node && scene_node_is_below(data->updated_node, node)) {
+			scene_blur->dirty = true;
+		}
 		if (scene_blur->dirty) {
 			// Restore the visible region back to default, without any opaque
 			// regions. This ensures that all nodes below are fully re-rendered
@@ -760,6 +819,7 @@ static void scene_node_bounds(struct wlr_scene_node *node,
 }
 
 static void scene_update_region(struct wlr_scene *scene,
+		struct wlr_scene_node *updated_node,
 		const pixman_region32_t *update_region) {
 	pixman_region32_t visible;
 	pixman_region32_init(&visible);
@@ -767,6 +827,7 @@ static void scene_update_region(struct wlr_scene *scene,
 
 	struct pixman_box32 *region_box = pixman_region32_extents(update_region);
 	struct scene_update_data data = {
+		.updated_node = updated_node,
 		.visible = &visible,
 		.update_region = update_region,
 		.update_box = {
@@ -842,7 +903,7 @@ static void scene_node_update(struct wlr_scene_node *node,
 		if (damage) {
 			scene_node_cleanup_when_disabled(node, scene->restack_xwayland_surfaces, &scene->outputs);
 
-			scene_update_region(scene, damage);
+			scene_update_region(scene, node, damage);
 			scene_damage_outputs(scene, damage);
 			pixman_region32_fini(damage);
 		}
@@ -862,7 +923,7 @@ static void scene_node_update(struct wlr_scene_node *node,
 	pixman_region32_copy(&update_region, damage);
 	scene_node_bounds(node, x, y, &update_region);
 
-	scene_update_region(scene, &update_region);
+	scene_update_region(scene, node, &update_region);
 	pixman_region32_fini(&update_region);
 
 	scene_node_visibility(node, damage);
@@ -1552,7 +1613,7 @@ void wlr_scene_buffer_set_opaque_region(struct wlr_scene_buffer *scene_buffer,
 	pixman_region32_t update_region;
 	pixman_region32_init(&update_region);
 	scene_node_bounds(&scene_buffer->node, x, y, &update_region);
-	scene_update_region(scene_node_get_root(&scene_buffer->node), &update_region);
+	scene_update_region(scene_node_get_root(&scene_buffer->node), &scene_buffer->node, &update_region);
 	pixman_region32_fini(&update_region);
 }