fifo: miscellaneous fifo fixes (#13136)

* LOGM: clang-tidy fix

* fix fifo state and scheduling

* disable fifo_pending_workaround by default

* fix tearing

* fix "empty" commit skipping
This commit is contained in:
UjinT34 2026-02-04 03:27:48 +03:00 committed by GitHub
parent cd7bdc7a43
commit 1bc857b12c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 82 additions and 47 deletions

View file

@ -1846,7 +1846,7 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.value = "debug:fifo_pending_workaround", .value = "debug:fifo_pending_workaround",
.description = "Fifo workaround for empty pending list", .description = "Fifo workaround for empty pending list",
.type = CONFIG_OPTION_BOOL, .type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{true}, .data = SConfigOptionDescription::SBoolData{false},
}, },
SConfigOptionDescription{ SConfigOptionDescription{
.value = "debug:render_solitary_wo_damage", .value = "debug:render_solitary_wo_damage",

View file

@ -574,7 +574,7 @@ CConfigManager::CConfigManager() {
registerConfigVar("debug:full_cm_proto", Hyprlang::INT{0}); registerConfigVar("debug:full_cm_proto", Hyprlang::INT{0});
registerConfigVar("debug:ds_handle_same_buffer", Hyprlang::INT{1}); registerConfigVar("debug:ds_handle_same_buffer", Hyprlang::INT{1});
registerConfigVar("debug:ds_handle_same_buffer_fifo", Hyprlang::INT{1}); registerConfigVar("debug:ds_handle_same_buffer_fifo", Hyprlang::INT{1});
registerConfigVar("debug:fifo_pending_workaround", Hyprlang::INT{1}); registerConfigVar("debug:fifo_pending_workaround", Hyprlang::INT{0});
registerConfigVar("debug:render_solitary_wo_damage", Hyprlang::INT{0}); registerConfigVar("debug:render_solitary_wo_damage", Hyprlang::INT{0});
registerConfigVar("decoration:rounding", Hyprlang::INT{0}); registerConfigVar("decoration:rounding", Hyprlang::INT{0});

View file

@ -18,7 +18,8 @@ CFifoResource::CFifoResource(UP<CWpFifoV1>&& resource_, SP<CWLSurfaceResource> s
return; 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) { m_resource->setWaitBarrier([this](CWpFifoV1* r) {
@ -27,54 +28,37 @@ CFifoResource::CFifoResource(UP<CWpFifoV1>&& resource_, SP<CWLSurfaceResource> s
return; return;
} }
if (!m_pending.barrierSet) if (!m_surface->m_current.barrierSet) {
return; // that might mean an empty commit with a barrier_set alone
static const auto PPEND = CConfigValue<Hyprlang::INT>("debug:fifo_pending_workaround");
if (!m_surface->m_pending.fifoScheduled)
m_surface->m_pending.fifoScheduled = checkMonitors(*PPEND);
m_pending.surfaceLocked = true; return;
}
m_surface->m_pending.surfaceLocked = true;
}); });
m_listeners.surfaceStateCommit = m_surface->m_events.stateCommit.listen([this](auto state) { m_listeners.surfaceStateCommit = m_surface->m_events.stateCommit.listen([this](auto state) {
static const auto PPEND = CConfigValue<Hyprlang::INT>("debug:fifo_pending_workaround"); if (!state || !state->surfaceLocked)
if (!m_pending.surfaceLocked)
return; return;
if (*PPEND) { static const auto PPEND = CConfigValue<Hyprlang::INT>("debug:fifo_pending_workaround");
//#TODO: //#TODO:
// this feels wrong, but if we have no pending frames, presented might never come because // 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. // we are waiting on the barrier to unlock and no damage is around.
// unlock on timeout instead? // unlock on timeout instead?
if (m_surface->m_enteredOutputs.empty() && m_surface->m_hlSurface) { if (!state->fifoScheduled)
for (auto& m : g_pCompositor->m_monitors) { state->fifoScheduled = checkMonitors(*PPEND);
if (!m || !m->m_enabled)
continue;
auto box = m_surface->m_hlSurface->getSurfaceBoxGlobal(); if (!state->fifoScheduled)
if (box && !box->intersection({m->m_position, m->m_size}).empty()) { return;
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);
}
}
}
// only lock once its mapped. // only lock once its mapped.
if (m_surface->m_mapped) if (m_surface->m_mapped)
m_surface->m_stateQueue.lock(state, LOCK_REASON_FIFO); m_surface->m_stateQueue.lock(state, LOCK_REASON_FIFO);
m_pending = {};
}); });
} }
@ -87,9 +71,41 @@ bool CFifoResource::good() {
} }
void CFifoResource::presented() { void CFifoResource::presented() {
m_surface->m_current.barrierSet = false;
m_surface->m_stateQueue.unlockFirst(LOCK_REASON_FIFO); m_surface->m_stateQueue.unlockFirst(LOCK_REASON_FIFO);
} }
bool CFifoResource::checkMonitors(bool needsSchedule) {
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.
if (needsSchedule)
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 false; // dont fifo lock on tearing.
if (needsSchedule)
g_pCompositor->scheduleFrameForMonitor(m.lock(), Aquamarine::IOutput::AQ_SCHEDULE_NEEDS_FRAME);
}
}
return true;
}
CFifoManagerResource::CFifoManagerResource(UP<CWpFifoManagerV1>&& resource_) : m_resource(std::move(resource_)) { CFifoManagerResource::CFifoManagerResource(UP<CWpFifoManagerV1>&& resource_) : m_resource(std::move(resource_)) {
if UNLIKELY (!m_resource->resource()) if UNLIKELY (!m_resource->resource())
return; return;

View file

@ -21,18 +21,12 @@ class CFifoResource {
WP<CWLSurfaceResource> m_surface; WP<CWLSurfaceResource> m_surface;
struct SState {
bool barrierSet = false;
bool surfaceLocked = false;
};
SState m_pending;
struct { struct {
CHyprSignalListener surfaceStateCommit; CHyprSignalListener surfaceStateCommit;
} m_listeners; } m_listeners;
void presented(); void presented();
bool checkMonitors(bool needsSchedule = false);
friend class CFifoProtocol; friend class CFifoProtocol;
friend class CFifoManagerResource; friend class CFifoManagerResource;

View file

@ -34,7 +34,7 @@
} else if (level == Log::DEBUG || level == Log::INFO || level == Log::TRACE) { \ } else if (level == Log::DEBUG || level == Log::INFO || level == Log::TRACE) { \
oss << "[" << EXTRACT_CLASS_NAME() << "] "; \ oss << "[" << EXTRACT_CLASS_NAME() << "] "; \
} \ } \
if constexpr (std::tuple_size<decltype(std::make_tuple(__VA_ARGS__))>::value == 1 && std::is_same_v<decltype(__VA_ARGS__), std::string>) { \ if constexpr (std::tuple_size_v<decltype(std::make_tuple(__VA_ARGS__))> == 1 && std::is_same_v<decltype(__VA_ARGS__), std::string>) { \
oss << __VA_ARGS__; \ oss << __VA_ARGS__; \
Log::logger->log(level, oss.str()); \ Log::logger->log(level, oss.str()); \
} else { \ } else { \

View file

@ -517,6 +517,15 @@ void CWLSurfaceResource::scheduleState(WP<SSurfaceState> state) {
} }
void CWLSurfaceResource::commitState(SSurfaceState& 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 && state.fifoScheduled)
return;
auto lastTexture = m_current.texture; auto lastTexture = m_current.texture;
m_current.updateFrom(state); m_current.updateFrom(state);

View file

@ -63,6 +63,10 @@ void SSurfaceState::reset() {
callbacks.clear(); callbacks.clear();
lockMask = LOCK_REASON_NONE; lockMask = LOCK_REASON_NONE;
barrierSet = false;
surfaceLocked = false;
fifoScheduled = false;
} }
void SSurfaceState::updateFrom(SSurfaceState& ref) { 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())); callbacks.insert(callbacks.end(), std::make_move_iterator(ref.callbacks.begin()), std::make_move_iterator(ref.callbacks.end()));
ref.callbacks.clear(); ref.callbacks.clear();
} }
if (ref.barrierSet)
barrierSet = ref.barrierSet;
} }

