On Tue, Mar 10, 2020 at 7:00 AM Zhiyi Zhang zzhang@codeweavers.com wrote:
On 3/10/20 11:49 AM, Zhiyi Zhang wrote:
On 3/10/20 5:12 AM, Brendan Shanks wrote:
Hi Zhiyi,
Thanks for your overview, that’s very helpful and makes sense. Red Dead Redemption 2 is using the LUID to interop with Win32, so a unified view of GPUs at the winex11/winemac level is necessary. And QueryDisplayConfig() is built around LUIDs, these changes likely need to happen before it can be properly implemented.
For Roderick and others, here’s what Red Dead Redemption 2 uses:
- the Vulkan-provided LUID must match up with the LUID returned by QueryDisplayConfig(). DisplayConfigGetDeviceInfo() is also called with that LUID to get the source’s GDI device name (i.e. ‘\.\DISPLAY1”), this must match the DeviceName returned by EnumDisplayDevices().
- The PCI vendor/device IDs are also extracted from the DeviceID string returned by EnumDisplayDevices(), and need to match the IDs returned by Vulkan. (This is another reason to have winex11 use Vulkan to enumerate GPUs, winex11 currently has no VID/PID info)
Using Vulkan to enumerate GPUs in winex11.drv doesn't work. Winex11.drv needs to know which output is connected to which GPU and Vulkan currently doesn't support that the last time I checked.
What information do you think is missing? Vulkan has a level of display device enumeration (I haven't personally used it though). The challenge might be mapping this back to X11 displays and screens. Though I guess there is a way (worst case using some kind of self-test..)
- EnumDisplaySettings() is then called, this is where the game gets screen resolutions.
Zhiyi, have you done any further work on these patches since 2018?
Only PCI ID support for winex11.drv. For LUID, nothing much. I was stuck in figuring out how to handle the multi-GPU case. I guess now that PCI ID can be implemented in winex11.drv, we can start by implementing LUID partially, Then work on OpenGL/Vulkan/XRandR if an extension is really needed. I could be missing something that can be used as a unique identifier.
I went through my notes again and found that there is a VK_EXT_pci_bus_info extension on Vulkan that can be used as unique identifier. With VK_EXT_pci_bus_info and the PCI device function address(/sys/class/drm/card0/device) from DRM device, which can be retrieved from RandR provider ID on Linux. So I think we have enough information now to implement this properly.
Why do we have to go all the way to the DRM level at sysfs? I guess to correlate GL adapters and Vulkan adapters as the numbers don't line up exactly.. Though at least newer GPUs should all have Vulkan and typically Vulkan GPUs can do data sharing with OpenGL. Maybe something can be done there to make them "the same". Maybe there are only really enumation challenges on non-Vulkan capable systems... honestly I need to dig in a bit deeper, haven't looke at the details in some time.
And OpenGL doesn't support multi-GPU unless we use vendor specific extensions. So I guess when wined3d is backed by OpenGL. We can still assume there is one GPU.
We can do limited multi GPU detection using OpenGL. I think you should be able to iterate over the number of X screens within the current X11 session and then have to do a "diagnostic test" by creating a GLX context. If the GPU renderer strings don't match, you are dealing with a different GPU. At least you can handle the relatively common laptop scenario in which there is an integrated and a discrete GPU. Vendor specific methods would be needed in case the two GPUs are identical.
Regarding MacOS, it should also be possible to retrieve bus info from IOKit as well.
Thanks, Zhiyi
Brendan
On Mar 7, 2020, at 12:23 AM, Zhiyi Zhang zzhang@codeweavers.com wrote:
Hi Brendan,
LUIDs should be allocated in winex11/winemac user graphics drivers for each GPU, and store them in SetupAPI. So that applications querying through SetupAPI or registry have a unified view. A GUID is currently allocated for each GPU[1], so it's easy to do the same for LUIDs as well. LUIDs can be stored as a SetupAPI device property[2] using the DEVPROPKEY_DISPLAY_ADAPTER_LUID key.
Winevulkan/OpenGL should build on SetupAPI to query LUIDs for each physical device. Specifically, winevulkan thunks vkGetPhysicalDeviceProperties2 to fill in LUID. OpenGL in wine implements EXT_external_objects_win32[3] to report LUID.
Then wined3d can use LUIDs from winevulkan/OpenGL to fill in LUID for wined3d adapters. Finally, DXGI gets adapter LUID from wined3d.
For a single GPU system. We can even use adapter ordinals as LUID(Proton hack). A problem arises when multiple GPUs are present, we need to establish one-to-one relationships for devices in each layer so that LUIDs can be used to uniquely identify a device. Now, this is where things get tricky.
In winex11, GPUs are enumerated via XRandR extension. GPUs listed in XRandR mean they're GDI-capable, but not necessarily OpenGL/Vulkan capable. So there may be more GPUs in winex11 than in OpenGL/Vulkan. Also, the order of GPUs that get enumerated in winex11 might be different from that in OpenGL/Vulkan so you can't match them reliably. PCI IDs[4] help to guess the relationships but it still has problems when GPUs have the same model. In essence, we need something like LUID in native OpenGL/Vulkan/XRandR to match them, which I haven't found any. So to solve this ultimately, a new extension in OpenGL/Vulkan/XRandR may to be introduced.
There're some discussions about LUID before in the wine-devel mail list[5]. That should give you more insight.
Thanks, Zhiyi
On 3/7/20 7:23 AM, Brendan Shanks wrote:
Red Dead Redemption 2 relies on the deviceLUID returned by vkGetPhysicalDeviceProperties2() matching up with the LUID reported by other Windows APIs. This patch uses DXGI to enumerate adapters, and uses the LUID of the D3D adapter matching the UUID provided through Vulkan.
The patch works, but I could see problems arising in case EnumAdapters() was backed by Vulkan and called vkGetPhysicalDeviceProperties2() itself.
Would it be preferable to cache the UUID/LUID pairs in wine_vk_init()? Or should DXGI not be used at all, in favor of reading these out of the registry (with changes needed to wined3d)?
dlls/winevulkan/Makefile.in | 2 +- dlls/winevulkan/make_vulkan | 2 + dlls/winevulkan/vulkan.c | 89 +++++++++++++++++++++++++++++++++ dlls/winevulkan/vulkan_thunks.c | 10 +--- dlls/winevulkan/vulkan_thunks.h | 4 ++ 5 files changed, 98 insertions(+), 9 deletions(-)
diff --git a/dlls/winevulkan/Makefile.in b/dlls/winevulkan/Makefile.in index e0bca6fad7..c112581e1e 100644 --- a/dlls/winevulkan/Makefile.in +++ b/dlls/winevulkan/Makefile.in @@ -1,6 +1,6 @@ MODULE = winevulkan.dll IMPORTLIB = winevulkan -IMPORTS = user32 gdi32 +IMPORTS = dxgi user32 gdi32
C_SRCS = \ vulkan.c \ diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index 3593410041..1879d640e0 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -165,6 +165,8 @@ FUNCTION_OVERRIDES = { "vkGetPhysicalDeviceExternalFenceProperties" : {"dispatch" : False, "driver" : False, "thunk" : False}, "vkGetPhysicalDeviceExternalSemaphoreProperties" : {"dispatch" : False, "driver" : False, "thunk" : False}, "vkGetPhysicalDeviceImageFormatProperties2" : {"dispatch" : True, "driver" : False, "thunk" : True, "private_thunk" : True},
"vkGetPhysicalDeviceProperties2" : {"dispatch" : True, "driver" : False, "thunk" : False, "private_thunk" : True},
"vkGetPhysicalDeviceProperties2KHR" : {"dispatch" : True, "driver" : False, "thunk" : False, "private_thunk" : True},
# Device functions "vkAllocateCommandBuffers" : {"dispatch" : True, "driver" : False, "thunk" : False},
diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index 59472bcef8..2ef39ada79 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -19,10 +19,16 @@
#include <stdarg.h>
+#define COBJMACROS #include "windef.h" #include "winbase.h" #include "winuser.h"
+#include "initguid.h" +#include "dxgi.h" +#include "wine/wined3d.h" +#include "wine/winedxgi.h"
#include "vulkan_private.h"
WINE_DEFAULT_DEBUG_CHANNEL(vulkan); @@ -1261,6 +1267,89 @@ void WINAPI wine_vkGetPhysicalDeviceExternalSemaphorePropertiesKHR(VkPhysicalDev properties->externalSemaphoreFeatures = 0; }
+static BOOL get_luid_for_device_uuid(const UUID *uuid, LUID *luid) +{
- UINT i = 0;
- BOOL found = FALSE;
- IDXGIFactory *factory = NULL;
- IDXGIAdapter *adapter = NULL;
- if (FAILED(CreateDXGIFactory(&IID_IDXGIFactory, (void **)&factory))) return FALSE;
- while (!found && IDXGIFactory_EnumAdapters(factory, i, &adapter) != DXGI_ERROR_NOT_FOUND)
- {
IWineDXGIAdapter *wine_adapter = NULL;
if (SUCCEEDED(IUnknown_QueryInterface(adapter, &IID_IWineDXGIAdapter, (void **)&wine_adapter)))
{
struct wine_dxgi_adapter_info adapter_info;
if (SUCCEEDED(IWineDXGIAdapter_get_adapter_info(wine_adapter, &adapter_info)))
{
if (IsEqualGUID(uuid, &adapter_info.device_uuid))
{
*luid = adapter_info.luid;
found = TRUE;
}
}
IWineDXGIAdapter_Release(wine_adapter);
}
IDXGIAdapter_Release(adapter);
i++;
- }
- if (factory) IDXGIFactory_Release(factory);
- return found;
+}
+void WINAPI wine_vkGetPhysicalDeviceProperties2(VkPhysicalDevice physical_device,
VkPhysicalDeviceProperties2 *properties)
+{
- VkPhysicalDeviceIDProperties *idprops;
- TRACE("%p, %p\n", physical_device, properties);
- thunk_vkGetPhysicalDeviceProperties2(physical_device, properties);
- if ((idprops = wine_vk_find_struct(properties, PHYSICAL_DEVICE_ID_PROPERTIES)))
- {
UUID *deviceUUID = (UUID *)idprops->deviceUUID;
LUID *luid = (LUID *)idprops->deviceLUID;
if (get_luid_for_device_uuid(deviceUUID, luid))
{
idprops->deviceNodeMask = 1;
idprops->deviceLUIDValid = VK_TRUE;
}
else
{
WARN("Failed to find corresponding adapter LUID for device UUID %s.\n", debugstr_guid(deviceUUID));
}
- }
+}
+void WINAPI wine_vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice physical_device,
VkPhysicalDeviceProperties2 *properties)
+{
- VkPhysicalDeviceIDProperties *idprops;
- TRACE("%p, %p\n", physical_device, properties);
- thunk_vkGetPhysicalDeviceProperties2KHR(physical_device, properties);
- if ((idprops = wine_vk_find_struct(properties, PHYSICAL_DEVICE_ID_PROPERTIES)))
- {
UUID *deviceUUID = (UUID *)idprops->deviceUUID;
LUID *luid = (LUID *)idprops->deviceLUID;
if (get_luid_for_device_uuid(deviceUUID, luid))
{
idprops->deviceNodeMask = 1;
idprops->deviceLUIDValid = VK_TRUE;
}
else
{
WARN("Failed to find corresponding adapter LUID for device UUID %s.\n", debugstr_guid(deviceUUID));
}
- }
+}
BOOL WINAPI DllMain(HINSTANCE hinst, DWORD reason, void *reserved) { TRACE("%p, %u, %p\n", hinst, reason, reserved); diff --git a/dlls/winevulkan/vulkan_thunks.c b/dlls/winevulkan/vulkan_thunks.c index fecf9ab502..89878f189a 100644 --- a/dlls/winevulkan/vulkan_thunks.c +++ b/dlls/winevulkan/vulkan_thunks.c @@ -4268,34 +4268,28 @@ void WINAPI wine_vkGetPhysicalDeviceProperties(VkPhysicalDevice physicalDevice, #endif }
-void WINAPI wine_vkGetPhysicalDeviceProperties2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2 *pProperties) +void thunk_vkGetPhysicalDeviceProperties2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2 *pProperties) { #if defined(USE_STRUCT_CONVERSION) VkPhysicalDeviceProperties2_host pProperties_host;
TRACE("%p, %p\n", physicalDevice, pProperties);
convert_VkPhysicalDeviceProperties2_win_to_host(pProperties, &pProperties_host); physicalDevice->instance->funcs.p_vkGetPhysicalDeviceProperties2(physicalDevice->phys_dev, &pProperties_host);
convert_VkPhysicalDeviceProperties2_host_to_win(&pProperties_host, pProperties);
#else
- TRACE("%p, %p\n", physicalDevice, pProperties); physicalDevice->instance->funcs.p_vkGetPhysicalDeviceProperties2(physicalDevice->phys_dev, pProperties);
#endif }
-static void WINAPI wine_vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2 *pProperties) +void thunk_vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2 *pProperties) { #if defined(USE_STRUCT_CONVERSION) VkPhysicalDeviceProperties2_host pProperties_host;
TRACE("%p, %p\n", physicalDevice, pProperties);
convert_VkPhysicalDeviceProperties2_win_to_host(pProperties, &pProperties_host); physicalDevice->instance->funcs.p_vkGetPhysicalDeviceProperties2KHR(physicalDevice->phys_dev, &pProperties_host);
convert_VkPhysicalDeviceProperties2_host_to_win(&pProperties_host, pProperties);
#else
- TRACE("%p, %p\n", physicalDevice, pProperties); physicalDevice->instance->funcs.p_vkGetPhysicalDeviceProperties2KHR(physicalDevice->phys_dev, pProperties);
#endif } diff --git a/dlls/winevulkan/vulkan_thunks.h b/dlls/winevulkan/vulkan_thunks.h index 01c1efb277..9896112176 100644 --- a/dlls/winevulkan/vulkan_thunks.h +++ b/dlls/winevulkan/vulkan_thunks.h @@ -64,11 +64,15 @@ void WINAPI wine_vkGetPhysicalDeviceExternalSemaphoreProperties(VkPhysicalDevice void WINAPI wine_vkGetPhysicalDeviceExternalSemaphorePropertiesKHR(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalSemaphoreInfo *pExternalSemaphoreInfo, VkExternalSemaphoreProperties *pExternalSemaphoreProperties) DECLSPEC_HIDDEN; VkResult WINAPI wine_vkGetPhysicalDeviceImageFormatProperties2(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceImageFormatInfo2 *pImageFormatInfo, VkImageFormatProperties2 *pImageFormatProperties); VkResult WINAPI wine_vkGetPhysicalDeviceImageFormatProperties2KHR(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceImageFormatInfo2 *pImageFormatInfo, VkImageFormatProperties2 *pImageFormatProperties) DECLSPEC_HIDDEN; +void WINAPI wine_vkGetPhysicalDeviceProperties2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2 *pProperties); +void WINAPI wine_vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2 *pProperties) DECLSPEC_HIDDEN; VkResult WINAPI wine_vkQueueSubmit(VkQueue queue, uint32_t submitCount, const VkSubmitInfo *pSubmits, VkFence fence);
/* Private thunks */ VkResult thunk_vkGetPhysicalDeviceImageFormatProperties2(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceImageFormatInfo2 *pImageFormatInfo, VkImageFormatProperties2 *pImageFormatProperties) DECLSPEC_HIDDEN; VkResult thunk_vkGetPhysicalDeviceImageFormatProperties2KHR(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceImageFormatInfo2 *pImageFormatInfo, VkImageFormatProperties2 *pImageFormatProperties) DECLSPEC_HIDDEN; +void thunk_vkGetPhysicalDeviceProperties2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2 *pProperties) DECLSPEC_HIDDEN; +void thunk_vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2 *pProperties) DECLSPEC_HIDDEN;
typedef struct VkAcquireNextImageInfoKHR_host {