summaryrefslogtreecommitdiff
path: root/src/protocols/DRMSyncobj.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/protocols/DRMSyncobj.cpp')
-rw-r--r--src/protocols/DRMSyncobj.cpp223
1 files changed, 160 insertions, 63 deletions
diff --git a/src/protocols/DRMSyncobj.cpp b/src/protocols/DRMSyncobj.cpp
index 38aab305..406b55e0 100644
--- a/src/protocols/DRMSyncobj.cpp
+++ b/src/protocols/DRMSyncobj.cpp
@@ -4,11 +4,82 @@
#include "core/Compositor.hpp"
#include "../helpers/sync/SyncTimeline.hpp"
#include "../Compositor.hpp"
+#include "render/OpenGL.hpp"
#include <fcntl.h>
using namespace Hyprutils::OS;
-CDRMSyncobjSurfaceResource::CDRMSyncobjSurfaceResource(SP<CWpLinuxDrmSyncobjSurfaceV1> resource_, SP<CWLSurfaceResource> surface_) : surface(surface_), resource(resource_) {
+CDRMSyncPointState::CDRMSyncPointState(WP<CDRMSyncobjTimelineResource> resource_, uint64_t point_, bool acquirePoint) :
+ m_resource(resource_), m_point(point_), m_acquirePoint(acquirePoint) {}
+
+const uint64_t& CDRMSyncPointState::point() {
+ return m_point;
+}
+
+WP<CDRMSyncobjTimelineResource> CDRMSyncPointState::resource() {
+ return m_resource;
+}
+
+WP<CSyncTimeline> CDRMSyncPointState::timeline() {
+ if (expired()) {
+ Debug::log(ERR, "CDRMSyncPointState: getting a timeline on a expired point");
+ return {};
+ }
+
+ return m_resource->timeline;
+}
+
+bool CDRMSyncPointState::expired() {
+ return !m_resource || !m_resource->timeline;
+}
+
+UP<CSyncReleaser> CDRMSyncPointState::createSyncRelease() {
+ if (expired()) {
+ Debug::log(ERR, "CDRMSyncPointState: creating a sync releaser on an expired point");
+ return nullptr;
+ }
+
+ if (m_releaseTaken)
+ Debug::log(ERR, "CDRMSyncPointState: creating a sync releaser on an already created SyncRelease");
+
+ m_releaseTaken = true;
+ return makeUnique<CSyncReleaser>(m_resource->timeline, m_point);
+}
+
+bool CDRMSyncPointState::addWaiter(const std::function<void()>& waiter) {
+ if (expired()) {
+ Debug::log(ERR, "CDRMSyncPointState: adding a waiter on an expired point");
+ return false;
+ }
+
+ m_acquireCommitted = true;
+ return m_resource->timeline->addWaiter(waiter, m_point, 0u);
+}
+
+bool CDRMSyncPointState::comitted() {
+ return m_acquireCommitted;
+}
+
+CFileDescriptor CDRMSyncPointState::exportAsFD() {
+ if (expired()) {
+ Debug::log(ERR, "CDRMSyncPointState: exporting a FD on an expired point");
+ return {};
+ }
+
+ return m_resource->timeline->exportAsSyncFileFD(m_point);
+}
+
+void CDRMSyncPointState::signal() {
+ if (expired()) {
+ Debug::log(ERR, "CDRMSyncPointState: signaling on an expired point");
+ return;
+ }
+
+ m_resource->timeline->signal(m_point);
+}
+
+CDRMSyncobjSurfaceResource::CDRMSyncobjSurfaceResource(UP<CWpLinuxDrmSyncobjSurfaceV1>&& resource_, SP<CWLSurfaceResource> surface_) :
+ surface(surface_), resource(std::move(resource_)) {
if UNLIKELY (!good())
return;
@@ -23,9 +94,8 @@ CDRMSyncobjSurfaceResource::CDRMSyncobjSurfaceResource(SP<CWpLinuxDrmSyncobjSurf
return;
}
- auto timeline = CDRMSyncobjTimelineResource::fromResource(timeline_);
- pending.acquireTimeline = timeline;
- pending.acquirePoint = ((uint64_t)hi << 32) | (uint64_t)lo;
+ auto timeline = CDRMSyncobjTimelineResource::fromResource(timeline_);
+ pendingAcquire = {timeline, ((uint64_t)hi << 32) | (uint64_t)lo, true};
});
resource->setSetReleasePoint([this](CWpLinuxDrmSyncobjSurfaceV1* r, wl_resource* timeline_, uint32_t hi, uint32_t lo) {
@@ -34,77 +104,102 @@ CDRMSyncobjSurfaceResource::CDRMSyncobjSurfaceResource(SP<CWpLinuxDrmSyncobjSurf
return;
}
- auto timeline = CDRMSyncobjTimelineResource::fromResource(timeline_);
- pending.releaseTimeline = timeline;
- pending.releasePoint = ((uint64_t)hi << 32) | (uint64_t)lo;
+ auto timeline = CDRMSyncobjTimelineResource::fromResource(timeline_);
+ pendingRelease = {timeline, ((uint64_t)hi << 32) | (uint64_t)lo, false};
});
listeners.surfacePrecommit = surface->events.precommit.registerListener([this](std::any d) {
- if ((pending.acquireTimeline || pending.releaseTimeline) && !surface->pending.texture) {
- resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_BUFFER, "Missing buffer");
- surface->pending.rejected = true;
- return;
+ if (!surface->pending.buffer && surface->pending.newBuffer && !surface->pending.texture) {
+ removeAllWaiters();
+ surface->commitPendingState(surface->pending);
+ return; // null buffer attached.
}
- if (!surface->pending.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");
- surface->pending.rejected = true;
- return;
+ if (!surface->pending.buffer && !surface->pending.newBuffer && surface->current.buffer) {
+ surface->current.bufferDamage.clear();
+ surface->current.damage.clear();
+ surface->commitPendingState(surface->current);
+ return; // no new buffer, but we still have current around and a commit happend, commit current again.
}
- if (!pending.acquireTimeline)
+ if (!surface->pending.buffer && !surface->pending.newBuffer && !surface->current.buffer) {
+ surface->commitPendingState(surface->pending); // no pending buffer, no current buffer. probably first commit
return;
+ }
- if (pending.acquireTimeline && pending.releaseTimeline && pending.acquireTimeline == pending.releaseTimeline) {
- if (pending.acquirePoint >= pending.releasePoint) {
- 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;
- }
+ if (!pendingAcquire.expired()) {
+ surface->pending.buffer->acquire = makeUnique<CDRMSyncPointState>(std::move(pendingAcquire));
+ pendingAcquire = {};
}
- // 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 (!pendingRelease.expired()) {
+ surface->pending.buffer->release = makeUnique<CDRMSyncPointState>(std::move(pendingRelease));
+ pendingRelease = {};
}
- if (materialized.value())
+ if (protocolError())
return;
- surface->lockPendingState();
- pending.acquireTimeline->timeline->addWaiter([this]() { surface->unlockPendingState(); }, pending.acquirePoint, DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE);
+ const auto& state = pendingStates.emplace_back(makeShared<SSurfaceState>(surface->pending));
+ surface->pending.damage.clear();
+ surface->pending.bufferDamage.clear();
+ surface->pending.newBuffer = false;
+ surface->pending.buffer.reset();
+
+ state->buffer->buffer->syncReleaser = state->buffer->release->createSyncRelease();
+ state->buffer->acquire->addWaiter([this, surf = surface, wp = CWeakPointer<SSurfaceState>(*std::prev(pendingStates.end()))] {
+ if (!surf)
+ return;
+
+ surf->commitPendingState(*wp.lock());
+ std::erase(pendingStates, wp);
+ });
});
+}
- 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;
- }
+void CDRMSyncobjSurfaceResource::removeAllWaiters() {
+ for (auto& s : pendingStates) {
+ if (s && s->buffer && s->buffer->acquire && !s->buffer->acquire->expired())
+ s->buffer->acquire->resource()->timeline->removeAllWaiters();
+ }
- if (pending.acquireTimeline) {
- current.acquireTimeline = pending.acquireTimeline;
- current.acquirePoint = pending.acquirePoint;
+ pendingStates.clear();
+}
+
+CDRMSyncobjSurfaceResource::~CDRMSyncobjSurfaceResource() {
+ removeAllWaiters();
+}
+
+bool CDRMSyncobjSurfaceResource::protocolError() {
+ if (!surface->pending.texture) {
+ resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_BUFFER, "Missing buffer");
+ surface->pending.rejected = true;
+ return true;
+ }
+
+ if (!!surface->pending.buffer->acquire != !!surface->pending.buffer->release) {
+ resource->error(surface->pending.buffer->acquire ? 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 true;
+ }
+
+ if (surface->pending.buffer->acquire->timeline() == surface->pending.buffer->release->timeline()) {
+ if (surface->pending.buffer->acquire->point() >= surface->pending.buffer->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 true;
}
+ }
- pending.releaseTimeline.reset();
- pending.acquireTimeline.reset();
- });
+ return false;
}
bool CDRMSyncobjSurfaceResource::good() {
return resource->resource();
}
-CDRMSyncobjTimelineResource::CDRMSyncobjTimelineResource(SP<CWpLinuxDrmSyncobjTimelineV1> resource_, CFileDescriptor&& fd_) : fd(std::move(fd_)), resource(resource_) {
+CDRMSyncobjTimelineResource::CDRMSyncobjTimelineResource(UP<CWpLinuxDrmSyncobjTimelineV1>&& resource_, CFileDescriptor&& fd_) : fd(std::move(fd_)), resource(std::move(resource_)) {
if UNLIKELY (!good())
return;
@@ -121,16 +216,20 @@ CDRMSyncobjTimelineResource::CDRMSyncobjTimelineResource(SP<CWpLinuxDrmSyncobjTi
}
}
-SP<CDRMSyncobjTimelineResource> CDRMSyncobjTimelineResource::fromResource(wl_resource* res) {
- auto data = (CDRMSyncobjTimelineResource*)(((CWpLinuxDrmSyncobjTimelineV1*)wl_resource_get_user_data(res))->data());
- return data ? data->self.lock() : nullptr;
+WP<CDRMSyncobjTimelineResource> CDRMSyncobjTimelineResource::fromResource(wl_resource* res) {
+ for (const auto& r : PROTO::sync->m_vTimelines) {
+ if (r && r->resource && r->resource->resource() == res)
+ return r;
+ }
+
+ return {};
}
bool CDRMSyncobjTimelineResource::good() {
return resource->resource();
}
-CDRMSyncobjManagerResource::CDRMSyncobjManagerResource(SP<CWpLinuxDrmSyncobjManagerV1> resource_) : resource(resource_) {
+CDRMSyncobjManagerResource::CDRMSyncobjManagerResource(UP<CWpLinuxDrmSyncobjManagerV1>&& resource_) : resource(std::move(resource_)) {
if UNLIKELY (!good())
return;
@@ -154,28 +253,28 @@ CDRMSyncobjManagerResource::CDRMSyncobjManagerResource(SP<CWpLinuxDrmSyncobjMana
return;
}
- auto RESOURCE = makeShared<CDRMSyncobjSurfaceResource>(makeShared<CWpLinuxDrmSyncobjSurfaceV1>(resource->client(), resource->version(), id), SURF);
+ const auto& RESOURCE = PROTO::sync->m_vSurfaces.emplace_back(
+ makeUnique<CDRMSyncobjSurfaceResource>(makeUnique<CWpLinuxDrmSyncobjSurfaceV1>(resource->client(), resource->version(), id), SURF));
if UNLIKELY (!RESOURCE->good()) {
resource->noMemory();
+ PROTO::sync->m_vSurfaces.pop_back();
return;
}
- PROTO::sync->m_vSurfaces.emplace_back(RESOURCE);
SURF->syncobj = RESOURCE;
LOGM(LOG, "New linux_syncobj at {:x} for surface {:x}", (uintptr_t)RESOURCE.get(), (uintptr_t)SURF.get());
});
resource->setImportTimeline([this](CWpLinuxDrmSyncobjManagerV1* r, uint32_t id, int32_t fd) {
- auto RESOURCE = makeShared<CDRMSyncobjTimelineResource>(makeShared<CWpLinuxDrmSyncobjTimelineV1>(resource->client(), resource->version(), id), CFileDescriptor{fd});
+ const auto& RESOURCE = PROTO::sync->m_vTimelines.emplace_back(
+ makeUnique<CDRMSyncobjTimelineResource>(makeUnique<CWpLinuxDrmSyncobjTimelineV1>(resource->client(), resource->version(), id), CFileDescriptor{fd}));
if UNLIKELY (!RESOURCE->good()) {
resource->noMemory();
+ PROTO::sync->m_vTimelines.pop_back();
return;
}
- PROTO::sync->m_vTimelines.emplace_back(RESOURCE);
- RESOURCE->self = RESOURCE;
-
LOGM(LOG, "New linux_drm_timeline at {:x}", (uintptr_t)RESOURCE.get());
});
}
@@ -184,12 +283,10 @@ bool CDRMSyncobjManagerResource::good() {
return resource->resource();
}
-CDRMSyncobjProtocol::CDRMSyncobjProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
- drmFD = g_pCompositor->m_iDRMFD;
-}
+CDRMSyncobjProtocol::CDRMSyncobjProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name), drmFD(g_pCompositor->m_iDRMFD) {}
void CDRMSyncobjProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
- const auto RESOURCE = m_vManagers.emplace_back(makeShared<CDRMSyncobjManagerResource>(makeShared<CWpLinuxDrmSyncobjManagerV1>(client, ver, id)));
+ const auto& RESOURCE = m_vManagers.emplace_back(makeUnique<CDRMSyncobjManagerResource>(makeUnique<CWpLinuxDrmSyncobjManagerV1>(client, ver, id)));
if UNLIKELY (!RESOURCE->good()) {
wl_client_post_no_memory(client);