Skip to main content
← PSoXide SDK guides and examples · SDK f2fec1c301f1 · All Cargo features enabled; check feature gates before use.

psx_telemetry/
lib.rs

1// SPDX-License-Identifier: GPL-2.0-or-later
2//! Shared guest/host telemetry id tables.
3//!
4//! The guest runtime (`psx-engine`) emits stage/task/counter events
5//! through an emulator-observed Expansion 2 port, and the emulator
6//! (`emulator-core`) decodes and aggregates them. Both sides used to
7//! carry hand-synced copies of the id tables; this crate owns the ids,
8//! the slot counts, and the compile-time guards once. It is `no_std`
9//! and const-only, so the MIPS guest and host tooling share it freely.
10
11#![no_std]
12
13pub mod emit;
14
15/// Declares one id module plus a `*_desc(id)` lookup returning each id's own
16/// rustdoc as a runtime string. Host tooling (the frame profiler's tooltips)
17/// reads descriptions through the lookup, so the docs here are the single
18/// source and cannot drift from what the UI shows. The const declarations
19/// inside the braces are ordinary Rust, passed through verbatim.
20macro_rules! id_table {
21    (
22        $(#[$mod_attr:meta])*
23        pub mod $module:ident;
24        pub fn $desc_fn:ident;
25        {
26            $(
27                $(#[doc = $doc:literal])+
28                pub const $name:ident: u16 = $value:expr;
29            )+
30        }
31    ) => {
32        $(#[$mod_attr])*
33        pub mod $module {
34            $(
35                $(#[doc = $doc])+
36                pub const $name: u16 = $value;
37            )+
38
39            /// Every id declared in this module, in declaration order.
40            pub const IDS: &[u16] = &[$($name),+];
41
42            /// The highest id declared here. Slot counts derive from it, so a
43            /// new id past the old maximum cannot be dropped by a stale count.
44            pub const MAX_ID: u16 = {
45                let mut max = 0;
46                let mut i = 0;
47                while i < IDS.len() {
48                    if IDS[i] > max {
49                        max = IDS[i];
50                    }
51                    i += 1;
52                }
53                max
54            };
55        }
56
57        /// Host-tooling description for an id: the id's doc comment from this
58        /// crate, verbatim. Returns `""` for unknown ids.
59        pub fn $desc_fn(id: u16) -> &'static str {
60            match id {
61                $( $module::$name => concat!($($doc),+), )+
62                _ => "",
63            }
64        }
65    };
66}
67
68id_table! {
69    /// Runtime stage ids.
70    pub mod stage;
71    pub fn stage_desc;
72    {
73    /// Per-frame gameplay/update work.
74    pub const UPDATE: u16 = 1;
75    /// Framebuffer clear before scene rendering.
76    pub const FRAME_CLEAR: u16 = 2;
77    /// Whole `Scene::render` call.
78    pub const RENDER: u16 = 3;
79    /// Present/vblank wait and framebuffer swap.
80    pub const PRESENT: u16 = 4;
81    /// Editor-playtest camera update.
82    pub const CAMERA: u16 = 5;
83    /// Grid-room surface rendering.
84    pub const ROOM: u16 = 6;
85    /// Legacy entity debug marker rendering.
86    pub const ENTITY_MARKERS: u16 = 7;
87    /// Placed model-instance rendering.
88    pub const MODEL_INSTANCES: u16 = 8;
89    /// Player model rendering.
90    pub const PLAYER: u16 = 9;
91    /// Whole-model bounds tests for placed model instances.
92    pub const MODEL_BOUNDS: u16 = 13;
93    /// Placed model draw calls after bounds culling.
94    pub const MODEL_DRAW: u16 = 14;
95    /// Whole-player bounds test.
96    pub const PLAYER_BOUNDS: u16 = 15;
97    /// Player model draw call after bounds culling.
98    pub const PLAYER_DRAW: u16 = 16;
99    /// Textured model joint pose sampling and transform setup.
100    pub const TEXTURED_MODEL_JOINTS: u16 = 17;
101    /// Textured model vertex projection.
102    pub const TEXTURED_MODEL_PROJECT: u16 = 18;
103    /// Textured model face culling, packet build, and command enqueue.
104    pub const TEXTURED_MODEL_FACES: u16 = 19;
105    /// Active room/chunk window rebuilds, including residency and cache setup.
106    pub const ACTIVE_ROOM_WINDOW: u16 = 20;
107    /// Runtime room surface-cache construction.
108    pub const ROOM_SURFACE_CACHE: u16 = 21;
109    /// Texture/atlas upload work.
110    pub const VRAM_UPLOAD: u16 = 22;
111    /// Editor-playtest CD streaming benchmark.
112    pub const CD_STREAM_BENCH: u16 = 23;
113    /// Steady-state portion of the editor-playtest CD streaming benchmark.
114    pub const CD_STREAM_STEADY: u16 = 24;
115    /// Sequential read of the real cooked world package.
116    pub const CD_WORLD_PACK_STREAM: u16 = 25;
117    /// Synchronous read of one streamed room chunk from WORLD.PAK.
118    pub const CD_ROOM_CHUNK_LOAD: u16 = 26;
119    /// Cached-room visible-cell/PVS list lookup.
120    pub const ROOM_VISIBLE_LIST: u16 = 27;
121    /// Cached-room visible-cell lookup and vertex-index gathering.
122    pub const ROOM_CELL_SELECT: u16 = 28;
123    /// Cached-room GTE/CPU vertex projection.
124    pub const ROOM_PROJECT: u16 = 29;
125    /// Cached-room per-vertex depth/fog preparation.
126    pub const ROOM_DEPTH_PREP: u16 = 30;
127    /// Cached-room surface culling, lighting, packet build, and command enqueue.
128    pub const ROOM_SURFACE_DRAW: u16 = 31;
129    /// Cooked sky/cyclorama backdrop rendering.
130    pub const SKY: u16 = 32;
131    /// Distant far-vista ring rendering.
132    pub const FAR_VISTA: u16 = 33;
133    /// Editor-authored image/card prop rendering.
134    pub const IMAGE_PROPS: u16 = 34;
135    /// Portal traversal and visible-room selection.
136    pub const PORTAL_VISIBILITY: u16 = 35;
137    /// Player-attached equipment / weapon rendering and hit-volume evaluation.
138    pub const EQUIPMENT: u16 = 12;
139    /// Deferred world-command sort and OT insertion.
140    pub const WORLD_FLUSH: u16 = 10;
141    /// Ordering-table DMA kick (CPU-side setup; excludes the GPU-draw wait).
142    pub const OT_SUBMIT: u16 = 11;
143    /// Blocking wait for the ordering-table DMA walk to finish: the
144    /// CPU-blocked-on-GPU portion of a submit, i.e. GPU draw + DMA cost.
145    pub const OT_WAIT: u16 = 45;
146    /// Player collision gather + motor solve (sim).
147    pub const SIM_COLLISION: u16 = 36;
148    /// Current-room tracking + active-window refresh (sim).
149    pub const SIM_ROOM_TRACK: u16 = 37;
150    /// Streamed-room residency reconcile (sim).
151    pub const SIM_RESIDENCY: u16 = 38;
152    /// Streamed-room sector pump (sim).
153    pub const SIM_PUMP: u16 = 39;
154    /// Character motor solve (floor snap + wall sweep), excludes the gather.
155    pub const SIM_SOLVE: u16 = 40;
156    /// Solid unbroken editor box props.
157    pub const BOX_PROPS: u16 = 41;
158    /// Persistent floor debris from broken editor box props.
159    pub const BOX_PROP_DEBRIS: u16 = 42;
160    /// Transient break shards from editor box props.
161    pub const BOX_PROP_SHARDS: u16 = 43;
162    /// Editor-authored image/card props, excluding box props.
163    pub const IMAGE_CARDS: u16 = 44;
164    /// Pre-collision actor work in the fixed update: prop advances,
165    /// interactables, lock-on, evade/anim input, attack break checks.
166    pub const UPDATE_ACTOR: u16 = 46;
167    /// Always-on active-room window refresh at the fixed-update tail.
168    pub const UPDATE_WINDOW: u16 = 47;
169    /// Inside ROOM_CELL_SELECT: candidate -> cached cell index lookup.
170    pub const CELL_LOOKUP: u16 = 48;
171    /// Inside ROOM_CELL_SELECT: per-cell view transform + frustum accept.
172    pub const CELL_DEPTH: u16 = 49;
173    /// Inside ROOM_CELL_SELECT: unique vertex-index collection.
174    pub const CELL_COLLECT: u16 = 50;
175    /// Phase-3 gameplay layer: entity state machines + logic event
176    /// graph + effect dispatch, ticked at the top of the update band.
177    /// Budgeted at 60k cycles per 30 fps frame in
178    /// docs/game-runtime-plan.md ("Phase 3 budget").
179    pub const GAME_LOGIC: u16 = 51;
180    }
181}
182
183/// Number of stage slots, including index zero for unknown/reserved ids: the
184/// highest stage id plus one. The host's stage arrays are indexed by id and
185/// out-of-range ids are dropped silently, so this follows [`stage::MAX_ID`]
186/// rather than a number to keep in step by hand.
187pub const STAGE_COUNT: usize = stage::MAX_ID as usize + 1;
188
189id_table! {
190    /// Runtime task ids.
191    pub mod task;
192    pub fn task_desc;
193    {
194    /// Built-in fixed simulation/update task.
195    pub const FIXED_UPDATE: u16 = 0;
196    /// Built-in visual render/present task.
197    pub const VISUAL_RENDER: u16 = 1;
198    }
199}
200
201/// Number of task slots, including reserved future scheduler jobs.
202pub const TASK_COUNT: usize = 16;
203
204const _: () = assert!(
205    (task::MAX_ID as usize) < TASK_COUNT,
206    "a task id has no slot"
207);
208
209id_table! {
210    /// Runtime counter ids.
211    pub mod counter;
212    pub fn counter_desc;
213    {
214    /// Textured primitive packets allocated this frame.
215    pub const TRI_PRIMITIVES: u16 = 1;
216    /// World render commands queued before flush.
217    pub const WORLD_COMMANDS: u16 = 2;
218    /// Placed model instances drawn.
219    pub const MODEL_INSTANCE_DRAWS: u16 = 3;
220    /// Vertices projected for placed model instances.
221    pub const MODEL_INSTANCE_PROJECTED_VERTICES: u16 = 4;
222    /// Triangles submitted for placed model instances.
223    pub const MODEL_INSTANCE_SUBMITTED_TRIS: u16 = 5;
224    /// Triangles culled for placed model instances.
225    pub const MODEL_INSTANCE_CULLED_TRIS: u16 = 6;
226    /// Triangles dropped for placed model instances.
227    pub const MODEL_INSTANCE_DROPPED_TRIS: u16 = 7;
228    /// Vertices projected for the player model.
229    pub const PLAYER_PROJECTED_VERTICES: u16 = 8;
230    /// Triangles submitted for the player model.
231    pub const PLAYER_SUBMITTED_TRIS: u16 = 9;
232    /// Triangles culled for the player model.
233    pub const PLAYER_CULLED_TRIS: u16 = 10;
234    /// Triangles dropped for the player model.
235    pub const PLAYER_DROPPED_TRIS: u16 = 11;
236    /// Bitfield of model-render overflow flags observed this frame.
237    pub const MODEL_OVERFLOW_FLAGS: u16 = 12;
238    /// Non-empty room grid cells considered by the visibility pass.
239    pub const ROOM_CELLS_CONSIDERED: u16 = 13;
240    /// Room grid cells drawn after visibility culling.
241    pub const ROOM_CELLS_DRAWN: u16 = 14;
242    /// Room grid cells rejected by the coarse frustum test.
243    pub const ROOM_CELLS_CULLED: u16 = 15;
244    /// Room floor/ceiling/wall surfaces considered for projection.
245    pub const ROOM_SURFACES_CONSIDERED: u16 = 16;
246    /// Player-attached equipment visuals drawn.
247    pub const EQUIPMENT_DRAWS: u16 = 17;
248    // 18 and 19 are retired (the render-path weapon-hitbox counters,
249    // removed with that dead system). Do not reuse: old counter-log
250    // captures still carry them under the old meaning.
251    /// Vertices projected for equipment models.
252    pub const EQUIPMENT_PROJECTED_VERTICES: u16 = 20;
253    /// Triangles submitted for equipment models.
254    pub const EQUIPMENT_SUBMITTED_TRIS: u16 = 21;
255    /// Triangles culled for equipment models.
256    pub const EQUIPMENT_CULLED_TRIS: u16 = 22;
257    /// Triangles dropped for equipment models.
258    pub const EQUIPMENT_DROPPED_TRIS: u16 = 23;
259    /// Placed model instance bounds tests.
260    pub const MODEL_INSTANCE_BOUNDS_TESTS: u16 = 24;
261    /// Placed model instances rejected by whole-model bounds.
262    pub const MODEL_INSTANCE_BOUNDS_CULLED: u16 = 25;
263    /// Player bounds tests.
264    pub const PLAYER_BOUNDS_TESTS: u16 = 26;
265    /// Player draws rejected by whole-model bounds.
266    pub const PLAYER_BOUNDS_CULLED: u16 = 27;
267    /// Joints sampled for textured model submits.
268    pub const TEXTURED_MODEL_JOINTS: u16 = 28;
269    /// Parts walked for textured model submits.
270    pub const TEXTURED_MODEL_PARTS: u16 = 29;
271    /// Vertices projected for textured model submits.
272    pub const TEXTURED_MODEL_VERTICES: u16 = 30;
273    /// Face records considered by textured model submits.
274    pub const TEXTURED_MODEL_FACES: u16 = 31;
275    /// Active runtime room/chunk records walked this frame.
276    pub const ROOM_ACTIVE_CHUNKS: u16 = 32;
277    /// Precomputed/grid-visible cells supplied to the room renderer.
278    pub const ROOM_VISIBLE_CELLS: u16 = 33;
279    /// Active room/chunk draws that used the cached surface path.
280    pub const ROOM_CACHED_DRAWS: u16 = 34;
281    /// Active room/chunk draws that used the direct uncached path.
282    pub const ROOM_UNCACHED_DRAWS: u16 = 35;
283    /// Remaining primitive packet slots at the end of scene emission.
284    pub const TRI_PRIMITIVE_REMAINING: u16 = 36;
285    /// Cached room cell headers resident in the active chunk window.
286    pub const ROOM_CACHE_CELLS: u16 = 37;
287    /// Cached room vertices resident in the active chunk window.
288    pub const ROOM_CACHE_VERTICES: u16 = 38;
289    /// Cached room surfaces resident in the active chunk window.
290    pub const ROOM_CACHE_SURFACES: u16 = 39;
291    /// Active room/chunk draws that fell back because surface caching failed.
292    pub const ROOM_CACHE_FALLBACK_DRAWS: u16 = 40;
293    /// Active room/chunk draws that fell back because visibility cells were unavailable.
294    pub const ROOM_VISIBILITY_FALLBACK_DRAWS: u16 = 41;
295    /// Room cells rejected by the global player/camera range gate.
296    pub const ROOM_CELLS_RANGE_CULLED: u16 = 42;
297    /// Candidate chunks that were within activation range this frame.
298    pub const ROOM_CHUNKS_CONSIDERED: u16 = 43;
299    /// Candidate chunks skipped because the active cache budget was full.
300    pub const ROOM_CHUNK_CACHE_SKIPS: u16 = 44;
301    /// Active room/chunk windows rebuilt.
302    pub const ROOM_WINDOW_REBUILDS: u16 = 45;
303    /// Active chunks successfully built during room-window rebuilds.
304    pub const ROOM_WINDOW_BUILT_CHUNKS: u16 = 46;
305    /// Runtime room surface caches built.
306    pub const ROOM_SURFACE_CACHE_BUILDS: u16 = 47;
307    /// Cells emitted while building runtime room surface caches.
308    pub const ROOM_SURFACE_CACHE_BUILD_CELLS: u16 = 48;
309    /// Vertices emitted while building runtime room surface caches.
310    pub const ROOM_SURFACE_CACHE_BUILD_VERTICES: u16 = 49;
311    /// Surfaces emitted while building runtime room surface caches.
312    pub const ROOM_SURFACE_CACHE_BUILD_SURFACES: u16 = 50;
313    /// Room texture uploads performed.
314    pub const ROOM_TEXTURE_UPLOADS: u16 = 51;
315    /// Model atlas uploads performed.
316    pub const MODEL_ATLAS_UPLOADS: u16 = 52;
317    /// Fixed simulation/control ticks run by the cadence layer.
318    pub const SIM_TICKS: u16 = 53;
319    /// Rendered visual frames produced by the cadence layer.
320    pub const VISUAL_FRAMES: u16 = 54;
321    /// Visual VBlank slots intentionally held/skipped instead of rendered.
322    pub const VISUAL_SKIPPED_VBLANKS: u16 = 55;
323    /// Visual frames that missed their target cadence slot.
324    pub const VISUAL_DEADLINE_MISSES: u16 = 56;
325    /// Configured visual cadence interval in VBlanks.
326    pub const VISUAL_INTERVAL_VBLANKS: u16 = 57;
327    /// Worst observed lateness for a visual frame in VBlanks.
328    pub const VISUAL_MAX_LATENESS_VBLANKS: u16 = 58;
329    /// Bytes read by the editor-playtest CD streaming benchmark.
330    pub const CD_STREAM_BENCH_BYTES: u16 = 59;
331    /// Sectors read by the editor-playtest CD streaming benchmark.
332    pub const CD_STREAM_BENCH_SECTORS: u16 = 60;
333    /// Poll-loop iterations spent waiting on CD/DMA readiness.
334    pub const CD_STREAM_BENCH_POLLS: u16 = 61;
335    /// FNV checksum observed over the streamed benchmark payload.
336    pub const CD_STREAM_BENCH_CHECKSUM: u16 = 62;
337    /// Expected FNV checksum for the streamed benchmark payload.
338    pub const CD_STREAM_BENCH_EXPECTED_CHECKSUM: u16 = 63;
339    /// Status code for the editor-playtest CD streaming benchmark.
340    pub const CD_STREAM_BENCH_STATUS: u16 = 64;
341    /// Bytes read during the steady-state CD streaming benchmark window.
342    pub const CD_STREAM_STEADY_BYTES: u16 = 65;
343    /// Sectors read during the steady-state CD streaming benchmark window.
344    pub const CD_STREAM_STEADY_SECTORS: u16 = 66;
345    /// Bytes read from WORLD.PAK during the CD streaming benchmark.
346    pub const CD_WORLD_PACK_BYTES: u16 = 67;
347    /// Sectors read from WORLD.PAK during the CD streaming benchmark.
348    pub const CD_WORLD_PACK_SECTORS: u16 = 68;
349    /// Chunk entries reported by the streamed WORLD.PAK header.
350    pub const CD_WORLD_PACK_CHUNKS: u16 = 69;
351    /// FNV checksum observed over streamed WORLD.PAK sectors.
352    pub const CD_WORLD_PACK_CHECKSUM: u16 = 70;
353    /// Status code for streamed WORLD.PAK validation.
354    pub const CD_WORLD_PACK_STATUS: u16 = 71;
355    /// Room chunk bytes loaded from WORLD.PAK resident slots.
356    pub const CD_ROOM_CHUNK_BYTES: u16 = 72;
357    /// Room chunk sectors read from WORLD.PAK resident slots.
358    pub const CD_ROOM_CHUNK_SECTORS: u16 = 73;
359    /// Room chunk slot loads issued against WORLD.PAK.
360    pub const CD_ROOM_CHUNK_LOADS: u16 = 74;
361    /// Room chunk slot loads served from an already-resident slot.
362    pub const CD_ROOM_CHUNK_HITS: u16 = 75;
363    /// Status code for streamed room chunk loading.
364    pub const CD_ROOM_CHUNK_STATUS: u16 = 76;
365    /// Stream scheduler requests considered for the active window.
366    pub const ROOM_STREAM_REQUESTS: u16 = 77;
367    /// Stream scheduler requests that were not resident yet.
368    pub const ROOM_STREAM_MISSES: u16 = 78;
369    /// Stream scheduler requests issued only as prefetch/lookahead.
370    pub const ROOM_STREAM_PREFETCH_REQUESTS: u16 = 79;
371    /// Resident room stream slots after scheduler processing.
372    pub const ROOM_STREAM_RESIDENT_SLOTS: u16 = 80;
373    /// Resident stream slots evicted to satisfy requests.
374    pub const ROOM_STREAM_EVICTIONS: u16 = 81;
375    /// Stream slot loads that failed validation or CD reads.
376    pub const ROOM_STREAM_FAILED_LOADS: u16 = 82;
377    /// Stream slot loads scheduled by the current window refresh.
378    pub const ROOM_STREAM_PENDING_LOADS: u16 = 83;
379    /// Unique cached room vertices projected by visible cells.
380    pub const ROOM_PROJECTED_VERTICES: u16 = 84;
381    /// Cycles spent on room-surface material lookup/setup.
382    pub const ROOM_SURF_MATERIAL_CYCLES: u16 = 85;
383    /// Cycles spent fetching/validating projected room-surface quads.
384    pub const ROOM_SURF_PROJECTED_CYCLES: u16 = 86;
385    /// Cycles spent on room-surface screen culling.
386    pub const ROOM_SURF_SCREEN_CYCLES: u16 = 87;
387    /// Cycles spent classifying room-surface kind.
388    pub const ROOM_SURF_KIND_CYCLES: u16 = 88;
389    /// Cycles spent on room-surface backface culling.
390    pub const ROOM_SURF_BACKFACE_CYCLES: u16 = 89;
391    /// Cycles spent selecting baked/lit room-surface vertex colors.
392    pub const ROOM_SURF_LIGHTING_CYCLES: u16 = 90;
393    /// Cycles spent submitting room-surface packets/commands.
394    pub const ROOM_SURF_SUBMIT_CYCLES: u16 = 91;
395    /// Room surfaces sampled by the micro-profiler.
396    pub const ROOM_SURF_PROFILED: u16 = 92;
397    /// Room surfaces with missing material records.
398    pub const ROOM_SURF_MATERIAL_MISSES: u16 = 93;
399    /// Room surfaces rejected by projected-quad validity checks.
400    pub const ROOM_SURF_PROJECTED_REJECTS: u16 = 94;
401    /// Room surfaces culled by screen bounds.
402    pub const ROOM_SURF_SCREEN_CULLED: u16 = 95;
403    /// Room surfaces culled by backface tests.
404    pub const ROOM_SURF_BACKFACE_CULLED: u16 = 96;
405    /// Room floor surfaces sampled by the micro-profiler.
406    pub const ROOM_SURF_FLOORS: u16 = 97;
407    /// Room ceiling surfaces sampled by the micro-profiler.
408    pub const ROOM_SURF_CEILINGS: u16 = 98;
409    /// Room wall surfaces sampled by the micro-profiler.
410    pub const ROOM_SURF_WALLS: u16 = 99;
411    /// Whole-quad room surfaces sampled by the micro-profiler.
412    pub const ROOM_SURF_WHOLE_QUADS: u16 = 100;
413    /// Split-triangle room surfaces sampled by the micro-profiler.
414    pub const ROOM_SURF_SPLIT_TRIS: u16 = 101;
415    /// Room surfaces where color selection returned no drawable colors.
416    pub const ROOM_SURF_LIGHTING_REJECTS: u16 = 102;
417    /// Cycles spent checking cached room triangle hardware safety.
418    pub const ROOM_SUBMIT_HW_SAFE_TEST_CYCLES: u16 = 103;
419    /// Cycles spent building cached room triangle packet values.
420    pub const ROOM_SUBMIT_PACKET_FILL_CYCLES: u16 = 104;
421    /// Cycles spent pushing cached room triangle packets into primitive storage.
422    pub const ROOM_SUBMIT_PRIMITIVE_PUSH_CYCLES: u16 = 105;
423    /// Cycles spent calculating cached room triangle depth/order keys.
424    pub const ROOM_SUBMIT_DEPTH_CYCLES: u16 = 106;
425    /// Cycles spent pushing cached room triangle world commands.
426    pub const ROOM_SUBMIT_COMMAND_CYCLES: u16 = 107;
427    /// Cycles spent in cached room triangle fallback split/general path.
428    pub const ROOM_SUBMIT_FALLBACK_CYCLES: u16 = 108;
429    /// Cached room triangle submits that used the hardware-safe fast path.
430    pub const ROOM_SUBMIT_HW_SAFE_CALLS: u16 = 109;
431    /// Cached room triangle submits that used the split/general fallback path.
432    pub const ROOM_SUBMIT_FALLBACK_CALLS: u16 = 110;
433    /// Cached room triangle submits rejected by command-buffer capacity.
434    pub const ROOM_SUBMIT_COMMAND_OVERFLOWS: u16 = 111;
435    /// Cached room triangle submits rejected by primitive-buffer capacity.
436    pub const ROOM_SUBMIT_PRIMITIVE_OVERFLOWS: u16 = 112;
437    /// Guest cycles spent rendering runtime model slot 0.
438    pub const MODEL_PROFILE_CYCLES_0: u16 = 113;
439    /// Guest cycles spent rendering runtime model slot 1.
440    pub const MODEL_PROFILE_CYCLES_1: u16 = 114;
441    /// Guest cycles spent rendering runtime model slot 2.
442    pub const MODEL_PROFILE_CYCLES_2: u16 = 115;
443    /// Guest cycles spent rendering runtime model slot 3.
444    pub const MODEL_PROFILE_CYCLES_3: u16 = 116;
445    /// Guest cycles spent rendering runtime model slot 4.
446    pub const MODEL_PROFILE_CYCLES_4: u16 = 117;
447    /// Guest cycles spent rendering runtime model slot 5.
448    pub const MODEL_PROFILE_CYCLES_5: u16 = 118;
449    /// Guest cycles spent rendering runtime model slot 6.
450    pub const MODEL_PROFILE_CYCLES_6: u16 = 119;
451    /// Guest cycles spent rendering runtime model slot 7.
452    pub const MODEL_PROFILE_CYCLES_7: u16 = 120;
453    /// Runtime model slot 0 draw submits.
454    pub const MODEL_PROFILE_DRAWS_0: u16 = 121;
455    /// Runtime model slot 1 draw submits.
456    pub const MODEL_PROFILE_DRAWS_1: u16 = 122;
457    /// Runtime model slot 2 draw submits.
458    pub const MODEL_PROFILE_DRAWS_2: u16 = 123;
459    /// Runtime model slot 3 draw submits.
460    pub const MODEL_PROFILE_DRAWS_3: u16 = 124;
461    /// Runtime model slot 4 draw submits.
462    pub const MODEL_PROFILE_DRAWS_4: u16 = 125;
463    /// Runtime model slot 5 draw submits.
464    pub const MODEL_PROFILE_DRAWS_5: u16 = 126;
465    /// Runtime model slot 6 draw submits.
466    pub const MODEL_PROFILE_DRAWS_6: u16 = 127;
467    /// Runtime model slot 7 draw submits.
468    pub const MODEL_PROFILE_DRAWS_7: u16 = 128;
469    /// Low 32 bits of the resident streamed room/chunk bitset.
470    pub const ROOM_STREAM_RESIDENT_MASK_LO: u16 = 129;
471    /// High 32 bits of the resident streamed room/chunk bitset.
472    pub const ROOM_STREAM_RESIDENT_MASK_HI: u16 = 130;
473    /// Low 32 bits of the active drawable room/chunk bitset.
474    pub const ROOM_ACTIVE_CHUNK_MASK_LO: u16 = 131;
475    /// High 32 bits of the active drawable room/chunk bitset.
476    pub const ROOM_ACTIVE_CHUNK_MASK_HI: u16 = 132;
477    /// Low 32 bits of the room/chunk bitset that submitted room geometry.
478    pub const ROOM_DRAWN_CHUNK_MASK_LO: u16 = 133;
479    /// High 32 bits of the room/chunk bitset that submitted room geometry.
480    pub const ROOM_DRAWN_CHUNK_MASK_HI: u16 = 134;
481    /// Runtime room/chunk index containing the player.
482    pub const ROOM_PLAYER_ROOM_INDEX: u16 = 135;
483    /// Player room-local X, biased for unsigned telemetry transport.
484    pub const ROOM_PLAYER_LOCAL_X_BIASED: u16 = 136;
485    /// Player room-local Z, biased for unsigned telemetry transport.
486    pub const ROOM_PLAYER_LOCAL_Z_BIASED: u16 = 137;
487    /// Camera/view yaw used by player-centred chunk diagnostics, in Q12 angle units.
488    pub const ROOM_PLAYER_VIEW_YAW_Q12: u16 = 138;
489    /// Current room used as the root of portal traversal.
490    pub const PORTAL_VIS_CURRENT_ROOM: u16 = 139;
491    /// Runtime rooms accepted by portal traversal.
492    pub const PORTAL_VIS_VISIBLE_ROOMS: u16 = 140;
493    /// Rooms one portal beyond the visible set.
494    pub const PORTAL_VIS_FRONTIER_ROOMS: u16 = 141;
495    /// Portal frustums accepted by the runtime traversal.
496    pub const PORTAL_VIS_FRUSTUMS: u16 = 142;
497    /// Directed portals tested by the runtime traversal.
498    pub const PORTAL_VIS_PORTALS_TESTED: u16 = 143;
499    /// Directed portals accepted by the runtime traversal.
500    pub const PORTAL_VIS_PORTALS_ACCEPTED: u16 = 144;
501    /// Portals rejected by source-facing backface tests.
502    pub const PORTAL_VIS_REJECT_BACKFACE: u16 = 145;
503    /// Portals rejected by camera/window clipping.
504    pub const PORTAL_VIS_REJECT_FRUSTUM: u16 = 146;
505    /// Portals rejected because the clipped cone was tiny.
506    pub const PORTAL_VIS_REJECT_TINY: u16 = 147;
507    /// Visible-room pool capacity hits.
508    pub const PORTAL_VIS_CAP_ROOM: u16 = 148;
509    /// Frustum pool capacity hits.
510    pub const PORTAL_VIS_CAP_FRUSTUM: u16 = 149;
511    /// Portal traversal max-depth hits.
512    pub const PORTAL_VIS_CAP_DEPTH: u16 = 150;
513    /// Portal-accepted rooms neither resident nor loading when the active window was built.
514    pub const PORTAL_VIS_VISIBLE_MISSING_RESIDENT: u16 = 151;
515    /// Stream priority requests for the current room.
516    pub const ROOM_STREAM_PRIORITY_CURRENT: u16 = 152;
517    /// Stream priority requests for portal-accepted rooms.
518    pub const ROOM_STREAM_PRIORITY_VISIBLE: u16 = 153;
519    /// Stream priority requests for portal-frontier rooms.
520    pub const ROOM_STREAM_PRIORITY_FRONTIER: u16 = 154;
521    /// Stream loads blocked because resident/requested rooms filled the pool.
522    pub const ROOM_STREAM_PROTECTED_FULL: u16 = 155;
523    /// Low 32 bits of the portal-accepted room bitset.
524    pub const PORTAL_VIS_VISIBLE_MASK_LO: u16 = 156;
525    /// High 32 bits of the portal-visible room bitset.
526    pub const PORTAL_VIS_VISIBLE_MASK_HI: u16 = 157;
527    /// Low 32 bits of the portal-frontier room bitset.
528    pub const PORTAL_VIS_FRONTIER_MASK_LO: u16 = 158;
529    /// High 32 bits of the portal-frontier room bitset.
530    pub const PORTAL_VIS_FRONTIER_MASK_HI: u16 = 159;
531    /// Low 32 bits of the visible-but-missing-residency room bitset.
532    pub const PORTAL_VIS_MISSING_MASK_LO: u16 = 160;
533    /// High 32 bits of the visible-but-missing-residency room bitset.
534    pub const PORTAL_VIS_MISSING_MASK_HI: u16 = 161;
535    /// Render camera room-local X, biased for unsigned telemetry transport.
536    pub const ROOM_CAMERA_LOCAL_X_BIASED: u16 = 162;
537    /// Render camera room-local Z, biased for unsigned telemetry transport.
538    pub const ROOM_CAMERA_LOCAL_Z_BIASED: u16 = 163;
539    /// Low 32 bits of destination rooms for portals tested this frame.
540    pub const PORTAL_VIS_TESTED_MASK_LO: u16 = 164;
541    /// High 32 bits of destination rooms for portals tested this frame.
542    pub const PORTAL_VIS_TESTED_MASK_HI: u16 = 165;
543    /// Low 32 bits of destination rooms for accepted portals this frame.
544    pub const PORTAL_VIS_ACCEPTED_MASK_LO: u16 = 166;
545    /// High 32 bits of destination rooms for accepted portals this frame.
546    pub const PORTAL_VIS_ACCEPTED_MASK_HI: u16 = 167;
547    /// Low 32 bits of destination rooms rejected by portal window clipping.
548    pub const PORTAL_VIS_REJECT_FRUSTUM_MASK_LO: u16 = 168;
549    /// High 32 bits of destination rooms rejected by portal window clipping.
550    pub const PORTAL_VIS_REJECT_FRUSTUM_MASK_HI: u16 = 169;
551    /// Portals recovered by occupied-room-bounds fallback.
552    pub const PORTAL_VIS_BOUNDS_FALLBACKS: u16 = 170;
553    /// Low 32 bits of destination rooms recovered by occupied-room-bounds fallback.
554    pub const PORTAL_VIS_BOUNDS_FALLBACK_MASK_LO: u16 = 171;
555    /// High 32 bits of destination rooms recovered by occupied-room-bounds fallback.
556    pub const PORTAL_VIS_BOUNDS_FALLBACK_MASK_HI: u16 = 172;
557    /// Effective resident streamed room slot limit for the current window.
558    pub const ROOM_STREAM_SLOT_LIMIT: u16 = 173;
559    /// Low 32 bits of rooms with in-flight streamed loads.
560    pub const ROOM_STREAM_LOADING_MASK_LO: u16 = 174;
561    /// High 32 bits of rooms with in-flight streamed loads.
562    pub const ROOM_STREAM_LOADING_MASK_HI: u16 = 175;
563    /// Portal-accepted rooms resident in the stream cache but not buildable.
564    pub const PORTAL_VIS_VISIBLE_BUILD_FAILED: u16 = 176;
565    /// Low 32 bits of visible resident rooms that failed active-room build.
566    pub const PORTAL_VIS_BUILD_FAILED_MASK_LO: u16 = 177;
567    /// High 32 bits of visible resident rooms that failed active-room build.
568    pub const PORTAL_VIS_BUILD_FAILED_MASK_HI: u16 = 178;
569    /// Low 32 bits of directed portal records tested this frame.
570    pub const PORTAL_VIS_TESTED_PORTAL_MASK_LO: u16 = 179;
571    /// High 32 bits of directed portal records tested this frame.
572    pub const PORTAL_VIS_TESTED_PORTAL_MASK_HI: u16 = 180;
573    /// Low 32 bits of directed portal records accepted this frame.
574    pub const PORTAL_VIS_ACCEPTED_PORTAL_MASK_LO: u16 = 181;
575    /// High 32 bits of directed portal records accepted this frame.
576    pub const PORTAL_VIS_ACCEPTED_PORTAL_MASK_HI: u16 = 182;
577    /// Low 32 bits of directed portal records rejected by camera/window clipping.
578    pub const PORTAL_VIS_REJECT_FRUSTUM_PORTAL_MASK_LO: u16 = 183;
579    /// High 32 bits of directed portal records rejected by camera/window clipping.
580    pub const PORTAL_VIS_REJECT_FRUSTUM_PORTAL_MASK_HI: u16 = 184;
581    /// Low 32 bits of directed portal records accepted by occupied-bounds fallback.
582    pub const PORTAL_VIS_BOUNDS_FALLBACK_PORTAL_MASK_LO: u16 = 185;
583    /// High 32 bits of directed portal records accepted by occupied-bounds fallback.
584    pub const PORTAL_VIS_BOUNDS_FALLBACK_PORTAL_MASK_HI: u16 = 186;
585    /// Render camera yaw sine in Q12, biased by 4096 for unsigned transport.
586    pub const ROOM_CAMERA_VIEW_SIN_YAW_Q12_BIASED: u16 = 187;
587    /// Render camera yaw cosine in Q12, biased by 4096 for unsigned transport.
588    pub const ROOM_CAMERA_VIEW_COS_YAW_Q12_BIASED: u16 = 188;
589    /// Render camera room-local Y, biased for unsigned telemetry transport.
590    pub const ROOM_CAMERA_LOCAL_Y_BIASED: u16 = 189;
591    /// Render camera pitch sine in Q12, biased by 4096 for unsigned transport.
592    pub const ROOM_CAMERA_VIEW_SIN_PITCH_Q12_BIASED: u16 = 190;
593    /// Render camera pitch cosine in Q12, biased by 4096 for unsigned transport.
594    pub const ROOM_CAMERA_VIEW_COS_PITCH_Q12_BIASED: u16 = 191;
595    /// Render camera absolute level X used by portal traversal, biased for unsigned transport.
596    pub const ROOM_CAMERA_GLOBAL_X_BIASED: u16 = 192;
597    /// Render camera absolute level Y used by portal traversal, biased for unsigned transport.
598    pub const ROOM_CAMERA_GLOBAL_Y_BIASED: u16 = 193;
599    /// Render camera absolute level Z used by portal traversal, biased for unsigned transport.
600    pub const ROOM_CAMERA_GLOBAL_Z_BIASED: u16 = 194;
601    /// Model vertices projected through CPU blend skinning.
602    pub const TEXTURED_MODEL_CPU_BLEND_VERTICES: u16 = 195;
603    /// Model faces handled by any packed fast-path helper.
604    pub const TEXTURED_MODEL_PACKED_FACE_CALLS: u16 = 196;
605    /// Model faces handled by the packed all-front/all-HW-bounds helper.
606    pub const TEXTURED_MODEL_PACKED_UNCLAMPED_CALLS: u16 = 197;
607    /// Model faces handled by packed all-front helpers that still clamp screen coordinates.
608    pub const TEXTURED_MODEL_PACKED_CLAMPED_CALLS: u16 = 198;
609    /// Model faces handled by the generic packed helper.
610    pub const TEXTURED_MODEL_PACKED_GENERAL_CALLS: u16 = 199;
611    /// Model faces handled by the fully general face path.
612    pub const TEXTURED_MODEL_FALLBACK_FACE_CALLS: u16 = 200;
613    /// Packed model faces that fell back to split/general submission due hardware extents.
614    pub const TEXTURED_MODEL_HW_EXTENT_FALLBACKS: u16 = 201;
615    /// Model faces dropped because they crossed or sat behind the near plane.
616    pub const TEXTURED_MODEL_NEAR_DROPS: u16 = 202;
617    /// Model faces dropped because the projected triangle was not hardware-safe.
618    pub const TEXTURED_MODEL_HW_UNSAFE_DROPS: u16 = 203;
619    /// Model triangles submitted through packed fast paths.
620    pub const TEXTURED_MODEL_FAST_SUBMITTED_TRIS: u16 = 204;
621    /// Model submits that required CPU blended vertices.
622    pub const TEXTURED_MODEL_CPU_BLEND_SUBMITS: u16 = 205;
623    /// Model submits that used primary-joint-only projection.
624    pub const TEXTURED_MODEL_PRIMARY_JOINT_SUBMITS: u16 = 206;
625    /// Model submits where all projected vertices were in front of the near plane.
626    pub const TEXTURED_MODEL_ALL_FRONT_SUBMITS: u16 = 207;
627    /// Model submits where all projected vertices were inside PS1 hardware bounds.
628    pub const TEXTURED_MODEL_ALL_HW_BOUNDS_SUBMITS: u16 = 208;
629    /// Model submits eligible for the fastest packed-unclamped face path.
630    pub const TEXTURED_MODEL_PACKED_UNCLAMPED_ELIGIBLE_SUBMITS: u16 = 209;
631    /// Model submits eligible for packed all-front helpers.
632    pub const TEXTURED_MODEL_PACKED_CLAMPED_ELIGIBLE_SUBMITS: u16 = 210;
633    /// Model submits eligible for the generic packed helper only.
634    pub const TEXTURED_MODEL_PACKED_GENERAL_ELIGIBLE_SUBMITS: u16 = 211;
635    /// Model triangles emitted by split/general fallback submission.
636    pub const TEXTURED_MODEL_SPLIT_TRIS: u16 = 212;
637    /// Model triangles skipped because face indices exceeded projected vertex ranges.
638    pub const TEXTURED_MODEL_SKIPPED_TRIS: u16 = 213;
639    /// Model submits that exceeded the projected vertex scratch buffer.
640    pub const TEXTURED_MODEL_VERTEX_OVERFLOW_SUBMITS: u16 = 214;
641    /// Model submits that exceeded primitive packet storage.
642    pub const TEXTURED_MODEL_PRIMITIVE_OVERFLOW_SUBMITS: u16 = 215;
643    /// Model submits that exceeded world-command storage.
644    pub const TEXTURED_MODEL_COMMAND_OVERFLOW_SUBMITS: u16 = 216;
645    /// Room-texture VRAM slots freed by residency eviction (Stage 4 teardown).
646    pub const VRAM_SLOTS_FREED: u16 = 217;
647    /// Texture uploads that found no free `VRAM_SLOTS` entry: the 64-slot
648    /// residency table cap, the binding VRAM budget for distinct resident
649    /// textures. A non-zero value during traversal is the silent missing-texture
650    /// root cause.
651    pub const VRAM_SLOT_TABLE_FULL: u16 = 218;
652    /// Room-texture uploads where `alloc_window` found no free space in the
653    /// room-material page band (the secondary 4bpp window cap).
654    pub const VRAM_WINDOW_FULL: u16 = 219;
655    /// Texture uploads where `alloc_clut` found no free CLUT slot (the CLUT band cap).
656    pub const VRAM_CLUT_FULL: u16 = 220;
657    /// Room-texture uploads skipped because the in-flight VRAM upload queue had
658    /// no free slot (the upload-queue depth throttle, another silent skip).
659    pub const VRAM_UPLOAD_QUEUE_FULL: u16 = 221;
660    /// Room materials left untextured because their texture failed to become
661    /// VRAM-resident (a real drop, not merely a pending upload): the silent
662    /// missing-texture fallback at material-build time.
663    pub const ROOM_MATERIAL_TEXTURE_DROPS: u16 = 222;
664    /// Room materials dropped because their `local_slot` is >= MAX_ROOM_MATERIALS,
665    /// i.e. the room references more distinct materials than the per-room material
666    /// table can hold. Every surface using a dropped slot renders untextured or
667    /// not at all. A non-zero value means the per-room material cap is too small
668    /// for the cooked room (raise MAX_ROOM_MATERIALS or reduce the room's
669    /// material count). This was the silent root cause of the demo10 invisible
670    /// frieze/stairs.
671    pub const ROOM_MATERIAL_SLOT_OVERFLOW: u16 = 223;
672    /// Game entities that ran their state machine this tick (the
673    /// phase-3 per-tick "thinker" count; budget target <= 8).
674    pub const GAME_ENTITIES_THOUGHT: u16 = 224;
675    /// Game-entity transitions INTO Patrol since spawn.
676    pub const GAME_ENTITY_PATROL_ENTERS: u16 = 225;
677    /// Game-entity transitions INTO Aggro since spawn.
678    pub const GAME_ENTITY_AGGRO_ENTERS: u16 = 226;
679    /// Game-entity transitions INTO Windup since spawn.
680    pub const GAME_ENTITY_WINDUP_ENTERS: u16 = 227;
681    /// Game-entity transitions INTO Attack since spawn (the souls
682    /// commit; contact resolves through the combat counters below).
683    pub const GAME_ENTITY_ATTACK_ENTERS: u16 = 228;
684    /// Logic records fired since init (LogicRuntime rolling total).
685    pub const LOGIC_RECORDS_FIRED: u16 = 229;
686    /// Game-entity poise breaks (transitions INTO Staggered) from
687    /// player hits (phase-3 combat slice).
688    pub const GAME_ENTITY_STAGGER_ENTERS: u16 = 230;
689    /// Game-entity deaths from player hits.
690    pub const GAME_ENTITY_DEATHS: u16 = 231;
691    /// Player melee-arc swings that connected with an entity.
692    pub const PLAYER_MELEE_HITS: u16 = 232;
693    /// Entity attacks that connected with the player (i-framed and
694    /// out-of-arc swings whiff and do not count).
695    pub const PLAYER_HITS_TAKEN: u16 = 233;
696
697    /// Live bytes in the persistent gameplay asset pool. Makes the difference
698    /// between whole-level and neighbourhood-scoped residency observable, which
699    /// is the only way to tell that asset paging is doing anything.
700    pub const PERSISTENT_ASSET_RESIDENT_BYTES: u16 = 234;
701
702    /// Times the persistent asset pool refused an allocation. Non-zero means the
703    /// cooked budget is too small for the neighbourhood, or the pool fragmented
704    /// past the point first-fit can serve. The visible symptom is otherwise a
705    /// texture that silently never loads, so this must stay at zero.
706    pub const PERSISTENT_ASSET_LOAD_FAILURES: u16 = 235;
707
708    /// Asset id of the FIRST persistent asset that failed to load, or
709    /// `u16::MAX` if the failure could not be attributed to one asset. Without
710    /// it `PERSISTENT_ASSET_LOAD_FAILURES` says only that something broke, and
711    /// the search space is every streamed asset in the level.
712    pub const PERSISTENT_ASSET_FAILED_ID: u16 = 236;
713
714    /// Why that asset failed: a `cd_stream` chunk status (0..11) when the
715    /// read reached the drive, or one of the `asset_streaming` reason codes
716    /// (100+) when it failed before any read was armed.
717    pub const PERSISTENT_ASSET_FAILED_REASON: u16 = 237;
718
719    /// Room surfaces whose exact TR subdivision predicate passed.
720    pub const ROOM_SURF_TR_SUBDIVISION_CANDIDATES: u16 = 238;
721
722    /// TR subdivision candidates that successfully emitted geometry.
723    pub const ROOM_SURF_TR_SUBDIVISION_SUBMITTED: u16 = 239;
724
725    /// Primitive packets emitted by room surface draws, excluding water,
726    /// props, actors, and other scene passes.
727    pub const ROOM_SURFACE_PACKETS: u16 = 240;
728
729    /// World commands emitted by room surface draws, excluding water, props,
730    /// actors, and other scene passes.
731    pub const ROOM_SURFACE_COMMANDS: u16 = 241;
732
733    /// Warp probe (read-only diagnostic, `room-surface-profile` only).
734    ///
735    /// Room surfaces bucketed by the closed-form predicted affine texture
736    /// error from `docs/texture-warping-2026-07-27.md`, cross-tabbed against
737    /// what the depth-band subdivision rule actually decided. Buckets are
738    /// `<1`, `1..2`, `2..4` and `>=4` calibrated texels.
739    ///
740    /// Error is reported as count / sum / max in 1/16 texel units rather than
741    /// as buckets: a first run bucketed it and put 99% of surfaces in the
742    /// top bin, which answers nothing. Sum and count give the mean; max gives
743    /// the worst case. `..._UNDER_1TX` is the one bucket worth keeping, since
744    /// "subdivided a surface that could not warp" is wasted primitives.
745    pub const ROOM_SURF_WARP_SUBDIVIDED_COUNT: u16 = 242;
746    /// Sum of predicted error over subdivided surfaces, 1/16 texel units.
747    pub const ROOM_SURF_WARP_SUBDIVIDED_SUM: u16 = 243;
748    /// Worst predicted error among subdivided surfaces, 1/16 texel units.
749    pub const ROOM_SURF_WARP_SUBDIVIDED_MAX: u16 = 244;
750    /// Subdivided surfaces predicted to warp under 1 texel: wasted work.
751    pub const ROOM_SURF_WARP_SUBDIVIDED_UNDER_1TX: u16 = 245;
752    /// Surfaces the depth-band rule left as authored polygons.
753    pub const ROOM_SURF_WARP_UNTOUCHED_COUNT: u16 = 246;
754    /// Sum of predicted error over untouched surfaces, 1/16 texel units.
755    pub const ROOM_SURF_WARP_UNTOUCHED_SUM: u16 = 247;
756    /// Worst predicted error among untouched surfaces, 1/16 texel units.
757    pub const ROOM_SURF_WARP_UNTOUCHED_MAX: u16 = 248;
758    /// Untouched surfaces predicted to warp under 1 texel: correctly skipped.
759    pub const ROOM_SURF_WARP_UNTOUCHED_UNDER_1TX: u16 = 249;
760
761    /// E2 (docs/engine-30fps-architecture-2026-07-26.md): cycles rebuilding the
762    /// per-surface `WorldSurfaceOptions` variants. This sits between the timed
763    /// lighting and submit sections, i.e. inside the ~48% of room_surface_draw
764    /// that no existing counter attributed.
765    pub const ROOM_SURF_OPTIONS_CYCLES: u16 = 250;
766
767    /// E2: per-cell setup ahead of the surface loop (tile depth options plus
768    /// the submit-depth table), charged once per accepted cell.
769    pub const ROOM_SURF_CELL_SETUP_CYCLES: u16 = 251;
770
771    /// E2: the whole per-surface draw call. `call - sum(inner sections)` is the
772    /// surface body no counter reaches; `stage - cell_setup - call` is the
773    /// loop's own overhead. Together these close the attribution.
774    pub const ROOM_SURF_CALL_CYCLES: u16 = 252;
775
776    /// Player attack actions that actually started (an accepted light,
777    /// heavy, or combo press; presses swallowed by locks or hit-stun do
778    /// not count). Lets headless combat gates assert attempt counts
779    /// independently of whether the swings later connect.
780    pub const PLAYER_ATTACK_STARTS: u16 = 253;
781
782    /// Player death events, any cause (combat damage reaching zero, BSP
783    /// liquid hazards, lethal water). Counted when the death sequence is
784    /// armed, not when the respawn completes.
785    pub const PLAYER_DEATHS: u16 = 254;
786
787    /// Checkpoint activations that assigned a NEW in-memory checkpoint
788    /// value. The logic-graph CHECKPOINT dispatch and the legacy direct
789    /// interactable path share one assignment point, so an activation is
790    /// never double-counted; re-activating the held checkpoint without
791    /// moving does not count again.
792    pub const PLAYER_CHECKPOINT_ACTIVATIONS: u16 = 255;
793
794    /// Player swing capsule contacts rejected by the one-hit-per-swing
795    /// mask: the authored-capsule entity gate took the already-hit
796    /// branch for an entity this swing had already connected with.
797    pub const PLAYER_DUPLICATE_HIT_REJECTIONS: u16 = 256;
798
799    /// DOOR logic records fired/activated at the runtime effect
800    /// dispatch (prompt, trigger volume, or relay chain alike).
801    pub const LOGIC_DOOR_ACTIVATIONS: u16 = 257;
802
803    /// Equipped player weapon resolved to its socket pose and submitted
804    /// for rendering for the FIRST time after a (re)spawn: one per life.
805    pub const PLAYER_WEAPON_ATTACHMENTS: u16 = 258;
806
807    /// Live game entities whose AI/body was suppressed this tick because
808    /// their leaf is outside the player's PVS row (BSP worlds only; one
809    /// count per suppressed entity per simulation tick).
810    pub const GAME_ENTITY_PVS_SUPPRESSIONS: u16 = 259;
811
812    /// BSP liquid hazard damage applications to the player (each lava or
813    /// slime cadence hit that subtracted health).
814    pub const PLAYER_LIQUID_DAMAGE_EVENTS: u16 = 260;
815    /// Player body facing (motor yaw) in Q12 angle units, sampled with the
816    /// player-map position counters. Lets headless runs check facing
817    /// against travel direction.
818    pub const PLAYER_FACING_YAW_Q12: u16 = 261;
819    /// World-space X of the player model's rendered forward axis (Q12,
820    /// biased by 4096): column 2 of the rotation the draw consumes.
821    pub const PLAYER_RENDER_FORWARD_X_Q12_BIASED: u16 = 262;
822    /// World-space Z of the player model's rendered forward axis (Q12,
823    /// biased by 4096).
824    pub const PLAYER_RENDER_FORWARD_Z_Q12_BIASED: u16 = 263;
825    /// Player animation state as its cooked action index
826    /// (`CharacterAnimationAction::to_index`), sampled every sim tick.
827    pub const PLAYER_ANIM_ACTION: u16 = 264;
828    /// Player feet Y (motor position), biased for unsigned transport like the
829    /// X/Z map counters.
830    pub const ROOM_PLAYER_LOCAL_Y_BIASED: u16 = 265;
831    /// Phase of the player's active clip in Q12 (0 = first frame, 4096 = last),
832    /// sampled where rendering samples it. Timing questions ("is the strike
833    /// where the constant says?") are answered from this, not from the source
834    /// clip in Blender: it accounts for the cook, playback speed and frame
835    /// range that the guest actually applies.
836    pub const PLAYER_ANIM_PHASE_Q12: u16 = 266;
837    }
838}
839
840/// Number of counter slots, including index zero for unknown/reserved ids:
841/// the highest counter id plus one ([`counter::MAX_ID`]). A counter id at or
842/// past this is dropped by the host.
843pub const COUNTER_COUNT: usize = counter::MAX_ID as usize + 1;
844
845#[cfg(test)]
846mod tests {
847    use super::*;
848
849    /// No id appears twice in a table: the host indexes slots by id, so a
850    /// duplicate would merge two stages into one row.
851    fn assert_unique(name: &str, ids: &[u16]) {
852        for (i, id) in ids.iter().enumerate() {
853            assert!(!ids[..i].contains(id), "{name}: id {id} declared twice");
854        }
855    }
856
857    #[test]
858    fn ids_are_unique_within_each_table() {
859        assert_unique("stage", stage::IDS);
860        assert_unique("task", task::IDS);
861        assert_unique("counter", counter::IDS);
862    }
863
864    #[test]
865    fn every_declared_id_has_a_description_and_unknown_ids_have_none() {
866        for &id in stage::IDS {
867            assert!(!stage_desc(id).is_empty(), "stage {id}");
868        }
869        for &id in task::IDS {
870            assert!(!task_desc(id).is_empty(), "task {id}");
871        }
872        for &id in counter::IDS {
873            assert!(!counter_desc(id).is_empty(), "counter {id}");
874        }
875        assert_eq!(stage_desc(0), "");
876        assert_eq!(stage_desc(u16::MAX), "");
877        assert_eq!(counter_desc(0), "");
878    }
879
880    #[test]
881    fn slot_counts_cover_every_id() {
882        // The host's arrays have STAGE_COUNT / COUNTER_COUNT / TASK_COUNT
883        // slots and drop an id outside them without a word.
884        for &id in stage::IDS {
885            assert!((id as usize) < STAGE_COUNT, "stage {id}");
886        }
887        for &id in counter::IDS {
888            assert!((id as usize) < COUNTER_COUNT, "counter {id}");
889        }
890        for &id in task::IDS {
891            assert!((id as usize) < TASK_COUNT, "task {id}");
892        }
893        // And they are exactly one past the highest id, not slack that would
894        // hide a missing bump: the old hand-written values were 52 and 267.
895        assert_eq!(STAGE_COUNT, 52);
896        assert_eq!(COUNTER_COUNT, 267);
897    }
898
899    #[test]
900    fn a_new_highest_id_raises_the_count_without_a_second_edit() {
901        // Same shape the macro generates, over a table whose maximum is not
902        // its last entry: the count follows the maximum.
903        const IDS: &[u16] = &[3, 90, 7];
904        const MAX: u16 = {
905            let mut max = 0;
906            let mut i = 0;
907            while i < IDS.len() {
908                if IDS[i] > max {
909                    max = IDS[i];
910                }
911                i += 1;
912            }
913            max
914        };
915        assert_eq!(MAX as usize + 1, 91);
916    }
917}