From 2cb5f1740ecd0565d6071a024a24e0b08e770f87 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Wed, 17 Aug 2016 20:33:10 +0200 Subject: [PATCH] Add OpenGL debug messages to the console --- src/CMakeLists.txt | 1 + src/gl/system/gl_cvars.h | 3 + src/gl/system/gl_debug.cpp | 249 +++++++++++++++++++++++++++++++ src/gl/system/gl_debug.h | 31 ++++ src/gl/system/gl_framebuffer.cpp | 4 + src/gl/system/gl_framebuffer.h | 5 + 6 files changed, 293 insertions(+) create mode 100644 src/gl/system/gl_debug.cpp create mode 100644 src/gl/system/gl_debug.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e002fc66f9..c74c9534d3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1117,6 +1117,7 @@ set( FASTMATH_SOURCES gl/shaders/gl_lensshader.cpp gl/system/gl_interface.cpp gl/system/gl_framebuffer.cpp + gl/system/gl_debug.cpp gl/system/gl_menu.cpp gl/system/gl_wipe.cpp gl/system/gl_load.c diff --git a/src/gl/system/gl_cvars.h b/src/gl/system/gl_cvars.h index 4b28bb6670..0c31f53a8d 100644 --- a/src/gl/system/gl_cvars.h +++ b/src/gl/system/gl_cvars.h @@ -54,4 +54,7 @@ EXTERN_CVAR(Float, gl_lens_k) EXTERN_CVAR(Float, gl_lens_kcube) EXTERN_CVAR(Float, gl_lens_chromatic) +EXTERN_CVAR(Int, gl_debug_level) +EXTERN_CVAR(Bool, gl_debug_breakpoint) + #endif // _GL_INTERN_H diff --git a/src/gl/system/gl_debug.cpp b/src/gl/system/gl_debug.cpp new file mode 100644 index 0000000000..5c3e602765 --- /dev/null +++ b/src/gl/system/gl_debug.cpp @@ -0,0 +1,249 @@ +/* +** gl_debig.cpp +** OpenGL debugging support functions +** +**--------------------------------------------------------------------------- +** Copyright 2016 Magnus Norddahl +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** 4. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** 5. Full disclosure of the entire project's source code, except for third +** party libraries is mandatory. (NOTE: This clause is non-negotiable!) +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "templates.h" +#include "gl/system/gl_system.h" +#include "gl/system/gl_interface.h" +#include "gl/system/gl_debug.h" + +#ifndef _MSC_VER +#include +#endif + +CVAR(Int, gl_debug_level, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); +CVAR(Bool, gl_debug_breakpoint, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); + +//----------------------------------------------------------------------------- +// +// Updates OpenGL debugging state +// +//----------------------------------------------------------------------------- + +void FGLDebug::Update() +{ + SetupBreakpointMode(); + UpdateLoggingLevel(); + OutputMessageLog(); +} + +//----------------------------------------------------------------------------- +// +// Turns on synchronous debugging on and off based on gl_debug_breakpoint +// +// Allows getting the debugger to break exactly at the OpenGL function emitting +// a message. +// +//----------------------------------------------------------------------------- + +void FGLDebug::SetupBreakpointMode() +{ + if (mBreakpointMode != gl_debug_breakpoint) + { + if (gl_debug_breakpoint) + { + glDebugMessageCallback(&FGLDebug::DebugCallback, this); + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); + } + else + { + glDebugMessageCallback(nullptr, nullptr); + glDisable(GL_DEBUG_OUTPUT_SYNCHRONOUS); + } + mBreakpointMode = gl_debug_breakpoint; + } +} + +//----------------------------------------------------------------------------- +// +// Tells OpenGL which debug messages we are interested in +// +//----------------------------------------------------------------------------- + +void FGLDebug::UpdateLoggingLevel() +{ + int level = gl_debug_level; + if (level != mCurrentLevel) + { + glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_HIGH, 0, nullptr, level > 0); + glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_MEDIUM, 0, nullptr, level > 1); + glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_LOW, 0, nullptr, level > 2); + glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_NOTIFICATION, 0, nullptr, level > 3); + mCurrentLevel = level; + } +} + +//----------------------------------------------------------------------------- +// +// Prints all logged messages to the console +// +//----------------------------------------------------------------------------- + +void FGLDebug::OutputMessageLog() +{ + if (mCurrentLevel <= 0) + return; + + GLint maxDebugMessageLength = 0; + glGetIntegerv(GL_MAX_DEBUG_MESSAGE_LENGTH, &maxDebugMessageLength); + + const int maxMessages = 50; + const int messageLogSize = maxMessages * maxDebugMessageLength; + + TArray sources, types, severities; + TArray ids; + TArray lengths; + TArray messageLog; + + sources.Resize(maxMessages); + types.Resize(maxMessages); + severities.Resize(maxMessages); + ids.Resize(maxMessages); + lengths.Resize(maxMessages); + messageLog.Resize(messageLogSize); + + while (true) + { + GLuint numMessages = glGetDebugMessageLog(maxMessages, messageLogSize, &sources[0], &types[0], &ids[0], &severities[0], &lengths[0], &messageLog[0]); + if (numMessages <= 0) break; + + GLsizei offset = 0; + for (GLuint i = 0; i < numMessages; i++) + { + PrintMessage(sources[i], types[i], ids[i], severities[i], lengths[i], &messageLog[offset]); + offset += lengths[i]; + } + } +} + +//----------------------------------------------------------------------------- +// +// Print a single message to the console +// +//----------------------------------------------------------------------------- + +void FGLDebug::PrintMessage(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message) +{ + static int messagesPrinted = 0; + messagesPrinted++; + if (messagesPrinted == 50) + { + Printf("Max OpenGL debug messages reached. Suppressing further output.\n"); + } + else if (messagesPrinted < 50) + { + FString msg(message, length); + FString sourceStr = SourceToString(source); + FString typeStr = TypeToString(type); + FString severityStr = SeverityToString(severity); + Printf("%s [%s] %s: %s\n", severityStr.GetChars(), sourceStr.GetChars(), typeStr.GetChars(), msg.GetChars()); + } +} + +//----------------------------------------------------------------------------- +// +// OpenGL callback function used when synchronous debugging is enabled +// +//----------------------------------------------------------------------------- + +void FGLDebug::DebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam) +{ + PrintMessage(source, type, id, severity, length, message); + +#ifdef _MSC_VER + DebugBreak(); +#else + raise(SIGTRAP); +#endif +} + +//----------------------------------------------------------------------------- +// +// Enum to string helpers +// +//----------------------------------------------------------------------------- + +FString FGLDebug::SourceToString(GLenum source) +{ + FString s; + switch (source) + { + case GL_DEBUG_SOURCE_API: s = "api"; break; + case GL_DEBUG_SOURCE_WINDOW_SYSTEM: s = "window system"; break; + case GL_DEBUG_SOURCE_SHADER_COMPILER: s = "shader compiler"; break; + case GL_DEBUG_SOURCE_THIRD_PARTY: s = "third party"; break; + case GL_DEBUG_SOURCE_APPLICATION: s = "application"; break; + case GL_DEBUG_SOURCE_OTHER: s = "other"; break; + default: s.Format("%d", (int)source); + } + return s; +} + +FString FGLDebug::TypeToString(GLenum type) +{ + FString s; + switch (type) + { + case GL_DEBUG_TYPE_ERROR: s = "error"; break; + case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: s = "deprecated"; break; + case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: s = "undefined"; break; + case GL_DEBUG_TYPE_PORTABILITY: s = "portability"; break; + case GL_DEBUG_TYPE_PERFORMANCE: s = "performance"; break; + case GL_DEBUG_TYPE_MARKER: s = "marker"; break; + case GL_DEBUG_TYPE_PUSH_GROUP: s = "push group"; break; + case GL_DEBUG_TYPE_POP_GROUP: s = "pop group"; break; + case GL_DEBUG_TYPE_OTHER: s = "other"; break; + default: s.Format("%d", (int)type); + } + return s; +} + +FString FGLDebug::SeverityToString(GLenum severity) +{ + FString s; + switch (severity) + { + case GL_DEBUG_SEVERITY_LOW: s = "low severity"; break; + case GL_DEBUG_SEVERITY_MEDIUM: s = "medium severity"; break; + case GL_DEBUG_SEVERITY_HIGH: s = "high severity"; break; + case GL_DEBUG_SEVERITY_NOTIFICATION: s = "notification"; break; + default: s.Format("%d", (int)severity); + } + return s; +} diff --git a/src/gl/system/gl_debug.h b/src/gl/system/gl_debug.h new file mode 100644 index 0000000000..ce919cebd3 --- /dev/null +++ b/src/gl/system/gl_debug.h @@ -0,0 +1,31 @@ +#ifndef __GL_DEBUG_H +#define __GL_DEBUG_H + +#include +#include "gl/system/gl_interface.h" +#include "c_cvars.h" +#include "r_defs.h" + +class FGLDebug +{ +public: + void Update(); + +private: + void SetupBreakpointMode(); + void UpdateLoggingLevel(); + void OutputMessageLog(); + + static void PrintMessage(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message); + + static void APIENTRY DebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam); + + static FString SourceToString(GLenum source); + static FString TypeToString(GLenum type); + static FString SeverityToString(GLenum severity); + + GLenum mCurrentLevel = 0; + bool mBreakpointMode = false; +}; + +#endif diff --git a/src/gl/system/gl_framebuffer.cpp b/src/gl/system/gl_framebuffer.cpp index 096a33393a..c74009e1fe 100644 --- a/src/gl/system/gl_framebuffer.cpp +++ b/src/gl/system/gl_framebuffer.cpp @@ -65,6 +65,7 @@ #include "gl/utility/gl_templates.h" #include "gl/gl_functions.h" #include "gl/renderer/gl_2ddrawer.h" +#include "gl_debug.h" IMPLEMENT_CLASS(OpenGLFrameBuffer) EXTERN_CVAR (Float, vid_brightness) @@ -105,6 +106,8 @@ OpenGLFrameBuffer::OpenGLFrameBuffer(void *hMonitor, int width, int height, int LastCamera = NULL; InitializeState(); + mDebug = std::make_shared(); + mDebug->Update(); gl_SetupMenu(); gl_GenerateGlobalBrightmapFromColormap(); DoSetGamma(); @@ -219,6 +222,7 @@ void OpenGLFrameBuffer::Swap() Finish.Unclock(); swapped = true; FHardwareTexture::UnbindAll(); + mDebug->Update(); } //=========================================================================== diff --git a/src/gl/system/gl_framebuffer.h b/src/gl/system/gl_framebuffer.h index d648da30cb..5315bb0a9a 100644 --- a/src/gl/system/gl_framebuffer.h +++ b/src/gl/system/gl_framebuffer.h @@ -6,8 +6,11 @@ #include "win32gliface.h" #endif +#include + class FHardwareTexture; class FSimpleVertexBuffer; +class FGLDebug; extern long gl_frameMS; extern long gl_frameCount; @@ -117,6 +120,8 @@ private: bool HWGammaActive = false; + std::shared_ptr mDebug; + public: AActor * LastCamera; int palette_brightness;