Check Vulkan Win32 interop extensions in DX12 bridge preflight

This commit is contained in:
Kelsi 2026-03-09 00:58:43 -07:00
parent fd2ca42f28
commit 19bc52f54e
3 changed files with 48 additions and 11 deletions

View file

@ -216,6 +216,7 @@ make -j$(nproc)
- DX12 device validation probe (default on):
- `WOWEE_FSR3_WRAPPER_DX12_VALIDATE_DEVICE=1`
- Set to `0` to skip adapter/device preflight.
- Bridge preflight also checks Vulkan Win32 interop funcs/extensions before enabling DX12 path.
- Path B wrapper libraries must export the clean wrapper ABI (`include/rendering/amd_fsr3_wrapper_abi.h`):
- `wowee_fsr3_wrapper_get_abi_version`
- `wowee_fsr3_wrapper_initialize`

View file

@ -60,6 +60,9 @@ Runtime note:
- DX12 bridge device preflight toggle:
- `WOWEE_FSR3_WRAPPER_DX12_VALIDATE_DEVICE=1` (default)
- `WOWEE_FSR3_WRAPPER_DX12_VALIDATE_DEVICE=0` to skip DXGI/D3D12 device creation probe
- DX12 bridge preflight also validates Vulkan Win32 interop support:
- required device functions: `vkGetMemoryWin32HandleKHR`, `vkImportSemaphoreWin32HandleKHR`, `vkGetSemaphoreWin32HandleKHR`
- required device extensions: `VK_KHR_external_memory`, `VK_KHR_external_memory_win32`, `VK_KHR_external_semaphore`, `VK_KHR_external_semaphore_win32`
- Path B wrapper ABI contract is declared in:
- `include/rendering/amd_fsr3_wrapper_abi.h`
- Required wrapper exports:

View file

@ -40,6 +40,24 @@ void writeError(char* outErrorText, uint32_t outErrorTextCapacity, const char* m
}
#if WOWEE_HAS_AMD_FSR3_FRAMEGEN
#if defined(_WIN32)
bool envEnabled(const char* key, bool defaultValue) {
const char* val = std::getenv(key);
if (!val || !*val) return defaultValue;
std::string s(val);
std::transform(s.begin(), s.end(), s.begin(),
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
return !(s == "0" || s == "false" || s == "off" || s == "no");
}
bool hasExtension(const std::vector<VkExtensionProperties>& extensions, const char* name) {
for (const VkExtensionProperties& ext : extensions) {
if (std::strcmp(ext.extensionName, name) == 0) return true;
}
return false;
}
#endif
enum class WrapperBackend {
VulkanRuntime,
Dx12Bridge
@ -261,7 +279,7 @@ bool runDx12BridgePreflight(const WoweeFsr3WrapperInitDesc* initDesc, std::strin
}
}
if (!initDesc || !initDesc->device || !initDesc->getDeviceProcAddr) {
if (!initDesc || !initDesc->device || !initDesc->getDeviceProcAddr || !initDesc->physicalDevice) {
missing.emplace_back("valid Vulkan device/getDeviceProcAddr");
} else {
const char* requiredVkInteropFns[] = {
@ -275,6 +293,31 @@ bool runDx12BridgePreflight(const WoweeFsr3WrapperInitDesc* initDesc, std::strin
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_WIN32_EXTENSION_NAME,
VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME,
VK_KHR_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME
};
for (const char* extName : requiredVkExtensions) {
if (!hasExtension(extensions, extName)) {
missing.emplace_back(extName);
}
}
}
}
}
std::vector<std::string> runtimeCandidates;
@ -652,13 +695,3 @@ WOWEE_FSR3_WRAPPER_EXPORT void wowee_fsr3_wrapper_shutdown(WoweeFsr3WrapperConte
(void)context;
#endif
}
#if defined(_WIN32)
bool envEnabled(const char* key, bool defaultValue) {
const char* val = std::getenv(key);
if (!val || !*val) return defaultValue;
std::string s(val);
std::transform(s.begin(), s.end(), s.begin(),
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
return !(s == "0" || s == "false" || s == "off" || s == "no");
}
#endif