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 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 #GstDebugLevel
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 #GstDebugLevel
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