From db3f2c693bcd060f3850a232b83324675407d313 Mon Sep 17 00:00:00 2001 From: Antonio Giner Date: Sat, 2 Dec 2023 18:30:12 +0100 Subject: [PATCH] [Linux] Add new option -dri_device. Use to force vsync on a specific dri device, e.g. "card0", "card1", etc., or "auto" for automatic selection (currently it will pick the first device with a valid connector." --- src/osd/modules/lib/osdobj_common.cpp | 1 + src/osd/modules/lib/osdobj_common.h | 2 + src/osd/modules/render/draw13.cpp | 94 +++++++++++++++++++++++++-- src/osd/modules/render/drawogl.cpp | 92 ++++++++++++++++++++++++-- 4 files changed, 177 insertions(+), 12 deletions(-) diff --git a/src/osd/modules/lib/osdobj_common.cpp b/src/osd/modules/lib/osdobj_common.cpp index 507a2339d7960..4edb9bc96d694 100644 --- a/src/osd/modules/lib/osdobj_common.cpp +++ b/src/osd/modules/lib/osdobj_common.cpp @@ -129,6 +129,7 @@ const options_entry osd_options::s_option_entries[] = { OSDOPTION_AUTOSYNC, "1", core_options::option_type::BOOLEAN, "automatically enable syncrefresh if refresh difference is below syncrefresh_tolerance" }, { OSDOPTION_AUTOFILTER, "1", core_options::option_type::BOOLEAN, "automatically set bilinear filtering with fractional stretching or interlaced " }, { OSDOPTION_AUTOSTRETCH, "1", core_options::option_type::BOOLEAN, "automatically set scaling mode (integer or fractional) based on the selected video mode " }, + { OSDOPTION_DRI_DEVICE, OSDOPTVAL_AUTO, core_options::option_type::STRING, "Force drm video card for vsync (card0, card1, etc.) or auto for automatic." }, { OSDOPTION_SCREEN_COMPOSITING, "0", core_options::option_type::BOOLEAN, "Readjust relative screen positions of a multi-display setup after mode switching (Linux)" }, { OSDOPTION_SCREEN_REORDERING, "0", core_options::option_type::BOOLEAN, "Reallocates desktop multiple screens stacked vertically, so super-resolutions fit (Linux)" }, { OSDOPTION_ALLOW_HW_REFRESH, "0", core_options::option_type::BOOLEAN, "Allow on-the-fly mode addition (Windows)" }, diff --git a/src/osd/modules/lib/osdobj_common.h b/src/osd/modules/lib/osdobj_common.h index cd6ac7e3bd973..da2907ff43449 100644 --- a/src/osd/modules/lib/osdobj_common.h +++ b/src/osd/modules/lib/osdobj_common.h @@ -123,6 +123,7 @@ #define OSDOPTION_AUTOSYNC "autosync" #define OSDOPTION_AUTOFILTER "autofilter" #define OSDOPTION_AUTOSTRETCH "autostretch" +#define OSDOPTION_DRI_DEVICE "dri_device" #define OSDOPTION_SCREEN_COMPOSITING "screen_compositing" #define OSDOPTION_SCREEN_REORDERING "screen_reordering" #define OSDOPTION_ALLOW_HW_REFRESH "allow_hw_refresh" @@ -203,6 +204,7 @@ class osd_options : public emu_options bool autosync() const { return bool_value(OSDOPTION_AUTOSYNC); } bool autofilter() const { return bool_value(OSDOPTION_AUTOFILTER); } bool autostretch() const { return bool_value(OSDOPTION_AUTOSTRETCH); } + const char *dri_device() const { return value(OSDOPTION_DRI_DEVICE); } bool screen_compositing() const { return bool_value(OSDOPTION_SCREEN_COMPOSITING); } bool screen_reordering() const { return bool_value(OSDOPTION_SCREEN_REORDERING); } bool allow_hw_refresh() const { return bool_value(OSDOPTION_ALLOW_HW_REFRESH); } diff --git a/src/osd/modules/render/draw13.cpp b/src/osd/modules/render/draw13.cpp index b07695c105844..e28c375de0981 100644 --- a/src/osd/modules/render/draw13.cpp +++ b/src/osd/modules/render/draw13.cpp @@ -39,6 +39,7 @@ #include #ifdef SDLMAME_X11 +#include // DRM #include #include @@ -248,9 +249,10 @@ static inline bool is_transparent(const float &a) //============================================================ #ifdef SDLMAME_X11 -static int drm_open(); +static int drm_open(const char *dri_device); static void drm_waitvblank(int crtc); static int fd = 0; +static const char* dri_device = nullptr; #endif //============================================================ @@ -482,7 +484,7 @@ int renderer_sdl2::create() if (window().index() == 0 && video_config.syncrefresh && video_config.sync_mode != 0) { // Try to open DRM device - fd = drm_open(); + fd = drm_open(dri_device); if (fd != 0) renderer_vsync = (video_config.sync_mode == 2 || video_config.sync_mode == 4)? true : false; } @@ -519,18 +521,94 @@ int renderer_sdl2::create() // drm_open //============================================================ -static int drm_open() +static int drm_open(const char *dri_device) { int fd = 0; - const char *node = {"/dev/dri/card0"}; + char dri_path[16]; + char *node = dri_path; + + // Dri device forced by user + if (strcmp(dri_device, "auto") != 0) + { + osd_printf_verbose("drm_open: %s for by user\n", dri_device); + snprintf(node, sizeof(dri_path), "/dev/dri/%s", dri_device); + } + + // Automatic selection + else + { + // Get an array of drm devices to check + int num_devices = drmGetDevices2(0, NULL, 0); + if (num_devices <= 0) + { + osd_printf_error("drm_open: couldn't find any drm device\n"); + return 0; + } + + drmDevicePtr *devices = (drmDevicePtr*)calloc(num_devices, sizeof(drmDevicePtr)); + if (drmGetDevices2(0, devices, num_devices) < 0) + { + osd_printf_error("drm_open: drmGetDevices2() failed\n"); + return 0; + } + + // Parse device list to find the first one with a valid connector + bool found = false; + + for (int i = 0; i < num_devices; i++) + { + // Skip non-primary nodes + if (devices[i]->available_nodes & (1 << DRM_NODE_PRIMARY)) + node = devices[i]->nodes[DRM_NODE_PRIMARY]; + + else continue; + + fd = open(node, O_RDWR | O_CLOEXEC); + if (fd < 0) + { + osd_printf_error("drm_open: couldn't open %s\n", node); + continue; + } + drmModeRes *resources = drmModeGetResources(fd); + if (resources && resources->count_connectors > 0 && resources->count_encoders > 0 && resources->count_crtcs > 0) + { + for (int j = 0; j < resources->count_connectors; j++) + { + drmModeConnector *conn = drmModeGetConnector(fd, resources->connectors[j]); + if (!conn) continue; + + // We found a valid connector, use it + if (conn->connection == DRM_MODE_CONNECTED && conn->count_modes > 0) + found = true; + + drmModeFreeConnector(conn); + if (found) break; + } + } + drmModeFreeResources(resources); + close(fd); + + if (found) break; + } + + drmFreeDevices(devices, num_devices); + free(devices); + + if (!found) + { + osd_printf_error("drm_open: couldn't find any device with a valid connector\n"); + return 0; + } + } fd = open(node, O_RDWR | O_CLOEXEC); if (fd < 0) { - fprintf(stderr, "cannot open %s\n", node); + osd_printf_error("drm_open: cannot open %s\n", node); return 0; } - osd_printf_verbose("%s successfully opened\n", node); + + osd_printf_verbose("drm_open: %s successfully opened\n", node); return fd; } @@ -1125,6 +1203,10 @@ int video_sdl2::init(osd_interface &osd, osd_options const &options) } } +#ifdef SDLMAME_X11 + dri_device = dynamic_cast(options).dri_device(); +#endif + return 0; } diff --git a/src/osd/modules/render/drawogl.cpp b/src/osd/modules/render/drawogl.cpp index 8d3dd0e93ed6e..b9567991602a3 100644 --- a/src/osd/modules/render/drawogl.cpp +++ b/src/osd/modules/render/drawogl.cpp @@ -63,6 +63,7 @@ typedef uint64_t HashT; #ifdef SDLMAME_X11 +#include // DRM #include #include @@ -563,9 +564,10 @@ static int glsl_shader_feature = glsl_shader_info::FEAT_PLAIN; // FIXME: why is bool renderer_ogl::s_shown_video_info = false; #ifdef SDLMAME_X11 -static int drm_open(); +static int drm_open(const char *dri_device); static void drm_waitvblank(int crtc); static int fd = 0; +static const char* dri_device = nullptr; #endif //============================================================ @@ -780,7 +782,7 @@ int renderer_ogl::create() if (window().index() == 0 && video_config.syncrefresh && video_config.sync_mode != 0) { // Try to open DRM device - fd = drm_open(); + fd = drm_open(dri_device); if (fd != 0) m_gl_context->set_swap_interval((video_config.sync_mode == 2 || video_config.sync_mode == 4)? 1 : 0); } @@ -816,18 +818,94 @@ int renderer_ogl::create() // drm_open //============================================================ -static int drm_open() +static int drm_open(const char *dri_device) { int fd = 0; - const char *node = {"/dev/dri/card0"}; + char dri_path[16]; + char *node = dri_path; + + // Dri device forced by user + if (strcmp(dri_device, "auto") != 0) + { + osd_printf_verbose("drm_open: %s for by user\n", dri_device); + snprintf(node, sizeof(dri_path), "/dev/dri/%s", dri_device); + } + + // Automatic selection + else + { + // Get an array of drm devices to check + int num_devices = drmGetDevices2(0, NULL, 0); + if (num_devices <= 0) + { + osd_printf_error("drm_open: couldn't find any drm device\n"); + return 0; + } + + drmDevicePtr *devices = (drmDevicePtr*)calloc(num_devices, sizeof(drmDevicePtr)); + if (drmGetDevices2(0, devices, num_devices) < 0) + { + osd_printf_error("drm_open: drmGetDevices2() failed\n"); + return 0; + } + + // Parse device list to find the first one with a valid connector + bool found = false; + + for (int i = 0; i < num_devices; i++) + { + // Skip non-primary nodes + if (devices[i]->available_nodes & (1 << DRM_NODE_PRIMARY)) + node = devices[i]->nodes[DRM_NODE_PRIMARY]; + + else continue; + + fd = open(node, O_RDWR | O_CLOEXEC); + if (fd < 0) + { + osd_printf_error("drm_open: couldn't open %s\n", node); + continue; + } + drmModeRes *resources = drmModeGetResources(fd); + if (resources && resources->count_connectors > 0 && resources->count_encoders > 0 && resources->count_crtcs > 0) + { + for (int j = 0; j < resources->count_connectors; j++) + { + drmModeConnector *conn = drmModeGetConnector(fd, resources->connectors[j]); + if (!conn) continue; + + // We found a valid connector, use it + if (conn->connection == DRM_MODE_CONNECTED && conn->count_modes > 0) + found = true; + + drmModeFreeConnector(conn); + if (found) break; + } + } + drmModeFreeResources(resources); + close(fd); + + if (found) break; + } + + drmFreeDevices(devices, num_devices); + free(devices); + + if (!found) + { + osd_printf_error("drm_open: couldn't find any device with a valid connector\n"); + return 0; + } + } fd = open(node, O_RDWR | O_CLOEXEC); if (fd < 0) { - fprintf(stderr, "cannot open %s\n", node); + osd_printf_error("drm_open: cannot open %s\n", node); return 0; } - osd_printf_verbose("%s successfully opened\n", node); + + osd_printf_verbose("drm_open: %s successfully opened\n", node); return fd; } @@ -3106,6 +3184,8 @@ int video_opengl::init(osd_interface &osd, osd_options const &options) osd_printf_verbose("Using SDL multi-window OpenGL driver (SDL 2.0+)\n"); #endif // defined(OSD_WINDOWS) + dri_device = dynamic_cast(options).dri_device(); + return 0; }