render/vulkan: add support for split render/display devices

Some systems have a split render/display architecture: the display
side is managed by a KMS driver and the render side is managed by a
separate Vulkan driver. Figuring out that these two drivers can
work together is not trivial.

Currently, the Vulkan renderer tries to find a Vulkan physical
device which matches the DRM device's dev_t. On split render/display,
there is no such device.

The platform bus has historically been abused for situations
where no other bus would make sense (e.g. VKMS, evdi). A new "faux"
bus has been introduced [1] for such devices, so the platform bus
should now be a pretty good hint that all devices are on the same
system-on-chip.

When we don't find a Vulkan physical device and the DRM device is
using the platform bus, fall back to any Vulkan physical which
also uses the platform bus.

[1]: https://lore.kernel.org/all/2025021023-sandstorm-precise-9f5d@gregkh/

Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/work_items/4055
This commit is contained in:
Simon Ser 2026-05-23 15:53:09 +02:00
parent 2ab2775743
commit f141edcd02

View file

@ -10,6 +10,7 @@
#include <sys/types.h>
#include <unistd.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#include <vulkan/vulkan.h>
#include <wlr/util/log.h>
#include <wlr/version.h>
@ -252,6 +253,14 @@ static void log_phdev(const VkPhysicalDeviceProperties *props) {
wlr_log(WLR_INFO, " Driver version: %u.%u.%u", dv_major, dv_minor, dv_patch);
}
struct wlr_vk_phdev_info {
VkPhysicalDeviceType type;
char name[VK_MAX_PHYSICAL_DEVICE_NAME_SIZE];
bool has_drm, has_render;
dev_t primary_devid, render_devid;
};
VkPhysicalDevice vulkan_find_drm_phdev(struct wlr_vk_instance *ini, int drm_fd) {
VkResult res;
uint32_t num_phdevs;
@ -269,12 +278,7 @@ VkPhysicalDevice vulkan_find_drm_phdev(struct wlr_vk_instance *ini, int drm_fd)
return VK_NULL_HANDLE;
}
struct stat drm_stat = {0};
if (drm_fd >= 0 && fstat(drm_fd, &drm_stat) != 0) {
wlr_log_errno(WLR_ERROR, "fstat failed");
return VK_NULL_HANDLE;
}
struct wlr_vk_phdev_info devices[1 + num_phdevs];
for (uint32_t i = 0; i < num_phdevs; ++i) {
VkPhysicalDevice phdev = phdevs[i];
@ -330,25 +334,81 @@ VkPhysicalDevice vulkan_find_drm_phdev(struct wlr_vk_instance *ini, int drm_fd)
wlr_log(WLR_INFO, " Driver name: %s (%s)", driver_props.driverName, driver_props.driverInfo);
}
struct wlr_vk_phdev_info info = {
.type = phdev_props.deviceType,
.has_drm = has_drm_props,
};
memcpy(info.name, phdev_props.deviceName, sizeof(phdev_props.deviceName));
if (has_drm_props) {
info.has_render = drm_props.hasRender;
info.primary_devid = makedev(drm_props.primaryMajor, drm_props.primaryMinor);
info.render_devid = makedev(drm_props.renderMajor, drm_props.renderMinor);
}
devices[i] = info;
}
// Find a Vulkan device matching the DRM device's dev_t
struct stat drm_stat = {0};
if (drm_fd >= 0 && fstat(drm_fd, &drm_stat) != 0) {
wlr_log_errno(WLR_ERROR, "fstat failed");
return VK_NULL_HANDLE;
}
for (size_t i = 0; i < num_phdevs; ++i) {
VkPhysicalDevice phdev = phdevs[i];
const struct wlr_vk_phdev_info *info = &devices[i];
bool found;
if (drm_fd >= 0) {
if (!has_drm_props) {
if (!info->has_drm) {
wlr_log(WLR_DEBUG, " Ignoring physical device \"%s\": "
"VK_EXT_physical_device_drm not supported",
phdev_props.deviceName);
"VK_EXT_physical_device_drm not supported", info->name);
continue;
}
dev_t primary_devid = makedev(drm_props.primaryMajor, drm_props.primaryMinor);
dev_t render_devid = makedev(drm_props.renderMajor, drm_props.renderMinor);
found = primary_devid == drm_stat.st_rdev || render_devid == drm_stat.st_rdev;
found = info->primary_devid == drm_stat.st_rdev || info->render_devid == drm_stat.st_rdev;
} else {
found = phdev_props.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU;
found = info->type == VK_PHYSICAL_DEVICE_TYPE_CPU;
}
if (found) {
wlr_log(WLR_INFO, "Found matching Vulkan physical device: %s",
phdev_props.deviceName);
wlr_log(WLR_INFO, "Found matching Vulkan physical device: %s", info->name);
return phdev;
}
}
// If the DRM device we're interested in uses the platform bus, is
// KMS-capable, and has no render node associated, search for a sibling
// render-capable platform bus device. The platform bus is a good enough
// hint that these two devices should be compatible.
drmDevice *drm_dev = NULL;
if (drmGetDevice2(drm_fd, 0, &drm_dev) != 0) {
wlr_log(WLR_ERROR, "drmGetDevice2() failed");
return VK_NULL_HANDLE;
}
bool is_platform_lone_kms = drm_dev->bustype == DRM_BUS_PLATFORM &&
!(drm_dev->available_nodes & (1 << DRM_NODE_RENDER)) && drmIsKMS(drm_fd);
drmFreeDevice(&drm_dev);
if (!is_platform_lone_kms) {
return VK_NULL_HANDLE;
}
for (size_t i = 0; i < num_phdevs; ++i) {
VkPhysicalDevice phdev = phdevs[i];
const struct wlr_vk_phdev_info *info = &devices[i];
if (!info->has_render) {
continue;
}
drmDevice *drm_dev = NULL;
if (drmGetDeviceFromDevId(info->render_devid, 0, &drm_dev) != 0) {
wlr_log(WLR_ERROR, "drmGetDeviceFromDevId() failed");
return VK_NULL_HANDLE;
}
if (drm_dev->bustype == DRM_BUS_PLATFORM) {
wlr_log(WLR_INFO, "Found Vulkan physical device on platform bus: %s", info->name);
return phdev;
}
}