[plasma/kwin] src: core/renderdevice: make the drm device optional

Xaver Hugl <[email protected]>
Newsgroups gmane.comp.kde.cvs
Message-ID <[email protected]>
Git commit 5299b3726b2034ce6f18ceb00d7c0c9e14b5a22a by Xaver Hugl.
Committed on 16/07/2026 at 18:38.
Pushed by zamundaaa into branch 'master'.

core/renderdevice: make the drm device optional

This will be used in the next commit

M  +1    -1    src/backends/drm/drm_egl_layer.cpp
M  +3    -2    src/compositor.cpp
M  +24   -21   src/core/gpumanager.cpp
M  +17   -5    src/core/renderdevice.cpp
M  +5    -1    src/core/renderdevice.h
M  +1    -1    src/multigpuswapchain.cpp
M  +3    -7    src/opengl/eglbackend.cpp
M  +1    -1    src/plugins/qpa/window.cpp
M  +1    -1    src/wayland/linuxdmabufv1clientbuffer.cpp
M  +2    -1    src/wayland_server.cpp

https://invent.kde.org/plasma/kwin/-/commit/5299b3726b2034ce6f18ceb00d7c0c9e14b5a22a

diff --git a/src/backends/drm/drm_egl_layer.cpp b/src/backends/drm/drm_egl_layer.cpp
index 7ef07821379..28830eb0e2f 100644
--- a/src/backends/drm/drm_egl_layer.cpp
+++ b/src/backends/drm/drm_egl_layer.cpp
@@ -136,7 +136,7 @@ bool EglGbmLayer::earlyScanoutChecks()
 bool EglGbmLayer::importScanoutBuffer(GraphicsBuffer *buffer, const std::shared_ptr<OutputFrame> &frame)
 {
     if (buffer->dmabufAttributes()->device != gpu()->drmDevice()->deviceId()
-        && (!gpu()->renderDevice() || buffer->dmabufAttributes()->device != gpu()->renderDevice()->drmDevice()->deviceId())) {
+        && (!gpu()->renderDevice() || buffer->dmabufAttributes()->device != gpu()->renderDevice()->deviceId())) {
         // Disallow direct scanout between GPUs, as
         // - there are some significant driver bugs with direct scanout from other GPUs,
         //   like https://gitlab.freedesktop.org/drm/amd/-/issues/2075
diff --git a/src/compositor.cpp b/src/compositor.cpp
index 57ad3ed9fb8..861eb44276f 100644
--- a/src/compositor.cpp
+++ b/src/compositor.cpp
@@ -148,7 +148,8 @@ static QVariantHash collectCrashInformation(const EglBackend *backend)
     QVariantHash gpuInformation;
     gpuInformation[QStringLiteral("api_type")] = QStringLiteral("OpenGL");
     gpuInformation[QStringLiteral("name")] = QString::fromUtf8(glPlatform->glRendererString());
-    if (const auto pciInfo = backend->renderDevice()->drmDevice()->pciDeviceInfo()) {
+    if (backend->renderDevice()->drmDevice() && backend->renderDevice()->drmDevice()->pciDeviceInfo()) {
+        const auto pciInfo = backend->renderDevice()->drmDevice()->pciDeviceInfo();
         gpuInformation[QStringLiteral("id")] = QString::number(pciInfo->device_id, 16);
         gpuInformation[QStringLiteral("vendor_id")] = QString::number(pciInfo->vendor_id, 16);
     }
@@ -249,7 +250,7 @@ bool Compositor::attemptOpenGLCompositing()
             qCWarning(KWIN_CORE, "Found no render device!");
             return false;
         }
-        qCDebug(KWIN_CORE, "Chose %s as the primary GPU", qPrintable(m_renderDevice->drmDevice()->path()));
+        qCDebug(KWIN_CORE, "Chose %s as the primary GPU", qPrintable(m_renderDevice->path()));
     }
     std::unique_ptr<EglBackend> backend = kwinApp()->outputBackend()->createOpenGLBackend(m_renderDevice);
     if (!backend->init()) {
diff --git a/src/core/gpumanager.cpp b/src/core/gpumanager.cpp
index ede33cfad9b..4c44e9143d6 100644
--- a/src/core/gpumanager.cpp
+++ b/src/core/gpumanager.cpp
@@ -108,9 +108,7 @@ RenderDevice *GpuManager::compatibleRenderDevice(dev_t id) const
 
 RenderDevice *GpuManager::findDevice(dev_t id) const
 {
-    const auto it = std::ranges::find_if(m_renderDevices, [id](const auto &device) {
-        return device->drmDevice()->deviceId() == id;
-    });
+    const auto it = std::ranges::find(m_renderDevices, id, &RenderDevice::deviceId);
     return it == m_renderDevices.end() ? nullptr : it->get();
 }
 
@@ -153,6 +151,9 @@ void GpuManager::updateCompatibilityMap()
 RenderDevice *GpuManager::findCompatibleRenderDevice(drmDevicePtr device) const
 {
     auto candidates = m_renderDevices | std::views::filter([device](const auto &renderDevice) {
+        if (!renderDevice->drmDevice()) {
+            return true;
+        }
         if (device->bustype == DRM_BUS_PLATFORM) {
             // devices with bus "platform" can be assumed to be compatible.
             return renderDevice->drmDevice()->busType() == DRM_BUS_PLATFORM;
@@ -180,19 +181,26 @@ RenderDevice *GpuManager::findCompatibleRenderDevice(drmDevicePtr device) const
             && left->vulkanDevice()->isSoftwareRenderer() != right->vulkanDevice()->isSoftwareRenderer()) {
             return !left->vulkanDevice()->isSoftwareRenderer();
         }
-        // if both have hardware acceleration, prefer a matching device
-        const bool sameDeviceLeft = drmDevicesEqual(device, left->drmDevice()->libdrmDevice()) == 1;
-        const bool sameDeviceRight = drmDevicesEqual(device, right->drmDevice()->libdrmDevice()) == 1;
-        if (sameDeviceLeft != sameDeviceRight) {
-            return sameDeviceLeft;
+        if (bool(left->drmDevice()) != bool(right->drmDevice())) {
+            // prefer udmabuf over software rendering on dumb buffers,
+            // since the latter is usually slower
+            return left->drmDevice() == nullptr;
         }
-        // prefer render nodes over KMS nodes
-        if (left->drmDevice()->isKMS() != right->drmDevice()->isKMS()) {
-            return !left->drmDevice()->isKMS();
+        if (left->drmDevice()) {
+            // if both have hardware acceleration, prefer a matching device
+            const bool sameDeviceLeft = drmDevicesEqual(device, left->drmDevice()->libdrmDevice()) == 1;
+            const bool sameDeviceRight = drmDevicesEqual(device, right->drmDevice()->libdrmDevice()) == 1;
+            if (sameDeviceLeft != sameDeviceRight) {
+                return sameDeviceLeft;
+            }
+            // prefer render nodes over KMS nodes
+            if (left->drmDevice()->isKMS() != right->drmDevice()->isKMS()) {
+                return !left->drmDevice()->isKMS();
+            }
         }
         // fallback: if both are equally good,
         // make sure we always get the same device
-        return left->drmDevice()->deviceId() < right->drmDevice()->deviceId();
+        return left->deviceId() < right->deviceId();
     })->get();
 }
 
@@ -200,10 +208,7 @@ 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) {
+            if (std::ranges::contains(m_renderDevices, path, &RenderDevice::path)) {
                 continue;
             }
             auto device = RenderDevice::open(path);
@@ -248,9 +253,7 @@ void GpuManager::handleUdevEvent()
                 continue;
             }
         }
-        const auto renderDevIt = std::ranges::find_if(m_renderDevices, [&udevDevice](const auto &device) {
-            return udevDevice->devNum() == device->drmDevice()->deviceId();
-        });
+        const auto renderDevIt = std::ranges::find(m_renderDevices, udevDevice->devNum(), &RenderDevice::deviceId);
         if (udevDevice->action() == QLatin1StringView("add")) {
             if (renderDevIt != m_renderDevices.end()) {
                 continue;
@@ -326,7 +329,7 @@ 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()));
+    qCDebug(KWIN_CORE, "Added render device %s", qPrintable(m_renderDevices.back()->path()));
     Q_EMIT renderDeviceAdded(m_renderDevices.back().get());
 }
 
@@ -341,7 +344,7 @@ void GpuManager::removeDevice(RenderDevice *device)
     auto ref = std::move(*it);
     m_renderDevices.erase(it);
     updateCompatibilityMap();
-    qCDebug(KWIN_CORE, "Removed render device %s", qPrintable(device->drmDevice()->path()));
+    qCDebug(KWIN_CORE, "Removed render device %s", qPrintable(device->path()));
     Q_EMIT renderDeviceRemoved(device);
 }
 
