kms: Fix inconsistent selection of primary GPU

Instead of choosing a primary GPU the first time `device_added` is
called (and then not updating it on the next call, even if that should
be the primary GPU), set the primary GPU only after all devices have
been initially added, and on future changes.

Alternately, the `was_empty` test can just be removed, but it's probably
best not to select the primary GPU multiple times each time the
compositor starts on a multi-GPU system.

Fixes https://github.com/pop-os/cosmic-comp/issues/1437.
This commit is contained in:
Ian Douglas Scott 2025-05-30 21:08:04 -07:00 • committed by Victoria Brekenfeld
parent 087ebaa2c7
commit 619e994955
2 changed files with 24 additions and 16 deletions

View file

@ -46,7 +46,7 @@ use std::{
cell::RefCell, cell::RefCell,
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
fmt, fmt,
path::{Path, PathBuf}, path::Path,
sync::{atomic::AtomicBool, mpsc::Receiver, Arc, RwLock}, sync::{atomic::AtomicBool, mpsc::Receiver, Arc, RwLock},
time::Duration, time::Duration,
}; };
@ -144,7 +144,7 @@ pub fn init_egl(gbm: &GbmDevice<DrmDeviceFd>) -> Result<EGLInternals> {
} }
impl State { impl State {
pub fn device_added(&mut self, dev: dev_t, path: PathBuf, dh: &DisplayHandle) -> Result<()> { pub fn device_added(&mut self, dev: dev_t, path: &Path, dh: &DisplayHandle) -> Result<()> {
if !self.backend.kms().session.is_active() { if !self.backend.kms().session.is_active() {
return Ok(()); return Ok(());
} }
@ -337,13 +337,7 @@ impl State {
// TODO atomic commit all surfaces together and drop surfaces, if it fails due to bandwidth // TODO atomic commit all surfaces together and drop surfaces, if it fails due to bandwidth
let kms = self.backend.kms(); let kms = self.backend.kms();
let was_empty = kms.drm_devices.is_empty();
kms.drm_devices.insert(drm_node, device); kms.drm_devices.insert(drm_node, device);
if was_empty {
if let Err(err) = kms.select_primary_gpu(dh) {
warn!("Failed to determine a new primary gpu: {}", err);
}
}
} }
self.common self.common
@ -497,12 +491,11 @@ impl State {
dh.remove_global::<State>(socket.drm_global); dh.remove_global::<State>(socket.drm_global);
} }
backend.api.as_mut().remove_node(&device.render_node); backend.api.as_mut().remove_node(&device.render_node);
let was_primary = *backend.primary_node.read().unwrap() == Some(device.render_node); backend
if was_primary { .primary_node
if let Err(err) = backend.select_primary_gpu(dh) { .write()
warn!("Failed to determine a new primary gpu: {}", err); .unwrap()
} .take_if(|node| node == &device.render_node);
}
} }
self.common self.common
.output_configuration_state .output_configuration_state

View file

@ -136,6 +136,10 @@ pub fn init_backend(
} }
} }
if let Err(err) = state.backend.kms().select_primary_gpu(dh) {
warn!("Failed to determine primary gpu: {}", err);
}
// start x11 // start x11
let primary = state.backend.kms().primary_node.read().unwrap().clone(); let primary = state.backend.kms().primary_node.read().unwrap().clone();
state.launch_xwayland(primary); state.launch_xwayland(primary);
@ -254,7 +258,10 @@ fn init_udev(
let dispatcher = Dispatcher::new(udev_backend, move |event, _, state: &mut State| { let dispatcher = Dispatcher::new(udev_backend, move |event, _, state: &mut State| {
let dh = state.common.display_handle.clone(); let dh = state.common.display_handle.clone();
match match event { match match event {
UdevEvent::Added { device_id, path } => state UdevEvent::Added {
device_id,
ref path,
} => state
.device_added(device_id, path, &dh) .device_added(device_id, path, &dh)
.with_context(|| format!("Failed to add drm device: {}", device_id)), .with_context(|| format!("Failed to add drm device: {}", device_id)),
UdevEvent::Changed { device_id } => state UdevEvent::Changed { device_id } => state
@ -265,7 +272,15 @@ fn init_udev(
.with_context(|| format!("Failed to remove drm device: {}", device_id)), .with_context(|| format!("Failed to remove drm device: {}", device_id)),
} { } {
Ok(()) => { Ok(()) => {
trace!("Successfully handled udev event.") trace!("Successfully handled udev event.");
let backend = state.backend.kms();
if matches!(event, UdevEvent::Added { .. } | UdevEvent::Removed { .. })
&& backend.primary_node.read().unwrap().is_none()
{
if let Err(err) = state.backend.kms().select_primary_gpu(&dh) {
warn!("Failed to determine a new primary gpu: {}", err);
}
}
} }
Err(err) => { Err(err) => {
error!(?err, "Error while handling udev event.") error!(?err, "Error while handling udev event.")