wayland/compositor: Add per-surface frame time estimation
This commit is contained in:
parent
38fe84abcf
commit
28a9000833
1 changed files with 84 additions and 4 deletions
|
|
@ -7,11 +7,12 @@ use smithay::{
|
||||||
delegate_compositor,
|
delegate_compositor,
|
||||||
desktop::{layer_map_for_output, LayerSurface, PopupKind, WindowSurfaceType},
|
desktop::{layer_map_for_output, LayerSurface, PopupKind, WindowSurfaceType},
|
||||||
reexports::wayland_server::{protocol::wl_surface::WlSurface, Client, Resource},
|
reexports::wayland_server::{protocol::wl_surface::WlSurface, Client, Resource},
|
||||||
utils::{Logical, Size, SERIAL_COUNTER},
|
utils::{Clock, Logical, Monotonic, Size, Time, SERIAL_COUNTER},
|
||||||
wayland::{
|
wayland::{
|
||||||
compositor::{
|
compositor::{
|
||||||
add_blocker, add_pre_commit_hook, with_states, BufferAssignment, CompositorClientState,
|
add_blocker, add_post_commit_hook, add_pre_commit_hook, with_states,
|
||||||
CompositorHandler, CompositorState, SurfaceAttributes,
|
with_surface_tree_downward, BufferAssignment, CompositorClientState, CompositorHandler,
|
||||||
|
CompositorState, SurfaceAttributes, SurfaceData, TraversalAction,
|
||||||
},
|
},
|
||||||
dmabuf::get_dmabuf,
|
dmabuf::get_dmabuf,
|
||||||
drm_syncobj::DrmSyncobjCachedState,
|
drm_syncobj::DrmSyncobjCachedState,
|
||||||
|
|
@ -25,7 +26,7 @@ use smithay::{
|
||||||
},
|
},
|
||||||
xwayland::XWaylandClientData,
|
xwayland::XWaylandClientData,
|
||||||
};
|
};
|
||||||
use std::sync::Mutex;
|
use std::{collections::VecDeque, sync::Mutex, time::Duration};
|
||||||
|
|
||||||
fn toplevel_ensure_initial_configure(
|
fn toplevel_ensure_initial_configure(
|
||||||
toplevel: &ToplevelSurface,
|
toplevel: &ToplevelSurface,
|
||||||
|
|
@ -92,6 +93,61 @@ pub fn client_compositor_state(client: &Client) -> &CompositorClientState {
|
||||||
panic!("Unknown client data type")
|
panic!("Unknown client data type")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct FrametimeData {
|
||||||
|
last_commit: Option<Time<Monotonic>>,
|
||||||
|
last_diffs: VecDeque<Duration>,
|
||||||
|
estimation: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for FrametimeData {
|
||||||
|
fn default() -> Self {
|
||||||
|
FrametimeData {
|
||||||
|
last_commit: None,
|
||||||
|
last_diffs: VecDeque::with_capacity(100),
|
||||||
|
estimation: Duration::MAX,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn frame_time_estimation(clock: &Clock<Monotonic>, states: &SurfaceData) -> Option<Duration> {
|
||||||
|
let data = states
|
||||||
|
.data_map
|
||||||
|
.get::<Mutex<FrametimeData>>()?
|
||||||
|
.lock()
|
||||||
|
.unwrap();
|
||||||
|
if let Some(ref last) = data.last_commit {
|
||||||
|
// if the time since the last commit is already higher than our estimation,
|
||||||
|
// there is no reason to not use that as a better "guess"
|
||||||
|
let diff = Time::elapsed(&last, clock.now());
|
||||||
|
Some(diff.max(data.estimation))
|
||||||
|
} else {
|
||||||
|
Some(data.estimation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn recursive_frame_time_estimation(
|
||||||
|
clock: &Clock<Monotonic>,
|
||||||
|
surface: &WlSurface,
|
||||||
|
) -> Option<Duration> {
|
||||||
|
let mut overall_estimate = None;
|
||||||
|
with_surface_tree_downward(
|
||||||
|
surface,
|
||||||
|
(),
|
||||||
|
|_, _, _| TraversalAction::DoChildren(()),
|
||||||
|
|_, data, _| {
|
||||||
|
let surface_estimate = frame_time_estimation(clock, data);
|
||||||
|
overall_estimate = match (overall_estimate, surface_estimate) {
|
||||||
|
(x, None) => x,
|
||||||
|
(None, Some(estimate)) => Some(estimate),
|
||||||
|
(Some(a), Some(b)) => Some(a.min(b)),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|_, _, _| true,
|
||||||
|
);
|
||||||
|
overall_estimate
|
||||||
|
}
|
||||||
|
|
||||||
impl CompositorHandler for State {
|
impl CompositorHandler for State {
|
||||||
fn compositor_state(&mut self) -> &mut CompositorState {
|
fn compositor_state(&mut self) -> &mut CompositorState {
|
||||||
&mut self.common.compositor_state
|
&mut self.common.compositor_state
|
||||||
|
|
@ -161,6 +217,30 @@ impl CompositorHandler for State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
add_post_commit_hook::<Self, _>(surface, |state, _dh, surface| {
|
||||||
|
let now = state.common.clock.now();
|
||||||
|
with_states(surface, |states| {
|
||||||
|
let mut data = states
|
||||||
|
.data_map
|
||||||
|
.get_or_insert_threadsafe::<Mutex<FrametimeData>, _>(Default::default)
|
||||||
|
.lock()
|
||||||
|
.unwrap();
|
||||||
|
if let Some(ref last) = data.last_commit {
|
||||||
|
let diff = Time::elapsed(last, now);
|
||||||
|
data.last_diffs.push_back(diff);
|
||||||
|
if data.last_diffs.len() > 100 {
|
||||||
|
data.last_diffs.pop_front();
|
||||||
|
}
|
||||||
|
data.estimation = data
|
||||||
|
.last_diffs
|
||||||
|
.iter()
|
||||||
|
.fold(Duration::ZERO, |acc, new| acc.saturating_add(*new))
|
||||||
|
/ (data.last_diffs.len() as u32);
|
||||||
|
}
|
||||||
|
data.last_commit = Some(now);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn commit(&mut self, surface: &WlSurface) {
|
fn commit(&mut self, surface: &WlSurface) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue