1use std::fmt;
2
3#[derive(Copy, Clone, Debug, Eq, PartialEq)]
5#[non_exhaustive]
6pub enum RenderError {
7 ZeroDimension,
9 DimensionTooLarge,
11 InvalidColor,
14 InvalidVisual,
16 InvalidEffectParameter,
18 UnknownEffect,
20 InvalidEffectParams,
22 InvalidEffectResource,
24 MissingEffectResource,
26 EffectResourceKindMismatch,
28 MissingEffectInput,
30 MissingText,
33 InvalidImageData,
35 TextureReadback(ReadbackFailure),
37 InvalidGraph,
39 ExternalTextureSync(Option<alpha2_media::GpuFault>),
43 InvalidPlaneTarget,
46 GpuTimeout,
48 GpuWorkDropped,
52 Gpu {
57 kind: alpha2_gpu::GpuErrorKind,
59 },
60 GpuDeviceLost,
63 UnsupportedHdr,
66}
67
68impl fmt::Display for RenderError {
69 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
70 match self {
71 Self::ZeroDimension => formatter.write_str("render dimensions must be non-zero"),
72 Self::DimensionTooLarge => {
73 formatter.write_str("render dimensions exceed the supported texture budget")
74 }
75 Self::InvalidColor => {
76 formatter.write_str(
77 "color components must be finite values from zero to one, premultiplied: none above alpha",
78 )
79 }
80 Self::InvalidVisual => formatter.write_str("layer presentation values are invalid"),
81 Self::InvalidEffectParameter => formatter.write_str("effect parameter is invalid"),
82 Self::UnknownEffect => formatter.write_str("effect is not registered"),
83 Self::InvalidEffectParams => {
84 formatter.write_str("effect parameters do not match schema")
85 }
86 Self::InvalidEffectResource => formatter.write_str("effect resource is invalid"),
87 Self::MissingEffectResource => {
88 formatter.write_str("effect resource is missing or not registered")
89 }
90 Self::MissingText => {
91 formatter.write_str("text was not prepared for this frame and size")
92 }
93 Self::EffectResourceKindMismatch => {
94 formatter.write_str("effect resource kind does not match schema")
95 }
96 Self::MissingEffectInput => formatter.write_str("effect input texture is missing"),
97 Self::InvalidImageData => {
98 formatter.write_str("RGBA byte length does not match the image size")
99 }
100 Self::TextureReadback(failure) => {
101 write!(formatter, "GPU texture readback failed: {failure}")
102 }
103 Self::InvalidGraph => formatter.write_str("render graph is structurally invalid"),
104 Self::ExternalTextureSync(None) => {
105 formatter.write_str("external texture synchronization failed")
106 }
107 Self::ExternalTextureSync(Some(fault)) => write!(
108 formatter,
109 "external texture synchronization failed ({})",
110 fault.as_str()
111 ),
112 Self::InvalidPlaneTarget => formatter.write_str(
113 "NV12 plane targets must be an R8 luma and an RG8 chroma render attachment at the output size and half of it",
114 ),
115 Self::GpuTimeout => formatter.write_str(GPU_TIMEOUT_MESSAGE),
116 Self::GpuWorkDropped => formatter.write_str(GPU_WORK_DROPPED_MESSAGE),
117 Self::Gpu { kind } => write!(
118 formatter,
119 "the GPU rejected this frame's work with a {} error",
120 kind.as_str()
121 ),
122 Self::GpuDeviceLost => formatter.write_str(GPU_DEVICE_LOST_MESSAGE),
123 Self::UnsupportedHdr => {
124 formatter.write_str("the color policy refuses HDR (PQ or HLG) sources")
125 }
126 }
127 }
128}
129
130impl std::error::Error for RenderError {}
131
132#[derive(Copy, Clone, Debug, Eq, PartialEq)]
140#[non_exhaustive]
141pub enum ReadbackFailure {
142 UnsupportedTarget,
144 Layout,
146 DuplicateMapRequest,
148 MapFailed,
150 CallbackDropped,
152 NotMapped,
154 DestinationTooSmall,
156 SubmissionMismatch,
158}
159
160impl ReadbackFailure {
161 #[must_use]
163 pub const fn as_str(self) -> &'static str {
164 match self {
165 Self::UnsupportedTarget => "unsupported-target",
166 Self::Layout => "layout",
167 Self::DuplicateMapRequest => "duplicate-map-request",
168 Self::MapFailed => "map-failed",
169 Self::CallbackDropped => "callback-dropped",
170 Self::NotMapped => "not-mapped",
171 Self::DestinationTooSmall => "destination-too-small",
172 Self::SubmissionMismatch => "submission-mismatch",
173 }
174 }
175}
176
177impl fmt::Display for ReadbackFailure {
178 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
179 formatter.write_str(self.as_str())
180 }
181}
182
183pub use alpha2_media::GPU_TIMEOUT_MESSAGE;
187
188pub const GPU_WORK_DROPPED_MESSAGE: &str = "GPU device lost: it completed submitted work without \
191 running it (a readback lacked its canary), so this process can no longer render";
192
193pub const GPU_DEVICE_LOST_MESSAGE: &str = "GPU device lost: it can no longer make the resources \
195 a frame needs, so this process can no longer render";
196
197pub const DEFAULT_GPU_WAIT_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(30);
200
201#[cfg(not(target_arch = "wasm32"))]
202#[allow(clippy::needless_pass_by_value)] pub(crate) fn poll_error(error: wgpu::PollError) -> RenderError {
204 match error {
205 wgpu::PollError::Timeout => RenderError::GpuTimeout,
206 wgpu::PollError::WrongSubmissionIndex(..) => {
207 RenderError::TextureReadback(ReadbackFailure::SubmissionMismatch)
208 }
209 }
210}