gl: add some GLSL utility functions
authorMatthew Waters <matthew@centricular.com>
Thu, 20 Aug 2015 05:11:06 +0000 (15:11 +1000)
committerTim-Philipp Müller <tim@centricular.com>
Sat, 9 Dec 2017 19:32:09 +0000 (19:32 +0000)
Specifically parsing/setting GLSL versions and the shader related
function table.

gst-libs/gst/gl/Makefile.am
gst-libs/gst/gl/gstglsl.c [new file with mode: 0644]
gst-libs/gst/gl/gstglsl.h [new file with mode: 0644]
gst-libs/gst/gl/gstglsl_private.h [new file with mode: 0644]
tests/check/libs/gstglsl.c [new file with mode: 0644]

index 7c2b938..239f5c4 100644 (file)
@@ -18,6 +18,7 @@ libgstgl_@GST_API_VERSION@_la_SOURCES = \
        gstglfilter.c \
        gstglbasefilter.c \
        gstglshader.c \
+       gstglsl.c \
        gstglshadervariables.c \
        gstglcolorconvert.c \
        gstgldownload.c \
@@ -31,6 +32,7 @@ libgstgl_@GST_API_VERSION@_la_SOURCES = \
        gstglsyncmeta.c \
        gstglviewconvert.c \
        gstgloverlaycompositor.c \
+       gstglsl_private.h \
        utils/opengl_versions.h
 
 libgstgl_@GST_API_VERSION@includedir = $(includedir)/gstreamer-@GST_API_VERSION@/gst/gl
@@ -46,6 +48,7 @@ libgstgl_@GST_API_VERSION@include_HEADERS = \
        gstglbasefilter.h \
        gstglshadervariables.h \
        gstglshader.h \
+       gstglsl.h \
        gstglcolorconvert.h \
        gstgldownload.h \
        gstgluploadmeta.h \
