From 560c53d87dedf7df8185eb370cfbf3575826e85c Mon Sep 17 00:00:00 2001 From: Vaxry Date: Mon, 27 Oct 2025 13:34:08 +0000 Subject: [PATCH] monitor/dpms: fix possible invalid state If dpms gets immediately re-enabled, a commit could fail, not schedule any frames anymore, and the monitor would be stuck off. Fix this by adding a timer to retry if commit fails. ref #12045 --- src/helpers/Monitor.cpp | 27 +++++++++++++++++++++++++-- src/helpers/Monitor.hpp | 3 +++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp index ac9c065f..e12a4bff 100644 --- a/src/helpers/Monitor.cpp +++ b/src/helpers/Monitor.cpp @@ -1882,13 +1882,14 @@ void CMonitor::setDPMS(bool on) { if (on) { // enable the monitor. Wait for the frame to be presented, then begin animation - m_dpmsBlackOpacity->setValueAndWarp(1.F); m_dpmsBlackOpacity->setCallbackOnEnd(nullptr); + m_dpmsBlackOpacity->setValueAndWarp(1.F); m_pendingDpmsAnimation = true; m_pendingDpmsAnimationCounter = 0; commitDPMSState(true); } else { // disable the monitor. Begin the animation, then do dpms on its end. + m_dpmsBlackOpacity->setCallbackOnEnd(nullptr); m_dpmsBlackOpacity->setValueAndWarp(0.F); *m_dpmsBlackOpacity = 1.F; m_dpmsBlackOpacity->setCallbackOnEnd( @@ -1908,7 +1909,29 @@ void CMonitor::commitDPMSState(bool state) { m_output->state->setEnabled(state); if (!m_state.commit()) { - Debug::log(ERR, "Couldn't commit output {} for DPMS = {}", m_name, state); + Debug::log(ERR, "Couldn't commit output {} for DPMS = {}, will retry.", m_name, state); + + // retry in 2 frames. This could happen when the DRM backend rejects our commit + // because disable + enable were sent almost instantly + + m_dpmsRetryTimer = makeShared( + std::chrono::milliseconds(2000 / sc(m_refreshRate)), + [this, self = m_self](SP s, void* d) { + if (!self) + return; + + m_output->state->resetExplicitFences(); + m_output->state->setEnabled(m_dpmsStatus); + if (!m_state.commit()) { + Debug::log(ERR, "Couldn't retry committing output {} for DPMS = {}", m_name, m_dpmsStatus); + return; + } + + m_dpmsRetryTimer.reset(); + }, + nullptr); + g_pEventLoopManager->addTimer(m_dpmsRetryTimer); + return; } diff --git a/src/helpers/Monitor.hpp b/src/helpers/Monitor.hpp index 687fc049..ff697725 100644 --- a/src/helpers/Monitor.hpp +++ b/src/helpers/Monitor.hpp @@ -68,6 +68,7 @@ struct SMonitorRule { class CMonitor; class CSyncTimeline; class CEGLSync; +class CEventLoopTimer; class CMonitorState { public: @@ -138,6 +139,8 @@ class CMonitor { bool m_createdByUser = false; bool m_isUnsafeFallback = false; + SP m_dpmsRetryTimer; + bool m_pendingFrame = false; // if we schedule a frame during rendering, reschedule it after bool m_renderingActive = false;