diff --git a/include/render/vulkan.h b/include/render/vulkan.h index 66739c40..d61db616 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -66,6 +66,7 @@ struct wlr_vk_device { // Tries to find the VkPhysicalDevice for the given drm fd. // Might find none and return VK_NULL_HANDLE. VkPhysicalDevice vulkan_find_drm_phdev(struct wlr_vk_instance *ini, int drm_fd); +int vulkan_open_phdev_drm_fd(VkPhysicalDevice phdev); // Creates a device for the given instance and physical device. struct wlr_vk_device *vulkan_device_create(struct wlr_vk_instance *ini, diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index 4e326c69..667301f9 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -2240,10 +2240,10 @@ struct wlr_renderer *wlr_vk_renderer_create_with_drm_fd(int drm_fd) { return NULL; } - // We duplicate it so it's not closed while we still need it. - dev->drm_fd = fcntl(drm_fd, F_DUPFD_CLOEXEC, 0); + // Do not use the drm_fd that was passed in: we should prefer the render + // node even if a primary node was provided + dev->drm_fd = vulkan_open_phdev_drm_fd(phdev); if (dev->drm_fd < 0) { - wlr_log_errno(WLR_ERROR, "fcntl(F_DUPFD_CLOEXEC) failed"); vulkan_device_destroy(dev); vulkan_instance_destroy(ini); return NULL; diff --git a/render/vulkan/vulkan.c b/render/vulkan/vulkan.c index 3849da66..418b7c8c 100644 --- a/render/vulkan/vulkan.c +++ b/render/vulkan/vulkan.c @@ -1,4 +1,8 @@ +#if !defined(__FreeBSD__) +#define _POSIX_C_SOURCE 200809L +#endif #include +#include #include #include #include @@ -366,6 +370,52 @@ VkPhysicalDevice vulkan_find_drm_phdev(struct wlr_vk_instance *ini, int drm_fd) return VK_NULL_HANDLE; } +int vulkan_open_phdev_drm_fd(VkPhysicalDevice phdev) { + // vulkan_find_drm_phdev() already checks that VK_EXT_physical_device_drm + // is supported + VkPhysicalDeviceDrmPropertiesEXT drm_props = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRM_PROPERTIES_EXT, + }; + VkPhysicalDeviceProperties2 props = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, + .pNext = &drm_props, + }; + vkGetPhysicalDeviceProperties2(phdev, &props); + + dev_t devid; + if (drm_props.hasRender) { + devid = makedev(drm_props.renderMajor, drm_props.renderMinor); + } else if (drm_props.hasPrimary) { + devid = makedev(drm_props.primaryMajor, drm_props.primaryMinor); + } else { + wlr_log(WLR_ERROR, "Physical device is missing both render and primary nodes"); + return -1; + } + + drmDevice *device = NULL; + if (drmGetDeviceFromDevId(devid, 0, &device) != 0) { + wlr_log_errno(WLR_ERROR, "drmGetDeviceFromDevId failed"); + return -1; + } + + const char *name = NULL; + if (device->available_nodes & (1 << DRM_NODE_RENDER)) { + name = device->nodes[DRM_NODE_RENDER]; + } else { + assert(device->available_nodes & (1 << DRM_NODE_PRIMARY)); + name = device->nodes[DRM_NODE_PRIMARY]; + wlr_log(WLR_DEBUG, "DRM device %s has no render node, " + "falling back to primary node", name); + } + + int drm_fd = open(name, O_RDWR | O_NONBLOCK | O_CLOEXEC); + if (drm_fd < 0) { + wlr_log_errno(WLR_ERROR, "Failed to open DRM node %s", name); + } + drmFreeDevice(&device); + return drm_fd; +} + static void load_device_proc(struct wlr_vk_device *dev, const char *name, void *proc_ptr) { void *proc = (void *)vkGetDeviceProcAddr(dev->dev, name);