diff --git a/debian/patches/sync.patch b/debian/patches/sync.patch new file mode 100644 index 0000000..f92ad4d --- /dev/null +++ b/debian/patches/sync.patch @@ -0,0 +1,438 @@ +From 7d74ba21c822ca1761a6d3f14555ea3a7e8aba9d Mon Sep 17 00:00:00 2001 +From: Tom Englund +Date: Tue, 18 Feb 2025 15:29:53 +0100 +Subject: [PATCH] syncobj: use eventfd instead of stalling fd checks + +use eventfd and add it to the event loop and when it recieves a signal +release the buffer, this means we dont stall entire compositor when +waiting for materilisation of the fd. and change its related usage, this +also means we need to store release points that can popup in a container +and signal them all on buffer release. + +im not sure why directscanout previously manually tried to signal +releasepoints. remove that and let the buffers releaser handle it. +--- + src/helpers/Monitor.cpp | 16 +----- + src/helpers/sync/SyncReleaser.cpp | 4 -- + src/helpers/sync/SyncReleaser.hpp | 3 - + src/helpers/sync/SyncTimeline.cpp | 17 ++++++ + src/helpers/sync/SyncTimeline.hpp | 1 + + src/protocols/DRMSyncobj.cpp | 78 +++++++++++++------------- + src/protocols/DRMSyncobj.hpp | 16 ++++-- + src/protocols/core/Compositor.cpp | 17 +++--- + src/protocols/core/Compositor.hpp | 4 +- + src/protocols/types/Buffer.hpp | 4 +- + src/render/Renderer.cpp | 5 +- + src/render/pass/SurfacePassElement.cpp | 4 +- + 12 files changed, 87 insertions(+), 82 deletions(-) + +diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp +index 24db3ea4b2d..6aa75ce9f95 100644 +--- a/src/helpers/Monitor.cpp ++++ b/src/helpers/Monitor.cpp +@@ -1315,10 +1315,10 @@ bool CMonitor::attemptDirectScanout() { + auto explicitOptions = g_pHyprRenderer->getExplicitSyncSettings(output); + + // wait for the explicit fence if present, and if kms explicit is allowed +- bool DOEXPLICIT = PSURFACE->syncobj && PSURFACE->syncobj->current.acquireTimeline && PSURFACE->syncobj->current.acquireTimeline->timeline && explicitOptions.explicitKMSEnabled; ++ bool DOEXPLICIT = PSURFACE->syncobj && PSURFACE->syncobj->acquire.resource && PSURFACE->syncobj->acquire.resource->timeline && explicitOptions.explicitKMSEnabled; + CFileDescriptor explicitWaitFD; + if (DOEXPLICIT) { +- explicitWaitFD = PSURFACE->syncobj->current.acquireTimeline->timeline->exportAsSyncFileFD(PSURFACE->syncobj->current.acquirePoint); ++ explicitWaitFD = PSURFACE->syncobj->acquire.resource->timeline->exportAsSyncFileFD(PSURFACE->syncobj->acquire.point); + if (!explicitWaitFD.isValid()) + Debug::log(TRACE, "attemptDirectScanout: failed to acquire an explicit wait fd"); + } +@@ -1352,18 +1352,6 @@ bool CMonitor::attemptDirectScanout() { + lastScanout = PCANDIDATE; + Debug::log(LOG, "Entered a direct scanout to {:x}: \"{}\"", (uintptr_t)PCANDIDATE.get(), PCANDIDATE->m_szTitle); + } +- +- // delay explicit sync feedback until kms release of the buffer +- if (DOEXPLICIT) { +- Debug::log(TRACE, "attemptDirectScanout: Delaying explicit sync release feedback until kms release"); +- PSURFACE->current.buffer->releaser->drop(); +- +- PSURFACE->current.buffer->buffer->hlEvents.backendRelease2 = PSURFACE->current.buffer->buffer->events.backendRelease.registerListener([PSURFACE](std::any d) { +- const bool DOEXPLICIT = PSURFACE->syncobj && PSURFACE->syncobj->current.releaseTimeline && PSURFACE->syncobj->current.releaseTimeline->timeline; +- if (DOEXPLICIT) +- PSURFACE->syncobj->current.releaseTimeline->timeline->signal(PSURFACE->syncobj->current.releasePoint); +- }); +- } + } else { + Debug::log(TRACE, "attemptDirectScanout: failed to scanout surface"); + lastScanout.reset(); +diff --git a/src/helpers/sync/SyncReleaser.cpp b/src/helpers/sync/SyncReleaser.cpp +index 198495ab6da..9fc6422b3cb 100644 +--- a/src/helpers/sync/SyncReleaser.cpp ++++ b/src/helpers/sync/SyncReleaser.cpp +@@ -19,7 +19,3 @@ CSyncReleaser::~CSyncReleaser() { + void CSyncReleaser::addReleaseSync(SP sync_) { + sync = sync_; + } +- +-void CSyncReleaser::drop() { +- timeline.reset(); +-} +\ No newline at end of file +diff --git a/src/helpers/sync/SyncReleaser.hpp b/src/helpers/sync/SyncReleaser.hpp +index e21d2e34bd2..ca571c1631c 100644 +--- a/src/helpers/sync/SyncReleaser.hpp ++++ b/src/helpers/sync/SyncReleaser.hpp +@@ -18,9 +18,6 @@ class CSyncReleaser { + CSyncReleaser(WP timeline_, uint64_t point_); + ~CSyncReleaser(); + +- // drops the releaser, will never signal anymore +- void drop(); +- + // wait for this gpu job to finish before releasing + void addReleaseSync(SP sync); + +diff --git a/src/helpers/sync/SyncTimeline.cpp b/src/helpers/sync/SyncTimeline.cpp +index 46b617bc6d6..c2e8b001b6e 100644 +--- a/src/helpers/sync/SyncTimeline.cpp ++++ b/src/helpers/sync/SyncTimeline.cpp +@@ -33,6 +33,12 @@ SP CSyncTimeline::create(int drmFD_, int drmSyncobjFD) { + } + + CSyncTimeline::~CSyncTimeline() { ++ for (auto& w : waiters) { ++ if (w->source) { ++ wl_event_source_remove(w->source); ++ w->source = nullptr; ++ } ++ } + if (handle == 0) + return; + +@@ -124,6 +130,17 @@ void CSyncTimeline::removeWaiter(SWaiter* w) { + std::erase_if(waiters, [w](const auto& e) { return e.get() == w; }); + } + ++void CSyncTimeline::removeAllWaiters() { ++ for (auto& w : waiters) { ++ if (w->source) { ++ wl_event_source_remove(w->source); ++ w->source = nullptr; ++ } ++ } ++ ++ waiters.clear(); ++} ++ + CFileDescriptor CSyncTimeline::exportAsSyncFileFD(uint64_t src) { + int sync = -1; + +diff --git a/src/helpers/sync/SyncTimeline.hpp b/src/helpers/sync/SyncTimeline.hpp +index ba65e004d0b..bfd70416106 100644 +--- a/src/helpers/sync/SyncTimeline.hpp ++++ b/src/helpers/sync/SyncTimeline.hpp +@@ -34,6 +34,7 @@ class CSyncTimeline { + + bool addWaiter(const std::function& waiter, uint64_t point, uint32_t flags); + void removeWaiter(SWaiter*); ++ void removeAllWaiters(); + Hyprutils::OS::CFileDescriptor exportAsSyncFileFD(uint64_t src); + bool importFromSyncFileFD(uint64_t dst, Hyprutils::OS::CFileDescriptor& fd); + bool transfer(SP from, uint64_t fromPoint, uint64_t toPoint); +diff --git a/src/protocols/DRMSyncobj.cpp b/src/protocols/DRMSyncobj.cpp +index 38aab305af3..425794814c9 100644 +--- a/src/protocols/DRMSyncobj.cpp ++++ b/src/protocols/DRMSyncobj.cpp +@@ -8,6 +8,11 @@ + #include + using namespace Hyprutils::OS; + ++CDRMSyncobjSurfaceResource::TimeLineState::~TimeLineState() { ++ if (resource) ++ resource->timeline->removeAllWaiters(); ++} ++ + CDRMSyncobjSurfaceResource::CDRMSyncobjSurfaceResource(SP resource_, SP surface_) : surface(surface_), resource(resource_) { + if UNLIKELY (!good()) + return; +@@ -23,9 +28,9 @@ CDRMSyncobjSurfaceResource::CDRMSyncobjSurfaceResource(SPsetSetReleasePoint([this](CWpLinuxDrmSyncobjSurfaceV1* r, wl_resource* timeline_, uint32_t hi, uint32_t lo) { +@@ -34,13 +39,14 @@ CDRMSyncobjSurfaceResource::CDRMSyncobjSurfaceResource(SP(release.resource->timeline, release.point)); + }); + + listeners.surfacePrecommit = surface->events.precommit.registerListener([this](std::any d) { +- if ((pending.acquireTimeline || pending.releaseTimeline) && !surface->pending.texture) { ++ if ((acquire.resource || release.resource) && !surface->pending.texture) { + resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_BUFFER, "Missing buffer"); + surface->pending.rejected = true; + return; +@@ -49,57 +55,49 @@ CDRMSyncobjSurfaceResource::CDRMSyncobjSurfaceResource(SPpending.newBuffer) + return; // this commit does not change the state here + +- if (!!pending.acquireTimeline != !!pending.releaseTimeline) { +- resource->error(pending.acquireTimeline ? WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_RELEASE_POINT : WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_ACQUIRE_POINT, +- "Missing timeline"); ++ if (!!acquire.resource != !!release.resource) { ++ resource->error(acquire.resource ? WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_RELEASE_POINT : WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_ACQUIRE_POINT, "Missing timeline"); + surface->pending.rejected = true; + return; + } + +- if (!pending.acquireTimeline) +- return; +- +- if (pending.acquireTimeline && pending.releaseTimeline && pending.acquireTimeline == pending.releaseTimeline) { +- if (pending.acquirePoint >= pending.releasePoint) { ++ if (acquire.resource && release.resource && acquire.resource == release.resource) { ++ if (acquire.point >= release.point) { + resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_CONFLICTING_POINTS, "Acquire and release points are on the same timeline, and acquire >= release"); + surface->pending.rejected = true; + return; + } + } + +- // wait for the acquire timeline to materialize +- auto materialized = pending.acquireTimeline->timeline->check(pending.acquirePoint, DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE); +- if (!materialized.has_value()) { +- LOGM(ERR, "Failed to check the acquire timeline"); +- resource->noMemory(); ++ if (!acquire.resource) + return; +- } + +- if (materialized.value()) +- return; +- +- surface->lockPendingState(); +- pending.acquireTimeline->timeline->addWaiter([this]() { surface->unlockPendingState(); }, pending.acquirePoint, DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE); +- }); +- +- listeners.surfaceCommit = surface->events.roleCommit.registerListener([this](std::any d) { +- // apply timelines if new ones have been attached, otherwise don't touch +- // the current ones +- if (pending.releaseTimeline) { +- current.releaseTimeline = pending.releaseTimeline; +- current.releasePoint = pending.releasePoint; ++ if (acquireWaiting) { ++ acquire.resource->timeline->removeAllWaiters(); ++ surface->unlockPendingState(); // unref for the new potential ref. + } + +- if (pending.acquireTimeline) { +- current.acquireTimeline = pending.acquireTimeline; +- current.acquirePoint = pending.acquirePoint; ++ if (acquire.resource->timeline->addWaiter( ++ [this]() { ++ if (surface.expired()) ++ return; ++ ++ surface->unlockPendingState(); ++ surface->commitPendingState(); ++ acquireWaiting = false; ++ }, ++ acquire.point, 0u)) { ++ surface->lockPendingState(); ++ acquireWaiting = true; + } +- +- pending.releaseTimeline.reset(); +- pending.acquireTimeline.reset(); + }); + } + ++CDRMSyncobjSurfaceResource::~CDRMSyncobjSurfaceResource() { ++ if (acquire.resource) ++ acquire.resource->timeline->removeAllWaiters(); ++} ++ + bool CDRMSyncobjSurfaceResource::good() { + return resource->resource(); + } +diff --git a/src/protocols/DRMSyncobj.hpp b/src/protocols/DRMSyncobj.hpp +index 9895dff17e3..6512ed419d8 100644 +--- a/src/protocols/DRMSyncobj.hpp ++++ b/src/protocols/DRMSyncobj.hpp +@@ -2,6 +2,7 @@ + + #include + #include "WaylandProtocol.hpp" ++#include "helpers/sync/SyncReleaser.hpp" + #include "linux-drm-syncobj-v1.hpp" + #include "../helpers/signal/Signal.hpp" + #include +@@ -13,21 +14,26 @@ class CSyncTimeline; + class CDRMSyncobjSurfaceResource { + public: + CDRMSyncobjSurfaceResource(SP resource_, SP surface_); ++ ~CDRMSyncobjSurfaceResource(); + + bool good(); + + WP surface; +- struct { +- WP acquireTimeline, releaseTimeline; +- uint64_t acquirePoint = 0, releasePoint = 0; +- } current, pending; ++ struct TimeLineState { ++ WP resource; ++ uint64_t point = 0; ++ ++ ~TimeLineState(); ++ } acquire, release; ++ ++ std::vector> releasePoints; + + private: + SP resource; ++ bool acquireWaiting = false; + + struct { + CHyprSignalListener surfacePrecommit; +- CHyprSignalListener surfaceCommit; + } listeners; + }; + +diff --git a/src/protocols/core/Compositor.cpp b/src/protocols/core/Compositor.cpp +index 92c3c425d7c..0674743ea9e 100644 +--- a/src/protocols/core/Compositor.cpp ++++ b/src/protocols/core/Compositor.cpp +@@ -113,8 +113,7 @@ CWLSurfaceResource::CWLSurfaceResource(SP resource_) : resource(reso + return; + } + +- if (stateLocks <= 0) +- commitPendingState(); ++ commitPendingState(); + }); + + resource->setDamage([this](CWlSurface* r, int32_t x, int32_t y, int32_t w, int32_t h) { pending.damage.add(CBox{x, y, w, h}); }); +@@ -412,16 +411,17 @@ CRegion CWLSurfaceResource::accumulateCurrentBufferDamage() { + } + + void CWLSurfaceResource::lockPendingState() { +- stateLocks++; ++ stateLocked = true; + } + + void CWLSurfaceResource::unlockPendingState() { +- stateLocks--; +- if (stateLocks <= 0) +- commitPendingState(); ++ stateLocked = false; + } + + void CWLSurfaceResource::commitPendingState() { ++ if (stateLocked && syncobj) ++ return; ++ + static auto PDROP = CConfigValue("render:allow_early_buffer_release"); + auto const previousBuffer = current.buffer; + current = pending; +@@ -433,8 +433,9 @@ void CWLSurfaceResource::commitPendingState() { + + events.roleCommit.emit(); + +- if (syncobj && syncobj->current.releaseTimeline && syncobj->current.releaseTimeline->timeline && current.buffer && current.buffer->buffer) +- current.buffer->releaser = makeShared(syncobj->current.releaseTimeline->timeline, syncobj->current.releasePoint); ++ if (syncobj && syncobj->release.resource && syncobj->release.resource->timeline && current.buffer && current.buffer->buffer) { ++ current.buffer->releaser = std::move(syncobj->releasePoints); ++ } + + if (current.texture) + current.texture->m_eTransform = wlTransformToHyprutils(current.transform); +diff --git a/src/protocols/core/Compositor.hpp b/src/protocols/core/Compositor.hpp +index aaaf9b1a9cb..fdc0c5001d3 100644 +--- a/src/protocols/core/Compositor.hpp ++++ b/src/protocols/core/Compositor.hpp +@@ -131,6 +131,7 @@ class CWLSurfaceResource { + void presentFeedback(timespec* when, PHLMONITOR pMonitor, bool discarded = false); + void lockPendingState(); + void unlockPendingState(); ++ void commitPendingState(); + + // returns a pair: found surface (null if not found) and surface local coords. + // localCoords param is relative to 0,0 of this surface +@@ -144,13 +145,12 @@ class CWLSurfaceResource { + // this stupid-ass hack is used + WP lastBuffer; + +- int stateLocks = 0; ++ bool stateLocked = false; + + void destroy(); + void releaseBuffers(bool onlyCurrent = true); + void dropPendingBuffer(); + void dropCurrentBuffer(); +- void commitPendingState(); + void bfHelper(std::vector> const& nodes, std::function, const Vector2D&, void*)> fn, void* data); + void updateCursorShm(CRegion damage = CBox{0, 0, INT16_MAX, INT16_MAX}); + +diff --git a/src/protocols/types/Buffer.hpp b/src/protocols/types/Buffer.hpp +index 7d84dce7390..03f4fe97d0e 100644 +--- a/src/protocols/types/Buffer.hpp ++++ b/src/protocols/types/Buffer.hpp +@@ -43,8 +43,8 @@ class CHLBufferReference { + CHLBufferReference(SP buffer, SP surface); + ~CHLBufferReference(); + +- WP buffer; +- SP releaser; ++ WP buffer; ++ std::vector> releaser; + + private: + WP surface; +diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp +index b61f1c3eb5b..41a8577e471 100644 +--- a/src/render/Renderer.cpp ++++ b/src/render/Renderer.cpp +@@ -1568,10 +1568,11 @@ bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) { + Debug::log(TRACE, "Explicit: can't add sync, EGLSync failed"); + else { + for (auto const& e : explicitPresented) { +- if (!e->current.buffer || !e->current.buffer->releaser) ++ if (!e->current.buffer || e->current.buffer->releaser.empty()) + continue; + +- e->current.buffer->releaser->addReleaseSync(sync); ++ for (auto& r : e->current.buffer->releaser) ++ r->addReleaseSync(sync); + } + } + +diff --git a/src/render/pass/SurfacePassElement.cpp b/src/render/pass/SurfacePassElement.cpp +index a5d3d7c001a..e6e03b99773 100644 +--- a/src/render/pass/SurfacePassElement.cpp ++++ b/src/render/pass/SurfacePassElement.cpp +@@ -50,8 +50,8 @@ void CSurfacePassElement::draw(const CRegion& damage) { + return; + + // explicit sync: wait for the timeline, if any +- if (data.surface->syncobj && data.surface->syncobj->current.acquireTimeline) { +- if (!g_pHyprOpenGL->waitForTimelinePoint(data.surface->syncobj->current.acquireTimeline->timeline, data.surface->syncobj->current.acquirePoint)) { ++ if (data.surface->syncobj && data.surface->syncobj->acquire.resource) { ++ if (!g_pHyprOpenGL->waitForTimelinePoint(data.surface->syncobj->acquire.resource->timeline, data.surface->syncobj->acquire.point)) { + Debug::log(ERR, "Renderer: failed to wait for explicit timeline"); + return; + } + + \ No newline at end of file