code wiki / _hdl_build / nx_vcodec_t8rd_gate.nx
nx_vcodec_t8rd_gate.nx source
↩ module page · 158 lines · 8834 B
1import "nx_gate_base.nx"
2// nx_vcodec_t8rd_gate.nx -- RD TRANSFORM-SELECT proof (task #46 rung 2b, encoder-only). The mad-of-SOURCE
3// policy classifies source pixels, but inter MBs code the MC RESIDUAL whose character differs; RD-AUTO
4// (emode bit2) costs BOTH transforms on the real residual per MB (exact CAVLC bit counters + recon SSE,
5// lambda = q^2/32 derived from the deadzone step) and patches the explicit t8 bit. Decoder is OBLIVIOUS --
6// the same vv_dec_t8/vv_dec_rct8 must decode auto streams bit-exact. Proves, on key+5P chains with REAL
7// panning motion (flat / textured / mixed content):
8// (1) auto streams decode bit-exact with the unchanged decoder (every frame, every class)
9// (2) auto never loses bytes materially (<= policy*102% + 8B) at PSNR within 1 dB -- and should WIN where
10// the source policy misclassifies residuals (textured pan)
11// (3) auto encode time < 2x policy (trial costing is transform-stage work; ME still dominates)
12// (4) the rct8 (range) composition also decodes bit-exact under auto + bytes reported
13// license_tier: ORIGINAL
14import "nx_syscalls.nx"
15import "nx_video_codec_wasm.nx"
16
17func grow(name: *u8, ok: i64) -> i64 { if ok==1 { gw(" PASS " as *u8) } else { gw(" FAIL " as *u8) } gw(name); gw("
18" as *u8); return ok }
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 eqb(a: *u8, b: *u8, n: i64) -> i64 { var i: i64=0; while i<n { if (a[i]&0xff)!=(b[i]&0xff) { return 0 } i=i+1 } return 1 }
24func cpb(d: *u8, s: *u8, n: i64) -> i64 { var i: i64=0; while i<n { d[i]=s[i]; i=i+1 } return 0 }
25func sseL(a: *u8, b: *u8, n: i64) -> i64 { var s: i64=0; var i: i64=0; while i<n { let d: i64=(a[i]&0xff)-(b[i]&0xff); s=s+d*d; i=i+1 } return s }
26func psnrdb(sse: i64, npix: i64) -> i64 {
27 if sse<=0 { return 99 }
28 let mse: i64 = (sse*100)/npix
29 var num: i64 = 6502500*100; var m: i64 = mse; if m<1 {m=1}
30 var ratio: i64 = num/m
31 var db: i64 = 0; var t: i64 = ratio
32 while t >= 100 { db=db+10; t=t/10 }
33 var fr: i64 = 0
34 if t>=13 { fr=1 } if t>=16 { fr=2 } if t>=20 { fr=3 } if t>=25 { fr=4 }
35 if t>=32 { fr=5 } if t>=40 { fr=6 } if t>=50 { fr=7 } if t>=63 { fr=8 } if t>=79 { fr=9 }
36 return db + fr }
37
38// content with REAL translation: the whole pattern pans right 3px/frame (kind 0 = flat gradient,
39// 1 = high-frequency texture, 2 = gradient + a textured square whose CONTENT moves with it).
40func fill_pan(yuv: *u8, W: i64, H: i64, sh: i64, kind: i64) -> i64 {
41 let N: i64=W*H; let C2: i64=(W/2)*(H/2)
42 let bx: i64 = 48 + sh*3
43 let by2: i64 = 32 + sh
44 var r: i64=0
45 while r<H { var c: i64=0
46 while c<W {
47 let cs: i64 = c + 512 - sh*3 // pan coordinate, kept positive for clean modulo
48 var v: i64 = 0
49 if kind == 0 { v = 90 + (cs + r) / 6 }
50 if kind == 1 { v = 128 + ((cs*7 + r*5) % 37) * 4 - 74 }
51 if kind == 2 {
52 v = 100 + (cs + r) / 8
53 if c>=bx { if c<bx+64 { if r>=by2 { if r<by2+48 {
54 v = 128 + (((c-bx)*7 + (r-by2)*5) % 37) * 4 - 74 } } } }
55 }
56 if v<0{v=0} if v>255{v=255}
57 yuv[r*W+c]=v as u8; c=c+1 } r=r+1 }
58 var i: i64=0
59 while i<C2 { let cr: i64=i/(W/2); let cc: i64=i%(W/2)
60 var u: i64 = 120 + (cc + 256 - sh)/32; if u>255{u=255}
61 yuv[N+i]=u as u8; yuv[N+C2+i]=(130 - cr/12) as u8; i=i+1 }
62 return 0 }
63
64func seedctx(rctx: *i64, emode: i64, est: *i64, probs: *i64, rcbuf: *u8, t8c: *i64) -> i64 {
65 rctx[0]=emode; rctx[1]=est as i64; rctx[2]=probs as i64; rctx[3]=rcbuf as i64; rctx[4]=t8c as i64; rctx[5]=0; return 0 }
66
67// key + 5 P chain of panning content `kind`; encodes POLICY (emode base) and AUTO (base+4) side by side,
68// decodes the AUTO stream with the ignorant decoder each frame. out[0]=policy bytes, out[1]=auto bytes,
69// out[2]=policy PSNR dB (aggregate), out[3]=auto PSNR dB, out[4]=frames bit-exact, out[5]=policy enc us,
70// out[6]=auto enc us. base: 0=CAVLC (vv_enc_t8), 1=range (vv_enc_rct8).
71func run_rd(W: i64, H: i64, qp: i64, base: i64, kind: i64, out: *i64) -> i64 {
72 let N: i64=W*H; let C2: i64=(W/2)*(H/2); let sz: i64=N+2*C2
73 let cur: *u8=sys_mmap(sz)
74 let prevP: *u8=sys_mmap(sz); let reconP: *u8=sys_mmap(sz)
75 let prevA: *u8=sys_mmap(sz); let reconA: *u8=sys_mmap(sz)
76 let prevD: *u8=sys_mmap(sz); let reconD: *u8=sys_mmap(sz)
77 let blk: *i64=sys_mmap(512) as *i64; let mv: *i64=sys_mmap(128) as *i64
78 let outP: *u8=sys_mmap(262144); let outA: *u8=sys_mmap(262144)
79 let est: *i64=sys_mmap(64) as *i64; let probs: *i64=sys_mmap(32*8) as *i64; let rcbuf: *u8=sys_mmap(262144)
80 let rctx: *i64=sys_mmap(8*8) as *i64
81 let t8c: *i64=sys_mmap(5120) as *i64
82 vc_t8_init(t8c)
83 out[0]=0; out[1]=0; out[4]=0; out[5]=0; out[6]=0
84 var sseP: i64=0; var sseA: i64=0
85 var f: i64=0
86 while f < 6 {
87 fill_pan(cur, W, H, f, kind)
88 var key: i64=0; if f==0 { key=1 }
89 // POLICY encode
90 seedctx(rctx, base, est, probs, rcbuf, t8c)
91 let t0: i64 = sys_now_us()
92 var pb: i64=0
93 if base==1 { pb = vv_enc_rct8(cur, prevP, reconP, W, H, qp, key, 6016, outP, 262144, blk, mv, rctx) }
94 else { pb = vv_enc_t8(cur, prevP, reconP, W, H, qp, key, 6016, outP, 262144, blk, mv, rctx) }
95 let t1: i64 = sys_now_us()
96 // AUTO encode (emode bit2)
97 seedctx(rctx, base+4, est, probs, rcbuf, t8c)
98 var ab: i64=0
99 if base==1 { ab = vv_enc_rct8(cur, prevA, reconA, W, H, qp, key, 6016, outA, 262144, blk, mv, rctx) }
100 else { ab = vv_enc_t8(cur, prevA, reconA, W, H, qp, key, 6016, outA, 262144, blk, mv, rctx) }
101 let t2: i64 = sys_now_us()
102 // the IGNORANT decoder must read the auto stream bit-exact
103 seedctx(rctx, base, est, probs, rcbuf, t8c)
104 if base==1 { vv_dec_rct8(prevD, reconD, W, H, qp, outA, ab, blk, mv, rctx) }
105 else { vv_dec_t8(prevD, reconD, W, H, qp, outA, ab, blk, mv, rctx) }
106 if eqb(reconD, reconA, sz)==1 { out[4]=out[4]+1 }
107 out[0]=out[0]+pb; out[1]=out[1]+ab
108 out[5]=out[5]+(t1-t0); out[6]=out[6]+(t2-t1)
109 sseP=sseP+sseL(reconP, cur, N); sseA=sseA+sseL(reconA, cur, N)
110 cpb(prevP, reconP, sz); cpb(prevA, reconA, sz); cpb(prevD, reconD, sz)
111 f=f+1
112 }
113 let dP: i64 = psnrdb(sseP, N*6)
114 let dA: i64 = psnrdb(sseA, N*6)
115 out[2]=dP; out[3]=dA
116 return 0 }
117
118func ck(p: *i64, t: *i64, ok: i64) -> i64 { t[0]=t[0]+1; if ok==1 { p[0]=p[0]+1 } return 0 }
119
120func report(name: *u8, o: *i64) -> i64 {
121 gw(" " as *u8); gw(name)
122 gw(": policy=" as *u8); gn(o[0]); gw("B@" as *u8); gn(o[2]); gw("dB auto=" as *u8); gn(o[1])
123 gw("B@" as *u8); gn(o[3]); gw("dB (" as *u8); gn(o[1]*100/o[0]); gw("%) exact=" as *u8); gn(o[4]); gw("/6" as *u8)
124 gw(" enc " as *u8); gn(o[5]/1000); gw("ms->" as *u8); gn(o[6]/1000); gw("ms\n" as *u8)
125 return 0 }
126
127func main() -> i64 {
128 gw("=== nx_vcodec_t8rd_gate: RD transform-select (auto, emode bit2) vs mad policy, ignorant decoder ===\n" as *u8)
129 let p: *i64=sys_mmap(16) as *i64; let t: *i64=sys_mmap(16) as *i64
130 let o: *i64=sys_mmap(8*8) as *i64
131 // [A] FLAT pan, CAVLC: policy already picks 8x8 everywhere -- auto must agree or beat, never regress
132 run_rd(256, 192, 20, 0, 0, o)
133 report("FLAT pan CAVLC" as *u8, o)
134 ck(p, t, (o[4]==6) as i64)
135 ck(p, t, (o[1] <= (o[0]*102)/100 + 8) as i64)
136 ck(p, t, (o[3] >= o[2]-1) as i64)
137 // [B] TEXTURED pan, CAVLC: policy forces 4x4 (source mad high) -- auto reads the RESIDUAL instead
138 run_rd(256, 192, 20, 0, 1, o)
139 report("TEX pan CAVLC" as *u8, o)
140 ck(p, t, (o[4]==6) as i64)
141 ck(p, t, (o[1] <= (o[0]*102)/100 + 8) as i64)
142 ck(p, t, (o[3] >= o[2]-1) as i64)
143 let texSpdOk: i64 = (o[6] < o[5]*2) as i64
144 // [C] MIXED pan, CAVLC
145 run_rd(256, 192, 20, 0, 2, o)
146 report("MIX pan CAVLC" as *u8, o)
147 ck(p, t, (o[4]==6) as i64)
148 ck(p, t, (o[1] <= (o[0]*102)/100 + 8) as i64)
149 ck(p, t, (o[3] >= o[2]-1) as i64)
150 ck(p, t, texSpdOk) // trial costing must stay transform-stage cheap (< 2x whole-encode) -- worst case is textured
151 // [D] MIXED pan through the RANGE composition (rct8): auto + ignorant decoder + bytes
152 run_rd(256, 192, 20, 1, 2, o)
153 report("MIX pan RANGE" as *u8, o)
154 ck(p, t, (o[4]==6) as i64)
155 ck(p, t, (o[1] <= (o[0]*102)/100 + 8) as i64)
156 gw("T8RD: " as *u8); gn(p[0]); gw("/" as *u8); gn(t[0])
157 if p[0]==t[0] { gw(" GREEN -- RD-auto never regresses, decoder oblivious, residual-true select live\n" as *u8); return 0 }
158 gw(" RED\n" as *u8); return 1 }