Add Linux bridge preflight and FD-handle validation

This commit is contained in:
Kelsi 2026-03-09 02:33:18 -07:00
parent dc29616513
commit 91f83cb108
3 changed files with 104 additions and 3 deletions

View file

@ -218,6 +218,9 @@ make -j$(nproc)
- Set to `0` to skip adapter/device preflight. - Set to `0` to skip adapter/device preflight.
- Windows `dx12_bridge` preflight checks Vulkan Win32 interop funcs/extensions before enabling DX12 path. - 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` 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`): - 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). - 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` - `wowee_fsr3_wrapper_get_abi_version`

View file

@ -59,6 +59,9 @@ Runtime note:
- `dx12_bridge` is opt-in. - `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 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. - 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: - DX12 bridge runtime override:
- `WOWEE_FSR3_DX12_RUNTIME_LIB=<path-to-amd_fidelityfx_framegeneration_dx12.dll>` - `WOWEE_FSR3_DX12_RUNTIME_LIB=<path-to-amd_fidelityfx_framegeneration_dx12.dll>`
- DX12 bridge device preflight toggle: - DX12 bridge device preflight toggle:

View file

@ -94,6 +94,66 @@ bool hasExternalImageInteropSupport(VkPhysicalDevice physicalDevice, VkFormat fo
} }
#endif #endif
#if defined(__linux__)
bool hasExtensionLinux(const std::vector<VkExtensionProperties>& 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<std::string> 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<VkExtensionProperties> 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 { enum class WrapperBackend {
VulkanRuntime, VulkanRuntime,
Dx12Bridge Dx12Bridge
@ -658,9 +718,11 @@ WOWEE_FSR3_WRAPPER_EXPORT int32_t wowee_fsr3_wrapper_initialize(const WoweeFsr3W
return -1; return -1;
} }
#elif defined(__linux__) #elif defined(__linux__)
// Linux bridge mode currently routes dispatch through Vulkan runtime symbols std::string preflightError;
// while preserving external interop metadata for wrapper-side diagnostics. if (!runLinuxBridgePreflight(initDesc, preflightError)) {
(void)initDesc; writeError(outErrorText, outErrorTextCapacity, preflightError.c_str());
return -1;
}
#endif #endif
} }
@ -953,6 +1015,24 @@ WOWEE_FSR3_WRAPPER_EXPORT int32_t wowee_fsr3_wrapper_dispatch_upscale(WoweeFsr3W
return -1; 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 #endif
FfxResourceDescription colorDesc = makeResourceDescription( FfxResourceDescription colorDesc = makeResourceDescription(
@ -1135,6 +1215,21 @@ WOWEE_FSR3_WRAPPER_EXPORT int32_t wowee_fsr3_wrapper_dispatch_framegen(WoweeFsr3
return -1; 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 #endif
FfxResourceDescription presentDesc = makeResourceDescription( FfxResourceDescription presentDesc = makeResourceDescription(