mirror of
https://github.com/hyprwm/Hyprland
synced 2026-05-07 05:18:21 +02:00
debug/overlay: optimize rendering, cleanup and nicetify (#14097)
optimizes the rendering, drops direct cairo, fixes up api. Also improves the visuals etc of the overlay
This commit is contained in:
parent
66ea2e2c9e
commit
b5ea887f07
8 changed files with 617 additions and 333 deletions
|
|
@ -74,7 +74,7 @@
|
|||
#include "plugins/PluginSystem.hpp"
|
||||
#include "hyprerror/HyprError.hpp"
|
||||
#include "notification/NotificationOverlay.hpp"
|
||||
#include "debug/HyprDebugOverlay.hpp"
|
||||
#include "debug/Overlay.hpp"
|
||||
#include "helpers/MonitorFrameScheduler.hpp"
|
||||
#include "i18n/Engine.hpp"
|
||||
#include "layout/LayoutManager.hpp"
|
||||
|
|
@ -591,7 +591,7 @@ void CCompositor::cleanup() {
|
|||
g_pCursorManager.reset();
|
||||
g_pPluginSystem.reset();
|
||||
Notification::overlay().reset();
|
||||
g_pDebugOverlay.reset();
|
||||
Debug::overlay().reset();
|
||||
g_pEventManager.reset();
|
||||
g_pSessionLockManager.reset();
|
||||
g_pHyprRenderer.reset();
|
||||
|
|
@ -694,8 +694,8 @@ void CCompositor::initManagers(eManagersInitStage stage) {
|
|||
Log::logger->log(Log::DEBUG, "Creating the SessionLockManager!");
|
||||
g_pSessionLockManager = makeUnique<CSessionLockManager>();
|
||||
|
||||
Log::logger->log(Log::DEBUG, "Creating the HyprDebugOverlay!");
|
||||
g_pDebugOverlay = makeUnique<CHyprDebugOverlay>();
|
||||
Log::logger->log(Log::DEBUG, "Creating the Debug Overlay!");
|
||||
Debug::overlay();
|
||||
|
||||
Log::logger->log(Log::DEBUG, "Creating the NotificationOverlay!");
|
||||
Notification::overlay();
|
||||
|
|
|
|||
|
|
@ -1,268 +0,0 @@
|
|||
#include <pango/pangocairo.h>
|
||||
#include "HyprDebugOverlay.hpp"
|
||||
#include "config/ConfigValue.hpp"
|
||||
#include "../Compositor.hpp"
|
||||
#include "../render/pass/TexPassElement.hpp"
|
||||
#include "../render/Renderer.hpp"
|
||||
#include "../managers/animation/AnimationManager.hpp"
|
||||
#include "../desktop/state/FocusState.hpp"
|
||||
|
||||
CHyprDebugOverlay::CHyprDebugOverlay() {
|
||||
m_texture = g_pHyprRenderer->createTexture();
|
||||
}
|
||||
|
||||
void CHyprMonitorDebugOverlay::renderData(PHLMONITOR pMonitor, float durationUs) {
|
||||
static auto PDEBUGOVERLAY = CConfigValue<Hyprlang::INT>("debug:overlay");
|
||||
|
||||
if (!*PDEBUGOVERLAY)
|
||||
return;
|
||||
|
||||
m_lastRenderTimes.emplace_back(durationUs / 1000.f);
|
||||
|
||||
if (m_lastRenderTimes.size() > sc<long unsigned int>(pMonitor->m_refreshRate))
|
||||
m_lastRenderTimes.pop_front();
|
||||
|
||||
if (!m_monitor)
|
||||
m_monitor = pMonitor;
|
||||
}
|
||||
|
||||
void CHyprMonitorDebugOverlay::renderDataNoOverlay(PHLMONITOR pMonitor, float durationUs) {
|
||||
static auto PDEBUGOVERLAY = CConfigValue<Hyprlang::INT>("debug:overlay");
|
||||
|
||||
if (!*PDEBUGOVERLAY)
|
||||
return;
|
||||
|
||||
m_lastRenderTimesNoOverlay.emplace_back(durationUs / 1000.f);
|
||||
|
||||
if (m_lastRenderTimesNoOverlay.size() > sc<long unsigned int>(pMonitor->m_refreshRate))
|
||||
m_lastRenderTimesNoOverlay.pop_front();
|
||||
|
||||
if (!m_monitor)
|
||||
m_monitor = pMonitor;
|
||||
}
|
||||
|
||||
void CHyprMonitorDebugOverlay::frameData(PHLMONITOR pMonitor) {
|
||||
static auto PDEBUGOVERLAY = CConfigValue<Hyprlang::INT>("debug:overlay");
|
||||
|
||||
if (!*PDEBUGOVERLAY)
|
||||
return;
|
||||
|
||||
m_lastFrametimes.emplace_back(std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - m_lastFrame).count() / 1000.f);
|
||||
|
||||
if (m_lastFrametimes.size() > sc<long unsigned int>(pMonitor->m_refreshRate))
|
||||
m_lastFrametimes.pop_front();
|
||||
|
||||
m_lastFrame = std::chrono::high_resolution_clock::now();
|
||||
|
||||
if (!m_monitor)
|
||||
m_monitor = pMonitor;
|
||||
|
||||
// anim data too
|
||||
const auto PMONITORFORTICKS = g_pHyprRenderer->m_mostHzMonitor ? g_pHyprRenderer->m_mostHzMonitor.lock() : Desktop::focusState()->monitor();
|
||||
if (PMONITORFORTICKS == pMonitor) {
|
||||
if (m_lastAnimationTicks.size() > sc<long unsigned int>(PMONITORFORTICKS->m_refreshRate))
|
||||
m_lastAnimationTicks.pop_front();
|
||||
|
||||
m_lastAnimationTicks.push_back(g_pAnimationManager->m_lastTickTimeMs);
|
||||
}
|
||||
}
|
||||
|
||||
int CHyprMonitorDebugOverlay::draw(int offset) {
|
||||
|
||||
if (!m_monitor)
|
||||
return 0;
|
||||
|
||||
// get avg fps
|
||||
float avgFrametime = 0;
|
||||
float maxFrametime = 0;
|
||||
float minFrametime = 9999;
|
||||
for (auto const& ft : m_lastFrametimes) {
|
||||
if (ft > maxFrametime)
|
||||
maxFrametime = ft;
|
||||
if (ft < minFrametime)
|
||||
minFrametime = ft;
|
||||
avgFrametime += ft;
|
||||
}
|
||||
float varFrametime = maxFrametime - minFrametime;
|
||||
avgFrametime /= m_lastFrametimes.empty() ? 1 : m_lastFrametimes.size();
|
||||
|
||||
float avgRenderTime = 0;
|
||||
float maxRenderTime = 0;
|
||||
float minRenderTime = 9999;
|
||||
for (auto const& rt : m_lastRenderTimes) {
|
||||
if (rt > maxRenderTime)
|
||||
maxRenderTime = rt;
|
||||
if (rt < minRenderTime)
|
||||
minRenderTime = rt;
|
||||
avgRenderTime += rt;
|
||||
}
|
||||
float varRenderTime = maxRenderTime - minRenderTime;
|
||||
avgRenderTime /= m_lastRenderTimes.empty() ? 1 : m_lastRenderTimes.size();
|
||||
|
||||
float avgRenderTimeNoOverlay = 0;
|
||||
float maxRenderTimeNoOverlay = 0;
|
||||
float minRenderTimeNoOverlay = 9999;
|
||||
for (auto const& rt : m_lastRenderTimesNoOverlay) {
|
||||
if (rt > maxRenderTimeNoOverlay)
|
||||
maxRenderTimeNoOverlay = rt;
|
||||
if (rt < minRenderTimeNoOverlay)
|
||||
minRenderTimeNoOverlay = rt;
|
||||
avgRenderTimeNoOverlay += rt;
|
||||
}
|
||||
float varRenderTimeNoOverlay = maxRenderTimeNoOverlay - minRenderTimeNoOverlay;
|
||||
avgRenderTimeNoOverlay /= m_lastRenderTimes.empty() ? 1 : m_lastRenderTimes.size();
|
||||
|
||||
float avgAnimMgrTick = 0;
|
||||
float maxAnimMgrTick = 0;
|
||||
float minAnimMgrTick = 9999;
|
||||
for (auto const& at : m_lastAnimationTicks) {
|
||||
if (at > maxAnimMgrTick)
|
||||
maxAnimMgrTick = at;
|
||||
if (at < minAnimMgrTick)
|
||||
minAnimMgrTick = at;
|
||||
avgAnimMgrTick += at;
|
||||
}
|
||||
float varAnimMgrTick = maxAnimMgrTick - minAnimMgrTick;
|
||||
avgAnimMgrTick /= m_lastAnimationTicks.empty() ? 1 : m_lastAnimationTicks.size();
|
||||
|
||||
const float FPS = 1.f / (avgFrametime / 1000.f); // frametimes are in ms
|
||||
const float idealFPS = m_lastFrametimes.size();
|
||||
|
||||
static auto fontFamily = CConfigValue<std::string>("misc:font_family");
|
||||
PangoLayout* layoutText = pango_cairo_create_layout(g_pDebugOverlay->m_cairo);
|
||||
PangoFontDescription* pangoFD = pango_font_description_new();
|
||||
|
||||
pango_font_description_set_family(pangoFD, (*fontFamily).c_str());
|
||||
pango_font_description_set_style(pangoFD, PANGO_STYLE_NORMAL);
|
||||
pango_font_description_set_weight(pangoFD, PANGO_WEIGHT_NORMAL);
|
||||
|
||||
float maxTextW = 0;
|
||||
int fontSize = 0;
|
||||
auto cr = g_pDebugOverlay->m_cairo;
|
||||
|
||||
auto showText = [cr, layoutText, pangoFD, &maxTextW, &fontSize](const char* text, int size) {
|
||||
if (fontSize != size) {
|
||||
pango_font_description_set_absolute_size(pangoFD, size * PANGO_SCALE);
|
||||
pango_layout_set_font_description(layoutText, pangoFD);
|
||||
fontSize = size;
|
||||
}
|
||||
|
||||
pango_layout_set_text(layoutText, text, -1);
|
||||
pango_cairo_show_layout(cr, layoutText);
|
||||
|
||||
int textW = 0, textH = 0;
|
||||
pango_layout_get_size(layoutText, &textW, &textH);
|
||||
textW /= PANGO_SCALE;
|
||||
textH /= PANGO_SCALE;
|
||||
if (textW > maxTextW)
|
||||
maxTextW = textW;
|
||||
|
||||
// move to next line
|
||||
cairo_rel_move_to(cr, 0, fontSize + 1);
|
||||
};
|
||||
|
||||
const int MARGIN_TOP = 8;
|
||||
const int MARGIN_LEFT = 4;
|
||||
cairo_move_to(cr, MARGIN_LEFT, MARGIN_TOP + offset);
|
||||
cairo_set_source_rgba(g_pDebugOverlay->m_cairo, 1.f, 1.f, 1.f, 1.f);
|
||||
|
||||
std::string text;
|
||||
showText(m_monitor->m_name.c_str(), 10);
|
||||
|
||||
if (FPS > idealFPS * 0.95f)
|
||||
cairo_set_source_rgba(g_pDebugOverlay->m_cairo, 0.2f, 1.f, 0.2f, 1.f);
|
||||
else if (FPS > idealFPS * 0.8f)
|
||||
cairo_set_source_rgba(g_pDebugOverlay->m_cairo, 1.f, 1.f, 0.2f, 1.f);
|
||||
else
|
||||
cairo_set_source_rgba(g_pDebugOverlay->m_cairo, 1.f, 0.2f, 0.2f, 1.f);
|
||||
|
||||
text = std::format("{} FPS", sc<int>(FPS));
|
||||
showText(text.c_str(), 16);
|
||||
|
||||
cairo_set_source_rgba(g_pDebugOverlay->m_cairo, 1.f, 1.f, 1.f, 1.f);
|
||||
|
||||
text = std::format("Avg Frametime: {:.2f}ms (var {:.2f}ms)", avgFrametime, varFrametime);
|
||||
showText(text.c_str(), 10);
|
||||
|
||||
text = std::format("Avg Rendertime: {:.2f}ms (var {:.2f}ms)", avgRenderTime, varRenderTime);
|
||||
showText(text.c_str(), 10);
|
||||
|
||||
text = std::format("Avg Rendertime (No Overlay): {:.2f}ms (var {:.2f}ms)", avgRenderTimeNoOverlay, varRenderTimeNoOverlay);
|
||||
showText(text.c_str(), 10);
|
||||
|
||||
text = std::format("Avg Anim Tick: {:.2f}ms (var {:.2f}ms) ({:.2f} TPS)", avgAnimMgrTick, varAnimMgrTick, 1.0 / (avgAnimMgrTick / 1000.0));
|
||||
showText(text.c_str(), 10);
|
||||
|
||||
pango_font_description_free(pangoFD);
|
||||
g_object_unref(layoutText);
|
||||
|
||||
double posX = 0, posY = 0;
|
||||
cairo_get_current_point(cr, &posX, &posY);
|
||||
|
||||
g_pHyprRenderer->damageBox(m_lastDrawnBox);
|
||||
m_lastDrawnBox = {sc<int>(g_pCompositor->m_monitors.front()->m_position.x) + MARGIN_LEFT - 1,
|
||||
sc<int>(g_pCompositor->m_monitors.front()->m_position.y) + offset + MARGIN_TOP - 1, sc<int>(maxTextW) + 2, posY - offset - MARGIN_TOP + 2};
|
||||
g_pHyprRenderer->damageBox(m_lastDrawnBox);
|
||||
|
||||
return posY - offset;
|
||||
}
|
||||
|
||||
void CHyprDebugOverlay::renderData(PHLMONITOR pMonitor, float durationUs) {
|
||||
static auto PDEBUGOVERLAY = CConfigValue<Hyprlang::INT>("debug:overlay");
|
||||
|
||||
if (!*PDEBUGOVERLAY)
|
||||
return;
|
||||
|
||||
m_monitorOverlays[pMonitor].renderData(pMonitor, durationUs);
|
||||
}
|
||||
|
||||
void CHyprDebugOverlay::renderDataNoOverlay(PHLMONITOR pMonitor, float durationUs) {
|
||||
static auto PDEBUGOVERLAY = CConfigValue<Hyprlang::INT>("debug:overlay");
|
||||
|
||||
if (!*PDEBUGOVERLAY)
|
||||
return;
|
||||
|
||||
m_monitorOverlays[pMonitor].renderDataNoOverlay(pMonitor, durationUs);
|
||||
}
|
||||
|
||||
void CHyprDebugOverlay::frameData(PHLMONITOR pMonitor) {
|
||||
static auto PDEBUGOVERLAY = CConfigValue<Hyprlang::INT>("debug:overlay");
|
||||
|
||||
if (!*PDEBUGOVERLAY)
|
||||
return;
|
||||
|
||||
m_monitorOverlays[pMonitor].frameData(pMonitor);
|
||||
}
|
||||
|
||||
void CHyprDebugOverlay::draw() {
|
||||
|
||||
const auto PMONITOR = g_pCompositor->m_monitors.front();
|
||||
|
||||
if (!m_cairoSurface || !m_cairo) {
|
||||
m_cairoSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, PMONITOR->m_pixelSize.x, PMONITOR->m_pixelSize.y);
|
||||
m_cairo = cairo_create(m_cairoSurface);
|
||||
}
|
||||
|
||||
// clear the pixmap
|
||||
cairo_save(m_cairo);
|
||||
cairo_set_operator(m_cairo, CAIRO_OPERATOR_CLEAR);
|
||||
cairo_paint(m_cairo);
|
||||
cairo_restore(m_cairo);
|
||||
|
||||
// draw the things
|
||||
int offsetY = 0;
|
||||
for (auto const& m : g_pCompositor->m_monitors) {
|
||||
offsetY += m_monitorOverlays[m].draw(offsetY);
|
||||
offsetY += 5; // for padding between mons
|
||||
}
|
||||
|
||||
cairo_surface_flush(m_cairoSurface);
|
||||
|
||||
// copy the data to an OpenGL texture we have
|
||||
m_texture = g_pHyprRenderer->createTexture(m_cairoSurface);
|
||||
|
||||
CTexPassElement::SRenderData data;
|
||||
data.tex = m_texture;
|
||||
data.box = {0, 0, PMONITOR->m_pixelSize.x, PMONITOR->m_pixelSize.y};
|
||||
g_pHyprRenderer->m_renderPass.add(makeUnique<CTexPassElement>(std::move(data)));
|
||||
}
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "../defines.hpp"
|
||||
#include "../render/Texture.hpp"
|
||||
#include <cairo/cairo.h>
|
||||
#include <map>
|
||||
#include <deque>
|
||||
|
||||
namespace Render {
|
||||
class IHyprRenderer;
|
||||
}
|
||||
|
||||
class CHyprMonitorDebugOverlay {
|
||||
public:
|
||||
int draw(int offset);
|
||||
|
||||
void renderData(PHLMONITOR pMonitor, float durationUs);
|
||||
void renderDataNoOverlay(PHLMONITOR pMonitor, float durationUs);
|
||||
void frameData(PHLMONITOR pMonitor);
|
||||
|
||||
private:
|
||||
std::deque<float> m_lastFrametimes;
|
||||
std::deque<float> m_lastRenderTimes;
|
||||
std::deque<float> m_lastRenderTimesNoOverlay;
|
||||
std::deque<float> m_lastAnimationTicks;
|
||||
std::chrono::high_resolution_clock::time_point m_lastFrame;
|
||||
PHLMONITORREF m_monitor;
|
||||
CBox m_lastDrawnBox;
|
||||
|
||||
friend class Render::IHyprRenderer;
|
||||
};
|
||||
|
||||
class CHyprDebugOverlay {
|
||||
public:
|
||||
CHyprDebugOverlay();
|
||||
void draw();
|
||||
void renderData(PHLMONITOR, float durationUs);
|
||||
void renderDataNoOverlay(PHLMONITOR, float durationUs);
|
||||
void frameData(PHLMONITOR);
|
||||
|
||||
private:
|
||||
std::map<PHLMONITORREF, CHyprMonitorDebugOverlay> m_monitorOverlays;
|
||||
|
||||
cairo_surface_t* m_cairoSurface = nullptr;
|
||||
cairo_t* m_cairo = nullptr;
|
||||
|
||||
SP<Render::ITexture> m_texture;
|
||||
|
||||
friend class CHyprMonitorDebugOverlay;
|
||||
friend class Render::IHyprRenderer;
|
||||
};
|
||||
|
||||
inline UP<CHyprDebugOverlay> g_pDebugOverlay;
|
||||
503
src/debug/Overlay.cpp
Normal file
503
src/debug/Overlay.cpp
Normal file
|
|
@ -0,0 +1,503 @@
|
|||
#include "Overlay.hpp"
|
||||
#include "config/ConfigValue.hpp"
|
||||
#include "../Compositor.hpp"
|
||||
#include "../render/pass/RectPassElement.hpp"
|
||||
#include "../render/pass/TexPassElement.hpp"
|
||||
#include "../render/Renderer.hpp"
|
||||
#include "../managers/animation/AnimationManager.hpp"
|
||||
#include "../desktop/state/FocusState.hpp"
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
namespace {
|
||||
constexpr float OVERLAY_REFRESH_INTERVAL_MS = 200.F;
|
||||
constexpr int OVERLAY_MARGIN_TOP = 4;
|
||||
constexpr int OVERLAY_MARGIN_LEFT = 4;
|
||||
constexpr int OVERLAY_LINE_GAP = 1;
|
||||
constexpr int OVERLAY_MONITOR_GAP = 5;
|
||||
constexpr int OVERLAY_BOX_MARGIN = 5;
|
||||
|
||||
constexpr int OVERLAY_FPS_GRAPH_HISTORY_SEC = 30;
|
||||
constexpr int OVERLAY_FPS_GRAPH_BAR_WIDTH = 3;
|
||||
constexpr int OVERLAY_FPS_GRAPH_BAR_GAP = 1;
|
||||
constexpr int OVERLAY_FPS_GRAPH_HEIGHT = 22;
|
||||
constexpr int OVERLAY_FPS_GRAPH_PADDING = 2;
|
||||
constexpr int OVERLAY_FPS_GRAPH_GAP_TOP = 2;
|
||||
constexpr float OVERLAY_FPS_GRAPH_BG_ALPHA = 0.35F;
|
||||
|
||||
const CHyprColor FPS_COLOR_BAD = CHyprColor{1.F, 0.2F, 0.2F, 1.F};
|
||||
const CHyprColor FPS_COLOR_GOOD = CHyprColor{0.2F, 1.F, 0.2F, 1.F};
|
||||
|
||||
struct SFPSGraphLayout {
|
||||
float innerWidth = 0.F;
|
||||
float innerHeight = 0.F;
|
||||
float width = 0.F;
|
||||
float height = 0.F;
|
||||
};
|
||||
|
||||
struct SFPSGraphDrawResult {
|
||||
float width = 0.F;
|
||||
float bottomY = 0.F;
|
||||
};
|
||||
}
|
||||
|
||||
static Hyprgraphics::CColor::SOkLab lerp(const Hyprgraphics::CColor::SOkLab& a, const Hyprgraphics::CColor::SOkLab& b, float ratio) {
|
||||
return Hyprgraphics::CColor::SOkLab{
|
||||
.l = std::lerp(a.l, b.l, ratio),
|
||||
.a = std::lerp(a.a, b.a, ratio),
|
||||
.b = std::lerp(a.b, b.b, ratio),
|
||||
};
|
||||
}
|
||||
|
||||
static CHyprColor fpsBarColor(float normalizedFPS) {
|
||||
return CHyprColor{Hyprgraphics::CColor{lerp(FPS_COLOR_BAD.asOkLab(), FPS_COLOR_GOOD.asOkLab(), normalizedFPS)}, 1.F};
|
||||
}
|
||||
|
||||
static SFPSGraphLayout fpsGraphLayout() {
|
||||
const float INNERWIDTH = sc<float>(OVERLAY_FPS_GRAPH_HISTORY_SEC * OVERLAY_FPS_GRAPH_BAR_WIDTH + (OVERLAY_FPS_GRAPH_HISTORY_SEC - 1) * OVERLAY_FPS_GRAPH_BAR_GAP);
|
||||
const float INNERHEIGHT = sc<float>(OVERLAY_FPS_GRAPH_HEIGHT);
|
||||
|
||||
return {
|
||||
.innerWidth = INNERWIDTH,
|
||||
.innerHeight = INNERHEIGHT,
|
||||
.width = INNERWIDTH + OVERLAY_FPS_GRAPH_PADDING * 2.F,
|
||||
.height = INNERHEIGHT + OVERLAY_FPS_GRAPH_PADDING * 2.F,
|
||||
};
|
||||
}
|
||||
|
||||
static SFPSGraphDrawResult drawFPSGraph(float x, float y, float idealFPS, const std::deque<float>& fpsHistory) {
|
||||
const auto LAYOUT = fpsGraphLayout();
|
||||
|
||||
CRectPassElement::SRectData bgData;
|
||||
bgData.box = {x, y, LAYOUT.width, LAYOUT.height};
|
||||
bgData.color = CHyprColor{0.F, 0.F, 0.F, OVERLAY_FPS_GRAPH_BG_ALPHA};
|
||||
bgData.round = 2;
|
||||
g_pHyprRenderer->m_renderPass.add(makeUnique<CRectPassElement>(bgData));
|
||||
|
||||
const size_t BARCOUNT = std::min(fpsHistory.size(), sc<size_t>(OVERLAY_FPS_GRAPH_HISTORY_SEC));
|
||||
const size_t LEADINGBLANKBARS = sc<size_t>(OVERLAY_FPS_GRAPH_HISTORY_SEC) - BARCOUNT;
|
||||
|
||||
for (size_t bar = 0; bar < BARCOUNT; ++bar) {
|
||||
const float FPSVALUE = fpsHistory[fpsHistory.size() - BARCOUNT + bar];
|
||||
const float NORMALIZEDFPS = std::clamp(FPSVALUE / idealFPS, 0.F, 1.F);
|
||||
const float BARHEIGHT = std::max(1.F, std::round(NORMALIZEDFPS * LAYOUT.innerHeight));
|
||||
const float BARX = x + OVERLAY_FPS_GRAPH_PADDING + sc<float>((LEADINGBLANKBARS + bar) * (OVERLAY_FPS_GRAPH_BAR_WIDTH + OVERLAY_FPS_GRAPH_BAR_GAP));
|
||||
const float BARY = y + OVERLAY_FPS_GRAPH_PADDING + (LAYOUT.innerHeight - BARHEIGHT);
|
||||
|
||||
CRectPassElement::SRectData barData;
|
||||
barData.box = {BARX, BARY, sc<float>(OVERLAY_FPS_GRAPH_BAR_WIDTH), BARHEIGHT};
|
||||
barData.color = fpsBarColor(NORMALIZEDFPS);
|
||||
g_pHyprRenderer->m_renderPass.add(makeUnique<CRectPassElement>(barData));
|
||||
}
|
||||
|
||||
return {
|
||||
.width = LAYOUT.width,
|
||||
.bottomY = y + LAYOUT.height,
|
||||
};
|
||||
}
|
||||
|
||||
using namespace Debug;
|
||||
|
||||
UP<COverlay>& Debug::overlay() {
|
||||
static UP<COverlay> p = makeUnique<COverlay>();
|
||||
return p;
|
||||
}
|
||||
|
||||
COverlay::COverlay() {
|
||||
m_frameTimer.reset();
|
||||
}
|
||||
|
||||
void CMonitorOverlay::renderData(PHLMONITOR pMonitor, float durationUs) {
|
||||
static auto PDEBUGOVERLAY = CConfigValue<Hyprlang::INT>("debug:overlay");
|
||||
|
||||
if (!*PDEBUGOVERLAY)
|
||||
return;
|
||||
|
||||
m_monitor = pMonitor;
|
||||
|
||||
m_lastRenderTimes.emplace_back(durationUs / 1000.F);
|
||||
|
||||
const auto SAMPLELIMIT = std::max<size_t>(1, sc<size_t>(std::ceil(std::max(1.F, pMonitor->m_refreshRate))));
|
||||
|
||||
if (m_lastRenderTimes.size() > SAMPLELIMIT)
|
||||
m_lastRenderTimes.pop_front();
|
||||
}
|
||||
|
||||
void CMonitorOverlay::renderDataNoOverlay(PHLMONITOR pMonitor, float durationUs) {
|
||||
static auto PDEBUGOVERLAY = CConfigValue<Hyprlang::INT>("debug:overlay");
|
||||
|
||||
if (!*PDEBUGOVERLAY)
|
||||
return;
|
||||
|
||||
m_monitor = pMonitor;
|
||||
|
||||
m_lastRenderTimesNoOverlay.emplace_back(durationUs / 1000.F);
|
||||
|
||||
const auto SAMPLELIMIT = std::max<size_t>(1, sc<size_t>(std::ceil(std::max(1.F, pMonitor->m_refreshRate))));
|
||||
|
||||
if (m_lastRenderTimesNoOverlay.size() > SAMPLELIMIT)
|
||||
m_lastRenderTimesNoOverlay.pop_front();
|
||||
}
|
||||
|
||||
void CMonitorOverlay::frameData(PHLMONITOR pMonitor) {
|
||||
static auto PDEBUGOVERLAY = CConfigValue<Hyprlang::INT>("debug:overlay");
|
||||
|
||||
if (!*PDEBUGOVERLAY)
|
||||
return;
|
||||
|
||||
m_monitor = pMonitor;
|
||||
|
||||
const auto NOW = std::chrono::high_resolution_clock::now();
|
||||
if (m_lastFrame.time_since_epoch().count() != 0)
|
||||
m_lastFrametimes.emplace_back(std::chrono::duration_cast<std::chrono::microseconds>(NOW - m_lastFrame).count() / 1000.F);
|
||||
|
||||
const auto SAMPLELIMIT = std::max<size_t>(1, sc<size_t>(std::ceil(std::max(1.F, pMonitor->m_refreshRate))));
|
||||
|
||||
if (m_lastFrametimes.size() > SAMPLELIMIT)
|
||||
m_lastFrametimes.pop_front();
|
||||
|
||||
m_lastFrame = NOW;
|
||||
|
||||
if (m_fpsSecondStart.time_since_epoch().count() == 0)
|
||||
m_fpsSecondStart = NOW;
|
||||
|
||||
++m_framesInCurrentSecond;
|
||||
|
||||
const auto SECONDWINDOWMS = std::chrono::duration_cast<std::chrono::milliseconds>(NOW - m_fpsSecondStart).count();
|
||||
if (SECONDWINDOWMS >= 1000) {
|
||||
const float ELAPSEDSECONDS = SECONDWINDOWMS / 1000.F;
|
||||
const float IDEALFPS = std::max(1.F, pMonitor->m_refreshRate);
|
||||
const float FPSINWINDOW = ELAPSEDSECONDS > 0.F ? m_framesInCurrentSecond / ELAPSEDSECONDS : 0.F;
|
||||
|
||||
m_lastFPSPerSecond.emplace_back(std::clamp(FPSINWINDOW, 0.F, IDEALFPS));
|
||||
|
||||
if (m_lastFPSPerSecond.size() > OVERLAY_FPS_GRAPH_HISTORY_SEC)
|
||||
m_lastFPSPerSecond.pop_front();
|
||||
|
||||
m_framesInCurrentSecond = 0;
|
||||
m_fpsSecondStart = NOW;
|
||||
}
|
||||
|
||||
// anim data too
|
||||
const auto PMONITORFORTICKS = g_pHyprRenderer->m_mostHzMonitor ? g_pHyprRenderer->m_mostHzMonitor.lock() : Desktop::focusState()->monitor();
|
||||
if (PMONITORFORTICKS && PMONITORFORTICKS == pMonitor) {
|
||||
const auto TICKLIMIT = std::max<size_t>(1, sc<size_t>(std::ceil(std::max(1.F, PMONITORFORTICKS->m_refreshRate))));
|
||||
|
||||
if (m_lastAnimationTicks.size() > TICKLIMIT)
|
||||
m_lastAnimationTicks.pop_front();
|
||||
|
||||
m_lastAnimationTicks.push_back(g_pAnimationManager->m_lastTickTimeMs);
|
||||
}
|
||||
}
|
||||
|
||||
const CBox& CMonitorOverlay::lastDrawnBox() const {
|
||||
return m_lastDrawnBox;
|
||||
}
|
||||
|
||||
void CMonitorOverlay::updateLine(size_t idx, const std::string& text, const CHyprColor& color, int fontSize, const std::string& fontFamily) {
|
||||
if (m_cachedLines.size() <= idx)
|
||||
m_cachedLines.resize(idx + 1);
|
||||
|
||||
auto& line = m_cachedLines[idx];
|
||||
if (line.texture && line.text == text && line.fontSize == fontSize && line.color == color)
|
||||
return;
|
||||
|
||||
line.text = text;
|
||||
line.color = color;
|
||||
line.fontSize = fontSize;
|
||||
line.texture = g_pHyprRenderer->renderText(text, color, fontSize, false, fontFamily);
|
||||
}
|
||||
|
||||
void CMonitorOverlay::rebuildCache() {
|
||||
m_cachedLines.clear();
|
||||
|
||||
if (!m_monitor)
|
||||
return;
|
||||
|
||||
const auto PMONITOR = m_monitor.lock();
|
||||
if (!PMONITOR)
|
||||
return;
|
||||
|
||||
auto metricsFromSamples = [](const std::deque<float>& samples) -> SMetricData {
|
||||
SMetricData metric;
|
||||
|
||||
if (samples.empty())
|
||||
return metric;
|
||||
|
||||
metric.min = std::numeric_limits<float>::max();
|
||||
metric.max = std::numeric_limits<float>::lowest();
|
||||
|
||||
for (const auto sample : samples) {
|
||||
metric.avg += sample;
|
||||
metric.min = std::min(metric.min, sample);
|
||||
metric.max = std::max(metric.max, sample);
|
||||
}
|
||||
|
||||
metric.avg /= samples.size();
|
||||
metric.var = metric.max - metric.min;
|
||||
|
||||
return metric;
|
||||
};
|
||||
|
||||
const auto FRAMEMETRICS = metricsFromSamples(m_lastFrametimes);
|
||||
const auto RENDERMETRICS = metricsFromSamples(m_lastRenderTimes);
|
||||
const auto RENDERMETRICSNOOVL = metricsFromSamples(m_lastRenderTimesNoOverlay);
|
||||
const auto ANIMATIONTICKMETRICS = metricsFromSamples(m_lastAnimationTicks);
|
||||
|
||||
const float FPS = FRAMEMETRICS.avg <= 0.F ? 0.F : 1000.F / FRAMEMETRICS.avg;
|
||||
const float IDEALFPS = std::max(1.F, PMONITOR->m_refreshRate);
|
||||
const float TICKTPS = ANIMATIONTICKMETRICS.avg <= 0.F ? 0.F : 1000.F / ANIMATIONTICKMETRICS.avg;
|
||||
|
||||
static auto FONTFAMILY = CConfigValue<std::string>("misc:font_family");
|
||||
|
||||
CHyprColor fpsColor = CHyprColor{1.F, 0.2F, 0.2F, 1.F};
|
||||
if (FPS > IDEALFPS * 0.95F)
|
||||
fpsColor = CHyprColor{0.2F, 1.F, 0.2F, 1.F};
|
||||
else if (FPS > IDEALFPS * 0.8F)
|
||||
fpsColor = CHyprColor{1.F, 1.F, 0.2F, 1.F};
|
||||
|
||||
size_t idx = 0;
|
||||
updateLine(idx++, PMONITOR->m_name, CHyprColor{1.F, 1.F, 1.F, 1.F}, 10, *FONTFAMILY);
|
||||
updateLine(idx++, std::format("{} FPS", sc<int>(std::round(FPS))), fpsColor, 16, *FONTFAMILY);
|
||||
updateLine(idx++, std::format("Avg Frametime: {:.2f}ms (var {:.2f}ms)", FRAMEMETRICS.avg, FRAMEMETRICS.var), CHyprColor{1.F, 1.F, 1.F, 1.F}, 10, *FONTFAMILY);
|
||||
updateLine(idx++, std::format("Avg Rendertime: {:.2f}ms (var {:.2f}ms)", RENDERMETRICS.avg, RENDERMETRICS.var), CHyprColor{1.F, 1.F, 1.F, 1.F}, 10, *FONTFAMILY);
|
||||
updateLine(idx++, std::format("Avg Rendertime (No Overlay): {:.2f}ms (var {:.2f}ms)", RENDERMETRICSNOOVL.avg, RENDERMETRICSNOOVL.var), CHyprColor{1.F, 1.F, 1.F, 1.F}, 10,
|
||||
*FONTFAMILY);
|
||||
updateLine(idx++, std::format("Avg Anim Tick: {:.2f}ms (var {:.2f}ms) ({:.2f} TPS)", ANIMATIONTICKMETRICS.avg, ANIMATIONTICKMETRICS.var, TICKTPS),
|
||||
CHyprColor{1.F, 1.F, 1.F, 1.F}, 10, *FONTFAMILY);
|
||||
|
||||
m_cachedLines.resize(idx);
|
||||
}
|
||||
|
||||
int CMonitorOverlay::draw(int offset, bool& cacheUpdated) {
|
||||
cacheUpdated = false;
|
||||
m_lastDrawnBox = {};
|
||||
|
||||
if (!m_monitor)
|
||||
return 0;
|
||||
|
||||
if (!m_cacheValid || m_cacheTimer.getMillis() >= OVERLAY_REFRESH_INTERVAL_MS) {
|
||||
rebuildCache();
|
||||
m_cacheValid = true;
|
||||
m_cacheTimer.reset();
|
||||
cacheUpdated = true;
|
||||
}
|
||||
|
||||
const auto PMONITOR = m_monitor.lock();
|
||||
const float IDEALFPS = PMONITOR ? std::max(1.F, PMONITOR->m_refreshRate) : 1.F;
|
||||
|
||||
float y = offset + OVERLAY_MARGIN_TOP + OVERLAY_BOX_MARGIN;
|
||||
float maxTextW = 0.F;
|
||||
|
||||
for (size_t i = 0; i < m_cachedLines.size(); ++i) {
|
||||
const auto& line = m_cachedLines[i];
|
||||
if (!line.texture)
|
||||
continue;
|
||||
|
||||
CTexPassElement::SRenderData data;
|
||||
data.tex = line.texture;
|
||||
data.box = {OVERLAY_MARGIN_LEFT + OVERLAY_BOX_MARGIN, y, line.texture->m_size.x, line.texture->m_size.y};
|
||||
data.a = 1.F;
|
||||
g_pHyprRenderer->m_renderPass.add(makeUnique<CTexPassElement>(std::move(data)));
|
||||
|
||||
maxTextW = std::max(maxTextW, sc<float>(line.texture->m_size.x));
|
||||
y += line.texture->m_size.y + OVERLAY_LINE_GAP;
|
||||
|
||||
if (i == 1) {
|
||||
const auto GRAPHDRAW = drawFPSGraph(OVERLAY_MARGIN_LEFT + OVERLAY_BOX_MARGIN, y + OVERLAY_FPS_GRAPH_GAP_TOP, IDEALFPS, m_lastFPSPerSecond);
|
||||
|
||||
maxTextW = std::max(maxTextW, GRAPHDRAW.width);
|
||||
y = GRAPHDRAW.bottomY + OVERLAY_LINE_GAP;
|
||||
}
|
||||
}
|
||||
|
||||
const float HEIGHT = y - offset - OVERLAY_MARGIN_TOP - OVERLAY_BOX_MARGIN;
|
||||
if (maxTextW <= 0.F || HEIGHT <= 0.F)
|
||||
return 0;
|
||||
|
||||
m_lastDrawnBox = {OVERLAY_MARGIN_LEFT - 1 + OVERLAY_BOX_MARGIN, offset + OVERLAY_MARGIN_TOP + OVERLAY_BOX_MARGIN - 1, sc<int>(std::ceil(maxTextW)) + 2,
|
||||
sc<int>(std::ceil(HEIGHT)) + 2};
|
||||
return sc<int>(std::ceil(y - offset));
|
||||
}
|
||||
|
||||
Vector2D CMonitorOverlay::size() const {
|
||||
return m_lastDrawnBox.size(); // this shouldn't change much
|
||||
}
|
||||
|
||||
void COverlay::renderData(PHLMONITOR pMonitor, float durationUs) {
|
||||
static auto PDEBUGOVERLAY = CConfigValue<Hyprlang::INT>("debug:overlay");
|
||||
|
||||
if (!*PDEBUGOVERLAY)
|
||||
return;
|
||||
|
||||
m_monitorOverlays[pMonitor].renderData(pMonitor, durationUs);
|
||||
}
|
||||
|
||||
void COverlay::renderDataNoOverlay(PHLMONITOR pMonitor, float durationUs) {
|
||||
static auto PDEBUGOVERLAY = CConfigValue<Hyprlang::INT>("debug:overlay");
|
||||
|
||||
if (!*PDEBUGOVERLAY)
|
||||
return;
|
||||
|
||||
m_monitorOverlays[pMonitor].renderDataNoOverlay(pMonitor, durationUs);
|
||||
}
|
||||
|
||||
void COverlay::frameData(PHLMONITOR pMonitor) {
|
||||
static auto PDEBUGOVERLAY = CConfigValue<Hyprlang::INT>("debug:overlay");
|
||||
|
||||
if (!*PDEBUGOVERLAY)
|
||||
return;
|
||||
|
||||
m_monitorOverlays[pMonitor].frameData(pMonitor);
|
||||
}
|
||||
|
||||
void COverlay::createWarningTexture(float maxW) {
|
||||
if (maxW <= 1) {
|
||||
m_warningTexture.reset();
|
||||
m_warningTextureMaxW = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (maxW == m_warningTextureMaxW)
|
||||
return;
|
||||
|
||||
static auto FONT = CConfigValue<std::string>("misc:font_family");
|
||||
|
||||
m_warningTexture = g_pHyprRenderer->renderText(Hyprgraphics::CTextResource::STextResourceData{
|
||||
.text = "[!] FPS might be below your monitor's refresh rate if there are no content updates",
|
||||
.font = *FONT,
|
||||
.fontSize = 8,
|
||||
.color = Colors::YELLOW.asRGB(),
|
||||
.maxSize = Vector2D{maxW, -1.F},
|
||||
});
|
||||
}
|
||||
|
||||
void COverlay::draw() {
|
||||
if (g_pCompositor->m_monitors.empty())
|
||||
return;
|
||||
|
||||
const auto PMONITOR = g_pCompositor->m_monitors.front();
|
||||
if (!PMONITOR)
|
||||
return;
|
||||
|
||||
bool haveAnyBox = false;
|
||||
bool cacheUpdated = false;
|
||||
int minX = 0;
|
||||
int minY = 0;
|
||||
int maxX = 0;
|
||||
int maxY = 0;
|
||||
int offsetY = 0;
|
||||
|
||||
float maxWidth = 0;
|
||||
|
||||
// draw background first
|
||||
{
|
||||
Vector2D fullSize = {};
|
||||
int monitorsWithOverlayData = 0;
|
||||
|
||||
for (const auto& m : g_pCompositor->m_monitors) {
|
||||
const Vector2D size = m_monitorOverlays[m].size();
|
||||
if (size.x <= 0 || size.y <= 0)
|
||||
continue;
|
||||
|
||||
fullSize.y += size.y + OVERLAY_MONITOR_GAP;
|
||||
fullSize.x = std::max(fullSize.x, size.x);
|
||||
++monitorsWithOverlayData;
|
||||
}
|
||||
|
||||
if (monitorsWithOverlayData > 0) {
|
||||
fullSize.y -= OVERLAY_MONITOR_GAP;
|
||||
|
||||
// Each monitor section is offset by OVERLAY_MARGIN_TOP + OVERLAY_BOX_MARGIN in CMonitorOverlay::draw,
|
||||
// while the per-monitor drawn box height only tracks content (+2 px padding).
|
||||
// Account for that inter-section offset so the backdrop spans stacked monitor overlays correctly.
|
||||
fullSize.y += sc<float>((monitorsWithOverlayData - 1) * (OVERLAY_MARGIN_TOP + OVERLAY_BOX_MARGIN - 2));
|
||||
}
|
||||
|
||||
maxWidth = fullSize.x;
|
||||
|
||||
if (fullSize.y > 1 && fullSize.x > 1) {
|
||||
CRectPassElement::SRectData data;
|
||||
data.box = CBox{{OVERLAY_MARGIN_LEFT, OVERLAY_MARGIN_TOP}, fullSize + Vector2D{OVERLAY_BOX_MARGIN, OVERLAY_BOX_MARGIN} * 2.F};
|
||||
data.color = CHyprColor{0.1F, 0.1F, 0.1F, 0.6F};
|
||||
data.round = 10;
|
||||
data.blur = true;
|
||||
g_pHyprRenderer->m_renderPass.add(makeUnique<CRectPassElement>(std::move(data)));
|
||||
|
||||
createWarningTexture(fullSize.x);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto const& monitor : g_pCompositor->m_monitors) {
|
||||
bool monitorUpdated = false;
|
||||
offsetY += m_monitorOverlays[monitor].draw(offsetY, monitorUpdated);
|
||||
cacheUpdated = cacheUpdated || monitorUpdated;
|
||||
|
||||
const auto& BOX = m_monitorOverlays[monitor].lastDrawnBox();
|
||||
if (BOX.width > 0 && BOX.height > 0) {
|
||||
const int boxMinX = sc<int>(std::floor(BOX.x));
|
||||
const int boxMinY = sc<int>(std::floor(BOX.y));
|
||||
const int boxMaxX = sc<int>(std::ceil(BOX.x + BOX.width));
|
||||
const int boxMaxY = sc<int>(std::ceil(BOX.y + BOX.height));
|
||||
|
||||
if (!haveAnyBox) {
|
||||
minX = boxMinX;
|
||||
minY = boxMinY;
|
||||
maxX = boxMaxX;
|
||||
maxY = boxMaxY;
|
||||
haveAnyBox = true;
|
||||
} else {
|
||||
minX = std::min(minX, boxMinX);
|
||||
minY = std::min(minY, boxMinY);
|
||||
maxX = std::max(maxX, boxMaxX);
|
||||
maxY = std::max(maxY, boxMaxY);
|
||||
}
|
||||
}
|
||||
|
||||
offsetY += OVERLAY_MONITOR_GAP;
|
||||
}
|
||||
|
||||
offsetY -= OVERLAY_MONITOR_GAP;
|
||||
|
||||
// render warning texture
|
||||
if (m_warningTexture) {
|
||||
{
|
||||
CRectPassElement::SRectData data;
|
||||
data.box = CBox{{OVERLAY_MARGIN_LEFT, offsetY + (OVERLAY_MARGIN_TOP * 2) + OVERLAY_BOX_MARGIN},
|
||||
{maxWidth + (OVERLAY_BOX_MARGIN * 2.F), m_warningTexture->m_size.y + (OVERLAY_BOX_MARGIN * 2.F)}};
|
||||
data.color = CHyprColor{0.1F, 0.1F, 0.1F, 0.6F};
|
||||
data.round = 10;
|
||||
data.blur = true;
|
||||
g_pHyprRenderer->m_renderPass.add(makeUnique<CRectPassElement>(std::move(data)));
|
||||
}
|
||||
|
||||
{
|
||||
CTexPassElement::SRenderData data;
|
||||
data.box = CBox{
|
||||
Vector2D{OVERLAY_MARGIN_LEFT + ((maxWidth - m_warningTexture->m_size.x) / 2.F), sc<float>(offsetY) + (OVERLAY_MARGIN_TOP * 2) + (OVERLAY_BOX_MARGIN * 2)}.round(),
|
||||
m_warningTexture->m_size};
|
||||
data.tex = m_warningTexture;
|
||||
g_pHyprRenderer->m_renderPass.add(makeUnique<CTexPassElement>(std::move(data)));
|
||||
}
|
||||
}
|
||||
|
||||
CBox newDrawnBox;
|
||||
if (haveAnyBox)
|
||||
newDrawnBox = {sc<int>(PMONITOR->m_position.x) + minX, sc<int>(PMONITOR->m_position.y) + minY, maxX - minX, maxY - minY};
|
||||
|
||||
if (cacheUpdated || newDrawnBox != m_lastDrawnBox) {
|
||||
if (m_lastDrawnBox.width > 0 && m_lastDrawnBox.height > 0)
|
||||
g_pHyprRenderer->damageBox(m_lastDrawnBox);
|
||||
|
||||
if (newDrawnBox.width > 0 && newDrawnBox.height > 0)
|
||||
g_pHyprRenderer->damageBox(newDrawnBox);
|
||||
|
||||
m_lastDrawnBox = newDrawnBox;
|
||||
}
|
||||
|
||||
if (m_frameTimer.getMillis() >= OVERLAY_REFRESH_INTERVAL_MS) {
|
||||
g_pCompositor->scheduleFrameForMonitor(PMONITOR);
|
||||
m_frameTimer.reset();
|
||||
}
|
||||
}
|
||||
88
src/debug/Overlay.hpp
Normal file
88
src/debug/Overlay.hpp
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
#pragma once
|
||||
|
||||
#include "../defines.hpp"
|
||||
#include "../render/Texture.hpp"
|
||||
#include "../helpers/time/Timer.hpp"
|
||||
#include <map>
|
||||
#include <deque>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Render {
|
||||
class IHyprRenderer;
|
||||
class ITexture;
|
||||
}
|
||||
|
||||
namespace Debug {
|
||||
|
||||
class CMonitorOverlay {
|
||||
public:
|
||||
int draw(int offset, bool& cacheUpdated);
|
||||
const CBox& lastDrawnBox() const;
|
||||
|
||||
void renderData(PHLMONITOR pMonitor, float durationUs);
|
||||
void renderDataNoOverlay(PHLMONITOR pMonitor, float durationUs);
|
||||
void frameData(PHLMONITOR pMonitor);
|
||||
|
||||
Vector2D size() const;
|
||||
|
||||
private:
|
||||
struct STextLine {
|
||||
std::string text;
|
||||
CHyprColor color;
|
||||
int fontSize = 10;
|
||||
SP<Render::ITexture> texture;
|
||||
};
|
||||
|
||||
struct SMetricData {
|
||||
float avg = 0.F;
|
||||
float min = 0.F;
|
||||
float max = 0.F;
|
||||
float var = 0.F;
|
||||
};
|
||||
|
||||
void updateLine(size_t idx, const std::string& text, const CHyprColor& color, int fontSize, const std::string& fontFamily);
|
||||
void rebuildCache();
|
||||
|
||||
std::deque<float> m_lastFrametimes;
|
||||
std::deque<float> m_lastFPSPerSecond;
|
||||
std::deque<float> m_lastRenderTimes;
|
||||
std::deque<float> m_lastRenderTimesNoOverlay;
|
||||
std::deque<float> m_lastAnimationTicks;
|
||||
std::chrono::high_resolution_clock::time_point m_lastFrame;
|
||||
std::chrono::high_resolution_clock::time_point m_fpsSecondStart;
|
||||
size_t m_framesInCurrentSecond = 0;
|
||||
PHLMONITORREF m_monitor;
|
||||
|
||||
std::vector<STextLine> m_cachedLines;
|
||||
bool m_cacheValid = false;
|
||||
CTimer m_cacheTimer;
|
||||
|
||||
CBox m_lastDrawnBox;
|
||||
|
||||
friend class Render::IHyprRenderer;
|
||||
};
|
||||
|
||||
class COverlay {
|
||||
public:
|
||||
COverlay();
|
||||
void draw();
|
||||
void renderData(PHLMONITOR, float durationUs);
|
||||
void renderDataNoOverlay(PHLMONITOR, float durationUs);
|
||||
void frameData(PHLMONITOR);
|
||||
|
||||
private:
|
||||
std::map<PHLMONITORREF, CMonitorOverlay> m_monitorOverlays;
|
||||
CBox m_lastDrawnBox;
|
||||
CTimer m_frameTimer;
|
||||
|
||||
void createWarningTexture(float maxW);
|
||||
SP<Render::ITexture> m_warningTexture;
|
||||
float m_warningTextureMaxW = 0;
|
||||
|
||||
friend class CHyprMonitorDebugOverlay;
|
||||
friend class Render::IHyprRenderer;
|
||||
};
|
||||
|
||||
UP<COverlay>& overlay();
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
#include "../protocols/PresentationTime.hpp"
|
||||
#include "../protocols/core/DataDevice.hpp"
|
||||
#include "../protocols/core/Compositor.hpp"
|
||||
#include "../debug/HyprDebugOverlay.hpp"
|
||||
#include "../debug/Overlay.hpp"
|
||||
#include "../helpers/Monitor.hpp"
|
||||
#include "pass/TexPassElement.hpp"
|
||||
#include "pass/SurfacePassElement.hpp"
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
#include "../protocols/LinuxDMABUF.hpp"
|
||||
#include "../helpers/sync/SyncTimeline.hpp"
|
||||
#include "../hyprerror/HyprError.hpp"
|
||||
#include "../debug/HyprDebugOverlay.hpp"
|
||||
#include "../debug/Overlay.hpp"
|
||||
#include "../notification/NotificationOverlay.hpp"
|
||||
#include "../layout/LayoutManager.hpp"
|
||||
#include "../layout/space/Space.hpp"
|
||||
|
|
@ -1523,6 +1523,17 @@ SP<ITexture> IHyprRenderer::renderText(const std::string& text, CHyprColor col,
|
|||
return tex;
|
||||
}
|
||||
|
||||
SP<ITexture> IHyprRenderer::renderText(Hyprgraphics::CTextResource::STextResourceData&& data) {
|
||||
auto res = makeAtomicShared<Hyprgraphics::CTextResource>(std::move(data));
|
||||
g_pAsyncResourceGatherer->enqueue(res);
|
||||
g_pAsyncResourceGatherer->await(res);
|
||||
|
||||
if (!res->m_asset.cairoSurface)
|
||||
return nullptr;
|
||||
|
||||
return createTexture(res->m_asset.pixelSize.x, res->m_asset.pixelSize.y, res->m_asset.cairoSurface->data());
|
||||
}
|
||||
|
||||
void IHyprRenderer::ensureLockTexturesRendered(bool load) {
|
||||
static bool loaded = false;
|
||||
|
||||
|
|
@ -1913,7 +1924,7 @@ void IHyprRenderer::renderMonitor(PHLMONITOR pMonitor, bool commit) {
|
|||
|
||||
if (*PDEBUGOVERLAY == 1) {
|
||||
renderStart = std::chrono::high_resolution_clock::now();
|
||||
g_pDebugOverlay->frameData(pMonitor);
|
||||
Debug::overlay()->frameData(pMonitor);
|
||||
}
|
||||
|
||||
if (!g_pCompositor->m_sessionActive)
|
||||
|
|
@ -2062,7 +2073,7 @@ void IHyprRenderer::renderMonitor(PHLMONITOR pMonitor, bool commit) {
|
|||
// for drawing the debug overlay
|
||||
if (pMonitor == g_pCompositor->m_monitors.front() && *PDEBUGOVERLAY == 1) {
|
||||
renderStartOverlay = std::chrono::high_resolution_clock::now();
|
||||
g_pDebugOverlay->draw();
|
||||
Debug::overlay()->draw();
|
||||
endRenderOverlay = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
|
|
@ -2140,13 +2151,13 @@ void IHyprRenderer::renderMonitor(PHLMONITOR pMonitor, bool commit) {
|
|||
|
||||
if (*PDEBUGOVERLAY == 1) {
|
||||
const float durationUs = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now() - renderStart).count() / 1000.f;
|
||||
g_pDebugOverlay->renderData(pMonitor, durationUs);
|
||||
Debug::overlay()->renderData(pMonitor, durationUs);
|
||||
|
||||
if (pMonitor == g_pCompositor->m_monitors.front()) {
|
||||
const float noOverlayUs = durationUs - std::chrono::duration_cast<std::chrono::nanoseconds>(endRenderOverlay - renderStartOverlay).count() / 1000.f;
|
||||
g_pDebugOverlay->renderDataNoOverlay(pMonitor, noOverlayUs);
|
||||
Debug::overlay()->renderDataNoOverlay(pMonitor, noOverlayUs);
|
||||
} else
|
||||
g_pDebugOverlay->renderDataNoOverlay(pMonitor, durationUs);
|
||||
Debug::overlay()->renderDataNoOverlay(pMonitor, durationUs);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2850,7 +2861,7 @@ bool IHyprRenderer::shouldRenderCursor() {
|
|||
}
|
||||
|
||||
std::tuple<float, float, float> IHyprRenderer::getRenderTimes(PHLMONITOR pMonitor) {
|
||||
const auto POVERLAY = &g_pDebugOverlay->m_monitorOverlays[pMonitor];
|
||||
const auto POVERLAY = &Debug::overlay()->m_monitorOverlays[pMonitor];
|
||||
|
||||
float avgRenderTime = 0;
|
||||
float maxRenderTime = 0;
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@
|
|||
#include "Framebuffer.hpp"
|
||||
#include "Texture.hpp"
|
||||
|
||||
#include <hyprgraphics/resource/resources/TextResource.hpp>
|
||||
|
||||
struct SMonitorRule;
|
||||
class CWorkspace;
|
||||
class CInputPopup;
|
||||
|
|
@ -164,6 +166,7 @@ namespace Render {
|
|||
virtual SP<ITexture> createTexture(const SP<Aquamarine::IBuffer> buffer, bool keepDataCopy = false);
|
||||
virtual SP<ITexture> renderText(const std::string& text, CHyprColor col, int pt, bool italic = false, const std::string& fontFamily = "", int maxWidth = 0,
|
||||
int weight = 400);
|
||||
virtual SP<ITexture> renderText(Hyprgraphics::CTextResource::STextResourceData&& data);
|
||||
SP<ITexture> loadAsset(const std::string& filename);
|
||||
virtual bool shouldUseNewBlurOptimizations(PHLLS pLayer, PHLWINDOW pWindow);
|
||||
virtual bool explicitSyncSupported() = 0;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue