kms: Adaptive VRR support
This commit is contained in:
parent
d72bf54628
commit
80965a61b9
7 changed files with 146 additions and 87 deletions
|
|
@ -349,40 +349,6 @@ pub fn calculate_refresh_rate(mode: Mode) -> u32 {
|
||||||
refresh as u32
|
refresh as u32
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn supports_vrr(dev: &impl ControlDevice, conn: connector::Handle) -> Result<bool> {
|
|
||||||
get_property_val(dev, conn, "vrr_capable").map(|(val_type, val)| {
|
|
||||||
match val_type.convert_value(val) {
|
|
||||||
property::Value::UnsignedRange(res) => res == 1,
|
|
||||||
property::Value::Boolean(res) => res,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_vrr(
|
|
||||||
dev: &impl ControlDevice,
|
|
||||||
crtc: crtc::Handle,
|
|
||||||
conn: connector::Handle,
|
|
||||||
vrr: bool,
|
|
||||||
) -> Result<bool> {
|
|
||||||
if supports_vrr(dev, conn)? {
|
|
||||||
dev.set_property(
|
|
||||||
conn,
|
|
||||||
get_prop(dev, crtc, "VRR_ENABLED")?,
|
|
||||||
property::Value::UnsignedRange(if vrr { 1 } else { 0 }).into(),
|
|
||||||
)
|
|
||||||
.map_err(Into::<anyhow::Error>::into)
|
|
||||||
.and_then(|_| get_property_val(dev, crtc, "VRR_ENABLED"))
|
|
||||||
.map(|(val_type, val)| match val_type.convert_value(val) {
|
|
||||||
property::Value::UnsignedRange(vrr) => vrr == 1,
|
|
||||||
property::Value::Boolean(vrr) => vrr,
|
|
||||||
_ => false,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_max_bpc(
|
pub fn get_max_bpc(
|
||||||
dev: &impl ControlDevice,
|
dev: &impl ControlDevice,
|
||||||
conn: connector::Handle,
|
conn: connector::Handle,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,11 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
use crate::{config::OutputState, shell::Shell, state::BackendData, utils::prelude::*};
|
use crate::{
|
||||||
|
config::{AdaptiveSync, OutputState},
|
||||||
|
shell::Shell,
|
||||||
|
state::BackendData,
|
||||||
|
utils::prelude::*,
|
||||||
|
};
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use calloop::LoopSignal;
|
use calloop::LoopSignal;
|
||||||
|
|
@ -663,10 +668,6 @@ impl KmsState {
|
||||||
let gbm = device.gbm.clone();
|
let gbm = device.gbm.clone();
|
||||||
let cursor_size = drm.cursor_size();
|
let cursor_size = drm.cursor_size();
|
||||||
|
|
||||||
let vrr = drm_helpers::set_vrr(drm, *crtc, conn, output_config.vrr)
|
|
||||||
.unwrap_or(false);
|
|
||||||
surface.output.set_adaptive_sync(vrr);
|
|
||||||
|
|
||||||
if let Some(bpc) = output_config.max_bpc {
|
if let Some(bpc) = output_config.max_bpc {
|
||||||
if let Err(err) = drm_helpers::set_max_bpc(drm, conn, bpc) {
|
if let Err(err) = drm_helpers::set_max_bpc(drm, conn, bpc) {
|
||||||
warn!(
|
warn!(
|
||||||
|
|
@ -678,20 +679,35 @@ impl KmsState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let vrr = output_config.vrr;
|
||||||
std::mem::drop(output_config);
|
std::mem::drop(output_config);
|
||||||
surface
|
|
||||||
.resume(drm_surface, gbm, cursor_size, vrr)
|
match surface.resume(drm_surface, gbm, cursor_size) {
|
||||||
.context("Failed to create surface")?;
|
Ok(_) => {
|
||||||
} else {
|
if surface.use_adaptive_sync(vrr)? {
|
||||||
if output_config.vrr != surface.output.adaptive_sync() {
|
surface.output.set_adaptive_sync(vrr);
|
||||||
surface.output.set_adaptive_sync(drm_helpers::set_vrr(
|
} else {
|
||||||
drm,
|
surface.output.config_mut().vrr = AdaptiveSync::Disabled;
|
||||||
surface.crtc,
|
surface.output.set_adaptive_sync(AdaptiveSync::Disabled);
|
||||||
surface.connector,
|
}
|
||||||
output_config.vrr,
|
}
|
||||||
)?);
|
Err(err) => {
|
||||||
|
surface.output.config_mut().enabled = OutputState::Disabled;
|
||||||
|
return Err(err).context("Failed to create surface");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
let vrr = output_config.vrr;
|
||||||
std::mem::drop(output_config);
|
std::mem::drop(output_config);
|
||||||
|
if vrr != surface.output.adaptive_sync() {
|
||||||
|
if surface.use_adaptive_sync(vrr)? {
|
||||||
|
surface.output.set_adaptive_sync(vrr);
|
||||||
|
} else if vrr != AdaptiveSync::Disabled {
|
||||||
|
anyhow::bail!("Requested VRR mode unsupported");
|
||||||
|
} else {
|
||||||
|
surface.output.set_adaptive_sync(AdaptiveSync::Disabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
surface
|
surface
|
||||||
.set_mode(*mode)
|
.set_mode(*mode)
|
||||||
.context("Failed to apply new mode")?;
|
.context("Failed to apply new mode")?;
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ use crate::{
|
||||||
element::{CosmicElement, DamageElement},
|
element::{CosmicElement, DamageElement},
|
||||||
init_shaders, workspace_elements, CursorMode, ElementFilter, GlMultiRenderer, CLEAR_COLOR,
|
init_shaders, workspace_elements, CursorMode, ElementFilter, GlMultiRenderer, CLEAR_COLOR,
|
||||||
},
|
},
|
||||||
|
config::AdaptiveSync,
|
||||||
shell::Shell,
|
shell::Shell,
|
||||||
state::SurfaceDmabufFeedback,
|
state::SurfaceDmabufFeedback,
|
||||||
utils::{prelude::*, quirks::workspace_overview_is_open},
|
utils::{prelude::*, quirks::workspace_overview_is_open},
|
||||||
|
|
@ -27,7 +28,7 @@ use smithay::{
|
||||||
},
|
},
|
||||||
drm::{
|
drm::{
|
||||||
compositor::{BlitFrameResultError, DrmCompositor, FrameError, PrimaryPlaneElement},
|
compositor::{BlitFrameResultError, DrmCompositor, FrameError, PrimaryPlaneElement},
|
||||||
DrmDeviceFd, DrmEventMetadata, DrmEventTime, DrmNode, DrmSurface,
|
DrmDeviceFd, DrmEventMetadata, DrmEventTime, DrmNode, DrmSurface, VrrSupport,
|
||||||
},
|
},
|
||||||
egl::EGLContext,
|
egl::EGLContext,
|
||||||
renderer::{
|
renderer::{
|
||||||
|
|
@ -104,7 +105,7 @@ static NVIDIA_LOGO: &'static [u8] = include_bytes!("../../../../resources/icons/
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Surface {
|
pub struct Surface {
|
||||||
pub(crate) connector: connector::Handle,
|
pub(crate) connector: connector::Handle,
|
||||||
pub(super) crtc: crtc::Handle,
|
pub(super) _crtc: crtc::Handle,
|
||||||
pub(crate) output: Output,
|
pub(crate) output: Output,
|
||||||
known_nodes: HashSet<DrmNode>,
|
known_nodes: HashSet<DrmNode>,
|
||||||
|
|
||||||
|
|
@ -125,6 +126,7 @@ pub struct SurfaceThreadState {
|
||||||
primary_node: DrmNode,
|
primary_node: DrmNode,
|
||||||
target_node: DrmNode,
|
target_node: DrmNode,
|
||||||
active: Arc<AtomicBool>,
|
active: Arc<AtomicBool>,
|
||||||
|
vrr_mode: AdaptiveSync,
|
||||||
compositor: Option<GbmDrmCompositor>,
|
compositor: Option<GbmDrmCompositor>,
|
||||||
|
|
||||||
state: QueueState,
|
state: QueueState,
|
||||||
|
|
@ -225,7 +227,6 @@ pub enum ThreadCommand {
|
||||||
surface: DrmSurface,
|
surface: DrmSurface,
|
||||||
gbm: GbmDevice<DrmDeviceFd>,
|
gbm: GbmDevice<DrmDeviceFd>,
|
||||||
cursor_size: Size<u32, BufferCoords>,
|
cursor_size: Size<u32, BufferCoords>,
|
||||||
vrr: bool,
|
|
||||||
result: SyncSender<Result<()>>,
|
result: SyncSender<Result<()>>,
|
||||||
},
|
},
|
||||||
NodeAdded {
|
NodeAdded {
|
||||||
|
|
@ -240,6 +241,8 @@ pub enum ThreadCommand {
|
||||||
VBlank(Option<DrmEventMetadata>),
|
VBlank(Option<DrmEventMetadata>),
|
||||||
ScheduleRender,
|
ScheduleRender,
|
||||||
SetMode(Mode, SyncSender<Result<()>>),
|
SetMode(Mode, SyncSender<Result<()>>),
|
||||||
|
AdaptiveSyncAvailable(SyncSender<Result<VrrSupport>>),
|
||||||
|
UseAdaptiveSync(AdaptiveSync),
|
||||||
End,
|
End,
|
||||||
DpmsOff,
|
DpmsOff,
|
||||||
}
|
}
|
||||||
|
|
@ -345,7 +348,7 @@ impl Surface {
|
||||||
|
|
||||||
Ok(Surface {
|
Ok(Surface {
|
||||||
connector,
|
connector,
|
||||||
crtc,
|
_crtc: crtc,
|
||||||
output: output.clone(),
|
output: output.clone(),
|
||||||
known_nodes: HashSet::new(),
|
known_nodes: HashSet::new(),
|
||||||
active,
|
active,
|
||||||
|
|
@ -402,6 +405,25 @@ impl Surface {
|
||||||
rx.recv().context("Surface thread died")?
|
rx.recv().context("Surface thread died")?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn use_adaptive_sync(&mut self, vrr: AdaptiveSync) -> Result<bool> {
|
||||||
|
if vrr != AdaptiveSync::Disabled {
|
||||||
|
let (tx, rx) = std::sync::mpsc::sync_channel(1);
|
||||||
|
let _ = self
|
||||||
|
.thread_command
|
||||||
|
.send(ThreadCommand::AdaptiveSyncAvailable(tx));
|
||||||
|
match rx.recv().context("Surface thread died")?? {
|
||||||
|
VrrSupport::RequiresModeset if vrr == AdaptiveSync::Enabled => return Ok(false),
|
||||||
|
VrrSupport::NotSupported => return Ok(false),
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = self
|
||||||
|
.thread_command
|
||||||
|
.send(ThreadCommand::UseAdaptiveSync(vrr));
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn suspend(&mut self) {
|
pub fn suspend(&mut self) {
|
||||||
let _ = self.thread_command.send(ThreadCommand::Suspend);
|
let _ = self.thread_command.send(ThreadCommand::Suspend);
|
||||||
}
|
}
|
||||||
|
|
@ -411,7 +433,6 @@ impl Surface {
|
||||||
surface: DrmSurface,
|
surface: DrmSurface,
|
||||||
gbm: GbmDevice<DrmDeviceFd>,
|
gbm: GbmDevice<DrmDeviceFd>,
|
||||||
cursor_size: Size<u32, BufferCoords>,
|
cursor_size: Size<u32, BufferCoords>,
|
||||||
vrr: bool,
|
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let (tx, rx) = std::sync::mpsc::sync_channel(1);
|
let (tx, rx) = std::sync::mpsc::sync_channel(1);
|
||||||
self.plane_formats = surface
|
self.plane_formats = surface
|
||||||
|
|
@ -432,7 +453,6 @@ impl Surface {
|
||||||
surface,
|
surface,
|
||||||
gbm,
|
gbm,
|
||||||
cursor_size,
|
cursor_size,
|
||||||
vrr,
|
|
||||||
result: tx,
|
result: tx,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -503,6 +523,7 @@ fn surface_thread(
|
||||||
target_node,
|
target_node,
|
||||||
active,
|
active,
|
||||||
compositor: None,
|
compositor: None,
|
||||||
|
vrr_mode: AdaptiveSync::Disabled,
|
||||||
|
|
||||||
state: QueueState::Idle,
|
state: QueueState::Idle,
|
||||||
timings: Timings::new(None, false),
|
timings: Timings::new(None, false),
|
||||||
|
|
@ -529,10 +550,9 @@ fn surface_thread(
|
||||||
surface,
|
surface,
|
||||||
gbm,
|
gbm,
|
||||||
cursor_size,
|
cursor_size,
|
||||||
vrr,
|
|
||||||
result,
|
result,
|
||||||
}) => {
|
}) => {
|
||||||
let _ = result.send(state.resume(surface, gbm, cursor_size, vrr));
|
let _ = result.send(state.resume(surface, gbm, cursor_size));
|
||||||
}
|
}
|
||||||
Event::Msg(ThreadCommand::NodeAdded { node, gbm, egl }) => {
|
Event::Msg(ThreadCommand::NodeAdded { node, gbm, egl }) => {
|
||||||
if let Err(err) = state.node_added(node, gbm, egl) {
|
if let Err(err) = state.node_added(node, gbm, egl) {
|
||||||
|
|
@ -562,6 +582,22 @@ fn surface_thread(
|
||||||
let _ = result.send(Err(anyhow::anyhow!("Set mode with inactive surface")));
|
let _ = result.send(Err(anyhow::anyhow!("Set mode with inactive surface")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Event::Msg(ThreadCommand::AdaptiveSyncAvailable(result)) => {
|
||||||
|
if let Some(compositor) = state.compositor.as_mut() {
|
||||||
|
let _ = result.send(
|
||||||
|
compositor
|
||||||
|
.vrr_supported(
|
||||||
|
compositor.pending_connectors().into_iter().next().unwrap(),
|
||||||
|
)
|
||||||
|
.map_err(Into::into),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let _ = result.send(Err(anyhow::anyhow!("Set vrr with inactive surface")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Event::Msg(ThreadCommand::UseAdaptiveSync(vrr)) => {
|
||||||
|
state.vrr_mode = vrr;
|
||||||
|
}
|
||||||
Event::Msg(ThreadCommand::DpmsOff) => {
|
Event::Msg(ThreadCommand::DpmsOff) => {
|
||||||
if let Some(compositor) = state.compositor.as_mut() {
|
if let Some(compositor) = state.compositor.as_mut() {
|
||||||
if let Err(err) = compositor.clear() {
|
if let Err(err) = compositor.clear() {
|
||||||
|
|
@ -623,7 +659,6 @@ impl SurfaceThreadState {
|
||||||
surface: DrmSurface,
|
surface: DrmSurface,
|
||||||
gbm: GbmDevice<DrmDeviceFd>,
|
gbm: GbmDevice<DrmDeviceFd>,
|
||||||
cursor_size: Size<u32, BufferCoords>,
|
cursor_size: Size<u32, BufferCoords>,
|
||||||
vrr: bool,
|
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let driver = surface.get_driver().ok();
|
let driver = surface.get_driver().ok();
|
||||||
let mut planes = surface.planes().clone();
|
let mut planes = surface.planes().clone();
|
||||||
|
|
@ -649,7 +684,6 @@ impl SurfaceThreadState {
|
||||||
.set_refresh_interval(Some(Duration::from_secs_f64(
|
.set_refresh_interval(Some(Duration::from_secs_f64(
|
||||||
1_000.0 / drm_helpers::calculate_refresh_rate(surface.pending_mode()) as f64,
|
1_000.0 / drm_helpers::calculate_refresh_rate(surface.pending_mode()) as f64,
|
||||||
)));
|
)));
|
||||||
self.timings.set_vrr(vrr);
|
|
||||||
|
|
||||||
match DrmCompositor::new(
|
match DrmCompositor::new(
|
||||||
&self.output,
|
&self.output,
|
||||||
|
|
@ -920,6 +954,11 @@ impl SurfaceThreadState {
|
||||||
|
|
||||||
self.timings.start_render(&self.clock);
|
self.timings.start_render(&self.clock);
|
||||||
|
|
||||||
|
let mut vrr = match self.vrr_mode {
|
||||||
|
AdaptiveSync::Force => true,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
let mut elements = {
|
let mut elements = {
|
||||||
let shell = self.shell.read().unwrap();
|
let shell = self.shell.read().unwrap();
|
||||||
let output = self.mirroring.as_ref().unwrap_or(&self.output);
|
let output = self.mirroring.as_ref().unwrap_or(&self.output);
|
||||||
|
|
@ -929,6 +968,9 @@ impl SurfaceThreadState {
|
||||||
let previous_workspace = previous_workspace
|
let previous_workspace = previous_workspace
|
||||||
.zip(previous_idx)
|
.zip(previous_idx)
|
||||||
.map(|((w, start), idx)| (w.handle, idx, start));
|
.map(|((w, start), idx)| (w.handle, idx, start));
|
||||||
|
if self.vrr_mode == AdaptiveSync::Enabled {
|
||||||
|
vrr = workspace.get_fullscreen().is_some();
|
||||||
|
}
|
||||||
let workspace = (workspace.handle, idx);
|
let workspace = (workspace.handle, idx);
|
||||||
|
|
||||||
std::mem::drop(shell);
|
std::mem::drop(shell);
|
||||||
|
|
@ -958,6 +1000,7 @@ impl SurfaceThreadState {
|
||||||
anyhow::format_err!("Failed to accumulate elements for rendering: {:?}", err)
|
anyhow::format_err!("Failed to accumulate elements for rendering: {:?}", err)
|
||||||
})?
|
})?
|
||||||
};
|
};
|
||||||
|
self.timings.set_vrr(vrr);
|
||||||
self.timings.elements_done(&self.clock);
|
self.timings.elements_done(&self.clock);
|
||||||
|
|
||||||
// we can't use the elements after `compositor.render_frame`,
|
// we can't use the elements after `compositor.render_frame`,
|
||||||
|
|
@ -1113,8 +1156,14 @@ impl SurfaceThreadState {
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
renderer = self.api.single_renderer(&self.target_node).unwrap();
|
renderer = self.api.single_renderer(&self.target_node).unwrap();
|
||||||
|
if let Err(err) = compositor.use_vrr(false) {
|
||||||
|
warn!("Unable to set adaptive VRR state: {}", err);
|
||||||
|
}
|
||||||
compositor.render_frame(&mut renderer, &elements, [0.0, 0.0, 0.0, 1.0])
|
compositor.render_frame(&mut renderer, &elements, [0.0, 0.0, 0.0, 1.0])
|
||||||
} else {
|
} else {
|
||||||
|
if let Err(err) = compositor.use_vrr(vrr) {
|
||||||
|
warn!("Unable to set adaptive VRR state: {}", err);
|
||||||
|
}
|
||||||
compositor.render_frame(
|
compositor.render_frame(
|
||||||
&mut renderer,
|
&mut renderer,
|
||||||
&elements,
|
&elements,
|
||||||
|
|
|
||||||
|
|
@ -98,19 +98,34 @@ pub enum OutputState {
|
||||||
Mirroring(String),
|
Mirroring(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_enabled() -> OutputState {
|
fn default_state() -> OutputState {
|
||||||
OutputState::Enabled
|
OutputState::Enabled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq)]
|
||||||
|
#[serde(rename_all = "lowercase")]
|
||||||
|
pub enum AdaptiveSync {
|
||||||
|
#[serde(rename = "true")]
|
||||||
|
Enabled,
|
||||||
|
#[serde(rename = "false")]
|
||||||
|
Disabled,
|
||||||
|
Force,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_sync() -> AdaptiveSync {
|
||||||
|
AdaptiveSync::Enabled
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
|
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
|
||||||
pub struct OutputConfig {
|
pub struct OutputConfig {
|
||||||
pub mode: ((i32, i32), Option<u32>),
|
pub mode: ((i32, i32), Option<u32>),
|
||||||
pub vrr: bool,
|
#[serde(default = "default_sync")]
|
||||||
|
pub vrr: AdaptiveSync,
|
||||||
pub scale: f64,
|
pub scale: f64,
|
||||||
#[serde(with = "TransformDef")]
|
#[serde(with = "TransformDef")]
|
||||||
pub transform: Transform,
|
pub transform: Transform,
|
||||||
pub position: (u32, u32),
|
pub position: (u32, u32),
|
||||||
#[serde(default = "default_enabled")]
|
#[serde(default = "default_state")]
|
||||||
pub enabled: OutputState,
|
pub enabled: OutputState,
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub max_bpc: Option<u32>,
|
pub max_bpc: Option<u32>,
|
||||||
|
|
@ -120,7 +135,7 @@ impl Default for OutputConfig {
|
||||||
fn default() -> OutputConfig {
|
fn default() -> OutputConfig {
|
||||||
OutputConfig {
|
OutputConfig {
|
||||||
mode: ((0, 0), None),
|
mode: ((0, 0), None),
|
||||||
vrr: false,
|
vrr: AdaptiveSync::Enabled,
|
||||||
scale: 1.0,
|
scale: 1.0,
|
||||||
transform: Transform::Normal,
|
transform: Transform::Normal,
|
||||||
position: (0, 0),
|
position: (0, 0),
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use smithay::{
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use super::geometry::*;
|
pub use super::geometry::*;
|
||||||
use crate::config::{OutputConfig, OutputState};
|
use crate::config::{AdaptiveSync, OutputConfig, OutputState};
|
||||||
pub use crate::shell::{SeatExt, Shell, Workspace};
|
pub use crate::shell::{SeatExt, Shell, Workspace};
|
||||||
pub use crate::state::{Common, State};
|
pub use crate::state::{Common, State};
|
||||||
pub use crate::wayland::handlers::xdg_shell::popup::update_reactive_popups;
|
pub use crate::wayland::handlers::xdg_shell::popup::update_reactive_popups;
|
||||||
|
|
@ -12,15 +12,15 @@ pub use crate::wayland::handlers::xdg_shell::popup::update_reactive_popups;
|
||||||
use std::{
|
use std::{
|
||||||
cell::{Ref, RefCell, RefMut},
|
cell::{Ref, RefCell, RefMut},
|
||||||
sync::{
|
sync::{
|
||||||
atomic::{AtomicBool, Ordering},
|
atomic::{AtomicU8, Ordering},
|
||||||
Mutex,
|
Mutex,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait OutputExt {
|
pub trait OutputExt {
|
||||||
fn geometry(&self) -> Rectangle<i32, Global>;
|
fn geometry(&self) -> Rectangle<i32, Global>;
|
||||||
fn adaptive_sync(&self) -> bool;
|
fn adaptive_sync(&self) -> AdaptiveSync;
|
||||||
fn set_adaptive_sync(&self, vrr: bool);
|
fn set_adaptive_sync(&self, vrr: AdaptiveSync);
|
||||||
fn mirroring(&self) -> Option<Output>;
|
fn mirroring(&self) -> Option<Output>;
|
||||||
fn set_mirroring(&self, output: Option<Output>);
|
fn set_mirroring(&self, output: Option<Output>);
|
||||||
|
|
||||||
|
|
@ -29,7 +29,7 @@ pub trait OutputExt {
|
||||||
fn config_mut(&self) -> RefMut<'_, OutputConfig>;
|
fn config_mut(&self) -> RefMut<'_, OutputConfig>;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Vrr(AtomicBool);
|
struct Vrr(AtomicU8);
|
||||||
|
|
||||||
struct Mirroring(Mutex<Option<WeakOutput>>);
|
struct Mirroring(Mutex<Option<WeakOutput>>);
|
||||||
|
|
||||||
|
|
@ -49,20 +49,27 @@ impl OutputExt for Output {
|
||||||
.as_global()
|
.as_global()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn adaptive_sync(&self) -> bool {
|
fn adaptive_sync(&self) -> AdaptiveSync {
|
||||||
self.user_data()
|
self.user_data()
|
||||||
.get::<Vrr>()
|
.get::<Vrr>()
|
||||||
.map(|vrr| vrr.0.load(Ordering::SeqCst))
|
.map(|vrr| match vrr.0.load(Ordering::SeqCst) {
|
||||||
.unwrap_or(false)
|
2 => AdaptiveSync::Force,
|
||||||
|
1 => AdaptiveSync::Enabled,
|
||||||
|
_ => AdaptiveSync::Disabled,
|
||||||
|
})
|
||||||
|
.unwrap_or(AdaptiveSync::Disabled)
|
||||||
}
|
}
|
||||||
fn set_adaptive_sync(&self, vrr: bool) {
|
fn set_adaptive_sync(&self, vrr: AdaptiveSync) {
|
||||||
let user_data = self.user_data();
|
let user_data = self.user_data();
|
||||||
user_data.insert_if_missing_threadsafe(|| Vrr(AtomicBool::new(false)));
|
user_data.insert_if_missing_threadsafe(|| Vrr(AtomicU8::new(0)));
|
||||||
user_data
|
user_data.get::<Vrr>().unwrap().0.store(
|
||||||
.get::<Vrr>()
|
match vrr {
|
||||||
.unwrap()
|
AdaptiveSync::Disabled => 0,
|
||||||
.0
|
AdaptiveSync::Enabled => 1,
|
||||||
.store(vrr, Ordering::SeqCst);
|
AdaptiveSync::Force => 2,
|
||||||
|
},
|
||||||
|
Ordering::SeqCst,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mirroring(&self) -> Option<Output> {
|
fn mirroring(&self) -> Option<Output> {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use smithay::{output::Output, utils::Point};
|
||||||
use tracing::{error, warn};
|
use tracing::{error, warn};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::{OutputConfig, OutputState},
|
config::{AdaptiveSync, OutputConfig, OutputState},
|
||||||
state::State,
|
state::State,
|
||||||
wayland::protocols::output_configuration::{
|
wayland::protocols::output_configuration::{
|
||||||
delegate_output_configuration, ModeConfiguration, OutputConfiguration,
|
delegate_output_configuration, ModeConfiguration, OutputConfiguration,
|
||||||
|
|
@ -120,7 +120,11 @@ impl State {
|
||||||
current_config.position = (position.x as u32, position.y as u32);
|
current_config.position = (position.x as u32, position.y as u32);
|
||||||
}
|
}
|
||||||
if let Some(vrr) = adaptive_sync {
|
if let Some(vrr) = adaptive_sync {
|
||||||
current_config.vrr = *vrr;
|
current_config.vrr = if *vrr {
|
||||||
|
AdaptiveSync::Force
|
||||||
|
} else {
|
||||||
|
AdaptiveSync::Disabled
|
||||||
|
};
|
||||||
}
|
}
|
||||||
if let Some(mirror) = mirroring {
|
if let Some(mirror) = mirroring {
|
||||||
current_config.enabled = OutputState::Mirroring(mirror.name());
|
current_config.enabled = OutputState::Mirroring(mirror.name());
|
||||||
|
|
|
||||||
|
|
@ -441,11 +441,13 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
if instance.obj.version() >= zwlr_output_head_v1::EVT_ADAPTIVE_SYNC_SINCE {
|
if instance.obj.version() >= zwlr_output_head_v1::EVT_ADAPTIVE_SYNC_SINCE {
|
||||||
instance.obj.adaptive_sync(if output.adaptive_sync() {
|
instance
|
||||||
zwlr_output_head_v1::AdaptiveSyncState::Enabled
|
.obj
|
||||||
} else {
|
.adaptive_sync(if output.adaptive_sync() == AdaptiveSync::Disabled {
|
||||||
zwlr_output_head_v1::AdaptiveSyncState::Disabled
|
zwlr_output_head_v1::AdaptiveSyncState::Disabled
|
||||||
});
|
} else {
|
||||||
|
zwlr_output_head_v1::AdaptiveSyncState::Enabled
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -498,4 +500,4 @@ macro_rules! delegate_output_configuration {
|
||||||
}
|
}
|
||||||
pub(crate) use delegate_output_configuration;
|
pub(crate) use delegate_output_configuration;
|
||||||
|
|
||||||
use crate::utils::prelude::OutputExt;
|
use crate::{config::AdaptiveSync, utils::prelude::OutputExt};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue