From 41b1b28ce119037953d52b1172dca25e5e7c5e82 Mon Sep 17 00:00:00 2001 From: Vaxry Date: Wed, 26 Jun 2024 19:26:38 +0200 Subject: [PATCH] drm: Implement hardware cursors --- include/aquamarine/allocator/Allocator.hpp | 7 +- include/aquamarine/allocator/GBM.hpp | 7 +- include/aquamarine/allocator/Swapchain.hpp | 21 +++- include/aquamarine/backend/DRM.hpp | 32 +++++- include/aquamarine/backend/Wayland.hpp | 2 +- include/aquamarine/backend/drm/Legacy.hpp | 1 + include/aquamarine/buffer/Buffer.hpp | 2 +- include/aquamarine/misc/Attachment.hpp | 1 + include/aquamarine/output/Output.hpp | 3 +- src/allocator/GBM.cpp | 30 +++-- src/allocator/Swapchain.cpp | 16 ++- src/backend/Wayland.cpp | 4 +- src/backend/drm/DRM.cpp | 124 +++++++++++++++------ src/backend/drm/impl/Legacy.cpp | 53 ++++++++- src/misc/Attachment.cpp | 4 + src/output/Output.cpp | 6 +- 16 files changed, 236 insertions(+), 77 deletions(-) diff --git a/include/aquamarine/allocator/Allocator.hpp b/include/aquamarine/allocator/Allocator.hpp index 09c6b74..7567dff 100644 --- a/include/aquamarine/allocator/Allocator.hpp +++ b/include/aquamarine/allocator/Allocator.hpp @@ -6,16 +6,17 @@ namespace Aquamarine { class CBackend; + class CSwapchain; struct SAllocatorBufferParams { Hyprutils::Math::Vector2D size; uint32_t format = DRM_FORMAT_INVALID; - bool scanout = false; + bool scanout = false, cursor = false; }; class IAllocator { public: - virtual Hyprutils::Memory::CSharedPointer acquire(const SAllocatorBufferParams& params) = 0; - virtual Hyprutils::Memory::CSharedPointer getBackend() = 0; + virtual Hyprutils::Memory::CSharedPointer acquire(const SAllocatorBufferParams& params, Hyprutils::Memory::CSharedPointer swapchain) = 0; + virtual Hyprutils::Memory::CSharedPointer getBackend() = 0; }; }; diff --git a/include/aquamarine/allocator/GBM.hpp b/include/aquamarine/allocator/GBM.hpp index e8df447..40b682b 100644 --- a/include/aquamarine/allocator/GBM.hpp +++ b/include/aquamarine/allocator/GBM.hpp @@ -8,6 +8,7 @@ struct gbm_bo; namespace Aquamarine { class CGBMAllocator; class CBackend; + class CSwapchain; class CGBMBuffer : public IBuffer { public: @@ -21,7 +22,7 @@ namespace Aquamarine { virtual SDMABUFAttrs dmabuf(); private: - CGBMBuffer(const SAllocatorBufferParams& params, Hyprutils::Memory::CWeakPointer allocator_); + CGBMBuffer(const SAllocatorBufferParams& params, Hyprutils::Memory::CWeakPointer allocator_, Hyprutils::Memory::CSharedPointer swapchain); Hyprutils::Memory::CWeakPointer allocator; @@ -36,8 +37,8 @@ namespace Aquamarine { public: static Hyprutils::Memory::CSharedPointer create(int drmfd_, Hyprutils::Memory::CWeakPointer backend_); - virtual Hyprutils::Memory::CSharedPointer acquire(const SAllocatorBufferParams& params); - virtual Hyprutils::Memory::CSharedPointer getBackend(); + virtual Hyprutils::Memory::CSharedPointer acquire(const SAllocatorBufferParams& params, Hyprutils::Memory::CSharedPointer swapchain_); + virtual Hyprutils::Memory::CSharedPointer getBackend(); // Hyprutils::Memory::CWeakPointer self; diff --git a/include/aquamarine/allocator/Swapchain.hpp b/include/aquamarine/allocator/Swapchain.hpp index 885bb3d..85cbe2b 100644 --- a/include/aquamarine/allocator/Swapchain.hpp +++ b/include/aquamarine/allocator/Swapchain.hpp @@ -4,31 +4,40 @@ namespace Aquamarine { + class IBackendImplementation; + struct SSwapchainOptions { size_t length = 0; Hyprutils::Math::Vector2D size; uint32_t format = DRM_FORMAT_INVALID; - bool scanout = false; + bool scanout = false, cursor = false /* requires scanout = true */; }; class CSwapchain { public: - CSwapchain(Hyprutils::Memory::CSharedPointer allocator_); + static Hyprutils::Memory::CSharedPointer create(Hyprutils::Memory::CSharedPointer allocator_, + Hyprutils::Memory::CSharedPointer backendImpl_); - bool reconfigure(const SSwapchainOptions& options_); + bool reconfigure(const SSwapchainOptions& options_); - bool contains(Hyprutils::Memory::CSharedPointer buffer); - Hyprutils::Memory::CSharedPointer next(int* age); - const SSwapchainOptions& currentOptions(); + bool contains(Hyprutils::Memory::CSharedPointer buffer); + Hyprutils::Memory::CSharedPointer next(int* age); + const SSwapchainOptions& currentOptions(); private: + CSwapchain(Hyprutils::Memory::CSharedPointer allocator_, Hyprutils::Memory::CSharedPointer backendImpl_); + bool fullReconfigure(const SSwapchainOptions& options_); bool resize(size_t newSize); // + Hyprutils::Memory::CWeakPointer self; SSwapchainOptions options; Hyprutils::Memory::CSharedPointer allocator; + Hyprutils::Memory::CWeakPointer backendImpl; std::vector> buffers; int lastAcquired = 0; + + friend class CGBMBuffer; }; }; diff --git a/include/aquamarine/backend/DRM.hpp b/include/aquamarine/backend/DRM.hpp index 02dddba..e47e210 100644 --- a/include/aquamarine/backend/DRM.hpp +++ b/include/aquamarine/backend/DRM.hpp @@ -15,6 +15,19 @@ namespace Aquamarine { typedef std::function FIdleCallback; + class CDRMBufferAttachment : public IAttachment { + public: + CDRMBufferAttachment(Hyprutils::Memory::CSharedPointer fb_); + virtual ~CDRMBufferAttachment() { + ; + } + virtual eAttachmentType type() { + return AQ_ATTACHMENT_DRM_BUFFER; + } + + Hyprutils::Memory::CSharedPointer fb; + }; + class CDRMBufferUnimportable : public IAttachment { public: CDRMBufferUnimportable() { @@ -39,16 +52,15 @@ namespace Aquamarine { void drop(); uint32_t id = 0; - Hyprutils::Memory::CSharedPointer buffer; + Hyprutils::Memory::CWeakPointer buffer; Hyprutils::Memory::CWeakPointer backend; + std::array boHandles = {0, 0, 0, 0}; private: CDRMFB(Hyprutils::Memory::CSharedPointer buffer_, Hyprutils::Memory::CWeakPointer backend_); - uint32_t submitBuffer(); + uint32_t submitBuffer(); - bool dropped = false, handlesClosed = false; - - std::array boHandles = {0}; + bool dropped = false, handlesClosed = false; }; struct SDRMLayer { @@ -109,6 +121,7 @@ namespace Aquamarine { Hyprutils::Memory::CSharedPointer primary; Hyprutils::Memory::CSharedPointer cursor; Hyprutils::Memory::CWeakPointer backend; + Hyprutils::Memory::CSharedPointer pendingCursor; union UDRMCRTCProps { struct { @@ -136,9 +149,13 @@ namespace Aquamarine { virtual bool setCursor(Hyprutils::Memory::CSharedPointer buffer, const Hyprutils::Math::Vector2D& hotspot); virtual void moveCursor(const Hyprutils::Math::Vector2D& coord); virtual void scheduleFrame(); - virtual Hyprutils::Math::Vector2D maxCursorSize(); + virtual void setCursorVisible(bool visible); + virtual Hyprutils::Math::Vector2D cursorPlaneSize(); Hyprutils::Memory::CWeakPointer self; + bool cursorVisible = true; + Hyprutils::Math::Vector2D cursorPos; // without hotspot + Hyprutils::Math::Vector2D cursorHotspot; private: CDRMOutput(const std::string& name_, Hyprutils::Memory::CWeakPointer backend_, Hyprutils::Memory::CSharedPointer connector_); @@ -228,6 +245,9 @@ namespace Aquamarine { public: virtual bool commit(Hyprutils::Memory::CSharedPointer connector, const SDRMConnectorCommitData& data) = 0; virtual bool reset(Hyprutils::Memory::CSharedPointer connector) = 0; + + // moving a cursor IIRC is almost instant on most hardware so we don't have to wait for a commit. + virtual bool moveCursor(Hyprutils::Memory::CSharedPointer connector) = 0; }; class CDRMBackend : public IBackendImplementation { diff --git a/include/aquamarine/backend/Wayland.hpp b/include/aquamarine/backend/Wayland.hpp index 6a05802..70eb705 100644 --- a/include/aquamarine/backend/Wayland.hpp +++ b/include/aquamarine/backend/Wayland.hpp @@ -47,7 +47,7 @@ namespace Aquamarine { virtual bool setCursor(Hyprutils::Memory::CSharedPointer buffer, const Hyprutils::Math::Vector2D& hotspot); virtual void moveCursor(const Hyprutils::Math::Vector2D& coord); virtual void scheduleFrame(); - virtual Hyprutils::Math::Vector2D maxCursorSize(); + virtual Hyprutils::Math::Vector2D cursorPlaneSize(); Hyprutils::Memory::CWeakPointer self; diff --git a/include/aquamarine/backend/drm/Legacy.hpp b/include/aquamarine/backend/drm/Legacy.hpp index 1f19a6b..275fb3f 100644 --- a/include/aquamarine/backend/drm/Legacy.hpp +++ b/include/aquamarine/backend/drm/Legacy.hpp @@ -8,6 +8,7 @@ namespace Aquamarine { CDRMLegacyImpl(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: diff --git a/include/aquamarine/buffer/Buffer.hpp b/include/aquamarine/buffer/Buffer.hpp index 77e428c..4fe2309 100644 --- a/include/aquamarine/buffer/Buffer.hpp +++ b/include/aquamarine/buffer/Buffer.hpp @@ -43,7 +43,7 @@ namespace Aquamarine { class IBuffer { public: virtual ~IBuffer() { - ; + attachments.clear(); }; virtual eBufferCapability caps() = 0; diff --git a/include/aquamarine/misc/Attachment.hpp b/include/aquamarine/misc/Attachment.hpp index 969eea2..eb21b2c 100644 --- a/include/aquamarine/misc/Attachment.hpp +++ b/include/aquamarine/misc/Attachment.hpp @@ -25,6 +25,7 @@ namespace Aquamarine { void add(Hyprutils::Memory::CSharedPointer attachment); void remove(Hyprutils::Memory::CSharedPointer attachment); void removeByType(eAttachmentType type); + void clear(); private: std::vector> attachments; diff --git a/include/aquamarine/output/Output.hpp b/include/aquamarine/output/Output.hpp index d2f53be..6d8dab1 100644 --- a/include/aquamarine/output/Output.hpp +++ b/include/aquamarine/output/Output.hpp @@ -100,7 +100,8 @@ namespace Aquamarine { virtual Hyprutils::Memory::CSharedPointer preferredMode(); virtual bool setCursor(Hyprutils::Memory::CSharedPointer buffer, const Hyprutils::Math::Vector2D& hotspot); virtual void moveCursor(const Hyprutils::Math::Vector2D& coord); // includes the hotspot - virtual Hyprutils::Math::Vector2D maxCursorSize(); // -1, -1 means no limit, 0, 0 means error + virtual void setCursorVisible(bool visible); // moving the cursor will make it visible again without this util + virtual Hyprutils::Math::Vector2D cursorPlaneSize(); // -1, -1 means no set size, 0, 0 means error virtual void scheduleFrame(); virtual size_t getGammaSize(); diff --git a/src/allocator/GBM.cpp b/src/allocator/GBM.cpp index 13f7d98..6f285ba 100644 --- a/src/allocator/GBM.cpp +++ b/src/allocator/GBM.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "FormatUtils.hpp" #include #include @@ -8,36 +9,45 @@ using namespace Aquamarine; using namespace Hyprutils::Memory; #define SP CSharedPointer -Aquamarine::CGBMBuffer::CGBMBuffer(const SAllocatorBufferParams& params, Hyprutils::Memory::CWeakPointer allocator_) : allocator(allocator_) { +Aquamarine::CGBMBuffer::CGBMBuffer(const SAllocatorBufferParams& params, Hyprutils::Memory::CWeakPointer allocator_, + Hyprutils::Memory::CSharedPointer swapchain) : + allocator(allocator_) { if (!allocator) return; attrs.size = params.size; attrs.format = params.format; + size = attrs.size; - const auto FORMATS = allocator->backend->getPrimaryRenderFormats(); + const bool CURSOR = params.cursor && params.scanout; + + if (CURSOR) + allocator->backend->log(AQ_LOG_WARNING, "GBM: Allocating a cursor buffer"); + + const auto FORMATS = CURSOR ? swapchain->backendImpl->getCursorFormats() : swapchain->backendImpl->getRenderFormats(); std::vector explicitModifiers; // check if we can use modifiers. If the requested support has any explicit modifier // supported by the primary backend, we can. - allocator->backend->log(AQ_LOG_TRACE, std::format("GBM: Searching for modifiers. Format len: {}", FORMATS.size())); for (auto& f : FORMATS) { if (f.drmFormat != params.format) continue; - allocator->backend->log(AQ_LOG_TRACE, "GBM: Format matched"); - for (auto& m : f.modifiers) { if (m == DRM_FORMAT_MOD_LINEAR || m == DRM_FORMAT_MOD_INVALID) continue; explicitModifiers.push_back(m); - - allocator->backend->log(AQ_LOG_TRACE, "GBM: Modifier matched"); } } + if (explicitModifiers.empty()) { + // fall back to using a linear buffer. + // FIXME: Nvidia cannot render to linear buffers. + explicitModifiers.push_back(DRM_FORMAT_MOD_LINEAR); + } + uint32_t flags = GBM_BO_USE_RENDERING; if (params.scanout) flags |= GBM_BO_USE_SCANOUT; @@ -46,7 +56,7 @@ Aquamarine::CGBMBuffer::CGBMBuffer(const SAllocatorBufferParams& params, Hypruti allocator->backend->log(AQ_LOG_WARNING, "GBM: Using modifier-less allocation"); bo = gbm_bo_create(allocator->gbmDevice, params.size.x, params.size.y, params.format, flags); } else - bo = gbm_bo_create_with_modifiers2(allocator->gbmDevice, params.size.x, params.size.y, params.format, explicitModifiers.data(), explicitModifiers.size(), flags); + bo = gbm_bo_create_with_modifiers(allocator->gbmDevice, params.size.x, params.size.y, params.format, explicitModifiers.data(), explicitModifiers.size()); if (!bo) { allocator->backend->log(AQ_LOG_ERROR, "GBM: Failed to allocate a GBM buffer: bo null"); @@ -147,7 +157,7 @@ Aquamarine::CGBMAllocator::CGBMAllocator(int fd_, Hyprutils::Memory::CWeakPointe free(drmName_); } -SP Aquamarine::CGBMAllocator::acquire(const SAllocatorBufferParams& params) { +SP Aquamarine::CGBMAllocator::acquire(const SAllocatorBufferParams& params, Hyprutils::Memory::CSharedPointer swapchain_) { if (params.size.x < 1 || params.size.y < 1) { backend->log(AQ_LOG_ERROR, std::format("Couldn't allocate a gbm buffer with invalid size {}", params.size)); return nullptr; @@ -158,7 +168,7 @@ SP Aquamarine::CGBMAllocator::acquire(const SAllocatorBufferParams& par return nullptr; } - auto newBuffer = SP(new CGBMBuffer(params, self)); + auto newBuffer = SP(new CGBMBuffer(params, self, swapchain_)); if (!newBuffer->good()) { backend->log(AQ_LOG_ERROR, std::format("Couldn't allocate a gbm buffer with size {} and format {}", params.size, fourccToName(params.format))); diff --git a/src/allocator/Swapchain.cpp b/src/allocator/Swapchain.cpp index fc486ed..21118a9 100644 --- a/src/allocator/Swapchain.cpp +++ b/src/allocator/Swapchain.cpp @@ -7,8 +7,14 @@ using namespace Hyprutils::Memory; using namespace Hyprutils::Math; #define SP CSharedPointer -Aquamarine::CSwapchain::CSwapchain(SP allocator_) : allocator(allocator_) { - if (!allocator) +SP Aquamarine::CSwapchain::create(SP allocator_, SP backendImpl_) { + auto p = SP(new CSwapchain(allocator_, backendImpl_)); + p->self = p; + return p; +} + +Aquamarine::CSwapchain::CSwapchain(SP allocator_, SP backendImpl_) : allocator(allocator_), backendImpl(backendImpl_) { + if (!allocator || !backendImpl) return; } @@ -64,7 +70,8 @@ SP Aquamarine::CSwapchain::next(int* age) { bool Aquamarine::CSwapchain::fullReconfigure(const SSwapchainOptions& options_) { buffers.clear(); for (size_t i = 0; i < options_.length; ++i) { - auto buf = allocator->acquire(SAllocatorBufferParams{.size = options_.size, .format = options_.format}); + auto buf = + allocator->acquire(SAllocatorBufferParams{.size = options_.size, .format = options_.format, .scanout = options_.scanout, .cursor = options_.cursor}, self.lock()); if (!buf) { allocator->getBackend()->log(AQ_LOG_ERROR, "Swapchain: Failed acquiring a buffer"); return false; @@ -85,7 +92,8 @@ bool Aquamarine::CSwapchain::resize(size_t newSize) { } } else { while (buffers.size() < newSize) { - auto buf = allocator->acquire(SAllocatorBufferParams{.size = options.size, .format = options.format}); + auto buf = + allocator->acquire(SAllocatorBufferParams{.size = options.size, .format = options.format, .scanout = options.scanout, .cursor = options.cursor}, self.lock()); if (!buf) { allocator->getBackend()->log(AQ_LOG_ERROR, "Swapchain: Failed acquiring a buffer"); return false; diff --git a/src/backend/Wayland.cpp b/src/backend/Wayland.cpp index 654b7d6..9f5cea2 100644 --- a/src/backend/Wayland.cpp +++ b/src/backend/Wayland.cpp @@ -184,7 +184,7 @@ bool Aquamarine::CWaylandBackend::setCursor(Hyprutils::Memory::CSharedPointerswapchain = makeShared(backend->allocator); + o->swapchain = CSwapchain::create(backend->allocator, self.lock()); if (!o->swapchain) { backend->log(AQ_LOG_ERROR, std::format("Output {} failed: swapchain creation failed", o->name)); continue; @@ -703,7 +703,7 @@ void Aquamarine::CWaylandOutput::onEnter(SP pointer, uint32_t seria pointer->sendSetCursor(serial, cursorState.cursorSurface.get(), cursorState.hotspot.x, cursorState.hotspot.y); } -Hyprutils::Math::Vector2D Aquamarine::CWaylandOutput::maxCursorSize() { +Hyprutils::Math::Vector2D Aquamarine::CWaylandOutput::cursorPlaneSize() { return {-1, -1}; // no limit } diff --git a/src/backend/drm/DRM.cpp b/src/backend/drm/DRM.cpp index f489522..bc69a9d 100644 --- a/src/backend/drm/DRM.cpp +++ b/src/backend/drm/DRM.cpp @@ -525,12 +525,16 @@ 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)); + ; // backend->log(AQ_LOG_ERROR, std::format("drm: Failed to handle event on fd {}", gpu->fd)); if (!idleCallbacks.empty()) { for (auto& c : idleCallbacks) { - c(); + if (c) + c(); } idleCallbacks.clear(); } @@ -557,7 +561,7 @@ void Aquamarine::CDRMBackend::onReady() { backend->log(AQ_LOG_DEBUG, std::format("drm: onReady: connector {} has output name {}", c->id, c->output->name)); // swapchain has to be created here because allocator is absent in connect if not ready - c->output->swapchain = makeShared(backend->allocator); + c->output->swapchain = CSwapchain::create(backend->allocator, self.lock()); c->output->swapchain->reconfigure(SSwapchainOptions{.length = 0, .scanout = true}); // mark the swapchain for scanout c->output->needsFrame = true; @@ -868,7 +872,7 @@ void Aquamarine::SDRMConnector::connect(drmModeConnector* connector) { if (!backend->backend->ready) return; - output->swapchain = makeShared(backend->backend->allocator); + output->swapchain = CSwapchain::create(backend->backend->allocator, backend->self.lock()); backend->backend->events.newOutput.emit(output); output->scheduleFrame(); } @@ -898,7 +902,7 @@ bool Aquamarine::SDRMConnector::commitState(const SDRMConnectorCommitData& data) void Aquamarine::SDRMConnector::applyCommit(const SDRMConnectorCommitData& data) { crtc->primary->back = data.mainFB; - if (crtc->cursor) + if (crtc->cursor && data.cursorFB) crtc->cursor->back = data.cursorFB; pendingCursorFB.reset(); @@ -908,13 +912,17 @@ void Aquamarine::SDRMConnector::applyCommit(const SDRMConnectorCommitData& data) } void Aquamarine::SDRMConnector::rollbackCommit(const SDRMConnectorCommitData& data) { - ; + // cursors are applied regardless. + if (crtc->cursor && data.cursorFB) + crtc->cursor->back = data.cursorFB; + + crtc->pendingCursor.reset(); } void Aquamarine::SDRMConnector::onPresent() { crtc->primary->last = crtc->primary->front; crtc->primary->front = crtc->primary->back; - if (crtc->cursor) { + if (crtc->cursor && crtc->cursor->back /* Don't shift if it hasn't updated */) { crtc->cursor->last = crtc->cursor->front; crtc->cursor->front = crtc->cursor->back; } @@ -932,6 +940,12 @@ bool Aquamarine::CDRMOutput::test() { return commitState(true); } +void Aquamarine::CDRMOutput::setCursorVisible(bool visible) { + cursorVisible = visible; + needsFrame = true; + scheduleFrame(); +} + bool Aquamarine::CDRMOutput::commitState(bool onlyTest) { if (!backend->backend->session->active) { backend->backend->log(AQ_LOG_ERROR, "drm: Session inactive"); @@ -994,9 +1008,9 @@ bool Aquamarine::CDRMOutput::commitState(bool onlyTest) { return false; } - if (STATE.enabled) + if (STATE.enabled && (COMMITTED & COutputState::eOutputStateProperties::AQ_OUTPUT_STATE_BUFFER)) flags |= DRM_MODE_PAGE_FLIP_EVENT; - if (STATE.presentationMode == AQ_OUTPUT_PRESENTATION_IMMEDIATE) + if (STATE.presentationMode == AQ_OUTPUT_PRESENTATION_IMMEDIATE && (COMMITTED & COutputState::eOutputStateProperties::AQ_OUTPUT_STATE_BUFFER)) flags |= DRM_MODE_PAGE_FLIP_ASYNC; } @@ -1007,20 +1021,8 @@ bool Aquamarine::CDRMOutput::commitState(bool onlyTest) { SP drmFB; auto buf = STATE.buffer; - // try to find the buffer in its layer - if (connector->crtc->primary->back && connector->crtc->primary->back->buffer == buf) { - backend->backend->log(AQ_LOG_TRACE, "drm: CRTC's back buffer matches committed"); - drmFB = connector->crtc->primary->back; - } else if (connector->crtc->primary->front && connector->crtc->primary->front->buffer == buf) { - backend->backend->log(AQ_LOG_TRACE, "drm: CRTC's front buffer matches committed"); - drmFB = connector->crtc->primary->front; - } else if (connector->crtc->primary->last && connector->crtc->primary->last->buffer == buf) { - backend->backend->log(AQ_LOG_TRACE, "drm: CRTC's last buffer matches committed :D"); // best case scenario and what is expected - drmFB = connector->crtc->primary->last; - } - if (!drmFB) - drmFB = CDRMFB::create(buf, backend); + drmFB = CDRMFB::create(buf, backend); // will return attachment if present if (!drmFB) { backend->backend->log(AQ_LOG_ERROR, "drm: Buffer failed to import to KMS"); @@ -1030,6 +1032,9 @@ bool Aquamarine::CDRMOutput::commitState(bool onlyTest) { data.mainFB = drmFB; } + if (connector->crtc->pendingCursor) + data.cursorFB = connector->crtc->pendingCursor; + data.blocking = BLOCKING; data.modeset = NEEDS_RECONFIG; data.flags = flags; @@ -1053,11 +1058,29 @@ SP Aquamarine::CDRMOutput::getBackend() { } bool Aquamarine::CDRMOutput::setCursor(SP buffer, const Vector2D& hotspot) { - return false; // FIXME: + if (!buffer) + setCursorVisible(false); + else { + cursorHotspot = hotspot; + auto fb = CDRMFB::create(buffer, backend); + if (!fb) { + backend->backend->log(AQ_LOG_ERROR, "drm: Cursor buffer failed to import to KMS"); + return false; + } + + backend->backend->log(AQ_LOG_DEBUG, std::format("drm: Cursor buffer imported into KMS with id {}", fb->id)); + + connector->crtc->pendingCursor = fb; + } + + needsFrame = true; + scheduleFrame(); + return true; } void Aquamarine::CDRMOutput::moveCursor(const Vector2D& coord) { - ; // FIXME: + cursorPos = coord; + backend->impl->moveCursor(connector); } void Aquamarine::CDRMOutput::scheduleFrame() { @@ -1067,7 +1090,7 @@ void Aquamarine::CDRMOutput::scheduleFrame() { backend->idleCallbacks.emplace_back([this]() { events.frame.emit(); }); } -Vector2D Aquamarine::CDRMOutput::maxCursorSize() { +Vector2D Aquamarine::CDRMOutput::cursorPlaneSize() { return backend->drmProps.cursorSize; } @@ -1077,11 +1100,25 @@ Aquamarine::CDRMOutput::CDRMOutput(const std::string& name_, Hyprutils::Memory:: } SP Aquamarine::CDRMFB::create(SP buffer_, Hyprutils::Memory::CWeakPointer backend_) { - auto fb = SP(new CDRMFB(buffer_, backend_)); + + SP fb; + + if (buffer_->attachments.has(AQ_ATTACHMENT_DRM_BUFFER)) { + auto at = (CDRMBufferAttachment*)buffer_->attachments.get(AQ_ATTACHMENT_DRM_BUFFER).get(); + fb = at->fb; + backend_->log(AQ_LOG_TRACE, std::format("drm: CDRMFB: buffer has drmfb attachment with fb {:x}", (uintptr_t)fb.get())); + } + + if (fb) + return fb; + + fb = SP(new CDRMFB(buffer_, backend_)); if (!fb->id) return nullptr; + buffer_->attachments.add(makeShared(fb)); + return fb; } @@ -1100,7 +1137,7 @@ Aquamarine::CDRMFB::CDRMFB(SP buffer_, Hyprutils::Memory::CWeakPointer< // TODO: check format for (int i = 0; i < attrs.planes; ++i) { - int ret = drmPrimeFDToHandle(backend->gpu->fd, attrs.fds.at(i), &boHandles.at(i)); + int ret = drmPrimeFDToHandle(backend->gpu->fd, attrs.fds.at(i), &boHandles[i]); if (ret) { backend->backend->log(AQ_LOG_ERROR, "drm: drmPrimeFDToHandle failed"); drop(); @@ -1120,7 +1157,7 @@ Aquamarine::CDRMFB::CDRMFB(SP buffer_, Hyprutils::Memory::CWeakPointer< backend->backend->log(AQ_LOG_TRACE, std::format("drm: new buffer {}", id)); - // FIXME: wlroots does this, I am unsure why, but if I do, the gpu driver will kill us. + // FIXME: why does this implode when it doesnt on wlroots or kwin? // closeHandles(); } @@ -1134,13 +1171,24 @@ void Aquamarine::CDRMFB::closeHandles() { handlesClosed = true; - for (auto& h : boHandles) { - if (h == 0) + std::vector closed; + + for (size_t i = 0; i < 4; ++i) { + if (boHandles.at(i) == 0) continue; - if (drmCloseBufferHandle(backend->gpu->fd, h)) + bool exists = false; + for (size_t j = 0; j < i; ++j) { + if (boHandles.at(i) == boHandles.at(j)) { + exists = true; + break; + } + } + if (exists) + continue; + + if (drmCloseBufferHandle(backend->gpu->fd, boHandles.at(i))) backend->backend->log(AQ_LOG_ERROR, "drm: drmCloseBufferHandle failed"); - h = 0; } } @@ -1166,9 +1214,9 @@ void Aquamarine::CDRMFB::drop() { uint32_t Aquamarine::CDRMFB::submitBuffer() { auto attrs = buffer->dmabuf(); uint32_t newID = 0; - std::array mods = {0}; + std::array mods = {0, 0, 0, 0}; for (size_t i = 0; i < attrs.planes; ++i) { - mods.at(i) = attrs.modifier; + mods[i] = attrs.modifier; } if (backend->drmProps.supportsAddFb2Modifiers && attrs.modifier != DRM_FORMAT_MOD_INVALID) { @@ -1177,7 +1225,7 @@ uint32_t Aquamarine::CDRMFB::submitBuffer() { fourccToName(attrs.format), attrs.modifier)); if (drmModeAddFB2WithModifiers(backend->gpu->fd, attrs.size.x, attrs.size.y, attrs.format, boHandles.data(), attrs.strides.data(), attrs.offsets.data(), mods.data(), &newID, DRM_MODE_FB_MODIFIERS)) { - backend->backend->log(AQ_LOG_ERROR, "drm: Failed to submit a buffer with AddFB2"); + backend->backend->log(AQ_LOG_ERROR, "drm: Failed to submit a buffer with drmModeAddFB2WithModifiers"); return 0; } } else { @@ -1191,7 +1239,7 @@ uint32_t Aquamarine::CDRMFB::submitBuffer() { std::format("drm: Using drmModeAddFB2 to import buffer into KMS: Size {} with format {} and mod {}", attrs.size, fourccToName(attrs.format), attrs.modifier)); if (drmModeAddFB2(backend->gpu->fd, attrs.size.x, attrs.size.y, attrs.format, boHandles.data(), attrs.strides.data(), attrs.offsets.data(), &newID, 0)) { - backend->backend->log(AQ_LOG_ERROR, "drm: drmModeAddFB2 failed"); + backend->backend->log(AQ_LOG_ERROR, "drm: Failed to submit a buffer with drmModeAddFB2"); return 0; } } @@ -1233,3 +1281,7 @@ void Aquamarine::SDRMConnectorCommitData::calculateMode(Hyprutils::Memory::CShar }; snprintf(modeInfo.name, sizeof(modeInfo.name), "%dx%d", (int)MODE->pixelSize.x, (int)MODE->pixelSize.y); } + +Aquamarine::CDRMBufferAttachment::CDRMBufferAttachment(SP fb_) : fb(fb_) { + ; +} diff --git a/src/backend/drm/impl/Legacy.cpp b/src/backend/drm/impl/Legacy.cpp index 2f6079b..e76c68b 100644 --- a/src/backend/drm/impl/Legacy.cpp +++ b/src/backend/drm/impl/Legacy.cpp @@ -13,6 +13,18 @@ Aquamarine::CDRMLegacyImpl::CDRMLegacyImpl(Hyprutils::Memory::CSharedPointer connector) { + Vector2D cursorPos = connector->output->cursorPos; + if (int ret2 = drmModeMoveCursor(connector->backend->gpu->fd, connector->crtc->id, cursorPos.x, cursorPos.y)) { + connector->backend->backend->log(AQ_LOG_ERROR, std::format("legacy drm: drmModeMoveCursor failed: {}", strerror(-ret2))); + return false; + } + + connector->backend->backend->log(AQ_LOG_DEBUG, "legacy drm: cursor move"); + + return true; +} + bool Aquamarine::CDRMLegacyImpl::commitInternal(Hyprutils::Memory::CSharedPointer connector, const SDRMConnectorCommitData& data) { const auto& STATE = connector->output->state->state(); SP mainFB; @@ -69,13 +81,48 @@ bool Aquamarine::CDRMLegacyImpl::commitInternal(Hyprutils::Memory::CSharedPointe } connector->output->vrrActive = STATE.adaptiveSync; - connector->backend->backend->log(AQ_LOG_ERROR, std::format("legacy drm: connector {} vrr -> {}", connector->id, STATE.adaptiveSync)); + connector->backend->backend->log(AQ_LOG_DEBUG, std::format("legacy drm: connector {} vrr -> {}", connector->id, STATE.adaptiveSync)); } // TODO: gamma - // TODO: cursor plane - if (drmModeSetCursor(connector->backend->gpu->fd, connector->crtc->id, 0, 0, 0)) + if (data.cursorFB && connector->crtc->cursor && connector->output->cursorVisible && enable) { + uint32_t boHandle = 0; + auto attrs = data.cursorFB->buffer->dmabuf(); + + if (int ret = drmPrimeFDToHandle(connector->backend->gpu->fd, attrs.fds.at(0), &boHandle); ret) { + connector->backend->backend->log(AQ_LOG_ERROR, std::format("legacy drm: drmPrimeFDToHandle failed: {}", strerror(-ret))); + return false; + } + + connector->backend->backend->log(AQ_LOG_DEBUG, + std::format("legacy drm: cursor fb: {} with bo handle {} from fd {}, size {}", connector->backend->gpu->fd, boHandle, + data.cursorFB->buffer->dmabuf().fds.at(0), data.cursorFB->buffer->size)); + + Vector2D cursorPos = connector->output->cursorPos; + + struct drm_mode_cursor2 request = { + .flags = DRM_MODE_CURSOR_BO | DRM_MODE_CURSOR_MOVE, + .crtc_id = connector->crtc->id, + .x = (int32_t)cursorPos.x, + .y = (int32_t)cursorPos.y, + .width = (uint32_t)data.cursorFB->buffer->size.x, + .height = (uint32_t)data.cursorFB->buffer->size.y, + .handle = boHandle, + .hot_x = (int32_t)connector->output->cursorHotspot.x, + .hot_y = (int32_t)connector->output->cursorHotspot.y, + }; + + int ret = drmIoctl(connector->backend->gpu->fd, DRM_IOCTL_MODE_CURSOR2, &request); + + if (boHandle && drmCloseBufferHandle(connector->backend->gpu->fd, boHandle)) + connector->backend->backend->log(AQ_LOG_ERROR, "legacy drm: drmCloseBufferHandle in cursor failed"); + + if (ret) { + connector->backend->backend->log(AQ_LOG_ERROR, std::format("legacy drm: cursor drmIoctl failed: {}", strerror(errno))); + return false; + } + } else if (drmModeSetCursor(connector->backend->gpu->fd, connector->crtc->id, 0, 0, 0)) connector->backend->backend->log(AQ_LOG_ERROR, "legacy drm: cursor null failed"); if (!enable) diff --git a/src/misc/Attachment.cpp b/src/misc/Attachment.cpp index 16a251f..8a05446 100644 --- a/src/misc/Attachment.cpp +++ b/src/misc/Attachment.cpp @@ -31,3 +31,7 @@ void Aquamarine::CAttachmentManager::remove(SP attachment) { void Aquamarine::CAttachmentManager::removeByType(eAttachmentType type) { std::erase_if(attachments, [type](const auto& e) { return e->type() == type; }); } + +void Aquamarine::CAttachmentManager::clear() { + attachments.clear(); +} diff --git a/src/output/Output.cpp b/src/output/Output.cpp index 3cb86ed..af5bb73 100644 --- a/src/output/Output.cpp +++ b/src/output/Output.cpp @@ -19,11 +19,15 @@ bool Aquamarine::IOutput::setCursor(Hyprutils::Memory::CSharedPointer b return false; } +void Aquamarine::IOutput::setCursorVisible(bool visible) { + ; +} + void Aquamarine::IOutput::scheduleFrame() { ; } -Hyprutils::Math::Vector2D Aquamarine::IOutput::maxCursorSize() { +Hyprutils::Math::Vector2D Aquamarine::IOutput::cursorPlaneSize() { return {}; // error }