From c3d9f7a72b15c499a849972bdaa70b1cb62a1d22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 1 Feb 2015 23:45:54 +0100 Subject: [PATCH] Support for debug groups from KHR_debug and EXT_debug_marker. Had to rework the API a bit, the original one (everything through DebugMessage) should be still available, but marked as deprecated (and will be removed in some future release). The whole cycle of reading up on a feature, understanding the feature, understanding the bigger concept under the feature and then having understood everything so thoroughly so I can document the functionality is time consuming. --- doc/opengl-mapping.dox | 13 +- doc/opengl-support.dox | 4 +- src/Magnum/CMakeLists.txt | 5 +- src/Magnum/DebugMarker.h | 8 +- src/Magnum/DebugMessage.cpp | 241 ----- src/Magnum/DebugMessage.h | 393 -------- src/Magnum/DebugOutput.cpp | 380 ++++++++ src/Magnum/DebugOutput.h | 895 ++++++++++++++++++ src/Magnum/Implementation/DebugState.cpp | 18 +- src/Magnum/Implementation/DebugState.h | 14 +- src/Magnum/Platform/magnum-info.cpp | 6 +- src/Magnum/Renderer.h | 9 +- src/Magnum/Test/AbstractOpenGLTester.h | 6 +- src/Magnum/Test/CMakeLists.txt | 4 +- src/Magnum/Test/DebugGLTest.cpp | 144 --- src/Magnum/Test/DebugOutputGLTest.cpp | 211 +++++ ...bugMessageTest.cpp => DebugOutputTest.cpp} | 58 +- 17 files changed, 1580 insertions(+), 829 deletions(-) delete mode 100644 src/Magnum/DebugMessage.cpp delete mode 100644 src/Magnum/DebugMessage.h create mode 100644 src/Magnum/DebugOutput.cpp create mode 100644 src/Magnum/DebugOutput.h delete mode 100644 src/Magnum/Test/DebugGLTest.cpp create mode 100644 src/Magnum/Test/DebugOutputGLTest.cpp rename src/Magnum/Test/{DebugMessageTest.cpp => DebugOutputTest.cpp} (51%) diff --git a/doc/opengl-mapping.dox b/doc/opengl-mapping.dox index 77077a65f..e2cc645e2 100644 --- a/doc/opengl-mapping.dox +++ b/doc/opengl-mapping.dox @@ -110,8 +110,8 @@ OpenGL function | Matching API OpenGL function | Matching API --------------------------------------- | ------------ -@fn_gl{DebugMessageCallback} | @ref DebugMessage::setCallback() -@fn_gl{DebugMessageControl} | @ref DebugMessage::setEnabled() +@fn_gl{DebugMessageCallback} | @ref DebugOutput::setCallback() +@fn_gl{DebugMessageControl} | @ref DebugOutput::setEnabled() @fn_gl{DebugMessageInsert}, \n @fn_gl_extension2{InsertEventMarker,EXT,debug_marker}, \n @fn_gl_extension{StringMarker,GREMEDY,string_marker} | @ref DebugMessage::insert() @fn_gl{DepthFunc} | @ref Renderer::setDepthFunction() @fn_gl{DepthMask} | @ref Renderer::setDepthMask() @@ -277,7 +277,8 @@ OpenGL function | Matching API @fn_gl{ProgramBinary} | | @fn_gl{ProgramParameter} | @ref AbstractShaderProgram::setRetrievableBinary(), \n @ref AbstractShaderProgram::setSeparable() @fn_gl{ProvokingVertex} | @ref Renderer::setProvokingVertex() -@fn_gl{PushDebugGroup}, @fn_gl{PopDebugGroup} | | +@fn_gl{PushDebugGroup}, \n @fn_gl_extension2{PushGroupMarker,EXT,debug_marker} | @ref DebugGroup::push() +@fn_gl{PopDebugGroup}, \n @fn_gl_extension2{PopGroupMarker,EXT,debug_marker} | @ref DebugGroup::pop() @subsection opengl-mapping-functions-q Q @@ -410,9 +411,9 @@ OpenGL function | Matching API @def_gl{MAX_COMPUTE_WORK_GROUP_COUNT} | | @def_gl{MAX_COMPUTE_WORK_GROUP_INVOCATIONS} | @ref AbstractShaderProgram::maxComputeWorkGroupInvocations() @def_gl{MAX_COMPUTE_WORK_GROUP_SIZE} | | -@def_gl{MAX_DEBUG_LOGGED_MESSAGES} | @ref DebugMessage::maxLoggedMessages() -@def_gl{MAX_DEBUG_MESSAGE_LENGTH} | @ref DebugMessage::maxMessageLength() -@def_gl{MAX_DEBUG_GROUP_STACK_DEPTH} | | +@def_gl{MAX_DEBUG_LOGGED_MESSAGES} | @ref DebugOutput::maxLoggedMessages() +@def_gl{MAX_DEBUG_MESSAGE_LENGTH} | @ref DebugOutput::maxMessageLength() +@def_gl{MAX_DEBUG_GROUP_STACK_DEPTH} | @ref DebugGroup::maxStackDepth() @def_gl{MAX_CLIP_DISTANCES} | | @def_gl{MAX_CULL_DISTANCES} | | @def_gl{MAX_COMBINED_CLIP_AND_CULL_DISTANCES} | | diff --git a/doc/opengl-support.dox b/doc/opengl-support.dox index 6d7565098..1bf2ead5c 100644 --- a/doc/opengl-support.dox +++ b/doc/opengl-support.dox @@ -177,7 +177,7 @@ GLSL 4.30 | done @extension{ARB,clear_buffer_object} | | @extension{ARB,compute_shader} | | @extension{ARB,copy_image} | | -@extension{KHR,debug} (also in ES) | missing log retrieval, sync, pipeline, transform feedback and sampler label and debug groups +@extension{KHR,debug} (also in ES) | missing log retrieval, sync, pipeline and sampler label @extension{ARB,explicit_uniform_location} | done @extension{ARB,fragment_layer_viewport} | done (shading language only) @extension{ARB,framebuffer_no_attachments} | | @@ -266,7 +266,7 @@ Extension | Status @extension{EXT,texture_sRGB_decode} (also in ES) | done @extension{EXT,shader_integer_mix} (also in ES) | done (shading language only) @extension2{EXT,debug_label} (also in ES) | missing pipeline and sampler label -@extension2{EXT,debug_marker} (also in ES) | missing marker groups +@extension2{EXT,debug_marker} (also in ES) | done @extension{GREMEDY,string_marker} | done @subsection opengl-support-es20 OpenGL ES 2.0 diff --git a/src/Magnum/CMakeLists.txt b/src/Magnum/CMakeLists.txt index 32cdd3bb1..9d7e0bde0 100644 --- a/src/Magnum/CMakeLists.txt +++ b/src/Magnum/CMakeLists.txt @@ -39,7 +39,7 @@ set(Magnum_SRCS ColorFormat.cpp CubeMapTexture.cpp Context.cpp - DebugMessage.cpp + DebugOutput.cpp DefaultFramebuffer.cpp Framebuffer.cpp Image.cpp @@ -96,7 +96,7 @@ set(Magnum_HEADERS ColorFormat.h Context.h CubeMapTexture.h - DebugMessage.h + DebugOutput.h DefaultFramebuffer.h DimensionTraits.h Extensions.h @@ -142,6 +142,7 @@ set(Magnum_PRIVATE_HEADERS if(BUILD_DEPRECATED) set(Magnum_HEADERS ${Magnum_HEADERS} DebugMarker.h + DebugMessage.h Query.h) endif() diff --git a/src/Magnum/DebugMarker.h b/src/Magnum/DebugMarker.h index 456cfb9d1..ef47d0fc1 100644 --- a/src/Magnum/DebugMarker.h +++ b/src/Magnum/DebugMarker.h @@ -26,11 +26,11 @@ */ /** @file - * @brief Typedef @ref Magnum::DebugMarker - * @deprecated Use @ref DebugMessage.h instead. + * @brief Class @ref Magnum::DebugMarker + * @deprecated Use @ref DebugOutput.h instead. */ -#include "Magnum/DebugMessage.h" +#include "Magnum/DebugOutput.h" #ifdef MAGNUM_BUILD_DEPRECATED namespace Magnum { @@ -46,7 +46,7 @@ class CORRADE_DEPRECATED("use DebugMessage instead") MAGNUM_EXPORT DebugMarker: * @deprecated Use @ref Magnum::DebugMessage::insert() "insert()" instead. */ static CORRADE_DEPRECATED("use DebugMessage::insert() instead") void mark(const std::string& string) { - insert(Source::Application, Type::Marker, 0, Severity::Notification, string); + insert(Source::Application, Type::Marker, 0, DebugOutput::Severity::Notification, string); } }; diff --git a/src/Magnum/DebugMessage.cpp b/src/Magnum/DebugMessage.cpp deleted file mode 100644 index 642c6e362..000000000 --- a/src/Magnum/DebugMessage.cpp +++ /dev/null @@ -1,241 +0,0 @@ -/* - This file is part of Magnum. - - Copyright © 2010, 2011, 2012, 2013, 2014, 2015 - 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. -*/ - -#include "DebugMessage.h" - -#include - -#include "Magnum/Context.h" -#include "Magnum/Extensions.h" -#include "Magnum/Implementation/State.h" -#include "Magnum/Implementation/DebugState.h" - -namespace Magnum { - -namespace { - -#if !defined(CORRADE_TARGET_EMSCRIPTEN) && !defined(CORRADE_TARGET_NACL) -void -#ifdef CORRADE_TARGET_WINDOWS -APIENTRY -#endif -callbackWrapper(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam) { - Context::current()->state().debug->messageCallback(DebugMessage::Source(source), DebugMessage::Type(type), id, DebugMessage::Severity(severity), std::string(message, length), userParam); -} -#endif - -void defaultCallback(const DebugMessage::Source source, const DebugMessage::Type type, const UnsignedInt id, const DebugMessage::Severity severity, const std::string& string, const void*) { - switch(severity) { - case DebugMessage::Severity::High: - Error() << source << type << id << severity << "\n " << string; - break; - - case DebugMessage::Severity::Medium: - case DebugMessage::Severity::Low: - Warning() << source << type << id << severity << "\n " << string; - break; - - default: Debug() << source << type << id << severity << "\n " << string; - } -} - -} - -Int DebugMessage::maxLoggedMessages() { - if(!Context::current()->isExtensionSupported()) - return 0; - - GLint& value = Context::current()->state().debug->maxLoggedMessages; - - if(value == 0) { - #ifndef MAGNUM_TARGET_GLES - glGetIntegerv(GL_MAX_DEBUG_LOGGED_MESSAGES, &value); - #else - glGetIntegerv(GL_MAX_DEBUG_LOGGED_MESSAGES_KHR, &value); - #endif - } - - return value; -} - -Int DebugMessage::maxMessageLength() { - if(!Context::current()->isExtensionSupported()) - return 0; - - GLint& value = Context::current()->state().debug->maxMessageLength; - - if(value == 0) { - #ifndef MAGNUM_TARGET_GLES - glGetIntegerv(GL_MAX_DEBUG_MESSAGE_LENGTH, &value); - #else - glGetIntegerv(GL_MAX_DEBUG_MESSAGE_LENGTH_KHR, &value); - #endif - } - - return value; -} - -void DebugMessage::setCallback(const Callback callback, const void* userParam) { - Context::current()->state().debug->messageCallbackImplementation(callback, userParam); -} - -void DebugMessage::setDefaultCallback() { - setCallback(defaultCallback, nullptr); -} - -void DebugMessage::insertInternal(const Source source, const Type type, const UnsignedInt id, const Severity severity, const Containers::ArrayReference string) { - Context::current()->state().debug->messageInsertImplementation(source, type, id, severity, string); -} - -void DebugMessage::insertImplementationNoOp(Source, Type, UnsignedInt, Severity, const Containers::ArrayReference) {} - -void DebugMessage::insertImplementationKhr(const Source source, const Type type, const UnsignedInt id, const Severity severity, const Containers::ArrayReference string) { - #ifndef MAGNUM_TARGET_GLES - glDebugMessageInsert(GLenum(source), GLenum(type), id, GLenum(severity), string.size(), string.data()); - #elif !defined(CORRADE_TARGET_EMSCRIPTEN) && !defined(CORRADE_TARGET_NACL) - glDebugMessageInsertKHR(GLenum(source), GLenum(type), id, GLenum(severity), string.size(), string.data()); - #else - static_cast(source); - static_cast(type); - static_cast(id); - static_cast(severity); - static_cast(string); - CORRADE_ASSERT_UNREACHABLE(); - #endif -} - -void DebugMessage::insertImplementationExt(Source, Type, UnsignedInt, Severity, const Containers::ArrayReference string) { - #if !defined(CORRADE_TARGET_EMSCRIPTEN) && !defined(CORRADE_TARGET_NACL) - glInsertEventMarkerEXT(string.size(), string.data()); - #else - static_cast(string); - CORRADE_ASSERT_UNREACHABLE(); - #endif -} - -#ifndef MAGNUM_TARGET_GLES -void DebugMessage::insertImplementationGremedy(Source, Type, UnsignedInt, Severity, const Containers::ArrayReference string) { - glStringMarkerGREMEDY(string.size(), string.data()); -} -#endif - -void DebugMessage::setEnabledInternal(const GLenum source, const GLenum type, const GLenum severity, const std::initializer_list ids, const bool enabled) { - Context::current()->state().debug->messageControlImplementation(source, type, severity, ids, enabled); -} - -void DebugMessage::controlImplementationNoOp(GLenum, GLenum, GLenum, std::initializer_list, bool) {} - -void DebugMessage::controlImplementationKhr(const GLenum source, const GLenum type, const GLenum severity, const std::initializer_list ids, const bool enabled) { - #ifndef MAGNUM_TARGET_GLES - glDebugMessageControl(source, type, severity, ids.size(), ids.begin(), enabled); - #elif !defined(CORRADE_TARGET_EMSCRIPTEN) && !defined(CORRADE_TARGET_NACL) - glDebugMessageControlKHR(source, type, severity, ids.size(), ids.begin(), enabled); - #else - static_cast(source); - static_cast(type); - static_cast(severity); - static_cast(ids); - static_cast(enabled); - CORRADE_ASSERT_UNREACHABLE(); - #endif -} - -void DebugMessage::callbackImplementationNoOp(Callback, const void*) {} - -void DebugMessage::callbackImplementationKhr(const Callback callback, const void* userParam) { - /* Replace the callback */ - const Callback original = Context::current()->state().debug->messageCallback; - Context::current()->state().debug->messageCallback = callback; - - /* Adding callback */ - if(!original && callback) { - #ifndef MAGNUM_TARGET_GLES - glDebugMessageCallback(callbackWrapper, userParam); - #elif !defined(CORRADE_TARGET_EMSCRIPTEN) && !defined(CORRADE_TARGET_NACL) - glDebugMessageCallbackKHR(callbackWrapper, userParam); - #else - static_cast(userParam); - CORRADE_ASSERT_UNREACHABLE(); - #endif - - /* Deleting callback */ - } else if(original && !callback) { - #ifndef MAGNUM_TARGET_GLES - glDebugMessageCallback(nullptr, nullptr); - #elif !defined(CORRADE_TARGET_EMSCRIPTEN) && !defined(CORRADE_TARGET_NACL) - glDebugMessageCallbackKHR(nullptr, nullptr); - #else - CORRADE_ASSERT_UNREACHABLE(); - #endif - } -} - -#ifndef DOXYGEN_GENERATING_OUTPUT -Debug operator<<(Debug debug, const DebugMessage::Source value) { - switch(value) { - #define _c(value) case DebugMessage::Source::value: return debug << "DebugMessage::Source::" #value; - _c(Api) - _c(WindowSystem) - _c(ShaderCompiler) - _c(ThirdParty) - _c(Application) - _c(Other) - #undef _c - } - - return debug << "DebugMessage::Source::(invalid)"; -} - -Debug operator<<(Debug debug, const DebugMessage::Type value) { - switch(value) { - #define _c(value) case DebugMessage::Type::value: return debug << "DebugMessage::Type::" #value; - _c(Error) - _c(DeprecatedBehavior) - _c(UndefinedBehavior) - _c(Portability) - _c(Performance) - _c(Other) - _c(Marker) - #undef _c - } - - return debug << "DebugMessage::Type::(invalid)"; -} - -Debug operator<<(Debug debug, const DebugMessage::Severity value) { - switch(value) { - #define _c(value) case DebugMessage::Severity::value: return debug << "DebugMessage::Severity::" #value; - _c(High) - _c(Medium) - _c(Low) - _c(Notification) - #undef _c - } - - return debug << "DebugMessage::Severity::(invalid)"; -} -#endif - -} diff --git a/src/Magnum/DebugMessage.h b/src/Magnum/DebugMessage.h deleted file mode 100644 index 8f6d8aa43..000000000 --- a/src/Magnum/DebugMessage.h +++ /dev/null @@ -1,393 +0,0 @@ -#ifndef Magnum_DebugMessage_h -#define Magnum_DebugMessage_h -/* - This file is part of Magnum. - - Copyright © 2010, 2011, 2012, 2013, 2014, 2015 - 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. -*/ - -/** @file - * @brief Class @ref Magnum::DebugMessage - */ - -#include -#include - -#include "Magnum/Magnum.h" -#include "Magnum/OpenGL.h" -#include "Magnum/visibility.h" - -namespace Magnum { - -namespace Implementation { struct DebugState; } - -/** -@brief Debug message - -Allows retrieving and inserting debug messages from and to OpenGL command -stream, for example with conjunction with various debuggers, such as Apitrace -or gDEBugger. - -## Basic usage - -To retrieve debug messages from either the GL or your application you need to -have OpenGL 4.3 or @extension{KHR,debug} desktop/ES extension. You need to -enable @ref Renderer::Feature::DebugOutput and possibly also -@ref Renderer::Feature::DebugOutputSynchronous. Then set up message callback -using @ref setCallback() or use the default one provided in -@ref setDefaultCallback(): - -@code -Renderer::enable(Renderer::Feature::DebugOutput); -Renderer::enable(Renderer::Feature::DebugOutputSynchronous); - -DebugMessage::setDefaultCallback(); -DebugMessage::insert(DebugMessage::Source::Application, DebugMessage::Type::Marker, - 1337, DebugMessage::Severity::Notification, "Hello from OpenGL command stream!"); -@endcode - -With default callback the messages will be printed on standard output: - - DebugMessage::Source::Application DebugMessage::Type::Marker -1 DebugMessage::Severity::Notification - Hello from OpenGL command stream! -*/ -class MAGNUM_EXPORT DebugMessage { - friend Implementation::DebugState; - - public: - /** - * @brief Message source - * - * @see @ref insert(), @ref setCallback() - */ - enum class Source: GLenum { - /** OpenGL */ - #ifndef MAGNUM_TARGET_GLES - Api = GL_DEBUG_SOURCE_API, - #else - Api = GL_DEBUG_SOURCE_API_KHR, - #endif - - /** Window system (GLX, WGL) */ - #ifndef MAGNUM_TARGET_GLES - WindowSystem = GL_DEBUG_SOURCE_WINDOW_SYSTEM, - #else - WindowSystem = GL_DEBUG_SOURCE_WINDOW_SYSTEM_KHR, - #endif - - /** Shader compiler */ - #ifndef MAGNUM_TARGET_GLES - ShaderCompiler = GL_DEBUG_SOURCE_SHADER_COMPILER, - #else - ShaderCompiler = GL_DEBUG_SOURCE_SHADER_COMPILER_KHR, - #endif - - /** External debugger or third-party middleware */ - #ifndef MAGNUM_TARGET_GLES - ThirdParty = GL_DEBUG_SOURCE_THIRD_PARTY, - #else - ThirdParty = GL_DEBUG_SOURCE_THIRD_PARTY_KHR, - #endif - - /** The application */ - #ifndef MAGNUM_TARGET_GLES - Application = GL_DEBUG_SOURCE_APPLICATION, - #else - Application = GL_DEBUG_SOURCE_APPLICATION_KHR, - #endif - - /** Any other source */ - #ifndef MAGNUM_TARGET_GLES - Other = GL_DEBUG_SOURCE_OTHER - #else - Other = GL_DEBUG_SOURCE_OTHER_KHR - #endif - }; - - /** - * @brief Message type - * - * @see @ref insert(), @ref setCallback() - */ - enum class Type: GLenum { - /** OpenGL error */ - #ifndef MAGNUM_TARGET_GLES - Error = GL_DEBUG_TYPE_ERROR, - #else - Error = GL_DEBUG_TYPE_ERROR_KHR, - #endif - - /** Behavior that has been marked for deprecation */ - #ifndef MAGNUM_TARGET_GLES - 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_GLES - 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_GLES - Portability = GL_DEBUG_TYPE_PORTABILITY, - #else - Portability = GL_DEBUG_TYPE_PORTABILITY_KHR, - #endif - - /** Implementation-dependent performance warning */ - #ifndef MAGNUM_TARGET_GLES - Performance = GL_DEBUG_TYPE_PERFORMANCE, - #else - Performance = GL_DEBUG_TYPE_PERFORMANCE_KHR, - #endif - - /** Any other type */ - #ifndef MAGNUM_TARGET_GLES - Other = GL_DEBUG_TYPE_OTHER, - #else - Other = GL_DEBUG_TYPE_OTHER_KHR, - #endif - - /** Annotation of the command stream */ - #ifndef MAGNUM_TARGET_GLES - Marker = GL_DEBUG_TYPE_MARKER - #else - Marker = GL_DEBUG_TYPE_MARKER_KHR - #endif - }; - - /** - * @brief Message severity - * - * @see @ref insert(), @ref setCallback() - */ - enum class Severity: GLenum { - /** - * Any OpenGL error, dangerous undefined behavior, shader - * compilation errors. - */ - #ifndef MAGNUM_TARGET_GLES - 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_GLES - Medium = GL_DEBUG_SEVERITY_MEDIUM, - #else - Medium = GL_DEBUG_SEVERITY_MEDIUM_KHR, - #endif - - /** Minor performance warnings, trivial undefined behavior. */ - #ifndef MAGNUM_TARGET_GLES - Low = GL_DEBUG_SEVERITY_LOW, - #else - Low = GL_DEBUG_SEVERITY_LOW_KHR, - #endif - - /** Any message other than error or performance warning. */ - #ifndef MAGNUM_TARGET_GLES - 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 is not supported and @extension{KHR,debug} - * desktop or ES extension is not available, returns `0`. - * @see @fn_gl{Get} with @def_gl{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 is not supported and @extension{KHR,debug} - * desktop or ES extension is not available, returns `0`. - * @see @fn_gl{Get} with @def_gl{MAX_DEBUG_MESSAGE_LENGTH} - */ - static Int maxMessageLength(); - - /** - * @brief Insert message - * @param source Message source. Allowed values are - * @ref Source::ThirdParty or @ref Source::Application. - * @param type Message type - * @param id Message-specific ID - * @param severity Message severity - * @param string The actual message - * - * If OpenGL 4.3 is not supported and none of @extension{KHR,debug}, - * @extension2{EXT,debug_marker} or @extension{GREMEDY,string_marker} - * is available, this function does nothing. - * - * If @extension{KHR,debug} is not available and only @extension2{EXT,debug_marker} - * or @extension{GREMEDY,string_marker} are available, only @p string - * is used and all other parameters are ignored. The call is then - * equivalent to the following: - * @code - * DebugMessage::insert(DebugMessage::Source::Application, - * DebugMessage::Type::Marker, 0, DebugMessage::Severity::Notification, string); - * @endcode - * - * @see @ref maxMessageLength(), @fn_gl{DebugMessageInsert}, - * @fn_gl_extension2{InsertEventMarker,EXT,debug_marker} or - * @fn_gl_extension{StringMarker,GREMEDY,string_marker} - */ - static void insert(Source source, Type type, UnsignedInt id, 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, Severity severity, const char(&string)[size]) { - insertInternal(source, type, id, severity, {string, size - 1}); - } - - /** - * @brief Enable or disable particular message type - * - * @see @ref Renderer::Feature::DebugOutput, @fn_gl{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 is not - * supported and @extension{KHR,debug} is not available, this function - * does nothing. - * @see @ref setDefaultCallback(), - * @ref Renderer::Feature::DebugOutputSynchronous, - * @fn_gl{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 either @ref Corrade::Utility::Error "Error", @ref Corrade::Utility::Warning "Warning" - * or @ref Corrade::Utility::Debug "Debug" in the following format: - * @code - * DebugMessage::insert(DebugMessage::Source::Application, - * DebugMessage::Type::Marker, 1337, DebugMessage::Severity::Notification, "Hello from OpenGL command stream!"); - * @endcode - * - * > DebugMessage::Source::Application DebugMessage::Type::Marker -1 DebugMessage::Severity::Notification\n - * >     Hello from OpenGL command stream! - */ - static void setDefaultCallback(); - - DebugMessage() = delete; - - private: - static void insertInternal(Source source, Type type, UnsignedInt id, Severity severity, Containers::ArrayReference string); - static MAGNUM_LOCAL void insertImplementationNoOp(Source, Type, UnsignedInt, Severity, Containers::ArrayReference); - static MAGNUM_LOCAL void insertImplementationKhr(Source source, Type type, UnsignedInt id, Severity severity, Containers::ArrayReference string); - static MAGNUM_LOCAL void insertImplementationExt(Source, Type, UnsignedInt, Severity, Containers::ArrayReference string); - #ifndef MAGNUM_TARGET_GLES - static MAGNUM_LOCAL void insertImplementationGremedy(Source, Type, UnsignedInt, Severity, Containers::ArrayReference string); - #endif - - static void setEnabledInternal(GLenum source, GLenum type, GLenum severity, std::initializer_list ids, bool enabled); - static MAGNUM_LOCAL void controlImplementationNoOp(GLenum, GLenum, GLenum, std::initializer_list, bool); - static MAGNUM_LOCAL void controlImplementationKhr(GLenum source, GLenum type, GLenum severity, std::initializer_list ids, bool enabled); - - static MAGNUM_LOCAL void callbackImplementationNoOp(Callback, const void*); - static MAGNUM_LOCAL void callbackImplementationKhr(Callback callback, const void* userParam); -}; - -/** @debugoperatorclassenum{Magnum::DebugMessage,Magnum::DebugMessage::Source} */ -Debug MAGNUM_EXPORT operator<<(Debug debug, DebugMessage::Source value); - -/** @debugoperatorclassenum{Magnum::DebugMessage,Magnum::DebugMessage::Type} */ -Debug MAGNUM_EXPORT operator<<(Debug debug, DebugMessage::Type value); - -/** @debugoperatorclassenum{Magnum::DebugMessage,Magnum::DebugMessage::Severity} */ -Debug MAGNUM_EXPORT operator<<(Debug debug, DebugMessage::Severity value); - -} - -#endif diff --git a/src/Magnum/DebugOutput.cpp b/src/Magnum/DebugOutput.cpp new file mode 100644 index 000000000..d578f5a13 --- /dev/null +++ b/src/Magnum/DebugOutput.cpp @@ -0,0 +1,380 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015 + 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. +*/ + +#include "DebugOutput.h" + +#include + +#include "Magnum/Context.h" +#include "Magnum/Extensions.h" +#include "Magnum/Implementation/State.h" +#include "Magnum/Implementation/DebugState.h" + +namespace Magnum { + +namespace { + +#if !defined(CORRADE_TARGET_EMSCRIPTEN) && !defined(CORRADE_TARGET_NACL) +void +#ifdef CORRADE_TARGET_WINDOWS +APIENTRY +#endif +callbackWrapper(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam) { + Context::current()->state().debug->messageCallback(DebugOutput::Source(source), DebugOutput::Type(type), id, DebugOutput::Severity(severity), std::string{message, std::size_t(length)}, userParam); +} +#endif + +void defaultCallback(const DebugOutput::Source source, const DebugOutput::Type type, const UnsignedInt id, const DebugOutput::Severity severity, const std::string& string, const void*) { + switch(severity) { + case DebugOutput::Severity::High: + Error() << source << type << id << severity << "\n " << string; + break; + + case DebugOutput::Severity::Medium: + case DebugOutput::Severity::Low: + Warning() << source << type << id << severity << "\n " << string; + break; + + default: Debug() << source << type << id << severity << "\n " << string; + } +} + +} + +Int DebugOutput::maxLoggedMessages() { + if(!Context::current()->isExtensionSupported()) + return 0; + + GLint& value = Context::current()->state().debug->maxLoggedMessages; + + if(value == 0) { + #ifndef MAGNUM_TARGET_GLES + glGetIntegerv(GL_MAX_DEBUG_LOGGED_MESSAGES, &value); + #else + glGetIntegerv(GL_MAX_DEBUG_LOGGED_MESSAGES_KHR, &value); + #endif + } + + return value; +} + +Int DebugOutput::maxMessageLength() { + if(!Context::current()->isExtensionSupported()) + return 0; + + GLint& value = Context::current()->state().debug->maxMessageLength; + + if(value == 0) { + #ifndef MAGNUM_TARGET_GLES + glGetIntegerv(GL_MAX_DEBUG_MESSAGE_LENGTH, &value); + #else + glGetIntegerv(GL_MAX_DEBUG_MESSAGE_LENGTH_KHR, &value); + #endif + } + + return value; +} + +void DebugOutput::setCallback(const Callback callback, const void* userParam) { + Context::current()->state().debug->callbackImplementation(callback, userParam); +} + +void DebugOutput::setDefaultCallback() { + setCallback(defaultCallback, nullptr); +} + +void DebugOutput::setEnabledInternal(const GLenum source, const GLenum type, const GLenum severity, const std::initializer_list ids, const bool enabled) { + Context::current()->state().debug->controlImplementation(source, type, severity, ids, enabled); +} + +void DebugOutput::controlImplementationNoOp(GLenum, GLenum, GLenum, std::initializer_list, bool) {} + +void DebugOutput::controlImplementationKhr(const GLenum source, const GLenum type, const GLenum severity, const std::initializer_list ids, const bool enabled) { + #if !defined(CORRADE_TARGET_EMSCRIPTEN) && !defined(CORRADE_TARGET_NACL) + #ifndef MAGNUM_TARGET_GLES + glDebugMessageControl + #else + glDebugMessageControlKHR + #endif + (source, type, severity, ids.size(), ids.begin(), enabled); + #else + static_cast(source); + static_cast(type); + static_cast(severity); + static_cast(ids); + static_cast(enabled); + CORRADE_ASSERT_UNREACHABLE(); + #endif +} + +void DebugOutput::callbackImplementationNoOp(Callback, const void*) {} + +void DebugOutput::callbackImplementationKhr(const Callback callback, const void* userParam) { + /* Replace the callback */ + const Callback original = Context::current()->state().debug->messageCallback; + Context::current()->state().debug->messageCallback = callback; + + /* Adding callback */ + if(!original && callback) { + #if !defined(CORRADE_TARGET_EMSCRIPTEN) && !defined(CORRADE_TARGET_NACL) + #ifndef MAGNUM_TARGET_GLES + glDebugMessageCallback + #else + glDebugMessageCallbackKHR + #endif + (callbackWrapper, userParam); + #else + static_cast(userParam); + CORRADE_ASSERT_UNREACHABLE(); + #endif + + /* Deleting callback */ + } else if(original && !callback) { + #if !defined(CORRADE_TARGET_EMSCRIPTEN) && !defined(CORRADE_TARGET_NACL) + #ifndef MAGNUM_TARGET_GLES + glDebugMessageCallback + #else + glDebugMessageCallbackKHR + #endif + (nullptr, nullptr); + #else + CORRADE_ASSERT_UNREACHABLE(); + #endif + } +} + +#ifndef DOXYGEN_GENERATING_OUTPUT +Debug operator<<(Debug debug, const DebugOutput::Source value) { + switch(value) { + #define _c(value) case DebugOutput::Source::value: return debug << "DebugOutput::Source::" #value; + _c(Api) + _c(WindowSystem) + _c(ShaderCompiler) + _c(ThirdParty) + _c(Application) + _c(Other) + #undef _c + } + + return debug << "DebugOutput::Source::(invalid)"; +} + +Debug operator<<(Debug debug, const DebugOutput::Type value) { + switch(value) { + #define _c(value) case DebugOutput::Type::value: return debug << "DebugOutput::Type::" #value; + _c(Error) + _c(DeprecatedBehavior) + _c(UndefinedBehavior) + _c(Portability) + _c(Performance) + _c(Marker) + _c(PushGroup) + _c(PopGroup) + _c(Other) + #undef _c + } + + return debug << "DebugOutput::Type::(invalid)"; +} + +Debug operator<<(Debug debug, const DebugOutput::Severity value) { + switch(value) { + #define _c(value) case DebugOutput::Severity::value: return debug << "DebugOutput::Severity::" #value; + _c(High) + _c(Medium) + _c(Low) + _c(Notification) + #undef _c + } + + return debug << "DebugOutput::Severity::(invalid)"; +} +#endif + +void DebugMessage::insertInternal(const Source source, const Type type, const UnsignedInt id, const DebugOutput::Severity severity, const Containers::ArrayReference string) { + Context::current()->state().debug->messageInsertImplementation(source, type, id, severity, string); +} + +void DebugMessage::insertImplementationNoOp(Source, Type, UnsignedInt, DebugOutput::Severity, const Containers::ArrayReference) {} + +void DebugMessage::insertImplementationKhr(const Source source, const Type type, const UnsignedInt id, const DebugOutput::Severity severity, const Containers::ArrayReference string) { + #if !defined(CORRADE_TARGET_EMSCRIPTEN) && !defined(CORRADE_TARGET_NACL) + #ifndef MAGNUM_TARGET_GLES + glDebugMessageInsert + #else + glDebugMessageInsertKHR + #endif + (GLenum(source), GLenum(type), id, GLenum(severity), string.size(), string.data()); + #else + static_cast(source); + static_cast(type); + static_cast(id); + static_cast(severity); + static_cast(string); + CORRADE_ASSERT_UNREACHABLE(); + #endif +} + +void DebugMessage::insertImplementationExt(Source, Type, UnsignedInt, DebugOutput::Severity, const Containers::ArrayReference string) { + #if !defined(CORRADE_TARGET_EMSCRIPTEN) && !defined(CORRADE_TARGET_NACL) + glInsertEventMarkerEXT(string.size(), string.data()); + #else + static_cast(string); + CORRADE_ASSERT_UNREACHABLE(); + #endif +} + +#ifndef MAGNUM_TARGET_GLES +void DebugMessage::insertImplementationGremedy(Source, Type, UnsignedInt, DebugOutput::Severity, const Containers::ArrayReference string) { + glStringMarkerGREMEDY(string.size(), string.data()); +} +#endif + +#ifndef DOXYGEN_GENERATING_OUTPUT +Debug operator<<(Debug debug, const DebugMessage::Source value) { + switch(value) { + #define _c(value) case DebugMessage::Source::value: return debug << "DebugMessage::Source::" #value; + _c(ThirdParty) + _c(Application) + #undef _c + #ifdef MAGNUM_BUILD_DEPRECATED + case DebugMessage::Source::Api: + case DebugMessage::Source::WindowSystem: + case DebugMessage::Source::ShaderCompiler: + case DebugMessage::Source::Other: + return debug << DebugOutput::Source(value); + #endif + } + + return debug << "DebugMessage::Source::(invalid)"; +} + +Debug operator<<(Debug debug, const DebugMessage::Type value) { + switch(value) { + #define _c(value) case DebugMessage::Type::value: return debug << "DebugMessage::Type::" #value; + _c(Error) + _c(DeprecatedBehavior) + _c(UndefinedBehavior) + _c(Portability) + _c(Performance) + _c(Other) + _c(Marker) + #undef _c + } + + return debug << "DebugMessage::Type::(invalid)"; +} +#endif + +Int DebugGroup::maxStackDepth() { + if(!Context::current()->isExtensionSupported()) + return 0; + + GLint& value = Context::current()->state().debug->maxStackDepth; + + if(value == 0) { + #ifndef MAGNUM_TARGET_GLES + glGetIntegerv(GL_MAX_DEBUG_GROUP_STACK_DEPTH, &value); + #else + glGetIntegerv(GL_MAX_DEBUG_GROUP_STACK_DEPTH_KHR, &value); + #endif + } + + return value; +} + +void DebugGroup::pushInternal(const Source source, const UnsignedInt id, const Containers::ArrayReference message) { + CORRADE_ASSERT(!_active, "DebugGroup::push(): group is already active", ); + Context::current()->state().debug->pushGroupImplementation(source, id, message); + _active = true; +} + +void DebugGroup::pop() { + CORRADE_ASSERT(_active, "DebugGroup::pop(): group is not active", ); + Context::current()->state().debug->popGroupImplementation(); + _active = false; +} + +void DebugGroup::pushImplementationNoOp(Source, UnsignedInt, Containers::ArrayReference) {} + +void DebugGroup::pushImplementationKhr(const Source source, const UnsignedInt id, const Containers::ArrayReference message) { + #if !defined(CORRADE_TARGET_EMSCRIPTEN) && !defined(CORRADE_TARGET_NACL) + #ifndef MAGNUM_TARGET_GLES + glPushDebugGroup + #else + glPushDebugGroupKHR + #endif + (GLenum(source), id, message.size(), message.data()); + #else + static_cast(source); + static_cast(id); + static_cast(message); + CORRADE_ASSERT_UNREACHABLE(); + #endif +} + +void DebugGroup::pushImplementationExt(Source, UnsignedInt, const Containers::ArrayReference message) { + #if !defined(CORRADE_TARGET_EMSCRIPTEN) && !defined(CORRADE_TARGET_NACL) + glPushGroupMarkerEXT(message.size(), message.data()); + #else + static_cast(message); + CORRADE_ASSERT_UNREACHABLE(); + #endif +} + +void DebugGroup::popImplementationNoOp() {} + +void DebugGroup::popImplementationKhr() { + #ifndef MAGNUM_TARGET_GLES + glPopDebugGroup(); + #elif !defined(CORRADE_TARGET_EMSCRIPTEN) && !defined(CORRADE_TARGET_NACL) + glPopDebugGroupKHR(); + #else + CORRADE_ASSERT_UNREACHABLE(); + #endif +} + +void DebugGroup::popImplementationExt() { + #if !defined(CORRADE_TARGET_EMSCRIPTEN) && !defined(CORRADE_TARGET_NACL) + glPopGroupMarkerEXT(); + #else + CORRADE_ASSERT_UNREACHABLE(); + #endif +} + +#ifndef DOXYGEN_GENERATING_OUTPUT +Debug operator<<(Debug debug, const DebugGroup::Source value) { + switch(value) { + #define _c(value) case DebugGroup::Source::value: return debug << "DebugGroup::Source::" #value; + _c(ThirdParty) + _c(Application) + #undef _c + } + + return debug << "DebugGroup::Source::(invalid)"; +} +#endif + +} diff --git a/src/Magnum/DebugOutput.h b/src/Magnum/DebugOutput.h new file mode 100644 index 000000000..ee0f53b0b --- /dev/null +++ b/src/Magnum/DebugOutput.h @@ -0,0 +1,895 @@ +#ifndef Magnum_DebugOutput_h +#define Magnum_DebugOutput_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015 + 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. +*/ + +/** @file + * @brief Class @ref Magnum::DebugOutput, @ref Magnum::DebugMessage, @ref Magnum::DebugGroup + */ + +#include +#include + +#include "Magnum/Magnum.h" +#include "Magnum/OpenGL.h" +#include "Magnum/visibility.h" + +namespace Magnum { + +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. + +## Basic usage + +Support for debug output is provided by OpenGL 4.3 or @extension{KHR,debug} +(desktop/ES extension). Subset of the functionality is provided also by +@extension2{EXT,debug_marker} (desktop/ES extensions) or +@extension{GREMEDY,string_marker} (desktop only extension). + +With OpenGL 4.3 or @extension{KHR,debug} desktop/ES extension, the debug output +needs to be enabled first. It can be enabled globally using +@ref Platform::Sdl2Application::Configuration::Flag::Debug "Platform::*Application::Configuration::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 @link DebugMessage @endlink: + +@code +Renderer::enable(Renderer::Feature::DebugOutput); +Renderer::enable(Renderer::Feature::DebugOutputSynchronous); +DebugOutput::setDefaultCallback(); + +// Disable rather spammy "Buffer detailed info" debug messages on NVidia drivers +DebugOutput::setEnabled(DebugOutput::Source::Api, DebugOutput::Type::Other, {131185}, false); + +{ + DebugGroup group{DebugGroup::Source::Application, 42, "Scene rendering"}; + + DebugMessage::insert(DebugMessage::Source::Application, DebugMessage::Type::Marker, + 1337, DebugOutput::Severity::Notification, "Rendering transparent mesh"); + + Renderer::enable(Renderer::Feature::Blending); + mesh.draw(shader); + Renderer::disable(Renderer::Feature::Blending); + + // ... +} +@endcode + +With default callback the group entering/leaving and the inserted message (and +possibly also other messages) will be printed on standard output: + +> DebugOutput::Source::Application DebugOutput::Type::PushGroup 42 DebugOutput::Severity::Notification\n +>     Scene rendering\n +> DebugOutput::Source::Application DebugOutput::Type::Marker 1337 DebugOutput::Severity::Notification\n +>     Rendering transparent mesh\n +> ...\n +> DebugOutput::Source::Application DebugOutput::Type::PopGroup 42 DebugOutput::Severity::Notification\n +>     Scene rendering + +If only @extension2{EXT,debug_marker} or @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 @extension{KHR,debug} nor +@extension2{EXT,debug_marker} nor @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. +*/ +class MAGNUM_EXPORT DebugOutput { + friend Implementation::DebugState; + + public: + /** + * @brief Message source + * + * @see @ref setEnabled() + */ + enum class Source: GLenum { + /** OpenGL */ + #ifndef MAGNUM_TARGET_GLES + Api = GL_DEBUG_SOURCE_API, + #else + Api = GL_DEBUG_SOURCE_API_KHR, + #endif + + /** Window system (GLX, WGL) */ + #ifndef MAGNUM_TARGET_GLES + WindowSystem = GL_DEBUG_SOURCE_WINDOW_SYSTEM, + #else + WindowSystem = GL_DEBUG_SOURCE_WINDOW_SYSTEM_KHR, + #endif + + /** Shader compiler */ + #ifndef MAGNUM_TARGET_GLES + ShaderCompiler = GL_DEBUG_SOURCE_SHADER_COMPILER, + #else + ShaderCompiler = GL_DEBUG_SOURCE_SHADER_COMPILER_KHR, + #endif + + /** External debugger or third-party middleware */ + #ifndef MAGNUM_TARGET_GLES + ThirdParty = GL_DEBUG_SOURCE_THIRD_PARTY, + #else + ThirdParty = GL_DEBUG_SOURCE_THIRD_PARTY_KHR, + #endif + + /** The application */ + #ifndef MAGNUM_TARGET_GLES + Application = GL_DEBUG_SOURCE_APPLICATION, + #else + Application = GL_DEBUG_SOURCE_APPLICATION_KHR, + #endif + + /** Any other source */ + #ifndef MAGNUM_TARGET_GLES + Other = GL_DEBUG_SOURCE_OTHER + #else + Other = GL_DEBUG_SOURCE_OTHER_KHR + #endif + }; + + /** + * @brief Message type + * + * @see @ref setEnabled() + */ + enum class Type: GLenum { + /** OpenGL error */ + #ifndef MAGNUM_TARGET_GLES + Error = GL_DEBUG_TYPE_ERROR, + #else + Error = GL_DEBUG_TYPE_ERROR_KHR, + #endif + + /** Behavior that has been marked for deprecation */ + #ifndef MAGNUM_TARGET_GLES + 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_GLES + 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_GLES + Portability = GL_DEBUG_TYPE_PORTABILITY, + #else + Portability = GL_DEBUG_TYPE_PORTABILITY_KHR, + #endif + + /** Implementation-dependent performance warning */ + #ifndef MAGNUM_TARGET_GLES + Performance = GL_DEBUG_TYPE_PERFORMANCE, + #else + Performance = GL_DEBUG_TYPE_PERFORMANCE_KHR, + #endif + + /** Annotation of the command stream */ + #ifndef MAGNUM_TARGET_GLES + Marker = GL_DEBUG_TYPE_MARKER, + #else + Marker = GL_DEBUG_TYPE_MARKER_KHR, + #endif + + /** Entering a debug group */ + #ifndef MAGNUM_TARGET_GLES + PushGroup = GL_DEBUG_TYPE_PUSH_GROUP, + #else + PushGroup = GL_DEBUG_TYPE_PUSH_GROUP_KHR, + #endif + + /** Leaving a debug group */ + #ifndef MAGNUM_TARGET_GLES + PopGroup = GL_DEBUG_TYPE_POP_GROUP, + #else + PopGroup = GL_DEBUG_TYPE_POP_GROUP_KHR, + #endif + + /** Any other type */ + #ifndef MAGNUM_TARGET_GLES + Other = GL_DEBUG_TYPE_OTHER, + #else + Other = GL_DEBUG_TYPE_OTHER_KHR, + #endif + }; + + /** + * @brief Message severity + * + * @see @ref setEnabled() + */ + enum class Severity: GLenum { + /** + * Any OpenGL error, dangerous undefined behavior, shader + * compilation errors. + */ + #ifndef MAGNUM_TARGET_GLES + 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_GLES + Medium = GL_DEBUG_SEVERITY_MEDIUM, + #else + Medium = GL_DEBUG_SEVERITY_MEDIUM_KHR, + #endif + + /** Minor performance warnings, trivial undefined behavior. */ + #ifndef MAGNUM_TARGET_GLES + Low = GL_DEBUG_SEVERITY_LOW, + #else + Low = GL_DEBUG_SEVERITY_LOW_KHR, + #endif + + /** Any message other than error or performance warning. */ + #ifndef MAGNUM_TARGET_GLES + 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 is not supported and @extension{KHR,debug} + * desktop or ES extension is not available, returns `0`. + * @see @fn_gl{Get} with @def_gl{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 is not supported and @extension{KHR,debug} + * desktop or ES extension is not available, returns `0`. + * @see @fn_gl{Get} with @def_gl{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 is not supported and @extension{KHR,debug} desktop or + * ES extension is not available, this function does nothing. + * @see @ref Renderer::Feature::DebugOutput, @fn_gl{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 is not + * supported and @extension{KHR,debug} desktop or ES extension is not + * available, this function does nothing. + * @see @ref setDefaultCallback(), + * @ref Renderer::Feature::DebugOutputSynchronous, + * @fn_gl{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 either @ref Corrade::Utility::Error "Error", @ref Corrade::Utility::Warning "Warning" + * or @ref Corrade::Utility::Debug "Debug" in the following format: + * @code + * DebugMessage::insert(DebugMessage::Source::Application, + * DebugMessage::Type::Marker, 1337, DebugOutput::Severity::Notification, "Hello from OpenGL command stream!"); + * @endcode + * + * > DebugOutput::Source::Application DebugOutput::Type::Marker 1337 DebugOutput::Severity::Notification\n + * >     Hello from OpenGL command stream! + */ + 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_LOCAL void controlImplementationNoOp(GLenum, GLenum, GLenum, std::initializer_list, bool); + static MAGNUM_LOCAL void controlImplementationKhr(GLenum source, GLenum type, GLenum severity, std::initializer_list ids, bool enabled); + + static MAGNUM_LOCAL void callbackImplementationNoOp(Callback, const void*); + static MAGNUM_LOCAL void callbackImplementationKhr(Callback callback, const void* userParam); +}; + +/** @debugoperatorclassenum{Magnum::DebugOutput,Magnum::DebugOutput::Source} */ +Debug MAGNUM_EXPORT operator<<(Debug debug, DebugOutput::Source value); + +/** @debugoperatorclassenum{Magnum::DebugOutput,Magnum::DebugOutput::Type} */ +Debug MAGNUM_EXPORT operator<<(Debug debug, DebugOutput::Type value); + +/** @debugoperatorclassenum{Magnum::DebugOutput,Magnum::DebugOutput::Severity} */ +Debug MAGNUM_EXPORT 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. + +## Basic usage + +See @ref DebugOutput for introduction. + +If OpenGL 4.3 is supported or @extension{KHR,debug} desktop or ES extension 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: + +@code +DebugMessage::insert(DebugMessage::Source::Application, DebugMessage::Type::Marker, + 1337, DebugOutput::Severity::Notification, "Hello from OpenGL command stream!"); +@endcode + +> DebugOutput::Source::Application DebugOutput::Type::Marker 1337 DebugOutput::Severity::Notification\n +>     Hello from OpenGL command stream! + +If only @extension2{EXT,debug_marker} or @extension{GREMEDY,string_marker} are +available, the message can be seen only through graphics debugger. + +If OpenGL 4.3 is not supported and neither @extension{KHR,debug} nor +@extension2{EXT,debug_marker} nor @extension{GREMEDY,string_marker} are +available, the function is essentially a no-op. + +## Performance notes + +If you ensure that you always use the `const char` 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 +*/ +class MAGNUM_EXPORT DebugMessage { + friend Implementation::DebugState; + + public: + /** + * @brief Message source + * + * @see @ref insert() + */ + enum class Source: GLenum { + #ifdef MAGNUM_BUILD_DEPRECATED + /** @copydoc DebugOutput::Source::Api + * @deprecated Use @ref DebugOutput::Source::Api instead. + */ + Api = GLenum(DebugOutput::Source::Api), + + /** @copydoc DebugOutput::Source::WindowSystem + * @deprecated Use @ref DebugOutput::Source::WindowSystem instead. + */ + WindowSystem = GLenum(DebugOutput::Source::WindowSystem), + + /** @copydoc DebugOutput::Source::ShaderCompiler + * @deprecated Use @ref DebugOutput::Source::ShaderCompiler instead. + */ + ShaderCompiler = GLenum(DebugOutput::Source::ShaderCompiler), + #endif + + /** External debugger or third-party middleware */ + #ifndef MAGNUM_TARGET_GLES + ThirdParty = GL_DEBUG_SOURCE_THIRD_PARTY, + #else + ThirdParty = GL_DEBUG_SOURCE_THIRD_PARTY_KHR, + #endif + + /** The application */ + #ifndef MAGNUM_TARGET_GLES + Application = GL_DEBUG_SOURCE_APPLICATION, + #else + Application = GL_DEBUG_SOURCE_APPLICATION_KHR, + #endif + + #ifdef MAGNUM_BUILD_DEPRECATED + /** @copydoc DebugOutput::Source::Other + * @deprecated Use @ref DebugOutput::Source::Other instead. + */ + Other = GLenum(DebugOutput::Source::Other) + #endif + }; + + /** + * @brief Message type + * + * @see @ref insert() + */ + enum class Type: GLenum { + /** OpenGL error */ + #ifndef MAGNUM_TARGET_GLES + Error = GL_DEBUG_TYPE_ERROR, + #else + Error = GL_DEBUG_TYPE_ERROR_KHR, + #endif + + /** Behavior that has been marked for deprecation */ + #ifndef MAGNUM_TARGET_GLES + 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_GLES + 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_GLES + Portability = GL_DEBUG_TYPE_PORTABILITY, + #else + Portability = GL_DEBUG_TYPE_PORTABILITY_KHR, + #endif + + /** Implementation-dependent performance warning */ + #ifndef MAGNUM_TARGET_GLES + Performance = GL_DEBUG_TYPE_PERFORMANCE, + #else + Performance = GL_DEBUG_TYPE_PERFORMANCE_KHR, + #endif + + /** Annotation of the command stream */ + #ifndef MAGNUM_TARGET_GLES + Marker = GL_DEBUG_TYPE_MARKER, + #else + Marker = GL_DEBUG_TYPE_MARKER_KHR, + #endif + + /** Any other type */ + #ifndef MAGNUM_TARGET_GLES + Other = GL_DEBUG_TYPE_OTHER + #else + Other = GL_DEBUG_TYPE_OTHER_KHR + #endif + }; + + #ifdef MAGNUM_BUILD_DEPRECATED + /** @copybrief DebugOutput::Severity + * @deprecated Use @ref DebugOutput::Severity instead. + */ + CORRADE_DEPRECATED("use DebugOutput::Severity instead") typedef DebugOutput::Severity Severity; + + /** @copybrief DebugOutput::Callback + * @deprecated Use @ref DebugOutput::Callback instead. + */ + /* Can't mark this as deprecated because compiler then complains when I use it as a parameter in setCallback() */ + typedef void(*Callback)(DebugMessage::Source, DebugMessage::Type, UnsignedInt, DebugOutput::Severity, const std::string&, const void*); + + /** @copybrief DebugOutput::maxLoggedMessages() + * @deprecated Use @ref DebugOutput::maxLoggedMessages() instead. + */ + CORRADE_DEPRECATED("use DebugOutput::maxLoggedMessages() instead") static Int maxLoggedMessages() { + return DebugOutput::maxLoggedMessages(); + } + + /** @copybrief DebugOutput::maxMessageLength() + * @deprecated Use @ref DebugOutput::maxMessageLength() instead. + */ + CORRADE_DEPRECATED("use DebugOutput::maxMessageLength() instead") static Int maxMessageLength() { + return DebugOutput::maxMessageLength(); + } + + /** @copybrief DebugOutput::setEnabled() + * @deprecated Use @ref DebugOutput::setEnabled() instead. + */ + #ifdef DOXYGEN_GENERATING_OUTPUT + template static void setEnabled(T... args); + #else + CORRADE_DEPRECATED("use DebugOutput::setEnabled() instead") static void setEnabled(Source source, Type type, std::initializer_list ids, bool enabled) { + DebugOutput::setEnabled(DebugOutput::Source(source), DebugOutput::Type(type), ids, enabled); + } + CORRADE_DEPRECATED("use DebugOutput::setEnabled() instead") static void setEnabled(Source source, Type type, DebugOutput::Severity severity, bool enabled) { + DebugOutput::setEnabled(DebugOutput::Source(source), DebugOutput::Type(type), severity, enabled); + } + CORRADE_DEPRECATED("use DebugOutput::setEnabled() instead") static void setEnabled(Source source, Type type, bool enabled) { + DebugOutput::setEnabled(DebugOutput::Source(source), DebugOutput::Type(type), enabled); + } + CORRADE_DEPRECATED("use DebugOutput::setEnabled() instead") static void setEnabled(Source source, DebugOutput::Severity severity, bool enabled) { + DebugOutput::setEnabled(DebugOutput::Source(source), severity, enabled); + } + CORRADE_DEPRECATED("use DebugOutput::setEnabled() instead") static void setEnabled(Source source, bool enabled) { + DebugOutput::setEnabled(DebugOutput::Source(source), enabled); + } + CORRADE_DEPRECATED("use DebugOutput::setEnabled() instead") static void setEnabled(Type type, DebugOutput::Severity severity, bool enabled) { + DebugOutput::setEnabled(DebugOutput::Type(type), severity, enabled); + } + CORRADE_DEPRECATED("use DebugOutput::setEnabled() instead") static void setEnabled(Type type, bool enabled) { + DebugOutput::setEnabled(DebugOutput::Type(type), enabled); + } + CORRADE_DEPRECATED("use DebugOutput::setEnabled() instead") static void setEnabled(DebugOutput::Severity severity, bool enabled) { + DebugOutput::setEnabled(severity, enabled); + } + CORRADE_DEPRECATED("use DebugOutput::setEnabled() instead") static void setEnabled(bool enabled) { + DebugOutput::setEnabled(enabled); + } + #endif + + /** @copybrief DebugOutput::setCallback() + * @deprecated Use @ref DebugOutput::setCallback() instead. + */ + CORRADE_DEPRECATED("use DebugOutput::setCallback() instead") static void setCallback(Callback callback, const void* userParam = nullptr) { + DebugOutput::setCallback(reinterpret_cast(callback), userParam); + } + + /** @copybrief DebugOutput::setDefaultCallback() + * @deprecated Use @ref DebugOutput::setDefaultCallback() instead. + */ + CORRADE_DEPRECATED("use DebugOutput::setDefaultCallback() instead") static void setDefaultCallback() { + DebugOutput::setDefaultCallback(); + } + #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 is not supported and neither @extension{KHR,debug} nor + * @extension2{EXT,debug_marker} (desktop or ES extensions) nor + * @extension{GREMEDY,string_marker} (desktop only extension) are + * available, this function does nothing. + * + * If @extension{KHR,debug} is not available and only @extension2{EXT,debug_marker} + * or @extension{GREMEDY,string_marker} are available, only @p string + * is used and all other parameters are ignored. + * @see @ref DebugOutput::maxMessageLength(), @fn_gl{DebugMessageInsert}, + * @fn_gl_extension2{InsertEventMarker,EXT,debug_marker} or + * @fn_gl_extension{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::ArrayReference string); + static MAGNUM_LOCAL void insertImplementationNoOp(Source, Type, UnsignedInt, DebugOutput::Severity, Containers::ArrayReference); + static MAGNUM_LOCAL void insertImplementationKhr(Source source, Type type, UnsignedInt id, DebugOutput::Severity severity, Containers::ArrayReference string); + static MAGNUM_LOCAL void insertImplementationExt(Source, Type, UnsignedInt, DebugOutput::Severity, Containers::ArrayReference string); + #ifndef MAGNUM_TARGET_GLES + static MAGNUM_LOCAL void insertImplementationGremedy(Source, Type, UnsignedInt, DebugOutput::Severity, Containers::ArrayReference string); + #endif +}; + +/** @debugoperatorclassenum{Magnum::DebugMessage,Magnum::DebugMessage::Source} */ +Debug MAGNUM_EXPORT operator<<(Debug debug, DebugMessage::Source value); + +/** @debugoperatorclassenum{Magnum::DebugMessage,Magnum::DebugMessage::Type} */ +Debug MAGNUM_EXPORT 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. + +## 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: +@code +{ + // Push debug group + DebugGroup group{DebugGroup::Source::Application, 42, "Scene rendering"}; + + Renderer::enable(Renderer::Feature::Blending); + mesh.draw(shader); + Renderer::disable(Renderer::Feature::Blending); + + // The debug group is popped automatically at the end of the scope +} +@endcode + +If, for some reason, you need to pop in different scope, you can call @ref push() +and @ref pop() manually: +@code +DebugGroup group; + +group.push(DebugGroup::Source::Application, 42, "Scene rendering"); + +Renderer::enable(Renderer::Feature::Blending); +mesh.draw(shader); +Renderer::disable(Renderer::Feature::Blending); + +group.pop(); +@endcode + +If OpenGL 4.3 is supported or @extension{KHR,debug} desktop or ES extension 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: + +> DebugOutput::Source::Application DebugOutput::Type::PushGroup 42 DebugOutput::Severity::Notification\n +>     Scene rendering\n +> DebugOutput::Source::Application DebugOutput::Type::PopGroup 42 DebugOutput::Severity::Notification\n +>     Scene rendering + +If only @extension2{EXT,debug_marker} is available, the group can be seen only +through graphics debugger. + +If OpenGL 4.3 is not supported and neither @extension{KHR,debug} nor +@extension2{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. + +## 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. + +## Performance notes + +If you ensure that you always use the `const char` 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 +*/ +class MAGNUM_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_GLES + ThirdParty = GL_DEBUG_SOURCE_THIRD_PARTY, + #else + ThirdParty = GL_DEBUG_SOURCE_THIRD_PARTY_KHR, + #endif + + /** The application */ + #ifndef MAGNUM_TARGET_GLES + 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 is not supported and @extension{KHR,debug} + * desktop or ES extension is not available, returns `0`. + * @see @fn_gl{Get} with @def_gl{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 is not supported and neither @extension{KHR,debug} nor + * @extension2{EXT,debug_marker} is available, this function does + * nothing. If @extension{KHR,debug} is not available and only + * @extension2{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{PushDebugGroup} or + * @fn_gl_extension2{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 is not supported and neither @extension{KHR,debug} nor + * @extension2{EXT,debug_marker} is available, this function does + * nothing. + * @see @ref push(), @ref Renderer::Error::StackUnderflow, + * @fn_gl{PopDebugGroup} or + * @fn_gl_extension2{PopGroupMarker,EXT,debug_marker} + */ + void pop(); + + private: + void pushInternal(Source source, UnsignedInt id, Containers::ArrayReference message); + + static MAGNUM_LOCAL void pushImplementationNoOp(Source source, UnsignedInt id, Containers::ArrayReference message); + static MAGNUM_LOCAL void pushImplementationKhr(Source source, UnsignedInt id, Containers::ArrayReference message); + static MAGNUM_LOCAL void pushImplementationExt(Source source, UnsignedInt id, Containers::ArrayReference message); + + static MAGNUM_LOCAL void popImplementationNoOp(); + static MAGNUM_LOCAL void popImplementationKhr(); + static MAGNUM_LOCAL void popImplementationExt(); + + bool _active; +}; + +/** @debugoperatorclassenum{Magnum::DebugGroup,Magnum::DebugGroup::Source} */ +Debug MAGNUM_EXPORT operator<<(Debug debug, DebugGroup::Source value); + +} + +#endif diff --git a/src/Magnum/Implementation/DebugState.cpp b/src/Magnum/Implementation/DebugState.cpp index 8410ab558..cdc5a4f8b 100644 --- a/src/Magnum/Implementation/DebugState.cpp +++ b/src/Magnum/Implementation/DebugState.cpp @@ -31,15 +31,23 @@ namespace Magnum { namespace Implementation { -DebugState::DebugState(Context& context, std::vector& extensions): maxLabelLength(0), maxLoggedMessages(0), maxMessageLength(0), messageCallback(nullptr) { +DebugState::DebugState(Context& context, std::vector& extensions): + maxLabelLength{0}, + maxLoggedMessages{0}, + maxMessageLength{0}, + maxStackDepth{0}, + messageCallback(nullptr) +{ if(context.isExtensionSupported()) { extensions.push_back(Extensions::GL::KHR::debug::string()); getLabelImplementation = &AbstractObject::getLabelImplementationKhr; labelImplementation = &AbstractObject::labelImplementationKhr; + controlImplementation = &DebugOutput::controlImplementationKhr; + callbackImplementation = &DebugOutput::callbackImplementationKhr; messageInsertImplementation = &DebugMessage::insertImplementationKhr; - messageControlImplementation = &DebugMessage::controlImplementationKhr; - messageCallbackImplementation = &DebugMessage::callbackImplementationKhr; + pushGroupImplementation = &DebugGroup::pushImplementationKhr; + popGroupImplementation = &DebugGroup::popImplementationKhr; } else { if(context.isExtensionSupported()) { @@ -64,8 +72,8 @@ DebugState::DebugState(Context& context, std::vector& extensions): #endif } else messageInsertImplementation = &DebugMessage::insertImplementationNoOp; - messageControlImplementation = &DebugMessage::controlImplementationNoOp; - messageCallbackImplementation = &DebugMessage::callbackImplementationNoOp; + controlImplementation = &DebugOutput::controlImplementationNoOp; + callbackImplementation = &DebugOutput::callbackImplementationNoOp; } } diff --git a/src/Magnum/Implementation/DebugState.h b/src/Magnum/Implementation/DebugState.h index 11434de98..4c5051853 100644 --- a/src/Magnum/Implementation/DebugState.h +++ b/src/Magnum/Implementation/DebugState.h @@ -28,7 +28,7 @@ #include #include -#include "Magnum/DebugMessage.h" +#include "Magnum/DebugOutput.h" namespace Magnum { namespace Implementation { @@ -38,12 +38,14 @@ struct DebugState { std::string(*getLabelImplementation)(GLenum, GLuint); void(*labelImplementation)(GLenum, GLuint, Containers::ArrayReference); - void(*messageInsertImplementation)(DebugMessage::Source, DebugMessage::Type, UnsignedInt, DebugMessage::Severity, Containers::ArrayReference); - void(*messageControlImplementation)(GLenum, GLenum, GLenum, std::initializer_list, bool); - void(*messageCallbackImplementation)(DebugMessage::Callback, const void*); + void(*messageInsertImplementation)(DebugMessage::Source, DebugMessage::Type, UnsignedInt, DebugOutput::Severity, Containers::ArrayReference); + void(*controlImplementation)(GLenum, GLenum, GLenum, std::initializer_list, bool); + void(*callbackImplementation)(DebugOutput::Callback, const void*); + void(*pushGroupImplementation)(DebugGroup::Source, UnsignedInt, Containers::ArrayReference); + void(*popGroupImplementation)(); - GLint maxLabelLength, maxLoggedMessages, maxMessageLength; - DebugMessage::Callback messageCallback; + GLint maxLabelLength, maxLoggedMessages, maxMessageLength, maxStackDepth; + DebugOutput::Callback messageCallback; }; }} diff --git a/src/Magnum/Platform/magnum-info.cpp b/src/Magnum/Platform/magnum-info.cpp index 296e5c40b..25e991217 100644 --- a/src/Magnum/Platform/magnum-info.cpp +++ b/src/Magnum/Platform/magnum-info.cpp @@ -35,7 +35,7 @@ #include "Magnum/BufferTexture.h" #endif #include "Magnum/Context.h" -#include "Magnum/DebugMessage.h" +#include "Magnum/DebugOutput.h" #include "Magnum/Extensions.h" #include "Magnum/Framebuffer.h" #include "Magnum/Mesh.h" @@ -608,8 +608,8 @@ MagnumInfo::MagnumInfo(const Arguments& arguments): Platform::WindowlessApplicat _h(KHR::debug) _l(AbstractObject::maxLabelLength()) - _l(DebugMessage::maxLoggedMessages()) - _l(DebugMessage::maxMessageLength()) + _l(DebugOutput::maxLoggedMessages()) + _l(DebugOutput::maxMessageLength()) } #ifdef MAGNUM_TARGET_GLES2 diff --git a/src/Magnum/Renderer.h b/src/Magnum/Renderer.h index cc8513e9f..e4a14c7b2 100644 --- a/src/Magnum/Renderer.h +++ b/src/Magnum/Renderer.h @@ -85,9 +85,10 @@ class MAGNUM_EXPORT Renderer { Blending = GL_BLEND, /** - * Debug output - * @see @ref DebugMessage, @ref DebugMessage::setEnabled(), - * @ref Feature::DebugOutputSynchronous + * Debug output. Disabled by default unless the GL context was + * created with debug output enabled. + * @see @ref DebugOutput, @ref Feature::DebugOutputSynchronous, + * @ref Platform::Sdl2Application::Configuration::Flag::DebugOutput "Platform::*Application::Configuration::Flag::DebugOutput" * @requires_gl43 Extension @extension{KHR,debug} * @requires_es_extension Extension @es_extension{KHR,debug} */ @@ -981,6 +982,7 @@ class MAGNUM_EXPORT Renderer { /** * Given operation would cause an internal stack to underflow. + * @see @ref DebugGroup * @requires_gl43 Extension @extension{KHR,debug} * @requires_es_extension Extension @es_extension2{KHR,debug,debug} */ @@ -992,6 +994,7 @@ class MAGNUM_EXPORT Renderer { /** * Given operation would cause an internal stack to overflow. + * @see @ref DebugGroup * @requires_gl43 Extension @extension{KHR,debug} * @requires_es_extension Extension @es_extension2{KHR,debug,debug} */ diff --git a/src/Magnum/Test/AbstractOpenGLTester.h b/src/Magnum/Test/AbstractOpenGLTester.h index 701a84296..2e92b2ee2 100644 --- a/src/Magnum/Test/AbstractOpenGLTester.h +++ b/src/Magnum/Test/AbstractOpenGLTester.h @@ -29,7 +29,7 @@ #include "Magnum/Context.h" #include "Magnum/Extensions.h" -#include "Magnum/DebugMessage.h" +#include "Magnum/DebugOutput.h" #include "Magnum/Renderer.h" #if !defined(MAGNUM_TARGET_GLES) || defined(MAGNUM_TARGET_DESKTOP_GLES) @@ -55,10 +55,10 @@ AbstractOpenGLTester::AbstractOpenGLTester(): Platform::WindowlessApplication({z if(Context::current()->isExtensionSupported()) { Renderer::enable(Renderer::Feature::DebugOutput); Renderer::enable(Renderer::Feature::DebugOutputSynchronous); - DebugMessage::setDefaultCallback(); + DebugOutput::setDefaultCallback(); /* Disable "Buffer detailed info" message on NV (too spammy) */ - DebugMessage::setEnabled(DebugMessage::Source::Api, DebugMessage::Type::Other, {131185}, false); + DebugOutput::setEnabled(DebugOutput::Source::Api, DebugOutput::Type::Other, {131185}, false); } } diff --git a/src/Magnum/Test/CMakeLists.txt b/src/Magnum/Test/CMakeLists.txt index e024dceaa..3938ce13f 100644 --- a/src/Magnum/Test/CMakeLists.txt +++ b/src/Magnum/Test/CMakeLists.txt @@ -28,7 +28,7 @@ corrade_add_test(AbstractShaderProgramTest AbstractShaderProgramTest.cpp LIBRARI corrade_add_test(ArrayTest ArrayTest.cpp) corrade_add_test(ColorTest ColorTest.cpp LIBRARIES MagnumMathTestLib) corrade_add_test(ContextTest ContextTest.cpp LIBRARIES Magnum) -corrade_add_test(DebugMessageTest DebugMessageTest.cpp LIBRARIES Magnum) +corrade_add_test(DebugOutputTest DebugOutputTest.cpp LIBRARIES Magnum) corrade_add_test(DefaultFramebufferTest DefaultFramebufferTest.cpp LIBRARIES Magnum) corrade_add_test(FramebufferTest FramebufferTest.cpp LIBRARIES Magnum) corrade_add_test(ImageTest ImageTest.cpp LIBRARIES Magnum) @@ -47,7 +47,7 @@ if(BUILD_GL_TESTS) corrade_add_test(BufferGLTest BufferGLTest.cpp LIBRARIES ${GL_TEST_LIBRARIES}) corrade_add_test(ContextGLTest ContextGLTest.cpp LIBRARIES ${GL_TEST_LIBRARIES}) corrade_add_test(CubeMapTextureGLTest CubeMapTextureGLTest.cpp LIBRARIES ${GL_TEST_LIBRARIES}) - corrade_add_test(DebugGLTest DebugGLTest.cpp LIBRARIES ${GL_TEST_LIBRARIES}) + corrade_add_test(DebugOutputGLTest DebugOutputGLTest.cpp LIBRARIES ${GL_TEST_LIBRARIES}) corrade_add_test(FramebufferGLTest FramebufferGLTest.cpp LIBRARIES ${GL_TEST_LIBRARIES}) corrade_add_test(MeshGLTest MeshGLTest.cpp LIBRARIES ${GL_TEST_LIBRARIES}) corrade_add_test(RenderbufferGLTest RenderbufferGLTest.cpp LIBRARIES ${GL_TEST_LIBRARIES}) diff --git a/src/Magnum/Test/DebugGLTest.cpp b/src/Magnum/Test/DebugGLTest.cpp deleted file mode 100644 index 3cc798ebc..000000000 --- a/src/Magnum/Test/DebugGLTest.cpp +++ /dev/null @@ -1,144 +0,0 @@ -/* - This file is part of Magnum. - - Copyright © 2010, 2011, 2012, 2013, 2014, 2015 - 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. -*/ - -#include - -#include "Magnum/Context.h" -#include "Magnum/DebugMessage.h" -#include "Magnum/Extensions.h" -#include "Magnum/Test/AbstractOpenGLTester.h" - -#ifdef MAGNUM_BUILD_DEPRECATED -#include "Magnum/DebugMarker.h" -#endif - -namespace Magnum { namespace Test { - -struct DebugGLTest: AbstractOpenGLTester { - explicit DebugGLTest(); - - void insertMessageNoOp(); - void insertMessage(); - void insertMessageFallback(); - - void setMessageEnabled(); - - void deprecated(); -}; - -DebugGLTest::DebugGLTest() { - addTests({&DebugGLTest::insertMessageNoOp, - &DebugGLTest::insertMessage, - &DebugGLTest::insertMessageFallback, - - &DebugGLTest::setMessageEnabled, - - #ifdef MAGNUM_BUILD_DEPRECATED - &DebugGLTest::deprecated - #endif - }); -} - -void DebugGLTest::insertMessageNoOp() { - if(Context::current()->isExtensionSupported() || - Context::current()->isExtensionSupported() - #ifndef MAGNUM_TARGET_GLES - || Context::current()->isExtensionSupported() - #endif - ) - CORRADE_SKIP("The extensions are supported, cannot test."); - - DebugMessage::insert(DebugMessage::Source::Application, DebugMessage::Type::Marker, - 1337, DebugMessage::Severity::Notification, "Hello from OpenGL command stream!"); - - MAGNUM_VERIFY_NO_ERROR(); -} - -void DebugGLTest::insertMessage() { - if(!Context::current()->isExtensionSupported()) - CORRADE_SKIP(Extensions::GL::KHR::debug::string() + std::string(" is not supported")); - - Renderer::enable(Renderer::Feature::DebugOutput); - - Renderer::enable(Renderer::Feature::DebugOutputSynchronous); - std::ostringstream out; - Debug::setOutput(&out); - DebugMessage::setDefaultCallback(); - DebugMessage::insert(DebugMessage::Source::Application, DebugMessage::Type::Marker, - 1337, DebugMessage::Severity::Notification, "Hello from OpenGL command stream!"); - - Renderer::enable(Renderer::Feature::DebugOutput); - - MAGNUM_VERIFY_NO_ERROR(); - CORRADE_COMPARE(out.str(), - "DebugMessage::Source::Application DebugMessage::Type::Marker 1337 DebugMessage::Severity::Notification \n" - " Hello from OpenGL command stream!\n"); -} - -void DebugGLTest::insertMessageFallback() { - if(Context::current()->isExtensionSupported() || - (!Context::current()->isExtensionSupported() - #ifndef MAGNUM_TARGET_GLES - && !Context::current()->isExtensionSupported() - #endif - )) - CORRADE_SKIP("No proper extension is supported"); - - DebugMessage::insert(DebugMessage::Source::Application, DebugMessage::Type::Marker, - 1337, DebugMessage::Severity::Notification, "Hello from OpenGL command stream!"); - - MAGNUM_VERIFY_NO_ERROR(); -} - -void DebugGLTest::setMessageEnabled() { - if(!Context::current()->isExtensionSupported()) - CORRADE_SKIP(Extensions::GL::KHR::debug::string() + std::string(" is not supported")); - - /* Try at least some combinations */ - DebugMessage::setEnabled(DebugMessage::Source::Application, true); - DebugMessage::setEnabled(DebugMessage::Source::Application, DebugMessage::Type::UndefinedBehavior, {3168, 35487, 234487}, false); - DebugMessage::setEnabled(true); - - MAGNUM_VERIFY_NO_ERROR(); -} - -#ifdef MAGNUM_BUILD_DEPRECATED -void DebugGLTest::deprecated() { - #ifdef __GNUC__ - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wdeprecated-declarations" - #endif - DebugMarker::mark("hello"); - #ifdef __GNUC__ - #pragma GCC diagnostic pop - #endif - - MAGNUM_VERIFY_NO_ERROR(); -} -#endif - -}} - -CORRADE_TEST_MAIN(Magnum::Test::DebugGLTest) diff --git a/src/Magnum/Test/DebugOutputGLTest.cpp b/src/Magnum/Test/DebugOutputGLTest.cpp new file mode 100644 index 000000000..a847d791d --- /dev/null +++ b/src/Magnum/Test/DebugOutputGLTest.cpp @@ -0,0 +1,211 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015 + 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. +*/ + +#include + +#include "Magnum/Context.h" +#include "Magnum/DebugOutput.h" +#include "Magnum/Extensions.h" +#include "Magnum/Test/AbstractOpenGLTester.h" + +#ifdef MAGNUM_BUILD_DEPRECATED +#include "Magnum/DebugMarker.h" +#endif + +namespace Magnum { namespace Test { + +struct DebugOutputGLTest: AbstractOpenGLTester { + explicit DebugOutputGLTest(); + + void setCallback(); + void setEnabled(); + + void messageNoOp(); + void message(); + void messageFallback(); + + void groupNoOp(); + void group(); + void groupFallback(); + + #ifdef MAGNUM_BUILD_DEPRECATED + void deprecated(); + #endif +}; + +DebugOutputGLTest::DebugOutputGLTest() { + addTests({&DebugOutputGLTest::setCallback, + &DebugOutputGLTest::setEnabled, + + &DebugOutputGLTest::messageNoOp, + &DebugOutputGLTest::message, + &DebugOutputGLTest::messageFallback, + + &DebugOutputGLTest::groupNoOp, + &DebugOutputGLTest::group, + &DebugOutputGLTest::groupFallback, + + #ifdef MAGNUM_BUILD_DEPRECATED + &DebugOutputGLTest::deprecated + #endif + }); +} + +void DebugOutputGLTest::setCallback() { + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::KHR::debug::string() + std::string(" is not supported")); + + /* Need to be careful, because the test runner is using debug output too */ + DebugOutput::setDefaultCallback(); + + MAGNUM_VERIFY_NO_ERROR(); +} + +void DebugOutputGLTest::setEnabled() { + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::KHR::debug::string() + std::string(" is not supported")); + + /* Try at least some combinations */ + DebugOutput::setEnabled(DebugOutput::Source::Application, true); + DebugOutput::setEnabled(DebugOutput::Source::Application, DebugOutput::Type::UndefinedBehavior, {3168, 35487, 234487}, false); + DebugOutput::setEnabled(true); + + MAGNUM_VERIFY_NO_ERROR(); +} + +void DebugOutputGLTest::messageNoOp() { + if(Context::current()->isExtensionSupported() || + Context::current()->isExtensionSupported() + #ifndef MAGNUM_TARGET_GLES + || Context::current()->isExtensionSupported() + #endif + ) + CORRADE_SKIP("The extensions are supported, cannot test."); + + DebugMessage::insert(DebugMessage::Source::Application, DebugMessage::Type::Marker, + 1337, DebugOutput::Severity::Notification, "Hello from OpenGL command stream!"); + + MAGNUM_VERIFY_NO_ERROR(); +} + +void DebugOutputGLTest::message() { + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::KHR::debug::string() + std::string(" is not supported")); + + /* Need to be careful, because the test runner is using debug output too */ + std::ostringstream out; + Debug::setOutput(&out); + DebugMessage::insert(DebugMessage::Source::Application, DebugMessage::Type::Marker, + 1337, DebugOutput::Severity::Notification, "Hello from OpenGL command stream!"); + + MAGNUM_VERIFY_NO_ERROR(); + CORRADE_COMPARE(out.str(), + "DebugOutput::Source::Application DebugOutput::Type::Marker 1337 DebugOutput::Severity::Notification \n" + " Hello from OpenGL command stream!\n"); +} + +void DebugOutputGLTest::messageFallback() { + if(Context::current()->isExtensionSupported() || + (!Context::current()->isExtensionSupported() + #ifndef MAGNUM_TARGET_GLES + && !Context::current()->isExtensionSupported() + #endif + )) + CORRADE_SKIP("No proper extension is supported"); + + DebugMessage::insert(DebugMessage::Source::Application, DebugMessage::Type::Marker, + 1337, DebugOutput::Severity::Notification, "Hello from OpenGL command stream!"); + + MAGNUM_VERIFY_NO_ERROR(); +} + +void DebugOutputGLTest::groupNoOp() { + if(Context::current()->isExtensionSupported() || + Context::current()->isExtensionSupported()) + CORRADE_SKIP("The extensions are supported, cannot test."); + + { + DebugGroup g{DebugGroup::Source::Application, 1337, "Debug group"}; + } + + MAGNUM_VERIFY_NO_ERROR(); +} + +void DebugOutputGLTest::group() { + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::KHR::debug::string() + std::string(" is not supported")); + + /* Need to be careful, because the test runner is using debug output too */ + std::ostringstream out; + Debug::setOutput(&out); + { + DebugGroup g1{DebugGroup::Source::Application, 42, "Automatic debug group"}; + DebugGroup g2; + g2.push(DebugGroup::Source::Application, 1337, "Manual debug group"); + g2.pop(); + } + + MAGNUM_VERIFY_NO_ERROR(); + CORRADE_COMPARE(out.str(), + "DebugOutput::Source::Application DebugOutput::Type::PushGroup 42 DebugOutput::Severity::Notification \n" + " Automatic debug group\n" + "DebugOutput::Source::Application DebugOutput::Type::PushGroup 1337 DebugOutput::Severity::Notification \n" + " Manual debug group\n" + "DebugOutput::Source::Application DebugOutput::Type::PopGroup 1337 DebugOutput::Severity::Notification \n" + " Manual debug group\n" + "DebugOutput::Source::Application DebugOutput::Type::PopGroup 42 DebugOutput::Severity::Notification \n" + " Automatic debug group\n"); +} + +void DebugOutputGLTest::groupFallback() { + if(Context::current()->isExtensionSupported() || + !Context::current()->isExtensionSupported()) + CORRADE_SKIP("No proper extension is supported"); + + { + DebugGroup g{DebugGroup::Source::Application, 1337, "Debug group"}; + } + + MAGNUM_VERIFY_NO_ERROR(); +} + +#ifdef MAGNUM_BUILD_DEPRECATED +void DebugOutputGLTest::deprecated() { + #ifdef __GNUC__ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdeprecated-declarations" + #endif + DebugMarker::mark("hello"); + #ifdef __GNUC__ + #pragma GCC diagnostic pop + #endif + + MAGNUM_VERIFY_NO_ERROR(); +} +#endif + +}} + +CORRADE_TEST_MAIN(Magnum::Test::DebugOutputGLTest) diff --git a/src/Magnum/Test/DebugMessageTest.cpp b/src/Magnum/Test/DebugOutputTest.cpp similarity index 51% rename from src/Magnum/Test/DebugMessageTest.cpp rename to src/Magnum/Test/DebugOutputTest.cpp index f0854b548..26df23359 100644 --- a/src/Magnum/Test/DebugMessageTest.cpp +++ b/src/Magnum/Test/DebugOutputTest.cpp @@ -26,42 +26,70 @@ #include #include -#include "Magnum/DebugMessage.h" +#include "Magnum/DebugOutput.h" namespace Magnum { namespace Test { -struct DebugMessageTest: TestSuite::Tester { - explicit DebugMessageTest(); +struct DebugOutputTest: TestSuite::Tester { + explicit DebugOutputTest(); void debugSource(); void debugType(); void debugSeverity(); + + void debugMessageSource(); + void debugMessageType(); + + void debugGroupSource(); }; -DebugMessageTest::DebugMessageTest() { - addTests({&DebugMessageTest::debugSource, - &DebugMessageTest::debugType, - &DebugMessageTest::debugSeverity}); +DebugOutputTest::DebugOutputTest() { + addTests({&DebugOutputTest::debugSource, + &DebugOutputTest::debugType, + &DebugOutputTest::debugSeverity, + + &DebugOutputTest::debugMessageSource, + &DebugOutputTest::debugMessageType, + + &DebugOutputTest::debugGroupSource}); +} + +void DebugOutputTest::debugSource() { + std::ostringstream o; + Debug(&o) << DebugOutput::Source::ShaderCompiler; + CORRADE_COMPARE(o.str(), "DebugOutput::Source::ShaderCompiler\n"); +} + +void DebugOutputTest::debugType() { + std::ostringstream o; + Debug(&o) << DebugOutput::Type::PushGroup; + CORRADE_COMPARE(o.str(), "DebugOutput::Type::PushGroup\n"); +} + +void DebugOutputTest::debugSeverity() { + std::ostringstream o; + Debug(&o) << DebugOutput::Severity::Notification; + CORRADE_COMPARE(o.str(), "DebugOutput::Severity::Notification\n"); } -void DebugMessageTest::debugSource() { +void DebugOutputTest::debugMessageSource() { std::ostringstream o; - Debug(&o) << DebugMessage::Source::ShaderCompiler; - CORRADE_COMPARE(o.str(), "DebugMessage::Source::ShaderCompiler\n"); + Debug(&o) << DebugMessage::Source::Application; + CORRADE_COMPARE(o.str(), "DebugMessage::Source::Application\n"); } -void DebugMessageTest::debugType() { +void DebugOutputTest::debugMessageType() { std::ostringstream o; Debug(&o) << DebugMessage::Type::DeprecatedBehavior; CORRADE_COMPARE(o.str(), "DebugMessage::Type::DeprecatedBehavior\n"); } -void DebugMessageTest::debugSeverity() { +void DebugOutputTest::debugGroupSource() { std::ostringstream o; - Debug(&o) << DebugMessage::Severity::Notification; - CORRADE_COMPARE(o.str(), "DebugMessage::Severity::Notification\n"); + Debug(&o) << DebugGroup::Source::ThirdParty; + CORRADE_COMPARE(o.str(), "DebugGroup::Source::ThirdParty\n"); } }} -CORRADE_TEST_MAIN(Magnum::Test::DebugMessageTest) +CORRADE_TEST_MAIN(Magnum::Test::DebugOutputTest)