123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635 |
- /******************************************************************************
- Copyright (C) 2019 by Ivan Avdeev <[email protected]>
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 2 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- ******************************************************************************/
- /* GL context initialization using EGL instead of GLX
- * Which is essential for improved and more performant screen grabbing and
- * VA-API feeding techniques.
- *
- * Note: most of x11-related functionality was taken from gl-x11.c
- */
- #include <X11/Xlib.h>
- #include <X11/Xlib-xcb.h>
- #include <xcb/xcb.h>
- #include <stdio.h>
- #include "gl-egl-common.h"
- #include "gl-x11-egl.h"
- #include <glad/glad_egl.h>
- typedef EGLDisplay(EGLAPIENTRYP PFNEGLGETPLATFORMDISPLAYEXTPROC)(
- EGLenum platform, void *native_display, const EGLint *attrib_list);
- static const int ctx_attribs[] = {
- #ifdef _DEBUG
- EGL_CONTEXT_OPENGL_DEBUG,
- EGL_TRUE,
- #endif
- EGL_CONTEXT_OPENGL_PROFILE_MASK,
- EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
- EGL_CONTEXT_MAJOR_VERSION,
- 3,
- EGL_CONTEXT_MINOR_VERSION,
- 3,
- EGL_NONE,
- };
- static int ctx_pbuffer_attribs[] = {EGL_WIDTH, 2, EGL_HEIGHT, 2, EGL_NONE};
- static const EGLint ctx_config_attribs[] = {EGL_STENCIL_SIZE,
- 0,
- EGL_DEPTH_SIZE,
- 0,
- EGL_BUFFER_SIZE,
- 24,
- EGL_ALPHA_SIZE,
- 0,
- EGL_RENDERABLE_TYPE,
- EGL_OPENGL_BIT,
- EGL_SURFACE_TYPE,
- EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
- EGL_NONE};
- struct gl_windowinfo {
- /* Windows in X11 are defined with integers (XID).
- * xcb_window_t is a define for this... they are
- * compatible with Xlib as well.
- */
- xcb_window_t window;
- EGLSurface surface;
- };
- struct gl_platform {
- Display *xdisplay;
- EGLDisplay edisplay;
- EGLConfig config;
- EGLContext context;
- EGLSurface pbuffer;
- };
- /* The following utility function is copied verbatim from GLX code. */
- static xcb_get_geometry_reply_t *get_window_geometry(xcb_connection_t *xcb_conn,
- xcb_drawable_t drawable)
- {
- xcb_get_geometry_cookie_t cookie;
- xcb_generic_error_t *error;
- xcb_get_geometry_reply_t *reply;
- cookie = xcb_get_geometry(xcb_conn, drawable);
- reply = xcb_get_geometry_reply(xcb_conn, cookie, &error);
- if (error) {
- blog(LOG_ERROR, "Failed to fetch parent window geometry!");
- free(error);
- free(reply);
- return 0;
- }
- return reply;
- }
- static const char *get_egl_error_string2(const EGLint error)
- {
- switch (error) {
- #define OBS_EGL_CASE_ERROR(e) \
- case e: \
- return #e;
- OBS_EGL_CASE_ERROR(EGL_SUCCESS)
- OBS_EGL_CASE_ERROR(EGL_NOT_INITIALIZED)
- OBS_EGL_CASE_ERROR(EGL_BAD_ACCESS)
- OBS_EGL_CASE_ERROR(EGL_BAD_ALLOC)
- OBS_EGL_CASE_ERROR(EGL_BAD_ATTRIBUTE)
- OBS_EGL_CASE_ERROR(EGL_BAD_CONTEXT)
- OBS_EGL_CASE_ERROR(EGL_BAD_CONFIG)
- OBS_EGL_CASE_ERROR(EGL_BAD_CURRENT_SURFACE)
- OBS_EGL_CASE_ERROR(EGL_BAD_DISPLAY)
- OBS_EGL_CASE_ERROR(EGL_BAD_SURFACE)
- OBS_EGL_CASE_ERROR(EGL_BAD_MATCH)
- OBS_EGL_CASE_ERROR(EGL_BAD_PARAMETER)
- OBS_EGL_CASE_ERROR(EGL_BAD_NATIVE_PIXMAP)
- OBS_EGL_CASE_ERROR(EGL_BAD_NATIVE_WINDOW)
- OBS_EGL_CASE_ERROR(EGL_CONTEXT_LOST)
- #undef OBS_EGL_CASE_ERROR
- default:
- return "Unknown";
- }
- }
- static const char *get_egl_error_string()
- {
- return get_egl_error_string2(eglGetError());
- }
- static EGLDisplay get_egl_display(struct gl_platform *plat)
- {
- Display *display = plat->xdisplay;
- EGLDisplay edisplay = EGL_NO_DISPLAY;
- const char *egl_client_extensions = NULL;
- egl_client_extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
- PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT =
- (PFNEGLGETPLATFORMDISPLAYEXTPROC)(strstr(egl_client_extensions,
- "EGL_EXT_platform_base")
- ? eglGetProcAddress(
- "eglGetPlatformDisplayEXT")
- : NULL);
- if (eglGetPlatformDisplayEXT) {
- edisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_X11_EXT,
- display, NULL);
- if (EGL_NO_DISPLAY == edisplay)
- blog(LOG_ERROR, "Failed to get EGL/X11 display");
- }
- if (EGL_NO_DISPLAY == edisplay)
- edisplay = eglGetDisplay(display);
- return edisplay;
- }
- static bool gl_context_create(struct gl_platform *plat)
- {
- Display *display = plat->xdisplay;
- int frame_buf_config_count = 0;
- EGLDisplay edisplay = EGL_NO_DISPLAY;
- EGLConfig config = NULL;
- EGLContext context = EGL_NO_CONTEXT;
- int egl_min = 0, egl_maj = 0;
- bool success = false;
- eglBindAPI(EGL_OPENGL_API);
- edisplay = get_egl_display(plat);
- if (EGL_NO_DISPLAY == edisplay) {
- blog(LOG_ERROR,
- "Failed to get EGL display using eglGetDisplay");
- return false;
- }
- if (!eglInitialize(edisplay, &egl_maj, &egl_min)) {
- blog(LOG_ERROR, "Failed to initialize EGL: %s",
- get_egl_error_string());
- return false;
- }
- if (!eglChooseConfig(edisplay, ctx_config_attribs, &config, 1,
- &frame_buf_config_count)) {
- blog(LOG_ERROR, "Unable to find suitable EGL config: %s",
- get_egl_error_string());
- goto error;
- }
- context =
- eglCreateContext(edisplay, config, EGL_NO_CONTEXT, ctx_attribs);
- #ifdef _DEBUG
- if (EGL_NO_CONTEXT == context) {
- const EGLint error = eglGetError();
- if (error == EGL_BAD_ATTRIBUTE) {
- /* Sometimes creation fails because debug gl is not supported */
- blog(LOG_ERROR,
- "Unable to create EGL context with DEBUG attrib, trying without");
- context = eglCreateContext(edisplay, config,
- EGL_NO_CONTEXT,
- ctx_attribs + 2);
- } else {
- blog(LOG_ERROR, "Unable to create EGL context: %s",
- get_egl_error_string2(error));
- goto error;
- }
- }
- #endif
- if (EGL_NO_CONTEXT == context) {
- blog(LOG_ERROR, "Unable to create EGL context: %s",
- get_egl_error_string());
- goto error;
- }
- plat->pbuffer =
- eglCreatePbufferSurface(edisplay, config, ctx_pbuffer_attribs);
- if (EGL_NO_SURFACE == plat->pbuffer) {
- blog(LOG_ERROR, "Failed to create OpenGL pbuffer: %s",
- get_egl_error_string());
- goto error;
- }
- plat->edisplay = edisplay;
- plat->config = config;
- plat->context = context;
- success = true;
- blog(LOG_DEBUG, "Created EGLDisplay %p", plat->edisplay);
- error:
- if (!success) {
- if (EGL_NO_CONTEXT != context)
- eglDestroyContext(edisplay, context);
- eglTerminate(edisplay);
- }
- XSync(display, false);
- return success;
- }
- static void gl_context_destroy(struct gl_platform *plat)
- {
- eglMakeCurrent(plat->edisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
- EGL_NO_CONTEXT);
- eglDestroyContext(plat->edisplay, plat->context);
- }
- static struct gl_windowinfo *
- gl_x11_egl_windowinfo_create(const struct gs_init_data *info)
- {
- UNUSED_PARAMETER(info);
- return bmalloc(sizeof(struct gl_windowinfo));
- }
- static void gl_x11_egl_windowinfo_destroy(struct gl_windowinfo *info)
- {
- bfree(info);
- }
- static Display *open_windowless_display(Display *platform_display)
- {
- Display *display;
- xcb_connection_t *xcb_conn;
- if (platform_display)
- display = platform_display;
- else
- display = XOpenDisplay(NULL);
- if (!display) {
- blog(LOG_ERROR, "Unable to open new X connection!");
- return NULL;
- }
- xcb_conn = XGetXCBConnection(display);
- if (!xcb_conn) {
- blog(LOG_ERROR, "Unable to get XCB connection to main display");
- goto error;
- }
- if (!gladLoadEGL()) {
- blog(LOG_ERROR, "Unable to load EGL entry functions.");
- goto error;
- }
- return display;
- error:
- XCloseDisplay(display);
- return NULL;
- }
- static int x_error_handler(Display *display, XErrorEvent *error)
- {
- char str1[512];
- char str2[512];
- char str3[512];
- XGetErrorText(display, error->error_code, str1, sizeof(str1));
- XGetErrorText(display, error->request_code, str2, sizeof(str2));
- XGetErrorText(display, error->minor_code, str3, sizeof(str3));
- blog(LOG_ERROR,
- "X Error: %s, Major opcode: %s, "
- "Minor opcode: %s, Serial: %lu",
- str1, str2, str3, error->serial);
- return 0;
- }
- static struct gl_platform *gl_x11_egl_platform_create(gs_device_t *device,
- uint32_t adapter)
- {
- /* There's some trickery here... we're mixing libX11, xcb, and EGL
- For an explanation see here: http://xcb.freedesktop.org/MixingCalls/
- Essentially, EGL requires Xlib. Everything else we use xcb. */
- struct gl_platform *plat = bmalloc(sizeof(struct gl_platform));
- Display *platform_display = obs_get_nix_platform_display();
- Display *display = open_windowless_display(platform_display);
- if (!display) {
- goto fail_display_open;
- }
- XSetEventQueueOwner(display, XCBOwnsEventQueue);
- XSetErrorHandler(x_error_handler);
- /* We assume later that cur_swap is already set. */
- device->plat = plat;
- plat->xdisplay = display;
- if (!gl_context_create(plat)) {
- blog(LOG_ERROR, "Failed to create context!");
- goto fail_context_create;
- }
- if (!eglMakeCurrent(plat->edisplay, plat->pbuffer, plat->pbuffer,
- plat->context)) {
- blog(LOG_ERROR, "Failed to make context current: %s",
- get_egl_error_string());
- goto fail_make_current;
- }
- if (!gladLoadGL()) {
- blog(LOG_ERROR, "Failed to load OpenGL entry functions.");
- goto fail_load_gl;
- }
- goto success;
- fail_make_current:
- gl_context_destroy(plat);
- fail_context_create:
- fail_load_gl:
- XCloseDisplay(display);
- fail_display_open:
- bfree(plat);
- plat = NULL;
- success:
- UNUSED_PARAMETER(adapter);
- return plat;
- }
- static void gl_x11_egl_platform_destroy(struct gl_platform *plat)
- {
- if (!plat)
- return;
- gl_context_destroy(plat);
- eglTerminate(plat->edisplay);
- bfree(plat);
- }
- static bool gl_x11_egl_platform_init_swapchain(struct gs_swap_chain *swap)
- {
- const struct gl_platform *plat = swap->device->plat;
- Display *display = plat->xdisplay;
- xcb_connection_t *xcb_conn = XGetXCBConnection(display);
- xcb_window_t wid = xcb_generate_id(xcb_conn);
- xcb_window_t parent = swap->info.window.id;
- xcb_get_geometry_reply_t *geometry =
- get_window_geometry(xcb_conn, parent);
- bool status = false;
- int visual;
- if (!geometry)
- goto fail_geometry_request;
- {
- if (!eglGetConfigAttrib(plat->edisplay, plat->config,
- EGL_NATIVE_VISUAL_ID,
- (EGLint *)&visual)) {
- blog(LOG_ERROR,
- "Cannot get visual id for EGL context: %s",
- get_egl_error_string());
- goto fail_visual_id;
- }
- }
- xcb_colormap_t colormap = xcb_generate_id(xcb_conn);
- uint32_t mask = XCB_CW_BORDER_PIXEL | XCB_CW_COLORMAP;
- uint32_t mask_values[] = {0, colormap, 0};
- xcb_create_colormap(xcb_conn, XCB_COLORMAP_ALLOC_NONE, colormap, parent,
- visual);
- xcb_void_cookie_t window_cookie = xcb_create_window_checked(
- xcb_conn, 24 /* Hardcoded? */, wid, parent, 0, 0,
- geometry->width, geometry->height, 0, 0, visual, mask,
- mask_values);
- xcb_generic_error_t *err = xcb_request_check(xcb_conn, window_cookie);
- if (err != NULL) {
- char text[512];
- XGetErrorText(display, err->error_code, text, sizeof(text));
- blog(LOG_ERROR,
- "Swapchain window creation failed: %s"
- ", Major opcode: %d, Minor opcode: %d",
- text, err->major_code, err->minor_code);
- goto fail_window_surface;
- }
- const EGLSurface surface =
- eglCreateWindowSurface(plat->edisplay, plat->config, wid, 0);
- if (EGL_NO_SURFACE == surface) {
- blog(LOG_ERROR, "Cannot get window EGL surface: %s",
- get_egl_error_string());
- goto fail_window_surface;
- }
- swap->wi->window = wid;
- swap->wi->surface = surface;
- xcb_map_window(xcb_conn, wid);
- status = true;
- goto success;
- fail_window_surface:
- fail_visual_id:
- fail_geometry_request:
- success:
- free(geometry);
- return status;
- }
- static void gl_x11_egl_platform_cleanup_swapchain(struct gs_swap_chain *swap)
- {
- UNUSED_PARAMETER(swap);
- /* Really nothing to clean up? */
- }
- static void gl_x11_egl_device_enter_context(gs_device_t *device)
- {
- const EGLContext context = device->plat->context;
- const EGLDisplay display = device->plat->edisplay;
- const EGLSurface surface = (device->cur_swap)
- ? device->cur_swap->wi->surface
- : device->plat->pbuffer;
- if (!eglMakeCurrent(display, surface, surface, context))
- blog(LOG_ERROR, "Failed to make context current: %s",
- get_egl_error_string());
- }
- static void gl_x11_egl_device_leave_context(gs_device_t *device)
- {
- const EGLDisplay display = device->plat->edisplay;
- if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE,
- EGL_NO_CONTEXT)) {
- blog(LOG_ERROR, "Failed to reset current context: %s",
- get_egl_error_string());
- }
- }
- static void *gl_x11_egl_device_get_device_obj(gs_device_t *device)
- {
- return device->plat->context;
- }
- static void gl_x11_egl_getclientsize(const struct gs_swap_chain *swap,
- uint32_t *width, uint32_t *height)
- {
- xcb_connection_t *xcb_conn =
- XGetXCBConnection(swap->device->plat->xdisplay);
- xcb_window_t window = swap->wi->window;
- xcb_get_geometry_reply_t *geometry =
- get_window_geometry(xcb_conn, window);
- if (geometry) {
- *width = geometry->width;
- *height = geometry->height;
- }
- free(geometry);
- }
- static void gl_x11_egl_update(gs_device_t *device)
- {
- Display *display = device->plat->xdisplay;
- xcb_window_t window = device->cur_swap->wi->window;
- uint32_t values[] = {device->cur_swap->info.cx,
- device->cur_swap->info.cy};
- xcb_configure_window(XGetXCBConnection(display), window,
- XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
- values);
- }
- static void gl_x11_egl_clear_context(gs_device_t *device)
- {
- Display *display = device->plat->edisplay;
- if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE,
- EGL_NO_CONTEXT)) {
- blog(LOG_ERROR, "Failed to reset current context.");
- }
- }
- static void gl_x11_egl_device_load_swapchain(gs_device_t *device,
- gs_swapchain_t *swap)
- {
- if (device->cur_swap == swap)
- return;
- device->cur_swap = swap;
- device_enter_context(device);
- }
- static void gl_x11_egl_device_present(gs_device_t *device)
- {
- Display *display = device->plat->xdisplay;
- xcb_connection_t *xcb_conn = XGetXCBConnection(display);
- xcb_generic_event_t *xcb_event;
- while ((xcb_event = xcb_poll_for_event(xcb_conn))) {
- free(xcb_event);
- }
- if (eglSwapInterval(device->plat->edisplay, 0) == EGL_FALSE) {
- blog(LOG_ERROR, "eglSwapInterval failed");
- }
- if (!eglSwapBuffers(device->plat->edisplay,
- device->cur_swap->wi->surface))
- blog(LOG_ERROR, "Cannot swap EGL buffers: %s",
- get_egl_error_string());
- }
- static struct gs_texture *gl_x11_egl_device_texture_create_from_dmabuf(
- gs_device_t *device, unsigned int width, unsigned int height,
- uint32_t drm_format, enum gs_color_format color_format,
- uint32_t n_planes, const int *fds, const uint32_t *strides,
- const uint32_t *offsets, const uint64_t *modifiers)
- {
- struct gl_platform *plat = device->plat;
- return gl_egl_create_dmabuf_image(plat->edisplay, width, height,
- drm_format, color_format, n_planes,
- fds, strides, offsets, modifiers);
- }
- static struct gs_texture *gl_x11_egl_device_texture_create_from_pixmap(
- gs_device_t *device, uint32_t width, uint32_t height,
- enum gs_color_format color_format, uint32_t target, void *pixmap)
- {
- struct gl_platform *plat = device->plat;
- return gl_egl_create_texture_from_pixmap(plat->edisplay, width, height,
- color_format, target,
- (EGLClientBuffer)pixmap);
- }
- static bool gl_x11_egl_device_query_dmabuf_capabilities(
- gs_device_t *device, enum gs_dmabuf_flags *dmabuf_flags,
- uint32_t **drm_formats, size_t *n_formats)
- {
- struct gl_platform *plat = device->plat;
- return gl_egl_query_dmabuf_capabilities(plat->xdisplay, dmabuf_flags,
- drm_formats, n_formats);
- }
- static bool gl_x11_egl_device_query_dmabuf_modifiers_for_format(
- gs_device_t *device, uint32_t drm_format, uint64_t **modifiers,
- size_t *n_modifiers)
- {
- struct gl_platform *plat = device->plat;
- return gl_egl_query_dmabuf_modifiers_for_format(
- plat->xdisplay, drm_format, modifiers, n_modifiers);
- }
- static const struct gl_winsys_vtable egl_x11_winsys_vtable = {
- .windowinfo_create = gl_x11_egl_windowinfo_create,
- .windowinfo_destroy = gl_x11_egl_windowinfo_destroy,
- .platform_create = gl_x11_egl_platform_create,
- .platform_destroy = gl_x11_egl_platform_destroy,
- .platform_init_swapchain = gl_x11_egl_platform_init_swapchain,
- .platform_cleanup_swapchain = gl_x11_egl_platform_cleanup_swapchain,
- .device_enter_context = gl_x11_egl_device_enter_context,
- .device_leave_context = gl_x11_egl_device_leave_context,
- .device_get_device_obj = gl_x11_egl_device_get_device_obj,
- .getclientsize = gl_x11_egl_getclientsize,
- .clear_context = gl_x11_egl_clear_context,
- .update = gl_x11_egl_update,
- .device_load_swapchain = gl_x11_egl_device_load_swapchain,
- .device_present = gl_x11_egl_device_present,
- .device_texture_create_from_dmabuf =
- gl_x11_egl_device_texture_create_from_dmabuf,
- .device_query_dmabuf_capabilities =
- gl_x11_egl_device_query_dmabuf_capabilities,
- .device_query_dmabuf_modifiers_for_format =
- gl_x11_egl_device_query_dmabuf_modifiers_for_format,
- .device_texture_create_from_pixmap =
- gl_x11_egl_device_texture_create_from_pixmap,
- };
- const struct gl_winsys_vtable *gl_x11_egl_get_winsys_vtable(void)
- {
- return &egl_x11_winsys_vtable;
- }
|