[330/906] Can now share textures with an external gl context

The external opengl context must be specify when creating
our OpenGL context (glx) or just after (wgl).
When calling glXCreateContext or wglShareLists, the
external opengl context must not be current.
Then our gl context can be current in the gl thread while
the external gl context is current in an other thread.
See tests/examples/clutter/cluttershare.c
This commit is contained in:
Julien Isorce 2009-04-18 13:57:44 +02:00 committed by Matthew Waters
parent b47fc234ef
commit 87a1652216
14 changed files with 287 additions and 50 deletions

View file

@ -158,6 +158,9 @@ gst_gl_display_init (GstGLDisplay * display, GstGLDisplayClass * klass)
display->upload_data_height = 0;
display->upload_data = NULL;
//foreign gl context
display->external_gl_context = 0;
//filter gen fbo
display->gen_fbo_width = 0;
display->gen_fbo_height = 0;
@ -527,7 +530,8 @@ gst_gl_display_thread_create_context (GstGLDisplay * display)
GLenum err = 0;
display->gl_window =
gst_gl_window_new (display->upload_width, display->upload_height);
gst_gl_window_new (display->upload_width, display->upload_height,
display->external_gl_context);
if (!display->gl_window) {
display->isAlive = FALSE;
@ -2065,12 +2069,13 @@ gst_gl_display_new (void)
* Called by the first gl element of a video/x-raw-gl flow */
void
gst_gl_display_create_context (GstGLDisplay * display,
GLint width, GLint height)
GLint width, GLint height, guint64 external_gl_context)
{
gst_gl_display_lock (display);
display->upload_width = width;
display->upload_height = height;
display->external_gl_context = external_gl_context;
display->gl_thread = g_thread_create (
(GThreadFunc) gst_gl_display_thread_create_context, display, TRUE, NULL);

View file

@ -135,6 +135,9 @@ struct _GstGLDisplay
gint upload_data_height;
gpointer upload_data;
//foreign gl context
guint64 external_gl_context;
//filter gen fbo
GLuint gen_fbo_width;
GLuint gen_fbo_height;
@ -236,7 +239,7 @@ GType gst_gl_display_get_type (void);
GstGLDisplay *gst_gl_display_new (void);
void gst_gl_display_create_context (GstGLDisplay * display,
GLint width, GLint height);
GLint width, GLint height, guint64 external_gl_context);
gboolean gst_gl_display_redisplay (GstGLDisplay * display, GLuint texture,
gint width, gint height);

View file

