code wiki / _hdl_build / nx_vsubpel_q_gate.nx

nx_vsubpel_q_gate.nx source

↩ module page · 60 lines · 3569 B

1// nx_vsubpel_q_gate.nx -- proves QUARTER-pel motion (nx_vsubpel qp_refine) beats half-pel when content moves by a 2// quarter pixel: half-pel can only land on 0 / 0.5, so a 0.25px shift leaves a residual it can't remove, while 3// quarter-pel lands exactly on 0.25 -> ~zero residual. The H.264/VP9 motion-precision rung past half-pel. 4import "nx_syscalls.nx" 5import "nx_gate_emit_lib.nx" 6import "nx_vsubpel.nx" 7import "nx_gate_verdict.nx" 8 9func g_abs(v: i64) -> i64 { if v < 0 { return 0 - v } return v } 10// SAD of cur's TxT block at (cx,cy) vs prev sampled at (px+.,py+.) with quarter offsets (qx,qy) 11func sad_at(cur: *u8, prev: *u8, W: i64, cx: i64, cy: i64, px: i64, py: i64, qx: i64, qy: i64, T: i64) -> i64 { 12 var s: i64=0; var ry: i64=0 13 while ry < T { var rx: i64=0 14 while rx < T { s = s + g_abs((cur[(cy+ry)*W+(cx+rx)] as i64) - qp_pixel(prev, W, px+rx, py+ry, qx, qy)); rx=rx+1 } ry=ry+1 } 15 return s 16} 17 18func main() -> i64 { 19 g_puts("nx_vsubpel quarter-pel gate (quarter-pel beats half-pel on a 0.25px shift, MEASURED)\n" as *u8) 20 var pass: i64 = 0; var total: i64 = 0 21 let W: i64 = 24; let H: i64 = 24; let T: i64 = 16 22 let cx: i64 = 4; let cy: i64 = 4 23 24 let A: *u8 = sys_mmap(W*H) as *u8 25 let B: *u8 = sys_mmap(W*H) as *u8 26 let mvq: *i64 = sys_mmap(2*8) as *i64 27 28 var y: i64 = 0 29 while y < H { var x: i64 = 0 30 while x < W { A[y*W+x] = (40 + ((x*11 + y*7) % 120)) as u8; x = x + 1 } y = y + 1 } 31 // B = A shifted +0.25px in x : B[x] = (3*A[x] + A[x+1] + 2)/4 (== qp_pixel at qx=1) 32 y = 0 33 while y < H { var x: i64 = 0 34 while x < W - 1 { B[y*W+x] = (((3*(A[y*W+x] as i64) + (A[y*W+(x+1)] as i64) + 2) / 4)) as u8; x = x + 1 } 35 B[y*W + (W-1)] = A[y*W + (W-1)]; y = y + 1 } 36 37 let int_sad: i64 = sad_at(B, A, W, cx, cy, cx, cy, 0, 0, T) // integer 38 var half_sad: i64 = sad_at(B, A, W, cx, cy, cx, cy, 2, 0, T) // best half (x-axis): 0 or 0.5 39 let half0: i64 = int_sad // half also includes (0,0) 40 if half0 < half_sad { half_sad = half0 } 41 let q_sad: i64 = qp_refine(B, A, W, H, cx, cy, 0, 0, T, mvq) // quarter-pel refine 42 43 g_puts(" [measure] residual SAD: integer=" as *u8); g_pn(int_sad); g_puts(" best-half=" as *u8); g_pn(half_sad); g_puts(" quarter=" as *u8); g_pn(q_sad) 44 g_puts(" best quarter MV=(" as *u8); g_pn(mvq[0]); g_puts("," as *u8); g_pn(mvq[1]); g_puts(")/4px\n" as *u8) 45 46 pass = pass + g_check("quarter-pel residual << half-pel residual (>= 4x smaller)" as *u8, q_sad * 4 < half_sad); total=total+1 47 pass = pass + g_check("quarter-pel found the true (0.25, 0) shift" as *u8, (mvq[0]==1) & (mvq[1]==0)); total=total+1 48 pass = pass + g_check("half-pel genuinely cannot match a 0.25 shift (half ~ integer)" as *u8, half_sad * 2 > int_sad); total=total+1 49 50 g_puts("---- vsubpel quarter gate: passed " as *u8); g_pn(pass); g_puts(" / " as *u8); g_pn(total); g_puts(" ----\n" as *u8) 51 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check 52 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled 53 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify. 54 let ctr__dry: *i64 = gv_ctr() 55 ctr__dry[0] = pass 56 ctr__dry[1] = total 57 let rc__dry: i64 = gv_verdict("VSUBPEL-Q-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8) 58 sys_exit(rc__dry) 59 return rc__dry 60}