Skip to content

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_H
  • AOWL_FT_MIN_N
  • AOWL_FT_NB
  • AOWL_FT_PCT_SAT
  • AOWL_FT_RING

Functions

SignatureLine
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

aoughwl — self-hosted platform for things n stuff. Contact / Support on Discord for access to the private backends.