Tie the stutter map's cause count to perf.c's at compile time, and rewrap the comment that lists the causes.

The review of d84aa5fb and c05e6528 found that nothing at compile time tied perf.c FT_C_N to stutter_map.c SM_CAUSES:
they sit in different translation units, perf.c ft_frame passes stutter_frame n = FT_C_N, and the areas' by_cause
counts and cause_short are indexed against SM_CAUSES, so a cause added on one side only would mislabel the map without
an error (tests/stutter_hitch_check.c checks only its own names[]). stutter_map.h now defines STUTTER_CAUSES,
stutter_map.c sizes SM_CAUSES from it, and perf.c asserts FT_C_N == STUTTER_CAUSES beside the FT_C_* list. With
STUTTER_CAUSES set to 11 the assertion stops perf.c's compile.

The review also found that the header comment line of stutter_map.c listing the causes ran to 142 columns, where the
file wraps at 120; the paragraph is rewrapped without a change of words.

No change to what runs. perf.c, stutter_map.c and the other files that include stutter_map.h (romfs.c, vk_exec_draw.c,
vk_pipeline.c) compile with the dev flags, perf.c and stutter_map.c add no -Wall -Wextra warnings, and
stutter_hitch_check and stutter_map_check pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
chrg6
2026-11-13 17:54:22 -05:00
parent 93be3e0665
commit a473e763b2
3 changed files with 12 additions and 8 deletions

View File

@@ -478,6 +478,7 @@ typedef struct {
// stutter_map.c cause_short and tools/stutter_map.py read these names, in this order.
enum { FT_C_PIPE, FT_C_XLATE, FT_C_TEX, FT_C_FENCE, FT_C_PACE, FT_C_HANDOFF, FT_C_REPLAY, FT_C_CENSUS, FT_C_REPORT,
FT_C_OTHER, FT_C_N };
_Static_assert(FT_C_N == STUTTER_CAUSES, "stutter_map.c cause_short and its counts follow FT_C_*");
static const char *const ft_cause_name[FT_C_N] = { "pipeline", "translation", "texture", "fence", "pacing", "hand-off",
"replay", "census", "report", "unattributed" };
static uint64_t census_part_ns; // present thread: the census walks since the last present (census_check)

View File

@@ -18,13 +18,13 @@
// 96 ms after an area load (2026-11-13) each counted as a change of pace before, as each was followed by one as long.
//
// What a hitch went to comes from the frame-time summary's split (perf.c ft_frame: pipeline, translation, texture,
// fence, pacing, hand-off, replay, census, report, unattributed), with the draws that met a pipeline or a shader missing between the previous
// present and this one (stutter_miss, from the replay thread). Each hitch gets a "stutter:" line, at most
// SM_LINES_PER_S a second and SM_LINES_MAX in all; the counts go on past that. Every SM_REPORT_NS of play, when the
// window loses the focus in play (perf.c session_report) and at exit,
// "stutter map" lines rank the areas by their hitches and the pipelines by the time draws lost to them, each with
// whether this install had recorded it before (the pipeline list or an earlier run) and whether one of its shaders was
// translated in play rather than prepared.
// fence, pacing, hand-off, replay, census, report, unattributed), with the draws that met a pipeline or a shader
// missing between the previous present and this one (stutter_miss, from the replay thread). Each hitch gets a
// "stutter:" line, at most SM_LINES_PER_S a second and SM_LINES_MAX in all; the counts go on past that. Every
// SM_REPORT_NS of play, when the window loses the focus in play (perf.c session_report) and at exit, "stutter map"
// lines rank the areas by their hitches and the pipelines by the time draws lost to them, each with whether this
// install had recorded it before (the pipeline list or an earlier run) and whether one of its shaders was translated
// in play rather than prepared.
//
// Threads: stutter_area runs on the game's loading threads, stutter_miss on the replay thread, stutter_ready on the
// pipeline workers (under vk_pipeline.c's P.lock; sm_lock is taken inside it and never takes it), stutter_frame and
@@ -42,7 +42,7 @@
int stutter_map_on;
#define SM_AREAS 128 // areas kept by name; later ones share the last slot
#define SM_CAUSES 10 // perf.c's FT_C_N
#define SM_CAUSES STUTTER_CAUSES // perf.c's FT_C_N, which perf.c asserts
#define SM_LOAD_NS 3000000000ull // after an area load, frames count as the load's for this long
#define SM_HITCH_MIN_NS 12000000.0 // a hitch is at least this long...
#define SM_HITCH_ANY_NS 25000000ull // ...and any frame over this is one

View File

@@ -22,6 +22,9 @@ void stutter_miss(int shader, int mesh, const uint8_t *vi, const uint8_t *fi, ui
void stutter_ready(int mesh, const uint8_t *vi, const uint8_t *fi, double ms, int recorded, int fresh, int fast);
// Every present (perf.c ft_frame): d the frame's interval, part the time it went to by cause (names[i], n of them;
// the last one is the part nothing recorded explains), cause the one the frame is charged to.
// STUTTER_CAUSES: the number of causes, perf.c's FT_C_N (perf.c asserts it), which stutter_map.c cause_short and the
// areas' counts are sized by; n is that many.
#define STUTTER_CAUSES 10
void stutter_frame(uint64_t frame, uint64_t now, uint64_t d, const uint64_t *part, const char *const *names, int n,
int cause);
void stutter_report(const char *tag); // the map so far; every 5 minutes of play, at a focus loss in play and at exit