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}