From 572b46870d3e31e91cac88dce3bb98a1fa0c1cc6 Mon Sep 17 00:00:00 2001 From: UjinT34 Date: Wed, 28 Jan 2026 02:36:27 +0300 Subject: [PATCH] fix fifo state and scheduling --- src/protocols/Fifo.cpp | 89 +++++++++++++---------- src/protocols/Fifo.hpp | 8 +- src/protocols/core/Compositor.cpp | 9 +++ src/protocols/types/SurfaceState.cpp | 7 ++ src/protocols/types/SurfaceState.hpp | 6 ++ src/protocols/types/SurfaceStateQueue.cpp | 3 + 6 files changed, 77 insertions(+), 45 deletions(-) diff --git a/src/protocols/Fifo.cpp b/src/protocols/Fifo.cpp index 386327b53..e491a9ee9 100644 --- a/src/protocols/Fifo.cpp +++ b/src/protocols/Fifo.cpp @@ -18,7 +18,8 @@ CFifoResource::CFifoResource(UP&& resource_, SP s return; } - m_pending.barrierSet = true; + m_surface->m_pending.barrierSet = true; + m_surface->m_pending.updated.bits.fifo = true; }); m_resource->setWaitBarrier([this](CWpFifoV1* r) { @@ -27,54 +28,34 @@ CFifoResource::CFifoResource(UP&& resource_, SP s return; } - if (!m_pending.barrierSet) - return; + if (!m_surface->m_current.barrierSet) { + // that might mean an empty commit with a barrier_set alone + static const auto PPEND = CConfigValue("debug:fifo_pending_workaround"); + if (*PPEND && !m_surface->m_pending.fifoScheduled) + m_surface->m_pending.fifoScheduled = scheduleFrame(); - m_pending.surfaceLocked = true; + return; + } + + m_surface->m_pending.surfaceLocked = true; }); m_listeners.surfaceStateCommit = m_surface->m_events.stateCommit.listen([this](auto state) { - static const auto PPEND = CConfigValue("debug:fifo_pending_workaround"); - - if (!m_pending.surfaceLocked) + if (!state || !state->surfaceLocked) return; - if (*PPEND) { - //#TODO: - // this feels wrong, but if we have no pending frames, presented might never come because - // we are waiting on the barrier to unlock and no damage is around. - // unlock on timeout instead? - if (m_surface->m_enteredOutputs.empty() && m_surface->m_hlSurface) { - for (auto& m : g_pCompositor->m_monitors) { - if (!m || !m->m_enabled) - continue; + static const auto PPEND = CConfigValue("debug:fifo_pending_workaround"); - auto box = m_surface->m_hlSurface->getSurfaceBoxGlobal(); - if (box && !box->intersection({m->m_position, m->m_size}).empty()) { - if (m->m_tearingState.activelyTearing) - return; // dont fifo lock on tearing. - - g_pCompositor->scheduleFrameForMonitor(m, Aquamarine::IOutput::AQ_SCHEDULE_NEEDS_FRAME); - } - } - } else { - for (auto& m : m_surface->m_enteredOutputs) { - if (!m) - continue; - - if (m->m_tearingState.activelyTearing) - return; // dont fifo lock on tearing. - - g_pCompositor->scheduleFrameForMonitor(m.lock(), Aquamarine::IOutput::AQ_SCHEDULE_NEEDS_FRAME); - } - } - } + //#TODO: + // this feels wrong, but if we have no pending frames, presented might never come because + // we are waiting on the barrier to unlock and no damage is around. + // unlock on timeout instead? + if (*PPEND && !state->fifoScheduled) + state->fifoScheduled = scheduleFrame(); // only lock once its mapped. if (m_surface->m_mapped) m_surface->m_stateQueue.lock(state, LOCK_REASON_FIFO); - - m_pending = {}; }); } @@ -87,9 +68,41 @@ bool CFifoResource::good() { } void CFifoResource::presented() { + m_surface->m_current.barrierSet = false; m_surface->m_stateQueue.unlockFirst(LOCK_REASON_FIFO); } +bool CFifoResource::scheduleFrame() { + if (m_surface->m_enteredOutputs.empty() && m_surface->m_hlSurface) { + for (auto& m : g_pCompositor->m_monitors) { + if (!m || !m->m_enabled) + continue; + + auto box = m_surface->m_hlSurface->getSurfaceBoxGlobal(); + if (box && !box->intersection({m->m_position, m->m_size}).empty()) { + if (m->m_tearingState.activelyTearing) + return false; // dont fifo lock on tearing. + + g_pCompositor->scheduleFrameForMonitor(m, Aquamarine::IOutput::AQ_SCHEDULE_NEEDS_FRAME); + return true; + } + } + } else { + for (auto& m : m_surface->m_enteredOutputs) { + if (!m) + continue; + + if (m->m_tearingState.activelyTearing) + return false; // dont fifo lock on tearing. + + g_pCompositor->scheduleFrameForMonitor(m.lock(), Aquamarine::IOutput::AQ_SCHEDULE_NEEDS_FRAME); + return true; + } + } + + return false; +} + CFifoManagerResource::CFifoManagerResource(UP&& resource_) : m_resource(std::move(resource_)) { if UNLIKELY (!m_resource->resource()) return; diff --git a/src/protocols/Fifo.hpp b/src/protocols/Fifo.hpp index 8551e78cd..e67276a06 100644 --- a/src/protocols/Fifo.hpp +++ b/src/protocols/Fifo.hpp @@ -21,18 +21,12 @@ class CFifoResource { WP m_surface; - struct SState { - bool barrierSet = false; - bool surfaceLocked = false; - }; - - SState m_pending; - struct { CHyprSignalListener surfaceStateCommit; } m_listeners; void presented(); + bool scheduleFrame(); friend class CFifoProtocol; friend class CFifoManagerResource; diff --git a/src/protocols/core/Compositor.cpp b/src/protocols/core/Compositor.cpp index 9fc447035..378da6c4b 100644 --- a/src/protocols/core/Compositor.cpp +++ b/src/protocols/core/Compositor.cpp @@ -517,6 +517,15 @@ void CWLSurfaceResource::scheduleState(WP state) { } void CWLSurfaceResource::commitState(SSurfaceState& state) { + // TODO might be incorrect. needed for VRR with FIFO to avoid same buffer extra frames for second commit when it's used in this way: + // wp_fifo_v1#43.set_barrier() + // wp_fifo_v1#43.wait_barrier() + // wl_surface#3.commit() + // wp_fifo_v1#43.wait_barrier() + // wl_surface#3.commit() + if (!state.updated.all && m_mapped) + return; + auto lastTexture = m_current.texture; m_current.updateFrom(state); diff --git a/src/protocols/types/SurfaceState.cpp b/src/protocols/types/SurfaceState.cpp index ecead0086..a85a3c441 100644 --- a/src/protocols/types/SurfaceState.cpp +++ b/src/protocols/types/SurfaceState.cpp @@ -63,6 +63,10 @@ void SSurfaceState::reset() { callbacks.clear(); lockMask = LOCK_REASON_NONE; + + barrierSet = false; + surfaceLocked = false; + fifoScheduled = false; } void SSurfaceState::updateFrom(SSurfaceState& ref) { @@ -112,4 +116,7 @@ void SSurfaceState::updateFrom(SSurfaceState& ref) { callbacks.insert(callbacks.end(), std::make_move_iterator(ref.callbacks.begin()), std::make_move_iterator(ref.callbacks.end())); ref.callbacks.clear(); } + + if (ref.barrierSet) + barrierSet = ref.barrierSet; } diff --git a/src/protocols/types/SurfaceState.hpp b/src/protocols/types/SurfaceState.hpp index dd7679622..315fa4fc4 100644 --- a/src/protocols/types/SurfaceState.hpp +++ b/src/protocols/types/SurfaceState.hpp @@ -48,6 +48,7 @@ struct SSurfaceState { bool acquire : 1; bool acked : 1; bool frame : 1; + bool fifo : 1; } bits; } updated; @@ -88,6 +89,11 @@ struct SSurfaceState { SP texture; void updateSynchronousTexture(SP lastTexture); + // fifo + bool barrierSet = false; + bool surfaceLocked = false; + bool fifoScheduled = false; + // helpers CRegion accumulateBufferDamage(); // transforms state.damage and merges it into state.bufferDamage void updateFrom(SSurfaceState& ref); // updates this state based on a reference state. diff --git a/src/protocols/types/SurfaceStateQueue.cpp b/src/protocols/types/SurfaceStateQueue.cpp index 348ac7118..82a04878a 100644 --- a/src/protocols/types/SurfaceStateQueue.cpp +++ b/src/protocols/types/SurfaceStateQueue.cpp @@ -68,6 +68,9 @@ auto CSurfaceStateQueue::find(const WP& state) -> std::dequelockMask & LOCK_REASON_FIFO && !m_surface->m_current.barrierSet) + front->lockMask &= ~LOCK_REASON_FIFO; + if (front->lockMask != LOCK_REASON_NONE) return;