Add wait-for-frame-completion-in-pipewire debug flag for NVIDIA screencasts

This commit is contained in:
Cole Leavitt
2025-03-17 10:30:20 -07:00
committed by Ivan Molodetskikh
parent e11af089aa
commit 5b6b6a5fe1
4 changed files with 81 additions and 11 deletions
+3
View File
@@ -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,
+27 -4
View File
@@ -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;
}
}
+34 -7
View File
@@ -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
+17
View File
@@ -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.