Browse Source

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.
pull/89/head
Vladimír Vondruš 11 years ago
parent
commit
c3d9f7a72b
  1. 13
      doc/opengl-mapping.dox
  2. 4
      doc/opengl-support.dox
  3. 5
      src/Magnum/CMakeLists.txt
  4. 8
      src/Magnum/DebugMarker.h
  5. 241
      src/Magnum/DebugMessage.cpp
  6. 393
      src/Magnum/DebugMessage.h
  7. 380
      src/Magnum/DebugOutput.cpp
  8. 895
      src/Magnum/DebugOutput.h
  9. 18
      src/Magnum/Implementation/DebugState.cpp
  10. 14
      src/Magnum/Implementation/DebugState.h
  11. 6
      src/Magnum/Platform/magnum-info.cpp
  12. 9
      src/Magnum/Renderer.h
  13. 6
      src/Magnum/Test/AbstractOpenGLTester.h
  14. 4
      src/Magnum/Test/CMakeLists.txt
  15. 144
      src/Magnum/Test/DebugGLTest.cpp
  16. 211
      src/Magnum/Test/DebugOutputGLTest.cpp
  17. 58
      src/Magnum/Test/DebugOutputTest.cpp

13
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} | |

4
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

5
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()

8
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);
}
};

241
src/Magnum/DebugMessage.cpp

