mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-11 09:55:36 +00:00
7066c849e4
New API: - gst_gl_context_get_config() - gst_gl_context_request_config() A GL context configuration is a GstStructure that has some well-known names for common values that can also be extended in platform-specific ways if necessary. Wrapped OpenGL contexts may be able to retrieve the GL context configuration depending on the platform. If that information is available, GstGLContext will attempt to create an context that matches the shared OpenGL context config unless gst_gl_context_request_config() has been called. A new environment variable 'GST_GL_CONFIG' will be read to influence the configuration chosen. The environment variable will only be used as a fallback if gst_gl_context_request_config() has not been called. Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/-/merge_requests/809>
1464 lines
44 KiB
C
1464 lines
44 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"
|
|
|
|
#include <gst/gl/gl.h>
|
|
#include <gst/gl/gstglcontext_private.h>
|
|
|
|
#include "gstegl.h"
|
|
#include "../utils/opengl_versions.h"
|
|
#include "../utils/gles_versions.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_WAYLAND
|
|
#include "../wayland/gstglwindow_wayland_egl.h"
|
|
#endif
|
|
#if GST_GL_HAVE_WINDOW_WIN32
|
|
#include "../win32/gstglwindow_win32.h"
|
|
#endif
|
|
#if GST_GL_HAVE_WINDOW_DISPMANX
|
|
#include "../dispmanx/gstglwindow_dispmanx_egl.h"
|
|
#endif
|
|
#if GST_GL_HAVE_WINDOW_GBM
|
|
#include "../gbm/gstglwindow_gbm_egl.h"
|
|
#endif
|
|
|
|
#define GST_CAT_DEFAULT gst_gl_context_debug
|
|
|
|
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 gboolean gst_gl_context_egl_check_feature (GstGLContext * context,
|
|
const gchar * feature);
|
|
static void gst_gl_context_egl_get_gl_platform_version (GstGLContext * context,
|
|
gint * major, gint * minor);
|
|
static GstStructure *gst_gl_context_egl_get_config (GstGLContext * context);
|
|
static gboolean gst_gl_context_egl_request_config (GstGLContext * context,
|
|
GstStructure * config);
|
|
|
|
G_DEFINE_TYPE (GstGLContextEGL, gst_gl_context_egl, GST_TYPE_GL_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);
|
|
context_class->check_feature =
|
|
GST_DEBUG_FUNCPTR (gst_gl_context_egl_check_feature);
|
|
context_class->get_current_context =
|
|
GST_DEBUG_FUNCPTR (gst_gl_context_egl_get_current_context);
|
|
context_class->get_gl_platform_version =
|
|
GST_DEBUG_FUNCPTR (gst_gl_context_egl_get_gl_platform_version);
|
|
context_class->get_config = GST_DEBUG_FUNCPTR (gst_gl_context_egl_get_config);
|
|
context_class->request_config =
|
|
GST_DEBUG_FUNCPTR (gst_gl_context_egl_request_config);
|
|
}
|
|
|
|
static void
|
|
gst_gl_context_egl_init (GstGLContextEGL * context)
|
|
{
|
|
}
|
|
|
|
/* Must be called in the gl thread */
|
|
GstGLContextEGL *
|
|
gst_gl_context_egl_new (GstGLDisplay * display)
|
|
{
|
|
GstGLContextEGL *context;
|
|
|
|
/* XXX: display type could theoretically be anything, as long as
|
|
* eglGetDisplay supports it. */
|
|
context = g_object_new (GST_TYPE_GL_CONTEXT_EGL, NULL);
|
|
gst_object_ref_sink (context);
|
|
|
|
return context;
|
|
}
|
|
|
|
static gboolean
|
|
gst_gl_context_egl_choose_format (GstGLContext * context, GError ** error)
|
|
{
|
|
#if GST_GL_HAVE_WINDOW_X11
|
|
if (GST_IS_GL_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 GstGLAPI
|
|
egl_conformant_to_gst (int conformant)
|
|
{
|
|
GstGLAPI ret = GST_GL_API_NONE;
|
|
|
|
if (conformant & EGL_OPENGL_BIT)
|
|
ret |= GST_GL_API_OPENGL | GST_GL_API_OPENGL3;
|
|
if (conformant & EGL_OPENGL_ES_BIT)
|
|
ret |= GST_GL_API_GLES1;
|
|
if (conformant & EGL_OPENGL_ES2_BIT)
|
|
ret |= GST_GL_API_GLES2;
|
|
#if defined(EGL_KHR_create_context)
|
|
if (conformant & EGL_OPENGL_ES3_BIT_KHR)
|
|
/* FIXME: need another gles3 value? */
|
|
ret |= GST_GL_API_GLES2;
|
|
#endif
|
|
#if 0
|
|
if (conformant & EGL_OPENVG_BIT)
|
|
conformant_values[i++] = "OpenVG";
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
static GstGLConfigSurfaceType
|
|
egl_surface_type_to_gst (int surface)
|
|
{
|
|
GstGLConfigSurfaceType ret = GST_GL_CONFIG_SURFACE_TYPE_NONE;
|
|
|
|
if (surface & EGL_WINDOW_BIT)
|
|
ret |= GST_GL_CONFIG_SURFACE_TYPE_WINDOW;
|
|
if (surface & EGL_PBUFFER_BIT)
|
|
ret |= GST_GL_CONFIG_SURFACE_TYPE_PBUFFER;
|
|
#if 0
|
|
if (surface & EGL_MULTISAMPLE_RESOLVE_BOX_BIT)
|
|
surface_values[i++] = "multisample-resolve-box";
|
|
if (surface & EGL_SWAP_BEHAVIOR_PRESERVED_BIT)
|
|
surface_values[i++] = "swap-behaviour-preserved";
|
|
if (surface & EGL_VG_ALPHA_FORMAT_PRE_BIT)
|
|
surface_values[i++] = "vg-alpha-format-pre";
|
|
if (surface & EGL_VG_COLORSPACE_LINEAR_BIT)
|
|
surface_values[i++] = "vg-colorspace-linear";
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
static GstGLConfigCaveat
|
|
egl_caveat_to_gst (int caveat)
|
|
{
|
|
switch (caveat) {
|
|
case EGL_NONE:
|
|
return GST_GL_CONFIG_CAVEAT_NONE;
|
|
case EGL_SLOW_CONFIG:
|
|
return GST_GL_CONFIG_CAVEAT_SLOW;
|
|
case EGL_NON_CONFORMANT_CONFIG:
|
|
return GST_GL_CONFIG_CAVEAT_NON_CONFORMANT;
|
|
default:
|
|
GST_WARNING ("unknown EGL caveat value %u (0x%x)", caveat, caveat);
|
|
return GST_GL_CONFIG_CAVEAT_NON_CONFORMANT;
|
|
}
|
|
}
|
|
|
|
static GstStructure *
|
|
egl_config_to_structure (EGLDisplay egl_display, EGLConfig config)
|
|
{
|
|
GstStructure *ret;
|
|
int val;
|
|
int buffer_type;
|
|
|
|
if (!egl_display)
|
|
return NULL;
|
|
|
|
ret = gst_structure_new (GST_GL_CONFIG_STRUCTURE_NAME,
|
|
GST_GL_CONFIG_STRUCTURE_SET_ARGS (PLATFORM, GstGLPlatform,
|
|
GST_GL_PLATFORM_EGL), NULL);
|
|
|
|
if (!eglGetConfigAttrib (egl_display, config, EGL_CONFIG_ID, &val))
|
|
goto failure;
|
|
gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS (CONFIG_ID, int,
|
|
val), NULL);
|
|
|
|
#if 0
|
|
{
|
|
/* Don't know how to translate this value, it's platform and implementation
|
|
* dependant
|
|
*/
|
|
int native_visual_type;
|
|
if (!eglGetConfigAttrib (egl_display, config, EGL_NATIVE_VISUAL_TYPE,
|
|
&native_visual_type))
|
|
goto failure;
|
|
}
|
|
#endif
|
|
|
|
if (!eglGetConfigAttrib (egl_display, config, EGL_NATIVE_VISUAL_ID, &val))
|
|
goto failure;
|
|
gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS (NATIVE_VISUAL_ID,
|
|
guint, val), NULL);
|
|
|
|
if (!eglGetConfigAttrib (egl_display, config, EGL_NATIVE_RENDERABLE, &val))
|
|
goto failure;
|
|
gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS (NATIVE_RENDERABLE,
|
|
gboolean, val), NULL);
|
|
|
|
if (!eglGetConfigAttrib (egl_display, config, EGL_CONFORMANT, &val))
|
|
goto failure;
|
|
gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS (CONFORMANT_API,
|
|
GstGLAPI, egl_conformant_to_gst (val)), NULL);
|
|
|
|
if (!eglGetConfigAttrib (egl_display, config, EGL_RENDERABLE_TYPE, &val))
|
|
goto failure;
|
|
gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS (RENDERABLE_API,
|
|
GstGLAPI, egl_conformant_to_gst (val)), NULL);
|
|
|
|
if (!eglGetConfigAttrib (egl_display, config, EGL_SURFACE_TYPE, &val))
|
|
goto failure;
|
|
gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS (SURFACE_TYPE,
|
|
GstGLConfigSurfaceType, egl_surface_type_to_gst (val)), NULL);
|
|
|
|
if (!eglGetConfigAttrib (egl_display, config, EGL_CONFIG_CAVEAT, &val))
|
|
goto failure;
|
|
gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS (CAVEAT,
|
|
GstGLConfigCaveat, egl_caveat_to_gst (val)), NULL);
|
|
|
|
if (!eglGetConfigAttrib (egl_display, config, EGL_LEVEL, &val))
|
|
goto failure;
|
|
gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS (LEVEL, int, val),
|
|
NULL);
|
|
|
|
|
|
if (!eglGetConfigAttrib (egl_display, config, EGL_COLOR_BUFFER_TYPE,
|
|
&buffer_type))
|
|
goto failure;
|
|
|
|
if (buffer_type == EGL_RGB_BUFFER) {
|
|
if (!eglGetConfigAttrib (egl_display, config, EGL_RED_SIZE, &val))
|
|
goto failure;
|
|
gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS (RED_SIZE, int,
|
|
val), NULL);
|
|
|
|
if (!eglGetConfigAttrib (egl_display, config, EGL_GREEN_SIZE, &val))
|
|
goto failure;
|
|
gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS (GREEN_SIZE, int,
|
|
val), NULL);
|
|
|
|
if (!eglGetConfigAttrib (egl_display, config, EGL_BLUE_SIZE, &val))
|
|
goto failure;
|
|
gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS (BLUE_SIZE, int,
|
|
val), NULL);
|
|
|
|
if (!eglGetConfigAttrib (egl_display, config, EGL_ALPHA_SIZE, &val))
|
|
goto failure;
|
|
gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS (ALPHA_SIZE, int,
|
|
val), NULL);
|
|
} else if (buffer_type == EGL_LUMINANCE_BUFFER) {
|
|
if (!eglGetConfigAttrib (egl_display, config, EGL_LUMINANCE_SIZE, &val))
|
|
goto failure;
|
|
gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS (LUMINANCE_SIZE,
|
|
int, val), NULL);
|
|
|
|
if (!eglGetConfigAttrib (egl_display, config, EGL_ALPHA_SIZE, &val))
|
|
goto failure;
|
|
gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS (ALPHA_SIZE, int,
|
|
val), NULL);
|
|
} else {
|
|
GST_WARNING ("unknown EGL_COLOR_BUFFER_TYPE value %x", buffer_type);
|
|
goto failure;
|
|
}
|
|
|
|
if (!eglGetConfigAttrib (egl_display, config, EGL_DEPTH_SIZE, &val))
|
|
goto failure;
|
|
gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS (DEPTH_SIZE, int,
|
|
val), NULL);
|
|
|
|
if (!eglGetConfigAttrib (egl_display, config, EGL_STENCIL_SIZE, &val))
|
|
goto failure;
|
|
gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS (STENCIL_SIZE, int,
|
|
val), NULL);
|
|
|
|
if (!eglGetConfigAttrib (egl_display, config, EGL_MIN_SWAP_INTERVAL, &val))
|
|
goto failure;
|
|
gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS (MIN_SWAP_INTERVAL,
|
|
int, val), NULL);
|
|
|
|
if (!eglGetConfigAttrib (egl_display, config, EGL_MAX_SWAP_INTERVAL, &val))
|
|
goto failure;
|
|
gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS (MAX_SWAP_INTERVAL,
|
|
int, val), NULL);
|
|
|
|
if (!eglGetConfigAttrib (egl_display, config, EGL_MAX_PBUFFER_WIDTH, &val))
|
|
goto failure;
|
|
gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS (MAX_PBUFFER_WIDTH,
|
|
int, val), NULL);
|
|
|
|
if (!eglGetConfigAttrib (egl_display, config, EGL_MAX_PBUFFER_HEIGHT, &val))
|
|
goto failure;
|
|
gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS (MAX_PBUFFER_HEIGHT,
|
|
int, val), NULL);
|
|
|
|
if (!eglGetConfigAttrib (egl_display, config, EGL_MAX_PBUFFER_PIXELS, &val))
|
|
goto failure;
|
|
gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS (MAX_PBUFFER_PIXELS,
|
|
int, val), NULL);
|
|
|
|
if (!eglGetConfigAttrib (egl_display, config, EGL_SAMPLE_BUFFERS, &val))
|
|
goto failure;
|
|
gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS (SAMPLE_BUFFERS, int,
|
|
val), NULL);
|
|
|
|
if (!eglGetConfigAttrib (egl_display, config, EGL_SAMPLES, &val))
|
|
goto failure;
|
|
gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS (SAMPLES, int, val),
|
|
NULL);
|
|
|
|
return ret;
|
|
|
|
failure:
|
|
gst_structure_free (ret);
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
gst_gl_context_egl_dump_config (GstGLContextEGL * egl, EGLConfig config)
|
|
{
|
|
int id;
|
|
int buffer_type;
|
|
|
|
if (!egl->egl_display)
|
|
return;
|
|
|
|
{
|
|
int native_visual_id, native_visual_type;
|
|
if (!eglGetConfigAttrib (egl->egl_display, config, EGL_CONFIG_ID, &id))
|
|
return;
|
|
if (!eglGetConfigAttrib (egl->egl_display, config, EGL_NATIVE_VISUAL_ID,
|
|
&native_visual_id))
|
|
return;
|
|
if (!eglGetConfigAttrib (egl->egl_display, config, EGL_NATIVE_VISUAL_TYPE,
|
|
&native_visual_type))
|
|
return;
|
|
GST_DEBUG_OBJECT (egl, "dumping EGLConfig %p with id 0x%x and "
|
|
"native visual id 0x%x of type 0x%x", config, id, native_visual_id,
|
|
native_visual_type);
|
|
}
|
|
|
|
{
|
|
#define MAX_CONFORMANT 8
|
|
int conformant, i = 0;
|
|
const char *conformant_values[MAX_CONFORMANT] = { NULL, };
|
|
char *conformant_str = NULL;;
|
|
|
|
if (!eglGetConfigAttrib (egl->egl_display, config, EGL_CONFORMANT,
|
|
&conformant))
|
|
return;
|
|
|
|
if (conformant & EGL_OPENGL_BIT)
|
|
conformant_values[i++] = "OpenGL";
|
|
if (conformant & EGL_OPENGL_ES_BIT)
|
|
conformant_values[i++] = "OpenGL ES";
|
|
if (conformant & EGL_OPENGL_ES2_BIT)
|
|
conformant_values[i++] = "OpenGL ES 2.x";
|
|
#if defined(EGL_KHR_create_context)
|
|
if (conformant & EGL_OPENGL_ES3_BIT_KHR)
|
|
conformant_values[i++] = "OpenGL ES 3.x";
|
|
#endif
|
|
if (conformant & EGL_OPENVG_BIT)
|
|
conformant_values[i++] = "OpenVG";
|
|
|
|
/* bad things have happened if this fails: we haven't allocated enough
|
|
* space to hold all the values */
|
|
g_assert (i < MAX_CONFORMANT);
|
|
|
|
conformant_str = g_strjoinv ("|", (char **) conformant_values);
|
|
GST_DEBUG_OBJECT (egl, "Conformant for %s", conformant_str);
|
|
g_free (conformant_str);
|
|
#undef MAX_CONFORMANT
|
|
}
|
|
|
|
{
|
|
#define MAX_RENDERABLE 8
|
|
int renderable, i = 0;
|
|
const char *renderable_values[MAX_RENDERABLE] = { NULL, };
|
|
char *renderable_str = NULL;
|
|
|
|
if (!eglGetConfigAttrib (egl->egl_display, config, EGL_RENDERABLE_TYPE,
|
|
&renderable))
|
|
return;
|
|
|
|
if (renderable & EGL_OPENGL_BIT)
|
|
renderable_values[i++] = "OpenGL";
|
|
if (renderable & EGL_OPENGL_ES_BIT)
|
|
renderable_values[i++] = "OpenGL ES";
|
|
if (renderable & EGL_OPENGL_ES2_BIT)
|
|
renderable_values[i++] = "OpenGL ES 2.x";
|
|
#if defined(EGL_KHR_create_context)
|
|
if (renderable & EGL_OPENGL_ES3_BIT_KHR)
|
|
renderable_values[i++] = "OpenGL ES 3.x";
|
|
#endif
|
|
if (renderable & EGL_OPENVG_BIT)
|
|
renderable_values[i++] = "OpenVG";
|
|
|
|
/* bad things have happened if this fails: we haven't allocated enough
|
|
* space to hold all the values */
|
|
g_assert (i < MAX_RENDERABLE);
|
|
|
|
renderable_str = g_strjoinv ("|", (char **) renderable_values);
|
|
GST_DEBUG_OBJECT (egl, "Renderable for %s", renderable_str);
|
|
g_free (renderable_str);
|
|
#undef MAX_RENDERABLE
|
|
}
|
|
|
|
{
|
|
#define MAX_SURFACE 8
|
|
int surface, i = 0;
|
|
const char *surface_values[MAX_SURFACE] = { NULL, };
|
|
char *surface_str = NULL;
|
|
|
|
if (!eglGetConfigAttrib (egl->egl_display, config, EGL_SURFACE_TYPE,
|
|
&surface))
|
|
return;
|
|
|
|
if (surface & EGL_WINDOW_BIT)
|
|
surface_values[i++] = "window";
|
|
if (surface & EGL_PBUFFER_BIT)
|
|
surface_values[i++] = "pbuffer";
|
|
if (surface & EGL_MULTISAMPLE_RESOLVE_BOX_BIT)
|
|
surface_values[i++] = "multisample-resolve-box";
|
|
if (surface & EGL_SWAP_BEHAVIOR_PRESERVED_BIT)
|
|
surface_values[i++] = "swap-behaviour-preserved";
|
|
if (surface & EGL_VG_ALPHA_FORMAT_PRE_BIT)
|
|
surface_values[i++] = "vg-alpha-format-pre";
|
|
if (surface & EGL_VG_COLORSPACE_LINEAR_BIT)
|
|
surface_values[i++] = "vg-colorspace-linear";
|
|
|
|
/* bad things have happened if this fails: we haven't allocated enough
|
|
* space to hold all the values */
|
|
g_assert (i < MAX_SURFACE);
|
|
|
|
surface_str = g_strjoinv ("|", (char **) surface_values);
|
|
GST_DEBUG_OBJECT (egl, "Surface for (0x%x) %s", surface, surface_str);
|
|
g_free (surface_str);
|
|
#undef MAX_RENDERABLE
|
|
}
|
|
|
|
{
|
|
#define MAX_CAVEAT 8
|
|
int caveat, i = 0;
|
|
const char *caveat_values[MAX_CAVEAT] = { NULL, };
|
|
if (!eglGetConfigAttrib (egl->egl_display, config, EGL_CONFIG_CAVEAT,
|
|
&caveat))
|
|
return;
|
|
if (caveat == EGL_SLOW_CONFIG) {
|
|
caveat_values[i++] = "slow";
|
|
} else if (caveat == EGL_NON_CONFORMANT_CONFIG) {
|
|
caveat_values[i++] = "non-conformant";
|
|
}
|
|
if (i > 0) {
|
|
char *caveat_str = g_strjoinv ("|", (char **) caveat_values);
|
|
GST_DEBUG_OBJECT (egl, "Advertised as %s", caveat_str);
|
|
g_free (caveat_str);
|
|
}
|
|
#undef MAX_CAVEAT
|
|
}
|
|
|
|
if (!eglGetConfigAttrib (egl->egl_display, config, EGL_COLOR_BUFFER_TYPE,
|
|
&buffer_type))
|
|
return;
|
|
if (buffer_type == EGL_RGB_BUFFER) {
|
|
int red, blue, green, alpha;
|
|
|
|
if (!eglGetConfigAttrib (egl->egl_display, config, EGL_RED_SIZE, &red))
|
|
return;
|
|
if (!eglGetConfigAttrib (egl->egl_display, config, EGL_GREEN_SIZE, &green))
|
|
return;
|
|
if (!eglGetConfigAttrib (egl->egl_display, config, EGL_BLUE_SIZE, &blue))
|
|
return;
|
|
if (!eglGetConfigAttrib (egl->egl_display, config, EGL_ALPHA_SIZE, &alpha))
|
|
return;
|
|
|
|
GST_DEBUG_OBJECT (egl, "[R, G, B, A] = [%i, %i, %i, %i]", red, green, blue,
|
|
alpha);
|
|
} else if (buffer_type == EGL_LUMINANCE_BUFFER) {
|
|
int luminance, alpha;
|
|
if (!eglGetConfigAttrib (egl->egl_display, config, EGL_LUMINANCE_SIZE,
|
|
&luminance))
|
|
return;
|
|
if (!eglGetConfigAttrib (egl->egl_display, config, EGL_ALPHA_SIZE, &alpha))
|
|
return;
|
|
GST_DEBUG_OBJECT (egl, "[L, A] = [%i, %i]", luminance, alpha);
|
|
} else {
|
|
GST_WARNING_OBJECT (egl, "unknown EGL_COLOR_BUFFER_TYPE value %x",
|
|
buffer_type);
|
|
return;
|
|
}
|
|
{
|
|
int depth, stencil;
|
|
if (!eglGetConfigAttrib (egl->egl_display, config, EGL_DEPTH_SIZE, &depth))
|
|
return;
|
|
if (!eglGetConfigAttrib (egl->egl_display, config, EGL_STENCIL_SIZE,
|
|
&stencil))
|
|
return;
|
|
GST_DEBUG_OBJECT (egl, "[D, S] = [%i, %i]", depth, stencil);
|
|
}
|
|
{
|
|
int min, max;
|
|
|
|
if (!eglGetConfigAttrib (egl->egl_display, config, EGL_MIN_SWAP_INTERVAL,
|
|
&min))
|
|
return;
|
|
if (!eglGetConfigAttrib (egl->egl_display, config, EGL_MAX_SWAP_INTERVAL,
|
|
&max))
|
|
return;
|
|
GST_DEBUG_OBJECT (egl, "Swap interval range is [%i, %i]", min, max);
|
|
}
|
|
{
|
|
int width, height, pixels;
|
|
|
|
if (!eglGetConfigAttrib (egl->egl_display, config, EGL_MAX_PBUFFER_WIDTH,
|
|
&width))
|
|
return;
|
|
if (!eglGetConfigAttrib (egl->egl_display, config, EGL_MAX_PBUFFER_HEIGHT,
|
|
&height))
|
|
return;
|
|
if (!eglGetConfigAttrib (egl->egl_display, config, EGL_MAX_PBUFFER_PIXELS,
|
|
&pixels))
|
|
return;
|
|
GST_DEBUG_OBJECT (egl,
|
|
"PBuffer maximum dimensions are [%i, %i]. Max pixels are %i", width,
|
|
height, pixels);
|
|
}
|
|
{
|
|
int sample_buffers, samples_per_pixel;
|
|
|
|
if (!eglGetConfigAttrib (egl->egl_display, config, EGL_SAMPLE_BUFFERS,
|
|
&sample_buffers))
|
|
return;
|
|
if (!eglGetConfigAttrib (egl->egl_display, config, EGL_SAMPLES,
|
|
&samples_per_pixel))
|
|
return;
|
|
GST_DEBUG_OBJECT (egl, "Multisample buffers: %i and Samples per pixel: %i",
|
|
sample_buffers, samples_per_pixel);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_gl_context_egl_dump_all_configs (GstGLContextEGL * egl)
|
|
{
|
|
int i, n;
|
|
EGLConfig *configs;
|
|
|
|
if (!eglGetConfigs (egl->egl_display, NULL, 0, &n)) {
|
|
GST_WARNING_OBJECT (egl, "Failed to get number of EGLConfig's");
|
|
return;
|
|
}
|
|
|
|
configs = g_new0 (EGLConfig, n);
|
|
if (!eglGetConfigs (egl->egl_display, configs, n, &n)) {
|
|
GST_WARNING_OBJECT (egl, "Failed to get the list of EGLConfig's");
|
|
goto out;
|
|
}
|
|
|
|
for (i = 0; i < n; i++)
|
|
gst_gl_context_egl_dump_config (egl, configs[i]);
|
|
|
|
out:
|
|
g_free (configs);
|
|
}
|
|
|
|
static gboolean
|
|
gst_gl_context_egl_choose_config (GstGLContextEGL * egl, GstGLAPI gl_api,
|
|
gint major, GError ** error)
|
|
{
|
|
gboolean create_context;
|
|
EGLint numConfigs;
|
|
gint i, n;
|
|
EGLint config_attrib[20];
|
|
EGLint egl_api = 0;
|
|
EGLBoolean ret = EGL_FALSE;
|
|
EGLint surface_type = EGL_WINDOW_BIT;
|
|
GstGLWindow *window;
|
|
|
|
window = gst_gl_context_get_window (GST_GL_CONTEXT (egl));
|
|
|
|
if (!window || !gst_gl_window_has_output_surface (window)) {
|
|
GST_INFO_OBJECT (egl,
|
|
"gl window has no output surface, use pixel buffer surfaces");
|
|
surface_type = EGL_PBUFFER_BIT;
|
|
}
|
|
|
|
if (window)
|
|
gst_object_unref (window);
|
|
|
|
create_context =
|
|
gst_gl_check_extension ("EGL_KHR_create_context", egl->egl_exts);
|
|
/* silence unused warnings */
|
|
(void) create_context;
|
|
|
|
if (gl_api & GST_GL_API_GLES2) {
|
|
if (major == 3) {
|
|
#if defined(EGL_KHR_create_context)
|
|
if (create_context) {
|
|
egl_api = EGL_OPENGL_ES3_BIT_KHR;
|
|
} else
|
|
#endif
|
|
{
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
egl_api = EGL_OPENGL_ES2_BIT;
|
|
}
|
|
} else
|
|
egl_api = EGL_OPENGL_BIT;
|
|
|
|
try_again:
|
|
i = 0;
|
|
n = G_N_ELEMENTS (config_attrib);
|
|
config_attrib[i++] = EGL_SURFACE_TYPE;
|
|
config_attrib[i++] = surface_type;
|
|
config_attrib[i++] = EGL_RENDERABLE_TYPE;
|
|
config_attrib[i++] = egl_api;
|
|
|
|
if (egl->requested_config) {
|
|
#define TRANSFORM_VALUE(GL_CONF_NAME,EGL_ATTR_NAME) \
|
|
G_STMT_START { \
|
|
if (gst_structure_has_field_typed (egl->requested_config, \
|
|
GST_GL_CONFIG_ATTRIB_NAME(GL_CONF_NAME), \
|
|
GST_GL_CONFIG_ATTRIB_GTYPE(GL_CONF_NAME))) { \
|
|
int val; \
|
|
if (gst_structure_get (egl->requested_config, \
|
|
GST_GL_CONFIG_ATTRIB_NAME(GL_CONF_NAME), \
|
|
GST_GL_CONFIG_ATTRIB_GTYPE(GL_CONF_NAME), &val, NULL)) { \
|
|
config_attrib[i++] = EGL_ATTR_NAME; \
|
|
config_attrib[i++] = (int) val; \
|
|
g_assert (i <= n); \
|
|
} \
|
|
} \
|
|
} G_STMT_END
|
|
|
|
TRANSFORM_VALUE (CONFIG_ID, EGL_CONFIG_ID);
|
|
TRANSFORM_VALUE (RED_SIZE, EGL_RED_SIZE);
|
|
TRANSFORM_VALUE (GREEN_SIZE, EGL_GREEN_SIZE);
|
|
TRANSFORM_VALUE (BLUE_SIZE, EGL_BLUE_SIZE);
|
|
TRANSFORM_VALUE (ALPHA_SIZE, EGL_ALPHA_SIZE);
|
|
TRANSFORM_VALUE (DEPTH_SIZE, EGL_DEPTH_SIZE);
|
|
TRANSFORM_VALUE (STENCIL_SIZE, EGL_STENCIL_SIZE);
|
|
/* TODO: more values */
|
|
#undef TRANSFORM_VALUE
|
|
} else {
|
|
#if defined(USE_EGL_RPI) && GST_GL_HAVE_WINDOW_WAYLAND
|
|
/* The configurations with a=0 seems to be buggy whereas
|
|
* it works when using dispmanx directly */
|
|
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_RED_SIZE;
|
|
config_attrib[i++] = 1;
|
|
config_attrib[i++] = EGL_GREEN_SIZE;
|
|
config_attrib[i++] = 1;
|
|
config_attrib[i++] = EGL_BLUE_SIZE;
|
|
config_attrib[i++] = 1;
|
|
}
|
|
|
|
config_attrib[i++] = EGL_NONE;
|
|
g_assert (i <= n);
|
|
|
|
ret = eglChooseConfig (egl->egl_display, config_attrib,
|
|
&egl->egl_config, 1, &numConfigs);
|
|
|
|
if (ret && numConfigs == 0) {
|
|
if (surface_type == EGL_PBUFFER_BIT) {
|
|
surface_type = EGL_WINDOW_BIT;
|
|
GST_TRACE_OBJECT (egl, "Retrying config with window bit");
|
|
goto try_again;
|
|
}
|
|
}
|
|
|
|
if (ret && numConfigs == 1) {
|
|
GST_INFO ("config set: %" G_GUINTPTR_FORMAT ", %u",
|
|
(guintptr) egl->egl_config, (unsigned int) numConfigs);
|
|
} else {
|
|
if (!ret) {
|
|
g_set_error (error, GST_GL_CONTEXT_ERROR,
|
|
GST_GL_CONTEXT_ERROR_WRONG_CONFIG, "Failed to choose EGLConfig: %s",
|
|
gst_egl_get_error_string (eglGetError ()));
|
|
} else if (numConfigs <= 1) {
|
|
g_set_error_literal (error, GST_GL_CONTEXT_ERROR,
|
|
GST_GL_CONTEXT_ERROR_WRONG_CONFIG,
|
|
"Could not find a compatible EGLConfig:");
|
|
} else {
|
|
g_warn_if_reached ();
|
|
}
|
|
goto failure;
|
|
}
|
|
|
|
GST_DEBUG_OBJECT (egl, "chosen EGLConfig:");
|
|
gst_gl_context_egl_dump_config (egl, egl->egl_config);
|
|
|
|
return TRUE;
|
|
|
|
failure:
|
|
return FALSE;
|
|
}
|
|
|
|
static EGLContext
|
|
_create_context_with_flags (GstGLContextEGL * egl, EGLContext share_context,
|
|
GstGLAPI gl_api, gint major, gint minor, gint contextFlags,
|
|
gint profileMask)
|
|
{
|
|
gboolean create_context;
|
|
#define N_ATTRIBS 20
|
|
gint attribs[N_ATTRIBS];
|
|
gint n = 0;
|
|
|
|
/* fail creation of apis/versions/flags that require EGL_KHR_create_context
|
|
* if the extension doesn't exist, namely:0
|
|
*
|
|
* - profile mask
|
|
* - context flags
|
|
* - GL3 > 3.1
|
|
* - GLES2 && minor > 0
|
|
*/
|
|
create_context =
|
|
gst_gl_check_extension ("EGL_KHR_create_context", egl->egl_exts);
|
|
(void) create_context;
|
|
if (!create_context && (profileMask || contextFlags
|
|
|| ((gl_api & GST_GL_API_OPENGL3)
|
|
&& GST_GL_CHECK_GL_VERSION (major, minor, 3, 2))
|
|
|| ((gl_api & GST_GL_API_GLES2) && minor > 0))) {
|
|
return 0;
|
|
}
|
|
|
|
GST_DEBUG_OBJECT (egl, "attempting to create OpenGL%s context version %d.%d "
|
|
"flags %x profile %x", gl_api & GST_GL_API_GLES2 ? " ES" : "", major,
|
|
minor, contextFlags, profileMask);
|
|
|
|
#if defined(EGL_KHR_create_context)
|
|
if (create_context) {
|
|
if (major) {
|
|
attribs[n++] = EGL_CONTEXT_MAJOR_VERSION_KHR;
|
|
attribs[n++] = major;
|
|
}
|
|
if (minor) {
|
|
attribs[n++] = EGL_CONTEXT_MINOR_VERSION_KHR;
|
|
attribs[n++] = minor;
|
|
}
|
|
if (contextFlags) {
|
|
attribs[n++] = EGL_CONTEXT_FLAGS_KHR;
|
|
attribs[n++] = contextFlags;
|
|
}
|
|
if (profileMask) {
|
|
attribs[n++] = EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR;
|
|
attribs[n++] = profileMask;
|
|
}
|
|
} else
|
|
#endif
|
|
{
|
|
attribs[n++] = EGL_CONTEXT_CLIENT_VERSION;
|
|
attribs[n++] = major;
|
|
}
|
|
attribs[n++] = EGL_NONE;
|
|
|
|
g_assert (n < N_ATTRIBS);
|
|
#undef N_ATTRIBS
|
|
|
|
return eglCreateContext (egl->egl_display, egl->egl_config, share_context,
|
|
attribs);
|
|
}
|
|
|
|
static gboolean
|
|
gst_gl_context_egl_create_context (GstGLContext * context,
|
|
GstGLAPI gl_api, GstGLContext * other_context, GError ** error)
|
|
{
|
|
GstGLContextEGL *egl;
|
|
GstGLWindow *window = NULL;
|
|
guintptr window_handle = 0;
|
|
EGLint egl_major;
|
|
EGLint egl_minor;
|
|
gboolean need_surface = TRUE;
|
|
guintptr external_gl_context = 0;
|
|
guintptr egl_display;
|
|
|
|
egl = GST_GL_CONTEXT_EGL (context);
|
|
window = gst_gl_context_get_window (context);
|
|
|
|
GST_DEBUG_OBJECT (context, "Creating EGL 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_OPENGL3 | 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;
|
|
}
|
|
|
|
if (!egl->display_egl) {
|
|
GstGLDisplay *display = gst_gl_context_get_display (context);
|
|
|
|
egl->display_egl = gst_gl_display_egl_from_gl_display (display);
|
|
if (!egl->display_egl) {
|
|
g_set_error (error, GST_GL_CONTEXT_ERROR,
|
|
GST_GL_CONTEXT_ERROR_RESOURCE_UNAVAILABLE,
|
|
"Failed to create EGLDisplay from native display");
|
|
gst_object_unref (display);
|
|
goto failure;
|
|
}
|
|
|
|
gst_object_unref (display);
|
|
}
|
|
|
|
egl_display = gst_gl_display_get_handle (GST_GL_DISPLAY (egl->display_egl));
|
|
egl->egl_display = (EGLDisplay) egl_display;
|
|
|
|
if (eglInitialize (egl->egl_display, &egl_major, &egl_minor)) {
|
|
GST_INFO ("egl initialized, version: %d.%d", egl_major, egl_minor);
|
|
} else {
|
|
g_set_error (error, GST_GL_CONTEXT_ERROR,
|
|
GST_GL_CONTEXT_ERROR_RESOURCE_UNAVAILABLE,
|
|
"Failed to initialize egl: %s",
|
|
gst_egl_get_error_string (eglGetError ()));
|
|
goto failure;
|
|
}
|
|
|
|
egl->egl_exts = eglQueryString (egl->egl_display, EGL_EXTENSIONS);
|
|
GST_DEBUG_OBJECT (egl, "Have EGL extensions: %s", egl->egl_exts);
|
|
|
|
gst_gl_context_egl_dump_all_configs (egl);
|
|
|
|
if (gl_api & (GST_GL_API_OPENGL | GST_GL_API_OPENGL3)) {
|
|
GstGLAPI chosen_gl_api = 0;
|
|
gint i;
|
|
|
|
/* egl + opengl only available with EGL 1.4+ */
|
|
if (egl_major == 1 && egl_minor <= 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)",
|
|
egl_major, egl_minor);
|
|
goto failure;
|
|
} else {
|
|
GST_WARNING
|
|
("EGL version (%i.%i) too old for OpenGL support, (needed at least 1.4)",
|
|
egl_major, egl_minor);
|
|
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_egl_get_error_string (eglGetError ()));
|
|
goto failure;
|
|
}
|
|
|
|
GST_INFO ("Bound OpenGL");
|
|
|
|
/* api, version only matters for gles */
|
|
if (!gst_gl_context_egl_choose_config (egl, GST_GL_API_OPENGL, 0, error)) {
|
|
g_assert (error == NULL || *error != NULL);
|
|
goto failure;
|
|
}
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (opengl_versions); i++) {
|
|
gint profileMask = 0;
|
|
gint contextFlags = 0;
|
|
|
|
if (GST_GL_CHECK_GL_VERSION (opengl_versions[i].major,
|
|
opengl_versions[i].minor, 3, 2)) {
|
|
/* skip gl3 contexts if requested */
|
|
if ((gl_api & GST_GL_API_OPENGL3) == 0)
|
|
continue;
|
|
|
|
chosen_gl_api = GST_GL_API_OPENGL3;
|
|
#if defined(EGL_KHR_create_context)
|
|
profileMask |= EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR;
|
|
contextFlags |= EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR;
|
|
#endif
|
|
} else if (opengl_versions[i].major == 3 && opengl_versions[i].minor == 1) {
|
|
/* skip 3.1, the implementation is free to give us either a core or a
|
|
* compatibility context (we have no say) */
|
|
continue;
|
|
} else {
|
|
/* skip legacy contexts if requested */
|
|
if ((gl_api & GST_GL_API_OPENGL) == 0)
|
|
continue;
|
|
|
|
chosen_gl_api = GST_GL_API_OPENGL;
|
|
}
|
|
|
|
egl->egl_context =
|
|
_create_context_with_flags (egl, (EGLContext) external_gl_context,
|
|
chosen_gl_api, opengl_versions[i].major,
|
|
opengl_versions[i].minor, contextFlags, profileMask);
|
|
|
|
if (egl->egl_context)
|
|
break;
|
|
|
|
#if defined(EGL_KHR_create_context)
|
|
profileMask &= ~EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR;
|
|
|
|
egl->egl_context =
|
|
_create_context_with_flags (egl, (EGLContext) external_gl_context,
|
|
chosen_gl_api, opengl_versions[i].major,
|
|
opengl_versions[i].minor, contextFlags, profileMask);
|
|
|
|
if (egl->egl_context)
|
|
break;
|
|
#endif
|
|
}
|
|
|
|
egl->gl_api = chosen_gl_api;
|
|
} else if (gl_api & GST_GL_API_GLES2) {
|
|
gint i;
|
|
|
|
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_egl_get_error_string (eglGetError ()));
|
|
goto failure;
|
|
}
|
|
|
|
GST_INFO ("Bound OpenGL|ES");
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (gles2_versions); i++) {
|
|
gint profileMask = 0;
|
|
gint contextFlags = 0;
|
|
guint maj = gles2_versions[i].major;
|
|
guint min = gles2_versions[i].minor;
|
|
|
|
if (!gst_gl_context_egl_choose_config (egl, GST_GL_API_GLES2, maj, error)) {
|
|
GST_DEBUG_OBJECT (context, "Failed to choose a GLES%d config: %s",
|
|
maj, error && *error ? (*error)->message : "Unknown");
|
|
g_clear_error (error);
|
|
continue;
|
|
}
|
|
#if defined(EGL_KHR_create_context)
|
|
/* try a debug context */
|
|
contextFlags |= EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR;
|
|
|
|
egl->egl_context =
|
|
_create_context_with_flags (egl, (EGLContext) external_gl_context,
|
|
GST_GL_API_GLES2, maj, min, contextFlags, profileMask);
|
|
|
|
if (egl->egl_context)
|
|
break;
|
|
|
|
/* try without a debug context */
|
|
contextFlags &= ~EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR;
|
|
#endif
|
|
|
|
egl->egl_context =
|
|
_create_context_with_flags (egl, (EGLContext) external_gl_context,
|
|
GST_GL_API_GLES2, maj, min, contextFlags, profileMask);
|
|
|
|
if (egl->egl_context)
|
|
break;
|
|
}
|
|
egl->gl_api = GST_GL_API_GLES2;
|
|
}
|
|
|
|
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_egl_get_error_string (eglGetError ()));
|
|
goto failure;
|
|
}
|
|
/* FIXME do we want a window vfunc ? */
|
|
#if GST_GL_HAVE_WINDOW_X11
|
|
if (GST_IS_GL_WINDOW_X11 (context->window)) {
|
|
gst_gl_window_x11_create_window ((GstGLWindowX11 *) context->window);
|
|
}
|
|
#endif
|
|
|
|
if (other_context == NULL) {
|
|
/* FIXME: fails to show two outputs at all. We need a property/option for
|
|
* glimagesink to say its a visible context */
|
|
#if GST_GL_HAVE_WINDOW_WAYLAND
|
|
if (GST_IS_GL_WINDOW_WAYLAND_EGL (context->window)) {
|
|
gst_gl_window_wayland_egl_create_window ((GstGLWindowWaylandEGL *)
|
|
context->window);
|
|
}
|
|
#endif
|
|
#if GST_GL_HAVE_WINDOW_WIN32
|
|
if (GST_IS_GL_WINDOW_WIN32 (context->window)) {
|
|
gst_gl_window_win32_create_window ((GstGLWindowWin32 *) context->window,
|
|
NULL);
|
|
}
|
|
#endif
|
|
#if GST_GL_HAVE_WINDOW_DISPMANX
|
|
if (GST_IS_GL_WINDOW_DISPMANX_EGL (context->window)) {
|
|
gst_gl_window_dispmanx_egl_create_window ((GstGLWindowDispmanxEGL *)
|
|
context->window);
|
|
}
|
|
#endif
|
|
#if GST_GL_HAVE_WINDOW_GBM
|
|
if (GST_IS_GL_WINDOW_GBM_EGL (context->window)) {
|
|
gst_gl_window_gbm_egl_create_window ((GstGLWindowGBMEGL *)
|
|
context->window);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (window)
|
|
window_handle = gst_gl_window_get_window_handle (window);
|
|
|
|
if (window_handle) {
|
|
#if GST_GL_HAVE_WINDOW_WINRT && defined (EGL_ANGLE_SURFACE_RENDER_TO_BACK_BUFFER)
|
|
const EGLint attrs[] = {
|
|
/* EGL_ANGLE_SURFACE_RENDER_TO_BACK_BUFFER is an optimization that can
|
|
* have large performance benefits on mobile devices. */
|
|
EGL_ANGLE_SURFACE_RENDER_TO_BACK_BUFFER, EGL_TRUE,
|
|
EGL_NONE
|
|
};
|
|
#else
|
|
const EGLint *attrs = NULL;
|
|
#endif
|
|
|
|
GST_DEBUG ("Creating EGLSurface from window_handle %p",
|
|
(void *) window_handle);
|
|
egl->egl_surface =
|
|
eglCreateWindowSurface (egl->egl_display, egl->egl_config,
|
|
(EGLNativeWindowType) window_handle, attrs);
|
|
/* Store window handle for later comparison */
|
|
egl->window_handle = window_handle;
|
|
} else if (!gst_gl_check_extension ("EGL_KHR_surfaceless_context",
|
|
egl->egl_exts)) {
|
|
EGLint surface_attrib[7];
|
|
gint j = 0;
|
|
|
|
GST_DEBUG ("Surfaceless context, creating PBufferSurface");
|
|
/* 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 {
|
|
GST_DEBUG ("No surface/handle !");
|
|
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_egl_get_error_string (eglGetError ()));
|
|
goto failure;
|
|
}
|
|
}
|
|
egl->egl_major = egl_major;
|
|
egl->egl_minor = egl_minor;
|
|
|
|
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_display, egl->egl_surface);
|
|
egl->egl_surface = EGL_NO_SURFACE;
|
|
}
|
|
|
|
if (egl->egl_context) {
|
|
eglDestroyContext (egl->egl_display, egl->egl_context);
|
|
egl->egl_context = NULL;
|
|
}
|
|
egl->window_handle = 0;
|
|
|
|
eglReleaseThread ();
|
|
|
|
if (egl->display_egl) {
|
|
gst_object_unref (egl->display_egl);
|
|
egl->display_egl = NULL;
|
|
}
|
|
|
|
if (egl->requested_config)
|
|
gst_structure_free (egl->requested_config);
|
|
egl->requested_config = NULL;
|
|
}
|
|
|
|
static gboolean
|
|
gst_gl_context_egl_activate (GstGLContext * context, gboolean activate)
|
|
{
|
|
GstGLContextEGL *egl;
|
|
gboolean result;
|
|
|
|
egl = GST_GL_CONTEXT_EGL (context);
|
|
|
|
if (activate) {
|
|
GstGLWindow *window = gst_gl_context_get_window (context);
|
|
guintptr handle = 0;
|
|
/* Check if the backing handle changed */
|
|
if (window) {
|
|
handle = gst_gl_window_get_window_handle (window);
|
|
gst_object_unref (window);
|
|
}
|
|
if (handle && handle != egl->window_handle) {
|
|
#if GST_GL_HAVE_WINDOW_WINRT && defined (EGL_ANGLE_SURFACE_RENDER_TO_BACK_BUFFER)
|
|
const EGLint attrs[] = {
|
|
/* EGL_ANGLE_SURFACE_RENDER_TO_BACK_BUFFER is an optimization that can
|
|
* have large performance benefits on mobile devices. */
|
|
EGL_ANGLE_SURFACE_RENDER_TO_BACK_BUFFER, EGL_TRUE,
|
|
EGL_NONE
|
|
};
|
|
#else
|
|
const EGLint *attrs = NULL;
|
|
#endif
|
|
GST_DEBUG_OBJECT (context,
|
|
"Handle changed (have:%p, now:%p), switching surface",
|
|
(void *) egl->window_handle, (void *) handle);
|
|
if (egl->egl_surface) {
|
|
result = eglDestroySurface (egl->egl_display, egl->egl_surface);
|
|
egl->egl_surface = EGL_NO_SURFACE;
|
|
if (!result) {
|
|
GST_ERROR_OBJECT (context, "Failed to destroy old window surface: %s",
|
|
gst_egl_get_error_string (eglGetError ()));
|
|
goto done;
|
|
}
|
|
}
|
|
egl->egl_surface =
|
|
eglCreateWindowSurface (egl->egl_display, egl->egl_config,
|
|
(EGLNativeWindowType) handle, attrs);
|
|
egl->window_handle = handle;
|
|
|
|
if (egl->egl_surface == EGL_NO_SURFACE) {
|
|
GST_ERROR_OBJECT (context, "Failed to create window surface: %s",
|
|
gst_egl_get_error_string (eglGetError ()));
|
|
result = FALSE;
|
|
goto done;
|
|
}
|
|
}
|
|
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);
|
|
}
|
|
|
|
if (!result) {
|
|
GST_ERROR_OBJECT (context,
|
|
"Failed to bind context to the current rendering thread: %s",
|
|
gst_egl_get_error_string (eglGetError ()));
|
|
}
|
|
|
|
done:
|
|
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
|
|
/* On Linux the .so is only in -dev packages, try with a real soname
|
|
* Proper compilers will optimize away the strcmp */
|
|
if (g_strcmp0 (G_MODULE_SUFFIX, "so") == 0)
|
|
module_egl = g_module_open ("libEGL.so.1", G_MODULE_BIND_LAZY);
|
|
|
|
/* This automatically handles the suffix and even .la files */
|
|
if (!module_egl)
|
|
module_egl = g_module_open ("libEGL", G_MODULE_BIND_LAZY);
|
|
#endif
|
|
|
|
return NULL;
|
|
}
|
|
|
|
gpointer
|
|
gst_gl_context_egl_get_proc_address (GstGLAPI gl_api, const gchar * name)
|
|
{
|
|
gpointer result = NULL;
|
|
static GOnce g_once = G_ONCE_INIT;
|
|
|
|
#ifdef __APPLE__
|
|
#if GST_GL_HAVE_OPENGL && !defined(GST_GL_LIBGL_MODULE_NAME)
|
|
if (!result && (gl_api & (GST_GL_API_OPENGL | GST_GL_API_OPENGL3))) {
|
|
static GModule *module_opengl = NULL;
|
|
if (g_once_init_enter (&module_opengl)) {
|
|
GModule *setup_module_opengl =
|
|
g_module_open ("libGL.dylib", G_MODULE_BIND_LAZY);
|
|
g_once_init_leave (&module_opengl, setup_module_opengl);
|
|
}
|
|
if (module_opengl)
|
|
g_module_symbol (module_opengl, name, &result);
|
|
}
|
|
#endif
|
|
#if GST_GL_HAVE_GLES2 && !defined(GST_GL_LIBGLESV2_MODULE_NAME)
|
|
if (!result && (gl_api & (GST_GL_API_GLES2))) {
|
|
static GModule *module_gles2 = NULL;
|
|
if (g_once_init_enter (&module_gles2)) {
|
|
GModule *setup_module_gles2 =
|
|
g_module_open ("libGLESv2.dylib", G_MODULE_BIND_LAZY);
|
|
g_once_init_leave (&module_gles2, setup_module_gles2);
|
|
}
|
|
if (module_gles2)
|
|
g_module_symbol (module_gles2, name, &result);
|
|
}
|
|
#endif
|
|
#endif // __APPLE__
|
|
|
|
if (!result)
|
|
result = gst_gl_context_default_get_proc_address (gl_api, 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;
|
|
}
|
|
|
|
static gboolean
|
|
gst_gl_context_egl_check_feature (GstGLContext * context, const gchar * feature)
|
|
{
|
|
GstGLContextEGL *context_egl = GST_GL_CONTEXT_EGL (context);
|
|
|
|
return gst_gl_check_extension (feature, context_egl->egl_exts);
|
|
}
|
|
|
|
guintptr
|
|
gst_gl_context_egl_get_current_context (void)
|
|
{
|
|
return (guintptr) eglGetCurrentContext ();
|
|
}
|
|
|
|
static void
|
|
gst_gl_context_egl_get_gl_platform_version (GstGLContext * context,
|
|
gint * major, gint * minor)
|
|
{
|
|
GstGLContextEGL *context_egl = GST_GL_CONTEXT_EGL (context);
|
|
|
|
*major = context_egl->egl_major;
|
|
*minor = context_egl->egl_minor;
|
|
}
|
|
|
|
static GstStructure *
|
|
gst_gl_context_egl_get_config (GstGLContext * context)
|
|
{
|
|
GstGLContextEGL *egl = GST_GL_CONTEXT_EGL (context);
|
|
|
|
g_return_val_if_fail (egl->egl_config, NULL);
|
|
|
|
return egl_config_to_structure (egl->egl_display, egl->egl_config);
|
|
}
|
|
|
|
static gboolean
|
|
gst_gl_context_egl_request_config (GstGLContext * context,
|
|
GstStructure * config)
|
|
{
|
|
GstGLContextEGL *egl = GST_GL_CONTEXT_EGL (context);
|
|
|
|
if (egl->requested_config)
|
|
gst_structure_free (egl->requested_config);
|
|
egl->requested_config = config;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gst_gl_context_egl_fill_info (GstGLContext * context)
|
|
{
|
|
EGLContext egl_context = (EGLContext) gst_gl_context_get_gl_context (context);
|
|
GstGLDisplay *display_egl;
|
|
GstStructure *config;
|
|
EGLDisplay *egl_display;
|
|
EGLConfig egl_config;
|
|
int config_id, n_configs;
|
|
int attrs[3];
|
|
|
|
if (!egl_context) {
|
|
GST_ERROR_OBJECT (context, "no GLX context");
|
|
return FALSE;
|
|
}
|
|
|
|
display_egl =
|
|
GST_GL_DISPLAY (gst_gl_display_egl_from_gl_display (context->display));
|
|
egl_display = (EGLDisplay) gst_gl_display_get_handle (display_egl);
|
|
|
|
if (EGL_TRUE != eglQueryContext (egl_display, egl_context, EGL_CONFIG_ID,
|
|
&config_id)) {
|
|
GST_WARNING_OBJECT (context,
|
|
"could not retrieve egl config id from egl context: %s",
|
|
gst_egl_get_error_string (eglGetError ()));
|
|
goto failure;
|
|
}
|
|
|
|
attrs[0] = EGL_CONFIG_ID;
|
|
attrs[1] = config_id;
|
|
attrs[2] = EGL_NONE;
|
|
|
|
if (EGL_TRUE != eglChooseConfig (egl_display, attrs, &egl_config, 1,
|
|
&n_configs) || n_configs <= 0) {
|
|
GST_WARNING_OBJECT (context,
|
|
"could not retrieve egl config from its ID 0x%x. "
|
|
"Wrong EGLDisplay or context?", config_id);
|
|
goto failure;
|
|
}
|
|
|
|
config = egl_config_to_structure (egl_display, egl_config);
|
|
if (!config) {
|
|
GST_WARNING_OBJECT (context, "could not transform config id 0x%x into "
|
|
"GstStructure", config_id);
|
|
goto failure;
|
|
}
|
|
|
|
GST_INFO_OBJECT (context, "found config %" GST_PTR_FORMAT, config);
|
|
|
|
g_object_set_data_full (G_OBJECT (context),
|
|
GST_GL_CONTEXT_WRAPPED_GL_CONFIG_NAME, config,
|
|
(GDestroyNotify) gst_structure_free);
|
|
|
|
gst_object_unref (display_egl);
|
|
return TRUE;
|
|
|
|
failure:
|
|
gst_object_unref (display_egl);
|
|
return FALSE;
|
|
}
|