mirror of
https://github.com/tangerineturtle1/NX2navi.git
synced 2026-11-15 19:19:58 -04:00
With frame generation and no frame limit chosen, limit to the display's refresh rate.
Since the pacer divides the limit by frame generation's factor, the default limit of 60 held the game to 30, 20 or 15 rendered frames a second at 2x, 3x or 4x on any monitor, 15 at DLSS 4x on a 165 Hz one. While AMD's or NVIDIA's frame generation swapchain is in use and neither fps_limit nor the older fps60 = 0 is set, the limit is now the monitor's rate, as with fps_limit = display: 4x on 165 Hz draws about 41 and shows 165. A limit the player set is kept, and the menu's Frame rate limit row shows the display choice while this applies. The mouse camera divided its input by the limit's frame ratio, which counts generated frames, so frame generation turned it faster by its factor; it now uses the rate the game draws at (frame_step_game_hz). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -36,7 +36,8 @@ Report problems as issues, following [CONTRIBUTING.md](CONTRIBUTING.md).
|
||||
GPUs (one, two or three generated frames for every frame the game draws), or AMD FSR 3.1 (any GPU), a generated
|
||||
frame between every two the game draws, with the HUD kept out of it, for about twice the frame rate on a fast
|
||||
display; in a window of another shape than the picture, frames are generated inside the picture only, so the black
|
||||
bars stay clean. With frame generation on, the frame limit counts the frames on screen. The frame limit can also be
|
||||
bars stay clean. With frame generation on, the frame limit counts the frames on screen, and with no limit chosen
|
||||
it is the display's refresh rate. The frame limit can also be
|
||||
handed to RivaTuner Statistics Server
|
||||
- HDR on HDR displays: the game's own native HDR, or its SDR picture tone mapped to the display's peak, with exposure, highlights, shadows, contrast, saturation and gamma in the menu
|
||||
- The game's known shaders prepared when the game is built, and the pipelines met in play built again in the
|
||||
|
||||
@@ -110,7 +110,8 @@ beside the game.
|
||||
background while you play.
|
||||
- **Screen** in the menu has Upscaling (DLSS on NVIDIA RTX cards, FSR 3.1 on any card), Frame generation (NVIDIA DLSS
|
||||
on RTX 40 and 50 cards, with a DLSS multiplier of 2x, or 3x and 4x on RTX 50 cards; FSR 3.1 on any card; it needs
|
||||
DLSS or FSR 3.1 upscaling on, and the frame limit then counts the frames on screen), HDR (the game's picture tone mapped to your display, with its own grading rows, or Native, the game's own HDR; turn HDR on in Windows first) and the
|
||||
DLSS or FSR 3.1 upscaling on, and the frame limit then counts the frames on screen; with no limit chosen it is the
|
||||
display's refresh rate), HDR (the game's picture tone mapped to your display, with its own grading rows, or Native, the game's own HDR; turn HDR on in Windows first) and the
|
||||
resolution. Those marked "after restart" take effect when the game starts again.
|
||||
- **Controls** in the menu has Button prompts: the game's own, or Xbox, PlayStation or keyboard and mouse ones when
|
||||
the build drew them (a kit with `prompt-art`). It takes effect after a restart. The keyboard and mouse prompts show
|
||||
|
||||
@@ -28,8 +28,9 @@ cards).
|
||||
- *Rebuild the Game* compiles the game for your current processor.
|
||||
- DLSS Frame Generation at 2x, 3x or 4x: the *DLSS multiplier* row under Frame generation in the settings menu
|
||||
(`framegen_multiplier`). 3x and 4x need a GeForce RTX 50 GPU and are not yet tested in play. The frame limit now
|
||||
counts the frames on screen: with 2x a limit of 60 shows 60 frames a second, not 120. RivaTuner Statistics Server
|
||||
stays out of the frame limit while DLSS Frame Generation runs.
|
||||
counts the frames on screen: with 2x a limit of 60 shows 60 frames a second, not 120. With no limit chosen, frame
|
||||
generation now uses the display's refresh rate, so on a 165 Hz monitor 4x draws about 41 frames a second and shows
|
||||
165. RivaTuner Statistics Server stays out of the frame limit while DLSS Frame Generation runs.
|
||||
|
||||
## Fixed
|
||||
|
||||
@@ -46,6 +47,8 @@ cards).
|
||||
video memory.
|
||||
- Shader preparation no longer crashes the RADV driver (AMD GPUs on Linux) on a texture size query.
|
||||
- Things that moved no longer leave a trail when neither DLSS nor FSR 3.1 is on.
|
||||
- With frame generation on, the mouse turned the camera as fast as if the game drew every frame on screen: twice as
|
||||
fast with FSR 3.1 frame generation.
|
||||
- The game's own Restart no longer brings the launcher back over the game, where Play could start a second copy.
|
||||
- A cut connection while the launcher reads a small file, such as the package index of *Gather Build Tools*, is tried
|
||||
again instead of failing the install.
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
# at the cost of tearing. A monitor running with variable refresh (FreeSync, G-Sync) follows the frames instead, so
|
||||
# there any limit inside its range is even.
|
||||
# With frame generation on (framegen, [display]) the limit counts the frames on screen, generated ones included: the
|
||||
# game draws the limit divided by the multiplier.
|
||||
# game draws the limit divided by the multiplier. Unset, the limit is then the monitor's refresh rate instead of 60, so
|
||||
# at 4x on a 165 Hz monitor the game draws about 41 fps and the screen shows 165; a limit set here is kept as it is.
|
||||
# fps_limit = 60
|
||||
# Where RivaTuner Statistics Server is installed and running, the limit goes into its profile for the game's
|
||||
# executable and RTSS does the limiting instead of the game; closing the game puts back what the profile held.
|
||||
@@ -319,7 +320,8 @@
|
||||
# kept out of it) or dlss (NVIDIA DLSS Frame Generation on GeForce RTX 40 and 50 GPUs, paced with Reflex; it needs
|
||||
# nx2navi_dlssg.dll and NVIDIA Streamline's files next to the executable, which the setup puts there). Both need DLSS or
|
||||
# FSR 3.1 upscaling (dlaa = 1). While frame generation runs, fps_limit counts the frames on screen, generated ones
|
||||
# included: the game draws the limit divided by the multiplier (2 for fsr3), so set the limit to the display's rate.
|
||||
# included: the game draws the limit divided by the multiplier (2 for fsr3). With fps_limit unset it is the display's
|
||||
# rate.
|
||||
# fsr3 takes effect at once, dlss (on or off) at the next start.
|
||||
# framegen = off
|
||||
# DLSS frame generation's multiplier: the frames on screen for each frame the game draws, 2, 3 or 4 (one, two or three
|
||||
|
||||
@@ -115,7 +115,8 @@ int frame_step_realtime(void) { return step_word != NULL; }
|
||||
|
||||
// The frame limit (menu, NX2NAVI_FPS_LIMIT, live): 30, 60, 90, 120, 144, any whole rate from 30 to 240, or display (the
|
||||
// refresh rate of the window's monitor, at most 240). A higher number gives 240, the rate of the shortest step the
|
||||
// game survives (STEP_MIN_S). Unset, the older NX2NAVI_FPS60=0 still means 30. A limit of 30 switches the set
|
||||
// game survives (STEP_MIN_S). Unset, the older NX2NAVI_FPS60=0 still means 30, and frame generation in use means display
|
||||
// (frame_limit_follows_display). A limit of 30 switches the set
|
||||
// fps60 off (the shipping minimum interval 2), every other limit switches it on; the controller reads its minimum
|
||||
// interval from the patched sites each frame. With the real-time step the pacer holds frames to the limit; above 60
|
||||
// only the pacer changes, as the step's floor is the limit's ceiling. Without it the game steps whole 60 Hz vsyncs and the pacer
|
||||
@@ -139,9 +140,11 @@ int frame_limit_adjustable(void) { return 1; }
|
||||
static unsigned limit_seen;
|
||||
static int limit_read;
|
||||
static int limit_hz = -1; // 0: display; -2: unlimited (free-running builds); -1 before the first read
|
||||
static int limit_unset; // neither NX2NAVI_FPS_LIMIT nor the older NX2NAVI_FPS60=0 is set: the default 60
|
||||
|
||||
static int limit_parse(void) {
|
||||
const char *e = cfg_get("NX2NAVI_FPS_LIMIT"), *old = cfg_get("NX2NAVI_FPS60");
|
||||
limit_unset = (!e || !*e) && !(old && !strcmp(old, "0"));
|
||||
if (!e || !*e) return old && !strcmp(old, "0") ? 30 : 60;
|
||||
if (!strcmp(e, "display")) return 0;
|
||||
int free_run = frame_free_running();
|
||||
@@ -204,6 +207,22 @@ static int movie_cap_hz(void) {
|
||||
// multiplier with NVIDIA's (vk_dlssg.c, one to three); 1 while neither generates (off, or paused for the menu).
|
||||
static int generated_factor(void) { return vk_fg_generating() ? 2 : vk_dlssg_multiplier(); }
|
||||
|
||||
// The limit in effect, as limit_hz counts it (0 display, -2 unlimited, else the rate). With frame generation's
|
||||
// swapchain in use and no limit chosen it is the display's rate: the pacer divides the limit by generated_factor, so
|
||||
// the default 60 held the game to 30, 20 or 15 rendered frames a second on any monitor, 15 at DLSS 4x on a 165 Hz one,
|
||||
// where the display rate gives 41 rendered and 165 shown. "In use" rather than generating keeps the limit still while
|
||||
// the menu pauses generation. A limit the player set is kept as it is.
|
||||
int frame_limit_follows_display(void) { return limit_unset && limit_hz > 0 && (vk_fg_active() || vk_dlssg_active()); }
|
||||
static int limit_effective(void) { return frame_limit_follows_display() ? 0 : limit_hz; }
|
||||
|
||||
// The rate the game renders at under the limit: frame_step_limit_hz over the frames shown per game frame (mouse_direct.c,
|
||||
// whose camera input follows the game's own frame time); 0 unknown.
|
||||
double frame_step_game_hz(void) {
|
||||
double hz = frame_step_limit_hz();
|
||||
int gen = generated_factor();
|
||||
return gen > 1 ? hz / gen : hz;
|
||||
}
|
||||
|
||||
// RivaTuner Statistics Server (rtss_limit.c) gets the same limit; while it limits this process the pacer holds no
|
||||
// frames, so the two never stack. NX2NAVI_RTSS=0 and frame generation (RTSS would count the generated frames) keep it out.
|
||||
uint64_t frame_step_cap_ns(void) {
|
||||
@@ -229,8 +248,16 @@ uint64_t frame_step_cap_ns(void) {
|
||||
return 0;
|
||||
}
|
||||
// unlimited during a movie: the movie's rate, times frame generation's factor for the division below
|
||||
double hz = limit_hz == -2 ? movie_hz * gen
|
||||
: shown_hz(limit_hz ? limit_hz : gfx_window_refresh_hz());
|
||||
int follow = frame_limit_follows_display(), limit = follow ? 0 : limit_hz;
|
||||
static int follow_was, follow_logs;
|
||||
if (follow != follow_was) {
|
||||
follow_was = follow;
|
||||
if (follow_logs++ < 20) {
|
||||
if (follow) rt_log("frame rate: frame generation with no limit set: the limit is the monitor's refresh rate");
|
||||
else rt_log("frame rate: limit %d fps again", limit_hz);
|
||||
}
|
||||
}
|
||||
double hz = limit == -2 ? movie_hz * gen : shown_hz(limit ? limit : gfx_window_refresh_hz());
|
||||
double max = frame_free_running() ? LIMIT_FREE_MAX_HZ : LIMIT_MAX_HZ;
|
||||
if (hz > max) hz = max;
|
||||
// Frame generation presents one to three generated frames between each two of the game's: the game is paced at the
|
||||
@@ -249,10 +276,11 @@ uint64_t frame_step_cap_ns(void) {
|
||||
double frame_step_limit_hz(void) {
|
||||
if (frame_free_running()) { // 0 for unlimited, as before the first read
|
||||
frame_limit_live();
|
||||
if (limit_hz == -2 || limit_hz == -1) return 0;
|
||||
return shown_hz(limit_hz ? limit_hz : gfx_window_refresh_hz());
|
||||
int limit = limit_effective();
|
||||
if (limit == -2 || limit == -1) return 0;
|
||||
return shown_hz(limit ? limit : gfx_window_refresh_hz());
|
||||
}
|
||||
int hz = limit_hz;
|
||||
int hz = limit_effective();
|
||||
if (hz < 0) return 0;
|
||||
double r = shown_hz(hz ? hz : gfx_window_refresh_hz());
|
||||
return !step_word && r > 60 ? 60 : r > LIMIT_MAX_HZ ? LIMIT_MAX_HZ : r;
|
||||
|
||||
@@ -317,6 +317,8 @@ int ov_movie_live(void); // ov_movie2.c: a mov
|
||||
int frame_limit_adjustable(void); // the frame limit setting changes the rate (always)
|
||||
int frame_free_running(void); // no fps60/realtime patch set: the game steps by the clock
|
||||
double frame_step_limit_hz(void); // the frame rate the limit gives (overlay); 0 unknown
|
||||
double frame_step_game_hz(void); // ... over frame generation's factor: the game's rate
|
||||
int frame_limit_follows_display(void); // no limit set and frame generation in use: the display's rate
|
||||
// rtss_limit.c: RivaTuner Statistics Server takes the frame limit where it runs
|
||||
void rtss_limit_want(int64_t mhz); // the pacer's limit in millihertz: >0, 0 none, -1 keep RTSS out
|
||||
int rtss_limit_active(void); // RTSS limits to the wanted rate: the pacer holds no frames
|
||||
|
||||
@@ -172,7 +172,7 @@ static void camera_input(Cpu *c) {
|
||||
float ratio = 1;
|
||||
if (frame_step_realtime()) memcpy(&ratio, rt_patch_page(), sizeof ratio);
|
||||
else {
|
||||
double hz = frame_step_limit_hz();
|
||||
double hz = frame_step_game_hz(); // frame generation's frames are not the game's
|
||||
if (hz > 0) ratio = (float)(30 / hz);
|
||||
}
|
||||
if (isfinite(ratio) && ratio > 0 && sensitivity > 0 && (dx || dy)) {
|
||||
|
||||
@@ -65,6 +65,7 @@ int vk_hdr_wanted(void);
|
||||
int vk_hdr_active(void);
|
||||
int vk_dlaa_needs_restart(void);
|
||||
double frame_step_limit_hz(void);
|
||||
int frame_limit_follows_display(void);
|
||||
int frame_limit_adjustable(void);
|
||||
int rtss_limit_state(void);
|
||||
int frame_free_running(void);
|
||||
@@ -711,7 +712,8 @@ static void row_game_lang(void) {
|
||||
opt_choice_list("NX2NAVI_LANG", UI_MENU_GAME_LANG, vals, names, n + 1, UI_MENU_GAME_LANG_DESC, cur);
|
||||
}
|
||||
|
||||
// The frame limit (frame_rate.c): unset, the older fps60 = 0 still means 30. The monitor choice shows the rate it gives.
|
||||
// The frame limit (frame_rate.c): unset, the older fps60 = 0 still means 30, and frame generation in use means the
|
||||
// monitor's rate (frame_limit_follows_display), which the row then shows. The monitor choice shows the rate it gives.
|
||||
// The note says when RivaTuner Statistics Server does the limiting (rtss_limit.c), cannot change its profile, or let the
|
||||
// game run past its limit.
|
||||
static void row_fps_limit() {
|
||||
@@ -732,7 +734,7 @@ static void row_fps_limit() {
|
||||
names[9] = display;
|
||||
}
|
||||
const char *set = cfg_get("NX2NAVI_FPS_LIMIT"), *old = cfg_get("NX2NAVI_FPS60");
|
||||
int cur = (!set || !*set) && old && !strcmp(old, "0") ? 0 : -1;
|
||||
int cur = (!set || !*set) && old && !strcmp(old, "0") ? 0 : frame_limit_follows_display() ? 9 : -1;
|
||||
int rtss = rtss_limit_state();
|
||||
const char *note = rtss == 1 ? menu_text(UI_MENU_FPS_RTSS)
|
||||
: rtss == 2 ? menu_text(UI_MENU_FPS_RTSS_READONLY)
|
||||
|
||||
Reference in New Issue
Block a user