inset_embedder/scene/mod.rs
1//! Flutter counterpart: dart:ui `Scene` and `SceneBuilder`, and the engine's layer tree they
2//! build (`flow/layers`).
3//!
4//! A frame's scene keeps its layers. The framework's layers build a tree of containers, pictures
5//! and composition layers through the builder (`builder.rs`, `layer.rs`), and a host gets that
6//! tree itself in [`View::present`](crate::View::present). What a host composites, the drawing
7//! between composition layers and each of them with the transforms, clips and opacity above it
8//! (`placement.rs`), is
9//! what [`slices`] makes of the tree (`slices.rs`): a utility every host calls today, which a
10//! host needing a walk of its own does without. valo only draws: what a container does to its
11//! children is replayed into valo's lists by the walk, and nothing of compositing enters them.
12//!
13//! The invariant a later editor must keep: the tree is the rawest record of a frame, built
14//! without computing anything, and every answer a host needs is worked out from it in one place.
15
16mod builder;
17mod layer;
18mod placement;
19mod slices;
20
21pub use builder::SceneBuilder;
22pub use layer::{
23 ContainerLayer, DisplayListLayer, Effect, EmbeddedLayer, EngineLayer, Layer, VideoLayer,
24};
25pub use placement::{Mutator, Placement};
26pub use slices::{Part, Slices, slices};
27
28/// Flutter `Scene`: what [`SceneBuilder::build`] produces and [`crate::View::present`] takes, the
29/// root of one frame's layer tree, flow's `LayerTree`. The view knows its own size.
30///
31/// Two scenes are equal when they show the same: what a host asks to tell a scene equal to the
32/// one it shows from one worth drawing. The comparison takes the time of the containers the frame
33/// built afresh, since a retained container compares by pointer and a picture by valo's identity
34/// first.
35#[derive(Debug, PartialEq)]
36pub struct Scene {
37 /// The root container, whose effect is [`Effect::None`].
38 pub root: ContainerLayer,
39}
40
41/// A composition layer no producer feeds, for the tests of this module. The crates above the
42/// embedder use `test_support`'s, which this crate's own tests are built without.
43#[cfg(test)]
44fn idle_layer() -> std::sync::Arc<dyn crate::CompositionLayer> {
45 struct IdleLayer;
46
47 impl crate::CompositionLayer for IdleLayer {
48 fn present(
49 &self,
50 _buffer: crate::HardwareBuffer,
51 _children: Vec<(
52 std::sync::Arc<dyn crate::CompositionLayer>,
53 crate::Placement,
54 )>,
55 _released: Box<dyn FnOnce() + Send + Sync>,
56 ) {
57 }
58 }
59
60 std::sync::Arc::new(IdleLayer)
61}
62
63#[cfg(test)]
64mod tests {
65 use std::sync::Arc;
66
67 use super::*;
68 use crate::{Canvas, Offset, Paint, Rect};
69
70 fn square() -> Arc<crate::Picture> {
71 let mut canvas = Canvas::new();
72 canvas.draw_rect(Rect::from_ltwh(0.0, 0.0, 10.0, 10.0), &Paint::default());
73 Arc::new(canvas.build())
74 }
75
76 /// A scene crosses threads, as a host drawing on a thread of its own hands it there, wherever
77 /// valo's pictures do: not on the web, whose GPU objects stay on their thread.
78 #[cfg(not(target_arch = "wasm32"))]
79 #[test]
80 fn a_scene_crosses_threads() {
81 fn sendable<T: Send + Sync>() {}
82 sendable::<Scene>();
83 }
84
85 #[test]
86 fn a_scene_that_retains_a_container_equals_one_that_built_the_same() {
87 let picture = square();
88 let mut builder = SceneBuilder::new();
89 builder.push_offset(1.0, 2.0);
90 builder.add_picture(Offset::ZERO, &picture, false, false);
91 let retained = builder.pop();
92 let first = builder.build();
93
94 let mut builder = SceneBuilder::new();
95 builder.add_retained(&retained);
96 assert_eq!(builder.build(), first);
97
98 let mut builder = SceneBuilder::new();
99 builder.push_offset(1.0, 2.0);
100 builder.add_picture(Offset::ZERO, &square(), false, false);
101 builder.pop();
102 assert_eq!(
103 builder.build(),
104 first,
105 "an equal picture in a new container"
106 );
107
108 let mut builder = SceneBuilder::new();
109 builder.push_offset(1.0, 3.0);
110 builder.add_picture(Offset::ZERO, &picture, false, false);
111 builder.pop();
112 assert_ne!(builder.build(), first, "the same picture elsewhere");
113 }
114}