mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-10-21 01:43:47 +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>
471 lines
14 KiB
Objective-C
471 lines
14 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);
|
|
GstStructure *gst_gl_context_eagl_get_config (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);
|
|
context_class->get_config = GST_DEBUG_FUNCPTR (gst_gl_context_eagl_get_config);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
enum EAGLFormat
|
|
{
|
|
FORMAT_RGBA8 = 1,
|
|
FORMAT_RGB565,
|
|
};
|
|
|
|
static GstStructure *
|
|
layer_config_to_structure (GstGLContextEagl *eagl, CAEAGLLayer * layer)
|
|
{
|
|
GstStructure *ret;
|
|
NSDictionary *drawableProps = [layer drawableProperties];
|
|
NSString *color_format;
|
|
enum EAGLFormat eagl_format = FORMAT_RGBA8;
|
|
|
|
ret = gst_structure_new (GST_GL_CONFIG_STRUCTURE_NAME,
|
|
GST_GL_CONFIG_STRUCTURE_SET_ARGS(PLATFORM, GstGLPlatform, GST_GL_PLATFORM_EAGL),
|
|
NULL);
|
|
|
|
color_format = [drawableProps objectForKey:kEAGLDrawablePropertyColorFormat];
|
|
if (!color_format)
|
|
color_format = [layer contentsFormat];
|
|
|
|
if (!color_format) {
|
|
GST_WARNING_OBJECT (eagl, "Could not retrieve color format from layer %p", layer);
|
|
goto failure;
|
|
}
|
|
|
|
if (color_format == kEAGLColorFormatRGBA8 || color_format == kCAContentsFormatRGBA8Uint) {
|
|
eagl_format = FORMAT_RGBA8;
|
|
} else if (color_format == kEAGLColorFormatRGB565) {
|
|
eagl_format = FORMAT_RGB565;
|
|
} else {
|
|
GST_WARNING_OBJECT (eagl, "unknown drawable format: %s", [color_format UTF8String]);
|
|
goto failure;
|
|
}
|
|
|
|
/* XXX: defaults chosen by _update_layer() */
|
|
gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS(DEPTH_SIZE, int, 16), NULL);
|
|
gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS(STENCIL_SIZE, int, 0), NULL);
|
|
|
|
switch (eagl_format) {
|
|
case FORMAT_RGBA8:
|
|
gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS(RED_SIZE, int, 8), NULL);
|
|
gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS(GREEN_SIZE, int, 8), NULL);
|
|
gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS(BLUE_SIZE, int, 8), NULL);
|
|
gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS(ALPHA_SIZE, int, 8), NULL);
|
|
break;
|
|
case FORMAT_RGB565:
|
|
gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS(RED_SIZE, int, 5), NULL);
|
|
gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS(GREEN_SIZE, int, 6), NULL);
|
|
gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS(BLUE_SIZE, int, 5), NULL);
|
|
gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS(ALPHA_SIZE, int, 0), NULL);
|
|
break;
|
|
default:
|
|
GST_WARNING_OBJECT (eagl, "Unhandled format!");
|
|
goto failure;
|
|
}
|
|
|
|
return ret;
|
|
|
|
failure:
|
|
gst_structure_free (ret);
|
|
return NULL;
|
|
}
|
|
|
|
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);
|
|
GstStructure *fmt;
|
|
|
|
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;
|
|
|
|
fmt = layer_config_to_structure (context_eagl, eagl_layer);
|
|
if (fmt) {
|
|
GST_DEBUG_OBJECT (context_eagl, "chosen config %" GST_PTR_FORMAT, fmt);
|
|
gst_structure_free (fmt);
|
|
}
|
|
|
|
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];
|
|
}
|
|
|
|
GstStructure *
|
|
gst_gl_context_eagl_get_config (GstGLContext * context)
|
|
{
|
|
GstGLContextEagl *eagl = GST_GL_CONTEXT_EAGL (context);
|
|
|
|
if (!eagl->priv->eagl_layer)
|
|
return NULL;
|
|
|
|
return layer_config_to_structure (eagl, (__bridge CAEAGLLayer *) eagl->priv->eagl_layer);
|
|
}
|