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 "gstglslstage.h"
26
27 #include "gl.h"
28 #include "gstglfuncs.h"
29 #include "gstglsl_private.h"
30
31 #ifndef GL_GEOMETRY_SHADER
32 #define GL_GEOMETRY_SHADER 0x8DD9
33 #endif
34 #ifndef GL_COMPUTE_SHADER
35 #define GL_COMPUTE_SHADER 0x91B9
36 #endif
37 #ifndef GL_TESS_CONTROL_SHADER
38 #define GL_TESS_CONTROL_SHADER 0x8E88
39 #endif
40 #ifndef GL_TESS_EVALUATION_SHADER
41 #define GL_TESS_EVALUATION_SHADER 0x8E87
42 #endif
43
44 /**
45 * SECTION:gstglslstage
46 * @short_description: object for dealing with OpenGL shader stages
47 * @title: GstGLSLStage
48 * @see_also: #GstGLShader
49 *
50 * #GstGLSLStage holds and represents a single OpenGL shader stage.
51 */
52
53 static const gchar *es2_version_header = "#version 100\n";
54
55 GST_DEBUG_CATEGORY_STATIC (gst_glsl_stage_debug);
56 #define GST_CAT_DEFAULT gst_glsl_stage_debug
57
58 struct _GstGLSLStagePrivate
59 {
60 GstGLSLFuncs vtable;
61
62 GLenum type;
63 GLhandleARB handle;
64 GstGLSLVersion version;
65 GstGLSLProfile profile;
66 gchar **strings;
67 gint n_strings;
68
69 gboolean compiled;
70 };
71
72 G_DEFINE_TYPE_WITH_CODE (GstGLSLStage, gst_glsl_stage, GST_TYPE_OBJECT,
73 G_ADD_PRIVATE (GstGLSLStage)
74 GST_DEBUG_CATEGORY_INIT (gst_glsl_stage_debug, "glslstage", 0,
75 "GLSL Stage"););
76
77 static void
gst_glsl_stage_finalize(GObject * object)78 gst_glsl_stage_finalize (GObject * object)
79 {
80 GstGLSLStage *stage = GST_GLSL_STAGE (object);
81 gint i;
82
83 if (stage->context) {
84 gst_object_unref (stage->context);
85 stage->context = NULL;
86 }
87
88 for (i = 0; i < stage->priv->n_strings; i++) {
89 g_free (stage->priv->strings[i]);
90 }
91 g_free (stage->priv->strings);
92 stage->priv->strings = NULL;
93
94 G_OBJECT_CLASS (gst_glsl_stage_parent_class)->finalize (object);
95 }
96
97 static void
gst_glsl_stage_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)98 gst_glsl_stage_set_property (GObject * object,
99 guint prop_id, const GValue * value, GParamSpec * pspec)
100 {
101 switch (prop_id) {
102 default:
103 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
104 break;
105 }
106 }
107
108 static void
gst_glsl_stage_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)109 gst_glsl_stage_get_property (GObject * object,
110 guint prop_id, GValue * value, GParamSpec * pspec)
111 {
112 switch (prop_id) {
113 default:
114 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
115 break;
116 }
117
118 }
119
120 static void
gst_glsl_stage_class_init(GstGLSLStageClass * klass)121 gst_glsl_stage_class_init (GstGLSLStageClass * klass)
122 {
123 GObjectClass *obj_class = G_OBJECT_CLASS (klass);
124
125 obj_class->finalize = gst_glsl_stage_finalize;
126 obj_class->set_property = gst_glsl_stage_set_property;
127 obj_class->get_property = gst_glsl_stage_get_property;
128 }
129
130 static void
gst_glsl_stage_init(GstGLSLStage * stage)131 gst_glsl_stage_init (GstGLSLStage * stage)
132 {
133 stage->priv = gst_glsl_stage_get_instance_private (stage);
134 }
135
136 static gboolean
_is_valid_shader_type(GLenum type)137 _is_valid_shader_type (GLenum type)
138 {
139 switch (type) {
140 case GL_VERTEX_SHADER:
141 case GL_FRAGMENT_SHADER:
142 case GL_TESS_CONTROL_SHADER:
143 case GL_TESS_EVALUATION_SHADER:
144 case GL_GEOMETRY_SHADER:
145 case GL_COMPUTE_SHADER:
146 return TRUE;
147 default:
148 return FALSE;
149 }
150 }
151
152 static const gchar *
_shader_type_to_string(GLenum type)153 _shader_type_to_string (GLenum type)
154 {
155 switch (type) {
156 case GL_VERTEX_SHADER:
157 return "vertex";
158 case GL_FRAGMENT_SHADER:
159 return "fragment";
160 case GL_TESS_CONTROL_SHADER:
161 return "tesselation control";
162 case GL_TESS_EVALUATION_SHADER:
163 return "tesselation evaluation";
164 case GL_GEOMETRY_SHADER:
165 return "geometry";
166 case GL_COMPUTE_SHADER:
167 return "compute";
168 default:
169 return "unknown";
170 }
171 }
172
173 static gboolean
_ensure_shader(GstGLSLStage * stage)174 _ensure_shader (GstGLSLStage * stage)
175 {
176 if (stage->priv->handle)
177 return TRUE;
178
179 if (!(stage->priv->handle =
180 stage->priv->vtable.CreateShader (stage->priv->type)))
181 return FALSE;
182
183 return stage->priv->handle != 0;
184 }
185
186 /**
187 * gst_glsl_stage_new_with_strings:
188 * @context: a #GstGLContext
189 * @type: the GL enum shader stage type
190 * @version: the #GstGLSLVersion
191 * @profile: the #GstGLSLProfile
192 * @n_strings: the number of strings in @str
193 * @str: (array length=n_strings):
194 * an array of strings concatted together to produce a shader
195 *
196 * Returns: (transfer floating): a new #GstGLSLStage of the specified @type
197 *
198 * Since: 1.8
199 */
200 GstGLSLStage *
gst_glsl_stage_new_with_strings(GstGLContext * context,guint type,GstGLSLVersion version,GstGLSLProfile profile,gint n_strings,const gchar ** str)201 gst_glsl_stage_new_with_strings (GstGLContext * context, guint type,
202 GstGLSLVersion version, GstGLSLProfile profile, gint n_strings,
203 const gchar ** str)
204 {
205 GstGLSLStage *stage;
206
207 g_return_val_if_fail (GST_IS_GL_CONTEXT (context), NULL);
208 g_return_val_if_fail (_is_valid_shader_type (type), NULL);
209
210 stage = g_object_new (GST_TYPE_GLSL_STAGE, NULL);
211 /* FIXME: GInittable */
212 if (!_gst_glsl_funcs_fill (&stage->priv->vtable, context)) {
213 gst_object_unref (stage);
214 return NULL;
215 }
216
217 stage->context = gst_object_ref (context);
218 stage->priv->type = type;
219 if (!gst_glsl_stage_set_strings (stage, version, profile, n_strings, str)) {
220 gst_object_unref (stage);
221 return NULL;
222 }
223
224 return stage;
225 }
226
227 /**
228 * gst_glsl_stage_new_with_string:
229 * @context: a #GstGLContext
230 * @type: the GL enum shader stage type
231 * @version: the #GstGLSLVersion
232 * @profile: the #GstGLSLProfile
233 * @str: a shader string
234 *
235 * Returns: (transfer floating): a new #GstGLSLStage of the specified @type
236 *
237 * Since: 1.8
238 */
239 GstGLSLStage *
gst_glsl_stage_new_with_string(GstGLContext * context,guint type,GstGLSLVersion version,GstGLSLProfile profile,const gchar * str)240 gst_glsl_stage_new_with_string (GstGLContext * context, guint type,
241 GstGLSLVersion version, GstGLSLProfile profile, const gchar * str)
242 {
243 return gst_glsl_stage_new_with_strings (context, type, version, profile, 1,
244 &str);
245 }
246
247 /**
248 * gst_glsl_stage_new:
249 * @context: a #GstGLContext
250 * @type: the GL enum shader stage type
251 *
252 * Returns: (transfer floating): a new #GstGLSLStage of the specified @type
253 *
254 * Since: 1.8
255 */
256 GstGLSLStage *
gst_glsl_stage_new(GstGLContext * context,guint type)257 gst_glsl_stage_new (GstGLContext * context, guint type)
258 {
259 return gst_glsl_stage_new_with_string (context, type, GST_GLSL_VERSION_NONE,
260 GST_GLSL_PROFILE_NONE, NULL);
261 }
262
263 /**
264 * gst_glsl_stage_new_with_default_vertex:
265 * @context: a #GstGLContext
266 *
267 * Returns: (transfer floating): a new #GstGLSLStage with the default vertex shader
268 *
269 * Since: 1.8
270 */
271 GstGLSLStage *
gst_glsl_stage_new_default_vertex(GstGLContext * context)272 gst_glsl_stage_new_default_vertex (GstGLContext * context)
273 {
274 return gst_glsl_stage_new_with_string (context, GL_VERTEX_SHADER,
275 GST_GLSL_VERSION_NONE,
276 GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY,
277 gst_gl_shader_string_vertex_default);
278 }
279
280 /**
281 * gst_glsl_stage_new_with_default_fragment:
282 * @context: a #GstGLContext
283 *
284 * Returns: (transfer floating): a new #GstGLSLStage with the default fragment shader
285 *
286 * Since: 1.8
287 */
288 GstGLSLStage *
gst_glsl_stage_new_default_fragment(GstGLContext * context)289 gst_glsl_stage_new_default_fragment (GstGLContext * context)
290 {
291 GstGLSLProfile profile = GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY;
292 GstGLSLVersion version = GST_GLSL_VERSION_NONE;
293 gchar *frag_str;
294 GstGLSLStage *stage;
295
296 frag_str =
297 gst_gl_shader_string_fragment_get_default (context, version, profile);
298
299 stage = gst_glsl_stage_new_with_string (context, GL_FRAGMENT_SHADER,
300 version, profile, frag_str);
301
302 g_free (frag_str);
303
304 return stage;
305 }
306
307 /**
308 * gst_glsl_stage_set_strings:
309 * @stage: a #GstGLSLStage
310 * @version: a #GstGLSLVersion
311 * @profile: a #GstGLSLProfile
312 * @n_strings: number of strings in @str
313 * @str: (array length=n_strings) (transfer none): a GLSL shader string
314 *
315 * Replaces the current shader string with @str.
316 *
317 * Since: 1.8
318 */
319 gboolean
gst_glsl_stage_set_strings(GstGLSLStage * stage,GstGLSLVersion version,GstGLSLProfile profile,gint n_strings,const gchar ** str)320 gst_glsl_stage_set_strings (GstGLSLStage * stage, GstGLSLVersion version,
321 GstGLSLProfile profile, gint n_strings, const gchar ** str)
322 {
323 gint i;
324
325 g_return_val_if_fail (GST_IS_GLSL_STAGE (stage), FALSE);
326 g_return_val_if_fail (n_strings > 0, FALSE);
327 g_return_val_if_fail (str != NULL, FALSE);
328
329 if (!gst_gl_context_supports_glsl_profile_version (stage->context, version,
330 profile)) {
331 const gchar *version_str = gst_glsl_version_to_string (version);
332 const gchar *profile_str = gst_glsl_profile_to_string (profile);
333 GST_ERROR_OBJECT (stage, "GL context does not support version %s and "
334 "profile %s", version_str, profile_str);
335 return FALSE;
336 }
337
338 stage->priv->version = version;
339 stage->priv->profile = profile;
340
341 for (i = 0; i < stage->priv->n_strings; i++) {
342 g_free (stage->priv->strings[i]);
343 }
344
345 if (stage->priv->n_strings < n_strings) {
346 /* only realloc if we need more space */
347 g_free (stage->priv->strings);
348 stage->priv->strings = g_new0 (gchar *, n_strings);
349 }
350
351 for (i = 0; i < n_strings; i++)
352 stage->priv->strings[i] = g_strdup (str[i]);
353 stage->priv->n_strings = n_strings;
354
355 return TRUE;
356 }
357
358 /**
359 * gst_glsl_stage_get_shader_type:
360 * @stage: a #GstGLSLStage
361 *
362 * Returns: The GL shader type for this shader stage
363 *
364 * Since: 1.8
365 */
366 guint
gst_glsl_stage_get_shader_type(GstGLSLStage * stage)367 gst_glsl_stage_get_shader_type (GstGLSLStage * stage)
368 {
369 g_return_val_if_fail (GST_IS_GLSL_STAGE (stage), 0);
370
371 return stage->priv->type;
372 }
373
374 /**
375 * gst_glsl_stage_get_handle:
376 * @stage: a #GstGLSLStage
377 *
378 * Returns: The GL handle for this shader stage
379 *
380 * Since: 1.8
381 */
382 guint
gst_glsl_stage_get_handle(GstGLSLStage * stage)383 gst_glsl_stage_get_handle (GstGLSLStage * stage)
384 {
385 g_return_val_if_fail (GST_IS_GLSL_STAGE (stage), 0);
386 g_return_val_if_fail (stage->priv->compiled, 0);
387
388 return stage->priv->handle;
389 }
390
391 /**
392 * gst_glsl_stage_get_version:
393 * @stage: a #GstGLSLStage
394 *
395 * Returns: The GLSL version for the current shader stage
396 *
397 * Since: 1.8
398 */
399 GstGLSLVersion
gst_glsl_stage_get_version(GstGLSLStage * stage)400 gst_glsl_stage_get_version (GstGLSLStage * stage)
401 {
402 g_return_val_if_fail (GST_IS_GLSL_STAGE (stage), 0);
403
404 return stage->priv->version;
405 }
406
407 /**
408 * gst_glsl_stage_get_profile:
409 * @stage: a #GstGLSLStage
410 *
411 * Returns: The GLSL profile for the current shader stage
412 *
413 * Since: 1.8
414 */
415 GstGLSLProfile
gst_glsl_stage_get_profile(GstGLSLStage * stage)416 gst_glsl_stage_get_profile (GstGLSLStage * stage)
417 {
418 g_return_val_if_fail (GST_IS_GLSL_STAGE (stage), 0);
419
420 return stage->priv->profile;
421 }
422
423 static void
_maybe_prepend_version(GstGLSLStage * stage,gchar ** shader_str,gint * n_vertex_sources,const gchar *** vertex_sources)424 _maybe_prepend_version (GstGLSLStage * stage, gchar ** shader_str,
425 gint * n_vertex_sources, const gchar *** vertex_sources)
426 {
427 gint n = *n_vertex_sources;
428 gboolean add_header = FALSE;
429 gint i, j;
430
431 /* FIXME: this all an educated guess */
432 if (gst_gl_context_check_gl_version (stage->context, GST_GL_API_OPENGL3, 3, 0)
433 && (stage->priv->profile & GST_GLSL_PROFILE_ES) != 0
434 && !_gst_glsl_shader_string_find_version (shader_str[0])) {
435 add_header = TRUE;
436 n++;
437 }
438
439 *vertex_sources = g_malloc0 (n * sizeof (gchar *));
440
441 i = 0;
442 if (add_header)
443 (*vertex_sources)[i++] = es2_version_header;
444
445 for (j = 0; j < stage->priv->n_strings; i++, j++)
446 (*vertex_sources)[i] = shader_str[j];
447 *n_vertex_sources = n;
448 }
449
450 struct compile
451 {
452 GstGLSLStage *stage;
453 GError **error;
454 gboolean result;
455 };
456
457 static void
_compile_shader(GstGLContext * context,struct compile * data)458 _compile_shader (GstGLContext * context, struct compile *data)
459 {
460 GstGLSLStagePrivate *priv = data->stage->priv;
461 GstGLSLFuncs *vtable = &data->stage->priv->vtable;
462 const GstGLFuncs *gl = context->gl_vtable;
463 const gchar **vertex_sources;
464 gchar info_buffer[2048];
465 gint n_vertex_sources;
466 GLint status;
467 gint len;
468 gint i;
469
470 if (data->stage->priv->compiled) {
471 data->result = TRUE;
472 return;
473 }
474
475 if (!_ensure_shader (data->stage)) {
476 g_set_error (data->error, GST_GLSL_ERROR, GST_GLSL_ERROR_COMPILE,
477 "Failed to create shader object");
478 data->result = FALSE;
479 return;
480 }
481
482 n_vertex_sources = data->stage->priv->n_strings;
483 _maybe_prepend_version (data->stage, priv->strings, &n_vertex_sources,
484 &vertex_sources);
485
486 GST_TRACE_OBJECT (data->stage, "compiling shader:");
487 for (i = 0; i < n_vertex_sources; i++) {
488 GST_TRACE_OBJECT (data->stage, "%s", vertex_sources[i]);
489 }
490
491 gl->ShaderSource (priv->handle, n_vertex_sources,
492 (const gchar **) vertex_sources, NULL);
493 gl->CompileShader (priv->handle);
494 g_free (vertex_sources);
495 /* FIXME: supported threaded GLSL compilers and don't destroy compilation
496 * performance by getting the compilation result directly after compilation */
497 status = GL_FALSE;
498 vtable->GetShaderiv (priv->handle, GL_COMPILE_STATUS, &status);
499
500 len = 0;
501 vtable->GetShaderInfoLog (priv->handle, sizeof (info_buffer) - 1, &len,
502 info_buffer);
503 info_buffer[len] = '\0';
504
505 if (status != GL_TRUE) {
506 GST_ERROR_OBJECT (data->stage, "%s shader compilation failed:%s",
507 _shader_type_to_string (priv->type), info_buffer);
508
509 g_set_error (data->error, GST_GLSL_ERROR, GST_GLSL_ERROR_COMPILE,
510 "%s shader compilation failed:%s",
511 _shader_type_to_string (priv->type), info_buffer);
512
513 vtable->DeleteShader (priv->handle);
514 data->result = FALSE;
515 return;
516 } else if (len > 1) {
517 GST_FIXME_OBJECT (data->stage, "%s shader info log:%s",
518 _shader_type_to_string (priv->type), info_buffer);
519 }
520
521 data->result = TRUE;
522 }
523
524 /**
525 * gst_glsl_stage_compile:
526 * @stage: a #GstGLSLStage
527 * @error: a #GError to use on failure
528 *
529 * Returns: whether the compilation suceeded
530 *
531 * Since: 1.8
532 */
533 gboolean
gst_glsl_stage_compile(GstGLSLStage * stage,GError ** error)534 gst_glsl_stage_compile (GstGLSLStage * stage, GError ** error)
535 {
536 struct compile data;
537
538 g_return_val_if_fail (GST_IS_GLSL_STAGE (stage), FALSE);
539
540 if (!stage->priv->strings) {
541 g_set_error (error, GST_GLSL_ERROR, GST_GLSL_ERROR_COMPILE,
542 "No shader source to compile");
543 return FALSE;
544 }
545
546 data.stage = stage;
547 data.error = error;
548
549 gst_gl_context_thread_add (stage->context,
550 (GstGLContextThreadFunc) _compile_shader, &data);
551
552 stage->priv->compiled = TRUE;
553
554 return data.result;
555 }
556