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 "gstgldebug.h"
26 
27 #include <glib/gprintf.h>
28 #include <string.h>
29 
30 #include "gstglcontext.h"
31 #include "gstglcontext_private.h"
32 #include "gstglfuncs.h"
33 
34 /**
35  * SECTION:gstgldebug
36  * @short_description: helper routines for dealing with OpenGL debugging
37  * @title: OpenGL debugging
38  * @see_also: #GstGLContext
39  */
40 
41 #define ASYNC_DEBUG_FILLED (1 << 0)
42 #define ASYNC_DEBUG_FROZEN (1 << 1)
43 
44 /* compatibility defines */
45 #ifndef GL_DEBUG_TYPE_ERROR
46 #define GL_DEBUG_TYPE_ERROR 0x824C
47 #endif
48 #ifndef GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR
49 #define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D
50 #endif
51 #ifndef GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR
52 #define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E
53 #endif
54 #ifndef GL_DEBUG_TYPE_PORTABILITY
55 #define GL_DEBUG_TYPE_PORTABILITY 0x824F
56 #endif
57 #ifndef GL_DEBUG_TYPE_PERFORMANCE
58 #define GL_DEBUG_TYPE_PERFORMANCE 0x8250
59 #endif
60 #ifndef GL_DEBUG_TYPE_MARKER
61 #define GL_DEBUG_TYPE_MARKER 0x8268
62 #endif
63 #ifndef GL_DEBUG_TYPE_OTHER
64 #define GL_DEBUG_TYPE_OTHER 0x8251
65 #endif
66 
67 #ifndef GL_DEBUG_SEVERITY_HIGH
68 #define GL_DEBUG_SEVERITY_HIGH 0x9146
69 #endif
70 #ifndef GL_DEBUG_SEVERITY_MEDIUM
71 #define GL_DEBUG_SEVERITY_MEDIUM 0x9147
72 #endif
73 #ifndef GL_DEBUG_SEVERITY_LOW
74 #define GL_DEBUG_SEVERITY_LOW 0x9148
75 #endif
76 #ifndef GL_DEBUG_SEVERITY_NOTIFICATION
77 #define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B
78 #endif
79 
80 #ifndef GL_DEBUG_SOURCE_API
81 #define GL_DEBUG_SOURCE_API 0x8246
82 #endif
83 #ifndef GL_DEBUG_SOURCE_WINDOW_SYSTEM
84 #define GL_DEBUG_SOURCE_WINDOW_SYSTEM 0x8247
85 #endif
86 #ifndef GL_DEBUG_SOURCE_SHADER_COMPILER
87 #define GL_DEBUG_SOURCE_SHADER_COMPILER 0x8248
88 #endif
89 #ifndef GL_DEBUG_SOURCE_THIRD_PARTY
90 #define GL_DEBUG_SOURCE_THIRD_PARTY 0x8249
91 #endif
92 #ifndef GL_DEBUG_SOURCE_APPLICATION
93 #define GL_DEBUG_SOURCE_APPLICATION 0x824A
94 #endif
95 #ifndef GL_DEBUG_SOURCE_OTHER
96 #define GL_DEBUG_SOURCE_OTHER 0x824B
97 #endif
98 
99 GST_DEBUG_CATEGORY_STATIC (gst_performance);
100 #define GST_CAT_DEFAULT gst_gl_debug
101 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
102 GST_DEBUG_CATEGORY_STATIC (default_debug);
103 GST_DEBUG_CATEGORY_STATIC (gst_gl_marker_debug);
104 
105 static void
_init_debug(void)106 _init_debug (void)
107 {
108   static volatile gsize _init = 0;
109 
110   if (g_once_init_enter (&_init)) {
111     GST_DEBUG_CATEGORY_GET (gst_performance, "GST_PERFORMANCE");
112     GST_DEBUG_CATEGORY_GET (gst_gl_debug, "gldebug");
113     GST_DEBUG_CATEGORY_GET (default_debug, "default");
114     GST_DEBUG_CATEGORY_INIT (gst_gl_marker_debug, "gldebugmarker", 0,
115         "OpenGL Markers");
116     g_once_init_leave (&_init, 1);
117   }
118 }
119 
120 static void
_free_async_debug_data(GstGLAsyncDebug * ad)121 _free_async_debug_data (GstGLAsyncDebug * ad)
122 {
123   if (ad->debug_msg) {
124     g_free (ad->debug_msg);
125     ad->debug_msg = NULL;
126     if (ad->object)
127       g_object_unref (ad->object);
128     ad->object = NULL;
129     ad->state_flags &= ~ASYNC_DEBUG_FILLED;
130   }
131 }
132 
133 /**
134  * gst_gl_async_debug_init:
135  * @ad: a #GstGLAsyncDebug
136  *
137  * Initialize @ad.  Intended for use with #GstGLAsyncDebug's that are embedded
138  * in other structs.
139  *
140  * Since: 1.8
141  */
142 void
gst_gl_async_debug_init(GstGLAsyncDebug * ad)143 gst_gl_async_debug_init (GstGLAsyncDebug * ad)
144 {
145   _init_debug ();
146 
147   memset (ad, 0, sizeof (*ad));
148 }
149 
150 /**
151  * gst_gl_async_debug_unset:
152  * @ad: a #GstGLAsyncDebug
153  *
154  * Unset any dynamically allocated data.  Intended for use with
155  * #GstGLAsyncDebug's that are embedded in other structs.
156  */
157 void
gst_gl_async_debug_unset(GstGLAsyncDebug * ad)158 gst_gl_async_debug_unset (GstGLAsyncDebug * ad)
159 {
160   gst_gl_async_debug_output_log_msg (ad);
161 
162   _free_async_debug_data (ad);
163 
164   if (ad->notify)
165     ad->notify (ad->user_data);
166 }
167 
168 /**
169  * gst_gl_async_debug_new: (skip)
170  *
171  * Free with gst_gl_async_debug_free()
172  *
173  * Returns: a new #GstGLAsyncDebug
174  *
175  * Since: 1.8
176  */
177 GstGLAsyncDebug *
gst_gl_async_debug_new(void)178 gst_gl_async_debug_new (void)
179 {
180   return g_new0 (GstGLAsyncDebug, 1);
181 }
182 
183 /**
184  * gst_gl_async_debug_free:
185  * @ad: a #GstGLAsyncDebug
186  *
187  * Frees @ad
188  *
189  * Since: 1.8
190  */
191 void
gst_gl_async_debug_free(GstGLAsyncDebug * ad)192 gst_gl_async_debug_free (GstGLAsyncDebug * ad)
193 {
194   gst_gl_async_debug_unset (ad);
195   g_free (ad);
196 }
197 
198 /**
199  * gst_gl_async_debug_freeze:
200  * @ad: a #GstGLAsyncDebug
201  *
202  * freeze the debug output.  While frozen, any call to
203  * gst_gl_async_debug_output_log_msg() will not output any messages but
204  * subsequent calls to gst_gl_async_debug_store_log_msg() will overwrite previous
205  * messages.
206  *
207  * Since: 1.8
208  */
209 void
gst_gl_async_debug_freeze(GstGLAsyncDebug * ad)210 gst_gl_async_debug_freeze (GstGLAsyncDebug * ad)
211 {
212   ad->state_flags |= ASYNC_DEBUG_FROZEN;
213 }
214 
215 /**
216  * gst_gl_async_debug_thaw:
217  * @ad: a #GstGLAsyncDebug
218  *
219  * unfreeze the debug output.  See gst_gl_async_debug_freeze() for what freezing means
220  *
221  * Since: 1.8
222  */
223 void
gst_gl_async_debug_thaw(GstGLAsyncDebug * ad)224 gst_gl_async_debug_thaw (GstGLAsyncDebug * ad)
225 {
226   ad->state_flags &= ~ASYNC_DEBUG_FROZEN;
227 }
228 
229 #if !defined(GST_DISABLE_GST_DEBUG)
230 
231 static inline const gchar *
_debug_severity_to_string(GLenum severity)232 _debug_severity_to_string (GLenum severity)
233 {
234   switch (severity) {
235     case GL_DEBUG_SEVERITY_HIGH:
236       return "high";
237     case GL_DEBUG_SEVERITY_MEDIUM:
238       return "medium";
239     case GL_DEBUG_SEVERITY_LOW:
240       return "low";
241     case GL_DEBUG_SEVERITY_NOTIFICATION:
242       return "notification";
243     default:
244       return "invalid";
245   }
246 }
247 
248 static inline const gchar *
_debug_source_to_string(GLenum source)249 _debug_source_to_string (GLenum source)
250 {
251   switch (source) {
252     case GL_DEBUG_SOURCE_API:
253       return "API";
254     case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
255       return "winsys";
256     case GL_DEBUG_SOURCE_SHADER_COMPILER:
257       return "shader compiler";
258     case GL_DEBUG_SOURCE_THIRD_PARTY:
259       return "third party";
260     case GL_DEBUG_SOURCE_APPLICATION:
261       return "application";
262     case GL_DEBUG_SOURCE_OTHER:
263       return "other";
264     default:
265       return "invalid";
266   }
267 }
268 
269 static inline const gchar *
_debug_type_to_string(GLenum type)270 _debug_type_to_string (GLenum type)
271 {
272   switch (type) {
273     case GL_DEBUG_TYPE_ERROR:
274       return "error";
275     case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
276       return "deprecated";
277     case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
278       return "undefined";
279     case GL_DEBUG_TYPE_PORTABILITY:
280       return "portability";
281     case GL_DEBUG_TYPE_PERFORMANCE:
282       return "performance";
283     case GL_DEBUG_TYPE_MARKER:
284       return "debug marker";
285     case GL_DEBUG_TYPE_OTHER:
286       return "other";
287     default:
288       return "invalid";
289   }
290 }
291 
292 static void GSTGLAPI
_gst_gl_debug_callback(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const gchar * message,gpointer user_data)293 _gst_gl_debug_callback (GLenum source, GLenum type, GLuint id, GLenum severity,
294     GLsizei length, const gchar * message, gpointer user_data)
295 {
296   GstGLContext *context = user_data;
297   const gchar *severity_str = _debug_severity_to_string (severity);
298   const gchar *source_str = _debug_source_to_string (source);
299   const gchar *type_str = _debug_type_to_string (type);
300 
301   _init_debug ();
302 
303   switch (type) {
304     case GL_DEBUG_TYPE_ERROR:
305     case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
306       GST_ERROR_OBJECT (context, "%s: GL %s from %s id:%u, %s", severity_str,
307           type_str, source_str, id, message);
308       break;
309     case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
310     case GL_DEBUG_TYPE_PORTABILITY:
311       GST_FIXME_OBJECT (context, "%s: GL %s from %s id:%u, %s", severity_str,
312           type_str, source_str, id, message);
313       break;
314     case GL_DEBUG_TYPE_PERFORMANCE:
315       GST_CAT_DEBUG_OBJECT (gst_performance, context, "%s: GL %s from %s id:%u,"
316           " %s", severity_str, type_str, source_str, id, message);
317       break;
318     default:
319       GST_DEBUG_OBJECT (context, "%s: GL %s from %s id:%u, %s", severity_str,
320           type_str, source_str, id, message);
321       break;
322   }
323 }
324 
325 G_GNUC_INTERNAL void _gst_gl_debug_enable (GstGLContext * context);
326 
327 G_GNUC_INTERNAL void
_gst_gl_debug_enable(GstGLContext * context)328 _gst_gl_debug_enable (GstGLContext * context)
329 {
330   const GstGLFuncs *gl = context->gl_vtable;
331   GstDebugLevel level;
332   GLenum debug_types[8];
333   guint i, n = 0;
334 
335   _init_debug ();
336 
337   if (!gl->DebugMessageCallback) {
338     GST_CAT_INFO_OBJECT (gst_gl_context_debug, context,
339         "No debugging support available");
340     return;
341   }
342 
343   if (!_gst_gl_context_debug_is_enabled (context))
344     return;
345 
346   GST_CAT_INFO_OBJECT (gst_gl_context_debug, context,
347       "Enabling GL context debugging");
348 
349   level = gst_debug_category_get_threshold (gst_gl_debug);
350 
351   gl->DebugMessageCallback (_gst_gl_debug_callback, context);
352   if (level >= GST_LEVEL_DEBUG) {
353     /* enable them all */
354     gl->DebugMessageControl (GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, 0,
355         GL_TRUE);
356   } else {
357     if (level >= GST_LEVEL_FIXME) {
358       debug_types[n++] = GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR;
359       debug_types[n++] = GL_DEBUG_TYPE_PORTABILITY;
360     }
361     if (level >= GST_LEVEL_ERROR) {
362       debug_types[n++] = GL_DEBUG_TYPE_ERROR;
363       debug_types[n++] = GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR;
364     }
365     g_assert (n < G_N_ELEMENTS (debug_types));
366     for (i = 0; i < n; i++) {
367       gl->DebugMessageControl (GL_DONT_CARE, debug_types[i], GL_DONT_CARE,
368           0, 0, GL_TRUE);
369     }
370   }
371 }
372 
373 /**
374  * gst_gl_insert_debug_marker:
375  * @context: a #GstGLContext
376  * @format: a printf-style format string
377  * @...: arguments form @format
378  *
379  * Inserts a marker into a GL debug stream.  Requires the 'gldebugmarker'
380  * debug category to be at least %GST_LEVEL_FIXME.
381  *
382  * Since: 1.8
383  */
384 void
gst_gl_insert_debug_marker(GstGLContext * context,const gchar * format,...)385 gst_gl_insert_debug_marker (GstGLContext * context, const gchar * format, ...)
386 {
387   const GstGLFuncs *gl = context->gl_vtable;
388   gchar *string;
389   gint len;
390   va_list args;
391 
392   _init_debug ();
393 
394   /* are we enabled */
395   if (gst_debug_category_get_threshold (gst_gl_marker_debug) < GST_LEVEL_FIXME)
396     return;
397 
398   va_start (args, format);
399   len = gst_info_vasprintf (&string, format, args);
400   va_end (args);
401 
402   /* gst_info_vasprintf() returns -1 on error, the various debug marker
403    * functions take len=-1 to mean null terminated */
404   if (len < 0 || string == NULL)
405     /* no debug output */
406     return;
407 
408   if (gl->DebugMessageInsert)
409     gl->DebugMessageInsert (GL_DEBUG_SOURCE_THIRD_PARTY, GL_DEBUG_TYPE_MARKER,
410         0, GL_DEBUG_SEVERITY_LOW, (gsize) len, string);
411   else if (gl->InsertEventMarker)
412     gl->InsertEventMarker (len, string);
413   else if (gl->StringMarker)
414     gl->StringMarker (len, string);
415 
416   g_free (string);
417 }
418 
419 /**
420  * gst_gl_async_debug_store_log_msg_valist:
421  * @ad: the #GstGLAsyncDebug to store the message in
422  * @cat: the #GstDebugCategory to output the message in
423  * @level: the #GstLevel
424  * @file: the file where the debug message originates from
425  * @function: the function where the debug message originates from
426  * @line: the line in @file where the debug message originates from
427  * @object: (allow-none): a #GObject to associate with the debug message
428  * @format: a printf style format string
429  * @varargs: the list of arguments for @format
430  *
431  * Stores a debug message for later output by gst_gl_async_debug_output_log_msg()
432  *
433  * Since: 1.8
434  */
435 void
gst_gl_async_debug_store_log_msg_valist(GstGLAsyncDebug * ad,GstDebugCategory * cat,GstDebugLevel level,const gchar * file,const gchar * function,gint line,GObject * object,const gchar * format,va_list varargs)436 gst_gl_async_debug_store_log_msg_valist (GstGLAsyncDebug * ad,
437     GstDebugCategory * cat, GstDebugLevel level, const gchar * file,
438     const gchar * function, gint line, GObject * object, const gchar * format,
439     va_list varargs)
440 {
441   gst_gl_async_debug_output_log_msg (ad);
442   _free_async_debug_data (ad);
443 
444   if (G_UNLIKELY (level <= GST_LEVEL_MAX && level <= _gst_debug_min)) {
445     if (!cat)
446       cat = default_debug;
447 
448     ad->cat = cat;
449     ad->level = level;
450     ad->file = file;
451     ad->function = function;
452     ad->line = line;
453     if (object)
454       ad->object = g_object_ref (object);
455     else
456       ad->object = NULL;
457 
458     ad->debug_msg = gst_info_strdup_vprintf (format, varargs);
459     ad->state_flags |= ASYNC_DEBUG_FILLED;
460   }
461 }
462 
463 /**
464  * gst_gl_async_debug_output_log_msg:
465  * @ad: the #GstGLAsyncDebug to store the message in
466  *
467  * Outputs a previously stored debug message.
468  */
469 void
gst_gl_async_debug_output_log_msg(GstGLAsyncDebug * ad)470 gst_gl_async_debug_output_log_msg (GstGLAsyncDebug * ad)
471 {
472   if ((ad->state_flags & ASYNC_DEBUG_FILLED) != 0
473       && (ad->state_flags & ASYNC_DEBUG_FROZEN) == 0) {
474     gchar *msg = NULL;
475 
476     if (ad->callback)
477       msg = ad->callback (ad->user_data);
478 
479     gst_debug_log (ad->cat, ad->level, ad->file, ad->function, ad->line,
480         ad->object, "%s %s", GST_STR_NULL (ad->debug_msg), msg ? msg : "");
481     g_free (msg);
482     _free_async_debug_data (ad);
483   }
484 }
485 
486 /**
487  * gst_gl_async_debug_store_log_msg:
488  * @ad: the #GstGLAsyncDebug to store the message in
489  * @cat: the #GstDebugCategory to output the message in
490  * @level: the #GstLevel
491  * @file: the file where the debug message originates from
492  * @function: the function where the debug message originates from
493  * @line: the line in @file where the debug message originates from
494  * @object: (allow-none): a #GObject to associate with the debug message
495  * @format: a printf style format string
496  * @...: the list of arguments for @format
497  *
498  * Stores a debug message for later output by gst_gl_async_debug_output_log_msg()
499  *
500  * Since: 1.8
501  */
502 void
gst_gl_async_debug_store_log_msg(GstGLAsyncDebug * ad,GstDebugCategory * cat,GstDebugLevel level,const gchar * file,const gchar * function,gint line,GObject * object,const gchar * format,...)503 gst_gl_async_debug_store_log_msg (GstGLAsyncDebug * ad, GstDebugCategory * cat,
504     GstDebugLevel level, const gchar * file, const gchar * function, gint line,
505     GObject * object, const gchar * format, ...)
506 {
507   va_list varargs;
508 
509   if (G_UNLIKELY (level <= GST_LEVEL_MAX && level <= _gst_debug_min)) {
510     va_start (varargs, format);
511     gst_gl_async_debug_store_log_msg_valist (ad, cat, level, file, function,
512         line, object, format, varargs);
513     va_end (varargs);
514   }
515 }
516 #else
517 G_GNUC_INTERNAL void _gst_gl_debug_enable (GstGLContext * context);
518 #endif
519