diff --git a/crates/fj-viewer/src/graphics/device.rs b/crates/fj-viewer/src/graphics/device.rs index 0062d5ea2..63d3bee21 100644 --- a/crates/fj-viewer/src/graphics/device.rs +++ b/crates/fj-viewer/src/graphics/device.rs @@ -1,3 +1,5 @@ +use tracing::{debug, error}; + #[derive(Debug)] pub struct Device { pub device: wgpu::Device, @@ -5,9 +7,66 @@ pub struct Device { } impl Device { + pub async fn from_preferred_adapter( + instance: &wgpu::Instance, + surface: &wgpu::Surface, + ) -> Result<(Self, wgpu::Adapter, wgpu::Features), DeviceError> { + let adapter = instance + .request_adapter(&wgpu::RequestAdapterOptions { + power_preference: wgpu::PowerPreference::None, + force_fallback_adapter: false, + compatible_surface: Some(surface), + }) + .await + .ok_or(DeviceError::RequestAdapter)?; + + debug!("Using adapter: {:?}", adapter.get_info()); + + let (device, features) = Device::new(&adapter).await?; + + Ok((device, adapter, features)) + } + + pub async fn try_from_all_adapters( + instance: &wgpu::Instance, + ) -> Result<(Self, wgpu::Adapter, wgpu::Features), DeviceError> { + let mut all_adapters = + instance.enumerate_adapters(wgpu::Backends::all()); + + let result = loop { + let Some(adapter) = all_adapters.next() else { + debug!("No more adapters to try"); + break None; + }; + + let (device, features) = match Device::new(&adapter).await { + Ok((device, adapter)) => (device, adapter), + Err(err) => { + error!( + "Failed to get device from adapter {:?}: {:?}", + adapter.get_info(), + err, + ); + continue; + } + }; + + break Some((device, adapter, features)); + }; + + for adapter in all_adapters { + debug!( + "Remaining adapter that wasn't tried: {:?}", + adapter.get_info() + ); + } + + result.ok_or(DeviceError::FoundNoWorkingAdapter) + } + pub async fn new( adapter: &wgpu::Adapter, - ) -> Result<(Self, wgpu::Features), wgpu::RequestDeviceError> { + ) -> Result<(Self, wgpu::Features), DeviceError> { let features = { let desired_features = wgpu::Features::POLYGON_MODE_LINE; let available_features = adapter.features(); @@ -49,3 +108,19 @@ impl Device { Ok((Device { device, queue }, features)) } } + +/// Render device initialization error +#[derive(Debug, thiserror::Error)] +pub enum DeviceError { + /// Failed to request adapter + #[error("Failed to request adapter")] + RequestAdapter, + + /// Failed to request device + #[error("Failed to request device")] + RequestDevice(#[from] wgpu::RequestDeviceError), + + /// Found no working adapter to get a device from + #[error("Found no working adapter to get a device from")] + FoundNoWorkingAdapter, +} diff --git a/crates/fj-viewer/src/graphics/mod.rs b/crates/fj-viewer/src/graphics/mod.rs index abda85fb1..0785f08de 100644 --- a/crates/fj-viewer/src/graphics/mod.rs +++ b/crates/fj-viewer/src/graphics/mod.rs @@ -15,6 +15,7 @@ mod uniforms; mod vertices; pub use self::{ + device::DeviceError, draw_config::DrawConfig, renderer::{DrawError, Renderer, RendererInitError}, }; diff --git a/crates/fj-viewer/src/graphics/renderer.rs b/crates/fj-viewer/src/graphics/renderer.rs index 469c26f2c..6f3960db7 100644 --- a/crates/fj-viewer/src/graphics/renderer.rs +++ b/crates/fj-viewer/src/graphics/renderer.rs @@ -1,7 +1,7 @@ use std::{io, mem::size_of, vec}; use thiserror::Error; -use tracing::{debug, trace}; +use tracing::{debug, error, trace}; use wgpu::util::DeviceExt as _; use crate::{ @@ -13,7 +13,7 @@ use super::{ device::Device, draw_config::DrawConfig, drawables::Drawables, geometries::Geometries, navigation_cube::NavigationCubeRenderer, pipelines::Pipelines, transform::Transform, uniforms::Uniforms, - vertices::Vertices, DEPTH_FORMAT, SAMPLE_COUNT, + vertices::Vertices, DeviceError, DEPTH_FORMAT, SAMPLE_COUNT, }; /// Graphics rendering state and target abstraction @@ -50,18 +50,28 @@ impl Renderer { debug!("Available adapter: {:?}", adapter.get_info()); } - let adapter = instance - .request_adapter(&wgpu::RequestAdapterOptions { - power_preference: wgpu::PowerPreference::None, - force_fallback_adapter: false, - compatible_surface: Some(&surface), - }) - .await - .ok_or(RendererInitError::RequestAdapter)?; - - debug!("Using adapter: {:?}", adapter.get_info()); - - let (device, features) = Device::new(&adapter).await?; + let result = Device::from_preferred_adapter(&instance, &surface).await; + let (device, adapter, features) = match result { + Ok((device, adapter, features)) => (device, adapter, features), + Err(_) => { + error!("Failed to acquire device from preferred adapter"); + + match Device::try_from_all_adapters(&instance).await { + Ok((device, adapter, features)) => { + (device, adapter, features) + } + Err(err) => { + error!("Prepend `RUST_LOG=fj_viewer=debug` and re-run"); + error!("Then open an issue and post your output"); + error!( + "https://github.com/hannobraun/fornjot/issues/new" + ); + + return Err(err.into()); + } + } + } + }; let color_format = 'color_format: { let capabilities = surface.get_capabilities(&adapter); @@ -374,15 +384,9 @@ pub enum RendererInitError { #[error("Error creating surface")] CreateSurface(#[from] wgpu::CreateSurfaceError), - /// Graphics accelerator acquisition error - #[error("Error request adapter")] - RequestAdapter, - - /// Device request errors - /// - /// See: [wgpu::RequestDeviceError](https://docs.rs/wgpu/latest/wgpu/struct.RequestDeviceError.html) - #[error("Error requesting device")] - RequestDevice(#[from] wgpu::RequestDeviceError), + /// Device error + #[error(transparent)] + Device(#[from] DeviceError), } /// Draw error diff --git a/crates/fj-viewer/src/lib.rs b/crates/fj-viewer/src/lib.rs index 6819762f7..b3532ef05 100644 --- a/crates/fj-viewer/src/lib.rs +++ b/crates/fj-viewer/src/lib.rs @@ -18,7 +18,7 @@ mod screen; mod viewer; pub use self::{ - graphics::RendererInitError, + graphics::{DeviceError, RendererInitError}, input::InputEvent, screen::{NormalizedScreenPosition, Screen, ScreenSize}, viewer::Viewer,