metal: make post_commit_margin configurable
This commit is contained in:
parent
76b0f2f734
commit
02ece60909
24 changed files with 224 additions and 55 deletions
|
|
@ -744,6 +744,10 @@ impl Client {
|
||||||
self.send(&ClientMessage::SetDirectScanoutEnabled { device, enabled });
|
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 {
|
pub fn connector_connected(&self, connector: Connector) -> bool {
|
||||||
let res = self.send_with_response(&ClientMessage::ConnectorConnected { connector });
|
let res = self.send_with_response(&ClientMessage::ConnectorConnected { connector });
|
||||||
get_response!(res, false, ConnectorConnected { connected });
|
get_response!(res, false, ConnectorConnected { connected });
|
||||||
|
|
|
||||||
|
|
@ -513,6 +513,10 @@ pub enum ClientMessage<'a> {
|
||||||
connector: Connector,
|
connector: Connector,
|
||||||
format: Format,
|
format: Format,
|
||||||
},
|
},
|
||||||
|
SetFlipMargin {
|
||||||
|
device: DrmDevice,
|
||||||
|
margin: Duration,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ use {
|
||||||
_private::WireMode,
|
_private::WireMode,
|
||||||
},
|
},
|
||||||
serde::{Deserialize, Serialize},
|
serde::{Deserialize, Serialize},
|
||||||
std::str::FromStr,
|
std::{str::FromStr, time::Duration},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The mode of a connector.
|
/// The mode of a connector.
|
||||||
|
|
@ -504,6 +504,16 @@ impl DrmDevice {
|
||||||
pub fn set_direct_scanout_enabled(self, enabled: bool) {
|
pub fn set_direct_scanout_enabled(self, enabled: bool) {
|
||||||
get!().set_direct_scanout_enabled(Some(self), enabled);
|
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.
|
/// A graphics API.
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
- Upload shm textures on a separate thread in the Vulkan renderer.
|
- Upload shm textures on a separate thread in the Vulkan renderer.
|
||||||
- Disable implicit sync in KMS.
|
- Disable implicit sync in KMS.
|
||||||
- Implement frame scheduling for KMS.
|
- Implement frame scheduling for KMS.
|
||||||
|
- The JAY_MAX_RENDER_TIME_NSEC environment variable has been removed.
|
||||||
|
|
||||||
# 1.5.0 (2024-09-02)
|
# 1.5.0 (2024-09-02)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -456,6 +456,9 @@ pub trait BackendDrmDevice {
|
||||||
let _ = lessee;
|
let _ = lessee;
|
||||||
let _ = connector_ids;
|
let _ = connector_ids;
|
||||||
}
|
}
|
||||||
|
fn set_flip_margin(&self, margin: u64) {
|
||||||
|
let _ = margin;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait BackendDrmLease {
|
pub trait BackendDrmLease {
|
||||||
|
|
|
||||||
|
|
@ -23,11 +23,7 @@ use {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
std::{
|
std::rc::{Rc, Weak},
|
||||||
env,
|
|
||||||
rc::{Rc, Weak},
|
|
||||||
sync::LazyLock,
|
|
||||||
},
|
|
||||||
uapi::c,
|
uapi::c,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -85,19 +81,9 @@ enum CursorProgramming {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const DEFAULT_PRE_COMMIT_MARGIN: u64 = 16_000_000; // 16ms
|
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 DEFAULT_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
|
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 {
|
impl MetalConnector {
|
||||||
pub fn schedule_present(&self) {
|
pub fn schedule_present(&self) {
|
||||||
self.present_trigger.trigger();
|
self.present_trigger.trigger();
|
||||||
|
|
@ -113,10 +99,13 @@ impl MetalConnector {
|
||||||
}
|
}
|
||||||
let mut expected_sequence = self.sequence.get() + 1;
|
let mut expected_sequence = self.sequence.get() + 1;
|
||||||
let mut start = Time::now_unchecked();
|
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 {
|
if use_frame_scheduling {
|
||||||
let margin = self.pre_commit_margin.get() + self.post_commit_margin.get();
|
let next_present = self
|
||||||
let next_present = self.next_flip_nsec.get().saturating_sub(margin);
|
.next_flip_nsec
|
||||||
|
.get()
|
||||||
|
.saturating_sub(self.pre_commit_margin.get())
|
||||||
|
.saturating_sub(self.post_commit_margin.get());
|
||||||
if start.nsec() < next_present {
|
if start.nsec() < next_present {
|
||||||
self.state.ring.timeout(next_present).await.unwrap();
|
self.state.ring.timeout(next_present).await.unwrap();
|
||||||
start = Time::now_unchecked();
|
start = Time::now_unchecked();
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,7 @@ use {
|
||||||
backends::metal::{
|
backends::metal::{
|
||||||
present::{
|
present::{
|
||||||
DirectScanoutCache, PresentFb, DEFAULT_POST_COMMIT_MARGIN,
|
DirectScanoutCache, PresentFb, DEFAULT_POST_COMMIT_MARGIN,
|
||||||
DEFAULT_PRE_COMMIT_MARGIN, MAX_POST_COMMIT_MARGIN, MIN_POST_COMMIT_MARGIN,
|
DEFAULT_PRE_COMMIT_MARGIN, POST_COMMIT_MARGIN_DELTA,
|
||||||
POST_COMMIT_MARGIN_DELTA,
|
|
||||||
},
|
},
|
||||||
MetalBackend, MetalError,
|
MetalBackend, MetalError,
|
||||||
},
|
},
|
||||||
|
|
@ -27,6 +26,7 @@ use {
|
||||||
wp_presentation_feedback::{KIND_HW_COMPLETION, KIND_VSYNC, KIND_ZERO_COPY},
|
wp_presentation_feedback::{KIND_HW_COMPLETION, KIND_VSYNC, KIND_ZERO_COPY},
|
||||||
},
|
},
|
||||||
state::State,
|
state::State,
|
||||||
|
tree::OutputNode,
|
||||||
udev::UdevDevice,
|
udev::UdevDevice,
|
||||||
utils::{
|
utils::{
|
||||||
asyncevent::AsyncEvent, bitflags::BitflagsExt, cell_ext::CellExt, clonecell::CloneCell,
|
asyncevent::AsyncEvent, bitflags::BitflagsExt, cell_ext::CellExt, clonecell::CloneCell,
|
||||||
|
|
@ -108,6 +108,7 @@ pub struct MetalDrmDevice {
|
||||||
pub leases: CopyHashMap<MetalLeaseId, MetalLeaseData>,
|
pub leases: CopyHashMap<MetalLeaseId, MetalLeaseData>,
|
||||||
pub leases_to_break: CopyHashMap<MetalLeaseId, MetalLeaseData>,
|
pub leases_to_break: CopyHashMap<MetalLeaseId, MetalLeaseData>,
|
||||||
pub paused: Cell<bool>,
|
pub paused: Cell<bool>,
|
||||||
|
pub min_post_commit_margin: Cell<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for MetalDrmDevice {
|
impl Debug for MetalDrmDevice {
|
||||||
|
|
@ -279,6 +280,19 @@ impl BackendDrmDevice for MetalDrmDevice {
|
||||||
});
|
});
|
||||||
lessee.created(lease);
|
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 {
|
pub struct HandleEvents {
|
||||||
|
|
@ -1067,8 +1081,8 @@ fn create_connector(
|
||||||
expected_sequence: Default::default(),
|
expected_sequence: Default::default(),
|
||||||
pre_commit_margin_decay: GeometricDecay::new(0.5, DEFAULT_PRE_COMMIT_MARGIN),
|
pre_commit_margin_decay: GeometricDecay::new(0.5, DEFAULT_PRE_COMMIT_MARGIN),
|
||||||
pre_commit_margin: Cell::new(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_decay: GeometricDecay::new(0.1, dev.min_post_commit_margin.get()),
|
||||||
post_commit_margin: Cell::new(DEFAULT_POST_COMMIT_MARGIN),
|
post_commit_margin: Cell::new(dev.min_post_commit_margin.get()),
|
||||||
vblank_miss_sec: Cell::new(0),
|
vblank_miss_sec: Cell::new(0),
|
||||||
vblank_miss_this_sec: Default::default(),
|
vblank_miss_this_sec: Default::default(),
|
||||||
presentation_is_sync: Cell::new(false),
|
presentation_is_sync: Cell::new(false),
|
||||||
|
|
@ -1750,6 +1764,7 @@ impl MetalBackend {
|
||||||
leases: Default::default(),
|
leases: Default::default(),
|
||||||
leases_to_break: Default::default(),
|
leases_to_break: Default::default(),
|
||||||
paused: Cell::new(false),
|
paused: Cell::new(false),
|
||||||
|
min_post_commit_margin: Cell::new(DEFAULT_POST_COMMIT_MARGIN),
|
||||||
});
|
});
|
||||||
|
|
||||||
let (connectors, futures) = get_connectors(self, &dev, &resources.connectors)?;
|
let (connectors, futures) = get_connectors(self, &dev, &resources.connectors)?;
|
||||||
|
|
@ -1943,24 +1958,11 @@ impl MetalBackend {
|
||||||
if let Some(fb) = connector.next_framebuffer.take() {
|
if let Some(fb) = connector.next_framebuffer.take() {
|
||||||
*connector.active_framebuffer.borrow_mut() = Some(fb);
|
*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 let Some(expected) = connector.expected_sequence.take() {
|
||||||
if connector.vblank_miss_sec.replace(tv_sec) != tv_sec {
|
if connector.vblank_miss_sec.replace(tv_sec) != tv_sec {
|
||||||
let n_missed = connector.vblank_miss_this_sec.replace(0);
|
self.update_post_commit_margin(dev, &connector, &dd, global.as_deref());
|
||||||
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();
|
let actual = connector.sequence.get();
|
||||||
if expected < actual {
|
if expected < actual {
|
||||||
|
|
@ -1973,12 +1975,10 @@ impl MetalBackend {
|
||||||
{
|
{
|
||||||
connector.schedule_present();
|
connector.schedule_present();
|
||||||
}
|
}
|
||||||
let dd = connector.display.borrow_mut();
|
|
||||||
connector
|
connector
|
||||||
.next_flip_nsec
|
.next_flip_nsec
|
||||||
.set(tv_sec as u64 * 1_000_000_000 + tv_usec as u64 * 1000 + dd.refresh as u64);
|
.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;
|
let mut flags = KIND_HW_COMPLETION;
|
||||||
if connector.presentation_is_sync.get() {
|
if connector.presentation_is_sync.get() {
|
||||||
flags |= KIND_VSYNC;
|
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) {
|
fn reset_planes(&self, dev: &MetalDrmDeviceData, changes: &mut Change, preserve: &Preserve) {
|
||||||
for plane in dev.dev.planes.values() {
|
for plane in dev.dev.planes.values() {
|
||||||
if preserve.planes.contains(&plane.id) {
|
if preserve.planes.contains(&plane.id) {
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ use {
|
||||||
fmt::{Display, Formatter},
|
fmt::{Display, Formatter},
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
|
time::Duration,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -66,6 +67,31 @@ pub enum CardCommand {
|
||||||
Api(ApiArgs),
|
Api(ApiArgs),
|
||||||
/// Modify the direct scanout setting of the card.
|
/// Modify the direct scanout setting of the card.
|
||||||
DirectScanout(DirectScanoutArgs),
|
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)]
|
#[derive(Args, Debug, Clone)]
|
||||||
|
|
@ -341,6 +367,7 @@ struct Output {
|
||||||
pub tearing_mode: TearingMode,
|
pub tearing_mode: TearingMode,
|
||||||
pub formats: Vec<String>,
|
pub formats: Vec<String>,
|
||||||
pub format: Option<String>,
|
pub format: Option<String>,
|
||||||
|
pub flip_margin_ns: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[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;
|
tc.round_trip().await;
|
||||||
}
|
}
|
||||||
|
|
@ -759,6 +798,14 @@ impl Randr {
|
||||||
};
|
};
|
||||||
println!(" transform: {}", name);
|
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 {
|
if o.modes.is_not_empty() && modes {
|
||||||
println!(" modes:");
|
println!(" modes:");
|
||||||
for mode in &o.modes {
|
for mode in &o.modes {
|
||||||
|
|
@ -838,6 +885,7 @@ impl Randr {
|
||||||
tearing_mode: TearingMode::NEVER,
|
tearing_mode: TearingMode::NEVER,
|
||||||
formats: vec![],
|
formats: vec![],
|
||||||
format: None,
|
format: None,
|
||||||
|
flip_margin_ns: None,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
jay_randr::NonDesktopOutput::handle(tc, randr, data.clone(), |data, msg| {
|
jay_randr::NonDesktopOutput::handle(tc, randr, data.clone(), |data, msg| {
|
||||||
|
|
@ -865,6 +913,7 @@ impl Randr {
|
||||||
tearing_mode: TearingMode::NEVER,
|
tearing_mode: TearingMode::NEVER,
|
||||||
formats: vec![],
|
formats: vec![],
|
||||||
format: None,
|
format: None,
|
||||||
|
flip_margin_ns: None,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
jay_randr::VrrState::handle(tc, randr, data.clone(), |data, msg| {
|
jay_randr::VrrState::handle(tc, randr, data.clone(), |data, msg| {
|
||||||
|
|
@ -896,6 +945,12 @@ impl Randr {
|
||||||
output.format = Some(msg.name.to_string());
|
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| {
|
jay_randr::Mode::handle(tc, randr, data.clone(), |data, msg| {
|
||||||
let mut data = data.borrow_mut();
|
let mut data = data.borrow_mut();
|
||||||
let c = data.connectors.last_mut().unwrap();
|
let c = data.connectors.last_mut().unwrap();
|
||||||
|
|
|
||||||
|
|
@ -507,6 +507,7 @@ fn create_dummy_output(state: &Rc<State>) {
|
||||||
vblank_event: Default::default(),
|
vblank_event: Default::default(),
|
||||||
latch_event: Default::default(),
|
latch_event: Default::default(),
|
||||||
presentation_event: Default::default(),
|
presentation_event: Default::default(),
|
||||||
|
flip_margin_ns: Default::default(),
|
||||||
});
|
});
|
||||||
let dummy_workspace = Rc::new(WorkspaceNode {
|
let dummy_workspace = Rc::new(WorkspaceNode {
|
||||||
id: state.node_ids.next(),
|
id: state.node_ids.next(),
|
||||||
|
|
|
||||||
|
|
@ -752,6 +752,13 @@ impl ConfigProxyHandler {
|
||||||
Ok(())
|
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(
|
fn handle_set_direct_scanout_enabled(
|
||||||
&self,
|
&self,
|
||||||
device: Option<DrmDevice>,
|
device: Option<DrmDevice>,
|
||||||
|
|
@ -1936,6 +1943,9 @@ impl ConfigProxyHandler {
|
||||||
ClientMessage::ConnectorSetFormat { connector, format } => self
|
ClientMessage::ConnectorSetFormat { connector, format } => self
|
||||||
.handle_connector_set_format(connector, format)
|
.handle_connector_set_format(connector, format)
|
||||||
.wrn("connector_set_format")?,
|
.wrn("connector_set_format")?,
|
||||||
|
ClientMessage::SetFlipMargin { device, margin } => self
|
||||||
|
.handle_set_flip_margin(device, margin)
|
||||||
|
.wrn("set_flip_margin")?,
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ impl Global for JayCompositorGlobal {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn version(&self) -> u32 {
|
fn version(&self) -> u32 {
|
||||||
9
|
10
|
||||||
}
|
}
|
||||||
|
|
||||||
fn required_caps(&self) -> ClientCaps {
|
fn required_caps(&self) -> ClientCaps {
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ pub struct JayRandr {
|
||||||
const VRR_CAPABLE_SINCE: Version = Version(2);
|
const VRR_CAPABLE_SINCE: Version = Version(2);
|
||||||
const TEARING_SINCE: Version = Version(3);
|
const TEARING_SINCE: Version = Version(3);
|
||||||
const FORMAT_SINCE: Version = Version(8);
|
const FORMAT_SINCE: Version = Version(8);
|
||||||
|
const FLIP_MARGIN_SINCE: Version = Version(10);
|
||||||
|
|
||||||
impl JayRandr {
|
impl JayRandr {
|
||||||
pub fn new(id: JayRandrId, client: &Rc<Client>, version: Version) -> Self {
|
pub fn new(id: JayRandrId, client: &Rc<Client>, 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();
|
let current_mode = global.mode.get();
|
||||||
for mode in &global.modes {
|
for mode in &global.modes {
|
||||||
self.client.event(Mode {
|
self.client.event(Mode {
|
||||||
|
|
@ -395,6 +404,14 @@ impl JayRandrRequestHandler for JayRandr {
|
||||||
c.global.connector.connector.set_fb_format(format);
|
c.global.connector.connector.set_fb_format(format);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_flip_margin(&self, req: SetFlipMargin<'_>, _slf: &Rc<Self>) -> 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! {
|
object_base! {
|
||||||
|
|
|
||||||
|
|
@ -179,6 +179,7 @@ impl ConnectorHandler {
|
||||||
latch_event: Default::default(),
|
latch_event: Default::default(),
|
||||||
vblank_event: Default::default(),
|
vblank_event: Default::default(),
|
||||||
presentation_event: Default::default(),
|
presentation_event: Default::default(),
|
||||||
|
flip_margin_ns: Default::default(),
|
||||||
});
|
});
|
||||||
on.update_visible();
|
on.update_visible();
|
||||||
on.update_rects();
|
on.update_rects();
|
||||||
|
|
|
||||||
|
|
@ -330,7 +330,7 @@ impl ToolClient {
|
||||||
self_id: s.registry,
|
self_id: s.registry,
|
||||||
name: s.jay_compositor.0,
|
name: s.jay_compositor.0,
|
||||||
interface: JayCompositor.name(),
|
interface: JayCompositor.name(),
|
||||||
version: s.jay_compositor.1.min(8),
|
version: s.jay_compositor.1.min(10),
|
||||||
id: id.into(),
|
id: id.into(),
|
||||||
});
|
});
|
||||||
self.jay_compositor.set(Some(id));
|
self.jay_compositor.set(Some(id));
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,7 @@ pub struct OutputNode {
|
||||||
pub latch_event: EventSource<dyn LatchListener>,
|
pub latch_event: EventSource<dyn LatchListener>,
|
||||||
pub vblank_event: EventSource<dyn VblankListener>,
|
pub vblank_event: EventSource<dyn VblankListener>,
|
||||||
pub presentation_event: EventSource<dyn PresentationListener>,
|
pub presentation_event: EventSource<dyn PresentationListener>,
|
||||||
|
pub flip_margin_ns: Cell<Option<u64>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait LatchListener {
|
pub trait LatchListener {
|
||||||
|
|
|
||||||
|
|
@ -274,6 +274,7 @@ pub struct ConfigDrmDevice {
|
||||||
pub match_: DrmDeviceMatch,
|
pub match_: DrmDeviceMatch,
|
||||||
pub gfx_api: Option<GfxApi>,
|
pub gfx_api: Option<GfxApi>,
|
||||||
pub direct_scanout_enabled: Option<bool>,
|
pub direct_scanout_enabled: Option<bool>,
|
||||||
|
pub flip_margin_ms: Option<f64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
|
||||||
|
|
@ -154,7 +154,7 @@ macro_rules! ty {
|
||||||
|
|
||||||
ty!(str, 'a, String, &'a str, v, v.as_str(), "a string");
|
ty!(str, 'a, String, &'a str, v, v.as_str(), "a string");
|
||||||
ty!(int, 'a, Integer, i64, v, *v, "an integer");
|
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!(bol, 'a, Boolean, bool, v, *v, "a boolean");
|
||||||
ty!(arr, 'a, Array, &'a [Spanned<Value>], v, &**v, "an array");
|
ty!(arr, 'a, Array, &'a [Spanned<Value>], v, &**v, "an array");
|
||||||
// ty!(tbl, 'a, Table, &'a IndexMap<Spanned<String>, Spanned<Value>>, v, v, "a table");
|
// ty!(tbl, 'a, Table, &'a IndexMap<Spanned<String>, Spanned<Value>>, v, v, "a table");
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use {
|
||||||
crate::{
|
crate::{
|
||||||
config::{
|
config::{
|
||||||
context::Context,
|
context::Context,
|
||||||
extractor::{bol, opt, recover, str, val, Extractor, ExtractorError},
|
extractor::{bol, fltorint, opt, recover, str, val, Extractor, ExtractorError},
|
||||||
parser::{DataType, ParseResult, Parser, UnexpectedDataType},
|
parser::{DataType, ParseResult, Parser, UnexpectedDataType},
|
||||||
parsers::{
|
parsers::{
|
||||||
drm_device_match::{DrmDeviceMatchParser, DrmDeviceMatchParserError},
|
drm_device_match::{DrmDeviceMatchParser, DrmDeviceMatchParserError},
|
||||||
|
|
@ -45,12 +45,14 @@ impl<'a> Parser for DrmDeviceParser<'a> {
|
||||||
table: &IndexMap<Spanned<String>, Spanned<Value>>,
|
table: &IndexMap<Spanned<String>, Spanned<Value>>,
|
||||||
) -> ParseResult<Self> {
|
) -> ParseResult<Self> {
|
||||||
let mut ext = Extractor::new(self.cx, span, table);
|
let mut ext = Extractor::new(self.cx, span, table);
|
||||||
let (name, match_val, direct_scanout_enabled, gfx_api_val) = ext.extract((
|
let (name, match_val, direct_scanout_enabled, gfx_api_val, flip_margin_ms) =
|
||||||
opt(str("name")),
|
ext.extract((
|
||||||
val("match"),
|
opt(str("name")),
|
||||||
recover(opt(bol("direct-scanout"))),
|
val("match"),
|
||||||
opt(val("gfx-api")),
|
recover(opt(bol("direct-scanout"))),
|
||||||
))?;
|
opt(val("gfx-api")),
|
||||||
|
recover(opt(fltorint("flip-margin-ms"))),
|
||||||
|
))?;
|
||||||
let gfx_api = match gfx_api_val {
|
let gfx_api = match gfx_api_val {
|
||||||
Some(api) => match api.parse(&mut GfxApiParser) {
|
Some(api) => match api.parse(&mut GfxApiParser) {
|
||||||
Ok(m) => Some(m),
|
Ok(m) => Some(m),
|
||||||
|
|
@ -80,6 +82,7 @@ impl<'a> Parser for DrmDeviceParser<'a> {
|
||||||
match_: match_val.parse_map(&mut DrmDeviceMatchParser(self.cx))?,
|
match_: match_val.parse_map(&mut DrmDeviceMatchParser(self.cx))?,
|
||||||
direct_scanout_enabled: direct_scanout_enabled.despan(),
|
direct_scanout_enabled: direct_scanout_enabled.despan(),
|
||||||
gfx_api,
|
gfx_api,
|
||||||
|
flip_margin_ms: flip_margin_ms.despan(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use {
|
||||||
crate::{
|
crate::{
|
||||||
config::{
|
config::{
|
||||||
context::Context,
|
context::Context,
|
||||||
extractor::{flt, opt, s32, Extractor, ExtractorError},
|
extractor::{fltorint, opt, s32, Extractor, ExtractorError},
|
||||||
parser::{DataType, ParseResult, Parser, UnexpectedDataType},
|
parser::{DataType, ParseResult, Parser, UnexpectedDataType},
|
||||||
Mode,
|
Mode,
|
||||||
},
|
},
|
||||||
|
|
@ -37,7 +37,7 @@ impl<'a> Parser for ModeParser<'a> {
|
||||||
) -> ParseResult<Self> {
|
) -> ParseResult<Self> {
|
||||||
let mut ext = Extractor::new(self.0, span, table);
|
let mut ext = Extractor::new(self.0, span, table);
|
||||||
let (width, height, refresh_rate) =
|
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 {
|
Ok(Mode {
|
||||||
width: width.value,
|
width: width.value,
|
||||||
height: height.value,
|
height: height.value,
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ use {
|
||||||
set_vrr_mode, Connector, DrmDevice,
|
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 {
|
fn default_seat() -> Seat {
|
||||||
|
|
@ -245,6 +245,9 @@ impl ConfigDrmDevice {
|
||||||
if let Some(dse) = self.direct_scanout_enabled {
|
if let Some(dse) = self.direct_scanout_enabled {
|
||||||
d.set_direct_scanout_enabled(dse);
|
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 _));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -653,6 +653,10 @@
|
||||||
"gfx-api": {
|
"gfx-api": {
|
||||||
"description": "If specified, sets the graphics API to use for this device.\n",
|
"description": "If specified, sets the graphics API to use for this device.\n",
|
||||||
"$ref": "#/$defs/GfxApi"
|
"$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": [
|
"required": [
|
||||||
|
|
|
||||||
|
|
@ -1266,6 +1266,17 @@ The table has the following fields:
|
||||||
|
|
||||||
The value of this field should be a [GfxApi](#types-GfxApi).
|
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.
|
||||||
|
|
||||||
|
|
||||||
<a name="types-DrmDeviceMatch"></a>
|
<a name="types-DrmDeviceMatch"></a>
|
||||||
### `DrmDeviceMatch`
|
### `DrmDeviceMatch`
|
||||||
|
|
|
||||||
|
|
@ -944,6 +944,16 @@ DrmDevice:
|
||||||
required: false
|
required: false
|
||||||
description: |
|
description: |
|
||||||
If specified, sets the graphics API to use for this device.
|
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:
|
GfxApi:
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,11 @@ request set_fb_format (since = 8) {
|
||||||
format: str,
|
format: str,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
request set_flip_margin (since = 10) {
|
||||||
|
dev: str,
|
||||||
|
margin_ns: pod(u64),
|
||||||
|
}
|
||||||
|
|
||||||
# events
|
# events
|
||||||
|
|
||||||
event global {
|
event global {
|
||||||
|
|
@ -151,3 +156,7 @@ event fb_format (since = 8) {
|
||||||
name: str,
|
name: str,
|
||||||
current: u32,
|
current: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
event flip_margin (since = 10) {
|
||||||
|
margin_ns: pod(u64),
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue