diff --git a/apps/figcraft/src/graphics.rs b/apps/figcraft/src/graphics.rs new file mode 100644 index 0000000..ee7b74f --- /dev/null +++ b/apps/figcraft/src/graphics.rs @@ -0,0 +1,34 @@ +//! Which graphics API draws the window. +//! +//! eframe offers wgpu every backend it was built with, and between equal GPUs wgpu takes Vulkan +//! first, on Windows too. A Vulkan driver that faults there takes the app down as the window opens: +//! an access violation inside the vendor's driver, which no guard of ours can catch (seen on a laptop +//! with two AMD GPUs whose drivers were years apart). Direct3D 12 is part of Windows, so on Windows +//! wgpu is offered Direct3D 12, and OpenGL for hardware without a Direct3D 12 driver. +//! +//! `WGPU_BACKEND` (`dx12`, `vulkan`, `gl`, `metal`) still names the backend on every system, and +//! systems other than Windows keep eframe's defaults. + +use eframe::egui_wgpu::{WgpuConfiguration, WgpuSetup}; +use eframe::wgpu::Backends; + +/// Direct3D 12, then OpenGL: wgpu ranks a hardware GPU on either ahead of Direct3D's software one. +const WINDOWS: Backends = Backends::DX12.union(Backends::GL); + +/// The backends wgpu chooses from: the ones `WGPU_BACKEND` asks for (`requested`), else [`WINDOWS`] +/// on Windows, else eframe's `defaults`. +pub fn backends(requested: Option, windows: bool, defaults: Backends) -> Backends { + match requested { + Some(requested) => requested, + None if windows => WINDOWS, + None => defaults, + } +} + +/// Offer wgpu [`backends`] instead of eframe's defaults. +pub fn configure(options: &mut WgpuConfiguration) { + if let WgpuSetup::CreateNew(create) = &mut options.wgpu_setup { + let offered = &mut create.instance_descriptor.backends; + *offered = backends(Backends::from_env(), cfg!(windows), *offered); + } +} diff --git a/apps/figcraft/src/graphics_tests.rs b/apps/figcraft/src/graphics_tests.rs new file mode 100644 index 0000000..e103d32 --- /dev/null +++ b/apps/figcraft/src/graphics_tests.rs @@ -0,0 +1,32 @@ +//! The backends offered to wgpu: no Vulkan on Windows unless it is asked for (a faulting Vulkan +//! driver crashed the app as its window opened), and nothing changed anywhere else. + +use eframe::wgpu::Backends; + +use crate::graphics::backends; + +/// What eframe offers when `WGPU_BACKEND` isn't set. +const DEFAULTS: Backends = Backends::PRIMARY.union(Backends::GL); + +#[test] +fn windows_is_offered_direct3d_and_not_vulkan() { + let offered = backends(None, true, DEFAULTS); + assert!(offered.contains(Backends::DX12), "Direct3D 12 draws the window"); + assert!(offered.contains(Backends::GL), "OpenGL is there for hardware without a Direct3D 12 driver"); + assert!(!offered.contains(Backends::VULKAN), "a Vulkan driver that faults can't be picked"); +} + +#[test] +fn wgpu_backend_names_the_backend_on_every_system() { + for windows in [true, false] { + for asked in [Backends::VULKAN, Backends::DX12, Backends::GL, Backends::VULKAN | Backends::GL] { + assert_eq!(backends(Some(asked), windows, DEFAULTS), asked, "windows: {windows}"); + } + } +} + +#[test] +fn other_systems_keep_eframes_defaults() { + assert_eq!(backends(None, false, DEFAULTS), DEFAULTS); + assert_eq!(backends(None, false, Backends::METAL), Backends::METAL); +} diff --git a/apps/figcraft/src/main.rs b/apps/figcraft/src/main.rs index 7689fb7..0332f7d 100644 --- a/apps/figcraft/src/main.rs +++ b/apps/figcraft/src/main.rs @@ -12,12 +12,15 @@ mod args; mod control_server; mod gamepad; +mod graphics; mod recovery; mod services; #[cfg(test)] mod args_tests; #[cfg(test)] +mod graphics_tests; +#[cfg(test)] mod recovery_online_tests; #[cfg(test)] mod recovery_tests; @@ -182,7 +185,8 @@ fn main() -> eframe::Result { if let Some(icon) = icon { viewport = viewport.with_icon(icon); } - let options = eframe::NativeOptions { viewport, renderer: eframe::Renderer::Wgpu, ..Default::default() }; + let mut options = eframe::NativeOptions { viewport, renderer: eframe::Renderer::Wgpu, ..Default::default() }; + graphics::configure(&mut options.wgpu_options); figcraft_ui_egui::windows::install(fig); eframe::run_native("FigCraft", options, Box::new(move |_cc| Ok(Box::new(App { queue, ctx_slot, title: String::new(), recovery })))) }