mirror of
https://github.com/tangerineturtle1/NX2navi.git
synced 2026-11-15 19:19:58 -04:00
Give the upscalers' device-idle waits and their releases perf marks of their own, so a slow frame's line names them.
The twelve places where DLSS (vk_dlaa.c) and FSR 3.1 and FSR 4 (vk_ffx.cpp, vk_ffx4.cpp context_for) wait for the whole device to be idle before they free what the GPU may still use go through one function, vk_upscaler_device_idle, which NX2NAVI_TRACE=perf times as "upscaler device waits". The releases of DLSS features and of FSR contexts are timed as "upscaler releases". These waits run on the replaying thread with the queue lock held, so the present's flip, the completion thread's hold release and upload submits wait with them at each pause-screen size, reload, window size or mode change; until now only the "DLSS feature creates" mark saw one of them, the wait in release_kept, which it summed with the create. That mark now times the create alone. Diagnostics only: nothing waits or frees differently. stutter_map_check, stutter_hitch_check, frame_step_check and fg_reset_check passed; every runtime file compiles, the changed ones with -Wall -Wextra and no warning. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -63,7 +63,10 @@ void perf_count(int kind);
|
||||
// pipe_create_in), guest memory maps (heap size, MapPhysicalMemory), NVN memory pool initialize and finalize
|
||||
// (vk_memory.c vk_pool_init, vk_pool_fini), device memory VMA allocates (blocks and dedicated memory, vk_device.c
|
||||
// timed_alloc), and DLSS and DLSS frame generation feature creates (vk_dlaa.c). perf_mark_begin returns 0 while the trace is off, so a normal run pays one branch per site.
|
||||
enum { PERF_M_FILE, PERF_M_ZSTD, PERF_M_PIPE, PERF_M_GMEM, PERF_M_POOL, PERF_M_DEVMEM, PERF_M_NGX, PERF_M_N };
|
||||
// UPWAIT: the upscalers' waits for the GPU before they free what they let go of (vk_dlaa.c vk_upscaler_device_idle);
|
||||
// UPREL: their releases of DLSS features and FSR contexts (vk_dlaa.c, vk_ffx.cpp, vk_ffx4.cpp).
|
||||
enum { PERF_M_FILE, PERF_M_ZSTD, PERF_M_PIPE, PERF_M_GMEM, PERF_M_POOL, PERF_M_DEVMEM, PERF_M_NGX, PERF_M_UPWAIT,
|
||||
PERF_M_UPREL, PERF_M_N };
|
||||
uint64_t perf_mark_begin(void);
|
||||
void perf_mark_end(int kind, uint64_t t0, uint64_t bytes); // nothing when t0 is 0
|
||||
void perf_mark_add(int kind, uint64_t ns, uint64_t bytes); // nothing while the trace is off
|
||||
|
||||
@@ -151,7 +151,7 @@ void perf_count(int kind) {
|
||||
// names them on a slow frame's line and adds them to the 60-frame window's line.
|
||||
static const char *const mark_name[PERF_M_N] = {"file reads", "zstd decodes", "pipeline creates", "guest memory maps",
|
||||
"pool init/finalize", "device memory allocations",
|
||||
"DLSS feature creates"};
|
||||
"DLSS feature creates", "upscaler device waits", "upscaler releases"};
|
||||
static volatile int64_t mk_ns[PERF_M_N], mk_n[PERF_M_N], mk_bytes[PERF_M_N], mk_max[PERF_M_N]; // since the last present
|
||||
typedef struct { uint64_t ns[PERF_M_N], n[PERF_M_N], bytes[PERF_M_N], max[PERF_M_N]; } MarkSums;
|
||||
static MarkSums mk_win; // the 60-frame window's, present thread only
|
||||
|
||||
@@ -726,6 +726,22 @@ static int idle_free(void) {
|
||||
return m;
|
||||
}
|
||||
|
||||
// The upscalers' waits for the device to be idle before they free what the GPU may still use (vk_dlaa.c, vk_ffx.cpp,
|
||||
// vk_ffx4.cpp), with the queue lock held: the present's flip, the completion thread's hold release and every upload
|
||||
// submit wait as long. NX2NAVI_TRACE=perf names them "upscaler device waits" on a slow frame's line.
|
||||
void vk_upscaler_device_idle(void) {
|
||||
uint64_t t0 = perf_mark_begin();
|
||||
vk_queue_lock(), vkDeviceWaitIdle(vk_ctx()->device), vk_queue_unlock();
|
||||
perf_mark_end(PERF_M_UPWAIT, t0, 0);
|
||||
}
|
||||
|
||||
// A DLSS feature's release, timed as "upscaler releases" (NX2NAVI_TRACE=perf).
|
||||
static void feat_release(NgxHandle *f) {
|
||||
uint64_t t0 = perf_mark_begin();
|
||||
N.release(f);
|
||||
perf_mark_end(PERF_M_UPREL, t0, 0);
|
||||
}
|
||||
|
||||
// The images and views of a size, then its feature. The device must be idle: free_size waits for it, vk_dlaa_release
|
||||
// has. Whatever was not made is skipped, so a half-made size goes the same way.
|
||||
static void destroy_size(struct Size *e) {
|
||||
@@ -744,13 +760,13 @@ static void destroy_size(struct Size *e) {
|
||||
if (e->dist_view[i]) vkDestroyImageView(c->device, e->dist_view[i], NULL);
|
||||
if (e->dist[i]) vmaDestroyImage(c->vma, e->dist[i], e->dist_mem[i]);
|
||||
}
|
||||
if (e->feat && N.release) N.release(e->feat), feat_released = 1;
|
||||
if (e->feat && N.release) feat_release(e->feat), feat_released = 1;
|
||||
if (D.host == e) D.host = NULL;
|
||||
memset(e, 0, sizeof *e);
|
||||
}
|
||||
|
||||
static void free_size(struct Size *e) {
|
||||
vk_queue_lock(), vkDeviceWaitIdle(vk_ctx()->device), vk_queue_unlock(); // a third scene size: the evicted images may still be in flight
|
||||
vk_upscaler_device_idle(); // a third scene size: the evicted images may still be in flight
|
||||
destroy_size(e);
|
||||
}
|
||||
|
||||
@@ -1420,7 +1436,7 @@ static void destroy_snap(struct Snap *s) {
|
||||
}
|
||||
|
||||
static void free_snap(struct Snap *s) {
|
||||
vk_queue_lock(), vkDeviceWaitIdle(vk_ctx()->device), vk_queue_unlock(); // a fourth size: the evicted images may still be in flight
|
||||
vk_upscaler_device_idle(); // a fourth size: the evicted images may still be in flight
|
||||
destroy_snap(s);
|
||||
}
|
||||
|
||||
@@ -1871,8 +1887,8 @@ static void stale_sizes(const struct Size *cur, const struct Size *host) {
|
||||
} else if (s->feat && D.evals >= s->feat_used + SIZE_STALE) {
|
||||
rt_log("dlaa: the DLSS feature at %ux%u -> %ux%u went unused for %d frames while its size ran on another "
|
||||
"size's: released", s->w, s->h, s->ow, s->oh, SIZE_STALE);
|
||||
vk_queue_lock(), vkDeviceWaitIdle(vk_ctx()->device), vk_queue_unlock();
|
||||
N.release(s->feat), s->feat = NULL, feat_released = 1;
|
||||
vk_upscaler_device_idle();
|
||||
feat_release(s->feat), s->feat = NULL, feat_released = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1886,8 +1902,8 @@ static void release_kept(const struct Size *making, int all) {
|
||||
struct Size *s = &D.sizes[i];
|
||||
if (s == making || !s->feat) continue;
|
||||
if (!all && (uint64_t)s->w * s->h <= (uint64_t)making->w * making->h) continue;
|
||||
if (!n++) vk_queue_lock(), vkDeviceWaitIdle(vk_ctx()->device), vk_queue_unlock();
|
||||
N.release(s->feat), s->feat = NULL;
|
||||
if (!n++) vk_upscaler_device_idle();
|
||||
feat_release(s->feat), s->feat = NULL;
|
||||
if (D.last == s) D.last = NULL;
|
||||
if (D.host == s) D.host = NULL;
|
||||
}
|
||||
@@ -2292,8 +2308,6 @@ own_feature:
|
||||
// The feature is made for the size of its entry: this frame's own, or the larger one of the DLSS mode's, which
|
||||
// was released since (release_kept) and takes this frame's size as a sub-rectangle.
|
||||
const uint32_t fw = host->w, fh = host->h;
|
||||
// NX2NAVI_TRACE=perf: the release of the kept features (a device idle wait) and the create, as one mark
|
||||
uint64_t t0 = perf_mark_begin();
|
||||
if (idle_free() != 2) release_kept(host, feat_released);
|
||||
feat_released = 0;
|
||||
p_setu(p, "CreationNodeMask", 1);
|
||||
@@ -2317,6 +2331,8 @@ own_feature:
|
||||
p_setu(p, "DLSS.Hint.Render.Preset.Balanced", (unsigned)pk);
|
||||
p_setu(p, "DLSS.Hint.Render.Preset.Performance", (unsigned)pm);
|
||||
p_setu(p, "DLSS.Hint.Render.Preset.UltraPerformance", (unsigned)pl);
|
||||
// NX2NAVI_TRACE=perf: the create alone; the wait and the releases of release_kept have marks of their own
|
||||
uint64_t t0 = perf_mark_begin();
|
||||
NgxResult r = N.create(cb, NGX_FEATURE_SUPERSAMPLING, p, &host->feat);
|
||||
perf_mark_end(PERF_M_NGX, t0, 0);
|
||||
if (!NGX_OK(r) || !host->feat) return dl_fail("DLSS feature creation", r);
|
||||
@@ -2469,7 +2485,7 @@ static void hudless_from(VkCommandBuffer cb, VkImage src, uint32_t level, uint32
|
||||
if (vk_hdr_native()) return;
|
||||
VkFormat fmt = hdr ? VK_FORMAT_R16G16B16A16_SFLOAT : VK_FORMAT_R8G8B8A8_UNORM;
|
||||
if (D.hl && (D.hl_w != w || D.hl_h != h || D.hl_fmt != fmt)) {
|
||||
vk_queue_lock(), vkDeviceWaitIdle(c->device), vk_queue_unlock();
|
||||
vk_upscaler_device_idle();
|
||||
hl_destroy();
|
||||
}
|
||||
if (!D.hl) {
|
||||
@@ -2592,7 +2608,7 @@ static int pb_images(VkCommandBuffer cb, uint32_t w, uint32_t h) {
|
||||
PB.used = D.presents;
|
||||
if (PB.scene && PB.w == w && PB.h == h) return 1;
|
||||
if (PB.scene || PB.comp) {
|
||||
vk_queue_lock(), vkDeviceWaitIdle(vk_ctx()->device), vk_queue_unlock();
|
||||
vk_upscaler_device_idle();
|
||||
pb_destroy();
|
||||
}
|
||||
VkImageUsageFlags u = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
|
||||
@@ -2832,7 +2848,7 @@ static void retire_idle(void) {
|
||||
}
|
||||
if (!retire_off_at) retire_off_at = now ? now : 1;
|
||||
if (now < retire_off_at + RETIRE_PRESENTS || !up_holds()) return;
|
||||
vk_queue_lock(), vkDeviceWaitIdle(vk_ctx()->device), vk_queue_unlock();
|
||||
vk_upscaler_device_idle();
|
||||
for (int i = 0; i < SNAPS; i++) destroy_snap(&R.s[i]);
|
||||
vk_ffx_release_idle();
|
||||
if (D.failed) { // as idle_release does for a size: frame generation is handed nothing of one from here on
|
||||
@@ -3056,7 +3072,7 @@ static void idle_release(void) {
|
||||
if (s->feat) {
|
||||
rt_log("dlaa: the DLSS feature at %ux%u went unused for %d presents with the upscaler off: released; "
|
||||
"its size's motion images stay for frame generation", s->w, s->h, IDLE_FREE_PRESENTS);
|
||||
N.release(s->feat), s->feat = NULL, feat_released = 1;
|
||||
feat_release(s->feat), s->feat = NULL, feat_released = 1;
|
||||
}
|
||||
if (D.host == s) D.host = NULL;
|
||||
continue;
|
||||
|
||||
@@ -141,7 +141,9 @@ const uint64_t UP_IDLE_NS = 90ull * 1000000000ull;
|
||||
|
||||
void destroy_up(Up *u, const char *why) {
|
||||
rt_log("ffx: the FSR 3.1 context for %ux%u (render sizes up to %ux%u) destroyed: %s", u->ow, u->oh, u->mw, u->mh, why);
|
||||
uint64_t t0 = perf_mark_begin(); // NX2NAVI_TRACE=perf: "upscaler releases"
|
||||
A.destroy(&u->ctx, nullptr);
|
||||
perf_mark_end(PERF_M_UPREL, t0, 0);
|
||||
*u = Up();
|
||||
}
|
||||
|
||||
@@ -195,7 +197,7 @@ Up *context_for(uint32_t w, uint32_t h, uint32_t ow, uint32_t oh) {
|
||||
int waited = 0;
|
||||
for (Up &u : U) // the contexts of this output size for another render size, once no command buffer holds them
|
||||
if (u.ctx && &u != found && u.ow == ow && u.oh == oh && up_tick - u.used >= UP_RECENT) {
|
||||
if (!waited++) vk_queue_lock(), vkDeviceWaitIdle(c->device), vk_queue_unlock(); // their work may be in flight
|
||||
if (!waited++) vk_upscaler_device_idle(); // their work may be in flight
|
||||
destroy_up(&u, "the render size changed");
|
||||
}
|
||||
if (found) {
|
||||
@@ -211,7 +213,7 @@ Up *context_for(uint32_t w, uint32_t h, uint32_t ow, uint32_t oh) {
|
||||
if (up_tick - u.used >= UP_RECENT && (!lru || u.used < lru->used)) lru = &u;
|
||||
}
|
||||
if (!slot && lru) {
|
||||
vk_queue_lock(), vkDeviceWaitIdle(c->device), vk_queue_unlock(); // its work may still be in flight
|
||||
vk_upscaler_device_idle(); // its work may still be in flight
|
||||
destroy_up(lru, "its slot went to another output size");
|
||||
slot = lru;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,9 @@ typedef struct {
|
||||
typedef struct { float fov_y, znear, zfar, meters; } VkFfxCamera;
|
||||
// vk_dlaa.c: the share (0..1) of the extra sharpening FSR 3.1 gets at a render width w of an output width ow
|
||||
float vk_dlaa_fsr3_boost(uint32_t w, uint32_t ow);
|
||||
// vk_dlaa.c: a wait for the device to be idle, the queue lock held, before an upscaler frees what the GPU may still
|
||||
// use (NX2NAVI_TRACE=perf: "upscaler device waits")
|
||||
void vk_upscaler_device_idle(void);
|
||||
|
||||
int vk_ffx_load(void); // 1 when the library is there with its entry points (loaded once)
|
||||
// Whether FSR 3.1 upscaling can run in this build, for the settings menu: after vk_ffx_load ran, what it found; before,
|
||||
|
||||
@@ -207,7 +207,9 @@ const uint64_t UP_IDLE_NS = 90ull * 1000000000ull;
|
||||
|
||||
void destroy_up(Up *u, const char *why) {
|
||||
rt_log("ffx4: the %ux%u context (render sizes up to %ux%u) destroyed: %s", u->ow, u->oh, u->mw, u->mh, why);
|
||||
uint64_t t0 = perf_mark_begin(); // NX2NAVI_TRACE=perf: "upscaler releases"
|
||||
A.destroy(&u->ctx, nullptr);
|
||||
perf_mark_end(PERF_M_UPREL, t0, 0);
|
||||
*u = Up();
|
||||
}
|
||||
|
||||
@@ -288,11 +290,10 @@ Up *context_for(uint32_t w, uint32_t h, uint32_t ow, uint32_t oh) {
|
||||
for (Up &u : U)
|
||||
if (u.ctx && u.ow == ow && u.oh == oh && u.drs == drs && (drs ? w <= u.mw && h <= u.mh : w == u.mw && h == u.mh))
|
||||
found = &u;
|
||||
const VkCtx *c = vk_ctx();
|
||||
int waited = 0;
|
||||
for (Up &u : U) // the contexts of this output size for another render size, once no command buffer holds them
|
||||
if (u.ctx && &u != found && u.ow == ow && u.oh == oh && up_tick - u.used >= UP_RECENT) {
|
||||
if (!waited++) vk_queue_lock(), vkDeviceWaitIdle(c->device), vk_queue_unlock(); // their work may be in flight
|
||||
if (!waited++) vk_upscaler_device_idle(); // their work may be in flight
|
||||
destroy_up(&u, "the render size changed");
|
||||
}
|
||||
if (found) {
|
||||
@@ -308,7 +309,7 @@ Up *context_for(uint32_t w, uint32_t h, uint32_t ow, uint32_t oh) {
|
||||
if (up_tick - u.used >= UP_RECENT && (!lru || u.used < lru->used)) lru = &u;
|
||||
}
|
||||
if (!slot && lru) {
|
||||
vk_queue_lock(), vkDeviceWaitIdle(c->device), vk_queue_unlock(); // its work may still be in flight
|
||||
vk_upscaler_device_idle(); // its work may still be in flight
|
||||
destroy_up(lru, "its slot went to another output size");
|
||||
slot = lru;
|
||||
}
|
||||
@@ -318,7 +319,7 @@ Up *context_for(uint32_t w, uint32_t h, uint32_t ow, uint32_t oh) {
|
||||
Up *u = create(slot, mw, mh, ow, oh, drs);
|
||||
if (!u) {
|
||||
int freed = 0;
|
||||
vk_queue_lock(), vkDeviceWaitIdle(c->device), vk_queue_unlock();
|
||||
vk_upscaler_device_idle();
|
||||
for (Up &o : U)
|
||||
if (o.ctx && up_tick - o.used >= UP_RECENT) destroy_up(&o, "room for a context whose creation failed"), freed++;
|
||||
freed += vk_ffx_trim_idle(UP_RECENT);
|
||||
|
||||
Reference in New Issue
Block a user