code wiki / _hdl_build / nx_vcodec_nf_train.nx

nx_vcodec_nf_train.nx source

↩ module page · 396 lines · 19698 B

1// nx_vcodec_nf_train.nx -- TRAINER for the learned decoder-side restoration filter (NEURAL track rung 1). 2// Sovereign, integer-only, no floats anywhere. For every (codec-mode in {legacy tf=1, t8-rich}, qp in 3// {12,20,32}) it runs OUR real codec (key + P chain, in-loop deblock -- exactly the live display path) 4// over the REAL frames 0..7 of bframe_test_decoded.yuv (576x1024 luma; frames 8..11 stay HELD OUT for 5// the gate), accumulates per (qp-band, gradient-class) integer normal equations over (3x3 deblocked-recon 6// context -> source pixel) pairs, then solves each class's 3x3+bias Q12 filter: 7// step 1 CLOSED FORM: the center-vs-3x3-mean Wiener blend a* = S_cm_s / S_cm2 (scale-free integer) 8// step 2 REFINE: damped diagonally-preconditioned integer gradient descent on the full 10-tap 9// least squares (J tracked from the moments; a step that raises J is reverted + step halved), 10// starting AT the blend -> the result can only match or beat it on the training distribution. 11// Classes with too few pairs keep the IDENTITY filter (never invent weights from noise). Emits the table 12// as GENERATED SOURCE runtime/nx_vcodec_nf_table.nx (vc_nf_load) -- generators, not hand-authored data. 13// license_tier: ORIGINAL 14import "nx_syscalls.nx" 15import "nx_video_codec_wasm.nx" 16import "nx_vcodec_nf.nx" 17import "nx_quality_metric.nx" 18const MIN_MAGIC_4096: i64 = 4096 19const MIN_MAGIC_3078: i64 = 3078 20const MIN_MAGIC_4194304: i64 = 4194304 21const MIN_MAGIC_5120: i64 = 5120 22const MIN_MAGIC_5000: i64 = 5000 23const MIN_MAGIC_65536: i64 = 65536 24 25const TW: i64 = 576 26const TH: i64 = 1024 27const TFRAMES: i64 = 8 28const MIN_PAIRS: i64 = 20000 // below this a class keeps identity (no training on noise) 29const GD_ITERS: i64 = 240 30 31func tw_(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 32func tn(v: i64) -> i64 { 33 let b: *u8=sys_mmap(28); var m: i64=v; if m<0{sys_write(1,"-" as *u8,1);m=0-m} 34 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} 35 var i: i64=0; while i<k{b[i]=t[k-1-i];i=i+1} sys_write(1,b,k); return 0 } 36 37// ACC slab layout (i64 offsets): ata 27x100 @0, atb 27x10 @2700, btb @2970, cnt @2997, scm2 @3024, 38// scms @3051 -> 3078 slots. bc = band*9 + class. 39const A_ATA: i64 = 0 40const A_ATB: i64 = 2700 41const A_BTB: i64 = 2970 42const A_CNT: i64 = 2997 43const A_SCM2: i64 = 3024 44const A_SCMS: i64 = 3051 45 46// run one (mode, qp) chain over frames 0..TFRAMES-1; pairs from frames [0, fsplit) accumulate into accT 47// (FIT moments), pairs from [fsplit, TFRAMES) into accV (VALIDATION moments -- per-class model selection). 48// mode 0 = legacy tf=1 stream, 1 = t8-rich stream (recon identical to rct8: entropy is lossless). 49func nf_run(yuv: *u8, mode: i64, qp: i64, accT: *i64, accV: *i64, fsplit: i64, enc: *u8, dprev: *u8, dout: *u8, stream: *u8, blk: *i64, mv: *i64, rctx: *i64) -> i64 { 50 let N: i64 = TW * TH 51 let FB: i64 = N + N / 2 52 let band: i64 = vc_nf_band(qp) 53 var z: i64 = 0 54 while z < N { dprev[z] = 0 as u8; z = z + 1 } 55 var f: i64 = 0 56 while f < TFRAMES { 57 var acc: *i64 = accT 58 if f >= fsplit { acc = accV } 59 let cur: *u8 = ((yuv as i64) + f * FB) as *u8 60 var key: i64 = 0 61 if f == 0 { key = 1 } 62 if mode == 1 { 63 rctx[0] = 0; rctx[1] = 0; rctx[2] = 0; rctx[3] = 0; rctx[5] = 0 64 vc_enc_frame_packed_t8(cur, dprev, enc, TW, TH, qp, key, qp*188, stream, blk, mv, 1, rctx) 65 rctx[0] = 0 66 vc_dec_frame_packed_t8(dprev, dout, TW, TH, qp, stream, blk, mv, 1, rctx) 67 } else { 68 vc_enc_frame_packed_tf(cur, dprev, enc, TW, TH, qp, key, qp*188, stream, blk, mv, 1) 69 vc_dec_frame_packed_tf(dprev, dout, TW, TH, qp, stream, blk, mv, 1) 70 } 71 vv_deblock_plane(dout, TW, TH, qp) // the live display path deblocks in-loop 72 // accumulate (3x3 context of deblocked recon -> source) into this band's class moments 73 var y: i64 = 1 74 while y < TH - 1 { 75 let r0: i64 = (y-1)*TW 76 let r1: i64 = y*TW 77 let r2: i64 = (y+1)*TW 78 var x: i64 = 1 79 while x < TW - 1 { 80 let p0: i64 = dout[r0+x-1] as i64 81 let p1: i64 = dout[r0+x] as i64 82 let p2: i64 = dout[r0+x+1] as i64 83 let p3: i64 = dout[r1+x-1] as i64 84 let p4: i64 = dout[r1+x] as i64 85 let p5: i64 = dout[r1+x+1] as i64 86 let p6: i64 = dout[r2+x-1] as i64 87 let p7: i64 = dout[r2+x] as i64 88 let p8: i64 = dout[r2+x+1] as i64 89 let s: i64 = cur[r1+x] as i64 90 let cls: i64 = vc_nf_class(p5 - p3, p7 - p1) 91 let bc: i64 = band * 9 + cls 92 let ab: i64 = A_ATA + bc * 100 93 let vb: i64 = A_ATB + bc * 10 94 // 10-vector v = [p0..p8, 4096] (bias column in Q12 so the solved bias is Q12-native) 95 acc[ab+0] = acc[ab+0] + p0*p0 96 acc[ab+1] = acc[ab+1] + p0*p1 97 acc[ab+2] = acc[ab+2] + p0*p2 98 acc[ab+3] = acc[ab+3] + p0*p3 99 acc[ab+4] = acc[ab+4] + p0*p4 100 acc[ab+5] = acc[ab+5] + p0*p5 101 acc[ab+6] = acc[ab+6] + p0*p6 102 acc[ab+7] = acc[ab+7] + p0*p7 103 acc[ab+8] = acc[ab+8] + p0*p8 104 acc[ab+11] = acc[ab+11] + p1*p1 105 acc[ab+12] = acc[ab+12] + p1*p2 106 acc[ab+13] = acc[ab+13] + p1*p3 107 acc[ab+14] = acc[ab+14] + p1*p4 108 acc[ab+15] = acc[ab+15] + p1*p5 109 acc[ab+16] = acc[ab+16] + p1*p6 110 acc[ab+17] = acc[ab+17] + p1*p7 111 acc[ab+18] = acc[ab+18] + p1*p8 112 acc[ab+22] = acc[ab+22] + p2*p2 113 acc[ab+23] = acc[ab+23] + p2*p3 114 acc[ab+24] = acc[ab+24] + p2*p4 115 acc[ab+25] = acc[ab+25] + p2*p5 116 acc[ab+26] = acc[ab+26] + p2*p6 117 acc[ab+27] = acc[ab+27] + p2*p7 118 acc[ab+28] = acc[ab+28] + p2*p8 119 acc[ab+33] = acc[ab+33] + p3*p3 120 acc[ab+34] = acc[ab+34] + p3*p4 121 acc[ab+35] = acc[ab+35] + p3*p5 122 acc[ab+36] = acc[ab+36] + p3*p6 123 acc[ab+37] = acc[ab+37] + p3*p7 124 acc[ab+38] = acc[ab+38] + p3*p8 125 acc[ab+44] = acc[ab+44] + p4*p4 126 acc[ab+45] = acc[ab+45] + p4*p5 127 acc[ab+46] = acc[ab+46] + p4*p6 128 acc[ab+47] = acc[ab+47] + p4*p7 129 acc[ab+48] = acc[ab+48] + p4*p8 130 acc[ab+55] = acc[ab+55] + p5*p5 131 acc[ab+56] = acc[ab+56] + p5*p6 132 acc[ab+57] = acc[ab+57] + p5*p7 133 acc[ab+58] = acc[ab+58] + p5*p8 134 acc[ab+66] = acc[ab+66] + p6*p6 135 acc[ab+67] = acc[ab+67] + p6*p7 136 acc[ab+68] = acc[ab+68] + p6*p8 137 acc[ab+77] = acc[ab+77] + p7*p7 138 acc[ab+78] = acc[ab+78] + p7*p8 139 acc[ab+88] = acc[ab+88] + p8*p8 140 acc[ab+9] = acc[ab+9] + p0 // bias column: Σp_i (x4096 applied at mirror time) 141 acc[ab+19] = acc[ab+19] + p1 142 acc[ab+29] = acc[ab+29] + p2 143 acc[ab+39] = acc[ab+39] + p3 144 acc[ab+49] = acc[ab+49] + p4 145 acc[ab+59] = acc[ab+59] + p5 146 acc[ab+69] = acc[ab+69] + p6 147 acc[ab+79] = acc[ab+79] + p7 148 acc[ab+89] = acc[ab+89] + p8 149 acc[vb+0] = acc[vb+0] + p0*s 150 acc[vb+1] = acc[vb+1] + p1*s 151 acc[vb+2] = acc[vb+2] + p2*s 152 acc[vb+3] = acc[vb+3] + p3*s 153 acc[vb+4] = acc[vb+4] + p4*s 154 acc[vb+5] = acc[vb+5] + p5*s 155 acc[vb+6] = acc[vb+6] + p6*s 156 acc[vb+7] = acc[vb+7] + p7*s 157 acc[vb+8] = acc[vb+8] + p8*s 158 acc[vb+9] = acc[vb+9] + s // vs bias column (x4096 applied at solve time) 159 acc[A_BTB + bc] = acc[A_BTB + bc] + s*s 160 acc[A_CNT + bc] = acc[A_CNT + bc] + 1 161 let sum9: i64 = p0+p1+p2+p3+p4+p5+p6+p7+p8 162 let dc: i64 = 9*p4 - sum9 163 let ds: i64 = 9*s - sum9 164 acc[A_SCM2 + bc] = acc[A_SCM2 + bc] + dc*dc 165 acc[A_SCMS + bc] = acc[A_SCMS + bc] + dc*ds 166 x = x + 1 167 } 168 y = y + 1 169 } 170 var c2: i64 = 0 171 while c2 < N { dprev[c2] = dout[c2]; c2 = c2 + 1 } 172 f = f + 1 173 } 174 return 0 175} 176// mirror the accumulated upper triangle + fill the bias row/col: ata[i][9]=Σv_i*4096 = atb-ish? No -- 177// bias column b_i9 = Σ p_i * 4096 needs Σp_i, which equals ata row against the constant. We accumulated 178// only pixel products; derive: Σp_i = (Σ p_i*s ... not available). So accumulate Σp_i separately? The 179// bias column is Σ p_i*4096 -> needs Σ p_i. Use atb[9] trick: we stored Σs at vb+9; for Σp_i note the 180// classifier is recon-driven and p4 is the center: we ALSO need Σp_i per class -> stored nowhere. FIX: 181// the [i][9] entries of ATA use slots ab+9,19,29,... (the unfilled upper-triangle column 9) accumulated 182// here as Σ p_i (x1); at solve time they are scaled by 4096. ata[9][9] = cnt*4096*4096. 183func nf_mirror(acc: *i64, bc: i64, M: *i64) -> i64 { 184 let ab: i64 = A_ATA + bc * 100 185 var i: i64 = 0 186 while i < 9 { 187 var j: i64 = i 188 while j < 9 { let v: i64 = acc[ab + i*10 + j]; M[i*10+j] = v; M[j*10+i] = v; j = j + 1 } 189 let bv: i64 = acc[ab + i*10 + 9] * MIN_MAGIC_4096 190 M[i*10+9] = bv 191 M[9*10+i] = bv 192 i = i + 1 193 } 194 M[99] = acc[A_CNT + bc] * MIN_MAGIC_4096 * MIN_MAGIC_4096 195 return 0 196} 197// J(w) x4096^2 relative scale = btb*2^24 - 2*4096^12... computed consistently for COMPARISON only: 198// J = (btb<<24) - 2*(w . atb4)<<12 + w^T M w with atb4 = [atb0..8 <<12, atb9 <<24 /4096...]. To stay 199// integer-simple we compare J' = w^T M w - 2<<12 * (w . atbq) where atbq_i = atb_i (i<9) and atbq_9 = 200// atb_9*4096. (btb constant drops out of comparisons.) 201func nf_j(M: *i64, atb: *i64, w: *i64) -> i64 { 202 // J' = (w^T M w - 2*4096*(w . B)) >> 20 with B = [atb0..8, atb9*4096]; the shared >>20 keeps every 203 // partial product inside i64 at worst-case moment magnitudes (~6e11) x Q12 weights. 204 var q: i64 = 0 205 var i: i64 = 0 206 while i < 10 { 207 var r: i64 = 0 208 var j: i64 = 0 209 while j < 10 { r = r + M[i*10+j]*w[j]; j = j + 1 } 210 q = q + (((r >> 12) * w[i]) >> 8) 211 i = i + 1 212 } 213 var lin: i64 = 0 214 i = 0 215 while i < 9 { lin = lin + ((atb[i]*w[i]) >> 8); i = i + 1 } 216 lin = lin + (((atb[9]*MIN_MAGIC_4096) >> 8) * w[9]) 217 // q ~ w^T M w >> 20; term2 >> 20 = 8192*(w.B) >> 20 = (w.B) >> 7 = lin*2 (lin ~ (w.B)>>8) 218 return q - lin*2 219} 220func main() -> i64 { 221 tw_("=== nx_vcodec_nf_train: learned restoration filter (integer Wiener-blend + GD refine) ===\n" as *u8) 222 let box: *i64 = sys_mmap(16) as *i64 223 let yuv: *u8 = sys_read_file("/mnt/c/Users/elder/nishi-core/nxc2/knowledge/staging/media/bframe_test_decoded.yuv" as *u8, box) 224 if (yuv as i64) == 0 { tw_("cannot read training yuv -> RED\n" as *u8); return 1 } 225 let N: i64 = TW * TH 226 if box[0] < 12 * (N + N/2) { tw_("file too small -> RED\n" as *u8); return 1 } 227 let acc: *i64 = sys_mmap(MIN_MAGIC_3078 * 8) as *i64 228 let accv: *i64 = sys_mmap(MIN_MAGIC_3078 * 8) as *i64 229 var zz: i64 = 0 230 while zz < MIN_MAGIC_3078 { acc[zz] = 0; accv[zz] = 0; zz = zz + 1 } 231 let enc: *u8 = sys_mmap(N + 64) 232 let dprev: *u8 = sys_mmap(N + 64) 233 let dout: *u8 = sys_mmap(N + 64) 234 let stream: *u8 = sys_mmap(MIN_MAGIC_4194304) 235 let blk: *i64 = sys_mmap(512) as *i64 236 let mv: *i64 = sys_mmap(128) as *i64 237 let t8c: *i64 = sys_mmap(MIN_MAGIC_5120) as *i64 238 vc_t8_init(t8c) 239 let rctx: *i64 = sys_mmap(64) as *i64 240 rctx[4] = t8c as i64 241 let qs: *i64 = sys_mmap(32) as *i64 242 qs[0] = 12; qs[1] = 20; qs[2] = 32 243 var qi: i64 = 0 244 while qi < 3 { 245 var md: i64 = 0 246 while md < 2 { 247 tw_(" run mode=" as *u8); tn(md); tw_(" qp=" as *u8); tn(qs[qi]); tw_(" ...\n" as *u8) 248 nf_run(yuv, md, qs[qi], acc, accv, 6, enc, dprev, dout, stream, blk, mv, rctx) 249 md = md + 1 250 } 251 qi = qi + 1 252 } 253 // ---- solve per (band, class): fit on frames 0..5, SELECT per class on validation frames 6..7 ---- 254 let M: *i64 = sys_mmap(100 * 8) as *i64 255 let Mv: *i64 = sys_mmap(100 * 8) as *i64 256 let w: *i64 = sys_mmap(10 * 8) as *i64 257 let wbest: *i64 = sys_mmap(10 * 8) as *i64 258 let wid: *i64 = sys_mmap(10 * 8) as *i64 259 let g: *i64 = sys_mmap(10 * 8) as *i64 260 var wi: i64 = 0 261 while wi < 10 { wid[wi] = 0; wi = wi + 1 } 262 wid[4] = MIN_MAGIC_4096 263 let tbl: *i64 = sys_mmap(VC_NF_TBL * 8) as *i64 264 vc_nf_identity(tbl) 265 var bc: i64 = 0 266 while bc < 27 { 267 let cnt: i64 = acc[A_CNT + bc] 268 if bc < 9 { 269 // band0 (qp<16) = IDENTITY BY POLICY: the codec is near-transparent there; any restoration can 270 // only remove real detail (measured: the first table lost 12 dB at qp12 on held-out content). 271 tw_(" bc=" as *u8); tn(bc); tw_(" band0 -> identity (near-transparent policy)\n" as *u8) 272 } else { if cnt >= MIN_PAIRS { 273 nf_mirror(acc, bc, M) 274 // ridge toward stability on thin data: += cnt>>8 on the pixel diagonal 275 var d: i64 = 0 276 while d < 9 { M[d*10+d] = M[d*10+d] + (cnt >> 8); d = d + 1 } 277 // step 1: closed-form blend a* (scale-free integer), converted to the 10-tap init 278 var a: i64 = MIN_MAGIC_4096 279 if acc[A_SCM2 + bc] > 0 { a = (acc[A_SCMS + bc] << 12) / acc[A_SCM2 + bc] } 280 if a < 0 { a = 0 } 281 if a > MIN_MAGIC_4096 { a = MIN_MAGIC_4096 } 282 let o: i64 = (MIN_MAGIC_4096 - a) / 9 283 var k: i64 = 0 284 while k < 9 { w[k] = o; k = k + 1 } 285 w[4] = MIN_MAGIC_4096 - 8*o 286 w[9] = 0 287 // step 2: damped Jacobi-preconditioned GD on the full LS (revert-on-worse, halve step), 288 // CONSTRAINED to unity DC gain (taps sum EXACTLY 4096, bias fixed 0): the filter learns only 289 // SHAPE, never gain/offset -- a train-set DC quirk can then never become a brightness-dependent 290 // gain error on unseen content (the first unconstrained run failed held-out exactly that way). 291 let atb: *i64 = ((acc as i64) + (A_ATB + bc*10) * 8) as *i64 292 var jbest: i64 = nf_j(M, atb, w) 293 k = 0 294 while k < 10 { wbest[k] = w[k]; k = k + 1 } 295 var damp: i64 = 2 296 var it: i64 = 0 297 while it < GD_ITERS { 298 var i2: i64 = 0 299 while i2 < 10 { 300 var r: i64 = 0 301 var j2: i64 = 0 302 while j2 < 10 { r = r + M[i2*10+j2]*w[j2]; j2 = j2 + 1 } 303 g[i2] = atb[i2] - (r >> 12) 304 i2 = i2 + 1 305 } 306 g[9] = 0 // bias frozen at 0 307 i2 = 0 308 while i2 < 9 { 309 var dd: i64 = M[i2*10+i2] >> 12 310 if dd < 1 { dd = 1 } 311 w[i2] = w[i2] + (g[i2] / dd) / damp 312 i2 = i2 + 1 313 } 314 // unity-gain projection: distribute the sum error over the taps, remainder to the center 315 var sw: i64 = 0 316 i2 = 0 317 while i2 < 9 { sw = sw + w[i2]; i2 = i2 + 1 } 318 let er: i64 = sw - MIN_MAGIC_4096 319 let dsh: i64 = er / 9 320 i2 = 0 321 while i2 < 9 { w[i2] = w[i2] - dsh; i2 = i2 + 1 } 322 w[4] = w[4] - (er - 9*dsh) 323 let jn: i64 = nf_j(M, atb, w) 324 if jn < jbest { 325 jbest = jn 326 i2 = 0 327 while i2 < 10 { wbest[i2] = w[i2]; i2 = i2 + 1 } 328 } else { 329 i2 = 0 330 while i2 < 10 { w[i2] = wbest[i2]; i2 = i2 + 1 } 331 damp = damp * 2 332 if damp > MIN_MAGIC_4096 { it = GD_ITERS } 333 } 334 it = it + 1 335 } 336 // VALIDATION SELECTION: keep the trained filter ONLY if it beats identity on frames it never fit 337 let vcnt: i64 = accv[A_CNT + bc] 338 var keep: i64 = 0 339 if vcnt >= MIN_MAGIC_5000 { 340 nf_mirror(accv, bc, Mv) 341 let atbv: *i64 = ((accv as i64) + (A_ATB + bc*10) * 8) as *i64 342 if nf_j(Mv, atbv, wbest) < nf_j(Mv, atbv, wid) { keep = 1 } 343 } 344 if keep == 1 { 345 k = 0 346 while k < 10 { tbl[bc*10 + k] = wbest[k]; k = k + 1 } 347 tw_(" bc=" as *u8); tn(bc); tw_(" pairs=" as *u8); tn(cnt); tw_(" a=" as *u8); tn(a); tw_(" TRAINED (val-win)\n" as *u8) 348 } else { 349 tw_(" bc=" as *u8); tn(bc); tw_(" pairs=" as *u8); tn(cnt); tw_(" a=" as *u8); tn(a); tw_(" -> identity (no val win)\n" as *u8) 350 } 351 } else { 352 tw_(" bc=" as *u8); tn(bc); tw_(" pairs=" as *u8); tn(cnt); tw_(" -> identity (too thin)\n" as *u8) 353 } } 354 bc = bc + 1 355 } 356 // ---- emit the GENERATED table source ---- 357 let txt: *u8 = sys_mmap(MIN_MAGIC_65536) 358 var p: i64 = 0 359 let hdr: *u8 = "// nx_vcodec_nf_table.nx -- GENERATED by nx_vcodec_nf_train (do not hand-edit; retrain instead).\n// Trained on bframe_test_decoded.yuv frames 0..7 (frames 8..11 held out for nx_vcodec_nf_gate),\n// codec modes {legacy tf=1, t8-rich} x qp {12,20,32}, deblocked recon -> source, integer LS.\n// license_tier: ORIGINAL (generated)\nfunc vc_nf_load(tbl: *i64) -> i64 {\n" as *u8 360 var hi: i64 = 0 361 while hdr[hi] != (0 as u8) { txt[p] = hdr[hi]; p = p + 1; hi = hi + 1 } 362 let nb: *u8 = sys_mmap(28) // hoisted: no mmap in the emit loop 363 var ti: i64 = 0 364 while ti < VC_NF_TBL { 365 txt[p] = 32; txt[p+1] = 32; p = p + 2 366 let t1: *u8 = "tbl[" as *u8 367 var t1i: i64 = 0 368 while t1[t1i] != (0 as u8) { txt[p] = t1[t1i]; p = p + 1; t1i = t1i + 1 } 369 // index 370 var m2: i64 = ti 371 var nk: i64 = 0 372 if m2 == 0 { nb[0] = 48; nk = 1 } 373 while m2 > 0 { nb[nk] = (48 + (m2 % 10)) as u8; m2 = m2 / 10; nk = nk + 1 } 374 var ri: i64 = nk - 1 375 while ri >= 0 { txt[p] = nb[ri]; p = p + 1; ri = ri - 1 } 376 txt[p] = 93; txt[p+1] = 61; p = p + 2 // "]=" 377 var v: i64 = tbl[ti] 378 if v < 0 { txt[p] = 45; p = p + 1; v = 0 - v } // '-' 379 var vk: i64 = 0 380 if v == 0 { nb[0] = 48; vk = 1 } 381 while v > 0 { nb[vk] = (48 + (v % 10)) as u8; v = v / 10; vk = vk + 1 } 382 ri = vk - 1 383 while ri >= 0 { txt[p] = nb[ri]; p = p + 1; ri = ri - 1 } 384 txt[p] = 10; p = p + 1 // newline 385 ti = ti + 1 386 } 387 let ftr: *u8 = " return 0\n}\n" as *u8 388 var fi2: i64 = 0 389 while ftr[fi2] != (0 as u8) { txt[p] = ftr[fi2]; p = p + 1; fi2 = fi2 + 1 } 390 let fd: i64 = sys_openat_wr("/mnt/c/Users/elder/nishi-core/nxc2/runtime/nx_vcodec_nf_table.nx" as *u8) 391 if fd < 0 { tw_("cannot write table -> RED\n" as *u8); return 1 } 392 sys_write(fd, txt, p) 393 sys_close(fd) 394 tw_("EMITTED runtime/nx_vcodec_nf_table.nx (" as *u8); tn(p); tw_("B) -- run nx_vcodec_nf_gate on the held-out frames\n" as *u8) 395 return 0 396}