metal: commit 1.5ms before the next page flip
This commit is contained in:
parent
a37ce1acda
commit
87d60d267e
4 changed files with 105 additions and 9 deletions
|
|
@ -12,6 +12,7 @@ use {
|
|||
SyncFile,
|
||||
},
|
||||
theme::Color,
|
||||
time::Time,
|
||||
tree::OutputNode,
|
||||
utils::{errorfmt::ErrorFmt, oserror::OsError, transform_ext::TransformExt},
|
||||
video::{
|
||||
|
|
@ -22,7 +23,11 @@ use {
|
|||
},
|
||||
},
|
||||
},
|
||||
std::rc::{Rc, Weak},
|
||||
std::{
|
||||
env,
|
||||
rc::{Rc, Weak},
|
||||
sync::LazyLock,
|
||||
},
|
||||
uapi::c,
|
||||
};
|
||||
|
||||
|
|
@ -79,19 +84,63 @@ enum CursorProgramming {
|
|||
},
|
||||
}
|
||||
|
||||
pub const DEFAULT_PRE_COMMIT_MARGIN: u64 = 16_000_000; // 16ms
|
||||
pub const MIN_POST_COMMIT_MARGIN: u64 = 1_500_000; // 1.5ms
|
||||
pub const MAX_POST_COMMIT_MARGIN: u64 = 16_000_000; // 16ms
|
||||
pub const DEFAULT_POST_COMMIT_MARGIN: u64 = MIN_POST_COMMIT_MARGIN;
|
||||
pub const POST_COMMIT_MARGIN_DELTA: u64 = 500_000; // 500us
|
||||
|
||||
static NO_FRAME_SCHEDULING: LazyLock<bool> = LazyLock::new(|| {
|
||||
let res = env::var("JAY_NO_FRAME_SCHEDULING").ok().as_deref() == Some("1");
|
||||
if res {
|
||||
log::warn!("Frame scheduling is disabled.");
|
||||
}
|
||||
res
|
||||
});
|
||||
|
||||
impl MetalConnector {
|
||||
pub fn schedule_present(&self) {
|
||||
self.present_trigger.trigger();
|
||||
}
|
||||
|
||||
pub async fn present_loop(self: Rc<Self>) {
|
||||
let mut cur_sec = 0;
|
||||
let mut max = 0;
|
||||
loop {
|
||||
self.present_trigger.triggered().await;
|
||||
if !self.can_present.get() {
|
||||
continue;
|
||||
}
|
||||
let mut expected_sequence = self.sequence.get() + 1;
|
||||
let mut start = Time::now_unchecked();
|
||||
let use_frame_scheduling = !self.try_async_flip() && !*NO_FRAME_SCHEDULING;
|
||||
if use_frame_scheduling {
|
||||
let margin = self.pre_commit_margin.get() + self.post_commit_margin.get();
|
||||
let next_present = self.next_flip_nsec.get().saturating_sub(margin);
|
||||
if start.nsec() < next_present {
|
||||
self.state.ring.timeout(next_present).await.unwrap();
|
||||
start = Time::now_unchecked();
|
||||
} else {
|
||||
expected_sequence += 1;
|
||||
}
|
||||
}
|
||||
if let Err(e) = self.present_once().await {
|
||||
log::error!("Could not present: {}", ErrorFmt(e));
|
||||
continue;
|
||||
}
|
||||
if use_frame_scheduling {
|
||||
self.expected_sequence.set(Some(expected_sequence));
|
||||
}
|
||||
self.state.set_backend_idle(false);
|
||||
let duration = start.elapsed();
|
||||
max = max.max(duration.as_nanos() as _);
|
||||
if start.0.tv_sec != cur_sec {
|
||||
cur_sec = start.0.tv_sec;
|
||||
self.pre_commit_margin_decay.add(max);
|
||||
self.pre_commit_margin
|
||||
.set(self.pre_commit_margin_decay.get());
|
||||
max = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -244,6 +293,10 @@ impl MetalConnector {
|
|||
}
|
||||
}
|
||||
|
||||
fn try_async_flip(&self) -> bool {
|
||||
self.tearing_requested.get() && self.dev.supports_async_commit
|
||||
}
|
||||
|
||||
fn program_connector(
|
||||
&self,
|
||||
version: u64,
|
||||
|
|
@ -253,7 +306,7 @@ impl MetalConnector {
|
|||
new_fb: Option<&PresentFb>,
|
||||
) -> Result<(), MetalError> {
|
||||
let mut changes = self.master.change();
|
||||
let mut try_async_flip = self.tearing_requested.get() && self.dev.supports_async_commit;
|
||||
let mut try_async_flip = self.try_async_flip();
|
||||
macro_rules! change {
|
||||
($c:expr, $prop:expr, $new:expr) => {{
|
||||
if $prop.value.get() != $new {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,11 @@ use {
|
|||
HardwareCursorUpdate, Mode, MonitorInfo,
|
||||
},
|
||||
backends::metal::{
|
||||
present::{DirectScanoutCache, PresentFb},
|
||||
present::{
|
||||
DirectScanoutCache, PresentFb, DEFAULT_POST_COMMIT_MARGIN,
|
||||
DEFAULT_PRE_COMMIT_MARGIN, MAX_POST_COMMIT_MARGIN, MIN_POST_COMMIT_MARGIN,
|
||||
POST_COMMIT_MARGIN_DELTA,
|
||||
},
|
||||
MetalBackend, MetalError,
|
||||
},
|
||||
drm_feedback::DrmFeedback,
|
||||
|
|
@ -27,8 +31,8 @@ use {
|
|||
udev::UdevDevice,
|
||||
utils::{
|
||||
asyncevent::AsyncEvent, bitflags::BitflagsExt, cell_ext::CellExt, clonecell::CloneCell,
|
||||
copyhashmap::CopyHashMap, errorfmt::ErrorFmt, numcell::NumCell, on_change::OnChange,
|
||||
opaque_cell::OpaqueCell, oserror::OsError,
|
||||
copyhashmap::CopyHashMap, errorfmt::ErrorFmt, geometric_decay::GeometricDecay,
|
||||
numcell::NumCell, on_change::OnChange, opaque_cell::OpaqueCell, oserror::OsError,
|
||||
},
|
||||
video::{
|
||||
dmabuf::DmaBufId,
|
||||
|
|
@ -464,6 +468,13 @@ pub struct MetalConnector {
|
|||
|
||||
pub version: NumCell<u64>,
|
||||
pub sequence: Cell<u64>,
|
||||
pub expected_sequence: Cell<Option<u64>>,
|
||||
pub pre_commit_margin: Cell<u64>,
|
||||
pub pre_commit_margin_decay: GeometricDecay,
|
||||
pub post_commit_margin: Cell<u64>,
|
||||
pub post_commit_margin_decay: GeometricDecay,
|
||||
pub vblank_miss_sec: Cell<u32>,
|
||||
pub vblank_miss_this_sec: NumCell<u32>,
|
||||
}
|
||||
|
||||
impl Debug for MetalConnector {
|
||||
|
|
@ -1055,6 +1066,13 @@ fn create_connector(
|
|||
try_switch_format: Cell::new(false),
|
||||
version: Default::default(),
|
||||
sequence: Default::default(),
|
||||
expected_sequence: Default::default(),
|
||||
pre_commit_margin_decay: GeometricDecay::new(0.5, DEFAULT_PRE_COMMIT_MARGIN),
|
||||
pre_commit_margin: Cell::new(DEFAULT_PRE_COMMIT_MARGIN),
|
||||
post_commit_margin_decay: GeometricDecay::new(0.1, DEFAULT_POST_COMMIT_MARGIN),
|
||||
post_commit_margin: Cell::new(DEFAULT_POST_COMMIT_MARGIN),
|
||||
vblank_miss_sec: Cell::new(0),
|
||||
vblank_miss_this_sec: Default::default(),
|
||||
});
|
||||
let futures = ConnectorFutures {
|
||||
_present: backend
|
||||
|
|
@ -1924,6 +1942,30 @@ impl MetalBackend {
|
|||
if let Some(fb) = connector.next_framebuffer.take() {
|
||||
*connector.active_framebuffer.borrow_mut() = Some(fb);
|
||||
}
|
||||
if let Some(expected) = connector.expected_sequence.take() {
|
||||
if connector.vblank_miss_sec.replace(tv_sec) != tv_sec {
|
||||
let n_missed = connector.vblank_miss_this_sec.replace(0);
|
||||
if n_missed > 0 {
|
||||
log::debug!("{}: Missed {n_missed} page flips", connector.kernel_id());
|
||||
let new_margin = (connector.post_commit_margin.get()
|
||||
+ POST_COMMIT_MARGIN_DELTA)
|
||||
.min(MAX_POST_COMMIT_MARGIN);
|
||||
connector.post_commit_margin_decay.reset(new_margin);
|
||||
connector.post_commit_margin.set(new_margin);
|
||||
} else {
|
||||
connector
|
||||
.post_commit_margin_decay
|
||||
.add(MIN_POST_COMMIT_MARGIN);
|
||||
connector
|
||||
.post_commit_margin
|
||||
.set(connector.post_commit_margin_decay.get());
|
||||
}
|
||||
}
|
||||
let actual = connector.sequence.get();
|
||||
if expected < actual {
|
||||
connector.vblank_miss_this_sec.fetch_add(1);
|
||||
}
|
||||
}
|
||||
if connector.has_damage.is_not_zero()
|
||||
|| connector.cursor_damage.get()
|
||||
|| connector.cursor_changed.get()
|
||||
|
|
|
|||
|
|
@ -58,6 +58,11 @@ impl Time {
|
|||
let nsec = self.0.tv_nsec as u64 / 1_000_000;
|
||||
sec + nsec
|
||||
}
|
||||
|
||||
pub fn elapsed(self) -> Duration {
|
||||
let now = Self::now_unchecked();
|
||||
now - self
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Time {}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ pub struct GeometricDecay {
|
|||
}
|
||||
|
||||
impl GeometricDecay {
|
||||
#[expect(dead_code)]
|
||||
pub fn new(mut p1: f64, reset: u64) -> Self {
|
||||
if p1.is_nan() || p1 < 0.01 {
|
||||
p1 = 0.01;
|
||||
|
|
@ -23,17 +22,14 @@ impl GeometricDecay {
|
|||
}
|
||||
}
|
||||
|
||||
#[expect(dead_code)]
|
||||
pub fn reset(&self, v: u64) {
|
||||
self.v.set(v as f64 / self.p1);
|
||||
}
|
||||
|
||||
#[expect(dead_code)]
|
||||
pub fn get(&self) -> u64 {
|
||||
(self.p1 * self.v.get()) as u64
|
||||
}
|
||||
|
||||
#[expect(dead_code)]
|
||||
pub fn add(&self, n: u64) {
|
||||
let v = n as f64 + self.p2 * self.v.get();
|
||||
self.v.set(v);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue