diff --git a/modules/plasma.nix b/modules/plasma.nix index b4dfad3..8d2379b 100644 --- a/modules/plasma.nix +++ b/modules/plasma.nix @@ -16,4 +16,8 @@ krfb # Use by kdeconnect for virtualmonitorplugin "krfb-virtualmonitor" discover ]; + + nixpkgs.overlays = [ + (import ../overlays/kwin) + ]; } diff --git a/overlays/kwin/5511.patch b/overlays/kwin/5511.patch new file mode 100644 index 0000000..341dc2e --- /dev/null +++ b/overlays/kwin/5511.patch @@ -0,0 +1,2027 @@ +From e1791de663807bc93c09418481cb844c7daeb8f7 Mon Sep 17 00:00:00 2001 +From: Xaver Hugl +Date: Wed, 22 Nov 2023 19:51:24 +0100 +Subject: [PATCH 1/5] wayland: implement linux-drm-syncobj-v1 + +linux-drm-syncobj-v1 allows drivers and apps to synchronize KWin's buffer access +to their rendering, and synchronize their rendering to KWin's buffer release. This +fixes severe glitches with the proprietary NVidia driver and allows for some +performance improvements with Mesa too. + +(cherry picked from commit 32addf4d599135678a5120470bc27a881f34a3d9) +--- + src/CMakeLists.txt | 1 + + src/backends/drm/drm_egl_backend.cpp | 16 ++ + src/backends/drm/drm_egl_backend.h | 3 + + src/backends/drm/drm_gpu.cpp | 8 + + src/backends/drm/drm_gpu.h | 2 + + src/compositor_wayland.cpp | 2 +- + src/core/graphicsbuffer.cpp | 6 + + src/core/graphicsbuffer.h | 8 + + src/core/renderbackend.cpp | 11 + + src/core/renderbackend.h | 5 + + src/core/syncobjtimeline.cpp | 83 ++++++ + src/core/syncobjtimeline.h | 72 +++++ + src/opengl/eglnativefence.cpp | 5 + + src/opengl/eglnativefence.h | 1 + + .../scenes/opengl/abstract_egl_backend.cpp | 5 + + .../scenes/opengl/abstract_egl_backend.h | 2 +- + .../scenes/opengl/openglbackend.cpp | 4 + + .../scenes/opengl/openglbackend.h | 3 + + src/scene/item.h | 1 + + src/scene/itemrenderer_opengl.cpp | 20 +- + src/scene/itemrenderer_opengl.h | 9 +- + src/scene/surfaceitem.cpp | 5 + + src/scene/surfaceitem.h | 3 + + src/scene/surfaceitem_wayland.cpp | 6 + + src/scene/surfaceitem_wayland.h | 1 + + src/scene/workspacescene_opengl.cpp | 2 +- + src/wayland/CMakeLists.txt | 8 + + src/wayland/linux_drm_syncobj_v1.cpp | 190 +++++++++++++ + src/wayland/linux_drm_syncobj_v1.h | 63 +++++ + src/wayland/linux_drm_syncobj_v1_p.h | 32 +++ + .../protocols/linux-drm-syncobj-v1.xml | 261 ++++++++++++++++++ + src/wayland/surface.cpp | 20 ++ + src/wayland/surface.h | 8 + + src/wayland/surface_p.h | 9 + + src/wayland/transaction.cpp | 27 +- + src/wayland/transaction_p.h | 15 + + src/wayland_server.cpp | 17 ++ + src/wayland_server.h | 7 + + 38 files changed, 935 insertions(+), 6 deletions(-) + create mode 100644 src/core/syncobjtimeline.cpp + create mode 100644 src/core/syncobjtimeline.h + create mode 100644 src/wayland/linux_drm_syncobj_v1.cpp + create mode 100644 src/wayland/linux_drm_syncobj_v1.h + create mode 100644 src/wayland/linux_drm_syncobj_v1_p.h + create mode 100644 src/wayland/protocols/linux-drm-syncobj-v1.xml + +diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt +index 4e0db4fe9a9..0130caf3b8e 100644 +--- a/src/CMakeLists.txt ++++ b/src/CMakeLists.txt +@@ -73,6 +73,7 @@ target_sources(kwin PRIVATE + core/session_logind.cpp + core/session_noop.cpp + core/shmgraphicsbufferallocator.cpp ++ core/syncobjtimeline.cpp + cursor.cpp + cursorsource.cpp + dbusinterface.cpp +diff --git a/src/backends/drm/drm_egl_backend.cpp b/src/backends/drm/drm_egl_backend.cpp +index cfb7a482a03..943407e1d9b 100644 +--- a/src/backends/drm/drm_egl_backend.cpp ++++ b/src/backends/drm/drm_egl_backend.cpp +@@ -9,6 +9,7 @@ + #include "drm_egl_backend.h" + #include "platformsupport/scenes/opengl/basiceglsurfacetexture_wayland.h" + // kwin ++#include "core/syncobjtimeline.h" + #include "drm_abstract_output.h" + #include "drm_backend.h" + #include "drm_egl_cursor_layer.h" +@@ -199,6 +200,21 @@ DrmGpu *EglGbmBackend::gpu() const + return m_backend->primaryGpu(); + } + ++bool EglGbmBackend::supportsTimelines() const ++{ ++ return m_backend->primaryGpu()->syncObjTimelinesSupported(); ++} ++ ++std::unique_ptr EglGbmBackend::importTimeline(FileDescriptor &&syncObjFd) ++{ ++ uint32_t handle = 0; ++ if (drmSyncobjFDToHandle(m_backend->primaryGpu()->fd(), syncObjFd.get(), &handle) != 0) { ++ qCWarning(KWIN_DRM) << "importing syncobj timeline failed!" << strerror(errno); ++ return nullptr; ++ } ++ return std::make_unique(m_backend->primaryGpu()->fd(), handle); ++} ++ + } // namespace KWin + + #include "moc_drm_egl_backend.cpp" +diff --git a/src/backends/drm/drm_egl_backend.h b/src/backends/drm/drm_egl_backend.h +index 43c431f07be..4a47e900cd3 100644 +--- a/src/backends/drm/drm_egl_backend.h ++++ b/src/backends/drm/drm_egl_backend.h +@@ -63,6 +63,9 @@ public: + EglDisplay *displayForGpu(DrmGpu *gpu); + std::shared_ptr contextForGpu(DrmGpu *gpu); + ++ bool supportsTimelines() const override; ++ std::unique_ptr importTimeline(FileDescriptor &&syncObjFd) override; ++ + private: + bool initializeEgl(); + bool initRenderingContext(); +diff --git a/src/backends/drm/drm_gpu.cpp b/src/backends/drm/drm_gpu.cpp +index c5d258a2d7c..7141ed8beac 100644 +--- a/src/backends/drm/drm_gpu.cpp ++++ b/src/backends/drm/drm_gpu.cpp +@@ -76,6 +76,9 @@ DrmGpu::DrmGpu(DrmBackend *backend, const QString &devNode, int fd, dev_t device + m_addFB2ModifiersSupported = drmGetCap(fd, DRM_CAP_ADDFB2_MODIFIERS, &capability) == 0 && capability == 1; + qCDebug(KWIN_DRM) << "drmModeAddFB2WithModifiers is" << (m_addFB2ModifiersSupported ? "supported" : "not supported") << "on GPU" << m_devNode; + ++ m_supportsSyncTimelines = drmGetCap(fd, DRM_CAP_SYNCOBJ_TIMELINE, &capability) == 0 && capability == 1; ++ qCDebug(KWIN_DRM) << "sync obj timelines are" << (m_supportsSyncTimelines ? "supported" : "not supported") << "on GPU" << this; ++ + // find out what driver this kms device is using + DrmUniquePtr version(drmGetVersion(fd)); + m_isI915 = strstr(version->name, "i915"); +@@ -699,6 +702,11 @@ bool DrmGpu::asyncPageflipSupported() const + return m_asyncPageflipSupported; + } + ++bool DrmGpu::syncObjTimelinesSupported() const ++{ ++ return m_supportsSyncTimelines; ++} ++ + bool DrmGpu::isI915() const + { + return m_isI915; +diff --git a/src/backends/drm/drm_gpu.h b/src/backends/drm/drm_gpu.h +index 0cc6b2126a3..c8dbde47af7 100644 +--- a/src/backends/drm/drm_gpu.h ++++ b/src/backends/drm/drm_gpu.h +@@ -77,6 +77,7 @@ public: + bool atomicModeSetting() const; + bool addFB2ModifiersSupported() const; + bool asyncPageflipSupported() const; ++ bool syncObjTimelinesSupported() const; + bool isI915() const; + bool isNVidia() const; + bool isVmwgfx() const; +@@ -146,6 +147,7 @@ private: + bool m_isRemoved = false; + bool m_isActive = true; + bool m_forceModeset = false; ++ bool m_supportsSyncTimelines = false; + clockid_t m_presentationClock; + gbm_device *m_gbmDevice; + FileDescriptor m_gbmFd; +diff --git a/src/compositor_wayland.cpp b/src/compositor_wayland.cpp +index f63ead77964..69916e9d348 100644 +--- a/src/compositor_wayland.cpp ++++ b/src/compositor_wayland.cpp +@@ -86,7 +86,7 @@ bool WaylandCompositor::attemptOpenGLCompositing() + } + + m_scene = std::make_unique(backend.get()); +- m_cursorScene = std::make_unique(std::make_unique()); ++ m_cursorScene = std::make_unique(std::make_unique(backend->eglDisplayObject())); + m_backend = std::move(backend); + + qCDebug(KWIN_CORE) << "OpenGL compositing has been successfully initialized"; +diff --git a/src/core/graphicsbuffer.cpp b/src/core/graphicsbuffer.cpp +index f234d09fa9f..e83fcd7bf8a 100644 +--- a/src/core/graphicsbuffer.cpp ++++ b/src/core/graphicsbuffer.cpp +@@ -40,6 +40,7 @@ void GraphicsBuffer::unref() + if (m_dropped) { + delete this; + } else { ++ m_releasePoints.clear(); + Q_EMIT released(); + } + } +@@ -73,6 +74,11 @@ const ShmAttributes *GraphicsBuffer::shmAttributes() const + return nullptr; + } + ++void GraphicsBuffer::addReleasePoint(const std::shared_ptr &releasePoint) ++{ ++ m_releasePoints.push_back(releasePoint); ++} ++ + bool GraphicsBuffer::alphaChannelFromDrmFormat(uint32_t format) + { + const auto info = FormatInfo::get(format); +diff --git a/src/core/graphicsbuffer.h b/src/core/graphicsbuffer.h +index cacf49dde5a..2e046e58de0 100644 +--- a/src/core/graphicsbuffer.h ++++ b/src/core/graphicsbuffer.h +@@ -16,6 +16,8 @@ + namespace KWin + { + ++class SyncReleasePoint; ++ + struct DmaBufAttributes + { + int planeCount = 0; +@@ -87,6 +89,11 @@ public: + virtual const DmaBufAttributes *dmabufAttributes() const; + virtual const ShmAttributes *shmAttributes() const; + ++ /** ++ * the added release point will be referenced as long as this buffer is referenced ++ */ ++ void addReleasePoint(const std::shared_ptr &releasePoint); ++ + static bool alphaChannelFromDrmFormat(uint32_t format); + + Q_SIGNALS: +@@ -95,6 +102,7 @@ Q_SIGNALS: + protected: + int m_refCount = 0; + bool m_dropped = false; ++ std::vector> m_releasePoints; + }; + + /** +diff --git a/src/core/renderbackend.cpp b/src/core/renderbackend.cpp +index 98276385cec..5bbf37d9a4b 100644 +--- a/src/core/renderbackend.cpp ++++ b/src/core/renderbackend.cpp +@@ -7,6 +7,7 @@ + #include "renderbackend.h" + #include "renderloop_p.h" + #include "scene/surfaceitem.h" ++#include "syncobjtimeline.h" + + #include + +@@ -103,6 +104,16 @@ std::unique_ptr RenderBackend::createSurfaceTextureWayland(Surfa + return nullptr; + } + ++bool RenderBackend::supportsTimelines() const ++{ ++ return false; ++} ++ ++std::unique_ptr RenderBackend::importTimeline(FileDescriptor &&syncObjFd) ++{ ++ return nullptr; ++} ++ + } // namespace KWin + + #include "moc_renderbackend.cpp" +diff --git a/src/core/renderbackend.h b/src/core/renderbackend.h +index b902a28984a..682e35aa0b8 100644 +--- a/src/core/renderbackend.h ++++ b/src/core/renderbackend.h +@@ -8,6 +8,7 @@ + + #include "core/rendertarget.h" + #include "effect/globals.h" ++#include "utils/filedescriptor.h" + + #include + +@@ -26,6 +27,7 @@ class SurfacePixmapX11; + class SurfaceTexture; + class PresentationFeedback; + class RenderLoop; ++class SyncTimeline; + + class PresentationFeedback + { +@@ -88,6 +90,9 @@ public: + + virtual std::unique_ptr createSurfaceTextureX11(SurfacePixmapX11 *pixmap); + virtual std::unique_ptr createSurfaceTextureWayland(SurfacePixmap *pixmap); ++ ++ virtual bool supportsTimelines() const; ++ virtual std::unique_ptr importTimeline(FileDescriptor &&syncObjFd); + }; + + } // namespace KWin +diff --git a/src/core/syncobjtimeline.cpp b/src/core/syncobjtimeline.cpp +new file mode 100644 +index 00000000000..3e2c100370a +--- /dev/null ++++ b/src/core/syncobjtimeline.cpp +@@ -0,0 +1,83 @@ ++/* ++ SPDX-FileCopyrightText: 2024 Xaver Hugl ++ ++ SPDX-License-Identifier: GPL-2.0-or-later ++*/ ++#include "syncobjtimeline.h" ++ ++#include ++#include ++ ++namespace KWin ++{ ++ ++SyncReleasePoint::SyncReleasePoint(const std::shared_ptr &timeline, uint64_t timelinePoint) ++ : m_timeline(timeline) ++ , m_timelinePoint(timelinePoint) ++{ ++} ++ ++SyncReleasePoint::~SyncReleasePoint() ++{ ++ m_timeline->signal(m_timelinePoint); ++} ++ ++SyncTimeline *SyncReleasePoint::timeline() const ++{ ++ return m_timeline.get(); ++} ++ ++uint64_t SyncReleasePoint::timelinePoint() const ++{ ++ return m_timelinePoint; ++} ++ ++SyncTimeline::SyncTimeline(int drmFd, uint32_t handle) ++ : m_drmFd(drmFd) ++ , m_handle(handle) ++{ ++} ++ ++SyncTimeline::~SyncTimeline() ++{ ++ drmSyncobjDestroy(m_drmFd, m_handle); ++} ++ ++FileDescriptor SyncTimeline::eventFd(uint64_t timelinePoint) const ++{ ++ FileDescriptor ret{eventfd(0, EFD_CLOEXEC)}; ++ if (!ret.isValid()) { ++ return {}; ++ } ++ struct drm_syncobj_eventfd args ++ { ++ .handle = m_handle, ++ .flags = 0, ++ .point = timelinePoint, ++ .fd = ret.get(), ++ }; ++ if (drmIoctl(m_drmFd, DRM_IOCTL_SYNCOBJ_EVENTFD, &args) != 0) { ++ return {}; ++ } ++ return ret; ++} ++ ++void SyncTimeline::signal(uint64_t timelinePoint) ++{ ++ drmSyncobjTimelineSignal(m_drmFd, &m_handle, &timelinePoint, 1); ++} ++ ++SyncReleasePointHolder::SyncReleasePointHolder(FileDescriptor &&requirement, std::unordered_set> &&releasePoints) ++ : m_fence(std::move(requirement)) ++ , m_notifier(m_fence.get(), QSocketNotifier::Type::Read) ++ , m_releasePoints(std::move(releasePoints)) ++{ ++ connect(&m_notifier, &QSocketNotifier::activated, this, &SyncReleasePointHolder::signaled); ++ m_notifier.setEnabled(true); ++} ++ ++void SyncReleasePointHolder::signaled() ++{ ++ delete this; ++} ++} +diff --git a/src/core/syncobjtimeline.h b/src/core/syncobjtimeline.h +new file mode 100644 +index 00000000000..cffdb75e330 +--- /dev/null ++++ b/src/core/syncobjtimeline.h +@@ -0,0 +1,72 @@ ++/* ++ SPDX-FileCopyrightText: 2024 Xaver Hugl ++ ++ SPDX-License-Identifier: GPL-2.0-or-later ++*/ ++#pragma once ++#include "kwin_export.h" ++#include "utils/filedescriptor.h" ++ ++#include ++#include ++#include ++#include ++#include ++ ++namespace KWin ++{ ++ ++class SyncTimeline; ++ ++/** ++ * A helper to signal the release point when it goes out of scope ++ */ ++class KWIN_EXPORT SyncReleasePoint ++{ ++public: ++ explicit SyncReleasePoint(const std::shared_ptr &timeline, uint64_t timelinePoint); ++ ~SyncReleasePoint(); ++ ++ SyncTimeline *timeline() const; ++ uint64_t timelinePoint() const; ++ ++private: ++ const std::shared_ptr m_timeline; ++ const uint64_t m_timelinePoint; ++}; ++ ++class KWIN_EXPORT SyncTimeline ++{ ++public: ++ explicit SyncTimeline(int drmFd, uint32_t handle); ++ ~SyncTimeline(); ++ ++ /** ++ * @returns an event fd that gets signalled when the timeline point gets signalled ++ */ ++ FileDescriptor eventFd(uint64_t timelinePoint) const; ++ ++ void signal(uint64_t timelinePoint); ++ ++private: ++ const int32_t m_drmFd; ++ const uint32_t m_handle; ++}; ++ ++class SyncReleasePointHolder : public QObject ++{ ++ Q_OBJECT ++public: ++ /** ++ * @param requirement the filedescriptor that needs to be readable before the release points may be signalled. Once that's happened, this object deletes itself!' ++ */ ++ explicit SyncReleasePointHolder(FileDescriptor &&requirement, std::unordered_set> &&releasePoints); ++ ++private: ++ void signaled(); ++ ++ const FileDescriptor m_fence; ++ QSocketNotifier m_notifier; ++ const std::unordered_set> m_releasePoints; ++}; ++} +diff --git a/src/opengl/eglnativefence.cpp b/src/opengl/eglnativefence.cpp +index e68098bc768..13c42ade543 100644 +--- a/src/opengl/eglnativefence.cpp ++++ b/src/opengl/eglnativefence.cpp +@@ -51,6 +51,11 @@ const FileDescriptor &EGLNativeFence::fileDescriptor() const + return m_fileDescriptor; + } + ++FileDescriptor &&EGLNativeFence::fileDescriptor() ++{ ++ return std::move(m_fileDescriptor); ++} ++ + bool EGLNativeFence::waitSync() const + { + return eglWaitSync(m_display->handle(), m_sync, 0) == EGL_TRUE; +diff --git a/src/opengl/eglnativefence.h b/src/opengl/eglnativefence.h +index d5f6bdbbba2..05c248ff8e0 100644 +--- a/src/opengl/eglnativefence.h ++++ b/src/opengl/eglnativefence.h +@@ -27,6 +27,7 @@ public: + + bool isValid() const; + const FileDescriptor &fileDescriptor() const; ++ FileDescriptor &&fileDescriptor(); + bool waitSync() const; + + static EGLNativeFence importFence(EglDisplay *display, FileDescriptor &&fd); +diff --git a/src/platformsupport/scenes/opengl/abstract_egl_backend.cpp b/src/platformsupport/scenes/opengl/abstract_egl_backend.cpp +index 4e227aef462..4ad2064148b 100644 +--- a/src/platformsupport/scenes/opengl/abstract_egl_backend.cpp ++++ b/src/platformsupport/scenes/opengl/abstract_egl_backend.cpp +@@ -13,6 +13,7 @@ + #include "opengl/egl_context_attribute_builder.h" + #include "utils/common.h" + #include "wayland/drmclientbuffer.h" ++#include "wayland/linux_drm_syncobj_v1.h" + #include "wayland_server.h" + // kwin libs + #include "opengl/eglimagetexture.h" +@@ -217,9 +218,13 @@ void AbstractEglBackend::initWayland() + .formatTable = includeShaderConversions(filterFormats({}, true)), + }); + ++ waylandServer()->setRenderBackend(this); + LinuxDmaBufV1ClientBufferIntegration *dmabuf = waylandServer()->linuxDmabuf(); + dmabuf->setRenderBackend(this); + dmabuf->setSupportedFormatsWithModifiers(m_tranches); ++ if (auto syncObj = waylandServer()->linuxSyncObj()) { ++ syncObj->setRenderBackend(this); ++ } + } + + void AbstractEglBackend::initClientExtensions() +diff --git a/src/platformsupport/scenes/opengl/abstract_egl_backend.h b/src/platformsupport/scenes/opengl/abstract_egl_backend.h +index 0abd331ed6b..1f77898db50 100644 +--- a/src/platformsupport/scenes/opengl/abstract_egl_backend.h ++++ b/src/platformsupport/scenes/opengl/abstract_egl_backend.h +@@ -34,7 +34,7 @@ public: + + EGLSurface surface() const; + EGLConfig config() const; +- EglDisplay *eglDisplayObject() const; ++ EglDisplay *eglDisplayObject() const override; + EglContext *contextObject(); + + bool testImportBuffer(GraphicsBuffer *buffer) override; +diff --git a/src/platformsupport/scenes/opengl/openglbackend.cpp b/src/platformsupport/scenes/opengl/openglbackend.cpp +index 92ca501d96e..0bb6de64021 100644 +--- a/src/platformsupport/scenes/opengl/openglbackend.cpp ++++ b/src/platformsupport/scenes/opengl/openglbackend.cpp +@@ -93,6 +93,10 @@ bool OpenGLBackend::checkGraphicsReset() + return true; + } + ++EglDisplay *OpenGLBackend::eglDisplayObject() const ++{ ++ return nullptr; ++} + } + + #include "moc_openglbackend.cpp" +diff --git a/src/platformsupport/scenes/opengl/openglbackend.h b/src/platformsupport/scenes/opengl/openglbackend.h +index 3318f4da187..e0749561826 100644 +--- a/src/platformsupport/scenes/opengl/openglbackend.h ++++ b/src/platformsupport/scenes/opengl/openglbackend.h +@@ -19,6 +19,7 @@ namespace KWin + class Output; + class OpenGLBackend; + class GLTexture; ++class EglDisplay; + + /** + * @brief The OpenGLBackend creates and holds the OpenGL context and is responsible for Texture from Pixmap. +@@ -97,6 +98,8 @@ public: + + virtual std::pair, ColorDescription> textureForOutput(Output *output) const; + ++ virtual EglDisplay *eglDisplayObject() const; ++ + protected: + /** + * @brief Sets the backend initialization to failed. +diff --git a/src/scene/item.h b/src/scene/item.h +index 5e9e4f1c92a..b77fc91c64b 100644 +--- a/src/scene/item.h ++++ b/src/scene/item.h +@@ -22,6 +22,7 @@ namespace KWin + + class SceneDelegate; + class Scene; ++class SyncReleasePoint; + + /** + * The Item class is the base class for items in the scene. +diff --git a/src/scene/itemrenderer_opengl.cpp b/src/scene/itemrenderer_opengl.cpp +index 44ec0918e33..3fee3863f2c 100644 +--- a/src/scene/itemrenderer_opengl.cpp ++++ b/src/scene/itemrenderer_opengl.cpp +@@ -8,7 +8,9 @@ + #include "core/pixelgrid.h" + #include "core/rendertarget.h" + #include "core/renderviewport.h" ++#include "core/syncobjtimeline.h" + #include "effect/effect.h" ++#include "opengl/eglnativefence.h" + #include "platformsupport/scenes/opengl/openglsurfacetexture.h" + #include "scene/decorationitem.h" + #include "scene/imageitem.h" +@@ -20,7 +22,8 @@ + namespace KWin + { + +-ItemRendererOpenGL::ItemRendererOpenGL() ++ItemRendererOpenGL::ItemRendererOpenGL(EglDisplay *eglDisplay) ++ : m_eglDisplay(eglDisplay) + { + const QString visualizeOptionsString = qEnvironmentVariable("KWIN_SCENE_VISUALIZE"); + if (!visualizeOptionsString.isEmpty()) { +@@ -46,6 +49,14 @@ void ItemRendererOpenGL::endFrame() + { + GLVertexBuffer::streamingBuffer()->endOfFrame(); + GLFramebuffer::popFramebuffer(); ++ ++ if (m_eglDisplay) { ++ EGLNativeFence fence(m_eglDisplay); ++ if (fence.isValid()) { ++ new SyncReleasePointHolder(std::move(fence.fileDescriptor()), std::move(m_releasePoints)); ++ } ++ } ++ m_releasePoints.clear(); + } + + QVector4D ItemRendererOpenGL::modulate(float opacity, float brightness) const +@@ -173,6 +184,7 @@ void ItemRendererOpenGL::createRenderNode(Item *item, RenderContext *context) + .coordinateType = UnnormalizedCoordinates, + .scale = scale, + .colorDescription = item->colorDescription(), ++ .bufferReleasePoint = nullptr, + }); + } + } else if (auto decorationItem = qobject_cast(item)) { +@@ -187,6 +199,7 @@ void ItemRendererOpenGL::createRenderNode(Item *item, RenderContext *context) + .coordinateType = UnnormalizedCoordinates, + .scale = scale, + .colorDescription = item->colorDescription(), ++ .bufferReleasePoint = nullptr, + }); + } + } else if (auto surfaceItem = qobject_cast(item)) { +@@ -202,6 +215,7 @@ void ItemRendererOpenGL::createRenderNode(Item *item, RenderContext *context) + .coordinateType = NormalizedCoordinates, + .scale = scale, + .colorDescription = item->colorDescription(), ++ .bufferReleasePoint = surfaceItem->bufferReleasePoint(), + }); + } + } +@@ -216,6 +230,7 @@ void ItemRendererOpenGL::createRenderNode(Item *item, RenderContext *context) + .coordinateType = NormalizedCoordinates, + .scale = scale, + .colorDescription = item->colorDescription(), ++ .bufferReleasePoint = nullptr, + }); + } + } +@@ -407,6 +422,9 @@ void ItemRendererOpenGL::renderItem(const RenderTarget &renderTarget, const Rend + contents.planes[plane]->unbind(); + } + } ++ if (renderNode.bufferReleasePoint) { ++ m_releasePoints.insert(renderNode.bufferReleasePoint); ++ } + } + if (shader) { + ShaderManager::instance()->popShader(); +diff --git a/src/scene/itemrenderer_opengl.h b/src/scene/itemrenderer_opengl.h +index c35839335c6..11ab8cea386 100644 +--- a/src/scene/itemrenderer_opengl.h ++++ b/src/scene/itemrenderer_opengl.h +@@ -10,9 +10,13 @@ + #include "platformsupport/scenes/opengl/openglsurfacetexture.h" + #include "scene/itemrenderer.h" + ++#include ++ + namespace KWin + { + ++class EglDisplay; ++ + class KWIN_EXPORT ItemRendererOpenGL : public ItemRenderer + { + public: +@@ -28,6 +32,7 @@ public: + TextureCoordinateType coordinateType = UnnormalizedCoordinates; + qreal scale = 1.0; + ColorDescription colorDescription; ++ std::shared_ptr bufferReleasePoint; + }; + + struct RenderContext +@@ -41,7 +46,7 @@ public: + const qreal renderTargetScale; + }; + +- ItemRendererOpenGL(); ++ ItemRendererOpenGL(EglDisplay *eglDisplay); + + void beginFrame(const RenderTarget &renderTarget, const RenderViewport &viewport) override; + void endFrame() override; +@@ -58,6 +63,8 @@ private: + void visualizeFractional(const RenderViewport &viewport, const QRegion ®ion, const RenderContext &renderContext); + + bool m_blendingEnabled = false; ++ EglDisplay *const m_eglDisplay; ++ std::unordered_set> m_releasePoints; + + struct + { +diff --git a/src/scene/surfaceitem.cpp b/src/scene/surfaceitem.cpp +index 4404dbc3c6c..f04053f3566 100644 +--- a/src/scene/surfaceitem.cpp ++++ b/src/scene/surfaceitem.cpp +@@ -270,6 +270,11 @@ std::chrono::nanoseconds SurfaceItem::frameTimeEstimation() const + } + } + ++std::shared_ptr SurfaceItem::bufferReleasePoint() const ++{ ++ return m_bufferReleasePoint; ++} ++ + SurfaceTexture::~SurfaceTexture() + { + } +diff --git a/src/scene/surfaceitem.h b/src/scene/surfaceitem.h +index 5dfcfd0ad16..f6c8ca5d56d 100644 +--- a/src/scene/surfaceitem.h ++++ b/src/scene/surfaceitem.h +@@ -38,6 +38,8 @@ public: + QSize bufferSize() const; + void setBufferSize(const QSize &size); + ++ std::shared_ptr bufferReleasePoint() const; ++ + QRegion mapFromBuffer(const QRegion ®ion) const; + + void addDamage(const QRegion ®ion); +@@ -82,6 +84,7 @@ protected: + std::deque m_lastDamageTimeDiffs; + std::optional m_lastDamage; + std::chrono::nanoseconds m_frameTimeEstimation = std::chrono::days(1000); ++ std::shared_ptr m_bufferReleasePoint; + }; + + class KWIN_EXPORT SurfaceTexture +diff --git a/src/scene/surfaceitem_wayland.cpp b/src/scene/surfaceitem_wayland.cpp +index 82863c3a456..6f16c47142f 100644 +--- a/src/scene/surfaceitem_wayland.cpp ++++ b/src/scene/surfaceitem_wayland.cpp +@@ -41,6 +41,7 @@ SurfaceItemWayland::SurfaceItemWayland(SurfaceInterface *surface, Scene *scene, + this, &SurfaceItemWayland::handleColorDescriptionChanged); + connect(surface, &SurfaceInterface::presentationModeHintChanged, + this, &SurfaceItemWayland::handlePresentationModeHintChanged); ++ connect(surface, &SurfaceInterface::bufferReleasePointChanged, this, &SurfaceItemWayland::handleReleasePointChanged); + + SubSurfaceInterface *subsurface = surface->subSurface(); + if (subsurface) { +@@ -184,6 +185,11 @@ void SurfaceItemWayland::handlePresentationModeHintChanged() + setPresentationHint(m_surface->presentationModeHint()); + } + ++void SurfaceItemWayland::handleReleasePointChanged() ++{ ++ m_bufferReleasePoint = m_surface->bufferReleasePoint(); ++} ++ + SurfacePixmapWayland::SurfacePixmapWayland(SurfaceItemWayland *item, QObject *parent) + : SurfacePixmap(Compositor::self()->backend()->createSurfaceTextureWayland(this), parent) + , m_item(item) +diff --git a/src/scene/surfaceitem_wayland.h b/src/scene/surfaceitem_wayland.h +index ab31192cb9a..c428f1ea417 100644 +--- a/src/scene/surfaceitem_wayland.h ++++ b/src/scene/surfaceitem_wayland.h +@@ -48,6 +48,7 @@ private Q_SLOTS: + void handleSubSurfaceMappedChanged(); + void handleColorDescriptionChanged(); + void handlePresentationModeHintChanged(); ++ void handleReleasePointChanged(); + + protected: + std::unique_ptr createPixmap() override; +diff --git a/src/scene/workspacescene_opengl.cpp b/src/scene/workspacescene_opengl.cpp +index 3a024f56bd9..aabee9be151 100644 +--- a/src/scene/workspacescene_opengl.cpp ++++ b/src/scene/workspacescene_opengl.cpp +@@ -40,7 +40,7 @@ namespace KWin + ***********************************************/ + + WorkspaceSceneOpenGL::WorkspaceSceneOpenGL(OpenGLBackend *backend) +- : WorkspaceScene(std::make_unique()) ++ : WorkspaceScene(std::make_unique(backend->eglDisplayObject())) + , m_backend(backend) + { + } +diff --git a/src/wayland/CMakeLists.txt b/src/wayland/CMakeLists.txt +index 41ee1c5581a..9d2add111fc 100644 +--- a/src/wayland/CMakeLists.txt ++++ b/src/wayland/CMakeLists.txt +@@ -231,6 +231,10 @@ ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml + PROTOCOL ${PROJECT_SOURCE_DIR}/src/wayland/protocols/xx-color-management-v2.xml + BASENAME xx-color-management-v2 + ) ++ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml ++ PROTOCOL ${PROJECT_SOURCE_DIR}/src/wayland/protocols/linux-drm-syncobj-v1.xml ++ BASENAME linux-drm-syncobj-v1 ++) + + target_sources(kwin PRIVATE + abstract_data_source.cpp +@@ -265,6 +269,7 @@ target_sources(kwin PRIVATE + keyboard_shortcuts_inhibit_v1.cpp + keystate.cpp + layershell_v1.cpp ++ linux_drm_syncobj_v1.cpp + linuxdmabufv1clientbuffer.cpp + lockscreen_overlay_v1.cpp + output.cpp +@@ -344,6 +349,7 @@ install(FILES + keyboard_shortcuts_inhibit_v1.h + keystate.h + layershell_v1.h ++ linux_drm_syncobj_v1.h + lockscreen_overlay_v1.h + output.h + output_order_v1.h +@@ -390,10 +396,12 @@ install(FILES + + ${CMAKE_CURRENT_BINARY_DIR}/qwayland-server-content-type-v1.h + ${CMAKE_CURRENT_BINARY_DIR}/qwayland-server-frog-color-management-v1.h ++ ${CMAKE_CURRENT_BINARY_DIR}/qwayland-server-linux-drm-syncobj-v1.h + ${CMAKE_CURRENT_BINARY_DIR}/qwayland-server-presentation-time.h + ${CMAKE_CURRENT_BINARY_DIR}/qwayland-server-xx-color-management-v2.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-content-type-v1-server-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-frog-color-management-v1-server-protocol.h ++ ${CMAKE_CURRENT_BINARY_DIR}/wayland-linux-drm-syncobj-v1-server-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-presentation-time-server-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-xx-color-management-v2-server-protocol.h + +diff --git a/src/wayland/linux_drm_syncobj_v1.cpp b/src/wayland/linux_drm_syncobj_v1.cpp +new file mode 100644 +index 00000000000..589ccd78c3b +--- /dev/null ++++ b/src/wayland/linux_drm_syncobj_v1.cpp +@@ -0,0 +1,190 @@ ++/* ++ KWin - the KDE window manager ++ This file is part of the KDE project. ++ ++ SPDX-FileCopyrightText: 2024 Xaver Hugl ++ ++ SPDX-License-Identifier: GPL-2.0-or-later ++*/ ++#include "linux_drm_syncobj_v1.h" ++#include "core/syncobjtimeline.h" ++#include "display.h" ++#include "linux_drm_syncobj_v1_p.h" ++#include "surface.h" ++#include "surface_p.h" ++#include "transaction.h" ++#include "utils/resource.h" ++ ++#include ++ ++namespace KWin ++{ ++ ++static constexpr uint32_t s_version = 1; ++ ++LinuxDrmSyncObjV1Interface::LinuxDrmSyncObjV1Interface(Display *display, QObject *parent) ++ : QObject(parent) ++ , QtWaylandServer::wp_linux_drm_syncobj_manager_v1(*display, s_version) ++{ ++} ++ ++void LinuxDrmSyncObjV1Interface::wp_linux_drm_syncobj_manager_v1_get_surface(Resource *resource, uint32_t id, wl_resource *surface) ++{ ++ SurfaceInterface *surf = SurfaceInterface::get(surface); ++ SurfaceInterfacePrivate *priv = SurfaceInterfacePrivate::get(surf); ++ if (priv->syncObjV1) { ++ wl_resource_post_error(resource->handle, error_surface_exists, "surface already exists"); ++ return; ++ } ++ priv->syncObjV1 = new LinuxDrmSyncObjSurfaceV1(surf, resource->client(), id); ++} ++ ++void LinuxDrmSyncObjV1Interface::wp_linux_drm_syncobj_manager_v1_import_timeline(Resource *resource, uint32_t id, int32_t rawFd) ++{ ++ FileDescriptor fd(rawFd); ++ // TODO add a GPU abstraction, instead of using the render backend ++ if (!m_renderBackend || isGlobalRemoved()) { ++ // to not crash the client, create an inert timeline ++ new LinuxDrmSyncObjTimelineV1(resource->client(), id, nullptr); ++ return; ++ } ++ auto timeline = m_renderBackend->importTimeline(std::move(fd)); ++ if (!timeline) { ++ wl_resource_post_error(resource->handle, WP_LINUX_DRM_SYNCOBJ_MANAGER_V1_ERROR_INVALID_TIMELINE, "Importing timeline failed"); ++ return; ++ } ++ new LinuxDrmSyncObjTimelineV1(resource->client(), id, std::move(timeline)); ++} ++ ++void LinuxDrmSyncObjV1Interface::setRenderBackend(RenderBackend *backend) ++{ ++ m_renderBackend = backend; ++} ++ ++void LinuxDrmSyncObjV1Interface::wp_linux_drm_syncobj_manager_v1_destroy(Resource *resource) ++{ ++ wl_resource_destroy(resource->handle); ++} ++ ++void LinuxDrmSyncObjV1Interface::remove() ++{ ++ QtWaylandServer::wp_linux_drm_syncobj_manager_v1::globalRemove(); ++} ++ ++void LinuxDrmSyncObjV1Interface::wp_linux_drm_syncobj_manager_v1_destroy_global() ++{ ++ delete this; ++} ++ ++LinuxDrmSyncObjTimelineV1::LinuxDrmSyncObjTimelineV1(wl_client *client, uint32_t id, std::unique_ptr &&timeline) ++ : QtWaylandServer::wp_linux_drm_syncobj_timeline_v1(client, id, s_version) ++ , m_timeline(std::move(timeline)) ++{ ++} ++ ++LinuxDrmSyncObjTimelineV1::~LinuxDrmSyncObjTimelineV1() ++{ ++} ++ ++void LinuxDrmSyncObjTimelineV1::wp_linux_drm_syncobj_timeline_v1_destroy_resource(Resource *resource) ++{ ++ delete this; ++} ++ ++void LinuxDrmSyncObjTimelineV1::wp_linux_drm_syncobj_timeline_v1_destroy(Resource *resource) ++{ ++ wl_resource_destroy(resource->handle); ++} ++ ++std::shared_ptr LinuxDrmSyncObjTimelineV1::timeline() const ++{ ++ return m_timeline; ++} ++ ++LinuxDrmSyncObjSurfaceV1::LinuxDrmSyncObjSurfaceV1(SurfaceInterface *surface, wl_client *client, uint32_t id) ++ : QtWaylandServer::wp_linux_drm_syncobj_surface_v1(client, id, s_version) ++ , m_surface(surface) ++{ ++} ++ ++LinuxDrmSyncObjSurfaceV1::~LinuxDrmSyncObjSurfaceV1() ++{ ++ if (m_surface) { ++ SurfaceInterfacePrivate::get(m_surface)->syncObjV1 = nullptr; ++ } ++} ++ ++void LinuxDrmSyncObjSurfaceV1::wp_linux_drm_syncobj_surface_v1_set_acquire_point(Resource *resource, wl_resource *timeline_resource, uint32_t point_hi, uint32_t point_lo) ++{ ++ if (!m_surface) { ++ wl_resource_post_error(resource->handle, WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_SURFACE, "Surface got destroyed already"); ++ return; ++ } ++ const auto timeline = resource_cast(timeline_resource); ++ if (!timeline->timeline()) { ++ // in the normal case this should never happen, but if it does, ++ // there's nothing we can do about it without killing the client ++ return; ++ } ++ const uint64_t point = (uint64_t(point_hi) << 32) | point_lo; ++ const auto priv = SurfaceInterfacePrivate::get(m_surface); ++ priv->pending->acquirePoint.timeline = timeline->timeline(); ++ priv->pending->acquirePoint.point = point; ++} ++ ++void LinuxDrmSyncObjSurfaceV1::wp_linux_drm_syncobj_surface_v1_set_release_point(Resource *resource, wl_resource *timeline_resource, uint32_t point_hi, uint32_t point_lo) ++{ ++ if (!m_surface) { ++ wl_resource_post_error(resource->handle, WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_SURFACE, "Surface got destroyed already"); ++ return; ++ } ++ const auto timeline = resource_cast(timeline_resource); ++ if (!timeline->timeline()) { ++ // in the normal case this should never happen, but if it does, ++ // there's nothing we can do about it without killing the client ++ return; ++ } ++ const uint64_t point = (uint64_t(point_hi) << 32) | point_lo; ++ SurfaceInterfacePrivate::get(m_surface)->pending->releasePoint = std::make_unique(timeline->timeline(), point); ++} ++ ++void LinuxDrmSyncObjSurfaceV1::wp_linux_drm_syncobj_surface_v1_destroy_resource(Resource *resource) ++{ ++ delete this; ++} ++ ++void LinuxDrmSyncObjSurfaceV1::wp_linux_drm_syncobj_surface_v1_destroy(Resource *resource) ++{ ++ wl_resource_destroy(resource->handle); ++} ++ ++bool LinuxDrmSyncObjSurfaceV1::maybeEmitProtocolErrors() ++{ ++ const auto priv = SurfaceInterfacePrivate::get(m_surface); ++ if (!priv->pending->bufferIsSet && !priv->pending->acquirePoint.timeline && !priv->pending->releasePoint) { ++ return false; ++ } ++ if (!priv->pending->acquirePoint.timeline) { ++ wl_resource_post_error(resource()->handle, error_no_acquire_point, "explicit sync is used, but no acquire point is set"); ++ return true; ++ } ++ if (!priv->pending->releasePoint) { ++ wl_resource_post_error(resource()->handle, error_no_release_point, "explicit sync is used, but no release point is set"); ++ return true; ++ } ++ if (priv->pending->acquirePoint.timeline.get() == priv->pending->releasePoint->timeline() ++ && priv->pending->acquirePoint.point >= priv->pending->releasePoint->timelinePoint()) { ++ wl_resource_post_error(resource()->handle, error_conflicting_points, "acquire and release points are on the same timeline and acquire >= release"); ++ return true; ++ } ++ if (!priv->pending->buffer) { ++ wl_resource_post_error(resource()->handle, error_no_buffer, "explicit sync is used, but no buffer is attached"); ++ return true; ++ } ++ if (!priv->pending->buffer->dmabufAttributes()) { ++ wl_resource_post_error(resource()->handle, error_unsupported_buffer, "only linux dmabuf buffers are allowed to use explicit sync"); ++ return true; ++ } ++ return false; ++} ++} +diff --git a/src/wayland/linux_drm_syncobj_v1.h b/src/wayland/linux_drm_syncobj_v1.h +new file mode 100644 +index 00000000000..ff6ddbe0340 +--- /dev/null ++++ b/src/wayland/linux_drm_syncobj_v1.h +@@ -0,0 +1,63 @@ ++/* ++ KWin - the KDE window manager ++ This file is part of the KDE project. ++ ++ SPDX-FileCopyrightText: 2024 Xaver Hugl ++ ++ SPDX-License-Identifier: GPL-2.0-or-later ++*/ ++#pragma once ++ ++#include "kwin_export.h" ++#include "qwayland-server-linux-drm-syncobj-v1.h" ++ ++#include ++#include ++ ++namespace KWin ++{ ++ ++class Display; ++class SurfaceInterface; ++class RenderBackend; ++class SyncTimeline; ++ ++class KWIN_EXPORT LinuxDrmSyncObjV1Interface : public QObject, private QtWaylandServer::wp_linux_drm_syncobj_manager_v1 ++{ ++ Q_OBJECT ++public: ++ explicit LinuxDrmSyncObjV1Interface(Display *display, QObject *parent = nullptr); ++ ++ void setRenderBackend(RenderBackend *backend); ++ void remove(); ++ ++private: ++ void wp_linux_drm_syncobj_manager_v1_get_surface(Resource *resource, uint32_t id, wl_resource *surface) override; ++ void wp_linux_drm_syncobj_manager_v1_import_timeline(Resource *resource, uint32_t id, int32_t fd) override; ++ void wp_linux_drm_syncobj_manager_v1_destroy(Resource *resource) override; ++ void wp_linux_drm_syncobj_manager_v1_destroy_global() override; ++ ++ QPointer m_renderBackend; ++}; ++ ++class LinuxDrmSyncObjSurfaceV1 : private QtWaylandServer::wp_linux_drm_syncobj_surface_v1 ++{ ++public: ++ explicit LinuxDrmSyncObjSurfaceV1(SurfaceInterface *surface, wl_client *client, uint32_t id); ++ ~LinuxDrmSyncObjSurfaceV1() override; ++ ++ /** ++ * checks for protocol errors that may need to be sent at commit time ++ * @returns if any protocol errors were actually emitted ++ */ ++ bool maybeEmitProtocolErrors(); ++ ++private: ++ void wp_linux_drm_syncobj_surface_v1_set_acquire_point(Resource *resource, wl_resource *timeline, uint32_t point_hi, uint32_t point_lo) override; ++ void wp_linux_drm_syncobj_surface_v1_set_release_point(Resource *resource, wl_resource *timeline, uint32_t point_hi, uint32_t point_lo) override; ++ void wp_linux_drm_syncobj_surface_v1_destroy_resource(Resource *resource) override; ++ void wp_linux_drm_syncobj_surface_v1_destroy(Resource *resource) override; ++ ++ const QPointer m_surface; ++}; ++} +diff --git a/src/wayland/linux_drm_syncobj_v1_p.h b/src/wayland/linux_drm_syncobj_v1_p.h +new file mode 100644 +index 00000000000..6efbc12ee30 +--- /dev/null ++++ b/src/wayland/linux_drm_syncobj_v1_p.h +@@ -0,0 +1,32 @@ ++/* ++ KWin - the KDE window manager ++ This file is part of the KDE project. ++ ++ SPDX-FileCopyrightText: 2024 Xaver Hugl ++ ++ SPDX-License-Identifier: GPL-2.0-or-later ++*/ ++#pragma once ++#include "linux_drm_syncobj_v1.h" ++ ++namespace KWin ++{ ++ ++class LinuxDrmSyncObjTimelineV1 : public QtWaylandServer::wp_linux_drm_syncobj_timeline_v1 ++{ ++public: ++ explicit LinuxDrmSyncObjTimelineV1(wl_client *client, uint32_t id, std::unique_ptr &&timeline); ++ ~LinuxDrmSyncObjTimelineV1() override; ++ ++ /** ++ * May return nullptr if the timeline resource is inert ++ */ ++ std::shared_ptr timeline() const; ++ ++private: ++ void wp_linux_drm_syncobj_timeline_v1_destroy_resource(Resource *resource) override; ++ void wp_linux_drm_syncobj_timeline_v1_destroy(Resource *resource) override; ++ ++ const std::shared_ptr m_timeline; ++}; ++} +diff --git a/src/wayland/protocols/linux-drm-syncobj-v1.xml b/src/wayland/protocols/linux-drm-syncobj-v1.xml +new file mode 100644 +index 00000000000..2c491eaf43a +--- /dev/null ++++ b/src/wayland/protocols/linux-drm-syncobj-v1.xml +@@ -0,0 +1,261 @@ ++ ++ ++ ++ Copyright 2016 The Chromium Authors. ++ Copyright 2017 Intel Corporation ++ Copyright 2018 Collabora, Ltd ++ Copyright 2021 Simon Ser ++ ++ Permission is hereby granted, free of charge, to any person obtaining a ++ copy of this software and associated documentation files (the "Software"), ++ to deal in the Software without restriction, including without limitation ++ the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ and/or sell copies of the Software, and to permit persons to whom the ++ Software is furnished to do so, subject to the following conditions: ++ ++ The above copyright notice and this permission notice (including the next ++ paragraph) shall be included in all copies or substantial portions of the ++ Software. ++ ++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++ DEALINGS IN THE SOFTWARE. ++ ++ ++ ++ This protocol allows clients to request explicit synchronization for ++ buffers. It is tied to the Linux DRM synchronization object framework. ++ ++ Synchronization refers to co-ordination of pipelined operations performed ++ on buffers. Most GPU clients will schedule an asynchronous operation to ++ render to the buffer, then immediately send the buffer to the compositor ++ to be attached to a surface. ++ ++ With implicit synchronization, ensuring that the rendering operation is ++ complete before the compositor displays the buffer is an implementation ++ detail handled by either the kernel or userspace graphics driver. ++ ++ By contrast, with explicit synchronization, DRM synchronization object ++ timeline points mark when the asynchronous operations are complete. When ++ submitting a buffer, the client provides a timeline point which will be ++ waited on before the compositor accesses the buffer, and another timeline ++ point that the compositor will signal when it no longer needs to access the ++ buffer contents for the purposes of the surface commit. ++ ++ Linux DRM synchronization objects are documented at: ++ https://dri.freedesktop.org/docs/drm/gpu/drm-mm.html#drm-sync-objects ++ ++ Warning! The protocol described in this file is currently in the testing ++ phase. Backward compatible changes may be added together with the ++ corresponding interface version bump. Backward incompatible changes can ++ only be done by creating a new major version of the extension. ++ ++ ++ ++ ++ This global is a factory interface, allowing clients to request ++ explicit synchronization for buffers on a per-surface basis. ++ ++ See wp_linux_drm_syncobj_surface_v1 for more information. ++ ++ ++ ++ ++ Destroy this explicit synchronization factory object. Other objects ++ shall not be affected by this request. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ Instantiate an interface extension for the given wl_surface to provide ++ explicit synchronization. ++ ++ If the given wl_surface already has an explicit synchronization object ++ associated, the surface_exists protocol error is raised. ++ ++ Graphics APIs, like EGL or Vulkan, that manage the buffer queue and ++ commits of a wl_surface themselves, are likely to be using this ++ extension internally. If a client is using such an API for a ++ wl_surface, it should not directly use this extension on that surface, ++ to avoid raising a surface_exists protocol error. ++ ++ ++ ++ ++ ++ ++ ++ Import a DRM synchronization object timeline. ++ ++ If the FD cannot be imported, the invalid_timeline error is raised. ++ ++ ++ ++ ++ ++ ++ ++ ++ This object represents an explicit synchronization object timeline ++ imported by the client to the compositor. ++ ++ ++ ++ ++ Destroy the synchronization object timeline. Other objects are not ++ affected by this request, in particular timeline points set by ++ set_acquire_point and set_release_point are not unset. ++ ++ ++ ++ ++ ++ ++ This object is an add-on interface for wl_surface to enable explicit ++ synchronization. ++ ++ Each surface can be associated with only one object of this interface at ++ any time. ++ ++ Explicit synchronization is guaranteed to be supported for buffers ++ created with any version of the linux-dmabuf protocol. Compositors are ++ free to support explicit synchronization for additional buffer types. ++ If at surface commit time the attached buffer does not support explicit ++ synchronization, an unsupported_buffer error is raised. ++ ++ As long as the wp_linux_drm_syncobj_surface_v1 object is alive, the ++ compositor may ignore implicit synchronization for buffers attached and ++ committed to the wl_surface. The delivery of wl_buffer.release events ++ for buffers attached to the surface becomes undefined. ++ ++ Clients must set both acquire and release points if and only if a ++ non-null buffer is attached in the same surface commit. See the ++ no_buffer, no_acquire_point and no_release_point protocol errors. ++ ++ If at surface commit time the acquire and release DRM syncobj timelines ++ are identical, the acquire point value must be strictly less than the ++ release point value, or else the conflicting_points protocol error is ++ raised. ++ ++ ++ ++ ++ Destroy this surface synchronization object. ++ ++ Any timeline point set by this object with set_acquire_point or ++ set_release_point since the last commit may be discarded by the ++ compositor. Any timeline point set by this object before the last ++ commit will not be affected. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ Set the timeline point that must be signalled before the compositor may ++ sample from the buffer attached with wl_surface.attach. ++ ++ The 64-bit unsigned value combined from point_hi and point_lo is the ++ point value. ++ ++ The acquire point is double-buffered state, and will be applied on the ++ next wl_surface.commit request for the associated surface. Thus, it ++ applies only to the buffer that is attached to the surface at commit ++ time. ++ ++ If an acquire point has already been attached during the same commit ++ cycle, the new point replaces the old one. ++ ++ If the associated wl_surface was destroyed, a no_surface error is ++ raised. ++ ++ If at surface commit time there is a pending acquire timeline point set ++ but no pending buffer attached, a no_buffer error is raised. If at ++ surface commit time there is a pending buffer attached but no pending ++ acquire timeline point set, the no_acquire_point protocol error is ++ raised. ++ ++ ++ ++ ++ ++ ++ ++ ++ Set the timeline point that must be signalled by the compositor when it ++ has finished its usage of the buffer attached with wl_surface.attach ++ for the relevant commit. ++ ++ Once the timeline point is signaled, and assuming the associated buffer ++ is not pending release from other wl_surface.commit requests, no ++ additional explicit or implicit synchronization with the compositor is ++ required to safely re-use the buffer. ++ ++ Note that clients cannot rely on the release point being always ++ signaled after the acquire point: compositors may release buffers ++ without ever reading from them. In addition, the compositor may use ++ different presentation paths for different commits, which may have ++ different release behavior. As a result, the compositor may signal the ++ release points in a different order than the client committed them. ++ ++ Because signaling a timeline point also signals every previous point, ++ it is generally not safe to use the same timeline object for the ++ release points of multiple buffers. The out-of-order signaling ++ described above may lead to a release point being signaled before the ++ compositor has finished reading. To avoid this, it is strongly ++ recommended that each buffer should use a separate timeline for its ++ release points. ++ ++ The 64-bit unsigned value combined from point_hi and point_lo is the ++ point value. ++ ++ The release point is double-buffered state, and will be applied on the ++ next wl_surface.commit request for the associated surface. Thus, it ++ applies only to the buffer that is attached to the surface at commit ++ time. ++ ++ If a release point has already been attached during the same commit ++ cycle, the new point replaces the old one. ++ ++ If the associated wl_surface was destroyed, a no_surface error is ++ raised. ++ ++ If at surface commit time there is a pending release timeline point set ++ but no pending buffer attached, a no_buffer error is raised. If at ++ surface commit time there is a pending buffer attached but no pending ++ release timeline point set, the no_release_point protocol error is ++ raised. ++ ++ ++ ++ ++ ++ ++ +diff --git a/src/wayland/surface.cpp b/src/wayland/surface.cpp +index 23f981f2f81..6a33c5fca10 100644 +--- a/src/wayland/surface.cpp ++++ b/src/wayland/surface.cpp +@@ -13,6 +13,7 @@ + #include "fractionalscale_v1_p.h" + #include "frog_colormanagement_v1.h" + #include "idleinhibit_v1_p.h" ++#include "linux_drm_syncobj_v1.h" + #include "linuxdmabufv1clientbuffer.h" + #include "output.h" + #include "pointerconstraints_v1_p.h" +@@ -342,6 +343,10 @@ void SurfaceInterfacePrivate::surface_commit(Resource *resource) + { + const bool sync = subsurface.handle && subsurface.handle->isSynchronized(); + ++ if (syncObjV1 && syncObjV1->maybeEmitProtocolErrors()) { ++ return; ++ } ++ + Transaction *transaction; + if (sync) { + if (!subsurface.transaction) { +@@ -518,6 +523,9 @@ void SurfaceState::mergeInto(SurfaceState *target) + target->offset = offset; + target->damage = damage; + target->bufferDamage = bufferDamage; ++ target->acquirePoint.timeline = std::exchange(acquirePoint.timeline, nullptr); ++ target->acquirePoint.point = acquirePoint.point; ++ target->releasePoint = std::move(releasePoint); + target->bufferIsSet = true; + } + if (viewport.sourceGeometryIsSet) { +@@ -600,6 +608,7 @@ void SurfaceInterfacePrivate::applyState(SurfaceState *next) + const bool visibilityChanged = bufferChanged && bool(current->buffer) != bool(next->buffer); + const bool colorDescriptionChanged = next->colorDescriptionIsSet; + const bool presentationModeHintChanged = next->presentationModeHintIsSet; ++ const bool bufferReleasePointChanged = next->releasePointIsSet; + + const QSizeF oldSurfaceSize = surfaceSize; + const QSize oldBufferSize = bufferSize; +@@ -608,6 +617,9 @@ void SurfaceInterfacePrivate::applyState(SurfaceState *next) + + next->mergeInto(current.get()); + bufferRef = current->buffer; ++ if (bufferRef && current->releasePoint) { ++ bufferRef->addReleasePoint(current->releasePoint); ++ } + scaleOverride = pendingScaleOverride; + + if (current->buffer) { +@@ -689,6 +701,9 @@ void SurfaceInterfacePrivate::applyState(SurfaceState *next) + if (presentationModeHintChanged) { + Q_EMIT q->presentationModeHintChanged(); + } ++ if (bufferReleasePointChanged) { ++ Q_EMIT q->bufferReleasePointChanged(); ++ } + + if (bufferChanged) { + if (current->buffer && (!current->damage.isEmpty() || !current->bufferDamage.isEmpty())) { +@@ -1179,6 +1194,11 @@ void SurfaceInterface::traverseTree(std::function SurfaceInterface::bufferReleasePoint() const ++{ ++ return d->current->releasePoint; ++} ++ + } // namespace KWin + + #include "moc_surface.cpp" +diff --git a/src/wayland/surface.h b/src/wayland/surface.h +index 597f06774f8..80d1fa55708 100644 +--- a/src/wayland/surface.h ++++ b/src/wayland/surface.h +@@ -32,6 +32,7 @@ class SlideInterface; + class SubSurfaceInterface; + class SurfaceInterfacePrivate; + class Transaction; ++class SyncReleasePoint; + + /** + * The SurfaceRole class represents a role assigned to a wayland surface. +@@ -342,6 +343,12 @@ public: + + void setPreferredColorDescription(const ColorDescription &descr); + ++ /** ++ * @returns the release point that should be referenced as long as the buffer on this surface ++ * is, or may still be used by the compositor ++ */ ++ std::shared_ptr bufferReleasePoint() const; ++ + /** + * Traverses the surface sub-tree with this surface as the root. + */ +@@ -426,6 +433,7 @@ Q_SIGNALS: + + void colorDescriptionChanged(); + void presentationModeHintChanged(); ++ void bufferReleasePointChanged(); + + /** + * Emitted when the Surface has been committed. +diff --git a/src/wayland/surface_p.h b/src/wayland/surface_p.h +index f78bd107b44..775f9d7bd2e 100644 +--- a/src/wayland/surface_p.h ++++ b/src/wayland/surface_p.h +@@ -28,6 +28,7 @@ class FractionalScaleV1Interface; + class FrogColorManagementSurfaceV1; + class PresentationTimeFeedback; + class XXColorSurfaceV2; ++class LinuxDrmSyncObjSurfaceV1; + + struct SurfaceState + { +@@ -58,6 +59,7 @@ struct SurfaceState + bool contentTypeIsSet = false; + bool presentationModeHintIsSet = false; + bool colorDescriptionIsSet = false; ++ bool releasePointIsSet = false; + qint32 bufferScale = 1; + OutputTransform bufferTransform = OutputTransform::Normal; + wl_list frameCallbacks; +@@ -71,6 +73,12 @@ struct SurfaceState + PresentationModeHint presentationHint = PresentationModeHint::VSync; + ColorDescription colorDescription = ColorDescription::sRGB; + std::unique_ptr presentationFeedback; ++ struct ++ { ++ std::shared_ptr timeline; ++ uint64_t point = 0; ++ } acquirePoint; ++ std::shared_ptr releasePoint; + + struct + { +@@ -169,6 +177,7 @@ public: + TearingControlV1Interface *tearing = nullptr; + FrogColorManagementSurfaceV1 *frogColorManagement = nullptr; + XXColorSurfaceV2 *xxColorSurface = nullptr; ++ LinuxDrmSyncObjSurfaceV1 *syncObjV1 = nullptr; + + struct + { +diff --git a/src/wayland/transaction.cpp b/src/wayland/transaction.cpp +index 93004ba863d..fcd19d4036f 100644 +--- a/src/wayland/transaction.cpp ++++ b/src/wayland/transaction.cpp +@@ -5,7 +5,9 @@ + */ + + #include "wayland/transaction.h" ++#include "core/syncobjtimeline.h" + #include "utils/filedescriptor.h" ++#include "wayland/clientconnection.h" + #include "wayland/subcompositor.h" + #include "wayland/surface_p.h" + #include "wayland/transaction_p.h" +@@ -76,6 +78,24 @@ bool TransactionDmaBufLocker::arm() + return !m_pending.isEmpty(); + } + ++TransactionEventFdLocker::TransactionEventFdLocker(Transaction *transaction, FileDescriptor &&eventFd, ClientConnection *client) ++ : m_transaction(transaction) ++ , m_client(client) ++ , m_eventFd(std::move(eventFd)) ++ , m_notifier(m_eventFd.get(), QSocketNotifier::Type::Read) ++{ ++ transaction->lock(); ++ connect(&m_notifier, &QSocketNotifier::activated, this, &TransactionEventFdLocker::unlock); ++ // when the client quits, the eventfd may never be signaled ++ connect(m_client, &ClientConnection::aboutToBeDestroyed, this, &TransactionEventFdLocker::unlock); ++} ++ ++void TransactionEventFdLocker::unlock() ++{ ++ m_transaction->unlock(); ++ delete this; ++} ++ + Transaction::Transaction() + { + } +@@ -248,7 +268,12 @@ void Transaction::commit() + for (TransactionEntry &entry : m_entries) { + if (entry.state->bufferIsSet && entry.state->buffer) { + // Avoid applying the transaction until all graphics buffers have become idle. +- if (auto locker = TransactionDmaBufLocker::get(entry.state->buffer)) { ++ if (entry.state->acquirePoint.timeline) { ++ auto eventFd = entry.state->acquirePoint.timeline->eventFd(entry.state->acquirePoint.point); ++ if (entry.surface && eventFd.isValid()) { ++ new TransactionEventFdLocker(this, std::move(eventFd), entry.surface->client()); ++ } ++ } else if (auto locker = TransactionDmaBufLocker::get(entry.state->buffer)) { + locker->add(this); + } + } +diff --git a/src/wayland/transaction_p.h b/src/wayland/transaction_p.h +index 5bcf148aaad..29a9921e81f 100644 +--- a/src/wayland/transaction_p.h ++++ b/src/wayland/transaction_p.h +@@ -33,4 +33,19 @@ private: + std::vector> m_notifiers; + }; + ++class TransactionEventFdLocker : public QObject ++{ ++ Q_OBJECT ++public: ++ TransactionEventFdLocker(Transaction *transaction, FileDescriptor &&eventFd, ClientConnection *client); ++ ++private: ++ void unlock(); ++ ++ Transaction *const m_transaction; ++ const QPointer m_client; ++ const FileDescriptor m_eventFd; ++ QSocketNotifier m_notifier; ++}; ++ + } // namespace KWin +diff --git a/src/wayland_server.cpp b/src/wayland_server.cpp +index 2b02eb2fa69..037b633fc6c 100644 +--- a/src/wayland_server.cpp ++++ b/src/wayland_server.cpp +@@ -41,6 +41,7 @@ + #include "wayland/inputmethod_v1.h" + #include "wayland/keyboard_shortcuts_inhibit_v1.h" + #include "wayland/keystate.h" ++#include "wayland/linux_drm_syncobj_v1.h" + #include "wayland/linuxdmabufv1clientbuffer.h" + #include "wayland/lockscreen_overlay_v1.h" + #include "wayland/output.h" +@@ -811,6 +812,22 @@ QString WaylandServer::socketName() const + return QString(); + } + ++LinuxDrmSyncObjV1Interface *WaylandServer::linuxSyncObj() const ++{ ++ return m_linuxDrmSyncObj; ++} ++ ++void WaylandServer::setRenderBackend(RenderBackend *backend) ++{ ++ if (backend->supportsTimelines()) { ++ if (!m_linuxDrmSyncObj) { ++ m_linuxDrmSyncObj = new LinuxDrmSyncObjV1Interface(m_display, m_display); ++ } ++ } else if (m_linuxDrmSyncObj) { ++ m_linuxDrmSyncObj->remove(); ++ } ++} ++ + #if KWIN_BUILD_SCREENLOCKER + WaylandServer::LockScreenPresentationWatcher::LockScreenPresentationWatcher(WaylandServer *server) + { +diff --git a/src/wayland_server.h b/src/wayland_server.h +index 8eb6f31a176..60dc67d9670 100644 +--- a/src/wayland_server.h ++++ b/src/wayland_server.h +@@ -58,6 +58,8 @@ class XdgSurfaceWindow; + class XdgToplevelWindow; + class PresentationTime; + class XXColorManagerV2; ++class LinuxDrmSyncObjV1Interface; ++class RenderBackend; + + class KWIN_EXPORT WaylandServer : public QObject + { +@@ -226,6 +228,10 @@ public: + return m_xdgActivationIntegration; + } + ++ LinuxDrmSyncObjV1Interface *linuxSyncObj() const; ++ ++ void setRenderBackend(RenderBackend *backend); ++ + Q_SIGNALS: + void windowAdded(KWin::Window *); + void windowRemoved(KWin::Window *); +@@ -284,6 +290,7 @@ private: + TearingControlManagerV1Interface *m_tearingControlInterface = nullptr; + XwaylandShellV1Interface *m_xwaylandShell = nullptr; + PresentationTime *m_presentationTime = nullptr; ++ LinuxDrmSyncObjV1Interface *m_linuxDrmSyncObj = nullptr; + QList m_windows; + InitializationFlags m_initFlags; + QHash m_waylandOutputs; +-- +GitLab + + +From 489761e5d772ad11d195c3eb679f6747d1aa2e46 Mon Sep 17 00:00:00 2001 +From: Xaver Hugl +Date: Sat, 6 Apr 2024 18:51:01 +0200 +Subject: [PATCH 2/5] core/syncobjtimeline: make explicit sync use + SYNC_IOC_MERGE instead of waiting on the CPU side + +This brings some performance benefits, because the application can potentially reuse +the buffer earlier, and it simplifies the code a bit + +(cherry picked from commit 4c6000b3e1829da5f0ccb81bbccfee37fb28cd24) +--- + src/core/syncobjtimeline.cpp | 63 +++++++++++++++++++++++++------ + src/core/syncobjtimeline.h | 28 ++++---------- + src/scene/itemrenderer_opengl.cpp | 5 ++- + src/wayland/surface.h | 6 ++- + 4 files changed, 67 insertions(+), 35 deletions(-) + +diff --git a/src/core/syncobjtimeline.cpp b/src/core/syncobjtimeline.cpp +index 3e2c100370a..1152e895722 100644 +--- a/src/core/syncobjtimeline.cpp ++++ b/src/core/syncobjtimeline.cpp +@@ -6,8 +6,24 @@ + #include "syncobjtimeline.h" + + #include ++#include + #include + ++#if defined(Q_OS_LINUX) ++#include ++#else ++struct sync_merge_data ++{ ++ char name[32]; ++ __s32 fd2; ++ __s32 fence; ++ __u32 flags; ++ __u32 pad; ++}; ++#define SYNC_IOC_MAGIC '>' ++#define SYNC_IOC_MERGE _IOWR(SYNC_IOC_MAGIC, 3, struct sync_merge_data) ++#endif ++ + namespace KWin + { + +@@ -19,7 +35,39 @@ SyncReleasePoint::SyncReleasePoint(const std::shared_ptr &timeline + + SyncReleasePoint::~SyncReleasePoint() + { +- m_timeline->signal(m_timelinePoint); ++ if (m_releaseFence.isValid()) { ++ m_timeline->moveInto(m_timelinePoint, m_releaseFence); ++ } else { ++ m_timeline->signal(m_timelinePoint); ++ } ++} ++ ++static FileDescriptor mergeSyncFds(const FileDescriptor &fd1, const FileDescriptor &fd2) ++{ ++ struct sync_merge_data data ++ { ++ .name = "merged release fence", ++ .fd2 = fd2.get(), ++ .fence = -1, ++ }; ++ int err = -1; ++ do { ++ err = ioctl(fd1.get(), SYNC_IOC_MERGE, &data); ++ } while (err == -1 && (errno == EINTR || errno == EAGAIN)); ++ if (err < 0) { ++ return FileDescriptor{}; ++ } else { ++ return FileDescriptor(data.fence); ++ } ++} ++ ++void SyncReleasePoint::addReleaseFence(const FileDescriptor &fd) ++{ ++ if (m_releaseFence.isValid()) { ++ m_releaseFence = mergeSyncFds(m_releaseFence, fd); ++ } else { ++ m_releaseFence = fd.duplicate(); ++ } + } + + SyncTimeline *SyncReleasePoint::timeline() const +@@ -67,17 +115,8 @@ void SyncTimeline::signal(uint64_t timelinePoint) + drmSyncobjTimelineSignal(m_drmFd, &m_handle, &timelinePoint, 1); + } + +-SyncReleasePointHolder::SyncReleasePointHolder(FileDescriptor &&requirement, std::unordered_set> &&releasePoints) +- : m_fence(std::move(requirement)) +- , m_notifier(m_fence.get(), QSocketNotifier::Type::Read) +- , m_releasePoints(std::move(releasePoints)) +-{ +- connect(&m_notifier, &QSocketNotifier::activated, this, &SyncReleasePointHolder::signaled); +- m_notifier.setEnabled(true); +-} +- +-void SyncReleasePointHolder::signaled() ++void SyncTimeline::moveInto(uint64_t timelinePoint, const FileDescriptor &fd) + { +- delete this; ++ drmSyncobjImportSyncFile(m_drmFd, m_handle, fd.get()); + } + } +diff --git a/src/core/syncobjtimeline.h b/src/core/syncobjtimeline.h +index cffdb75e330..4ced3e06272 100644 +--- a/src/core/syncobjtimeline.h ++++ b/src/core/syncobjtimeline.h +@@ -7,11 +7,8 @@ + #include "kwin_export.h" + #include "utils/filedescriptor.h" + +-#include +-#include + #include + #include +-#include + + namespace KWin + { +@@ -30,9 +27,16 @@ public: + SyncTimeline *timeline() const; + uint64_t timelinePoint() const; + ++ /** ++ * Adds the fence of a graphics job that this release point should wait for ++ * before the timeline point is signaled ++ */ ++ void addReleaseFence(const FileDescriptor &fd); ++ + private: + const std::shared_ptr m_timeline; + const uint64_t m_timelinePoint; ++ FileDescriptor m_releaseFence; + }; + + class KWIN_EXPORT SyncTimeline +@@ -47,26 +51,10 @@ public: + FileDescriptor eventFd(uint64_t timelinePoint) const; + + void signal(uint64_t timelinePoint); ++ void moveInto(uint64_t timelinePoint, const FileDescriptor &fd); + + private: + const int32_t m_drmFd; + const uint32_t m_handle; + }; +- +-class SyncReleasePointHolder : public QObject +-{ +- Q_OBJECT +-public: +- /** +- * @param requirement the filedescriptor that needs to be readable before the release points may be signalled. Once that's happened, this object deletes itself!' +- */ +- explicit SyncReleasePointHolder(FileDescriptor &&requirement, std::unordered_set> &&releasePoints); +- +-private: +- void signaled(); +- +- const FileDescriptor m_fence; +- QSocketNotifier m_notifier; +- const std::unordered_set> m_releasePoints; +-}; + } +diff --git a/src/scene/itemrenderer_opengl.cpp b/src/scene/itemrenderer_opengl.cpp +index 3fee3863f2c..ffcc6de3914 100644 +--- a/src/scene/itemrenderer_opengl.cpp ++++ b/src/scene/itemrenderer_opengl.cpp +@@ -53,7 +53,10 @@ void ItemRendererOpenGL::endFrame() + if (m_eglDisplay) { + EGLNativeFence fence(m_eglDisplay); + if (fence.isValid()) { +- new SyncReleasePointHolder(std::move(fence.fileDescriptor()), std::move(m_releasePoints)); ++ for (const auto &releasePoint : m_releasePoints) { ++ releasePoint->addReleaseFence(fence.fileDescriptor()); ++ } ++ m_releasePoints.clear(); + } + } + m_releasePoints.clear(); +diff --git a/src/wayland/surface.h b/src/wayland/surface.h +index 80d1fa55708..152f1accc10 100644 +--- a/src/wayland/surface.h ++++ b/src/wayland/surface.h +@@ -344,8 +344,10 @@ public: + void setPreferredColorDescription(const ColorDescription &descr); + + /** +- * @returns the release point that should be referenced as long as the buffer on this surface +- * is, or may still be used by the compositor ++ * Returns the current release point for the buffer on this surface. The buffer keeps the ++ * release point referenced as long as it's referenced itself; for synchronization on the ++ * GPU side, the compositor has to either keep the release point referenced as long as the ++ * GPU task is running, or add a fence for each GPU task to the release point + */ + std::shared_ptr bufferReleasePoint() const; + +-- +GitLab + + +From eef4cbe0278e81c33b496932400854a74a08bfa4 Mon Sep 17 00:00:00 2001 +From: Xaver Hugl +Date: Tue, 16 Apr 2024 13:51:42 +0200 +Subject: [PATCH 3/5] wayland_server: guard against DRM_IOCTL_SYNCOBJ_EVENTFD + being broken + +...either because it's not implemented, or because it's buggy. + +(cherry picked from commit 26afbfb6fa030c3dadf6a22309686f96ca033f59) +--- + src/wayland_server.cpp | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/src/wayland_server.cpp b/src/wayland_server.cpp +index 037b633fc6c..d9240f0e8ce 100644 +--- a/src/wayland_server.cpp ++++ b/src/wayland_server.cpp +@@ -19,6 +19,7 @@ + #include "layershellv1window.h" + #include "main.h" + #include "options.h" ++#include "utils/kernel.h" + #include "utils/serviceutils.h" + #include "virtualdesktops.h" + #include "wayland/appmenu.h" +@@ -820,6 +821,15 @@ LinuxDrmSyncObjV1Interface *WaylandServer::linuxSyncObj() const + void WaylandServer::setRenderBackend(RenderBackend *backend) + { + if (backend->supportsTimelines()) { ++ // ensure the DRM_IOCTL_SYNCOBJ_EVENTFD ioctl is supported ++ const auto linuxVersion = linuxKernelVersion(); ++ if (linuxVersion.majorVersion() < 6 && linuxVersion.minorVersion() < 6) { ++ return; ++ } ++ // also ensure the implementation isn't totally broken, see https://lists.freedesktop.org/archives/dri-devel/2024-January/439101.html ++ if (linuxVersion.majorVersion() == 6 && (linuxVersion.minorVersion() == 7 || (linuxVersion.minorVersion() == 6 && linuxVersion.patchVersion() < 19))) { ++ return; ++ } + if (!m_linuxDrmSyncObj) { + m_linuxDrmSyncObj = new LinuxDrmSyncObjV1Interface(m_display, m_display); + } +-- +GitLab + + +From 2e9ecb0edf137309fb526adc7c8504e8c669b17f Mon Sep 17 00:00:00 2001 +From: Erik Kurzinger +Date: Tue, 16 Apr 2024 14:44:50 -0700 +Subject: [PATCH 4/5] core/syncobjtimeline: import release fence at correct + timeline point + +SyncTimeline::moveInto imports the provided fence to the syncobj by +calling drmSyncobjImportSyncFile. However, that function assumes the +syncobj a binary syncobj, meaning the fence will be imported at timeline +point 0, not at the intended timeline point. + +Since there is no timeline equivalent of drmSyncobjImportSyncFile, to +achieve the correct behavior we create a temporary binary syncobj, +import the fence into that, and then use drmSyncobjTransfer to transfer +the fence to the desired timeline point on the destination syncobj. + +Signed-off-by: Erik Kurzinger +(cherry picked from commit 9ca7b9b1cf292e790223419dc197737f875109d4) +--- + src/core/syncobjtimeline.cpp | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/src/core/syncobjtimeline.cpp b/src/core/syncobjtimeline.cpp +index 1152e895722..80054e93480 100644 +--- a/src/core/syncobjtimeline.cpp ++++ b/src/core/syncobjtimeline.cpp +@@ -117,6 +117,10 @@ void SyncTimeline::signal(uint64_t timelinePoint) + + void SyncTimeline::moveInto(uint64_t timelinePoint, const FileDescriptor &fd) + { +- drmSyncobjImportSyncFile(m_drmFd, m_handle, fd.get()); ++ uint32_t tempHandle = 0; ++ drmSyncobjCreate(m_drmFd, 0, &tempHandle); ++ drmSyncobjImportSyncFile(m_drmFd, tempHandle, fd.get()); ++ drmSyncobjTransfer(m_drmFd, m_handle, timelinePoint, tempHandle, 0, 0); ++ drmSyncobjDestroy(m_drmFd, tempHandle); + } + } +-- +GitLab + + +From 463ae633e878004b1799f618641a0c44573c10f4 Mon Sep 17 00:00:00 2001 +From: Xaver Hugl +Date: Wed, 17 Apr 2024 03:26:34 +0200 +Subject: [PATCH 5/5] wayland/surface: fix the change signal for release points + not being emitted + +(cherry picked from commit eed7943939524895225ab4cf4eb3a1fa880b42bf) +--- + src/wayland/surface.cpp | 2 +- + src/wayland/surface_p.h | 1 - + 2 files changed, 1 insertion(+), 2 deletions(-) + +diff --git a/src/wayland/surface.cpp b/src/wayland/surface.cpp +index 6a33c5fca10..6a77e98d0ee 100644 +--- a/src/wayland/surface.cpp ++++ b/src/wayland/surface.cpp +@@ -608,7 +608,7 @@ void SurfaceInterfacePrivate::applyState(SurfaceState *next) + const bool visibilityChanged = bufferChanged && bool(current->buffer) != bool(next->buffer); + const bool colorDescriptionChanged = next->colorDescriptionIsSet; + const bool presentationModeHintChanged = next->presentationModeHintIsSet; +- const bool bufferReleasePointChanged = next->releasePointIsSet; ++ const bool bufferReleasePointChanged = next->bufferIsSet && current->releasePoint != next->releasePoint; + + const QSizeF oldSurfaceSize = surfaceSize; + const QSize oldBufferSize = bufferSize; +diff --git a/src/wayland/surface_p.h b/src/wayland/surface_p.h +index 775f9d7bd2e..7bcf95c0bef 100644 +--- a/src/wayland/surface_p.h ++++ b/src/wayland/surface_p.h +@@ -59,7 +59,6 @@ struct SurfaceState + bool contentTypeIsSet = false; + bool presentationModeHintIsSet = false; + bool colorDescriptionIsSet = false; +- bool releasePointIsSet = false; + qint32 bufferScale = 1; + OutputTransform bufferTransform = OutputTransform::Normal; + wl_list frameCallbacks; +-- +GitLab + diff --git a/overlays/kwin/default.nix b/overlays/kwin/default.nix new file mode 100644 index 0000000..d766a6e --- /dev/null +++ b/overlays/kwin/default.nix @@ -0,0 +1,9 @@ +self: super: { + kdePackages = super.kdePackages.overrideScope (kde-self: kde-super: rec { + kwin = kde-super.kwin.overrideAttrs (oldAttrs: rec { + patches = oldAttrs.patches ++ [ + ./5511.patch # https://invent.kde.org/plasma/kwin/-/merge_requests/5511 + ]; + }); + }); +} \ No newline at end of file