One squashed history of three stacked PRs, onto main atb16816671: - #426 (wai/pack-efficiency): a title's whole AV1 ladder in one package over one integer base; several floor rungs (packaging §7.1), a re-pack from a parent (§11.5), a floor that is the ladder's own CMAF (§7.7), a floor-only profile (§7.8), a streaming pack (§11.4), and the independent verifier at scale (§9.4). - #438 (wai/cross-fabric): each pack and verify step on the fabric with the least measured energy (§11.6); the `fabrics` member of measurement claims (jwp-receipts §2.4.8, energy-measurement §6.3); the wai-pack-gpu crate; `wai-pack fabric`; `wai-pack floor-decode`. - #452 (wai/gpu-int-tier), its GPU-tier part only: claiming decode-equivalence on a GPU (determinism-tiers §5), the WGSL integer semantics suite, the rules check on naga's IR, and the kernels' shift masks and min(max()) clamps. Left out, by the review's split (EN-C takes it over): #452's platform decode in dispatch, its commits6ad92e2e8,8695bc75fandfed8694f4and its CHANGELOG entry: extensions/platform-decode.md, platform-decode-conformance/, wai::platform_decode, wai-web's platform_decode bundle and demo, and its two SPEC.md sentences. #438's research note is not carried as written: it is rewritten as wai/research/cross-fabric.md, with hardware named by architecture, PCI id and OS version, and every table marked as one run. floor_decode.rs and a wai_pack.rs comment name the platform's media engine and a full-length film the same way. wai/CHANGELOG.md is the union of both sides' entries, the stack's first. Co-Authored-By: Claude Opus 5.5 <[email protected]>
79 lines
3.8 KiB
Rust
79 lines
3.8 KiB
Rust
//! Each kernel against its reference on every adapter that opens: the motion search
|
||
//! against packaging §5.5's search as the text states it, the SSE against the plain sum,
|
||
//! BLAKE3 against the reference crate on the test vectors' lengths. Every case is seeded,
|
||
//! so a difference names its case. With no adapter, each test prints SKIPPED and passes,
|
||
//! unless WAI_PACK_GPU_REQUIRE is set.
|
||
|
||
mod support;
|
||
|
||
use support::*;
|
||
|
||
#[test]
|
||
fn the_motion_search_is_the_texts() {
|
||
for g in gpus() {
|
||
let mut rng = Rng(0x5eed_0001);
|
||
let mut n = 0;
|
||
for &(h, w, block, range, prec) in MOTION_CASES {
|
||
for (kind, src, rf) in frame_pairs(&mut rng, h, w) {
|
||
let got = g.motion_frame(&src, &rf, h, w, block, range, prec).expect("the kernel runs");
|
||
let want = search(&src, &rf, h, w, block, range, prec);
|
||
assert_eq!(got, want, "{}: {kind} {h}x{w} block {block} range {range} prec {prec}", g.adapter().label());
|
||
n += 1;
|
||
}
|
||
}
|
||
eprintln!("{}: motion search, {n} frames, every vector the text's", g.adapter().label());
|
||
}
|
||
}
|
||
|
||
/// What the kernel does not take is refused before anything runs.
|
||
#[test]
|
||
fn the_motion_search_refuses_what_it_does_not_take() {
|
||
for g in gpus() {
|
||
let f = vec![0u8; 4 * 4 * 3];
|
||
let un = |r: Result<Vec<(i16, i16)>, wai_pack_gpu::GpuError>| matches!(r, Err(wai_pack_gpu::GpuError::Unsupported(_)));
|
||
assert!(un(g.motion_frame(&f, &f, 4, 4, 4, 1, 3)), "precision 3");
|
||
assert!(un(g.motion_frame(&f, &f, 4, 4, 4, 128, 2)), "range 128");
|
||
assert!(un(g.motion_frame(&f, &f, 4, 5, 4, 1, 2)), "a frame that is not h × w × 3");
|
||
assert!(un(g.motion_frame(&f, &f, 4, 4, 0, 1, 2)), "block 0");
|
||
assert!(un(g.motion_frame(&f, &f, 4, 4, 2370, 1, 2)), "a block whose cost can pass 2^32 - 1");
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn the_sse_is_the_sum() {
|
||
for g in gpus() {
|
||
let mut rng = Rng(0x5eed_0002);
|
||
for n in [0usize, 1, 3, 4, 5, 4095, 4096, 4097, 65_537, 1_048_579] {
|
||
let (a, b) = (rng.bytes(n), rng.bytes(n));
|
||
assert_eq!(g.sse(&a, &b).unwrap(), sse(&a, &b), "{}: {n} random bytes", g.adapter().label());
|
||
}
|
||
// The largest partial: every byte 0 against 255, 4,096 to a thread.
|
||
let (a, b) = (vec![0u8; 3 * 4096 + 7], vec![255u8; 3 * 4096 + 7]);
|
||
assert_eq!(g.sse(&a, &b).unwrap(), sse(&a, &b), "{}: the largest squares", g.adapter().label());
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn blake3_is_the_reference() {
|
||
for g in gpus() {
|
||
let inputs: Vec<Vec<u8>> = BLAKE3_LENGTHS.iter().map(|&n| blake3_input(n)).collect();
|
||
let refs: Vec<&[u8]> = inputs.iter().map(|v| v.as_slice()).collect();
|
||
// All at once, in one dispatch, and each alone.
|
||
let many = g.blake3_many(&refs).unwrap();
|
||
for (m, h) in refs.iter().zip(&many) {
|
||
assert_eq!(h, blake3::hash(m).as_bytes(), "{}: {} bytes, in a batch", g.adapter().label(), m.len());
|
||
assert_eq!(&g.blake3_many(&[m]).unwrap()[0], blake3::hash(m).as_bytes(), "{}: {} bytes alone", g.adapter().label(), m.len());
|
||
}
|
||
// Random messages at random lengths, hashed in batches of mixed sizes.
|
||
let mut rng = Rng(0x5eed_0003);
|
||
let msgs: Vec<Vec<u8>> = (0..200).map(|_| {
|
||
let n = rng.below(20_000) as usize;
|
||
rng.bytes(n)
|
||
}).collect();
|
||
let refs: Vec<&[u8]> = msgs.iter().map(|v| v.as_slice()).collect();
|
||
for (m, h) in refs.iter().zip(g.blake3_many(&refs).unwrap()) {
|
||
assert_eq!(&h, blake3::hash(m).as_bytes(), "{}: a random message of {} bytes", g.adapter().label(), m.len());
|
||
}
|
||
eprintln!("{}: BLAKE3, {} lengths and 200 random messages, every hash the reference crate's", g.adapter().label(), BLAKE3_LENGTHS.len());
|
||
}
|
||
}
|