render/egl: initialize wlr_egl with EGL_PLATFORM_DEVICE_EXT

Uses the EXT_device_query extension to get the EGL device matching the
requested DRM file descriptor. If the extension is not supported or no device
is found, the EGL device will be retrieved using GBM.

Depends on the EGL_EXT_device_enumeration to get the list of EGL devices.
This commit is contained in:
Simon Zeni 2021-01-20 21:14:04 -05:00 committed by Simon Ser
parent 7667ab73bd
commit 6f19295647
2 changed files with 146 additions and 35 deletions

View File

@ -44,6 +44,11 @@ struct wlr_egl {
// Device extensions // Device extensions
bool EXT_device_drm; bool EXT_device_drm;
bool EXT_device_drm_render_node; bool EXT_device_drm_render_node;
// Client extensions
bool EXT_device_query;
bool KHR_platform_gbm;
bool EXT_platform_device;
} exts; } exts;
struct { struct {
@ -56,6 +61,7 @@ struct wlr_egl {
PFNEGLDEBUGMESSAGECONTROLKHRPROC eglDebugMessageControlKHR; PFNEGLDEBUGMESSAGECONTROLKHRPROC eglDebugMessageControlKHR;
PFNEGLQUERYDISPLAYATTRIBEXTPROC eglQueryDisplayAttribEXT; PFNEGLQUERYDISPLAYATTRIBEXTPROC eglQueryDisplayAttribEXT;
PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT; PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT;
PFNEGLQUERYDEVICESEXTPROC eglQueryDevicesEXT;
} procs; } procs;
struct wlr_drm_format_set dmabuf_texture_formats; struct wlr_drm_format_set dmabuf_texture_formats;

View File

@ -156,7 +156,7 @@ out:
free(formats); 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)); struct wlr_egl *egl = calloc(1, sizeof(struct wlr_egl));
if (egl == NULL) { if (egl == NULL) {
wlr_log_errno(WLR_ERROR, "Allocation failed"); 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; return NULL;
} }
wlr_log(WLR_INFO, "Supported EGL client extensions: %s", client_exts_str);
if (!check_egl_ext(client_exts_str, "EGL_KHR_platform_gbm")) {
wlr_log(WLR_ERROR, "EGL_KHR_platform_gbm not supported");
return NULL;
}
if (!check_egl_ext(client_exts_str, "EGL_EXT_platform_base")) { if (!check_egl_ext(client_exts_str, "EGL_EXT_platform_base")) {
wlr_log(WLR_ERROR, "EGL_EXT_platform_base not supported"); 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, 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_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")) { if (check_egl_ext(client_exts_str, "EGL_KHR_debug")) {
load_egl_proc(&egl->procs.eglDebugMessageControlKHR, load_egl_proc(&egl->procs.eglDebugMessageControlKHR,
"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) { if (eglBindAPI(EGL_OPENGL_ES_API) == EGL_FALSE) {
wlr_log(WLR_ERROR, "Failed to bind to the OpenGL ES API"); wlr_log(WLR_ERROR, "Failed to bind to the OpenGL ES API");
goto error; return NULL;
} }
egl->gbm_device = gbm_create_device(drm_fd); return egl;
if (!egl->gbm_device) { }
wlr_log(WLR_ERROR, "Failed to create GBM device");
goto error;
}
egl->display = egl->procs.eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_KHR, static bool egl_init(struct wlr_egl *egl, EGLenum platform,
egl->gbm_device, NULL); void *remote_display) {
egl->display = egl->procs.eglGetPlatformDisplayEXT(platform,
remote_display, NULL);
if (egl->display == EGL_NO_DISPLAY) { if (egl->display == EGL_NO_DISPLAY) {
wlr_log(WLR_ERROR, "Failed to create EGL display"); wlr_log(WLR_ERROR, "Failed to create EGL display");
goto error; return false;
} }
EGLint major, minor; EGLint major, minor;
if (eglInitialize(egl->display, &major, &minor) == EGL_FALSE) { if (eglInitialize(egl->display, &major, &minor) == EGL_FALSE) {
wlr_log(WLR_ERROR, "Failed to initialize EGL"); wlr_log(WLR_ERROR, "Failed to initialize EGL");
goto error; return false;
} }
const char *display_exts_str = eglQueryString(egl->display, EGL_EXTENSIONS); const char *display_exts_str = eglQueryString(egl->display, EGL_EXTENSIONS);
if (display_exts_str == NULL) { if (display_exts_str == NULL) {
wlr_log(WLR_ERROR, "Failed to query EGL display extensions"); 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")) { 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; const char *device_exts_str = NULL, *driver_name = NULL;
if (check_egl_ext(client_exts_str, "EGL_EXT_device_query")) { if (egl->exts.EXT_device_query) {
load_egl_proc(&egl->procs.eglQueryDisplayAttribEXT,
"eglQueryDisplayAttribEXT");
load_egl_proc(&egl->procs.eglQueryDeviceStringEXT,
"eglQueryDeviceStringEXT");
EGLAttrib device_attrib; EGLAttrib device_attrib;
if (!egl->procs.eglQueryDisplayAttribEXT(egl->display, if (!egl->procs.eglQueryDisplayAttribEXT(egl->display,
EGL_DEVICE_EXT, &device_attrib)) { EGL_DEVICE_EXT, &device_attrib)) {
wlr_log(WLR_ERROR, "eglQueryDisplayAttribEXT(EGL_DEVICE_EXT) failed"); wlr_log(WLR_ERROR, "eglQueryDisplayAttribEXT(EGL_DEVICE_EXT) failed");
goto error; return false;
} }
egl->device = (EGLDeviceEXT)device_attrib; 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); egl->procs.eglQueryDeviceStringEXT(egl->device, EGL_EXTENSIONS);
if (device_exts_str == NULL) { if (device_exts_str == NULL) {
wlr_log(WLR_ERROR, "eglQueryDeviceStringEXT(EGL_EXTENSIONS) failed"); wlr_log(WLR_ERROR, "eglQueryDeviceStringEXT(EGL_EXTENSIONS) failed");
goto error; return false;
} }
if (check_egl_ext(device_exts_str, "EGL_MESA_device_software")) { 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 " wlr_log(WLR_ERROR, "Software rendering detected, please use "
"the WLR_RENDERER_ALLOW_SOFTWARE environment variable " "the WLR_RENDERER_ALLOW_SOFTWARE environment variable "
"to proceed"); "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")) { !check_egl_ext(display_exts_str, "EGL_MESA_configless_context")) {
wlr_log(WLR_ERROR, "EGL_KHR_no_config_context or " wlr_log(WLR_ERROR, "EGL_KHR_no_config_context or "
"EGL_MESA_configless_context not supported"); "EGL_MESA_configless_context not supported");
goto error; return false;
} }
if (!check_egl_ext(display_exts_str, "EGL_KHR_surfaceless_context")) { if (!check_egl_ext(display_exts_str, "EGL_KHR_surfaceless_context")) {
wlr_log(WLR_ERROR, wlr_log(WLR_ERROR, "EGL_KHR_surfaceless_context not supported");
"EGL_KHR_surfaceless_context not supported"); return false;
goto error;
} }
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); wlr_log(WLR_INFO, "Supported EGL display extensions: %s", display_exts_str);
if (device_exts_str != NULL) { if (device_exts_str != NULL) {
wlr_log(WLR_INFO, "Supported EGL device extensions: %s", device_exts_str); 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)); wlr_log(WLR_INFO, "EGL vendor: %s", eglQueryString(egl->display, EGL_VENDOR));
if (driver_name != NULL) { if (driver_name != NULL) {
wlr_log(WLR_INFO, "EGL driver name: %s", driver_name); 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); EGL_NO_CONTEXT, attribs);
if (egl->context == EGL_NO_CONTEXT) { if (egl->context == EGL_NO_CONTEXT) {
wlr_log(WLR_ERROR, "Failed to create EGL context"); wlr_log(WLR_ERROR, "Failed to create EGL context");
goto error; return false;
} }
if (request_high_priority) { 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: 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) { if (egl->display) {
eglMakeCurrent(egl->display, EGL_NO_SURFACE, EGL_NO_SURFACE,
EGL_NO_CONTEXT);
eglTerminate(egl->display); eglTerminate(egl->display);
} }
free(egl);
eglReleaseThread(); eglReleaseThread();
return NULL; return NULL;
} }