diff --git a/include/aquamarine/backend/Backend.hpp b/include/aquamarine/backend/Backend.hpp index b115fa3..5ecb79e 100644 --- a/include/aquamarine/backend/Backend.hpp +++ b/include/aquamarine/backend/Backend.hpp @@ -112,6 +112,9 @@ namespace Aquamarine { /* remove an idle event from the queue */ void removeIdleEvent(Hyprutils::Memory::CSharedPointer> pfn); + // utils + int reopenDRMNode(int drmFD, bool allowRenderNode = true); + struct { Hyprutils::Signal::CSignal newOutput; Hyprutils::Signal::CSignal newPointer; diff --git a/src/backend/Backend.cpp b/src/backend/Backend.cpp index b2b6abd..752a654 100644 --- a/src/backend/Backend.cpp +++ b/src/backend/Backend.cpp @@ -9,6 +9,8 @@ #include #include #include +#include +#include using namespace Hyprutils::Memory; using namespace Aquamarine; @@ -144,7 +146,13 @@ bool Aquamarine::CBackend::start() { // TODO: obviously change this when (if) we add different allocators. for (auto& b : implementations) { if (b->drmFD() >= 0) { - primaryAllocator = CGBMAllocator::create(b->drmFD(), self); + auto fd = reopenDRMNode(b->drmFD()); + if (fd < 0) { + // this is critical, we cannot create an allocator properly + log(AQ_LOG_CRITICAL, "Failed to create an allocator (reopenDRMNode failed)"); + return false; + } + primaryAllocator = CGBMAllocator::create(fd, self); break; } } @@ -258,3 +266,64 @@ void Aquamarine::CBackend::dispatchIdle() { updateIdleTimer(); } + +// Yoinked from wlroots, render/allocator/allocator.c +// Ref-counting reasons, see https://gitlab.freedesktop.org/mesa/drm/-/merge_requests/110 +int Aquamarine::CBackend::reopenDRMNode(int drmFD, bool allowRenderNode) { + + if (drmIsMaster(drmFD)) { + // Only recent kernels support empty leases + uint32_t lesseeID = 0; + int leaseFD = drmModeCreateLease(drmFD, nullptr, 0, O_CLOEXEC, &lesseeID); + if (leaseFD >= 0) { + return leaseFD; + } else if (leaseFD != -EINVAL && leaseFD != -EOPNOTSUPP) { + log(AQ_LOG_ERROR, "reopenDRMNode: drmModeCreateLease failed"); + return -1; + } + log(AQ_LOG_DEBUG, "reopenDRMNode: drmModeCreateLease failed, falling back to open"); + } + + char* name = nullptr; + if (allowRenderNode) + name = drmGetRenderDeviceNameFromFd(drmFD); + + if (!name) { + // primary node or no name + name = drmGetDeviceNameFromFd2(drmFD); + + if (!name) { + log(AQ_LOG_ERROR, "reopenDRMNode: drmGetDeviceNameFromFd2 failed"); + return -1; + } + } + + log(AQ_LOG_DEBUG, std::format("reopenDRMNode: opening node {}", name)); + + int newFD = open(name, O_RDWR | O_CLOEXEC); + if (newFD < 0) { + log(AQ_LOG_ERROR, std::format("reopenDRMNode: failed to open node {}", name)); + free(name); + return -1; + } + + free(name); + + // We need to authenticate if we are using a DRM primary node and are the master + if (drmIsMaster(drmFD) && drmGetNodeTypeFromFd(drmFD) == DRM_NODE_PRIMARY) { + drm_magic_t magic; + if (int ret = drmGetMagic(newFD, &magic); ret < 0) { + log(AQ_LOG_ERROR, std::format("reopenDRMNode: drmGetMagic failed: {}", strerror(-ret))); + close(newFD); + return -1; + } + + if (int ret = drmAuthMagic(newFD, magic); ret < 0) { + log(AQ_LOG_ERROR, std::format("reopenDRMNode: drmAuthMagic failed: {}", strerror(-ret))); + close(newFD); + return -1; + } + } + + return newFD; +} diff --git a/src/backend/drm/DRM.cpp b/src/backend/drm/DRM.cpp index 42af003..afca396 100644 --- a/src/backend/drm/DRM.cpp +++ b/src/backend/drm/DRM.cpp @@ -1617,7 +1617,7 @@ 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(); + closeHandles(); listeners.destroyBuffer = buffer->events.destroy.registerListener([this](std::any d) { drop();