diff options
| author | Cole Leavitt <coleleavitt@protonmail.com> | 2025-03-17 10:30:20 -0700 |
|---|---|---|
| committer | Ivan Molodetskikh <yalterz@gmail.com> | 2025-03-17 12:03:43 -0700 |
| commit | 5b6b6a5fe111c3b55ba1f8976ec7a3daac3fc04c (patch) | |
| tree | c67baa6e04542a37d7b75c863cf1309130707f91 | |
| parent | e11af089aaf43ff6f1d4036dbbddccc342c3bc29 (diff) | |
| download | niri-5b6b6a5fe111c3b55ba1f8976ec7a3daac3fc04c.tar.gz niri-5b6b6a5fe111c3b55ba1f8976ec7a3daac3fc04c.tar.bz2 niri-5b6b6a5fe111c3b55ba1f8976ec7a3daac3fc04c.zip | |
Add wait-for-frame-completion-in-pipewire debug flag for NVIDIA screencasts
| -rw-r--r-- | niri-config/src/lib.rs | 3 | ||||
| -rw-r--r-- | src/niri.rs | 31 | ||||
| -rw-r--r-- | src/pw_utils.rs | 41 | ||||
| -rw-r--r-- | wiki/Configuration:-Debug-Options.md | 17 |
4 files changed, 81 insertions, 11 deletions
diff --git a/niri-config/src/lib.rs b/niri-config/src/lib.rs index ddf1ef00..80bc76d7 100644 --- a/niri-config/src/lib.rs +++ b/niri-config/src/lib.rs @@ -2039,6 +2039,8 @@ pub struct DebugConfig { #[knuffel(child)] pub wait_for_frame_completion_before_queueing: bool, #[knuffel(child)] + pub wait_for_frame_completion_in_pipewire: bool, + #[knuffel(child)] pub enable_overlay_planes: bool, #[knuffel(child)] pub disable_cursor_plane: bool, @@ -4837,6 +4839,7 @@ mod tests { preview_render: None, dbus_interfaces_in_non_session_instances: false, wait_for_frame_completion_before_queueing: false, + wait_for_frame_completion_in_pipewire: false, enable_overlay_planes: false, disable_cursor_plane: false, disable_direct_scanout: false, diff --git a/src/niri.rs b/src/niri.rs index 3dc6bf79..e85ec938 100644 --- a/src/niri.rs +++ b/src/niri.rs @@ -1630,8 +1630,12 @@ impl State { match &cast.target { CastTarget::Nothing => { + let config = self.niri.config.borrow(); + let wait_for_sync = config.debug.wait_for_frame_completion_in_pipewire; + drop(config); + self.backend.with_primary_renderer(|renderer| { - if cast.dequeue_buffer_and_clear(renderer) { + if cast.dequeue_buffer_and_clear(renderer, wait_for_sync) { cast.last_frame_time = get_monotonic_time(); } }); @@ -1671,6 +1675,10 @@ impl State { } } + let config = self.niri.config.borrow(); + let wait_for_sync = config.debug.wait_for_frame_completion_in_pipewire; + drop(config); + self.backend.with_primary_renderer(|renderer| { // FIXME: pointer. let elements = mapped @@ -1678,7 +1686,13 @@ impl State { .rev() .collect::<Vec<_>>(); - if cast.dequeue_buffer_and_render(renderer, &elements, bbox.size, scale) { + if cast.dequeue_buffer_and_render( + renderer, + &elements, + bbox.size, + scale, + wait_for_sync, + ) { cast.last_frame_time = get_monotonic_time(); } }); @@ -4370,6 +4384,10 @@ impl Niri { let scale = Scale::from(output.current_scale().fractional_scale()); + let config = self.config.borrow(); + let wait_for_sync = config.debug.wait_for_frame_completion_in_pipewire; + drop(config); + let mut elements = None; let mut casts_to_stop = vec![]; @@ -4401,7 +4419,7 @@ impl Niri { self.render(renderer, output, true, RenderTarget::Screencast) }); - if cast.dequeue_buffer_and_render(renderer, elements, size, scale) { + if cast.dequeue_buffer_and_render(renderer, elements, size, scale, wait_for_sync) { cast.last_frame_time = target_presentation_time; } } @@ -4423,6 +4441,10 @@ impl Niri { let scale = Scale::from(output.current_scale().fractional_scale()); + let config = self.config.borrow(); + let wait_for_sync = config.debug.wait_for_frame_completion_in_pipewire; + drop(config); + let mut casts_to_stop = vec![]; let mut casts = mem::take(&mut self.casts); @@ -4461,7 +4483,8 @@ impl Niri { // FIXME: pointer. let elements: Vec<_> = mapped.render_for_screen_cast(renderer, scale).collect(); - if cast.dequeue_buffer_and_render(renderer, &elements, bbox.size, scale) { + if cast.dequeue_buffer_and_render(renderer, &elements, bbox.size, scale, wait_for_sync) + { cast.last_frame_time = target_presentation_time; } } diff --git a/src/pw_utils.rs b/src/pw_utils.rs index a518de83..c47fa63f 100644 --- a/src/pw_utils.rs +++ b/src/pw_utils.rs @@ -821,6 +821,7 @@ impl Cast { elements: &[impl RenderElement<GlesRenderer>], size: Size<i32, Physical>, scale: Scale<f64>, + wait_for_sync: bool, ) -> bool { let CastState::Ready { damage_tracker, .. } = &mut *self.state.borrow_mut() else { error!("cast must be in Ready state to render"); @@ -851,7 +852,7 @@ impl Cast { let fd = buffer.datas_mut()[0].as_raw().fd; let dmabuf = &self.dmabufs.borrow()[&fd]; - if let Err(err) = render_to_dmabuf( + match render_to_dmabuf( renderer, dmabuf.clone(), size, @@ -859,8 +860,19 @@ impl Cast { Transform::Normal, elements.iter().rev(), ) { - warn!("error rendering to dmabuf: {err:?}"); - return false; + Ok(sync_point) => { + // FIXME: implement PipeWire explicit sync, and at the very least async wait. + if wait_for_sync { + let _span = tracy_client::span!("wait for completion"); + if let Err(err) = sync_point.wait() { + warn!("error waiting for pw frame completion: {err:?}"); + } + } + } + Err(err) => { + warn!("error rendering to dmabuf: {err:?}"); + return false; + } } for (data, (stride, offset)) in @@ -880,7 +892,11 @@ impl Cast { true } - pub fn dequeue_buffer_and_clear(&mut self, renderer: &mut GlesRenderer) -> bool { + pub fn dequeue_buffer_and_clear( + &mut self, + renderer: &mut GlesRenderer, + wait_for_sync: bool, + ) -> bool { // Clear out the damage tracker if we're in Ready state. if let CastState::Ready { damage_tracker, .. } = &mut *self.state.borrow_mut() { *damage_tracker = None; @@ -894,9 +910,20 @@ impl Cast { let fd = buffer.datas_mut()[0].as_raw().fd; let dmabuf = &self.dmabufs.borrow()[&fd]; - if let Err(err) = clear_dmabuf(renderer, dmabuf.clone()) { - warn!("error clearing dmabuf: {err:?}"); - return false; + match clear_dmabuf(renderer, dmabuf.clone()) { + Ok(sync_point) => { + // FIXME: implement PipeWire explicit sync, and at the very least async wait. + if wait_for_sync { + let _span = tracy_client::span!("wait for completion"); + if let Err(err) = sync_point.wait() { + warn!("error waiting for pw frame completion: {err:?}"); + } + } + } + Err(err) => { + warn!("error clearing dmabuf: {err:?}"); + return false; + } } for (data, (stride, offset)) in diff --git a/wiki/Configuration:-Debug-Options.md b/wiki/Configuration:-Debug-Options.md index e3586b7a..0911ebb1 100644 --- a/wiki/Configuration:-Debug-Options.md +++ b/wiki/Configuration:-Debug-Options.md @@ -21,6 +21,7 @@ debug { force-pipewire-invalid-modifier dbus-interfaces-in-non-session-instances wait-for-frame-completion-before-queueing + wait-for-frame-completion-in-pipewire emulate-zero-presentation-time disable-resize-throttling disable-transactions @@ -152,6 +153,22 @@ debug { } ``` +### `wait-for-frame-completion-in-pipewire` + +<sup>Since: next release</sup> + +Wait until every screencast frame is done rendering before handing it over to PipeWire. + +Sometimes helps on NVIDIA to prevent glitched frames when screencasting. + +This debug flag will eventually be removed once we handle this properly (via explicit sync in PipeWire). + +```kdl +debug { + wait-for-frame-completion-in-pipewire +} +``` + ### `emulate-zero-presentation-time` Emulate zero (unknown) presentation time returned from DRM. |
