The review of 09027478 found that FSR 3.1 lost its fallback for a refused creation. Up to 0.10.1, vk_ffx.cpp
context_for waited for the device and destroyed the other render size's or the least recently used context before
create_counted, so their video memory was free for the new one. Since 09027478 they only go into g_dead, and the drain
in vk_upscaler_let_go destroys those whose last upscale the GPU is past. When it is still behind and the library
refuses the new context for want of memory, context_for returned nullptr, vk_ffx_upscale 0, and evaluate's dl_fail
turned DLAA off for the run. DLSS (evaluate) and FSR 4 (context_for) already wait for the GPU to pass what they let go
of and try again.
context_for now does the same when the creation is refused and FSR 3.1's or FSR 4's list of contexts let go of is not
empty: vk_upscaler_gpu_wait (a timeline wait for those submissions, not the device's idle; the device with
upscaler_idle_wait = 1), vk_ffx_dead_take, and a second create_counted when something was destroyed. A line says the
refusal, the wait and the outcome. upscaler_idle_check now also fails when vk_dlaa.c evaluate or vk_ffx.cpp or
vk_ffx4.cpp context_for has no vk_upscaler_gpu_wait (a scratch copy without the retry fails it), and docs/building.md
says so.
Not reproduced before the change (found by review). Checks that passed: vk_ffx.cpp compiles with -Wall -Wextra and no
warning, upscaler_idle_check, tools/check_content.py --staged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
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>
Every report adds a line for video memory outside VMA (the driver's usage and budget per heap against VMA's blocks, and counts of pipelines by kind and by last use, stage libraries, compute pipelines, layouts and descriptor pools, with the swapchain, DLSS and FSR sizes), a line that splits the process's commit by owner (the game's heap and maps, device-local memory, VMA's host blocks, the C runtime heap with the shader caches, pipeline tables, texture cache, log queue and flight recorder in it, the driver's pipeline cache data and the rest), and a line naming what grew by more than 64 MiB since the report before and since play began. Every ten minutes of play and at exit a ranking line orders the categories by growth and marks those that only climbed; tests/mem_growth_check.py checks that bookkeeping.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Switched off in the menu or failed, the upscaler kept everything it had made for the process: per scene size its
output, motion vector, mask and sharpening images and a DLSS feature (81 MiB of images at 2560x1440 with DLAA, 182 MiB
at 2160 lines, and about 0.5 GB per DLSS feature), the reactive mask's snapshots, the HUD-less pair and the FSR 3.1
and FSR 4 upscale contexts. Eight presents after it goes off they are freed on the replay thread, which makes every
evaluation, after a wait for the device to be idle, as the code already does for a size it replaces. Switched on
again, it makes them anew with a new history, as any switch on does.
The HUD-less picture was made and copied every frame, and kept, after frame generation went off, since the flag that
asks for it was never cleared on Windows: the frame generator's release clears it now, and the pair goes once no frame
generation swapchain is up.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
FSR 3.1 was soft at Performance and Ultra Performance, most of all in motion. On a village save, while the camera turned at a steady rate (present screenshots every 0.4 s, the mean absolute Laplacian of the scene's luminance): FSR 3.1 Native AA 6.7, FSR 4 Performance 5.9 and Ultra Performance 5.4, FSR 3.1 Performance 4.0 and Ultra Performance 3.3. The library's own input checks found nothing wrong, and its tuning constants (velocity factor, accumulation per frame, shading change scale, behind the test switch NX2NAVI_FFX_KEYS) gave nothing or took detail away. Its own sharpening at 100 instead of the default 80 gave 5.0 and 4.1.
Now the share of extra sharpening grows from none at three quarters of the output's width to all of it at half (vk_dlaa_fsr3_boost). FSR 3.1's own sharpness goes that share of the way to its maximum, and its picture gets a second RCAS pass (shaders/dlaa_sharpen.comp, AMD's FSR 1 header with the linear light taken through c / (1 + max c) and back), before the HUD is drawn. Both follow the sharpness slider; 0 turns them off, and NX2NAVI_FSR3_SHARPEN=0 leaves the second pass out. At RCAS's full strength the pass took Performance to 8.2, past Native AA, with grain on every surface, so it runs 1.5 stops weaker: Performance 5.7, Ultra Performance 4.7, Quality 5.2. 63.4 fps against 65.1 without the pass. The HDR picture is clean. FSR 4 is untouched.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Conflicts: the settings key lists take both sides; frame generation keeps the camera-cut handling and logs its counters every 600 frames with the cuts among them; the pass-time mark moves into draw_viewport; mo_desc keeps the pose depth and notes the reader.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
AMD's Windows driver gives the graphics family a single queue. vk_device.c then made no frame generation queues, and
vk_fg_create_swapchain returned without a log line: AMD FSR 3.1 frame generation set in the menu did nothing on an
RX 580. The present queue (which nothing else may submit to) and the image acquire queue now come from a family that
can present, compute families first, with their families passed to AMD's swapchain; the log names them, or says that
there are none, and the menu offers FSR 3.1 frame generation only where they exist.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
One FSR 4 context took every render scale of the menu, with another render size at each switch and without the dynamic resolution flag. The first frame after a switch from 67 % to 100 % ended the game with an access violation in AMD's driver inside FSR 4's dispatch. AMD's sample waits for the GPU and creates the context anew on every render resolution change, and sets FFX_UPSCALE_ENABLE_DYNAMIC_RESOLUTION only for its dynamic resolution mode.
FSR 3.1 and FSR 4 now keep a context per render size and output size. The previous one is destroyed once no command buffer can hold it (eight upscales later), so a mode switch costs one creation of about 50 ms and two contexts for a few frames. With the game's dynamic resolution on (NX2NAVI_DYN_RES), one context made with the flag takes every step. NX2NAVI_FFX_DEBUG=1 (test) turns on the library's own checks of each dispatch.
Tested on an RX 7900 XT with switches every 15 frames and every 90 frames between every mode and between FSR 4 and FSR 3.1 (32 switches): one context made per switch, the old one gone a few frames later, no failure and no crash.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DLSS frame generation (Streamline and the NGX path) and FSR 3.1 and 4 upscaling and frame generation were told the depth came from a camera with near 0.1 and no far plane. Ocarina's projection has a finite far plane that changes with the camera: in Hyrule Field its z row is (-1.00001, -0.200001), near 0.1 and far about 25,000; the camera before it had far about 1,000. With the far plane taken as missing, a surface 10,000 units away was read as 16,650. The planes, and whether the depth is inverted, now come from the frame's projection and the device's depth mode every frame (vk_dlaa.c proj_planes), with one log line when they change. The stored depth is already 0 near and 1 far: the -w..w clip depth the pipelines keep maps to exactly what a 0..w projection with the same planes stores, and a frame dump's scene depth spans 0.975 to 1.0.
FSR's contexts lose their infinite-depth flag and take the far plane from each dispatch. FSR frame generation now gets the camera's field of view instead of a fixed 1 radian, and the upscaler gets it too (its caller passed 0). The view's units are metres, not the centimetres FSR was told: the camera orbits Link about 4 units behind him at about 3 units a second.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each upscaling mode made a context of its own (about 217 MiB of video memory for FSR 4 at 3440x1440, 50 to 70 ms each), up to six were kept beside FSR 3.1's, and three FSR 4 creations that failed in a row turned FSR 4 off for the run: the menu then showed FSR 3.1 and no longer listed FSR 4. A player's log had the third failure (error 3) right after stepping through the modes with ten contexts alive. A context now takes every render size up to its output size, which the API allows at each dispatch; a creation that fails frees every context not in use, FSR 3.1's idle ones too, and is tried again; FSR 3.1 serves the frames between tries two seconds apart; only four failures in a row turn FSR 4 off, and choosing it in the menu again tries it anew.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brought onto the line with FSR 4, its frame generation and the debug menu. Where both sides had added to the same
place, both stay: the patch table keeps the pickup count and the free camera, the settings list keeps both sides'
names, the menu includes the co-op page and the debug menu, and the launcher builder takes the pipeline list and the
FSR 4 folder. Both sides had added the menu's typed-character event; one definition stays, with the window code that
passes characters outside the basic plane.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
The library creates its pipelines itself and names no pipeline cache. It is now handed pipeline creates of the runtime's in the place of the device's two, and a create without a cache is asked of the pipeline cache first and compiled into the play cache otherwise. ffx_pipe_cache = 0 turns it off. --probe-ffx-cache makes one upscaling context without a window or the game and says where its pipelines came from, and tests/ffx_cache_check.py runs it.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
runtime/vk_ffx4.cpp loads amd_fidelityfx_loader_vk.dll from beside the executable and asks it which upscaler
versions the device can run. Where a 4.x version is listed (Radeon RX 6000, RX 7000 and RX 9000 cards with AMD's
driver 26.10.1 or newer) the menu's Upscaling row gains AMD FSR 4 Native AA, Quality, Balanced, Performance and
Ultra Performance, and fsr4 = 1 runs it in FSR 3.1's place with the same inputs. Its contexts are made with that
version asked for by its id and the provider is asked afterwards which version it runs, so no other upscaler can
be shown under the name. When a context or a dispatch fails, FSR 3.1 makes the frame, and after three failures
FSR 4 is off for the run. Each upscaler starts its history anew on the frame after a change from one to the other.
The setting is off by default and the Linux build compiles stubs.
On an RX 7900 XT (driver 26.10.1, 3440x1440, HDR) the library runs FSR 4.1.2: a context takes 54 ms to make and
217 MiB of video memory. Standing in Hyrule Field: Quality 47.8 fps where FSR 3.1 gives 51.5, Native AA 41.4.
Screenshots of 601 consecutive frames standing, 801 during a camera sweep, 701 each in Kakariko and the
graveyard, and 1,301 across changes between FSR 3.1 and FSR 4, their modes and off show no flicker that FSR 3.1
does not have, and it runs together with FSR 3.1 frame generation.
The SDK's API headers are in runtime/third_party/ffx_sdk, unmodified, with the licence. tools/make_kit.py and
tools/make_launcher.py take AMD's two signed libraries with --ffx4, and tools/make_game.py copies them beside the
game when the kit has both.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
On Linux the Screen tab listed the five FSR 3.1 upscaling modes and AMD's frame generation, but only the
Windows build loads amd_fidelityfx_vk.dll: a player who picked a mode got the scene at the mode's share of
the picture with nothing to scale it back up. vk_ffx_available answers from the load's result once it ran,
and before that from the library file being there, so the menu can ask every frame. Where it says no,
the menu lists neither, a mode that is still set shows "in use: off", the scene keeps the game's own
share (vk_dlaa_fsr3), frame generation keeps the plain swapchain with one log line, and the texts name
what does run.
Pull request #371 by mooki000, taken as it is; two hunks merged by hand (vk_fg_wanted, which now also
stays off after a failure, and a README line that had gained Texture filtering). It touches vk_dlaa.c,
which another session is changing: vk_dlaa_fsr3 gains the availability test and one log text changes.
On Windows the menu changes only where the DLL is missing. The Windows files compile; nothing was run.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Each render and output size gets its own FSR 3.1 context, and there were six, never freed. Stepping through the render scales in the menu, plus the game's second scenes, used them up; the seventh size found no context, and FSR 3.1 went off for the rest of the run, leaving the game's own upscaler stand-in to show the picture. When all six are taken, the least recently used one not used in the last few calls is now destroyed once the GPU is idle, as DLSS's sizes already are.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With Frame generation set to AMD FSR 3.1 (after a restart), AMD's frame generation swapchain takes the place of the
present path's own: the device creates two more graphics queues for its presents and image acquires, the present
path acquires, gets images and presents through its replacement functions, and every frame configures generation
and records its preparation from the depth and camera motion vectors the DLSS or FSR 3.1 upscaling pass made (one of
them must be on). Generation pauses while the menu is open. On an RTX 5090 at a 60 fps game, about one frame was
generated for every frame drawn in play.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Upscaling choice gains FSR 3.1 Native AA, Quality, Balanced, Performance and Ultra Performance. FSR 3.1 runs
through AMD's signed amd_fidelityfx_vk.dll (loaded at run time; the build kit ships it) in DLSS's place, on the same
scene light, depth and camera motion vectors and at the same render scales. Its Vulkan back end needs
VK_KHR_get_memory_requirements2, which the device now enables when FSR 3.1 is chosen.
A half-size effect pass with three targets and a half-size depth was taken for a G-buffer, which switched every later
frame to the deferred frame shape: there the output pass of a smaller scene was never found, so DLSS Quality,
Balanced, Performance and Ultra Performance did nothing on a 16:9 picture. Such a pass smaller than the scene is now
left alone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>