diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp index 24db3ea4..5aa1896c 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"); } @@ -1356,12 +1356,15 @@ bool CMonitor::attemptDirectScanout() { // 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(); + //#TODO is this correct, or should only the last release feedback be delayed? + for (auto& r : PSURFACE->current.buffer->releaser) { + r->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; + const bool DOEXPLICIT = PSURFACE->syncobj && PSURFACE->syncobj->release.resource && PSURFACE->syncobj->release.resource->timeline; if (DOEXPLICIT) - PSURFACE->syncobj->current.releaseTimeline->timeline->signal(PSURFACE->syncobj->current.releasePoint); + PSURFACE->syncobj->release.resource->timeline->signal(PSURFACE->syncobj->release.point); }); } } else { diff --git a/src/helpers/sync/SyncTimeline.cpp b/src/helpers/sync/SyncTimeline.cpp index 46b617bc..0f388479 100644 --- a/src/helpers/sync/SyncTimeline.cpp +++ b/src/helpers/sync/SyncTimeline.cpp @@ -33,6 +33,13 @@ 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; diff --git a/src/protocols/DRMSyncobj.cpp b/src/protocols/DRMSyncobj.cpp index 38aab305..ac31bfe5 100644 --- a/src/protocols/DRMSyncobj.cpp +++ b/src/protocols/DRMSyncobj.cpp @@ -23,9 +23,9 @@ CDRMSyncobjSurfaceResource::CDRMSyncobjSurfaceResource(SPsetSetReleasePoint([this](CWpLinuxDrmSyncobjSurfaceV1* r, wl_resource* timeline_, uint32_t hi, uint32_t lo) { @@ -34,13 +34,14 @@ CDRMSyncobjSurfaceResource::CDRMSyncobjSurfaceResource(SP(release)); }); 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,54 +50,32 @@ 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) + if (!acquire.resource) 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(); - 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 (pending.acquireTimeline) { - current.acquireTimeline = pending.acquireTimeline; - current.acquirePoint = pending.acquirePoint; - } - - pending.releaseTimeline.reset(); - pending.acquireTimeline.reset(); + acquire.resource->timeline->addWaiter( + [this]() { + if (surface.expired()) + return; + + surface->unlockPendingState(); + }, + acquire.point, 0u); }); } diff --git a/src/protocols/DRMSyncobj.hpp b/src/protocols/DRMSyncobj.hpp index 9895dff1..68b2a4e3 100644 --- a/src/protocols/DRMSyncobj.hpp +++ b/src/protocols/DRMSyncobj.hpp @@ -17,10 +17,12 @@ class 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; + } acquire, release; + + std::vector> releasePoints; private: SP resource; diff --git a/src/protocols/core/Compositor.cpp b/src/protocols/core/Compositor.cpp index 92c3c425..e98c25fc 100644 --- a/src/protocols/core/Compositor.cpp +++ b/src/protocols/core/Compositor.cpp @@ -417,6 +417,7 @@ void CWLSurfaceResource::lockPendingState() { void CWLSurfaceResource::unlockPendingState() { stateLocks--; + if (stateLocks <= 0) commitPendingState(); } @@ -433,8 +434,16 @@ 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->acquire.resource && syncobj->acquire.resource->timeline && current.buffer && current.buffer->buffer) { + for (auto& r : syncobj->releasePoints) { + if (r->resource.expired()) + continue; + + current.buffer->releaser.emplace_back(makeShared(r->resource->timeline, r->point)); + } + + syncobj->releasePoints.clear(); + } if (current.texture) current.texture->m_eTransform = wlTransformToHyprutils(current.transform); diff --git a/src/protocols/types/Buffer.hpp b/src/protocols/types/Buffer.hpp index 7d84dce7..6ed5c01f 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 b61f1c3e..bb7b1e1d 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) //#TODO is this correct? should it only be on last point? + r->addReleaseSync(sync); } } diff --git a/src/render/pass/SurfacePassElement.cpp b/src/render/pass/SurfacePassElement.cpp index a5d3d7c0..e6e03b99 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; }