#ifndef Magnum_GL_DebugOutput_h #define Magnum_GL_DebugOutput_h /* This file is part of Magnum. Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021 Vladimír Vondruš Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef MAGNUM_TARGET_WEBGL /** @file * @brief Class @ref Magnum::GL::DebugOutput, @ref Magnum::GL::DebugMessage, @ref Magnum::GL::DebugGroup */ #endif #include #include #include "Magnum/Magnum.h" #include "Magnum/GL/OpenGL.h" #include "Magnum/GL/visibility.h" #ifndef MAGNUM_TARGET_WEBGL namespace Magnum { namespace GL { namespace Implementation { struct DebugState; } /** @brief Debug output Manages OpenGL debug output. The debug messages are emitted either from driver (such as GL error descriptions and various performance and optimization hints) or from third party software and the application itself using @ref DebugMessage and @ref DebugGroup, which can be also used to mark various portions of command stream in various graphics debuggers, such as ApiTrace or gDEBugger. @section GL-Magnum-DebugOutput-usage Basic usage Support for debug output is provided by OpenGL 4.3 / OpenGL ES 3.2 or @gl_extension{KHR,debug} (desktop/ES extension, covered also by @gl_extension{ANDROID,extension_pack_es31a}). Subset of the functionality is provided also by @gl_extension{EXT,debug_marker} (desktop/ES extensions) or @gl_extension{GREMEDY,string_marker} (desktop only extension). With OpenGL 4.3 / OpenGL ES 3.2 or @gl_extension{KHR,debug} desktop/ES extension, the debug output needs to be enabled first. It can be enabled globally using @ref Platform::Sdl2Application::GLConfiguration::Flag::Debug "Platform::*Application::GLConfiguration::Flag::Debug" when creating context or only for some portions of the code using @ref Renderer::Feature::DebugOutput. If enabled globally, some OpenGL drivers may provide additional debugging information. In addition to that you can control the output at even finer granularity using @ref setEnabled(). You can gather the messages either through graphics debugger or in the application itself by setting up message callback using @ref setCallback() or @ref setDefaultCallback(). You might also want to enable @ref Renderer::Feature::DebugOutputSynchronous. Example usage, completely with @ref DebugGroup and @ref DebugMessage is below. @m_class{m-block m-success} @par Enabling debug output from the command line / environment Apart from setting up the debug output callbacks manually, it's also possible to enable it conveniently using the `--magnum-gpu-validation` @ref GL-Context-command-line "command-line or environment option" --- ideal for quick debugging of rendering issues. If you are using application classes from the @ref Platform namespace, this option also ensures that @ref Platform::Sdl2Application::GLConfiguration::Flag::Debug "GLConfiguration::Flag::Debug" is passed for context creation, both with windowed and windowless application implementations. @snippet MagnumGL.cpp DebugOutput-usage With default callback the group entering/leaving and the inserted message (and possibly also other messages) will be printed on standard output: @code{.shell-session} Debug output: application debug group enter (42): Scene rendering Debug output: application marker (1337): Rendering transparent mesh ... Debug output: application debug group leave (42): Scene rendering @endcode If only @gl_extension{EXT,debug_marker} or @gl_extension{GREMEDY,string_marker} are supported, only user-inserted messages and debug groups are supported and they can be seen only through graphics debugger. If OpenGL 4.3 is not supported and neither @gl_extension{KHR,debug} nor @gl_extension{EXT,debug_marker} nor @gl_extension{GREMEDY,string_marker} are available, all the functions are essentially a no-op. Besides inserting messages into GL command stream you can also annotate OpenGL objects with labels. See @ref AbstractQuery::setLabel(), @ref AbstractShaderProgram::setLabel(), @ref AbstractTexture::setLabel(), @ref Buffer::setLabel(), @ref Framebuffer::setLabel(), @ref Mesh::setLabel(), @ref Renderbuffer::setLabel(), @ref Shader::setLabel() and @ref TransformFeedback::setLabel() for more information. @requires_gles Debug output is not available in WebGL. */ class MAGNUM_GL_EXPORT DebugOutput { friend Implementation::DebugState; public: /** * @brief Message source * * @see @ref setEnabled() * @m_enum_values_as_keywords */ enum class Source: GLenum { /** OpenGL */ #ifndef MAGNUM_TARGET_GLES2 Api = GL_DEBUG_SOURCE_API, #else Api = GL_DEBUG_SOURCE_API_KHR, #endif /** Window system (GLX, WGL) */ #ifndef MAGNUM_TARGET_GLES2 WindowSystem = GL_DEBUG_SOURCE_WINDOW_SYSTEM, #else WindowSystem = GL_DEBUG_SOURCE_WINDOW_SYSTEM_KHR, #endif /** Shader compiler */ #ifndef MAGNUM_TARGET_GLES2 ShaderCompiler = GL_DEBUG_SOURCE_SHADER_COMPILER, #else ShaderCompiler = GL_DEBUG_SOURCE_SHADER_COMPILER_KHR, #endif /** External debugger or third-party middleware */ #ifndef MAGNUM_TARGET_GLES2 ThirdParty = GL_DEBUG_SOURCE_THIRD_PARTY, #else ThirdParty = GL_DEBUG_SOURCE_THIRD_PARTY_KHR, #endif /** The application */ #ifndef MAGNUM_TARGET_GLES2 Application = GL_DEBUG_SOURCE_APPLICATION, #else Application = GL_DEBUG_SOURCE_APPLICATION_KHR, #endif /** Any other source */ #ifndef MAGNUM_TARGET_GLES2 Other = GL_DEBUG_SOURCE_OTHER #else Other = GL_DEBUG_SOURCE_OTHER_KHR #endif }; /** * @brief Message type * * @see @ref setEnabled() * @m_enum_values_as_keywords */ enum class Type: GLenum { /** OpenGL error */ #ifndef MAGNUM_TARGET_GLES2 Error = GL_DEBUG_TYPE_ERROR, #else Error = GL_DEBUG_TYPE_ERROR_KHR, #endif /** Behavior that has been marked for deprecation */ #ifndef MAGNUM_TARGET_GLES2 DeprecatedBehavior = GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR, #else DeprecatedBehavior = GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_KHR, #endif /** Behavior that is undefined according to the specification */ #ifndef MAGNUM_TARGET_GLES2 UndefinedBehavior = GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR, #else UndefinedBehavior = GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_KHR, #endif /** Non-portable usage of extensions or shaders */ #ifndef MAGNUM_TARGET_GLES2 Portability = GL_DEBUG_TYPE_PORTABILITY, #else Portability = GL_DEBUG_TYPE_PORTABILITY_KHR, #endif /** Implementation-dependent performance warning */ #ifndef MAGNUM_TARGET_GLES2 Performance = GL_DEBUG_TYPE_PERFORMANCE, #else Performance = GL_DEBUG_TYPE_PERFORMANCE_KHR, #endif /** Annotation of the command stream */ #ifndef MAGNUM_TARGET_GLES2 Marker = GL_DEBUG_TYPE_MARKER, #else Marker = GL_DEBUG_TYPE_MARKER_KHR, #endif /** Entering a debug group */ #ifndef MAGNUM_TARGET_GLES2 PushGroup = GL_DEBUG_TYPE_PUSH_GROUP, #else PushGroup = GL_DEBUG_TYPE_PUSH_GROUP_KHR, #endif /** Leaving a debug group */ #ifndef MAGNUM_TARGET_GLES2 PopGroup = GL_DEBUG_TYPE_POP_GROUP, #else PopGroup = GL_DEBUG_TYPE_POP_GROUP_KHR, #endif /** Any other type */ #ifndef MAGNUM_TARGET_GLES2 Other = GL_DEBUG_TYPE_OTHER, #else Other = GL_DEBUG_TYPE_OTHER_KHR, #endif }; /** * @brief Message severity * * @see @ref setEnabled() * @m_enum_values_as_keywords */ enum class Severity: GLenum { /** * Any OpenGL error, dangerous undefined behavior, shader * compilation errors. */ #ifndef MAGNUM_TARGET_GLES2 High = GL_DEBUG_SEVERITY_HIGH, #else High = GL_DEBUG_SEVERITY_HIGH_KHR, #endif /** * Severe performance warnings, shader compilation warnings, use of * deprecated behavior. */ #ifndef MAGNUM_TARGET_GLES2 Medium = GL_DEBUG_SEVERITY_MEDIUM, #else Medium = GL_DEBUG_SEVERITY_MEDIUM_KHR, #endif /** Minor performance warnings, trivial undefined behavior. */ #ifndef MAGNUM_TARGET_GLES2 Low = GL_DEBUG_SEVERITY_LOW, #else Low = GL_DEBUG_SEVERITY_LOW_KHR, #endif /** Any message other than error or performance warning. */ #ifndef MAGNUM_TARGET_GLES2 Notification = GL_DEBUG_SEVERITY_NOTIFICATION #else Notification = GL_DEBUG_SEVERITY_NOTIFICATION_KHR #endif }; /** * @brief Debug callback * * @see @ref setCallback() */ typedef void(*Callback)(Source, Type, UnsignedInt, Severity, const std::string&, const void*); /** * @brief Max count of debug messages in log * * The result is cached, repeated queries don't result in repeated * OpenGL calls. If OpenGL 4.3 / OpenGL ES 3.2 is not supported and * @gl_extension{KHR,debug} desktop or ES extension (covered also by * @gl_extension{ANDROID,extension_pack_es31a}) is not available, returns * @cpp 0 @ce. * @see @fn_gl{Get} with @def_gl_keyword{MAX_DEBUG_LOGGED_MESSAGES} */ static Int maxLoggedMessages(); /** * @brief Max debug message length * * The result is cached, repeated queries don't result in repeated * OpenGL calls. If OpenGL 4.3 / OpenGL ES 3.2 is not supported and * @gl_extension{KHR,debug} desktop or ES extension (covered also by * @gl_extension{ANDROID,extension_pack_es31a}) is not available, returns * @cpp 0 @ce. * @see @fn_gl{Get} with @def_gl_keyword{MAX_DEBUG_MESSAGE_LENGTH} */ static Int maxMessageLength(); /** * @brief Enable or disable particular output type * * @attention If any @ref DebugGroup is active when making this call, * the setting will be remembered only for the time in which the * group is active and leaving it will revert the setting to state * set in parent debug group. See @ref DebugGroup documentation * for more information. * * If OpenGL 4.3 / OpenGL ES 3.2 is not supported and @gl_extension{KHR,debug} * desktop or ES extension (covered also by * @gl_extension{ANDROID,extension_pack_es31a}) is not available, this * function does nothing. * @see @ref Renderer::Feature::DebugOutput, * @fn_gl_keyword{DebugMessageControl} */ static void setEnabled(Source source, Type type, std::initializer_list ids, bool enabled) { setEnabledInternal(GLenum(source), GLenum(type), GL_DONT_CARE, ids, enabled); } /** @overload */ static void setEnabled(Source source, Type type, Severity severity, bool enabled) { setEnabledInternal(GLenum(source), GLenum(type), GLenum(severity), {}, enabled); } /** @overload */ static void setEnabled(Source source, Type type, bool enabled) { setEnabledInternal(GLenum(source), GLenum(type), GL_DONT_CARE, {}, enabled); } /** @overload */ static void setEnabled(Source source, Severity severity, bool enabled) { setEnabledInternal(GLenum(source), GL_DONT_CARE, GLenum(severity), {}, enabled); } /** @overload */ static void setEnabled(Source source, bool enabled) { setEnabledInternal(GLenum(source), GL_DONT_CARE, GL_DONT_CARE, {}, enabled); } /** @overload */ static void setEnabled(Type type, Severity severity, bool enabled) { setEnabledInternal(GL_DONT_CARE, GLenum(type), GLenum(severity), {}, enabled); } /** @overload */ static void setEnabled(Type type, bool enabled) { setEnabledInternal(GL_DONT_CARE, GLenum(type), GL_DONT_CARE, {}, enabled); } /** @overload */ static void setEnabled(Severity severity, bool enabled) { setEnabledInternal(GL_DONT_CARE, GL_DONT_CARE, GLenum(severity), {}, enabled); } /** @overload */ static void setEnabled(bool enabled) { setEnabledInternal(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, {}, enabled); } /** * @brief Set debug message callback * * The messages are sent to the callback only if * @ref Renderer::Feature::DebugOutput is enabled. If OpenGL 4.3 / * OpenGL ES 3.2 is not supported and @gl_extension{KHR,debug} desktop or * ES extension (covered also by @gl_extension{ANDROID,extension_pack_es31a}) * is not available, this function does nothing. * * @attention The function is not necessarily called from the same * thread as the one that caused the message to appear --- in * particular, you can't assume the @ref GL::Context will be * present in the callback context. It might work on some drivers, * but not on others. * * @see @ref setDefaultCallback(), * @ref Renderer::Feature::DebugOutputSynchronous, * @fn_gl_keyword{DebugMessageCallback} */ static void setCallback(Callback callback, const void* userParam = nullptr); /** * @brief Set default debug message callback * * See @ref setCallback() for more information. The message is printed * to @ref Corrade::Utility::Debug "Debug" output in the following * format: * * @snippet MagnumGL.cpp DebugOutput-setDefaultCallback * * @code{.shell-session} * Debug output: application marker (1337): Hello from OpenGL command stream! * @endcode */ static void setDefaultCallback(); /** @brief There's no point in having an instance of this class */ DebugOutput() = delete; private: static void setEnabledInternal(GLenum source, GLenum type, GLenum severity, std::initializer_list ids, bool enabled); static MAGNUM_GL_LOCAL void controlImplementationNoOp(GLenum, GLenum, GLenum, std::initializer_list, bool); #ifndef MAGNUM_TARGET_GLES2 static MAGNUM_GL_LOCAL void controlImplementationKhrDesktopES32(GLenum source, GLenum type, GLenum severity, std::initializer_list ids, bool enabled); #endif #ifdef MAGNUM_TARGET_GLES static MAGNUM_GL_LOCAL void controlImplementationKhrES(GLenum source, GLenum type, GLenum severity, std::initializer_list ids, bool enabled); #endif static MAGNUM_GL_LOCAL void callbackImplementationNoOp(Callback, const void*); #ifndef MAGNUM_TARGET_GLES2 static MAGNUM_GL_LOCAL void callbackImplementationKhrDesktopES32(Callback callback, const void* userParam); #endif #ifdef MAGNUM_TARGET_GLES static MAGNUM_GL_LOCAL void callbackImplementationKhrES(Callback callback, const void* userParam); #endif }; /** @debugoperatorclassenum{DebugOutput,DebugOutput::Source} */ MAGNUM_GL_EXPORT Debug& operator<<(Debug& debug, DebugOutput::Source value); /** @debugoperatorclassenum{DebugOutput,DebugOutput::Type} */ MAGNUM_GL_EXPORT Debug& operator<<(Debug& debug, DebugOutput::Type value); /** @debugoperatorclassenum{DebugOutput,DebugOutput::Severity} */ MAGNUM_GL_EXPORT Debug& operator<<(Debug& debug, DebugOutput::Severity value); /** @brief Debug message Allows inserting messages GL command stream with labels, useful for example with conjunction with various graphics debuggers, such as ApiTrace or gDEBugger. @section GL-DebugMessage-usage Basic usage See @ref DebugOutput for introduction. If OpenGL 4.3 / OpenGL ES 3.2 is supported or @gl_extension{KHR,debug} desktop or ES extension (covered also by @gl_extension{ANDROID,extension_pack_es31a}) is available and default debug output callback is enabled for given kind of messages, the inserted message will be printed on standard output in the following form: @snippet MagnumGL.cpp DebugMessage-usage

