Index: libraries/win32/opengl/include/GL/glext.h =================================================================== --- libraries/win32/opengl/include/GL/glext.h +++ libraries/win32/opengl/include/GL/glext.h @@ -7,7 +7,7 @@ /* ** Copyright (c) 2007-2010 The Khronos Group Inc. -** +** ** Permission is hereby granted, free of charge, to any person obtaining a ** copy of this software and/or associated documentation files (the ** "Materials"), to deal in the Materials without restriction, including @@ -15,10 +15,10 @@ ** distribute, sublicense, and/or sell copies of the Materials, and to ** permit persons to whom the Materials are 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 Materials. -** +** ** THE MATERIALS ARE 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. @@ -2126,6 +2126,29 @@ #define GL_DEBUG_SEVERITY_LOW_ARB 0x9148 #endif +#ifndef GL_debug_output +#define GL_DEBUG_OUTPUT 0x92E0 +#define GL_DEBUG_SOURCE_SHADER_COMPILER 0x8248 +#define GL_DEBUG_SOURCE_APPLICATION 0x824A +#define GL_DEBUG_SOURCE_OTHER 0x824B +#define GL_DEBUG_SOURCE_API 0x8246 +#define GL_DEBUG_SOURCE_WINDOW_SYSTEM 0x8247 +#define GL_DEBUG_SOURCE_THIRD_PARTY 0x8249 +#define GL_DEBUG_TYPE_ERROR 0x824C +#define GL_DEBUG_TYPE_MARKER 0x8268 +#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D +#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E +#define GL_DEBUG_TYPE_PORTABILITY 0x824F +#define GL_DEBUG_TYPE_PERFORMANCE 0x8250 +#define GL_DEBUG_TYPE_PUSH_GROUP 0x8269 +#define GL_DEBUG_TYPE_POP_GROUP 0x826A +#define GL_DEBUG_SEVERITY_HIGH 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM 0x9147 +#define GL_DEBUG_SEVERITY_LOW 0x9148 +#define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B +#define GL_DEBUG_TYPE_OTHER 0x8251 +#endif + #ifndef GL_ARB_robustness /* reuse GL_NO_ERROR */ #define GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB 0x00000004 Index: source/gui/CGUIText.cpp =================================================================== --- source/gui/CGUIText.cpp +++ source/gui/CGUIText.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 Wildfire Games. +/* Copyright (C) 2021 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -429,6 +429,7 @@ void CGUIText::Draw(CGUI& pGUI, const CGUIColor& DefaultColor, const CPos& pos, const float z, const CRect& clipping) const { + OGL_SCOPED_DEBUG_GROUP("Draw GUI text"); CShaderTechniquePtr tech = g_Renderer.GetShaderManager().LoadEffect(str_gui_text); tech->BeginPass(); Index: source/gui/GUIRenderer.cpp =================================================================== --- source/gui/GUIRenderer.cpp +++ source/gui/GUIRenderer.cpp @@ -339,7 +339,7 @@ // Called every frame, to draw the object (based on cached calculations) // TODO: batching by shader/texture/etc would be nice - + OGL_SCOPED_DEBUG_GROUP("Draw GUI element"); CMatrix3D matrix = GetDefaultGuiMatrix(); glDisable(GL_BLEND); Index: source/gui/ObjectTypes/CMiniMap.cpp =================================================================== --- source/gui/ObjectTypes/CMiniMap.cpp +++ source/gui/ObjectTypes/CMiniMap.cpp @@ -384,7 +384,8 @@ // (those operations cause a gpu sync, which slows down the way gpu works) void CMiniMap::Draw() { - PROFILE3("render minimap"); + OGL_SCOPED_DEBUG_GROUP("Draw MiniMap"); + PROFILE3("Draw MiniMap"); // The terrain isn't actually initialized until the map is loaded, which // happens when the game is started, so abort until then. Index: source/lib/external_libraries/glext_funcs.h =================================================================== --- source/lib/external_libraries/glext_funcs.h +++ source/lib/external_libraries/glext_funcs.h @@ -142,6 +142,13 @@ typedef GLuint GLhandleARB; +// GL_KHR_debug / GLES3.2 +typedef void (GL_APIENTRY *GLDEBUGPROCKHR)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam); +FUNC2(void, glDebugMessageCallbackKHR, glDebugMessageCallback, "3.2", (GLDEBUGPROCKHR callback, const GLvoid* userParam)) +FUNC2(void, glDebugMessageControlKHR, glDebugMessageControl, "3.2", (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint* ids, GLboolean enabled)) +FUNC2(void, glPushDebugGroupKHR, glPushDebugGroup, "3.2", (GLenum source, GLuint id, GLsizei length, const GLchar* message)) +FUNC2(void, glPopDebugGroupKHR, glPopDebugGroup, "3.2", (void)) +FUNC2(void, glObjectLabelKHR, glObjectLabel, "3.2", (GLenum identifier, GLuint name, GLsizei length, const GLchar* label)) #else // We might not have multisample on macOS. @@ -151,6 +158,19 @@ // were these defined as real functions in gl.h already? +// GL_KHR_debug / GL4.3 +// We might not have khr defines on macOS. +#ifndef GL_DEBUG_SOURCE_APPLICATION +#define GL_DEBUG_SOURCE_APPLICATION 0x824A +#endif + +typedef void (GL_APIENTRY* GLDEBUGPROCKHR)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam); +FUNC2(void, glDebugMessageCallbackKHR, glDebugMessageCallback, "4.3", (GLDEBUGPROCKHR callback, const GLvoid* userParam)) +FUNC2(void, glDebugMessageControlKHR, glDebugMessageControl, "4.3", (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint* ids, GLboolean enabled)) +FUNC2(void, glPushDebugGroupKHR, glPushDebugGroup, "4.3", (GLenum source, GLuint id, GLsizei length, const GLchar* message)) +FUNC2(void, glPopDebugGroupKHR, glPopDebugGroup, "4.3", (void)) +FUNC2(void, glObjectLabelKHR, glObjectLabel, "4.3", (GLenum identifier, GLuint name, GLsizei length, const GLchar *label)) + // GL_EXT_draw_range_elements / GL1.2: FUNC2(void, glDrawRangeElementsEXT, glDrawRangeElements, "1.2", (GLenum, GLuint, GLuint, GLsizei, GLenum, GLvoid*)) Index: source/lib/ogl.h =================================================================== --- source/lib/ogl.h +++ source/lib/ogl.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Wildfire Games. +/* Copyright (C) 2021 Wildfire Games. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -191,4 +191,31 @@ extern GLint ogl_max_tex_size; /// [pixels] extern GLint ogl_max_tex_units; /// limit on GL_TEXTUREn +#define OGL_SCOPED_DEBUG_GROUP(groupName) ogl_DebugScopedGroup glDebugScopedGroup(groupName) + +#ifndef NDEBUG +/** + * RAII for glPushDebugGroupKHR() and pglPopDebugGroupKHR(). + */ +struct ogl_DebugScopedGroup +{ + ogl_DebugScopedGroup(const char* message) + { + pglPushDebugGroupKHR(GL_DEBUG_SOURCE_APPLICATION, 0x0ad, -1, message); + } + + ~ogl_DebugScopedGroup() + { + pglPopDebugGroupKHR(); + } +}; +#else +// Fallback when we’re not debugging. +struct ogl_DebugScopedGroup +{ + ogl_DebugScopedGroup(const char* UNUSED(message)) {} + ~ogl_DebugScopedGroup() {} +}; +#endif + #endif // #ifndef INCLUDED_OGL Index: source/lib/ogl.cpp =================================================================== --- source/lib/ogl.cpp +++ source/lib/ogl.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Wildfire Games. +/* Copyright (C) 2021 Wildfire Games. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -422,6 +422,84 @@ } +void APIENTRY 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,4 +570,22 @@ #if !CONFIG2_GLES glGetIntegerv(GL_MAX_TEXTURE_UNITS, &ogl_max_tex_units); #endif + +#ifndef NDEBUG +#if CONFIG2_GLES + bool is_core = ogl_HaveVersion("3.2"); +#else + bool is_core = ogl_HaveVersion("4.3"); +#endif + if (is_core || ogl_HaveExtension("GL_KHR_debug")) + { + glEnable(GL_DEBUG_OUTPUT); + pglDebugMessageCallbackKHR(ogl_OnDebugMessage, nullptr); + + // Filter out our own debug group messages + GLuint id = 0x0ad; + pglDebugMessageControlKHR(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PUSH_GROUP, GL_DONT_CARE, 1, &id, GL_FALSE); + pglDebugMessageControlKHR(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_POP_GROUP, GL_DONT_CARE, 1, &id, GL_FALSE); + } +#endif } Index: source/lib/res/graphics/ogl_tex.cpp =================================================================== --- source/lib/res/graphics/ogl_tex.cpp +++ source/lib/res/graphics/ogl_tex.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 Wildfire Games. +/* Copyright (C) 2021 Wildfire Games. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -28,8 +28,6 @@ #include "precompiled.h" #include "ogl_tex.h" -#include - #include "lib/app_hooks.h" #include "lib/ogl.h" #include "lib/bits.h" @@ -39,6 +37,8 @@ #include "lib/res/h_mgr.h" #include "lib/fnv_hash.h" +#include +#include //---------------------------------------------------------------------------- // OpenGL helper routines @@ -480,6 +480,13 @@ if(ot->flags & OT_NEED_AUTO_UPLOAD) (void)ogl_tex_upload(h); + +#ifndef NDEBUG + const std::string name = pathname.string8(); + glBindTexture(GL_TEXTURE_2D, ot->id); + pglObjectLabelKHR(GL_TEXTURE, ot->id, name.size(), name.c_str()); +#endif + return INFO::OK; } Index: source/lib/sysdep/os/win/wgl.h =================================================================== --- source/lib/sysdep/os/win/wgl.h +++ source/lib/sysdep/os/win/wgl.h @@ -41,6 +41,10 @@ #ifndef APIENTRY #define APIENTRY __stdcall #endif +#ifndef GL_APIENTRY +#define GL_APIENTRY APIENTRY +#endif + #ifndef WINAPI #define WINAPI __stdcall #endif Index: source/ps/CLogger.cpp =================================================================== --- source/ps/CLogger.cpp +++ source/ps/CLogger.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 Wildfire Games. +/* Copyright (C) 2021 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -203,6 +203,7 @@ void CLogger::Render() { PROFILE3_GPU("logger"); + OGL_SCOPED_DEBUG_GROUP("Draw CLogger messages"); CleanupRenderQueue(); Index: source/renderer/Renderer.cpp =================================================================== --- source/renderer/Renderer.cpp +++ source/renderer/Renderer.cpp @@ -711,6 +711,7 @@ void CRenderer::RenderShadowMap(const CShaderDefines& context) { PROFILE3_GPU("shadow map"); + OGL_SCOPED_DEBUG_GROUP("Render shadow map"); m->shadow.BeginRender(); @@ -746,6 +747,8 @@ 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 @@ -798,6 +801,7 @@ void CRenderer::RenderModels(const CShaderDefines& context, int cullGroup) { PROFILE3_GPU("models"); + OGL_SCOPED_DEBUG_GROUP("Render models"); int flags = 0; @@ -833,6 +837,7 @@ 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; @@ -1009,6 +1014,7 @@ void CRenderer::RenderReflections(const CShaderDefines& context, const CBoundingBoxAligned& scissor) { PROFILE3_GPU("water reflections"); + OGL_SCOPED_DEBUG_GROUP("Render water reflections"); WaterManager& wm = m->waterManager; @@ -1082,6 +1088,7 @@ void CRenderer::RenderRefractions(const CShaderDefines& context, const CBoundingBoxAligned &scissor) { PROFILE3_GPU("water refractions"); + OGL_SCOPED_DEBUG_GROUP("Render water refractions"); WaterManager& wm = m->waterManager; @@ -1138,6 +1145,7 @@ 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); @@ -1239,6 +1247,7 @@ void CRenderer::RenderParticles(int cullGroup) { PROFILE3_GPU("particles"); + OGL_SCOPED_DEBUG_GROUP("Render particles"); m->particleRenderer.RenderParticles(cullGroup); @@ -1271,7 +1280,8 @@ // RenderSubmissions: force rendering of any batched objects void CRenderer::RenderSubmissions(const CBoundingBoxAligned& waterScissor) { - PROFILE3("render submissions"); + PROFILE3("Render submissions"); + OGL_SCOPED_DEBUG_GROUP("Render submissions"); GetScene().GetLOSTexture().InterpolateLOS(); Index: source/renderer/SkyManager.cpp =================================================================== --- source/renderer/SkyManager.cpp +++ source/renderer/SkyManager.cpp @@ -61,6 +61,9 @@ L"top", L"top" }; + + OGL_SCOPED_DEBUG_GROUP("Load Sky Textures"); + /*for (size_t i = 0; i < ARRAY_SIZE(m_SkyTexture); ++i) { VfsPath path = VfsPath("art/textures/skies") / m_SkySet / (Path::String(s_imageNames[i])+L".dds"); @@ -196,6 +199,8 @@ // Render sky void SkyManager::RenderSky() { + OGL_SCOPED_DEBUG_GROUP("Render Sky"); + #if CONFIG2_GLES #warning TODO: implement SkyManager::RenderSky for GLES #else Index: source/renderer/TerrainRenderer.cpp =================================================================== --- source/renderer/TerrainRenderer.cpp +++ source/renderer/TerrainRenderer.cpp @@ -422,6 +422,7 @@ bool TerrainRenderer::RenderFancyWater(const CShaderDefines& context, int cullGroup, ShadowMap* shadow) { PROFILE3_GPU("fancy water"); + OGL_SCOPED_DEBUG_GROUP("Render Fancy Water"); WaterManager* WaterMgr = g_Renderer.GetWaterManager(); CShaderDefines defines = context; @@ -602,6 +603,8 @@ void TerrainRenderer::RenderSimpleWater(int cullGroup) { + OGL_SCOPED_DEBUG_GROUP("Render Simple Water"); + #if CONFIG2_GLES UNUSED2(cullGroup); #else Index: source/renderer/WaterManager.cpp =================================================================== --- source/renderer/WaterManager.cpp +++ source/renderer/WaterManager.cpp @@ -455,6 +455,8 @@ // This requires m_DistanceHeightmap to be defined properly. void WaterManager::CreateWaveMeshes() { + OGL_SCOPED_DEBUG_GROUP("Create Wave Meshes"); + if (m_MapSize == 0) return; @@ -844,6 +846,8 @@ void WaterManager::RenderWaves(const CFrustum& frustrum) { + OGL_SCOPED_DEBUG_GROUP("Render Waves"); + #if CONFIG2_GLES #warning Fix WaterManager::RenderWaves on GLES #else