nx_enginelab_lib.nx source
↩ module page · 1253 lines · 56204 B
1// nx_enginelab_lib.nx -- THE SOVEREIGN ENGINE INSTRUMENT (library half).
2//
3// WHAT THIS IS. The open-source engine-testing field ships five SEPARATE programs with five
4// SEPARATE capture formats: a frame debugger (draw calls + pipeline state), a CPU zone profiler
5// (nanosecond scopes, threads, allocations, lock waits), a telemetry overlay (frame pacing, lows,
6// stutter), a GPU scheduling visualiser (queue slices, vsync, CPU-bound vs GPU-bound), and an
7// automated visual-regression harness (golden images). Each is excellent alone.
8//
9// THE DEFECT THE FIELD LIVES WITH, AND OUR EXCEED. Because those five captures are five files with
10// five clocks, A FRAME-TIME SPIKE CAN NEVER BE JOINED TO THE DRAW CALL THAT CAUSED IT. You see the
11// spike in one tool and go hunting in another, by hand, by eye. Here every subsystem writes into ONE
12// record layout on ONE clock, so the join is arithmetic rather than detective work: el_frame_attr
13// answers 'this frame cost 41ms; of that, N draws, X us of GPU queue, Y us of CPU root zones, Z us
14// of lock wait, B bytes allocated'. That is the whole reason for a single spine, and it is the
15// mechanism no separate-format tool can offer.
16//
17// LAWS MADE STRUCTURAL HERE, not left to the caller to remember:
18// - NO SILENT CAPS. A full store REFUSES and counts the refusal (EL_H_DROP). Every report carries
19// dropped= so a truncated measurement can never read as a complete one.
20// - ABSTAIN, NEVER ACQUIT. Every verdict has a third state. A bound-verdict with no GPU evidence
21// returns UNKNOWN, not CPU-BOUND. A capture diff over ZERO draws returns UNMEASURABLE, not
22// 'identical' -- the empty-set-passes defect wearing a profiler costume.
23// - THE REASON TRAVELS WITH THE COUNT. Errors are split by cause (non-monotonic clock, stack
24// overflow, end-without-begin), never merged into one number nobody can act on.
25// - COMPOSE, NEVER RE-IMPLEMENT. Rolling FPS is nx_fpsmeter (fm_init/fm_tick/fm_fps), the frame
26// budget is nx_frame_budget (fb_budget_us/fb_fits/fb_headroom_us) and image parity is
27// nx_visual_diff (vd_grid_parity). There is exactly one of each ruler in this estate and this
28// file adds none of them.
29// - THRESHOLDS ARE PARAMETERS. Every bar (bound occupancy, stutter multiple, golden pass line) is
30// an argument with a named default const, so it can be driven from conf and never hides in code.
31//
32// license_tier: ORIGINAL No hardware writes (Rule 26): this reads and computes, it never programs
33// a device. Pure integer arithmetic, wasm-friendly, daemon-reusable.
34import "nx_syscalls.nx"
35// nx_visual_diff is DELIBERATELY NOT IMPORTED HERE. It was, until the golden-image row moved out to
36// nx_enginelab_golden.nx: this file is the pure-integer spine and must stay cheap enough for a wasm
37// world to adopt. See the golden-image note further down for the full reasoning.
38import "nx_fpsmeter.nx"
39import "nx_frame_budget.nx"
40// nxi_buf is the estate's ONE integer-to-decimal emitter (nx_itoa_lib). The capture serialiser at
41// the foot of this file composes it rather than retyping a digit walk, and it costs NOTHING new in
42// the import graph: nx_itoa_lib imports only nx_syscalls, which this file already has. The spine
43// stays pure integer and wasm-adoptable -- no image decoder, no second allocator.
44import "nx_itoa_lib.nx"
45
46// ---- THE RECORD ---------------------------------------------------------------------------------
47// ONE layout for every instrument class. Eight i64 per event. The payload fields carry different
48// meanings per kind and each meaning is named below rather than remembered.
49const EL_I64: i64 = 8 // bytes per i64
50const EL_REC: i64 = 8 // i64 per record
51
52const EL_F_KIND: i64 = 0
53const EL_F_TS: i64 = 1 // monotonic microseconds, caller-supplied
54const EL_F_DUR: i64 = 2 // filled at close; EL_DUR_OPEN while a scope is open
55const EL_F_TID: i64 = 3 // CPU thread id, or GPU queue id for EL_K_GPU
56const EL_F_NAME: i64 = 4 // interned name id -- an id, never a string: no allocation in a hot loop
57const EL_F_A: i64 = 5 // DRAW: pipeline id ALLOC: bytes GPU: submit ts
58const EL_F_B: i64 = 6 // DRAW: vertex count ALLOC: address LOCK: owner tid
59const EL_F_C: i64 = 7 // DRAW: state hash ZONE: child total us FRAME: frame ordinal
60
61const EL_DUR_OPEN: i64 = -1 // a scope that has begun and not ended
62
63// event kinds
64const EL_K_FRAME: i64 = 1
65const EL_K_ZONE: i64 = 2
66const EL_K_GPU: i64 = 3
67const EL_K_DRAW: i64 = 4
68const EL_K_ALLOC: i64 = 5
69const EL_K_FREE: i64 = 6
70const EL_K_LOCK: i64 = 7
71const EL_K_VSYNC: i64 = 8
72
73// ---- THE STORE ----------------------------------------------------------------------------------
74// [header EL_H][zone stack EL_STACK_MAX][records cap * EL_REC]
75const EL_H_CAP: i64 = 0
76const EL_H_N: i64 = 1
77const EL_H_DROP: i64 = 2 // records REFUSED because the store was full. Never silent.
78const EL_H_SP: i64 = 3 // zone stack depth
79const EL_H_LASTF: i64 = 4 // record index of the most recent FRAME, or -1
80const EL_H_MAXSP: i64 = 5 // high-water stack depth
81const EL_H_ERRMONO: i64 = 6 // timestamps that went backwards
82const EL_H_ERROVF: i64 = 7 // zone stack overflows
83const EL_H_ERRUND: i64 = 8 // zone END with no matching BEGIN
84const EL_H_NFRAME: i64 = 9 // FRAME records pushed
85const EL_H_MAGIC: i64 = 10
86const EL_H_RESV: i64 = 11
87const EL_H: i64 = 12
88
89const EL_STACK_MAX: i64 = 64 // nesting depth; an overflow is COUNTED and refused, never wrapped
90const EL_REC_BASE: i64 = EL_H + EL_STACK_MAX
91const EL_MAGIC: i64 = 3126741 // witness that a pointer is really a store
92
93func el_new(cap: i64) -> *i64 {
94 if cap <= 0 { return 0 as *i64 }
95 let words: i64 = EL_REC_BASE + cap * EL_REC
96 let st: *i64 = sys_mmap(words * EL_I64) as *i64
97 if (st as i64) == 0 { return 0 as *i64 }
98 // EXPLICIT init: the arena allocator is not guaranteed to hand back zeroed pages, and a header
99 // that merely LOOKS zeroed is the class of defect this whole file exists to catch.
100 st[EL_H_CAP] = cap
101 st[EL_H_N] = 0
102 st[EL_H_DROP] = 0
103 st[EL_H_SP] = 0
104 st[EL_H_LASTF] = 0 - 1
105 st[EL_H_MAXSP] = 0
106 st[EL_H_ERRMONO] = 0
107 st[EL_H_ERROVF] = 0
108 st[EL_H_ERRUND] = 0
109 st[EL_H_NFRAME] = 0
110 st[EL_H_MAGIC] = EL_MAGIC
111 st[EL_H_RESV] = 0
112 return st
113}
114
115func el_ok(st: *i64) -> i64 {
116 if (st as i64) == 0 { return 0 }
117 if st[EL_H_MAGIC] != EL_MAGIC { return 0 }
118 return 1
119}
120func el_count(st: *i64) -> i64 { return st[EL_H_N] }
121func el_dropped(st: *i64) -> i64 { return st[EL_H_DROP] }
122// COVERAGE, in one call: 1 when every event offered was stored. A caller that publishes a number
123// without asking this is publishing a prefix as a population.
124func el_complete(st: *i64) -> i64 { if st[EL_H_DROP] == 0 { return 1 } return 0 }
125func el_errors(st: *i64) -> i64 { return st[EL_H_ERRMONO] + st[EL_H_ERROVF] + st[EL_H_ERRUND] }
126
127func el_get(st: *i64, i: i64, f: i64) -> i64 { return st[EL_REC_BASE + i * EL_REC + f] }
128func el_set(st: *i64, i: i64, f: i64, v: i64) -> i64 { st[EL_REC_BASE + i * EL_REC + f] = v; return 0 }
129
130// Push one event. Returns the record index, or -1 when the store is FULL (and counts the refusal).
131func el_push(st: *i64, kind: i64, ts: i64, dur: i64, tid: i64, name: i64, a: i64, b: i64, c: i64) -> i64 {
132 let n: i64 = st[EL_H_N]
133 if n >= st[EL_H_CAP] {
134 st[EL_H_DROP] = st[EL_H_DROP] + 1
135 return 0 - 1
136 }
137 el_set(st, n, EL_F_KIND, kind)
138 el_set(st, n, EL_F_TS, ts)
139 el_set(st, n, EL_F_DUR, dur)
140 el_set(st, n, EL_F_TID, tid)
141 el_set(st, n, EL_F_NAME, name)
142 el_set(st, n, EL_F_A, a)
143 el_set(st, n, EL_F_B, b)
144 el_set(st, n, EL_F_C, c)
145 st[EL_H_N] = n + 1
146 return n
147}
148
149// ---- CPU ZONES (the Tracy / Orbit class) ---------------------------------------------------------
150// SELF TIME is the whole point. Inclusive time tells you a function is expensive; only self time
151// tells you whether the cost is IN it or BELOW it, and a profiler that reports inclusive alone sends
152// every reader to the top of the call tree. Each open zone accumulates its direct children's
153// inclusive time in EL_F_C, so self = dur - children, computed exactly, never sampled.
154func el_zone_begin(st: *i64, ts: i64, tid: i64, name: i64) -> i64 {
155 let sp: i64 = st[EL_H_SP]
156 if sp >= EL_STACK_MAX {
157 st[EL_H_ERROVF] = st[EL_H_ERROVF] + 1
158 return 0 - 1
159 }
160 let idx: i64 = el_push(st, EL_K_ZONE, ts, EL_DUR_OPEN, tid, name, 0, 0, 0)
161 if idx < 0 { return 0 - 1 }
162 st[EL_H + sp] = idx
163 st[EL_H_SP] = sp + 1
164 if sp + 1 > st[EL_H_MAXSP] { st[EL_H_MAXSP] = sp + 1 }
165 return idx
166}
167
168// Close the innermost open zone. Returns its record index, or -1 on underflow / backwards clock.
169func el_zone_end(st: *i64, ts: i64) -> i64 {
170 let sp: i64 = st[EL_H_SP]
171 if sp <= 0 {
172 // An END with no BEGIN is not a zero-length zone: it is a broken instrumentation site, and
173 // silently inventing a duration for it would put a fabricated row in every report.
174 st[EL_H_ERRUND] = st[EL_H_ERRUND] + 1
175 return 0 - 1
176 }
177 let idx: i64 = st[EL_H + sp - 1]
178 let start: i64 = el_get(st, idx, EL_F_TS)
179 if ts < start {
180 // A backwards clock cannot produce a negative duration that anyone should aggregate.
181 st[EL_H_ERRMONO] = st[EL_H_ERRMONO] + 1
182 return 0 - 1
183 }
184 let dur: i64 = ts - start
185 el_set(st, idx, EL_F_DUR, dur)
186 st[EL_H_SP] = sp - 1
187 if sp - 1 > 0 {
188 let parent: i64 = st[EL_H + sp - 2]
189 el_set(st, parent, EL_F_C, el_get(st, parent, EL_F_C) + dur)
190 }
191 return idx
192}
193
194func el_zone_open(st: *i64) -> i64 { return st[EL_H_SP] }
195func el_zone_self(st: *i64, idx: i64) -> i64 {
196 let d: i64 = el_get(st, idx, EL_F_DUR)
197 if d == EL_DUR_OPEN { return EL_DUR_OPEN }
198 return d - el_get(st, idx, EL_F_C)
199}
200
201// Aggregate one zone name. out: [0]=calls [1]=inclusive us [2]=self us [3]=max inclusive us
202// [4]=still-open occurrences (reported, never folded into the totals as if they had a duration).
203const EL_AGG_CALLS: i64 = 0
204const EL_AGG_INCL: i64 = 1
205const EL_AGG_SELF: i64 = 2
206const EL_AGG_MAX: i64 = 3
207const EL_AGG_OPEN: i64 = 4
208const EL_AGG_SLOTS: i64 = 5
209func el_zone_agg(st: *i64, name: i64, out: *i64) -> i64 {
210 var k: i64 = 0
211 while k < EL_AGG_SLOTS { out[k] = 0; k = k + 1 }
212 let n: i64 = st[EL_H_N]
213 var i: i64 = 0
214 while i < n {
215 if el_get(st, i, EL_F_KIND) == EL_K_ZONE {
216 if el_get(st, i, EL_F_NAME) == name {
217 let d: i64 = el_get(st, i, EL_F_DUR)
218 if d == EL_DUR_OPEN { out[EL_AGG_OPEN] = out[EL_AGG_OPEN] + 1 }
219 else {
220 out[EL_AGG_CALLS] = out[EL_AGG_CALLS] + 1
221 out[EL_AGG_INCL] = out[EL_AGG_INCL] + d
222 out[EL_AGG_SELF] = out[EL_AGG_SELF] + (d - el_get(st, i, EL_F_C))
223 if d > out[EL_AGG_MAX] { out[EL_AGG_MAX] = d }
224 }
225 }
226 }
227 i = i + 1
228 }
229 return out[EL_AGG_CALLS]
230}
231
232// The hottest zone BY SELF TIME across the whole store. Returns the name id, or -1 when there is no
233// closed zone at all -- an empty store has no hottest zone and must not nominate record 0.
234func el_zone_hottest(st: *i64, out: *i64) -> i64 {
235 let n: i64 = st[EL_H_N]
236 var best_name: i64 = 0 - 1
237 var best_self: i64 = 0 - 1
238 // NEVER ALLOCATE IN A HOT LOOP: one scratch for the whole scan, not one per record.
239 let agg: *i64 = sys_mmap(EL_AGG_SLOTS * EL_I64) as *i64
240 var i: i64 = 0
241 while i < n {
242 if el_get(st, i, EL_F_KIND) == EL_K_ZONE {
243 let d: i64 = el_get(st, i, EL_F_DUR)
244 if d != EL_DUR_OPEN {
245 let nm: i64 = el_get(st, i, EL_F_NAME)
246 let agg: *i64 = sys_mmap(EL_AGG_SLOTS * EL_I64) as *i64
247 el_zone_agg(st, nm, agg)
248 if agg[EL_AGG_SELF] > best_self { best_self = agg[EL_AGG_SELF]; best_name = nm }
249 sys_munmap(agg as *u8, EL_AGG_SLOTS * EL_I64)
250 }
251 }
252 i = i + 1
253 }
254 sys_munmap(agg as *u8, EL_AGG_SLOTS * EL_I64)
255 out[0] = best_self
256 return best_name
257}
258
259// ---- ALLOCATIONS AND LOCK WAITS ------------------------------------------------------------------
260func el_alloc(st: *i64, ts: i64, tid: i64, name: i64, bytes: i64, addr: i64) -> i64 {
261 return el_push(st, EL_K_ALLOC, ts, 0, tid, name, bytes, addr, 0)
262}
263func el_free(st: *i64, ts: i64, tid: i64, addr: i64) -> i64 {
264 return el_push(st, EL_K_FREE, ts, 0, tid, 0, 0, addr, 0)
265}
266func el_lock_wait(st: *i64, ts: i64, dur: i64, tid: i64, name: i64, owner: i64) -> i64 {
267 return el_push(st, EL_K_LOCK, ts, dur, tid, name, 0, owner, 0)
268}
269
270// Live bytes by matching FREE addresses against ALLOC addresses.
271// out: [0]=alloc count [1]=free count [2]=bytes allocated [3]=bytes freed [4]=unmatched frees.
272// An UNMATCHED FREE is surfaced rather than swallowed: it means the capture began mid-run, and a
273// leak number computed over a capture that missed the allocation is a number about the window, not
274// about the program. The caller is told so it can say which.
275const EL_MEM_NALLOC: i64 = 0
276const EL_MEM_NFREE: i64 = 1
277const EL_MEM_BALLOC: i64 = 2
278const EL_MEM_BFREE: i64 = 3
279const EL_MEM_ORPHAN: i64 = 4
280const EL_MEM_SLOTS: i64 = 5
281func el_mem_stats(st: *i64, out: *i64) -> i64 {
282 var k: i64 = 0
283 while k < EL_MEM_SLOTS { out[k] = 0; k = k + 1 }
284 let n: i64 = st[EL_H_N]
285 var i: i64 = 0
286 while i < n {
287 let kd: i64 = el_get(st, i, EL_F_KIND)
288 if kd == EL_K_ALLOC {
289 out[EL_MEM_NALLOC] = out[EL_MEM_NALLOC] + 1
290 out[EL_MEM_BALLOC] = out[EL_MEM_BALLOC] + el_get(st, i, EL_F_A)
291 }
292 if kd == EL_K_FREE {
293 out[EL_MEM_NFREE] = out[EL_MEM_NFREE] + 1
294 let addr: i64 = el_get(st, i, EL_F_B)
295 var j: i64 = 0
296 var found: i64 = 0
297 while j < i {
298 if found == 0 {
299 if el_get(st, j, EL_F_KIND) == EL_K_ALLOC {
300 if el_get(st, j, EL_F_B) == addr {
301 out[EL_MEM_BFREE] = out[EL_MEM_BFREE] + el_get(st, j, EL_F_A)
302 found = 1
303 }
304 }
305 }
306 j = j + 1
307 }
308 if found == 0 { out[EL_MEM_ORPHAN] = out[EL_MEM_ORPHAN] + 1 }
309 }
310 i = i + 1
311 }
312 return out[EL_MEM_BALLOC] - out[EL_MEM_BFREE]
313}
314
315// ---- FRAMES AND PACING (the MangoHud class) ------------------------------------------------------
316// Mark a frame boundary. The PREVIOUS frame's duration is closed here, so a frame's cost is the
317// distance to the next boundary -- the only definition that cannot double-count.
318func el_frame_mark(st: *i64, ts: i64) -> i64 {
319 let prev: i64 = st[EL_H_LASTF]
320 let ord: i64 = st[EL_H_NFRAME]
321 let idx: i64 = el_push(st, EL_K_FRAME, ts, EL_DUR_OPEN, 0, 0, 0, 0, ord)
322 if idx < 0 { return 0 - 1 }
323 if prev >= 0 {
324 let pts: i64 = el_get(st, prev, EL_F_TS)
325 if ts < pts { st[EL_H_ERRMONO] = st[EL_H_ERRMONO] + 1 }
326 else { el_set(st, prev, EL_F_DUR, ts - pts) }
327 }
328 st[EL_H_LASTF] = idx
329 st[EL_H_NFRAME] = ord + 1
330 return idx
331}
332
333// record index of the n-th FRAME, or -1
334func el_frame_rec(st: *i64, ord: i64) -> i64 {
335 let n: i64 = st[EL_H_N]
336 var i: i64 = 0
337 var res: i64 = 0 - 1
338 while i < n {
339 if res < 0 {
340 if el_get(st, i, EL_F_KIND) == EL_K_FRAME {
341 if el_get(st, i, EL_F_C) == ord { res = i }
342 }
343 }
344 i = i + 1
345 }
346 return res
347}
348
349func el_sort_desc(a: *i64, n: i64) -> i64 {
350 var i: i64 = 1
351 while i < n {
352 let v: i64 = a[i]
353 var j: i64 = i - 1
354 var placed: i64 = 0
355 while j >= 0 {
356 if placed == 0 {
357 if a[j] < v { a[j+1] = a[j]; j = j - 1 }
358 else { a[j+1] = v; placed = 1 }
359 }
360 else { j = 0 - 1 }
361 }
362 if placed == 0 { a[0] = v }
363 i = i + 1
364 }
365 return 0
366}
367
368// Frame pacing statistics. Every definition is stated because a percentile with an unstated
369// definition is a number two people will read two ways.
370// avg fps : frames per second over the measured span, x1000 (integer, no float anywhere)
371// 1 percent low: THE MEAN FRAME TIME OF THE WORST 1 PERCENT OF FRAMES, expressed as fps x1000.
372// This is the CapFrameX/MangoHud sense, NOT the 99th-percentile frame time -- they
373// differ, and quoting one while meaning the other is how pacing claims drift.
374// stutter : a frame whose duration exceeds mult_num/mult_den times the MEDIAN frame.
375const EL_FS_FRAMES: i64 = 0
376const EL_FS_SPAN: i64 = 1
377const EL_FS_AVGFPS: i64 = 2 // x1000
378const EL_FS_WORST: i64 = 3 // us
379const EL_FS_MEDIAN: i64 = 4 // us
380const EL_FS_LOW1: i64 = 5 // x1000 fps
381const EL_FS_LOW01: i64 = 6 // x1000 fps
382const EL_FS_STUTTER: i64 = 7
383const EL_FS_OPEN: i64 = 8 // frames with no successor, excluded from every statistic
384const EL_FS_SLOTS: i64 = 9
385
386const EL_FPS_SCALE: i64 = 1000000000 // us -> fps x1000
387const EL_LOW1_PERMIL: i64 = 10 // worst 1 percent
388const EL_LOW01_PERMIL: i64 = 1 // worst 0.1 percent
389const EL_STUTTER_NUM_DEFAULT: i64 = 2 // > 2x median = a stutter (named, and overridable)
390const EL_STUTTER_DEN_DEFAULT: i64 = 1
391
392func el_low_mean_fps(a: *i64, n: i64, permil: i64) -> i64 {
393 // a is sorted DESCENDING by duration, so the worst frames are at the front.
394 var k: i64 = (n * permil) / 1000
395 if k < 1 { k = 1 }
396 if k > n { k = n }
397 var sum: i64 = 0
398 var i: i64 = 0
399 while i < k { sum = sum + a[i]; i = i + 1 }
400 let mean: i64 = sum / k
401 if mean <= 0 { return 0 }
402 return EL_FPS_SCALE / mean
403}
404
405func el_frame_stats(st: *i64, mult_num: i64, mult_den: i64, out: *i64) -> i64 {
406 var k: i64 = 0
407 while k < EL_FS_SLOTS { out[k] = 0; k = k + 1 }
408 let n: i64 = st[EL_H_N]
409 // count closed frames first so the scratch is sized from the data, never from a guessed cap
410 var cnt: i64 = 0
411 var i: i64 = 0
412 while i < n {
413 if el_get(st, i, EL_F_KIND) == EL_K_FRAME {
414 let d: i64 = el_get(st, i, EL_F_DUR)
415 if d == EL_DUR_OPEN { out[EL_FS_OPEN] = out[EL_FS_OPEN] + 1 }
416 else { if d > 0 { cnt = cnt + 1 } }
417 }
418 i = i + 1
419 }
420 out[EL_FS_FRAMES] = cnt
421 if cnt == 0 { return 0 }
422 let arr: *i64 = sys_mmap(cnt * EL_I64) as *i64
423 var w: i64 = 0
424 var span: i64 = 0
425 i = 0
426 while i < n {
427 if el_get(st, i, EL_F_KIND) == EL_K_FRAME {
428 let d: i64 = el_get(st, i, EL_F_DUR)
429 if d != EL_DUR_OPEN {
430 if d > 0 {
431 if w < cnt { arr[w] = d; w = w + 1 }
432 span = span + d
433 }
434 }
435 }
436 i = i + 1
437 }
438 out[EL_FS_SPAN] = span
439 el_sort_desc(arr, cnt)
440 out[EL_FS_WORST] = arr[0]
441 out[EL_FS_MEDIAN] = arr[cnt / 2]
442 if span > 0 { out[EL_FS_AVGFPS] = (EL_FPS_SCALE * cnt) / span }
443 out[EL_FS_LOW1] = el_low_mean_fps(arr, cnt, EL_LOW1_PERMIL)
444 out[EL_FS_LOW01] = el_low_mean_fps(arr, cnt, EL_LOW01_PERMIL)
445 let bar: i64 = (out[EL_FS_MEDIAN] * mult_num) / mult_den
446 var s: i64 = 0
447 var j: i64 = 0
448 while j < cnt {
449 if arr[j] > bar { s = s + 1 }
450 j = j + 1
451 }
452 out[EL_FS_STUTTER] = s
453 sys_munmap(arr as *u8, cnt * EL_I64)
454 return cnt
455}
456
457// COMPOSITION, not duplication: the rolling FPS answer comes from nx_fpsmeter, the estate's one
458// rolling meter. This walks our FRAME records through fm_tick and returns fm_fps -- so if that
459// meter ever changes, this changes with it, and there is no second answer to reconcile.
460func el_fps_rolling(st: *i64, now_ms: i64) -> i64 {
461 let m: *i64 = sys_mmap((2 + FM_CAP) * EL_I64) as *i64
462 fm_init(m)
463 let n: i64 = st[EL_H_N]
464 var i: i64 = 0
465 while i < n {
466 if el_get(st, i, EL_F_KIND) == EL_K_FRAME { fm_tick(m, el_get(st, i, EL_F_TS) / 1000) }
467 i = i + 1
468 }
469 let r: i64 = fm_fps(m, now_ms)
470 sys_munmap(m as *u8, (2 + FM_CAP) * EL_I64)
471 return r
472}
473
474// COMPOSITION: the budget verdict is nx_frame_budget's, not a second opinion.
475// out: [0]=budget us [1]=headroom us. Returns 1 fits / 0 over.
476func el_budget_verdict(frame_us: i64, target_fps: i64, out: *i64) -> i64 {
477 let budget: i64 = fb_budget_us(target_fps)
478 out[0] = budget
479 out[1] = fb_headroom_us(frame_us, budget)
480 return fb_fits(frame_us, budget)
481}
482
483// ---- GPU QUEUES (the GpuVis class) ---------------------------------------------------------------
484func el_gpu_slice(st: *i64, submit_ts: i64, start_ts: i64, dur: i64, queue: i64, name: i64) -> i64 {
485 return el_push(st, EL_K_GPU, start_ts, dur, queue, name, submit_ts, 0, 0)
486}
487func el_vsync(st: *i64, ts: i64) -> i64 { return el_push(st, EL_K_VSYNC, ts, 0, 0, 0, 0, 0, 0) }
488
489// Queue latency: how long work sat between SUBMIT and START. This is the number that says whether
490// the GPU is starved by the CPU or backed up behind itself, and it is invisible to any tool that
491// records only execution time.
492// out: [0]=slices [1]=busy us [2]=total submit-to-start us [3]=worst submit-to-start us
493const EL_GQ_SLICES: i64 = 0
494const EL_GQ_BUSY: i64 = 1
495const EL_GQ_WAIT: i64 = 2
496const EL_GQ_WORST: i64 = 3
497const EL_GQ_SLOTS: i64 = 4
498func el_gpu_stats(st: *i64, queue: i64, out: *i64) -> i64 {
499 var k: i64 = 0
500 while k < EL_GQ_SLOTS { out[k] = 0; k = k + 1 }
501 let n: i64 = st[EL_H_N]
502 var i: i64 = 0
503 while i < n {
504 if el_get(st, i, EL_F_KIND) == EL_K_GPU {
505 if el_get(st, i, EL_F_TID) == queue {
506 out[EL_GQ_SLICES] = out[EL_GQ_SLICES] + 1
507 let d: i64 = el_get(st, i, EL_F_DUR)
508 if d != EL_DUR_OPEN { out[EL_GQ_BUSY] = out[EL_GQ_BUSY] + d }
509 let wait: i64 = el_get(st, i, EL_F_TS) - el_get(st, i, EL_F_A)
510 if wait >= 0 {
511 out[EL_GQ_WAIT] = out[EL_GQ_WAIT] + wait
512 if wait > out[EL_GQ_WORST] { out[EL_GQ_WORST] = wait }
513 }
514 }
515 }
516 i = i + 1
517 }
518 return out[EL_GQ_SLICES]
519}
520
521// ---- THE BOUND VERDICT ---------------------------------------------------------------------------
522// CPU-bound or GPU-bound is the first question anyone asks of an engine, and it is the question most
523// easily answered wrongly, because the honest answer is often 'I cannot see'. There are FIVE outcomes
524// and the third one is load-bearing: with no GPU evidence in the window we return UNKNOWN. Reporting
525// CPU-BOUND because no GPU slices were recorded would be an instrument acquitting on absent evidence.
526const EL_BOUND_UNKNOWN: i64 = 3
527const EL_BOUND_GPU: i64 = 1
528const EL_BOUND_CPU: i64 = 2
529const EL_BOUND_BOTH: i64 = 4
530const EL_BOUND_IDLE: i64 = 5
531const EL_OCC_HI_DEFAULT: i64 = 900 // permil of the frame a unit must occupy to be called the bound
532
533const EL_BV_DUR: i64 = 0
534const EL_BV_GPUOCC: i64 = 1 // permil
535const EL_BV_CPUOCC: i64 = 2 // permil
536const EL_BV_GPUUS: i64 = 3
537const EL_BV_CPUUS: i64 = 4
538const EL_BV_SLOTS: i64 = 5
539
540func el_bound_verdict(st: *i64, ord: i64, hi_permil: i64, out: *i64) -> i64 {
541 var k: i64 = 0
542 while k < EL_BV_SLOTS { out[k] = 0; k = k + 1 }
543 let fi: i64 = el_frame_rec(st, ord)
544 if fi < 0 { return EL_BOUND_UNKNOWN }
545 let dur: i64 = el_get(st, fi, EL_F_DUR)
546 if dur == EL_DUR_OPEN { return EL_BOUND_UNKNOWN }
547 if dur <= 0 { return EL_BOUND_UNKNOWN }
548 let t0: i64 = el_get(st, fi, EL_F_TS)
549 let t1: i64 = t0 + dur
550 out[EL_BV_DUR] = dur
551 let n: i64 = st[EL_H_N]
552 var gpu: i64 = 0
553 var cpu: i64 = 0
554 var seen_gpu: i64 = 0
555 var i: i64 = 0
556 while i < n {
557 let ts: i64 = el_get(st, i, EL_F_TS)
558 if ts >= t0 {
559 if ts < t1 {
560 let kd: i64 = el_get(st, i, EL_F_KIND)
561 let d: i64 = el_get(st, i, EL_F_DUR)
562 if kd == EL_K_GPU {
563 seen_gpu = 1
564 if d != EL_DUR_OPEN { gpu = gpu + d }
565 }
566 if kd == EL_K_ZONE {
567 // ROOT zones only. Summing nested zones would double-count a call tree and
568 // manufacture an occupancy above 1000 permil out of correct data.
569 if el_get(st, i, EL_F_C) >= 0 {
570 if d != EL_DUR_OPEN {
571 if el_zone_is_root(st, i) == 1 { cpu = cpu + d }
572 }
573 }
574 }
575 }
576 }
577 i = i + 1
578 }
579 if seen_gpu == 0 { return EL_BOUND_UNKNOWN }
580 out[EL_BV_GPUUS] = gpu
581 out[EL_BV_CPUUS] = cpu
582 let go: i64 = (gpu * 1000) / dur
583 let co: i64 = (cpu * 1000) / dur
584 out[EL_BV_GPUOCC] = go
585 out[EL_BV_CPUOCC] = co
586 if go >= hi_permil {
587 if co >= hi_permil { return EL_BOUND_BOTH }
588 return EL_BOUND_GPU
589 }
590 if co >= hi_permil { return EL_BOUND_CPU }
591 return EL_BOUND_IDLE
592}
593
594// A zone is a ROOT when no other closed zone on the same thread strictly contains it.
595func el_zone_is_root(st: *i64, idx: i64) -> i64 {
596 let n: i64 = st[EL_H_N]
597 let ts: i64 = el_get(st, idx, EL_F_TS)
598 let tid: i64 = el_get(st, idx, EL_F_TID)
599 var j: i64 = 0
600 var contained: i64 = 0
601 while j < n {
602 if j != idx {
603 if el_get(st, j, EL_F_KIND) == EL_K_ZONE {
604 if el_get(st, j, EL_F_TID) == tid {
605 let jd: i64 = el_get(st, j, EL_F_DUR)
606 if jd != EL_DUR_OPEN {
607 let jt: i64 = el_get(st, j, EL_F_TS)
608 if jt <= ts {
609 if jt + jd >= ts + el_get(st, idx, EL_F_DUR) {
610 if jt != ts { contained = 1 }
611 else { if j < idx { contained = 1 } }
612 }
613 }
614 }
615 }
616 }
617 }
618 j = j + 1
619 }
620 if contained == 1 { return 0 }
621 return 1
622}
623
624// ---- DRAW CALLS AND FRAME CAPTURE (the RenderDoc class) ------------------------------------------
625func el_draw(st: *i64, ts: i64, tid: i64, name: i64, pipeline: i64, verts: i64, state_hash: i64) -> i64 {
626 return el_push(st, EL_K_DRAW, ts, 0, tid, name, pipeline, verts, state_hash)
627}
628
629// A state hash built from the bound pipeline state. Polynomial, integer, order-sensitive: two draws
630// with the same bindings in a different order are NOT the same state and must not hash alike.
631const EL_HASH_MUL: i64 = 131
632const EL_HASH_MOD: i64 = 1000000007
633func el_state_hash(vals: *i64, n: i64) -> i64 {
634 var h: i64 = 0
635 var i: i64 = 0
636 while i < n {
637 h = (h * EL_HASH_MUL + vals[i]) % EL_HASH_MOD
638 i = i + 1
639 }
640 return h
641}
642
643func el_draw_count(st: *i64) -> i64 {
644 let n: i64 = st[EL_H_N]
645 var c: i64 = 0
646 var i: i64 = 0
647 while i < n {
648 if el_get(st, i, EL_F_KIND) == EL_K_DRAW { c = c + 1 }
649 i = i + 1
650 }
651 return c
652}
653
654// REDUNDANT STATE: consecutive draws on one queue that bind an identical state. Every one is a bind
655// the engine paid for and did not need -- the classic finding a frame debugger exists to surface.
656func el_draw_redundant(st: *i64) -> i64 {
657 let n: i64 = st[EL_H_N]
658 var prev_hash: i64 = 0 - 1
659 var prev_tid: i64 = 0 - 1
660 var have: i64 = 0
661 var red: i64 = 0
662 var i: i64 = 0
663 while i < n {
664 if el_get(st, i, EL_F_KIND) == EL_K_DRAW {
665 let h: i64 = el_get(st, i, EL_F_C)
666 let t: i64 = el_get(st, i, EL_F_TID)
667 if have == 1 {
668 if t == prev_tid { if h == prev_hash { red = red + 1 } }
669 }
670 prev_hash = h
671 prev_tid = t
672 have = 1
673 }
674 i = i + 1
675 }
676 return red
677}
678
679// CAPTURE DIFF -- the frame debugger's real job. Two captures of the SAME frame that render
680// differently: name the exact draw ordinal where they diverge and the field that differs, instead of
681// leaving a human to compare two lists by eye.
682const EL_DIFF_SAME: i64 = 0
683const EL_DIFF_DIVERGENT: i64 = 1
684const EL_DIFF_UNMEASURABLE:i64 = 3
685const EL_DF_ORD: i64 = 0
686const EL_DF_FIELD: i64 = 1
687const EL_DF_AVAL: i64 = 2
688const EL_DF_BVAL: i64 = 3
689const EL_DF_ACNT: i64 = 4
690const EL_DF_BCNT: i64 = 5
691const EL_DF_SLOTS: i64 = 6
692// field codes, so the caller learns WHAT differs, not merely THAT something does
693const EL_DFF_COUNT: i64 = 1
694const EL_DFF_PIPELINE: i64 = 2
695const EL_DFF_VERTS: i64 = 3
696const EL_DFF_STATE: i64 = 4
697const EL_DFF_NAME: i64 = 5
698
699func el_draw_nth(st: *i64, ord: i64) -> i64 {
700 let n: i64 = st[EL_H_N]
701 var c: i64 = 0
702 var res: i64 = 0 - 1
703 var i: i64 = 0
704 while i < n {
705 if res < 0 {
706 if el_get(st, i, EL_F_KIND) == EL_K_DRAW {
707 if c == ord { res = i }
708 c = c + 1
709 }
710 }
711 i = i + 1
712 }
713 return res
714}
715
716func el_capture_diff(a: *i64, b: *i64, out: *i64) -> i64 {
717 var k: i64 = 0
718 while k < EL_DF_SLOTS { out[k] = 0 - 1; k = k + 1 }
719 let na: i64 = el_draw_count(a)
720 let nb: i64 = el_draw_count(b)
721 out[EL_DF_ACNT] = na
722 out[EL_DF_BCNT] = nb
723 // THE EMPTY-SET LAW. Two captures with no draws are not 'identical frames'; they are two
724 // measurements that did not happen. Returning SAME here would let a harness that captured
725 // nothing report a clean regression run forever.
726 if na == 0 { return EL_DIFF_UNMEASURABLE }
727 if nb == 0 { return EL_DIFF_UNMEASURABLE }
728 var lim: i64 = na
729 if nb < lim { lim = nb }
730 var i: i64 = 0
731 var found: i64 = 0
732 while i < lim {
733 if found == 0 {
734 let ia: i64 = el_draw_nth(a, i)
735 let ib: i64 = el_draw_nth(b, i)
736 let pa: i64 = el_get(a, ia, EL_F_A)
737 let pb: i64 = el_get(b, ib, EL_F_A)
738 let va: i64 = el_get(a, ia, EL_F_B)
739 let vb: i64 = el_get(b, ib, EL_F_B)
740 let sa: i64 = el_get(a, ia, EL_F_C)
741 let sb: i64 = el_get(b, ib, EL_F_C)
742 let ma: i64 = el_get(a, ia, EL_F_NAME)
743 let mb: i64 = el_get(b, ib, EL_F_NAME)
744 if pa != pb { out[EL_DF_ORD]=i; out[EL_DF_FIELD]=EL_DFF_PIPELINE; out[EL_DF_AVAL]=pa; out[EL_DF_BVAL]=pb; found=1 }
745 if found == 0 { if va != vb { out[EL_DF_ORD]=i; out[EL_DF_FIELD]=EL_DFF_VERTS; out[EL_DF_AVAL]=va; out[EL_DF_BVAL]=vb; found=1 } }
746 if found == 0 { if sa != sb { out[EL_DF_ORD]=i; out[EL_DF_FIELD]=EL_DFF_STATE; out[EL_DF_AVAL]=sa; out[EL_DF_BVAL]=sb; found=1 } }
747 if found == 0 { if ma != mb { out[EL_DF_ORD]=i; out[EL_DF_FIELD]=EL_DFF_NAME; out[EL_DF_AVAL]=ma; out[EL_DF_BVAL]=mb; found=1 } }
748 }
749 i = i + 1
750 }
751 if found == 1 { return EL_DIFF_DIVERGENT }
752 if na != nb {
753 out[EL_DF_ORD] = lim
754 out[EL_DF_FIELD] = EL_DFF_COUNT
755 out[EL_DF_AVAL] = na
756 out[EL_DF_BVAL] = nb
757 return EL_DIFF_DIVERGENT
758 }
759 return EL_DIFF_SAME
760}
761
762// ---- GOLDEN-IMAGE REGRESSION LIVES IN nx_enginelab_golden.nx -------------------------------------
763// EXTRACTED 2026-08-28, and the reason is measurable rather than stylistic. el_golden was HERE, and
764// having it here forced this file to import nx_visual_diff -- and with it a PNG decoder -- on every
765// consumer of a spine that is otherwise PURE INTEGER by design. The spine is pure integer precisely
766// so it can be compiled into a wasm world, which is the natural first consumer of an engine
767// instrument; a consumer that only wants frames and zones should not pay for an image decoder.
768// A CAPABILITY THAT TAXES EVERY CONSUMER FOR A FEATURE MOST OF THEM NEVER CALL BELONGS IN ITS OWN
769// MODULE. Import nx_enginelab_golden.nx when you want the golden-image row; the function, its
770// thresholds and its UNMEASURABLE refusal moved verbatim and are unchanged.
771
772// ---- THE JOIN: what no separate-format tool can answer -------------------------------------------
773// One frame, every instrument class, one call. This is the payoff of the single spine.
774const EL_FA_DUR: i64 = 0
775const EL_FA_DRAWS: i64 = 1
776const EL_FA_GPUUS: i64 = 2
777const EL_FA_CPUUS: i64 = 3
778const EL_FA_LOCKUS: i64 = 4
779const EL_FA_ALLOCB: i64 = 5
780const EL_FA_EVENTS: i64 = 6
781const EL_FA_HOTNAME:i64 = 7
782const EL_FA_SLOTS: i64 = 8
783
784func el_frame_attr(st: *i64, ord: i64, out: *i64) -> i64 {
785 var k: i64 = 0
786 while k < EL_FA_SLOTS { out[k] = 0; k = k + 1 }
787 out[EL_FA_HOTNAME] = 0 - 1
788 let fi: i64 = el_frame_rec(st, ord)
789 if fi < 0 { return 0 }
790 let dur: i64 = el_get(st, fi, EL_F_DUR)
791 if dur == EL_DUR_OPEN { return 0 }
792 out[EL_FA_DUR] = dur
793 let t0: i64 = el_get(st, fi, EL_F_TS)
794 let t1: i64 = t0 + dur
795 let n: i64 = st[EL_H_N]
796 var best_self: i64 = 0 - 1
797 var i: i64 = 0
798 while i < n {
799 let ts: i64 = el_get(st, i, EL_F_TS)
800 if ts >= t0 {
801 if ts < t1 {
802 out[EL_FA_EVENTS] = out[EL_FA_EVENTS] + 1
803 let kd: i64 = el_get(st, i, EL_F_KIND)
804 let d: i64 = el_get(st, i, EL_F_DUR)
805 if kd == EL_K_DRAW { out[EL_FA_DRAWS] = out[EL_FA_DRAWS] + 1 }
806 if kd == EL_K_GPU { if d != EL_DUR_OPEN { out[EL_FA_GPUUS] = out[EL_FA_GPUUS] + d } }
807 if kd == EL_K_LOCK { if d != EL_DUR_OPEN { out[EL_FA_LOCKUS] = out[EL_FA_LOCKUS] + d } }
808 if kd == EL_K_ALLOC { out[EL_FA_ALLOCB] = out[EL_FA_ALLOCB] + el_get(st, i, EL_F_A) }
809 if kd == EL_K_ZONE {
810 if d != EL_DUR_OPEN {
811 if el_zone_is_root(st, i) == 1 { out[EL_FA_CPUUS] = out[EL_FA_CPUUS] + d }
812 let self_us: i64 = d - el_get(st, i, EL_F_C)
813 if self_us > best_self { best_self = self_us; out[EL_FA_HOTNAME] = el_get(st, i, EL_F_NAME) }
814 }
815 }
816 }
817 }
818 i = i + 1
819 }
820 return 1
821}
822
823// ---- EL1: A CAPTURE THAT LEAVES THE PROCESS ------------------------------------------------------
824// Everything above analyses a spine that lives and dies inside one process, and that gap is the
825// whole difference between an instrument and an anecdote: a capture you cannot attach to a bug
826// report, diff across machines, or reopen tomorrow is a measurement that expired with its session.
827// This estate has the receipt -- every visual claim made about the world pages on 2026-08-28 died
828// with the session that made it, and the acceptance corpus holds no banked subject-side frame.
829//
830// THE FORMAT IS TEXT, AND THAT IS A DECISION, NOT A SHORTCUT. The other surface of this instrument
831// runs IN-PAGE, in JavaScript, on whatever engine the operator actually uses. An in-page instrument
832// needs NO DRIVER PROTOCOL: it behaves identically on Gecko and on Chromium, whereas a capture taken
833// over a Chrome DevTools wire works on exactly one of them and -- measured on this estate -- returns
834// a confident WRONG answer on timing, because requestAnimationFrame does not tick under that wire at
835// all. Text is the one format both surfaces can write without either importing the other's runtime.
836// It also keeps this rung an ORACLE relationship rather than a DEPENDENCY: nothing here acquires a
837// browser, a driver or a frame. The caller supplies the data, exactly as el_golden takes decoded
838// images from its caller and never goes looking for them itself.
839//
840// TRUNCATION IS REFUSED, NEVER PARSED. The record count appears in the HEADER and again in the
841// TRAILER, so a file that stops early cannot satisfy both and is rejected by arithmetic rather than
842// by hoping a reader notices. The declared count is additionally bounded by what the file could
843// PHYSICALLY hold, so a header claiming a million records over two hundred bytes is refused before
844// anything is allocated.
845//
846// COVERAGE SURVIVES THE ROUND TRIP. EL_H_DROP is serialised and restored. Without that, writing an
847// incomplete capture and reading it back would LAUNDER it into a complete-looking one -- the
848// coverage marker filtered out of the very artifact whose job is to carry it.
849const EL_CAP_MAGIC: *u8 = "NXEL1"
850const EL_CAP_TRAILER: *u8 = "NXEL1END"
851const EL_SP: i64 = 32
852const EL_NL: i64 = 10
853const EL_CR: i64 = 13
854const EL_HTAB: i64 = 9
855const EL_MINUS_CH: i64 = 45
856const EL_D0: i64 = 48
857const EL_D9: i64 = 57
858const EL_DECBASE: i64 = 10
859// An i64 is at most 19 digits plus a sign. EL_CAP_LINE_MAX bounds one record line, EL_CAP_EDGE_MAX
860// one header or trailer line; both are DERIVED from that width rather than picked, and the buffer a
861// caller needs is computed from the data by el_capture_bytes so nothing is ever guessed or clipped.
862const EL_CAP_LINE_MAX: i64 = 200
863const EL_CAP_EDGE_MAX: i64 = 128
864// The smallest a record line can physically be: eight single-digit fields, seven separators, newline.
865const EL_CAP_MIN_LINE: i64 = 16
866
867// refusal reasons -- THE REASON TRAVELS WITH THE REFUSAL, never a bare zero the caller must guess at
868const EL_CP_OK: i64 = 0
869const EL_CP_NOFILE: i64 = 1
870const EL_CP_BAD_MAGIC: i64 = 2
871const EL_CP_BAD_HEADER: i64 = 3
872const EL_CP_TOO_BIG: i64 = 4
873const EL_CP_SHORT: i64 = 5
874const EL_CP_NO_TRAILER: i64 = 6
875const EL_CP_TRAILER_MISMATCH: i64 = 7
876const EL_CP_NOMEM: i64 = 8
877// THE SEVENTH REASON, AND IT CLOSES THE SILENT CASE THE OTHER SIX LEAVE OPEN. Header-and-trailer
878// counts catch a file that STOPS early. They cannot catch a file that is the RIGHT LENGTH with ONE
879// WRONG DIGIT INSIDE a record: the count is unchanged, header and trailer still agree, the file
880// parses cleanly, and every analyzer then computes confidently over a number that is not what was
881// measured. That is this board's own recurring defect wearing a file format -- an instrument
882// returning a confident wrong answer instead of refusing -- so the wire carries a checksum.
883// IT WAS ARGUED THAT A TEXT WIRE OUT OF A JAVASCRIPT CONSOLE CANNOT OFFER THIS GUARANTEE. That is
884// refuted by construction, which is why the guarantee is here rather than excused: the producer
885// already walks every record value to format it, so folding them costs one multiply-add per field
886// and imports NOTHING. An absent guarantee must name its reason; this one is simply not absent.
887const EL_CP_CHECKSUM: i64 = 9
888// Mirrors ELS_HASH_MUL / ELS_HASH_MOD in nx_enginelab_store.nx ON PURPOSE rather than importing it:
889// the spine must not take a file-I/O module into its graph. The modulus is under 2^31 and the
890// multiplier under 2^20, so every intermediate stays below 2^53 and the fold is EXACT in JavaScript
891// double arithmetic as well as in i64 -- which is precisely the property that lets both surfaces
892// compute the same checksum without either importing the other's runtime.
893const EL_WCK_MUL: i64 = 1000003
894const EL_WCK_MOD: i64 = 2147483647
895
896// Folded over the DECLARED COUNTS and then every record field, in order. The counts are included so
897// a corrupted drop or frame count is caught too -- coverage metadata is exactly the thing whose
898// silent corruption would let a partial capture read as a complete one.
899func el_wire_ck(st: *i64, n: i64, nframe: i64, drop: i64) -> i64 {
900 var ck: i64 = 0
901 ck = (ck * EL_WCK_MUL + (n % EL_WCK_MOD)) % EL_WCK_MOD
902 ck = (ck * EL_WCK_MUL + (nframe % EL_WCK_MOD)) % EL_WCK_MOD
903 ck = (ck * EL_WCK_MUL + (drop % EL_WCK_MOD)) % EL_WCK_MOD
904 var i: i64 = 0
905 while i < n {
906 var f: i64 = 0
907 while f < EL_REC {
908 var v: i64 = el_get(st, i, f) % EL_WCK_MOD
909 if v < 0 { v = v + EL_WCK_MOD }
910 ck = (ck * EL_WCK_MUL + v) % EL_WCK_MOD
911 f = f + 1
912 }
913 i = i + 1
914 }
915 return ck
916}
917
918func el_cat_lit(d: *u8, o: i64, s: *u8) -> i64 {
919 var i: i64 = 0
920 var p: i64 = o
921 while s[i] != (0 as u8) { d[p] = s[i]; p = p + 1; i = i + 1 }
922 return p
923}
924
925// DERIVED FROM THE DATA, never a picked ceiling: a caller sizes its buffer exactly, so a full store
926// cannot be silently clipped at write time.
927func el_capture_bytes(st: *i64) -> i64 {
928 if el_ok(st) == 0 { return 0 }
929 return EL_CAP_EDGE_MAX + st[EL_H_N] * EL_CAP_LINE_MAX + EL_CAP_EDGE_MAX
930}
931
932func el_capture_serialize(st: *i64, buf: *u8, cap: i64) -> i64 {
933 if el_ok(st) == 0 { return 0 - 1 }
934 if cap < el_capture_bytes(st) { return 0 - 1 }
935 let n: i64 = st[EL_H_N]
936 var p: i64 = el_cat_lit(buf, 0, EL_CAP_MAGIC)
937 buf[p] = EL_SP as u8; p = p + 1
938 p = nxi_buf(buf, p, n)
939 buf[p] = EL_SP as u8; p = p + 1
940 p = nxi_buf(buf, p, st[EL_H_NFRAME])
941 buf[p] = EL_SP as u8; p = p + 1
942 p = nxi_buf(buf, p, st[EL_H_DROP])
943 buf[p] = EL_NL as u8; p = p + 1
944 var i: i64 = 0
945 while i < n {
946 var f: i64 = 0
947 while f < EL_REC {
948 if f > 0 { buf[p] = EL_SP as u8; p = p + 1 }
949 p = nxi_buf(buf, p, el_get(st, i, f))
950 f = f + 1
951 }
952 buf[p] = EL_NL as u8; p = p + 1
953 i = i + 1
954 }
955 p = el_cat_lit(buf, p, EL_CAP_TRAILER)
956 buf[p] = EL_SP as u8; p = p + 1
957 p = nxi_buf(buf, p, n)
958 buf[p] = EL_SP as u8; p = p + 1
959 p = nxi_buf(buf, p, el_wire_ck(st, n, st[EL_H_NFRAME], st[EL_H_DROP]))
960 buf[p] = EL_NL as u8; p = p + 1
961 return p
962}
963
964func el_is_ws(c: i64) -> i64 {
965 if c == EL_SP { return 1 }
966 if c == EL_NL { return 1 }
967 if c == EL_CR { return 1 }
968 if c == EL_HTAB { return 1 }
969 return 0
970}
971
972func el_scan_ws(buf: *u8, len: i64, pos: i64) -> i64 {
973 var p: i64 = pos
974 while p < len {
975 if el_is_ws(buf[p] as i64) == 0 { return p }
976 p = p + 1
977 }
978 return p
979}
980
981// out[0] receives the value; the RETURN is the next position, or -1 REFUSED. The refusal channel is
982// deliberately separate from the value, and that is not style: -1 is a LEGITIMATE field value in
983// this format (EL_DUR_OPEN marks a scope that began and has not ended), so any parser signalling
984// refusal by returning -1 is structurally unable to express the difference and could not be
985// composed here however correct it is elsewhere.
986func el_scan_i64(buf: *u8, len: i64, pos: i64, out: *i64) -> i64 {
987 var p: i64 = el_scan_ws(buf, len, pos)
988 if p >= len { return 0 - 1 }
989 var neg: i64 = 0
990 if (buf[p] as i64) == EL_MINUS_CH { neg = 1; p = p + 1 }
991 var acc: i64 = 0
992 var got: i64 = 0
993 var run: i64 = 1
994 while run == 1 {
995 if p >= len { run = 0 }
996 else {
997 let c: i64 = buf[p] as i64
998 if c >= EL_D0 {
999 if c <= EL_D9 {
1000 acc = acc * EL_DECBASE + (c - EL_D0)
1001 got = got + 1
1002 p = p + 1
1003 }
1004 else { run = 0 }
1005 }
1006 else { run = 0 }
1007 }
1008 }
1009 if got == 0 { return 0 - 1 }
1010 if neg == 1 { acc = 0 - acc }
1011 out[0] = acc
1012 return p
1013}
1014
1015func el_scan_lit(buf: *u8, len: i64, pos: i64, lit: *u8) -> i64 {
1016 let p: i64 = el_scan_ws(buf, len, pos)
1017 var i: i64 = 0
1018 while lit[i] != (0 as u8) {
1019 if p + i >= len { return 0 - 1 }
1020 if buf[p+i] != lit[i] { return 0 - 1 }
1021 i = i + 1
1022 }
1023 return p + i
1024}
1025
1026// Returns a store, or 0 with err[0] naming WHICH refusal. Declared imprecision, stated rather than
1027// left for a reader to discover: the refusal paths abandon two small scratch allocations into the
1028// arena instead of unwinding them at each of nine exits. A refusal is not a hot path and the arena
1029// is reset per run; the alternative was nine hand-written unwinds, which is where an error-path
1030// leak actually comes from.
1031func el_capture_parse(buf: *u8, len: i64, err: *i64) -> *i64 {
1032 err[0] = EL_CP_BAD_MAGIC
1033 var p: i64 = el_scan_lit(buf, len, 0, EL_CAP_MAGIC)
1034 if p < 0 { return 0 as *i64 }
1035 let t: *i64 = sys_mmap(EL_I64) as *i64
1036 err[0] = EL_CP_BAD_HEADER
1037 p = el_scan_i64(buf, len, p, t)
1038 if p < 0 { return 0 as *i64 }
1039 let n: i64 = t[0]
1040 p = el_scan_i64(buf, len, p, t)
1041 if p < 0 { return 0 as *i64 }
1042 let nframe: i64 = t[0]
1043 p = el_scan_i64(buf, len, p, t)
1044 if p < 0 { return 0 as *i64 }
1045 let drop: i64 = t[0]
1046 if n < 0 { return 0 as *i64 }
1047 if nframe < 0 { return 0 as *i64 }
1048 if drop < 0 { return 0 as *i64 }
1049 // A DECLARED COUNT IS A CLAIM AND IS CHECKED AGAINST PHYSICS BEFORE IT IS TRUSTED: no file can
1050 // hold more records than its own length divided by the shortest legal record line.
1051 if n > len / EL_CAP_MIN_LINE { err[0] = EL_CP_TOO_BIG; return 0 as *i64 }
1052 let st: *i64 = el_new(n + 1)
1053 if el_ok(st) == 0 { err[0] = EL_CP_NOMEM; return 0 as *i64 }
1054 let fv: *i64 = sys_mmap(EL_REC * EL_I64) as *i64
1055 var i: i64 = 0
1056 while i < n {
1057 var f: i64 = 0
1058 while f < EL_REC {
1059 p = el_scan_i64(buf, len, p, t)
1060 if p < 0 { err[0] = EL_CP_SHORT; return 0 as *i64 }
1061 fv[f] = t[0]
1062 f = f + 1
1063 }
1064 el_push(st, fv[EL_F_KIND], fv[EL_F_TS], fv[EL_F_DUR], fv[EL_F_TID], fv[EL_F_NAME], fv[EL_F_A], fv[EL_F_B], fv[EL_F_C])
1065 i = i + 1
1066 }
1067 let tp: i64 = el_scan_lit(buf, len, p, EL_CAP_TRAILER)
1068 if tp < 0 { err[0] = EL_CP_NO_TRAILER; return 0 as *i64 }
1069 let q: i64 = el_scan_i64(buf, len, tp, t)
1070 if q < 0 { err[0] = EL_CP_NO_TRAILER; return 0 as *i64 }
1071 if t[0] != n { err[0] = EL_CP_TRAILER_MISMATCH; return 0 as *i64 }
1072 // THE COUNTS AGREEING PROVES ONLY THAT THE FILE DID NOT STOP EARLY. It says nothing about a digit
1073 // INSIDE a record having changed -- the case that parses cleanly and hands every analyzer one
1074 // wrong number with no symptom at all. The checksum is the only thing between that and a
1075 // confident answer, and it is folded over the declared counts as well as the records, so a
1076 // corrupted DROP count cannot quietly turn a partial capture into a complete-looking one.
1077 let cq: i64 = el_scan_i64(buf, len, q, t)
1078 if cq < 0 { err[0] = EL_CP_CHECKSUM; return 0 as *i64 }
1079 if t[0] != el_wire_ck(st, n, nframe, drop) { err[0] = EL_CP_CHECKSUM; return 0 as *i64 }
1080 // RESTORED, NOT RECOMPUTED. el_push rebuilds the records but touches none of the frame
1081 // bookkeeping, and DROP describes events the ORIGINAL store REFUSED -- no amount of replaying
1082 // the survivors can rediscover it. Losing either would launder an incomplete capture into a
1083 // complete-looking one, which is the single thing this format exists to prevent.
1084 st[EL_H_NFRAME] = nframe
1085 st[EL_H_DROP] = drop
1086 var lf: i64 = 0 - 1
1087 var k: i64 = 0
1088 while k < n {
1089 if el_get(st, k, EL_F_KIND) == EL_K_FRAME { lf = k }
1090 k = k + 1
1091 }
1092 st[EL_H_LASTF] = lf
1093 err[0] = EL_CP_OK
1094 return st
1095}
1096
1097// FILE I/O DELIBERATELY DOES NOT LIVE HERE. This lane wrote an el_capture_write/el_capture_read
1098// pair at this point and DELETED them within the hour: nx_enginelab_store.nx had landed the same
1099// rung independently, better placed and better specified (magic, version, a declared byte length, a
1100// checksum, and SIX named refusal reasons that each print the declared and actual byte counts). Two
1101// definitions of one name is a compile stop, and the compiler was right to stop -- but the real
1102// lesson is the one the estate keeps re-learning: TWO LANES BUILT THE SAME RUNG INSIDE ONE HOUR, AND
1103// THE COLLISION WAS CAUGHT BY THE LINKER RATHER THAN BY EITHER LANE LOOKING FIRST.
1104//
1105// WHAT SURVIVES ABOVE IS NOT A SECOND PERSISTENCE LAYER AND MUST NOT BE READ AS ONE. el_capture_bytes,
1106// el_capture_serialize and el_capture_parse touch NO file and open NO path: they are the BROWSER WIRE.
1107// nx_enginelab_store persists a capture as i64 words for native-to-native reuse, which a page cannot
1108// produce; these three move a capture as TEXT out of a JavaScript console and back into this spine,
1109// which is the only shape that crosses that boundary. Different subject, different direction, and the
1110// spine stays pure-buffer either way -- no path, no descriptor, no import beyond what was already here.
1111
1112// ---- EL7: THE OVERLAY -- ONE RULER, TWO SURFACES -------------------------------------------------
1113// An overlay is not a second profiler. Every pacing figure below is COMPOSED from el_frame_stats, so
1114// there is exactly one place in this estate where a frame statistic is defined and the overlay
1115// cannot drift from it -- not by discipline, but because it has no arithmetic of its own to drift
1116// with. Mutate the composition and the gate's field-by-field equality tooth fails immediately.
1117//
1118// AND IT JUDGES NOTHING. It returns numbers and names the axes it could not observe. The one
1119// instrument in this estate that survived its subject changing under it did exactly this: it printed
1120// observed state and its named teeth and left the verdict to the caller, which is why it needed no
1121// edit when the question changed from one thing to another. Every instrument here that went stale
1122// had baked a verdict in.
1123//
1124// QUALITY TRAVELS ON THE SAME CLOCK AS COST, AND THAT IS THE WHOLE POINT OF THIS ROW.
1125// A frame-time number published WITHOUT a paired image-quality number is how performance work
1126// silently becomes pixels. A controller that can only see milliseconds can only ever SPEND quality,
1127// because quality is not a thing it is able to measure -- so it is not trading, it is draining. Here
1128// the quality level and its REASON are inputs to the SAME call that produces the cost figures and
1129// land in the SAME structure, so there is no way to report one axis with the other absent.
1130//
1131// THE QUALITY FIGURE IS NO-REFERENCE BY REQUIREMENT, NOT BY CONVENIENCE, AND THAT IS THE WHOLE
1132// DESIGN CONSTRAINT OF THIS ROW. The two rulers a reader reaches for first are both structurally
1133// unavailable here, and neither absence is a shortcut:
1134// SSIM and PSNR are FULL-REFERENCE. They score a frame against a ground-truth frame. A live
1135// renderer running at a coarsened ray size HAS no ground truth, and producing one costs exactly the
1136// work the coarsening exists to avoid -- so a full-reference score is not merely absent at runtime,
1137// it is self-defeating there. It stays the right ruler for an OFFLINE golden comparison (el_golden
1138// over a captured frame) and the wrong one for a per-frame overlay. Different jobs, different rulers.
1139// nx_quality_balance speaks perplexity-permil and bits-per-weight. That is model-weight fidelity,
1140// a different sense of the word quality entirely, with no conversion into image terms anywhere in
1141// the corpus. Wiring it here would be a homonym, not a measurement.
1142// => What a no-reference term MAY use is the renderer's OWN declared parameters, and there are two:
1143// SAMPLING DENSITY below, and BAND CARRIAGE -- rlf_band_resolvable(res, lambda_um) in nx_relief_lib
1144// answers 'is this declared physical band actually carried at this resolution' as 1 or 0 with no
1145// reference image at all. Carriage plus density is a defensible no-reference pair in physical units.
1146// It is named here as the COMPOSITION TARGET rather than re-implemented, and it is deliberately not
1147// yet an argument to el_overlay_stats: that is a signature change, it touches every tooth that calls
1148// it, and it lands after these figures are green. One change at a time.
1149//
1150// SAMPLING DENSITY, AND ITS LIMIT STATED RATHER THAN IMPLIED: permil of the
1151// device's own pixel count actually rendered. That is exactly the axis an adaptive resolution
1152// governor spends; it is free to read, and it needs NO framebuffer readback -- a readback would
1153// stall the pipeline and change the very frame time being measured, which is the instrument becoming
1154// the defect. It is NOT a perceptual score, and it is not offered as one. Perceptual render-content
1155// correctness is el_golden in nx_enginelab_golden.nx over a captured frame; this row composes
1156// nothing from there and does not pretend to replace it.
1157const EL_Q_UNKNOWN: i64 = 0
1158const EL_Q_NATIVE: i64 = 1
1159const EL_Q_GOVERNOR: i64 = 2
1160const EL_Q_RECORD: i64 = 3
1161const EL_Q_EXPLICIT: i64 = 4
1162const EL_Q_GEOMETRY: i64 = 5
1163const EL_Q_FULL_PERMIL: i64 = 1000
1164
1165const EL_OV_FRAMES: i64 = 0
1166const EL_OV_AVGFPS: i64 = 1
1167const EL_OV_WORST: i64 = 2
1168const EL_OV_MEDIAN: i64 = 3
1169const EL_OV_LOW1: i64 = 4
1170const EL_OV_STUTTER: i64 = 5
1171const EL_OV_OPEN: i64 = 6
1172const EL_OV_DROPPED: i64 = 7
1173const EL_OV_DRAWS: i64 = 8
1174const EL_OV_REDUNDANT: i64 = 9
1175const EL_OV_BUDGET: i64 = 10
1176const EL_OV_HEADROOM: i64 = 11
1177const EL_OV_QPERMIL: i64 = 12
1178const EL_OV_QREASON: i64 = 13
1179const EL_OV_TRIS: i64 = 14
1180const EL_OV_SLOTS: i64 = 15
1181
1182func el_overlay_stats(st: *i64, target_fps: i64, q_permil: i64, q_reason: i64, tris: i64, out: *i64) -> i64 {
1183 var k: i64 = 0
1184 while k < EL_OV_SLOTS { out[k] = 0; k = k + 1 }
1185 // The quality pair is written BEFORE the early return, so a store the overlay cannot read still
1186 // reports the quality level it was handed rather than a zero indistinguishable from full detail.
1187 out[EL_OV_QPERMIL] = q_permil
1188 out[EL_OV_QREASON] = q_reason
1189 out[EL_OV_TRIS] = tris
1190 if el_ok(st) == 0 { return 0 - 1 }
1191 let fs: *i64 = sys_mmap(EL_FS_SLOTS * EL_I64) as *i64
1192 let cnt: i64 = el_frame_stats(st, EL_STUTTER_NUM_DEFAULT, EL_STUTTER_DEN_DEFAULT, fs)
1193 out[EL_OV_FRAMES] = fs[EL_FS_FRAMES]
1194 out[EL_OV_AVGFPS] = fs[EL_FS_AVGFPS]
1195 out[EL_OV_WORST] = fs[EL_FS_WORST]
1196 out[EL_OV_MEDIAN] = fs[EL_FS_MEDIAN]
1197 out[EL_OV_LOW1] = fs[EL_FS_LOW1]
1198 out[EL_OV_STUTTER] = fs[EL_FS_STUTTER]
1199 out[EL_OV_OPEN] = fs[EL_FS_OPEN]
1200 out[EL_OV_DROPPED] = el_dropped(st)
1201 out[EL_OV_DRAWS] = el_draw_count(st)
1202 out[EL_OV_REDUNDANT] = el_draw_redundant(st)
1203 let bv: *i64 = sys_mmap(2 * EL_I64) as *i64
1204 el_budget_verdict(fs[EL_FS_MEDIAN], target_fps, bv)
1205 out[EL_OV_BUDGET] = bv[0]
1206 out[EL_OV_HEADROOM] = bv[1]
1207 sys_munmap(bv as *u8, 2 * EL_I64)
1208 sys_munmap(fs as *u8, EL_FS_SLOTS * EL_I64)
1209 return cnt
1210}
1211
1212const EL_OV_MAGIC: *u8 = "NXOV1"
1213const EL_OV_TRAILER: *u8 = "NXOV1END"
1214// Fifteen labelled rows plus a magic and a trailer, none of which can exceed a label plus an i64.
1215const EL_OV_TEXT_MAX: i64 = 1024
1216
1217func el_ov_row(buf: *u8, p0: i64, label: *u8, v: i64) -> i64 {
1218 var p: i64 = el_cat_lit(buf, p0, label)
1219 buf[p] = EL_SP as u8; p = p + 1
1220 p = nxi_buf(buf, p, v)
1221 buf[p] = EL_NL as u8; p = p + 1
1222 return p
1223}
1224
1225// The canonical overlay block. The in-page surface renders a SUBSET of these same fields live and
1226// defers every derived statistic to this function via a dumped capture, so the browser never carries
1227// a second copy of the arithmetic. Returns the length written, or -1 REFUSED on a short buffer.
1228func el_overlay_emit(st: *i64, target_fps: i64, q_permil: i64, q_reason: i64, tris: i64, buf: *u8, cap: i64) -> i64 {
1229 if cap < EL_OV_TEXT_MAX { return 0 - 1 }
1230 let o: *i64 = sys_mmap(EL_OV_SLOTS * EL_I64) as *i64
1231 el_overlay_stats(st, target_fps, q_permil, q_reason, tris, o)
1232 var p: i64 = el_cat_lit(buf, 0, EL_OV_MAGIC)
1233 buf[p] = EL_NL as u8; p = p + 1
1234 p = el_ov_row(buf, p, "frames" as *u8, o[EL_OV_FRAMES])
1235 p = el_ov_row(buf, p, "open" as *u8, o[EL_OV_OPEN])
1236 p = el_ov_row(buf, p, "dropped" as *u8, o[EL_OV_DROPPED])
1237 p = el_ov_row(buf, p, "avgfps_x1000" as *u8, o[EL_OV_AVGFPS])
1238 p = el_ov_row(buf, p, "worst_us" as *u8, o[EL_OV_WORST])
1239 p = el_ov_row(buf, p, "median_us" as *u8, o[EL_OV_MEDIAN])
1240 p = el_ov_row(buf, p, "low1_x1000" as *u8, o[EL_OV_LOW1])
1241 p = el_ov_row(buf, p, "stutter" as *u8, o[EL_OV_STUTTER])
1242 p = el_ov_row(buf, p, "draws" as *u8, o[EL_OV_DRAWS])
1243 p = el_ov_row(buf, p, "redundant_binds" as *u8, o[EL_OV_REDUNDANT])
1244 p = el_ov_row(buf, p, "tris" as *u8, o[EL_OV_TRIS])
1245 p = el_ov_row(buf, p, "budget_us" as *u8, o[EL_OV_BUDGET])
1246 p = el_ov_row(buf, p, "headroom_us" as *u8, o[EL_OV_HEADROOM])
1247 p = el_ov_row(buf, p, "quality_permil" as *u8, o[EL_OV_QPERMIL])
1248 p = el_ov_row(buf, p, "quality_reason" as *u8, o[EL_OV_QREASON])
1249 p = el_cat_lit(buf, p, EL_OV_TRAILER)
1250 buf[p] = EL_NL as u8; p = p + 1
1251 sys_munmap(o as *u8, EL_OV_SLOTS * EL_I64)
1252 return p
1253}