#ifndef Magnum_GL_DebugOutput_h
#define Magnum_GL_DebugOutput_h
/*
    This file is part of Magnum.

    Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019
              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.
*/

#ifndef MAGNUM_TARGET_WEBGL
/** @file
 * @brief Class @ref Magnum::GL::DebugOutput, @ref Magnum::GL::DebugMessage, @ref Magnum::GL::DebugGroup
 */
#endif

#include <string>
#include <Corrade/Containers/ArrayView.h>

#include "Magnum/Magnum.h"
#include "Magnum/GL/OpenGL.h"
#include "Magnum/GL/visibility.h"

#ifndef MAGNUM_TARGET_WEBGL
namespace Magnum { namespace GL {

namespace Implementation { struct DebugState; }

/**
@brief Debug output

Manages OpenGL debug output. The debug messages are emitted either from driver
(such as GL error descriptions and various performance and optimization hints)
or from third party software and the application itself using @ref DebugMessage
and @ref DebugGroup, which can be also used to mark various portions of command
stream in various graphics debuggers, such as ApiTrace or gDEBugger.

@section GL-Magnum-DebugOutput-usage Basic usage

Support for debug output is provided by OpenGL 4.3 / OpenGL ES 3.2 or
@gl_extension{KHR,debug} (desktop/ES extension, covered also by
@gl_extension{ANDROID,extension_pack_es31a}). Subset of the functionality is
provided also by @gl_extension{EXT,debug_marker} (desktop/ES extensions) or
@gl_extension{GREMEDY,string_marker} (desktop only extension).

With OpenGL 4.3 / OpenGL ES 3.2 or @gl_extension{KHR,debug} desktop/ES extension,
the debug output needs to be enabled first. It can be enabled globally using
@ref Platform::Sdl2Application::GLConfiguration::Flag::Debug "Platform::*Application::GLConfiguration::Flag::Debug"
when creating context or only for some portions of the code using
@ref Renderer::Feature::DebugOutput. If enabled globally, some OpenGL drivers
may provide additional debugging information. In addition to that you can
control the output at even finer granularity using @ref setEnabled().

You can gather the messages either through graphics debugger or in the
application itself by setting up message callback using @ref setCallback() or
@ref setDefaultCallback(). You might also want to enable
@ref Renderer::Feature::DebugOutputSynchronous. Example usage, completely with
@ref DebugGroup and @ref DebugMessage is below.

@m_class{m-block m-success}

@par Enabling debug output from the command line / environment
    Apart from setting up the debug output callbacks manually, it's also
    possible to enable it conveniently using the `--magnum-gpu-validation`
    @ref GL-Context-command-line "command-line or environment option" --- ideal
    for quick debugging of rendering issues. If you are using application
    classes from the @ref Platform namespace, this option also ensures that
    @ref Platform::Sdl2Application::GLConfiguration::Flag::Debug "GLConfiguration::Flag::Debug"
    is passed for context creation, both with windowed and windowless
    application implementations.

@snippet MagnumGL.cpp DebugOutput-usage

With default callback the group entering/leaving and the inserted message (and
possibly also other messages) will be printed on standard output:

@code{.shell-session}
Debug output: application debug group enter (42): Scene rendering
Debug output: application marker (1337): Rendering transparent mesh
...
Debug output: application debug group leave (42): Scene rendering
@endcode

If only @gl_extension{EXT,debug_marker} or @gl_extension{GREMEDY,string_marker} are
supported, only user-inserted messages and debug groups are supported and they
can be seen only through graphics debugger.

If OpenGL 4.3 is not supported and neither @gl_extension{KHR,debug} nor
@gl_extension{EXT,debug_marker} nor @gl_extension{GREMEDY,string_marker} are
available, all the functions are essentially a no-op.

Besides inserting messages into GL command stream you can also annotate OpenGL
objects with labels. See @ref AbstractQuery::setLabel(),
@ref AbstractShaderProgram::setLabel(), @ref AbstractTexture::setLabel(),
@ref Buffer::setLabel(), @ref Framebuffer::setLabel(), @ref Mesh::setLabel(),
@ref Renderbuffer::setLabel(), @ref Shader::setLabel() and
@ref TransformFeedback::setLabel() for more information.

@requires_gles Debug output is not available in WebGL.
*/
class MAGNUM_GL_EXPORT DebugOutput {
    friend Implementation::DebugState;

