Files
NX2navi/runtime/vk_ffx.h
chrg6 9065d1485f Let FSR 3.1 and FSR 4 destroy a context they let go of once the GPU is past its last upscale, instead of waiting for the device.
vk_ffx.cpp context_for (two places) and vk_ffx4.cpp context_for (three) waited for the whole device to be idle, with
the queue lock held, on the thread that replays the game's submissions, before they destroyed an upscale context of
another render size, the least recently used one for a new output size, or (FSR 4) every idle one after a refused
creation: a stall of the present's flip, the completion thread and upload submits at every pause-screen size, reload,
window size, dynamic resolution step or mode change with FSR on.

A context that goes is now let go of (retire_up): it goes into a list (vk_up_defer.h) with the timeline value of the
submission of its last upscale (Up.fence, set after each dispatch) and is destroyed on the same thread once the GPU is
past it, at the points where vk_dlaa.c takes its own list (every evaluation, every pass while none runs, and after
something is let go of). The 90-second trims, vk_ffx_trim_idle and vk_ffx_release_idle (the upscaler off, which no
longer needs retire_idle's wait) let go of theirs the same way. FSR 4's create descriptors, which the API asks to stay
live until the context is destroyed, move from the slot into a calloc'd Made that goes with the context, so the slot
can take a new context while the old one waits. A refused FSR 4 creation now waits on the timeline from the host for
the contexts let go of, no queue touched (vk_upscaler_gpu_wait), and tries again when any went; with
NX2NAVI_UPSCALER_IDLE_WAIT=1 that wait and every let go wait for the device as before. docs/architecture.md says
where the upscalers' releases go.

C-5 is narrowed: on the default path no upscaler waits for the device any more, so these waits no longer touch the
frame generation swapchain's own queues; vk_fg.cpp, vk_fg4.cpp, gfx_present.c, vk_hdr.c, vk_fsr.c and vk_color.c keep
theirs.

Not reproduced before the change (no FSR session log here). Every runtime file compiles, vk_dlaa.c, vk_ffx.cpp and
vk_ffx4.cpp with -Wall -Wextra and no warning; vk_ffx4.cpp's non-Windows stubs compile against vk_ffx.h;
up_defer_check passes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 20:13:01 -05:00

94 lines
6.5 KiB
C

// AMD FidelityFX (vk_ffx.cpp): FSR 3.1 temporal upscaling through amd_fidelityfx_vk.dll, loaded at run time.
#pragma once
#include <vulkan/vulkan.h>
typedef struct {
VkImage image; // level 0, layer 0, in VK_IMAGE_LAYOUT_GENERAL
VkFormat format;
uint32_t width, height;
int storage; // created with VK_IMAGE_USAGE_STORAGE_BIT (FSR writes it)
} VkFfxImage;
// The scene camera FSR takes, from the frame's projection (vk_dlaa.c proj_planes): the vertical field of view in
// radians, the near and far planes' view distances (far: VK_DLAA_FAR_INFINITE, 1e8, for a projection without a far
// plane) and the metres in one unit of view space. A field of 0 is not known, and FSR then gets 1 radian, 0.1, 1e8 and
// 1.
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);
// vk_dlaa.c "what the upscalers let go of while the GPU may still use it", on the thread that makes the upscalers'
// calls (vk_dlaa.c's pass hooks):
int vk_upscaler_idle_wait(void); // NX2NAVI_UPSCALER_IDLE_WAIT: 1 waits for the device wherever one lets go of something
// After something was let go of: what the GPU is past goes (with NX2NAVI_UPSCALER_IDLE_WAIT=1 after a wait for the
// device, vk_upscaler_settle).
void vk_upscaler_let_go(void);
// A wait for the device, then everything let go of that a submission made so far names goes (a present gate stays).
void vk_upscaler_settle(void);
// A refused creation: waits, without the device's idle, until the GPU is past timeline value v (no further than the
// submissions made so far) and returns the value it is past; with NX2NAVI_UPSCALER_IDLE_WAIT=1, or when that wait
// fails, a wait for the device.
uint64_t vk_upscaler_gpu_wait(uint64_t v);
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,
// whether the library file is there, asked once without loading it. 0 where the build has no FSR 3.1 to load.
int vk_ffx_available(void);
// The same for frame generation (vk_fg.cpp), which runs through the library's own API calls (vk_ffx_create and the
// rest below), not only an upscaler.
int vk_ffx_framegen_available(void);
// Records FSR 3.1 into cb: color (the scene light, render size), depth (D32F), mv (R16G16F, render pixels, current to
// previous), out (the output size, RGBA16F, storage). jx, jy: the jitter the scene took, render pixels. cam: the
// frame's camera. 0 on failure (the caller falls back as it does when DLSS fails).
int vk_ffx_upscale(VkCommandBuffer cb, const VkFfxImage *color, const VkFfxImage *depth, const VkFfxImage *mv,
const VkFfxImage *out, float jx, float jy, int reset, float dt_ms, const VkFfxCamera *cam);
void vk_ffx_release(void); // before the device goes away (the device idle)
// The upscale contexts of FSR 3.1 and FSR 4 (vk_dlaa.c retire_idle): any held, and let them all go (the caller's
// vk_upscaler_let_go frees them once the GPU is past them).
int vk_ffx_holds(void);
void vk_ffx_release_idle(void);
// FSR 3.1's contexts that no upscale used in the last `recent` calls, let go of: room for an FSR 4 context whose
// creation failed, whose caller makes their memory free (vk_ffx_dead_take after vk_upscaler_gpu_wait). Their count.
int vk_ffx_trim_idle(uint64_t recent);
// The FSR 3.1 and FSR 4 contexts let go of: those whose last upscale's submission is at most done are destroyed (their
// count); the highest such submission among those left (0: none).
int vk_ffx_dead_take(uint64_t done);
uint64_t vk_ffx_dead_fence(void);
// The device's functions as the library is given them (ffxCreateBackendVKDesc.vkDeviceProcAddr): its pipeline creates
// go through the runtime's pipeline caches (vk_pipeline.c vk_pipeline_create_foreign; NX2NAVI_FFX_PIPE_CACHE=0: not).
VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vk_ffx_proc_addr(VkDevice dev, const char *name);
// AMD FSR 4 (vk_ffx4.cpp): the ML upscaler of AMD's FSR SDK 2.4, through amd_fidelityfx_loader_vk.dll and
// amd_fidelityfx_upscaler_vk.dll beside the executable. vk_ffx_upscale runs it in FSR 3.1's place when the setting
// asks (NX2NAVI_FSR4) and it runs here, and falls back to FSR 3.1 when it fails.
int vk_ffx4_available(void); // the libraries are there and list a 4.x version for the device; asked once, with the
// device, from any thread (0 before the device exists)
const char *vk_ffx4_name(void); // "AMD FSR 4.1.2" once vk_ffx4_available found it
int vk_ffx4_upscale(VkCommandBuffer cb, const VkFfxImage *color, const VkFfxImage *depth, const VkFfxImage *mv,
const VkFfxImage *out, float jx, float jy, int reset, float dt_ms,
const VkFfxCamera *cam); // as vk_ffx_upscale
void vk_ffx4_release(void);
int vk_ffx4_holds(void); // an FSR 4 upscale context is made
void vk_ffx4_release_idle(void); // every FSR 4 upscale context let go of (vk_ffx_release_idle)
int vk_ffx4_dead_take(uint64_t done); // FSR 4's part of vk_ffx_dead_take
uint64_t vk_ffx4_dead_fence(void); // FSR 4's part of vk_ffx_dead_fence
int vk_ffx4_failed(void); // it failed three times in a row: FSR 3.1 serves until vk_ffx4_retry
void vk_ffx4_retry(void); // the menu chose FSR 4 again: try it anew after such failures
void vk_ffx4_trim(uint64_t now_ns); // at each upscale: FSR 4's contexts unused for a while go
uint64_t vk_ffx4_vram(int *contexts); // FSR 4's upscale contexts and their video memory (vk_ffx_vram)
int vk_ffx_fsr4(void); // the setting asks for FSR 4 and it runs here: what vk_ffx_upscale uses, for the menu
#if defined(__cplusplus) && !defined(VK_FFX_NO_API)
// The API's calls themselves, for vk_fg.cpp (0 = FFX_API_RETURN_OK): the library loaded first (vk_ffx_load).
#include "third_party/ffx_api/ffx_api.h"
extern "C" {
ffxReturnCode_t vk_ffx_create(ffxContext *ctx, ffxCreateContextDescHeader *desc);
ffxReturnCode_t vk_ffx_destroy(ffxContext *ctx);
ffxReturnCode_t vk_ffx_configure(ffxContext *ctx, const ffxConfigureDescHeader *desc);
ffxReturnCode_t vk_ffx_query(ffxContext *ctx, ffxQueryDescHeader *desc);
ffxReturnCode_t vk_ffx_dispatch(ffxContext *ctx, const ffxDispatchDescHeader *desc);
}
#endif