[PATCH] winex11.drv: Support reporting correct PCI ID for GPUs.

Zhiyi Zhang zzhang at codeweavers.com
Fri Feb 21 03:02:52 CST 2020


Signed-off-by: Zhiyi Zhang <zzhang at codeweavers.com>
---
 configure.ac                 |  48 ++++++
 dlls/winex11.drv/Makefile.in |   2 +-
 dlls/winex11.drv/xrandr.c    | 293 +++++++++++++++++++++++++++++++----
 3 files changed, 311 insertions(+), 32 deletions(-)

diff --git a/configure.ac b/configure.ac
index 47d2b750c0..8d5312d80e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -42,6 +42,7 @@ AC_ARG_WITH(cups,      AS_HELP_STRING([--without-cups],[do not use CUPS]))
 AC_ARG_WITH(curses,    AS_HELP_STRING([--without-curses],[do not use (n)curses]),
             [if test "x$withval" = "xno"; then ac_cv_header_ncurses_h=no; ac_cv_header_curses_h=no; fi])
 AC_ARG_WITH(dbus,      AS_HELP_STRING([--without-dbus],[do not use DBus (dynamic device support)]))
+AC_ARG_WITH(drm,       AS_HELP_STRING([--without-drm],[do not use DRM (Direct Rendering Manager support)]))
 AC_ARG_WITH(faudio,    AS_HELP_STRING([--without-faudio],[do not use FAudio (XAudio2 support)]))
 AC_ARG_WITH(float-abi, AS_HELP_STRING([--with-float-abi=abi],[specify the ABI (soft|softfp|hard) for ARM platforms]))
 AC_ARG_WITH(fontconfig,AS_HELP_STRING([--without-fontconfig],[do not use fontconfig]))
