open-standards/wai/wai-pack-gpu/tests/kernels.rs
Transaction Science 836d0255df wai packaging stack: #426, #438 and #452's GPU tier, squashed onto main
One squashed history of three stacked PRs, onto main at b16816671:

- #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 commits 6ad92e2e8, 8695bc75f and fed8694f4 and 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]>
2026-10-24 20:31:28 -04:00

79 lines
3.8 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! 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());
}
}