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