Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/facett

facett-map/tests/draw_geom_shared.rs

//! **A frame's `DrawGeom` coordinates are SHARED, not re-cloned** (LAW 2, C3).
//!
//! `WaysLayer::features_into` used to run `w.lonlat.clone()` + `w.holes.clone()`
//! per CPU-owned way per frame — a heap allocation and 16 B per vertex of churn
//! for geometry that had not changed (the cost `MapLayer::features_into`'s own
//! zero-copy measurement itemises). `DrawGeom` now carries `Arc`s cloned from the
//! owning layer's per-way slots, so frame N and frame N+1 must hand out THE SAME
//! allocation. `Arc::ptr_eq` is the applied-output oracle: it cannot be satisfied
//! by two equal copies, only by actual sharing.
//!
//! **Seen red**: with the slot bypassed (`Arc::new(w.lonlat.clone())` per frame —
//! the old behaviour lifted into the new types) every `ptr_eq` here fails —
//! recorded in the fix's report.

use egui::Color32;
use facett_map::Way;
use facett_map::layer::{DrawGeom, LandmassLayer, LatLon, MapLayer, MapView, WaysLayer};
use std::sync::Arc;

fn view() -> MapView {
    MapView::new(LatLon::new(59.5, 15.05), 9.0, (800.0, 600.0))
}

/// One feature's `(id, outer Arc, holes Arc)`.
type FeatureArcs = (i64, Arc<Vec<(f64, f64)>>, Option<Arc<Vec<Vec<(f64, f64)>>>>);

/// Collect `(id, outer Arc, holes Arc)` per feature of one frame.
fn frame_arcs(l: &dyn MapLayer, v: &MapView) -> Vec<FeatureArcs> {
    l.features(v)
        .into_iter()
        .filter_map(|f| match f.geom {
            DrawGeom::Line(pts) => Some((f.id, pts, None)),
            DrawGeom::Polygon { outer, holes } => Some((f.id, outer, Some(holes))),
            _ => None,
        })
        .collect()
}

/// Two frames of the same `WaysLayer` share every ring allocation pointer-for-
/// pointer — lines and polygons (with holes) alike.
#[test]
fn ways_layer_shares_ring_allocations_across_frames() {
    let mut poly = Way::filled(
        1,
        "water",
        vec![(15.0, 59.4), (15.1, 59.4), (15.1, 59.5), (15.0, 59.4)],
        Color32::BLUE,
        Color32::DARK_BLUE,
        1.0,
    );
    // A real hole ring, so the holes Arc is not vacuously empty.
    poly.holes = vec![vec![(15.04, 59.42), (15.06, 59.42), (15.05, 59.44)]];
    let line = Way::line(2, "road", vec![(15.0, 59.45), (15.2, 59.46)], Color32::GRAY, 2.0);
    let layer = WaysLayer::new("w", vec![poly, line]);
    let v = view();

    let a = frame_arcs(&layer, &v);
    let b = frame_arcs(&layer, &v);
    assert_eq!(a.len(), 2, "both fixture ways must draw ({} did)", a.len());
    assert_eq!(a.len(), b.len());
    for ((ia, oa, ha), (ib, ob, hb)) in a.iter().zip(&b) {
        assert_eq!(ia, ib);
        assert!(Arc::ptr_eq(oa, ob), "way {ia}: frame 2 re-allocated the outer ring instead of sharing it");
        if let (Some(ha), Some(hb)) = (ha, hb) {
            assert!(Arc::ptr_eq(ha, hb), "way {ia}: frame 2 re-allocated the hole rings");
            assert!(!ha.is_empty(), "the polygon fixture's holes must be real, or the hole assert is vacuous");
        }
    }
}

/// The landmass base fill — the single biggest ring on the pane — shares too.
#[test]
fn landmass_layer_shares_ring_allocations_across_frames() {
    let ring: Vec<(f64, f64)> = (0..64)
        .map(|k| {
            let t = k as f64 / 64.0 * std::f64::consts::TAU;
            (15.05 + 0.4 * t.cos(), 59.5 + 0.25 * t.sin())
        })
        .collect();
    let layer = LandmassLayer::from_rings("land", [ring], Color32::from_rgb(240, 235, 220));
    let v = view();
    let a = frame_arcs(&layer, &v);
    let b = frame_arcs(&layer, &v);
    assert_eq!(a.len(), 1, "the land ring must draw");
    assert!(Arc::ptr_eq(&a[0].1, &b[0].1), "the landmass outer ring was re-allocated between frames");
}