facett-helix/tests/gpu_bloom.rs
//! **GPU render-proof — the helix on the emissive bloom lane (reused from
//! facett-graph3d).**
//!
//! Drives [`HelixView::gpu_scene`] headlessly through
//! [`facett_graph3d::graph_gpu::render_graph_offscreen`]: it projects the helix nodes +
//! the soft rubberband spline edges into instanced emissive discs/filaments, renders
//! them into the shared HDR offscreen (additive blend) and runs the bright→blur→
//! composite bloom chain, then reads the LDR framebuffer + draw counts back. Every
//! assertion is on DATA (draw counts, luminance, bloom A/B energy) — never a pixel.
//!
//! Run: `cargo test -p facett-helix --features wgpu --test gpu_bloom`
//! (needs a wgpu adapter / `lavapipe`; self-skips with a printed note when none is
//! present, so a CPU-only box never sees a false failure).
#![cfg(feature = "wgpu")]
use facett_graph3d::graph_gpu::{render_graph_offscreen, BloomParams};
use facett_helix::HelixView;
/// Serialise GPU work across test threads (spinning up several software adapters
/// concurrently can SIGSEGV in lavapipe).
static GPU_GUARD: std::sync::Mutex<()> = std::sync::Mutex::new(());
/// CPU-only oracle (no GPU): `gpu_scene` projects every node + emits spline-edge
/// segments, and the emissive scaling pushes radiance **above 1.0** so the bright pass
/// (threshold ≈ 1.0) catches the helix. Runs on every box.
#[test]
fn gpu_scene_emits_emissive_helix_instances() {
let v = HelixView::demo().with_clock(1.0);
let scene = v.gpu_scene(256.0, 256.0);
assert!(scene.nodes.len() >= 6, "one instance per helix node");
assert!(!scene.edges.is_empty(), "the rounded spline edges emit filament segments");
let peak = scene.nodes.iter().flat_map(|n| [n.color[0], n.color[1], n.color[2]]).fold(0.0f32, f32::max);
assert!(peak > 1.0, "node emissive radiance must exceed 1.0 to bloom (got {peak})");
}
/// INJECT-ASSERT (render proof): the GPU lane draws the helix's node + spline-segment
/// instances, produces a **non-blank** frame, and bloom adds measurable energy
/// (bloom-on mean luminance strictly exceeds bloom-off).
#[test]
fn helix_gpu_draws_with_bloom_energy() {
let _guard = GPU_GUARD.lock().unwrap_or_else(|p| p.into_inner());
let v = HelixView::demo().with_clock(1.0);
let scene = v.gpu_scene(256.0, 256.0);
let (w, h) = (256, 256);
let Some(on) = render_graph_offscreen(w, h, &scene, BloomParams::default()) else {
facett_core::testmatrix::gpu_skip(
"facett-helix",
"helix_gpu_draws_with_bloom_energy",
"no wgpu adapter",
"the helix lane issues every node and spline-edge instance and the bloom pass \
adds measurable energy",
);
return;
};
assert_eq!(on.node_instances as usize, scene.nodes.len(), "all node instances issued");
assert_eq!(on.edge_instances as usize, scene.edges.len(), "all spline-edge segments issued");
let luma_on = on.mean_luma();
eprintln!("[helix-gpu] mean luma bloom-on = {luma_on:.5}");
assert!(luma_on > 0.01, "frame must be non-blank (got {luma_on:.5})");
let off = render_graph_offscreen(w, h, &scene, BloomParams::default().off()).expect("adapter present");
let luma_off = off.mean_luma();
eprintln!("[helix-gpu] mean luma bloom-off = {luma_off:.5}");
assert!(luma_on > luma_off * 1.02, "bloom must add energy: on={luma_on:.5} > off={luma_off:.5}");
}