diff --git a/jay-config/src/_private/client.rs b/jay-config/src/_private/client.rs index 5170ab3a..4e35aa34 100644 --- a/jay-config/src/_private/client.rs +++ b/jay-config/src/_private/client.rs @@ -744,6 +744,10 @@ impl Client { self.send(&ClientMessage::SetDirectScanoutEnabled { device, enabled }); } + pub fn set_flip_margin(&self, device: DrmDevice, margin: Duration) { + self.send(&ClientMessage::SetFlipMargin { device, margin }); + } + pub fn connector_connected(&self, connector: Connector) -> bool { let res = self.send_with_response(&ClientMessage::ConnectorConnected { connector }); get_response!(res, false, ConnectorConnected { connected }); diff --git a/jay-config/src/_private/ipc.rs b/jay-config/src/_private/ipc.rs index 2e847584..d5a4b173 100644 --- a/jay-config/src/_private/ipc.rs +++ b/jay-config/src/_private/ipc.rs @@ -513,6 +513,10 @@ pub enum ClientMessage<'a> { connector: Connector, format: Format, }, + SetFlipMargin { + device: DrmDevice, + margin: Duration, + }, } #[derive(Serialize, Deserialize, Debug)] diff --git a/jay-config/src/video.rs b/jay-config/src/video.rs index 69ae7bf1..db70af0e 100644 --- a/jay-config/src/video.rs +++ b/jay-config/src/video.rs @@ -12,7 +12,7 @@ use { _private::WireMode, }, serde::{Deserialize, Serialize}, - std::str::FromStr, + std::{str::FromStr, time::Duration}, }; /// The mode of a connector. @@ -504,6 +504,16 @@ impl DrmDevice { pub fn set_direct_scanout_enabled(self, enabled: bool) { get!().set_direct_scanout_enabled(Some(self), enabled); } + + /// Sets the flip margin of this device. + /// + /// This is duration between the compositor initiating a page flip and the output's + /// vblank event. This determines the minimum input latency. The default is 1.5 ms. + /// + /// Note that if the margin is too small, the compositor will dynamically increase it. + pub fn set_flip_margin(self, margin: Duration) { + get!().set_flip_margin(self, margin); + } } /// A graphics API. diff --git a/release-notes.md b/release-notes.md index 667d6dd2..7bd38d07 100644 --- a/release-notes.md +++ b/release-notes.md @@ -7,6 +7,7 @@ - Upload shm textures on a separate thread in the Vulkan renderer. - Disable implicit sync in KMS. - Implement frame scheduling for KMS. +- The JAY_MAX_RENDER_TIME_NSEC environment variable has been removed. # 1.5.0 (2024-09-02) diff --git a/src/backend.rs b/src/backend.rs index 772db6ff..a7f66df8 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -456,6 +456,9 @@ pub trait BackendDrmDevice { let _ = lessee; let _ = connector_ids; } + fn set_flip_margin(&self, margin: u64) { + let _ = margin; + } } pub trait BackendDrmLease { diff --git a/src/backends/metal/present.rs b/src/backends/metal/present.rs index 4c1e93f7..8a5a66cc 100644 --- a/src/backends/metal/present.rs +++ b/src/backends/metal/present.rs @@ -23,11 +23,7 @@ use { }, }, }, - std::{ - env, - rc::{Rc, Weak}, - sync::LazyLock, - }, + std::rc::{Rc, Weak}, uapi::c, }; @@ -85,19 +81,9 @@ 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 DEFAULT_POST_COMMIT_MARGIN: u64 = 1_500_000; // 1.5ms; pub const POST_COMMIT_MARGIN_DELTA: u64 = 500_000; // 500us -static NO_FRAME_SCHEDULING: LazyLock = 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(); @@ -113,10 +99,13 @@ impl MetalConnector { } 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; + let use_frame_scheduling = !self.try_async_flip(); 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); + let next_present = self + .next_flip_nsec + .get() + .saturating_sub(self.pre_commit_margin.get()) + .saturating_sub(self.post_commit_margin.get()); if start.nsec() < next_present { self.state.ring.timeout(next_present).await.unwrap(); start = Time::now_unchecked(); diff --git a/src/backends/metal/video.rs b/src/backends/metal/video.rs index 6a47b8a4..672f6cd9 100644 --- a/src/backends/metal/video.rs +++ b/src/backends/metal/video.rs @@ -10,8 +10,7 @@ use { backends::metal::{ present::{ DirectScanoutCache, PresentFb, DEFAULT_POST_COMMIT_MARGIN, - DEFAULT_PRE_COMMIT_MARGIN, MAX_POST_COMMIT_MARGIN, MIN_POST_COMMIT_MARGIN, - POST_COMMIT_MARGIN_DELTA, + DEFAULT_PRE_COMMIT_MARGIN, POST_COMMIT_MARGIN_DELTA, }, MetalBackend, MetalError, }, @@ -27,6 +26,7 @@ use { wp_presentation_feedback::{KIND_HW_COMPLETION, KIND_VSYNC, KIND_ZERO_COPY}, }, state::State, + tree::OutputNode, udev::UdevDevice, utils::{ asyncevent::AsyncEvent, bitflags::BitflagsExt, cell_ext::CellExt, clonecell::CloneCell, @@ -108,6 +108,7 @@ pub struct MetalDrmDevice { pub leases: CopyHashMap, pub leases_to_break: CopyHashMap, pub paused: Cell, + pub min_post_commit_margin: Cell, } impl Debug for MetalDrmDevice { @@ -279,6 +280,19 @@ impl BackendDrmDevice for MetalDrmDevice { }); lessee.created(lease); } + + fn set_flip_margin(&self, margin: u64) { + self.min_post_commit_margin.set(margin); + if let Some(dd) = self.backend.device_holder.drm_devices.get(&self.devnum) { + for c in dd.connectors.lock().values() { + c.post_commit_margin.set(margin); + c.post_commit_margin_decay.reset(margin); + if let Some(output) = self.backend.state.root.outputs.get(&c.connector_id) { + output.flip_margin_ns.set(Some(margin)); + } + } + } + } } pub struct HandleEvents { @@ -1067,8 +1081,8 @@ fn create_connector( 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), + post_commit_margin_decay: GeometricDecay::new(0.1, dev.min_post_commit_margin.get()), + post_commit_margin: Cell::new(dev.min_post_commit_margin.get()), vblank_miss_sec: Cell::new(0), vblank_miss_this_sec: Default::default(), presentation_is_sync: Cell::new(false), @@ -1750,6 +1764,7 @@ impl MetalBackend { leases: Default::default(), leases_to_break: Default::default(), paused: Cell::new(false), + min_post_commit_margin: Cell::new(DEFAULT_POST_COMMIT_MARGIN), }); let (connectors, futures) = get_connectors(self, &dev, &resources.connectors)?; @@ -1943,24 +1958,11 @@ impl MetalBackend { if let Some(fb) = connector.next_framebuffer.take() { *connector.active_framebuffer.borrow_mut() = Some(fb); } + let dd = connector.display.borrow(); + let global = self.state.root.outputs.get(&connector.connector_id); 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()); - } + self.update_post_commit_margin(dev, &connector, &dd, global.as_deref()); } let actual = connector.sequence.get(); if expected < actual { @@ -1973,12 +1975,10 @@ impl MetalBackend { { connector.schedule_present(); } - let dd = connector.display.borrow_mut(); connector .next_flip_nsec .set(tv_sec as u64 * 1_000_000_000 + tv_usec as u64 * 1000 + dd.refresh as u64); { - let global = self.state.root.outputs.get(&connector.connector_id); let mut flags = KIND_HW_COMPLETION; if connector.presentation_is_sync.get() { flags |= KIND_VSYNC; @@ -2002,6 +2002,38 @@ impl MetalBackend { } } + fn update_post_commit_margin( + &self, + dev: &MetalDrmDeviceData, + connector: &MetalConnector, + dd: &ConnectorDisplayData, + global: Option<&OutputNode>, + ) { + let n_missed = connector.vblank_miss_this_sec.replace(0); + let old_margin = connector.post_commit_margin.get(); + let new_margin = if n_missed > 0 { + log::debug!("{}: Missed {n_missed} page flips", connector.kernel_id()); + let refresh = dd.refresh as u64; + if old_margin >= refresh { + return; + } + let new_margin = (old_margin + POST_COMMIT_MARGIN_DELTA).min(refresh); + connector.post_commit_margin_decay.reset(new_margin); + new_margin + } else { + let min_margin = dev.dev.min_post_commit_margin.get(); + if min_margin >= connector.post_commit_margin.get() { + return; + } + connector.post_commit_margin_decay.add(min_margin); + connector.post_commit_margin_decay.get() + }; + connector.post_commit_margin.set(new_margin); + if let Some(global) = &global { + global.flip_margin_ns.set(Some(new_margin)); + } + } + fn reset_planes(&self, dev: &MetalDrmDeviceData, changes: &mut Change, preserve: &Preserve) { for plane in dev.dev.planes.values() { if preserve.planes.contains(&plane.id) { diff --git a/src/cli/randr.rs b/src/cli/randr.rs index caf5a9e2..34646df4 100644 --- a/src/cli/randr.rs +++ b/src/cli/randr.rs @@ -15,6 +15,7 @@ use { fmt::{Display, Formatter}, rc::Rc, str::FromStr, + time::Duration, }, }; @@ -66,6 +67,31 @@ pub enum CardCommand { Api(ApiArgs), /// Modify the direct scanout setting of the card. DirectScanout(DirectScanoutArgs), + /// Modify timing settings of the card. + Timing(TimingArgs), +} + +#[derive(Args, Debug, Clone)] +pub struct TimingArgs { + #[clap(subcommand)] + pub cmd: TimingCmd, +} + +#[derive(Subcommand, Debug, Clone)] +pub enum TimingCmd { + /// Sets the margin to use for page flips. + /// + /// This is duration between the compositor initiating a page flip and the output's + /// vblank event. This determines the minimum input latency. The default is 1.5 ms. + /// + /// Note that if the margin is too small, the compositor will dynamically increase it. + SetFlipMargin(SetFlipMarginArgs), +} + +#[derive(Args, Debug, Clone)] +pub struct SetFlipMarginArgs { + /// The margin in milliseconds. + pub margin_ms: f64, } #[derive(Args, Debug, Clone)] @@ -341,6 +367,7 @@ struct Output { pub tearing_mode: TearingMode, pub formats: Vec, pub format: Option, + pub flip_margin_ns: Option, } #[derive(Copy, Clone, Debug)] @@ -626,6 +653,18 @@ impl Randr { }, }); } + CardCommand::Timing(ts) => match ts.cmd { + TimingCmd::SetFlipMargin(sfm) => { + self.handle_error(randr, |msg| { + eprintln!("Could not modify the flip margin: {}", msg); + }); + tc.send(jay_randr::SetFlipMargin { + self_id: randr, + dev: &args.card, + margin_ns: (sfm.margin_ms * 1_000_000.0) as u64, + }); + } + }, } tc.round_trip().await; } @@ -759,6 +798,14 @@ impl Randr { }; println!(" transform: {}", name); } + if let Some(flip_margin_ns) = o.flip_margin_ns { + if flip_margin_ns != 1_500_000 { + println!( + " flip margin: {:?}", + Duration::from_nanos(flip_margin_ns) + ); + } + } if o.modes.is_not_empty() && modes { println!(" modes:"); for mode in &o.modes { @@ -838,6 +885,7 @@ impl Randr { tearing_mode: TearingMode::NEVER, formats: vec![], format: None, + flip_margin_ns: None, }); }); jay_randr::NonDesktopOutput::handle(tc, randr, data.clone(), |data, msg| { @@ -865,6 +913,7 @@ impl Randr { tearing_mode: TearingMode::NEVER, formats: vec![], format: None, + flip_margin_ns: None, }); }); jay_randr::VrrState::handle(tc, randr, data.clone(), |data, msg| { @@ -896,6 +945,12 @@ impl Randr { output.format = Some(msg.name.to_string()); } }); + jay_randr::FlipMargin::handle(tc, randr, data.clone(), |data, msg| { + let mut data = data.borrow_mut(); + let c = data.connectors.last_mut().unwrap(); + let output = c.output.as_mut().unwrap(); + output.flip_margin_ns = Some(msg.margin_ns); + }); jay_randr::Mode::handle(tc, randr, data.clone(), |data, msg| { let mut data = data.borrow_mut(); let c = data.connectors.last_mut().unwrap(); diff --git a/src/compositor.rs b/src/compositor.rs index 3389115d..43fea12d 100644 --- a/src/compositor.rs +++ b/src/compositor.rs @@ -507,6 +507,7 @@ fn create_dummy_output(state: &Rc) { vblank_event: Default::default(), latch_event: Default::default(), presentation_event: Default::default(), + flip_margin_ns: Default::default(), }); let dummy_workspace = Rc::new(WorkspaceNode { id: state.node_ids.next(), diff --git a/src/config/handler.rs b/src/config/handler.rs index 104bd4f3..987d0a15 100644 --- a/src/config/handler.rs +++ b/src/config/handler.rs @@ -752,6 +752,13 @@ impl ConfigProxyHandler { Ok(()) } + fn handle_set_flip_margin(&self, device: DrmDevice, margin: Duration) -> Result<(), CphError> { + self.get_drm_device(device)? + .dev + .set_flip_margin(margin.as_nanos().try_into().unwrap_or(u64::MAX)); + Ok(()) + } + fn handle_set_direct_scanout_enabled( &self, device: Option, @@ -1936,6 +1943,9 @@ impl ConfigProxyHandler { ClientMessage::ConnectorSetFormat { connector, format } => self .handle_connector_set_format(connector, format) .wrn("connector_set_format")?, + ClientMessage::SetFlipMargin { device, margin } => self + .handle_set_flip_margin(device, margin) + .wrn("set_flip_margin")?, } Ok(()) } diff --git a/src/ifs/jay_compositor.rs b/src/ifs/jay_compositor.rs index 401852ec..2d3e41d6 100644 --- a/src/ifs/jay_compositor.rs +++ b/src/ifs/jay_compositor.rs @@ -70,7 +70,7 @@ impl Global for JayCompositorGlobal { } fn version(&self) -> u32 { - 9 + 10 } fn required_caps(&self) -> ClientCaps { diff --git a/src/ifs/jay_randr.rs b/src/ifs/jay_randr.rs index 4a806d61..042c28f5 100644 --- a/src/ifs/jay_randr.rs +++ b/src/ifs/jay_randr.rs @@ -29,6 +29,7 @@ pub struct JayRandr { const VRR_CAPABLE_SINCE: Version = Version(2); const TEARING_SINCE: Version = Version(3); const FORMAT_SINCE: Version = Version(8); +const FLIP_MARGIN_SINCE: Version = Version(10); impl JayRandr { pub fn new(id: JayRandrId, client: &Rc, version: Version) -> Self { @@ -144,6 +145,14 @@ impl JayRandr { } } } + if self.version >= FLIP_MARGIN_SINCE { + if let Some(margin_ns) = node.flip_margin_ns.get() { + self.client.event(FlipMargin { + self_id: self.id, + margin_ns, + }); + } + } let current_mode = global.mode.get(); for mode in &global.modes { self.client.event(Mode { @@ -395,6 +404,14 @@ impl JayRandrRequestHandler for JayRandr { c.global.connector.connector.set_fb_format(format); Ok(()) } + + fn set_flip_margin(&self, req: SetFlipMargin<'_>, _slf: &Rc) -> Result<(), Self::Error> { + let Some(dev) = self.get_device(req.dev) else { + return Ok(()); + }; + dev.dev.set_flip_margin(req.margin_ns); + Ok(()) + } } object_base! { diff --git a/src/tasks/connector.rs b/src/tasks/connector.rs index 52d47a14..d305974d 100644 --- a/src/tasks/connector.rs +++ b/src/tasks/connector.rs @@ -179,6 +179,7 @@ impl ConnectorHandler { latch_event: Default::default(), vblank_event: Default::default(), presentation_event: Default::default(), + flip_margin_ns: Default::default(), }); on.update_visible(); on.update_rects(); diff --git a/src/tools/tool_client.rs b/src/tools/tool_client.rs index fe88bc6e..048cf3bc 100644 --- a/src/tools/tool_client.rs +++ b/src/tools/tool_client.rs @@ -330,7 +330,7 @@ impl ToolClient { self_id: s.registry, name: s.jay_compositor.0, interface: JayCompositor.name(), - version: s.jay_compositor.1.min(8), + version: s.jay_compositor.1.min(10), id: id.into(), }); self.jay_compositor.set(Some(id)); diff --git a/src/tree/output.rs b/src/tree/output.rs index f43e961c..2f774406 100644 --- a/src/tree/output.rs +++ b/src/tree/output.rs @@ -83,6 +83,7 @@ pub struct OutputNode { pub latch_event: EventSource, pub vblank_event: EventSource, pub presentation_event: EventSource, + pub flip_margin_ns: Cell>, } pub trait LatchListener { diff --git a/toml-config/src/config.rs b/toml-config/src/config.rs index b18281d8..ffd9af1d 100644 --- a/toml-config/src/config.rs +++ b/toml-config/src/config.rs @@ -274,6 +274,7 @@ pub struct ConfigDrmDevice { pub match_: DrmDeviceMatch, pub gfx_api: Option, pub direct_scanout_enabled: Option, + pub flip_margin_ms: Option, } #[derive(Debug, Clone)] diff --git a/toml-config/src/config/extractor.rs b/toml-config/src/config/extractor.rs index 58c2d7d5..90da0cc5 100644 --- a/toml-config/src/config/extractor.rs +++ b/toml-config/src/config/extractor.rs @@ -154,7 +154,7 @@ macro_rules! ty { ty!(str, 'a, String, &'a str, v, v.as_str(), "a string"); ty!(int, 'a, Integer, i64, v, *v, "an integer"); -ty!(flt, 'a, Float, f64, v, *v, "a float"); +// ty!(flt, 'a, Float, f64, v, *v, "a float"); ty!(bol, 'a, Boolean, bool, v, *v, "a boolean"); ty!(arr, 'a, Array, &'a [Spanned], v, &**v, "an array"); // ty!(tbl, 'a, Table, &'a IndexMap, Spanned>, v, v, "a table"); diff --git a/toml-config/src/config/parsers/drm_device.rs b/toml-config/src/config/parsers/drm_device.rs index 4a4afd52..ddd0a66d 100644 --- a/toml-config/src/config/parsers/drm_device.rs +++ b/toml-config/src/config/parsers/drm_device.rs @@ -2,7 +2,7 @@ use { crate::{ config::{ context::Context, - extractor::{bol, opt, recover, str, val, Extractor, ExtractorError}, + extractor::{bol, fltorint, opt, recover, str, val, Extractor, ExtractorError}, parser::{DataType, ParseResult, Parser, UnexpectedDataType}, parsers::{ drm_device_match::{DrmDeviceMatchParser, DrmDeviceMatchParserError}, @@ -45,12 +45,14 @@ impl<'a> Parser for DrmDeviceParser<'a> { table: &IndexMap, Spanned>, ) -> ParseResult { let mut ext = Extractor::new(self.cx, span, table); - let (name, match_val, direct_scanout_enabled, gfx_api_val) = ext.extract(( - opt(str("name")), - val("match"), - recover(opt(bol("direct-scanout"))), - opt(val("gfx-api")), - ))?; + let (name, match_val, direct_scanout_enabled, gfx_api_val, flip_margin_ms) = + ext.extract(( + opt(str("name")), + val("match"), + recover(opt(bol("direct-scanout"))), + opt(val("gfx-api")), + recover(opt(fltorint("flip-margin-ms"))), + ))?; let gfx_api = match gfx_api_val { Some(api) => match api.parse(&mut GfxApiParser) { Ok(m) => Some(m), @@ -80,6 +82,7 @@ impl<'a> Parser for DrmDeviceParser<'a> { match_: match_val.parse_map(&mut DrmDeviceMatchParser(self.cx))?, direct_scanout_enabled: direct_scanout_enabled.despan(), gfx_api, + flip_margin_ms: flip_margin_ms.despan(), }) } } diff --git a/toml-config/src/config/parsers/mode.rs b/toml-config/src/config/parsers/mode.rs index 3482b6d2..e8af6ba6 100644 --- a/toml-config/src/config/parsers/mode.rs +++ b/toml-config/src/config/parsers/mode.rs @@ -2,7 +2,7 @@ use { crate::{ config::{ context::Context, - extractor::{flt, opt, s32, Extractor, ExtractorError}, + extractor::{fltorint, opt, s32, Extractor, ExtractorError}, parser::{DataType, ParseResult, Parser, UnexpectedDataType}, Mode, }, @@ -37,7 +37,7 @@ impl<'a> Parser for ModeParser<'a> { ) -> ParseResult { let mut ext = Extractor::new(self.0, span, table); let (width, height, refresh_rate) = - ext.extract((s32("width"), s32("height"), opt(flt("refresh-rate"))))?; + ext.extract((s32("width"), s32("height"), opt(fltorint("refresh-rate"))))?; Ok(Mode { width: width.value, height: height.value, diff --git a/toml-config/src/lib.rs b/toml-config/src/lib.rs index 8ed0bbec..8567d86d 100644 --- a/toml-config/src/lib.rs +++ b/toml-config/src/lib.rs @@ -35,7 +35,7 @@ use { set_vrr_mode, Connector, DrmDevice, }, }, - std::{cell::RefCell, io::ErrorKind, path::PathBuf, rc::Rc}, + std::{cell::RefCell, io::ErrorKind, path::PathBuf, rc::Rc, time::Duration}, }; fn default_seat() -> Seat { @@ -245,6 +245,9 @@ impl ConfigDrmDevice { if let Some(dse) = self.direct_scanout_enabled { d.set_direct_scanout_enabled(dse); } + if let Some(fm) = self.flip_margin_ms { + d.set_flip_margin(Duration::from_nanos((fm * 1_000_000.0) as _)); + } } } diff --git a/toml-spec/spec/spec.generated.json b/toml-spec/spec/spec.generated.json index 80eefd17..9a972fd5 100644 --- a/toml-spec/spec/spec.generated.json +++ b/toml-spec/spec/spec.generated.json @@ -653,6 +653,10 @@ "gfx-api": { "description": "If specified, sets the graphics API to use for this device.\n", "$ref": "#/$defs/GfxApi" + }, + "flip-margin-ms": { + "type": "number", + "description": "If specified, sets the flip margin of this device.\n\nThis is duration between the compositor initiating a page flip and the output's\nvblank event. This determines the minimum input latency. The default is 1.5 ms.\n\nNote that if the margin is too small, the compositor will dynamically increase it.\n" } }, "required": [ diff --git a/toml-spec/spec/spec.generated.md b/toml-spec/spec/spec.generated.md index 6157bfa2..5c51ff1c 100644 --- a/toml-spec/spec/spec.generated.md +++ b/toml-spec/spec/spec.generated.md @@ -1266,6 +1266,17 @@ The table has the following fields: The value of this field should be a [GfxApi](#types-GfxApi). +- `flip-margin-ms` (optional): + + If specified, sets the flip margin of this device. + + This is duration between the compositor initiating a page flip and the output's + vblank event. This determines the minimum input latency. The default is 1.5 ms. + + Note that if the margin is too small, the compositor will dynamically increase it. + + The value of this field should be a number. + ### `DrmDeviceMatch` diff --git a/toml-spec/spec/spec.yaml b/toml-spec/spec/spec.yaml index a8602ce1..b34ca4d1 100644 --- a/toml-spec/spec/spec.yaml +++ b/toml-spec/spec/spec.yaml @@ -944,6 +944,16 @@ DrmDevice: required: false description: | If specified, sets the graphics API to use for this device. + flip-margin-ms: + kind: number + required: false + description: | + If specified, sets the flip margin of this device. + + This is duration between the compositor initiating a page flip and the output's + vblank event. This determines the minimum input latency. The default is 1.5 ms. + + Note that if the margin is too small, the compositor will dynamically increase it. GfxApi: diff --git a/wire/jay_randr.txt b/wire/jay_randr.txt index 109e52f7..4799bd62 100644 --- a/wire/jay_randr.txt +++ b/wire/jay_randr.txt @@ -75,6 +75,11 @@ request set_fb_format (since = 8) { format: str, } +request set_flip_margin (since = 10) { + dev: str, + margin_ns: pod(u64), +} + # events event global { @@ -151,3 +156,7 @@ event fb_format (since = 8) { name: str, current: u32, } + +event flip_margin (since = 10) { + margin_ns: pod(u64), +}