[plasma/kwin/Plasma/6.7] src: core/gpumanager: add KWIN_RENDER_NODES environment variable

Xaver Hugl <[email protected]>
Newsgroups gmane.comp.kde.cvs
Message-ID <[email protected]>
Git commit 6ca3d111c5081850248975ab5ab9dcac73da522a by Xaver Hugl, on behalf of Méven Car.
Committed on 20/07/2026 at 19:27.
Pushed by zamundaaa into branch 'Plasma/6.7'.

core/gpumanager: add KWIN_RENDER_NODES environment variable

This is mostly for autotests, but may be useful in some other cases as well,
like for debugging or for VFIO.
Different to the KWIN_DRM_DEVICES env var, the order doesn't matter for this.
It only restricts which render nodes KWin may open directly (Vulkan may open
other render nodes internally).

CCBUG: 521814

(cherry picked from commit 61aae2174ceb57a12583f4382f856a3dab07cacd)

Co-authored-by: Xaver Hugl <[email protected]>

M  +2    -23   src/backends/drm/drm_backend.cpp
M  +57   -8    src/core/gpumanager.cpp
M  +3    -0    src/core/gpumanager.h

https://invent.kde.org/plasma/kwin/-/commit/6ca3d111c5081850248975ab5ab9dcac73da522a

diff --git a/src/backends/drm/drm_backend.cpp b/src/backends/drm/drm_backend.cpp
index aa73aa7e259..ff8f2d8b271 100644
--- a/src/backends/drm/drm_backend.cpp
+++ b/src/backends/drm/drm_backend.cpp
@@ -11,6 +11,7 @@
 #include "config-kwin.h"
 
 #include "backends/libinput/libinputbackend.h"
+#include "core/gpumanager.h"
 #include "core/outputconfiguration.h"
 #include "core/renderdevice.h"
 #include "core/session.h"
