Compile AMD's FSR 3.1 upscaler passes from the FidelityFX SDK #822

Closed
notquiteog wants to merge 45 commits from feat/fsr-vendor into main
notquiteog commented 2026-11-08 22:35:56 +00:00 (Migrated from github.com)

The shader half of an FSR backend, and the render-target census that settles a question the docs kept asserting.

Stacks on #821. That PR has the seam an FSR backend plugs into; until it merges, GitHub shows its four commits here too. Review #821 first.

The shaders

Adds AMD's FidelityFX SDK v1.1.4 GPU sources under third_party/fidelityfx-sdk/ — unmodified, MIT, with a PROVENANCE.md — and scripts/fsr3_shaders.py to compile the eleven variants into gpu/vk/shaders/fsr3_*_comp_spv.h.

Eleven, not eight: the eight passes, plus wave-free variants of both pyramids for devices without the subgroup operations the single-pass downsampler prefers, and an accumulate compiled with sharpening on. The option set lives in one place in the script because each option is a promise about the inputs — HDR colour, low-resolution motion that is not jittered, reversed depth — and the host code has to hand the passes what they were compiled for.

This is the SDK-source route, not AMD's FidelityFX API: no runtime library is needed, none is included, and one code path serves Windows and Linux. #577 vendors AMD's API headers under third_party/fidelityfx/ for the other route, which reaches FSR 4 but needs AMD's runtime and is Windows-only. They are different things, but if both land one of them should go, and docs/upscaling.md says so rather than leaving it implicit.

The headers are checked in, like every other shader in gpu/vk/shaders, so a clone builds with no glslang. tests/test_fsr3_shaders.py recompiles all eleven and compares, and validates each module with spirv-val, so they cannot drift from their sources and an invalid module is caught here rather than on a device. glslang-tools is added to the Linux CI step that had spirv-tools only.

That costs about 1.3 MB of generated hex. Generating at build time would remove it at the price of a glslang dependency for every build — your call; the doc records the trade rather than deciding it.

Nothing includes these headers yet. The host side that records the passes is the next PR.

The census

RX_VK_MRT_CENSUS=1 logs each distinct render target set once, from targets() (once per NOP_SET_RT, not per draw), capped at 512 sets, free while off.

It exists to answer whether a denoising upscaler — DLSS Ray Reconstruction, FSR Ray Regeneration — could ever have inputs here, since both want a ray-traced renderer's albedo, normal and roughness guides rather than colour/motion/depth. The honest test is whether the game writes a G-buffer.

It does bind multi-attachment passes, which is the opposite of what I expected: of 136 distinct sets, 7 bind six attachments, 2 bind three, 6 bind two. The formats settle it the other way:

sets attachments what they are
103 1 the scene at 2048x1152 B10G11R11, the 2560x1440 RGBA8 sRGB present, and the UI
18 0 depth-only
7 6 1x1 → 64x64, all B10G11R11: six cubemap faces down a mip chain
6 2 170x96 / 384x216 R16F+R32F with D32; 341x192 / 768x432 RGBA16F pairs; 480x270 two B10G11R11; 960x540 B10G11R11+R8
2 3 341x192 / 768x432, R32F + undefined + R16F with D32

A deferred G-buffer is three or four attachments at the main render resolution carrying albedo, packed normals and roughness. None of this is that: the scene is a single HDR target and every multi-attachment pass is a cubemap face chain or post-processing at a quarter resolution or less. So there are no guides for a denoising upscaler on this title — now on evidence rather than on the absence of a target named "normal".

Tested

Linux, RTX 5060 Ti, driver 615.71.09. All eleven variants compile and pass spirv-val; the regeneration check fails as it should on a changed word in a header, a changed compile option and a missing header. Full game build from a v131072 dump (build e0f3ccfa) in 8m20s, and a 240 s headless run produced the census above. python -I -B tests/run_all.py: 90 passed, with the same 14 failures and 7 skips as pristine main in this environment.

No fps claim: that run had RX_VK_PRECOMPILE=0, so every pipeline compiled on demand.

AI assistance: recorded in the commit trailers.

