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 ) {
;
}
2025-07-19 12:38:41 +02:00
bool CMonitorFrameScheduler : : newSchedulingEnabled ( ) {
2025-07-08 12:41:10 +02:00
static auto PENABLENEW = CConfigValue < Hyprlang : : INT > ( " render:new_render_scheduling " ) ;
2026-02-14 00:52:00 +01:00
return * PENABLENEW & & g_pHyprOpenGL - > explicitSyncSupported ( ) & & m_monitor & & ! m_monitor - > m_directScanoutIsActive ;
2025-07-19 12:38:41 +02:00
}
void CMonitorFrameScheduler : : onSyncFired ( ) {
if ( ! newSchedulingEnabled ( ) )
2025-07-08 12:41:10 +02:00
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.
2025-12-18 17:23:24 +00:00
Log : : logger - > log ( Log : : TRACE , " CMonitorFrameScheduler: {} -> onSyncFired, didn't miss. " , m_monitor - > m_name ) ;
2025-07-08 12:41:10 +02:00
m_renderAtFrame = true ;
return ;
}
2025-12-18 17:23:24 +00:00
Log : : logger - > log ( Log : : TRACE , " CMonitorFrameScheduler: {} -> onSyncFired, missed. " , m_monitor - > m_name ) ;
2025-07-08 12:41:10 +02:00
// 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 ( ) ;
2025-08-16 16:52:28 +02:00
// get a ref to ourselves. renderMonitor can destroy this scheduler if it decides to perform a monitor reload
// FIXME: this is horrible. "renderMonitor" should not be able to do that.
auto self = m_self ;
2025-07-08 12:41:10 +02:00
g_pHyprRenderer - > renderMonitor ( m_monitor . lock ( ) , false ) ;
2025-08-16 16:52:28 +02:00
if ( ! self )
return ;
2025-07-08 12:41:10 +02:00
onFinishRender ( ) ;
}
void CMonitorFrameScheduler : : onPresented ( ) {
2025-07-19 12:38:41 +02:00
if ( ! newSchedulingEnabled ( ) )
2025-07-08 12:41:10 +02:00
return ;
if ( ! m_pendingThird )
return ;
2025-12-18 17:23:24 +00:00
Log : : logger - > log ( Log : : TRACE , " CMonitorFrameScheduler: {} -> onPresented, missed, committing pending. " , m_monitor - > m_name ) ;
2025-07-08 12:41:10 +02:00
m_pendingThird = false ;
2025-12-18 17:23:24 +00:00
Log : : logger - > log ( Log : : TRACE , " CMonitorFrameScheduler: {} -> onPresented, missed, committing pending at the earliest convenience. " , m_monitor - > m_name ) ;
2025-07-08 12:41:10 +02:00
m_pendingThird = false ;
g_pEventLoopManager - > doLater ( [ m = m_monitor . lock ( ) ] {
2025-07-18 12:09:43 +02:00
if ( ! m )
return ;
2025-07-08 12:41:10 +02:00
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 ( ) {
if ( ! canRender ( ) )
return ;
2025-08-22 20:24:25 +03:00
m_monitor - > recheckSolitary ( ) ;
2025-07-08 12:41:10 +02:00
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 ;
}
2025-07-19 12:38:41 +02:00
if ( ! newSchedulingEnabled ( ) ) {
2025-07-08 12:41:10 +02:00
m_monitor - > m_lastPresentationTimer . reset ( ) ;
g_pHyprRenderer - > renderMonitor ( m_monitor . lock ( ) ) ;
return ;
}
if ( ! m_renderAtFrame ) {
2025-12-18 17:23:24 +00:00
Log : : logger - > log ( Log : : TRACE , " CMonitorFrameScheduler: {} -> frame event, but m_renderAtFrame = false. " , m_monitor - > m_name ) ;
2025-07-08 12:41:10 +02:00
return ;
}
2025-12-18 17:23:24 +00:00
Log : : logger - > log ( Log : : TRACE , " CMonitorFrameScheduler: {} -> frame event, render = true, rendering normally. " , m_monitor - > m_name ) ;
2025-07-08 12:41:10 +02:00
m_lastRenderBegun = hrc : : now ( ) ;
2025-08-16 16:52:28 +02:00
// get a ref to ourselves. renderMonitor can destroy this scheduler if it decides to perform a monitor reload
// FIXME: this is horrible. "renderMonitor" should not be able to do that.
auto self = m_self ;
2025-07-08 12:41:10 +02:00
g_pHyprRenderer - > renderMonitor ( m_monitor . lock ( ) ) ;
2025-08-16 16:52:28 +02:00
if ( ! self )
return ;
2025-07-08 12:41:10 +02:00
onFinishRender ( ) ;
}
void CMonitorFrameScheduler : : onFinishRender ( ) {
m_sync = CEGLSync : : create ( ) ; // this destroys the old sync
2025-08-16 16:52:28 +02:00
g_pEventLoopManager - > doOnReadable ( m_sync - > fd ( ) . duplicate ( ) , [ this , self = m_self ] {
if ( ! self ) // 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 ) {
2025-12-18 17:23:24 +00:00
Log : : logger - > log ( Log : : WARN , " Attempted to render frame on inactive session! " ) ;
2025-07-08 12:41:10 +02:00
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 ;
}