From 69e121727dd93c746aa3f31d13d5cc323a8fca7d Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Sun, 16 Jun 2024 01:41:46 +0200 Subject: [PATCH] Add r_glDebugContext to enable OpenGL debug context and -callback needs SDL2 and GL_ARB_debug_output --- neo/renderer/RenderSystem.h | 3 + neo/renderer/RenderSystem_init.cpp | 96 +++++++++++++++++++++++++++++- neo/renderer/qgl.h | 3 + neo/renderer/tr_local.h | 2 + neo/sys/glimp.cpp | 21 +++++++ 5 files changed, 122 insertions(+), 3 deletions(-) diff --git a/neo/renderer/RenderSystem.h b/neo/renderer/RenderSystem.h index 15a98645..1474ee4f 100644 --- a/neo/renderer/RenderSystem.h +++ b/neo/renderer/RenderSystem.h @@ -78,6 +78,7 @@ typedef struct glconfig_s { bool twoSidedStencilAvailable; bool textureNonPowerOfTwoAvailable; bool depthBoundsTestAvailable; + bool glDebugOutputAvailable; // GL framebuffer size, see also winWidth and winHeight int vidWidth, vidHeight; // passed to R_BeginFrame @@ -95,6 +96,8 @@ typedef struct glconfig_s { bool shouldFillWindowAlpha; bool isWayland; // DG: for other wayland-specific hacks.. (does *not* detect XWayland!) + bool haveDebugContext; + // For some reason people decided that we need displays with ultra small pixels, // so everything rendered on them must be scaled up to be legible. // unfortunately, this bullshit feature was "improved" upon by deciding that the best diff --git a/neo/renderer/RenderSystem_init.cpp b/neo/renderer/RenderSystem_init.cpp index 4e7562e3..9b4a039d 100644 --- a/neo/renderer/RenderSystem_init.cpp +++ b/neo/renderer/RenderSystem_init.cpp @@ -239,6 +239,8 @@ idCVar r_screenshotPngCompression("r_screenshotPngCompression", "3", CVAR_RENDER idCVar r_windowResizable("r_windowResizable", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_BOOL, "Allow resizing (and maximizing) the window (needs SDL2; with 2.0.5 or newer it's applied immediately)" ); idCVar r_vidRestartAlwaysFull( "r_vidRestartAlwaysFull", 0, CVAR_RENDERER | CVAR_ARCHIVE | CVAR_BOOL, "Always do a full vid_restart (ignore 'partial' argument), e.g. when changing window size" ); +idCVar r_glDebugContext( "r_glDebugContext", "0", CVAR_RENDERER | CVAR_BOOL, "Enable OpenGL Debug context - requires vid_restart, needs SDL2" ); + // define qgl functions #define QGLPROC(name, rettype, args) rettype (APIENTRYP q##name) args; #include "renderer/qgl_proc.h" @@ -288,6 +290,9 @@ PFNGLDEPTHBOUNDSEXTPROC qglDepthBoundsEXT; // DG: couldn't find any extension for this, it's supported in GL2.0 and newer, incl OpenGL ES2.0 PFNGLSTENCILOPSEPARATEPROC qglStencilOpSeparate; +// GL_ARB_debug_output +PFNGLDEBUGMESSAGECALLBACKARBPROC qglDebugMessageCallbackARB; + // eez: This is a slight hack for letting us select the desired screenshot format in other functions // This is a hack to avoid adding another function parameter to idRenderSystem::TakeScreenshot(), // which would break the API of the dhewm3 SDK for mods. @@ -296,6 +301,60 @@ PFNGLSTENCILOPSEPARATEPROC qglStencilOpSeparate; // it must set g_screenshotFormat accordingly before each call to TakeScreenshot(). int g_screenshotFormat = -1; +enum { + // Not all GL.h header know about GL_DEBUG_SEVERITY_NOTIFICATION_*. + QGL_DEBUG_SEVERITY_NOTIFICATION = 0x826B +}; + +/* + * Callback function for debug output. + */ +static void APIENTRY +DebugCallback( GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, + const GLchar *message, const void *userParam ) +{ + const char* sourceStr = "Source: Unknown"; + const char* typeStr = "Type: Unknown"; + const char* severityStr = "Severity: Unknown"; + + switch (severity) + { +#define SVRCASE(X, STR) case GL_DEBUG_SEVERITY_ ## X ## _ARB : severityStr = STR; break; + case QGL_DEBUG_SEVERITY_NOTIFICATION: return; + SVRCASE(HIGH, "Severity: High") + SVRCASE(MEDIUM, "Severity: Medium") + SVRCASE(LOW, "Severity: Low") +#undef SVRCASE + } + + switch (source) + { +#define SRCCASE(X) case GL_DEBUG_SOURCE_ ## X ## _ARB: sourceStr = "Source: " #X; break; + SRCCASE(API); + SRCCASE(WINDOW_SYSTEM); + SRCCASE(SHADER_COMPILER); + SRCCASE(THIRD_PARTY); + SRCCASE(APPLICATION); + SRCCASE(OTHER); +#undef SRCCASE + } + + switch(type) + { +#define TYPECASE(X) case GL_DEBUG_TYPE_ ## X ## _ARB: typeStr = "Type: " #X; break; + TYPECASE(ERROR); + TYPECASE(DEPRECATED_BEHAVIOR); + TYPECASE(UNDEFINED_BEHAVIOR); + TYPECASE(PORTABILITY); + TYPECASE(PERFORMANCE); + TYPECASE(OTHER); +#undef TYPECASE + } + + common->Warning( "GLDBG %s %s %s: %s\n", sourceStr, typeStr, severityStr, message ); + +} + /* ================= R_CheckExtension @@ -415,15 +474,15 @@ static void R_CheckPortableExtensions( void ) { qglActiveStencilFaceEXT = (PFNGLACTIVESTENCILFACEEXTPROC)GLimp_ExtensionPointer( "glActiveStencilFaceEXT" ); if( glConfig.glVersion >= 2.0) { - common->Printf( "... got GL2.0+ glStencilOpSeparate()\n" ); + common->Printf( "...got GL2.0+ glStencilOpSeparate()\n" ); qglStencilOpSeparate = (PFNGLSTENCILOPSEPARATEPROC)GLimp_ExtensionPointer( "glStencilOpSeparate" ); } else if( R_CheckExtension( "GL_ATI_separate_stencil" ) ) { - common->Printf( "... got glStencilOpSeparateATI() (GL_ATI_separate_stencil)\n" ); + common->Printf( "...got glStencilOpSeparateATI() (GL_ATI_separate_stencil)\n" ); // the ATI version of glStencilOpSeparate() has the same signature and should also // behave identical to the GL2 version (in Mesa3D it's just an alias) qglStencilOpSeparate = (PFNGLSTENCILOPSEPARATEPROC)GLimp_ExtensionPointer( "glStencilOpSeparateATI" ); } else { - common->Printf( "... don't have glStencilOpSeparateATI() or (GL2.0+) glStencilOpSeparate()\n" ); + common->Printf( "X..don't have glStencilOpSeparateATI() or (GL2.0+) glStencilOpSeparate()\n" ); qglStencilOpSeparate = NULL; } @@ -482,6 +541,37 @@ static void R_CheckPortableExtensions( void ) { qglDepthBoundsEXT = (PFNGLDEPTHBOUNDSEXTPROC)GLimp_ExtensionPointer( "glDepthBoundsEXT" ); } + // GL_ARB_debug_output + glConfig.glDebugOutputAvailable = false; + if ( glConfig.haveDebugContext ) { + if ( strstr( glConfig.extensions_string, "GL_ARB_debug_output" ) ) { + glConfig.glDebugOutputAvailable = true; + qglDebugMessageCallbackARB = (PFNGLDEBUGMESSAGECALLBACKARBPROC)GLimp_ExtensionPointer( "glDebugMessageCallbackARB" ); + if ( r_glDebugContext.GetBool() ) { + common->Printf( "...using GL_ARB_debug_output (r_glDebugContext is set)\n" ); + qglDebugMessageCallbackARB(DebugCallback, NULL); + qglEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB); + } else { + common->Printf( "...found GL_ARB_debug_output, but not using it (r_glDebugContext is not set)\n" ); + } + } else { + common->Printf( "X..GL_ARB_debug_output not found\n" ); + qglDebugMessageCallbackARB = NULL; + if ( r_glDebugContext.GetBool() ) { + common->Warning( "r_glDebugContext is set, but can't be used because GL_ARB_debug_output is not supported" ); + } + } + } else { + if ( strstr( glConfig.extensions_string, "GL_ARB_debug_output" ) ) { + if ( r_glDebugContext.GetBool() ) { + common->Printf( "...found GL_ARB_debug_output, but not using it (no debug context)\n" ); + } else { + common->Printf( "...found GL_ARB_debug_output, but not using it (r_glDebugContext is not set)\n" ); + } + } else { + common->Printf( "X..GL_ARB_debug_output not found\n" ); + } + } } diff --git a/neo/renderer/qgl.h b/neo/renderer/qgl.h index 846a0d74..64177009 100644 --- a/neo/renderer/qgl.h +++ b/neo/renderer/qgl.h @@ -119,6 +119,9 @@ extern PFNGLPROGRAMLOCALPARAMETER4FVARBPROC qglProgramLocalParameter4fvARB; // GL_EXT_depth_bounds_test extern PFNGLDEPTHBOUNDSEXTPROC qglDepthBoundsEXT; +// GL_ARB_debug_output +extern PFNGLDEBUGMESSAGECALLBACKARBPROC qglDebugMessageCallbackARB; + #if defined( _WIN32 ) && defined(ID_ALLOW_TOOLS) extern BOOL(WINAPI * qwglSwapBuffers)(HDC); diff --git a/neo/renderer/tr_local.h b/neo/renderer/tr_local.h index f00c5b98..79c39de1 100644 --- a/neo/renderer/tr_local.h +++ b/neo/renderer/tr_local.h @@ -983,6 +983,8 @@ extern idCVar r_materialOverride; // override all materials extern idCVar r_debugRenderToTexture; +extern idCVar r_glDebugContext; + /* ==================================================================== diff --git a/neo/sys/glimp.cpp b/neo/sys/glimp.cpp index afee28dc..1f0bad2e 100644 --- a/neo/sys/glimp.cpp +++ b/neo/sys/glimp.cpp @@ -279,6 +279,11 @@ try_again: #if SDL_VERSION_ATLEAST(2, 0, 0) + if ( r_glDebugContext.GetBool() ) { + common->Printf( "Requesting an OpenGL Debug Context (r_glDebugContext is enabled)\n" ); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); + } + if ( parms.fullScreen && parms.fullScreenDesktop ) { common->Printf( "Will create a pseudo-fullscreen window at the current desktop resolution\n" ); } else { @@ -435,6 +440,10 @@ try_again: if (SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, r_swapInterval.GetInteger()) < 0) common->Warning("SDL_GL_SWAP_CONTROL not supported"); + if ( r_glDebugContext.GetBool() ) { + common->Warning( "r_glDebugContext is set, but not supported by SDL1.2!\n" ); + } + r_swapInterval.ClearModified(); window = SDL_SetVideoMode(parms.width, parms.height, colorbits, flags); @@ -588,6 +597,18 @@ try_again: } #endif + glConfig.haveDebugContext = false; +#if SDL_VERSION_ATLEAST(2, 0, 0) + int cflags = 0; + if ( SDL_GL_GetAttribute( SDL_GL_CONTEXT_FLAGS, &cflags ) == 0 ) { + glConfig.haveDebugContext = (cflags & SDL_GL_CONTEXT_DEBUG_FLAG) != 0; + if ( glConfig.haveDebugContext ) + common->Printf( "Got a debug context!\n" ); + else if( r_glDebugContext.GetBool() ) { + common->Warning( "Requested a debug context, but didn't get one!\n" ); + } + } +#endif break; }