aur-obs-studio-wayland/0013-libobs-opengl-Introduc...

467 lines
13 KiB
Diff

From fc5b9a4e3f196e0795b1966b64b86d02e4cc1ecb Mon Sep 17 00:00:00 2001
From: Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
Date: Mon, 9 Mar 2020 21:09:11 -0300
Subject: [PATCH 13/24] libobs-opengl: Introduce an EGL/Wayland renderer
Introduce a new Wayland/EGL renderer.
---
libobs-opengl/CMakeLists.txt | 23 +++
libobs-opengl/gl-nix.c | 7 +-
libobs-opengl/gl-nix.h | 1 +
libobs-opengl/gl-wayland-egl.c | 342 +++++++++++++++++++++++++++++++++
libobs-opengl/gl-wayland-egl.h | 22 +++
5 files changed, 394 insertions(+), 1 deletion(-)
create mode 100644 libobs-opengl/gl-wayland-egl.c
create mode 100644 libobs-opengl/gl-wayland-egl.h
diff --git a/libobs-opengl/CMakeLists.txt b/libobs-opengl/CMakeLists.txt
index f84636cf..0f694b6c 100644
--- a/libobs-opengl/CMakeLists.txt
+++ b/libobs-opengl/CMakeLists.txt
@@ -52,6 +52,29 @@ else()
gl-nix.c
gl-x11-egl.c
gl-x11-glx.c)
+
+ if(ENABLE_WAYLAND)
+ find_package(EGL REQUIRED)
+ find_package(Wayland REQUIRED)
+
+ include_directories(
+ ${WAYLAND_CLIENT_INCLUDE_DIRS}
+ ${WAYLAND_EGL_INCLUDE_DIRS}
+ ${EGL_INCLUDE_DIRS})
+
+ add_definitions(
+ ${WAYLAND_DEFINITIONS})
+
+ set(libobs-opengl_PLATFORM_DEPS
+ ${libobs-opengl_PLATFORM_DEPS}
+ ${WAYLAND_CLIENT_LIBRARIES}
+ ${WAYLAND_EGL_LIBRARIES}
+ ${EGL_LIBRARIES})
+
+ set(libobs-opengl_PLATFORM_SOURCES
+ ${libobs-opengl_PLATFORM_SOURCES}
+ gl-wayland-egl.c)
+ endif()
endif()
set(libobs-opengl_SOURCES
diff --git a/libobs-opengl/gl-nix.c b/libobs-opengl/gl-nix.c
index 581e16a4..6c272c3d 100644
--- a/libobs-opengl/gl-nix.c
+++ b/libobs-opengl/gl-nix.c
@@ -19,6 +19,10 @@
#include "gl-x11-glx.h"
#include "gl-x11-egl.h"
+#ifdef ENABLE_WAYLAND
+#include "gl-wayland-egl.h"
+#endif
+
static const struct gl_winsys_vtable *gl_vtable = NULL;
static void init_winsys(void)
@@ -34,7 +38,8 @@ static void init_winsys(void)
break;
#ifdef ENABLE_WAYLAND
case OBS_NIX_PLATFORM_WAYLAND:
- blog(LOG_ERROR, "EGL/Wayland not implemented yet");
+ gl_vtable = gl_wayland_egl_get_winsys_vtable();
+ blog(LOG_INFO, "Using EGL/Wayland");
break;
#endif
}
diff --git a/libobs-opengl/gl-nix.h b/libobs-opengl/gl-nix.h
index f5532719..741154da 100644
--- a/libobs-opengl/gl-nix.h
+++ b/libobs-opengl/gl-nix.h
@@ -17,6 +17,7 @@
#pragma once
+#include <obs.h>
#include <obs-nix-platform.h>
#include "gl-subsystem.h"
diff --git a/libobs-opengl/gl-wayland-egl.c b/libobs-opengl/gl-wayland-egl.c
new file mode 100644
index 00000000..aad6993e
--- /dev/null
+++ b/libobs-opengl/gl-wayland-egl.c
@@ -0,0 +1,342 @@
+/******************************************************************************
+ Copyright (C) 2019 by Jason Francis <cycl0ps@tuta.io>
+
+ 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/>.
+******************************************************************************/
+
+#include "gl-wayland-egl.h"
+
+#include <wayland-client.h>
+#include <wayland-egl.h>
+
+#include <glad/glad_egl.h>
+
+static const EGLint config_attribs[] = {EGL_SURFACE_TYPE,
+ EGL_WINDOW_BIT,
+ EGL_RENDERABLE_TYPE,
+ EGL_OPENGL_BIT,
+ EGL_STENCIL_SIZE,
+ 0,
+ EGL_DEPTH_SIZE,
+ 0,
+ EGL_BUFFER_SIZE,
+ 32,
+ EGL_ALPHA_SIZE,
+ 8,
+ EGL_NATIVE_RENDERABLE,
+ EGL_TRUE,
+ EGL_NONE};
+
+static const EGLint 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 const EGLint khr_ctx_attribs[] = {
+#ifdef _DEBUG
+ EGL_CONTEXT_FLAGS_KHR,
+ EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR,
+#endif
+ EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR,
+ EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR,
+ EGL_CONTEXT_MAJOR_VERSION_KHR,
+ 3,
+ EGL_CONTEXT_MINOR_VERSION_KHR,
+ 3,
+ EGL_NONE};
+
+struct gl_windowinfo {
+ struct wl_egl_window *window;
+ EGLSurface egl_surface;
+};
+
+struct gl_platform {
+ struct wl_display *wl_display;
+ EGLDisplay display;
+ EGLConfig config;
+ EGLContext context;
+};
+
+struct gl_windowinfo *
+gl_wayland_egl_windowinfo_create(const struct gs_init_data *info)
+{
+ struct wl_egl_window *window =
+ wl_egl_window_create(info->window.display, info->cx, info->cy);
+ if (window == NULL) {
+ blog(LOG_ERROR, "wl_egl_window_create failed");
+ return NULL;
+ }
+
+ struct gl_windowinfo *wi = bmalloc(sizeof(struct gl_windowinfo));
+ wi->window = window;
+ return wi;
+}
+
+static void gl_wayland_egl_windowinfo_destroy(struct gl_windowinfo *info)
+{
+ wl_egl_window_destroy(info->window);
+ bfree(info);
+}
+
+static bool egl_make_current(EGLDisplay display, EGLSurface surface,
+ EGLContext context)
+{
+ if (eglBindAPI(EGL_OPENGL_API) == EGL_FALSE) {
+ blog(LOG_ERROR, "eglBindAPI failed");
+ }
+
+ if (!eglMakeCurrent(display, surface, surface, context)) {
+ blog(LOG_ERROR, "eglMakeCurrent failed");
+ return false;
+ }
+ return true;
+}
+
+static bool egl_context_create(struct gl_platform *plat, const EGLint *attribs)
+{
+ bool success = false;
+ EGLint num_config;
+
+ if (eglBindAPI(EGL_OPENGL_API) == EGL_FALSE) {
+ blog(LOG_ERROR, "eglBindAPI failed");
+ }
+
+ EGLBoolean result = eglChooseConfig(plat->display, config_attribs,
+ &plat->config, 1, &num_config);
+ if (result != EGL_TRUE || num_config == 0) {
+ blog(LOG_ERROR, "eglChooseConfig failed");
+ goto error;
+ }
+
+ plat->context = eglCreateContext(plat->display, plat->config,
+ EGL_NO_CONTEXT, attribs);
+ if (plat->context == EGL_NO_CONTEXT) {
+ blog(LOG_ERROR, "eglCreateContext failed");
+ goto error;
+ }
+
+ success =
+ egl_make_current(plat->display, EGL_NO_SURFACE, plat->context);
+
+error:
+ return success;
+}
+
+static void egl_context_destroy(struct gl_platform *plat)
+{
+ egl_make_current(plat->display, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ eglDestroyContext(plat->display, plat->context);
+}
+
+static bool extension_supported(const char *extensions, const char *search)
+{
+ const char *result = strstr(extensions, search);
+ unsigned long len = strlen(search);
+ return result != NULL &&
+ (result == extensions || *(result - 1) == ' ') &&
+ (result[len] == ' ' || result[len] == '\0');
+}
+
+static struct gl_platform *gl_wayland_egl_platform_create(gs_device_t *device,
+ uint32_t adapter)
+{
+ struct gl_platform *plat = bmalloc(sizeof(struct gl_platform));
+
+ plat->wl_display = obs_get_nix_platform_display();
+
+ device->plat = plat;
+
+ plat->display = eglGetDisplay(plat->wl_display);
+ if (plat->display == EGL_NO_DISPLAY) {
+ blog(LOG_ERROR, "eglGetDisplay failed");
+ goto fail_display_init;
+ }
+
+ EGLint major;
+ EGLint minor;
+
+ if (eglInitialize(plat->display, &major, &minor) == EGL_FALSE) {
+ blog(LOG_ERROR, "eglInitialize failed");
+ goto fail_display_init;
+ }
+
+ blog(LOG_INFO, "Initialized EGL %d.%d", major, minor);
+
+ const char *extensions = eglQueryString(plat->display, EGL_EXTENSIONS);
+ blog(LOG_DEBUG, "Supported EGL Extensions: %s", extensions);
+
+ const EGLint *attribs = ctx_attribs;
+ if (major == 1 && minor == 4) {
+ if (extension_supported(extensions, "EGL_KHR_create_context")) {
+ attribs = khr_ctx_attribs;
+ } else {
+ blog(LOG_ERROR,
+ "EGL_KHR_create_context extension is required to use EGL 1.4.");
+ goto fail_context_create;
+ }
+ } else if (major < 1 || (major == 1 && minor < 4)) {
+ blog(LOG_ERROR, "EGL 1.4 or higher is required.");
+ goto fail_context_create;
+ }
+
+ if (!egl_context_create(plat, attribs)) {
+ goto fail_context_create;
+ }
+
+ if (!gladLoadGL()) {
+ blog(LOG_ERROR, "Failed to load OpenGL entry functions.");
+ goto fail_load_gl;
+ }
+
+ goto success;
+
+fail_load_gl:
+ egl_context_destroy(plat);
+fail_context_create:
+ eglTerminate(plat->display);
+fail_display_init:
+ bfree(plat);
+ plat = NULL;
+success:
+ UNUSED_PARAMETER(adapter);
+ return plat;
+}
+
+static void gl_wayland_egl_platform_destroy(struct gl_platform *plat)
+{
+ if (plat) {
+ egl_context_destroy(plat);
+ eglTerminate(plat->display);
+ bfree(plat);
+ }
+}
+
+static bool gl_wayland_egl_platform_init_swapchain(struct gs_swap_chain *swap)
+{
+ struct gl_platform *plat = swap->device->plat;
+ EGLSurface egl_surface = eglCreateWindowSurface(
+ plat->display, plat->config, swap->wi->window, NULL);
+ if (egl_surface == EGL_NO_SURFACE) {
+ blog(LOG_ERROR, "eglCreateWindowSurface failed");
+ return false;
+ }
+ swap->wi->egl_surface = egl_surface;
+ return true;
+}
+
+static void
+gl_wayland_egl_platform_cleanup_swapchain(struct gs_swap_chain *swap)
+{
+ struct gl_platform *plat = swap->device->plat;
+ eglDestroySurface(plat->display, swap->wi->egl_surface);
+}
+
+static void gl_wayland_egl_device_enter_context(gs_device_t *device)
+{
+ struct gl_platform *plat = device->plat;
+ EGLSurface surface = EGL_NO_SURFACE;
+ if (device->cur_swap != NULL)
+ surface = device->cur_swap->wi->egl_surface;
+ egl_make_current(plat->display, surface, plat->context);
+}
+
+static void gl_wayland_egl_device_leave_context(gs_device_t *device)
+{
+ struct gl_platform *plat = device->plat;
+ egl_make_current(plat->display, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+}
+
+static void *gl_wayland_egl_device_get_device_obj(gs_device_t *device)
+{
+ return device->plat->context;
+}
+
+static void gl_wayland_egl_getclientsize(const struct gs_swap_chain *swap,
+ uint32_t *width, uint32_t *height)
+{
+ wl_egl_window_get_attached_size(swap->wi->window, (void *)width,
+ (void *)height);
+}
+
+static void gl_wayland_egl_clear_context(gs_device_t *device)
+{
+ struct gl_platform *plat = device->plat;
+ egl_make_current(plat->display, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+}
+
+static void gl_wayland_egl_update(gs_device_t *device)
+{
+ wl_egl_window_resize(device->cur_swap->wi->window,
+ device->cur_swap->info.cx,
+ device->cur_swap->info.cy, 0, 0);
+}
+
+static void gl_wayland_egl_device_load_swapchain(gs_device_t *device,
+ gs_swapchain_t *swap)
+{
+ if (device->cur_swap == swap)
+ return;
+
+ device->cur_swap = swap;
+
+ struct gl_platform *plat = device->plat;
+ if (swap == NULL) {
+ egl_make_current(plat->display, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ } else {
+ egl_make_current(plat->display, swap->wi->egl_surface,
+ plat->context);
+ }
+}
+
+static void gl_wayland_egl_device_present(gs_device_t *device)
+{
+ struct gl_platform *plat = device->plat;
+ struct gl_windowinfo *wi = device->cur_swap->wi;
+ if (eglSwapInterval(plat->display, 0) == EGL_FALSE) {
+ blog(LOG_ERROR, "eglSwapInterval failed");
+ }
+ if (eglSwapBuffers(plat->display, wi->egl_surface) == EGL_FALSE) {
+ blog(LOG_ERROR, "eglSwapBuffers failed");
+ }
+}
+
+static const struct gl_winsys_vtable egl_wayland_winsys_vtable = {
+ .windowinfo_create = gl_wayland_egl_windowinfo_create,
+ .windowinfo_destroy = gl_wayland_egl_windowinfo_destroy,
+ .platform_create = gl_wayland_egl_platform_create,
+ .platform_destroy = gl_wayland_egl_platform_destroy,
+ .platform_init_swapchain = gl_wayland_egl_platform_init_swapchain,
+ .platform_cleanup_swapchain = gl_wayland_egl_platform_cleanup_swapchain,
+ .device_enter_context = gl_wayland_egl_device_enter_context,
+ .device_leave_context = gl_wayland_egl_device_leave_context,
+ .device_get_device_obj = gl_wayland_egl_device_get_device_obj,
+ .getclientsize = gl_wayland_egl_getclientsize,
+ .clear_context = gl_wayland_egl_clear_context,
+ .update = gl_wayland_egl_update,
+ .device_load_swapchain = gl_wayland_egl_device_load_swapchain,
+ .device_present = gl_wayland_egl_device_present,
+};
+
+const struct gl_winsys_vtable *gl_wayland_egl_get_winsys_vtable(void)
+{
+ return &egl_wayland_winsys_vtable;
+}
diff --git a/libobs-opengl/gl-wayland-egl.h b/libobs-opengl/gl-wayland-egl.h
new file mode 100644
index 00000000..3384576f
--- /dev/null
+++ b/libobs-opengl/gl-wayland-egl.h
@@ -0,0 +1,22 @@
+/******************************************************************************
+ Copyright (C) 2019 by Jason Francis <cycl0ps@tuta.io>
+
+ 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/>.
+******************************************************************************/
+
+#pragma once
+
+#include "gl-nix.h"
+
+const struct gl_winsys_vtable *gl_wayland_egl_get_winsys_vtable(void);
--
2.28.0