diff --git a/src/core/renderdevice.cpp b/src/core/renderdevice.cpp
index 02b1ca7e684..2d87fe63966 100644
--- a/src/core/renderdevice.cpp
+++ b/src/core/renderdevice.cpp
@@ -82,6 +82,8 @@ RenderDevice::RenderDevice(std::unique_ptr<DrmDevice> &&device, std::unique_ptr<
     : m_device(std::move(device))
     , m_display(std::move(display))
     , m_vulkanInstance(createVulkanInstance(m_vulkanContext))
+    , m_path(m_device->path())
+    , m_deviceId(m_device->deviceId())
 {
     createVulkanDevice();
     m_allImportableFormats = getImportFormats(m_display.get(), m_vulkanDevice.get());
@@ -96,6 +98,16 @@ DrmDevice *RenderDevice::drmDevice() const
     return m_device.get();
 }
 
+QString RenderDevice::path() const
+{
+    return m_path;
+}
+
+dev_t RenderDevice::deviceId() const
+{
+    return m_deviceId;
+}
+
 GraphicsBufferAllocator *RenderDevice::allocator() const
 {
     return m_device->allocator();
@@ -138,7 +150,7 @@ static constexpr std::array s_requiredVulkanExtensions = {
     VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME,
 };
 
-static std::unique_ptr<VulkanDevice> openVulkanDevice(const vk::raii::Instance &instance, DrmDevice *drm)
+static std::unique_ptr<VulkanDevice> openVulkanDevice(const vk::raii::Instance &instance, DrmDevice *drm, const QString &path)
 {
     const auto [enumerateResult, physicalDevices] = instance.enumeratePhysicalDevices();
     if (enumerateResult != vk::Result::eSuccess) {
@@ -146,7 +158,7 @@ static std::unique_ptr<VulkanDevice> openVulkanDevice(const vk::raii::Instance &
         return nullptr;
     }
     // with faux devices like vkms and vgem, we can only do software rendering
-    const bool needsSoftwareDevice = drm->busType() == DRM_BUS_FAUX;
+    const bool needsSoftwareDevice = !drm || drm->busType() == DRM_BUS_FAUX;
     for (const vk::raii::PhysicalDevice &physicalDevice : physicalDevices) {
         const auto basicProperties = physicalDevice.getProperties2();
         const bool isSoftwareDevice = basicProperties.properties.deviceType == vk::PhysicalDeviceType::eCpu;
@@ -250,10 +262,10 @@ static std::unique_ptr<VulkanDevice> openVulkanDevice(const vk::raii::Instance &
         if (ret->supportedFormats().isEmpty()) {
             continue;
         }
-        qCDebug(KWIN_VULKAN, "Found Vulkan device %s for %s", deviceName, qPrintable(drm->path()));
+        qCDebug(KWIN_VULKAN, "Found Vulkan device %s for %s", deviceName, qPrintable(path));
         return ret;
     }
-    qCDebug(KWIN_VULKAN, "No Vulkan device found for %s", qPrintable(drm->path()));
+    qCDebug(KWIN_VULKAN, "No Vulkan device found for %s", qPrintable(path));
     return nullptr;
 }
 
@@ -273,7 +285,7 @@ void RenderDevice::createVulkanDevice()
     if (!*m_vulkanInstance) {
         return;
     }
-    m_vulkanDevice = openVulkanDevice(m_vulkanInstance, m_device.get());
+    m_vulkanDevice = openVulkanDevice(m_vulkanInstance, m_device.get(), m_path);
     if (m_vulkanDevice) {
         connect(m_vulkanDevice.get(), &VulkanDevice::deviceLost, this, &RenderDevice::handleVulkanDeviceLoss);
     }
diff --git a/src/core/renderdevice.h b/src/core/renderdevice.h
index 386854180b1..78901f02a46 100644
--- a/src/core/renderdevice.h
+++ b/src/core/renderdevice.h
@@ -36,10 +36,12 @@ public:
 
     /**
      * the underlying drm device that can be used to allocate buffers for this render device
-     * This doesn't necessarily represent a render node!
+     * This doesn't necessarily represent a render node, and may be nullptr!
      */
     DrmDevice *drmDevice() const;
     GraphicsBufferAllocator *allocator() const;
+    QString path() const;
+    dev_t deviceId() const;
     EglDisplay *eglDisplay() const;
     /**
      * @returns an EGL context suitable for rendering with this render device,
@@ -84,6 +86,8 @@ private:
     FormatModifierMap m_allImportableFormats;
     std::weak_ptr<EglContext> m_eglContext;
     bool m_inReset = false;
+    const QString m_path;
+    const dev_t m_deviceId;
 };
 
 }
diff --git a/src/multigpuswapchain.cpp b/src/multigpuswapchain.cpp
index 9e19eac8965..330feeaea35 100644
--- a/src/multigpuswapchain.cpp
+++ b/src/multigpuswapchain.cpp
@@ -115,7 +115,7 @@ std::unique_ptr<MultiGpuSwapchain> MultiGpuSwapchain::create(RenderDevice *copyD
             .software = false,
             .scanout = scanout,
         };
-        auto eglSwapchain = EglSwapchain::create(copyDevice->drmDevice()->allocator(), context.get(), options);
+        auto eglSwapchain = EglSwapchain::create(copyDevice->allocator(), context.get(), options);
         if (eglSwapchain) {
             return std::make_unique<MultiGpuSwapchain>(copyDevice, targetDevice, context, std::move(eglSwapchain));
         }
diff --git a/src/opengl/eglbackend.cpp b/src/opengl/eglbackend.cpp
index 0a88b909097..81172f89139 100644
--- a/src/opengl/eglbackend.cpp
+++ b/src/opengl/eglbackend.cpp
@@ -100,10 +100,6 @@ void EglBackend::cleanup()
 
 void EglBackend::initWayland()
 {
-    if (!WaylandServer::self()) {
-        return;
-    }
-
     auto filterFormats = [this](std::optional<uint32_t> bpc, bool withExternalOnlyYUV) {
         FormatModifierMap set;
         const auto &allFormats = m_renderDevice->eglDisplay()->allSupportedDrmFormats();
@@ -155,17 +151,17 @@ void EglBackend::initWayland()
     };
 
     m_tranches.append({
-        .device = m_renderDevice->drmDevice()->deviceId(),
+        .device = m_renderDevice->deviceId(),
         .flags = LinuxDmaBufV1Feedback::TrancheFlag::Sampling,
         .formatTable = filterFormats(10, false),
     });
     m_tranches.append({
-        .device = m_renderDevice->drmDevice()->deviceId(),
+        .device = m_renderDevice->deviceId(),
         .flags = LinuxDmaBufV1Feedback::TrancheFlag::Sampling,
         .formatTable = filterFormats(8, false),
     });
     m_tranches.append({
-        .device = m_renderDevice->drmDevice()->deviceId(),
+        .device = m_renderDevice->deviceId(),
         .flags = LinuxDmaBufV1Feedback::TrancheFlag::Sampling,
         .formatTable = includeShaderConversions(filterFormats(std::nullopt, true)),
     });
diff --git a/src/plugins/qpa/window.cpp b/src/plugins/qpa/window.cpp
index 79ed0cbe32e..ccd770ea585 100644
--- a/src/plugins/qpa/window.cpp
+++ b/src/plugins/qpa/window.cpp
@@ -58,7 +58,7 @@ Swapchain *Window::swapchain(const std::shared_ptr<EglContext> &context, const F
             static ShmGraphicsBufferAllocator shmAllocator;
             allocator = &shmAllocator;
         } else {
-            allocator = Compositor::self()->backend()->renderDevice()->drmDevice()->allocator();
+            allocator = Compositor::self()->backend()->renderDevice()->allocator();
         }
 
         for (auto it = formats.begin(); it != formats.end(); it++) {
diff --git a/src/wayland/linuxdmabufv1clientbuffer.cpp b/src/wayland/linuxdmabufv1clientbuffer.cpp
index eda7b3709bc..8e5bab213f1 100644
--- a/src/wayland/linuxdmabufv1clientbuffer.cpp
+++ b/src/wayland/linuxdmabufv1clientbuffer.cpp
@@ -468,7 +468,7 @@ QList<LinuxDmaBufV1Feedback::Tranche> LinuxDmaBufV1Feedback::createScanoutTranch
         if (tranche.device != mainDevice) {
             continue;
         }
-        if (compatibleWithScanout && tranche.device != compatibleWithScanout->drmDevice()->deviceId()) {
+        if (compatibleWithScanout && tranche.device != compatibleWithScanout->deviceId()) {
             // limit scanout tranches to devices we can also sample from
             continue;
         }
diff --git a/src/wayland_server.cpp b/src/wayland_server.cpp
index f522edd4662..ce25711d535 100644
--- a/src/wayland_server.cpp
+++ b/src/wayland_server.cpp
@@ -817,7 +817,8 @@ ExtBackgroundEffectManagerV1 *WaylandServer::backgroundEffectManager() const
 
 void WaylandServer::setRenderBackend(RenderBackend *backend)
 {
-    if (backend->renderDevice()->drmDevice()->supportsSyncObjTimelines()) {
+    if (backend->renderDevice()->drmDevice()
+        && backend->renderDevice()->drmDevice()->supportsSyncObjTimelines()) {
         // ensure the DRM_IOCTL_SYNCOBJ_EVENTFD ioctl is supported
         const auto linuxVersion = linuxKernelVersion();
         if (linuxVersion.majorVersion() < 6 && linuxVersion.minorVersion() < 6) {
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.