GPU-compute building extrusion — guide
How to use the GPU-compute building extrusion (FLIGHT-SIM #3b). Everything up to the storage buffers works with no GPU; the dispatch needs the wgpu feature + an adapter.
Marshal footprints (no GPU needed)
use facett_map3d_flight::{Footprint, BuildingMarshal};
let footprints = vec![
Footprint::new(vec![(0.0,0.0),(0.6,0.0),(0.6,0.3),(0.3,0.3),(0.3,0.6),(0.0,0.6)], 0.4), // concave L
Footprint { ring: quad, height: 0.25, min_height: 0.0, building_type: 3 },
];
let marshal = BuildingMarshal::from_footprints(&footprints);
// marshal.contour : flat [f32;2] ring vertices (open, CCW)
// marshal.caps : flat u32 ear-clip cap indices
// marshal.meta : Vec<BuildingMeta> (prefix-summed output bases)
// marshal.total_vertices / total_indices : output buffer sizes
from_footprints opens + CCW-normalises each ring, precomputes its cap triangulation with the shared §3a ear-clip, and stamps the per-building BuildingMeta. Degenerate footprints (< 3 distinct vertices / collinear) are silently dropped.
The CPU reference / fallback (no GPU needed)
let mesh = marshal.cpu_extrude(); // Mesh3 — same op extrude.wgsl runs, on the CPU
Use this as the fallback on a host with no adapter (wasm / a laptop tester / a deck pane), and as the oracle the GPU is validated against. For a single footprint it is vertex-for-vertex equal to facett_map3d::extrude_prism_mesh (the landed CPU path).
Dispatch on the GPU (wgpu feature + adapter)
use facett_map3d_flight::ExtrudeGpu;
let ext = ExtrudeGpu::new(&device); // builds the pipeline (validates the WGSL)
let mesh = ext.extrude(&device, &queue, &marshal); // Mesh3 read back from the compute pass
extrude uploads the three storage buffers, dispatches cs_extrude (one invocation per building), and reads the positions/normals/indices back. The output is what an indirect draw_indexed_indirect consumes when the render-pass wiring lands (roadmap).
Inspect a building's bounds
let (lo, hi) = BuildingMarshal::building_aabb(&mesh, &marshal.meta[0]);
// lo/hi = design-plane (x,z) bbox × (min_height .. height) for that building
Check parity (GPU vs CPU reference)
use facett_map3d_flight::geom_parity;
let rep = geom_parity(&gpu_mesh, &cpu_mesh);
assert!(rep.index_match && rep.max_pos_err < 1e-4);
Emitters (gated #[cfg(feature = "testmatrix")])
extrude_gpu_marshal_oracle— the GPU-less gate: marshalled extrusion == CPU oracle (vertex/tri counts,worst_pos/worst_nrmatf32precision).extrude_gpu_dispatch_parity— GPU present:index_match+max_pos_err/max_nrm_errvs the CPU reference; GPU absent: the skip sentinel.