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

@ -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);
}
}
}