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

commit0ca12632aa8840ebbe92f69b56ddb60abe87a058
parent0aea884d10
authorLucas Galante <[email protected]>
date2026-08-12 18:53
fix: optimized blur works for rounded windows — drop the radius opt-out

The `radius > 0 -> use_optimized = false` gate (7fd57da) assumed the
optimized blur node paints a square under the rounded standard node.
It doesn't: fx_render_pass_add_optimized_blur only re-bakes the shared
offscreen cache (read_to_buffer), and the visible shape — including
corner radii — always comes from the standard blur node's sampler,
which carries wlr_scene_blur_set_corner_radius in both modes. Since
every decorated window is rounded, the gate silently disabled
scenefx_optimized_blur DE-wide, forcing full-backdrop dual-kawase blur
per frame per translucent window (3× ~2064x2064 'optimized: 0' passes
per frame, ~66fps even at idle).

Measured downstream effect (cce-ui@a73777c / cce-system-interface@
9a8c7d3 tracers): compositor frame rate drops 66->~30fps under hover
traffic, frame-done to demand-driven clients arrives 2-7 vsyncs late
(28-99ms, growing), hover feedback lands at 4-10Hz. Layer-shell status
gates are untouched (status backdrop deliberately live-blurs the
windows beneath it).

VERIFY PENDING restart: the running session picks up the new cce-fx on
next compositor restart. Recheck with the same tracers: idle should
quiesce (no 66fps re-render loop), and hover-sweep frame-done waits
should sit at ~16ms.

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

 src/server/window.rs       | 23 ++++++++++++++++-------
 src/server/xdg_toplevel.rs |  9 ++++++++-
 2 files changed, 24 insertions(+), 8 deletions(-)

diff --git a/src/server/window.rs b/src/server/window.rs
index 97ecaf3..c840432 100644
--- a/src/server/window.rs
+++ b/src/server/window.rs
@@ -2047,12 +2047,14 @@ impl Window {
             } else {
                 0
             };
-            // The optimized blur node sits UNDER the standard one and cannot be rounded
-            // (wlr_scene_optimized_blur has no radius field in the vendored scenefx), so
-            // for a window with rounded corners it would keep painting square corners
-            // underneath a correctly rounded standard blur. Trade the optimization away
-            // exactly where it would be visible, and keep it everywhere else.
-            let use_optimized = if is_status || radius > 0 {
+            // Rounded corners do NOT require live blur: the corner shape is applied by the
+            // standard blur node's sampler (wlr_scene_blur_set_corner_radius) in both modes;
+            // the optimized node only re-bakes the shared offscreen cache
+            // (fx_render_pass_add_optimized_blur -> read_to_buffer) and never paints on
+            // screen. The old `radius > 0` opt-out silently disabled the optimization for
+            // every (rounded) window, forcing full-backdrop dual-kawase blur per frame per
+            // translucent window — the DE-wide hover-lag / constant-GPU-load root cause.
+            let use_optimized = if is_status {
                 false
             } else {
                 (*self.server).wm.layout.scenefx_optimized_blur
@@ -2508,7 +2510,14 @@ impl Window {
                 } else {
                     0
                 };
-                let use_optimized = if is_status || radius > 0 {
+                // Rounded corners do NOT require live blur: the corner shape is applied by the
+                // standard blur node's sampler (wlr_scene_blur_set_corner_radius) in both modes;
+                // the optimized node only re-bakes the shared offscreen cache
+                // (fx_render_pass_add_optimized_blur -> read_to_buffer) and never paints on
+                // screen. The old `radius > 0` opt-out silently disabled the optimization for
+                // every (rounded) window, forcing full-backdrop dual-kawase blur per frame per
+                // translucent window — the DE-wide hover-lag / constant-GPU-load root cause.
+                let use_optimized = if is_status {
                     false
                 } else {
                     (*self.server).wm.layout.scenefx_optimized_blur
diff --git a/src/server/xdg_toplevel.rs b/src/server/xdg_toplevel.rs
index 9b2a4ad..2c2fe54 100644
--- a/src/server/xdg_toplevel.rs
+++ b/src/server/xdg_toplevel.rs
@@ -604,7 +604,14 @@ unsafe extern "C" fn handle_commit(listener: *mut ffi::wl_listener, _data: *mut
     } else {
         crate::window::widen_corner_radius(radius, actual_w as i32, actual_h as i32)
     };
-    let use_optimized = if is_status || radius > 0 {
+    // Rounded corners do NOT require live blur: the corner shape is applied by the
+    // standard blur node's sampler (wlr_scene_blur_set_corner_radius) in both modes;
+    // the optimized node only re-bakes the shared offscreen cache
+    // (fx_render_pass_add_optimized_blur -> read_to_buffer) and never paints on
+    // screen. The old `radius > 0` opt-out silently disabled the optimization for
+    // every (rounded) window, forcing full-backdrop dual-kawase blur per frame per
+    // translucent window — the DE-wide hover-lag / constant-GPU-load root cause.
+    let use_optimized = if is_status {
         false
     } else {
         (*(*window).server).wm.layout.scenefx_optimized_blur