1use std::fmt;
2
3#[derive(Debug)]
4pub enum RenderError {
5 DeviceLost(String),
6 Surface(String),
7 MaterialCompile { name: String, reason: String },
8 ShaderValidation(String),
9 UnsupportedFormat(wgpu::TextureFormat),
10 VertexOverflow { needed: usize, max: usize },
11 FrameAcquire(String),
12}
13
14impl fmt::Display for RenderError {
15 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
16 match self {
17 RenderError::DeviceLost(msg) => {
18 write!(f, "GPU device lost: {msg}. Try recreating the renderer.")
19 }
20 RenderError::Surface(msg) => {
21 write!(
22 f,
23 "Surface error: {msg}. Check window state and GPU availability."
24 )
25 }
26 RenderError::MaterialCompile { name, reason } => {
27 write!(
28 f,
29 "Material compile failed for graph '{name}': {reason}. Validate node connections and types."
30 )
31 }
32 RenderError::ShaderValidation(msg) => {
33 write!(
34 f,
35 "Shader validation failed: {msg}. See inner WGSL error for line/column info."
36 )
37 }
38 RenderError::UnsupportedFormat(fmt) => {
39 write!(
40 f,
41 "Surface format {fmt:?} not supported by this adapter. Try a different backend."
42 )
43 }
44 RenderError::VertexOverflow { needed, max } => {
45 write!(
46 f,
47 "Vertex buffer overflow: needed {needed} vertices, max is {max}. Batch geometry or increase pool size."
48 )
49 }
50 RenderError::FrameAcquire(msg) => {
51 write!(f, "Failed to acquire next frame from surface: {msg}")
52 }
53 }
54 }
55}
56
57impl std::error::Error for RenderError {}
58
59#[cfg(test)]
60mod tests {
61 use super::*;
62
63 #[test]
64 fn device_lost_includes_message_and_suggestion() {
65 let err = RenderError::DeviceLost("GPU removed".into());
66 let msg = err.to_string();
67 assert!(msg.contains("GPU removed"), "should contain message");
68 assert!(msg.contains("recreating"), "should suggest recreating");
69 }
70
71 #[test]
72 fn material_compile_includes_name_and_reason() {
73 let err = RenderError::MaterialCompile {
74 name: "graph_0".into(),
75 reason: "cycle detected".into(),
76 };
77 let msg = err.to_string();
78 assert!(msg.contains("graph_0"), "should contain graph name");
79 assert!(msg.contains("cycle detected"), "should contain reason");
80 assert!(msg.contains("node connections"), "should suggest fix");
81 }
82
83 #[test]
84 fn vertex_overflow_includes_counts() {
85 let err = RenderError::VertexOverflow {
86 needed: 100000,
87 max: 65536,
88 };
89 let msg = err.to_string();
90 assert!(msg.contains("100000"), "should contain needed count");
91 assert!(msg.contains("65536"), "should contain max count");
92 }
93
94 #[test]
95 fn shader_validation_includes_detail() {
96 let err = RenderError::ShaderValidation("type mismatch at line 42".into());
97 let msg = err.to_string();
98 assert!(msg.contains("type mismatch"), "should contain detail");
99 assert!(msg.contains("WGSL"), "should mention WGSL");
100 }
101
102 #[test]
103 fn error_trait_satisfied() {
104 let _boxed: Box<dyn std::error::Error> = Box::new(RenderError::FrameAcquire("test".into()));
105 }
106}