diff --git a/apps/artcraft-launcher/src/main.rs b/apps/artcraft-launcher/src/main.rs index 043a0e2..3806d42 100644 --- a/apps/artcraft-launcher/src/main.rs +++ b/apps/artcraft-launcher/src/main.rs @@ -218,7 +218,38 @@ fn adapter_rank(backend: eframe::wgpu::Backend, device_type: eframe::wgpu::Devic (backend != Backend::Dx12, device) } -#[cfg(not(windows))] +/// wgpu's adapter on Linux and the BSDs: the GPU the desktop runs on. egui asks for the +/// high-performance GPU, which on a hybrid laptop in PRIME on-demand mode is the discrete one while +/// the display server runs on the integrated one: every frame then crosses between the GPUs, and +/// while the window is resized it shows stale or garbled frames and can stop drawing (issue #69). +/// With no preference wgpu takes the first adapter Vulkan lists, and Mesa's device-select layer +/// lists the display server's GPU first (or the one `DRI_PRIME` picks). Without that layer the +/// order follows the ICD files, where lavapipe (`lvp_icd`) can sort before the real driver, so a +/// software rasterizer only wins when nothing else can draw. `WGPU_POWER_PREF` (`low`, `high` or +/// `none`) still overrides it, and then wgpu picks the adapter itself. +#[cfg(not(any(windows, target_os = "macos")))] +fn wgpu_options() -> eframe::WgpuConfiguration { + use eframe::wgpu::{DeviceType, PowerPreference}; + let mut config = eframe::WgpuConfiguration::default(); + if let eframe::egui_wgpu::WgpuSetup::CreateNew(setup) = &mut config.wgpu_setup { + if let Some(preference) = PowerPreference::from_env() { + setup.power_preference = preference; + } else { + setup.power_preference = PowerPreference::None; + setup.native_adapter_selector = Some(Arc::new(|adapters, surface| { + adapters + .iter() + .filter(|a| surface.is_none_or(|s| a.is_surface_supported(s))) + .min_by_key(|a| a.get_info().device_type == DeviceType::Cpu) + .cloned() + .ok_or_else(|| "no graphics adapter can draw to the window".to_owned()) + })); + } + } + config +} + +#[cfg(target_os = "macos")] fn wgpu_options() -> eframe::WgpuConfiguration { eframe::WgpuConfiguration::default() } @@ -970,4 +1001,17 @@ mod tests { assert!(adapter_rank(Backend::Dx12, discrete, false) < adapter_rank(Backend::Dx12, integrated, false)); assert!(adapter_rank(Backend::Dx12, integrated, true) < adapter_rank(Backend::Dx12, discrete, true)); } + + #[cfg(not(any(windows, target_os = "macos")))] + #[test] + fn linux_draws_on_the_desktops_gpu_unless_told_otherwise() { + use eframe::wgpu::PowerPreference; + let config = wgpu_options(); + let eframe::egui_wgpu::WgpuSetup::CreateNew(setup) = &config.wgpu_setup else { + panic!("the launcher creates its own wgpu instance"); + }; + // egui would ask for the discrete GPU: on a PRIME laptop that's not the desktop's (issue #69). + let expected = PowerPreference::from_env().unwrap_or(PowerPreference::None); + assert_eq!(setup.power_preference, expected); + } }