diff --git a/include/aquamarine/backend/DRM.hpp b/include/aquamarine/backend/DRM.hpp index cc39686..d4beb9d 100644 --- a/include/aquamarine/backend/DRM.hpp +++ b/include/aquamarine/backend/DRM.hpp @@ -233,6 +233,8 @@ namespace Aquamarine { }; struct SDRMPageFlip { + uint64_t queuedSequence = 0; + std::optional> presentTime = std::nullopt; Hyprutils::Memory::CWeakPointer connector; }; diff --git a/src/backend/drm/DRM.cpp b/src/backend/drm/DRM.cpp index 147aca3..0d60fd5 100644 --- a/src/backend/drm/DRM.cpp +++ b/src/backend/drm/DRM.cpp @@ -921,20 +921,24 @@ int Aquamarine::CDRMBackend::drmRenderNodeFD() { return gpu->renderNodeFd; } -static void handlePF(int fd, unsigned seq, unsigned tv_sec, unsigned tv_usec, unsigned crtc_id, void* data) { - auto pageFlip = (SDRMPageFlip*)data; - - if (!pageFlip || !pageFlip->connector) - return; - +static void present(SDRMPageFlip* pageFlip) { pageFlip->connector->isPageFlipPending = false; const auto& BACKEND = pageFlip->connector->backend; - TRACE(BACKEND->log(AQ_LOG_TRACE, std::format("drm: pf event seq {} sec {} usec {} crtc {}", seq, tv_sec, tv_usec, crtc_id))); + if (!pageFlip->presentTime) { + TRACE(BACKEND->log(AQ_LOG_ERROR, "drm: present event missing presentTime")); + return; + } + + auto& presentTime = pageFlip->presentTime.value(); + + TRACE(BACKEND->log( + AQ_LOG_TRACE, + std::format("drm: present event seq {} sec {} usec {} crtc {}", presentTime.first, presentTime.second.tv_sec, presentTime.second.tv_nsec, pageFlip->connector->crtc->id))); if (pageFlip->connector->status != DRM_MODE_CONNECTED || !pageFlip->connector->crtc) { - BACKEND->log(AQ_LOG_DEBUG, "drm: Ignoring a pf event from a disabled crtc / connector"); + BACKEND->log(AQ_LOG_DEBUG, "drm: Ignoring a present event from a disabled crtc / connector"); return; } @@ -942,13 +946,11 @@ static void handlePF(int fd, unsigned seq, unsigned tv_sec, unsigned tv_usec, un uint32_t flags = IOutput::AQ_OUTPUT_PRESENT_VSYNC | IOutput::AQ_OUTPUT_PRESENT_HW_CLOCK | IOutput::AQ_OUTPUT_PRESENT_HW_COMPLETION | IOutput::AQ_OUTPUT_PRESENT_ZEROCOPY; - timespec presented = {.tv_sec = (time_t)tv_sec, .tv_nsec = (long)(tv_usec * 1000)}; - pageFlip->connector->output->events.present.emit(IOutput::SPresentEvent{ .presented = BACKEND->sessionActive(), - .when = &presented, - .seq = seq, - .refresh = (int)(pageFlip->connector->refresh ? (1000000000000LL / pageFlip->connector->refresh) : 0), + .when = &presentTime.second, + .seq = sc(presentTime.first), + .refresh = sc(pageFlip->connector->refresh ? (1000000000000LL / pageFlip->connector->refresh) : 0), .flags = flags, }); @@ -956,10 +958,41 @@ static void handlePF(int fd, unsigned seq, unsigned tv_sec, unsigned tv_usec, un pageFlip->connector->output->events.frame.emit(); } +static void handlePF(int fd, unsigned seq, unsigned tv_sec, unsigned tv_usec, unsigned crtc_id, void* data) { + auto pageFlip = rc(data); + + if (!pageFlip || !pageFlip->connector) + return; + + if (!pageFlip->presentTime || pageFlip->presentTime->first != seq) { + TRACE(pageFlip->connector->backend->log(AQ_LOG_TRACE, std::format("drm: handlePF event seq {} didnt have a timestamp from handleCRTCSequence", seq))); + pageFlip->presentTime = {seq, {.tv_sec = (time_t)tv_sec, .tv_nsec = (long)(tv_usec * 1000)}}; + } + + auto& presentTime = pageFlip->presentTime.value(); + TRACE(pageFlip->connector->backend->log( + AQ_LOG_TRACE, + std::format("drm: handlePF event seq {} sec {} usec {} crtc {}", presentTime.first, presentTime.second.tv_sec, presentTime.second.tv_nsec, pageFlip->connector->crtc->id))); + + present(pageFlip); +} + +static void handleCRTCSequence(int fd, uint64_t seq, uint64_t ns, uint64_t data) { + auto* pageFlip = rc(data); + + if (!pageFlip || !pageFlip->connector || pageFlip->queuedSequence != seq) + return; + + TRACE(pageFlip->connector->backend->log(AQ_LOG_TRACE, std::format("drm: handleCRTCSequence event seq {} ns {}", seq, ns))); + + pageFlip->presentTime = {seq, {.tv_sec = sc(ns / 1'000'000'000), .tv_nsec = sc(ns % 1'000'000'000)}}; +} + bool Aquamarine::CDRMBackend::dispatchEvents() { drmEventContext event = { - .version = 3, + .version = 4, .page_flip_handler2 = ::handlePF, + .sequence_handler = ::handleCRTCSequence, }; if (drmHandleEvent(gpu->fd, &event) != 0) diff --git a/src/backend/drm/impl/Atomic.cpp b/src/backend/drm/impl/Atomic.cpp index 2eb0f7b..496459c 100644 --- a/src/backend/drm/impl/Atomic.cpp +++ b/src/backend/drm/impl/Atomic.cpp @@ -258,6 +258,18 @@ bool Aquamarine::CDRMAtomicRequest::commit(uint32_t flagssss) { return false; } + if (conn && flagssss & DRM_MODE_PAGE_FLIP_EVENT) { + uint64_t prevSeq, prevNs; + if (drmCrtcGetSequence(backend->gpu->fd, conn->crtc->id, &prevSeq, &prevNs) == 0) { + if (drmCrtcQueueSequence(backend->gpu->fd, conn->crtc->id, DRM_CRTC_SEQUENCE_NEXT_ON_MISS, prevSeq + 1, &conn->pendingPageFlip.queuedSequence, + rc(&conn->pendingPageFlip)) != 0) { + backend->log(AQ_LOG_TRACE, "atomic drm request: failed to queue crtc sequence"); + conn->pendingPageFlip.queuedSequence = 0; // fallback to page_flip_handler2 + } + } else + backend->log(AQ_LOG_TRACE, "atomic drm request: failed to get crtc sequence"); + } + if (auto ret = drmModeAtomicCommit(backend->gpu->fd, req, flagssss, conn ? &conn->pendingPageFlip : nullptr); ret) { backend->log((flagssss & DRM_MODE_ATOMIC_TEST_ONLY) ? AQ_LOG_DEBUG : AQ_LOG_ERROR, std::format("atomic drm request: failed to commit: {}, flags: {}", strerror(ret == -1 ? errno : -ret), flagsToStr(flagssss)));