diff options
| author | srdusr <[email protected]> | 2024-08-29 21:31:00 +0200 |
|---|---|---|
| committer | srdusr <[email protected]> | 2024-08-29 21:31:00 +0200 |
| commit | 3509cf6127185d9fed5a63ceca18a003213a20d7 (patch) | |
| tree | 661e39acfebd37cbb6abaddc686df724d869b988 | |
| parent | 4fb537aa4cfe52e364dbcd9a97078f0206ada54b (diff) | |
| download | srdwm-3509cf6127185d9fed5a63ceca18a003213a20d7.tar.gz srdwm-3509cf6127185d9fed5a63ceca18a003213a20d7.zip | |
Fix VT-switch resume getting stuck on a stale pending page flip
A flip issued right before a VT switch away could still be undelivered
when the session resumed - the kernel refuses a new page flip on a CRTC
with one already outstanding, which showed up live as a black screen
that never recovered across two switch attempts, with a rapid repeating
"Device or resource busy" loop in the log. Drain and apply any pending
DRM events before reasserting CRTCs and rendering again on resume, so a
stale flip from before the switch can't collide with the fresh one.
| -rw-r--r-- | crates/wayland/src/udev/session.rs | 25 |
1 files changed, 24 insertions, 1 deletions
diff --git a/crates/wayland/src/udev/session.rs b/crates/wayland/src/udev/session.rs index 2853f33..fa40b0e 100644 --- a/crates/wayland/src/udev/session.rs +++ b/crates/wayland/src/udev/session.rs @@ -63,9 +63,32 @@ pub(crate) fn register_session_notifier(handle: &LoopHandle<'static, CompState>, SessionEvent::ActivateSession => { log::info!("udev: session resumed (VT switch back)"); udev.active = true; + let card = udev.card.clone(); + + // A flip issued right before the VT switch away may + // never have completed while inactive (nothing was + // scanning out), and its completion event can still be + // sitting undelivered on the DRM fd. The kernel refuses + // a new page flip on a CRTC with one already + // unacknowledged (EBUSY) - drain and apply any such + // events now, before reasserting crtcs, so a stale flip + // from before the switch can't collide with the fresh + // one `render_udev_frame` is about to issue below. + match card.receive_events() { + Ok(events) => { + for event in events { + let DrmEvent::PageFlip(flip) = event else { continue }; + if let Some(head) = udev.heads.iter_mut().find(|h| h.crtc == flip.crtc) { + head.front = 1 - head.front; + head.flip_pending = false; + } + } + } + Err(e) => log::debug!("udev: no pending flip events to drain on resume: {e}"), + } + // Some drivers reset mode-setting state across a VT // switch; reassert every head before rendering again. - let card = udev.card.clone(); for head in &mut udev.heads { let fb = head.buffers[head.front].fb; if let Err(e) = card.set_crtc(head.crtc, Some(fb), (0, 0), &[], None) { |