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 }