From 3e5f6d0084a44d8b52d57bb107cbb6f926fb8e08 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Mon, 9 Mar 2026 02:08:26 -0700 Subject: [PATCH] Auto-fallback wrapper backend to DX12 bridge on Windows --- src/rendering/amd_fsr3_wrapper_impl.cpp | 168 ++++++++++++++---------- 1 file changed, 101 insertions(+), 67 deletions(-) diff --git a/src/rendering/amd_fsr3_wrapper_impl.cpp b/src/rendering/amd_fsr3_wrapper_impl.cpp index 79bb4257..3dcf7156 100644 --- a/src/rendering/amd_fsr3_wrapper_impl.cpp +++ b/src/rendering/amd_fsr3_wrapper_impl.cpp @@ -596,8 +596,10 @@ WOWEE_FSR3_WRAPPER_EXPORT int32_t wowee_fsr3_wrapper_initialize(const WoweeFsr3W return -1; } - const WrapperBackend backend = selectBackend(); - if (backend == WrapperBackend::Dx12Bridge) { + const char* backendEnvRaw = std::getenv("WOWEE_FSR3_WRAPPER_BACKEND"); + const bool backendExplicit = (backendEnvRaw && *backendEnvRaw); + const WrapperBackend selectedBackend = selectBackend(); + if (selectedBackend == WrapperBackend::Dx12Bridge) { #if !defined(_WIN32) writeError(outErrorText, outErrorTextCapacity, "dx12_bridge backend is Windows-only in current wrapper build"); @@ -611,103 +613,133 @@ WOWEE_FSR3_WRAPPER_EXPORT int32_t wowee_fsr3_wrapper_initialize(const WoweeFsr3W #endif } - std::vector candidates; + std::vector baseCandidates; if (const char* backendEnv = std::getenv("WOWEE_FSR3_WRAPPER_BACKEND_LIB")) { - if (*backendEnv) candidates.emplace_back(backendEnv); + if (*backendEnv) baseCandidates.emplace_back(backendEnv); } if (const char* runtimeEnv = std::getenv("WOWEE_FFX_SDK_RUNTIME_LIB")) { - if (*runtimeEnv) candidates.emplace_back(runtimeEnv); + if (*runtimeEnv) baseCandidates.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"); -#elif defined(__APPLE__) - candidates.emplace_back("libffx_fsr3_vk.dylib"); - candidates.emplace_back("libffx_fsr3.dylib"); - candidates.emplace_back("libffx_fsr3_bridge.dylib"); -#else - candidates.emplace_back("./libffx_fsr3_vk.so"); - candidates.emplace_back("libffx_fsr3_vk.so"); - candidates.emplace_back("libffx_fsr3.so"); - candidates.emplace_back("libffx_fsr3_bridge.so"); -#endif WrapperContext* ctx = new WrapperContext{}; - ctx->backend = backend; + ctx->backend = selectedBackend; #if defined(_WIN32) - if (backend == WrapperBackend::Dx12Bridge) { + auto initDx12BridgeState = [&](const WoweeFsr3WrapperInitDesc* desc) -> bool { + if (ctx->dx12Device) return true; + if (!runDx12BridgePreflight(desc, ctx->lastError)) return false; if (D3D12CreateDevice(nullptr, D3D_FEATURE_LEVEL_12_0, IID_PPV_ARGS(&ctx->dx12Device)) != S_OK || !ctx->dx12Device) { - destroyContext(ctx); - writeError(outErrorText, outErrorTextCapacity, "dx12_bridge failed to create D3D12 device"); - return -1; + ctx->lastError = "dx12_bridge failed to create D3D12 device"; + return false; } D3D12_COMMAND_QUEUE_DESC queueDesc{}; queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; if (ctx->dx12Device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&ctx->dx12Queue)) != S_OK || !ctx->dx12Queue) { - destroyContext(ctx); - writeError(outErrorText, outErrorTextCapacity, "dx12_bridge failed to create command queue"); - return -1; + ctx->lastError = "dx12_bridge failed to create command queue"; + return false; } if (ctx->dx12Device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&ctx->dx12CommandAllocator)) != S_OK || !ctx->dx12CommandAllocator) { - destroyContext(ctx); - writeError(outErrorText, outErrorTextCapacity, "dx12_bridge failed to create command allocator"); - return -1; + ctx->lastError = "dx12_bridge failed to create command allocator"; + return false; } if (ctx->dx12Device->CreateCommandList( 0, D3D12_COMMAND_LIST_TYPE_DIRECT, ctx->dx12CommandAllocator, nullptr, IID_PPV_ARGS(&ctx->dx12CommandList)) != S_OK || !ctx->dx12CommandList) { - destroyContext(ctx); - writeError(outErrorText, outErrorTextCapacity, "dx12_bridge failed to create command list"); - return -1; + ctx->lastError = "dx12_bridge failed to create command list"; + return false; } ctx->dx12CommandList->Close(); if (ctx->dx12Device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&ctx->dx12Fence)) != S_OK || !ctx->dx12Fence) { - destroyContext(ctx); - writeError(outErrorText, outErrorTextCapacity, "dx12_bridge failed to create fence"); - return -1; + ctx->lastError = "dx12_bridge failed to create fence"; + return false; } ctx->dx12FenceEvent = CreateEventA(nullptr, FALSE, FALSE, nullptr); if (!ctx->dx12FenceEvent) { - destroyContext(ctx); - writeError(outErrorText, outErrorTextCapacity, "dx12_bridge failed to create fence event"); - return -1; + ctx->lastError = "dx12_bridge failed to create fence event"; + return false; } ctx->dx12FenceValue = 1; + return true; + }; +#endif + + auto tryLoadForBackend = [&](WrapperBackend backend) -> bool { +#if defined(_WIN32) + if (backend == WrapperBackend::Dx12Bridge && !initDx12BridgeState(initDesc)) { + return false; + } +#endif + std::vector candidates = baseCandidates; +#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"); +#elif defined(__APPLE__) + candidates.emplace_back("libffx_fsr3_vk.dylib"); + candidates.emplace_back("libffx_fsr3.dylib"); + candidates.emplace_back("libffx_fsr3_bridge.dylib"); +#else + candidates.emplace_back("./libffx_fsr3_vk.so"); + candidates.emplace_back("libffx_fsr3_vk.so"); + candidates.emplace_back("libffx_fsr3.so"); + candidates.emplace_back("libffx_fsr3_bridge.so"); +#endif + + for (const std::string& path : candidates) { + void* candidateHandle = openLibrary(path.c_str()); + if (!candidateHandle) continue; + + RuntimeFns candidateFns{}; +#if defined(_WIN32) + const bool bound = (backend == WrapperBackend::Dx12Bridge) + ? bindDx12RuntimeFns(candidateHandle, candidateFns) + : bindVulkanRuntimeFns(candidateHandle, candidateFns); +#else + const bool bound = bindVulkanRuntimeFns(candidateHandle, candidateFns); +#endif + if (!bound) { + closeLibrary(candidateHandle); + continue; + } + + ctx->backend = backend; + ctx->backendLibHandle = candidateHandle; + ctx->fns = candidateFns; + return true; + } + return false; + }; + + bool loaded = tryLoadForBackend(selectedBackend); +#if defined(_WIN32) + if (!loaded && !backendExplicit && selectedBackend == WrapperBackend::VulkanRuntime) { + loaded = tryLoadForBackend(WrapperBackend::Dx12Bridge); } #endif - for (const std::string& path : candidates) { - void* candidateHandle = openLibrary(path.c_str()); - if (!candidateHandle) continue; - - RuntimeFns candidateFns{}; -#if defined(_WIN32) - const bool bound = (backend == WrapperBackend::Dx12Bridge) - ? bindDx12RuntimeFns(candidateHandle, candidateFns) - : bindVulkanRuntimeFns(candidateHandle, candidateFns); -#else - const bool bound = bindVulkanRuntimeFns(candidateHandle, candidateFns); -#endif - if (!bound) { - closeLibrary(candidateHandle); - continue; + if (!loaded) { + if (ctx->backendLibHandle) { + closeLibrary(ctx->backendLibHandle); + ctx->backendLibHandle = nullptr; } - - ctx->backendLibHandle = candidateHandle; - ctx->fns = candidateFns; - break; } if (!ctx->backendLibHandle) { + const bool attemptedDx12 = + (selectedBackend == WrapperBackend::Dx12Bridge) +#if defined(_WIN32) + || (!backendExplicit && selectedBackend == WrapperBackend::VulkanRuntime) +#endif + ; + const std::string err = ctx->lastError; destroyContext(ctx); - if (backend == WrapperBackend::Dx12Bridge) { + if (attemptedDx12) { writeError(outErrorText, outErrorTextCapacity, - "dx12_bridge requested, but no dispatch-capable runtime exports were found"); + err.empty() ? "dx12_bridge requested, but no dispatch-capable runtime exports were found" + : err.c_str()); } else { writeError(outErrorText, outErrorTextCapacity, "no FSR3 backend runtime found for wrapper"); } @@ -721,7 +753,7 @@ WOWEE_FSR3_WRAPPER_EXPORT int32_t wowee_fsr3_wrapper_initialize(const WoweeFsr3W return -1; } - if (backend == WrapperBackend::Dx12Bridge) { + if (ctx->backend == WrapperBackend::Dx12Bridge) { #if defined(_WIN32) ctx->scratchBufferSize = ctx->fns.getScratchMemorySizeDX12(FFX_FSR3_CONTEXT_COUNT); #else @@ -744,7 +776,7 @@ WOWEE_FSR3_WRAPPER_EXPORT int32_t wowee_fsr3_wrapper_initialize(const WoweeFsr3W FfxInterface backendShared{}; FfxErrorCode ifaceErr = FFX_ERROR_INVALID_ARGUMENT; - if (backend == WrapperBackend::Dx12Bridge) { + if (ctx->backend == WrapperBackend::Dx12Bridge) { #if defined(_WIN32) FfxDevice ffxDevice = ctx->fns.getDeviceDX12(ctx->dx12Device); ifaceErr = ctx->fns.getInterfaceDX12( @@ -761,7 +793,9 @@ WOWEE_FSR3_WRAPPER_EXPORT int32_t wowee_fsr3_wrapper_initialize(const WoweeFsr3W } if (ifaceErr != FFX_OK) { destroyContext(ctx); - writeError(outErrorText, outErrorTextCapacity, "ffxGetInterfaceVK failed"); + writeError(outErrorText, outErrorTextCapacity, + ctx->backend == WrapperBackend::Dx12Bridge ? "ffxGetInterfaceDX12 failed" + : "ffxGetInterfaceVK failed"); return -1; }