mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-14 04:16:35 +00:00
cad1bb32c8
This commit makes the loading of the GModules threadsafe, and always first tries to load the symbol for the GL library that is selected for the current context. Only then it falls back to looking into the current module (NULL), and only as a last resort the context specific function (e.g. eglGetProcAddress()) is called. Also add configure parameters to select the names of the library modules instead of using the defaults, and let the defaults be independent of the G_MODULE_SUFFIX. https://bugzilla.gnome.org/show_bug.cgi?id=728753
527 lines
15 KiB
C
527 lines
15 KiB
C
/*
|
|
* GStreamer
|
|
* Copyright (C) 2013 Sebastian Dröge <slomo@circular-chaos.org>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <gmodule.h>
|
|
|
|
/* FIXME: Sharing contexts requires the EGLDisplay to be the same
|
|
* may need to box it.
|
|
*/
|
|
|
|
#include "gstglcontext_egl.h"
|
|
|
|
#if GST_GL_HAVE_WINDOW_X11
|
|
#include "../x11/gstglwindow_x11.h"
|
|
#include <gst/gl/x11/gstgldisplay_x11.h>
|
|
#endif
|
|
#if GST_GL_HAVE_WINDOW_WIN32
|
|
#include "../win32/gstglwindow_win32.h"
|
|
#endif
|
|
|
|
static gboolean gst_gl_context_egl_create_context (GstGLContext * context,
|
|
GstGLAPI gl_api, GstGLContext * other_context, GError ** error);
|
|
static void gst_gl_context_egl_destroy_context (GstGLContext * context);
|
|
static gboolean gst_gl_context_egl_choose_format (GstGLContext * context,
|
|
GError ** error);
|
|
|
|
static gboolean gst_gl_context_egl_activate (GstGLContext * context,
|
|
gboolean activate);
|
|
static void gst_gl_context_egl_swap_buffers (GstGLContext * context);
|
|
static guintptr gst_gl_context_egl_get_gl_context (GstGLContext * context);
|
|
static GstGLAPI gst_gl_context_egl_get_gl_api (GstGLContext * context);
|
|
static GstGLPlatform gst_gl_context_egl_get_gl_platform (GstGLContext *
|
|
context);
|
|
static gpointer gst_gl_context_egl_get_proc_address (GstGLContext * context,
|
|
const gchar * name);
|
|
|
|
G_DEFINE_TYPE (GstGLContextEGL, gst_gl_context_egl, GST_GL_TYPE_CONTEXT);
|
|
|
|
static void
|
|
gst_gl_context_egl_class_init (GstGLContextEGLClass * klass)
|
|
{
|
|
GstGLContextClass *context_class = (GstGLContextClass *) klass;
|
|
|
|
context_class->get_gl_context =
|
|
GST_DEBUG_FUNCPTR (gst_gl_context_egl_get_gl_context);
|
|
context_class->activate = GST_DEBUG_FUNCPTR (gst_gl_context_egl_activate);
|
|
context_class->create_context =
|
|
GST_DEBUG_FUNCPTR (gst_gl_context_egl_create_context);
|
|
context_class->destroy_context =
|
|
GST_DEBUG_FUNCPTR (gst_gl_context_egl_destroy_context);
|
|
context_class->choose_format =
|
|
GST_DEBUG_FUNCPTR (gst_gl_context_egl_choose_format);
|
|
context_class->swap_buffers =
|
|
GST_DEBUG_FUNCPTR (gst_gl_context_egl_swap_buffers);
|
|
|
|
context_class->get_gl_api = GST_DEBUG_FUNCPTR (gst_gl_context_egl_get_gl_api);
|
|
context_class->get_gl_platform =
|
|
GST_DEBUG_FUNCPTR (gst_gl_context_egl_get_gl_platform);
|
|
context_class->get_proc_address =
|
|
GST_DEBUG_FUNCPTR (gst_gl_context_egl_get_proc_address);
|
|
}
|
|
|
|
static void
|
|
gst_gl_context_egl_init (GstGLContextEGL * context)
|
|
{
|
|
}
|
|
|
|
/* Must be called in the gl thread */
|
|
GstGLContextEGL *
|
|
gst_gl_context_egl_new (void)
|
|
{
|
|
GstGLContextEGL *window = g_object_new (GST_GL_TYPE_CONTEXT_EGL, NULL);
|
|
|
|
return window;
|
|
}
|
|
|
|
static const gchar *
|
|
gst_gl_context_egl_get_error_string (void)
|
|
{
|
|
EGLint nErr = eglGetError ();
|
|
|
|
switch (nErr) {
|
|
case EGL_SUCCESS:
|
|
return "EGL_SUCCESS";
|
|
case EGL_BAD_DISPLAY:
|
|
return "EGL_BAD_DISPLAY";
|
|
case EGL_NOT_INITIALIZED:
|
|
return "EGL_NOT_INITIALIZED";
|
|
case EGL_BAD_ACCESS:
|
|
return "EGL_BAD_ACCESS";
|
|
case EGL_BAD_ALLOC:
|
|
return "EGL_BAD_ALLOC";
|
|
case EGL_BAD_ATTRIBUTE:
|
|
return "EGL_BAD_ATTRIBUTE";
|
|
case EGL_BAD_CONFIG:
|
|
return "EGL_BAD_CONFIG";
|
|
case EGL_BAD_CONTEXT:
|
|
return "EGL_BAD_CONTEXT";
|
|
case EGL_BAD_CURRENT_SURFACE:
|
|
return "EGL_BAD_CURRENT_SURFACE";
|
|
case EGL_BAD_MATCH:
|
|
return "EGL_BAD_MATCH";
|
|
case EGL_BAD_NATIVE_PIXMAP:
|
|
return "EGL_BAD_NATIVE_PIXMAP";
|
|
case EGL_BAD_NATIVE_WINDOW:
|
|
return "EGL_BAD_NATIVE_WINDOW";
|
|
case EGL_BAD_PARAMETER:
|
|
return "EGL_BAD_PARAMETER";
|
|
case EGL_BAD_SURFACE:
|
|
return "EGL_BAD_SURFACE";
|
|
default:
|
|
return "unknown";
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
gst_gl_context_egl_choose_format (GstGLContext * context, GError ** error)
|
|
{
|
|
#if GST_GL_HAVE_WINDOW_X11
|
|
if (GST_GL_IS_WINDOW_X11 (context->window)) {
|
|
GstGLWindow *window = gst_gl_context_get_window (context);
|
|
GstGLWindowX11 *window_x11 = GST_GL_WINDOW_X11 (window);
|
|
gint ret;
|
|
|
|
window_x11->visual_info = g_new0 (XVisualInfo, 1);
|
|
ret = XMatchVisualInfo (window_x11->device, window_x11->screen_num,
|
|
window_x11->depth, TrueColor, window_x11->visual_info);
|
|
|
|
gst_object_unref (window);
|
|
|
|
if (ret == 0) {
|
|
g_set_error (error, GST_GL_CONTEXT_ERROR,
|
|
GST_GL_CONTEXT_ERROR_WRONG_CONFIG, "Failed to match XVisualInfo");
|
|
return FALSE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_gl_context_egl_choose_config (GstGLContextEGL * egl,
|
|
GstGLContext * other_context, GError ** error)
|
|
{
|
|
EGLint numConfigs;
|
|
gint i = 0;
|
|
EGLint config_attrib[20];
|
|
|
|
config_attrib[i++] = EGL_SURFACE_TYPE;
|
|
config_attrib[i++] = EGL_WINDOW_BIT;
|
|
config_attrib[i++] = EGL_RENDERABLE_TYPE;
|
|
if (egl->gl_api & GST_GL_API_GLES2)
|
|
config_attrib[i++] = EGL_OPENGL_ES2_BIT;
|
|
else
|
|
config_attrib[i++] = EGL_OPENGL_BIT;
|
|
#if defined(USE_EGL_RPI) && GST_GL_HAVE_WINDOW_WAYLAND
|
|
/* The configurations r=5 g=6 b=5 seems to be buggy whereas
|
|
* it works when using dispmanx directly */
|
|
config_attrib[i++] = EGL_BUFFER_SIZE;
|
|
config_attrib[i++] = 24;
|
|
/* same with a=0 */
|
|
config_attrib[i++] = EGL_ALPHA_SIZE;
|
|
config_attrib[i++] = 1;
|
|
#endif
|
|
config_attrib[i++] = EGL_DEPTH_SIZE;
|
|
config_attrib[i++] = 16;
|
|
config_attrib[i++] = EGL_NONE;
|
|
|
|
if (eglChooseConfig (egl->egl_display, config_attrib,
|
|
&egl->egl_config, 1, &numConfigs)) {
|
|
GST_INFO ("config set: %" G_GUINTPTR_FORMAT ", %u",
|
|
(guintptr) egl->egl_config, (unsigned int) numConfigs);
|
|
} else {
|
|
g_set_error (error, GST_GL_CONTEXT_ERROR, GST_GL_CONTEXT_ERROR_WRONG_CONFIG,
|
|
"Failed to set window configuration: %s",
|
|
gst_gl_context_egl_get_error_string ());
|
|
goto failure;
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
failure:
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_gl_context_egl_create_context (GstGLContext * context,
|
|
GstGLAPI gl_api, GstGLContext * other_context, GError ** error)
|
|
{
|
|
GstGLContextEGL *egl;
|
|
GstGLWindow *window = NULL;
|
|
EGLNativeWindowType window_handle = (EGLNativeWindowType) 0;
|
|
gint i = 0;
|
|
EGLint context_attrib[3];
|
|
EGLint majorVersion;
|
|
EGLint minorVersion;
|
|
const gchar *egl_exts;
|
|
gboolean need_surface = TRUE;
|
|
guintptr external_gl_context = 0;
|
|
GstGLDisplay *display;
|
|
|
|
egl = GST_GL_CONTEXT_EGL (context);
|
|
window = gst_gl_context_get_window (context);
|
|
|
|
if (other_context) {
|
|
if (gst_gl_context_get_gl_platform (other_context) != GST_GL_PLATFORM_EGL) {
|
|
g_set_error (error, GST_GL_CONTEXT_ERROR,
|
|
GST_GL_CONTEXT_ERROR_WRONG_CONFIG,
|
|
"Cannot share context with non-EGL context");
|
|
goto failure;
|
|
}
|
|
external_gl_context = gst_gl_context_get_gl_context (other_context);
|
|
}
|
|
|
|
if ((gl_api & (GST_GL_API_OPENGL | GST_GL_API_GLES2)) == GST_GL_API_NONE) {
|
|
g_set_error (error, GST_GL_CONTEXT_ERROR, GST_GL_CONTEXT_ERROR_WRONG_API,
|
|
"EGL supports opengl or gles2");
|
|
goto failure;
|
|
}
|
|
|
|
display = gst_gl_context_get_display (context);
|
|
|
|
if (display->type == GST_GL_DISPLAY_TYPE_EGL) {
|
|
egl->egl_display = (EGLDisplay) gst_gl_display_get_handle (display);
|
|
} else {
|
|
guintptr native_display = gst_gl_display_get_handle (display);
|
|
|
|
if (!native_display) {
|
|
GstGLWindow *window = NULL;
|
|
GST_WARNING ("Failed to get a global display handle, falling back to "
|
|
"per-window display handles. Context sharing may not work");
|
|
|
|
if (other_context)
|
|
window = gst_gl_context_get_window (other_context);
|
|
if (!window)
|
|
window = gst_gl_context_get_window (context);
|
|
if (window) {
|
|
native_display = gst_gl_window_get_display (window);
|
|
gst_object_unref (window);
|
|
}
|
|
}
|
|
|
|
egl->egl_display = eglGetDisplay ((EGLNativeDisplayType) native_display);
|
|
}
|
|
gst_object_unref (display);
|
|
|
|
if (eglInitialize (egl->egl_display, &majorVersion, &minorVersion)) {
|
|
GST_INFO ("egl initialized, version: %d.%d", majorVersion, minorVersion);
|
|
} else {
|
|
g_set_error (error, GST_GL_CONTEXT_ERROR,
|
|
GST_GL_CONTEXT_ERROR_RESOURCE_UNAVAILABLE,
|
|
"Failed to initialize egl: %s", gst_gl_context_egl_get_error_string ());
|
|
goto failure;
|
|
}
|
|
|
|
if (gl_api & GST_GL_API_OPENGL) {
|
|
/* egl + opengl only available with EGL 1.4+ */
|
|
if (majorVersion == 1 && minorVersion <= 3) {
|
|
if ((gl_api & ~GST_GL_API_OPENGL) == GST_GL_API_NONE) {
|
|
g_set_error (error, GST_GL_CONTEXT_ERROR, GST_GL_CONTEXT_ERROR_OLD_LIBS,
|
|
"EGL version (%i.%i) too old for OpenGL support, (needed at least 1.4)",
|
|
majorVersion, minorVersion);
|
|
goto failure;
|
|
} else {
|
|
GST_WARNING
|
|
("EGL version (%i.%i) too old for OpenGL support, (needed at least 1.4)",
|
|
majorVersion, minorVersion);
|
|
if (gl_api & GST_GL_API_GLES2) {
|
|
goto try_gles2;
|
|
} else {
|
|
g_set_error (error, GST_GL_CONTEXT_ERROR,
|
|
GST_GL_CONTEXT_ERROR_WRONG_CONFIG,
|
|
"Failed to choose a suitable OpenGL API");
|
|
goto failure;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!eglBindAPI (EGL_OPENGL_API)) {
|
|
g_set_error (error, GST_GL_CONTEXT_ERROR, GST_GL_CONTEXT_ERROR_FAILED,
|
|
"Failed to bind OpenGL API: %s",
|
|
gst_gl_context_egl_get_error_string ());
|
|
goto failure;
|
|
}
|
|
|
|
GST_INFO ("Using OpenGL");
|
|
egl->gl_api = GST_GL_API_OPENGL;
|
|
} else if (gl_api & GST_GL_API_GLES2) {
|
|
try_gles2:
|
|
if (!eglBindAPI (EGL_OPENGL_ES_API)) {
|
|
g_set_error (error, GST_GL_CONTEXT_ERROR, GST_GL_CONTEXT_ERROR_FAILED,
|
|
"Failed to bind OpenGL|ES API: %s",
|
|
gst_gl_context_egl_get_error_string ());
|
|
goto failure;
|
|
}
|
|
|
|
GST_INFO ("Using OpenGL|ES 2.0");
|
|
egl->gl_api = GST_GL_API_GLES2;
|
|
}
|
|
|
|
if (!gst_gl_context_egl_choose_config (egl, other_context, error)) {
|
|
g_assert (error == NULL || *error != NULL);
|
|
goto failure;
|
|
}
|
|
|
|
GST_DEBUG ("about to create gl context\n");
|
|
|
|
if (egl->gl_api & GST_GL_API_GLES2) {
|
|
context_attrib[i++] = EGL_CONTEXT_CLIENT_VERSION;
|
|
context_attrib[i++] = 2;
|
|
}
|
|
context_attrib[i++] = EGL_NONE;
|
|
|
|
egl->egl_context =
|
|
eglCreateContext (egl->egl_display, egl->egl_config,
|
|
(EGLContext) external_gl_context, context_attrib);
|
|
|
|
if (egl->egl_context != EGL_NO_CONTEXT) {
|
|
GST_INFO ("gl context created: %" G_GUINTPTR_FORMAT,
|
|
(guintptr) egl->egl_context);
|
|
} else {
|
|
g_set_error (error, GST_GL_CONTEXT_ERROR,
|
|
GST_GL_CONTEXT_ERROR_CREATE_CONTEXT,
|
|
"Failed to create a OpenGL context: %s",
|
|
gst_gl_context_egl_get_error_string ());
|
|
goto failure;
|
|
}
|
|
|
|
egl_exts = eglQueryString (egl->egl_display, EGL_EXTENSIONS);
|
|
|
|
if (other_context == NULL) {
|
|
/* FIXME do we want a window vfunc ? */
|
|
#if GST_GL_HAVE_WINDOW_X11
|
|
if (GST_GL_IS_WINDOW_X11 (context->window)) {
|
|
gst_gl_window_x11_create_window ((GstGLWindowX11 *) context->window);
|
|
}
|
|
#endif
|
|
#if GST_GL_HAVE_WINDOW_WIN32
|
|
if (GST_GL_IS_WINDOW_WIN32 (context->window)) {
|
|
gst_gl_window_win32_create_window ((GstGLWindowWin32 *) context->window);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (window)
|
|
window_handle =
|
|
(EGLNativeWindowType) gst_gl_window_get_window_handle (window);
|
|
|
|
if (window_handle) {
|
|
egl->egl_surface =
|
|
eglCreateWindowSurface (egl->egl_display, egl->egl_config,
|
|
window_handle, NULL);
|
|
} else if (!gst_gl_check_extension ("EGL_KHR_surfaceless_context", egl_exts)) {
|
|
EGLint surface_attrib[7];
|
|
gint j = 0;
|
|
|
|
/* FIXME: Width/height doesn't seem to matter but we can't leave them
|
|
* at 0, otherwise X11 complains about BadValue */
|
|
surface_attrib[j++] = EGL_WIDTH;
|
|
surface_attrib[j++] = 1;
|
|
surface_attrib[j++] = EGL_HEIGHT;
|
|
surface_attrib[j++] = 1;
|
|
surface_attrib[j++] = EGL_LARGEST_PBUFFER;
|
|
surface_attrib[j++] = EGL_TRUE;
|
|
surface_attrib[j++] = EGL_NONE;
|
|
|
|
egl->egl_surface =
|
|
eglCreatePbufferSurface (egl->egl_display, egl->egl_config,
|
|
surface_attrib);
|
|
} else {
|
|
egl->egl_surface = EGL_NO_SURFACE;
|
|
need_surface = FALSE;
|
|
}
|
|
|
|
if (need_surface) {
|
|
if (egl->egl_surface != EGL_NO_SURFACE) {
|
|
GST_INFO ("surface created");
|
|
} else {
|
|
g_set_error (error, GST_GL_CONTEXT_ERROR, GST_GL_CONTEXT_ERROR_FAILED,
|
|
"Failed to create window surface: %s",
|
|
gst_gl_context_egl_get_error_string ());
|
|
goto failure;
|
|
}
|
|
}
|
|
|
|
if (window)
|
|
gst_object_unref (window);
|
|
|
|
return TRUE;
|
|
|
|
failure:
|
|
if (window)
|
|
gst_object_unref (window);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
gst_gl_context_egl_destroy_context (GstGLContext * context)
|
|
{
|
|
GstGLContextEGL *egl;
|
|
|
|
egl = GST_GL_CONTEXT_EGL (context);
|
|
|
|
gst_gl_context_egl_activate (context, FALSE);
|
|
|
|
if (egl->egl_surface)
|
|
eglDestroySurface (egl->egl_surface, egl->egl_display);
|
|
|
|
if (egl->egl_context)
|
|
eglDestroyContext (egl->egl_display, egl->egl_context);
|
|
|
|
eglReleaseThread ();
|
|
}
|
|
|
|
static gboolean
|
|
gst_gl_context_egl_activate (GstGLContext * context, gboolean activate)
|
|
{
|
|
GstGLContextEGL *egl;
|
|
gboolean result;
|
|
|
|
egl = GST_GL_CONTEXT_EGL (context);
|
|
|
|
if (activate)
|
|
result = eglMakeCurrent (egl->egl_display, egl->egl_surface,
|
|
egl->egl_surface, egl->egl_context);
|
|
else
|
|
result = eglMakeCurrent (egl->egl_display, EGL_NO_SURFACE,
|
|
EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
|
|
|
return result;
|
|
}
|
|
|
|
static guintptr
|
|
gst_gl_context_egl_get_gl_context (GstGLContext * context)
|
|
{
|
|
return (guintptr) GST_GL_CONTEXT_EGL (context)->egl_context;
|
|
}
|
|
|
|
static void
|
|
gst_gl_context_egl_swap_buffers (GstGLContext * context)
|
|
{
|
|
GstGLContextEGL *egl;
|
|
|
|
egl = GST_GL_CONTEXT_EGL (context);
|
|
|
|
eglSwapBuffers (egl->egl_display, egl->egl_surface);
|
|
}
|
|
|
|
static GstGLAPI
|
|
gst_gl_context_egl_get_gl_api (GstGLContext * context)
|
|
{
|
|
return GST_GL_CONTEXT_EGL (context)->gl_api;
|
|
}
|
|
|
|
static GstGLPlatform
|
|
gst_gl_context_egl_get_gl_platform (GstGLContext * context)
|
|
{
|
|
return GST_GL_PLATFORM_EGL;
|
|
}
|
|
|
|
static GModule *module_egl;
|
|
|
|
static gpointer
|
|
load_egl_module (gpointer user_data)
|
|
{
|
|
#ifdef GST_GL_LIBEGL_MODULE_NAME
|
|
module_egl = g_module_open (GST_GL_LIBEGL_MODULE_NAME, G_MODULE_BIND_LAZY);
|
|
#else
|
|
/* This automatically handles the suffix and even .la files */
|
|
module_egl = g_module_open ("libEGL", G_MODULE_BIND_LAZY);
|
|
|
|
/* On Linux the .so is only in -dev packages, try with a real soname
|
|
* Proper compilers will optimize away the strcmp */
|
|
if (!module_egl && strcmp (G_MODULE_SUFFIX, "so") == 0)
|
|
module_egl = g_module_open ("libEGL.so.1", G_MODULE_BIND_LAZY);
|
|
#endif
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static gpointer
|
|
gst_gl_context_egl_get_proc_address (GstGLContext * context, const gchar * name)
|
|
{
|
|
gpointer result = NULL;
|
|
static GOnce g_once = G_ONCE_INIT;
|
|
|
|
result = gst_gl_context_default_get_proc_address (context, name);
|
|
|
|
g_once (&g_once, load_egl_module, NULL);
|
|
|
|
if (!result && module_egl) {
|
|
g_module_symbol (module_egl, name, &result);
|
|
}
|
|
|
|
/* FIXME: On Android this returns wrong addresses for non-EGL functions */
|
|
#if GST_GL_HAVE_WINDOW_ANDROID
|
|
if (!result && g_str_has_prefix (name, "egl")) {
|
|
#else
|
|
if (!result) {
|
|
result = eglGetProcAddress (name);
|
|
#endif
|
|
}
|
|
|
|
return result;
|
|
}
|