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:
1 parent
247fe1092d
commit
a617c818de
62 files changed
+5894
-89
No files matched your search
@@ -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
|
||||
|
||||
Reference in new issue
Block a user