Auto-fallback wrapper backend to DX12 bridge on Windows

This commit is contained in:
Kelsi 2026-03-09 02:08:26 -07:00
parent 1bcc2c6b85
commit 3e5f6d0084

View file

@ -596,8 +596,10 @@ WOWEE_FSR3_WRAPPER_EXPORT int32_t wowee_fsr3_wrapper_initialize(const WoweeFsr3W
return -1; return -1;
} }
const WrapperBackend backend = selectBackend(); const char* backendEnvRaw = std::getenv("WOWEE_FSR3_WRAPPER_BACKEND");
if (backend == WrapperBackend::Dx12Bridge) { const bool backendExplicit = (backendEnvRaw && *backendEnvRaw);
const WrapperBackend selectedBackend = selectBackend();
if (selectedBackend == WrapperBackend::Dx12Bridge) {
#if !defined(_WIN32) #if !defined(_WIN32)
writeError(outErrorText, outErrorTextCapacity, writeError(outErrorText, outErrorTextCapacity,
"dx12_bridge backend is Windows-only in current wrapper build"); "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 #endif
} }
std::vector<std::string> candidates; std::vector<std::string> baseCandidates;
if (const char* backendEnv = std::getenv("WOWEE_FSR3_WRAPPER_BACKEND_LIB")) { 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 (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{}; WrapperContext* ctx = new WrapperContext{};
ctx->backend = backend; ctx->backend = selectedBackend;
#if defined(_WIN32) #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) { if (D3D12CreateDevice(nullptr, D3D_FEATURE_LEVEL_12_0, IID_PPV_ARGS(&ctx->dx12Device)) != S_OK || !ctx->dx12Device) {
destroyContext(ctx); ctx->lastError = "dx12_bridge failed to create D3D12 device";
writeError(outErrorText, outErrorTextCapacity, "dx12_bridge failed to create D3D12 device"); return false;
return -1;
} }
D3D12_COMMAND_QUEUE_DESC queueDesc{}; D3D12_COMMAND_QUEUE_DESC queueDesc{};
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
if (ctx->dx12Device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&ctx->dx12Queue)) != S_OK || !ctx->dx12Queue) { if (ctx->dx12Device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&ctx->dx12Queue)) != S_OK || !ctx->dx12Queue) {
destroyContext(ctx); ctx->lastError = "dx12_bridge failed to create command queue";
writeError(outErrorText, outErrorTextCapacity, "dx12_bridge failed to create command queue"); return false;
return -1;
} }
if (ctx->dx12Device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&ctx->dx12CommandAllocator)) != S_OK || if (ctx->dx12Device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&ctx->dx12CommandAllocator)) != S_OK ||
!ctx->dx12CommandAllocator) { !ctx->dx12CommandAllocator) {
destroyContext(ctx); ctx->lastError = "dx12_bridge failed to create command allocator";
writeError(outErrorText, outErrorTextCapacity, "dx12_bridge failed to create command allocator"); return false;
return -1;
} }
if (ctx->dx12Device->CreateCommandList( if (ctx->dx12Device->CreateCommandList(
0, D3D12_COMMAND_LIST_TYPE_DIRECT, ctx->dx12CommandAllocator, nullptr, 0, D3D12_COMMAND_LIST_TYPE_DIRECT, ctx->dx12CommandAllocator, nullptr,
IID_PPV_ARGS(&ctx->dx12CommandList)) != S_OK || !ctx->dx12CommandList) { IID_PPV_ARGS(&ctx->dx12CommandList)) != S_OK || !ctx->dx12CommandList) {
destroyContext(ctx); ctx->lastError = "dx12_bridge failed to create command list";
writeError(outErrorText, outErrorTextCapacity, "dx12_bridge failed to create command list"); return false;
return -1;
} }
ctx->dx12CommandList->Close(); ctx->dx12CommandList->Close();
if (ctx->dx12Device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&ctx->dx12Fence)) != S_OK || !ctx->dx12Fence) { if (ctx->dx12Device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&ctx->dx12Fence)) != S_OK || !ctx->dx12Fence) {
destroyContext(ctx); ctx->lastError = "dx12_bridge failed to create fence";
writeError(outErrorText, outErrorTextCapacity, "dx12_bridge failed to create fence"); return false;
return -1;
} }
ctx->dx12FenceEvent = CreateEventA(nullptr, FALSE, FALSE, nullptr); ctx->dx12FenceEvent = CreateEventA(nullptr, FALSE, FALSE, nullptr);
if (!ctx->dx12FenceEvent) { if (!ctx->dx12FenceEvent) {
destroyContext(ctx); ctx->lastError = "dx12_bridge failed to create fence event";
writeError(outErrorText, outErrorTextCapacity, "dx12_bridge failed to create fence event"); return false;
return -1;
} }
ctx->dx12FenceValue = 1; 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<std::string> 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 #endif
for (const std::string& path : candidates) { if (!loaded) {
void* candidateHandle = openLibrary(path.c_str()); if (ctx->backendLibHandle) {
if (!candidateHandle) continue; closeLibrary(ctx->backendLibHandle);
ctx->backendLibHandle = nullptr;
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->backendLibHandle = candidateHandle;
ctx->fns = candidateFns;
break;
} }
if (!ctx->backendLibHandle) { 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); destroyContext(ctx);
if (backend == WrapperBackend::Dx12Bridge) { if (attemptedDx12) {
writeError(outErrorText, outErrorTextCapacity, 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 { } else {
writeError(outErrorText, outErrorTextCapacity, "no FSR3 backend runtime found for wrapper"); 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; return -1;
} }
if (backend == WrapperBackend::Dx12Bridge) { if (ctx->backend == WrapperBackend::Dx12Bridge) {
#if defined(_WIN32) #if defined(_WIN32)
ctx->scratchBufferSize = ctx->fns.getScratchMemorySizeDX12(FFX_FSR3_CONTEXT_COUNT); ctx->scratchBufferSize = ctx->fns.getScratchMemorySizeDX12(FFX_FSR3_CONTEXT_COUNT);
#else #else
@ -744,7 +776,7 @@ WOWEE_FSR3_WRAPPER_EXPORT int32_t wowee_fsr3_wrapper_initialize(const WoweeFsr3W
FfxInterface backendShared{}; FfxInterface backendShared{};
FfxErrorCode ifaceErr = FFX_ERROR_INVALID_ARGUMENT; FfxErrorCode ifaceErr = FFX_ERROR_INVALID_ARGUMENT;
if (backend == WrapperBackend::Dx12Bridge) { if (ctx->backend == WrapperBackend::Dx12Bridge) {
#if defined(_WIN32) #if defined(_WIN32)
FfxDevice ffxDevice = ctx->fns.getDeviceDX12(ctx->dx12Device); FfxDevice ffxDevice = ctx->fns.getDeviceDX12(ctx->dx12Device);
ifaceErr = ctx->fns.getInterfaceDX12( ifaceErr = ctx->fns.getInterfaceDX12(
@ -761,7 +793,9 @@ WOWEE_FSR3_WRAPPER_EXPORT int32_t wowee_fsr3_wrapper_initialize(const WoweeFsr3W
} }
if (ifaceErr != FFX_OK) { if (ifaceErr != FFX_OK) {
destroyContext(ctx); destroyContext(ctx);
writeError(outErrorText, outErrorTextCapacity, "ffxGetInterfaceVK failed"); writeError(outErrorText, outErrorTextCapacity,
ctx->backend == WrapperBackend::Dx12Bridge ? "ffxGetInterfaceDX12 failed"
: "ffxGetInterfaceVK failed");
return -1; return -1;
} }