The shader half of an FSR backend, and the render-target census that settles a question the docs kept asserting. > **Stacks on #821.** That PR has the seam an FSR backend plugs into; until it merges, GitHub shows its four commits here too. Review #821 first. ### The shaders Adds AMD's FidelityFX SDK v1.1.4 GPU sources under `third_party/fidelityfx-sdk/` — unmodified, MIT, with a `PROVENANCE.md` — and `scripts/fsr3_shaders.py` to compile the eleven variants into `gpu/vk/shaders/fsr3_*_comp_spv.h`. Eleven, not eight: the eight passes, plus wave-free variants of both pyramids for devices without the subgroup operations the single-pass downsampler prefers, and an accumulate compiled with sharpening on. The option set lives in one place in the script because each option is a promise about the inputs — HDR colour, low-resolution motion that is not jittered, reversed depth — and the host code has to hand the passes what they were compiled for. **This is the SDK-source route, not AMD's FidelityFX API**: no runtime library is needed, none is included, and one code path serves Windows and Linux. #577 vendors AMD's *API* headers under `third_party/fidelityfx/` for the other route, which reaches FSR 4 but needs AMD's runtime and is Windows-only. They are different things, but if both land one of them should go, and `docs/upscaling.md` says so rather than leaving it implicit. The headers are checked in, like every other shader in `gpu/vk/shaders`, so a clone builds with no glslang. `tests/test_fsr3_shaders.py` recompiles all eleven and compares, and validates each module with `spirv-val`, so they cannot drift from their sources and an invalid module is caught here rather than on a device. `glslang-tools` is added to the Linux CI step that had `spirv-tools` only. That costs about 1.3 MB of generated hex. Generating at build time would remove it at the price of a glslang dependency for every build — **your call**; the doc records the trade rather than deciding it. Nothing includes these headers yet. The host side that records the passes is the next PR. ### The census `RX_VK_MRT_CENSUS=1` logs each distinct render target set once, from `targets()` (once per `NOP_SET_RT`, not per draw), capped at 512 sets, free while off. It exists to answer whether a denoising upscaler — DLSS Ray Reconstruction, FSR Ray Regeneration — could ever have inputs here, since both want a ray-traced renderer's albedo, normal and roughness guides rather than colour/motion/depth. The honest test is whether the game writes a G-buffer. **It does bind multi-attachment passes**, which is the opposite of what I expected: of 136 distinct sets, 7 bind six attachments, 2 bind three, 6 bind two. The formats settle it the other way: | sets | attachments | what they are | |---|---|---| | 103 | 1 | the scene at 2048x1152 B10G11R11, the 2560x1440 RGBA8 sRGB present, and the UI | | 18 | 0 | depth-only | | 7 | 6 | 1x1 → 64x64, all B10G11R11: six cubemap faces down a mip chain | | 6 | 2 | 170x96 / 384x216 R16F+R32F with D32; 341x192 / 768x432 RGBA16F pairs; 480x270 two B10G11R11; 960x540 B10G11R11+R8 | | 2 | 3 | 341x192 / 768x432, R32F + undefined + R16F with D32 | A deferred G-buffer is three or four attachments at the **main render resolution** carrying albedo, packed normals and roughness. None of this is that: the scene is a single HDR target and every multi-attachment pass is a cubemap face chain or post-processing at a quarter resolution or less. So there are no guides for a denoising upscaler on this title — now on evidence rather than on the absence of a target named "normal". ### Tested Linux, RTX 5060 Ti, driver 615.71.09. All eleven variants compile and pass `spirv-val`; the regeneration check fails as it should on a changed word in a header, a changed compile option and a missing header. Full game build from a v131072 dump (build `e0f3ccfa`) in 8m20s, and a 240 s headless run produced the census above. `python -I -B tests/run_all.py`: 90 passed, with the same 14 failures and 7 skips as pristine `main` in this environment. No fps claim: that run had `RX_VK_PRECOMPILE=0`, so every pipeline compiled on demand. AI assistance: recorded in the commit trailers.
NotRexed commented 2026-11-09 05:30:25 +00:00 (Migrated from github.com)

Thank you! This has grown to about 56,000 lines (the vendored FidelityFX SDK, plus camera, vsync and frame-rate changes in host/nn_vi.c and nx2/oot_camera.py), stacked on #821. It's too large and mixed to review and test as one. Closing to keep the queue clear; please split it (the vendored SDK and shader compile script on their own, then the camera/vsync changes, each with how it was tested) and reopen.

Thank you! This has grown to about 56,000 lines (the vendored FidelityFX SDK, plus camera, vsync and frame-rate changes in host/nn_vi.c and nx2/oot_camera.py), stacked on #821. It's too large and mixed to review and test as one. Closing to keep the queue clear; please split it (the vendored SDK and shader compile script on their own, then the camera/vsync changes, each with how it was tested) and reopen.
Commenting is not possible because the repository is archived.
No description provided.