facett-map3d/tests/raster_fanout.rs
//! **The gatling scanline raster is byte-identical to the serial z-buffer** (LAW 2).
//!
//! `raster_zbuffer` fans whole scanlines across znippy's fork-join gatling — each
//! worker owns disjoint `color` + `invz` rows, and each row runs its triangles in
//! the SERIAL triangle order (the CSR y-bucket fill preserves it), so every pixel
//! sees the same depth-test sequence the triangle-outer serial loop ran. The claim
//! is therefore bit identity of BOTH framebuffers, not "the same picture": colour
//! AND interpolated `1/z`, compared on bits (`f32::to_bits`), fanned vs the
//! `workers = 1` serial reference on a real projected scene.
//!
//! The reference is NOT the new kernel at `workers = 1` — that would be blind to a
//! bug in the shared row kernel itself (proved: an `(y+1) % h` row-shift probe
//! passed a fanned-vs-workers-1 comparison, because both sides ran the same wrong
//! closure). The reference is the PRE-FAN triangle-outer serial loop, carried
//! verbatim in [`reference_raster`] below and frozen there on purpose.
//!
//! **Seen red**: with the row hand-off deliberately broken (`invz` row taken at
//! `(y + 1) % h` instead of `y`) the depth buffer diverges against this reference —
//! recorded in the fix's report. A CSR bucket fill that reordered triangles within
//! a row, or a worker writing a row it did not own, fails the same assertions.
use egui::{Color32, Pos2};
use facett_map3d::extrude::{GroundTerrain, RoofStyle, build_with_water_terrain_styled};
use facett_map3d::{OrbitCamera, ShadedTri, raster_zbuffer, raster_zbuffer_buffers, shade_mesh_tris};
/// **The pre-fan serial z-buffer rasteriser, verbatim** (facett-map3d/src/lib.rs as
/// of the commit before the gatling fan) — the frozen byte-identity reference. Do
/// not "sync" it with the library kernel: its whole value is that it is the OLD
/// algorithm (triangle-outer, pixel-inner, one thread), so a bug in the new shared
/// row kernel cannot hide by appearing on both sides of the comparison.
fn reference_raster(
tris: &[ShadedTri],
width: usize,
height: usize,
origin: Pos2,
ppp: f32,
) -> (Vec<Color32>, Vec<f32>) {
let mut color = vec![Color32::TRANSPARENT; width * height];
let mut invz = vec![0.0f32; width * height];
let (fw, fh) = (width as f32, height as f32);
for t in tris {
let px = [
(t.pts[0].x - origin.x) * ppp,
(t.pts[1].x - origin.x) * ppp,
(t.pts[2].x - origin.x) * ppp,
];
let py = [
(t.pts[0].y - origin.y) * ppp,
(t.pts[1].y - origin.y) * ppp,
(t.pts[2].y - origin.y) * ppp,
];
let iz = [1.0 / t.vz[0].max(1e-4), 1.0 / t.vz[1].max(1e-4), 1.0 / t.vz[2].max(1e-4)];
let area = (px[1] - px[0]) * (py[2] - py[0]) - (px[2] - px[0]) * (py[1] - py[0]);
if area.abs() < 1e-6 {
continue;
}
let inv_area = 1.0 / area;
let minx = px.iter().copied().fold(f32::MAX, f32::min).floor().clamp(0.0, fw - 1.0) as usize;
let maxx = px.iter().copied().fold(f32::MIN, f32::max).ceil().clamp(0.0, fw - 1.0) as usize;
let miny = py.iter().copied().fold(f32::MAX, f32::min).floor().clamp(0.0, fh - 1.0) as usize;
let maxy = py.iter().copied().fold(f32::MIN, f32::max).ceil().clamp(0.0, fh - 1.0) as usize;
let col = t.col;
for y in miny..=maxy {
for x in minx..=maxx {
let (sx, sy) = (x as f32 + 0.5, y as f32 + 0.5);
let w0 = ((px[1] - sx) * (py[2] - sy) - (px[2] - sx) * (py[1] - sy)) * inv_area;
let w1 = ((px[2] - sx) * (py[0] - sy) - (px[0] - sx) * (py[2] - sy)) * inv_area;
let w2 = 1.0 - w0 - w1;
if w0 < -1e-4 || w1 < -1e-4 || w2 < -1e-4 {
continue;
}
let d = w0 * iz[0] + w1 * iz[1] + w2 * iz[2];
let idx = y * width + x;
if d > invz[idx] {
invz[idx] = d;
color[idx] = col;
}
}
}
}
(color, invz)
}
/// A real shaded-triangle scene: the rabbit-less synthetic town projected through
/// the shared CPU projection kernel — the exact input the painter rasterises.
fn scene(w: f32, h: f32) -> Vec<facett_map3d::ShadedTri> {
let theme = facett_core::Theme::default();
let (lon0, lat0) = (17.70, 59.60);
let step = 0.0012;
let mut ways = Vec::new();
let mut id = 1i64;
for j in 0..10 {
for i in 0..10 {
let x = lon0 + i as f64 * step;
let y = lat0 + j as f64 * step;
let s = step * 0.45;
let ring = [(x, y), (x + s, y), (x + s, y + s), (x, y + s), (x, y)];
let tags = if (i + j) % 3 == 0 {
vec![("building".to_string(), "yes".to_string()), ("height".to_string(), format!("{}", 6 + (i + j) % 24))]
} else {
vec![("landuse".to_string(), "residential".to_string())]
};
if let Some(way) = facett_map::Way::from_lonlat(id, &ring, &tags, &theme) {
ways.push(way);
id += 1;
}
}
}
let (mesh, ..) = build_with_water_terrain_styled(&ways, 0.2, GroundTerrain::Flat, RoofStyle::Pitched);
let cam = OrbitCamera::default();
shade_mesh_tris(&mesh, &cam, egui::pos2(w * 0.5, h * 0.5), h * 0.45, egui::Color32::from_rgb(180, 170, 150))
}
/// Fanned (one worker per core) vs the frozen pre-fan serial reference on a frame
/// past the 64 k-pixel parallel threshold: both buffers bit-identical, and the
/// frame is not vacuously empty.
#[test]
fn fanned_raster_is_byte_identical_to_serial() {
let (w, h) = (640usize, 400usize); // 256 000 px > the 65 536 px threshold
let tris = scene(w as f32, h as f32);
assert!(tris.len() > 100, "scene projected only {} tris — the raster proof would be vacuous", tris.len());
let origin = egui::pos2(0.0, 0.0);
let (sc, sz) = reference_raster(&tris, w, h, origin, 1.0);
let (fc, fz) = raster_zbuffer_buffers(&tris, w, h, origin, 1.0, 0);
// The kernel's own single-worker mode must ALSO match the old loop — it is the
// path every sub-threshold frame takes in production.
let (kc, kz) = raster_zbuffer_buffers(&tris, w, h, origin, 1.0, 1);
assert_eq!(sc, kc, "workers=1 kernel color differs from the pre-fan serial loop");
assert!(sz.iter().zip(&kz).all(|(a, b)| a.to_bits() == b.to_bits()), "workers=1 kernel invz differs from the pre-fan serial loop");
assert_eq!(sc, fc, "color framebuffer differs fanned vs serial");
let (mut covered, mut diverged) = (0usize, 0usize);
for (k, (a, b)) in sz.iter().zip(&fz).enumerate() {
if a.to_bits() != b.to_bits() {
diverged += 1;
if diverged == 1 {
eprintln!("first invz divergence at px {k}: serial {a} vs fanned {b}");
}
}
if *a > 0.0 {
covered += 1;
}
}
assert_eq!(diverged, 0, "invz framebuffer diverges on {diverged} pixels");
// Not a blank frame: the identity must be about real coverage, not two empty
// buffers agreeing (the identity-value trap).
assert!(covered > 10_000, "only {covered} covered pixels — the scene barely rasterised");
// And the public entry returns exactly the fanned color buffer.
assert_eq!(raster_zbuffer(&tris, w, h, origin, 1.0), fc);
}
/// **Wall-time measurement** (LAW 7 — run explicitly, release, on the real box):
/// `cargo test -p facett-map3d --release --test raster_fanout -- --ignored --nocapture`
/// Serial reference vs fanned raster at a real 1600×1000 window. Ignored in the
/// normal suite: it is a measurement, not a guard.
#[test]
#[ignore = "wall-time measurement; run --release with --ignored --nocapture"]
fn measure_fanned_vs_serial_raster() {
let (w, h) = (1600usize, 1000usize);
// A dense deterministic screen-space load (an LCG scatter of ~40 000 small
// triangles) — the projected-town fixture above is right for the identity
// proof but only leaves a few hundred triangles on screen, which measures the
// buffer clear more than the raster.
let mut seed = 0x5EED_1234_u64;
let mut rnd = move || {
seed = seed.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
(seed >> 33) as f32 / (1u64 << 31) as f32
};
let tris: Vec<ShadedTri> = (0..40_000)
.map(|_| {
let (cx, cy) = (rnd() * w as f32, rnd() * h as f32);
let r = 6.0 + rnd() * 30.0;
let z = 5.0 + rnd() * 80.0;
ShadedTri {
pts: [
egui::pos2(cx, cy - r),
egui::pos2(cx - r, cy + r),
egui::pos2(cx + r, cy + r),
],
vz: [z, z + rnd(), z + rnd()],
depth: z,
col: Color32::from_rgb((rnd() * 255.0) as u8, (rnd() * 255.0) as u8, 200),
}
})
.collect();
let origin = egui::pos2(0.0, 0.0);
let best = |f: &dyn Fn()| {
(0..5)
.map(|_| {
let t0 = std::time::Instant::now();
f();
t0.elapsed()
})
.min()
.unwrap()
};
let serial = best(&|| {
let _ = reference_raster(&tris, w, h, origin, 1.0);
});
let kernel1 = best(&|| {
let _ = raster_zbuffer_buffers(&tris, w, h, origin, 1.0, 1);
});
let fanned = best(&|| {
let _ = raster_zbuffer_buffers(&tris, w, h, origin, 1.0, 0);
});
eprintln!(
"raster fan: {} tris @ {w}x{h} — old serial {:?}, kernel workers=1 {:?}, fanned {:?} = {:.2}x vs old",
tris.len(),
serial,
kernel1,
fanned,
serial.as_secs_f64() / fanned.as_secs_f64()
);
}
/// Below the pixel threshold the raster stays on the serial rows — still identical
/// to the frozen reference, asserted so a future threshold change cannot silently
/// flip small frames into a differently-ordered path.
#[test]
fn small_frame_stays_identical_too() {
let (w, h) = (160usize, 120usize); // 19 200 px < 65 536
let tris = scene(w as f32, h as f32);
let origin = egui::pos2(0.0, 0.0);
let (sc, sz) = reference_raster(&tris, w, h, origin, 1.25);
let (fc, fz) = raster_zbuffer_buffers(&tris, w, h, origin, 1.25, 0);
assert_eq!(sc, fc);
assert!(sz.iter().zip(&fz).all(|(a, b)| a.to_bits() == b.to_bits()));
}