makepad/libs/model/tests/portable_identity.rs
Admin 0e64721efa render, model, platform, audio_synth, ai: shadow atlases clipped per cascade and face, portable math for identical model builds everywhere, foliage and city rendering, Score replaces the synth sequencer, and sandbox audio generation fixes
- render: CSM cascades and spot/point shadow faces draw inside a per-draw scissor, with a depth shader that has no discard for solid and rigid casters (downtown city shadow pass 3.3 ms → 0.4 ms, spot shadows 2.7 → 0.1 ms); cascade selection by caster bounds on Apple; per-frame model draw order built once; clustered light terms per pixel; foliage wind, impostors, grass and LOD model sets; city glass and interior light; the viewmodel eye-space fix for narrow panes; joint-following parts use the skin's exact joint matrix.
- model / csg / mesh_edit / remesh / gltf: makepad_csg_math::portable (sin/cos/atan/exp/ln/pow/hypot/cbrt from exactly rounded operations), used at 320 call sites, so a model hashes and builds byte-identically on macOS and Linux (MODEL_GENERATOR_VERSION 2, CHARACTER_GENERATOR_VERSION 42; tests/portable_identity.rs); lathes always face outward; sculpted-shell booleans with a tolerant surface map; decal and badge lettering; LOD model sets; grip poses for first-person arms.
- platform: DrawCall scissor rectangles (Metal and Vulkan; other backends keep the old clip); draw_shader_ready for a fallback while a pipeline compiles; Metal clock reads once per frame; Linux render-texture readback.
- audio_synth: score.rs and its Sequencer are removed; game music now runs on Stage's Score engine; drum_note moves into instruments.rs. Includes Stage SF1's SoundFontInstrument change: programs share one loaded font instead of ~300 MB each.
- ai: the sfx model generates at whole seconds and trims to the requested length (fractional lengths came back as noise); fleet roles let node 165 serve audio as a fallback.
- widgets/core: screen recordings of any app inside a Makepad checkout land in <makepad>/local/screencap.
- fab: FabDragNumber read_only, for the sandbox time dial.

Checked on node 165: cargo check --release --workspace; makepad crate suites 1442 passed; sandbox 3095 passed; portable model hashes identical on the Mac mini.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 08:11:15 +02:00

55 lines
2.7 KiB
Rust

//! One program, one product, on every machine. Models built on a Mac and on
//! Linux must be byte-identical: the store addresses built products by
//! content, so a product that differs per platform splits the store and
//! misses every cache the other machine filled. These goldens were recorded
//! on Linux (node 165) and pass unchanged on macOS (the Mac mini); a
//! transcendental routed back through the platform libm breaks them there.
use makepad_model::json::{self, Value};
use makepad_model::build_program;
use makepad_model::Limits;
fn fnv(bytes: &[u8]) -> u64 {
bytes.iter().fold(0xcbf2_9ce4_8422_2325u64, |h, b| (h ^ *b as u64).wrapping_mul(0x100_0000_01b3))
}
fn product(program: &str) -> u64 {
let ops = match json::parse(program.as_bytes()).unwrap() {
Value::Arr(ops) => ops,
_ => unreachable!(),
};
let built = build_program(&ops, Limits::default(), &mut |alias| Err(format!("no import {alias}")), None).unwrap();
fnv(&built.compiled.glb)
}
const PROGRAMS: &[(&str, &str)] = &[
("sphere+cylinder", r#"[{"op":"sphere","object":"ball","radius":0.5,"segments":24,"rings":12,"smooth":true},
{"op":"cylinder","object":"post","radius":0.2,"height":2,"segments":17,"smooth":true}]"#),
("lathe+sweep", r#"[{"op":"lathe","object":"vase","profile":[[0,0],[0.7,0.1],[0.8,0.8],[0.3,1.7],[0,1.8]],"axis":1,"segments":23,"caps":true,"material":0,"outward":true},
{"op":"sweep","object":"rail","profile":[[-0.1,-0.1],[0.1,-0.1],[0.1,0.1],[-0.1,0.1]],"path":[[0,0,0],[0,0.4,1],[0.5,0.9,2],[1.4,1.1,2.6]],"caps":true,"material":0}]"#),
("shapes", r#"[{"op":"capsule","object":"pill","radius":0.3,"height":1.2,"segments":19,"rings":7,"material":0},
{"op":"cone","object":"cone","radius":0.6,"top_radius":0.2,"height":1.1,"segments":21,"smooth":true,"material":0},
{"op":"torus","object":"ring","radius":0.8,"tube":0.15,"segments":29,"sides":11,"material":0}]"#),
("character", r#"[{"op":"character","preset":"halcyon_warden"}]"#),
];
/// Recorded on Linux. A mismatch on another platform means some geometry
/// still goes through the platform libm (see `makepad_csg_math::portable`).
const GOLDEN: &[(&str, u64)] = &[
("sphere+cylinder", 0x73f3_858b_4f3d_f1cd),
("lathe+sweep", 0x0dc2_4fc8_8b97_c245),
("shapes", 0x4e83_3371_811a_a309),
("character", 0x95fb_81d9_a066_ffc0),
];
#[test]
fn every_platform_builds_the_same_bytes() {
let mut got = Vec::new();
for (name, program) in PROGRAMS {
got.push((*name, product(program)));
}
for (name, hash) in &got {
eprintln!("portable identity: {name} {hash:#018x}");
}
assert_eq!(got, GOLDEN, "a built product changed on this platform");
}