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