engine/rr: native preset F (DLSS 4.5 Ray Reconstruction) and the reference's fused roundings

- engine/compile_rr2.py, runtime_rr2.inc, engine/rr/rr2_m.comp, exposure_f.comp,
  gen_rr2_enc0.py, rr2_pack.py, rr2_ref.py: the rr2_* model (nvngx_dlssd.dll 310.9's
  third network) as d4r's own shaders - exposure, enc0 gather/block split, enc1-5 and
  dec4-0 through one matrix shader, translated output passes; weights packed from the
  DLL, argument blocks from the capture
- ptx2rr: match the driver's fused roundings - opaque-zero OR around cvt.rn.f16.f32
  against v_fma_mix, half-away-from-zero formatted stores, UNORM 10:10:10:2 as RGBA32F
  of q/1023, OCML tanhF with the AMDGPU exp lowering, binary16 fma and fetch roundings;
  --probe for register bisection against ZLUDA
- dec0_m/swin_m/enc0_*: RR_MIX build option (D4R_RR_MIX=1), one v_fma_mix rounding per
  32-deep step, ~5% faster, not bit-exact; default build stays exact
- runtime_rr.inc: RModel/REngine dispatch to the D4RR0002 model; replay_rr: RGBA32F and
  A2B10G10R1010 texel sizes, roughnessPacked bit, larger debug staging
- shim: engine\rr-f model slot, 310.9 version probe so preset F is only claimed when the
  CUDA path has it; d4r.ini / d4r_config.py Model = F; run_rr_engine_compare.sh D4R_RR_DLL
- kernels/rr/swin_native.h + rr_swin_native_test.cpp: HIP port of encoder 1 for the
  FP8-product and rounding cost measurements
- engine/rr/README: preset F's block, options, capture-by-capture results, timings and
  the open camera-matrix difference
This commit is contained in:
woutermans committed 2026-10-13 13:59:17 +02:00
1 parent 065fe2c2a1
commit 8eb7a94533
31 files changed
+3547 -128

No files matched your search

