|
| 1 | +//! M1 — motion compensation via the cognitive-shader primitives. |
| 2 | +//! |
| 3 | +//! Proof-of-concept for the codec↔cognitive-shader unification documented in |
| 4 | +//! `.claude/knowledge/pr-x12-codec-cognitive-substrate-mapping.md`: |
| 5 | +//! |
| 6 | +//! - **E-7** — "block-matching motion estimation *is* i8gemm." HEVC ME does |
| 7 | +//! SAD; reformulated as SSD `‖A‖² − 2·A·B + ‖B‖²`, the middle term `A·B` is |
| 8 | +//! a GEMM. This PoC runs the search through the SHIPPED |
| 9 | +//! `hpc::quantized::int8_gemm_i32` (the same u8×i8→i32 VNNI kernel the |
| 10 | +//! cognitive shader uses for attention/distance) — NOT a bespoke SAD loop. |
| 11 | +//! - **H-7** — "the codec *is* the substrate." Each block's residual |
| 12 | +//! magnitude classifies into the SHIPPED `hpc::codec::CellMode` |
| 13 | +//! (Skip/Merge/Delta/Escape) — the codec's mode taxonomy IS the MC |
| 14 | +//! per-block decision. |
| 15 | +//! |
| 16 | +//! Pipeline (all shipped primitives, zero new substrate): |
| 17 | +//! 1. reference frame (deterministic 7-bit-luma texture) |
| 18 | +//! 2. current frame = per-block shifted reference + small residual (known MVs) |
| 19 | +//! 3. **ME**: per block, `int8_gemm_i32` computes `A·B` for all candidates in |
| 20 | +//! a ±R window; SSD = `‖B‖² − 2·A·B` (+ const `‖A‖²`) → argmin → MV. |
| 21 | +//! 4. **cross-check**: brute-force direct SSD gives the ground-truth argmin. |
| 22 | +//! E-7 holds iff the GEMM argmin == the brute-force argmin for EVERY block. |
| 23 | +//! 5. **MC**: gather the reference block at the recovered MV (contiguous |
| 24 | +//! block fetch) + add the stored residual → reconstruction. |
| 25 | +//! 6. classify residual → `CellMode`; assert reconstruction is bit-exact. |
| 26 | +//! |
| 27 | +//! What this PROVES: motion estimation runs through the shader's i8 GEMM and |
| 28 | +//! is bit-identical to direct SSD; motion compensation is gather+add; |
| 29 | +//! reconstruction is exact; the per-block decision is the codec's mode taxonomy. |
| 30 | +//! What this does NOT do (that's M2, the multi-month decoder): parse a real |
| 31 | +//! `.265` bitstream (CABAC entropy decode + inverse transform to extract the |
| 32 | +//! MVs/residuals), sub-pel interpolation, deblock/SAO. Frames are synthetic |
| 33 | +//! 7-bit luma with integer-translation MVs so the i8 GEMM is exact. |
| 34 | +//! |
| 35 | +//! Run: `cargo run --release --example mc_via_shader --features codec` |
| 36 | +
|
| 37 | +use ndarray::hpc::codec::CellMode; |
| 38 | +use ndarray::hpc::quantized::int8_gemm_i32; |
| 39 | + |
| 40 | +const W: usize = 128; |
| 41 | +const H: usize = 128; |
| 42 | +const B: usize = 8; // block edge (8×8) |
| 43 | +const K: usize = B * B; // pixels per block = GEMM K |
| 44 | +const BX: usize = W / B; // blocks per row |
| 45 | +const BY: usize = H / B; // blocks per column |
| 46 | +const R: i32 = 4; // motion search radius (±R) |
| 47 | + |
| 48 | +/// SplitMix64 — deterministic, dependency-free per-pixel texture + residual. |
| 49 | +fn mix(mut z: u64) -> u64 { |
| 50 | + z = z.wrapping_add(0x9E37_79B9_7F4A_7C15); |
| 51 | + z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9); |
| 52 | + z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB); |
| 53 | + z ^ (z >> 31) |
| 54 | +} |
| 55 | + |
| 56 | +/// 7-bit luma [0,127] so the value fits both `u8` (current) and `i8` |
| 57 | +/// (reference) operands of the VNNI kernel with no centering games. |
| 58 | +fn ref_pixel(x: usize, y: usize) -> u8 { |
| 59 | + (mix((y as u64) << 20 | x as u64) & 0x7F) as u8 |
| 60 | +} |
| 61 | + |
| 62 | +/// Ground-truth per-block MV — a deterministic small field, forced to 0 near |
| 63 | +/// the border so `block_pos + mv + B` stays in bounds. |
| 64 | +fn gt_mv(bx: usize, by: usize) -> (i32, i32) { |
| 65 | + let raw_x = ((bx + 2 * by) % 5) as i32 - 2; |
| 66 | + let raw_y = ((2 * bx + by) % 5) as i32 - 2; |
| 67 | + // keep block + mv + search window inside the frame |
| 68 | + let px = (bx * B) as i32; |
| 69 | + let py = (by * B) as i32; |
| 70 | + let ok = |p: i32, d: i32, span: i32| p + d - R >= 0 && p + d + R + B as i32 <= span; |
| 71 | + let mvx = if ok(px, raw_x, W as i32) { raw_x } else { 0 }; |
| 72 | + let mvy = if ok(py, raw_y, H as i32) { raw_y } else { 0 }; |
| 73 | + (mvx, mvy) |
| 74 | +} |
| 75 | + |
| 76 | +/// Small deterministic residual on ~1/3 of blocks (drives Delta); the rest |
| 77 | +/// are residual-free (drive Skip). Values kept tiny (|r| ≤ 3). |
| 78 | +fn residual(bx: usize, by: usize, i: usize) -> i32 { |
| 79 | + if !(bx * 7 + by * 3).is_multiple_of(3) { |
| 80 | + return 0; // residual-free block |
| 81 | + } |
| 82 | + ((mix((bx as u64) << 40 | (by as u64) << 20 | i as u64) % 7) as i32) - 3 |
| 83 | +} |
| 84 | + |
| 85 | +fn ref_block(reference: &[u8], px: i32, py: i32) -> [i8; K] { |
| 86 | + let mut blk = [0i8; K]; |
| 87 | + for j in 0..B { |
| 88 | + for i in 0..B { |
| 89 | + blk[j * B + i] = reference[(py as usize + j) * W + (px as usize + i)] as i8; |
| 90 | + } |
| 91 | + } |
| 92 | + blk |
| 93 | +} |
| 94 | + |
| 95 | +fn main() { |
| 96 | + // ── 1. reference frame ─────────────────────────────────────────────── |
| 97 | + let reference: Vec<u8> = (0..H * W).map(|p| ref_pixel(p % W, p / W)).collect(); |
| 98 | + |
| 99 | + // ── 2. current frame = shifted-ref + residual (known MVs) ──────────── |
| 100 | + let mut current = vec![0u8; H * W]; |
| 101 | + for by in 0..BY { |
| 102 | + for bx in 0..BX { |
| 103 | + let (mvx, mvy) = gt_mv(bx, by); |
| 104 | + let (px, py) = ((bx * B) as i32, (by * B) as i32); |
| 105 | + for j in 0..B { |
| 106 | + for i in 0..B { |
| 107 | + let refv = reference[(py + mvy + j as i32) as usize * W + (px + mvx + i as i32) as usize] as i32; |
| 108 | + let v = (refv + residual(bx, by, j * B + i)).clamp(0, 127); |
| 109 | + current[(py as usize + j) * W + (px as usize + i)] = v as u8; |
| 110 | + } |
| 111 | + } |
| 112 | + } |
| 113 | + } |
| 114 | + |
| 115 | + // ── 3+4. ME via i8 GEMM, cross-checked against brute-force SSD ─────── |
| 116 | + let mut recovered = vec![(0i32, 0i32); BX * BY]; |
| 117 | + let mut gemm_eq_brute = 0usize; // E-7: GEMM argmin == direct-SSD argmin |
| 118 | + let mut mv_exact = 0usize; // recovered MV == ground-truth MV |
| 119 | + let mut gemm_calls = 0usize; |
| 120 | + let t0 = std::time::Instant::now(); |
| 121 | + |
| 122 | + for by in 0..BY { |
| 123 | + for bx in 0..BX { |
| 124 | + let (px, py) = ((bx * B) as i32, (by * B) as i32); |
| 125 | + // current block A (u8), row vector [1×K] |
| 126 | + let mut a = [0u8; K]; |
| 127 | + for j in 0..B { |
| 128 | + for i in 0..B { |
| 129 | + a[j * B + i] = current[(py as usize + j) * W + (px as usize + i)]; |
| 130 | + } |
| 131 | + } |
| 132 | + |
| 133 | + // enumerate in-bounds candidates; build B = [K × N] (candidate per |
| 134 | + // column: b[k*N + n]) so int8_gemm_i32(1,N,K) yields C[n] = A·B_n. |
| 135 | + let mut cand: Vec<(i32, i32)> = Vec::with_capacity(((2 * R + 1) * (2 * R + 1)) as usize); |
| 136 | + for dy in -R..=R { |
| 137 | + for dx in -R..=R { |
| 138 | + let (cx, cy) = (px + dx, py + dy); |
| 139 | + if cx >= 0 && cy >= 0 && cx + B as i32 <= W as i32 && cy + B as i32 <= H as i32 { |
| 140 | + cand.push((dx, dy)); |
| 141 | + } |
| 142 | + } |
| 143 | + } |
| 144 | + let n = cand.len(); |
| 145 | + let mut bmat = vec![0i8; K * n]; |
| 146 | + let mut b_sq = vec![0i64; n]; // ‖B_n‖² |
| 147 | + for (col, &(dx, dy)) in cand.iter().enumerate() { |
| 148 | + let blk = ref_block(&reference, px + dx, py + dy); |
| 149 | + let mut s = 0i64; |
| 150 | + for k in 0..K { |
| 151 | + bmat[k * n + col] = blk[k]; |
| 152 | + s += (blk[k] as i64) * (blk[k] as i64); |
| 153 | + } |
| 154 | + b_sq[col] = s; |
| 155 | + } |
| 156 | + |
| 157 | + // THE SHADER KERNEL: C[1×N] = A[1×K] · B[K×N] (u8 × i8 → i32) |
| 158 | + let mut c = vec![0i32; n]; |
| 159 | + int8_gemm_i32(&a, &bmat, &mut c, 1, n, K); |
| 160 | + gemm_calls += 1; |
| 161 | + |
| 162 | + // SSD_n = ‖A‖² − 2·(A·B_n) + ‖B_n‖²; ‖A‖² is const per block → |
| 163 | + // argmin over (‖B_n‖² − 2·C[n]). |
| 164 | + let gemm_arg = (0..n) |
| 165 | + .min_by_key(|&nn| b_sq[nn] - 2 * c[nn] as i64) |
| 166 | + .unwrap(); |
| 167 | + |
| 168 | + // brute-force direct SSD = Σ (A_k − B_k)² — the ground truth. |
| 169 | + let brute_arg = (0..n) |
| 170 | + .min_by_key(|&nn| { |
| 171 | + let (dx, dy) = cand[nn]; |
| 172 | + let blk = ref_block(&reference, px + dx, py + dy); |
| 173 | + (0..K) |
| 174 | + .map(|k| { |
| 175 | + let d = a[k] as i64 - blk[k] as i64; |
| 176 | + d * d |
| 177 | + }) |
| 178 | + .sum::<i64>() |
| 179 | + }) |
| 180 | + .unwrap(); |
| 181 | + |
| 182 | + if gemm_arg == brute_arg { |
| 183 | + gemm_eq_brute += 1; |
| 184 | + } |
| 185 | + let mv = cand[gemm_arg]; |
| 186 | + recovered[by * BX + bx] = mv; |
| 187 | + if mv == gt_mv(bx, by) { |
| 188 | + mv_exact += 1; |
| 189 | + } |
| 190 | + } |
| 191 | + } |
| 192 | + let me_ms = t0.elapsed().as_secs_f64() * 1000.0; |
| 193 | + |
| 194 | + // ── 5+6. MC (gather ref block @ recovered MV) + residual; classify + verify |
| 195 | + let mut recon = vec![0u8; H * W]; |
| 196 | + let mut modes = [0usize; 4]; // skip, merge, delta, escape |
| 197 | + let mut max_err = 0i32; |
| 198 | + for by in 0..BY { |
| 199 | + for bx in 0..BX { |
| 200 | + let (px, py) = ((bx * B) as i32, (by * B) as i32); |
| 201 | + let (mvx, mvy) = recovered[by * BX + bx]; |
| 202 | + // prediction = reference block at MV (contiguous gather) |
| 203 | + let pred = ref_block(&reference, px + mvx, py + mvy); // i8 == 7-bit luma |
| 204 | + let mut maxres = 0i32; |
| 205 | + for j in 0..B { |
| 206 | + for i in 0..B { |
| 207 | + let k = j * B + i; |
| 208 | + let cur = current[(py as usize + j) * W + (px as usize + i)] as i32; |
| 209 | + let res = cur - pred[k] as i32; // stored residual = current − prediction |
| 210 | + maxres = maxres.max(res.abs()); |
| 211 | + let r = (pred[k] as i32 + res).clamp(0, 255); // reconstruction = pred + residual |
| 212 | + recon[(py as usize + j) * W + (px as usize + i)] = r as u8; |
| 213 | + max_err = max_err.max((r - cur).abs()); |
| 214 | + } |
| 215 | + } |
| 216 | + // per-block decision = codec mode: residual magnitude → CellMode |
| 217 | + let mode = if maxres == 0 { |
| 218 | + CellMode::Skip |
| 219 | + } else if maxres <= 127 && recovered[by * BX + bx] == recovered[by.saturating_sub(1) * BX + bx] { |
| 220 | + CellMode::Merge // MV inherited from N neighbour + in-range residual |
| 221 | + } else if maxres <= 127 { |
| 222 | + CellMode::Delta |
| 223 | + } else { |
| 224 | + CellMode::Escape |
| 225 | + }; |
| 226 | + modes[mode as usize] += 1; |
| 227 | + } |
| 228 | + } |
| 229 | + |
| 230 | + // ── report ─────────────────────────────────────────────────────────── |
| 231 | + let nblk = BX * BY; |
| 232 | + let pct = |x: usize| 100.0 * x as f64 / nblk as f64; |
| 233 | + println!("M1 — motion compensation via cognitive-shader primitives"); |
| 234 | + println!(" frame {W}×{H}, {B}×{B} blocks = {nblk} blocks, 7-bit luma, ±{R} search\n"); |
| 235 | + println!( |
| 236 | + " [E-7] i8gemm SSD argmin == brute-force direct SSD : {gemm_eq_brute}/{nblk} ({:.1}%)", |
| 237 | + pct(gemm_eq_brute) |
| 238 | + ); |
| 239 | + println!(" → motion estimation IS int8_gemm_i32 (the shader kernel), bit-identical to SAD/SSD"); |
| 240 | + println!(" MV recovery (i8gemm ME == ground-truth motion) : {mv_exact}/{nblk} ({:.1}%)", pct(mv_exact)); |
| 241 | + println!(" MC reconstruction max abs pixel error : {max_err} (0 = bit-exact)"); |
| 242 | + println!(" [H-7] per-block CellMode histogram (residual → mode):"); |
| 243 | + println!(" skip={} merge={} delta={} escape={}", modes[0], modes[1], modes[2], modes[3]); |
| 244 | + println!( |
| 245 | + " throughput: {gemm_calls} int8_gemm_i32 calls (one per block) in {me_ms:.2} ms → {:.0} blocks/s", |
| 246 | + nblk as f64 / (me_ms / 1000.0) |
| 247 | + ); |
| 248 | + |
| 249 | + // hard asserts — the PoC's falsifiers |
| 250 | + assert_eq!(gemm_eq_brute, nblk, "E-7 FALSIFIED: i8gemm ME disagrees with direct SSD"); |
| 251 | + assert_eq!(max_err, 0, "MC reconstruction is not bit-exact"); |
| 252 | + println!("\n RESULT: E-7 holds (ME == i8gemm), MC is bit-exact, decision = codec modes. Concept proven."); |
| 253 | +} |
0 commit comments