From a3aa5fca12b59df0c1ee421b482a51a425e25abd Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Sat, 30 Sep 2023 17:13:56 +0400 Subject: Refactor frame scheduling Combine the redraw state variables into one enum, and refactor to get rid of the requirement that a VBlank must queue a subsequent redraw. Also fix the bug where ongoing animations that produced no damage could stall the redrawing. --- src/backend/tty.rs | 77 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 57 insertions(+), 20 deletions(-) (limited to 'src/backend/tty.rs') diff --git a/src/backend/tty.rs b/src/backend/tty.rs index 11595ef7..6f665432 100644 --- a/src/backend/tty.rs +++ b/src/backend/tty.rs @@ -1,5 +1,6 @@ use std::cell::RefCell; use std::collections::{HashMap, HashSet}; +use std::mem; use std::os::fd::FromRawFd; use std::path::{Path, PathBuf}; use std::rc::Rc; @@ -38,7 +39,7 @@ use smithay_drm_extras::drm_scanner::{DrmScanEvent, DrmScanner}; use smithay_drm_extras::edid::EdidInfo; use crate::config::Config; -use crate::niri::{OutputRenderElements, State}; +use crate::niri::{OutputRenderElements, State, RedrawState}; use crate::utils::get_monotonic_time; use crate::Niri; @@ -703,34 +704,55 @@ impl Tty { .plot(surface.sequence_delta_plot_name, delta); } - assert!(output_state.waiting_for_vblank); - assert!(output_state.estimated_vblank_timer.is_none()); - - output_state.waiting_for_vblank = false; output_state.frame_clock.presented(presentation_time); output_state.current_estimated_sequence = Some(meta.sequence); - niri.queue_redraw(output); + + let redraw_needed = match mem::replace(&mut output_state.redraw_state, RedrawState::Idle) { + RedrawState::Idle => unreachable!(), + RedrawState::Queued(_) => unreachable!(), + RedrawState::WaitingForVBlank { redraw_needed } => redraw_needed, + RedrawState::WaitingForEstimatedVBlank(_) => unreachable!(), + RedrawState::WaitingForEstimatedVBlankAndQueued(_) => unreachable!(), + }; + + if redraw_needed || output_state.unfinished_animations_remain { + niri.queue_redraw(output); + } else { + niri.send_frame_callbacks(&output); + } } - fn on_estimated_vblank_timer(&self, niri: &mut Niri, output: &Output) { + fn on_estimated_vblank_timer(&self, niri: &mut Niri, output: Output) { let span = tracy_client::span!("Tty::on_estimated_vblank_timer"); let name = output.name(); span.emit_text(&name); - let Some(output_state) = niri.output_state.get_mut(output) else { + let Some(output_state) = niri.output_state.get_mut(&output) else { error!("missing output state for {name}"); return; }; - assert!(!output_state.waiting_for_vblank); - let token = output_state.estimated_vblank_timer.take(); - assert!(token.is_some()); + match mem::replace(&mut output_state.redraw_state, RedrawState::Idle) { + RedrawState::Idle => unreachable!(), + RedrawState::Queued(_) => unreachable!(), + RedrawState::WaitingForVBlank { .. } => unreachable!(), + RedrawState::WaitingForEstimatedVBlank(_) => (), + // The timer fired just in front of a redraw. + RedrawState::WaitingForEstimatedVBlankAndQueued((_, idle)) => { + output_state.redraw_state = RedrawState::Queued(idle); + return; + } + } if let Some(sequence) = output_state.current_estimated_sequence.as_mut() { *sequence = sequence.wrapping_add(1); - niri.send_frame_callbacks(output); + if output_state.unfinished_animations_remain { + niri.queue_redraw(output); + } else { + niri.send_frame_callbacks(&output); + } } } @@ -793,10 +815,18 @@ impl Tty { match drm_compositor.queue_frame(data) { Ok(()) => { let output_state = niri.output_state.get_mut(output).unwrap(); - output_state.waiting_for_vblank = true; - if let Some(token) = output_state.estimated_vblank_timer.take() { - niri.event_loop.remove(token); - } + let new_state = RedrawState::WaitingForVBlank { + redraw_needed: false, + }; + match mem::replace(&mut output_state.redraw_state, new_state) { + RedrawState::Idle => unreachable!(), + RedrawState::Queued(_) => (), + RedrawState::WaitingForVBlank { .. } => unreachable!(), + RedrawState::WaitingForEstimatedVBlank(_) => unreachable!(), + RedrawState::WaitingForEstimatedVBlankAndQueued((token, _)) => { + niri.event_loop.remove(token); + } + }; return Some(&surface.dmabuf_feedback); } @@ -904,8 +934,15 @@ fn queue_estimated_vblank_timer( target_presentation_time: Duration, ) { let output_state = niri.output_state.get_mut(&output).unwrap(); - if output_state.estimated_vblank_timer.is_some() { - return; + match mem::take(&mut output_state.redraw_state) { + RedrawState::Idle => unreachable!(), + RedrawState::Queued(_) => (), + RedrawState::WaitingForVBlank { .. } => unreachable!(), + RedrawState::WaitingForEstimatedVBlank(token) + | RedrawState::WaitingForEstimatedVBlankAndQueued((token, _)) => { + output_state.redraw_state = RedrawState::WaitingForEstimatedVBlank(token); + return; + } } let now = get_monotonic_time(); @@ -915,9 +952,9 @@ fn queue_estimated_vblank_timer( .insert_source(timer, move |_, _, data| { data.backend .tty() - .on_estimated_vblank_timer(&mut data.niri, &output); + .on_estimated_vblank_timer(&mut data.niri, output.clone()); TimeoutAction::Drop }) .unwrap(); - output_state.estimated_vblank_timer = Some(token); + output_state.redraw_state = RedrawState::WaitingForEstimatedVBlank(token); } -- cgit