mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-28 19:20:35 +00:00
c506adc950
This means we cannot access [view layer] or view.bounds from the OpenGL thread. This also means that we need to call the main thread when setting the window handle. However, we cannot perform that synchronously as that may deadlock with the application performing the set_window_handle() call. We need to defer the actual update and run it asynchronously and wait for the window handle update internally at each point it is needed. Fixes: https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/-/issues/372 Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/-/merge_requests/681>
386 lines
12 KiB
Objective-C
386 lines
12 KiB
Objective-C
/*
|
|
* GStreamer
|
|
* Copyright (C) 2014 Sebastian Dröge <sebastian@centricular.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.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#import <OpenGLES/EAGL.h>
|
|
#import <QuartzCore/QuartzCore.h>
|
|
#import <UIKit/UIKit.h>
|
|
#include <OpenGLES/ES2/gl.h>
|
|
|
|
#include "gstglcontext_eagl.h"
|
|
#include "../gstglcontext_private.h"
|
|
#include "gstglios_utils.h"
|
|
|
|
#define GST_GL_CONTEXT_EAGL_CONTEXT(obj) \
|
|
((__bridge EAGLContext *)(obj->priv->eagl_context))
|
|
#define GST_GL_CONTEXT_EAGL_LAYER(obj) \
|
|
((__bridge CAEAGLLayer *)(obj->priv->eagl_layer))
|
|
|
|
#define GST_CAT_DEFAULT gst_gl_context_debug
|
|
|
|
static gboolean gst_gl_context_eagl_create_context (GstGLContext * context,
|
|
GstGLAPI gl_api, GstGLContext * other_context, GError ** error);
|
|
static void gst_gl_context_eagl_destroy_context (GstGLContext * context);
|
|
static gboolean gst_gl_context_eagl_choose_format (GstGLContext * context,
|
|
GError ** error);
|
|
static guintptr gst_gl_context_eagl_get_gl_context (GstGLContext * window);
|
|
static gboolean gst_gl_context_eagl_activate (GstGLContext * context,
|
|
gboolean activate);
|
|
static void gst_gl_context_eagl_swap_buffers (GstGLContext * context);
|
|
static GstGLAPI gst_gl_context_eagl_get_gl_api (GstGLContext * context);
|
|
static GstGLPlatform gst_gl_context_eagl_get_gl_platform (GstGLContext *
|
|
context);
|
|
|
|
struct _GstGLContextEaglPrivate
|
|
{
|
|
gpointer eagl_context;
|
|
|
|
/* Used if we render to a window */
|
|
gpointer eagl_layer;
|
|
GLuint framebuffer;
|
|
GLuint color_renderbuffer;
|
|
GLuint depth_renderbuffer;
|
|
};
|
|
|
|
G_DEFINE_TYPE_WITH_PRIVATE (GstGLContextEagl, gst_gl_context_eagl,
|
|
GST_TYPE_GL_CONTEXT);
|
|
|
|
static void
|
|
gst_gl_context_eagl_class_init (GstGLContextEaglClass * klass)
|
|
{
|
|
GstGLContextClass *context_class;
|
|
|
|
context_class = (GstGLContextClass *) klass;
|
|
|
|
context_class->destroy_context =
|
|
GST_DEBUG_FUNCPTR (gst_gl_context_eagl_destroy_context);
|
|
context_class->create_context =
|
|
GST_DEBUG_FUNCPTR (gst_gl_context_eagl_create_context);
|
|
context_class->choose_format =
|
|
GST_DEBUG_FUNCPTR (gst_gl_context_eagl_choose_format);
|
|
context_class->get_gl_context =
|
|
GST_DEBUG_FUNCPTR (gst_gl_context_eagl_get_gl_context);
|
|
context_class->activate = GST_DEBUG_FUNCPTR (gst_gl_context_eagl_activate);
|
|
context_class->swap_buffers =
|
|
GST_DEBUG_FUNCPTR (gst_gl_context_eagl_swap_buffers);
|
|
context_class->get_gl_api =
|
|
GST_DEBUG_FUNCPTR (gst_gl_context_eagl_get_gl_api);
|
|
context_class->get_gl_platform =
|
|
GST_DEBUG_FUNCPTR (gst_gl_context_eagl_get_gl_platform);
|
|
}
|
|
|
|
static void
|
|
gst_gl_context_eagl_init (GstGLContextEagl * context)
|
|
{
|
|
context->priv = gst_gl_context_eagl_get_instance_private (context);
|
|
}
|
|
|
|
/* Must be called in the gl thread */
|
|
GstGLContextEagl *
|
|
gst_gl_context_eagl_new (GstGLDisplay * display)
|
|
{
|
|
GstGLContextEagl *context;
|
|
|
|
/* there isn't actually a display type for eagl yet? */
|
|
context = g_object_new (GST_TYPE_GL_CONTEXT_EAGL, NULL);
|
|
gst_object_ref_sink (context);
|
|
|
|
return context;
|
|
}
|
|
|
|
void
|
|
gst_gl_context_eagl_resize (GstGLContextEagl * eagl_context)
|
|
{
|
|
int width, height;
|
|
|
|
glBindRenderbuffer (GL_RENDERBUFFER, eagl_context->priv->color_renderbuffer);
|
|
[GST_GL_CONTEXT_EAGL_CONTEXT(eagl_context)
|
|
renderbufferStorage:GL_RENDERBUFFER
|
|
fromDrawable:GST_GL_CONTEXT_EAGL_LAYER(eagl_context)];
|
|
glGetRenderbufferParameteriv (GL_RENDERBUFFER,
|
|
GL_RENDERBUFFER_WIDTH, &width);
|
|
glGetRenderbufferParameteriv (GL_RENDERBUFFER,
|
|
GL_RENDERBUFFER_HEIGHT, &height);
|
|
glBindRenderbuffer (GL_RENDERBUFFER, eagl_context->priv->depth_renderbuffer);
|
|
glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width,
|
|
height);
|
|
}
|
|
|
|
static void
|
|
gst_gl_context_eagl_release_layer (GstGLContext * context)
|
|
{
|
|
GstGLContextEagl *context_eagl;
|
|
|
|
context_eagl = GST_GL_CONTEXT_EAGL (context);
|
|
|
|
if (context_eagl->priv->eagl_layer) {
|
|
gst_gl_context_eagl_activate (context, TRUE);
|
|
|
|
[GST_GL_CONTEXT_EAGL_CONTEXT(context_eagl)
|
|
renderbufferStorage:GL_RENDERBUFFER
|
|
fromDrawable:nil];
|
|
|
|
glDeleteFramebuffers (1, &context_eagl->priv->framebuffer);
|
|
context_eagl->priv->framebuffer = 0;
|
|
|
|
glDeleteRenderbuffers (1, &context_eagl->priv->depth_renderbuffer);
|
|
context_eagl->priv->depth_renderbuffer = 0;
|
|
glDeleteRenderbuffers (1, &context_eagl->priv->color_renderbuffer);
|
|
context_eagl->priv->color_renderbuffer = 0;
|
|
|
|
CFRelease (context_eagl->priv->eagl_layer);
|
|
context_eagl->priv->eagl_layer = NULL;
|
|
gst_gl_context_eagl_activate (context, FALSE);
|
|
}
|
|
}
|
|
|
|
void
|
|
gst_gl_context_eagl_update_layer (GstGLContext * context, gpointer layer)
|
|
{
|
|
GLuint framebuffer;
|
|
GLuint color_renderbuffer;
|
|
GLuint depth_renderbuffer;
|
|
GLint width;
|
|
GLint height;
|
|
CAEAGLLayer *eagl_layer;
|
|
GLenum status;
|
|
GstGLContextEagl *context_eagl = GST_GL_CONTEXT_EAGL (context);
|
|
GstGLContextEaglPrivate *priv = context_eagl->priv;
|
|
GstGLWindow *window = gst_gl_context_get_window (context);
|
|
|
|
if (!layer || !gst_gl_window_get_window_handle (window)) {
|
|
GST_INFO_OBJECT (context, "window handle not set yet, not updating layer");
|
|
goto out;
|
|
}
|
|
|
|
if (priv->eagl_layer)
|
|
gst_gl_context_eagl_release_layer (context);
|
|
|
|
eagl_layer = (__bridge CAEAGLLayer *) layer;
|
|
[EAGLContext setCurrentContext:GST_GL_CONTEXT_EAGL_CONTEXT(context_eagl)];
|
|
|
|
/* Allocate framebuffer */
|
|
glGenFramebuffers (1, &framebuffer);
|
|
glBindFramebuffer (GL_FRAMEBUFFER, framebuffer);
|
|
/* Allocate color render buffer */
|
|
glGenRenderbuffers (1, &color_renderbuffer);
|
|
glBindRenderbuffer (GL_RENDERBUFFER, color_renderbuffer);
|
|
[GST_GL_CONTEXT_EAGL_CONTEXT(context_eagl) renderbufferStorage: GL_RENDERBUFFER fromDrawable:eagl_layer];
|
|
glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
|
GL_RENDERBUFFER, color_renderbuffer);
|
|
/* Get renderbuffer width/height */
|
|
glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH,
|
|
&width);
|
|
glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT,
|
|
&height);
|
|
/* allocate depth render buffer */
|
|
glGenRenderbuffers (1, &depth_renderbuffer);
|
|
glBindRenderbuffer (GL_RENDERBUFFER, depth_renderbuffer);
|
|
glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width,
|
|
height);
|
|
glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
|
|
GL_RENDERBUFFER, depth_renderbuffer);
|
|
|
|
/* check creation status */
|
|
status = glCheckFramebufferStatus (GL_FRAMEBUFFER);
|
|
if (status != GL_FRAMEBUFFER_COMPLETE) {
|
|
GST_ERROR ("Failed to make complete framebuffer object %x", status);
|
|
goto out;
|
|
}
|
|
glBindRenderbuffer (GL_RENDERBUFFER, 0);
|
|
glBindFramebuffer (GL_FRAMEBUFFER, 0);
|
|
|
|
priv->eagl_layer = (__bridge_retained gpointer) eagl_layer;
|
|
priv->framebuffer = framebuffer;
|
|
priv->color_renderbuffer = color_renderbuffer;
|
|
priv->depth_renderbuffer = depth_renderbuffer;
|
|
|
|
out:
|
|
if (window)
|
|
gst_object_unref (window);
|
|
if (layer)
|
|
CFRelease (layer);
|
|
}
|
|
|
|
static gboolean
|
|
gst_gl_context_eagl_create_context (GstGLContext * context, GstGLAPI gl_api,
|
|
GstGLContext * other_context, GError ** error)
|
|
{
|
|
GstGLContextEagl *context_eagl = GST_GL_CONTEXT_EAGL (context);
|
|
GstGLContextEaglPrivate *priv = context_eagl->priv;
|
|
GstGLWindow *window;
|
|
GstGLWindowEagl *window_eagl;
|
|
gpointer layer;
|
|
EAGLSharegroup *share_group;
|
|
|
|
if (other_context) {
|
|
EAGLContext *external_gl_context = (__bridge EAGLContext *)(void *)
|
|
gst_gl_context_get_gl_context (other_context);
|
|
share_group = [external_gl_context sharegroup];
|
|
} else {
|
|
share_group = nil;
|
|
}
|
|
|
|
priv->eagl_context = (__bridge_retained gpointer)[[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3 sharegroup:share_group];
|
|
if (!priv->eagl_context) {
|
|
priv->eagl_context = (__bridge_retained gpointer)[[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2 sharegroup:share_group];
|
|
}
|
|
if (!priv->eagl_context) {
|
|
g_set_error_literal (error, GST_GL_CONTEXT_ERROR,
|
|
GST_GL_CONTEXT_ERROR_CREATE_CONTEXT,
|
|
"Failed to create OpenGL ES context");
|
|
return FALSE;
|
|
}
|
|
|
|
priv->eagl_layer = NULL;
|
|
priv->framebuffer = 0;
|
|
priv->color_renderbuffer = 0;
|
|
priv->depth_renderbuffer = 0;
|
|
|
|
GST_INFO_OBJECT (context, "context created, updating layer");
|
|
window = gst_gl_context_get_window (context);
|
|
if (!window) {
|
|
g_set_error_literal (error, GST_GL_CONTEXT_ERROR,
|
|
GST_GL_CONTEXT_ERROR_CREATE_CONTEXT,
|
|
"No window to render into");
|
|
return FALSE;
|
|
}
|
|
|
|
window_eagl = GST_GL_WINDOW_EAGL (window);
|
|
layer = gst_gl_window_eagl_get_layer (window_eagl);
|
|
gst_gl_context_eagl_update_layer (context, layer);
|
|
|
|
gst_object_unref (window);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gst_gl_context_eagl_destroy_context (GstGLContext * context)
|
|
{
|
|
GstGLContextEagl *context_eagl;
|
|
|
|
context_eagl = GST_GL_CONTEXT_EAGL (context);
|
|
|
|
if (!context_eagl->priv->eagl_context)
|
|
return;
|
|
|
|
gst_gl_context_eagl_release_layer (context);
|
|
|
|
CFRelease(context_eagl->priv->eagl_context);
|
|
context_eagl->priv->eagl_context = NULL;
|
|
}
|
|
|
|
static gboolean
|
|
gst_gl_context_eagl_choose_format (GstGLContext * context, GError ** error)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
static guintptr
|
|
gst_gl_context_eagl_get_gl_context (GstGLContext * context)
|
|
{
|
|
return (guintptr) GST_GL_CONTEXT_EAGL (context)->priv->eagl_context;
|
|
}
|
|
|
|
void
|
|
gst_gl_context_eagl_prepare_draw (GstGLContextEagl * context)
|
|
{
|
|
if (!context->priv->eagl_layer)
|
|
return;
|
|
|
|
glBindFramebuffer (GL_FRAMEBUFFER, context->priv->framebuffer);
|
|
glBindRenderbuffer (GL_RENDERBUFFER, context->priv->color_renderbuffer);
|
|
}
|
|
|
|
void
|
|
gst_gl_context_eagl_finish_draw (GstGLContextEagl * context)
|
|
{
|
|
if (!context->priv->eagl_layer)
|
|
return;
|
|
|
|
glBindRenderbuffer (GL_RENDERBUFFER, 0);
|
|
glBindFramebuffer (GL_FRAMEBUFFER, 0);
|
|
}
|
|
|
|
static void
|
|
gst_gl_context_eagl_swap_buffers (GstGLContext * context)
|
|
{
|
|
GstGLContextEagl *context_eagl;
|
|
|
|
context_eagl = GST_GL_CONTEXT_EAGL (context);
|
|
|
|
if (!context_eagl->priv->eagl_layer)
|
|
return;
|
|
|
|
[GST_GL_CONTEXT_EAGL_CONTEXT(context_eagl) presentRenderbuffer:GL_RENDERBUFFER];
|
|
}
|
|
|
|
static gboolean
|
|
gst_gl_context_eagl_activate (GstGLContext * context, gboolean activate)
|
|
{
|
|
GstGLContextEagl *context_eagl;
|
|
|
|
context_eagl = GST_GL_CONTEXT_EAGL (context);
|
|
|
|
if (activate) {
|
|
EAGLContext *cur_ctx =[EAGLContext currentContext];
|
|
|
|
if (cur_ctx == context_eagl->priv->eagl_context) {
|
|
GST_DEBUG ("Already attached the context to thread %p", g_thread_self ());
|
|
return TRUE;
|
|
}
|
|
|
|
GST_DEBUG ("Attaching context to thread %p", g_thread_self ());
|
|
if ([EAGLContext setCurrentContext:GST_GL_CONTEXT_EAGL_CONTEXT(context_eagl)] == NO) {
|
|
GST_ERROR ("Couldn't make context current");
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
GST_DEBUG ("Detaching context from thread %p", g_thread_self ());
|
|
if ([EAGLContext setCurrentContext:nil] == NO) {
|
|
GST_ERROR ("Couldn't unbind context");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static GstGLAPI
|
|
gst_gl_context_eagl_get_gl_api (GstGLContext * context)
|
|
{
|
|
return GST_GL_API_GLES2;
|
|
}
|
|
|
|
static GstGLPlatform
|
|
gst_gl_context_eagl_get_gl_platform (GstGLContext * context)
|
|
{
|
|
return GST_GL_PLATFORM_EAGL;
|
|
}
|
|
|
|
guintptr
|
|
gst_gl_context_eagl_get_current_context (void)
|
|
{
|
|
return (guintptr) [EAGLContext currentContext];
|
|
}
|