diff --git a/ext/gl/effects/gstgleffectidentity.c b/ext/gl/effects/gstgleffectidentity.c index 9740cc7c56..ce29f60014 100644 --- a/ext/gl/effects/gstgleffectidentity.c +++ b/ext/gl/effects/gstgleffectidentity.c @@ -42,17 +42,18 @@ gst_gl_effects_identity_callback (gint width, gint height, guint texture, shader = g_hash_table_lookup (effects->shaderstable, "identity0"); if (!shader) { - shader = gst_gl_shader_new (context); - g_hash_table_insert (effects->shaderstable, (gchar *) "identity0", shader); + GError *error = NULL; - if (!gst_gl_shader_compile_with_default_vf_and_check (shader, - &filter->draw_attr_position_loc, &filter->draw_attr_texture_loc)) { - /* gst gl context error is already set */ + if (!(shader = gst_gl_shader_new_default (context, &error))) { GST_ELEMENT_ERROR (effects, RESOURCE, NOT_FOUND, - ("Failed to initialize identity shader, %s", - gst_gl_context_get_error ()), (NULL)); + ("Failed to initialize identity shader: %s", error->message), (NULL)); return; } + + filter->draw_attr_position_loc = + gst_gl_shader_get_attribute_location (shader, "a_position"); + filter->draw_attr_texture_loc = + gst_gl_shader_get_attribute_location (shader, "a_texcoord"); } gst_gl_shader_use (shader); diff --git a/ext/gl/gstglcolorscale.c b/ext/gl/gstglcolorscale.c index f4c104c092..c7df0a8604 100644 --- a/ext/gl/gstglcolorscale.c +++ b/ext/gl/gstglcolorscale.c @@ -142,19 +142,20 @@ gst_gl_colorscale_gl_start (GstGLBaseFilter * base_filter) GstGLColorscale *colorscale = GST_GL_COLORSCALE (base_filter); GstGLFilter *filter = GST_GL_FILTER (base_filter); GstGLShader *shader; + GError *error = NULL; - shader = gst_gl_shader_new (base_filter->context); - - if (!gst_gl_shader_compile_with_default_vf_and_check (shader, - &filter->draw_attr_position_loc, &filter->draw_attr_texture_loc)) { - gst_gl_context_clear_shader (base_filter->context); + if (!(shader = gst_gl_shader_new_default (base_filter->context, &error))) { + GST_ERROR_OBJECT (colorscale, "Failed to initialize shader: %s", + error->message); gst_object_unref (shader); - GST_ERROR_OBJECT (colorscale, "Failed to initialize identity shader"); - GST_ELEMENT_ERROR (colorscale, RESOURCE, NOT_FOUND, ("%s", - "Failed to initialize identity shader"), (NULL)); return FALSE; } + filter->draw_attr_position_loc = + gst_gl_shader_get_attribute_location (shader, "a_position"); + filter->draw_attr_texture_loc = + gst_gl_shader_get_attribute_location (shader, "a_texcoord"); + colorscale->shader = shader; return GST_GL_BASE_FILTER_CLASS (parent_class)->gl_start (base_filter); diff --git a/ext/gl/gstgldifferencematte.c b/ext/gl/gstgldifferencematte.c index b2b5fa5e95..a746b3dfd0 100644 --- a/ext/gl/gstgldifferencematte.c +++ b/ext/gl/gstgldifferencematte.c @@ -78,7 +78,9 @@ static void gst_gl_differencematte_init_gl_resources (GstGLFilter * filter) { GstGLDifferenceMatte *differencematte = GST_GL_DIFFERENCEMATTE (filter); - GstGLFuncs *gl = GST_GL_BASE_FILTER (filter)->context->gl_vtable; + GstGLContext *context = GST_GL_BASE_FILTER (filter)->context; + const GstGLFuncs *gl = context->gl_vtable; + GError *error = NULL; gint i; for (i = 0; i < 4; i++) { @@ -96,45 +98,57 @@ gst_gl_differencematte_init_gl_resources (GstGLFilter * filter) gst_gl_shader_new (GST_GL_BASE_FILTER (filter)->context); } - if (!gst_gl_shader_compile_with_default_v_and_check (differencematte->shader - [0], difference_fragment_source, &filter->draw_attr_position_loc, - &filter->draw_attr_texture_loc)) { - gst_gl_context_set_error (GST_GL_BASE_FILTER (differencematte)->context, - "Failed to initialize difference shader"); + if (!(differencematte->shader[0] = + gst_gl_shader_new_link_with_stages (context, &error, + gst_glsl_stage_new_default_vertex (context), + gst_glsl_stage_new_with_string (context, GL_FRAGMENT_SHADER, + GST_GLSL_VERSION_NONE, GST_GLSL_PROFILE_ES, + difference_fragment_source), NULL))) { GST_ELEMENT_ERROR (differencematte, RESOURCE, NOT_FOUND, ("%s", gst_gl_context_get_error ()), (NULL)); return; } - if (!gst_gl_shader_compile_with_default_v_and_check (differencematte->shader - [1], hconv7_fragment_source_gles2, &filter->draw_attr_position_loc, - &filter->draw_attr_texture_loc)) { - gst_gl_context_set_error (GST_GL_BASE_FILTER (differencematte)->context, - "Failed to initialize hconv7 shader"); + if (!(differencematte->shader[1] = + gst_gl_shader_new_link_with_stages (context, &error, + gst_glsl_stage_new_default_vertex (context), + gst_glsl_stage_new_with_string (context, GL_FRAGMENT_SHADER, + GST_GLSL_VERSION_NONE, GST_GLSL_PROFILE_ES, + hconv7_fragment_source_gles2), NULL))) { GST_ELEMENT_ERROR (differencematte, RESOURCE, NOT_FOUND, ("%s", gst_gl_context_get_error ()), (NULL)); return; } - if (!gst_gl_shader_compile_with_default_v_and_check (differencematte->shader - [2], vconv7_fragment_source_gles2, &filter->draw_attr_position_loc, - &filter->draw_attr_texture_loc)) { - gst_gl_context_set_error (GST_GL_BASE_FILTER (differencematte)->context, - "Failed to initialize vconv7 shader"); + if (!(differencematte->shader[2] = + gst_gl_shader_new_link_with_stages (context, &error, + gst_glsl_stage_new_default_vertex (context), + gst_glsl_stage_new_with_string (context, GL_FRAGMENT_SHADER, + GST_GLSL_VERSION_NONE, GST_GLSL_PROFILE_ES, + vconv7_fragment_source_gles2), NULL))) { GST_ELEMENT_ERROR (differencematte, RESOURCE, NOT_FOUND, ("%s", gst_gl_context_get_error ()), (NULL)); return; } - if (!gst_gl_shader_compile_with_default_v_and_check (differencematte->shader - [3], texture_interp_fragment_source, &filter->draw_attr_position_loc, - &filter->draw_attr_texture_loc)) { - gst_gl_context_set_error (GST_GL_BASE_FILTER (differencematte)->context, - "Failed to initialize interp shader"); + if (!(differencematte->shader[3] = + gst_gl_shader_new_link_with_stages (context, &error, + gst_glsl_stage_new_default_vertex (context), + gst_glsl_stage_new_with_string (context, GL_FRAGMENT_SHADER, + GST_GLSL_VERSION_NONE, GST_GLSL_PROFILE_ES, + texture_interp_fragment_source), NULL))) { GST_ELEMENT_ERROR (differencematte, RESOURCE, NOT_FOUND, ("%s", gst_gl_context_get_error ()), (NULL)); return; } + + /* FIXME: this should really be per shader */ + filter->draw_attr_position_loc = + gst_gl_shader_get_attribute_location (differencematte->shader[3], + "a_position"); + filter->draw_attr_texture_loc = + gst_gl_shader_get_attribute_location (differencematte->shader[3], + "a_texcoord"); } /* free resources that need a gl context */ diff --git a/ext/gl/gstgleffects.c b/ext/gl/gstgleffects.c index 6cf5afcfae..cf4de6e8fb 100644 --- a/ext/gl/gstgleffects.c +++ b/ext/gl/gstgleffects.c @@ -545,21 +545,22 @@ gst_gl_effects_get_fragment_shader (GstGLEffects * effects, shader = g_hash_table_lookup (effects->shaderstable, shader_name); if (!shader) { - shader = gst_gl_shader_new (context); - if (!gst_gl_shader_compile_with_default_v_and_check (shader, - shader_source_gles2, &filter->draw_attr_position_loc, - &filter->draw_attr_texture_loc)) { - /* gst gl context error is already set */ - GST_ELEMENT_ERROR (effects, RESOURCE, NOT_FOUND, - ("Failed to initialize %s shader, %s", - shader_name, gst_gl_context_get_error ()), (NULL)); - gst_object_unref (shader); - shader = NULL; - } - } + GError *error = NULL; - if (!shader) - return NULL; + if (!(shader = gst_gl_shader_new_link_with_stages (context, &error, + gst_glsl_stage_new_default_vertex (context), + gst_glsl_stage_new_with_string (context, GL_FRAGMENT_SHADER, + GST_GLSL_VERSION_NONE, GST_GLSL_PROFILE_ES, + shader_source_gles2), NULL))) { + GST_ELEMENT_ERROR (effects, RESOURCE, NOT_FOUND, + ("Failed to initialize %s shader", shader_name), (NULL)); + } + + filter->draw_attr_position_loc = + gst_gl_shader_get_attribute_location (shader, "a_position"); + filter->draw_attr_texture_loc = + gst_gl_shader_get_attribute_location (shader, "a_texcoord"); + } g_hash_table_insert (effects->shaderstable, (gchar *) shader_name, shader); diff --git a/ext/gl/gstglimagesink.c b/ext/gl/gstglimagesink.c index 2c3b935f94..be2e8383a2 100644 --- a/ext/gl/gstglimagesink.c +++ b/ext/gl/gstglimagesink.c @@ -1682,13 +1682,23 @@ static void gst_glimage_sink_thread_init_redisplay (GstGLImageSink * gl_sink) { const GstGLFuncs *gl = gl_sink->context->gl_vtable; + GError *error = NULL; - gl_sink->redisplay_shader = gst_gl_shader_new (gl_sink->context); - - if (!gst_gl_shader_compile_with_default_vf_and_check - (gl_sink->redisplay_shader, &gl_sink->attr_position, - &gl_sink->attr_texture)) + if (!(gl_sink->redisplay_shader = + gst_gl_shader_new_link_with_stages (gl_sink->context, &error, + gst_glsl_stage_new_default_vertex (gl_sink->context), + gst_glsl_stage_new_default_fragment (gl_sink->context), NULL))) { + GST_ERROR_OBJECT (gl_sink, "Failed to link shader: %s", error->message); gst_glimage_sink_cleanup_glthread (gl_sink); + return; + } + + gl_sink->attr_position = + gst_gl_shader_get_attribute_location (gl_sink->redisplay_shader, + "a_position"); + gl_sink->attr_texture = + gst_gl_shader_get_attribute_location (gl_sink->redisplay_shader, + "a_texcoord"); if (gl->GenVertexArrays) { gl->GenVertexArrays (1, &gl_sink->vao); diff --git a/gst-libs/gst/gl/gstgloverlaycompositor.c b/gst-libs/gst/gl/gstgloverlaycompositor.c index 97a4aab4be..a1cdce6af9 100644 --- a/gst-libs/gst/gl/gstgloverlaycompositor.c +++ b/gst-libs/gst/gl/gstgloverlaycompositor.c @@ -425,12 +425,24 @@ gst_gl_overlay_compositor_init_gl (GstGLContext * context, { GstGLOverlayCompositor *compositor = (GstGLOverlayCompositor *) compositor_pointer; + GError *error = NULL; - if (!gst_gl_shader_compile_with_default_v_and_check (compositor->shader, - fragment_shader, &compositor->position_attrib, - &compositor->texcoord_attrib)) { - GST_ERROR ("could not initialize shader."); + if (!(compositor->shader = + gst_gl_shader_new_link_with_stages (context, &error, + gst_glsl_stage_new_default_vertex (context), + gst_glsl_stage_new_with_string (context, GL_FRAGMENT_SHADER, + GST_GLSL_VERSION_NONE, + GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY, + fragment_shader), NULL))) { + GST_ERROR_OBJECT (compositor, "could not initialize shader: %s", + error->message); + return; } + + compositor->position_attrib = + gst_gl_shader_get_attribute_location (compositor->shader, "a_position"); + compositor->texcoord_attrib = + gst_gl_shader_get_attribute_location (compositor->shader, "a_texcoord"); } GstGLOverlayCompositor * @@ -441,8 +453,6 @@ gst_gl_overlay_compositor_new (GstGLContext * context) compositor->context = gst_object_ref (context); - compositor->shader = gst_gl_shader_new (compositor->context); - gst_gl_context_thread_add (compositor->context, gst_gl_overlay_compositor_init_gl, compositor); diff --git a/gst-libs/gst/gl/gstglshader.c b/gst-libs/gst/gl/gstglshader.c index a41bf388f3..1eea611032 100644 --- a/gst-libs/gst/gl/gstglshader.c +++ b/gst-libs/gst/gl/gstglshader.c @@ -25,38 +25,8 @@ #include "gl.h" #include "gstglshader.h" +#include "gstglsl_private.h" -/* FIXME: separate into separate shader stage objects that can be added/removed - * independently of the shader program */ - -static const gchar *es2_version_header = "#version 100\n"; - -/* *INDENT-OFF* */ -static const gchar *simple_vertex_shader_str_gles2 = - "attribute vec4 a_position;\n" - "attribute vec2 a_texcoord;\n" - "varying vec2 v_texcoord;\n" - "void main()\n" - "{\n" - " gl_Position = a_position;\n" - " v_texcoord = a_texcoord;\n" - "}\n"; - -static const gchar *simple_fragment_shader_str_gles2 = - "#ifdef GL_ES\n" - "precision mediump float;\n" - "#endif\n" - "varying vec2 v_texcoord;\n" - "uniform sampler2D tex;\n" - "void main()\n" - "{\n" - " gl_FragColor = texture2D(tex, v_texcoord);\n" - "}"; -/* *INDENT-ON* */ - -#ifndef GL_COMPILE_STATUS -#define GL_COMPILE_STATUS 0x8B81 -#endif #ifndef GLhandleARB #define GLhandleARB GLuint #endif @@ -94,27 +64,17 @@ typedef struct _GstGLShaderVTable enum { PROP_0, - PROP_VERTEX_SRC, - PROP_FRAGMENT_SRC, - PROP_COMPILED, - PROP_ACTIVE /* unused */ + PROP_LINKED, }; struct _GstGLShaderPrivate { - gchar *vertex_src; - gchar *fragment_src; - - GLhandleARB vertex_handle; - GLhandleARB fragment_handle; GLhandleARB program_handle; + GList *stages; - gboolean compiled; - gboolean active; + gboolean linked; - GstGLAPI gl_api; - - GstGLShaderVTable vtable; + GstGLSLFuncs vtable; }; GST_DEBUG_CATEGORY_STATIC (gst_gl_shader_debug); @@ -130,21 +90,21 @@ _cleanup_shader (GstGLContext * context, GstGLShader * shader) { GstGLShaderPrivate *priv = shader->priv; + GST_OBJECT_LOCK (shader); + /* release shader objects */ - gst_gl_shader_release (shader); + gst_gl_shader_release_unlocked (shader); /* delete program */ if (priv->program_handle) { GST_TRACE ("finalizing program shader %u", priv->program_handle); priv->vtable.DeleteProgram (priv->program_handle); - /* err = glGetError (); */ - /* GST_WARNING ("error: 0x%x", err); */ - /* glGetObjectParameteriv(priv->program_handle, GL_OBJECT_DELETE_STATUS_, &status); */ - /* GST_INFO ("program deletion status:%s", status == GL_TRUE ? "true" : "false" ); */ } GST_DEBUG ("shader deleted %u", priv->program_handle); + + GST_OBJECT_UNLOCK (shader); } static void @@ -158,14 +118,9 @@ gst_gl_shader_finalize (GObject * object) GST_TRACE_OBJECT (shader, "finalizing shader %u", priv->program_handle); - g_free (priv->vertex_src); - g_free (priv->fragment_src); - gst_gl_context_thread_add (shader->context, (GstGLContextThreadFunc) _cleanup_shader, shader); - priv->fragment_handle = 0; - priv->vertex_handle = 0; priv->program_handle = 0; if (shader->context) { @@ -180,15 +135,7 @@ static void gst_gl_shader_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { - GstGLShader *shader = GST_GL_SHADER (object); - switch (prop_id) { - case PROP_VERTEX_SRC: - gst_gl_shader_set_vertex_source (shader, g_value_get_string (value)); - break; - case PROP_FRAGMENT_SRC: - gst_gl_shader_set_fragment_source (shader, g_value_get_string (value)); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -203,27 +150,13 @@ gst_gl_shader_get_property (GObject * object, GstGLShaderPrivate *priv = shader->priv; switch (prop_id) { - case PROP_VERTEX_SRC: - g_value_set_string (value, priv->vertex_src); - break; - case PROP_FRAGMENT_SRC: - g_value_set_string (value, priv->fragment_src); - break; - case PROP_COMPILED: - g_value_set_boolean (value, priv->compiled); + case PROP_LINKED: + g_value_set_boolean (value, priv->linked); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } - -} - -int -gst_gl_shader_get_program_handle (GstGLShader * shader) -{ - GstGLShaderPrivate *priv = shader->priv; - return (int) priv->program_handle; } static void @@ -239,82 +172,14 @@ gst_gl_shader_class_init (GstGLShaderClass * klass) obj_class->get_property = gst_gl_shader_get_property; /* .. and install properties */ - g_object_class_install_property (obj_class, - PROP_VERTEX_SRC, - g_param_spec_string ("vertex-src", - "Vertex Source", - "GLSL Vertex Shader source code", NULL, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (obj_class, - PROP_FRAGMENT_SRC, - g_param_spec_string ("fragment-src", - "Fragment Source", - "GLSL Fragment Shader source code", NULL, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (obj_class, - PROP_ACTIVE, - g_param_spec_string ("active", - "Active", "Enable/Disable the shader", NULL, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (obj_class, - PROP_COMPILED, - g_param_spec_boolean ("compiled", - "Compiled", - "Shader compile and link status", FALSE, + PROP_LINKED, + g_param_spec_boolean ("linked", + "Linked", + "Shader link status", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); } -void -gst_gl_shader_set_vertex_source (GstGLShader * shader, const gchar * src) -{ - GstGLShaderPrivate *priv; - - g_return_if_fail (GST_GL_IS_SHADER (shader)); - g_return_if_fail (src != NULL); - - priv = shader->priv; - - if (gst_gl_shader_is_compiled (shader)) - gst_gl_shader_release (shader); - - g_free (priv->vertex_src); - - priv->vertex_src = g_strdup (src); -} - -void -gst_gl_shader_set_fragment_source (GstGLShader * shader, const gchar * src) -{ - GstGLShaderPrivate *priv; - - g_return_if_fail (GST_GL_IS_SHADER (shader)); - g_return_if_fail (src != NULL); - - priv = shader->priv; - - if (gst_gl_shader_is_compiled (shader)) - gst_gl_shader_release (shader); - - g_free (priv->fragment_src); - - priv->fragment_src = g_strdup (src); -} - -const gchar * -gst_gl_shader_get_vertex_source (GstGLShader * shader) -{ - g_return_val_if_fail (GST_GL_IS_SHADER (shader), NULL); - return shader->priv->vertex_src; -} - -const gchar * -gst_gl_shader_get_fragment_source (GstGLShader * shader) -{ - g_return_val_if_fail (GST_GL_IS_SHADER (shader), NULL); - return shader->priv->fragment_src; -} - static void gst_gl_shader_init (GstGLShader * self) { @@ -323,268 +188,426 @@ gst_gl_shader_init (GstGLShader * self) priv = self->priv = GST_GL_SHADER_GET_PRIVATE (self); - priv->vertex_src = NULL; - priv->fragment_src = NULL; - - priv->fragment_handle = 0; - priv->vertex_handle = 0; - - priv->compiled = FALSE; - priv->active = FALSE; /* unused at the moment */ - - /* FIXME: add API to get/set this for each shader */ - priv->gl_api = GST_GL_API_ANY; + priv->linked = FALSE; } -static gboolean -_fill_vtable (GstGLShader * shader, GstGLContext * context) -{ - GstGLFuncs *gl = context->gl_vtable; - GstGLShaderVTable *vtable = &shader->priv->vtable; - - if (gl->CreateProgram) { - vtable->CreateProgram = gl->CreateProgram; - vtable->DeleteProgram = gl->DeleteProgram; - vtable->UseProgram = gl->UseProgram; - - vtable->CreateShader = gl->CreateShader; - vtable->DeleteShader = gl->DeleteShader; - vtable->AttachShader = gl->AttachShader; - vtable->DetachShader = gl->DetachShader; - - vtable->GetAttachedShaders = gl->GetAttachedShaders; - - vtable->GetShaderInfoLog = gl->GetShaderInfoLog; - vtable->GetShaderiv = gl->GetShaderiv; - vtable->GetProgramInfoLog = gl->GetProgramInfoLog; - vtable->GetProgramiv = gl->GetProgramiv; - } else if (gl->CreateProgramObject) { - vtable->CreateProgram = gl->CreateProgramObject; - vtable->DeleteProgram = gl->DeleteObject; - vtable->UseProgram = gl->UseProgramObject; - - vtable->CreateShader = gl->CreateShaderObject; - vtable->DeleteShader = gl->DeleteObject; - vtable->AttachShader = gl->AttachObject; - vtable->DetachShader = gl->DetachObject; - - vtable->GetAttachedShaders = gl->GetAttachedObjects; - - vtable->GetShaderInfoLog = gl->GetInfoLog; - vtable->GetShaderiv = gl->GetObjectParameteriv; - vtable->GetProgramInfoLog = gl->GetInfoLog; - vtable->GetProgramiv = gl->GetObjectParameteriv; - } else { - return FALSE; - } - - return TRUE; -} - -GstGLShader * -gst_gl_shader_new (GstGLContext * context) +static GstGLShader * +_new_with_stages_va_list (GstGLContext * context, GError ** error, + va_list varargs) { GstGLShader *shader; + GstGLSLStage *stage; g_return_val_if_fail (GST_GL_IS_CONTEXT (context), NULL); shader = g_object_new (GST_GL_TYPE_SHADER, NULL); shader->context = gst_object_ref (context); - GST_DEBUG_OBJECT (shader, "Created new GLShader for context %" GST_PTR_FORMAT, - context); + while ((stage = va_arg (varargs, GstGLSLStage *))) { + if (!gst_glsl_stage_compile (stage, error)) { + gst_object_unref (shader); + return NULL; + } + if (!gst_gl_shader_attach (shader, stage)) { + g_set_error (error, GST_GLSL_ERROR, GST_GLSL_ERROR_PROGRAM, + "Failed to attach stage to program"); + gst_object_unref (shader); + return NULL; + } + } return shader; } -gboolean -gst_gl_shader_is_compiled (GstGLShader * shader) +/** + * gst_gl_shader_new_link_with_stages: + * @context: a #GstGLContext + * @error: a #GError + * + * Each stage will attempt to be compiled and attached to @shader. Then + * the shader will be linked. On error, %NULL will be returned and @error will + * contain the details of the error. + * + * Note: must be called in the GL thread + * + * Returns: (transfer full): a new @shader with the specified stages. + */ +GstGLShader * +gst_gl_shader_new_link_with_stages (GstGLContext * context, GError ** error, + ...) { + GstGLShader *shader; + va_list varargs; + + va_start (varargs, error); + shader = _new_with_stages_va_list (context, error, varargs); + va_end (varargs); + + if (!shader) + return NULL; + + if (!gst_gl_shader_link (shader, error)) + return NULL; + + return shader; +} + +/** + * gst_gl_shader_new_with_stages: + * @context: a #GstGLContext + * @error: a #GError + * + * Each stage will attempt to be compiled and attached to @shader. On error, + * %NULL will be returned and @error will contain the details of the error. + * + * Note: must be called in the GL thread + * + * Returns: (transfer full): a new @shader with the specified stages. + */ +GstGLShader * +gst_gl_shader_new_with_stages (GstGLContext * context, GError ** error, ...) +{ + GstGLShader *shader; + va_list varargs; + + va_start (varargs, error); + shader = _new_with_stages_va_list (context, error, varargs); + va_end (varargs); + + return shader; +} + +/** + * gst_gl_shader_new: + * @context: a #GstGLContext + * + * Note: must be called in the GL thread + * + * Returns: (transfer full): a new empty @shader + */ +GstGLShader * +gst_gl_shader_new (GstGLContext * context) +{ + return gst_gl_shader_new_with_stages (context, NULL, NULL); +} + +/** + * gst_gl_shader_new_default: + * @context: a #GstGLContext + * + * Note: must be called in the GL thread + * + * Returns: (transfer full): a default @shader + */ +GstGLShader * +gst_gl_shader_new_default (GstGLContext * context, GError ** error) +{ + return gst_gl_shader_new_link_with_stages (context, error, + gst_glsl_stage_new_default_vertex (context), + gst_glsl_stage_new_default_fragment (context), NULL); +} + +/** + * gst_gl_shader_is_linked: + * @shader: a #GstGLShader + * + * Note: must be called in the GL thread + * + * Returns: whether @shader has been successfully linked + */ +gboolean +gst_gl_shader_is_linked (GstGLShader * shader) +{ + gboolean ret; + g_return_val_if_fail (GST_GL_IS_SHADER (shader), FALSE); - return shader->priv->compiled; + GST_OBJECT_LOCK (shader); + ret = shader->priv->linked; + GST_OBJECT_UNLOCK (shader); + + return ret; } static gboolean -_shader_string_has_version (const gchar * str) +_ensure_program (GstGLShader * shader) { - gboolean sl_comment = FALSE; - gboolean ml_comment = FALSE; - gboolean has_version = FALSE; - gint i = 0; + if (shader->priv->program_handle) + return TRUE; - /* search for #version to allow for preceeding comments as allowed by the - * GLSL specification */ - while (str && str[i] != '\0' && i < 1024) { - if (sl_comment) { - if (str[i] != '\n') - sl_comment = FALSE; - i++; - continue; - } + shader->priv->program_handle = shader->priv->vtable.CreateProgram (); + return shader->priv->program_handle != 0; +} - if (ml_comment) { - if (g_strstr_len (&str[i], 2, "*/")) { - ml_comment = FALSE; - i += 2; - } else { - i++; - } - continue; - } +/** + * gst_gl_shader_get_program_handle: + * @shader: a #GstGLShader + * + * Returns: the GL program handle for this shader + */ +int +gst_gl_shader_get_program_handle (GstGLShader * shader) +{ + int ret; - if (g_strstr_len (&str[i], 2, "//")) { - sl_comment = TRUE; - i += 2; - continue; - } + g_return_val_if_fail (GST_GL_IS_SHADER (shader), 0); - if (g_strstr_len (&str[i], 2, "/*")) { - ml_comment = TRUE; - i += 2; - continue; - } + GST_OBJECT_LOCK (shader); + ret = (int) shader->priv->program_handle; + GST_OBJECT_UNLOCK (shader); - if (g_strstr_len (&str[i], 1, "#")) { - if (g_strstr_len (&str[i], 8, "#version")) - has_version = TRUE; - break; - } + return ret; +} - i++; +/** + * gst_gl_shader_detach_unlocked: + * @shader: a #GstGLShader + * @stage: a #GstGLSLStage to attach + * + * Detaches @stage from @shader. @stage must have been successfully attached + * to @shader with gst_gl_shader_attach() or gst_gl_shader_attach_unlocked(). + * + * Note: must be called in the GL thread + */ +void +gst_gl_shader_detach_unlocked (GstGLShader * shader, GstGLSLStage * stage) +{ + guint stage_handle; + GList *elem; + + g_return_if_fail (GST_GL_IS_SHADER (shader)); + g_return_if_fail (GST_IS_GLSL_STAGE (stage)); + + if (!_gst_glsl_funcs_fill (&shader->priv->vtable, shader->context)) { + GST_WARNING_OBJECT (shader, "Failed to retreive required GLSL functions"); + return; } - return has_version; + if (!shader->priv->program_handle) + return; + + elem = g_list_find (shader->priv->stages, stage); + if (!elem) { + GST_FIXME_OBJECT (shader, "Could not find stage %p in shader %p", stage, + shader); + return; + } + + stage_handle = gst_glsl_stage_get_handle (stage); + if (!stage_handle) { + GST_FIXME_OBJECT (shader, "Stage %p doesn't have a GL handle", stage); + return; + } + + g_assert (shader->context->gl_vtable->IsProgram (shader->priv-> + program_handle)); + g_assert (shader->context->gl_vtable->IsShader (stage_handle)); + + GST_LOG_OBJECT (shader, "detaching shader %i from program %i", stage_handle, + (int) shader->priv->program_handle); + shader->priv->vtable.DetachShader (shader->priv->program_handle, + stage_handle); + + shader->priv->stages = g_list_remove_link (shader->priv->stages, elem); + gst_object_unref (stage); + g_list_free_1 (elem); } -static void -_maybe_prepend_version (GstGLShader * shader, const gchar * shader_str, - gint * n_vertex_sources, const gchar *** vertex_sources) +/** + * gst_gl_shader_detach: + * @shader: a #GstGLShader + * @stage: a #GstGLSLStage to attach + * + * Detaches @stage from @shader. @stage must have been successfully attached + * to @shader with gst_gl_shader_attach() or gst_gl_shader_attach_unlocked(). + * + * Note: must be called in the GL thread + */ +void +gst_gl_shader_detach (GstGLShader * shader, GstGLSLStage * stage) { - gint n = 1; + g_return_if_fail (GST_GL_IS_SHADER (shader)); + g_return_if_fail (GST_IS_GLSL_STAGE (stage)); - /* FIXME: this all an educated guess */ - if (gst_gl_context_check_gl_version (shader->context, GST_GL_API_OPENGL3, 3, - 0) - && (shader->priv->gl_api & GST_GL_API_GLES2) != 0 - && !_shader_string_has_version (shader_str)) - n = 2; - - *vertex_sources = g_malloc0 (n * sizeof (gchar *)); - - if (n > 1) - *vertex_sources[0] = es2_version_header; - - (*vertex_sources)[n - 1] = shader_str; - *n_vertex_sources = n; + GST_OBJECT_LOCK (shader); + gst_gl_shader_detach_unlocked (shader, stage); + GST_OBJECT_UNLOCK (shader); } +/** + * gst_gl_shader_attach_unlocked: + * @shader: a #GstGLShader + * @stage: a #GstGLSLStage to attach + * + * Attaches @stage to @shader. @stage must have been successfully compiled + * with gst_glsl_stage_compile(). + * + * Note: must be called in the GL thread + * + * Returns: whether @stage could be attached to @shader + */ gboolean -gst_gl_shader_compile (GstGLShader * shader, GError ** error) +gst_gl_shader_attach_unlocked (GstGLShader * shader, GstGLSLStage * stage) { - GstGLShaderPrivate *priv; - GstGLFuncs *gl; - - gchar info_buffer[2048]; - gint len = 0; - GLint status = GL_FALSE; + guint stage_handle; g_return_val_if_fail (GST_GL_IS_SHADER (shader), FALSE); + g_return_val_if_fail (GST_IS_GLSL_STAGE (stage), FALSE); + + if (!_gst_glsl_funcs_fill (&shader->priv->vtable, shader->context)) { + GST_WARNING_OBJECT (shader, "Failed to retreive required GLSL functions"); + return FALSE; + } + + if (!_ensure_program (shader)) + return FALSE; + + /* already attached? */ + if (g_list_find (shader->priv->stages, stage)) + return TRUE; + + stage_handle = gst_glsl_stage_get_handle (stage); + if (!stage_handle) { + return FALSE; + } + + g_assert (shader->context->gl_vtable->IsProgram (shader->priv-> + program_handle)); + g_assert (shader->context->gl_vtable->IsShader (stage_handle)); + + shader->priv->stages = + g_list_prepend (shader->priv->stages, gst_object_ref_sink (stage)); + GST_LOG_OBJECT (shader, "attaching shader %i to program %i", stage_handle, + (int) shader->priv->program_handle); + shader->priv->vtable.AttachShader (shader->priv->program_handle, + stage_handle); + + return TRUE; +} + +/** + * gst_gl_shader_attach: + * @shader: a #GstGLShader + * @stage: a #GstGLSLStage to attach + * + * Attaches @stage to @shader. @stage must have been successfully compiled + * with gst_glsl_stage_compile(). + * + * Note: must be called in the GL thread + * + * Returns: whether @stage could be attached to @shader + */ +gboolean +gst_gl_shader_attach (GstGLShader * shader, GstGLSLStage * stage) +{ + gboolean ret; + + g_return_val_if_fail (GST_GL_IS_SHADER (shader), FALSE); + g_return_val_if_fail (GST_IS_GLSL_STAGE (stage), FALSE); + + GST_OBJECT_LOCK (shader); + ret = gst_gl_shader_attach_unlocked (shader, stage); + GST_OBJECT_UNLOCK (shader); + + return ret; +} + +/** + * gst_gl_shader_compile_attach_stage: + * @shader: a #GstGLShader + * @stage: a #GstGLSLStage to attach + * @error: a #GError + * + * Compiles @stage and attaches it to @shader. + * + * Note: must be called in the GL thread + * + * Returns: whether @stage could be compiled and attached to @shader + */ +gboolean +gst_gl_shader_compile_attach_stage (GstGLShader * shader, GstGLSLStage * stage, + GError ** error) +{ + g_return_val_if_fail (GST_IS_GLSL_STAGE (stage), FALSE); + + if (!gst_glsl_stage_compile (stage, error)) { + return FALSE; + } + + if (!gst_gl_shader_attach (shader, stage)) { + g_set_error (error, GST_GLSL_ERROR, GST_GLSL_ERROR_COMPILE, + "Failed to attach stage to shader"); + return FALSE; + } + + return TRUE; +} + +/** + * gst_gl_shader_link: + * @shader: a #GstGLShader + * @error: a #GError + * + * Links the current list of #GstGLSLStage's in @shader. + * + * Note: must be called in the GL thread + * + * Returns: whether @shader could be linked together. + */ +gboolean +gst_gl_shader_link (GstGLShader * shader, GError ** error) +{ + GstGLShaderPrivate *priv; + const GstGLFuncs *gl; + gchar info_buffer[2048]; + GLint status = GL_FALSE; + gint len = 0; + gboolean ret; + GList *elem; + + g_return_val_if_fail (GST_GL_IS_SHADER (shader), FALSE); + + GST_OBJECT_LOCK (shader); priv = shader->priv; gl = shader->context->gl_vtable; - if (priv->compiled) - return priv->compiled; + if (priv->linked) { + GST_OBJECT_UNLOCK (shader); + return TRUE; + } - if (!_fill_vtable (shader, shader->context)) + if (!_gst_glsl_funcs_fill (&shader->priv->vtable, shader->context)) { + g_set_error (error, GST_GLSL_ERROR, GST_GLSL_ERROR_PROGRAM, + "Failed to retreive required GLSL functions"); + GST_OBJECT_UNLOCK (shader); return FALSE; + } - shader->priv->program_handle = shader->priv->vtable.CreateProgram (); + if (!_ensure_program (shader)) { + g_set_error (error, GST_GLSL_ERROR, GST_GLSL_ERROR_PROGRAM, + "Failed to create GL program object"); + GST_OBJECT_UNLOCK (shader); + return FALSE; + } GST_TRACE ("shader created %u", shader->priv->program_handle); - g_return_val_if_fail (priv->program_handle, FALSE); + for (elem = shader->priv->stages; elem; elem = elem->next) { + GstGLSLStage *stage = elem->data; - if (priv->vertex_src) { - gint n_vertex_sources; - const gchar **vertex_sources; - - _maybe_prepend_version (shader, priv->vertex_src, &n_vertex_sources, - &vertex_sources); - - /* create vertex object */ - priv->vertex_handle = priv->vtable.CreateShader (GL_VERTEX_SHADER); - gl->ShaderSource (priv->vertex_handle, n_vertex_sources, vertex_sources, - NULL); - g_free (vertex_sources); - - /* compile */ - gl->CompileShader (priv->vertex_handle); - /* check everything is ok */ - status = GL_FALSE; - gl->GetShaderiv (priv->vertex_handle, GL_COMPILE_STATUS, &status); - - priv->vtable.GetShaderInfoLog (priv->vertex_handle, - sizeof (info_buffer) - 1, &len, info_buffer); - info_buffer[len] = '\0'; - - if (status != GL_TRUE) { - GST_ERROR ("Vertex Shader compilation failed:\n%s", info_buffer); - - g_set_error (error, GST_GL_SHADER_ERROR, - GST_GL_SHADER_ERROR_COMPILE, - "Vertex Shader compilation failed:\n%s", info_buffer); - - priv->vtable.DeleteShader (priv->vertex_handle); - priv->compiled = FALSE; - return priv->compiled; - } else if (len > 1) { - GST_FIXME ("vertex shader info log:\n%s\n", info_buffer); + if (!gst_glsl_stage_compile (stage, error)) { + GST_OBJECT_UNLOCK (shader); + return FALSE; } - priv->vtable.AttachShader (priv->program_handle, priv->vertex_handle); - GST_LOG ("vertex shader attached %u", priv->vertex_handle); - } - - if (priv->fragment_src) { - gint n_fragment_sources; - const gchar **fragment_sources; - - _maybe_prepend_version (shader, priv->fragment_src, &n_fragment_sources, - &fragment_sources); - - /* create fragment object */ - priv->fragment_handle = priv->vtable.CreateShader (GL_FRAGMENT_SHADER); - gl->ShaderSource (priv->fragment_handle, n_fragment_sources, - fragment_sources, NULL); - g_free (fragment_sources); - /* compile */ - gl->CompileShader (priv->fragment_handle); - /* check everything is ok */ - status = GL_FALSE; - priv->vtable.GetShaderiv (priv->fragment_handle, - GL_COMPILE_STATUS, &status); - - priv->vtable.GetShaderInfoLog (priv->fragment_handle, - sizeof (info_buffer) - 1, &len, info_buffer); - info_buffer[len] = '\0'; - if (status != GL_TRUE) { - GST_ERROR ("Fragment Shader compilation failed:\n%s", info_buffer); - - g_set_error (error, GST_GL_SHADER_ERROR, - GST_GL_SHADER_ERROR_COMPILE, - "Fragment Shader compilation failed:\n%s", info_buffer); - - priv->vtable.DeleteShader (priv->fragment_handle); - priv->compiled = FALSE; - return priv->compiled; - } else if (len > 1) { - GST_FIXME ("vertex shader info log:\n%s\n", info_buffer); + if (!gst_gl_shader_attach_unlocked (shader, stage)) { + g_set_error (error, GST_GLSL_ERROR, GST_GLSL_ERROR_COMPILE, + "Failed to attach shader %" GST_PTR_FORMAT "to program %" + GST_PTR_FORMAT, stage, shader); + GST_OBJECT_UNLOCK (shader); + return FALSE; } - priv->vtable.AttachShader (priv->program_handle, priv->fragment_handle); - - GST_LOG ("fragment shader attached %u", priv->fragment_handle); } /* if nothing failed link shaders */ @@ -599,53 +622,81 @@ gst_gl_shader_compile (GstGLShader * shader, GError ** error) if (status != GL_TRUE) { GST_ERROR ("Shader linking failed:\n%s", info_buffer); - g_set_error (error, GST_GL_SHADER_ERROR, - GST_GL_SHADER_ERROR_LINK, "Shader Linking failed:\n%s", info_buffer); - priv->compiled = FALSE; - return priv->compiled; + g_set_error (error, GST_GLSL_ERROR, GST_GLSL_ERROR_LINK, + "Shader Linking failed:\n%s", info_buffer); + ret = priv->linked = FALSE; + GST_OBJECT_UNLOCK (shader); + return ret; } else if (len > 1) { GST_FIXME ("shader link log:\n%s\n", info_buffer); } - /* success! */ - priv->compiled = TRUE; - g_object_notify (G_OBJECT (shader), "compiled"); - return priv->compiled; + ret = priv->linked = TRUE; + GST_OBJECT_UNLOCK (shader); + + g_object_notify (G_OBJECT (shader), "linked"); + + return ret; } +/** + * gst_gl_shader_release_unlocked: + * @shader: a #GstGLShader + * + * Releases the shader and stages. + * + * Note: must be called in the GL thread + */ void -gst_gl_shader_release (GstGLShader * shader) +gst_gl_shader_release_unlocked (GstGLShader * shader) { GstGLShaderPrivate *priv; + GList *elem; g_return_if_fail (GST_GL_IS_SHADER (shader)); priv = shader->priv; - if (!priv->compiled || !priv->program_handle) - return; + for (elem = shader->priv->stages; elem; elem = elem->next) { + GstGLSLStage *stage = elem->data; - if (priv->vertex_handle) { /* not needed but nvidia doesn't care to respect the spec */ - GST_TRACE ("finalizing vertex shader %u", priv->vertex_handle); - - priv->vtable.DeleteShader (priv->vertex_handle); + gst_gl_shader_detach_unlocked (shader, stage); } - if (priv->fragment_handle) { - GST_TRACE ("finalizing fragment shader %u", priv->fragment_handle); + g_list_free_full (shader->priv->stages, (GDestroyNotify) gst_object_unref); + shader->priv->stages = NULL; - priv->vtable.DeleteShader (priv->fragment_handle); - } + priv->linked = FALSE; - if (priv->vertex_handle) - priv->vtable.DetachShader (priv->program_handle, priv->vertex_handle); - if (priv->fragment_handle) - priv->vtable.DetachShader (priv->program_handle, priv->fragment_handle); - - priv->compiled = FALSE; - g_object_notify (G_OBJECT (shader), "compiled"); + g_object_notify (G_OBJECT (shader), "linked"); } +/** + * gst_gl_shader_release: + * @shader: a #GstGLShader + * + * Releases the shader and stages. + * + * Note: must be called in the GL thread + */ +void +gst_gl_shader_release (GstGLShader * shader) +{ + g_return_if_fail (GST_GL_IS_SHADER (shader)); + + GST_OBJECT_LOCK (shader); + gst_gl_shader_release_unlocked (shader); + GST_OBJECT_UNLOCK (shader); +} + +/** + * gst_gl_shader_use: + * @shader: a #GstGLShader + * + * Mark's @shader as being used for the next GL draw command. + * + * Note: must be called in the GL thread and @shader must have been linked. + */ void gst_gl_shader_use (GstGLShader * shader) { @@ -662,6 +713,14 @@ gst_gl_shader_use (GstGLShader * shader) return; } +/** + * gst_gl_context_clear_shader: + * @shader: a #GstGLShader + * + * Clear's the currently set shader from the GL state machine. + * + * Note: must be called in the GL thread. + */ void gst_gl_context_clear_shader (GstGLContext * context) { @@ -677,104 +736,6 @@ gst_gl_context_clear_shader (GstGLContext * context) gl->UseProgramObject (0); } -gboolean -gst_gl_shader_compile_and_check (GstGLShader * shader, - const gchar * source, GstGLShaderSourceType type) -{ - gboolean is_compiled = FALSE; - - g_object_get (G_OBJECT (shader), "compiled", &is_compiled, NULL); - - if (!is_compiled) { - GError *error = NULL; - - switch (type) { - case GST_GL_SHADER_FRAGMENT_SOURCE: - gst_gl_shader_set_fragment_source (shader, source); - break; - case GST_GL_SHADER_VERTEX_SOURCE: - gst_gl_shader_set_vertex_source (shader, source); - break; - default: - g_assert_not_reached (); - break; - } - - gst_gl_shader_compile (shader, &error); - if (error) { - gst_gl_context_set_error (shader->context, "%s", error->message); - g_error_free (error); - gst_gl_context_clear_shader (shader->context); - - return FALSE; - } - } - return TRUE; -} - -gboolean -gst_gl_shader_compile_all_with_attribs_and_check (GstGLShader * shader, - const gchar * v_src, const gchar * f_src, const gint n_attribs, - const gchar * attrib_names[], GLint attrib_locs[]) -{ - gint i = 0; - GError *error = NULL; - - gst_gl_shader_set_vertex_source (shader, v_src); - gst_gl_shader_set_fragment_source (shader, f_src); - - gst_gl_shader_compile (shader, &error); - if (error) { - gst_gl_context_set_error (shader->context, "%s", error->message); - g_error_free (error); - gst_gl_context_clear_shader (shader->context); - - return FALSE; - } - - for (i = 0; i < n_attribs; i++) - attrib_locs[i] = - gst_gl_shader_get_attribute_location (shader, attrib_names[i]); - - return TRUE; -} - -gboolean -gst_gl_shader_compile_with_default_f_and_check (GstGLShader * shader, - const gchar * v_src, const gint n_attribs, const gchar * attrib_names[], - GLint attrib_locs[]) -{ - return gst_gl_shader_compile_all_with_attribs_and_check (shader, v_src, - simple_fragment_shader_str_gles2, n_attribs, attrib_names, attrib_locs); -} - -gboolean -gst_gl_shader_compile_with_default_v_and_check (GstGLShader * shader, - const gchar * f_src, GLint * pos_loc, GLint * tex_loc) -{ - const gchar *attrib_names[2] = { "a_position", "a_texcoord" }; - GLint attrib_locs[2] = { 0 }; - gboolean ret = TRUE; - - ret = gst_gl_shader_compile_all_with_attribs_and_check (shader, - simple_vertex_shader_str_gles2, f_src, 2, attrib_names, attrib_locs); - - if (ret) { - *pos_loc = attrib_locs[0]; - *tex_loc = attrib_locs[1]; - } - - return ret; -} - -gboolean -gst_gl_shader_compile_with_default_vf_and_check (GstGLShader * shader, - GLint * pos_loc, GLint * tex_loc) -{ - return gst_gl_shader_compile_with_default_v_and_check (shader, - simple_fragment_shader_str_gles2, pos_loc, tex_loc); -} - void gst_gl_shader_set_uniform_1f (GstGLShader * shader, const gchar * name, gfloat value) @@ -1233,16 +1194,20 @@ gst_gl_shader_get_attribute_location (GstGLShader * shader, const gchar * name) { GstGLShaderPrivate *priv; GstGLFuncs *gl; + gint ret; g_return_val_if_fail (shader != NULL, -1); priv = shader->priv; g_return_val_if_fail (priv->program_handle != 0, -1); - if (0 == priv->vertex_handle) - return -1; gl = shader->context->gl_vtable; - return gl->GetAttribLocation (priv->program_handle, name); + ret = gl->GetAttribLocation (priv->program_handle, name); + + GST_TRACE_OBJECT (shader, "retreived program %i attribute \'%s\' location %i", + (int) priv->program_handle, name, ret); + + return ret; } void @@ -1257,11 +1222,8 @@ gst_gl_shader_bind_attribute_location (GstGLShader * shader, GLuint index, g_return_if_fail (priv->program_handle != 0); gl = shader->context->gl_vtable; + GST_TRACE_OBJECT (shader, "binding program %i attribute \'%s\' location %i", + (int) priv->program_handle, name, index); + gl->BindAttribLocation (priv->program_handle, index, name); } - -GQuark -gst_gl_shader_error_quark (void) -{ - return g_quark_from_static_string ("gst-gl-shader-error"); -} diff --git a/gst-libs/gst/gl/gstglshader.h b/gst-libs/gst/gl/gstglshader.h index 97f98fc76f..97b6c01cdb 100644 --- a/gst-libs/gst/gl/gstglshader.h +++ b/gst-libs/gst/gl/gstglshader.h @@ -26,6 +26,7 @@ G_BEGIN_DECLS +GType gst_gl_shader_get_type (void); #define GST_GL_TYPE_SHADER (gst_gl_shader_get_type()) #define GST_GL_SHADER(o) (G_TYPE_CHECK_INSTANCE_CAST((o), GST_GL_TYPE_SHADER, GstGLShader)) #define GST_GL_SHADER_CLASS(k) (G_TYPE_CHECK_CLASS((k), GST_GL_TYPE_SHADER, GstGLShaderClass)) @@ -33,19 +34,6 @@ G_BEGIN_DECLS #define GST_GL_IS_SHADER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), GST_GL_TYPE_SHADER)) #define GST_GL_SHADER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GST_GL_TYPE_SHADER, GstGLShaderClass)) -#define GST_GL_SHADER_ERROR (gst_gl_shader_error_quark ()) - -typedef enum { - GST_GL_SHADER_ERROR_COMPILE, - GST_GL_SHADER_ERROR_LINK, - GST_GL_SHADER_ERROR_PROGRAM -} GstGLShaderError; - -typedef enum { - GST_GL_SHADER_FRAGMENT_SOURCE, - GST_GL_SHADER_VERTEX_SOURCE -} GstGLShaderSourceType; - struct _GstGLShader { /*< private >*/ GstObject parent; @@ -53,6 +41,8 @@ struct _GstGLShader { GstGLContext *context; GstGLShaderPrivate *priv; + + gpointer _padding[GST_PADDING]; }; struct _GstGLShaderClass { @@ -60,33 +50,29 @@ struct _GstGLShaderClass { GstObjectClass parent_class; }; -/* methods */ +GstGLShader * gst_gl_shader_new (GstGLContext *context); +GstGLShader * gst_gl_shader_new_with_stages (GstGLContext * context, GError ** error, ...); +GstGLShader * gst_gl_shader_new_link_with_stages (GstGLContext * context, GError ** error, ...); +GstGLShader * gst_gl_shader_new_default (GstGLContext * context, GError ** error); -GQuark gst_gl_shader_error_quark (void); -GType gst_gl_shader_get_type (void); +gboolean gst_gl_shader_attach (GstGLShader * shader, GstGLSLStage * stage); +gboolean gst_gl_shader_attach_unlocked (GstGLShader * shader, GstGLSLStage * stage); -GstGLShader * gst_gl_shader_new (GstGLContext *context); +void gst_gl_shader_detach (GstGLShader * shader, GstGLSLStage * stage); +void gst_gl_shader_detach_unlocked (GstGLShader * shader, GstGLSLStage * stage); -int gst_gl_shader_get_program_handle(GstGLShader * shader); +gboolean gst_gl_shader_compile_attach_stage (GstGLShader * shader, + GstGLSLStage *stage, + GError ** error); +gboolean gst_gl_shader_link (GstGLShader * shader, GError ** error); +gboolean gst_gl_shader_is_linked (GstGLShader *shader); -void gst_gl_shader_set_vertex_source (GstGLShader *shader, const gchar *src); -void gst_gl_shader_set_fragment_source (GstGLShader *shader, const gchar *src); -const gchar * gst_gl_shader_get_vertex_source (GstGLShader *shader); -const gchar * gst_gl_shader_get_fragment_source (GstGLShader *shader); +int gst_gl_shader_get_program_handle (GstGLShader * shader); -void gst_gl_shader_set_active (GstGLShader *shader, gboolean active); -gboolean gst_gl_shader_is_compiled (GstGLShader *shader); -gboolean gst_gl_shader_compile (GstGLShader *shader, GError **error); -gboolean gst_gl_shader_compile_and_check (GstGLShader *shader, const gchar *source, GstGLShaderSourceType type); -gboolean gst_gl_shader_compile_all_with_attribs_and_check (GstGLShader *shader, const gchar *v_src, const gchar *f_src, const gint n_attribs, const gchar *attrib_names[], GLint attrib_locs[]); - -gboolean gst_gl_shader_compile_with_default_f_and_check (GstGLShader *shader, const gchar *v_src, const gint n_attribs, const gchar *attrib_names[], GLint attrib_locs[]); -gboolean gst_gl_shader_compile_with_default_v_and_check (GstGLShader *shader, const gchar *f_src, GLint *pos_loc, GLint *tex_loc); -gboolean gst_gl_shader_compile_with_default_vf_and_check (GstGLShader *shader, GLint *pos_loc, GLint *tex_loc); - -void gst_gl_shader_release (GstGLShader *shader); -void gst_gl_shader_use (GstGLShader *shader); -void gst_gl_context_clear_shader (GstGLContext *context); +void gst_gl_shader_release (GstGLShader *shader); +void gst_gl_shader_release_unlocked (GstGLShader * shader); +void gst_gl_shader_use (GstGLShader *shader); +void gst_gl_context_clear_shader (GstGLContext *context); void gst_gl_shader_set_uniform_1i (GstGLShader *shader, const gchar *name, gint value); void gst_gl_shader_set_uniform_1iv (GstGLShader *shader, const gchar *name, guint count, gint *value); diff --git a/gst-libs/gst/gl/gstglutils.c b/gst-libs/gst/gl/gstglutils.c index 4312ead541..8acfb97443 100644 --- a/gst-libs/gst/gl/gstglutils.c +++ b/gst-libs/gst/gl/gstglutils.c @@ -375,20 +375,60 @@ gst_gl_context_del_fbo (GstGLContext * context, GLuint fbo, GLuint depth_buffer) gst_object_unref (frame); } -static void -_compile_shader (GstGLContext * context, GstGLShader ** shader) +struct _compile_shader { + GstGLShader **shader; + const gchar *vertex_src; + const gchar *fragment_src; +}; + +static void +_compile_shader (GstGLContext * context, struct _compile_shader *data) +{ + GstGLShader *shader; + GstGLSLStage *vert, *frag; GError *error = NULL; - gst_gl_shader_compile (*shader, &error); - if (error) { - gst_gl_context_set_error (context, "%s", error->message); + shader = gst_gl_shader_new (context); + + if (data->vertex_src) { + vert = gst_glsl_stage_new_with_string (context, GL_VERTEX_SHADER, + GST_GLSL_VERSION_NONE, GST_GLSL_PROFILE_NONE, data->vertex_src); + if (!gst_glsl_stage_compile (vert, &error)) { + GST_ERROR_OBJECT (vert, "%s", error->message); + gst_object_unref (shader); + return; + } + if (!gst_gl_shader_attach (shader, vert)) { + gst_object_unref (shader); + return; + } + } + + if (data->fragment_src) { + frag = gst_glsl_stage_new_with_string (context, GL_FRAGMENT_SHADER, + GST_GLSL_VERSION_NONE, GST_GLSL_PROFILE_NONE, data->fragment_src); + if (!gst_glsl_stage_compile (frag, &error)) { + GST_ERROR_OBJECT (frag, "%s", error->message); + gst_object_unref (shader); + return; + } + if (!gst_gl_shader_attach (shader, frag)) { + gst_object_unref (shader); + return; + } + } + + if (!gst_gl_shader_link (shader, &error)) { + GST_ERROR_OBJECT (shader, "%s", error->message); g_error_free (error); error = NULL; gst_gl_context_clear_shader (context); - gst_object_unref (*shader); - *shader = NULL; + gst_object_unref (shader); + return; } + + *data->shader = shader; } /* Called by glfilter */ @@ -396,18 +436,17 @@ gboolean gst_gl_context_gen_shader (GstGLContext * context, const gchar * vert_src, const gchar * frag_src, GstGLShader ** shader) { + struct _compile_shader data; + g_return_val_if_fail (frag_src != NULL || vert_src != NULL, FALSE); g_return_val_if_fail (shader != NULL, FALSE); - *shader = gst_gl_shader_new (context); - - if (frag_src) - gst_gl_shader_set_fragment_source (*shader, frag_src); - if (vert_src) - gst_gl_shader_set_vertex_source (*shader, vert_src); + data.shader = shader; + data.vertex_src = vert_src; + data.fragment_src = frag_src; gst_gl_context_thread_add (context, (GstGLContextThreadFunc) _compile_shader, - shader); + &data); return *shader != NULL; } diff --git a/tests/check/libs/gstglcontext.c b/tests/check/libs/gstglcontext.c index 8bb5f99fcc..6ad97b8d18 100644 --- a/tests/check/libs/gstglcontext.c +++ b/tests/check/libs/gstglcontext.c @@ -28,29 +28,6 @@ #include -#if GST_GL_HAVE_GLES2 -/* *INDENT-OFF* */ -static const gchar *vertex_shader_str_gles2 = - "attribute vec4 a_position; \n" - "attribute vec2 a_texCoord; \n" - "varying vec2 v_texCoord; \n" - "void main() \n" - "{ \n" - " gl_Position = a_position; \n" - " v_texCoord = a_texCoord; \n" - "} \n"; - -static const gchar *fragment_shader_str_gles2 = - "precision mediump float; \n" - "varying vec2 v_texCoord; \n" - "uniform sampler2D s_texture; \n" - "void main() \n" - "{ \n" - " gl_FragColor = texture2D( s_texture, v_texCoord );\n" - "} \n"; -/* *INDENT-ON* */ -#endif - static GstGLDisplay *display; static void @@ -67,17 +44,15 @@ teardown (void) static GLuint fbo_id, rbo, tex; static GstGLFramebuffer *fbo; -#if GST_GL_HAVE_GLES2 -static GError *error; static GstGLShader *shader; static GLint shader_attr_position_loc; static GLint shader_attr_texture_loc; -#endif static void init (gpointer data) { GstGLContext *context = data; + GError *error = NULL; /* has to be called in the thread that is going to use the framebuffer */ fbo = gst_gl_framebuffer_new (context); @@ -88,25 +63,14 @@ init (gpointer data) gst_gl_context_gen_texture (context, &tex, GST_VIDEO_FORMAT_RGBA, 320, 240); fail_if (tex == 0, "failed to create texture"); -#if GST_GL_HAVE_GLES2 - if (gst_gl_context_get_gl_api (context) & GST_GL_API_GLES2) { - shader = gst_gl_shader_new (context); - fail_if (shader == NULL, "failed to create shader object"); + shader = gst_gl_shader_new_default (context, &error); + fail_if (shader == NULL, "failed to create shader object: %s", + error->message); - gst_gl_shader_set_vertex_source (shader, vertex_shader_str_gles2); - gst_gl_shader_set_fragment_source (shader, fragment_shader_str_gles2); - - error = NULL; - gst_gl_shader_compile (shader, &error); - fail_if (error != NULL, "Error compiling shader %s\n", - error ? error->message : "Unknown Error"); - - shader_attr_position_loc = - gst_gl_shader_get_attribute_location (shader, "a_position"); - shader_attr_texture_loc = - gst_gl_shader_get_attribute_location (shader, "a_texCoord"); - } -#endif + shader_attr_position_loc = + gst_gl_shader_get_attribute_location (shader, "a_position"); + shader_attr_texture_loc = + gst_gl_shader_get_attribute_location (shader, "a_texcoord"); } static void @@ -116,10 +80,7 @@ deinit (gpointer data) GstGLFuncs *gl = context->gl_vtable; gl->DeleteTextures (1, &tex); gst_object_unref (fbo); -#if GST_GL_HAVE_GLES2 - if (gst_gl_context_get_gl_api (context) & GST_GL_API_GLES2) - gst_object_unref (shader); -#endif + gst_object_unref (shader); } static void @@ -150,81 +111,38 @@ draw_render (gpointer data) GstGLContext *context = data; GstGLContextClass *context_class = GST_GL_CONTEXT_GET_CLASS (context); const GstGLFuncs *gl = context->gl_vtable; + const GLfloat vVertices[] = { 1.0f, 1.0f, 0.0f, + 1.0f, 0.0f, + -1.0f, 1.0f, 0.0f, + 0.0f, 0.0f, + -1.0f, -1.0f, 0.0f, + 0.0f, 1.0f, + 1.0f, -1.0f, 0.0f, + 1.0f, 1.0f + }; - /* redraw the texture into the system provided framebuffer */ + GLushort indices[] = { 0, 1, 2, 0, 2, 3 }; -#if GST_GL_HAVE_OPENGL - if (gst_gl_context_get_gl_api (context) & GST_GL_API_OPENGL) { - GLfloat verts[8] = { 1.0f, 1.0f, - -1.0f, 1.0f, - -1.0f, -1.0f, - 1.0f, -1.0f - }; - GLfloat texcoords[8] = { 1.0f, 0.0f, - 0.0f, 0.0f, - 0.0f, 1.0f, - 1.0f, 1.0f - }; + gl->Clear (GL_COLOR_BUFFER_BIT); - gl->Viewport (0, 0, 320, 240); + gst_gl_shader_use (shader); - gl->Clear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + /* Load the vertex position */ + gl->VertexAttribPointer (shader_attr_position_loc, 3, + GL_FLOAT, GL_FALSE, 5 * sizeof (GLfloat), vVertices); - gl->MatrixMode (GL_PROJECTION); - gl->LoadIdentity (); + /* Load the texture coordinate */ + gl->VertexAttribPointer (shader_attr_texture_loc, 2, + GL_FLOAT, GL_FALSE, 5 * sizeof (GLfloat), &vVertices[3]); - gl->ActiveTexture (GL_TEXTURE0); - gl->BindTexture (GL_TEXTURE_2D, tex); + gl->EnableVertexAttribArray (shader_attr_position_loc); + gl->EnableVertexAttribArray (shader_attr_texture_loc); - gl->EnableClientState (GL_VERTEX_ARRAY); - gl->VertexPointer (2, GL_FLOAT, 0, &verts); + gl->ActiveTexture (GL_TEXTURE0); + gl->BindTexture (GL_TEXTURE_2D, tex); + gst_gl_shader_set_uniform_1i (shader, "s_texture", 0); - gl->ClientActiveTexture (GL_TEXTURE0); - gl->EnableClientState (GL_TEXTURE_COORD_ARRAY); - gl->TexCoordPointer (2, GL_FLOAT, 0, &texcoords); - - gl->DrawArrays (GL_TRIANGLE_FAN, 0, 4); - - gl->DisableClientState (GL_VERTEX_ARRAY); - gl->DisableClientState (GL_TEXTURE_COORD_ARRAY); - } -#endif -#if GST_GL_HAVE_GLES2 - if (gst_gl_context_get_gl_api (context) & GST_GL_API_GLES2) { - const GLfloat vVertices[] = { 1.0f, 1.0f, 0.0f, - 1.0f, 0.0f, - -1.0f, 1.0f, 0.0f, - 0.0f, 0.0f, - -1.0f, -1.0f, 0.0f, - 0.0f, 1.0f, - 1.0f, -1.0f, 0.0f, - 1.0f, 1.0f - }; - - GLushort indices[] = { 0, 1, 2, 0, 2, 3 }; - - gl->Clear (GL_COLOR_BUFFER_BIT); - - gst_gl_shader_use (shader); - - /* Load the vertex position */ - gl->VertexAttribPointer (shader_attr_position_loc, 3, - GL_FLOAT, GL_FALSE, 5 * sizeof (GLfloat), vVertices); - - /* Load the texture coordinate */ - gl->VertexAttribPointer (shader_attr_texture_loc, 2, - GL_FLOAT, GL_FALSE, 5 * sizeof (GLfloat), &vVertices[3]); - - gl->EnableVertexAttribArray (shader_attr_position_loc); - gl->EnableVertexAttribArray (shader_attr_texture_loc); - - gl->ActiveTexture (GL_TEXTURE0); - gl->BindTexture (GL_TEXTURE_2D, tex); - gst_gl_shader_set_uniform_1i (shader, "s_texture", 0); - - gl->DrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices); - } -#endif + gl->DrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices); context_class->swap_buffers (context); } @@ -314,6 +232,7 @@ check_wrapped (gpointer data) fail_if (!ret, "error received %s\n", error ? error->message : "Unknown error"); fail_if (wrapped_context->gl_vtable->TexImage2D == NULL); + gst_gl_context_activate (wrapped_context, FALSE); } GST_START_TEST (test_wrapped_context) diff --git a/tests/check/libs/gstglupload.c b/tests/check/libs/gstglupload.c index b04eb9ee34..fac69d08a2 100644 --- a/tests/check/libs/gstglupload.c +++ b/tests/check/libs/gstglupload.c @@ -29,41 +29,14 @@ #include -#if GST_GL_HAVE_GLES2 -/* *INDENT-OFF* */ -static const gchar *vertex_shader_str_gles2 = - "attribute vec4 a_position; \n" - "attribute vec2 a_texCoord; \n" - "varying vec2 v_texCoord; \n" - "void main() \n" - "{ \n" - " gl_Position = a_position; \n" - " v_texCoord = a_texCoord; \n" - "} \n"; - -static const gchar *fragment_shader_str_gles2 = - "precision mediump float; \n" - "varying vec2 v_texCoord; \n" - "uniform sampler2D s_texture; \n" - "void main() \n" - "{ \n" - " gl_FragColor = texture2D( s_texture, v_texCoord );\n" - "} \n"; -/* *INDENT-ON* */ -#endif - static GstGLDisplay *display; static GstGLContext *context; static GstGLWindow *window; static GstGLUpload *upload; static guint tex_id; -#if GST_GL_HAVE_GLES2 -static GError *error; static GstGLShader *shader; static GLint shader_attr_position_loc; static GLint shader_attr_texture_loc; -#endif - #define FORMAT GST_VIDEO_GL_TEXTURE_TYPE_RGBA #define WIDTH 10 @@ -118,25 +91,15 @@ teardown (void) static void init (gpointer data) { -#if GST_GL_HAVE_GLES2 - if (gst_gl_context_get_gl_api (context) & GST_GL_API_GLES2) { - shader = gst_gl_shader_new (context); - fail_if (shader == NULL, "failed to create shader object"); + GError *error = NULL; - gst_gl_shader_set_vertex_source (shader, vertex_shader_str_gles2); - gst_gl_shader_set_fragment_source (shader, fragment_shader_str_gles2); + shader = gst_gl_shader_new_default (context, &error); + fail_if (shader == NULL, "failed to create shader object %s", error->message); - error = NULL; - gst_gl_shader_compile (shader, &error); - fail_if (error != NULL, "Error compiling shader %s\n", - error ? error->message : "Unknown Error"); - - shader_attr_position_loc = - gst_gl_shader_get_attribute_location (shader, "a_position"); - shader_attr_texture_loc = - gst_gl_shader_get_attribute_location (shader, "a_texCoord"); - } -#endif + shader_attr_position_loc = + gst_gl_shader_get_attribute_location (shader, "a_position"); + shader_attr_texture_loc = + gst_gl_shader_get_attribute_location (shader, "a_texCoord"); } static void @@ -145,81 +108,38 @@ draw_render (gpointer data) GstGLContext *context = data; GstGLContextClass *context_class = GST_GL_CONTEXT_GET_CLASS (context); const GstGLFuncs *gl = context->gl_vtable; + const GLfloat vVertices[] = { 1.0f, 1.0f, 0.0f, + 1.0f, 0.0f, + -1.0f, 1.0f, 0.0f, + 0.0f, 0.0f, + -1.0f, -1.0f, 0.0f, + 0.0f, 1.0f, + 1.0f, -1.0f, 0.0f, + 1.0f, 1.0f + }; - /* redraw the texture into the system provided framebuffer */ + GLushort indices[] = { 0, 1, 2, 0, 2, 3 }; -#if GST_GL_HAVE_OPENGL - if (gst_gl_context_get_gl_api (context) & GST_GL_API_OPENGL) { - GLfloat verts[8] = { 1.0f, 1.0f, - -1.0f, 1.0f, - -1.0f, -1.0f, - 1.0f, -1.0f - }; - GLfloat texcoords[8] = { 1.0f, 0.0f, - 0.0f, 0.0f, - 0.0f, 1.0f, - 1.0f, 1.0f - }; + gl->Clear (GL_COLOR_BUFFER_BIT); - gl->Viewport (0, 0, 10, 10); + gst_gl_shader_use (shader); - gl->Clear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + /* Load the vertex position */ + gl->VertexAttribPointer (shader_attr_position_loc, 3, + GL_FLOAT, GL_FALSE, 5 * sizeof (GLfloat), vVertices); - gl->MatrixMode (GL_PROJECTION); - gl->LoadIdentity (); + /* Load the texture coordinate */ + gl->VertexAttribPointer (shader_attr_texture_loc, 2, + GL_FLOAT, GL_FALSE, 5 * sizeof (GLfloat), &vVertices[3]); - gl->ActiveTexture (GL_TEXTURE_2D); - gl->BindTexture (GL_TEXTURE_2D, tex_id); + gl->EnableVertexAttribArray (shader_attr_position_loc); + gl->EnableVertexAttribArray (shader_attr_texture_loc); - gl->EnableClientState (GL_VERTEX_ARRAY); - gl->VertexPointer (2, GL_FLOAT, 0, &verts); + gl->ActiveTexture (GL_TEXTURE0); + gl->BindTexture (GL_TEXTURE_2D, tex_id); + gst_gl_shader_set_uniform_1i (shader, "s_texture", 0); - gl->ClientActiveTexture (GL_TEXTURE0); - gl->EnableClientState (GL_TEXTURE_COORD_ARRAY); - gl->TexCoordPointer (2, GL_FLOAT, 0, &texcoords); - - gl->DrawArrays (GL_TRIANGLE_FAN, 0, 4); - - gl->DisableClientState (GL_VERTEX_ARRAY); - gl->DisableClientState (GL_TEXTURE_COORD_ARRAY); - } -#endif -#if GST_GL_HAVE_GLES2 - if (gst_gl_context_get_gl_api (context) & GST_GL_API_GLES2) { - const GLfloat vVertices[] = { 1.0f, 1.0f, 0.0f, - 1.0f, 0.0f, - -1.0f, 1.0f, 0.0f, - 0.0f, 0.0f, - -1.0f, -1.0f, 0.0f, - 0.0f, 1.0f, - 1.0f, -1.0f, 0.0f, - 1.0f, 1.0f - }; - - GLushort indices[] = { 0, 1, 2, 0, 2, 3 }; - - gl->Clear (GL_COLOR_BUFFER_BIT); - - gst_gl_shader_use (shader); - - /* Load the vertex position */ - gl->VertexAttribPointer (shader_attr_position_loc, 3, - GL_FLOAT, GL_FALSE, 5 * sizeof (GLfloat), vVertices); - - /* Load the texture coordinate */ - gl->VertexAttribPointer (shader_attr_texture_loc, 2, - GL_FLOAT, GL_FALSE, 5 * sizeof (GLfloat), &vVertices[3]); - - gl->EnableVertexAttribArray (shader_attr_position_loc); - gl->EnableVertexAttribArray (shader_attr_texture_loc); - - gl->ActiveTexture (GL_TEXTURE0); - gl->BindTexture (GL_TEXTURE_2D, tex_id); - gst_gl_shader_set_uniform_1i (shader, "s_texture", 0); - - gl->DrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices); - } -#endif + gl->DrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices); context_class->swap_buffers (context); }