@@ -51,28 +52,6 @@ using namespace std::chrono_literals;
 namespace KWin
 {
 
-static QStringList splitPathList(const QString &input, const QChar delimiter)
-{
-    QStringList ret;
-    QString tmp;
-    for (int i = 0; i < input.size(); i++) {
-        if (input[i] == delimiter) {
-            if (i > 0 && input[i - 1] == '\\') {
-                tmp[tmp.size() - 1] = delimiter;
-            } else if (!tmp.isEmpty()) {
-                ret.append(tmp);
-                tmp = QString();
-            }
-        } else {
-            tmp.append(input[i]);
-        }
-    }
-    if (!tmp.isEmpty()) {
-        ret.append(tmp);
-    }
-    return ret;
-}
-
 DrmBackend::DrmBackend(Session *session, QObject *parent)
     : OutputBackend(parent)
     , m_udev(std::make_unique<Udev>())
@@ -95,7 +74,7 @@ QList<BackendOutput *> DrmBackend::outputs() const
 
 bool DrmBackend::initialize()
 {
-    m_explicitGpus = splitPathList(qEnvironmentVariable("KWIN_DRM_DEVICES"), ':');
+    m_explicitGpus = GpuManager::splitPathList(qEnvironmentVariable("KWIN_DRM_DEVICES"));
 
     connect(m_session, &Session::devicePaused, this, [this](dev_t deviceId) {
         if (const auto gpu = findGpu(deviceId)) {
diff --git a/src/core/gpumanager.cpp b/src/core/gpumanager.cpp
index 5ab30d81d11..e6f5ac646df 100644
--- a/src/core/gpumanager.cpp
+++ b/src/core/gpumanager.cpp
@@ -14,6 +14,7 @@
 #include "utils/udev.h"
 #include "vulkan/vulkan_device.h"
 
+#include <QFileInfo>
 #include <QSocketNotifier>
 #include <fcntl.h>
 #include <sys/ioctl.h>
@@ -47,12 +48,36 @@ static std::optional<dev_t> getDevId(const FileDescriptor &fd)
     return buf.st_rdev;
 }
 
+QStringList GpuManager::splitPathList(const QString &input)
+{
+    const QChar delimiter = ':';
+    QStringList ret;
+    QString tmp;
+    for (int i = 0; i < input.size(); i++) {
+        if (input[i] == delimiter) {
+            if (i > 0 && input[i - 1] == '\\') {
+                tmp[tmp.size() - 1] = delimiter;
+            } else if (!tmp.isEmpty()) {
+                ret.append(tmp);
+                tmp = QString();
+            }
+        } else {
+            tmp.append(input[i]);
+        }
+    }
+    if (!tmp.isEmpty()) {
+        ret.append(tmp);
+    }
+    return ret;
+}
+
 GpuManager::GpuManager()
     : m_udmabuf(::open("/dev/udmabuf", O_RDWR | O_CLOEXEC))
     , m_udmabufDevId(getDevId(m_udmabuf))
     , m_udev(std::make_unique<Udev>())
     , m_udevMonitor(m_udev->createMonitor())
     , m_udevNotifier(std::make_unique<QSocketNotifier>(m_udevMonitor->fd(), QSocketNotifier::Read))
+    , m_explicitRenderNodes(qEnvironmentVariableIsSet("KWIN_RENDER_NODES") ? splitPathList(qEnvironmentVariable("KWIN_RENDER_NODES")) : std::optional<QStringList>())
 {
     m_udevMonitor->filterSubsystemDevType("drm");
     connect(m_udevNotifier.get(), &QSocketNotifier::activated, this, &GpuManager::handleUdevEvent);
@@ -171,6 +196,22 @@ RenderDevice *GpuManager::findCompatibleRenderDevice(drmDevicePtr device) const
 
 void GpuManager::scanForRenderDevices()
 {
+    if (m_explicitRenderNodes.has_value()) {
+        for (const QString &path : *m_explicitRenderNodes) {
+            const bool hasDevice = std::ranges::contains(m_renderDevices, path, [](const auto &device) {
+                return device->drmDevice()->path();
+            });
+            if (hasDevice) {
+                continue;
+            }
+            auto device = RenderDevice::open(path);
+            if (!device) {
+                continue;
+            }
+            addDevice(std::move(device));
+        }
+        return;
+    }
     const auto devices = m_udev->listRenderNodes();
     for (const auto &udevDevice : devices) {
         if (findDevice(udevDevice->devNum())) {
@@ -180,10 +221,7 @@ void GpuManager::scanForRenderDevices()
         if (!device) {
             continue;
         }
-        qCDebug(KWIN_CORE, "Adding render device %s", qPrintable(device->drmDevice()->path()));
-        m_renderDevices.push_back(std::move(device));
-        updateCompatibilityMap();
-        Q_EMIT renderDeviceAdded(m_renderDevices.back().get());
+        addDevice(std::move(device));
     }
 }
 
@@ -202,6 +240,12 @@ void GpuManager::handleUdevEvent()
         if (!isRenderNode) {
             continue;
         }
+        if (m_explicitRenderNodes.has_value()) {
+            const auto canonicalPath = QFileInfo(udevDevice->devNode()).canonicalFilePath();
+            if (!m_explicitRenderNodes->contains(udevDevice->devNode())) {
+                continue;
+            }
+        }
         const auto renderDevIt = std::ranges::find_if(m_renderDevices, [&udevDevice](const auto &device) {
             return udevDevice->devNum() == device->drmDevice()->deviceId();
         });
@@ -213,10 +257,7 @@ void GpuManager::handleUdevEvent()
             if (!device) {
                 continue;
             }
-            m_renderDevices.push_back(std::move(device));
-            updateCompatibilityMap();
-            qCDebug(KWIN_CORE, "Added render device %s", qPrintable(m_renderDevices.back()->drmDevice()->path()));
-            Q_EMIT renderDeviceAdded(m_renderDevices.back().get());
+            addDevice(std::move(device));
         } else if (udevDevice->action() == QLatin1StringView("remove")) {
             if (renderDevIt == m_renderDevices.end()) {
                 continue;
@@ -230,6 +271,14 @@ void GpuManager::handleUdevEvent()
     }
 }
 
+void GpuManager::addDevice(std::unique_ptr<RenderDevice> &&device)
+{
+    m_renderDevices.push_back(std::move(device));
+    updateCompatibilityMap();
+    qCDebug(KWIN_CORE, "Added render device %s", qPrintable(m_renderDevices.back()->drmDevice()->path()));
+    Q_EMIT renderDeviceAdded(m_renderDevices.back().get());
+}
+
 static uint64_t align(uint64_t size, uint64_t minimum)
 {
     if (auto remainder = size % minimum) {
diff --git a/src/core/gpumanager.h b/src/core/gpumanager.h
index b6882fc8b5d..03be9442a22 100644
--- a/src/core/gpumanager.h
+++ b/src/core/gpumanager.h
@@ -30,6 +30,7 @@ class KWIN_EXPORT GpuManager : public QObject
 public:
     static std::unique_ptr<GpuManager> s_self;
     static GpuManager *self();
+    static QStringList splitPathList(const QString &input);
 
     explicit GpuManager();
     ~GpuManager();
@@ -62,12 +63,14 @@ private:
     void handleUdevEvent();
     void updateCompatibilityMap();
     RenderDevice *findCompatibleRenderDevice(drmDevicePtr device) const;
+    void addDevice(std::unique_ptr<RenderDevice> &&device);
 
     const FileDescriptor m_udmabuf;
     const std::optional<dev_t> m_udmabufDevId;
     const std::unique_ptr<Udev> m_udev;
     const std::unique_ptr<UdevMonitor> m_udevMonitor;
     const std::unique_ptr<QSocketNotifier> m_udevNotifier;
+    const std::optional<QStringList> m_explicitRenderNodes;
     std::vector<std::unique_ptr<RenderDevice>> m_renderDevices;
     QHash<dev_t, RenderDevice *> m_compatibleDeviceMap;
 };
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.