Run native Windows DLSS K and M through an explicit OptiScaler source backend

This commit is contained in:
Codex committed 2026-10-01 16:13:07 +03:00
1 parent 27d6a1230a
commit e14b4cb54c
9 files changed
+633 -8

No files matched your search

+4
View File
@@ -147,6 +147,10 @@ endif()
install(TARGETS d4r_nvapi_compat RUNTIME DESTINATION nvapi-compat) install(TARGETS d4r_nvapi_compat RUNTIME DESTINATION nvapi-compat)
target_link_libraries(d4r_d3d12_hip_interop_probe PRIVATE d3d12 dxgi dxguid) target_link_libraries(d4r_d3d12_hip_interop_probe PRIVATE d3d12 dxgi dxguid)
target_link_libraries(d4r_d3d12_dlss_probe PRIVATE d3d12 dxgi dxguid) target_link_libraries(d4r_d3d12_dlss_probe PRIVATE d3d12 dxgi dxguid)
add_executable(d4r_debug_launcher tools/windows/debug_launcher.cpp)
target_include_directories(d4r_debug_launcher PRIVATE tools/windows)
target_link_libraries(d4r_debug_launcher PRIVATE version dbghelp)
install(TARGETS d4r_debug_launcher RUNTIME DESTINATION bin)
target_link_libraries(d4r_d3d12_command_probe PRIVATE d3d12 dxgi dxguid) target_link_libraries(d4r_d3d12_command_probe PRIVATE d3d12 dxgi dxguid)
target_link_libraries(d4r_d3d12_pixel_probe PRIVATE d3d12 dxgi dxguid) target_link_libraries(d4r_d3d12_pixel_probe PRIVATE d3d12 dxgi dxguid)
add_dependencies(d4r_d3d12_pixel_probe d4r_pixel_code_object) add_dependencies(d4r_d3d12_pixel_probe d4r_pixel_code_object)
+4 -2
View File
@@ -14,8 +14,10 @@ submission, and returns output in the same frame without CPU image copies.
RGBA16/32F, R11G11B10, RGBA/BGRA8, depth, exposure and motion formats have RGBA16/32F, R11G11B10, RGBA/BGRA8, depth, exposure and motion formats have
independent D3D12 typed SRV/UAV conversion tests. Four-frame K/M runs with packed independent D3D12 typed SRV/UAV conversion tests. Four-frame K/M runs with packed
output match the FP16 baseline exactly after resource-format quantisation. output match the FP16 baseline exactly after resource-format quantisation.
The build incorporates upstream RDNA4 commit `dbef4b2`. OptiScaler/game The build incorporates upstream RDNA4 commit `dbef4b2`. Patched OptiScaler now
integration and performance profiling remain in progress; no game support is passes standalone K/M native-network tests with exact RGB agreement against
the direct-d4r baseline; see [the source patch](patches/optiscaler/README.md).
Game integration and performance profiling remain in progress; no game support is
claimed yet. See [build instructions and hardware results](docs/windows-rdna4-port.md). claimed yet. See [build instructions and hardware results](docs/windows-rdna4-port.md).
See [supported games](SUPPORTED_GAMES.md) for the tested games and DLSS models. See [supported games](SUPPORTED_GAMES.md) for the tested games and DLSS models.
+36 -5
View File
@@ -3,7 +3,8 @@
Branch: `windows-rdna4`. Target: Windows 11 x64, RX 9070 XT, **gfx1201**. Branch: `windows-rdna4`. Target: Windows 11 x64, RX 9070 XT, **gfx1201**.
Priority: correct K, correct M, native Windows, same-frame output, then speed. Priority: correct K, correct M, native Windows, same-frame output, then speed.
This is a work log for an implementation in progress. Standalone DLSS K/M This is a work log for an implementation in progress. Standalone DLSS K/M
works on the real Windows GPU; OptiScaler/game support remains unverified. works on the real Windows GPU, including the patched OptiScaler frontend.
An actual game session remains unverified.
## Milestone and gates ## Milestone and gates
@@ -36,7 +37,7 @@ Do not integrate NGX until the integer PTX workload is stable on the real GPU.
| M6: K layers, full transformer and image validation | PASS: 11 real-weight layers, 44 native launches, four D3D12 frames bit-exact against native CUDA, 81.85–92.91 dB versus translated K | | M6: K layers, full transformer and image validation | PASS: 11 real-weight layers, 44 native launches, four D3D12 frames bit-exact against native CUDA, 81.85–92.91 dB versus translated K |
| M7: M FP16-equivalent baseline and full transformer | PASS: all blocks of 40 native launches exactly match NumPy; targeted independent PTX regression matches; four D3D12 frames bit-exact against corrected translated M | | M7: M FP16-equivalent baseline and full transformer | PASS: all blocks of 40 native launches exactly match NumPy; targeted independent PTX regression matches; four D3D12 frames bit-exact against corrected translated M |
| M8: standalone Windows D3D12 NGX harness | PASS for ordinary recorded EvaluateFeature and explicit boundaries: K/M, four frames each, no shim CPU image copies; both bit-exact against explicit boundary baselines | | M8: standalone Windows D3D12 NGX harness | PASS for ordinary recorded EvaluateFeature and explicit boundaries: K/M, four frames each, no shim CPU image copies; both bit-exact against explicit boundary baselines |
| M9: OptiScaler integration, profiling, installation | Pending | | M9: OptiScaler integration, profiling, installation | Patched frontend K/M PASS; game, profiling and final package pending |
## Public NGX identity and OptiScaler gate (2026-10-01) ## Public NGX identity and OptiScaler gate (2026-10-01)
@@ -70,13 +71,43 @@ A local OptiScaler source fork starts from
`45a2001303ddff632e279f77aef85ceede5832cb`. Its explicit `45a2001303ddff632e279f77aef85ceede5832cb`. Its explicit
`[DLSS] AllowExternalBackend=true` option requires an absolute NvngxPath with `[DLSS] AllowExternalBackend=true` option requires an absolute NvngxPath with
the `d4r_WindowsBackendVersion` ABI-1 marker; it does not spoof HIP architecture. the `d4r_WindowsBackendVersion` ABI-1 marker; it does not spoof HIP architecture.
The marker is GPU-free and safe during discovery. The pending patch also lets The marker is GPU-free and safe during discovery. The patch lets the external
the external backend load its own NVAPI provider through the original loader. backend load its own NVAPI provider through the original loader and preserves
This source integration has not yet passed the frontend harness. the exact locally supplied CUDA NGX core path from frontend redirection.
Physical adapter capability is queried outside DLL startup instead of using
NVIDIA architecture as a proxy. GetFeatureRequirements spoofing is bypassed.
Explicit external DLSS selection returns errors instead of silently using FSR.
Microsoft Build Tools 17.14 / MSVC 14.44.35207 and SDK 10.0.26100 were installed Microsoft Build Tools 17.14 / MSVC 14.44.35207 and SDK 10.0.26100 were installed
from the signature-verified official bootstrapper without restarting Windows. from the signature-verified official bootstrapper without restarting Windows.
No game files have been modified. No game files have been modified.
The OptiScaler fork commit is `33bbac2` on `windows-rdna4-d4r`. Its reproducible
source patch and build instructions are in [patches/optiscaler](../patches/optiscaler/README.md).
`build-optiscaler-windows.ps1` produces a DLL, PDB, GPL license and dependency
metadata from pinned source/submodules; it disables upstream's unrelated
packaging post-build commands. The public D3D12 probe now supplies and submits
a real open creation command list, as required by OptiScaler's API.
`optiscaler-k-final.zip` and `optiscaler-m-final.zip` pass four complete
OptiScaler -> Windows d4r -> CUDA NGX -> ZLUDA/HIP evaluations on RX 9070 XT.
They contain 44 K and 40 M native transformer launches, respectively. Every
RGB value exactly matches the independently quantised direct-d4r baseline;
max absolute/relative error is 0. No FSR evaluation, NaN/Inf, shim CPU image copy
or previous-frame output is observed. The invalid-core regression intentionally
passes the shim as the CUDA core: it fails with an explicit frontend
`refusing FSR fallback` message and zero FSR evaluations, as required.
The loader crash was localized with a PDB to Util::IsSubpath calling
filesystem::relative/weakly_canonical from LdrLoadDll interception. The patched
check is lexical, case-insensitive and handles empty relative paths without
filesystem I/O. The separate `d4r_debug_launcher.exe`, enabled by
`-CaptureExceptions`, captures minidumps, first/second-chance exception codes,
module load/unload addresses and raw stack slots outside the hooked process.
It propagates the child's exit code and terminates its child on runner timeout
via a process lifetime job. `optiscaler-m-source-debug2.zip` contains the
successful dump capture and `optiscaler-source-stack-symbols.log` the source
attribution. The ordinary in-process crash filter remains available too.
## Initial PSO and enhanced texture layouts (2026-10-01) ## Initial PSO and enhanced texture layouts (2026-10-01)
The backend now captures the initial PSO supplied to public CreateCommandList, The backend now captures the initial PSO supplied to public CreateCommandList,
@@ -0,0 +1,328 @@
From 33bbac250ac7e2ca4eff67e8817ada9dad578208 Mon Sep 17 00:00:00 2001
From: Codex <codex@localhost>
Date: Thu, 1 Oct 2026 16:10:08 +0300
Subject: [PATCH] Support explicitly selected Windows d4r DLSS backend on
physical AMD adapters
---
OptiScaler/Config.cpp | 3 ++
OptiScaler/Config.h | 1 +
OptiScaler/OptiScaler.vcxproj | 4 +--
OptiScaler/State.h | 1 +
OptiScaler/Util.cpp | 12 +++++--
OptiScaler/dllmain.cpp | 33 +++++++++++++++++--
OptiScaler/hooks/LibraryLoad_Hooks.cpp | 17 ++++++++++
OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp | 17 ++++++++++
OptiScaler/misc/IdentifyGpu.cpp | 20 +++++++++++
OptiScaler/proxies/NVNGX_Proxy.h | 5 ++-
OptiScaler/upscalers/FeatureProvider_Dx12.cpp | 10 ++++++
11 files changed, 115 insertions(+), 8 deletions(-)
diff --git a/OptiScaler/Config.cpp b/OptiScaler/Config.cpp
index d8c62d3..f444387 100644
--- a/OptiScaler/Config.cpp
+++ b/OptiScaler/Config.cpp
@@ -323,6 +323,7 @@ bool Config::Reload(std::filesystem::path iniPath)
{
// Don't enable again if set false because of no nvngx found
DLSSEnabled.set_from_config(readBool("DLSS", "Enabled"));
+ DLSSAllowExternalBackend.set_from_config(readBool("DLSS", "AllowExternalBackend"));
UseGenericAppIdWithDlss.set_from_config(readBool("DLSS", "UseGenericAppIdWithDlss"));
RenderPresetOverride.set_from_config(readBool("DLSS", "RenderPresetOverride"));
@@ -1168,6 +1169,8 @@ bool Config::SaveIni()
// DLSS
{
ini.SetValue("DLSS", "Enabled", GetBoolValue(Instance()->DLSSEnabled.value_for_config()).c_str());
+ ini.SetValue("DLSS", "AllowExternalBackend",
+ GetBoolValue(Instance()->DLSSAllowExternalBackend.value_for_config()).c_str());
ini.SetValue("DLSS", "RenderPresetOverride",
GetBoolValue(Instance()->RenderPresetOverride.value_for_config()).c_str());
ini.SetValue("DLSS", "RenderPresetForAll",
diff --git a/OptiScaler/Config.h b/OptiScaler/Config.h
index 4ab62a4..0caf8c8 100644
--- a/OptiScaler/Config.h
+++ b/OptiScaler/Config.h
@@ -275,6 +275,7 @@ class Config
// DLSS
CustomOptional<bool> DLSSEnabled { true };
+ CustomOptional<bool> DLSSAllowExternalBackend { false };
CustomOptional<bool> RenderPresetOverride { false };
CustomOptional<uint32_t> RenderPresetForAll { 0 };
CustomOptional<uint32_t> RenderPresetDLAA { 0 };
diff --git a/OptiScaler/OptiScaler.vcxproj b/OptiScaler/OptiScaler.vcxproj
index 24ca80b..dfbfc97 100644
--- a/OptiScaler/OptiScaler.vcxproj
+++ b/OptiScaler/OptiScaler.vcxproj
@@ -236,7 +236,7 @@
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
- <GenerateDebugInformation>false</GenerateDebugInformation>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<AdditionalDependencies>winhttp.lib;WindowsApp.lib;Dbghelp.lib;dxgi.lib;d3d11.lib;d3d12.lib;vulkan-1.lib;dxguid.lib;freetype.lib;ffx_fsr2_api_x64.lib;ffx_fsr2_api_dx11_x64.lib;ffx_fsr2_api_dx12_x64.lib;ffx_fsr2_api_vk_x64.lib;ffx_fsr2_212_api_dx12_x64.lib;ffx_fsr2_212_api_vk_x64.lib;ffx_fsr2_212_api_x64.lib;d3dcompiler.lib;ffx_backend_dx11_x64.lib;ffx_fsr3_x64.lib;ffx_frameinterpolation_x64.lib;ffx_fsr3upscaler_x64.lib;ffx_opticalflow_x64.lib;%(AdditionalDependencies)</AdditionalDependencies>
<ModuleDefinitionFile>Source.def</ModuleDefinitionFile>
@@ -873,4 +873,4 @@ copy NUL "$(SolutionDir)x64\Release\a\!! EXTRACT ALL FILES TO GAME FOLDER !!" /Y
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
-</Project>
\ No newline at end of file
+</Project>
diff --git a/OptiScaler/State.h b/OptiScaler/State.h
index 42164de..84f8263 100644
--- a/OptiScaler/State.h
+++ b/OptiScaler/State.h
@@ -265,6 +265,7 @@ class State
int dlssgDetectedInterpolationCount = 0;
// DLSS
+ bool dlssExternalBackend = false;
bool dlssPresetsOverriddenExternally = false;
bool dlssPresetsOverridenByOpti = false;
uint32_t dlssRenderPresetExternal = 0;
diff --git a/OptiScaler/Util.cpp b/OptiScaler/Util.cpp
index f7ac6fa..7367a91 100644
--- a/OptiScaler/Util.cpp
+++ b/OptiScaler/Util.cpp
@@ -486,8 +486,16 @@ bool Util::GetFileVersion(std::wstring dllPath, version_t* fileVersionOut, versi
bool Util::IsSubpath(const std::filesystem::path& path, const std::filesystem::path& base)
{
- auto rel = std::filesystem::relative(path, base);
- auto first = *rel.begin();
+ // Called by LdrLoadDll hooks: relative()/weakly_canonical() perform file
+ // queries, can re-enter the hooks, and throw when an optional directory or
+ // a bare DLL search name cannot be resolved. This is a lexical routing
+ // check, not a filesystem access control decision.
+ auto normalizedPath = std::filesystem::path(ToLower(path.lexically_normal().wstring()));
+ auto normalizedBase = std::filesystem::path(ToLower(base.lexically_normal().wstring()));
+ auto rel = normalizedPath.lexically_relative(normalizedBase);
+ if (rel.empty())
+ return false;
+ const auto first = *rel.begin();
return first != "." && first != "..";
}
diff --git a/OptiScaler/dllmain.cpp b/OptiScaler/dllmain.cpp
index cba14b1..b4b786e 100644
--- a/OptiScaler/dllmain.cpp
+++ b/OptiScaler/dllmain.cpp
@@ -948,7 +948,7 @@ static void CheckWorkingMode()
// NVAPI
// Doesn't seem to like GetModuleHandle for some reason, so call our load to make sure
- if (GetDllNameWModule(&nvapiNamesW) != nullptr)
+ if (!State::Instance().dlssExternalBackend && GetDllNameWModule(&nvapiNamesW) != nullptr)
{
// This hooks nvapi as well when possible
auto nvapi64 = LibraryLoadHooks::LoadNvApi();
@@ -1958,7 +1958,33 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv
return nvidiaDetected;
};
- bool possibleNvidia = isNvidiaViaNvapi();
+ // Opt in only to a configured, versioned Windows d4r backend. The
+ // marker performs no HIP/NGX initialization under DLL_PROCESS_ATTACH.
+ // GPU support is still checked by its public NGX requirements API.
+ if (Config::Instance()->DLSSEnabled.value_or_default() &&
+ Config::Instance()->DLSSAllowExternalBackend.value_or_default() &&
+ Config::Instance()->NvngxPath.has_value())
+ {
+ auto backendPath = std::filesystem::path(Config::Instance()->NvngxPath.value()) / L"_nvngx.dll";
+ if (backendPath.is_absolute())
+ {
+ auto module = NtdllProxy::LoadLibraryExW_Ldr(backendPath.c_str(), nullptr,
+ LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
+ if (module)
+ {
+ using BackendVersion = unsigned (*)();
+ auto version = reinterpret_cast<BackendVersion>(
+ KernelBaseProxy::GetProcAddress_()(module, "d4r_WindowsBackendVersion"));
+ State::Instance().dlssExternalBackend = version && version() == 1;
+ NtdllProxy::FreeLibrary_Ldr(module);
+ }
+ }
+ if (State::Instance().dlssExternalBackend)
+ spdlog::info("Validated external Windows d4r DLSS backend; retaining physical GPU identity");
+ else
+ spdlog::error("AllowExternalBackend requires an absolute NvngxPath containing Windows d4r ABI 1");
+ }
+ bool possibleNvidia = !State::Instance().dlssExternalBackend && isNvidiaViaNvapi();
spdlog::info("");
spdlog::info("Check for DLSS files");
@@ -1992,7 +2018,8 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv
State::Instance().NVNGX_DLSSG_Path = Util::FindFilePath(exePath, "nvngx_dlssg.dll");
// Not 100% accurate for Nvidia cards without DLSS
- if (Config::Instance()->DLSSEnabled.value_or_default() && possibleNvidia)
+ if (Config::Instance()->DLSSEnabled.value_or_default() &&
+ (possibleNvidia || State::Instance().dlssExternalBackend))
{
if (State::Instance().NVNGX_DLSS_Path.has_value())
{
diff --git a/OptiScaler/hooks/LibraryLoad_Hooks.cpp b/OptiScaler/hooks/LibraryLoad_Hooks.cpp
index 90bdb89..108ea5b 100644
--- a/OptiScaler/hooks/LibraryLoad_Hooks.cpp
+++ b/OptiScaler/hooks/LibraryLoad_Hooks.cpp
@@ -46,6 +46,18 @@ HMODULE LibraryLoadHooks::LoadLibraryCheckW(std::wstring libName, LPCWSTR lpLibF
{
auto libNameA = wstring_to_string(libName);
+ // The external D3D12 shim itself uses a locally supplied CUDA NGX core.
+ // Keep that exact absolute path distinct from game-facing NGX interception.
+ if (State::Instance().dlssExternalBackend)
+ {
+ wchar_t corePath[32768] {};
+ const auto length = GetEnvironmentVariableW(L"D4R_NGX_CORE", corePath, 32768);
+ if (length && length < 32768 &&
+ Util::ToLower(std::filesystem::path(libName).lexically_normal().wstring()) ==
+ Util::ToLower(std::filesystem::path(corePath).lexically_normal().wstring()))
+ return nullptr;
+ }
+
#ifdef LOG_LIB_OPERATIONS
LOG_TRACE("{}", libNameA);
#endif
@@ -132,6 +144,11 @@ HMODULE LibraryLoadHooks::LoadLibraryCheckW(std::wstring libName, LPCWSTR lpLibF
// NvApi64.dll
if (CheckDllNameW(&libName, &nvapiNamesW))
{
+ // An explicit external CUDA backend owns its NVAPI topology provider.
+ // Returning OptiScaler's fake-NVAPI would replace that provider and can
+ // recurse into DXGI while the NGX loader holds the loader lock.
+ if (State::Instance().dlssExternalBackend)
+ return nullptr; // Continue through the original loader.
LOG_INFO("{} call!", libNameA);
return LibraryLoadHooks::LoadNvApi();
diff --git a/OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp b/OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp
index 68e1b42..89c9ba8 100644
--- a/OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp
+++ b/OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp
@@ -687,6 +687,7 @@ static NVSDK_NGX_Result TryCreateOptiFeature(ID3D12GraphicsCommandList* InCmdLis
}
// Assign handle
+ const bool allocatedHandle = *OutHandle == nullptr;
if (*OutHandle == nullptr)
*OutHandle = new NVSDK_NGX_Handle { handleId };
else
@@ -705,6 +706,16 @@ static NVSDK_NGX_Result TryCreateOptiFeature(ID3D12GraphicsCommandList* InCmdLis
}
else
{
+ if (state.dlssExternalBackend && upscalerBackend == Upscaler::DLSS)
+ {
+ LOG_ERROR("External DLSS initialization failed; refusing FSR fallback");
+ if (shouldRestoreSigs)
+ D3D12Hooks::RestoreRoot(InCmdList);
+ D3D12Hooks::SetRootSignatureTracking(true);
+ Dx12Contexts.erase(handleId);
+ if (allocatedHandle) { delete *OutHandle; *OutHandle = nullptr; }
+ return NVSDK_NGX_Result_Fail;
+ }
LOG_ERROR("Feature '{}' initialization failed falling back to FSR 2.1.2", UpscalerDisplayName(upscalerBackend));
state.newBackend = Upscaler::FSR21;
state.changeBackend[handleId] = true;
@@ -1027,6 +1038,12 @@ static NVSDK_NGX_Result TryEvaluateOptiFeature(ID3D12GraphicsCommandList* InCmdL
// Fallback to FSR 2.1.2 if feature failed to initialize and user didn't explicitly request it
if (!feature->IsInited() && cfg.Dx12Upscaler.value_or_default() != Upscaler::FSR21)
{
+ if (state.dlssExternalBackend && cfg.Dx12Upscaler.value_or_default() == Upscaler::DLSS)
+ {
+ LOG_ERROR("External DLSS feature is not initialized; refusing FSR fallback");
+ D3D12Hooks::SetRootSignatureTracking(true);
+ return NVSDK_NGX_Result_Fail;
+ }
LOG_WARN("Feature '{}' failed to initialize. Falling back to FSR 2.1.2", feature->Name());
ImGui::InsertNotification({ ImGuiToastType::Warning, 10000, "Falling back to FSR 2.1.2" });
diff --git a/OptiScaler/misc/IdentifyGpu.cpp b/OptiScaler/misc/IdentifyGpu.cpp
index 3877f9f..6c05904 100644
--- a/OptiScaler/misc/IdentifyGpu.cpp
+++ b/OptiScaler/misc/IdentifyGpu.cpp
@@ -8,6 +8,7 @@
#include <proxies/D3d12_Proxy.h>
#include "nvapi/NvApiTypes.h"
#include <hooks/Amdxc64_Hooks.h>
+#include <proxies/NVNGX_Proxy.h>
using Microsoft::WRL::ComPtr;
@@ -187,6 +188,25 @@ std::vector<GpuInformation> IdentifyGpu::checkGpuInfo()
for (auto& gpuInfo : localCachedInfo)
{
+ if (State::Instance().dlssExternalBackend && !gpuInfo.softwareAdapter)
+ {
+ // Query the configured D3D12 backend outside DLL_PROCESS_ATTACH.
+ // Preserve vendor/device/architecture data obtained from DXGI.
+ auto requirements = NVNGXProxy::D3D12_GetFeatureRequirements();
+ ComPtr<IDXGIAdapter1> physicalAdapter;
+ if (requirements && SUCCEEDED(factory->EnumAdapterByLuid(gpuInfo.luid, IID_PPV_ARGS(&physicalAdapter))))
+ {
+ NVSDK_NGX_FeatureDiscoveryInfo discovery {};
+ discovery.SDKVersion = NVSDK_NGX_Version_API;
+ discovery.FeatureID = NVSDK_NGX_Feature_SuperSampling;
+ NVSDK_NGX_FeatureRequirement supported {};
+ const auto result = requirements(physicalAdapter.Get(), &discovery, &supported);
+ gpuInfo.dlssCapable = result == NVSDK_NGX_Result_Success &&
+ supported.FeatureSupported == NVSDK_NGX_FeatureSupportResult_Supported;
+ LOG_INFO("External D3D12 DLSS backend: {} support={} result={:X}",
+ gpuInfo.name, gpuInfo.dlssCapable, (unsigned) result);
+ }
+ }
if (gpuInfo.vendorId == VendorId::Nvidia)
{
queryNvapi(gpuInfo);
diff --git a/OptiScaler/proxies/NVNGX_Proxy.h b/OptiScaler/proxies/NVNGX_Proxy.h
index f6c08e5..7892009 100644
--- a/OptiScaler/proxies/NVNGX_Proxy.h
+++ b/OptiScaler/proxies/NVNGX_Proxy.h
@@ -500,7 +500,10 @@ class NVNGXProxy
{
LOG_INFO("Getting nvngx method addresses");
- HookNgxApi(_module.dll);
+ // The external bridge reports physical adapter support itself.
+ // Do not overwrite its unsupported result with NVIDIA spoofing.
+ if (!State::Instance().dlssExternalBackend)
+ HookNgxApi(_module.dll);
_module.D3D11_Init =
(PFN_D3D11_Init) KernelBaseProxy::GetProcAddress_()(_module.dll, "NVSDK_NGX_D3D11_Init");
diff --git a/OptiScaler/upscalers/FeatureProvider_Dx12.cpp b/OptiScaler/upscalers/FeatureProvider_Dx12.cpp
index d738498..1191b29 100644
--- a/OptiScaler/upscalers/FeatureProvider_Dx12.cpp
+++ b/OptiScaler/upscalers/FeatureProvider_Dx12.cpp
@@ -42,6 +42,11 @@ bool FeatureProvider_Dx12::GetFeature(Upscaler upscaler, UINT handleId, NVSDK_NG
break;
case Upscaler::DLSS:
+ if (state.dlssExternalBackend && (!primaryGpu.dlssCapable || !state.NVNGX_DLSS_Path.has_value()))
+ {
+ LOG_ERROR("Configured external D3D12 DLSS backend is unavailable; refusing FSR fallback");
+ return false;
+ }
if (primaryGpu.dlssCapable && state.NVNGX_DLSS_Path.has_value())
{
*feature = std::make_unique<DLSSFeatureDx12>(handleId, parameters);
@@ -77,6 +82,11 @@ bool FeatureProvider_Dx12::GetFeature(Upscaler upscaler, UINT handleId, NVSDK_NG
if (!loaded)
{
+ if (state.dlssExternalBackend && upscaler == Upscaler::DLSS)
+ {
+ LOG_ERROR("External D3D12 DLSS module failed to load; refusing FSR fallback");
+ return false;
+ }
// Fail after the constructor
ImGui::InsertNotification({ ImGuiToastType::Warning, 10000, "Falling back to FSR 2.1.2" });
*feature = std::make_unique<FSR2FeatureDx12_212>(handleId, parameters);
--
2.55.0.windows.5
+40
View File
@@ -0,0 +1,40 @@
# Explicit Windows d4r backend
Base: OptiScaler `45a2001303ddff632e279f77aef85ceede5832cb` (2026-09-30).
The modifications to OptiScaler are GPL-3.0, matching its upstream license.
No NVIDIA DLL is supplied or patched.
`0001` introduces `[DLSS] AllowExternalBackend=true`, default false. Set
`[Libraries] NvngxPath` to the absolute directory containing the Windows d4r
shim as `_nvngx.dll`. Discovery requires its GPU-free ABI-1 marker. Physical
adapter support is queried through the bridge's D3D12 requirements API after
DLL startup; vendor, LUID and HIP architecture are retained.
The configured `D4R_NGX_CORE` absolute path bypasses game-facing NGX DLL
redirection. The external backend loads its own NVAPI provider. An explicitly
selected external DLSS backend reports initialization/load errors instead of
silently switching to FSR. The patch fixes the upstream loader's filesystem
exception by making IsSubpath a lexical check, and retains a PDB for diagnosis.
Build with Microsoft Build Tools 2022, v143 and Windows SDK 10.0.26100:
```powershell
.\scripts\windows\build-optiscaler-windows.ps1
```
The script clones the pinned source if absent, checks/applies the patch,
initializes pinned submodules, invokes MSBuild and writes the DLL, PDB, license
and build metadata into `dist/optiscaler-windows-d4r`. Existing source is not
reset. The upstream packaging post-build event is disabled for this build.
The official bootstrapper is https://aka.ms/vs/17/release/vs_buildtools.exe;
install the `Microsoft.VisualStudio.Workload.VCTools`,
`Microsoft.VisualStudio.Component.VC.Tools.x86.x64` and
`Microsoft.VisualStudio.Component.Windows11SDK.26100` components. The audited
local build uses MSVC 14.44.35207 / Build Tools 17.14.
For standalone verification add
`-OptiScalerDll <absolute-dist-path>/OptiScaler.dll -CaptureExceptions`
to the documented K/M runner. Its private INI selects DLSS, disables frame
generation and keeps RestoreComputeSignature, RestoreGraphicSignature and
ExtendedStateRestore false. Use `-RequireNativeNetwork`; finite output alone
cannot distinguish DLSS from an unwanted FSR replacement.
@@ -0,0 +1,75 @@
[CmdletBinding()]
param(
[string]$SourceRoot,
[string]$BuildToolsRoot,
[string]$InstallRoot,
[int]$Jobs = 6
)
$ErrorActionPreference = 'Stop'
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
if (!$SourceRoot) { $SourceRoot = Join-Path $repo 'external/OptiScaler' }
if (!$InstallRoot) { $InstallRoot = Join-Path $repo 'dist/optiscaler-windows-d4r' }
$SourceRoot = [IO.Path]::GetFullPath($SourceRoot)
$InstallRoot = [IO.Path]::GetFullPath($InstallRoot)
$base = '45a2001303ddff632e279f77aef85ceede5832cb'
$patch = Join-Path $repo 'patches/optiscaler/0001-windows-external-d4r-backend.patch'
$previousPath = $env:PATH
try {
# Git for Windows' submodule helper needs its POSIX utility directory.
$gitExe = (Get-Command git -ErrorAction Stop).Source
$gitInstall = [IO.Path]::GetFullPath((Join-Path (Split-Path $gitExe) '..'))
$gitUtilities = Join-Path $gitInstall 'usr/bin'
if (Test-Path -LiteralPath $gitUtilities) { $env:PATH = "$gitUtilities;$env:PATH" }
if (!(Test-Path -LiteralPath (Join-Path $SourceRoot '.git'))) {
& git clone --filter=blob:none --no-checkout https://github.com/optiscaler/OptiScaler.git $SourceRoot
if ($LASTEXITCODE) { throw 'OptiScaler clone failed.' }
& git -C $SourceRoot checkout --detach $base
if ($LASTEXITCODE) { throw 'Pinned OptiScaler checkout failed.' }
}
& git -C $SourceRoot merge-base --is-ancestor $base HEAD
if ($LASTEXITCODE) { throw "OptiScaler must be based on audited commit $base" }
$ErrorActionPreference = 'Continue'
& git -C $SourceRoot apply --reverse --check $patch 2>$null
$alreadyApplied = $LASTEXITCODE -eq 0
$ErrorActionPreference = 'Stop'
if (!$alreadyApplied) {
& git -C $SourceRoot apply --check $patch
if ($LASTEXITCODE) { throw 'External backend patch conflicts with the existing source; no files changed.' }
& git -C $SourceRoot apply $patch
if ($LASTEXITCODE) { throw 'External backend patch failed.' }
}
& git -C $SourceRoot submodule update --init --depth 1
if ($LASTEXITCODE) { throw 'Pinned OptiScaler submodules failed.' }
if (!$BuildToolsRoot -and (Test-Path -LiteralPath (Join-Path $repo '.tools/vs2022/MSBuild/Current/Bin/MSBuild.exe'))) {
$BuildToolsRoot = Join-Path $repo '.tools/vs2022'
}
if (!$BuildToolsRoot) {
$vswhere = Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio/Installer/vswhere.exe'
if (Test-Path -LiteralPath $vswhere) {
$BuildToolsRoot = & $vswhere -latest -products '*' -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath
}
}
if (!$BuildToolsRoot) { throw 'MSVC v143 Build Tools and Windows SDK 10.0.26100 are required; see patches/optiscaler/README.md.' }
$msbuild = Join-Path $BuildToolsRoot 'MSBuild/Current/Bin/MSBuild.exe'
if (!(Test-Path -LiteralPath $msbuild)) { throw "MSBuild missing: $msbuild" }
New-Item -ItemType Directory -Force $InstallRoot | Out-Null
$log = Join-Path $InstallRoot 'build.log'
$ErrorActionPreference = 'Continue'
& $msbuild (Join-Path $SourceRoot 'OptiScaler.sln') "/m:$Jobs" /p:Configuration=Release /p:Platform=x64 `
/p:PostBuildEventUseInBuild=false /verbosity:quiet "/flp:logfile=$log;verbosity=normal"
$ErrorActionPreference = 'Stop'
if ($LASTEXITCODE) { throw "OptiScaler build failed ($LASTEXITCODE); see $log" }
foreach ($file in @('OptiScaler.dll', 'OptiScaler.pdb')) {
Copy-Item -LiteralPath (Join-Path $SourceRoot "x64/Release/$file") -Destination $InstallRoot -Force
}
Copy-Item -LiteralPath (Join-Path $SourceRoot 'LICENSE') -Destination (Join-Path $InstallRoot 'GPL-3.0.txt') -Force
Copy-Item -LiteralPath $patch -Destination $InstallRoot -Force
$metadata = @{base=$base; sourceCommit=(& git -C $SourceRoot rev-parse HEAD).Trim();
sourceDirty=[bool](& git -C $SourceRoot status --porcelain); backendAbi=1;
buildTools=$BuildToolsRoot; msbuildVersion=(& $msbuild -version -nologo | Select-Object -Last 1);
patchSha256=(Get-FileHash -LiteralPath $patch -Algorithm SHA256).Hash;
dllSha256=(Get-FileHash -LiteralPath (Join-Path $InstallRoot 'OptiScaler.dll') -Algorithm SHA256).Hash;
submodules=@(& git -C $SourceRoot submodule status)}
$metadata | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $InstallRoot 'build-info.json') -Encoding UTF8
Write-Host "Built OptiScaler with external Windows d4r backend: $InstallRoot"
} finally { $env:PATH = $previousPath }
+6
View File
@@ -15,6 +15,7 @@ param(
[switch]$Trace, [switch]$Trace,
[switch]$RequireNativeNetwork, [switch]$RequireNativeNetwork,
[switch]$CommandListBackend, [switch]$CommandListBackend,
[switch]$CaptureExceptions,
[ValidateSet('baseline','packed','unorm')][string]$PixelProfile = 'baseline', [ValidateSet('baseline','packed','unorm')][string]$PixelProfile = 'baseline',
[ValidateSet('legacy','enhanced')][string]$BarrierMode = 'legacy', [ValidateSet('legacy','enhanced')][string]$BarrierMode = 'legacy',
[int]$Iterations = 32, [int]$Iterations = 32,
@@ -71,6 +72,11 @@ function Quote-Argument([string]$Value) {
} }
function Invoke-Probe([string]$Name, [string]$Exe, [string[]]$Arguments) { function Invoke-Probe([string]$Name, [string]$Exe, [string[]]$Arguments) {
if (!(Test-Path -LiteralPath $Exe)) { throw "Executable missing: $Exe" } if (!(Test-Path -LiteralPath $Exe)) { throw "Executable missing: $Exe" }
if ($CaptureExceptions) {
$Arguments = @('--output-directory', $OutputDirectory, '--', $Exe) + $Arguments
$Exe = Join-Path $PackageRoot 'bin/d4r_debug_launcher.exe'
if (!(Test-Path -LiteralPath $Exe)) { throw "Debugger launcher missing: $Exe" }
}
$stdout = Join-Path $OutputDirectory "$Name.stdout.log" $stdout = Join-Path $OutputDirectory "$Name.stdout.log"
$stderr = Join-Path $OutputDirectory "$Name.stderr.log" $stderr = Join-Path $OutputDirectory "$Name.stderr.log"
$argumentLine = ($Arguments | ForEach-Object { Quote-Argument $_ }) -join ' ' $argumentLine = ($Arguments | ForEach-Object { Quote-Argument $_ }) -join ' '
+10 -1
View File
@@ -193,7 +193,16 @@ int main(int argc, char** argv) {
for (const char* name : {"DLSS.Hint.Render.Preset.DLAA", "DLSS.Hint.Render.Preset.Quality", "DLSS.Hint.Render.Preset.Balanced", 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); "DLSS.Hint.Render.Preset.Performance", "DLSS.Hint.Render.Preset.UltraPerformance", "DLSS.Hint.Render.Preset.UltraQuality"}) d4r_ngx_set_uint(parameters, name, args.preset);
void* handle = nullptr; void* handle = nullptr;
check(shim.symbol<Create>("NVSDK_NGX_D3D12_CreateFeature")(nullptr, 1, parameters, &handle), "D3D12 CreateFeature"); // Public NGX and OptiScaler require an open creation command list.
// Submit it independently from the pending synthetic input producer.
ComPtr<ID3D12CommandAllocator> creationAllocator;
ComPtr<ID3D12GraphicsCommandList> creationList;
dx(device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(creationAllocator.GetAddressOf())), "Create feature allocator");
dx(device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, creationAllocator.Get(), nullptr,
IID_PPV_ARGS(creationList.GetAddressOf())), "Create feature command list");
check(shim.symbol<Create>("NVSDK_NGX_D3D12_CreateFeature")(creationList.Get(), 1, parameters, &handle), "D3D12 CreateFeature");
dx(creationList->Close(), "Close feature creation list");
ID3D12CommandList* creationBatch[] = {creationList.Get()}; queue->ExecuteCommandLists(1, creationBatch); drain();
auto release = shim.symbol<Release>("NVSDK_NGX_D3D12_ReleaseFeature"); auto release = shim.symbol<Release>("NVSDK_NGX_D3D12_ReleaseFeature");
struct FeatureCleanup { Release fn; void* h; ~FeatureCleanup() { (void)fn(h); } } featureCleanup{release, handle}; struct FeatureCleanup { Release fn; void* h; ~FeatureCleanup() { (void)fn(h); } } featureCleanup{release, handle};
static const char* names[] = {"Color", "Depth", "MotionVectors", "Output", "ExposureTexture"}; static const char* names[] = {"Color", "Depth", "MotionVectors", "Output", "ExposureTexture"};
+130
View File
@@ -0,0 +1,130 @@
// Out-of-process crash capture: unaffected by the frontend's loader hooks.
#include "diagnostic.h"
#include <map>
#include <fstream>
namespace {
std::wstring quote(const std::wstring& value) {
std::wstring result = L"\""; unsigned slashes = 0;
for (wchar_t c : value) {
if (c == L'\\') { ++slashes; continue; }
result.append(slashes * (c == L'\"' ? 2 : 1), L'\\'); slashes = 0;
if (c == L'\"') result += L'\\';
result += c;
}
result.append(slashes * 2, L'\\'); return result + L'\"';
}
std::string file_path(HANDLE file) {
wchar_t path[32768]{};
const auto length = GetFinalPathNameByHandleW(file, path, 32768, FILE_NAME_NORMALIZED);
return length && length < 32768 ? d4r::diag::utf8(path) : "unknown";
}
struct Module { std::string path; uint32_t size; };
uint32_t module_size(HANDLE process, uintptr_t base) {
IMAGE_DOS_HEADER dos{}; IMAGE_NT_HEADERS64 nt{}; SIZE_T read = 0;
if (!ReadProcessMemory(process, reinterpret_cast<void*>(base), &dos, sizeof(dos), &read) ||
dos.e_magic != IMAGE_DOS_SIGNATURE || dos.e_lfanew < 0 || dos.e_lfanew > 65536 ||
!ReadProcessMemory(process, reinterpret_cast<void*>(base + dos.e_lfanew), &nt, sizeof(nt), &read) ||
nt.Signature != IMAGE_NT_SIGNATURE) return 0;
return nt.OptionalHeader.SizeOfImage;
}
}
int main(int argc, char** argv) {
try {
if (argc < 5 || std::strcmp(argv[1], "--output-directory") || std::strcmp(argv[3], "--"))
throw std::runtime_error("Usage: d4r_debug_launcher --output-directory PATH -- EXE [arguments]");
const std::filesystem::path directory(d4r::diag::wide(argv[2]));
std::filesystem::create_directories(directory);
std::ofstream log(directory / L"debugger.log", std::ios::app);
const auto executable = d4r::diag::wide(argv[4]);
std::wstring command;
for (int i = 4; i < argc; ++i) { if (i > 4) command += L' '; command += quote(d4r::diag::wide(argv[i])); }
STARTUPINFOW startup{sizeof(startup)}; PROCESS_INFORMATION child{};
startup.dwFlags = STARTF_USESTDHANDLES;
startup.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
startup.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
startup.hStdError = GetStdHandle(STD_ERROR_HANDLE);
HANDLE job = CreateJobObjectW(nullptr, nullptr);
JOBOBJECT_EXTENDED_LIMIT_INFORMATION limits{};
limits.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
if (!job || !SetInformationJobObject(job, JobObjectExtendedLimitInformation, &limits, sizeof(limits)))
throw std::runtime_error("Cannot create diagnostic process lifetime job");
if (!CreateProcessW(executable.c_str(), command.data(), nullptr, nullptr, TRUE,
DEBUG_ONLY_THIS_PROCESS | CREATE_NO_WINDOW, nullptr, nullptr, &startup, &child))
throw std::runtime_error("CreateProcess debugger error=" + std::to_string(GetLastError()));
if (!AssignProcessToJobObject(job, child.hProcess)) {
TerminateProcess(child.hProcess, 1); CloseHandle(job);
throw std::runtime_error("AssignProcessToJobObject debugger failed");
}
std::map<uintptr_t, Module> modules;
bool initialBreakpoint = true, dumped = false; DWORD exitCode = 1;
auto add_module = [&](void* base, HANDLE file) {
const auto address = reinterpret_cast<uintptr_t>(base);
modules[address] = {file_path(file), module_size(child.hProcess, address)};
log << "LOAD base=0x" << std::hex << address << " size=0x" << modules[address].size << " path=" << modules[address].path << std::endl;
if (file && file != INVALID_HANDLE_VALUE) CloseHandle(file);
};
auto address_info = [&](uintptr_t address) {
auto it = modules.upper_bound(address);
if (it != modules.begin()) {
--it;
if (address - it->first < it->second.size)
log << " module=" << it->second.path << "+0x" << std::hex << address - it->first;
}
};
DEBUG_EVENT event{};
while (WaitForDebugEvent(&event, INFINITE)) {
DWORD disposition = DBG_CONTINUE;
if (event.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT) {
add_module(event.u.CreateProcessInfo.lpBaseOfImage, event.u.CreateProcessInfo.hFile);
CloseHandle(event.u.CreateProcessInfo.hProcess); CloseHandle(event.u.CreateProcessInfo.hThread);
} else if (event.dwDebugEventCode == CREATE_THREAD_DEBUG_EVENT) CloseHandle(event.u.CreateThread.hThread);
else if (event.dwDebugEventCode == LOAD_DLL_DEBUG_EVENT) add_module(event.u.LoadDll.lpBaseOfDll, event.u.LoadDll.hFile);
else if (event.dwDebugEventCode == UNLOAD_DLL_DEBUG_EVENT) {
const auto base = reinterpret_cast<uintptr_t>(event.u.UnloadDll.lpBaseOfDll);
log << "UNLOAD base=0x" << std::hex << base << std::endl; modules.erase(base);
} else if (event.dwDebugEventCode == EXCEPTION_DEBUG_EVENT) {
const auto& exception = event.u.Exception;
const DWORD code = exception.ExceptionRecord.ExceptionCode;
log << "EXCEPTION code=0x" << std::hex << code << " firstChance=" << exception.dwFirstChance
<< " address=0x" << reinterpret_cast<uintptr_t>(exception.ExceptionRecord.ExceptionAddress) << std::endl;
disposition = DBG_EXCEPTION_NOT_HANDLED;
if (initialBreakpoint && code == EXCEPTION_BREAKPOINT) { initialBreakpoint = false; disposition = DBG_CONTINUE; }
if (!dumped && (!exception.dwFirstChance || code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_ILLEGAL_INSTRUCTION || code == EXCEPTION_STACK_OVERFLOW || code == EXCEPTION_IN_PAGE_ERROR)) {
dumped = true;
log << "FAULT code=0x" << std::hex << code << " address=0x" << reinterpret_cast<uintptr_t>(exception.ExceptionRecord.ExceptionAddress)
<< " thread=" << std::dec << event.dwThreadId << " firstChance=" << exception.dwFirstChance;
address_info(reinterpret_cast<uintptr_t>(exception.ExceptionRecord.ExceptionAddress)); log << std::endl;
HANDLE thread = OpenThread(THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION, FALSE, event.dwThreadId);
CONTEXT context{}; context.ContextFlags = CONTEXT_ALL;
if (thread && GetThreadContext(thread, &context)) {
log << "CONTEXT rip=0x" << std::hex << context.Rip << " rsp=0x" << context.Rsp << " rcx=0x" << context.Rcx << " rdx=0x" << context.Rdx << std::endl;
EXCEPTION_RECORD record = exception.ExceptionRecord;
EXCEPTION_POINTERS pointers{&record, &context};
MINIDUMP_EXCEPTION_INFORMATION info{event.dwThreadId, &pointers, FALSE};
const auto path = directory / (L"crash-" + std::to_wstring(child.dwProcessId) + L".dmp");
HANDLE file = CreateFileW(path.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, 0, nullptr);
if (file != INVALID_HANDLE_VALUE) {
const BOOL ok = MiniDumpWriteDump(child.hProcess, child.dwProcessId, file,
static_cast<MINIDUMP_TYPE>(MiniDumpNormal | MiniDumpWithThreadInfo), &info, nullptr, nullptr);
log << "MINIDUMP success=" << ok << " error=" << (ok ? 0 : GetLastError()) << std::endl; CloseHandle(file);
}
uintptr_t stack[256]{}; SIZE_T read = 0;
if (ReadProcessMemory(child.hProcess, reinterpret_cast<void*>(context.Rsp), stack, sizeof(stack), &read))
for (unsigned i = 0; i < read / sizeof(uintptr_t); ++i) {
log << "STACK slot=" << std::dec << i << " address=0x" << std::hex << stack[i]; address_info(stack[i]); log << '\n';
}
}
if (thread) CloseHandle(thread); log.flush();
}
} else if (event.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT) {
exitCode = event.u.ExitProcess.dwExitCode;
log << "EXIT code=0x" << std::hex << exitCode << std::endl;
ContinueDebugEvent(event.dwProcessId, event.dwThreadId, disposition); break;
}
ContinueDebugEvent(event.dwProcessId, event.dwThreadId, disposition);
}
CloseHandle(child.hThread); CloseHandle(child.hProcess); CloseHandle(job);
return static_cast<int>(exitCode);
} catch (const std::exception& error) { std::fprintf(stderr, "DEBUG_LAUNCH_FAILURE %s\n", error.what()); return 1; }
}