mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-10 03:19:40 +00:00
185da3d1a4
This patch is to change the inheritance of GstVaapiDisplay to GstObject, instead of GstVaapiMiniObject. In this way we can use all the available infrastructure for GObject/GstObject such as GstTracer, GIR, etc. In addition, a new debug category for GstVaapiDisplay is created to make it easier to trace debug messages. It is named "vaapidisplay" and it transverse all the VA display backends (DRM, GLX, EGL, Wayland, ...) This patch is a step forward to expose GstVaapiDisplay for users in a future library. https://bugzilla.gnome.org/show_bug.cgi?id=768266 Signed-off-by: Víctor Manuel Jáquez Leal <victorx.jaquez@intel.com>
1421 lines
38 KiB
C
1421 lines
38 KiB
C
/*
|
|
* gstvaapiutils_egl.c - EGL utilities
|
|
*
|
|
* Copyright (C) 2014 Intel Corporation
|
|
* Author: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public License
|
|
* as published by the Free Software Foundation; either version 2.1
|
|
* 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free
|
|
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301
|
|
*/
|
|
|
|
#include "sysdeps.h"
|
|
#include "gstvaapiutils_egl.h"
|
|
|
|
#define DEBUG 1
|
|
#include "gstvaapidebug.h"
|
|
|
|
typedef struct egl_message_s EglMessage;
|
|
struct egl_message_s
|
|
{
|
|
EglObject base;
|
|
EglContextRunFunc func;
|
|
gpointer args;
|
|
};
|
|
|
|
static void
|
|
egl_message_finalize (EglMessage * msg)
|
|
{
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
// Utility functions
|
|
|
|
typedef struct gl_version_info_s GlVersionInfo;
|
|
struct gl_version_info_s
|
|
{
|
|
guint gles_version;
|
|
guint gl_api_bit;
|
|
guint gl_api;
|
|
const gchar *gl_api_name;
|
|
};
|
|
|
|
static const GlVersionInfo gl_version_info[] = {
|
|
{0, EGL_OPENGL_BIT, EGL_OPENGL_API, "OpenGL"},
|
|
{1, EGL_OPENGL_ES_BIT, EGL_OPENGL_ES_API, "OpenGL_ES"},
|
|
{2, EGL_OPENGL_ES2_BIT, EGL_OPENGL_ES_API, "OpenGL_ES2"},
|
|
{3, EGL_OPENGL_ES3_BIT_KHR, EGL_OPENGL_ES_API, "OpenGL_ES3"},
|
|
{0,}
|
|
};
|
|
|
|
static const GlVersionInfo *
|
|
gl_version_info_lookup (guint gles_version)
|
|
{
|
|
const GlVersionInfo *vinfo;
|
|
|
|
for (vinfo = gl_version_info; vinfo->gl_api_bit != 0; vinfo++) {
|
|
if (vinfo->gles_version == gles_version)
|
|
return vinfo;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static const GlVersionInfo *
|
|
gl_version_info_lookup_by_api (guint api)
|
|
{
|
|
const GlVersionInfo *vinfo;
|
|
|
|
for (vinfo = gl_version_info; vinfo->gl_api_bit != 0; vinfo++) {
|
|
if (api & vinfo->gl_api_bit)
|
|
return vinfo;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static const GlVersionInfo *
|
|
gl_version_info_lookup_by_api_name (const gchar * name)
|
|
{
|
|
const GlVersionInfo *vinfo;
|
|
|
|
for (vinfo = gl_version_info; vinfo->gl_api_bit != 0; vinfo++) {
|
|
if (g_strcmp0 (vinfo->gl_api_name, name) == 0)
|
|
return vinfo;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static gboolean
|
|
g_strv_match_string (gchar ** extensions_list, const gchar * name)
|
|
{
|
|
if (extensions_list) {
|
|
for (; *extensions_list != NULL; extensions_list++) {
|
|
if (g_strcmp0 (*extensions_list, name) == 0)
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
egl_find_attrib_value (const EGLint * attribs, EGLint type, EGLint * value_ptr)
|
|
{
|
|
while (attribs[0] != EGL_NONE) {
|
|
if (attribs[0] == type) {
|
|
if (value_ptr)
|
|
*value_ptr = attribs[1];
|
|
return TRUE;
|
|
}
|
|
attribs += 2;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
// Basic objects
|
|
|
|
#define EGL_OBJECT(object) ((EglObject *)(object))
|
|
#define EGL_OBJECT_CLASS(klass) ((EglObjectClass *)(klass))
|
|
|
|
#define EGL_OBJECT_DEFINE_CLASS_WITH_CODE(TN, t_n, code) \
|
|
static void \
|
|
G_PASTE(t_n,_finalize) (TN * object); \
|
|
\
|
|
static inline const EglObjectClass * \
|
|
G_PASTE(t_n,_class) (void) \
|
|
{ \
|
|
static G_PASTE(TN,Class) g_class; \
|
|
static gsize g_class_init = FALSE; \
|
|
\
|
|
if (g_once_init_enter (&g_class_init)) { \
|
|
GstVaapiMiniObjectClass *const object_class = \
|
|
GST_VAAPI_MINI_OBJECT_CLASS (&g_class); \
|
|
code; \
|
|
object_class->size = sizeof (TN); \
|
|
object_class->finalize = (GDestroyNotify) \
|
|
G_PASTE(t_n,_finalize); \
|
|
g_once_init_leave (&g_class_init, TRUE); \
|
|
} \
|
|
return EGL_OBJECT_CLASS (&g_class); \
|
|
}
|
|
|
|
#define EGL_OBJECT_DEFINE_CLASS(TN, t_n) \
|
|
EGL_OBJECT_DEFINE_CLASS_WITH_CODE (TN, t_n, /**/)
|
|
|
|
static inline gpointer
|
|
egl_object_new (const EglObjectClass * klass)
|
|
{
|
|
return gst_vaapi_mini_object_new (GST_VAAPI_MINI_OBJECT_CLASS (klass));
|
|
}
|
|
|
|
static inline gpointer
|
|
egl_object_new0 (const EglObjectClass * klass)
|
|
{
|
|
return gst_vaapi_mini_object_new0 (GST_VAAPI_MINI_OBJECT_CLASS (klass));
|
|
}
|
|
|
|
typedef struct egl_object_class_s EglMessageClass;
|
|
typedef struct egl_object_class_s EglVTableClass;
|
|
typedef struct egl_object_class_s EglDisplayClass;
|
|
typedef struct egl_object_class_s EglConfigClass;
|
|
typedef struct egl_object_class_s EglContextClass;
|
|
typedef struct egl_object_class_s EglSurfaceClass;
|
|
typedef struct egl_object_class_s EglProgramClass;
|
|
typedef struct egl_object_class_s EglWindowClass;
|
|
|
|
EGL_OBJECT_DEFINE_CLASS (EglMessage, egl_message);
|
|
EGL_OBJECT_DEFINE_CLASS (EglVTable, egl_vtable);
|
|
EGL_OBJECT_DEFINE_CLASS (EglDisplay, egl_display);
|
|
EGL_OBJECT_DEFINE_CLASS (EglConfig, egl_config);
|
|
EGL_OBJECT_DEFINE_CLASS (EglContext, egl_context);
|
|
EGL_OBJECT_DEFINE_CLASS (EglSurface, egl_surface);
|
|
EGL_OBJECT_DEFINE_CLASS (EglProgram, egl_program);
|
|
EGL_OBJECT_DEFINE_CLASS (EglWindow, egl_window);
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
// Desktop OpenGL and OpenGL|ES dispatcher (vtable)
|
|
|
|
static GMutex gl_vtables_lock;
|
|
static EglVTable *gl_vtables[4];
|
|
|
|
#if (USE_GLES_VERSION_MASK & (1U << 0))
|
|
static const gchar *gl_library_names[] = {
|
|
"libGL.la",
|
|
"libGL.so.1",
|
|
NULL
|
|
};
|
|
#endif
|
|
|
|
#if (USE_GLES_VERSION_MASK & (1U << 1))
|
|
static const gchar *gles1_library_names[] = {
|
|
"libGLESv1_CM.la",
|
|
"libGLESv1_CM.so.1",
|
|
NULL
|
|
};
|
|
#endif
|
|
|
|
#if (USE_GLES_VERSION_MASK & (1U << 2))
|
|
static const gchar *gles2_library_names[] = {
|
|
"libGLESv2.la",
|
|
"libGLESv2.so.2",
|
|
NULL
|
|
};
|
|
#endif
|
|
|
|
static const gchar **gl_library_names_group[] = {
|
|
#if (USE_GLES_VERSION_MASK & (1U << 0))
|
|
gl_library_names,
|
|
#endif
|
|
NULL
|
|
};
|
|
|
|
static const gchar **gles1_library_names_group[] = {
|
|
#if (USE_GLES_VERSION_MASK & (1U << 1))
|
|
gles1_library_names,
|
|
#endif
|
|
NULL
|
|
};
|
|
|
|
static const gchar **gles2_library_names_group[] = {
|
|
#if (USE_GLES_VERSION_MASK & (1U << 2))
|
|
gles2_library_names,
|
|
#endif
|
|
NULL
|
|
};
|
|
|
|
static const gchar **gles3_library_names_group[] = {
|
|
#if (USE_GLES_VERSION_MASK & (1U << 3))
|
|
gles2_library_names,
|
|
#endif
|
|
NULL
|
|
};
|
|
|
|
static const gchar ***
|
|
egl_vtable_get_library_names_group (guint gles_version)
|
|
{
|
|
const gchar ***library_names_group;
|
|
|
|
switch (gles_version) {
|
|
case 0:
|
|
library_names_group = gl_library_names_group;
|
|
break;
|
|
case 1:
|
|
library_names_group = gles1_library_names_group;
|
|
break;
|
|
case 2:
|
|
library_names_group = gles2_library_names_group;
|
|
break;
|
|
case 3:
|
|
library_names_group = gles3_library_names_group;
|
|
break;
|
|
default:
|
|
library_names_group = NULL;
|
|
break;
|
|
}
|
|
return library_names_group;
|
|
}
|
|
|
|
static gboolean
|
|
egl_vtable_check_extension (EglVTable * vtable, EGLDisplay display,
|
|
gboolean is_egl, const gchar * group_name, guint * group_ptr)
|
|
{
|
|
gchar ***extensions_list;
|
|
const gchar *extensions;
|
|
|
|
g_return_val_if_fail (group_name != NULL, FALSE);
|
|
g_return_val_if_fail (group_ptr != NULL, FALSE);
|
|
|
|
if (*group_ptr > 0)
|
|
return TRUE;
|
|
|
|
GST_DEBUG ("check for %s extension %s", is_egl ? "EGL" : "GL", group_name);
|
|
|
|
if (is_egl) {
|
|
if (!vtable->egl_extensions) {
|
|
extensions = eglQueryString (display, EGL_EXTENSIONS);
|
|
if (!extensions)
|
|
return FALSE;
|
|
GST_DEBUG ("EGL extensions: %s", extensions);
|
|
vtable->egl_extensions = g_strsplit (extensions, " ", 0);
|
|
}
|
|
extensions_list = &vtable->egl_extensions;
|
|
} else {
|
|
if (!vtable->gl_extensions) {
|
|
extensions = (const gchar *) vtable->glGetString (GL_EXTENSIONS);
|
|
if (!extensions)
|
|
return FALSE;
|
|
GST_DEBUG ("GL extensions: %s", extensions);
|
|
vtable->gl_extensions = g_strsplit (extensions, " ", 0);
|
|
}
|
|
extensions_list = &vtable->gl_extensions;
|
|
}
|
|
if (!g_strv_match_string (*extensions_list, group_name))
|
|
return FALSE;
|
|
|
|
GST_LOG (" found %s extension %s", is_egl ? "EGL" : "GL", group_name);
|
|
(*group_ptr)++;
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
egl_vtable_load_symbol (EglVTable * vtable, EGLDisplay display, gboolean is_egl,
|
|
const gchar * symbol_name, gpointer * symbol_ptr,
|
|
const gchar * group_name, guint * group_ptr)
|
|
{
|
|
void (*symbol) (void);
|
|
|
|
if (group_ptr && !*group_ptr) {
|
|
if (!egl_vtable_check_extension (vtable, display, is_egl, group_name,
|
|
group_ptr))
|
|
return FALSE;
|
|
}
|
|
|
|
if (is_egl) {
|
|
symbol = eglGetProcAddress (symbol_name);
|
|
} else {
|
|
if (!g_module_symbol (vtable->base.handle.p, symbol_name,
|
|
(gpointer *) & symbol))
|
|
return FALSE;
|
|
}
|
|
if (!symbol)
|
|
return FALSE;
|
|
|
|
GST_LOG (" found symbol %s", symbol_name);
|
|
if (symbol_ptr)
|
|
*symbol_ptr = symbol;
|
|
if (group_ptr)
|
|
(*group_ptr)++;
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
egl_vtable_load_egl_symbols (EglVTable * vtable, EGLDisplay display)
|
|
{
|
|
guint n = 0;
|
|
|
|
#define EGL_DEFINE_EXTENSION(NAME) do { \
|
|
egl_vtable_check_extension (vtable, display, TRUE, \
|
|
"EGL_" G_STRINGIFY (NAME), \
|
|
&vtable->GL_PROTO_GEN_CONCAT(has_EGL_,NAME)); \
|
|
} while (0);
|
|
|
|
#define EGL_PROTO_BEGIN(NAME, TYPE, EXTENSION) do { \
|
|
n += egl_vtable_load_symbol (vtable, display, TRUE, \
|
|
G_STRINGIFY(GL_PROTO_GEN_CONCAT(egl,NAME)), \
|
|
(gpointer *) &vtable->GL_PROTO_GEN_CONCAT(egl,NAME), \
|
|
"EGL_" G_STRINGIFY(EXTENSION), \
|
|
&vtable->GL_PROTO_GEN_CONCAT(has_EGL_,EXTENSION)); \
|
|
} while (0);
|
|
|
|
#include "egl_vtable.h"
|
|
|
|
vtable->num_egl_symbols = n;
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
egl_vtable_load_gl_symbols (EglVTable * vtable, EGLDisplay display)
|
|
{
|
|
guint n = 0;
|
|
|
|
vtable->has_GL_CORE_1_0 = 1;
|
|
vtable->has_GL_CORE_1_1 = 1;
|
|
vtable->has_GL_CORE_1_3 = 1;
|
|
vtable->has_GL_CORE_2_0 = 1;
|
|
|
|
#define GL_DEFINE_EXTENSION(NAME) do { \
|
|
egl_vtable_check_extension (vtable, display, FALSE, \
|
|
"GL_" G_STRINGIFY (NAME), \
|
|
&vtable->GL_PROTO_GEN_CONCAT(has_GL_,NAME)); \
|
|
} while (0);
|
|
|
|
#define GL_PROTO_BEGIN(NAME, TYPE, EXTENSION) do { \
|
|
n += egl_vtable_load_symbol (vtable, display, FALSE, \
|
|
G_STRINGIFY(GL_PROTO_GEN_CONCAT(gl,NAME)), \
|
|
(gpointer *) &vtable->GL_PROTO_GEN_CONCAT(gl,NAME), \
|
|
"GL_" G_STRINGIFY(EXTENSION), \
|
|
&vtable->GL_PROTO_GEN_CONCAT(has_GL_,EXTENSION)); \
|
|
} while (0);
|
|
|
|
#include "egl_vtable.h"
|
|
|
|
--vtable->has_GL_CORE_1_0;
|
|
--vtable->has_GL_CORE_1_1;
|
|
--vtable->has_GL_CORE_1_3;
|
|
--vtable->has_GL_CORE_2_0;
|
|
|
|
vtable->num_gl_symbols = n;
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
egl_vtable_try_load_library (EglVTable * vtable, const gchar * name)
|
|
{
|
|
if (vtable->base.handle.p)
|
|
g_module_close (vtable->base.handle.p);
|
|
vtable->base.handle.p = g_module_open (name,
|
|
G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
|
|
if (!vtable->base.handle.p)
|
|
return FALSE;
|
|
|
|
GST_DEBUG ("loaded backend: %s", g_module_name (vtable->base.handle.p));
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
egl_vtable_find_library (EglVTable * vtable)
|
|
{
|
|
const gchar ***library_names_ptr =
|
|
egl_vtable_get_library_names_group (vtable->gles_version);
|
|
|
|
if (!library_names_ptr)
|
|
return FALSE;
|
|
|
|
for (; *library_names_ptr != NULL; library_names_ptr++) {
|
|
const gchar **library_name_ptr = *library_names_ptr;
|
|
for (; *library_name_ptr != NULL; library_name_ptr++) {
|
|
if (egl_vtable_try_load_library (vtable, *library_name_ptr))
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
egl_vtable_init (EglVTable * vtable, EGLDisplay display, guint gles_version)
|
|
{
|
|
GST_DEBUG ("initialize for OpenGL|ES API version %d", gles_version);
|
|
|
|
vtable->gles_version = gles_version;
|
|
if (!egl_vtable_find_library (vtable))
|
|
return FALSE;
|
|
if (!egl_vtable_load_egl_symbols (vtable, display))
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
egl_vtable_finalize (EglVTable * vtable)
|
|
{
|
|
g_strfreev (vtable->egl_extensions);
|
|
g_strfreev (vtable->gl_extensions);
|
|
if (vtable->base.handle.p)
|
|
g_module_close (vtable->base.handle.p);
|
|
|
|
if (vtable->base.is_wrapped) {
|
|
g_mutex_lock (&gl_vtables_lock);
|
|
gl_vtables[vtable->gles_version] = NULL;
|
|
g_mutex_unlock (&gl_vtables_lock);
|
|
}
|
|
}
|
|
|
|
static EglVTable *
|
|
egl_vtable_new (EglDisplay * display, guint gles_version)
|
|
{
|
|
EglVTable *vtable;
|
|
|
|
g_return_val_if_fail (display != NULL, NULL);
|
|
|
|
vtable = egl_object_new0 (egl_vtable_class ());
|
|
if (!vtable
|
|
|| !egl_vtable_init (vtable, display->base.handle.p, gles_version))
|
|
goto error;
|
|
return vtable;
|
|
|
|
error:
|
|
egl_object_replace (&vtable, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
static EglVTable *
|
|
egl_vtable_new_cached (EglDisplay * display, guint gles_version)
|
|
{
|
|
EglVTable *vtable, **vtable_ptr;
|
|
|
|
g_return_val_if_fail (gles_version < G_N_ELEMENTS (gl_vtables), NULL);
|
|
|
|
vtable_ptr = &gl_vtables[gles_version];
|
|
|
|
g_mutex_lock (&gl_vtables_lock);
|
|
vtable = *vtable_ptr;
|
|
if (vtable)
|
|
egl_object_ref (vtable);
|
|
else {
|
|
vtable = egl_vtable_new (display, gles_version);
|
|
if (vtable) {
|
|
vtable->base.is_wrapped = TRUE;
|
|
*vtable_ptr = vtable;
|
|
}
|
|
}
|
|
g_mutex_unlock (&gl_vtables_lock);
|
|
return vtable;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
// EGL Display
|
|
|
|
static gboolean
|
|
egl_display_run (EglDisplay * display, EglContextRunFunc func, gpointer args)
|
|
{
|
|
EglMessage *msg;
|
|
|
|
if (display->gl_thread == g_thread_self ()) {
|
|
func (args);
|
|
return TRUE;
|
|
}
|
|
|
|
msg = egl_object_new0 (egl_message_class ());
|
|
if (!msg)
|
|
return FALSE;
|
|
|
|
msg->base.is_valid = TRUE;
|
|
msg->func = func;
|
|
msg->args = args;
|
|
g_async_queue_push (display->gl_queue, egl_object_ref (msg));
|
|
|
|
g_mutex_lock (&display->mutex);
|
|
while (msg->base.is_valid)
|
|
g_cond_wait (&display->gl_thread_ready, &display->mutex);
|
|
g_mutex_unlock (&display->mutex);
|
|
egl_object_unref (msg);
|
|
return TRUE;
|
|
}
|
|
|
|
static gpointer
|
|
egl_display_thread (gpointer data)
|
|
{
|
|
EglDisplay *const display = data;
|
|
EGLDisplay gl_display = display->base.handle.p;
|
|
EGLint major_version, minor_version;
|
|
gchar **gl_apis, **gl_api;
|
|
|
|
if (!display->base.is_wrapped) {
|
|
gl_display = display->base.handle.p = eglGetDisplay (gl_display);
|
|
if (!gl_display)
|
|
goto error;
|
|
if (!eglInitialize (gl_display, &major_version, &minor_version))
|
|
goto error;
|
|
}
|
|
|
|
display->gl_vendor_string =
|
|
g_strdup (eglQueryString (gl_display, EGL_VENDOR));
|
|
display->gl_version_string =
|
|
g_strdup (eglQueryString (gl_display, EGL_VERSION));
|
|
display->gl_apis_string =
|
|
g_strdup (eglQueryString (gl_display, EGL_CLIENT_APIS));
|
|
|
|
GST_INFO ("EGL vendor: %s", display->gl_vendor_string);
|
|
GST_INFO ("EGL version: %s", display->gl_version_string);
|
|
GST_INFO ("EGL client APIs: %s", display->gl_apis_string);
|
|
|
|
gl_apis = g_strsplit (display->gl_apis_string, " ", 0);
|
|
if (!gl_apis)
|
|
goto error;
|
|
for (gl_api = gl_apis; *gl_api != NULL; gl_api++) {
|
|
const GlVersionInfo *const vinfo =
|
|
gl_version_info_lookup_by_api_name (*gl_api);
|
|
|
|
if (vinfo)
|
|
display->gl_apis |= vinfo->gl_api_bit;
|
|
}
|
|
g_strfreev (gl_apis);
|
|
if (!display->gl_apis)
|
|
goto error;
|
|
|
|
display->base.is_valid = TRUE;
|
|
g_cond_broadcast (&display->gl_thread_ready);
|
|
|
|
while (!display->gl_thread_cancel) {
|
|
EglMessage *const msg =
|
|
g_async_queue_timeout_pop (display->gl_queue, 100000);
|
|
|
|
if (msg) {
|
|
if (msg->base.is_valid) {
|
|
msg->func (msg->args);
|
|
msg->base.is_valid = FALSE;
|
|
g_cond_broadcast (&display->gl_thread_ready);
|
|
}
|
|
egl_object_unref (msg);
|
|
}
|
|
}
|
|
|
|
done:
|
|
if (gl_display != EGL_NO_DISPLAY && !display->base.is_wrapped)
|
|
eglTerminate (gl_display);
|
|
display->base.handle.p = NULL;
|
|
g_cond_broadcast (&display->gl_thread_ready);
|
|
return NULL;
|
|
|
|
error:
|
|
display->base.is_valid = FALSE;
|
|
goto done;
|
|
}
|
|
|
|
static gboolean
|
|
egl_display_init (EglDisplay * display)
|
|
{
|
|
display->gl_queue =
|
|
g_async_queue_new_full ((GDestroyNotify) gst_vaapi_mini_object_unref);
|
|
if (!display->gl_queue)
|
|
return FALSE;
|
|
|
|
g_mutex_init (&display->mutex);
|
|
g_cond_init (&display->gl_thread_ready);
|
|
display->gl_thread = g_thread_try_new ("OpenGL Thread", egl_display_thread,
|
|
display, NULL);
|
|
if (!display->gl_thread)
|
|
return FALSE;
|
|
|
|
g_mutex_lock (&display->mutex);
|
|
g_cond_wait (&display->gl_thread_ready, &display->mutex);
|
|
g_mutex_unlock (&display->mutex);
|
|
return display->base.is_valid;
|
|
}
|
|
|
|
static void
|
|
egl_display_finalize (EglDisplay * display)
|
|
{
|
|
display->gl_thread_cancel = TRUE;
|
|
g_thread_join (display->gl_thread);
|
|
g_cond_clear (&display->gl_thread_ready);
|
|
g_mutex_clear (&display->mutex);
|
|
g_async_queue_unref (display->gl_queue);
|
|
|
|
g_free (display->gl_vendor_string);
|
|
g_free (display->gl_version_string);
|
|
g_free (display->gl_apis_string);
|
|
}
|
|
|
|
static EglDisplay *
|
|
egl_display_new_full (gpointer handle, gboolean is_wrapped)
|
|
{
|
|
EglDisplay *display;
|
|
|
|
display = egl_object_new0 (egl_display_class ());
|
|
if (!display)
|
|
return NULL;
|
|
|
|
display->base.handle.p = handle;
|
|
display->base.is_wrapped = is_wrapped;
|
|
if (!egl_display_init (display))
|
|
goto error;
|
|
return display;
|
|
|
|
error:
|
|
egl_object_unref (display);
|
|
return NULL;
|
|
}
|
|
|
|
EglDisplay *
|
|
egl_display_new (gpointer native_display)
|
|
{
|
|
g_return_val_if_fail (native_display != NULL, NULL);
|
|
|
|
return egl_display_new_full (native_display, FALSE);
|
|
}
|
|
|
|
EglDisplay *
|
|
egl_display_new_wrapped (EGLDisplay gl_display)
|
|
{
|
|
g_return_val_if_fail (gl_display != EGL_NO_DISPLAY, NULL);
|
|
|
|
return egl_display_new_full (gl_display, TRUE);
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
// EGL Config
|
|
|
|
static gboolean
|
|
egl_config_init (EglConfig * config, EglDisplay * display,
|
|
const EGLint * attribs)
|
|
{
|
|
EGLDisplay const gl_display = display->base.handle.p;
|
|
const GlVersionInfo *vinfo;
|
|
EGLConfig gl_config;
|
|
EGLint v, gl_apis, num_configs;
|
|
|
|
egl_object_replace (&config->display, display);
|
|
|
|
if (!eglChooseConfig (gl_display, attribs, &gl_config, 1, &num_configs))
|
|
return FALSE;
|
|
if (num_configs != 1)
|
|
return FALSE;
|
|
config->base.handle.p = gl_config;
|
|
|
|
if (!eglGetConfigAttrib (gl_display, gl_config, EGL_CONFIG_ID, &v))
|
|
return FALSE;
|
|
config->config_id = v;
|
|
|
|
if (!eglGetConfigAttrib (gl_display, gl_config, EGL_NATIVE_VISUAL_ID, &v))
|
|
return FALSE;
|
|
config->visual_id = v;
|
|
|
|
if (!eglGetConfigAttrib (gl_display, gl_config, EGL_RENDERABLE_TYPE, &v))
|
|
return FALSE;
|
|
if (!egl_find_attrib_value (attribs, EGL_RENDERABLE_TYPE, &gl_apis))
|
|
return FALSE;
|
|
vinfo = gl_version_info_lookup_by_api (v & gl_apis);
|
|
if (!vinfo)
|
|
return FALSE;
|
|
config->gles_version = vinfo->gles_version;
|
|
config->gl_api = vinfo->gles_version > 0 ? EGL_OPENGL_ES_API : EGL_OPENGL_API;
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
egl_config_finalize (EglConfig * config)
|
|
{
|
|
egl_object_replace (&config->display, NULL);
|
|
}
|
|
|
|
EglConfig *
|
|
egl_config_new (EglDisplay * display, guint gles_version, GstVideoFormat format)
|
|
{
|
|
EGLint attribs[2 * 6 + 1], *attrib = attribs;
|
|
const GstVideoFormatInfo *finfo;
|
|
const GlVersionInfo *vinfo;
|
|
|
|
g_return_val_if_fail (display != NULL, NULL);
|
|
|
|
finfo = gst_video_format_get_info (format);
|
|
if (!finfo || !GST_VIDEO_FORMAT_INFO_IS_RGB (finfo))
|
|
return NULL;
|
|
|
|
vinfo = gl_version_info_lookup (gles_version);
|
|
if (!vinfo)
|
|
return NULL;
|
|
|
|
*attrib++ = EGL_COLOR_BUFFER_TYPE;
|
|
*attrib++ = EGL_RGB_BUFFER;
|
|
*attrib++ = EGL_RED_SIZE;
|
|
*attrib++ = GST_VIDEO_FORMAT_INFO_DEPTH (finfo, GST_VIDEO_COMP_R);
|
|
*attrib++ = EGL_GREEN_SIZE;
|
|
*attrib++ = GST_VIDEO_FORMAT_INFO_DEPTH (finfo, GST_VIDEO_COMP_G);
|
|
*attrib++ = EGL_BLUE_SIZE;
|
|
*attrib++ = GST_VIDEO_FORMAT_INFO_DEPTH (finfo, GST_VIDEO_COMP_B);
|
|
*attrib++ = EGL_ALPHA_SIZE;
|
|
*attrib++ = GST_VIDEO_FORMAT_INFO_DEPTH (finfo, GST_VIDEO_COMP_A);
|
|
*attrib++ = EGL_RENDERABLE_TYPE;
|
|
*attrib++ = vinfo->gl_api_bit;
|
|
*attrib++ = EGL_NONE;
|
|
g_assert (attrib - attribs <= G_N_ELEMENTS (attribs));
|
|
|
|
return egl_config_new_with_attribs (display, attribs);
|
|
}
|
|
|
|
EglConfig *
|
|
egl_config_new_with_attribs (EglDisplay * display, const EGLint * attribs)
|
|
{
|
|
EglConfig *config;
|
|
|
|
g_return_val_if_fail (display != NULL, NULL);
|
|
g_return_val_if_fail (attribs != NULL, NULL);
|
|
|
|
config = egl_object_new0 (egl_config_class ());
|
|
if (!config || !egl_config_init (config, display, attribs))
|
|
goto error;
|
|
return config;
|
|
|
|
error:
|
|
egl_object_replace (&config, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
static EglConfig *
|
|
egl_config_new_from_gl_context (EglDisplay * display, EGLContext gl_context)
|
|
{
|
|
EGLDisplay const gl_display = display->base.handle.p;
|
|
EGLint attribs[3 * 2 + 1], *attrib = attribs;
|
|
EGLint config_id, api, v;
|
|
guint gles_version;
|
|
const GlVersionInfo *vinfo;
|
|
|
|
if (!eglQueryContext (gl_display, gl_context, EGL_CONFIG_ID, &config_id))
|
|
return NULL;
|
|
if (!eglQueryContext (gl_display, gl_context, EGL_CONTEXT_CLIENT_TYPE, &api))
|
|
return NULL;
|
|
if (!eglQueryContext (gl_display, gl_context, EGL_CONTEXT_CLIENT_VERSION, &v))
|
|
return NULL;
|
|
|
|
if (api == EGL_OPENGL_API)
|
|
gles_version = 0;
|
|
else if (api == EGL_OPENGL_ES_API)
|
|
gles_version = v;
|
|
else {
|
|
GST_ERROR ("unsupported EGL client API (%d)", api);
|
|
return NULL;
|
|
}
|
|
|
|
vinfo = gl_version_info_lookup (gles_version);
|
|
if (!vinfo)
|
|
return NULL;
|
|
|
|
*attrib++ = EGL_COLOR_BUFFER_TYPE;
|
|
*attrib++ = EGL_RGB_BUFFER;
|
|
*attrib++ = EGL_CONFIG_ID;
|
|
*attrib++ = config_id;
|
|
*attrib++ = EGL_RENDERABLE_TYPE;
|
|
*attrib++ = vinfo->gl_api_bit;
|
|
*attrib++ = EGL_NONE;
|
|
g_assert (attrib - attribs <= G_N_ELEMENTS (attribs));
|
|
|
|
return egl_config_new_with_attribs (display, attribs);
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
// EGL Surface
|
|
|
|
static void
|
|
egl_surface_finalize (EglSurface * surface)
|
|
{
|
|
if (surface->base.handle.p != EGL_NO_SURFACE && !surface->base.is_wrapped)
|
|
eglDestroySurface (surface->display->base.handle.p, surface->base.handle.p);
|
|
egl_object_replace (&surface->display, NULL);
|
|
}
|
|
|
|
static EglSurface *
|
|
egl_surface_new_wrapped (EglDisplay * display, EGLSurface gl_surface)
|
|
{
|
|
EglSurface *surface;
|
|
|
|
g_return_val_if_fail (display != NULL, NULL);
|
|
|
|
surface = egl_object_new (egl_surface_class ());
|
|
if (!surface)
|
|
return NULL;
|
|
|
|
surface->base.is_wrapped = TRUE;
|
|
surface->base.handle.p = gl_surface;
|
|
surface->display = egl_object_ref (display);
|
|
return surface;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
// EGL Context
|
|
|
|
static void egl_context_state_get_current (EglContextState * cs);
|
|
|
|
static gboolean
|
|
egl_context_state_set_current (EglContextState * new_cs,
|
|
EglContextState * old_cs);
|
|
|
|
static gboolean
|
|
ensure_vtable (EglContext * ctx)
|
|
{
|
|
if (!ctx->vtable) {
|
|
ctx->vtable = egl_vtable_new_cached (ctx->display,
|
|
ctx->config ? ctx->config->gles_version : 0);
|
|
if (!ctx->vtable)
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
ensure_context (EglContext * ctx, EGLContext gl_parent_context)
|
|
{
|
|
EGLDisplay *const gl_display = ctx->display->base.handle.p;
|
|
EGLContext gl_context = ctx->base.handle.p;
|
|
EGLint gles_attribs[3], *attribs = NULL;
|
|
|
|
if (!gl_context) {
|
|
if (ctx->config->gles_version >= 2) {
|
|
attribs = gles_attribs;
|
|
attribs[0] = EGL_CONTEXT_CLIENT_VERSION;
|
|
attribs[1] = ctx->config->gles_version;
|
|
attribs[2] = EGL_NONE;
|
|
}
|
|
|
|
gl_context = eglCreateContext (gl_display, ctx->config->base.handle.p,
|
|
gl_parent_context, attribs);
|
|
if (!gl_context)
|
|
goto error_create_context;
|
|
ctx->base.handle.p = gl_context;
|
|
}
|
|
return TRUE;
|
|
|
|
/* ERRORS */
|
|
error_create_context:
|
|
GST_ERROR ("failed to create EGL context");
|
|
return FALSE;
|
|
}
|
|
|
|
static inline gboolean
|
|
ensure_gl_is_surfaceless (EglContext * ctx)
|
|
{
|
|
return ctx->vtable->has_EGL_KHR_surfaceless_context ||
|
|
(ctx->read_surface && ctx->draw_surface);
|
|
}
|
|
|
|
static gboolean
|
|
ensure_gl_scene (EglContext * ctx)
|
|
{
|
|
EglVTable *vtable;
|
|
|
|
if (!ensure_gl_is_surfaceless (ctx))
|
|
return FALSE;
|
|
|
|
if (ctx->base.is_valid)
|
|
return TRUE;
|
|
|
|
vtable = egl_context_get_vtable (ctx, TRUE);
|
|
if (!vtable)
|
|
return FALSE;
|
|
|
|
vtable->glClearColor (0.0, 0.0, 0.0, 1.0);
|
|
if (ctx->config && ctx->config->gles_version == 0)
|
|
vtable->glEnable (GL_TEXTURE_2D);
|
|
vtable->glDisable (GL_BLEND);
|
|
vtable->glDisable (GL_DEPTH_TEST);
|
|
|
|
ctx->base.is_valid = TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* egl_context_state_get_current:
|
|
* @cs: return location to the current #EglContextState
|
|
*
|
|
* Retrieves the current EGL context, display and surface to pack into
|
|
* the #EglContextState struct.
|
|
*/
|
|
static void
|
|
egl_context_state_get_current (EglContextState * cs)
|
|
{
|
|
cs->display = eglGetCurrentDisplay ();
|
|
cs->context = eglGetCurrentContext ();
|
|
if (cs->context) {
|
|
cs->read_surface = eglGetCurrentSurface (EGL_READ);
|
|
cs->draw_surface = eglGetCurrentSurface (EGL_DRAW);
|
|
} else {
|
|
cs->read_surface = EGL_NO_SURFACE;
|
|
cs->draw_surface = EGL_NO_SURFACE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* egl_context_state_set_current:
|
|
* @new_cs: the requested new #EglContextState
|
|
* @old_cs: return location to the context that was previously current
|
|
*
|
|
* Makes the @new_cs EGL context the current EGL rendering context of
|
|
* the calling thread, replacing the previously current context if
|
|
* there was one.
|
|
*
|
|
* If @old_cs is non %NULL, the previously current EGL context and
|
|
* surface are recorded.
|
|
*
|
|
* Return value: %TRUE on success
|
|
*/
|
|
static gboolean
|
|
egl_context_state_set_current (EglContextState * new_cs,
|
|
EglContextState * old_cs)
|
|
{
|
|
/* If display is NULL, this could be that new_cs was retrieved from
|
|
egl_context_state_get_current() with none set previously. If that case,
|
|
the other fields are also NULL and we don't return an error */
|
|
if (!new_cs->display)
|
|
return !new_cs->context && !new_cs->read_surface && !new_cs->draw_surface;
|
|
|
|
if (old_cs) {
|
|
if (old_cs == new_cs)
|
|
return TRUE;
|
|
egl_context_state_get_current (old_cs);
|
|
if (old_cs->display == new_cs->display &&
|
|
old_cs->context == new_cs->context &&
|
|
old_cs->read_surface == new_cs->read_surface &&
|
|
old_cs->draw_surface == new_cs->draw_surface)
|
|
return TRUE;
|
|
}
|
|
return eglMakeCurrent (new_cs->display, new_cs->draw_surface,
|
|
new_cs->read_surface, new_cs->context);
|
|
}
|
|
|
|
static gboolean
|
|
egl_context_init (EglContext * ctx, EglDisplay * display, EglConfig * config,
|
|
EGLContext gl_parent_context)
|
|
{
|
|
egl_object_replace (&ctx->display, display);
|
|
egl_object_replace (&ctx->config, config);
|
|
|
|
if (config)
|
|
eglBindAPI (config->gl_api);
|
|
|
|
if (!ensure_vtable (ctx))
|
|
return FALSE;
|
|
if (!ensure_context (ctx, gl_parent_context))
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
egl_context_finalize (EglContext * ctx)
|
|
{
|
|
if (ctx->base.handle.p && !ctx->base.is_wrapped)
|
|
eglDestroyContext (ctx->display->base.handle.p, ctx->base.handle.p);
|
|
egl_object_replace (&ctx->read_surface, NULL);
|
|
egl_object_replace (&ctx->draw_surface, NULL);
|
|
egl_object_replace (&ctx->config, NULL);
|
|
egl_object_replace (&ctx->display, NULL);
|
|
egl_object_replace (&ctx->vtable, NULL);
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
EglDisplay *display;
|
|
EglConfig *config;
|
|
EGLContext gl_parent_context;
|
|
EglContext *context; /* result */
|
|
} CreateContextArgs;
|
|
|
|
static void
|
|
do_egl_context_new (CreateContextArgs * args)
|
|
{
|
|
EglContext *ctx;
|
|
|
|
ctx = egl_object_new0 (egl_context_class ());
|
|
if (!ctx || !egl_context_init (ctx, args->display, args->config,
|
|
args->gl_parent_context))
|
|
goto error;
|
|
args->context = ctx;
|
|
return;
|
|
|
|
error:
|
|
egl_object_replace (&ctx, NULL);
|
|
args->context = NULL;
|
|
}
|
|
|
|
EglContext *
|
|
egl_context_new (EglDisplay * display, EglConfig * config, EglContext * parent)
|
|
{
|
|
CreateContextArgs args;
|
|
|
|
g_return_val_if_fail (display != NULL, NULL);
|
|
g_return_val_if_fail (config != NULL, NULL);
|
|
|
|
args.display = display;
|
|
args.config = config;
|
|
args.gl_parent_context = parent ? parent->base.handle.p : EGL_NO_CONTEXT;
|
|
if (!egl_display_run (display, (EglContextRunFunc) do_egl_context_new, &args))
|
|
return NULL;
|
|
return args.context;
|
|
}
|
|
|
|
EglContext *
|
|
egl_context_new_wrapped (EglDisplay * display, EGLContext gl_context)
|
|
{
|
|
CreateContextArgs args;
|
|
EglConfig *config;
|
|
gboolean success;
|
|
|
|
g_return_val_if_fail (display != NULL, NULL);
|
|
g_return_val_if_fail (gl_context != EGL_NO_CONTEXT, NULL);
|
|
|
|
config = egl_config_new_from_gl_context (display, gl_context);
|
|
if (!config)
|
|
return NULL;
|
|
|
|
args.display = display;
|
|
args.config = config;
|
|
args.gl_parent_context = gl_context;
|
|
success = egl_display_run (display, (EglContextRunFunc) do_egl_context_new,
|
|
&args);
|
|
egl_object_unref (config);
|
|
if (!success)
|
|
return NULL;
|
|
return args.context;
|
|
}
|
|
|
|
EglVTable *
|
|
egl_context_get_vtable (EglContext * ctx, gboolean need_gl_symbols)
|
|
{
|
|
g_return_val_if_fail (ctx != NULL, NULL);
|
|
g_return_val_if_fail (ctx->display->gl_thread == g_thread_self (), NULL);
|
|
|
|
if (!ensure_vtable (ctx))
|
|
return NULL;
|
|
|
|
if (need_gl_symbols && !(ctx->vtable->num_gl_symbols > 0 ||
|
|
egl_vtable_load_gl_symbols (ctx->vtable,
|
|
ctx->display->base.handle.p)))
|
|
return NULL;
|
|
return ctx->vtable;
|
|
}
|
|
|
|
static void
|
|
egl_context_set_surface (EglContext * ctx, EglSurface * surface)
|
|
{
|
|
g_return_if_fail (ctx != NULL);
|
|
g_return_if_fail (surface != NULL);
|
|
|
|
egl_object_replace (&ctx->read_surface, surface);
|
|
egl_object_replace (&ctx->draw_surface, surface);
|
|
}
|
|
|
|
gboolean
|
|
egl_context_set_current (EglContext * ctx, gboolean activate,
|
|
EglContextState * old_cs)
|
|
{
|
|
EglContextState cs, *new_cs;
|
|
|
|
g_return_val_if_fail (ctx != NULL, FALSE);
|
|
g_return_val_if_fail (ctx->display->gl_thread == g_thread_self (), FALSE);
|
|
|
|
if (activate) {
|
|
new_cs = &cs;
|
|
new_cs->display = ctx->display->base.handle.p;
|
|
new_cs->context = ctx->base.handle.p;
|
|
new_cs->draw_surface = ctx->draw_surface ?
|
|
ctx->draw_surface->base.handle.p : EGL_NO_SURFACE;
|
|
new_cs->read_surface = ctx->read_surface ?
|
|
ctx->read_surface->base.handle.p : EGL_NO_SURFACE;
|
|
} else if (old_cs) {
|
|
new_cs = old_cs;
|
|
old_cs = NULL;
|
|
} else {
|
|
new_cs = &cs;
|
|
new_cs->display = ctx->display->base.handle.p;
|
|
new_cs->context = EGL_NO_CONTEXT;
|
|
new_cs->draw_surface = EGL_NO_SURFACE;
|
|
new_cs->read_surface = EGL_NO_SURFACE;
|
|
old_cs = NULL;
|
|
}
|
|
|
|
if (!egl_context_state_set_current (new_cs, old_cs))
|
|
return FALSE;
|
|
if (activate && !ensure_gl_scene (ctx))
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
egl_context_run (EglContext * ctx, EglContextRunFunc func, gpointer args)
|
|
{
|
|
g_return_val_if_fail (ctx != NULL, FALSE);
|
|
g_return_val_if_fail (func != NULL, FALSE);
|
|
|
|
return egl_display_run (ctx->display, func, args);
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
// EGL Program
|
|
|
|
static GLuint
|
|
egl_compile_shader (EglContext * ctx, GLenum type, const char *source)
|
|
{
|
|
EglVTable *const vtable = egl_context_get_vtable (ctx, TRUE);
|
|
GLuint shader;
|
|
GLint status;
|
|
char log[BUFSIZ];
|
|
GLsizei log_length;
|
|
|
|
shader = vtable->glCreateShader (type);
|
|
vtable->glShaderSource (shader, 1, &source, NULL);
|
|
vtable->glCompileShader (shader);
|
|
vtable->glGetShaderiv (shader, GL_COMPILE_STATUS, &status);
|
|
if (!status) {
|
|
GST_ERROR ("failed to compile %s shader",
|
|
type == GL_FRAGMENT_SHADER ? "fragment" :
|
|
type == GL_VERTEX_SHADER ? "vertex" : "<unknown>");
|
|
|
|
vtable->glGetShaderInfoLog (shader, sizeof (log), &log_length, log);
|
|
GST_ERROR ("info log: %s", log);
|
|
return 0;
|
|
}
|
|
return shader;
|
|
}
|
|
|
|
static void
|
|
egl_program_finalize (EglProgram * program)
|
|
{
|
|
EglVTable *const vtable = program->vtable;
|
|
|
|
if (program->base.handle.u)
|
|
vtable->glDeleteProgram (program->base.handle.u);
|
|
if (program->frag_shader)
|
|
vtable->glDeleteShader (program->frag_shader);
|
|
if (program->vert_shader)
|
|
vtable->glDeleteShader (program->vert_shader);
|
|
egl_object_replace (&program->vtable, NULL);
|
|
}
|
|
|
|
static gboolean
|
|
egl_program_init (EglProgram * program, EglContext * ctx,
|
|
const gchar * frag_shader_text, const gchar * vert_shader_text)
|
|
{
|
|
EglVTable *const vtable = egl_context_get_vtable (ctx, TRUE);
|
|
GLuint prog_id;
|
|
char msg[BUFSIZ];
|
|
GLsizei msglen;
|
|
GLint status;
|
|
|
|
if (ctx->config->gles_version == 1)
|
|
goto error_unsupported_gles_version;
|
|
|
|
program->vtable = egl_object_ref (vtable);
|
|
|
|
program->frag_shader =
|
|
egl_compile_shader (ctx, GL_FRAGMENT_SHADER, frag_shader_text);
|
|
if (!program->frag_shader)
|
|
return FALSE;
|
|
|
|
program->vert_shader =
|
|
egl_compile_shader (ctx, GL_VERTEX_SHADER, vert_shader_text);
|
|
if (!program->vert_shader)
|
|
return FALSE;
|
|
|
|
prog_id = vtable->glCreateProgram ();
|
|
if (!prog_id)
|
|
return FALSE;
|
|
program->base.handle.u = prog_id;
|
|
|
|
vtable->glAttachShader (prog_id, program->frag_shader);
|
|
vtable->glAttachShader (prog_id, program->vert_shader);
|
|
vtable->glBindAttribLocation (prog_id, 0, "position");
|
|
vtable->glBindAttribLocation (prog_id, 1, "texcoord");
|
|
vtable->glLinkProgram (prog_id);
|
|
|
|
vtable->glGetProgramiv (prog_id, GL_LINK_STATUS, &status);
|
|
if (!status)
|
|
goto error_link_program;
|
|
return TRUE;
|
|
|
|
/* ERRORS */
|
|
error_unsupported_gles_version:
|
|
GST_ERROR ("unsupported shader with OpenGL|ES version 1");
|
|
return FALSE;
|
|
error_link_program:
|
|
vtable->glGetProgramInfoLog (prog_id, sizeof (msg), &msglen, msg);
|
|
GST_ERROR ("failed to link program: %s", msg);
|
|
return FALSE;
|
|
}
|
|
|
|
EglProgram *
|
|
egl_program_new (EglContext * ctx, const gchar * frag_shader_text,
|
|
const gchar * vert_shader_text)
|
|
{
|
|
EglProgram *program;
|
|
|
|
g_return_val_if_fail (ctx != NULL, NULL);
|
|
g_return_val_if_fail (frag_shader_text != NULL, NULL);
|
|
g_return_val_if_fail (vert_shader_text != NULL, NULL);
|
|
|
|
program = egl_object_new0 (egl_program_class ());
|
|
if (!program
|
|
|| !egl_program_init (program, ctx, frag_shader_text, vert_shader_text))
|
|
goto error;
|
|
return program;
|
|
|
|
error:
|
|
egl_object_replace (&program, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
// EGL Window
|
|
|
|
static gboolean
|
|
egl_window_init (EglWindow * window, EglContext * ctx, gpointer native_window)
|
|
{
|
|
EGLSurface gl_surface;
|
|
|
|
window->context = egl_context_new (ctx->display, ctx->config, ctx);
|
|
if (!window->context)
|
|
return FALSE;
|
|
ctx = window->context;
|
|
|
|
gl_surface = eglCreateWindowSurface (ctx->display->base.handle.p,
|
|
ctx->config->base.handle.p, (EGLNativeWindowType) native_window, NULL);
|
|
if (!gl_surface)
|
|
return FALSE;
|
|
|
|
window->surface = egl_surface_new_wrapped (ctx->display, gl_surface);
|
|
if (!window->surface)
|
|
goto error_create_surface;
|
|
window->base.handle.p = gl_surface;
|
|
window->base.is_wrapped = FALSE;
|
|
|
|
egl_context_set_surface (ctx, window->surface);
|
|
return TRUE;
|
|
|
|
/* ERRORS */
|
|
error_create_surface:
|
|
GST_ERROR ("failed to create EGL wrapper surface");
|
|
eglDestroySurface (ctx->display->base.handle.p, gl_surface);
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
egl_window_finalize (EglWindow * window)
|
|
{
|
|
if (window->context && window->base.handle.p)
|
|
eglDestroySurface (window->context->display->base.handle.p,
|
|
window->base.handle.p);
|
|
|
|
egl_object_replace (&window->surface, NULL);
|
|
egl_object_replace (&window->context, NULL);
|
|
}
|
|
|
|
EglWindow *
|
|
egl_window_new (EglContext * ctx, gpointer native_window)
|
|
{
|
|
EglWindow *window;
|
|
|
|
g_return_val_if_fail (ctx != NULL, NULL);
|
|
g_return_val_if_fail (native_window != NULL, NULL);
|
|
|
|
window = egl_object_new0 (egl_window_class ());
|
|
if (!window || !egl_window_init (window, ctx, native_window))
|
|
goto error;
|
|
return window;
|
|
|
|
error:
|
|
egl_object_replace (&window, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
// Misc utility functions
|
|
|
|
void
|
|
egl_matrix_set_identity (gfloat m[16])
|
|
{
|
|
#define MAT(m,r,c) (m)[(c) * 4 + (r)]
|
|
MAT (m, 0, 0) = 1.0;
|
|
MAT (m, 0, 1) = 0.0;
|
|
MAT (m, 0, 2) = 0.0;
|
|
MAT (m, 0, 3) = 0.0;
|
|
MAT (m, 1, 0) = 0.0;
|
|
MAT (m, 1, 1) = 1.0;
|
|
MAT (m, 1, 2) = 0.0;
|
|
MAT (m, 1, 3) = 0.0;
|
|
MAT (m, 2, 0) = 0.0;
|
|
MAT (m, 2, 1) = 0.0;
|
|
MAT (m, 2, 2) = 1.0;
|
|
MAT (m, 2, 3) = 0.0;
|
|
MAT (m, 3, 0) = 0.0;
|
|
MAT (m, 3, 1) = 0.0;
|
|
MAT (m, 3, 2) = 0.0;
|
|
MAT (m, 3, 3) = 1.0;
|
|
#undef MAT
|
|
}
|
|
|
|
/**
|
|
* egl_create_texture:
|
|
* @ctx: the parent #EglContext object
|
|
* @target: the target to which the texture is bound
|
|
* @format: the format of the pixel data
|
|
* @width: the requested width, in pixels
|
|
* @height: the requested height, in pixels
|
|
*
|
|
* Creates a texture with the specified dimensions and @format. The
|
|
* internal format will be automatically derived from @format.
|
|
*
|
|
* Return value: the newly created texture name
|
|
*/
|
|
guint
|
|
egl_create_texture (EglContext * ctx, guint target, guint format,
|
|
guint width, guint height)
|
|
{
|
|
EglVTable *const vtable = egl_context_get_vtable (ctx, TRUE);
|
|
guint internal_format, texture, bytes_per_component;
|
|
|
|
internal_format = format;
|
|
switch (format) {
|
|
case GL_LUMINANCE:
|
|
bytes_per_component = 1;
|
|
break;
|
|
case GL_LUMINANCE_ALPHA:
|
|
bytes_per_component = 2;
|
|
break;
|
|
case GL_RGBA:
|
|
case GL_BGRA_EXT:
|
|
internal_format = GL_RGBA;
|
|
bytes_per_component = 4;
|
|
break;
|
|
default:
|
|
bytes_per_component = 0;
|
|
break;
|
|
}
|
|
g_assert (bytes_per_component > 0);
|
|
|
|
vtable->glGenTextures (1, &texture);
|
|
vtable->glBindTexture (target, texture);
|
|
|
|
if (width > 0 && height > 0)
|
|
vtable->glTexImage2D (target, 0, internal_format, width, height, 0,
|
|
format, GL_UNSIGNED_BYTE, NULL);
|
|
|
|
vtable->glTexParameteri (target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
vtable->glTexParameteri (target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
vtable->glTexParameteri (target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
vtable->glTexParameteri (target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
vtable->glPixelStorei (GL_UNPACK_ALIGNMENT, bytes_per_component);
|
|
|
|
return texture;
|
|
}
|
|
|
|
/**
|
|
* egl_destroy_texture:
|
|
* @ctx: the parent #EglContext object
|
|
* @texture: the texture name to delete
|
|
*
|
|
* Destroys the supplied @texture name.
|
|
*/
|
|
void
|
|
egl_destroy_texture (EglContext * ctx, guint texture)
|
|
{
|
|
EglVTable *const vtable = egl_context_get_vtable (ctx, TRUE);
|
|
|
|
vtable->glDeleteTextures (1, &texture);
|
|
}
|