diff --git a/source/lib/external_libraries/opengles2_wrapper.h b/source/lib/external_libraries/opengles2_wrapper.h index e23f165972..ec6caef77d 100644 --- a/source/lib/external_libraries/opengles2_wrapper.h +++ b/source/lib/external_libraries/opengles2_wrapper.h @@ -88,6 +88,10 @@ // GL_OES_depth32 #define GL_DEPTH_COMPONENT32 GL_DEPTH_COMPONENT32_OES +// GL_KHR_debug +#define glPopDebugGroupKHR glPopDebugGroup +#define glPushDebugGroupKHR glPushDebugGroup + #endif // CONFIG2_GLES #endif // !INCLUDED_GLES2_WRAPPER diff --git a/source/lib/ogl.cpp b/source/lib/ogl.cpp index 6629b9bd62..f8a539125f 100644 --- a/source/lib/ogl.cpp +++ b/source/lib/ogl.cpp @@ -59,6 +59,9 @@ static bool have_15 = false; static bool have_14 = false; static bool have_13 = false; static bool have_12 = false; +#if KHR_DEBUG_ENABLED +static bool have_KHR = false; +#endif // return a C string of unspecified length containing a space-separated @@ -372,6 +375,84 @@ void ogl_WarnIfErrorLoc(const char *file, int line) debug_printf("%s:%d: OpenGL error(s) occurred: %s (%04x)\n", file, line, ogl_GetErrorName(first_error), (unsigned int)first_error); } +void GLAD_API_PTR ogl_OnDebugMessage(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei UNUSED(length), const GLchar* message, const void* UNUSED(user_param)) +{ + std::string debugSource = "unknown"; + std::string debugType = "unknown"; + std::string debugSeverity = "unknown"; + + switch (source) + { + case GL_DEBUG_SOURCE_API: + debugSource = "the API"; + break; + case GL_DEBUG_SOURCE_WINDOW_SYSTEM: + debugSource = "the window system"; + break; + case GL_DEBUG_SOURCE_SHADER_COMPILER: + debugSource = "the shader compiler"; + break; + case GL_DEBUG_SOURCE_THIRD_PARTY: + debugSource = "a third party"; + break; + case GL_DEBUG_SOURCE_APPLICATION: + debugSource = "the application"; + break; + case GL_DEBUG_SOURCE_OTHER: + debugSource = "somewhere"; + break; + } + + switch (type) + { + case GL_DEBUG_TYPE_ERROR: + debugType = "error"; + break; + case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: + debugType = "deprecated behaviour"; + break; + case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: + debugType = "undefined behaviour"; + break; + case GL_DEBUG_TYPE_PORTABILITY: + debugType = "portability"; + break; + case GL_DEBUG_TYPE_PERFORMANCE: + debugType = "performance"; + break; + case GL_DEBUG_TYPE_OTHER: + debugType = "other"; + break; + case GL_DEBUG_TYPE_MARKER: + debugType = "marker"; + break; + case GL_DEBUG_TYPE_PUSH_GROUP: + debugType = "push group"; + break; + case GL_DEBUG_TYPE_POP_GROUP: + debugType = "pop group"; + break; + } + + switch (severity) + { + case GL_DEBUG_SEVERITY_HIGH: + debugSeverity = "high"; + break; + case GL_DEBUG_SEVERITY_MEDIUM: + debugSeverity = "medium"; + break; + case GL_DEBUG_SEVERITY_LOW: + debugSeverity = "low"; + break; + case GL_DEBUG_SEVERITY_NOTIFICATION: + debugSeverity = "notification"; + break; + } + + debug_printf("OpenGL | %s: %s source: %s id %u: %s\n", debugSeverity.c_str(), debugType.c_str(), debugSource.c_str(), id, message); +} + // ignore and reset the specified error (as returned by glGetError). // any other errors that have occurred are reported as ogl_WarnIfError would. @@ -492,6 +573,25 @@ bool ogl_Init(void* (load)(const char*)) glEnable(GL_TEXTURE_2D); +#if KHR_DEBUG_ENABLED +#if CONFIG2_GLES + bool is_core = ogl_HaveVersion(3, 2); +#else + bool is_core = ogl_HaveVersion(4, 3); +#endif + have_KHR = is_core || ogl_HaveExtension("GL_KHR_debug"); + if (have_KHR) + { + glEnable(GL_DEBUG_OUTPUT); + glDebugMessageCallback(ogl_OnDebugMessage, nullptr); + + // Filter out our own debug group messages + GLuint id = 0x0ad; + glDebugMessageControl(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PUSH_GROUP, GL_DONT_CARE, 1, &id, GL_FALSE); + glDebugMessageControl(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_POP_GROUP, GL_DONT_CARE, 1, &id, GL_FALSE); + } +#endif + return true; } @@ -510,3 +610,21 @@ void ogl_SetVsyncEnabled(bool enabled) UNUSED2(enabled); #endif } + +#if KHR_DEBUG_ENABLED +ogl_DebugScopedGroup::ogl_DebugScopedGroup(const char* message) +{ + if (!have_KHR) + return; + + glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0x0ad, -1, message); +} + +ogl_DebugScopedGroup::~ogl_DebugScopedGroup() +{ + if (!have_KHR) + return; + + glPopDebugGroup(); +} +#endif diff --git a/source/lib/ogl.h b/source/lib/ogl.h index 8f004a8f64..641a2b9499 100644 --- a/source/lib/ogl.h +++ b/source/lib/ogl.h @@ -150,4 +150,21 @@ extern bool ogl_SquelchError(GLenum err_to_ignore); extern GLint ogl_max_tex_size; /// [pixels] extern GLint ogl_max_tex_units; /// limit on GL_TEXTUREn + +#ifdef NDEBUG +# define KHR_DEBUG_ENABLED 0 +# define OGL_SCOPED_DEBUG_GROUP(groupName) +#else +# define KHR_DEBUG_ENABLED 1 +# define OGL_SCOPED_DEBUG_GROUP(groupName) ogl_DebugScopedGroup glDebugScopedGroup(groupName) +/** + * RAII for glPushDebugGroup() and glPopDebugGroup(). + */ +struct ogl_DebugScopedGroup +{ + ogl_DebugScopedGroup(const char* message); + ~ogl_DebugScopedGroup(); +}; +#endif + #endif // #ifndef INCLUDED_OGL diff --git a/source/lib/res/graphics/ogl_tex.cpp b/source/lib/res/graphics/ogl_tex.cpp index 889ae40be2..949b997d57 100644 --- a/source/lib/res/graphics/ogl_tex.cpp +++ b/source/lib/res/graphics/ogl_tex.cpp @@ -471,6 +471,13 @@ static Status OglTex_reload(OglTex* ot, const PIVFS& vfs, const VfsPath& pathnam if(ot->flags & OT_NEED_AUTO_UPLOAD) (void)ogl_tex_upload(h); +#if KHR_DEBUG_ENABLED + const std::string name = pathname.string8(); + glBindTexture(GL_TEXTURE_2D, ot->id); + glObjectLabel(GL_TEXTURE, ot->id, name.size(), name.c_str()); +#endif + + return INFO::OK; } diff --git a/source/ps/CLogger.cpp b/source/ps/CLogger.cpp index 63d7495f69..51a6c0953c 100644 --- a/source/ps/CLogger.cpp +++ b/source/ps/CLogger.cpp @@ -22,6 +22,7 @@ #include "graphics/Canvas2D.h" #include "graphics/FontMetrics.h" #include "graphics/TextRenderer.h" +#include "lib/ogl.h" #include "lib/os_path.h" #include "lib/timer.h" #include "lib/utf8.h" @@ -205,6 +206,7 @@ void CLogger::WriteWarning(const char* message) void CLogger::Render() { PROFILE3_GPU("logger"); + OGL_SCOPED_DEBUG_GROUP("Draw CLogger messages"); CleanupRenderQueue(); diff --git a/source/ps/GameSetup/GameSetup.cpp b/source/ps/GameSetup/GameSetup.cpp index d0fd4324aa..f92e7cd32d 100644 --- a/source/ps/GameSetup/GameSetup.cpp +++ b/source/ps/GameSetup/GameSetup.cpp @@ -256,6 +256,7 @@ void Render() if (g_DoRenderGui) { + OGL_SCOPED_DEBUG_GROUP("Draw GUI"); // All GUI elements are drawn in Z order to render semi-transparent // objects correctly. g_GUI->Draw(); diff --git a/source/renderer/Renderer.cpp b/source/renderer/Renderer.cpp index 1393354a37..42b646dac8 100644 --- a/source/renderer/Renderer.cpp +++ b/source/renderer/Renderer.cpp @@ -658,6 +658,7 @@ void CRenderer::SetClearColor(SColor4ub color) void CRenderer::RenderShadowMap(const CShaderDefines& context) { PROFILE3_GPU("shadow map"); + OGL_SCOPED_DEBUG_GROUP("Render shadow map"); CShaderDefines contextCast = context; contextCast.Add(str_MODE_SHADOWCAST, str_1); @@ -701,6 +702,7 @@ void CRenderer::RenderShadowMap(const CShaderDefines& context) void CRenderer::RenderPatches(const CShaderDefines& context, int cullGroup) { PROFILE3_GPU("patches"); + OGL_SCOPED_DEBUG_GROUP("Render patches"); #if CONFIG2_GLES #warning TODO: implement wireface/edged rendering mode GLES @@ -750,6 +752,7 @@ void CRenderer::RenderPatches(const CShaderDefines& context, int cullGroup) void CRenderer::RenderModels(const CShaderDefines& context, int cullGroup) { PROFILE3_GPU("models"); + OGL_SCOPED_DEBUG_GROUP("Render models"); int flags = 0; @@ -784,6 +787,7 @@ void CRenderer::RenderModels(const CShaderDefines& context, int cullGroup) void CRenderer::RenderTransparentModels(const CShaderDefines& context, int cullGroup, ETransparentMode transparentMode, bool disableFaceCulling) { PROFILE3_GPU("transparent models"); + OGL_SCOPED_DEBUG_GROUP("Render transparent models"); int flags = 0; @@ -959,6 +963,7 @@ void CRenderer::ComputeRefractionCamera(CCamera& camera, const CBoundingBoxAlign void CRenderer::RenderReflections(const CShaderDefines& context, const CBoundingBoxAligned& scissor) { PROFILE3_GPU("water reflections"); + OGL_SCOPED_DEBUG_GROUP("Render water reflections"); WaterManager& wm = m->waterManager; @@ -1034,6 +1039,7 @@ void CRenderer::RenderReflections(const CShaderDefines& context, const CBounding void CRenderer::RenderRefractions(const CShaderDefines& context, const CBoundingBoxAligned &scissor) { PROFILE3_GPU("water refractions"); + OGL_SCOPED_DEBUG_GROUP("Render water refractions"); WaterManager& wm = m->waterManager; @@ -1092,6 +1098,7 @@ void CRenderer::RenderRefractions(const CShaderDefines& context, const CBounding void CRenderer::RenderSilhouettes(const CShaderDefines& context) { PROFILE3_GPU("silhouettes"); + OGL_SCOPED_DEBUG_GROUP("Render water silhouettes"); CShaderDefines contextOccluder = context; contextOccluder.Add(str_MODE_SILHOUETTEOCCLUDER, str_1); @@ -1168,6 +1175,7 @@ void CRenderer::RenderSilhouettes(const CShaderDefines& context) void CRenderer::RenderParticles(int cullGroup) { PROFILE3_GPU("particles"); + OGL_SCOPED_DEBUG_GROUP("Render particles"); m->particleRenderer.RenderParticles(cullGroup); @@ -1189,6 +1197,7 @@ void CRenderer::RenderParticles(int cullGroup) void CRenderer::RenderSubmissions(const CBoundingBoxAligned& waterScissor) { PROFILE3("render submissions"); + OGL_SCOPED_DEBUG_GROUP("Render submissions"); GetScene().GetLOSTexture().InterpolateLOS(); diff --git a/source/renderer/SkyManager.cpp b/source/renderer/SkyManager.cpp index b65cba2373..13cf5b64cf 100644 --- a/source/renderer/SkyManager.cpp +++ b/source/renderer/SkyManager.cpp @@ -55,6 +55,7 @@ SkyManager::SkyManager() // Load all sky textures void SkyManager::LoadSkyTextures() { + OGL_SCOPED_DEBUG_GROUP("Load Sky Textures"); static const CStrW images[NUMBER_OF_TEXTURES + 1] = { L"front", L"back", @@ -208,6 +209,7 @@ std::vector SkyManager::GetSkySets() const // Render sky void SkyManager::RenderSky() { + OGL_SCOPED_DEBUG_GROUP("Render Sky"); #if CONFIG2_GLES #warning TODO: implement SkyManager::RenderSky for GLES #else diff --git a/source/renderer/TerrainRenderer.cpp b/source/renderer/TerrainRenderer.cpp index d2f5a0c910..a169762f85 100644 --- a/source/renderer/TerrainRenderer.cpp +++ b/source/renderer/TerrainRenderer.cpp @@ -353,6 +353,7 @@ CBoundingBoxAligned TerrainRenderer::ScissorWater(int cullGroup, const CCamera& bool TerrainRenderer::RenderFancyWater(const CShaderDefines& context, int cullGroup, ShadowMap* shadow) { PROFILE3_GPU("fancy water"); + OGL_SCOPED_DEBUG_GROUP("Render Fancy Water"); WaterManager* waterManager = g_Renderer.GetWaterManager(); CShaderDefines defines = context; @@ -529,6 +530,7 @@ void TerrainRenderer::RenderSimpleWater(int cullGroup) UNUSED2(cullGroup); #else PROFILE3_GPU("simple water"); + OGL_SCOPED_DEBUG_GROUP("Render Simple Water"); WaterManager* WaterMgr = g_Renderer.GetWaterManager(); CLOSTexture& losTexture = g_Game->GetView()->GetLOSTexture(); diff --git a/source/renderer/WaterManager.cpp b/source/renderer/WaterManager.cpp index 5e6a7f174d..6c254e39ee 100644 --- a/source/renderer/WaterManager.cpp +++ b/source/renderer/WaterManager.cpp @@ -447,6 +447,8 @@ void WaterManager::RecomputeDistanceHeightmap() // This requires m_DistanceHeightmap to be defined properly. void WaterManager::CreateWaveMeshes() { + OGL_SCOPED_DEBUG_GROUP("Create Wave Meshes"); + if (m_MapSize == 0) return; @@ -836,6 +838,7 @@ void WaterManager::CreateWaveMeshes() void WaterManager::RenderWaves(const CFrustum& frustrum) { + OGL_SCOPED_DEBUG_GROUP("Render Waves"); #if CONFIG2_GLES #warning Fix WaterManager::RenderWaves on GLES #else