#include #include #include #include #include #include #include #include "glapi.h" // Extension documentation // https://www.khronos.org/registry/EGL/extensions/KHR/EGL_KHR_image_base.txt. // https://cgit.freedesktop.org/mesa/mesa/tree/docs/specs/WL_bind_wayland_display.spec const char *egl_error(void) { switch (eglGetError()) { case EGL_SUCCESS: return "Success"; case EGL_NOT_INITIALIZED: return "Not initialized"; case EGL_BAD_ACCESS: return "Bad access"; case EGL_BAD_ALLOC: return "Bad alloc"; case EGL_BAD_ATTRIBUTE: return "Bad attribute"; case EGL_BAD_CONTEXT: return "Bad Context"; case EGL_BAD_CONFIG: return "Bad Config"; case EGL_BAD_CURRENT_SURFACE: return "Bad current surface"; case EGL_BAD_DISPLAY: return "Bad display"; case EGL_BAD_SURFACE: return "Bad surface"; case EGL_BAD_MATCH: return "Bad match"; case EGL_BAD_PARAMETER: return "Bad parameter"; case EGL_BAD_NATIVE_PIXMAP: return "Bad native pixmap"; case EGL_BAD_NATIVE_WINDOW: return "Bad native window"; case EGL_CONTEXT_LOST: return "Context lost"; default: return "Unknown"; } } static bool egl_get_config(EGLDisplay disp, EGLint *attribs, EGLConfig *out, EGLint visual_id) { EGLint count = 0, matched = 0, ret; ret = eglGetConfigs(disp, NULL, 0, &count); if (ret == EGL_FALSE || count == 0) { wlr_log(L_ERROR, "eglGetConfigs returned no configs"); return false; } EGLConfig configs[count]; ret = eglChooseConfig(disp, attribs, configs, count, &matched); if (ret == EGL_FALSE) { wlr_log(L_ERROR, "eglChooseConfig failed"); return false; } for (int i = 0; i < matched; ++i) { EGLint visual; if (!eglGetConfigAttrib(disp, configs[i], EGL_NATIVE_VISUAL_ID, &visual)) { continue; } if (!visual_id || visual == visual_id) { *out = configs[i]; return true; } } wlr_log(L_ERROR, "no valid egl config found"); return false; } bool wlr_egl_init(struct wlr_egl *egl, EGLenum platform, void *remote_display, EGLint *config_attribs, EGLint visual_id) { if (!load_glapi()) { return false; } if (eglBindAPI(EGL_OPENGL_ES_API) == EGL_FALSE) { wlr_log(L_ERROR, "Failed to bind to the OpenGL ES API: %s", egl_error()); goto error; } if (platform == EGL_PLATFORM_SURFACELESS_MESA) { assert(remote_display == NULL); egl->display = eglGetDisplay(EGL_DEFAULT_DISPLAY); } else { egl->display = eglGetPlatformDisplayEXT(platform, remote_display, NULL); } if (egl->display == EGL_NO_DISPLAY) { wlr_log(L_ERROR, "Failed to create EGL display: %s", egl_error()); goto error; } EGLint major, minor; if (eglInitialize(egl->display, &major, &minor) == EGL_FALSE) { wlr_log(L_ERROR, "Failed to initialize EGL: %s", egl_error()); goto error; } if (!egl_get_config(egl->display, config_attribs, &egl->config, visual_id)) { wlr_log(L_ERROR, "Failed to get EGL config"); goto error; } static const EGLint attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; egl->context = eglCreateContext(egl->display, egl->config, EGL_NO_CONTEXT, attribs); if (egl->context == EGL_NO_CONTEXT) { wlr_log(L_ERROR, "Failed to create EGL context: %s", egl_error()); goto error; } eglMakeCurrent(egl->display, EGL_NO_SURFACE, EGL_NO_SURFACE, egl->context); egl->egl_exts_str = eglQueryString(egl->display, EGL_EXTENSIONS); egl->gl_exts_str = (const char*) glGetString(GL_EXTENSIONS); wlr_log(L_INFO, "Using EGL %d.%d", (int)major, (int)minor); wlr_log(L_INFO, "Supported EGL extensions: %s", egl->egl_exts_str); wlr_log(L_INFO, "EGL vendor: %s", eglQueryString(egl->display, EGL_VENDOR)); wlr_log(L_INFO, "Using %s", glGetString(GL_VERSION)); wlr_log(L_INFO, "GL vendor: %s", glGetString(GL_VENDOR)); wlr_log(L_INFO, "Supported OpenGL ES extensions: %s", egl->gl_exts_str); if (strstr(egl->egl_exts_str, "EGL_WL_bind_wayland_display") == NULL || strstr(egl->egl_exts_str, "EGL_KHR_image_base") == NULL) { wlr_log(L_ERROR, "Required egl extensions not supported"); goto error; } egl->egl_exts.buffer_age = strstr(egl->egl_exts_str, "EGL_EXT_buffer_age") != NULL; egl->egl_exts.swap_buffers_with_damage = strstr(egl->egl_exts_str, "EGL_EXT_swap_buffers_with_damage") != NULL || strstr(egl->egl_exts_str, "EGL_KHR_swap_buffers_with_damage") != NULL; return true; error: eglMakeCurrent(EGL_NO_DISPLAY, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglTerminate(egl->display); eglReleaseThread(); return false; } void wlr_egl_finish(struct wlr_egl *egl) { eglMakeCurrent(EGL_NO_DISPLAY, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); if (egl->wl_display && eglUnbindWaylandDisplayWL) { eglUnbindWaylandDisplayWL(egl->display, egl->wl_display); } eglDestroyContext(egl->display, egl->context); eglTerminate(egl->display); eglReleaseThread(); } bool wlr_egl_bind_display(struct wlr_egl *egl, struct wl_display *local_display) { if (!eglBindWaylandDisplayWL) { return false; } if (eglBindWaylandDisplayWL(egl->display, local_display)) { egl->wl_display = local_display; return true; } return false; } bool wlr_egl_query_buffer(struct wlr_egl *egl, struct wl_resource *buf, int attrib, int *value) { if (!eglQueryWaylandBufferWL) { return false; } return eglQueryWaylandBufferWL(egl->display, buf, attrib, value); } EGLImage wlr_egl_create_image(struct wlr_egl *egl, EGLenum target, EGLClientBuffer buffer, const EGLint *attribs) { if (!eglCreateImageKHR) { return false; } return eglCreateImageKHR(egl->display, egl->context, target, buffer, attribs); } bool wlr_egl_destroy_image(struct wlr_egl *egl, EGLImage image) { if (!eglDestroyImageKHR) { return false; } eglDestroyImageKHR(egl->display, image); return true; } EGLSurface wlr_egl_create_surface(struct wlr_egl *egl, void *window) { EGLSurface surf = eglCreatePlatformWindowSurfaceEXT(egl->display, egl->config, window, NULL); if (surf == EGL_NO_SURFACE) { wlr_log(L_ERROR, "Failed to create EGL surface: %s", egl_error()); return EGL_NO_SURFACE; } return surf; } int wlr_egl_get_buffer_age(struct wlr_egl *egl, EGLSurface surface) { if (!egl->egl_exts.buffer_age) { return -1; } EGLint buffer_age; EGLBoolean ok = eglQuerySurface(egl->display, surface, EGL_BUFFER_AGE_EXT, &buffer_age); if (!ok) { wlr_log(L_ERROR, "Failed to get EGL surface buffer age: %s", egl_error()); return -1; } return buffer_age; } bool wlr_egl_make_current(struct wlr_egl *egl, EGLSurface surface, int *buffer_age) { if (!eglMakeCurrent(egl->display, surface, surface, egl->context)) { wlr_log(L_ERROR, "eglMakeCurrent failed: %s", egl_error()); return false; } if (buffer_age != NULL) { *buffer_age = wlr_egl_get_buffer_age(egl, surface); } return true; } bool wlr_egl_swap_buffers(struct wlr_egl *egl, EGLSurface surface, pixman_region32_t *damage) { EGLBoolean ret; if (damage != NULL && egl->egl_exts.swap_buffers_with_damage) { int nrects; pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); EGLint egl_damage[4 * nrects]; for (int i = 0; i < nrects; ++i) { egl_damage[4*i] = rects[i].x1; egl_damage[4*i + 1] = rects[i].y1; egl_damage[4*i + 2] = rects[i].x2 - rects[i].x1; egl_damage[4*i + 3] = rects[i].y2 - rects[i].y1; } assert(eglSwapBuffersWithDamageEXT || eglSwapBuffersWithDamageKHR); if (eglSwapBuffersWithDamageEXT) { ret = eglSwapBuffersWithDamageEXT(egl->display, surface, egl_damage, nrects); } else { ret = eglSwapBuffersWithDamageKHR(egl->display, surface, egl_damage, nrects); } } else { ret = eglSwapBuffers(egl->display, surface); } if (!ret) { wlr_log(L_ERROR, "eglSwapBuffers failed: %s", egl_error()); return false; } return true; }