cranpose_render_common/graph.rs
1use std::{
2 cell::Cell,
3 ops::{Deref, DerefMut, Range},
4 rc::Rc,
5 sync::atomic::{AtomicU32, Ordering},
6};
7
8use cranpose_core::{NodeId, collections::map::HashSet};
9use cranpose_ui::{
10 GraphicsLayer, ModifierNodeSlices, Point, Rect, RenderEffect, TextOverflow, TextStyle,
11 text::{AnnotatedString, RenderString},
12};
13pub use cranpose_ui_graphics::transform::{FrontPolygon, ProjectiveTransform, quad_bounds};
14use cranpose_ui_graphics::{
15 BlendMode, ColorFilter, CommandRecording, DrawPlacement, DrawPrimitive, RecordingSummary,
16};
17
18use crate::{
19 glyph_reach::glyph_bounds,
20 raster_cache::LayerRasterCacheHashes,
21 text_paint::{TextPaint, TextPaintCache},
22};
23
24mod heap_bytes;
25use heap_bytes::layer_heap_bytes;
26mod inline_node;
27pub use inline_node::InlineNode;
28
29/// Reasons a layer is drawn into its own offscreen buffer instead of directly into its parent.
30#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
31pub struct IsolationReasons {
32 /// The layer asked for offscreen compositing.
33 pub explicit_offscreen: bool,
34 /// The layer clips to a shape that is not a rectangle.
35 pub shape_clip: bool,
36 /// The layer applies a render effect to its content.
37 pub effect: bool,
38 /// The layer applies an effect to what lies behind it.
39 pub backdrop: bool,
40 /// The layer's alpha is below one and must apply to its content as a group.
41 pub group_opacity: bool,
42 /// The layer uses a blend mode other than source-over.
43 pub blend_mode: bool,
44}
45
46impl IsolationReasons {
47 /// Whether at least one reason requires the layer to be isolated.
48 pub fn has_any(self) -> bool {
49 self.explicit_offscreen
50 || self.shape_clip
51 || self.effect
52 || self.backdrop
53 || self.group_opacity
54 || self.blend_mode
55 }
56}
57
58/// Whether a renderer may keep the rasterized pixels of a layer between frames.
59#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
60pub enum CachePolicy {
61 /// The layer is drawn directly each frame.
62 #[default]
63 None,
64 /// The layer is a candidate for a raster cache because it is isolated, scaled or rotated.
65 Auto,
66}
67
68/// Pointer handling attached to a layer.
69#[derive(Clone)]
70pub struct HitTestNode {
71 /// The node's modifier slices, shared rather than copied: they hold its
72 /// pointer inputs and the pointer icon it asks for while hovered. A node
73 /// that only names an icon is still a hit target, which is how a
74 /// decorative panel carries a cursor without handling clicks.
75 pub handlers: Rc<ModifierNodeSlices>,
76}
77
78/// One draw primitive together with its clip.
79#[derive(Clone, Debug, PartialEq)]
80pub struct DrawPrimitiveNode {
81 /// The shape, image or other primitive to draw.
82 pub primitive: DrawPrimitive,
83 /// Rectangle that cuts the primitive, or `None` when it is not clipped.
84 pub clip: Option<Rect>,
85}
86
87/// A run of text to draw, with the layout facts that a renderer needs.
88#[derive(Clone, Debug, PartialEq)]
89pub struct TextPrimitiveNode {
90 /// Layout node that owns the text.
91 pub node_id: NodeId,
92 /// Box of the laid-out text in logical pixels, after alignment and panning.
93 pub rect: Rect,
94 /// Shared so the renderer can hand the same allocation to every draw it
95 /// emits for this node instead of deep-copying the string once per emit.
96 pub text: Rc<AnnotatedString>,
97 /// `text` as the renderer's draws carry it across threads, shared with
98 /// the prepared layout it came from so no frame converts it again.
99 pub render_text: std::sync::Arc<RenderString>,
100 /// The lines and glyphs the text draws, from its top left corner at
101 /// `rect`'s.
102 pub layout: cranpose_text::TextLayout,
103 /// Shared so every frame's draws of this text hand over the same style
104 /// instead of copying it.
105 pub text_style: std::sync::Arc<TextStyle>,
106 /// Resolved font size in logical pixels.
107 pub font_size: f32,
108 /// How the text was laid out to handle text that does not fit.
109 pub overflow: TextOverflow,
110 /// Rectangle that cuts the glyphs: the node's box when the layout overflows it and overflow
111 /// is not visible, or when the text pans; `None` otherwise, as Compose cuts a text.
112 pub clip: Option<Rect>,
113 /// What a renderer reads of `text_style` and `text`'s spans to draw the
114 /// glyphs, so that drawing a plain text does not load the style.
115 pub paint: TextPaintCache,
116}
117
118impl TextPrimitiveNode {
119 /// The paint of the node's glyphs.
120 pub fn paint(&self) -> TextPaint {
121 self.paint.get_or(|| {
122 TextPaint::of(
123 &self.text_style,
124 self.text_style.render_hash(),
125 self.render_text.span_styles(),
126 (self.font_size, &self.layout),
127 (self.render_text.render_hash(), self.render_text.is_empty()),
128 )
129 })
130 }
131
132 /// Conservative bounds for the emitted glyphs and paint: the clip, or
133 /// the layout rectangle and wherever the ink of a glyph, a stroke's
134 /// included, reaches past it. Returns `None` when an unclipped layout or
135 /// style can draw beyond both: a visible overflow, a shadow of the style
136 /// or a span, or a baseline shift.
137 pub fn draw_bounds(&self) -> Option<Rect> {
138 if let Some(clip) = self.clip {
139 return Some(clip);
140 }
141 if matches!(self.overflow, TextOverflow::Visible) {
142 return None;
143 }
144 // The paint, kept in the node, tells this without loading the style.
145 let paint = self.paint();
146 (!paint.casts_shadow && paint.baseline_shift == 0.0)
147 .then(|| glyph_bounds(self.rect, &self.layout, paint.spread))
148 }
149
150 fn draws_within(&self, bounds: Rect) -> bool {
151 self.draw_bounds()
152 .is_some_and(|drawn| rect_within(drawn, bounds))
153 }
154}
155
156/// Whether a primitive draws before or after the children of its layer.
157#[derive(Clone, Copy, Debug, PartialEq, Eq)]
158pub enum PrimitivePhase {
159 /// Drawn under the children, like a background.
160 BeforeChildren,
161 /// Drawn over the children, like a foreground or overlay.
162 AfterChildren,
163}
164
165/// The content of a [`PrimitiveEntry`].
166#[derive(Clone, Debug, PartialEq)]
167pub enum PrimitiveNode {
168 /// A draw primitive with its clip.
169 Draw(Box<DrawPrimitiveNode>),
170 /// A text run.
171 Text(Box<TextPrimitiveNode>),
172}
173
174/// One primitive of a layer together with the phase that orders it against the children.
175#[derive(Clone, Debug, PartialEq)]
176pub struct PrimitiveEntry {
177 /// Whether the primitive draws before or after the children of its layer.
178 pub phase: PrimitivePhase,
179 /// The primitive.
180 pub node: PrimitiveNode,
181}
182
183/// Scene properties with implicit defaults and independently mutable clones.
184#[derive(Clone, Debug, Default)]
185pub struct LayerProperties(Option<Box<GraphicsLayer>>);
186
187impl LayerProperties {
188 /// The properties that were set, or `None` when they read as defaults.
189 pub fn get(&self) -> Option<&GraphicsLayer> {
190 self.0.as_deref()
191 }
192
193 /// Whether these properties read as `value`, `None` for the defaults:
194 /// a node without a layer compares nothing.
195 #[inline]
196 pub(crate) fn reads_as(&self, value: Option<&GraphicsLayer>) -> bool {
197 match (self.get(), value) {
198 (None, None) => true,
199 (stored, value) => {
200 stored.unwrap_or(&GraphicsLayer::DEFAULT)
201 == value.unwrap_or(&GraphicsLayer::DEFAULT)
202 }
203 }
204 }
205
206 pub(crate) fn replace(&mut self, value: Option<GraphicsLayer>) {
207 match (&mut self.0, value) {
208 (Some(current), Some(value)) => **current = value,
209 (storage, value) => *storage = value.map(Box::new),
210 }
211 }
212}
213
214impl Deref for LayerProperties {
215 type Target = GraphicsLayer;
216
217 fn deref(&self) -> &Self::Target {
218 self.0.as_deref().unwrap_or(&GraphicsLayer::DEFAULT)
219 }
220}
221
222impl DerefMut for LayerProperties {
223 fn deref_mut(&mut self) -> &mut Self::Target {
224 self.0.get_or_insert_with(Box::default)
225 }
226}
227
228impl From<GraphicsLayer> for LayerProperties {
229 fn from(value: GraphicsLayer) -> Self {
230 Self(Some(Box::new(value)))
231 }
232}
233
234impl From<Option<GraphicsLayer>> for LayerProperties {
235 fn from(value: Option<GraphicsLayer>) -> Self {
236 Self(value.map(Box::new))
237 }
238}
239
240impl PartialEq for LayerProperties {
241 fn eq(&self, other: &Self) -> bool {
242 self.reads_as(other.get())
243 }
244}
245
246/// A node of the render graph that groups content under one set of layer properties: bounds,
247/// transform, clip, graphics layer and children.
248#[derive(Clone)]
249pub struct LayerNode {
250 /// Layout node of this layer, or `None` for the root and the synthetic wrapper layers.
251 pub node_id: Option<NodeId>,
252 /// Set on the synthetic layer that holds a node's outer draws, those chained
253 /// before its graphics layer, around that node's own layer. Scene updates
254 /// address the node through this id because the node's layer has none of the
255 /// outer draws and the wrapper has no node id of its own.
256 pub wraps: Option<NodeId>,
257 /// The bounds of the node's layer, where its clip cuts, its transforms
258 /// pivot and an offscreen pass draws it.
259 pub local_bounds: Rect,
260 /// The node's own rect, `(0, 0, size)`, where its pointer handlers are,
261 /// when it differs from `local_bounds`: a clip or graphics layer that
262 /// wraps a coordinator a later padding or offset moves bounds the layer
263 /// there instead, as in Compose. `None` when the two agree.
264 pub node_bounds: Option<Rect>,
265 /// Maps the layer's local space to its parent's content space, with the placement, the graphics
266 /// layer transform and the parent's content offset applied.
267 pub transform_to_parent: ProjectiveTransform,
268 /// Offset in logical pixels of the node's content box from the node origin, for example from
269 /// padding. Children are placed relative to it.
270 pub content_offset: Point,
271 /// Whether the layer is inside a motion context such as an animated scroll, where a renderer
272 /// must not snap its content rigidly to the pixel grid.
273 pub motion_context_animated: bool,
274 /// Whether the layer moves its content as one translated block, as a scroll container does,
275 /// so a renderer may snap that content to the pixel grid as a whole.
276 pub translated_content_context: bool,
277 /// Translation in logical pixels of the content when `translated_content_context` is set.
278 pub translated_content_offset: Point,
279 /// Where layout placed this layer within its parent's content, before the
280 /// parent's content offset. Scene updates add these up from the root, with
281 /// each layer's content offset and graphics-layer translation, to find the
282 /// window origin of a subtree they rebuild.
283 pub origin_in_parent: Point,
284 /// The graphics layer properties; they read as defaults when none were set.
285 pub graphics_layer: LayerProperties,
286 /// Whether the layer cuts its content to `local_bounds`.
287 pub clip_to_bounds: bool,
288 /// Pointer handling of the node, or `None` when the node has none.
289 pub hit_test: Option<HitTestNode>,
290 /// Whether this layer or a layer beneath it has a hit test node. Scene building keeps it
291 /// current.
292 pub has_hit_targets: bool,
293 /// [`Self::has_space`], worked out the first time a walk asks and
294 /// forgotten when the layer or its children change.
295 pub space: Cell<Option<bool>>,
296 /// Whether this subtree publishes live window origins (a text field's
297 /// popup anchor, a scroll container's viewport rect). Those sinks are
298 /// written during a full lowering, so the scroll fast path may translate
299 /// a retained subtree in place only when this is false.
300 pub has_origin_sinks: bool,
301 /// [`Self::draws_within_bounds`], worked out the first time a renderer
302 /// asks and forgotten when the layer's children change.
303 pub contained: Cell<Option<bool>>,
304 /// The reasons this layer is drawn through its own offscreen buffer.
305 pub isolation: IsolationReasons,
306 /// Set on a layer that holds part of one draw command's draws, those a
307 /// `DrawScope` transform or clip groups: the phase they paint in among
308 /// the parent's own draws, which they belong to. `None` on the layer of
309 /// a layout node and on the root.
310 pub draw_phase: Option<PrimitivePhase>,
311 /// Whether a renderer may keep this layer's pixels between frames.
312 pub cache_policy: CachePolicy,
313 /// The raster cache hashes, computed the first time a renderer asks and
314 /// kept until the layer or a layer beneath it changes.
315 pub cache_hashes: Cell<Option<LayerRasterCacheHashes>>,
316 /// The scene updates that changed what the layer draws.
317 pub content_changes: ContentChanges,
318 /// The number the layer took when it or a layer beneath it last changed.
319 pub change: LayerChange,
320 /// The number the layer took when it last changed itself: its own
321 /// properties, draws or list of children. A change beneath it leaves
322 /// this number, so a renderer that kept the layer's own items knows they
323 /// hold.
324 pub own_change: LayerChange,
325 /// Content in draw order: primitives, draw runs and child layers.
326 pub children: Vec<RenderNode>,
327}
328
329/// A number a layer takes when it is made and whenever it or a layer beneath
330/// it changes, as its raster cache hashes are forgotten: a renderer that
331/// kept what it built from a subtree knows from an equal number that nothing
332/// in it changed since, save the subtree's placement in its parent.
333#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
334pub struct LayerChange(u32);
335
336impl LayerChange {
337 /// A number no layer took in the last 2^32 changes.
338 fn next() -> Self {
339 static LAST: AtomicU32 = AtomicU32::new(0);
340 Self(LAST.fetch_add(1, Ordering::Relaxed).wrapping_add(1))
341 }
342}
343
344impl Default for LayerChange {
345 fn default() -> Self {
346 Self::next()
347 }
348}
349
350/// The last scene update that changed what a layer draws, and how many
351/// updates in a row up to it did.
352#[derive(Clone, Copy, Debug, Default)]
353pub struct ContentChanges {
354 last_update: u64,
355 run: u32,
356}
357
358impl ContentChanges {
359 fn note(&mut self, update: u64) {
360 if self.run > 0 && self.last_update == update {
361 return;
362 }
363 self.run = if self.run > 0 && self.last_update + 1 == update {
364 self.run + 1
365 } else {
366 1
367 };
368 self.last_update = update;
369 }
370}
371
372impl Default for LayerNode {
373 fn default() -> Self {
374 Self {
375 node_id: None,
376 wraps: None,
377 local_bounds: Rect {
378 x: 0.0,
379 y: 0.0,
380 width: 0.0,
381 height: 0.0,
382 },
383 node_bounds: None,
384 transform_to_parent: ProjectiveTransform::identity(),
385 content_offset: Point::default(),
386 motion_context_animated: false,
387 translated_content_context: false,
388 translated_content_offset: Point::default(),
389 origin_in_parent: Point::default(),
390 graphics_layer: LayerProperties::default(),
391 clip_to_bounds: false,
392 hit_test: None,
393 has_hit_targets: false,
394 space: Cell::new(None),
395 has_origin_sinks: false,
396 contained: Cell::new(None),
397 isolation: IsolationReasons::default(),
398 draw_phase: None,
399 cache_policy: CachePolicy::None,
400 cache_hashes: Cell::new(None),
401 content_changes: ContentChanges::default(),
402 change: LayerChange::default(),
403 own_change: LayerChange::default(),
404 children: Vec::new(),
405 }
406 }
407}
408
409/// How far past its bounds a layer that draws within them may still put
410/// pixels: glyph and edge antialiasing.
411pub const CONTAINED_DRAW_SLACK: f32 = 1.0;
412
413impl LayerNode {
414 /// Every string this layer and the layers below it paint, in draw order.
415 pub fn painted_text(&self) -> Vec<String> {
416 let mut out = Vec::new();
417 collect_painted_text(&self.children, &mut out);
418 out
419 }
420
421 /// Whether everything this layer and its subtree draw lies within its
422 /// `local_bounds`, give or take [`CONTAINED_DRAW_SLACK`], so a renderer
423 /// may skip the whole subtree where those bounds are clipped away.
424 /// Worked out at the first call after a change and kept until the next.
425 pub fn draws_within_bounds(&self) -> bool {
426 if let Some(contained) = self.contained.get() {
427 return contained;
428 }
429 let contained = self.content_draws_within_bounds();
430 self.contained.set(Some(contained));
431 contained
432 }
433
434 /// Whether this layer's content, as [`LayerNode::draws_within_bounds`]
435 /// describes it, stays within its bounds: it clips to them, or its draws,
436 /// its texts and its children placed where they are all fit inside.
437 pub fn content_draws_within_bounds(&self) -> bool {
438 self.visual_clip_rect().is_some()
439 || nodes_draw_within(
440 &self.children,
441 inflate_rect(self.local_bounds, CONTAINED_DRAW_SLACK),
442 )
443 }
444
445 pub(crate) fn refresh_child_facts(&mut self) {
446 self.has_hit_targets = self.hit_test.is_some()
447 || self.children.iter().any(|child| match child {
448 RenderNode::Layer(child_layer) => child_layer.has_hit_targets,
449 RenderNode::Primitive(_) | RenderNode::DrawRun(_) | RenderNode::Inline(_) => false,
450 });
451 self.contained.set(None);
452 self.space.set(None);
453 }
454
455 /// Whether this layer or a layer beneath it is a scene, a mesh surface
456 /// or a 3D transform, so a walk for what lies in 3D skips the subtrees
457 /// without one.
458 pub fn has_space(&self) -> bool {
459 if let Some(known) = self.space.get() {
460 return known;
461 }
462 let found = self.graphics_layer.space.is_some()
463 || self.children.iter().any(|child| match child {
464 RenderNode::Layer(child_layer) => child_layer.has_space(),
465 RenderNode::Primitive(_) | RenderNode::DrawRun(_) | RenderNode::Inline(_) => false,
466 });
467 self.space.set(Some(found));
468 found
469 }
470
471 /// Whether this layer, drawn as a child, puts nothing outside `bounds`:
472 /// it draws within its own bounds, casts no shadow, applies no effect,
473 /// and its bounds placed in its parent lie inside.
474 fn draws_within_parent(&self, bounds: Rect) -> bool {
475 self.draws_within_bounds()
476 && self.graphics_layer.shadow_elevation <= 0.0
477 && self.effect().is_none()
478 && self.backdrop().is_none()
479 && rect_within(
480 self.transform_to_parent
481 .bounds_for_rect(inflate_rect(self.local_bounds, CONTAINED_DRAW_SLACK)),
482 bounds,
483 )
484 }
485
486 /// The node's own rect, where its pointer handlers are.
487 pub fn node_rect(&self) -> Rect {
488 self.node_bounds.unwrap_or(self.local_bounds)
489 }
490
491 /// The rectangle that cuts the layer's content and pointer hits: its bounds when the layer
492 /// clips to bounds or its graphics layer clips, otherwise `None`.
493 pub fn clip_rect(&self) -> Option<Rect> {
494 (self.clip_to_bounds || self.graphics_layer.clip).then_some(self.local_bounds)
495 }
496
497 /// Where the layer's own shadow is cut: its bounds when it clips its
498 /// content to them. A graphics-layer clip leaves the shadow whole.
499 pub fn shadow_clip(&self) -> Option<Rect> {
500 self.clip_to_bounds.then_some(self.local_bounds)
501 }
502
503 /// Where the layer's drawing is cut: its clip or, when it composites
504 /// through an offscreen buffer for its alpha or by request, its bounds,
505 /// as Compose's layer-sized buffer cuts it. Hit testing takes only
506 /// [`Self::clip_rect`]: alpha hides nothing from a finger.
507 #[inline]
508 pub fn visual_clip_rect(&self) -> Option<Rect> {
509 self.clip_rect().or_else(|| {
510 (self.isolation.group_opacity || self.isolation.explicit_offscreen)
511 .then_some(self.local_bounds)
512 })
513 }
514
515 /// The render effect applied to the layer's content, if any.
516 pub fn effect(&self) -> Option<&RenderEffect> {
517 self.graphics_layer.render_effect.as_ref()
518 }
519
520 /// The effect applied to what lies behind the layer, if any.
521 pub fn backdrop(&self) -> Option<&RenderEffect> {
522 self.graphics_layer.backdrop_effect.as_ref()
523 }
524
525 /// Alpha of the layer from 0.0 (transparent) to 1.0 (opaque).
526 pub fn opacity(&self) -> f32 {
527 self.graphics_layer.alpha
528 }
529
530 /// How the layer composites onto what lies beneath it.
531 pub fn blend_mode(&self) -> BlendMode {
532 self.graphics_layer.blend_mode
533 }
534
535 /// The color filter applied to the layer's content, if any.
536 pub fn color_filter(&self) -> Option<&ColorFilter> {
537 self.graphics_layer.color_filter.as_ref()
538 }
539
540 /// Hash of everything the layer draws into its target: bounds, clip, alpha, children and their
541 /// placement. Equal hashes mean cached pixels are still valid. Computed on first use.
542 pub fn target_content_hash(&self) -> u64 {
543 self.raster_cache_hashes().target_content
544 }
545
546 /// Hash of the layer's content like `target_content_hash`, except that the translated content
547 /// offset is left out, so the hash stays equal while the content only moves.
548 pub fn motion_source_content_hash(&self) -> u64 {
549 crate::graph_hash::layer_motion_source_content_hash(self)
550 }
551
552 /// Hash of the layer's render effect, for keying cached effect output.
553 pub fn effect_hash(&self) -> u64 {
554 self.raster_cache_hashes().effect
555 }
556
557 fn raster_cache_hashes(&self) -> LayerRasterCacheHashes {
558 if let Some(hashes) = self.cache_hashes.get() {
559 return hashes;
560 }
561 let hashes = crate::graph_hash::layer_raster_cache_hashes(self);
562 self.cache_hashes.set(Some(hashes));
563 hashes
564 }
565
566 /// Forgets the raster cache hashes of this layer and every layer beneath
567 /// it, after a change the scene builder did not make: they are computed
568 /// again when asked for.
569 pub fn forget_raster_cache_hashes(&mut self) {
570 self.forget_own_raster_cache_hashes();
571 for child in &mut self.children {
572 if let RenderNode::Layer(child) = child {
573 child.forget_raster_cache_hashes();
574 }
575 }
576 }
577
578 /// Forgets this layer's own raster cache hashes and takes new change
579 /// numbers: a property its hash covers changed.
580 pub(crate) fn forget_own_raster_cache_hashes(&mut self) {
581 self.cache_hashes.set(None);
582 self.change = LayerChange::next();
583 self.own_change = LayerChange::next();
584 }
585
586 /// Records that scene update `update` changed a layer beneath this one,
587 /// which it holds in its place: the hashes and the subtree's change
588 /// number go, the layer's own number stays.
589 pub(crate) fn note_descendant_change(&mut self, update: u64) {
590 self.cache_hashes.set(None);
591 self.change = LayerChange::next();
592 self.content_changes.note(update);
593 }
594
595 /// Records that scene update `update` changed what this layer draws, and
596 /// forgets its hashes.
597 pub(crate) fn note_content_change(&mut self, update: u64) {
598 self.forget_own_raster_cache_hashes();
599 self.content_changes.note(update);
600 }
601
602 /// Whether what this layer draws changed in scene update `update` and in
603 /// the one before it. A raster cache keyed on its content would miss
604 /// again on the next frame, so a renderer neither hashes nor caches it.
605 pub fn content_churns(&self, update: u64) -> bool {
606 self.content_changes.run >= 2 && self.content_changes.last_update == update
607 }
608}
609
610/// A child of a layer in the render graph.
611#[derive(Clone)]
612pub enum RenderNode {
613 /// A single draw or text primitive.
614 Primitive(PrimitiveEntry),
615 /// A whole draw command's primitives as one node. A heavy canvas records
616 /// thousands of primitives per frame; wrapping each in its own
617 /// [`RenderNode`] made the graph rebuild move every one of them twice and
618 /// free seventeen thousand nodes per frame on a stress scene. The run
619 /// keeps the recorded vector intact — semantically it is exactly that
620 /// many consecutive `Primitive` draw entries with no per-primitive clip.
621 DrawRun(DrawRunNode),
622 /// A nested layer.
623 Layer(Box<LayerNode>),
624 /// A layout node drawn in this layer instead of a layer of its own.
625 Inline(Box<InlineNode>),
626}
627
628impl RenderNode {
629 /// The phase the node paints in among its layer's own draws: a
630 /// primitive's or run's, or a draw group layer's. `None` for the layer
631 /// of a layout node, which is a child of the layer.
632 pub fn draw_phase(&self) -> Option<PrimitivePhase> {
633 match self {
634 Self::Primitive(entry) => Some(entry.phase),
635 Self::DrawRun(run) => Some(run.phase),
636 Self::Layer(layer) => layer.draw_phase,
637 Self::Inline(_) => None,
638 }
639 }
640
641 fn draws_after_children(&self) -> bool {
642 self.draw_phase() == Some(PrimitivePhase::AfterChildren)
643 }
644}
645
646/// Visits `children`, a layer's child list, in the order they draw: in list
647/// order, save that the draws after the children wait until every other
648/// node has drawn.
649pub fn for_each_in_draw_order(children: &[RenderNode], mut visit: impl FnMut(&RenderNode)) {
650 let mut first_deferred = children.len();
651 for (index, child) in children.iter().enumerate() {
652 if child.draws_after_children() {
653 first_deferred = first_deferred.min(index);
654 } else {
655 visit(child);
656 }
657 }
658 children[first_deferred..]
659 .iter()
660 .filter(|child| child.draws_after_children())
661 .for_each(visit);
662}
663
664/// Stable identity of one placement of a draw command: its layout node,
665/// command index and placement pass. A `WithContent` command shares one CPU
666/// recording between its two runs, while each placement retains independent
667/// renderer state under this identity.
668#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
669pub struct DrawCommandId {
670 /// Layout node that owns the draw command.
671 pub node_id: NodeId,
672 /// Index of the command among the draw commands of the node.
673 pub command_index: u32,
674 /// Whether the command draws behind the node's content or over it.
675 pub placement: DrawPlacement,
676}
677
678/// A run of recorded draw primitives from one draw command, kept together as one node.
679#[derive(Clone, Debug, PartialEq)]
680pub struct DrawRunNode {
681 /// Whether the run draws before or after the children of its layer.
682 pub phase: PrimitivePhase,
683 /// Which draw command recorded these primitives. `None` only for runs
684 /// with no per-command provenance (hand-built tests).
685 pub command: Option<DrawCommandId>,
686 /// The recording registry keeps a handle per layout node and command,
687 /// so this recording's buffers survive the draw run being dropped and
688 /// the command can record into them on a later update. Nothing mutates
689 /// a recording after construction, which is
690 /// what makes sharing sound.
691 pub recording: Rc<CommandRecording>,
692 /// The segments of the recording this run draws: one placement's part
693 /// of a with-content command, or the whole recording.
694 pub segments: Range<u32>,
695 /// Content facts consumers keep asking per frame, answered while
696 /// recording and never by rescanning.
697 pub summary: DrawRunSummary,
698}
699
700/// What a run contains, answered while recording.
701pub type DrawRunSummary = RecordingSummary;
702
703impl DrawRunNode {
704 /// Creates a run for `primitives` that has no draw command provenance.
705 pub fn new(phase: PrimitivePhase, primitives: Vec<DrawPrimitive>) -> Self {
706 Self::for_command(phase, None, primitives)
707 }
708
709 /// Creates a run for `primitives` recorded by the draw command `command`.
710 pub fn for_command(
711 phase: PrimitivePhase,
712 command: Option<DrawCommandId>,
713 primitives: Vec<DrawPrimitive>,
714 ) -> Self {
715 let recording = CommandRecording::from_primitives(primitives);
716 let segments = recording.all_segments();
717 Self::for_command_shared(phase, command, Rc::new(recording), segments)
718 }
719
720 /// Creates a run over `segments` of an existing shared `recording`, without copying it.
721 pub fn for_command_shared(
722 phase: PrimitivePhase,
723 command: Option<DrawCommandId>,
724 recording: Rc<CommandRecording>,
725 segments: Range<u32>,
726 ) -> Self {
727 let summary = recording.summary_in(&segments);
728 Self {
729 phase,
730 command,
731 recording,
732 segments,
733 summary,
734 }
735 }
736
737 /// The run's primitives, materialised from the recording in order.
738 pub fn primitives(&self) -> impl Iterator<Item = DrawPrimitive> + '_ {
739 self.recording.primitives(self.segments.clone())
740 }
741
742 /// Runs `visit` on the run's primitives in order, as [`Self::primitives`]
743 /// yields them, without the iterator's step between segments.
744 pub fn for_each_primitive(&self, visit: impl FnMut(DrawPrimitive)) {
745 self.recording
746 .for_each_primitive(self.segments.clone(), visit);
747 }
748
749 /// The rect every entry of the run can reach.
750 pub fn coverage_rects(&self) -> impl Iterator<Item = Rect> + '_ {
751 self.recording.coverage_rects(self.segments.clone())
752 }
753
754 /// Number of primitives in the run.
755 pub fn len(&self) -> usize {
756 self.recording.len_in(&self.segments)
757 }
758
759 /// Whether the run holds no primitives.
760 pub fn is_empty(&self) -> bool {
761 self.recording.is_empty_in(&self.segments)
762 }
763}
764
765/// The scene as a tree of layers that a renderer draws.
766#[derive(Clone)]
767pub struct RenderGraph {
768 /// The top layer; every other node of the graph is beneath it.
769 pub root: LayerNode,
770 /// How many scene updates the graph took: the number of the latest.
771 pub update: u64,
772 /// The way to the layer the last scene updates changed alone, for the
773 /// next update that changes it alone.
774 pub(crate) retained_path: Option<crate::scene_builder::RetainedPath>,
775 // The marks of the last walk to the graph's dirty layers.
776 pub(crate) dirty_ancestry: crate::scene_builder::DirtyAncestry,
777}
778
779impl RenderGraph {
780 /// Creates a graph around `root` at update zero.
781 pub fn new(root: LayerNode) -> Self {
782 Self {
783 root,
784 update: 0,
785 retained_path: None,
786 dirty_ancestry: crate::scene_builder::DirtyAncestry::default(),
787 }
788 }
789
790 /// Number of nodes in the graph. A layer counts once and each primitive of a draw run counts on
791 /// its own.
792 pub fn node_count(&self) -> usize {
793 fn count_nodes(nodes: &[RenderNode]) -> usize {
794 nodes
795 .iter()
796 .map(|child| match child {
797 RenderNode::Primitive(_) => 1,
798 RenderNode::DrawRun(run) => run.len(),
799 RenderNode::Layer(child_layer) => 1 + count_nodes(&child_layer.children),
800 RenderNode::Inline(inline) => 1 + count_nodes(&inline.content),
801 })
802 .sum::<usize>()
803 }
804
805 1 + count_nodes(&self.root.children)
806 }
807
808 /// Estimated heap memory held beneath the root layer, in bytes, for memory reports.
809 pub fn heap_bytes(&self) -> usize {
810 layer_heap_bytes(&self.root)
811 }
812
813 /// Replaces the set with the graph's visual observation owners, retaining its capacity.
814 pub fn collect_retained_visual_observation_nodes(&self, nodes: &mut HashSet<NodeId>) {
815 fn collect(layer: &LayerNode, nodes: &mut HashSet<NodeId>) {
816 if let Some(node_id) = layer.node_id {
817 nodes.insert(node_id);
818 }
819 for child in &layer.children {
820 match child {
821 RenderNode::DrawRun(run) => {
822 if let Some(command) = run.command {
823 nodes.insert(command.node_id);
824 }
825 }
826 RenderNode::Layer(child) => collect(child, nodes),
827 RenderNode::Inline(inline) => {
828 nodes.insert(inline.node_id);
829 }
830 RenderNode::Primitive(_) => {}
831 }
832 }
833 }
834
835 nodes.clear();
836 collect(&self.root, nodes);
837 }
838}
839
840fn collect_painted_text(nodes: &[RenderNode], out: &mut Vec<String>) {
841 for child in nodes {
842 match child {
843 RenderNode::Primitive(primitive) => {
844 if let PrimitiveNode::Text(text) = &primitive.node {
845 out.push(text.text.text.clone());
846 }
847 }
848 RenderNode::Layer(child) => collect_painted_text(&child.children, out),
849 RenderNode::Inline(inline) => collect_painted_text(&inline.content, out),
850 RenderNode::DrawRun(run) => {
851 for primitive in run.primitives() {
852 if let DrawPrimitive::Text(text) = primitive {
853 out.push(text.text.to_string());
854 }
855 }
856 }
857 }
858 }
859}
860
861// Whether `nodes`, a layer's children, put nothing outside `bounds` of it.
862fn nodes_draw_within(nodes: &[RenderNode], bounds: Rect) -> bool {
863 nodes.iter().all(|child| match child {
864 RenderNode::DrawRun(run) => {
865 !run.summary.has_shadow
866 && run
867 .recording
868 .bounds()
869 .is_none_or(|drawn| rect_within(drawn, bounds))
870 }
871 RenderNode::Primitive(entry) => match &entry.node {
872 PrimitiveNode::Text(text) => text.draws_within(bounds),
873 PrimitiveNode::Draw(_) => false,
874 },
875 RenderNode::Layer(layer) => layer.draws_within_parent(bounds),
876 RenderNode::Inline(inline) => inline.draws_within_parent(bounds),
877 })
878}
879
880fn inflate_rect(rect: Rect, by: f32) -> Rect {
881 Rect {
882 x: rect.x - by,
883 y: rect.y - by,
884 width: rect.width + by * 2.0,
885 height: rect.height + by * 2.0,
886 }
887}
888
889fn rect_within(inner: Rect, outer: Rect) -> bool {
890 inner.x >= outer.x
891 && inner.y >= outer.y
892 && inner.x + inner.width <= outer.x + outer.width
893 && inner.y + inner.height <= outer.y + outer.height
894}
895
896#[cfg(test)]
897#[path = "tests/graph_tests.rs"]
898mod tests;