@ -1,241 +0,0 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015
Vladimír Vondruš <mosra@centrum.cz>
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 <Corrade/Utility/Assert.h>
#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<Extensions::GL::KHR::debug>())
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<Extensions::GL::KHR::debug>())
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<const char> string) {
Context::current()->state().debug->messageInsertImplementation(source, type, id, severity, string);
}
void DebugMessage::insertImplementationNoOp(Source, Type, UnsignedInt, Severity, const Containers::ArrayReference<const char>) {}
void DebugMessage::insertImplementationKhr(const Source source, const Type type, const UnsignedInt id, const Severity severity, const Containers::ArrayReference<const char> 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<void>(source);
static_cast<void>(type);
static_cast<void>(id);
static_cast<void>(severity);
static_cast<void>(string);
CORRADE_ASSERT_UNREACHABLE();
#endif
}
void DebugMessage::insertImplementationExt(Source, Type, UnsignedInt, Severity, const Containers::ArrayReference<const char> string) {
#if !defined(CORRADE_TARGET_EMSCRIPTEN) && !defined(CORRADE_TARGET_NACL)
glInsertEventMarkerEXT(string.size(), string.data());
#else
static_cast<void>(string);
CORRADE_ASSERT_UNREACHABLE();
#endif
}
#ifndef MAGNUM_TARGET_GLES
void DebugMessage::insertImplementationGremedy(Source, Type, UnsignedInt, Severity, const Containers::ArrayReference<const char> string) {
glStringMarkerGREMEDY(string.size(), string.data());
}
#endif
void DebugMessage::setEnabledInternal(const GLenum source, const GLenum type, const GLenum severity, const std::initializer_list<UnsignedInt> ids, const bool enabled) {
Context::current()->state().debug->messageControlImplementation(source, type, severity, ids, enabled);
}
void DebugMessage::controlImplementationNoOp(GLenum, GLenum, GLenum, std::initializer_list<UnsignedInt>, bool) {}
void DebugMessage::controlImplementationKhr(const GLenum source, const GLenum type, const GLenum severity, const std::initializer_list<UnsignedInt> 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<void>(source);
static_cast<void>(type);
static_cast<void>(severity);
static_cast<void>(ids);
static_cast<void>(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<void>(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
}

393
src/Magnum/DebugMessage.h

@ -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š <mosra@centrum.cz>
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 <string>
#include <Corrade/Containers/Array.h>
#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<std::size_t size> 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<UnsignedInt> 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
* > &nbsp;&nbsp;&nbsp;&nbsp;Hello from OpenGL command stream!
*/
static void setDefaultCallback();
DebugMessage() = delete;
private:
static void insertInternal(Source source, Type type, UnsignedInt id, Severity severity, Containers::ArrayReference<const char> string);
static MAGNUM_LOCAL void insertImplementationNoOp(Source, Type, UnsignedInt, Severity, Containers::ArrayReference<const char>);
static MAGNUM_LOCAL void insertImplementationKhr(Source source, Type type, UnsignedInt id, Severity severity, Containers::ArrayReference<const char> string);
static MAGNUM_LOCAL void insertImplementationExt(Source, Type, UnsignedInt, Severity, Containers::ArrayReference<const char> string);
#ifndef MAGNUM_TARGET_GLES
static MAGNUM_LOCAL void insertImplementationGremedy(Source, Type, UnsignedInt, Severity, Containers::ArrayReference<const char> string);
#endif
static void setEnabledInternal(GLenum source, GLenum type, GLenum severity, std::initializer_list<UnsignedInt> ids, bool enabled);
static MAGNUM_LOCAL void controlImplementationNoOp(GLenum, GLenum, GLenum, std::initializer_list<UnsignedInt>, bool);
static MAGNUM_LOCAL void controlImplementationKhr(GLenum source, GLenum type, GLenum severity, std::initializer_list<UnsignedInt> 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

380
src/Magnum/DebugOutput.cpp

@ -0,0 +1,380 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015
Vladimír Vondruš <mosra@centrum.cz>
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 <Corrade/Utility/Assert.h>
#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<Extensions::GL::KHR::debug>())
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<Extensions::GL::KHR::debug>())
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<UnsignedInt> ids, const bool enabled) {
Context::current()->state().debug->controlImplementation(source, type, severity, ids, enabled);
}
void DebugOutput::controlImplementationNoOp(GLenum, GLenum, GLenum, std::initializer_list<UnsignedInt>, bool) {}
void DebugOutput::controlImplementationKhr(const GLenum source, const GLenum type, const GLenum severity, const std::initializer_list<UnsignedInt> 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<void>(source);
static_cast<void>(type);
static_cast<void>(severity);
static_cast<void>(ids);
static_cast<void>(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<void>(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<const char> string) {
Context::current()->state().debug->messageInsertImplementation(source, type, id, severity, string);
}
void DebugMessage::insertImplementationNoOp(Source, Type, UnsignedInt, DebugOutput::Severity, const Containers::ArrayReference<const char>) {}
void DebugMessage::insertImplementationKhr(const Source source, const Type type, const UnsignedInt id, const DebugOutput::Severity severity, const Containers::ArrayReference<const char> 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<void>(source);
static_cast<void>(type);
static_cast<void>(id);
static_cast<void>(severity);
static_cast<void>(string);
CORRADE_ASSERT_UNREACHABLE();
#endif
}
void DebugMessage::insertImplementationExt(Source, Type, UnsignedInt, DebugOutput::Severity, const Containers::ArrayReference<const char> string) {
#if !defined(CORRADE_TARGET_EMSCRIPTEN) && !defined(CORRADE_TARGET_NACL)
glInsertEventMarkerEXT(string.size(), string.data());
#else
static_cast<void>(string);
CORRADE_ASSERT_UNREACHABLE();
#endif
}
#ifndef MAGNUM_TARGET_GLES
void DebugMessage::insertImplementationGremedy(Source, Type, UnsignedInt, DebugOutput::Severity, const Containers::ArrayReference<const char> 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<Extensions::GL::KHR::debug>())
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<const char> 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<const char>) {}
void DebugGroup::pushImplementationKhr(const Source source, const UnsignedInt id, const Containers::ArrayReference<const char> 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<void>(source);
static_cast<void>(id);
static_cast<void>(message);
CORRADE_ASSERT_UNREACHABLE();
#endif
}
void DebugGroup::pushImplementationExt(Source, UnsignedInt, const Containers::ArrayReference<const char> message) {
#if !defined(CORRADE_TARGET_EMSCRIPTEN) && !defined(CORRADE_TARGET_NACL)
glPushGroupMarkerEXT(message.size(), message.data());
#else
static_cast<void>(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
}

895
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š <mosra@centrum.cz>
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 <string>
#include <Corrade/Containers/Array.h>
#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
> &nbsp;&nbsp;&nbsp;&nbsp;Scene rendering\n
> DebugOutput::Source::Application DebugOutput::Type::Marker 1337 DebugOutput::Severity::Notification\n
> &nbsp;&nbsp;&nbsp;&nbsp;Rendering transparent mesh\n
> ...\n
> DebugOutput::Source::Application DebugOutput::Type::PopGroup 42 DebugOutput::Severity::Notification\n
> &nbsp;&nbsp;&nbsp;&nbsp;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<UnsignedInt> 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
* > &nbsp;&nbsp;&nbsp;&nbsp;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<UnsignedInt> ids, bool enabled);
static MAGNUM_LOCAL void controlImplementationNoOp(GLenum, GLenum, GLenum, std::initializer_list<UnsignedInt>, bool);
static MAGNUM_LOCAL void controlImplementationKhr(GLenum source, GLenum type, GLenum severity, std::initializer_list<UnsignedInt> 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
> &nbsp;&nbsp;&nbsp;&nbsp;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<class ...T> static void setEnabled(T... args);
#else
CORRADE_DEPRECATED("use DebugOutput::setEnabled() instead") static void setEnabled(Source source, Type type, std::initializer_list<UnsignedInt> 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<DebugOutput::Callback>(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<std::size_t size> 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<const char> string);
static MAGNUM_LOCAL void insertImplementationNoOp(Source, Type, UnsignedInt, DebugOutput::Severity, Containers::ArrayReference<const char>);
static MAGNUM_LOCAL void insertImplementationKhr(Source source, Type type, UnsignedInt id, DebugOutput::Severity severity, Containers::ArrayReference<const char> string);
static MAGNUM_LOCAL void insertImplementationExt(Source, Type, UnsignedInt, DebugOutput::Severity, Containers::ArrayReference<const char> string);
#ifndef MAGNUM_TARGET_GLES
static MAGNUM_LOCAL void insertImplementationGremedy(Source, Type, UnsignedInt, DebugOutput::Severity, Containers::ArrayReference<const char> 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
> &nbsp;&nbsp;&nbsp;&nbsp;Scene rendering\n
> DebugOutput::Source::Application DebugOutput::Type::PopGroup 42 DebugOutput::Severity::Notification\n
> &nbsp;&nbsp;&nbsp;&nbsp;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<std::size_t size> 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<std::size_t size> 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<const char> message);
static MAGNUM_LOCAL void pushImplementationNoOp(Source source, UnsignedInt id, Containers::ArrayReference<const char> message);
static MAGNUM_LOCAL void pushImplementationKhr(Source source, UnsignedInt id, Containers::ArrayReference<const char> message);
static MAGNUM_LOCAL void pushImplementationExt(Source source, UnsignedInt id, Containers::ArrayReference<const char> 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

18
src/Magnum/Implementation/DebugState.cpp

@ -31,15 +31,23 @@
namespace Magnum { namespace Implementation {
DebugState::DebugState(Context& context, std::vector<std::string>& extensions): maxLabelLength(0), maxLoggedMessages(0), maxMessageLength(0), messageCallback(nullptr) {
DebugState::DebugState(Context& context, std::vector<std::string>& extensions):
maxLabelLength{0},
maxLoggedMessages{0},
maxMessageLength{0},
maxStackDepth{0},
messageCallback(nullptr)
{
if(context.isExtensionSupported<Extensions::GL::KHR::debug>()) {
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<Extensions::GL::EXT::debug_label>()) {
@ -64,8 +72,8 @@ DebugState::DebugState(Context& context, std::vector<std::string>& extensions):
#endif
} else messageInsertImplementation = &DebugMessage::insertImplementationNoOp;
messageControlImplementation = &DebugMessage::controlImplementationNoOp;
messageCallbackImplementation = &DebugMessage::callbackImplementationNoOp;
controlImplementation = &DebugOutput::controlImplementationNoOp;
callbackImplementation = &DebugOutput::callbackImplementationNoOp;
}
}

14
src/Magnum/Implementation/DebugState.h

@ -28,7 +28,7 @@
#include <string>
#include <vector>
#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<const char>);
void(*messageInsertImplementation)(DebugMessage::Source, DebugMessage::Type, UnsignedInt, DebugMessage::Severity, Containers::ArrayReference<const char>);
void(*messageControlImplementation)(GLenum, GLenum, GLenum, std::initializer_list<UnsignedInt>, bool);
void(*messageCallbackImplementation)(DebugMessage::Callback, const void*);
void(*messageInsertImplementation)(DebugMessage::Source, DebugMessage::Type, UnsignedInt, DebugOutput::Severity, Containers::ArrayReference<const char>);
void(*controlImplementation)(GLenum, GLenum, GLenum, std::initializer_list<UnsignedInt>, bool);
void(*callbackImplementation)(DebugOutput::Callback, const void*);
void(*pushGroupImplementation)(DebugGroup::Source, UnsignedInt, Containers::ArrayReference<const char>);
void(*popGroupImplementation)();
GLint maxLabelLength, maxLoggedMessages, maxMessageLength;
DebugMessage::Callback messageCallback;
GLint maxLabelLength, maxLoggedMessages, maxMessageLength, maxStackDepth;
DebugOutput::Callback messageCallback;
};
}}

6
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

9
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}
*/

6
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<Extensions::GL::KHR::debug>()) {
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);
}
}

