From 8c10857f14e6c91f3b3099b9508e1f4d68c2d276 Mon Sep 17 00:00:00 2001 From: vaxerski <43317083+vaxerski@users.noreply.github.com> Date: Sat, 28 May 2022 17:32:19 +0200 Subject: [PATCH] Added a debug overlay --- src/Compositor.cpp | 3 + src/Compositor.hpp | 1 + src/config/ConfigManager.cpp | 1 + src/debug/HyprDebugOverlay.cpp | 154 +++++++++++++++++++++++++++++++++ src/debug/HyprDebugOverlay.hpp | 44 ++++++++++ src/events/Monitors.cpp | 18 ++++ src/render/Renderer.cpp | 4 - 7 files changed, 221 insertions(+), 4 deletions(-) create mode 100644 src/debug/HyprDebugOverlay.cpp create mode 100644 src/debug/HyprDebugOverlay.hpp diff --git a/src/Compositor.cpp b/src/Compositor.cpp index faf58440..b554f361 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -192,6 +192,9 @@ void CCompositor::startCompositor() { Debug::log(LOG, "Creating the EventManager!"); g_pEventManager = std::make_unique(); g_pEventManager->startThread(); + + Debug::log(LOG, "Creating the HyprDebugOverlay!"); + g_pDebugOverlay = std::make_unique(); // // diff --git a/src/Compositor.hpp b/src/Compositor.hpp index 8a1a7750..5a3f55c4 100644 --- a/src/Compositor.hpp +++ b/src/Compositor.hpp @@ -15,6 +15,7 @@ #include "managers/KeybindManager.hpp" #include "managers/AnimationManager.hpp" #include "managers/EventManager.hpp" +#include "debug/HyprDebugOverlay.hpp" #include "helpers/Monitor.hpp" #include "helpers/Workspace.hpp" #include "Window.hpp" diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 659627b1..48371fbb 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -35,6 +35,7 @@ void CConfigManager::setDefaultVars() { configValues["debug:int"].intValue = 0; configValues["debug:log_damage"].intValue = 0; + configValues["debug:overlay"].intValue = 0; configValues["decoration:rounding"].intValue = 1; configValues["decoration:blur"].intValue = 1; diff --git a/src/debug/HyprDebugOverlay.cpp b/src/debug/HyprDebugOverlay.cpp new file mode 100644 index 00000000..214965a8 --- /dev/null +++ b/src/debug/HyprDebugOverlay.cpp @@ -0,0 +1,154 @@ +#include "HyprDebugOverlay.hpp" +#include "../Compositor.hpp" + +void CHyprMonitorDebugOverlay::renderData(SMonitor* pMonitor, float µs) { + m_dLastRenderTimes.push_back(µs / 1000.f); + + if (m_dLastRenderTimes.size() > (long unsigned int)pMonitor->refreshRate) + m_dLastRenderTimes.pop_front(); + + if (!m_pMonitor) + m_pMonitor = pMonitor; +} + +void CHyprMonitorDebugOverlay::frameData(SMonitor* pMonitor) { + m_dLastFrametimes.push_back(std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - m_tpLastFrame).count() / 1000.f); + + if (m_dLastFrametimes.size() > (long unsigned int)pMonitor->refreshRate) + m_dLastFrametimes.pop_front(); + + m_tpLastFrame = std::chrono::high_resolution_clock::now(); + + if (!m_pMonitor) + m_pMonitor = pMonitor; +} + +int CHyprMonitorDebugOverlay::draw(int offset) { + + if (!m_pMonitor) + return 0; + + int yOffset = offset; + cairo_text_extents_t cairoExtents; + float maxX = 0; + std::string text = ""; + + // get avg fps + float avgFrametime = 0; + for (auto& ft : m_dLastFrametimes) { + avgFrametime += ft; + } + avgFrametime /= m_dLastFrametimes.size() == 0 ? 1 : m_dLastFrametimes.size(); + + float avgRenderTime = 0; + for (auto& rt : m_dLastRenderTimes) { + avgRenderTime += rt; + } + avgRenderTime /= m_dLastRenderTimes.size() == 0 ? 1 : m_dLastRenderTimes.size(); + + const float FPS = 1.f / (avgFrametime / 1000.f); // frametimes are in ms + const float idealFPS = m_dLastFrametimes.size(); + + cairo_select_font_face(g_pDebugOverlay->m_pCairo, "Noto Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); + + cairo_set_font_size(g_pDebugOverlay->m_pCairo, 10); + cairo_set_source_rgba(g_pDebugOverlay->m_pCairo, 1.f, 1.f, 1.f, 1.f); + + yOffset += 10; + cairo_move_to(g_pDebugOverlay->m_pCairo, 0, yOffset); + text = m_pMonitor->szName; + cairo_show_text(g_pDebugOverlay->m_pCairo, text.c_str()); + cairo_text_extents(g_pDebugOverlay->m_pCairo, text.c_str(), &cairoExtents); + if (cairoExtents.width > maxX) maxX = cairoExtents.width; + + cairo_set_font_size(g_pDebugOverlay->m_pCairo, 16); + + if (FPS > idealFPS * 0.95f) + cairo_set_source_rgba(g_pDebugOverlay->m_pCairo, 0.2f, 1.f, 0.2f, 1.f); + else if (FPS > idealFPS * 0.8f) + cairo_set_source_rgba(g_pDebugOverlay->m_pCairo, 1.f, 1.f, 0.2f, 1.f); + else + cairo_set_source_rgba(g_pDebugOverlay->m_pCairo, 1.f, 0.2f, 0.2f, 1.f); + + yOffset += 17; + cairo_move_to(g_pDebugOverlay->m_pCairo, 0, yOffset); + text = std::string(std::to_string((int)FPS) + " FPS"); + cairo_show_text(g_pDebugOverlay->m_pCairo, text.c_str()); + cairo_text_extents(g_pDebugOverlay->m_pCairo, text.c_str(), &cairoExtents); + if (cairoExtents.width > maxX) maxX = cairoExtents.width; + + cairo_set_font_size(g_pDebugOverlay->m_pCairo, 10); + cairo_set_source_rgba(g_pDebugOverlay->m_pCairo, 1.f, 1.f, 1.f, 1.f); + + yOffset += 11; + cairo_move_to(g_pDebugOverlay->m_pCairo, 0, yOffset); + text = std::string("Avg Frametime: " + std::to_string((int)avgFrametime) + "." + std::to_string((int)(avgFrametime * 10.f) % 10) + "ms"); + cairo_show_text(g_pDebugOverlay->m_pCairo, text.c_str()); + cairo_text_extents(g_pDebugOverlay->m_pCairo, text.c_str(), &cairoExtents); + if (cairoExtents.width > maxX) maxX = cairoExtents.width; + + yOffset += 11; + cairo_move_to(g_pDebugOverlay->m_pCairo, 0, yOffset); + text = std::string("Avg Rendertime: " + std::to_string((int)avgRenderTime) + "." + std::to_string((int)(avgRenderTime * 10.f) % 10) + "ms"); + cairo_show_text(g_pDebugOverlay->m_pCairo, text.c_str()); + cairo_text_extents(g_pDebugOverlay->m_pCairo, text.c_str(), &cairoExtents); + if (cairoExtents.width > maxX) maxX = cairoExtents.width; + + yOffset += 11; + + g_pHyprRenderer->damageBox(&m_wbLastDrawnBox); + m_wbLastDrawnBox = {(int)g_pCompositor->m_lMonitors.front().vecPosition.x, (int)g_pCompositor->m_lMonitors.front().vecPosition.y + offset - 1, (int)maxX + 2, yOffset - offset + 2}; + g_pHyprRenderer->damageBox(&m_wbLastDrawnBox); + + return yOffset - offset; +} + +void CHyprDebugOverlay::renderData(SMonitor* pMonitor, float µs) { + m_mMonitorOverlays[pMonitor].renderData(pMonitor, µs); +} + +void CHyprDebugOverlay::frameData(SMonitor* pMonitor) { + m_mMonitorOverlays[pMonitor].frameData(pMonitor); +} + +void CHyprDebugOverlay::draw() { + + const auto PMONITOR = &g_pCompositor->m_lMonitors.front(); + + if (!m_pCairoSurface || !m_pCairo) { + m_pCairoSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, PMONITOR->vecSize.x, PMONITOR->vecSize.y); + m_pCairo = cairo_create(m_pCairoSurface); + } + + // clear the pixmap + cairo_save(m_pCairo); + cairo_set_operator(m_pCairo, CAIRO_OPERATOR_CLEAR); + cairo_paint(m_pCairo); + cairo_restore(m_pCairo); + + // draw the things + int offsetY = 0; + for (auto& m : g_pCompositor->m_lMonitors) { + offsetY += m_mMonitorOverlays[&m].draw(offsetY); + offsetY += 5; // for padding between mons + } + + cairo_surface_flush(m_pCairoSurface); + + // copy the data to an OpenGL texture we have + const auto DATA = cairo_image_surface_get_data(m_pCairoSurface); + m_tTexture.allocate(); + glBindTexture(GL_TEXTURE_2D, m_tTexture.m_iTexID); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + +#ifndef GLES2 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED); +#endif + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, PMONITOR->vecSize.x, PMONITOR->vecSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA); + + wlr_box pMonBox = {0,0,PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y}; + g_pHyprOpenGL->renderTexture(m_tTexture, &pMonBox, 255.f); +} diff --git a/src/debug/HyprDebugOverlay.hpp b/src/debug/HyprDebugOverlay.hpp new file mode 100644 index 00000000..aa476bf7 --- /dev/null +++ b/src/debug/HyprDebugOverlay.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include "../defines.hpp" +#include "../helpers/Monitor.hpp" +#include "../render/Texture.hpp" +#include +#include +#include + +class CHyprMonitorDebugOverlay { +public: + int draw(int offset); + + void renderData(SMonitor* pMonitor, float µs); + void frameData(SMonitor* pMonitor); + +private: + std::deque m_dLastFrametimes; + std::deque m_dLastRenderTimes; + std::chrono::high_resolution_clock::time_point m_tpLastFrame; + SMonitor* m_pMonitor = nullptr; + wlr_box m_wbLastDrawnBox; +}; + +class CHyprDebugOverlay { +public: + + void draw(); + void renderData(SMonitor*, float µs); + void frameData(SMonitor*); + +private: + + std::unordered_map m_mMonitorOverlays; + + cairo_surface_t* m_pCairoSurface = nullptr; + cairo_t* m_pCairo = nullptr; + + CTexture m_tTexture; + + friend class CHyprMonitorDebugOverlay; +}; + +inline std::unique_ptr g_pDebugOverlay; \ No newline at end of file diff --git a/src/events/Monitors.cpp b/src/events/Monitors.cpp index c89a2d5e..c8f330d7 100644 --- a/src/events/Monitors.cpp +++ b/src/events/Monitors.cpp @@ -131,6 +131,13 @@ void Events::listener_newOutput(wl_listener* listener, void* data) { void Events::listener_monitorFrame(void* owner, void* data) { SMonitor* const PMONITOR = (SMonitor*)owner; + static std::chrono::high_resolution_clock::time_point startRender = std::chrono::high_resolution_clock::now(); + + if (g_pConfigManager->getInt("debug:overlay") == 1) { + startRender = std::chrono::high_resolution_clock::now(); + g_pDebugOverlay->frameData(PMONITOR); + } + // Hack: only check when monitor with top hz refreshes, saves a bit of resources. // This is for stuff that should be run every frame if (PMONITOR->ID == pMostHzMonitor->ID) { @@ -213,6 +220,14 @@ void Events::listener_monitorFrame(void* owner, void* data) { g_pHyprRenderer->renderAllClientsForMonitor(PMONITOR->ID, &now); + // if correct monitor draw hyprerror + if (PMONITOR->ID == 0) + g_pHyprError->draw(); + + // for drawing the debug overlay + if (PMONITOR->ID == 0 && g_pConfigManager->getInt("debug:overlay") == 1) + g_pDebugOverlay->draw(); + wlr_renderer_begin(g_pCompositor->m_sWLRRenderer, PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y); wlr_output_render_software_cursors(PMONITOR->output, NULL); @@ -238,6 +253,9 @@ void Events::listener_monitorFrame(void* owner, void* data) { wlr_output_commit(PMONITOR->output); wlr_output_schedule_frame(PMONITOR->output); + + if (g_pConfigManager->getInt("debug:overlay") == 1) + g_pDebugOverlay->renderData(PMONITOR, std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - startRender).count() / 1000.f); } void Events::listener_monitorDestroy(void* owner, void* data) { diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 982f7277..23ff3073 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -194,10 +194,6 @@ void CHyprRenderer::renderAllClientsForMonitor(const int& ID, timespec* time) { } renderDragIcon(PMONITOR, time); - - // if correct monitor draw hyprerror - if (PMONITOR == &g_pCompositor->m_lMonitors.front()) - g_pHyprError->draw(); } void CHyprRenderer::outputMgrApplyTest(wlr_output_configuration_v1* config, bool test) {