Fix cross-platform FSR3 compile path and Path-A runtime wiring

This commit is contained in:
Kelsi 2026-03-09 04:24:24 -07:00
parent 725602b5e5
commit 9ff9f2f1f1
7 changed files with 333 additions and 113 deletions

View file

@ -1,8 +1,15 @@
#include <cstddef>
#if WOWEE_AMD_FFX_SDK_KITS
#include <ffx_fsr3upscaler.h>
#include <ffx_frameinterpolation.h>
#include <ffx_opticalflow.h>
#include <ffx_vk.h>
#else
#include <FidelityFX/host/ffx_fsr3upscaler.h>
#include <FidelityFX/host/ffx_frameinterpolation.h>
#include <FidelityFX/host/ffx_opticalflow.h>
#include <FidelityFX/host/backends/vk/ffx_vk.h>
#endif
namespace wowee::rendering {
@ -14,11 +21,14 @@ bool amdFsr3FramegenCompileProbe() {
FfxFrameInterpolationContext fiContext{};
FfxOpticalflowContext ofContext{};
FfxInterface backend{};
#if WOWEE_AMD_FFX_SDK_KITS
FfxApiDimensions2D renderSize{};
#else
FfxDimensions2D renderSize{};
#endif
static_assert(FFX_FSR3UPSCALER_VERSION_MAJOR >= 3, "Expected FSR3 upscaler v3+");
static_assert(FFX_FRAMEINTERPOLATION_VERSION_MAJOR >= 1, "Expected frame interpolation v1+");
static_assert(FFX_OPTICALFLOW_VERSION_MAJOR >= 1, "Expected optical flow v1+");
(void)fsr3Context;
(void)fiContext;

View file

@ -1,5 +1,4 @@
#include "rendering/amd_fsr3_runtime.hpp"
#include "rendering/amd_fsr3_wrapper_abi.h"
#include <algorithm>
@ -18,9 +17,14 @@
#endif
#if WOWEE_HAS_AMD_FSR3_FRAMEGEN
#if WOWEE_AMD_FFX_SDK_KITS
#include "third_party/ffx_fsr3_legacy_compat.h"
#include <ffx_vk.h>
#else
#include <FidelityFX/host/ffx_fsr3.h>
#include <FidelityFX/host/backends/vk/ffx_vk.h>
#endif
#endif
namespace wowee::rendering {
@ -57,10 +61,7 @@ AmdFsr3Runtime::~AmdFsr3Runtime() {
shutdown();
}
bool AmdFsr3Runtime::hasWrapperExternalInterop() const {
if (loadPathKind_ != LoadPathKind::Wrapper) return false;
return (wrapperCapabilities_ & WOWEE_FSR3_WRAPPER_CAP_EXTERNAL_INTEROP) != 0u;
}
bool AmdFsr3Runtime::hasWrapperExternalInterop() const { return false; }
#if WOWEE_HAS_AMD_FSR3_FRAMEGEN
namespace {
@ -191,28 +192,22 @@ bool AmdFsr3Runtime::initialize(const AmdFsr3RuntimeInitDesc& desc) {
if (const char* envPath = std::getenv("WOWEE_FFX_SDK_RUNTIME_LIB")) {
if (*envPath) candidates.push_back({envPath, LoadPathKind::Official});
}
if (const char* wrapperEnv = std::getenv("WOWEE_FFX_SDK_RUNTIME_WRAPPER_LIB")) {
if (*wrapperEnv) candidates.push_back({wrapperEnv, LoadPathKind::Wrapper});
}
#if defined(_WIN32)
candidates.push_back({"amd_fidelityfx_vk.dll", LoadPathKind::Official});
candidates.push_back({"libamd_fidelityfx_vk.dll", LoadPathKind::Official});
candidates.push_back({"ffx_fsr3_vk.dll", LoadPathKind::Official});
candidates.push_back({"ffx_fsr3.dll", LoadPathKind::Official});
candidates.push_back({"ffx_fsr3_vk_wrapper.dll", LoadPathKind::Wrapper});
candidates.push_back({"ffx_fsr3_bridge.dll", LoadPathKind::Wrapper});
#elif defined(__APPLE__)
candidates.push_back({"libamd_fidelityfx_vk.dylib", LoadPathKind::Official});
candidates.push_back({"libffx_fsr3_vk.dylib", LoadPathKind::Official});
candidates.push_back({"libffx_fsr3.dylib", LoadPathKind::Official});
candidates.push_back({"libffx_fsr3_vk_wrapper.dylib", LoadPathKind::Wrapper});
candidates.push_back({"libffx_fsr3_bridge.dylib", LoadPathKind::Wrapper});
#else
candidates.push_back({"./libamd_fidelityfx_vk.so", LoadPathKind::Official});
candidates.push_back({"libamd_fidelityfx_vk.so", LoadPathKind::Official});
candidates.push_back({"./libffx_fsr3_vk.so", LoadPathKind::Official});
candidates.push_back({"libffx_fsr3_vk.so", LoadPathKind::Official});
candidates.push_back({"libffx_fsr3.so", LoadPathKind::Official});
candidates.push_back({"./libffx_fsr3_vk_wrapper.so", LoadPathKind::Wrapper});
candidates.push_back({"libffx_fsr3_vk_wrapper.so", LoadPathKind::Wrapper});
candidates.push_back({"libffx_fsr3_bridge.so", LoadPathKind::Wrapper});
#endif
for (const Candidate& candidate : candidates) {
#if defined(_WIN32)
HMODULE h = LoadLibraryA(candidate.path.c_str());
@ -228,7 +223,7 @@ bool AmdFsr3Runtime::initialize(const AmdFsr3RuntimeInitDesc& desc) {
break;
}
if (!libHandle_) {
lastError_ = "no official runtime (Path A) or wrapper runtime (Path B) found";
lastError_ = "no official runtime (Path A) found";
return false;
}
@ -353,7 +348,11 @@ bool AmdFsr3Runtime::initialize(const AmdFsr3RuntimeInitDesc& desc) {
return false;
}
#if WOWEE_AMD_FFX_SDK_KITS
scratchBufferSize_ = fns_->getScratchMemorySizeVK(FFX_FSR3_CONTEXT_COUNT);
#else
scratchBufferSize_ = fns_->getScratchMemorySizeVK(desc.physicalDevice, FFX_FSR3_CONTEXT_COUNT);
#endif
if (scratchBufferSize_ == 0) {
LOG_WARNING("FSR3 runtime: scratch buffer size query returned 0.");
lastError_ = "scratch buffer size query returned 0";
@ -368,14 +367,23 @@ bool AmdFsr3Runtime::initialize(const AmdFsr3RuntimeInitDesc& desc) {
return false;
}
#if WOWEE_AMD_FFX_SDK_KITS
FfxDevice ffxDevice = fns_->getDeviceVK(desc.device);
#else
VkDeviceContext vkDevCtx{};
vkDevCtx.vkDevice = desc.device;
vkDevCtx.vkPhysicalDevice = desc.physicalDevice;
vkDevCtx.vkDeviceProcAddr = desc.getDeviceProcAddr;
FfxDevice ffxDevice = fns_->getDeviceVK(&vkDevCtx);
#endif
FfxInterface backendShared{};
FfxErrorCode ifaceErr = fns_->getInterfaceVK(&backendShared, ffxDevice, scratchBuffer_, scratchBufferSize_, FFX_FSR3_CONTEXT_COUNT);
#if WOWEE_AMD_FFX_SDK_KITS
FfxErrorCode ifaceErr = fns_->getInterfaceVK(
&backendShared, ffxDevice, desc.physicalDevice, scratchBuffer_, scratchBufferSize_, FFX_FSR3_CONTEXT_COUNT);
#else
FfxErrorCode ifaceErr = fns_->getInterfaceVK(
&backendShared, ffxDevice, scratchBuffer_, scratchBufferSize_, FFX_FSR3_CONTEXT_COUNT);
#endif
if (ifaceErr != FFX_OK) {
LOG_WARNING("FSR3 runtime: ffxGetInterfaceVK failed (", static_cast<int>(ifaceErr), ").");
lastError_ = "ffxGetInterfaceVK failed";
@ -541,13 +549,23 @@ bool AmdFsr3Runtime::dispatchUpscale(const AmdFsr3RuntimeDispatchDesc& desc) {
static wchar_t kDepthName[] = L"FSR3_Depth";
static wchar_t kMotionName[] = L"FSR3_MotionVectors";
static wchar_t kOutputName[] = L"FSR3_Output";
#if WOWEE_AMD_FFX_SDK_KITS
dispatch.color = fns_->getResourceVK(desc.colorImage, colorDesc, kColorName, FFX_RESOURCE_STATE_COMPUTE_READ);
dispatch.depth = fns_->getResourceVK(desc.depthImage, depthDesc, kDepthName, FFX_RESOURCE_STATE_COMPUTE_READ);
dispatch.motionVectors = fns_->getResourceVK(desc.motionVectorImage, mvDesc, kMotionName, FFX_RESOURCE_STATE_COMPUTE_READ);
#else
dispatch.color = fns_->getResourceVK(reinterpret_cast<void*>(desc.colorImage), colorDesc, kColorName, FFX_RESOURCE_STATE_COMPUTE_READ);
dispatch.depth = fns_->getResourceVK(reinterpret_cast<void*>(desc.depthImage), depthDesc, kDepthName, FFX_RESOURCE_STATE_COMPUTE_READ);
dispatch.motionVectors = fns_->getResourceVK(reinterpret_cast<void*>(desc.motionVectorImage), mvDesc, kMotionName, FFX_RESOURCE_STATE_COMPUTE_READ);
#endif
dispatch.exposure = FfxResource{};
dispatch.reactive = FfxResource{};
dispatch.transparencyAndComposition = FfxResource{};
#if WOWEE_AMD_FFX_SDK_KITS
dispatch.upscaleOutput = fns_->getResourceVK(desc.outputImage, outDesc, kOutputName, FFX_RESOURCE_STATE_UNORDERED_ACCESS);
#else
dispatch.upscaleOutput = fns_->getResourceVK(reinterpret_cast<void*>(desc.outputImage), outDesc, kOutputName, FFX_RESOURCE_STATE_UNORDERED_ACCESS);
#endif
dispatch.jitterOffset.x = desc.jitterX;
dispatch.jitterOffset.y = desc.jitterY;
dispatch.motionVectorScale.x = desc.motionScaleX;
@ -658,10 +676,17 @@ bool AmdFsr3Runtime::dispatchFrameGeneration(const AmdFsr3RuntimeDispatchDesc& d
static wchar_t kInterpolatedName[] = L"FSR3_InterpolatedOutput";
FfxFrameGenerationDispatchDescription fgDispatch{};
fgDispatch.commandList = fns_->getCommandListVK(desc.commandBuffer);
#if WOWEE_AMD_FFX_SDK_KITS
fgDispatch.presentColor = fns_->getResourceVK(
desc.outputImage, presentDesc, kPresentName, FFX_RESOURCE_STATE_COMPUTE_READ);
fgDispatch.outputs[0] = fns_->getResourceVK(
desc.frameGenOutputImage, fgOutDesc, kInterpolatedName, FFX_RESOURCE_STATE_UNORDERED_ACCESS);
#else
fgDispatch.presentColor = fns_->getResourceVK(
reinterpret_cast<void*>(desc.outputImage), presentDesc, kPresentName, FFX_RESOURCE_STATE_COMPUTE_READ);
fgDispatch.outputs[0] = fns_->getResourceVK(
reinterpret_cast<void*>(desc.frameGenOutputImage), fgOutDesc, kInterpolatedName, FFX_RESOURCE_STATE_UNORDERED_ACCESS);
#endif
fgDispatch.numInterpolatedFrames = 1;
fgDispatch.reset = desc.reset;
fgDispatch.backBufferTransferFunction = FFX_BACKBUFFER_TRANSFER_FUNCTION_SRGB;

View file

@ -1,12 +1,20 @@
#include "rendering/amd_fsr3_wrapper_abi.h"
#if WOWEE_HAS_AMD_FSR3_FRAMEGEN
#if WOWEE_AMD_FFX_SDK_KITS
#include "third_party/ffx_fsr3_legacy_compat.h"
#include <ffx_vk.h>
#if defined(_WIN32)
#include <ffx_dx12.h>
#endif
#else
#include <FidelityFX/host/backends/vk/ffx_vk.h>
#if defined(_WIN32)
#include <FidelityFX/host/backends/dx12/ffx_dx12.h>
#endif
#include <FidelityFX/host/ffx_fsr3.h>
#endif
#endif
#include <algorithm>
#include <array>
@ -878,7 +886,11 @@ WOWEE_FSR3_WRAPPER_EXPORT int32_t wowee_fsr3_wrapper_initialize(const WoweeFsr3W
ctx->scratchBufferSize = 0;
#endif
} else {
#if WOWEE_AMD_FFX_SDK_KITS
ctx->scratchBufferSize = ctx->fns.getScratchMemorySizeVK(FFX_FSR3_CONTEXT_COUNT);
#else
ctx->scratchBufferSize = ctx->fns.getScratchMemorySizeVK(initDesc->physicalDevice, FFX_FSR3_CONTEXT_COUNT);
#endif
}
if (ctx->scratchBufferSize == 0) {
destroyContext(ctx);
@ -901,6 +913,11 @@ WOWEE_FSR3_WRAPPER_EXPORT int32_t wowee_fsr3_wrapper_initialize(const WoweeFsr3W
&backendShared, ffxDevice, ctx->scratchBuffer, ctx->scratchBufferSize, FFX_FSR3_CONTEXT_COUNT);
#endif
} else {
#if WOWEE_AMD_FFX_SDK_KITS
FfxDevice ffxDevice = ctx->fns.getDeviceVK(initDesc->device);
ifaceErr = ctx->fns.getInterfaceVK(
&backendShared, ffxDevice, initDesc->physicalDevice, ctx->scratchBuffer, ctx->scratchBufferSize, FFX_FSR3_CONTEXT_COUNT);
#else
VkDeviceContext vkDevCtx{};
vkDevCtx.vkDevice = initDesc->device;
vkDevCtx.vkPhysicalDevice = initDesc->physicalDevice;
@ -908,6 +925,7 @@ WOWEE_FSR3_WRAPPER_EXPORT int32_t wowee_fsr3_wrapper_initialize(const WoweeFsr3W
FfxDevice ffxDevice = ctx->fns.getDeviceVK(&vkDevCtx);
ifaceErr = ctx->fns.getInterfaceVK(
&backendShared, ffxDevice, ctx->scratchBuffer, ctx->scratchBufferSize, FFX_FSR3_CONTEXT_COUNT);
#endif
}
if (ifaceErr != FFX_OK) {
const bool wasDx12Backend = (ctx->backend == WrapperBackend::Dx12Bridge);
@ -1108,10 +1126,17 @@ WOWEE_FSR3_WRAPPER_EXPORT int32_t wowee_fsr3_wrapper_dispatch_upscale(WoweeFsr3W
#endif
} else {
dispatch.commandList = ctx->fns.getCommandListVK(dispatchDesc->commandBuffer);
#if WOWEE_AMD_FFX_SDK_KITS
dispatch.color = ctx->fns.getResourceVK(dispatchDesc->colorImage, colorDesc, kColorName, FFX_RESOURCE_STATE_COMPUTE_READ);
dispatch.depth = ctx->fns.getResourceVK(dispatchDesc->depthImage, depthDesc, kDepthName, FFX_RESOURCE_STATE_COMPUTE_READ);
dispatch.motionVectors = ctx->fns.getResourceVK(dispatchDesc->motionVectorImage, mvDesc, kMotionName, FFX_RESOURCE_STATE_COMPUTE_READ);
dispatch.upscaleOutput = ctx->fns.getResourceVK(dispatchDesc->outputImage, outDesc, kOutputName, FFX_RESOURCE_STATE_UNORDERED_ACCESS);
#else
dispatch.color = ctx->fns.getResourceVK(reinterpret_cast<void*>(dispatchDesc->colorImage), colorDesc, kColorName, FFX_RESOURCE_STATE_COMPUTE_READ);
dispatch.depth = ctx->fns.getResourceVK(reinterpret_cast<void*>(dispatchDesc->depthImage), depthDesc, kDepthName, FFX_RESOURCE_STATE_COMPUTE_READ);
dispatch.motionVectors = ctx->fns.getResourceVK(reinterpret_cast<void*>(dispatchDesc->motionVectorImage), mvDesc, kMotionName, FFX_RESOURCE_STATE_COMPUTE_READ);
dispatch.upscaleOutput = ctx->fns.getResourceVK(reinterpret_cast<void*>(dispatchDesc->outputImage), outDesc, kOutputName, FFX_RESOURCE_STATE_UNORDERED_ACCESS);
#endif
}
dispatch.exposure = FfxResource{};
dispatch.reactive = FfxResource{};
@ -1289,8 +1314,13 @@ WOWEE_FSR3_WRAPPER_EXPORT int32_t wowee_fsr3_wrapper_dispatch_framegen(WoweeFsr3
#endif
} else {
fgDispatch.commandList = ctx->fns.getCommandListVK(dispatchDesc->commandBuffer);
#if WOWEE_AMD_FFX_SDK_KITS
fgDispatch.presentColor = ctx->fns.getResourceVK(dispatchDesc->outputImage, presentDesc, kPresentName, FFX_RESOURCE_STATE_COMPUTE_READ);
fgDispatch.outputs[0] = ctx->fns.getResourceVK(dispatchDesc->frameGenOutputImage, fgOutDesc, kInterpolatedName, FFX_RESOURCE_STATE_UNORDERED_ACCESS);
#else
fgDispatch.presentColor = ctx->fns.getResourceVK(reinterpret_cast<void*>(dispatchDesc->outputImage), presentDesc, kPresentName, FFX_RESOURCE_STATE_COMPUTE_READ);
fgDispatch.outputs[0] = ctx->fns.getResourceVK(reinterpret_cast<void*>(dispatchDesc->frameGenOutputImage), fgOutDesc, kInterpolatedName, FFX_RESOURCE_STATE_UNORDERED_ACCESS);
#endif
}
fgDispatch.numInterpolatedFrames = 1;
fgDispatch.reset = (dispatchDesc->reset != 0u);

View file

@ -119,13 +119,7 @@ static int envIntOrDefault(const char* key, int defaultValue) {
#if defined(_WIN32)
static uint64_t exportImageMemoryHandleWin32(VkDevice device, PFN_vkGetDeviceProcAddr getDeviceProcAddr,
VmaAllocator allocator, const AllocatedImage& image) {
#if !defined(VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR) || !defined(VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT)
(void)device;
(void)getDeviceProcAddr;
(void)allocator;
(void)image;
return 0;
#else
#if defined(VK_USE_PLATFORM_WIN32_KHR) && defined(VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR) && defined(VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT)
if (!device || !getDeviceProcAddr || !allocator || !image.allocation) return 0;
auto getMemHandle = reinterpret_cast<PFN_vkGetMemoryWin32HandleKHR>(
getDeviceProcAddr(device, "vkGetMemoryWin32HandleKHR"));
@ -143,17 +137,18 @@ static uint64_t exportImageMemoryHandleWin32(VkDevice device, PFN_vkGetDevicePro
HANDLE outHandle = nullptr;
if (getMemHandle(device, &handleInfo, &outHandle) != VK_SUCCESS || !outHandle) return 0;
return reinterpret_cast<uint64_t>(outHandle);
#else
(void)device;
(void)getDeviceProcAddr;
(void)allocator;
(void)image;
return 0;
#endif
}
static uint64_t exportSemaphoreHandleWin32(VkDevice device, PFN_vkGetDeviceProcAddr getDeviceProcAddr,
VkSemaphore semaphore) {
#if !defined(VK_STRUCTURE_TYPE_SEMAPHORE_GET_WIN32_HANDLE_INFO_KHR) || !defined(VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT)
(void)device;
(void)getDeviceProcAddr;
(void)semaphore;
return 0;
#else
#if defined(VK_USE_PLATFORM_WIN32_KHR) && defined(VK_STRUCTURE_TYPE_SEMAPHORE_GET_WIN32_HANDLE_INFO_KHR) && defined(VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT)
if (!device || !getDeviceProcAddr || !semaphore) return 0;
auto getSemHandle = reinterpret_cast<PFN_vkGetSemaphoreWin32HandleKHR>(
getDeviceProcAddr(device, "vkGetSemaphoreWin32HandleKHR"));
@ -167,6 +162,11 @@ static uint64_t exportSemaphoreHandleWin32(VkDevice device, PFN_vkGetDeviceProcA
HANDLE outHandle = nullptr;
if (getSemHandle(device, &handleInfo, &outHandle) != VK_SUCCESS || !outHandle) return 0;
return reinterpret_cast<uint64_t>(outHandle);
#else
(void)device;
(void)getDeviceProcAddr;
(void)semaphore;
return 0;
#endif
}
#endif
@ -4001,16 +4001,7 @@ bool Renderer::initFSR2Resources() {
fsr2_.amdFsr3FramegenRuntimeReady = fsr2_.amdFsr3Runtime->initialize(fgInit);
if (fsr2_.amdFsr3FramegenRuntimeReady) {
fsr2_.amdFsr3RuntimeLastError.clear();
if (fsr2_.amdFsr3Runtime->loadPathKind() == AmdFsr3Runtime::LoadPathKind::Wrapper) {
const std::string& wrapperBackend = fsr2_.amdFsr3Runtime->wrapperBackendName();
if (!wrapperBackend.empty()) {
fsr2_.amdFsr3RuntimePath = "Path B (" + wrapperBackend + ")";
} else {
fsr2_.amdFsr3RuntimePath = "Path B";
}
} else {
fsr2_.amdFsr3RuntimePath = "Path A";
}
fsr2_.amdFsr3RuntimePath = "Path A";
LOG_INFO("FSR3 framegen runtime library loaded from ", fsr2_.amdFsr3Runtime->loadedLibraryPath(),
" (upscale+framegen dispatch enabled)");
} else {