From fd2ca42f2892874fabceb1f28fb6ec93660628bf Mon Sep 17 00:00:00 2001 From: Kelsi Date: Mon, 9 Mar 2026 00:54:36 -0700 Subject: [PATCH] Add DX12 factory/device preflight for bridge mode --- README.md | 3 ++ docs/AMD_FSR2_INTEGRATION.md | 3 ++ src/rendering/amd_fsr3_wrapper_impl.cpp | 70 +++++++++++++++++++++++-- 3 files changed, 72 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index dc29e1a1..87a432ca 100644 --- a/README.md +++ b/README.md @@ -213,6 +213,9 @@ make -j$(nproc) - `WOWEE_FSR3_WRAPPER_BACKEND=dx12_bridge` (default on Windows; bridge dispatch wiring still in progress) - DX12 runtime override (for `dx12_bridge`): - `WOWEE_FSR3_DX12_RUNTIME_LIB=C:\\path\\to\\amd_fidelityfx_framegeneration_dx12.dll` + - DX12 device validation probe (default on): + - `WOWEE_FSR3_WRAPPER_DX12_VALIDATE_DEVICE=1` + - Set to `0` to skip adapter/device preflight. - Path B wrapper libraries must export the clean wrapper ABI (`include/rendering/amd_fsr3_wrapper_abi.h`): - `wowee_fsr3_wrapper_get_abi_version` - `wowee_fsr3_wrapper_initialize` diff --git a/docs/AMD_FSR2_INTEGRATION.md b/docs/AMD_FSR2_INTEGRATION.md index 32122f7f..dcc843f8 100644 --- a/docs/AMD_FSR2_INTEGRATION.md +++ b/docs/AMD_FSR2_INTEGRATION.md @@ -57,6 +57,9 @@ Runtime note: - `WOWEE_FSR3_WRAPPER_BACKEND=dx12_bridge` - DX12 bridge runtime override: - `WOWEE_FSR3_DX12_RUNTIME_LIB=` +- DX12 bridge device preflight toggle: + - `WOWEE_FSR3_WRAPPER_DX12_VALIDATE_DEVICE=1` (default) + - `WOWEE_FSR3_WRAPPER_DX12_VALIDATE_DEVICE=0` to skip DXGI/D3D12 device creation probe - Path B wrapper ABI contract is declared in: - `include/rendering/amd_fsr3_wrapper_abi.h` - Required wrapper exports: diff --git a/src/rendering/amd_fsr3_wrapper_impl.cpp b/src/rendering/amd_fsr3_wrapper_impl.cpp index 8f2d8e93..ebd1db50 100644 --- a/src/rendering/amd_fsr3_wrapper_impl.cpp +++ b/src/rendering/amd_fsr3_wrapper_impl.cpp @@ -18,6 +18,8 @@ #if defined(_WIN32) #include +#include +#include #else #include #endif @@ -235,15 +237,28 @@ bool runDx12BridgePreflight(const WoweeFsr3WrapperInitDesc* initDesc, std::strin HMODULE d3d12 = LoadLibraryA("d3d12.dll"); if (!d3d12) { missing.emplace_back("d3d12.dll"); - } else { - FreeLibrary(d3d12); } HMODULE dxgi = LoadLibraryA("dxgi.dll"); if (!dxgi) { missing.emplace_back("dxgi.dll"); - } else { - FreeLibrary(dxgi); + } + + using PFN_D3D12CreateDeviceLocal = HRESULT(WINAPI*)(IUnknown*, D3D_FEATURE_LEVEL, REFIID, void**); + using PFN_CreateDXGIFactory2Local = HRESULT(WINAPI*)(UINT, REFIID, void**); + PFN_D3D12CreateDeviceLocal pD3D12CreateDevice = nullptr; + PFN_CreateDXGIFactory2Local pCreateDXGIFactory2 = nullptr; + if (d3d12) { + pD3D12CreateDevice = reinterpret_cast(GetProcAddress(d3d12, "D3D12CreateDevice")); + if (!pD3D12CreateDevice) { + missing.emplace_back("D3D12CreateDevice"); + } + } + if (dxgi) { + pCreateDXGIFactory2 = reinterpret_cast(GetProcAddress(dxgi, "CreateDXGIFactory2")); + if (!pCreateDXGIFactory2) { + missing.emplace_back("CreateDXGIFactory2"); + } } if (!initDesc || !initDesc->device || !initDesc->getDeviceProcAddr) { @@ -292,6 +307,43 @@ bool runDx12BridgePreflight(const WoweeFsr3WrapperInitDesc* initDesc, std::strin missing.emplace_back("ffxCreateContext/ffxConfigure/ffxDispatch exports"); } + // Optional strict probe: attempt creating a DXGI factory and D3D12 device. + if (missing.empty() && envEnabled("WOWEE_FSR3_WRAPPER_DX12_VALIDATE_DEVICE", true) && + pCreateDXGIFactory2 && pD3D12CreateDevice) { + IDXGIFactory6* factory = nullptr; + HRESULT hrFactory = pCreateDXGIFactory2(0, IID_PPV_ARGS(&factory)); + if (FAILED(hrFactory) || !factory) { + missing.emplace_back("DXGI factory creation"); + } else { + IDXGIAdapter1* adapter = nullptr; + bool hasHardwareAdapter = false; + for (UINT adapterIndex = 0; factory->EnumAdapters1(adapterIndex, &adapter) != DXGI_ERROR_NOT_FOUND; ++adapterIndex) { + DXGI_ADAPTER_DESC1 desc{}; + if (SUCCEEDED(adapter->GetDesc1(&desc)) && !(desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)) { + hasHardwareAdapter = true; + break; + } + adapter->Release(); + adapter = nullptr; + } + if (!hasHardwareAdapter || !adapter) { + missing.emplace_back("hardware DXGI adapter"); + } else { + ID3D12Device* d3d12Device = nullptr; + HRESULT hrDevice = pD3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_12_0, IID_PPV_ARGS(&d3d12Device)); + if (FAILED(hrDevice) || !d3d12Device) { + missing.emplace_back("D3D12 device creation (feature level 12_0)"); + } + if (d3d12Device) d3d12Device->Release(); + adapter->Release(); + } + factory->Release(); + } + } + + if (dxgi) FreeLibrary(dxgi); + if (d3d12) FreeLibrary(d3d12); + if (missing.empty()) return true; std::ostringstream oss; @@ -600,3 +652,13 @@ WOWEE_FSR3_WRAPPER_EXPORT void wowee_fsr3_wrapper_shutdown(WoweeFsr3WrapperConte (void)context; #endif } +#if defined(_WIN32) +bool envEnabled(const char* key, bool defaultValue) { + const char* val = std::getenv(key); + if (!val || !*val) return defaultValue; + std::string s(val); + std::transform(s.begin(), s.end(), s.begin(), + [](unsigned char c) { return static_cast(std::tolower(c)); }); + return !(s == "0" || s == "false" || s == "off" || s == "no"); +} +#endif