Skip to main content

embedded_3dgfx/
error.rs

1//! Rendering error taxonomy shared by the record and execute stages.
2
3/// Error returned by a display backend transfer.
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5#[non_exhaustive]
6pub enum DisplayError {
7    /// DMA transfer is still in progress.
8    Busy,
9    /// Hardware error during transfer.
10    HardwareError,
11    /// Invalid buffer configuration.
12    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}