View file

@ -48,6 +48,7 @@ struct SSurfaceState {
bool acquire : 1; bool acquire : 1;
bool acked : 1; bool acked : 1;
bool frame : 1; bool frame : 1;
bool fifo : 1;
} bits; } bits;
} updated; } updated;
@ -88,6 +89,11 @@ struct SSurfaceState {
SP<CTexture> texture; SP<CTexture> texture;
void updateSynchronousTexture(SP<CTexture> lastTexture); void updateSynchronousTexture(SP<CTexture> lastTexture);
// fifo
bool barrierSet = false;
bool surfaceLocked = false;
bool fifoScheduled = false;
// helpers // helpers
CRegion accumulateBufferDamage(); // transforms state.damage and merges it into state.bufferDamage CRegion accumulateBufferDamage(); // transforms state.damage and merges it into state.bufferDamage
void updateFrom(SSurfaceState& ref); // updates this state based on a reference state. void updateFrom(SSurfaceState& ref); // updates this state based on a reference state.

View file

@ -68,6 +68,9 @@ auto CSurfaceStateQueue::find(const WP<SSurfaceState>& state) -> std::deque<UP<S
void CSurfaceStateQueue::tryProcess() { void CSurfaceStateQueue::tryProcess() {
while (!m_queue.empty()) { while (!m_queue.empty()) {
auto& front = m_queue.front(); auto& front = m_queue.front();
if (front->lockMask & LOCK_REASON_FIFO && !m_surface->m_current.barrierSet)
front->lockMask &= ~LOCK_REASON_FIFO;
if (front->lockMask != LOCK_REASON_NONE) if (front->lockMask != LOCK_REASON_NONE)
return; return;