From ed65fa07a4fedfc27f6e14712a5a8d88d916c322 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Thu, 3 Oct 2024 08:54:15 +0200 Subject: [PATCH] metal: emulate vblank events on the nvidia driver --- release-notes.md | 1 + src/backends/metal/video.rs | 23 +++++++++++++++++++---- src/compositor.rs | 1 + src/state.rs | 1 + src/tasks/connector.rs | 1 + src/tree/output.rs | 10 ++++++++++ src/utils/event_listener.rs | 8 ++++++++ 7 files changed, 41 insertions(+), 4 deletions(-) diff --git a/release-notes.md b/release-notes.md index 54915dd7..ec359474 100644 --- a/release-notes.md +++ b/release-notes.md @@ -3,6 +3,7 @@ - Various bugfixes. - Tiles and workspaces can now be dragged with the mouse. - Vulkan is now the default renderer. +- Emulate vblank events on the nvidia driver. # 1.6.0 (2024-09-25) diff --git a/src/backends/metal/video.rs b/src/backends/metal/video.rs index 12d9d147..eb17e8ea 100644 --- a/src/backends/metal/video.rs +++ b/src/backends/metal/video.rs @@ -740,8 +740,19 @@ impl MetalConnector { fn queue_sequence(&self) { if let Some(crtc) = self.crtc.get() { + if crtc.needs_vblank_emulation.get() { + return; + } if let Err(e) = self.master.queue_sequence(crtc.id) { - log::error!("Could not queue a CRTC sequence: {}", ErrorFmt(e)); + log::error!("Could not queue a CRTC sequence: {}", ErrorFmt(&e)); + if let DrmError::QueueSequence(OsError(c::EOPNOTSUPP)) = e { + if let Some(node) = self.state.root.outputs.get(&self.connector_id) { + log::warn!("{}: Switching to vblank emulation", self.kernel_id()); + crtc.needs_vblank_emulation.set(true); + node.global.connector.needs_vblank_emulation.set(true); + node.vblank(); + } + } } else { crtc.have_queued_sequence.set(true); } @@ -944,6 +955,7 @@ pub struct MetalCrtc { pub mode_blob: CloneCell>>, pub have_queued_sequence: Cell, + pub needs_vblank_emulation: Cell, } impl Debug for MetalCrtc { @@ -1291,6 +1303,7 @@ fn create_crtc( vrr_enabled: props.get("VRR_ENABLED")?.map(|v| v == 1), mode_blob: Default::default(), have_queued_sequence: Cell::new(false), + needs_vblank_emulation: Cell::new(false), }) } @@ -1955,6 +1968,10 @@ impl MetalBackend { connector.queue_sequence(); } self.update_u32_sequence(&connector, sequence); + let time_ns = tv_sec as u64 * 1_000_000_000 + tv_usec as u64 * 1000; + if crtc.needs_vblank_emulation.get() { + self.handle_drm_sequence_event(dev, crtc_id, time_ns as _, connector.sequence.get()); + } connector.can_present.set(true); if let Some(fb) = connector.next_framebuffer.take() { *connector.active_framebuffer.borrow_mut() = Some(fb); @@ -1976,9 +1993,7 @@ impl MetalBackend { { connector.schedule_present(); } - connector - .next_flip_nsec - .set(tv_sec as u64 * 1_000_000_000 + tv_usec as u64 * 1000 + dd.refresh as u64); + connector.next_flip_nsec.set(time_ns + dd.refresh as u64); { let mut flags = KIND_HW_COMPLETION; if connector.presentation_is_sync.get() { diff --git a/src/compositor.rs b/src/compositor.rs index 91405259..61e02dce 100644 --- a/src/compositor.rs +++ b/src/compositor.rs @@ -521,6 +521,7 @@ fn create_dummy_output(state: &Rc) { drm_dev: None, async_event: Default::default(), damaged: Cell::new(false), + needs_vblank_emulation: Cell::new(false), }); let schedule = Rc::new(OutputSchedule::new( &state.ring, diff --git a/src/state.rs b/src/state.rs index f63c23a3..1871084b 100644 --- a/src/state.rs +++ b/src/state.rs @@ -305,6 +305,7 @@ pub struct ConnectorData { pub drm_dev: Option>, pub async_event: Rc, pub damaged: Cell, + pub needs_vblank_emulation: Cell, } pub struct OutputData { diff --git a/src/tasks/connector.rs b/src/tasks/connector.rs index 494764ce..229b6b18 100644 --- a/src/tasks/connector.rs +++ b/src/tasks/connector.rs @@ -32,6 +32,7 @@ pub fn handle(state: &Rc, connector: &Rc) { drm_dev: drm_dev.clone(), async_event: Rc::new(AsyncEvent::default()), damaged: Cell::new(false), + needs_vblank_emulation: Cell::new(false), }); if let Some(dev) = drm_dev { dev.connectors.set(id, data.clone()); diff --git a/src/tree/output.rs b/src/tree/output.rs index a526e00d..d0dffce6 100644 --- a/src/tree/output.rs +++ b/src/tree/output.rs @@ -141,6 +141,16 @@ impl OutputNode { for listener in self.vblank_event.iter() { listener.after_vblank(); } + if self.global.connector.needs_vblank_emulation.get() { + if self.vblank_event.has_listeners() { + self.global.connector.damage(); + } else { + let connector = self.global.connector.clone(); + self.vblank_event.on_attach(Box::new(move || { + connector.damage(); + })); + } + } } pub fn presented(&self, tv_sec: u64, tv_nsec: u32, refresh: u32, seq: u64, flags: u32) { diff --git a/src/utils/event_listener.rs b/src/utils/event_listener.rs index aef88ff2..784ed72f 100644 --- a/src/utils/event_listener.rs +++ b/src/utils/event_listener.rs @@ -30,6 +30,14 @@ impl EventSource { iter: self.listeners.iter(), } } + + pub fn has_listeners(&self) -> bool { + self.listeners.is_not_empty() + } + + pub fn on_attach(&self, f: Box) { + self.on_attach.set(Some(f)); + } } pub struct EventSourceIter {