Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
fix: droplet lens joins the blur damage-extension machinery
The lens samples the frame-so-far around itself, so it inherits the
exact stale-sample artifact apply_blur_region's comment documents for
blur: partial damage under or near a drop re-renders only the damaged
sliver against fresh below-content while neighboring pixels keep the
previous frame's final composite — including the drop's own output —
which flashed segment parts visible/invisible whenever damage swept
nearby (ticking clock/battery text, windows animating under the
strip). Droplet nodes with a live lens now extend damage exactly like
blur nodes: any damage within their sampling reach (refraction px +
the ghost's 0.35-box span, the same margin the render pass copies)
re-renders the whole node from uniformly fresh content.
Verified headless: cursor sweeps within sampling reach of a
refracting drop leave its rendered region byte-identical across six
captures; text ticks change only the glyphs.
Co-Authored-By: Claude Fable 5 <[email protected]>
scenefx/types/scene/wlr_scene.c | 26 +++++++++++++++++++++++++-
1 file changed, 25 insertions(+), 1 deletion(-)
diff --git a/scenefx/types/scene/wlr_scene.c b/scenefx/types/scene/wlr_scene.c
index 21a9667..1781f91 100644
--- a/scenefx/types/scene/wlr_scene.c
+++ b/scenefx/types/scene/wlr_scene.c
@@ -3336,6 +3336,21 @@ static bool should_blur_node_extend_damage(struct wlr_scene_node *node,
return false;
}
break;
+ case WLR_SCENE_NODE_DROPLET:;
+ // The droplet lens samples the frame-so-far around itself, so it has
+ // the same stale-sample problem the blur comment below describes:
+ // partial damage under or NEAR the drop leaves neighboring pixels
+ // holding last frame's final composite (including the drop's own
+ // output), and re-rendering only the damaged sliver bakes those into
+ // part of the lens while the rest keeps the old frame — which is
+ // exactly the visible/invisible flashing on segments whose text
+ // ticks (clock, battery). Whole-node re-render, independent of the
+ // scene blur config.
+ struct wlr_scene_droplet *droplet_node = wlr_scene_droplet_from_node(node);
+ if (droplet_node->refr <= 0.0f && droplet_node->ghost <= 0.0f) {
+ return false;
+ }
+ return true;
default:
return false;
}
@@ -3347,7 +3362,16 @@ static bool apply_blur_region(struct wlr_scene_node *node, struct blur_data *blu
struct render_data *render_data, struct wlr_output_state *output_state,
const pixman_region32_t *original_damage, pixman_region32_t *blur_padding_region) {
bool should_compensate_blur = false;
- const int sample_size = blur_data_calc_size(blur_data);
+ int sample_size = blur_data_calc_size(blur_data);
+ if (node->type == WLR_SCENE_NODE_DROPLET) {
+ // The droplet's sampling reach: rim refraction plus the inverted
+ // ghost's span (0.35 x the box about its center) — the same margin
+ // fx_render_pass_add_droplet copies. Node fields are logical px.
+ struct wlr_scene_droplet *droplet = wlr_scene_droplet_from_node(node);
+ int dim = droplet->width > droplet->height ? droplet->width : droplet->height;
+ sample_size = (int)((droplet->refr + 1.0f + 0.35f * (float)dim)
+ * render_data->scale) + 1;
+ }
pixman_region32_t node_visible_region;
pixman_region32_init(&node_visible_region);