diff --git a/README.md b/README.md index 36e0838a..fe537bae 100644 --- a/README.md +++ b/README.md @@ -218,6 +218,9 @@ make -j$(nproc) - Set to `0` to skip adapter/device preflight. - Windows `dx12_bridge` preflight checks Vulkan Win32 interop funcs/extensions before enabling DX12 path. - Linux `dx12_bridge` is enabled for wrapper runtime compatibility mode and uses Vulkan dispatch symbols in this build. + - Linux `dx12_bridge` preflight validates Vulkan FD interop funcs/extensions: + - `vkGetMemoryFdKHR`, `vkGetSemaphoreFdKHR` + - `VK_KHR_external_memory`, `VK_KHR_external_memory_fd`, `VK_KHR_external_semaphore`, `VK_KHR_external_semaphore_fd` - Path B wrapper libraries must export the clean wrapper ABI (`include/rendering/amd_fsr3_wrapper_abi.h`): - ABI version is currently `3` (dispatch includes external-memory/semaphore handles plus acquire/release fence values for bridge sync). - `wowee_fsr3_wrapper_get_abi_version` diff --git a/docs/AMD_FSR2_INTEGRATION.md b/docs/AMD_FSR2_INTEGRATION.md index 3e76265e..681cba10 100644 --- a/docs/AMD_FSR2_INTEGRATION.md +++ b/docs/AMD_FSR2_INTEGRATION.md @@ -59,6 +59,9 @@ Runtime note: - `dx12_bridge` is opt-in. - On Windows: `dx12_bridge` performs DX12/Vulkan preflight, then loads the first runtime library exposing the required FSR3 dispatch exports. - On Linux: `dx12_bridge` is enabled for wrapper runtime compatibility mode and uses Vulkan dispatch symbols in this build. +- Linux bridge preflight validates Vulkan FD interop support: + - required device functions: `vkGetMemoryFdKHR`, `vkGetSemaphoreFdKHR` + - required device extensions: `VK_KHR_external_memory`, `VK_KHR_external_memory_fd`, `VK_KHR_external_semaphore`, `VK_KHR_external_semaphore_fd` - 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 a5302c39..005a7eb8 100644 --- a/src/rendering/amd_fsr3_wrapper_impl.cpp +++ b/src/rendering/amd_fsr3_wrapper_impl.cpp @@ -94,6 +94,66 @@ bool hasExternalImageInteropSupport(VkPhysicalDevice physicalDevice, VkFormat fo } #endif +#if defined(__linux__) +bool hasExtensionLinux(const std::vector& extensions, const char* name) { + for (const VkExtensionProperties& ext : extensions) { + if (std::strcmp(ext.extensionName, name) == 0) return true; + } + return false; +} + +bool runLinuxBridgePreflight(const WoweeFsr3WrapperInitDesc* initDesc, std::string& errorMessage) { + std::vector missing; + if (!initDesc || !initDesc->device || !initDesc->getDeviceProcAddr || !initDesc->physicalDevice) { + missing.emplace_back("valid Vulkan device/getDeviceProcAddr"); + } else { + const char* requiredVkInteropFns[] = { + "vkGetMemoryFdKHR", + "vkGetSemaphoreFdKHR" + }; + for (const char* fn : requiredVkInteropFns) { + PFN_vkVoidFunction fp = initDesc->getDeviceProcAddr(initDesc->device, fn); + if (!fp) missing.emplace_back(fn); + } + + uint32_t extCount = 0; + VkResult extErr = vkEnumerateDeviceExtensionProperties(initDesc->physicalDevice, nullptr, &extCount, nullptr); + if (extErr != VK_SUCCESS || extCount == 0) { + missing.emplace_back("vkEnumerateDeviceExtensionProperties"); + } else { + std::vector extensions(extCount); + extErr = vkEnumerateDeviceExtensionProperties( + initDesc->physicalDevice, nullptr, &extCount, extensions.data()); + if (extErr != VK_SUCCESS) { + missing.emplace_back("vkEnumerateDeviceExtensionProperties(data)"); + } else { + const char* requiredVkExtensions[] = { + VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME, + VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME, + VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME, + VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME + }; + for (const char* extName : requiredVkExtensions) { + if (!hasExtensionLinux(extensions, extName)) { + missing.emplace_back(extName); + } + } + } + } + } + + if (missing.empty()) return true; + std::ostringstream oss; + oss << "dx12_bridge preflight failed (linux), missing: "; + for (size_t i = 0; i < missing.size(); ++i) { + if (i) oss << ", "; + oss << missing[i]; + } + errorMessage = oss.str(); + return false; +} +#endif + enum class WrapperBackend { VulkanRuntime, Dx12Bridge @@ -658,9 +718,11 @@ WOWEE_FSR3_WRAPPER_EXPORT int32_t wowee_fsr3_wrapper_initialize(const WoweeFsr3W return -1; } #elif defined(__linux__) - // Linux bridge mode currently routes dispatch through Vulkan runtime symbols - // while preserving external interop metadata for wrapper-side diagnostics. - (void)initDesc; + std::string preflightError; + if (!runLinuxBridgePreflight(initDesc, preflightError)) { + writeError(outErrorText, outErrorTextCapacity, preflightError.c_str()); + return -1; + } #endif } @@ -953,6 +1015,24 @@ WOWEE_FSR3_WRAPPER_EXPORT int32_t wowee_fsr3_wrapper_dispatch_upscale(WoweeFsr3W return -1; } } +#elif defined(__linux__) + if (ctx->backend == WrapperBackend::Dx12Bridge) { + const uint32_t requiredMask = + WOWEE_FSR3_WRAPPER_EXTERNAL_COLOR_MEMORY | + WOWEE_FSR3_WRAPPER_EXTERNAL_DEPTH_MEMORY | + WOWEE_FSR3_WRAPPER_EXTERNAL_MOTION_MEMORY | + WOWEE_FSR3_WRAPPER_EXTERNAL_OUTPUT_MEMORY | + WOWEE_FSR3_WRAPPER_EXTERNAL_ACQUIRE_SEMAPHORE | + WOWEE_FSR3_WRAPPER_EXTERNAL_RELEASE_SEMAPHORE; + if ((dispatchDesc->externalFlags & requiredMask) != requiredMask || + dispatchDesc->colorMemoryHandle == 0 || dispatchDesc->depthMemoryHandle == 0 || + dispatchDesc->motionVectorMemoryHandle == 0 || dispatchDesc->outputMemoryHandle == 0 || + dispatchDesc->acquireSemaphoreHandle == 0 || dispatchDesc->releaseSemaphoreHandle == 0 || + dispatchDesc->acquireSemaphoreValue == 0 || dispatchDesc->releaseSemaphoreValue == 0) { + setContextError(ctx, "dx12_bridge dispatch missing required external FDs for upscale"); + return -1; + } + } #endif FfxResourceDescription colorDesc = makeResourceDescription( @@ -1135,6 +1215,21 @@ WOWEE_FSR3_WRAPPER_EXPORT int32_t wowee_fsr3_wrapper_dispatch_framegen(WoweeFsr3 return -1; } } +#elif defined(__linux__) + if (ctx->backend == WrapperBackend::Dx12Bridge) { + const uint32_t requiredMask = + WOWEE_FSR3_WRAPPER_EXTERNAL_OUTPUT_MEMORY | + WOWEE_FSR3_WRAPPER_EXTERNAL_FRAMEGEN_OUTPUT_MEMORY | + WOWEE_FSR3_WRAPPER_EXTERNAL_ACQUIRE_SEMAPHORE | + WOWEE_FSR3_WRAPPER_EXTERNAL_RELEASE_SEMAPHORE; + if ((dispatchDesc->externalFlags & requiredMask) != requiredMask || + dispatchDesc->outputMemoryHandle == 0 || dispatchDesc->frameGenOutputMemoryHandle == 0 || + dispatchDesc->acquireSemaphoreHandle == 0 || dispatchDesc->releaseSemaphoreHandle == 0 || + dispatchDesc->acquireSemaphoreValue == 0 || dispatchDesc->releaseSemaphoreValue == 0) { + setContextError(ctx, "dx12_bridge dispatch missing required external FDs for frame generation"); + return -1; + } + } #endif FfxResourceDescription presentDesc = makeResourceDescription(