    public:
        /**
         * @brief Message source
         *
         * @see @ref setEnabled()
         * @m_enum_values_as_keywords
         */
        enum class Source: GLenum {
            /** OpenGL */
            #ifndef MAGNUM_TARGET_GLES2
            Api = GL_DEBUG_SOURCE_API,
            #else
            Api = GL_DEBUG_SOURCE_API_KHR,
            #endif

            /** Window system (GLX, WGL) */
            #ifndef MAGNUM_TARGET_GLES2
            WindowSystem = GL_DEBUG_SOURCE_WINDOW_SYSTEM,
            #else
            WindowSystem = GL_DEBUG_SOURCE_WINDOW_SYSTEM_KHR,
            #endif

            /** Shader compiler */
            #ifndef MAGNUM_TARGET_GLES2
            ShaderCompiler = GL_DEBUG_SOURCE_SHADER_COMPILER,
            #else
            ShaderCompiler = GL_DEBUG_SOURCE_SHADER_COMPILER_KHR,
            #endif

            /** External debugger or third-party middleware */
            #ifndef MAGNUM_TARGET_GLES2
            ThirdParty = GL_DEBUG_SOURCE_THIRD_PARTY,
            #else
            ThirdParty = GL_DEBUG_SOURCE_THIRD_PARTY_KHR,
            #endif

            /** The application */
            #ifndef MAGNUM_TARGET_GLES2
            Application = GL_DEBUG_SOURCE_APPLICATION,
            #else
            Application = GL_DEBUG_SOURCE_APPLICATION_KHR,
            #endif

            /** Any other source */
            #ifndef MAGNUM_TARGET_GLES2
            Other = GL_DEBUG_SOURCE_OTHER
            #else
            Other = GL_DEBUG_SOURCE_OTHER_KHR
            #endif
        };

        /**
         * @brief Message type
         *
         * @see @ref setEnabled()
         * @m_enum_values_as_keywords
         */
        enum class Type: GLenum {
            /** OpenGL error */
            #ifndef MAGNUM_TARGET_GLES2
            Error = GL_DEBUG_TYPE_ERROR,
            #else
            Error = GL_DEBUG_TYPE_ERROR_KHR,
            #endif

            /** Behavior that has been marked for deprecation */
            #ifndef MAGNUM_TARGET_GLES2
            DeprecatedBehavior = GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR,
            #else
            DeprecatedBehavior = GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_KHR,
            #endif

            /** Behavior that is undefined according to the specification */
            #ifndef MAGNUM_TARGET_GLES2
            UndefinedBehavior = GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR,
            #else
            UndefinedBehavior = GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_KHR,
            #endif

            /** Non-portable usage of extensions or shaders */
            #ifndef MAGNUM_TARGET_GLES2
            Portability = GL_DEBUG_TYPE_PORTABILITY,
            #else
            Portability = GL_DEBUG_TYPE_PORTABILITY_KHR,
            #endif

            /** Implementation-dependent performance warning */
            #ifndef MAGNUM_TARGET_GLES2
            Performance = GL_DEBUG_TYPE_PERFORMANCE,
            #else
            Performance = GL_DEBUG_TYPE_PERFORMANCE_KHR,
            #endif

            /** Annotation of the command stream */
            #ifndef MAGNUM_TARGET_GLES2
            Marker = GL_DEBUG_TYPE_MARKER,
            #else
            Marker = GL_DEBUG_TYPE_MARKER_KHR,
            #endif

            /** Entering a debug group */
            #ifndef MAGNUM_TARGET_GLES2
            PushGroup = GL_DEBUG_TYPE_PUSH_GROUP,
            #else
            PushGroup = GL_DEBUG_TYPE_PUSH_GROUP_KHR,
            #endif

            /** Leaving a debug group */
            #ifndef MAGNUM_TARGET_GLES2
            PopGroup = GL_DEBUG_TYPE_POP_GROUP,
            #else
            PopGroup = GL_DEBUG_TYPE_POP_GROUP_KHR,
            #endif

