diff --git a/src/platform/common.h b/src/platform/common.h index a114af70..7a15cb09 100644 --- a/src/platform/common.h +++ b/src/platform/common.h @@ -861,6 +861,15 @@ namespace platf { */ std::string get_host_name(); + /** + * @brief Resolves the render device path to use for hardware encoding. + * @details If `config::video.adapter_name` is set, returns that. + * Otherwise, auto-detects the GPU with a connected display via `find_render_node_with_display()`. + * Falls back to `/dev/dri/renderD128` if detection fails. + * @return Resolved render device path (may be empty on non-Linux platforms). + */ + std::string resolve_render_device(); + /** * @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 ba40a802..6839c0bb 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,61 @@ 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 + } + + std::string resolve_render_device() { + if (!config::video.adapter_name.empty()) { + return config::video.adapter_name; + } + auto detected = find_render_node_with_display(); + return detected.empty() ? "/dev/dri/renderD128" : detected; + } + } // namespace platf diff --git a/src/platform/linux/vaapi.cpp b/src/platform/linux/vaapi.cpp index 18d7ff48..525ef76b 100644 --- a/src/platform/linux/vaapi.cpp +++ b/src/platform/linux/vaapi.cpp @@ -522,9 +522,7 @@ namespace va { va::display_t display {vaGetDisplayDRM(fd)}; if (!display) { - auto render_device = config::video.adapter_name.empty() ? "/dev/dri/renderD128" : config::video.adapter_name.c_str(); - - BOOST_LOG(error) << "Couldn't open a va display from DRM with device: "sv << render_device; + BOOST_LOG(error) << "Couldn't open a va display from DRM with device: "sv << platf::resolve_render_device(); return -1; } @@ -642,9 +640,9 @@ namespace va { } std::unique_ptr make_avcodec_encode_device(int width, int height, int offset_x, int offset_y, bool vram) { - auto render_device = config::video.adapter_name.empty() ? "/dev/dri/renderD128" : config::video.adapter_name.c_str(); + auto render_device = platf::resolve_render_device(); - file_t file = open(render_device, O_RDWR); + file_t file = ::open(render_device.c_str(), O_RDWR); // NOSONAR(cpp:S1874) - `_sopen_s` not available if (file.el < 0) { char string[1024]; BOOST_LOG(error) << "Couldn't open "sv << render_device << ": " << strerror_r(errno, string, sizeof(string)); diff --git a/src/platform/linux/vulkan_encode.cpp b/src/platform/linux/vulkan_encode.cpp index 31927179..3e49fad7 100644 --- a/src/platform/linux/vulkan_encode.cpp +++ b/src/platform/linux/vulkan_encode.cpp @@ -79,7 +79,7 @@ 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] == '/') { + if (auto render_path = platf::resolve_render_device(); 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 c678d80c..12edfd7f 100644 --- a/src/platform/macos/misc.mm +++ b/src/platform/macos/misc.mm @@ -560,6 +560,10 @@ namespace platf { std::unique_ptr create_high_precision_timer() { return std::make_unique(); } + + std::string resolve_render_device() { + return {}; + } } // namespace platf namespace dyn { diff --git a/src/platform/windows/misc.cpp b/src/platform/windows/misc.cpp index 40a23f00..32bfef15 100644 --- a/src/platform/windows/misc.cpp +++ b/src/platform/windows/misc.cpp @@ -1791,4 +1791,8 @@ namespace platf { return true; } + + std::string resolve_render_device() { + return {}; + } } // namespace platf diff --git a/src/video.cpp b/src/video.cpp index 67234bc1..17106133 100644 --- a/src/video.cpp +++ b/src/video.cpp @@ -2994,9 +2994,9 @@ namespace video { return hw_device_buf; } - auto render_device = config::video.adapter_name.empty() ? nullptr : config::video.adapter_name.c_str(); + auto render_device = platf::resolve_render_device(); - auto status = av_hwdevice_ctx_create(&hw_device_buf, AV_HWDEVICE_TYPE_VAAPI, render_device, nullptr, 0); + auto status = av_hwdevice_ctx_create(&hw_device_buf, AV_HWDEVICE_TYPE_VAAPI, render_device.empty() ? nullptr : render_device.c_str(), nullptr, 0); if (status < 0) { char string[AV_ERROR_MAX_STRING_SIZE]; BOOST_LOG(error) << "Failed to create a VAAPI device: "sv << av_make_error_string(string, AV_ERROR_MAX_STRING_SIZE, status); @@ -3019,10 +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 render_device = platf::resolve_render_device(); - auto status = av_hwdevice_ctx_create(&hw_device_buf, AV_HWDEVICE_TYPE_VULKAN, render_device, nullptr, 0); + auto status = av_hwdevice_ctx_create(&hw_device_buf, AV_HWDEVICE_TYPE_VULKAN, render_device.c_str(), nullptr, 0); if (status >= 0) { BOOST_LOG(info) << "Using Vulkan device: "sv << render_device; return hw_device_buf;