facett-map/tests/still_frame_work.rs
//! **WHAT A STILL FRAME ACTUALLY REDOES** — lane GG #11, and the measurement audit #40
//! says the whole of PART 2 was missing: *"no pan-vs-still bench split"*.
//!
//! # Why the measurement comes before the fix
//!
//! Audit #11 says `place_map_labels` *"sorts label_idx and lays out labels EVERY frame,
//! 0.96–9.45 ms — the dominant still-frame CPU cost"*, and prescribes a memo. That is a
//! claim about work that DID happen, and this lane has already found three audit claims
//! that did not survive being run (#2's transparent halo, #4's pass-3 lift, #7's flattened
//! shores). So the seam is counted first and the number reported before anything is
//! memoised.
//!
//! A COUNT and not a stopwatch, deliberately. A memo's value is work that does NOT happen,
//! and work that does not happen is invisible in the output; a wall time moves with
//! whatever else the box was doing, while *1 layout over 60 still frames* against *60* is
//! the same answer on any machine. The argument `lane_scans` and `gpu_sig_walks` already
//! make in this crate.
//!
//! ```bash
//! cargo test -p facett-map --test still_frame_work -- --nocapture
//! ```
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use facett_map::layer::{LatLon, MapFacet, MapTheme, MapView, WaysLayer};
use facett_map::Way;
const CANVAS: egui::Vec2 = egui::vec2(900.0, 600.0);
const FRAMES: usize = 60;
/// A roster with real NAMED ways — a layout with nothing to lay out would measure the
/// early return and not the text shaping the audit is about.
fn named_ways(n: usize) -> Vec<Way> {
(0..n)
.map(|i| {
let lat = 59.30 + (i as f64) * 0.0004;
let tags = vec![
("highway".to_string(), "residential".to_string()),
("name".to_string(), format!("Testgatan {i}")),
];
Way::from_lonlat_with(
i as i64,
&[(18.00, lat), (18.06, lat + 0.0002), (18.12, lat)],
&tags,
&facett_map::MapPalette::cartographic(false),
)
.expect("a 3-vertex named way")
})
.collect()
}
/// Drive `FRAMES` frames and hand back `(label layouts, wall time)`.
///
/// The facet moves into the harness's closure, so the seam is read from INSIDE it into a
/// shared cell — the only shape `build_ui` allows, and it reads the count after each
/// frame rather than once at the end so a memo that fired late would still show.
fn drive(ways: Vec<Way>, pan: bool) -> (usize, f64) {
let seen = Arc::new(AtomicUsize::new(0));
let sink = Arc::clone(&seen);
let view = MapView::new(LatLon::new(59.33, 18.06), 13.0, (CANVAS.x, CANVAS.y));
let mut f = MapFacet::new("still-frame-work", view, MapTheme::default())
.with_layer(Box::new(WaysLayer::new("osm-ways", ways)));
let mut h = egui_kittest::Harness::builder().with_size(CANVAS).build_ui(move |ui| {
if pan {
// A pan is a CAMERA move and not a data change — exactly the case a memo
// must not swallow. Driven through the facet's own gesture seam so it is the
// same path a wheel/drag takes.
f.view_mut().center.lon += 0.0002;
}
f.ui(ui);
sink.store(f.label_layout_count(), Ordering::Relaxed);
});
let t = std::time::Instant::now();
for _ in 0..FRAMES {
h.run();
}
let ms = t.elapsed().as_secs_f64() * 1000.0;
(seen.load(Ordering::Relaxed), ms)
}
/// **THE MEASUREMENT, reported before it is judged.**
///
/// If the still count equals the frame count the audit is right and the layout runs every
/// frame. If it is 1, it is already memoised and #11 is closed by something else. Either
/// way the number goes in the log, and the assertion below only holds the property that
/// must be true whatever the answer.
#[test]
fn report_what_a_still_frame_and_a_pan_each_cost_in_label_layouts() {
let ways = named_ways(400);
let (still_n, still_ms) = drive(ways.clone(), false);
let (pan_n, pan_ms) = drive(ways, true);
eprintln!(
"[GG11] 400 named ways, {FRAMES} frames — STILL: {still_n} label layouts, \
{still_ms:.1} ms ({:.2} ms/frame) | PAN: {pan_n} layouts, {pan_ms:.1} ms \
({:.2} ms/frame)",
still_ms / FRAMES as f64,
pan_ms / FRAMES as f64,
);
// NON-VACUITY: the frames really ran and the seam really fired. A count of 0 would
// mean the harness never reached the painter, and every number above would be noise.
assert!(
still_n > 0 && pan_n > 0,
"the label layout never ran ({still_n} still / {pan_n} pan) — the harness did not \
reach the painter, so nothing here measures anything"
);
// A PAN must lay out again: the camera moved, so the boxes are in different places.
// This is the property a memo must not break, and it is asserted now so the memo
// cannot be written without it.
assert!(
pan_n > 1,
"a panning frame must re-place its labels — the camera moved, so a memo that \
held across a pan would pin every name to where it used to be"
);
// ── CHARACTERISATION: there is no memo today, and that is deliberate ─────────
//
// MEASURED here: **62 layouts over 60 still frames** — the layout runs on every
// frame, so audit #11's premise is correct. Its prescribed cure is not: the doc above
// `place_map_labels` already argues, from its own measurement, that a
// `(camera, roster)` memo is **wrong by construction** — the placement is computed
// against egui's font state (`layout_no_wrap` shapes through `Context::fonts`) and
// **the font definitions and atlas carry no version a `MapFacet` can read**. Confirmed
// by inspection: `epaint 0.35`'s `Fonts` exposes no generation, version or epoch.
// A stale answer here is not a slow frame — it is names painted into rects the painter
// no longer measures, which `label_collision.rs` asserts on.
//
// And the prerequisite that doc names — *"a facet-side (text, FontId,
// pixels_per_point) galley cache that makes the shaping cost, not the placement, the
// thing that is saved"* — **already exists upstream**: `epaint::text::fonts` has a
// `GalleyCache` with generation-based GC, so identical text at the same font and
// pixels-per-point is already shaped once and reused across frames. Building one in
// this crate would be a LAW 5 twin of it and would buy nothing.
//
// So what is left per frame is the FILTER + SORT + the placement solver, not the
// shaping — and that IS safely memoisable, by a key the font state enters through the
// measured galley SIZES rather than through a version nobody exposes. That is the
// shape of the fix, and it is a change of its own.
//
// This assertion pins today's answer so the next person to touch it does so with the
// numbers in hand rather than by accident.
assert!(
still_n >= FRAMES,
"there is no label memo today and this test records that on purpose ({still_n} \
layouts over {FRAMES} still frames). If this has fallen, someone memoised the \
placement — good, and this file is where the before/after belongs: update the \
numbers above, keep the pan assertion, and say how the key covers font state."
);
}