mirror of
https://github.com/tangerineturtle1/NX2navi.git
synced 2026-11-15 19:19:58 -04:00
Performance: draws reuse the bindings that did not change and record only the dynamic states that differ from what the command buffer holds; small uploads come from a staging arena; finished submissions are held briefly and handed to the queue in batches; the recorder thread can keep a CPU core of its own; ASTC block-linear textures are deswizzled on the GPU; dynamic resolution steps the upscaler mode to hold a frame-time target. Each has a setting in nx2navi.ini (bind_reuse, dyn_delta, stg_arena, submit_batch, recorder_core, recorder_prio, astc_gpu_deswizzle, dyn_res) and the staging cache now frees what it holds at shutdown. Capture: NX2NAVI_DUMP=key saves the frame after each press of Insert, with mesh draws listed by task, mesh and fragment shader, depth test and face culling, and mesh_prims.txt giving each mesh draw's primitive, clipping and fragment counts. All of it is off unless NX2NAVI_DUMP is set. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
99 lines
4.9 KiB
C
99 lines
4.9 KiB
C
// Dynamic resolution from the GPU's own timestamps: the scene's resolution follows how long the GPU takes per frame,
|
|
// so a heavy view renders fewer pixels and keeps the frame time, and a light one goes back to the player's choice. The
|
|
// scene size is the one the game composes in (resolution.c), and it moves in the steps DLSS upscales from: quality,
|
|
// balanced, performance and ultra performance, 2/3, 0.58, 1/2 and 1/3 of the output on each axis.
|
|
//
|
|
// NX2NAVI_DYN_RES=1 turns it on (off by default: it changes the picture's sharpness), and it acts only while
|
|
// NX2NAVI_DLSS is one of those upscaling modes, which is then the sharpest step it uses; the lowest is performance,
|
|
// or ultra performance when that is the player's own mode. While it runs it asks for GPU timestamps
|
|
// (perf_gpu_timing_want; vk_sync.c times every submission part) and reads their running total (vk_gpu_busy_all) once
|
|
// per presented frame. It averages the busy time and moves one step at a time: down when the average is over
|
|
// NX2NAVI_DYN_RES_TARGET milliseconds (default 16.7, a 60 Hz frame) by 8 %, up when it is under 75 % of it, and holds
|
|
// each step for NX2NAVI_DYN_RES_STEP frames (default 45), because a size that changes every few frames is worse to look
|
|
// at than a steady one. The step in use goes to vk_dlaa.c dlss_mode as an override (dynres_override), which applies it
|
|
// as it applies the menu's choice; the player's setting itself never changes.
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "cfg.h"
|
|
#include "host.h"
|
|
#include "vk.h"
|
|
|
|
#define DYN_LEVELS 4
|
|
static const char *const DYN_MODE[DYN_LEVELS] = {"quality", "balanced", "performance", "ultra_performance"};
|
|
|
|
static struct {
|
|
int on; // dynamic resolution is acting now
|
|
int base, low; // the player's step (the sharpest used) and the lowest one used
|
|
int level; // the step in use
|
|
int settle; // frames the step has been held
|
|
int step; // frames to hold a step
|
|
double avg_ns; // moving average of the GPU's busy time per frame
|
|
double target_ns;
|
|
uint64_t last_busy; // vk_gpu_busy_all at the previous presented frame
|
|
unsigned cfg_seen; // cfg_version the settings were read at
|
|
char base_mode[32];
|
|
} D = {.cfg_seen = ~0u};
|
|
|
|
static int mode_index(const char *m) {
|
|
for (int i = 0; i < DYN_LEVELS; i++)
|
|
if (m && !strcmp(m, DYN_MODE[i])) return i;
|
|
return -1;
|
|
}
|
|
|
|
// The settings, again whenever one changed (the menu switches them live).
|
|
static void dynres_read(void) {
|
|
D.cfg_seen = cfg_version();
|
|
const char *m = cfg_value("NX2NAVI_DLSS");
|
|
int want = cfg_bool("NX2NAVI_DYN_RES"), base = mode_index(m);
|
|
double ms = cfg_double("NX2NAVI_DYN_RES_TARGET");
|
|
D.target_ns = (ms >= 1 ? ms : 16.7) * 1e6;
|
|
D.step = cfg_int("NX2NAVI_DYN_RES_STEP");
|
|
if (D.step < 8) D.step = 8;
|
|
int on = want && base >= 0;
|
|
if (on && (!D.on || strcmp(m, D.base_mode))) { // (re)start at the player's step
|
|
D.base = D.level = base;
|
|
D.low = base > 2 ? base : 2;
|
|
D.settle = 0, D.avg_ns = 0, D.last_busy = vk_gpu_busy_all();
|
|
rt_log("resolution: dynamic resolution on: target %.1f ms, DLSS %s down to %s", D.target_ns / 1e6, DYN_MODE[D.base],
|
|
DYN_MODE[D.low]);
|
|
} else if (!on && D.on) {
|
|
rt_log("resolution: dynamic resolution off: DLSS %s as set", m);
|
|
} else if (want && base < 0) {
|
|
static int said;
|
|
if (!said++) rt_log("resolution: dynamic resolution needs a DLSS upscaling mode (quality, balanced, performance or "
|
|
"ultra performance); DLSS is %s", m);
|
|
}
|
|
snprintf(D.base_mode, sizeof D.base_mode, "%s", m);
|
|
D.on = on;
|
|
perf_gpu_timing_want(on);
|
|
}
|
|
|
|
const char *dynres_override(void) { return D.on && D.level != D.base ? DYN_MODE[D.level] : NULL; }
|
|
|
|
// Once per presented frame, before vk_dlaa.c reads the mode.
|
|
void dynres_frame(void) {
|
|
if (cfg_version() != D.cfg_seen) dynres_read();
|
|
if (!D.on) return;
|
|
uint64_t now = vk_gpu_busy_all(), d = now - D.last_busy;
|
|
D.last_busy = now;
|
|
if (!d || d > 1000000000ull) return; // no timing this frame yet, or a stall that says nothing about the scene
|
|
D.avg_ns = D.avg_ns > 0 ? D.avg_ns * 0.9 + (double)d * 0.1 : (double)d;
|
|
static int frames, said;
|
|
if (++frames % 600 == 0 && said < 12) {
|
|
said++;
|
|
rt_log("resolution: dynamic resolution: GPU %.2f ms a frame, DLSS %s, target %.2f ms", D.avg_ns / 1e6,
|
|
DYN_MODE[D.level], D.target_ns / 1e6);
|
|
}
|
|
if (++D.settle < D.step) return;
|
|
int to = D.level;
|
|
if (D.avg_ns > D.target_ns * 1.08 && D.level < D.low) to = D.level + 1;
|
|
else if (D.avg_ns < D.target_ns * 0.75 && D.level > D.base) to = D.level - 1;
|
|
if (to == D.level) return;
|
|
rt_log("resolution: dynamic resolution: GPU %.2f ms a frame against %.2f ms: DLSS %s -> %s", D.avg_ns / 1e6,
|
|
D.target_ns / 1e6, DYN_MODE[D.level], DYN_MODE[to]);
|
|
D.level = to, D.settle = 0;
|
|
}
|