network natively on HIP with Vulkan->HIP shared memory, extend K kernels to
gfx11 (RDNA3), and add dependency/packaging/test tooling around them.
Windows: FSR RR backend, native HIP K runner, gfx11 K kernels, tooling
FSR Ray Regeneration (experimental)
- Add fsr_rr_backend.{h,cpp}, fsr_rr_passes.h and the fsr_rr/ prepare and
compose compute shaders. They run AMD's FidelityFX denoiser
(amd_fidelityfx_denoiser_dx12.dll, SDK 2.3, not bundled) on a private
vkd3d-proton device.
- Add the amdxc64 shim (tools/windows/amdxc64_shim) and two vkd3d-proton
patches for libcxx includes. AMD's amdxc64.dll is never loaded.
- d4r_nvngx_windows: RrFeature now selects between the native DLSS RR
backend and the FSR backend (D4R_RR_BACKEND=fsr). After the denoiser it
runs an internal Super Sampling stage (feature 1) to upscale. If SR is
unavailable and output != render size, the frame fails; it is never
returned noisy.
- d4r.ini / player_config: new [RayReconstruction] FsrDenoiser,
FsrStabilityBias (auto or >= 0) and FsrDetail (0-1), with validation, and
matching D4R_FSR_* environment variables.
- Add the FSR RR fixture, output-check and ngx test scripts, and a shader
compile script.
Native HIP K runner (Windows)
- CMake: add d4r_engine_k_hip (d4r-k-hip) plus separate volk/engine copies,
because VK_USE_PLATFORM_WIN32_KHR changes volk's entry points.
- Share Vulkan memory with HIP via Win32 opaque handles on Windows
(VK_KHR_external_memory_win32) and via fds on Linux. Handle types are now
intptr_t, and the platform choice is in D4R_EXT_MEMORY_* macros.
- HipNetwork: add debugRepeatHashes and debugNodeMicros, exposed as
D4R_HIP_REPEAT_CHECK, D4R_HIP_REPEAT_ALL and D4R_HIP_NODE_PROFILE in
replay.cpp.
- replay.cpp: display-resolution-motion frames (FrameFlagDisplayMotion) no
longer require near2rnd == out2rnd. Both values must still be in (0, 1].
K kernels on gfx11
- Allow position-token and enc1 tiling on __GFX11__ as well as gfx12.
- Add an opt-in ds_bpermute cross-half exchange
(D4R_WINDOWS_GFX11_DS_EXCHANGE, enabled for K on Windows gfx11). M keeps
its LDS path.
- Fix a block-barrier race in the position layer. Without POS_VSHARE a fast
wave could overwrite its hb rows while a slower wave still read them for a
later head, giving rare wrong results on gfx1101 (enc0 outputs differing
between identical launches).
Packaging and install
- install.bat, d4r.ini, WINDOWS_README: FSR option, and
install_dependencies.py to install pinned NVIDIA dependencies with staged,
hash-verified replacement and rollback.
- Update the release package, local package refresh and test scripts.
- Add the InteropBatchInputCopies option (default false). Batching is faster
on some synthetic runs, but player temporal exactness is unresolved.
Tooling and tests
- K model: capture_k_weights, build_k_model, compare_k_outputs,
benchmark_k_hip, build_capture_runtime, test_k_model and the
zluda-k-output-capture patch. Captured weights and models are private
local assets and must not enter release archives.
- Synthetic probes: test_m_ngx, test_sr_dynamic,
prepare_rr_dynamic_fixture and the d3d12/rr_ngx probe updates.
- Add tests/windows/test_install_dependencies.py.
Docs
- Update the windows-fsr-rr, k-performance, local-package, rr-interop and
rr-model-cache docs. Add windows-parity-handoff.md and
windows-parity-continuation-prompt.md.
204 lines
15 KiB
Markdown
204 lines
15 KiB
Markdown
# Native Windows FSR Ray Regeneration
|
|
|
|
## Status (2026-10-17): runs on RDNA3 through vkd3d-proton; synthetic validation only
|
|
|
|
`[RayReconstruction] Backend = fsr` answers a game's DLSS RR request (NGX feature 13) with AMD's FSR Ray
|
|
Regeneration 1.2 denoiser, then upscales with an internal Super Sampling feature. Selection, `install.bat` [F]
|
|
and `d4r\fsr` payload are described in `WINDOWS_README.txt`. **No game exposes RR to test with, so everything
|
|
below is a synthetic scene; game compatibility and image quality against DLSS RR are unverified.**
|
|
|
|
Why the earlier "zero providers" result was correct: AMD's denoiser ships only `wmma_fp8` shaders, issued
|
|
through AMD's driver extension (`AmdExtD3DShaderIntrinsicsUAV`), and RDNA3 has no FP8 WMMA hardware. Native
|
|
Windows drivers therefore offer no provider. Linux gets one through vkd3d-proton with dxil-spirv's scalar FP8
|
|
fallback and a Proton `amdxc64.dll` shim. The Windows route reproduces that:
|
|
|
|
1. **vkd3d-proton for Windows** (`tools/windows/build_fsr_payload.py`): vkd3d-proton `3dfc6f07` plus dxil-spirv
|
|
PR 307 (`f9f6294f`) and `-Denable_extended_emulation=true`, built with llvm-mingw clang; two libc++ include
|
|
patches in `patches/vkd3d-proton-windows/`. The shipped `d4r\fsr\d4r_vkd3d_core.dll` is `d3d12core.dll`
|
|
renamed and loaded by path; `IVKD3DCoreInterface::CreateDevice` is called directly (the IDL has no base
|
|
interface, so it is vtable slot 0). `DXIL_SPIRV_CONFIG=wmma_rdna3_workaround` is set in-process.
|
|
2. **amdxc64 shim** (`tools/windows/amdxc64_shim`): recovered from the denoiser's call sites (interface IDs
|
|
`{014937ec-…}` factory, `{f714e11a-…}` wave-matrix properties with a 16x16x16 type-0xb entry,
|
|
`{ba019d53-…}` intrinsics enable, `{b58d6601-…}` device info). It advertises the FP8 shape the shaders use;
|
|
the arithmetic is emulated by dxil-spirv. The device-info values are placeholders. It never forwards to
|
|
AMD's driver.
|
|
3. **Lookup redirect.** The denoiser finds `amdxc64.dll` with `GetModuleHandle`. In a game process AMD's own
|
|
D3D12 driver has already loaded the real `amdxc64.dll`, which crashes on a vkd3d device. The loaded copy of
|
|
AMD's DLL gets its `GetModuleHandleA/W` imports redirected in memory to the shim. The DLL on disk is not
|
|
modified.
|
|
|
|
Data path (`tools/windows/fsr_rr_backend.cpp`): the game queue copies the guides into the same shared D3D12
|
|
packet the native DLSS backend uses; the packet is imported into vkd3d's `VkDevice`; `fsr_rr_prepare` and
|
|
`fsr_rr_compose` (Vulkan, recorded through vkd3d's DXVK interop) convert DLSS-RR guides to FSR inputs and back
|
|
around `ffxDispatch`; the composed colour returns through the packet to a game-device texture, and the internal
|
|
SR feature upscales it into the game's output. Every hand-off is CPU-synchronized; no vkd3d patch is needed.
|
|
|
|
Evidence (all synthetic, RX 7700 XT, static camera, zero jitter):
|
|
- Probe (`fsr_rr_probe`, 960x540, 4 signals): provider found, context created, 48 frames finite, denoised, reset
|
|
handled; GPU median 20.3 ms (`build/fsr-vkd3d-probe-v1/out3`).
|
|
- NGX feature-13 path (`tools/windows/test_fsr_rr_ngx.py`, noisy scene with a known noise-free answer):
|
|
640x360 render=output without SR, RMSE vs clean 0.488 -> 0.060 (`build/fsr-rr-shim-run-v9`); 640x360 ->
|
|
1280x720 with SR from the extracted v9 package, RMSE 0.289 against 0.581 for the noisy input and 0.314 for the
|
|
noise-free render-size image nearest-upsampled (`build/fsr-rr-ngx-upscale-v9-v1`). About 10-11 ms per frame
|
|
(convert+denoise+compose ~7 ms, SR submit ~2.6 ms); first RR start compiles AMD's shaders (~65 s cold).
|
|
- Native DLSS RR regression with the same shim: `PASS NGX RR ... standalone_exact=1`.
|
|
|
|
Guide conversion check: a diagnostic (`D4R_FSR_RR_DUMP_DIR`, first `D4R_FSR_RR_DUMP_FRAMES` frames) dumps the
|
|
converted linear depth, motion and normals. On the moving fixture the converted motion equals the ground-truth
|
|
`PreviousUV - CurrentUV` (FFX's contract) to 1.5e-6, the view-z delta is 0 for lateral motion, and linear depth is
|
|
reconstructed to 5e-7 (`build/fsr-rr-dump-moving-v2`).
|
|
|
|
Motion vectors matter, measured (FSR alone, same-size, moving camera, RMSE vs noise-free): with AMD's default
|
|
stability bias the ordering was misleading (correct 0.107, flipped 0.066, zeroed 0.158, because default bias
|
|
over-weights history and rejection scored better); at stability bias 0 it is physically ordered (correct 0.033,
|
|
flipped 0.042, zeroed 0.061). `FsrStabilityBias` (`D4R_FSR_RR_STABILITY_BIAS`), `FsrDetail` (`D4R_FSR_RR_DETAIL`),
|
|
`D4R_FSR_RR_KEY_<n>`, `D4R_FSR_RR_DEMOD_FLOOR` and `D4R_FSR_RR_HALO_LIMIT` follow the Linux adapter. Through the
|
|
full pipeline (FSR then SR): same size, bias 0 gives 0.036 against 0.107 for auto (3x); with a 2x SR upscale auto
|
|
gives 0.214 against 0.248 for 0 (auto 14% better). The shipped default is therefore `auto`.
|
|
|
|
Timing (RX 7700 XT, synthetic, two direct signals, SR included, per frame, median): 1080p Quality
|
|
(1280x720 -> 1920x1080) 29.8 ms total, 1440p Quality (1707x960 -> 2560x1440) 50.6 ms total. GPU timestamps
|
|
(`D4R_FSR_RR_LOG`) at 1280x720 render: prepare 0.5 ms, **AMD's denoiser dispatch 21 ms**, compose+copy 0.2 ms: the
|
|
denoiser itself dominates. Linux reports about 8 ms for AMD's denoiser at 1280x720 on gfx1101 under RADV; the same
|
|
vkd3d-proton/dxil-spirv stack is about 2.6x slower here, on AMD's Windows Vulkan driver. Optimization parity with
|
|
Linux is NOT achieved, and the cause (driver code generation for the emulated FP8 shaders, cooperative-matrix use,
|
|
a missing vkd3d/dxil-spirv option) has not been identified. The 3-4 ms outside the denoiser (packet copies and
|
|
the CPU-synchronized hand-offs) is also unoptimized.
|
|
|
|
Limits: output may exceed render size only through the SR stage (without SR, output must equal render size);
|
|
dynamic render sizes must stay within creation dimensions and the SR library's queried dynamic range;
|
|
unsupported sizes fail before submission (see the newer dynamic-resolution evidence below);
|
|
diffuse is a direct signal (no diffuse hit distance); the exposure texture only goes to the SR stage; HDR +
|
|
render-resolution unjittered motion only; RGBA16F/RGBA32F output without SR; mapped float guide formats and depth
|
|
planes have focused synthetic coverage, while additional guide encodings remain unsupported; alpha output is not produced;
|
|
first RR start compiles AMD's shaders (~65 s, cached per game exe in
|
|
`%LOCALAPPDATA%\d4r\cache\vkd3d`). Each evaluation blocks the CPU until its GPU work completes (as the
|
|
native DLSS RR backend does), so it is not asynchronous. No game evidence exists for FSR RR.
|
|
|
|
## Dynamic resolution: SR range identified and enforced (2026-10-17)
|
|
|
|
The earlier `0xBAD00005` was caused by an invalid SR render size, not a blanket lack of dynamic-resolution support.
|
|
NVIDIA's own log reports Quality's minimum as half the output dimensions: the previous 640x360 -> 1280x720
|
|
fixture tried 576x324, below the 640x360 minimum. Plain SR reproduces that rejection with flags 0 and 11,
|
|
including reset-every-frame (`build/sr-dynamic-isolation-v1/`). Valid plain-SR subrects pass 24 synthetic frames
|
|
with creation 426x256, output 640x384 and smaller render 384x224 (`build/sr-dynamic-isolation-v2/summary.json`).
|
|
|
|
The internal SR feature now queries `DLSSOptimalSettingsCallback` using
|
|
[NVIDIA's helper contract](https://github.com/NVIDIA/DLSS/blob/main/include/nvsdk_ngx_helpers.h).
|
|
FSR intersects the returned range with its existing creation-size bounds and rejects invalid sizes synchronously
|
|
before recording a command boundary. A missing or invalid query retains the previous creation-size-only range.
|
|
No ratio is hardcoded in production. The plain-SR probe validates/logs its requested subrect; the FSR wrapper
|
|
checks every reported extent and SR submission count. `--sr-bounds` tests each axis below the pinned Quality
|
|
minimum, restores valid dimensions, and completes subsequent evaluations on the same command list.
|
|
|
|
Moving-camera FSR+SR: creation 640x360, output 960x540, render sizes 640x360 / 576x324 / 480x270 and back,
|
|
24 frames per run. Both retained creation-sized textures and resized textures pass, without feature recreation.
|
|
Queried SR range is 480x270..960x540; creation dimensions still cap this FSR feature at 640x360.
|
|
Evidence: `build/fsr-rr-valid-dynamic-maxres-bounds-v1/summary.json` and
|
|
`build/fsr-rr-valid-dynamic-resized-bounds-v1/summary.json`. Both below-minimum axes reject before submission;
|
|
all 24 subsequent SR outputs are finite and nonuniform. Tail RMSE is 0.07665 versus noisy 0.48826 and ideal
|
|
nearest-resampled clean input 0.03894. This is synthetic denoising evidence, not exact/reference-quality parity:
|
|
maximum error within the sky mask reaches 0.78223. Alpha, other encodings, broader motion and games remain open.
|
|
|
|
## Format coverage and denoiser optimization trials (2026-10-17, later pass)
|
|
|
|
FSR-only output now accepts RGBA32F, including temporal switching with RGBA16F. Composition still runs at
|
|
half precision. A checked Vulkan nearest blit widens the completed half image into a lazily allocated float
|
|
image, then copies it through the existing shared packet. No CPU tensor conversion is added, and no new alpha
|
|
or FP32-denoiser claim is made. The ordinary FSR+SR path continues to hand SR a half image.
|
|
|
|
Synthetic moving-camera tests at 640x360 pass 24 frames each: canonical half output; alternating half/float
|
|
output plus alternating canonical/float guides; and RGBA32F output with typeless D32S8 depth and unchanged
|
|
stencil. `--verify-output-widening` compares every final output component against a diagnostic readback of
|
|
the same frame's composed half image, widening the independent reference where needed. All components match
|
|
exactly. Converted linear depth, motion and normals also match canonical dumps byte-for-byte across all 24
|
|
guide-switch frames. Evidence: `build/fsr-formats-{half,switch,d32s8}-composed-v1/`.
|
|
`test_fsr_rr_ngx.py --without-sr` uses the existing diagnostic environment contract because player startup
|
|
requires SR dependencies. It is not an installer/player-without-dependencies compatibility claim.
|
|
|
|
Three denoiser/compiler trials produced no reliable gain and are not promoted: cooperative conversion-loop
|
|
unrolling, forcing the minimum subgroup size, and retaining the RDNA3 layout workaround without global FP16
|
|
RTZ. AMD's denoiser and all quality settings/signals remain unchanged. At 1280x720, these fixtures start near
|
|
21.5 ms and reach about 31-32 ms in later frames, so a single whole-run median can mislead. Late-frame timings
|
|
stay within the control spread. Static and moving noisy fixtures retain the existing finite/nonuniform and
|
|
RMSE < 0.5 * noisy-input gate. No lower-resolution or skipped-signal shortcut is substituted.
|
|
|
|
Cross-process FSR output is not bit-exact even for repeated unchanged controls: in the 720p moving fixture,
|
|
frame 0 has 2,087,117 differing components and maximum absolute difference 0.1435546875 between two controls.
|
|
Their tail RMSE remains about 0.0612 versus noisy 0.4872. This precludes attributing inter-process image
|
|
differences solely to a candidate. The same-frame format-conversion gate avoids that ambiguity; K keeps its
|
|
strict full-frame exact gate. The FSR numerical variability is recorded, not fixed or reclassified as exact.
|
|
Evidence: `build/fsr-perf-720-{baseline-v2,baseline-repeat-v1,unroll-v2,wave32-v1,rte-v1}/` and
|
|
`build/fsr-perf-static-720-{baseline,unroll}-v1/`. Experimental binaries remain private under build/;
|
|
the shipped payload stays `build/fsr-payload-v1`. The two third-party source files edited for experiments
|
|
were restored; pre-existing libc++ include fixes remain. Linux denoiser performance parity is still open.
|
|
|
|
## History: the investigation before the vkd3d route
|
|
|
|
## Corrected provider descriptor recheck
|
|
|
|
The query now passes `FFX_API_CREATE_CONTEXT_DESC_TYPE_DENOISER` in
|
|
`ffxQueryDescGetVersions.createDescType`, rather than its effect ID. AMD's
|
|
[API contract](https://github.com/GPUOpen-LibrariesAndSDKs/FidelityFX-SDK/blob/main/Kits/FidelityFX/api/include/ffx_api.h)
|
|
defines this field as the creation description type. A bounded query against
|
|
the unchanged pinned AMD DLL and real RX 7700 XT still returns OK with zero
|
|
providers. The adapter reports shader model 6.6. No context is created or
|
|
denoiser dispatch attempted; FSR remains unavailable. Evidence:
|
|
`build/fsr-rr-correct-descriptor-v1/validation.json`. The earlier probe binary is
|
|
preserved in `build/fsr-rr-descriptor-baseline-v1/`. No driver, identity,
|
|
security, GPU setting or game changes occurred.
|
|
|
|
The native D3D12 provider check currently fails on RX 7700 XT. This does not
|
|
implement a working Windows FSR RR backend or DLSS RR replacement.
|
|
|
|
AMD SDK 2.3's unmodified denoiser 1.2.0.2740 loads successfully and accepts the
|
|
provider query, but returns an empty provider list. The installed driver reports
|
|
shader model 6.6. No context creation or denoiser dispatch follows this result.
|
|
The DLL's Authenticode signature was valid, signed by Advanced Micro Devices;
|
|
its SHA256 matches the Linux reference's tested binary:
|
|
`48f1e5888ba6a0a3d59a98b9751e37c392b0f7b8c223d0082d5c1f40642879d3`.
|
|
|
|
Linux's RDNA3 route uses vkd3d-proton, dxil-spirv scalar FP8 emulation, and
|
|
`wmma_rdna3_workaround`. Those are not native Windows driver features. Native
|
|
parity requires a compatible shader execution route as well as the DLSS guide
|
|
conversion and internal SR adapter. Merely importing the Linux backend or
|
|
selecting `Backend=fsr` would not supply those pieces.
|
|
|
|
The DLSS native D/E path already uses Linux's gfx11 shaders and lowering. A
|
|
read-only comparison against `34e6b0fe71d770729000a553f79b784e9b2dc279` verifies
|
|
25 shader/runtime/packer sources after CRLF/LF normalization. Both installer-built
|
|
D/E models verify all 66 products, 11 native 32-wide stages and the translated
|
|
64-wide output stage. Evidence: `build/rr-rdna3-source-parity-v1.json`.
|
|
This establishes source/recipe parity for native DLSS RR only. It provides no
|
|
FSR dispatch, FSR optimization parity or measured GPU-time parity. FSR's missing
|
|
native execution route remains a separate prerequisite.
|
|
|
|
## Reproduce without a game
|
|
|
|
```powershell
|
|
./scripts/windows/setup-fsr-rr.ps1
|
|
cmake --build build/native-engine-windows-gfx1101 --target d4r_fsr_rr_probe
|
|
./scripts/windows/test-fsr-rr.ps1 `
|
|
-Probe build/native-engine-windows-gfx1101/d4r_fsr_rr_probe.exe `
|
|
-DenoiserDll .tools/fsr-rr-1.2/amd_fidelityfx_denoiser_dx12.dll `
|
|
-OutputDirectory build/fsr-rr-new-query -QueryOnly
|
|
```
|
|
|
|
Use a new output folder under ignored `build/`. The wrapper pins the DLL hash,
|
|
captures stdout/stderr, limits execution to 120 seconds, and propagates failure.
|
|
Without `-QueryOnly`, the harness proceeds only if a provider exists: it measures
|
|
48 synthetic frames with static/moving cameras and a reset against noise-free
|
|
signals. A successful query alone is not execution or image-quality evidence.
|
|
The Windows adapter helper queries the device's DXGI identity. It does not load
|
|
Proton driver shims or modify drivers, GPU settings, or security policy.
|
|
|
|
## Source provenance
|
|
|
|
`tools/fsr_rr_probe.cpp`, `tools/fsr_rr_scene.h`, `tools/d4r_fsr_rr.h`, and the
|
|
MIT-licensed FidelityFX API headers were imported from Linux reference
|
|
`34e6b0fe71d770729000a553f79b784e9b2dc279`. The probe includes the native Windows
|
|
identity helper instead of Linux's sysfs/vkd3d identity helper. AMD's DLL stays
|
|
under ignored `.tools/` and is not included in source or release packages.
|
|
|
|
Evidence: `build/fsr-rr-native-wrapper-query/probe.json`. No game was touched.
|