            /** Any other type */
            #ifndef MAGNUM_TARGET_GLES2
            Other = GL_DEBUG_TYPE_OTHER,
            #else
            Other = GL_DEBUG_TYPE_OTHER_KHR,
            #endif
        };

        /**
         * @brief Message severity
         *
         * @see @ref setEnabled()
         * @m_enum_values_as_keywords
         */
        enum class Severity: GLenum {
            /**
             * Any OpenGL error, dangerous undefined behavior, shader
             * compilation errors.
             */
            #ifndef MAGNUM_TARGET_GLES2
            High = GL_DEBUG_SEVERITY_HIGH,
            #else
            High = GL_DEBUG_SEVERITY_HIGH_KHR,
            #endif

            /**
             * Severe performance warnings, shader compilation warnings, use of
             * deprecated behavior.
             */
            #ifndef MAGNUM_TARGET_GLES2
            Medium = GL_DEBUG_SEVERITY_MEDIUM,
            #else
            Medium = GL_DEBUG_SEVERITY_MEDIUM_KHR,
            #endif

            /** Minor performance warnings, trivial undefined behavior. */
            #ifndef MAGNUM_TARGET_GLES2
            Low = GL_DEBUG_SEVERITY_LOW,
            #else
            Low = GL_DEBUG_SEVERITY_LOW_KHR,
            #endif

            /** Any message other than error or performance warning. */
            #ifndef MAGNUM_TARGET_GLES2
            Notification = GL_DEBUG_SEVERITY_NOTIFICATION
            #else
            Notification = GL_DEBUG_SEVERITY_NOTIFICATION_KHR
            #endif
        };

        /**
         * @brief Debug callback
         *
         * @see @ref setCallback()
         */
        typedef void(*Callback)(Source, Type, UnsignedInt, Severity, const std::string&, const void*);

        /**
         * @brief Max count of debug messages in log
         *
         * The result is cached, repeated queries don't result in repeated
         * OpenGL calls. If OpenGL 4.3 / OpenGL ES 3.2 is not supported and
         * @gl_extension{KHR,debug} desktop or ES extension (covered also by
         * @gl_extension{ANDROID,extension_pack_es31a}) is not available, returns
         * @cpp 0 @ce.
         * @see @fn_gl{Get} with @def_gl_keyword{MAX_DEBUG_LOGGED_MESSAGES}
         */
        static Int maxLoggedMessages();

        /**
         * @brief Max debug message length
         *
         * The result is cached, repeated queries don't result in repeated
         * OpenGL calls. If OpenGL 4.3 / OpenGL ES 3.2 is not supported and
         * @gl_extension{KHR,debug} desktop or ES extension (covered also by
         * @gl_extension{ANDROID,extension_pack_es31a}) is not available, returns
         * @cpp 0 @ce.
         * @see @fn_gl{Get} with @def_gl_keyword{MAX_DEBUG_MESSAGE_LENGTH}
         */
        static Int maxMessageLength();

