Skip to content

Commit

Permalink
metal: commit 1.5ms before the next page flip
Browse files Browse the repository at this point in the history
  • Loading branch information
mahkoh committed Sep 10, 2024
1 parent cd9b6a1 commit 56d00ca
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 8 deletions.
41 changes: 40 additions & 1 deletion src/backends/metal/present.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use {
SyncFile,
},
theme::Color,
time::Time,
tree::OutputNode,
utils::{errorfmt::ErrorFmt, oserror::OsError, transform_ext::TransformExt},
video::{
Expand Down Expand Up @@ -79,19 +80,53 @@ enum CursorProgramming {
},
}

pub const DEFAULT_PRESENT_DURATION: u64 = 16_000_000; // 16ms

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;
const SAFETY_MARGIN: u64 = 1_500_000; // 1.5ms
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 sync_flip = !self.try_async_flip();
if sync_flip {
let next_present = self
.next_flip_nsec
.get()
.saturating_sub(self.present_margin.get() + SAFETY_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 sync_flip {
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.present_decay.add(max);
self.present_margin.set(self.present_decay.get());
max = 0;
}
}
}

Expand Down Expand Up @@ -244,6 +279,10 @@ impl MetalConnector {
}
}

fn try_async_flip(&self) -> bool {
self.tearing_requested.get() && self.dev.supports_async_commit
}

fn program_connector(
&self,
version: u64,
Expand All @@ -253,7 +292,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 {
Expand Down
23 changes: 20 additions & 3 deletions src/backends/metal/video.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use {
HardwareCursorUpdate, Mode, MonitorInfo,
},
backends::metal::{
present::{DirectScanoutCache, PresentFb},
present::{DirectScanoutCache, PresentFb, DEFAULT_PRESENT_DURATION},
MetalBackend, MetalError,
},
drm_feedback::DrmFeedback,
Expand All @@ -27,8 +27,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,
Expand Down Expand Up @@ -464,6 +464,9 @@ pub struct MetalConnector {

pub version: NumCell<u64>,
pub sequence: Cell<u64>,
pub expected_sequence: Cell<Option<u64>>,
pub present_decay: GeometricDecay,
pub present_margin: Cell<u64>,
}

impl Debug for MetalConnector {
Expand Down Expand Up @@ -1052,6 +1055,9 @@ fn create_connector(
try_switch_format: Cell::new(false),
version: Default::default(),
sequence: Default::default(),
expected_sequence: Default::default(),
present_decay: GeometricDecay::new(0.5, DEFAULT_PRESENT_DURATION),
present_margin: Cell::new(DEFAULT_PRESENT_DURATION),
});
let futures = ConnectorFutures {
_present: backend
Expand Down Expand Up @@ -1916,6 +1922,17 @@ 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() {
let actual = connector.sequence.get();
if expected < actual {
log::debug!(
"{}: Missed page flip: expected {expected}, actual {actual}",
connector.kernel_id(),
);
connector.present_decay.reset(DEFAULT_PRESENT_DURATION);
connector.present_margin.set(DEFAULT_PRESENT_DURATION);
}
}
if connector.has_damage.is_not_zero()
|| connector.cursor_damage.get()
|| connector.cursor_changed.get()
Expand Down
5 changes: 5 additions & 0 deletions src/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {}
Expand Down
4 changes: 0 additions & 4 deletions src/utils/geometric_decay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand Down

0 comments on commit 56d00ca

Please sign in to comment.