diff --git a/src/platform/common.h b/src/platform/common.h index a114af709e3..dc7be4ed14f 100644 --- a/src/platform/common.h +++ b/src/platform/common.h @@ -861,6 +861,8 @@ namespace platf { */ std::string get_host_name(); + std::string find_render_node_with_display(); + /** * @brief Gets the supported gamepads for this platform backend. * @details This may be called prior to `platf::input()`! diff --git a/src/platform/linux/misc.cpp b/src/platform/linux/misc.cpp index ba40a802873..df6d7f365ab 100644 --- a/src/platform/linux/misc.cpp +++ b/src/platform/linux/misc.cpp @@ -38,6 +38,12 @@ #include #include +#ifdef SUNSHINE_BUILD_DRM + #include + #include + #include +#endif + // local includes #include "graphics.h" #include "misc.h" @@ -1163,4 +1169,53 @@ namespace platf { std::unique_ptr create_high_precision_timer() { return std::make_unique(); } + + std::string find_render_node_with_display() { +#ifdef SUNSHINE_BUILD_DRM + auto *dir = opendir("/dev/dri"); + if (!dir) { + return {}; + } + + std::string result; + while (auto *entry = readdir(dir)) { + if (strncmp(entry->d_name, "card", 4) != 0 || !isdigit(entry->d_name[4])) { + continue; + } + + std::string path = std::string("/dev/dri/") + entry->d_name; + int fd = open(path.c_str(), O_RDWR); + if (fd < 0) { + continue; + } + + auto *res = drmModeGetResources(fd); + if (res) { + for (int i = 0; i < res->count_connectors && result.empty(); i++) { + auto *conn = drmModeGetConnector(fd, res->connectors[i]); + if (conn) { + if (conn->connection == DRM_MODE_CONNECTED) { + char *render = drmGetRenderDeviceNameFromFd(fd); + if (render) { + result = render; + free(render); + } + } + drmModeFreeConnector(conn); + } + } + drmModeFreeResources(res); + } + close(fd); + if (!result.empty()) { + break; + } + } + closedir(dir); + return result; +#else + return {}; +#endif + } + } // namespace platf diff --git a/src/platform/linux/vulkan_encode.cpp b/src/platform/linux/vulkan_encode.cpp index 3113e11fd48..f8fdbf44d72 100644 --- a/src/platform/linux/vulkan_encode.cpp +++ b/src/platform/linux/vulkan_encode.cpp @@ -79,7 +79,9 @@ namespace vk { static int create_vulkan_hwdevice(AVBufferRef **hw_device_buf) { // Resolve render device path to Vulkan device index - if (auto render_path = config::video.adapter_name.empty() ? "/dev/dri/renderD128" : config::video.adapter_name; render_path[0] == '/') { + auto detected = platf::find_render_node_with_display(); + auto fallback = detected.empty() ? std::string("/dev/dri/renderD128") : detected; + if (auto render_path = config::video.adapter_name.empty() ? fallback : config::video.adapter_name; render_path[0] == '/') { if (auto idx = find_vulkan_index_for_render_node(render_path.c_str()); !idx.empty() && av_hwdevice_ctx_create(hw_device_buf, AV_HWDEVICE_TYPE_VULKAN, idx.c_str(), nullptr, 0) >= 0) { return 0; } diff --git a/src/platform/macos/misc.mm b/src/platform/macos/misc.mm index c678d80c504..89edae10ca0 100644 --- a/src/platform/macos/misc.mm +++ b/src/platform/macos/misc.mm @@ -560,6 +560,10 @@ operator bool() override { std::unique_ptr create_high_precision_timer() { return std::make_unique(); } + + std::string find_render_node_with_display() { + return {}; + } } // namespace platf namespace dyn { diff --git a/src/platform/windows/misc.cpp b/src/platform/windows/misc.cpp index 40a23f00cbf..fb94860127b 100644 --- a/src/platform/windows/misc.cpp +++ b/src/platform/windows/misc.cpp @@ -1791,4 +1791,8 @@ namespace platf { return true; } + + std::string find_render_node_with_display() { + return {}; + } } // namespace platf diff --git a/src/video.cpp b/src/video.cpp index 67234bc112d..c5fa03da31c 100644 --- a/src/video.cpp +++ b/src/video.cpp @@ -3019,8 +3019,10 @@ namespace video { return hw_device_buf; } - // Try render device path first (like VAAPI does), then fallback to device indices - auto render_device = config::video.adapter_name.empty() ? "/dev/dri/renderD128" : config::video.adapter_name.c_str(); + // Try render device path first, auto-detecting the GPU with a connected display + auto detected = platf::find_render_node_with_display(); + auto fallback = detected.empty() ? std::string("/dev/dri/renderD128") : detected; + auto render_device = config::video.adapter_name.empty() ? fallback.c_str() : config::video.adapter_name.c_str(); auto status = av_hwdevice_ctx_create(&hw_device_buf, AV_HWDEVICE_TYPE_VULKAN, render_device, nullptr, 0); if (status >= 0) {