        /**
         * @brief Enable or disable particular output type
         *
         * @attention If any @ref DebugGroup is active when making this call,
         *      the setting will be remembered only for the time in which the
         *      group is active and leaving it will revert the setting to state
         *      set in parent debug group. See @ref DebugGroup documentation
         *      for more information.
         *
         * If OpenGL 4.3 / OpenGL ES 3.2 is not supported and @gl_extension{KHR,debug}
         * desktop or ES extension (covered also by
         * @gl_extension{ANDROID,extension_pack_es31a}) is not available, this
         * function does nothing.
         * @see @ref Renderer::Feature::DebugOutput,
         *      @fn_gl_keyword{DebugMessageControl}
         */
        static void setEnabled(Source source, Type type, std::initializer_list<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 /
         * OpenGL ES 3.2 is not supported and @gl_extension{KHR,debug} desktop or
         * ES extension (covered also by @gl_extension{ANDROID,extension_pack_es31a})
         * is not available, this function does nothing.
         *
         * @attention The function is not necessarily called from the same
         *      thread as the one that caused the message to appear --- in
         *      particular, you can't assume the @ref GL::Context will be
         *      present in the callback context. It might work on some drivers,
         *      but not on others.
         *
         * @see @ref setDefaultCallback(),
         *      @ref Renderer::Feature::DebugOutputSynchronous,
         *      @fn_gl_keyword{DebugMessageCallback}
         */
        static void setCallback(Callback callback, const void* userParam = nullptr);

        /**
         * @brief Set default debug message callback
         *
         * See @ref setCallback() for more information. The message is printed
         * to @ref Corrade::Utility::Debug "Debug" output in the following
         * format:
         *
         * @snippet MagnumGL.cpp DebugOutput-setDefaultCallback
         *
         * @code{.shell-session}
         * Debug output: application marker (1337): Hello from OpenGL command stream!
         * @endcode
         */
        static void setDefaultCallback();

        /** @brief There's no point in having an instance of this class */
        DebugOutput() = delete;

    private:
        static void setEnabledInternal(GLenum source, GLenum type, GLenum severity, std::initializer_list<UnsignedInt> ids, bool enabled);
        static MAGNUM_GL_LOCAL void controlImplementationNoOp(GLenum, GLenum, GLenum, std::initializer_list<UnsignedInt>, bool);
        #ifndef MAGNUM_TARGET_GLES2
        static MAGNUM_GL_LOCAL void controlImplementationKhrDesktopES32(GLenum source, GLenum type, GLenum severity, std::initializer_list<UnsignedInt> ids, bool enabled);
        #endif
        #ifdef MAGNUM_TARGET_GLES
        static MAGNUM_GL_LOCAL void controlImplementationKhrES(GLenum source, GLenum type, GLenum severity, std::initializer_list<UnsignedInt> ids, bool enabled);
        #endif

        static MAGNUM_GL_LOCAL void callbackImplementationNoOp(Callback, const void*);
        #ifndef MAGNUM_TARGET_GLES2
        static MAGNUM_GL_LOCAL void callbackImplementationKhrDesktopES32(Callback callback, const void* userParam);
        #endif
        #ifdef MAGNUM_TARGET_GLES
        static MAGNUM_GL_LOCAL void callbackImplementationKhrES(Callback callback, const void* userParam);
        #endif
};

/** @debugoperatorclassenum{DebugOutput,DebugOutput::Source} */
MAGNUM_GL_EXPORT Debug& operator<<(Debug& debug, DebugOutput::Source value);

/** @debugoperatorclassenum{DebugOutput,DebugOutput::Type} */
MAGNUM_GL_EXPORT Debug& operator<<(Debug& debug, DebugOutput::Type value);

/** @debugoperatorclassenum{DebugOutput,DebugOutput::Severity} */
MAGNUM_GL_EXPORT Debug& operator<<(Debug& debug, DebugOutput::Severity value);

/**
@brief Debug message

Allows inserting messages GL command stream with labels, useful for example
with conjunction with various graphics debuggers, such as ApiTrace or
gDEBugger.

@section GL-DebugMessage-usage Basic usage

See @ref DebugOutput for introduction.

If OpenGL 4.3 / OpenGL ES 3.2 is supported or @gl_extension{KHR,debug} desktop or
ES extension (covered also by @gl_extension{ANDROID,extension_pack_es31a}) is
available and default debug output callback is enabled for given kind of
messages, the inserted message will be printed on standard output in the
following form:

@snippet MagnumGL.cpp DebugMessage-usage

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

If only @gl_extension{EXT,debug_marker} or @gl_extension{GREMEDY,string_marker} are
available, the message can be seen only through graphics debugger.

If OpenGL 4.3 is not supported and neither @gl_extension{KHR,debug} nor
@gl_extension{EXT,debug_marker} nor @gl_extension{GREMEDY,string_marker} are
available, the function is essentially a no-op.

@section GL-DebugMessage-performance-notes Performance notes

If you ensure that you always use the @cpp const char @ce overload of
@ref insert() and the debug output is either not supported or turned off, the
calls will not result in any allocations and thus won't have any negative
performance effects.

@see @ref DebugGroup
@requires_gles Debug output is not available in WebGL.
*/
class MAGNUM_GL_EXPORT DebugMessage {
    friend Implementation::DebugState;

