code wiki / (root) / nx_vpick_gate.nx

nx_vpick_gate.nx source

↩ module page · 882 lines · 60800 B

1// nx_vpick_gate.nx -- the gate for nx_vpick_lib (/compare/dcc DC4: ray-pick and a transform gizmo). 2// 3// THE CLAIM UNDER TEST is that a click in the sovereign viewport resolves to the RIGHT triangle and a drag 4// resolves to an UNDOABLE operation. Both halves are asserted on values a wrong implementation cannot 5// produce, never on a return code alone -- because the three cheapest wrong pickers in the world all 6// return plausible codes: one that always misses, one that always returns triangle 0, and one that returns 7// whichever triangle happens to be first in the buffer. 8// 9// THE FIXTURE CAMERA IS THE SHIPPED CAMERA. VG_W, VG_H, VG_DIST and VG_FOVH below are literally MV_W, 10// MV_H, DIST and FOVH from nx_meshview_wasm. If the pick maths had drifted from the renderer's convention 11// the exact-value teeth would fail, because they are the numbers that camera actually produces. 12// 13// ANTI-VACUITY, named where it lives, because most of these teeth are individually satisfiable by a stub: 14// * a picker that ALWAYS MISSES passes every miss tooth -- killed by the exact-barycentric positive 15// control, which pins u, v, w, t and the hit point to arithmetic worked out by hand. 16// * a picker that ALWAYS RETURNS 0 passes the positive control -- killed by the named-miss tooth. 17// * a picker that RETURNS THE FIRST HIT IN THE BUFFER passes a single depth test -- killed by running 18// the SAME two triangles in both buffer orders and requiring DIFFERENT answers (1 then 0). A 19// first-wins picker answers 0 twice, a last-wins picker answers 1 twice; only genuine depth sorting 20// answers 1 then 0. 21// * a degenerate-triangle guard that simply skips everything passes "degenerate is never picked" -- 22// killed by putting the degenerate FIRST and requiring the valid triangle behind it to be found WITH 23// its correct barycentrics. 24// * a gizmo that returns ZERO is undoable, replayable and axis-constrained -- killed by comparing its 25// magnitude against a closed form derived a DIFFERENT way (view half-width at the pivot plane), which 26// also kills a gizmo that returns the raw pixel delta. 27// * an edit stack that records nothing has a stable digest -- killed by requiring the digest to CHANGE 28// and by reading the moved vertex back component by component. 29// * a BENCH whose loop the compiler deleted times as FREE and reads as a very fast picker -- killed by 30// accumulating the picked index and requiring the sum to equal the repetition count times what a single 31// pick returns, a number no deleted loop can produce. 32// * a LINEARITY verdict taken at ONE triangle count is not a verdict at all, because a quadratic curve 33// and a linear one are the same number at a single N -- killed by four rungs spanning 64x, and by a 34// bar derived from the two hypotheses being separated rather than tuned until the result passed. 35// Every guard in the library is a gv_bite: it must fire on the bad input AND stay silent on the good one. 36// 37// THE SECOND HALF OF THE RUNG, ADDED 2026-08-25. A pick that is CORRECT is still not an interaction claim 38// until it carries a COST at a stated triangle count -- dcc.plan's unit line says exactly that, and the 39// rung's done-rule was half unmet while this file proved only correctness. Section 9 times vp_pick_screen 40// over procedurally generated grids at four triangle counts, publishes nanoseconds per operation with its 41// repetition count and clock source printed beside it so the arithmetic can be redone, checks that the cost 42// grows no worse than linearly across the range, and judges the result against the frame budget that 43// knowledge/gamefeel_oracle.conf ALREADY banks -- the same two cited rows nx_tissuebudget measures the 44// tissue tick against, reused rather than a second bar invented beside it. The figure is a NATIVE FLOOR and 45// says so: the shipped viewport is wasm in a visitor's browser, which is slower. 46// 47// 100% sovereign. No hardware writes (Rule 26). license_tier: ORIGINAL expect_exit: 0 48import "nx_syscalls.nx" 49import "nx_gate_verdict.nx" 50import "nx_vpick_lib.nx" 51import "nx_frame_budget.nx" 52import "nx_oracleband_lib.nx" 53 54const VG_I64: i64 = 8 55const VG_NV: i64 = 9 // 3 triangles worth of vertices: far, near, degenerate 56const VG_MESH_WORDS: i64 = 27 // VG_NV * 3 57const VG_NCELLS: i64 = 27 // vp_cells_needed(VG_NV) 58const VG_CAP: i64 = 8 // op capacity; no fixture here pushes more than one 59 60// ---- the fixture camera IS nx_meshview_wasm's camera ---- 61const VG_W: i64 = 64 // MV_W 62const VG_H: i64 = 64 // MV_H 63const VG_YAW0: i64 = 0 64const VG_YAW90: i64 = 90 65const VG_DIST: i64 = 65536 // DIST (4.0 in Q14) 66const VG_FOVH: i64 = 30 // FOVH (half field of view, degrees) 67const VG_FOV90: i64 = 90 // a half-fov with no forward-facing frustum: cot(90) is 0 68 69// ---- screen points. The centre is the exact centre of the viewport, so ndc is exactly zero there and 70// the positive control carries NO truncation at all -- every expected value below is exact arithmetic. 71const VG_CX: i64 = 32 // VG_W / 2 72const VG_CY: i64 = 32 // VG_H / 2 73const VG_DPX: i64 = 8 // the drag length used everywhere, in pixels 74const VG_BX_R: i64 = 40 // VG_CX + VG_DPX 75const VG_BX_L: i64 = 24 // VG_CX - VG_DPX 76const VG_BX_BIG: i64 = 48 // VG_CX + 2*VG_DPX 77const VG_BY_D: i64 = 40 // VG_CY + VG_DPX (screen y grows DOWNWARD) 78const VG_EDGE_X: i64 = 63 // VG_W - 1, the last legal column 79const VG_OFF_X: i64 = 64 // VG_W, the first illegal column 80 81// ---- derived comparison slack for the gizmo closed-form tooth ---- 82// The library and the gate reach the same number by different routes, and both truncate. Per ray: the 83// eye-space x loses at most 1 ulp, and the hit point multiplies it by t/Q = VG_DIST/RC_Q = 4, so at most 4; 84// the final hit division loses at most 1 more. Two rays therefore contribute at most 10. The gate's own 85// closed form divides twice, contributing at most 2 more. Total 12. The measured gap on this fixture is 86// PRINTED below so a reader sees the real figure rather than trusting the bound. 87const VG_SLACK: i64 = 12 88// AND THE MEASURED GAP, obtained by TIGHTENING the tooth above until it would have broken rather than by 89// asserting a number: the gate was rebuilt and re-run with this slack at 3 and stayed GREEN, so the real 90// disagreement between the library and the independently derived closed form is at most 3 parts in roughly 91// 9470 -- a quarter of the derived ceiling. This is a RATCHET, not a second opinion, and it must tighten if 92// the arithmetic ever improves. The derived bound above STAYS, because it is the honest ceiling for any 93// camera; this one speaks only for the shipped-camera fixture, and that is why both are asserted. 94const VG_SLACK_MEASURED: i64 = 3 95 96// ANTI-VACUITY FLOOR for the "this is not merely the pixel delta" tooth. The gizmo answer on this fixture 97// is expect_dx = 9472 against a pixel delta of VG_DPX = 8, a ratio of ~1184. The tooth asserts only 100x, 98// leaving an order of magnitude of headroom, so it cannot go vacuous if the camera fixture is retuned -- 99// while still refuting any implementation that hands back the raw pixel delta. A floor, deliberately not 100// the measured ratio: tightening this to ~1184 would make it a second (and duplicate) copy of the 101// closed-form ratchet above, which already pins the value exactly. 102const VG_NOT_PIXELS_MULT: i64 = 100 103 104// A small odd multiplier for the fixture CHANGE DETECTOR below. This is not a hash and is not claimed to 105// be one; it exists only so "the mesh buffer was not written" is one comparison instead of 27. 106const VG_SUM_MUL: i64 = 131 107 108const VG_BAD_AXIS: i64 = 3 // one past VP_AXIS_Z 109const VG_BAD_VERT: i64 = 9 // == VG_NV, the first out-of-range vertex index 110 111// TWO RECORDS THAT ARE BOTH THREE WIDE ARE STILL TWO RECORDS. A fixture vertex carries components and a 112// fixture triangle carries vertex indices; they are equal today by coincidence, not for a shared reason, so 113// they are named separately -- one constant serving two unrelated purposes can never be tuned for either. 114// The index-buffer sizings further down hand-wrote this same 3 a SECOND time, and that second copy is the 115// one that drifts: widen a triangle and the writer below still compiles while the allocation is short. 116const VG_VERT_C: i64 = 3 // i64 components per fixture vertex: x, y, z 117const VG_TRI_I: i64 = 3 // i64 vertex indices per fixture triangle: a, b, c 118const VG_IX_1TRI: i64 = VG_TRI_I // a one-triangle index buffer 119const VG_IX_2TRI: i64 = VG_TRI_I + VG_TRI_I // the two-triangle fixtures (near/far pairs) 120 121// ===================================================================================================== 122// THE MEASUREMENT HALF (section 9 of main). Constants first, with their provenance beside them. 123// ===================================================================================================== 124// THE CITED BUDGET COMES FROM THE TABLE, NOT FROM THIS FILE. knowledge/gamefeel_oracle.conf already carries 125// frame_budget_ms_90hz and js_block_ms, both sourced to vendor WebXR performance guidance, and 126// nx_tissuebudget already measures the tissue tick against exactly those two rows. Reusing that SAME source 127// rather than inventing a second one is the whole point: two organs pricing one frame against two different 128// bars is how an estate ends up unable to say which bar it missed. 129const VB_CONF: *u8 = "knowledge/gamefeel_oracle.conf\x00" 130const VB_FPS_VR: i64 = 90 // the rate frame_budget_ms_90hz is cited at; identical to TB_FPS_VR 131const VB_MS_US: i64 = 1000 // microseconds in a millisecond -- the conf states its rows in ms 132 133// THE TRIANGLE-COUNT LADDER. FOUR rungs spanning 64x, because ONE triangle count cannot see the shape of 134// the cost curve at all: at a single N a quadratic picker and a linear one are the same number, and a 135// number that cannot distinguish them is what makes a latency claim opinion. The grid resolutions DOUBLE, 136// and a grid's triangle count goes as the square of its resolution, so the triangle count QUADRUPLES at 137// every rung -- adjacent pairs 4x apart, the extremes 64x apart. Far enough that the derived 138// linear-versus-quadratic bar has real margin, small enough that the whole sweep finishes well inside the 139// gate runner's deadline. 140// THE TOP OF THE LADDER IS BOUNDED BY THE RUNNER'S DEADLINE, AND THAT BOUND IS DECLARED RATHER THAN 141// DISCOVERED. A first cut of this sweep ran g up to 64 (8192 triangles) and took roughly eight seconds 142// against the twelve-second deadline /api/gate_run allows -- on a box whose own repeat samples at that size 143// differed by 31 percent. A gate that intermittently outlives its caller's timeout is indistinguishable 144// from a gate that emits nothing, so the top rung is set where the whole sweep finishes in about a second 145// and a half. NOTHING IS LOST BY IT: the cost is measurably linear over the range that remains, so the 146// per-triangle figure this publishes predicts any larger mesh, and that prediction was CHECKED against the 147// removed rung before it was removed (predicted 1.87 ms at 8192 triangles, directly measured 1.97 ms). 148const VB_LADDER: i64 = 4 149const VB_G0: i64 = 4 150const VB_G1: i64 = 8 151const VB_G2: i64 = 16 152const VB_G3: i64 = 32 153const VB_GMAX: i64 = 32 // the largest rung; the buffers are sized from it and a tooth checks it 154// The quadrupling above, named once so the derived bar and the repeatability bar can both refer to it. 155// IT IS NOT TRUSTED: a fixture tooth checks it against what vp_bench_tris actually returns for each pair, 156// because a step written beside a ladder is a second copy of the ladder's shape and it is the copy that 157// drifts. 158const VB_STEP: i64 = 4 159 160// THE BENCH PICK POINT. Off centre in BOTH axes on purpose: the exact centre of the viewport lands on a 161// grid vertex when g is even and on a cell diagonal when g is odd, and a pick resolving on a shared edge is 162// a legitimate answer but a fragile fixture. These two put the world point near 0.65, 0.36 of the grid's 163// [-1,+1] span at every rung -- and the fixture tooth ASSERTS the hit rather than assuming it. 164const VB_PX: i64 = 41 165const VB_PY: i64 = 27 166 167// TWO RESOLUTION CONDITIONS, BOTH DERIVED FROM THE CLOCK, BOTH REQUIRED. 168// sys_clock_now_us is clock_gettime(CLOCK_MONOTONIC) with its nanoseconds divided by a thousand, so the 169// finest interval it can report is ONE MICROSECOND and a single pick is far below that. 170// VB_RESOLVE_US one microsecond of clock error over a run of a thousand microseconds is one part in a 171// thousand -- the identical derivation nx_tissuebudget banked for the tissue tick, reused 172// rather than re-invented. 173// VB_REPS_MIN the SAME one part in a thousand reached from the other side: with fewer than a thousand 174// repetitions a single anomalous operation moves the mean by more than the clock error we 175// just paid to eliminate. Both must hold before a figure is published. 176const VB_RESOLVE_US: i64 = 1000 177const VB_REPS_MIN: i64 = 1000 178const VB_REPS0: i64 = 1024 // first attempt, the smallest power of two at or above VB_REPS_MIN 179const VB_REPS_CAP: i64 = 4194304 // refuse to spin forever: ANNOUNCED, never silent (same shape as TB_TICKS_CAP) 180// TWO timing samples of each configuration. Two is not an average -- it is the smallest number that can 181// DETECT disagreement at all, and detecting it is the job: a figure whose own repeat disagrees by more than 182// the bar it is about to be judged against cannot support that judgement. The published elapsed is the 183// MINIMUM of the two, because contention on a shared box can only ever ADD time to a sample and never 184// remove it, which is also why the figure is declared a FLOOR. 185const VB_SAMPLES: i64 = 2 186 187func vg_eq(a: i64, b: i64) -> i64 { if a == b { return 1 } return 0 } 188func vg_ne(a: i64, b: i64) -> i64 { if a != b { return 1 } return 0 } 189func vg_gt(a: i64, b: i64) -> i64 { if a > b { return 1 } return 0 } 190func vg_lt(a: i64, b: i64) -> i64 { if a < b { return 1 } return 0 } 191func vg_near(a: i64, b: i64, slack: i64) -> i64 { 192 var d: i64 = a - b 193 if d < 0 { d = 0 - d } 194 if d <= slack { return 1 } 195 return 0 196} 197func vg_absd(a: i64, b: i64) -> i64 { 198 var d: i64 = a - b 199 if d < 0 { d = 0 - d } 200 return d 201} 202func vg_setv(m: *i64, k: i64, x: i64, y: i64, z: i64) -> i64 { m[k*VG_VERT_C+VP_AXIS_X]=x; m[k*VG_VERT_C+VP_AXIS_Y]=y; m[k*VG_VERT_C+VP_AXIS_Z]=z; return 0 } 203func vg_seti(p: *i64, k: i64, a: i64, b: i64, c: i64) -> i64 { p[k*VG_TRI_I+0]=a; p[k*VG_TRI_I+1]=b; p[k*VG_TRI_I+2]=c; return 0 } 204func vg_setray(r: *i64, ox: i64, oy: i64, oz: i64, dx: i64, dy: i64, dz: i64) -> i64 { 205 r[VP_RAY_OX]=ox; r[VP_RAY_OY]=oy; r[VP_RAY_OZ]=oz 206 r[VP_RAY_DX]=dx; r[VP_RAY_DY]=dy; r[VP_RAY_DZ]=dz 207 return 0 208} 209func vg_setcam(c: *i64, w: i64, h: i64, yaw: i64, dist: i64, fovh: i64) -> i64 { 210 c[VP_CAM_W]=w; c[VP_CAM_H]=h; c[VP_CAM_YAW]=yaw; c[VP_CAM_DIST]=dist; c[VP_CAM_FOVH]=fovh 211 c[VP_CAM_PX]=0; c[VP_CAM_PY]=0; c[VP_CAM_PZ]=0 212 return 0 213} 214func vg_setdrag(d: *i64, ax: i64, ay: i64, bx: i64, by: i64) -> i64 { 215 d[VP_DRAG_AX]=ax; d[VP_DRAG_AY]=ay; d[VP_DRAG_BX]=bx; d[VP_DRAG_BY]=by 216 return 0 217} 218func vg_sum(p: *i64, n: i64) -> i64 { 219 var h: i64 = 0 220 var i: i64 = 0 221 while i < n { h = h * VG_SUM_MUL + p[i] + i + 1; i = i + 1 } 222 return h 223} 224// PRINT THE VALUES, NOT JUST PASS OR FAIL. A tooth that reports a boolean cannot say why it failed. 225func vg_show(label: *u8, v: i64) -> i64 { 226 gv_puts(" " as *u8) 227 gv_puts(label) 228 gv_puts("=" as *u8) 229 gv_num(v) 230 gv_puts("\n" as *u8) 231 return 0 232} 233 234// ONE RESOLVED MEASUREMENT AT ONE TRIANGLE COUNT. Takes VB_SAMPLES timed runs at a repetition count and 235// doubles that count until the runs satisfy BOTH derived conditions above. 236// THE SAMPLES ARE THEIR OWN RESOLUTION PROBE, deliberately: an earlier shape ran a separate discarded probe 237// at the full repetition count, which cost a THIRD of the whole sweep's runtime to learn something the 238// samples themselves already report. The warm-up it was also serving is not lost -- the caller takes a 239// single vp_pick_screen on this exact mesh immediately before calling here, which touches every vertex and 240// index the timed loop will touch. 241// out[0]=reps out[1]=min elapsed us out[2]=max elapsed us out[3]=the accumulator the last sample returned 242// THE PUBLISHED ELAPSED IS THE MINIMUM, and the maximum is handed back beside it rather than discarded: 243// contention on a shared box can only ever ADD time to a sample, never remove it, so the minimum is the best 244// available estimate of the uncontended cost -- and printing the spread is what lets a reader see how noisy 245// the box was rather than having to trust that it was quiet. 246// Returns 1 = resolved, 0 = reached the ANNOUNCED cap without resolving, which ABSTAINS rather than 247// dividing an unresolved elapsed into a fabricated per-operation cost of zero. 248func vb_measure(cam: *i64, mesh: *i64, nverts: i64, idx: *i64, ntris: i64, scratch: *i64, pout: *i64, out: *i64) -> i64 { 249 var reps: i64 = VB_REPS0 250 while reps <= VB_REPS_CAP { 251 var lo: i64 = 0 252 var hi: i64 = 0 253 var acc: i64 = 0 254 var s: i64 = 0 255 while s < VB_SAMPLES { 256 let a0: i64 = sys_clock_now_us() 257 acc = vp_bench_run(cam, VB_PX, VB_PY, mesh, nverts, idx, ntris, scratch, pout, reps) 258 let a1: i64 = sys_clock_now_us() 259 let e: i64 = a1 - a0 260 if s == 0 { lo = e; hi = e } 261 if e < lo { lo = e } 262 if e > hi { hi = e } 263 s = s + 1 264 } 265 var ok: i64 = 1 266 if lo < VB_RESOLVE_US { ok = 0 } 267 if reps < VB_REPS_MIN { ok = 0 } 268 if ok == 1 { 269 out[0] = reps 270 out[1] = lo 271 out[2] = hi 272 out[3] = acc 273 return 1 274 } 275 reps = reps * 2 276 } 277 return 0 278} 279 280func main(argc: i64, argv: *i64) -> i64 { 281 let ctr: *i64 = gv_ctr() 282 gv_head("nx_vpick_gate -- ray-pick and transform gizmo for the sovereign viewport: a click resolves to a triangle, a drag resolves to an undoable operation" as *u8) 283 284 // --------------------------------------------------------------------------------------------- 285 // FIXTURE. Two congruent triangles at different depths plus one deliberately degenerate triangle. 286 // verts 0,1,2 FAR triangle in the z = 0 plane 287 // verts 3,4,5 NEAR the same triangle pushed to z = +1.0 (nearer the orbit camera, which sits at +z) 288 // verts 6,7,8 DEGENERATE: three collinear points along +x, so the cross product is exactly zero 289 // --------------------------------------------------------------------------------------------- 290 let mesh: *i64 = sys_mmap(VG_MESH_WORDS * VG_I64) as *i64 291 vg_setv(mesh, 0, 0 - RC_Q, 0 - RC_Q, 0) 292 vg_setv(mesh, 1, RC_Q, 0 - RC_Q, 0) 293 vg_setv(mesh, 2, 0, RC_Q, 0) 294 vg_setv(mesh, 3, 0 - RC_Q, 0 - RC_Q, RC_Q) 295 vg_setv(mesh, 4, RC_Q, 0 - RC_Q, RC_Q) 296 vg_setv(mesh, 5, 0, RC_Q, RC_Q) 297 vg_setv(mesh, 6, 0, 0, 0) 298 vg_setv(mesh, 7, RC_Q, 0, 0) 299 vg_setv(mesh, 8, RC_Q * 2, 0, 0) 300 let mesh_sum_before: i64 = vg_sum(mesh, VG_MESH_WORDS) 301 302 let ix_one: *i64 = sys_mmap(VG_IX_1TRI * VG_I64) as *i64 303 let ix_farnear: *i64 = sys_mmap(VG_IX_2TRI * VG_I64) as *i64 304 let ix_nearfar: *i64 = sys_mmap(VG_IX_2TRI * VG_I64) as *i64 305 let ix_degvalid: *i64 = sys_mmap(VG_IX_2TRI * VG_I64) as *i64 306 let ix_badvert: *i64 = sys_mmap(VG_IX_1TRI * VG_I64) as *i64 307 vg_seti(ix_one, 0, 0, 1, 2) 308 vg_seti(ix_farnear, 0, 0, 1, 2) 309 vg_seti(ix_farnear, 1, 3, 4, 5) 310 vg_seti(ix_nearfar, 0, 3, 4, 5) 311 vg_seti(ix_nearfar, 1, 0, 1, 2) 312 vg_seti(ix_degvalid, 0, 6, 7, 8) 313 vg_seti(ix_degvalid, 1, 0, 1, 2) 314 vg_seti(ix_badvert, 0, 0, 1, VG_BAD_VERT) 315 316 let cam: *i64 = sys_mmap(VP_CAM_WORDS * VG_I64) as *i64 317 let cam90: *i64 = sys_mmap(VP_CAM_WORDS * VG_I64) as *i64 318 let cambadw: *i64 = sys_mmap(VP_CAM_WORDS * VG_I64) as *i64 319 let cambadf: *i64 = sys_mmap(VP_CAM_WORDS * VG_I64) as *i64 320 vg_setcam(cam, VG_W, VG_H, VG_YAW0, VG_DIST, VG_FOVH) 321 vg_setcam(cam90, VG_W, VG_H, VG_YAW90, VG_DIST, VG_FOVH) 322 vg_setcam(cambadw, 0, VG_H, VG_YAW0, VG_DIST, VG_FOVH) 323 vg_setcam(cambadf, VG_W, VG_H, VG_YAW0, VG_DIST, VG_FOV90) 324 325 let scratch: *i64 = sys_mmap(VP_SCRATCH_WORDS * VG_I64) as *i64 326 let ray: *i64 = sys_mmap(VP_RAY_WORDS * VG_I64) as *i64 327 let hray: *i64 = sys_mmap(VP_RAY_WORDS * VG_I64) as *i64 328 let pout: *i64 = sys_mmap(VP_R_WORDS * VG_I64) as *i64 329 let pout2: *i64 = sys_mmap(VP_R_WORDS * VG_I64) as *i64 330 let gout: *i64 = sys_mmap(VP_G_WORDS * VG_I64) as *i64 331 332 // ============================================================================================= 333 // 1. SCREEN POINT -> RAY, in the renderer's convention. Exact integers, no slack. 334 // ============================================================================================= 335 let rc_centre: i64 = vp_ray_from_screen(cam, VG_CX, VG_CY, ray, 0) 336 gv_check("fixture-centre-ray-was-built" as *u8, vg_eq(rc_centre, VP_OK), ctr) 337 vg_show("ray_ox" as *u8, ray[VP_RAY_OX]) 338 vg_show("ray_oz" as *u8, ray[VP_RAY_OZ]) 339 vg_show("ray_dz" as *u8, ray[VP_RAY_DZ]) 340 // The orbit camera sits at +z looking down -z, exactly as mv_render_loaded_cam places it by translating 341 // the model to (0,0,-dist). 342 gv_check("ray-origin-is-the-orbit-camera-position" as *u8, vg_eq(ray[VP_RAY_OZ], VG_DIST), ctr) 343 gv_check("ray-origin-is-on-the-z-axis-at-yaw-zero" as *u8, vg_eq(ray[VP_RAY_OX], 0), ctr) 344 gv_check("centre-pixel-looks-straight-down-negative-z" as *u8, vg_eq(ray[VP_RAY_DZ], 0 - RC_Q), ctr) 345 gv_check("centre-pixel-has-no-lateral-component" as *u8, vg_eq(ray[VP_RAY_DX], 0), ctr) 346 gv_check("centre-pixel-has-no-vertical-component" as *u8, vg_eq(ray[VP_RAY_DY], 0), ctr) 347 348 // Orbiting a quarter turn must MOVE THE CAMERA, not the picture. At yaw 90 the eye is on -x looking 349 // toward +x. Both numbers are exact because rc_sin_q14(90) is exactly RC_Q. 350 let rc_y90: i64 = vp_ray_from_screen(cam90, VG_CX, VG_CY, ray, 0) 351 gv_check("fixture-yaw-90-ray-was-built" as *u8, vg_eq(rc_y90, VP_OK), ctr) 352 gv_check("orbiting-90-degrees-moves-the-eye-onto-the-negative-x-axis" as *u8, vg_eq(ray[VP_RAY_OX], 0 - VG_DIST), ctr) 353 gv_check("orbiting-90-degrees-points-the-view-along-positive-x" as *u8, vg_eq(ray[VP_RAY_DX], RC_Q), ctr) 354 gv_check("orbiting-90-degrees-leaves-no-z-component" as *u8, vg_eq(ray[VP_RAY_DZ], 0), ctr) 355 356 vp_ray_from_screen(cam, VG_BX_R, VG_CY, ray, 0) 357 gv_check("a-pixel-right-of-centre-tilts-the-ray-right" as *u8, vg_gt(ray[VP_RAY_DX], 0), ctr) 358 vp_ray_from_screen(cam, VG_CX, VG_BY_D, ray, 0) 359 // mv_proj1 maps ndc y through (Q - clipy) so screen y grows DOWNWARD; the inverse must invert it back, 360 // and getting this backwards is the classic viewport bug that makes a gizmo drag the wrong way. 361 gv_check("screen-y-grows-downward-so-a-lower-pixel-tilts-the-ray-down" as *u8, vg_lt(ray[VP_RAY_DY], 0), ctr) 362 363 let scr_bad: i64 = vp_ray_from_screen(cam, VG_OFF_X, VG_CY, ray, 0) 364 let scr_good: i64 = vp_ray_from_screen(cam, VG_EDGE_X, VG_CY, ray, 0) 365 gv_bite("neg-control-screen-point-outside-the-viewport" as *u8, vg_eq(scr_bad, VP_E_SCREEN), vg_eq(scr_good, VP_E_SCREEN), ctr) 366 let scr_neg: i64 = vp_ray_from_screen(cam, 0 - 1, VG_CY, ray, 0) 367 gv_check("negative-screen-coordinate-is-refused-by-the-same-name" as *u8, vg_eq(scr_neg, VP_E_SCREEN), ctr) 368 369 let vp_bad: i64 = vp_ray_from_screen(cambadw, VG_CX, VG_CY, ray, 0) 370 let vp_good: i64 = vp_ray_from_screen(cam, VG_CX, VG_CY, ray, 0) 371 gv_bite("neg-control-viewport-dimension-invalid" as *u8, vg_eq(vp_bad, VP_E_VIEWPORT), vg_eq(vp_good, VP_E_VIEWPORT), ctr) 372 373 // A half-fov of 90 has no forward-facing frustum. rc_perspective_cs falls back to the identity there; 374 // a picker must refuse instead, or it answers confidently about a picture nobody is looking at. 375 let fov_bad: i64 = vp_ray_from_screen(cambadf, VG_CX, VG_CY, ray, 0) 376 let fov_good: i64 = vp_ray_from_screen(cam, VG_CX, VG_CY, ray, 0) 377 gv_bite("neg-control-half-fov-with-no-forward-frustum" as *u8, vg_eq(fov_bad, VP_E_FOV), vg_eq(fov_good, VP_E_FOV), ctr) 378 379 // ============================================================================================= 380 // 2. THE POSITIVE CONTROL: a ray through the known centre picks THAT triangle, with exact weights. 381 // The world origin lies at v0 + (1/4)*e1 + (1/2)*e2 for this triangle, so u, v, w are exactly 382 // Q/4, Q/2, Q/4 and t is exactly the orbit distance. Worked out by hand before the code was written. 383 // ============================================================================================= 384 vp_ray_from_screen(cam, VG_CX, VG_CY, ray, 0) 385 let deg_cross_ok: i64 = vp_tri_hit(mesh, VG_NV, 0, 1, 2, ray, 0, pout) 386 gv_check("fixture-the-target-triangle-is-not-degenerate" as *u8, vg_eq(deg_cross_ok, VP_HIT), ctr) 387 388 let pick0: i64 = vp_pick(mesh, VG_NV, ix_one, 1, ray, 0, pout) 389 vg_show("picked_triangle" as *u8, pick0) 390 vg_show("u_q14" as *u8, pout[VP_R_U]) 391 vg_show("v_q14" as *u8, pout[VP_R_V]) 392 vg_show("w_q14" as *u8, pout[VP_R_W]) 393 vg_show("t_q14" as *u8, pout[VP_R_T]) 394 gv_check("a-ray-through-the-known-centre-picks-that-triangle" as *u8, vg_eq(pick0, 0), ctr) 395 gv_check("barycentric-u-is-exactly-one-quarter" as *u8, vg_eq(pout[VP_R_U], RC_Q / 4), ctr) 396 gv_check("barycentric-v-is-exactly-one-half" as *u8, vg_eq(pout[VP_R_V], RC_Q / 2), ctr) 397 gv_check("barycentric-w-is-exactly-one-quarter" as *u8, vg_eq(pout[VP_R_W], RC_Q / 4), ctr) 398 gv_check("the-three-barycentrics-sum-to-one" as *u8, vg_eq(pout[VP_R_U] + pout[VP_R_V] + pout[VP_R_W], RC_Q), ctr) 399 gv_check("the-ray-parameter-is-exactly-the-orbit-distance" as *u8, vg_eq(pout[VP_R_T], VG_DIST), ctr) 400 gv_check("the-hit-point-is-the-known-world-origin-x" as *u8, vg_eq(pout[VP_R_HX], 0), ctr) 401 gv_check("the-hit-point-is-the-known-world-origin-y" as *u8, vg_eq(pout[VP_R_HY], 0), ctr) 402 gv_check("the-hit-point-is-the-known-world-origin-z" as *u8, vg_eq(pout[VP_R_HZ], 0), ctr) 403 // The library derives the hit point from the barycentrics. Re-deriving it the OTHER way, from 404 // origin + t*dir, is an independent check that the two formulations agree. 405 let alt_x: i64 = ray[VP_RAY_OX] + (pout[VP_R_T] * ray[VP_RAY_DX]) / RC_Q 406 let alt_z: i64 = ray[VP_RAY_OZ] + (pout[VP_R_T] * ray[VP_RAY_DZ]) / RC_Q 407 gv_check("barycentric-hit-point-agrees-with-origin-plus-t-times-direction" as *u8, vg_eq(alt_x, pout[VP_R_HX]), ctr) 408 gv_check("barycentric-hit-depth-agrees-with-origin-plus-t-times-direction" as *u8, vg_eq(alt_z, pout[VP_R_HZ]), ctr) 409 410 // vp_pick_screen must be the composition of the two, not a second implementation of either. 411 let picks: i64 = vp_pick_screen(cam, VG_CX, VG_CY, mesh, VG_NV, ix_one, 1, scratch, pout2) 412 gv_check("pick-screen-composes-ray-construction-and-pick" as *u8, vg_eq(picks, pick0), ctr) 413 gv_check("pick-screen-reproduces-the-same-ray-parameter" as *u8, vg_eq(pout2[VP_R_T], pout[VP_R_T]), ctr) 414 415 // ============================================================================================= 416 // 3. THE MISS IS NAMED, AND IT IS NOT TRIANGLE ZERO. 417 // ============================================================================================= 418 let rc_corner: i64 = vp_ray_from_screen(cam, 0, 0, ray, 0) 419 gv_check("fixture-corner-ray-was-built" as *u8, vg_eq(rc_corner, VP_OK), ctr) 420 let miss: i64 = vp_pick(mesh, VG_NV, ix_one, 1, ray, 0, pout) 421 vg_show("miss_code" as *u8, miss) 422 gv_check("a-ray-that-misses-returns-the-named-miss" as *u8, vg_eq(miss, VP_MISS_NONE), ctr) 423 gv_check("the-named-miss-is-not-triangle-zero" as *u8, vg_ne(VP_MISS_NONE, 0), ctr) 424 gv_check("the-named-miss-is-negative-so-no-valid-index-can-collide" as *u8, vg_lt(VP_MISS_NONE, 0), ctr) 425 gv_check("a-missing-ray-still-reports-outside-not-behind" as *u8, vg_eq(vp_tri_hit_at(mesh, VG_NV, ix_one, 1, 0, ray, 0, pout), VP_NOHIT_OUTSIDE), ctr) 426 427 // ============================================================================================= 428 // 4. DEPTH SORTING, PROVEN INDEPENDENT OF BUFFER ORDER. 429 // ============================================================================================= 430 vp_ray_from_screen(cam, VG_CX, VG_CY, ray, 0) 431 let pick_fn: i64 = vp_pick(mesh, VG_NV, ix_farnear, 2, ray, 0, pout) 432 let t_fn: i64 = pout[VP_R_T] 433 let pick_nf: i64 = vp_pick(mesh, VG_NV, ix_nearfar, 2, ray, 0, pout2) 434 let t_nf: i64 = pout2[VP_R_T] 435 vg_show("pick_far_first" as *u8, pick_fn) 436 vg_show("pick_near_first" as *u8, pick_nf) 437 vg_show("t_of_winner" as *u8, t_fn) 438 gv_check("fixture-the-near-triangle-is-genuinely-nearer" as *u8, vg_eq(t_fn, VG_DIST - RC_Q), ctr) 439 gv_check("the-nearer-triangle-wins-when-it-is-second-in-the-buffer" as *u8, vg_eq(pick_fn, 1), ctr) 440 gv_check("the-nearer-triangle-still-wins-when-the-order-is-reversed" as *u8, vg_eq(pick_nf, 0), ctr) 441 // THE ANTI-VACUITY TOOTH FOR DEPTH: a first-wins picker answers 0 both times and a last-wins picker 442 // answers 1 both times. Only genuine depth sorting produces two DIFFERENT indices for the same pair. 443 gv_check("the-two-buffer-orders-give-different-indices-so-order-is-not-the-answer" as *u8, vg_ne(pick_fn, pick_nf), ctr) 444 gv_check("both-orders-agree-on-the-winning-depth" as *u8, vg_eq(t_fn, t_nf), ctr) 445 446 // ============================================================================================= 447 // 5. DEGENERATE TRIANGLES ARE CLASSIFIED, SKIPPED, AND DO NOT HIDE WHAT IS BEHIND THEM. 448 // ============================================================================================= 449 let degc: i64 = vp_tri_hit_at(mesh, VG_NV, ix_degvalid, 2, 0, ray, 0, pout) 450 gv_check("a-zero-area-triangle-is-classified-degenerate" as *u8, vg_eq(degc, VP_NOHIT_DEGENERATE), ctr) 451 gv_check("degenerate-is-a-distinct-answer-from-parallel" as *u8, vg_ne(VP_NOHIT_DEGENERATE, VP_NOHIT_PARALLEL), ctr) 452 let pick_dv: i64 = vp_pick(mesh, VG_NV, ix_degvalid, 2, ray, 0, pout) 453 gv_check("a-degenerate-triangle-is-never-reported-as-the-hit" as *u8, vg_ne(pick_dv, 0), ctr) 454 // ANTI-VACUITY: a guard that simply skipped everything would also never report the degenerate one. 455 // The valid triangle SITTING BEHIND IT IN THE BUFFER must still be found, with its exact weights. 456 gv_check("skipping-the-degenerate-still-finds-the-valid-triangle-behind-it" as *u8, vg_eq(pick_dv, 1), ctr) 457 gv_check("the-triangle-found-past-the-degenerate-has-the-exact-weights" as *u8, vg_eq(pout[VP_R_U], RC_Q / 4), ctr) 458 459 // A ray lying IN the plane of a non-degenerate triangle is parallel, and must say parallel. 460 vg_setray(hray, 0 - RC_Q * 2, 0, 0, RC_Q, 0, 0) 461 let par: i64 = vp_tri_hit_at(mesh, VG_NV, ix_one, 1, 0, hray, 0, pout) 462 gv_check("a-ray-in-the-triangle-plane-is-classified-parallel" as *u8, vg_eq(par, VP_NOHIT_PARALLEL), ctr) 463 464 // A triangle BEHIND the ray origin is not a hit. This is the false-positive that makes a viewport 465 // select things behind the camera. 466 vg_setray(hray, 0, 0, VG_DIST, 0, 0, RC_Q) 467 let beh: i64 = vp_tri_hit_at(mesh, VG_NV, ix_one, 1, 0, hray, 0, pout) 468 gv_check("a-triangle-behind-the-origin-is-classified-behind" as *u8, vg_eq(beh, VP_NOHIT_BEHIND), ctr) 469 gv_check("a-triangle-behind-the-origin-is-not-picked" as *u8, vg_eq(vp_pick(mesh, VG_NV, ix_one, 1, hray, 0, pout), VP_MISS_NONE), ctr) 470 471 // ============================================================================================= 472 // 6. THE REMAINING GUARDS, EACH BITE-PROVEN. 473 // ============================================================================================= 474 vp_ray_from_screen(cam, VG_CX, VG_CY, ray, 0) 475 let tix_bad: i64 = vp_tri_hit_at(mesh, VG_NV, ix_one, 1, 1, ray, 0, pout) 476 let tix_good: i64 = vp_tri_hit_at(mesh, VG_NV, ix_one, 1, 0, ray, 0, pout) 477 gv_bite("neg-control-triangle-index-out-of-range" as *u8, vg_eq(tix_bad, VP_E_TRI_IDX), vg_eq(tix_good, VP_E_TRI_IDX), ctr) 478 479 let vix_bad: i64 = vp_pick(mesh, VG_NV, ix_badvert, 1, ray, 0, pout) 480 let vix_good: i64 = vp_pick(mesh, VG_NV, ix_one, 1, ray, 0, pout) 481 gv_bite("neg-control-vertex-index-out-of-range-inside-the-index-buffer" as *u8, vg_eq(vix_bad, VP_E_TRI_IDX), vg_eq(vix_good, VP_E_TRI_IDX), ctr) 482 gv_check("a-corrupt-index-buffer-refuses-rather-than-quietly-reporting-a-miss" as *u8, vg_ne(vix_bad, VP_MISS_NONE), ctr) 483 484 vg_setray(hray, 0, 0, VG_DIST, 0, 0, 0) 485 let zd_bad: i64 = vp_pick(mesh, VG_NV, ix_one, 1, hray, 0, pout) 486 let zd_good: i64 = vp_pick(mesh, VG_NV, ix_one, 1, ray, 0, pout) 487 gv_bite("neg-control-zero-length-ray-direction" as *u8, vg_eq(zd_bad, VP_E_ZERO_DIR), vg_eq(zd_good, VP_E_ZERO_DIR), ctr) 488 // A zero direction would make every triangle look parallel, so the picker would answer a confident 489 // MISS. That is a wrong answer wearing a right-looking one, which is why it refuses by name instead. 490 gv_check("a-zero-direction-refuses-rather-than-reporting-a-plausible-miss" as *u8, vg_ne(zd_bad, VP_MISS_NONE), ctr) 491 492 vg_setray(hray, VP_ORIGIN_MAX + 1, 0, 0, 0, 0, 0 - RC_Q) 493 let bd_bad: i64 = vp_pick(mesh, VG_NV, ix_one, 1, hray, 0, pout) 494 let bd_good: i64 = vp_pick(mesh, VG_NV, ix_one, 1, ray, 0, pout) 495 gv_bite("neg-control-coordinate-outside-the-derived-overflow-bound" as *u8, vg_eq(bd_bad, VP_E_BOUND), vg_eq(bd_good, VP_E_BOUND), ctr) 496 497 gv_check("every-refusal-carries-a-distinct-name" as *u8, vg_ne(vp_code_name(VP_E_BOUND) as i64, vp_code_name(VP_E_TRI_IDX) as i64), ctr) 498 // The pick codes start at -16 precisely so a propagated nx_editstack refusal (-1 to -9) can never be 499 // mistaken for one of these, and vice versa. 500 gv_check("pick-and-edit-stack-refusal-code-spaces-do-not-overlap" as *u8, vg_lt(VP_MISS_NONE, ES_E_ARGS), ctr) 501 502 gv_check("picking-never-wrote-a-single-word-of-the-mesh-buffer" as *u8, vg_eq(vg_sum(mesh, VG_MESH_WORDS), mesh_sum_before), ctr) 503 504 // ============================================================================================= 505 // 7. THE GIZMO. It has no mesh pointer at all, so it is structurally incapable of moving a vertex. 506 // ============================================================================================= 507 let drag_r: *i64 = sys_mmap(VP_DRAG_WORDS * VG_I64) as *i64 508 let drag_l: *i64 = sys_mmap(VP_DRAG_WORDS * VG_I64) as *i64 509 let drag_b: *i64 = sys_mmap(VP_DRAG_WORDS * VG_I64) as *i64 510 let drag_d: *i64 = sys_mmap(VP_DRAG_WORDS * VG_I64) as *i64 511 let drag_0: *i64 = sys_mmap(VP_DRAG_WORDS * VG_I64) as *i64 512 vg_setdrag(drag_r, VG_CX, VG_CY, VG_BX_R, VG_CY) 513 vg_setdrag(drag_l, VG_CX, VG_CY, VG_BX_L, VG_CY) 514 vg_setdrag(drag_b, VG_CX, VG_CY, VG_BX_BIG, VG_CY) 515 vg_setdrag(drag_d, VG_CX, VG_CY, VG_CX, VG_BY_D) 516 vg_setdrag(drag_0, VG_CX, VG_CY, VG_CX, VG_CY) 517 518 let gr: i64 = vp_gizmo_delta(cam, drag_r, VP_AXIS_X, scratch, gout) 519 let dq_right: i64 = gout[VP_G_SCALAR] 520 vg_show("gizmo_dx_right" as *u8, dq_right) 521 gv_check("fixture-gizmo-accepted-the-drag" as *u8, vg_eq(gr, VP_OK), ctr) 522 gv_check("dragging-right-along-x-gives-a-positive-x-delta" as *u8, vg_gt(dq_right, 0), ctr) 523 gv_check("an-x-constrained-drag-leaves-y-exactly-zero" as *u8, vg_eq(gout[VP_G_DY], 0), ctr) 524 gv_check("an-x-constrained-drag-leaves-z-exactly-zero" as *u8, vg_eq(gout[VP_G_DZ], 0), ctr) 525 gv_check("the-scalar-equals-the-one-nonzero-component" as *u8, vg_eq(gout[VP_G_DX], dq_right), ctr) 526 527 // THE INDEPENDENT CLOSED FORM, reached a different way: at the pivot plane the visible half-width is 528 // dist*aspect/cot(fovh), and a drag of n pixels covers 2n/w of that half-width. No ray, no plane 529 // intersection, no barycentrics -- so agreeing with it is evidence, not tautology. This is also what 530 // kills a gizmo that simply returns the pixel delta: the expected answer here is three orders of 531 // magnitude larger than 8. 532 let asp: i64 = (VG_W * RC_Q) / VG_H 533 let fq: i64 = (rc_cos_q14(VG_FOVH) * RC_Q) / rc_sin_q14(VG_FOVH) 534 let halfw: i64 = (VG_DIST * asp) / fq 535 let expect_dx: i64 = (halfw * 2 * VG_DPX) / VG_W 536 vg_show("closed_form_expected" as *u8, expect_dx) 537 vg_show("measured_minus_expected" as *u8, vg_absd(dq_right, expect_dx)) 538 vg_show("derived_slack" as *u8, VG_SLACK) 539 gv_check("gizmo-magnitude-matches-an-independently-derived-closed-form" as *u8, vg_near(dq_right, expect_dx, VG_SLACK), ctr) 540 gv_check("gizmo-closed-form-gap-holds-at-the-measured-ratchet-not-just-the-derived-ceiling" as *u8, vg_near(dq_right, expect_dx, VG_SLACK_MEASURED), ctr) 541 gv_check("gizmo-magnitude-is-not-merely-the-pixel-delta" as *u8, vg_gt(dq_right, VG_DPX * VG_NOT_PIXELS_MULT), ctr) 542 543 vp_gizmo_delta(cam, drag_l, VP_AXIS_X, scratch, gout) 544 let dq_left: i64 = gout[VP_G_SCALAR] 545 vg_show("gizmo_dx_left" as *u8, dq_left) 546 gv_check("dragging-left-gives-the-opposite-sign" as *u8, vg_lt(dq_left, 0), ctr) 547 // Magnitude equality is asserted within the derived slack rather than exactly, because the direction 548 // of integer-division rounding on a negative operand is not a property this gate should pin down. 549 gv_check("dragging-left-gives-the-same-magnitude-as-dragging-right" as *u8, vg_near(vg_absd(dq_left, 0), vg_absd(dq_right, 0), VG_SLACK), ctr) 550 551 vp_gizmo_delta(cam, drag_b, VP_AXIS_X, scratch, gout) 552 gv_check("a-longer-drag-gives-a-strictly-larger-delta" as *u8, vg_gt(gout[VP_G_SCALAR], dq_right), ctr) 553 554 vp_gizmo_delta(cam, drag_d, VP_AXIS_Y, scratch, gout) 555 vg_show("gizmo_dy_down" as *u8, gout[VP_G_DY]) 556 gv_check("a-y-constrained-drag-moves-only-y" as *u8, vg_eq(gout[VP_G_DX], 0), ctr) 557 gv_check("a-y-constrained-drag-leaves-z-exactly-zero" as *u8, vg_eq(gout[VP_G_DZ], 0), ctr) 558 gv_check("dragging-down-the-screen-moves-the-world-point-down" as *u8, vg_lt(gout[VP_G_DY], 0), ctr) 559 560 vp_gizmo_delta(cam, drag_0, VP_AXIS_X, scratch, gout) 561 gv_check("a-zero-length-drag-produces-exactly-zero-delta" as *u8, vg_eq(gout[VP_G_SCALAR], 0), ctr) 562 563 let ax_bad: i64 = vp_gizmo_delta(cam, drag_r, VG_BAD_AXIS, scratch, gout) 564 let ax_good: i64 = vp_gizmo_delta(cam, drag_r, VP_AXIS_Z, scratch, gout) 565 gv_bite("neg-control-unknown-gizmo-axis" as *u8, vg_eq(ax_bad, VP_E_AXIS), vg_eq(ax_good, VP_E_AXIS), ctr) 566 567 gv_check("the-gizmo-never-wrote-a-single-word-of-the-mesh-buffer" as *u8, vg_eq(vg_sum(mesh, VG_MESH_WORDS), mesh_sum_before), ctr) 568 569 // ============================================================================================= 570 // 8. THE POINT OF THE RUNG: the drag becomes ONE operation on the stack, and it is undoable. 571 // ============================================================================================= 572 let st: *i64 = es_new(VG_CAP, VG_NCELLS) 573 gv_check("fixture-stack-allocated" as *u8, vg_ne(st as i64, 0), ctr) 574 gv_check("fixture-stack-seeded-from-the-mesh" as *u8, vg_eq(vp_seed_stack(st, mesh, VG_NV), VP_OK), ctr) 575 gv_check("seeding-preserved-vertex-0-x" as *u8, vg_eq(vp_vert_component(st, 0, VP_AXIS_X, VG_NV), 0 - RC_Q), ctr) 576 gv_check("seeding-preserved-vertex-3-z" as *u8, vg_eq(vp_vert_component(st, 3, VP_AXIS_Z, VG_NV), RC_Q), ctr) 577 gv_check("fixture-stack-starts-with-an-empty-log" as *u8, vg_eq(es_count(st), 0), ctr) 578 579 let d_before: i64 = es_digest(st) 580 let ad: i64 = vp_apply_drag(st, cam, drag_r, VP_AXIS_X, 0, 1, VG_NV, scratch, gout) 581 let dq: i64 = gout[VP_G_SCALAR] 582 let d_after: i64 = es_digest(st) 583 gv_check("the-drag-was-accepted" as *u8, vg_eq(ad, VP_OK), ctr) 584 gv_check("a-drag-pushes-exactly-one-operation" as *u8, vg_eq(es_count(st), 1), ctr) 585 gv_check("a-single-vertex-drag-records-a-cell-add" as *u8, vg_eq(es_op_field(st, 0, ES_F_KIND), ES_OP_ADD), ctr) 586 gv_check("the-recorded-operation-targets-the-selected-vertex-cell" as *u8, vg_eq(es_op_field(st, 0, ES_F_A0), vp_cell(VP_AXIS_X, 0, VG_NV)), ctr) 587 gv_check("the-recorded-operation-carries-the-gizmo-delta" as *u8, vg_eq(es_op_field(st, 0, ES_F_A1), dq), ctr) 588 gv_check("the-vertex-moved-by-exactly-the-gizmo-delta" as *u8, vg_eq(vp_vert_component(st, 0, VP_AXIS_X, VG_NV), 0 - RC_Q + dq), ctr) 589 gv_check("the-drag-left-that-vertex-y-untouched" as *u8, vg_eq(vp_vert_component(st, 0, VP_AXIS_Y, VG_NV), 0 - RC_Q), ctr) 590 gv_check("the-drag-left-that-vertex-z-untouched" as *u8, vg_eq(vp_vert_component(st, 0, VP_AXIS_Z, VG_NV), 0), ctr) 591 gv_check("the-drag-left-every-other-vertex-untouched" as *u8, vg_eq(vp_vert_component(st, 1, VP_AXIS_X, VG_NV), RC_Q), ctr) 592 // ANTI-VACUITY ON THE INSTRUMENT: a constant digest would satisfy every equality tooth below. 593 gv_check("the-digest-discriminates-the-dragged-state-from-the-base" as *u8, vg_ne(d_after, d_before), ctr) 594 595 // COMPOSITION PROOF, both directions. Replaying the log from the base must reproduce the state 596 // bit-for-bit, and an independently pushed identical operation must land on the same digest. 597 gv_check("replaying-the-log-returns-ok" as *u8, vg_eq(es_replay(st), ES_OK), ctr) 598 gv_check("the-state-after-the-drag-equals-replaying-that-one-operation" as *u8, vg_eq(es_digest(st), d_after), ctr) 599 let twin: *i64 = es_new(VG_CAP, VG_NCELLS) 600 vp_seed_stack(twin, mesh, VG_NV) 601 es_push(twin, ES_OP_ADD, vp_cell(VP_AXIS_X, 0, VG_NV), dq, 0, 0) 602 gv_check("the-drag-equals-an-independently-pushed-identical-operation" as *u8, vg_eq(es_digest(twin), d_after), ctr) 603 604 gv_check("undo-after-a-drag-returns-ok" as *u8, vg_eq(es_undo(st), ES_OK), ctr) 605 gv_check("undo-after-a-drag-restores-the-exact-prior-digest" as *u8, vg_eq(es_digest(st), d_before), ctr) 606 gv_check("undo-restores-the-exact-prior-vertex-value" as *u8, vg_eq(vp_vert_component(st, 0, VP_AXIS_X, VG_NV), 0 - RC_Q), ctr) 607 gv_check("redo-after-the-undo-restores-the-dragged-digest" as *u8, vg_eq(es_redo(st), ES_OK), ctr) 608 gv_check("redo-lands-back-on-the-dragged-state" as *u8, vg_eq(es_digest(st), d_after), ctr) 609 610 // A ZERO-MAGNITUDE GESTURE STILL COUNTS AS A GESTURE, or undo starts skipping clicks. 611 let stz: *i64 = es_new(VG_CAP, VG_NCELLS) 612 vp_seed_stack(stz, mesh, VG_NV) 613 let dz0: i64 = es_digest(stz) 614 vp_apply_drag(stz, cam, drag_0, VP_AXIS_X, 0, 1, VG_NV, scratch, gout) 615 gv_check("a-zero-length-drag-still-records-one-operation" as *u8, vg_eq(es_count(stz), 1), ctr) 616 gv_check("a-zero-length-drag-leaves-the-state-identical" as *u8, vg_eq(es_digest(stz), dz0), ctr) 617 618 // THE RANGE DRAG is where the component-major cell layout earns its keep: N vertices moved along one 619 // axis is ONE range-add, not N adds, so it is still a single undo. 620 let str3: *i64 = es_new(VG_CAP, VG_NCELLS) 621 vp_seed_stack(str3, mesh, VG_NV) 622 let rr: i64 = vp_apply_drag(str3, cam, drag_r, VP_AXIS_X, 0, 3, VG_NV, scratch, gout) 623 let rdq: i64 = gout[VP_G_SCALAR] 624 gv_check("a-multi-vertex-drag-was-accepted" as *u8, vg_eq(rr, VP_OK), ctr) 625 gv_check("a-multi-vertex-drag-still-pushes-exactly-one-operation" as *u8, vg_eq(es_count(str3), 1), ctr) 626 gv_check("a-multi-vertex-drag-records-a-range-add" as *u8, vg_eq(es_op_field(str3, 0, ES_F_KIND), ES_OP_RADD), ctr) 627 gv_check("the-range-covers-exactly-the-selected-vertices" as *u8, vg_eq(es_op_field(str3, 0, ES_F_A1) - es_op_field(str3, 0, ES_F_A0), 3), ctr) 628 gv_check("range-drag-moved-the-first-selected-vertex" as *u8, vg_eq(vp_vert_component(str3, 0, VP_AXIS_X, VG_NV), 0 - RC_Q + rdq), ctr) 629 gv_check("range-drag-moved-the-last-selected-vertex" as *u8, vg_eq(vp_vert_component(str3, 2, VP_AXIS_X, VG_NV), 0 + rdq), ctr) 630 gv_check("range-drag-left-the-vertex-just-past-the-selection-alone" as *u8, vg_eq(vp_vert_component(str3, 3, VP_AXIS_X, VG_NV), 0 - RC_Q), ctr) 631 gv_check("range-drag-did-not-leak-into-another-component" as *u8, vg_eq(vp_vert_component(str3, 0, VP_AXIS_Y, VG_NV), 0 - RC_Q), ctr) 632 gv_check("undo-of-a-range-drag-is-a-single-undo" as *u8, vg_eq(es_undo(str3), ES_OK), ctr) 633 gv_check("undo-of-a-range-drag-restores-every-selected-vertex" as *u8, vg_eq(vp_vert_component(str3, 2, VP_AXIS_X, VG_NV), 0), ctr) 634 635 // A REFUSED DRAG MUST PUSH NOTHING, or a later replay applies a delta nobody computed. 636 let stx: *i64 = es_new(VG_CAP, VG_NCELLS) 637 vp_seed_stack(stx, mesh, VG_NV) 638 let sel_bad: i64 = vp_apply_drag(stx, cam, drag_r, VP_AXIS_X, VG_NV - 1, 2, VG_NV, scratch, gout) 639 let sel_good: i64 = vp_apply_drag(stx, cam, drag_r, VP_AXIS_X, 0, 1, VG_NV, scratch, gout) 640 gv_bite("neg-control-selection-running-past-the-end-of-the-mesh" as *u8, vg_eq(sel_bad, VP_E_CELL), vg_eq(sel_good, VP_E_CELL), ctr) 641 642 let sty: *i64 = es_new(VG_CAP, VG_NCELLS) 643 vp_seed_stack(sty, mesh, VG_NV) 644 let axr: i64 = vp_apply_drag(sty, cam, drag_r, VG_BAD_AXIS, 0, 1, VG_NV, scratch, gout) 645 gv_check("a-refused-gizmo-propagates-its-own-name" as *u8, vg_eq(axr, VP_E_AXIS), ctr) 646 gv_check("a-refused-drag-pushes-nothing-onto-the-log" as *u8, vg_eq(es_count(sty), 0), ctr) 647 648 gv_check("the-whole-run-never-wrote-a-word-of-the-mesh-buffer" as *u8, vg_eq(vg_sum(mesh, VG_MESH_WORDS), mesh_sum_before), ctr) 649 650 // ============================================================================================= 651 // 9. WHAT A PICK COSTS, AT A STATED TRIANGLE COUNT -- the other half of the rung's done-rule. 652 // ============================================================================================= 653 // Everything above proves the pick is CORRECT. None of it says what it COSTS, and dcc.plan's unit line 654 // is explicit that an interaction claim with no triangle count is opinion wearing a number. This is the 655 // number, with its N, its clock, its repetition count and its growth curve all printed so the whole 656 // thing can be re-derived by anyone who doubts it. 657 // 658 // THE FIGURE IS A NATIVE FLOOR AND IS LABELLED ONE. It is x86 on the estate host; the shipped viewport 659 // is WebAssembly in a visitor's browser, which is slower on hardware that is very likely weaker. A floor 660 // is decisive in exactly one direction -- if the floor already misses the budget the browser cannot make 661 // it -- and it can never prove the browser fits. Same declaration nx_tissuebudget makes about the tissue 662 // tick, for the same reason, and it is stated here rather than left for a reader to infer. 663 let bmesh: *i64 = sys_mmap(vp_bench_verts(VB_GMAX) * VP_THREE * VG_I64) as *i64 664 let bidx: *i64 = sys_mmap(vp_bench_tris(VB_GMAX) * VP_TRI_I * VG_I64) as *i64 665 let lad: *i64 = sys_mmap(VB_LADDER * VG_I64) as *i64 666 let bn: *i64 = sys_mmap(VB_LADDER * VG_I64) as *i64 667 let bns: *i64 = sys_mmap(VB_LADDER * VG_I64) as *i64 668 let bk: *i64 = sys_mmap(VB_LADDER * VG_I64) as *i64 669 let brep: *i64 = sys_mmap(VB_LADDER * VG_I64) as *i64 670 let blo: *i64 = sys_mmap(VB_LADDER * VG_I64) as *i64 671 let bhi: *i64 = sys_mmap(VB_LADDER * VG_I64) as *i64 672 let mout: *i64 = sys_mmap(VP_R_WORDS * VG_I64) as *i64 673 lad[0] = VB_G0 674 lad[1] = VB_G1 675 lad[2] = VB_G2 676 lad[3] = VB_G3 677 678 // The buffers above are sized from the LIBRARY'S OWN closed forms at the largest rung, so there is no 679 // hand-written word count anywhere here to drift out of step with the ladder. These two teeth are what 680 // stop the ladder and the allocation from disagreeing silently. 681 gv_check("fixture-the-buffers-are-sized-for-the-largest-rung-actually-on-the-ladder" as *u8, vg_eq(lad[VB_LADDER - 1], VB_GMAX), ctr) 682 gv_check("fixture-each-rung-quadruples-the-triangle-count-as-the-derived-bar-assumes" as *u8, 683 vg_eq(vp_bench_tris(VB_G1), VB_STEP * vp_bench_tris(VB_G0)) 684 * vg_eq(vp_bench_tris(VB_G2), VB_STEP * vp_bench_tris(VB_G1)) 685 * vg_eq(vp_bench_tris(VB_G3), VB_STEP * vp_bench_tris(VB_G2)), ctr) 686 687 var gridok: i64 = 1 688 var allhit: i64 = 1 689 var allres: i64 = 1 690 var allacc: i64 = 1 691 var allrepeat: i64 = 1 692 var allpos: i64 = 1 693 var benchclean: i64 = 1 694 var s: i64 = 0 695 while s < VB_LADDER { 696 let g: i64 = lad[s] 697 let nv: i64 = vp_bench_verts(g) 698 let nt: i64 = vp_bench_grid(bmesh, bidx, g) 699 if nt != vp_bench_tris(g) { gridok = 0 } 700 bn[s] = nt 701 let bsum: i64 = vg_sum(bmesh, nv * VP_THREE) 702 // ASSERT THE FIXTURE REACHED THE CONDITION BEFORE ASSERTING THE OUTCOME: a bench whose pick MISSED 703 // would still produce a perfectly respectable elapsed time, and it would be timing the miss path 704 // rather than the pick. h must be a real triangle index at every rung. 705 let h: i64 = vp_pick_screen(cam, VB_PX, VB_PY, bmesh, nv, bidx, nt, scratch, pout) 706 if h < 0 { allhit = 0 } 707 if vb_measure(cam, bmesh, nv, bidx, nt, scratch, pout, mout) == 0 { allres = 0 } 708 brep[s] = mout[0] 709 blo[s] = mout[1] 710 bhi[s] = mout[2] 711 // THE ANTI-DEAD-CODE TOOTH. reps picks each returning h must accumulate to reps*(h+1); a loop the 712 // compiler removed cannot produce that number, and a removed loop is what a suspiciously fast 713 // benchmark actually measures. 714 if mout[3] != mout[0] * (h + 1) { allacc = 0 } 715 // REPEATABILITY, judged against the bar the result is about to face. The tightest bar the linearity 716 // teeth apply is the adjacent-pair one, sqrt(VB_STEP); asking vp_growth_ok for a size pair of 717 // 1 to VB_STEP evaluates exactly that bar with the SAME ruler and introduces no new constant. 718 if vp_growth_ok(1, mout[1], VB_STEP, mout[2]) != 1 { allrepeat = 0 } 719 let ns: i64 = vp_ns_per_op(mout[1], mout[0]) 720 bns[s] = ns 721 if ns <= 0 { allpos = 0 } 722 bk[s] = vp_cost_per_tri(ns, nt) 723 if vg_sum(bmesh, nv * VP_THREE) != bsum { benchclean = 0 } 724 s = s + 1 725 } 726 727 gv_puts(" clock=sys_clock_now_us CLOCK_MONOTONIC resolution_us=1 resolve_floor_us=" as *u8) 728 gv_num(VB_RESOLVE_US) 729 gv_puts(" min_reps=" as *u8); gv_num(VB_REPS_MIN) 730 gv_puts(" samples=" as *u8); gv_num(VB_SAMPLES) 731 gv_puts(" published=min-elapsed (a NATIVE FLOOR)\n" as *u8) 732 var p: i64 = 0 733 while p < VB_LADDER { 734 gv_puts(" tris=" as *u8); gv_num(bn[p]) 735 gv_puts(" reps=" as *u8); gv_num(brep[p]) 736 gv_puts(" us_min=" as *u8); gv_num(blo[p]) 737 gv_puts(" us_max=" as *u8); gv_num(bhi[p]) 738 gv_puts(" ns_per_pick=" as *u8); gv_num(bns[p]) 739 gv_puts(" ps_per_triangle=" as *u8); gv_num(bk[p]) 740 gv_puts("\n" as *u8) 741 p = p + 1 742 } 743 744 gv_check("fixture-the-bench-generator-wrote-exactly-its-closed-form-triangle-count-at-every-rung" as *u8, gridok, ctr) 745 gv_check("fixture-the-bench-pick-hits-a-real-triangle-at-every-stated-triangle-count" as *u8, allhit, ctr) 746 gv_check("fixture-every-timing-run-resolved-against-the-declared-clock-floor" as *u8, allres, ctr) 747 gv_check("the-timed-loop-really-ran-every-repetition-it-was-paid-for" as *u8, allacc, ctr) 748 gv_check("two-independent-samples-of-one-configuration-agree-within-the-bar-the-result-is-judged-against" as *u8, allrepeat, ctr) 749 gv_check("a-per-operation-cost-is-published-in-nanoseconds-at-every-stated-triangle-count" as *u8, allpos, ctr) 750 gv_check("the-timed-picks-never-wrote-a-word-of-the-bench-mesh" as *u8, benchclean, ctr) 751 // A picker that answered without reading the index buffer would cost the same at 128 triangles and at 752 // 8192. Monotonic growth is the cheapest proof that the loop is actually scanning what it claims to. 753 var mono: i64 = 1 754 var m: i64 = 1 755 while m < VB_LADDER { 756 if bns[m] <= bns[m - 1] { mono = 0 } 757 m = m + 1 758 } 759 gv_check("the-published-cost-grows-with-triangle-count-so-the-loop-is-scanning-the-whole-buffer" as *u8, mono, ctr) 760 761 // ---- LINEARITY. A pick that is accidentally quadratic is the defect this measurement exists to catch, 762 // and a single N cannot see it. Every adjacent pair is tested as well as the extremes, so a blow-up 763 // anywhere in the range fails its own local pair against a TIGHTER bar than the whole-range one. 764 var linadj: i64 = 1 765 var l: i64 = 1 766 while l < VB_LADDER { 767 if vp_growth_ok(bn[l - 1], bk[l - 1], bn[l], bk[l]) != 1 { linadj = 0 } 768 gv_puts(" growth " as *u8); gv_num(bn[l - 1]) 769 gv_puts(" to " as *u8); gv_num(bn[l]) 770 gv_puts(" tris: per-triangle cost permil=" as *u8); gv_num(vp_growth_permil(bk[l - 1], bk[l])) 771 gv_puts(" (" as *u8); gv_num(VP_PERMIL) 772 gv_puts(" would be exactly linear, " as *u8); gv_num(VB_STEP * VP_PERMIL) 773 gv_puts(" quadratic)\n" as *u8) 774 l = l + 1 775 } 776 gv_puts(" growth " as *u8); gv_num(bn[0]) 777 gv_puts(" to " as *u8); gv_num(bn[VB_LADDER - 1]) 778 gv_puts(" tris: per-triangle cost permil=" as *u8); gv_num(vp_growth_permil(bk[0], bk[VB_LADDER - 1])) 779 gv_puts(" (" as *u8); gv_num(VP_PERMIL) 780 gv_puts(" would be exactly linear, " as *u8); gv_num((bn[VB_LADDER - 1] / bn[0]) * VP_PERMIL) 781 gv_puts(" quadratic)\n" as *u8) 782 gv_check("cost-grows-no-worse-than-linearly-between-every-adjacent-pair-on-the-ladder" as *u8, linadj, ctr) 783 gv_check("cost-grows-no-worse-than-linearly-across-the-whole-range-of-the-ladder" as *u8, 784 vg_eq(vp_growth_ok(bn[0], bk[0], bn[VB_LADDER - 1], bk[VB_LADDER - 1]), 1), ctr) 785 786 // A ruler that accepted everything would pass every linearity tooth above, so the ruler is bitten on 787 // synthetic pairs whose answer is known by construction. The quadratic pair is exactly the hypothesis 788 // the derived bar was built to separate: per-triangle cost growing by the same factor as triangle count. 789 let qn_lo: i64 = 100 790 let qk_lo: i64 = 1000 791 let qn_hi: i64 = qn_lo * VB_STEP * VB_STEP 792 let quad_bad: i64 = vp_growth_ok(qn_lo, qk_lo, qn_hi, qk_lo * VB_STEP * VB_STEP) 793 let lin_good: i64 = vp_growth_ok(qn_lo, qk_lo, qn_hi, qk_lo) 794 gv_bite("neg-control-a-quadratic-cost-curve-is-REFUSED-by-the-growth-ruler" as *u8, vg_eq(quad_bad, 0), vg_eq(lin_good, 0), ctr) 795 // AND THE BAR IS FINITE. sqrt(R) for that synthetic pair is exactly VB_STEP, so a per-triangle growth 796 // sitting ON the bar must pass and one a whisker above it must fail. Without this the ruler could be 797 // accepting everything short of the fully quadratic case and nothing above would notice. 798 let at_bar: i64 = vp_growth_ok(qn_lo, qk_lo, qn_hi, qk_lo * VB_STEP) 799 let over_bar: i64 = vp_growth_ok(qn_lo, qk_lo, qn_hi, qk_lo * VB_STEP + 1) 800 gv_bite("neg-control-the-growth-bar-is-finite-so-one-unit-over-it-is-REFUSED" as *u8, vg_eq(over_bar, 0), vg_eq(at_bar, 0), ctr) 801 let ordbad: i64 = vp_growth_ok(bn[VB_LADDER - 1], bk[VB_LADDER - 1], bn[0], bk[0]) 802 let ordgood: i64 = vp_growth_ok(bn[0], bk[0], bn[VB_LADDER - 1], bk[VB_LADDER - 1]) 803 gv_bite("neg-control-the-growth-ruler-refuses-a-size-pair-it-cannot-compare" as *u8, vg_eq(ordbad, VP_E_ARGS), vg_eq(ordgood, VP_E_ARGS), ctr) 804 805 // ---- THE BAR ITSELF IS CITED DATA, NEVER A NUMBER IN THIS FILE. 806 let nrows: i64 = ob_load(VB_CONF as *u8) 807 var tableok: i64 = 0 808 if nrows > 0 { tableok = 1 } 809 // gv_need, not gv_check: an unreadable table means the gate COULD NOT LOOK, which is a different answer 810 // from the budget being missed, and collapsing the two would let a missing file read as a failing pick. 811 gv_need("the-cited-gamefeel-oracle-table-is-readable" as *u8, tableok, ctr) 812 if tableok == 1 { 813 // The frame budget is DERIVED from the rate (1e6/fps) rather than typed, and then CROSS-CHECKED 814 // against the row that cites it in milliseconds. If those two ever disagree one of them is wrong. 815 let frame_us: i64 = fb_budget_us(VB_FPS_VR) 816 var frok: i64 = 0 817 let rfr: i64 = ob_find("frame_budget_ms_90hz" as *u8) 818 if rfr >= 0 { 819 gv_puts(" frame_budget_ms_90hz cited [" as *u8); gv_num(ob_lo(rfr)) 820 gv_puts(".." as *u8); gv_num(ob_hi(rfr)) 821 gv_puts("] ms vs derived " as *u8); gv_num(frame_us); gv_puts(" us\n" as *u8) 822 if frame_us >= ob_lo(rfr) * VB_MS_US { if frame_us <= ob_hi(rfr) * VB_MS_US { frok = 1 } } 823 } 824 gv_check("the-DERIVED-frame-budget-agrees-with-the-CITED-frame_budget_ms_90hz-row" as *u8, frok, ctr) 825 // js_block_ms is the BINDING bar for a pick, and the conf says why in its own source field: any app 826 // logic over that ceiling should be optimised. A pick is app logic on the main thread, so this is 827 // the row that governs it; the frame budget is what the FRACTION is reported against. 828 var jsceil_us: i64 = 0 829 let rjs: i64 = ob_find("js_block_ms" as *u8) 830 if rjs >= 0 { 831 jsceil_us = ob_hi(rjs) * VB_MS_US 832 gv_puts(" js_block_ms cited [" as *u8); gv_num(ob_lo(rjs)) 833 gv_puts(".." as *u8); gv_num(ob_hi(rjs)) 834 gv_puts("] ms => " as *u8); gv_num(jsceil_us); gv_puts(" us app-logic ceiling\n" as *u8) 835 } 836 var fitmax: i64 = 0 837 var q: i64 = 0 838 while q < VB_LADDER { 839 let us_pick: i64 = bns[q] / VP_NS_PER_US 840 var f: i64 = 0 841 if jsceil_us > 0 { f = fb_fits(us_pick, jsceil_us) } 842 if f == 1 { fitmax = bn[q] } 843 gv_puts(" tris=" as *u8); gv_num(bn[q]) 844 gv_puts(" us_per_pick=" as *u8); gv_num(us_pick) 845 gv_puts(" frame_permil=" as *u8); gv_num(vp_frame_permil(bns[q], frame_us)) 846 gv_puts(" frame_ppm=" as *u8); gv_num(vp_frame_ppm(bns[q], frame_us)) 847 gv_puts(" fits_js_block_ms=" as *u8); gv_num(f) 848 gv_puts("\n" as *u8) 849 q = q + 1 850 } 851 // HOW MANY TRIANGLES THE CITED CEILING ACTUALLY BUYS. This is the number a board can act on, and it 852 // is only meaningful BECAUSE the linearity teeth above passed: with the per-triangle cost proven flat 853 // across the range, the ceiling divided by that cost is the triangle budget for one pick. There are a 854 // million picoseconds in a microsecond, which is VP_NS_PER_US twice rather than a fresh literal. 855 var ceil_tris: i64 = 0 856 if jsceil_us > 0 { if bk[VB_LADDER - 1] > 0 { ceil_tris = (jsceil_us * VP_NS_PER_US * VP_NS_PER_US) / bk[VB_LADDER - 1] } } 857 gv_puts(" PUBLISHED: a pick over " as *u8); gv_num(bn[VB_LADDER - 1]) 858 gv_puts(" triangles costs " as *u8); gv_num(bns[VB_LADDER - 1]) 859 gv_puts(" ns, " as *u8); gv_num(vp_frame_ppm(bns[VB_LADDER - 1], frame_us)) 860 gv_puts(" ppm of a " as *u8); gv_num(frame_us) 861 gv_puts(" us 90 Hz frame; per-triangle " as *u8); gv_num(bk[VB_LADDER - 1]) 862 gv_puts(" ps; the cited " as *u8); gv_num(jsceil_us) 863 gv_puts(" us ceiling buys " as *u8); gv_num(ceil_tris) 864 gv_puts(" triangles per pick; largest measured rung inside it = " as *u8); gv_num(fitmax) 865 gv_puts("; NATIVE FLOOR, the browser is slower\n" as *u8) 866 gv_check("a-pick-fits-the-CITED-js_block_ms-app-logic-ceiling-at-a-stated-triangle-count" as *u8, vg_gt(fitmax, 0), ctr) 867 // THE EXTRAPOLATION MUST AGREE WITH THE MEASUREMENT IT IS BUILT FROM. If the largest measured rung 868 // fits the ceiling, the triangle budget derived from the per-triangle cost cannot come out smaller 869 // than that rung -- and if it did, the linear model and the direct timing would be disagreeing, which 870 // is the one thing that would make the published budget worthless. 871 gv_check("the-derived-triangle-budget-agrees-with-the-directly-measured-rung-it-extrapolates-from" as *u8, vg_gt(ceil_tris, fitmax), ctr) 872 var negfired: i64 = 0 873 var negsilent: i64 = 0 874 if jsceil_us > 0 { 875 if fb_fits(jsceil_us + 1, jsceil_us) == 0 { negfired = 1 } 876 if fb_fits(jsceil_us, jsceil_us) == 0 { negsilent = 1 } 877 } 878 gv_bite("neg-control-a-cost-one-microsecond-over-the-cited-ceiling-is-REFUSED" as *u8, negfired, negsilent, ctr) 879 } 880 881 return gv_verdict("nx_vpick_gate" as *u8, ctr, "a click resolves to a triangle by depth rather than by buffer order, a drag resolves to one entry on the operation log instead of a write to the vertex buffer, and the cost of a pick is published in nanoseconds at four stated triangle counts against a cited frame budget" as *u8) 882}