diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 94e49d20..37673014 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -86,6 +86,8 @@ void CConfigManager::setDefaultVars() { configValues["misc:hide_cursor_on_touch"].intValue = 1; configValues["misc:mouse_move_focuses_monitor"].intValue = 1; configValues["misc:suppress_portal_warnings"].intValue = 0; + configValues["misc:render_ahead_of_time"].intValue = 0; + configValues["misc:render_ahead_safezone"].intValue = 1; configValues["debug:int"].intValue = 0; configValues["debug:log_damage"].intValue = 0; diff --git a/src/debug/HyprDebugOverlay.hpp b/src/debug/HyprDebugOverlay.hpp index 67f188d2..03027018 100644 --- a/src/debug/HyprDebugOverlay.hpp +++ b/src/debug/HyprDebugOverlay.hpp @@ -7,6 +7,8 @@ #include #include +class CHyprRenderer; + class CHyprMonitorDebugOverlay { public: int draw(int offset); @@ -23,6 +25,8 @@ class CHyprMonitorDebugOverlay { std::chrono::high_resolution_clock::time_point m_tpLastFrame; CMonitor* m_pMonitor = nullptr; wlr_box m_wbLastDrawnBox; + + friend class CHyprRenderer; }; class CHyprDebugOverlay { @@ -41,6 +45,7 @@ class CHyprDebugOverlay { CTexture m_tTexture; friend class CHyprMonitorDebugOverlay; + friend class CHyprRenderer; }; 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 0ea64e67..ae5095aa 100644 --- a/src/events/Monitors.cpp +++ b/src/events/Monitors.cpp @@ -126,224 +126,37 @@ void Events::listener_monitorFrame(void* owner, void* data) { if (!PMONITOR->m_bEnabled) return; - static std::chrono::high_resolution_clock::time_point startRender = std::chrono::high_resolution_clock::now(); - static std::chrono::high_resolution_clock::time_point startRenderOverlay = std::chrono::high_resolution_clock::now(); - static std::chrono::high_resolution_clock::time_point endRenderOverlay = std::chrono::high_resolution_clock::now(); + static auto* const PENABLERAT = &g_pConfigManager->getConfigValuePtr("misc:render_ahead_of_time")->intValue; + static auto* const PRATSAFE = &g_pConfigManager->getConfigValuePtr("misc:render_ahead_safezone")->intValue; - static auto* const PDEBUGOVERLAY = &g_pConfigManager->getConfigValuePtr("debug:overlay")->intValue; - static auto* const PDAMAGETRACKINGMODE = &g_pConfigManager->getConfigValuePtr("debug:damage_tracking")->intValue; - static auto* const PDAMAGEBLINK = &g_pConfigManager->getConfigValuePtr("debug:damage_blink")->intValue; - static auto* const PNODIRECTSCANOUT = &g_pConfigManager->getConfigValuePtr("misc:no_direct_scanout")->intValue; - static auto* const PVFR = &g_pConfigManager->getConfigValuePtr("misc:vfr")->intValue; + PMONITOR->lastPresentationTimer.reset(); - static int damageBlinkCleanup = 0; // because double-buffered - - if (!*PDAMAGEBLINK) - damageBlinkCleanup = 0; - - if (*PDEBUGOVERLAY == 1) { - startRender = std::chrono::high_resolution_clock::now(); - g_pDebugOverlay->frameData(PMONITOR); - } - - if (PMONITOR->framesToSkip > 0) { - PMONITOR->framesToSkip -= 1; - - if (!PMONITOR->noFrameSchedule) - g_pCompositor->scheduleFrameForMonitor(PMONITOR); - else { - Debug::log(LOG, "NoFrameSchedule hit for %s.", PMONITOR->szName.c_str()); + if (*PENABLERAT) { + if (!PMONITOR->RATScheduled) { + // render + g_pHyprRenderer->renderMonitor(PMONITOR); } - g_pLayoutManager->getCurrentLayout()->recalculateMonitor(PMONITOR->ID); - if (PMONITOR->framesToSkip > 10) - PMONITOR->framesToSkip = 0; - return; - } + PMONITOR->RATScheduled = false; - // checks // - if (PMONITOR->ID == g_pHyprRenderer->m_pMostHzMonitor->ID || - *PVFR == 1) { // unfortunately with VFR we don't have the guarantee mostHz is going to be updated all the time, so we have to ignore that - g_pCompositor->sanityCheckWorkspaces(); + const auto& [avg, max, min] = g_pHyprRenderer->getRenderTimes(PMONITOR); - g_pConfigManager->dispatchExecOnce(); // We exec-once when at least one monitor starts refreshing, meaning stuff has init'd - - if (g_pConfigManager->m_bWantsMonitorReload) - g_pConfigManager->performMonitorReload(); - - g_pHyprRenderer->ensureCursorRenderingMode(); // so that the cursor gets hidden/shown if the user requested timeouts - } - // // - - if (PMONITOR->scheduledRecalc) { - PMONITOR->scheduledRecalc = false; - g_pLayoutManager->getCurrentLayout()->recalculateMonitor(PMONITOR->ID); - } - - // Direct scanout first - if (!*PNODIRECTSCANOUT) { - if (g_pHyprRenderer->attemptDirectScanout(PMONITOR)) { + if (max + *PRATSAFE > 1000.0 / PMONITOR->refreshRate) return; - } else if (g_pHyprRenderer->m_pLastScanout) { - Debug::log(LOG, "Left a direct scanout."); - g_pHyprRenderer->m_pLastScanout = nullptr; - } - } - EMIT_HOOK_EVENT("preRender", PMONITOR); + const auto MSLEFT = 1000.0 / PMONITOR->refreshRate - PMONITOR->lastPresentationTimer.getMillis(); - timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); + PMONITOR->RATScheduled = true; - // check the damage - pixman_region32_t damage; - bool hasChanged; - pixman_region32_init(&damage); + const auto ESTRENDERTIME = std::ceil(avg + *PRATSAFE); + const auto TIMETOSLEEP = std::floor(MSLEFT - ESTRENDERTIME); - if (*PDAMAGETRACKINGMODE == -1) { - Debug::log(CRIT, "Damage tracking mode -1 ????"); - return; - } - - if (!wlr_output_damage_attach_render(PMONITOR->damage, &hasChanged, &damage)) { - Debug::log(ERR, "Couldn't attach render to display %s ???", PMONITOR->szName.c_str()); - return; - } - - PMONITOR->renderingActive = true; - - // we need to cleanup fading out when rendering the appropriate context - g_pCompositor->cleanupFadingOut(PMONITOR->ID); - - if (!hasChanged && *PDAMAGETRACKINGMODE != DAMAGE_TRACKING_NONE && PMONITOR->forceFullFrames == 0 && damageBlinkCleanup == 0) { - pixman_region32_fini(&damage); - wlr_output_rollback(PMONITOR->output); - - if (*PDAMAGEBLINK || *PVFR == 0) - g_pCompositor->scheduleFrameForMonitor(PMONITOR); - - PMONITOR->renderingActive = false; - - return; - } - - // if we have no tracking or full tracking, invalidate the entire monitor - if (*PDAMAGETRACKINGMODE == DAMAGE_TRACKING_NONE || *PDAMAGETRACKINGMODE == DAMAGE_TRACKING_MONITOR || PMONITOR->forceFullFrames > 0 || damageBlinkCleanup > 0 || - PMONITOR->isMirror() /* why??? */) { - pixman_region32_union_rect(&damage, &damage, 0, 0, (int)PMONITOR->vecTransformedSize.x * 10, (int)PMONITOR->vecTransformedSize.y * 10); // wot? - - pixman_region32_copy(&g_pHyprOpenGL->m_rOriginalDamageRegion, &damage); + if (MSLEFT < 1 || MSLEFT < ESTRENDERTIME) + g_pHyprRenderer->renderMonitor(PMONITOR); + else + wl_event_source_timer_update(PMONITOR->renderTimer, TIMETOSLEEP); } else { - static auto* const PBLURENABLED = &g_pConfigManager->getConfigValuePtr("decoration:blur")->intValue; - - // if we use blur we need to expand the damage for proper blurring - if (*PBLURENABLED == 1) { - // TODO: can this be optimized? - static auto* const PBLURSIZE = &g_pConfigManager->getConfigValuePtr("decoration:blur_size")->intValue; - static auto* const PBLURPASSES = &g_pConfigManager->getConfigValuePtr("decoration:blur_passes")->intValue; - const auto BLURRADIUS = - *PBLURPASSES > 10 ? pow(2, 15) : std::clamp(*PBLURSIZE, (int64_t)1, (int64_t)40) * pow(2, *PBLURPASSES); // is this 2^pass? I don't know but it works... I think. - - // now, prep the damage, get the extended damage region - wlr_region_expand(&damage, &damage, BLURRADIUS); // expand for proper blurring - - pixman_region32_copy(&g_pHyprOpenGL->m_rOriginalDamageRegion, &damage); - - wlr_region_expand(&damage, &damage, BLURRADIUS); // expand for proper blurring 2 - } else { - pixman_region32_copy(&g_pHyprOpenGL->m_rOriginalDamageRegion, &damage); - } - } - - if (PMONITOR->forceFullFrames > 0) { - PMONITOR->forceFullFrames -= 1; - if (PMONITOR->forceFullFrames > 10) - PMONITOR->forceFullFrames = 0; - } - - // TODO: this is getting called with extents being 0,0,0,0 should it be? - // potentially can save on resources. - - g_pHyprOpenGL->begin(PMONITOR, &damage); - - if (PMONITOR->isMirror()) { - g_pHyprOpenGL->renderMirrored(); - } else { - g_pHyprOpenGL->clear(CColor(17.0 / 255.0, 17.0 / 255.0, 17.0 / 255.0, 1.0)); - g_pHyprOpenGL->clearWithTex(); // will apply the hypr "wallpaper" - - g_pHyprRenderer->renderAllClientsForMonitor(PMONITOR->ID, &now); - - if (PMONITOR == g_pCompositor->m_pLastMonitor) { - g_pHyprNotificationOverlay->draw(PMONITOR); - g_pHyprError->draw(); - } - - // for drawing the debug overlay - if (PMONITOR == g_pCompositor->m_vMonitors.front().get() && *PDEBUGOVERLAY == 1) { - startRenderOverlay = std::chrono::high_resolution_clock::now(); - g_pDebugOverlay->draw(); - endRenderOverlay = std::chrono::high_resolution_clock::now(); - } - - if (*PDAMAGEBLINK && damageBlinkCleanup == 0) { - wlr_box monrect = {0, 0, PMONITOR->vecTransformedSize.x, PMONITOR->vecTransformedSize.y}; - g_pHyprOpenGL->renderRect(&monrect, CColor(1.0, 0.0, 1.0, 100.0 / 255.0), 0); - damageBlinkCleanup = 1; - } else if (*PDAMAGEBLINK) { - damageBlinkCleanup++; - if (damageBlinkCleanup > 3) - damageBlinkCleanup = 0; - } - - if (wlr_renderer_begin(g_pCompositor->m_sWLRRenderer, PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y)) { - wlr_output_render_software_cursors(PMONITOR->output, NULL); - wlr_renderer_end(g_pCompositor->m_sWLRRenderer); - } - } - - g_pHyprOpenGL->end(); - - // calc frame damage - pixman_region32_t frameDamage; - pixman_region32_init(&frameDamage); - - const auto TRANSFORM = wlr_output_transform_invert(PMONITOR->output->transform); - wlr_region_transform(&frameDamage, &g_pHyprOpenGL->m_rOriginalDamageRegion, TRANSFORM, (int)PMONITOR->vecTransformedSize.x, (int)PMONITOR->vecTransformedSize.y); - - if (*PDAMAGETRACKINGMODE == DAMAGE_TRACKING_NONE || *PDAMAGETRACKINGMODE == DAMAGE_TRACKING_MONITOR) - pixman_region32_union_rect(&frameDamage, &frameDamage, 0, 0, (int)PMONITOR->vecTransformedSize.x, (int)PMONITOR->vecTransformedSize.y); - - if (*PDAMAGEBLINK) - pixman_region32_union(&frameDamage, &frameDamage, &damage); - - wlr_output_set_damage(PMONITOR->output, &frameDamage); - - if (!PMONITOR->mirrors.empty()) - g_pHyprRenderer->damageMirrorsWith(PMONITOR, &frameDamage); - - pixman_region32_fini(&frameDamage); - pixman_region32_fini(&damage); - - PMONITOR->renderingActive = false; - - if (!wlr_output_commit(PMONITOR->output)) - return; - - if (*PDAMAGEBLINK || *PVFR == 0 || PMONITOR->pendingFrame) - g_pCompositor->scheduleFrameForMonitor(PMONITOR); - - PMONITOR->pendingFrame = false; - - if (*PDEBUGOVERLAY == 1) { - const float µs = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - startRender).count() / 1000.f; - g_pDebugOverlay->renderData(PMONITOR, µs); - if (PMONITOR == g_pCompositor->m_vMonitors.front().get()) { - const float µsNoOverlay = µs - std::chrono::duration_cast(endRenderOverlay - startRenderOverlay).count() / 1000.f; - g_pDebugOverlay->renderDataNoOverlay(PMONITOR, µsNoOverlay); - } else { - g_pDebugOverlay->renderDataNoOverlay(PMONITOR, µs); - } + g_pHyprRenderer->renderMonitor(PMONITOR); } } diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp index 6ad60dfa..d8bae3a8 100644 --- a/src/helpers/Monitor.cpp +++ b/src/helpers/Monitor.cpp @@ -2,6 +2,12 @@ #include "../Compositor.hpp" +int ratHandler(void* data) { + g_pHyprRenderer->renderMonitor((CMonitor*)data); + + return 1; +} + void CMonitor::onConnect(bool noRule) { hyprListener_monitorDestroy.removeCallback(); hyprListener_monitorFrame.removeCallback(); @@ -156,10 +162,17 @@ void CMonitor::onConnect(bool noRule) { if (!found) g_pCompositor->setActiveMonitor(this); + + renderTimer = wl_event_loop_add_timer(g_pCompositor->m_sWLEventLoop, ratHandler, this); } void CMonitor::onDisconnect() { + if (renderTimer) { + wl_event_source_remove(renderTimer); + renderTimer = nullptr; + } + if (!m_bEnabled || g_pCompositor->m_bIsShuttingDown) return; diff --git a/src/helpers/Monitor.hpp b/src/helpers/Monitor.hpp index 9ad6d917..0707d79f 100644 --- a/src/helpers/Monitor.hpp +++ b/src/helpers/Monitor.hpp @@ -6,6 +6,7 @@ #include #include #include +#include "Timer.hpp" struct SMonitorRule; @@ -45,6 +46,10 @@ class CMonitor { bool pendingFrame = false; // if we schedule a frame during rendering, reschedule it after bool renderingActive = false; + wl_event_source* renderTimer = nullptr; // for RAT + bool RATScheduled = false; + CTimer lastPresentationTimer; + // mirroring CMonitor* pMirrorOf = nullptr; std::vector mirrors; diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index fcda60cc..a5a576d3 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -740,6 +740,230 @@ bool CHyprRenderer::attemptDirectScanout(CMonitor* pMonitor) { return true; } +void CHyprRenderer::renderMonitor(CMonitor* pMonitor) { + static std::chrono::high_resolution_clock::time_point startRender = std::chrono::high_resolution_clock::now(); + static std::chrono::high_resolution_clock::time_point startRenderOverlay = std::chrono::high_resolution_clock::now(); + static std::chrono::high_resolution_clock::time_point endRenderOverlay = std::chrono::high_resolution_clock::now(); + + static auto* const PDEBUGOVERLAY = &g_pConfigManager->getConfigValuePtr("debug:overlay")->intValue; + static auto* const PDAMAGETRACKINGMODE = &g_pConfigManager->getConfigValuePtr("debug:damage_tracking")->intValue; + static auto* const PDAMAGEBLINK = &g_pConfigManager->getConfigValuePtr("debug:damage_blink")->intValue; + static auto* const PNODIRECTSCANOUT = &g_pConfigManager->getConfigValuePtr("misc:no_direct_scanout")->intValue; + static auto* const PVFR = &g_pConfigManager->getConfigValuePtr("misc:vfr")->intValue; + + static int damageBlinkCleanup = 0; // because double-buffered + + if (!*PDAMAGEBLINK) + damageBlinkCleanup = 0; + + startRender = std::chrono::high_resolution_clock::now(); + + if (*PDEBUGOVERLAY == 1) { + g_pDebugOverlay->frameData(pMonitor); + } + + if (pMonitor->framesToSkip > 0) { + pMonitor->framesToSkip -= 1; + + if (!pMonitor->noFrameSchedule) + g_pCompositor->scheduleFrameForMonitor(pMonitor); + else { + Debug::log(LOG, "NoFrameSchedule hit for %s.", pMonitor->szName.c_str()); + } + g_pLayoutManager->getCurrentLayout()->recalculateMonitor(pMonitor->ID); + + if (pMonitor->framesToSkip > 10) + pMonitor->framesToSkip = 0; + return; + } + + // checks // + if (pMonitor->ID == m_pMostHzMonitor->ID || + *PVFR == 1) { // unfortunately with VFR we don't have the guarantee mostHz is going to be updated all the time, so we have to ignore that + g_pCompositor->sanityCheckWorkspaces(); + + g_pConfigManager->dispatchExecOnce(); // We exec-once when at least one monitor starts refreshing, meaning stuff has init'd + + if (g_pConfigManager->m_bWantsMonitorReload) + g_pConfigManager->performMonitorReload(); + + ensureCursorRenderingMode(); // so that the cursor gets hidden/shown if the user requested timeouts + } + // // + + if (pMonitor->scheduledRecalc) { + pMonitor->scheduledRecalc = false; + g_pLayoutManager->getCurrentLayout()->recalculateMonitor(pMonitor->ID); + } + + // Direct scanout first + if (!*PNODIRECTSCANOUT) { + if (attemptDirectScanout(pMonitor)) { + return; + } else if (m_pLastScanout) { + Debug::log(LOG, "Left a direct scanout."); + m_pLastScanout = nullptr; + } + } + + EMIT_HOOK_EVENT("preRender", pMonitor); + + timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + // check the damage + pixman_region32_t damage; + bool hasChanged; + pixman_region32_init(&damage); + + if (*PDAMAGETRACKINGMODE == -1) { + Debug::log(CRIT, "Damage tracking mode -1 ????"); + return; + } + + if (!wlr_output_damage_attach_render(pMonitor->damage, &hasChanged, &damage)) { + Debug::log(ERR, "Couldn't attach render to display %s ???", pMonitor->szName.c_str()); + return; + } + + pMonitor->renderingActive = true; + + // we need to cleanup fading out when rendering the appropriate context + g_pCompositor->cleanupFadingOut(pMonitor->ID); + + if (!hasChanged && *PDAMAGETRACKINGMODE != DAMAGE_TRACKING_NONE && pMonitor->forceFullFrames == 0 && damageBlinkCleanup == 0) { + pixman_region32_fini(&damage); + wlr_output_rollback(pMonitor->output); + + if (*PDAMAGEBLINK || *PVFR == 0) + g_pCompositor->scheduleFrameForMonitor(pMonitor); + + pMonitor->renderingActive = false; + + return; + } + + // if we have no tracking or full tracking, invalidate the entire monitor + if (*PDAMAGETRACKINGMODE == DAMAGE_TRACKING_NONE || *PDAMAGETRACKINGMODE == DAMAGE_TRACKING_MONITOR || pMonitor->forceFullFrames > 0 || damageBlinkCleanup > 0 || + pMonitor->isMirror() /* why??? */) { + pixman_region32_union_rect(&damage, &damage, 0, 0, (int)pMonitor->vecTransformedSize.x * 10, (int)pMonitor->vecTransformedSize.y * 10); // wot? + + pixman_region32_copy(&g_pHyprOpenGL->m_rOriginalDamageRegion, &damage); + } else { + static auto* const PBLURENABLED = &g_pConfigManager->getConfigValuePtr("decoration:blur")->intValue; + + // if we use blur we need to expand the damage for proper blurring + if (*PBLURENABLED == 1) { + // TODO: can this be optimized? + static auto* const PBLURSIZE = &g_pConfigManager->getConfigValuePtr("decoration:blur_size")->intValue; + static auto* const PBLURPASSES = &g_pConfigManager->getConfigValuePtr("decoration:blur_passes")->intValue; + const auto BLURRADIUS = + *PBLURPASSES > 10 ? pow(2, 15) : std::clamp(*PBLURSIZE, (int64_t)1, (int64_t)40) * pow(2, *PBLURPASSES); // is this 2^pass? I don't know but it works... I think. + + // now, prep the damage, get the extended damage region + wlr_region_expand(&damage, &damage, BLURRADIUS); // expand for proper blurring + + pixman_region32_copy(&g_pHyprOpenGL->m_rOriginalDamageRegion, &damage); + + wlr_region_expand(&damage, &damage, BLURRADIUS); // expand for proper blurring 2 + } else { + pixman_region32_copy(&g_pHyprOpenGL->m_rOriginalDamageRegion, &damage); + } + } + + if (pMonitor->forceFullFrames > 0) { + pMonitor->forceFullFrames -= 1; + if (pMonitor->forceFullFrames > 10) + pMonitor->forceFullFrames = 0; + } + + // TODO: this is getting called with extents being 0,0,0,0 should it be? + // potentially can save on resources. + + g_pHyprOpenGL->begin(pMonitor, &damage); + + if (pMonitor->isMirror()) { + g_pHyprOpenGL->renderMirrored(); + } else { + g_pHyprOpenGL->clear(CColor(17.0 / 255.0, 17.0 / 255.0, 17.0 / 255.0, 1.0)); + g_pHyprOpenGL->clearWithTex(); // will apply the hypr "wallpaper" + + renderAllClientsForMonitor(pMonitor->ID, &now); + + if (pMonitor == g_pCompositor->m_pLastMonitor) { + g_pHyprNotificationOverlay->draw(pMonitor); + g_pHyprError->draw(); + } + + // for drawing the debug overlay + if (pMonitor == g_pCompositor->m_vMonitors.front().get() && *PDEBUGOVERLAY == 1) { + startRenderOverlay = std::chrono::high_resolution_clock::now(); + g_pDebugOverlay->draw(); + endRenderOverlay = std::chrono::high_resolution_clock::now(); + } + + if (*PDAMAGEBLINK && damageBlinkCleanup == 0) { + wlr_box monrect = {0, 0, pMonitor->vecTransformedSize.x, pMonitor->vecTransformedSize.y}; + g_pHyprOpenGL->renderRect(&monrect, CColor(1.0, 0.0, 1.0, 100.0 / 255.0), 0); + damageBlinkCleanup = 1; + } else if (*PDAMAGEBLINK) { + damageBlinkCleanup++; + if (damageBlinkCleanup > 3) + damageBlinkCleanup = 0; + } + + if (wlr_renderer_begin(g_pCompositor->m_sWLRRenderer, pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y)) { + wlr_output_render_software_cursors(pMonitor->output, NULL); + wlr_renderer_end(g_pCompositor->m_sWLRRenderer); + } + } + + g_pHyprOpenGL->end(); + + // calc frame damage + pixman_region32_t frameDamage; + pixman_region32_init(&frameDamage); + + const auto TRANSFORM = wlr_output_transform_invert(pMonitor->output->transform); + wlr_region_transform(&frameDamage, &g_pHyprOpenGL->m_rOriginalDamageRegion, TRANSFORM, (int)pMonitor->vecTransformedSize.x, (int)pMonitor->vecTransformedSize.y); + + if (*PDAMAGETRACKINGMODE == DAMAGE_TRACKING_NONE || *PDAMAGETRACKINGMODE == DAMAGE_TRACKING_MONITOR) + pixman_region32_union_rect(&frameDamage, &frameDamage, 0, 0, (int)pMonitor->vecTransformedSize.x, (int)pMonitor->vecTransformedSize.y); + + if (*PDAMAGEBLINK) + pixman_region32_union(&frameDamage, &frameDamage, &damage); + + wlr_output_set_damage(pMonitor->output, &frameDamage); + + if (!pMonitor->mirrors.empty()) + g_pHyprRenderer->damageMirrorsWith(pMonitor, &frameDamage); + + pixman_region32_fini(&frameDamage); + pixman_region32_fini(&damage); + + pMonitor->renderingActive = false; + + if (!wlr_output_commit(pMonitor->output)) + return; + + if (*PDAMAGEBLINK || *PVFR == 0 || pMonitor->pendingFrame) + g_pCompositor->scheduleFrameForMonitor(pMonitor); + + pMonitor->pendingFrame = false; + + const float µs = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - startRender).count() / 1000.f; + g_pDebugOverlay->renderData(pMonitor, µs); + + if (*PDEBUGOVERLAY == 1) { + if (pMonitor == g_pCompositor->m_vMonitors.front().get()) { + const float µsNoOverlay = µs - std::chrono::duration_cast(endRenderOverlay - startRenderOverlay).count() / 1000.f; + g_pDebugOverlay->renderDataNoOverlay(pMonitor, µsNoOverlay); + } else { + g_pDebugOverlay->renderDataNoOverlay(pMonitor, µs); + } + } +} + void CHyprRenderer::setWindowScanoutMode(CWindow* pWindow) { if (!g_pCompositor->m_sWLRLinuxDMABuf || g_pSessionLockManager->isSessionLocked()) return; @@ -1528,3 +1752,21 @@ void CHyprRenderer::ensureCursorRenderingMode() { bool CHyprRenderer::shouldRenderCursor() { return m_bHasARenderedCursor; } + +std::tuple CHyprRenderer::getRenderTimes(CMonitor* pMonitor) { + const auto POVERLAY = &g_pDebugOverlay->m_mMonitorOverlays[pMonitor]; + + float avgRenderTime = 0; + float maxRenderTime = 0; + float minRenderTime = 9999; + for (auto& rt : POVERLAY->m_dLastRenderTimes) { + if (rt > maxRenderTime) + maxRenderTime = rt; + if (rt < minRenderTime) + minRenderTime = rt; + avgRenderTime += rt; + } + avgRenderTime /= POVERLAY->m_dLastRenderTimes.size() == 0 ? 1 : POVERLAY->m_dLastRenderTimes.size(); + + return std::make_tuple<>(avgRenderTime, maxRenderTime, minRenderTime); +} diff --git a/src/render/Renderer.hpp b/src/render/Renderer.hpp index 9f2456f8..4ce0f6dc 100644 --- a/src/render/Renderer.hpp +++ b/src/render/Renderer.hpp @@ -31,33 +31,35 @@ struct SSessionLockSurface; class CHyprRenderer { public: - void renderAllClientsForMonitor(const int&, timespec*); - void outputMgrApplyTest(wlr_output_configuration_v1*, bool); - void arrangeLayersForMonitor(const int&); - void damageSurface(wlr_surface*, double, double); - void damageWindow(CWindow*); - void damageBox(wlr_box*); - void damageBox(const int& x, const int& y, const int& w, const int& h); - void damageRegion(pixman_region32_t*); - void damageMonitor(CMonitor*); - void damageMirrorsWith(CMonitor*, pixman_region32_t*); - bool applyMonitorRule(CMonitor*, SMonitorRule*, bool force = false); - bool shouldRenderWindow(CWindow*, CMonitor*); - bool shouldRenderWindow(CWindow*); - void ensureCursorRenderingMode(); - bool shouldRenderCursor(); - void calculateUVForSurface(CWindow*, wlr_surface*, bool main = false); + void renderMonitor(CMonitor* pMonitor); + void renderAllClientsForMonitor(const int&, timespec*); + void outputMgrApplyTest(wlr_output_configuration_v1*, bool); + void arrangeLayersForMonitor(const int&); + void damageSurface(wlr_surface*, double, double); + void damageWindow(CWindow*); + void damageBox(wlr_box*); + void damageBox(const int& x, const int& y, const int& w, const int& h); + void damageRegion(pixman_region32_t*); + void damageMonitor(CMonitor*); + void damageMirrorsWith(CMonitor*, pixman_region32_t*); + bool applyMonitorRule(CMonitor*, SMonitorRule*, bool force = false); + bool shouldRenderWindow(CWindow*, CMonitor*); + bool shouldRenderWindow(CWindow*); + void ensureCursorRenderingMode(); + bool shouldRenderCursor(); + void calculateUVForSurface(CWindow*, wlr_surface*, bool main = false); + std::tuple getRenderTimes(CMonitor* pMonitor); // avg max min - bool m_bWindowRequestedCursorHide = false; - bool m_bBlockSurfaceFeedback = false; - bool m_bRenderingSnapshot = false; - CWindow* m_pLastScanout = nullptr; - CMonitor* m_pMostHzMonitor = nullptr; + bool m_bWindowRequestedCursorHide = false; + bool m_bBlockSurfaceFeedback = false; + bool m_bRenderingSnapshot = false; + CWindow* m_pLastScanout = nullptr; + CMonitor* m_pMostHzMonitor = nullptr; - DAMAGETRACKINGMODES damageTrackingModeFromStr(const std::string&); + DAMAGETRACKINGMODES damageTrackingModeFromStr(const std::string&); - bool attemptDirectScanout(CMonitor*); - void setWindowScanoutMode(CWindow*); + bool attemptDirectScanout(CMonitor*); + void setWindowScanoutMode(CWindow*); private: void arrangeLayerArray(CMonitor*, const std::vector>&, bool, wlr_box*);