    public:
        /**
         * @brief Message source
         *
         * @see @ref insert()
         * @m_enum_values_as_keywords
         */
        enum class Source: GLenum {
            /** External debugger or third-party middleware */
            #ifndef MAGNUM_TARGET_GLES2
            ThirdParty = GL_DEBUG_SOURCE_THIRD_PARTY,
            #else
            ThirdParty = GL_DEBUG_SOURCE_THIRD_PARTY_KHR,
            #endif

            /** The application */
            #ifndef MAGNUM_TARGET_GLES2
            Application = GL_DEBUG_SOURCE_APPLICATION
            #else
            Application = GL_DEBUG_SOURCE_APPLICATION_KHR
            #endif
        };

        /**
         * @brief Message type
         *
         * @see @ref insert()
         * @m_enum_values_as_keywords
         */
        enum class Type: GLenum {
            /** OpenGL error */
            #ifndef MAGNUM_TARGET_GLES2
            Error = GL_DEBUG_TYPE_ERROR,
            #else
            Error = GL_DEBUG_TYPE_ERROR_KHR,
            #endif

            /** Behavior that has been marked for deprecation */
            #ifndef MAGNUM_TARGET_GLES2
            DeprecatedBehavior = GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR,
            #else
            DeprecatedBehavior = GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_KHR,
            #endif

            /** Behavior that is undefined according to the specification */
            #ifndef MAGNUM_TARGET_GLES2
            UndefinedBehavior = GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR,
            #else
            UndefinedBehavior = GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_KHR,
            #endif

            /** Non-portable usage of extensions or shaders */
            #ifndef MAGNUM_TARGET_GLES2
            Portability = GL_DEBUG_TYPE_PORTABILITY,
            #else
            Portability = GL_DEBUG_TYPE_PORTABILITY_KHR,
            #endif

            /** Implementation-dependent performance warning */
            #ifndef MAGNUM_TARGET_GLES2
            Performance = GL_DEBUG_TYPE_PERFORMANCE,
            #else
            Performance = GL_DEBUG_TYPE_PERFORMANCE_KHR,
            #endif

            /** Annotation of the command stream */
            #ifndef MAGNUM_TARGET_GLES2
            Marker = GL_DEBUG_TYPE_MARKER,
            #else
            Marker = GL_DEBUG_TYPE_MARKER_KHR,
            #endif

            /** Any other type */
            #ifndef MAGNUM_TARGET_GLES2
            Other = GL_DEBUG_TYPE_OTHER
            #else
            Other = GL_DEBUG_TYPE_OTHER_KHR
            #endif
        };

        /**
         * @brief Insert message
         * @param source    Message source
         * @param type      Message type
         * @param id        Message-specific ID
         * @param severity  Message severity
         * @param string    The actual message
         *
         * If OpenGL 4.3 / OpenGL ES 3.2 is not supported and neither
         * @gl_extension{KHR,debug} (covered also by @gl_extension{ANDROID,extension_pack_es31a})
         * nor @gl_extension{EXT,debug_marker} (desktop or ES extensions) nor
         * @gl_extension{GREMEDY,string_marker} (desktop only extension) are
         * available, this function does nothing.
         *
         * If @gl_extension{KHR,debug} is not available and only @gl_extension{EXT,debug_marker}
         * or @gl_extension{GREMEDY,string_marker} are available, only @p string
         * is used and all other parameters are ignored.
         * @see @ref DebugOutput::maxMessageLength(),
         *      @fn_gl_keyword{DebugMessageInsert},
         *      @fn_gl_extension_keyword{InsertEventMarker,EXT,debug_marker} or
         *      @fn_gl_extension_keyword{StringMarker,GREMEDY,string_marker}
         */
        static void insert(Source source, Type type, UnsignedInt id, DebugOutput::Severity severity, const std::string& string) {
            insertInternal(source, type, id, severity, {string.data(), string.size()});
        }

