Skip to content

Commit

Permalink
metal: implement tearing
Browse files Browse the repository at this point in the history
  • Loading branch information
mahkoh committed Jul 18, 2024
1 parent d355059 commit 49f6304
Show file tree
Hide file tree
Showing 31 changed files with 726 additions and 51 deletions.
8 changes: 5 additions & 3 deletions docs/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ Jay supports leasing VR headsets to applications.

Jay supports adaptive sync with configurable cursor refresh rates.

## Tearing

Jay supports tearing presentation for games.

## Protocol Support

Jay supports the following wayland protocols:
Expand Down Expand Up @@ -153,7 +157,7 @@ Jay supports the following wayland protocols:
| wp_presentation | 1 | |
| wp_security_context_manager_v1 | 1 | |
| wp_single_pixel_buffer_manager_v1 | 1 | |
| wp_tearing_control_manager_v1 | 1[^no_tearing] | |
| wp_tearing_control_manager_v1 | 1 | |
| wp_viewporter | 1 | |
| xdg_activation_v1 | 1 | |
| xdg_toplevel_drag_manager_v1 | 1 | |
Expand All @@ -176,7 +180,6 @@ Jay supports the following wayland protocols:
| zxdg_output_manager_v1 | 3 | |

[^no_touch]: Touch input is not supported.
[^no_tearing]: Tearing screen updates are not supported.
[^lsaccess]: Sandboxes can restrict access to this protocol.
[^ts_rejected]: Seat creation is always rejected.

Expand All @@ -185,4 +188,3 @@ Jay supports the following wayland protocols:
The following features are currently not supported but might get implemented in the future:

- Touch support.
- Tearing updates of fullscreen games.
6 changes: 5 additions & 1 deletion jay-config/src/_private/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use {
timer::Timer,
video::{
connector_type::{ConnectorType, CON_UNKNOWN},
Connector, DrmDevice, GfxApi, Mode, Transform, VrrMode,
Connector, DrmDevice, GfxApi, Mode, TearingMode, Transform, VrrMode,
},
Axis, Direction, ModifiedKeySym, PciId, Workspace,
},
Expand Down Expand Up @@ -808,6 +808,10 @@ impl Client {
self.send(&ClientMessage::SetVrrCursorHz { connector, hz })
}

pub fn set_tearing_mode(&self, connector: Option<Connector>, mode: TearingMode) {
self.send(&ClientMessage::SetTearingMode { connector, mode })
}

pub fn drm_devices(&self) -> Vec<DrmDevice> {
let res = self.send_with_response(&ClientMessage::GetDrmDevices);
get_response!(res, vec![], GetDrmDevices { devices });
Expand Down
9 changes: 8 additions & 1 deletion jay-config/src/_private/ipc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ use {
logging::LogLevel,
theme::{colors::Colorable, sized::Resizable, Color},
timer::Timer,
video::{connector_type::ConnectorType, Connector, DrmDevice, GfxApi, Transform, VrrMode},
video::{
connector_type::ConnectorType, Connector, DrmDevice, GfxApi, TearingMode, Transform,
VrrMode,
},
Axis, Direction, PciId, Workspace,
_private::{PollableId, WireMode},
},
Expand Down Expand Up @@ -495,6 +498,10 @@ pub enum ClientMessage<'a> {
connector: Option<Connector>,
hz: f64,
},
SetTearingMode {
connector: Option<Connector>,
mode: TearingMode,
},
}

#[derive(Serialize, Deserialize, Debug)]
Expand Down
32 changes: 32 additions & 0 deletions jay-config/src/video.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,11 @@ impl Connector {
pub fn set_vrr_cursor_hz(self, hz: f64) {
get!().set_vrr_cursor_hz(Some(self), hz)
}

/// Sets the tearing mode.
pub fn set_tearing_mode(self, mode: TearingMode) {
get!().set_tearing_mode(Some(self), mode)
}
}

