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

commit291f8fe76d57886a7603db71955ce0efb2f23ad0
parenta4ea755592
authorLucas Galante <[email protected]>
date2026-07-17 11:34
fix(scenefx): stop blur-node reorder churn in river_scene_node_enable_blur

The trailing lower_to_bottom pair is not idempotent: on an already-ordered
tree ([opt_blur, std_blur, content]) each call swaps the two blur nodes
twice, and every swap fires scene_node_update over the node's whole
window-sized region. Since enable_blur runs on every surface commit and
every render_finish, every blurred window's full area was damaged and
recomposited on every wm tick — the background flicker visible through
semi-opaque unfocused (dimmed) windows. Reorder only when the desired
bottom order doesn't already hold.

Co-Authored-By: Claude Fable 5 <[email protected]>

 src/server/wlroots_log_wrapper.c | 23 +++++++++++++++++------
 1 file changed, 17 insertions(+), 6 deletions(-)

diff --git a/src/server/wlroots_log_wrapper.c b/src/server/wlroots_log_wrapper.c
index ef47fd7..65b36e9 100644
--- a/src/server/wlroots_log_wrapper.c
+++ b/src/server/wlroots_log_wrapper.c
@@ -770,12 +770,23 @@ void river_scene_node_enable_blur(struct wlr_scene_node *node, bool enabled, boo
 		wlr_scene_blur_set_transparency_mask_source((struct wlr_scene_blur *)std_blur_node, source_buffer);
 	}
 
-	// Ensure correct stack order (from back to front): opt_blur_node -> std_blur_node -> window content
-	if (std_blur_node) {
-		wlr_scene_node_lower_to_bottom(std_blur_node);
-	}
-	if (opt_blur_node) {
-		wlr_scene_node_lower_to_bottom(opt_blur_node);
+	// Ensure correct stack order (from back to front): opt_blur_node -> std_blur_node -> window content.
+	// Only reorder when out of order: the lower_to_bottom pair is not idempotent
+	// (on an already-ordered tree each call swaps the two nodes, and every swap
+	// damages the node's whole window-sized region — this runs per commit and
+	// per render_finish, so the no-op path must not touch the scene graph).
+	struct wlr_scene_node *want_bottom = opt_blur_node ? opt_blur_node : std_blur_node;
+	struct wlr_scene_node *want_second = opt_blur_node ? std_blur_node : NULL;
+	bool ordered = want_bottom != NULL
+		&& tree->children.next == &want_bottom->link
+		&& (want_second == NULL || want_bottom->link.next == &want_second->link);
+	if (!ordered) {
+		if (std_blur_node) {
+			wlr_scene_node_lower_to_bottom(std_blur_node);
+		}
+		if (opt_blur_node) {
+			wlr_scene_node_lower_to_bottom(opt_blur_node);
+		}
 	}
 }