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

nordisk/facett

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()));
}