fix(linux): auto-detect GPU with connected display for VAAPI and Vulkan (#4961)
This commit is contained in:
@@ -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()`!
|
||||
|
||||
@@ -38,6 +38,12 @@
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef SUNSHINE_BUILD_DRM
|
||||
#include <dirent.h>
|
||||
#include <xf86drm.h>
|
||||
#include <xf86drmMode.h>
|
||||
#endif
|
||||
|
||||
// local includes
|
||||
#include "graphics.h"
|
||||
#include "misc.h"
|
||||
@@ -1163,4 +1169,61 @@ namespace platf {
|
||||
std::unique_ptr<high_precision_timer> create_high_precision_timer() {
|
||||
return std::make_unique<linux_high_precision_timer>();
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -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<platf::avcodec_encode_device_t> 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));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -560,6 +560,10 @@ namespace platf {
|
||||
std::unique_ptr<high_precision_timer> create_high_precision_timer() {
|
||||
return std::make_unique<macos_high_precision_timer>();
|
||||
}
|
||||
|
||||
std::string resolve_render_device() {
|
||||
return {};
|
||||
}
|
||||
} // namespace platf
|
||||
|
||||
namespace dyn {
|
||||
|
||||
@@ -1791,4 +1791,8 @@ namespace platf {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string resolve_render_device() {
|
||||
return {};
|
||||
}
|
||||
} // namespace platf
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user