nx_yuvgen.nx source
↩ module page · 134 lines · 5909 B
1// nx_yuvgen.nx -- synthesise a YUV420p test clip so the videocodec gates can RUN on the NAS.
2//
3// WHY THIS EXISTS. 17 videocodec gates all open
4// /mnt/c/Users/elder/nishi-core/nxc2/knowledge/staging/media/bframe_test_decoded.yuv
5// a HARDCODED LAPTOP PATH. Execution moved laptop->NAS and the asset did not follow, so all 17 return
6// "cannot read yuv -> RED" with pass=0/0 -- ZERO teeth run. That is a gate that never executed, not a
7// gate that failed, and it is currently what blocks EVERY deploy in the ecosystem through the
8// nx_deploy_ready evidence-honesty check. The file is absent from the LAPTOP too, so the evidence has
9// been unreproducible for some time and the RED is honest.
10//
11// WHY SYNTHETIC IS THE RIGHT ANSWER, NOT A COMPROMISE. These benches measure codec BEHAVIOUR -- B-frame
12// vs P-chain at matched PSNR, decode parity, motion-vector cost. That needs real MOTION, not a real
13// movie. A generated clip with KNOWN, CONTROLLED motion is a better instrument than an arbitrary
14// video: the ground truth is exact, it is hermetic, it costs no 10MB asset transfer, and it cannot
15// silently change under you. Same reasoning that made nx_q4k_dot_simd runnable with synthesised Q4_K
16// blocks instead of a 1.1GB model.
17//
18// CONTENT (deliberately non-degenerate -- a flat or static clip would make the comparison meaningless):
19// - a fixed diagonal gradient background, so intra prediction has real structure to model;
20// - a bright block TRANSLATING a few pixels per frame, so motion estimation has a true displacement
21// to find and B-frames have something to interpolate that P-chains must code;
22// - a slow luma pedestal drift, so rate control sees frame-to-frame change.
23// U/V carry a mild spatial ramp plus a per-frame tint so chroma is exercised rather than constant.
24//
25// argv: <outpath> [W] [H] [frames] defaults 576x1024x16 (the gates need >=12).
26// Writes plain YUV420p: Y plane W*H, then U and V each (W/2)*(H/2), per frame, no header.
27// license_tier: ORIGINAL No hw writes (Rule 26).
28import "nx_syscalls.nx"
29import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc)
30
31const YG_DEF_W: i64 = 576
32const YG_DEF_H: i64 = 1024
33const YG_DEF_F: i64 = 16
34const YG_MODE: i64 = 420
35
36func yg_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
37func yg_p(s: *u8) -> i64 { sys_write(1, s, yg_len(s)); return 0 }
38// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer
39// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the
40// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls).
41// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign.
42func yg_n(v: i64) -> i64 { nxi_out(v); return 0 }
43func yg_atoi(s: *u8) -> i64 {
44 var v: i64 = 0
45 var i: i64 = 0
46 while s[i] != (0 as u8) {
47 let c: i64 = s[i] as i64
48 if c >= 48 { if c <= 57 { v = v * 10 + (c - 48) } }
49 i = i + 1
50 }
51 return v
52}
53func yg_clamp(v: i64) -> i64 {
54 if v < 16 { return 16 }
55 if v > 235 { return 235 }
56 return v
57}
58
59func main(argc: i64, argv: *i64) -> i64 {
60 if argc < 2 {
61 yg_p("usage: nx_yuvgen <outpath> [W] [H] [frames] (YUV420p, default 576x1024x16)\n" as *u8)
62 return 2
63 }
64 let path: *u8 = argv[1] as *u8
65 var W: i64 = YG_DEF_W
66 var H: i64 = YG_DEF_H
67 var F: i64 = YG_DEF_F
68 if argc >= 3 { W = yg_atoi(argv[2] as *u8) }
69 if argc >= 4 { H = yg_atoi(argv[3] as *u8) }
70 if argc >= 5 { F = yg_atoi(argv[4] as *u8) }
71 if W <= 0 { return 3 }
72 if H <= 0 { return 3 }
73 if F <= 0 { return 3 }
74
75 let cw: i64 = W / 2
76 let ch: i64 = H / 2
77 let ysz: i64 = W * H
78 let csz: i64 = cw * ch
79 let yb: *u8 = sys_mmap(ysz + 64)
80 let ub: *u8 = sys_mmap(csz + 64)
81 let vb: *u8 = sys_mmap(csz + 64)
82
83 let fd: i64 = sys_openat_wr(path, 420)
84 if fd < 0 { yg_p("REFUSED reason=outfile-unwritable path=" as *u8); yg_p(path); yg_p("\n" as *u8); return 4 }
85
86 var total: i64 = 0
87 var f: i64 = 0
88 while f < F {
89 // moving block: translates diagonally, wrapping, so every frame has a true displacement
90 let bx: i64 = (f * 11) % (W - 64)
91 let by: i64 = (f * 7) % (H - 64)
92 let ped: i64 = (f * 3) % 24
93 var y: i64 = 0
94 while y < H {
95 var x: i64 = 0
96 let row: i64 = y * W
97 while x < W {
98 var lum: i64 = 40 + ((x + y) % 160) + ped
99 if x >= bx { if x < bx + 64 { if y >= by { if y < by + 64 { lum = 210 } } } }
100 yb[row + x] = yg_clamp(lum) as u8
101 x = x + 1
102 }
103 y = y + 1
104 }
105 var cy: i64 = 0
106 while cy < ch {
107 var cx: i64 = 0
108 let crow: i64 = cy * cw
109 while cx < cw {
110 ub[crow + cx] = yg_clamp(110 + (cx % 40) + (f % 12)) as u8
111 vb[crow + cx] = yg_clamp(130 + (cy % 40) - (f % 12)) as u8
112 cx = cx + 1
113 }
114 cy = cy + 1
115 }
116 var w1: i64 = 0
117 while w1 < ysz { let r: i64 = sys_write(fd, ((yb as i64) + w1) as *u8, ysz - w1); if r <= 0 { w1 = ysz } else { w1 = w1 + r } }
118 var w2: i64 = 0
119 while w2 < csz { let r: i64 = sys_write(fd, ((ub as i64) + w2) as *u8, csz - w2); if r <= 0 { w2 = csz } else { w2 = w2 + r } }
120 var w3: i64 = 0
121 while w3 < csz { let r: i64 = sys_write(fd, ((vb as i64) + w3) as *u8, csz - w3); if r <= 0 { w3 = csz } else { w3 = w3 + r } }
122 total = total + ysz + csz + csz
123 f = f + 1
124 }
125 sys_close(fd)
126 yg_p("NX-YUVGEN OK path=" as *u8); yg_p(path)
127 yg_p(" mode=" as *u8); yg_n(YG_MODE)
128 yg_p(" w=" as *u8); yg_n(W)
129 yg_p(" h=" as *u8); yg_n(H)
130 yg_p(" frames=" as *u8); yg_n(F)
131 yg_p(" bytes=" as *u8); yg_n(total)
132 yg_p("\n" as *u8)
133 return 0
134}