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}