diff --git a/modules/system/desktop/plasma/default.nix b/modules/system/desktop/plasma/default.nix
index b0387fe..521f98d 100644
--- a/modules/system/desktop/plasma/default.nix
+++ b/modules/system/desktop/plasma/default.nix
@@ -33,10 +33,5 @@ in
       glxinfo
       clinfo
     ];
-
-    # Uncomment when kwin is available in nixpkgs and NVIDIA 555
-    nixpkgs.overlays = [
-      (import ../../../../overlays/kwin)
-    ];
   };
-}
\ No newline at end of file
+}
diff --git a/overlays/.gitkeep b/overlays/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/overlays/kwin/5511.patch b/overlays/kwin/5511.patch
deleted file mode 100644
index 341dc2e..0000000
--- a/overlays/kwin/5511.patch
+++ /dev/null
@@ -1,2027 +0,0 @@
-From e1791de663807bc93c09418481cb844c7daeb8f7 Mon Sep 17 00:00:00 2001
-From: Xaver Hugl <xaver.hugl@gmail.com>
-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<SyncTimeline> 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<SyncTimeline>(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<EglContext> contextForGpu(DrmGpu *gpu);
- 
-+    bool supportsTimelines() const override;
-+    std::unique_ptr<SyncTimeline> 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<drmVersion> 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<WorkspaceSceneOpenGL>(backend.get());
--    m_cursorScene = std::make_unique<CursorScene>(std::make_unique<ItemRendererOpenGL>());
-+    m_cursorScene = std::make_unique<CursorScene>(std::make_unique<ItemRendererOpenGL>(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<SyncReleasePoint> &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<SyncReleasePoint> &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<std::shared_ptr<SyncReleasePoint>> 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 <drm_fourcc.h>
- 
-@@ -103,6 +104,16 @@ std::unique_ptr<SurfaceTexture> RenderBackend::createSurfaceTextureWayland(Surfa
-     return nullptr;
- }
- 
-+bool RenderBackend::supportsTimelines() const
-+{
-+    return false;
-+}
-+
-+std::unique_ptr<SyncTimeline> 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 <QObject>
- 
-@@ -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<SurfaceTexture> createSurfaceTextureX11(SurfacePixmapX11 *pixmap);
-     virtual std::unique_ptr<SurfaceTexture> createSurfaceTextureWayland(SurfacePixmap *pixmap);
-+
-+    virtual bool supportsTimelines() const;
-+    virtual std::unique_ptr<SyncTimeline> 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 <xaver.hugl@gmail.com>
-+
-+    SPDX-License-Identifier: GPL-2.0-or-later
-+*/
-+#include "syncobjtimeline.h"
-+
-+#include <sys/eventfd.h>
-+#include <xf86drm.h>
-+
-+namespace KWin
-+{
-+
-+SyncReleasePoint::SyncReleasePoint(const std::shared_ptr<SyncTimeline> &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<std::shared_ptr<SyncReleasePoint>> &&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 <xaver.hugl@gmail.com>
-+
-+    SPDX-License-Identifier: GPL-2.0-or-later
-+*/
-+#pragma once
-+#include "kwin_export.h"
-+#include "utils/filedescriptor.h"
-+
-+#include <QObject>
-+#include <QSocketNotifier>
-+#include <memory>
-+#include <stdint.h>
-+#include <unordered_set>
-+
-+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<SyncTimeline> &timeline, uint64_t timelinePoint);
-+    ~SyncReleasePoint();
-+
-+    SyncTimeline *timeline() const;
-+    uint64_t timelinePoint() const;
-+
-+private:
-+    const std::shared_ptr<SyncTimeline> 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<std::shared_ptr<SyncReleasePoint>> &&releasePoints);
-+
-+private:
-+    void signaled();
-+
-+    const FileDescriptor m_fence;
-+    QSocketNotifier m_notifier;
-+    const std::unordered_set<std::shared_ptr<SyncReleasePoint>> 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<std::shared_ptr<GLTexture>, 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<DecorationItem *>(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<SurfaceItem *>(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 <unordered_set>
-+
- 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<SyncReleasePoint> 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 &region, const RenderContext &renderContext);
- 
-     bool m_blendingEnabled = false;
-+    EglDisplay *const m_eglDisplay;
-+    std::unordered_set<std::shared_ptr<SyncReleasePoint>> 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<SyncReleasePoint> 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<SyncReleasePoint> bufferReleasePoint() const;
-+
-     QRegion mapFromBuffer(const QRegion &region) const;
- 
-     void addDamage(const QRegion &region);
-@@ -82,6 +84,7 @@ protected:
-     std::deque<std::chrono::nanoseconds> m_lastDamageTimeDiffs;
-     std::optional<std::chrono::steady_clock::time_point> m_lastDamage;
-     std::chrono::nanoseconds m_frameTimeEstimation = std::chrono::days(1000);
-+    std::shared_ptr<SyncReleasePoint> 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<SurfacePixmap> 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<ItemRendererOpenGL>())
-+    : WorkspaceScene(std::make_unique<ItemRendererOpenGL>(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 <xaver.hugl@gmail.com>
-+
-+    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 <xf86drm.h>
-+
-+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<SyncTimeline> &&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<SyncTimeline> 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<LinuxDrmSyncObjTimelineV1 *>(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<LinuxDrmSyncObjTimelineV1 *>(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<SyncReleasePoint>(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 <xaver.hugl@gmail.com>
-+
-+    SPDX-License-Identifier: GPL-2.0-or-later
-+*/
-+#pragma once
-+
-+#include "kwin_export.h"
-+#include "qwayland-server-linux-drm-syncobj-v1.h"
-+
-+#include <QObject>
-+#include <QPointer>
-+
-+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<RenderBackend> 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<SurfaceInterface> 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 <xaver.hugl@gmail.com>
-+
-+    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<SyncTimeline> &&timeline);
-+    ~LinuxDrmSyncObjTimelineV1() override;
-+
-+    /**
-+     * May return nullptr if the timeline resource is inert
-+     */
-+    std::shared_ptr<SyncTimeline> 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<SyncTimeline> 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 @@
-+<?xml version="1.0" encoding="UTF-8"?>
-+<protocol name="linux_drm_syncobj_v1">
-+  <copyright>
-+    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.
-+  </copyright>
-+
-+  <description summary="protocol for providing explicit synchronization">
-+    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.
-+  </description>
-+
-+  <interface name="wp_linux_drm_syncobj_manager_v1" version="1">
-+    <description summary="global for providing explicit synchronization">
-+      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.
-+    </description>
-+
-+    <request name="destroy" type="destructor">
-+      <description summary="destroy explicit synchronization factory object">
-+        Destroy this explicit synchronization factory object. Other objects
-+        shall not be affected by this request.
-+      </description>
-+    </request>
-+
-+    <enum name="error">
-+      <entry name="surface_exists" value="0"
-+        summary="the surface already has a synchronization object associated"/>
-+      <entry name="invalid_timeline" value="1"
-+        summary="the timeline object could not be imported"/>
-+    </enum>
-+
-+    <request name="get_surface">
-+      <description summary="extend surface interface for explicit synchronization">
-+        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.
-+      </description>
-+      <arg name="id" type="new_id" interface="wp_linux_drm_syncobj_surface_v1"
-+        summary="the new synchronization surface object id"/>
-+      <arg name="surface" type="object" interface="wl_surface"
-+        summary="the surface"/>
-+    </request>
-+
-+    <request name="import_timeline">
-+      <description summary="import a DRM syncobj timeline">
-+        Import a DRM synchronization object timeline.
-+
-+        If the FD cannot be imported, the invalid_timeline error is raised.
-+      </description>
-+      <arg name="id" type="new_id" interface="wp_linux_drm_syncobj_timeline_v1"/>
-+      <arg name="fd" type="fd" summary="drm_syncobj file descriptor"/>
-+    </request>
-+  </interface>
-+
-+  <interface name="wp_linux_drm_syncobj_timeline_v1" version="1">
-+    <description summary="synchronization object timeline">
-+      This object represents an explicit synchronization object timeline
-+      imported by the client to the compositor.
-+    </description>
-+
-+    <request name="destroy" type="destructor">
-+      <description summary="destroy the timeline">
-+        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.
-+      </description>
-+    </request>
-+  </interface>
-+
-+  <interface name="wp_linux_drm_syncobj_surface_v1" version="1">
-+    <description summary="per-surface explicit synchronization">
-+      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.
-+    </description>
-+
-+    <request name="destroy" type="destructor">
-+      <description summary="destroy the surface synchronization object">
-+        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.
-+      </description>
-+    </request>
-+
-+    <enum name="error">
-+      <entry name="no_surface" value="1"
-+        summary="the associated wl_surface was destroyed"/>
-+      <entry name="unsupported_buffer" value="2"
-+        summary="the buffer does not support explicit synchronization"/>
-+      <entry name="no_buffer" value="3" summary="no buffer was attached"/>
-+      <entry name="no_acquire_point" value="4"
-+        summary="no acquire timeline point was set"/>
-+      <entry name="no_release_point" value="5"
-+        summary="no release timeline point was set"/>
-+      <entry name="conflicting_points" value="6"
-+        summary="acquire and release timeline points are in conflict"/>
-+    </enum>
-+
-+    <request name="set_acquire_point">
-+      <description summary="set the acquire timeline point">
-+        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.
-+      </description>
-+      <arg name="timeline" type="object" interface="wp_linux_drm_syncobj_timeline_v1"/>
-+      <arg name="point_hi" type="uint" summary="high 32 bits of the point value"/>
-+      <arg name="point_lo" type="uint" summary="low 32 bits of the point value"/>
-+    </request>
-+
-+    <request name="set_release_point">
-+      <description summary="set the release timeline point">
-+        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.
-+      </description>
-+      <arg name="timeline" type="object" interface="wp_linux_drm_syncobj_timeline_v1"/>
-+      <arg name="point_hi" type="uint" summary="high 32 bits of the point value"/>
-+      <arg name="point_lo" type="uint" summary="low 32 bits of the point value"/>
-+    </request>
-+  </interface>
-+</protocol>
-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<void(SurfaceInterface *surface
-     }
- }
- 
-+std::shared_ptr<SyncReleasePoint> 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<SyncReleasePoint> 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<PresentationTimeFeedback> presentationFeedback;
-+    struct
-+    {
-+        std::shared_ptr<SyncTimeline> timeline;
-+        uint64_t point = 0;
-+    } acquirePoint;
-+    std::shared_ptr<SyncReleasePoint> 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<std::unique_ptr<QSocketNotifier>> 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<ClientConnection> 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<Window *> m_windows;
-     InitializationFlags m_initFlags;
-     QHash<Output *, OutputInterface *> m_waylandOutputs;
--- 
-GitLab
-
-
-From 489761e5d772ad11d195c3eb679f6747d1aa2e46 Mon Sep 17 00:00:00 2001
-From: Xaver Hugl <xaver.hugl@gmail.com>
-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 <sys/eventfd.h>
-+#include <sys/ioctl.h>
- #include <xf86drm.h>
- 
-+#if defined(Q_OS_LINUX)
-+#include <linux/sync_file.h>
-+#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<SyncTimeline> &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<std::shared_ptr<SyncReleasePoint>> &&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 <QObject>
--#include <QSocketNotifier>
- #include <memory>
- #include <stdint.h>
--#include <unordered_set>
- 
- 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<SyncTimeline> 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<std::shared_ptr<SyncReleasePoint>> &&releasePoints);
--
--private:
--    void signaled();
--
--    const FileDescriptor m_fence;
--    QSocketNotifier m_notifier;
--    const std::unordered_set<std::shared_ptr<SyncReleasePoint>> 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<SyncReleasePoint> bufferReleasePoint() const;
- 
--- 
-GitLab
-
-
-From eef4cbe0278e81c33b496932400854a74a08bfa4 Mon Sep 17 00:00:00 2001
-From: Xaver Hugl <xaver.hugl@gmail.com>
-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 <ekurzinger@nvidia.com>
-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 <ekurzinger@nvidia.com>
-(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 <xaver.hugl@gmail.com>
-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
deleted file mode 100644
index d766a6e..0000000
--- a/overlays/kwin/default.nix
+++ /dev/null
@@ -1,9 +0,0 @@
-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