wai-ops-gpu gains the kernel wgsl/splat.wgsl, one invocation a pixel and one workgroup a 16 x 16 tile, over the operator set's probed 64-bit products; the host projects, culls and orders. wai-rs's splat_exact::gpu (features splat_exact, ops_gpu) composes on every adapter found, and tests/splat_exact_gpu.rs holds every canonical and own-pose view of the splat corpus to the CPU's frame byte for byte and requires three edited kernels to be caught. The macos job of wai-ops-gpu.yml runs it under WAI_OPS_GPU_REQUIRE; the linux job builds it. Co-Authored-By: Claude Opus 5.5 <[email protected]>
85 lines
4.1 KiB
Rust
85 lines
4.1 KiB
Rust
//! `wai-splat-render:i1` with its compositing on a GPU (`splat_exact::gpu`,
|
||
//! extensions/splat-exact.md §5.6–§5.7, §11): every canonical view and every own-pose view of
|
||
//! every scene of `live-conformance/splat/` composes, on every adapter this process finds, to
|
||
//! the frame the CPU gives, byte for byte; and the corpus's views catch each of three edited
|
||
//! kernels (a contribution rounded down, no alpha ceiling, the cut-off dropped).
|
||
//!
|
||
//! **A skip is not a pass.** With no adapter the test prints `splat-exact gpu: SKIPPED` and
|
||
//! fails when `WAI_OPS_GPU_REQUIRE` is set; when `WAI_OPS_GPU_EXPECT` names an adapter key,
|
||
//! an adapter of that key must be among those found. Each adapter's result is one line,
|
||
//! `splat-exact gpu: key=… views=… differ=… mutants_survived=… (adapter)`, appended to
|
||
//! `WAI_OPS_GPU_REPORT` when it is set.
|
||
#![cfg(all(feature = "splat_exact", feature = "ops_gpu"))]
|
||
|
||
use std::io::Write;
|
||
|
||
use wai::splat_exact::gpu::{render, render_with, wai_ops_gpu};
|
||
use wai::splat_exact::{corpus, gaussians, render as cpu_render};
|
||
|
||
fn report(line: &str) {
|
||
println!("{line}");
|
||
if let Ok(p) = std::env::var("WAI_OPS_GPU_REPORT") {
|
||
let mut f = std::fs::OpenOptions::new().create(true).append(true).open(&p).unwrap();
|
||
writeln!(f, "{line}").unwrap();
|
||
}
|
||
}
|
||
|
||
/// Edits of the kernel, each a rule of §5.6 changed.
|
||
fn mutants() -> Vec<(&'static str, String)> {
|
||
let src = wai_ops_gpu::splat::source();
|
||
let edit = |from: &str, to: &str| {
|
||
assert_eq!(src.matches(from).count(), 1, "{from}");
|
||
src.replace(from, to)
|
||
};
|
||
vec![
|
||
("contribution rounded down", edit("let c = (alpha * t + 32768u) >> 16u;", "let c = (alpha * t) >> 16u;")),
|
||
("no alpha ceiling", edit("(w & 0xFFFFu)) >> 16u), 64880u);", "(w & 0xFFFFu)) >> 16u), 65536u);")),
|
||
("no cut-off", edit("if (d2 > CUTOFF) {", "if (d2 > CUTOFF * 2) {")),
|
||
]
|
||
}
|
||
|
||
#[test]
|
||
fn every_view_composes_on_the_gpu_to_the_cpu_frame() {
|
||
let found = wai_ops_gpu::blocking::discover();
|
||
for (a, why) in &found.refused {
|
||
println!("adapter refused: {} ({why})", a.describe());
|
||
}
|
||
if let Ok(want) = std::env::var("WAI_OPS_GPU_EXPECT") {
|
||
assert!(found.found.iter().any(|g| g.adapter().key() == want), "WAI_OPS_GPU_EXPECT is {want}, and no adapter of that key was found");
|
||
}
|
||
if found.found.is_empty() {
|
||
report("splat-exact gpu: SKIPPED (no adapter)");
|
||
assert!(std::env::var_os("WAI_OPS_GPU_REQUIRE").is_none(), "WAI_OPS_GPU_REQUIRE is set and no adapter was found");
|
||
return;
|
||
}
|
||
let scenes: Vec<_> = corpus::scenes().into_iter().map(|(n, p, poses)| (n, gaussians(&p).unwrap(), poses)).collect();
|
||
let muts = mutants();
|
||
for g in &found.found {
|
||
let (mut views, mut equal) = (0, 0);
|
||
let mut caught = vec![false; muts.len()];
|
||
for (name, gs, poses) in &scenes {
|
||
for (i, p) in poses.iter().enumerate() {
|
||
let cpu = cpu_render(gs, p).expect("a frame");
|
||
let gpu = render(g, gs, p).expect("the device ran").expect("a frame");
|
||
views += 1;
|
||
if gpu == cpu {
|
||
equal += 1;
|
||
} else {
|
||
let n = cpu.iter().zip(&gpu).filter(|(a, b)| a != b).count();
|
||
println!("{name} view {i}: {n} bytes differ on {}", g.adapter().key());
|
||
}
|
||
for (m, (_, text)) in muts.iter().enumerate() {
|
||
if !caught[m] && render_with(g, Some(text), gs, p).expect("the device ran").expect("a frame") != cpu {
|
||
caught[m] = true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
let n_caught = caught.iter().filter(|c| **c).count();
|
||
report(&format!("splat-exact gpu: key={} views={views} differ={} mutants_survived={} ({})", g.adapter().key(), views - equal, muts.len() - n_caught, g.adapter().describe()));
|
||
assert_eq!(equal, views, "every view composes to the CPU's frame on {}", g.adapter().key());
|
||
for (m, c) in caught.iter().enumerate() {
|
||
assert!(c, "the mutant '{}' was not caught on {}", muts[m].0, g.adapter().key());
|
||
}
|
||
}
|
||
}
|