diff options
| author | srdusr <[email protected]> | 2025-02-17 22:04:00 +0200 |
|---|---|---|
| committer | srdusr <[email protected]> | 2025-02-17 22:04:00 +0200 |
| commit | 78d889c48e5b1336c73f28a2e2d5c8a571c7dd43 (patch) | |
| tree | 4f930c85e79f5f37a3c4e515de885cf1f80f2314 /crates/wayland/src/udev/render.rs | |
| parent | 0a4c4b5941fe982ccb3d3175e26d9d83f0025ffd (diff) | |
| parent | f7c6e9c607742ed373752aadf5761e99ec3eb64a (diff) | |
| download | srdwm-78d889c48e5b1336c73f28a2e2d5c8a571c7dd43.tar.gz srdwm-78d889c48e5b1336c73f28a2e2d5c8a571c7dd43.zip | |
Merge branch 'main' into rust-rewrite
# Conflicts:
# crates/config/src/engine/general.rs
# crates/wayland/src/udev/drm.rs
# crates/wayland/src/udev/mod.rs
# crates/wayland/src/udev/render.rs
# crates/wayland/src/winit/render.rs
Diffstat (limited to 'crates/wayland/src/udev/render.rs')
| -rw-r--r-- | crates/wayland/src/udev/render.rs | 58 |
1 files changed, 46 insertions, 12 deletions
diff --git a/crates/wayland/src/udev/render.rs b/crates/wayland/src/udev/render.rs index d5d776d..3a62292 100644 --- a/crates/wayland/src/udev/render.rs +++ b/crates/wayland/src/udev/render.rs @@ -136,11 +136,12 @@ impl CompState { head.flip_pending = false; } } + let now = Instant::now(); let ready: Vec<(usize, Output)> = udev .heads .iter() .enumerate() - .filter(|(_, h)| !h.flip_pending) + .filter(|(_, h)| !h.flip_pending && h.flip_retry_after.is_none_or(|t| now >= t)) .map(|(i, h)| (i, h.output.clone())) .collect(); // Kept separately from `presented` below: layer-shell surfaces @@ -597,18 +598,35 @@ impl CompState { // rendered rect either way) but not for the border, // which sits inside the shadow's footprint and needs // the *later* push, not the earlier one, to actually - // end up on top of it. Not fragment-clipped against - // `occluders` like the titlebar/border above: at - // `SHADOW_MAX_ALPHA`'s low opacity, a shadow bleeding - // slightly onto a window stacked in front of this one - // reads as a soft edge, not the hard-line bleed-through - // that made the titlebar/border need it. + // end up on top of it. + // + // Fragment-clipped against `occluders` now, same as the + // titlebar/border above - this used to skip that on + // the reasoning that `SHADOW_MAX_ALPHA`'s low opacity + // would read as a soft edge, not the hard-line bleed- + // through that made the titlebar/border need it. True + // along a shadow's straight edges, false at its + // corners: `shadow_bitmap` falls off by Chebyshev + // (square-ring) distance, not radial, so each corner is + // a hard-edged square block at up to ~35% opacity, not + // a soft vignette - reported live as a small dark + // rectangular patch sitting on top of whatever window a + // floating/cascaded window's own corner happened to + // overlap, most visible exactly where two windows' + // corners nearly meet, which this compositor's default + // cascade placement does constantly. if let Some(shadow) = self.shadow_buffers.get(&id) { let rect = decoration::shadow_rect(frame); - let pos = ((rect.x - origin.x) as f64, (rect.y - origin.y) as f64); - match MemoryRenderBufferRenderElement::from_buffer(&mut udev.renderer, pos, shadow, None, None, None, Kind::Unspecified) { - Ok(elem) => custom_elements.push(crate::elements::OverlayElement::Memory(elem)), - Err(e) => log::warn!("udev: failed to import shadow buffer: {e}"), + for fragment in crate::elements::visible_border_fragments(rect, &occluders) { + let pos = ((fragment.x - origin.x) as f64, (fragment.y - origin.y) as f64); + let src = Rectangle::new( + Point::from(((fragment.x - rect.x) as f64, (fragment.y - rect.y) as f64)), + Size::from((fragment.width as f64, fragment.height as f64)), + ); + match MemoryRenderBufferRenderElement::from_buffer(&mut udev.renderer, pos, shadow, None, Some(src), None, Kind::Unspecified) { + Ok(elem) => custom_elements.push(crate::elements::OverlayElement::Memory(elem)), + Err(e) => log::warn!("udev: failed to import shadow buffer: {e}"), + } } } // The window's own content, at its own `opacity` -- @@ -834,9 +852,25 @@ impl CompState { if has_damage { let head = &mut udev.heads[index]; if let Err(e) = head.copy_and_flip(&udev.card, back, &damage_rects) { - log::error!("udev: page flip failed: {e}"); + // Backed off, not retried on the very next poll tick -- + // see `UdevHead::flip_retry_after`'s own doc comment for + // the real, live-reproduced incident this prevents: a + // failing flip (confirmed live as `EBUSY` right after a + // VT-switch resume, while the kernel's own `set_crtc` + // commit was still settling) used to be retried + // immediately, forever, since nothing else gated + // `ready` on anything but `flip_pending` - which a + // failed `page_flip` call never sets. A fixed, short + // cooldown is enough to ride out that kind of transient + // kernel-side race without needing to distinguish it + // from a real, permanent failure - either way, hammering + // the same doomed `page_flip` call in a tight loop with + // no backoff at all was never the right response. + head.flip_retry_after = Some(Instant::now() + Duration::from_millis(200)); + log::error!("udev: page flip failed: {e} - retrying in 200ms"); continue; } + head.flip_retry_after = None; // This buffer is now fully up to date. It won't be rendered // into again until the *other* slot has also been presented // once (strict two-buffer alternation), so by then it will |