makepad/libs/ai/models/splat/tests/artifact_contract.rs
Admin 7f59912916 libs/ai: one AI stack, replacing libs/ggml, llama, mlx, cuda, tts, voice2 and pbr_paint
The model code was spread across eight crates that had grown into each other:
ggml and cuda and mlx each owned part of a tensor runtime, llama and tts and
voice2 each owned part of a model, and libs/diffusion owned everything else.
They are now one tree with an explicit shape:

  libs/ai/cuda     — kernels and launch surface
  libs/ai/metal    — Metal shaders and the shim
  libs/ai/llm      — the language-model runtime (sessions, lanes, contexts,
                     the CUDA and Metal executors, the compiled Metal path)
  libs/ai/models/  — common, flux, h3, music, paint, speech, stems, vision

libs/diffusion is not deleted but demoted: what remains is the VALIDATOR
crate — several dozen `*_validate.rs` oracles that check a native
implementation against a reference, which is where they belong now that the
implementations live next door.

The functional work inside the move is mostly in the LLM runtime: N lanes that
draft while one verify batch serves all of them, per-slot prefill over a shared
folded attention arena, speculation that survives batching, and a scheduler
that reports rather than publishes. And in the CUDA build: a machine without
usable CUDA must still LINK (and say so), the default kernel arch is the
building machine's GPU, `NO_CUDA` forces the stub even where the toolkit
exists, and kernels compile in parallel with progress.

libs/video_flow is new here: classical optical flow estimation and the `mkfl`
motion-field payload — a flow field measured from a clip without a model,
which is what drives free-rate bounce-looping playback and the uprez/tween
enhance pipe.
2026-08-23 01:34:35 +02:00

165 lines
6.7 KiB
Rust

