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,
collections::{HashMap, HashSet},
fmt,
path::{Path, PathBuf},
path::Path,
sync::{atomic::AtomicBool, mpsc::Receiver, Arc, RwLock},
time::Duration,
};
@ -144,7 +144,7 @@ pub fn init_egl(gbm: &GbmDevice<DrmDeviceFd>) -> Result<EGLInternals> {
}
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() {
return Ok(());
}
@ -337,13 +337,7 @@ impl State {
// TODO atomic commit all surfaces together and drop surfaces, if it fails due to bandwidth
let kms = self.backend.kms();
let was_empty = kms.drm_devices.is_empty();
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
@ -497,12 +491,11 @@ impl State {
dh.remove_global::<State>(socket.drm_global);
}
backend.api.as_mut().remove_node(&device.render_node);
let was_primary = *backend.primary_node.read().unwrap() == Some(device.render_node);
if was_primary {
if let Err(err) = backend.select_primary_gpu(dh) {
warn!("Failed to determine a new primary gpu: {}", err);
}
}
backend
.primary_node
.write()
.unwrap()
.take_if(|node| node == &device.render_node);
}
self.common
.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
let primary = state.backend.kms().primary_node.read().unwrap().clone();
state.launch_xwayland(primary);
@ -254,7 +258,10 @@ fn init_udev(
let dispatcher = Dispatcher::new(udev_backend, move |event, _, state: &mut State| {
let dh = state.common.display_handle.clone();
match match event {
UdevEvent::Added { device_id, path } => state
UdevEvent::Added {
device_id,
ref path,
} => state
.device_added(device_id, path, &dh)
.with_context(|| format!("Failed to add drm device: {}", device_id)),
UdevEvent::Changed { device_id } => state
@ -265,7 +272,15 @@ fn init_udev(
.with_context(|| format!("Failed to remove drm device: {}", device_id)),
} {
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) => {
error!(?err, "Error while handling udev event.")