[PATCH v8 2/7] winex11.drv: Initialize GPU registry data.

Zhiyi Zhang zzhang at codeweavers.com
Mon Jun 10 08:12:17 CDT 2019


Signed-off-by: Zhiyi Zhang <zzhang at codeweavers.com>
---
 dlls/winex11.drv/Makefile.in |   2 +-
 dlls/winex11.drv/display.c   | 154 +++++++++++++++++++++++++++++++++++
 dlls/winex11.drv/x11drv.h    |  22 +++++
 dlls/winex11.drv/xinerama.c  |  25 ++++++
 4 files changed, 202 insertions(+), 1 deletion(-)

diff --git a/dlls/winex11.drv/Makefile.in b/dlls/winex11.drv/Makefile.in
index 9ca2fc6efe..72ab1bb77b 100644
--- a/dlls/winex11.drv/Makefile.in
+++ b/dlls/winex11.drv/Makefile.in
@@ -1,5 +1,5 @@
 MODULE    = winex11.drv
-IMPORTS   = uuid user32 gdi32 advapi32
+IMPORTS   = uuid user32 gdi32 advapi32 ntoskrnl setupapi
 DELAYIMPORTS = comctl32 ole32 shell32 imm32
 EXTRAINCL = $(X_CFLAGS)
 EXTRALIBS = $(X_LIBS) $(X_EXTRA_LIBS)
diff --git a/dlls/winex11.drv/display.c b/dlls/winex11.drv/display.c
index e3e4dce84d..6ff5828b3a 100644
--- a/dlls/winex11.drv/display.c
+++ b/dlls/winex11.drv/display.c
@@ -26,11 +26,37 @@
 #include "winbase.h"
 #include "winuser.h"
 #include "winreg.h"
+#include "initguid.h"
+#include "devguid.h"
+#include "devpkey.h"
+#include "setupapi.h"
+#define WIN32_NO_STATUS
+#include "winternl.h"
+#include "ddk/ntddk.h"
 #include "wine/debug.h"
+#include "wine/unicode.h"
 #include "x11drv.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(x11drv);
 
+static const WCHAR driver_descW[] = {'D','r','i','v','e','r','D','e','s','c',0};
+static const WCHAR video_idW[] = {'V','i','d','e','o','I','D',0};
+static const WCHAR guid_fmtW[] = {
+    '{','%','0','8','x','-','%','0','4','x','-','%','0','4','x','-','%','0','2','x','%','0','2','x','-',
+    '%','0','2','x','%','0','2','x','%','0','2','x','%','0','2','x','%','0','2','x','%','0','2','x','}',0};
+static const WCHAR gpu_instance_fmtW[] = {
+    'P','C','I','\\',
+    'V','E','N','_','%','0','4','X','&',
+    'D','E','V','_','%','0','4','X','&',
+    'S','U','B','S','Y','S','_','%','0','8','X','&',
+    'R','E','V','_','%','0','2','X','\\',
+    '%','0','8','X',0};
+static const WCHAR gpu_hardware_id_fmtW[] = {
+    'P','C','I','\\',
+    'V','E','N','_','%','0','4','X','&',
+    'D','E','V','_','%','0','4','X','&',
+    'S','U','B','S','Y','S','_','0','0','0','0','0','0','0','0','&',
+    'R','E','V','_','0','0',0};
 static const WCHAR video_keyW[] = {
     'H','A','R','D','W','A','R','E','\\',
     'D','E','V','I','C','E','M','A','P','\\',
@@ -47,10 +73,120 @@ void X11DRV_DisplayDevices_SetHandler(const struct x11drv_display_device_handler
     }
 }
 
