aboutsummaryrefslogtreecommitdiff
path: root/render/egl.c
diff options
context:
space:
mode:
Diffstat (limited to 'render/egl.c')
-rw-r--r--render/egl.c175
1 files changed, 140 insertions, 35 deletions
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;
}