TL;DR: Add an experimental FSR Ray Regeneration backend on Windows, run the K

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.
This commit is contained in:
ayo committed 2026-10-17 01:43:06 -04:00
1 parent 247fe1092d
commit a617c818de
62 files changed
+5894 -89

No files matched your search

+28 -1
View File
@@ -90,15 +90,21 @@ foreach(family k m)
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${source}")
file(READ "${source}" kernel_source)
string(REGEX MATCH "// d4r-build-flags: ([^\r\n]+)" flags_line "${kernel_source}")
set(source_flags_text "${CMAKE_MATCH_1}")
set(native_flags)
if(family STREQUAL "k" AND D4R_K_FP16_BASELINE)
list(APPEND native_flags -DD4R_K_FP16_BASELINE)
endif()
if(family STREQUAL "k" AND WIN32 AND D4R_GPU_ARCH MATCHES "^gfx11")
# Bit-preserving cross-half exchange, validated after the position-layer barrier fix.
# Keep M on its separately validated LDS exchange path.
list(APPEND native_flags -DD4R_WINDOWS_GFX11_DS_EXCHANGE)
endif()
if(family STREQUAL "m" AND D4R_M_FP16_BASELINE)
list(APPEND native_flags -DSWIN_EXACT -DSWIN_EXACT_PV)
endif()
if(flags_line)
separate_arguments(source_flags NATIVE_COMMAND "${CMAKE_MATCH_1}")
separate_arguments(source_flags NATIVE_COMMAND "${source_flags_text}")
list(APPEND native_flags ${source_flags})
endif()
add_custom_command(OUTPUT "${native_object}"
@@ -159,6 +165,23 @@ if(D4R_BUILD_NATIVE_ENGINE)
set_target_properties(d4r_engine_${runner} PROPERTIES OUTPUT_NAME "d4r-${runner}")
install(TARGETS d4r_engine_${runner} RUNTIME DESTINATION engine/bin)
endforeach()
# Experimental: K runner with the native HIP network (Win32 opaque memory shared Vulkan -> HIP). Separate
# volk/engine copies because VK_USE_PLATFORM_WIN32_KHR changes volk's loaded entry points.
add_library(d4r_volk_hip STATIC "${D4R_VOLK_ROOT}/volk.c")
target_include_directories(d4r_volk_hip PUBLIC "${D4R_VOLK_ROOT}" "${D4R_VULKAN_HEADERS_ROOT}/include")
target_compile_definitions(d4r_volk_hip PUBLIC VK_NO_PROTOTYPES VK_USE_PLATFORM_WIN32_KHR WIN32_LEAN_AND_MEAN NOMINMAX)
add_library(d4r_engine_hip STATIC engine/runtime.cpp engine/game.cpp engine/game_m.cpp engine/game_rr.cpp)
target_precompile_headers(d4r_engine_hip PRIVATE engine/volk_pch.h)
target_link_libraries(d4r_engine_hip PUBLIC d4r_volk_hip)
add_executable(d4r_engine_k_hip engine/replay.cpp engine/hip_net.cpp)
target_include_directories(d4r_engine_k_hip PRIVATE "${D4R_HIP_ROOT}/include")
target_compile_definitions(d4r_engine_k_hip PRIVATE D4R_ENGINE_VOLK D4R_ENGINE_HIP __HIP_PLATFORM_AMD__)
target_precompile_headers(d4r_engine_k_hip PRIVATE engine/volk_pch.h)
target_link_libraries(d4r_engine_k_hip PRIVATE d4r_engine_hip "${D4R_HIP_ROOT}/lib/amdhip64.lib")
if(MINGW)
target_link_options(d4r_engine_k_hip PRIVATE -static)
endif()
set_target_properties(d4r_engine_k_hip PROPERTIES OUTPUT_NAME "d4r-k-hip")
else()
message(STATUS "Standalone engine skipped: run scripts/windows/setup-native-engine.ps1 for pinned Vulkan-Headers and Volk")
endif()
@@ -203,6 +226,10 @@ target_include_directories(d4r_nvngx_windows PRIVATE "${D4R_HIP_ROOT}/include")
target_link_libraries(d4r_nvngx_windows PRIVATE version dbghelp d3d12 dxguid minhook bcrypt)
if(TARGET d4r_engine)
target_sources(d4r_nvngx_windows PRIVATE tools/windows/rr_native_backend.cpp)
# Experimental FSR Ray Regeneration backend (needs vkd3d-proton, AMD's denoiser DLL and the amdxc64 shim at run time).
target_sources(d4r_nvngx_windows PRIVATE tools/windows/fsr_rr_backend.cpp)
target_compile_definitions(d4r_nvngx_windows PRIVATE D4R_FSR_RR_BACKEND=1)
target_link_libraries(d4r_nvngx_windows PRIVATE dxgi)
target_link_libraries(d4r_nvngx_windows PRIVATE d4r_engine)
target_compile_definitions(d4r_nvngx_windows PRIVATE D4R_NATIVE_RR_BACKEND=1)
endif()
+158 -1
View File
@@ -1,4 +1,152 @@
# Native Windows FSR Ray Regeneration investigation
# 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.
@@ -16,6 +164,15 @@ 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
+145
View File
@@ -1,5 +1,150 @@
# Windows K performance work
## Current optimization pass: strict DS exchange, 1080p and 1440p (2026-10-17)
After the position-layer barrier fix, the explicit gfx11 DS cross-half exchange passes the strict gate.
`kernels/common/wave32_exchange.h` uses `ds_bpermute` with the current wave's lane XOR 16 instead of the
volatile LDS array and its fences. No arithmetic, half rounding, weights or tensor layouts change.
Windows gfx11 K selects it through `D4R_WINDOWS_GFX11_DS_EXCHANGE`; M retains its validated LDS path.
The experimental builder retains explicit `-LaneExchange lds|ds` selection for comparisons.
Final CMake-built candidates, authentic private K model, synthetic 24-frame temporal cases, 25 frames/submit,
12 timing trials with three warmup trials excluded. Timestamps span Vulkan front, HIP network, Vulkan back
and the CPU synchronization gaps; these are not network-only or game timings:
| Output | Fixed LDS baseline | Strict DS candidate | Change |
|---|---:|---:|---:|
| 1920x1080 | 2286.4 us | 1811.9 us | 20.8% faster |
| 2560x1440 | 3587.7 us | 2885.4 us | 19.6% faster |
All five frame products match baseline hashes exactly at both sizes (120 products per case).
Evidence: `build/k-ds-cmake-1080-final-v1/summary.json`, `build/k-ds-cmake-1440-final-v1/summary.json`.
Independent manual-build comparisons also pass, at 1808.7/2877.0 us. CMake/manual code objects differ as
artifacts; the actual CMake objects receive their own full output checks. Final native objects are under
`build/k-ds-native-cmake-v2/`. The v1 CMake candidate is rejected: an intermediate CMake regex overwrote the
captured per-source flags; the capture is now saved immediately, preserving CU and QKV-split flags.
Further experiments retained no reliable additional gain: position token counts 1/2/4, shared V values,
SPIR-V size optimization of input/output, and all-WGP scheduling. Every compared candidate here is exact;
many are slower. WGP measured 2862.3 vs 2847.2 us at 1440p and 1795.7 vs 1813.9 at 1080p, within small
run-to-run differences and inconsistent across sizes. It is not promoted. Evidence is under
`build/k-hip-opt-sweep-v1/`, `k-hip-opt-ds-share-v1/`, `k-hip-opt-stage-mt1-v1/`, and `k-ds-wgp-*-v1/`.
The HIP standalone engine/model is still separate from the package's NGX feature-1 path. The package can
use the same faster K layer objects through its existing ZLUDA/NGX route; that route requires separate
whole-frame validation and CPU interop timing. Linux targets of 1.5/2.47 ms remain unmet. Further substantial
work would require the Vulkan front/output stages or the cross-API synchronization path; it is outside this
bounded optimization pass. Broader K temporal/reference-quality and production-adapter work remains open.
The final v14 ZIP's loader/INI passes 64 exact synthetic NGX K frames per size in control/candidate/candidate/control
order. CPU interop medians: 1080p 3.900 -> 3.3915 ms (13.0%); 1440p 5.4015 -> 4.820 ms (10.8%). Unlike the
standalone timestamp table, these include CPU API/copy/synchronization/NGX overhead. They are not game FPS.
Evidence: `build/k-ds-local-v14-{1080,1440}-v1/summary.json`. An earlier legacy 1080p run overlaps build activity;
its exactness evidence remains valid, but the packaged player runs supersede its timings.
Stability diagnostic: `build/k-ds-repeat-node-final-v1.log`, 300 frozen-input rounds x 8 window phases (2,400
launches), zero output-hash mismatches. Its individually instrumented network nodes total 1,926.04 us at 1440p.
That instrumentation is separate from accepted frame timings. Raw readbacks created by this pass were checked
against recorded hashes and pruned to recover 5.05 GiB; logs, summaries, hashes and final previews remain.
Cleanup inventory: `build/k-opt-readback-cleanup-v1.json`. No earlier artifacts were deleted.
## Temporal isolation follow-up
The intermittent drift is not fixed or attributed to a specific layer. New
`benchmark_k_interop.py --reset-every-frame` passes an explicit diagnostic option
to the D3D12 harness, verifies its application and records it in the report.
This mode does not represent normal temporal inference or game performance.
With the validated private CU output, 16 frames per run and four player runs:
| Isolation | Exact evaluations | Evidence under build/ |
|---|---:|---|
| Reset every frame, ABBA input batching | 64 | k-player-reset-every-frame-1080-v1 |
| All K network layers translated, CU output retained, temporal ABBA | 64 | k-player-translated-network-temporal-v2 |
| All native K, unbatched identical controls | 64 | k-player-native-temporal-controls-v1 |
| Native encoders only, translated decoders, temporal ABBA | 64 | k-player-native-encoders-temporal-v1 |
| Translated encoders, native decoders only, temporal ABBA | 64 | k-player-native-decoders-temporal-v1 |
| All native K and CU output, temporal ABBA rerun | 64 | k-player-native-temporal-abba-v2 |
Each row has an authoritative `summary.json`. Reset-every-frame also produces
one identical output hash across all frames within each run. Thus accumulated
state matters in the earlier failing case, but that alone does not establish a
history implementation defect. The mixed native/translated cases do not isolate
an individual layer, and the full native ABBA rerun passes after the earlier
failure. Initialization, cache state, scheduling and arithmetic remain possible
investigation targets; no one cause is demonstrated.
Diagnostic manifests select translated or native K layers while preserving the
same object files, private CU output, dependencies and baseline. Runtime logs
verify actual translated enc0/dec0 selection for the all-translated case. Those
manifest variants remain private under `build/k-network-*-native-output-cu-v1/`
and `build/k-translated-network-output-cu-v1/`; no source kernel, runtime default,
game or delivered ZIP was changed to use them. First translated-network fixture
v1 failed before NGX because its setup command had a quoting error and did not
create the selected directory. A native Python fixture setup corrected it; v2
passes. No security policy block or bypass occurred.
The reset diagnostic's timings are not normal K timings. Mixed/translated cases
also are not proposed performance configurations. The full native rerun records
CPU medians 3.018/2.9155 ms, unbatched/batched, but earlier drift still prevents
promoting a default or claiming deterministic behavior. No GPU-time/Linux parity,
image-quality or real-game speed conclusion follows from these isolation runs.
## Latest: rejected kernel candidates and unresolved player temporal exactness
No new kernel or batching default is promoted. The current delivered ZIP remains
v8 with batching off. The working tree adds an experimental
`[Interop] BatchInputCopies` switch, default false, plus parser/environment tests
and checker support. Missing/invalid values keep older unbatched behavior.
CPU player-config tests pass; checker syntax parses. Exactness in the player
performance fixture remains unresolved, so keep the switch false.
`benchmark_k_interop.py --control-native DIR` selects an explicit verified native
baseline. `--player-config` stages dependencies and selects batching/native paths
through actual INI. It preserves per-run player logs. `--sequence controls` or
`batches` repeats identical settings to diagnose determinism. Failed comparisons
record mismatching frame names and hashes; they do not receive accepted timing.
`compare_k_outputs.py` measures preserved half-output differences without GPU work.
New private experiments, all artifacts retained and excluded from ZIPs:
- WGP scheduling for the existing native output override: 64 exact 1080p frames,
with CU/WGP warmed CPU interop medians 3.048/3.0455 ms (no reliable gain).
At 1440p the candidate failed the exact-output gate: five of 16 compared
frames differ, 14,836 components, max absolute difference 0.00537109375;
all values finite. Candidate rejected. Evidence:
`build/k-output-wgp-vs-cu-{1080,1440}-v1/` and 1440p `mismatch-analysis.json`.
- CU input override: recipe changes only eight round-to-zero idioms; no surface
stores matched this LDR input variant. Four of 16 frames differ, 993 components,
max absolute difference 0.00048828125; finite. Candidate rejected. Evidence:
`build/k-input-cu-vs-output-cu-1080-v1/mismatch-analysis.json`.
- Legacy/environment mode with validated private CU output plus existing input
batching: 128 exact frames across 1080p/1440p ABBA comparisons. Warmed CPU
interop medians: 3.072/2.9305 ms at 1080p, 4.4605/4.3345 ms at 1440p,
unbatched/batched. `build/k-output-cu-batch-{1080,1440}-v1/summary.json`.
Player-mode checks exposed an important limit to those narrow successful runs:
- In the baseline-kernel 16-frame ABBA run, both batched runs match first control
exactly, but the final unbatched control differs in 15 frames, 38,472
components, max absolute difference 0.01318359375. This does not isolate a
batching defect. `build/k-batch-player-1080-v1/repeat-analysis.json`.
- Four identical unbatched player runs of eight frames each pass all 32 exact
frames. `build/k-player-control-repeat-1080-v1/summary.json`.
- A 16-frame player comparison using the validated private CU output differs
between control and batched run in eight frames, 1,416 components, max absolute
difference 0.00048828125. First difference is frame 8, with RGB differences
spread across the image and unchanged alpha. Candidate is not accepted.
`build/k-batch-player-output-cu-1080-v1/difference-locations.json`.
These results leave temporal determinism, arithmetic/initialization and ordering
as investigation targets. They do not prove any one of those causes. The kernel
candidates failed acceptance, but baseline drift prevents attributing every
difference solely to a candidate. No relaxed tolerance was substituted for the
exact gate. Private candidates, validated CU control and original runtime remain
separate. No GPU-time/Linux parity or game-speed claim, game access, driver/GPU/
security change, commit or publication occurred.
Linux's reported 1.5 ms at 1080p and 2.47 ms at 1440p remain targets, not verified
Windows results. The synthetic measurements below include CPU synchronization,
format/array copies and NGX execution. They are not directly comparable to a
+180 -1
View File
@@ -1,6 +1,150 @@
# Local gfx1101 drop-in package
## Current local development package: v4
## Current local development package: v14 (2026-10-17)
`build/d4r-windows-gfx1101-local-v14.zip`, 133,240,107 bytes, SHA256
`2f88dec3b4086371efefa8fbfa316c3f7b86fb7858ca1c1bdee63146056f7edf`.
Every manifest hash and the exact ZIP entry set verify. It adds strict DS lane exchange in all eleven K objects
and FSR-only RGBA32F output with temporal format switching. The original FSR denoiser payload is unchanged;
compiler/subgroup/rounding trials produced no reliable gain. v13 and older packages remain preserved.
Actual fresh extraction, architecture loader and player INI: K control/candidate/candidate/control passes 64 exact
frames per size. Warmed CPU interop medians are 3.900 -> 3.3915 ms at 1080p and 5.4015 -> 4.820 ms at 1440p,
about 13%/11% faster. These include CPU synchronization/copies/NGX and are separate from standalone HIP GPU
timestamps (1.812/2.885 ms). The HIP runner/private model is not in the ZIP.
FSR-only test through the fresh loader passes 24 moving-camera frames with alternating float guides and half/float
output, typeless D32S8 and preserved stencil. Every output component equals the same-frame composed half result,
widened independently where needed. FSR+SR dynamic regression passes another 24 frames, retained resources and
minimum-bound rejections. Native RR passes six exact camera/reset frames with contracts and enhanced/inherited
states. Evidence: `build/k-ds-local-v14-{1080,1440}-v1/`, `build/fsr-local-v14-format-switch-v1/`,
`build/fsr-local-v14-dynamic-sr-v1/`, `build/rr-native-local-v14-regression-v1/`.
All execution is synthetic on gfx1101. Full install.bat was not rerun, and no game, driver/security/GPU setting,
commit, branch, PR or publication was involved. Linux targets, FSR denoiser speed, broader encodings/alpha/reference
quality and the original remaining parity gaps are not declared complete. This ends the user's narrowed pass.
## Preserved v13 package evidence
`build/d4r-windows-gfx1101-local-v13.zip`, 133,239,594 bytes, SHA256
`b2ff1157e27b9353afcf287fa8df011c7efa119ac4e26ab7746166ff87b05b34`.
Every manifest hash verifies. This is v12 plus FSR/SR dynamic render-size support within the loaded SR library's
queried bounds. Below-minimum sizes reject before command recording. The three fixed K objects remain included;
the HIP standalone K engine/model is still outside the package. All older packages remain preserved.
Fresh-extracted architecture loader/INI passes 24 moving-camera dynamic FSR+SR frames, including both below-minimum
axis rejections, retained input resources and all SR submissions (`build/fsr-rr-local-v13-dynamic-bounds-v1/`).
Native RR regression passes six exact camera/reset frames with contracts and enhanced/inherited states
(`build/rr-native-local-v13-dynamic-regression-v1/`). These are synthetic tests with separately staged dependencies.
Full `install.bat` was not rerun: the NGX core is absent from the download cache and would require the 945 MiB
driver archive. No driver runs, game interaction, commit or publication occurred. See `windows-fsr-rr.md` and the
newest checklist entry for exact synthetic coverage and remaining limits. Real-game/reference-quality and Linux
performance parity remain unverified.
## Preserved v8 package evidence
`build/d4r-windows-gfx1101-local-v8.zip` is 130,975,742 bytes; SHA256
`962530d1a914268cc6f9d2ec908185f8e4d9e5baafca6800d77ebc3281a647fe`.
It adds GPU RGBA32F RR output conversion and temporal RGBA16F/RGBA32F output
switching to v7 coverage. Packaged loader/INI E passes 12 exact evaluations
across two live features, inverted D32S8 and unchanged stencil. Float32 results
match widened standalone half output; this is synthetic engine consistency,
not NVIDIA reference quality or game/timing parity.
Installer still installs pinned SR, RR DLL and NGX core through bundled Python,
with optional D/E model generation/deployment. No NVIDIA DLL, private K override
or generated model is bundled. Every ZIP manifest hash verifies. Evidence:
`build/d4r-windows-gfx1101-local-v8.validation.json` and
`build/rr-ngx-local-v8-output-switch-e-v1/summary.json`. Other output encodings,
external exposure, FSR execution, K timing parity and original engine gaps remain.
## Preserved v7 package evidence
`build/d4r-windows-gfx1101-local-v7.zip` is 130,973,094 bytes; SHA256
`b7c68e6f6148c648b6e8e6f47362e0d5f0f6c6796d651d1e762a80d8f07e8d54`.
It retains the v6 native Python installer/clean portable tools and adds native
RR dynamic render-size handling. NVIDIA SR/RR/core DLLs still download locally;
no NVIDIA binary, private K override or RR model is bundled. All final manifest
hashes verify. Older ZIPs are preserved.
Native RR supports per-frame render dimensions from 64 pixels through creation
size, with fixed output size and implicit history reset on dimension changes.
Model weights/pipelines and the shared packet/fence remain loaded. Packaged
loader/INI E tests pass 16 exact evaluations across two live features and changing
sizes, both with resized input textures and retained creation-sized textures.
Six static D depth/format-switch frames also pass. These are synthetic engine
consistency checks, not NVIDIA reference quality, real-game RR or timing parity.
See `windows-rr-interop.md` for coverage and limitations. Evidence:
`build/d4r-windows-gfx1101-local-v7.validation.json` and
`build/rr-ngx-local-v7-dynamic-two-e-v1/`,
`build/rr-ngx-local-v7-max-dynamic-two-e-v1/`, and
`build/rr-ngx-local-v7-static-regression-v2/`.
## Preserved v6 package evidence
`build/d4r-windows-gfx1101-local-v6.zip` is 130,970,436 bytes; SHA256
`a144185823af334483543d4989584d65cfa0418049e56fb8e58410b91ae8700b`.
It adds the pristine portable-tools v2 inputs and native Python dependency
installer to the verified runtime. Every ZIP manifest hash verifies. No NVIDIA
DLL, private K override or generated RR model is bundled. All earlier ZIPs,
including intermediate v5, remain preserved. V5 lacks the final README update;
use v6 for the documented installer flow.
Extract beside the game executable and run `install.bat`. The batch wrapper
uses bundled isolated Python, without PowerShell, system Python or Git Bash.
It installs pinned SR 310.9.1 at `d4r/dlss/nvngx_dlss.dll`, RR 310.9.1 at
`d4r/rr/nvngx_dlssd.dll`, and 617.14 core at `d4r/ngx/_nvngx.dll`. Driver archive
is only extracted, never executed. The Python installer retains the exact
existing PowerShell installer URLs and SHA256 pins. Hash identity excludes old
596.36 core fixtures.
Choose N to retain existing RR settings. Optional D/E selection builds and
enables a private exact gfx1101 model. Tool inputs copy by manifest into the
external cache; generated products remain there and in a versioned private
runtime model directory. Existing models remain intact. Configuration selection
preserves comments/BOM/newlines and creates an exact external INI backup.
Model failure leaves verified DLLs installed and does not select an incomplete
model. Automatic RR building currently requires a gfx1101-only package.
DLL replacement first stages and verifies all changes and external original
backups. Replacement failure rolls back applied entries; concurrent edits are
preserved. Reruns skip matching DLLs and reuse only verified model products.
`install.bat --yes --offline --cache-root PATH --backup-root PATH` supports
preverified DLL caches. Add `--rr-preset D` or `E` and `--enable-rr` for model
generation/selection without the interactive prompts. Cache and backups must
stay outside the game directory.
Validation uses ignored mock game folders only:
- Eight CPU tests cover preservation, rollback including newly created files,
concurrent edits, cache rejection, pin parity and manifest-only tool copying.
- Actual v5 batch wrapper builds/deploys D in 48 steps, verifies 66 model
products, exact DLL/INI backups, unrelated-file preservation and an unchanged
repeat. E also completes its actual 48-step installer path.
- Actual v6 ZIP interactive default installs all three pinned DLLs while
declining model generation leaves INI unchanged. E cache reuse/deployment,
repeat safety and tampered-tool rejection with unchanged runtime hashes pass.
- Native downloader fetches verified official 7-Zip, reuses the hash-verified
617.14 driver archive and extracts a core matching its exact pin. No driver runs.
- Installer-deployed E selected solely through INI passes six exact synthetic
NGX hardware-depth frames through the packaged architecture loader, with
optional guides, enhanced/inherited access and stencil preservation. This is
engine consistency, not NVIDIA quality reference or real-game RR evidence.
Evidence: `build/native-installer-v1/validation.json`,
`build/native-installer-v2/validation.json`,
`build/native-installer-download-v1/validation.json`,
`build/rr-ngx-native-installer-e-v1/summary.json`, and
`build/d4r-windows-gfx1101-local-v6.validation.json`. The final v6 fixture also
passes the same six exact E frames:
`build/rr-ngx-local-v6-installer-e-v1/summary.json`. Modified PowerShell release
packager passed static parser validation; its fixture suite was not rerun under
the recorded script policy. No policy bypass, game access, commit, publication
or system setting change occurred. FSR execution, broad game/quality validation,
K timing parity and remaining original engine requirements remain open.
## Preserved v4 package evidence
`build/d4r-windows-gfx1101-local-v4.zip` is 90,720,715 bytes; SHA256
`74fbb4c78dba3e616b9bbba7dc1fe4cddf0a17855968b174f8ea69968d873dfc`.
@@ -96,3 +240,38 @@ depth and high-resolution motion vectors; other variants retain the normal path.
The workflow output under `build/k-native-cache-workflow/native` is byte-identical
to the previously validated 17-object candidate. The new player-mode smoke loaded
that directory. These NVIDIA-derived objects stay local and are absent from V3.
## Local v9: experimental FSR RR payload (2026-10-17)
`build/d4r-windows-gfx1101-local-v9.zip`, 133,200,126 bytes, SHA256
`332719443a9b050e6a5b479816a81fb4d3e482a8956e2c20f2596904f7f6152d`. Built by `tools/windows/refresh_local_package.py`
from the verified v8 ZIP (`--fsr-payload build/fsr-payload-v1`); all manifest hashes verified; v2-v8 preserved.
Added over v8: the shim with the FSR RR backend and its Super Sampling stage (`d4r/_nvngx_gfx1101.dll`);
`d4r/fsr/d4r_vkd3d_core.dll` (vkd3d-proton built by `tools/windows/build_fsr_payload.py`, LGPL-2.1, source offer
and patches in `d4r/fsr/SOURCE.txt` and `d4r/fsr/patches/`) and `d4r/fsr/amdxc64.dll` (d4r's capability shim, not AMD
software); vkd3d-proton, dxil-spirv and dxbc-spirv licence texts in `d4r/licenses/`; `install.bat` with an [F]SR
choice and `install-dependencies.py --enable-fsr` / `--install-fsr` (AMD's denoiser, SHA256 `48f1e588...`, is
downloaded hash-verified, backed up and rolled back like the NVIDIA DLLs, and `Backend = fsr` is written to
`d4r.ini` with an external backup); `d4r.ini` key `[RayReconstruction] FsrDenoiser`; README text.
Still not bundled: NVIDIA DLLs, any RR/K model, AMD's denoiser. FSR RR is off by default.
Verification (synthetic only, no game): ZIP extracted fresh and `test_fsr_rr_ngx.py` run against the extracted files
(`build/fsr-rr-ngx-upscale-v9-v1`: PASS, SR used, RMSE 0.289 vs 0.581 noisy input); release-package test PASS
cases=7 including FSR payload accept/reject cases (`build/release-package-fsr-v1`); installer unit tests 12 OK;
player-config test PASS cases=2; installer FSR path run for real on the extracted copy (AMD DLL downloaded,
hash matched, installed, single `Backend` line changed, backup equals original, second run changes nothing).
A full `install.bat` run was not repeated: the NGX core is not in the local download cache and would pull the
945 MiB driver archive. The new K work (HIP network, authentic K model) is NOT in v9.
## Local v11: v9 plus FSR tuning, GPU timing and dynamic-size refusal (2026-10-17)
`build/d4r-windows-gfx1101-local-v11.zip`, 133,236,207 bytes, SHA256
`ba91dd6cc178865244c9903a461f213cdcacf38bcf6905e919ba5e8ee652b228`; built the same way as v9 (verified v8 base,
`--fsr-payload build/fsr-payload-v1`), all hashes verified. v9 and an intermediate v10 are preserved but superseded.
Differences from v9: `d4r.ini` keys `FsrStabilityBias` (default auto) and `FsrDetail`; the shim's GPU-timestamp log
(`D4R_FSR_RR_LOG`), motion/depth dump diagnostic, tuning keys, exposure handled by the SR stage, and a clean refusal
of render sizes below the creation size. Validated from a fresh extraction with `test_fsr_rr_ngx.py`: static
same-size 0.127 (noisy 0.485), moving 640x360 -> 1280x720 0.2135 (noisy 0.554), moving same-size 0.1069 (noisy 0.486);
all use SR. Synthetic only, no game. The HIP K network and authentic K model are NOT in this package.
+90
View File
@@ -1,5 +1,95 @@
# Native RR D3D12/Vulkan bridge gate
## Current output coverage: RGBA16F and RGBA32F
Native RR now accepts `DXGI_FORMAT_R32G32B32A32_FLOAT` output as well as the
existing RGBA16F output. Model inference remains unchanged. A same-size nearest
Vulkan image blit widens the model's half output on the GPU, then copies the
float32 image through the shared packet into the borrowed D3D12 output texture.
The backend checks source/destination blit support and allocates a private
device-local conversion image only when needed. Packet output space reserves
the largest supported texel size. No CPU tensor conversion enters production.
Output format may change between frames without feature/model recreation or
history reset. The conversion image is retained, with explicit transfer
read/write barriers. Output dimensions remain fixed. Other encodings, external
exposure conversion and game/reference-quality validation remain open.
Synthetic D: eight frames across dynamic dimensions, two live features,
creation-sized input resources, optional guides and enhanced/inherited states
pass all 16 evaluations. Every RGBA32F component matches the independently
widened standalone half reference exactly. Synthetic E: six inverted D32S8
frames alternate half/float32 output without losing temporal consistency or
changing stencil. Packaged v8 loader/INI with two live E features passes all 12
evaluations. Final handles/parameters are released before queued submission.
This establishes output conversion and engine consistency, not NVIDIA reference
quality, game RR or a performance improvement.
Evidence: `build/rr-ngx-output32-d-v1/summary.json`,
`build/rr-ngx-output-switch-e-v1/summary.json`, and
`build/rr-ngx-local-v8-output-switch-e-v1/summary.json`. Prior baseline binaries
remain in `build/rr-output32-baseline-v1/`; older ZIPs remain unchanged.
## Current native RR coverage: dynamic render sizes and two live features
Native NGX feature 13 accepts per-frame render dimensions from 64 pixels through
the feature's creation dimensions. Output size remains fixed. Each queued frame
owns its render dimensions alongside the existing resource/scalar/matrix
snapshot. Dimension changes rebuild size-dependent views, descriptors and
temporal history only after prior GPU work retires. Vulkan model pipelines,
uploaded weights, the creation-sized shared packet and timeline fence survive.
History resets implicitly on a size change, even without a caller Reset flag.
Unchanged dimensions preserve history. Invalid dimensions fail before recording
or submission; failed reconfiguration poisons the backend rather than allowing
partially rebuilt history to run.
`prepare_rr_dynamic_fixture.py` generates independent standalone segment
references and combines them into an NGX sequence with no explicit reset at size
changes. The sequence is 96x96 twice, 64x64 twice, 96x96 twice, and 72x80 twice,
with fixed 192x192 output and alternating absent/present optional guides.
The 48x48 first fixture failed at the engine's existing 64-pixel minimum before
NGX testing; it remains preserved. Corrected D/E fixtures each pass eight exact
frames. D passes without SR dependencies; E passes player INI selection.
`test_rr_ngx.py --dynamic-extents` checks every size/output and the exact resize
diagnostic sequence. `--two-features` evaluates two live RR features with separate
Vulkan devices on each game command list. It reads back both outputs separately
and releases both handles/parameters before final submission. Packaged v7 E
passes all 16 evaluations with enhanced/inherited access, optional guides and
owned geometry. `--max-resources` retains creation-sized D3D12 input textures
while the render rectangles change; that path also passes 16 exact evaluations.
This tests multiple live handles and ordered delayed submission, not parallel
GPU inference, threaded submission or asynchronous throughput.
Six static D frames still pass inverted D32S8 depth, unchanged stencil,
RGBA32/RG32/scalar-R16 guide switching and AutoExposure. All comparisons are
against the same standalone engine, not NVIDIA quality-reference frames or game
RR. No performance gain is claimed for resize support or model reuse.
Evidence: `build/rr-ngx-dynamic-d-v2/summary.json`,
`build/rr-ngx-dynamic-e-v2/summary.json`,
`build/rr-ngx-dynamic-two-features-d-v1/summary.json`,
`build/rr-ngx-local-v7-dynamic-two-e-v1/summary.json`,
`build/rr-ngx-local-v7-max-dynamic-two-e-v1/summary.json`, and
`build/rr-ngx-local-v7-static-regression-v2/summary.json`.
The first E wrapper run completed all exact GPU outputs but failed its diagnostic
check because player mode redirects stderr to d4r_nvngx.log. The verifier now
reads that log in player mode; the corrected run passes. Earlier artifacts and
baseline shim/probe copies remain intact under build/rr-dynamic-baseline-v1/.
The first v7 static regression invoked the target DLL directly beside its
player INI while requesting an environment-only/no-SR test. Player configuration
correctly overrode those environment settings and rejected missing local SR
dependencies before GPU evaluation. The corrected packaged-loader/player-INI
fixture supplies pinned dependencies and passes all six frames; no policy block
or bypass occurred.
Other existing native coverage includes optional guides, float/UNORM inputs,
typed/typeless D32/D32S8 depth, preserved stencil and creation-flag validation.
Guide encodings, external exposure conversion, output-format conversion, output
size changes, broader concurrency, game/reference-quality validation and FSR
execution remain incomplete. Older sections below describe earlier milestones;
the newest checklist entries supersede those earlier limitations.
## Runtime extraction in progress (2026-10-16)
The packet layout now has a geometry-only constructor with no fixture I/O.
+20
View File
@@ -1,5 +1,25 @@
# Local Windows RR model cache
## Installer integration (supersedes earlier integration gaps below)
The local v6 runtime ZIP now includes clean portable tools and
`tools/windows/install_dependencies.py` as `install-dependencies.py`.
`install.bat` uses bundled Python to install all three pinned NVIDIA dependencies,
including `d4r/rr/nvngx_dlssd.dll`. It optionally builds/deploys/enables D/E.
No system Python, pip, PowerShell build wrapper or Git Bash is required.
Only gfx1101-only packages support automatic RR building at present.
Tool sources copy by verified manifest to the external dependency cache;
private model products stay outside package inputs. D and E actual installer
builds complete 48 steps. D deployment verifies all 66 products and repeats
without new backups or INI changes. V6 interactive DLL-only installation and E
cache reuse pass. A modified bundled tool is rejected without runtime changes.
The installer-deployed E model passes six exact synthetic NGX hardware-depth
frames through packaged loader/INI selection. Model-build failure leaves the
separately verified DLL installation in place. See `windows-local-package.md`
for archive identity, commands and evidence. No new inference speed or image
quality claim follows from this installer integration.
## Native Python builder without Git Bash
`tools/windows/build_rr_model.py` now runs the extraction, template generation,
+49 -4
View File
@@ -9,6 +9,7 @@
#include "hip_net.h"
#include <hip/hip_runtime_api.h>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <stdexcept>
@@ -64,8 +65,13 @@ uint32_t global32(hipModule_t module, const char* name, uint32_t fallback) {
}
return v;
}
void* import(int fd, uint64_t bytes, hipExternalMemory_t& memory) {
hipExternalMemoryHandleDesc desc{}; desc.type = hipExternalMemoryHandleTypeOpaqueFd; desc.handle.fd = fd; desc.size = bytes;
void* import(intptr_t fd, uint64_t bytes, hipExternalMemory_t& memory) {
hipExternalMemoryHandleDesc desc{}; desc.size = bytes;
#ifdef _WIN32
desc.type = hipExternalMemoryHandleTypeOpaqueWin32; desc.handle.win32.handle = reinterpret_cast<void*>(fd);
#else
desc.type = hipExternalMemoryHandleTypeOpaqueFd; desc.handle.fd = int(fd);
#endif
HIPCHECK(hipImportExternalMemory(&memory, &desc));
hipExternalMemoryBufferDesc buffer{}; buffer.offset = 0; buffer.size = bytes;
void* at = nullptr; HIPCHECK(hipExternalMemoryGetMappedBuffer(&at, memory, &buffer));
@@ -101,6 +107,7 @@ struct HipNetwork::Impl {
std::vector<Node> preps;
std::vector<Node> nodes[MaxGraphs];
std::vector<void*> owned;
std::vector<uint64_t> ownedBytes;
hipExternalMemory_t shared[2]{};
hipStream_t stream = nullptr;
hipGraph_t graph[MaxGraphs]{};
@@ -108,6 +115,7 @@ struct HipNetwork::Impl {
uint32_t graphs = 2, frame = 0;
hipEvent_t start = nullptr, stop = nullptr;
bool pending = false;
void* outputPtr = nullptr; uint64_t outputBytes = 0;
~Impl() {
if (stream) (void)hipStreamSynchronize(stream);
for (auto e : executable) if (e) (void)hipGraphExecDestroy(e);
@@ -125,7 +133,7 @@ struct HipNetwork::Impl {
}
};
HipNetwork::HipNetwork(const std::string& directory, uint32_t width, uint32_t height, int tokensFd, int outputFd,
HipNetwork::HipNetwork(const std::string& directory, uint32_t width, uint32_t height, intptr_t tokensFd, intptr_t outputFd,
uint64_t tokensBytes, uint64_t outputBytes) : impl(std::make_unique<Impl>()) {
auto& n = *impl;
HIPCHECK(hipInit(0));
@@ -143,6 +151,7 @@ HipNetwork::HipNetwork(const std::string& directory, uint32_t width, uint32_t he
if (count != layers || file.size() < 16 + 64ull * count) throw std::runtime_error("invalid hipnet.bin");
void* tokens = import(tokensFd, tokensBytes, n.shared[0]);
void* output = import(outputFd, outputBytes, n.shared[1]);
n.outputPtr = output; n.outputBytes = outputBytes;
// Both dimensions describe the layer-0 token grid: preset M's render dimensions, preset K's output
// dimensions (makeKShape: max 256, align 32 of the output quarter resolution).
uint32_t tw = 0, th = 0;
@@ -191,7 +200,7 @@ HipNetwork::HipNetwork(const std::string& directory, uint32_t width, uint32_t he
return uint32_t(names.size() - 1);
};
auto allocate = [&](uint64_t bytes) {
void* p = nullptr; HIPCHECK(hipMalloc(&p, size_t(bytes) + 4096)); n.owned.push_back(p); return p;
void* p = nullptr; HIPCHECK(hipMalloc(&p, size_t(bytes) + 4096)); n.owned.push_back(p); n.ownedBytes.push_back(bytes); return p;
};
std::vector<uint32_t> layerKernel(layers), divisor(layers);
std::vector<int32_t> shiftX(layers), shiftY(layers);
@@ -328,5 +337,41 @@ bool HipNetwork::done() {
check(e, "hipEventQuery"); n.pending = false; return true;
}
void HipNetwork::wait() { HIPCHECK(hipStreamSynchronize(impl->stream)); impl->pending = false; }
std::vector<uint64_t> HipNetwork::debugRepeatHashes(uint32_t rounds) {
// per launch: the output buffer hash, then one hash per owned allocation (weights and activations)
auto& n = *impl; std::vector<uint64_t> hashes; std::vector<unsigned char> host;
auto hash = [&](void* p, uint64_t bytes) {
host.resize(size_t(bytes)); HIPCHECK(hipMemcpyDtoH(host.data(), p, host.size()));
uint64_t h = 1469598103934665603ull;
// 64-bit words: byte-wise FNV over the ~18 MB head buffer cost more than the network itself
const size_t words = host.size() / 8; const uint64_t* data = reinterpret_cast<const uint64_t*>(host.data());
for (size_t i = 0; i < words; ++i) { h ^= data[i]; h *= 1099511628211ull; h ^= h >> 29; }
for (size_t i = words * 8; i < host.size(); ++i) { h ^= host[i]; h *= 1099511628211ull; }
hashes.push_back(h);
};
for (uint32_t r = 0; r < rounds; ++r)
for (uint32_t i = 0; i < 8; ++i) {
launch(); wait();
hash(n.outputPtr, n.outputBytes);
if (std::getenv("D4R_HIP_REPEAT_ALL"))
for (size_t k = 0; k < n.owned.size(); ++k) hash(n.owned[k], n.ownedBytes[k]);
}
return hashes;
}
std::vector<float> HipNetwork::debugNodeMicros(uint32_t phase, uint32_t trials) {
auto& n = *impl; const auto& nodes = n.nodes[phase % n.graphs];
std::vector<std::vector<float>> samples(nodes.size());
hipEvent_t a = nullptr, b = nullptr; HIPCHECK(hipEventCreate(&a)); HIPCHECK(hipEventCreate(&b));
for (uint32_t t = 0; t < trials + 2; ++t)
for (size_t i = 0; i < nodes.size(); ++i) {
HIPCHECK(hipEventRecord(a, n.stream)); n.run(nodes[i]); HIPCHECK(hipEventRecord(b, n.stream));
HIPCHECK(hipEventSynchronize(b)); float ms = 0; HIPCHECK(hipEventElapsedTime(&ms, a, b));
if (t >= 2) samples[i].push_back(ms * 1000.0f);
}
(void)hipEventDestroy(a); (void)hipEventDestroy(b);
std::vector<float> result;
for (auto& s : samples) { std::sort(s.begin(), s.end()); result.push_back(s[s.size() / 2]); }
return result;
}
float HipNetwork::lastMilliseconds() { float ms = 0; HIPCHECK(hipEventElapsedTime(&ms, impl->start, impl->stop)); return ms; }
} // namespace d4r
+9 -2
View File
@@ -19,13 +19,14 @@
#include <cstdint>
#include <memory>
#include <string>
#include <vector>
namespace d4r {
class HipNetwork {
public:
// tokensFd, outputFd: opaque file descriptors of the two shared Vulkan allocations (ownership passes to HIP);
// tokensFd, outputFd: opaque file descriptors (Linux) or Win32 NT handles (Windows; the caller keeps ownership) of the two shared Vulkan allocations (ownership passes to HIP);
// allocationBytes: the size of each allocation; the buffers start at offset 0. width/height are preset
// dependent, as above.
HipNetwork(const std::string& modelDirectory, uint32_t width, uint32_t height, int tokensFd, int outputFd,
HipNetwork(const std::string& modelDirectory, uint32_t width, uint32_t height, intptr_t tokensFd, intptr_t outputFd,
uint64_t tokensAllocationBytes, uint64_t outputAllocationBytes);
~HipNetwork();
HipNetwork(const HipNetwork&) = delete;
@@ -34,6 +35,12 @@ public:
bool done(); // true once the queued layers have finished
void wait(); // blocks until they have
float lastMilliseconds(); // GPU time of the last finished launch
// Diagnostic: relaunches the network `rounds` x 8 times on the unchanged input tokens (the graphs cycle with
// period 8) and returns an FNV-1a hash of the output buffer after each launch, rounds-major.
std::vector<uint64_t> debugRepeatHashes(uint32_t rounds);
// Diagnostic: median GPU microseconds of every launch node of graph `phase` run individually (not graph-fused),
// over `trials` runs. Node order is network order, a layer's post kernels following its main kernel.
std::vector<float> debugNodeMicros(uint32_t phase, uint32_t trials);
private:
struct Impl;
std::unique_ptr<Impl> impl;
+49 -8
View File
@@ -6,6 +6,14 @@
#ifdef D4R_ENGINE_HIP
#include "hip_net.h"
#endif
// Opaque memory handle shared with HIP: a file descriptor on Linux, a Win32 NT handle on Windows.
#ifdef _WIN32
#define D4R_EXT_MEMORY_EXTENSION "VK_KHR_external_memory_win32"
#define D4R_EXT_MEMORY_HANDLE VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT
#else
#define D4R_EXT_MEMORY_EXTENSION VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME
#define D4R_EXT_MEMORY_HANDLE VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT
#endif
#include <algorithm>
#include <chrono>
#include <cstring>
@@ -87,8 +95,8 @@ struct Context {
throw std::runtime_error("VK_KHR_cooperative_matrix required");
if (fp8 && std::none_of(extensions.begin(), extensions.end(), [](const auto& e) { return !std::strcmp(e.extensionName, VK_EXT_SHADER_FLOAT8_EXTENSION_NAME); }))
throw std::runtime_error("VK_EXT_shader_float8 required for M");
if (externalMemory && std::none_of(extensions.begin(), extensions.end(), [](const auto& e) { return !std::strcmp(e.extensionName, VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME); }))
throw std::runtime_error("VK_KHR_external_memory_fd required for the native HIP network");
if (externalMemory && std::none_of(extensions.begin(), extensions.end(), [](const auto& e) { return !std::strcmp(e.extensionName, D4R_EXT_MEMORY_EXTENSION); }))
throw std::runtime_error("external memory extension required for the native HIP network");
VkPhysicalDeviceShaderFloat8FeaturesEXT float8{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT8_FEATURES_EXT};
VkPhysicalDeviceCooperativeMatrixFeaturesKHR matrix{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COOPERATIVE_MATRIX_FEATURES_KHR, fp8 ? &float8 : nullptr};
VkPhysicalDeviceVulkan13Features f13{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES, &matrix};
@@ -137,7 +145,7 @@ struct Context {
const char* enabled[3] = {VK_KHR_COOPERATIVE_MATRIX_EXTENSION_NAME};
uint32_t enabledCount = 1;
if (fp8) enabled[enabledCount++] = VK_EXT_SHADER_FLOAT8_EXTENSION_NAME;
if (externalMemory) enabled[enabledCount++] = VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME;
if (externalMemory) enabled[enabledCount++] = D4R_EXT_MEMORY_EXTENSION;
VkDeviceCreateInfo di{VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, &features};
di.queueCreateInfoCount = 1; di.pQueueCreateInfos = &qi; di.enabledExtensionCount = enabledCount;
di.ppEnabledExtensionNames = enabled;
@@ -212,16 +220,16 @@ struct Buffer {
};
#ifdef D4R_ENGINE_HIP
// A storage buffer whose memory another API (HIP) imports through a file descriptor, for the native network.
struct Shared { VkBuffer buffer{}; VkDeviceMemory memory{}; VkDeviceSize allocation = 0; int fd = -1; };
struct Shared { VkBuffer buffer{}; VkDeviceMemory memory{}; VkDeviceSize allocation = 0; intptr_t fd = -1; };
static Shared shared(const Context& c, VkDeviceSize size) {
Shared s;
VkExternalMemoryBufferCreateInfo ei{VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO};
ei.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
ei.handleTypes = D4R_EXT_MEMORY_HANDLE;
VkBufferCreateInfo bi{VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, &ei}; bi.size = size; bi.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
CK(vkCreateBuffer(c.device, &bi, nullptr, &s.buffer));
VkMemoryRequirements r; vkGetBufferMemoryRequirements(c.device, s.buffer, &r);
VkExportMemoryAllocateInfo xi{VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO};
xi.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
xi.handleTypes = D4R_EXT_MEMORY_HANDLE;
VkMemoryAllocateInfo ai{VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, &xi}; ai.allocationSize = s.allocation = r.size;
uint32_t index = c.memory.memoryTypeCount;
for (uint32_t i = 0; i < c.memory.memoryTypeCount; ++i)
@@ -229,11 +237,19 @@ static Shared shared(const Context& c, VkDeviceSize size) {
if (index == c.memory.memoryTypeCount) throw std::runtime_error("no device-local memory type");
ai.memoryTypeIndex = index;
CK(vkAllocateMemory(c.device, &ai, nullptr, &s.memory)); CK(vkBindBufferMemory(c.device, s.buffer, s.memory, 0));
#ifdef _WIN32
VkMemoryGetWin32HandleInfoKHR gi{VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR};
gi.memory = s.memory; gi.handleType = D4R_EXT_MEMORY_HANDLE;
const auto getHandle = reinterpret_cast<PFN_vkGetMemoryWin32HandleKHR>(vkGetDeviceProcAddr(c.device, "vkGetMemoryWin32HandleKHR"));
if (!getHandle) throw std::runtime_error("vkGetMemoryWin32HandleKHR is unavailable");
HANDLE handle = nullptr; CK(getHandle(c.device, &gi, &handle)); s.fd = reinterpret_cast<intptr_t>(handle);
#else
VkMemoryGetFdInfoKHR gi{VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR};
gi.memory = s.memory; gi.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
gi.memory = s.memory; gi.handleType = D4R_EXT_MEMORY_HANDLE;
const auto getFd = reinterpret_cast<PFN_vkGetMemoryFdKHR>(vkGetDeviceProcAddr(c.device, "vkGetMemoryFdKHR"));
if (!getFd) throw std::runtime_error("vkGetMemoryFdKHR is unavailable");
CK(getFd(c.device, &gi, &s.fd));
int fd = -1; CK(getFd(c.device, &gi, &fd)); s.fd = fd;
#endif
return s;
}
#endif
@@ -419,6 +435,31 @@ int run(const std::string& modelDir, const std::string& caseFile, const std::str
context.submit();
#ifdef D4R_ENGINE_HIP
network->launch(); network->wait();
static bool nodesProfiled = false;
if (!nodesProfiled && std::getenv("D4R_HIP_NODE_PROFILE")) {
nodesProfiled = true; float total = 0;
auto micros = network->debugNodeMicros(0, 30);
for (size_t k = 0; k < micros.size(); ++k) { std::cout << " node " << k << ": " << micros[k] << " us\n"; total += micros[k]; }
std::cout << "hip node profile total (individually launched, phase 0): " << total << " us\n";
}
static bool repeatChecked = false;
if (!repeatChecked && std::getenv("D4R_HIP_REPEAT_CHECK")) {
repeatChecked = true;
const uint32_t rounds = uint32_t(std::atoi(std::getenv("D4R_HIP_REPEAT_CHECK")));
auto hashes = network->debugRepeatHashes(rounds); unsigned bad = 0;
const size_t stride = hashes.size() / (size_t(rounds) * 8); // 1 unless D4R_HIP_REPEAT_ALL
for (uint32_t phase = 0; phase < 8; ++phase)
for (uint32_t r = 1; r < rounds; ++r) {
bool any = false;
for (size_t k = 0; k < stride; ++k)
if (hashes[(size_t(r) * 8 + phase) * stride + k] != hashes[size_t(phase) * stride + k]) {
any = true; std::cout << " mismatch round " << r << " phase " << phase << " slot " << k << " (0=output, 1+=owned alloc)\n";
}
if (any) ++bad;
}
std::cout << "hip repeat check: " << rounds << " rounds x 8 phases, mismatching launches vs round 0: " << bad << '\n';
}
if (profile) std::cout << " hip network GPU: " << network->lastMilliseconds() * 1000.0 << " us\n";
#else
throw std::runtime_error("this build has no HIP support");
#endif
+6 -1
View File
@@ -569,7 +569,12 @@ void Engine::setFrame(const FrameImages& images, const InputParams& input, const
if ((input.flags & FrameFlagDisplayMotion) && !e.model.hasDisplayMotion)
throw std::runtime_error("display-resolution motion needs a model compiled by the current compile_k.py");
if (!(output.out2rnd[0] > 0 && output.out2rnd[0] <= 1 && output.out2rnd[1] > 0 && output.out2rnd[1] <= 1) ||
output.near2rnd[0] != output.out2rnd[0] || output.near2rnd[1] != output.out2rnd[1] ||
// Display-resolution motion bypasses the depth-neighbour lookup. NGX
// uses (1,1) for its unused nearest scale in that variant; standard
// frame construction uses the render ratio. Both are valid there.
(!(input.flags & FrameFlagDisplayMotion) &&
(output.near2rnd[0] != output.out2rnd[0] || output.near2rnd[1] != output.out2rnd[1])) ||
!(output.near2rnd[0] > 0 && output.near2rnd[0] <= 1 && output.near2rnd[1] > 0 && output.near2rnd[1] <= 1) ||
!std::isfinite(output.sharp) ||
!(std::isfinite(input.expo) && input.expo > 0 && std::isfinite(input.expoIn) && input.expoIn > 0 &&
std::isfinite(output.expo) && output.expo > 0 && std::isfinite(input.expoRatio) && input.expoRatio > 0 &&
+7
View File
@@ -8,6 +8,12 @@
__attribute__((device, always_inline)) static inline uint32_t d4r_wave32_other_half(uint32_t value)
{
#if defined(D4R_WINDOWS_GFX11_LANE_EXCHANGE) && defined(__GFX11__)
#if defined(D4R_WINDOWS_GFX11_DS_EXCHANGE)
// Experimental bit-preserving cross-half exchange. The previous player test
// predated the position-layer block-barrier fix; retain explicit selection.
const uint32_t lane = __builtin_amdgcn_mbcnt_lo(~0u, 0u);
return __builtin_amdgcn_ds_bpermute((lane ^ 16u) * 4u, value);
#else
__attribute__((shared)) volatile uint32_t scratch[512];
const uint32_t tid = __builtin_amdgcn_workitem_id_x() + __builtin_amdgcn_workgroup_size_x() *
(__builtin_amdgcn_workitem_id_y() + __builtin_amdgcn_workgroup_size_y() * __builtin_amdgcn_workitem_id_z());
@@ -25,6 +31,7 @@ __attribute__((device, always_inline)) static inline uint32_t d4r_wave32_other_h
__builtin_amdgcn_wave_barrier();
__builtin_amdgcn_fence(__ATOMIC_ACQUIRE, "wavefront");
return result;
#endif
#else
return __builtin_amdgcn_permlanex16(value, value, 0x76543210u, 0xfedcba98u, false, false);
#endif
+2 -2
View File
@@ -3,8 +3,8 @@
#define PWIN_F32ACC
#ifndef D4R_K_POS_MT
#define D4R_K_POS_MT 2
#elif !defined(__GFX12__) || !defined(D4R_K_FP16_BASELINE)
#error "experimental position token tiling requires gfx12 strict K baseline"
#elif (!defined(__GFX12__) && !defined(__GFX11__)) || !defined(D4R_K_FP16_BASELINE)
#error "experimental position token tiling requires gfx11/gfx12 strict K baseline"
#endif
#include "pwin_pos.h"
POS_DECODER(dltss_pwin_dec0_layer, 2, 32, 64, 40, 48, D4R_K_POS_MT)
+2 -2
View File
@@ -4,8 +4,8 @@
#define PWIN_F32ACC
#ifndef D4R_K_POS_MT
#define D4R_K_POS_MT 2
#elif !defined(__GFX12__) || !defined(D4R_K_FP16_BASELINE)
#error "experimental position token tiling requires gfx12 strict K baseline"
#elif (!defined(__GFX12__) && !defined(__GFX11__)) || !defined(D4R_K_FP16_BASELINE)
#error "experimental position token tiling requires gfx11/gfx12 strict K baseline"
#endif
// Reuse V across the window's waves. Paired gfx1201 replays retain every half
// code and reduce enc0 time by about 5%; gfx11 and arithmetic experiments keep
+2 -2
View File
@@ -2,8 +2,8 @@
// d4r-build-flags: -mcumode
#define PWIN_F32ACC
#ifdef D4R_K_ENC1_POS_MT
#if !defined(__GFX12__) || !defined(D4R_K_FP16_BASELINE)
#error "experimental enc1 token tiling requires gfx12 strict K baseline"
#if (!defined(__GFX12__) && !defined(__GFX11__)) || !defined(D4R_K_FP16_BASELINE)
#error "experimental enc1 token tiling requires gfx11/gfx12 strict K baseline"
#endif
#if D4R_K_ENC1_POS_MT != 1 && D4R_K_ENC1_POS_MT != 2 && D4R_K_ENC1_POS_MT != 4
#error "enc1 token tiles per wave must be 1, 2 or 4"
+7
View File
@@ -121,6 +121,13 @@ __device__ void pos_core(const PwinParams& p, const u8v* __restrict__ img, const
}
}
#ifndef POS_VSHARE
// Without the shared V^T slots every wave computed V of ALL 64 tokens from hb in every head, i.e. it read the rows of
// the other waves. The residual stage below overwrites this wave's own hb rows with the MLP input; without a
// block barrier a fast wave does that while a slower one still reads those rows for a later head (a rare,
// timing-dependent wrong result seen on gfx1101: enc0 outputs differing between identical launches).
block_sync();
#endif
STAMP(3);
// ---- residual, MLP init acc = x1 + b2, MLP input m = x1 * g2 (-> hb)
#pragma unroll
+33 -6
View File
@@ -29,8 +29,12 @@ Install
2. Run install.bat and accept the download prompt. Existing d4r DLLs are backed up outside the game
folder with their original paths and SHA256 hashes. Verified downloads are cached in
%LOCALAPPDATA%\d4r\cache.
The Ray Reconstruction DLL alone does not enable RR. Experimental native RR requires a locally
generated D/E model and explicit d4r.ini selection; game compatibility remains unverified.
Bundled isolated Python runs the installer; system Python, PowerShell and Git Bash are not required.
Choose N to keep RR off, or D/E to build and enable an experimental private native RR model.
Automatic RR building currently requires the gfx1101-only package. Model tools run from the
external cache. Existing models remain intact; d4r.ini receives an exact external backup before
selecting the new relative model path. Game compatibility and reference quality remain unverified.
A model-build failure leaves the verified NVIDIA DLLs installed and does not enable the model.
3. Optional: check the install. In the game folder, open PowerShell and run
powershell -ExecutionPolicy Bypass -File d4r\d4r-check.ps1
4. Start the game from Steam, Epic or a shortcut as usual. In its graphics settings choose DLSS
@@ -48,8 +52,27 @@ Edit d4r\d4r.ini (it is commented) and restart the game. Each game has its own c
- AsyncInterop, ValidateOutput, Log: for testing and bug reports; the defaults are the tested setting.
- RayReconstruction/Backend: off by default. native selects experimental native D3D12/Vulkan RR;
ModelDir selects your locally generated D/E model (auto: d4r\engine\rr-d). Relative paths resolve
beside d4r.ini. No model is bundled or automatically generated by install.bat yet.
fsr currently rejects RR requests because native Windows FSR RR execution is unavailable.
beside d4r.ini. No model is bundled; install.bat can optionally generate and select D/E locally.
fsr (experimental) answers the game's DLSS RR request with AMD's FSR Ray Regeneration denoiser,
then DLSS Super Sampling upscales the result. AMD does not support FSR RR on RDNA3, so d4r runs
AMD's unmodified denoiser DLL on a private vkd3d-proton device (d4r\fsr, built by d4r, LGPL; see
d4r\fsr\SOURCE.txt) that emulates the FP8 math on RDNA3. AMD's amd_fidelityfx_denoiser_dx12.dll is
not bundled: install.bat [F] downloads the pinned file (SHA256 48f1e588...), or set FsrDenoiser.
FsrStabilityBias (default auto = AMD's 1; 0 is much sharper without upscaling, see d4r.ini) and
FsrDetail (0-1) tune the denoiser.
The first RR start compiles AMD's shaders and can freeze the game for about a minute; they are
cached in %LOCALAPPDATA%\d4r\cache\vkd3d. Synthetic validation only: guides must be HDR with
render-resolution, unjittered motion vectors; diffuse hit distance is not used. The exposure texture
goes only to Super Sampling. Dynamic render sizes must stay within creation dimensions and the
Super Sampling library's queried range; unsupported sizes fail before submission.
Game compatibility and image quality against DLSS RR are unverified.
FSR-only output accepts RGBA16F or RGBA32F, including format changes between frames. RGBA32F
widens the completed half result on the GPU; it does not change denoiser arithmetic or produce alpha.
Native RR accepts per-frame render dimensions from 64 pixels up to the feature's creation size.
Changing dimensions resets temporal history and retains the loaded model and shared GPU packet.
Output dimensions remain fixed. This behavior has synthetic D/E coverage only.
RR accepts RGBA16F and RGBA32F output textures, including temporal format changes;
float32 output is widened on the GPU. Other output encodings remain unsupported.
OptiScaler's own settings (overlay, render ratios) are in OptiScaler.ini.
If something goes wrong
@@ -60,12 +83,14 @@ If something goes wrong
- For a bug report run d4r\d4r-check.ps1 -Report and send the ZIP it puts on your Desktop,
with your GPU, the game, the model and what the image looked like. Logs can contain folder names.
- Not supported: frame generation, standalone inference engine selection in the game, DirectX 11,
Vulkan game integration, or FSR RR. Native DLSS RR has synthetic feature-13 validation only;
Vulkan game integration, or supported FSR RR (the fsr backend is experimental). Native DLSS RR and
FSR RR have synthetic feature-13 validation only;
it does not establish game compatibility or NVIDIA reference quality. E/L hints do not establish full parity.
Uninstall
---------
Delete dxgi.dll, OptiScaler.ini, OptiScaler.log (if present), WINDOWS_README.txt and the d4r folder
Delete dxgi.dll, OptiScaler.ini, OptiScaler.log (if present), WINDOWS_README.txt, install.bat,
install-dependencies.py, install-dlss.ps1 and the d4r folder
from the game folder. The cache in %LOCALAPPDATA%\d4r can be deleted too. Nothing else was changed.
Licenses
@@ -74,3 +99,5 @@ d4r is GNU General Public License, version 3 only (GPL-3.0-only). This ZIP also
patched; the patches are in the source repository), and AMD's HIP runtime. d4r\licenses has the
texts. d4r is not affiliated with NVIDIA, AMD or the OptiScaler project. NVIDIA's files are yours
to supply, under NVIDIA's terms.
Portable model tools include Python, NumPy and glslang; their license texts are under
d4r\model-tools\licenses, with additional dependency notices in the tool directories.
+18 -1
View File
@@ -27,6 +27,11 @@ ShowWatermark = false
; faster depends on the game and has not been measured broadly; turn it off if the game crashes,
; stutters or shows a wrong image.
AsyncInterop = false
; Experimental: submit input conversions/array copies as one ordered GPU batch, retaining
; whole-context completion before NGX. Some synthetic runs are faster, but player temporal
; exactness is unresolved. Keep false until that investigation is complete.
; Missing/invalid values retain the older unbatched behavior. Set false to disable batching.
BatchInputCopies = false
[Debug]
; Check every DLSS output for invalid values (NaN/Inf) on the GPU. Costs some speed; use it only
@@ -51,8 +56,20 @@ NativeFp8 = false
[RayReconstruction]
; off is the default. native uses the local Vulkan RR engine through native D3D12.
; fsr is currently unavailable and rejects RR requests. No fallback to noisy upscaling.
; fsr (experimental) runs AMD's FSR Ray Regeneration denoiser on a private vkd3d-proton device
; (d4r\fsr) and upscales with Super Sampling. install.bat [F] sets it up. A frame that cannot be
; denoised fails; it is never returned noisy.
Backend = off
; auto selects d4r/engine/rr-d. Relative paths resolve beside this d4r.ini.
; Generate the D/E model locally; model data is not bundled in the ZIP.
ModelDir = auto
; Backend = fsr only: AMD's amd_fidelityfx_denoiser_dx12.dll. auto = d4r\amd_fidelityfx_denoiser_dx12.dll.
; AMD's file is not bundled; install.bat downloads the pinned FidelityFX SDK 2.3 binary.
FsrDenoiser = auto
; Backend = fsr only. AMD's temporal stability bias: auto keeps AMD's default of 1, which weights history heavily.
; Measured on d4r's synthetic moving scene (error against the noise-free image): without upscaling 0 is about 3x
; closer than auto (0.036 vs 0.107); after a 2x Super Sampling upscale auto was about 16% closer (0.214 vs 0.248).
; Linux testing on a game capture found 0 closer to DLSS RR. Pick per game; a number >= 0 is allowed.
FsrStabilityBias = auto
; Backend = fsr only. 0-1: share of the colour that skips the denoiser (keeps hair/water/foliage detail, adds grain).
FsrDetail = 0
+27 -1
View File
@@ -1,10 +1,36 @@
@echo off
setlocal
if not exist "%~dp0d4r\model-tools\python\python.exe" (
echo Missing portable tools. Extract the complete current d4r ZIP first.
exit /b 1
)
if not "%~1"=="" goto arguments
echo d4r will download NVIDIA DLSS files and an NVIDIA driver archive.
echo Driver archive is about 945 MiB (991 MB). It will only extract _nvngx.dll; it will not install or change drivers.
choice /C YN /N /M "Continue? [Y/N] "
if errorlevel 2 exit /b 0
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0install-dlss.ps1" -PackageRoot "%~dp0." -Yes
echo Optional Ray Reconstruction backends (disabled by default; synthetic validation only, game compatibility unverified):
echo [D]/[E] native DLSS RR model built locally from NVIDIA's RR DLL (gfx1101 only).
echo [F] experimental FSR Ray Regeneration: downloads AMD's pinned 14 MB denoiser DLL; runs it through the
echo bundled vkd3d-proton (d4r\fsr). AMD does not support FSR RR on RDNA3; d4r emulates its FP8 math.
choice /C NDEF /N /M "Enable RR? [N]one / [D] / [E] / [F]SR: "
if errorlevel 4 goto fsr
if errorlevel 3 goto modelE
if errorlevel 2 goto modelD
"%~dp0d4r\model-tools\python\python.exe" "%~dp0install-dependencies.py" --package-root "%~dp0." --yes
goto result
:modelD
"%~dp0d4r\model-tools\python\python.exe" "%~dp0install-dependencies.py" --package-root "%~dp0." --yes --rr-preset D --enable-rr
goto result
:modelE
"%~dp0d4r\model-tools\python\python.exe" "%~dp0install-dependencies.py" --package-root "%~dp0." --yes --rr-preset E --enable-rr
goto result
:fsr
"%~dp0d4r\model-tools\python\python.exe" "%~dp0install-dependencies.py" --package-root "%~dp0." --yes --enable-fsr
goto result
:arguments
"%~dp0d4r\model-tools\python\python.exe" "%~dp0install-dependencies.py" --package-root "%~dp0." %*
:result
set "D4R_INSTALL_EXIT=%ERRORLEVEL%"
if not "%D4R_INSTALL_EXIT%"=="0" echo d4r install failed. Read the message above.
exit /b %D4R_INSTALL_EXIT%
@@ -0,0 +1,12 @@
diff --git a/scratch_pool.hpp b/scratch_pool.hpp
index 24fe09be..72866f24 100644
--- a/scratch_pool.hpp
+++ b/scratch_pool.hpp
@@ -25,6 +25,7 @@
#ifndef DXIL_SPV_SCRATCH_POOL_H_
#define DXIL_SPV_SCRATCH_POOL_H_
+#include <exception>
#include "thread_local_allocator.hpp"
namespace dxil_spv
@@ -0,0 +1,11 @@
diff --git a/util/util_small_vector.h b/util/util_small_vector.h
index 78881c4..a20efbf 100644
--- a/util/util_small_vector.h
+++ b/util/util_small_vector.h
@@ -1,5 +1,6 @@
#pragma once
+#include <new>
#include <algorithm>
#include <cstddef>
#include <cstdint>
@@ -3,7 +3,7 @@ param([ValidateSet('enc0','enc1','enc2','enc3','enc4','dec5','dec4','dec3','dec2
[string]$Layer='enc1', [ValidateSet('source','cu','wgp')][string]$Mode='source',
[ValidateSet('baseline','load','store','both')][string]$NonTemporal='baseline',
[ValidateSet(0,1,2,4)][int]$PosTiles=0, [switch]$ShareValues, [switch]$PrivateValues,
[switch]$PackedAccumulator,
[switch]$PackedAccumulator, [ValidateSet('lds','ds')][string]$LaneExchange='lds',
[ValidateSet('gfx1100','gfx1101','gfx1102','gfx1103','gfx1150','gfx1151','gfx1152','gfx1153','gfx1154','gfx1200','gfx1201')]
[string]$GpuArch='gfx1201',
[string]$HipRoot, [string]$OutputDirectory)
@@ -11,8 +11,8 @@ $ErrorActionPreference='Stop'
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
if (!$HipRoot) { $HipRoot=Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' }
if ($ShareValues -and $PrivateValues) { throw 'Choose shared or private V storage.' }
if ($GpuArch -notmatch '^gfx12' -and ($PosTiles -or $ShareValues -or $PrivateValues -or $PackedAccumulator)) {
throw 'Token tiling and accumulator/storage experiments require gfx12.'
if ($GpuArch -notmatch '^gfx12' -and $PackedAccumulator) {
throw 'Packed accumulator experiments require gfx12.'
}
if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo "build/native-k-experiment-$GpuArch-$Layer-$Mode-$NonTemporal-pos$PosTiles-share$([int][bool]$ShareValues)-private$([int][bool]$PrivateValues)-packed$([int][bool]$PackedAccumulator)" }
$OutputDirectory=[IO.Path]::GetFullPath($OutputDirectory)
@@ -46,6 +46,10 @@ if ($ShareValues) {
if ($PackedAccumulator) { $flags+='-DD4R_K_PACKED_ACC' }
if ($PrivateValues) { $flags+='-DD4R_K_PRIVATE_V' }
if ($GpuArch -match '^gfx11') { $flags+='-DD4R_WINDOWS_GFX11_LANE_EXCHANGE' }
if ($LaneExchange -eq 'ds') {
if ($GpuArch -notmatch '^gfx11') { throw 'DS lane exchange experiment requires gfx11.' }
$flags+='-DD4R_WINDOWS_GFX11_DS_EXCHANGE'
}
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
$object=Join-Path $OutputDirectory "$name.hsaco"
$arguments=@('-x','hip','-std=c++17',"--offload-arch=$GpuArch",'--offload-device-only',
@@ -64,7 +68,7 @@ if ($LASTEXITCODE) { throw 'Native object metadata extraction failed.' }
@{layer=$Layer; architecture=$GpuArch; experimental=$true; numericalValidation='pending';
mode=$Mode; nonTemporal=$NonTemporal; tokenTilesPerWave=$PosTiles; shareValues=[bool]$ShareValues;
privateValues=[bool]$PrivateValues;
packedAccumulator=[bool]$PackedAccumulator;
packedAccumulator=[bool]$PackedAccumulator; laneExchange=$LaneExchange;
compiler=$compiler; arguments=$arguments;
sourceCommit=(git -C $repo rev-parse HEAD); workingTreeDirty=[bool](& git -C $repo status --porcelain);
compilerVersion=(& $compiler --version | Out-String).Trim();
+26
View File
@@ -0,0 +1,26 @@
"""Compile the FSR RR conversion shaders with the pinned glslang into tools/windows/fsr_rr/fsr_rr_shaders_spv.h."""
import hashlib
import struct
import subprocess
import sys
import tempfile
from pathlib import Path
repo = Path(__file__).resolve().parents[2]
glslang = repo / '.tools/glslang-16.6.0/bin/glslangValidator.exe'
if hashlib.sha256(glslang.read_bytes()).hexdigest() != '9643709c02c83e26773f96b6a87f197ab2e84f78be0879ad8fb3619418b5d9ad':
sys.exit('requires the pinned glslang 16.6.0')
source = repo / 'tools/windows/fsr_rr'
lines = ['#pragma once', '#include <cstdint>', '// Generated by scripts/windows/compile-fsr-rr-shaders.py; do not edit.']
with tempfile.TemporaryDirectory() as tmp:
for name in ('fsr_rr_prepare', 'fsr_rr_compose'):
out = Path(tmp) / (name + '.spv')
subprocess.run([str(glslang), '-V', '--target-env', 'vulkan1.0', '-o', str(out), str(source / (name + '.comp'))],
check=True, stdout=subprocess.DEVNULL)
data = out.read_bytes()
words = struct.unpack('<' + 'I' * (len(data) // 4), data)
lines.append(f'static const uint32_t d4r_vk_{name}[] = {{')
lines.extend(','.join(f'0x{w:08x}' for w in words[i:i + 8]) + ',' for i in range(0, len(words), 8))
lines.append('};')
(source / 'fsr_rr_shaders_spv.h').write_text('\n'.join(lines) + '\n')
print('wrote', source / 'fsr_rr_shaders_spv.h')
+2 -2
View File
@@ -198,8 +198,8 @@ $iniPath = Join-Path $d4r 'd4r.ini'
$model = 'K'
if (Test-Path -LiteralPath $iniPath) {
$section = ''; $iniProblems = 0
$known = @{ 'dlss.model' = 1; 'dlss.showwatermark' = 1; 'interop.asyncinterop' = 1; 'debug.validateoutput' = 1; 'debug.log' = 1; 'paths.cachedir' = 1; 'paths.nativedir' = 1; 'kernels.nativefp8' = 1; 'rayreconstruction.backend' = 1; 'rayreconstruction.modeldir' = 1 }
$boolean = @('dlss.showwatermark', 'interop.asyncinterop', 'debug.validateoutput', 'kernels.nativefp8')
$known = @{ 'dlss.model' = 1; 'dlss.showwatermark' = 1; 'interop.asyncinterop' = 1; 'interop.batchinputcopies' = 1; 'debug.validateoutput' = 1; 'debug.log' = 1; 'paths.cachedir' = 1; 'paths.nativedir' = 1; 'kernels.nativefp8' = 1; 'rayreconstruction.backend' = 1; 'rayreconstruction.modeldir' = 1 }
$boolean = @('dlss.showwatermark', 'interop.asyncinterop', 'interop.batchinputcopies', 'debug.validateoutput', 'kernels.nativefp8')
foreach ($raw in (Read-TextFile $iniPath).TrimStart([char]0xFEFF) -split "`n") {
$line = $raw.Trim()
if (!$line -or $line.StartsWith(';') -or $line.StartsWith('#')) { continue }
+45 -1
View File
@@ -9,11 +9,18 @@ param(
[Parameter(Mandatory=$true)][string]$PackageRoot,
[string[]]$Architectures, # default: every target in gpu-targets.json; a subset makes a smaller release
[string]$ArchivePath,
[string]$RrToolsArchive,
[string]$FsrPayload, # build_fsr_payload.py output: vkd3d-proton core + amdxc64 shim for the experimental FSR RR backend
[switch]$LocalDevelopment
)
$ErrorActionPreference='Stop'
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
if (!$RrToolsArchive) { $RrToolsArchive=Join-Path $repo 'build/rr-portable-tools-v2.zip' }
if (!(Test-Path -LiteralPath $RrToolsArchive -PathType Leaf) -or
(Get-FileHash -LiteralPath $RrToolsArchive -Algorithm SHA256).Hash -ne '297289a57a82142eb952479c99112b1c352a6753eb15bc6b2431bf686aed9b22') {
throw 'Pass -RrToolsArchive with the verified clean portable-tools v2 ZIP. Never package a used tool directory.'
}
if (!$GamePackagePrefix) { $GamePackagePrefix=Join-Path $repo 'dist/windows-gpu-coverage-game' }
$PackageRoot=[IO.Path]::GetFullPath($PackageRoot)
if($LocalDevelopment){
@@ -81,19 +88,56 @@ Stage (Join-Path $repo 'packaging/windows/d4r.ini') 'd4r/d4r.ini'
Stage (Join-Path $repo 'tools/windows/dll-pins.txt') 'd4r/dll-pins.txt'
Stage (Join-Path $repo 'tools/windows/gpu-targets.json') 'd4r/gpu-targets.json'
if (!$inventoryStaged) { throw 'No d4r/d4r_gpu_inventory.exe in the inputs; d4r-check.ps1 needs it to query the GPU.' }
if ($FsrPayload) {
# Experimental FSR RR backend: d4r-built vkd3d-proton (LGPL, source offer in SOURCE.txt) and d4r's amdxc64 shim.
# AMD's denoiser is never bundled; install.bat downloads the hash-pinned file when FSR is selected.
$FsrPayload=[IO.Path]::GetFullPath($FsrPayload)
$payloadManifest=Get-Content -LiteralPath (Join-Path $FsrPayload 'fsr-payload.json') -Raw | ConvertFrom-Json
if ($payloadManifest.kind -ne 'd4r-fsr-payload' -or $payloadManifest.amdDenoiserBundled) { throw 'Unsupported FSR payload manifest.' }
foreach ($property in $payloadManifest.files.PSObject.Properties) {
$file=Join-Path $FsrPayload $property.Name
if ((Get-FileHash -LiteralPath $file -Algorithm SHA256).Hash -ne $property.Value) { throw "FSR payload file changed: $($property.Name)" } # -ne ignores case
}
foreach ($name in @('d4r_vkd3d_core.dll','amdxc64.dll')) { Stage (Join-Path $FsrPayload "fsr/$name") "d4r/fsr/$name" }
Stage (Join-Path $FsrPayload 'SOURCE.txt') 'd4r/fsr/SOURCE.txt'
foreach ($license in Get-ChildItem -LiteralPath (Join-Path $FsrPayload 'licenses') -File) { Stage $license.FullName "d4r/licenses/$($license.Name)" }
foreach ($patch in Get-ChildItem -LiteralPath (Join-Path $repo 'patches/vkd3d-proton-windows') -Filter '*.patch' -File) { Stage $patch.FullName "d4r/fsr/patches/$($patch.Name)" }
}
foreach ($optional in @(@{source='packaging/windows/WINDOWS_README.txt'; path='WINDOWS_README.txt'},
@{source='packaging/windows/install.bat'; path='install.bat'},
@{source='scripts/windows/install-dlss.ps1'; path='install-dlss.ps1'},
@{source='tools/windows/install_dependencies.py'; path='install-dependencies.py'},
@{source='scripts/windows/d4r-check.ps1'; path='d4r/d4r-check.ps1'})) {
$path=Join-Path $repo $optional.source
if (Test-Path -LiteralPath $path) { Stage $path $optional.path }
}
Add-Type -AssemblyName System.IO.Compression,System.IO.Compression.FileSystem
$toolZip=[IO.Compression.ZipFile]::OpenRead([IO.Path]::GetFullPath($RrToolsArchive))
try {
$reader=[IO.StreamReader]::new($toolZip.GetEntry('rr-tools.json').Open())
try { $toolManifest=$reader.ReadToEnd() | ConvertFrom-Json } finally { $reader.Dispose() }
foreach ($entry in $toolZip.Entries) {
if ($entry.FullName.EndsWith('/')) { continue }
$name=$entry.FullName
if ($name -match '(^/|\\|:|(^|/)\.\.(/|$))') { throw "Unsafe portable-tools entry: $name" }
$relative='d4r/model-tools/'+$name
$destination=Join-Path $PackageRoot $relative
New-Item -ItemType Directory -Force (Split-Path $destination) | Out-Null
$inputStream=$entry.Open(); $outputStream=[IO.File]::Open($destination,[IO.FileMode]::CreateNew)
try { $inputStream.CopyTo($outputStream) } finally { $inputStream.Dispose(); $outputStream.Dispose() }
$digest=(Get-FileHash -LiteralPath $destination -Algorithm SHA256).Hash
if ($name -ne 'rr-tools.json' -and $digest -ne $toolManifest.files.PSObject.Properties[$name].Value) {
throw "Portable-tools hash mismatch: $name"
}
$files.Add(@{path=$relative; sha256=$digest; version=$null})
}
} finally { $toolZip.Dispose() }
# Shows players where the NGX core goes; a ZIP cannot carry an empty folder.
$placeholder=Join-Path $PackageRoot 'd4r/ngx/PUT _nvngx.dll HERE.txt'
New-Item -ItemType Directory -Force (Split-Path $placeholder) | Out-Null
"Copy NVIDIA's _nvngx.dll into this folder. See WINDOWS_README.txt." | Set-Content -LiteralPath $placeholder -Encoding ASCII
$files.Add(@{path='d4r/ngx/PUT _nvngx.dll HERE.txt'; sha256=(Get-FileHash -LiteralPath $placeholder -Algorithm SHA256).Hash; version=$null})
if (Get-ChildItem -LiteralPath $PackageRoot -Recurse -File | Where-Object { $_.Name -in @('nvngx_dlss.dll','nvngx_dlssd.dll') -or ($_.Name -eq '_nvngx.dll' -and $_.DirectoryName -ne (Join-Path $PackageRoot 'd4r')) }) {
if (Get-ChildItem -LiteralPath $PackageRoot -Recurse -File | Where-Object { $_.Name -in @('nvngx_dlss.dll','nvngx_dlssd.dll','amd_fidelityfx_denoiser_dx12.dll') -or ($_.Name -eq '_nvngx.dll' -and $_.DirectoryName -ne (Join-Path $PackageRoot 'd4r')) }) {
throw 'Proprietary NVIDIA binaries must not be in the release.'
}
# Read by d4r-check.ps1: every shipped file's hash, so a damaged or mixed install is reported.
@@ -54,7 +54,8 @@ $release=Join-Path $OutputDirectory 'release'
Invoke-Package $prefix $release
foreach ($path in @('dxgi.dll','OptiScaler.ini','d4r/_nvngx.dll','d4r/d4r.ini','d4r/dll-pins.txt','d4r/package.json',
'd4r/hip/bin/amdhip64_7.dll','d4r/zluda/nvcuda.dll','d4r/licenses/d4r.txt','d4r/ngx/PUT _nvngx.dll HERE.txt',
'd4r/gpu-targets.json','d4r/d4r_gpu_inventory.exe','d4r/d4r-check.ps1','WINDOWS_README.txt')) {
'd4r/gpu-targets.json','d4r/d4r_gpu_inventory.exe','d4r/d4r-check.ps1','WINDOWS_README.txt',
'install.bat','install-dependencies.py','d4r/model-tools/python/python.exe','d4r/model-tools/rr-tools.json')) {
if (!(Test-Path -LiteralPath (Join-Path $release $path))) { throw "Release lacks $path" }
}
if ((Get-Content -LiteralPath (Join-Path $release 'd4r/_nvngx.dll') -Raw).Trim() -ne 'loader fixture') { throw 'd4r/_nvngx.dll is not the loader.' }
@@ -91,6 +92,47 @@ try { & (Join-Path $PSScriptRoot 'package-windows-release.ps1') -GamePackagePref
if (!$rejected) { throw 'An unknown architecture was not rejected.' }
$results+=@{case='architecture-subset'; passed=$true; shipped=$kept}
# 1c. The optional FSR payload (vkd3d-proton core + amdxc64 shim) ships under d4r/fsr with its licences and source
# offer; AMD's denoiser never ships; a payload that claims to bundle it, or a changed file, is rejected.
function New-FsrPayload([string]$Root,[bool]$ClaimsAmd) {
$files=[ordered]@{'fsr/d4r_vkd3d_core.dll'='core fixture'; 'fsr/amdxc64.dll'='shim fixture'; 'SOURCE.txt'='source offer';
'licenses/vkd3d-proton-LGPL-2.1.txt'='lgpl'; 'licenses/dxil-spirv-MIT.txt'='mit'}
$hashes=[ordered]@{}
foreach ($entry in $files.GetEnumerator()) {
$path=Join-Path $Root $entry.Key; New-Item -ItemType Directory -Force (Split-Path $path) | Out-Null
Set-Content -LiteralPath $path $entry.Value -Encoding ASCII
$hashes[$entry.Key]=(Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash.ToLowerInvariant()
}
@{schema=1; kind='d4r-fsr-payload'; amdDenoiserBundled=$ClaimsAmd; files=$hashes} | ConvertTo-Json -Depth 4 |
Set-Content -LiteralPath (Join-Path $Root 'fsr-payload.json') -Encoding UTF8
}
$goodPrefix=Join-Path $OutputDirectory 'inputs/good/game'
$payload=Join-Path $OutputDirectory 'fsr-payload'; New-FsrPayload $payload $false
$fsrRelease=Join-Path $OutputDirectory 'release-fsr'
& (Join-Path $PSScriptRoot 'package-windows-release.ps1') -GamePackagePrefix $goodPrefix -LoaderDll $loader -PackageRoot $fsrRelease -FsrPayload $payload | Out-Null
foreach ($path in @('d4r/fsr/d4r_vkd3d_core.dll','d4r/fsr/amdxc64.dll','d4r/fsr/SOURCE.txt','d4r/licenses/vkd3d-proton-LGPL-2.1.txt',
'd4r/licenses/dxil-spirv-MIT.txt','d4r/fsr/patches/0001-dxil-spirv-include-exception-for-libcxx.patch')) {
if (!(Test-Path -LiteralPath (Join-Path $fsrRelease $path))) { throw "FSR release lacks $path" }
}
if (Get-ChildItem -LiteralPath $fsrRelease -Recurse -Filter 'amd_fidelityfx_denoiser_dx12.dll') { throw "AMD's denoiser must never ship." }
$fsrManifest=Get-Content -LiteralPath (Join-Path $fsrRelease 'd4r/package.json') -Raw | ConvertFrom-Json
foreach ($file in $fsrManifest.files) {
if ((Get-FileHash -LiteralPath (Join-Path $fsrRelease $file.path) -Algorithm SHA256).Hash -ne $file.sha256) { throw "FSR manifest hash wrong: $($file.path)" }
}
$fsrShipped=@(Get-ChildItem -LiteralPath $fsrRelease -Recurse -File | ForEach-Object { $_.FullName.Substring($fsrRelease.Length+1).Replace('\','/') } | Where-Object { $_ -ne 'd4r/package.json' })
if (Compare-Object $fsrShipped @($fsrManifest.files.path)) { throw 'FSR release manifest and shipped files differ.' }
if (Test-Path -LiteralPath (Join-Path $release 'd4r/fsr')) { throw 'The default release must not carry the FSR payload.' }
$claims=Join-Path $OutputDirectory 'fsr-payload-claims-amd'; New-FsrPayload $claims $true
$rejected=$false
try { & (Join-Path $PSScriptRoot 'package-windows-release.ps1') -GamePackagePrefix $goodPrefix -LoaderDll $loader -PackageRoot (Join-Path $OutputDirectory 'release-fsr-claims') -FsrPayload $claims | Out-Null } catch { $rejected=$_.Exception.Message -match 'Unsupported FSR payload' }
if (!$rejected) { throw 'A payload claiming to bundle AMD software was not rejected.' }
$tampered=Join-Path $OutputDirectory 'fsr-payload-tampered'; New-FsrPayload $tampered $false
Set-Content -LiteralPath (Join-Path $tampered 'fsr/amdxc64.dll') 'changed after hashing' -Encoding ASCII
$rejected=$false
try { & (Join-Path $PSScriptRoot 'package-windows-release.ps1') -GamePackagePrefix $goodPrefix -LoaderDll $loader -PackageRoot (Join-Path $OutputDirectory 'release-fsr-tampered') -FsrPayload $tampered | Out-Null } catch { $rejected=$_.Exception.Message -match 'FSR payload file changed' }
if (!$rejected) { throw 'A changed FSR payload file was not rejected.' }
$results+=@{case='fsr-payload'; passed=$true; files=$fsrShipped.Count}
# 2. A code object for another GPU in a target's input is rejected.
$prefix=Join-Path $OutputDirectory 'inputs/mixed/game'
New-Inputs $prefix { param($root,$arch) if ($arch -eq 'gfx1101') { Write-CodeObject (Join-Path $root 'd4r/native/kernel3.hsaco') 65 } }
@@ -125,7 +167,7 @@ $metadata=Get-Content -LiteralPath (Join-Path $developmentRoot 'package/d4r/pack
if(!$metadata.localDevelopment){throw 'Local ZIP lacks development marker.'}
$archive=[IO.Compression.ZipFile]::OpenRead($zip)
try{
foreach($required in @('install.bat','install-dlss.ps1','d4r/d4r.ini')){
foreach($required in @('install.bat','install-dlss.ps1','install-dependencies.py','d4r/d4r.ini','d4r/model-tools/python/python.exe')){
if(!$archive.GetEntry($required)){throw "Local ZIP lacks $required"}
}
}finally{$archive.Dispose()}
+188
View File
@@ -0,0 +1,188 @@
"""Native installer preservation, rollback and pin contract tests; no downloads."""
import hashlib
import importlib.util
import json
from pathlib import Path
import tempfile
import unittest
from unittest.mock import patch
REPO = Path(__file__).resolve().parents[2]
spec = importlib.util.spec_from_file_location('install_dependencies', REPO / 'tools/windows/install_dependencies.py')
installer = importlib.util.module_from_spec(spec)
spec.loader.exec_module(installer)
class InstallTests(unittest.TestCase):
def setUp(self):
self.work = tempfile.TemporaryDirectory(prefix='d4r-installer-test-')
self.addCleanup(self.work.cleanup)
self.root = Path(self.work.name)
self.runtime = self.root / 'game/d4r'
self.runtime.mkdir(parents=True)
self.assets = []
for index in range(3):
source = self.root / f'source{index}'
source.write_bytes(f'new{index}'.encode())
relative = f'{index}/test.dll'
target = self.runtime / relative
target.parent.mkdir()
target.write_bytes(f'old{index}'.encode())
self.assets.append((source, relative, installer.sha(source)))
self.backups = self.root / 'backups'
def assert_originals(self):
for index, (_, relative, _) in enumerate(self.assets):
self.assertEqual((self.runtime / relative).read_bytes(), f'old{index}'.encode())
self.assertFalse(list(self.runtime.rglob('*.install-*')))
self.assertFalse(list(self.runtime.rglob('*.rollback-*')))
def test_backup_idempotence_unrelated(self):
unrelated = self.runtime / 'd4r.ini'
unrelated.write_bytes(b'\xef\xbb\xbf; keep\r\n')
result = installer.transaction(self.runtime, self.assets, self.backups)
self.assertEqual(result['changed'], 3)
manifest = json.loads((Path(result['backup']) / 'backup-manifest.json').read_text())
for index, entry in enumerate(manifest['files']):
self.assertEqual(Path(entry['backup']).read_bytes(), f'old{index}'.encode())
self.assertEqual(installer.transaction(self.runtime, self.assets, self.backups)['changed'], 0)
self.assertEqual(len(list(self.backups.iterdir())), 1)
self.assertEqual(unrelated.read_bytes(), b'\xef\xbb\xbf; keep\r\n')
def test_failure_rolls_back_replacements_and_new_files(self):
(self.runtime / self.assets[0][1]).unlink()
replace = installer.os.replace
def fail_third(source, destination):
if '.install-' in str(source) and destination == self.runtime / self.assets[2][1]:
raise OSError('injected replacement failure')
return replace(source, destination)
with patch.object(installer.os, 'replace', side_effect=fail_third):
with self.assertRaisesRegex(OSError, 'injected'):
installer.transaction(self.runtime, self.assets, self.backups)
self.assertFalse((self.runtime / self.assets[0][1]).exists())
for index in (1, 2):
self.assertEqual((self.runtime / self.assets[index][1]).read_bytes(), f'old{index}'.encode())
self.assertFalse(list(self.runtime.rglob('*.install-*')))
def test_concurrent_destination_edit_survives_rollback(self):
replace = installer.os.replace
def change_second(source, destination):
result = replace(source, destination)
if '.install-' in str(source) and destination == self.runtime / self.assets[0][1]:
(self.runtime / self.assets[1][1]).write_bytes(b'concurrent')
return result
with patch.object(installer.os, 'replace', side_effect=change_second):
with self.assertRaisesRegex(RuntimeError, 'changed after backup'):
installer.transaction(self.runtime, self.assets, self.backups)
self.assertEqual((self.runtime / self.assets[0][1]).read_bytes(), b'old0')
self.assertEqual((self.runtime / self.assets[1][1]).read_bytes(), b'concurrent')
def test_invalid_staging_never_mutates_runtime(self):
self.assets[2][0].write_bytes(b'tampered')
with self.assertRaisesRegex(RuntimeError, 'Staged DLL SHA256'):
installer.transaction(self.runtime, self.assets, self.backups)
self.assert_originals()
def test_pins_match_existing_installer(self):
original = (REPO / 'scripts/windows/install-dlss.ps1').read_text()
for _, _, url, digest in installer.PINS.values():
self.assertIn(digest, original)
if url:
self.assertIn(url, original)
for url, digest in (installer.DRIVER, installer.SEVENZIP):
self.assertIn(url, original)
self.assertIn(digest, original)
def test_fsr_denoiser_pin_matches_setup_script_and_is_optional(self):
name, relative, url, digest = installer.FSR_DENOISER
script = (REPO / 'scripts/windows/setup-fsr-rr.ps1').read_text()
self.assertIn(digest, script)
self.assertIn(url, script)
self.assertEqual((name, relative), ('amd_fidelityfx_denoiser_dx12.dll',) * 2)
self.assertNotIn('fsr', installer.PINS) # the NVIDIA pin table and dll-pins.txt stay NVIDIA-only
def test_fsr_collect_is_cached_verified_and_opt_in(self):
cache = self.root / 'cache'
payload = b'amd denoiser stand-in'
digest = hashlib.sha256(payload).hexdigest()
(cache / 'amd').mkdir(parents=True)
(cache / 'amd' / 'amd_fidelityfx_denoiser_dx12.dll').write_bytes(payload)
fake = ('amd_fidelityfx_denoiser_dx12.dll', 'amd_fidelityfx_denoiser_dx12.dll', 'https://unused.invalid', digest)
with patch.object(installer, 'FSR_DENOISER', fake), patch.object(installer, 'collect', wraps=installer.collect):
with patch.object(installer, 'PINS', {}):
self.assertEqual(installer.collect(cache, True, False), [])
assets = installer.collect(cache, True, True)
self.assertEqual([(a[1], a[2]) for a in assets], [('amd_fidelityfx_denoiser_dx12.dll', digest)])
(cache / 'amd' / 'amd_fidelityfx_denoiser_dx12.dll').write_bytes(b'tampered')
with patch.object(installer, 'FSR_DENOISER', fake), patch.object(installer, 'PINS', {}):
with self.assertRaisesRegex(RuntimeError, 'Invalid offline cache'):
installer.collect(cache, True, True)
def test_ini_selection_of_fsr(self):
ini = self.runtime / 'd4r.ini'
cases = (
(['[RayReconstruction]', 'Backend = fsr'], True),
(['[RayReconstruction]', 'Backend = FSR ; note'], True),
(['[RayReconstruction]', 'Backend = native'], False),
(['[Other]', 'Backend = fsr'], False),
([';[RayReconstruction]', '; Backend = fsr'], False),
([''], False),
)
for lines, expected in cases:
ini.write_text('\n'.join(lines) + '\n', encoding='utf-8')
self.assertEqual(installer.ini_selects_fsr(self.runtime), expected, lines)
def test_select_fsr_backend_preserves_ini_and_backs_up(self):
original = ('; keep\n[RayReconstruction]\n; note\nBackend = off ; trailing\nModelDir = auto\n[Debug]\nLog = normal\n').encode()
ini = self.runtime / 'd4r.ini'
ini.write_bytes(b'\xef\xbb\xbf' + original)
backup = installer.enable_fsr(self.runtime, self.backups)
self.assertEqual(backup.read_bytes(), b'\xef\xbb\xbf' + original)
updated = ini.read_bytes()
self.assertTrue(updated.startswith(b'\xef\xbb\xbf'))
self.assertIn(b'Backend = fsr ; trailing', updated)
self.assertEqual(updated.replace(b'Backend = fsr', b'Backend = off'), b'\xef\xbb\xbf' + original)
self.assertIsNone(installer.enable_fsr(self.runtime, self.backups))
self.assertEqual(len(list(self.backups.iterdir())), 1)
ini.write_bytes(b'[Debug]\nLog = normal\n')
installer.enable_fsr(self.runtime, self.backups)
self.assertTrue(installer.ini_selects_fsr(self.runtime))
ini.write_bytes(b'[RayReconstruction]')
installer.enable_fsr(self.runtime, self.backups)
self.assertTrue(installer.ini_selects_fsr(self.runtime))
def test_offline_bad_cache_preserved(self):
cache = self.root / 'cache.dll'
cache.write_bytes(b'old')
with self.assertRaisesRegex(RuntimeError, 'Invalid offline cache'):
installer.verified_download(cache, 'https://unused.invalid', '0' * 64, True)
self.assertEqual(cache.read_bytes(), b'old')
def test_cache_backups_outside_game(self):
with self.assertRaisesRegex(RuntimeError, 'outside game'):
installer.external(self.runtime / 'cache', self.runtime.parent)
installer.external(self.backups, self.runtime.parent)
def test_tools_ignore_unlisted_private_products_and_reject_tamper(self):
bundle = self.root / 'bundle'
names = ('python/python.exe', 'recipe/tools/windows/build_rr_model.py',
'recipe/tools/windows/deploy_rr_model.py', 'recipe/engine/rr/native_kernels.txt')
for name in names:
target = bundle / name
target.parent.mkdir(parents=True, exist_ok=True)
target.write_bytes(name.encode())
private = bundle / 'private.weights'
private.write_bytes(b'never copy')
manifest = dict(kind='local-rr-model-tools', modelsBundled=False, nvidiaBinariesBundled=False,
files={name: installer.sha(bundle / name) for name in names})
(bundle / 'rr-tools.json').write_text(json.dumps(manifest))
target = installer.prepare_model_tools(bundle, self.root / 'cache')
self.assertFalse((target / private.name).exists())
(target / names[0]).write_bytes(b'tampered')
with self.assertRaisesRegex(RuntimeError, 'cache changed'):
installer.prepare_model_tools(bundle, self.root / 'cache')
if __name__ == '__main__':
unittest.main()
+1 -1
View File
@@ -154,7 +154,7 @@ struct FsrRrRuntime
uint64_t count = 0;
ffxQueryDescGetVersions desc = {};
desc.header.type = FFX_API_QUERY_DESC_TYPE_GET_VERSIONS;
desc.createDescType = FFX_API_EFFECT_ID_DENOISER;
desc.createDescType = FFX_API_CREATE_CONTEXT_DESC_TYPE_DENOISER;
desc.device = device;
desc.outputCount = &count;
queryResult = query(nullptr, &desc.header);
+83
View File
@@ -0,0 +1,83 @@
// Experimental app-local amdxc64.dll for running AMD's FSR Ray Regeneration denoiser on a vkd3d-proton device
// (D3D12 over Vulkan) on native Windows RDNA3. AMD's real amdxc64.dll expects AMD's own ID3D12Device and must not
// see a vkd3d device. This shim only answers the capability queries the denoiser makes (recovered from the call
// sites in amd_fidelityfx_denoiser_dx12.dll 1.2.0.2740): it advertises the FP8 wave-matrix shape the denoiser's
// shaders use. The shaders themselves are executed by dxil-spirv's FP8 emulation inside vkd3d-proton, not by
// hardware FP8. It does not forward anything to AMD's driver.
#include <windows.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
static void trace(const char* what, const GUID* g) {
const char* path = getenv("D4R_AMDXC_LOG"); if (!path) return;
FILE* f = fopen(path, "a"); if (!f) return;
if (g) fprintf(f, "%s {%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n", what, g->Data1, g->Data2, g->Data3, g->Data4[0], g->Data4[1], g->Data4[2], g->Data4[3], g->Data4[4], g->Data4[5], g->Data4[6], g->Data4[7]);
else fprintf(f, "%s\n", what);
fclose(f);
}
static const GUID IID_Factory = {0x014937ec, 0x9288, 0x446f, {0xa9, 0xac, 0xd7, 0x5a, 0x8e, 0x3a, 0x98, 0x4f}};
static const GUID IID_WaveMatrix = {0xf714e11a, 0xb54e, 0x4e0f, {0xab, 0xc5, 0xdf, 0x58, 0xb1, 0x81, 0x33, 0xd1}};
static const GUID IID_Intrinsics = {0xba019d53, 0xccab, 0x4cbd, {0xb5, 0x6a, 0x72, 0x30, 0xed, 0x43, 0x30, 0xad}};
static const GUID IID_DeviceInfo = {0xb58d6601, 0x7401, 0x4234, {0x81, 0x80, 0x6f, 0xeb, 0xfc, 0x0e, 0x48, 0x4c}};
typedef struct Obj { void** vtbl; LONG refs; } Obj;
static ULONG WINAPI AddRef_(void* self) { return (ULONG)InterlockedIncrement(&((Obj*)self)->refs); }
static ULONG WINAPI Release_(void* self) { return (ULONG)InterlockedDecrement(&((Obj*)self)->refs); } // statics: never freed
static HRESULT WINAPI QI_(void* self, const GUID* iid, void** out) { (void)iid; *out = self; AddRef_(self); return S_OK; }
static HRESULT WINAPI Unused_(void* self) { (void)self; return (HRESULT)0x80004001; } // E_NOTIMPL
static HRESULT WINAPI Ok_(void* self) { (void)self; return S_OK; }
static HRESULT WINAPI OkOpcode_(void* self, uint32_t opcode) { (void)self; (void)opcode; trace("EnableOpcode", NULL); return S_OK; }
// IID_WaveMatrix slot 16: (count in/out, properties); entry = M, N, K (u64), then 0xb, 0xb, 1, 1 (u32), flag (u8)
static HRESULT WINAPI WaveMatrixProperties_(void* self, uint64_t* count, uint8_t* props) {
(void)self; trace("WaveMatrixProperties", NULL);
if (*count < 1) { *count = 1; return S_OK; }
memset(props, 0, 0x30);
uint64_t shape[3] = {16, 16, 16}; memcpy(props, shape, sizeof(shape));
uint32_t types[4] = {0xb, 0xb, 1, 1}; memcpy(props + 0x18, types, sizeof(types));
props[0x28] = 0;
*count = 1;
return S_OK;
}
// IID_DeviceInfo slot 3: (request[0x50], size). Command 2 fills 0x40 nonzero bytes at +0x10.
static HRESULT WINAPI DeviceInfo_(void* self, uint8_t* request, uint64_t size) {
(void)self; trace("DeviceInfo", NULL);
if (size < 0x50) return (HRESULT)0x80070057;
uint32_t command; memcpy(&command, request, 4);
if (command != 2) return (HRESULT)0x80004001;
uint64_t info[8] = {0x1101, 1, 0x20, 0x2000, 64, 1, 1, 1}; // placeholders, see docs/windows-fsr-rr.md
memcpy(request + 0x10, info, sizeof(info));
return S_OK;
}
#define VT 24
static void* vt_factory[VT], *vt_wave[VT], *vt_intr[VT], *vt_info[VT];
static Obj factory = {vt_factory, 1}, wave = {vt_wave, 1}, intr = {vt_intr, 1}, info = {vt_info, 1};
static HRESULT WINAPI CreateInterface_(void* self, void* device, const GUID* iid, void** out) {
(void)self; (void)device; *out = NULL; trace("CreateInterface", iid);
Obj* o = NULL;
if (IsEqualGUID(iid, &IID_WaveMatrix)) o = &wave;
else if (IsEqualGUID(iid, &IID_Intrinsics)) o = &intr;
else if (IsEqualGUID(iid, &IID_DeviceInfo)) o = &info;
if (!o) return (HRESULT)0x80004002; // E_NOINTERFACE
AddRef_(o); *out = o; return S_OK;
}
static void init_tables(void) {
void** all[4] = {vt_factory, vt_wave, vt_intr, vt_info};
for (int t = 0; t < 4; ++t) {
all[t][0] = (void*)QI_; all[t][1] = (void*)AddRef_; all[t][2] = (void*)Release_;
for (int i = 3; i < VT; ++i) all[t][i] = (void*)Unused_;
}
vt_factory[3] = (void*)CreateInterface_;
vt_wave[16] = (void*)WaveMatrixProperties_;
vt_intr[3] = (void*)Ok_; vt_intr[4] = (void*)OkOpcode_; vt_intr[5] = (void*)Ok_;
vt_info[3] = (void*)DeviceInfo_;
}
__declspec(dllexport) HRESULT WINAPI AmdExtD3DCreateInterface(void* device, const GUID* iid, void** out) {
static volatile LONG ready = 0; (void)device; *out = NULL; trace("AmdExtD3DCreateInterface", iid);
if (!ready) { init_tables(); InterlockedExchange(&ready, 1); } // idempotent: concurrent init writes the same values
if (!IsEqualGUID(iid, &IID_Factory)) return (HRESULT)0x80004002; // E_NOINTERFACE
AddRef_(&factory); *out = &factory; return S_OK;
}
+107
View File
@@ -0,0 +1,107 @@
"""Compare strict HIP K candidates against a fixed model on synthetic temporal frames.
Each candidate overlays only named code objects in a private model copy. All five
frame products must match the baseline hashes exactly. Raw files created by this
run are hashed and deleted immediately after each process; existing evidence is
never removed. Reported frame timestamps include Vulkan front/back and CPU gaps.
"""
import argparse
import hashlib
import json
import os
from pathlib import Path
import re
import shutil
import subprocess
import sys
repo = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(repo / '.tools/python/vendor'))
import numpy as np
def main():
parser = argparse.ArgumentParser(description=__doc__)
for name in ('runner', 'model', 'case', 'output'):
parser.add_argument('--' + name, required=True, type=lambda p: Path(p).resolve())
parser.add_argument('--candidate', action='append', default=[], metavar='LABEL=OBJECT_DIRECTORY')
parser.add_argument('--repeats', type=int, default=25)
args = parser.parse_args()
if not args.output.is_relative_to((repo / 'build').resolve()):
parser.error('output must stay under ignored build/')
args.output.mkdir(parents=True, exist_ok=False)
frame_count = sum(1 for line in args.case.read_text().splitlines()
if line.split() and line.split()[0] in ('frame', 'frame_auto'))
if not frame_count:
parser.error('case must contain explicit frame or frame_auto entries')
expected_products = {f'frame-{i}.{suffix}.bin' for i in range(frame_count)
for suffix in ('312', '288', '296', '304', 'head')}
env = {k: v for k, v in os.environ.items() if not k.startswith('D4R_')}
env['PATH'] = str(repo / '.tools/therock-10.2.0a20260929/_rocm_sdk_core/bin') + os.pathsep + env['PATH']
report = {'synthetic': True, 'gameAccessed': False, 'exactGate': 'all five frame products',
'timingScope': 'Vulkan front + HIP network + Vulkan back, including CPU synchronization gaps',
'runnerSha256': hashlib.sha256(args.runner.read_bytes()).hexdigest(),
'caseSha256': hashlib.sha256(args.case.read_bytes()).hexdigest(), 'runs': []}
reference = None
variants = [('baseline', None)] + [tuple(s.split('=', 1)) for s in args.candidate]
for label, source in variants:
if not re.fullmatch(r'[a-zA-Z0-9_-]+', label):
parser.error('candidate label must be letters, digits, underscores or hyphens')
output = args.output / label
output.mkdir()
model = args.model
row = {'label': label, 'objects': {}}
if source:
model = output / 'model'
shutil.copytree(args.model, model)
inputs = sorted(Path(source).resolve().glob('*.hsaco')) + sorted(Path(source).resolve().glob('*.spv'))
for obj in inputs:
destination = model / 'hip' / obj.name if obj.suffix == '.hsaco' else model / obj.name
if not destination.is_file():
raise RuntimeError('candidate must replace an existing object: ' + obj.name)
shutil.copy2(obj, destination)
row['objects'][obj.name] = hashlib.sha256(obj.read_bytes()).hexdigest()
if not row['objects']:
raise RuntimeError('candidate contains no objects')
manifest = json.loads((model / 'model.json').read_text())
for name, digest in row['objects'].items():
manifest['sha256'][name] = digest
manifest['experimentalOverlay'] = row['objects']
(model / 'model.json').write_text(json.dumps(manifest, indent=2) + '\n')
command = [str(args.runner), str(model), str(args.case), str(output / 'frames'), str(args.repeats), 'hip']
with (output / 'stdout.log').open('wb') as out, (output / 'stderr.log').open('wb') as err:
result = subprocess.run(command, env=env, stdout=out, stderr=err, timeout=240)
row['exitCode'] = result.returncode
hashes = {}
finite = True
for path in sorted((output / 'frames').glob('frame-*.bin')):
hashes[path.name] = hashlib.sha256(path.read_bytes()).hexdigest()
if path.name.endswith('.312.bin'):
values = np.fromfile(path, '<f2').astype('f4')
finite = finite and bool(np.isfinite(values).all() and np.var(values) > 1e-6)
if not path.resolve().is_relative_to(args.output):
raise RuntimeError('raw cleanup escaped the fresh output directory')
path.unlink()
row['hashes'] = hashes
row['finiteNonuniformOutput'] = finite
if reference is None:
reference = hashes
row['differentProducts'] = [k for k in sorted(set(hashes) | set(reference)) if hashes.get(k) != reference.get(k)]
row['exact'] = result.returncode == 0 and set(hashes) == expected_products and not row['differentProducts'] and finite
text = (output / 'stdout.log').read_text(errors='replace')
timing = re.search(r'full native frame: ([0-9.]+) us GPU median \(min ([0-9.]+)\)', text)
if timing:
row['measuredMedianUs'] = float(timing[1])
row['measuredMinUs'] = float(timing[2])
report['runs'].append(row)
(args.output / 'summary.json').write_text(json.dumps(report, indent=2) + '\n')
print(f'{label}: exact={row["exact"]} differing={len(row["differentProducts"])} median_us={row.get("measuredMedianUs")}', flush=True)
if result.returncode:
return 1
report['passed'] = all(row['exact'] for row in report['runs'])
(args.output / 'summary.json').write_text(json.dumps(report, indent=2) + '\n')
return 0 if report['passed'] else 1
if __name__ == '__main__':
raise SystemExit(main())
+47 -2
View File
@@ -11,6 +11,7 @@ from pathlib import Path
import re
import statistics
import subprocess
import shutil
def main():
@@ -24,9 +25,17 @@ def main():
help='diagnostic CPU stage logging; adds overhead to timings')
parser.add_argument('--candidate-native', type=lambda s: Path(s).resolve(),
help='compare an experimental native directory instead of input batching')
parser.add_argument('--control-native', type=lambda s: Path(s).resolve(),
help='use an explicit verified control kernel directory while retaining runtime dependencies')
parser.add_argument('--player-config', action='store_true', help='select batching/native paths through staged player INI')
parser.add_argument('--sequence', choices=('abba', 'controls', 'batches'), default='abba',
help='paired comparison, or repeated identical settings to diagnose baseline determinism')
parser.add_argument('--reset-every-frame', action='store_true', help='diagnose history dependence; not representative temporal performance')
parser.add_argument('--profile-kernels', action='store_true',
help='diagnostic deferred GPU events, allowing legacy-stream dependencies; timings are instrumented')
args = parser.parse_args()
if args.player_config and (args.profile_stages or args.profile_kernels):
parser.error('player mode clears diagnostic profiling flags; use a separate instrumented run')
if args.frames < 8 or args.frames > 120:
parser.error('frames must be 8..120')
if not re.fullmatch(r'[1-9][0-9]*x[1-9][0-9]*', args.resolution):
@@ -50,6 +59,21 @@ def main():
native = runtime / 'native'
if (native / 'gfx1101').is_dir():
native = native / 'gfx1101'
if args.control_native:
native = args.control_native
report['playerConfig'] = args.player_config
report['sequence'] = args.sequence
report['resetEveryFrame'] = args.reset_every_frame
module = args.shim
if args.player_config:
runtime = args.output / 'player'
shutil.copytree(args.runtime, runtime)
module = runtime / 'd4r_nvngx.dll'
shutil.copy2(args.shim, module)
for source, relative in ((args.core, 'ngx/_nvngx.dll'), (args.dlss, 'dlss/nvngx_dlss.dll')):
destination = runtime / relative
destination.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(source, destination)
nvapi = runtime / 'nvapi/nvapi64.dll'
if not nvapi.is_file():
nvapi = runtime / 'nvapi/gfx1101/nvapi64.dll'
@@ -82,7 +106,8 @@ def main():
D4R_ZLUDA_PROFILE_EVERY='1')
try:
reference = None
for index, batched in enumerate((False, True, True, False)):
variants = {'abba': (False, True, True, False), 'controls': (False,) * 4, 'batches': (True,) * 4}
for index, batched in enumerate(variants[args.sequence]):
label = ('native-candidate' if args.candidate_native else 'batch') if batched else 'control'
output = args.output / f'{index}-{label}'
output.mkdir()
@@ -92,17 +117,32 @@ def main():
else:
env.pop('D4R_BATCH_INPUT_COPIES', None)
env['D4R_ZLUDA_NATIVE_DIR'] = str(args.candidate_native if batched and args.candidate_native else native)
command = [str(args.probe), '--module', str(args.shim), '--hip-root', str(runtime / 'hip'),
if args.player_config:
config = (Path(__file__).resolve().parents[2] / 'packaging/windows/d4r.ini').read_text()
config = re.sub(r'(?m)^BatchInputCopies\s*=\s*(?:true|false)\s*$',
'BatchInputCopies = ' + str(batched and not args.candidate_native).lower(), config)
selected_native = args.candidate_native if batched and args.candidate_native else native
config = config.replace('NativeDir = auto', 'NativeDir = ' + str(selected_native))
(runtime / 'd4r.ini').write_text(config)
command = [str(args.probe), '--module', str(module), '--hip-root', str(runtime / 'hip'),
'--cuda-dll', str(runtime / 'zluda/nvcuda.dll'), '--ngx-core', str(args.core),
'--dlss-dll', str(args.dlss), '--nvapi-dll', str(nvapi),
'--ngx-abi', 'project', '--interop-mode', 'command-list', '--preset', '11',
'--ngx-output-resolution', args.resolution,
'--ngx-input-resolution', args.input_resolution, '--iterations', str(args.frames)]
if args.player_config:
command += ['--player-mode', '1']
if args.reset_every_frame:
command += ['--reset-every-frame', '1']
with (output / 'stdout.log').open('wb') as out, (output / 'stderr.log').open('wb') as err:
result = subprocess.run(command, env=env, stdout=out, stderr=err, timeout=180)
if args.player_config and (runtime / 'd4r_nvngx.log').exists():
shutil.copy2(runtime / 'd4r_nvngx.log', output / 'player.log')
text = (output / 'stdout.log').read_text(errors='replace')
if result.returncode or 'PASS D3D12_DLSS' not in text:
raise RuntimeError(f'run {index} failed ({result.returncode}); inspect {output}')
if args.reset_every_frame and 'D3D12_RESET every_frame=1' not in text:
raise RuntimeError('Requested reset diagnostic was not applied')
iw, ih = map(int, args.input_resolution.split('x'))
expected_size = f'D3D12_NGX_DIMENSIONS input={iw}x{ih} output={ow}x{oh}'
if expected_size not in text or f'D4R_INTEROP async=0 batch_inputs={int(batched and not args.candidate_native)}' not in text:
@@ -118,6 +158,9 @@ def main():
if reference is None:
reference = hashes
elif hashes != reference:
report['mismatchRun'] = index
report['differentFrames'] = [name for name, digest in hashes.items() if reference.get(name) != digest]
report['mismatchedOutputHashes'] = hashes
raise RuntimeError(f'run {index}: output differs from control')
report['runs'].append({'batch': batched and not bool(args.candidate_native),
'candidate': batched, 'allFrameMs': times,
@@ -125,6 +168,8 @@ def main():
print(f'run={index} variant={label} warmed_cpu_interop_ms={statistics.median(times[4:]):.3f}', flush=True)
for mode in (False, True):
values = [t for row in report['runs'] if row['candidate'] == mode for t in row['allFrameMs'][4:]]
if not values:
continue
key = ('candidateMedianMs' if args.candidate_native else 'batchMedianMs') if mode else 'controlMedianMs'
report[key] = statistics.median(values)
report['passed'] = True
+95
View File
@@ -0,0 +1,95 @@
"""Build an isolated diagnostic ZLUDA runtime using the already-built LLVM tree.
Never replaces the delivered runtime or rebuilds LLVM. Output is local under
build/. The copied runtime is for audited synthetic captures, not game use.
"""
import argparse
import hashlib
import json
import os
from pathlib import Path
import shutil
import subprocess
def sha(path):
with path.open('rb') as stream:
return hashlib.file_digest(stream, 'sha256').hexdigest()
def main():
repo = Path(__file__).resolve().parents[2]
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--runtime', required=True, type=lambda p: Path(p).resolve())
parser.add_argument('--output', required=True, type=lambda p: Path(p).resolve())
parser.add_argument('--jobs', type=int, default=4)
args = parser.parse_args()
if args.output.exists() or not args.output.is_relative_to((repo / 'build').resolve()):
parser.error('use a fresh directory under ignored build/')
if args.output.is_relative_to(args.runtime) or not 1 <= args.jobs <= 8:
parser.error('output must be outside input runtime; jobs must be 1..8')
source = repo / 'external/ZLUDA'
if '"hiluma_engine_output_depthreg_mvhi_ldr_max_v2_rel" => Some(328)' not in (source / 'zluda/src/impl/d4r_replay.rs').read_text():
parser.error('apply tools/windows/zluda-k-output-capture.patch to the prepared Windows ZLUDA source first')
llvm = repo / 'build/zluda-llvm-windows-gnu'
rust = repo / '.tools/rust-1.98.1-gnu'
compiler = repo / '.tools/llvm-mingw-20260922-ucrt-x86_64'
target = repo / 'build/zluda-rust-windows-gnu'
for path in (llvm / 'bin/llvm-config.exe', rust / 'bin/cargo.exe', source / 'ptx/lib/zluda_ptx_impl.bc'):
if not path.is_file():
parser.error('prebuilt dependency missing: ' + str(path))
args.output.mkdir(parents=True)
artifact = target / 'x86_64-pc-windows-gnu/release/nvcuda.dll'
if artifact.exists():
shutil.copy2(artifact, args.output / 'previous-cargo-nvcuda.dll')
environment = os.environ.copy()
environment.update(CARGO_HOME=str(repo / '.tools/cargo-home'), CARGO_TARGET_DIR=str(target),
CARGO_BUILD_JOBS=str(args.jobs), RUSTC=str(rust / 'bin/rustc.exe'),
CC=str(compiler / 'bin/x86_64-w64-mingw32-clang.exe'),
CXX=str(compiler / 'bin/x86_64-w64-mingw32-clang++.exe'),
AR=str(compiler / 'bin/llvm-ar.exe'), CXXSTDLIB='c++',
LIBRARY_PATH=str(rust / 'lib/rustlib/x86_64-pc-windows-gnu/lib/self-contained'),
CARGO_TARGET_X86_64_PC_WINDOWS_GNU_LINKER=str(compiler / 'bin/x86_64-w64-mingw32-clang.exe'),
CARGO_TARGET_X86_64_PC_WINDOWS_GNU_RUSTFLAGS='-C link-arg=-static',
ZLUDA_LLVM_BUILD_DIR=str(llvm))
environment['PATH'] = os.pathsep.join(str(p) for p in (rust / 'bin', compiler / 'bin',
repo / '.tools/python/cmake/data/bin', repo / '.tools/python/bin', Path('C:/Program Files/Git/usr/bin'))) + os.pathsep + environment['PATH']
report = dict(passed=False, diagnosticOnly=True, llvmRebuilt=False, deliveredRuntimeChanged=False,
originalRuntimeSha256=sha(args.runtime / 'zluda/nvcuda.dll'),
captureSourceSha256=sha(source / 'zluda/src/impl/d4r_replay.rs'),
llvmConfigSha256=sha(llvm / 'bin/llvm-config.exe'))
try:
command = [str(rust / 'bin/cargo.exe'), 'build', '--offline', '--locked', '--release',
'--target', 'x86_64-pc-windows-gnu', '-p', 'zluda']
with (args.output / 'cargo.log').open('wb') as log:
process = subprocess.Popen(command, cwd=source, env=environment, stdout=log, stderr=subprocess.STDOUT)
try:
code = process.wait(timeout=1800)
except subprocess.TimeoutExpired:
subprocess.run(['taskkill', '/PID', str(process.pid), '/T', '/F'], check=False, timeout=30,
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
process.wait(timeout=30)
raise RuntimeError('Capture runtime build timed out; process tree terminated')
if code:
raise subprocess.CalledProcessError(code, command)
runtime = args.output / 'runtime'
shutil.copytree(args.runtime, runtime)
shutil.copy2(artifact, runtime / 'zluda/nvcuda.dll')
for name in ('libc++.dll', 'libunwind.dll'):
shutil.copy2(compiler / 'bin' / name, runtime / 'zluda' / name)
if sha(args.runtime / 'zluda/nvcuda.dll') != report['originalRuntimeSha256']:
raise RuntimeError('Original runtime changed during build')
report.update(passed=True, runtime=str(runtime), runtimeSha256=sha(runtime / 'zluda/nvcuda.dll'),
sourceBase=subprocess.check_output(['git', 'rev-parse', 'HEAD'], cwd=source, text=True).strip(),
sourceWorkingTree='local modified; capture source hash recorded, no clean source assertion',
gpuValidation=False, published=False)
print(json.dumps(report, indent=2))
except Exception as error:
report['error'] = str(error)
raise
finally:
(args.output / 'build.json').write_text(json.dumps(report, indent=2) + '\n')
if __name__ == '__main__':
main()
+146
View File
@@ -0,0 +1,146 @@
"""Build the experimental FSR Ray Regeneration payload for native Windows: vkd3d-proton + the amdxc64 shim.
Output (a fresh directory under ignored build/):
fsr/d4r_vkd3d_core.dll vkd3d-proton's d3d12core.dll, built for Windows with dxil-spirv's scalar FP8 fallback
(upstream PR 307) and extended emulation, renamed so it cannot collide with the system's
fsr/amdxc64.dll d4r's capability shim (tools/windows/amdxc64_shim); never forwards to AMD's driver
licenses/ vkd3d-proton (LGPL-2.1), dxil-spirv (MIT), dxbc-spirv licence texts
SOURCE.txt exact commits, patches and build flags: the LGPL source offer for the shipped binary
fsr-payload.json hashes of everything above
AMD's amd_fidelityfx_denoiser_dx12.dll is NOT part of the payload (installed hash-pinned by install.bat on request).
Needs network once (git clone of vkd3d-proton and its submodules), the vendored llvm-mingw, pinned glslang 16.6.0,
ninja, and meson (installed with pip --target into .tools/meson-pkg when missing).
"""
import argparse
import hashlib
import json
import os
import shutil
import subprocess
import sys
from pathlib import Path
repo = Path(__file__).resolve().parents[2]
VKD3D_URL = 'https://github.com/HansKristian-Work/vkd3d-proton.git'
VKD3D_COMMIT = '3dfc6f07d0953b1e8b41705275c2c59cc7374fc5'
DXIL_FSR_MERGE = 'f9f6294f3a6795e071bef3a64d2bee99e7c1b7bc' # dxil-spirv PR 307 merge (scalar FP8 fallback)
GLSLANG_SHA256 = '9643709c02c83e26773f96b6a87f197ab2e84f78be0879ad8fb3619418b5d9ad'
def sha(path):
return hashlib.sha256(Path(path).read_bytes()).hexdigest()
def run(command, cwd=None, env=None, log=None):
print('+ ' + ' '.join(str(part) for part in command), flush=True)
with open(log, 'ab') if log else open(os.devnull, 'wb') as out:
result = subprocess.run([str(part) for part in command], cwd=cwd, env=env, stdout=out, stderr=subprocess.STDOUT)
if result.returncode:
raise RuntimeError(f'command failed ({result.returncode}): {command[0]} {command[1] if len(command) > 1 else ""}; see {log}')
def git(args, cwd, log, identity=False):
prefix = ['git', '-c', 'user.name=d4r', '-c', 'user.email=d4r@localhost'] if identity else ['git']
run(prefix + list(args), cwd=cwd, log=log)
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--output', required=True, type=lambda p: Path(p).resolve())
parser.add_argument('--source', type=lambda p: Path(p).resolve(),
help='reuse an existing vkd3d-proton clone (must be a checkout of the pinned commit); default: fresh clone under --output/_work')
args = parser.parse_args()
if not args.output.is_relative_to((repo / 'build').resolve()):
parser.error('output must stay under ignored build/')
args.output.mkdir(parents=True, exist_ok=False)
work = args.output / '_work'
work.mkdir()
log = work / 'build.log'
tools = repo / '.tools'
mingw = tools / 'llvm-mingw-20260922-ucrt-x86_64/bin'
glslang = tools / 'glslang-16.6.0/bin/glslangValidator.exe'
ninja = tools / 'python/bin/ninja.exe'
for required in (mingw / 'x86_64-w64-mingw32-clang.exe', glslang, ninja):
if not required.is_file():
raise RuntimeError('missing tool: ' + str(required))
if sha(glslang) != GLSLANG_SHA256:
raise RuntimeError('requires the pinned glslang 16.6.0')
meson_path = tools / 'meson-pkg'
if not (meson_path / 'mesonbuild').is_dir():
run([sys.executable, '-m', 'pip', 'install', '--target', meson_path, 'meson'], log=log)
source = args.source
if source is None:
source = work / 'vkd3d-proton'
git(['clone', VKD3D_URL, source], work, log)
git(['checkout', '-q', '-f', '--detach', VKD3D_COMMIT], source, log)
git(['submodule', 'update', '--init', '--recursive', '-q'], source, log)
if args.source is not None:
git(['submodule', 'update', '--force', '--recursive', '-q'], source, log)
# dxil-spirv: the accepted scalar FP8 fallback (PR 307), cherry-picked as the Linux build script does
dxil = source / 'subprojects/dxil-spirv'
git(['fetch', '-q', 'origin', DXIL_FSR_MERGE], dxil, log)
listing = subprocess.run(['git', 'rev-list', '--reverse', DXIL_FSR_MERGE + '^1..' + DXIL_FSR_MERGE + '^2'], cwd=dxil,
capture_output=True, text=True, check=True).stdout.split()
ancestor = subprocess.run(['git', 'merge-base', '--is-ancestor', DXIL_FSR_MERGE + '^2', 'HEAD'], cwd=dxil).returncode == 0
if not ancestor:
git(['cherry-pick', '-x'] + listing, dxil, log, identity=True)
# libc++ include fixes (GCC pulled these in transitively; libc++ does not)
for patch, where in (('0001-dxil-spirv-include-exception-for-libcxx.patch', dxil),
('0002-dxbc-spirv-include-new-for-libcxx.patch', dxil / 'subprojects/dxbc-spirv')):
full = repo / 'patches/vkd3d-proton-windows' / patch
check = subprocess.run(['git', 'apply', '--check', '--reverse', str(full)], cwd=where, capture_output=True)
if check.returncode: # not applied yet
git(['apply', full], where, log)
escaped = lambda path: str(path).replace('\\', '/')
cross = work / 'cross.txt'
cross.write_text('\n'.join([
'[binaries]',
f"c = '{escaped(mingw / 'x86_64-w64-mingw32-clang.exe')}'",
f"cpp = '{escaped(mingw / 'x86_64-w64-mingw32-clang++.exe')}'",
f"ar = '{escaped(mingw / 'x86_64-w64-mingw32-ar.exe')}'",
f"strip = '{escaped(mingw / 'x86_64-w64-mingw32-strip.exe')}'",
f"windres = '{escaped(mingw / 'x86_64-w64-mingw32-windres.exe')}'",
f"widl-mingw-tools-fallback = '{escaped(mingw / 'x86_64-w64-mingw32-widl.exe')}'",
'', '[properties]', 'needs_exe_wrapper = true', '',
'[host_machine]', "system = 'windows'", "cpu_family = 'x86_64'", "cpu = 'x86_64'", "endian = 'little'", '']))
env = dict(os.environ)
env['PATH'] = os.pathsep.join([str(mingw), str(glslang.parent), str(ninja.parent), env['PATH']])
env['PYTHONPATH'] = str(meson_path)
build = work / 'build'
mesonmain = [sys.executable, '-m', 'mesonbuild.mesonmain']
run(mesonmain + ['setup', '--cross-file', cross, '--buildtype', 'release', '-Denable_tests=false',
'-Denable_extended_emulation=true', build, source], cwd=source, env=env, log=log)
run(mesonmain + ['compile', '-C', build], cwd=source, env=env, log=log)
fsr = args.output / 'fsr'
fsr.mkdir()
core = build / 'libs/d3d12core/d3d12core.dll'
run([mingw / 'x86_64-w64-mingw32-strip.exe', '-o', fsr / 'd4r_vkd3d_core.dll', core], log=log)
run([mingw / 'x86_64-w64-mingw32-clang.exe', '-shared', '-O2', '-Wall', '-o', fsr / 'amdxc64.dll',
repo / 'tools/windows/amdxc64_shim/amdxc64_shim.c', '-Wl,--kill-at'], log=log)
licenses = args.output / 'licenses'
licenses.mkdir()
for src, name in ((source / 'LICENSE', 'vkd3d-proton-LGPL-2.1.txt'), (source / 'COPYING', 'vkd3d-proton-COPYING.txt'),
(dxil / 'LICENSE.MIT', 'dxil-spirv-MIT.txt'), (dxil / 'subprojects/dxbc-spirv/LICENSE', 'dxbc-spirv-LICENSE.txt')):
shutil.copy2(src, licenses / name)
heads = {name: subprocess.run(['git', 'rev-parse', 'HEAD'], cwd=path, capture_output=True, text=True, check=True).stdout.strip()
for name, path in (('vkd3d-proton', source), ('dxil-spirv', dxil), ('dxbc-spirv', dxil / 'subprojects/dxbc-spirv'))}
patches = {p.name: sha(p) for p in sorted((repo / 'patches/vkd3d-proton-windows').glob('*.patch'))}
(args.output / 'SOURCE.txt').write_text('\n'.join([
'fsr/d4r_vkd3d_core.dll is vkd3d-proton (LGPL-2.1) built for Windows by tools/windows/build_fsr_payload.py.',
'Corresponding source: ' + VKD3D_URL + ' at ' + VKD3D_COMMIT + ',',
'dxil-spirv with upstream PR 307 (merge ' + DXIL_FSR_MERGE + ') cherry-picked, and the patches in',
'patches/vkd3d-proton-windows/ of the d4r repository (libc++ include fixes). Build: meson cross build with',
'llvm-mingw clang, -Denable_extended_emulation=true, buildtype release; the DLL is stripped and renamed.',
'Built HEADs: ' + json.dumps(heads), 'Patches: ' + json.dumps(patches),
'fsr/amdxc64.dll is d4r original code (tools/windows/amdxc64_shim). It is not AMD software.', '']))
files = {str(p.relative_to(args.output)).replace('\\', '/'): sha(p) for p in sorted(args.output.rglob('*'))
if p.is_file() and '_work' not in p.relative_to(args.output).parts}
(args.output / 'fsr-payload.json').write_text(json.dumps(dict(
schema=1, kind='d4r-fsr-payload', amdDenoiserBundled=False, vkd3dProtonCommit=VKD3D_COMMIT, dxilSpirvFsrMerge=DXIL_FSR_MERGE,
builtHeads=heads, patches=patches, files=files), indent=2) + '\n')
print(json.dumps(files, indent=2))
print('FSR_PAYLOAD_BUILT ' + str(args.output))
if __name__ == '__main__':
main()
+91
View File
@@ -0,0 +1,91 @@
"""Compile a private native K LDR model from verified Windows synthetic captures.
Requires all 11 extracted layers and 16 captured reconstruction tables. Never
substitutes synthetic tables, installs into a game, or creates a release archive.
"""
import argparse
import hashlib
import json
import os
from pathlib import Path
import subprocess
import sys
def sha(path):
with path.open('rb') as stream:
return hashlib.file_digest(stream, 'sha256').hexdigest()
def main():
repo = Path(__file__).resolve().parents[2]
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--capture', required=True, type=lambda p: Path(p).resolve())
parser.add_argument('--output', required=True, type=lambda p: Path(p).resolve())
parser.add_argument('--glslang', type=lambda p: Path(p).resolve(), default=repo / '.tools/glslang-16.6.0/bin/glslangValidator.exe')
parser.add_argument('--reuse', action='store_true')
args = parser.parse_args()
if not args.output.is_relative_to((repo / 'build').resolve()):
parser.error('private model products must stay under ignored build/')
validation = json.loads((args.capture / 'validation.json').read_text())
if (validation.get('passed') is not True or validation.get('reconstructionLutCaptured') is not True
or validation.get('reconstructionTables') != 16 or validation.get('extractedLayers') != 11
or validation.get('numerics') != 'explicit-f16-gains'):
parser.error('requires verified complete 11-layer/16-table capture')
if set(validation['products']) != {'k.bin', 'k.json', 'lut.bin'}:
raise RuntimeError('Unexpected/missing capture products')
weights = args.capture / 'weights'
for name, expected in validation['products'].items():
if sha(weights / name) != expected:
raise RuntimeError('Capture product changed: ' + name)
if (weights / 'lut.bin').stat().st_size != 16 * 32768:
raise RuntimeError('Incomplete reconstruction tables')
if sha(args.glslang) != '9643709c02c83e26773f96b6a87f197ab2e84f78be0879ad8fb3619418b5d9ad':
raise RuntimeError('Requires pinned glslang 16.6.0')
inputs = ['engine/compile_k.py', 'engine/test_k.py', 'engine/layer.comp', 'engine/input_k.comp',
'engine/output_k.comp', 'engine/exposure_k.comp', 'engine/normalized_store.glsl']
identity = dict(schema=1, preset='K', variant='ldr', numerics='explicit-f16-gains',
captureProducts=validation['products'], glslang=sha(args.glslang),
python=sha(Path(sys.executable)), sources={name:sha(repo / name) for name in inputs},
builder=sha(Path(__file__)))
stamp = args.output / 'windows-k-model-cache.json'
def products():
return {p.name:sha(p) for p in sorted(args.output.iterdir()) if p.is_file() and p.name != stamp.name}
if args.output.exists():
if not args.reuse:
parser.error('use a fresh output directory or --reuse')
cached = json.loads(stamp.read_text())
if cached['identity'] != identity or cached['products'] != products():
raise RuntimeError('K model cache changed; preserve it and use a fresh output directory')
print('K_MODEL_CACHE_HIT')
return
env = {key:value for key,value in os.environ.items() if not key.startswith(('D4R_', 'K_DEFS_'))}
env['PYTHONPATH'] = os.pathsep.join(str(repo / name) for name in ('.tools/python/vendor', 'engine', 'kernels/tools'))
env['PATH'] = str(args.glslang.parent) + os.pathsep + env['PATH']
args.output.mkdir(parents=True)
command = [sys.executable, str(repo / 'engine/compile_k.py'), str(weights / 'k'),
str(weights / 'lut.bin'), str(args.output), '--ldr']
with (args.output / 'compile.log').open('wb') as log:
process = subprocess.Popen(command, env=env, stdout=log, stderr=subprocess.STDOUT)
try:
code = process.wait(timeout=600)
except subprocess.TimeoutExpired:
subprocess.run(['taskkill', '/PID', str(process.pid), '/T', '/F'], check=False, timeout=30,
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
process.wait(timeout=30)
raise RuntimeError('K compilation timed out; process tree terminated')
if code:
raise RuntimeError('K compilation failed; inspect compile.log')
manifest = json.loads((args.output / 'model.json').read_text())
if manifest.get('numerics') != 'explicit-f16-gains' or manifest.get('variant') != 'ldr' or len(manifest['layers']) != 11:
raise RuntimeError('Incorrect compiled K model contract')
for name, expected in manifest['sha256'].items():
if Path(name).name != name or sha(args.output / name) != expected:
raise RuntimeError('Compiled model product failed verification')
stamp.write_text(json.dumps(dict(identity=identity, products=products(), privateLocalModel=True,
gpuValidated=False, gameValidated=False, published=False), indent=2) + '\n')
print('K_MODEL_BUILT private=1 gpu_validated=0 products=' + str(len(products())))
if __name__ == '__main__':
main()
+109
View File
@@ -0,0 +1,109 @@
"""Capture and extract private K weights through the existing synthetic Windows probe.
Uses the audited ZLUDA layer-capture hook, not a game or DLL patch. Generated
weights/captures are private local assets and must never enter release archives.
This does not yet capture the reconstruction LUT or build a full K model.
"""
import argparse
import hashlib
import json
import os
from pathlib import Path
import subprocess
import sys
def sha(path):
with path.open('rb') as stream:
return hashlib.file_digest(stream, 'sha256').hexdigest()
def main():
repo = Path(__file__).resolve().parents[2]
parser = argparse.ArgumentParser(description=__doc__)
for name in ('probe', 'shim', 'runtime', 'core', 'dlss', 'native', 'output'):
parser.add_argument('--' + name, required=True, type=lambda p: Path(p).resolve())
parser.add_argument('--output-resolution', default='512x288', help='NGX output WxH (default 512x288)')
parser.add_argument('--input-resolution', default='256x144', help='NGX render WxH (default 256x144)')
parser.add_argument('--include-lut', action='store_true', help='requires audited 328-byte output capture support in isolated runtime')
args = parser.parse_args()
if args.output.exists() or not args.output.is_relative_to((repo / 'build').resolve()):
parser.error('use a fresh directory under ignored build/')
if sha(args.dlss) != '3975567b8943c53acce397f2b72380092f84f162d00b0d2c7d08a1025c563983':
parser.error('requires pinned SR 310.9.1')
if sha(args.core) != '66767018c36b3bab46398dade3adf173daa3730fda75965689ea848c9bc4e79b':
parser.error('requires pinned NGX core 617.14')
args.output.mkdir(parents=True)
capture = args.output / 'capture'
env = {key: value for key, value in os.environ.items()
if not key.startswith('D4R_') and key not in ('ZLUDA_LOG_DIR', 'ZLUDA_CACHE_DIR', 'ZLUDA_CUDA_LIB')}
runtime = args.runtime
nvapi = runtime / 'nvapi/gfx1101/nvapi64.dll'
if not nvapi.is_file():
nvapi = runtime / 'nvapi/nvapi64.dll'
env.update(HIP_PATH=str(runtime / 'hip'), ZLUDA_CUDA_LIB=str(runtime / 'zluda/nvcuda.dll'),
D4R_NVAPI_BACKEND=str(runtime / 'nvapi/backend/nvapi64.dll'),
D4R_FORMAT_MODULE=str(runtime / 'pixel_convert_gfx1101.hsaco'),
D4R_ZLUDA_NATIVE_DIR=str(args.native), D4R_ZLUDA_WMMA='1',
D4R_ZLUDA_WMMA_FP8='1', D4R_ZLUDA_WMMA_F16_REFERENCE='1', D4R_QUIET_API='1',
D4R_DIAG_DIR=str(args.output), D4R_NGX_DATA_DIR=str(args.output / 'ngx-data'),
D4R_CUDA_REPLAY_DUMP_DIR=str(capture), D4R_CUDA_REPLAY_DUMP_FILTER='dltss_pwin_',
D4R_CUDA_REPLAY_DUMP_LIMIT='1')
if args.include_lut:
env['D4R_CUDA_REPLAY_DUMP_FILTER'] = ''
env['PATH'] = str(runtime / 'hip/bin') + os.pathsep + str(runtime / 'zluda') + os.pathsep + env['PATH']
command = [str(args.probe), '--module', str(args.shim), '--hip-root', str(runtime / 'hip'),
'--cuda-dll', str(runtime / 'zluda/nvcuda.dll'), '--ngx-core', str(args.core),
'--dlss-dll', str(args.dlss), '--nvapi-dll', str(nvapi), '--ngx-abi', 'project',
'--interop-mode', 'command-list', '--preset', '11', '--ngx-output-resolution', args.output_resolution,
'--ngx-input-resolution', args.input_resolution, '--iterations', '1']
report = dict(passed=False, privateLocalWeights=True, gameAccessed=False, reconstructionLutCaptured=False,
artifacts={str(p):sha(p) for p in (args.probe, args.shim, args.core, args.dlss, runtime / 'zluda/nvcuda.dll')})
try:
with (args.output / 'probe.stdout.log').open('wb') as out, (args.output / 'probe.stderr.log').open('wb') as err:
result = subprocess.run(command, env=env, stdout=out, stderr=err, timeout=180)
if result.returncode or 'PASS D3D12_DLSS' not in (args.output / 'probe.stdout.log').read_text(errors='replace'):
raise RuntimeError('Synthetic capture probe failed; inspect logs')
expected = {'enc0', 'enc1', 'enc2', 'enc3', 'enc4', 'dec5', 'dec4', 'dec3', 'dec2', 'dec1', 'dec0'}
captures = list(capture.glob('replay-*-dltss_pwin_*_layer'))
found = {p.name.split('-dltss_pwin_')[1].removesuffix('_layer') for p in captures}
if found != expected or len(captures) != 11:
raise RuntimeError('Missing/duplicate audited K layer captures')
weights = args.output / 'weights'
weights.mkdir()
if args.include_lut:
outputs = list(capture.glob('replay-*-hiluma_engine_output_depthreg_mvhi_ldr_max_v2_rel'))
if len(outputs) != 1 or (outputs[0] / 'args.bin').stat().st_size != 328:
raise RuntimeError('Missing or invalid audited K output capture')
fields = [line.split() for line in (outputs[0] / 'manifest.txt').read_text().splitlines()]
pointers = [row for row in fields if row[0] == 'pointer' and row[1] == '280']
if len(pointers) != 1:
raise RuntimeError('Missing/duplicate reconstruction LUT pointer')
_, _, identifier, offset = pointers[0]
lut = (outputs[0] / ('alloc-' + identifier + '.bin')).read_bytes()
if len(lut) != 16 * 32768 or int(offset) % 32768 or not 0 <= int(offset) < len(lut) or not any(lut):
raise RuntimeError('Unexpected reconstruction-table allocation; refusing guessed extraction')
(weights / 'lut.bin').write_bytes(lut)
report.update(reconstructionLutCaptured=True, reconstructionTables=16, capturedLutIndex=int(offset) // 32768)
extraction_env = env.copy()
extraction_env['PYTHONPATH'] = os.pathsep.join(str(repo / path) for path in ('.tools/python/vendor', 'engine', 'kernels/tools'))
with (args.output / 'extract.log').open('wb') as out:
subprocess.run([sys.executable, str(repo / 'engine/extract_k.py'), str(capture), str(weights / 'k'),
'--unfold-gains'], env=extraction_env, stdout=out, stderr=subprocess.STDOUT, check=True, timeout=120)
index = json.loads((weights / 'k.json').read_text())
if set(index) != expected or any(layer['fold_gains'] for layer in index.values()):
raise RuntimeError('Incomplete/incorrect K weight extraction')
report.update(passed=True, layerCaptures=11, extractedLayers=11, numerics='explicit-f16-gains',
products={p.name:sha(p) for p in weights.iterdir() if p.is_file()},
weightBytes=(weights / 'k.bin').stat().st_size,
standaloneModelComplete=False, qualityReference=False, published=False)
print(json.dumps(report, indent=2))
except Exception as error:
report['error'] = str(error)
raise
finally:
(args.output / 'validation.json').write_text(json.dumps(report, indent=2) + '\n')
if __name__ == '__main__':
main()
+75
View File
@@ -0,0 +1,75 @@
"""Compare rr_ngx_probe outputs of the synthetic noisy scene against its noise-free answer, frame by frame.
The fixture's frames.txt first line is `size RW RH OW OH`; dynamic-extents.txt (when present) gives each frame's render
size. Per frame the reference is clean-<frame>.bin (clean.bin for fixtures from the first generator version) at output
size. When a frame's render size differs from the output size the baselines are the noisy input nearest-resampled to
output size and, if clean-render-<frame>.bin exists, the noise-free render-size image resampled the same way (the best a
render-resolution result could be).
"""
import argparse
import json
import sys
from pathlib import Path
repo = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(repo / '.tools/python/vendor'))
import numpy as np
def resampler(render_shape, shape):
if render_shape == shape:
return lambda a: a
ys = np.minimum((np.arange(shape[0]) * render_shape[0] / shape[0]).astype(int), render_shape[0] - 1)
xs = np.minimum((np.arange(shape[1]) * render_shape[1] / shape[1]).astype(int), render_shape[1] - 1)
return lambda a: a[ys][:, xs]
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--fixture', required=True, type=Path)
parser.add_argument('--output', required=True, type=Path)
parser.add_argument('--report', required=True, type=Path)
parser.add_argument('--ratio', type=float, default=0.5, help='tail RMSE must be below ratio * the input baseline')
parser.add_argument('--output-float32', action='store_true')
parser.add_argument('--switch-output', action='store_true')
args = parser.parse_args()
sizes = list(map(int, (args.fixture / 'frames.txt').read_text().split('\n')[0].split()[1:5]))
shape = (sizes[3], sizes[2], 4) # output size
n = len(list(args.fixture.glob('*-color.bin')))
extents_file = args.fixture / 'dynamic-extents.txt'
extents = [tuple(map(int, line.split())) for line in extents_file.read_text().split('\n') if line.strip()] if extents_file.exists() else None
sky = np.load(args.fixture / 'sky.npy')
body = ~sky
rows = []
for i in range(n):
fw, fh = extents[i] if extents else (sizes[0], sizes[1])
render_shape = (fh, fw, 4)
resample = resampler(render_shape, shape)
reference = args.fixture / f'clean-{i}.bin'
clean = np.fromfile(reference if reference.exists() else args.fixture / 'clean.bin', '<f2').astype('f8').reshape(shape)[..., :3]
float_output = args.output_float32 and (not args.switch_output or i % 2 == 1)
out = np.fromfile(args.output / f'{i}-out.bin', '<f4' if float_output else '<f2').astype('f8').reshape(shape)[..., :3]
noisy = resample(np.fromfile(args.fixture / f'{i}-color.bin', '<f2').astype('f8').reshape(render_shape)[..., :3])
rmse = lambda a: float(np.sqrt(np.mean((a[body] - clean[body]) ** 2)))
row = dict(frame=i, renderSize=[fw, fh], finite=bool(np.isfinite(out).all()), outputRmse=rmse(out), inputRmse=rmse(noisy),
skyMaxAbs=float(np.abs(out[sky] - clean[sky]).max()) if sky.any() else 0.0,
differsFromInput=bool(not np.array_equal(out, noisy)))
ideal = args.fixture / f'clean-render-{i}.bin'
if ideal.exists():
row['idealNearestRmse'] = rmse(resample(np.fromfile(ideal, '<f2').astype('f8').reshape(render_shape)[..., :3]))
rows.append(row)
tail = rows[-8:]
summary = dict(frames=n, creationRenderSize=sizes[:2], outputSize=sizes[2:], dynamicExtents=bool(extents),
allFinite=all(r['finite'] for r in rows), allDifferFromInput=all(r['differsFromInput'] for r in rows),
tailOutputRmse=float(np.mean([r['outputRmse'] for r in tail])), tailInputRmse=float(np.mean([r['inputRmse'] for r in tail])),
tailSkyMaxAbs=max(r['skyMaxAbs'] for r in tail), qualityReference=False, gameAccessed=False, synthetic=True)
if all('idealNearestRmse' in r for r in tail):
summary['idealNearestRmse'] = float(np.mean([r['idealNearestRmse'] for r in tail]))
summary['denoised'] = summary['tailOutputRmse'] < args.ratio * summary['tailInputRmse']
args.report.write_text(json.dumps(dict(summary=summary, frames=rows), indent=2) + '\n')
print(json.dumps(summary, indent=2))
sys.exit(0 if summary['allFinite'] and summary['allDifferFromInput'] and summary['denoised'] else 1)
if __name__ == '__main__':
main()
+51
View File
@@ -0,0 +1,51 @@
"""Inspect preserved synthetic K half-output differences without running a GPU."""
import argparse
import json
from pathlib import Path
import sys
def main():
repo = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(repo / '.tools/python/vendor'))
import numpy as np
parser = argparse.ArgumentParser(description=__doc__)
for name in ('reference', 'candidate', 'output'):
parser.add_argument('--' + name, required=True, type=lambda p: Path(p).resolve())
parser.add_argument('--width', required=True, type=int)
args = parser.parse_args()
if args.output.exists() or not args.output.is_relative_to((repo / 'build').resolve()) or args.width <= 0:
parser.error('use a fresh JSON output under build/ and a positive width')
names = sorted(p.name for p in args.reference.glob('output-*.rgba16f'))
if not names or names != sorted(p.name for p in args.candidate.glob('output-*.rgba16f')):
raise RuntimeError('Output sets differ or are empty')
rows = []
for name in names:
a = np.fromfile(args.reference / name, '<f2')
b = np.fromfile(args.candidate / name, '<f2')
if a.size != b.size or a.size % (args.width * 4):
raise RuntimeError('Invalid output dimensions')
indices = np.flatnonzero(a.view('<u2') != b.view('<u2'))
pixels = indices // 4
finite = np.isfinite(a) & np.isfinite(b)
max_difference = float(np.max(np.abs(a[finite].astype('f4') - b[finite].astype('f4')))) if finite.any() else None
rows.append(dict(frame=name, changedComponents=int(indices.size),
channels=[int(np.count_nonzero(indices % 4 == channel)) for channel in range(4)],
maxAbsoluteDifference=max_difference,
nonfiniteReference=int(np.count_nonzero(~np.isfinite(a))),
nonfiniteCandidate=int(np.count_nonzero(~np.isfinite(b))),
finite=bool(np.isfinite(a).all() and np.isfinite(b).all()),
boundsXY=[int((pixels % args.width).min()), int((pixels // args.width).min()),
int((pixels % args.width).max()), int((pixels // args.width).max())] if indices.size else None))
report = dict(exact=all(row['changedComponents'] == 0 for row in rows), rows=rows,
changedFrames=sum(row['changedComponents'] > 0 for row in rows),
changedComponents=sum(row['changedComponents'] for row in rows),
maximumAbsoluteDifference=max((row['maxAbsoluteDifference'] for row in rows if row['maxAbsoluteDifference'] is not None), default=None),
gameAccessed=False)
with args.output.open('x') as stream:
json.dump(report, stream, indent=2, allow_nan=False)
print(json.dumps({key: value for key, value in report.items() if key != 'rows'}, indent=2))
if __name__ == '__main__':
main()
+21 -2
View File
@@ -13,6 +13,15 @@ int main(int argc, char** argv) {
start();
try {
Args args(argc, argv);
unsigned subrectWidth = 0, subrectHeight = 0;
if (const char* subrect = std::getenv("D4R_PROBE_RENDER_SUBRECT")) {
char extra = 0;
if (std::sscanf(subrect, "%ux%u%c", &subrectWidth, &subrectHeight, &extra) != 2 ||
subrectWidth < 64 || subrectHeight < 64 ||
subrectWidth > (args.input_width ? args.input_width : args.output_width / 2) ||
subrectHeight > (args.input_height ? args.input_height : args.output_height / 2))
throw std::runtime_error("D4R_PROBE_RENDER_SUBRECT must be WIDTHxHEIGHT within 64x64..creation size");
}
if (args.cuda_dll.empty() || (args.interop_mode != "images" && (args.module.empty() || args.ngx_core.empty() || args.dlss_dll.empty())))
throw std::runtime_error("--module shim DLL, --cuda-dll, --ngx-core and --dlss-dll required");
const char* diagDir = std::getenv("D4R_DIAG_DIR");
@@ -243,7 +252,8 @@ int main(int argc, char** argv) {
for (const auto& pair : {std::make_pair("Width", width), std::make_pair("Height", height),
std::make_pair("OutWidth", outWidth), std::make_pair("OutHeight", outHeight),
std::make_pair("CreationNodeMask", 1u), std::make_pair("VisibilityNodeMask", 1u)}) d4r_ngx_set_uint(parameters, pair.first, pair.second);
d4r_ngx_set_int(parameters, "PerfQualityValue", 2); d4r_ngx_set_int(parameters, "DLSS.Feature.Create.Flags", args.ngx_create_flags);
d4r_ngx_set_int(parameters, "PerfQualityValue", int(args.ngx_quality)); d4r_ngx_set_int(parameters, "DLSS.Feature.Create.Flags", args.ngx_create_flags);
std::printf("D3D12_NGX_QUALITY value=%u\n", args.ngx_quality);
std::printf("D3D12_NGX_CREATE_FLAGS value=%u\n", args.ngx_create_flags);
for (const char* name : {"DLSS.Hint.Render.Preset.DLAA", "DLSS.Hint.Render.Preset.Quality", "DLSS.Hint.Render.Preset.Balanced",
"DLSS.Hint.Render.Preset.Performance", "DLSS.Hint.Render.Preset.UltraPerformance", "DLSS.Hint.Render.Preset.UltraQuality"}) d4r_ngx_set_uint(parameters, name, args.preset);
@@ -342,6 +352,7 @@ int main(int argc, char** argv) {
std::printf("D3D12_OUTPUT preset=%u frame=%u finite=1 mean=%.9g variance=%.9g frame_age=0\n", args.preset, frame, sum/count, variance);
if (variance < 1e-6) throw std::runtime_error("D3D12 output lost the synthetic pattern");
};
std::printf("D3D12_RESET every_frame=%u\n", unsigned(args.reset_each_frame));
for (unsigned frame = 0; frame < args.iterations; ++frame) {
if (burst) {
dx(device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(allocator.ReleaseAndGetAddressOf())), "Burst allocator");
@@ -349,7 +360,15 @@ int main(int argc, char** argv) {
dx(list->Close(), "Burst initial Close");
readback = make_buffer(device.Get(), textures[3].bytes, D3D12_HEAP_TYPE_READBACK, D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);
}
d4r_ngx_set_int(parameters, "Reset", frame == 0 ? 1 : 0);
d4r_ngx_set_int(parameters, "Reset", args.reset_each_frame || frame == 0 ? 1 : 0);
// Diagnostic: D4R_PROBE_RENDER_SUBRECT=WxH evaluates with that render rectangle on frames >= 1 (the
// feature keeps its creation size), to exercise dynamic-resolution Super Sampling.
const unsigned renderWidth = subrectWidth && frame >= 1 ? subrectWidth : width;
const unsigned renderHeight = subrectHeight && frame >= 1 ? subrectHeight : height;
d4r_ngx_set_uint(parameters, "DLSS.Render.Subrect.Dimensions.Width", renderWidth);
d4r_ngx_set_uint(parameters, "DLSS.Render.Subrect.Dimensions.Height", renderHeight);
std::printf("D3D12_RENDER_SUBRECT frame=%u size=%ux%u reset=%u retained_resources=1\n",
frame, renderWidth, renderHeight, unsigned(args.reset_each_frame || frame == 0));
if (commandBackend) {
dx(allocator->Reset(), "Reset(game-style allocator)"); dx(list->Reset(allocator.Get(), nullptr), "Reset(game-style list)");
if (earlySignature) {
+125 -9
View File
@@ -5,6 +5,9 @@
#if defined(D4R_NATIVE_RR_BACKEND)
#include "rr_native_backend.h"
#endif
#if defined(D4R_FSR_RR_BACKEND)
#include "fsr_rr_backend.h"
#endif
#include <dxgi.h>
#include <unordered_map>
#include <functional>
@@ -29,7 +32,7 @@ void initialize(ID3D12Device* device, unsigned long long app, const wchar_t* dat
auto candidate = std::make_shared<d4r::win::RuntimeInitialization>(device, app, data, sdk, common, project);
bool native = false;
#if defined(D4R_NATIVE_RR_BACKEND)
const char* backend = std::getenv("D4R_RR_BACKEND"); native = backend && std::string(backend) == "native";
const char* backend = std::getenv("D4R_RR_BACKEND"); native = backend && (std::string(backend) == "native" || std::string(backend) == "fsr");
#endif
// Default SR startup remains eager. Native RR owns its Vulkan execution
// independently; SR initializes only when its capabilities/feature are used.
@@ -40,20 +43,133 @@ struct Handle { unsigned Id; };
unsigned nextId = 1;
std::unordered_map<Handle*, std::shared_ptr<d4r::win::Feature>> features;
#if defined(D4R_NATIVE_RR_BACKEND)
bool rr_is_fsr() { const char* backend = std::getenv("D4R_RR_BACKEND"); return backend && std::string(backend) == "fsr"; }
// One Ray Reconstruction feature served by the configured backend: native DLSS RR (D/E models) or FSR RR.
struct RrFeature {
d4r::win::rr::Creation creation;
d4r::win::rr::Backend backend;
std::unique_ptr<d4r::win::rr::Backend> native;
#if defined(D4R_FSR_RR_BACKEND)
std::unique_ptr<d4r::win::rr::FsrBackend> fsr;
// The Super Sampling stage after FSR's denoiser: an internal feature 1 at the game's render and output sizes,
// configured like any SR feature ([DLSS] Model applies). Absent only when SR cannot start and output == render.
std::shared_ptr<d4r::win::Feature> sr;
void* srParams = nullptr;
~RrFeature() { if (srParams) d4r_ngx_parameters_destroy(srParams); }
void start_super_sampling() {
try {
ensure_sr_runtime();
void* creationParams = d4r_ngx_parameters_create();
struct Guard { void* p; ~Guard() { d4r_ngx_parameters_destroy(p); } } guard{creationParams};
d4r_ngx_set_uint(creationParams, "Width", creation.width); d4r_ngx_set_uint(creationParams, "Height", creation.height);
d4r_ngx_set_uint(creationParams, "OutWidth", creation.outWidth); d4r_ngx_set_uint(creationParams, "OutHeight", creation.outHeight);
d4r_ngx_set_int(creationParams, "PerfQualityValue", creation.quality);
d4r_ngx_set_int(creationParams, "DLSS.Feature.Create.Flags", int(creation.flags));
d4r_ngx_set_int(creationParams, "DLSS.Enable.Output.Subrects", int(creation.outputSubrects));
d4r::win::player::apply_preset(creationParams);
sr = std::make_shared<d4r::win::Feature>(runtime, creationParams);
srParams = d4r_ngx_parameters_create();
} catch (const std::exception& error) {
std::fprintf(stderr, "D4R_FSR_RR_SR_UNAVAILABLE %s\n", error.what());
sr.reset();
if (creation.outWidth != creation.width || creation.outHeight != creation.height)
throw std::runtime_error(std::string("FSR RR needs the Super Sampling stage to upscale, which is unavailable: ") + error.what());
return; // output == render: the denoised colour goes straight to the output
}
fsr->set_super_sampling([this](ID3D12CommandQueue* queue, ID3D12Resource* denoised,
const d4r::win::rr::Snapshot& frame, const std::array<d4r::win::ResourceAccess, d4r::win::rr::Snapshot::Count>& access) {
using d4r::win::rr::Snapshot;
void* sp = srParams;
d4r_ngx_set_d3d12_resource(sp, "Color", denoised);
d4r_ngx_set_d3d12_resource(sp, "Depth", frame.resources[Snapshot::Depth].Get());
d4r_ngx_set_d3d12_resource(sp, "MotionVectors", frame.resources[Snapshot::Motion].Get());
d4r_ngx_set_d3d12_resource(sp, "Output", frame.resources[Snapshot::Output].Get());
d4r_ngx_set_d3d12_resource(sp, "ExposureTexture", frame.resources[Snapshot::Exposure].Get());
const auto& settings = frame.settings;
d4r_ngx_set_float(sp, "Jitter.Offset.X", settings.jitter[0]); d4r_ngx_set_float(sp, "Jitter.Offset.Y", settings.jitter[1]);
d4r_ngx_set_float(sp, "MV.Scale.X", settings.motionScale[0]); d4r_ngx_set_float(sp, "MV.Scale.Y", settings.motionScale[1]);
d4r_ngx_set_float(sp, "Sharpness", 0.0f);
d4r_ngx_set_float(sp, "DLSS.Pre.Exposure", settings.preExposure); d4r_ngx_set_float(sp, "DLSS.Exposure.Scale", frame.exposureScale);
d4r_ngx_set_float(sp, "FrameTimeDeltaInMsec", settings.frameTimeMs);
d4r_ngx_set_int(sp, "Reset", settings.reset ? 1 : 0);
d4r_ngx_set_uint(sp, "DLSS.Render.Subrect.Dimensions.Width", frame.renderWidth);
d4r_ngx_set_uint(sp, "DLSS.Render.Subrect.Dimensions.Height", frame.renderHeight);
d4r_ngx_set_uint(sp, "DLSS.Input.Color.Subrect.Base.X", 0); d4r_ngx_set_uint(sp, "DLSS.Input.Color.Subrect.Base.Y", 0);
d4r_ngx_set_uint(sp, "DLSS.Input.Depth.Subrect.Base.X", frame.origins[Snapshot::Depth][0]);
d4r_ngx_set_uint(sp, "DLSS.Input.Depth.Subrect.Base.Y", frame.origins[Snapshot::Depth][1]);
d4r_ngx_set_uint(sp, "DLSS.Input.MV.Subrect.Base.X", frame.origins[Snapshot::Motion][0]);
d4r_ngx_set_uint(sp, "DLSS.Input.MV.Subrect.Base.Y", frame.origins[Snapshot::Motion][1]);
d4r_ngx_set_uint(sp, "DLSS.Output.Subrect.Base.X", frame.origins[Snapshot::Output][0]);
d4r_ngx_set_uint(sp, "DLSS.Output.Subrect.Base.Y", frame.origins[Snapshot::Output][1]);
// resource states, as the shim's own feature-1 evaluation passes them
struct Plane { const char* state; d4r::win::ResourceAccess access; };
d4r::win::ResourceAccess denoisedAccess(D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);
const Plane planes[] = {{"D4R.Color.State", denoisedAccess}, {"D4R.Depth.State", access[Snapshot::Depth]},
{"D4R.Motion.State", access[Snapshot::Motion]}, {"D4R.Output.State", access[Snapshot::Output]},
{"D4R.Exposure.State", access[Snapshot::Exposure]}};
for (const auto& plane : planes) {
const std::string prefix(plane.state);
d4r_ngx_set_uint(sp, plane.state, unsigned(plane.access.legacy));
d4r_ngx_set_uint(sp, (prefix + ".Enhanced").c_str(), plane.access.enhanced);
d4r_ngx_set_uint(sp, (prefix + ".Layout").c_str(), unsigned(plane.access.layout));
d4r_ngx_set_uint(sp, (prefix + ".Access").c_str(), unsigned(plane.access.access));
}
sr->evaluate_boundary(queue, sp);
}, sr->render_limits());
}
#endif
RrFeature(ID3D12Device* device, void* parameters, const std::string& directory)
: creation(parameters), backend(device, creation, directory) {}
: creation(parameters) {
#if defined(D4R_FSR_RR_BACKEND)
if (rr_is_fsr()) {
fsr = std::make_unique<d4r::win::rr::FsrBackend>(device, creation, d4r::win::rr::FsrConfig::from_environment());
start_super_sampling();
return;
}
#endif
native = std::make_unique<d4r::win::rr::Backend>(device, creation, directory);
}
void validate_frame(const d4r::win::rr::Snapshot& frame) {
#if defined(D4R_FSR_RR_BACKEND)
if (fsr) { fsr->validate_frame(frame); return; }
#endif
native->validate_frame(frame);
}
void evaluate(ID3D12CommandQueue* queue, const d4r::win::rr::Snapshot& frame,
const std::array<d4r::win::ResourceAccess, d4r::win::rr::Snapshot::Count>& access) {
#if defined(D4R_FSR_RR_BACKEND)
if (fsr) { fsr->evaluate(queue, frame, access); return; }
#endif
native->evaluate(queue, frame, access);
}
void drain() {
#if defined(D4R_FSR_RR_BACKEND)
if (fsr) { fsr->drain(); if (sr) sr->drain(); return; }
#endif
native->drain();
}
};
std::unordered_map<Handle*, std::shared_ptr<RrFeature>> rrFeatures;
std::string rr_directory() {
const char* backend = std::getenv("D4R_RR_BACKEND");
if (backend && std::string(backend) == "fsr") return {}; // FSR RR has no DLSS model directory
if (!backend || std::string(backend) != "native")
throw std::runtime_error("Native RR requires D4R_RR_BACKEND=native");
throw std::runtime_error("Native RR requires D4R_RR_BACKEND=native (or fsr)");
return d4r::win::env_path("D4R_RR_MODEL_DIR");
}
// FSR readiness is a cheap file check: building the backend loads vkd3d-proton and compiles AMD's shaders.
bool fsr_ready() {
#if defined(D4R_FSR_RR_BACKEND)
const auto config = d4r::win::rr::FsrConfig::from_environment();
auto present = [](const std::wstring& path) { return !path.empty() && GetFileAttributesW(path.c_str()) != INVALID_FILE_ATTRIBUTES; };
const bool ready = present(config.vkd3dCore) && present(config.denoiserDll) && present(config.amdxcShim);
if (!ready) std::fprintf(stderr, "D4R_RR_UNAVAILABLE FSR RR needs D4R_FSR_VKD3D_CORE, D4R_FSR_RR_DLL and D4R_FSR_AMDXC_SHIM to exist\n");
return ready;
#else
return false;
#endif
}
bool rr_ready(ID3D12Device* device) {
if (rr_is_fsr()) return fsr_ready();
try {
const auto directory = rr_directory();
void* p = d4r_ngx_parameters_create();
@@ -270,7 +386,7 @@ API unsigned d4r_D3D12_EvaluateAtBoundary(ID3D12CommandQueue* queue, Handle* han
auto it = rrFeatures.find(handle); if (it != rrFeatures.end()) rr = it->second; }
if (rr) return call([&] {
RrSnapshot snapshot(rr->creation, p, nullptr); snapshot.resolve();
rr->backend.evaluate(queue, snapshot.frame, snapshot.access); return 1u;
rr->evaluate(queue, snapshot.frame, snapshot.access); return 1u;
});
#endif
std::shared_ptr<d4r::win::Feature> feature;
@@ -288,9 +404,9 @@ API unsigned NVSDK_NGX_D3D12_EvaluateFeature(ID3D12GraphicsCommandList* list, co
auto snapshot = std::make_shared<RrSnapshot>(rr->creation, p, list);
// Reject unsupported contracts before splitting the logical command
// list. Submission still rechecks resources and resolved access.
rr->backend.validate_frame(snapshot->frame);
rr->validate_frame(snapshot->frame);
d4r::win::commands::record_boundary(list, [rr, snapshot](ID3D12CommandQueue* queue) {
snapshot->resolve(); rr->backend.evaluate(queue, snapshot->frame, snapshot->access);
snapshot->resolve(); rr->evaluate(queue, snapshot->frame, snapshot->access);
});
return 1u;
});
@@ -361,7 +477,7 @@ API unsigned NVSDK_NGX_D3D12_ReleaseFeature(Handle* handle) {
{ std::lock_guard<std::mutex> lock(apiMutex);
auto it = rrFeatures.find(handle);
if (it != rrFeatures.end()) { rr = std::move(it->second); rrFeatures.erase(it); delete handle; } }
if (rr) return call([&] { rr->backend.drain(); rr.reset(); return 1u; });
if (rr) return call([&] { rr->drain(); rr.reset(); return 1u; });
#endif
std::shared_ptr<d4r::win::Feature> feature;
{ std::lock_guard<std::mutex> lock(apiMutex);
@@ -376,7 +492,7 @@ API unsigned NVSDK_NGX_D3D12_Shutdown() {
return call([&] {
if (runtime && std::getenv("D4R_ASYNC_INTEROP")) runtime->drain_async_queue();
#if defined(D4R_NATIVE_RR_BACKEND)
for (auto& pair : rrFeatures) pair.second->backend.drain();
for (auto& pair : rrFeatures) pair.second->drain();
for (auto& pair : rrFeatures) delete pair.first;
rrFeatures.clear();
#endif
+9
View File
@@ -120,6 +120,7 @@ struct Args {
std::string barrier_mode = "legacy";
bool early_indirect = false;
bool player_mode = false;
bool reset_each_frame = false;
std::string ngx_core, dlss_dll;
std::string nvapi_dll;
std::string ngx_abi = "driver";
@@ -131,6 +132,7 @@ struct Args {
std::string ngx_mode = "init";
unsigned preset = 11;
unsigned ngx_create_flags = 0;
unsigned ngx_quality = 2;
unsigned output_width = 512, output_height = 288;
unsigned input_width = 0, input_height = 0;
std::string fixture_dir;
@@ -162,6 +164,7 @@ struct Args {
}
else if (key == "--early-indirect") early_indirect = value == "1";
else if (key == "--player-mode") player_mode = value == "1";
else if (key == "--reset-every-frame") reset_each_frame = value == "1";
else if (key == "--ngx-core") ngx_core = value;
else if (key == "--dlss-dll") dlss_dll = value;
else if (key == "--nvapi-dll") nvapi_dll = value;
@@ -190,6 +193,12 @@ struct Args {
if (end != value.size() || (ngx_create_flags != 0 && ngx_create_flags != 11))
throw std::runtime_error("ngx-create-flags must be 0 or 11 (HDR, low-resolution MV, inverted depth)");
}
else if (key == "--ngx-quality") {
size_t end = 0;
const auto parsed = std::stoul(value, &end);
if (end != value.size() || parsed > 5) throw std::runtime_error("ngx-quality must be 0..5");
ngx_quality = static_cast<unsigned>(parsed);
}
else if (key == "--ngx-output-resolution") {
const auto separator = value.find('x');
if (separator == std::string::npos) throw std::runtime_error("Output resolution must be WIDTHxHEIGHT");
+55
View File
@@ -0,0 +1,55 @@
#version 450
// FSR Ray Regeneration backend, stage 3: the denoised diffuse and specular signals remodulated by
// their albedos, plus the residual the denoiser never saw (particles, what demodulation could not
// carry, sky). The result is render-resolution colour for the Super Sampling stage.
layout(local_size_x = 8, local_size_y = 8) in;
layout(binding = 0, rgba16f) uniform readonly image2D denoisedDiffuse;
layout(binding = 1, rgba16f) uniform readonly image2D denoisedSpecular;
layout(binding = 2, rgba16f) uniform readonly image2D diffuseAlbedo;
layout(binding = 3, rgba16f) uniform readonly image2D specularAlbedo;
layout(binding = 4, rgba16f) uniform readonly image2D residual;
layout(binding = 5, rgba16f) uniform writeonly image2D composed;
layout(binding = 6, rgba16f) uniform readonly image2D noisyDiffuse;
layout(binding = 7, rgba16f) uniform readonly image2D noisySpecular;
layout(push_constant) uniform Constants
{
uvec2 renderSize;
float haloLimit; // > 0: each denoised lobe at most this multiple of the brightest noisy input of that lobe around it
uint unused;
} pc;
void main()
{
const ivec2 px = ivec2(gl_GlobalInvocationID.xy);
if (px.x >= int(pc.renderSize.x) || px.y >= int(pc.renderSize.y))
return;
const vec3 da = imageLoad(diffuseAlbedo, px).rgb, sa = imageLoad(specularAlbedo, px).rgb;
vec3 diffuse = imageLoad(denoisedDiffuse, px).rgb * da;
vec3 specular = imageLoad(denoisedSpecular, px).rgb * sa;
if (pc.haloLimit > 0.0)
{
// The denoiser can carry a thin object's large demodulated signal (dark albedo) across its edge, where this
// texel's albedo turns it into a bright fringe. A denoised value above everything around it in the noisy
// input is such a leak: capped, by luminance (the hue is kept).
const vec3 w = vec3(0.2126, 0.7152, 0.0722);
float diffuseMax = 0.0, specularMax = 0.0;
for (int dy = -1; dy <= 1; ++dy)
for (int dx = -1; dx <= 1; ++dx)
{
const ivec2 q = clamp(px + ivec2(dx, dy), ivec2(0), ivec2(pc.renderSize) - 1);
// what the lobe contributes there (the neighbour's own albedo): a leaked demodulated signal
// remodulated by this texel's albedo exceeds all of these
diffuseMax = max(diffuseMax, dot(imageLoad(noisyDiffuse, q).rgb * imageLoad(diffuseAlbedo, q).rgb, w));
specularMax = max(specularMax, dot(imageLoad(noisySpecular, q).rgb * imageLoad(specularAlbedo, q).rgb, w));
}
const float dl = dot(diffuse, w), sl = dot(specular, w);
if (dl > pc.haloLimit * diffuseMax)
diffuse *= pc.haloLimit * diffuseMax / dl;
if (sl > pc.haloLimit * specularMax)
specular *= pc.haloLimit * specularMax / sl;
}
const vec3 color = diffuse + specular + imageLoad(residual, px).rgb;
imageStore(composed, px, vec4(clamp(color, 0.0, 65000.0), 1.0));
}
+194
View File
@@ -0,0 +1,194 @@
#version 450
// FSR Ray Regeneration backend, stage 1: a game's DLSS Ray Reconstruction inputs -> the inputs of
// AMD's FidelityFX denoiser 1.2 (ffx_denoiser.h), at render resolution.
//
// The split of the game's single noisy colour into a diffuse and a specular signal follows the
// DLSS-RR -> FSR-RR adapter of Zach Hembree's OptiScaler branch ffx-denoise-experimental (GPL-3.0,
// 18ac686, FSRDInputConv.hlsl, "Mode 2") and burak113's continuation (5a6adb1): each lobe gets the
// colour in proportion to its albedo and is demodulated by it. That is an approximation - one
// colour cannot be split exactly - and what does not survive demodulation goes to the residual,
// which bypasses the denoiser. Unlike that reference this writes FSR-RR 1.2's contract: signed
// linear depth reconstructed from hardware or linear depth, motion as previous-minus-current UV,
// linear (non-gamma) albedo and the colour before particles as the denoised base.
layout(local_size_x = 8, local_size_y = 8) in;
layout(binding = 0) uniform sampler2D inColor;
layout(binding = 1) uniform sampler2D inDepth;
layout(binding = 2) uniform sampler2D inMotion;
layout(binding = 3) uniform sampler2D inNormals;
layout(binding = 4) uniform sampler2D inRoughness; // the normals again when roughness is packed
layout(binding = 5) uniform sampler2D inDiffuseAlbedo;
layout(binding = 6) uniform sampler2D inSpecularAlbedo;
layout(binding = 7) uniform sampler2D inSpecularHit; // bound to the normals when absent
layout(binding = 8) uniform sampler2D inDiffuseHit; // bound to the normals when absent
layout(binding = 9) uniform sampler2D inBeforeParticles; // bound to the colour when absent
layout(binding = 10, r32f) uniform writeonly image2D outLinearDepth;
layout(binding = 11, rgba16f) uniform writeonly image2D outMotion;
layout(binding = 12, rgba16f) uniform writeonly image2D outNormals;
layout(binding = 13, rgba16f) uniform writeonly image2D outDiffuseAlbedo;
layout(binding = 14, rgba16f) uniform writeonly image2D outSpecularAlbedo;
layout(binding = 15, rgba16f) uniform writeonly image2D outDiffuse;
layout(binding = 16, rgba16f) uniform writeonly image2D outSpecular;
layout(binding = 17, rgba16f) uniform writeonly image2D outResidual;
// Matrices are uploaded as the row-major, row-vector (v' = vM) float[16] NGX and FFX use. Read as a
// GLSL (column-major) mat4 that is the transpose, so M * v here is the row-vector product v * M.
layout(push_constant) uniform Constants
{
mat4 clipToView; // inverse(ViewToClipMatrix)
mat4 viewToPreviousView; // inverse(WorldToView) * previous WorldToView
vec4 renderSize; // width, height, 1 / width, 1 / height
vec4 motion; // xy: MV texel -> UV delta (MV.Scale / MV resolution); zw: MV texel per render pixel
uvec4 colorDepthBase; // colour subrect origin xy, depth subrect origin xy
uvec4 motionBaseFlags; // motion subrect origin xy, flags, unused
vec4 depthParams; // x: sign of view z in front of the camera, y: sky depth (hardware), z: far marker
vec4 detail; // x: share of the colour that bypasses the denoiser (0: none); y: smallest albedo a
// signal is divided by
} pc;
const uint kHardwareDepth = 1u;
const uint kRoughnessPacked = 2u;
const uint kSpecularHit = 4u;
const uint kSpecularHitInW = 8u; // DLSSD.SpecularRayDirectionHitDistance: the distance is .w
const uint kDiffuseHit = 16u;
const uint kDiffuseHitInW = 32u;
const uint kNormalsUnorm = 64u; // [0,1]-encoded normals (UNORM formats)
const uint kBeforeParticles = 128u;
const uint kInvalidNormalPassthrough = 256u;
const float kHalfMax = 65000.0;
vec3 safe(vec3 v)
{
// NaN fails both comparisons and becomes 0
return vec3(v.x >= 0.0 ? min(v.x, kHalfMax) : 0.0, v.y >= 0.0 ? min(v.y, kHalfMax) : 0.0,
v.z >= 0.0 ? min(v.z, kHalfMax) : 0.0);
}
// AMD's NormalToOctahedronUv (FidelityFX denoising.md 5.3.1)
vec2 octahedron(vec3 n)
{
n /= abs(n.x) + abs(n.y) + abs(n.z);
vec2 k = vec2(n.x >= 0.0 ? 1.0 : -1.0, n.y >= 0.0 ? 1.0 : -1.0);
vec2 xy = n.z < 0.0 ? (1.0 - abs(n.yx)) * k : n.xy;
return xy * 0.5 + 0.5;
}
void main()
{
const ivec2 px = ivec2(gl_GlobalInvocationID.xy);
if (px.x >= int(pc.renderSize.x) || px.y >= int(pc.renderSize.y))
return;
const uint flags = pc.motionBaseFlags.z;
const vec2 uv = (vec2(px) + 0.5) * pc.renderSize.zw;
const vec2 ndc = vec2(uv.x * 2.0 - 1.0, 1.0 - uv.y * 2.0);
// Signed view-space position. Hardware depth goes through the inverse projection, which carries
// the game's handedness and (reversed) depth range; linear depth is the distance along the view
// axis, given its sign by the projection.
const float depth = texelFetch(inDepth, ivec2(pc.colorDepthBase.zw) + px, 0).r;
vec3 viewPos;
bool sky;
if ((flags & kHardwareDepth) != 0u)
{
const vec4 p = pc.clipToView * vec4(ndc, depth, 1.0);
viewPos = p.xyz / p.w;
sky = depth == pc.depthParams.y || !(abs(p.w) > 0.0);
}
else
{
const vec4 p = pc.clipToView * vec4(ndc, 0.5, 1.0);
const vec3 ray = p.xyz / p.w;
const float z = depth * pc.depthParams.x;
viewPos = ray * (z / ray.z);
sky = !(depth > 0.0) || depth >= pc.depthParams.z;
}
const vec3 color = safe(texelFetch(inColor, ivec2(pc.colorDepthBase.xy) + px, 0).rgb);
const vec3 base = (flags & kBeforeParticles) != 0u ? safe(texelFetch(inBeforeParticles, px, 0).rgb) : color;
// Particles (colour after minus before) stay out of the denoiser; signed, so smoke can darken.
const vec3 particles = color - base;
vec3 diffuseAlbedo = clamp(texelFetch(inDiffuseAlbedo, px, 0).rgb, 0.0, 1.0);
vec3 specularAlbedo = clamp(texelFetch(inSpecularAlbedo, px, 0).rgb, 0.0, 1.0);
// Energy above 1 is taken from the diffuse lobe first (reference: albedo overshoot clamp).
diffuseAlbedo = max(diffuseAlbedo - max(diffuseAlbedo + specularAlbedo - 1.0, 0.0), 0.0);
const float albedoSum = dot(diffuseAlbedo + specularAlbedo, vec3(1.0));
sky = sky || !(albedoSum > 3e-3) || any(isnan(viewPos)) || any(isinf(viewPos));
const vec4 normalTexel = texelFetch(inNormals, px, 0);
vec3 normal = (flags & kNormalsUnorm) != 0u ? normalTexel.xyz * 2.0 - 1.0 : normalTexel.xyz;
const float normalLength = dot(normal, normal);
// A zero/invalid normal is no surface, not an invented world-Z-facing mirror. Alan Wake 2
// supplies zero normals for its sky with finite FP16-max depth, so the far bound alone misses it.
if ((flags & kInvalidNormalPassthrough) != 0u)
sky = sky || !(normalLength > 1e-8) || isnan(normalLength) || isinf(normalLength);
if (sky)
{
// Outside linearDepthBounds: the denoiser passes these texels through, and the residual carries
// the whole colour.
imageStore(outLinearDepth, px, vec4(pc.depthParams.z * 10.0 * pc.depthParams.x));
imageStore(outMotion, px, vec4(0.0));
imageStore(outNormals, px, vec4(0.5, 0.5, 1.0, 0.0));
imageStore(outDiffuseAlbedo, px, vec4(0.0));
imageStore(outSpecularAlbedo, px, vec4(0.0));
imageStore(outDiffuse, px, vec4(0.0));
imageStore(outSpecular, px, vec4(0.0));
imageStore(outResidual, px, vec4(color, 0.0));
return;
}
imageStore(outLinearDepth, px, vec4(viewPos.z));
// 2.5D motion: DLSS motion vectors point from this pixel to where it was (previous - current), in
// the MV texture's pixels. The depth delta is the camera's: the previous view's z of this point
// (moving geometry is not reprojected; there is no previous depth for it).
const ivec2 mvTexel = ivec2(pc.motionBaseFlags.xy) + ivec2(vec2(px) * pc.motion.zw);
const vec2 mv = texelFetch(inMotion, mvTexel, 0).xy * pc.motion.xy;
const vec4 previous = pc.viewToPreviousView * vec4(viewPos, 1.0);
imageStore(outMotion, px, vec4(mv, previous.z / previous.w - viewPos.z, 0.0));
normal = dot(normal, normal) > 1e-8 ? normalize(normal) : vec3(0.0, 0.0, 1.0);
const float roughness = clamp((flags & kRoughnessPacked) != 0u ? normalTexel.w : texelFetch(inRoughness, px, 0).r,
0.0, 1.0);
// material type 0 (the default material) everywhere
imageStore(outNormals, px, vec4(octahedron(normal), roughness, 0.0));
imageStore(outDiffuseAlbedo, px, vec4(diffuseAlbedo, 0.0));
imageStore(outSpecularAlbedo, px, vec4(specularAlbedo, 0.0));
// Split by albedo and demodulate (reference "Mode 2"). Per channel, so a tinted metal keeps its tint.
const vec3 total = diffuseAlbedo + specularAlbedo;
const vec3 specularShare = specularAlbedo / max(total, vec3(1e-4));
const vec3 specularColor = base * specularShare;
const vec3 diffuseColor = base - specularColor;
// A dark albedo would turn light it did not shape (fog, sky light, the background at a thin object's edge) into
// a huge signal that the denoiser clips (fog lost) or spreads (bright fringes): no albedo below the floor
// divides a signal, and what the division cannot carry stays in the residual.
vec3 diffuse = safe(diffuseColor / max(diffuseAlbedo, vec3(pc.detail.y)));
vec3 specular = safe(specularColor / max(specularAlbedo, vec3(pc.detail.y)));
if (pc.detail.x > 0.0)
{
// Fine detail the denoiser treats as noise (hair, water ripples): part of the colour goes around it,
// through the residual.
const float denoised = 1.0 - clamp(pc.detail.x, 0.0, 1.0);
diffuse *= denoised;
specular *= denoised;
}
const vec3 remodulated = diffuse * diffuseAlbedo + specular * specularAlbedo;
// A: the ray hit distance for an indirect signal (ignored, but non-negative, for a direct one)
float specularHit = 0.0, diffuseHit = 0.0;
if ((flags & kSpecularHit) != 0u)
{
const vec4 hit = texelFetch(inSpecularHit, px, 0);
specularHit = clamp((flags & kSpecularHitInW) != 0u ? hit.w : hit.x, 0.0, kHalfMax);
}
if ((flags & kDiffuseHit) != 0u)
{
const vec4 hit = texelFetch(inDiffuseHit, px, 0);
diffuseHit = clamp((flags & kDiffuseHitInW) != 0u ? hit.w : hit.x, 0.0, kHalfMax);
}
imageStore(outDiffuse, px, vec4(diffuse, diffuseHit));
imageStore(outSpecular, px, vec4(specular, specularHit));
imageStore(outResidual, px, vec4(base - remodulated + particles, 0.0));
}
+729
View File
@@ -0,0 +1,729 @@
#pragma once
#include <cstdint>
// Generated by scripts/windows/compile-fsr-rr-shaders.py; do not edit.
static const uint32_t d4r_vk_fsr_rr_prepare[] = {
0x07230203,0x00010000,0x0008000b,0x000002d8,0x00000000,0x00020011,0x00000001,0x0006000b,
0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e,0x00000000,0x00000001,
0x0006000f,0x00000005,0x00000004,0x6e69616d,0x00000000,0x00000078,0x00060010,0x00000004,
0x00000011,0x00000008,0x00000008,0x00000001,0x00030003,0x00000002,0x000001c2,0x00040005,
0x00000004,0x6e69616d,0x00000000,0x00050005,0x0000000b,0x65666173,0x33667628,0x0000003b,
0x00030005,0x0000000a,0x00000076,0x00060005,0x00000010,0x6174636f,0x72646568,0x76286e6f,
0x003b3366,0x00030005,0x0000000f,0x0000006e,0x00030005,0x0000004d,0x0000006b,0x00030005,
0x00000059,0x00007978,0x00030005,0x00000075,0x00007870,0x00080005,0x00000078,0x475f6c67,
0x61626f6c,0x766e496c,0x7461636f,0x496e6f69,0x00000044,0x00050005,0x00000083,0x736e6f43,
0x746e6174,0x00000073,0x00060006,0x00000083,0x00000000,0x70696c63,0x69566f54,0x00007765,
0x00080006,0x00000083,0x00000001,0x77656976,0x72506f54,0x6f697665,0x69567375,0x00007765,
0x00060006,0x00000083,0x00000002,0x646e6572,0x69537265,0x0000657a,0x00050006,0x00000083,
0x00000003,0x69746f6d,0x00006e6f,0x00070006,0x00000083,0x00000004,0x6f6c6f63,0x70654472,
0x61426874,0x00006573,0x00070006,0x00000083,0x00000005,0x69746f6d,0x61426e6f,0x6c466573,
0x00736761,0x00060006,0x00000083,0x00000006,0x74706564,0x72615068,0x00736d61,0x00050006,
0x00000083,0x00000007,0x61746564,0x00006c69,0x00030005,0x00000085,0x00006370,0x00040005,
0x0000009a,0x67616c66,0x00000073,0x00030005,0x0000009f,0x00007675,0x00030005,0x000000a9,
0x0063646e,0x00040005,0x000000b4,0x74706564,0x00000068,0x00040005,0x000000b8,0x65446e69,
0x00687470,0x00030005,0x000000cc,0x00000070,0x00040005,0x000000d6,0x77656976,0x00736f50,
0x00030005,0x000000df,0x00796b73,0x00030005,0x000000ef,0x00000070,0x00030005,0x000000f7,
0x00796172,0x00030005,0x000000fe,0x0000007a,0x00040005,0x00000114,0x6f6c6f63,0x00000072,
0x00040005,0x00000115,0x6f436e69,0x00726f6c,0x00040005,0x0000011f,0x61726170,0x0000006d,
0x00040005,0x00000122,0x65736162,0x00000000,0x00070005,0x0000012a,0x65426e69,0x65726f66,
0x74726150,0x656c6369,0x00000073,0x00040005,0x0000012f,0x61726170,0x0000006d,0x00050005,
0x00000135,0x74726170,0x656c6369,0x00000073,0x00060005,0x00000139,0x66666964,0x41657375,
0x6465626c,0x0000006f,0x00060005,0x0000013a,0x69446e69,0x73756666,0x626c4165,0x006f6465,
0x00060005,0x00000143,0x63657073,0x72616c75,0x65626c41,0x00006f64,0x00070005,0x00000144,
0x70536e69,0x6c756365,0x6c417261,0x6f646562,0x00000000,0x00050005,0x00000158,0x65626c61,
0x75536f64,0x0000006d,0x00050005,0x00000176,0x6d726f6e,0x65546c61,0x006c6578,0x00050005,
0x00000177,0x6f4e6e69,0x6c616d72,0x00000073,0x00040005,0x0000017c,0x6d726f6e,0x00006c61,
0x00060005,0x0000018d,0x6d726f6e,0x654c6c61,0x6874676e,0x00000000,0x00060005,0x000001b1,
0x4c74756f,0x61656e69,0x70654472,0x00006874,0x00050005,0x000001be,0x4d74756f,0x6f69746f,
0x0000006e,0x00050005,0x000001c2,0x4e74756f,0x616d726f,0x0000736c,0x00070005,0x000001c6,
0x4474756f,0x75666669,0x6c416573,0x6f646562,0x00000000,0x00070005,0x000001c9,0x5374756f,
0x75636570,0x4172616c,0x6465626c,0x0000006f,0x00050005,0x000001cc,0x4474756f,0x75666669,
0x00006573,0x00050005,0x000001cf,0x5374756f,0x75636570,0x0072616c,0x00050005,0x000001d2,
0x5274756f,0x64697365,0x006c6175,0x00040005,0x000001e0,0x6554766d,0x006c6578,0x00030005,
0x000001ee,0x0000766d,0x00050005,0x000001ef,0x6f4d6e69,0x6e6f6974,0x00000000,0x00050005,
0x000001f9,0x76657270,0x73756f69,0x00000000,0x00050005,0x0000021d,0x67756f72,0x73656e68,
0x00000073,0x00050005,0x00000227,0x6f526e69,0x6e686775,0x00737365,0x00040005,0x00000231,
0x61726170,0x0000006d,0x00040005,0x00000246,0x61746f74,0x0000006c,0x00060005,0x0000024a,
0x63657073,0x72616c75,0x72616853,0x00000065,0x00060005,0x00000251,0x63657073,0x72616c75,
0x6f6c6f43,0x00000072,0x00060005,0x00000255,0x66666964,0x43657375,0x726f6c6f,0x00000000,
0x00040005,0x00000259,0x66666964,0x00657375,0x00040005,0x00000262,0x61726170,0x0000006d,
0x00050005,0x00000264,0x63657073,0x72616c75,0x00000000,0x00040005,0x0000026c,0x61726170,
0x0000006d,0x00050005,0x00000273,0x6f6e6564,0x64657369,0x00000000,0x00050005,0x0000027e,
0x6f6d6572,0x616c7564,0x00646574,0x00050005,0x00000286,0x63657073,0x72616c75,0x00746948,
0x00050005,0x00000287,0x66666964,0x48657375,0x00007469,0x00030005,0x0000028e,0x00746968,
0x00060005,0x0000028f,0x70536e69,0x6c756365,0x69487261,0x00000074,0x00030005,0x000002a8,
0x00746968,0x00060005,0x000002a9,0x69446e69,0x73756666,0x74694865,0x00000000,0x00040047,
0x00000078,0x0000000b,0x0000001c,0x00030047,0x00000083,0x00000002,0x00040048,0x00000083,
0x00000000,0x00000005,0x00050048,0x00000083,0x00000000,0x00000007,0x00000010,0x00050048,
0x00000083,0x00000000,0x00000023,0x00000000,0x00040048,0x00000083,0x00000001,0x00000005,
0x00050048,0x00000083,0x00000001,0x00000007,0x00000010,0x00050048,0x00000083,0x00000001,
0x00000023,0x00000040,0x00050048,0x00000083,0x00000002,0x00000023,0x00000080,0x00050048,
0x00000083,0x00000003,0x00000023,0x00000090,0x00050048,0x00000083,0x00000004,0x00000023,
0x000000a0,0x00050048,0x00000083,0x00000005,0x00000023,0x000000b0,0x00050048,0x00000083,
0x00000006,0x00000023,0x000000c0,0x00050048,0x00000083,0x00000007,0x00000023,0x000000d0,
0x00040047,0x000000b8,0x00000021,0x00000001,0x00040047,0x000000b8,0x00000022,0x00000000,
0x00040047,0x00000115,0x00000021,0x00000000,0x00040047,0x00000115,0x00000022,0x00000000,
0x00040047,0x0000012a,0x00000021,0x00000009,0x00040047,0x0000012a,0x00000022,0x00000000,
0x00040047,0x0000013a,0x00000021,0x00000005,0x00040047,0x0000013a,0x00000022,0x00000000,
0x00040047,0x00000144,0x00000021,0x00000006,0x00040047,0x00000144,0x00000022,0x00000000,
0x00040047,0x00000177,0x00000021,0x00000003,0x00040047,0x00000177,0x00000022,0x00000000,
0x00030047,0x000001b1,0x00000019,0x00040047,0x000001b1,0x00000021,0x0000000a,0x00040047,
0x000001b1,0x00000022,0x00000000,0x00030047,0x000001be,0x00000019,0x00040047,0x000001be,
0x00000021,0x0000000b,0x00040047,0x000001be,0x00000022,0x00000000,0x00030047,0x000001c2,
0x00000019,0x00040047,0x000001c2,0x00000021,0x0000000c,0x00040047,0x000001c2,0x00000022,
0x00000000,0x00030047,0x000001c6,0x00000019,0x00040047,0x000001c6,0x00000021,0x0000000d,
0x00040047,0x000001c6,0x00000022,0x00000000,0x00030047,0x000001c9,0x00000019,0x00040047,
0x000001c9,0x00000021,0x0000000e,0x00040047,0x000001c9,0x00000022,0x00000000,0x00030047,
0x000001cc,0x00000019,0x00040047,0x000001cc,0x00000021,0x0000000f,0x00040047,0x000001cc,
0x00000022,0x00000000,0x00030047,0x000001cf,0x00000019,0x00040047,0x000001cf,0x00000021,
0x00000010,0x00040047,0x000001cf,0x00000022,0x00000000,0x00030047,0x000001d2,0x00000019,
0x00040047,0x000001d2,0x00000021,0x00000011,0x00040047,0x000001d2,0x00000022,0x00000000,
0x00040047,0x000001ef,0x00000021,0x00000002,0x00040047,0x000001ef,0x00000022,0x00000000,
0x00040047,0x00000227,0x00000021,0x00000004,0x00040047,0x00000227,0x00000022,0x00000000,
0x00040047,0x0000028f,0x00000021,0x00000007,0x00040047,0x0000028f,0x00000022,0x00000000,
0x00040047,0x000002a9,0x00000021,0x00000008,0x00040047,0x000002a9,0x00000022,0x00000000,
0x00040047,0x000002d7,0x0000000b,0x00000019,0x00020013,0x00000002,0x00030021,0x00000003,
0x00000002,0x00030016,0x00000006,0x00000020,0x00040017,0x00000007,0x00000006,0x00000003,
0x00040020,0x00000008,0x00000007,0x00000007,0x00040021,0x00000009,0x00000007,0x00000008,
0x00040017,0x0000000d,0x00000006,0x00000002,0x00040021,0x0000000e,0x0000000d,0x00000008,
0x00040015,0x00000012,0x00000020,0x00000000,0x0004002b,0x00000012,0x00000013,0x00000000,
0x00040020,0x00000014,0x00000007,0x00000006,0x0004002b,0x00000006,0x00000017,0x00000000,
0x00020014,0x00000018,0x0004002b,0x00000006,0x0000001f,0x477de800,0x0004002b,0x00000012,
0x00000023,0x00000001,0x0004002b,0x00000012,0x0000002f,0x00000002,0x00040020,0x0000004c,
0x00000007,0x0000000d,0x0004002b,0x00000006,0x00000051,0x3f800000,0x0004002b,0x00000006,
0x00000052,0xbf800000,0x0004002b,0x00000006,0x0000006c,0x3f000000,0x00040015,0x00000072,
0x00000020,0x00000001,0x00040017,0x00000073,0x00000072,0x00000002,0x00040020,0x00000074,
0x00000007,0x00000073,0x00040017,0x00000076,0x00000012,0x00000003,0x00040020,0x00000077,
0x00000001,0x00000076,0x0004003b,0x00000077,0x00000078,0x00000001,0x00040017,0x00000079,
0x00000012,0x00000002,0x00040020,0x0000007d,0x00000007,0x00000072,0x00040017,0x00000080,
0x00000006,0x00000004,0x00040018,0x00000081,0x00000080,0x00000004,0x00040017,0x00000082,
0x00000012,0x00000004,0x000a001e,0x00000083,0x00000081,0x00000081,0x00000080,0x00000080,
0x00000082,0x00000082,0x00000080,0x00000080,0x00040020,0x00000084,0x00000009,0x00000083,
0x0004003b,0x00000084,0x00000085,0x00000009,0x0004002b,0x00000072,0x00000086,0x00000002,
0x00040020,0x00000087,0x00000009,0x00000006,0x00040020,0x00000099,0x00000007,0x00000012,
0x0004002b,0x00000072,0x0000009b,0x00000005,0x00040020,0x0000009c,0x00000009,0x00000012,
0x00040020,0x000000a4,0x00000009,0x00000080,0x0004002b,0x00000006,0x000000ac,0x40000000,
0x00090019,0x000000b5,0x00000006,0x00000001,0x00000000,0x00000000,0x00000000,0x00000001,
0x00000000,0x0003001b,0x000000b6,0x000000b5,0x00040020,0x000000b7,0x00000000,0x000000b6,
0x0004003b,0x000000b7,0x000000b8,0x00000000,0x0004002b,0x00000072,0x000000ba,0x00000004,
0x00040020,0x000000bb,0x00000009,0x00000082,0x0004002b,0x00000072,0x000000c2,0x00000000,
0x00040020,0x000000cb,0x00000007,0x00000080,0x00040020,0x000000cd,0x00000009,0x00000081,
0x0004002b,0x00000012,0x000000d9,0x00000003,0x00040020,0x000000de,0x00000007,0x00000018,
0x0004002b,0x00000072,0x000000e1,0x00000006,0x0004003b,0x000000b7,0x00000115,0x00000000,
0x0004002b,0x00000012,0x00000124,0x00000080,0x0004003b,0x000000b7,0x0000012a,0x00000000,
0x0004003b,0x000000b7,0x0000013a,0x00000000,0x0004003b,0x000000b7,0x00000144,0x00000000,
0x0006002c,0x00000007,0x0000015c,0x00000051,0x00000051,0x00000051,0x0004002b,0x00000006,
0x00000163,0x3b449ba6,0x00040017,0x0000016b,0x00000018,0x00000003,0x0004003b,0x000000b7,
0x00000177,0x00000000,0x0004002b,0x00000012,0x0000017e,0x00000040,0x0004002b,0x00000012,
0x00000192,0x00000100,0x0004002b,0x00000006,0x0000019c,0x322bcc77,0x00090019,0x000001af,
0x00000006,0x00000001,0x00000000,0x00000000,0x00000000,0x00000002,0x00000003,0x00040020,
0x000001b0,0x00000000,0x000001af,0x0004003b,0x000001b0,0x000001b1,0x00000000,0x0004002b,
0x00000006,0x000001b6,0x41200000,0x00090019,0x000001bc,0x00000006,0x00000001,0x00000000,
0x00000000,0x00000000,0x00000002,0x00000002,0x00040020,0x000001bd,0x00000000,0x000001bc,
0x0004003b,0x000001bd,0x000001be,0x00000000,0x0007002c,0x00000080,0x000001c1,0x00000017,
0x00000017,0x00000017,0x00000017,0x0004003b,0x000001bd,0x000001c2,0x00000000,0x0007002c,
0x00000080,0x000001c5,0x0000006c,0x0000006c,0x00000051,0x00000017,0x0004003b,0x000001bd,
0x000001c6,0x00000000,0x0004003b,0x000001bd,0x000001c9,0x00000000,0x0004003b,0x000001bd,
0x000001cc,0x00000000,0x0004003b,0x000001bd,0x000001cf,0x00000000,0x0004003b,0x000001bd,
0x000001d2,0x00000000,0x0004002b,0x00000072,0x000001e7,0x00000003,0x0004003b,0x000000b7,
0x000001ef,0x00000000,0x0004002b,0x00000072,0x000001fa,0x00000001,0x0006002c,0x00000007,
0x0000021b,0x00000017,0x00000017,0x00000051,0x0004003b,0x000000b7,0x00000227,0x00000000,
0x0004002b,0x00000006,0x0000024d,0x38d1b717,0x0006002c,0x00000007,0x0000024e,0x0000024d,
0x0000024d,0x0000024d,0x0004002b,0x00000072,0x0000025c,0x00000007,0x0004002b,0x00000012,
0x00000289,0x00000004,0x0004003b,0x000000b7,0x0000028f,0x00000000,0x0004002b,0x00000012,
0x00000295,0x00000008,0x0004002b,0x00000012,0x000002a3,0x00000010,0x0004003b,0x000000b7,
0x000002a9,0x00000000,0x0004002b,0x00000012,0x000002af,0x00000020,0x0006002c,0x00000076,
0x000002d7,0x00000295,0x00000295,0x00000023,0x00050036,0x00000002,0x00000004,0x00000000,
0x00000003,0x000200f8,0x00000005,0x0004003b,0x00000074,0x00000075,0x00000007,0x0004003b,
0x00000099,0x0000009a,0x00000007,0x0004003b,0x0000004c,0x0000009f,0x00000007,0x0004003b,
0x0000004c,0x000000a9,0x00000007,0x0004003b,0x00000014,0x000000b4,0x00000007,0x0004003b,
0x000000cb,0x000000cc,0x00000007,0x0004003b,0x00000008,0x000000d6,0x00000007,0x0004003b,
0x000000de,0x000000df,0x00000007,0x0004003b,0x000000cb,0x000000ef,0x00000007,0x0004003b,
0x00000008,0x000000f7,0x00000007,0x0004003b,0x00000014,0x000000fe,0x00000007,0x0004003b,
0x00000008,0x00000114,0x00000007,0x0004003b,0x00000008,0x0000011f,0x00000007,0x0004003b,
0x00000008,0x00000122,0x00000007,0x0004003b,0x00000008,0x00000127,0x00000007,0x0004003b,
0x00000008,0x0000012f,0x00000007,0x0004003b,0x00000008,0x00000135,0x00000007,0x0004003b,
0x00000008,0x00000139,0x00000007,0x0004003b,0x00000008,0x00000143,0x00000007,0x0004003b,
0x00000014,0x00000158,0x00000007,0x0004003b,0x000000cb,0x00000176,0x00000007,0x0004003b,
0x00000008,0x0000017c,0x00000007,0x0004003b,0x00000008,0x00000181,0x00000007,0x0004003b,
0x00000014,0x0000018d,0x00000007,0x0004003b,0x00000074,0x000001e0,0x00000007,0x0004003b,
0x0000004c,0x000001ee,0x00000007,0x0004003b,0x000000cb,0x000001f9,0x00000007,0x0004003b,
0x00000008,0x00000215,0x00000007,0x0004003b,0x00000014,0x0000021d,0x00000007,0x0004003b,
0x00000014,0x00000221,0x00000007,0x0004003b,0x00000008,0x00000231,0x00000007,0x0004003b,
0x00000008,0x00000246,0x00000007,0x0004003b,0x00000008,0x0000024a,0x00000007,0x0004003b,
0x00000008,0x00000251,0x00000007,0x0004003b,0x00000008,0x00000255,0x00000007,0x0004003b,
0x00000008,0x00000259,0x00000007,0x0004003b,0x00000008,0x00000262,0x00000007,0x0004003b,
0x00000008,0x00000264,0x00000007,0x0004003b,0x00000008,0x0000026c,0x00000007,0x0004003b,
0x00000014,0x00000273,0x00000007,0x0004003b,0x00000008,0x0000027e,0x00000007,0x0004003b,
0x00000014,0x00000286,0x00000007,0x0004003b,0x00000014,0x00000287,0x00000007,0x0004003b,
0x000000cb,0x0000028e,0x00000007,0x0004003b,0x00000014,0x00000298,0x00000007,0x0004003b,
0x000000cb,0x000002a8,0x00000007,0x0004003b,0x00000014,0x000002b2,0x00000007,0x0004003d,
0x00000076,0x0000007a,0x00000078,0x0007004f,0x00000079,0x0000007b,0x0000007a,0x0000007a,
0x00000000,0x00000001,0x0004007c,0x00000073,0x0000007c,0x0000007b,0x0003003e,0x00000075,
0x0000007c,0x00050041,0x0000007d,0x0000007e,0x00000075,0x00000013,0x0004003d,0x00000072,
0x0000007f,0x0000007e,0x00060041,0x00000087,0x00000088,0x00000085,0x00000086,0x00000013,
0x0004003d,0x00000006,0x00000089,0x00000088,0x0004006e,0x00000072,0x0000008a,0x00000089,
0x000500af,0x00000018,0x0000008b,0x0000007f,0x0000008a,0x000400a8,0x00000018,0x0000008c,
0x0000008b,0x000300f7,0x0000008e,0x00000000,0x000400fa,0x0000008c,0x0000008d,0x0000008e,
0x000200f8,0x0000008d,0x00050041,0x0000007d,0x0000008f,0x00000075,0x00000023,0x0004003d,
0x00000072,0x00000090,0x0000008f,0x00060041,0x00000087,0x00000091,0x00000085,0x00000086,
0x00000023,0x0004003d,0x00000006,0x00000092,0x00000091,0x0004006e,0x00000072,0x00000093,
0x00000092,0x000500af,0x00000018,0x00000094,0x00000090,0x00000093,0x000200f9,0x0000008e,
0x000200f8,0x0000008e,0x000700f5,0x00000018,0x00000095,0x0000008b,0x00000005,0x00000094,
0x0000008d,0x000300f7,0x00000097,0x00000000,0x000400fa,0x00000095,0x00000096,0x00000097,
0x000200f8,0x00000096,0x000100fd,0x000200f8,0x00000097,0x00060041,0x0000009c,0x0000009d,
0x00000085,0x0000009b,0x0000002f,0x0004003d,0x00000012,0x0000009e,0x0000009d,0x0003003e,
0x0000009a,0x0000009e,0x0004003d,0x00000073,0x000000a0,0x00000075,0x0004006f,0x0000000d,
0x000000a1,0x000000a0,0x00050050,0x0000000d,0x000000a2,0x0000006c,0x0000006c,0x00050081,
0x0000000d,0x000000a3,0x000000a1,0x000000a2,0x00050041,0x000000a4,0x000000a5,0x00000085,
0x00000086,0x0004003d,0x00000080,0x000000a6,0x000000a5,0x0007004f,0x0000000d,0x000000a7,
0x000000a6,0x000000a6,0x00000002,0x00000003,0x00050085,0x0000000d,0x000000a8,0x000000a3,
0x000000a7,0x0003003e,0x0000009f,0x000000a8,0x00050041,0x00000014,0x000000aa,0x0000009f,
0x00000013,0x0004003d,0x00000006,0x000000ab,0x000000aa,0x00050085,0x00000006,0x000000ad,
0x000000ab,0x000000ac,0x00050083,0x00000006,0x000000ae,0x000000ad,0x00000051,0x00050041,
0x00000014,0x000000af,0x0000009f,0x00000023,0x0004003d,0x00000006,0x000000b0,0x000000af,
0x00050085,0x00000006,0x000000b1,0x000000b0,0x000000ac,0x00050083,0x00000006,0x000000b2,
0x00000051,0x000000b1,0x00050050,0x0000000d,0x000000b3,0x000000ae,0x000000b2,0x0003003e,
0x000000a9,0x000000b3,0x0004003d,0x000000b6,0x000000b9,0x000000b8,0x00050041,0x000000bb,
0x000000bc,0x00000085,0x000000ba,0x0004003d,0x00000082,0x000000bd,0x000000bc,0x0007004f,
0x00000079,0x000000be,0x000000bd,0x000000bd,0x00000002,0x00000003,0x0004007c,0x00000073,
0x000000bf,0x000000be,0x0004003d,0x00000073,0x000000c0,0x00000075,0x00050080,0x00000073,
0x000000c1,0x000000bf,0x000000c0,0x00040064,0x000000b5,0x000000c3,0x000000b9,0x0007005f,
0x00000080,0x000000c4,0x000000c3,0x000000c1,0x00000002,0x000000c2,0x00050051,0x00000006,
0x000000c5,0x000000c4,0x00000000,0x0003003e,0x000000b4,0x000000c5,0x0004003d,0x00000012,
0x000000c6,0x0000009a,0x000500c7,0x00000012,0x000000c7,0x000000c6,0x00000023,0x000500ab,
0x00000018,0x000000c8,0x000000c7,0x00000013,0x000300f7,0x000000ca,0x00000000,0x000400fa,
0x000000c8,0x000000c9,0x000000ee,0x000200f8,0x000000c9,0x00050041,0x000000cd,0x000000ce,
0x00000085,0x000000c2,0x0004003d,0x00000081,0x000000cf,0x000000ce,0x0004003d,0x0000000d,
0x000000d0,0x000000a9,0x0004003d,0x00000006,0x000000d1,0x000000b4,0x00050051,0x00000006,
0x000000d2,0x000000d0,0x00000000,0x00050051,0x00000006,0x000000d3,0x000000d0,0x00000001,
0x00070050,0x00000080,0x000000d4,0x000000d2,0x000000d3,0x000000d1,0x00000051,0x00050091,
0x00000080,0x000000d5,0x000000cf,0x000000d4,0x0003003e,0x000000cc,0x000000d5,0x0004003d,
0x00000080,0x000000d7,0x000000cc,0x0008004f,0x00000007,0x000000d8,0x000000d7,0x000000d7,
0x00000000,0x00000001,0x00000002,0x00050041,0x00000014,0x000000da,0x000000cc,0x000000d9,
0x0004003d,0x00000006,0x000000db,0x000000da,0x00060050,0x00000007,0x000000dc,0x000000db,
0x000000db,0x000000db,0x00050088,0x00000007,0x000000dd,0x000000d8,0x000000dc,0x0003003e,
0x000000d6,0x000000dd,0x0004003d,0x00000006,0x000000e0,0x000000b4,0x00060041,0x00000087,
0x000000e2,0x00000085,0x000000e1,0x00000023,0x0004003d,0x00000006,0x000000e3,0x000000e2,
0x000500b4,0x00000018,0x000000e4,0x000000e0,0x000000e3,0x000400a8,0x00000018,0x000000e5,
0x000000e4,0x000300f7,0x000000e7,0x00000000,0x000400fa,0x000000e5,0x000000e6,0x000000e7,
0x000200f8,0x000000e6,0x00050041,0x00000014,0x000000e8,0x000000cc,0x000000d9,0x0004003d,
0x00000006,0x000000e9,0x000000e8,0x0006000c,0x00000006,0x000000ea,0x00000001,0x00000004,
0x000000e9,0x000500ba,0x00000018,0x000000eb,0x000000ea,0x00000017,0x000400a8,0x00000018,
0x000000ec,0x000000eb,0x000200f9,0x000000e7,0x000200f8,0x000000e7,0x000700f5,0x00000018,
0x000000ed,0x000000e4,0x000000c9,0x000000ec,0x000000e6,0x0003003e,0x000000df,0x000000ed,
0x000200f9,0x000000ca,0x000200f8,0x000000ee,0x00050041,0x000000cd,0x000000f0,0x00000085,
0x000000c2,0x0004003d,0x00000081,0x000000f1,0x000000f0,0x0004003d,0x0000000d,0x000000f2,
0x000000a9,0x00050051,0x00000006,0x000000f3,0x000000f2,0x00000000,0x00050051,0x00000006,
0x000000f4,0x000000f2,0x00000001,0x00070050,0x00000080,0x000000f5,0x000000f3,0x000000f4,
0x0000006c,0x00000051,0x00050091,0x00000080,0x000000f6,0x000000f1,0x000000f5,0x0003003e,
0x000000ef,0x000000f6,0x0004003d,0x00000080,0x000000f8,0x000000ef,0x0008004f,0x00000007,
0x000000f9,0x000000f8,0x000000f8,0x00000000,0x00000001,0x00000002,0x00050041,0x00000014,
0x000000fa,0x000000ef,0x000000d9,0x0004003d,0x00000006,0x000000fb,0x000000fa,0x00060050,
0x00000007,0x000000fc,0x000000fb,0x000000fb,0x000000fb,0x00050088,0x00000007,0x000000fd,
0x000000f9,0x000000fc,0x0003003e,0x000000f7,0x000000fd,0x0004003d,0x00000006,0x000000ff,
0x000000b4,0x00060041,0x00000087,0x00000100,0x00000085,0x000000e1,0x00000013,0x0004003d,
0x00000006,0x00000101,0x00000100,0x00050085,0x00000006,0x00000102,0x000000ff,0x00000101,
0x0003003e,0x000000fe,0x00000102,0x0004003d,0x00000007,0x00000103,0x000000f7,0x0004003d,
0x00000006,0x00000104,0x000000fe,0x00050041,0x00000014,0x00000105,0x000000f7,0x0000002f,
0x0004003d,0x00000006,0x00000106,0x00000105,0x00050088,0x00000006,0x00000107,0x00000104,
0x00000106,0x0005008e,0x00000007,0x00000108,0x00000103,0x00000107,0x0003003e,0x000000d6,
0x00000108,0x0004003d,0x00000006,0x00000109,0x000000b4,0x000500ba,0x00000018,0x0000010a,
0x00000109,0x00000017,0x000400a8,0x00000018,0x0000010b,0x0000010a,0x000400a8,0x00000018,
0x0000010c,0x0000010b,0x000300f7,0x0000010e,0x00000000,0x000400fa,0x0000010c,0x0000010d,
0x0000010e,0x000200f8,0x0000010d,0x0004003d,0x00000006,0x0000010f,0x000000b4,0x00060041,
0x00000087,0x00000110,0x00000085,0x000000e1,0x0000002f,0x0004003d,0x00000006,0x00000111,
0x00000110,0x000500be,0x00000018,0x00000112,0x0000010f,0x00000111,0x000200f9,0x0000010e,
0x000200f8,0x0000010e,0x000700f5,0x00000018,0x00000113,0x0000010b,0x000000ee,0x00000112,
0x0000010d,0x0003003e,0x000000df,0x00000113,0x000200f9,0x000000ca,0x000200f8,0x000000ca,
0x0004003d,0x000000b6,0x00000116,0x00000115,0x00050041,0x000000bb,0x00000117,0x00000085,
0x000000ba,0x0004003d,0x00000082,0x00000118,0x00000117,0x0007004f,0x00000079,0x00000119,
0x00000118,0x00000118,0x00000000,0x00000001,0x0004007c,0x00000073,0x0000011a,0x00000119,
0x0004003d,0x00000073,0x0000011b,0x00000075,0x00050080,0x00000073,0x0000011c,0x0000011a,
0x0000011b,0x00040064,0x000000b5,0x0000011d,0x00000116,0x0007005f,0x00000080,0x0000011e,
0x0000011d,0x0000011c,0x00000002,0x000000c2,0x0008004f,0x00000007,0x00000120,0x0000011e,
0x0000011e,0x00000000,0x00000001,0x00000002,0x0003003e,0x0000011f,0x00000120,0x00050039,
0x00000007,0x00000121,0x0000000b,0x0000011f,0x0003003e,0x00000114,0x00000121,0x0004003d,
0x00000012,0x00000123,0x0000009a,0x000500c7,0x00000012,0x00000125,0x00000123,0x00000124,
0x000500ab,0x00000018,0x00000126,0x00000125,0x00000013,0x000300f7,0x00000129,0x00000000,
0x000400fa,0x00000126,0x00000128,0x00000132,0x000200f8,0x00000128,0x0004003d,0x000000b6,
0x0000012b,0x0000012a,0x0004003d,0x00000073,0x0000012c,0x00000075,0x00040064,0x000000b5,
0x0000012d,0x0000012b,0x0007005f,0x00000080,0x0000012e,0x0000012d,0x0000012c,0x00000002,
0x000000c2,0x0008004f,0x00000007,0x00000130,0x0000012e,0x0000012e,0x00000000,0x00000001,
0x00000002,0x0003003e,0x0000012f,0x00000130,0x00050039,0x00000007,0x00000131,0x0000000b,
0x0000012f,0x0003003e,0x00000127,0x00000131,0x000200f9,0x00000129,0x000200f8,0x00000132,
0x0004003d,0x00000007,0x00000133,0x00000114,0x0003003e,0x00000127,0x00000133,0x000200f9,
0x00000129,0x000200f8,0x00000129,0x0004003d,0x00000007,0x00000134,0x00000127,0x0003003e,
0x00000122,0x00000134,0x0004003d,0x00000007,0x00000136,0x00000114,0x0004003d,0x00000007,
0x00000137,0x00000122,0x00050083,0x00000007,0x00000138,0x00000136,0x00000137,0x0003003e,
0x00000135,0x00000138,0x0004003d,0x000000b6,0x0000013b,0x0000013a,0x0004003d,0x00000073,
0x0000013c,0x00000075,0x00040064,0x000000b5,0x0000013d,0x0000013b,0x0007005f,0x00000080,
0x0000013e,0x0000013d,0x0000013c,0x00000002,0x000000c2,0x0008004f,0x00000007,0x0000013f,
0x0000013e,0x0000013e,0x00000000,0x00000001,0x00000002,0x00060050,0x00000007,0x00000140,
0x00000017,0x00000017,0x00000017,0x00060050,0x00000007,0x00000141,0x00000051,0x00000051,
0x00000051,0x0008000c,0x00000007,0x00000142,0x00000001,0x0000002b,0x0000013f,0x00000140,
0x00000141,0x0003003e,0x00000139,0x00000142,0x0004003d,0x000000b6,0x00000145,0x00000144,
0x0004003d,0x00000073,0x00000146,0x00000075,0x00040064,0x000000b5,0x00000147,0x00000145,
0x0007005f,0x00000080,0x00000148,0x00000147,0x00000146,0x00000002,0x000000c2,0x0008004f,
0x00000007,0x00000149,0x00000148,0x00000148,0x00000000,0x00000001,0x00000002,0x00060050,
0x00000007,0x0000014a,0x00000017,0x00000017,0x00000017,0x00060050,0x00000007,0x0000014b,
0x00000051,0x00000051,0x00000051,0x0008000c,0x00000007,0x0000014c,0x00000001,0x0000002b,
0x00000149,0x0000014a,0x0000014b,0x0003003e,0x00000143,0x0000014c,0x0004003d,0x00000007,
0x0000014d,0x00000139,0x0004003d,0x00000007,0x0000014e,0x00000139,0x0004003d,0x00000007,
0x0000014f,0x00000143,0x00050081,0x00000007,0x00000150,0x0000014e,0x0000014f,0x00060050,
0x00000007,0x00000151,0x00000051,0x00000051,0x00000051,0x00050083,0x00000007,0x00000152,
0x00000150,0x00000151,0x00060050,0x00000007,0x00000153,0x00000017,0x00000017,0x00000017,
0x0007000c,0x00000007,0x00000154,0x00000001,0x00000028,0x00000152,0x00000153,0x00050083,
0x00000007,0x00000155,0x0000014d,0x00000154,0x00060050,0x00000007,0x00000156,0x00000017,
0x00000017,0x00000017,0x0007000c,0x00000007,0x00000157,0x00000001,0x00000028,0x00000155,
0x00000156,0x0003003e,0x00000139,0x00000157,0x0004003d,0x00000007,0x00000159,0x00000139,
0x0004003d,0x00000007,0x0000015a,0x00000143,0x00050081,0x00000007,0x0000015b,0x00000159,
0x0000015a,0x00050094,0x00000006,0x0000015d,0x0000015b,0x0000015c,0x0003003e,0x00000158,
0x0000015d,0x0004003d,0x00000018,0x0000015e,0x000000df,0x000400a8,0x00000018,0x0000015f,
0x0000015e,0x000300f7,0x00000161,0x00000000,0x000400fa,0x0000015f,0x00000160,0x00000161,
0x000200f8,0x00000160,0x0004003d,0x00000006,0x00000162,0x00000158,0x000500ba,0x00000018,
0x00000164,0x00000162,0x00000163,0x000400a8,0x00000018,0x00000165,0x00000164,0x000200f9,
0x00000161,0x000200f8,0x00000161,0x000700f5,0x00000018,0x00000166,0x0000015e,0x00000129,
0x00000165,0x00000160,0x000400a8,0x00000018,0x00000167,0x00000166,0x000300f7,0x00000169,
0x00000000,0x000400fa,0x00000167,0x00000168,0x00000169,0x000200f8,0x00000168,0x0004003d,
0x00000007,0x0000016a,0x000000d6,0x0004009c,0x0000016b,0x0000016c,0x0000016a,0x0004009a,
0x00000018,0x0000016d,0x0000016c,0x000200f9,0x00000169,0x000200f8,0x00000169,0x000700f5,
0x00000018,0x0000016e,0x00000166,0x00000161,0x0000016d,0x00000168,0x000400a8,0x00000018,
0x0000016f,0x0000016e,0x000300f7,0x00000171,0x00000000,0x000400fa,0x0000016f,0x00000170,
0x00000171,0x000200f8,0x00000170,0x0004003d,0x00000007,0x00000172,0x000000d6,0x0004009d,
0x0000016b,0x00000173,0x00000172,0x0004009a,0x00000018,0x00000174,0x00000173,0x000200f9,
0x00000171,0x000200f8,0x00000171,0x000700f5,0x00000018,0x00000175,0x0000016e,0x00000169,
0x00000174,0x00000170,0x0003003e,0x000000df,0x00000175,0x0004003d,0x000000b6,0x00000178,
0x00000177,0x0004003d,0x00000073,0x00000179,0x00000075,0x00040064,0x000000b5,0x0000017a,
0x00000178,0x0007005f,0x00000080,0x0000017b,0x0000017a,0x00000179,0x00000002,0x000000c2,
0x0003003e,0x00000176,0x0000017b,0x0004003d,0x00000012,0x0000017d,0x0000009a,0x000500c7,
0x00000012,0x0000017f,0x0000017d,0x0000017e,0x000500ab,0x00000018,0x00000180,0x0000017f,
0x00000013,0x000300f7,0x00000183,0x00000000,0x000400fa,0x00000180,0x00000182,0x00000189,
0x000200f8,0x00000182,0x0004003d,0x00000080,0x00000184,0x00000176,0x0008004f,0x00000007,
0x00000185,0x00000184,0x00000184,0x00000000,0x00000001,0x00000002,0x0005008e,0x00000007,
0x00000186,0x00000185,0x000000ac,0x00060050,0x00000007,0x00000187,0x00000051,0x00000051,
0x00000051,0x00050083,0x00000007,0x00000188,0x00000186,0x00000187,0x0003003e,0x00000181,
0x00000188,0x000200f9,0x00000183,0x000200f8,0x00000189,0x0004003d,0x00000080,0x0000018a,
0x00000176,0x0008004f,0x00000007,0x0000018b,0x0000018a,0x0000018a,0x00000000,0x00000001,
0x00000002,0x0003003e,0x00000181,0x0000018b,0x000200f9,0x00000183,0x000200f8,0x00000183,
0x0004003d,0x00000007,0x0000018c,0x00000181,0x0003003e,0x0000017c,0x0000018c,0x0004003d,
0x00000007,0x0000018e,0x0000017c,0x0004003d,0x00000007,0x0000018f,0x0000017c,0x00050094,
0x00000006,0x00000190,0x0000018e,0x0000018f,0x0003003e,0x0000018d,0x00000190,0x0004003d,
0x00000012,0x00000191,0x0000009a,0x000500c7,0x00000012,0x00000193,0x00000191,0x00000192,
0x000500ab,0x00000018,0x00000194,0x00000193,0x00000013,0x000300f7,0x00000196,0x00000000,
0x000400fa,0x00000194,0x00000195,0x00000196,0x000200f8,0x00000195,0x0004003d,0x00000018,
0x00000197,0x000000df,0x000400a8,0x00000018,0x00000198,0x00000197,0x000300f7,0x0000019a,
0x00000000,0x000400fa,0x00000198,0x00000199,0x0000019a,0x000200f8,0x00000199,0x0004003d,
0x00000006,0x0000019b,0x0000018d,0x000500ba,0x00000018,0x0000019d,0x0000019b,0x0000019c,
0x000400a8,0x00000018,0x0000019e,0x0000019d,0x000200f9,0x0000019a,0x000200f8,0x0000019a,
0x000700f5,0x00000018,0x0000019f,0x00000197,0x00000195,0x0000019e,0x00000199,0x000400a8,
0x00000018,0x000001a0,0x0000019f,0x000300f7,0x000001a2,0x00000000,0x000400fa,0x000001a0,
0x000001a1,0x000001a2,0x000200f8,0x000001a1,0x0004003d,0x00000006,0x000001a3,0x0000018d,
0x0004009c,0x00000018,0x000001a4,0x000001a3,0x000200f9,0x000001a2,0x000200f8,0x000001a2,
0x000700f5,0x00000018,0x000001a5,0x0000019f,0x0000019a,0x000001a4,0x000001a1,0x000400a8,
0x00000018,0x000001a6,0x000001a5,0x000300f7,0x000001a8,0x00000000,0x000400fa,0x000001a6,
0x000001a7,0x000001a8,0x000200f8,0x000001a7,0x0004003d,0x00000006,0x000001a9,0x0000018d,
0x0004009d,0x00000018,0x000001aa,0x000001a9,0x000200f9,0x000001a8,0x000200f8,0x000001a8,
0x000700f5,0x00000018,0x000001ab,0x000001a5,0x000001a2,0x000001aa,0x000001a7,0x0003003e,
0x000000df,0x000001ab,0x000200f9,0x00000196,0x000200f8,0x00000196,0x0004003d,0x00000018,
0x000001ac,0x000000df,0x000300f7,0x000001ae,0x00000000,0x000400fa,0x000001ac,0x000001ad,
0x000001ae,0x000200f8,0x000001ad,0x0004003d,0x000001af,0x000001b2,0x000001b1,0x0004003d,
0x00000073,0x000001b3,0x00000075,0x00060041,0x00000087,0x000001b4,0x00000085,0x000000e1,
0x0000002f,0x0004003d,0x00000006,0x000001b5,0x000001b4,0x00050085,0x00000006,0x000001b7,
0x000001b5,0x000001b6,0x00060041,0x00000087,0x000001b8,0x00000085,0x000000e1,0x00000013,
0x0004003d,0x00000006,0x000001b9,0x000001b8,0x00050085,0x00000006,0x000001ba,0x000001b7,
0x000001b9,0x00070050,0x00000080,0x000001bb,0x000001ba,0x000001ba,0x000001ba,0x000001ba,
0x00040063,0x000001b2,0x000001b3,0x000001bb,0x0004003d,0x000001bc,0x000001bf,0x000001be,
0x0004003d,0x00000073,0x000001c0,0x00000075,0x00040063,0x000001bf,0x000001c0,0x000001c1,
0x0004003d,0x000001bc,0x000001c3,0x000001c2,0x0004003d,0x00000073,0x000001c4,0x00000075,
0x00040063,0x000001c3,0x000001c4,0x000001c5,0x0004003d,0x000001bc,0x000001c7,0x000001c6,
0x0004003d,0x00000073,0x000001c8,0x00000075,0x00040063,0x000001c7,0x000001c8,0x000001c1,
0x0004003d,0x000001bc,0x000001ca,0x000001c9,0x0004003d,0x00000073,0x000001cb,0x00000075,
0x00040063,0x000001ca,0x000001cb,0x000001c1,0x0004003d,0x000001bc,0x000001cd,0x000001cc,
0x0004003d,0x00000073,0x000001ce,0x00000075,0x00040063,0x000001cd,0x000001ce,0x000001c1,
0x0004003d,0x000001bc,0x000001d0,0x000001cf,0x0004003d,0x00000073,0x000001d1,0x00000075,
0x00040063,0x000001d0,0x000001d1,0x000001c1,0x0004003d,0x000001bc,0x000001d3,0x000001d2,
0x0004003d,0x00000073,0x000001d4,0x00000075,0x0004003d,0x00000007,0x000001d5,0x00000114,
0x00050051,0x00000006,0x000001d6,0x000001d5,0x00000000,0x00050051,0x00000006,0x000001d7,
0x000001d5,0x00000001,0x00050051,0x00000006,0x000001d8,0x000001d5,0x00000002,0x00070050,
0x00000080,0x000001d9,0x000001d6,0x000001d7,0x000001d8,0x00000017,0x00040063,0x000001d3,
0x000001d4,0x000001d9,0x000100fd,0x000200f8,0x000001ae,0x0004003d,0x000001af,0x000001db,
0x000001b1,0x0004003d,0x00000073,0x000001dc,0x00000075,0x00050041,0x00000014,0x000001dd,
0x000000d6,0x0000002f,0x0004003d,0x00000006,0x000001de,0x000001dd,0x00070050,0x00000080,
0x000001df,0x000001de,0x000001de,0x000001de,0x000001de,0x00040063,0x000001db,0x000001dc,
0x000001df,0x00050041,0x000000bb,0x000001e1,0x00000085,0x0000009b,0x0004003d,0x00000082,
0x000001e2,0x000001e1,0x0007004f,0x00000079,0x000001e3,0x000001e2,0x000001e2,0x00000000,
0x00000001,0x0004007c,0x00000073,0x000001e4,0x000001e3,0x0004003d,0x00000073,0x000001e5,
0x00000075,0x0004006f,0x0000000d,0x000001e6,0x000001e5,0x00050041,0x000000a4,0x000001e8,
0x00000085,0x000001e7,0x0004003d,0x00000080,0x000001e9,0x000001e8,0x0007004f,0x0000000d,
0x000001ea,0x000001e9,0x000001e9,0x00000002,0x00000003,0x00050085,0x0000000d,0x000001eb,
0x000001e6,0x000001ea,0x0004006e,0x00000073,0x000001ec,0x000001eb,0x00050080,0x00000073,
0x000001ed,0x000001e4,0x000001ec,0x0003003e,0x000001e0,0x000001ed,0x0004003d,0x000000b6,
0x000001f0,0x000001ef,0x0004003d,0x00000073,0x000001f1,0x000001e0,0x00040064,0x000000b5,
0x000001f2,0x000001f0,0x0007005f,0x00000080,0x000001f3,0x000001f2,0x000001f1,0x00000002,
0x000000c2,0x0007004f,0x0000000d,0x000001f4,0x000001f3,0x000001f3,0x00000000,0x00000001,
0x00050041,0x000000a4,0x000001f5,0x00000085,0x000001e7,0x0004003d,0x00000080,0x000001f6,
0x000001f5,0x0007004f,0x0000000d,0x000001f7,0x000001f6,0x000001f6,0x00000000,0x00000001,
0x00050085,0x0000000d,0x000001f8,0x000001f4,0x000001f7,0x0003003e,0x000001ee,0x000001f8,
0x00050041,0x000000cd,0x000001fb,0x00000085,0x000001fa,0x0004003d,0x00000081,0x000001fc,
0x000001fb,0x0004003d,0x00000007,0x000001fd,0x000000d6,0x00050051,0x00000006,0x000001fe,
0x000001fd,0x00000000,0x00050051,0x00000006,0x000001ff,0x000001fd,0x00000001,0x00050051,
0x00000006,0x00000200,0x000001fd,0x00000002,0x00070050,0x00000080,0x00000201,0x000001fe,
0x000001ff,0x00000200,0x00000051,0x00050091,0x00000080,0x00000202,0x000001fc,0x00000201,
0x0003003e,0x000001f9,0x00000202,0x0004003d,0x000001bc,0x00000203,0x000001be,0x0004003d,
0x00000073,0x00000204,0x00000075,0x0004003d,0x0000000d,0x00000205,0x000001ee,0x00050041,
0x00000014,0x00000206,0x000001f9,0x0000002f,0x0004003d,0x00000006,0x00000207,0x00000206,
0x00050041,0x00000014,0x00000208,0x000001f9,0x000000d9,0x0004003d,0x00000006,0x00000209,
0x00000208,0x00050088,0x00000006,0x0000020a,0x00000207,0x00000209,0x00050041,0x00000014,
0x0000020b,0x000000d6,0x0000002f,0x0004003d,0x00000006,0x0000020c,0x0000020b,0x00050083,
0x00000006,0x0000020d,0x0000020a,0x0000020c,0x00050051,0x00000006,0x0000020e,0x00000205,
0x00000000,0x00050051,0x00000006,0x0000020f,0x00000205,0x00000001,0x00070050,0x00000080,
0x00000210,0x0000020e,0x0000020f,0x0000020d,0x00000017,0x00040063,0x00000203,0x00000204,
0x00000210,0x0004003d,0x00000007,0x00000211,0x0000017c,0x0004003d,0x00000007,0x00000212,
0x0000017c,0x00050094,0x00000006,0x00000213,0x00000211,0x00000212,0x000500ba,0x00000018,
0x00000214,0x00000213,0x0000019c,0x000300f7,0x00000217,0x00000000,0x000400fa,0x00000214,
0x00000216,0x0000021a,0x000200f8,0x00000216,0x0004003d,0x00000007,0x00000218,0x0000017c,
0x0006000c,0x00000007,0x00000219,0x00000001,0x00000045,0x00000218,0x0003003e,0x00000215,
0x00000219,0x000200f9,0x00000217,0x000200f8,0x0000021a,0x0003003e,0x00000215,0x0000021b,
0x000200f9,0x00000217,0x000200f8,0x00000217,0x0004003d,0x00000007,0x0000021c,0x00000215,
0x0003003e,0x0000017c,0x0000021c,0x0004003d,0x00000012,0x0000021e,0x0000009a,0x000500c7,
0x00000012,0x0000021f,0x0000021e,0x0000002f,0x000500ab,0x00000018,0x00000220,0x0000021f,
0x00000013,0x000300f7,0x00000223,0x00000000,0x000400fa,0x00000220,0x00000222,0x00000226,
0x000200f8,0x00000222,0x00050041,0x00000014,0x00000224,0x00000176,0x000000d9,0x0004003d,
0x00000006,0x00000225,0x00000224,0x0003003e,0x00000221,0x00000225,0x000200f9,0x00000223,
0x000200f8,0x00000226,0x0004003d,0x000000b6,0x00000228,0x00000227,0x0004003d,0x00000073,
0x00000229,0x00000075,0x00040064,0x000000b5,0x0000022a,0x00000228,0x0007005f,0x00000080,
0x0000022b,0x0000022a,0x00000229,0x00000002,0x000000c2,0x00050051,0x00000006,0x0000022c,
0x0000022b,0x00000000,0x0003003e,0x00000221,0x0000022c,0x000200f9,0x00000223,0x000200f8,
0x00000223,0x0004003d,0x00000006,0x0000022d,0x00000221,0x0008000c,0x00000006,0x0000022e,
0x00000001,0x0000002b,0x0000022d,0x00000017,0x00000051,0x0003003e,0x0000021d,0x0000022e,
0x0004003d,0x000001bc,0x0000022f,0x000001c2,0x0004003d,0x00000073,0x00000230,0x00000075,
0x0004003d,0x00000007,0x00000232,0x0000017c,0x0003003e,0x00000231,0x00000232,0x00050039,
0x0000000d,0x00000233,0x00000010,0x00000231,0x0004003d,0x00000006,0x00000234,0x0000021d,
0x00050051,0x00000006,0x00000235,0x00000233,0x00000000,0x00050051,0x00000006,0x00000236,
0x00000233,0x00000001,0x00070050,0x00000080,0x00000237,0x00000235,0x00000236,0x00000234,
0x00000017,0x00040063,0x0000022f,0x00000230,0x00000237,0x0004003d,0x000001bc,0x00000238,
0x000001c6,0x0004003d,0x00000073,0x00000239,0x00000075,0x0004003d,0x00000007,0x0000023a,
0x00000139,0x00050051,0x00000006,0x0000023b,0x0000023a,0x00000000,0x00050051,0x00000006,
0x0000023c,0x0000023a,0x00000001,0x00050051,0x00000006,0x0000023d,0x0000023a,0x00000002,
0x00070050,0x00000080,0x0000023e,0x0000023b,0x0000023c,0x0000023d,0x00000017,0x00040063,
0x00000238,0x00000239,0x0000023e,0x0004003d,0x000001bc,0x0000023f,0x000001c9,0x0004003d,
0x00000073,0x00000240,0x00000075,0x0004003d,0x00000007,0x00000241,0x00000143,0x00050051,
0x00000006,0x00000242,0x00000241,0x00000000,0x00050051,0x00000006,0x00000243,0x00000241,
0x00000001,0x00050051,0x00000006,0x00000244,0x00000241,0x00000002,0x00070050,0x00000080,
0x00000245,0x00000242,0x00000243,0x00000244,0x00000017,0x00040063,0x0000023f,0x00000240,
0x00000245,0x0004003d,0x00000007,0x00000247,0x00000139,0x0004003d,0x00000007,0x00000248,
0x00000143,0x00050081,0x00000007,0x00000249,0x00000247,0x00000248,0x0003003e,0x00000246,
0x00000249,0x0004003d,0x00000007,0x0000024b,0x00000143,0x0004003d,0x00000007,0x0000024c,
0x00000246,0x0007000c,0x00000007,0x0000024f,0x00000001,0x00000028,0x0000024c,0x0000024e,
0x00050088,0x00000007,0x00000250,0x0000024b,0x0000024f,0x0003003e,0x0000024a,0x00000250,
0x0004003d,0x00000007,0x00000252,0x00000122,0x0004003d,0x00000007,0x00000253,0x0000024a,
0x00050085,0x00000007,0x00000254,0x00000252,0x00000253,0x0003003e,0x00000251,0x00000254,
0x0004003d,0x00000007,0x00000256,0x00000122,0x0004003d,0x00000007,0x00000257,0x00000251,
0x00050083,0x00000007,0x00000258,0x00000256,0x00000257,0x0003003e,0x00000255,0x00000258,
0x0004003d,0x00000007,0x0000025a,0x00000255,0x0004003d,0x00000007,0x0000025b,0x00000139,
0x00060041,0x00000087,0x0000025d,0x00000085,0x0000025c,0x00000023,0x0004003d,0x00000006,
0x0000025e,0x0000025d,0x00060050,0x00000007,0x0000025f,0x0000025e,0x0000025e,0x0000025e,
0x0007000c,0x00000007,0x00000260,0x00000001,0x00000028,0x0000025b,0x0000025f,0x00050088,
0x00000007,0x00000261,0x0000025a,0x00000260,0x0003003e,0x00000262,0x00000261,0x00050039,
0x00000007,0x00000263,0x0000000b,0x00000262,0x0003003e,0x00000259,0x00000263,0x0004003d,
0x00000007,0x00000265,0x00000251,0x0004003d,0x00000007,0x00000266,0x00000143,0x00060041,
0x00000087,0x00000267,0x00000085,0x0000025c,0x00000023,0x0004003d,0x00000006,0x00000268,
0x00000267,0x00060050,0x00000007,0x00000269,0x00000268,0x00000268,0x00000268,0x0007000c,
0x00000007,0x0000026a,0x00000001,0x00000028,0x00000266,0x00000269,0x00050088,0x00000007,
0x0000026b,0x00000265,0x0000026a,0x0003003e,0x0000026c,0x0000026b,0x00050039,0x00000007,
0x0000026d,0x0000000b,0x0000026c,0x0003003e,0x00000264,0x0000026d,0x00060041,0x00000087,
0x0000026e,0x00000085,0x0000025c,0x00000013,0x0004003d,0x00000006,0x0000026f,0x0000026e,
0x000500ba,0x00000018,0x00000270,0x0000026f,0x00000017,0x000300f7,0x00000272,0x00000000,
0x000400fa,0x00000270,0x00000271,0x00000272,0x000200f8,0x00000271,0x00060041,0x00000087,
0x00000274,0x00000085,0x0000025c,0x00000013,0x0004003d,0x00000006,0x00000275,0x00000274,
0x0008000c,0x00000006,0x00000276,0x00000001,0x0000002b,0x00000275,0x00000017,0x00000051,
0x00050083,0x00000006,0x00000277,0x00000051,0x00000276,0x0003003e,0x00000273,0x00000277,
0x0004003d,0x00000006,0x00000278,0x00000273,0x0004003d,0x00000007,0x00000279,0x00000259,
0x0005008e,0x00000007,0x0000027a,0x00000279,0x00000278,0x0003003e,0x00000259,0x0000027a,
0x0004003d,0x00000006,0x0000027b,0x00000273,0x0004003d,0x00000007,0x0000027c,0x00000264,
0x0005008e,0x00000007,0x0000027d,0x0000027c,0x0000027b,0x0003003e,0x00000264,0x0000027d,
0x000200f9,0x00000272,0x000200f8,0x00000272,0x0004003d,0x00000007,0x0000027f,0x00000259,
0x0004003d,0x00000007,0x00000280,0x00000139,0x00050085,0x00000007,0x00000281,0x0000027f,
0x00000280,0x0004003d,0x00000007,0x00000282,0x00000264,0x0004003d,0x00000007,0x00000283,
0x00000143,0x00050085,0x00000007,0x00000284,0x00000282,0x00000283,0x00050081,0x00000007,
0x00000285,0x00000281,0x00000284,0x0003003e,0x0000027e,0x00000285,0x0003003e,0x00000286,
0x00000017,0x0003003e,0x00000287,0x00000017,0x0004003d,0x00000012,0x00000288,0x0000009a,
0x000500c7,0x00000012,0x0000028a,0x00000288,0x00000289,0x000500ab,0x00000018,0x0000028b,
0x0000028a,0x00000013,0x000300f7,0x0000028d,0x00000000,0x000400fa,0x0000028b,0x0000028c,
0x0000028d,0x000200f8,0x0000028c,0x0004003d,0x000000b6,0x00000290,0x0000028f,0x0004003d,
0x00000073,0x00000291,0x00000075,0x00040064,0x000000b5,0x00000292,0x00000290,0x0007005f,
0x00000080,0x00000293,0x00000292,0x00000291,0x00000002,0x000000c2,0x0003003e,0x0000028e,
0x00000293,0x0004003d,0x00000012,0x00000294,0x0000009a,0x000500c7,0x00000012,0x00000296,
0x00000294,0x00000295,0x000500ab,0x00000018,0x00000297,0x00000296,0x00000013,0x000300f7,
0x0000029a,0x00000000,0x000400fa,0x00000297,0x00000299,0x0000029d,0x000200f8,0x00000299,
0x00050041,0x00000014,0x0000029b,0x0000028e,0x000000d9,0x0004003d,0x00000006,0x0000029c,
0x0000029b,0x0003003e,0x00000298,0x0000029c,0x000200f9,0x0000029a,0x000200f8,0x0000029d,
0x00050041,0x00000014,0x0000029e,0x0000028e,0x00000013,0x0004003d,0x00000006,0x0000029f,
0x0000029e,0x0003003e,0x00000298,0x0000029f,0x000200f9,0x0000029a,0x000200f8,0x0000029a,
0x0004003d,0x00000006,0x000002a0,0x00000298,0x0008000c,0x00000006,0x000002a1,0x00000001,
0x0000002b,0x000002a0,0x00000017,0x0000001f,0x0003003e,0x00000286,0x000002a1,0x000200f9,
0x0000028d,0x000200f8,0x0000028d,0x0004003d,0x00000012,0x000002a2,0x0000009a,0x000500c7,
0x00000012,0x000002a4,0x000002a2,0x000002a3,0x000500ab,0x00000018,0x000002a5,0x000002a4,
0x00000013,0x000300f7,0x000002a7,0x00000000,0x000400fa,0x000002a5,0x000002a6,0x000002a7,
0x000200f8,0x000002a6,0x0004003d,0x000000b6,0x000002aa,0x000002a9,0x0004003d,0x00000073,
0x000002ab,0x00000075,0x00040064,0x000000b5,0x000002ac,0x000002aa,0x0007005f,0x00000080,
0x000002ad,0x000002ac,0x000002ab,0x00000002,0x000000c2,0x0003003e,0x000002a8,0x000002ad,
0x0004003d,0x00000012,0x000002ae,0x0000009a,0x000500c7,0x00000012,0x000002b0,0x000002ae,
0x000002af,0x000500ab,0x00000018,0x000002b1,0x000002b0,0x00000013,0x000300f7,0x000002b4,
0x00000000,0x000400fa,0x000002b1,0x000002b3,0x000002b7,0x000200f8,0x000002b3,0x00050041,
0x00000014,0x000002b5,0x000002a8,0x000000d9,0x0004003d,0x00000006,0x000002b6,0x000002b5,
0x0003003e,0x000002b2,0x000002b6,0x000200f9,0x000002b4,0x000200f8,0x000002b7,0x00050041,
0x00000014,0x000002b8,0x000002a8,0x00000013,0x0004003d,0x00000006,0x000002b9,0x000002b8,
0x0003003e,0x000002b2,0x000002b9,0x000200f9,0x000002b4,0x000200f8,0x000002b4,0x0004003d,
0x00000006,0x000002ba,0x000002b2,0x0008000c,0x00000006,0x000002bb,0x00000001,0x0000002b,
0x000002ba,0x00000017,0x0000001f,0x0003003e,0x00000287,0x000002bb,0x000200f9,0x000002a7,
0x000200f8,0x000002a7,0x0004003d,0x000001bc,0x000002bc,0x000001cc,0x0004003d,0x00000073,
0x000002bd,0x00000075,0x0004003d,0x00000007,0x000002be,0x00000259,0x0004003d,0x00000006,
0x000002bf,0x00000287,0x00050051,0x00000006,0x000002c0,0x000002be,0x00000000,0x00050051,
0x00000006,0x000002c1,0x000002be,0x00000001,0x00050051,0x00000006,0x000002c2,0x000002be,
0x00000002,0x00070050,0x00000080,0x000002c3,0x000002c0,0x000002c1,0x000002c2,0x000002bf,
0x00040063,0x000002bc,0x000002bd,0x000002c3,0x0004003d,0x000001bc,0x000002c4,0x000001cf,
0x0004003d,0x00000073,0x000002c5,0x00000075,0x0004003d,0x00000007,0x000002c6,0x00000264,
0x0004003d,0x00000006,0x000002c7,0x00000286,0x00050051,0x00000006,0x000002c8,0x000002c6,
0x00000000,0x00050051,0x00000006,0x000002c9,0x000002c6,0x00000001,0x00050051,0x00000006,
0x000002ca,0x000002c6,0x00000002,0x00070050,0x00000080,0x000002cb,0x000002c8,0x000002c9,
0x000002ca,0x000002c7,0x00040063,0x000002c4,0x000002c5,0x000002cb,0x0004003d,0x000001bc,
0x000002cc,0x000001d2,0x0004003d,0x00000073,0x000002cd,0x00000075,0x0004003d,0x00000007,
0x000002ce,0x00000122,0x0004003d,0x00000007,0x000002cf,0x0000027e,0x00050083,0x00000007,
0x000002d0,0x000002ce,0x000002cf,0x0004003d,0x00000007,0x000002d1,0x00000135,0x00050081,
0x00000007,0x000002d2,0x000002d0,0x000002d1,0x00050051,0x00000006,0x000002d3,0x000002d2,
0x00000000,0x00050051,0x00000006,0x000002d4,0x000002d2,0x00000001,0x00050051,0x00000006,
0x000002d5,0x000002d2,0x00000002,0x00070050,0x00000080,0x000002d6,0x000002d3,0x000002d4,
0x000002d5,0x00000017,0x00040063,0x000002cc,0x000002cd,0x000002d6,0x000100fd,0x00010038,
0x00050036,0x00000007,0x0000000b,0x00000000,0x00000009,0x00030037,0x00000008,0x0000000a,
0x000200f8,0x0000000c,0x0004003b,0x00000014,0x0000001a,0x00000007,0x0004003b,0x00000014,
0x00000027,0x00000007,0x0004003b,0x00000014,0x00000033,0x00000007,0x00050041,0x00000014,
0x00000015,0x0000000a,0x00000013,0x0004003d,0x00000006,0x00000016,0x00000015,0x000500be,
0x00000018,0x00000019,0x00000016,0x00000017,0x000300f7,0x0000001c,0x00000000,0x000400fa,
0x00000019,0x0000001b,0x00000021,0x000200f8,0x0000001b,0x00050041,0x00000014,0x0000001d,
0x0000000a,0x00000013,0x0004003d,0x00000006,0x0000001e,0x0000001d,0x0007000c,0x00000006,
0x00000020,0x00000001,0x00000025,0x0000001e,0x0000001f,0x0003003e,0x0000001a,0x00000020,
0x000200f9,0x0000001c,0x000200f8,0x00000021,0x0003003e,0x0000001a,0x00000017,0x000200f9,
0x0000001c,0x000200f8,0x0000001c,0x0004003d,0x00000006,0x00000022,0x0000001a,0x00050041,
0x00000014,0x00000024,0x0000000a,0x00000023,0x0004003d,0x00000006,0x00000025,0x00000024,
0x000500be,0x00000018,0x00000026,0x00000025,0x00000017,0x000300f7,0x00000029,0x00000000,
0x000400fa,0x00000026,0x00000028,0x0000002d,0x000200f8,0x00000028,0x00050041,0x00000014,
0x0000002a,0x0000000a,0x00000023,0x0004003d,0x00000006,0x0000002b,0x0000002a,0x0007000c,
0x00000006,0x0000002c,0x00000001,0x00000025,0x0000002b,0x0000001f,0x0003003e,0x00000027,
0x0000002c,0x000200f9,0x00000029,0x000200f8,0x0000002d,0x0003003e,0x00000027,0x00000017,
0x000200f9,0x00000029,0x000200f8,0x00000029,0x0004003d,0x00000006,0x0000002e,0x00000027,
0x00050041,0x00000014,0x00000030,0x0000000a,0x0000002f,0x0004003d,0x00000006,0x00000031,
0x00000030,0x000500be,0x00000018,0x00000032,0x00000031,0x00000017,0x000300f7,0x00000035,
0x00000000,0x000400fa,0x00000032,0x00000034,0x00000039,0x000200f8,0x00000034,0x00050041,
0x00000014,0x00000036,0x0000000a,0x0000002f,0x0004003d,0x00000006,0x00000037,0x00000036,
0x0007000c,0x00000006,0x00000038,0x00000001,0x00000025,0x00000037,0x0000001f,0x0003003e,
0x00000033,0x00000038,0x000200f9,0x00000035,0x000200f8,0x00000039,0x0003003e,0x00000033,
0x00000017,0x000200f9,0x00000035,0x000200f8,0x00000035,0x0004003d,0x00000006,0x0000003a,
0x00000033,0x00060050,0x00000007,0x0000003b,0x00000022,0x0000002e,0x0000003a,0x000200fe,
0x0000003b,0x00010038,0x00050036,0x0000000d,0x00000010,0x00000000,0x0000000e,0x00030037,
0x00000008,0x0000000f,0x000200f8,0x00000011,0x0004003b,0x0000004c,0x0000004d,0x00000007,
0x0004003b,0x0000004c,0x00000059,0x00000007,0x0004003b,0x0000004c,0x0000005d,0x00000007,
0x00050041,0x00000014,0x0000003e,0x0000000f,0x00000013,0x0004003d,0x00000006,0x0000003f,
0x0000003e,0x0006000c,0x00000006,0x00000040,0x00000001,0x00000004,0x0000003f,0x00050041,
0x00000014,0x00000041,0x0000000f,0x00000023,0x0004003d,0x00000006,0x00000042,0x00000041,
0x0006000c,0x00000006,0x00000043,0x00000001,0x00000004,0x00000042,0x00050081,0x00000006,
0x00000044,0x00000040,0x00000043,0x00050041,0x00000014,0x00000045,0x0000000f,0x0000002f,
0x0004003d,0x00000006,0x00000046,0x00000045,0x0006000c,0x00000006,0x00000047,0x00000001,
0x00000004,0x00000046,0x00050081,0x00000006,0x00000048,0x00000044,0x00000047,0x0004003d,
0x00000007,0x00000049,0x0000000f,0x00060050,0x00000007,0x0000004a,0x00000048,0x00000048,
0x00000048,0x00050088,0x00000007,0x0000004b,0x00000049,0x0000004a,0x0003003e,0x0000000f,
0x0000004b,0x00050041,0x00000014,0x0000004e,0x0000000f,0x00000013,0x0004003d,0x00000006,
0x0000004f,0x0000004e,0x000500be,0x00000018,0x00000050,0x0000004f,0x00000017,0x000600a9,
0x00000006,0x00000053,0x00000050,0x00000051,0x00000052,0x00050041,0x00000014,0x00000054,
0x0000000f,0x00000023,0x0004003d,0x00000006,0x00000055,0x00000054,0x000500be,0x00000018,
0x00000056,0x00000055,0x00000017,0x000600a9,0x00000006,0x00000057,0x00000056,0x00000051,
0x00000052,0x00050050,0x0000000d,0x00000058,0x00000053,0x00000057,0x0003003e,0x0000004d,
0x00000058,0x00050041,0x00000014,0x0000005a,0x0000000f,0x0000002f,0x0004003d,0x00000006,
0x0000005b,0x0000005a,0x000500b8,0x00000018,0x0000005c,0x0000005b,0x00000017,0x000300f7,
0x0000005f,0x00000000,0x000400fa,0x0000005c,0x0000005e,0x00000067,0x000200f8,0x0000005e,
0x0004003d,0x00000007,0x00000060,0x0000000f,0x0007004f,0x0000000d,0x00000061,0x00000060,
0x00000060,0x00000001,0x00000000,0x0006000c,0x0000000d,0x00000062,0x00000001,0x00000004,
0x00000061,0x00050050,0x0000000d,0x00000063,0x00000051,0x00000051,0x00050083,0x0000000d,
0x00000064,0x00000063,0x00000062,0x0004003d,0x0000000d,0x00000065,0x0000004d,0x00050085,
0x0000000d,0x00000066,0x00000064,0x00000065,0x0003003e,0x0000005d,0x00000066,0x000200f9,
0x0000005f,0x000200f8,0x00000067,0x0004003d,0x00000007,0x00000068,0x0000000f,0x0007004f,
0x0000000d,0x00000069,0x00000068,0x00000068,0x00000000,0x00000001,0x0003003e,0x0000005d,
0x00000069,0x000200f9,0x0000005f,0x000200f8,0x0000005f,0x0004003d,0x0000000d,0x0000006a,
0x0000005d,0x0003003e,0x00000059,0x0000006a,0x0004003d,0x0000000d,0x0000006b,0x00000059,
0x0005008e,0x0000000d,0x0000006d,0x0000006b,0x0000006c,0x00050050,0x0000000d,0x0000006e,
0x0000006c,0x0000006c,0x00050081,0x0000000d,0x0000006f,0x0000006d,0x0000006e,0x000200fe,
0x0000006f,0x00010038,
};
static const uint32_t d4r_vk_fsr_rr_compose[] = {
0x07230203,0x00010000,0x0008000b,0x000000db,0x00000000,0x00020011,0x00000001,0x0006000b,
0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e,0x00000000,0x00000001,
0x0006000f,0x00000005,0x00000004,0x6e69616d,0x00000000,0x0000000d,0x00060010,0x00000004,
0x00000011,0x00000008,0x00000008,0x00000001,0x00030003,0x00000002,0x000001c2,0x00040005,
0x00000004,0x6e69616d,0x00000000,0x00030005,0x00000009,0x00007870,0x00080005,0x0000000d,
0x475f6c67,0x61626f6c,0x766e496c,0x7461636f,0x496e6f69,0x00000044,0x00050005,0x00000018,
0x736e6f43,0x746e6174,0x00000073,0x00060006,0x00000018,0x00000000,0x646e6572,0x69537265,
0x0000657a,0x00060006,0x00000018,0x00000001,0x6f6c6168,0x696d694c,0x00000074,0x00050006,
0x00000018,0x00000002,0x73756e75,0x00006465,0x00030005,0x0000001a,0x00006370,0x00030005,
0x00000031,0x00006164,0x00060005,0x00000034,0x66666964,0x41657375,0x6465626c,0x0000006f,
0x00030005,0x0000003a,0x00006173,0x00060005,0x0000003b,0x63657073,0x72616c75,0x65626c41,
0x00006f64,0x00040005,0x00000040,0x66666964,0x00657375,0x00060005,0x00000041,0x6f6e6564,
0x64657369,0x66666944,0x00657375,0x00050005,0x00000048,0x63657073,0x72616c75,0x00000000,
0x00070005,0x00000049,0x6f6e6564,0x64657369,0x63657053,0x72616c75,0x00000000,0x00050005,
0x00000059,0x66666964,0x4d657375,0x00007861,0x00050005,0x0000005a,0x63657073,0x72616c75,
0x0078614d,0x00030005,0x0000005b,0x00007964,0x00030005,0x00000064,0x00007864,0x00030005,
0x0000006c,0x00000071,0x00060005,0x0000007b,0x73696f6e,0x66694479,0x65737566,0x00000000,
0x00060005,0x0000008c,0x73696f6e,0x65705379,0x616c7563,0x00000072,0x00030005,0x0000009c,
0x00006c64,0x00030005,0x0000009f,0x00006c73,0x00040005,0x000000c2,0x6f6c6f63,0x00000072,
0x00050005,0x000000c6,0x69736572,0x6c617564,0x00000000,0x00050005,0x000000cc,0x706d6f63,
0x6465736f,0x00000000,0x00040047,0x0000000d,0x0000000b,0x0000001c,0x00030047,0x00000018,
0x00000002,0x00050048,0x00000018,0x00000000,0x00000023,0x00000000,0x00050048,0x00000018,
0x00000001,0x00000023,0x00000008,0x00050048,0x00000018,0x00000002,0x00000023,0x0000000c,
0x00030047,0x00000034,0x00000018,0x00040047,0x00000034,0x00000021,0x00000002,0x00040047,
0x00000034,0x00000022,0x00000000,0x00030047,0x0000003b,0x00000018,0x00040047,0x0000003b,
0x00000021,0x00000003,0x00040047,0x0000003b,0x00000022,0x00000000,0x00030047,0x00000041,
0x00000018,0x00040047,0x00000041,0x00000021,0x00000000,0x00040047,0x00000041,0x00000022,
0x00000000,0x00030047,0x00000049,0x00000018,0x00040047,0x00000049,0x00000021,0x00000001,
0x00040047,0x00000049,0x00000022,0x00000000,0x00030047,0x0000007b,0x00000018,0x00040047,
0x0000007b,0x00000021,0x00000006,0x00040047,0x0000007b,0x00000022,0x00000000,0x00030047,
0x0000008c,0x00000018,0x00040047,0x0000008c,0x00000021,0x00000007,0x00040047,0x0000008c,
0x00000022,0x00000000,0x00030047,0x000000c6,0x00000018,0x00040047,0x000000c6,0x00000021,
0x00000004,0x00040047,0x000000c6,0x00000022,0x00000000,0x00030047,0x000000cc,0x00000019,
0x00040047,0x000000cc,0x00000021,0x00000005,0x00040047,0x000000cc,0x00000022,0x00000000,
0x00040047,0x000000da,0x0000000b,0x00000019,0x00020013,0x00000002,0x00030021,0x00000003,
0x00000002,0x00040015,0x00000006,0x00000020,0x00000001,0x00040017,0x00000007,0x00000006,
0x00000002,0x00040020,0x00000008,0x00000007,0x00000007,0x00040015,0x0000000a,0x00000020,
0x00000000,0x00040017,0x0000000b,0x0000000a,0x00000003,0x00040020,0x0000000c,0x00000001,
0x0000000b,0x0004003b,0x0000000c,0x0000000d,0x00000001,0x00040017,0x0000000e,0x0000000a,
0x00000002,0x00020014,0x00000012,0x0004002b,0x0000000a,0x00000013,0x00000000,0x00040020,
0x00000014,0x00000007,0x00000006,0x00030016,0x00000017,0x00000020,0x0005001e,0x00000018,
0x0000000e,0x00000017,0x0000000a,0x00040020,0x00000019,0x00000009,0x00000018,0x0004003b,
0x00000019,0x0000001a,0x00000009,0x0004002b,0x00000006,0x0000001b,0x00000000,0x00040020,
0x0000001c,0x00000009,0x0000000a,0x0004002b,0x0000000a,0x00000024,0x00000001,0x00040017,
0x0000002f,0x00000017,0x00000003,0x00040020,0x00000030,0x00000007,0x0000002f,0x00090019,
0x00000032,0x00000017,0x00000001,0x00000000,0x00000000,0x00000000,0x00000002,0x00000002,
0x00040020,0x00000033,0x00000000,0x00000032,0x0004003b,0x00000033,0x00000034,0x00000000,
0x00040017,0x00000037,0x00000017,0x00000004,0x0004003b,0x00000033,0x0000003b,0x00000000,
0x0004003b,0x00000033,0x00000041,0x00000000,0x0004003b,0x00000033,0x00000049,0x00000000,
0x0004002b,0x00000006,0x00000050,0x00000001,0x00040020,0x00000051,0x00000009,0x00000017,
0x0004002b,0x00000017,0x00000054,0x00000000,0x00040020,0x00000058,0x00000007,0x00000017,
0x0004002b,0x00000006,0x0000005c,0xffffffff,0x0005002c,0x00000007,0x00000072,0x0000001b,
0x0000001b,0x00040020,0x00000073,0x00000009,0x0000000e,0x0004003b,0x00000033,0x0000007b,
0x00000000,0x0004002b,0x00000017,0x00000085,0x3e59b3d0,0x0004002b,0x00000017,0x00000086,
0x3f371759,0x0004002b,0x00000017,0x00000087,0x3d93dd98,0x0006002c,0x0000002f,0x00000088,
0x00000085,0x00000086,0x00000087,0x0004003b,0x00000033,0x0000008c,0x00000000,0x0004003b,
0x00000033,0x000000c6,0x00000000,0x0004003b,0x00000033,0x000000cc,0x00000000,0x0004002b,
0x00000017,0x000000d0,0x477de800,0x0004002b,0x00000017,0x000000d4,0x3f800000,0x0004002b,
0x0000000a,0x000000d9,0x00000008,0x0006002c,0x0000000b,0x000000da,0x000000d9,0x000000d9,
0x00000024,0x00050036,0x00000002,0x00000004,0x00000000,0x00000003,0x000200f8,0x00000005,
0x0004003b,0x00000008,0x00000009,0x00000007,0x0004003b,0x00000030,0x00000031,0x00000007,
0x0004003b,0x00000030,0x0000003a,0x00000007,0x0004003b,0x00000030,0x00000040,0x00000007,
0x0004003b,0x00000030,0x00000048,0x00000007,0x0004003b,0x00000058,0x00000059,0x00000007,
0x0004003b,0x00000058,0x0000005a,0x00000007,0x0004003b,0x00000014,0x0000005b,0x00000007,
0x0004003b,0x00000014,0x00000064,0x00000007,0x0004003b,0x00000008,0x0000006c,0x00000007,
0x0004003b,0x00000058,0x0000009c,0x00000007,0x0004003b,0x00000058,0x0000009f,0x00000007,
0x0004003b,0x00000030,0x000000c2,0x00000007,0x0004003d,0x0000000b,0x0000000f,0x0000000d,
0x0007004f,0x0000000e,0x00000010,0x0000000f,0x0000000f,0x00000000,0x00000001,0x0004007c,
0x00000007,0x00000011,0x00000010,0x0003003e,0x00000009,0x00000011,0x00050041,0x00000014,
0x00000015,0x00000009,0x00000013,0x0004003d,0x00000006,0x00000016,0x00000015,0x00060041,
0x0000001c,0x0000001d,0x0000001a,0x0000001b,0x00000013,0x0004003d,0x0000000a,0x0000001e,
0x0000001d,0x0004007c,0x00000006,0x0000001f,0x0000001e,0x000500af,0x00000012,0x00000020,
0x00000016,0x0000001f,0x000400a8,0x00000012,0x00000021,0x00000020,0x000300f7,0x00000023,
0x00000000,0x000400fa,0x00000021,0x00000022,0x00000023,0x000200f8,0x00000022,0x00050041,
0x00000014,0x00000025,0x00000009,0x00000024,0x0004003d,0x00000006,0x00000026,0x00000025,
0x00060041,0x0000001c,0x00000027,0x0000001a,0x0000001b,0x00000024,0x0004003d,0x0000000a,
0x00000028,0x00000027,0x0004007c,0x00000006,0x00000029,0x00000028,0x000500af,0x00000012,
0x0000002a,0x00000026,0x00000029,0x000200f9,0x00000023,0x000200f8,0x00000023,0x000700f5,
0x00000012,0x0000002b,0x00000020,0x00000005,0x0000002a,0x00000022,0x000300f7,0x0000002d,
0x00000000,0x000400fa,0x0000002b,0x0000002c,0x0000002d,0x000200f8,0x0000002c,0x000100fd,
0x000200f8,0x0000002d,0x0004003d,0x00000032,0x00000035,0x00000034,0x0004003d,0x00000007,
0x00000036,0x00000009,0x00050062,0x00000037,0x00000038,0x00000035,0x00000036,0x0008004f,
0x0000002f,0x00000039,0x00000038,0x00000038,0x00000000,0x00000001,0x00000002,0x0003003e,
0x00000031,0x00000039,0x0004003d,0x00000032,0x0000003c,0x0000003b,0x0004003d,0x00000007,
0x0000003d,0x00000009,0x00050062,0x00000037,0x0000003e,0x0000003c,0x0000003d,0x0008004f,
0x0000002f,0x0000003f,0x0000003e,0x0000003e,0x00000000,0x00000001,0x00000002,0x0003003e,
0x0000003a,0x0000003f,0x0004003d,0x00000032,0x00000042,0x00000041,0x0004003d,0x00000007,
0x00000043,0x00000009,0x00050062,0x00000037,0x00000044,0x00000042,0x00000043,0x0008004f,
0x0000002f,0x00000045,0x00000044,0x00000044,0x00000000,0x00000001,0x00000002,0x0004003d,
0x0000002f,0x00000046,0x00000031,0x00050085,0x0000002f,0x00000047,0x00000045,0x00000046,
0x0003003e,0x00000040,0x00000047,0x0004003d,0x00000032,0x0000004a,0x00000049,0x0004003d,
0x00000007,0x0000004b,0x00000009,0x00050062,0x00000037,0x0000004c,0x0000004a,0x0000004b,
0x0008004f,0x0000002f,0x0000004d,0x0000004c,0x0000004c,0x00000000,0x00000001,0x00000002,
0x0004003d,0x0000002f,0x0000004e,0x0000003a,0x00050085,0x0000002f,0x0000004f,0x0000004d,
0x0000004e,0x0003003e,0x00000048,0x0000004f,0x00050041,0x00000051,0x00000052,0x0000001a,
0x00000050,0x0004003d,0x00000017,0x00000053,0x00000052,0x000500ba,0x00000012,0x00000055,
0x00000053,0x00000054,0x000300f7,0x00000057,0x00000000,0x000400fa,0x00000055,0x00000056,
0x00000057,0x000200f8,0x00000056,0x0003003e,0x00000059,0x00000054,0x0003003e,0x0000005a,
0x00000054,0x0003003e,0x0000005b,0x0000005c,0x000200f9,0x0000005d,0x000200f8,0x0000005d,
0x000400f6,0x0000005f,0x00000060,0x00000000,0x000200f9,0x00000061,0x000200f8,0x00000061,
0x0004003d,0x00000006,0x00000062,0x0000005b,0x000500b3,0x00000012,0x00000063,0x00000062,
0x00000050,0x000400fa,0x00000063,0x0000005e,0x0000005f,0x000200f8,0x0000005e,0x0003003e,
0x00000064,0x0000005c,0x000200f9,0x00000065,0x000200f8,0x00000065,0x000400f6,0x00000067,
0x00000068,0x00000000,0x000200f9,0x00000069,0x000200f8,0x00000069,0x0004003d,0x00000006,
0x0000006a,0x00000064,0x000500b3,0x00000012,0x0000006b,0x0000006a,0x00000050,0x000400fa,
0x0000006b,0x00000066,0x00000067,0x000200f8,0x00000066,0x0004003d,0x00000007,0x0000006d,
0x00000009,0x0004003d,0x00000006,0x0000006e,0x00000064,0x0004003d,0x00000006,0x0000006f,
0x0000005b,0x00050050,0x00000007,0x00000070,0x0000006e,0x0000006f,0x00050080,0x00000007,
0x00000071,0x0000006d,0x00000070,0x00050041,0x00000073,0x00000074,0x0000001a,0x0000001b,
0x0004003d,0x0000000e,0x00000075,0x00000074,0x0004007c,0x00000007,0x00000076,0x00000075,
0x00050050,0x00000007,0x00000077,0x00000050,0x00000050,0x00050082,0x00000007,0x00000078,
0x00000076,0x00000077,0x0008000c,0x00000007,0x00000079,0x00000001,0x0000002d,0x00000071,
0x00000072,0x00000078,0x0003003e,0x0000006c,0x00000079,0x0004003d,0x00000017,0x0000007a,
0x00000059,0x0004003d,0x00000032,0x0000007c,0x0000007b,0x0004003d,0x00000007,0x0000007d,
0x0000006c,0x00050062,0x00000037,0x0000007e,0x0000007c,0x0000007d,0x0008004f,0x0000002f,
0x0000007f,0x0000007e,0x0000007e,0x00000000,0x00000001,0x00000002,0x0004003d,0x00000032,
0x00000080,0x00000034,0x0004003d,0x00000007,0x00000081,0x0000006c,0x00050062,0x00000037,
0x00000082,0x00000080,0x00000081,0x0008004f,0x0000002f,0x00000083,0x00000082,0x00000082,
0x00000000,0x00000001,0x00000002,0x00050085,0x0000002f,0x00000084,0x0000007f,0x00000083,
0x00050094,0x00000017,0x00000089,0x00000084,0x00000088,0x0007000c,0x00000017,0x0000008a,
0x00000001,0x00000028,0x0000007a,0x00000089,0x0003003e,0x00000059,0x0000008a,0x0004003d,
0x00000017,0x0000008b,0x0000005a,0x0004003d,0x00000032,0x0000008d,0x0000008c,0x0004003d,
0x00000007,0x0000008e,0x0000006c,0x00050062,0x00000037,0x0000008f,0x0000008d,0x0000008e,
0x0008004f,0x0000002f,0x00000090,0x0000008f,0x0000008f,0x00000000,0x00000001,0x00000002,
0x0004003d,0x00000032,0x00000091,0x0000003b,0x0004003d,0x00000007,0x00000092,0x0000006c,
0x00050062,0x00000037,0x00000093,0x00000091,0x00000092,0x0008004f,0x0000002f,0x00000094,
0x00000093,0x00000093,0x00000000,0x00000001,0x00000002,0x00050085,0x0000002f,0x00000095,
0x00000090,0x00000094,0x00050094,0x00000017,0x00000096,0x00000095,0x00000088,0x0007000c,
0x00000017,0x00000097,0x00000001,0x00000028,0x0000008b,0x00000096,0x0003003e,0x0000005a,
0x00000097,0x000200f9,0x00000068,0x000200f8,0x00000068,0x0004003d,0x00000006,0x00000098,
0x00000064,0x00050080,0x00000006,0x00000099,0x00000098,0x00000050,0x0003003e,0x00000064,
0x00000099,0x000200f9,0x00000065,0x000200f8,0x00000067,0x000200f9,0x00000060,0x000200f8,
0x00000060,0x0004003d,0x00000006,0x0000009a,0x0000005b,0x00050080,0x00000006,0x0000009b,
0x0000009a,0x00000050,0x0003003e,0x0000005b,0x0000009b,0x000200f9,0x0000005d,0x000200f8,
0x0000005f,0x0004003d,0x0000002f,0x0000009d,0x00000040,0x00050094,0x00000017,0x0000009e,
0x0000009d,0x00000088,0x0003003e,0x0000009c,0x0000009e,0x0004003d,0x0000002f,0x000000a0,
0x00000048,0x00050094,0x00000017,0x000000a1,0x000000a0,0x00000088,0x0003003e,0x0000009f,
0x000000a1,0x0004003d,0x00000017,0x000000a2,0x0000009c,0x00050041,0x00000051,0x000000a3,
0x0000001a,0x00000050,0x0004003d,0x00000017,0x000000a4,0x000000a3,0x0004003d,0x00000017,
0x000000a5,0x00000059,0x00050085,0x00000017,0x000000a6,0x000000a4,0x000000a5,0x000500ba,
0x00000012,0x000000a7,0x000000a2,0x000000a6,0x000300f7,0x000000a9,0x00000000,0x000400fa,
0x000000a7,0x000000a8,0x000000a9,0x000200f8,0x000000a8,0x00050041,0x00000051,0x000000aa,
0x0000001a,0x00000050,0x0004003d,0x00000017,0x000000ab,0x000000aa,0x0004003d,0x00000017,
0x000000ac,0x00000059,0x00050085,0x00000017,0x000000ad,0x000000ab,0x000000ac,0x0004003d,
0x00000017,0x000000ae,0x0000009c,0x00050088,0x00000017,0x000000af,0x000000ad,0x000000ae,
0x0004003d,0x0000002f,0x000000b0,0x00000040,0x0005008e,0x0000002f,0x000000b1,0x000000b0,
0x000000af,0x0003003e,0x00000040,0x000000b1,0x000200f9,0x000000a9,0x000200f8,0x000000a9,
0x0004003d,0x00000017,0x000000b2,0x0000009f,0x00050041,0x00000051,0x000000b3,0x0000001a,
0x00000050,0x0004003d,0x00000017,0x000000b4,0x000000b3,0x0004003d,0x00000017,0x000000b5,
0x0000005a,0x00050085,0x00000017,0x000000b6,0x000000b4,0x000000b5,0x000500ba,0x00000012,
0x000000b7,0x000000b2,0x000000b6,0x000300f7,0x000000b9,0x00000000,0x000400fa,0x000000b7,
0x000000b8,0x000000b9,0x000200f8,0x000000b8,0x00050041,0x00000051,0x000000ba,0x0000001a,
0x00000050,0x0004003d,0x00000017,0x000000bb,0x000000ba,0x0004003d,0x00000017,0x000000bc,
0x0000005a,0x00050085,0x00000017,0x000000bd,0x000000bb,0x000000bc,0x0004003d,0x00000017,
0x000000be,0x0000009f,0x00050088,0x00000017,0x000000bf,0x000000bd,0x000000be,0x0004003d,
0x0000002f,0x000000c0,0x00000048,0x0005008e,0x0000002f,0x000000c1,0x000000c0,0x000000bf,
0x0003003e,0x00000048,0x000000c1,0x000200f9,0x000000b9,0x000200f8,0x000000b9,0x000200f9,
0x00000057,0x000200f8,0x00000057,0x0004003d,0x0000002f,0x000000c3,0x00000040,0x0004003d,
0x0000002f,0x000000c4,0x00000048,0x00050081,0x0000002f,0x000000c5,0x000000c3,0x000000c4,
0x0004003d,0x00000032,0x000000c7,0x000000c6,0x0004003d,0x00000007,0x000000c8,0x00000009,
0x00050062,0x00000037,0x000000c9,0x000000c7,0x000000c8,0x0008004f,0x0000002f,0x000000ca,
0x000000c9,0x000000c9,0x00000000,0x00000001,0x00000002,0x00050081,0x0000002f,0x000000cb,
0x000000c5,0x000000ca,0x0003003e,0x000000c2,0x000000cb,0x0004003d,0x00000032,0x000000cd,
0x000000cc,0x0004003d,0x00000007,0x000000ce,0x00000009,0x0004003d,0x0000002f,0x000000cf,
0x000000c2,0x00060050,0x0000002f,0x000000d1,0x00000054,0x00000054,0x00000054,0x00060050,
0x0000002f,0x000000d2,0x000000d0,0x000000d0,0x000000d0,0x0008000c,0x0000002f,0x000000d3,
0x00000001,0x0000002b,0x000000cf,0x000000d1,0x000000d2,0x00050051,0x00000017,0x000000d5,
0x000000d3,0x00000000,0x00050051,0x00000017,0x000000d6,0x000000d3,0x00000001,0x00050051,
0x00000017,0x000000d7,0x000000d3,0x00000002,0x00070050,0x00000037,0x000000d8,0x000000d5,
0x000000d6,0x000000d7,0x000000d4,0x00040063,0x000000cd,0x000000ce,0x000000d8,0x000100fd,
0x00010038,
};
+931
View File
@@ -0,0 +1,931 @@
// FSR Ray Regeneration as the native Windows Ray Reconstruction backend (experimental). See fsr_rr_backend.h.
//
// Data path of one evaluation (every hand-off is CPU-synchronized; no vkd3d-proton patch is needed):
// game queue : guides -> shared packet (D3D12 copies, as the native DLSS backend), signal, CPU waits
// vkd3d queue: one command list on a private vkd3d-proton device:
// packet -> staging images, fsr_rr_prepare (Vulkan, recorded through vkd3d's interop),
// AMD ffxDispatch, fsr_rr_compose (Vulkan), composed image -> packet output; CPU waits
// game queue : packet output -> the game's output texture, signal, CPU waits
// The conversion and composition follow the Linux adapter (reference 34e6b0fe, tools/d4r_fsr_rr_backend.inl).
#include "fsr_rr_backend.h"
#include "../d4r_fsr_rr.h"
#include "../d4r_vkd3d_interop.h"
#include "fsr_rr_passes.h"
#include "d3d12_command_hooks.h"
#include <volk.h>
#include "rr_bridge_replay.h"
#include <vulkan/vulkan_win32.h>
#include <dxgi1_4.h>
#include <chrono>
#include <cstdlib>
#include <mutex>
#include <tuple>
#include <vector>
#include <string>
namespace d4r::win::rr {
namespace {
using Clock = std::chrono::steady_clock;
double since(Clock::time_point start) { return std::chrono::duration<double, std::milli>(Clock::now() - start).count(); }
bool log_enabled() { static const bool on = std::getenv("D4R_FSR_RR_LOG") != nullptr; return on; }
LONG WINAPI crash_report(EXCEPTION_POINTERS* info) {
const auto code = info->ExceptionRecord->ExceptionCode;
if (code != 0xC0000005 && code != 0xC000001D && code != 0xC0000094 && code != 0xC00000FD) return EXCEPTION_CONTINUE_SEARCH;
char name[MAX_PATH] = "?"; HMODULE module = nullptr;
const void* at = info->ExceptionRecord->ExceptionAddress;
if (GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, static_cast<const char*>(at), &module))
GetModuleFileNameA(module, name, MAX_PATH);
std::fprintf(stderr, "FSR_RR crash: code=%08lx at %p (%s +0x%llx) info0=%llu info1=%p\n", code, at, name,
(unsigned long long)(static_cast<const char*>(at) - reinterpret_cast<const char*>(module)),
(unsigned long long)info->ExceptionRecord->ExceptionInformation[0], (void*)info->ExceptionRecord->ExceptionInformation[1]);
std::fflush(stderr);
return EXCEPTION_CONTINUE_SEARCH;
}
// The denoiser finds amdxc64.dll with GetModuleHandle, so in a process whose game device already loaded AMD's real
// amdxc64.dll (it always has: AMD's D3D12 driver does), the name resolves to AMD's driver component, which crashes
// on a vkd3d device. The loaded copy of AMD's DLL gets its GetModuleHandleA/W imports redirected in memory so that
// the name resolves to d4r's shim; every other lookup is forwarded. The DLL on disk is never modified.
HMODULE g_shimModule = nullptr;
HMODULE WINAPI module_handle_a(LPCSTR name) {
if (name && g_shimModule && !_stricmp(name, "amdxc64.dll")) return g_shimModule;
return GetModuleHandleA(name);
}
HMODULE WINAPI module_handle_w(LPCWSTR name) {
if (name && g_shimModule && !_wcsicmp(name, L"amdxc64.dll")) return g_shimModule;
return GetModuleHandleW(name);
}
bool redirect_amdxc(HMODULE module, HMODULE shim) {
g_shimModule = shim;
auto* base = reinterpret_cast<unsigned char*>(module);
auto* dos = reinterpret_cast<IMAGE_DOS_HEADER*>(base);
auto* nt = reinterpret_cast<IMAGE_NT_HEADERS*>(base + dos->e_lfanew);
const auto& directory = nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
if (!directory.VirtualAddress) return false;
unsigned patched = 0;
for (auto* descriptor = reinterpret_cast<IMAGE_IMPORT_DESCRIPTOR*>(base + directory.VirtualAddress); descriptor->Name; ++descriptor) {
if (_stricmp(reinterpret_cast<const char*>(base + descriptor->Name), "KERNEL32.dll") != 0) continue;
auto* lookup = reinterpret_cast<IMAGE_THUNK_DATA*>(base + (descriptor->OriginalFirstThunk ? descriptor->OriginalFirstThunk : descriptor->FirstThunk));
auto* address = reinterpret_cast<void**>(base + descriptor->FirstThunk);
for (; lookup->u1.AddressOfData; ++lookup, ++address) {
if (lookup->u1.Ordinal & IMAGE_ORDINAL_FLAG) continue;
const char* function = reinterpret_cast<IMAGE_IMPORT_BY_NAME*>(base + lookup->u1.AddressOfData)->Name;
void* replacement = !std::strcmp(function, "GetModuleHandleA") ? reinterpret_cast<void*>(&module_handle_a)
: !std::strcmp(function, "GetModuleHandleW") ? reinterpret_cast<void*>(&module_handle_w) : nullptr;
if (!replacement) continue;
DWORD old = 0;
if (!VirtualProtect(address, sizeof(void*), PAGE_READWRITE, &old)) return false;
*address = replacement; ++patched;
VirtualProtect(address, sizeof(void*), old, &old);
}
}
return patched != 0;
}
void trace(const char* what) { if (log_enabled()) { std::fprintf(stderr, "FSR_RR step: %s\n", what); std::fflush(stderr); } }
std::wstring widen(const char* text) {
if (!text || !*text) return {};
const int n = MultiByteToWideChar(CP_UTF8, 0, text, -1, nullptr, 0);
std::wstring out(size_t(n > 0 ? n - 1 : 0), L'\0');
if (n > 1) MultiByteToWideChar(CP_UTF8, 0, text, -1, out.data(), n);
return out;
}
std::wstring env_wide(const char* name) { return widen(std::getenv(name)); }
std::string narrow(const std::wstring& text) {
std::string out(size_t(WideCharToMultiByte(CP_UTF8, 0, text.c_str(), -1, nullptr, 0, nullptr, nullptr)), '\0');
if (!out.empty()) { WideCharToMultiByte(CP_UTF8, 0, text.c_str(), -1, out.data(), int(out.size()), nullptr, nullptr); out.pop_back(); }
return out;
}
// The vkd3d-proton core interface (include/vkd3d_core_interface.idl). The IDL declares no base interface, so the
// vtable starts at CreateDevice: slot 0, not 3.
const GUID CLSID_Core = {0xed53efad, 0xda21, 0x4d96, {0xa1, 0xbc, 0xe7, 0x34, 0xe0, 0x78, 0x87, 0x9c}};
const GUID IID_CoreInterface = {0x18da885c, 0xa7b5, 0x464e, {0xa1, 0x21, 0xcc, 0xb7, 0x5d, 0x4d, 0xfc, 0x31}};
struct CoreInterface {
struct Vtbl {
HRESULT(STDMETHODCALLTYPE* createDevice)(CoreInterface*, IUnknown*, D3D_FEATURE_LEVEL, REFIID, void**);
};
const Vtbl* vtbl;
};
// Row-major, row-vector 4x4 helpers (the NGX/FFX convention), in double. From the Linux adapter.
bool invert(const float (&m)[16], double (&out)[16]) {
double a[16]; for (int i = 0; i < 16; ++i) a[i] = m[i];
double inv[16];
inv[0] = a[5] * a[10] * a[15] - a[5] * a[11] * a[14] - a[9] * a[6] * a[15] + a[9] * a[7] * a[14] + a[13] * a[6] * a[11] - a[13] * a[7] * a[10];
inv[4] = -a[4] * a[10] * a[15] + a[4] * a[11] * a[14] + a[8] * a[6] * a[15] - a[8] * a[7] * a[14] - a[12] * a[6] * a[11] + a[12] * a[7] * a[10];
inv[8] = a[4] * a[9] * a[15] - a[4] * a[11] * a[13] - a[8] * a[5] * a[15] + a[8] * a[7] * a[13] + a[12] * a[5] * a[11] - a[12] * a[7] * a[9];
inv[12] = -a[4] * a[9] * a[14] + a[4] * a[10] * a[13] + a[8] * a[5] * a[14] - a[8] * a[6] * a[13] - a[12] * a[5] * a[10] + a[12] * a[6] * a[9];
inv[1] = -a[1] * a[10] * a[15] + a[1] * a[11] * a[14] + a[9] * a[2] * a[15] - a[9] * a[3] * a[14] - a[13] * a[2] * a[11] + a[13] * a[3] * a[10];
inv[5] = a[0] * a[10] * a[15] - a[0] * a[11] * a[14] - a[8] * a[2] * a[15] + a[8] * a[3] * a[14] + a[12] * a[2] * a[11] - a[12] * a[3] * a[10];
inv[9] = -a[0] * a[9] * a[15] + a[0] * a[11] * a[13] + a[8] * a[1] * a[15] - a[8] * a[3] * a[13] - a[12] * a[1] * a[11] + a[12] * a[3] * a[9];
inv[13] = a[0] * a[9] * a[14] - a[0] * a[10] * a[13] - a[8] * a[1] * a[14] + a[8] * a[2] * a[13] + a[12] * a[1] * a[10] - a[12] * a[2] * a[9];
inv[2] = a[1] * a[6] * a[15] - a[1] * a[7] * a[14] - a[5] * a[2] * a[15] + a[5] * a[3] * a[14] + a[13] * a[2] * a[7] - a[13] * a[3] * a[6];
inv[6] = -a[0] * a[6] * a[15] + a[0] * a[7] * a[14] + a[4] * a[2] * a[15] - a[4] * a[3] * a[14] - a[12] * a[2] * a[7] + a[12] * a[3] * a[6];
inv[10] = a[0] * a[5] * a[15] - a[0] * a[7] * a[13] - a[4] * a[1] * a[15] + a[4] * a[3] * a[13] + a[12] * a[1] * a[7] - a[12] * a[3] * a[5];
inv[14] = -a[0] * a[5] * a[14] + a[0] * a[6] * a[13] + a[4] * a[1] * a[14] - a[4] * a[2] * a[13] - a[12] * a[1] * a[6] + a[12] * a[2] * a[5];
inv[3] = -a[1] * a[6] * a[11] + a[1] * a[7] * a[10] + a[5] * a[2] * a[11] - a[5] * a[3] * a[10] - a[9] * a[2] * a[7] + a[9] * a[3] * a[6];
inv[7] = a[0] * a[6] * a[11] - a[0] * a[7] * a[10] - a[4] * a[2] * a[11] + a[4] * a[3] * a[10] + a[8] * a[2] * a[7] - a[8] * a[3] * a[6];
inv[11] = -a[0] * a[5] * a[11] + a[0] * a[7] * a[9] + a[4] * a[1] * a[11] - a[4] * a[3] * a[9] - a[8] * a[1] * a[7] + a[8] * a[3] * a[5];
inv[15] = a[0] * a[5] * a[10] - a[0] * a[6] * a[9] - a[4] * a[1] * a[10] + a[4] * a[2] * a[9] + a[8] * a[1] * a[6] - a[8] * a[2] * a[5];
const double det = a[0] * inv[0] + a[1] * inv[4] + a[2] * inv[8] + a[3] * inv[12];
if (!std::isfinite(det) || std::fabs(det) < 1e-30) return false;
for (int i = 0; i < 16; ++i) { out[i] = inv[i] / det; if (!std::isfinite(out[i])) return false; }
return true;
}
void multiply(const double (&a)[16], const double (&b)[16], double (&out)[16]) {
for (int r = 0; r < 4; ++r) for (int c = 0; c < 4; ++c) {
double sum = 0; for (int k = 0; k < 4; ++k) sum += a[r * 4 + k] * b[k * 4 + c]; out[r * 4 + c] = sum;
}
}
bool unorm(VkFormat format) {
return format == VK_FORMAT_R8G8B8A8_UNORM || format == VK_FORMAT_A2B10G10R10_UNORM_PACK32;
}
size_t resource_index(size_t plane) { return plane < 7 ? plane : plane + 2; }
// Same sampled-input formats as the native backend: the texel values reach Vulkan unchanged.
rr_bridge::Plane input_format(DXGI_FORMAT format, size_t plane) {
if (plane == Snapshot::Motion) {
if (format == DXGI_FORMAT_R16G16_FLOAT) return {nullptr, VK_FORMAT_R16G16_SFLOAT, 4};
if (format == DXGI_FORMAT_R32G32_FLOAT) return {nullptr, VK_FORMAT_R32G32_SFLOAT, 8};
} else if (plane == Snapshot::Depth || plane == Snapshot::HitDistance || plane == Snapshot::Disocclusion || plane == Snapshot::Subsurface) {
if (plane == Snapshot::Depth && (format == DXGI_FORMAT_R32_TYPELESS || format == DXGI_FORMAT_D32_FLOAT ||
format == DXGI_FORMAT_R32G8X24_TYPELESS || format == DXGI_FORMAT_D32_FLOAT_S8X24_UINT || format == DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS))
return {nullptr, VK_FORMAT_R32_SFLOAT, 4};
if (format == DXGI_FORMAT_R32_FLOAT) return {nullptr, VK_FORMAT_R32_SFLOAT, 4};
if (format == DXGI_FORMAT_R16_FLOAT) return {nullptr, VK_FORMAT_R16_SFLOAT, 2};
if (plane != Snapshot::Depth && format == DXGI_FORMAT_R8_UNORM) return {nullptr, VK_FORMAT_R8_UNORM, 1};
} else {
if (format == DXGI_FORMAT_R16G16B16A16_FLOAT) return {nullptr, VK_FORMAT_R16G16B16A16_SFLOAT, 8};
if (format == DXGI_FORMAT_R32G32B32A32_FLOAT) return {nullptr, VK_FORMAT_R32G32B32A32_SFLOAT, 16};
if (format == DXGI_FORMAT_R8G8B8A8_UNORM) return {nullptr, VK_FORMAT_R8G8B8A8_UNORM, 4};
if (format == DXGI_FORMAT_R10G10B10A2_UNORM) return {nullptr, VK_FORMAT_A2B10G10R10_UNORM_PACK32, 4};
if (plane == Snapshot::Roughness) {
if (format == DXGI_FORMAT_R16_FLOAT) return {nullptr, VK_FORMAT_R16_SFLOAT, 2};
if (format == DXGI_FORMAT_R32_FLOAT) return {nullptr, VK_FORMAT_R32_SFLOAT, 4};
if (format == DXGI_FORMAT_R8_UNORM) return {nullptr, VK_FORMAT_R8_UNORM, 1};
}
}
throw std::runtime_error("Unsupported FSR RR sampled input format");
}
DXGI_FORMAT copy_format(ID3D12Device* device, ID3D12Resource* texture, size_t plane) {
const auto desc = resource_desc(texture);
if (plane != Snapshot::Depth) return desc.Format;
D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprint{}; UINT rows = 0; UINT64 rowBytes = 0, bytes = 0;
device->GetCopyableFootprints(&desc, 0, 1, 0, &footprint, &rows, &rowBytes, &bytes);
const auto format = input_format(desc.Format, plane);
if (!bytes || bytes == UINT64_MAX || rows != desc.Height || rowBytes != desc.Width * format.bytes ||
footprint.Footprint.Width != desc.Width || footprint.Footprint.Height != desc.Height)
throw std::runtime_error("Unsupported FSR RR depth-plane footprint");
return footprint.Footprint.Format;
}
// The Vulkan entry points used on vkd3d-proton's VkDevice. Loaded from the loader by instance/device, never through
// volk's globals, which belong to the native backend's own devices.
struct VkApi {
PFN_vkGetDeviceProcAddr deviceProc = nullptr;
PFN_vkGetPhysicalDeviceMemoryProperties memoryProperties = nullptr;
PFN_vkGetPhysicalDeviceFormatProperties formatProperties = nullptr;
PFN_vkCreateBuffer createBuffer = nullptr; PFN_vkDestroyBuffer destroyBuffer = nullptr;
PFN_vkGetBufferMemoryRequirements bufferRequirements = nullptr;
PFN_vkAllocateMemory allocate = nullptr; PFN_vkFreeMemory free = nullptr;
PFN_vkBindBufferMemory bindBuffer = nullptr;
PFN_vkCreateImage createImage = nullptr; PFN_vkDestroyImage destroyImage = nullptr;
PFN_vkGetImageMemoryRequirements imageRequirements = nullptr; PFN_vkBindImageMemory bindImage = nullptr;
PFN_vkCmdCopyBufferToImage copyBufferToImage = nullptr; PFN_vkCmdCopyImageToBuffer copyImageToBuffer = nullptr;
PFN_vkCmdBlitImage blitImage = nullptr;
PFN_vkCmdPipelineBarrier barrier = nullptr;
PFN_vkGetMemoryWin32HandlePropertiesKHR handleProperties = nullptr;
PFN_vkDeviceWaitIdle waitIdle = nullptr;
};
struct StagingImage {
VkImage image = VK_NULL_HANDLE; VkDeviceMemory memory = VK_NULL_HANDLE; VkImageView view = VK_NULL_HANDLE;
VkFormat format = VK_FORMAT_UNDEFINED;
};
struct Intermediate {
ComPtr<ID3D12Resource> resource; D3D12_RESOURCE_DESC desc{};
D3D12_RESOURCE_STATES state = D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
VkImageView view = VK_NULL_HANDLE; VkImageLayout layout = VK_IMAGE_LAYOUT_GENERAL; VkImage image = VK_NULL_HANDLE;
};
enum Tex { LinearDepth, MotionT, NormalsT, DiffuseAlbedo, SpecularAlbedo, DiffuseT, SpecularT, DiffuseOut, SpecularOut, Residual, Composed, Widened, TexCount };
std::mutex hostMutex;
}
FsrConfig FsrConfig::from_environment() {
FsrConfig config;
config.vkd3dCore = env_wide("D4R_FSR_VKD3D_CORE");
config.denoiserDll = env_wide("D4R_FSR_RR_DLL");
config.amdxcShim = env_wide("D4R_FSR_AMDXC_SHIM");
config.cacheDir = env_wide("D4R_FSR_CACHE_DIR");
return config;
}
struct FsrBackend::Impl {
Creation creation;
rr_bridge::Layout layout;
ComPtr<ID3D12Device> d3d; // the game's device
ComPtr<ID3D12CommandQueue> queue;
ComPtr<ID3D12Resource> shared;
ComPtr<ID3D12Fence> fence;
ComPtr<ID3D12CommandAllocator> producerAllocator, consumerAllocator;
ComPtr<ID3D12GraphicsCommandList> producer, consumer;
Handle event;
uint64_t sequence = 0;
bool pending = false, failed = false;
// --- private vkd3d-proton side ---
HMODULE core = nullptr;
ComPtr<ID3D12Device> vdev;
ComPtr<ID3D12CommandQueue> vqueue;
ComPtr<ID3D12CommandAllocator> vallocator;
ComPtr<ID3D12GraphicsCommandList> vlist;
ComPtr<ID3D12Fence> vfence; Handle vevent; uint64_t vsequence = 0;
ComPtr<ID3D12DXVKInteropDevice1> interop;
VkInstance vkInstance = VK_NULL_HANDLE; VkPhysicalDevice vkPhysical = VK_NULL_HANDLE; VkDevice vk = VK_NULL_HANDLE;
VkQueue vkQueue = VK_NULL_HANDLE; uint32_t vkFamily = 0;
VkApi api;
D4rFsrRrPasses passes;
FsrRrRuntime runtime;
ffxContext context = nullptr; uint32_t signals = 0;
VkBuffer packetBuffer = VK_NULL_HANDLE; VkDeviceMemory packetMemory = VK_NULL_HANDLE;
std::array<StagingImage, 11> staging{};
std::array<Intermediate, TexCount> textures{};
float previousView[16] = {}; bool havePrevious = false;
uint32_t frameIndex = 0;
FsrConfig config;
HMODULE shimModule = nullptr;
FsrBackend::SuperSampling superSampling;
std::array<unsigned, 4> srRenderLimits{}; // min width/height, max width/height from the SR library
// D4R_FSR_RR_LOG: GPU timestamps of the vkd3d list (begin, after prepare, after denoise, end)
ComPtr<ID3D12QueryHeap> stamps; ComPtr<ID3D12Resource> stampReadback; UINT64 stampFrequency = 0;
void stamp(UINT index) { if (stamps) vlist->EndQuery(stamps.Get(), D3D12_QUERY_TYPE_TIMESTAMP, index); }
ComPtr<ID3D12Resource> denoised; // game-device copy of the denoised colour, the Super Sampling stage's input
D3D12_RESOURCE_STATES denoisedState = D3D12_RESOURCE_STATE_COMMON;
Impl(ID3D12Device* device, const Creation& c, const FsrConfig& cfg)
: creation(c), layout(c.width, c.height, c.outWidth, c.outHeight), d3d(device), config(cfg) {
// Reserve each guide's largest texel size once, as the native backend does, so format changes keep the packet.
VkDeviceSize offset = 0;
for (auto& plane : layout.planes) {
plane.offset = (offset + 511) & ~VkDeviceSize(511);
plane.pitch = (VkDeviceSize(layout.rw) * 16 + 255) & ~VkDeviceSize(255);
offset = plane.offset + plane.pitch * layout.rh;
}
layout.outputOffset = (offset + 511) & ~VkDeviceSize(511);
layout.outputPitch = (VkDeviceSize(layout.ow) * 16 + 255) & ~VkDeviceSize(255);
layout.bytes = layout.outputOffset + layout.outputPitch * layout.oh;
}
~Impl() {
if (vk && api.waitIdle) (void)api.waitIdle(vk);
if (vqueue && vfence) {
const uint64_t value = ++vsequence;
if (SUCCEEDED(vqueue->Signal(vfence.Get(), value)) && SUCCEEDED(vfence->SetEventOnCompletion(value, vevent.value)))
WaitForSingleObject(vevent.value, 10000);
}
if (context) runtime.destroyContext(&context, nullptr);
if (vk) {
for (auto& s : staging) destroy_staging(s);
for (auto& t : textures) if (t.view) passes.destroyView(vk, t.view, nullptr);
passes.destroy();
if (packetBuffer) api.destroyBuffer(vk, packetBuffer, nullptr);
if (packetMemory) api.free(vk, packetMemory, nullptr);
}
// vkd3d-proton's DLL stays loaded for the process: its device objects are released by their ComPtrs.
}
void destroy_staging(StagingImage& s) {
if (s.view) passes.destroyView(vk, s.view, nullptr);
if (s.image) api.destroyImage(vk, s.image, nullptr);
if (s.memory) api.free(vk, s.memory, nullptr);
s = StagingImage{};
}
uint32_t memory_type(uint32_t bits, VkMemoryPropertyFlags flags) {
VkPhysicalDeviceMemoryProperties properties{}; api.memoryProperties(vkPhysical, &properties);
for (uint32_t i = 0; i < properties.memoryTypeCount; ++i)
if ((bits & (1u << i)) && (properties.memoryTypes[i].propertyFlags & flags) == flags) return i;
throw std::runtime_error("No compatible FSR RR Vulkan memory type");
}
void check(VkResult r, const char* what) {
if (r != VK_SUCCESS) throw std::runtime_error(std::string(what) + " VkResult=" + std::to_string(r));
}
void initialize() {
if (!d3d) throw std::runtime_error("Null FSR RR D3D12 device");
if ((creation.flags & 3u) != 3u) throw std::runtime_error("FSR RR requires HDR and render-resolution motion vectors");
if (creation.flags & 4u) throw std::runtime_error("FSR RR does not convert jittered motion vectors");
if (creation.quality < 0 || creation.quality > 5) throw std::runtime_error("Unsupported RR quality mode");
if (config.vkd3dCore.empty() || config.denoiserDll.empty() || config.amdxcShim.empty())
throw std::runtime_error("FSR RR needs D4R_FSR_VKD3D_CORE, D4R_FSR_RR_DLL and D4R_FSR_AMDXC_SHIM");
// --- the game-device packet, fence and copy lists (as the native backend) ---
shared = make_buffer(d3d.Get(), layout.bytes, D3D12_HEAP_TYPE_DEFAULT, D3D12_HEAP_FLAG_SHARED, D3D12_RESOURCE_STATE_COMMON);
dx(d3d->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence)), "FSR RR fence");
event.value = CreateEventW(nullptr, FALSE, FALSE, nullptr);
vevent.value = CreateEventW(nullptr, FALSE, FALSE, nullptr);
if (!event.value || !vevent.value) throw std::runtime_error("FSR RR event creation failed");
auto createList = [&](ID3D12Device* device, auto& allocator, auto& list) {
dx(device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&allocator)), "FSR RR allocator");
dx(device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, allocator.Get(), nullptr, IID_PPV_ARGS(&list)), "FSR RR command list");
dx(list->Close(), "FSR RR initial close");
};
createList(d3d.Get(), producerAllocator, producer); createList(d3d.Get(), consumerAllocator, consumer);
Handle memoryHandle;
dx(d3d->CreateSharedHandle(shared.Get(), nullptr, GENERIC_ALL, nullptr, &memoryHandle.value), "FSR RR packet handle");
if (log_enabled()) AddVectoredExceptionHandler(1, crash_report);
trace("before start_vkd3d");
start_vkd3d();
trace("before import_packet");
import_packet(memoryHandle.value);
trace("before vkd3d list");
createList(vdev.Get(), vallocator, vlist);
trace("before create_intermediates");
create_intermediates();
trace("before create_context");
create_context();
}
void start_vkd3d() {
// vkd3d-proton's own environment: the dxil-spirv RDNA3 WMMA/FP8 emulation, and a cache for its shaders.
SetEnvironmentVariableA("DXIL_SPIRV_CONFIG", "wmma_rdna3_workaround");
if (!config.cacheDir.empty() && !std::getenv("VKD3D_SHADER_CACHE_PATH")) {
CreateDirectoryW(config.cacheDir.c_str(), nullptr);
SetEnvironmentVariableW(L"VKD3D_SHADER_CACHE_PATH", config.cacheDir.c_str());
}
// The denoiser consults amdxc64.dll only when it is already loaded: ours, never AMD's.
shimModule = LoadLibraryW(config.amdxcShim.c_str());
if (!shimModule) throw std::runtime_error("FSR RR amdxc64 shim could not be loaded");
core = LoadLibraryExW(config.vkd3dCore.c_str(), nullptr, LOAD_WITH_ALTERED_SEARCH_PATH);
if (!core) throw std::runtime_error("FSR RR vkd3d-proton core could not be loaded");
using GetInterface = HRESULT(WINAPI*)(REFCLSID, REFIID, void**);
auto getInterface = reinterpret_cast<GetInterface>(reinterpret_cast<void*>(GetProcAddress(core, "D3D12GetInterface")));
CoreInterface* iface = nullptr;
if (!getInterface || FAILED(getInterface(CLSID_Core, IID_CoreInterface, reinterpret_cast<void**>(&iface))) || !iface)
throw std::runtime_error("FSR RR vkd3d-proton core interface unavailable");
ComPtr<IDXGIFactory4> factory; dx(CreateDXGIFactory1(IID_PPV_ARGS(&factory)), "FSR RR DXGI factory");
ComPtr<IDXGIAdapter1> adapter; dx(factory->EnumAdapterByLuid(adapter_luid(d3d.Get()), IID_PPV_ARGS(&adapter)), "FSR RR adapter");
dx(iface->vtbl->createDevice(iface, adapter.Get(), D3D_FEATURE_LEVEL_12_0, IID_PPV_ARGS(&vdev)), "FSR RR vkd3d device");
trace("vkd3d device created");
D3D12_COMMAND_QUEUE_DESC queueDesc{}; queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
dx(vdev->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&vqueue)), "FSR RR vkd3d queue");
dx(vdev->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&vfence)), "FSR RR vkd3d fence");
if (log_enabled()) {
D3D12_QUERY_HEAP_DESC heap{}; heap.Type = D3D12_QUERY_HEAP_TYPE_TIMESTAMP; heap.Count = 4;
if (SUCCEEDED(vdev->CreateQueryHeap(&heap, IID_PPV_ARGS(&stamps))) && SUCCEEDED(vqueue->GetTimestampFrequency(&stampFrequency)) && stampFrequency)
stampReadback = make_buffer(vdev.Get(), 4 * sizeof(UINT64), D3D12_HEAP_TYPE_READBACK, D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);
else stamps.Reset();
}
trace("vkd3d queue and fence created");
dx(vdev->QueryInterface(IID_PPV_ARGS(&interop)), "FSR RR vkd3d Vulkan interop");
dx(interop->GetVulkanHandles(&vkInstance, &vkPhysical, &vk), "FSR RR Vulkan handles");
dx(interop->GetVulkanQueueInfo(vqueue.Get(), &vkQueue, &vkFamily), "FSR RR Vulkan queue info");
trace("interop and handles ok");
HMODULE vulkan = GetModuleHandleA("vulkan-1.dll"); if (!vulkan) vulkan = LoadLibraryA("vulkan-1.dll");
auto instanceProc = vulkan ? reinterpret_cast<PFN_vkGetInstanceProcAddr>(reinterpret_cast<void*>(GetProcAddress(vulkan, "vkGetInstanceProcAddr"))) : nullptr;
if (!instanceProc) throw std::runtime_error("FSR RR Vulkan loader unavailable");
api.deviceProc = reinterpret_cast<PFN_vkGetDeviceProcAddr>(instanceProc(vkInstance, "vkGetDeviceProcAddr"));
api.memoryProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceMemoryProperties>(instanceProc(vkInstance, "vkGetPhysicalDeviceMemoryProperties"));
api.formatProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceFormatProperties>(instanceProc(vkInstance, "vkGetPhysicalDeviceFormatProperties"));
auto physicalProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceProperties>(instanceProc(vkInstance, "vkGetPhysicalDeviceProperties"));
if (!api.deviceProc || !api.memoryProperties || !api.formatProperties || !physicalProperties) throw std::runtime_error("FSR RR Vulkan entry points missing");
#define D4R_VK(member, name) api.member = reinterpret_cast<decltype(api.member)>(api.deviceProc(vk, "vk" #name)); \
if (!api.member) throw std::runtime_error("FSR RR Vulkan entry point missing: vk" #name)
D4R_VK(createBuffer, CreateBuffer); D4R_VK(destroyBuffer, DestroyBuffer); D4R_VK(bufferRequirements, GetBufferMemoryRequirements);
D4R_VK(allocate, AllocateMemory); D4R_VK(free, FreeMemory); D4R_VK(bindBuffer, BindBufferMemory);
D4R_VK(createImage, CreateImage); D4R_VK(destroyImage, DestroyImage); D4R_VK(imageRequirements, GetImageMemoryRequirements);
D4R_VK(bindImage, BindImageMemory); D4R_VK(copyBufferToImage, CmdCopyBufferToImage); D4R_VK(copyImageToBuffer, CmdCopyImageToBuffer);
D4R_VK(blitImage, CmdBlitImage);
D4R_VK(barrier, CmdPipelineBarrier); D4R_VK(handleProperties, GetMemoryWin32HandlePropertiesKHR); D4R_VK(waitIdle, DeviceWaitIdle);
#undef D4R_VK
trace("vulkan entry points loaded");
VkPhysicalDeviceProperties limits{}; physicalProperties(vkPhysical, &limits);
if (!passes.init(vk, api.deviceProc, limits.limits.maxPushConstantsSize))
throw std::runtime_error("FSR RR conversion passes could not be created");
trace("passes created");
if (!runtime.load(config.denoiserDll)) throw std::runtime_error(std::string("FSR RR denoiser DLL: ") + fsr_rr_stage_name(runtime.stage));
if (!redirect_amdxc(runtime.module, shimModule)) throw std::runtime_error("FSR RR could not redirect the denoiser's amdxc64 lookup");
trace("amdxc64 lookup redirected to the d4r shim");
trace("before message callback");
runtime.set_message_callback([](uint32_t type, const wchar_t* message) {
std::fprintf(stderr, "FSR RR runtime %s: %ls\n", type == FFX_API_MESSAGE_TYPE_WARNING ? "warning" : "error", message);
}, FFX_API_CONFIGURE_GLOBALDEBUG_LEVEL_WARNINGS);
trace("denoiser dll loaded");
trace("before query_providers");
if (!runtime.query_providers(vdev.Get()))
throw std::runtime_error(std::string("FSR RR provider: ") + fsr_rr_stage_name(runtime.stage));
trace("providers queried");
if (log_enabled()) std::fprintf(stderr, "FSR_RR provider \"%s\" on vkd3d-proton\n", runtime.providerName.c_str());
}
void import_packet(HANDLE resource) {
VkExternalMemoryBufferCreateInfo external{VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO};
external.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE_BIT;
VkBufferCreateInfo bi{VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, &external}; bi.size = layout.bytes;
bi.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
check(api.createBuffer(vk, &bi, nullptr, &packetBuffer), "FSR RR packet buffer");
VkMemoryRequirements req{}; api.bufferRequirements(vk, packetBuffer, &req);
VkMemoryWin32HandlePropertiesKHR handleProperties{VK_STRUCTURE_TYPE_MEMORY_WIN32_HANDLE_PROPERTIES_KHR};
check(api.handleProperties(vk, VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE_BIT, resource, &handleProperties), "FSR RR packet memory types");
const uint32_t type = memory_type(req.memoryTypeBits & handleProperties.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
VkImportMemoryWin32HandleInfoKHR imported{VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR};
imported.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE_BIT; imported.handle = resource;
VkMemoryDedicatedAllocateInfo dedicated{VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO, &imported};
dedicated.buffer = packetBuffer;
VkMemoryAllocateInfo ai{VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, &dedicated};
ai.allocationSize = req.size; ai.memoryTypeIndex = type;
check(api.allocate(vk, &ai, nullptr, &packetMemory), "FSR RR packet import");
check(api.bindBuffer(vk, packetBuffer, packetMemory, 0), "FSR RR packet bind");
}
void create_intermediate(int i) {
auto& t = textures[i];
t.desc = {}; t.desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
t.desc.Width = creation.width; t.desc.Height = creation.height; t.desc.DepthOrArraySize = 1; t.desc.MipLevels = 1;
t.desc.Format = i == LinearDepth ? DXGI_FORMAT_R32_FLOAT : i == Widened ? DXGI_FORMAT_R32G32B32A32_FLOAT : DXGI_FORMAT_R16G16B16A16_FLOAT;
t.desc.SampleDesc.Count = 1; t.desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
D3D12_HEAP_PROPERTIES heap{}; heap.Type = D3D12_HEAP_TYPE_DEFAULT;
dx(vdev->CreateCommittedResource(&heap, D3D12_HEAP_FLAG_NONE, &t.desc, t.state, nullptr, IID_PPV_ARGS(&t.resource)), "FSR RR intermediate texture");
UINT64 handle = 0, offset = 0; VkFormat format = VK_FORMAT_UNDEFINED;
dx(interop->GetVulkanResourceInfo1(t.resource.Get(), &handle, &offset, &format), "FSR RR intermediate Vulkan info");
if (!handle) throw std::runtime_error("FSR RR intermediate has no Vulkan image");
t.image = reinterpret_cast<VkImage>(handle);
interop->GetVulkanImageLayout(t.resource.Get(), D3D12_RESOURCE_STATE_UNORDERED_ACCESS, &t.layout);
VkImageViewCreateInfo info{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};
info.image = t.image; info.viewType = VK_IMAGE_VIEW_TYPE_2D; info.format = format;
info.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
check(passes.createView(vk, &info, nullptr, &t.view), "FSR RR intermediate view");
}
void create_intermediates() {
// The float output image is needed only for an FSR-only RGBA32F output.
for (int i = 0; i < Widened; ++i) create_intermediate(i);
}
// AMD's tweakable settings (FfxApiConfigureDenoiserKey 1-6). Defaults are logged; D4R_FSR_RR_KEY_<n>=<float>
// overrides key n (diagnostics) and D4R_FSR_RR_STABILITY_BIAS ([RayReconstruction] FsrStabilityBias) sets key 2.
// AMD's default stability bias of 1 weights history heavily, which blurs fine detail on some content.
void apply_denoiser_keys() {
static const char* const names[7] = {"", "cross bilateral normal strength", "stability bias", "max radiance",
"radiance clip std k", "gaussian kernel relaxation", "disocclusion threshold"};
std::string defaults;
for (uint64_t key = 1; key <= 6; ++key) {
float value = 0.0f;
ffxQueryDescDenoiserGetDefaultKeyValue query = {};
query.header.type = FFX_API_QUERY_DESC_TYPE_DENOISER_GET_DEFAULT_KEYVALUE;
query.key = key; query.count = 1; query.data = &value;
char text[96];
if (runtime.query(&context, &query.header) == FFX_API_RETURN_OK)
std::snprintf(text, sizeof(text), "%s%s %g", defaults.empty() ? "" : ", ", names[key], value);
else
std::snprintf(text, sizeof(text), "%s%s ?", defaults.empty() ? "" : ", ", names[key]);
defaults += text;
char variable[32];
std::snprintf(variable, sizeof(variable), "D4R_FSR_RR_KEY_%u", unsigned(key));
std::string override = std::getenv(variable) ? std::getenv(variable) : "";
if (override.empty() && key == 2 && std::getenv("D4R_FSR_RR_STABILITY_BIAS") && std::string(std::getenv("D4R_FSR_RR_STABILITY_BIAS")) != "auto")
override = std::getenv("D4R_FSR_RR_STABILITY_BIAS");
if (override.empty()) continue;
char* end = nullptr;
const float wanted = std::strtof(override.c_str(), &end);
if (end == override.c_str() || !std::isfinite(wanted)) {
std::fprintf(stderr, "FSR_RR: %s override '%s' is not a number; ignored\n", names[key], override.c_str());
continue;
}
ffxConfigureDescDenoiserKeyValue configure = {};
configure.header.type = FFX_API_CONFIGURE_DESC_TYPE_DENOISER_KEYVALUE;
configure.key = key; configure.count = 1; configure.data = &wanted;
const ffxReturnCode_t configured = runtime.configure(&context, &configure.header);
std::fprintf(stderr, "FSR_RR: %s set to %g (%s)\n", names[key], wanted, fsr_rr_return_name(configured));
}
if (log_enabled()) std::fprintf(stderr, "FSR_RR denoiser defaults: %s\n", defaults.c_str());
}
// [RayReconstruction] FsrDetail (D4R_FSR_RR_DETAIL, 0-1): the share of the colour that bypasses the denoiser through
// the residual; D4R_FSR_RR_DEMOD_FLOOR (default 0.1): the smallest albedo a signal is divided by; D4R_FSR_RR_HALO_LIMIT
// (default 1, 0 = off): caps each denoised lobe at that multiple of the brightest noisy input around the texel.
static float env_float(const char* name, float fallback, float low, float high) {
const char* text = std::getenv(name);
if (!text || !*text) return fallback;
char* end = nullptr; const float value = std::strtof(text, &end);
if (end == text || !std::isfinite(value) || value < low || value > high)
throw std::runtime_error(std::string(name) + " must be a number between " + std::to_string(low) + " and " + std::to_string(high));
return value;
}
void create_context() {
const auto start = Clock::now();
// Diffuse is a direct signal (the game's diffuse hit distance is not carried); specular is indirect only
// when the game passes a specular hit distance, which decides the context's signal set once.
signals = FFX_DENOISER_SIGNAL_DIRECT_DIFFUSE | FFX_DENOISER_SIGNAL_DIRECT_SPECULAR;
const ffxReturnCode_t created = fsr_rr_create_context(runtime, vdev.Get(), creation.width, creation.height, signals, 0, context);
if (created != FFX_API_RETURN_OK) throw std::runtime_error(std::string("FSR RR ffxCreateContext failed: ") + fsr_rr_return_name(created));
apply_denoiser_keys();
if (log_enabled()) std::fprintf(stderr, "FSR_RR context created in %.0f ms (%ux%u, signals 0x%x)\n", since(start), creation.width, creation.height, signals);
}
void validate_frame(const Snapshot& frame) {
if (failed) throw std::runtime_error("FSR RR backend is poisoned after failed submission");
if (frame.renderWidth < 64 || frame.renderHeight < 64 || frame.renderWidth > creation.width || frame.renderHeight > creation.height)
throw std::runtime_error("RR render extent must be at least 64 and within creation geometry");
// The exposure texture belongs to the Super Sampling stage (it receives it); the denoiser does not use it.
if (frame.resources[Snapshot::Exposure] && !superSampling) throw std::runtime_error("FSR RR exposure texture needs the Super Sampling stage");
// The old dynamic fixture shrank below NVIDIA's Quality minimum, triggering BAD00005 and a teardown fault.
// Query the SR library's actual range and reject unsupported sizes before recording GPU work.
if (superSampling && (frame.renderWidth < srRenderLimits[0] || frame.renderHeight < srRenderLimits[1] ||
frame.renderWidth > srRenderLimits[2] || frame.renderHeight > srRenderLimits[3]))
throw std::runtime_error("FSR RR render size is outside the Super Sampling dynamic render range");
if (frame.exposureScale != 1 || frame.depthType || frame.normalsType || frame.roughnessType || frame.diffuseType || frame.specularType)
throw std::runtime_error("FSR RR guide encoding or exposure scale requires conversion");
if (frame.resources[Snapshot::Disocclusion] || frame.resources[Snapshot::Subsurface])
throw std::runtime_error("FSR RR does not use the disocclusion or subsurface guides");
if (!superSampling && (frame.renderWidth != creation.outWidth || frame.renderHeight != creation.outHeight))
throw std::runtime_error("FSR RR upscaling needs a Super Sampling stage (none is installed) or output size == render size");
for (size_t i = 0; i < Snapshot::Count; ++i) {
if (i == Snapshot::Exposure) continue;
auto* resource = frame.resources[i].Get();
if (!resource && i >= Snapshot::HitDistance) continue;
if (!resource) throw std::runtime_error("Missing required FSR RR texture");
ComPtr<ID3D12Device> owner; dx(resource->GetDevice(IID_PPV_ARGS(&owner)), "FSR RR texture device");
if (owner.Get() != d3d.Get()) throw std::runtime_error("FSR RR texture belongs to another device");
const auto desc = resource_desc(resource);
if (i != Snapshot::Output) (void)input_format(desc.Format, i);
if (i == Snapshot::Depth) {
(void)copy_format(d3d.Get(), resource, i);
if ((desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) &&
(frame.origins[i][0] || frame.origins[i][1] || frame.renderWidth != desc.Width || frame.renderHeight != desc.Height))
throw std::runtime_error("FSR RR depth-stencil requires a full depth-plane rectangle");
}
const uint32_t width = i == Snapshot::Output ? layout.ow : frame.renderWidth;
const uint32_t height = i == Snapshot::Output ? layout.oh : frame.renderHeight;
if (desc.Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE2D || (i == Snapshot::Output && !superSampling &&
desc.Format != DXGI_FORMAT_R16G16B16A16_FLOAT && desc.Format != DXGI_FORMAT_R32G32B32A32_FLOAT) ||
desc.SampleDesc.Count != 1 || desc.DepthOrArraySize != 1 ||
frame.origins[i][0] > desc.Width || width > desc.Width - frame.origins[i][0] ||
frame.origins[i][1] > desc.Height || height > desc.Height - frame.origins[i][1])
throw std::runtime_error("Unsupported FSR RR texture format or subrect (FSR-only output must be RGBA16F or RGBA32F)");
if (i == Snapshot::Output && !superSampling && desc.Format == DXGI_FORMAT_R32G32B32A32_FLOAT) {
VkFormatProperties source{}, destination{};
api.formatProperties(vkPhysical, VK_FORMAT_R16G16B16A16_SFLOAT, &source);
api.formatProperties(vkPhysical, VK_FORMAT_R32G32B32A32_SFLOAT, &destination);
if (!(source.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT) ||
!(destination.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT))
throw std::runtime_error("FSR RR output widening requires half-to-float blit support");
}
if (i == Snapshot::Output && !(desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS))
throw std::runtime_error("RR output lacks UAV capability");
}
for (size_t i = 0; i < Snapshot::Count; ++i)
if (i != Snapshot::Output && frame.resources[i] == frame.resources[Snapshot::Output]) throw std::runtime_error("RR input/output alias is unsupported");
}
void ensure_staging(size_t plane) {
auto& s = staging[plane]; const VkFormat wanted = layout.planes[plane].format;
if (s.image && s.format == wanted) return;
destroy_staging(s);
VkImageCreateInfo ci{VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO}; ci.imageType = VK_IMAGE_TYPE_2D; ci.format = wanted;
ci.extent = {creation.width, creation.height, 1}; ci.mipLevels = ci.arrayLayers = 1; ci.samples = VK_SAMPLE_COUNT_1_BIT;
ci.tiling = VK_IMAGE_TILING_OPTIMAL; ci.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
ci.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
check(api.createImage(vk, &ci, nullptr, &s.image), "FSR RR staging image");
VkMemoryRequirements req{}; api.imageRequirements(vk, s.image, &req);
VkMemoryAllocateInfo ai{VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO}; ai.allocationSize = req.size;
ai.memoryTypeIndex = memory_type(req.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
check(api.allocate(vk, &ai, nullptr, &s.memory), "FSR RR staging memory");
check(api.bindImage(vk, s.image, s.memory, 0), "FSR RR staging bind");
VkImageViewCreateInfo vi{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO}; vi.image = s.image; vi.viewType = VK_IMAGE_VIEW_TYPE_2D;
vi.format = wanted; vi.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
check(passes.createView(vk, &vi, nullptr, &s.view), "FSR RR staging view");
s.format = wanted;
}
void wait_vkd3d() {
const uint64_t value = ++vsequence;
dx(vqueue->Signal(vfence.Get(), value), "FSR RR vkd3d signal");
dx(vfence->SetEventOnCompletion(value, vevent.value), "FSR RR vkd3d completion event");
if (WaitForSingleObject(vevent.value, 60000) != WAIT_OBJECT_0) throw std::runtime_error("FSR RR vkd3d completion timeout");
if (vfence->GetCompletedValue() == UINT64_MAX) throw std::runtime_error("FSR RR vkd3d device removed");
}
void wait_game(ID3D12CommandQueue* q, uint64_t value) {
dx(q->Signal(fence.Get(), value), "FSR RR game signal");
dx(fence->SetEventOnCompletion(value, event.value), "FSR RR game completion event");
if (WaitForSingleObject(event.value, 10000) != WAIT_OBJECT_0) throw std::runtime_error("FSR RR D3D12 completion timeout");
if (fence->GetCompletedValue() == UINT64_MAX) throw std::runtime_error("FSR RR D3D12 device removed");
}
void validate(ID3D12CommandQueue* q, const Snapshot& frame, const std::array<ResourceAccess, Snapshot::Count>& access) {
if (failed) throw std::runtime_error("FSR RR backend is poisoned after failed submission");
if (!q) throw std::runtime_error("Null RR submission queue");
D3D12_COMMAND_QUEUE_DESC queueDesc{};
#if defined(__MINGW32__)
q->GetDesc(&queueDesc);
#else
queueDesc = q->GetDesc();
#endif
if (queueDesc.Type != D3D12_COMMAND_LIST_TYPE_DIRECT) throw std::runtime_error("RR requires a direct queue");
ComPtr<ID3D12Device> owner; dx(q->GetDevice(IID_PPV_ARGS(&owner)), "RR queue device");
if (owner.Get() != d3d.Get()) throw std::runtime_error("RR queue belongs to another device");
for (size_t i = 0; i < access.size(); ++i)
if (frame.resources[i] && (access[i].pending_split || access[i].inherited)) throw std::runtime_error("Unresolved RR texture state");
validate_frame(frame);
}
void evaluate(ID3D12CommandQueue* q, const Snapshot& frame, const std::array<ResourceAccess, Snapshot::Count>& access) {
validate(q, frame, access);
failed = true;
const auto begin = Clock::now();
layout.rw = frame.renderWidth; layout.rh = frame.renderHeight;
for (size_t i = 0; i < layout.planes.size(); ++i) {
const auto resource = resource_index(i);
layout.planes[i].enabled = frame.resources[resource] != nullptr;
if (!layout.planes[i].enabled) continue;
const auto format = input_format(resource_desc(frame.resources[resource].Get()).Format, resource);
layout.planes[i].format = format.format; layout.planes[i].bytes = format.bytes;
}
const bool floatOutput = !superSampling && resource_desc(frame.resources[Snapshot::Output].Get()).Format == DXGI_FORMAT_R32G32B32A32_FLOAT;
if (floatOutput && !textures[Widened].resource) create_intermediate(Widened);
layout.outputBytes = floatOutput ? 16 : 8;
layout.outputFormat = floatOutput ? VK_FORMAT_R32G32B32A32_SFLOAT : VK_FORMAT_R16G16B16A16_SFLOAT;
const auto outputFormat = floatOutput ? DXGI_FORMAT_R32G32B32A32_FLOAT : DXGI_FORMAT_R16G16B16A16_FLOAT;
commands::InternalScope internal;
// --- 1. the game's guides into the shared packet ---
dx(producerAllocator->Reset(), "FSR RR producer allocator reset"); dx(producer->Reset(producerAllocator.Get(), nullptr), "FSR RR producer reset");
transition(producer.Get(), shared.Get(), D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_COPY_DEST);
for (size_t i = 0; i < layout.planes.size(); ++i) {
const auto resource = resource_index(i);
auto* texture = frame.resources[resource].Get(); const auto& plane = layout.planes[i];
if (!plane.enabled) continue;
texture_copy_barrier(producer.Get(), texture, access[resource], false, false);
auto source = rr_bridge::D3D12Frames::texture(texture);
auto dest = rr_bridge::D3D12Frames::placed(shared.Get(), plane.offset, plane.pitch, copy_format(d3d.Get(), texture, resource), layout.rw, layout.rh);
const auto x = frame.origins[resource][0], y = frame.origins[resource][1];
D3D12_BOX box{x, y, 0, x + layout.rw, y + layout.rh, 1};
const bool depthStencil = (resource_desc(texture).Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) != 0;
producer->CopyTextureRegion(&dest, 0, 0, 0, &source, depthStencil ? nullptr : &box);
texture_copy_barrier(producer.Get(), texture, access[resource], false, true);
}
transition(producer.Get(), shared.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COMMON);
dx(producer->Close(), "FSR RR producer close");
ID3D12CommandList* inputs[] = {producer.Get()}; q->ExecuteCommandLists(1, inputs);
pending = true; queue = q;
wait_game(q, ++sequence);
const double producedMs = since(begin);
// --- 2. conversion, denoising and composition on the vkd3d device ---
run_vkd3d(frame);
const double vkd3dMs = since(begin) - producedMs;
// --- 3. the packet's output into the game's output texture ---
dx(consumerAllocator->Reset(), "FSR RR consumer allocator reset"); dx(consumer->Reset(consumerAllocator.Get(), nullptr), "FSR RR consumer reset");
transition(consumer.Get(), shared.Get(), D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_COPY_SOURCE);
auto* output = frame.resources[Snapshot::Output].Get();
if (superSampling) {
// the denoised colour at render size goes to a game-device texture; the Super Sampling stage upscales it
if (!denoised) {
D3D12_HEAP_PROPERTIES heap{}; heap.Type = D3D12_HEAP_TYPE_DEFAULT;
D3D12_RESOURCE_DESC desc{}; desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; desc.Width = creation.width; desc.Height = creation.height;
desc.DepthOrArraySize = desc.MipLevels = 1; desc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT; desc.SampleDesc.Count = 1;
desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
dx(d3d->CreateCommittedResource(&heap, D3D12_HEAP_FLAG_NONE, &desc, denoisedState, nullptr, IID_PPV_ARGS(&denoised)), "FSR RR denoised texture");
}
transition(consumer.Get(), denoised.Get(), denoisedState, D3D12_RESOURCE_STATE_COPY_DEST);
auto source = rr_bridge::D3D12Frames::placed(shared.Get(), layout.outputOffset, layout.outputPitch, outputFormat, layout.rw, layout.rh);
auto dest = rr_bridge::D3D12Frames::texture(denoised.Get());
consumer->CopyTextureRegion(&dest, 0, 0, 0, &source, nullptr);
denoisedState = D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE;
transition(consumer.Get(), denoised.Get(), D3D12_RESOURCE_STATE_COPY_DEST, denoisedState);
} else {
texture_copy_barrier(consumer.Get(), output, access[Snapshot::Output], true, false);
auto source = rr_bridge::D3D12Frames::placed(shared.Get(), layout.outputOffset, layout.outputPitch, outputFormat, layout.ow, layout.oh);
auto dest = rr_bridge::D3D12Frames::texture(output);
consumer->CopyTextureRegion(&dest, frame.origins[Snapshot::Output][0], frame.origins[Snapshot::Output][1], 0, &source, nullptr);
texture_copy_barrier(consumer.Get(), output, access[Snapshot::Output], true, true);
}
transition(consumer.Get(), shared.Get(), D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_COMMON);
dx(consumer->Close(), "FSR RR consumer close");
ID3D12CommandList* outputs[] = {consumer.Get()}; q->ExecuteCommandLists(1, outputs);
wait_game(q, ++sequence);
if (superSampling) {
// the stage records/submits its own work on `q`; the denoised texture is in NON_PIXEL_SHADER_RESOURCE
const auto upscaleStart = Clock::now();
superSampling(q, denoised.Get(), frame, access);
if (log_enabled()) std::fprintf(stderr, "FSR_RR super sampling submitted in %.2f ms\n", since(upscaleStart));
}
pending = false; failed = false; queue.Reset();
if (log_enabled())
std::fprintf(stderr, "FSR_RR frame %u: %ux%u produce %.2f ms, vkd3d (convert+denoise+compose) %.2f ms, total %.2f ms\n",
frameIndex, layout.rw, layout.rh, producedMs, vkd3dMs, since(begin));
++frameIndex;
}
void run_vkd3d(const Snapshot& frame) {
const auto& s = frame.settings;
const uint32_t rw = layout.rw, rh = layout.rh;
// which game planes exist decides the context's specular signal once
const bool specularHit = layout.planes[7].enabled;
const uint32_t wanted = FFX_DENOISER_SIGNAL_DIRECT_DIFFUSE |
(specularHit ? FFX_DENOISER_SIGNAL_INDIRECT_SPECULAR : FFX_DENOISER_SIGNAL_DIRECT_SPECULAR);
if (wanted != signals) {
if (frameIndex == 0) {
runtime.destroyContext(&context, nullptr); context = nullptr; signals = wanted;
const ffxReturnCode_t created = fsr_rr_create_context(runtime, vdev.Get(), creation.width, creation.height, signals, 0, context);
if (created != FFX_API_RETURN_OK) throw std::runtime_error(std::string("FSR RR ffxCreateContext failed: ") + fsr_rr_return_name(created));
apply_denoiser_keys();
} else throw std::runtime_error("FSR RR: the game's hit-distance guide changed after the denoiser was created (recreate the feature)");
}
for (size_t plane : {0, 1, 2, 3, 4, 5, 6, 7, 10}) if (layout.planes[plane].enabled) ensure_staging(plane);
double clipToView[16], viewToWorld[16];
if (!invert(s.viewToClip, clipToView) || !invert(s.worldToView, viewToWorld)) throw std::runtime_error("FSR RR camera matrix is not invertible");
const bool reset = s.reset || !havePrevious;
double previousView[16], viewToPrevious[16];
for (int i = 0; i < 16; ++i) previousView[i] = reset ? s.worldToView[i] : this->previousView[i];
multiply(viewToWorld, previousView, viewToPrevious);
D4rFsrRrPrepareConstants c = {};
for (int i = 0; i < 16; ++i) { c.clipToView[i] = float(clipToView[i]); c.viewToPreviousView[i] = float(viewToPrevious[i]); }
c.renderSize[0] = float(rw); c.renderSize[1] = float(rh); c.renderSize[2] = 1.0f / rw; c.renderSize[3] = 1.0f / rh;
c.motion[0] = s.motionScale[0] / rw; c.motion[1] = s.motionScale[1] / rh; c.motion[2] = 1.0f; c.motion[3] = 1.0f;
uint32_t flags = 0;
if (s.hardwareDepth) flags |= kFsrRrHardwareDepth;
if (s.roughnessPacked) flags |= kFsrRrRoughnessPacked;
if (specularHit) flags |= kFsrRrSpecularHit;
if (unorm(layout.planes[3].format)) flags |= kFsrRrNormalsUnorm;
if (layout.planes[10].enabled) flags |= kFsrRrBeforeParticles;
flags |= kFsrRrInvalidNormalPassthrough;
c.motionBaseFlags[2] = flags;
const float viewZSign = s.viewToClip[11] < 0.0f ? -1.0f : 1.0f;
c.depthParams[0] = viewZSign; c.depthParams[1] = s.depthInverted ? 0.0f : 1.0f; c.depthParams[2] = 1.0e6f;
c.detail[0] = env_float("D4R_FSR_RR_DETAIL", 0.0f, 0.0f, 1.0f);
c.detail[1] = std::max(env_float("D4R_FSR_RR_DEMOD_FLOOR", 0.1f, 0.0f, 1.0f), 1e-3f);
dx(vallocator->Reset(), "FSR RR vkd3d allocator reset"); dx(vlist->Reset(vallocator.Get(), nullptr), "FSR RR vkd3d list reset");
stamp(0);
auto setState = [&](int index, D3D12_RESOURCE_STATES state) {
auto& t = textures[index];
if (t.resource) { transition(vlist.Get(), t.resource.Get(), t.state, state); t.state = state; }
};
for (int i = 0; i < TexCount; ++i) setState(i, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
VkCommandBuffer command = VK_NULL_HANDLE;
dx(interop->BeginVkCommandBufferInterop(vlist.Get(), &command), "FSR RR Vulkan interop begin");
// the packet comes in from the game's device
VkBufferMemoryBarrier acquire{VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER}; acquire.buffer = packetBuffer; acquire.size = VK_WHOLE_SIZE;
acquire.srcQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL; acquire.dstQueueFamilyIndex = vkFamily;
acquire.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT;
api.barrier(command, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 1, &acquire, 0, nullptr);
for (size_t plane : {0, 1, 2, 3, 4, 5, 6, 7, 10}) {
if (!layout.planes[plane].enabled) continue;
const auto& st = staging[plane];
VkImageMemoryBarrier toCopy{VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER}; toCopy.image = st.image;
toCopy.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; toCopy.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
toCopy.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; toCopy.srcQueueFamilyIndex = toCopy.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
toCopy.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
api.barrier(command, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &toCopy);
VkBufferImageCopy region{}; region.bufferOffset = layout.planes[plane].offset;
region.bufferRowLength = uint32_t(layout.planes[plane].pitch / layout.planes[plane].bytes);
region.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}; region.imageExtent = {rw, rh, 1};
api.copyBufferToImage(command, packetBuffer, st.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
VkImageMemoryBarrier toRead = toCopy; toRead.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; toRead.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
toRead.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; toRead.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
api.barrier(command, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &toRead);
}
auto sampled = [&](size_t plane) { return D4rFsrRrImage{staging[plane].view, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL}; };
const bool hasHit = layout.planes[7].enabled, hasParticles = layout.planes[10].enabled;
D4rFsrRrImage images[D4rFsrRrPasses::kPrepareInputs + D4rFsrRrPasses::kPrepareOutputs] = {
sampled(0), sampled(2), sampled(1), sampled(3), sampled(4), sampled(5), sampled(6),
hasHit ? sampled(7) : sampled(3), sampled(3), hasParticles ? sampled(10) : sampled(0)};
static const int targets[D4rFsrRrPasses::kPrepareOutputs] = {LinearDepth, MotionT, NormalsT, DiffuseAlbedo, SpecularAlbedo, DiffuseT, SpecularT, Residual};
for (uint32_t i = 0; i < D4rFsrRrPasses::kPrepareOutputs; ++i)
images[D4rFsrRrPasses::kPrepareInputs + i] = {textures[targets[i]].view, textures[targets[i]].layout};
passes.record_prepare(command, images, c, rw, rh);
dx(interop->EndVkCommandBufferInterop(vlist.Get()), "FSR RR Vulkan interop end");
stamp(1);
// --- AMD FSR Ray Regeneration (D3D12 on vkd3d-proton) ---
const D3D12_RESOURCE_STATES read = D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
for (int index : {LinearDepth, MotionT, NormalsT, DiffuseAlbedo, SpecularAlbedo, DiffuseT, SpecularT}) setState(index, read);
auto ffx = [&](int index, uint32_t state) {
const auto& t = textures[index];
return fsr_rr_texture(t.resource.Get(), t.desc, fsr_rr_surface_format(t.desc.Format), state);
};
const uint32_t ffxRead = FFX_API_RESOURCE_STATE_PIXEL_COMPUTE_READ;
ffxDispatchDescDenoiser desc = {};
desc.header.type = FFX_API_DISPATCH_DESC_TYPE_DENOISER;
desc.commandList = vlist.Get();
desc.linearDepth = ffx(LinearDepth, ffxRead); desc.motionVectors = ffx(MotionT, ffxRead); desc.normals = ffx(NormalsT, ffxRead);
desc.specularAlbedo = ffx(SpecularAlbedo, ffxRead); desc.diffuseAlbedo = ffx(DiffuseAlbedo, ffxRead);
desc.motionVectorScale = {1.0f, 1.0f, 1.0f};
desc.jitterOffsets = {s.jitter[0], s.jitter[1]};
const double position[3] = {viewToWorld[12], viewToWorld[13], viewToWorld[14]};
double previousPosition[3] = {position[0], position[1], position[2]};
if (!reset) {
double previousInverse[16];
if (invert(this->previousView, previousInverse)) previousPosition[0] = previousInverse[12], previousPosition[1] = previousInverse[13], previousPosition[2] = previousInverse[14];
}
desc.cameraPositionDelta = {float(previousPosition[0] - position[0]), float(previousPosition[1] - position[1]), float(previousPosition[2] - position[2])};
std::memcpy(&desc.view, s.worldToView, sizeof(desc.view)); std::memcpy(&desc.projection, s.viewToClip, sizeof(desc.projection));
desc.linearDepthBounds = {0.0f, 1.0e6f};
desc.renderSize = {rw, rh};
desc.frameIndex = frameIndex;
desc.flags = uint32_t(FFX_DENOISER_DISPATCH_NON_GAMMA_ALBEDO) | (reset ? uint32_t(FFX_DENOISER_DISPATCH_RESET) : 0u);
ffxDispatchDescDenoiserDirectDiffuse directDiffuse = {}; ffxDispatchDescDenoiserDirectSpecular directSpecular = {};
ffxDispatchDescDenoiserIndirectSpecular indirectSpecular = {};
directDiffuse.header.type = FFX_API_DISPATCH_DESC_TYPE_DENOISER_DIRECT_DIFFUSE;
FfxApiDenoiserSignal* diffuseSignal = &directDiffuse.signal;
FfxApiDenoiserSignal* specularSignal; ffxApiHeader* specularHeader;
if (specularHit) {
indirectSpecular.header.type = FFX_API_DISPATCH_DESC_TYPE_DENOISER_INDIRECT_SPECULAR; specularSignal = &indirectSpecular.signal; specularHeader = &indirectSpecular.header;
} else {
directSpecular.header.type = FFX_API_DISPATCH_DESC_TYPE_DENOISER_DIRECT_SPECULAR; specularSignal = &directSpecular.signal; specularHeader = &directSpecular.header;
}
diffuseSignal->input = ffx(DiffuseT, ffxRead); diffuseSignal->output = ffx(DiffuseOut, FFX_API_RESOURCE_STATE_UNORDERED_ACCESS);
specularSignal->input = ffx(SpecularT, ffxRead); specularSignal->output = ffx(SpecularOut, FFX_API_RESOURCE_STATE_UNORDERED_ACCESS);
desc.header.pNext = &directDiffuse.header; directDiffuse.header.pNext = specularHeader;
const ffxReturnCode_t dispatched = runtime.dispatch(&context, &desc.header);
if (dispatched != FFX_API_RETURN_OK) throw std::runtime_error(std::string("FSR RR ffxDispatch failed: ") + fsr_rr_return_name(dispatched));
D3D12_RESOURCE_BARRIER uav{}; uav.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV; vlist->ResourceBarrier(1, &uav);
stamp(2);
// --- composition, then the composed image into the packet's output region ---
for (int i = 0; i < TexCount; ++i) setState(i, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
const D4rFsrRrImage compose[D4rFsrRrPasses::kComposeImages] = {
{textures[DiffuseOut].view, textures[DiffuseOut].layout}, {textures[SpecularOut].view, textures[SpecularOut].layout},
{textures[DiffuseAlbedo].view, textures[DiffuseAlbedo].layout}, {textures[SpecularAlbedo].view, textures[SpecularAlbedo].layout},
{textures[Residual].view, textures[Residual].layout}, {textures[Composed].view, textures[Composed].layout},
{textures[DiffuseT].view, textures[DiffuseT].layout}, {textures[SpecularT].view, textures[SpecularT].layout}};
dx(interop->BeginVkCommandBufferInterop(vlist.Get(), &command), "FSR RR Vulkan interop begin (compose)");
passes.record_compose(command, compose, rw, rh, env_float("D4R_FSR_RR_HALO_LIMIT", 1.0f, 0.0f, 1000.0f));
int copiedImage = Composed;
if (layout.outputBytes == 16) {
// Preserve the denoiser/composition's half arithmetic; widen only the completed output on the GPU.
VkImageBlit region{}; region.srcSubresource = region.dstSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1};
region.srcOffsets[1] = region.dstOffsets[1] = {int32_t(rw), int32_t(rh), 1};
api.blitImage(command, textures[Composed].image, textures[Composed].layout,
textures[Widened].image, textures[Widened].layout, 1, &region, VK_FILTER_NEAREST);
passes.full_barrier(command);
copiedImage = Widened;
}
VkBufferImageCopy back{}; back.bufferOffset = layout.outputOffset; back.bufferRowLength = uint32_t(layout.outputPitch / layout.outputBytes);
back.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}; back.imageExtent = {rw, rh, 1};
api.copyImageToBuffer(command, textures[copiedImage].image, textures[copiedImage].layout, packetBuffer, 1, &back);
VkBufferMemoryBarrier release = acquire; release.srcQueueFamilyIndex = vkFamily; release.dstQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL;
release.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; release.dstAccessMask = 0;
api.barrier(command, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 1, &release, 0, nullptr);
dx(interop->EndVkCommandBufferInterop(vlist.Get()), "FSR RR Vulkan interop end (compose)");
// Diagnostic (D4R_FSR_RR_DUMP_DIR, first D4R_FSR_RR_DUMP_FRAMES frames, default 3): raw copies of the converted
// linear depth and motion textures, to check the DLSS -> FFX guide conversion numerically.
stamp(3);
if (stamps) vlist->ResolveQueryData(stamps.Get(), D3D12_QUERY_TYPE_TIMESTAMP, 0, 4, stampReadback.Get(), 0);
const char* dumpDir = std::getenv("D4R_FSR_RR_DUMP_DIR");
const unsigned dumpFrames = std::getenv("D4R_FSR_RR_DUMP_FRAMES") ? unsigned(std::atoi(std::getenv("D4R_FSR_RR_DUMP_FRAMES"))) : 3;
struct Dump { int index; const char* name; ComPtr<ID3D12Resource> buffer; D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprint; UINT rows; UINT bytesPerTexel; };
std::vector<Dump> dumps;
if (dumpDir && frameIndex < dumpFrames) {
for (auto [index, name, texel] : {std::tuple<int, const char*, UINT>{LinearDepth, "linear-depth", 4}, {MotionT, "motion", 8}, {NormalsT, "normals", 8}, {Composed, "composed", 8}}) {
Dump d{index, name, {}, {}, 0, texel};
UINT64 rowBytes = 0, total = 0; auto desc = textures[index].desc;
vdev->GetCopyableFootprints(&desc, 0, 1, 0, &d.footprint, &d.rows, &rowBytes, &total);
d.buffer = make_buffer(vdev.Get(), total, D3D12_HEAP_TYPE_READBACK, D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);
transition(vlist.Get(), textures[index].resource.Get(), textures[index].state, D3D12_RESOURCE_STATE_COPY_SOURCE);
D3D12_TEXTURE_COPY_LOCATION destination{}; destination.pResource = d.buffer.Get();
destination.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; destination.PlacedFootprint = d.footprint;
D3D12_TEXTURE_COPY_LOCATION source{}; source.pResource = textures[index].resource.Get(); source.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
vlist->CopyTextureRegion(&destination, 0, 0, 0, &source, nullptr);
transition(vlist.Get(), textures[index].resource.Get(), D3D12_RESOURCE_STATE_COPY_SOURCE, textures[index].state);
dumps.push_back(std::move(d));
}
}
dx(vlist->Close(), "FSR RR vkd3d list close");
ID3D12CommandList* lists[] = {vlist.Get()}; vqueue->ExecuteCommandLists(1, lists);
wait_vkd3d();
if (stamps) {
void* mapped = nullptr; D3D12_RANGE range{0, 4 * sizeof(UINT64)};
if (SUCCEEDED(stampReadback->Map(0, &range, &mapped))) {
UINT64 t[4]; std::memcpy(t, mapped, sizeof(t)); stampReadback->Unmap(0, nullptr);
auto ms = [&](int a, int b) { return double(t[b] - t[a]) * 1000.0 / double(stampFrequency); };
std::fprintf(stderr, "FSR_RR gpu frame %u: prepare %.2f ms, denoise %.2f ms, compose+copy %.2f ms, total %.2f ms\n", frameIndex, ms(0, 1), ms(1, 2), ms(2, 3), ms(0, 3));
}
}
for (auto& d : dumps) {
void* mapped = nullptr; D3D12_RANGE range{0, size_t(d.footprint.Footprint.RowPitch) * d.rows};
dx(d.buffer->Map(0, &range, &mapped), "FSR RR dump map");
const std::string path = std::string(dumpDir) + "/frame" + std::to_string(frameIndex) + "-" + d.name + ".bin";
if (FILE* out = std::fopen(path.c_str(), "wb")) {
for (UINT row = 0; row < rw && row < d.rows; ++row) {} // (rows are written below; width = render width)
for (UINT row = 0; row < rh; ++row)
std::fwrite(static_cast<const char*>(mapped) + size_t(row) * d.footprint.Footprint.RowPitch, 1, size_t(rw) * d.bytesPerTexel, out);
std::fclose(out);
}
d.buffer->Unmap(0, nullptr);
}
std::memcpy(this->previousView, s.worldToView, sizeof(this->previousView));
havePrevious = true;
}
};
FsrBackend::FsrBackend(ID3D12Device* device, const Creation& creation, const FsrConfig& config) {
std::lock_guard<std::mutex> lock(hostMutex);
auto candidate = std::make_unique<Impl>(device, creation, config);
candidate->initialize(); impl = std::move(candidate);
}
FsrBackend::~FsrBackend() {
std::lock_guard<std::mutex> lock(hostMutex);
if (impl && impl->pending) {
std::fprintf(stderr, "D4R_FSR_RR_QUARANTINE incomplete GPU work; retaining backend and guide resources\n");
(void)impl.release();
} else impl.reset();
}
void FsrBackend::evaluate(ID3D12CommandQueue* queue, const Snapshot& frame, const std::array<ResourceAccess, Snapshot::Count>& access) {
std::lock_guard<std::mutex> lock(hostMutex); impl->evaluate(queue, frame, access);
}
void FsrBackend::set_super_sampling(SuperSampling stage, const std::array<unsigned, 4>& renderLimits) {
std::lock_guard<std::mutex> lock(hostMutex); impl->superSampling = std::move(stage); impl->srRenderLimits = renderLimits;
}
void FsrBackend::validate_frame(const Snapshot& frame) { std::lock_guard<std::mutex> lock(hostMutex); impl->validate_frame(frame); }
void FsrBackend::drain() {
std::lock_guard<std::mutex> lock(hostMutex);
if (impl->pending || impl->failed) throw std::runtime_error("FSR RR backend cannot retire failed GPU work");
}
}
+41
View File
@@ -0,0 +1,41 @@
#pragma once
#include "d3d12_external.h"
#include "rr_ngx_snapshot.h"
#include <functional>
#include <memory>
#include <string>
namespace d4r::win::rr {
// Where the experimental FSR Ray Regeneration backend finds its pieces. All paths are explicit: nothing is
// searched for, and AMD's amdxc64.dll is never loaded.
struct FsrConfig {
std::wstring vkd3dCore; // d4r's vkd3d-proton d3d12core.dll, renamed so it cannot collide with the system's
std::wstring denoiserDll; // AMD's amd_fidelityfx_denoiser_dx12.dll (FidelityFX SDK 2.3), unmodified
std::wstring amdxcShim; // d4r's amdxc64 shim (tools/windows/amdxc64_shim)
std::wstring cacheDir; // vkd3d-proton's shader cache: the denoiser's shaders are compiled once
static FsrConfig from_environment();
};
// FSR Ray Regeneration as the Ray Reconstruction backend on native Windows. The game's guides cross to a private
// vkd3d-proton device (D3D12 over Vulkan, dxil-spirv FP8 emulation) through the same shared packet the native
// DLSS backend uses; AMD's denoiser runs there. Evaluation is synchronous, as the native backend's is.
// The denoiser works at render resolution. When the output is larger (or a Super Sampling stage is wanted at all),
// the owner installs one with set_super_sampling(): the denoised colour is then copied into a texture on the game's
// device and handed to that callback together with the frame, which must record/submit the upscale into the game's
// output on `queue`. Without it an output larger than the render size is refused, never returned noisy.
class FsrBackend {
struct Impl;
std::unique_ptr<Impl> impl;
public:
FsrBackend(ID3D12Device* device, const Creation& creation, const FsrConfig& config);
~FsrBackend();
FsrBackend(const FsrBackend&) = delete;
FsrBackend& operator=(const FsrBackend&) = delete;
using SuperSampling = std::function<void(ID3D12CommandQueue*, ID3D12Resource* denoised, const Snapshot&,
const std::array<ResourceAccess, Snapshot::Count>&)>;
void set_super_sampling(SuperSampling stage, const std::array<unsigned, 4>& renderLimits);
void validate_frame(const Snapshot& frame);
void evaluate(ID3D12CommandQueue* queue, const Snapshot& frame, const std::array<ResourceAccess, Snapshot::Count>& access);
void drain();
};
}
+10
View File
@@ -4,12 +4,22 @@
#include <d3d12.h>
#include <dxgi1_4.h>
#include <cstdio>
#include <cstdlib>
#include <string>
// Native Windows uses the installed driver's normal D3D12 path. Do not import
// Proton's amdxc64 shim or Linux PCI/sysfs discovery into this process.
inline bool d4r_fsr_rr_driver_extension(ID3D12Device* device, std::string& detail)
{
// Experimental: the denoiser only consults amdxc64.dll when it is already loaded in the process. With
// D4R_FSR_AMDXC_SHIM=path, preload d4r's own shim (tools/windows/amdxc64_shim) for a vkd3d-proton device.
// AMD's real amdxc64.dll is never loaded by this helper.
if (const char* shim = std::getenv("D4R_FSR_AMDXC_SHIM")) {
if (!LoadLibraryA(shim)) {
detail = "D4R_FSR_AMDXC_SHIM could not be loaded";
return false;
}
}
LUID luid{};
device->GetAdapterLuid(&luid);
IDXGIFactory4* factory = nullptr;
+239
View File
@@ -0,0 +1,239 @@
// The FSR Ray Regeneration backend's own two compute passes (tools/windows/fsr_rr/fsr_rr_prepare.comp and
// fsr_rr_compose.comp), recorded into the game's D3D12 command list through vkd3d-proton's Vulkan
// interop. Descriptors are pushed (VK_KHR_push_descriptor, which vkd3d-proton requires), so nothing
// here outlives the command buffer that recorded it except the pipelines.
#pragma once
#include <vulkan/vulkan_core.h>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include "fsr_rr/fsr_rr_shaders_spv.h"
struct D4rFsrRrPrepareConstants
{
float clipToView[16];
float viewToPreviousView[16];
float renderSize[4];
float motion[4];
uint32_t colorDepthBase[4];
uint32_t motionBaseFlags[4];
float depthParams[4];
float detail[4];
};
static_assert(sizeof(D4rFsrRrPrepareConstants) == 224, "fsr_rr_prepare.comp push constants");
enum D4rFsrRrPrepareFlags : uint32_t
{
kFsrRrHardwareDepth = 1u,
kFsrRrRoughnessPacked = 2u,
kFsrRrSpecularHit = 4u,
kFsrRrSpecularHitInW = 8u,
kFsrRrDiffuseHit = 16u,
kFsrRrDiffuseHitInW = 32u,
kFsrRrNormalsUnorm = 64u,
kFsrRrBeforeParticles = 128u,
kFsrRrInvalidNormalPassthrough = 256u,
};
// One image a pass reads or writes: the view and the layout the image is in while the pass runs.
struct D4rFsrRrImage
{
VkImageView view = VK_NULL_HANDLE;
VkImageLayout layout = VK_IMAGE_LAYOUT_GENERAL;
};
struct D4rFsrRrPasses
{
static constexpr uint32_t kPrepareInputs = 10, kPrepareOutputs = 8, kComposeImages = 8;
VkDevice device = VK_NULL_HANDLE;
VkSampler sampler = VK_NULL_HANDLE;
VkDescriptorSetLayout prepareSet = VK_NULL_HANDLE, composeSet = VK_NULL_HANDLE;
VkPipelineLayout prepareLayout = VK_NULL_HANDLE, composeLayout = VK_NULL_HANDLE;
VkPipeline preparePipeline = VK_NULL_HANDLE, composePipeline = VK_NULL_HANDLE;
PFN_vkCreateSampler createSampler = nullptr;
PFN_vkDestroySampler destroySampler = nullptr;
PFN_vkCreateDescriptorSetLayout createSetLayout = nullptr;
PFN_vkDestroyDescriptorSetLayout destroySetLayout = nullptr;
PFN_vkCreatePipelineLayout createLayout = nullptr;
PFN_vkDestroyPipelineLayout destroyLayout = nullptr;
PFN_vkCreateShaderModule createShader = nullptr;
PFN_vkDestroyShaderModule destroyShader = nullptr;
PFN_vkCreateComputePipelines createPipelines = nullptr;
PFN_vkDestroyPipeline destroyPipeline = nullptr;
PFN_vkCmdBindPipeline bindPipeline = nullptr;
PFN_vkCmdPushConstants push = nullptr;
PFN_vkCmdPushDescriptorSetKHR pushDescriptors = nullptr;
PFN_vkCmdDispatch dispatch = nullptr;
PFN_vkCmdPipelineBarrier barrier = nullptr;
PFN_vkCreateImageView createView = nullptr;
PFN_vkDestroyImageView destroyView = nullptr;
bool pipeline(const uint32_t* code, size_t bytes, VkPipelineLayout layout, VkPipeline& result)
{
VkShaderModuleCreateInfo shader = {VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO};
shader.codeSize = bytes;
shader.pCode = code;
VkShaderModule module = VK_NULL_HANDLE;
if (createShader(device, &shader, nullptr, &module) != VK_SUCCESS)
return false;
VkComputePipelineCreateInfo info = {VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO};
info.stage = {VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO};
info.stage.stage = VK_SHADER_STAGE_COMPUTE_BIT;
info.stage.module = module;
info.stage.pName = "main";
info.layout = layout;
const VkResult status = createPipelines(device, VK_NULL_HANDLE, 1, &info, nullptr, &result);
destroyShader(device, module, nullptr);
return status == VK_SUCCESS;
}
bool set_layout(const VkDescriptorType* types, uint32_t count, uint32_t pushBytes, VkDescriptorSetLayout& set,
VkPipelineLayout& layout)
{
VkDescriptorSetLayoutBinding bindings[kPrepareInputs + kPrepareOutputs] = {};
for (uint32_t i = 0; i < count; ++i)
{
bindings[i].binding = i;
bindings[i].descriptorType = types[i];
bindings[i].descriptorCount = 1;
bindings[i].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
bindings[i].pImmutableSamplers = types[i] == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER ? &sampler : nullptr;
}
VkDescriptorSetLayoutCreateInfo info = {VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO};
info.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR;
info.bindingCount = count;
info.pBindings = bindings;
if (createSetLayout(device, &info, nullptr, &set) != VK_SUCCESS)
return false;
VkPushConstantRange range = {VK_SHADER_STAGE_COMPUTE_BIT, 0, pushBytes};
VkPipelineLayoutCreateInfo pipelineLayout = {VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO};
pipelineLayout.setLayoutCount = 1;
pipelineLayout.pSetLayouts = &set;
pipelineLayout.pushConstantRangeCount = 1;
pipelineLayout.pPushConstantRanges = &range;
return createLayout(device, &pipelineLayout, nullptr, &layout) == VK_SUCCESS;
}
// `maxPushConstants`: VkPhysicalDeviceLimits::maxPushConstantsSize (the prepare pass needs 224).
bool init(VkDevice d, PFN_vkGetDeviceProcAddr proc, uint32_t maxPushConstants)
{
device = d;
if (maxPushConstants < sizeof(D4rFsrRrPrepareConstants))
return false;
#define D4R_LOAD(member, name) \
member = reinterpret_cast<decltype(member)>(proc(d, "vk" #name)); \
if (!member) \
return false
D4R_LOAD(createSampler, CreateSampler);
D4R_LOAD(destroySampler, DestroySampler);
D4R_LOAD(createSetLayout, CreateDescriptorSetLayout);
D4R_LOAD(destroySetLayout, DestroyDescriptorSetLayout);
D4R_LOAD(createLayout, CreatePipelineLayout);
D4R_LOAD(destroyLayout, DestroyPipelineLayout);
D4R_LOAD(createShader, CreateShaderModule);
D4R_LOAD(destroyShader, DestroyShaderModule);
D4R_LOAD(createPipelines, CreateComputePipelines);
D4R_LOAD(destroyPipeline, DestroyPipeline);
D4R_LOAD(bindPipeline, CmdBindPipeline);
D4R_LOAD(push, CmdPushConstants);
D4R_LOAD(pushDescriptors, CmdPushDescriptorSetKHR);
D4R_LOAD(dispatch, CmdDispatch);
D4R_LOAD(barrier, CmdPipelineBarrier);
D4R_LOAD(createView, CreateImageView);
D4R_LOAD(destroyView, DestroyImageView);
#undef D4R_LOAD
VkSamplerCreateInfo nearest = {VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO};
nearest.magFilter = nearest.minFilter = VK_FILTER_NEAREST;
nearest.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
nearest.addressModeU = nearest.addressModeV = nearest.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
if (createSampler(d, &nearest, nullptr, &sampler) != VK_SUCCESS)
return false;
VkDescriptorType prepare[kPrepareInputs + kPrepareOutputs];
for (uint32_t i = 0; i < kPrepareInputs + kPrepareOutputs; ++i)
prepare[i] = i < kPrepareInputs ? VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER : VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
VkDescriptorType compose[kComposeImages];
for (VkDescriptorType& type : compose)
type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
return set_layout(prepare, kPrepareInputs + kPrepareOutputs, sizeof(D4rFsrRrPrepareConstants), prepareSet,
prepareLayout) &&
set_layout(compose, kComposeImages, 16, composeSet, composeLayout) &&
pipeline(d4r_vk_fsr_rr_prepare, sizeof(d4r_vk_fsr_rr_prepare), prepareLayout, preparePipeline) &&
pipeline(d4r_vk_fsr_rr_compose, sizeof(d4r_vk_fsr_rr_compose), composeLayout, composePipeline);
}
void destroy()
{
if (device == VK_NULL_HANDLE || destroyPipeline == nullptr)
return;
destroyPipeline(device, preparePipeline, nullptr);
destroyPipeline(device, composePipeline, nullptr);
destroyLayout(device, prepareLayout, nullptr);
destroyLayout(device, composeLayout, nullptr);
destroySetLayout(device, prepareSet, nullptr);
destroySetLayout(device, composeSet, nullptr);
destroySampler(device, sampler, nullptr);
*this = D4rFsrRrPasses{};
}
// Everything the D3D12 side wrote before is visible to the pass, and the pass's writes to what
// the D3D12 side records after.
void full_barrier(VkCommandBuffer cmd)
{
VkMemoryBarrier memory = {VK_STRUCTURE_TYPE_MEMORY_BARRIER};
memory.srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT;
memory.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT;
barrier(cmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 1, &memory, 0, nullptr,
0, nullptr);
}
void record(VkCommandBuffer cmd, VkPipeline pipeline, VkPipelineLayout layout, const D4rFsrRrImage* images,
uint32_t count, uint32_t sampledCount, const void* constants, uint32_t constantBytes, uint32_t width,
uint32_t height)
{
VkDescriptorImageInfo infos[kPrepareInputs + kPrepareOutputs] = {};
VkWriteDescriptorSet writes[kPrepareInputs + kPrepareOutputs] = {};
for (uint32_t i = 0; i < count; ++i)
{
infos[i].sampler = i < sampledCount ? sampler : VK_NULL_HANDLE;
infos[i].imageView = images[i].view;
infos[i].imageLayout = images[i].layout;
writes[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writes[i].dstBinding = i;
writes[i].descriptorCount = 1;
writes[i].descriptorType =
i < sampledCount ? VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER : VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
writes[i].pImageInfo = &infos[i];
}
bindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline);
pushDescriptors(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, layout, 0, count, writes);
push(cmd, layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, constantBytes, constants);
dispatch(cmd, (width + 7) / 8, (height + 7) / 8, 1);
}
// images: the 10 sampled inputs, then the 8 storage outputs (fsr_rr_prepare.comp's bindings)
void record_prepare(VkCommandBuffer cmd, const D4rFsrRrImage* images, const D4rFsrRrPrepareConstants& constants,
uint32_t width, uint32_t height)
{
full_barrier(cmd);
record(cmd, preparePipeline, prepareLayout, images, kPrepareInputs + kPrepareOutputs, kPrepareInputs,
&constants, sizeof(constants), width, height);
full_barrier(cmd);
}
// images: denoised diffuse, denoised specular, diffuse albedo, specular albedo, residual, composed, noisy diffuse,
// noisy specular. haloLimit: fsr_rr_compose.comp's limit (0: off)
void record_compose(VkCommandBuffer cmd, const D4rFsrRrImage* images, uint32_t width, uint32_t height,
float haloLimit = 0.0f)
{
uint32_t limitBits;
std::memcpy(&limitBits, &haloLimit, sizeof(limitBits));
const uint32_t constants[4] = {width, height, limitBits, 0};
full_barrier(cmd);
record(cmd, composePipeline, composeLayout, images, kComposeImages, 0, constants, sizeof(constants), width,
height);
full_barrier(cmd);
}
};
+351
View File
@@ -0,0 +1,351 @@
"""Install pinned local NVIDIA dependencies without PowerShell or driver installation.
All downloads and backups stay outside the game. Runtime replacements are staged,
hash verified and rolled back on failure. No model generation is implicit.
"""
import argparse
import hashlib
import json
import os
from pathlib import Path, PurePosixPath
import re
import shutil
import subprocess
import tempfile
import urllib.request
import uuid
PINS = {
'dlss': ('nvngx_dlss.dll', 'dlss/nvngx_dlss.dll',
'https://raw.githubusercontent.com/NVIDIA/DLSS/374959484e79a640feaba44c93ac8cfb0a03f5b5/lib/Windows_x86_64/rel/nvngx_dlss.dll',
'3975567b8943c53acce397f2b72380092f84f162d00b0d2c7d08a1025c563983'),
'dlssd': ('nvngx_dlssd.dll', 'rr/nvngx_dlssd.dll',
'https://raw.githubusercontent.com/NVIDIA/DLSS/374959484e79a640feaba44c93ac8cfb0a03f5b5/lib/Windows_x86_64/rel/nvngx_dlssd.dll',
'4bc7ea5fcb2f32cf86bc2cb072e8d2860914a0be511cbdcb2fc206c1d9d83b80'),
'ngx': ('_nvngx.dll', 'ngx/_nvngx.dll', None,
'66767018c36b3bab46398dade3adf173daa3730fda75965689ea848c9bc4e79b'),
}
# Optional: AMD's FSR Ray Regeneration denoiser (FidelityFX SDK 2.3), only for [RayReconstruction] Backend = fsr.
# AMD's file, never bundled; the same pin as scripts/windows/setup-fsr-rr.ps1.
FSR_DENOISER = ('amd_fidelityfx_denoiser_dx12.dll', 'amd_fidelityfx_denoiser_dx12.dll',
'https://raw.githubusercontent.com/GPUOpen-LibrariesAndSDKs/FidelityFX-SDK/v2.3.0/Kits/FidelityFX/signedbin/amd_fidelityfx_denoiser_dx12.dll',
'48f1e5888ba6a0a3d59a98b9751e37c392b0f7b8c223d0082d5c1f40642879d3')
DRIVER = ('https://us.download.nvidia.com/Windows/617.14/617.14-desktop-win10-win11-64bit-international-dch-whql.exe',
'63d3da5ed52547bf3fd86204bb9ea1455947b24e2aa2519f359126f9a6efe13f')
SEVENZIP = ('https://github.com/ip7z/7zip/releases/download/26.04/7zr.exe',
'256feca8e274e5da655e2a284fabafd9f554365eb164862089dacd4e8276d282')
def sha(path):
with path.open('rb') as stream:
return hashlib.file_digest(stream, 'sha256').hexdigest()
def external(path, game):
if path.resolve().is_relative_to(game.resolve()):
raise RuntimeError('Cache and backups must stay outside game directory: ' + str(path))
def verified_download(path, url, expected, offline=False):
if path.exists():
if path.is_file() and sha(path) == expected:
return path
if offline:
raise RuntimeError('Invalid offline cache: ' + str(path))
path.rename(path.with_name(path.name + '.bad-' + uuid.uuid4().hex))
if offline:
raise RuntimeError('Missing offline cache: ' + str(path))
path.parent.mkdir(parents=True, exist_ok=True)
temporary = path.with_name(path.name + '.part-' + uuid.uuid4().hex)
try:
print('DOWNLOAD ' + path.name, flush=True)
with urllib.request.urlopen(url, timeout=60) as response, temporary.open('xb') as out:
shutil.copyfileobj(response, out)
if sha(temporary) != expected:
raise RuntimeError('Download SHA256 mismatch: ' + path.name)
os.replace(temporary, path)
finally:
temporary.unlink(missing_ok=True)
return path
def ini_selects_fsr(runtime):
"""True when d4r.ini sets [RayReconstruction] Backend = fsr (comments and case ignored)."""
section = ''
for line in (runtime / 'd4r.ini').read_text(encoding='utf-8-sig').splitlines():
line = line.split(';', 1)[0].split('#', 1)[0].strip()
if line.startswith('[') and line.endswith(']'):
section = line[1:-1].strip().lower()
elif section == 'rayreconstruction' and '=' in line:
key, value = line.split('=', 1)
if key.strip().lower() == 'backend' and value.strip().lower() == 'fsr':
return True
return False
def select_backend(data, backend):
"""Return d4r.ini bytes with [RayReconstruction] Backend set to `backend`; everything else is preserved."""
bom = data.startswith(b'\xef\xbb\xbf')
text = data.decode('utf-8-sig')
newline = '\r\n' if '\r\n' in text else '\n'
result, in_rr, found, done = [], False, False, False
for line in text.splitlines(keepends=True):
stripped = line.strip()
if stripped.startswith('['):
if in_rr and not done:
result.append('Backend = ' + backend + newline)
done = True
in_rr = stripped.split(']', 1)[0][1:].strip().casefold() == 'rayreconstruction'
found = found or in_rr
elif in_rr and '=' in line and not stripped.startswith((';', '#')) and line.split('=', 1)[0].strip().casefold() == 'backend':
prefix, body = line.split('=', 1)
ending = '\r\n' if body.endswith('\r\n') else '\n' if body.endswith('\n') else ''
body = body[:len(body) - len(ending)]
comment = re.search(r'\s[;#]', body)
result.append(prefix + '= ' + backend + (body[comment.start():] if comment else '') + ending)
done = True
continue
result.append(line)
if in_rr and not done:
if result and not result[-1].endswith(('\n', '\r')):
result.append(newline)
result.append('Backend = ' + backend + newline)
elif not found:
if result and not result[-1].endswith(('\n', '\r')):
result.append(newline)
result.extend((newline, '[RayReconstruction]' + newline, 'Backend = ' + backend + newline))
return (b'\xef\xbb\xbf' if bom else b'') + ''.join(result).encode('utf-8')
def enable_fsr(runtime, backup_root):
"""Select the FSR backend in d4r.ini with an external backup; idempotent."""
config = runtime / 'd4r.ini'
original = config.read_bytes()
updated = select_backend(original, 'fsr')
if updated == original:
return None
backup_root.mkdir(parents=True, exist_ok=True)
backup = backup_root / ('fsr-config-' + uuid.uuid4().hex + '.ini')
with backup.open('xb') as stream:
stream.write(original)
temporary = config.with_name('d4r.ini.' + uuid.uuid4().hex + '.tmp')
with temporary.open('xb') as stream:
stream.write(updated)
if config.read_bytes() != original:
temporary.unlink()
raise RuntimeError('d4r.ini changed during installation; preserved current configuration')
os.replace(temporary, config)
return backup
def collect(cache, offline, fsr=False):
assets = []
for kind, (name, relative, url, expected) in PINS.items():
cached = cache / 'nvidia' / name
if kind == 'ngx' and not (cached.is_file() and sha(cached) == expected):
if offline:
raise RuntimeError('Missing or invalid offline NGX 617.14 core')
driver = verified_download(cache / 'nvidia-driver-617.14.exe', *DRIVER)
extractor = verified_download(cache / 'tools/7zr-26.04.exe', *SEVENZIP)
with tempfile.TemporaryDirectory(prefix='d4r-ngx-') as work:
subprocess.run([str(extractor), 'e', '-y', '-o' + work, str(driver),
'Display.Driver/_nvngx.dll'], check=True, timeout=600)
core = Path(work) / name
if sha(core) != expected:
raise RuntimeError('Extracted NGX core SHA256 mismatch')
cached.parent.mkdir(parents=True, exist_ok=True)
if cached.exists():
cached.rename(cached.with_name(name + '.bad-' + uuid.uuid4().hex))
staged = cached.with_name(name + '.part-' + uuid.uuid4().hex)
try:
shutil.copy2(core, staged)
if sha(staged) != expected:
raise RuntimeError('Staged core SHA256 mismatch')
os.replace(staged, cached)
finally:
staged.unlink(missing_ok=True)
else:
verified_download(cached, url, expected, offline)
assets.append((cached, relative, expected))
if fsr:
name, relative, url, expected = FSR_DENOISER
assets.append((verified_download(cache / 'amd' / name, url, expected, offline), relative, expected))
return assets
def transaction(runtime, assets, backup_root):
"""Back up every replacement before first mutation; preserve concurrent edits."""
entries, applied, staged_files = [], [], []
backup = backup_root / ('dlss-install-' + uuid.uuid4().hex)
try:
for source, relative, expected in assets:
if Path(relative).is_absolute() or '..' in Path(relative).parts:
raise RuntimeError('Invalid runtime destination')
destination = runtime / relative
if not destination.resolve().is_relative_to(runtime.resolve()):
raise RuntimeError('Runtime destination escapes runtime')
if destination.exists() and not destination.is_file():
raise RuntimeError('DLL destination is not a file: ' + str(destination))
previous = sha(destination) if destination.exists() else None
if previous == expected:
continue
original = backup / relative
if previous is not None:
original.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(destination, original)
if sha(original) != previous:
raise RuntimeError('Backup SHA256 mismatch')
entries.append(dict(path=str(destination), sha256=previous, installHash=expected,
backup=str(original) if previous is not None else None))
destination.parent.mkdir(parents=True, exist_ok=True)
staged = destination.with_name(destination.name + '.install-' + uuid.uuid4().hex)
staged_files.append(staged)
shutil.copy2(source, staged)
if sha(staged) != expected:
raise RuntimeError('Staged DLL SHA256 mismatch')
if entries:
backup.mkdir(parents=True, exist_ok=True)
(backup / 'backup-manifest.json').write_text(json.dumps({'files': entries}, indent=2) + '\n')
for entry, staged in zip(entries, staged_files):
destination = Path(entry['path'])
current = sha(destination) if destination.exists() else None
if current != entry['sha256']:
raise RuntimeError('Destination changed after backup: ' + str(destination))
os.replace(staged, destination)
applied.append(entry)
if sha(destination) != entry['installHash']:
raise RuntimeError('Installed DLL SHA256 mismatch')
return {'changed': len(entries), 'backup': str(backup) if entries else None,
'files': {relative: sha(runtime / relative) for _, relative, _ in assets}}
except BaseException:
errors = []
for entry in reversed(applied):
destination = Path(entry['path'])
try:
if not destination.is_file() or sha(destination) != entry['installHash']:
errors.append('Concurrent edit preserved: ' + str(destination))
continue
if entry['backup']:
original = Path(entry['backup'])
if sha(original) != entry['sha256']:
raise RuntimeError('Rollback backup changed')
staged = destination.with_name(destination.name + '.rollback-' + uuid.uuid4().hex)
staged_files.append(staged)
shutil.copy2(original, staged)
os.replace(staged, destination)
else:
destination.unlink()
except Exception as error:
errors.append(str(error))
if errors:
print('ROLLBACK ATTENTION: ' + '; '.join(errors), flush=True)
raise
finally:
for staged in staged_files:
staged.unlink(missing_ok=True)
def prepare_model_tools(bundle, cache):
"""Copy only verified clean inputs; generated products never become inputs."""
data = (bundle / 'rr-tools.json').read_bytes()
manifest = json.loads(data)
if (manifest.get('kind') != 'local-rr-model-tools' or manifest.get('modelsBundled') is not False
or manifest.get('nvidiaBinariesBundled') is not False):
raise RuntimeError('Unsupported model-tool manifest')
target = cache / ('rr-tools-' + hashlib.sha256(data).hexdigest()[:16])
if not target.resolve().is_relative_to(cache.resolve()):
raise RuntimeError('Model-tool cache escapes cache root')
files = manifest['files']
required = {'python/python.exe', 'recipe/tools/windows/build_rr_model.py',
'recipe/tools/windows/deploy_rr_model.py', 'recipe/engine/rr/native_kernels.txt'}
if not required.issubset(files):
raise RuntimeError('Incomplete model-tool bundle')
for name, expected in files.items():
parts = PurePosixPath(name)
if parts.is_absolute() or '..' in parts.parts or '\\' in name or ':' in name:
raise RuntimeError('Unsafe model-tool path')
source = bundle.joinpath(*parts.parts)
if not source.resolve().is_relative_to(bundle.resolve()) or sha(source) != expected:
raise RuntimeError('Model-tool input changed: ' + name)
destination = target.joinpath(*parts.parts)
if not destination.resolve().is_relative_to(target.resolve()):
raise RuntimeError('Model-tool cache path escaped')
if destination.exists():
if sha(destination) != expected:
raise RuntimeError('Model-tool cache changed; preserved: ' + str(destination))
else:
destination.parent.mkdir(parents=True, exist_ok=True)
with source.open('rb') as src, destination.open('xb') as out:
shutil.copyfileobj(src, out)
if sha(destination) != expected:
raise RuntimeError('Model-tool copy SHA256 mismatch')
# A stale or used cache is never swept into a package. Build reuse validates
# generated products separately through the existing builder's identity.
return target
def install_model(runtime, cache, backup, preset, enable):
metadata = json.loads((runtime / 'package.json').read_text(encoding='utf-8-sig'))
if set(metadata['targets']) != {'gfx1101'}:
raise RuntimeError('Automatic RR build currently requires gfx1101-only package')
tools = prepare_model_tools(runtime / 'model-tools', cache)
python = tools / 'python/python.exe'
recipe = tools / 'recipe'
output = recipe / 'build' / ('rr-' + preset.lower())
# Each builder child has its own 600s deadline and kills a timed-out tree.
subprocess.run([str(python), str(recipe / 'tools/windows/build_rr_model.py'),
'--dll', str(runtime / 'rr/nvngx_dlssd.dll'), '--preset', preset,
'--output', str(output), '--reuse'], check=True)
command = [str(python), str(recipe / 'tools/windows/deploy_rr_model.py'),
'--model', str(output), '--runtime', str(runtime), '--backup-root', str(backup)]
if enable:
command.append('--enable')
subprocess.run(command, check=True, timeout=120)
def main():
local = Path(os.environ.get('LOCALAPPDATA', str(Path.home()))) / 'd4r'
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--package-root', required=True, type=lambda p: Path(p).resolve())
parser.add_argument('--cache-root', type=lambda p: Path(p).resolve(), default=local / 'cache')
parser.add_argument('--backup-root', type=lambda p: Path(p).resolve(), default=local / 'backups')
parser.add_argument('--offline', action='store_true')
parser.add_argument('--yes', action='store_true')
parser.add_argument('--rr-preset', choices=('D', 'E'), help='build and deploy optional exact gfx1101 RR model')
parser.add_argument('--enable-rr', action='store_true', help='select generated native RR model in d4r.ini')
parser.add_argument('--enable-fsr', action='store_true', help='install the FSR denoiser and select Backend = fsr in d4r.ini')
parser.add_argument('--install-fsr', action='store_true',
help="also install AMD's pinned FSR Ray Regeneration denoiser (automatic when d4r.ini selects Backend = fsr)")
args = parser.parse_args()
runtime = args.package_root / 'd4r' if (args.package_root / 'd4r/d4r.ini').is_file() else args.package_root
if not (runtime / 'd4r.ini').is_file():
parser.error('Extract Windows release first; runtime d4r.ini missing')
game = runtime.parent
external(args.cache_root, game)
external(args.backup_root, game)
if args.enable_rr and not args.rr_preset:
parser.error('--enable-rr requires --rr-preset D or E')
pins = (runtime / 'dll-pins.txt').read_text(encoding='utf-8-sig')
for kind, (_, _, _, expected) in PINS.items():
if not re.search(r'^\s*' + kind + r'\s+' + expected + r'\s+\S+\s+\S+\s*$', pins, re.M | re.I):
raise RuntimeError('Release does not recognize pinned ' + kind)
if args.enable_fsr and args.rr_preset:
parser.error('--enable-fsr and --rr-preset select different RR backends')
fsr = args.install_fsr or args.enable_fsr or ini_selects_fsr(runtime)
if fsr and not all((runtime / 'fsr' / name).is_file() for name in ('d4r_vkd3d_core.dll', 'amdxc64.dll')):
parser.error("FSR Ray Regeneration needs this release's d4r/fsr payload (d4r_vkd3d_core.dll, amdxc64.dll); extract the complete ZIP")
if not args.yes and input('Download pinned DLSS/RR 310.9.1 and NGX 617.14 (~945 MiB archive)? [Y/n] ').lower().startswith('n'):
return
result = transaction(runtime, collect(args.cache_root, args.offline, fsr), args.backup_root)
print(json.dumps(result, indent=2))
if args.enable_fsr:
backup = enable_fsr(runtime, args.backup_root)
print(json.dumps({'fsrBackendSelected': True, 'configBackup': str(backup) if backup else None}, indent=2))
if args.rr_preset:
print('DLL installation verified. Building optional private RR model; a model failure leaves verified DLLs installed.', flush=True)
install_model(runtime, args.cache_root, args.backup_root, args.rr_preset, args.enable_rr)
if __name__ == '__main__':
main()
+139
View File
@@ -0,0 +1,139 @@
"""Synthetic noisy DLSS-RR-style scene with a known noise-free answer, in the rr_ngx_probe fixture format.
Left-handed view space, linear depth, render size == output size or output larger. Surfaces: a wall (stripes fixed in
WORLD space), a disc (a sphere's normals) and a sky band. The colour is (diffuse + specular albedo) * smooth lighting
times per-frame mean-preserving log-normal noise; the sky is noise-free. The camera may translate along world x
(--camera-speed per frame): the guides then carry the exact motion vectors (previous minus current, render pixels) and
the view matrix, and clean-<frame>.bin is the noise-free output-size image of that frame. clean.bin is frame 0's.
--extents W1xH1,W2xH2,... makes the render size change per frame (cycling, each within --size): the guides are written
at that frame's size and dynamic-extents.txt lists them (the probe's "dynamic-extents" mode).
No game, no NVIDIA runtime. Output stays under ignored build/.
"""
import argparse
import math
import sys
from pathlib import Path
repo = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(repo / '.tools/python/vendor'))
import numpy as np
FOV_DEGREES = 60.0
def scene(width, height, cam_x=0.0, prev_cam_x=None):
"""Returns depth, normal, diffuse, specular, clean colour, sky mask and motion (render pixels, prev - current)."""
if prev_cam_x is None:
prev_cam_x = cam_x
y, x = np.mgrid[0:height, 0:width].astype('f8')
u, v = (x + 0.5) / width * 2 - 1, 1 - (y + 0.5) / height * 2
aspect = width / height
tangent = math.tan(math.radians(FOV_DEGREES) / 2)
# camera-space ray scale: x_view = u * aspect * tangent * z, y_view = v * tangent * z
z = np.full((height, width), 10.0)
normal = np.zeros((height, width, 3)); normal[..., 2] = -1.0
diffuse = np.empty((height, width, 3)); diffuse[:] = (0.60, 0.50, 0.40)
# the disc: a world-fixed sphere of radius 0.9 centred at world (0.9, -0.5, 7.5)
centre = np.array([0.9, -0.5, 7.5]); radius = 0.9
ray = np.stack([u * aspect * tangent, v * tangent, np.ones_like(u)], axis=-1)
origin = np.array([cam_x, 0.0, 0.0])
oc = origin - centre
b = np.einsum('ijk,k->ij', ray, oc); a = np.einsum('ijk,ijk->ij', ray, ray); c = float(oc @ oc - radius * radius)
disc_term = b * b - a * c
hit = disc_term > 0
t = np.where(hit, (-b - np.sqrt(np.maximum(disc_term, 0))) / a, 0)
point = origin + ray * t[..., None]
disc = hit & (t > 0)
z[disc] = (t * ray[..., 2])[disc] # view z = t (the ray's z component is 1)
normal[disc] = ((point - centre) / radius)[disc]
world_x = cam_x + u * aspect * tangent * 10.0
stripe = (np.floor(world_x / 0.45).astype(int) % 2) == 0
diffuse[stripe & ~disc] = (0.35, 0.45, 0.55)
diffuse[disc] = (0.80, 0.25, 0.20)
sky = v > 0.7
z[sky] = 1.0e8; normal[sky] = 0.0; diffuse[sky] = 0.0; disc = disc & ~sky
specular = np.full((height, width, 3), 0.04); specular[sky] = 0.0
# lighting is a function of the surface point (world x, screen-independent y), so it moves with the scene
surf_x = np.where(disc, point[..., 0], world_x)
light = (1.5 + 0.8 * np.sin(surf_x * 2.2) * np.cos(y * 360.0 / height / 41.0))[..., None] * np.array([1.0, 0.92, 0.8])
clean = (diffuse + specular) * light
clean[sky] = (0.35, 0.55, 0.95)
# motion: previous minus current screen position of the same surface point, in render pixels
motion = np.zeros((height, width, 2))
dx = cam_x - prev_cam_x
if dx != 0.0:
depth = np.where(sky, 1.0, z)
motion[..., 0] = dx / (depth * tangent * aspect) * (width / 2.0)
motion[sky] = 0.0
return z, normal, diffuse, specular, clean, sky, motion
def half_bytes(a):
return np.ascontiguousarray(a.astype('<f2')).tobytes()
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--output', required=True, type=Path)
parser.add_argument('--size', default='640x360', help='render size (the feature creation size)')
parser.add_argument('--out-size', help='output size (default: render size)')
parser.add_argument('--frames', type=int, default=24)
parser.add_argument('--noise', type=float, default=0.6, help='log-normal sigma of the per-frame colour noise')
parser.add_argument('--camera-speed', type=float, default=0.0, help='camera translation along world x per frame')
parser.add_argument('--extents', help='per-frame render sizes WxH,WxH,... cycling (each within --size, aspect ratio kept)')
parser.add_argument('--seed', type=int, default=7)
args = parser.parse_args()
if not args.output.resolve().is_relative_to((repo / 'build').resolve()):
parser.error('output must stay under ignored build/')
args.output.mkdir(parents=True, exist_ok=False)
width, height = map(int, args.size.split('x'))
out_width, out_height = map(int, (args.out_size or args.size).split('x'))
extents = [tuple(map(int, item.split('x'))) for item in args.extents.split(',')] if args.extents else [(width, height)]
for ew, eh in extents:
if ew < 64 or eh < 64 or ew > width or eh > height:
parser.error('every extent must be at least 64 and within --size')
rng = np.random.default_rng(args.seed)
f = 1.0 / math.tan(math.radians(FOV_DEGREES) / 2)
near, far = 0.1, 1000.0
lines = [f'size {width} {height} {out_width} {out_height}']
extent_lines = []
for i in range(args.frames):
fw, fh = extents[i % len(extents)]
extent_lines.append(f'{fw} {fh}')
aspect = fw / fh
proj = [f / aspect, 0, 0, 0, 0, f, 0, 0, 0, 0, far / (far - near), 1, 0, 0, -near * far / (far - near), 0]
cam = args.camera_speed * i
z, normal, diffuse, specular, clean_render, sky, motion = scene(fw, fh, cam, cam - args.camera_speed if i else cam)
if (out_width, out_height) == (fw, fh):
clean_out, sky_out = clean_render, sky
else:
clean_out, sky_out = scene(out_width, out_height, cam)[4:6]
noise = np.exp(args.noise * rng.standard_normal((fh, fw, 3)) - 0.5 * args.noise ** 2)
noise[sky] = 1.0
color = np.concatenate([clean_render * noise, np.ones((fh, fw, 1))], axis=-1)
rgba = lambda c3: np.concatenate([c3, np.zeros((fh, fw, 1))], axis=-1)
(args.output / f'{i}-color.bin').write_bytes(half_bytes(color))
(args.output / f'{i}-motion.bin').write_bytes(half_bytes(motion))
(args.output / f'{i}-depth.bin').write_bytes(np.ascontiguousarray(z.astype('<f4')).tobytes())
(args.output / f'{i}-normals.bin').write_bytes(half_bytes(rgba(normal)))
rough = np.zeros((fh, fw, 4)); rough[..., 0] = 0.5
(args.output / f'{i}-roughness.bin').write_bytes(half_bytes(rough))
alb = lambda a: np.concatenate([np.round(a * 255), np.full((fh, fw, 1), 255)], axis=-1).astype('u1').tobytes()
(args.output / f'{i}-diffuse.bin').write_bytes(alb(diffuse))
(args.output / f'{i}-specular.bin').write_bytes(alb(specular))
view = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -cam, 0, 0, 1] # row-vector world -> view: translate by -camera
lines.append('frame %d 0 0 1 1 1 %d 0 ' % (i, 1 if i == 0 else 0) + ' '.join(f'{v:.9g}' for v in view + proj))
(args.output / f'clean-{i}.bin').write_bytes(half_bytes(np.concatenate([clean_out, np.ones((out_height, out_width, 1))], axis=-1)))
if (out_width, out_height) != (fw, fh): # the best a render-resolution result could be, before upscaling
(args.output / f'clean-render-{i}.bin').write_bytes(half_bytes(np.concatenate([clean_render, np.ones((fh, fw, 1))], axis=-1)))
if i == 0:
(args.output / 'clean.bin').write_bytes((args.output / 'clean-0.bin').read_bytes())
np.save(args.output / 'sky.npy', sky_out)
(args.output / 'frames.txt').write_text('\n'.join(lines) + '\n')
if args.extents:
(args.output / 'dynamic-extents.txt').write_text('\n'.join(extent_lines) + '\n')
print('wrote', args.output)
if __name__ == '__main__':
main()
+29
View File
@@ -14,6 +14,7 @@
#include <atomic>
#include <memory>
#include <mutex>
#include <array>
namespace d4r::win {
inline std::string env_path(const char* name) {
@@ -312,6 +313,34 @@ public:
std::lock_guard<std::mutex> lock(rt_->mutex); cleanup();
}
Feature(const Feature&) = delete;
// Query the loaded SR library, using NVIDIA's optimal-settings callback contract.
// An unavailable/invalid query keeps the conservative creation-size-only range.
std::array<unsigned, 4> render_limits() {
std::lock_guard<std::mutex> lock(rt_->mutex);
rt_->current();
std::array<unsigned, 4> limits{width, height, width, height};
void* caps = nullptr;
if (rt_->capabilities(&caps) != 1 || !caps) return limits;
struct Guard { Runtime& rt; void* p; ~Guard() { (void)rt.destroy(p); } } guard{*rt_, caps};
void* callback = nullptr;
if (d4r_ngx_get_void(caps, "DLSSOptimalSettingsCallback", &callback) != 1 || !callback) return limits;
d4r_ngx_set_uint(caps, "Width", ngx::uint_value(parameters_, "OutWidth"));
d4r_ngx_set_uint(caps, "Height", ngx::uint_value(parameters_, "OutHeight"));
d4r_ngx_set_int(caps, "PerfQualityValue", ngx::int_value(parameters_, "PerfQualityValue"));
d4r_ngx_set_int(caps, "RTXValue", 0);
using Optimal = unsigned(*)(void*);
if (reinterpret_cast<Optimal>(callback)(caps) != 1) return limits;
const unsigned optimalWidth = ngx::uint_value(caps, "OutWidth");
const unsigned optimalHeight = ngx::uint_value(caps, "OutHeight");
const std::array<unsigned, 4> queried{
ngx::uint_value(caps, "DLSS.Get.Dynamic.Min.Render.Width", optimalWidth),
ngx::uint_value(caps, "DLSS.Get.Dynamic.Min.Render.Height", optimalHeight),
ngx::uint_value(caps, "DLSS.Get.Dynamic.Max.Render.Width", optimalWidth),
ngx::uint_value(caps, "DLSS.Get.Dynamic.Max.Render.Height", optimalHeight)};
if (queried[0] && queried[1] && queried[2] >= queried[0] && queried[3] >= queried[1]) limits = queried;
std::fprintf(stderr, "D4R_SR_RENDER_LIMITS min=%ux%u max=%ux%u\n", limits[0], limits[1], limits[2], limits[3]);
return limits;
}
void drain() {
if (!std::getenv("D4R_ASYNC_INTEROP")) return;
wait_worker();
+27 -2
View File
@@ -13,6 +13,8 @@
#include "hip_api.h"
#include "ngx_parameters.h"
#include <bcrypt.h>
#include <cmath>
#include <cstdlib>
#include <fstream>
#include <mutex>
#include <optional>
@@ -25,10 +27,14 @@ struct Settings {
unsigned preset = 11; // K
bool nativeFp8 = false;
bool asyncInterop = false, validateOutput = false, verbose = false, showWatermark = false;
bool batchInputCopies = false;
std::wstring cacheDir;
std::wstring nativeDir;
std::string rrBackend = "off";
std::wstring rrModelDir;
std::string fsrStabilityBias = "auto"; // [RayReconstruction] FsrStabilityBias: auto (AMD's default 1) or a number
std::string fsrDetail; // [RayReconstruction] FsrDetail: 0-1 share of the colour that bypasses the denoiser
std::wstring fsrDenoiser; // [RayReconstruction] FsrDenoiser: AMD's amd_fidelityfx_denoiser_dx12.dll (default: beside the shim)
};
struct State {
bool active = false;
@@ -117,6 +123,7 @@ inline Settings read_settings(const std::vector<IniEntry>& ini, std::vector<std:
} else if (name == "kernels.nativefp8") settings.nativeFp8 = flag(entry, false);
else if (name == "dlss.showwatermark") settings.showWatermark = flag(entry, false);
else if (name == "interop.asyncinterop") settings.asyncInterop = flag(entry, false);
else if (name == "interop.batchinputcopies") settings.batchInputCopies = flag(entry, false);
else if (name == "debug.validateoutput") settings.validateOutput = flag(entry, false);
else if (name == "debug.log") {
const auto value = lower(entry.value);
@@ -132,8 +139,18 @@ inline Settings read_settings(const std::vector<IniEntry>& ini, std::vector<std:
if (value.empty() || value == "auto" || value == "off") settings.rrBackend = "off";
else if (value == "native" || value == "fsr") {
settings.rrBackend = value;
if (value == "fsr") notes.push_back("d4r.ini: native Windows FSR RR is unavailable; RR requests will be rejected");
if (value == "fsr") notes.push_back("d4r.ini: native Windows FSR RR is experimental: it runs AMD's denoiser on a private vkd3d-proton device (d4r\\fsr) and needs AMD's amd_fidelityfx_denoiser_dx12.dll");
} else { settings.rrBackend = "off"; notes.push_back("d4r.ini: [RayReconstruction] Backend must be off, native or fsr; using off"); }
} else if (name == "rayreconstruction.fsrstabilitybias" || name == "rayreconstruction.fsrdetail") {
const bool bias = name == "rayreconstruction.fsrstabilitybias";
const auto value = lower(entry.value);
char* end = nullptr; const float number = std::strtof(value.c_str(), &end);
const bool numeric = !value.empty() && end == value.c_str() + value.size() && std::isfinite(number) && number >= 0.0f && (bias || number <= 1.0f);
if (bias && (value.empty() || value == "auto")) settings.fsrStabilityBias = "auto";
else if (numeric) (bias ? settings.fsrStabilityBias : settings.fsrDetail) = value;
else notes.push_back(std::string("d4r.ini: [RayReconstruction] ") + entry.key + " must be " + (bias ? "auto or a number >= 0" : "a number from 0 to 1") + ", not '" + entry.value + "'; using the default");
} else if (name == "rayreconstruction.fsrdenoiser") {
settings.fsrDenoiser = !entry.value.empty() && lower(entry.value) != "auto" ? diag::wide(entry.value) : L"";
} else if (name == "rayreconstruction.modeldir") {
settings.rrModelDir = !entry.value.empty() && lower(entry.value) != "auto" ? diag::wide(entry.value) : L"";
} else notes.push_back("d4r.ini: [" + entry.section + "] " + entry.key + " is not a Windows setting; ignored");
@@ -313,6 +330,13 @@ inline void apply(const std::filesystem::path& dir, const Settings& settings) {
const std::pair<const wchar_t*, std::optional<std::wstring>> values[] = {
{L"D4R_RR_BACKEND", settings.rrBackend == "off" ? unset : std::optional<std::wstring>(diag::wide(settings.rrBackend))},
{L"D4R_RR_MODEL_DIR", settings.rrBackend == "native" ? std::optional<std::wstring>(rr_model_directory(dir, settings).wstring()) : unset},
{L"D4R_FSR_VKD3D_CORE", settings.rrBackend == "fsr" ? p(L"fsr\\d4r_vkd3d_core.dll") : unset},
{L"D4R_FSR_AMDXC_SHIM", settings.rrBackend == "fsr" ? p(L"fsr\\amdxc64.dll") : unset},
{L"D4R_FSR_RR_DLL", settings.rrBackend == "fsr" ? (settings.fsrDenoiser.empty() ? p(L"amd_fidelityfx_denoiser_dx12.dll") :
std::optional<std::wstring>(std::filesystem::absolute(std::filesystem::path(settings.fsrDenoiser).is_relative() ? dir / settings.fsrDenoiser : std::filesystem::path(settings.fsrDenoiser)).lexically_normal().wstring())) : unset},
{L"D4R_FSR_RR_STABILITY_BIAS", settings.rrBackend == "fsr" && settings.fsrStabilityBias != "auto" ? std::optional<std::wstring>(diag::wide(settings.fsrStabilityBias)) : unset},
{L"D4R_FSR_RR_DETAIL", settings.rrBackend == "fsr" && !settings.fsrDetail.empty() ? std::optional<std::wstring>(diag::wide(settings.fsrDetail)) : unset},
{L"D4R_FSR_CACHE_DIR", settings.rrBackend == "fsr" ? std::optional<std::wstring>((cache / L"vkd3d").wstring()) : unset},
{L"D4R_HIP_ROOT", p(L"hip")}, {L"HIP_PATH", p(L"hip")},
{L"D4R_NVCUDA_DLL", p(L"zluda\\nvcuda.dll")}, {L"ZLUDA_CUDA_LIB", p(L"zluda\\nvcuda.dll")},
{L"D4R_NVAPI_DLL", std::optional<std::wstring>(compatNvapi.wstring())},
@@ -331,11 +355,12 @@ inline void apply(const std::filesystem::path& dir, const Settings& settings) {
{L"D4R_QUIET_API", settings.verbose ? unset : on},
{L"D4R_VALIDATE_OUTPUT", settings.validateOutput ? on : unset},
{L"D4R_ASYNC_INTEROP", settings.asyncInterop ? on : unset},
{L"D4R_BATCH_INPUT_COPIES", settings.batchInputCopies ? on : unset},
{L"D4R_SHIM_WATERMARK", settings.showWatermark ? on : unset},
{L"D4R_PROFILE_STAGES", unset}, {L"D4R_ZLUDA_PROFILE", unset}, {L"D4R_ZLUDA_PROFILE_DEFERRED", unset},
{L"D4R_ZLUDA_PROFILE_ALLOW_LEGACY", unset}, {L"D4R_ZLUDA_PROFILE_EVERY", unset},
{L"D4R_PROFILE_COMMAND_HOOKS", unset}, {L"D4R_ZLUDA_PROFILE_API", unset}, {L"D4R_PROFILE_GPU_BOUNDARY", unset},
{L"D4R_DISABLE_INTEROP_LIST_CACHE", unset}, {L"D4R_BATCH_INPUT_COPIES", unset}, {L"D4R_INTEROP_VERIFY", unset},
{L"D4R_DISABLE_INTEROP_LIST_CACHE", unset}, {L"D4R_INTEROP_VERIFY", unset},
{L"D4R_DIAG_DIR", unset}, {L"ZLUDA_LOG_DIR", unset},
{L"ZLUDA_CACHE_DIR", std::optional<std::wstring>(cache.wstring())},
{L"PATH", std::optional<std::wstring>((dir / L"hip\\bin").wstring() + L";" + (dir / L"zluda").wstring() +
+31 -2
View File
@@ -46,6 +46,16 @@ int wmain(int argc, wchar_t** argv) {
expect(s.preset == 12 && has_note(notes, "not validated"), "Model = 12 selects NGX preset 12 with limitation note");
s = settings("[Interop]\nAsyncInterop = yes\n[Debug]\nValidateOutput = on\nLog = verbose\n", notes);
expect(s.asyncInterop && s.validateOutput && s.verbose && notes.empty(), "boolean spellings and Log = verbose");
s = settings("[Interop]\nBatchInputCopies = yes\n", notes);
expect(s.batchInputCopies && notes.empty(), "input batching selected through player INI");
s.cacheDir = (dir / L"cache").wstring(); player::apply(dir, s);
expect(std::getenv("D4R_BATCH_INPUT_COPIES") != nullptr, "player environment enables input batching");
s = settings("[Interop]\nBatchInputCopies = invalid\n", notes);
expect(!s.batchInputCopies && has_note(notes, "BatchInputCopies must be true or false"), "invalid batching falls back to unbatched with a note");
s.cacheDir = (dir / L"cache").wstring(); player::apply(dir, s);
expect(std::getenv("D4R_BATCH_INPUT_COPIES") == nullptr, "player environment clears earlier input batching");
s = settings("", notes);
expect(!s.batchInputCopies && notes.empty(), "older INIs retain unbatched behavior");
s = settings("[Debug]\nValidateOutput = maybe\nLog = loud\n", notes);
expect(!s.validateOutput && !s.verbose && has_note(notes, "must be true or false, not 'maybe'") &&
has_note(notes, "must be normal or verbose"), "invalid values fall back with plain notes");
@@ -77,11 +87,30 @@ int wmain(int argc, wchar_t** argv) {
expect(std::string(std::getenv("D4R_RR_BACKEND")) == "native" &&
std::filesystem::path(_wgetenv(L"D4R_RR_MODEL_DIR")) == player::rr_model_directory(dir, s), "native RR environment selected without a launcher");
s = settings("[RayReconstruction]\nBackend = fsr\n", notes);
expect(s.rrBackend == "fsr" && has_note(notes, "FSR RR is unavailable"), "unavailable FSR selection remains explicit");
expect(s.rrBackend == "fsr" && has_note(notes, "FSR RR is experimental"), "FSR selection is reported as experimental");
s = settings("[RayReconstruction]\nBackend = fsr\nFsrDenoiser = amd\\denoiser.dll\n", notes);
s.cacheDir = (dir / L"cache").wstring(); player::apply(dir, s);
expect(std::string(std::getenv("D4R_RR_BACKEND")) == "fsr" &&
std::filesystem::path(_wgetenv(L"D4R_FSR_RR_DLL")) == std::filesystem::absolute(dir / L"amd" / L"denoiser.dll").lexically_normal() &&
std::filesystem::path(_wgetenv(L"D4R_FSR_VKD3D_CORE")) == dir / L"fsr" / L"d4r_vkd3d_core.dll" &&
std::filesystem::path(_wgetenv(L"D4R_FSR_AMDXC_SHIM")) == dir / L"fsr" / L"amdxc64.dll" &&
std::filesystem::path(_wgetenv(L"D4R_FSR_CACHE_DIR")) == dir / L"cache" / L"vkd3d" && !std::getenv("D4R_RR_MODEL_DIR"),
"FSR environment selected from d4r.ini without a launcher");
s = settings("[RayReconstruction]\nBackend = fsr\nFsrStabilityBias = 0\nFsrDetail = 0.25\n", notes);
s.cacheDir = (dir / L"cache").wstring(); player::apply(dir, s);
expect(notes.empty() || has_note(notes, "experimental"), "FSR tuning keys are recognized");
expect(std::string(std::getenv("D4R_FSR_RR_STABILITY_BIAS")) == "0" && std::string(std::getenv("D4R_FSR_RR_DETAIL")) == "0.25",
"FSR tuning reaches the environment");
s = settings("[RayReconstruction]\nBackend = fsr\nFsrStabilityBias = auto\nFsrDetail = 2\n", notes);
s.cacheDir = (dir / L"cache").wstring(); player::apply(dir, s);
expect(has_note(notes, "FsrDetail must be a number from 0 to 1") && !std::getenv("D4R_FSR_RR_STABILITY_BIAS") && !std::getenv("D4R_FSR_RR_DETAIL"),
"auto bias leaves AMD's default and an out-of-range detail is rejected");
s = settings("[RayReconstruction]\nBackend = typo\n", notes);
expect(s.rrBackend == "off" && has_note(notes, "Backend must be"), "invalid RR backend stays off");
s = settings("", notes); s.cacheDir = (dir / L"cache").wstring(); player::apply(dir, s);
expect(!std::getenv("D4R_RR_BACKEND") && !std::getenv("D4R_RR_MODEL_DIR"), "default RR off clears stale selection");
expect(!std::getenv("D4R_RR_BACKEND") && !std::getenv("D4R_RR_MODEL_DIR") && !std::getenv("D4R_FSR_RR_DLL") &&
!std::getenv("D4R_FSR_VKD3D_CORE") && !std::getenv("D4R_FSR_AMDXC_SHIM") && !std::getenv("D4R_FSR_CACHE_DIR") &&
!std::getenv("D4R_FSR_RR_STABILITY_BIAS") && !std::getenv("D4R_FSR_RR_DETAIL"), "default RR off clears stale selection");
std::filesystem::create_directories(dir / L"nvapi");
{ std::ofstream(dir / L"nvapi" / L"nvapi64.dll", std::ios::binary) << "generic nvapi fixture"; }
@@ -0,0 +1,72 @@
"""Build bounded synthetic dynamic-size RR references with the standalone engine.
Reference resets at each size change; combined NGX frames deliberately omit that
reset to verify the adapter's implicit history reset. This is engine consistency,
not an NVIDIA image-quality reference. No game files are read or written.
"""
import argparse
import json
import os
from pathlib import Path
import shutil
import struct
import subprocess
import sys
def main():
repo = Path(__file__).resolve().parents[2]
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--runner', required=True, type=lambda p: Path(p).resolve())
parser.add_argument('--model', required=True, type=lambda p: Path(p).resolve())
parser.add_argument('--output', required=True, type=lambda p: Path(p).resolve())
args = parser.parse_args()
if args.output.exists() or not args.output.is_relative_to((repo / 'build').resolve()):
parser.error('use a fresh directory under ignored build/')
args.output.mkdir(parents=True)
inputs, reference = args.output / 'inputs', args.output / 'reference'
inputs.mkdir(); reference.mkdir()
lines, extents = ['size 96 96 192 192'], []
env = {key: value for key, value in os.environ.items() if not key.startswith('D4R_')}
env['D4R_RR_TIME_REPEAT'] = '1'
index = 0
for segment, (width, height) in enumerate(((96, 96), (64, 64), (96, 96), (72, 80))):
source, result = args.output / f'segment-{segment}', args.output / f'reference-{segment}'
subprocess.run([sys.executable, str(repo / 'scripts/windows/rr-synthetic-inputs.py'), str(source),
'--width', str(width), '--height', str(height), '--frames', '2',
'--output-width', '192', '--output-height', '192'], check=True, timeout=60)
# Exercise optional-view rebinding across reconfiguration and an absent
# guide frame. Fixture uploads and standalone replay use the same values.
for frame in (1,):
for name, texel in (('hitdistance', struct.pack('<f', 2.0)),
('disocclusion', struct.pack('<e', 0.0)),
('sss', struct.pack('<e', 0.125)),
('particles', struct.pack('<4e', 0.2, 0.3, 0.4, 1.0))):
(source / f'{frame}-{name}.bin').write_bytes(texel * (width * height))
result.mkdir()
with (args.output / f'segment-{segment}.log').open('wb') as log:
subprocess.run([str(args.runner), str(args.model), str(source), str(result)],
env=env, stdout=log, stderr=subprocess.STDOUT, check=True, timeout=180)
frames = (source / 'frames.txt').read_text().splitlines()[1:]
for local, line in enumerate(frames):
fields = line.split()
fields[1] = str(index)
if segment and local == 0:
fields[7] = '0' # omit caller reset on a size change
lines.append(' '.join(fields))
extents.append([width, height])
for path in source.glob(f'{local}-*.bin'):
shutil.copy2(path, inputs / (str(index) + path.name[len(str(local)):]))
shutil.copy2(result / f'{local}-out.bin', reference / f'{index}-out.bin')
index += 1
(inputs / 'frames.txt').write_text('\n'.join(lines) + '\n')
(inputs / 'dynamic-extents.txt').write_text(''.join(f'{w} {h}\n' for w, h in extents))
report = dict(kind='synthetic-dynamic-rr-engine-consistency', extents=extents,
frames=index, callerResetOnSizeChange=False, qualityReference=False,
model=str(args.model), runner=str(args.runner), gameAccessed=False)
(args.output / 'fixture.json').write_text(json.dumps(report, indent=2) + '\n')
print(json.dumps(report, indent=2))
if __name__ == '__main__':
main()
+83 -4
View File
@@ -17,6 +17,11 @@ def main():
parser = argparse.ArgumentParser(description=__doc__)
for name in ('base-zip', 'shim', 'output', 'archive'):
parser.add_argument('--' + name, required=True, type=lambda value: Path(value).resolve())
parser.add_argument('--rr-tools', required=True, type=lambda value: Path(value).resolve())
parser.add_argument('--native-fix', type=lambda value: Path(value).resolve(),
help='directory of rebuilt gfx1101 K layer .hsaco files that replace same-named packaged objects')
parser.add_argument('--fsr-payload', type=lambda value: Path(value).resolve(),
help='build_fsr_payload.py output to add as d4r/fsr (experimental FSR RR; AMD denoiser never bundled)')
args = parser.parse_args()
repo = Path(__file__).resolve().parents[2]
build = (repo / 'build').resolve()
@@ -26,8 +31,10 @@ def main():
if args.archive.is_relative_to(args.output):
parser.error('archive must be outside the package directory')
base_hash = sha(args.base_zip)
if base_hash != 'e93be288f4013616cc2229be0132b40395d5031d8733bfa63886ba75ce1389ca':
parser.error('base must be the verified local v3 ZIP')
# verified local ZIPs this refresher may start from: v3, and v8 (build/d4r-windows-gfx1101-local-v8.zip)
if base_hash not in ('e93be288f4013616cc2229be0132b40395d5031d8733bfa63886ba75ce1389ca',
'962530d1a914268cc6f9d2ec908185f8e4d9e5baafca6800d77ebc3281a647fe'):
parser.error('base must be a verified local ZIP (v3 or v8)')
with zipfile.ZipFile(args.base_zip) as base:
manifest = json.loads(base.read('d4r/package.json').decode('utf-8-sig'))
if not manifest.get('localDevelopment') or set(manifest['targets']) != {'gfx1101'}:
@@ -60,7 +67,76 @@ def main():
raise RuntimeError('Overlay is not part of the validated package layout: ' + name)
shutil.copy2(source, args.output / name)
records[name] = {'path': name, 'sha256': sha(source).upper(), 'version': None}
prohibited = {'nvngx_dlss.dll', 'nvngx_dlssd.dll'}
native_report = None
if args.native_fix:
# Replace named K layer code objects with rebuilt ones (e.g. the pwin_pos.h barrier fix). Each must already be part
# of the package's gfx1101 native folder and must be a gfx1101 AMDGPU ELF (e_machine 224, EF_AMDGPU_MACH byte 70).
replaced = {}
for source in sorted(args.native_fix.glob('*.hsaco')):
relative = 'd4r/native/gfx1101/' + source.name
data = source.read_bytes()
if relative not in records or data[:4] != b'\x7fELF' or data[4] != 2 or data[18] != 0xe0 or data[48] != 70:
raise RuntimeError('Native fix is not a replacement gfx1101 code object: ' + source.name)
shutil.copy2(source, args.output / relative)
replaced[relative] = {'before': records[relative]['sha256'], 'after': sha(source).upper()}
records[relative] = {'path': relative, 'sha256': sha(source).upper(), 'version': None}
if not replaced:
raise RuntimeError('--native-fix directory has no .hsaco files')
native_report = replaced
installer = repo / 'tools/windows/install_dependencies.py'
shutil.copy2(installer, args.output / 'install-dependencies.py')
records['install-dependencies.py'] = {'path': 'install-dependencies.py', 'sha256': sha(installer).upper(), 'version': None}
fsr_report = None
if args.fsr_payload:
# Experimental FSR RR: d4r-built vkd3d-proton core (LGPL, SOURCE.txt is the source offer) and d4r's amdxc64 shim.
payload = json.loads((args.fsr_payload / 'fsr-payload.json').read_text(encoding='utf-8'))
if payload.get('kind') != 'd4r-fsr-payload' or payload.get('amdDenoiserBundled') is not False:
raise RuntimeError('Unsupported FSR payload manifest')
additions = {}
for name, digest in payload['files'].items():
source = args.fsr_payload / name
if sha(source) != digest:
raise RuntimeError('FSR payload file changed: ' + name)
if name.startswith('fsr/'):
additions['d4r/' + name] = source
elif name.startswith('licenses/'):
additions['d4r/' + name] = source
elif name == 'SOURCE.txt':
additions['d4r/fsr/SOURCE.txt'] = source
for patch in sorted((repo / 'patches/vkd3d-proton-windows').glob('*.patch')):
additions['d4r/fsr/patches/' + patch.name] = patch
for relative, source in additions.items():
if relative in records:
raise RuntimeError('FSR payload path already in package: ' + relative)
destination = args.output / relative
destination.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(source, destination)
records[relative] = {'path': relative, 'sha256': sha(source).upper(), 'version': None}
fsr_report = {'payloadManifestSha256': sha(args.fsr_payload / 'fsr-payload.json'), 'files': sorted(additions),
'vkd3dProtonCommit': payload['vkd3dProtonCommit'], 'amdDenoiserBundled': False}
# Consume the pristine archive, never an extracted/used tool directory.
if sha(args.rr_tools) != '297289a57a82142eb952479c99112b1c352a6753eb15bc6b2431bf686aed9b22':
raise RuntimeError('Requires verified clean portable-tools v2 ZIP')
with zipfile.ZipFile(args.rr_tools) as tools:
tool_manifest = json.loads(tools.read('rr-tools.json'))
expected_files = tool_manifest['files']
names = [entry.filename for entry in tools.infolist() if not entry.is_dir()]
if len(names) != len(set(names)) or set(names) != set(expected_files) | {'rr-tools.json'}:
raise RuntimeError('Portable-tools ZIP entries differ from manifest')
for name in names:
parts = PurePosixPath(name)
if parts.is_absolute() or '..' in parts.parts or '\\' in name or ':' in name:
raise RuntimeError('Unsafe portable-tools path')
data = tools.read(name)
digest = hashlib.sha256(data).hexdigest()
if name in expected_files and digest != expected_files[name]:
raise RuntimeError('Portable-tools hash mismatch: ' + name)
relative = 'd4r/model-tools/' + name
destination = args.output / relative
destination.parent.mkdir(parents=True, exist_ok=True)
destination.write_bytes(data)
records[relative] = {'path': relative, 'sha256': digest.upper(), 'version': None}
prohibited = {'nvngx_dlss.dll', 'nvngx_dlssd.dll', 'amd_fidelityfx_denoiser_dx12.dll'}
for path in args.output.rglob('*'):
if path.is_file() and (path.name.lower() in prohibited or path.suffix.lower() in ('.spv', '.ptx', '.cubin', '.weights')):
raise RuntimeError('Proprietary/model artifact cannot be bundled: ' + str(path))
@@ -69,7 +145,10 @@ def main():
files=list(records.values()),
localRefresh={'baseZipSha256': base_hash, 'binaryProvenance': 'local modified build; hashes authoritative',
'overlays': {name: sha(path) for name, path in overlays.items()},
'rr': 'experimental synthetic validation; disabled by default; models generated separately'})
'rrToolsZipSha256': sha(args.rr_tools),
'installerSha256': sha(installer),
'fsrPayload': fsr_report, 'nativeFix': native_report,
'rr': 'experimental synthetic validation; optional local D/E model generation; disabled by default'})
manifest['targets']['gfx1101']['binarySourceCommit'] = None
manifest['targets']['gfx1101']['hardwareValidation'] = 'SR prior limited evidence; native RR synthetic NGX lifecycle only; no game RR or performance parity'
(args.output / 'd4r/package.json').write_text(json.dumps(manifest, indent=2) + '\n')
+101 -15
View File
@@ -45,6 +45,8 @@ struct Layout {
{"sss", VK_FORMAT_R16_SFLOAT, 2, 0, 0, false},
{"particles", VK_FORMAT_R16G16B16A16_SFLOAT, 8, 0, 0, false}}};
VkDeviceSize bytes = 0, outputOffset = 0, outputPitch = 0;
VkFormat outputFormat = VK_FORMAT_R16G16B16A16_SFLOAT;
uint32_t outputBytes = 8;
std::filesystem::path inputs, output, reference;
std::vector<Frame> frames;
// Runtime layout construction performs no fixture I/O. Both callers use
@@ -63,14 +65,14 @@ struct Layout {
bytes += plane.pitch * rh;
}
outputOffset = (bytes + 511) & ~VkDeviceSize(511);
outputPitch = (VkDeviceSize(ow) * 8 + 255) & ~VkDeviceSize(255);
outputPitch = (VkDeviceSize(ow) * outputBytes + 255) & ~VkDeviceSize(255);
bytes = outputOffset + outputPitch * oh;
}
Layout(const std::string& inputDir, const std::string& outputDir, const std::string& referenceDir)
: inputs(inputDir), output(outputDir), reference(referenceDir) {
std::ifstream f(inputs / "frames.txt"); std::string token;
if (!(f >> token >> rw >> rh >> ow >> oh) || token != "size" || rw < 32 || rh < 32 ||
rw > 1024 || rh > 1024 || !ow || !oh || ow > 2560 || oh > 1440)
rw > 2560 || rh > 1440 || !ow || !oh || ow > 2560 || oh > 1440)
throw std::runtime_error("Invalid bounded RR replay geometry");
while (f >> token) {
Frame frame{}; int reset, mode;
@@ -119,21 +121,34 @@ struct Layout {
}
}
void validate(const void* source, unsigned frame) const {
std::vector<char> packed(size_t(ow) * oh * 8);
std::vector<char> packed(size_t(ow) * oh * outputBytes);
for (uint32_t y = 0; y < oh; ++y)
std::memcpy(packed.data() + size_t(y) * ow * 8,
static_cast<const char*>(source) + outputOffset + y * outputPitch, size_t(ow) * 8);
std::memcpy(packed.data() + size_t(y) * ow * outputBytes,
static_cast<const char*>(source) + outputOffset + y * outputPitch, size_t(ow) * outputBytes);
double low = std::numeric_limits<double>::infinity(), high = -low;
for (size_t i = 0; i < packed.size() / 2; ++i) {
uint16_t bits; std::memcpy(&bits, packed.data() + i * 2, 2);
const double value = d4r::diag::binary16::half_value(bits);
for (size_t i = 0; i < size_t(ow) * oh * 4; ++i) {
double value;
if (outputBytes == 16) { float v; std::memcpy(&v, packed.data() + i * 4, 4); value = v; }
else { uint16_t bits; std::memcpy(&bits, packed.data() + i * 2, 2); value = d4r::diag::binary16::half_value(bits); }
if (!std::isfinite(value)) throw std::runtime_error("RR output contains NaN/Inf");
if (i % 4 != 3) { low = std::min(low, value); high = std::max(high, value); }
}
if (!(high - low > 1e-5)) throw std::runtime_error("RR output is uniform");
const auto name = std::to_string(frames.at(frame).id) + "-out.bin";
if (!reference.empty() && read(reference / name) != packed)
throw std::runtime_error("Bridged RR output differs from standalone replay");
if (!reference.empty()) {
auto expected = read(reference / name);
if (outputBytes == 16) {
if (expected.size() != size_t(ow) * oh * 8) throw std::runtime_error("Invalid half output reference size");
std::vector<char> widened(packed.size());
for (size_t i = 0; i < expected.size() / 2; ++i) {
uint16_t bits; std::memcpy(&bits, expected.data() + i * 2, 2);
const float value = float(d4r::diag::binary16::half_value(bits));
std::memcpy(widened.data() + i * 4, &value, 4);
}
expected = std::move(widened);
}
if (expected != packed) throw std::runtime_error("Bridged RR output differs from standalone replay");
}
std::ofstream file(output / name, std::ios::binary); file.write(packed.data(), std::streamsize(packed.size()));
if (!file) throw std::runtime_error("RR output write failed");
std::printf("RR_BRIDGED_FRAME id=%u finite=1 nonuniform=1 standalone_exact=%u output=%ux%u\n",
@@ -187,7 +202,7 @@ struct D3D12Frames {
};
for (size_t i = 0; i < inputs.size(); ++i) if (l.planes[i].enabled)
create(i == 2 && depthFormat != DXGI_FORMAT_UNKNOWN ? depthFormat : format(l.planes[i].format), l.rw, l.rh, false, inputs[i].GetAddressOf());
create(DXGI_FORMAT_R16G16B16A16_FLOAT, l.ow, l.oh, true, output.GetAddressOf());
create(format(l.outputFormat), l.ow, l.oh, true, output.GetAddressOf());
}
void recordInputs(ID3D12GraphicsCommandList* cb, ID3D12Resource* upload, ID3D12Resource* shared, uint32_t optionalMask = 15) {
for (size_t i = 0; i < inputs.size(); ++i) {
@@ -213,7 +228,18 @@ struct D3D12Frames {
cb->CopyTextureRegion(&image, 0, 0, 0, &src, nullptr);
barrier(cb, input, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COPY_SOURCE);
auto dst = placed(shared, plane.offset, plane.pitch, copyFormat, layout.rw, layout.rh);
cb->CopyTextureRegion(&dst, 0, 0, 0, &image, nullptr);
// A dynamic subrect can occupy a creation-sized fixture texture.
// Only depth-stencil resources require whole-subresource copies.
Microsoft::WRL::ComPtr<ID3D12Device> owner; checkD3D(input->GetDevice(IID_PPV_ARGS(&owner)));
D3D12_RESOURCE_DESC description{};
#if defined(__MINGW32__)
input->GetDesc(&description);
#else
description = input->GetDesc();
#endif
D3D12_BOX box{0, 0, 0, layout.rw, layout.rh, 1};
cb->CopyTextureRegion(&dst, 0, 0, 0, &image,
(description.Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) ? nullptr : &box);
barrier(cb, input, D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);
}
}
@@ -240,7 +266,9 @@ struct Replay {
std::array<std::vector<std::unique_ptr<Image>>, 11> alternatives;
std::unique_ptr<d4r::RModel> model;
std::unique_ptr<d4r::REngine> engine;
std::unique_ptr<Image> convertedOutput;
bool first = true;
bool modelUploaded = false;
Replay(const Layout& l, VkPhysicalDevice physical, VkDevice device, const std::string& directory)
: layout(l), physical(physical), device(device) {
const auto data = read(std::filesystem::path(directory) / "rr.txt");
@@ -251,6 +279,45 @@ struct Replay {
model = std::make_unique<d4r::RModel>(physical, device, directory);
engine = std::make_unique<d4r::REngine>(*model, l.rw, l.rh, l.ow, l.oh);
}
// Caller retires all submissions before changing layout extents. Pipelines
// and uploaded weights survive; descriptors/views and temporal state do not.
void resize() {
engine.reset();
for (auto& image : images) image.reset();
for (auto& cached : alternatives) cached.clear();
for (size_t i = 0; i < images.size(); ++i)
if (layout.planes[i].enabled) configure(i, layout.planes[i].format);
engine = std::make_unique<d4r::REngine>(*model, layout.rw, layout.rh, layout.ow, layout.oh);
first = true;
}
void configureOutput() {
if (layout.outputFormat == VK_FORMAT_R16G16B16A16_SFLOAT) return;
if (layout.outputFormat != VK_FORMAT_R32G32B32A32_SFLOAT)
throw std::runtime_error("Unsupported RR output conversion");
if (convertedOutput) return;
VkFormatProperties source{}, target{};
vkGetPhysicalDeviceFormatProperties(physical, VK_FORMAT_R16G16B16A16_SFLOAT, &source);
vkGetPhysicalDeviceFormatProperties(physical, layout.outputFormat, &target);
if (!(source.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT) ||
(target.optimalTilingFeatures & (VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT)) !=
(VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT))
throw std::runtime_error("RR float32 output blit unsupported");
auto image = std::make_unique<Image>(); image->device = device; image->format = layout.outputFormat;
VkImageCreateInfo ci{VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO}; ci.imageType = VK_IMAGE_TYPE_2D;
ci.format = image->format; ci.extent = {layout.ow, layout.oh, 1}; ci.mipLevels = ci.arrayLayers = 1;
ci.samples = VK_SAMPLE_COUNT_1_BIT; ci.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
check(vkCreateImage(device, &ci, nullptr, &image->image), "RR converted output image");
VkMemoryRequirements req; vkGetImageMemoryRequirements(device, image->image, &req);
VkPhysicalDeviceMemoryProperties memory; vkGetPhysicalDeviceMemoryProperties(physical, &memory);
uint32_t type = memory.memoryTypeCount;
for (uint32_t n = 0; n < memory.memoryTypeCount; ++n)
if ((req.memoryTypeBits & (1u << n)) && (memory.memoryTypes[n].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) { type = n; break; }
if (type == memory.memoryTypeCount) throw std::runtime_error("No converted RR output memory type");
VkMemoryAllocateInfo ai{VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO}; ai.allocationSize = req.size; ai.memoryTypeIndex = type;
check(vkAllocateMemory(device, &ai, nullptr, &image->memory), "RR converted output memory");
check(vkBindImageMemory(device, image->image, image->memory, 0), "RR converted output bind");
convertedOutput = std::move(image);
}
// Cache views by format: REngine caches descriptors referencing old views,
// so they must remain alive until the engine is destroyed.
void configure(size_t i, VkFormat format) {
@@ -312,15 +379,34 @@ struct Replay {
VkImageView* optional[] = {&inputs.specularHitDistance, &inputs.disocclusionMask, &inputs.subsurfaceGuide, &inputs.colorBeforeParticles};
for (size_t i = 7; i < images.size(); ++i) if (layout.planes[i].enabled && (optionalMask & (1u << (i - 7)))) *optional[i - 7] = images[i]->view;
engine->setFrame(inputs, settings);
if (first) { model->recordUpload(cb); engine->recordInitialize(cb); first = false; }
if (first) {
if (!modelUploaded) { model->recordUpload(cb); modelUploaded = true; }
engine->recordInitialize(cb); first = false;
}
engine->recordFrame(cb);
VkImageMemoryBarrier b{VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER}; b.image = engine->outputImage();
b.oldLayout = b.newLayout = VK_IMAGE_LAYOUT_GENERAL; b.srcQueueFamilyIndex = b.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
b.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; b.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; b.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
vkCmdPipelineBarrier(cb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &b);
VkBufferImageCopy copy{}; copy.bufferOffset = layout.outputOffset; copy.bufferRowLength = uint32_t(layout.outputPitch / 8);
VkImage output = engine->outputImage();
if (layout.outputFormat != VK_FORMAT_R16G16B16A16_SFLOAT) {
if (!convertedOutput) throw std::runtime_error("RR converted output was not configured");
VkImageMemoryBarrier target = b; target.image = convertedOutput->image;
target.oldLayout = convertedOutput->initialized ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED;
target.newLayout = VK_IMAGE_LAYOUT_GENERAL;
target.srcAccessMask = convertedOutput->initialized ? VK_ACCESS_TRANSFER_READ_BIT : 0;
target.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
vkCmdPipelineBarrier(cb, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &target);
VkImageBlit region{}; region.srcSubresource = region.dstSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1};
region.srcOffsets[1] = region.dstOffsets[1] = {int32_t(layout.ow), int32_t(layout.oh), 1};
vkCmdBlitImage(cb, output, VK_IMAGE_LAYOUT_GENERAL, convertedOutput->image, VK_IMAGE_LAYOUT_GENERAL, 1, &region, VK_FILTER_NEAREST);
target.oldLayout = target.newLayout; target.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; target.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
vkCmdPipelineBarrier(cb, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &target);
convertedOutput->initialized = true; output = convertedOutput->image;
}
VkBufferImageCopy copy{}; copy.bufferOffset = layout.outputOffset; copy.bufferRowLength = uint32_t(layout.outputPitch / layout.outputBytes);
copy.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}; copy.imageExtent = {layout.ow, layout.oh, 1};
vkCmdCopyImageToBuffer(cb, engine->outputImage(), VK_IMAGE_LAYOUT_GENERAL, shared, 1, &copy);
vkCmdCopyImageToBuffer(cb, output, VK_IMAGE_LAYOUT_GENERAL, shared, 1, &copy);
}
};
}
+24 -7
View File
@@ -81,6 +81,7 @@ struct Backend::Impl {
offset = plane.offset + plane.pitch * layout.rh;
}
layout.outputOffset = (offset + 511) & ~VkDeviceSize(511);
layout.outputPitch = (VkDeviceSize(layout.ow) * 16 + 255) & ~VkDeviceSize(255);
layout.bytes = layout.outputOffset + layout.outputPitch * layout.oh;
}
void dispatch() {
@@ -151,8 +152,8 @@ struct Backend::Impl {
}
void validate_frame(const Snapshot& frame) {
if (failed) throw std::runtime_error("Native RR backend is poisoned after failed submission");
if (frame.renderWidth != layout.rw || frame.renderHeight != layout.rh)
throw std::runtime_error("Dynamic RR render extent requires engine reconfiguration");
if (frame.renderWidth < 64 || frame.renderHeight < 64 || frame.renderWidth > creation.width || frame.renderHeight > creation.height)
throw std::runtime_error("RR render extent must be at least 64 and within creation geometry");
if (frame.resources[Snapshot::Exposure]) throw std::runtime_error("RR exposure texture is not implemented");
if (frame.exposureScale != 1 || frame.depthType || frame.normalsType || frame.roughnessType || frame.diffuseType || frame.specularType)
throw std::runtime_error("RR guide encoding or exposure scale requires conversion");
@@ -170,12 +171,13 @@ struct Backend::Impl {
// D3D12 requires whole-subresource copies for depth-stencil.
// The copied subresource is depth plane 0, never stencil.
if ((desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) &&
(frame.origins[i][0] || frame.origins[i][1] || layout.rw != desc.Width || layout.rh != desc.Height))
(frame.origins[i][0] || frame.origins[i][1] || frame.renderWidth != desc.Width || frame.renderHeight != desc.Height))
throw std::runtime_error("RR depth-stencil requires a full depth-plane rectangle");
}
const uint32_t width = i == Snapshot::Output ? layout.ow : layout.rw;
const uint32_t height = i == Snapshot::Output ? layout.oh : layout.rh;
if (desc.Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE2D || (i == Snapshot::Output && desc.Format != DXGI_FORMAT_R16G16B16A16_FLOAT) || desc.SampleDesc.Count != 1 || desc.DepthOrArraySize != 1 ||
const uint32_t width = i == Snapshot::Output ? layout.ow : frame.renderWidth;
const uint32_t height = i == Snapshot::Output ? layout.oh : frame.renderHeight;
if (desc.Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE2D || (i == Snapshot::Output &&
desc.Format != DXGI_FORMAT_R16G16B16A16_FLOAT && desc.Format != DXGI_FORMAT_R32G32B32A32_FLOAT) || desc.SampleDesc.Count != 1 || desc.DepthOrArraySize != 1 ||
frame.origins[i][0] > desc.Width || width > desc.Width - frame.origins[i][0] ||
frame.origins[i][1] > desc.Height || height > desc.Height - frame.origins[i][1])
throw std::runtime_error("Unsupported native RR texture format or subrect");
@@ -187,6 +189,17 @@ struct Backend::Impl {
}
void evaluate(ID3D12CommandQueue* q, const Snapshot& frame, const std::array<ResourceAccess, Snapshot::Count>& access) {
validate(q, frame, access); dispatch();
// Any mutation below may invalidate descriptors or engine bookkeeping;
// a failed reconfiguration must never reuse a partially rebuilt engine.
failed = true;
if (frame.renderWidth != layout.rw || frame.renderHeight != layout.rh) {
if (pending) throw std::runtime_error("RR resize requires retired GPU work");
layout.rw = frame.renderWidth; layout.rh = frame.renderHeight;
// Keep creation-sized packet pitches/offsets and imported fence.
// Rebuild only size-dependent views/history, retaining model data.
replay->resize();
std::fprintf(stderr, "D4R_RR_RESIZE render=%ux%u history_reset=1 model_reused=1 packet_reused=1\n", layout.rw, layout.rh);
}
for (size_t i = 0; i < layout.planes.size(); ++i) {
const auto resource = resource_index(i);
layout.planes[i].enabled = frame.resources[resource] != nullptr;
@@ -195,6 +208,10 @@ struct Backend::Impl {
replay->configure(i, format.format);
layout.planes[i].format = format.format; layout.planes[i].bytes = format.bytes;
}
const auto outputFormat = resource_desc(frame.resources[Snapshot::Output].Get()).Format;
layout.outputBytes = outputFormat == DXGI_FORMAT_R32G32B32A32_FLOAT ? 16 : 8;
layout.outputFormat = layout.outputBytes == 16 ? VK_FORMAT_R32G32B32A32_SFLOAT : VK_FORMAT_R16G16B16A16_SFLOAT;
replay->configureOutput();
// Recording mutates engine upload/history bookkeeping. Any later error
// requires recreation, even if no GPU submission has started yet.
failed = true;
@@ -222,7 +239,7 @@ struct Backend::Impl {
auto* output = frame.resources[Snapshot::Output].Get();
texture_copy_barrier(consumer.Get(), output, access[Snapshot::Output], true, false);
auto source = rr_bridge::D3D12Frames::placed(shared.Get(), layout.outputOffset, layout.outputPitch,
DXGI_FORMAT_R16G16B16A16_FLOAT, layout.ow, layout.oh);
outputFormat, layout.ow, layout.oh);
auto dest = rr_bridge::D3D12Frames::texture(output);
consumer->CopyTextureRegion(&dest, frame.origins[Snapshot::Output][0], frame.origins[Snapshot::Output][1], 0, &source, nullptr);
texture_copy_barrier(consumer.Get(), output, access[Snapshot::Output], true, true);
+98 -3
View File
@@ -21,6 +21,13 @@ int main(int argc, char** argv) try {
const bool floatGuides = mode.find("float-guides") != std::string::npos;
const bool switchGuides = mode.find("switch-guides") != std::string::npos;
const bool autoExposure = mode.find("auto-exposure") != std::string::npos;
const bool dynamicExtents = mode.find("dynamic-extents") != std::string::npos;
const bool twoFeatures = mode.find("two-features") != std::string::npos;
const bool maxResources = mode.find("max-resources") != std::string::npos;
const bool outputFloat32 = mode.find("output-float32") != std::string::npos;
const bool switchOutput = mode.find("switch-output") != std::string::npos;
if (switchOutput && (!outputFloat32 || maxResources)) throw std::runtime_error("Output switching requires float32 mode and resized resources");
if (maxResources && !dynamicExtents) throw std::runtime_error("Creation-sized resources require dynamic fixture");
DXGI_FORMAT depthFormat = DXGI_FORMAT_UNKNOWN;
if (mode.find("depth-d32s8-typeless") != std::string::npos) depthFormat = DXGI_FORMAT_R32G8X24_TYPELESS;
else if (mode.find("depth-d32s8") != std::string::npos) depthFormat = DXGI_FORMAT_D32_FLOAT_S8X24_UINT;
@@ -29,7 +36,25 @@ int main(int argc, char** argv) try {
const bool stencil = depthFormat == DXGI_FORMAT_R32G8X24_TYPELESS || depthFormat == DXGI_FORMAT_D32_FLOAT_S8X24_UINT;
if (enhanced && split) throw std::runtime_error("The split fixture currently tests legacy split barriers only");
rr_bridge::Layout layout(argv[2], argv[4], argv[3]);
if (outputFloat32) {
layout.outputFormat = VK_FORMAT_R32G32B32A32_SFLOAT; layout.outputBytes = 16;
layout.initializePacket();
}
const auto canonical = layout;
std::vector<std::array<uint32_t, 2>> extents;
if (dynamicExtents) {
if (switchGuides || floatGuides || depthFormat != DXGI_FORMAT_UNKNOWN)
throw std::runtime_error("Dynamic fixture currently uses canonical guides and R32 depth");
std::ifstream sizes(layout.inputs / "dynamic-extents.txt");
uint32_t width, height;
while (sizes >> width >> height) {
if (width < 64 || height < 64 || width > layout.rw || height > layout.rh)
throw std::runtime_error("Invalid bounded dynamic fixture extent");
extents.push_back({width, height});
}
if (!sizes.eof() || extents.size() != layout.frames.size())
throw std::runtime_error("Dynamic fixture extent count differs from frames");
}
if (floatGuides) {
for (size_t i : {size_t(0), size_t(3)}) {
layout.planes[i].format = VK_FORMAT_R32G32B32A32_SFLOAT; layout.planes[i].bytes = 16;
@@ -170,6 +195,8 @@ int main(int argc, char** argv) try {
d4r_ngx_set_uint(p, "DLSS.Feature.Create.Flags", creationFlags);
}
void* feature = nullptr; check(create(nullptr, 13, p, &feature), "RR CreateFeature");
void* peerFeature = nullptr;
if (twoFeatures) check(create(nullptr, 13, p, &peerFeature), "RR peer CreateFeature");
auto textures = std::make_unique<rr_bridge::D3D12Frames>(layout, device.Get(), depthFormat);
ComPtr<ID3D12Resource> stencilUpload, stencilReadback;
D3D12_PLACED_SUBRESOURCE_FOOTPRINT stencilFootprint{}; UINT64 stencilBytes = 0;
@@ -196,14 +223,30 @@ int main(int argc, char** argv) try {
auto upload = make_buffer(device.Get(), layout.bytes, D3D12_HEAP_TYPE_UPLOAD, D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_STATE_GENERIC_READ);
auto scratch = make_buffer(device.Get(), layout.bytes, D3D12_HEAP_TYPE_DEFAULT, D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);
auto readback = make_buffer(device.Get(), layout.bytes, D3D12_HEAP_TYPE_READBACK, D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);
ComPtr<ID3D12Resource> peerReadback;
if (twoFeatures) {
peerReadback = make_buffer(device.Get(), layout.bytes, D3D12_HEAP_TYPE_READBACK, D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);
std::filesystem::create_directories(layout.output / "peer");
}
ComPtr<ID3D12Fence> fence; dx(device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence)), "NGX RR fixture fence");
Handle event; event.value = CreateEventW(nullptr, FALSE, FALSE, nullptr);
if (!event.value) throw std::runtime_error("NGX RR fixture event");
for (unsigned index = 0; index < layout.frames.size(); ++index) {
if (dynamicExtents) {
layout.rw = extents[index][0]; layout.rh = extents[index][1];
layout.initializePacket();
if (!maxResources) textures = std::make_unique<rr_bridge::D3D12Frames>(layout, device.Get(), depthFormat);
}
if (switchGuides) {
layout = index % 2 ? floatLayout : canonical;
textures = std::make_unique<rr_bridge::D3D12Frames>(layout, device.Get(), depthFormat);
}
if (switchOutput) {
layout.outputBytes = index % 2 ? 16 : 8;
layout.outputFormat = index % 2 ? VK_FORMAT_R32G32B32A32_SFLOAT : VK_FORMAT_R16G16B16A16_SFLOAT;
layout.initializePacket();
textures = std::make_unique<rr_bridge::D3D12Frames>(layout, device.Get(), depthFormat);
}
void* mapped = nullptr; D3D12_RANGE noRead{};
dx(upload->Map(0, &noRead, &mapped), "NGX RR upload map"); uploadFrame(mapped, index); upload->Unmap(0, nullptr);
ComPtr<ID3D12CommandAllocator> allocator; ComPtr<ID3D12GraphicsCommandList> list;
@@ -246,6 +289,8 @@ int main(int argc, char** argv) try {
for (size_t i = 0; i < 4; ++i)
d4r_ngx_set_d3d12_resource(p, optionalNames[i], layout.frames[index].optional[i] ? textures->inputs[i + 7].Get() : nullptr);
auto settings = layout.frames[index].settings;
d4r_ngx_set_uint(p, "DLSS.Render.Subrect.Dimensions.Width", layout.rw);
d4r_ngx_set_uint(p, "DLSS.Render.Subrect.Dimensions.Height", layout.rh);
d4r_ngx_set_float(p, "Jitter.Offset.X", settings.jitter[0]); d4r_ngx_set_float(p, "Jitter.Offset.Y", settings.jitter[1]);
d4r_ngx_set_float(p, "MV.Scale.X", settings.motionScale[0]); d4r_ngx_set_float(p, "MV.Scale.Y", settings.motionScale[1]);
d4r_ngx_set_float(p, "DLSS.Pre.Exposure", settings.preExposure);
@@ -257,6 +302,18 @@ int main(int argc, char** argv) try {
for (const auto* state : states) d4r_ngx_set_uint(p, state, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);
d4r_ngx_set_uint(p, "D4R.Output.State", D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
}
if (mode.find("sr-bounds") != std::string::npos && index == 0) {
// This synthetic Quality fixture uses the pinned SR library's half-output minimum.
// Reject each axis below that minimum, then submit a valid frame on the same list.
require(layout.ow / 2 > 64 && layout.oh / 2 > 64, "SR bounds fixture is too small");
d4r_ngx_set_uint(p, "DLSS.Render.Subrect.Dimensions.Width", layout.ow / 2 - 1);
require(evaluate(list.Get(), feature, p, nullptr) != 1, "SR width below dynamic minimum accepted");
d4r_ngx_set_uint(p, "DLSS.Render.Subrect.Dimensions.Width", layout.rw);
d4r_ngx_set_uint(p, "DLSS.Render.Subrect.Dimensions.Height", layout.oh / 2 - 1);
require(evaluate(list.Get(), feature, p, nullptr) != 1, "SR height below dynamic minimum accepted");
d4r_ngx_set_uint(p, "DLSS.Render.Subrect.Dimensions.Height", layout.rh);
std::puts("RR_NGX_SR_BOUNDS width_below_min_rejected=1 height_below_min_rejected=1 command_list_preserved=1");
}
if (contracts && index == 0) {
auto rejects = [&](const char* name) { require(evaluate(list.Get(), feature, p, nullptr) != 1, name); };
if (depthFormat == DXGI_FORMAT_D32_FLOAT || stencil) {
@@ -268,15 +325,20 @@ int main(int argc, char** argv) try {
d4r_ngx_set_void(p, "ViewToClipMatrix", settings.viewToClip);
d4r_ngx_set_d3d12_resource(p, "DLSSD.SpecularHitDistance", textures->inputs[0].Get()); rejects("Invalid RR scalar guide format accepted");
d4r_ngx_set_d3d12_resource(p, "DLSSD.SpecularHitDistance", layout.frames[index].optional[0] ? textures->inputs[7].Get() : nullptr);
d4r_ngx_set_uint(p, "DLSS.Render.Subrect.Dimensions.Width", layout.rw + 1); rejects("Unsupported RR dynamic extent accepted");
d4r_ngx_set_uint(p, "DLSS.Render.Subrect.Dimensions.Width", canonical.rw + 1); rejects("RR extent exceeding creation size accepted");
d4r_ngx_set_uint(p, "DLSS.Render.Subrect.Dimensions.Width", layout.rw);
d4r_ngx_set_uint(p, "DLSS.Render.Subrect.Dimensions.Height", canonical.rh + 1); rejects("RR height exceeding creation size accepted");
d4r_ngx_set_uint(p, "DLSS.Render.Subrect.Dimensions.Height", layout.rh);
d4r_ngx_set_uint(p, "DLSS.Render.Subrect.Dimensions.Width", 63); rejects("RR width below engine minimum accepted");
d4r_ngx_set_uint(p, "DLSS.Render.Subrect.Dimensions.Width", layout.rw);
std::puts("RR_NGX_EXTENT_BOUNDS creation_max_rejected=1 engine_min_rejected=1 command_list_preserved=1");
d4r_ngx_set_uint(p, "DLSS.Depth.Type", 1); rejects("Unsupported RR encoding accepted");
d4r_ngx_set_uint(p, "DLSS.Depth.Type", 0);
d4r_ngx_set_uint(p, "DLSS.Input.Color.Subrect.Base.X", layout.rw); rejects("Invalid RR subrect accepted");
d4r_ngx_set_uint(p, "DLSS.Input.Color.Subrect.Base.X", 0);
d4r_ngx_set_d3d12_resource(p, "Output", textures->inputs[0].Get()); rejects("Aliased or invalid RR output accepted");
d4r_ngx_set_d3d12_resource(p, "Output", textures->output.Get());
std::puts("RR_NGX_NEGATIVE rejected=13 command_list_preserved=1");
std::puts("RR_NGX_NEGATIVE rejected=15 command_list_preserved=1");
}
if (split && index == 0) {
D3D12_RESOURCE_BARRIER barrier{}; barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
@@ -288,16 +350,35 @@ int main(int argc, char** argv) try {
std::puts("RR_NGX_SPLIT unfinished_rejected=1 paired_before_submit=1");
}
check(evaluate(list.Get(), feature, p, nullptr), "RR EvaluateFeature");
if (twoFeatures) {
// Preserve first feature output before a second live Vulkan backend
// consumes the same borrowed inputs and publishes to the same UAV.
// Both evaluations are delayed boundaries in one game command list.
auto* output = textures->output.Get();
if (enhanced) texture_copy_barrier(list.Get(), output, enhancedAccess(true), false, false);
else transition(list.Get(), output, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
auto source = rr_bridge::D3D12Frames::texture(output);
auto destination = rr_bridge::D3D12Frames::placed(peerReadback.Get(), layout.outputOffset, layout.outputPitch,
rr_bridge::D3D12Frames::format(layout.outputFormat), layout.ow, layout.oh);
list->CopyTextureRegion(&destination, 0, 0, 0, &source, nullptr);
if (enhanced) texture_copy_barrier(list.Get(), output, enhancedAccess(true), false, true);
else transition(list.Get(), output, D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
check(evaluate(list.Get(), peerFeature, p, nullptr), "RR peer EvaluateFeature");
}
// Both pointer payloads and parameter scalars change before submission.
settings.worldToView[0] = 999; settings.viewToClip[5] = 999;
d4r_ngx_set_float(p, "DLSS.Pre.Exposure", 999);
d4r_ngx_set_d3d12_resource(p, "Color", nullptr);
if (dynamicExtents) {
d4r_ngx_set_uint(p, "DLSS.Render.Subrect.Dimensions.Width", 1);
d4r_ngx_set_uint(p, "DLSS.Render.Subrect.Dimensions.Height", 1);
}
auto output = textures->output;
if (enhanced) texture_copy_barrier(list.Get(), output.Get(), enhancedAccess(true), false, false);
else transition(list.Get(), output.Get(), D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
auto source = rr_bridge::D3D12Frames::texture(output.Get());
auto dest = rr_bridge::D3D12Frames::placed(readback.Get(), layout.outputOffset, layout.outputPitch,
DXGI_FORMAT_R16G16B16A16_FLOAT, layout.ow, layout.oh);
rr_bridge::D3D12Frames::format(layout.outputFormat), layout.ow, layout.oh);
list->CopyTextureRegion(&dest, 0, 0, 0, &source, nullptr);
if (enhanced) {
texture_copy_barrier(list.Get(), output.Get(), enhancedAccess(true), false, true);
@@ -309,6 +390,7 @@ int main(int argc, char** argv) try {
if (stencil) stencilCopy(list.Get(), textures->inputs[2].Get(), false);
if (index + 1 == layout.frames.size()) {
check(release(feature), "RR Release before submission"); feature = nullptr;
if (peerFeature) { check(release(peerFeature), "RR peer Release before submission"); peerFeature = nullptr; }
check(destroy(p), "RR Destroy parameters before submission"); p = nullptr;
textures.reset(); output.Reset();
}
@@ -321,6 +403,13 @@ int main(int argc, char** argv) try {
D3D12_RANGE range{0, SIZE_T(layout.bytes)}; dx(readback->Map(0, &range, &mapped), "NGX RR readback map");
try { layout.validate(mapped, index); } catch (...) { readback->Unmap(0, nullptr); throw; }
readback->Unmap(0, nullptr);
if (twoFeatures) {
dx(peerReadback->Map(0, &range, &mapped), "NGX RR first feature readback map");
auto peerLayout = layout; peerLayout.output /= "peer";
try { peerLayout.validate(mapped, index); } catch (...) { peerReadback->Unmap(0, nullptr); throw; }
peerReadback->Unmap(0, nullptr);
std::printf("RR_NGX_PEER_FRAME index=%u exact=1 both_features_live=1\n", index);
}
if (stencil) {
D3D12_RANGE stencilRange{0, SIZE_T(stencilBytes)};
dx(stencilReadback->Map(0, &stencilRange, &mapped), "RR stencil verification map");
@@ -331,6 +420,7 @@ int main(int argc, char** argv) try {
}
std::printf("RR_NGX_FRAME index=%u snapshot_mutated=1 exact=1 optional_mask=%u hardware_depth=%u depth_inverted=%u\n",
index, optionalMask, settings.hardwareDepth, settings.depthInverted);
if (dynamicExtents) std::printf("RR_NGX_EXTENT index=%u render=%ux%u exact=1\n", index, layout.rw, layout.rh);
}
check(shutdown(), "RR Shutdown");
if (contracts) std::puts("PASS RR_NGX_CONTRACTS capabilities=1 requirements=1 warp_rejected=1 invalid_inputs_rejected=1");
@@ -341,6 +431,11 @@ int main(int argc, char** argv) try {
if (floatGuides) std::puts("PASS RR_NGX_FLOAT_GUIDES color_rgba32=1 motion_rg32=1 normals_rgba32=1 roughness_r16=1");
if (switchGuides) std::puts("PASS RR_NGX_SWITCH_GUIDES alternate_each_frame=1 feature_recreated=0");
if (autoExposure) std::puts("PASS RR_NGX_FLAGS hdr=1 render_motion=1 auto_exposure=1");
if (dynamicExtents) std::puts("PASS RR_NGX_DYNAMIC_EXTENTS feature_recreated=0 snapshot_geometry_owned=1");
if (twoFeatures) std::puts("PASS RR_NGX_TWO_FEATURES delayed_boundaries=2 separate_vulkan_devices=1 release_both_before_submit=1");
if (maxResources) std::puts("PASS RR_NGX_MAX_RESOURCES creation_size_retained=1 dynamic_input_subrects=1");
if (outputFloat32) std::puts("PASS RR_NGX_OUTPUT_FLOAT32 gpu_conversion=1 half_reference_widening_exact=1");
if (switchOutput) std::puts("PASS RR_NGX_SWITCH_OUTPUT alternating_half_float=1 feature_recreated=0");
if (depthFormat != DXGI_FORMAT_UNKNOWN) std::printf("PASS RR_NGX_DEPTH format=%u stencil_preserved=%u\n", unsigned(depthFormat), stencil);
std::puts("PASS RR_NGX_LIFECYCLE feature=13 snapshot_mutation=1 release_before_submit=1 parameter_destroy_before_submit=1");
return 0;
+215
View File
@@ -0,0 +1,215 @@
"""Run the synthetic NGX feature-13 harness with the FSR Ray Regeneration backend selected only through d4r.ini.
Stages a private player folder from a packaged runtime, adds the experimental FSR payload (vkd3d-proton core, the
amdxc64 shim) and AMD's denoiser DLL, runs d4r_rr_ngx_probe with a synthetic noisy scene (make_fsr_rr_fixture.py),
and compares the output with the scene's noise-free answer. No game, no NVIDIA denoiser; Super Sampling runs when the
packaged runtime has its dependencies. Output stays under ignored build/.
"""
import argparse
import hashlib
import json
import os
import re
import shutil
import statistics
import subprocess
import sys
from pathlib import Path
repo = Path(__file__).resolve().parents[2]
def sha(path):
return hashlib.sha256(Path(path).read_bytes()).hexdigest()
def main():
parser = argparse.ArgumentParser(description=__doc__)
for name in ('probe', 'shim', 'runtime', 'core', 'dlss', 'fsr-dir', 'denoiser', 'fixture', 'output'):
parser.add_argument('--' + name, required=True, type=lambda value: Path(value).resolve())
parser.add_argument('--timeout', type=int, default=1500)
parser.add_argument('--without-sr', action='store_true', help='omit SR/core dependencies and require the same-size FSR-only fallback')
parser.add_argument('--cache-dir', type=lambda value: Path(value).resolve(), help='isolated cache for shader/compiler experiments')
parser.add_argument('--vkd3d-config', help='diagnostic per-process vkd3d configuration')
parser.add_argument('--fp16-rte', action='store_true', help='experimental dxil-spirv payload: retain layout workaround but omit global FP16 RTZ')
parser.add_argument('--float-guides', action='store_true')
parser.add_argument('--switch-guides', action='store_true')
parser.add_argument('--output-float32', action='store_true')
parser.add_argument('--switch-output', action='store_true')
parser.add_argument('--depth-format', choices=('r32', 'd32', 'r32-typeless', 'd32s8', 'd32s8-typeless'), default='r32')
parser.add_argument('--reference-frames', type=lambda value: Path(value).resolve(), help='require exact components against canonical half-output frames, widening the reference for float32')
parser.add_argument('--verify-output-widening', action='store_true', help='FSR-only: dump each composed half image and require exact output widening in the same frame')
parser.add_argument('--max-resources', action='store_true', help='with --dynamic-extents: keep creation-sized textures and change only the render rectangle, as games do')
parser.add_argument('--dynamic-extents', action='store_true', help='run the probe in its dynamic-extents mode (the fixture has dynamic-extents.txt)')
parser.add_argument('--sr-bounds', action='store_true', help='Quality fixture: reject render width/height below the pinned SR dynamic minimum, then evaluate normally')
parser.add_argument('--ini-set', action='append', default=[], metavar='KEY=VALUE',
help='override a [RayReconstruction] key in the staged d4r.ini (repeatable), e.g. FsrStabilityBias=auto')
args = parser.parse_args()
if args.switch_output and not args.output_float32:
parser.error('--switch-output requires --output-float32')
if args.switch_guides and not args.float_guides:
parser.error('--switch-guides requires --float-guides')
if args.verify_output_widening and not args.without_sr:
parser.error('--verify-output-widening requires --without-sr')
build = (repo / 'build').resolve()
if not args.output.is_relative_to(build):
parser.error('output must stay under ignored build/')
args.output.mkdir(parents=True, exist_ok=False)
staged = args.output / 'player'
shutil.copytree(args.runtime, staged)
shutil.copy2(args.shim, staged / 'd4r_nvngx.dll')
for source, relative in ((args.core, 'ngx/_nvngx.dll'), (args.dlss, 'dlss/nvngx_dlss.dll')):
target = staged / relative
if args.without_sr:
if target.exists():
raise RuntimeError('--without-sr needs a runtime without installed SR/core DLLs')
continue
target.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(source, target)
fsr = staged / 'fsr'
fsr.mkdir(exist_ok=True)
for name in ('d4r_vkd3d_core.dll', 'amdxc64.dll'):
shutil.copy2(args.fsr_dir / name, fsr / name)
shutil.copy2(args.denoiser, staged / 'amd_fidelityfx_denoiser_dx12.dll')
config = (repo / 'packaging/windows/d4r.ini').read_text()
if 'Backend = off' not in config:
raise RuntimeError('packaging d4r.ini no longer has [RayReconstruction] Backend = off')
config = config.replace('Backend = off', 'Backend = fsr')
if args.cache_dir:
config = config.replace('CacheDir = auto', 'CacheDir = ' + str(args.cache_dir))
for item in args.ini_set:
key, value = item.split('=', 1)
pattern = re.compile(r'^' + re.escape(key) + r'\s*=.*$', re.M)
if not pattern.search(config):
raise RuntimeError('d4r.ini has no ' + key + ' line to override')
config = pattern.sub(lambda match: key + ' = ' + value, config, count=1)
(staged / 'd4r.ini').write_text(config)
env = {key: value for key, value in os.environ.items()
if not key.startswith(('D4R_', 'VKD3D_', 'DXIL_')) and key not in ('HIP_PATH', 'ZLUDA_CUDA_LIB', 'ZLUDA_LOG_DIR', 'ZLUDA_CACHE_DIR')}
env['D4R_FSR_RR_LOG'] = '1' # per-frame stage timings in stderr; selection itself comes only from d4r.ini
if args.vkd3d_config:
env['VKD3D_CONFIG'] = args.vkd3d_config
if args.fp16_rte:
env['D4R_DXIL_F16_RTE'] = '1'
if args.verify_output_widening:
dump = args.output / 'converted'
dump.mkdir()
env['D4R_FSR_RR_DUMP_DIR'] = str(dump)
env['D4R_FSR_RR_DUMP_FRAMES'] = str(len(list(args.fixture.glob('*-color.bin'))))
if args.without_sr:
# Player startup requires SR dependencies. Use the existing diagnostic environment
# contract to exercise the supported FSR-only fallback without changing player policy.
cache = (args.cache_dir or (Path(os.environ['LOCALAPPDATA']) / 'd4r/cache')) / 'vkd3d'
cache.mkdir(parents=True, exist_ok=True)
env.update(D4R_HIP_ROOT=str(staged / 'hip'), D4R_RR_BACKEND='fsr',
D4R_FSR_VKD3D_CORE=str(fsr / 'd4r_vkd3d_core.dll'),
D4R_FSR_AMDXC_SHIM=str(fsr / 'amdxc64.dll'),
D4R_FSR_RR_DLL=str(staged / 'amd_fidelityfx_denoiser_dx12.dll'),
D4R_FSR_CACHE_DIR=str(cache))
command = [str(args.probe), str(staged / 'd4r_nvngx.dll'), str(args.fixture), '', str(args.output / 'frames'), '']
modes = []
if args.dynamic_extents:
modes.append('dynamic-extents' + ('-max-resources' if args.max_resources else ''))
if args.sr_bounds:
modes.append('sr-bounds')
if args.float_guides:
modes.append('float-guides')
if args.switch_guides:
modes.append('switch-guides')
if args.output_float32:
modes.append('output-float32')
if args.switch_output:
modes.append('switch-output')
if args.depth_format != 'r32':
modes.append('depth-' + args.depth_format)
if modes:
command.append('-'.join(modes))
report = {'passed': False, 'gameAccessed': False, 'qualityReference': False, 'synthetic': True,
'selectionMode': 'diagnostic environment without SR' if args.without_sr else 'player INI',
'artifacts': {str(path): sha(path) for path in (args.probe, args.shim, args.core, args.dlss, args.denoiser,
args.fsr_dir / 'd4r_vkd3d_core.dll', args.fsr_dir / 'amdxc64.dll')}}
try:
with (args.output / 'stdout.log').open('wb') as out, (args.output / 'stderr.log').open('wb') as err:
result = subprocess.run(command, env=env, stdout=out, stderr=err, timeout=args.timeout)
report['exitCode'] = result.returncode
text = (args.output / 'stdout.log').read_text(errors='replace')
if result.returncode or 'PASS RR_NGX_LIFECYCLE' not in text:
raise RuntimeError('NGX RR harness failed; inspect ' + str(args.output))
if args.dynamic_extents:
expected = [tuple(map(int, line.split())) for line in (args.fixture / 'dynamic-extents.txt').read_text().splitlines() if line.strip()]
actual = [(int(w), int(h)) for w, h in re.findall(r'RR_NGX_EXTENT index=\d+ render=(\d+)x(\d+) exact=1', text)]
if actual != expected or 'PASS RR_NGX_DYNAMIC_EXTENTS' not in text:
raise RuntimeError('Dynamic render dimensions were not applied')
if args.max_resources and 'PASS RR_NGX_MAX_RESOURCES' not in text:
raise RuntimeError('Creation-sized resources were not retained')
report['renderExtents'] = actual
if args.sr_bounds:
if 'RR_NGX_SR_BOUNDS width_below_min_rejected=1 height_below_min_rejected=1 command_list_preserved=1' not in text:
raise RuntimeError('SR minimum bound rejections were not verified')
report['srMinimumRejectedBeforeSubmission'] = True
quality_command = [sys.executable, str(repo / 'tools/windows/check_fsr_rr_output.py'), '--fixture', str(args.fixture),
'--output', str(args.output / 'frames'), '--report', str(args.output / 'quality.json')]
if args.output_float32:
quality_command.append('--output-float32')
if args.switch_output:
quality_command.append('--switch-output')
quality = subprocess.run(quality_command,
capture_output=True, text=True)
report['quality'] = json.loads((args.output / 'quality.json').read_text())['summary']
if quality.returncode:
raise RuntimeError('FSR RR output quality check failed; inspect ' + str(args.output / 'quality.json'))
if args.reference_frames:
sys.path.insert(0, str(repo / '.tools/python/vendor'))
import numpy as np
comparisons = []
for index in range(len(list(args.fixture.glob('*-color.bin')))):
float_output = args.output_float32 and (not args.switch_output or index % 2 == 1)
actual = np.fromfile(args.output / 'frames' / f'{index}-out.bin', '<f4' if float_output else '<f2')
reference = np.fromfile(args.reference_frames / f'{index}-out.bin', '<f2').astype(actual.dtype)
comparisons.append(bool(np.array_equal(actual, reference)))
report['exactReferenceFrames'] = comparisons
if not all(comparisons):
raise RuntimeError('FSR format output differs from the canonical half reference')
if args.verify_output_widening:
sys.path.insert(0, str(repo / '.tools/python/vendor'))
import numpy as np
exact = []
for index in range(len(list(args.fixture.glob('*-color.bin')))):
float_output = args.output_float32 and (not args.switch_output or index % 2 == 1)
actual = np.fromfile(args.output / 'frames' / f'{index}-out.bin', '<f4' if float_output else '<f2')
composed = np.fromfile(args.output / 'converted' / f'frame{index}-composed.bin', '<f2').astype(actual.dtype)
exact.append(bool(np.array_equal(actual, composed)))
report['exactSameFrameOutputConversion'] = exact
if not all(exact):
raise RuntimeError('FSR output differs from the same-frame composed half image')
# player mode redirects the shim's stderr into its own log beside d4r_nvngx.dll
err_text = (args.output / 'stderr.log').read_text(errors='replace')
playerLog = staged / 'd4r_nvngx.log'
if playerLog.exists():
err_text += playerLog.read_text(errors='replace')
shutil.copy2(playerLog, args.output / 'd4r_nvngx.log')
report['superSamplingUsed'] = 'FSR_RR super sampling submitted' in err_text
if args.without_sr and report['superSamplingUsed']:
raise RuntimeError('SR ran in the FSR-only test')
report['superSamplingFrames'] = err_text.count('FSR_RR super sampling submitted')
expected_frames = len(list(args.fixture.glob('*-color.bin')))
if (args.dynamic_extents or args.sr_bounds) and report['superSamplingFrames'] != expected_frames:
raise RuntimeError('Dynamic SR fixture did not upscale every frame')
report['srRenderLimits'] = re.findall(r'D4R_SR_RENDER_LIMITS min=(\d+x\d+) max=(\d+x\d+)', err_text)
timings = re.findall(r'FSR_RR gpu frame \d+: prepare ([0-9.]+) ms, denoise ([0-9.]+) ms, compose\+copy ([0-9.]+) ms, total ([0-9.]+) ms', err_text)
if len(timings) > 4:
report['gpuTiming'] = {name: statistics.median(float(row[i]) for row in timings[4:])
for i, name in enumerate(('prepareMedianMs', 'denoiseMedianMs', 'composeCopyMedianMs', 'totalMedianMs'))}
report['gpuTiming']['warmupFrames'] = 4
report['gpuTiming']['measuredFrames'] = len(timings) - 4
report.update(passed=True)
print('PASS FSR_RR_NGX super_sampling=%d tail_rmse=%.4f input_rmse=%.4f ideal_nearest_rmse=%.4f' % (
report['superSamplingUsed'], report['quality']['tailOutputRmse'], report['quality']['tailInputRmse'], report['quality'].get('idealNearestRmse', float('nan'))))
finally:
(args.output / 'summary.json').write_text(json.dumps(report, indent=2) + '\n')
if not report['passed']:
sys.exit(1)
if __name__ == '__main__':
main()
+78
View File
@@ -0,0 +1,78 @@
"""Run a bounded synthetic Vulkan K model smoke. No NGX, CUDA or game inference.
Authentic captured weights do not make the generated scene a quality reference.
Reports GPU dispatch validity separately from model provenance and game parity.
"""
import argparse
import hashlib
import json
import os
from pathlib import Path
import struct
import subprocess
import sys
def main():
repo = Path(__file__).resolve().parents[2]
parser = argparse.ArgumentParser(description=__doc__)
for name in ('runner', 'model', 'output'):
parser.add_argument('--' + name, required=True, type=lambda p: Path(p).resolve())
args = parser.parse_args()
if args.output.exists() or not args.output.is_relative_to((repo / 'build').resolve()):
parser.error('use fresh output under ignored build/')
cache = json.loads((args.model / 'windows-k-model-cache.json').read_text())
for name, expected in cache['products'].items():
if Path(name).name != name or hashlib.sha256((args.model / name).read_bytes()).hexdigest() != expected:
raise RuntimeError('Compiled K model product changed')
args.output.mkdir(parents=True)
rw = rh = 128
ow = oh = 256
color, depth = bytearray(), bytearray()
for y in range(rh):
for x in range(rw):
u, v = x / (rw - 1), y / (rh - 1)
color += struct.pack('<4e', .1 + .7 * u, .1 + .6 * v, .2 + .3 * u * v, 1)
depth += struct.pack('<f', .5)
(args.output / 'color.bin').write_bytes(color)
(args.output / 'depth.bin').write_bytes(depth)
(args.output / 'motion.bin').write_bytes(bytes(rw * rh * 4))
lines = [f'output {ow} {oh}']
for index, (name, channels, bits) in enumerate((('color', 4, 16), ('motion', 2, 16), ('depth', 1, 32))):
lines.append(f'image {index} {json.dumps(str(args.output / (name + ".bin")))} {rw} {rh} {channels} {bits}')
for frame in range(4):
lines.append(f'frame_auto 0 1 2 1 1 1 {int(frame == 0)} 0 0 0 0 0 0 1.37')
case = args.output / 'case.txt'
case.write_text('\n'.join(lines) + '\n')
env = {key:value for key,value in os.environ.items() if not key.startswith('D4R_')}
report = dict(passed=False, synthetic=True, authenticCapturedWeights=True, qualityReference=False,
gameAccessed=False, cudaNgxInference=False, model=str(args.model),
runnerSha256=hashlib.sha256(args.runner.read_bytes()).hexdigest())
try:
with (args.output / 'stdout.log').open('wb') as out, (args.output / 'stderr.log').open('wb') as err:
result = subprocess.run([str(args.runner), str(args.model), str(case), str(args.output / 'frames')],
env=env, stdout=out, stderr=err, timeout=180)
if result.returncode:
raise RuntimeError('Native K runner failed; inspect logs')
sys.path.insert(0, str(repo / '.tools/python/vendor'))
import numpy as np
outputs = sorted((args.output / 'frames').glob('frame-*.312.bin'))
if len(outputs) != 4:
raise RuntimeError('Incomplete native K output set')
for path in outputs:
values = np.fromfile(path, '<f2')
if values.size != ow * oh * 4 or not np.isfinite(values).all() or float(np.ptp(values.astype('f4').reshape(-1, 4)[:, :3])) < 1e-5:
raise RuntimeError('Invalid native K output: ' + path.name)
report.update(passed=True, frames=4, finiteNonuniform=True,
outputs={p.name:hashlib.sha256(p.read_bytes()).hexdigest() for p in outputs},
gpuTimeParity=False, gameValidated=False)
print(json.dumps(report, indent=2))
except Exception as error:
report['error'] = str(error)
raise
finally:
(args.output / 'validation.json').write_text(json.dumps(report, indent=2) + '\n')
if __name__ == '__main__':
main()
+129
View File
@@ -0,0 +1,129 @@
"""Validate Windows SR preset M on synthetic D3D12 resources, never a game.
Tests packaged native M layers, a translated-PTX control on the same GPU, and
an identical native repeat. Finite/nonuniform output, actual layer selection,
and exact full-frame comparisons are separate gates. This is not NVIDIA
hardware/reference-quality validation or a standalone M engine test.
"""
import argparse
import hashlib
import json
import os
from pathlib import Path
import re
import shutil
import subprocess
import sys
repo = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(repo / '.tools/python/vendor'))
import numpy as np
LAYERS = ('rrlite_enc1_4x4', 'rrlite_enc2_4x4', 'rrlite_enc3_tube_4x4', 'rrlite_dec2_4x4', 'rrlite_dec1_4x4')
def sha(path):
return hashlib.sha256(Path(path).read_bytes()).hexdigest()
def main():
parser = argparse.ArgumentParser(description=__doc__)
for name in ('probe', 'shim', 'runtime', 'core', 'dlss', 'output'):
parser.add_argument('--' + name, required=True, type=lambda s: Path(s).resolve())
parser.add_argument('--resolution', default='512x288')
parser.add_argument('--frames', type=int, default=8)
parser.add_argument('--timeout', type=int, default=300)
parser.add_argument('--native-objects', type=lambda s: Path(s).resolve(), help='overlay all five candidate M objects in the staged player only')
args = parser.parse_args()
if not args.output.is_relative_to((repo / 'build').resolve()):
parser.error('output must stay under ignored build/')
if not re.fullmatch(r'[1-9][0-9]*x[1-9][0-9]*', args.resolution) or not 4 <= args.frames <= 32:
parser.error('resolution must be WIDTHxHEIGHT; frames must be 4..32')
ow, oh = map(int, args.resolution.split('x'))
args.output.mkdir(parents=True, exist_ok=False)
player = args.output / 'player'
shutil.copytree(args.runtime, player)
module = player / 'd4r_nvngx.dll'
shutil.copy2(args.shim, module)
for source, relative in ((args.core, 'ngx/_nvngx.dll'), (args.dlss, 'dlss/nvngx_dlss.dll')):
target = player / relative
target.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(source, target)
config = (repo / 'packaging/windows/d4r.ini').read_text()
config, replaced = re.subn(r'^Model\s*=\s*K\s*$', 'Model = M', config, count=1, flags=re.M)
if replaced != 1:
raise RuntimeError('expected the default Model = K setting')
(player / 'd4r.ini').write_text(config)
native = player / 'native/gfx1101'
if args.native_objects:
for name in LAYERS:
shutil.copy2(args.native_objects / (name + '.hsaco'), native / (name + '.hsaco'))
manifest_path = native / 'd4r-kernels.txt'
manifest = manifest_path.read_text()
control_manifest = ''.join(line for line in manifest.splitlines(keepends=True)
if not any(line.startswith(name + ' ') for name in LAYERS))
env = {k: v for k, v in os.environ.items() if not k.startswith(('D4R_', 'ZLUDA_'))}
nvapi = player / 'nvapi/gfx1101/nvapi64.dll'
if not nvapi.is_file():
nvapi = player / 'nvapi/nvapi64.dll'
report = {'passed': False, 'synthetic': True, 'gameAccessed': False, 'qualityReference': False,
'preset': 13, 'model': 'M', 'quality': 'Performance', 'createFlags': 11,
'render': [ow // 2, oh // 2], 'output': [ow, oh], 'frames': args.frames,
'nativeFp8': False, 'comparison': 'same-RDNA3 translated PTX and native repeat',
'artifacts': {str(p): sha(p) for p in (args.probe, args.shim, args.core, args.dlss)},
'nativeObjects': {name: sha(native / (name + '.hsaco')) for name in LAYERS}, 'runs': []}
reference_hashes = None
try:
for label in ('native', 'translated', 'native-repeat'):
folder = args.output / label
folder.mkdir()
manifest_path.write_text(control_manifest if label == 'translated' else manifest)
child_env = dict(env, D4R_DIAG_DIR=str(folder))
command = [str(args.probe), '--module', str(module), '--hip-root', str(player / 'hip'),
'--cuda-dll', str(player / 'zluda/nvcuda.dll'), '--nvapi-dll', str(nvapi),
'--ngx-core', str(args.core), '--dlss-dll', str(args.dlss), '--ngx-abi', 'project',
'--interop-mode', 'command-list', '--player-mode', '1', '--preset', '13',
'--ngx-quality', '0', '--ngx-create-flags', '11', '--ngx-output-resolution', args.resolution,
'--ngx-input-resolution', f'{ow // 2}x{oh // 2}', '--iterations', str(args.frames)]
with (folder / 'stdout.log').open('wb') as out, (folder / 'stderr.log').open('wb') as err:
result = subprocess.run(command, env=child_env, stdout=out, stderr=err, timeout=args.timeout)
text = (folder / 'stdout.log').read_text(errors='replace')
logs = (folder / 'stderr.log').read_text(errors='replace')
player_log = player / 'd4r_nvngx.log'
if player_log.exists():
shutil.copy2(player_log, folder / 'player.log')
logs += player_log.read_text(errors='replace')
hashes = {p.name: sha(p) for p in sorted(folder.glob('output-*.rgba16f'))}
selected = {name: bool(re.search(r'\[d4r-native\] "' + re.escape(name) + r'" ->', logs)) for name in LAYERS}
row = {'label': label, 'exitCode': result.returncode, 'outputHashes': hashes,
'nativeLayersLoaded': selected, 'finiteFrames': len(re.findall(r'D3D12_OUTPUT preset=13 frame=\d+ finite=1 ', text)),
'playerSelectedM': 'model=M native_fp8=0' in logs,
'performanceSelected': 'D3D12_NGX_QUALITY value=0' in text}
row['executionPassed'] = (result.returncode == 0 and 'PASS D3D12_DLSS' in text
and len(hashes) == args.frames and row['finiteFrames'] == args.frames
and row['playerSelectedM'] and row['performanceSelected'])
row['selectionPassed'] = not any(selected.values()) if label == 'translated' else all(selected.values())
if reference_hashes is None:
reference_hashes = hashes
row['exactNativeControl'] = hashes == reference_hashes and len(hashes) == args.frames
row['differences'] = []
for name in hashes:
if hashes[name] != reference_hashes.get(name):
actual = np.fromfile(folder / name, '<f2').astype('f4')
baseline = np.fromfile(args.output / 'native' / name, '<f2').astype('f4')
row['differences'].append({'frame': name, 'components': int(np.count_nonzero(actual != baseline)),
'maxAbs': float(np.max(np.abs(actual - baseline)))})
report['runs'].append(row)
(args.output / 'summary.json').write_text(json.dumps(report, indent=2) + '\n')
print(f'{label}: execution={row["executionPassed"]} selection={row["selectionPassed"]} exact={row["exactNativeControl"]} frames={row["finiteFrames"]}', flush=True)
if not row['executionPassed']:
break
report['passed'] = len(report['runs']) == 3 and all(r['executionPassed'] and r['selectionPassed'] and r['exactNativeControl'] for r in report['runs'])
finally:
manifest_path.write_text(manifest)
(args.output / 'summary.json').write_text(json.dumps(report, indent=2) + '\n')
return 0 if report['passed'] else 1
if __name__ == '__main__':
raise SystemExit(main())
+75 -1
View File
@@ -6,6 +6,7 @@ import os
import shutil
from pathlib import Path
import subprocess
import struct
def main():
@@ -19,6 +20,11 @@ def main():
parser.add_argument('--float-guides', action='store_true', help='exercise equivalent float32 color/motion/normals and scalar roughness')
parser.add_argument('--switch-guides', action='store_true', help='alternate canonical and float guide formats without recreating feature')
parser.add_argument('--auto-exposure', action='store_true', help='also set the public NGX AutoExposure creation flag')
parser.add_argument('--dynamic-extents', action='store_true', help='read per-frame bounded render dimensions from dynamic-extents.txt')
parser.add_argument('--two-features', action='store_true', help='evaluate two live independent RR features on each game command list')
parser.add_argument('--max-resources', action='store_true', help='retain creation-sized textures while dynamic input rectangles change')
parser.add_argument('--output-float32', action='store_true', help='validate GPU half-to-float32 output conversion against widened standalone output')
parser.add_argument('--switch-output', action='store_true', help='alternate half/float32 output formats without recreating feature')
parser.add_argument('--depth-format', choices=('r32', 'r32-typeless', 'd32', 'd32s8', 'd32s8-typeless'), default='r32', help='exercise native depth-plane copy format')
parser.add_argument('--player-config', action='store_true', help='stage a private player folder and select RR only through d4r.ini')
parser.add_argument('--use-existing-ini', action='store_true', help='preserve the selected runtime INI when staging player validation')
@@ -35,6 +41,12 @@ def main():
parser.error('--without-sr requires --contracts and a configured native RR backend')
if args.switch_guides and not args.float_guides:
parser.error('--switch-guides requires --float-guides')
if args.dynamic_extents and (args.float_guides or args.switch_guides or args.depth_format != 'r32'):
parser.error('--dynamic-extents currently uses canonical guides and R32 depth')
if args.max_resources and not args.dynamic_extents:
parser.error('--max-resources requires --dynamic-extents')
if args.switch_output and (not args.output_float32 or args.max_resources):
parser.error('--switch-output requires --output-float32 and cannot retain max resources')
build = Path(__file__).resolve().parents[2] / 'build'
if not args.output.is_relative_to(build.resolve()):
parser.error('output must stay under ignored build/')
@@ -92,6 +104,16 @@ def main():
mode += '-switch-guides'
if args.auto_exposure:
mode += '-auto-exposure'
if args.dynamic_extents:
mode += '-dynamic-extents'
if args.two_features:
mode += '-two-features'
if args.max_resources:
mode += '-max-resources'
if args.output_float32:
mode += '-output-float32'
if args.switch_output:
mode += '-switch-output'
if args.depth_format != 'r32':
mode += '-depth-' + args.depth_format
if mode:
@@ -128,6 +150,32 @@ def main():
raise RuntimeError('NGX RR temporal guide-format switching checks incomplete')
if args.auto_exposure and 'PASS RR_NGX_FLAGS' not in text:
raise RuntimeError('NGX RR creation flag checks incomplete')
if args.dynamic_extents:
extents = [tuple(map(int, line.split())) for line in (args.inputs / 'dynamic-extents.txt').read_text().splitlines()]
if 'PASS RR_NGX_DYNAMIC_EXTENTS' not in text:
raise RuntimeError('NGX RR dynamic extent checks incomplete')
for index, (width, height) in enumerate(extents):
if f'RR_NGX_EXTENT index={index} render={width}x{height} exact=1' not in text:
raise RuntimeError('Incomplete NGX RR dynamic extent output')
creation = tuple(map(int, (args.inputs / 'frames.txt').read_text().splitlines()[0].split()[1:3]))
previous, expected_resizes = creation, []
for size in extents:
if size != previous:
expected_resizes.extend([f'D4R_RR_RESIZE render={size[0]}x{size[1]} history_reset=1 model_reused=1 packet_reused=1'] * (2 if args.two_features else 1))
previous = size
diagnostics = args.output / ('player/d4r_nvngx.log' if args.player_config else 'stderr.log')
actual_resizes = [line for line in diagnostics.read_text().splitlines() if line.startswith('D4R_RR_RESIZE ')]
if actual_resizes != expected_resizes:
raise RuntimeError('NGX RR resize operations differ from requested sequence')
if args.contracts and 'RR_NGX_EXTENT_BOUNDS' not in text:
raise RuntimeError('NGX RR extent bounds checks missing')
report['dynamicExtents'] = extents
report.update(geometryChanges=len(expected_resizes), modelReusedAcrossResize=True,
packetReusedAcrossResize=True, historyResetOnResize=True)
if args.max_resources:
if 'PASS RR_NGX_MAX_RESOURCES' not in text:
raise RuntimeError('NGX RR creation-sized dynamic input checks missing')
report['creationSizedInputResourcesRetained'] = True
depth_values = {'r32-typeless': 39, 'd32': 40, 'd32s8': 20, 'd32s8-typeless': 19}
if args.depth_format != 'r32':
marker = f'PASS RR_NGX_DEPTH format={depth_values[args.depth_format]} stencil_preserved={int(args.depth_format.startswith("d32s8"))}'
@@ -140,9 +188,35 @@ def main():
outputs = sorted(path.name for path in (args.output / 'frames').glob('*-out.bin'))
if outputs != sorted(expected):
raise RuntimeError('Incomplete NGX RR output set')
def reference_bytes(name):
data = (args.reference / name).read_bytes()
if args.output_float32 and (not args.switch_output or expected.index(name) % 2):
return b''.join(struct.pack('<f', value[0]) for value in struct.iter_unpack('<e', data))
return data
for name in expected:
if (args.output / 'frames' / name).read_bytes() != (args.reference / name).read_bytes():
if (args.output / 'frames' / name).read_bytes() != reference_bytes(name):
raise RuntimeError('NGX RR reference mismatch: ' + name)
if args.two_features:
if 'PASS RR_NGX_TWO_FEATURES' not in text:
raise RuntimeError('NGX RR two-feature lifecycle checks incomplete')
peer_outputs = sorted(path.name for path in (args.output / 'frames/peer').glob('*-out.bin'))
if peer_outputs != sorted(expected):
raise RuntimeError('Incomplete first-feature output set')
for index, name in enumerate(expected):
if (args.output / 'frames/peer' / name).read_bytes() != reference_bytes(name):
raise RuntimeError('NGX RR first-feature reference mismatch: ' + name)
if f'RR_NGX_PEER_FRAME index={index} exact=1 both_features_live=1' not in text:
raise RuntimeError('NGX RR first-feature validation incomplete')
report.update(liveFeatures=2, bothFeaturesStandaloneExact=True, separateVulkanDevices=True,
twoDelayedBoundariesPerCommandList=True, bothReleasedBeforeFinalSubmit=True)
if args.output_float32:
if 'PASS RR_NGX_OUTPUT_FLOAT32' not in text:
raise RuntimeError('NGX RR float32 output checks missing')
report.update(outputFormat='RGBA32F', gpuOutputConversion=True, widenedHalfReferenceExact=True)
if args.switch_output:
if 'PASS RR_NGX_SWITCH_OUTPUT' not in text:
raise RuntimeError('NGX RR temporal output format switching checks missing')
report.update(outputFormat='alternating RGBA16F/RGBA32F', outputFormatSwitchWithoutFeatureRecreation=True)
optional_names = ('hitdistance', 'disocclusion', 'sss', 'particles')
optional_frames = {name: sum((args.inputs / (frame.removesuffix('-out.bin') + '-' + name + '.bin')).is_file()
for frame in expected) for name in optional_names}
+92
View File
@@ -0,0 +1,92 @@
"""Probe plain Windows SR render subrects without a game or FSR denoiser.
Retains creation-sized resources. Separate child processes compare static,
smaller-render, and reset-on-every-frame cases. Output is diagnostic evidence,
not a quality or performance gate.
"""
import argparse
import hashlib
import json
import os
from pathlib import Path
import re
import subprocess
def main():
parser = argparse.ArgumentParser(description=__doc__)
for name in ('probe', 'shim', 'runtime', 'core', 'dlss', 'output'):
parser.add_argument('--' + name, required=True, type=lambda p: Path(p).resolve())
parser.add_argument('--creation-size', default='426x256')
parser.add_argument('--smaller-render-size', default='384x224')
args = parser.parse_args()
build = Path(__file__).resolve().parents[2] / 'build'
if not args.output.is_relative_to(build.resolve()):
parser.error('output must stay under ignored build/')
args.output.mkdir(parents=True, exist_ok=False)
runtime = args.runtime
native = runtime / 'native'
if (native / 'gfx1101').is_dir():
native /= 'gfx1101'
nvapi = runtime / 'nvapi/nvapi64.dll'
if not nvapi.is_file():
nvapi = runtime / 'nvapi/gfx1101/nvapi64.dll'
env = {k: v for k, v in os.environ.items()
if not k.startswith(('D4R_', 'ZLUDA_'))}
env.update(HIP_PATH=str(runtime / 'hip'),
ZLUDA_CUDA_LIB=str(runtime / 'zluda/nvcuda.dll'),
D4R_NVAPI_BACKEND=str(runtime / 'nvapi/backend/nvapi64.dll'),
D4R_FORMAT_MODULE=str(runtime / 'pixel_convert_gfx1101.hsaco'),
D4R_ZLUDA_NATIVE_DIR=str(native), D4R_ZLUDA_WMMA='1',
D4R_ZLUDA_WMMA_FP8='1', D4R_ZLUDA_WMMA_F16_REFERENCE='1',
D4R_QUIET_API='1',
ZLUDA_CACHE_DIR=str(Path(os.environ['LOCALAPPDATA']) / 'd4r/cache'))
env['PATH'] = str(runtime / 'hip/bin') + os.pathsep + str(runtime / 'zluda') + os.pathsep + env['PATH']
report = {'synthetic': True, 'gameAccessed': False, 'qualityReference': False,
'performanceEvidence': False, 'creationSize': args.creation_size,
'outputSize': [640, 384], 'smallerRenderSize': args.smaller_render_size,
'artifacts': {str(p): hashlib.sha256(p.read_bytes()).hexdigest()
for p in (args.probe, args.shim, args.core, args.dlss)},
'runs': []}
for flags in (0, 11):
for mode in ('static', 'smaller', 'smaller-reset'):
output = args.output / f'flags-{flags}-{mode}'
output.mkdir()
child_env = dict(env, D4R_DIAG_DIR=str(output), D4R_NGX_DATA_DIR=str(output / 'ngx-data'))
if mode != 'static':
child_env['D4R_PROBE_RENDER_SUBRECT'] = args.smaller_render_size
command = [str(args.probe), '--module', str(args.shim),
'--hip-root', str(runtime / 'hip'), '--cuda-dll', str(runtime / 'zluda/nvcuda.dll'),
'--ngx-core', str(args.core), '--dlss-dll', str(args.dlss), '--nvapi-dll', str(nvapi),
'--ngx-abi', 'project', '--interop-mode', 'command-list', '--preset', '11',
'--ngx-create-flags', str(flags), '--ngx-input-resolution', args.creation_size,
'--ngx-output-resolution', '640x384', '--iterations', '4']
if mode == 'smaller-reset':
command += ['--reset-every-frame', '1']
row = {'flags': flags, 'mode': mode, 'command': command}
with (output / 'stdout.log').open('wb') as out, (output / 'stderr.log').open('wb') as err:
try:
result = subprocess.run(command, env=child_env, stdout=out, stderr=err, timeout=180)
row['exitCode'] = result.returncode
except subprocess.TimeoutExpired:
row['timeout'] = True
text = (output / 'stdout.log').read_text(errors='replace')
row['finiteFrames'] = len(re.findall(r'D3D12_OUTPUT preset=11 frame=\d+ finite=1 ', text))
expected = [args.creation_size] + [args.creation_size if mode == 'static' else args.smaller_render_size] * 3
row['appliedRenderSizes'] = re.findall(r'D3D12_RENDER_SUBRECT frame=\d+ size=(\d+x\d+) ', text)
row['passed'] = (row.get('exitCode') == 0 and 'PASS D3D12_DLSS' in text
and row['finiteFrames'] == 4 and row['appliedRenderSizes'] == expected)
row['outputHashes'] = {p.name: hashlib.sha256(p.read_bytes()).hexdigest()
for p in sorted(output.glob('output-*.rgba16f'))}
report['runs'].append(row)
(args.output / 'summary.json').write_text(json.dumps(report, indent=2) + '\n')
print(f'flags={flags} mode={mode} exit={row.get("exitCode")} finite_frames={row["finiteFrames"]}', flush=True)
if row.get('timeout'):
return 1
report['passed'] = all(row['passed'] for row in report['runs'])
(args.output / 'summary.json').write_text(json.dumps(report, indent=2) + '\n')
return 0 if report['passed'] else 1
if __name__ == '__main__':
raise SystemExit(main())
@@ -0,0 +1,23 @@
# Base Windows capture-helper SHA256: 517e0b158fad0070c556d84ce62749159a4b1f1a7b4bee93632f62cc23b3ec0e
--- a/zluda/src/impl/d4r_replay.rs
+++ b/zluda/src/impl/d4r_replay.rs
@@ -15,6 +15,10 @@
"dltss_pwin_dec1_layer" | "dltss_pwin_dec0_layer" => Some(176),
"rrlite_enc1_4x4" | "rrlite_enc2_4x4" | "rrlite_dec2_4x4" | "rrlite_dec1_4x4" => Some(56),
"rrlite_enc3_tube_4x4" => Some(88),
+ // Audited SR 310.9.1 output variant: the reconstruction LUT pointer is
+ // at byte 280 of this exact 328-byte PTX argument block. Other texture
+ // kernels remain outside this capture hook's whitelist.
+ "hiluma_engine_output_depthreg_mvhi_ldr_max_v2_rel" => Some(328),
_ => None,
}
}
@@ -131,6 +135,8 @@
assert_eq!(super::parameter_size("rrlite_enc3_tube_4x4"), Some(88));
assert_eq!(super::parameter_size("dltss_pwin_enc0_layer_prep"), None);
assert_eq!(super::parameter_size("rrlite_enc3_tube_4x4_prep"), None);
+ assert_eq!(super::parameter_size("hiluma_engine_output_depthreg_mvhi_ldr_max_v2_rel"), Some(328));
+ assert_eq!(super::parameter_size("hiluma_engine_output_depthreg_mvhi_hdr_max_v2_rel"), None);
assert_eq!(super::parameter_size("dltss_pwin_unknown_layer"), None);
}
}