atomic: use sequence_handler and drmEventcontext version 4

use CRTC Sequence handler if we can, poll the current sequence
drmCrtcGetSequence and if it succeeds, try queue the next one if we can
or fallback to the old pageflip handler.

by using crtc sequences we are allowed a much higher resolution
timestamp and get much more correct timestamps for our presentation
events that is not rounded away.

will reduce jitters and drifting in timestamps and get us lower latencies.
This commit is contained in:
Tom Englund 2026-02-04 19:07:37 +01:00
parent def5e74c97
commit f6fddf9954
3 changed files with 61 additions and 14 deletions

View file

@ -233,6 +233,8 @@ namespace Aquamarine {
};
struct SDRMPageFlip {
uint64_t queuedSequence = 0;
std::optional<std::pair<uint64_t, timespec>> presentTime = std::nullopt;
Hyprutils::Memory::CWeakPointer<SDRMConnector> connector;
};

View file

@ -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<unsigned int>(presentTime.first),
.refresh = sc<int>(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<SDRMPageFlip*>(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<SDRMPageFlip*>(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<time_t>(ns / 1'000'000'000), .tv_nsec = sc<long>(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)

View file

@ -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<uint64_t>(&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)));