Skip to content

Commit

Permalink
kms: Adaptive VRR support
Browse files Browse the repository at this point in the history
  • Loading branch information
Drakulix committed Nov 25, 2024
1 parent 8db6f49 commit d68ea97
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 87 deletions.
34 changes: 0 additions & 34 deletions src/backend/kms/drm_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -349,40 +349,6 @@ pub fn calculate_refresh_rate(mode: Mode) -> 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(
dev: &impl ControlDevice,
conn: connector::Handle,
Expand Down
48 changes: 32 additions & 16 deletions src/backend/kms/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
// 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 calloop::LoopSignal;
Expand Down Expand Up @@ -663,10 +668,6 @@ impl KmsState {
let gbm = device.gbm.clone();
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 Err(err) = drm_helpers::set_max_bpc(drm, conn, bpc) {
warn!(
Expand All @@ -678,20 +679,35 @@ impl KmsState {
}
}

let vrr = output_config.vrr;
std::mem::drop(output_config);
surface
.resume(drm_surface, gbm, cursor_size, vrr)
.context("Failed to create surface")?;
} else {
if output_config.vrr != surface.output.adaptive_sync() {
surface.output.set_adaptive_sync(drm_helpers::set_vrr(
drm,
surface.crtc,
surface.connector,
output_config.vrr,
)?);

match surface.resume(drm_surface, gbm, cursor_size) {
Ok(_) => {
if surface.use_adaptive_sync(vrr)? {
surface.output.set_adaptive_sync(vrr);
} else {
surface.output.config_mut().vrr = AdaptiveSync::Disabled;
surface.output.set_adaptive_sync(AdaptiveSync::Disabled);
}
}
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);
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
.set_mode(*mode)
.context("Failed to apply new mode")?;
Expand Down
69 changes: 59 additions & 10 deletions src/backend/kms/surface/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::{
element::{CosmicElement, DamageElement},
init_shaders, workspace_elements, CursorMode, ElementFilter, GlMultiRenderer, CLEAR_COLOR,
},
config::AdaptiveSync,
shell::Shell,
state::SurfaceDmabufFeedback,
utils::{prelude::*, quirks::workspace_overview_is_open},
Expand All @@ -27,7 +28,7 @@ use smithay::{
},
drm::{
compositor::{BlitFrameResultError, DrmCompositor, FrameError, PrimaryPlaneElement},
DrmDeviceFd, DrmEventMetadata, DrmEventTime, DrmNode, DrmSurface,
DrmDeviceFd, DrmEventMetadata, DrmEventTime, DrmNode, DrmSurface, VrrSupport,
},
egl::EGLContext,
renderer::{
Expand Down Expand Up @@ -104,7 +105,7 @@ static NVIDIA_LOGO: &'static [u8] = include_bytes!("../../../../resources/icons/
#[derive(Debug)]
pub struct Surface {
pub(crate) connector: connector::Handle,
pub(super) crtc: crtc::Handle,
pub(super) _crtc: crtc::Handle,
pub(crate) output: Output,
known_nodes: HashSet<DrmNode>,

Expand All @@ -125,6 +126,7 @@ pub struct SurfaceThreadState {
primary_node: DrmNode,
target_node: DrmNode,
active: Arc<AtomicBool>,
vrr_mode: AdaptiveSync,
compositor: Option<GbmDrmCompositor>,

state: QueueState,
Expand Down Expand Up @@ -225,7 +227,6 @@ pub enum ThreadCommand {
surface: DrmSurface,
gbm: GbmDevice<DrmDeviceFd>,
cursor_size: Size<u32, BufferCoords>,
vrr: bool,
result: SyncSender<Result<()>>,
},
NodeAdded {
Expand All @@ -240,6 +241,8 @@ pub enum ThreadCommand {
VBlank(Option<DrmEventMetadata>),
ScheduleRender,
SetMode(Mode, SyncSender<Result<()>>),
AdaptiveSyncAvailable(SyncSender<Result<VrrSupport>>),
UseAdaptiveSync(AdaptiveSync),
End,
DpmsOff,
}
Expand Down Expand Up @@ -345,7 +348,7 @@ impl Surface {

Ok(Surface {
connector,
crtc,
_crtc: crtc,
output: output.clone(),
known_nodes: HashSet::new(),
active,
Expand Down Expand Up @@ -402,6 +405,25 @@ impl Surface {
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) {
let _ = self.thread_command.send(ThreadCommand::Suspend);
}
Expand All @@ -411,7 +433,6 @@ impl Surface {
surface: DrmSurface,
gbm: GbmDevice<DrmDeviceFd>,
cursor_size: Size<u32, BufferCoords>,
vrr: bool,
) -> Result<()> {
let (tx, rx) = std::sync::mpsc::sync_channel(1);
self.plane_formats = surface
Expand All @@ -432,7 +453,6 @@ impl Surface {
surface,
gbm,
cursor_size,
vrr,
result: tx,
});

Expand Down Expand Up @@ -503,6 +523,7 @@ fn surface_thread(
target_node,
active,
compositor: None,
vrr_mode: AdaptiveSync::Disabled,

state: QueueState::Idle,
timings: Timings::new(None, false),
Expand All @@ -529,10 +550,9 @@ fn surface_thread(
surface,
gbm,
cursor_size,
vrr,
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 }) => {
if let Err(err) = state.node_added(node, gbm, egl) {
Expand Down Expand Up @@ -562,6 +582,22 @@ fn surface_thread(
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) => {
if let Some(compositor) = state.compositor.as_mut() {
if let Err(err) = compositor.clear() {
Expand Down Expand Up @@ -623,7 +659,6 @@ impl SurfaceThreadState {
surface: DrmSurface,
gbm: GbmDevice<DrmDeviceFd>,
cursor_size: Size<u32, BufferCoords>,
vrr: bool,
) -> Result<()> {
let driver = surface.get_driver().ok();
let mut planes = surface.planes().clone();
Expand All @@ -649,7 +684,6 @@ impl SurfaceThreadState {
.set_refresh_interval(Some(Duration::from_secs_f64(
1_000.0 / drm_helpers::calculate_refresh_rate(surface.pending_mode()) as f64,
)));
self.timings.set_vrr(vrr);

match DrmCompositor::new(
&self.output,
Expand Down Expand Up @@ -920,6 +954,11 @@ impl SurfaceThreadState {

self.timings.start_render(&self.clock);

let mut vrr = match self.vrr_mode {
AdaptiveSync::Force => true,
_ => false,
};

let mut elements = {
let shell = self.shell.read().unwrap();
let output = self.mirroring.as_ref().unwrap_or(&self.output);
Expand All @@ -929,6 +968,9 @@ impl SurfaceThreadState {
let previous_workspace = previous_workspace
.zip(previous_idx)
.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);

std::mem::drop(shell);
Expand Down Expand Up @@ -958,6 +1000,7 @@ impl SurfaceThreadState {
anyhow::format_err!("Failed to accumulate elements for rendering: {:?}", err)
})?
};
self.timings.set_vrr(vrr);
self.timings.elements_done(&self.clock);

// we can't use the elements after `compositor.render_frame`,
Expand Down Expand Up @@ -1113,8 +1156,14 @@ impl SurfaceThreadState {
.collect::<Vec<_>>();

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])
} else {
if let Err(err) = compositor.use_vrr(vrr) {
warn!("Unable to set adaptive VRR state: {}", err);
}
compositor.render_frame(
&mut renderer,
&elements,
Expand Down
23 changes: 19 additions & 4 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,19 +98,34 @@ pub enum OutputState {
Mirroring(String),
}

fn default_enabled() -> OutputState {
fn default_state() -> OutputState {
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)]
pub struct OutputConfig {
pub mode: ((i32, i32), Option<u32>),
pub vrr: bool,
#[serde(default = "default_sync")]
pub vrr: AdaptiveSync,
pub scale: f64,
#[serde(with = "TransformDef")]
pub transform: Transform,
pub position: (u32, u32),
#[serde(default = "default_enabled")]
#[serde(default = "default_state")]
pub enabled: OutputState,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_bpc: Option<u32>,
Expand All @@ -120,7 +135,7 @@ impl Default for OutputConfig {
fn default() -> OutputConfig {
OutputConfig {
mode: ((0, 0), None),
vrr: false,
vrr: AdaptiveSync::Enabled,
scale: 1.0,
transform: Transform::Normal,
position: (0, 0),
Expand Down
Loading

0 comments on commit d68ea97

Please sign in to comment.