From e25253a6e8f8cdb401f35a68cb23e7241b48d953 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Mon, 9 Mar 2026 01:23:04 -0700 Subject: [PATCH] Enable dx12_bridge init path and runtime export compatibility scan --- README.md | 2 +- docs/AMD_FSR2_INTEGRATION.md | 3 +- src/rendering/amd_fsr3_wrapper_impl.cpp | 65 +++++++++++++++---------- 3 files changed, 43 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index d0fffdeb..6ffc53e1 100644 --- a/README.md +++ b/README.md @@ -210,7 +210,7 @@ make -j$(nproc) - Wrapper backend override (what the wrapper loads underneath): `WOWEE_FSR3_WRAPPER_BACKEND_LIB=/absolute/path/to/libffx_fsr3_vk.so`. - Wrapper backend mode: - `WOWEE_FSR3_WRAPPER_BACKEND=vulkan_runtime` (default on all platforms) - - `WOWEE_FSR3_WRAPPER_BACKEND=dx12_bridge` (opt-in; bridge dispatch wiring still in progress) + - `WOWEE_FSR3_WRAPPER_BACKEND=dx12_bridge` (opt-in; runs DX12 preflight and then selects a dispatch-capable runtime export set) - 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): diff --git a/docs/AMD_FSR2_INTEGRATION.md b/docs/AMD_FSR2_INTEGRATION.md index eef256cb..f192c45d 100644 --- a/docs/AMD_FSR2_INTEGRATION.md +++ b/docs/AMD_FSR2_INTEGRATION.md @@ -55,7 +55,8 @@ Runtime note: - Wrapper backend mode selection: - `WOWEE_FSR3_WRAPPER_BACKEND=vulkan_runtime` - `WOWEE_FSR3_WRAPPER_BACKEND=dx12_bridge` -- Default is `vulkan_runtime` on all platforms. `dx12_bridge` is opt-in while Vulkan<->DX12 interop dispatch is still in progress. +- Default is `vulkan_runtime` on all platforms. +- `dx12_bridge` is opt-in and now performs DX12/Vulkan preflight, then loads the first runtime library exposing the required FSR3 dispatch exports. - DX12 bridge runtime override: - `WOWEE_FSR3_DX12_RUNTIME_LIB=` - DX12 bridge device preflight toggle: diff --git a/src/rendering/amd_fsr3_wrapper_impl.cpp b/src/rendering/amd_fsr3_wrapper_impl.cpp index f3498894..85ca9a65 100644 --- a/src/rendering/amd_fsr3_wrapper_impl.cpp +++ b/src/rendering/amd_fsr3_wrapper_impl.cpp @@ -258,6 +258,24 @@ void* resolveSymbol(void* handle, const char* symbol) { #endif } +bool bindVulkanRuntimeFns(void* libHandle, RuntimeFns& outFns) { + outFns = RuntimeFns{}; + outFns.getScratchMemorySizeVK = reinterpret_cast(resolveSymbol(libHandle, "ffxGetScratchMemorySizeVK")); + outFns.getDeviceVK = reinterpret_cast(resolveSymbol(libHandle, "ffxGetDeviceVK")); + outFns.getInterfaceVK = reinterpret_cast(resolveSymbol(libHandle, "ffxGetInterfaceVK")); + outFns.getCommandListVK = reinterpret_cast(resolveSymbol(libHandle, "ffxGetCommandListVK")); + outFns.getResourceVK = reinterpret_cast(resolveSymbol(libHandle, "ffxGetResourceVK")); + outFns.fsr3ContextCreate = reinterpret_cast(resolveSymbol(libHandle, "ffxFsr3ContextCreate")); + outFns.fsr3ContextDispatchUpscale = reinterpret_cast(resolveSymbol(libHandle, "ffxFsr3ContextDispatchUpscale")); + outFns.fsr3ConfigureFrameGeneration = reinterpret_cast(resolveSymbol(libHandle, "ffxFsr3ConfigureFrameGeneration")); + outFns.fsr3DispatchFrameGeneration = reinterpret_cast(resolveSymbol(libHandle, "ffxFsr3DispatchFrameGeneration")); + outFns.fsr3ContextDestroy = reinterpret_cast(resolveSymbol(libHandle, "ffxFsr3ContextDestroy")); + + return outFns.getScratchMemorySizeVK && outFns.getDeviceVK && outFns.getInterfaceVK && + outFns.getCommandListVK && outFns.getResourceVK && outFns.fsr3ContextCreate && + outFns.fsr3ContextDispatchUpscale && outFns.fsr3ContextDestroy; +} + void destroyContext(WrapperContext* ctx) { if (!ctx) return; if (ctx->fsr3ContextStorage && ctx->fns.fsr3ContextDestroy) { @@ -482,9 +500,6 @@ WOWEE_FSR3_WRAPPER_EXPORT int32_t wowee_fsr3_wrapper_initialize(const WoweeFsr3W writeError(outErrorText, outErrorTextCapacity, preflightError.c_str()); return -1; } - writeError(outErrorText, outErrorTextCapacity, - "dx12_bridge preflight passed, but Vulkan<->DX12 interop dispatch is not implemented yet"); - return -1; #endif } @@ -496,6 +511,10 @@ WOWEE_FSR3_WRAPPER_EXPORT int32_t wowee_fsr3_wrapper_initialize(const WoweeFsr3W if (*runtimeEnv) candidates.emplace_back(runtimeEnv); } #if defined(_WIN32) + if (backend == WrapperBackend::Dx12Bridge) { + candidates.emplace_back("amd_fidelityfx_framegeneration_dx12.dll"); + candidates.emplace_back("ffx_framegeneration_dx12.dll"); + } candidates.emplace_back("ffx_fsr3_vk.dll"); candidates.emplace_back("ffx_fsr3.dll"); candidates.emplace_back("ffx_fsr3_bridge.dll"); @@ -513,31 +532,27 @@ WOWEE_FSR3_WRAPPER_EXPORT int32_t wowee_fsr3_wrapper_initialize(const WoweeFsr3W WrapperContext* ctx = new WrapperContext{}; ctx->backend = backend; for (const std::string& path : candidates) { - ctx->backendLibHandle = openLibrary(path.c_str()); - if (ctx->backendLibHandle) break; + void* candidateHandle = openLibrary(path.c_str()); + if (!candidateHandle) continue; + + RuntimeFns candidateFns{}; + if (!bindVulkanRuntimeFns(candidateHandle, candidateFns)) { + closeLibrary(candidateHandle); + continue; + } + + ctx->backendLibHandle = candidateHandle; + ctx->fns = candidateFns; + break; } if (!ctx->backendLibHandle) { destroyContext(ctx); - writeError(outErrorText, outErrorTextCapacity, "no FSR3 backend runtime found for wrapper"); - return -1; - } - - ctx->fns.getScratchMemorySizeVK = reinterpret_castfns.getScratchMemorySizeVK)>(resolveSymbol(ctx->backendLibHandle, "ffxGetScratchMemorySizeVK")); - ctx->fns.getDeviceVK = reinterpret_castfns.getDeviceVK)>(resolveSymbol(ctx->backendLibHandle, "ffxGetDeviceVK")); - ctx->fns.getInterfaceVK = reinterpret_castfns.getInterfaceVK)>(resolveSymbol(ctx->backendLibHandle, "ffxGetInterfaceVK")); - ctx->fns.getCommandListVK = reinterpret_castfns.getCommandListVK)>(resolveSymbol(ctx->backendLibHandle, "ffxGetCommandListVK")); - ctx->fns.getResourceVK = reinterpret_castfns.getResourceVK)>(resolveSymbol(ctx->backendLibHandle, "ffxGetResourceVK")); - ctx->fns.fsr3ContextCreate = reinterpret_castfns.fsr3ContextCreate)>(resolveSymbol(ctx->backendLibHandle, "ffxFsr3ContextCreate")); - ctx->fns.fsr3ContextDispatchUpscale = reinterpret_castfns.fsr3ContextDispatchUpscale)>(resolveSymbol(ctx->backendLibHandle, "ffxFsr3ContextDispatchUpscale")); - ctx->fns.fsr3ConfigureFrameGeneration = reinterpret_castfns.fsr3ConfigureFrameGeneration)>(resolveSymbol(ctx->backendLibHandle, "ffxFsr3ConfigureFrameGeneration")); - ctx->fns.fsr3DispatchFrameGeneration = reinterpret_castfns.fsr3DispatchFrameGeneration)>(resolveSymbol(ctx->backendLibHandle, "ffxFsr3DispatchFrameGeneration")); - ctx->fns.fsr3ContextDestroy = reinterpret_castfns.fsr3ContextDestroy)>(resolveSymbol(ctx->backendLibHandle, "ffxFsr3ContextDestroy")); - - if (!ctx->fns.getScratchMemorySizeVK || !ctx->fns.getDeviceVK || !ctx->fns.getInterfaceVK || - !ctx->fns.getCommandListVK || !ctx->fns.getResourceVK || !ctx->fns.fsr3ContextCreate || - !ctx->fns.fsr3ContextDispatchUpscale || !ctx->fns.fsr3ContextDestroy) { - destroyContext(ctx); - writeError(outErrorText, outErrorTextCapacity, "backend missing required ffx symbols"); + if (backend == WrapperBackend::Dx12Bridge) { + writeError(outErrorText, outErrorTextCapacity, + "dx12_bridge requested, but no dispatch-capable runtime exports were found"); + } else { + writeError(outErrorText, outErrorTextCapacity, "no FSR3 backend runtime found for wrapper"); + } return -1; }