@ -79,10 +79,9 @@ struct _GstGLWindowClass {
GQuark gst_gl_window_error_quark (void);
GType gst_gl_window_get_type (void);
GstGLWindow * gst_gl_window_new (gint width, gint height);
GstGLWindow * gst_gl_window_new (gint width, gint height, guint64 external_gl_context);
void gst_gl_window_set_external_window_id (GstGLWindow *window, guint64 id);
void gst_gl_window_set_external_gl_context (GstGLWindow *window, guint64 context);
void gst_gl_window_set_draw_callback (GstGLWindow *window, GstGLWindowCB callback, gpointer data);
void gst_gl_window_set_resize_callback (GstGLWindow *window, GstGLWindowCB2 callback, gpointer data);
void gst_gl_window_set_close_callback (GstGLWindow *window, GstGLWindowCB callback, gpointer data);

View file

@ -146,7 +146,7 @@ gst_gl_window_init (GstGLWindow * window)
/* Must be called in the gl thread */
GstGLWindow *
gst_gl_window_new (gint width, gint height)
gst_gl_window_new (gint width, gint height, guint64 external_gl_context)
{
GstGLWindow *window = g_object_new (GST_GL_TYPE_WINDOW, NULL);
GstGLWindowPrivate *priv = window->priv;
@ -229,12 +229,6 @@ gst_gl_window_set_external_window_id (GstGLWindow * window, guint64 id)
g_debug ("failed to register current thread, cannot set external window id");
}
void
gst_gl_window_set_external_gl_context (GstGLWindow * window, guint64 context)
{
g_warning ("gst_gl_window_set_external_gl_context: not implemented\n");
}
/* Must be called in the gl thread */
void
gst_gl_window_set_draw_callback (GstGLWindow * window, GstGLWindowCB callback,

View file

@ -51,6 +51,7 @@ struct _GstGLWindowPrivate
HWND internal_win_id;
HDC device;
HGLRC gl_context;
HGLRC external_gl_context;
GstGLWindowCB draw_cb;
gpointer draw_data;
GstGLWindowCB2 resize_cb;
@ -143,7 +144,7 @@ gst_gl_window_init (GstGLWindow * window)
/* Must be called in the gl thread */
GstGLWindow *
gst_gl_window_new (gint width, gint height)
gst_gl_window_new (gint width, gint height, guint64 external_gl_context)
{
GstGLWindow *window = g_object_new (GST_GL_TYPE_WINDOW, NULL);
GstGLWindowPrivate *priv = window->priv;
@ -160,6 +161,7 @@ gst_gl_window_new (gint width, gint height)
priv->internal_win_id = 0;
priv->device = 0;
priv->gl_context = 0;
priv->external_gl_context = (HGLRC) external_gl_context;
priv->draw_cb = NULL;
priv->draw_data = NULL;
priv->resize_cb = NULL;
@ -226,12 +228,6 @@ gst_gl_window_set_external_window_id (GstGLWindow * window, guint64 id)
rect.bottom, FALSE);
}
void
gst_gl_window_set_external_gl_context (GstGLWindow * window, guint64 context)
{
g_warning ("gst_gl_window_set_external_gl_context: not implemented\n");
}
/* Must be called in the gl thread */
void
gst_gl_window_set_draw_callback (GstGLWindow * window, GstGLWindowCB callback,
@ -411,6 +407,14 @@ window_proc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
if (!wglMakeCurrent (priv->device, priv->gl_context))
g_debug ("failed to make opengl context current %d, %x\r\n", hWnd,
GetLastError ());
if (priv->external_gl_context) {
if (!wglShareLists (priv->gl_context, priv->external_gl_context))
g_debug ("failed to share opengl context %lud with %lud\n",
priv->gl_context, priv->external_gl_context);
else
g_debug ("share opengl context succeed\n");
}
}
SetProp (hWnd, "gl_window", window);

View file

@ -48,6 +48,7 @@ struct _GstGLWindowPrivate
EGLDisplay display;
EGLSurface surface;
EGLContext gl_context;
EGLContext external_gl_context;
GstGLWindowCB draw_cb;
gpointer draw_data;
GstGLWindowCB2 resize_cb;
@ -140,7 +141,7 @@ gst_gl_window_init (GstGLWindow * window)
/* Must be called in the gl thread */
GstGLWindow *
gst_gl_window_new (gint width, gint height)
gst_gl_window_new (gint width, gint height, guint64 external_gl_context)
{
GstGLWindow *window = g_object_new (GST_GL_TYPE_WINDOW, NULL);
GstGLWindowPrivate *priv = window->priv;
@ -158,6 +159,7 @@ gst_gl_window_new (gint width, gint height)
priv->display = 0;
priv->surface = 0;
priv->gl_context = 0;
priv->external_gl_context = (EGLContext) external_gl_context;
priv->draw_cb = NULL;
priv->draw_data = NULL;
priv->resize_cb = NULL;
@ -224,12 +226,6 @@ gst_gl_window_set_external_window_id (GstGLWindow * window, guint64 id)
rect.bottom, FALSE);
}
void
gst_gl_window_set_external_gl_context (GstGLWindow * window, guint64 context)
{
g_warning ("gst_gl_window_set_external_gl_context: not implemented\n");
}
/* Must be called in the gl thread */
void
gst_gl_window_set_draw_callback (GstGLWindow * window, GstGLWindowCB callback,
@ -402,7 +398,7 @@ window_proc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
priv->surface, hWnd, EGLErrorString ());
priv->gl_context =
eglCreateContext (priv->display, config, EGL_NO_CONTEXT,
eglCreateContext (priv->display, config, priv->external_gl_context,
contextAttribs);
if (priv->gl_context != EGL_NO_CONTEXT)
g_debug ("gl context created: %d\n", priv->gl_context);

View file

@ -242,7 +242,7 @@ gst_gl_window_init (GstGLWindow * window)
/* Must be called in the gl thread */
GstGLWindow *
gst_gl_window_new (gint width, gint height)
gst_gl_window_new (gint width, gint height, guint64 external_gl_context)
{
GstGLWindow *window = g_object_new (GST_GL_TYPE_WINDOW, NULL);
GstGLWindowPrivate *priv = window->priv;
@ -379,7 +379,8 @@ gst_gl_window_new (gint width, gint height)
XSetWMProtocols (priv->device, priv->internal_win_id, wm_atoms, 2);
priv->gl_context =
glXCreateContext (priv->device, priv->visual_info, NULL, TRUE);
glXCreateContext (priv->device, priv->visual_info,
(GLXContext) external_gl_context, TRUE);
g_debug ("gl context id: %ld\n", (gulong) priv->gl_context);
@ -446,12 +447,6 @@ gst_gl_window_set_external_window_id (GstGLWindow * window, guint64 id)
}
}
void
gst_gl_window_set_external_gl_context (GstGLWindow * window, guint64 context)
{
g_warning ("gst_gl_window_set_external_gl_context: not implemented\n");
}
void
gst_gl_window_set_draw_callback (GstGLWindow * window, GstGLWindowCB callback,
gpointer data)

