#include "Fifo.hpp" #include "Compositor.hpp" #include "core/Compositor.hpp" #include "../helpers/Monitor.hpp" #include "../event/EventBus.hpp" CFifoResource::CFifoResource(UP&& resource_, SP surface) : m_resource(std::move(resource_)), m_surface(surface) { if UNLIKELY (!m_resource->resource()) return; m_resource->setData(this); m_resource->setDestroy([this](CWpFifoV1* r) { PROTO::fifo->destroyResource(this); }); m_resource->setOnDestroy([this](CWpFifoV1* r) { PROTO::fifo->destroyResource(this); }); m_resource->setSetBarrier([this](CWpFifoV1* r) { if (!m_surface) { r->error(WP_FIFO_V1_ERROR_SURFACE_DESTROYED, "Surface was gone"); return; } m_surface->m_pending.barrierSet = true; m_surface->m_pending.updated.bits.fifo = true; }); m_resource->setWaitBarrier([this](CWpFifoV1* r) { if (!m_surface) { r->error(WP_FIFO_V1_ERROR_SURFACE_DESTROYED, "Surface was gone"); 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 (!m_surface->m_pending.fifoScheduled) m_surface->m_pending.fifoScheduled = checkMonitors(*PPEND); return; } m_surface->m_pending.surfaceLocked = true; }); m_listeners.surfaceStateCommit = m_surface->m_events.stateCommit.listen([this](auto state) { if (!state || !state->surfaceLocked) return; static const auto PPEND = CConfigValue("debug:fifo_pending_workaround"); //#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 (!state->fifoScheduled) state->fifoScheduled = checkMonitors(*PPEND); if (!state->fifoScheduled) return; // only lock once its mapped. if (m_surface->m_mapped) m_surface->m_stateQueue.lock(state, LOCK_REASON_FIFO); }); } CFifoResource::~CFifoResource() { ; } bool CFifoResource::good() { return m_resource->resource(); } void CFifoResource::presented() { m_surface->m_current.barrierSet = false; 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&& resource_) : m_resource(std::move(resource_)) { if UNLIKELY (!m_resource->resource()) return; m_resource->setDestroy([this](CWpFifoManagerV1* r) { PROTO::fifo->destroyResource(this); }); m_resource->setOnDestroy([this](CWpFifoManagerV1* r) { PROTO::fifo->destroyResource(this); }); m_resource->setGetFifo([](CWpFifoManagerV1* r, uint32_t id, wl_resource* surfResource) { if (!surfResource) { r->error(-1, "No resource for fifo"); return; } auto surf = CWLSurfaceResource::fromResource(surfResource); if (!surf) { r->error(-1, "No surface for fifo"); return; } if (surf->m_fifo) { r->error(WP_FIFO_MANAGER_V1_ERROR_ALREADY_EXISTS, "Surface already has a fifo"); return; } const auto& RESOURCE = PROTO::fifo->m_fifos.emplace_back(makeUnique(makeUnique(r->client(), r->version(), id), surf)); if (!RESOURCE->good()) { r->noMemory(); PROTO::fifo->m_fifos.pop_back(); return; } surf->m_fifo = RESOURCE; LOGM(Log::DEBUG, "New fifo at {:x} for surface {:x}", (uintptr_t)RESOURCE, (uintptr_t)surf.get()); }); } CFifoManagerResource::~CFifoManagerResource() { ; } bool CFifoManagerResource::good() { return m_resource->resource(); } CFifoProtocol::CFifoProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { static auto P = Event::bus()->m_events.monitor.added.listen([this](PHLMONITOR M) { M->m_events.presented.listenStatic([this, m = PHLMONITORREF{M}]() { if (!m || !PROTO::fifo) return; onMonitorPresent(m.lock()); }); }); } void CFifoProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { const auto RESOURCE = m_managers.emplace_back(makeUnique(makeUnique(client, ver, id))).get(); if (!RESOURCE->good()) { wl_client_post_no_memory(client); m_managers.pop_back(); return; } } void CFifoProtocol::destroyResource(CFifoManagerResource* res) { std::erase_if(m_managers, [&](const auto& other) { return other.get() == res; }); } void CFifoProtocol::destroyResource(CFifoResource* res) { std::erase_if(m_fifos, [&](const auto& other) { return other.get() == res; }); } void CFifoProtocol::onMonitorPresent(PHLMONITOR m) { if (m->m_tearingState.activelyTearing) return; // fifo isnt locked on tearing. for (const auto& fifo : m_fifos) { if (!fifo->m_surface) continue; if (!fifo->m_surface->m_mapped) { fifo->presented(); continue; } auto it = std::ranges::find_if(fifo->m_surface->m_enteredOutputs, [m](auto& mon) { return mon == m; }); if (it != fifo->m_surface->m_enteredOutputs.end()) { fifo->presented(); continue; } if (fifo->m_surface->m_hlSurface) { auto box = fifo->m_surface->m_hlSurface->getSurfaceBoxGlobal(); if (box && !box->intersection({m->m_position, m->m_size}).empty()) { fifo->presented(); continue; } } } }