+/* Initialize a GPU instance */
+static BOOL X11DRV_InitGpu(HDEVINFO devinfo, const struct x11drv_gpu *gpu, INT gpu_index)
+{
+    static const BOOL present = TRUE;
+    SP_DEVINFO_DATA device_data = {sizeof(device_data)};
+    WCHAR instanceW[MAX_PATH];
+    WCHAR bufferW[1024];
+    HKEY hkey = NULL;
+    GUID guid;
+    INT written;
+    DWORD size;
+    BOOL ret = FALSE;
+
+    sprintfW(instanceW, gpu_instance_fmtW, gpu->vendor_id, gpu->device_id, gpu->subsys_id, gpu->revision_id, gpu_index);
+    if (!SetupDiOpenDeviceInfoW(devinfo, instanceW, NULL, 0, &device_data))
+    {
+        SetupDiCreateDeviceInfoW(devinfo, instanceW, &GUID_DEVCLASS_DISPLAY, gpu->name, NULL, 0, &device_data);
+        if (!SetupDiRegisterDeviceInfo(devinfo, &device_data, 0, NULL, NULL, NULL))
+            goto done;
+    }
+
+    /* Write HaredwareID registry property, REG_MULTI_SZ */
+    written = sprintfW(bufferW, gpu_hardware_id_fmtW, gpu->vendor_id, gpu->device_id);
+    bufferW[written + 1] = 0;
+    if (!SetupDiSetDeviceRegistryPropertyW(devinfo, &device_data, SPDRP_HARDWAREID, (const BYTE *)bufferW,
+                                           (written + 2) * sizeof(WCHAR)))
+        goto done;
+
+    /* Write DEVPKEY_Device_IsPresent property */
+    if (!SetupDiSetDevicePropertyW(devinfo, &device_data, &DEVPKEY_Device_IsPresent, DEVPROP_TYPE_BOOLEAN,
+                                   (const BYTE *)&present, sizeof(present), 0))
+        goto done;
+
+    /* Open driver key.
+     * This is where HKLM\System\CurrentControlSet\Control\Video\{GPU GUID}\{Adapter Index} links to */
+    hkey = SetupDiCreateDevRegKeyW(devinfo, &device_data, DICS_FLAG_GLOBAL, 0, DIREG_DRV, NULL, NULL);
+
+    /* Write DriverDesc value */
+    if (RegSetValueExW(hkey, driver_descW, 0, REG_SZ, (const BYTE *)gpu->name,
+                       (strlenW(gpu->name) + 1) * sizeof(WCHAR)))
+        goto done;
+    RegCloseKey(hkey);
+
+    /* Write GUID in VideoID in .../instance/Device Parameters, reuse the GUID if it's existent */
+    hkey = SetupDiCreateDevRegKeyW(devinfo, &device_data, DICS_FLAG_GLOBAL, 0, DIREG_DEV, NULL, NULL);
+
+    size = sizeof(bufferW);
+    if (RegQueryValueExW(hkey, video_idW, 0, NULL, (BYTE *)bufferW, &size))
+    {
+        ExUuidCreate(&guid);
+        sprintfW(bufferW, guid_fmtW, guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2],
+                 guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
+        if (RegSetValueExW(hkey, video_idW, 0, REG_SZ, (const BYTE *)bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR)))
+            goto done;
+    }
+
+    ret = TRUE;
+done:
+    RegCloseKey(hkey);
+    if (!ret)
+        ERR("Failed to initialize GPU\n");
+    return ret;
+}
+
+static void prepare_devices(void)
+{
+    static const BOOL not_present = FALSE;
+    SP_DEVINFO_DATA device_data = {sizeof(device_data)};
+    HDEVINFO devinfo;
+    DWORD i = 0;
+
+    /* FIXME:
+     * Currently SetupDiGetClassDevsW with DIGCF_PRESENT is unsupported, So we need to clean up not present devices in
+     * case application use SetupDiGetClassDevsW to enumerate devices. Wrong devices could exist in registry as a result
+     * of prefix copying or having devices unplugged. But then we couldn't simply delete GPUs because we need to retain
+     * the same GUID for the same GPU. */
+    devinfo = SetupDiGetClassDevsW(&GUID_DEVCLASS_DISPLAY, NULL, NULL, 0);
+    while (SetupDiEnumDeviceInfo(devinfo, i++, &device_data))
+    {
+        if (!SetupDiSetDevicePropertyW(devinfo, &device_data, &DEVPKEY_Device_IsPresent, DEVPROP_TYPE_BOOLEAN,
+                                       (const BYTE *)&not_present, sizeof(not_present), 0))
+            ERR("Failed to set GPU present property\n");
+    }
+    SetupDiDestroyDeviceInfoList(devinfo);
+}
+
+static void cleanup_devices(void)
+{
+    SP_DEVINFO_DATA device_data = {sizeof(device_data)};
+    HDEVINFO devinfo;
+    DWORD type;
+    DWORD i = 0;
+    BOOL present;
+
+    devinfo = SetupDiGetClassDevsW(&GUID_DEVCLASS_DISPLAY, NULL, NULL, 0);
+    while (SetupDiEnumDeviceInfo(devinfo, i++, &device_data))
+    {
+        present = FALSE;
+        SetupDiGetDevicePropertyW(devinfo, &device_data, &DEVPKEY_Device_IsPresent, &type, (BYTE *)&present,
+                                  sizeof(present), NULL, 0);
+        if (!present && !SetupDiRemoveDevice(devinfo, &device_data))
+            ERR("Failed to remove GPU\n");
+    }
+    SetupDiDestroyDeviceInfoList(devinfo);
+}
+
 void X11DRV_DisplayDevices_Init(void)
 {
     static const WCHAR init_mutexW[] = {'d','i','s','p','l','a','y','_','d','e','v','i','c','e','_','i','n','i','t',0};
     HANDLE mutex;
+    struct x11drv_gpu *gpus = NULL;
+    INT gpu_count;
+    INT gpu;
+    HDEVINFO gpu_devinfo = NULL;
     HKEY video_hkey = NULL;
     DWORD disposition = 0;
 
@@ -70,8 +206,26 @@ void X11DRV_DisplayDevices_Init(void)
 
     TRACE("via %s\n", wine_dbgstr_a(handler.name));
 
+    prepare_devices();
+
+    gpu_devinfo = SetupDiCreateDeviceInfoList(&GUID_DEVCLASS_DISPLAY, NULL);
+
+    /* Initialize GPUs */
+    if (!handler.pGetGpus(&gpus, &gpu_count))
+        goto done;
+
+    for (gpu = 0; gpu < gpu_count; gpu++)
+    {
+        if (!X11DRV_InitGpu(gpu_devinfo, &gpus[gpu], gpu))
+            goto done;
+    }
+
 done:
+    cleanup_devices();
+    SetupDiDestroyDeviceInfoList(gpu_devinfo);
     RegCloseKey(video_hkey);
     ReleaseMutex(mutex);
     CloseHandle(mutex);
+    if (gpus)
+        handler.pFreeGpus(gpus);
 }
diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h
index fbc5a104f6..e50158d282 100644
--- a/dlls/winex11.drv/x11drv.h
+++ b/dlls/winex11.drv/x11drv.h
@@ -667,6 +667,20 @@ void X11DRV_XRandR_Init(void) DECLSPEC_HIDDEN;
 
 /* X11 display device handler. Used to initialize display device registry data */
 
+/* Represent a physical GPU in the PCI slots */
+struct x11drv_gpu
+{
+    /* ID to uniquely identify a GPU in handler */
+    ULONG_PTR id;
+    /* Name */
+    WCHAR name[128];
+    /* PCI ID */
+    UINT vendor_id;
+    UINT device_id;
+    UINT subsys_id;
+    UINT revision_id;
+};
+
 /* Required functions for display device registry initialization */
 struct x11drv_display_device_handler
 {
@@ -675,6 +689,14 @@ struct x11drv_display_device_handler
 
     /* Higher priority can override handlers with lower proprity */
     INT priority;
+
+    /* pGetGpus will be called to get a list of GPUs. First GPU has to be where the primary adapter is.
+     *
+     * Return FALSE on failure with parameters unchanged */
+    BOOL (*pGetGpus)(struct x11drv_gpu **gpus, int *count);
+
+    /* pFreeGpus will be called to free a GPU list from pGetGpus */
+    void (*pFreeGpus)(struct x11drv_gpu *gpus);
 };
 
 extern void X11DRV_DisplayDevices_SetHandler(const struct x11drv_display_device_handler *handler) DECLSPEC_HIDDEN;
diff --git a/dlls/winex11.drv/xinerama.c b/dlls/winex11.drv/xinerama.c
index 35dd36049b..900cfd91db 100644
--- a/dlls/winex11.drv/xinerama.c
+++ b/dlls/winex11.drv/xinerama.c
@@ -201,6 +201,29 @@ RECT get_primary_monitor_rect(void)
     return get_primary()->rcMonitor;
 }
 
+static BOOL xinerama_get_gpus( struct x11drv_gpu **new_gpus, int *count )
+{
+    static const WCHAR wine_adapterW[] = {'W','i','n','e',' ','A','d','a','p','t','e','r',0};
+    struct x11drv_gpu *gpus;
+
+    /* Xinerama has no support for GPU, faking one */
+    gpus = heap_calloc( 1, sizeof(*gpus) );
+    if (!gpus)
+        return FALSE;
+
+    lstrcpyW( gpus[0].name, wine_adapterW );
+
+    *new_gpus = gpus;
+    *count = 1;
+
+    return TRUE;
+}
+
+static void xinerama_free_gpus( struct x11drv_gpu *gpus )
+{
+    heap_free( gpus );
+}
+
 void xinerama_init( unsigned int width, unsigned int height )
 {
     struct x11drv_display_device_handler handler;
@@ -239,6 +262,8 @@ void xinerama_init( unsigned int width, unsigned int height )
 
     strcpy( handler.name, "Xinerama" );
     handler.priority = 100;
+    handler.pGetGpus = xinerama_get_gpus;
+    handler.pFreeGpus = xinerama_free_gpus;
     X11DRV_DisplayDevices_SetHandler( &handler );
 
     TRACE( "virtual size: %s primary: %s\n",
-- 
2.20.1





More information about the wine-devel mailing list