Skip to main content

pax_runtime/engine/
piet_render_context.rs

1use pax_runtime_api::{Fill, Stroke, StrokeCap, StrokeJoin};
2use piet::{
3    kurbo::{self, Affine, Shape},
4    InterpolationMode, LineCap, LineJoin, LinearGradient, RadialGradient, StrokeStyle,
5};
6use std::{
7    cell::RefCell,
8    collections::{HashMap, HashSet},
9    rc::Rc,
10};
11
12use super::layer_surface::{
13    replay_batches_by_directional_priority, surface_intersects_coverage_bounds, LayerSurfaceEntry,
14    LayerSurfaceLayout, LayoutChangeKind, ReplayPriorityEntry, SurfaceReplayCoordinator,
15};
16#[cfg(debug_assertions)]
17use super::layer_surface::{visible_surface_escape, VisibleSurfaceEscape};
18use crate::api;
19
20#[cfg(debug_assertions)]
21fn log_visible_surface_escape(layer: usize, escape: VisibleSurfaceEscape) {
22    log::warn!(
23        "[pax-tile-window-escape] backend=piet layer={} visible_surfaces={} escaped_visible_surfaces={} max_gap_x={:.1} max_gap_y={:.1} previous_bounds={} visible_bounds={}",
24        layer,
25        escape.visible_surfaces,
26        escape.escaped_visible_surfaces,
27        escape.max_gap_x,
28        escape.max_gap_y,
29        format_rect(escape.previous_bounds),
30        format_rect(escape.visible_bounds),
31    );
32}
33
34#[cfg(debug_assertions)]
35fn format_rect(bounds: kurbo::Rect) -> String {
36    format!(
37        "{:.1},{:.1}-{:.1},{:.1}",
38        bounds.x0, bounds.y0, bounds.x1, bounds.y1
39    )
40}
41
42struct ImgData<R: piet::RenderContext> {
43    img: R::Image,
44    size: (usize, usize),
45}
46
47type ClearFn = Box<dyn Fn()>;
48type ConfigureFn = Box<dyn Fn(f32, f32, u32, u32, [f32; 2], bool)>;
49type LayerDef<R> = (PietLayerTarget<R>, Box<dyn Fn() -> LayerSurfaceLayout>);
50
51/// Retained metadata for one piet-backed browser canvas surface.
52pub struct PietLayerRenderer<R: piet::RenderContext> {
53    key: String,
54    host_signature: String,
55    context: R,
56    clear_fn: ClearFn,
57    configure_fn: ConfigureFn,
58    origin_x: f32,
59    origin_y: f32,
60    logical_width: f32,
61    logical_height: f32,
62    surface_width: u32,
63    surface_height: u32,
64    dpr: [f32; 2],
65}
66
67impl<R: piet::RenderContext> PietLayerRenderer<R> {
68    pub fn new(
69        key: String,
70        host_signature: String,
71        context: R,
72        clear_fn: ClearFn,
73        configure_fn: ConfigureFn,
74        surface: &LayerSurfaceEntry,
75    ) -> Self {
76        Self {
77            key,
78            host_signature,
79            context,
80            clear_fn,
81            configure_fn,
82            origin_x: surface.origin_x,
83            origin_y: surface.origin_y,
84            logical_width: surface.surface.logical_width,
85            logical_height: surface.surface.logical_height,
86            surface_width: surface.surface.surface_width,
87            surface_height: surface.surface.surface_height,
88            dpr: surface.surface.dpr,
89        }
90    }
91
92    fn context_mut(&mut self) -> &mut R {
93        &mut self.context
94    }
95
96    fn clear(&self) {
97        (self.clear_fn)();
98    }
99
100    fn intersects_coverage_bounds(&self, bounds: &kurbo::Rect) -> bool {
101        surface_intersects_coverage_bounds(
102            bounds,
103            self.origin_x as f64,
104            self.origin_y as f64,
105            self.logical_width as f64,
106            self.logical_height as f64,
107        )
108    }
109
110    fn coverage_bounds(&self) -> kurbo::Rect {
111        kurbo::Rect::new(
112            self.origin_x as f64,
113            self.origin_y as f64,
114            self.origin_x as f64 + self.logical_width as f64,
115            self.origin_y as f64 + self.logical_height as f64,
116        )
117    }
118
119    fn update_layout(&mut self, surface: &LayerSurfaceEntry) -> LayoutChangeKind {
120        let origin_changed = (self.origin_x - surface.origin_x).abs() > f32::EPSILON
121            || (self.origin_y - surface.origin_y).abs() > f32::EPSILON;
122        let size_changed = self.logical_width != surface.surface.logical_width
123            || self.logical_height != surface.surface.logical_height
124            || self.surface_width != surface.surface.surface_width
125            || self.surface_height != surface.surface.surface_height
126            || self.dpr != surface.surface.dpr;
127
128        self.origin_x = surface.origin_x;
129        self.origin_y = surface.origin_y;
130        self.logical_width = surface.surface.logical_width;
131        self.logical_height = surface.surface.logical_height;
132        self.surface_width = surface.surface.surface_width;
133        self.surface_height = surface.surface.surface_height;
134        self.dpr = surface.surface.dpr;
135
136        if size_changed || origin_changed {
137            (self.configure_fn)(
138                self.origin_x,
139                self.origin_y,
140                self.surface_width,
141                self.surface_height,
142                self.dpr,
143                size_changed,
144            );
145        }
146
147        if size_changed {
148            self.clear();
149        }
150
151        if size_changed {
152            LayoutChangeKind::Resized
153        } else if origin_changed {
154            LayoutChangeKind::OriginOnly
155        } else {
156            LayoutChangeKind::Unchanged
157        }
158    }
159}
160
161/// Current piet surface set for one logical layer.
162pub struct PietLayerTarget<R: piet::RenderContext> {
163    renderers: Vec<PietLayerRenderer<R>>,
164    active: bool,
165}
166
167impl<R: piet::RenderContext> PietLayerTarget<R> {
168    pub fn new(renderers: Vec<PietLayerRenderer<R>>, active: bool) -> Self {
169        Self { renderers, active }
170    }
171
172    fn activate(&mut self) -> bool {
173        let changed = !self.active;
174        self.active = true;
175        changed
176    }
177
178    fn deactivate(&mut self) {
179        self.active = false;
180    }
181}
182
183/// `RenderContext` implementation backed by piet.
184pub struct PietRenderer<R: piet::RenderContext> {
185    layers: Vec<LayerDef<R>>,
186    image_map: HashMap<String, ImgData<R>>,
187    layer_factory: Box<dyn Fn(usize) -> LayerDef<R>>,
188    ready_layers: Vec<usize>,
189    replay_layers: Vec<usize>,
190    surface_replay: SurfaceReplayCoordinator,
191    active_render_scopes: Vec<Vec<Vec<usize>>>,
192    clean_skipped_canvas_nodes: HashSet<(usize, u32)>,
193}
194
195impl<R: piet::RenderContext> PietRenderer<R> {
196    /// Create a piet renderer with a chassis-provided logical layer factory.
197    pub fn new(layer_factory: impl Fn(usize) -> LayerDef<R> + 'static) -> Self {
198        Self {
199            layer_factory: Box::new(layer_factory),
200            layers: Vec::new(),
201            image_map: HashMap::new(),
202            ready_layers: Vec::new(),
203            replay_layers: Vec::new(),
204            surface_replay: SurfaceReplayCoordinator::default(),
205            active_render_scopes: Vec::new(),
206            clean_skipped_canvas_nodes: HashSet::new(),
207        }
208    }
209
210    fn first_context_mut(&mut self) -> Option<&mut R> {
211        self.layers
212            .iter_mut()
213            .find_map(|(target, _)| target.renderers.first_mut())
214            .map(PietLayerRenderer::context_mut)
215    }
216
217    fn with_layer_context(&mut self, layer: usize, mut f: impl FnMut(&mut R)) {
218        let scoped_indices = self
219            .active_render_scopes
220            .get(layer)
221            .and_then(|scopes| scopes.last())
222            .cloned();
223
224        let Some((target, _)) = self.layers.get_mut(layer) else {
225            return;
226        };
227        if !target.active {
228            return;
229        }
230
231        if let Some(indices) = scoped_indices {
232            for index in indices {
233                if let Some(renderer) = target.renderers.get_mut(index) {
234                    f(renderer.context_mut());
235                }
236            }
237        } else {
238            for renderer in &mut target.renderers {
239                f(renderer.context_mut());
240            }
241        }
242    }
243
244    fn push_render_scope(&mut self, layer: usize, renderer_indices: Vec<usize>) {
245        if self.active_render_scopes.len() <= layer {
246            self.active_render_scopes.resize_with(layer + 1, Vec::new);
247        }
248        self.active_render_scopes[layer].push(renderer_indices);
249    }
250
251    fn pop_render_scope(&mut self, layer: usize) {
252        if let Some(scopes) = self.active_render_scopes.get_mut(layer) {
253            scopes.pop();
254        }
255    }
256
257    fn take_replay_layer_ids(&mut self) -> Vec<usize> {
258        let mut replay = std::mem::take(&mut self.replay_layers);
259        replay.sort_unstable();
260        replay.dedup();
261        replay
262    }
263
264    fn targeted_or_all_indices(&self, layer: usize, renderer_count: usize) -> Vec<usize> {
265        self.surface_replay
266            .targeted_or_all_indices(layer, renderer_count)
267    }
268
269    fn clear_targeted_replay_scope(&mut self, layer: usize) {
270        self.surface_replay.clear_targeted_replay_scope(layer);
271    }
272
273    fn set_targeted_replay_batches(
274        &mut self,
275        layer: usize,
276        batches: Vec<Vec<usize>>,
277        bounds_by_surface: HashMap<usize, Vec<kurbo::Rect>>,
278    ) {
279        self.surface_replay
280            .set_targeted_replay_batches(layer, batches, bounds_by_surface);
281    }
282
283    fn advance_targeted_replay_queue(&mut self, layer: usize) -> bool {
284        self.surface_replay.advance_targeted_replay_queue(layer)
285    }
286
287    fn remember_canvas_node_coverage(
288        &mut self,
289        layer: usize,
290        node_id: u32,
291        coverage_bounds: kurbo::Rect,
292    ) {
293        self.surface_replay
294            .remember_canvas_node_coverage(layer, node_id, coverage_bounds);
295    }
296
297    fn forget_canvas_node_coverage(&mut self, layer: usize, node_id: u32) {
298        self.surface_replay
299            .forget_canvas_node_coverage(layer, node_id);
300    }
301
302    fn targeted_replay_surface_bounds(
303        &self,
304        layer: usize,
305        target: &PietLayerTarget<R>,
306    ) -> Option<Vec<kurbo::Rect>> {
307        self.surface_replay
308            .targeted_replay_surface_bounds(layer, |index| {
309                target
310                    .renderers
311                    .get(index)
312                    .map(PietLayerRenderer::coverage_bounds)
313            })
314    }
315
316    fn spatial_replay_node_ids(&self, layer: usize) -> Option<Vec<u32>> {
317        let Some((target, _)) = self.layers.get(layer) else {
318            return None;
319        };
320        let surface_bounds = self.targeted_replay_surface_bounds(layer, target)?;
321        self.surface_replay
322            .spatial_replay_node_ids_for_surface_bounds(layer, &surface_bounds)
323    }
324
325    fn refresh_layer_layouts<I>(&mut self, layer_indices: I)
326    where
327        I: IntoIterator<Item = usize>,
328    {
329        for layer_index in layer_indices {
330            let Some((target, layout_provider)) = self.layers.get_mut(layer_index) else {
331                continue;
332            };
333            let layout = (layout_provider)();
334            let layout_matches = layer_layout_matches_target(target, &layout);
335
336            if !layout.active {
337                target.deactivate();
338                self.clear_targeted_replay_scope(layer_index);
339                continue;
340            }
341
342            if !layout_matches {
343                self.clear_targeted_replay_scope(layer_index);
344                let (new_target, new_provider) = (self.layer_factory)(layer_index);
345                let has_surfaces = !new_target.renderers.is_empty();
346                self.layers[layer_index] = (new_target, new_provider);
347                if has_surfaces {
348                    self.ready_layers.push(layer_index);
349                    self.replay_layers.push(layer_index);
350                }
351                continue;
352            }
353
354            let activated_replay = target.activate() && !target.renderers.is_empty();
355
356            let mut targeted_replay_entries = Vec::new();
357            let mut targeted_replay_bounds = HashMap::new();
358            let mut needs_full_layer_replay = false;
359            #[cfg(debug_assertions)]
360            {
361                let previous_surface_bounds: Vec<_> = target
362                    .renderers
363                    .iter()
364                    .map(PietLayerRenderer::coverage_bounds)
365                    .collect();
366                if let Some(escape) =
367                    visible_surface_escape(&previous_surface_bounds, &layout.surfaces)
368                {
369                    log_visible_surface_escape(layer_index, escape);
370                }
371            }
372            for (index, (surface, renderer)) in layout
373                .surfaces
374                .iter()
375                .zip(target.renderers.iter_mut())
376                .enumerate()
377            {
378                let previous_bounds = renderer.coverage_bounds();
379                let layout_change = renderer.update_layout(surface);
380                let current_bounds = renderer.coverage_bounds();
381                match layout_change {
382                    LayoutChangeKind::Unchanged => {}
383                    LayoutChangeKind::OriginOnly => {
384                        targeted_replay_entries.push(ReplayPriorityEntry::new(
385                            index,
386                            surface.replay_priority,
387                            previous_bounds,
388                            current_bounds,
389                        ));
390                        targeted_replay_bounds.insert(index, vec![previous_bounds, current_bounds]);
391                    }
392                    LayoutChangeKind::Resized => {
393                        needs_full_layer_replay = true;
394                    }
395                }
396            }
397            if activated_replay || needs_full_layer_replay {
398                self.clear_targeted_replay_scope(layer_index);
399                self.replay_layers.push(layer_index);
400            } else if !targeted_replay_entries.is_empty() {
401                self.set_targeted_replay_batches(
402                    layer_index,
403                    piet_replay_batches_for_retarget(targeted_replay_entries),
404                    targeted_replay_bounds,
405                );
406                self.replay_layers.push(layer_index);
407            }
408        }
409    }
410}
411
412fn piet_replay_batches_for_retarget(entries: Vec<ReplayPriorityEntry>) -> Vec<Vec<usize>> {
413    let batches = replay_batches_by_directional_priority(&entries);
414    let visible_indices: HashSet<_> = entries
415        .iter()
416        .filter(|entry| entry.priority <= 0)
417        .map(|entry| entry.index)
418        .collect();
419    if visible_indices.is_empty() {
420        return batches;
421    }
422
423    let mut visible_batch = Vec::new();
424    let mut warm_batches = Vec::new();
425    for batch in batches {
426        let mut warm_batch = Vec::new();
427        for index in batch {
428            if visible_indices.contains(&index) {
429                visible_batch.push(index);
430            } else {
431                warm_batch.push(index);
432            }
433        }
434        if !warm_batch.is_empty() {
435            warm_batches.push(warm_batch);
436        }
437    }
438
439    let mut coalesced_batches = Vec::with_capacity(warm_batches.len() + 1);
440    coalesced_batches.push(visible_batch);
441    coalesced_batches.extend(warm_batches);
442    coalesced_batches
443}
444
445fn layer_layout_matches_target<R: piet::RenderContext>(
446    target: &PietLayerTarget<R>,
447    layout: &LayerSurfaceLayout,
448) -> bool {
449    layout.surfaces.len() == target.renderers.len()
450        && layout
451            .surfaces
452            .iter()
453            .zip(target.renderers.iter())
454            .all(|(surface, renderer)| {
455                surface.key == renderer.key && surface.host_signature == renderer.host_signature
456            })
457}
458
459#[cfg(test)]
460mod tests {
461    use super::piet_replay_batches_for_retarget;
462    use crate::engine::layer_surface::ReplayPriorityEntry;
463    use kurbo::Rect;
464
465    #[test]
466    fn piet_replay_batches_coalesce_visible_surfaces_first() {
467        let batches = piet_replay_batches_for_retarget(vec![
468            replay_entry(0, 0, 0.0, 100.0),
469            replay_entry(1, 0, 100.0, 200.0),
470            replay_entry(2, 1, 200.0, 300.0),
471            replay_entry(3, 1, -100.0, 0.0),
472        ]);
473
474        assert_eq!(batches[0], vec![1, 0]);
475        assert_eq!(batches[1], vec![2]);
476        assert_eq!(batches[2], vec![3]);
477    }
478
479    #[test]
480    fn piet_replay_batches_keep_warm_only_directional_batches() {
481        let batches = piet_replay_batches_for_retarget(vec![
482            replay_entry(0, 1, 0.0, 100.0),
483            replay_entry(1, 1, 100.0, 200.0),
484            replay_entry(2, 1, -100.0, 0.0),
485        ]);
486
487        assert_eq!(batches, vec![vec![1], vec![0], vec![2]]);
488    }
489
490    fn replay_entry(
491        index: usize,
492        priority: i32,
493        previous_y: f64,
494        current_y: f64,
495    ) -> ReplayPriorityEntry {
496        ReplayPriorityEntry::new(
497            index,
498            priority,
499            Rect::new(0.0, previous_y, 100.0, previous_y + 100.0),
500            Rect::new(0.0, current_y, 100.0, current_y + 100.0),
501        )
502    }
503}
504
505impl<R: piet::RenderContext> api::RenderContext for PietRenderer<R> {
506    fn fill_with_opacity(&mut self, layer: usize, path: kurbo::BezPath, fill: &Fill, opacity: f64) {
507        let rect = path.bounding_box();
508        let brush = fill_to_piet_brush(&fill.with_alpha_factor(opacity), rect);
509        self.with_layer_context(layer, |context| context.fill(path.clone(), &brush));
510    }
511
512    fn stroke_with_opacity(
513        &mut self,
514        layer: usize,
515        path: kurbo::BezPath,
516        stroke: &Stroke,
517        opacity: f64,
518    ) {
519        let rect = path.bounding_box();
520        let brush = fill_to_piet_brush(
521            &Fill::Solid(stroke.color.get()).with_alpha_factor(opacity),
522            rect,
523        );
524        let width = stroke.width.get().expect_pixels().to_float();
525        let style = stroke_to_piet_style(stroke);
526        self.with_layer_context(layer, |context| {
527            context.stroke_styled(path.clone(), &brush, width, &style)
528        });
529    }
530
531    fn stroke_with_draw_range_and_material_and_opacity(
532        &mut self,
533        layer: usize,
534        path: kurbo::BezPath,
535        stroke: &Stroke,
536        _material: &api::Material,
537        opacity: f64,
538        draw_start: f64,
539        draw_end: f64,
540    ) {
541        let draw_start = draw_start.clamp(0.0, 1.0);
542        let draw_end = draw_end.clamp(0.0, 1.0);
543        if draw_start >= draw_end {
544            return;
545        }
546        let path = if draw_start <= f64::EPSILON && draw_end >= 1.0 - f64::EPSILON {
547            path
548        } else {
549            api::drawing::path_trim::trim_bez_path(&path, draw_start, draw_end)
550        };
551        self.stroke_with_opacity(layer, path, stroke, opacity);
552    }
553
554    fn save(&mut self, layer: usize) {
555        self.with_layer_context(layer, |context| {
556            let _ = context.save();
557        });
558    }
559
560    fn transform(&mut self, layer: usize, affine: Affine) {
561        self.with_layer_context(layer, |context| context.transform(affine));
562    }
563
564    fn clip(&mut self, layer: usize, path: kurbo::BezPath) {
565        self.with_layer_context(layer, |context| context.clip(path.clone()));
566    }
567
568    fn restore(&mut self, layer: usize) {
569        self.with_layer_context(layer, |context| {
570            let _ = context.restore();
571        });
572    }
573
574    fn load_image(&mut self, path: &str, buf: &[u8], width: usize, height: usize) {
575        let Some(render_context) = self.first_context_mut() else {
576            return;
577        };
578        let img = render_context
579            .make_image(width, height, buf, piet::ImageFormat::RgbaSeparate)
580            .expect("image creation successful");
581        self.image_map.insert(
582            path.to_owned(),
583            ImgData {
584                img,
585                size: (width, height),
586            },
587        );
588    }
589
590    fn get_image_size(&mut self, image_path: &str) -> Option<(usize, usize)> {
591        self.image_map.get(image_path).map(|img| img.size)
592    }
593
594    fn draw_image(&mut self, layer: usize, image_path: &str, rect: kurbo::Rect) {
595        let Some(data) = self.image_map.get(image_path) else {
596            return;
597        };
598        let scoped_indices = self
599            .active_render_scopes
600            .get(layer)
601            .and_then(|scopes| scopes.last())
602            .cloned();
603        let Some((target, _)) = self.layers.get_mut(layer) else {
604            return;
605        };
606        if !target.active {
607            return;
608        }
609        if let Some(indices) = scoped_indices {
610            for index in indices {
611                if let Some(renderer) = target.renderers.get_mut(index) {
612                    renderer
613                        .context_mut()
614                        .draw_image(&data.img, rect, InterpolationMode::Bilinear);
615                }
616            }
617        } else {
618            for renderer in &mut target.renderers {
619                renderer
620                    .context_mut()
621                    .draw_image(&data.img, rect, InterpolationMode::Bilinear);
622            }
623        }
624    }
625
626    fn layers(&self) -> usize {
627        self.layers.len()
628    }
629
630    fn resize_layers_to(&mut self, layer_count: usize, dirty_canvases: Rc<RefCell<Vec<bool>>>) {
631        let current_len = self.layers.len();
632        match layer_count.cmp(&current_len) {
633            std::cmp::Ordering::Less => {
634                self.layers.truncate(layer_count);
635                self.active_render_scopes.truncate(layer_count);
636                self.surface_replay.truncate_layers(layer_count);
637            }
638            std::cmp::Ordering::Equal => return,
639            std::cmp::Ordering::Greater => {
640                for i in current_len..layer_count {
641                    let (target, provider) = (self.layer_factory)(i);
642                    let has_surfaces = target.active && !target.renderers.is_empty();
643                    self.layers.push((target, provider));
644                    if let Some(dirty_bit) = dirty_canvases.borrow_mut().get_mut(i) {
645                        *dirty_bit = true;
646                    }
647                    if has_surfaces {
648                        self.ready_layers.push(i);
649                    }
650                }
651            }
652        }
653    }
654
655    fn image_loaded(&self, path: &str) -> bool {
656        self.image_map.contains_key(path)
657    }
658
659    fn clear(&mut self, layer: usize) {
660        let scoped_indices = self
661            .active_render_scopes
662            .get(layer)
663            .and_then(|scopes| scopes.last())
664            .cloned()
665            .or_else(|| {
666                self.layers
667                    .get(layer)
668                    .map(|(target, _)| self.targeted_or_all_indices(layer, target.renderers.len()))
669            });
670        let Some((target, _)) = self.layers.get_mut(layer) else {
671            return;
672        };
673        if !target.active {
674            return;
675        }
676        if let Some(indices) = scoped_indices {
677            for index in indices {
678                if let Some(renderer) = target.renderers.get(index) {
679                    renderer.clear();
680                }
681            }
682        } else {
683            for renderer in &target.renderers {
684                renderer.clear();
685            }
686        }
687    }
688
689    fn flush(&mut self, layer: usize, _dirty_canvases: Rc<RefCell<Vec<bool>>>) {
690        if self.advance_targeted_replay_queue(layer) {
691            self.replay_layers.push(layer);
692        }
693    }
694
695    fn resize(&mut self, _width: usize, _height: usize) {
696        let layer_count = self.layers.len();
697        self.refresh_layer_layouts(0..layer_count);
698    }
699
700    fn refresh_layers(&mut self, layers: &[usize]) {
701        self.refresh_layer_layouts(layers.iter().copied());
702    }
703
704    fn clear_targeted_replay(&mut self, layer: usize) {
705        self.clear_targeted_replay_scope(layer);
706    }
707
708    fn take_ready_canvas_layers(&mut self) -> Vec<usize> {
709        let mut ready = std::mem::take(&mut self.ready_layers);
710        ready.sort_unstable();
711        ready.dedup();
712        ready
713    }
714
715    fn take_replay_canvas_layer_updates(
716        &mut self,
717    ) -> Vec<pax_runtime_api::ReplayCanvasLayerUpdate> {
718        let replay_layers = self.take_replay_layer_ids();
719        replay_layers
720            .into_iter()
721            .map(|layer| pax_runtime_api::ReplayCanvasLayerUpdate {
722                layer,
723                node_ids: self.spatial_replay_node_ids(layer),
724            })
725            .collect()
726    }
727
728    fn retains_canvas_nodes(&self) -> bool {
729        false
730    }
731
732    fn begin_node(&mut self, layer: usize, _node_id: u32, _z_index: i32, _light_mask: u32) -> bool {
733        let renderer_count = {
734            let Some((target, _)) = self.layers.get(layer) else {
735                return false;
736            };
737            if !target.active {
738                return false;
739            }
740            target.renderers.len()
741        };
742        self.remember_canvas_node_coverage(
743            layer,
744            _node_id,
745            kurbo::Rect::new(
746                f64::NEG_INFINITY,
747                f64::NEG_INFINITY,
748                f64::INFINITY,
749                f64::INFINITY,
750            ),
751        );
752        let selected = self.targeted_or_all_indices(layer, renderer_count);
753        let began = !selected.is_empty();
754        if began {
755            self.push_render_scope(layer, selected);
756        }
757        began
758    }
759
760    fn begin_node_with_bounds(
761        &mut self,
762        layer: usize,
763        node_id: u32,
764        _z_index: i32,
765        coverage_bounds: kurbo::Rect,
766        _light_mask: u32,
767    ) -> bool {
768        let renderer_count = {
769            let Some((target, _)) = self.layers.get(layer) else {
770                return false;
771            };
772            if !target.active {
773                return false;
774            }
775            target.renderers.len()
776        };
777        self.remember_canvas_node_coverage(layer, node_id, coverage_bounds);
778        let candidate_indices = self.targeted_or_all_indices(layer, renderer_count);
779        let Some((target, _)) = self.layers.get(layer) else {
780            return false;
781        };
782        let selected: Vec<_> = target
783            .renderers
784            .iter()
785            .enumerate()
786            .filter(|(index, _)| candidate_indices.contains(index))
787            .filter_map(|(index, renderer)| {
788                renderer
789                    .intersects_coverage_bounds(&coverage_bounds)
790                    .then_some(index)
791            })
792            .collect();
793        let began = !selected.is_empty();
794        if began {
795            self.push_render_scope(layer, selected);
796        } else {
797            self.clean_skipped_canvas_nodes.insert((layer, node_id));
798        }
799        began
800    }
801
802    fn take_clean_skipped_node(&mut self, layer: usize, node_id: u32) -> bool {
803        self.clean_skipped_canvas_nodes.remove(&(layer, node_id))
804    }
805
806    fn end_node(&mut self, layer: usize, _node_id: u32) -> bool {
807        self.pop_render_scope(layer);
808        true
809    }
810
811    fn remove_node(&mut self, layer: usize, node_id: u32) -> bool {
812        self.forget_canvas_node_coverage(layer, node_id);
813        true
814    }
815}
816
817fn fill_to_piet_brush(fill: &Fill, rect: kurbo::Rect) -> piet::PaintBrush {
818    match fill {
819        Fill::Solid(color) => color.to_piet_color().into(),
820        Fill::LinearGradient(linear) => {
821            let linear_gradient = LinearGradient::new(
822                Fill::to_unit_point(linear.start, (rect.width(), rect.height())),
823                Fill::to_unit_point(linear.end, (rect.width(), rect.height())),
824                Fill::to_piet_gradient_stops(linear.stops.clone()),
825            );
826            linear_gradient.into()
827        }
828        Fill::RadialGradient(radial) => {
829            let origin = Fill::to_unit_point(radial.start, (rect.width(), rect.height()));
830            let center = Fill::to_unit_point(radial.end, (rect.width(), rect.height()));
831            let gradient_stops = Fill::to_piet_gradient_stops(radial.stops.clone());
832            let radial_gradient = RadialGradient::new(radial.radius, gradient_stops)
833                .with_center(center)
834                .with_origin(origin);
835            radial_gradient.into()
836        }
837    }
838}
839
840fn stroke_to_piet_style(stroke: &Stroke) -> StrokeStyle {
841    let mut style = StrokeStyle::new();
842    style.set_line_cap(match stroke.cap.get() {
843        StrokeCap::Butt => LineCap::Butt,
844        StrokeCap::Round => LineCap::Round,
845        StrokeCap::Square => LineCap::Square,
846    });
847    style.set_line_join(match stroke.join.get() {
848        StrokeJoin::Miter => LineJoin::Miter { limit: 4.0 },
849        StrokeJoin::Round => LineJoin::Round,
850        StrokeJoin::Bevel => LineJoin::Bevel,
851    });
852    style
853}