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/frame_churn_memo.rs

//! **The per-frame plan/signature churn is memoised — proven by counting the work**
//! (LAW 2).
//!
//! Two `O(ways)` derivations ran on every frame of a pane whose layers had not
//! changed: `way_lane_plan` (two `.any()` scans of every way, re-asked again by
//! every `way_lane()` reader — `state_json` alone is 3.3×/s in korp) and
//! `gpu_geometry_signature` (a bbox+vertex fold over every taken way, twice — once
//! per lane). Both answers are pinned by `O(layers)` inputs: the facet's
//! `layer_epoch`, the renderer latch, the origin, and each `WaysLayer`'s
//! construction-stamped `geometry_generation`.
//!
//! "It is cached" is a claim about work that did NOT happen, and the painted frame
//! looks identical either way — so the oracle is a COUNTER on the work itself
//! (`way_lane_scan_count` / `gpu_sig_walk_count`), not the output.
//!
//! **Seen red**: with the memo bypassed (early-return removed) both counters climb
//! to the ask count (101 / 101 where 1 / 1 is asserted) — recorded in the fix's
//! report.
#![cfg(feature = "wgpu")]

use egui::Color32;
use facett_map::Way;
use facett_map::layer::{LatLon, MapFacet, MapTheme, MapView, WayLane, WaysLayer};

fn ways(seed: f64) -> Vec<Way> {
    let mut v = Vec::new();
    for k in 0..40 {
        let y = 47.0 + seed + k as f64 * 0.01;
        // Unfilled polylines — the GPU LINE lane's food.
        v.push(Way::line(k, "road", vec![(9.0, y), (9.2, y + 0.004), (9.4, y)], Color32::GRAY, 2.0));
        // Filled closed rings — the GPU FILL lane's food.
        let x = 9.0 + k as f64 * 0.01;
        v.push(Way::filled(
            1000 + k,
            "water",
            vec![(x, 47.5 + seed), (x + 0.008, 47.5 + seed), (x + 0.008, 47.508 + seed), (x, 47.5 + seed)],
            Color32::BLUE,
            Color32::DARK_BLUE,
            1.0,
        ));
    }
    v
}

fn facet() -> MapFacet {
    MapFacet::new(
        "memo",
        MapView::new(LatLon::new(47.3, 9.2), 9.0, (800.0, 600.0)),
        MapTheme::default(),
    )
    .with_layer(Box::new(WaysLayer::new("w", ways(0.0))))
}

/// A hundred unchanged frames ask for the lane plan; the way scan runs ONCE. A
/// layer toggle (the epoch) re-scans exactly once more.
#[test]
fn lane_plan_scans_once_across_unchanged_frames() {
    facett_map::gpu::set_renderer_installed_for_test(true);
    let mut f = facet();
    let first = f.way_lane();
    // Non-vacuous: with the latch forced on and both way kinds present, the plan
    // must actually take the layer — otherwise the scan being counted never
    // exercised its interesting arms.
    assert_eq!(first, WayLane::Gpu { layers: 1 }, "plan must take the ways layer");
    for _ in 0..100 {
        assert_eq!(f.way_lane(), first);
    }
    assert_eq!(f.way_lane_scan_count(), 1, "unchanged layers must not re-scan the ways");
    // The epoch: a toggle changes the answer's inputs, so exactly one more scan.
    f.toggle_layer("w");
    for _ in 0..100 {
        let _ = f.way_lane();
    }
    assert_eq!(f.way_lane_scan_count(), 2, "one toggle = one re-scan, not one per frame");
    facett_map::gpu::set_renderer_installed_for_test(false);
}

/// A hundred unchanged frames ask for the VRAM signature; the `O(ways)` walk runs
/// ONCE. A moved origin or a new layer re-walks exactly once each — and changes
/// the signature (the latch must still see real geometry changes).
#[test]
fn geometry_signature_walks_once_across_unchanged_frames() {
    let mut f = facet();
    let origin = [0.52, 0.31];
    let s1 = f.gpu_geometry_signature_for_test(&[0], origin);
    for _ in 0..100 {
        assert_eq!(f.gpu_geometry_signature_for_test(&[0], origin), s1);
    }
    assert_eq!(f.gpu_sig_walk_count(), 1, "unchanged geometry must not re-walk the ways");
    // A moved origin is a different bake — new key, one more walk, different sig.
    let s2 = f.gpu_geometry_signature_for_test(&[0], [0.11, 0.87]);
    assert_eq!(f.gpu_sig_walk_count(), 2);
    assert_ne!(s1, s2, "a moved origin must change the signature");
    // korp's data-change shape: a NEW WaysLayer (new generation) — one more walk,
    // different sig, and the OLD key still answers from... a fresh walk, because the
    // epoch moved (correctness beats reuse across epochs).
    f.push_layer(Box::new(WaysLayer::new("w2", ways(1.0))));
    let s3 = f.gpu_geometry_signature_for_test(&[0, 1], origin);
    assert_eq!(f.gpu_sig_walk_count(), 3);
    assert_ne!(s3, s1, "a new way set must change the signature");
}