From 7f22b9c2143b755746fcbceecf9204dec3c6901d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 22 Dec 2013 22:56:26 +0100 Subject: [PATCH] Support for debug message callback from KHR_debug. --- src/DebugMessage.cpp | 59 +++++++++++++++++++++++++ src/DebugMessage.h | 72 ++++++++++++++++++++++++++++--- src/Implementation/DebugState.cpp | 5 ++- src/Implementation/DebugState.h | 2 + src/Renderer.h | 25 +++++++++++ src/Test/DebugGLTest.cpp | 13 ++++++ 6 files changed, 170 insertions(+), 6 deletions(-) diff --git a/src/DebugMessage.cpp b/src/DebugMessage.cpp index b45314baf..b087c09c1 100644 --- a/src/DebugMessage.cpp +++ b/src/DebugMessage.cpp @@ -33,6 +33,29 @@ namespace Magnum { +namespace { + +void 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); +} + +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; @@ -71,6 +94,14 @@ void DebugMessage::insert(const Source source, const Type type, const UnsignedIn Context::current()->state().debug->messageInsertImplementation(source, type, id, severity, string); } +void DebugMessage::setCallback(const Callback callback, const void* userParam) { + Context::current()->state().debug->messageCallbackImplementation(callback, userParam); +} + +void DebugMessage::setDefaultCallback() { + setCallback(defaultCallback, nullptr); +} + void DebugMessage::insertImplementationNoOp(Source, Type, UnsignedInt, Severity, const std::string&) {} void DebugMessage::insertImplementationKhr(const Source source, const Type type, const UnsignedInt id, const Severity severity, const std::string& string) { @@ -104,6 +135,34 @@ void DebugMessage::insertImplementationGremedy(Source, Type, UnsignedInt, Severi } #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); + #else + static_cast(userParam); + CORRADE_INTERNAL_ASSERT(false); + //glDebugMessageCallbackEXT(callbackWrapper, userParam); + #endif + + /* Deleting callback */ + } else if(original && !callback) { + #ifndef MAGNUM_TARGET_GLES + glDebugMessageCallback(nullptr, nullptr); + #else + CORRADE_INTERNAL_ASSERT(false); + //glDebugMessageCallbackEXT(nullptr, nullptr); + #endif + } +} + #ifndef DOXYGEN_GENERATING_OUTPUT Debug operator<<(Debug debug, const DebugMessage::Source value) { switch(value) { diff --git a/src/DebugMessage.h b/src/DebugMessage.h index 00726c28a..d91cff627 100644 --- a/src/DebugMessage.h +++ b/src/DebugMessage.h @@ -41,8 +41,32 @@ namespace Implementation { struct DebugState; } /** @brief Debug message -Allows inserting debug messages into OpenGL command stream, for example with -conjunction with various debuggers, such as Apitrace or gDEBugger. +Allows retrieving and inserting debug messages from and to OpenGL command +stream, for example with conjunction with various debuggers, such as Apitrace +or gDEBugger. + +@section DebugMessage-usage 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::setFeature(Renderer::Feature::DebugOutput, true); +Renderer::setFeature(Renderer::Feature::DebugOutputSynchronous, true); + +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 class Implementation::DebugState; @@ -51,7 +75,7 @@ class MAGNUM_EXPORT DebugMessage { /** * @brief Message source * - * @see @ref insert() + * @see @ref insert(), @ref setCallback() */ enum class Source: GLenum { /** OpenGL */ @@ -100,7 +124,7 @@ class MAGNUM_EXPORT DebugMessage { /** * @brief Message type * - * @see @ref insert() + * @see @ref insert(), @ref setCallback() */ enum class Type: GLenum { /** OpenGL error */ @@ -156,7 +180,7 @@ class MAGNUM_EXPORT DebugMessage { /** * @brief Message severity * - * @see @ref insert() + * @see @ref insert(), @ref setCallback() */ enum class Severity: GLenum { /** @@ -194,6 +218,13 @@ class MAGNUM_EXPORT DebugMessage { #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 * @@ -242,6 +273,34 @@ class MAGNUM_EXPORT DebugMessage { */ static void insert(Source source, Type type, UnsignedInt id, Severity severity, const std::string& string); + /** + * @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 + */ + 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
+         *     Hello from OpenGL command stream!
+ */ + static void setDefaultCallback(); + DebugMessage() = delete; private: @@ -251,6 +310,9 @@ class MAGNUM_EXPORT DebugMessage { #ifndef MAGNUM_TARGET_GLES static MAGNUM_LOCAL void insertImplementationGremedy(Source, Type, UnsignedInt, Severity, const std::string& string); #endif + + static MAGNUM_LOCAL void callbackImplementationNoOp(Callback, const void*); + static MAGNUM_LOCAL void callbackImplementationKhr(Callback callback, const void* userParam); }; /** @debugoperator{Magnum::DebugMessage} */ diff --git a/src/Implementation/DebugState.cpp b/src/Implementation/DebugState.cpp index 14ea03eea..e00b1bb62 100644 --- a/src/Implementation/DebugState.cpp +++ b/src/Implementation/DebugState.cpp @@ -30,11 +30,12 @@ namespace Magnum { namespace Implementation { -DebugState::DebugState(Context& context): maxLabelLength(0), maxLoggedMessages(0), maxMessageLength(0) { +DebugState::DebugState(Context& context): maxLabelLength(0), maxLoggedMessages(0), maxMessageLength(0), messageCallback(nullptr) { if(context.isExtensionSupported()) { getLabelImplementation = &AbstractObject::getLabelImplementationKhr; labelImplementation = &AbstractObject::labelImplementationKhr; messageInsertImplementation = &DebugMessage::insertImplementationKhr; + messageCallbackImplementation = &DebugMessage::callbackImplementationKhr; } else { if(context.isExtensionSupported()) { @@ -53,6 +54,8 @@ DebugState::DebugState(Context& context): maxLabelLength(0), maxLoggedMessages(0 #endif else messageInsertImplementation = &DebugMessage::insertImplementationNoOp; + + messageCallbackImplementation = &DebugMessage::callbackImplementationNoOp; } } diff --git a/src/Implementation/DebugState.h b/src/Implementation/DebugState.h index e9995d706..0b3ec18f1 100644 --- a/src/Implementation/DebugState.h +++ b/src/Implementation/DebugState.h @@ -37,8 +37,10 @@ struct DebugState { void(*labelImplementation)(GLenum, GLuint, const std::string&); void(*messageInsertImplementation)(DebugMessage::Source, DebugMessage::Type, UnsignedInt, DebugMessage::Severity, const std::string&); + void(*messageCallbackImplementation)(DebugMessage::Callback, const void*); GLint maxLabelLength, maxLoggedMessages, maxMessageLength; + DebugMessage::Callback messageCallback; }; }} diff --git a/src/Renderer.h b/src/Renderer.h index bdad9382d..f9254b114 100644 --- a/src/Renderer.h +++ b/src/Renderer.h @@ -79,6 +79,31 @@ class MAGNUM_EXPORT Renderer { */ Blending = GL_BLEND, + /** + * Debug output + * @requires_gl43 %Extension @extension{KHR,debug} + * @requires_es_extension %Extension @es_extension{KHR,debug} + * @see @ref DebugMessage, @ref Feature::DebugOutputSynchronous + */ + #ifndef MAGNUM_TARGET_GLES + DebugOutput = GL_DEBUG_OUTPUT, + #else + DebugOutput = GL_DEBUG_OUTPUT_KHR, + #endif + + /** + * Synchronous debug output. Has effect only if @ref Feature::DebugOutput + * is enabled. + * @requires_gl43 %Extension @extension{KHR,debug} + * @requires_es_extension %Extension @es_extension{KHR,debug} + * @see @ref DebugMessage + */ + #ifndef MAGNUM_TARGET_GLES + DebugOutputSynchronous = GL_DEBUG_OUTPUT_SYNCHRONOUS, + #else + DebugOutputSynchronous = GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR, + #endif + #ifndef MAGNUM_TARGET_GLES /** * Depth clamping. If enabled, ignores near and far clipping plane. diff --git a/src/Test/DebugGLTest.cpp b/src/Test/DebugGLTest.cpp index 43c1689e9..88cd1f4bb 100644 --- a/src/Test/DebugGLTest.cpp +++ b/src/Test/DebugGLTest.cpp @@ -24,6 +24,8 @@ #include "Test/AbstractOpenGLTester.h" +#include + #include "Context.h" #include "DebugMessage.h" #include "Extensions.h" @@ -72,10 +74,21 @@ void DebugGLTest::insertMessage() { if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::KHR::debug::string() + std::string(" is not supported")); + Renderer::setFeature(Renderer::Feature::DebugOutput, true); + + Renderer::setFeature(Renderer::Feature::DebugOutputSynchronous, true); + 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::setFeature(Renderer::Feature::DebugOutput, false); + 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() {