Skip to main content

alpha2_gpu/
error.rs

1use std::fmt;
2
3/// Failure while acquiring a GPU context, with the platform reason as its
4/// [`source`](std::error::Error::source).
5#[derive(Debug)]
6pub struct GpuError(Cause);
7
8/// The step of acquiring a GPU context that failed.
9#[derive(Copy, Clone, Debug, Eq, PartialEq)]
10#[non_exhaustive]
11pub enum GpuAcquisitionStep {
12    /// No suitable adapter was found.
13    Adapter,
14    /// The adapter could not create a device.
15    Device,
16    /// A backend's instance did not start (on Windows, DX12 with its shader compiler).
17    Instance,
18    /// `ALPHA2_GPU_ADAPTER` names no adapter.
19    Pin,
20    /// No hardware adapter made a device, and software adapters were not wanted.
21    NoHardwareAdapter,
22}
23
24// Browser WebGPU has one instance, one adapter and no pin, so wasm uses only the first two.
25#[derive(Debug)]
26#[cfg_attr(target_arch = "wasm32", allow(dead_code))]
27enum Cause {
28    Adapter(wgpu::RequestAdapterError),
29    Device(wgpu::RequestDeviceError),
30    Instance {
31        backend: &'static str,
32        compiler: String,
33    },
34    Pin(String),
35    NoHardwareAdapter {
36        software: Vec<String>,
37        refusals: Vec<(String, String)>,
38    },
39}
40
41impl GpuError {
42    pub(crate) const fn adapter(error: wgpu::RequestAdapterError) -> Self {
43        Self(Cause::Adapter(error))
44    }
45
46    pub(crate) const fn device(error: wgpu::RequestDeviceError) -> Self {
47        Self(Cause::Device(error))
48    }
49
50    #[cfg(not(target_arch = "wasm32"))]
51    pub(crate) const fn instance(backend: &'static str, compiler: String) -> Self {
52        Self(Cause::Instance { backend, compiler })
53    }
54
55    /// A DX12 backend that lists no adapter because its shader compiler did not load. Reported
56    /// as [`GpuAcquisitionStep::Instance`].
57    #[cfg(not(target_arch = "wasm32"))]
58    #[must_use]
59    pub fn no_dx12_adapter() -> Self {
60        Self::instance("DX12", crate::dx12_shader_compiler().to_string())
61    }
62
63    #[cfg(not(target_arch = "wasm32"))]
64    pub(crate) const fn pin(reason: String) -> Self {
65        Self(Cause::Pin(reason))
66    }
67
68    #[cfg(not(target_arch = "wasm32"))]
69    pub(crate) const fn no_hardware(
70        software: Vec<String>,
71        refusals: Vec<(String, String)>,
72    ) -> Self {
73        Self(Cause::NoHardwareAdapter { software, refusals })
74    }
75
76    /// The step that failed.
77    #[must_use]
78    pub const fn step(&self) -> GpuAcquisitionStep {
79        match self.0 {
80            Cause::Adapter(_) => GpuAcquisitionStep::Adapter,
81            Cause::Device(_) => GpuAcquisitionStep::Device,
82            Cause::Instance { .. } => GpuAcquisitionStep::Instance,
83            Cause::Pin(_) => GpuAcquisitionStep::Pin,
84            Cause::NoHardwareAdapter { .. } => GpuAcquisitionStep::NoHardwareAdapter,
85        }
86    }
87}
88
89impl std::error::Error for GpuError {
90    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
91        match &self.0 {
92            Cause::Adapter(error) => Some(error),
93            Cause::Device(error) => Some(error),
94            Cause::Instance { .. } | Cause::Pin(_) | Cause::NoHardwareAdapter { .. } => None,
95        }
96    }
97}
98
99impl fmt::Display for GpuError {
100    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
101        match &self.0 {
102            Cause::Adapter(error) => write!(formatter, "could not acquire a GPU adapter: {error}"),
103            Cause::Device(error) => write!(formatter, "could not acquire a GPU device: {error}"),
104            Cause::Instance { backend, compiler } if compiler.is_empty() => {
105                write!(formatter, "the {backend} backend did not start")
106            }
107            Cause::Instance { backend, compiler } => write!(
108                formatter,
109                "the {backend} backend did not start (shader compiler: {compiler}): is the file there?"
110            ),
111            Cause::Pin(reason) => formatter.write_str(reason),
112            Cause::NoHardwareAdapter { software, refusals } => {
113                formatter.write_str("no hardware GPU")?;
114                if !software.is_empty() {
115                    write!(
116                        formatter,
117                        "; only software adapters ({}), which are not used here",
118                        software.join(", ")
119                    )?;
120                }
121                if refusals.is_empty() && software.is_empty() {
122                    formatter.write_str(": no GPU adapter was found")?;
123                }
124                for (adapter, reason) in refusals {
125                    write!(formatter, "; {adapter}: {reason}")?;
126                }
127                Ok(())
128            }
129        }
130    }
131}