1use std::{
8 any::TypeId,
9 sync::{Arc, OnceLock, Weak},
10};
11
12use smallvec::SmallVec;
13
14use crate::{
15 Command, ComputeCommand, DrawCommand, DrawRegion, FloatRect, LocalToWindow, SampleRegion,
16 component_tree::SceneRope,
17 px::{Px, PxPosition, PxRect, PxSize},
18};
19
20#[derive(Debug, Clone, Copy, PartialEq)]
22pub struct DrawGeometry {
23 pub local_size: PxSize,
25 pub local_to_window: LocalToWindow,
27}
28
29impl DrawGeometry {
30 #[must_use]
32 pub const fn new(local_size: PxSize, local_to_window: LocalToWindow) -> Self {
33 Self {
34 local_size,
35 local_to_window,
36 }
37 }
38
39 #[must_use]
41 pub fn from_position_size(position: PxPosition, local_size: PxSize) -> Self {
42 Self::new(
43 local_size,
44 LocalToWindow::new(1.0, position).expect("finite draw position"),
45 )
46 }
47
48 #[must_use]
50 pub fn window_origin(self) -> PxPosition {
51 self.local_to_window.map_point(PxPosition::ZERO)
52 }
53
54 #[must_use]
56 pub fn window_size(self) -> [f32; 2] {
57 self.local_to_window.map_size(self.local_size)
58 }
59
60 #[must_use]
62 pub fn window_bounds(self) -> FloatRect {
63 self.padded_window_bounds(crate::PaddingRect::ZERO)
64 }
65
66 #[must_use]
68 pub fn padded_window_bounds(self, padding: crate::PaddingRect) -> FloatRect {
69 self.local_to_window.map_rect(FloatRect {
70 x: -padding.left.to_f32(),
71 y: -padding.top.to_f32(),
72 width: self.local_size.width.to_f32() + padding.left.to_f32() + padding.right.to_f32(),
73 height: self.local_size.height.to_f32()
74 + padding.top.to_f32()
75 + padding.bottom.to_f32(),
76 })
77 }
78}
79
80#[derive(Clone)]
82pub struct RenderFragmentOp {
83 pub command: Command,
85 pub type_id: TypeId,
87 pub size_override: Option<PxSize>,
89 pub position_override: Option<PxPosition>,
91}
92
93#[derive(Default, Clone)]
95pub struct RenderFragment {
96 ops: Option<Arc<SmallVec<[RenderFragmentOp; 1]>>>,
97}
98
99impl RenderFragment {
100 #[must_use]
102 pub fn ops(&self) -> &[RenderFragmentOp] {
103 self.ops.as_deref().map_or(&[], SmallVec::as_slice)
104 }
105
106 #[must_use]
108 pub fn ops_mut(&mut self) -> &mut [RenderFragmentOp] {
109 self.ops
110 .as_mut()
111 .map_or(&mut [], |ops| Arc::make_mut(ops).as_mut_slice())
112 }
113
114 pub fn push_draw_command<C: DrawCommand + 'static>(&mut self, command: C) -> u32 {
116 self.push_op(Command::Draw(Box::new(command)), TypeId::of::<C>())
117 }
118
119 pub fn push_compute_command<C: ComputeCommand + 'static>(&mut self, command: C) -> u32 {
121 self.push_op(Command::Compute(Box::new(command)), TypeId::of::<C>())
122 }
123
124 fn push_op(&mut self, command: Command, type_id: TypeId) -> u32 {
125 let ops = Arc::make_mut(self.ops.get_or_insert_with(Arc::default));
126 let index = ops.len() as u32;
127 ops.push(RenderFragmentOp {
128 command,
129 type_id,
130 size_override: None,
131 position_override: None,
132 });
133 index
134 }
135
136 pub fn clear(&mut self) {
138 if let Some(ops) = self.ops.as_mut().and_then(Arc::get_mut) {
139 ops.clear();
140 } else {
141 self.ops = None;
142 }
143 }
144
145 pub(crate) fn shares_ops(&self, other: &Self) -> bool {
146 match (&self.ops, &other.ops) {
147 (Some(a), Some(b)) => Arc::ptr_eq(a, b),
148 (None, None) => true,
149 _ => false,
150 }
151 }
152
153 pub(crate) fn supports_partial_redraw(&self) -> bool {
154 self.ops().iter().all(|op| partial_redraw_safe(&op.command))
155 }
156}
157
158#[derive(Clone)]
160pub struct RenderGraphOp {
161 pub command: Command,
163 pub type_id: TypeId,
165 pub geometry: DrawGeometry,
167 pub opacity: f32,
169 pub sequence_index: usize,
171}
172
173#[derive(Clone, Default)]
175pub struct RenderGraph {
176 data: Arc<RenderGraphData>,
177 damage: Option<PxRect>,
178 previous: Option<Weak<RenderGraphData>>,
179}
180
181#[derive(Default)]
182struct RenderGraphData {
183 order_plan: OnceLock<Arc<RenderOrderPlan>>,
184 scene: Option<Arc<SceneRope>>,
185 flat: OnceLock<FlatRenderGraph>,
186}
187
188#[derive(Default)]
189struct FlatRenderGraph {
190 ops: Vec<RenderGraphOp>,
191 partial_redraw: bool,
192}
193
194impl RenderGraphData {
195 fn flat(&self) -> &FlatRenderGraph {
196 self.flat.get_or_init(|| {
197 let Some(scene) = &self.scene else {
198 return FlatRenderGraph::default();
199 };
200 let mut builder = RenderGraphBuilder::new();
201 scene.emit(&mut builder, None);
202 builder.into_flat()
203 })
204 }
205
206 fn supports_partial_redraw(&self) -> bool {
207 self.flat
208 .get()
209 .map_or(self.scene.is_some(), |flat| flat.partial_redraw)
210 }
211}
212
213struct RenderOrderPlan {
214 inputs: Box<[OpInfo]>,
215 indices: Box<[usize]>,
216}
217
218impl RenderOrderPlan {
219 fn new(ops: &[RenderGraphOp]) -> Self {
220 let inputs: Box<[_]> = ops.iter().map(OpInfo::new).collect();
221 let indices = order_indices(ops, &inputs).into_boxed_slice();
222 Self { inputs, indices }
223 }
224
225 fn matches(&self, ops: &[RenderGraphOp]) -> bool {
226 self.inputs.len() == ops.len()
227 && self
228 .inputs
229 .iter()
230 .zip(ops)
231 .all(|(input, op)| *input == OpInfo::new(op))
232 }
233}
234
235impl RenderGraph {
236 pub(crate) fn from_partial_scene(scene: Arc<SceneRope>) -> Self {
237 Self {
238 data: Arc::new(RenderGraphData {
239 scene: Some(scene),
240 ..RenderGraphData::default()
241 }),
242 damage: None,
243 previous: None,
244 }
245 }
246
247 pub(crate) fn with_damage(mut self, damage: Option<PxRect>, previous: Option<&Self>) -> Self {
248 self.damage = damage;
249 self.previous = previous.map(|graph| Arc::downgrade(&graph.data));
250 self
251 }
252
253 pub(crate) fn damage_since(&self, previous: Option<&Self>) -> Option<PxRect> {
254 let previous = previous?;
255 if !self.data.supports_partial_redraw() || !previous.data.supports_partial_redraw() {
256 return None;
257 }
258 if Arc::ptr_eq(&self.data, &previous.data) {
259 return Some(PxRect::ZERO);
260 }
261 if self
262 .previous
263 .as_ref()
264 .is_some_and(|data| std::ptr::eq(data.as_ptr(), Arc::as_ptr(&previous.data)))
265 {
266 self.damage
267 } else {
268 None
270 }
271 }
272
273 pub(crate) fn for_damage(&self, damage: PxRect) -> Option<Self> {
274 let scene = self.data.scene.as_ref()?;
275 let mut builder = RenderGraphBuilder::new();
276 scene.emit(&mut builder, Some(damage));
277 Some(builder.finish())
278 }
279
280 pub(crate) fn supports_partial_redraw(&self) -> bool {
281 self.data.supports_partial_redraw()
282 }
283
284 #[must_use]
286 pub fn ops(&self) -> &[RenderGraphOp] {
287 &self.data.flat().ops
288 }
289
290 fn order_plan(&self, previous: Option<&Self>) -> &Arc<RenderOrderPlan> {
291 self.data.order_plan.get_or_init(|| {
292 if let Some(plan) = previous.and_then(|graph| graph.data.order_plan.get())
293 && plan.matches(self.ops())
294 {
295 Arc::clone(plan)
296 } else {
297 Arc::new(RenderOrderPlan::new(self.ops()))
298 }
299 })
300 }
301
302 pub(crate) fn ordered_ops(
303 &self,
304 previous: Option<&Self>,
305 ) -> impl Iterator<Item = &RenderGraphOp> {
306 let ops = self.ops();
307 self.order_plan(previous)
308 .indices
309 .iter()
310 .map(|&index| &ops[index])
311 }
312}
313
314pub(crate) struct RenderGraphBuilder {
316 ops: Vec<RenderGraphOp>,
317}
318
319impl RenderGraphBuilder {
320 pub(crate) fn new() -> Self {
322 Self { ops: Vec::new() }
323 }
324
325 fn push_state(&mut self, command: Command) {
326 self.ops.push(RenderGraphOp {
327 command,
328 type_id: TypeId::of::<Command>(),
329 geometry: DrawGeometry::new(PxSize::ZERO, LocalToWindow::IDENTITY),
330 opacity: 1.0,
331 sequence_index: self.ops.len(),
332 });
333 }
334
335 pub(crate) fn push_clip_push(&mut self, rect: PxRect) {
337 self.push_state(Command::ClipPush(rect));
338 }
339
340 pub(crate) fn push_shape_clip_push(&mut self, region: crate::ClipRegion) {
342 self.push_state(Command::ShapeClipPush(region));
343 }
344
345 pub(crate) fn push_clip_pop(&mut self) {
347 self.push_state(Command::ClipPop);
348 }
349
350 pub(crate) fn append_fragment(
352 &mut self,
353 fragment: &RenderFragment,
354 geometry: DrawGeometry,
355 opacity: f32,
356 ) {
357 for op in fragment.ops() {
358 let mut command = op.command.clone();
359 if let Command::Draw(ref mut command) = command {
360 command.apply_opacity(opacity);
361 }
362 let mapping = geometry
363 .local_to_window
364 .with_local_origin(op.position_override.unwrap_or(PxPosition::ZERO));
365 self.ops.push(RenderGraphOp {
366 command,
367 type_id: op.type_id,
368 geometry: DrawGeometry::new(
369 op.size_override.unwrap_or(geometry.local_size),
370 mapping,
371 ),
372 opacity,
373 sequence_index: self.ops.len(),
374 });
375 }
376 }
377
378 pub(crate) fn finish(self) -> RenderGraph {
380 RenderGraph {
381 data: Arc::new(RenderGraphData {
382 flat: OnceLock::from(self.into_flat()),
383 ..RenderGraphData::default()
384 }),
385 damage: None,
386 previous: None,
387 }
388 }
389
390 fn into_flat(self) -> FlatRenderGraph {
391 let partial_redraw = self.ops.iter().all(|op| partial_redraw_safe(&op.command));
392 FlatRenderGraph {
393 ops: self.ops,
394 partial_redraw,
395 }
396 }
397}
398
399fn partial_redraw_safe(command: &Command) -> bool {
400 match command {
401 Command::Draw(command) => {
402 command.sample_region().is_none() && command.supports_partial_redraw()
403 }
404 Command::Compute(_) => false,
405 _ => true,
406 }
407}
408mod ordering;
409#[cfg(test)]
410mod retained_order_tests;
411
412use ordering::order_indices;
413
414#[cfg(test)]
415fn order_ops(ops: Vec<RenderGraphOp>) -> Vec<RenderGraphOp> {
416 let infos: Vec<_> = ops.iter().map(OpInfo::new).collect();
417 order_indices(&ops, &infos)
418 .into_iter()
419 .map(|index| ops[index].clone())
420 .collect()
421}
422
423#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
424enum OpCategory {
425 ContinuationDraw,
426 BarrierDraw,
427 Compute,
428 StateChange,
429}
430
431#[derive(PartialEq, Eq)]
432struct OpInfo {
433 category: OpCategory,
434 command_kind: std::mem::Discriminant<Command>,
435 type_id: TypeId,
436 sequence_index: usize,
437 read_rect: Option<PxRect>,
438 write_rect: Option<PxRect>,
439}
440
441impl OpInfo {
442 fn new(op: &RenderGraphOp) -> Self {
443 let category = match &op.command {
444 Command::Draw(command) => {
445 if command.sample_region().is_some() {
446 OpCategory::BarrierDraw
447 } else {
448 OpCategory::ContinuationDraw
449 }
450 }
451 Command::Compute(_) => OpCategory::Compute,
452 Command::ClipPush(_) | Command::ShapeClipPush(_) | Command::ClipPop => {
453 OpCategory::StateChange
454 }
455 };
456
457 Self {
458 category,
459 command_kind: std::mem::discriminant(&op.command),
460 type_id: op.type_id,
461 sequence_index: op.sequence_index,
462 read_rect: scene_read_rect(op),
463 write_rect: scene_write_rect(op),
464 }
465 }
466}
467
468fn scene_read_rect(op: &RenderGraphOp) -> Option<PxRect> {
469 match &op.command {
470 Command::Draw(command) => command
471 .sample_region()
472 .map(|region| sample_region_rect(region, op.geometry)),
473 Command::Compute(command) => Some(sample_region_rect(command.barrier(), op.geometry)),
474 Command::ClipPush(_) | Command::ShapeClipPush(_) | Command::ClipPop => None,
475 }
476}
477
478fn scene_write_rect(op: &RenderGraphOp) -> Option<PxRect> {
479 match &op.command {
480 Command::Draw(command) => Some(
481 command
482 .ordering_rect(op.geometry)
483 .unwrap_or_else(|| draw_region_rect(command.draw_region(), op.geometry)),
484 ),
485 Command::Compute(_) => Some(op.geometry.window_bounds().outward()),
486 Command::ClipPush(_) | Command::ShapeClipPush(_) | Command::ClipPop => None,
487 }
488}
489
490fn sample_region_rect(region: SampleRegion, geometry: DrawGeometry) -> PxRect {
491 match region {
492 SampleRegion::Global => PxRect::new(Px::ZERO, Px::ZERO, Px::MAX, Px::MAX),
493 SampleRegion::PaddedLocal(padding) => geometry.padded_window_bounds(padding).outward(),
494 SampleRegion::Absolute(rect) => rect,
495 }
496}
497
498fn draw_region_rect(region: DrawRegion, geometry: DrawGeometry) -> PxRect {
499 match region {
500 DrawRegion::Global => PxRect::new(Px::ZERO, Px::ZERO, Px::MAX, Px::MAX),
501 DrawRegion::PaddedLocal(padding) => geometry.padded_window_bounds(padding).outward(),
502 DrawRegion::Absolute(rect) => rect,
503 }
504}
505
506fn is_state_change(op: &RenderGraphOp) -> bool {
507 matches!(
508 op.command,
509 Command::ClipPush(_) | Command::ShapeClipPush(_) | Command::ClipPop
510 )
511}
512
513#[cfg(test)]
514mod tests {
515 use super::*;
516 use crate::{
517 PaddingRect, Px, PxPosition, PxSize, SampleRegion, renderer::drawer::command::DrawCommand,
518 };
519
520 #[test]
521 fn fragment_overrides_map_through_the_node_transform_once() {
522 let _runtime_session = crate::RuntimeSession::new();
523 let _runtime_scope = _runtime_session.enter();
524 let mut fragment = RenderFragment::default();
525 fragment.push_draw_command(ProbeDraw {
526 id: 1,
527 sample: None,
528 });
529 for op in fragment.ops_mut() {
530 op.size_override = Some(PxSize::new(Px(64), Px(32)));
531 op.position_override = Some(PxPosition::from_f32_xy(4.0, 8.0));
532 }
533 let mut builder = RenderGraphBuilder::new();
534 builder.append_fragment(
535 &fragment,
536 DrawGeometry::new(
537 PxSize::new(Px(240), Px(56)),
538 LocalToWindow::new(0.625, PxPosition::from_f32_xy(10.25, 20.5)).unwrap(),
539 ),
540 1.0,
541 );
542 let graph = builder.finish();
543 let scene = &graph.ops()[0];
544 assert_eq!(scene.geometry.local_size, PxSize::new(Px(64), Px(32)));
545 assert_eq!(
546 scene.geometry.window_origin(),
547 PxPosition::from_f32_xy(12.75, 25.5)
548 );
549 assert_eq!(scene.geometry.window_size(), [40.0, 20.0]);
550 }
551
552 #[derive(Clone)]
553 struct ProbeDraw {
554 id: u32,
555 sample: Option<SampleRegion>,
556 }
557
558 impl DrawCommand for ProbeDraw {
559 fn supports_partial_redraw(&self) -> bool {
560 true
561 }
562
563 fn sample_region(&self) -> Option<SampleRegion> {
564 self.sample
565 }
566
567 fn apply_opacity(&mut self, _opacity: f32) {}
568 }
569
570 #[test]
571 fn damage_requires_the_rendered_predecessor_and_safe_contents_in_both_frames() {
572 #[derive(Clone)]
573 struct ResourceDraw {
574 _resource: Arc<()>,
575 }
576
577 impl DrawCommand for ResourceDraw {
578 fn supports_partial_redraw(&self) -> bool {
579 true
580 }
581
582 fn apply_opacity(&mut self, _: f32) {}
583 }
584
585 #[derive(Clone)]
586 struct UntrackedResource;
587
588 impl DrawCommand for UntrackedResource {
589 fn apply_opacity(&mut self, _: f32) {}
590 }
591
592 #[derive(Clone)]
593 struct Filter;
594
595 impl ComputeCommand for Filter {
596 fn barrier(&self) -> SampleRegion {
597 SampleRegion::Global
598 }
599 }
600
601 let geometry =
602 DrawGeometry::from_position_size(PxPosition::ZERO, PxSize::new(Px(8), Px(8)));
603 let resource = Arc::new(());
604 let resource_lifetime = Arc::downgrade(&resource);
605 let first = {
606 let mut fragment = RenderFragment::default();
607 fragment.push_draw_command(ResourceDraw {
608 _resource: resource,
609 });
610 let mut builder = RenderGraphBuilder::new();
611 builder.append_fragment(&fragment, geometry, 1.0);
612 builder.finish()
613 };
614 let damage = PxRect::new(Px(2), Px(3), Px(4), Px(5));
615 let second = RenderGraphBuilder::new()
616 .finish()
617 .with_damage(Some(damage), Some(&first));
618 assert_eq!(
619 second.damage_since(None),
620 None,
621 "new targets need all pixels"
622 );
623 assert_eq!(second.damage_since(Some(&first)), Some(damage));
624 assert_eq!(second.damage_since(Some(&second)), Some(PxRect::ZERO));
625 let third = RenderGraphBuilder::new()
626 .finish()
627 .with_damage(Some(damage), Some(&second));
628 assert_eq!(
629 third.damage_since(Some(&first)),
630 None,
631 "skipping a scene update loses its damage"
632 );
633
634 let sampled = RenderGraphBuilder {
635 ops: vec![padded_sample_op(1, 0, 0, 0, 10, 10, 8)],
636 }
637 .finish()
638 .with_damage(Some(damage), Some(&first));
639 assert_eq!(
640 sampled.damage_since(Some(&first)),
641 None,
642 "background effects need full replay"
643 );
644 let removed = RenderGraphBuilder::new()
645 .finish()
646 .with_damage(Some(damage), Some(&sampled));
647 assert_eq!(
648 removed.damage_since(Some(&sampled)),
649 None,
650 "removing an effect must discard its output too"
651 );
652 let mut fragment = RenderFragment::default();
653 fragment.push_draw_command(UntrackedResource);
654 let mut builder = RenderGraphBuilder::new();
655 builder.append_fragment(&fragment, geometry, 1.0);
656 let unknown = builder.finish().with_damage(Some(damage), Some(&first));
657 assert_eq!(
658 unknown.damage_since(Some(&first)),
659 None,
660 "custom commands must explicitly opt in"
661 );
662 fragment.clear();
663 fragment.push_compute_command(Filter);
664 let mut builder = RenderGraphBuilder::new();
665 builder.append_fragment(&fragment, geometry, 1.0);
666 let computed = builder.finish().with_damage(Some(damage), Some(&first));
667 assert_eq!(
668 computed.damage_since(Some(&first)),
669 None,
670 "compute work must fully reconstruct its input scene"
671 );
672 drop(first);
673 assert!(
674 resource_lifetime.upgrade().is_none(),
675 "damage history must not keep prior draw resources alive"
676 );
677 }
678
679 fn draw_op(id: u32, sequence: usize, x: i32, y: i32, w: i32, h: i32) -> RenderGraphOp {
680 RenderGraphOp {
681 command: Command::Draw(Box::new(ProbeDraw { id, sample: None })),
682 type_id: TypeId::of::<ProbeDraw>(),
683 geometry: DrawGeometry::from_position_size(
684 PxPosition::new(Px::new(x), Px::new(y)),
685 PxSize::new(Px::new(w), Px::new(h)),
686 ),
687 opacity: 1.0,
688 sequence_index: sequence,
689 }
690 }
691
692 fn padded_sample_op(
693 id: u32,
694 sequence: usize,
695 x: i32,
696 y: i32,
697 w: i32,
698 h: i32,
699 pad: i32,
700 ) -> RenderGraphOp {
701 let mut op = draw_op(id, sequence, x, y, w, h);
702 if let Command::Draw(command) = &mut op.command {
703 let probe = command.downcast_mut::<ProbeDraw>().expect("probe draw");
704 probe.sample = Some(SampleRegion::PaddedLocal(PaddingRect::uniform(Px::new(
705 pad,
706 ))));
707 }
708 op
709 }
710
711 fn clip_push(sequence: usize) -> RenderGraphOp {
712 RenderGraphOp {
713 command: Command::ClipPush(PxRect::new(Px::ZERO, Px::ZERO, Px::new(10), Px::new(10))),
714 type_id: TypeId::of::<Command>(),
715 geometry: DrawGeometry::new(PxSize::ZERO, LocalToWindow::IDENTITY),
716 opacity: 1.0,
717 sequence_index: sequence,
718 }
719 }
720
721 fn clip_pop(sequence: usize) -> RenderGraphOp {
722 RenderGraphOp {
723 command: Command::ClipPop,
724 type_id: TypeId::of::<Command>(),
725 geometry: DrawGeometry::new(PxSize::ZERO, LocalToWindow::IDENTITY),
726 opacity: 1.0,
727 sequence_index: sequence,
728 }
729 }
730
731 fn probe_id(op: &RenderGraphOp) -> u32 {
732 match &op.command {
733 Command::Draw(command) => command.downcast_ref::<ProbeDraw>().expect("probe").id,
734 _ => u32::MAX,
735 }
736 }
737
738 #[test]
739 fn disjoint_clip_siblings_keep_every_draw_in_its_original_clip_scope() {
740 let _runtime_session = crate::RuntimeSession::new();
741 let _runtime_scope = _runtime_session.enter();
742 let mut ops = Vec::new();
743 let mut sequence = 0usize;
744 for clip in 0..8 {
745 ops.push(clip_push(sequence));
746 sequence += 1;
747 let x = (clip as i32) * 100;
748 ops.push(draw_op(clip * 2, sequence, x, 0, 20, 20));
749 sequence += 1;
750 ops.push(draw_op(clip * 2 + 1, sequence, x + 5, 5, 20, 20));
751 sequence += 1;
752 ops.push(clip_pop(sequence));
753 sequence += 1;
754 }
755
756 let ordered = order_ops(ops);
757 assert_eq!(ordered.len(), 32);
758 for (scope, commands) in ordered.chunks_exact(4).enumerate() {
759 assert!(matches!(commands[0].command, Command::ClipPush(_)));
760 assert_eq!(probe_id(&commands[1]), (scope * 2) as u32);
761 assert_eq!(probe_id(&commands[2]), (scope * 2 + 1) as u32);
762 assert!(matches!(commands[3].command, Command::ClipPop));
763 }
764 }
765
766 #[test]
767 fn padded_local_sampling_cannot_move_before_the_draw_it_samples() {
768 let _runtime_session = crate::RuntimeSession::new();
769 let _runtime_scope = _runtime_session.enter();
770 let mut ops = vec![draw_op(1, 0, 14, 0, 10, 10)];
771 for i in 1..24 {
772 ops.push(draw_op(i as u32 + 2, i, i as i32 * 50, 0, 10, 10));
773 }
774 ops.push(padded_sample_op(2, 24, 0, 0, 10, 10, 8));
775
776 let ordered = order_ops(ops);
777 assert_eq!(probe_id(&ordered[0]), 1);
778 assert_eq!(probe_id(&ordered[1]), 2);
779 }
780
781 #[test]
782 fn touching_a_sample_region_does_not_prevent_reordering() {
783 let _runtime_session = crate::RuntimeSession::new();
784 let _runtime_scope = _runtime_session.enter();
785 let earlier = draw_op(1, 0, 18, 0, 10, 10);
786 let later = padded_sample_op(2, 1, 0, 0, 10, 10, 8);
787 let ordered = order_ops(vec![earlier, later]);
788 assert_eq!(probe_id(&ordered[0]), 2);
789 assert_eq!(probe_id(&ordered[1]), 1);
790 }
791
792 #[test]
793 fn ready_draws_continue_a_batch_until_an_overlapping_draw_blocks_it() {
794 let _runtime_session = crate::RuntimeSession::new();
795 let _runtime_scope = _runtime_session.enter();
796 let mut ops = vec![
797 draw_op(1, 0, 0, 0, 10, 10),
798 draw_op(2, 1, 20, 0, 10, 10),
799 draw_op(3, 2, 40, 0, 10, 10),
800 draw_op(4, 3, 60, 0, 10, 10),
801 draw_op(5, 4, 20, 0, 10, 10),
802 draw_op(6, 5, 80, 0, 10, 10),
803 ];
804 for index in [1, 3, 5] {
805 ops[index].type_id = TypeId::of::<u32>();
806 }
807
808 let ordered = order_ops(ops);
809 let ids: Vec<_> = ordered.iter().map(probe_id).collect();
810 assert_eq!(ids, [1, 3, 2, 4, 6, 5]);
811 }
812
813 #[test]
814 fn densely_overlapping_draws_fall_back_to_author_order() {
815 let _runtime_session = crate::RuntimeSession::new();
816 let _runtime_scope = _runtime_session.enter();
817 let mut ops = vec![draw_op(0, 0, 100, 0, 10, 10)];
818 ops.push(padded_sample_op(1, 1, 200, 0, 10, 10, 5));
819 for i in 2..1026 {
820 ops.push(draw_op(i as u32, i, 0, 0, 10, 10));
821 }
822
823 let ordered = order_ops(ops);
824 assert!(ordered.iter().map(probe_id).eq(0..1026));
825 }
826
827 #[test]
828 fn unclosed_clip_falls_back_to_sequence() {
829 let _runtime_session = crate::RuntimeSession::new();
830 let _runtime_scope = _runtime_session.enter();
831 let ops = vec![
832 clip_push(0),
833 draw_op(7, 1, 0, 0, 10, 10),
834 draw_op(8, 2, 0, 0, 10, 10),
835 ];
836 let ordered = order_ops(ops);
837 assert_eq!(probe_id(&ordered[1]), 7);
838 assert_eq!(probe_id(&ordered[2]), 8);
839 assert!(matches!(ordered[0].command, Command::ClipPush(_)));
840 }
841}