exposure_f.comp ran NGX's auto exposure in two passes: the 16x16 blocks wrote
their fixed-point luma sums, then a single workgroup added them after a
barrier and finished (11 us of a 25 us stage). PASS 2 does what NVIDIA's
kernel does: each block adds its sum to a 64-bit total with an atomic add
and counts itself in with an acquire-release add; the last block reads the
total, finishes and leaves both words zero for the next frame. The total is
an integer, the same in any order, so the exposure image and the values are
the two passes' bit for bit. compile_rr2.py builds exposure_f2.spv next to
the two passes' shaders (the block sum and the finish are shared functions
now; their arithmetic is unchanged).
64-bit buffer atomics are a device feature (shaderBufferInt64Atomics), so
RModel takes whether the device has it enabled (default no) and preset F
uses the one pass only then and only with a model that has exposure_f2.spv;
otherwise the two passes as before. vkd3d-proton creates its device with
every feature the GPU reports except a list it turns off, which does not
include this one (it backs D3D12's 64-bit atomics with it); the shim reads
the device's features through the interop interface (GetDeviceFeatures) and
logs the result. d4r-rr enables every feature the GPU has, and
D4R_RR_TEST_NO_INT64_ATOMICS=1 turns this one off to run the two passes.
Ray Reconstruction never runs on the native Vulkan frontend's device.
Every image of every frame byte-identical to the previous build (1280x720
-> 1440p, 1114x626 -> 1080p, 960x540 -> 1080p, the stress inputs, with
D4R_RR_TEST_ROBUST2=1, at levels 0 and 3, with D4R_RR2_SPLIT=1), and so is
the two-pass fallback of the new build; presets D and E unchanged. Controls:
the last block finishing with twice the total changes 64 of the 90 images of
the 1280x720 sequence (and nothing under the fallback), finishing without
its own block's sum 58 of 90.
RX 9070 XT, d4r-rr 720p -> 1440p, interleaved: exposure 25.2 -> 14.9 us,
frame 3.536 -> 3.526 ms (the fallback 25.1 us).
Signed-off-by: itchyfeetleech <[email protected]>
NGX's CNN takes this frame's exposure scale and pre-exposure in both translated kernels (input words 244-256,
reconstruction 592-604) and the previous frame's exposure as the input kernel's texture 352
(cuda_copy_exposure_kernel's half surface). The runtime left all of it at the model dump's values, so HDR games
with a pre-exposure other than 1 or a changing exposure measured 18-39 dB against NGX; now 54-64 dB.
AutoExposure (flag 0x40), which the shim refused for E/F: NGX runs K's log-mean measurement on M's grid every
second frame. exposure_k.comp MODE 3 writes it to the 1x1 image the kernels sample; sr/exposure_copy_e.comp
keeps the previous exposure. 60-66 dB over eleven frames at 960x540 -> 1920x1080, pre-exposure 19-21.
The reconstruction's jitter words 544-556 are (y, x); the runtime wrote (-x, -y), which matches only the
harness's first three jitters, so every later frame of a game's jitter sequence measured 26-28 dB. Twelve-frame
captures now measure 60-66 dB HDR and LDR; on the Steam Deck through the shim 51-67 dB against ZLUDA.
The D3D12 harness takes per-frame lists in D4R_HARNESS_PRE_EXPOSURE / _EXPOSURE_SCALE; d4r-e takes the
AutoExposure flag, per-frame exposure values and the game's exposure texture. E/F models need rebuilding
(the install's model cache key changes); older models keep the old binding and refuse AutoExposure.
Presets E (5) and F (6) run on d4r's own Vulkan engine without cooperative
matrices: the input and reconstruction kernels translated from the user's DLL
(engine/compile_sr_e.py, rr/ptx2rr.py), the network engine/sr/e_net.comp with
packed v_pk_fma_f16 accumulation (engine/cnn_sr/mma_f16.glsl, the default
head; --head-accumulation f32 keeps the f32 head). Runtime runtime_e.inc
(EModel/EEngine), game adapter game_e.cpp, replay d4r-e; the shim serves them
from engine\e and engine\f (D4R_ENGINE_MODEL_E_DIR / _F_DIR), LDR or HDR, any
render size, not with AutoExposure. build-engine-models.sh builds both.
Translator fixes that apply to every translated kernel: .relu on f16/f16x2
fma/add/mul was ignored; add.rz was always taken for the roundf idiom (lost
the log1pf correction term); formatted image stores expand at the call site
(SPIR-V image type fix); f16 mma.sync m16n8k8/k16, .const loads, shared
16-bit stores, aligned 32-bit arena words (D4R_WORD_ARENA), unified arena.
Steam Deck gfx1033: 2.55 ms at 640x360 -> 1280x720, 6.20 ms at 1080p
output; Rounded Quality sizes take mid_3x3 (reconstruction 2.44 -> 1.23 ms).
Ray Reconstruction CNN presets A-C (compile_rr3.py, the packed RR flavor)
stay out of this commit; engine/README.md already documents their tuning.
- GameFrameR/RFrameImages: DLSSD.ScreenSpaceSubsurfaceScatteringGuide (arg 264)
and DLSSD.ColorBeforeParticles (output stage, texture 416 with its own history
pair 384/392, zero-initialised on first use); guides array 6 -> 8
- DLSS.Use.HW.Depth: byte 980 and output byte 443 bit 24 (Cyberpunk passes the
nonlinear depth buffer); replay's 'inverted' field becomes a bitmask
- shim: bind the two new resources, read the HW-depth flag, and accept every
preset hint except E's 5 by mapping it to D's diamond_wallaby kernels
(harness captures show 0x80000002/0x80000005 run preset D)
- harness: D4R_HARNESS_RR_EXTRA=particles,sss and D4R_HARNESS_RR_HW_DEPTH=1
- ptx2rr: out-of-range integer fetches return zero, matching ZLUDA's behaviour
(the reference the stages are checked against)
- engine/rr/README: the capture findings behind the offsets
- engine/rr: d4r's own shaders for the denoiser frame (exposure, six encoders,
five decoders, hkpn output stage) at NGX layouts, plus PTX->GLSL/HIP packing
scripts, reference implementations and weight extraction
- runtime.h/runtime_rr.inc, game_rr.{h,cpp}: GameDenoiser wired into the engine
build (d4r-rr replay binary) and the D3D12 shim's feature-13 path
- shim: answer GetFeatureRequirements from d4r's own state only (the old
cross-binary CUDA handoff crashed OptiScaler's scan); drop the core's
CUDA GetFeatureRequirements import
- shim/harness: exact guide capture/replay of a frame's RR inputs
- kernels/rr: expanded enc0/engine prototypes; kernels/tools: ptxsim rewrite
plus rrsim and an engine test; vulkan: ptx2glsl FP8/cooperative-matrix
support, kvk inout buffers and KVK_DISABLE_OPTIMIZATION
- bridge: report the surface array in cuSurfObjectCreate traces
- packaging: RR settings and the ICU launch overrides in D4R_README
A standalone Vulkan inference engine (engine/) for DLSS presets K and M that
runs from weights extracted from the user's own nvngx_dlss.dll, with d4r's
own input, network and reconstruction shaders or the native HIP network
layers, recorded into the game's command list by the shim. Opt-in
([Engine] Enabled / D4R_ENGINE=1); anything uncovered falls back to the
CUDA/ZLUDA path with the reason logged.
Byte-identical to NGX's CUDA backend under the phase-capable reference
(1706x960 -> 2560x1440, native HIP network, eleven frames, RX 9070 XT):
- K render- and display-resolution motion vectors, HDR and LDR
- K game-supplied exposure and automatic exposure with any pre-exposure
and exposure scale (one residual: effective exposure exactly 2 or 4)
- K and M with the DLSS indicator's inverted-axis parameters
- M with any exposure settings (NGX ignores them for M)
K packages need the current compile_k.py (marker D4RO0003).
Also: HIP shared buffers are pooled across feature recreation (ROCm leaks
destroyed imports), hipnet.bin must match the preset, M-only games do not
load the K model, install_d4r_runtime.sh stages k, k-ldr, m, m-ldr with
hip/, the activation table has a generator (gen_activation_m.py), and
package_release.sh can bundle the engine's shaders (never its weights).
Signed-off-by: woutermans <[email protected]>