1use std::fmt;
2
3#[derive(Debug)]
6pub struct GpuError(Cause);
7
8#[derive(Copy, Clone, Debug, Eq, PartialEq)]
10#[non_exhaustive]
11pub enum GpuAcquisitionStep {
12 Adapter,
14 Device,
16 Instance,
18 Pin,
20 NoHardwareAdapter,
22}
23
24#[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 #[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 #[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}