kms/timings: Base next_render_time on time to submit
This commit is contained in:
parent
d1e0e28d3c
commit
db25cc4b0b
2 changed files with 54 additions and 6 deletions
|
|
@ -523,7 +523,7 @@ fn surface_thread(
|
||||||
vrr_mode: AdaptiveSync::Disabled,
|
vrr_mode: AdaptiveSync::Disabled,
|
||||||
|
|
||||||
state: QueueState::Idle,
|
state: QueueState::Idle,
|
||||||
timings: Timings::new(None, None, false),
|
timings: Timings::new(None, None, false, target_node),
|
||||||
frame_callback_seq: 0,
|
frame_callback_seq: 0,
|
||||||
thread_sender,
|
thread_sender,
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,19 @@
|
||||||
use std::{collections::VecDeque, num::NonZeroU64, time::Duration};
|
use std::{collections::VecDeque, num::NonZeroU64, time::Duration};
|
||||||
|
|
||||||
use smithay::utils::{Clock, Monotonic, Time};
|
use smithay::{
|
||||||
|
backend::drm::DrmNode,
|
||||||
|
utils::{Clock, Monotonic, Time},
|
||||||
|
};
|
||||||
use tracing::{debug, error};
|
use tracing::{debug, error};
|
||||||
|
|
||||||
const FRAME_TIME_BUFFER: Duration = Duration::from_millis(1);
|
const BASE_SAFETY_MARGIN: Duration = Duration::from_millis(3);
|
||||||
const FRAME_TIME_WINDOW: usize = 3;
|
const SAMPLE_TIME_WINDOW: usize = 5;
|
||||||
|
|
||||||
pub struct Timings {
|
pub struct Timings {
|
||||||
refresh_interval_ns: Option<NonZeroU64>,
|
refresh_interval_ns: Option<NonZeroU64>,
|
||||||
min_refresh_interval_ns: Option<NonZeroU64>,
|
min_refresh_interval_ns: Option<NonZeroU64>,
|
||||||
vrr: bool,
|
vrr: bool,
|
||||||
|
vendor: Option<u32>,
|
||||||
|
|
||||||
pub pending_frame: Option<PendingFrame>,
|
pub pending_frame: Option<PendingFrame>,
|
||||||
pub previous_frames: VecDeque<Frame>,
|
pub previous_frames: VecDeque<Frame>,
|
||||||
|
|
@ -37,6 +41,10 @@ impl Frame {
|
||||||
self.render_duration_elements + self.render_duration_draw
|
self.render_duration_elements + self.render_duration_draw
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn submit_time(&self) -> Duration {
|
||||||
|
Time::elapsed(&self.render_start, self.presentation_submitted)
|
||||||
|
}
|
||||||
|
|
||||||
fn frame_time(&self) -> Duration {
|
fn frame_time(&self) -> Duration {
|
||||||
Time::elapsed(&self.render_start, self.presentation_presented)
|
Time::elapsed(&self.render_start, self.presentation_presented)
|
||||||
}
|
}
|
||||||
|
|
@ -49,6 +57,7 @@ impl Timings {
|
||||||
refresh_interval: Option<Duration>,
|
refresh_interval: Option<Duration>,
|
||||||
min_interval: Option<Duration>,
|
min_interval: Option<Duration>,
|
||||||
vrr: bool,
|
vrr: bool,
|
||||||
|
node: DrmNode,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let refresh_interval_ns = if let Some(interval) = &refresh_interval {
|
let refresh_interval_ns = if let Some(interval) = &refresh_interval {
|
||||||
assert_eq!(interval.as_secs(), 0);
|
assert_eq!(interval.as_secs(), 0);
|
||||||
|
|
@ -64,10 +73,20 @@ impl Timings {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let vendor = if let Ok(vendor) = std::fs::read_to_string(format!(
|
||||||
|
"/sys/class/drm/renderD{}/device/vendor",
|
||||||
|
node.minor()
|
||||||
|
)) {
|
||||||
|
u32::from_str_radix(&vendor.trim()[2..], 16).ok()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
refresh_interval_ns,
|
refresh_interval_ns,
|
||||||
min_refresh_interval_ns,
|
min_refresh_interval_ns,
|
||||||
vrr,
|
vrr,
|
||||||
|
vendor,
|
||||||
|
|
||||||
pending_frame: None,
|
pending_frame: None,
|
||||||
previous_frames: VecDeque::new(),
|
previous_frames: VecDeque::new(),
|
||||||
|
|
@ -209,6 +228,22 @@ impl Timings {
|
||||||
/ (self.previous_frames.len() as u32)
|
/ (self.previous_frames.len() as u32)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn avg_submittime(&self, window: usize) -> Option<Duration> {
|
||||||
|
if self.previous_frames.len() < window || window == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(
|
||||||
|
self.previous_frames
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.take(window)
|
||||||
|
.map(|f| f.submit_time())
|
||||||
|
.try_fold(Duration::ZERO, |acc, x| acc.checked_add(x))?
|
||||||
|
/ (window.min(self.previous_frames.len()) as u32),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn avg_frametime(&self, window: usize) -> Option<Duration> {
|
pub fn avg_frametime(&self, window: usize) -> Option<Duration> {
|
||||||
if self.previous_frames.len() < window || window == 0 {
|
if self.previous_frames.len() < window || window == 0 {
|
||||||
return None;
|
return None;
|
||||||
|
|
@ -308,15 +343,28 @@ impl Timings {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn next_render_time(&self, clock: &Clock<Monotonic>) -> Duration {
|
pub fn next_render_time(&self, clock: &Clock<Monotonic>) -> Duration {
|
||||||
|
let Some(refresh_interval) = self.refresh_interval_ns else {
|
||||||
|
return Duration::ZERO; // we don't know what to expect, so render immediately.
|
||||||
|
};
|
||||||
|
|
||||||
|
const MIN_MARGIN: Duration = Duration::from_millis(3);
|
||||||
|
let baseline = MIN_MARGIN.max(Duration::from_nanos(refresh_interval.get() / 2));
|
||||||
|
|
||||||
let estimated_presentation_time = self.next_presentation_time(clock);
|
let estimated_presentation_time = self.next_presentation_time(clock);
|
||||||
if estimated_presentation_time.is_zero() {
|
if estimated_presentation_time.is_zero() {
|
||||||
return Duration::ZERO;
|
return Duration::ZERO;
|
||||||
}
|
}
|
||||||
|
|
||||||
let Some(avg_frametime) = self.avg_frametime(FRAME_TIME_WINDOW) else {
|
// HACK: Nvidia returns `page_flip`/`commit` early, so we have no information to optimize latency on submission.
|
||||||
|
if self.vendor == Some(0x10de) {
|
||||||
return Duration::ZERO;
|
return Duration::ZERO;
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(avg_submittime) = self.avg_submittime(SAMPLE_TIME_WINDOW) else {
|
||||||
|
return estimated_presentation_time.saturating_sub(baseline + BASE_SAFETY_MARGIN);
|
||||||
};
|
};
|
||||||
|
|
||||||
estimated_presentation_time.saturating_sub(avg_frametime + FRAME_TIME_BUFFER)
|
let margin = avg_submittime + BASE_SAFETY_MARGIN;
|
||||||
|
estimated_presentation_time.saturating_sub(margin)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue