Appearance
aowlspt_frametime.h
Source: abi/aowlspt_frametime.h — 241 lines, 27 file-scope functions.
What this header owns
Reproduced verbatim from the header's own banner comment — these notes are frequently the only written record of why the subsystem is shaped the way it is.
text
aowlspt_frametime.h -- THE FRAME METER. A frame-interval instrument that
depends on NOTHING under test.
WHY THIS EXISTS, measured. The client runs at ~9-10 fps in raid (110ms/frame
from the maps mod's frame counter; 100.7ms independently from natesp's own
gap counter) while the menu is ~37 fps. The obvious next step -- turn host
features off one at a time and watch the number -- was attempted twice and
BOTH attempts measured nothing, because every frame counter in the host lives
inside a feature:
attempt 1: 27 features off -> no frame data at all (the counters live in
natEspDiag and the maps diag; the instruments were disabled with
the subject).
attempt 2: natEsp off but natEspDiag on -> "the pane renderer has not run
yet (0 calls)", and no `raid phase =` line at all, because the
raid-phase latch is driven from the ESP path and the maps HUD
only draws once that latch reads DEPLOYED.
So frame instrumentation was TRANSITIVELY GATED on `natEsp`, and the one
experiment that matters -- what is the frame rate with the ESP off -- was
impossible to run. That is the gap this file closes.
WHAT IT HOOKS, and why that is independent of every feature. Exactly one
thing: `aowl_ft_tick()`, called from the FIRST statement of the
`i == gDrainSlot` branch of `patchFired` -- the host's own
`EFT.TarkovApplication::Update` drain, before `mainDrain()` and before every
rider. That drain is bound by the host bridge itself (`gDrainSlot`), not by
any feature flag; it ticks for the whole session, menu and raid alike; and it
is the same anchor every feature rides, so the interval it measures IS the
interval those features are charged against. It installs NO detour, resolves
NO name, and calls NOTHING in the game.
IT CANNOT FAULT, so it arms no SEH guard -- deliberately. Its entire working
set is the statics in this file plus `QueryPerformanceCounter`. It
dereferences no game pointer, allocates nothing (managed or otherwise) and
takes no lock. `aowl_p_p_seh` is not re-entrant, and the drain branch it is
called from is not inside one, so a guard here would buy nothing and could
only mislead a later reader into thinking a guard is required at that call
site. The steady-state cost is one QPC read, one 64-bit divide and ~20
integer ops -- tens of nanoseconds against a 110,000,000ns frame.
WHAT IT REPORTS: mean, p50, p95, max, a histogram and the SAMPLE COUNT, over
two windows -- LIFETIME (every tick since the flag came on) and RECENT (the
last AOWL_FT_RING intervals, re-bucketed on demand at report time so a reader
looking at the log during a raid gets the raid's number even though the
lifetime figure folds the menu in).
IT DOES NOT DISTINGUISH MENU FROM RAID, and says so in its own line. The only
feature-free menu/raid signals available on this build are the raid-phase
latch (driven from the ESP path -- the exact dependency this file exists to
escape) and `EFT.UI.PreloaderUI::Update` (menu-only, but every rider slot on
it is flag-gated, so with all features off it never fires). Inventing a third
would mean resolving something new. The RECENT window is the honest
substitute: it is a time-local number a human can read at a known moment.
NO SENTINEL IS EVER PRINTED AS A NUMBER. The predecessor meter printed
INT64_MAX/1000 as a "p95" -- a saturation rendered as a measurement, which is
a confidently wrong diagnostic. Here the edge table is entirely FINITE and
reaches 2s; samples above the top edge go to a separate overflow counter; and
a percentile that lands there returns AOWL_FT_PCT_SAT, which is NEGATIVE
precisely so no formatting path can mistake it for a duration. Three returns,
never two: a real edge, -1 for "no samples", or the saturation marker.
THREE OUTCOMES ON THE COUNT, never two. Below AOWL_FT_MIN_N samples the
caller is required to say INCONCLUSIVE. "Not enough samples" is not a pass.
There is deliberately NO budget and NO PASS/FAIL on the frame rate itself:
what counts as an acceptable frame time is the human's call, not the meter's.Constants
AOWLSPT_FRAMETIME_HAOWL_FT_MIN_NAOWL_FT_NBAOWL_FT_PCT_SATAOWL_FT_RING
Functions
| Signature | Line |
|---|---|
void aowl_ft_bucket_add(aowl_ft_bucket* b, int64_t ns) | 117 |
int64_t aowl_ft_pct(const aowl_ft_bucket* b, int64_t pct) | 128 |
void aowl_ft_tick(void) | 142 |
void aowl_ft_set_enabled(int32_t on) | 173 |
int32_t aowl_ft_enabled(void) | 179 |
int64_t aowl_ft_samples(void) | 182 |
int64_t aowl_ft_ticks_seen(void) | 183 |
int64_t aowl_ft_dropped(void) | 184 |
int64_t aowl_ft_mean_ns(void) | 185 |
int64_t aowl_ft_p50_ns(void) | 189 |
int64_t aowl_ft_p95_ns(void) | 190 |
int64_t aowl_ft_max_ns(void) | 191 |
int64_t aowl_ft_min_ns(void) | 192 |
int64_t aowl_ft_hist(int32_t i) | 193 |
int64_t aowl_ft_ovf(void) | 197 |
int64_t aowl_ft_recent(void) | 205 |
int64_t aowl_ft_r_mean_ns(void) | 220 |
int64_t aowl_ft_r_p50_ns(void) | 224 |
int64_t aowl_ft_r_p95_ns(void) | 225 |
int64_t aowl_ft_r_max_ns(void) | 226 |
int64_t aowl_ft_r_samples(void) | 227 |
int64_t aowl_ft_pct_sat(void) | 230 |
int64_t aowl_ft_top_edge(void) | 231 |
int32_t aowl_ft_bins(void) | 232 |
int64_t aowl_ft_edge(int32_t i) | 233 |
int64_t aowl_ft_min_samples(void) | 237 |
int32_t aowl_ft_ring_size(void) | 238 |