+114 -35
View File
@@ -612,10 +612,12 @@ static void load_portable_config()
portable_set("D4R_RR_PRESET", "4");
else if (upper == "E")
portable_set("D4R_RR_PRESET", "5");
else if (upper == "0" || upper == "4" || upper == "5")
else if (upper == "F") // DLSS 4.5 Ray Reconstruction: nvngx_dlssd.dll 310.9 or later
portable_set("D4R_RR_PRESET", "6");
else if (upper == "0" || upper == "4" || upper == "5" || upper == "6")
portable_set("D4R_RR_PRESET", upper);
else
g_portable.notes.push_back("d4r.ini: [RayReconstruction] Model must be auto, default, D or E, not '" +
g_portable.notes.push_back("d4r.ini: [RayReconstruction] Model must be auto, default, D, E or F, not '" +
rrModel + "'; leaving the model to the game");
}
@@ -1344,9 +1346,17 @@ struct Global
DenoiserProbe denoiser;
bool denoiserAttempted = false;
std::wstring denoiserPath;
// nvngx_dlssd.dll's file version (major, minor, build, revision); all zero when it has no version resource
unsigned denoiserVersion[4] = {};
};
static Global g;
// nvngx_dlssd.dll 310.9 and newer hold preset F (DLSS 4.5 Ray Reconstruction); unknown versions count as without.
static bool denoiser_has_preset_f()
{
return g.denoiserVersion[0] > 310 || (g.denoiserVersion[0] == 310 && g.denoiserVersion[1] >= 9);
}
static std::mutex g_featureApiMutex;
template <typename Function> static bool load_export(HMODULE module, const char* name, Function& function)
@@ -1608,6 +1618,35 @@ static bool load_denoiser_library()
logf("found the DLSS-Denoiser library %ls (mapped as %ls): %s", g.denoiserPath.c_str(), mapped,
g.denoiser.cudaCapable ? "CUDA entry points present"
: "no CUDA entry points - not a CUDA-capable denoiser build");
// The file version, from the module's version resource (VS_FIXEDFILEINFO follows its signature 0xFEEF04BD).
// Preset F (DLSS 4.5, the rr2_* kernels) exists from 310.9 on: an older library runs preset D for that hint.
if (HRSRC found = FindResourceW(g.denoiserModule, MAKEINTRESOURCEW(1), MAKEINTRESOURCEW(16)))
{
const DWORD size = SizeofResource(g.denoiserModule, found);
HGLOBAL loaded = LoadResource(g.denoiserModule, found);
const unsigned char* bytes = loaded != nullptr ? static_cast<const unsigned char*>(LockResource(loaded)) : nullptr;
for (DWORD at = 0; bytes != nullptr && at + 16 <= size; at += 4)
{
uint32_t words[4];
std::memcpy(words, bytes + at, sizeof(words));
if (words[0] != 0xFEEF04BDu)
continue;
g.denoiserVersion[0] = words[2] >> 16;
g.denoiserVersion[1] = words[2] & 0xffffu;
g.denoiserVersion[2] = words[3] >> 16;
g.denoiserVersion[3] = words[3] & 0xffffu;
break;
}
}
if (g.denoiserVersion[0] == 0)
logf("Ray Reconstruction: the DLSS-Denoiser library has no file version; whether its CUDA path has preset F "
"(DLSS 4.5, 310.9 or newer) is unknown");
else
logf("Ray Reconstruction: DLSS-Denoiser library version %u.%u.%u.%u: its CUDA path %s", g.denoiserVersion[0],
g.denoiserVersion[1], g.denoiserVersion[2], g.denoiserVersion[3],
denoiser_has_preset_f() ? "has preset F (DLSS 4.5)"
: "has NO preset F (DLSS 4.5 needs 310.9 or newer) - preset F is shown only by "
"frames the engine serves; frames the CUDA backend serves show preset D");
return g.denoiser.cudaCapable;
}
@@ -2561,6 +2600,7 @@ struct Feature
double engineNetWaitEmaUs = 0.0; // worker only: smoothed launch-to-completion wait, for adaptive polling
void* engineNet = nullptr;
bool engineRefused = false;
bool rrCompileWarned = false; // the "CUDA backend has no preset F" warning was logged for the compile frames
float engineJitter[2] = {0, 0}; // of the previous engine frame
// Serializes picking the slot to present (game thread) against picking
// the slot to overwrite (worker).
@@ -8248,54 +8288,62 @@ static bool engine_evaluate_m(Feature& feature, ID3D12GraphicsCommandList* list,
return true;
}
// Ray Reconstruction models (engine/compile_rr.py): one per preset, engine\rr-d and engine\rr-e next to this DLL, or
// D4R_ENGINE_MODEL_RR_D_DIR / D4R_ENGINE_MODEL_RR_E_DIR.
// Ray Reconstruction models (engine/compile_rr.py; preset F: engine/compile_rr2.py): one per preset, engine\rr-d,
// engine\rr-e and engine\rr-f next to this DLL, or D4R_ENGINE_MODEL_RR_D_DIR / _E_DIR / _F_DIR. Slots: 0 D, 1 E, 2 F.
struct EngineBackendR
{
// The model's pipelines take minutes to compile on a cold shader cache, so each preset's model loads on a thread
// of its own; until it is ready the CUDA backend serves the frames. state: 0 not started, 1 loading, 2 ready,
// 3 failed.
std::once_flag once[2]; // [preset E]
std::atomic<int> state[2] = {};
std::unique_ptr<d4r::RModel> models[2];
std::once_flag once[3];
std::atomic<int> state[3] = {};
std::unique_ptr<d4r::RModel> models[3];
};
static EngineBackendR g_engineR;
static void init_engine_backend_rr(bool presetE)
static const char g_rrLetter[3] = {'D', 'E', 'F'};
static const uint32_t g_rrPreset[3] = {4, 5, 6};
static std::string rr_model_directory(int slot)
{
std::string directory = env_string(presetE ? "D4R_ENGINE_MODEL_RR_E_DIR" : "D4R_ENGINE_MODEL_RR_D_DIR");
static const char* const variables[3] = {"D4R_ENGINE_MODEL_RR_D_DIR", "D4R_ENGINE_MODEL_RR_E_DIR", "D4R_ENGINE_MODEL_RR_F_DIR"};
static const wchar_t* const folders[3] = {L"\\engine\\rr-d", L"\\engine\\rr-e", L"\\engine\\rr-f"};
std::string directory = env_string(variables[slot]);
if (directory.empty())
{
char folder[MAX_PATH * 3] = {};
WideCharToMultiByte(CP_ACP, 0, (g_portable.dir + (presetE ? L"\\engine\\rr-e" : L"\\engine\\rr-d")).c_str(), -1,
folder, sizeof(folder), nullptr, nullptr);
WideCharToMultiByte(CP_ACP, 0, (g_portable.dir + folders[slot]).c_str(), -1, folder, sizeof(folder), nullptr, nullptr);
directory = folder;
}
return directory[0] == '/' ? "Z:" + directory : directory;
}
static void init_engine_backend_rr(int slot)
{
const std::string path = rr_model_directory(slot);
if (g_vk.interop == nullptr || g_vk.lifetime == nullptr || g_vk.device == VK_NULL_HANDLE || d4r::vkTable.CreateBuffer == nullptr)
{
logf("engine backend (Ray Reconstruction): Vulkan interop is unavailable");
g_engineR.state[presetE] = 3;
g_engineR.state[slot] = 3;
return;
}
const std::string path = directory[0] == '/' ? "Z:" + directory : directory;
logf("engine backend (Ray Reconstruction %c): loading the model from %s and compiling its pipelines; the CUDA "
"backend serves frames until it is ready", presetE ? 'E' : 'D', path.c_str());
"backend serves frames until it is ready", g_rrLetter[slot], path.c_str());
const auto start = std::chrono::steady_clock::now();
try
{
auto model = std::make_unique<d4r::RModel>(g_vk.physical, g_vk.device, path);
if (model->preset() != (presetE ? 5u : 4u))
throw std::runtime_error(presetE ? "the directory holds a preset D model" : "the directory holds a preset E model");
g_engineR.models[presetE] = std::move(model);
if (model->preset() != g_rrPreset[slot])
throw std::runtime_error("the directory holds a model of another preset");
g_engineR.models[slot] = std::move(model);
}
catch (const std::exception& error)
{
logf("engine backend (Ray Reconstruction): cannot load the model from %s: %s", path.c_str(), error.what());
g_engineR.state[presetE] = 3;
g_engineR.state[slot] = 3;
return;
}
g_engineR.state[presetE] = 2;
logf("engine backend (Ray Reconstruction %c): model %s loaded and pipelines compiled in %.0f ms", presetE ? 'E' : 'D',
g_engineR.state[slot] = 2;
logf("engine backend (Ray Reconstruction %c): model %s loaded and pipelines compiled in %.0f ms", g_rrLetter[slot],
path.c_str(), std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - start).count());
}
@@ -8305,30 +8353,55 @@ static bool engine_evaluate_rr(Feature& feature, ID3D12GraphicsCommandList* list
ID3D12Resource* depth, ID3D12Resource* motion, ID3D12Resource* output, void* parameters,
const FrameParams& p, uint32_t frame)
{
// set below once the preset is known: preset F was asked for but nvngx_dlssd.dll (the CUDA backend) has none
bool cudaLacksF = false;
auto warn_not_f = [&](const char* when) {
if (!cudaLacksF)
return;
logf("WARNING: Ray Reconstruction preset F (DLSS 4.5) was selected, but %s feature %u is served by the CUDA "
"backend, whose nvngx_dlssd.dll %u.%u.%u.%u has no preset F: those frames show preset D, NOT F",
when, feature.handle.Id, g.denoiserVersion[0], g.denoiserVersion[1], g.denoiserVersion[2],
g.denoiserVersion[3]);
};
auto refuse = [&](const char* reason) {
feature.engineRefused = true;
logf("engine backend (Ray Reconstruction): %s; feature %u uses the CUDA backend from frame %u", reason,
feature.handle.Id, frame);
warn_not_f("from this frame on");
return false;
};
// NGX 310.7 runs preset E's kernels for the hint 5 only and preset D's (diamond_wallaby) for every other value:
// none, the retired A / B / C (1-3), 6, and values with the top bit set such as Cyberpunk 2077's 0x80000002 or
// 0x80000005 (harness captures with D4R_RR_PRESET, 2026-10-12).
const bool presetE = feature.preset == 5;
std::call_once(g_engineR.once[presetE], [presetE] {
g_engineR.state[presetE] = 1;
// NGX 310.9 has preset F (DLSS 4.5, the rr2_* kernels) and runs it for every hint but 4 (D) and 5 (E): none,
// 6, Cyberpunk 2077's 0x80000002 (harness runs with D4R_RR_PRESET, 2026-10-13). The shim cannot see which DLL
// version the CUDA backend loaded, so that mapping applies when the user built a preset F model (engine\rr-f),
// which only a 310.9 DLL yields.
static const bool haveF = GetFileAttributesA((rr_model_directory(2) + "\\rr.txt").c_str()) != INVALID_FILE_ATTRIBUTES;
const int slot = feature.preset == 5 ? 1 : (haveF && feature.preset != 4) ? 2 : 0;
std::call_once(g_engineR.once[slot], [slot] {
g_engineR.state[slot] = 1;
// D4R_ENGINE_RR_SYNC=1 (harness comparisons): load in place so the engine serves the first frame
if (env_uint("D4R_ENGINE_RR_SYNC", 0) != 0)
init_engine_backend_rr(presetE);
init_engine_backend_rr(slot);
else
std::thread([presetE] { init_engine_backend_rr(presetE); }).detach();
std::thread([slot] { init_engine_backend_rr(slot); }).detach();
});
const int state = g_engineR.state[presetE].load();
cudaLacksF = slot == 2 && !denoiser_has_preset_f();
const int state = g_engineR.state[slot].load();
if (state == 1)
return false; // still compiling: the CUDA backend serves this frame, the engine is tried again next frame
d4r::RModel* const model = g_engineR.models[presetE].get();
{
// still compiling: the CUDA backend serves this frame, the engine is tried again next frame
if (!feature.rrCompileWarned)
{
feature.rrCompileWarned = true;
warn_not_f("while the preset F model compiles,");
}
return false;
}
d4r::RModel* const model = g_engineR.models[slot].get();
if (state != 2 || model == nullptr)
return refuse(presetE ? "no preset E model (engine\\rr-e)" : "no preset D model (engine\\rr-d)");
return refuse(slot == 1 ? "no preset E model (engine\\rr-e)" : slot == 2 ? "no preset F model (engine\\rr-f)" : "no preset D model (engine\\rr-d)");
// NVSDK_NGX_DLSS_Feature_Flags: IsHDR 1, MVLowRes 2, MVJittered 4, DepthInverted 8.
if ((feature.flags & 0x3) != 0x3)
return refuse("needs the HDR flag and render-resolution motion vectors");
@@ -8336,6 +8409,11 @@ static bool engine_evaluate_rr(Feature& feature, ID3D12GraphicsCommandList* list
return refuse("jittered motion vectors are not implemented");
if (!p.auxMatrixSet[0] || !p.auxMatrixSet[1])
return refuse("the camera matrices are missing");
// Preset F below 4/3 scaling (DLAA and the like): NGX runs another output filter there, rr2_post_kernel_3_2,
// which the model does not have (harness runs at 1920x1080 output, 2026-10-13: 1440x810 and smaller render sizes
// use rr2_post_kernel, 1456x820 and larger rr2_post_kernel_3_2)
if (slot == 2 && (3ull * feature.outWidth < 4ull * p.renderWidth || 3ull * feature.outHeight < 4ull * p.renderHeight))
return refuse("preset F below 4/3 scaling uses an output filter the engine does not have (rr2_post_kernel_3_2)");
if (feature.engineR != nullptr &&
(p.renderWidth != feature.engineMRenderWidth || p.renderHeight != feature.engineMRenderHeight))
return refuse("changing the effective render resolution after initialization is not implemented");
@@ -8343,15 +8421,16 @@ static bool engine_evaluate_rr(Feature& feature, ID3D12GraphicsCommandList* list
{"DLSS.Input.DiffuseAlbedo", "GBuffer.DiffuseAlbedo"},
{"DLSS.Input.SpecularAlbedo", "GBuffer.SpecularAlbedo"}};
ID3D12Resource* guides[4] = {};
// DLSS.Roughness.Mode 1 (packed): no roughness texture, the roughness is the normals' w
const bool roughnessPacked = get_resource(parameters, "GBuffer.Roughness") == nullptr &&
get_uint_or(parameters, "DLSS.Roughness.Mode", 0) == 1;
// DLSS.Roughness.Mode 1 (packed): the roughness is the normals' w. Preset F follows the mode also when a roughness
// texture is bound (harness run against the CUDA path, 2026-10-13); D and E are only known without one (RE Requiem)
const bool roughnessPacked = get_uint_or(parameters, "DLSS.Roughness.Mode", 0) == 1 &&
(slot == 2 || get_resource(parameters, "GBuffer.Roughness") == nullptr);
for (int i = 0; i < 4; ++i)
{
guides[i] = get_resource(parameters, guideNames[i][0]);
if (guides[i] == nullptr && guideNames[i][1] != nullptr)
guides[i] = get_resource(parameters, guideNames[i][1]);
if (guides[i] == nullptr && i == 1 && roughnessPacked)
if (i == 1 && roughnessPacked)
guides[i] = guides[0];
if (guides[i] == nullptr)
return refuse("a guide the engine needs (normals, roughness, diffuse or specular albedo) is missing");
@@ -8474,7 +8553,7 @@ static bool engine_evaluate_rr(Feature& feature, ID3D12GraphicsCommandList* list
feature.engineMRenderWidth = rw;
feature.engineMRenderHeight = rh;
logf("engine backend (Ray Reconstruction %c): feature %u runs in the game's command list (%ux%u -> %ux%u)",
presetE ? 'E' : 'D', feature.handle.Id, rw, rh, feature.outWidth, feature.outHeight);
g_rrLetter[slot], feature.handle.Id, rw, rh, feature.outWidth, feature.outHeight);
}
}
catch (const std::exception& error)
@@ -8515,7 +8594,7 @@ static bool engine_evaluate_rr(Feature& feature, ID3D12GraphicsCommandList* list
return refuse(failure);
if (frame <= 3 || frame % 600 == 0)
logf("engine frame %u recorded, Ray Reconstruction %c (jitter %.6f,%.6f, reset %d, mvScale %.3f,%.3f)", frame,
presetE ? 'E' : 'D', p.jitterX, p.jitterY, s.reset, p.mvScaleX, p.mvScaleY);
g_rrLetter[slot], p.jitterX, p.jitterY, s.reset, p.mvScaleX, p.mvScaleY);
return true;
}