fj_viewer/graphics/
device.rs

1use tracing::{debug, error};
2
3#[derive(Debug)]
4pub struct Device {
5    pub device: wgpu::Device,
6    pub queue: wgpu::Queue,
7}
8
9impl Device {
10    pub async fn from_preferred_adapter(
11        instance: &wgpu::Instance,
12        surface: &wgpu::Surface<'_>,
13    ) -> Result<(Self, wgpu::Adapter, wgpu::Features), DeviceError> {
14        let adapter = instance
15            .request_adapter(&wgpu::RequestAdapterOptions {
16                power_preference: wgpu::PowerPreference::None,
17                force_fallback_adapter: false,
18                compatible_surface: Some(surface),
19            })
20            .await
21            .ok_or(DeviceError::RequestAdapter)?;
22
23        debug!("Using adapter: {:?}", adapter.get_info());
24
25        let (device, features) = Device::new(&adapter).await?;
26
27        Ok((device, adapter, features))
28    }
29
30    pub async fn try_from_all_adapters(
31        instance: &wgpu::Instance,
32    ) -> Result<(Self, wgpu::Adapter, wgpu::Features), DeviceError> {
33        let mut all_adapters = instance
34            .enumerate_adapters(wgpu::Backends::all())
35            .into_iter();
36
37        let result = loop {
38            let Some(adapter) = all_adapters.next() else {
39                debug!("No more adapters to try");
40                break None;
41            };
42
43            let (device, features) = match Device::new(&adapter).await {
44                Ok((device, adapter)) => (device, adapter),
45                Err(err) => {
46                    error!(
47                        "Failed to get device from adapter {:?}: {:?}",
48                        adapter.get_info(),
49                        err,
50                    );
51                    continue;
52                }
53            };
54
55            break Some((device, adapter, features));
56        };
57
58        for adapter in all_adapters {
59            debug!(
60                "Remaining adapter that wasn't tried: {:?}",
61                adapter.get_info()
62            );
63        }
64
65        result.ok_or(DeviceError::FoundNoWorkingAdapter)
66    }
67
68    pub async fn new(
69        adapter: &wgpu::Adapter,
70    ) -> Result<(Self, wgpu::Features), DeviceError> {
71        let required_features = {
72            let desired_features = wgpu::Features::POLYGON_MODE_LINE;
73            let available_features = adapter.features();
74
75            // By requesting the intersection of desired and available features,
76            // we prevent two things:
77            //
78            // 1. That requesting the device panics, which would happen if we
79            //    requested unavailable features.
80            // 2. That a developer ends up accidentally using features that
81            //    happen to be available on their machine, but that aren't
82            //    necessarily available for all the users.
83            desired_features.intersection(available_features)
84        };
85
86        let required_limits = {
87            // This is the lowest of the available defaults. It should guarantee
88            // that we can run pretty much everywhere.
89            let lowest_limits = wgpu::Limits::downlevel_webgl2_defaults();
90
91            // However, these lowest limits aren't necessarily capable of
92            // supporting the screen resolution of our current platform, so
93            // let's amend them.
94            let supported_limits = adapter.limits();
95            lowest_limits.using_resolution(supported_limits)
96        };
97
98        let (device, queue) = adapter
99            .request_device(
100                &wgpu::DeviceDescriptor {
101                    label: None,
102                    required_features,
103                    required_limits,
104                },
105                None,
106            )
107            .await?;
108
109        Ok((Device { device, queue }, required_features))
110    }
111}
112
113/// Render device initialization error
114#[derive(Debug, thiserror::Error)]
115pub enum DeviceError {
116    /// Failed to request adapter
117    #[error("Failed to request adapter")]
118    RequestAdapter,
119
120    /// Failed to request device
121    #[error("Failed to request device")]
122    RequestDevice(#[from] wgpu::RequestDeviceError),
123
124    /// Found no working adapter to get a device from
125    #[error("Found no working adapter to get a device from")]
126    FoundNoWorkingAdapter,
127}