/* * GStreamer * Copyright (C) 2015 Matthew Waters * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "gstgldebug.h" #include #include #include "gstglcontext.h" #include "gstglcontext_private.h" #include "gstglfuncs.h" /** * SECTION:gstgldebug * @short_description: helper routines for dealing with OpenGL debugging * @title: OpenGL debugging * @see_also: #GstGLContext */ #define ASYNC_DEBUG_FILLED (1 << 0) #define ASYNC_DEBUG_FROZEN (1 << 1) /* compatibility defines */ #ifndef GL_DEBUG_TYPE_ERROR #define GL_DEBUG_TYPE_ERROR 0x824C #endif #ifndef GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR #define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D #endif #ifndef GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR #define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E #endif #ifndef GL_DEBUG_TYPE_PORTABILITY #define GL_DEBUG_TYPE_PORTABILITY 0x824F #endif #ifndef GL_DEBUG_TYPE_PERFORMANCE #define GL_DEBUG_TYPE_PERFORMANCE 0x8250 #endif #ifndef GL_DEBUG_TYPE_MARKER #define GL_DEBUG_TYPE_MARKER 0x8268 #endif #ifndef GL_DEBUG_TYPE_OTHER #define GL_DEBUG_TYPE_OTHER 0x8251 #endif #ifndef GL_DEBUG_SEVERITY_HIGH #define GL_DEBUG_SEVERITY_HIGH 0x9146 #endif #ifndef GL_DEBUG_SEVERITY_MEDIUM #define GL_DEBUG_SEVERITY_MEDIUM 0x9147 #endif #ifndef GL_DEBUG_SEVERITY_LOW #define GL_DEBUG_SEVERITY_LOW 0x9148 #endif #ifndef GL_DEBUG_SEVERITY_NOTIFICATION #define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B #endif #ifndef GL_DEBUG_SOURCE_API #define GL_DEBUG_SOURCE_API 0x8246 #endif #ifndef GL_DEBUG_SOURCE_WINDOW_SYSTEM #define GL_DEBUG_SOURCE_WINDOW_SYSTEM 0x8247 #endif #ifndef GL_DEBUG_SOURCE_SHADER_COMPILER #define GL_DEBUG_SOURCE_SHADER_COMPILER 0x8248 #endif #ifndef GL_DEBUG_SOURCE_THIRD_PARTY #define GL_DEBUG_SOURCE_THIRD_PARTY 0x8249 #endif #ifndef GL_DEBUG_SOURCE_APPLICATION #define GL_DEBUG_SOURCE_APPLICATION 0x824A #endif #ifndef GL_DEBUG_SOURCE_OTHER #define GL_DEBUG_SOURCE_OTHER 0x824B #endif GST_DEBUG_CATEGORY_STATIC (gst_performance); #define GST_CAT_DEFAULT gst_gl_debug GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); GST_DEBUG_CATEGORY_STATIC (default_debug); GST_DEBUG_CATEGORY_STATIC (gst_gl_marker_debug); static void _init_debug (void) { static volatile gsize _init = 0; if (g_once_init_enter (&_init)) { GST_DEBUG_CATEGORY_GET (gst_performance, "GST_PERFORMANCE"); GST_DEBUG_CATEGORY_GET (gst_gl_debug, "gldebug"); GST_DEBUG_CATEGORY_GET (default_debug, "default"); GST_DEBUG_CATEGORY_INIT (gst_gl_marker_debug, "gldebugmarker", 0, "OpenGL Markers"); g_once_init_leave (&_init, 1); } } static void _free_async_debug_data (GstGLAsyncDebug * ad) { if (ad->debug_msg) { g_free (ad->debug_msg); ad->debug_msg = NULL; if (ad->object) g_object_unref (ad->object); ad->object = NULL; ad->state_flags &= ~ASYNC_DEBUG_FILLED; } } /** * gst_gl_async_debug_init: * @ad: a #GstGLAsyncDebug * * Initialize @ad. Intended for use with #GstGLAsyncDebug's that are embedded * in other structs. * * Since: 1.8 */ void gst_gl_async_debug_init (GstGLAsyncDebug * ad) { _init_debug (); memset (ad, 0, sizeof (*ad)); } /** * gst_gl_async_debug_unset: * @ad: a #GstGLAsyncDebug * * Unset any dynamically allocated data. Intended for use with * #GstGLAsyncDebug's that are embedded in other structs. */ void gst_gl_async_debug_unset (GstGLAsyncDebug * ad) { gst_gl_async_debug_output_log_msg (ad); _free_async_debug_data (ad); if (ad->notify) ad->notify (ad->user_data); } /** * gst_gl_async_debug_new: (skip) * * Free with gst_gl_async_debug_free() * * Returns: a new #GstGLAsyncDebug * * Since: 1.8 */ GstGLAsyncDebug * gst_gl_async_debug_new (void) { return g_new0 (GstGLAsyncDebug, 1); } /** * gst_gl_async_debug_free: * @ad: a #GstGLAsyncDebug * * Frees @ad * * Since: 1.8 */ void gst_gl_async_debug_free (GstGLAsyncDebug * ad) { gst_gl_async_debug_unset (ad); g_free (ad); } /** * gst_gl_async_debug_freeze: * @ad: a #GstGLAsyncDebug * * freeze the debug output. While frozen, any call to * gst_gl_async_debug_output_log_msg() will not output any messages but * subsequent calls to gst_gl_async_debug_store_log_msg() will overwrite previous * messages. * * Since: 1.8 */ void gst_gl_async_debug_freeze (GstGLAsyncDebug * ad) { ad->state_flags |= ASYNC_DEBUG_FROZEN; } /** * gst_gl_async_debug_thaw: * @ad: a #GstGLAsyncDebug * * unfreeze the debug output. See gst_gl_async_debug_freeze() for what freezing means * * Since: 1.8 */ void gst_gl_async_debug_thaw (GstGLAsyncDebug * ad) { ad->state_flags &= ~ASYNC_DEBUG_FROZEN; } #if !defined(GST_DISABLE_GST_DEBUG) static inline const gchar * _debug_severity_to_string (GLenum severity) { switch (severity) { case GL_DEBUG_SEVERITY_HIGH: return "high"; case GL_DEBUG_SEVERITY_MEDIUM: return "medium"; case GL_DEBUG_SEVERITY_LOW: return "low"; case GL_DEBUG_SEVERITY_NOTIFICATION: return "notification"; default: return "invalid"; } } static inline const gchar * _debug_source_to_string (GLenum source) { switch (source) { case GL_DEBUG_SOURCE_API: return "API"; case GL_DEBUG_SOURCE_WINDOW_SYSTEM: return "winsys"; case GL_DEBUG_SOURCE_SHADER_COMPILER: return "shader compiler"; case GL_DEBUG_SOURCE_THIRD_PARTY: return "third party"; case GL_DEBUG_SOURCE_APPLICATION: return "application"; case GL_DEBUG_SOURCE_OTHER: return "other"; default: return "invalid"; } } static inline const gchar * _debug_type_to_string (GLenum type) { switch (type) { case GL_DEBUG_TYPE_ERROR: return "error"; case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: return "deprecated"; case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: return "undefined"; case GL_DEBUG_TYPE_PORTABILITY: return "portability"; case GL_DEBUG_TYPE_PERFORMANCE: return "performance"; case GL_DEBUG_TYPE_MARKER: return "debug marker"; case GL_DEBUG_TYPE_OTHER: return "other"; default: return "invalid"; } } static void GSTGLAPI _gst_gl_debug_callback (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const gchar * message, gpointer user_data) { GstGLContext *context = user_data; const gchar *severity_str = _debug_severity_to_string (severity); const gchar *source_str = _debug_source_to_string (source); const gchar *type_str = _debug_type_to_string (type); _init_debug (); switch (type) { case GL_DEBUG_TYPE_ERROR: case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: GST_ERROR_OBJECT (context, "%s: GL %s from %s id:%u, %s", severity_str, type_str, source_str, id, message); break; case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: case GL_DEBUG_TYPE_PORTABILITY: GST_FIXME_OBJECT (context, "%s: GL %s from %s id:%u, %s", severity_str, type_str, source_str, id, message); break; case GL_DEBUG_TYPE_PERFORMANCE: GST_CAT_DEBUG_OBJECT (gst_performance, context, "%s: GL %s from %s id:%u," " %s", severity_str, type_str, source_str, id, message); break; default: GST_DEBUG_OBJECT (context, "%s: GL %s from %s id:%u, %s", severity_str, type_str, source_str, id, message); break; } } G_GNUC_INTERNAL void _gst_gl_debug_enable (GstGLContext * context); G_GNUC_INTERNAL void _gst_gl_debug_enable (GstGLContext * context) { const GstGLFuncs *gl = context->gl_vtable; GstDebugLevel level; GLenum debug_types[8]; guint i, n = 0; _init_debug (); if (!gl->DebugMessageCallback) { GST_CAT_INFO_OBJECT (gst_gl_context_debug, context, "No debugging support available"); return; } level = gst_debug_category_get_threshold (gst_gl_debug); if (level < GST_LEVEL_ERROR) { GST_CAT_INFO_OBJECT (gst_gl_context_debug, context, "Disabling GL context debugging (gldebug category debug level < error)"); return; } GST_CAT_INFO_OBJECT (gst_gl_context_debug, context, "Enabling GL context debugging"); gl->DebugMessageCallback (_gst_gl_debug_callback, context); if (level >= GST_LEVEL_DEBUG) { /* enable them all */ gl->DebugMessageControl (GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, 0, GL_TRUE); } else { if (level >= GST_LEVEL_FIXME) { debug_types[n++] = GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR; debug_types[n++] = GL_DEBUG_TYPE_PORTABILITY; } if (level >= GST_LEVEL_ERROR) { debug_types[n++] = GL_DEBUG_TYPE_ERROR; debug_types[n++] = GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR; } g_assert (n < G_N_ELEMENTS (debug_types)); for (i = 0; i < n; i++) { gl->DebugMessageControl (GL_DONT_CARE, debug_types[i], GL_DONT_CARE, 0, 0, GL_TRUE); } } } /** * gst_gl_insert_debug_marker: * @context: a #GstGLContext * @format: a printf-style format string * @...: arguments form @format * * Inserts a marker into a GL debug stream. Requires the 'gldebugmarker' * debug category to be at least %GST_LEVEL_FIXME. * * Since: 1.8 */ void gst_gl_insert_debug_marker (GstGLContext * context, const gchar * format, ...) { const GstGLFuncs *gl = context->gl_vtable; gchar *string; gint len; va_list args; _init_debug (); /* are we enabled */ if (gst_debug_category_get_threshold (gst_gl_marker_debug) < GST_LEVEL_FIXME) return; va_start (args, format); len = gst_info_vasprintf (&string, format, args); va_end (args); /* gst_info_vasprintf() returns -1 on error, the various debug marker * functions take len=-1 to mean null terminated */ if (len < 0 || string == NULL) /* no debug output */ return; if (gl->DebugMessageInsert) gl->DebugMessageInsert (GL_DEBUG_SOURCE_THIRD_PARTY, GL_DEBUG_TYPE_MARKER, 0, GL_DEBUG_SEVERITY_LOW, (gsize) len, string); else if (gl->InsertEventMarker) gl->InsertEventMarker (len, string); else if (gl->StringMarker) gl->StringMarker (len, string); g_free (string); } /** * gst_gl_async_debug_store_log_msg_valist: * @ad: the #GstGLAsyncDebug to store the message in * @cat: the #GstDebugCategory to output the message in * @level: the #GstLevel * @file: the file where the debug message originates from * @function: the function where the debug message originates from * @line: the line in @file where the debug message originates from * @object: (allow-none): a #GObject to associate with the debug message * @format: a printf style format string * @varargs: the list of arguments for @format * * Stores a debug message for later output by gst_gl_async_debug_output_log_msg() * * Since: 1.8 */ 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) { gst_gl_async_debug_output_log_msg (ad); _free_async_debug_data (ad); if (G_UNLIKELY (level <= GST_LEVEL_MAX && level <= _gst_debug_min)) { if (!cat) cat = default_debug; ad->cat = cat; ad->level = level; ad->file = file; ad->function = function; ad->line = line; if (object) ad->object = g_object_ref (object); else ad->object = NULL; ad->debug_msg = gst_info_strdup_vprintf (format, varargs); ad->state_flags |= ASYNC_DEBUG_FILLED; } } /** * gst_gl_async_debug_output_log_msg: * @ad: the #GstGLAsyncDebug to store the message in * * Outputs a previously stored debug message. */ void gst_gl_async_debug_output_log_msg (GstGLAsyncDebug * ad) { if ((ad->state_flags & ASYNC_DEBUG_FILLED) != 0 && (ad->state_flags & ASYNC_DEBUG_FROZEN) == 0) { gchar *msg = NULL; if (ad->callback) msg = ad->callback (ad->user_data); gst_debug_log (ad->cat, ad->level, ad->file, ad->function, ad->line, ad->object, "%s %s", GST_STR_NULL (ad->debug_msg), msg ? msg : ""); g_free (msg); _free_async_debug_data (ad); } } /** * gst_gl_async_debug_store_log_msg: * @ad: the #GstGLAsyncDebug to store the message in * @cat: the #GstDebugCategory to output the message in * @level: the #GstLevel * @file: the file where the debug message originates from * @function: the function where the debug message originates from * @line: the line in @file where the debug message originates from * @object: (allow-none): a #GObject to associate with the debug message * @format: a printf style format string * @...: the list of arguments for @format * * Stores a debug message for later output by gst_gl_async_debug_output_log_msg() * * Since: 1.8 */ 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, ...) { va_list varargs; if (G_UNLIKELY (level <= GST_LEVEL_MAX && level <= _gst_debug_min)) { va_start (varargs, format); gst_gl_async_debug_store_log_msg_valist (ad, cat, level, file, function, line, object, format, varargs); va_end (varargs); } } #else G_GNUC_INTERNAL void _gst_gl_debug_enable (GstGLContext * context); #endif