renderer: improve modeset timings (#11461)

some CRTCs will just happily eat frames and we can't do much. Some will eat one.

Adds a 5-frame buffer to DPMS and Added animations. Better than nothing.
This commit is contained in:
Vaxry
2025-08-17 17:14:29 +01:00
committed by GitHub
parent 251288ec59
commit 0840103ae0
3 changed files with 38 additions and 8 deletions

View File

@@ -66,6 +66,7 @@ void CMonitor::onConnect(bool noRule) {
CScopeGuard x = {[]() { g_pCompositor->arrangeMonitors(); }};
m_zoomAnimProgress->setValueAndWarp(0.F);
m_zoomAnimFrameCounter = 0;
g_pEventLoopManager->doLater([] { g_pConfigManager->ensurePersistentWorkspacesPresent(); });
@@ -83,10 +84,17 @@ void CMonitor::onConnect(bool noRule) {
m_listeners.presented = m_output->events.present.listen([this](const Aquamarine::IOutput::SPresentEvent& event) {
if (m_pendingDpmsAnimation) {
// the first frame after a dpms on has been presented. Let's start the animation
m_pendingDpmsAnimationCounter++;
// we give ourselves 5 frames of a buffer. The first presentation event still doesn't usually say that we actually
// are scanning out to the CRTC, and it could still be modesetting.
// this is not ideal (some CRTCs will just eat frames) but it's better than nothing
m_dpmsBlackOpacity->setValueAndWarp(1.F);
*m_dpmsBlackOpacity = 0.F;
m_pendingDpmsAnimation = false;
if (m_pendingDpmsAnimationCounter == 5) {
*m_dpmsBlackOpacity = 0.F;
m_pendingDpmsAnimation = false;
}
}
timespec* ts = event.when;
@@ -102,8 +110,26 @@ void CMonitor::onConnect(bool noRule) {
else
PROTO::presentation->onPresented(m_self.lock(), Time::fromTimespec(event.when), event.refresh, event.seq, event.flags);
if (m_zoomAnimProgress->goal() == 0.F)
*m_zoomAnimProgress = 1.F;
if (m_zoomAnimFrameCounter < 5) {
m_zoomAnimFrameCounter++;
// we give ourselves 5 frames of a buffer. The first presentation event still doesn't usually say that we actually
// are scanning out to the CRTC, and it could still be modesetting.
// this is not ideal (some CRTCs will just eat frames) but it's better than nothing
m_zoomAnimProgress->setValueAndWarp(0.F);
if (m_zoomAnimFrameCounter == 5) {
// start the animation for realzies
*m_zoomAnimProgress = 1.F;
}
// damage the entire display to force a frame immediately
g_pEventLoopManager->doLater([self = m_self] {
if (!self)
return;
g_pHyprRenderer->damageMonitor(self.lock());
});
}
m_frameScheduler->onPresented();
});
@@ -1580,7 +1606,8 @@ void CMonitor::setDPMS(bool on) {
// enable the monitor. Wait for the frame to be presented, then begin animation
m_dpmsBlackOpacity->setValueAndWarp(1.F);
m_dpmsBlackOpacity->setCallbackOnEnd(nullptr);
m_pendingDpmsAnimation = true;
m_pendingDpmsAnimation = true;
m_pendingDpmsAnimationCounter = 0;
commitDPMSState(true);
} else {
// disable the monitor. Begin the animation, then do dpms on its end.

View File

@@ -183,12 +183,15 @@ class CMonitor {
// for dpms off anim
PHLANIMVAR<float> m_dpmsBlackOpacity;
bool m_pendingDpmsAnimation = false;
bool m_pendingDpmsAnimation = false;
int m_pendingDpmsAnimationCounter = 0;
PHLANIMVAR<float> m_cursorZoom;
// for initial zoom anim
PHLANIMVAR<float> m_zoomAnimProgress;
CTimer m_newMonitorAnimTimer;
int m_zoomAnimFrameCounter = 0;
struct {
bool canTear = false;

View File

@@ -1310,7 +1310,7 @@ void CHyprRenderer::renderMonitor(PHLMONITOR pMonitor, bool commit) {
else
g_pHyprOpenGL->m_renderData.mouseZoomFactor = 1.f;
if (pMonitor->m_zoomAnimProgress->isBeingAnimated()) {
if (pMonitor->m_zoomAnimProgress->value() != 1) {
g_pHyprOpenGL->m_renderData.mouseZoomFactor = 2.0 - pMonitor->m_zoomAnimProgress->value(); // 2x zoom -> 1x zoom
g_pHyprOpenGL->m_renderData.mouseZoomUseMouse = false;
g_pHyprOpenGL->m_renderData.useNearestNeighbor = false;