kms: Limit frame-callbacks to one per refresh

This commit is contained in:
Victoria Brekenfeld 2024-06-10 20:54:13 +02:00 committed by Victoria Brekenfeld
parent 92f3dbce01
commit 31ff17a323
4 changed files with 83 additions and 38 deletions

View file

@ -77,7 +77,7 @@ use std::{
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
mem, mem,
sync::{ sync::{
atomic::{AtomicBool, AtomicUsize, Ordering}, atomic::{AtomicBool, Ordering},
mpsc::{Receiver, SyncSender}, mpsc::{Receiver, SyncSender},
Arc, Condvar, Mutex, RwLock, Arc, Condvar, Mutex, RwLock,
}, },
@ -104,7 +104,6 @@ pub struct Surface {
known_nodes: HashSet<DrmNode>, known_nodes: HashSet<DrmNode>,
active: Arc<AtomicBool>, active: Arc<AtomicBool>,
frame_callback_seq: Arc<AtomicUsize>,
feedback: HashMap<DrmNode, SurfaceDmabufFeedback>, feedback: HashMap<DrmNode, SurfaceDmabufFeedback>,
plane_formats: HashSet<Format>, plane_formats: HashSet<Format>,
@ -123,7 +122,7 @@ pub struct SurfaceThreadState {
state: QueueState, state: QueueState,
timings: Timings, timings: Timings,
frame_callback_seq: Arc<AtomicUsize>, frame_callback_seq: usize,
thread_sender: Sender<SurfaceCommand>, thread_sender: Sender<SurfaceCommand>,
output: Output, output: Output,
@ -239,7 +238,7 @@ pub enum ThreadCommand {
#[derive(Debug)] #[derive(Debug)]
pub enum SurfaceCommand { pub enum SurfaceCommand {
SendFrames, SendFrames(usize),
RenderStates(RenderElementStates), RenderStates(RenderElementStates),
} }
@ -255,14 +254,11 @@ impl Surface {
shell: Arc<RwLock<Shell>>, shell: Arc<RwLock<Shell>>,
startup_done: Arc<(Mutex<bool>, Condvar)>, startup_done: Arc<(Mutex<bool>, Condvar)>,
) -> Result<Self> { ) -> Result<Self> {
let frame_callback_seq = Arc::new(AtomicUsize::new(0));
let (tx, rx) = channel::<ThreadCommand>(); let (tx, rx) = channel::<ThreadCommand>();
let (tx2, rx2) = channel::<SurfaceCommand>(); let (tx2, rx2) = channel::<SurfaceCommand>();
let active = Arc::new(AtomicBool::new(false)); let active = Arc::new(AtomicBool::new(false));
let active_clone = active.clone(); let active_clone = active.clone();
let frame_callback_seq_clone = frame_callback_seq.clone();
let output_clone = output.clone(); let output_clone = output.clone();
std::thread::Builder::new() std::thread::Builder::new()
@ -274,7 +270,6 @@ impl Surface {
target_node, target_node,
shell, shell,
active_clone, active_clone,
frame_callback_seq_clone,
tx2, tx2,
rx, rx,
startup_done, startup_done,
@ -287,8 +282,8 @@ impl Surface {
let output_clone = output.clone(); let output_clone = output.clone();
let thread_token = evlh let thread_token = evlh
.insert_source(rx2, move |command, _, state| match command { .insert_source(rx2, move |command, _, state| match command {
Event::Msg(SurfaceCommand::SendFrames) => { Event::Msg(SurfaceCommand::SendFrames(sequence)) => {
state.common.send_frames(&output_clone); state.common.send_frames(&output_clone, Some(sequence));
} }
Event::Msg(SurfaceCommand::RenderStates(states)) => { Event::Msg(SurfaceCommand::RenderStates(states)) => {
state.common.update_primary_output(&output_clone, &states); state.common.update_primary_output(&output_clone, &states);
@ -340,7 +335,6 @@ impl Surface {
connector, connector,
crtc, crtc,
output: output.clone(), output: output.clone(),
frame_callback_seq,
known_nodes: HashSet::new(), known_nodes: HashSet::new(),
active, active,
feedback: HashMap::new(), feedback: HashMap::new(),
@ -445,7 +439,6 @@ fn surface_thread(
target_node: DrmNode, target_node: DrmNode,
shell: Arc<RwLock<Shell>>, shell: Arc<RwLock<Shell>>,
active: Arc<AtomicBool>, active: Arc<AtomicBool>,
frame_callback_seq: Arc<AtomicUsize>,
thread_sender: Sender<SurfaceCommand>, thread_sender: Sender<SurfaceCommand>,
thread_receiver: Channel<ThreadCommand>, thread_receiver: Channel<ThreadCommand>,
startup_done: Arc<(Mutex<bool>, Condvar)>, startup_done: Arc<(Mutex<bool>, Condvar)>,
@ -484,7 +477,7 @@ fn surface_thread(
state: QueueState::Idle, state: QueueState::Idle,
timings: Timings::new(None, false), timings: Timings::new(None, false),
frame_callback_seq, frame_callback_seq: 0,
thread_sender, thread_sender,
output, output,
@ -759,7 +752,7 @@ impl SurfaceThreadState {
} }
} }
self.frame_callback_seq.fetch_add(1, Ordering::SeqCst); self.frame_callback_seq = self.frame_callback_seq.wrapping_add(1);
if self.shell.read().unwrap().animations_going() { if self.shell.read().unwrap().animations_going() {
self.queue_redraw(false); self.queue_redraw(false);
@ -1232,7 +1225,7 @@ impl SurfaceThreadState {
}; };
if self.mirroring.is_none() { if self.mirroring.is_none() {
self.frame_callback_seq.fetch_add(1, Ordering::SeqCst); self.frame_callback_seq = self.frame_callback_seq.wrapping_add(1);
let states = frame_result.states; let states = frame_result.states;
self.send_frame_callbacks(); self.send_frame_callbacks();
@ -1311,7 +1304,9 @@ impl SurfaceThreadState {
fn send_frame_callbacks(&mut self) { fn send_frame_callbacks(&mut self) {
if self.mirroring.is_none() { if self.mirroring.is_none() {
let _ = self.thread_sender.send(SurfaceCommand::SendFrames); let _ = self
.thread_sender
.send(SurfaceCommand::SendFrames(self.frame_callback_seq));
} }
} }

View file

@ -71,7 +71,7 @@ impl WinitState {
self.backend self.backend
.submit(damage.map(|x| x.as_slice())) .submit(damage.map(|x| x.as_slice()))
.with_context(|| "Failed to submit buffer for display")?; .with_context(|| "Failed to submit buffer for display")?;
state.send_frames(&self.output); state.send_frames(&self.output, None);
state.update_primary_output(&self.output, &states); state.update_primary_output(&self.output, &states);
state.send_dmabuf_feedback(&self.output, &states, |_| None); state.send_dmabuf_feedback(&self.output, &states, |_| None);
if damage.is_some() { if damage.is_some() {

View file

@ -221,7 +221,7 @@ impl Surface {
self.surface self.surface
.submit() .submit()
.with_context(|| "Failed to submit buffer for display")?; .with_context(|| "Failed to submit buffer for display")?;
state.send_frames(&self.output); state.send_frames(&self.output, None);
state.update_primary_output(&self.output, &states); state.update_primary_output(&self.output, &states);
state.send_dmabuf_feedback(&self.output, &states, |_| None); state.send_dmabuf_feedback(&self.output, &states, |_| None);
if damage.is_some() { if damage.is_some() {

View file

@ -60,7 +60,7 @@ use smithay::{
utils::{Clock, IsAlive, Monotonic}, utils::{Clock, IsAlive, Monotonic},
wayland::{ wayland::{
alpha_modifier::AlphaModifierState, alpha_modifier::AlphaModifierState,
compositor::{CompositorClientState, CompositorState}, compositor::{CompositorClientState, CompositorState, SurfaceData},
dmabuf::{DmabufFeedback, DmabufGlobal, DmabufState}, dmabuf::{DmabufFeedback, DmabufGlobal, DmabufState},
fractional_scale::{with_fractional_scale, FractionalScaleManagerState}, fractional_scale::{with_fractional_scale, FractionalScaleManagerState},
idle_inhibit::IdleInhibitManagerState, idle_inhibit::IdleInhibitManagerState,
@ -233,6 +233,18 @@ pub struct SurfaceDmabufFeedback {
pub scanout_feedback: DmabufFeedback, pub scanout_feedback: DmabufFeedback,
} }
#[derive(Debug)]
struct SurfaceFrameThrottlingState {
last_sent_at: RefCell<Option<(Output, usize)>>,
}
impl Default for SurfaceFrameThrottlingState {
fn default() -> Self {
SurfaceFrameThrottlingState {
last_sent_at: RefCell::new(None),
}
}
}
impl BackendData { impl BackendData {
pub fn kms(&mut self) -> &mut KmsState { pub fn kms(&mut self) -> &mut KmsState {
match self { match self {
@ -836,9 +848,45 @@ impl Common {
} }
} }
pub fn send_frames(&self, output: &Output) { pub fn send_frames(&self, output: &Output, sequence: Option<usize>) {
let time = self.clock.now(); let time = self.clock.now();
let throttle = Some(Duration::from_secs(1)); let should_send = |surface: &WlSurface, states: &SurfaceData| {
// Do the standard primary scanout output check. For pointer surfaces it deduplicates
// the frame callbacks across potentially multiple outputs, and for regular windows and
// layer-shell surfaces it avoids sending frame callbacks to invisible surfaces.
let current_primary_output = surface_primary_scanout_output(surface, states);
if current_primary_output.as_ref() != Some(output) {
return None;
}
let Some(sequence) = sequence else {
return Some(output.clone());
};
// Next, check the throttling status.
let frame_throttling_state = states
.data_map
.get_or_insert(SurfaceFrameThrottlingState::default);
let mut last_sent_at = frame_throttling_state.last_sent_at.borrow_mut();
let mut send = true;
// If we already sent a frame callback to this surface this output refresh
// cycle, don't send one again to prevent empty-damage commit busy loops.
if let Some((last_output, last_sequence)) = &*last_sent_at {
if last_output == output && *last_sequence == sequence {
send = false;
}
}
if send {
*last_sent_at = Some((output.clone(), sequence));
Some(output.clone())
} else {
None
}
};
let throttle = Some(Duration::from_millis(995));
let shell = self.shell.read().unwrap(); let shell = self.shell.read().unwrap();
if let Some(session_lock) = shell.session_lock.as_ref() { if let Some(session_lock) = shell.session_lock.as_ref() {
@ -849,9 +897,13 @@ impl Common {
.set_preferred_scale(output.current_scale().fractional_scale()); .set_preferred_scale(output.current_scale().fractional_scale());
}); });
}); });
send_frames_surface_tree(lock_surface.wl_surface(), output, time, None, |_, _| { send_frames_surface_tree(
Some(output.clone()) lock_surface.wl_surface(),
}); output,
time,
None,
should_send,
);
} }
} }
@ -875,15 +927,19 @@ impl Common {
.unwrap_or(CursorImageStatus::default_named()); .unwrap_or(CursorImageStatus::default_named());
if let CursorImageStatus::Surface(wl_surface) = cursor_status { if let CursorImageStatus::Surface(wl_surface) = cursor_status {
send_frames_surface_tree(&wl_surface, output, time, Some(Duration::ZERO), |_, _| { send_frames_surface_tree(
None &wl_surface,
}) output,
time,
Some(Duration::ZERO),
should_send,
)
} }
if let Some(move_grab) = seat.user_data().get::<SeatMoveGrabState>() { if let Some(move_grab) = seat.user_data().get::<SeatMoveGrabState>() {
if let Some(grab_state) = move_grab.lock().unwrap().as_ref() { if let Some(grab_state) = move_grab.lock().unwrap().as_ref() {
for (window, _) in grab_state.element().windows() { for (window, _) in grab_state.element().windows() {
window.send_frame(output, time, throttle, surface_primary_scanout_output); window.send_frame(output, time, throttle, should_send);
} }
} }
} }
@ -898,14 +954,14 @@ impl Common {
.mapped() .mapped()
.for_each(|mapped| { .for_each(|mapped| {
for (window, _) in mapped.windows() { for (window, _) in mapped.windows() {
window.send_frame(output, time, throttle, surface_primary_scanout_output); window.send_frame(output, time, throttle, should_send);
} }
}); });
let active = shell.active_space(output); let active = shell.active_space(output);
active.mapped().for_each(|mapped| { active.mapped().for_each(|mapped| {
for (window, _) in mapped.windows() { for (window, _) in mapped.windows() {
window.send_frame(output, time, throttle, surface_primary_scanout_output); window.send_frame(output, time, throttle, should_send);
} }
}); });
@ -934,19 +990,13 @@ impl Common {
shell.override_redirect_windows.iter().for_each(|or| { shell.override_redirect_windows.iter().for_each(|or| {
if let Some(wl_surface) = or.wl_surface() { if let Some(wl_surface) = or.wl_surface() {
send_frames_surface_tree( send_frames_surface_tree(&wl_surface, output, time, throttle, should_send);
&wl_surface,
output,
time,
throttle,
surface_primary_scanout_output,
);
} }
}); });
let map = smithay::desktop::layer_map_for_output(output); let map = smithay::desktop::layer_map_for_output(output);
for layer_surface in map.layers() { for layer_surface in map.layers() {
layer_surface.send_frame(output, time, throttle, surface_primary_scanout_output); layer_surface.send_frame(output, time, throttle, should_send);
} }
} }
} }