facett-map/tests/side_carries_depth.rs
//! **Depth rides in `LineVertex::side`, keeping the vertex at 48 B** — lane MP-a.
//!
//! # Why per-DRAW depth is not enough
//!
//! The tile lane's draw is **already band-major** (`gpu/mod.rs`: *"One run per declared
//! draw band"*, groups single-band by construction, sorted on `(draw_band, tile_key)`).
//! So bands already order correctly across tiles — and the two-tile oracle still fails,
//! because `way_draw_band` **deliberately drops the tier**: motorway and residential are
//! both `LINE_BAND + 200`, one band. Inside a band the draw walks TILES, and the
//! casing/fill two-pass runs per draw, so a later tile's casing lands on an earlier
//! tile's fill.
//!
//! Depth therefore has to be **per way**, not per draw. And it cannot be a new vertex
//! field: `LineVertex` is exactly 48 B, asserted at compile time, and that size is
//! load-bearing — the vertex layout, the chunk partition and the bake format all rest
//! on it.
//!
//! # The spare bit
//!
//! `side` is `+1.0` or `-1.0`: one bit of information in a whole `f32`. Packing the
//! depth into its magnitude — `sign(side) * (1.0 + depth)` — keeps the vertex at 48 B and
//! costs one `abs`/`sign` pair in the shader.
//!
//! Precision is the thing to check rather than assume: in `[1, 2)` an `f32` step is
//! `2^-23 ≈ 1.19e-7`, so a depth is representable to about that, and
//! [`CASING_DEPTH_BIAS`] (`1e-6`) is comfortably above it — a casing and its fill stay
//! distinguishable after the round trip. That is asserted here, not argued.
//!
//! | gate | red arm |
//! |---|---|
//! | side survives the round trip, both signs | drop the sign |
//! | depth survives to better than the casing bias | pack into 8 bits |
//! | the packed value never lands on 0, where `sign` is ambiguous | allow depth −1 |
//!
//! ```bash
//! cargo test -p facett-map --test side_carries_depth
//! ```
use facett_map::paint_order::{pack_side_depth, unpack_depth, unpack_side, CASING_DEPTH_BIAS};
/// **Both halves come back.**
#[test]
fn the_side_and_the_depth_both_survive() {
for side in [1.0f32, -1.0] {
for depth in [0.0f32, 0.25, 0.5, 0.75, 1.0] {
let p = pack_side_depth(side, depth);
assert_eq!(unpack_side(p), side, "side lost for depth {depth}");
assert!(
(unpack_depth(p) - depth).abs() < CASING_DEPTH_BIAS / 4.0,
"depth {depth} came back {} — the error must stay well under the casing \
bias {CASING_DEPTH_BIAS}, or a casing and its own fill collapse",
unpack_depth(p)
);
}
}
}
/// **A casing and its fill stay apart after the round trip.** This is the whole reason
/// the precision matters: if the packing cannot resolve `CASING_DEPTH_BIAS`, the dark
/// outline lands exactly on the bright line it outlines and the z-fight is worse than
/// the bug being fixed.
#[test]
fn a_casing_and_its_fill_stay_distinguishable() {
let d = facett_map::paint_order::depth_of(1240, false);
let c = facett_map::paint_order::depth_of(1240, true);
let (pd, pc) = (pack_side_depth(1.0, d), pack_side_depth(1.0, c));
assert!(
unpack_depth(pc) > unpack_depth(pd),
"after packing, the casing must still be BEHIND its fill: {} vs {}",
unpack_depth(pc),
unpack_depth(pd)
);
assert_ne!(pd, pc, "the two must not pack to the same bits");
}
/// **The packed value is never zero**, where `sign` cannot tell `+` from `−`. The
/// magnitude starts at `1.0`, so this holds by construction — asserted because the
/// construction is the guarantee.
#[test]
fn the_packed_value_never_reaches_the_ambiguous_zero() {
for side in [1.0f32, -1.0] {
for depth in [0.0f32, 1.0] {
let p = pack_side_depth(side, depth);
assert!(p.abs() >= 1.0, "packed {p} for side {side}, depth {depth}");
}
}
}