1use std::error::Error;
2use std::fmt;
3
4use crate::scene::{ElementKind, SourceField};
5use crate::{
6 CompatibilityDiagnostic, ErrorCode, ErrorDescriptor, FrameRenderRequest, RenderError,
7 RenderOutcome, SceneDocument,
8};
9
10#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
12#[non_exhaustive]
13pub enum FrameKind {
14 Frame,
16 MagicFrame,
18}
19
20impl FrameKind {
21 pub const fn as_str(self) -> &'static str {
23 match self {
24 Self::Frame => "frame",
25 Self::MagicFrame => "magicframe",
26 }
27 }
28}
29
30impl fmt::Display for FrameKind {
31 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
32 formatter.write_str(self.as_str())
33 }
34}
35
36#[derive(Clone, Copy)]
38pub struct FrameRef<'scene> {
39 pub(crate) scene: &'scene SceneDocument,
40 pub(crate) source_index: usize,
41 ordinal: u32,
42 kind: FrameKind,
43 id: Option<&'scene str>,
44 name: Option<&'scene str>,
45}
46
47impl FrameRef<'_> {
48 pub const fn ordinal(self) -> u32 {
50 self.ordinal
51 }
52
53 pub const fn kind(self) -> FrameKind {
55 self.kind
56 }
57
58 pub(crate) fn belongs_to(self, scene: &SceneDocument) -> bool {
59 self.scene.same_identity(scene)
60 }
61}
62
63impl<'scene> FrameRef<'scene> {
64 pub const fn id(self) -> Option<&'scene str> {
71 self.id
72 }
73
74 pub const fn name(self) -> Option<&'scene str> {
76 self.name
77 }
78}
79
80impl PartialEq for FrameRef<'_> {
84 fn eq(&self, other: &Self) -> bool {
85 self.belongs_to(other.scene) && self.source_index == other.source_index
86 }
87}
88
89impl Eq for FrameRef<'_> {}
90
91impl std::hash::Hash for FrameRef<'_> {
92 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
93 self.scene.hash_identity(state);
94 self.source_index.hash(state);
95 }
96}
97
98impl fmt::Debug for FrameRef<'_> {
99 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
100 formatter
101 .debug_struct("FrameRef")
102 .field("ordinal", &self.ordinal)
103 .field("kind", &self.kind)
104 .field("id", &self.id)
105 .field("name", &self.name)
106 .finish_non_exhaustive()
107 }
108}
109
110#[derive(Clone, Debug)]
112pub struct FrameRefs<'scene> {
113 scene: &'scene SceneDocument,
114 source_index: usize,
115 ordinal: u32,
116}
117
118impl<'scene> FrameRefs<'scene> {
119 pub(crate) const fn new(scene: &'scene SceneDocument) -> Self {
120 Self {
121 scene,
122 source_index: 0,
123 ordinal: 0,
124 }
125 }
126}
127
128impl<'scene> Iterator for FrameRefs<'scene> {
129 type Item = FrameRef<'scene>;
130
131 fn next(&mut self) -> Option<Self::Item> {
132 while self.source_index < self.scene.element_occurrence_count() {
133 let source_index = self.source_index;
134 self.source_index += 1;
135 if self.scene.element_is_silent(source_index) {
136 continue;
137 }
138 let Some(source) = self.scene.element_at(source_index) else {
139 continue;
140 };
141 let kind = match source.kind {
142 ElementKind::Frame => FrameKind::Frame,
143 ElementKind::MagicFrame => FrameKind::MagicFrame,
144 _ => continue,
145 };
146 self.ordinal = self.ordinal.saturating_add(1);
147 let id = match source.id {
148 SourceField::Value(id) if !id.is_empty() => Some(id),
149 SourceField::Value(_) | SourceField::Missing | SourceField::Invalid => None,
150 };
151 let name = match source.name {
152 SourceField::Value(name) => name,
153 SourceField::Missing | SourceField::Invalid => None,
154 };
155 return Some(FrameRef {
156 scene: self.scene,
157 source_index,
158 ordinal: self.ordinal,
159 kind,
160 id,
161 name,
162 });
163 }
164 None
165 }
166}
167
168#[derive(Clone, Copy, Debug, Eq, PartialEq)]
170#[non_exhaustive]
171pub enum FrameBatchErrorKind {
172 ForeignTarget,
174 DuplicateTarget,
176 SharedPreparationFailed,
178}
179
180#[derive(Clone, Debug)]
185pub struct FrameBatchError {
186 kind: FrameBatchErrorKind,
187 ordinal: Option<u32>,
188 preparation_subject: Option<&'static str>,
189}
190
191impl FrameBatchError {
192 pub(crate) const fn new(kind: FrameBatchErrorKind, ordinal: u32) -> Self {
193 Self {
194 kind,
195 ordinal: Some(ordinal),
196 preparation_subject: None,
197 }
198 }
199
200 pub(crate) const fn preparation(error: &crate::render::FramePreparationError) -> Self {
201 Self::from_preparation_subject(error.subject())
202 }
203
204 const fn from_preparation_subject(subject: &'static str) -> Self {
205 Self {
206 kind: FrameBatchErrorKind::SharedPreparationFailed,
207 ordinal: None,
208 preparation_subject: Some(subject),
209 }
210 }
211
212 pub const fn kind(&self) -> FrameBatchErrorKind {
214 self.kind
215 }
216
217 pub const fn ordinal(&self) -> Option<u32> {
219 self.ordinal
220 }
221
222 pub const fn code(&self) -> ErrorCode {
224 match self.kind {
225 FrameBatchErrorKind::ForeignTarget => ErrorCode::FRAME_BATCH_FOREIGN_TARGET,
226 FrameBatchErrorKind::DuplicateTarget => ErrorCode::FRAME_BATCH_DUPLICATE_TARGET,
227 FrameBatchErrorKind::SharedPreparationFailed => {
228 ErrorCode::FRAME_BATCH_SHARED_PREPARATION_FAILED
229 }
230 }
231 }
232
233 pub fn descriptor(&self) -> ErrorDescriptor {
235 ErrorDescriptor::frame_batch(self.code(), self.to_string())
236 }
237}
238
239impl fmt::Display for FrameBatchError {
240 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
241 match self.kind {
242 FrameBatchErrorKind::ForeignTarget => {
243 write!(
244 formatter,
245 "frame {} belongs to another scene",
246 self.ordinal.unwrap_or(0)
247 )
248 }
249 FrameBatchErrorKind::DuplicateTarget => {
250 write!(
251 formatter,
252 "frame {} was selected more than once",
253 self.ordinal.unwrap_or(0)
254 )
255 }
256 FrameBatchErrorKind::SharedPreparationFailed => write!(
257 formatter,
258 "shared frame preparation failed for {}",
259 self.preparation_subject.unwrap_or("unknown_subject")
260 ),
261 }
262 }
263}
264
265impl Error for FrameBatchError {}
266
267#[derive(Debug)]
269pub struct FrameRenderEntry<'scene> {
270 frame: FrameRef<'scene>,
271 result: Result<RenderOutcome, RenderError>,
272 target_diagnostics: Vec<CompatibilityDiagnostic>,
273}
274
275impl<'scene> FrameRenderEntry<'scene> {
276 pub const fn frame(&self) -> FrameRef<'scene> {
278 self.frame
279 }
280
281 pub const fn result(&self) -> &Result<RenderOutcome, RenderError> {
283 &self.result
284 }
285
286 pub fn target_diagnostics(&self) -> &[CompatibilityDiagnostic] {
288 &self.target_diagnostics
289 }
290
291 pub fn into_result(self) -> Result<RenderOutcome, RenderError> {
298 self.result
299 }
300}
301
302pub struct FrameRenderCursor<'scene, 'request> {
304 catalog: &'static crate::catalog::RevisionCatalog,
305 frames: Vec<FrameRef<'scene>>,
306 request: &'request FrameRenderRequest,
307 prepared: Option<crate::render::PreparedFrameRender>,
308 next_index: usize,
309 global_diagnostics: Vec<CompatibilityDiagnostic>,
310 global_diagnostics_initialized: bool,
311}
312
313impl fmt::Debug for FrameRenderCursor<'_, '_> {
314 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
315 formatter
316 .debug_struct("FrameRenderCursor")
317 .field("frame_count", &self.frames.len())
318 .field("next_index", &self.next_index)
319 .field("prepared", &self.prepared.is_some())
320 .finish_non_exhaustive()
321 }
322}
323
324impl<'scene, 'request> FrameRenderCursor<'scene, 'request> {
325 pub(crate) fn new(
326 catalog: &'static crate::catalog::RevisionCatalog,
327 scene: &'scene SceneDocument,
328 frames: impl IntoIterator<Item = FrameRef<'scene>>,
329 request: &'request FrameRenderRequest,
330 ) -> Result<Self, FrameBatchError> {
331 let mut selected = Vec::new();
332 for frame in frames {
333 selected
334 .try_reserve(1)
335 .map_err(|_| FrameBatchError::from_preparation_subject("target_selection"))?;
336 selected.push(frame);
337 }
338 let mut frames = selected;
339 for frame in &frames {
340 if !frame.belongs_to(scene) {
341 return Err(FrameBatchError::new(
342 FrameBatchErrorKind::ForeignTarget,
343 frame.ordinal(),
344 ));
345 }
346 }
347 frames.sort_unstable_by_key(|frame| frame.ordinal());
348 if let Some(duplicate) = frames
349 .windows(2)
350 .find(|frames| frames[0].source_index == frames[1].source_index)
351 {
352 return Err(FrameBatchError::new(
353 FrameBatchErrorKind::DuplicateTarget,
354 duplicate[0].ordinal(),
355 ));
356 }
357 let prepared = if frames.is_empty() {
358 None
359 } else {
360 Some(
361 crate::render::PreparedFrameRender::new(
362 catalog.frame_profile(),
363 catalog.scene_surface(),
364 scene,
365 request.as_render_request(),
366 )
367 .map_err(|error| FrameBatchError::preparation(&error))?,
368 )
369 };
370 Ok(Self {
371 catalog,
372 frames,
373 request,
374 prepared,
375 next_index: 0,
376 global_diagnostics: Vec::new(),
377 global_diagnostics_initialized: false,
378 })
379 }
380
381 pub fn global_diagnostics(&self) -> &[CompatibilityDiagnostic] {
383 &self.global_diagnostics
384 }
385}
386
387impl<'scene> Iterator for FrameRenderCursor<'scene, '_> {
388 type Item = FrameRenderEntry<'scene>;
389
390 fn next(&mut self) -> Option<Self::Item> {
391 let frame = *self.frames.get(self.next_index)?;
392 self.next_index += 1;
393 let (result, global_diagnostics, target_diagnostics) = crate::render::render_frame_scoped(
394 self.catalog,
395 frame,
396 self.request,
397 self.prepared.as_ref(),
398 );
399 if !self.global_diagnostics_initialized {
400 self.global_diagnostics = global_diagnostics;
401 self.global_diagnostics_initialized = true;
402 }
403 Some(FrameRenderEntry {
404 frame,
405 result,
406 target_diagnostics,
407 })
408 }
409}