tty: Add support for disabling DRM devices

This commit is contained in:
Shaun Ren
2025-08-30 20:42:46 -04:00
committed by Ivan Molodetskikh
parent b7f1e382a2
commit 5c91e3191d
5 changed files with 109 additions and 4 deletions

View File

@@ -18,6 +18,8 @@ debug {
disable-direct-scanout
restrict-primary-scanout-to-matching-format
render-drm-device "/dev/dri/renderD129"
ignore-drm-device "/dev/dri/renderD128"
ignore-drm-device "/dev/dri/renderD130"
force-pipewire-invalid-modifier
dbus-interfaces-in-non-session-instances
wait-for-frame-completion-before-queueing
@@ -115,6 +117,20 @@ debug {
}
```
### `ignore-drm-device`
<sup>Since: next release</sup>
List DRM devices that niri will ignore.
Useful for GPU passthrough when you don't want niri to open a certain device.
```kdl
debug {
ignore-drm-device "/dev/dri/renderD128"
ignore-drm-device "/dev/dri/renderD130"
}
```
### `force-pipewire-invalid-modifier`
<sup>Since: 25.01</sup>

View File

@@ -13,6 +13,7 @@ pub struct Debug {
pub keep_max_bpc_unchanged: bool,
pub restrict_primary_scanout_to_matching_format: bool,
pub render_drm_device: Option<PathBuf>,
pub ignored_drm_devices: Vec<PathBuf>,
pub force_pipewire_invalid_modifier: bool,
pub emulate_zero_presentation_time: bool,
pub disable_resize_throttling: bool,
@@ -45,6 +46,8 @@ pub struct DebugPart {
pub restrict_primary_scanout_to_matching_format: Option<Flag>,
#[knuffel(child, unwrap(argument))]
pub render_drm_device: Option<PathBuf>,
#[knuffel(children(name = "ignore-drm-device"), unwrap(argument))]
pub ignored_drm_devices: Vec<PathBuf>,
#[knuffel(child)]
pub force_pipewire_invalid_modifier: Option<Flag>,
#[knuffel(child)]
@@ -91,6 +94,9 @@ impl MergeWith<DebugPart> for Debug {
);
merge_clone_opt!((self, part), preview_render, render_drm_device);
self.ignored_drm_devices
.extend(part.ignored_drm_devices.iter().cloned());
}
}

View File

@@ -831,6 +831,8 @@ mod tests {
debug {
render-drm-device "/dev/dri/renderD129"
ignore-drm-device "/dev/dri/renderD128"
ignore-drm-device "/dev/dri/renderD130"
}
workspace "workspace-1" {
@@ -2008,6 +2010,10 @@ mod tests {
render_drm_device: Some(
"/dev/dri/renderD129",
),
ignored_drm_devices: [
"/dev/dri/renderD128",
"/dev/dri/renderD130",
],
force_pipewire_invalid_modifier: false,
emulate_zero_presentation_time: false,
disable_resize_throttling: false,

View File

@@ -83,6 +83,8 @@ pub struct Tty {
primary_node: DrmNode,
// DRM render node corresponding to the primary GPU.
primary_render_node: DrmNode,
// Ignored DRM nodes.
ignored_nodes: HashSet<DrmNode>,
// Devices indexed by DRM node (not necessarily the render node).
devices: HashMap<DrmNode, OutputDevice>,
// The dma-buf global corresponds to the output device (the primary GPU). It is only `Some()`
@@ -328,6 +330,11 @@ impl Tty {
}
info!("using as the render node: {node_path}");
let mut ignored_nodes = ignored_nodes_from_config(&config.borrow());
if ignored_nodes.remove(&primary_node) || ignored_nodes.remove(&primary_render_node) {
warn!("ignoring the primary node or render node is not allowed");
}
Ok(Self {
config,
session,
@@ -336,6 +343,7 @@ impl Tty {
gpu_manager,
primary_node,
primary_render_node,
ignored_nodes,
devices: HashMap::new(),
dmabuf_global: None,
update_output_config_on_resume: false,
@@ -504,6 +512,11 @@ impl Tty {
let node = DrmNode::from_dev_id(device_id)?;
if self.ignored_nodes.contains(&node) {
debug!("node is ignored, skipping");
return Ok(());
}
let open_flags = OFlags::RDWR | OFlags::CLOEXEC | OFlags::NOCTTY | OFlags::NONBLOCK;
let fd = self.session.open(path, open_flags)?;
let device_fd = DrmDeviceFd::new(DeviceFd::from(fd));
@@ -1822,6 +1835,48 @@ impl Tty {
}
self.update_output_config_on_resume = false;
// Update ignored nodes.
let mut ignored_nodes = ignored_nodes_from_config(&self.config.borrow());
if ignored_nodes.remove(&self.primary_node)
|| ignored_nodes.remove(&self.primary_render_node)
{
warn!("ignoring the primary node or render node is not allowed");
}
if ignored_nodes != self.ignored_nodes {
self.ignored_nodes = ignored_nodes;
let mut device_list = self
.udev_dispatcher
.as_source_ref()
.device_list()
.map(|(device_id, path)| (device_id, path.to_owned()))
.collect::<HashMap<_, _>>();
let removed_devices = self
.devices
.keys()
.filter(|node| {
self.ignored_nodes.contains(node) || !device_list.contains_key(&node.dev_id())
})
.copied()
.collect::<Vec<_>>();
for node in removed_devices {
device_list.remove(&node.dev_id());
self.device_removed(node.dev_id(), niri);
}
for node in self.devices.keys() {
device_list.remove(&node.dev_id());
}
for (device_id, path) in device_list {
if let Err(err) = self.device_added(device_id, &path, niri) {
warn!("error adding device {path:?}: {err:?}");
}
}
}
// Figure out if we should disable laptop panels.
let mut disable_laptop_panels = false;
if niri.is_lid_closed {
@@ -2181,10 +2236,7 @@ impl GammaProps {
}
}
fn primary_node_from_config(config: &Config) -> Option<(DrmNode, DrmNode)> {
let path = config.debug.render_drm_device.as_ref()?;
debug!("attempting to use render node from config: {path:?}");
fn primary_node_from_render_node(path: &Path) -> Option<(DrmNode, DrmNode)> {
match DrmNode::from_path(path) {
Ok(node) => {
if node.ty() == NodeType::Render {
@@ -2215,9 +2267,30 @@ fn primary_node_from_config(config: &Config) -> Option<(DrmNode, DrmNode)> {
warn!("error opening {path:?} as DRM node: {err:?}");
}
}
None
}
fn primary_node_from_config(config: &Config) -> Option<(DrmNode, DrmNode)> {
let path = config.debug.render_drm_device.as_ref()?;
debug!("attempting to use render node from config: {path:?}");
primary_node_from_render_node(path)
}
fn ignored_nodes_from_config(config: &Config) -> HashSet<DrmNode> {
let mut disabled_nodes = HashSet::new();
for path in &config.debug.ignored_drm_devices {
if let Some((primary_node, render_node)) = primary_node_from_render_node(path) {
disabled_nodes.insert(primary_node);
disabled_nodes.insert(render_node);
}
}
disabled_nodes
}
fn surface_dmabuf_feedback(
compositor: &GbmDrmCompositor,
primary_formats: FormatSet,

View File

@@ -1504,6 +1504,10 @@ impl State {
output_config_changed = true;
}
if config.debug.ignored_drm_devices != old_config.debug.ignored_drm_devices {
output_config_changed = true;
}
// FIXME: move backdrop rendering into layout::Monitor, then this will become unnecessary.
if config.overview.backdrop_color != old_config.overview.backdrop_color {
output_config_changed = true;