diff --git a/gst-libs/gst/gl/gstglsl.c b/gst-libs/gst/gl/gstglsl.c
new file mode 100644 (file)
index 0000000..b26872f
--- /dev/null
@@ -0,0 +1,543 @@
+/*
+ * GStreamer
+ * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/gl/gl.h>
+
+#include "gstglsl.h"
+#include "gstglsl_private.h"
+
+GQuark
+gst_glsl_error_quark (void)
+{
+  return g_quark_from_static_string ("gst-glsl-error");
+}
+
+struct glsl_version_string
+{
+  GstGLSLVersion version;
+  const gchar *name;
+};
+
+static const struct glsl_version_string glsl_versions[] = {
+  /* keep in sync with definition in the header */
+  {GST_GLSL_VERSION_100, "100"},
+  {GST_GLSL_VERSION_110, "110"},
+  {GST_GLSL_VERSION_120, "120"},
+  {GST_GLSL_VERSION_130, "130"},
+  {GST_GLSL_VERSION_140, "140"},
+  {GST_GLSL_VERSION_150, "150"},
+  {GST_GLSL_VERSION_300, "300"},
+  {GST_GLSL_VERSION_310, "310"},
+  {GST_GLSL_VERSION_320, "320"},
+  {GST_GLSL_VERSION_330, "330"},
+  {GST_GLSL_VERSION_400, "400"},
+  {GST_GLSL_VERSION_410, "410"},
+  {GST_GLSL_VERSION_420, "420"},
+  {GST_GLSL_VERSION_430, "430"},
+  {GST_GLSL_VERSION_440, "440"},
+  {GST_GLSL_VERSION_450, "450"},
+};
+
+struct glsl_profile_string
+{
+  GstGLSLProfile profile;
+  const gchar *name;
+};
+
+static const struct glsl_profile_string glsl_profiles[] = {
+  /* keep in sync with definition in the header */
+  {GST_GLSL_PROFILE_ES, "es"},
+  {GST_GLSL_PROFILE_CORE, "core"},
+  {GST_GLSL_PROFILE_COMPATIBILITY, "compatibility"},
+};
+
+const gchar *
+gst_glsl_version_to_string (GstGLSLVersion version)
+{
+  int i;
+
+  if (version == GST_GLSL_VERSION_NONE)
+    return NULL;
+
+  for (i = 0; i < G_N_ELEMENTS (glsl_versions); i++) {
+    if (version == glsl_versions[i].version)
+      return glsl_versions[i].name;
+  }
+
+  return NULL;
+}
+
+GstGLSLVersion
+gst_glsl_version_from_string (const gchar * string)
+{
+  gchar *str;
+  int i;
+
+  if (string == NULL)
+    return 0;
+
+  str = g_strdup (string);
+  str = g_strstrip (str);
+
+  for (i = 0; i < G_N_ELEMENTS (glsl_versions); i++) {
+    if (g_strcmp0 (str, glsl_versions[i].name) == 0) {
+      g_free (str);
+      return glsl_versions[i].version;
+    }
+  }
+
+  g_free (str);
+  return 0;
+}
+
+const gchar *
+gst_glsl_profile_to_string (GstGLSLProfile profile)
+{
+  int i;
+
+  if (profile == GST_GLSL_PROFILE_NONE)
+    return NULL;
+
+  /* multiple profiles are not allowed */
+  if ((profile & (profile - 1)) != 0)
+    return NULL;
+
+  for (i = 0; i < G_N_ELEMENTS (glsl_profiles); i++) {
+    if (profile == glsl_profiles[i].profile)
+      return glsl_profiles[i].name;
+  }
+
+  return NULL;
+}
+
+GstGLSLProfile
+gst_glsl_profile_from_string (const gchar * string)
+{
+  gchar *str;
+  int i;
+
+  if (string == NULL)
+    return GST_GLSL_PROFILE_NONE;
+
+  str = g_strdup (string);
+  str = g_strstrip (str);
+
+  for (i = 0; i < G_N_ELEMENTS (glsl_profiles); i++) {
+    if (g_strcmp0 (str, glsl_profiles[i].name) == 0) {
+      g_free (str);
+      return glsl_profiles[i].profile;
+    }
+  }
+
+  g_free (str);
+  return GST_GLSL_PROFILE_NONE;
+}
+
+static gboolean
+_is_valid_version_profile (GstGLSLVersion version, GstGLSLProfile profile)
+{
+  if (version == GST_GLSL_VERSION_NONE)
+    return TRUE;
+
+  /* versions that may not need an explicit profile */
+  if (version <= GST_GLSL_VERSION_150 && profile == GST_GLSL_PROFILE_NONE)
+    return TRUE;
+
+  /* ES versions require an ES profile */
+  if (version == GST_GLSL_VERSION_100 || version == GST_GLSL_VERSION_300
+      || version == GST_GLSL_VERSION_310 || version == GST_GLSL_VERSION_320)
+    return profile == GST_GLSL_PROFILE_ES;
+
+  /* required profile and no ES profile for normal GL contexts */
+  if (version >= GST_GLSL_VERSION_330)
+    return profile == GST_GLSL_PROFILE_NONE || profile == GST_GLSL_PROFILE_CORE
+        || profile == GST_GLSL_PROFILE_COMPATIBILITY;
+
+  if (version <= GST_GLSL_VERSION_150)
+    return profile == GST_GLSL_PROFILE_NONE
+        || profile == GST_GLSL_PROFILE_COMPATIBILITY;
+
+  return FALSE;
+}
+
+gchar *
+gst_glsl_version_profile_to_string (GstGLSLVersion version,
+    GstGLSLProfile profile)
+{
+  const gchar *version_s, *profile_s;
+
+  if (!_is_valid_version_profile (version, profile))
+    return NULL;
+
+  version_s = gst_glsl_version_to_string (version);
+  /* no profiles in GL/ES <= 150 */
+  if (version <= GST_GLSL_VERSION_150)
+    profile_s = NULL;
+  else
+    profile_s = gst_glsl_profile_to_string (profile);
+
+  if (!version_s)
+    return NULL;
+
+  if (profile_s)
+    return g_strdup_printf ("%s %s", version_s, profile_s);
+
+  return g_strdup (version_s);
+}
+
+static void
+_fixup_version_profile (GstGLSLVersion * version, GstGLSLProfile * profile)
+{
+  if (*version == GST_GLSL_VERSION_100 || *version == GST_GLSL_VERSION_300
+      || *version == GST_GLSL_VERSION_310 || *version == GST_GLSL_VERSION_320)
+    *profile = GST_GLSL_PROFILE_ES;
+  else if (*version <= GST_GLSL_VERSION_150)
+    *profile = GST_GLSL_PROFILE_COMPATIBILITY;
+  else if (*profile == GST_GLSL_PROFILE_NONE
+      && *version >= GST_GLSL_VERSION_330)
+    *profile = GST_GLSL_PROFILE_CORE;
+}
+
+/* @str points to the start of "#version", "#    version" or "#\tversion", etc */
+static const gchar *
+_check_valid_version_preprocessor_string (const gchar * str)
+{
+  gint i = 0;
+
+  if (!str || !str[i])
+    return NULL;
+
+  /* there can be whitespace between the '#' and 'version' */
+  do {
+    i++;
+    if (str[i] == '\0' || str[i] == '\n' || str[i] == '\r')
+      return NULL;
+  } while (g_ascii_isspace (str[i]));
+  if (g_strstr_len (&str[i], 7, "version"))
+    return &str[i + 7];
+
+  return NULL;
+}
+
+gboolean
+gst_glsl_version_profile_from_string (const gchar * string,
+    GstGLSLVersion * version_ret, GstGLSLProfile * profile_ret)
+{
+  gchar *str, *version_s, *profile_s;
+  GstGLSLVersion version;
+  GstGLSLProfile profile;
+  gint i;
+
+  if (!string)
+    goto error;
+
+  str = g_strdup (string);
+  version_s = g_strstrip (str);
+
+  /* skip possible #version prefix */
+  if (str[0] == '#') {
+    if (!(version_s =
+            (gchar *) _check_valid_version_preprocessor_string (version_s)))
+      goto error;
+  }
+
+  version_s = g_strstrip (version_s);
+
+  i = 0;
+  while (version_s && version_s[i] != '\0' && g_ascii_isdigit (version_s[i]))
+    i++;
+  /* wrong version length */
+  if (i != 3) {
+    g_free (str);
+    goto error;
+  }
+
+  version_s[i] = '\0';
+  i++;
+  profile_s = &version_s[i];
+  profile_s = g_strstrip (profile_s);
+
+  profile = gst_glsl_profile_from_string (profile_s);
+  version = gst_glsl_version_from_string (version_s);
+  g_free (str);
+
+  /* check whether the parsed data is valid */
+  if (!version)
+    goto error;
+  if (!_is_valid_version_profile (version, profile))
+    goto error;
+  /* got a profile when none was expected */
+  if (version <= GST_GLSL_VERSION_150 && profile != GST_GLSL_PROFILE_NONE)
+    goto error;
+
+  _fixup_version_profile (&version, &profile);
+
+  if (profile_ret)
+    *profile_ret = profile;
+  if (version_ret)
+    *version_ret = version;
+
+  return TRUE;
+
+error:
+  {
+    if (profile_ret)
+      *profile_ret = GST_GLSL_PROFILE_NONE;
+    if (version_ret)
+      *version_ret = GST_GLSL_VERSION_NONE;
+    return FALSE;
+  }
+}
+
+/* returns the pointer in @str to the #version declaration */
+const gchar *
+_gst_glsl_shader_string_find_version (const gchar * str)
+{
+  gboolean sl_comment = FALSE;
+  gboolean ml_comment = FALSE;
+  gboolean newline = TRUE;
+  gint i = 0;
+
+  /* search for #version while allowing for preceeding comments/whitespace as
+   * permitted by the GLSL specification */
+  while (str && str[i] != '\0' && i < 1024) {
+    if (str[i] == '\n' || str[i] == '\r') {
+      newline = TRUE;
+      sl_comment = FALSE;
+      i++;
+      continue;
+    }
+
+    if (g_ascii_isspace (str[i]))
+      goto next;
+
+    if (sl_comment)
+      goto next;
+
+    if (ml_comment) {
+      if (g_strstr_len (&str[i], 2, "*/")) {
+        ml_comment = FALSE;
+        i++;
+      }
+      goto next;
+    }
+
+    if (g_strstr_len (&str[i], 2, "//")) {
+      sl_comment = TRUE;
+      i++;
+      goto next;
+    }
+
+    if (g_strstr_len (&str[i], 2, "/*")) {
+      ml_comment = TRUE;
+      i++;
+      goto next;
+    }
+
+    if (str[i] == '#') {
+      if (newline && _check_valid_version_preprocessor_string (&str[i]))
+        return &str[i];
+      break;
+    }
+
+  next:
+    newline = FALSE;
+    i++;
+  }
+
+  return NULL;
+}
+
+gboolean
+gst_glsl_string_get_version_profile (const gchar * s, GstGLSLVersion * version,
+    GstGLSLProfile * profile)
+{
+  const gchar *version_profile_s;
+
+  version_profile_s = _gst_glsl_shader_string_find_version (s);
+  if (!version_profile_s)
+    goto error;
+
+  if (!gst_glsl_version_profile_from_string (version_profile_s, version,
+          profile))
+    goto error;
+
+  return TRUE;
+
+error:
+  {
+    if (version)
+      *version = GST_GLSL_VERSION_NONE;
+    if (profile)
+      *profile = GST_GLSL_PROFILE_NONE;
+    return FALSE;
+  }
+}
+
+GstGLSLVersion
+gst_gl_version_to_glsl_version (GstGLAPI gl_api, gint maj, gint min)
+{
+  g_return_val_if_fail (gl_api != GST_GL_API_NONE, 0);
+
+  if (gl_api & GST_GL_API_GLES2) {
+    if (maj == 2 && min == 0)
+      return 100;
+
+    if (maj == 3 && min >= 0 && min <= 2)
+      return maj * 100 + min * 10;
+
+    return 0;
+  }
+
+  /* versions match for >= 3.3 */
+  if (gl_api & (GST_GL_API_OPENGL3 | GST_GL_API_OPENGL)) {
+    if (maj > 3 || (maj == 3 && min >= 3))
+      return maj * 100 + min * 10;
+
+    if (maj == 3 && min == 2)
+      return 150;
+    if (maj == 3 && min == 1)
+      return 140;
+    if (maj == 3 && min == 0)
+      return 130;
+    if (maj == 2 && min == 1)
+      return 120;
+    if (maj == 2 && min == 0)
+      return 110;
+
+    return 0;
+  }
+
+  return 0;
+}
+
+gboolean
+gst_gl_context_supports_glsl_profile_version (GstGLContext * context,
+    GstGLSLVersion version, GstGLSLProfile profile)
+{
+  g_return_val_if_fail (GST_GL_IS_CONTEXT (context), FALSE);
+
+  if (!_is_valid_version_profile (version, profile))
+    return FALSE;
+
+  if (profile != GST_GLSL_PROFILE_NONE) {
+    if (gst_gl_context_check_gl_version (context, GST_GL_API_GLES2, 2, 0)) {
+      if ((profile & GST_GLSL_PROFILE_ES) == 0)
+        return FALSE;
+    } else if ((gst_gl_context_get_gl_api (context) & GST_GL_API_OPENGL) != 0) {
+      if ((profile & GST_GLSL_PROFILE_COMPATIBILITY) == 0)
+        return FALSE;
+    } else if ((gst_gl_context_get_gl_api (context) & GST_GL_API_OPENGL3) != 0) {
+      /* GL_ARB_es2_compatibility is requried for GL3 contexts */
+      if ((profile & (GST_GLSL_PROFILE_CORE | GST_GLSL_PROFILE_ES)) == 0)
+        return FALSE;
+    } else {
+      g_assert_not_reached ();
+    }
+  }
+
+  if (version != GST_GLSL_VERSION_NONE) {
+    GstGLAPI gl_api;
+    gint maj, min, glsl_version;
+
+    if (gst_gl_context_check_gl_version (context, GST_GL_API_GLES2, 3, 1)) {
+      if (version > GST_GLSL_VERSION_310)
+        return FALSE;
+    } else if (gst_gl_context_check_gl_version (context, GST_GL_API_GLES2, 3,
+            0)) {
+      if (version > GST_GLSL_VERSION_300)
+        return FALSE;
+    } else if (gst_gl_context_check_gl_version (context, GST_GL_API_GLES2, 2,
+            0)) {
+      if (version > GST_GLSL_VERSION_100)
+        return FALSE;
+    }
+
+    gl_api = gst_gl_context_get_gl_api (context);
+    gst_gl_context_get_gl_version (context, &maj, &min);
+    glsl_version = gst_gl_version_to_glsl_version (gl_api, maj, min);
+    if (glsl_version > version)
+      return FALSE;
+
+    if (gst_gl_context_check_gl_version (context, GST_GL_API_OPENGL3, 1, 0))
+      /* GL_ARB_es2_compatibility is requried for GL3 contexts */
+      if (version < GST_GLSL_VERSION_150 && version != GST_GLSL_VERSION_100)
+        return FALSE;
+
+    if (gst_gl_context_check_gl_version (context, GST_GL_API_OPENGL, 1, 0)
+        && version < GST_GLSL_VERSION_110)
+      return FALSE;
+  }
+
+  return TRUE;
+}
+
+gboolean
+_gst_glsl_funcs_fill (GstGLSLFuncs * vtable, GstGLContext * context)
+{
+  GstGLFuncs *gl = context->gl_vtable;
+
+  if (vtable->initialized)
+    return TRUE;
+
+  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 {
+    vtable->initialized = FALSE;
+    return FALSE;
+  }
+
+  vtable->initialized = TRUE;
+  return TRUE;
+}
diff --git a/gst-libs/gst/gl/gstglsl.h b/gst-libs/gst/gl/gstglsl.h
new file mode 100644 (file)
index 0000000..9f051f6
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * GStreamer
+ * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GST_GLSL_H__
+#define __GST_GLSL_H__
+
+#include <gst/gl/gstgl_fwd.h>
+
+G_BEGIN_DECLS
+
+GQuark gst_glsl_error_quark (void);
+#define GST_GLSL_ERROR (gst_glsl_error_quark ())
+
+typedef enum {
+  GST_GLSL_ERROR_COMPILE,
+  GST_GLSL_ERROR_LINK,
+  GST_GLSL_ERROR_PROGRAM,
+} GstGLSLError;
+
+typedef enum
+{
+  GST_GLSL_VERSION_NONE = 0,
+
+  GST_GLSL_VERSION_100 = 100, /* ES */
+  GST_GLSL_VERSION_110 = 110, /* GL */
+  GST_GLSL_VERSION_120 = 120, /* GL */
+  GST_GLSL_VERSION_130 = 130, /* GL */
+  GST_GLSL_VERSION_140 = 140, /* GL */
+  GST_GLSL_VERSION_150 = 150, /* GL */
+  GST_GLSL_VERSION_300 = 300, /* ES */
+  GST_GLSL_VERSION_310 = 310, /* ES */
+  GST_GLSL_VERSION_320 = 320, /* ES */
+  GST_GLSL_VERSION_330 = 330, /* GL */
+  GST_GLSL_VERSION_400 = 400, /* GL */
+  GST_GLSL_VERSION_410 = 410, /* GL */
+  GST_GLSL_VERSION_420 = 420, /* GL */
+  GST_GLSL_VERSION_430 = 430, /* GL */
+  GST_GLSL_VERSION_440 = 440, /* GL */
+  GST_GLSL_VERSION_450 = 450, /* GL */
+
+  GST_GLSL_VERSION_ANY = -1,
+} GstGLSLVersion;
+
+typedef enum
+{
+  /* XXX: maybe make GstGLAPI instead */
+  GST_GLSL_PROFILE_NONE = 0,
+
+  GST_GLSL_PROFILE_ES = (1 << 0),
+  GST_GLSL_PROFILE_CORE = (1 << 1),
+  GST_GLSL_PROFILE_COMPATIBILITY = (1 << 2),
+
+  GST_GLSL_PROFILE_ANY = -1,
+} GstGLSLProfile;
+
+GstGLSLVersion gst_glsl_version_from_string         (const gchar * string);
+const gchar *  gst_glsl_version_to_string           (GstGLSLVersion version);
+
+GstGLSLProfile gst_glsl_profile_from_string         (const gchar * string);
+const gchar *  gst_glsl_profile_to_string           (GstGLSLProfile profile);
+
+gchar *        gst_glsl_version_profile_to_string   (GstGLSLVersion version,
+                                                     GstGLSLProfile profile);
+gboolean       gst_glsl_version_profile_from_string (const gchar * string,
+                                                     GstGLSLVersion * version,
+                                                     GstGLSLProfile * profile);
+
+gboolean       gst_glsl_string_get_version_profile  (const gchar *s,
+                                                     GstGLSLVersion * version,
+                                                     GstGLSLProfile * profile);
+
+GstGLSLVersion gst_gl_version_to_glsl_version       (GstGLAPI gl_api, gint maj, gint min);
+gboolean       gst_gl_context_supports_glsl_profile_version (GstGLContext * context,
+                                                             GstGLSLVersion version,
+                                                             GstGLSLProfile profile);
+
+G_END_DECLS
+
+#endif /* __GST_GLSL_H__ */
diff --git a/gst-libs/gst/gl/gstglsl_private.h b/gst-libs/gst/gl/gstglsl_private.h
new file mode 100644 (file)
index 0000000..c23ff91
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * GStreamer
+ * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GST_GLSL_PRIVATE_H__
+#define __GST_GLSL_PRIVATE_H__
+
+#include <gst/gl/gstgl_fwd.h>
+
+G_BEGIN_DECLS
+
+#ifndef GL_COMPILE_STATUS
+#define GL_COMPILE_STATUS             0x8B81
+#endif
+#ifndef GLhandleARB
+#define GLhandleARB GLuint
+#endif
+
+typedef struct _GstGLSLFuncs
+{
+  gboolean initialized;
+
+  GLuint GSTGLAPI (*CreateProgram) (void);
+  void GSTGLAPI (*DeleteProgram) (GLuint program);
+  void GSTGLAPI (*UseProgram) (GLuint program);
+  void GSTGLAPI (*GetAttachedShaders) (GLuint program, GLsizei maxcount,
+      GLsizei * count, GLuint * shaders);
+
+  GLuint GSTGLAPI (*CreateShader) (GLenum shaderType);
+  void GSTGLAPI (*DeleteShader) (GLuint shader);
+  void GSTGLAPI (*AttachShader) (GLuint program, GLuint shader);
+  void GSTGLAPI (*DetachShader) (GLuint program, GLuint shader);
+
+  void GSTGLAPI (*GetShaderiv) (GLuint program, GLenum pname, GLint * params);
+  void GSTGLAPI (*GetProgramiv) (GLuint program, GLenum pname, GLint * params);
+  void GSTGLAPI (*GetShaderInfoLog) (GLuint shader, GLsizei maxLength,
+      GLsizei * length, char *log);
+  void GSTGLAPI (*GetProgramInfoLog) (GLuint shader, GLsizei maxLength,
+      GLsizei * length, char *log);
+} GstGLSLFuncs;
+
+gboolean _gst_glsl_funcs_fill (GstGLSLFuncs * vtable, GstGLContext * context);
+const gchar * _gst_glsl_shader_string_find_version (const gchar * str);
+
+G_END_DECLS
+
+#endif /* __GST_GLSL_PRIVATE_H__ */
diff --git a/tests/check/libs/gstglsl.c b/tests/check/libs/gstglsl.c
new file mode 100644 (file)
index 0000000..ce69731
--- /dev/null
@@ -0,0 +1,307 @@
+/* GStreamer
+ * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include <gst/check/gstcheck.h>
+
+#include <gst/gl/gstglsl.h>
+
+#include <stdio.h>
+
+static void
+setup (void)
+{
+}
+
+static void
+teardown (void)
+{
+}
+
+/* *INDENT-OFF* */
+static const struct {GstGLSLVersion version; const gchar * name;} glsl_versions[] = {
+  {GST_GLSL_VERSION_100, "100"},
+  {GST_GLSL_VERSION_110, "110"},
+  {GST_GLSL_VERSION_120, "120"},
+  {GST_GLSL_VERSION_130, "130"},
+  {GST_GLSL_VERSION_140, "140"},
+  {GST_GLSL_VERSION_150, "150"},
+  {GST_GLSL_VERSION_300, "300"},
+  {GST_GLSL_VERSION_310, "310"},
+  {GST_GLSL_VERSION_320, "320"},
+  {GST_GLSL_VERSION_330, "330"},
+  {GST_GLSL_VERSION_400, "400"},
+  {GST_GLSL_VERSION_410, "410"},
+  {GST_GLSL_VERSION_420, "420"},
+  {GST_GLSL_VERSION_430, "430"},
+  {GST_GLSL_VERSION_440, "440"},
+  {GST_GLSL_VERSION_450, "450"},
+};
+
+static const struct {GstGLSLProfile profile; const gchar * name;} glsl_profiles[] = {
+  {GST_GLSL_PROFILE_ES, "es"},
+  {GST_GLSL_PROFILE_CORE, "core"},
+  {GST_GLSL_PROFILE_COMPATIBILITY, "compatibility"},
+};
+
+static const struct {GstGLSLVersion version; GstGLSLProfile profile; const gchar * name;} glsl_version_profiles[] = {
+  {GST_GLSL_VERSION_100, GST_GLSL_PROFILE_ES, "100"},
+  {GST_GLSL_VERSION_110, GST_GLSL_PROFILE_COMPATIBILITY, "110"},
+  {GST_GLSL_VERSION_120, GST_GLSL_PROFILE_COMPATIBILITY, "120"},
+  {GST_GLSL_VERSION_130, GST_GLSL_PROFILE_COMPATIBILITY, "130"},
+  {GST_GLSL_VERSION_140, GST_GLSL_PROFILE_COMPATIBILITY, "140"},
+  {GST_GLSL_VERSION_150, GST_GLSL_PROFILE_COMPATIBILITY, "150"},
+  {GST_GLSL_VERSION_300, GST_GLSL_PROFILE_ES, "300 es"},
+  {GST_GLSL_VERSION_310, GST_GLSL_PROFILE_ES, "310 es"},
+  {GST_GLSL_VERSION_320, GST_GLSL_PROFILE_ES, "320 es"},
+  {GST_GLSL_VERSION_330, GST_GLSL_PROFILE_CORE, "330 core"},
+  {GST_GLSL_VERSION_330, GST_GLSL_PROFILE_COMPATIBILITY, "330 compatibility"},
+  {GST_GLSL_VERSION_400, GST_GLSL_PROFILE_CORE, "400 core"},
+  {GST_GLSL_VERSION_400, GST_GLSL_PROFILE_COMPATIBILITY, "400 compatibility"},
+  {GST_GLSL_VERSION_410, GST_GLSL_PROFILE_CORE, "410 core"},
+  {GST_GLSL_VERSION_410, GST_GLSL_PROFILE_COMPATIBILITY, "410 compatibility"},
+  {GST_GLSL_VERSION_420, GST_GLSL_PROFILE_CORE, "420 core"},
+  {GST_GLSL_VERSION_420, GST_GLSL_PROFILE_COMPATIBILITY, "420 compatibility"},
+  {GST_GLSL_VERSION_430, GST_GLSL_PROFILE_CORE, "430 core"},
+  {GST_GLSL_VERSION_430, GST_GLSL_PROFILE_COMPATIBILITY, "430 compatibility"},
+  {GST_GLSL_VERSION_440, GST_GLSL_PROFILE_CORE, "440 core"},
+  {GST_GLSL_VERSION_440, GST_GLSL_PROFILE_COMPATIBILITY, "440 compatibility"},
+  {GST_GLSL_VERSION_450, GST_GLSL_PROFILE_CORE, "450 core"},
+  {GST_GLSL_VERSION_450, GST_GLSL_PROFILE_COMPATIBILITY, "450 compatibility"},
+};
+
+static const gchar * invalid_deserialize_glsl[] = {
+  "",
+  " \t\r\n",
+  "ael dja",
+  "es",
+  "core",
+  "compatibility",
+  "1000",
+  "100 es",
+  "100 core",
+  "100 compatibility",
+  "150 es",
+  "150 core",
+  "150 compatibility",
+  "300 core",
+  "300 compatibility",
+  "310 core",
+  "310 compatibility",
+  "320 core",
+  "320 compatibility",
+  "330 es",
+};
+
+static const struct {GstGLSLVersion version; GstGLSLProfile profile;} invalid_serialize_glsl[] = {
+  {GST_GLSL_VERSION_100, GST_GLSL_PROFILE_CORE},
+  {GST_GLSL_VERSION_100, GST_GLSL_PROFILE_COMPATIBILITY},
+  {GST_GLSL_VERSION_110, GST_GLSL_PROFILE_ES},
+  {GST_GLSL_VERSION_110, GST_GLSL_PROFILE_CORE},
+  {GST_GLSL_VERSION_120, GST_GLSL_PROFILE_ES},
+  {GST_GLSL_VERSION_120, GST_GLSL_PROFILE_CORE},
+  {GST_GLSL_VERSION_130, GST_GLSL_PROFILE_ES},
+  {GST_GLSL_VERSION_130, GST_GLSL_PROFILE_CORE},
+  {GST_GLSL_VERSION_140, GST_GLSL_PROFILE_ES},
+  {GST_GLSL_VERSION_140, GST_GLSL_PROFILE_CORE},
+  {GST_GLSL_VERSION_150, GST_GLSL_PROFILE_ES},
+  {GST_GLSL_VERSION_150, GST_GLSL_PROFILE_CORE},
+  {GST_GLSL_VERSION_300, GST_GLSL_PROFILE_NONE},
+  {GST_GLSL_VERSION_300, GST_GLSL_PROFILE_CORE},
+  {GST_GLSL_VERSION_300, GST_GLSL_PROFILE_COMPATIBILITY},
+  {GST_GLSL_VERSION_310, GST_GLSL_PROFILE_NONE},
+  {GST_GLSL_VERSION_310, GST_GLSL_PROFILE_CORE},
+  {GST_GLSL_VERSION_310, GST_GLSL_PROFILE_COMPATIBILITY},
+  {GST_GLSL_VERSION_320, GST_GLSL_PROFILE_NONE},
+  {GST_GLSL_VERSION_320, GST_GLSL_PROFILE_CORE},
+  {GST_GLSL_VERSION_320, GST_GLSL_PROFILE_COMPATIBILITY},
+  {GST_GLSL_VERSION_330, GST_GLSL_PROFILE_ES},
+  {GST_GLSL_VERSION_400, GST_GLSL_PROFILE_ES},
+  {GST_GLSL_VERSION_410, GST_GLSL_PROFILE_ES},
+  {GST_GLSL_VERSION_420, GST_GLSL_PROFILE_ES},
+  {GST_GLSL_VERSION_430, GST_GLSL_PROFILE_ES},
+  {GST_GLSL_VERSION_440, GST_GLSL_PROFILE_ES},
+  {GST_GLSL_VERSION_450, GST_GLSL_PROFILE_ES},
+};
+
+static const struct {const gchar *name; gboolean succeed; GstGLSLVersion version; GstGLSLProfile profile;} glsl_str_map[] = {
+  {"//#version 100\n", FALSE, GST_GLSL_VERSION_NONE, GST_GLSL_PROFILE_NONE},
+  {"/*\n#version 100*/\n", FALSE, GST_GLSL_VERSION_NONE, GST_GLSL_PROFILE_NONE},
+  {"/*\r#version 100*/", FALSE, GST_GLSL_VERSION_NONE, GST_GLSL_PROFILE_NONE},
+  {"#\rversion 100", FALSE, GST_GLSL_VERSION_NONE, GST_GLSL_PROFILE_NONE},
+  {"#\nversion 100", FALSE, GST_GLSL_VERSION_NONE, GST_GLSL_PROFILE_NONE},
+  {"\t#version 100", FALSE, GST_GLSL_VERSION_NONE, GST_GLSL_PROFILE_NONE},
+  {"//\r#version 100", TRUE, GST_GLSL_VERSION_100, GST_GLSL_PROFILE_ES},
+  {"//\n#version 100", TRUE, GST_GLSL_VERSION_100, GST_GLSL_PROFILE_ES},
+  {"# \tversion 100", TRUE, GST_GLSL_VERSION_100, GST_GLSL_PROFILE_ES},
+  {"\n#version 100", TRUE, GST_GLSL_VERSION_100, GST_GLSL_PROFILE_ES},
+  {"\r#version 100", TRUE, GST_GLSL_VERSION_100, GST_GLSL_PROFILE_ES},
+};
+/* *INDENT-ON* */
+
+GST_START_TEST (test_serialization)
+{
+  gint i;
+
+  /* versions */
+  for (i = 0; i < G_N_ELEMENTS (glsl_versions); i++) {
+    GstGLSLVersion version;
+    const gchar *version_s;
+
+    version_s = gst_glsl_version_to_string (glsl_versions[i].version);
+    fail_unless (g_strcmp0 (version_s, glsl_versions[i].name) == 0, "%s != %s",
+        version_s, glsl_versions[i].name);
+    version = gst_glsl_version_from_string (glsl_versions[i].name);
+    fail_unless (version == glsl_versions[i].version, "%s != %s",
+        gst_glsl_version_to_string (glsl_versions[i].version),
+        gst_glsl_version_to_string (version));
+  }
+
+  /* profiles */
+  for (i = 0; i < G_N_ELEMENTS (glsl_profiles); i++) {
+    GstGLSLProfile profile;
+    const gchar *profile_s;
+
+    profile_s = gst_glsl_profile_to_string (glsl_profiles[i].profile);
+    fail_unless (g_strcmp0 (profile_s, glsl_profiles[i].name) == 0, "%s != %s",
+        profile_s, glsl_profiles[i].name);
+    profile = gst_glsl_profile_from_string (glsl_profiles[i].name);
+    fail_unless (profile == glsl_profiles[i].profile, "%s != %s",
+        gst_glsl_profile_to_string (glsl_profiles[i].profile),
+        gst_glsl_profile_to_string (profile));
+  }
+
+  for (i = 0; i < G_N_ELEMENTS (glsl_version_profiles); i++) {
+    gchar *version_profile_s;
+    GstGLSLVersion version;
+    GstGLSLProfile profile;
+
+    version_profile_s =
+        gst_glsl_version_profile_to_string (glsl_version_profiles[i].version,
+        glsl_version_profiles[i].profile);
+    fail_unless (g_strcmp0 (version_profile_s,
+            glsl_version_profiles[i].name) == 0, "%s != %s", version_profile_s,
+        glsl_version_profiles[i].name);
+    fail_unless (gst_glsl_version_profile_from_string (glsl_version_profiles
+            [i].name, &version, &profile), "Failed to parse %s",
+        glsl_version_profiles[i].name);
+    fail_unless (profile == glsl_version_profiles[i].profile
+        && version == glsl_version_profiles[i].version, "%s != %s %s",
+        glsl_version_profiles[i].name, gst_glsl_version_to_string (version),
+        gst_glsl_profile_to_string (profile));
+  }
+
+  /* failures */
+  for (i = 0; i < G_N_ELEMENTS (invalid_deserialize_glsl); i++) {
+    GstGLSLVersion version;
+    GstGLSLProfile profile;
+
+    fail_if (gst_glsl_version_profile_from_string (invalid_deserialize_glsl[i],
+            &version, &profile),
+        "successfully deserialized %s into %s %s (should have failed)",
+        invalid_deserialize_glsl[i], gst_glsl_version_to_string (version),
+        gst_glsl_profile_to_string (profile));
+  }
+
+  /* failures */
+  for (i = 0; i < G_N_ELEMENTS (invalid_serialize_glsl); i++) {
+    gchar *version_profile_s;
+
+    version_profile_s =
+        gst_glsl_version_profile_to_string (invalid_serialize_glsl[i].version,
+        invalid_serialize_glsl[i].profile);
+
+    fail_if (version_profile_s != NULL,
+        "successfully serialized %s from %s %s (should have failed)",
+        version_profile_s,
+        gst_glsl_version_to_string (invalid_serialize_glsl[i].version),
+        gst_glsl_profile_to_string (invalid_serialize_glsl[i].profile));
+
+    g_free (version_profile_s);
+  }
+
+  /* map strings to version/profile */
+  for (i = 0; i < G_N_ELEMENTS (glsl_str_map); i++) {
+    GstGLSLVersion version;
+    GstGLSLProfile profile;
+
+    fail_unless (glsl_str_map[i].succeed ==
+        gst_glsl_string_get_version_profile (glsl_str_map[i].name, &version,
+            &profile), "Incorrect result for parsing \'%s\': %s",
+        glsl_str_map[i].name, glsl_str_map[i].succeed ? "false" : "true");
+    if (glsl_str_map[i].succeed) {
+      fail_unless (version == glsl_str_map[i].version, "With %s: %s != %s",
+          glsl_str_map[i].name,
+          gst_glsl_version_to_string (glsl_str_map[i].version),
+          gst_glsl_version_to_string (version));
+      fail_unless (profile == glsl_str_map[i].profile, "With %s: %s != %s",
+          glsl_str_map[i].name,
+          gst_glsl_profile_to_string (glsl_str_map[i].profile),
+          gst_glsl_profile_to_string (profile));
+    }
+  }
+
+  /* special ones */
+  {
+    GstGLSLVersion version;
+    GstGLSLProfile profile;
+    gchar *version_profile_s;
+
+    version_profile_s =
+        gst_glsl_version_profile_to_string (GST_GLSL_VERSION_100,
+        GST_GLSL_PROFILE_ES);
+    fail_unless (g_strcmp0 (version_profile_s, "100") == 0, "%s != 100",
+        version_profile_s);
+    g_free (version_profile_s);
+
+    version_profile_s =
+        gst_glsl_version_profile_to_string (GST_GLSL_VERSION_100,
+        GST_GLSL_PROFILE_NONE);
+    fail_unless (g_strcmp0 (version_profile_s, "100") == 0, "%s != 100",
+        version_profile_s);
+    g_free (version_profile_s);
+
+    fail_unless (gst_glsl_version_profile_from_string ("100", &version,
+            &profile));
+    fail_unless (version == GST_GLSL_VERSION_100
+        && profile == GST_GLSL_PROFILE_ES, "100 != %s %s",
+        gst_glsl_version_to_string (version),
+        gst_glsl_profile_to_string (profile));
+  }
+}
+
+GST_END_TEST;
+
+static Suite *
+gst_gl_upload_suite (void)
+{
+  Suite *s = suite_create ("GstGLSL");
+  TCase *tc_chain = tcase_create ("glsl");
+
+  suite_add_tcase (s, tc_chain);
+  tcase_add_checked_fixture (tc_chain, setup, teardown);
+  tcase_add_test (tc_chain, test_serialization);
+
+  return s;
+}
+
+GST_CHECK_MAIN (gst_gl_upload);