View file

@ -247,7 +247,7 @@ gst_gl_window_init (GstGLWindow * window)
/* Must be called in the gl thread */
GstGLWindow *
gst_gl_window_new (gint width, gint height)
gst_gl_window_new (gint width, gint height, guint64 external_gl_context)
{
GstGLWindow *window = g_object_new (GST_GL_TYPE_WINDOW, NULL);
GstGLWindowPrivate *priv = window->priv;
@ -395,7 +395,7 @@ gst_gl_window_new (gint width, gint height)
(gulong) priv->gl_display, EGLErrorString ());
priv->gl_context =
eglCreateContext (priv->gl_display, config, EGL_NO_CONTEXT,
eglCreateContext (priv->gl_display, config, (EGLContext) external_gl_context,
context_attrib);
if (priv->gl_context != EGL_NO_CONTEXT)
g_debug ("gl context created: %ld\n", (gulong) priv->gl_context);
@ -448,12 +448,6 @@ gst_gl_window_set_external_window_id (GstGLWindow * window, guint64 id)
}
}
void
gst_gl_window_set_external_gl_context (GstGLWindow * window, guint64 context)
{
g_warning ("gst_gl_window_set_external_gl_context: not implemented\n");
}
void
gst_gl_window_set_draw_callback (GstGLWindow * window, GstGLWindowCB callback,
gpointer data)

View file

@ -395,7 +395,7 @@ gst_gl_colorscale_set_caps (GstBaseTransform * bt, GstCaps * incaps,
//init unvisible opengl context
gst_gl_display_create_context (colorscale->display,
colorscale->output_video_width, colorscale->output_video_height);
colorscale->output_video_width, colorscale->output_video_height, 0);
//blocking call, init colorspace conversion if needed
gst_gl_display_init_upload (colorscale->display,

View file

@ -514,7 +514,7 @@ gst_glimage_sink_render (GstBaseSink * bsink, GstBuffer * buf)
//init opengl context
gst_gl_display_create_context (glimage_sink->display,
glimage_sink->width, glimage_sink->height);
glimage_sink->width, glimage_sink->height, 0);
if (glimage_sink->window_id)
gst_gl_display_set_window_id (glimage_sink->display,

View file

@ -364,7 +364,7 @@ gst_gl_test_src_setcaps (GstBaseSrc * bsrc, GstCaps * caps)
gltestsrc->display = gst_gl_display_new ();
gst_gl_display_create_context (gltestsrc->display,
gltestsrc->width, gltestsrc->height);
gltestsrc->width, gltestsrc->height, 0);
gst_gl_display_gen_fbo (gltestsrc->display, gltestsrc->width,
gltestsrc->height, &gltestsrc->fbo, &gltestsrc->depthbuffer);

View file

@ -123,7 +123,8 @@ static GstStaticPadTemplate gst_gl_upload_sink_pad_template =
/* Properties */
enum
{
PROP_0
PROP_0,
PROP_EXTERNAL_OPENGL_CONTEXT
};
#define DEBUG_INIT(bla) \
@ -186,6 +187,12 @@ gst_gl_upload_class_init (GstGLUploadClass * klass)
GST_BASE_TRANSFORM_CLASS (klass)->get_unit_size = gst_gl_upload_get_unit_size;
GST_BASE_TRANSFORM_CLASS (klass)->prepare_output_buffer =
gst_gl_upload_prepare_output_buffer;
g_object_class_install_property (gobject_class, PROP_EXTERNAL_OPENGL_CONTEXT,
g_param_spec_uint64 ("external_opengl_context",
"External OpenGL context",
"Give an external OpenGL context with which to share textures",
0, _UI64_MAX, 0, G_PARAM_WRITABLE));
}
static void
@ -198,9 +205,14 @@ static void
gst_gl_upload_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
//GstGLUpload* upload = GST_GL_UPLOAD (object);
GstGLUpload *upload = GST_GL_UPLOAD (object);
switch (prop_id) {
case PROP_EXTERNAL_OPENGL_CONTEXT:
{
upload->external_gl_context = g_value_get_uint64 (value);
break;
}
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -227,6 +239,7 @@ gst_gl_upload_reset (GstGLUpload * upload)
g_object_unref (upload->display);
upload->display = NULL;
}
upload->external_gl_context = 0;
}
static gboolean
@ -438,7 +451,8 @@ gst_gl_upload_set_caps (GstBaseTransform * bt, GstCaps * incaps,
//init unvisible opengl context
gst_gl_display_create_context (upload->display,
upload->gl_width, upload->gl_height);
upload->gl_width, upload->gl_height,
upload->external_gl_context);
//init colorspace conversion if needed
gst_gl_display_init_upload (upload->display, upload->video_format,

View file

@ -53,7 +53,8 @@ struct _GstGLUpload
gint video_width;
gint video_height;
gint gl_width;
gint gl_height;
gint gl_height;
guint64 external_gl_context;
};
struct _GstGLUploadClass