@code{.shell-session} Debug output: application marker (1337): Hello from OpenGL command stream! @endcode

If only @gl_extension{EXT,debug_marker} or @gl_extension{GREMEDY,string_marker} are available, the message can be seen only through graphics debugger. If OpenGL 4.3 is not supported and neither @gl_extension{KHR,debug} nor @gl_extension{EXT,debug_marker} nor @gl_extension{GREMEDY,string_marker} are available, the function is essentially a no-op. @section GL-DebugMessage-performance-notes Performance notes If you ensure that you always use the @cpp const char @ce overload of @ref insert() and the debug output is either not supported or turned off, the calls will not result in any allocations and thus won't have any negative performance effects. @see @ref DebugGroup @requires_gles Debug output is not available in WebGL. */ class MAGNUM_GL_EXPORT DebugMessage { friend Implementation::DebugState; public: /** * @brief Message source * * @see @ref insert() * @m_enum_values_as_keywords */ enum class Source: GLenum { /** External debugger or third-party middleware */ #ifndef MAGNUM_TARGET_GLES2 ThirdParty = GL_DEBUG_SOURCE_THIRD_PARTY, #else ThirdParty = GL_DEBUG_SOURCE_THIRD_PARTY_KHR, #endif /** The application */ #ifndef MAGNUM_TARGET_GLES2 Application = GL_DEBUG_SOURCE_APPLICATION #else Application = GL_DEBUG_SOURCE_APPLICATION_KHR #endif }; /** * @brief Message type * * @see @ref insert() * @m_enum_values_as_keywords */ enum class Type: GLenum { /** OpenGL error */ #ifndef MAGNUM_TARGET_GLES2 Error = GL_DEBUG_TYPE_ERROR, #else Error = GL_DEBUG_TYPE_ERROR_KHR, #endif /** Behavior that has been marked for deprecation */ #ifndef MAGNUM_TARGET_GLES2 DeprecatedBehavior = GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR, #else DeprecatedBehavior = GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_KHR, #endif /** Behavior that is undefined according to the specification */ #ifndef MAGNUM_TARGET_GLES2 UndefinedBehavior = GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR, #else UndefinedBehavior = GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_KHR, #endif /** Non-portable usage of extensions or shaders */ #ifndef MAGNUM_TARGET_GLES2 Portability = GL_DEBUG_TYPE_PORTABILITY, #else Portability = GL_DEBUG_TYPE_PORTABILITY_KHR, #endif /** Implementation-dependent performance warning */ #ifndef MAGNUM_TARGET_GLES2 Performance = GL_DEBUG_TYPE_PERFORMANCE, #else Performance = GL_DEBUG_TYPE_PERFORMANCE_KHR, #endif /** Annotation of the command stream */ #ifndef MAGNUM_TARGET_GLES2 Marker = GL_DEBUG_TYPE_MARKER, #else Marker = GL_DEBUG_TYPE_MARKER_KHR, #endif /** Any other type */ #ifndef MAGNUM_TARGET_GLES2 Other = GL_DEBUG_TYPE_OTHER #else Other = GL_DEBUG_TYPE_OTHER_KHR #endif }; /** * @brief Insert message * @param source Message source * @param type Message type * @param id Message-specific ID * @param severity Message severity * @param string The actual message * * If OpenGL 4.3 / OpenGL ES 3.2 is not supported and neither * @gl_extension{KHR,debug} (covered also by @gl_extension{ANDROID,extension_pack_es31a}) * nor @gl_extension{EXT,debug_marker} (desktop or ES extensions) nor * @gl_extension{GREMEDY,string_marker} (desktop only extension) are * available, this function does nothing. * * If @gl_extension{KHR,debug} is not available and only @gl_extension{EXT,debug_marker} * or @gl_extension{GREMEDY,string_marker} are available, only @p string * is used and all other parameters are ignored. * @see @ref DebugOutput::maxMessageLength(), * @fn_gl_keyword{DebugMessageInsert}, * @fn_gl_extension_keyword{InsertEventMarker,EXT,debug_marker} or * @fn_gl_extension_keyword{StringMarker,GREMEDY,string_marker} */ static void insert(Source source, Type type, UnsignedInt id, DebugOutput::Severity severity, const std::string& string) { insertInternal(source, type, id, severity, {string.data(), string.size()}); } /** @overload */ template static void insert(Source source, Type type, UnsignedInt id, DebugOutput::Severity severity, const char(&string)[size]) { insertInternal(source, type, id, severity, {string, size - 1}); } /** @brief There's no point in having an instance of this class */ DebugMessage() = delete; private: static void insertInternal(Source source, Type type, UnsignedInt id, DebugOutput::Severity severity, Containers::ArrayView string); static MAGNUM_GL_LOCAL void insertImplementationNoOp(Source, Type, UnsignedInt, DebugOutput::Severity, Containers::ArrayView); #ifndef MAGNUM_TARGET_GLES2 static MAGNUM_GL_LOCAL void insertImplementationKhrDesktopES32(Source source, Type type, UnsignedInt id, DebugOutput::Severity severity, Containers::ArrayView string); #endif #ifdef MAGNUM_TARGET_GLES static MAGNUM_GL_LOCAL void insertImplementationKhrES(Source source, Type type, UnsignedInt id, DebugOutput::Severity severity, Containers::ArrayView string); #endif static MAGNUM_GL_LOCAL void insertImplementationExt(Source, Type, UnsignedInt, DebugOutput::Severity, Containers::ArrayView string); #ifndef MAGNUM_TARGET_GLES static MAGNUM_GL_LOCAL void insertImplementationGremedy(Source, Type, UnsignedInt, DebugOutput::Severity, Containers::ArrayView string); #endif }; /** @debugoperatorclassenum{DebugMessage,DebugMessage::Source} */ MAGNUM_GL_EXPORT Debug& operator<<(Debug& debug, DebugMessage::Source value); /** @debugoperatorclassenum{DebugMessage,DebugMessage::Type} */ MAGNUM_GL_EXPORT Debug& operator<<(Debug& debug, DebugMessage::Type value); /** @brief Debug group Allows marking portions of GL command stream with labels, useful for example with conjunction with various graphics debuggers, such as Apitrace or gDEBugger. @section GL-DebugGroup-usage Basic usage See @ref DebugOutput for introduction. Easiest way is to push debug group by creating instance and pop it automatically at the end of scope: @snippet MagnumGL.cpp DebugGroup-usage1 If, for some reason, you need to pop in different scope, you can call @ref push() and @ref pop() manually: @snippet MagnumGL.cpp DebugGroup-usage2 If OpenGL 4.3 / OpenGL ES 3.2 is supported or @gl_extension{KHR,debug} desktop or ES extension (covered also by @gl_extension{ANDROID,extension_pack_es31a}) is available and the default debug output callback is enabled for these kinds of messages, the group entering and leaving will be printed on standard output in the following form: @code{.shell-session} Debug output: application debug group enter (42): Scene rendering Debug output: application debug group leave (42): Scene rendering @endcode If only @gl_extension{EXT,debug_marker} is available, the group can be seen only through graphics debugger. If OpenGL 4.3 / OpenGL ES 3.2 is not supported and neither @gl_extension{KHR,debug} nor @gl_extension{EXT,debug_marker} are available, the functions are essentially a no-op. @attention To avoid accidental debug group stack overflow/underflow, you cannot call @ref push() again when the group is already pushed onto the stack, similarly for @ref pop(). So if you want to have nested debug groups, you need to create one instance for each level. @section GL-DebugGroup-volume-control Interaction with debug output volume control Besides putting hierarchical messages in debug output, the group also affects settings done by @ref DebugOutput::setEnabled(). Entering debug group inherits the settings from previously active debug group, call to @ref DebugOutput::setEnabled() will be remembered only for the time in which given group is active and leaving it will revert the setting to state set in parent debug group. No state is preserved, thus calling @ref push() after previous @ref pop() will not restore settings done when the group was active previously. @section GL-DebugGroup-performance-notes Performance notes If you ensure that you always use the @cpp const char @ce overload of @ref push() and the debug output is either not supported or turned off, the calls will not result in any allocations and thus won't have any negative performance effects. @see @ref DebugMessage @requires_gles Debug output is not available in WebGL. */ class MAGNUM_GL_EXPORT DebugGroup { friend Implementation::DebugState; public: /** * @brief Message source * * @see @ref DebugGroup(), @ref push() */ enum class Source: GLenum { /** External debugger or third-party middleware */ #ifndef MAGNUM_TARGET_GLES2 ThirdParty = GL_DEBUG_SOURCE_THIRD_PARTY, #else ThirdParty = GL_DEBUG_SOURCE_THIRD_PARTY_KHR, #endif /** The application */ #ifndef MAGNUM_TARGET_GLES2 Application = GL_DEBUG_SOURCE_APPLICATION #else Application = GL_DEBUG_SOURCE_APPLICATION_KHR #endif }; /** * @brief Max debug group stack depth * * The result is cached, repeated queries don't result in repeated * OpenGL calls. If OpenGL 4.3 / OpenGL ES 3.2 is not supported and * @gl_extension{KHR,debug} desktop or ES extension (covered also by * @gl_extension{ANDROID,extension_pack_es31a}) is not available, returns * `0`. * @see @fn_gl{Get} with @def_gl_keyword{MAX_DEBUG_GROUP_STACK_DEPTH} */ static Int maxStackDepth(); /** * @brief Default constructor * * Doesn't do anything. Call @ref push() to enter debug group. */ explicit DebugGroup(): _active{false} {} /** * @brief Constructor * * Calls @ref push(). * @see @link ~DebugGroup() @endlink, @ref pop() */ explicit DebugGroup(Source source, UnsignedInt id, const std::string& message): DebugGroup{} { push(source, id, message); } /** @overload */ template explicit DebugGroup(Source source, UnsignedInt id, const char(&message)[size]): DebugGroup{} { push(source, id, message); } /** * @brief Destructor * * If the group is active, calls @ref pop(). */ ~DebugGroup() { if(_active) pop(); } /** * @brief Push debug group onto the stack * * Expects that the group isn't already pushed on the stack. The group * entering message is put into debug output with * @ref DebugOutput::Type::PushGroup and * @ref DebugOutput::Severity::Notification. * * If OpenGL 4.3 / OpenGL ES 3.2 is not supported and neither * @gl_extension{KHR,debug} (covered also by @gl_extension{ANDROID,extension_pack_es31a}) * nor @gl_extension{EXT,debug_marker} is available, this function does * nothing. If @gl_extension{KHR,debug} is not available and only * @gl_extension{EXT,debug_marker} is available, only @p message is used * and all other parameters are ignored. * @see @ref pop(), @ref maxStackDepth(), @ref DebugOutput::maxMessageLength(), * @ref Renderer::Error::StackOverflow, @fn_gl_keyword{PushDebugGroup} * or @fn_gl_extension_keyword{PushGroupMarker,EXT,debug_marker} */ void push(Source source, UnsignedInt id, const std::string& message) { pushInternal(source, id, {message.data(), message.size()}); } /** @overload */ template void push(Source source, UnsignedInt id, const char(&message)[size]) { pushInternal(source, id, {message, size - 1}); } /** * @brief Pop debug group from the stack * * Expects that the group is currently pushed on the stack. Leaving the * group will also revert all @ref DebugOutput::setEnabled() settings * done when the group was active. See class documentation for more * information. The group leaving message is put into debug output with * @ref DebugOutput::Type::PopGroup and * @ref DebugOutput::Severity::Notification. * * If OpenGL 4.3 / OpenGL ES 3.2 is not supported and neither * @gl_extension{KHR,debug} (covered also by @gl_extension{ANDROID,extension_pack_es31a}) * nor @gl_extension{EXT,debug_marker} is available, this function does * nothing. * @see @ref push(), @ref Renderer::Error::StackUnderflow, * @fn_gl_keyword{PopDebugGroup} or * @fn_gl_extension_keyword{PopGroupMarker,EXT,debug_marker} */ void pop(); private: void pushInternal(Source source, UnsignedInt id, Containers::ArrayView message); static MAGNUM_GL_LOCAL void pushImplementationNoOp(Source source, UnsignedInt id, Containers::ArrayView message); #ifndef MAGNUM_TARGET_GLES2 static MAGNUM_GL_LOCAL void pushImplementationKhrDesktopES32(Source source, UnsignedInt id, Containers::ArrayView message); #endif #ifdef MAGNUM_TARGET_GLES static MAGNUM_GL_LOCAL void pushImplementationKhrES(Source source, UnsignedInt id, Containers::ArrayView message); #endif static MAGNUM_GL_LOCAL void pushImplementationExt(Source source, UnsignedInt id, Containers::ArrayView message); static MAGNUM_GL_LOCAL void popImplementationNoOp(); #ifndef MAGNUM_TARGET_GLES2 static MAGNUM_GL_LOCAL void popImplementationKhrDesktopES32(); #endif #ifdef MAGNUM_TARGET_GLES static MAGNUM_GL_LOCAL void popImplementationKhrES(); #endif static MAGNUM_GL_LOCAL void popImplementationExt(); bool _active; }; /** @debugoperatorclassenum{DebugGroup,DebugGroup::Source} */ MAGNUM_GL_EXPORT Debug& operator<<(Debug& debug, DebugGroup::Source value); /* Exposed for testing */ namespace Implementation { MAGNUM_GL_EXPORT void defaultDebugCallback(DebugOutput::Source source, DebugOutput::Type type, UnsignedInt id, DebugOutput::Severity severity, const std::string& string, std::ostream* output); } }} #else #error this header is not available in WebGL build #endif #endif