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

commite8a0d2e518234fefa20ef8c3146a1320ae0f62c3
parent968c182506
authorLucas Galante <[email protected]>
date2026-08-17 12:23
scenefx: fade follows the rounded outline; fix fade-mode scale corruption

Two rect fade bugs that together made cell corner radii invisible:

- quad_round.frag faded by distance to the STRAIGHT edges (a per-axis
  product), forming a square-cornered onset that visually swamped the
  corner arcs whenever fade and radius were combined. The fade now uses
  the same rounded-rect SDF that cuts the corners, so it hugs the arcs.
- The render entity multiplied the whole wire-encoded fade value
  (inset_px * 1000 + fade_mode) by the output scale, corrupting the mode
  digit — quadratic became gaussian on a 2x output. Only the inset part
  scales now.

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

 scenefx/render/fx_renderer/shaders/quad_round.frag | 30 +++++++++-------------
 scenefx/types/scene/wlr_scene.c                    |  8 +++++-
 2 files changed, 19 insertions(+), 19 deletions(-)

diff --git a/scenefx/render/fx_renderer/shaders/quad_round.frag b/scenefx/render/fx_renderer/shaders/quad_round.frag
index 8a2a0e4..0097061 100644
--- a/scenefx/render/fx_renderer/shaders/quad_round.frag
+++ b/scenefx/render/fx_renderer/shaders/quad_round.frag
@@ -65,33 +65,27 @@ void main() {
 
 	vec4 final_color = v_color;
 	if (fade_inset > 0.0) {
-		// Calculate distance to closest vertical and horizontal edges
-		float dist_x = min(relative_pos.x + 0.5, size.x - 0.5 - relative_pos.x);
-		float dist_y = min(relative_pos.y + 0.5, size.y - 0.5 - relative_pos.y);
+		// Fade by distance to the ROUNDED outline — the same SDF that cuts
+		// the corners — not the straight edges. The old per-axis product
+		// formed a square-cornered onset at each corner that visually
+		// swamped the corner arcs whenever fade and radius were combined.
+		float edge_dist = -dist;
 
 		// Clamp to [0, fade_inset] and normalize
-		float factor_x = clamp(dist_x / fade_inset, 0.0, 1.0);
-		float factor_y = clamp(dist_y / fade_inset, 0.0, 1.0);
+		float factor = clamp(edge_dist / fade_inset, 0.0, 1.0);
 
-		// Apply fade mode to components individually for best S-curve transition
 		if (fade_mode == 1) {
-			factor_x = smoothstep(0.0, 1.0, factor_x);
-			factor_y = smoothstep(0.0, 1.0, factor_y);
+			factor = smoothstep(0.0, 1.0, factor);
 		} else if (fade_mode == 2) {
-			factor_x = factor_x * factor_x;
-			factor_y = factor_y * factor_y;
+			factor = factor * factor;
 		} else if (fade_mode == 3) {
-			factor_x = 0.5 - 0.5 * cos(factor_x * 3.14159265);
-			factor_y = 0.5 - 0.5 * cos(factor_y * 3.14159265);
+			factor = 0.5 - 0.5 * cos(factor * 3.14159265);
 		} else if (fade_mode == 4) {
-			float x = 1.0 - factor_x;
-			factor_x = clamp((exp(-3.0 * x * x) - 0.049) / 0.951, 0.0, 1.0);
-			float y = 1.0 - factor_y;
-			factor_y = clamp((exp(-3.0 * y * y) - 0.049) / 0.951, 0.0, 1.0);
+			float x = 1.0 - factor;
+			factor = clamp((exp(-3.0 * x * x) - 0.049) / 0.951, 0.0, 1.0);
 		}
 
-		float combined_factor = factor_x * factor_y;
-		final_color *= pow(combined_factor, 2.2);
+		final_color *= pow(factor, 2.2);
 	}
 
 	gl_FragColor = final_color * quad_corner_alpha * clip_corner_alpha;
diff --git a/scenefx/types/scene/wlr_scene.c b/scenefx/types/scene/wlr_scene.c
index 79b4f47..97d5a40 100644
--- a/scenefx/types/scene/wlr_scene.c
+++ b/scenefx/types/scene/wlr_scene.c
@@ -2279,11 +2279,17 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren
 
 		// TODO: Use the base wlr_render_pass_add_rect as a fast-path in the future
 		if (!fx_corner_radii_is_empty(&rect_corners) || scene_rect->fade_inset > 0) {
+			// fade_inset is wire-encoded as inset_px * 1000 + fade_mode:
+			// scale only the inset part — multiplying the whole encoding
+			// also multiplied the mode digit (quadratic became gaussian on
+			// a 2x output).
+			int fade_enc = scene_rect->fade_inset;
+			int fade_px = (int)((float)(fade_enc / 1000) * data->scale + 0.5f);
 			struct fx_render_rounded_rect_options rounded_rect_options = {
 				.base = rect_options.base,
 				.corners = fx_corner_radii_scale(rect_corners, data->scale),
 				.clipped_region = rect_options.clipped_region,
-				.fade_inset = scene_rect->fade_inset * data->scale,
+				.fade_inset = fade_px * 1000 + fade_enc % 1000,
 			};
 			fx_render_pass_add_rounded_rect(fx_pass, &rounded_rect_options);
 		} else {