diff --git a/include/aquamarine/backend/Backend.hpp b/include/aquamarine/backend/Backend.hpp index 7891595..bf1480d 100644 --- a/include/aquamarine/backend/Backend.hpp +++ b/include/aquamarine/backend/Backend.hpp @@ -104,6 +104,12 @@ namespace Aquamarine { /* get a vector of the backend implementations available */ const std::vector>& getImplementations(); + /* push an idle event to the queue */ + void addIdleEvent(Hyprutils::Memory::CSharedPointer> fn); + + /* remove an idle event from the queue */ + void removeIdleEvent(Hyprutils::Memory::CSharedPointer> pfn); + struct { Hyprutils::Signal::CSignal newOutput; Hyprutils::Signal::CSignal newPointer; @@ -130,6 +136,13 @@ namespace Aquamarine { Hyprutils::Memory::CWeakPointer self; std::vector> sessionFDs; + struct { + int fd = -1; + std::vector>> pending; + } idle; + + void dispatchIdle(); + // struct { std::condition_variable loopSignal; diff --git a/include/aquamarine/backend/DRM.hpp b/include/aquamarine/backend/DRM.hpp index 795944d..e7bbad0 100644 --- a/include/aquamarine/backend/DRM.hpp +++ b/include/aquamarine/backend/DRM.hpp @@ -45,7 +45,8 @@ namespace Aquamarine { public: ~CDRMFB(); - static Hyprutils::Memory::CSharedPointer create(Hyprutils::Memory::CSharedPointer buffer_, Hyprutils::Memory::CWeakPointer backend_, bool* isNew = nullptr); + static Hyprutils::Memory::CSharedPointer create(Hyprutils::Memory::CSharedPointer buffer_, Hyprutils::Memory::CWeakPointer backend_, + bool* isNew = nullptr); void closeHandles(); // drops the buffer from KMS @@ -170,10 +171,13 @@ namespace Aquamarine { private: CDRMOutput(const std::string& name_, Hyprutils::Memory::CWeakPointer backend_, Hyprutils::Memory::CSharedPointer connector_); - bool commitState(bool onlyTest = false); + bool commitState(bool onlyTest = false); - Hyprutils::Memory::CWeakPointer backend; - Hyprutils::Memory::CSharedPointer connector; + Hyprutils::Memory::CWeakPointer backend; + Hyprutils::Memory::CSharedPointer connector; + Hyprutils::Memory::CSharedPointer> frameIdle; + + bool lastCommitNoBuffer = true; friend struct SDRMConnector; }; @@ -194,6 +198,7 @@ namespace Aquamarine { uint32_t gammaLut = 0; uint32_t fbDamage = 0; uint32_t modeBlob = 0; + bool blobbed = false; } atomic; void calculateMode(Hyprutils::Memory::CSharedPointer connector); diff --git a/src/backend/Backend.cpp b/src/backend/Backend.cpp index 26272ed..bff068a 100644 --- a/src/backend/Backend.cpp +++ b/src/backend/Backend.cpp @@ -5,11 +5,29 @@ #include #include #include +#include +#include +#include using namespace Hyprutils::Memory; using namespace Aquamarine; #define SP CSharedPointer +#define TIMESPEC_NSEC_PER_SEC 1000000000L + +static void timespecAddNs(timespec* pTimespec, int64_t delta) { + int delta_ns_low = delta % TIMESPEC_NSEC_PER_SEC; + int delta_s_high = delta / TIMESPEC_NSEC_PER_SEC; + + pTimespec->tv_sec += delta_s_high; + + pTimespec->tv_nsec += (long)delta_ns_low; + if (pTimespec->tv_nsec >= TIMESPEC_NSEC_PER_SEC) { + pTimespec->tv_nsec -= TIMESPEC_NSEC_PER_SEC; + ++pTimespec->tv_sec; + } +} + static const char* backendTypeToName(eBackendType type) { switch (type) { case AQ_BACKEND_DRM: return "drm"; @@ -67,6 +85,9 @@ Hyprutils::Memory::CSharedPointer Aquamarine::CBackend::create(const s } } + // create a timerfd for idle events + backend->idle.fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); + return backend; } @@ -80,7 +101,7 @@ bool Aquamarine::CBackend::start() { bool fallback = false; int started = 0; - auto optionsForType = [this] (eBackendType type) -> SBackendImplementationOptions { + auto optionsForType = [this](eBackendType type) -> SBackendImplementationOptions { for (auto& o : implementationOptions) { if (o.backendType == type) return o; @@ -95,8 +116,7 @@ bool Aquamarine::CBackend::start() { log(AQ_LOG_ERROR, std::format("Requested backend ({}) could not start, enabling fallbacks", backendTypeToName(implementations.at(i)->type()))); fallback = true; if (optionsForType(implementations.at(i)->type()).backendRequestMode == AQ_BACKEND_REQUEST_MANDATORY) { - log(AQ_LOG_CRITICAL, - std::format("Requested backend ({}) could not start and it's mandatory, cannot continue!", backendTypeToName(implementations.at(i)->type()))); + log(AQ_LOG_CRITICAL, std::format("Requested backend ({}) could not start and it's mandatory, cannot continue!", backendTypeToName(implementations.at(i)->type()))); implementations.clear(); return false; } @@ -158,6 +178,8 @@ std::vector> Aquamarine::CBackend::ge result.emplace_back(sfd); } + result.emplace_back(makeShared(idle.fd, [this]() { dispatchIdle(); })); + return result; } @@ -191,6 +213,33 @@ std::vector Aquamarine::CBackend::getPrimaryRenderFormats() { return {}; } -const std::vector>& Aquamarine::CBackend::getImplementations() { +const std::vector>& Aquamarine::CBackend::getImplementations() { return implementations; } + +void Aquamarine::CBackend::addIdleEvent(SP> fn) { + auto r = idle.pending.emplace_back(fn); + + // update timerfd + timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + itimerspec ts = {.it_value = now}; + + if (timerfd_settime(idle.fd, TFD_TIMER_ABSTIME, &ts, nullptr)) + log(AQ_LOG_ERROR, std::format("backend: failed to arm timerfd: {}", strerror(errno))); +} + +void Aquamarine::CBackend::removeIdleEvent(SP> pfn) { + std::erase(idle.pending, pfn); +} + +void Aquamarine::CBackend::dispatchIdle() { + auto cpy = idle.pending; + idle.pending.clear(); + + for (auto& i : cpy) { + if (i && *i) + (*i)(); + } +} diff --git a/src/backend/drm/DRM.cpp b/src/backend/drm/DRM.cpp index 76096c8..d680d1c 100644 --- a/src/backend/drm/DRM.cpp +++ b/src/backend/drm/DRM.cpp @@ -576,19 +576,8 @@ bool Aquamarine::CDRMBackend::dispatchEvents() { .page_flip_handler2 = ::handlePF, }; - // we will call this asynchronously and not only when drm fd polls, so we - // ignore the errors (from e.g. a partial read) - // TODO: is this ok? if (drmHandleEvent(gpu->fd, &event) != 0) - ; // backend->log(AQ_LOG_ERROR, std::format("drm: Failed to handle event on fd {}", gpu->fd)); - - if (!idleCallbacks.empty()) { - for (auto& c : idleCallbacks) { - if (c) - c(); - } - idleCallbacks.clear(); - } + backend->log(AQ_LOG_ERROR, std::format("drm: Failed to handle event on fd {}", gpu->fd)); return true; } @@ -1015,7 +1004,9 @@ void Aquamarine::SDRMConnector::onPresent() { } Aquamarine::CDRMOutput::~CDRMOutput() { - ; + backend->backend->removeIdleEvent(frameIdle); + connector->isPageFlipPending = false; + connector->frameEventScheduled = false; } bool Aquamarine::CDRMOutput::commit() { @@ -1146,7 +1137,7 @@ bool Aquamarine::CDRMOutput::commitState(bool onlyTest) { } data.blocking = BLOCKING; - data.modeset = NEEDS_RECONFIG; + data.modeset = NEEDS_RECONFIG || lastCommitNoBuffer; data.flags = flags; data.test = onlyTest; if (MODE->modeInfo.has_value()) @@ -1156,10 +1147,14 @@ bool Aquamarine::CDRMOutput::commitState(bool onlyTest) { bool ok = connector->commitState(data); - events.commit.emit(); + if (onlyTest) + return ok; + events.commit.emit(); state->onCommit(); + lastCommitNoBuffer = !data.mainFB; + return ok; } @@ -1202,12 +1197,7 @@ void Aquamarine::CDRMOutput::scheduleFrame() { connector->frameEventScheduled = true; - backend->idleCallbacks.emplace_back([this]() { - connector->frameEventScheduled = false; - if (connector->isPageFlipPending) - return; - events.frame.emit(); - }); + backend->backend->addIdleEvent(frameIdle); } Vector2D Aquamarine::CDRMOutput::cursorPlaneSize() { @@ -1217,6 +1207,13 @@ Vector2D Aquamarine::CDRMOutput::cursorPlaneSize() { Aquamarine::CDRMOutput::CDRMOutput(const std::string& name_, Hyprutils::Memory::CWeakPointer backend_, SP connector_) : backend(backend_), connector(connector_) { name = name_; + + frameIdle = makeShared>([this]() { + connector->frameEventScheduled = false; + if (connector->isPageFlipPending) + return; + events.frame.emit(); + }); } SP Aquamarine::CDRMFB::create(SP buffer_, Hyprutils::Memory::CWeakPointer backend_, bool* isNew) { diff --git a/src/backend/drm/impl/Atomic.cpp b/src/backend/drm/impl/Atomic.cpp index 01d52db..ad502c6 100644 --- a/src/backend/drm/impl/Atomic.cpp +++ b/src/backend/drm/impl/Atomic.cpp @@ -47,7 +47,7 @@ void Aquamarine::CDRMAtomicRequest::planeProps(Hyprutils::Memory::CSharedPointer // Disable the plane backend->log(AQ_LOG_TRACE, std::format("atomic planeProps: disabling plane {}", plane->id)); add(plane->id, plane->props.fb_id, 0); - add(plane->id, plane->props.crtc_id, crtc); + add(plane->id, plane->props.crtc_id, 0); add(plane->id, plane->props.crtc_x, (uint64_t)pos.x); add(plane->id, plane->props.crtc_y, (uint64_t)pos.y); return; @@ -73,7 +73,7 @@ void Aquamarine::CDRMAtomicRequest::planeProps(Hyprutils::Memory::CSharedPointer void Aquamarine::CDRMAtomicRequest::addConnector(Hyprutils::Memory::CSharedPointer connector, SDRMConnectorCommitData& data) { const auto& STATE = connector->output->state->state(); - const bool enable = STATE.enabled; + const bool enable = STATE.enabled && data.mainFB; backend->log(AQ_LOG_TRACE, std::format("atomic addConnector blobs: mode_id {}, active {}, crtc_id {}, link_status {}, content_type {}", connector->crtc->props.mode_id, @@ -82,8 +82,8 @@ void Aquamarine::CDRMAtomicRequest::addConnector(Hyprutils::Memory::CSharedPoint add(connector->id, connector->props.crtc_id, enable ? connector->crtc->id : 0); if (data.modeset && enable) { - backend->log(AQ_LOG_TRACE, std::format("atomic: mode blob {}", data.atomic.modeBlob)); add(connector->crtc->id, connector->crtc->props.mode_id, data.atomic.modeBlob); + data.atomic.blobbed = true; } if (data.modeset && enable && connector->props.link_status) @@ -182,7 +182,8 @@ void Aquamarine::CDRMAtomicRequest::rollback(SDRMConnectorCommitData& data) { return; conn->crtc->atomic.ownModeID = true; - rollbackBlob(&conn->crtc->atomic.modeID, data.atomic.modeBlob); + if (data.atomic.blobbed) + rollbackBlob(&conn->crtc->atomic.modeID, data.atomic.modeBlob); // TODO: gamma //rollbackBlob(&conn->crtc->atomic.gammaLut, conn->atomic.gammaLut); destroyBlob(data.atomic.fbDamage); @@ -196,7 +197,8 @@ void Aquamarine::CDRMAtomicRequest::apply(SDRMConnectorCommitData& data) { conn->crtc->atomic.modeID = 0; conn->crtc->atomic.ownModeID = true; - commitBlob(&conn->crtc->atomic.modeID, data.atomic.modeBlob); + if (data.atomic.blobbed) + commitBlob(&conn->crtc->atomic.modeID, data.atomic.modeBlob); // TODO: gamma //commitBlob(&conn->crtc->atomic.gammaLut, conn->atomic.gammaLut); destroyBlob(data.atomic.fbDamage); @@ -262,7 +264,8 @@ bool Aquamarine::CDRMAtomicImpl::commit(Hyprutils::Memory::CSharedPointerisPageFlipPending = true; + if (!data.test && data.mainFB && connector->output->state->state().enabled && (flags & DRM_MODE_PAGE_FLIP_EVENT)) + connector->isPageFlipPending = true; } else request.rollback(data);