code wiki / _hdl_build / nx_vcodec_subpel_ceiling.nx
nx_vcodec_subpel_ceiling.nx source
↩ module page · 177 lines · 8659 B
1// nx_vcodec_subpel_ceiling.nx -- RE-AUDIT the self-judged "6-tap sub-pel = 0" verdict (task #17) against a MEASURED
2// residual ceiling, decoder-transparent. The codec's sub-pel is BILINEAR (qp_pixel: a 4-neighbour weighted blend that
3// BLURS the reference). H.264 uses a SHARP 6-tap FIR (1,-5,20,20,-5,1)/32 that preserves high-frequency detail -> a
4// tighter prediction -> smaller residual -> fewer bits at identical quality. This probe isolates FILTER SHARPNESS:
5// for each interior 16x16 P-block, integer-search the clean prev (+/-16), then refine to HALF-pel with (a) the existing
6// bilinear filter and (b) a proper 6-tap filter, and sum the best SAD each way over several P-frames. If 6-tap's total
7// residual is materially lower, the "0" verdict was a short-bench lie (like RD-skip and wide-ME were) and the real
8// enc+dec+wasm build is justified. Pure measurement on CLEAN originals => no bitstream, no parity risk. license_tier: ORIGINAL
9import "nx_syscalls.nx"
10import "nx_vsubpel.nx"
11import "nx_vmotion.nx"
12const K_MAGIC_2147483647: i64 = 2147483647
13const K_MAGIC_2026: i64 = 2026
14
15const NW: i64 = 576
16const NH: i64 = 1024
17
18func gw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
19func gn(v: i64) -> i64 {
20 let b: *u8=sys_mmap(28); var m: i64=v; if m<0{sys_write(1,"-" as *u8,1);m=0-m}
21 let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48 as u8;k=1} while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1}
22 var i: i64=0; while i<k{b[i]=t[k-1-i];i=i+1} sys_write(1,b,k); return 0 }
23func g2(v: i64) -> i64 { gn(v/100); gw("." as *u8); let f: i64=v%100; if f<10 { gw("0" as *u8) } gn(f); return 0 }
24func clp(v: i64) -> i64 { if v<0 { return 0 } if v>255 { return 255 } return v }
25
26// 6-tap horizontal half-pel PRE-SHIFT value sampled at (x+0.5, y): taps prev[y, x-2 .. x+3]
27func s6h(prev: *u8, W: i64, x: i64, y: i64) -> i64 {
28 let e: i64 = prev[y*W + x-2] as i64
29 let f: i64 = prev[y*W + x-1] as i64
30 let g: i64 = prev[y*W + x] as i64
31 let h: i64 = prev[y*W + x+1] as i64
32 let i: i64 = prev[y*W + x+2] as i64
33 let j: i64 = prev[y*W + x+3] as i64
34 return e - 5*f + 20*g + 20*h - 5*i + j
35}
36// 6-tap vertical half-pel PRE-SHIFT value sampled at (x, y+0.5): taps prev[y-2 .. y+3, x]
37func s6v(prev: *u8, W: i64, x: i64, y: i64) -> i64 {
38 let e: i64 = prev[(y-2)*W + x] as i64
39 let f: i64 = prev[(y-1)*W + x] as i64
40 let g: i64 = prev[y*W + x] as i64
41 let h: i64 = prev[(y+1)*W + x] as i64
42 let i: i64 = prev[(y+2)*W + x] as i64
43 let j: i64 = prev[(y+3)*W + x] as i64
44 return e - 5*f + 20*g + 20*h - 5*i + j
45}
46// 6-tap CENTER (j) PRE-SHIFT^2 value at (x+0.5, y+0.5): 6-tap vertical over 6 horizontal-half values at rows y-2..y+3
47func s6c(prev: *u8, W: i64, x: i64, y: i64) -> i64 {
48 let e: i64 = s6h(prev, W, x, y-2)
49 let f: i64 = s6h(prev, W, x, y-1)
50 let g: i64 = s6h(prev, W, x, y)
51 let h: i64 = s6h(prev, W, x, y+1)
52 let i: i64 = s6h(prev, W, x, y+2)
53 let j: i64 = s6h(prev, W, x, y+3)
54 return e - 5*f + 20*g + 20*h - 5*i + j
55}
56// one 6-tap half-pel sample at reference (x,y) with half offsets hx,hy in {0,1}
57func s6_pixel(prev: *u8, W: i64, x: i64, y: i64, hx: i64, hy: i64) -> i64 {
58 if hx == 0 {
59 if hy == 0 { return prev[y*W + x] as i64 }
60 return clp((s6v(prev, W, x, y) + 16) >> 5)
61 }
62 if hy == 0 { return clp((s6h(prev, W, x, y) + 16) >> 5) }
63 return clp((s6c(prev, W, x, y) + 512) >> 10)
64}
65// best 6-tap HALF-pel SAD for the TxT block at (cx,cy), integer base (px,py): test the 4 (hx,hy)
66func s6_refine(cur: *u8, prev: *u8, W: i64, cx: i64, cy: i64, px: i64, py: i64, T: i64) -> i64 {
67 var best: i64 = K_MAGIC_2147483647
68 var hy: i64 = 0
69 while hy <= 1 {
70 var hx: i64 = 0
71 while hx <= 1 {
72 var sad: i64 = 0
73 var ry: i64 = 0
74 while ry < T {
75 var rx: i64 = 0
76 while rx < T {
77 let d: i64 = (cur[(cy+ry)*W + (cx+rx)] as i64) - s6_pixel(prev, W, px+rx, py+ry, hx, hy)
78 if d < 0 { sad = sad - d } else { sad = sad + d }
79 rx = rx + 1
80 }
81 ry = ry + 1
82 }
83 if sad < best { best = sad }
84 hx = hx + 1
85 }
86 hy = hy + 1
87 }
88 return best
89}
90// best BILINEAR half-pel SAD (the codec's current filter), same 4 (hx,hy), via hp_pixel
91func bl_refine(cur: *u8, prev: *u8, W: i64, cx: i64, cy: i64, px: i64, py: i64, T: i64) -> i64 {
92 var best: i64 = K_MAGIC_2147483647
93 var hy: i64 = 0
94 while hy <= 1 {
95 var hx: i64 = 0
96 while hx <= 1 {
97 var sad: i64 = 0
98 var ry: i64 = 0
99 while ry < T {
100 var rx: i64 = 0
101 while rx < T {
102 let d: i64 = (cur[(cy+ry)*W + (cx+rx)] as i64) - hp_pixel(prev, W, px+rx, py+ry, hx, hy)
103 if d < 0 { sad = sad - d } else { sad = sad + d }
104 rx = rx + 1
105 }
106 ry = ry + 1
107 }
108 if sad < best { best = sad }
109 hx = hx + 1
110 }
111 hy = hy + 1
112 }
113 return best
114}
115
116func main() -> i64 {
117 let vmscr3: *i64 = sys_mmap(32) as *i64 // vm_search scratch (K_MAGIC_2026-07-29: hoisted ONE alloc; the per-call sys_mmap inside vm_search was the wasm 0-stub + native map-leak class)
118 gw("=== nx_vcodec_subpel_ceiling: 6-tap vs bilinear half-pel residual (re-audit the 'sub-pel=0' verdict) ===\n" as *u8)
119 let box: *i64 = sys_mmap(16) as *i64
120 let yuv: *u8 = sys_read_file("/mnt/c/Users/elder/nishi-core/nxc2/knowledge/staging/media/bframe_test_decoded.yuv" as *u8, box)
121 if (yuv as i64) == 0 { gw("cannot read yuv -> RED\n" as *u8); return 1 }
122 let N: i64 = NW*NH
123 let FB: i64 = N + N/2
124 if box[0] < 12 * FB { gw("file too small -> RED\n" as *u8); return 1 }
125 let mv: *i64 = sys_mmap(64) as *i64
126 let T: i64 = 16
127
128 var totInt: i64 = 0; var totBil: i64 = 0; var tot6: i64 = 0; var nblk: i64 = 0
129 // predict P-frames 6,7,8 from their clean previous originals (decoder-transparent ceiling)
130 var fp: i64 = 6
131 while fp <= 8 {
132 let cur: *u8 = ((yuv as i64) + fp*FB) as *u8
133 let prev: *u8 = ((yuv as i64) + (fp-1)*FB) as *u8
134 var cy: i64 = 32
135 while cy <= NH-48 {
136 var cx: i64 = 32
137 while cx <= NW-48 {
138 let isad: i64 = vm_search(cur, prev, NW, NH, cx/16, cy/16, T, 16, mv, vmscr3)
139 let px: i64 = cx + mv[0]; let py: i64 = cy + mv[1]
140 // 6-tap needs a 2px margin beyond the block on every side; skip blocks whose MV lands near the edge
141 var ok: i64 = 1
142 if px < 2 { ok = 0 }
143 if py < 2 { ok = 0 }
144 if px + T + 3 >= NW { ok = 0 }
145 if py + T + 3 >= NH { ok = 0 }
146 if ok == 1 {
147 let sb: i64 = bl_refine(cur, prev, NW, cx, cy, px, py, T)
148 let s6: i64 = s6_refine(cur, prev, NW, cx, cy, px, py, T)
149 totInt = totInt + isad
150 totBil = totBil + sb
151 tot6 = tot6 + s6
152 nblk = nblk + 1
153 }
154 cx = cx + 16
155 }
156 cy = cy + 16
157 }
158 fp = fp + 1
159 }
160 gw("blocks measured=" as *u8); gn(nblk); gw(" (interior 16x16, 3 P-frames)\n" as *u8)
161 gw(" integer-pel total SAD = " as *u8); gn(totInt); gw("\n" as *u8)
162 gw(" bilinear-half total SAD = " as *u8); gn(totBil)
163 let bilGainInt: i64 = (totInt-totBil)*1000/totInt
164 gw(" (-" as *u8); gn(bilGainInt); gw("permille vs integer)\n" as *u8)
165 gw(" 6-tap-half total SAD = " as *u8); gn(tot6)
166 let sixGainInt: i64 = (totInt-tot6)*1000/totInt
167 gw(" (-" as *u8); gn(sixGainInt); gw("permille vs integer)\n" as *u8)
168 // the decisive number: 6-tap residual vs bilinear residual at the SAME half-pel granularity (isolates sharpness)
169 let sixVsBil: i64 = (totBil-tot6)*1000/totBil
170 gw(" === 6-tap vs bilinear residual = " as *u8)
171 if tot6 < totBil { gw("-" as *u8); gn(sixVsBil); gw("permille (6-tap TIGHTER -> BUILD IT) ===\n" as *u8) }
172 else { gw("+" as *u8); gn((tot6-totBil)*1000/totBil); gw("permille (6-tap WORSE -> verdict holds, dead) ===\n" as *u8) }
173 // liar-killer: 6-tap must at least beat integer (a sharper sub-pel can't be worse than no sub-pel on real motion)
174 if tot6 < totInt { gw("LIAR-KILLER PASS: 6-tap beats integer-pel (sub-pel is doing real work)\n" as *u8) }
175 else { gw("LIAR-KILLER WARN: 6-tap did NOT beat integer -- content near-static or a bug\n" as *u8) }
176 return 0
177}