1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5#[non_exhaustive]
6pub enum DisplayError {
7 Busy,
9 HardwareError,
11 InvalidBuffer,
13}
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16#[non_exhaustive]
17pub enum BudgetKind {
18 DrawPrimitives {
19 attempted: usize,
20 max: usize,
21 },
22 MeshesPerFrame {
23 attempted: usize,
24 max: usize,
25 },
26 TrianglesPerMesh {
27 attempted: usize,
28 max: usize,
29 },
30 VerticesPerMesh {
31 attempted: usize,
32 max: usize,
33 },
34 Textures {
35 attempted: usize,
36 max: usize,
37 },
38 FramebufferDimensions {
39 width: usize,
40 height: usize,
41 max_width: usize,
42 max_height: usize,
43 },
44 ZBufferLength {
45 expected: usize,
46 got: usize,
47 },
48}
49
50impl BudgetKind {
51 pub fn key(&self) -> &'static str {
52 match self {
53 BudgetKind::DrawPrimitives { .. } => "DrawPrimitives",
54 BudgetKind::MeshesPerFrame { .. } => "MeshesPerFrame",
55 BudgetKind::TrianglesPerMesh { .. } => "TrianglesPerMesh",
56 BudgetKind::VerticesPerMesh { .. } => "VerticesPerMesh",
57 BudgetKind::Textures { .. } => "Textures",
58 BudgetKind::FramebufferDimensions { .. } => "FramebufferDimensions",
59 BudgetKind::ZBufferLength { .. } => "ZBufferLength",
60 }
61 }
62}
63
64#[derive(Debug, Clone, Copy, PartialEq, Eq)]
65#[non_exhaustive]
66pub enum RenderError {
67 OutOfBudget(BudgetKind),
68 InvalidInput(&'static str),
69 Backend(DisplayError),
70 BackendFault(BackendFaultKind),
71 Stall(StallKind),
72 Recoverable {
73 fault: RuntimeFaultKind,
74 action: RecoveryAction,
75 },
76}
77
78#[derive(Debug, Clone, Copy, PartialEq, Eq)]
79#[non_exhaustive]
80pub enum BackendFaultKind {
81 DmaBusyTimeout,
82 TransferStartFailed,
83 InvalidBufferConfig,
84 DeviceUnavailable,
85}
86
87#[derive(Debug, Clone, Copy, PartialEq, Eq)]
88#[non_exhaustive]
89pub enum StallKind {
90 RecordStage,
91 ExecuteStage,
92 PresentStage,
93}
94
95#[derive(Debug, Clone, Copy, PartialEq, Eq)]
96#[non_exhaustive]
97pub enum RuntimeFaultKind {
98 Backend(BackendFaultKind),
99 Budget(BudgetKind),
100 Stall(StallKind),
101}
102
103#[derive(Debug, Clone, Copy, PartialEq, Eq)]
104#[non_exhaustive]
105pub enum RecoveryAction {
106 Retry,
107 RetryWithFallback,
108 DropEffects,
109 ReduceQuality,
110 SkipFrame,
111 ResetBackend,
112}
113
114impl From<DisplayError> for RenderError {
115 fn from(value: DisplayError) -> Self {
116 match value {
117 DisplayError::Busy => Self::BackendFault(BackendFaultKind::DmaBusyTimeout),
118 DisplayError::HardwareError => {
119 Self::BackendFault(BackendFaultKind::TransferStartFailed)
120 }
121 DisplayError::InvalidBuffer => {
122 Self::BackendFault(BackendFaultKind::InvalidBufferConfig)
123 }
124 }
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131
132 #[test]
133 fn budget_kind_key_is_stable() {
134 assert_eq!(
135 BudgetKind::DrawPrimitives {
136 attempted: 1,
137 max: 2
138 }
139 .key(),
140 "DrawPrimitives"
141 );
142 assert_eq!(
143 BudgetKind::FramebufferDimensions {
144 width: 1,
145 height: 1,
146 max_width: 2,
147 max_height: 2
148 }
149 .key(),
150 "FramebufferDimensions"
151 );
152 assert_eq!(
153 BudgetKind::ZBufferLength {
154 expected: 16,
155 got: 8
156 }
157 .key(),
158 "ZBufferLength"
159 );
160 }
161
162 #[test]
163 fn display_error_maps_to_backend_fault() {
164 assert_eq!(
165 RenderError::from(DisplayError::Busy),
166 RenderError::BackendFault(BackendFaultKind::DmaBusyTimeout)
167 );
168 assert_eq!(
169 RenderError::from(DisplayError::HardwareError),
170 RenderError::BackendFault(BackendFaultKind::TransferStartFailed)
171 );
172 assert_eq!(
173 RenderError::from(DisplayError::InvalidBuffer),
174 RenderError::BackendFault(BackendFaultKind::InvalidBufferConfig)
175 );
176 }
177}