//! Artifact and checkpoint contracts.
//!
//! * The PLY this crate writes must load with `makepad_splat`, the in-repo
//! 3DGS reader the viewer uses — including the activations that reader
//! applies (exp on scales, sigmoid on opacity, SH DC -> color), which is
//! what proves the writer emitted PRE-activation values.
//! * The state-dict contracts are checked against the real released
//! safetensors headers when `MAKEPAD_SPLAT_WEIGHTS_DIR` points at a
//! directory holding the pinned files. Without it those checks skip, so
//! the suite still runs on a machine with no weights.
use makepad_ai_splat::splat::SplatWeights;
use makepad_ai_splat::splat_decoder::decoder_expected_tensors;
use makepad_ai_splat::splat_dino::SplatDino;
use makepad_ai_splat::splat_flow::flow_expected_tensors;
use makepad_ai_splat::splat_ply::{write_ply, PlySplat};
use makepad_ai_splat::splat_rand::{gaussian_offset_perturbation, SplatRng};
use std::path::PathBuf;
fn sample_splats(count: usize) -> Vec<PlySplat> {
let mut rng = SplatRng::new(5);
(0..count)
.map(|_| {
let mut rotation = [rng.normal(), rng.normal(), rng.normal(), rng.normal()];
let norm: f32 = rotation.iter().map(|v| v * v).sum::<f32>().sqrt().max(1e-6);
for value in &mut rotation {
*value /= norm;
}
PlySplat {
position: [rng.normal(), rng.normal(), rng.normal()],
f_dc: [rng.normal(), rng.normal(), rng.normal()],
opacity: rng.normal(),
scale: [-5.0 + rng.normal(), -5.0 + rng.normal(), -5.0 + rng.normal()],
rotation,
}
})
.collect()
}
#[test]
fn generated_ply_loads_with_the_in_repo_reader() {
let splats = sample_splats(64);
let bytes = write_ply(&splats);
// No path hint: the reader must recognise the PLY from its own magic.
let scene =
makepad_splat::load_splat_from_bytes(&bytes, None).expect("in-repo reader must accept it");
assert_eq!(scene.splats.len(), splats.len());
const SH_C0: f32 = 0.282_094_8;
for (loaded, source) in scene.splats.iter().zip(&splats) {
// Positions pass through unchanged.
for axis in 0..3 {
assert!((loaded.position[axis] - source.position[axis]).abs() < 1e-6);
}
// The reader exponentiates the scale columns: the writer must have
// emitted LOG scales.
for axis in 0..3 {
assert!(
(loaded.scale[axis] - source.scale[axis].exp()).abs() < 1e-6,
"{} vs {}",
loaded.scale[axis],
source.scale[axis].exp()
);
}
// ... sigmoids the opacity column: the writer emitted a LOGIT.
let want_alpha = 1.0 / (1.0 + (-source.opacity).exp());
assert!((loaded.color[3] - want_alpha).abs() < 1e-6);
// ... and reads f_dc as the SH DC term.
for channel in 0..3 {
let want = (0.5 + SH_C0 * source.f_dc[channel]).clamp(0.0, 1.0);
assert!((loaded.color[channel] - want).abs() < 1e-6);
}
// The reader stores rotations xyzw; the writer emits wxyz.
assert!((loaded.rotation[3] - source.rotation[0]).abs() < 1e-5);
assert!((loaded.rotation[0] - source.rotation[1]).abs() < 1e-5);
}
// Bounds are recomputed, so a non-degenerate cloud must have extent.
assert!(scene.bounds_max[0] > scene.bounds_min[0]);
}
#[test]
fn empty_and_single_splat_plys_are_still_valid() {
let scene = makepad_splat::load_splat_from_bytes(&write_ply(&[]), None).unwrap();
assert!(scene.splats.is_empty());
let bytes = write_ply(&sample_splats(1));
let scene = makepad_splat::load_splat_from_bytes(&bytes, Some(std::path::Path::new("a.ply")))
.unwrap();
assert_eq!(scene.splats.len(), 1);
}
/// `MAKEPAD_SPLAT_WEIGHTS_DIR` layout: the pinned files under their HF paths
/// or flat by basename.
fn weights_dir() -> Option<PathBuf> {
std::env::var_os("MAKEPAD_SPLAT_WEIGHTS_DIR").map(PathBuf::from)
}
fn open(dir: &std::path::Path, name: &str) -> Option<SplatWeights> {
let flat = dir.join(name);
if flat.exists() {
return SplatWeights::load(&flat).ok();
}
None
}
fn assert_contract(weights: &SplatWeights, expected: &[(String, Vec<usize>)], label: &str) {
let mut missing = Vec::new();
for (name, shape) in expected {
match weights.dtype_shape(name) {
Ok((_dtype, actual)) => assert_eq!(
&actual, shape,
"{label}: {name} is {actual:?}, the port expects {shape:?}"
),
Err(_) => missing.push(name.clone()),
}
}
assert!(missing.is_empty(), "{label}: missing {missing:?}");
// Every tensor in the file must be consumed (the DINO repack carries one
// extra `mask_token` the reference also drops).
let expected_names: std::collections::HashSet<&str> =
expected.iter().map(|(n, _)| n.as_str()).collect();
let unused: Vec<&String> = weights
.tensor_names()
.filter(|name| !expected_names.contains(name.as_str()))
.filter(|name| name.as_str() != "embeddings.mask_token")
.collect();
assert!(unused.is_empty(), "{label}: unread tensors {unused:?}");
}
#[test]
fn state_dict_contracts_match_the_released_checkpoints() {
let Some(dir) = weights_dir() else {
eprintln!("skipping: set MAKEPAD_SPLAT_WEIGHTS_DIR to check the real headers");
return;
};
let mut checked = 0usize;
if let Some(weights) = open(&dir, "triposplat_fp16.safetensors") {
assert_contract(&weights, &flow_expected_tensors(), "flow");
checked += 1;
}
if let Some(weights) = open(&dir, "triposplat_vae_decoder_fp16.safetensors") {
assert_contract(&weights, &decoder_expected_tensors(), "decoder");
checked += 1;
// The Hammersley generator must reproduce the checkpoint's own
// points_offset_perturbation buffer.
let stored = weights
.f32_shaped("gs.points_offset_perturbation", &[32, 3])
.unwrap();
let generated = gaussian_offset_perturbation(1.5);
for (a, b) in stored.iter().zip(&generated) {
// fp16 storage: compare at fp16 resolution.
assert!((a - b).abs() < 5e-3, "{a} vs {b}");
}
}
if let Some(weights) = open(&dir, "dino_v3_vit_h.safetensors") {
assert_contract(&weights, &SplatDino::expected_tensors(), "dino");
checked += 1;
}
// Pointing the variable at the wrong directory must fail loudly rather
// than silently reporting a green contract.
assert_eq!(
checked, 3,
"MAKEPAD_SPLAT_WEIGHTS_DIR={} held {checked}/3 pinned checkpoints",
dir.display()
);
}