aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCole Leavitt <coleleavitt@protonmail.com>2025-03-17 10:30:20 -0700
committerIvan Molodetskikh <yalterz@gmail.com>2025-03-17 12:03:43 -0700
commit5b6b6a5fe111c3b55ba1f8976ec7a3daac3fc04c (patch)
treec67baa6e04542a37d7b75c863cf1309130707f91
parente11af089aaf43ff6f1d4036dbbddccc342c3bc29 (diff)
downloadniri-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.rs3
-rw-r--r--src/niri.rs31
-rw-r--r--src/pw_utils.rs41
-rw-r--r--wiki/Configuration:-Debug-Options.md17
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.