code wiki / (root) / nx_enginelab_lib.nx

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}