frust_engine/error.rs
1use thiserror::Error;
2
3/// Engine render errors — returned on frame paths, never panicked.
4///
5/// Mirrors the reference sparse-strips renderer's error cases plus engine-specific constraints.
6/// The engine always returns errors on invalid or oversized resources rather than
7/// panicking, per Frust's frame-path invariant (E17).
8#[derive(Debug, Clone, Error)]
9pub enum EngineError {
10 #[error("atlas allocation failed")]
11 AtlasError,
12
13 #[error("missing texture binding")]
14 MissingTextureBinding,
15
16 #[error("intermediate texture too large")]
17 IntermediateTextureTooLarge,
18
19 #[error("intermediate texture limit reached")]
20 IntermediateTextureLimitReached,
21
22 #[error("target exceeds u16 ceiling (65,535 pixels)")]
23 TargetTooLarge,
24
25 #[error("non-finite transform refused")]
26 InvalidTransform,
27
28 /// A command's own geometry — a rectangle's extents, a corner radius, a
29 /// path point, a stroke width, a dash length or phase — is non-finite.
30 ///
31 /// Separate from [`InvalidTransform`](Self::InvalidTransform) because the
32 /// two name different halves of a frame's input: a transform maps geometry
33 /// onto the device grid, while this is the geometry itself, and a caller
34 /// chasing a blank frame needs to know which of the two it recorded wrong.
35 /// Both are refused by the same up-front walk, before any lowering runs.
36 #[error("non-finite geometry refused")]
37 InvalidGeometry,
38
39 /// A frame's layer shape is outside what the engine's scheduler serves.
40 ///
41 /// `reason` names what was found — a layer graph needing a third live
42 /// intermediate page at one pass, a chain deeper than the two-page
43 /// ping-pong serves, a filter layer, a non-default blend mode — because the
44 /// caller's own log is where a frame that produced no pixels has to be
45 /// explainable from.
46 ///
47 /// Returned rather than panicked (E17), but it is not a route to a second
48 /// renderer: the engine tier carries none, and which renderer draws a
49 /// surface is settled when that surface is configured, not per frame. The
50 /// caller decides what becomes of the frame, and skipping it is the only
51 /// answer available — `frust-render`'s engine arm releases the acquired
52 /// texture unpresented, counts the refusal and logs `reason` rate-limited,
53 /// leaving whatever was presented last on the screen.
54 #[error("scheduler escalation: {reason}")]
55 SchedulerEscalation {
56 /// What the scheduler found that it does not serve.
57 reason: String,
58 },
59
60 #[error("alpha capacity exhausted")]
61 AlphaCapacity,
62
63 #[error("encoded-paint capacity exhausted")]
64 PaintCapacity,
65}