[PATCH v2 0/2] MR10523: winemac.drv: Try to get GPU VendorId/DeviceId from Vulkan/MoltenVK.
Some PlayStation PC SDK games get the vendor id and device id from dxgi adapter's GetDesc() and then construct a path based on it and try to find out the matched device from 'HKLM\System\CurrentControlSet\Enum\PCI' as an active GPU. Currently, device id can be inconsistent in winemac.drv and wined3d for Apple Silicon on macOS. The first patch tries to query it from Vulkan to make it consistent with the device id in wined3d. -- v2: wined3d/vk: Make deviceID always PCI-compatible. winemac.drv: Try to get GPU VendorId/DeviceId from Vulkan/MoltenVK. https://gitlab.winehq.org/wine/wine/-/merge_requests/10523
From: Jactry Zeng <jzeng@codeweavers.com> This is trying to fix a similar issue that commit 731f06d9 tried to fix. That commit generates a device id for Apple GPUs, but the device id can be inconsistent with the device id in wined3d, which queries it from Vulkan. Because the scheme that MoltenVK used to generate the device id can be changed. This commit only takes the low 16-bit of the device id returned by MoltenVK. --- configure.ac | 10 ++ dlls/winemac.drv/Makefile.in | 1 + dlls/winemac.drv/cocoa_display.m | 188 +++++++++++++++++++++++++++++++ include/config.h.in | 3 + 4 files changed, 202 insertions(+) diff --git a/configure.ac b/configure.ac index 88cca33bcd6..4de57e24d3b 100644 --- a/configure.ac +++ b/configure.ac @@ -1952,10 +1952,20 @@ then if test "x$ac_cv_lib_soname_vulkan" = "x" then WINE_CHECK_SONAME(MoltenVK, vkGetInstanceProcAddr, [AC_DEFINE_UNQUOTED(SONAME_LIBVULKAN,["$ac_cv_lib_soname_MoltenVK"])]) + if test "x$ac_cv_lib_soname_MoltenVK" != "x" + then + WINE_PACKAGE_FLAGS(VULKAN_METAL,[vulkan],,,, + [AC_CHECK_HEADERS([vulkan/vulkan_metal.h],,, + [#include <vulkan/vulkan.h>])]) + fi fi fi WINE_NOTICE_WITH(vulkan,[test "x$ac_cv_lib_soname_vulkan" = "x" -a "x$ac_cv_lib_soname_MoltenVK" = "x"], [libvulkan and libMoltenVK ${notice_platform}development files not found, Vulkan won't be supported.]) +if test "x$ac_cv_lib_soname_MoltenVK" != "x" -a "x$ac_cv_header_vulkan_vulkan_metal_h" != "xyes" +then + WINE_NOTICE([Vulkan Metal ${notice_platform}development files not found, features that depends on Vulkan Metal extensions won't be available.]) +fi dnl **** Check for gcc specific options **** diff --git a/dlls/winemac.drv/Makefile.in b/dlls/winemac.drv/Makefile.in index 576a2fbd0c5..0831a7bf743 100644 --- a/dlls/winemac.drv/Makefile.in +++ b/dlls/winemac.drv/Makefile.in @@ -2,6 +2,7 @@ MODULE = winemac.drv UNIXLIB = winemac.so IMPORTS = uuid rpcrt4 user32 gdi32 win32u DELAYIMPORTS = ole32 shell32 imm32 +UNIX_CFLAGS = $(VULKAN_METAL_CFLAGS) UNIX_LIBS = \ -lwin32u \ -framework AppKit \ diff --git a/dlls/winemac.drv/cocoa_display.m b/dlls/winemac.drv/cocoa_display.m index d22ada8c804..b9a8b5ea352 100644 --- a/dlls/winemac.drv/cocoa_display.m +++ b/dlls/winemac.drv/cocoa_display.m @@ -25,9 +25,14 @@ #ifdef HAVE_MTLDEVICE_REGISTRYID #import <Metal/Metal.h> #endif +#ifdef HAVE_VULKAN_VULKAN_METAL_H +#include <vulkan/vulkan.h> +#include <vulkan/vulkan_metal.h> +#endif #include <dlfcn.h> #include "macdrv_cocoa.h" + #pragma GCC diagnostic ignored "-Wdeclaration-after-statement" static uint64_t dedicated_gpu_id; @@ -192,6 +197,183 @@ static int macdrv_get_gpu_info_from_registry_id(struct macdrv_gpu* gpu, uint64_t return ret; } +#ifdef HAVE_VULKAN_VULKAN_METAL_H +static int macdrv_get_gpu_info_from_vulkan(struct macdrv_gpu *gpu, id<MTLDevice> mtldevice) +{ + static PFN_vkGetPhysicalDeviceQueueFamilyProperties p_vkGetPhysicalDeviceQueueFamilyProperties; + static PFN_vkGetPhysicalDeviceProperties p_vkGetPhysicalDeviceProperties; + static PFN_vkEnumeratePhysicalDevices p_vkEnumeratePhysicalDevices; + static PFN_vkExportMetalObjectsEXT p_vkExportMetalObjectsEXT; + static PFN_vkGetInstanceProcAddr p_vkGetInstanceProcAddr; + static PFN_vkGetDeviceProcAddr p_vkGetDeviceProcAddr; + static PFN_vkDestroyInstance p_vkDestroyInstance; + static PFN_vkCreateInstance p_vkCreateInstance; + static PFN_vkDestroyDevice p_vkDestroyDevice; + static PFN_vkCreateDevice p_vkCreateDevice; + VkExportMetalObjectCreateInfoEXT export_device_create_info = { + .sType = VK_STRUCTURE_TYPE_EXPORT_METAL_OBJECT_CREATE_INFO_EXT, + .pNext = NULL, + .exportObjectType = VK_EXPORT_METAL_OBJECT_TYPE_METAL_DEVICE_BIT_EXT, + }; + VkApplicationInfo app_info = { + .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, + .pNext = NULL, + .apiVersion = VK_API_VERSION_1_0 + }; + VkInstanceCreateInfo create_info = { + .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, + .pNext = &export_device_create_info, + .pApplicationInfo = &app_info, + .enabledExtensionCount = 0, + .enabledLayerCount = 0 + }; + VkExportMetalDeviceInfoEXT device_info_ext = { + .sType = VK_STRUCTURE_TYPE_EXPORT_METAL_DEVICE_INFO_EXT, + .pNext = NULL, + .mtlDevice = NULL + }; + VkExportMetalObjectsInfoEXT export_info = { + .sType = VK_STRUCTURE_TYPE_EXPORT_METAL_OBJECTS_INFO_EXT, + .pNext = &device_info_ext + }; + static void *vulkan_handle = NULL; + VkInstance vk_instance = NULL; + uint32_t device_count = 0; + VkResult result; + int ret = -1; + + vulkan_handle = dlopen(SONAME_LIBVULKAN, RTLD_LAZY | RTLD_LOCAL); + if (!vulkan_handle) return ret; + + p_vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)dlsym(vulkan_handle, "vkGetInstanceProcAddr"); + if (!p_vkGetInstanceProcAddr) + goto done; + + p_vkCreateInstance = (PFN_vkCreateInstance)p_vkGetInstanceProcAddr(NULL, "vkCreateInstance"); + if (!p_vkCreateInstance || p_vkCreateInstance(&create_info, NULL, &vk_instance)) + goto done; + + p_vkDestroyInstance = (PFN_vkDestroyInstance)p_vkGetInstanceProcAddr(vk_instance, "vkDestroyInstance"); + if (!p_vkDestroyInstance) + goto done; + +#define LOAD_VK_FUNC(f) \ + if (!(p_##f = (void *)p_vkGetInstanceProcAddr(vk_instance, #f))) \ + goto done; \ + + LOAD_VK_FUNC(vkCreateDevice) + LOAD_VK_FUNC(vkDestroyDevice) + LOAD_VK_FUNC(vkEnumeratePhysicalDevices) + LOAD_VK_FUNC(vkGetDeviceProcAddr) + LOAD_VK_FUNC(vkGetPhysicalDeviceProperties) + LOAD_VK_FUNC(vkGetPhysicalDeviceQueueFamilyProperties) +#undef LOAD_VK_FUNC + + result = p_vkEnumeratePhysicalDevices(vk_instance, &device_count, NULL); + if (result != VK_SUCCESS) + goto done; + + if (device_count > 0) + { + const char *extensions[] = {"VK_EXT_metal_objects"}; + uint32_t extensions_count = sizeof(extensions) / sizeof(extensions[0]); + uint32_t family_count, i, family_index; + VkDeviceQueueCreateInfo queue_create_info = { + .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, + .queueCount = 1 + }; + VkPhysicalDeviceFeatures device_features = {0}; + VkDeviceCreateInfo device_create_info = { + .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, + .pQueueCreateInfos = &queue_create_info, + .queueCreateInfoCount = 1, + .pEnabledFeatures = &device_features, + .enabledExtensionCount = extensions_count, + .ppEnabledExtensionNames = extensions, + .enabledLayerCount = 0 + }; + VkPhysicalDeviceProperties properties; + VkQueueFamilyProperties *families; + VkPhysicalDevice *devices; + VkDevice vk_device = NULL; + float priority; + + devices = (VkPhysicalDevice *)malloc(sizeof(VkPhysicalDevice) * device_count); + if (!devices) + goto done; + + result = p_vkEnumeratePhysicalDevices(vk_instance, &device_count, devices); + if (result != VK_SUCCESS) + { + free(devices); + goto done; + } + + while (device_count--) + { + family_index = UINT32_MAX; + p_vkGetPhysicalDeviceQueueFamilyProperties(devices[device_count], &family_count, NULL); + if (!family_count) + continue; + + families = (VkQueueFamilyProperties*)malloc(sizeof(VkQueueFamilyProperties) * family_count); + if (!families) + continue; + + p_vkGetPhysicalDeviceQueueFamilyProperties(devices[device_count], &family_count, families); + + for (i = 0; i < family_count; i++) + { + if (families[i].queueCount && (families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)) + { + family_index = i; + break; + } + } + free(families); + + if (family_index == UINT32_MAX) + continue; + + priority = 1.0f; + queue_create_info.queueFamilyIndex = family_index; + queue_create_info.pQueuePriorities = &priority; + result = p_vkCreateDevice(devices[device_count], &device_create_info, NULL, &vk_device); + if (result != VK_SUCCESS) + continue; + + p_vkExportMetalObjectsEXT = (PFN_vkExportMetalObjectsEXT)p_vkGetDeviceProcAddr(vk_device, + "vkExportMetalObjectsEXT"); + if (!p_vkExportMetalObjectsEXT) + { + p_vkDestroyDevice(vk_device, NULL); + continue; + } + + device_info_ext.mtlDevice = NULL; + p_vkExportMetalObjectsEXT(vk_device, &export_info); + if ([device_info_ext.mtlDevice isEqual:mtldevice]) + { + p_vkGetPhysicalDeviceProperties(devices[device_count], &properties); + gpu->vendor_id = properties.vendorID; + gpu->device_id = properties.deviceID & 0xffff; + ret = 0; + p_vkDestroyDevice(vk_device, NULL); + break; + } + + p_vkDestroyDevice(vk_device, NULL); + } + free(devices); + } + +done: + if (vk_instance) p_vkDestroyInstance(vk_instance, NULL); + if (vulkan_handle) dlclose(vulkan_handle); + return ret; +} +#endif + /*********************************************************************** * macdrv_get_gpu_info_from_mtldevice * @@ -204,6 +386,12 @@ static int macdrv_get_gpu_info_from_mtldevice(struct macdrv_gpu* gpu, id<MTLDevi int ret; if ((ret = macdrv_get_gpu_info_from_registry_id(gpu, [device registryID]))) return ret; + +#ifdef HAVE_VULKAN_VULKAN_METAL_H + if (!macdrv_get_gpu_info_from_vulkan(gpu, device)) + return 0; +#endif + #if defined(MAC_OS_X_VERSION_10_15) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_15 /* Apple GPUs aren't PCI devices and therefore have no device ID * Use the Metal GPUFamily as the device ID */ diff --git a/include/config.h.in b/include/config.h.in index 0343acc8d36..0990c801b1d 100644 --- a/include/config.h.in +++ b/include/config.h.in @@ -663,6 +663,9 @@ /* Define to 1 if you have the <valgrind/valgrind.h> header file. */ #undef HAVE_VALGRIND_VALGRIND_H +/* Define to 1 if you have the <vulkan/vulkan_metal.h> header file. */ +#undef HAVE_VULKAN_VULKAN_METAL_H + /* Define to 1 if the Wine preloader is being used. */ #undef HAVE_WINE_PRELOADER -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10523
From: Jactry Zeng <jzeng@codeweavers.com> The current version of MoltenVK returns a self-generated 32-bit device id for Apple GPUs; this isn't compatible with PCI's 16-bit device id. --- dlls/wined3d/adapter_vk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/wined3d/adapter_vk.c b/dlls/wined3d/adapter_vk.c index 55c7b167156..cfaf48f16fa 100644 --- a/dlls/wined3d/adapter_vk.c +++ b/dlls/wined3d/adapter_vk.c @@ -2190,7 +2190,7 @@ static bool adapter_vk_init_driver_info(struct wined3d_adapter_vk *adapter_vk, debugstr_a(properties->deviceName), properties->vendorID, properties->deviceID); description.vendor = properties->vendorID; - description.device = properties->deviceID; + description.device = properties->deviceID & 0xffff; description.description = properties->deviceName; description.driver = guess_display_driver(properties->vendorID); description.vidmem = vram_bytes; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10523
This isn't going to quite be enough to get it consistent in all cases. This may need more thought. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10523#note_134560
On Tue Apr 7 09:12:15 2026 +0000, Elizabeth Figura wrote:
This isn't going to quite be enough to get it consistent in all cases. This may need more thought. Hi Zeb,
Are there any known cases that won't be satisfied by this approach? AFAIS, this isn't ideal that wined3d and winemac.drv (also winex11.drv) queries these IDs from Vulkan separately, so if something goes wrong in winemac.drv, but success in wined3d (or reverse), then these IDs can be inconsistent again. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10523#note_135282
participants (3)
-
Elizabeth Figura (@zfigura) -
Jactry Zeng -
Jactry Zeng (@jactry)