diff --git a/include/aquamarine/backend/Backend.hpp b/include/aquamarine/backend/Backend.hpp index 1bc7f7c..32a6e05 100644 --- a/include/aquamarine/backend/Backend.hpp +++ b/include/aquamarine/backend/Backend.hpp @@ -88,8 +88,9 @@ namespace Aquamarine { 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; - virtual Hyprutils::Memory::CWeakPointer getPrimary() = 0; + virtual std::vector> getAllocators() = 0; + virtual Hyprutils::Memory::CWeakPointer getPrimary() = 0; + virtual int drmRenderNodeFD() = 0; }; class CBackend { @@ -148,6 +149,9 @@ namespace Aquamarine { bool ready = false; Hyprutils::Memory::CSharedPointer session; + /* Get the primary DRM RenderNode */ + int drmRenderNodeFD(); + private: CBackend(); diff --git a/include/aquamarine/backend/DRM.hpp b/include/aquamarine/backend/DRM.hpp index 56a0e01..cc39686 100644 --- a/include/aquamarine/backend/DRM.hpp +++ b/include/aquamarine/backend/DRM.hpp @@ -379,6 +379,7 @@ namespace Aquamarine { std::vector idleCallbacks; std::string gpuName; + virtual int drmRenderNodeFD(); private: CDRMBackend(Hyprutils::Memory::CSharedPointer backend); diff --git a/include/aquamarine/backend/Headless.hpp b/include/aquamarine/backend/Headless.hpp index 1de6a0d..c9840fb 100644 --- a/include/aquamarine/backend/Headless.hpp +++ b/include/aquamarine/backend/Headless.hpp @@ -52,6 +52,7 @@ namespace Aquamarine { virtual Hyprutils::Memory::CWeakPointer getPrimary(); Hyprutils::Memory::CWeakPointer self; + virtual int drmRenderNodeFD(); private: CHeadlessBackend(Hyprutils::Memory::CSharedPointer backend_); diff --git a/include/aquamarine/backend/Session.hpp b/include/aquamarine/backend/Session.hpp index cb7e5ae..b0fb453 100644 --- a/include/aquamarine/backend/Session.hpp +++ b/include/aquamarine/backend/Session.hpp @@ -29,6 +29,7 @@ namespace Aquamarine { static Hyprutils::Memory::CSharedPointer openIfKMS(Hyprutils::Memory::CSharedPointer session_, const std::string& path_); bool supportsKMS(); + void resolveMatchingRenderNode(udev_device* cardDevice); int fd = -1; int deviceID = -1; @@ -53,6 +54,8 @@ namespace Aquamarine { Hyprutils::Signal::CSignalT<> remove; } events; + int renderNodeFd = -1; + private: Hyprutils::Memory::CWeakPointer session; }; diff --git a/include/aquamarine/backend/Wayland.hpp b/include/aquamarine/backend/Wayland.hpp index 94d75da..0b9f307 100644 --- a/include/aquamarine/backend/Wayland.hpp +++ b/include/aquamarine/backend/Wayland.hpp @@ -139,6 +139,7 @@ namespace Aquamarine { virtual Hyprutils::Memory::CWeakPointer getPrimary(); Hyprutils::Memory::CWeakPointer self; + virtual int drmRenderNodeFD(); private: CWaylandBackend(Hyprutils::Memory::CSharedPointer backend); diff --git a/src/backend/Backend.cpp b/src/backend/Backend.cpp index 36e5681..7d63fe1 100644 --- a/src/backend/Backend.cpp +++ b/src/backend/Backend.cpp @@ -210,6 +210,17 @@ int Aquamarine::CBackend::drmFD() { return -1; } +int Aquamarine::CBackend::drmRenderNodeFD() { + for (auto const& i : implementations) { + int fd = i->drmRenderNodeFD(); + if (fd < 0) + continue; + + return fd; + } + return -1; +} + bool Aquamarine::CBackend::hasSession() { return session; } diff --git a/src/backend/Headless.cpp b/src/backend/Headless.cpp index d9075cc..927baa7 100644 --- a/src/backend/Headless.cpp +++ b/src/backend/Headless.cpp @@ -102,6 +102,10 @@ int Aquamarine::CHeadlessBackend::drmFD() { return -1; } +int Aquamarine::CHeadlessBackend::drmRenderNodeFD() { + return -1; +} + bool Aquamarine::CHeadlessBackend::dispatchEvents() { return true; } diff --git a/src/backend/Session.cpp b/src/backend/Session.cpp index 65516b2..d74442f 100644 --- a/src/backend/Session.cpp +++ b/src/backend/Session.cpp @@ -1,4 +1,5 @@ #include +#include extern "C" { #include @@ -141,6 +142,9 @@ Aquamarine::CSessionDevice::~CSessionDevice() { session->backend->log(AQ_LOG_ERROR, std::format("libseat: Couldn't close device at {}", path)); if (fd >= 0) close(fd); + + if (renderNodeFd) + close(renderNodeFd); } bool Aquamarine::CSessionDevice::supportsKMS() { @@ -157,6 +161,84 @@ bool Aquamarine::CSessionDevice::supportsKMS() { return kms; } +void Aquamarine::CSessionDevice::resolveMatchingRenderNode(udev_device* cardDevice) { + if (!cardDevice) + return; + + auto pciParent = udev_device_get_parent_with_subsystem_devtype(cardDevice, "pci", nullptr); + const auto* pciSyspath = pciParent ? udev_device_get_syspath(pciParent) : nullptr; + + auto* enumerate = udev_enumerate_new(session->udevHandle); + if (!enumerate) + return; + + udev_enumerate_add_match_subsystem(enumerate, "drm"); + udev_enumerate_scan_devices(enumerate); + + auto* devices = udev_enumerate_get_list_entry(enumerate); + udev_list_entry* entry = nullptr; + + bool matched = false; + + udev_list_entry_foreach(entry, devices) { + const auto* path = udev_list_entry_get_name(entry); + auto dev = udev_device_new_from_syspath(session->udevHandle, path); + if (!dev) + continue; + + const auto* devnode = udev_device_get_devnode(dev); + const auto* devtype = udev_device_get_devtype(dev); + + if (!devnode || !devtype || strcmp(devtype, "drm_minor") != 0 || !strstr(devnode, "renderD")) { + udev_device_unref(dev); + continue; + } + + auto devParent = udev_device_get_parent_with_subsystem_devtype(dev, "pci", nullptr); + if (devParent && pciSyspath && strcmp(udev_device_get_syspath(devParent), pciSyspath) == 0) { + renderNodeFd = open(devnode, O_RDWR | O_CLOEXEC); + if (renderNodeFd < 0) + session->backend->log(AQ_LOG_WARNING, std::format("drm: Failed to open matching render node {}", devnode)); + else + matched = true; + + udev_device_unref(dev); + break; + } + + udev_device_unref(dev); + } + + if (!matched) { + // fallback to the first render node + udev_list_entry_foreach(entry, devices) { + const auto* path = udev_list_entry_get_name(entry); + auto dev = udev_device_new_from_syspath(session->udevHandle, path); + if (!dev) + continue; + + const auto* devnode = udev_device_get_devnode(dev); + const auto* devtype = udev_device_get_devtype(dev); + + if (!devnode || !devtype || strcmp(devtype, "drm_minor") != 0 || !strstr(devnode, "renderD")) { + udev_device_unref(dev); + continue; + } + + renderNodeFd = open(devnode, O_RDWR | O_CLOEXEC); + if (renderNodeFd >= 0) { + session->backend->log(AQ_LOG_WARNING, std::format("drm: No matching render node for {}, falling back to {}", path, devnode)); + udev_device_unref(dev); + break; + } + + udev_device_unref(dev); + } + } + + udev_enumerate_unref(enumerate); +} + SP Aquamarine::CSessionDevice::openIfKMS(SP session_, const std::string& path_) { auto dev = makeShared(session_, path_); if (!dev->supportsKMS()) diff --git a/src/backend/Wayland.cpp b/src/backend/Wayland.cpp index 0610f6a..f3a6391 100644 --- a/src/backend/Wayland.cpp +++ b/src/backend/Wayland.cpp @@ -139,6 +139,11 @@ int Aquamarine::CWaylandBackend::drmFD() { return drmState.fd; } +int Aquamarine::CWaylandBackend::drmRenderNodeFD() { + // creation already attempts to use the rendernode, so just return same fd as drmFD(). + return drmState.fd; +} + bool Aquamarine::CWaylandBackend::createOutput(const std::string& szName) { auto o = outputs.emplace_back(SP(new CWaylandOutput(szName.empty() ? std::format("WAYLAND-{}", ++lastOutputID) : szName, self))); o->self = o; diff --git a/src/backend/drm/DRM.cpp b/src/backend/drm/DRM.cpp index aa7a497..bf386f2 100644 --- a/src/backend/drm/DRM.cpp +++ b/src/backend/drm/DRM.cpp @@ -150,6 +150,8 @@ static std::vector> scanGPUs(SP backend) { continue; } + sessionDevice->resolveMatchingRenderNode(device); + udev_device_unref(device); if (isBootVGA) @@ -906,6 +908,10 @@ int Aquamarine::CDRMBackend::drmFD() { return gpu->fd; } +int Aquamarine::CDRMBackend::drmRenderNodeFD() { + return gpu->renderNodeFd; +} + static void handlePF(int fd, unsigned seq, unsigned tv_sec, unsigned tv_usec, unsigned crtc_id, void* data) { auto pageFlip = (SDRMPageFlip*)data;