code wiki / _hdl_build / nx_v128_probe.nx

nx_v128_probe.nx source

↩ module page · 298 lines · 10915 B

1// nx_v128_probe.nx -- F618 DIFFERENTIAL PROBE, POST-FLIP FORM (2026-07-21; no main; every fn exported 2// by the wat backend, imported natively by nx_v128_gate). Since the FLIP, the SHIPPING vm_* kernels 3// (nx_vmotion.nx) route T==16 rows through v128_sad16 -- scalar natively, INTERCEPTED to wasm-SIMD by 4// the shared emitter. The scalar REFERENCE therefore lives HERE as pvr_* copies of the pre-flip vm_* 5// bodies (pure per-pixel loops, no v128_sad16 call, so the intercept can never touch them). 6// pv_sweep_scalar / pv_time_scalar -> pvr_* (reference, scalar in EVERY lane) 7// pv_sweep_simd / pv_time_simd -> vm_* (shipping path: scalar native, SIMD in wasm) 8// The sweeps compute an ORDER-SENSITIVE rolling checksum over best-SAD + MV per macroblock (full 9// center-first search, cap-aborts live) plus an UNALIGNED raw-SAD pass and a capped-SAD pass. 10// scalar-chk == simd-chk == native-chk is the bit-exactness claim, mechanically. i64 multiply wraps 11// mod 2^64 identically in native x86, the sovereign VM, and V8 -- the checksum is portable-exact. 12// NOTE (inherited, both arms identically): sys_mmap is a 0-stub in the wasm lane, so the searchers' 13// 32-byte b scratch sits at linear address 0 (the shipped codec's own behavior; sequential calls 14// re-init it). Frames must live well above -- callers use base >= 0x20000. 15// license_tier: ORIGINAL 16import "nx_syscalls.nx" 17import "nx_vmotion.nx" 18const K_MAGIC_2147483647: i64 = 2147483647 19const K_MAGIC_5381: i64 = 5381 20const K_MAGIC_1000003: i64 = 1000003 21const K_MAGIC_8192: i64 = 8192 22const K_MAGIC_4096: i64 = 4096 23 24// ---- pvr_*: the pre-flip scalar reference chain (bodies = pre-flip vm_* verbatim) ---- 25func pvr_sad(cur: *u8, prev: *u8, W: i64, cx: i64, cy: i64, px: i64, py: i64, T: i64) -> i64 { 26 var s: i64 = 0 27 var yy: i64 = 0 28 while yy < T { 29 let crow: i64 = (cy + yy) * W + cx 30 let prow: i64 = (py + yy) * W + px 31 var xx: i64 = 0 32 while xx < T { 33 let d: i64 = (cur[crow + xx] as i64) - (prev[prow + xx] as i64) 34 if d < 0 { s = s - d } else { s = s + d } 35 xx = xx + 1 36 } 37 yy = yy + 1 38 } 39 return s 40} 41func pvr_sad_capped(cur: *u8, prev: *u8, W: i64, cx: i64, cy: i64, px: i64, py: i64, T: i64, cap: i64) -> i64 { 42 var s: i64 = 0 43 var yy: i64 = 0 44 while yy < T { 45 let crow: i64 = (cy + yy) * W + cx 46 let prow: i64 = (py + yy) * W + px 47 var xx: i64 = 0 48 while xx < T { 49 let d: i64 = (cur[crow + xx] as i64) - (prev[prow + xx] as i64) 50 if d < 0 { s = s - d } else { s = s + d } 51 xx = xx + 1 52 } 53 if s >= cap { return s } 54 yy = yy + 1 55 } 56 return s 57} 58// ONE probe-owned driver, kernel-SELECTED (k=0 scalar reference pvr_sad_capped, k=1 the shipping 59// vm_sad_capped that the wat backend lowers to fused v128). F1111: this replaces the old pvr_try/ 60// pvr_search_q pair, which were a HAND-COPY of production's vm_search_q -- so the moment production's 61// SEARCH STRATEGY changed (adaptive radius), this KERNEL gate went RED for a reason that had nothing to 62// do with the kernel. A gate must test ONE thing. The driver is now probe-owned and FIXED (full-R scan, 63// deadzone floor), so both arms are identical by construction and the ONLY difference is which SAD runs. 64// Production search correctness is covered where it belongs: bgop par=0 + nx_vcodec_layout_gate. 65func pv_try_k(cur: *u8, prev: *u8, W: i64, H: i64, cx: i64, cy: i64, T: i64, dx: i64, dy: i64, b: *i64, k: i64) -> i64 { 66 let px: i64 = cx + dx 67 let py: i64 = cy + dy 68 if px >= 0 { if py >= 0 { if px + T <= W { if py + T <= H { 69 var s: i64 = 0 70 if k == 0 { s = pvr_sad_capped(cur, prev, W, cx, cy, px, py, T, b[0]) } 71 else { s = vm_sad_capped(cur, prev, W, cx, cy, px, py, T, b[0]) } 72 if s < b[0] { b[0] = s; b[1] = dx; b[2] = dy } 73 } } } } 74 return 0 75} 76func pv_search_k(cur: *u8, prev: *u8, W: i64, H: i64, bx: i64, by: i64, T: i64, R: i64, mv: *i64, qp: i64, k: i64) -> i64 { 77 let cx: i64 = bx * T 78 let cy: i64 = by * T 79 let b: *i64 = sys_mmap(32) as *i64 80 b[0] = K_MAGIC_2147483647 81 b[1] = 0 82 b[2] = 0 83 pv_try_k(cur, prev, W, H, cx, cy, T, 0, 0, b, k) 84 let vm_floor: i64 = (T * T * qp) / 8 85 var r: i64 = 1 86 while r <= R { 87 var prune: i64 = 0 88 if r > 2 { if b[0] < vm_floor { prune = 1 } } 89 if prune == 1 { r = R + 1 } else { 90 var dx: i64 = 0 - r 91 while dx <= r { 92 pv_try_k(cur, prev, W, H, cx, cy, T, dx, 0 - r, b, k) 93 pv_try_k(cur, prev, W, H, cx, cy, T, dx, r, b, k) 94 dx = dx + 1 95 } 96 var dy: i64 = 0 - r + 1 97 while dy <= r - 1 { 98 pv_try_k(cur, prev, W, H, cx, cy, T, 0 - r, dy, b, k) 99 pv_try_k(cur, prev, W, H, cx, cy, T, r, dy, b, k) 100 dy = dy + 1 101 } 102 r = r + 1 103 } 104 } 105 mv[0] = b[1] 106 mv[1] = b[2] 107 return b[0] 108} 109 110// fill f0 (at base) and f1 (at base + W*H) with codec-real content: gradient + edge + texture noise, 111// f1 = f0 translated right by 1+(seed%3) px with per-pixel jitter -- real MVs, full 0..255 byte range 112// (bytes >= 128 are REQUIRED so the signed-extadd mutation is distinguishable). 113func pv_fill(base: i64, seed: i64, W: i64, H: i64) -> i64 { 114 let N: i64 = W * H 115 let f0: *u8 = base as *u8 116 let f1a: i64 = base + N 117 let f1: *u8 = f1a as *u8 118 var i: i64 = 0 119 while i < N { 120 let x: i64 = i % W 121 let y: i64 = i / W 122 var v: i64 = (x + y) * 2 + ((x * y) % 17) + ((seed * (i + 1)) / 16 % 13) 123 if x > (W / 2) { v = v + 40 } 124 v = v % 256 125 f0[i] = v as u8 126 i = i + 1 127 } 128 let sh: i64 = 1 + (seed % 3) 129 var yy: i64 = 0 130 while yy < H { 131 var xx: i64 = 0 132 while xx < W { 133 var sx: i64 = xx - sh 134 if sx < 0 { sx = 0 } 135 let pv: i64 = f0[yy * W + sx] as i64 136 var v2: i64 = pv + ((xx * yy + seed) % 3) 137 if v2 > 255 { v2 = 255 } 138 f1[yy * W + xx] = v2 as u8 139 xx = xx + 1 140 } 141 yy = yy + 1 142 } 143 return N 144} 145 146// ---- SCALAR sweep (the reference path: pvr_*, scalar in every lane) ---- 147func pv_sweep_scalar(base: i64, W: i64, H: i64, qp: i64) -> i64 { 148 let N: i64 = W * H 149 let f0: *u8 = base as *u8 150 let f1a: i64 = base + N 151 let f1: *u8 = f1a as *u8 152 let mva: i64 = base + 2 * N 153 let mv: *i64 = mva as *i64 154 var chk: i64 = K_MAGIC_5381 155 let BW: i64 = W / 16 156 let BH: i64 = H / 16 157 var by: i64 = 0 158 while by < BH { 159 var bx: i64 = 0 160 while bx < BW { 161 let s: i64 = pv_search_k(f1, f0, W, H, bx, by, 16, 16, mv, qp, 0) 162 let m0: i64 = (mv[0] + 16) & 63 163 let m1: i64 = (mv[1] + 16) & 63 164 chk = chk * K_MAGIC_1000003 + s * K_MAGIC_8192 + m0 * 64 + m1 165 bx = bx + 1 166 } 167 by = by + 1 168 } 169 // unaligned raw-SAD pass (arbitrary offsets) + capped pass (abort points) 170 let wlim: i64 = W - 16 171 let hlim: i64 = H - 16 172 var k: i64 = 0 173 while k < 100 { 174 var cx: i64 = (k * 37) % wlim 175 var cy: i64 = (k * 53) % hlim 176 var px: i64 = cx + (k % 7) - 3 177 var py: i64 = cy + (k % 5) - 2 178 if px < 0 { px = 0 } 179 if px > wlim { px = wlim } 180 if py < 0 { py = 0 } 181 if py > hlim { py = hlim } 182 let s2: i64 = pvr_sad(f1, f0, W, cx, cy, px, py, 16) 183 let cap: i64 = 200 + k * 13 184 let s3: i64 = pvr_sad_capped(f1, f0, W, cx, cy, px, py, 16, cap) 185 chk = chk * K_MAGIC_1000003 + s2 * K_MAGIC_4096 + s3 186 k = k + 1 187 } 188 return chk 189} 190 191// ---- SIMD sweep (identical body, SHIPPING vm_* kernels: SIMD in wasm, scalar native) ---- 192func pv_sweep_simd(base: i64, W: i64, H: i64, qp: i64) -> i64 { 193 let N: i64 = W * H 194 let f0: *u8 = base as *u8 195 let f1a: i64 = base + N 196 let f1: *u8 = f1a as *u8 197 let mva: i64 = base + 2 * N 198 let mv: *i64 = mva as *i64 199 var chk: i64 = K_MAGIC_5381 200 let BW: i64 = W / 16 201 let BH: i64 = H / 16 202 var by: i64 = 0 203 while by < BH { 204 var bx: i64 = 0 205 while bx < BW { 206 let s: i64 = pv_search_k(f1, f0, W, H, bx, by, 16, 16, mv, qp, 1) 207 let m0: i64 = (mv[0] + 16) & 63 208 let m1: i64 = (mv[1] + 16) & 63 209 chk = chk * K_MAGIC_1000003 + s * K_MAGIC_8192 + m0 * 64 + m1 210 bx = bx + 1 211 } 212 by = by + 1 213 } 214 let wlim: i64 = W - 16 215 let hlim: i64 = H - 16 216 var k: i64 = 0 217 while k < 100 { 218 var cx: i64 = (k * 37) % wlim 219 var cy: i64 = (k * 53) % hlim 220 var px: i64 = cx + (k % 7) - 3 221 var py: i64 = cy + (k % 5) - 2 222 if px < 0 { px = 0 } 223 if px > wlim { px = wlim } 224 if py < 0 { py = 0 } 225 if py > hlim { py = hlim } 226 let s2: i64 = vm_sad(f1, f0, W, cx, cy, px, py, 16) 227 let cap: i64 = 200 + k * 13 228 let s3: i64 = vm_sad_capped(f1, f0, W, cx, cy, px, py, 16, cap) 229 chk = chk * K_MAGIC_1000003 + s2 * K_MAGIC_4096 + s3 230 k = k + 1 231 } 232 return chk 233} 234 235// ---- timed entries (the V8 A/B: whole-frame 16x16 search sweep only, the hot kernel shape) ---- 236func pv_time_scalar(base: i64, W: i64, H: i64, qp: i64) -> i64 { 237 let N: i64 = W * H 238 let f0: *u8 = base as *u8 239 let f1a: i64 = base + N 240 let f1: *u8 = f1a as *u8 241 let mva: i64 = base + 2 * N 242 let mv: *i64 = mva as *i64 243 var chk: i64 = K_MAGIC_5381 244 let BW: i64 = W / 16 245 let BH: i64 = H / 16 246 var by: i64 = 0 247 while by < BH { 248 var bx: i64 = 0 249 while bx < BW { 250 let s: i64 = pv_search_k(f1, f0, W, H, bx, by, 16, 16, mv, qp, 0) 251 chk = chk * K_MAGIC_1000003 + s 252 bx = bx + 1 253 } 254 by = by + 1 255 } 256 return chk 257} 258func pv_time_simd(base: i64, W: i64, H: i64, qp: i64) -> i64 { 259 let N: i64 = W * H 260 let f0: *u8 = base as *u8 261 let f1a: i64 = base + N 262 let f1: *u8 = f1a as *u8 263 let mva: i64 = base + 2 * N 264 let mv: *i64 = mva as *i64 265 var chk: i64 = K_MAGIC_5381 266 let BW: i64 = W / 16 267 let BH: i64 = H / 16 268 var by: i64 = 0 269 while by < BH { 270 var bx: i64 = 0 271 while bx < BW { 272 let s: i64 = pv_search_k(f1, f0, W, H, bx, by, 16, 16, mv, qp, 1) 273 chk = chk * K_MAGIC_1000003 + s 274 bx = bx + 1 275 } 276 by = by + 1 277 } 278 return chk 279} 280 281// ---- micro entries ---- 282func pv_sad16(a: i64, b: i64) -> i64 { 283 let pa: *u8 = a as *u8 284 let pb: *u8 = b as *u8 285 return v128_sad16(pa, pb) 286} 287func pv_sad16_ref(a: i64, b: i64) -> i64 { 288 let pa: *u8 = a as *u8 289 let pb: *u8 = b as *u8 290 var s: i64 = 0 291 var i: i64 = 0 292 while i < 16 { 293 let d: i64 = (pa[i] as i64) - (pb[i] as i64) 294 if d < 0 { s = s - d } else { s = s + d } 295 i = i + 1 296 } 297 return s 298}