glshader: attempt to detect the gles2 inside opengl3 case
authorMatthew Waters <matthew@centricular.com>
Tue, 12 May 2015 12:04:26 +0000 (22:04 +1000)
committerTim-Philipp Müller <tim@centricular.com>
Sat, 9 Dec 2017 19:32:01 +0000 (19:32 +0000)
This is necessary to use gles2 shaders in a GL 3 core context on
OS X which fails without a proper #version being set on the shaders.

gst-libs/gst/gl/gstglshader.c

index 8b06d6e..016e475 100644 (file)
 #include "gl.h"
 #include "gstglshader.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"
@@ -107,6 +112,8 @@ struct _GstGLShaderPrivate
   gboolean compiled;
   gboolean active;
 
+  GstGLAPI gl_api;
+
   GstGLShaderVTable vtable;
 };
 
@@ -324,6 +331,9 @@ gst_gl_shader_init (GstGLShader * self)
 
   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;
 }
 
 static gboolean
@@ -395,6 +405,80 @@ gst_gl_shader_is_compiled (GstGLShader * shader)
   return shader->priv->compiled;
 }
 
+static gboolean
+_shader_string_has_version (const gchar * str)
+{
+  gboolean sl_comment = FALSE;
+  gboolean ml_comment = FALSE;
+  gboolean has_version = FALSE;
+  gint i = 0;
+
+  /* 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;
+    }
+
+    if (ml_comment) {
+      if (g_strstr_len (&str[i], 2, "*/")) {
+        ml_comment = FALSE;
+        i += 2;
+      } else {
+        i++;
+      }
+      continue;
+    }
+
+    if (g_strstr_len (&str[i], 2, "//")) {
+      sl_comment = TRUE;
+      i += 2;
+      continue;
+    }
+
+    if (g_strstr_len (&str[i], 2, "/*")) {
+      ml_comment = TRUE;
+      i += 2;
+      continue;
+    }
+
+    if (g_strstr_len (&str[i], 1, "#")) {
+      if (g_strstr_len (&str[i], 8, "#version"))
+        has_version = TRUE;
+      break;
+    }
+
+    i++;
+  }
+
+  return has_version;
+}
+
+static void
+_maybe_prepend_version (GstGLShader * shader, const gchar * shader_str,
+    gint * n_vertex_sources, const gchar *** vertex_sources)
+{
+  gint n = 1;
+
+  /* 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;
+}
+
 gboolean
 gst_gl_shader_compile (GstGLShader * shader, GError ** error)
 {
@@ -423,10 +507,18 @@ gst_gl_shader_compile (GstGLShader * shader, GError ** error)
   g_return_val_if_fail (priv->program_handle, FALSE);
 
   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 */
-    const gchar *vertex_source = priv->vertex_src;
     priv->vertex_handle = priv->vtable.CreateShader (GL_VERTEX_SHADER);
-    gl->ShaderSource (priv->vertex_handle, 1, &vertex_source, NULL);
+    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 */
@@ -455,10 +547,17 @@ gst_gl_shader_compile (GstGLShader * shader, GError ** error)
   }
 
   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 */
-    const gchar *fragment_source = priv->fragment_src;
     priv->fragment_handle = priv->vtable.CreateShader (GL_FRAGMENT_SHADER);
-    gl->ShaderSource (priv->fragment_handle, 1, &fragment_source, NULL);
+    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 */