diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/backend/mod.rs | 7 | ||||
| -rw-r--r-- | src/backend/tty.rs | 88 | ||||
| -rw-r--r-- | src/frame_clock.rs | 4 | ||||
| -rw-r--r-- | src/niri.rs | 53 | ||||
| -rw-r--r-- | src/protocols/output_management.rs | 10 | ||||
| -rw-r--r-- | src/window/mod.rs | 7 |
6 files changed, 119 insertions, 50 deletions
diff --git a/src/backend/mod.rs b/src/backend/mod.rs index c5662184..850f47ef 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -153,6 +153,13 @@ impl Backend { } } + pub fn set_output_on_demand_vrr(&mut self, niri: &mut Niri, output: &Output, enable_vrr: bool) { + match self { + Backend::Tty(tty) => tty.set_output_on_demand_vrr(niri, output, enable_vrr), + Backend::Winit(_) => (), + } + } + pub fn on_output_config_changed(&mut self, niri: &mut Niri) { match self { Backend::Tty(tty) => tty.on_output_config_changed(niri), diff --git a/src/backend/tty.rs b/src/backend/tty.rs index 10becafe..1d891d91 100644 --- a/src/backend/tty.rs +++ b/src/backend/tty.rs @@ -180,6 +180,7 @@ struct TtyOutputState { struct Surface { name: String, compositor: GbmDrmCompositor, + connector: connector::Handle, dmabuf_feedback: Option<SurfaceDmabufFeedback>, gamma_props: Option<GammaProps>, /// Gamma change to apply upon session resume. @@ -426,18 +427,6 @@ impl Tty { } // Restore VRR. - let Some(connector) = - surface.compositor.pending_connectors().into_iter().next() - else { - error!("surface pending connectors is empty"); - continue; - }; - let Some(connector) = device.drm_scanner.connectors().get(&connector) - else { - error!("missing enabled connector in drm_scanner"); - continue; - }; - let output = niri .global_space .outputs() @@ -457,7 +446,7 @@ impl Tty { try_to_change_vrr( &device.drm, - connector, + surface.connector, *crtc, surface, output_state, @@ -832,15 +821,13 @@ impl Tty { let mut vrr_enabled = false; if let Some(capable) = is_vrr_capable(&device.drm, connector.handle()) { if capable { - let word = if config.variable_refresh_rate { - "enabling" - } else { - "disabling" - }; + // Even if on-demand, we still disable it until later checks. + let vrr = config.is_vrr_always_on(); + let word = if vrr { "enabling" } else { "disabling" }; - match set_vrr_enabled(&device.drm, crtc, config.variable_refresh_rate) { + match set_vrr_enabled(&device.drm, crtc, vrr) { Ok(enabled) => { - if enabled != config.variable_refresh_rate { + if enabled != vrr { warn!("failed {} VRR", word); } @@ -851,13 +838,13 @@ impl Tty { } } } else { - if config.variable_refresh_rate { + if !config.is_vrr_always_off() { warn!("cannot enable VRR because connector is not vrr_capable"); } // Try to disable it anyway to work around a bug where resetting DRM state causes // vrr_capable to be reset to 0, potentially leaving VRR_ENABLED at 1. - let res = set_vrr_enabled(&device.drm, crtc, config.variable_refresh_rate); + let res = set_vrr_enabled(&device.drm, crtc, false); if matches!(res, Ok(true)) { warn!("error disabling VRR"); @@ -865,7 +852,7 @@ impl Tty { vrr_enabled = true; } } - } else if config.variable_refresh_rate { + } else if !config.is_vrr_always_off() { warn!("cannot enable VRR because connector is not vrr_capable"); } @@ -1017,6 +1004,7 @@ impl Tty { let surface = Surface { name: output_name.clone(), + connector: connector.handle(), compositor, dmabuf_feedback, gamma_props, @@ -1661,6 +1649,33 @@ impl Tty { } } + pub fn set_output_on_demand_vrr(&mut self, niri: &mut Niri, output: &Output, enable_vrr: bool) { + let _span = tracy_client::span!("Tty::set_output_on_demand_vrr"); + + let output_state = niri.output_state.get_mut(output).unwrap(); + output_state.on_demand_vrr_enabled = enable_vrr; + if output_state.frame_clock.vrr() == enable_vrr { + return; + } + for (&node, device) in self.devices.iter_mut() { + for (&crtc, surface) in device.surfaces.iter_mut() { + let tty_state: &TtyOutputState = output.user_data().get().unwrap(); + if tty_state.node == node && tty_state.crtc == crtc { + try_to_change_vrr( + &device.drm, + surface.connector, + crtc, + surface, + output_state, + enable_vrr, + ); + self.refresh_ipc_outputs(niri); + return; + } + } + } + } + pub fn on_output_config_changed(&mut self, niri: &mut Niri) { let _span = tracy_client::span!("Tty::on_output_config_changed"); @@ -1675,9 +1690,7 @@ impl Tty { let mut to_connect = vec![]; for (&node, device) in &mut self.devices { - for surface in device.surfaces.values_mut() { - let crtc = surface.compositor.crtc(); - + for (&crtc, surface) in device.surfaces.iter_mut() { let config = self .config .borrow() @@ -1691,12 +1704,8 @@ impl Tty { } // Check if we need to change the mode. - let Some(connector) = surface.compositor.pending_connectors().into_iter().next() + let Some(connector) = device.drm_scanner.connectors().get(&surface.connector) else { - error!("surface pending connectors is empty"); - continue; - }; - let Some(connector) = device.drm_scanner.connectors().get(&connector) else { error!("missing enabled connector in drm_scanner"); continue; }; @@ -1707,8 +1716,9 @@ impl Tty { }; let change_mode = surface.compositor.pending_mode() != mode; - let change_vrr = surface.vrr_enabled != config.variable_refresh_rate; - if !change_mode && !change_vrr { + let change_always_vrr = surface.vrr_enabled != config.is_vrr_always_on(); + let is_on_demand_vrr = config.is_vrr_on_demand(); + if !change_mode && !change_always_vrr && !is_on_demand_vrr { continue; } @@ -1729,14 +1739,16 @@ impl Tty { continue; }; - if change_vrr { + if (is_on_demand_vrr && surface.vrr_enabled != output_state.on_demand_vrr_enabled) + || (!is_on_demand_vrr && change_always_vrr) + { try_to_change_vrr( &device.drm, - connector, + connector.handle(), crtc, surface, output_state, - config.variable_refresh_rate, + !surface.vrr_enabled, ); } @@ -2388,7 +2400,7 @@ pub fn set_gamma_for_crtc( fn try_to_change_vrr( device: &DrmDevice, - connector: &connector::Info, + connector: connector::Handle, crtc: crtc::Handle, surface: &mut Surface, output_state: &mut crate::niri::OutputState, @@ -2396,7 +2408,7 @@ fn try_to_change_vrr( ) { let _span = tracy_client::span!("try_to_change_vrr"); - if is_vrr_capable(device, connector.handle()) == Some(true) { + if is_vrr_capable(device, connector) == Some(true) { let word = if enable_vrr { "enabling" } else { "disabling" }; match set_vrr_enabled(device, crtc, enable_vrr) { diff --git a/src/frame_clock.rs b/src/frame_clock.rs index d60867ea..f96475c8 100644 --- a/src/frame_clock.rs +++ b/src/frame_clock.rs @@ -40,6 +40,10 @@ impl FrameClock { self.last_presentation_time = None; } + pub fn vrr(&self) -> bool { + self.vrr + } + pub fn presented(&mut self, presentation_time: Duration) { if presentation_time.is_zero() { // Not interested in these. diff --git a/src/niri.rs b/src/niri.rs index 9aec5ed1..ac07f113 100644 --- a/src/niri.rs +++ b/src/niri.rs @@ -299,6 +299,7 @@ pub struct OutputState { pub global: GlobalId, pub frame_clock: FrameClock, pub redraw_state: RedrawState, + pub on_demand_vrr_enabled: bool, // After the last redraw, some ongoing animations still remain. pub unfinished_animations_remain: bool, /// Last sequence received in a vblank event. @@ -1202,8 +1203,14 @@ impl State { } } } - niri_ipc::OutputAction::Vrr { enable } => { - config.variable_refresh_rate = enable; + niri_ipc::OutputAction::Vrr { vrr } => { + config.variable_refresh_rate = if vrr.vrr { + Some(niri_config::Vrr { + on_demand: vrr.on_demand, + }) + } else { + None + } } } } @@ -2005,6 +2012,7 @@ impl Niri { let state = OutputState { global, redraw_state: RedrawState::Idle, + on_demand_vrr_enabled: false, unfinished_animations_remain: false, frame_clock: FrameClock::new(refresh_interval, vrr), last_drm_sequence: None, @@ -3089,6 +3097,8 @@ impl Niri { lock_state => self.lock_state = lock_state, } + self.refresh_on_demand_vrr(backend, output); + // Send the frame callbacks. // // FIXME: The logic here could be a bit smarter. Currently, during an animation, the @@ -3118,6 +3128,39 @@ impl Niri { }); } + pub fn refresh_on_demand_vrr(&mut self, backend: &mut Backend, output: &Output) { + let _span = tracy_client::span!("Niri::refresh_on_demand_vrr"); + let Some(on_demand) = self + .config + .borrow() + .outputs + .find(&output.name()) + .map(|output| output.is_vrr_on_demand()) + else { + warn!("error getting output config for {}", output.name()); + return; + }; + if !on_demand { + return; + } + + let current = self.layout.windows_for_output(output).any(|mapped| { + mapped.rules().variable_refresh_rate == Some(true) && { + let mut visible = false; + mapped.window.with_surfaces(|surface, states| { + if !visible + && surface_primary_scanout_output(surface, states).as_ref() == Some(output) + { + visible = true; + } + }); + visible + } + }); + + backend.set_output_on_demand_vrr(self, output, current); + } + pub fn update_primary_scanout_output( &self, output: &Output, @@ -3181,13 +3224,9 @@ impl Niri { let offscreen_id = offscreen_id.as_ref(); win.with_surfaces(|surface, states| { - states - .data_map - .insert_if_missing_threadsafe(Mutex::<PrimaryScanoutOutput>::default); let surface_primary_scanout_output = states .data_map - .get::<Mutex<PrimaryScanoutOutput>>() - .unwrap(); + .get_or_insert_threadsafe(Mutex::<PrimaryScanoutOutput>::default); surface_primary_scanout_output .lock() .unwrap() diff --git a/src/protocols/output_management.rs b/src/protocols/output_management.rs index 23e419ba..3839bdb4 100644 --- a/src/protocols/output_management.rs +++ b/src/protocols/output_management.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use std::iter::zip; use std::mem; -use niri_config::FloatOrInt; +use niri_config::{FloatOrInt, Vrr}; use niri_ipc::Transform; use smithay::reexports::wayland_protocols_wlr::output_management::v1::server::{ zwlr_output_configuration_head_v1, zwlr_output_configuration_v1, zwlr_output_head_v1, @@ -693,9 +693,9 @@ where new_config.scale = Some(FloatOrInt(scale)); } zwlr_output_configuration_head_v1::Request::SetAdaptiveSync { state } => { - let enabled = match state { - WEnum::Value(AdaptiveSyncState::Enabled) => true, - WEnum::Value(AdaptiveSyncState::Disabled) => false, + let vrr = match state { + WEnum::Value(AdaptiveSyncState::Enabled) => Some(Vrr { on_demand: false }), + WEnum::Value(AdaptiveSyncState::Disabled) => None, _ => { warn!("SetAdaptativeSync: unknown requested adaptative sync"); conf_head.post_error( @@ -705,7 +705,7 @@ where return; } }; - new_config.variable_refresh_rate = enabled; + new_config.variable_refresh_rate = vrr; } _ => unreachable!(), } diff --git a/src/window/mod.rs b/src/window/mod.rs index 0e0774fd..4d06bb04 100644 --- a/src/window/mod.rs +++ b/src/window/mod.rs @@ -72,6 +72,9 @@ pub struct ResolvedWindowRules { /// Whether to block out this window from certain render targets. pub block_out_from: Option<BlockOutFrom>, + + /// Whether to enable VRR on this window's primary output if it is on-demand. + pub variable_refresh_rate: Option<bool>, } impl<'a> WindowRef<'a> { @@ -132,6 +135,7 @@ impl ResolvedWindowRules { geometry_corner_radius: None, clip_to_geometry: None, block_out_from: None, + variable_refresh_rate: None, } } @@ -231,6 +235,9 @@ impl ResolvedWindowRules { if let Some(x) = rule.block_out_from { resolved.block_out_from = Some(x); } + if let Some(x) = rule.variable_refresh_rate { + resolved.variable_refresh_rate = Some(x); + } } resolved.open_on_output = open_on_output.map(|x| x.to_owned()); |
