code wiki / _hdl_build / nx_vcodec_predceiling_gate.nx
nx_vcodec_predceiling_gate.nx source
↩ module page · 102 lines · 5388 B
1import "nx_gate_base.nx"
2// nx_vcodec_predceiling_gate.nx -- 803-followup: MEASURE the prediction-lever ceiling BEFORE building sub-MB
3// motion partitions (the expensive vcv-7 wire change). The pt8 result proved transform-size is NOT the P-frame
4// lever (0.2-0.6%); the coded-MB cost must be PREDICTION (residual too big) or ENTROPY. This gate quantifies the
5// PREDICTION headroom: for each genuinely-coded P-MB (non-skip), it compares the best motion-compensated residual
6// SAD under three partitionings -- 16x16 single MV (what we do now) vs 4x 8x8 MVs vs 16x 4x4 MVs. A big SAD drop
7// from finer partitions = motion partitions are the real lever (build vcv-7). A small drop = prediction is near
8// its limit and the lever is ENTROPY (CABAC), not partitions. Reference = prior ORIGINAL frame (clean ceiling).
9// Samples every 4th P-frame for speed. license: ORIGINAL
10import "nx_syscalls.nx"
11import "nx_video_codec_wasm.nx"
12import "nx_vmotion.nx"
13
14const SW: i64 = 352
15const SH: i64 = 288
16const NF: i64 = 48
17const RS: i64 = 8 // integer search radius
18
19func grow(name: *u8, ok: i64) -> i64 { if ok==1 { gw(" PASS " as *u8) } else { gw(" FAIL " as *u8) } gw(name); gw("
20" as *u8); return ok }
21func gn(v: i64) -> i64 {
22 let b: *u8=sys_mmap(28); var m: i64=v; if m<0{sys_write(1,"-" as *u8,1);m=0-m}
23 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}
24 var i: i64=0; while i<k{b[i]=t[k-1-i];i=i+1} sys_write(1,b,k); return 0 }
25
26// SAD of a BxB block at (x0,y0) in cur vs prev shifted by (dx,dy)
27func blk_sad(cur: *u8, prev: *u8, W: i64, x0: i64, y0: i64, B: i64, dx: i64, dy: i64) -> i64 {
28 var s: i64=0; var j: i64=0
29 while j<B { var i: i64=0
30 while i<B {
31 let d: i64=(cur[(y0+j)*W+(x0+i)]&0xff) - (prev[(y0+j+dy)*W+(x0+i+dx)]&0xff)
32 if d<0 { s=s-d } else { s=s+d }
33 i=i+1 } j=j+1 }
34 return s }
35
36// best integer-MV SAD for a BxB block at (x0,y0), searching [-RS,RS]^2 (clamped to frame)
37func best_sad(cur: *u8, prev: *u8, W: i64, H: i64, x0: i64, y0: i64, B: i64) -> i64 {
38 var best: i64=2147483647
39 var dy: i64=0-RS
40 while dy<=RS {
41 if y0+dy>=0 { if y0+B+dy<=H {
42 var dx: i64=0-RS
43 while dx<=RS {
44 if x0+dx>=0 { if x0+B+dx<=W {
45 let s: i64=blk_sad(cur, prev, W, x0, y0, B, dx, dy)
46 if s<best { best=s }
47 } }
48 dx=dx+1 }
49 } }
50 dy=dy+1 }
51 return best }
52
53func frames(yuv: *u8, hi: i64, total: i64, fp: *i64, fdata: i64) -> i64 {
54 var nf: i64=0; var p: i64=hi
55 while nf < NF { if p>=total { break }
56 var q: i64=p; while q<total { if (yuv[q]&0xff)==10 { break } q=q+1 } q=q+1
57 if q+fdata>total { break } fp[nf]=(yuv as i64)+q; nf=nf+1; p=q+fdata }
58 return nf }
59
60func main() -> i64 {
61 gw("=== nx_vcodec_predceiling_gate: motion-partition residual ceiling on /tmp/seq.y4m (qp=8) ===\n" as *u8)
62 let box: *i64 = sys_mmap(16) as *i64
63 let yuv: *u8 = sys_read_file("/tmp/seq.y4m" as *u8, box)
64 if (yuv as i64)==0 { gw("cannot read /tmp/seq.y4m -> RED\n" as *u8); return 1 }
65 let total: i64=box[0]
66 var hi: i64=0; while hi<total { if (yuv[hi]&0xff)==10 { break } hi=hi+1 } hi=hi+1
67 let C2: i64=(SW/2)*(SH/2); let fdata: i64=SW*SH+2*C2
68 let fp: *i64=sys_mmap(128*8) as *i64
69 let nf: i64=frames(yuv, hi, total, fp, fdata)
70 if nf < NF { gw("too few frames -> RED\n" as *u8); return 1 }
71 let mbx: i64=SW/16; let mby: i64=SH/16
72 var sad16: i64=0; var sad8: i64=0; var sad4: i64=0; var coded: i64=0
73 var f: i64=1
74 while f < NF {
75 let cur: *u8=fp[f] as *u8; let prev: *u8=fp[f-1] as *u8 // prior ORIGINAL frame = clean prediction ceiling
76 var by: i64=0
77 while by<mby { var bx: i64=0
78 while bx<mbx {
79 let x0: i64=bx*16; let y0: i64=by*16
80 let zs: i64=vm_sad_zero(cur, prev, SW, bx, by, 16)
81 if zs >= 8*188 { // genuinely-coded MB (non-skip) -- where the bits actually go
82 coded=coded+1
83 sad16=sad16+best_sad(cur, prev, SW, SH, x0, y0, 16)
84 // 4x 8x8
85 var q: i64=0
86 while q<4 { let qx: i64=x0+(q%2)*8; let qy: i64=y0+(q/2)*8; sad8=sad8+best_sad(cur, prev, SW, SH, qx, qy, 8); q=q+1 }
87 // 16x 4x4
88 var r: i64=0
89 while r<16 { let rx: i64=x0+(r%4)*4; let ry: i64=y0+(r/4)*4; sad4=sad4+best_sad(cur, prev, SW, SH, rx, ry, 4); r=r+1 }
90 }
91 bx=bx+1 } by=by+1 }
92 f=f+4 // sample every 4th P-frame for speed
93 }
94 if coded==0 { gw(" no coded MBs (all skip) -- prediction lever N/A on this seq\n" as *u8); return 0 }
95 gw(" coded MBs sampled=" as *u8); gn(coded); gw("\n" as *u8)
96 gw(" residual SAD 16x16(now)=" as *u8); gn(sad16); gw(" 8x8x4=" as *u8); gn(sad8); gw(" 4x4x16=" as *u8); gn(sad4); gw("\n" as *u8)
97 if sad16 > 0 {
98 gw(" 8x8 cuts residual " as *u8); gn((sad16-sad8)*1000/sad16); gw(" per-mille vs 16x16; 4x4 cuts " as *u8); gn((sad16-sad4)*1000/sad16); gw(" per-mille\n" as *u8)
99 }
100 gw(" (big cut = motion partitions ARE the lever -> build vcv-7; small cut = prediction near limit -> entropy is the lever)\n" as *u8)
101 gw("PREDCEILING: DONE\n" as *u8)
102 return 0 }