From f0a0fcb0bfd8c4443bf399da9c5d188e52a4725b Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Sat, 17 Oct 2015 01:08:29 +1100 Subject: [PATCH] gl/examples: add a live shader demo using the new GstGLSLStage Implemented with videotestsrc ! glshader ! glupload ! gtkglsink Errors on an invalid shader compilation are ignored however any error provided by the glsl compiler is printed to stdout. --- tests/examples/gtk/Makefile.am | 15 +- tests/examples/gtk/glliveshader.c | 353 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 367 insertions(+), 1 deletion(-) create mode 100644 tests/examples/gtk/glliveshader.c diff --git a/tests/examples/gtk/Makefile.am b/tests/examples/gtk/Makefile.am index 945331d..71441b4 100644 --- a/tests/examples/gtk/Makefile.am +++ b/tests/examples/gtk/Makefile.am @@ -9,7 +9,7 @@ gtksink_LDADD = $(GTK3_LIBS) \ if USE_GTK3_GL if USE_GL -noinst_PROGRAMS += gtkglsink +noinst_PROGRAMS += gtkglsink glliveshader gtkglsink_SOURCES = gtkglsink.c gtkglsink_CFLAGS = $(GTK3_CFLAGS) \ @@ -20,5 +20,18 @@ gtkglsink_CFLAGS = $(GTK3_CFLAGS) \ gtkglsink_LDADD = $(GTK3_LIBS) \ $(GST_LIBS) \ $(GL_LIBS) + +glliveshader_SOURCES = glliveshader.c +glliveshader_CFLAGS = $(GTK3_CFLAGS) \ + -I$(top_srcdir)/gst-libs \ + -I$(top_builddir)/gst-libs \ + $(GST_PLUGINS_BAD_CFLAGS) \ + $(GST_PLUGINS_BASE_CFLAGS) \ + $(GST_CFLAGS) \ + $(GL_CFLAGS) +glliveshader_LDADD = $(GTK3_LIBS) \ + $(GST_LIBS) \ + $(GL_LIBS) \ + $(top_builddir)/gst-libs/gst/gl/libgstgl-$(GST_API_VERSION).la endif endif diff --git a/tests/examples/gtk/glliveshader.c b/tests/examples/gtk/glliveshader.c new file mode 100644 index 0000000..a8d128a --- /dev/null +++ b/tests/examples/gtk/glliveshader.c @@ -0,0 +1,353 @@ +/* + * GStreamer + * Copyright (C) 2015 Matthew Waters + * + * 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. + */ + +#include +#include +#include +#if GST_GL_HAVE_WINDOW_X11 +#include +#endif + +static GMainLoop *loop; + +static const gchar *vert = "#version 330\n\ +in vec4 a_position;\n\ +in vec2 a_texcoord;\n\ +out vec2 v_texcoord;\n\ +uniform float time;\n\ +uniform float width;\n\ +uniform float height;\n\ +void main()\n\ +{\n\ + gl_Position = a_position;\n\ + v_texcoord = a_texcoord;\n\ +}\n"; + +static const gchar *geom = "#version 330\n\ +\n\ +layout(triangles) in;\n\ +layout(triangle_strip, max_vertices = 3) out;\n\ +in vec2 v_texcoord[];\n\ +out vec2 g_texcoord;\n\ +\n\ +void main() {\n\ + for(int i = 0; i < 3; i++) {\n\ + gl_Position = gl_in[i].gl_Position;\n\ + g_texcoord = v_texcoord[i];\n\ + EmitVertex();\n\ + }\n\ + EndPrimitive();\n\ +}\n"; + +static const gchar *frag = "#version 330\n\ +in vec2 g_texcoord;\n\ +uniform sampler2D tex;\n\ +uniform float time;\n\ +uniform float width;\n\ +uniform float height;\n\ +void main()\n\ +{\n\ + gl_FragColor = texture2D(tex, g_texcoord);\n\ +}\n"; + +#define MAX_SHADER_STAGES 8 +struct shader_state; + +struct text_view_state +{ + struct shader_state *state; + + GLenum type; + gchar *str; +}; + +struct shader_state +{ + GstGLContext *context; + GstElement *shader; + gboolean shader_linked; + GtkWidget *label; + struct text_view_state text_states[MAX_SHADER_STAGES]; + gint n_stages; +}; + +static gboolean +bus_call (GstBus * bus, GstMessage * msg, gpointer data) +{ + switch (GST_MESSAGE_TYPE (msg)) { + case GST_MESSAGE_EOS: + g_print ("End of stream\n"); + g_main_loop_quit (loop); + break; + case GST_MESSAGE_ERROR:{ + gchar *debug; + GError *error; + + gst_message_parse_error (msg, &error, &debug); + g_free (debug); + + g_printerr ("Error: %s\n", error->message); + g_error_free (error); + + g_main_loop_quit (loop); + break; + } + default: + break; + } + + return TRUE; +} + +static gchar * +_find_source_for_shader_type (struct shader_state *state, GLenum type) +{ + int i = 0; + + for (i = 0; i < state->n_stages; i++) { + if (state->text_states[i].type == type) + return state->text_states[i].str; + } + + return NULL; +} + +static gboolean +_add_stage_to_shader (GstGLShader * shader, struct shader_state *state, + GLenum type, const gchar * default_src) +{ + GError *error = NULL; + GstGLSLVersion version; + GstGLSLProfile profile; + GstGLSLStage *stage; + const gchar *src; + + src = _find_source_for_shader_type (state, type); + if (!src) + src = default_src; + if (!src) + /* FIXME: assume this stage is not needed */ + return TRUE; + + if (!gst_glsl_string_get_version_profile (src, &version, &profile)) { + g_print ("Warning: failed to retreive GLSL version and profile for " + "shader type 0x%x\nsrc:\n%s\n", type, src); + } + + if (!(stage = gst_glsl_stage_new_with_string (shader->context, type, + version, profile, src))) { + g_print ("Error: Failed to create GLSL Stage from src:\n%s\n", src); + return FALSE; + } + + if (!gst_gl_shader_compile_attach_stage (shader, stage, &error)) { + /* ignore failed shader compilations */ + g_print ("%s", error->message); + return FALSE; + } + + return TRUE; +} + +static GstGLShader * +_new_shader (GstGLContext * context, struct shader_state *state) +{ + GstGLShader *shader = gst_gl_shader_new (context); + GError *error = NULL; + + if (!_add_stage_to_shader (shader, state, GL_VERTEX_SHADER, vert)) { + gst_object_unref (shader); + return NULL; + } + if (!_add_stage_to_shader (shader, state, GL_GEOMETRY_SHADER, geom)) { + gst_object_unref (shader); + return NULL; + } + if (!_add_stage_to_shader (shader, state, GL_FRAGMENT_SHADER, frag)) { + gst_object_unref (shader); + return NULL; + } + + if (!gst_gl_shader_link (shader, &error)) { + /* ignore failed shader compilations */ + g_print ("%s", error->message); + gst_object_unref (shader); + return NULL; + } + + return shader; +} + +static GstGLShader * +_create_shader (GstElement * element, struct shader_state *state) +{ + GstGLContext *context; + GstGLShader *shader; + + g_object_get (G_OBJECT (element), "context", &context, NULL); + + shader = _new_shader (context, state); + state->shader_linked = TRUE; + + if (state->context) + gst_object_unref (state->context); + state->context = context; + + return shader; +} + +static void +_modify_shader (GstGLContext * context, struct shader_state *state) +{ + GstGLShader *shader; + + if (!(shader = _new_shader (context, state))) { + state->shader_linked = FALSE; + return; + } + state->shader_linked = TRUE; + + g_object_set (state->shader, "shader", shader, NULL); +} + +static void +_on_text_changed (GtkTextBuffer * text, struct text_view_state *state) +{ + GtkTextIter start, end; + + if (!state->state->context) + return; + + gtk_text_buffer_get_bounds (text, &start, &end); + if (state->str) + g_free (state->str); + state->str = gtk_text_buffer_get_text (text, &start, &end, FALSE); + gst_gl_context_thread_add (state->state->context, + (GstGLContextThreadFunc) _modify_shader, state->state); + + gtk_label_set_text (GTK_LABEL (state->state->label), + state->state->shader_linked ? "Success" : "Failure"); +} + +static GtkWidget * +_new_source_view (struct shader_state *state, GLenum type, const gchar * templ) +{ + static int i = 0; + GtkWidget *scroll, *text_view; + GtkTextBuffer *text; + + g_return_val_if_fail (i < MAX_SHADER_STAGES, NULL); + + state->text_states[i].state = state; + state->text_states[i].type = type; + state->text_states[i].str = g_strdup (templ); + + scroll = gtk_scrolled_window_new (NULL, NULL); + gtk_widget_set_size_request (scroll, 20, 20); + text_view = gtk_text_view_new (); + gtk_container_add (GTK_CONTAINER (scroll), text_view); + text = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view)); + if (state->text_states[i].str) + gtk_text_buffer_set_text (text, state->text_states[i].str, -1); + g_signal_connect (text, "changed", G_CALLBACK (_on_text_changed), + &state->text_states[i]); + state->n_stages++; + i++; + + return scroll; +} + +int +main (int argc, char *argv[]) +{ + GstElement *pipeline, *src, *upload, *shader, *sink; + GtkWidget *window, *paned, *video, *right_box, *book; + struct shader_state state = { 0, }; + GstBus *bus; + +#if GST_GL_HAVE_WINDOW_X11 + XInitThreads (); +#endif + + gst_init (&argc, &argv); + gtk_init (&argc, &argv); + + loop = g_main_loop_new (NULL, FALSE); + + pipeline = gst_pipeline_new (NULL); + src = gst_element_factory_make ("videotestsrc", NULL); + upload = gst_element_factory_make ("glupload", NULL); + shader = gst_element_factory_make ("glshader", NULL); + sink = gst_element_factory_make ("gtkglsink", NULL); + g_object_get (sink, "widget", &video, NULL); + + g_assert (src && shader && sink); + gst_bin_add_many (GST_BIN (pipeline), src, upload, shader, sink, NULL); + g_assert (gst_element_link_many (src, upload, shader, sink, NULL)); + + bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); + gst_bus_add_watch (bus, bus_call, loop); + gst_object_unref (bus); + + state.shader = gst_object_ref (shader); + g_signal_connect (shader, "create-shader", G_CALLBACK (_create_shader), + &state); + + book = gtk_notebook_new (); + /* text view inside a scroll view */ + gtk_notebook_append_page (GTK_NOTEBOOK (book), _new_source_view (&state, + GL_VERTEX_SHADER, vert), gtk_label_new ("Vertex")); + gtk_notebook_append_page (GTK_NOTEBOOK (book), _new_source_view (&state, + GL_GEOMETRY_SHADER, geom), gtk_label_new ("Geometry")); + gtk_notebook_append_page (GTK_NOTEBOOK (book), _new_source_view (&state, + GL_FRAGMENT_SHADER, frag), gtk_label_new ("Fragment")); + /* status label */ + state.label = gtk_label_new ("Success"); + + /* right side source code editor */ + right_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + gtk_box_pack_start (GTK_BOX (right_box), book, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (right_box), state.label, FALSE, TRUE, 0); + + paned = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL); + gtk_paned_pack1 (GTK_PANED (paned), video, TRUE, FALSE); + gtk_widget_set_size_request (video, 20, 20); + gtk_paned_pack2 (GTK_PANED (paned), right_box, TRUE, FALSE); + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_default_size (GTK_WINDOW (window), 640, 480); + gtk_container_add (GTK_CONTAINER (window), paned); + + gtk_widget_show_all (window); + + gst_element_set_state (pipeline, GST_STATE_PLAYING); + + g_main_loop_run (loop); + + gst_element_set_state (pipeline, GST_STATE_NULL); + + /*shader strings leaked here */ + /*g_free (state.str); */ + gst_object_unref (state.shader); + + gst_object_unref (pipeline); + + return 0; +} -- 2.7.4