@@ -86,6 +87,10 @@ AC_ARG_WITH(unwind,    AS_HELP_STRING([--without-unwind],[do not use the libunwi
 AC_ARG_WITH(v4l2,      AS_HELP_STRING([--without-v4l2],[do not use v4l2 (video capture)]))
 AC_ARG_WITH(vkd3d,     AS_HELP_STRING([--without-vkd3d],[do not use vkd3d (Direct3D 12 support)]))
 AC_ARG_WITH(vulkan,    AS_HELP_STRING([--without-vulkan],[do not use Vulkan]))
+AC_ARG_WITH(x11-xcb,   AS_HELP_STRING([--without-x11-xcb],[do not use Xlib XCB support]),
+            [if test "x$withval" = "xno"; then ac_cv_header_X11_Xlib_xcb_h=no; fi])
+AC_ARG_WITH(xcb-dri3,  AS_HELP_STRING([--without-xcb-dri3],[do not use XCB DRI3 support]),
+            [if test "x$withval" = "xno"; then ac_cv_header_xcb_dri3_h=no; fi])
 AC_ARG_WITH(xcomposite,AS_HELP_STRING([--without-xcomposite],[do not use the Xcomposite extension]),
             [if test "x$withval" = "xno"; then ac_cv_header_X11_extensions_Xcomposite_h=no; fi])
 AC_ARG_WITH(xcursor,   AS_HELP_STRING([--without-xcursor],[do not use the Xcursor extension]),
@@ -1115,6 +1120,7 @@ then
 
     dnl *** All of the following tests require X11/Xlib.h
     AC_CHECK_HEADERS([X11/Xlib.h \
+                      X11/Xlib-xcb.h \
                       X11/XKBlib.h \
                       X11/Xutil.h \
                       X11/Xcursor/Xcursor.h \
@@ -1226,6 +1232,48 @@ then
         WINE_NOTICE_WITH(xrandr,[test "x$ac_cv_lib_soname_Xrandr" = "x"],
                          [libxrandr ${notice_platform}development files not found, XRandr won't be supported.])
 
+        dnl *** Check for Xlib XCB
+        if test "$ac_cv_header_X11_Xlib_xcb_h" = "yes" -a "x$ac_cv_lib_soname_Xrandr" != "x"
+        then
+            AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <X11/Xlib-xcb.h>]],
+                [[static typeof(XGetXCBConnection) * func; if (func) return 0;]])],
+                [WINE_CHECK_SONAME(X11-xcb, XGetXCBConnection,,, [$X_LIBS $X_EXTRA_LIBS])],
+                [WINE_NOTICE([libx11-xcb ${notice_platform}development files too old, Xlib XCB won't be supported.])])
+        fi
+        WINE_NOTICE_WITH(x11_xcb, [test "x$ac_cv_lib_soname_X11_xcb" = "x"],
+                         [libx11-xcb ${notice_platform}development files not found, Xlib XCB won't be supported.])
+
+        dnl *** Check for XCB DRI3
+        if test "x$ac_cv_lib_soname_X11_xcb" != "x"
+        then
+            AC_CHECK_HEADERS([xcb/dri3.h])
+            if test "$ac_cv_header_xcb_dri3_h" = "yes"
+            then
+                AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <xcb/dri3.h>]],
+                    [[static typeof(xcb_dri3_open) * func; if (func) return 0;]])],
+                    [WINE_CHECK_SONAME(xcb-dri3, xcb_dri3_open,,, [$X_LIBS $X_EXTRA_LIBS])],
+                    [WINE_NOTICE([libxcb-dri3 ${notice_platform}development files too old, XCB DRI3 won't be supported.])])
+            fi
+        fi
+        WINE_NOTICE_WITH(xcb_dri3, [test "x$ac_cv_lib_soname_xcb_dri3" = "x"],
+                         [libxcb-dri3 ${notice_platform}development files not found, XCB DRI3 won't be supported.])
+
+        dnl *** Check for libdrm
+        if test "x$with_drm" != "xno" -a "x$ac_cv_lib_soname_Xrandr" != "x"
+        then
+            WINE_PACKAGE_FLAGS(DRM, [libdrm],,,,
+                [AC_CHECK_HEADERS([xf86drm.h])
+                if test "$ac_cv_header_xf86drm_h" = "yes"
+                then
+                    AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <xf86drm.h>]],
+                        [[static typeof(drmGetDevice) * func; if (func) return 0;]])],
+                        [WINE_CHECK_SONAME(drm, drmGetDevice,,, [$DRM_LIBS])],
+                        [WINE_NOTICE([libdrm ${notice_platform}development files too old, DRM may not work.])])
+                fi])
+        fi
+        WINE_NOTICE_WITH(drm, [test "x$ac_cv_lib_soname_drm" = "x"],
+                        [libdrm ${notice_platform}development files not found, DRM won't be supported.])
+
         dnl *** Check for Xfixes extension
         if test "$ac_cv_header_X11_extensions_Xfixes_h" = "yes"
         then
diff --git a/dlls/winex11.drv/Makefile.in b/dlls/winex11.drv/Makefile.in
index 3e2d7ef895..71258a0a23 100644
--- a/dlls/winex11.drv/Makefile.in
+++ b/dlls/winex11.drv/Makefile.in
@@ -1,7 +1,7 @@
 MODULE    = winex11.drv
 IMPORTS   = uuid setupapi rpcrt4 user32 gdi32 advapi32
 DELAYIMPORTS = comctl32 ole32 shell32 imm32
-EXTRAINCL = $(X_CFLAGS)
+EXTRAINCL = $(X_CFLAGS) $(DRM_CFLAGS)
 EXTRALIBS = $(X_LIBS) $(X_EXTRA_LIBS)
 
 C_SRCS = \
diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c
index 930e0282be..d405dcba7a 100644
--- a/dlls/winex11.drv/xrandr.c
+++ b/dlls/winex11.drv/xrandr.c
@@ -33,6 +33,15 @@ WINE_DECLARE_DEBUG_CHANNEL(winediag);
 
 #include <X11/Xlib.h>
 #include <X11/extensions/Xrandr.h>
+#ifdef HAVE_X11_XLIB_XCB_H
+#include <X11/Xlib-xcb.h>
+#endif
+#ifdef HAVE_XCB_DRI3_H
+#include <xcb/dri3.h>
+#endif
+#ifdef HAVE_XF86DRM_H
+#include <xf86drm.h>
+#endif
 #include "x11drv.h"
 
 #include "wine/heap.h"
@@ -76,6 +85,25 @@ MAKE_FUNCPTR(XRRGetProviderInfo)
 MAKE_FUNCPTR(XRRFreeProviderInfo)
 #endif
 
+#if defined(SONAME_LIBX11_XCB) && defined(SONAME_LIBXCB_DRI3)
+MAKE_FUNCPTR(XGetXCBConnection)
+MAKE_FUNCPTR(xcb_dri3_id)
+MAKE_FUNCPTR(xcb_dri3_open)
+MAKE_FUNCPTR(xcb_dri3_open_reply)
+MAKE_FUNCPTR(xcb_dri3_open_reply_fds)
+MAKE_FUNCPTR(xcb_get_extension_data)
+static void *x11_xcb_handle;
+static void *xcb_dri3_handle;
+static BOOL dri3_loaded;
+#endif
+
+#ifdef SONAME_LIBDRM
+MAKE_FUNCPTR(drmFreeDevice)
+MAKE_FUNCPTR(drmGetDevice)
+static void *drm_handle;
+static BOOL drm_loaded;
+#endif
+
 #undef MAKE_FUNCPTR
 
 static struct x11drv_mode_info *dd_modes;
@@ -91,49 +119,73 @@ static int load_xrandr(void)
         (xrandr_handle = wine_dlopen(SONAME_LIBXRANDR, RTLD_NOW, NULL, 0)))
     {
 
-#define LOAD_FUNCPTR(f) \
-        if((p##f = wine_dlsym(xrandr_handle, #f, NULL, 0)) == NULL) \
+#define LOAD_SYMBOL(library, symbol) \
+        if((p##symbol = wine_dlsym(library##_handle, #symbol, NULL, 0)) == NULL) \
             goto sym_not_found;
 
-        LOAD_FUNCPTR(XRRConfigCurrentConfiguration)
-        LOAD_FUNCPTR(XRRConfigCurrentRate)
-        LOAD_FUNCPTR(XRRFreeScreenConfigInfo)
-        LOAD_FUNCPTR(XRRGetScreenInfo)
-        LOAD_FUNCPTR(XRRQueryExtension)
-        LOAD_FUNCPTR(XRRQueryVersion)
-        LOAD_FUNCPTR(XRRRates)
-        LOAD_FUNCPTR(XRRSetScreenConfig)
-        LOAD_FUNCPTR(XRRSetScreenConfigAndRate)
-        LOAD_FUNCPTR(XRRSizes)
+        LOAD_SYMBOL(xrandr, XRRConfigCurrentConfiguration)
+        LOAD_SYMBOL(xrandr, XRRConfigCurrentRate)
+        LOAD_SYMBOL(xrandr, XRRFreeScreenConfigInfo)
+        LOAD_SYMBOL(xrandr, XRRGetScreenInfo)
+        LOAD_SYMBOL(xrandr, XRRQueryExtension)
+        LOAD_SYMBOL(xrandr, XRRQueryVersion)
+        LOAD_SYMBOL(xrandr, XRRRates)
+        LOAD_SYMBOL(xrandr, XRRSetScreenConfig)
+        LOAD_SYMBOL(xrandr, XRRSetScreenConfigAndRate)
+        LOAD_SYMBOL(xrandr, XRRSizes)
         r = 1;
 
 #ifdef HAVE_XRRGETSCREENRESOURCES
-        LOAD_FUNCPTR(XRRFreeCrtcInfo)
-        LOAD_FUNCPTR(XRRFreeOutputInfo)
-        LOAD_FUNCPTR(XRRFreeScreenResources)
-        LOAD_FUNCPTR(XRRGetCrtcInfo)
-        LOAD_FUNCPTR(XRRGetOutputInfo)
-        LOAD_FUNCPTR(XRRGetScreenResources)
-        LOAD_FUNCPTR(XRRSetCrtcConfig)
-        LOAD_FUNCPTR(XRRSetScreenSize)
+        LOAD_SYMBOL(xrandr, XRRFreeCrtcInfo)
+        LOAD_SYMBOL(xrandr, XRRFreeOutputInfo)
+        LOAD_SYMBOL(xrandr, XRRFreeScreenResources)
+        LOAD_SYMBOL(xrandr, XRRGetCrtcInfo)
+        LOAD_SYMBOL(xrandr, XRRGetOutputInfo)
+        LOAD_SYMBOL(xrandr, XRRGetScreenResources)
+        LOAD_SYMBOL(xrandr, XRRSetCrtcConfig)
+        LOAD_SYMBOL(xrandr, XRRSetScreenSize)
         r = 2;
 #endif
 
 #ifdef HAVE_XRRGETPROVIDERRESOURCES
-        LOAD_FUNCPTR(XRRSelectInput)
-        LOAD_FUNCPTR(XRRGetOutputPrimary)
-        LOAD_FUNCPTR(XRRGetProviderResources)
-        LOAD_FUNCPTR(XRRFreeProviderResources)
-        LOAD_FUNCPTR(XRRGetProviderInfo)
-        LOAD_FUNCPTR(XRRFreeProviderInfo)
+        LOAD_SYMBOL(xrandr, XRRSelectInput)
+        LOAD_SYMBOL(xrandr, XRRGetOutputPrimary)
+        LOAD_SYMBOL(xrandr, XRRGetProviderResources)
+        LOAD_SYMBOL(xrandr, XRRFreeProviderResources)
+        LOAD_SYMBOL(xrandr, XRRGetProviderInfo)
+        LOAD_SYMBOL(xrandr, XRRFreeProviderInfo)
         r = 4;
 #endif
 
-#undef LOAD_FUNCPTR
+#if defined(SONAME_LIBX11_XCB) && defined(SONAME_LIBXCB_DRI3)
+        if ((x11_xcb_handle = wine_dlopen(SONAME_LIBX11_XCB, RTLD_NOW, NULL, 0)) &&
+            (xcb_dri3_handle = wine_dlopen(SONAME_LIBXCB_DRI3, RTLD_NOW, NULL, 0)))
+        {
+            LOAD_SYMBOL(x11_xcb, XGetXCBConnection)
+            LOAD_SYMBOL(xcb_dri3, xcb_dri3_id)
+            LOAD_SYMBOL(xcb_dri3, xcb_dri3_open)
+            LOAD_SYMBOL(xcb_dri3, xcb_dri3_open_reply)
+            LOAD_SYMBOL(xcb_dri3, xcb_dri3_open_reply_fds)
+            LOAD_SYMBOL(xcb_dri3, xcb_get_extension_data)
+            dri3_loaded = TRUE;
+        }
+#endif
+
+#ifdef SONAME_LIBDRM
+        if ((drm_handle = wine_dlopen(SONAME_LIBDRM, RTLD_NOW, NULL, 0)))
+        {
+            LOAD_SYMBOL(drm, drmFreeDevice)
+            LOAD_SYMBOL(drm, drmGetDevice)
+            drm_loaded = TRUE;
+        }
+#endif
+
+#undef LOAD_SYMBOL
+    }
 
 sym_not_found:
-        if (!r)  TRACE("Unable to load function ptrs from XRandR library\n");
-    }
+    if (!r)
+        TRACE("Unable to load function ptrs from XRandR library\n");
     return r;
 }
 
@@ -662,6 +714,181 @@ static BOOL is_crtc_primary( RECT primary, const XRRCrtcInfo *crtc )
            crtc->y + crtc->height == primary.bottom;
 }
 
+static int get_drm_device_from_provider( RRProvider provider )
+{
+#if defined(SONAME_LIBX11_XCB) && defined(SONAME_LIBXCB_DRI3)
+    const xcb_query_extension_reply_t *extension;
+    xcb_dri3_open_cookie_t cookie;
+    xcb_dri3_open_reply_t *reply;
+    xcb_connection_t *connection;
+    int *fds, fd;
+
+    if (!dri3_loaded)
+        return -1;
+
+    connection = pXGetXCBConnection( gdi_display );
+    extension = pxcb_get_extension_data( connection, pxcb_dri3_id );
+    if (!extension || !extension->present)
+    {
+        WARN("DRI3 is unsupported.\n");
+        return -1;
+    }
+
+    cookie = pxcb_dri3_open( connection, DefaultRootWindow( gdi_display ), provider );
+    reply = pxcb_dri3_open_reply( connection, cookie, NULL );
+
+    if (!reply)
+        return -1;
+
+    if (reply->nfd != 1)
+    {
+        free( reply );
+        return -1;
+    }
+
+    fds = pxcb_dri3_open_reply_fds( connection, reply );
+    fd = fds[0];
+    free( reply );
+    fcntl( fd, F_SETFD, FD_CLOEXEC );
+    return fd;
+#endif /* defined(SONAME_LIBX11_XCB) && defined(SONAME_LIBXCB_DRI3) */
+
+    WARN("DRI3 support not compiled in. Finding a DRM device with a RandR provider won't work!\n");
+    return -1;
+}
+
+/* Fallback when DRI3 is unavailable. For example, GPUs using NVIDIA proprietary drivers.
+ * This functions may not get the correct device when there are multiple GPUs present */
+static int get_drm_device_from_index( int gpu_index )
+{
+#ifdef __linux__
+    char device_path[MAX_PATH];
+    int fd;
+
+    sprintf( device_path, "/dev/dri/card%d", gpu_index );
+    fd = open( device_path, O_RDONLY );
+    if (fd < 0)
+        return -1;
+
+    fcntl( fd, F_SETFD, FD_CLOEXEC );
+    return fd;
+#endif /* __linux__ */
+
+    return -1;
+}
+
+#ifdef __linux__
+static unsigned int read_id( const char *device_name, const char *id_name )
+{
+    char filename[MAX_PATH];
+    unsigned int id = 0;
+    FILE *file;
+
+    sprintf( filename, "%s/%s", device_name, id_name );
+    file = fopen( filename, "r" );
+    if (!file)
+        return 0;
+
+    fscanf( file, "%x", &id );
+    fclose( file );
+    return id;
+}
+#endif /* __linux__ */
+
+static BOOL get_gpu_pci_id( struct x11drv_gpu *gpu, int gpu_index )
+{
+    int fd = get_drm_device_from_provider( (RRProvider)gpu->id );
+
+    if (fd < 0)
+        fd = get_drm_device_from_index( gpu_index );
+
+    if (fd < 0)
+    {
+        WARN("Failed to get DRM device.\n");
+        return FALSE;
+    }
+
+#ifdef SONAME_LIBDRM
+    {
+        drmDevice *device;
+        int ret;
+
+        if (!drm_loaded)
+        {
+            close( fd );
+            return FALSE;
+        }
+
+        ret = pdrmGetDevice( fd, &device );
+        close( fd );
+
+        if (ret != 0)
+            return FALSE;
+
+        if (device->bustype != DRM_BUS_PCI)
+        {
+            pdrmFreeDevice( &device );
+            return FALSE;
+        }
+
+        gpu->vendor_id = device->deviceinfo.pci->vendor_id;
+        gpu->device_id = device->deviceinfo.pci->device_id;
+        gpu->subsys_id = (UINT)device->deviceinfo.pci->subdevice_id << 16 | device->deviceinfo.pci->subvendor_id;
+        gpu->revision_id = device->deviceinfo.pci->revision_id;
+        pdrmFreeDevice( &device );
+        return TRUE;
+    }
+#endif /* SONAME_LIBDRM */
+
+    /* Fallback on Linux when libdrm is too old to have drmGetDevice() */
+#ifdef __linux__
+    {
+        char fd_path[MAX_PATH], link[MAX_PATH], device_path[128], node_name[64];
+        char *subsystem_name, subsystem_path[MAX_PATH];
+        int ret;
+
+        /* Get DRM device path from fd */
+        snprintf( fd_path, sizeof(fd_path), "/proc/self/fd/%d", fd );
+        ret = readlink( fd_path, link, sizeof(link) - 1 );
+        close( fd );
+
+        if (ret < 0)
+            return FALSE;
+
+        link[ret] = 0;
+        if (sscanf( link, "/dev/dri/%63s", node_name ) != 1)
+            return FALSE;
+
+        snprintf( device_path, sizeof(device_path), "/sys/class/drm/%s/device", node_name );
+        snprintf( subsystem_path, sizeof(subsystem_path), "%s/subsystem", device_path );
+
+        /* Check if device is using PCI */
+        ret = readlink( subsystem_path, link, sizeof(link) - 1 );
+        if (ret < 0)
+            return FALSE;
+
+        link[ret] = 0;
+        subsystem_name = strrchr( link, '/' );
+        if (!subsystem_name)
+            return FALSE;
+
+        if (strncmp( subsystem_name + 1, "pci", 3 ))
+            return FALSE;
+
+        /* Read IDs */
+        gpu->vendor_id = read_id( device_path, "vendor" );
+        gpu->device_id = read_id( device_path, "device" );
+        gpu->subsys_id = read_id( device_path, "subsystem_device" ) << 16 | read_id( device_path, "subsystem_vendor" );
+        gpu->revision_id = read_id( device_path, "revision" );
+        return TRUE;
+    }
+#endif /* __linux__ */
+
+    close( fd );
+    WARN("DRM support not compiled in. No valid PCI ID will be reported for GPUs.\n");
+    return FALSE;
+}
+
 static BOOL xrandr14_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};
@@ -725,9 +952,13 @@ static BOOL xrandr14_get_gpus( struct x11drv_gpu **new_gpus, int *count )
 
         gpus[i].id = provider_resources->providers[i];
         MultiByteToWideChar( CP_UTF8, 0, provider_info->name, -1, gpus[i].name, ARRAY_SIZE(gpus[i].name) );
-        /* PCI IDs are all zero because there is currently no portable way to get it via XRandR. Some AMD drivers report
-         * their PCI address in the name but many others don't */
         pXRRFreeProviderInfo( provider_info );
+
+        if (!get_gpu_pci_id( &gpus[i], i ))
+            WARN("Failed to get PCI ID for GPU %s\n", wine_dbgstr_w(gpus[i].name));
+
+        TRACE("name:%s vendor id:0x%04x device id:0x%04x subsystem id:0x%08x revision id:0x%02x\n",
+              wine_dbgstr_w(gpus[i].name), gpus[i].vendor_id, gpus[i].device_id, gpus[i].subsys_id, gpus[i].revision_id);
     }
 
     /* Make primary GPU the first */
-- 
2.20.1



More information about the wine-devel mailing list