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