libobs-opengl: Introduce an EGL/Wayland renderer

Introduce a new Wayland/EGL renderer.
master
Georges Basile Stavracas Neto 2020-03-09 21:09:11 -03:00
parent 137966e01f
commit eab27571d2
5 changed files with 394 additions and 1 deletions

View File

@ -48,6 +48,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

View File

@ -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
}

View File

@ -17,6 +17,7 @@
#pragma once
#include <obs.h>
#include <obs-nix-platform.h>
#include "gl-subsystem.h"

View File

@ -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;
}

View File

@ -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);