4
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})

144
src/Magnum/Test/DebugGLTest.cpp

@ -1,144 +0,0 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015
Vladimír Vondruš <mosra@centrum.cz>
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 <sstream>
#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<Extensions::GL::KHR::debug>() ||
Context::current()->isExtensionSupported<Extensions::GL::EXT::debug_marker>()
#ifndef MAGNUM_TARGET_GLES
|| Context::current()->isExtensionSupported<Extensions::GL::GREMEDY::string_marker>()
#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<Extensions::GL::KHR::debug>())
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<Extensions::GL::KHR::debug>() ||
(!Context::current()->isExtensionSupported<Extensions::GL::EXT::debug_marker>()
#ifndef MAGNUM_TARGET_GLES
&& !Context::current()->isExtensionSupported<Extensions::GL::GREMEDY::string_marker>()
#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<Extensions::GL::KHR::debug>())
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)

211
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š <mosra@centrum.cz>
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 <sstream>
#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<Extensions::GL::KHR::debug>())
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<Extensions::GL::KHR::debug>())
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<Extensions::GL::KHR::debug>() ||
Context::current()->isExtensionSupported<Extensions::GL::EXT::debug_marker>()
#ifndef MAGNUM_TARGET_GLES
|| Context::current()->isExtensionSupported<Extensions::GL::GREMEDY::string_marker>()
#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<Extensions::GL::KHR::debug>())
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<Extensions::GL::KHR::debug>() ||
(!Context::current()->isExtensionSupported<Extensions::GL::EXT::debug_marker>()
#ifndef MAGNUM_TARGET_GLES
&& !Context::current()->isExtensionSupported<Extensions::GL::GREMEDY::string_marker>()
#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<Extensions::GL::KHR::debug>() ||
Context::current()->isExtensionSupported<Extensions::GL::EXT::debug_marker>())
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<Extensions::GL::KHR::debug>())
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<Extensions::GL::KHR::debug>() ||
!Context::current()->isExtensionSupported<Extensions::GL::EXT::debug_marker>())
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)

58
src/Magnum/Test/DebugMessageTest.cpp → src/Magnum/Test/DebugOutputTest.cpp

@ -26,42 +26,70 @@
#include <sstream>
#include <Corrade/TestSuite/Tester.h>
#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)
Loading…
Cancel
Save