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}