1use crate::renderer::api::core::ShaderModuleId;
18use crate::renderer::api::pipeline::RenderPipelineId;
19use std::fmt;
20
21#[derive(Debug)]
23pub enum ShaderError {
24 LoadError {
26 path: String,
28 source_error: String,
30 },
31 CompilationError {
33 label: String,
35 details: String,
37 },
38 NotFound {
40 id: ShaderModuleId,
42 },
43 InvalidEntryPoint {
45 id: ShaderModuleId,
47 entry_point: String,
49 },
50}
51
52impl fmt::Display for ShaderError {
53 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54 match self {
55 ShaderError::LoadError { path, source_error } => {
56 write!(
57 f,
58 "Failed to load shader source from '{path}': {source_error}"
59 )
60 }
61 ShaderError::CompilationError { label, details } => {
62 write!(f, "Shader compilation failed for '{label}': {details}")
63 }
64 ShaderError::NotFound { id } => {
65 write!(f, "Shader module not found for ID: {id:?}")
66 }
67 ShaderError::InvalidEntryPoint { id, entry_point } => {
68 write!(
69 f,
70 "Invalid entry point '{entry_point}' for shader module {id:?}"
71 )
72 }
73 }
74 }
75}
76
77impl std::error::Error for ShaderError {}
78
79#[derive(Debug)]
81pub enum PipelineError {
82 LayoutCreationFailed(String),
84 CompilationFailed {
86 label: Option<String>,
88 details: String,
90 },
91 InvalidShaderModuleForPipeline {
93 id: ShaderModuleId,
95 pipeline_label: Option<String>,
97 },
98 InvalidRenderPipeline {
100 id: RenderPipelineId,
102 },
103 MissingEntryPointForFragmentShader {
105 pipeline_label: Option<String>,
107 shader_id: ShaderModuleId,
109 },
110 IncompatibleColorTarget(String),
112 IncompatibleDepthStencilFormat(String),
114 FeatureNotSupported(String),
116}
117
118impl fmt::Display for PipelineError {
119 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120 match self {
121 PipelineError::LayoutCreationFailed(msg) => {
122 write!(f, "Pipeline layout creation failed: {msg}")
123 }
124 PipelineError::CompilationFailed { label, details } => {
125 write!(
126 f,
127 "Pipeline compilation failed for '{}': {}",
128 label.as_deref().unwrap_or("Unknown"),
129 details
130 )
131 }
132 PipelineError::InvalidShaderModuleForPipeline { id, pipeline_label } => {
133 write!(
134 f,
135 "Invalid shader module {:?} for pipeline '{}'",
136 id,
137 pipeline_label.as_deref().unwrap_or("Unknown")
138 )
139 }
140 PipelineError::InvalidRenderPipeline { id } => {
141 write!(f, "Invalid render pipeline ID: {id:?}")
142 }
143 PipelineError::MissingEntryPointForFragmentShader {
144 pipeline_label,
145 shader_id,
146 } => {
147 write!(
148 f,
149 "Missing entry point for fragment shader in pipeline '{}', shader ID: {:?}",
150 pipeline_label.as_deref().unwrap_or("Unknown"),
151 shader_id
152 )
153 }
154 PipelineError::IncompatibleColorTarget(msg) => {
155 write!(f, "Incompatible color target format: {msg}")
156 }
157 PipelineError::IncompatibleDepthStencilFormat(msg) => {
158 write!(f, "Incompatible depth/stencil format: {msg}")
159 }
160 PipelineError::FeatureNotSupported(msg) => {
161 write!(f, "Feature not supported: {msg}")
162 }
163 }
164 }
165}
166
167impl std::error::Error for PipelineError {}
168
169#[derive(Debug)]
171pub enum ResourceError {
172 Shader(ShaderError),
174 Pipeline(PipelineError),
176 NotFound,
178 InvalidHandle,
180 BackendError(String),
182 OutOfBounds,
184}
185
186impl fmt::Display for ResourceError {
187 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
188 match self {
189 ResourceError::Shader(err) => write!(f, "Shader resource error: {err}"),
190 ResourceError::Pipeline(err) => write!(f, "Pipeline resource error: {err}"),
191 ResourceError::NotFound => write!(f, "Resource not found with ID."),
192 ResourceError::InvalidHandle => write!(f, "Invalid resource handle or ID."),
193 ResourceError::BackendError(msg) => {
194 write!(f, "Backend-specific resource error: {msg}")
195 }
196 ResourceError::OutOfBounds => {
197 write!(f, "Resource access out of bounds.")
198 }
199 }
200 }
201}
202
203impl std::error::Error for ResourceError {
204 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
205 match self {
206 ResourceError::Shader(err) => Some(err),
207 _ => None,
208 }
209 }
210}
211
212impl From<ShaderError> for ResourceError {
213 fn from(err: ShaderError) -> Self {
214 ResourceError::Shader(err)
215 }
216}
217
218impl From<PipelineError> for ResourceError {
219 fn from(err: PipelineError) -> Self {
220 ResourceError::Pipeline(err)
221 }
222}
223
224#[derive(Debug)]
226pub enum RenderError {
227 NotInitialized,
229 InitializationFailed(String),
231 SurfaceAcquisitionFailed(String),
233 RenderingFailed(String),
235 ResourceError(ResourceError),
237 DeviceLost,
240 DeviceOutOfMemory(String),
245 Internal(String),
247}
248
249impl RenderError {
250 pub fn is_fatal(&self) -> bool {
258 matches!(
259 self,
260 RenderError::DeviceLost | RenderError::DeviceOutOfMemory(_)
261 )
262 }
263}
264
265impl fmt::Display for RenderError {
266 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
267 match self {
268 RenderError::NotInitialized => {
269 write!(f, "The rendering system is not initialized.")
270 }
271 RenderError::InitializationFailed(msg) => {
272 write!(f, "Failed to initialize graphics backend: {msg}")
273 }
274 RenderError::SurfaceAcquisitionFailed(msg) => {
275 write!(f, "Failed to acquire surface for rendering: {msg}")
276 }
277 RenderError::RenderingFailed(msg) => {
278 write!(f, "A critical rendering operation failed: {msg}")
279 }
280 RenderError::ResourceError(err) => {
281 write!(f, "Graphics resource operation failed: {err}")
282 }
283 RenderError::DeviceLost => write!(
284 f,
285 "The graphics device was lost and needs to be reinitialized."
286 ),
287 RenderError::DeviceOutOfMemory(msg) => {
288 write!(f, "The graphics device ran out of memory: {msg}")
289 }
290 RenderError::Internal(msg) => {
291 write!(f, "An internal or unexpected error occurred: {msg}")
292 }
293 }
294 }
295}
296
297impl std::error::Error for RenderError {
298 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
299 match self {
300 RenderError::ResourceError(err) => Some(err),
301 _ => None,
302 }
303 }
304}
305
306impl From<ResourceError> for RenderError {
307 fn from(err: ResourceError) -> Self {
308 RenderError::ResourceError(err)
309 }
310}
311
312#[cfg(test)]
313mod tests {
314 use std::error::Error;
315
316 use super::*;
317 use crate::renderer::api::core::ShaderModuleId;
318
319 #[test]
320 fn shader_error_display() {
321 let err = ShaderError::LoadError {
322 path: "path/to/shader.wgsl".to_string(),
323 source_error: "File not found".to_string(),
324 };
325 assert_eq!(
326 format!("{err}"),
327 "Failed to load shader source from 'path/to/shader.wgsl': File not found"
328 );
329
330 let err_comp = ShaderError::CompilationError {
331 label: "MyShader".to_string(),
332 details: "Syntax error at line 5".to_string(),
333 };
334 assert_eq!(
335 format!("{err_comp}"),
336 "Shader compilation failed for 'MyShader': Syntax error at line 5"
337 );
338 }
339
340 #[test]
341 fn resource_error_display_wrapping_shader_error() {
342 let shader_err = ShaderError::NotFound {
343 id: ShaderModuleId(42),
344 };
345 let res_err: ResourceError = shader_err.into();
346 assert_eq!(
347 format!("{res_err}"),
348 "Shader resource error: Shader module not found for ID: ShaderModuleId(42)"
349 );
350 assert!(res_err.source().is_some());
351 }
352
353 #[test]
354 fn render_error_display_wrapping_resource_error() {
355 let shader_err = ShaderError::NotFound {
356 id: ShaderModuleId(101),
357 };
358 let res_err: ResourceError = shader_err.into();
359 let render_err: RenderError = res_err.into();
360 assert_eq!(
361 format!("{render_err}"),
362 "Graphics resource operation failed: Shader resource error: Shader module not found for ID: ShaderModuleId(101)"
363 );
364 assert!(render_err.source().is_some());
365 assert!(render_err.source().unwrap().source().is_some());
366 }
367
368 #[test]
369 fn render_error_is_fatal_classification() {
370 assert!(RenderError::DeviceLost.is_fatal());
372 assert!(RenderError::DeviceOutOfMemory("vk OOM".to_string()).is_fatal());
373
374 assert!(!RenderError::NotInitialized.is_fatal());
376 assert!(!RenderError::SurfaceAcquisitionFailed("Timeout".to_string()).is_fatal());
377 assert!(!RenderError::RenderingFailed("pass".to_string()).is_fatal());
378 assert!(!RenderError::Internal("misc".to_string()).is_fatal());
379 }
380
381 #[test]
382 fn device_out_of_memory_display() {
383 let err = RenderError::DeviceOutOfMemory("allocation failed".to_string());
384 assert_eq!(
385 format!("{err}"),
386 "The graphics device ran out of memory: allocation failed"
387 );
388 }
389}