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

View file

@ -221,7 +221,7 @@ impl Surface {
self.surface
.submit()
.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.send_dmabuf_feedback(&self.output, &states, |_| None);
if damage.is_some() {

View file

@ -60,7 +60,7 @@ use smithay::{
utils::{Clock, IsAlive, Monotonic},
wayland::{
alpha_modifier::AlphaModifierState,
compositor::{CompositorClientState, CompositorState},
compositor::{CompositorClientState, CompositorState, SurfaceData},
dmabuf::{DmabufFeedback, DmabufGlobal, DmabufState},
fractional_scale::{with_fractional_scale, FractionalScaleManagerState},
idle_inhibit::IdleInhibitManagerState,
@ -233,6 +233,18 @@ pub struct SurfaceDmabufFeedback {
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 {
pub fn kms(&mut self) -> &mut KmsState {
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 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();
if let Some(session_lock) = shell.session_lock.as_ref() {
@ -849,9 +897,13 @@ impl Common {
.set_preferred_scale(output.current_scale().fractional_scale());
});
});
send_frames_surface_tree(lock_surface.wl_surface(), output, time, None, |_, _| {
Some(output.clone())
});
send_frames_surface_tree(
lock_surface.wl_surface(),
output,
time,
None,
should_send,
);
}
}
@ -875,15 +927,19 @@ impl Common {
.unwrap_or(CursorImageStatus::default_named());
if let CursorImageStatus::Surface(wl_surface) = cursor_status {
send_frames_surface_tree(&wl_surface, output, time, Some(Duration::ZERO), |_, _| {
None
})
send_frames_surface_tree(
&wl_surface,
output,
time,
Some(Duration::ZERO),
should_send,
)
}
if let Some(move_grab) = seat.user_data().get::<SeatMoveGrabState>() {
if let Some(grab_state) = move_grab.lock().unwrap().as_ref() {
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()
.for_each(|mapped| {
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);
active.mapped().for_each(|mapped| {
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| {
if let Some(wl_surface) = or.wl_surface() {
send_frames_surface_tree(
&wl_surface,
output,
time,
throttle,
surface_primary_scanout_output,
);
send_frames_surface_tree(&wl_surface, output, time, throttle, should_send);
}
});
let map = smithay::desktop::layer_map_for_output(output);
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);
}
}
}