        /** @overload */
        template<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::ArrayView<const char> string);
        static MAGNUM_GL_LOCAL void insertImplementationNoOp(Source, Type, UnsignedInt, DebugOutput::Severity, Containers::ArrayView<const char>);
        #ifndef MAGNUM_TARGET_GLES2
        static MAGNUM_GL_LOCAL void insertImplementationKhrDesktopES32(Source source, Type type, UnsignedInt id, DebugOutput::Severity severity, Containers::ArrayView<const char> string);
        #endif
        #ifdef MAGNUM_TARGET_GLES
        static MAGNUM_GL_LOCAL void insertImplementationKhrES(Source source, Type type, UnsignedInt id, DebugOutput::Severity severity, Containers::ArrayView<const char> string);
        #endif
        static MAGNUM_GL_LOCAL void insertImplementationExt(Source, Type, UnsignedInt, DebugOutput::Severity, Containers::ArrayView<const char> string);
        #ifndef MAGNUM_TARGET_GLES
        static MAGNUM_GL_LOCAL void insertImplementationGremedy(Source, Type, UnsignedInt, DebugOutput::Severity, Containers::ArrayView<const char> string);
        #endif
};

/** @debugoperatorclassenum{DebugMessage,DebugMessage::Source} */
MAGNUM_GL_EXPORT Debug& operator<<(Debug& debug, DebugMessage::Source value);

/** @debugoperatorclassenum{DebugMessage,DebugMessage::Type} */
MAGNUM_GL_EXPORT Debug& operator<<(Debug& debug, DebugMessage::Type value);

/**
@brief Debug group

Allows marking portions of GL command stream with labels, useful for example
with conjunction with various graphics debuggers, such as Apitrace or gDEBugger.

@section GL-DebugGroup-usage Basic usage

See @ref DebugOutput for introduction.

Easiest way is to push debug group by creating instance and pop it
automatically at the end of scope:

@snippet MagnumGL.cpp DebugGroup-usage1

If, for some reason, you need to pop in different scope, you can call @ref push()
and @ref pop() manually:

@snippet MagnumGL.cpp DebugGroup-usage2

If OpenGL 4.3 / OpenGL ES 3.2 is supported or @gl_extension{KHR,debug} desktop or
ES extension (covered also by @gl_extension{ANDROID,extension_pack_es31a}) is
available and the default debug output callback is enabled for these kinds of
messages, the group entering and leaving will be printed on standard output in
the following form:

@code{.shell-session}
Debug output: application debug group enter (42): Scene rendering
Debug output: application debug group leave (42): Scene rendering
@endcode

If only @gl_extension{EXT,debug_marker} is available, the group can be seen only
through graphics debugger.

If OpenGL 4.3 / OpenGL ES 3.2 is not supported and neither @gl_extension{KHR,debug}
nor @gl_extension{EXT,debug_marker} are available, the functions are essentially a
no-op.

@attention To avoid accidental debug group stack overflow/underflow, you cannot
    call @ref push() again when the group is already pushed onto the stack,
    similarly for @ref pop(). So if you want to have nested debug groups, you
    need to create one instance for each level.

@section GL-DebugGroup-volume-control Interaction with debug output volume control

Besides putting hierarchical messages in debug output, the group also affects
settings done by @ref DebugOutput::setEnabled(). Entering debug group inherits
the settings from previously active debug group, call to
@ref DebugOutput::setEnabled() will be remembered only for
the time in which given group is active and leaving it will revert the setting
to state set in parent debug group. No state is preserved, thus calling
@ref push() after previous @ref pop() will not restore settings done when the
group was active previously.

@section GL-DebugGroup-performance-notes Performance notes

If you ensure that you always use the @cpp const char @ce overload of
@ref push() and the debug output is either not supported or turned off, the
calls will not result in any allocations and thus won't have any negative
performance effects.

@see @ref DebugMessage
@requires_gles Debug output is not available in WebGL.
*/
class MAGNUM_GL_EXPORT DebugGroup {
    friend Implementation::DebugState;

    public:
        /**
         * @brief Message source
         *
         * @see @ref DebugGroup(), @ref push()
         */
        enum class Source: GLenum {
            /** External debugger or third-party middleware */
            #ifndef MAGNUM_TARGET_GLES2
            ThirdParty = GL_DEBUG_SOURCE_THIRD_PARTY,
            #else
            ThirdParty = GL_DEBUG_SOURCE_THIRD_PARTY_KHR,
            #endif

            /** The application */
            #ifndef MAGNUM_TARGET_GLES2
            Application = GL_DEBUG_SOURCE_APPLICATION
            #else
            Application = GL_DEBUG_SOURCE_APPLICATION_KHR
            #endif
        };

