Merge branch 'match-platform-device' into 'master'

Draft: render: open platform device render node

See merge request wlroots/wlroots!4372
This commit is contained in:
Simon Ser 2023-11-19 18:44:40 +00:00
commit e6d9aa5d62
8 changed files with 133 additions and 130 deletions

View file

@ -7,7 +7,6 @@ struct wlr_egl {
EGLDisplay display; EGLDisplay display;
EGLContext context; EGLContext context;
EGLDeviceEXT device; // may be EGL_NO_DEVICE_EXT EGLDeviceEXT device; // may be EGL_NO_DEVICE_EXT
struct gbm_device *gbm_device;
struct { struct {
// Display extensions // Display extensions
@ -23,7 +22,6 @@ struct wlr_egl {
// Client extensions // Client extensions
bool EXT_device_query; bool EXT_device_query;
bool KHR_platform_gbm;
bool EXT_platform_device; bool EXT_platform_device;
bool KHR_display_reference; bool KHR_display_reference;
} exts; } exts;

View file

@ -146,6 +146,8 @@ bool wlr_renderer_init_wl_shm(struct wlr_renderer *r,
/** /**
* Obtains the FD of the DRM device used for rendering, or -1 if unavailable. * Obtains the FD of the DRM device used for rendering, or -1 if unavailable.
* *
* The FD is guaranteed to be a DRM render node.
*
* The caller doesn't have ownership of the FD, it must not close it. * The caller doesn't have ownership of the FD, it must not close it.
*/ */
int wlr_renderer_get_drm_fd(struct wlr_renderer *r); int wlr_renderer_get_drm_fd(struct wlr_renderer *r);

View file

@ -5,7 +5,6 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <gbm.h>
#include <wlr/render/egl.h> #include <wlr/render/egl.h>
#include <wlr/util/log.h> #include <wlr/util/log.h>
#include <wlr/util/region.h> #include <wlr/util/region.h>
@ -219,8 +218,6 @@ static struct wlr_egl *egl_create(void) {
load_egl_proc(&egl->procs.eglGetPlatformDisplayEXT, load_egl_proc(&egl->procs.eglGetPlatformDisplayEXT,
"eglGetPlatformDisplayEXT"); "eglGetPlatformDisplayEXT");
egl->exts.KHR_platform_gbm = check_egl_ext(client_exts_str,
"EGL_KHR_platform_gbm");
egl->exts.EXT_platform_device = check_egl_ext(client_exts_str, egl->exts.EXT_platform_device = check_egl_ext(client_exts_str,
"EGL_EXT_platform_device"); "EGL_EXT_platform_device");
egl->exts.KHR_display_reference = check_egl_ext(client_exts_str, egl->exts.KHR_display_reference = check_egl_ext(client_exts_str,
@ -496,28 +493,6 @@ static EGLDeviceEXT get_egl_device_from_drm_fd(struct wlr_egl *egl,
return egl_device; return egl_device;
} }
static int open_render_node(int drm_fd) {
char *render_name = drmGetRenderDeviceNameFromFd(drm_fd);
if (render_name == NULL) {
// This can happen on split render/display platforms, fallback to
// primary node
render_name = drmGetPrimaryDeviceNameFromFd(drm_fd);
if (render_name == NULL) {
wlr_log_errno(WLR_ERROR, "drmGetPrimaryDeviceNameFromFd failed");
return -1;
}
wlr_log(WLR_DEBUG, "DRM device '%s' has no render node, "
"falling back to primary node", render_name);
}
int render_fd = open(render_name, O_RDWR | O_CLOEXEC);
if (render_fd < 0) {
wlr_log_errno(WLR_ERROR, "Failed to open DRM node '%s'", render_name);
}
free(render_name);
return render_fd;
}
struct wlr_egl *wlr_egl_create_with_drm_fd(int drm_fd) { struct wlr_egl *wlr_egl_create_with_drm_fd(int drm_fd) {
struct wlr_egl *egl = egl_create(); struct wlr_egl *egl = egl_create();
if (egl == NULL) { if (egl == NULL) {
@ -525,48 +500,21 @@ struct wlr_egl *wlr_egl_create_with_drm_fd(int drm_fd) {
return NULL; return NULL;
} }
if (egl->exts.EXT_platform_device) { if (!egl->exts.EXT_platform_device) {
/*
* Search for the EGL device matching the DRM fd using the
* EXT_device_enumeration extension.
*/
EGLDeviceEXT egl_device = get_egl_device_from_drm_fd(egl, drm_fd);
if (egl_device != EGL_NO_DEVICE_EXT) {
if (egl_init(egl, EGL_PLATFORM_DEVICE_EXT, egl_device)) {
wlr_log(WLR_DEBUG, "Using EGL_PLATFORM_DEVICE_EXT");
return egl;
}
goto error;
}
/* Falls back on GBM in case the device was not found */
} else {
wlr_log(WLR_DEBUG, "EXT_platform_device not supported"); wlr_log(WLR_DEBUG, "EXT_platform_device not supported");
}
if (egl->exts.KHR_platform_gbm) {
int gbm_fd = open_render_node(drm_fd);
if (gbm_fd < 0) {
wlr_log(WLR_ERROR, "Failed to open DRM render node");
goto error; goto error;
} }
egl->gbm_device = gbm_create_device(gbm_fd); EGLDeviceEXT egl_device = get_egl_device_from_drm_fd(egl, drm_fd);
if (!egl->gbm_device) { if (egl_device == EGL_NO_DEVICE_EXT) {
close(gbm_fd); goto error;
wlr_log(WLR_ERROR, "Failed to create GBM device"); }
if (!egl_init(egl, EGL_PLATFORM_DEVICE_EXT, egl_device)) {
goto error; goto error;
} }
if (egl_init(egl, EGL_PLATFORM_GBM_KHR, egl->gbm_device)) {
wlr_log(WLR_DEBUG, "Using EGL_PLATFORM_GBM_KHR");
return egl; return egl;
}
gbm_device_destroy(egl->gbm_device);
close(gbm_fd);
} else {
wlr_log(WLR_DEBUG, "KHR_platform_gbm not supported");
}
error: error:
wlr_log(WLR_ERROR, "Failed to initialize EGL context"); wlr_log(WLR_ERROR, "Failed to initialize EGL context");
@ -623,12 +571,6 @@ void wlr_egl_destroy(struct wlr_egl *egl) {
eglReleaseThread(); eglReleaseThread();
if (egl->gbm_device) {
int gbm_fd = gbm_device_get_fd(egl->gbm_device);
gbm_device_destroy(egl->gbm_device);
close(gbm_fd);
}
free(egl); free(egl);
} }
@ -936,12 +878,7 @@ static char *get_render_name(const char *name) {
if (match == NULL) { if (match == NULL) {
wlr_log(WLR_ERROR, "Cannot find DRM device %s", name); wlr_log(WLR_ERROR, "Cannot find DRM device %s", name);
} else if (!(match->available_nodes & (1 << DRM_NODE_RENDER))) { } else if (!(match->available_nodes & (1 << DRM_NODE_RENDER))) {
// Likely a split display/render setup. Pick the primary node and hope wlr_log(WLR_ERROR, "DRM device has no render node");
// Mesa will open the right render node under-the-hood.
wlr_log(WLR_DEBUG, "DRM device %s has no render node, "
"falling back to primary node", name);
assert(match->available_nodes & (1 << DRM_NODE_PRIMARY));
render_name = strdup(match->nodes[DRM_NODE_PRIMARY]);
} else { } else {
render_name = strdup(match->nodes[DRM_NODE_RENDER]); render_name = strdup(match->nodes[DRM_NODE_RENDER]);
} }

View file

@ -23,9 +23,8 @@ endif
if 'gles2' in renderers or 'auto' in renderers if 'gles2' in renderers or 'auto' in renderers
egl = dependency('egl', required: 'gles2' in renderers) egl = dependency('egl', required: 'gles2' in renderers)
gbm = dependency('gbm', required: 'gles2' in renderers) if egl.found()
if egl.found() and gbm.found() wlr_deps += egl
wlr_deps += [egl, gbm]
wlr_files += files('egl.c') wlr_files += files('egl.c')
internal_features += { 'egl': true } internal_features += { 'egl': true }
endif endif

View file

@ -376,32 +376,24 @@ int vulkan_open_phdev_drm_fd(VkPhysicalDevice phdev) {
}; };
vkGetPhysicalDeviceProperties2(phdev, &props); vkGetPhysicalDeviceProperties2(phdev, &props);
dev_t devid; if (!drm_props.hasRender) {
if (drm_props.hasRender) { wlr_log(WLR_ERROR, "Physical device is missing render node");
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; return -1;
} }
dev_t devid = makedev(drm_props.renderMajor, drm_props.renderMinor);
drmDevice *device = NULL; drmDevice *device = NULL;
if (drmGetDeviceFromDevId(devid, 0, &device) != 0) { if (drmGetDeviceFromDevId(devid, 0, &device) != 0) {
wlr_log_errno(WLR_ERROR, "drmGetDeviceFromDevId failed"); wlr_log_errno(WLR_ERROR, "drmGetDeviceFromDevId failed");
return -1; return -1;
} }
const char *name = NULL; if (!(device->available_nodes & (1 << DRM_NODE_RENDER))) {
if (device->available_nodes & (1 << DRM_NODE_RENDER)) { wlr_log(WLR_ERROR, "DRM device has no render node");
name = device->nodes[DRM_NODE_RENDER]; return -1;
} 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);
} }
const char *name = device->nodes[DRM_NODE_RENDER];
int drm_fd = open(name, O_RDWR | O_NONBLOCK | O_CLOEXEC); int drm_fd = open(name, O_RDWR | O_NONBLOCK | O_CLOEXEC);
if (drm_fd < 0) { if (drm_fd < 0) {
wlr_log_errno(WLR_ERROR, "Failed to open DRM node %s", name); wlr_log_errno(WLR_ERROR, "Failed to open DRM node %s", name);

View file

@ -245,27 +245,42 @@ bool wlr_renderer_init_wl_display(struct wlr_renderer *r,
return true; return true;
} }
static int open_drm_render_node(void) { static drmDevice **list_drm_devices(void) {
uint32_t flags = 0; uint32_t flags = 0;
int devices_len = drmGetDevices2(flags, NULL, 0); int devices_len = drmGetDevices2(flags, NULL, 0);
if (devices_len < 0) { if (devices_len < 0) {
wlr_log(WLR_ERROR, "drmGetDevices2 failed: %s", strerror(-devices_len)); wlr_log(WLR_ERROR, "drmGetDevices2 failed: %s", strerror(-devices_len));
return -1; return NULL;
} }
drmDevice **devices = calloc(devices_len, sizeof(*devices)); drmDevice **devices = calloc(devices_len + 1, sizeof(*devices));
if (devices == NULL) { if (devices == NULL) {
wlr_log_errno(WLR_ERROR, "Allocation failed"); wlr_log_errno(WLR_ERROR, "Allocation failed");
return -1; return NULL;
} }
devices_len = drmGetDevices2(flags, devices, devices_len); devices_len = drmGetDevices2(flags, devices, devices_len);
if (devices_len < 0) { if (devices_len < 0) {
free(devices); free(devices);
wlr_log(WLR_ERROR, "drmGetDevices2 failed: %s", strerror(-devices_len)); wlr_log(WLR_ERROR, "drmGetDevices2 failed: %s", strerror(-devices_len));
return NULL;
}
return devices;
}
static void destroy_drm_devices(drmDevice **devices) {
for (size_t i = 0; devices[i] != NULL; i++) {
drmFreeDevice(&devices[i]);
}
free(devices);
}
static int open_drm_render_node(void) {
drmDevice **devices = list_drm_devices();
if (devices == NULL) {
return -1; return -1;
} }
int fd = -1; int fd = -1;
for (int i = 0; i < devices_len; i++) { for (size_t i = 0; devices[i] != NULL; i++) {
drmDevice *dev = devices[i]; drmDevice *dev = devices[i];
if (dev->available_nodes & (1 << DRM_NODE_RENDER)) { if (dev->available_nodes & (1 << DRM_NODE_RENDER)) {
const char *name = dev->nodes[DRM_NODE_RENDER]; const char *name = dev->nodes[DRM_NODE_RENDER];
@ -283,11 +298,73 @@ static int open_drm_render_node(void) {
} }
out: out:
for (int i = 0; i < devices_len; i++) { destroy_drm_devices(devices);
drmFreeDevice(&devices[i]); return fd;
} }
free(devices);
static int open_backend_drm_render_node(int backend_drm_fd) {
// First, try to open the render node of the DRM device used by the backend
drmDevice *dev = NULL;
if (drmGetDevice2(backend_drm_fd, 0, &dev) != 0) {
wlr_log(WLR_ERROR, "drmGetDevice2 failed");
return -1;
}
if (dev->available_nodes & (1 << DRM_NODE_RENDER)) {
const char *name = dev->nodes[DRM_NODE_RENDER];
wlr_log(WLR_DEBUG, "Opening DRM render node '%s'", name);
int fd = open(name, O_RDWR | O_CLOEXEC);
if (fd < 0) {
wlr_log_errno(WLR_ERROR, "Failed to open '%s'", name);
}
drmFreeDevice(&dev);
return fd;
}
if (dev->bustype != DRM_BUS_PLATFORM) {
wlr_log(WLR_ERROR, "DRM device '%s' does not have a render node",
dev->nodes[DRM_NODE_PRIMARY]);
drmFreeDevice(&dev);
return -1;
}
drmFreeDevice(&dev);
// Try to find another platform device with a render node
drmDevice **devices = list_drm_devices();
if (devices == NULL) {
return -1;
}
int fd = -1;
for (size_t i = 0; devices[i] != NULL; i++) {
drmDevice *dev = devices[i];
if (dev->bustype != DRM_BUS_PLATFORM ||
!(dev->available_nodes & (1 << DRM_NODE_RENDER))) {
continue;
}
if (fd >= 0) {
close(fd);
fd = -1;
wlr_log(WLR_ERROR, "Found multiple platform device DRM render nodes");
break;
}
const char *name = dev->nodes[DRM_NODE_RENDER];
wlr_log(WLR_DEBUG, "Opening platform device DRM render node '%s'", name);
fd = open(name, O_RDWR | O_CLOEXEC);
if (fd < 0) {
wlr_log_errno(WLR_ERROR, "Failed to open '%s'", name);
goto out_devices;
}
}
if (fd < 0) {
wlr_log(WLR_ERROR, "Failed to find any platform device DRM render node");
}
out_devices:
destroy_drm_devices(devices);
return fd; return fd;
} }
@ -318,11 +395,15 @@ static bool open_preferred_drm_fd(struct wlr_backend *backend, int *drm_fd_ptr,
return true; return true;
} }
// Prefer the backend's DRM node, if any // Prefer the backend's DRM device, if any
int backend_drm_fd = wlr_backend_get_drm_fd(backend); int backend_drm_fd = wlr_backend_get_drm_fd(backend);
if (backend_drm_fd >= 0) { if (backend_drm_fd >= 0) {
*drm_fd_ptr = backend_drm_fd; int drm_fd = open_backend_drm_render_node(backend_drm_fd);
*own_drm_fd = false; if (drm_fd < 0) {
return false;
}
*drm_fd_ptr = drm_fd;
*own_drm_fd = true;
return true; return true;
} }

View file

@ -214,15 +214,13 @@ struct wlr_drm *wlr_drm_create(struct wl_display *display,
return NULL; return NULL;
} }
char *node_name = NULL; if (!(dev->available_nodes & (1 << DRM_NODE_RENDER))) {
if (dev->available_nodes & (1 << DRM_NODE_RENDER)) { wlr_log(WLR_DEBUG, "No DRM render node available");
node_name = strdup(dev->nodes[DRM_NODE_RENDER]); drmFreeDevice(&dev);
} else { return NULL;
assert(dev->available_nodes & (1 << DRM_NODE_PRIMARY));
wlr_log(WLR_DEBUG, "No DRM render node available, "
"falling back to primary node '%s'", dev->nodes[DRM_NODE_PRIMARY]);
node_name = strdup(dev->nodes[DRM_NODE_PRIMARY]);
} }
char *node_name = strdup(dev->nodes[DRM_NODE_RENDER]);
drmFreeDevice(&dev); drmFreeDevice(&dev);
if (node_name == NULL) { if (node_name == NULL) {
return NULL; return NULL;

View file

@ -884,24 +884,20 @@ static bool set_default_feedback(struct wlr_linux_dmabuf_v1 *linux_dmabuf,
goto error_compiled; goto error_compiled;
} }
const char *name = NULL; if (!(device->available_nodes & (1 << DRM_NODE_RENDER))) {
if (device->available_nodes & (1 << DRM_NODE_RENDER)) { wlr_log(WLR_DEBUG, "DRM device has no render node");
name = device->nodes[DRM_NODE_RENDER];
} else {
// Likely a split display/render setup
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 main_device_fd = open(name, O_RDWR | O_CLOEXEC);
drmFreeDevice(&device);
if (main_device_fd < 0) {
wlr_log_errno(WLR_ERROR, "Failed to open DRM device %s", name);
goto error_compiled; goto error_compiled;
} }
const char *name = device->nodes[DRM_NODE_RENDER];
int main_device_fd = open(name, O_RDWR | O_CLOEXEC);
if (main_device_fd < 0) {
wlr_log_errno(WLR_ERROR, "Failed to open DRM device %s", name);
drmFreeDevice(&device);
goto error_compiled;
}
drmFreeDevice(&device);
size_t tranches_len = size_t tranches_len =
feedback->tranches.size / sizeof(struct wlr_linux_dmabuf_feedback_v1_tranche); feedback->tranches.size / sizeof(struct wlr_linux_dmabuf_feedback_v1_tranche);
const struct wlr_linux_dmabuf_feedback_v1_tranche *tranches = feedback->tranches.data; const struct wlr_linux_dmabuf_feedback_v1_tranche *tranches = feedback->tranches.data;