From 3cd9d043b528b61dd3a2251f9fea520c21a159a2 Mon Sep 17 00:00:00 2001 From: jpaone Date: Thu, 24 Oct 2024 12:56:48 -0600 Subject: [PATCH] Issue #29: Add OpenGL 4.3 Debug callback --- CHANGELOG.md | 1 + OpenGLEngine.hpp | 33 ++++++++++++++++ OpenGLUtils.hpp | 100 ++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 116 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c3e3ab9..694f8ea4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## v ?.?.? - ?? ??? ???? - Fixed GPU memory leak of not deleting cube IBOs - Add CSCI441::OpenGLUtils::checkOpenGLError() method to check the error queue +- When requesting an OpenGL 4.3+ debug context, register debug callback ## v 5.5.0 - 18 Oct 2024 - TextureUtils::loadAndRegister2DTexture() can silence error message diff --git a/OpenGLEngine.hpp b/OpenGLEngine.hpp index 10f22f4c..4b6bec14 100644 --- a/OpenGLEngine.hpp +++ b/OpenGLEngine.hpp @@ -234,6 +234,19 @@ namespace CSCI441 { */ static void mErrorCallback(int error, const char* DESCRIPTION) { fprintf(stderr, "[ERROR]: %d\n\t%s\n", error, DESCRIPTION ); } + /** + * @brief callback called whenever a debug message is signaled + */ + static void mDebugMessageCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam) { + fprintf( stdout, "[VERBOSE]: Debug Message (%d): source = %s, type = %s, severity = %s, message = %s\n", + id, + CSCI441::OpenGLUtils::debugSourceToString(source), + CSCI441::OpenGLUtils::debugTypeToString(type), + CSCI441::OpenGLUtils::debugSeverityToString(severity), + message + ); + } + /** * callback called when GLFW pWindow is resized. internally updated mWindowWidth and * mWindowHeight to new values @@ -398,6 +411,12 @@ inline void CSCI441::OpenGLEngine::mSetupGLFW() { glfwWindowHint( GLFW_DOUBLEBUFFER, GLFW_TRUE ); // request double buffering glfwWindowHint(GLFW_RESIZABLE, mWindowResizable ); // set if our window should be able to be resized + // if wanting debug information with Version 4.3 or higher + if( DEBUG + && (mOpenGLMajorVersion > 4 || (mOpenGLMajorVersion == 4 && mOpenGLMinorVersion >= 3)) ) { + glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, true); // request a debug context + } + // create a window for a given size, with a given title mpWindow = glfwCreateWindow(mWindowWidth, mWindowHeight, mWindowTitle, nullptr, nullptr ); if( !mpWindow ) { // if the window could not be created, NULL is returned @@ -411,6 +430,20 @@ inline void CSCI441::OpenGLEngine::mSetupGLFW() { glfwSetInputMode(mpWindow, GLFW_LOCK_KEY_MODS, GLFW_TRUE); // track state of Caps Lock and Num Lock keys glfwSetWindowUserPointer(mpWindow, (void*)this); glfwSetWindowSizeCallback(mpWindow, mWindowResizeCallback); + + // if wanting debug information with Version 4.3 or higher + if( DEBUG + && (mOpenGLMajorVersion > 4 || (mOpenGLMajorVersion == 4 && mOpenGLMinorVersion >= 3)) ) { + // check if debug context was created + int flags; glGetIntegerv(GL_CONTEXT_FLAGS, &flags); + if (flags & GL_CONTEXT_FLAG_DEBUG_BIT) { + // register callback to synchronously print any debug messages without having to call glGetError() + glEnable(GL_DEBUG_OUTPUT); + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); + glDebugMessageCallback(mDebugMessageCallback, nullptr); + glDebugMessageControl( GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE ); + } + } } } } diff --git a/OpenGLUtils.hpp b/OpenGLUtils.hpp index cca6277d..41fb73ab 100755 --- a/OpenGLUtils.hpp +++ b/OpenGLUtils.hpp @@ -88,6 +88,32 @@ namespace CSCI441 { * and clearing the error flag. */ [[maybe_unused]] void checkOpenGLErrors(); + + /** + * @brief Converts error value to string representation + * @param err OpenGL error code + * @return name of error + */ + [[maybe_unused]] const char* openGLErrorMessage(GLenum err); + + /** + * @brief Converts debug source value to string representation + * @param source debug source + * @return name of debug source + */ + [[maybe_unused]] const char* debugSourceToString(GLenum source); + /** + * @brief Converts debug type value to string representation + * @param type debug type + * @return type of debug message + */ + [[maybe_unused]] const char* debugTypeToString(GLenum type); + /** + * @brief Converts debug severity value to string representation + * @param severity debug severity + * @return severity of debug message + */ + [[maybe_unused]] const char* debugSeverityToString(GLenum severity); }; } @@ -104,7 +130,6 @@ namespace CSCI441_INTERNAL { void printOpenGLParam2f(const char *FORMAT, GLenum name ); [[maybe_unused]] void printOpenGLParam3(const char *FORMAT, GLenum name ); void printOpenGLParam4(const char *FORMAT, GLenum name ); - const char* openGLErrorMessage(GLenum err); } //********************************************************************************** @@ -264,11 +289,66 @@ inline void CSCI441::OpenGLUtils::printOpenGLExtensions() { inline void CSCI441::OpenGLUtils::checkOpenGLErrors() { GLenum err; while( (err = glGetError()) != GL_NO_ERROR ) { - fprintf( stderr, "[ERROR]: OpenGL Error %d: %s\n", err, CSCI441_INTERNAL::openGLErrorMessage(err) ); + fprintf( stderr, "[ERROR]: OpenGL Error (%d): %s\n", err, CSCI441::OpenGLUtils::openGLErrorMessage(err) ); err = glGetError(); } } +inline const char* CSCI441::OpenGLUtils::openGLErrorMessage(GLenum err) { + switch(err) { + case GL_NO_ERROR: return "No error"; + case GL_INVALID_ENUM: return "Invalid enum"; + case GL_INVALID_VALUE: return "Invalid value"; + case GL_INVALID_OPERATION: return "Invalid operation"; + case GL_STACK_OVERFLOW: return "Stack overflow"; + case GL_STACK_UNDERFLOW: return "Stack underflow"; + case GL_OUT_OF_MEMORY: return "Out of memory"; + case GL_INVALID_FRAMEBUFFER_OPERATION: return "Invalid framebuffer operation"; + case GL_CONTEXT_LOST: return "Context lost"; + default: return "Unknown"; + } +} + +[[maybe_unused]] +inline const char* CSCI441::OpenGLUtils::debugSourceToString(GLenum source) { + switch(source) { + case GL_DEBUG_SOURCE_API: return "API"; + case GL_DEBUG_SOURCE_WINDOW_SYSTEM: return "Window System"; + case GL_DEBUG_SOURCE_SHADER_COMPILER: return "Shader Compiler"; + case GL_DEBUG_SOURCE_THIRD_PARTY: return "Third Party"; + case GL_DEBUG_SOURCE_APPLICATION: return "Application"; + case GL_DEBUG_SOURCE_OTHER: return "Other"; + default: return "Unknown"; + } +} + +[[maybe_unused]] +inline const char* CSCI441::OpenGLUtils::debugTypeToString(GLenum type) { + switch(type) { + case GL_DEBUG_TYPE_ERROR: return "Error"; + case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: return "Deprecated Behavior"; + case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: return "Undefined Behavior"; + case GL_DEBUG_TYPE_PORTABILITY: return "Portability"; + case GL_DEBUG_TYPE_PERFORMANCE: return "Performance"; + case GL_DEBUG_TYPE_MARKER: return "Marker"; + case GL_DEBUG_TYPE_PUSH_GROUP: return "Push Group"; + case GL_DEBUG_TYPE_POP_GROUP: return "Pop Group"; + case GL_DEBUG_TYPE_OTHER: return "Other"; + default: return "Unknown"; + } +} + +[[maybe_unused]] +inline const char* CSCI441::OpenGLUtils::debugSeverityToString(GLenum severity) { + switch(severity) { + case GL_DEBUG_SEVERITY_HIGH: return "High"; + case GL_DEBUG_SEVERITY_MEDIUM: return "Medium"; + case GL_DEBUG_SEVERITY_LOW: return "Low"; + case GL_DEBUG_SEVERITY_NOTIFICATION: return "Notification"; + default: return "Unknown"; + } +} + //********************************************************************************** //********************************************************************************** // Internal function implementations @@ -316,20 +396,4 @@ inline void CSCI441_INTERNAL::printOpenGLParam4(const char * const FORMAT, const fprintf(stdout, FORMAT, values[0], values[1], values[2], values[3] ); } -inline const char* CSCI441_INTERNAL::openGLErrorMessage(GLenum err) { - switch(err) { - case GL_NO_ERROR: return "No error"; - case GL_INVALID_ENUM: return "Invalid enum"; - case GL_INVALID_VALUE: return "Invalid value"; - case GL_INVALID_OPERATION: return "Invalid operation"; - case GL_STACK_OVERFLOW: return "Stack overflow"; - case GL_STACK_UNDERFLOW: return "Stack underflow"; - case GL_OUT_OF_MEMORY: return "Out of memory"; - case GL_INVALID_FRAMEBUFFER_OPERATION: return "Invalid framebuffer operation"; - case GL_CONTEXT_LOST: return "Context lost"; - default: return "Unknown"; - } -} - - #endif // CSCI441_OPENGL_UTILS_H