From 478ba4e4e2389a688e0689ddbfe09089f427bb14 Mon Sep 17 00:00:00 2001 From: Nic Toumayan <70429301+nictoumayan@users.noreply.github.com> Date: Sat, 24 Oct 2026 14:44:04 +0900 Subject: [PATCH] fix(ui): ask wgpu for no more than the GPU offers (#458) egui requests wgpu's default limits on every backend but GL, and device creation fails when the adapter falls short of any one of them. The Raspberry Pi 5's V3D driver (Mesa V3DV) allows 15 inter-stage shader variables against the default 16, so the launcher exited before the first frame with "Limit 'max_inter_stage_shader_variables' value 16 is better than allowed 15". Wrap egui's device descriptor so each requested limit is lowered to the adapter's where it is below the request. GPUs that meet the defaults get the same request as before. Includes regression coverage with the V3D limit. Co-authored-by: Claude Opus 5.5 Co-authored-by: Brandon Thomas --- apps/artcraft-launcher/src/main.rs | 38 +++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/apps/artcraft-launcher/src/main.rs b/apps/artcraft-launcher/src/main.rs index f4f79a71..c1dcdf19 100644 --- a/apps/artcraft-launcher/src/main.rs +++ b/apps/artcraft-launcher/src/main.rs @@ -184,11 +184,33 @@ fn native_options(hidden: bool) -> eframe::NativeOptions { viewport, persist_window: true, persistence_path: settings_file::path(APP_ID), - wgpu_options: wgpu_options(), + wgpu_options: within_adapter_limits(wgpu_options()), ..Default::default() } } +/// Asks wgpu for no more than the GPU offers. egui requests wgpu's default limits on every backend +/// but GL, and creating the device fails when the adapter falls short of any one of them: the +/// Raspberry Pi 5's V3D driver allows 15 inter-stage shader variables against the default 16, and +/// the launcher exited before the first frame. egui's own shaders need far less than either. +fn within_adapter_limits(mut config: eframe::WgpuConfiguration) -> eframe::WgpuConfiguration { + if let eframe::egui_wgpu::WgpuSetup::CreateNew(setup) = &mut config.wgpu_setup { + let egui_descriptor = Arc::clone(&setup.device_descriptor); + setup.device_descriptor = Arc::new(move |adapter| { + let mut descriptor = egui_descriptor(adapter); + descriptor.required_limits = fit_limits(descriptor.required_limits, &adapter.limits()); + descriptor + }); + } + config +} + +/// `requested`, with each limit the adapter can't meet lowered (or raised, for alignments) to +/// what it can. +fn fit_limits(requested: eframe::wgpu::Limits, adapter: &eframe::wgpu::Limits) -> eframe::wgpu::Limits { + requested.or_worse_values_from(adapter) +} + /// wgpu's backend list. On Windows that is Direct3D 12 with GL as the fallback, not Vulkan: /// Vulkan goes through vendor ICDs and third-party overlay layers, and on hybrid AMD laptops whose /// integrated and discrete GPUs run different driver packages (common on OEM machines, where the @@ -1145,6 +1167,20 @@ mod tests { assert!(adapter_rank(Backend::Dx12, integrated, true) < adapter_rank(Backend::Dx12, discrete, true)); } + #[test] + fn the_device_asks_for_no_more_than_the_gpu_offers() { + use eframe::wgpu::Limits; + // Raspberry Pi 5 (V3D 7.1, Mesa V3DV): wgpu's defaults want 16 inter-stage variables. + let v3d = Limits { max_inter_stage_shader_variables: 15, ..Limits::default() }; + assert!(!Limits::default().check_limits(&v3d)); + let fitted = fit_limits(Limits::default(), &v3d); + assert!(fitted.check_limits(&v3d)); + assert_eq!(fitted.max_inter_stage_shader_variables, 15); + // A GPU that offers more still gets egui's request, not everything it has. + let generous = Limits { max_inter_stage_shader_variables: 28, ..Limits::default() }; + assert_eq!(fit_limits(Limits::default(), &generous), Limits::default()); + } + #[cfg(not(any(windows, target_os = "macos")))] #[test] fn linux_draws_on_the_desktops_gpu_unless_told_otherwise() {