metal: implement tearing
This commit is contained in:
parent
d355059ad9
commit
49f6304716
31 changed files with 726 additions and 51 deletions
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
@ -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>>,
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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(());
|
||||
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
|
@ -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();
|
||||
|
|
@ -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
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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
|
||||
|
|
@ -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())),
|
||||
}
|
||||
|
|
@ -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> {
|
||||
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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() {
|
||||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use {
|
|||
},
|
||||
clap::{Args, Subcommand, ValueEnum},
|
||||
isnt::std_1::vec::IsntVecExt,
|
||||
jay_config::video::{Transform, VrrMode},
|
||||
jay_config::video::{TearingMode, Transform, VrrMode},
|
||||
std::{
|
||||
cell::RefCell,
|
||||
fmt::{Display, Formatter},
|
||||
|
|
@ -120,6 +120,8 @@ pub enum OutputCommand {
|
|||
NonDesktop(NonDesktopArgs),
|
||||
/// Change VRR settings.
|
||||
Vrr(VrrArgs),
|
||||
/// Change tearing settings.
|
||||
Tearing(TearingArgs),
|
||||
}
|
||||
|
||||
#[derive(ValueEnum, Debug, Clone)]
|
||||
|
|
@ -144,13 +146,13 @@ pub struct VrrArgs {
|
|||
#[derive(Subcommand, Debug, Clone)]
|
||||
pub enum VrrCommand {
|
||||
/// Sets the mode that determines when VRR is enabled.
|
||||
SetMode(SetModeArgs),
|
||||
SetMode(SetVrrModeArgs),
|
||||
/// Sets the maximum refresh rate of the cursor.
|
||||
SetCursorHz(CursorHzArgs),
|
||||
}
|
||||
|
||||
#[derive(Args, Debug, Clone)]
|
||||
pub struct SetModeArgs {
|
||||
pub struct SetVrrModeArgs {
|
||||
#[clap(value_enum)]
|
||||
pub mode: VrrModeArg,
|
||||
}
|
||||
|
|
@ -175,6 +177,41 @@ pub struct CursorHzArgs {
|
|||
pub rate: String,
|
||||
}
|
||||
|
||||
#[derive(Args, Debug, Clone)]
|
||||
pub struct TearingArgs {
|
||||
#[clap(subcommand)]
|
||||
pub command: TearingCommand,
|
||||
}
|
||||
|
||||
#[derive(Subcommand, Debug, Clone)]
|
||||
pub enum TearingCommand {
|
||||
/// Sets the mode that determines when tearing is enabled.
|
||||
SetMode(SetTearingModeArgs),
|
||||
}
|
||||
|
||||
#[derive(Args, Debug, Clone)]
|
||||
pub struct SetTearingModeArgs {
|
||||
#[clap(value_enum)]
|
||||
pub mode: TearingModeArg,
|
||||
}
|
||||
|
||||
#[derive(ValueEnum, Debug, Copy, Clone, Hash, PartialEq)]
|
||||
pub enum TearingModeArg {
|
||||
/// Tearing is never enabled.
|
||||
Never,
|
||||
/// Tearing is always enabled.
|
||||
Always,
|
||||
/// Tearing is enabled when one or more applications are displayed fullscreen.
|
||||
Variant1,
|
||||
/// Tearing is enabled when a single application is displayed fullscreen.
|
||||
Variant2,
|
||||
/// Tearing is enabled when a single application is displayed fullscreen and the
|
||||
/// application has requested tearing.
|
||||
///
|
||||
/// This is the default.
|
||||
Variant3,
|
||||
}
|
||||
|
||||
#[derive(Args, Debug, Clone)]
|
||||
pub struct PositionArgs {
|
||||
/// The top-left x coordinate.
|
||||
|
|
@ -280,6 +317,7 @@ struct Output {
|
|||
pub vrr_enabled: bool,
|
||||
pub vrr_mode: VrrMode,
|
||||
pub vrr_cursor_hz: Option<f64>,
|
||||
pub tearing_mode: TearingMode,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
|
@ -487,6 +525,27 @@ impl Randr {
|
|||
}
|
||||
}
|
||||
}
|
||||
OutputCommand::Tearing(a) => {
|
||||
self.handle_error(randr, move |msg| {
|
||||
eprintln!("Could not change the tearing setting: {}", msg);
|
||||
});
|
||||
match a.command {
|
||||
TearingCommand::SetMode(a) => {
|
||||
let mode = match a.mode {
|
||||
TearingModeArg::Never => VrrMode::NEVER,
|
||||
TearingModeArg::Always => VrrMode::ALWAYS,
|
||||
TearingModeArg::Variant1 => VrrMode::VARIANT_1,
|
||||
TearingModeArg::Variant2 => VrrMode::VARIANT_2,
|
||||
TearingModeArg::Variant3 => VrrMode::VARIANT_3,
|
||||
};
|
||||
tc.send(jay_randr::SetTearingMode {
|
||||
self_id: randr,
|
||||
output: &args.output,
|
||||
mode: mode.0,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
tc.round_trip().await;
|
||||
}
|
||||
|
|
@ -621,6 +680,21 @@ impl Randr {
|
|||
println!(" VRR cursor hz: {}", hz);
|
||||
}
|
||||
}
|
||||
{
|
||||
let mode_str;
|
||||
let mode = match o.tearing_mode {
|
||||
TearingMode::NEVER => "never",
|
||||
TearingMode::ALWAYS => "always",
|
||||
TearingMode::VARIANT_1 => "variant1",
|
||||
TearingMode::VARIANT_2 => "variant2",
|
||||
TearingMode::VARIANT_3 => "variant3",
|
||||
_ => {
|
||||
mode_str = format!("unknown ({})", o.vrr_mode.0);
|
||||
&mode_str
|
||||
}
|
||||
};
|
||||
println!(" Tearing mode: {}", mode);
|
||||
}
|
||||
println!(" position: {} x {}", o.x, o.y);
|
||||
println!(" logical size: {} x {}", o.width, o.height);
|
||||
if let Some(mode) = &o.current_mode {
|
||||
|
|
@ -713,6 +787,7 @@ impl Randr {
|
|||
vrr_enabled: false,
|
||||
vrr_mode: VrrMode::NEVER,
|
||||
vrr_cursor_hz: None,
|
||||
tearing_mode: TearingMode::NEVER,
|
||||
});
|
||||
});
|
||||
jay_randr::NonDesktopOutput::handle(tc, randr, data.clone(), |data, msg| {
|
||||
|
|
@ -737,6 +812,7 @@ impl Randr {
|
|||
vrr_enabled: false,
|
||||
vrr_mode: VrrMode::NEVER,
|
||||
vrr_cursor_hz: None,
|
||||
tearing_mode: TearingMode::NEVER,
|
||||
});
|
||||
});
|
||||
jay_randr::VrrState::handle(tc, randr, data.clone(), |data, msg| {
|
||||
|
|
@ -753,6 +829,12 @@ impl Randr {
|
|||
let output = c.output.as_mut().unwrap();
|
||||
output.vrr_cursor_hz = Some(msg.hz);
|
||||
});
|
||||
jay_randr::TearingState::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.tearing_mode = TearingMode(msg.mode);
|
||||
});
|
||||
jay_randr::Mode::handle(tc, randr, data.clone(), |data, msg| {
|
||||
let mut data = data.borrow_mut();
|
||||
let c = data.connectors.last_mut().unwrap();
|
||||
|
|
|
|||
|
|
@ -33,7 +33,8 @@ use {
|
|||
tasks::{self, idle},
|
||||
tree::{
|
||||
container_layout, container_render_data, float_layout, float_titles,
|
||||
output_render_data, DisplayNode, NodeIds, OutputNode, VrrMode, WorkspaceNode,
|
||||
output_render_data, DisplayNode, NodeIds, OutputNode, TearingMode, VrrMode,
|
||||
WorkspaceNode,
|
||||
},
|
||||
user_session::import_environment,
|
||||
utils::{
|
||||
|
|
@ -249,6 +250,7 @@ fn start_compositor2(
|
|||
damage_visualizer: DamageVisualizer::new(&engine),
|
||||
default_vrr_mode: Cell::new(VrrMode::NEVER),
|
||||
default_vrr_cursor_hz: Cell::new(None),
|
||||
default_tearing_mode: Cell::new(TearingMode::VARIANT_3),
|
||||
});
|
||||
state.tracker.register(ClientId::from_raw(0));
|
||||
create_dummy_output(&state);
|
||||
|
|
@ -425,6 +427,7 @@ fn create_dummy_output(state: &Rc<State>) {
|
|||
pos: Default::default(),
|
||||
vrr_mode: Cell::new(VrrMode::NEVER),
|
||||
vrr_cursor_hz: Default::default(),
|
||||
tearing_mode: Cell::new(&TearingMode::Never),
|
||||
});
|
||||
let connector = Rc::new(DummyOutput {
|
||||
id: state.connector_ids.next(),
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ use {
|
|||
theme::{Color, ThemeSized, DEFAULT_FONT},
|
||||
tree::{
|
||||
move_ws_to_output, ContainerNode, ContainerSplit, FloatNode, Node, NodeVisitorBase,
|
||||
OutputNode, VrrMode, WsMoveConfig,
|
||||
OutputNode, TearingMode, VrrMode, WsMoveConfig,
|
||||
},
|
||||
utils::{
|
||||
asyncevent::AsyncEvent,
|
||||
|
|
@ -48,7 +48,10 @@ use {
|
|||
logging::LogLevel,
|
||||
theme::{colors::Colorable, sized::Resizable},
|
||||
timer::Timer as JayTimer,
|
||||
video::{Connector, DrmDevice, GfxApi, Transform, VrrMode as ConfigVrrMode},
|
||||
video::{
|
||||
Connector, DrmDevice, GfxApi, TearingMode as ConfigTearingMode, Transform,
|
||||
VrrMode as ConfigVrrMode,
|
||||
},
|
||||
Axis, Direction, Workspace,
|
||||
},
|
||||
libloading::Library,
|
||||
|
|
@ -1045,7 +1048,7 @@ impl ConfigProxyHandler {
|
|||
Some(c) => {
|
||||
let connector = self.get_output_node(c)?;
|
||||
connector.global.persistent.vrr_mode.set(mode);
|
||||
connector.update_vrr_state();
|
||||
connector.update_presentation_type();
|
||||
}
|
||||
_ => self.state.default_vrr_mode.set(mode),
|
||||
}
|
||||
|
|
@ -1072,6 +1075,25 @@ impl ConfigProxyHandler {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_set_tearing_mode(
|
||||
&self,
|
||||
connector: Option<Connector>,
|
||||
mode: ConfigTearingMode,
|
||||
) -> Result<(), CphError> {
|
||||
let Some(mode) = TearingMode::from_config(mode) else {
|
||||
return Err(CphError::UnknownTearingMode(mode));
|
||||
};
|
||||
match connector {
|
||||
Some(c) => {
|
||||
let connector = self.get_output_node(c)?;
|
||||
connector.global.persistent.tearing_mode.set(mode);
|
||||
connector.update_presentation_type();
|
||||
}
|
||||
_ => self.state.default_tearing_mode.set(mode),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_connector_set_transform(
|
||||
&self,
|
||||
connector: Connector,
|
||||
|
|
@ -1872,6 +1894,9 @@ impl ConfigProxyHandler {
|
|||
ClientMessage::SetVrrCursorHz { connector, hz } => self
|
||||
.handle_set_vrr_cursor_hz(connector, hz)
|
||||
.wrn("set_vrr_cursor_hz")?,
|
||||
ClientMessage::SetTearingMode { connector, mode } => self
|
||||
.handle_set_tearing_mode(connector, mode)
|
||||
.wrn("set_tearing_mode")?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -1937,6 +1962,8 @@ enum CphError {
|
|||
UnknownVrrMode(ConfigVrrMode),
|
||||
#[error("Invalid cursor hz {0}")]
|
||||
InvalidCursorHz(f64),
|
||||
#[error("Unknown tearing mode {0:?}")]
|
||||
UnknownTearingMode(ConfigTearingMode),
|
||||
}
|
||||
|
||||
trait WithRequestName {
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ impl Global for JayCompositorGlobal {
|
|||
}
|
||||
|
||||
fn version(&self) -> u32 {
|
||||
2
|
||||
3
|
||||
}
|
||||
|
||||
fn required_caps(&self) -> ClientCaps {
|
||||
|
|
|
|||
|
|
@ -7,11 +7,13 @@ use {
|
|||
object::{Object, Version},
|
||||
scale::Scale,
|
||||
state::{ConnectorData, DrmDevData, OutputData},
|
||||
tree::{OutputNode, VrrMode},
|
||||
tree::{OutputNode, TearingMode, VrrMode},
|
||||
utils::{gfx_api_ext::GfxApiExt, transform_ext::TransformExt},
|
||||
wire::{jay_randr::*, JayRandrId},
|
||||
},
|
||||
jay_config::video::{GfxApi, Transform, VrrMode as ConfigVrrMode},
|
||||
jay_config::video::{
|
||||
GfxApi, TearingMode as ConfigTearingMode, Transform, VrrMode as ConfigVrrMode,
|
||||
},
|
||||
std::rc::Rc,
|
||||
thiserror::Error,
|
||||
};
|
||||
|
|
@ -24,6 +26,7 @@ pub struct JayRandr {
|
|||
}
|
||||
|
||||
const VRR_CAPABLE_SINCE: Version = Version(2);
|
||||
const TEARING_SINCE: Version = Version(3);
|
||||
|
||||
impl JayRandr {
|
||||
pub fn new(id: JayRandrId, client: &Rc<Client>, version: Version) -> Self {
|
||||
|
|
@ -116,6 +119,12 @@ impl JayRandr {
|
|||
});
|
||||
}
|
||||
}
|
||||
if self.version >= TEARING_SINCE {
|
||||
self.client.event(TearingState {
|
||||
self_id: self.id,
|
||||
mode: node.global.persistent.tearing_mode.get().to_config().0,
|
||||
});
|
||||
}
|
||||
let current_mode = global.mode.get();
|
||||
for mode in &global.modes {
|
||||
self.client.event(Mode {
|
||||
|
|
@ -325,7 +334,7 @@ impl JayRandrRequestHandler for JayRandr {
|
|||
return Ok(());
|
||||
};
|
||||
c.global.persistent.vrr_mode.set(mode);
|
||||
c.update_vrr_state();
|
||||
c.update_presentation_type();
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
|
@ -340,6 +349,22 @@ impl JayRandrRequestHandler for JayRandr {
|
|||
c.schedule.set_cursor_hz(req.hz);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_tearing_mode(
|
||||
&self,
|
||||
req: SetTearingMode<'_>,
|
||||
_slf: &Rc<Self>,
|
||||
) -> Result<(), Self::Error> {
|
||||
let Some(mode) = TearingMode::from_config(ConfigTearingMode(req.mode)) else {
|
||||
return Err(JayRandrError::UnknownTearingMode(req.mode));
|
||||
};
|
||||
let Some(c) = self.get_output_node(req.output) else {
|
||||
return Ok(());
|
||||
};
|
||||
c.global.persistent.tearing_mode.set(mode);
|
||||
c.update_presentation_type();
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
|
|
@ -357,5 +382,7 @@ pub enum JayRandrError {
|
|||
ClientError(Box<ClientError>),
|
||||
#[error("Unknown VRR mode {0}")]
|
||||
UnknownVrrMode(u32),
|
||||
#[error("Unknown tearing mode {0}")]
|
||||
UnknownTearingMode(u32),
|
||||
}
|
||||
efrom!(JayRandrError, ClientError);
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use {
|
|||
object::{Object, Version},
|
||||
rect::Rect,
|
||||
state::{ConnectorData, State},
|
||||
tree::{calculate_logical_size, OutputNode, VrrMode},
|
||||
tree::{calculate_logical_size, OutputNode, TearingMode, VrrMode},
|
||||
utils::{clonecell::CloneCell, copyhashmap::CopyHashMap, transform_ext::TransformExt},
|
||||
wire::{wl_output::*, WlOutputId, ZxdgOutputV1Id},
|
||||
},
|
||||
|
|
@ -93,6 +93,7 @@ pub struct PersistentOutputState {
|
|||
pub pos: Cell<(i32, i32)>,
|
||||
pub vrr_mode: Cell<&'static VrrMode>,
|
||||
pub vrr_cursor_hz: Cell<Option<f64>>,
|
||||
pub tearing_mode: Cell<&'static TearingMode>,
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Hash)]
|
||||
|
|
|
|||
|
|
@ -287,7 +287,7 @@ pub struct WlSurface {
|
|||
pub constraints: SmallMap<SeatId, Rc<SeatConstraint>, 1>,
|
||||
xwayland_serial: Cell<Option<u64>>,
|
||||
tearing_control: CloneCell<Option<Rc<WpTearingControlV1>>>,
|
||||
tearing: Cell<bool>,
|
||||
pub tearing: Cell<bool>,
|
||||
version: Version,
|
||||
pub has_content_type_manager: Cell<bool>,
|
||||
pub content_type: Cell<Option<ContentType>>,
|
||||
|
|
@ -1235,8 +1235,11 @@ impl WlSurface {
|
|||
self.opaque_region.set(region);
|
||||
}
|
||||
}
|
||||
let mut tearing_changed = false;
|
||||
if let Some(tearing) = pending.tearing.take() {
|
||||
self.tearing.set(tearing);
|
||||
if self.tearing.replace(tearing) != tearing {
|
||||
tearing_changed = true;
|
||||
}
|
||||
}
|
||||
if let Some(content_type) = pending.content_type.take() {
|
||||
self.content_type.set(content_type);
|
||||
|
|
@ -1305,6 +1308,13 @@ impl WlSurface {
|
|||
pending.buffer_damage.clear();
|
||||
pending.surface_damage.clear();
|
||||
pending.damage_full = false;
|
||||
if tearing_changed {
|
||||
if let Some(tl) = self.toplevel.get() {
|
||||
if tl.tl_data().is_fullscreen.get() {
|
||||
self.output.get().update_presentation_type();
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -64,8 +64,8 @@ use {
|
|||
time::Time,
|
||||
tree::{
|
||||
ContainerNode, ContainerSplit, Direction, DisplayNode, FloatNode, Node, NodeIds,
|
||||
NodeVisitorBase, OutputNode, PlaceholderNode, ToplevelNode, ToplevelNodeBase, VrrMode,
|
||||
WorkspaceNode,
|
||||
NodeVisitorBase, OutputNode, PlaceholderNode, TearingMode, ToplevelNode,
|
||||
ToplevelNodeBase, VrrMode, WorkspaceNode,
|
||||
},
|
||||
utils::{
|
||||
activation_token::ActivationToken, asyncevent::AsyncEvent, bindings::Bindings,
|
||||
|
|
@ -203,6 +203,7 @@ pub struct State {
|
|||
pub damage_visualizer: DamageVisualizer,
|
||||
pub default_vrr_mode: Cell<&'static VrrMode>,
|
||||
pub default_vrr_cursor_hz: Cell<Option<f64>>,
|
||||
pub default_tearing_mode: Cell<&'static TearingMode>,
|
||||
}
|
||||
|
||||
// impl Drop for State {
|
||||
|
|
|
|||
|
|
@ -125,6 +125,7 @@ impl ConnectorHandler {
|
|||
pos: Cell::new((x1, 0)),
|
||||
vrr_mode: Cell::new(self.state.default_vrr_mode.get()),
|
||||
vrr_cursor_hz: Cell::new(self.state.default_vrr_cursor_hz.get()),
|
||||
tearing_mode: Cell::new(self.state.default_tearing_mode.get()),
|
||||
});
|
||||
self.state
|
||||
.persistent_output_states
|
||||
|
|
@ -242,7 +243,7 @@ impl ConnectorHandler {
|
|||
}
|
||||
self.state.add_global(&global);
|
||||
self.state.tree_changed();
|
||||
on.update_vrr_state();
|
||||
on.update_presentation_type();
|
||||
'outer: loop {
|
||||
while let Some(event) = self.data.connector.event() {
|
||||
match event {
|
||||
|
|
|
|||
|
|
@ -330,7 +330,7 @@ impl ToolClient {
|
|||
self_id: s.registry,
|
||||
name: s.jay_compositor.0,
|
||||
interface: JayCompositor.name(),
|
||||
version: s.jay_compositor.1.min(2),
|
||||
version: s.jay_compositor.1.min(3),
|
||||
id: id.into(),
|
||||
});
|
||||
self.jay_compositor.set(Some(id));
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ use {
|
|||
wire::{JayOutputId, JayScreencastId, ZwlrScreencopyFrameV1Id},
|
||||
},
|
||||
ahash::AHashMap,
|
||||
jay_config::video::{Transform, VrrMode as ConfigVrrMode},
|
||||
jay_config::video::{TearingMode as ConfigTearingMode, Transform, VrrMode as ConfigVrrMode},
|
||||
smallvec::SmallVec,
|
||||
std::{
|
||||
cell::{Cell, RefCell},
|
||||
|
|
@ -789,7 +789,12 @@ impl OutputNode {
|
|||
self.state.tree_changed();
|
||||
}
|
||||
|
||||
pub fn update_vrr_state(&self) {
|
||||
pub fn update_presentation_type(&self) {
|
||||
self.update_vrr_state();
|
||||
self.update_tearing();
|
||||
}
|
||||
|
||||
fn update_vrr_state(&self) {
|
||||
let enabled = match self.global.persistent.vrr_mode.get() {
|
||||
VrrMode::Never => false,
|
||||
VrrMode::Always => true,
|
||||
|
|
@ -821,6 +826,33 @@ impl OutputNode {
|
|||
};
|
||||
self.global.connector.connector.set_vrr_enabled(enabled);
|
||||
}
|
||||
|
||||
fn update_tearing(&self) {
|
||||
let enabled = match self.global.persistent.tearing_mode.get() {
|
||||
TearingMode::Never => false,
|
||||
TearingMode::Always => true,
|
||||
TearingMode::Fullscreen { surface } => 'get: {
|
||||
let Some(ws) = self.workspace.get() else {
|
||||
break 'get false;
|
||||
};
|
||||
let Some(tl) = ws.fullscreen.get() else {
|
||||
break 'get false;
|
||||
};
|
||||
if let Some(req) = surface {
|
||||
let Some(surface) = tl.tl_scanout_surface() else {
|
||||
break 'get false;
|
||||
};
|
||||
if req.tearing_requested {
|
||||
if !surface.tearing.get() {
|
||||
break 'get false;
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
};
|
||||
self.global.connector.connector.set_tearing_enabled(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct OutputTitle {
|
||||
|
|
@ -1185,3 +1217,55 @@ impl VrrMode {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum TearingMode {
|
||||
Never,
|
||||
Always,
|
||||
Fullscreen {
|
||||
surface: Option<TearingSurfaceRequirements>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct TearingSurfaceRequirements {
|
||||
tearing_requested: bool,
|
||||
}
|
||||
|
||||
impl TearingMode {
|
||||
pub const NEVER: &'static Self = &Self::Never;
|
||||
pub const ALWAYS: &'static Self = &Self::Always;
|
||||
pub const VARIANT_1: &'static Self = &Self::Fullscreen { surface: None };
|
||||
pub const VARIANT_2: &'static Self = &Self::Fullscreen {
|
||||
surface: Some(TearingSurfaceRequirements {
|
||||
tearing_requested: false,
|
||||
}),
|
||||
};
|
||||
pub const VARIANT_3: &'static Self = &Self::Fullscreen {
|
||||
surface: Some(TearingSurfaceRequirements {
|
||||
tearing_requested: true,
|
||||
}),
|
||||
};
|
||||
|
||||
pub fn from_config(mode: ConfigTearingMode) -> Option<&'static Self> {
|
||||
let res = match mode {
|
||||
ConfigTearingMode::NEVER => Self::NEVER,
|
||||
ConfigTearingMode::ALWAYS => Self::ALWAYS,
|
||||
ConfigTearingMode::VARIANT_1 => Self::VARIANT_1,
|
||||
ConfigTearingMode::VARIANT_2 => Self::VARIANT_2,
|
||||
ConfigTearingMode::VARIANT_3 => Self::VARIANT_3,
|
||||
_ => return None,
|
||||
};
|
||||
Some(res)
|
||||
}
|
||||
|
||||
pub fn to_config(&self) -> ConfigVrrMode {
|
||||
match self {
|
||||
Self::NEVER => ConfigVrrMode::NEVER,
|
||||
Self::ALWAYS => ConfigVrrMode::ALWAYS,
|
||||
Self::VARIANT_1 => ConfigVrrMode::VARIANT_1,
|
||||
Self::VARIANT_2 => ConfigVrrMode::VARIANT_2,
|
||||
Self::VARIANT_3 => ConfigVrrMode::VARIANT_3,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -181,7 +181,7 @@ impl WorkspaceNode {
|
|||
surface.send_feedback(&fb);
|
||||
}
|
||||
}
|
||||
self.output.get().update_vrr_state();
|
||||
self.output.get().update_presentation_type();
|
||||
}
|
||||
|
||||
pub fn remove_fullscreen_node(&self) {
|
||||
|
|
@ -195,7 +195,7 @@ impl WorkspaceNode {
|
|||
surface.send_feedback(&fb);
|
||||
}
|
||||
}
|
||||
self.output.get().update_vrr_state();
|
||||
self.output.get().update_presentation_type();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -41,14 +41,15 @@ use crate::{
|
|||
dmabuf::DmaBuf,
|
||||
drm::sys::{
|
||||
auth_magic, drm_format_modifier, drm_format_modifier_blob, drop_master, get_version,
|
||||
revoke_lease, DRM_CAP_CURSOR_HEIGHT, DRM_CAP_CURSOR_WIDTH, FORMAT_BLOB_CURRENT,
|
||||
revoke_lease, DRM_CAP_ATOMIC_ASYNC_PAGE_FLIP, DRM_CAP_CURSOR_HEIGHT,
|
||||
DRM_CAP_CURSOR_WIDTH, FORMAT_BLOB_CURRENT,
|
||||
},
|
||||
Modifier, INVALID_MODIFIER,
|
||||
},
|
||||
};
|
||||
pub use sys::{
|
||||
drm_mode_modeinfo, DRM_CLIENT_CAP_ATOMIC, DRM_MODE_ATOMIC_ALLOW_MODESET,
|
||||
DRM_MODE_ATOMIC_NONBLOCK, DRM_MODE_PAGE_FLIP_EVENT,
|
||||
DRM_MODE_ATOMIC_NONBLOCK, DRM_MODE_PAGE_FLIP_ASYNC, DRM_MODE_PAGE_FLIP_EVENT,
|
||||
};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
|
|
@ -339,6 +340,10 @@ impl DrmMaster {
|
|||
Ok((width, height))
|
||||
}
|
||||
|
||||
pub fn supports_async_commit(&self) -> bool {
|
||||
self.get_cap(DRM_CAP_ATOMIC_ASYNC_PAGE_FLIP) == Ok(1)
|
||||
}
|
||||
|
||||
pub fn get_connector_info(
|
||||
&self,
|
||||
connector: DrmConnector,
|
||||
|
|
|
|||
|
|
@ -238,6 +238,7 @@ const DRM_MODE_PROP_ATOMIC: u32 = 0x80000000;
|
|||
|
||||
pub const DRM_CAP_CURSOR_WIDTH: u64 = 0x8;
|
||||
pub const DRM_CAP_CURSOR_HEIGHT: u64 = 0x9;
|
||||
pub const DRM_CAP_ATOMIC_ASYNC_PAGE_FLIP: u64 = 0x15;
|
||||
|
||||
#[repr(C)]
|
||||
struct drm_mode_property_enum {
|
||||
|
|
@ -865,6 +866,7 @@ struct drm_mode_atomic {
|
|||
const DRM_IOCTL_MODE_ATOMIC: u64 = drm_iowr::<drm_mode_atomic>(0xbc);
|
||||
|
||||
pub const DRM_MODE_PAGE_FLIP_EVENT: u32 = 0x01;
|
||||
pub const DRM_MODE_PAGE_FLIP_ASYNC: u32 = 0x02;
|
||||
pub const DRM_MODE_ATOMIC_TEST_ONLY: u32 = 0x0100;
|
||||
pub const DRM_MODE_ATOMIC_NONBLOCK: u32 = 0x0200;
|
||||
pub const DRM_MODE_ATOMIC_ALLOW_MODESET: u32 = 0x0400;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue