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}