• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * GStreamer
3  * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include <gst/gl/gl.h>
26 
27 #include "gstglsl.h"
28 #include "gstglsl_private.h"
29 
30 /**
31  * SECTION:gstglsl
32  * @title: GstGLSL
33  * @short_description: helpers for dealing with OpenGL shaders
34  * @see_also: #GstGLSLStage, #GstGLShader
35  */
36 
37 #define GST_CAT_DEFAULT gst_glsl_debug
38 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
39 
40 static void
_init_debug(void)41 _init_debug (void)
42 {
43   static gsize _init = 0;
44 
45   if (g_once_init_enter (&_init)) {
46     GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "glsl", 0,
47         "OpenGL Shading Language");
48     g_once_init_leave (&_init, 1);
49   }
50 }
51 
52 /**
53  * gst_glsl_error_quark:
54  *
55  * Returns: the quark used for GstGLSL in #GError's
56  */
57 GQuark
gst_glsl_error_quark(void)58 gst_glsl_error_quark (void)
59 {
60   return g_quark_from_static_string ("gst-glsl-error-quark");
61 }
62 
63 struct glsl_version_string
64 {
65   GstGLSLVersion version;
66   const gchar *name;
67 };
68 
69 static const struct glsl_version_string glsl_versions[] = {
70   /* keep in sync with definition in the header */
71   {GST_GLSL_VERSION_100, "100"},
72   {GST_GLSL_VERSION_110, "110"},
73   {GST_GLSL_VERSION_120, "120"},
74   {GST_GLSL_VERSION_130, "130"},
75   {GST_GLSL_VERSION_140, "140"},
76   {GST_GLSL_VERSION_150, "150"},
77   {GST_GLSL_VERSION_300, "300"},
78   {GST_GLSL_VERSION_310, "310"},
79   {GST_GLSL_VERSION_320, "320"},
80   {GST_GLSL_VERSION_330, "330"},
81   {GST_GLSL_VERSION_400, "400"},
82   {GST_GLSL_VERSION_410, "410"},
83   {GST_GLSL_VERSION_420, "420"},
84   {GST_GLSL_VERSION_430, "430"},
85   {GST_GLSL_VERSION_440, "440"},
86   {GST_GLSL_VERSION_450, "450"},
87 };
88 
89 struct glsl_profile_string
90 {
91   GstGLSLProfile profile;
92   const gchar *name;
93 };
94 
95 static const struct glsl_profile_string glsl_profiles[] = {
96   /* keep in sync with definition in the header */
97   {GST_GLSL_PROFILE_ES, "es"},
98   {GST_GLSL_PROFILE_CORE, "core"},
99   {GST_GLSL_PROFILE_COMPATIBILITY, "compatibility"},
100 };
101 
102 /**
103  * gst_glsl_version_to_string:
104  * @version: a #GstGLSLVersion
105  *
106  * Returns: (nullable): the name of @version or %NULL on error
107  */
108 const gchar *
gst_glsl_version_to_string(GstGLSLVersion version)109 gst_glsl_version_to_string (GstGLSLVersion version)
110 {
111   int i;
112 
113   if (version == GST_GLSL_VERSION_NONE)
114     return NULL;
115 
116   for (i = 0; i < G_N_ELEMENTS (glsl_versions); i++) {
117     if (version == glsl_versions[i].version)
118       return glsl_versions[i].name;
119   }
120 
121   return NULL;
122 }
123 
124 /**
125  * gst_glsl_version_from_string:
126  * @string: a GLSL version string
127  *
128  * Returns: the #GstGLSLVersion of @string or %GST_GLSL_VERSION_NONE on error
129  */
130 GstGLSLVersion
gst_glsl_version_from_string(const gchar * string)131 gst_glsl_version_from_string (const gchar * string)
132 {
133   gchar *str;
134   int i;
135 
136   if (string == NULL)
137     return 0;
138 
139   str = g_strdup (string);
140   str = g_strstrip (str);
141 
142   for (i = 0; i < G_N_ELEMENTS (glsl_versions); i++) {
143     if (g_strcmp0 (str, glsl_versions[i].name) == 0) {
144       g_free (str);
145       return glsl_versions[i].version;
146     }
147   }
148 
149   g_free (str);
150   return 0;
151 }
152 
153 /**
154  * gst_glsl_profile_to_string:
155  * @profile: a #GstGLSLProfile
156  *
157  * Returns: (nullable): the name for @profile or %NULL on error
158  */
159 const gchar *
gst_glsl_profile_to_string(GstGLSLProfile profile)160 gst_glsl_profile_to_string (GstGLSLProfile profile)
161 {
162   int i;
163 
164   if (profile == GST_GLSL_PROFILE_NONE)
165     return NULL;
166 
167   /* multiple profiles are not allowed */
168   if ((profile & (profile - 1)) != 0)
169     return NULL;
170 
171   for (i = 0; i < G_N_ELEMENTS (glsl_profiles); i++) {
172     if (profile == glsl_profiles[i].profile)
173       return glsl_profiles[i].name;
174   }
175 
176   return NULL;
177 }
178 
179 /**
180  * gst_glsl_profile_from_string:
181  * @string: a GLSL version string
182  *
183  * Returns: the #GstGLSLProfile of @string or %GST_GLSL_PROFILE_NONE on error
184  */
185 GstGLSLProfile
gst_glsl_profile_from_string(const gchar * string)186 gst_glsl_profile_from_string (const gchar * string)
187 {
188   gchar *str;
189   int i;
190 
191   if (string == NULL)
192     return GST_GLSL_PROFILE_NONE;
193 
194   str = g_strdup (string);
195   str = g_strstrip (str);
196 
197   for (i = 0; i < G_N_ELEMENTS (glsl_profiles); i++) {
198     if (g_strcmp0 (str, glsl_profiles[i].name) == 0) {
199       g_free (str);
200       return glsl_profiles[i].profile;
201     }
202   }
203 
204   g_free (str);
205   return GST_GLSL_PROFILE_NONE;
206 }
207 
208 static gboolean
_is_valid_version_profile(GstGLSLVersion version,GstGLSLProfile profile)209 _is_valid_version_profile (GstGLSLVersion version, GstGLSLProfile profile)
210 {
211   if (version == GST_GLSL_VERSION_NONE)
212     return TRUE;
213 
214   /* versions that may not need an explicit profile */
215   if (version <= GST_GLSL_VERSION_150 && profile == GST_GLSL_PROFILE_NONE)
216     return TRUE;
217 
218   /* ES versions require an ES profile */
219   if (version == GST_GLSL_VERSION_100 || version == GST_GLSL_VERSION_300
220       || version == GST_GLSL_VERSION_310 || version == GST_GLSL_VERSION_320)
221     return profile == GST_GLSL_PROFILE_ES;
222 
223   /* required profile and no ES profile for normal GL contexts */
224   if (version == GST_GLSL_VERSION_150 || version >= GST_GLSL_VERSION_330)
225     return profile == GST_GLSL_PROFILE_NONE || profile == GST_GLSL_PROFILE_CORE
226         || profile == GST_GLSL_PROFILE_COMPATIBILITY;
227 
228   if (version <= GST_GLSL_VERSION_140)
229     return profile == GST_GLSL_PROFILE_NONE
230         || profile == GST_GLSL_PROFILE_COMPATIBILITY;
231 
232   return FALSE;
233 }
234 
235 /**
236  * gst_glsl_version_profile_to_string:
237  * @version: a #GstGLSLVersion
238  * @profile: a #GstGLSLVersion
239  *
240  * Returns: the combined GLSL `#version` string for @version and @profile
241  */
242 gchar *
gst_glsl_version_profile_to_string(GstGLSLVersion version,GstGLSLProfile profile)243 gst_glsl_version_profile_to_string (GstGLSLVersion version,
244     GstGLSLProfile profile)
245 {
246   const gchar *version_s, *profile_s;
247 
248   if (!_is_valid_version_profile (version, profile))
249     return NULL;
250 
251   version_s = gst_glsl_version_to_string (version);
252   /* no profiles in GL/ES <= 150 */
253   if (version <= GST_GLSL_VERSION_140)
254     profile_s = NULL;
255   else
256     profile_s = gst_glsl_profile_to_string (profile);
257 
258   if (!version_s)
259     return NULL;
260 
261   if (profile_s)
262     return g_strdup_printf ("%s %s", version_s, profile_s);
263 
264   return g_strdup (version_s);
265 }
266 
267 static void
_fixup_version_profile(GstGLSLVersion * version,GstGLSLProfile * profile)268 _fixup_version_profile (GstGLSLVersion * version, GstGLSLProfile * profile)
269 {
270   if (*version == GST_GLSL_VERSION_100 || *version == GST_GLSL_VERSION_300
271       || *version == GST_GLSL_VERSION_310 || *version == GST_GLSL_VERSION_320)
272     *profile = GST_GLSL_PROFILE_ES;
273   else if (*version <= GST_GLSL_VERSION_140)
274     *profile = GST_GLSL_PROFILE_COMPATIBILITY;
275   else if (*profile == GST_GLSL_PROFILE_NONE
276       && (*version >= GST_GLSL_VERSION_150 || *version >= GST_GLSL_VERSION_330))
277     *profile = GST_GLSL_PROFILE_CORE;
278 }
279 
280 /* @str points to the start of "#version", "#    version" or "#\tversion", etc */
281 static const gchar *
_check_valid_version_preprocessor_string(const gchar * str)282 _check_valid_version_preprocessor_string (const gchar * str)
283 {
284   gint i = 0;
285 
286   if (!str || !str[i])
287     return NULL;
288 
289   /* there can be whitespace between the '#' and 'version' */
290   do {
291     i++;
292     if (str[i] == '\0' || str[i] == '\n' || str[i] == '\r')
293       return NULL;
294   } while (g_ascii_isspace (str[i]));
295   if (g_strstr_len (&str[i], 7, "version"))
296     return &str[i + 7];
297 
298   return NULL;
299 }
300 
301 /**
302  * gst_glsl_version_profile_from_string:
303  * @string: a valid GLSL `#version` string
304  * @version_ret: (out): resulting #GstGLSLVersion
305  * @profile_ret: (out): resulting #GstGLSLVersion
306  *
307  * Note: this function expects either a `#version` GLSL preprocesser directive
308  * or a valid GLSL version and/or profile.
309  *
310  * Returns: TRUE if a valid `#version` string was found, FALSE otherwise
311  */
312 gboolean
gst_glsl_version_profile_from_string(const gchar * string,GstGLSLVersion * version_ret,GstGLSLProfile * profile_ret)313 gst_glsl_version_profile_from_string (const gchar * string,
314     GstGLSLVersion * version_ret, GstGLSLProfile * profile_ret)
315 {
316   gchar *str, *version_s, *profile_s;
317   GstGLSLVersion version = GST_GLSL_VERSION_NONE;
318   GstGLSLProfile profile = GST_GLSL_PROFILE_NONE;
319   gint i;
320 
321   _init_debug ();
322 
323   if (!string)
324     goto error;
325 
326   str = g_strdup (string);
327   version_s = g_strstrip (str);
328 
329   /* skip possible #version prefix */
330   if (str[0] == '#') {
331     if (!(version_s =
332             (gchar *) _check_valid_version_preprocessor_string (version_s))) {
333       GST_WARNING ("Invalid preprocesser directive detected");
334       g_free (str);
335       goto error;
336     }
337   }
338 
339   version_s = g_strstrip (version_s);
340 
341   i = 0;
342   while (version_s && version_s[i] != '\0' && g_ascii_isdigit (version_s[i]))
343     i++;
344   /* wrong version length */
345   if (i != 3) {
346     GST_WARNING ("version number has the wrong number of digits: %s",
347         version_s);
348     g_free (str);
349     goto error;
350   }
351 
352   if (version_s[i] != 0) {
353     version_s[i] = '\0';
354     i++;
355     profile_s = &version_s[i];
356     profile_s = g_strstrip (profile_s);
357 
358     profile = gst_glsl_profile_from_string (profile_s);
359   }
360   version = gst_glsl_version_from_string (version_s);
361   g_free (str);
362 
363   /* check whether the parsed data is valid */
364   if (!version) {
365     GST_WARNING ("Could not map the version number to a valid GLSL version:");
366     goto error;
367   }
368   if (!_is_valid_version_profile (version, profile)) {
369     GST_WARNING ("Invalid version/profile combination specified: %s %s",
370         gst_glsl_version_to_string (version),
371         gst_glsl_profile_to_string (profile));
372     goto error;
373   }
374   /* got a profile when none was expected */
375   if (version <= GST_GLSL_VERSION_140 && profile != GST_GLSL_PROFILE_NONE) {
376     GST_WARNING
377         ("Found a profile (%s) with a version (%s) that does not support "
378         "profiles", gst_glsl_version_to_string (version),
379         gst_glsl_profile_to_string (profile));
380     goto error;
381   }
382 
383   _fixup_version_profile (&version, &profile);
384 
385   if (profile_ret)
386     *profile_ret = profile;
387   if (version_ret)
388     *version_ret = version;
389 
390   return TRUE;
391 
392 error:
393   {
394     if (profile_ret)
395       *profile_ret = GST_GLSL_PROFILE_NONE;
396     if (version_ret)
397       *version_ret = GST_GLSL_VERSION_NONE;
398     return FALSE;
399   }
400 }
401 
402 /* returns the pointer in @str to the #version declaration */
403 const gchar *
_gst_glsl_shader_string_find_version(const gchar * str)404 _gst_glsl_shader_string_find_version (const gchar * str)
405 {
406   gboolean sl_comment = FALSE;
407   gboolean ml_comment = FALSE;
408   gboolean newline = TRUE;
409   gint i = 0;
410 
411   _init_debug ();
412 
413   /* search for #version while allowing for preceding comments/whitespace as
414    * permitted by the GLSL specification */
415   while (str && str[i] != '\0' && i < 1024) {
416     if (str[i] == '\n' || str[i] == '\r') {
417       newline = TRUE;
418       sl_comment = FALSE;
419       i++;
420       continue;
421     }
422 
423     if (g_ascii_isspace (str[i]))
424       goto next;
425 
426     if (sl_comment)
427       goto next;
428 
429     if (ml_comment) {
430       if (g_strstr_len (&str[i], 2, "*/")) {
431         ml_comment = FALSE;
432         i++;
433       }
434       goto next;
435     }
436 
437     if (g_strstr_len (&str[i], 2, "//")) {
438       sl_comment = TRUE;
439       i++;
440       goto next;
441     }
442 
443     if (g_strstr_len (&str[i], 2, "/*")) {
444       ml_comment = TRUE;
445       i++;
446       goto next;
447     }
448 
449     if (str[i] == '#') {
450       if (newline && _check_valid_version_preprocessor_string (&str[i])) {
451         GST_DEBUG ("found #version declaration at index %i", i);
452         return &str[i];
453       }
454       break;
455     }
456 
457   next:
458     newline = FALSE;
459     i++;
460   }
461 
462   GST_DEBUG ("no #version declaration found in the first 1K");
463 
464   return NULL;
465 }
466 
467 /**
468  * gst_glsl_string_get_version_profile:
469  * @s: string to search for a valid `#version` string
470  * @version: (out): resulting #GstGLSLVersion
471  * @profile: (out): resulting #GstGLSLProfile
472  *
473  * Note: this function first searches the first 1 kilobytes for a `#version`
474  * preprocessor directive and then executes gst_glsl_version_profile_from_string().
475  *
476  * Returns: TRUE if a valid `#version` string was found, FALSE otherwise
477  */
478 gboolean
gst_glsl_string_get_version_profile(const gchar * s,GstGLSLVersion * version,GstGLSLProfile * profile)479 gst_glsl_string_get_version_profile (const gchar * s, GstGLSLVersion * version,
480     GstGLSLProfile * profile)
481 {
482   const gchar *version_profile_s;
483 
484   version_profile_s = _gst_glsl_shader_string_find_version (s);
485   if (!version_profile_s)
486     goto error;
487 
488   if (!gst_glsl_version_profile_from_string (version_profile_s, version,
489           profile))
490     goto error;
491 
492   return TRUE;
493 
494 error:
495   {
496     if (version)
497       *version = GST_GLSL_VERSION_NONE;
498     if (profile)
499       *profile = GST_GLSL_PROFILE_NONE;
500     return FALSE;
501   }
502 }
503 
504 /**
505  * gst_gl_version_to_glsl_version:
506  * @gl_api: the #GstGLAPI
507  * @maj: the major GL version
508  * @min: the minor GL version
509  *
510  * Returns: The minimum supported #GstGLSLVersion available for @gl_api, @maj and @min
511  */
512 GstGLSLVersion
gst_gl_version_to_glsl_version(GstGLAPI gl_api,gint maj,gint min)513 gst_gl_version_to_glsl_version (GstGLAPI gl_api, gint maj, gint min)
514 {
515   g_return_val_if_fail (gl_api != GST_GL_API_NONE, 0);
516 
517   _init_debug ();
518 
519   if (gl_api & GST_GL_API_GLES2) {
520     if (maj == 2 && min == 0)
521       return 100;
522 
523     if (maj == 3 && min >= 0 && min <= 2)
524       return maj * 100 + min * 10;
525 
526     GST_WARNING ("unknown GLES version");
527     return 0;
528   }
529 
530   /* versions match for >= 3.3 */
531   if (gl_api & (GST_GL_API_OPENGL3 | GST_GL_API_OPENGL)) {
532     if (maj > 3 || (maj == 3 && min >= 3))
533       return maj * 100 + min * 10;
534 
535     if (maj == 3 && min == 2)
536       return 150;
537     if (maj == 3 && min == 1)
538       return 140;
539     if (maj == 3 && min == 0)
540       return 130;
541     if (maj == 2 && min == 1)
542       return 120;
543     if (maj == 2 && min == 0)
544       return 110;
545 
546     GST_WARNING ("unknown GL version");
547     return 0;
548   }
549 
550   GST_WARNING ("unknown GL API");
551   return 0;
552 }
553 
554 /**
555  * gst_gl_context_supports_glsl_profile_version:
556  * @context: a #GstGLContext
557  * @version: a #GstGLSLVersion
558  * @profile: a #GstGLSLProfile
559  *
560  * Returns: Whether @context supports the combination of @version with @profile
561  */
562 gboolean
gst_gl_context_supports_glsl_profile_version(GstGLContext * context,GstGLSLVersion version,GstGLSLProfile profile)563 gst_gl_context_supports_glsl_profile_version (GstGLContext * context,
564     GstGLSLVersion version, GstGLSLProfile profile)
565 {
566   g_return_val_if_fail (GST_IS_GL_CONTEXT (context), FALSE);
567 
568   if (!_is_valid_version_profile (version, profile))
569     return FALSE;
570 
571   if (profile != GST_GLSL_PROFILE_NONE) {
572     if (gst_gl_context_check_gl_version (context, GST_GL_API_GLES2, 2, 0)) {
573       if ((profile & GST_GLSL_PROFILE_ES) == 0)
574         return FALSE;
575     } else if ((gst_gl_context_get_gl_api (context) & GST_GL_API_OPENGL) != 0) {
576       if ((profile & GST_GLSL_PROFILE_COMPATIBILITY) == 0)
577         return FALSE;
578     } else if ((gst_gl_context_get_gl_api (context) & GST_GL_API_OPENGL3) != 0) {
579       /* GL_ARB_es2_compatibility is required for GL3 contexts */
580       if ((profile & (GST_GLSL_PROFILE_CORE | GST_GLSL_PROFILE_ES)) == 0)
581         return FALSE;
582     } else {
583       g_assert_not_reached ();
584     }
585   }
586 
587   if (version != GST_GLSL_VERSION_NONE) {
588     GstGLAPI gl_api;
589     gint maj, min, glsl_version;
590 
591     if (gst_gl_context_check_gl_version (context, GST_GL_API_GLES2, 3, 1)) {
592       if (version > GST_GLSL_VERSION_310)
593         return FALSE;
594     } else if (gst_gl_context_check_gl_version (context, GST_GL_API_GLES2, 3,
595             0)) {
596       if (version > GST_GLSL_VERSION_300)
597         return FALSE;
598     } else if (gst_gl_context_check_gl_version (context, GST_GL_API_GLES2, 2,
599             0)) {
600       if (version > GST_GLSL_VERSION_100)
601         return FALSE;
602     }
603 
604     gl_api = gst_gl_context_get_gl_api (context);
605     gst_gl_context_get_gl_version (context, &maj, &min);
606     glsl_version = gst_gl_version_to_glsl_version (gl_api, maj, min);
607     if (version > glsl_version)
608       return FALSE;
609 
610     if (gst_gl_context_check_gl_version (context, GST_GL_API_OPENGL3, 1, 0))
611       /* GL_ARB_es2_compatibility is required for GL3 contexts */
612       if (version < GST_GLSL_VERSION_150 && version != GST_GLSL_VERSION_100)
613         return FALSE;
614 
615     if (gst_gl_context_check_gl_version (context, GST_GL_API_OPENGL, 1, 0)
616         && version < GST_GLSL_VERSION_110)
617       return FALSE;
618   }
619 
620   return TRUE;
621 }
622 
623 gboolean
_gst_glsl_funcs_fill(GstGLSLFuncs * vtable,GstGLContext * context)624 _gst_glsl_funcs_fill (GstGLSLFuncs * vtable, GstGLContext * context)
625 {
626   GstGLFuncs *gl = context->gl_vtable;
627 
628   if (vtable->initialized)
629     return TRUE;
630 
631   if (gl->CreateProgram) {
632     vtable->CreateProgram = gl->CreateProgram;
633     vtable->DeleteProgram = gl->DeleteProgram;
634     vtable->UseProgram = gl->UseProgram;
635 
636     vtable->CreateShader = gl->CreateShader;
637     vtable->DeleteShader = gl->DeleteShader;
638     vtable->AttachShader = gl->AttachShader;
639     vtable->DetachShader = gl->DetachShader;
640 
641     vtable->GetAttachedShaders = gl->GetAttachedShaders;
642 
643     vtable->GetShaderInfoLog = gl->GetShaderInfoLog;
644     vtable->GetShaderiv = gl->GetShaderiv;
645     vtable->GetProgramInfoLog = gl->GetProgramInfoLog;
646     vtable->GetProgramiv = gl->GetProgramiv;
647   } else if (gl->CreateProgramObject) {
648     vtable->CreateProgram = gl->CreateProgramObject;
649     vtable->DeleteProgram = gl->DeleteObject;
650     vtable->UseProgram = gl->UseProgramObject;
651 
652     vtable->CreateShader = gl->CreateShaderObject;
653     vtable->DeleteShader = gl->DeleteObject;
654     vtable->AttachShader = gl->AttachObject;
655     vtable->DetachShader = gl->DetachObject;
656 
657     vtable->GetAttachedShaders = gl->GetAttachedObjects;
658 
659     vtable->GetShaderInfoLog = gl->GetInfoLog;
660     vtable->GetShaderiv = gl->GetObjectParameteriv;
661     vtable->GetProgramInfoLog = gl->GetInfoLog;
662     vtable->GetProgramiv = gl->GetObjectParameteriv;
663   } else {
664     vtable->initialized = FALSE;
665     return FALSE;
666   }
667 
668   vtable->initialized = TRUE;
669   return TRUE;
670 }
671 
672 static gchar *
_mangle_external_image_extension(const gchar * str,GstGLContext * context,GstGLTextureTarget from,GstGLTextureTarget to,GstGLSLVersion version,GstGLSLProfile profile)673 _mangle_external_image_extension (const gchar * str, GstGLContext * context,
674     GstGLTextureTarget from, GstGLTextureTarget to, GstGLSLVersion version,
675     GstGLSLProfile profile)
676 {
677   GST_DEBUG ("is oes? %d, profile == ES? %d, version >= 300? %d, "
678       "have essl3? %d", to == GST_GL_TEXTURE_TARGET_EXTERNAL_OES,
679       profile == GST_GLSL_PROFILE_ES, version >= GST_GLSL_VERSION_300,
680       gst_gl_context_check_feature (context,
681           "GL_OES_EGL_image_external_essl3"));
682 
683   /* replace GL_OES_EGL_image_external with GL_OES_EGL_image_external_essl3 where supported */
684   if (to == GST_GL_TEXTURE_TARGET_EXTERNAL_OES && profile == GST_GLSL_PROFILE_ES
685       && version >= GST_GLSL_VERSION_300) {
686     if (gst_gl_context_check_feature (context,
687             "GL_OES_EGL_image_external_essl3")) {
688       GRegex *regex = g_regex_new (
689           /* '#extension ' with optional spacing */
690           "(#[ \\t]*extension[ \\t]+)"
691           /* what we're looking to replace */
692           "GL_OES_EGL_image_external"
693           /* ':' with optional spacing */
694           "([ \\t]*:[ \\t]*"
695           /* some word like require, disable, etc followed by spacing and a newline */
696           "\\S+[ \\t]*\\R)",
697           0, 0, NULL);
698       gchar *tmp = g_regex_replace (regex, str, -1, 0,
699           "\\1GL_OES_EGL_image_external_essl3\\2", 0, NULL);
700       g_regex_unref (regex);
701       return tmp;
702     } else {
703       GST_FIXME ("Undefined situation detected. GLES3 supported but "
704           "GL_OES_EGL_image_external_essl3 not supported.  Falling back to the "
705           "older GL_OES_EGL_image_external extension");
706       return g_strdup (str);
707     }
708   } else {
709     return g_strdup (str);
710   }
711 }
712 
713 static gchar *
_mangle_texture_access(const gchar * str,GstGLContext * context,GstGLTextureTarget from,GstGLTextureTarget to,GstGLSLVersion version,GstGLSLProfile profile)714 _mangle_texture_access (const gchar * str, GstGLContext * context,
715     GstGLTextureTarget from, GstGLTextureTarget to, GstGLSLVersion version,
716     GstGLSLProfile profile)
717 {
718   const gchar *from_str = NULL, *to_str = NULL;
719   gchar *ret, *tmp;
720   gchar *regex_find;
721   GRegex *regex;
722 
723   if (from == GST_GL_TEXTURE_TARGET_2D)
724     from_str = "texture2D";
725   if (from == GST_GL_TEXTURE_TARGET_RECTANGLE)
726     from_str = "texture2DRect";
727   if (from == GST_GL_TEXTURE_TARGET_EXTERNAL_OES)
728     from_str = "texture2D";
729 
730   /* GL3 || gles3 but not when external-oes unless the image_external_essl3 extension is supported */
731   if (profile == GST_GLSL_PROFILE_CORE || (profile == GST_GLSL_PROFILE_ES
732           && version >= GST_GLSL_VERSION_300
733           && (to != GST_GL_TEXTURE_TARGET_EXTERNAL_OES
734               || gst_gl_context_check_feature (context,
735                   "GL_OES_EGL_image_external_essl3")))) {
736     to_str = "texture";
737   } else {
738     if (to == GST_GL_TEXTURE_TARGET_2D)
739       to_str = "texture2D";
740     if (to == GST_GL_TEXTURE_TARGET_RECTANGLE)
741       to_str = "texture2DRect";
742     if (to == GST_GL_TEXTURE_TARGET_EXTERNAL_OES)
743       to_str = "texture2D";
744   }
745 
746   /* followed by any amount of whitespace then a bracket */
747   regex_find = g_strdup_printf ("%s(?=\\s*\\()", from_str);
748   regex = g_regex_new (regex_find, 0, 0, NULL);
749   tmp = g_regex_replace_literal (regex, str, -1, 0, to_str, 0, NULL);
750   g_free (regex_find);
751   g_regex_unref (regex);
752 
753   if (tmp) {
754     ret = tmp;
755   } else {
756     GST_FIXME ("Couldn't mangle texture access successfully from %s to %s",
757         from_str, to_str);
758     ret = g_strdup (str);
759   }
760 
761   return ret;
762 }
763 
764 static gchar *
_mangle_sampler_type(const gchar * str,GstGLTextureTarget from,GstGLTextureTarget to)765 _mangle_sampler_type (const gchar * str, GstGLTextureTarget from,
766     GstGLTextureTarget to)
767 {
768   const gchar *from_str = NULL, *to_str = NULL;
769   gchar *ret, *tmp;
770   gchar *regex_find;
771   GRegex *regex;
772 
773   if (from == GST_GL_TEXTURE_TARGET_2D)
774     from_str = "sampler2D";
775   if (from == GST_GL_TEXTURE_TARGET_RECTANGLE)
776     from_str = "sampler2DRect";
777   if (from == GST_GL_TEXTURE_TARGET_EXTERNAL_OES)
778     from_str = "samplerExternalOES";
779 
780   if (to == GST_GL_TEXTURE_TARGET_2D)
781     to_str = "sampler2D";
782   if (to == GST_GL_TEXTURE_TARGET_RECTANGLE)
783     to_str = "sampler2DRect";
784   if (to == GST_GL_TEXTURE_TARGET_EXTERNAL_OES)
785     to_str = "samplerExternalOES";
786 
787   /* followed by some whitespace  */
788   regex_find = g_strdup_printf ("%s(?=\\s)", from_str);
789   regex = g_regex_new (regex_find, 0, 0, NULL);
790   tmp = g_regex_replace_literal (regex, str, -1, 0, to_str, 0, NULL);
791   g_free (regex_find);
792   g_regex_unref (regex);
793 
794   if (tmp) {
795     ret = tmp;
796   } else {
797     GST_FIXME ("Couldn't mangle sampler type successfully from %s to %s",
798         from_str, to_str);
799     ret = g_strdup (str);
800   }
801 
802   return ret;
803 }
804 
805 static gchar *
_mangle_varying_attribute(const gchar * str,guint shader_type,GstGLSLVersion version,GstGLSLProfile profile)806 _mangle_varying_attribute (const gchar * str, guint shader_type,
807     GstGLSLVersion version, GstGLSLProfile profile)
808 {
809   if (shader_type == GL_VERTEX_SHADER) {
810     if (profile == GST_GLSL_PROFILE_CORE || (profile == GST_GLSL_PROFILE_ES
811             && version >= GST_GLSL_VERSION_300)) {
812       gchar *tmp, *tmp2;
813       GRegex *regex;
814 
815       /* followed by some whitespace  */
816       regex = g_regex_new ("varying(?=\\s)", 0, 0, NULL);
817       tmp = g_regex_replace_literal (regex, str, -1, 0, "out", 0, NULL);
818       g_regex_unref (regex);
819 
820       /* followed by some whitespace  */
821       regex = g_regex_new ("attribute(?=\\s)", 0, 0, NULL);
822       tmp2 = g_regex_replace_literal (regex, tmp, -1, 0, "in", 0, NULL);
823       g_regex_unref (regex);
824 
825       g_free (tmp);
826       return tmp2;
827     }
828   } else if (shader_type == GL_FRAGMENT_SHADER) {
829     if (profile == GST_GLSL_PROFILE_CORE || (profile == GST_GLSL_PROFILE_ES
830             && version >= GST_GLSL_VERSION_300)) {
831       gchar *tmp;
832       GRegex *regex;
833 
834       /* followed by some whitespace  */
835       regex = g_regex_new ("varying(?=\\s)", 0, 0, NULL);
836       tmp = g_regex_replace_literal (regex, str, -1, 0, "in", 0, NULL);
837       g_regex_unref (regex);
838 
839       return tmp;
840     }
841   }
842   return g_strdup (str);
843 }
844 
845 static gchar *
_mangle_frag_color_data(const gchar * str)846 _mangle_frag_color_data (const gchar * str)
847 {
848   GRegex *regex;
849   gchar *ret, *tmp;
850 
851   regex = g_regex_new ("gl_FragColor", 0, 0, NULL);
852   ret = g_regex_replace_literal (regex, str, -1, 0, "fragColor", 0, NULL);
853   g_regex_unref (regex);
854 
855   tmp = ret;
856   /* search and replace 'gl_FragData[NUM]' into fragColor_NUM */
857   regex = g_regex_new ("gl_FragData\\[(\\d+)\\]", 0, 0, NULL);
858   ret = g_regex_replace (regex, tmp, -1, 0, "fragColor_\\1", 0, NULL);
859   g_regex_unref (regex);
860   g_free (tmp);
861 
862   return ret;
863 }
864 
865 static void
_mangle_version_profile_from_gl_api(GstGLContext * context,GstGLTextureTarget from,GstGLTextureTarget to,GstGLSLVersion * version,GstGLSLProfile * profile)866 _mangle_version_profile_from_gl_api (GstGLContext * context,
867     GstGLTextureTarget from, GstGLTextureTarget to, GstGLSLVersion * version,
868     GstGLSLProfile * profile)
869 {
870   GstGLAPI gl_api;
871   gint gl_major, gl_minor;
872 
873   gl_api = gst_gl_context_get_gl_api (context);
874   gst_gl_context_get_gl_version (context, &gl_major, &gl_minor);
875 
876   *version = GST_GLSL_VERSION_NONE;
877   *profile = GST_GLSL_PROFILE_NONE;
878 
879   if (gl_api & GST_GL_API_OPENGL3) {
880     if (gl_major > 3 || gl_minor >= 3) {
881       *version = GST_GLSL_VERSION_330;
882       *profile = GST_GLSL_PROFILE_CORE;
883     } else {
884       *version = GST_GLSL_VERSION_150;
885       *profile = GST_GLSL_PROFILE_NONE;
886     }
887   } else if (gl_api & GST_GL_API_GLES2) {
888     /* We don't know which texture function to use if we have GLES3 and
889      * don't have the essl3 extension */
890     if (gl_major >= 3 && (to != GST_GL_TEXTURE_TARGET_EXTERNAL_OES
891             || gst_gl_context_check_feature (context,
892                 "GL_OES_EGL_image_external_essl3"))) {
893       *version = GST_GLSL_VERSION_300;
894       *profile = GST_GLSL_PROFILE_ES;
895     } else if (gl_major >= 2) {
896       *version = GST_GLSL_VERSION_100;
897       *profile = GST_GLSL_PROFILE_ES;
898     }
899   } else if (gl_api & GST_GL_API_OPENGL) {
900     *version = GST_GLSL_VERSION_110;
901     *profile = GST_GLSL_PROFILE_COMPATIBILITY;
902   }
903 }
904 
905 gchar *
_gst_glsl_mangle_shader(const gchar * str,guint shader_type,GstGLTextureTarget from,GstGLTextureTarget to,GstGLContext * context,GstGLSLVersion * version,GstGLSLProfile * profile)906 _gst_glsl_mangle_shader (const gchar * str, guint shader_type,
907     GstGLTextureTarget from, GstGLTextureTarget to, GstGLContext * context,
908     GstGLSLVersion * version, GstGLSLProfile * profile)
909 {
910   gchar *tmp, *tmp2;
911 
912   _init_debug ();
913 
914   g_return_val_if_fail (GST_IS_GL_CONTEXT (context), NULL);
915 
916   _mangle_version_profile_from_gl_api (context, from, to, version, profile);
917   tmp2 =
918       _mangle_external_image_extension (str, context, from, to, *version,
919       *profile);
920   tmp = _mangle_texture_access (tmp2, context, from, to, *version, *profile);
921   g_free (tmp2);
922   tmp2 = _mangle_sampler_type (tmp, from, to);
923   g_free (tmp);
924   tmp = _mangle_varying_attribute (tmp2, shader_type, *version, *profile);
925   g_free (tmp2);
926   if (shader_type == GL_FRAGMENT_SHADER) {
927     if ((*profile == GST_GLSL_PROFILE_ES && *version >= GST_GLSL_VERSION_300)
928         || (*profile == GST_GLSL_PROFILE_CORE
929             && *version >= GST_GLSL_VERSION_150)) {
930       tmp2 = _mangle_frag_color_data (tmp);
931       g_free (tmp);
932       tmp = tmp2;
933     }
934   }
935   return tmp;
936 }
937 
938 /**
939  * gst_gl_context_supports_precision:
940  * @context: a #GstGLContext
941  * @version: a #GstGLSLVersion
942  * @profile: a #GstGLSLProfile
943  *
944  * Returns: whether @context supports the 'precision' specifier in GLSL shaders
945  *
946  * Since: 1.16
947  */
948 gboolean
gst_gl_context_supports_precision(GstGLContext * context,GstGLSLVersion version,GstGLSLProfile profile)949 gst_gl_context_supports_precision (GstGLContext * context,
950     GstGLSLVersion version, GstGLSLProfile profile)
951 {
952   gboolean es2 = FALSE;
953 
954   g_return_val_if_fail (GST_IS_GL_CONTEXT (context), FALSE);
955 
956   if ((profile & GST_GLSL_PROFILE_ES) == 0)
957     return FALSE;
958 
959   es2 = gst_gl_context_check_gl_version (context, GST_GL_API_GLES2, 2, 0)
960       || gst_gl_context_check_feature (context, "GL_ARB_ES2_compatibility");
961 
962   return es2 && context->gl_vtable->GetShaderPrecisionFormat;
963 }
964 
965 /**
966  * gst_gl_context_supports_precision_highp:
967  * @context: a #GstGLContext
968  * @version: a #GstGLSLVersion
969  * @profile: a #GstGLSLProfile
970  *
971  * Returns: whether @context supports the 'precision highp' specifier in GLSL shaders
972  *
973  * Since: 1.16
974  */
975 gboolean
gst_gl_context_supports_precision_highp(GstGLContext * context,GstGLSLVersion version,GstGLSLProfile profile)976 gst_gl_context_supports_precision_highp (GstGLContext * context,
977     GstGLSLVersion version, GstGLSLProfile profile)
978 {
979   gint v_range[2] = { 0, }
980   , v_precision = 0;
981   gint f_range[2] = { 0, }
982   , f_precision = 0;
983 
984   g_return_val_if_fail (GST_IS_GL_CONTEXT (context), FALSE);
985 
986   if (!gst_gl_context_supports_precision (context, version, profile))
987     return FALSE;
988 
989   context->gl_vtable->GetShaderPrecisionFormat (GL_VERTEX_SHADER, GL_HIGH_FLOAT,
990       v_range, &v_precision);
991   context->gl_vtable->GetShaderPrecisionFormat (GL_FRAGMENT_SHADER,
992       GL_HIGH_FLOAT, f_range, &f_precision);
993 
994   return v_range[0] != 0 && v_range[1] != 0 && v_precision != 0 &&
995       f_range[0] != 0 && f_range[1] != 0 && f_precision != 0;
996 }
997