kms: Limit frame-callbacks to one per refresh
This commit is contained in:
parent
92f3dbce01
commit
31ff17a323
4 changed files with 83 additions and 38 deletions
|
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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() {
|
||||||
|
|
|
||||||
|
|
@ -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() {
|
||||||
|
|
|
||||||
90
src/state.rs
90
src/state.rs
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue