Log the limit RivaTuner Statistics Server's profile holds, not the one the game asked for, when RTSS rounded it.

An RTSS without FramerateLimitDenominator gets a fractional limit rounded to whole frames (apply: 59.94 becomes 60),
but the line after the write named the rate the game asked for: "limits to 59.940 fps" while the profile held 60. A
play-test log would hide that RTSS limits above a 59.94 Hz monitor. When the written limit over its denominator
differs from the asked rate, the line now names both: "limits to 60/1 (60.000 fps; the game asked 59.940)". What is
written does not change.

tests/rtss_worker_check.c gains round_logged (an RTSS with the denominator: 59940/1000, logged as 59.940 fps) and
round_logged_whole (without it: 59.94 and 143.856 Hz logged as 60/1 and 144/1, a whole 60 as before). The second
failed before the change and passes after it; a compile of rtss_limit.c with -Wall passes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
chrg6
2026-11-13 17:16:23 -05:00
parent 07aca0170c
commit b2b37a3e5d
2 changed files with 34 additions and 2 deletions

View File

@@ -208,8 +208,12 @@ static int apply(int64_t mhz) {
}
applied = mhz;
update_profiles();
if (mhz) rt_log("frame rate: RivaTuner Statistics Server's profile %s limits to %.3f fps", profile, mhz / 1000.0);
else rt_log("frame rate: RivaTuner Statistics Server's profile %s has no limit (unlimited)", profile);
if (!mhz) rt_log("frame rate: RivaTuner Statistics Server's profile %s has no limit (unlimited)", profile);
else if ((int64_t)limit * 1000 == mhz * denominator)
rt_log("frame rate: RivaTuner Statistics Server's profile %s limits to %.3f fps", profile, mhz / 1000.0);
else // rounded to whole frames by an RTSS without FramerateLimitDenominator: the log says what RTSS holds
rt_log("frame rate: RivaTuner Statistics Server's profile %s limits to %lu/%lu (%.3f fps; the game asked %.3f)",
profile, limit, denominator, (double)limit / denominator, mhz / 1000.0);
return 1;
}

View File

@@ -424,6 +424,32 @@ static void active_race(void) {
CHECK(!hits, "the game's limiter stood aside for a kept-out want %ld times", hits);
}
// A limit of a fractional monitor (display at 59.94 and 143.856 Hz): an RTSS that knows the denominator holds it as it
// is, one that does not holds it rounded to whole frames, and the log says what the profile holds, not what was asked.
static void round_logged(void) {
app_set(1, 0xA, 1000, 2000, 60);
ask(59940);
poll();
show("display at 59.94 Hz");
CHECK(own.limit == 59940 && own.denom == 1000, "the denominator holds the fraction");
CHECK(logged("profile ocarina.exe limits to 59.940 fps\n"), "the exact limit as it is");
}
static void round_logged_whole(void) {
no_denominator = 1;
app_set(1, 0xA, 1000, 2000, 60);
ask(59940);
poll();
show("display at 59.94 Hz, RTSS without a denominator");
CHECK(own.limit == 60, "rounded to whole frames");
CHECK(logged("profile ocarina.exe limits to 60/1 (60.000 fps; the game asked 59.940)"), "the log names 60/1");
ask(143856);
poll();
CHECK(logged("profile ocarina.exe limits to 144/1 (144.000 fps; the game asked 143.856)"), "the log names 144/1");
ask(60000);
poll();
CHECK(logged("profile ocarina.exe limits to 60.000 fps\n"), "a whole limit as it is");
}
static const struct { const char *name; void (*run)(void); } scenarios[] = {
{"restore_race", restore_race},
{"restore_player", restore_player},
@@ -434,6 +460,8 @@ static const struct { const char *name; void (*run)(void); } scenarios[] = {
{"kept_lowlat_then_limit", kept_lowlat_then_limit},
{"kept_framegen", kept_framegen},
{"active_race", active_race},
{"round_logged", round_logged},
{"round_logged_whole", round_logged_whole},
};
int main(int argc, char **argv) {