Skip to main content

oxicuda_webgpu/
error.rs

1//! Error types for the oxicuda-webgpu backend.
2
3use oxicuda_backend::BackendError;
4
5/// Errors specific to the WebGPU backend.
6#[derive(Debug, thiserror::Error)]
7pub enum WebGpuError {
8    /// No compatible WebGPU adapter was found on this system.
9    #[error("no WebGPU adapter found")]
10    NoAdapter,
11
12    /// The device request to the adapter failed.
13    #[error("device request failed: {0}")]
14    DeviceRequest(String),
15
16    /// The GPU ran out of memory during buffer allocation.
17    #[error("out of device memory")]
18    OutOfMemory,
19
20    /// WGSL shader source failed to compile.
21    #[error("WGSL shader compilation failed: {0}")]
22    ShaderCompilation(String),
23
24    /// A compute pipeline could not be created.
25    #[error("pipeline creation failed: {0}")]
26    PipelineCreation(String),
27
28    /// The backend has not been initialized yet.
29    #[error("not initialized")]
30    NotInitialized,
31
32    /// The requested operation is not supported by this backend.
33    #[error("unsupported operation: {0}")]
34    Unsupported(String),
35
36    /// An invalid argument was passed to an operation.
37    #[error("invalid argument: {0}")]
38    InvalidArgument(String),
39
40    /// A buffer mapping operation failed.
41    #[error("buffer mapping failed: {0}")]
42    BufferMapping(String),
43
44    /// An async operation timed out waiting for the adapter.
45    #[error("adapter timeout")]
46    Timeout,
47
48    /// A wgpu validation, out-of-memory, or internal error was captured by
49    /// the non-fatal `on_uncaptured_error` handler installed in
50    /// [`crate::device::WebGpuDevice::new`], instead of aborting the process
51    /// (wgpu's default handler is fatal). See
52    /// [`crate::device::WebGpuDevice::poll_error`].
53    #[error("uncaptured wgpu error: {0}")]
54    UncapturedError(String),
55
56    /// The device was reported lost (GPU reset, driver failure, or an
57    /// external `Device::destroy()` call) by the callback installed via
58    /// `Device::set_device_lost_callback` in
59    /// [`crate::device::WebGpuDevice::new`].
60    #[error("device lost: {0}")]
61    DeviceLost(String),
62}
63
64/// Convenience result alias for WebGPU operations.
65pub type WebGpuResult<T> = Result<T, WebGpuError>;
66
67impl From<WebGpuError> for BackendError {
68    fn from(e: WebGpuError) -> Self {
69        match e {
70            WebGpuError::NoAdapter => BackendError::DeviceError("no WebGPU adapter found".into()),
71            WebGpuError::DeviceRequest(msg) => {
72                BackendError::DeviceError(format!("device request failed: {msg}"))
73            }
74            WebGpuError::OutOfMemory => BackendError::OutOfMemory,
75            WebGpuError::ShaderCompilation(msg) => {
76                BackendError::DeviceError(format!("WGSL shader compilation failed: {msg}"))
77            }
78            WebGpuError::PipelineCreation(msg) => {
79                BackendError::DeviceError(format!("pipeline creation failed: {msg}"))
80            }
81            WebGpuError::NotInitialized => BackendError::NotInitialized,
82            WebGpuError::Unsupported(msg) => BackendError::Unsupported(msg),
83            WebGpuError::InvalidArgument(msg) => BackendError::InvalidArgument(msg),
84            WebGpuError::BufferMapping(msg) => {
85                BackendError::DeviceError(format!("buffer mapping failed: {msg}"))
86            }
87            WebGpuError::Timeout => BackendError::DeviceError("adapter timeout".into()),
88            WebGpuError::UncapturedError(msg) => {
89                BackendError::DeviceError(format!("uncaptured wgpu error: {msg}"))
90            }
91            WebGpuError::DeviceLost(msg) => {
92                BackendError::DeviceError(format!("device lost: {msg}"))
93            }
94        }
95    }
96}
97
98#[cfg(test)]
99mod tests {
100    use super::*;
101
102    #[test]
103    fn webgpu_error_display() {
104        assert_eq!(
105            WebGpuError::NoAdapter.to_string(),
106            "no WebGPU adapter found"
107        );
108        assert_eq!(
109            WebGpuError::DeviceRequest("oops".into()).to_string(),
110            "device request failed: oops"
111        );
112        assert_eq!(WebGpuError::OutOfMemory.to_string(), "out of device memory");
113        assert_eq!(
114            WebGpuError::ShaderCompilation("syntax error".into()).to_string(),
115            "WGSL shader compilation failed: syntax error"
116        );
117        assert_eq!(
118            WebGpuError::PipelineCreation("invalid layout".into()).to_string(),
119            "pipeline creation failed: invalid layout"
120        );
121        assert_eq!(WebGpuError::NotInitialized.to_string(), "not initialized");
122        assert_eq!(
123            WebGpuError::Unsupported("foo".into()).to_string(),
124            "unsupported operation: foo"
125        );
126        assert_eq!(
127            WebGpuError::InvalidArgument("bad arg".into()).to_string(),
128            "invalid argument: bad arg"
129        );
130        assert_eq!(
131            WebGpuError::BufferMapping("lock poisoned".into()).to_string(),
132            "buffer mapping failed: lock poisoned"
133        );
134        assert_eq!(WebGpuError::Timeout.to_string(), "adapter timeout");
135        assert_eq!(
136            WebGpuError::UncapturedError("oom".into()).to_string(),
137            "uncaptured wgpu error: oom"
138        );
139        assert_eq!(
140            WebGpuError::DeviceLost("reset".into()).to_string(),
141            "device lost: reset"
142        );
143    }
144
145    #[test]
146    fn webgpu_error_from_backend_error() {
147        // Verify the From conversion produces the correct BackendError variants.
148        let e = BackendError::from(WebGpuError::OutOfMemory);
149        assert_eq!(e, BackendError::OutOfMemory);
150
151        let e = BackendError::from(WebGpuError::NotInitialized);
152        assert_eq!(e, BackendError::NotInitialized);
153
154        let e = BackendError::from(WebGpuError::Unsupported("bar".into()));
155        assert_eq!(e, BackendError::Unsupported("bar".into()));
156
157        let e = BackendError::from(WebGpuError::InvalidArgument("baz".into()));
158        assert_eq!(e, BackendError::InvalidArgument("baz".into()));
159
160        let e = BackendError::from(WebGpuError::NoAdapter);
161        assert!(matches!(e, BackendError::DeviceError(_)));
162
163        let e = BackendError::from(WebGpuError::DeviceRequest("x".into()));
164        assert!(matches!(e, BackendError::DeviceError(_)));
165
166        let e = BackendError::from(WebGpuError::BufferMapping("m".into()));
167        assert!(matches!(e, BackendError::DeviceError(_)));
168
169        let e = BackendError::from(WebGpuError::Timeout);
170        assert!(matches!(e, BackendError::DeviceError(_)));
171
172        let e = BackendError::from(WebGpuError::UncapturedError("oom".into()));
173        assert!(matches!(e, BackendError::DeviceError(_)));
174
175        let e = BackendError::from(WebGpuError::DeviceLost("reset".into()));
176        assert!(matches!(e, BackendError::DeviceError(_)));
177    }
178}