mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-04 23:46:43 +00:00
e1ca575408
Use of the EGL_MESA_platform_surfaceless EGL extension to create an EGL display that is not depending on any kind of windowing system. Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5511>
422 lines
12 KiB
C
422 lines
12 KiB
C
/*
|
|
* GStreamer
|
|
* Copyright (C) 2014 Matthew Waters <ystreet00@gmail.com>
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
/**
|
|
* SECTION:gstgldisplay_egl
|
|
* @short_description: EGL Display connection
|
|
* @title: GstGLDisplayEGL
|
|
* @see_also: #GstGLDisplay
|
|
*
|
|
* #GstGLDisplayEGL represents a connection to an EGL `EGLDisplay` handle created
|
|
* internally (gst_gl_display_egl_new() or gst_gl_display_egl_new_surfaceless())
|
|
* or wrapped by the application (gst_gl_display_egl_new_with_egl_display())
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "gstgldisplay_egl.h"
|
|
|
|
#include <gst/gl/gstglfeature.h>
|
|
|
|
#include "gstegl.h"
|
|
#include "gsteglimage.h"
|
|
#include "gstglmemoryegl.h"
|
|
|
|
GST_DEBUG_CATEGORY_STATIC (gst_gl_display_egl_debug);
|
|
#define GST_CAT_DEFAULT gst_gl_display_egl_debug
|
|
|
|
#ifndef EGL_PLATFORM_X11
|
|
#define EGL_PLATFORM_X11 0x31D5
|
|
#endif
|
|
#ifndef EGL_PLATFORM_WAYLAND
|
|
#define EGL_PLATFORM_WAYLAND 0x31D8
|
|
#endif
|
|
#ifndef EGL_PLATFORM_GBM_MESA
|
|
#define EGL_PLATFORM_GBM_MESA 0x31D7
|
|
#endif
|
|
#ifndef EGL_PLATFORM_ANDROID
|
|
#define EGL_PLATFORM_ANDROID 0x3141
|
|
#endif
|
|
#ifndef EGL_PLATFORM_DEVICE_EXT
|
|
#define EGL_PLATFORM_DEVICE_EXT 0x313F
|
|
#endif
|
|
#ifndef EGL_PLATFORM_ANGLE_ANGLE
|
|
#define EGL_PLATFORM_ANGLE_ANGLE 0x3202
|
|
#endif
|
|
|
|
typedef EGLDisplay (*_gst_eglGetPlatformDisplay_type) (EGLenum platform,
|
|
void *native_display, const EGLint * attrib_list);
|
|
|
|
G_DEFINE_TYPE (GstGLDisplayEGL, gst_gl_display_egl, GST_TYPE_GL_DISPLAY);
|
|
|
|
static void gst_gl_display_egl_finalize (GObject * object);
|
|
static guintptr gst_gl_display_egl_get_handle (GstGLDisplay * display);
|
|
|
|
static void
|
|
init_debug (void)
|
|
{
|
|
static gsize _init = 0;
|
|
|
|
if (g_once_init_enter (&_init)) {
|
|
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "gldisplayegl", 0,
|
|
"OpenGL EGL Display");
|
|
g_once_init_leave (&_init, 1);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_gl_display_egl_class_init (GstGLDisplayEGLClass * klass)
|
|
{
|
|
GST_GL_DISPLAY_CLASS (klass)->get_handle =
|
|
GST_DEBUG_FUNCPTR (gst_gl_display_egl_get_handle);
|
|
|
|
G_OBJECT_CLASS (klass)->finalize = gst_gl_display_egl_finalize;
|
|
}
|
|
|
|
static void
|
|
gst_gl_display_egl_init (GstGLDisplayEGL * display_egl)
|
|
{
|
|
GstGLDisplay *display = (GstGLDisplay *) display_egl;
|
|
|
|
display->type = GST_GL_DISPLAY_TYPE_EGL;
|
|
display_egl->foreign_display = FALSE;
|
|
|
|
gst_gl_memory_egl_init_once ();
|
|
}
|
|
|
|
static void
|
|
gst_gl_display_egl_finalize (GObject * object)
|
|
{
|
|
GstGLDisplayEGL *display_egl = GST_GL_DISPLAY_EGL (object);
|
|
|
|
if (display_egl->display && !display_egl->foreign_display) {
|
|
eglTerminate (display_egl->display);
|
|
display_egl->display = NULL;
|
|
}
|
|
|
|
G_OBJECT_CLASS (gst_gl_display_egl_parent_class)->finalize (object);
|
|
}
|
|
|
|
/**
|
|
* gst_gl_display_egl_get_from_native:
|
|
* @type: a #GstGLDisplayType
|
|
* @display: pointer to a display (or 0)
|
|
*
|
|
* Attempts to create a new `EGLDisplay` from @display. If @type is
|
|
* %GST_GL_DISPLAY_TYPE_ANY or %GST_GL_DISPLAY_TYPE_EGL_SURFACELESS, then
|
|
* @display must be 0. @type must not be %GST_GL_DISPLAY_TYPE_NONE.
|
|
*
|
|
* Returns: (nullable): A `EGLDisplay` or `EGL_NO_DISPLAY`
|
|
*
|
|
* Since: 1.12
|
|
*/
|
|
gpointer
|
|
gst_gl_display_egl_get_from_native (GstGLDisplayType type, guintptr display)
|
|
{
|
|
const gchar *egl_exts;
|
|
EGLDisplay ret = EGL_NO_DISPLAY;
|
|
_gst_eglGetPlatformDisplay_type _gst_eglGetPlatformDisplay = NULL;
|
|
|
|
g_return_val_if_fail (type != GST_GL_DISPLAY_TYPE_NONE, EGL_NO_DISPLAY);
|
|
g_return_val_if_fail ((type != GST_GL_DISPLAY_TYPE_ANY &&
|
|
type != GST_GL_DISPLAY_TYPE_EGL_SURFACELESS && display != 0)
|
|
|| ((type == GST_GL_DISPLAY_TYPE_ANY ||
|
|
type == GST_GL_DISPLAY_TYPE_EGL_SURFACELESS) && display == 0),
|
|
EGL_NO_DISPLAY);
|
|
|
|
init_debug ();
|
|
|
|
/* given an EGLDisplay already */
|
|
if (type == GST_GL_DISPLAY_TYPE_EGL)
|
|
return (gpointer) display;
|
|
|
|
egl_exts = eglQueryString (EGL_NO_DISPLAY, EGL_EXTENSIONS);
|
|
GST_DEBUG ("egl no display extensions: %s", egl_exts);
|
|
|
|
if (eglGetError () != EGL_SUCCESS || !egl_exts)
|
|
goto default_display;
|
|
|
|
/* check if we can actually choose the egl display type */
|
|
if (!gst_gl_check_extension ("EGL_KHR_client_get_all_proc_addresses",
|
|
egl_exts))
|
|
goto default_display;
|
|
if (!gst_gl_check_extension ("EGL_EXT_platform_base", egl_exts))
|
|
goto default_display;
|
|
|
|
/* we need EXT for WinRT to pass attributes */
|
|
#if !GST_GL_HAVE_WINDOW_WINRT
|
|
_gst_eglGetPlatformDisplay = (_gst_eglGetPlatformDisplay_type)
|
|
eglGetProcAddress ("eglGetPlatformDisplay");
|
|
#endif
|
|
if (!_gst_eglGetPlatformDisplay)
|
|
_gst_eglGetPlatformDisplay = (_gst_eglGetPlatformDisplay_type)
|
|
eglGetProcAddress ("eglGetPlatformDisplayEXT");
|
|
if (!_gst_eglGetPlatformDisplay)
|
|
goto default_display;
|
|
|
|
/* try each platform in turn */
|
|
#if GST_GL_HAVE_WINDOW_X11
|
|
if (ret == EGL_NO_DISPLAY && (type & GST_GL_DISPLAY_TYPE_X11) &&
|
|
(gst_gl_check_extension ("EGL_KHR_platform_x11", egl_exts) ||
|
|
gst_gl_check_extension ("EGL_EXT_platform_x11", egl_exts))) {
|
|
ret = _gst_eglGetPlatformDisplay (EGL_PLATFORM_X11, (gpointer) display,
|
|
NULL);
|
|
}
|
|
#endif
|
|
#if GST_GL_HAVE_WINDOW_WAYLAND
|
|
if (ret == EGL_NO_DISPLAY && (type & GST_GL_DISPLAY_TYPE_WAYLAND) &&
|
|
(gst_gl_check_extension ("EGL_KHR_platform_wayland", egl_exts) ||
|
|
gst_gl_check_extension ("EGL_EXT_platform_wayland", egl_exts))) {
|
|
ret = _gst_eglGetPlatformDisplay (EGL_PLATFORM_WAYLAND, (gpointer) display,
|
|
NULL);
|
|
}
|
|
#endif
|
|
#if GST_GL_HAVE_WINDOW_GBM
|
|
if (ret == EGL_NO_DISPLAY && (type & GST_GL_DISPLAY_TYPE_GBM) &&
|
|
(gst_gl_check_extension ("EGL_MESA_platform_gbm", egl_exts) ||
|
|
gst_gl_check_extension ("EGL_KHR_platform_gbm", egl_exts))) {
|
|
ret = _gst_eglGetPlatformDisplay (EGL_PLATFORM_GBM_MESA, (gpointer) display,
|
|
NULL);
|
|
}
|
|
#endif
|
|
#if GST_GL_HAVE_WINDOW_WINRT
|
|
if (ret == EGL_NO_DISPLAY && (type & GST_GL_DISPLAY_TYPE_EGL) &&
|
|
(gst_gl_check_extension ("EGL_ANGLE_platform_angle", egl_exts) ||
|
|
gst_gl_check_extension ("EGL_ANGLE_platform_angle", egl_exts))) {
|
|
const EGLint attrs[] = {
|
|
/* These are the default display attributes, used to request ANGLE's
|
|
* D3D11 renderer. eglInitialize will only succeed with these
|
|
* attributes if the hardware supports D3D11 Feature Level 10_0+. */
|
|
EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
|
|
|
|
#ifdef EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER
|
|
/* EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER is an optimization
|
|
* that can have large performance benefits on mobile devices. Its
|
|
* syntax is subject to change, though. Please update your Visual
|
|
* Studio templates if you experience compilation issues with it. */
|
|
EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER, EGL_TRUE,
|
|
#endif
|
|
|
|
/* EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE is an option that
|
|
* enables ANGLE to automatically call the IDXGIDevice3::Trim method
|
|
* on behalf of the application when it gets suspended. Calling
|
|
* IDXGIDevice3::Trim when an application is suspended is a Windows
|
|
* Store application certification requirement. */
|
|
EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE, EGL_TRUE,
|
|
EGL_NONE,
|
|
};
|
|
ret = _gst_eglGetPlatformDisplay (EGL_PLATFORM_ANGLE_ANGLE,
|
|
(gpointer) display, attrs);
|
|
}
|
|
#endif
|
|
if (ret == EGL_NO_DISPLAY && (type & GST_GL_DISPLAY_TYPE_EGL_DEVICE) &&
|
|
(gst_gl_check_extension ("EGL_EXT_device_base", egl_exts) &&
|
|
gst_gl_check_extension ("EGL_EXT_platform_device", egl_exts))) {
|
|
ret =
|
|
_gst_eglGetPlatformDisplay (EGL_PLATFORM_DEVICE_EXT, (gpointer) display,
|
|
NULL);
|
|
}
|
|
/* android only has one winsys/display connection */
|
|
if (ret == EGL_NO_DISPLAY && (type & GST_GL_DISPLAY_TYPE_EGL_SURFACELESS) &&
|
|
gst_gl_check_extension ("EGL_MESA_platform_surfaceless", egl_exts)) {
|
|
ret = _gst_eglGetPlatformDisplay (EGL_PLATFORM_SURFACELESS_MESA,
|
|
(gpointer) display, NULL);
|
|
}
|
|
|
|
if (ret != EGL_NO_DISPLAY)
|
|
return ret;
|
|
|
|
/* otherwise rely on the implementation to choose the correct display
|
|
* based on the pointer */
|
|
default_display:
|
|
return (gpointer) eglGetDisplay ((EGLNativeDisplayType) display);
|
|
}
|
|
|
|
/**
|
|
* gst_gl_display_egl_new:
|
|
*
|
|
* Create a new #GstGLDisplayEGL using the default EGL_DEFAULT_DISPLAY.
|
|
*
|
|
* Returns: (transfer full) (nullable): a new #GstGLDisplayEGL or %NULL
|
|
*/
|
|
GstGLDisplayEGL *
|
|
gst_gl_display_egl_new (void)
|
|
{
|
|
GstGLDisplayEGL *ret;
|
|
gpointer display;
|
|
|
|
init_debug ();
|
|
|
|
display = gst_gl_display_egl_get_from_native (GST_GL_DISPLAY_TYPE_ANY, 0);
|
|
|
|
if (!display) {
|
|
GST_INFO ("Failed to open EGL display connection");
|
|
return NULL;
|
|
}
|
|
|
|
ret = g_object_new (GST_TYPE_GL_DISPLAY_EGL, NULL);
|
|
gst_object_ref_sink (ret);
|
|
ret->display = display;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* gst_gl_display_egl_new_surfaceless:
|
|
*
|
|
* Create a new surfaceless #GstGLDisplayEGL using the Mesa3D
|
|
* EGL_PLATFORM_SURFACELESS_MESA extension.
|
|
*
|
|
* Returns: (transfer full) (nullable): a new #GstGLDisplayEGL or %NULL
|
|
*
|
|
* Since: 1.24
|
|
*/
|
|
GstGLDisplayEGL *
|
|
gst_gl_display_egl_new_surfaceless (void)
|
|
{
|
|
GstGLDisplayEGL *ret;
|
|
gpointer display;
|
|
|
|
init_debug ();
|
|
|
|
display =
|
|
gst_gl_display_egl_get_from_native (GST_GL_DISPLAY_TYPE_EGL_SURFACELESS,
|
|
0);
|
|
|
|
if (!display) {
|
|
GST_INFO ("Failed to create a surfaceless EGL display");
|
|
return NULL;
|
|
}
|
|
|
|
ret = g_object_new (GST_TYPE_GL_DISPLAY_EGL, NULL);
|
|
gst_object_ref_sink (ret);
|
|
ret->display = display;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* gst_gl_display_egl_new_with_display:
|
|
* @display: an existing and connected EGLDisplay
|
|
*
|
|
* Creates a new display connection from a EGLDisplay.
|
|
*
|
|
* Returns: (transfer full): a new #GstGLDisplayEGL
|
|
*
|
|
* Since: 1.12
|
|
*/
|
|
GstGLDisplayEGL *
|
|
gst_gl_display_egl_new_with_egl_display (gpointer display)
|
|
{
|
|
GstGLDisplayEGL *ret;
|
|
|
|
g_return_val_if_fail (display != NULL, NULL);
|
|
|
|
init_debug ();
|
|
|
|
ret = g_object_new (GST_TYPE_GL_DISPLAY_EGL, NULL);
|
|
gst_object_ref_sink (ret);
|
|
|
|
ret->display = display;
|
|
ret->foreign_display = TRUE;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static gpointer
|
|
_ref_if_set (gpointer data, gpointer user_data)
|
|
{
|
|
if (data)
|
|
gst_object_ref (data);
|
|
return data;
|
|
}
|
|
|
|
/**
|
|
* gst_gl_display_egl_from_gl_display:
|
|
* @display: (transfer none): an existing #GstGLDisplay
|
|
*
|
|
* Creates a EGL display connection from a native Display.
|
|
*
|
|
* This function will return the same value for multiple calls with the same
|
|
* @display.
|
|
*
|
|
* Returns: (transfer full) (nullable): a new #GstGLDisplayEGL
|
|
*
|
|
* Since: 1.12
|
|
*/
|
|
GstGLDisplayEGL *
|
|
gst_gl_display_egl_from_gl_display (GstGLDisplay * display)
|
|
{
|
|
GstGLDisplayEGL *ret;
|
|
GstGLDisplayType display_type;
|
|
guintptr native_display;
|
|
|
|
g_return_val_if_fail (GST_IS_GL_DISPLAY (display), NULL);
|
|
|
|
init_debug ();
|
|
|
|
if (GST_IS_GL_DISPLAY_EGL (display)) {
|
|
GST_LOG_OBJECT (display, "display %" GST_PTR_FORMAT "is already a "
|
|
"GstGLDisplayEGL", display);
|
|
return gst_object_ref (display);
|
|
}
|
|
|
|
/* try to get a previously set GstGLDisplayEGL */
|
|
ret = g_object_dup_data (G_OBJECT (display), GST_GL_DISPLAY_EGL_NAME,
|
|
(GDuplicateFunc) _ref_if_set, NULL);
|
|
if (ret && GST_IS_GL_DISPLAY_EGL (ret)) {
|
|
GST_LOG_OBJECT (display, "display %" GST_PTR_FORMAT "already has a "
|
|
"GstGLDisplayEGL %" GST_PTR_FORMAT, display, ret);
|
|
return ret;
|
|
}
|
|
|
|
if (ret)
|
|
gst_object_unref (ret);
|
|
|
|
display_type = gst_gl_display_get_handle_type (display);
|
|
native_display = gst_gl_display_get_handle (display);
|
|
|
|
g_return_val_if_fail (native_display != 0, NULL);
|
|
g_return_val_if_fail (display_type != GST_GL_DISPLAY_TYPE_NONE, NULL);
|
|
|
|
ret = g_object_new (GST_TYPE_GL_DISPLAY_EGL, NULL);
|
|
gst_object_ref_sink (ret);
|
|
|
|
ret->display =
|
|
gst_gl_display_egl_get_from_native (display_type, native_display);
|
|
|
|
if (!ret->display) {
|
|
GST_WARNING_OBJECT (ret, "failed to get EGLDisplay from native display");
|
|
gst_object_unref (ret);
|
|
return NULL;
|
|
}
|
|
g_object_set_data_full (G_OBJECT (display), GST_GL_DISPLAY_EGL_NAME,
|
|
gst_object_ref (ret), (GDestroyNotify) gst_object_unref);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static guintptr
|
|
gst_gl_display_egl_get_handle (GstGLDisplay * display)
|
|
{
|
|
return (guintptr) GST_GL_DISPLAY_EGL (display)->display;
|
|
}
|