From: Zhiyi Zhang Subject: [PATCH] winex11.drv: Support reporting correct PCI ID for GPUs. Message-Id: <6faad556-c333-7806-13f4-67185b02083e@codeweavers.com> Date: Fri, 21 Feb 2020 17:02:52 +0800 Signed-off-by: Zhiyi Zhang --- 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 ]], + [[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 ]], + [[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 ]], + [[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 #include +#ifdef HAVE_X11_XLIB_XCB_H +#include +#endif +#ifdef HAVE_XCB_DRI3_H +#include +#endif +#ifdef HAVE_XF86DRM_H +#include +#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