View file

@ -0,0 +1,232 @@
/*
* GStreamer
* Copyright (C) 2009 Julien Isorce <julien.isorce@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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <GL/glew.h>
#ifdef WIN32
#include <GL/wglew.h>
#else
#include <GL/glxew.h>
#endif
#include <GL/gl.h>
#include <clutter/clutter.h>
#include <gst/gst.h>
/* This example shows how to use textures that come from a
* gst-plugins-gl pipeline, into the clutter framework
*/
/* hack */
typedef struct _GstGLBuffer GstGLBuffer;
struct _GstGLBuffer {
GstBuffer buffer;
GObject *obj;
gint width;
gint height;
GLuint texture;
};
/* clutter scene */
ClutterActor*
setup_stage (ClutterStage * stage)
{
/* timeline */
ClutterTimeline *timeline = clutter_timeline_new (120, 50);
clutter_timeline_set_loop (timeline, TRUE);
clutter_timeline_start (timeline);
/* effect template */
ClutterEffectTemplate *effect_template = clutter_effect_template_new (timeline, CLUTTER_ALPHA_SINE_INC);
/* texture actor */
ClutterActor *texture_actor = clutter_texture_new ();
clutter_container_add_actor (CLUTTER_CONTAINER (stage), texture_actor);
clutter_actor_set_position (texture_actor, 300, 170);
clutter_actor_set_scale (texture_actor, 0.8, 0.8);
clutter_effect_rotate (effect_template, texture_actor,
CLUTTER_Z_AXIS, 180.0,
50, 50, 0,
CLUTTER_ROTATE_CW,
NULL, NULL);
clutter_actor_show (texture_actor);
g_object_set_data (G_OBJECT (texture_actor), "stage", stage);
g_object_unref (effect_template);
g_object_unref (timeline);
/* rectangle actor */
ClutterColor rect_color = { 125, 50, 200, 255 };
ClutterActor* actorRect = clutter_rectangle_new_with_color (&rect_color);
clutter_container_add_actor (CLUTTER_CONTAINER (stage), actorRect);
clutter_actor_set_size (actorRect, 50, 50);
clutter_actor_set_position (actorRect, 300, 300);
clutter_effect_rotate (effect_template, actorRect,
CLUTTER_Z_AXIS, 180.0,
25, 25, 0,
CLUTTER_ROTATE_CW,
NULL, NULL);
clutter_actor_show (actorRect);
return texture_actor;
}
/* put a gst gl buffer in the texture actor */
gboolean
update_texture_actor (gpointer data)
{
GstGLBuffer *gst_gl_buf = (GstGLBuffer *) data;
/* Create a cogl texture from the gst gl texture */
glEnable (GL_TEXTURE_2D);
glBindTexture (GL_TEXTURE_2D, gst_gl_buf->texture);
CoglHandle cogl_texture = cogl_texture_new_from_foreign (gst_gl_buf->texture,
GL_TEXTURE_2D, gst_gl_buf->width, gst_gl_buf->height, 0, 0, COGL_PIXEL_FORMAT_RGBA_8888);
cogl_texture_set_filters (cogl_texture, GL_LINEAR, GL_LINEAR);
glDisable (GL_TEXTURE_2D);
/* Previous cogl texture is replaced and so its ref counter discreases to 0.
* According to the source code, glDeleteTexture is not called when the previous
* ref counter of the previous cogl texture is reaching 0 because is_foreign is TRUE */
ClutterTexture *texture_actor = g_type_get_qdata (G_TYPE_FROM_INSTANCE (gst_gl_buf), g_quark_from_string ("texture_actor"));
clutter_texture_set_cogl_texture (CLUTTER_TEXTURE (texture_actor), cogl_texture);
cogl_texture_unref (cogl_texture);
/* Keep a ref on the current gst_gl_buffer associated to the texture_actor.
* The old gst_gl_buffer is unref */
g_object_set_data_full (G_OBJECT (texture_actor), "gst_gl_buffer",
gst_gl_buf, (GDestroyNotify) gst_mini_object_unref);
/* we can now show the clutter scene if not yet visible */
ClutterActor *stage = g_object_get_data (G_OBJECT (texture_actor), "stage");
if (!CLUTTER_ACTOR_IS_VISIBLE (stage))
clutter_actor_show_all (stage);
return FALSE;
}
/* fakesink handoff callback */
void
on_gst_buffer (GstElement* element, GstBuffer* buf, GstPad* pad, ClutterActor* texture_actor)
{
/* increase ref because our pipeline and clutter scene have not a same framerate */
gst_buffer_ref (buf);
/* Just to avoid a global variable of texture_actor
* Texture_actor is not null because callback connection is set after the
* texture_actor was being setted up */
g_assert (texture_actor);
g_type_set_qdata (G_TYPE_FROM_INSTANCE (buf), g_quark_from_string ("texture_actor"), texture_actor);
/* Here we are in the pipeline thread. It means that this thread may be
* not the same as the clutter thread
* make sure that the texture actor is updated in the clutter thread */
clutter_threads_add_idle (update_texture_actor, buf);
}
int
main (int argc, char *argv[])
{
/* init clutter then gstreamer */
clutter_init (&argc, &argv);
gst_init (&argc, &argv);
/* init glew */
GLenum err = glewInit ();
if (err != GLEW_OK)
g_debug ("failed to init GLEW: %s", glewGetErrorString (err));
/* retrieve and turn off clutter opengl context */
#ifdef WIN32
HGLRC clutter_gl_context = wglGetCurrentContext ();
HDC clutter_dc = wglGetCurrentDC ();
wglMakeCurrent (0, 0);
#else
Display *clutter_display = clutter_x11_get_default_display ();
Window clutter_win = clutter_x11_get_stage_window (CLUTTER_STAGE (stage));
GLXContext clutter_gl_context = glXGetCurrentContext ();
glXMakeCurrent (clutter_display, None, 0);
#endif
/* setup gstreamer pipeline */
GstPipeline *pipeline =
GST_PIPELINE (gst_parse_launch
("videotestsrc ! video/x-raw-rgb, bpp=32, depth=32, width=320, height=240, framerate=(fraction)30/1 ! "
"glupload ! fakesink sync=1", NULL));
/* clutter_gl_context is an external OpenGL context with which gst-plugins-gl want to share textures */
GstElement *glupload = gst_bin_get_by_name (GST_BIN (pipeline), "glupload0");
g_object_set (G_OBJECT (glupload), "external-opengl-context", (guint64) GPOINTER_TO_UINT (clutter_gl_context), NULL);
g_object_unref (glupload);
/* play pipeline */
gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING);
GstState state = GST_STATE_PLAYING;
if (gst_element_get_state (GST_ELEMENT (pipeline), &state, NULL, GST_CLOCK_TIME_NONE) != GST_STATE_CHANGE_SUCCESS)
{
g_debug ("failed to play pipeline\n");
return -1;
}
/* turn on back clutter opengl context */
#ifdef WIN32
wglMakeCurrent (clutter_dc, clutter_gl_context);
#else
glXMakeCurrent (clutter_display, clutter_win, clutter_gl_context);
#endif
/* clutter stage */
ClutterActor* stage = clutter_stage_get_default ();
clutter_actor_set_size (stage, 640, 480);
clutter_actor_set_position (stage, 0, 0);
clutter_stage_set_title (CLUTTER_STAGE (stage), "clutter and gst-plugins-gl");
ClutterActor *clutter_texture = setup_stage (CLUTTER_STAGE (stage));
/* set a callback to retrieve the gst gl textures */
GstElement *fakesink = gst_bin_get_by_name (GST_BIN (pipeline), "fakesink0");
g_object_set (G_OBJECT (fakesink), "signal-handoffs", TRUE, NULL);
g_signal_connect (fakesink, "handoff", G_CALLBACK (on_gst_buffer), clutter_texture);
g_object_unref (fakesink);
/* main loop */
clutter_main ();
/* deinit */
gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL);
g_object_unref (pipeline);
return 0;
}