diff --git a/include/aquamarine/allocator/Allocator.hpp b/include/aquamarine/allocator/Allocator.hpp index 5ca54af..02552f2 100644 --- a/include/aquamarine/allocator/Allocator.hpp +++ b/include/aquamarine/allocator/Allocator.hpp @@ -16,6 +16,7 @@ namespace Aquamarine { enum eAllocatorType { AQ_ALLOCATOR_TYPE_GBM = 0, + AQ_ALLOCATOR_TYPE_DRM_DUMB, }; class IAllocator { diff --git a/include/aquamarine/allocator/DRMDumb.hpp b/include/aquamarine/allocator/DRMDumb.hpp new file mode 100644 index 0000000..a5eca2c --- /dev/null +++ b/include/aquamarine/allocator/DRMDumb.hpp @@ -0,0 +1,68 @@ +#pragma once + +#include "Allocator.hpp" + +namespace Aquamarine { + class CDRMDumbAllocator; + class CBackend; + class CSwapchain; + + class CDRMDumbBuffer : public IBuffer { + public: + virtual ~CDRMDumbBuffer(); + + virtual eBufferCapability caps(); + virtual eBufferType type(); + virtual void update(const Hyprutils::Math::CRegion& damage); + virtual bool isSynchronous(); + virtual bool good(); + virtual SDMABUFAttrs dmabuf(); + virtual std::tuple beginDataPtr(uint32_t flags); + virtual void endDataPtr(); + + private: + CDRMDumbBuffer(const SAllocatorBufferParams& params, Hyprutils::Memory::CWeakPointer allocator_, + Hyprutils::Memory::CSharedPointer swapchain); + + Hyprutils::Memory::CWeakPointer allocator; + + // + Hyprutils::Math::Vector2D pixelSize; + uint32_t stride = 0, handle = 0; + uint64_t bufferLen = 0; + uint8_t* data = nullptr; + int primeFD = -1; + + // + SDMABUFAttrs attrs{.success = false}; + + friend class CDRMDumbAllocator; + }; + + class CDRMDumbAllocator : public IAllocator { + public: + ~CDRMDumbAllocator(); + static Hyprutils::Memory::CSharedPointer create(int drmfd_, Hyprutils::Memory::CWeakPointer backend_); + + virtual Hyprutils::Memory::CSharedPointer acquire(const SAllocatorBufferParams& params, Hyprutils::Memory::CSharedPointer swapchain_); + virtual Hyprutils::Memory::CSharedPointer getBackend(); + virtual int drmFD(); + virtual eAllocatorType type(); + + // + Hyprutils::Memory::CWeakPointer self; + + private: + CDRMDumbAllocator(int fd_, Hyprutils::Memory::CWeakPointer backend_); + + // a vector purely for tracking (debugging) the buffers and nothing more + std::vector> buffers; + + Hyprutils::Memory::CWeakPointer backend; + + int drmfd = -1; + + friend class CDRMDumbBuffer; + friend class CDRMRenderer; + }; +}; diff --git a/include/aquamarine/backend/Backend.hpp b/include/aquamarine/backend/Backend.hpp index 6e40d62..2dd039c 100644 --- a/include/aquamarine/backend/Backend.hpp +++ b/include/aquamarine/backend/Backend.hpp @@ -66,18 +66,19 @@ namespace Aquamarine { AQ_BACKEND_CAPABILITY_POINTER = (1 << 0), }; - virtual eBackendType type() = 0; - virtual bool start() = 0; - virtual std::vector> pollFDs() = 0; - virtual int drmFD() = 0; - virtual bool dispatchEvents() = 0; - virtual uint32_t capabilities() = 0; - virtual void onReady() = 0; - virtual std::vector getRenderFormats() = 0; - virtual std::vector getCursorFormats() = 0; - virtual bool createOutput(const std::string& name = "") = 0; // "" means auto - virtual Hyprutils::Memory::CSharedPointer preferredAllocator() = 0; - virtual std::vector getRenderableFormats(); // empty = use getRenderFormats + virtual eBackendType type() = 0; + virtual bool start() = 0; + virtual std::vector> pollFDs() = 0; + virtual int drmFD() = 0; + virtual bool dispatchEvents() = 0; + virtual uint32_t capabilities() = 0; + virtual void onReady() = 0; + virtual std::vector getRenderFormats() = 0; + virtual std::vector getCursorFormats() = 0; + virtual bool createOutput(const std::string& name = "") = 0; // "" means auto + virtual Hyprutils::Memory::CSharedPointer preferredAllocator() = 0; + virtual std::vector getRenderableFormats(); // empty = use getRenderFormats + virtual std::vector> getAllocators() = 0; }; class CBackend { diff --git a/include/aquamarine/backend/DRM.hpp b/include/aquamarine/backend/DRM.hpp index 40ea97a..3347cda 100644 --- a/include/aquamarine/backend/DRM.hpp +++ b/include/aquamarine/backend/DRM.hpp @@ -15,6 +15,7 @@ namespace Aquamarine { class CDRMOutput; struct SDRMConnector; class CDRMRenderer; + class CDRMDumbAllocator; typedef std::function FIdleCallback; @@ -340,28 +341,29 @@ namespace Aquamarine { class CDRMBackend : public IBackendImplementation { public: virtual ~CDRMBackend(); - virtual eBackendType type(); - virtual bool start(); - virtual std::vector> pollFDs(); - virtual int drmFD(); - virtual bool dispatchEvents(); - virtual uint32_t capabilities(); - virtual bool setCursor(Hyprutils::Memory::CSharedPointer buffer, const Hyprutils::Math::Vector2D& hotspot); - virtual void onReady(); - virtual std::vector getRenderFormats(); - virtual std::vector getCursorFormats(); - virtual bool createOutput(const std::string& name = ""); - virtual Hyprutils::Memory::CSharedPointer preferredAllocator(); - virtual std::vector getRenderableFormats(); + virtual eBackendType type(); + virtual bool start(); + virtual std::vector> pollFDs(); + virtual int drmFD(); + virtual bool dispatchEvents(); + virtual uint32_t capabilities(); + virtual bool setCursor(Hyprutils::Memory::CSharedPointer buffer, const Hyprutils::Math::Vector2D& hotspot); + virtual void onReady(); + virtual std::vector getRenderFormats(); + virtual std::vector getCursorFormats(); + virtual bool createOutput(const std::string& name = ""); + virtual Hyprutils::Memory::CSharedPointer preferredAllocator(); + virtual std::vector getRenderableFormats(); + virtual std::vector> getAllocators(); - Hyprutils::Memory::CWeakPointer self; + Hyprutils::Memory::CWeakPointer self; - void log(eBackendLogLevel, const std::string&); - bool sessionActive(); - int getNonMasterFD(); + void log(eBackendLogLevel, const std::string&); + bool sessionActive(); + int getNonMasterFD(); - std::vector idleCallbacks; - std::string gpuName; + std::vector idleCallbacks; + std::string gpuName; private: CDRMBackend(Hyprutils::Memory::CSharedPointer backend); @@ -396,6 +398,8 @@ namespace Aquamarine { std::vector formats; std::vector glFormats; + Hyprutils::Memory::CSharedPointer dumbAllocator; + bool atomic = false; struct { diff --git a/include/aquamarine/backend/Headless.hpp b/include/aquamarine/backend/Headless.hpp index d619528..aee26a4 100644 --- a/include/aquamarine/backend/Headless.hpp +++ b/include/aquamarine/backend/Headless.hpp @@ -8,6 +8,7 @@ namespace Aquamarine { class CBackend; class CHeadlessBackend; + class IAllocator; class CHeadlessOutput : public IOutput { public: @@ -35,20 +36,21 @@ namespace Aquamarine { class CHeadlessBackend : public IBackendImplementation { public: virtual ~CHeadlessBackend(); - virtual eBackendType type(); - virtual bool start(); - virtual std::vector> pollFDs(); - virtual int drmFD(); - virtual bool dispatchEvents(); - virtual uint32_t capabilities(); - virtual bool setCursor(Hyprutils::Memory::CSharedPointer buffer, const Hyprutils::Math::Vector2D& hotspot); - virtual void onReady(); - virtual std::vector getRenderFormats(); - virtual std::vector getCursorFormats(); - virtual bool createOutput(const std::string& name = ""); - virtual Hyprutils::Memory::CSharedPointer preferredAllocator(); + virtual eBackendType type(); + virtual bool start(); + virtual std::vector> pollFDs(); + virtual int drmFD(); + virtual bool dispatchEvents(); + virtual uint32_t capabilities(); + virtual bool setCursor(Hyprutils::Memory::CSharedPointer buffer, const Hyprutils::Math::Vector2D& hotspot); + virtual void onReady(); + virtual std::vector getRenderFormats(); + virtual std::vector getCursorFormats(); + virtual bool createOutput(const std::string& name = ""); + virtual Hyprutils::Memory::CSharedPointer preferredAllocator(); + virtual std::vector> getAllocators(); - Hyprutils::Memory::CWeakPointer self; + Hyprutils::Memory::CWeakPointer self; private: CHeadlessBackend(Hyprutils::Memory::CSharedPointer backend_); diff --git a/include/aquamarine/backend/Wayland.hpp b/include/aquamarine/backend/Wayland.hpp index ea71a0b..33d6502 100644 --- a/include/aquamarine/backend/Wayland.hpp +++ b/include/aquamarine/backend/Wayland.hpp @@ -123,20 +123,21 @@ namespace Aquamarine { class CWaylandBackend : public IBackendImplementation { public: virtual ~CWaylandBackend(); - virtual eBackendType type(); - virtual bool start(); - virtual std::vector> pollFDs(); - virtual int drmFD(); - virtual bool dispatchEvents(); - virtual uint32_t capabilities(); - virtual bool setCursor(Hyprutils::Memory::CSharedPointer buffer, const Hyprutils::Math::Vector2D& hotspot); - virtual void onReady(); - virtual std::vector getRenderFormats(); - virtual std::vector getCursorFormats(); - virtual bool createOutput(const std::string& name = ""); - virtual Hyprutils::Memory::CSharedPointer preferredAllocator(); + virtual eBackendType type(); + virtual bool start(); + virtual std::vector> pollFDs(); + virtual int drmFD(); + virtual bool dispatchEvents(); + virtual uint32_t capabilities(); + virtual bool setCursor(Hyprutils::Memory::CSharedPointer buffer, const Hyprutils::Math::Vector2D& hotspot); + virtual void onReady(); + virtual std::vector getRenderFormats(); + virtual std::vector getCursorFormats(); + virtual bool createOutput(const std::string& name = ""); + virtual Hyprutils::Memory::CSharedPointer preferredAllocator(); + virtual std::vector> getAllocators(); - Hyprutils::Memory::CWeakPointer self; + Hyprutils::Memory::CWeakPointer self; private: CWaylandBackend(Hyprutils::Memory::CSharedPointer backend); diff --git a/src/allocator/DRMDumb.cpp b/src/allocator/DRMDumb.cpp new file mode 100644 index 0000000..e1fb32f --- /dev/null +++ b/src/allocator/DRMDumb.cpp @@ -0,0 +1,159 @@ +#include +#include +#include +#include +#include "FormatUtils.hpp" +#include "Shared.hpp" +#include +#include +#include +#include +#include +#include +#include "../backend/drm/Renderer.hpp" + +using namespace Aquamarine; +using namespace Hyprutils::Memory; +#define SP CSharedPointer +#define WP CWeakPointer + +Aquamarine::CDRMDumbBuffer::CDRMDumbBuffer(const SAllocatorBufferParams& params, Hyprutils::Memory::CWeakPointer allocator_, + Hyprutils::Memory::CSharedPointer swapchain) : allocator(allocator_) { + attrs.format = params.format; + + if (int ret = drmModeCreateDumbBuffer(allocator->drmFD(), params.size.x, params.size.y, 32, 0, &handle, &stride, &bufferLen); ret < 0) { + allocator->backend->log(AQ_LOG_ERROR, std::format("failed to create a drm_dumb buffer: {}", strerror(-ret))); + return; + } + + pixelSize = {(double)params.size.x, (double)params.size.y}; + + attrs.size = pixelSize; + attrs.strides.at(0) = stride; + attrs.planes = 1; + size = pixelSize; + + uint64_t offset = 0; + if (int ret = drmModeMapDumbBuffer(allocator->drmFD(), handle, &offset); ret < 0) { + allocator->backend->log(AQ_LOG_ERROR, std::format("failed to map a drm_dumb buffer: {}", strerror(-ret))); + return; + } + + data = (uint8_t*)mmap(nullptr, bufferLen, PROT_READ | PROT_WRITE, MAP_SHARED, allocator->drmFD(), offset); + if (data == MAP_FAILED) { + allocator->backend->log(AQ_LOG_ERROR, "failed to mmap a drm_dumb buffer"); + return; + } + + // set the entire buffer so we dont get garbage + memset(data, 0xFF, bufferLen); + + if (int ret = drmPrimeHandleToFD(allocator->drmFD(), handle, DRM_CLOEXEC, &primeFD); ret < 0) { + allocator->backend->log(AQ_LOG_ERROR, std::format("failed to map a drm_dumb buffer: {}", strerror(-ret))); + return; + } + + attrs.fds.at(0) = primeFD; + + attrs.success = true; + + allocator->backend->log(AQ_LOG_DEBUG, std::format("DRM Dumb: Allocated a new buffer with primeFD {}, size {} and format {}", primeFD, attrs.size, fourccToName(attrs.format))); +} + +Aquamarine::CDRMDumbBuffer::~CDRMDumbBuffer() { + events.destroy.emit(); + + TRACE(allocator->backend->log(AQ_LOG_TRACE, std::format("DRM Dumb: dropping buffer {}", primeFD))); + + if (handle == 0) + return; + + if (data) + munmap(data, bufferLen); + + drmModeDestroyDumbBuffer(allocator->drmFD(), handle); +} + +eBufferCapability Aquamarine::CDRMDumbBuffer::caps() { + return eBufferCapability::BUFFER_CAPABILITY_DATAPTR; +} + +eBufferType Aquamarine::CDRMDumbBuffer::type() { + return eBufferType::BUFFER_TYPE_DMABUF; +} + +void Aquamarine::CDRMDumbBuffer::update(const Hyprutils::Math::CRegion& damage) { + ; // nothing to do +} + +bool Aquamarine::CDRMDumbBuffer::isSynchronous() { + return true; +} + +bool Aquamarine::CDRMDumbBuffer::good() { + return attrs.success && data; +} + +SDMABUFAttrs Aquamarine::CDRMDumbBuffer::dmabuf() { + return attrs; +} + +std::tuple Aquamarine::CDRMDumbBuffer::beginDataPtr(uint32_t flags) { + return {data, attrs.format, bufferLen}; +} + +void Aquamarine::CDRMDumbBuffer::endDataPtr() { + ; // nothing to do +} + +Aquamarine::CDRMDumbAllocator::~CDRMDumbAllocator() { + ; // nothing to do +} + +SP Aquamarine::CDRMDumbAllocator::create(int drmfd_, Hyprutils::Memory::CWeakPointer backend_) { + if (drmGetNodeTypeFromFd(drmfd_) != DRM_NODE_PRIMARY) { + backend_->log(AQ_LOG_ERROR, "DRM Dumb: Cannot create allocator when drmfd is not the primary node"); + return nullptr; + } + + uint64_t hasDumb = 0; + if (drmGetCap(drmfd_, DRM_CAP_DUMB_BUFFER, &hasDumb) < 0) { + backend_->log(AQ_LOG_ERROR, "DRM Dumb: Failed to query hasDumb"); + return nullptr; + } + + if (!hasDumb) { + backend_->log(AQ_LOG_ERROR, "DRM Dumb: hasDumb is false, gpu driver doesn't support dumb buffers!"); + return nullptr; + } + + auto a = SP(new CDRMDumbAllocator(drmfd_, backend_)); + a->self = a; + + backend_->log(AQ_LOG_DEBUG, "DRM Dumb: created a dumb allocator"); + + return a; +} + +SP Aquamarine::CDRMDumbAllocator::acquire(const SAllocatorBufferParams& params, SP swapchain_) { + auto buf = SP(new CDRMDumbBuffer(params, self, swapchain_)); + if (!buf->good()) + return nullptr; + return buf; +} + +SP Aquamarine::CDRMDumbAllocator::getBackend() { + return backend.lock(); +} + +int Aquamarine::CDRMDumbAllocator::drmFD() { + return drmfd; +} + +eAllocatorType Aquamarine::CDRMDumbAllocator::type() { + return eAllocatorType::AQ_ALLOCATOR_TYPE_DRM_DUMB; +} + +Aquamarine::CDRMDumbAllocator::CDRMDumbAllocator(int fd_, Hyprutils::Memory::CWeakPointer backend_) : drmfd(fd_), backend(backend_) { + ; // nothing to do +} diff --git a/src/backend/Headless.cpp b/src/backend/Headless.cpp index 16fd5d9..b556a07 100644 --- a/src/backend/Headless.cpp +++ b/src/backend/Headless.cpp @@ -187,6 +187,10 @@ SP Aquamarine::CHeadlessBackend::preferredAllocator() { return backend->primaryAllocator; } +std::vector> Aquamarine::CHeadlessBackend::getAllocators() { + return {backend->primaryAllocator}; +} + bool Aquamarine::CHeadlessBackend::CTimer::expired() { return std::chrono::steady_clock::now() > when; } diff --git a/src/backend/Wayland.cpp b/src/backend/Wayland.cpp index e622fc0..ec81b07 100644 --- a/src/backend/Wayland.cpp +++ b/src/backend/Wayland.cpp @@ -442,6 +442,10 @@ SP Aquamarine::CWaylandBackend::preferredAllocator() { return backend->primaryAllocator; } +std::vector> Aquamarine::CWaylandBackend::getAllocators() { + return {backend->primaryAllocator}; +} + Aquamarine::CWaylandOutput::CWaylandOutput(const std::string& name_, Hyprutils::Memory::CWeakPointer backend_) : backend(backend_) { name = name_; diff --git a/src/backend/drm/DRM.cpp b/src/backend/drm/DRM.cpp index f4de19e..f69a444 100644 --- a/src/backend/drm/DRM.cpp +++ b/src/backend/drm/DRM.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -255,6 +256,8 @@ std::vector> Aquamarine::CDRMBackend::attempt(SP backe newPrimary = drmBackend; } + drmBackend->dumbAllocator = CDRMDumbAllocator::create(gpu->fd, backend); + backends.emplace_back(drmBackend); // so that session can handle udev change/remove events for this gpu @@ -961,6 +964,10 @@ SP Aquamarine::CDRMBackend::preferredAllocator() { return backend->primaryAllocator; } +std::vector> Aquamarine::CDRMBackend::getAllocators() { + return {backend->primaryAllocator, dumbAllocator}; +} + bool Aquamarine::SDRMPlane::init(drmModePlane* plane) { id = plane->plane_id; @@ -1647,34 +1654,46 @@ SP Aquamarine::CDRMOutput::getBackend() { } bool Aquamarine::CDRMOutput::setCursor(SP buffer, const Vector2D& hotspot) { - if (buffer && !buffer->dmabuf().success) { - backend->backend->log(AQ_LOG_ERROR, "drm: Cursor buffer has to be a dmabuf"); - return false; - } - if (!connector->crtc) return false; if (!buffer) setCursorVisible(false); else { + auto bufferType = buffer->type(); + + if (!buffer->good()) { + backend->backend->log(AQ_LOG_ERROR, "drm: bad buffer passed to setCursor"); + return false; + } + + if ((bufferType == eBufferType::BUFFER_TYPE_SHM && !buffer->shm().success) || (bufferType == eBufferType::BUFFER_TYPE_DMABUF && !buffer->dmabuf().success)) { + backend->backend->log(AQ_LOG_ERROR, "drm: Invalid buffer passed to setCursor"); + return false; + } + SP fb; if (backend->primary) { TRACE(backend->backend->log(AQ_LOG_TRACE, "drm: Backend requires cursor blit, blitting")); + // TODO: will this not implode on drm_dumb?! + if (!mgpu.cursorSwapchain) { TRACE(backend->backend->log(AQ_LOG_TRACE, "drm: No cursorSwapchain for blit, creating")); mgpu.cursorSwapchain = CSwapchain::create(backend->rendererState.allocator, backend.lock()); } - auto OPTIONS = mgpu.cursorSwapchain->currentOptions(); - OPTIONS.multigpu = false; - OPTIONS.scanout = true; - OPTIONS.cursor = true; - OPTIONS.format = buffer->dmabuf().format; - OPTIONS.size = buffer->dmabuf().size; - OPTIONS.length = 2; + const auto FORMAT = bufferType == eBufferType::BUFFER_TYPE_SHM ? buffer->shm().format : buffer->dmabuf().format; + const auto SIZE = bufferType == eBufferType::BUFFER_TYPE_SHM ? buffer->shm().size : buffer->dmabuf().size; + + auto OPTIONS = mgpu.cursorSwapchain->currentOptions(); + OPTIONS.multigpu = false; + OPTIONS.scanout = true; + OPTIONS.cursor = true; + OPTIONS.format = FORMAT; + OPTIONS.size = SIZE; + OPTIONS.length = 2; if (!mgpu.cursorSwapchain->reconfigure(OPTIONS)) { backend->backend->log(AQ_LOG_ERROR, "drm: Backend requires blit, but the mgpu cursorSwapchain failed reconfiguring"); @@ -1808,7 +1827,7 @@ Aquamarine::CDRMFB::CDRMFB(SP buffer_, Hyprutils::Memory::CWeakPointer< void Aquamarine::CDRMFB::import() { auto attrs = buffer->dmabuf(); if (!attrs.success) { - backend->backend->log(AQ_LOG_ERROR, "drm: Buffer submitted has no dmabuf"); + backend->backend->log(AQ_LOG_ERROR, "drm: Buffer submitted has no dmabuf or a drm handle"); return; } @@ -1818,7 +1837,6 @@ void Aquamarine::CDRMFB::import() { } // TODO: check format - for (int i = 0; i < attrs.planes; ++i) { int ret = drmPrimeFDToHandle(backend->gpu->fd, attrs.fds.at(i), &boHandles[i]); if (ret) { @@ -1831,6 +1849,7 @@ void Aquamarine::CDRMFB::import() { } id = submitBuffer(); + if (!id) { backend->backend->log(AQ_LOG_ERROR, "drm: Failed to submit a buffer to KMS"); buffer->attachments.add(makeShared()); @@ -1840,7 +1859,6 @@ void Aquamarine::CDRMFB::import() { TRACE(backend->backend->log(AQ_LOG_TRACE, std::format("drm: new buffer {}", id))); - // FIXME: why does this implode when it doesnt on wlroots or kwin? closeHandles(); listeners.destroyBuffer = buffer->events.destroy.registerListener([this](std::any d) { @@ -1915,8 +1933,12 @@ void Aquamarine::CDRMFB::drop() { } uint32_t Aquamarine::CDRMFB::submitBuffer() { + uint32_t newID = 0; + + if (!buffer->dmabuf().success) + return 0; + auto attrs = buffer->dmabuf(); - uint32_t newID = 0; std::array mods = {0, 0, 0, 0}; for (size_t i = 0; i < attrs.planes; ++i) { mods[i] = attrs.modifier;