diff --git a/include/wlr/render/egl.h b/include/wlr/render/egl.h index 3a09ba97..903b70a3 100644 --- a/include/wlr/render/egl.h +++ b/include/wlr/render/egl.h @@ -44,6 +44,11 @@ struct wlr_egl { // Device extensions bool EXT_device_drm; bool EXT_device_drm_render_node; + + // Client extensions + bool EXT_device_query; + bool KHR_platform_gbm; + bool EXT_platform_device; } exts; struct { @@ -56,6 +61,7 @@ struct wlr_egl { PFNEGLDEBUGMESSAGECONTROLKHRPROC eglDebugMessageControlKHR; PFNEGLQUERYDISPLAYATTRIBEXTPROC eglQueryDisplayAttribEXT; PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT; + PFNEGLQUERYDEVICESEXTPROC eglQueryDevicesEXT; } procs; struct wlr_drm_format_set dmabuf_texture_formats; diff --git a/render/egl.c b/render/egl.c index efb4e8d9..3b57b0d5 100644 --- a/render/egl.c +++ b/render/egl.c @@ -156,7 +156,7 @@ out: free(formats); } -struct wlr_egl *wlr_egl_create_with_drm_fd(int drm_fd) { +static struct wlr_egl *egl_create(void) { struct wlr_egl *egl = calloc(1, sizeof(struct wlr_egl)); if (egl == NULL) { wlr_log_errno(WLR_ERROR, "Allocation failed"); @@ -172,11 +172,7 @@ struct wlr_egl *wlr_egl_create_with_drm_fd(int drm_fd) { } return NULL; } - - if (!check_egl_ext(client_exts_str, "EGL_KHR_platform_gbm")) { - wlr_log(WLR_ERROR, "EGL_KHR_platform_gbm not supported"); - return NULL; - } + wlr_log(WLR_INFO, "Supported EGL client extensions: %s", client_exts_str); if (!check_egl_ext(client_exts_str, "EGL_EXT_platform_base")) { wlr_log(WLR_ERROR, "EGL_EXT_platform_base not supported"); @@ -185,6 +181,24 @@ struct wlr_egl *wlr_egl_create_with_drm_fd(int drm_fd) { load_egl_proc(&egl->procs.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_EXT_platform_device"); + + if (check_egl_ext(client_exts_str, "EGL_EXT_device_enumeration")) { + load_egl_proc(&egl->procs.eglQueryDevicesEXT, "eglQueryDevicesEXT"); + } + + if (check_egl_ext(client_exts_str, "EGL_EXT_device_query")) { + egl->exts.EXT_device_query = true; + load_egl_proc(&egl->procs.eglQueryDeviceStringEXT, + "eglQueryDeviceStringEXT"); + load_egl_proc(&egl->procs.eglQueryDisplayAttribEXT, + "eglQueryDisplayAttribEXT"); + } + if (check_egl_ext(client_exts_str, "EGL_KHR_debug")) { load_egl_proc(&egl->procs.eglDebugMessageControlKHR, "eglDebugMessageControlKHR"); @@ -201,32 +215,31 @@ struct wlr_egl *wlr_egl_create_with_drm_fd(int drm_fd) { if (eglBindAPI(EGL_OPENGL_ES_API) == EGL_FALSE) { wlr_log(WLR_ERROR, "Failed to bind to the OpenGL ES API"); - goto error; + return NULL; } - egl->gbm_device = gbm_create_device(drm_fd); - if (!egl->gbm_device) { - wlr_log(WLR_ERROR, "Failed to create GBM device"); - goto error; - } + return egl; +} - egl->display = egl->procs.eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_KHR, - egl->gbm_device, NULL); +static bool egl_init(struct wlr_egl *egl, EGLenum platform, + void *remote_display) { + egl->display = egl->procs.eglGetPlatformDisplayEXT(platform, + remote_display, NULL); if (egl->display == EGL_NO_DISPLAY) { wlr_log(WLR_ERROR, "Failed to create EGL display"); - goto error; + return false; } EGLint major, minor; if (eglInitialize(egl->display, &major, &minor) == EGL_FALSE) { wlr_log(WLR_ERROR, "Failed to initialize EGL"); - goto error; + return false; } const char *display_exts_str = eglQueryString(egl->display, EGL_EXTENSIONS); if (display_exts_str == NULL) { wlr_log(WLR_ERROR, "Failed to query EGL display extensions"); - return NULL; + return false; } if (check_egl_ext(display_exts_str, "EGL_KHR_image_base")) { @@ -247,17 +260,12 @@ struct wlr_egl *wlr_egl_create_with_drm_fd(int drm_fd) { } const char *device_exts_str = NULL, *driver_name = NULL; - if (check_egl_ext(client_exts_str, "EGL_EXT_device_query")) { - load_egl_proc(&egl->procs.eglQueryDisplayAttribEXT, - "eglQueryDisplayAttribEXT"); - load_egl_proc(&egl->procs.eglQueryDeviceStringEXT, - "eglQueryDeviceStringEXT"); - + if (egl->exts.EXT_device_query) { EGLAttrib device_attrib; if (!egl->procs.eglQueryDisplayAttribEXT(egl->display, EGL_DEVICE_EXT, &device_attrib)) { wlr_log(WLR_ERROR, "eglQueryDisplayAttribEXT(EGL_DEVICE_EXT) failed"); - goto error; + return false; } egl->device = (EGLDeviceEXT)device_attrib; @@ -265,7 +273,7 @@ struct wlr_egl *wlr_egl_create_with_drm_fd(int drm_fd) { egl->procs.eglQueryDeviceStringEXT(egl->device, EGL_EXTENSIONS); if (device_exts_str == NULL) { wlr_log(WLR_ERROR, "eglQueryDeviceStringEXT(EGL_EXTENSIONS) failed"); - goto error; + return false; } if (check_egl_ext(device_exts_str, "EGL_MESA_device_software")) { @@ -276,7 +284,7 @@ struct wlr_egl *wlr_egl_create_with_drm_fd(int drm_fd) { wlr_log(WLR_ERROR, "Software rendering detected, please use " "the WLR_RENDERER_ALLOW_SOFTWARE environment variable " "to proceed"); - goto error; + return false; } } @@ -297,21 +305,19 @@ struct wlr_egl *wlr_egl_create_with_drm_fd(int drm_fd) { !check_egl_ext(display_exts_str, "EGL_MESA_configless_context")) { wlr_log(WLR_ERROR, "EGL_KHR_no_config_context or " "EGL_MESA_configless_context not supported"); - goto error; + return false; } if (!check_egl_ext(display_exts_str, "EGL_KHR_surfaceless_context")) { - wlr_log(WLR_ERROR, - "EGL_KHR_surfaceless_context not supported"); - goto error; + wlr_log(WLR_ERROR, "EGL_KHR_surfaceless_context not supported"); + return false; } - wlr_log(WLR_INFO, "Using EGL %d.%d", (int)major, (int)minor); - wlr_log(WLR_INFO, "Supported EGL client extensions: %s", client_exts_str); wlr_log(WLR_INFO, "Supported EGL display extensions: %s", display_exts_str); if (device_exts_str != NULL) { wlr_log(WLR_INFO, "Supported EGL device extensions: %s", device_exts_str); } + wlr_log(WLR_INFO, "Using EGL %d.%d", (int)major, (int)minor); wlr_log(WLR_INFO, "EGL vendor: %s", eglQueryString(egl->display, EGL_VENDOR)); if (driver_name != NULL) { wlr_log(WLR_INFO, "EGL driver name: %s", driver_name); @@ -345,7 +351,7 @@ struct wlr_egl *wlr_egl_create_with_drm_fd(int drm_fd) { EGL_NO_CONTEXT, attribs); if (egl->context == EGL_NO_CONTEXT) { wlr_log(WLR_ERROR, "Failed to create EGL context"); - goto error; + return false; } if (request_high_priority) { @@ -359,13 +365,112 @@ struct wlr_egl *wlr_egl_create_with_drm_fd(int drm_fd) { } } - return egl; + return true; +} + +static bool device_has_name(const drmDevice *device, const char *name); + +static EGLDeviceEXT get_egl_device_from_drm_fd(struct wlr_egl *egl, + int drm_fd) { + if (egl->procs.eglQueryDevicesEXT == NULL) { + wlr_log(WLR_DEBUG, "EGL_EXT_device_enumeration not supported"); + return EGL_NO_DEVICE_EXT; + } + + EGLint nb_devices = 0; + if (!egl->procs.eglQueryDevicesEXT(0, NULL, &nb_devices)) { + wlr_log(WLR_ERROR, "Failed to query EGL devices"); + return EGL_NO_DEVICE_EXT; + } + + EGLDeviceEXT *devices = calloc(nb_devices, sizeof(EGLDeviceEXT)); + if (devices == NULL) { + wlr_log_errno(WLR_ERROR, "Failed to allocate EGL device list"); + return EGL_NO_DEVICE_EXT; + } + + if (!egl->procs.eglQueryDevicesEXT(nb_devices, devices, &nb_devices)) { + wlr_log(WLR_ERROR, "Failed to query EGL devices"); + return EGL_NO_DEVICE_EXT; + } + + drmDevice *device = NULL; + int ret = drmGetDevice(drm_fd, &device); + if (ret < 0) { + wlr_log(WLR_ERROR, "Failed to get DRM device: %s", strerror(-ret)); + return EGL_NO_DEVICE_EXT; + } + + EGLDeviceEXT egl_device = NULL; + for (int i = 0; i < nb_devices; i++) { + const char *egl_device_name = egl->procs.eglQueryDeviceStringEXT( + devices[i], EGL_DRM_DEVICE_FILE_EXT); + if (egl_device_name == NULL) { + continue; + } + + if (device_has_name(device, egl_device_name)) { + wlr_log(WLR_DEBUG, "Using EGL device %s", egl_device_name); + egl_device = devices[i]; + break; + } + } + + free(devices); + + return egl_device; +} + +struct wlr_egl *wlr_egl_create_with_drm_fd(int drm_fd) { + struct wlr_egl *egl = egl_create(); + if (egl == NULL) { + wlr_log(WLR_ERROR, "Failed to create EGL context"); + return NULL; + } + + 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"); + } + + if (egl->exts.KHR_platform_gbm) { + egl->gbm_device = gbm_create_device(drm_fd); + if (!egl->gbm_device) { + wlr_log(WLR_ERROR, "Failed to create GBM device"); + goto error; + } + + if (egl_init(egl, EGL_PLATFORM_GBM_KHR, egl->gbm_device)) { + wlr_log(WLR_DEBUG, "Using EGL_PLATFORM_GBM_KHR"); + return egl; + } + + gbm_device_destroy(egl->gbm_device); + } else { + wlr_log(WLR_DEBUG, "KHR_platform_gbm not supported"); + } error: - eglMakeCurrent(EGL_NO_DISPLAY, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + wlr_log(WLR_ERROR, "Failed to initialize EGL context"); if (egl->display) { + eglMakeCurrent(egl->display, EGL_NO_SURFACE, EGL_NO_SURFACE, + EGL_NO_CONTEXT); eglTerminate(egl->display); } + free(egl); eglReleaseThread(); return NULL; }