From 55ac96271ff950d4d3e9d37c3e0eed30a51c25ad Mon Sep 17 00:00:00 2001 From: Vaxry Date: Thu, 27 Jun 2024 21:48:01 +0200 Subject: [PATCH] DRM: preliminary atomic support doesn't fucking work yet --- include/aquamarine/backend/DRM.hpp | 15 ++ include/aquamarine/backend/drm/Atomic.hpp | 45 ++++ src/allocator/GBM.cpp | 2 +- src/backend/drm/DRM.cpp | 17 +- src/backend/drm/impl/Atomic.cpp | 283 ++++++++++++++++++++++ src/include/Shared.hpp | 4 + src/utils/Shared.cpp | 7 + 7 files changed, 370 insertions(+), 3 deletions(-) create mode 100644 include/aquamarine/backend/drm/Atomic.hpp create mode 100644 src/backend/drm/impl/Atomic.cpp create mode 100644 src/utils/Shared.cpp diff --git a/include/aquamarine/backend/DRM.hpp b/include/aquamarine/backend/DRM.hpp index 0eff697..a396e37 100644 --- a/include/aquamarine/backend/DRM.hpp +++ b/include/aquamarine/backend/DRM.hpp @@ -118,6 +118,12 @@ namespace Aquamarine { int gammaSize = 0; } legacy; + struct { + bool ownModeID = false; + uint32_t modeID = 0; + uint32_t gammaLut = 0; + } atomic; + Hyprutils::Memory::CSharedPointer primary; Hyprutils::Memory::CSharedPointer cursor; Hyprutils::Memory::CWeakPointer backend; @@ -219,6 +225,13 @@ namespace Aquamarine { drmModeModeInfo fallbackModeInfo; + struct { + uint32_t modeID = 0; + uint32_t gammaLut = 0; + uint32_t fbDamage = 0; + bool vrrEnabled = false; + } atomic; + union UDRMConnectorProps { struct { uint32_t edid; @@ -315,5 +328,7 @@ namespace Aquamarine { friend class CDRMOutput; friend struct SDRMPageFlip; friend class CDRMLegacyImpl; + friend class CDRMAtomicImpl; + friend class CDRMAtomicRequest; }; }; diff --git a/include/aquamarine/backend/drm/Atomic.hpp b/include/aquamarine/backend/drm/Atomic.hpp new file mode 100644 index 0000000..0294de8 --- /dev/null +++ b/include/aquamarine/backend/drm/Atomic.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include "../DRM.hpp" + +namespace Aquamarine { + class CDRMAtomicImpl : public IDRMImplementation { + public: + CDRMAtomicImpl(Hyprutils::Memory::CSharedPointer backend_); + virtual bool commit(Hyprutils::Memory::CSharedPointer connector, const SDRMConnectorCommitData& data); + virtual bool reset(Hyprutils::Memory::CSharedPointer connector); + virtual bool moveCursor(Hyprutils::Memory::CSharedPointer connector); + + private: + bool prepareConnector(Hyprutils::Memory::CSharedPointer connector, const SDRMConnectorCommitData& data); + + Hyprutils::Memory::CWeakPointer backend; + + friend class CDRMAtomicRequest; + }; + + class CDRMAtomicRequest { + public: + CDRMAtomicRequest(Hyprutils::Memory::CWeakPointer backend); + ~CDRMAtomicRequest(); + + void addConnector(Hyprutils::Memory::CSharedPointer connector, const SDRMConnectorCommitData& data); + bool commit(uint32_t flagssss); + void add(uint32_t id, uint32_t prop, uint64_t val); + void planeProps(Hyprutils::Memory::CSharedPointer plane, Hyprutils::Memory::CSharedPointer fb, uint32_t crtc, Hyprutils::Math::Vector2D pos); + + void rollback(); + void apply(); + + bool failed = false; + + private: + void destroyBlob(uint32_t id); + void commitBlob(uint32_t* current, uint32_t next); + void rollbackBlob(uint32_t* current, uint32_t next); + + Hyprutils::Memory::CWeakPointer backend; + drmModeAtomicReq* req = nullptr; + Hyprutils::Memory::CSharedPointer conn; + }; +}; diff --git a/src/allocator/GBM.cpp b/src/allocator/GBM.cpp index 63a5183..a1e6e25 100644 --- a/src/allocator/GBM.cpp +++ b/src/allocator/GBM.cpp @@ -69,7 +69,7 @@ Aquamarine::CGBMBuffer::CGBMBuffer(const SAllocatorBufferParams& params, Hypruti if (attrs.format == DRM_FORMAT_INVALID) { attrs.format = guessFormatFrom(FORMATS, CURSOR).drmFormat; if (attrs.format != DRM_FORMAT_INVALID) - allocator->backend->log(AQ_LOG_WARNING, std::format("GBM: Automatically selected format {} for new GBM buffer", fourccToName(attrs.format))); + allocator->backend->log(AQ_LOG_DEBUG, std::format("GBM: Automatically selected format {} for new GBM buffer", fourccToName(attrs.format))); } if (attrs.format == DRM_FORMAT_INVALID) { diff --git a/src/backend/drm/DRM.cpp b/src/backend/drm/DRM.cpp index d5855fc..e058a11 100644 --- a/src/backend/drm/DRM.cpp +++ b/src/backend/drm/DRM.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -16,6 +17,7 @@ extern "C" { #include "Props.hpp" #include "FormatUtils.hpp" +#include "Shared.hpp" using namespace Aquamarine; using namespace Hyprutils::Memory; @@ -287,11 +289,22 @@ bool Aquamarine::CDRMBackend::checkFeatures() { drmProps.supportsAsyncCommit = drmGetCap(gpu->fd, DRM_CAP_ASYNC_PAGE_FLIP, &cap) == 0 && cap == 1; drmProps.supportsAddFb2Modifiers = drmGetCap(gpu->fd, DRM_CAP_ADDFB2_MODIFIERS, &cap) == 0 && cap == 1; + // FIXME: move to NO_ATOMIC when this piece of shit works + if (!envEnabled("AQ_ATOMIC")) { + backend->log(AQ_LOG_WARNING, "drm: AQ_ATOMIC not enabled, using the legacy drm iface"); + impl = makeShared(self.lock()); + } else if (drmSetClientCap(gpu->fd, DRM_CLIENT_CAP_ATOMIC, 1)) { + backend->log(AQ_LOG_WARNING, "drm: failed to set DRM_CLIENT_CAP_ATOMIC, falling back to legacy"); + impl = makeShared(self.lock()); + } else { + backend->log(AQ_LOG_DEBUG, "drm: Atomic supported, using atomic for modesetting"); + impl = makeShared(self.lock()); + drmProps.supportsAsyncCommit = drmGetCap(gpu->fd, DRM_CAP_ATOMIC_ASYNC_PAGE_FLIP, &cap) == 0 && cap == 1; + } + backend->log(AQ_LOG_DEBUG, std::format("drm: drmProps.supportsAsyncCommit: {}", drmProps.supportsAsyncCommit)); backend->log(AQ_LOG_DEBUG, std::format("drm: drmProps.supportsAddFb2Modifiers: {}", drmProps.supportsAddFb2Modifiers)); - impl = makeShared(self.lock()); - // TODO: allow no-modifiers? return true; diff --git a/src/backend/drm/impl/Atomic.cpp b/src/backend/drm/impl/Atomic.cpp new file mode 100644 index 0000000..a50075c --- /dev/null +++ b/src/backend/drm/impl/Atomic.cpp @@ -0,0 +1,283 @@ +#include +#include +#include +#include +#include + +using namespace Aquamarine; +using namespace Hyprutils::Memory; +using namespace Hyprutils::Math; +#define SP CSharedPointer + +Aquamarine::CDRMAtomicRequest::CDRMAtomicRequest(Hyprutils::Memory::CWeakPointer backend_) : backend(backend_) { + req = drmModeAtomicAlloc(); + if (!req) + failed = true; +} + +Aquamarine::CDRMAtomicRequest::~CDRMAtomicRequest() { + if (req) + drmModeAtomicFree(req); +} + +void Aquamarine::CDRMAtomicRequest::add(uint32_t id, uint32_t prop, uint64_t val) { + if (failed) + return; + + if (id == 0 || prop == 0) { + backend->log(AQ_LOG_ERROR, "atomic drm request: failed to add prop: id / prop == 0"); + return; + } + + if (drmModeAtomicAddProperty(req, id, prop, val) < 0) { + backend->log(AQ_LOG_ERROR, "atomic drm request: failed to add prop"); + failed = true; + } +} + +void Aquamarine::CDRMAtomicRequest::planeProps(Hyprutils::Memory::CSharedPointer plane, Hyprutils::Memory::CSharedPointer fb, uint32_t crtc, + Hyprutils::Math::Vector2D pos) { + + if (failed) + return; + + if (!fb || !crtc) { + // 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, 0); + return; + } + + backend->log(AQ_LOG_TRACE, + std::format("atomic planeProps: prop blobs: src_x {}, src_y {}, src_w {}, src_h {}, crtc_w {}, crtc_h {}, fb_id {}, crtc_id {}, crtc_x {}, crtc_y {}", + plane->props.src_x, plane->props.src_y, plane->props.src_w, plane->props.src_h, plane->props.crtc_w, plane->props.crtc_h, plane->props.fb_id, + plane->props.crtc_id, plane->props.crtc_x, plane->props.crtc_y)); + + // src_ are 16.16 fixed point (lol) + add(plane->id, plane->props.src_x, 0); + add(plane->id, plane->props.src_y, 0); + add(plane->id, plane->props.src_w, ((uint64_t)fb->buffer->size.x) << 16); + add(plane->id, plane->props.src_h, ((uint64_t)fb->buffer->size.y) << 16); + add(plane->id, plane->props.crtc_w, (uint32_t)fb->buffer->size.x); + add(plane->id, plane->props.crtc_h, (uint32_t)fb->buffer->size.y); + add(plane->id, plane->props.fb_id, fb->id); + add(plane->id, plane->props.crtc_id, crtc); + add(plane->id, plane->props.crtc_x, (uint64_t)pos.x); + add(plane->id, plane->props.crtc_y, (uint64_t)pos.y); +} + +void Aquamarine::CDRMAtomicRequest::addConnector(Hyprutils::Memory::CSharedPointer connector, const SDRMConnectorCommitData& data) { + const auto& STATE = connector->output->state->state(); + 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, + connector->crtc->props.active, connector->props.crtc_id, connector->props.link_status, connector->props.content_type)); + + add(connector->id, connector->props.crtc_id, enable ? connector->crtc->id : 0); + + if (data.modeset && enable && connector->props.link_status) + add(connector->id, connector->props.link_status, DRM_MODE_LINK_STATUS_GOOD); + + // TODO: allow to send aq a content type, maybe? Wayland has a protocol for this. + if (enable && connector->props.content_type) + add(connector->id, connector->props.content_type, DRM_MODE_CONTENT_TYPE_GRAPHICS); + + if (data.modeset && enable && connector->props.max_bpc && connector->maxBpcBounds.at(1)) + add(connector->id, connector->props.max_bpc, 8); // FIXME: this isnt always 8 + + add(connector->crtc->id, connector->crtc->props.mode_id, connector->atomic.modeID); + add(connector->crtc->id, connector->crtc->props.active, enable); + + if (enable) { + // TODO: gamma + if (connector->crtc->props.vrr_enabled) + add(connector->crtc->id, connector->crtc->props.vrr_enabled, (uint64_t)STATE.adaptiveSync); + + planeProps(connector->crtc->primary, data.mainFB, connector->crtc->id, {}); + + if (connector->crtc->primary->props.fb_damage_clips) + add(connector->crtc->primary->id, connector->crtc->primary->props.fb_damage_clips, connector->atomic.fbDamage); + + if (connector->crtc->cursor) { + if (!connector->output->cursorVisible) + planeProps(connector->crtc->cursor, nullptr, 0, {}); + else + planeProps(connector->crtc->cursor, data.cursorFB, connector->crtc->id, connector->output->cursorPos - connector->output->cursorHotspot); + } + + } else { + planeProps(connector->crtc->primary, nullptr, 0, {}); + if (connector->crtc->cursor) + planeProps(connector->crtc->cursor, nullptr, 0, {}); + } + + conn = connector; +} + +bool Aquamarine::CDRMAtomicRequest::commit(uint32_t flagssss) { + static auto flagsToStr = [](uint32_t flags) { + std::string result; + if (flags & DRM_MODE_ATOMIC_ALLOW_MODESET) + result += "ATOMIC_ALLOW_MODESET "; + if (flags & DRM_MODE_ATOMIC_NONBLOCK) + result += "ATOMIC_NONBLOCK "; + if (flags & DRM_MODE_ATOMIC_TEST_ONLY) + result += "ATOMIC_TEST_ONLY "; + if (flags & DRM_MODE_PAGE_FLIP_EVENT) + result += "PAGE_FLIP_EVENT "; + if (flags & DRM_MODE_PAGE_FLIP_ASYNC) + result += "PAGE_FLIP_ASYNC "; + if (flags & (~DRM_MODE_ATOMIC_FLAGS)) + result += " + invalid..."; + return result; + }; + + if (failed) { + backend->log((flagssss & DRM_MODE_ATOMIC_TEST_ONLY) ? AQ_LOG_DEBUG : AQ_LOG_ERROR, std::format("atomic drm request: failed to commit, failed flag set to true")); + return false; + } + + if (auto ret = drmModeAtomicCommit(backend->gpu->fd, req, flagssss, &conn->pendingPageFlip); 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), flagsToStr(flagssss))); + return false; + } + + return true; +} + +void Aquamarine::CDRMAtomicRequest::destroyBlob(uint32_t id) { + if (!id) + return; + + if (drmModeDestroyPropertyBlob(backend->gpu->fd, id)) + backend->log(AQ_LOG_ERROR, "atomic drm request: failed to destroy a blob"); +} + +void Aquamarine::CDRMAtomicRequest::commitBlob(uint32_t* current, uint32_t next) { + if (*current == next) + return; + destroyBlob(*current); + *current = next; +} + +void Aquamarine::CDRMAtomicRequest::rollbackBlob(uint32_t* current, uint32_t next) { + if (*current == next) + return; + destroyBlob(next); +} + +void Aquamarine::CDRMAtomicRequest::rollback() { + conn->crtc->atomic.ownModeID = true; + rollbackBlob(&conn->crtc->atomic.modeID, conn->atomic.modeID); + // TODO: gamma + //rollbackBlob(&conn->crtc->atomic.gammaLut, conn->atomic.gammaLut); + destroyBlob(conn->atomic.fbDamage); +} + +void Aquamarine::CDRMAtomicRequest::apply() { + if (!conn->crtc->atomic.ownModeID) + conn->crtc->atomic.modeID = 0; + + conn->crtc->atomic.ownModeID = true; + commitBlob(&conn->crtc->atomic.modeID, conn->atomic.modeID); + // TODO: gamma + //commitBlob(&conn->crtc->atomic.gammaLut, conn->atomic.gammaLut); + destroyBlob(conn->atomic.fbDamage); +} + +Aquamarine::CDRMAtomicImpl::CDRMAtomicImpl(Hyprutils::Memory::CSharedPointer backend_) : backend(backend_) { + ; +} + +bool Aquamarine::CDRMAtomicImpl::prepareConnector(Hyprutils::Memory::CSharedPointer connector, const SDRMConnectorCommitData& data) { + const auto& STATE = connector->output->state->state(); + const bool enable = STATE.enabled; + + if (data.modeset) { + if (!enable) + connector->atomic.modeID = 0; + else { + if (drmModeCreatePropertyBlob(connector->backend->gpu->fd, (drmModeModeInfo*)&data.modeInfo, sizeof(drmModeModeInfo), &connector->atomic.modeID)) { + connector->backend->backend->log(AQ_LOG_ERROR, "atomic drm: failed to create a modeset blob"); + return false; + } + } + } + + // TODO: gamma + + if (STATE.committed & COutputState::AQ_OUTPUT_STATE_DAMAGE && connector->crtc->primary->props.fb_damage_clips) { + if (STATE.damage.empty()) + connector->atomic.fbDamage = 0; + else { + std::vector rects = STATE.damage.getRects(); + if (drmModeCreatePropertyBlob(connector->backend->gpu->fd, rects.data(), sizeof(pixman_box32_t) * rects.size(), &connector->atomic.fbDamage)) { + connector->backend->backend->log(AQ_LOG_ERROR, "atomic drm: failed to create a damage blob"); + return false; + } + } + } + + return true; +} + +bool Aquamarine::CDRMAtomicImpl::commit(Hyprutils::Memory::CSharedPointer connector, const SDRMConnectorCommitData& data) { + if (!prepareConnector(connector, data)) + return false; + + CDRMAtomicRequest request(backend); + + request.addConnector(connector, data); + + uint32_t flags = data.flags; + if (data.test) + flags |= DRM_MODE_ATOMIC_TEST_ONLY; + if (data.modeset) + flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; + if (!data.blocking && !data.test) + flags |= DRM_MODE_ATOMIC_NONBLOCK; + + const bool ok = request.commit(flags); + + if (ok) + request.apply(); + else + request.rollback(); + + return ok; +} + +bool Aquamarine::CDRMAtomicImpl::reset(Hyprutils::Memory::CSharedPointer connector) { + CDRMAtomicRequest request(backend); + + for (auto& crtc : backend->crtcs) { + request.add(crtc->id, crtc->props.mode_id, 0); + request.add(crtc->id, crtc->props.active, 0); + } + + for (auto& conn : backend->connectors) { + request.add(conn->id, conn->props.crtc_id, 0); + } + + for (auto& plane : backend->planes) { + request.planeProps(plane, nullptr, 0, {}); + } + + return request.commit(DRM_MODE_ATOMIC_ALLOW_MODESET); +} + +bool Aquamarine::CDRMAtomicImpl::moveCursor(Hyprutils::Memory::CSharedPointer connector) { + if (!connector->output->cursorVisible || !connector->output->state->state().enabled || !connector->crtc || !connector->crtc->cursor) + return true; + + CDRMAtomicRequest request(backend); + + request.planeProps(connector->crtc->cursor, connector->crtc->cursor->front, connector->crtc->id, connector->output->cursorPos - connector->output->cursorHotspot); + + return request.commit(DRM_MODE_ATOMIC_NONBLOCK); + + return true; +} \ No newline at end of file diff --git a/src/include/Shared.hpp b/src/include/Shared.hpp index 932da2b..164411c 100644 --- a/src/include/Shared.hpp +++ b/src/include/Shared.hpp @@ -4,6 +4,10 @@ #include #include +namespace Aquamarine { + bool envEnabled(const std::string& env); +}; + #define RASSERT(expr, reason, ...) \ if (!(expr)) { \ std::cout << std::format("\n==========================================================================================\nASSERTION FAILED! \n\n{}\n\nat: line {} in {}", \ diff --git a/src/utils/Shared.cpp b/src/utils/Shared.cpp new file mode 100644 index 0000000..50fa7b3 --- /dev/null +++ b/src/utils/Shared.cpp @@ -0,0 +1,7 @@ +#include "Shared.hpp" +#include + +bool Aquamarine::envEnabled(const std::string& env) { + auto e = getenv(env.c_str()); + return e && e == std::string{"1"}; +} \ No newline at end of file