Skip to content

Commit db25cc4

Browse files
committed
kms/timings: Base next_render_time on time to submit
1 parent d1e0e28 commit db25cc4

File tree

2 files changed

+54
-6
lines changed

2 files changed

+54
-6
lines changed

src/backend/kms/surface/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -523,7 +523,7 @@ fn surface_thread(
523523
vrr_mode: AdaptiveSync::Disabled,
524524

525525
state: QueueState::Idle,
526-
timings: Timings::new(None, None, false),
526+
timings: Timings::new(None, None, false, target_node),
527527
frame_callback_seq: 0,
528528
thread_sender,
529529

src/backend/kms/surface/timings.rs

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
use std::{collections::VecDeque, num::NonZeroU64, time::Duration};
22

3-
use smithay::utils::{Clock, Monotonic, Time};
3+
use smithay::{
4+
backend::drm::DrmNode,
5+
utils::{Clock, Monotonic, Time},
6+
};
47
use tracing::{debug, error};
58

6-
const FRAME_TIME_BUFFER: Duration = Duration::from_millis(1);
7-
const FRAME_TIME_WINDOW: usize = 3;
9+
const BASE_SAFETY_MARGIN: Duration = Duration::from_millis(3);
10+
const SAMPLE_TIME_WINDOW: usize = 5;
811

912
pub struct Timings {
1013
refresh_interval_ns: Option<NonZeroU64>,
1114
min_refresh_interval_ns: Option<NonZeroU64>,
1215
vrr: bool,
16+
vendor: Option<u32>,
1317

1418
pub pending_frame: Option<PendingFrame>,
1519
pub previous_frames: VecDeque<Frame>,
@@ -37,6 +41,10 @@ impl Frame {
3741
self.render_duration_elements + self.render_duration_draw
3842
}
3943

44+
fn submit_time(&self) -> Duration {
45+
Time::elapsed(&self.render_start, self.presentation_submitted)
46+
}
47+
4048
fn frame_time(&self) -> Duration {
4149
Time::elapsed(&self.render_start, self.presentation_presented)
4250
}
@@ -49,6 +57,7 @@ impl Timings {
4957
refresh_interval: Option<Duration>,
5058
min_interval: Option<Duration>,
5159
vrr: bool,
60+
node: DrmNode,
5261
) -> Self {
5362
let refresh_interval_ns = if let Some(interval) = &refresh_interval {
5463
assert_eq!(interval.as_secs(), 0);
@@ -64,10 +73,20 @@ impl Timings {
6473
None
6574
};
6675

76+
let vendor = if let Ok(vendor) = std::fs::read_to_string(format!(
77+
"/sys/class/drm/renderD{}/device/vendor",
78+
node.minor()
79+
)) {
80+
u32::from_str_radix(&vendor.trim()[2..], 16).ok()
81+
} else {
82+
None
83+
};
84+
6785
Self {
6886
refresh_interval_ns,
6987
min_refresh_interval_ns,
7088
vrr,
89+
vendor,
7190

7291
pending_frame: None,
7392
previous_frames: VecDeque::new(),
@@ -209,6 +228,22 @@ impl Timings {
209228
/ (self.previous_frames.len() as u32)
210229
}
211230

231+
pub fn avg_submittime(&self, window: usize) -> Option<Duration> {
232+
if self.previous_frames.len() < window || window == 0 {
233+
return None;
234+
}
235+
236+
Some(
237+
self.previous_frames
238+
.iter()
239+
.rev()
240+
.take(window)
241+
.map(|f| f.submit_time())
242+
.try_fold(Duration::ZERO, |acc, x| acc.checked_add(x))?
243+
/ (window.min(self.previous_frames.len()) as u32),
244+
)
245+
}
246+
212247
pub fn avg_frametime(&self, window: usize) -> Option<Duration> {
213248
if self.previous_frames.len() < window || window == 0 {
214249
return None;
@@ -308,15 +343,28 @@ impl Timings {
308343
}
309344

310345
pub fn next_render_time(&self, clock: &Clock<Monotonic>) -> Duration {
346+
let Some(refresh_interval) = self.refresh_interval_ns else {
347+
return Duration::ZERO; // we don't know what to expect, so render immediately.
348+
};
349+
350+
const MIN_MARGIN: Duration = Duration::from_millis(3);
351+
let baseline = MIN_MARGIN.max(Duration::from_nanos(refresh_interval.get() / 2));
352+
311353
let estimated_presentation_time = self.next_presentation_time(clock);
312354
if estimated_presentation_time.is_zero() {
313355
return Duration::ZERO;
314356
}
315357

316-
let Some(avg_frametime) = self.avg_frametime(FRAME_TIME_WINDOW) else {
358+
// HACK: Nvidia returns `page_flip`/`commit` early, so we have no information to optimize latency on submission.
359+
if self.vendor == Some(0x10de) {
317360
return Duration::ZERO;
361+
}
362+
363+
let Some(avg_submittime) = self.avg_submittime(SAMPLE_TIME_WINDOW) else {
364+
return estimated_presentation_time.saturating_sub(baseline + BASE_SAFETY_MARGIN);
318365
};
319366

320-
estimated_presentation_time.saturating_sub(avg_frametime + FRAME_TIME_BUFFER)
367+
let margin = avg_submittime + BASE_SAFETY_MARGIN;
368+
estimated_presentation_time.saturating_sub(margin)
321369
}
322370
}

0 commit comments

Comments
 (0)