/// Returns all available DRM devices.
Expand Down Expand Up @@ -580,3 +585,30 @@ pub fn set_vrr_mode(mode: VrrMode) {
pub fn set_vrr_cursor_hz(hz: f64) {
get!().set_vrr_cursor_hz(None, hz)
}

/// The tearing mode of a connector.
#[derive(Serialize, Deserialize, Copy, Clone, Debug, Eq, PartialEq, Hash, Default)]
pub struct TearingMode(pub u32);

impl TearingMode {
/// Tearing is never enabled.
pub const NEVER: Self = Self(0);
/// Tearing is always enabled.
pub const ALWAYS: Self = Self(1);
/// Tearing is enabled when one or more applications are displayed fullscreen.
pub const VARIANT_1: Self = Self(2);
/// Tearing is enabled when a single application is displayed fullscreen.
pub const VARIANT_2: Self = Self(3);
/// Tearing is enabled when a single application is displayed fullscreen and the
/// application has requested tearing.
///
/// This is the default.
pub const VARIANT_3: Self = Self(4);
}

/// Sets the default tearing mode.
///
/// This setting can be overwritten on a per-connector basis with [Connector::set_tearing_mode].
pub fn set_tearing_mode(mode: TearingMode) {
get!().set_tearing_mode(None, mode)
}
1 change: 1 addition & 0 deletions release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

- Add fine-grained damage tracking.
- Add support for adaptive sync.
- Add support for tearing.

# 1.4.0 (2024-07-07)

Expand Down
3 changes: 3 additions & 0 deletions src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ pub trait Connector {
fn set_vrr_enabled(&self, enabled: bool) {
let _ = enabled;
}
fn set_tearing_enabled(&self, enabled: bool) {
let _ = enabled;
}
}

#[derive(Debug)]
Expand Down
76 changes: 67 additions & 9 deletions src/backends/metal/video.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ use {
DrmCrtc, DrmEncoder, DrmError, DrmEvent, DrmFramebuffer, DrmLease, DrmMaster,
DrmModeInfo, DrmObject, DrmPlane, DrmProperty, DrmPropertyDefinition,
DrmPropertyType, DrmVersion, PropBlob, DRM_CLIENT_CAP_ATOMIC,
DRM_MODE_ATOMIC_ALLOW_MODESET, DRM_MODE_ATOMIC_NONBLOCK, DRM_MODE_PAGE_FLIP_EVENT,
DRM_MODE_ATOMIC_ALLOW_MODESET, DRM_MODE_ATOMIC_NONBLOCK, DRM_MODE_PAGE_FLIP_ASYNC,
DRM_MODE_PAGE_FLIP_EVENT,
},
gbm::{GbmBo, GbmDevice, GBM_BO_USE_LINEAR, GBM_BO_USE_RENDERING, GBM_BO_USE_SCANOUT},
Modifier, INVALID_MODIFIER,
Expand Down Expand Up @@ -89,6 +90,7 @@ pub struct MetalDrmDevice {
pub _max_height: u32,
pub cursor_width: u64,
pub cursor_height: u64,
pub supports_async_commit: bool,
pub gbm: GbmDevice,
pub handle_events: HandleEvents,
pub ctx: CloneCell<Rc<MetalRenderContext>>,
Expand Down Expand Up @@ -456,6 +458,8 @@ pub struct MetalConnector {
pub active_framebuffer: RefCell<Option<PresentFb>>,
pub next_framebuffer: OpaqueCell<Option<PresentFb>>,
pub direct_scanout_active: Cell<bool>,

pub tearing_requested: Cell<bool>,
}

impl Debug for MetalConnector {
Expand Down Expand Up @@ -947,6 +951,16 @@ impl MetalConnector {
let cursor = self.cursor_plane.get();
let mut new_fb = None;
let mut changes = self.master.change();
let mut try_async_flip = self.tearing_requested.get() && self.dev.supports_async_commit;
macro_rules! change {
($c:expr, $prop:expr, $new:expr) => {{
if $prop.value.get() != $new {
$c.change($prop.id, $new as u64);
try_async_flip = false;
$prop.pending_value.set(Some($new));
}
}};
}
if self.has_damage.get() {
if !self.backend.check_render_context(&self.dev) {
return Ok(());
Expand Down Expand Up @@ -978,13 +992,13 @@ impl MetalConnector {
let in_fence = fb.sync_file.as_ref().map(|s| s.raw()).unwrap_or(-1);
changes.change_object(plane.id, |c| {
c.change(plane.fb_id, fb.fb.id().0 as _);
c.change(plane.src_w.id, (src_width as u64) << 16);
c.change(plane.src_h.id, (src_height as u64) << 16);
c.change(plane.crtc_x.id, crtc_x as u64);
c.change(plane.crtc_y.id, crtc_y as u64);
c.change(plane.crtc_w.id, crtc_w as u64);
c.change(plane.crtc_h.id, crtc_h as u64);
if !self.dev.is_nvidia {
change!(c, plane.src_w, (src_width as u32) << 16);
change!(c, plane.src_h, (src_height as u32) << 16);
change!(c, plane.crtc_x, crtc_x);
change!(c, plane.crtc_y, crtc_y);
change!(c, plane.crtc_w, crtc_w);
change!(c, plane.crtc_h, crtc_h);
if !try_async_flip && !self.dev.is_nvidia {
c.change(plane.in_fence_fd, in_fence as u64);
}
});
Expand All @@ -1002,6 +1016,7 @@ impl MetalConnector {
let mut cursor_swap_buffer = false;
let mut cursor_sync_file = None;
if self.cursor_changed.get() && cursor.is_some() {
try_async_flip = false;
let plane = cursor.unwrap();
if self.cursor_enabled.get() {
cursor_swap_buffer = self.cursor_swap_buffer.get();
Expand Down Expand Up @@ -1039,7 +1054,18 @@ impl MetalConnector {
});
}
}
if let Err(e) = changes.commit(DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT, 0) {
let mut res;
'commit: {
const FLAGS: u32 = DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT;
if try_async_flip {
res = changes.commit(FLAGS | DRM_MODE_PAGE_FLIP_ASYNC, 0);
if res.is_ok() {
break 'commit;
}
}
res = changes.commit(FLAGS, 0);
}
if let Err(e) = res {
if let DrmError::Atomic(OsError(c::EACCES)) = e {
log::debug!("Could not perform atomic commit, likely because we're no longer the DRM master");
self.render_result
Expand Down Expand Up @@ -1069,6 +1095,19 @@ impl MetalConnector {
.discard_presentation_feedback();
Err(MetalError::Commit(e))
} else {
macro_rules! apply_change {
($prop:expr) => {
if let Some(v) = $prop.pending_value.take() {
$prop.value.set(v);
}
};
}
apply_change!(plane.src_w);
apply_change!(plane.src_h);
apply_change!(plane.crtc_x);
apply_change!(plane.crtc_y);
apply_change!(plane.crtc_w);
apply_change!(plane.crtc_h);
node.schedule.presented();
self.perform_screencopies(&new_fb, &node);
if let Some(fb) = new_fb {
Expand Down Expand Up @@ -1374,6 +1413,19 @@ impl Connector for MetalConnector {
crtc.vrr_enabled.value.set(new_enabled);
self.send_vrr_enabled();
}

fn set_tearing_enabled(&self, enabled: bool) {
if !self.dev.supports_async_commit {
return;
}
if self.tearing_requested.replace(enabled) != enabled {
let msg = match enabled {
true => "Enabling",
false => "Disabling",
};
log::debug!("{msg} tearing on output {}", self.kernel_id());
}
}
}

pub struct MetalCrtc {
Expand Down Expand Up @@ -1524,6 +1576,7 @@ fn create_connector(
next_framebuffer: Default::default(),
direct_scanout_active: Cell::new(false),
next_flip_nsec: Cell::new(0),
tearing_requested: Cell::new(false),
});
let futures = ConnectorFutures {
_present: backend
Expand Down Expand Up @@ -1810,6 +1863,7 @@ impl CollectedProperties {
Some((def, value)) => Ok(MutableProperty {
id: def.id,
value: Cell::new(*value),
pending_value: Cell::new(None),
}),
_ => Err(DrmError::MissingProperty(name.to_string().into_boxed_str())),
}
Expand All @@ -1820,6 +1874,7 @@ impl CollectedProperties {
pub struct MutableProperty<T: Copy> {
pub id: DrmProperty,
pub value: Cell<T>,
pub pending_value: Cell<Option<T>>,
}

impl<T: Copy> MutableProperty<T> {
Expand All @@ -1830,6 +1885,7 @@ impl<T: Copy> MutableProperty<T> {
MutableProperty {
id: self.id,
value: Cell::new(f(self.value.into_inner())),
pending_value: Cell::new(None),
}
}
}
Expand Down Expand Up @@ -1987,6 +2043,7 @@ impl MetalBackend {
disconnect |= !old.is_same_monitor(&dd);
}
if disconnect {
c.tearing_requested.set(false);
if let Some(lease_id) = c.lease.get() {
if let Some(lease) = dev.dev.leases.remove(&lease_id) {
if !lease.try_revoke() {
Expand Down Expand Up @@ -2152,6 +2209,7 @@ impl MetalBackend {
_max_height: resources.max_height,
cursor_width,
cursor_height,
supports_async_commit: master.supports_async_commit(),
gbm,
handle_events: HandleEvents {
handle_events: Cell::new(None),
Expand Down
Loading

0 comments on commit 49f6304

Please sign in to comment.