2025-07-08 12:41:10 +02:00
# include "MonitorFrameScheduler.hpp"
# include "../config/ConfigValue.hpp"
# include "../Compositor.hpp"
# include "../render/Renderer.hpp"
# include "../managers/eventLoop/EventLoopManager.hpp"
CMonitorFrameScheduler : : CMonitorFrameScheduler ( PHLMONITOR m ) : m_monitor ( m ) {
;
}
void CMonitorFrameScheduler : : onSyncFired ( ) {
static auto PENABLENEW = CConfigValue < Hyprlang : : INT > ( " render:new_render_scheduling " ) ;
if ( ! * PENABLENEW )
return ;
// Sync fired: reset submitted state, set as rendered. Check the last render time. If we are running
// late, we will instantly render here.
if ( std : : chrono : : duration_cast < std : : chrono : : microseconds > ( hrc : : now ( ) - m_lastRenderBegun ) . count ( ) / 1000.F < 1000.F / m_monitor - > m_refreshRate ) {
// we are in. Frame is valid. We can just render as normal.
Debug : : log ( TRACE , " CMonitorFrameScheduler: {} -> onSyncFired, didn't miss. " , m_monitor - > m_name ) ;
m_renderAtFrame = true ;
return ;
}
Debug : : log ( TRACE , " CMonitorFrameScheduler: {} -> onSyncFired, missed. " , m_monitor - > m_name ) ;
// we are out. The frame is taking too long to render. Begin rendering immediately, but don't commit yet.
m_pendingThird = true ;
m_renderAtFrame = false ; // block frame rendering, we already scheduled
m_lastRenderBegun = hrc : : now ( ) ;
g_pHyprRenderer - > renderMonitor ( m_monitor . lock ( ) , false ) ;
onFinishRender ( ) ;
}
void CMonitorFrameScheduler : : onPresented ( ) {
static auto PENABLENEW = CConfigValue < Hyprlang : : INT > ( " render:new_render_scheduling " ) ;
if ( ! * PENABLENEW )
return ;
if ( ! m_pendingThird )
return ;
Debug : : log ( TRACE , " CMonitorFrameScheduler: {} -> onPresented, missed, committing pending. " , m_monitor - > m_name ) ;
m_pendingThird = false ;
Debug : : log ( TRACE , " CMonitorFrameScheduler: {} -> onPresented, missed, committing pending at the earliest convenience. " , m_monitor - > m_name ) ;
m_pendingThird = false ;
g_pEventLoopManager - > doLater ( [ m = m_monitor . lock ( ) ] {
g_pHyprRenderer - > commitPendingAndDoExplicitSync ( m ) ; // commit the pending frame. If it didn't fire yet (is not rendered) it doesn't matter. Syncs will wait.
// schedule a frame: we might have some missed damage, which got cleared due to the above commit.
// TODO: this is not always necessary, but doesn't hurt in general. We likely won't hit this if nothing's happening anyways.
if ( m - > m_damage . hasChanged ( ) )
g_pCompositor - > scheduleFrameForMonitor ( m ) ;
} ) ;
}
void CMonitorFrameScheduler : : onFrame ( ) {
static auto PENABLENEW = CConfigValue < Hyprlang : : INT > ( " render:new_render_scheduling " ) ;
if ( ! canRender ( ) )
return ;
g_pHyprRenderer - > recheckSolitaryForMonitor ( m_monitor . lock ( ) ) ;
m_monitor - > m_tearingState . busy = false ;
if ( m_monitor - > m_tearingState . activelyTearing & & m_monitor - > m_solitaryClient . lock ( ) /* can be invalidated by a recheck */ ) {
if ( ! m_monitor - > m_tearingState . frameScheduledWhileBusy )
return ; // we did not schedule a frame yet to be displayed, but we are tearing. Why render?
m_monitor - > m_tearingState . nextRenderTorn = true ;
m_monitor - > m_tearingState . frameScheduledWhileBusy = false ;
}
if ( ! * PENABLENEW ) {
m_monitor - > m_lastPresentationTimer . reset ( ) ;
g_pHyprRenderer - > renderMonitor ( m_monitor . lock ( ) ) ;
return ;
}
if ( ! m_renderAtFrame ) {
Debug : : log ( TRACE , " CMonitorFrameScheduler: {} -> frame event, but m_renderAtFrame = false. " , m_monitor - > m_name ) ;
return ;
}
Debug : : log ( TRACE , " CMonitorFrameScheduler: {} -> frame event, render = true, rendering normally. " , m_monitor - > m_name ) ;
m_lastRenderBegun = hrc : : now ( ) ;
g_pHyprRenderer - > renderMonitor ( m_monitor . lock ( ) ) ;
onFinishRender ( ) ;
}
void CMonitorFrameScheduler : : onFinishRender ( ) {
m_sync = CEGLSync : : create ( ) ; // this destroys the old sync
g_pEventLoopManager - > doOnReadable ( m_sync - > fd ( ) . duplicate ( ) , [ this , mon = m_monitor ] {
2025-07-17 21:59:20 +02:00
if ( ! mon ) // might've gotten destroyed
2025-07-08 12:41:10 +02:00
return ;
onSyncFired ( ) ;
} ) ;
}
bool CMonitorFrameScheduler : : canRender ( ) {
if ( ( g_pCompositor - > m_aqBackend - > hasSession ( ) & & ! g_pCompositor - > m_aqBackend - > session - > active ) | | ! g_pCompositor - > m_sessionActive | | g_pCompositor - > m_unsafeState ) {
Debug : : log ( WARN , " Attempted to render frame on inactive session! " ) ;
if ( g_pCompositor - > m_unsafeState & & std : : ranges : : any_of ( g_pCompositor - > m_monitors . begin ( ) , g_pCompositor - > m_monitors . end ( ) , [ & ] ( auto & m ) {
return m - > m_output ! = g_pCompositor - > m_unsafeOutput - > m_output ;
} ) ) {
// restore from unsafe state
g_pCompositor - > leaveUnsafeState ( ) ;
}
return false ; // cannot draw on session inactive (different tty)
}
if ( ! m_monitor - > m_enabled )
return false ;
return true ;
}