        /**
         * @brief Max debug group stack depth
         *
         * The result is cached, repeated queries don't result in repeated
         * OpenGL calls. If OpenGL 4.3 / OpenGL ES 3.2 is not supported and
         * @gl_extension{KHR,debug} desktop or ES extension (covered also by
         * @gl_extension{ANDROID,extension_pack_es31a}) is not available, returns
         * `0`.
         * @see @fn_gl{Get} with @def_gl_keyword{MAX_DEBUG_GROUP_STACK_DEPTH}
         */
        static Int maxStackDepth();

        /**
         * @brief Default constructor
         *
         * Doesn't do anything. Call @ref push() to enter debug group.
         */
        explicit DebugGroup(): _active{false} {}

        /**
         * @brief Constructor
         *
         * Calls @ref push().
         * @see @link ~DebugGroup() @endlink, @ref pop()
         */
        explicit DebugGroup(Source source, UnsignedInt id, const std::string& message): DebugGroup{} {
            push(source, id, message);
        }

        /** @overload */
        template<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 / OpenGL ES 3.2 is not supported and neither
         * @gl_extension{KHR,debug} (covered also by @gl_extension{ANDROID,extension_pack_es31a})
         * nor @gl_extension{EXT,debug_marker} is available, this function does
         * nothing. If @gl_extension{KHR,debug} is not available and only
         * @gl_extension{EXT,debug_marker} is available, only @p message is used
         * and all other parameters are ignored.
         * @see @ref pop(), @ref maxStackDepth(), @ref DebugOutput::maxMessageLength(),
         *      @ref Renderer::Error::StackOverflow, @fn_gl_keyword{PushDebugGroup}
         *      or @fn_gl_extension_keyword{PushGroupMarker,EXT,debug_marker}
         */
        void push(Source source, UnsignedInt id, const std::string& message) {
            pushInternal(source, id, {message.data(), message.size()});
        }

        /** @overload */
        template<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 / OpenGL ES 3.2 is not supported and neither
         * @gl_extension{KHR,debug} (covered also by @gl_extension{ANDROID,extension_pack_es31a})
         * nor @gl_extension{EXT,debug_marker} is available, this function does
         * nothing.
         * @see @ref push(), @ref Renderer::Error::StackUnderflow,
         *      @fn_gl_keyword{PopDebugGroup} or
         *      @fn_gl_extension_keyword{PopGroupMarker,EXT,debug_marker}
         */
        void pop();

    private:
        void pushInternal(Source source, UnsignedInt id, Containers::ArrayView<const char> message);

        static MAGNUM_GL_LOCAL void pushImplementationNoOp(Source source, UnsignedInt id, Containers::ArrayView<const char> message);
        #ifndef MAGNUM_TARGET_GLES2
        static MAGNUM_GL_LOCAL void pushImplementationKhrDesktopES32(Source source, UnsignedInt id, Containers::ArrayView<const char> message);
        #endif
        #ifdef MAGNUM_TARGET_GLES
        static MAGNUM_GL_LOCAL void pushImplementationKhrES(Source source, UnsignedInt id, Containers::ArrayView<const char> message);
        #endif
        static MAGNUM_GL_LOCAL void pushImplementationExt(Source source, UnsignedInt id, Containers::ArrayView<const char> message);

        static MAGNUM_GL_LOCAL void popImplementationNoOp();
        #ifndef MAGNUM_TARGET_GLES2
        static MAGNUM_GL_LOCAL void popImplementationKhrDesktopES32();
        #endif
        #ifdef MAGNUM_TARGET_GLES
        static MAGNUM_GL_LOCAL void popImplementationKhrES();
        #endif
        static MAGNUM_GL_LOCAL void popImplementationExt();

        bool _active;
};

/** @debugoperatorclassenum{DebugGroup,DebugGroup::Source} */
MAGNUM_GL_EXPORT Debug& operator<<(Debug& debug, DebugGroup::Source value);

/* Exposed for testing */
namespace Implementation {
    MAGNUM_GL_EXPORT void defaultDebugCallback(DebugOutput::Source source, DebugOutput::Type type, UnsignedInt id, DebugOutput::Severity severity, const std::string& string, std::ostream* output);
}

}}
#else
#error this header is not available in WebGL build
#endif

#endif
