[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    [kwin/fredrik/vulkan] /: Add the beginnings of a Vulkan compositing plugin
From:       Fredrik_Höglund <null () kde ! org>
Date:       2018-02-16 17:01:48
Message-ID: E1emjOO-0004Yx-Ha () code ! kde ! org
[Download RAW message or body]

Git commit 2cae8ad3f571fd78c1a38af753b0521e4a9dbab3 by Fredrik Höglund.
Committed on 16/02/2018 at 16:58.
Pushed by fredrik into branch 'fredrik/vulkan'.

Add the beginnings of a Vulkan compositing plugin

The scene initializes Vulkan, and clears and presents swap chain images,
but everything else is stubbed out.

M  +1    -0    data/org_kde_kwin.categories
M  +2    -0    dbusinterface.cpp
M  +2    -1    libkwineffects/kwinglobals.h
M  +7    -0    options.cpp
M  +5    -0    platform.cpp
M  +2    -0    platform.h
M  +4    -0    platformsupport/scenes/CMakeLists.txt
A  +16   -0    platformsupport/scenes/vulkan/CMakeLists.txt
A  +64   -0    platformsupport/scenes/vulkan/backend.cpp     [License: GPL (v2)]
A  +118  -0    platformsupport/scenes/vulkan/backend.h     [License: GPL (v2)]
M  +9    -0    plugins/platforms/wayland/CMakeLists.txt
M  +19   -3    plugins/platforms/wayland/wayland_backend.cpp
M  +1    -0    plugins/platforms/wayland/wayland_backend.h
A  +87   -0    plugins/platforms/wayland/wayland_vulkan_backend.cpp     [License: GPL \
(v2)] A  +62   -0    plugins/platforms/wayland/wayland_vulkan_backend.h     [License: \
GPL (v2)] M  +10   -0    plugins/platforms/x11/standalone/CMakeLists.txt
A  +130  -0    plugins/platforms/x11/standalone/vulkanbackend.cpp     [License: GPL \
(v2)] A  +63   -0    plugins/platforms/x11/standalone/vulkanbackend.h     [License: \
GPL (v2)] M  +15   -0    plugins/platforms/x11/standalone/x11_platform.cpp
M  +1    -0    plugins/platforms/x11/standalone/x11_platform.h
M  +9    -0    plugins/platforms/x11/windowed/CMakeLists.txt
A  +102  -0    plugins/platforms/x11/windowed/x11_vulkan_backend.cpp     [License: \
GPL (v2)] A  +60   -0    plugins/platforms/x11/windowed/x11_vulkan_backend.h     \
[License: GPL (v2)] M  +26   -0    \
plugins/platforms/x11/windowed/x11windowed_backend.cpp M  +6    -3    \
plugins/platforms/x11/windowed/x11windowed_backend.h M  +3    -0    \
plugins/scenes/CMakeLists.txt A  +35   -0    plugins/scenes/vulkan/CMakeLists.txt
A  +50   -0    plugins/scenes/vulkan/decorationrenderer.cpp     [License: GPL (v2)]
A  +58   -0    plugins/scenes/vulkan/decorationrenderer.h     [License: GPL (v2)]
A  +305  -0    plugins/scenes/vulkan/descriptorset.cpp     [License: GPL (v2)]
A  +287  -0    plugins/scenes/vulkan/descriptorset.h     [License: GPL (v2)]
A  +83   -0    plugins/scenes/vulkan/effectframe.cpp     [License: GPL (v2)]
A  +52   -0    plugins/scenes/vulkan/effectframe.h     [License: GPL (v2)]
A  +1792 -0    plugins/scenes/vulkan/scene.cpp     [License: GPL (v2)]
A  +249  -0    plugins/scenes/vulkan/scene.h     [License: GPL (v2)]
A  +50   -0    plugins/scenes/vulkan/shadow.cpp     [License: GPL (v2)]
A  +49   -0    plugins/scenes/vulkan/shadow.h     [License: GPL (v2)]
A  +345  -0    plugins/scenes/vulkan/swapchain.cpp     [License: GPL (v2)]
A  +169  -0    plugins/scenes/vulkan/swapchain.h     [License: GPL (v2)]
A  +9    -0    plugins/scenes/vulkan/vulkan.json
A  +55   -0    plugins/scenes/vulkan/window.cpp     [License: GPL (v2)]
A  +52   -0    plugins/scenes/vulkan/window.h     [License: GPL (v2)]
A  +70   -0    plugins/scenes/vulkan/windowpixmap.cpp     [License: GPL (v2)]
A  +70   -0    plugins/scenes/vulkan/windowpixmap.h     [License: GPL (v2)]
M  +5    -1    workspace.cpp

https://commits.kde.org/kwin/2cae8ad3f571fd78c1a38af753b0521e4a9dbab3

diff --git a/data/org_kde_kwin.categories b/data/org_kde_kwin.categories
index a256fa2c5..69ae6fed3 100644
--- a/data/org_kde_kwin.categories
+++ b/data/org_kde_kwin.categories
@@ -19,3 +19,4 @@ kwin_qpa_plugin KWin QtPlatformAbstraction plugin
 kwin_scene_xrender KWin XRender based compositor scene plugin
 kwin_scene_qpainter KWin QPainter based compositor scene plugin
 kwin_scene_opengl KWin OpenGL based compositor scene plugins
+kwin_vulkan_scene KWin Vulkan based compositor scene plugin
diff --git a/dbusinterface.cpp b/dbusinterface.cpp
index 8aaaa32ed..03c6b5ac9 100644
--- a/dbusinterface.cpp
+++ b/dbusinterface.cpp
@@ -218,6 +218,8 @@ QString CompositorDBusInterface::compositingType() const
         } else {
             return QStringLiteral("gl2");
         }
+    case VulkanCompositing:
+        return QStringLiteral("vulkan");
     case QPainterCompositing:
         return QStringLiteral("qpainter");
     case NoCompositing:
diff --git a/libkwineffects/kwinglobals.h b/libkwineffects/kwinglobals.h
index 0ca4891d7..6885f42c4 100644
--- a/libkwineffects/kwinglobals.h
+++ b/libkwineffects/kwinglobals.h
@@ -52,7 +52,8 @@ enum CompositingType {
     OpenGLCompositing = 1,
     XRenderCompositing = 1<<1,
     QPainterCompositing = 1<< 2,
-    OpenGL2Compositing = 1<<3 | OpenGLCompositing
+    OpenGL2Compositing = 1<<3 | OpenGLCompositing,
+    VulkanCompositing = 1<<4
 };
 
 enum OpenGLPlatformInterface {
diff --git a/options.cpp b/options.cpp
index a8cd58f16..34a27e428 100644
--- a/options.cpp
+++ b/options.cpp
@@ -901,6 +901,8 @@ bool Options::loadCompositingConfig (bool force)
         compositingMode = XRenderCompositing;
     else if (compositingBackend == "QPainter")
         compositingMode = QPainterCompositing;
+    else if (compositingBackend == "Vulkan")
+        compositingMode = VulkanCompositing;
     else
         compositingMode = OpenGLCompositing;
 
@@ -921,6 +923,11 @@ bool Options::loadCompositingConfig (bool force)
             compositingMode = QPainterCompositing;
             useCompositing = true;
             break;
+        case 'V':
+            qCDebug(KWIN_CORE) << "Compositing forced to Vulkan mode by environment \
variable"; +            compositingMode = VulkanCompositing;
+            useCompositing = true;
+            break;
         case 'N':
             if (getenv("KDE_FAILSAFE"))
                 qCDebug(KWIN_CORE) << "Compositing disabled forcefully by KDE \
                failsafe mode";
diff --git a/platform.cpp b/platform.cpp
index 65ce0259d..a5a7694d9 100644
--- a/platform.cpp
+++ b/platform.cpp
@@ -106,6 +106,11 @@ QPainterBackend *Platform::createQPainterBackend()
     return nullptr;
 }
 
+VulkanBackend *Platform::createVulkanBackend()
+{
+    return nullptr;
+}
+
 Edge *Platform::createScreenEdge(ScreenEdges *edges)
 {
     return new Edge(edges);
diff --git a/platform.h b/platform.h
index e88dff529..8bec94623 100644
--- a/platform.h
+++ b/platform.h
@@ -54,6 +54,7 @@ class Scene;
 class Screens;
 class ScreenEdges;
 class Toplevel;
+class VulkanBackend;
 class WaylandCursorTheme;
 
 namespace Decoration
@@ -72,6 +73,7 @@ public:
     virtual Screens *createScreens(QObject *parent = nullptr);
     virtual OpenGLBackend *createOpenGLBackend();
     virtual QPainterBackend *createQPainterBackend();
+    virtual VulkanBackend *createVulkanBackend();
     /**
      * Allows the platform to create a platform specific screen edge.
      * The default implementation creates a Edge.
diff --git a/platformsupport/scenes/CMakeLists.txt \
b/platformsupport/scenes/CMakeLists.txt index 6e560cbcd..c124c85bd 100644
--- a/platformsupport/scenes/CMakeLists.txt
+++ b/platformsupport/scenes/CMakeLists.txt
@@ -1,2 +1,6 @@
 add_subdirectory(qpainter)
 add_subdirectory(opengl)
+
+if (Vulkan_FOUND)
+    add_subdirectory(vulkan)
+endif()
diff --git a/platformsupport/scenes/vulkan/CMakeLists.txt \
b/platformsupport/scenes/vulkan/CMakeLists.txt new file mode 100644
index 000000000..6c5100ae6
--- /dev/null
+++ b/platformsupport/scenes/vulkan/CMakeLists.txt
@@ -0,0 +1,16 @@
+set(SCENE_VULKAN_BACKEND_SRCS backend.cpp)
+
+include(ECMQtDeclareLoggingCategory)
+ecm_qt_declare_logging_category(SCENE_VULKAN_BACKEND_SRCS
+    HEADER
+        logging.h
+    IDENTIFIER
+        KWIN_VULKAN
+    CATEGORY_NAME
+        kwin_scene_vulkan
+    DEFAULT_SEVERITY
+        Critical
+)
+
+add_library(SceneVulkanBackend STATIC ${SCENE_VULKAN_BACKEND_SRCS})
+target_link_libraries(SceneVulkanBackend Qt5::Core)
diff --git a/platformsupport/scenes/vulkan/backend.cpp \
b/platformsupport/scenes/vulkan/backend.cpp new file mode 100644
index 000000000..ccb94b834
--- /dev/null
+++ b/platformsupport/scenes/vulkan/backend.cpp
@@ -0,0 +1,64 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright (C) 2017 Fredrik Höglund <fredrik@kde.org>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*********************************************************************/
+
+#include "backend.h"
+
+namespace KWin {
+
+
+VulkanBackend::VulkanBackend()
+{
+}
+
+
+VulkanBackend::~VulkanBackend()
+{
+}
+
+
+OverlayWindow *VulkanBackend::overlayWindow()
+{
+    return nullptr;
+}
+
+
+void VulkanBackend::showOverlay()
+{
+}
+
+
+bool VulkanBackend::perScreenRendering() const
+{
+    return false;
+}
+
+
+void VulkanBackend::screenGeometryChanged(const QSize &)
+{
+}
+
+
+bool VulkanBackend::isValid() const
+{
+    return false;
+}
+
+
+} // namespace KWin
diff --git a/platformsupport/scenes/vulkan/backend.h \
b/platformsupport/scenes/vulkan/backend.h new file mode 100644
index 000000000..344d8d40d
--- /dev/null
+++ b/platformsupport/scenes/vulkan/backend.h
@@ -0,0 +1,118 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright (C) 2017 Fredrik Höglund <fredrik@kde.org>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*********************************************************************/
+
+#ifndef BACKEND_H
+#define BACKEND_H
+
+#include <memory>
+
+class QByteArray;
+class QSize;
+
+namespace KWin
+{
+
+class OverlayWindow;
+class VulkanInstance;
+class VulkanSurface;
+class VulkanPhysicalDevice;
+
+class VulkanBackend
+{
+public:
+    /**
+     * Destroys the Vulkan backend
+     */
+    virtual ~VulkanBackend();
+
+    /**
+     * Returns a pointer to the overlay window, or nullptr if the backend does
+     * not use an overlay window.
+     */
+    virtual OverlayWindow *overlayWindow();
+
+    /**
+     * Returns true if the backend uses an overlay window, and false otherwise.
+     */
+    virtual bool usesOverlayWindow() const = 0;
+
+    /**
+     * Shows the overlay window.
+     */
+    virtual void showOverlay();
+
+    /**
+     * Returns true if each screen has a separate surface, and false otherwise.
+     */
+    virtual bool perScreenRendering() const;
+
+    /**
+     * Notifies the backend that the screen has been resized.
+     */
+    virtual void screenGeometryChanged(const QSize &size);
+
+    /**
+     * Sets the Vulkan instance that will be used to create Vulkan surfaces.
+     */
+    void setInstance(VulkanInstance *instance) { m_instance = instance; }
+
+    /**
+     * Returns the name of the platform surface extension for the backend, i.e.
+     * VK_KHR_xcb_surface, VK_KHR_wayland_surface etc.
+     */
+    virtual QByteArray platformSurfaceExtensionName() const = 0;
+
+    /**
+     * Returns true if the given queue family in the given physical device supports
+     * presenting images to the surface, and false otherwise.
+     */
+    virtual bool getPhysicalDevicePresentationSupport(VulkanPhysicalDevice \
physicalDevice, +                                                      uint32_t \
queueFamilyIndex) const = 0; +
+    /**
+     * Creates the Vulkan surface(s).
+     *
+     * Returns true if successful, and false otherwise.
+     */
+    virtual bool createSurfaces() = 0;
+
+    /**
+     * Returns a handle to the Vulkan surface.
+     */
+    virtual std::shared_ptr<VulkanSurface> surface() const = 0;
+
+    /**
+     * Returns true if the backend is valid, and false otherwise.
+     */
+    virtual bool isValid() const;
+
+protected:
+    /**
+     * Creates the Vulkan backend.
+     */
+    VulkanBackend();
+
+protected:
+    VulkanInstance *m_instance = nullptr;
+};
+
+} // namespace KWin
+
+#endif // BACKEND_H
diff --git a/plugins/platforms/wayland/CMakeLists.txt \
b/plugins/platforms/wayland/CMakeLists.txt index 1fc8e3639..20546c00a 100644
--- a/plugins/platforms/wayland/CMakeLists.txt
+++ b/plugins/platforms/wayland/CMakeLists.txt
@@ -9,6 +9,11 @@ if(HAVE_WAYLAND_EGL)
 endif()
 
 include_directories(${CMAKE_SOURCE_DIR}/platformsupport/scenes/opengl)
+
+if(Vulkan_FOUND)
+    set(WAYLAND_BACKEND_SOURCES ${WAYLAND_BACKEND_SOURCES} \
wayland_vulkan_backend.cpp) +endif()
+
 add_library(KWinWaylandWaylandBackend MODULE ${WAYLAND_BACKEND_SOURCES})
 target_link_libraries(KWinWaylandWaylandBackend kwin KF5::WaylandClient \
SceneQPainterBackend)  
@@ -16,6 +21,10 @@ if(HAVE_WAYLAND_EGL)
     target_link_libraries(KWinWaylandWaylandBackend SceneOpenGLBackend Wayland::Egl)
 endif()
 
+if(Vulkan_FOUND)
+    target_link_libraries(KWinWaylandWaylandBackend SceneVulkanBackend \
kwinvulkanutils) +endif()
+
 install(
     TARGETS
         KWinWaylandWaylandBackend
diff --git a/plugins/platforms/wayland/wayland_backend.cpp \
b/plugins/platforms/wayland/wayland_backend.cpp index 1beec1710..1d720e714 100644
--- a/plugins/platforms/wayland/wayland_backend.cpp
+++ b/plugins/platforms/wayland/wayland_backend.cpp
@@ -31,6 +31,9 @@ along with this program.  If not, see \
<http://www.gnu.org/licenses/>.  #if HAVE_WAYLAND_EGL
 #include "egl_wayland_backend.h"
 #endif
+#if HAVE_VULKAN
+#include "wayland_vulkan_backend.h"
+#endif
 #include <KWayland/Client/buffer.h>
 #include <KWayland/Client/compositor.h>
 #include <KWayland/Client/connection_thread.h>
@@ -582,6 +585,15 @@ QPainterBackend *WaylandBackend::createQPainterBackend()
     return new WaylandQPainterBackend(this);
 }
 
+VulkanBackend *WaylandBackend::createVulkanBackend()
+{
+#if HAVE_VULKAN
+    return new WaylandVulkanBackend(this);
+#else
+    return nullptr;
+#endif
+}
+
 void WaylandBackend::flush()
 {
     if (m_connectionThreadObject) {
@@ -655,11 +667,15 @@ void WaylandBackend::updateWindowTitle()
 
 QVector<CompositingType> WaylandBackend::supportedCompositors() const
 {
+    QVector<CompositingType> types;
 #if HAVE_WAYLAND_EGL
-    return QVector<CompositingType>{OpenGLCompositing, QPainterCompositing};
-#else
-    return QVector<CompositingType>{QPainterCompositing};
+    types << OpenGLCompositing;
+#endif
+#if HAVE_VULKAN
+    types << VulkanCompositing;
 #endif
+    types << QPainterCompositing;
+    return types;
 }
 
 
diff --git a/plugins/platforms/wayland/wayland_backend.h \
b/plugins/platforms/wayland/wayland_backend.h index 898479c33..7dc1c525d 100644
--- a/plugins/platforms/wayland/wayland_backend.h
+++ b/plugins/platforms/wayland/wayland_backend.h
@@ -139,6 +139,7 @@ public:
     Screens *createScreens(QObject *parent = nullptr) override;
     OpenGLBackend *createOpenGLBackend() override;
     QPainterBackend *createQPainterBackend() override;
+    VulkanBackend *createVulkanBackend() override;
 
     QSize screenSize() const override {
         return shellSurfaceSize();
diff --git a/plugins/platforms/wayland/wayland_vulkan_backend.cpp \
b/plugins/platforms/wayland/wayland_vulkan_backend.cpp new file mode 100644
index 000000000..f49c3b4d4
--- /dev/null
+++ b/plugins/platforms/wayland/wayland_vulkan_backend.cpp
@@ -0,0 +1,87 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright (C) 2017 Fredrik Höglund <fredrik@kde.org>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*********************************************************************/
+
+#include "wayland_vulkan_backend.h"
+#include "wayland_backend.h"
+
+#include <KWayland/Client/surface.h>
+
+#include "kwinvulkanutils.h"
+
+namespace KWin
+{
+
+
+WaylandVulkanBackend::WaylandVulkanBackend(Wayland::WaylandBackend *platform)
+    : VulkanBackend(),
+      m_platform(platform)
+{
+}
+
+
+WaylandVulkanBackend::~WaylandVulkanBackend()
+{
+}
+
+
+bool WaylandVulkanBackend::usesOverlayWindow() const
+{
+    return false;
+}
+
+
+QByteArray WaylandVulkanBackend::platformSurfaceExtensionName() const
+{
+    return QByteArrayLiteral(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME);
+}
+
+
+bool WaylandVulkanBackend::createSurfaces()
+{
+    m_surface = std::make_shared<VulkanSurface>(m_instance, m_platform->display(), \
*m_platform->surface()); +    return m_surface->isValid();
+}
+
+
+std::shared_ptr<VulkanSurface> WaylandVulkanBackend::surface() const
+{
+    return m_surface;
+}
+
+
+bool WaylandVulkanBackend::getPhysicalDevicePresentationSupport(VulkanPhysicalDevice \
device, uint32_t queueFamilyIndex) const +{
+    return device.getWaylandPresentationSupportKHR(queueFamilyIndex, \
m_platform->display()); +}
+
+
+void WaylandVulkanBackend::screenGeometryChanged(const QSize &size)
+{
+    Q_UNUSED(size)
+}
+
+
+bool WaylandVulkanBackend::isValid() const
+{
+    return true;
+}
+
+
+} // namespace KWin
diff --git a/plugins/platforms/wayland/wayland_vulkan_backend.h \
b/plugins/platforms/wayland/wayland_vulkan_backend.h new file mode 100644
index 000000000..08c7031ea
--- /dev/null
+++ b/plugins/platforms/wayland/wayland_vulkan_backend.h
@@ -0,0 +1,62 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright (C) 2017 Fredrik Höglund <fredrik@kde.org>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*********************************************************************/
+
+#ifndef VULKAN_WAYLAND_BACKEND_H
+#define VULKAN_WAYLAND_BACKEND_H
+
+#include "platformsupport/scenes/vulkan/backend.h"
+
+#include <memory>
+
+namespace KWin
+{
+
+namespace Wayland {
+    class WaylandBackend;
+}
+
+class VulkanSurface;
+
+/**
+ * Vulkan backend
+ **/
+class WaylandVulkanBackend : public VulkanBackend
+{
+public:
+    explicit WaylandVulkanBackend(Wayland::WaylandBackend *platform);
+    ~WaylandVulkanBackend() override;
+
+    bool usesOverlayWindow() const override;
+    void screenGeometryChanged(const QSize &size) override;
+    QByteArray platformSurfaceExtensionName() const override;
+    bool getPhysicalDevicePresentationSupport(VulkanPhysicalDevice device,
+                                              uint32_t queueFamilyIndex) const \
override; +    bool createSurfaces() override;
+    std::shared_ptr<VulkanSurface> surface() const override;
+    bool isValid() const override;
+
+private:
+    Wayland::WaylandBackend *m_platform;
+    std::shared_ptr<VulkanSurface> m_surface;
+};
+
+} // namespace KWin
+
+#endif // X11VULKANBACKEND_H
diff --git a/plugins/platforms/x11/standalone/CMakeLists.txt \
b/plugins/platforms/x11/standalone/CMakeLists.txt index a1ec49608..2e466deac 100644
--- a/plugins/platforms/x11/standalone/CMakeLists.txt
+++ b/plugins/platforms/x11/standalone/CMakeLists.txt
@@ -23,7 +23,13 @@ if(HAVE_EPOXY_GLX)
     set(X11PLATFORM_SOURCES ${X11PLATFORM_SOURCES} glxbackend.cpp \
glx_context_attribute_builder.cpp)  endif()
 
+
 include_directories(${CMAKE_SOURCE_DIR}/platformsupport/scenes/opengl)
+include_directories(${CMAKE_SOURCE_DIR}/platformsupport/scenes/vulkan)
+
+if (Vulkan_FOUND)
+    set(X11PLATFORM_SOURCES ${X11PLATFORM_SOURCES} vulkanbackend.cpp)
+endif()
 
 add_library(KWinX11Platform MODULE ${X11PLATFORM_SOURCES})
 target_link_libraries(KWinX11Platform eglx11common kwin kwinxrenderutils \
SceneOpenGLBackend Qt5::X11Extras XCB::CURSOR KF5::Crash) @@ -31,6 +37,10 @@ \
if(X11_Xinput_FOUND)  target_link_libraries(KWinX11Platform ${X11_Xinput_LIB})
 endif()
 
+if (Vulkan_FOUND)
+    target_link_libraries(KWinX11Platform SceneVulkanBackend kwinvulkanutils)
+endif()
+
 if(HAVE_DL_LIBRARY)
     target_link_libraries(KWinX11Platform ${DL_LIBRARY})
 endif()
diff --git a/plugins/platforms/x11/standalone/vulkanbackend.cpp \
b/plugins/platforms/x11/standalone/vulkanbackend.cpp new file mode 100644
index 000000000..854c90004
--- /dev/null
+++ b/plugins/platforms/x11/standalone/vulkanbackend.cpp
@@ -0,0 +1,130 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright (C) 2017 Fredrik Höglund <fredrik@kde.org>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*********************************************************************/
+
+#include "vulkanbackend.h"
+#include "kwinvulkanutils.h"
+
+#include "x11_platform.h"
+#include "overlaywindow_x11.h"
+#include "logging.h"
+#include "screens.h"
+
+namespace KWin
+{
+
+
+X11VulkanBackend::X11VulkanBackend(X11StandalonePlatform *platform)
+    : VulkanBackend(),
+      m_platform(platform),
+      m_overlayWindow(platform->createOverlayWindow())
+{
+}
+
+
+X11VulkanBackend::~X11VulkanBackend()
+{
+    if (m_window)
+        xcb_destroy_window(connection(), m_window);
+
+    overlayWindow()->destroy();
+    delete m_overlayWindow;
+}
+
+
+OverlayWindow *X11VulkanBackend::overlayWindow()
+{
+    return m_overlayWindow;
+}
+
+
+bool X11VulkanBackend::usesOverlayWindow() const
+{
+    return true;
+}
+
+
+void X11VulkanBackend::showOverlay()
+{
+    if (m_overlayWindow && m_overlayWindow->window())
+        m_overlayWindow->show();
+}
+
+
+QByteArray X11VulkanBackend::platformSurfaceExtensionName() const
+{
+    return QByteArrayLiteral(VK_KHR_XCB_SURFACE_EXTENSION_NAME);
+}
+
+
+bool X11VulkanBackend::createSurfaces()
+{
+    if (!overlayWindow()->create()) {
+        qCCritical(KWIN_X11STANDALONE) << "Failed to create overlay window";
+        return false;
+    }
+
+    xcb_connection_t * const c = connection();
+    const QSize size = screens()->size();
+
+    m_window = xcb_generate_id(c);
+    xcb_create_window(c, XCB_COPY_FROM_PARENT, m_window, overlayWindow()->window(),
+                      0, 0, size.width(), size.height(), 0, \
XCB_WINDOW_CLASS_INPUT_OUTPUT, +                      XCB_COPY_FROM_PARENT, 0, \
nullptr); +
+    m_surface = std::make_shared<VulkanSurface>(m_instance, c, m_window);
+    if (!m_surface->isValid()) {
+        qCCritical(KWIN_X11STANDALONE) << "Failed to create Vulkan surface";
+        return false;
+    }
+
+    overlayWindow()->setup(m_window);
+    return true;
+}
+
+
+std::shared_ptr<VulkanSurface> X11VulkanBackend::surface() const
+{
+    return m_surface;
+}
+
+
+bool X11VulkanBackend::getPhysicalDevicePresentationSupport(VulkanPhysicalDevice \
device, uint32_t queueFamilyIndex) const +{
+    return device.getXcbPresentationSupportKHR(queueFamilyIndex, connection(), \
defaultScreen()->root_visual); +}
+
+
+void X11VulkanBackend::screenGeometryChanged(const QSize &size)
+{
+    // Resize the X window
+    const uint16_t mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | \
XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT; +    const uint32_t values[] = { \
0, 0, (uint32_t) size.width(), (uint32_t) size.height() }; +
+    xcb_configure_window(connection(), m_window, mask, values);
+    overlayWindow()->setup(m_window);
+}
+
+
+bool X11VulkanBackend::isValid() const
+{
+    return true;
+}
+
+} // namespace KWin
diff --git a/plugins/platforms/x11/standalone/vulkanbackend.h \
b/plugins/platforms/x11/standalone/vulkanbackend.h new file mode 100644
index 000000000..fa1358c8f
--- /dev/null
+++ b/plugins/platforms/x11/standalone/vulkanbackend.h
@@ -0,0 +1,63 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright (C) 2017 Fredrik Höglund <fredrik@kde.org>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*********************************************************************/
+
+#ifndef X11_VULKAN_BACKEND_H
+#define X11_VULKAN_BACKEND_H
+
+#include <xcb/xproto.h>
+#include "platformsupport/scenes/vulkan/backend.h"
+
+
+namespace KWin
+{
+
+class X11StandalonePlatform;
+class OverlayWindow;
+
+/**
+ * Vulkan backend
+ **/
+class X11VulkanBackend : public VulkanBackend
+{
+public:
+    explicit X11VulkanBackend(X11StandalonePlatform *platform);
+    ~X11VulkanBackend() override;
+
+    OverlayWindow* overlayWindow() override;
+    bool usesOverlayWindow() const override;
+    void showOverlay() override;
+    void screenGeometryChanged(const QSize &size) override;
+    QByteArray platformSurfaceExtensionName() const override;
+    bool getPhysicalDevicePresentationSupport(VulkanPhysicalDevice device,
+                                              uint32_t queueFamilyIndex) const \
override; +    bool createSurfaces() override;
+    std::shared_ptr<VulkanSurface> surface() const override;
+    bool isValid() const override;
+
+private:
+    X11StandalonePlatform *m_platform;
+    OverlayWindow *m_overlayWindow;
+    std::shared_ptr<VulkanSurface> m_surface;
+    xcb_window_t m_window;
+};
+
+} // namespace KWin
+
+#endif // X11_VULKAN_BACKEND_H
diff --git a/plugins/platforms/x11/standalone/x11_platform.cpp \
b/plugins/platforms/x11/standalone/x11_platform.cpp index 7439ed246..db4d4c03d 100644
--- a/plugins/platforms/x11/standalone/x11_platform.cpp
+++ b/plugins/platforms/x11/standalone/x11_platform.cpp
@@ -27,6 +27,9 @@ along with this program.  If not, see \
<http://www.gnu.org/licenses/>.  #if HAVE_EPOXY_GLX
 #include "glxbackend.h"
 #endif
+#if HAVE_VULKAN
+#include "vulkanbackend.h"
+#endif
 #if HAVE_X11_XINPUT
 #include "xinputintegration.h"
 #endif
@@ -130,6 +133,15 @@ OpenGLBackend *X11StandalonePlatform::createOpenGLBackend()
     }
 }
 
+VulkanBackend *X11StandalonePlatform::createVulkanBackend()
+{
+#ifdef HAVE_VULKAN
+    return new X11VulkanBackend(this);
+#else
+    return nullptr;
+#endif
+}
+
 Edge *X11StandalonePlatform::createScreenEdge(ScreenEdges *edges)
 {
     if (m_screenEdgesFilter.isNull()) {
@@ -431,6 +443,9 @@ QVector<CompositingType> \
X11StandalonePlatform::supportedCompositors() const  #if HAVE_EPOXY_GLX
     compositors << OpenGLCompositing;
 #endif
+#ifdef HAVE_VULKAN
+    compositors << VulkanCompositing;
+#endif
 #ifdef KWIN_HAVE_XRENDER_COMPOSITING
     compositors << XRenderCompositing;
 #endif
diff --git a/plugins/platforms/x11/standalone/x11_platform.h \
b/plugins/platforms/x11/standalone/x11_platform.h index 3cfdb47df..b5badb917 100644
--- a/plugins/platforms/x11/standalone/x11_platform.h
+++ b/plugins/platforms/x11/standalone/x11_platform.h
@@ -46,6 +46,7 @@ public:
 
     Screens *createScreens(QObject *parent = nullptr) override;
     OpenGLBackend *createOpenGLBackend() override;
+    VulkanBackend *createVulkanBackend() override;
     Edge *createScreenEdge(ScreenEdges *parent) override;
     void createPlatformCursor(QObject *parent = nullptr) override;
     bool requiresCompositing() const override;
diff --git a/plugins/platforms/x11/windowed/CMakeLists.txt \
b/plugins/platforms/x11/windowed/CMakeLists.txt index 8236c6b48..2cf7fff14 100644
--- a/plugins/platforms/x11/windowed/CMakeLists.txt
+++ b/plugins/platforms/x11/windowed/CMakeLists.txt
@@ -6,9 +6,18 @@ set(X11BACKEND_SOURCES
 )
 
 include_directories(${CMAKE_SOURCE_DIR}/platformsupport/scenes/opengl)
+
+if(Vulkan_FOUND)
+    set(X11BACKEND_SOURCES ${X11BACKEND_SOURCES} x11_vulkan_backend.cpp)
+endif()
+
 add_library(KWinWaylandX11Backend MODULE ${X11BACKEND_SOURCES})
 target_link_libraries(KWinWaylandX11Backend eglx11common kwin kwinxrenderutils \
X11::XCB SceneQPainterBackend SceneOpenGLBackend)  
+if(Vulkan_FOUND)
+target_link_libraries(KWinWaylandX11Backend kwin X11::XCB SceneVulkanBackend \
kwinvulkanutils) +endif()
+
 install(
     TARGETS
         KWinWaylandX11Backend
diff --git a/plugins/platforms/x11/windowed/x11_vulkan_backend.cpp \
b/plugins/platforms/x11/windowed/x11_vulkan_backend.cpp new file mode 100644
index 000000000..5947b0ae4
--- /dev/null
+++ b/plugins/platforms/x11/windowed/x11_vulkan_backend.cpp
@@ -0,0 +1,102 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright (C) 2017 Fredrik Höglund <fredrik@kde.org>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*********************************************************************/
+
+#include "x11_vulkan_backend.h"
+
+#include "x11windowed_backend.h"
+#include "kwinvulkanutils.h"
+
+#include "screens.h"
+
+namespace KWin
+{
+
+X11VulkanBackend::X11VulkanBackend(X11WindowedBackend *platform)
+    : VulkanBackend(),
+      m_platform(platform)
+{
+}
+
+
+X11VulkanBackend::~X11VulkanBackend()
+{
+}
+
+
+bool X11VulkanBackend::usesOverlayWindow() const
+{
+    return false;
+}
+
+
+QByteArray X11VulkanBackend::platformSurfaceExtensionName() const
+{
+    return QByteArrayLiteral(VK_KHR_XCB_SURFACE_EXTENSION_NAME);
+}
+
+
+bool X11VulkanBackend::createSurfaces()
+{
+    m_surfaces.reserve(screens()->count());
+
+    for (int i = 0; i < screens()->count(); ++i) {
+        auto surface = std::make_shared<VulkanSurface>(m_instance, \
m_platform->connection(), m_platform->windowForScreen(i)); +
+        if (!surface->isValid())
+            surface = nullptr;
+
+        m_surfaces.emplace_back(surface);
+    }
+
+    if (m_surfaces.empty())
+        return false;
+
+    return true;
+}
+
+
+std::shared_ptr<VulkanSurface> X11VulkanBackend::surface() const
+{
+    if (m_surfaces.empty())
+        return nullptr;
+
+    return m_surfaces.front();
+}
+
+
+bool X11VulkanBackend::getPhysicalDevicePresentationSupport(VulkanPhysicalDevice \
device, uint32_t queueFamilyIndex) const +{
+    return device.getXcbPresentationSupportKHR(queueFamilyIndex, \
m_platform->connection(), m_platform->visual()); +}
+
+
+void X11VulkanBackend::screenGeometryChanged(const QSize &size)
+{
+    Q_UNUSED(size)
+}
+
+
+bool X11VulkanBackend::isValid() const
+{
+    return true;
+}
+
+
+} // namespace KWin
diff --git a/plugins/platforms/x11/windowed/x11_vulkan_backend.h \
b/plugins/platforms/x11/windowed/x11_vulkan_backend.h new file mode 100644
index 000000000..09d6fab5a
--- /dev/null
+++ b/plugins/platforms/x11/windowed/x11_vulkan_backend.h
@@ -0,0 +1,60 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright (C) 2017 Fredrik Höglund <fredrik@kde.org>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*********************************************************************/
+
+#ifndef X11_VULKAN_BACKEND_H
+#define X11_VULKAN_BACKEND_H
+
+#include <memory>
+#include <vector>
+
+#include "platformsupport/scenes/vulkan/backend.h"
+
+namespace KWin
+{
+
+class X11WindowedBackend;
+class VulkanSurface;
+
+/**
+ * Vulkan backend
+ **/
+class X11VulkanBackend : public VulkanBackend
+{
+public:
+    explicit X11VulkanBackend(X11WindowedBackend *platform);
+    ~X11VulkanBackend() override;
+
+    bool usesOverlayWindow() const override;
+    void screenGeometryChanged(const QSize &size) override;
+    QByteArray platformSurfaceExtensionName() const override;
+    bool getPhysicalDevicePresentationSupport(VulkanPhysicalDevice device,
+                                              uint32_t queueFamilyIndex) const \
override; +    bool createSurfaces() override;
+    std::shared_ptr<VulkanSurface> surface() const override;
+    bool isValid() const override;
+
+private:
+    X11WindowedBackend *m_platform;
+    std::vector<std::shared_ptr<VulkanSurface>> m_surfaces;
+};
+
+} // namespace KWin
+
+#endif // X11_VULKAN_BACKEND_H
diff --git a/plugins/platforms/x11/windowed/x11windowed_backend.cpp \
b/plugins/platforms/x11/windowed/x11windowed_backend.cpp index 95306b027..6d22552fb \
                100644
--- a/plugins/platforms/x11/windowed/x11windowed_backend.cpp
+++ b/plugins/platforms/x11/windowed/x11windowed_backend.cpp
@@ -17,6 +17,7 @@ GNU General Public License for more details.
 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *********************************************************************/
+#include <config-kwin.h>
 #include "x11windowed_backend.h"
 #include "scene_qpainter_x11_backend.h"
 #include "logging.h"
@@ -25,6 +26,9 @@ along with this program.  If not, see \
<http://www.gnu.org/licenses/>.  #include "egl_x11_backend.h"
 #include "screens.h"
 #include <kwinxrenderutils.h>
+#if HAVE_VULKAN
+#include "x11_vulkan_backend.h"
+#endif
 // KDE
 #include <KLocalizedString>
 #include <NETWM>
@@ -454,6 +458,15 @@ QPainterBackend *X11WindowedBackend::createQPainterBackend()
     return new X11WindowedQPainterBackend(this);
 }
 
+VulkanBackend *X11WindowedBackend::createVulkanBackend()
+{
+#if HAVE_VULKAN
+    return new X11VulkanBackend(this);
+#else
+    return nullptr;
+#endif
+}
+
 void X11WindowedBackend::warpPointer(const QPointF &globalPos)
 {
     const xcb_window_t w = m_windows.at(0).window;
@@ -461,6 +474,19 @@ void X11WindowedBackend::warpPointer(const QPointF &globalPos)
     xcb_flush(m_connection);
 }
 
+QVector<CompositingType> X11WindowedBackend::supportedCompositors() const
+{
+    QVector<CompositingType> types;
+
+    types << OpenGLCompositing;
+#ifdef HAVE_VULKAN
+    types << VulkanCompositing;
+#endif
+    types << QPainterCompositing;
+
+    return types;
+}
+
 xcb_window_t X11WindowedBackend::windowForScreen(int screen) const
 {
     if (screen > m_windows.count()) {
diff --git a/plugins/platforms/x11/windowed/x11windowed_backend.h \
b/plugins/platforms/x11/windowed/x11windowed_backend.h index e17149a79..e17ff31c8 \
                100644
--- a/plugins/platforms/x11/windowed/x11windowed_backend.h
+++ b/plugins/platforms/x11/windowed/x11windowed_backend.h
@@ -64,14 +64,17 @@ public:
     }
     xcb_window_t rootWindow() const;
 
+    xcb_visualid_t visual() const {
+        return m_screen->root_visual;
+    }
+
     Screens *createScreens(QObject *parent = nullptr) override;
     OpenGLBackend *createOpenGLBackend() override;
     QPainterBackend* createQPainterBackend() override;
+    VulkanBackend* createVulkanBackend() override;
     void warpPointer(const QPointF &globalPos) override;
 
-    QVector<CompositingType> supportedCompositors() const override {
-        return QVector<CompositingType>{OpenGLCompositing, QPainterCompositing};
-    }
+    QVector<CompositingType> supportedCompositors() const override;
 
 Q_SIGNALS:
     void sizeChanged();
diff --git a/plugins/scenes/CMakeLists.txt b/plugins/scenes/CMakeLists.txt
index eb84a43ce..4636d2dd9 100644
--- a/plugins/scenes/CMakeLists.txt
+++ b/plugins/scenes/CMakeLists.txt
@@ -1,5 +1,8 @@
 add_subdirectory(opengl)
 add_subdirectory(qpainter)
+if (Vulkan_FOUND)
+    add_subdirectory(vulkan)
+endif()
 if( KWIN_BUILD_XRENDER_COMPOSITING )
     add_subdirectory(xrender)
 endif()
diff --git a/plugins/scenes/vulkan/CMakeLists.txt \
b/plugins/scenes/vulkan/CMakeLists.txt new file mode 100644
index 000000000..efc02eb66
--- /dev/null
+++ b/plugins/scenes/vulkan/CMakeLists.txt
@@ -0,0 +1,35 @@
+set(SCENE_VULKAN_SRCS
+    scene.cpp
+    swapchain.cpp
+    descriptorset.cpp
+    window.cpp
+    windowpixmap.cpp
+    shadow.cpp
+    effectframe.cpp
+    decorationrenderer.cpp)
+
+include(ECMQtDeclareLoggingCategory)
+ecm_qt_declare_logging_category(
+    SCENE_VULKAN_SRCS HEADER
+        logging.h
+    IDENTIFIER
+        KWIN_VULKAN
+    CATEGORY_NAME
+        kwin_vulkan_scene
+    DEFAULT_SEVERITY
+        Debug
+)
+
+add_library(KWinSceneVulkan MODULE ${SCENE_VULKAN_SRCS})
+target_link_libraries(KWinSceneVulkan
+    kwin
+    SceneVulkanBackend
+    kwinvulkanutils
+)
+
+install(
+    TARGETS
+        KWinSceneVulkan
+    DESTINATION
+        ${PLUGIN_INSTALL_DIR}/org.kde.kwin.scenes/
+)
diff --git a/plugins/scenes/vulkan/decorationrenderer.cpp \
b/plugins/scenes/vulkan/decorationrenderer.cpp new file mode 100644
index 000000000..1ef75243b
--- /dev/null
+++ b/plugins/scenes/vulkan/decorationrenderer.cpp
@@ -0,0 +1,50 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright  © 2017-2018 Fredrik Höglund <fredrik@kde.org>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*********************************************************************/
+
+#include "decorationrenderer.h"
+
+namespace KWin
+{
+
+
+VulkanDecorationRenderer::VulkanDecorationRenderer(Decoration::DecoratedClientImpl \
*client, VulkanScene *scene) +    : Renderer(client),
+      m_scene(scene)
+{
+}
+
+
+VulkanDecorationRenderer::~VulkanDecorationRenderer()
+{
+}
+
+
+void VulkanDecorationRenderer::render()
+{
+}
+
+
+void VulkanDecorationRenderer::reparent(Deleted *deleted)
+{
+    Renderer::reparent(deleted);
+}
+
+
+} // namespace KWin
diff --git a/plugins/scenes/vulkan/decorationrenderer.h \
b/plugins/scenes/vulkan/decorationrenderer.h new file mode 100644
index 000000000..1ab9dccb6
--- /dev/null
+++ b/plugins/scenes/vulkan/decorationrenderer.h
@@ -0,0 +1,58 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright  © 2017-2018 Fredrik Höglund <fredrik@kde.org>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*********************************************************************/
+
+#ifndef DECORATIONRENDERER_H
+#define DECORATIONRENDERER_H
+
+#include "scene.h"
+
+namespace KWin
+{
+
+class VulkanScene;
+
+class VulkanDecorationRenderer : public Decoration::Renderer
+{
+    Q_OBJECT
+
+public:
+    enum class DecorationPart : int {
+        Left = 0,
+        Top,
+        Right,
+        Bottom,
+        Count
+    };
+
+    explicit VulkanDecorationRenderer(Decoration::DecoratedClientImpl *client, \
VulkanScene *scene); +    ~VulkanDecorationRenderer() override;
+
+    VulkanScene *scene() const { return m_scene; }
+
+    void render() override;
+    void reparent(Deleted *deleted) override;
+
+private:
+    VulkanScene *m_scene;
+};
+
+} // namespace KWin
+
+#endif // DECORATIONRENDERER_H
diff --git a/plugins/scenes/vulkan/descriptorset.cpp \
b/plugins/scenes/vulkan/descriptorset.cpp new file mode 100644
index 000000000..22e1e2fe8
--- /dev/null
+++ b/plugins/scenes/vulkan/descriptorset.cpp
@@ -0,0 +1,305 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright  © 2017-2018 Fredrik Höglund <fredrik@kde.org>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*********************************************************************/
+
+#include "descriptorset.h"
+
+namespace KWin {
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
+
+
+SceneDescriptorPool::SceneDescriptorPool(const CreateInfo &createInfo)
+    : m_device(createInfo.device),
+      m_poolSizes(createInfo.poolSizes),
+      m_layout(createInfo.descriptorSetLayout),
+      m_maxSets(createInfo.maxSets)
+{
+}
+
+
+SceneDescriptorPool::~SceneDescriptorPool()
+{
+    for (VkDescriptorPool pool : m_pools)
+        m_device->destroyDescriptorPool(pool, nullptr);
+}
+
+
+VkDescriptorSet SceneDescriptorPool::allocateDescriptorSet()
+{
+    if (m_unusedSets.size() > 0) {
+        const VkDescriptorSet set = m_unusedSets.front();
+        m_unusedSets.pop();
+        return set;
+    }
+
+    // Create a new descriptor pool
+    if (m_available == 0) {
+        // Note that we don't set VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT
+        const VkDescriptorPoolCreateInfo createInfo = {
+            .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
+            .pNext = nullptr,
+            .flags = 0,
+            .maxSets = m_maxSets,
+            .poolSizeCount = (uint32_t) m_poolSizes.size(),
+            .pPoolSizes = m_poolSizes.data()
+        };
+
+        VkDescriptorPool pool;
+        m_device->createDescriptorPool(&createInfo, nullptr, &pool);
+
+        m_pools.push_back(pool);
+        m_available = m_maxSets;
+    }
+
+    // Allocate a new descriptor set from the pool
+    const VkDescriptorSetAllocateInfo allocInfo = {
+        .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
+        .pNext = nullptr,
+        .descriptorPool = m_pools.back(),
+        .descriptorSetCount = 1,
+        .pSetLayouts = &m_layout
+    };
+
+    VkDescriptorSet set;
+    m_device->allocateDescriptorSets(&allocInfo, &set);
+
+    --m_available;
+    return set;
+}
+
+
+void SceneDescriptorPool::freeDescriptorSet(VkDescriptorSet set)
+{
+    m_unusedSets.push(set);
+}
+
+
+
+// ------------------------------------------------------------------
+
+
+
+TextureDescriptorSet::TextureDescriptorSet(SceneDescriptorPool *pool)
+    : m_pool(pool)
+{
+    m_set = m_pool->allocateDescriptorSet();
+}
+
+
+TextureDescriptorSet::~TextureDescriptorSet()
+{
+    if (m_pool && m_set)
+        m_pool->freeDescriptorSet(m_set);
+}
+
+
+void TextureDescriptorSet::update(VkSampler sampler,
+                                      const std::shared_ptr<VulkanImageView> \
&imageView, +                                      VkImageLayout imageLayout,
+                                      const std::shared_ptr<VulkanBuffer> \
&uniformBuffer) +{
+    const VkDescriptorBufferInfo uniformBufferInfo = {
+        .buffer = uniformBuffer->handle(),
+        .offset = 0,
+        .range = sizeof(VulkanPipelineManager::TextureUniformData)
+    };
+
+    const VkDescriptorImageInfo imageInfo = {
+        .sampler = sampler,
+        .imageView = imageView->handle(),
+        .imageLayout = imageLayout
+    };
+
+    const VkWriteDescriptorSet descriptorWrites[] = {
+        {
+            .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
+            .pNext = nullptr,
+            .dstSet = m_set,
+            .dstBinding = 0,
+            .dstArrayElement = 0,
+            .descriptorCount = 1,
+            .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+            .pImageInfo = &imageInfo,
+            .pBufferInfo = nullptr,
+            .pTexelBufferView = nullptr
+        },
+        {
+            .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
+            .pNext = nullptr,
+            .dstSet = m_set,
+            .dstBinding = 1,
+            .dstArrayElement = 0,
+            .descriptorCount = 1,
+            .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
+            .pImageInfo = nullptr,
+            .pBufferInfo = &uniformBufferInfo,
+            .pTexelBufferView = nullptr
+        },
+    };
+
+    m_pool->device()->updateDescriptorSets(ARRAY_SIZE(descriptorWrites), \
descriptorWrites, 0, nullptr); +
+    m_sampler = sampler;
+    m_imageView = imageView;
+    m_imageLayout = imageLayout;
+    m_uniformBuffer = uniformBuffer;
+}
+
+
+
+// ------------------------------------------------------------------
+
+
+
+CrossFadeDescriptorSet::CrossFadeDescriptorSet(SceneDescriptorPool *pool)
+    : m_pool(pool)
+{
+    m_set = m_pool->allocateDescriptorSet();
+}
+
+
+CrossFadeDescriptorSet::~CrossFadeDescriptorSet()
+{
+    if (m_pool && m_set)
+        m_pool->freeDescriptorSet(m_set);
+}
+
+
+void CrossFadeDescriptorSet::update(VkSampler sampler,
+                                      const std::shared_ptr<VulkanImageView> \
&imageView1, +                                      const \
std::shared_ptr<VulkanImageView> &imageView2, +                                      \
VkImageLayout imageLayout, +                                      const \
std::shared_ptr<VulkanBuffer> &uniformBuffer) +{
+    const VkDescriptorBufferInfo uniformBufferInfo = {
+        .buffer = uniformBuffer->handle(),
+        .offset = 0,
+        .range  = sizeof(VulkanPipelineManager::TextureUniformData)
+    };
+
+    const VkDescriptorImageInfo imageInfo[] = {
+        {
+            .sampler = sampler,
+            .imageView = imageView1->handle(),
+            .imageLayout = imageLayout
+        },
+        {
+            .sampler = sampler,
+            .imageView = imageView2->handle(),
+            .imageLayout = imageLayout
+        }
+    };
+
+    const VkWriteDescriptorSet descriptorWrites[] = {
+        {
+            .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
+            .pNext = nullptr,
+            .dstSet = m_set,
+            .dstBinding = 0,
+            .dstArrayElement = 0,
+            .descriptorCount = 2,
+            .descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
+            .pImageInfo = imageInfo,
+            .pBufferInfo = nullptr,
+            .pTexelBufferView = nullptr
+        },
+        {
+            .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
+            .pNext = nullptr,
+            .dstSet = m_set,
+            .dstBinding = 1,
+            .dstArrayElement = 0,
+            .descriptorCount = 1,
+            .descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER,
+            .pImageInfo = imageInfo,
+            .pBufferInfo = nullptr,
+            .pTexelBufferView = nullptr
+        },
+        {
+            .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
+            .pNext = nullptr,
+            .dstSet = m_set,
+            .dstBinding = 2,
+            .dstArrayElement = 0,
+            .descriptorCount = 1,
+            .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
+            .pImageInfo = nullptr,
+            .pBufferInfo = &uniformBufferInfo,
+            .pTexelBufferView = nullptr
+        },
+    };
+
+    m_pool->device()->updateDescriptorSets(ARRAY_SIZE(descriptorWrites), \
descriptorWrites, 0, nullptr); +
+    m_sampler = sampler;
+    m_imageView1 = imageView1;
+    m_imageView2 = imageView2;
+    m_imageLayout = imageLayout;
+    m_uniformBuffer = uniformBuffer;
+}
+
+
+
+// ------------------------------------------------------------------
+
+
+
+
+ColorDescriptorSet::ColorDescriptorSet(SceneDescriptorPool *pool)
+    : m_pool(pool)
+{
+    m_set = m_pool->allocateDescriptorSet();
+}
+
+
+ColorDescriptorSet::~ColorDescriptorSet()
+{
+    if (m_pool && m_set)
+        m_pool->freeDescriptorSet(m_set);
+}
+
+
+void ColorDescriptorSet::update(const std::shared_ptr<VulkanBuffer> &uniformBuffer)
+{
+    const VkDescriptorBufferInfo uniformBufferInfo = {
+        .buffer = uniformBuffer->handle(),
+        .offset = 0,
+        .range  = sizeof(VulkanPipelineManager::ColorUniformData)
+    };
+
+    const VkWriteDescriptorSet descriptorWrite = {
+        .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
+        .pNext = nullptr,
+        .dstSet = m_set,
+        .dstBinding = 0,
+        .dstArrayElement = 0,
+        .descriptorCount = 1,
+        .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
+        .pImageInfo = nullptr,
+        .pBufferInfo = &uniformBufferInfo,
+        .pTexelBufferView = nullptr
+    };
+
+    m_pool->device()->updateDescriptorSets(1, &descriptorWrite, 0, nullptr);
+
+    m_uniformBuffer = uniformBuffer;
+}
+
+} // namespace KWin
diff --git a/plugins/scenes/vulkan/descriptorset.h \
b/plugins/scenes/vulkan/descriptorset.h new file mode 100644
index 000000000..4938d1d7e
--- /dev/null
+++ b/plugins/scenes/vulkan/descriptorset.h
@@ -0,0 +1,287 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright  © 2017-2018 Fredrik Höglund <fredrik@kde.org>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*********************************************************************/
+
+#ifndef DESCRIPTOR_SET_H
+#define DESCRIPTOR_SET_H
+
+#include "kwinvulkanutils.h"
+
+#include <deque>
+#include <queue>
+
+
+namespace KWin {
+
+
+class SceneDescriptorPool;
+
+
+/**
+ * TextureDescriptorSet encapsulates a descriptor set with a combined image-sampler
+ * and a dynamic uniform buffer binding.
+ */
+ class TextureDescriptorSet : public VulkanObject
+{
+public:
+    TextureDescriptorSet(SceneDescriptorPool *pool);
+
+    TextureDescriptorSet(const TextureDescriptorSet &other) = delete;
+
+    TextureDescriptorSet(TextureDescriptorSet &&other)
+        : m_pool(other.m_pool),
+          m_set(other.m_set),
+          m_sampler(other.m_sampler),
+          m_imageView(other.m_imageView),
+          m_imageLayout(other.m_imageLayout),
+          m_uniformBuffer(std::move(other.m_uniformBuffer))
+    {
+        other.m_pool = nullptr;
+        other.m_set = VK_NULL_HANDLE;
+        other.m_sampler = VK_NULL_HANDLE;
+        other.m_imageView = nullptr;
+        other.m_imageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+        other.m_uniformBuffer = nullptr;
+    }
+
+    ~TextureDescriptorSet() override;
+
+    void update(VkSampler sampler,
+                const std::shared_ptr<VulkanImageView> &imageView,
+                VkImageLayout imageLayout,
+                const std::shared_ptr<VulkanBuffer> &uniformBuffer);
+
+    VkSampler sampler() const { return m_sampler; }
+    std::shared_ptr<VulkanImageView> imageView() const { return m_imageView; }
+    VkImageLayout imageLayout() const { return m_imageLayout; }
+    std::shared_ptr<VulkanBuffer> uniformBuffer() const { return m_uniformBuffer; }
+
+    VkDescriptorSet handle() const { return m_set; }
+    operator VkDescriptorSet () const { return m_set; }
+
+    TextureDescriptorSet &operator = (const TextureDescriptorSet &other) = delete;
+
+    TextureDescriptorSet &operator = (TextureDescriptorSet &&other) {
+        m_pool = other.m_pool;
+        m_set = other.m_set;
+        m_sampler = other.m_sampler;
+        m_imageView = other.m_imageView;
+        m_imageLayout = other.m_imageLayout;
+        m_uniformBuffer = std::move(other.m_uniformBuffer);
+
+        other.m_pool = nullptr;
+        other.m_set = VK_NULL_HANDLE;
+        other.m_sampler = VK_NULL_HANDLE;
+        other.m_imageView = nullptr;
+        other.m_imageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+        other.m_uniformBuffer = nullptr;
+        return *this;
+    }
+
+private:
+    SceneDescriptorPool *m_pool;
+    VkDescriptorSet m_set;
+    VkSampler m_sampler;
+    std::shared_ptr<VulkanImageView> m_imageView;
+    VkImageLayout m_imageLayout;
+    std::shared_ptr<VulkanBuffer> m_uniformBuffer;
+};
+
+
+
+// ------------------------------------------------------------------
+
+
+
+/**
+ * CrossFadeDescriptorSet encapsulates a descriptor set with an array of two \
sampled-images, + * and a dynamic uniform buffer binding.
+ */
+class CrossFadeDescriptorSet : public VulkanObject
+{
+public:
+    CrossFadeDescriptorSet(SceneDescriptorPool *pool);
+
+    CrossFadeDescriptorSet(const CrossFadeDescriptorSet &other) = delete;
+
+    CrossFadeDescriptorSet(CrossFadeDescriptorSet &&other)
+        : m_pool(other.m_pool),
+          m_set(other.m_set),
+          m_sampler(other.m_sampler),
+          m_imageView1(other.m_imageView1),
+          m_imageView2(other.m_imageView2),
+          m_imageLayout(other.m_imageLayout),
+          m_uniformBuffer(std::move(other.m_uniformBuffer))
+    {
+        other.m_pool = nullptr;
+        other.m_set = VK_NULL_HANDLE;
+        other.m_sampler = VK_NULL_HANDLE;
+        other.m_imageView1 = nullptr;
+        other.m_imageView2 = nullptr;
+        other.m_imageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+        other.m_uniformBuffer = nullptr;
+    }
+
+    ~CrossFadeDescriptorSet() override;
+
+    void update(VkSampler sampler,
+                const std::shared_ptr<VulkanImageView> &imageView1,
+                const std::shared_ptr<VulkanImageView> &imageView2,
+                VkImageLayout imageLayout,
+                const std::shared_ptr<VulkanBuffer> &uniformBuffer);
+
+    VkSampler sampler() const { return m_sampler; }
+    std::shared_ptr<VulkanImageView> imageView1() const { return m_imageView1; }
+    std::shared_ptr<VulkanImageView> imageView2() const { return m_imageView2; }
+    VkImageLayout imageLayout() const { return m_imageLayout; }
+    std::shared_ptr<VulkanBuffer> uniformBuffer() const { return m_uniformBuffer; }
+
+    VkDescriptorSet handle() const { return m_set; }
+    operator VkDescriptorSet () const { return m_set; }
+
+    CrossFadeDescriptorSet &operator = (const CrossFadeDescriptorSet &other) = \
delete; +
+    CrossFadeDescriptorSet &operator = (CrossFadeDescriptorSet &&other) {
+        m_pool = other.m_pool;
+        m_set = other.m_set;
+        m_sampler = other.m_sampler;
+        m_imageView1 = other.m_imageView1;
+        m_imageView1 = other.m_imageView2;
+        m_imageLayout = other.m_imageLayout;
+        m_uniformBuffer = std::move(other.m_uniformBuffer);
+
+        other.m_pool = nullptr;
+        other.m_set = VK_NULL_HANDLE;
+        other.m_sampler = VK_NULL_HANDLE;
+        other.m_imageView1 = nullptr;
+        other.m_imageView2 = nullptr;
+        other.m_imageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+        other.m_uniformBuffer = nullptr;
+        return *this;
+    }
+
+private:
+    SceneDescriptorPool *m_pool;
+    VkDescriptorSet m_set;
+    VkSampler m_sampler;
+    std::shared_ptr<VulkanImageView> m_imageView1;
+    std::shared_ptr<VulkanImageView> m_imageView2;
+    VkImageLayout m_imageLayout;
+    std::shared_ptr<VulkanBuffer> m_uniformBuffer;
+};
+
+
+
+// ------------------------------------------------------------------
+
+
+
+/**
+ * ColorDescriptorSet encapsulates a descriptor set with a dynamic uniform buffer \
binding. + */
+class ColorDescriptorSet : public VulkanObject
+{
+public:
+    ColorDescriptorSet(SceneDescriptorPool *pool);
+
+    ColorDescriptorSet(const ColorDescriptorSet &other) = delete;
+
+    ColorDescriptorSet(ColorDescriptorSet &&other)
+        : m_pool(other.m_pool),
+          m_set(other.m_set),
+          m_uniformBuffer(std::move(other.m_uniformBuffer))
+    {
+        other.m_pool = nullptr;
+        other.m_set = VK_NULL_HANDLE;
+        other.m_uniformBuffer = nullptr;
+    }
+
+    ~ColorDescriptorSet() override;
+
+    void update(const std::shared_ptr<VulkanBuffer> &uniformBuffer);
+
+    std::shared_ptr<VulkanBuffer> uniformBuffer() const { return m_uniformBuffer; }
+
+    VkDescriptorSet handle() const { return m_set; }
+    operator VkDescriptorSet () const { return m_set; }
+
+    ColorDescriptorSet &operator = (const ColorDescriptorSet &other) = delete;
+
+    ColorDescriptorSet &operator = (ColorDescriptorSet &&other) {
+        m_pool = other.m_pool;
+        m_set = other.m_set;
+        m_uniformBuffer = std::move(other.m_uniformBuffer);
+
+        other.m_pool = nullptr;
+        other.m_set = VK_NULL_HANDLE;
+        other.m_uniformBuffer = nullptr;
+        return *this;
+    }
+
+private:
+    SceneDescriptorPool *m_pool;
+    VkDescriptorSet m_set;
+    std::shared_ptr<VulkanBuffer> m_uniformBuffer;
+};
+
+
+
+// ------------------------------------------------------------------
+
+
+
+/**
+ * SceneDescriptorPool allocates descriptors for descriptor sets.
+ */
+class SceneDescriptorPool
+{
+public:
+    struct CreateInfo {
+        VulkanDevice *device;
+        VkDescriptorSetLayout descriptorSetLayout;
+        uint32_t maxSets;
+        std::initializer_list<VkDescriptorPoolSize> poolSizes;
+    };
+
+    SceneDescriptorPool(const CreateInfo &createInfo);
+    ~SceneDescriptorPool();
+
+    VulkanDevice *device() const { return m_device; }
+
+private:
+    VkDescriptorSet allocateDescriptorSet();
+    void freeDescriptorSet(VkDescriptorSet set);
+
+private:
+    VulkanDevice *m_device;
+    std::deque<VkDescriptorPool> m_pools;
+    std::queue<VkDescriptorSet> m_unusedSets;
+    std::vector<VkDescriptorPoolSize> m_poolSizes;
+    VkDescriptorSetLayout m_layout = VK_NULL_HANDLE;
+    uint32_t m_maxSets = 0;
+    uint32_t m_available = 0;
+
+friend class TextureDescriptorSet;
+friend class CrossFadeDescriptorSet;
+friend class ColorDescriptorSet;
+};
+
+} // namespace KWin
+
+#endif // DESCRIPTOR_SET_H
diff --git a/plugins/scenes/vulkan/effectframe.cpp \
b/plugins/scenes/vulkan/effectframe.cpp new file mode 100644
index 000000000..a9ea2c61f
--- /dev/null
+++ b/plugins/scenes/vulkan/effectframe.cpp
@@ -0,0 +1,83 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright  © 2017-2018 Fredrik Höglund <fredrik@kde.org>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*********************************************************************/
+
+#include "effectframe.h"
+
+namespace KWin
+{
+
+
+VulkanEffectFrame::VulkanEffectFrame(EffectFrameImpl *frame, VulkanScene *scene)
+    : Scene::EffectFrame(frame),
+      m_scene(scene)
+{
+}
+
+
+VulkanEffectFrame::~VulkanEffectFrame()
+{
+}
+
+
+void VulkanEffectFrame::render(QRegion region, double opacity, double frameOpacity)
+{
+    Q_UNUSED(region)
+    Q_UNUSED(opacity)
+    Q_UNUSED(frameOpacity)
+}
+
+
+// Called when the geometry has changed, the plasma theme has changed, or an effect \
has called free() +void VulkanEffectFrame::free()
+{
+}
+
+
+// Called when the icon and/or icon size has changed
+void VulkanEffectFrame::freeIconFrame()
+{
+}
+
+
+// Called when the geometry has changed as a result of a font and/or text change
+void VulkanEffectFrame::freeTextFrame()
+{
+}
+
+
+// Called when the geometry of the selection has changed
+void VulkanEffectFrame::freeSelection()
+{
+}
+
+
+// Called when cross-fading is enabled, and the icon is about to be changed
+void VulkanEffectFrame::crossFadeIcon()
+{
+}
+
+
+// Called when cross-fading is enabled, and the text is about to be changed
+void VulkanEffectFrame::crossFadeText()
+{
+}
+
+
+} // namespace KWin
diff --git a/plugins/scenes/vulkan/effectframe.h \
b/plugins/scenes/vulkan/effectframe.h new file mode 100644
index 000000000..72af35891
--- /dev/null
+++ b/plugins/scenes/vulkan/effectframe.h
@@ -0,0 +1,52 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright  © 2017-2018 Fredrik Höglund <fredrik@kde.org>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*********************************************************************/
+
+#ifndef EFFECTFRAME_H
+#define EFFECTFRAME_H
+
+#include "scene.h"
+
+namespace KWin
+{
+
+class VulkanEffectFrame : public Scene::EffectFrame
+{
+public:
+    VulkanEffectFrame(EffectFrameImpl *frame, VulkanScene *scene);
+    ~VulkanEffectFrame() override final;
+
+    void render(QRegion region, double opacity, double frameOpacity) override final;
+
+    void free() override final;
+
+    void freeIconFrame() override final;
+    void freeTextFrame() override final;
+    void freeSelection() override final;
+
+    void crossFadeIcon() override final;
+    void crossFadeText() override final;
+
+private:
+    VulkanScene *m_scene;
+};
+
+} // namespace KWin
+
+#endif // EFFECTFRAME_H
diff --git a/plugins/scenes/vulkan/scene.cpp b/plugins/scenes/vulkan/scene.cpp
new file mode 100644
index 000000000..fea0264d9
--- /dev/null
+++ b/plugins/scenes/vulkan/scene.cpp
@@ -0,0 +1,1792 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright  © 2017-2018 Fredrik Höglund <fredrik@kde.org>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*********************************************************************/
+
+#include "scene.h"
+#include "swapchain.h"
+#include "window.h"
+#include "shadow.h"
+#include "decorationrenderer.h"
+#include "descriptorset.h"
+#include "utils.h"
+
+#include "effectframe.h"
+#include "platform.h"
+#include "screens.h"
+#include "logging.h"
+#include "platformsupport/scenes/vulkan/backend.h"
+
+#include <QGraphicsScale>
+#include <KNotification>
+#include <KLocalizedString>
+
+
+using namespace std::placeholders;
+
+
+namespace KWin {
+
+
+VulkanFactory::VulkanFactory(QObject *parent)
+    : SceneFactory(parent)
+{
+}
+
+
+VulkanFactory::~VulkanFactory() = default;
+
+
+Scene *VulkanFactory::create(QObject *parent) const
+{
+    auto backend = std::unique_ptr<VulkanBackend>(kwinApp()->platform()->createVulkanBackend());
 +
+    if (!backend || !backend->isValid())
+        return nullptr;
+
+    VulkanScene *scene = new VulkanScene(std::move(backend), parent);
+
+    if (scene && scene->initFailed()) {
+        delete scene;
+        scene = nullptr;
+    }
+
+    return scene;
+}
+
+
+
+// ------------------------------------------------------------------
+
+
+
+VulkanScene::VulkanScene(std::unique_ptr<VulkanBackend> &&backend, QObject *parent)
+    : Scene(parent),
+      m_backend(std::move(backend))
+{
+    init();
+}
+
+
+VulkanScene::~VulkanScene()
+{
+    if (!m_device)
+        return;
+
+    m_device->waitIdle();
+
+    for (FrameData &data : m_frameData) {
+        if (data.imageAcquisitionFenceSubmitted)
+            data.imageAcquisitionFence.wait();
+    }
+}
+
+
+bool VulkanScene::init()
+{
+    // Instance layers
+    // ---------------
+    std::vector<const char *> enabledLayers;
+
+    if (qgetenv("KWIN_ENABLE_VULKAN_VALIDATION") == "1") {
+        // Note that VK_LAYER_LUNARG_standard_validation is a meta layer that
+        // loads all standard validation layers in the optimal order.
+        // The loader removes duplicates, so it's safe to list the others anyway.
+        static const char * const validationLayers[] {
+            "VK_LAYER_LUNARG_standard_validation",
+            "VK_LAYER_GOOGLE_threading",
+            "VK_LAYER_LUNARG_parameter_validation",
+            "VK_LAYER_LUNARG_object_tracker",
+            "VK_LAYER_LUNARG_core_validation",
+            "VK_LAYER_GOOGLE_unique_objects",
+        };
+
+        const VulkanLayerVector supportedLayers = VulkanInstance::supportedLayers();
+        enabledLayers.reserve(std::extent<decltype(validationLayers)>());
+
+        for (const char *layer : validationLayers) {
+            if (supportedLayers.contains(layer)) {
+                enabledLayers.push_back(layer);
+            }
+        }
+    }
+
+    // Instance extensions
+    // -------------------
+    const VulkanExtensionVector supportedInstanceExtensions = \
VulkanInstance::supportedExtensions(); +
+    const char *requiredInstanceExtensions[] = {
+        VK_KHR_SURFACE_EXTENSION_NAME,
+        m_backend->platformSurfaceExtensionName()
+    };
+
+    std::vector<const char *> enabledInstanceExtensions;
+    enabledInstanceExtensions.reserve(std::extent<decltype(requiredInstanceExtensions)>());
 +
+    for (auto extension : requiredInstanceExtensions) {
+        if (!supportedInstanceExtensions.contains(extension)) {
+            qCCritical(KWIN_VULKAN) << "Required Vulkan extension" << extension << \
"is not supported."; +            return false;
+        }
+
+        enabledInstanceExtensions.push_back(extension);
+    }
+
+    bool haveGetPhysicalDeviceProperties2 = \
supportedInstanceExtensions.contains(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
 +    if (haveGetPhysicalDeviceProperties2)
+        enabledInstanceExtensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
 +
+    bool haveExtDebugReport = \
supportedInstanceExtensions.contains(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); +    if \
(haveExtDebugReport) +        \
enabledInstanceExtensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); +
+
+    // Create the vulkan instance
+    // --------------------------
+    const VkApplicationInfo applicationInfo {
+        .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
+        .pNext = nullptr,
+        .pApplicationName = KWIN_NAME,
+        .applicationVersion = VK_MAKE_VERSION(KWIN_VERSION_MAJOR, \
KWIN_VERSION_MINOR, KWIN_VERSION_PATCH), +        .pEngineName = nullptr,
+        .engineVersion = VK_MAKE_VERSION(0, 0, 0),
+        .apiVersion = VK_API_VERSION_1_0
+    };
+
+    m_instance = VulkanInstance(applicationInfo, enabledLayers, \
enabledInstanceExtensions); +    if (!m_instance.isValid()) {
+        qCCritical(KWIN_VULKAN) << "Failed to create a Vulkan instance";
+        return false;
+    }
+
+    m_backend->setInstance(&m_instance);
+
+
+    // Resolve all entry points
+    // ------------------------
+    resolveFunctions(m_instance);
+
+
+    // Create the debug callback
+    // -------------------------
+    if (haveExtDebugReport) {
+        auto debugFunc = [](VkFlags msgFlags, VkDebugReportObjectTypeEXT objType,
+                            uint64_t srcObject, size_t location, int32_t msgCode,
+                            const char *pLayerPrefix, const char *pMsg, void \
*pUserData) -> VkBool32 { +            Q_UNUSED(objType)
+            Q_UNUSED(srcObject)
+            Q_UNUSED(location)
+            Q_UNUSED(pUserData)
+
+            if (msgFlags & VK_DEBUG_REPORT_ERROR_BIT_EXT) {
+                qCWarning(KWIN_VULKAN, "ERROR: [%s] Code %d : %s", pLayerPrefix, \
msgCode, pMsg); +            } else if (msgFlags & VK_DEBUG_REPORT_WARNING_BIT_EXT) {
+                qCWarning(KWIN_VULKAN, "WARNING: [%s] Code %d : %s", pLayerPrefix, \
msgCode, pMsg); +            }
+
+            return false;
+        };
+
+        m_debugReportCallback = VulkanDebugReportCallback(m_instance,
+                                                          \
VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT, +                    \
debugFunc); +    }
+
+
+    // Create the Vulkan surface
+    // -------------------------
+    if (!m_backend->createSurfaces()) {
+        qCCritical(KWIN_VULKAN) << "Failed to create the Vulkan platform \
surface(s)"; +        return false;
+    }
+
+    std::shared_ptr<VulkanSurface> surface = m_backend->surface();
+
+
+    // Enumerate the available GPU's
+    // -----------------------------
+    const char *requiredDeviceExtensions[] = {
+        VK_KHR_SWAPCHAIN_EXTENSION_NAME,
+    };
+
+    const char *optionalDeviceExtensions[] = {
+        VK_KHR_MAINTENANCE1_EXTENSION_NAME,
+        VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME,
+        VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME,
+        VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME,
+        VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME,
+        VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME,
+        VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_EXTENSION_NAME,
+        VK_KHR_MAINTENANCE2_EXTENSION_NAME,
+        VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME,
+        VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME,
+        VK_KHR_BIND_MEMORY_2_EXTENSION_NAME,
+        VK_EXT_DISCARD_RECTANGLES_EXTENSION_NAME,
+        VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME,
+    };
+
+    VulkanExtensionVector supportedDeviceExtensions;
+
+    VulkanPhysicalDevice physicalDevice;
+    uint32_t graphicsQueueFamilyIndex = 0;
+    uint32_t presentQueueFamilyIndex = 0;
+    uint32_t bestScore = 0;
+
+    for (VulkanPhysicalDevice &device : m_instance.enumeratePhysicalDevices()) {
+        // Enumerate the device extensions
+        supportedDeviceExtensions = device.enumerateDeviceExtensionProperties();
+
+        // The device must support all required extensions
+        if (!supportedDeviceExtensions.containsAll(requiredDeviceExtensions))
+            continue;
+
+        // The device must support sampling linear VK_FORMAT_B8G8R8A8_UNORM images.
+        // This is used to upload decoration textures.
+        // AMD, Intel, and NVIDIA all support this.
+        VkFormatProperties formatProperties;
+        device.getFormatProperties(VK_FORMAT_B8G8R8A8_UNORM, &formatProperties);
+
+        if (!(formatProperties.linearTilingFeatures & \
VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) +            continue;
+
+        // Swapchain images must be renderable
+        VkSurfaceCapabilitiesKHR capabilities;
+        device.getSurfaceCapabilitiesKHR(surface->handle(), &capabilities);
+
+        if (!(capabilities.supportedUsageFlags & \
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)) +            continue;
+
+        // Enumerate the available queue families
+        const std::vector<VkQueueFamilyProperties> queueFamilies = \
device.getQueueFamilyProperties(); +
+        // Find queue families that support graphics and presentation
+        uint32_t graphicsIndex = UINT32_MAX;
+        uint32_t presentIndex = UINT32_MAX;
+
+        for (unsigned i = 0; i < queueFamilies.size(); i++) {
+            const VkQueueFamilyProperties &family = queueFamilies[i];
+
+            if (family.queueCount < 1)
+                continue;
+
+            // Note that the VK_QUEUE_TRANSFER_BIT does not need to be set when
+            // VK_QUEUE_GRAPHICS_BIT is set; transfer support is implied
+            if (graphicsIndex == UINT32_MAX && (family.queueFlags & \
VK_QUEUE_GRAPHICS_BIT)) +                graphicsIndex = i;
+
+            // Check if the queue family can present images to the surface
+            if (presentIndex == UINT32_MAX) {
+                VkBool32 supported = VK_FALSE;
+                if (device.getSurfaceSupportKHR(i, surface->handle(), &supported) == \
VK_SUCCESS) { +                    // Check for platform specific present support
+                    if (supported && \
m_backend->getPhysicalDevicePresentationSupport(device, i)) +                        \
presentIndex = i; +                }
+            }
+        }
+
+        // The device must support graphics and presentation
+        if (graphicsIndex == UINT32_MAX || presentIndex == UINT32_MAX)
+            continue;
+
+        VkPhysicalDeviceProperties properties;
+        device.getProperties(&properties);
+
+        // Assign a score to the device based on the type
+        const struct {
+            VkPhysicalDeviceType type;
+            int score;
+        } scores[] = {
+            { VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU, 500 },
+            { VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU,   400 },
+            { VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU,    300 },
+            { VK_PHYSICAL_DEVICE_TYPE_CPU,            200 }
+        };
+
+        uint32_t score = 100;
+
+        const auto it = std::find_if(std::cbegin(scores), std::cend(scores), \
[&](const auto &entry) { return entry.type == properties.deviceType; }); +        if \
(it != std::cend(scores)) +            score = it->score;
+
+        if (score > bestScore) {
+            bestScore                = score;
+            physicalDevice           = device;
+            graphicsQueueFamilyIndex = graphicsIndex;
+            presentQueueFamilyIndex  = presentIndex;
+        }
+    }
+
+    if (!physicalDevice.isValid()) {
+        qCCritical(KWIN_VULKAN) << "Failed to find a usable GPU";
+        return false;
+    }
+
+
+    // Get the physical device properties
+    // ----------------------------------
+    if (haveGetPhysicalDeviceProperties2) {
+        VkPhysicalDevicePushDescriptorPropertiesKHR pushDescriptorProperties = {
+            .sType = \
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR, +            .pNext \
= nullptr, +            .maxPushDescriptors = 0
+        };
+
+        VkPhysicalDeviceDiscardRectanglePropertiesEXT discardRectangleProperties = {
+            .sType = \
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DISCARD_RECTANGLE_PROPERTIES_EXT, +            \
.pNext = &pushDescriptorProperties, +            .maxDiscardRectangles = 0
+        };
+
+        VkPhysicalDeviceProperties2KHR physicalDeviceProperties2 = {
+            .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR,
+            .pNext = &discardRectangleProperties,
+            .properties = {}
+        };
+
+        physicalDevice.getProperties2KHR(&physicalDeviceProperties2);
+
+        m_physicalDeviceProperties = physicalDeviceProperties2.properties;
+        m_maxDiscardRects = discardRectangleProperties.maxDiscardRectangles;
+        m_maxPushDescriptors = pushDescriptorProperties.maxPushDescriptors;
+    } else {
+        physicalDevice.getProperties(&m_physicalDeviceProperties);
+
+        m_maxDiscardRects = 0;
+        m_maxPushDescriptors = 0;
+    }
+
+    qCInfo(KWIN_VULKAN).nospace() << "Vulkan API version: "
+        << VK_VERSION_MAJOR(m_physicalDeviceProperties.apiVersion) << '.'
+        << VK_VERSION_MINOR(m_physicalDeviceProperties.apiVersion) << '.'
+        << VK_VERSION_PATCH(m_physicalDeviceProperties.apiVersion);
+
+    qCInfo(KWIN_VULKAN).nospace().noquote() << "Driver version: "
+        << driverVersionString(m_physicalDeviceProperties.vendorID, \
m_physicalDeviceProperties.driverVersion); +
+    qCInfo(KWIN_VULKAN).nospace().noquote() << "Vendor ID: " << \
vendorName(m_physicalDeviceProperties.vendorID); +    \
qCInfo(KWIN_VULKAN).nospace().noquote() << "Device ID: " << showbase << hex << \
m_physicalDeviceProperties.deviceID; +    qCInfo(KWIN_VULKAN).nospace().noquote() << \
"Device name: " << m_physicalDeviceProperties.deviceName; +    \
qCInfo(KWIN_VULKAN).nospace().noquote() << "Device type: " << \
enumToString(m_physicalDeviceProperties.deviceType); +
+
+    // Get the supported surface formats
+    // ---------------------------------
+    const std::vector<VkSurfaceFormatKHR> supportedFormats = \
physicalDevice.getSurfaceFormatsKHR(surface->handle()); +
+    const VkFormat preferredFormats[] {
+        VK_FORMAT_A2R10G10B10_UNORM_PACK32,
+        VK_FORMAT_A2B10G10R10_UNORM_PACK32,
+        VK_FORMAT_B8G8R8A8_UNORM,
+        VK_FORMAT_R8G8B8A8_UNORM,
+        VK_FORMAT_A8B8G8R8_UNORM_PACK32,
+        VK_FORMAT_B8G8R8A8_SRGB,
+        VK_FORMAT_R8G8B8A8_SRGB,
+        VK_FORMAT_A8B8G8R8_SRGB_PACK32,
+        VK_FORMAT_B8G8R8_UNORM,
+        VK_FORMAT_R8G8B8_UNORM,
+        VK_FORMAT_B8G8R8_SRGB,
+        VK_FORMAT_R8G8B8_SRGB,
+    };
+
+    m_surfaceFormat = VK_FORMAT_UNDEFINED;
+    m_surfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
+
+    if (supportedFormats.size() == 1 && supportedFormats[0].format == \
VK_FORMAT_UNDEFINED) { +        // The surface supports any format - pick the first \
in our list of preferred formats +        m_surfaceFormat = preferredFormats[0];
+
+        qCDebug(KWIN_VULKAN).noquote() << "Using" << enumToString(m_surfaceFormat) \
<< "for swapchain images"; +    } else if (supportedFormats.size() > 0) {
+        // Default to the first supported format in case none of our preferred \
formats are supported +        m_surfaceFormat = supportedFormats[0].format;
+        m_surfaceColorSpace = supportedFormats[0].colorSpace;
+
+        // Find the first supported format in preferredFormats
+        for (VkFormat preferred : preferredFormats) {
+            auto it = std::find_if(supportedFormats.begin(), supportedFormats.end(),
+                                   [=](const VkSurfaceFormatKHR &supported) { return \
supported.format == preferred; }); +            if (it != supportedFormats.end()) {
+                m_surfaceFormat = it->format;
+                m_surfaceColorSpace = it->colorSpace;
+                break;
+            }
+        }
+
+        qCInfo(KWIN_VULKAN) << "Supported surface formats:";
+        for (const VkSurfaceFormatKHR &supported : supportedFormats) {
+            if (supported.format == m_surfaceFormat)
+                qCInfo(KWIN_VULKAN).nospace().noquote() << "  > " << \
enumToString(supported.format); +            else
+                qCInfo(KWIN_VULKAN).nospace().noquote() << "    " << \
enumToString(supported.format); +        }
+    } else {
+        qCCritical(KWIN_VULKAN) << "The surface does not support any image formats";
+        return false;
+    }
+
+
+    // Get the supported present modes
+    // -------------------------------
+    const std::vector<VkPresentModeKHR> supportedPresentModes = \
physicalDevice.getSurfacePresentModesKHR(surface->handle()); +    uint32_t \
swapchainImageCount; +
+    if (supportedPresentModes.size() > 0) {
+        const VkPresentModeKHR preferredPresentModes[] {
+            VK_PRESENT_MODE_MAILBOX_KHR,
+            VK_PRESENT_MODE_FIFO_KHR,
+            VK_PRESENT_MODE_FIFO_RELAXED_KHR,
+            VK_PRESENT_MODE_IMMEDIATE_KHR
+        };
+
+        // Find the first supported mode in preferredPresentModes
+        auto result = std::find_first_of(std::begin(preferredPresentModes), \
std::end(preferredPresentModes), +                                         \
std::begin(supportedPresentModes), std::end(supportedPresentModes)); +
+        if (result != std::end(preferredPresentModes))
+            m_presentMode = *result;
+        else
+            m_presentMode = supportedPresentModes[0];
+
+        switch (m_presentMode) {
+        case VK_PRESENT_MODE_IMMEDIATE_KHR:
+        case VK_PRESENT_MODE_FIFO_KHR:
+        case VK_PRESENT_MODE_FIFO_RELAXED_KHR:
+        default:
+            swapchainImageCount = 2;
+            break;
+
+        case VK_PRESENT_MODE_MAILBOX_KHR:
+            swapchainImageCount = 3;
+            break;
+        }
+
+        qCInfo(KWIN_VULKAN) << "Supported present modes:";
+        for (const VkPresentModeKHR mode : supportedPresentModes) {
+            if (mode == m_presentMode)
+                qCInfo(KWIN_VULKAN).nospace().noquote() << "  > " << \
enumToString(mode); +            else
+                qCInfo(KWIN_VULKAN).nospace().noquote() << "    " << \
enumToString(mode); +        }
+    } else {
+        qCCritical(KWIN_VULKAN) << "The physical device does not support any \
presentation modes."; +        return false;
+    }
+
+
+    // Create the logical device
+    // -------------------------
+    const float queuePriorities[] { 1.0f };
+    std::vector<const char *> enabledDeviceExtensions;
+
+    for (const char *name : requiredDeviceExtensions)
+        enabledDeviceExtensions.push_back(name);
+
+    for (const char *name : optionalDeviceExtensions) {
+        if (supportedDeviceExtensions.contains(name)) {
+            enabledDeviceExtensions.push_back(name);
+        }
+    }
+
+    m_separatePresentQueue = graphicsQueueFamilyIndex != presentQueueFamilyIndex;
+
+    const VkDeviceQueueCreateInfo queueCreateInfo[] = {
+        {
+            .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
+            .pNext = nullptr,
+            .flags = 0,
+            .queueFamilyIndex = graphicsQueueFamilyIndex,
+            .queueCount = 1,
+            .pQueuePriorities = queuePriorities
+        },
+        {
+            .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
+            .pNext = nullptr,
+            .flags = 0,
+            .queueFamilyIndex = presentQueueFamilyIndex,
+            .queueCount = 1,
+            .pQueuePriorities = queuePriorities
+        }
+    };
+
+    const VkPhysicalDeviceFeatures enabledFeatures = {};
+
+    const VkDeviceCreateInfo deviceCreateInfo = {
+        .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
+        .pNext = nullptr,
+        .flags = 0,
+        .queueCreateInfoCount = m_separatePresentQueue ? 2u : 1u,
+        .pQueueCreateInfos = queueCreateInfo,
+        .enabledLayerCount = 0,
+        .ppEnabledLayerNames = nullptr,
+        .enabledExtensionCount = (uint32_t) enabledDeviceExtensions.size(),
+        .ppEnabledExtensionNames = enabledDeviceExtensions.data(),
+        .pEnabledFeatures = &enabledFeatures
+    };
+
+    m_device = std::make_unique<VulkanDevice>(physicalDevice, &deviceCreateInfo);
+    if (!m_device->isValid()) {
+        qCCritical(KWIN_VULKAN) << "Failed to create the logical device";
+        return false;
+    }
+
+    // Get the queues from the device
+    m_graphicsQueue = m_device->getQueue(graphicsQueueFamilyIndex, 0);
+
+    if (m_separatePresentQueue)
+        m_presentQueue = m_device->getQueue(presentQueueFamilyIndex, 0);
+    else
+        m_presentQueue = m_graphicsQueue;
+
+
+    // Report the enabled layers & extensions
+    qCInfo(KWIN_VULKAN) << "Enabled instance layers:";
+    for (auto &layer : enabledLayers)
+        qCInfo(KWIN_VULKAN) << "    " << layer;
+
+    qCInfo(KWIN_VULKAN) << "Enabled instance extensions:";
+    for (auto &extension : enabledInstanceExtensions)
+        qCInfo(KWIN_VULKAN) << "    " << extension;
+
+    qCInfo(KWIN_VULKAN) << "Enabled device extensions:";
+    for (auto &extension : enabledDeviceExtensions)
+        qCInfo(KWIN_VULKAN) << "    " << extension;
+
+
+    // Create the command pools
+    // ------------------------
+    if (m_separatePresentQueue)
+        m_presentCommandPool = VulkanCommandPool(device(), 0, \
m_presentQueue.familyIndex()); +
+
+    // Create the render passes
+    // ------------------------
+    if (!createRenderPasses()) {
+        qCCritical(KWIN_VULKAN) << "Failed to create the render passes";
+        return false;
+    }
+
+
+    // Create the samplers
+    // -------------------
+    m_nearestSampler =
+            VulkanSampler(device(),
+                          {
+                              .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
+                              .pNext = nullptr,
+                              .flags = 0,
+                              .magFilter = VK_FILTER_NEAREST,
+                              .minFilter = VK_FILTER_NEAREST,
+                              .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
+                              .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
+                              .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
+                              .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
+                              .mipLodBias = 0.0f,
+                              .anisotropyEnable = VK_FALSE,
+                              .maxAnisotropy = 1.0f,
+                              .compareEnable = VK_FALSE,
+                              .compareOp = VK_COMPARE_OP_ALWAYS,
+                              .minLod = 0.0f,
+                              .maxLod = 1.0f,
+                              .borderColor = \
VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, +                              \
.unnormalizedCoordinates = VK_TRUE +                          });
+
+    m_linearSampler =
+            VulkanSampler(device(),
+                          {
+                              .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
+                              .pNext = nullptr,
+                              .flags = 0,
+                              .magFilter = VK_FILTER_LINEAR,
+                              .minFilter = VK_FILTER_LINEAR,
+                              .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
+                              .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
+                              .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
+                              .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
+                              .mipLodBias = 0.0f,
+                              .anisotropyEnable = VK_FALSE,
+                              .maxAnisotropy = 1.0f,
+                              .compareEnable = VK_FALSE,
+                              .compareOp  = VK_COMPARE_OP_ALWAYS,
+                              .minLod = 0.0f,
+                              .maxLod = 1.0f,
+                              .borderColor = \
VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, +                              \
.unnormalizedCoordinates = VK_TRUE +                          });
+
+
+    // Create the swapchain
+    // --------------------
+    m_swapchain = std::make_unique<VulkanSwapchain>(device(), m_presentQueue, \
surface->handle(), m_surfaceFormat, m_surfaceColorSpace, m_presentMode, \
m_loadRenderPass); +    \
m_swapchain->setSupportsIncrementalPresent(supportedDeviceExtensions.contains(VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME));
 +    m_swapchain->setPreferredImageCount(swapchainImageCount);
+
+
+    // Create the pipeline manager
+    // ---------------------------
+    m_pipelineCache = std::make_unique<VulkanPipelineCache>(device());
+
+    m_pipelineManager = std::make_unique<VulkanPipelineManager>(device(), \
m_pipelineCache.get(), +                                                              \
m_nearestSampler, m_linearSampler, +                                                  \
m_loadRenderPass, (VkRenderPass) VK_NULL_HANDLE, +                                    \
supportedDeviceExtensions.contains(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME)); +
+    if (!m_pipelineManager->isValid()) {
+        qCCritical(KWIN_VULKAN) << "Failed to create the Vulkan pipeline manager";
+        return false;
+    }
+
+
+    // Prepare the frame & paint pass ring buffers
+    // -------------------------------------------
+    for (FrameData &data : m_frameData) {
+        data.imageAcquisitionFence = VulkanFence(device());
+        data.imageAcquisitionFenceSubmitted = false;
+
+        data.imageAcquisitionSemaphore = \
std::make_shared<VulkanSemaphore>(device()); +
+        if (m_separatePresentQueue) {
+            data.acquireOwnershipSemaphore = \
std::make_shared<VulkanSemaphore>(device()); +            \
data.releaseOwnershipSemaphore = std::make_shared<VulkanSemaphore>(device()); +       \
} +    }
+
+    for (PaintPassData &data : m_paintPassData) {
+        data.semaphore = std::make_shared<VulkanSemaphore>(device());
+        data.commandPools[GraphicsQueueFamily] = VulkanCommandPool(device(), \
VK_COMMAND_POOL_CREATE_TRANSIENT_BIT, m_graphicsQueue.familyIndex()); +        \
data.setupCommandBuffer = std::make_unique<VulkanCommandBuffer>(device(), \
data.commandPools[GraphicsQueueFamily], VK_COMMAND_BUFFER_LEVEL_PRIMARY); +        \
data.mainCommandBuffer = std::make_unique<VulkanCommandBuffer>(device(), \
data.commandPools[GraphicsQueueFamily], VK_COMMAND_BUFFER_LEVEL_PRIMARY); +        \
data.fence = VulkanFence(device()); +        data.fenceSubmitted = false;
+    }
+
+
+    // Create the device memory allocator
+    // ----------------------------------
+    m_deviceMemoryAllocator = \
std::make_unique<VulkanDeviceMemoryAllocator>(device(), enabledDeviceExtensions); +
+
+    // Create the upload managers
+    // --------------------------
+    m_uploadManager =
+            std::make_unique<VulkanUploadManager>(device(),
+                                                  deviceMemoryAllocator(),
+                                                  16 * 1024,
+                                                  VK_BUFFER_USAGE_TRANSFER_SRC_BIT | \
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, +             \
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, +                                                \
m_physicalDeviceProperties.limits); +
+    m_imageUploadManager =
+            std::make_unique<VulkanUploadManager>(device(),
+                                                  deviceMemoryAllocator(),
+                                                  8 * 1024 * 1024,
+                                                  VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
+                                                  \
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, +                                                \
m_physicalDeviceProperties.limits); +
+
+    // Create the descriptor pools
+    // ---------------------------
+    m_textureDescriptorPool =
+            std::make_unique<SceneDescriptorPool>(SceneDescriptorPool::CreateInfo {
+                                                      .device = device(),
+                                                      .descriptorSetLayout =
+                                                              \
pipelineManager()->descriptorSetLayout(VulkanPipelineManager::Texture), +             \
.maxSets = 72, +                                                      .poolSizes = {
+                                                          { \
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 72 }, +                                    \
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 72 } +                                   \
} +                                                  });
+
+    m_crossFadeDescriptorPool =
+            std::make_unique<SceneDescriptorPool>(SceneDescriptorPool::CreateInfo {
+                                                     .device = device(),
+                                                     .descriptorSetLayout =
+                                                             \
pipelineManager()->descriptorSetLayout(VulkanPipelineManager::TwoTextures), +         \
.maxSets = 8, +                                                     .poolSizes = {
+                                                         { \
VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,          16 }, +                                    \
{ VK_DESCRIPTOR_TYPE_SAMPLER,                 8 }, +                                  \
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,  8 } +                                   \
} +                                                  });
+
+    m_colorDescriptorPool =
+            std::make_unique<SceneDescriptorPool>(SceneDescriptorPool::CreateInfo {
+                                                      .device = device(),
+                                                      .descriptorSetLayout =
+                                                              \
pipelineManager()->descriptorSetLayout(VulkanPipelineManager::FlatColor), +           \
.maxSets = 16, +                                                      .poolSizes = {
+                                                          { \
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 16 } +                                     \
} +                                                  });
+
+    m_valid = true;
+    return m_valid;
+}
+
+
+bool VulkanScene::usesOverlayWindow() const
+{
+    return m_backend->usesOverlayWindow();
+}
+
+
+OverlayWindow *VulkanScene::overlayWindow()
+{
+    return m_backend->overlayWindow();
+}
+
+
+// These functions create projection matrices compatible with the Vulkan coordinate \
system +//
+// In Vulkan the normalized device coordinates (-1, -1) correspond to the \
uppper-left corner +// of the viewport, and the near and far clip planes correspond \
to 0 and +1 respectively. +// QMatrix4x4::ortho() and QMatrix4x4::frustum() create \
matrices for the OpenGL coordinate +// system, where (-1, -1) correspond to the \
lower-left corner, and the near and far clip +// planes correspond to -1 and +1. \
respectively +QMatrix4x4 VulkanScene::ortho(float left, float right, float bottom, \
float top, float near, float far) const +{
+    return QMatrix4x4(2.0f / (right - left), 0.0f,                  0.0f,            \
-(right + left) / (right - left), +                      0.0f,                  2.0f \
/ (bottom - top), 0.0f,                -(top + bottom) / (bottom - top), +            \
0.0f,                  0.0f,                  1.0f / (near - far),  near / (near - \
far), +                      0.0f,                  0.0f,                  0.0f,      \
1.0f); +}
+
+
+QMatrix4x4 VulkanScene::frustum(float left, float right, float bottom, float top, \
float zNear, float zFar) const +{
+    const float x = 2.0f * zNear   / (right  - left);
+    const float y = 2.0f * zNear   / (bottom - top);
+    const float a = (right + left) / (right  - left);
+    const float b = (top + bottom) / (bottom - top);
+    const float c = zFar           / (zNear  - zFar);
+    const float d = zNear * zFar   / (zNear  - zFar);
+
+    return QMatrix4x4(x,     0.0f,  a,     0.0f,
+                      0.0f,  y,     b,     0.0f,
+                      0.0f,  0.0f,  c,     d,
+                      0.0f,  0.0f, -1.0f,  0.0f);
+}
+
+
+QMatrix4x4 VulkanScene::createProjectionMatrix() const
+{
+    // Create a perspective projection with a 60 ° field-of-view,
+    // and an aspect ratio of 1.0.
+    const float fovY   =   60.0f;
+    const float aspect =    1.0f;
+    const float zNear  =    0.1f;
+    const float zFar   =  100.0f;
+
+    const float yMax   =  zNear * std::tan(fovY * M_PI / 360.0f);
+    const float yMin   = -yMax;
+    const float xMin   =  yMin * aspect;
+    const float xMax   =  yMax * aspect;
+
+    const QMatrix4x4 projection = frustum(xMin, xMax, yMin, yMax, zNear, zFar);
+
+    // Create a second matrix that transforms screen coordinates
+    // to world coordinates.
+    const float scaleFactor = 1.1 * std::tan(fovY * M_PI / 360.0f) / yMax;
+    const QSize size = screens()->size();
+
+    QMatrix4x4 matrix;
+    matrix.translate(xMin * scaleFactor, yMax * scaleFactor, -1.1);
+    matrix.scale( (xMax - xMin) * scaleFactor / size.width(),
+                 -(yMax - yMin) * scaleFactor / size.height(),
+                  0.001);
+
+    // Combine the matrices
+    return projection * matrix;
+}
+
+
+QMatrix4x4 VulkanScene::screenMatrix(int mask, const ScreenPaintData &data) const
+{
+    QMatrix4x4 matrix;
+
+    if (!(mask & PAINT_SCREEN_TRANSFORMED))
+        return matrix;
+
+    matrix.translate(data.translation());
+    data.scale().applyTo(&matrix);
+
+    if (data.rotationAngle() == 0.0)
+        return matrix;
+
+    // Apply the rotation
+    //
+    // Note that we cannot use data.rotation->applyTo(&matrix) as QGraphicsRotation
+    // uses projectedRotate() to map back to 2D
+    matrix.translate(data.rotationOrigin());
+    const QVector3D axis = data.rotationAxis();
+    matrix.rotate(data.rotationAngle(), axis.x(), axis.y(), axis.z());
+    matrix.translate(-data.rotationOrigin());
+
+    return matrix;
+}
+
+
+void VulkanScene::handleDeviceLostError()
+{
+    qCDebug(KWIN_VULKAN) << "Attempting to reset compositing following \
VK_ERROR_DEVICE_LOST."; +    QMetaObject::invokeMethod(this, "resetCompositing", \
Qt::QueuedConnection); +
+    KNotification::event(QStringLiteral("graphicsreset"), i18n("Desktop effects were \
restarted due to a graphics reset")); +}
+
+
+void VulkanScene::handleFatalError()
+{
+    QMetaObject::invokeMethod(this, "compositingFailed", Qt::QueuedConnection);
+    KNotification::event(QStringLiteral("compositingfailed"),
+                         i18n("Desktop effects have been suspended due to a fatal \
error")); +}
+
+
+bool VulkanScene::checkResult(VkResult result, std::function<void(void)> \
printDebugMessage) +{
+    switch (result) {
+    case VK_SUCCESS:
+        return true;
+
+    case VK_ERROR_DEVICE_LOST:
+        handleDeviceLostError();
+        return false;
+
+    case VK_ERROR_OUT_OF_HOST_MEMORY:
+    case VK_ERROR_OUT_OF_DEVICE_MEMORY:
+    case VK_ERROR_SURFACE_LOST_KHR:
+    default:
+        printDebugMessage();
+        handleFatalError();
+        return false;
+    }
+}
+
+
+struct ScopeGuard
+{
+    ~ScopeGuard() { destructor(); }
+    std::function<void(void)> destructor;
+};
+
+
+qint64 VulkanScene::paint(QRegion damage, ToplevelList windows)
+{
+    QElapsedTimer timer;
+    timer.start();
+
+    createStackingOrder(windows);
+
+    ScopeGuard stackingOrderGuard {[this]{ clearStackingOrder(); }};
+
+    if (m_backend->perScreenRendering()) {
+        // TODO
+    } else {
+        int mask = 0;
+
+        // (Re)create the swap chain if necessary
+        if (!m_swapchain->isValid()) {
+            const VkResult result = m_swapchain->create();
+
+            if (!checkResult(result, []{ qCCritical(KWIN_VULKAN) << "Swapchain \
creation failed"; })) +                return 0;
+
+            m_imageAcquired = false;
+        }
+
+        FrameData &frame = m_frameData[m_frameIndex];
+
+        if (!m_imageAcquired) {
+            if (frame.imageAcquisitionFenceSubmitted) {
+                // Block on the acquisition fence to prevent us from getting more \
than +                // m_frameData.size() frames ahead of the presentation engine.
+                frame.imageAcquisitionFence.wait();
+                frame.imageAcquisitionFence.reset();
+
+                frame.imageAcquisitionFenceSubmitted = false;
+            }
+
+            // Attempt to acquire an image from the swapchain
+            int32_t imageIndex = -1;
+            VkResult result = m_swapchain->acquireNextImage(UINT64_MAX, \
frame.imageAcquisitionSemaphore->handle(), frame.imageAcquisitionFence, &imageIndex); \
+ +            switch (result) {
+            case VK_SUCCESS:
+                break;
+
+            case VK_SUBOPTIMAL_KHR:
+                // Schedule a full repaint and invalidate the swapchain
+                // so it is recreated in the next paint pass.
+                m_swapchain->invalidate();
+                effects->addRepaintFull();
+                break;
+
+            case VK_ERROR_OUT_OF_DATE_KHR:
+                effects->addRepaintFull();
+                return 0;
+
+            case VK_ERROR_DEVICE_LOST:
+                handleDeviceLostError();
+                return 0;
+
+            default:
+                qCCritical(KWIN_VULKAN).noquote() << "vkAcquireNextImageKHR \
returned" << enumToString(result); +                handleFatalError();
+                return 0;
+            };
+
+            m_waitSemaphores.insert(frame.imageAcquisitionSemaphore);
+            frame.imageAcquisitionFenceSubmitted = true;
+
+            m_bufferAge = m_swapchain->bufferAge();
+            m_surfaceLayout = m_bufferAge == 0 ? VK_IMAGE_LAYOUT_UNDEFINED : \
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; +            m_needQueueOwnershipTransfer = false; \
// Will depend on whether we load or clear the contents +            m_imageAcquired \
= true; +        }
+
+        QRegion repaint = m_swapchain->accumulatedDamageHistory(m_bufferAge);
+
+        m_projectionMatrix = createProjectionMatrix();
+        m_renderPassStarted = false;
+        m_clearPending = false;
+        m_usedIndexBuffer = false;
+
+        PaintPassData &paintPass = m_paintPassData[m_paintPassIndex];
+
+        if (paintPass.fenceSubmitted) {
+            paintPass.fence.wait();
+
+            // Reset the command pools
+            for (auto &pool : paintPass.commandPools)
+                pool.reset();
+
+            // Clear lists of Vulkan objects referenced by this paint pass
+            paintPass.busyObjects.clear();
+
+            paintPass.fence.reset();
+            paintPass.fenceSubmitted = false;
+        }
+
+
+        // After this call, updateRegion will contain the damaged region in the
+        // back buffer. This is the region that needs to be posted to repair
+        // the front buffer. It does not include the additional damage returned
+        // by accumulatedDamageHistory(). validRegion is the region that has been
+        // repainted, and it may be larger than updateRegion.
+        QRegion updateRegion, validRegion;
+        paintScreen(&mask, damage, repaint, &updateRegion, &validRegion, \
projectionMatrix()); +
+        updateRegion &= QRegion(0, 0, m_swapchain->width(), m_swapchain->height());
+
+
+        // Submit the command buffers
+        // --------------------------
+        if (m_commandBuffersPending || m_clearPending) {
+            VulkanCommandBuffer *setupCommandBuffer = \
paintPass.setupCommandBuffer.get(); +            VulkanCommandBuffer \
*mainCommandBuffer = paintPass.mainCommandBuffer.get(); +
+            std::vector<VkSemaphore> waitSemaphores = \
m_waitSemaphores.toNativeHandleVector(); +
+            // If the present queue has ownership of the image we need to submit a
+            // command buffer to the present queue that relinquishes ownership \
before +            // we submit the main command buffer to the graphics queue. The \
graphics +            // queue ownership acquisition operation is handled by \
beginRenderPass(). +            if (m_needQueueOwnershipTransfer) {
+                std::shared_ptr<VulkanCommandBuffer> &cmd = \
m_swapchain->releaseOwnershipCommandBuffer(); +
+                // This command buffer is recorded once and reused
+                if (!cmd) {
+                    cmd = std::make_shared<VulkanCommandBuffer>(device(), \
m_presentCommandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY); +
+                    cmd->begin(VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT);
+                    cmd->pipelineBarrier(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
+                                         VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
+                                         0,
+                                         {},
+                                         {},
+                                         {
+                                             {
+                                                 .sType = \
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, +                                             \
.pNext = nullptr, +                                                 .srcAccessMask = \
VK_ACCESS_MEMORY_READ_BIT, +                                                 \
.dstAccessMask = 0, +                                                 .oldLayout = \
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, +                                                 \
.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, +                              \
.srcQueueFamilyIndex = m_presentQueue.familyIndex(), +                                \
.dstQueueFamilyIndex = m_graphicsQueue.familyIndex(), +                               \
.image = m_swapchain->currentImage(), +                                               \
.subresourceRange = { +                                                     \
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, +                                            \
.baseMipLevel = 0, +                                                     .levelCount \
= 1, +                                                     .baseArrayLayer = 0,
+                                                     .layerCount = 1
+                                                 }
+                                             }
+                                         });
+                    cmd->end();
+                }
+
+                // Submit the command buffer to the present queue
+                const VkCommandBuffer commandBuffers[] = { cmd->handle() };
+                const VkSemaphore signalSemaphores[] = { \
frame.releaseOwnershipSemaphore->handle() }; +                \
std::vector<VkPipelineStageFlags> waitDstStageMasks(waitSemaphores.size(), \
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); +
+                const VkResult result = m_presentQueue.submit({
+                                                                  {
+                                                                      .sType = \
VK_STRUCTURE_TYPE_SUBMIT_INFO, +                                                      \
.pNext = nullptr, +                                                                   \
.waitSemaphoreCount = (uint32_t) waitSemaphores.size(), +                             \
.pWaitSemaphores = waitSemaphores.data(), +                                           \
.pWaitDstStageMask = waitDstStageMasks.data(), +                                      \
.commandBufferCount = 1, +                                                            \
.pCommandBuffers = commandBuffers, +                                                  \
.signalSemaphoreCount = 1, +                                                          \
.pSignalSemaphores= signalSemaphores +                                                \
} +                                                              });
+
+                if (!checkResult(result, [=]{ qCCritical(KWIN_VULKAN).noquote() << \
"VkQueueSubmit returned" << enumToString(result); })) +                    return 0;
+
+                waitSemaphores = { signalSemaphores[0] };
+                m_needQueueOwnershipTransfer = false;
+            }
+
+            // Force the clear render pass to begin
+            if (m_clearPending && !m_renderPassStarted)
+                beginRenderPass(mainCommandBuffer);
+
+            if (mainCommandBuffer->isRenderPassActive())
+                mainCommandBuffer->endRenderPass();
+
+            m_renderPassStarted = false;
+
+            // Transition the image to VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
+            if (!updateRegion.isEmpty() && m_surfaceLayout != \
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) { +                uint32_t srcAccessMask = \
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; +                uint32_t dstAccessMask = \
VK_ACCESS_MEMORY_READ_BIT; +                uint32_t srcQueueFamilyIndex = \
VK_QUEUE_FAMILY_IGNORED; +                uint32_t dstQueueFamilyIndex = \
VK_QUEUE_FAMILY_IGNORED; +
+                if (m_separatePresentQueue) {
+                    // Transfer ownership to the present queue
+                    srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+                    dstAccessMask = 0;
+                    srcQueueFamilyIndex = m_graphicsQueue.familyIndex();
+                    dstQueueFamilyIndex = m_presentQueue.familyIndex();
+                }
+
+                const VkImageMemoryBarrier barrier = {
+                    .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+                    .pNext = nullptr,
+                    .srcAccessMask = srcAccessMask,
+                    .dstAccessMask = dstAccessMask,
+                    .oldLayout = m_surfaceLayout,
+                    .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
+                    .srcQueueFamilyIndex = srcQueueFamilyIndex,
+                    .dstQueueFamilyIndex = dstQueueFamilyIndex,
+                    .image = m_swapchain->currentImage(),
+                    .subresourceRange = {
+                        .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+                        .baseMipLevel = 0,
+                        .levelCount = 1,
+                        .baseArrayLayer = 0,
+                        .layerCount = 1
+                    }
+                };
+
+                assert(mainCommandBuffer->isActive());
+                mainCommandBuffer->pipelineBarrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
 +                                                   \
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, +                                               \
0, {}, {}, { barrier }); +                m_surfaceLayout = \
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; +            }
+
+
+            // Flush the upload managers
+            std::vector<VkMappedMemoryRange> ranges;
+
+            m_uploadManager->getNonCoherentAllocatedRanges(std::back_inserter(ranges));
 +            m_imageUploadManager->getNonCoherentAllocatedRanges(std::back_inserter(ranges));
 +
+            if (!ranges.empty())
+                m_device->flushMappedMemoryRanges(ranges.size(), ranges.data());
+
+            addBusyReference(m_uploadManager->createFrameBoundary());
+            addBusyReference(m_imageUploadManager->createFrameBoundary());
+
+
+            // Submit the command buffers to the graphics queue
+            VkCommandBuffer commandBuffers[2];
+            uint32_t commandBufferCount = 0;
+
+            if (setupCommandBuffer->isActive()) {
+                commandBuffers[commandBufferCount++] = setupCommandBuffer->handle();
+                setupCommandBuffer->end();
+            }
+
+            if (mainCommandBuffer->isActive()) {
+                commandBuffers[commandBufferCount++] = mainCommandBuffer->handle();
+                mainCommandBuffer->end();
+            }
+
+            assert(commandBufferCount > 0);
+
+            const VkSemaphore signalSemaphores[] = { paintPass.semaphore->handle() \
}; +            std::vector<VkPipelineStageFlags> \
waitDstStageMasks(waitSemaphores.size(), \
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT); +
+            const VkResult result = m_graphicsQueue.submit({
+                                                               {
+                                                                   .sType = \
VK_STRUCTURE_TYPE_SUBMIT_INFO, +                                                      \
.pNext = nullptr, +                                                                   \
.waitSemaphoreCount = (uint32_t) waitSemaphores.size(), +                             \
.pWaitSemaphores = waitSemaphores.data(), +                                           \
.pWaitDstStageMask = waitDstStageMasks.data(), +                                      \
.commandBufferCount = commandBufferCount, +                                           \
.pCommandBuffers = commandBuffers, +                                                  \
.signalSemaphoreCount = uint32_t(updateRegion.isEmpty() ? 0 : 1), +                   \
.pSignalSemaphores = updateRegion.isEmpty() ? nullptr : signalSemaphores +            \
} +                                                           },
+                                                           paintPass.fence);
+
+            m_commandBuffersPending = false;
+
+            if (!checkResult(result, [=]{ qCCritical(KWIN_VULKAN).noquote() << \
"vkQueueSubmit returned" << enumToString(result); })) +                return 0;
+
+            // Acquire present queue ownership of the swapchain image
+            if (m_separatePresentQueue && !updateRegion.isEmpty()) {
+                std::shared_ptr<VulkanCommandBuffer> &cmd = \
m_swapchain->acquireOwnershipCommandBuffer(); +
+                // This command buffer is recorded once and reused
+                if (!cmd) {
+                    cmd = std::make_shared<VulkanCommandBuffer>(device(), \
m_presentCommandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY); +
+                    // Note that we never do this unless we have rendered to the \
image +                    // and we are about to present it, so we know that the \
source layout +                    // is VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL.
+                    cmd->begin(VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT);
+                    cmd->pipelineBarrier(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
+                                         VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
+                                         0,
+                                         {},
+                                         {},
+                                         {
+                                             {
+                                                 .sType = \
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, +                                             \
.pNext = nullptr, +                                                 .srcAccessMask = \
0, +                                                 .dstAccessMask = 0,
+                                                 .oldLayout = \
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, +                                           \
.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, +                                       \
.srcQueueFamilyIndex = m_graphicsQueue.familyIndex(), +                               \
.dstQueueFamilyIndex = m_presentQueue.familyIndex(), +                                \
.image = m_swapchain->currentImage(), +                                               \
.subresourceRange = { +                                                     \
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, +                                            \
.baseMipLevel = 0, +                                                     .levelCount \
= 1, +                                                     .baseArrayLayer = 0,
+                                                     .layerCount = 1
+                                                 }
+                                             }
+                                         });
+                    cmd->end();
+                }
+
+                // Submit the command buffer to the present queue
+                const VkCommandBuffer commandBuffers[] = { cmd->handle() };
+                const VkSemaphore waitSemaphores[] = { paintPass.semaphore->handle() \
}; +                const VkPipelineStageFlags waitDstStageMasks[] = { \
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT }; +                const VkSemaphore \
signalSemaphores[] = { frame.acquireOwnershipSemaphore->handle() }; +
+                const VkResult result = m_presentQueue.submit({
+                                                                  {
+                                                                      .sType = \
VK_STRUCTURE_TYPE_SUBMIT_INFO, +                                                      \
.pNext = nullptr, +                                                                   \
.waitSemaphoreCount = 1, +                                                            \
.pWaitSemaphores = waitSemaphores, +                                                  \
.pWaitDstStageMask = waitDstStageMasks, +                                             \
.commandBufferCount = 1, +                                                            \
.pCommandBuffers = commandBuffers, +                                                  \
.signalSemaphoreCount = 1, +                                                          \
.pSignalSemaphores = signalSemaphores +                                               \
} +                                                              });
+
+                if (!checkResult(result, [=]{ qCCritical(KWIN_VULKAN).noquote() << \
"VkQueueSubmit returned" << enumToString(result); })) +                    return 0;
+            }
+
+            paintPass.fenceSubmitted = true;
+
+            m_waitSemaphores.clear();
+            m_paintPassIndex = (m_paintPassIndex + 1) % m_paintPassData.size();
+        }
+
+
+        // Present the image
+        // -----------------
+        if (!updateRegion.isEmpty()) {
+            m_backend->showOverlay();
+
+            std::shared_ptr<VulkanSemaphore> semaphore = m_separatePresentQueue ?
+                    frame.acquireOwnershipSemaphore : paintPass.semaphore;
+
+            const VkResult result = \
m_swapchain->present(m_swapchain->currentIndex(), updateRegion, semaphore->handle()); \
+            m_imageAcquired = false; +
+            switch (result) {
+            case VK_SUCCESS:
+                break;
+
+            case VK_SUBOPTIMAL_KHR:
+                m_swapchain->invalidate();
+                effects->addRepaintFull();
+                break;
+
+            case VK_ERROR_OUT_OF_DATE_KHR:
+                effects->addRepaintFull();
+                m_waitSemaphores.insert(semaphore);
+                break;
+
+            case VK_ERROR_DEVICE_LOST:
+                handleDeviceLostError();
+                return 0;
+
+            default:
+            case VK_ERROR_OUT_OF_HOST_MEMORY:
+            case VK_ERROR_OUT_OF_DEVICE_MEMORY:
+            case VK_ERROR_SURFACE_LOST_KHR:
+                qCCritical(KWIN_VULKAN).noquote() << "vkQueuePresentKHR returned" << \
enumToString(result); +                handleFatalError();
+                return 0;
+            }
+
+            m_swapchain->addToDamageHistory(updateRegion);
+
+            m_frameIndex = (m_frameIndex + 1) % m_frameData.size();
+        } else if (paintPass.fenceSubmitted) {
+            // We have executed a paint pass, but updateRegion is empty.
+            // This means that any rendering done will only have repaired
+            // the back buffer, making it identical to the front buffer.
+            //
+            // In this case we won't post the back buffer. Instead we'll just
+            // set the buffer age to one, so the repaired regions won't be
+            // rendered again in the next paint pass.
+            m_bufferAge = 1;
+        }
+    }
+
+    return timer.nsecsElapsed();
+}
+
+
+void VulkanScene::paintSimpleScreen(int mask, QRegion region)
+{
+    m_screenProjectionMatrix = m_projectionMatrix;
+
+    Scene::paintSimpleScreen(mask, region);
+}
+
+
+void VulkanScene::paintGenericScreen(int mask, ScreenPaintData data)
+{
+    m_screenProjectionMatrix = m_projectionMatrix * screenMatrix(mask, data);
+
+    Scene::paintGenericScreen(mask, data);
+}
+
+
+void VulkanScene::paintBackground(QRegion region)
+{
+    const QRegion swapchainRegion(0, 0, m_swapchain->width(), \
m_swapchain->height()); +    region &= swapchainRegion;
+
+    if (region.isEmpty())
+        return;
+
+    if (region == swapchainRegion && !m_renderPassStarted) {
+        // If we haven't started the render pass yet, this is the initial clearing
+        // of the back buffer. Instead of clearing the buffer here, we will mark
+        // the clear as pending, and let beginRenderPass() handle it.
+        m_clearPending = true;
+        return;
+    }
+
+    auto vbo = uploadManager()->allocate(region.rectCount() * 4 * \
sizeof(QVector2D)); +    auto ubo = \
uploadManager()->emplaceUniform<VulkanPipelineManager::ColorUniformData>(m_projectionMatrix, \
QVector4D(0.0f, 0.0f, 0.0f, 1.0f)); +
+    QVector2D *vertex = static_cast<QVector2D *>(vbo.data());
+    for (const QRect &r : region) {
+        const float x0 = r.x();
+        const float y0 = r.y();
+        const float x1 = r.x() + r.width();
+        const float y1 = r.y() + r.height();
+
+        *vertex++ = { x0, y0 }; // Top-left
+        *vertex++ = { x1, y0 }; // Top-right
+        *vertex++ = { x1, y1 }; // Bottom-right
+        *vertex++ = { x0, y1 }; // Bottom-left
+    }
+
+    const QRect r = region.boundingRect();
+
+    const VkRect2D scissor = {
+        .offset = { (int32_t)  r.x(),     (int32_t)  r.y()      },
+        .extent = { (uint32_t) r.width(), (uint32_t) r.height() }
+    };
+
+    VkPipeline pipeline;
+    VkPipelineLayout pipelineLayout;
+
+    std::tie(pipeline, pipelineLayout) =
+            pipelineManager()->pipeline(VulkanPipelineManager::FlatColor,
+                                        VulkanPipelineManager::NoTraits,
+                                        VulkanPipelineManager::DescriptorSet,
+                                        VulkanPipelineManager::TriangleList,
+                                        VulkanPipelineManager::SwapchainRenderPass);
+
+    if (!m_clearDescriptorSet || m_clearDescriptorSet->uniformBuffer() != \
ubo.buffer()) { +        if (!m_clearDescriptorSet || \
m_clearDescriptorSet.use_count() > 1) +            m_clearDescriptorSet = \
std::make_shared<ColorDescriptorSet>(colorDescriptorPool()); +
+        m_clearDescriptorSet->update(ubo.buffer());
+    }
+
+    auto cmd = mainCommandBuffer();
+
+    cmd->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
+    cmd->bindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, { \
m_clearDescriptorSet->handle() }, { (uint32_t) ubo.offset() }); +    \
cmd->bindVertexBuffers(0, { vbo.handle() }, { vbo.offset() } ); +    \
cmd->bindIndexBuffer(indexBufferForQuadCount(region.rectCount()), 0, \
VK_INDEX_TYPE_UINT16); +    cmd->setScissor(0, 1, &scissor);
+    cmd->drawIndexed(region.rectCount() * 6, 1, 0, 0, 0);
+
+    addBusyReference(m_clearDescriptorSet);
+}
+
+
+void VulkanScene::paintCursor()
+{
+    // Don't paint if we use a hardware cursor
+    if (!kwinApp()->platform()->usesSoftwareCursor())
+        return;
+
+    // TODO
+}
+
+
+void VulkanScene::beginRenderPass(VulkanCommandBuffer *cmd)
+{
+    VkFramebuffer framebuffer = m_swapchain->currentFramebuffer();
+    VkRenderPass renderPass;
+
+    if (m_clearPending || m_surfaceLayout == VK_IMAGE_LAYOUT_UNDEFINED) {
+        renderPass = m_clearRenderPass;
+        m_clearPending = false;
+    } else if (m_surfaceLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) {
+        if (m_separatePresentQueue) {
+            renderPass = m_loadRenderPass;
+            m_needQueueOwnershipTransfer = true;
+        } else {
+            renderPass = m_transitionRenderPass;
+        }
+    } else {
+        assert(m_surfaceLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
+        renderPass = m_loadRenderPass;
+    }
+
+    const VkClearColorValue clearColor = {
+        .float32 = { 0.0f, 0.0f, 0.0f, 1.0f }
+    };
+
+    const VkClearValue clearValue = {
+        .color = clearColor,
+    };
+
+    uint32_t width = m_swapchain->width();
+    uint32_t height = m_swapchain->height();
+
+    const VkRenderPassBeginInfo renderPassBeginInfo = {
+        .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
+        .pNext = nullptr,
+        .renderPass = renderPass,
+        .framebuffer = framebuffer,
+        .renderArea = {
+            .offset = { 0,     0      },
+            .extent = { width, height }
+        },
+        .clearValueCount = 1,
+        .pClearValues = &clearValue
+    };
+
+    const VkViewport viewport = {
+        .x = 0.0f,
+        .y = 0.0f,
+        .width = float(width),
+        .height = float(height),
+        .minDepth = 0.0f,
+        .maxDepth = 1.0f
+    };
+
+    // Begin recording the command buffer
+    // ----------------------------------
+    cmd->begin(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
+
+    if (m_needQueueOwnershipTransfer) {
+        // Acquire graphics queue onwership of the swapchain image.
+        // The present queue release operation is handled by paint().
+        cmd->pipelineBarrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+                             VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+                             0,
+                             {},
+                             {},
+                             {
+                                 {
+                                     .sType = \
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, +                                     .pNext \
= nullptr, +                                     .srcAccessMask = 0,
+                                     .dstAccessMask = \
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | +                                               \
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, +                                     \
.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, +                                     \
.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, +                              \
.srcQueueFamilyIndex = m_presentQueue.familyIndex(), +                                \
.dstQueueFamilyIndex = m_graphicsQueue.familyIndex(), +                               \
.image = m_swapchain->currentImage(), +                                     \
.subresourceRange = { +                                         .aspectMask = \
VK_IMAGE_ASPECT_COLOR_BIT, +                                         .baseMipLevel = \
0, +                                         .levelCount = 1,
+                                         .baseArrayLayer = 0,
+                                         .layerCount = 1
+                                     }
+                                 }
+                             });
+    }
+
+    cmd->beginRenderPass(&renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
+    cmd->setViewport(0, 1, &viewport);
+
+    m_surfaceLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+    m_renderPassStarted = true;
+}
+
+
+VulkanCommandBuffer *VulkanScene::mainCommandBuffer()
+{
+    auto &paintPass = currentPaintPass();
+    auto cmd = currentPaintPass().mainCommandBuffer.get();
+
+    if (!cmd->isActive()) {
+        assert(!paintPass.fenceSubmitted);
+        assert(!m_renderPassStarted);
+        beginRenderPass(cmd);
+        m_commandBuffersPending = true;
+    }
+
+    return cmd;
+}
+
+
+VulkanCommandBuffer *VulkanScene::setupCommandBuffer()
+{
+    auto &paintPass = currentPaintPass();
+    auto cmd = paintPass.setupCommandBuffer.get();
+
+    if (!cmd->isActive()) {
+        if (paintPass.fenceSubmitted) {
+            paintPass.fence.wait();
+            paintPass.fenceSubmitted = false;
+
+            // Reset the command pools
+            for (auto &pool : paintPass.commandPools)
+                pool.reset();
+
+            // Drop busy references
+            paintPass.busyObjects.clear();
+        }
+
+        cmd->begin(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
+        m_commandBuffersPending = true;
+    }
+
+    return cmd;
+}
+
+
+CompositingType VulkanScene::compositingType() const
+{
+    return VulkanCompositing;
+}
+
+
+bool VulkanScene::initFailed() const
+{
+    return !m_valid;
+}
+
+
+Scene::EffectFrame *VulkanScene::createEffectFrame(EffectFrameImpl *frame)
+{
+    return new VulkanEffectFrame(frame, this);
+}
+
+
+Shadow *VulkanScene::createShadow(Toplevel *toplevel)
+{
+    return new VulkanShadow(toplevel, this);
+}
+
+
+Decoration::Renderer \
*VulkanScene::createDecorationRenderer(Decoration::DecoratedClientImpl *impl) +{
+    return new VulkanDecorationRenderer(impl, this);
+}
+
+
+void VulkanScene::screenGeometryChanged(const QSize &size)
+{
+    m_backend->screenGeometryChanged(size);
+
+    if (m_swapchain)
+        m_swapchain->invalidate();
+}
+
+
+Scene::Window *VulkanScene::createWindow(Toplevel *toplevel)
+{
+    return new VulkanWindow(toplevel, this);
+}
+
+
+bool VulkanScene::createRenderPasses()
+{
+    const VkAttachmentDescription loadBackBufferAttachment {
+        .flags = 0,
+        .format = m_surfaceFormat,
+        .samples = VK_SAMPLE_COUNT_1_BIT,
+        .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD,
+        .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
+        .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
+        .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
+        .initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+        .finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
+    };
+
+    const VkAttachmentDescription transitionBackBufferAttachment {
+        .flags = 0,
+        .format = m_surfaceFormat,
+        .samples = VK_SAMPLE_COUNT_1_BIT,
+        .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD,
+        .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
+        .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
+        .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
+        .initialLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
+        .finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
+    };
+
+    const VkAttachmentDescription clearBackBufferAttachment {
+        .flags = 0,
+        .format = m_surfaceFormat,
+        .samples = VK_SAMPLE_COUNT_1_BIT,
+        .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
+        .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
+        .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
+        .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
+        .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
+        .finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
+    };
+
+    static const VkAttachmentReference colorAttachmentRef {
+        .attachment = 0,
+        .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
+    };
+
+    const VkSubpassDescription subpass {
+        .flags = 0,
+        .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
+        .inputAttachmentCount = 0,
+        .pInputAttachments = nullptr,
+        .colorAttachmentCount = 1,
+        .pColorAttachments = &colorAttachmentRef,
+        .pResolveAttachments = nullptr,
+        .pDepthStencilAttachment = nullptr,
+        .preserveAttachmentCount = 0,
+        .pPreserveAttachments = nullptr
+    };
+
+    static const VkSubpassDependency clearStartDependency = {
+        .srcSubpass = VK_SUBPASS_EXTERNAL,
+        .dstSubpass = 0,
+        .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+        .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+        .srcAccessMask = 0,
+        .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+        .dependencyFlags = 0
+    };
+
+    static const VkSubpassDependency transitionStartDependency = {
+        .srcSubpass = VK_SUBPASS_EXTERNAL,
+        .dstSubpass = 0,
+        .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+        .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+        .srcAccessMask = 0,
+        .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | \
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, +        .dependencyFlags = 0
+    };
+
+    static const VkSubpassDependency loadStartDependency = {
+        .srcSubpass = VK_SUBPASS_EXTERNAL,
+        .dstSubpass = 0,
+        .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+        .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+        .srcAccessMask = 0,
+        .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | \
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, +        .dependencyFlags = 0
+    };
+
+    static const VkSubpassDependency endDependency = {
+        .srcSubpass = 0,
+        .dstSubpass = VK_SUBPASS_EXTERNAL,
+        .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+        .dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
+        .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+        .dstAccessMask = 0,
+        .dependencyFlags = 0
+    };
+
+    m_clearRenderPass          = VulkanRenderPass(device(), { \
clearBackBufferAttachment      }, { subpass }, { clearStartDependency,      \
endDependency }); +    m_transitionRenderPass     = VulkanRenderPass(device(), { \
transitionBackBufferAttachment }, { subpass }, { transitionStartDependency, \
endDependency }); +    m_loadRenderPass           = VulkanRenderPass(device(), { \
loadBackBufferAttachment       }, { subpass }, { loadStartDependency,       \
endDependency }); +
+    return true;
+}
+
+
+VkBuffer VulkanScene::indexBufferForQuadCount(uint32_t quadCount)
+{
+    if (m_indexBufferQuadCount >= quadCount) {
+        if (!m_usedIndexBuffer) {
+            addBusyReference(m_indexBuffer);
+            addBusyReference(m_indexBufferMemory);
+
+            m_usedIndexBuffer = true;
+        }
+
+        return m_indexBuffer->handle();
+    }
+
+    const size_t bytesPerQuad = 6 * sizeof(uint16_t);
+    quadCount = std::max<uint32_t>(align(quadCount * bytesPerQuad, 4096), 16384) / \
bytesPerQuad; +
+    auto writeIndices = [](uint16_t *dst, uint32_t quadCount) {
+        for (uint32_t i = 0; i < quadCount; i++) {
+            for (int j : {1, 0, 3, 3, 2, 1}) {
+                *dst++ = i * 4 + j;
+            }
+        }
+    };
+
+    size_t size = quadCount * bytesPerQuad;
+    m_indexBuffer = std::make_shared<VulkanBuffer>(device(),
+                                                   VkBufferCreateInfo {
+                                                       .sType = \
VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, +                                               \
.pNext = nullptr, +                                                       .flags = 0,
+                                                       .size = size,
+                                                       .usage = \
VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, +                \
.sharingMode = VK_SHARING_MODE_EXCLUSIVE, +                                           \
.queueFamilyIndexCount = 0, +                                                       \
.pQueueFamilyIndices = nullptr, +                                                    \
}); +
+    m_indexBufferMemory = deviceMemoryAllocator()->allocateMemory(m_indexBuffer, \
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 0); +
+    if (m_indexBufferMemory->isHostVisible()) {
+        // Map the memory if it is host visible and write the indices directly into \
it +        uint16_t *indices = nullptr;
+        m_indexBufferMemory->map(0, (void **) &indices);
+
+        writeIndices(indices, quadCount);
+
+        if (!m_indexBufferMemory->isHostCoherent()) {
+            m_device->flushMappedMemoryRanges({
+                                                  {
+                                                      .sType = \
VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, +                                              \
.pNext = nullptr, +                                                      .memory = \
m_indexBufferMemory->handle(), +                                                      \
.offset = 0, +                                                      .size = \
VK_WHOLE_SIZE +                                                  }
+                                              });
+        }
+
+        m_indexBufferMemory->unmap();
+    } else {
+        // Upload the indices to staging memory and copy them to the index buffer
+        auto range = uploadManager()->allocate(size);
+        writeIndices((uint16_t *) range.data(), quadCount);
+        auto cmd = setupCommandBuffer();
+
+        cmd->copyBuffer(range.buffer()->handle(),
+                        m_indexBuffer->handle(),
+                        {
+                            {
+                                .srcOffset = range.offset(),
+                                .dstOffset = 0,
+                                .size = size
+                            }
+                        });
+
+        const VkBufferMemoryBarrier barrier {
+            .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
+            .pNext = nullptr,
+            .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
+            .dstAccessMask = VK_ACCESS_INDEX_READ_BIT,
+            .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+            .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+            .buffer = m_indexBuffer->handle(),
+            .offset = 0,
+            .size = size
+        };
+
+        cmd->pipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
+                             VK_PIPELINE_STAGE_VERTEX_INPUT_BIT,
+                             0, {}, { barrier }, {});
+    }
+
+    addBusyReference(m_indexBuffer);
+    addBusyReference(m_indexBufferMemory);
+
+    m_indexBufferQuadCount = quadCount;
+    m_usedIndexBuffer = true;
+
+    return m_indexBuffer->handle();
+}
+
+
+} // namespace KWin
diff --git a/plugins/scenes/vulkan/scene.h b/plugins/scenes/vulkan/scene.h
new file mode 100644
index 000000000..219078818
--- /dev/null
+++ b/plugins/scenes/vulkan/scene.h
@@ -0,0 +1,249 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright  © 2017-2018 Fredrik Höglund <fredrik@kde.org>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*********************************************************************/
+
+#ifndef VULKAN_SCENE_H
+#define VULKAN_SCENE_H
+
+#include "../../../scene.h"
+#include "decorations/decorationrenderer.h"
+#include "kwinvulkanutils.h"
+
+#include <deque>
+#include <queue>
+#include <unordered_set>
+
+namespace KWin
+{
+
+class VulkanBackend;
+class VulkanSwapchain;
+class SceneDescriptorPool;
+class TextureDescriptorSet;
+class ColorDescriptorSet;
+class Shadow;
+
+
+
+// ------------------------------------------------------------------
+
+
+
+class KWIN_EXPORT VulkanFactory : public SceneFactory
+{
+    Q_OBJECT
+    Q_INTERFACES(KWin::SceneFactory)
+    Q_PLUGIN_METADATA(IID "org.kde.kwin.Scene" FILE "vulkan.json")
+
+public:
+    explicit VulkanFactory(QObject *parent = nullptr);
+    ~VulkanFactory() override;
+
+    Scene *create(QObject *parent = nullptr) const override;
+};
+
+
+
+// ------------------------------------------------------------------
+
+
+
+template <typename T>
+class SharedVulkanObjectSet : public std::unordered_set<std::shared_ptr<T>>
+{
+public:
+    SharedVulkanObjectSet() : std::unordered_set<std::shared_ptr<T>>() {}
+
+    auto toNativeHandleVector() const {
+        std::vector<typename T::NativeHandleType> vector;
+        vector.reserve(this->size());
+        for (const auto &entry : *this)
+            vector.push_back(entry->handle());
+        return vector;
+    }
+};
+
+
+
+// ------------------------------------------------------------------
+
+
+
+class VulkanScene : public Scene
+{
+    Q_OBJECT
+
+    enum QueueFamily {
+        GraphicsQueueFamily = 0,
+        QueueFamilyCount
+    };
+
+    struct FrameData
+    {
+        std::shared_ptr<VulkanSemaphore> imageAcquisitionSemaphore;
+        std::shared_ptr<VulkanSemaphore> acquireOwnershipSemaphore;
+        std::shared_ptr<VulkanSemaphore> releaseOwnershipSemaphore;
+        VulkanFence imageAcquisitionFence;
+        bool imageAcquisitionFenceSubmitted;
+    };
+
+    struct PaintPassData
+    {
+        VulkanCommandPool commandPools[QueueFamilyCount];
+        std::unique_ptr<VulkanCommandBuffer> setupCommandBuffer;
+        std::unique_ptr<VulkanCommandBuffer> mainCommandBuffer;
+        std::unordered_set<std::shared_ptr<VulkanObject>> busyObjects;
+        std::unordered_set<std::shared_ptr<VulkanCommandBuffer>> \
busyGraphicsCommandBuffers; +        std::shared_ptr<VulkanSemaphore> semaphore;
+        VulkanFence fence;
+        bool fenceSubmitted;
+    };
+
+    enum { FramesInFlight = 2 };
+
+public:
+    explicit VulkanScene(std::unique_ptr<VulkanBackend> &&backend, QObject *parent = \
nullptr); +    ~VulkanScene() override;
+
+    bool usesOverlayWindow() const override;
+    OverlayWindow* overlayWindow() override;
+    qint64 paint(QRegion damage, ToplevelList windows) override;
+    void paintSimpleScreen(int mask, QRegion region) override;
+    void paintGenericScreen(int mask, ScreenPaintData data) override;
+    void paintBackground(QRegion region) override;
+    void paintCursor() override;
+    CompositingType compositingType() const override;
+    bool initFailed() const override;
+    EffectFrame *createEffectFrame(EffectFrameImpl *frame) override;
+    Shadow *createShadow(Toplevel *toplevel) override;
+    Decoration::Renderer *createDecorationRenderer(Decoration::DecoratedClientImpl \
*impl) override; +    void screenGeometryChanged(const QSize &size) override;
+
+    bool animationsSupported() const override { return true; }
+
+    VulkanBackend *backend() const { return m_backend.get(); }
+
+    /**
+     * Returns a pointer to the logical device.
+     */
+    VulkanDevice *device() const { return m_device.get(); }
+
+    VulkanPipelineManager *pipelineManager() const { return m_pipelineManager.get(); \
} +    VulkanUploadManager *uploadManager() const { return m_uploadManager.get(); }
+    VulkanUploadManager *imageUploadManager() const { return \
m_imageUploadManager.get(); } +    VulkanDeviceMemoryAllocator \
*deviceMemoryAllocator() const { return m_deviceMemoryAllocator.get(); } +    \
SceneDescriptorPool *textureDescriptorPool() const { return \
m_textureDescriptorPool.get(); } +    SceneDescriptorPool *crossFadeDescriptorPool() \
const { return m_crossFadeDescriptorPool.get(); } +    SceneDescriptorPool \
*colorDescriptorPool() const { return m_colorDescriptorPool.get(); } +
+    VkBuffer indexBufferForQuadCount(uint32_t quadCount);
+
+    FrameData &currentFrame() { return m_frameData[m_frameIndex]; }
+    const FrameData &currentFrame() const { return m_frameData[m_frameIndex]; }
+
+    PaintPassData &currentPaintPass() { return m_paintPassData[m_paintPassIndex]; }
+    const PaintPassData &currentPaintPass() const { return \
m_paintPassData[m_paintPassIndex]; } +
+    /**
+     * Adds the given object to the list of objects referenced by the current frame.
+     */
+    void addBusyReference(std::shared_ptr<VulkanObject> object) { \
currentPaintPass().busyObjects.insert(object); } +
+    QMatrix4x4 projectionMatrix() const { return m_projectionMatrix; }
+    QMatrix4x4 screenProjectionMatrix() const override final { return \
m_screenProjectionMatrix; } +
+    VulkanCommandBuffer *mainCommandBuffer();
+    VulkanCommandBuffer *setupCommandBuffer();
+
+    VkSampler linearSampler() const { return m_linearSampler.handle(); }
+    VkSampler nearestSampler() const { return m_nearestSampler.handle(); }
+
+    uint32_t optimalBufferCopyOffsetAlignment() const { return \
m_physicalDeviceProperties.limits.optimalBufferCopyOffsetAlignment; } +    uint32_t \
optimalBufferCopyRowPitchAlignment() const { return \
m_physicalDeviceProperties.limits.optimalBufferCopyRowPitchAlignment; } +
+    QMatrix4x4 ortho(float left, float right, float bottom, float top, float near, \
float far) const; +    QMatrix4x4 frustum(float left, float right, float bottom, \
float top, float zNear, float zFar) const; +
+protected:
+    Scene::Window *createWindow(Toplevel *toplevel) override;
+
+private:
+    bool init();
+    bool createRenderPasses();
+    void beginRenderPass(VulkanCommandBuffer *cmd);
+    QMatrix4x4 createProjectionMatrix() const;
+    QMatrix4x4 screenMatrix(int mask, const ScreenPaintData &data) const;
+    void handleDeviceLostError();
+    void handleFatalError();
+    bool checkResult(VkResult result, std::function<void(void)> debugMessage);
+
+private:
+    VulkanInstance                                           m_instance;
+    VulkanDebugReportCallback                                m_debugReportCallback;
+    std::unique_ptr<VulkanBackend>                           m_backend;
+    std::unique_ptr<VulkanDevice>                            m_device;
+    std::unique_ptr<VulkanPipelineCache>                     m_pipelineCache;
+    std::unique_ptr<VulkanPipelineManager>                   m_pipelineManager;
+    std::unique_ptr<VulkanUploadManager>                     m_uploadManager;
+    std::unique_ptr<VulkanUploadManager>                     m_imageUploadManager;
+    std::unique_ptr<VulkanDeviceMemoryAllocator>             \
m_deviceMemoryAllocator; +    std::unique_ptr<SceneDescriptorPool>                    \
m_textureDescriptorPool; +    std::unique_ptr<SceneDescriptorPool>                    \
m_crossFadeDescriptorPool; +    std::unique_ptr<SceneDescriptorPool>                  \
m_colorDescriptorPool; +    VulkanCommandPool                                        \
m_presentCommandPool; +    std::unique_ptr<VulkanSwapchain>                         \
m_swapchain; +    VulkanQueue                                              \
m_graphicsQueue; +    VulkanQueue                                              \
m_presentQueue; +    VulkanRenderPass                                         \
m_clearRenderPass; +    VulkanRenderPass                                         \
m_transitionRenderPass; +    VulkanRenderPass                                         \
m_loadRenderPass; +    VkFormat                                                 \
m_surfaceFormat; +    VkColorSpaceKHR                                          \
m_surfaceColorSpace; +    VkPresentModeKHR                                         \
m_presentMode; +    VkPhysicalDeviceProperties                               \
m_physicalDeviceProperties; +    uint32_t                                             \
m_maxDiscardRects; +    uint32_t                                                 \
m_maxPushDescriptors; +    VkImageLayout                                            \
m_surfaceLayout = VK_IMAGE_LAYOUT_UNDEFINED; +    std::array<FrameData, \
FramesInFlight>                    m_frameData; +    std::array<PaintPassData, \
FramesInFlight>                m_paintPassData; +    \
SharedVulkanObjectSet<VulkanSemaphore>                   m_waitSemaphores; +    \
std::shared_ptr<VulkanBuffer>                            m_indexBuffer; +    \
std::shared_ptr<VulkanDeviceMemory>                      m_indexBufferMemory; +    \
uint32_t                                                 m_indexBufferQuadCount = 0; \
+    std::shared_ptr<ColorDescriptorSet>                      m_clearDescriptorSet; + \
VulkanSampler                                            m_nearestSampler; +    \
VulkanSampler                                            m_linearSampler; +    \
QMatrix4x4                                               m_projectionMatrix; +    \
QMatrix4x4                                               m_screenProjectionMatrix; +  \
int                                                      m_frameIndex = 0; +    int   \
m_paintPassIndex = 0; +    int                                                      \
m_bufferAge = 0; +    bool                                                     \
m_valid = false; +    bool                                                     \
m_separatePresentQueue = false; +    bool                                             \
m_needQueueOwnershipTransfer = false; +    bool                                       \
m_imageAcquired = false; +    bool                                                    \
m_renderPassStarted = false; +    bool                                                \
m_commandBuffersPending = false; +    bool                                            \
m_clearPending = false; +    bool                                                     \
m_usedIndexBuffer = false; +};
+
+} // namespace KWin
+
+#endif // VULKAN_SCENE_H
diff --git a/plugins/scenes/vulkan/shadow.cpp b/plugins/scenes/vulkan/shadow.cpp
new file mode 100644
index 000000000..2d985ffd0
--- /dev/null
+++ b/plugins/scenes/vulkan/shadow.cpp
@@ -0,0 +1,50 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright  © 2017-2018 Fredrik Höglund <fredrik@kde.org>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*********************************************************************/
+
+#include "shadow.h"
+
+namespace KWin
+{
+
+
+VulkanShadow::VulkanShadow(Toplevel *toplevel, VulkanScene *scene)
+    : Shadow(toplevel),
+      m_scene(scene)
+{
+}
+
+
+VulkanShadow::~VulkanShadow()
+{
+}
+
+
+void VulkanShadow::buildQuads()
+{
+}
+
+
+bool VulkanShadow::prepareBackend()
+{
+    return true;
+}
+
+
+} // namespace KWin
diff --git a/plugins/scenes/vulkan/shadow.h b/plugins/scenes/vulkan/shadow.h
new file mode 100644
index 000000000..17e774457
--- /dev/null
+++ b/plugins/scenes/vulkan/shadow.h
@@ -0,0 +1,49 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright  © 2017-2018 Fredrik Höglund <fredrik@kde.org>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*********************************************************************/
+
+#ifndef VULKAN_SHADOW_H
+#define VULKAN_SHADOW_H
+
+#include "scene.h"
+#include "../../../shadow.h"
+
+
+namespace KWin
+{
+
+class VulkanShadow : public Shadow
+{
+public:
+    explicit VulkanShadow(Toplevel *toplevel, VulkanScene *scene);
+    ~VulkanShadow() override;
+
+    VulkanScene *scene() const { return m_scene; }
+
+protected:
+    void buildQuads() override final;
+    bool prepareBackend() override final;
+
+private:
+    VulkanScene *m_scene;
+};
+
+} // namespace KWin
+
+#endif // VULKAN_SHADOW_H
diff --git a/plugins/scenes/vulkan/swapchain.cpp \
b/plugins/scenes/vulkan/swapchain.cpp new file mode 100644
index 000000000..cfa3f1ca8
--- /dev/null
+++ b/plugins/scenes/vulkan/swapchain.cpp
@@ -0,0 +1,345 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright  © 2017-2018 Fredrik Höglund <fredrik@kde.org>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*********************************************************************/
+
+#include "swapchain.h"
+#include "screens.h"
+#include "logging.h"
+
+namespace KWin
+{
+
+template <typename T>
+constexpr const T &clamp(const T &v, const T &lo, const T &hi)
+{
+    return std::max(lo, std::min(hi, v));
+}
+
+
+VulkanSwapchain::VulkanSwapchain(VulkanDevice *device,
+                                 VulkanQueue queue,
+                                 VkSurfaceKHR surface,
+                                 VkFormat format,
+                                 VkColorSpaceKHR colorSpace,
+                                 VkPresentModeKHR presentMode,
+                                 VkRenderPass renderPass)
+    : m_device(device),
+      m_queue(queue),
+      m_surface(surface),
+      m_format(format),
+      m_colorSpace(colorSpace),
+      m_presentMode(presentMode),
+      m_preferredImageCount(2),
+      m_renderPass(renderPass),
+      m_swapchain(VK_NULL_HANDLE),
+      m_extent({0, 0}),
+      m_isValid(false)
+{
+}
+
+
+VulkanSwapchain::~VulkanSwapchain()
+{
+    // Destroy the old image views and framebuffers
+    for (Buffer &buffer : m_buffers) {
+        if (buffer.framebuffer)
+            m_device->destroyFramebuffer(buffer.framebuffer, nullptr);
+
+        if (buffer.imageView)
+            m_device->destroyImageView(buffer.imageView, nullptr);
+    }
+
+    m_device->destroySwapchainKHR(m_swapchain, nullptr);
+}
+
+
+VkResult VulkanSwapchain::create()
+{
+    m_isValid = false;
+
+    auto physicalDevice = m_device->physicalDevice();
+
+    VkSurfaceCapabilitiesKHR capabilities;
+    VkResult result = physicalDevice.getSurfaceCapabilitiesKHR(m_surface, \
&capabilities); +
+    if (result != VK_SUCCESS) {
+        qCCritical(KWIN_VULKAN) << "vkPhysicalDeviceGetSurfaceCapabilitiesKHR \
returned" << enumToString(result); +        return result;
+    }
+
+    if (capabilities.currentExtent.width == UINT32_MAX) {
+        const QSize size = Screens::self()->size();
+        m_extent.width  = clamp<uint32_t>(size.width(),  \
capabilities.minImageExtent.width,  capabilities.maxImageExtent.width); +        \
m_extent.height = clamp<uint32_t>(size.height(), capabilities.minImageExtent.height, \
capabilities.maxImageExtent.height); +    } else {
+        m_extent = capabilities.currentExtent;
+    }
+
+    uint32_t imageCount = std::max<uint32_t>(capabilities.minImageCount, \
m_preferredImageCount); +    if (capabilities.maxImageCount > 0)
+        imageCount = std::min(imageCount, capabilities.maxImageCount);
+
+    VkSurfaceTransformFlagBitsKHR preTransform;
+    if (capabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)
+        preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
+    else
+        preTransform = capabilities.currentTransform;
+
+    VkCompositeAlphaFlagBitsKHR compositeAlpha;
+
+    if (capabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR)
+        compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
+    else if (capabilities.supportedCompositeAlpha & \
VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR) +        compositeAlpha = \
VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR; +    else if \
(capabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR) +  \
compositeAlpha = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR; +    else
+        compositeAlpha = VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR;
+
+    VkImageUsageFlags imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | \
VK_IMAGE_USAGE_TRANSFER_SRC_BIT; +    imageUsage &= capabilities.supportedUsageFlags;
+
+    VkSwapchainKHR oldSwapchain = m_swapchain;
+
+    const VkSwapchainCreateInfoKHR createInfo = {
+        .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
+        .pNext = nullptr,
+        .flags = 0,
+        .surface = m_surface,
+        .minImageCount = imageCount,
+        .imageFormat = m_format,
+        .imageColorSpace = m_colorSpace,
+        .imageExtent = m_extent,
+        .imageArrayLayers = 1,
+        .imageUsage = imageUsage,
+        .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
+        .queueFamilyIndexCount = 0,
+        .pQueueFamilyIndices = nullptr,
+        .preTransform = preTransform,
+        .compositeAlpha = compositeAlpha,
+        .presentMode = m_presentMode,
+        .clipped = VK_FALSE,
+        .oldSwapchain = oldSwapchain
+    };
+
+    result = m_device->createSwapchainKHR(&createInfo, nullptr, &m_swapchain);
+
+    if (result != VK_SUCCESS) {
+        qCCritical(KWIN_VULKAN) << "vkCreateSwapchainKHR returned" << \
enumToString(result); +        return result;
+    }
+
+    m_queue.waitIdle();
+
+    // Destroy the old swap chain now if we are recreating it
+    if (oldSwapchain != VK_NULL_HANDLE) {
+        m_device->destroySwapchainKHR(oldSwapchain, nullptr);
+    }
+
+    // Destroy the old image views, framebuffers and command buffers
+    for (Buffer &buffer : m_buffers) {
+        if (buffer.framebuffer)
+            m_device->destroyFramebuffer(buffer.framebuffer, nullptr);
+
+        if (buffer.imageView)
+            m_device->destroyImageView(buffer.imageView, nullptr);
+
+        buffer.acquireOwnershipCommandBuffer = nullptr;
+        buffer.releaseOwnershipCommandBuffer = nullptr;
+    }
+
+    // Get the actual swapchain image count
+    if ((result = m_device->getSwapchainImagesKHR(m_swapchain, &imageCount, \
nullptr)) != VK_SUCCESS) { +        qCCritical(KWIN_VULKAN) << \
"vkGetSwapchainImagesKHR returned" << enumToString(result); +        return result;
+    }
+
+    // Get the swapchain images
+    std::vector<VkImage> images(imageCount);
+    if ((result = m_device->getSwapchainImagesKHR(m_swapchain, &imageCount, \
images.data())) != VK_SUCCESS) { +        qCCritical(KWIN_VULKAN) << \
"vkGetSwapchainImagesKHR returned" << enumToString(result); +        return result;
+    }
+
+    m_buffers.resize(images.size());
+
+    // Create the image views and framebuffers
+    for (unsigned int i = 0; i < m_buffers.size(); i++) {
+        m_buffers[i].image = images[i];
+        m_buffers[i].imageView = VK_NULL_HANDLE;
+        m_buffers[i].framebuffer = VK_NULL_HANDLE;
+        m_buffers[i].sequence = 0;
+
+        const VkImageViewCreateInfo imageViewCreateInfo = {
+            .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
+            .pNext = nullptr,
+            .flags = 0,
+            .image = images[i],
+            .viewType = VK_IMAGE_VIEW_TYPE_2D,
+            .format = m_format,
+            .components = {
+                .r = VK_COMPONENT_SWIZZLE_IDENTITY,
+                .g = VK_COMPONENT_SWIZZLE_IDENTITY,
+                .b = VK_COMPONENT_SWIZZLE_IDENTITY,
+                .a = VK_COMPONENT_SWIZZLE_IDENTITY
+            },
+            .subresourceRange = {
+                .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+                .baseMipLevel = 0,
+                .levelCount = 1,
+                .baseArrayLayer = 0,
+                .layerCount = 1
+            }
+        };
+
+        if ((result = m_device->createImageView(&imageViewCreateInfo, nullptr, \
&m_buffers[i].imageView)) != VK_SUCCESS) { +            qCCritical(KWIN_VULKAN) << \
"vkCreateImageView returned" << enumToString(result); +            return result;
+        }
+
+        const VkFramebufferCreateInfo framebufferCreateInfo = {
+            .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
+            .pNext = nullptr,
+            .flags = 0,
+            .renderPass = m_renderPass,
+            .attachmentCount = 1,
+            .pAttachments = &m_buffers[i].imageView,
+            .width = m_extent.width,
+            .height = m_extent.height,
+            .layers = 1
+        };
+
+        if ((result = m_device->createFramebuffer(&framebufferCreateInfo, nullptr, \
&m_buffers[i].framebuffer)) != VK_SUCCESS) { +            qCCritical(KWIN_VULKAN) << \
"vkCreateFramebuffer returned" << enumToString(result); +            return result;
+        }
+    }
+
+    m_damageHistory.clear();
+    m_isValid = true;
+    m_sequence = 0;
+    m_index = -1;
+
+    return VK_SUCCESS;
+}
+
+
+VkResult VulkanSwapchain::acquireNextImage(uint64_t timeout, VkSemaphore semaphore, \
VkFence fence, int32_t *imageIndex) +{
+    ++m_sequence;
+
+    if (m_index != -1)
+        m_buffers[m_index].sequence = m_sequence;
+
+    VkResult result = m_device->acquireNextImageKHR(m_swapchain, timeout, semaphore, \
fence, (uint32_t *) &m_index); +
+    m_isValid = (result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR);
+
+    if (imageIndex && m_isValid)
+        *imageIndex = m_index;
+
+    return result;
+}
+
+
+VkResult VulkanSwapchain::present(uint32_t imageIndex, const QRegion &updateRegion, \
VkSemaphore waitSemaphore) +{
+    std::vector<VkRectLayerKHR> rects;
+
+    if (m_supportsIncrementalPresent) {
+        rects.reserve(updateRegion.rectCount());
+
+        for (const QRect &r : updateRegion) {
+            rects.emplace_back(VkRectLayerKHR {
+                                   .offset = { r.x(),                 r.y()          \
}, +                                   .extent = { (uint32_t) r.width(),  (uint32_t) \
r.height() }, +                                   .layer  = 0
+                               });
+
+        }
+    }
+
+    const VkPresentRegionKHR region = {
+        .rectangleCount = (uint32_t) rects.size(),
+        .pRectangles = rects.data()
+    };
+
+    const VkPresentRegionsKHR regions = {
+        .sType = VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR,
+        .pNext = nullptr,
+        .swapchainCount = 1,
+        .pRegions = &region
+    };
+
+    const VkPresentInfoKHR presentInfo = {
+        .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
+        .pNext = m_supportsIncrementalPresent ? &regions : nullptr,
+        .waitSemaphoreCount = waitSemaphore ? 1u : 0u,
+        .pWaitSemaphores = &waitSemaphore,
+        .swapchainCount = 1,
+        .pSwapchains = &m_swapchain,
+        .pImageIndices = &imageIndex,
+        .pResults = nullptr
+    };
+
+    VkResult result = m_queue.presentKHR(&presentInfo);
+
+    if (result != VK_SUCCESS)
+        m_isValid = false;
+
+    return result;
+}
+
+
+uint32_t VulkanSwapchain::bufferAge() const
+{
+    if (m_index != -1 && m_buffers[m_index].sequence != 0)
+        return m_sequence - m_buffers[m_index].sequence + 1;
+
+    return 0;
+}
+
+
+void VulkanSwapchain::addToDamageHistory(const QRegion &region)
+{
+    if (m_damageHistory.size() > 10)
+        m_damageHistory.pop_back();
+
+    m_damageHistory.emplace_front(region);
+}
+
+
+QRegion VulkanSwapchain::accumulatedDamageHistory(int bufferAge) const
+{
+    QRegion region;
+
+    // Note: An age of zero means the buffer contents are undefined
+    if (bufferAge > 0 && bufferAge <= (int) m_damageHistory.size()) {
+        auto it = m_damageHistory.cbegin();
+        for (int i = 0; i < bufferAge - 1; i++)
+            region |= *it++;
+    } else {
+        const QSize &s = screens()->size();
+        region = QRegion(0, 0, s.width(), s.height());
+    }
+
+    return region;
+}
+
+} // namespace KWin
diff --git a/plugins/scenes/vulkan/swapchain.h b/plugins/scenes/vulkan/swapchain.h
new file mode 100644
index 000000000..ca80b52a6
--- /dev/null
+++ b/plugins/scenes/vulkan/swapchain.h
@@ -0,0 +1,169 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright  © 2017-2018 Fredrik Höglund <fredrik@kde.org>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*********************************************************************/
+
+#ifndef SWAPCHAIN_H
+#define SWAPCHAIN_H
+
+#include "scene.h"
+
+namespace KWin
+{
+
+class VulkanSwapchain
+{
+    struct Buffer {
+        VkImage image;
+        VkImageView imageView;
+        VkFramebuffer framebuffer;
+        std::shared_ptr<VulkanCommandBuffer> acquireOwnershipCommandBuffer;
+        std::shared_ptr<VulkanCommandBuffer> releaseOwnershipCommandBuffer;
+        uint64_t sequence;
+    };
+
+public:
+    /**
+     * Creates a Vulkan swap chain.
+     *
+     * @param device        The logical device
+     * @param surface       The surface the swap chain will present to
+     * @param format        The format the swap chain images will have
+     * @param colorspace    The color space the swap chain images will use
+     * @param renderPass    The render pass the swap chain images will be compatible \
with +     */
+    VulkanSwapchain(VulkanDevice *device,
+                    VulkanQueue queue,
+                    VkSurfaceKHR surface,
+                    VkFormat format,
+                    VkColorSpaceKHR colorspace,
+                    VkPresentModeKHR presentMode,
+                    VkRenderPass renderPass);
+
+    /**
+     * Destroys the swap chain.
+     */
+    ~VulkanSwapchain();
+
+    /**
+     * (Re)creates the swap chain.
+     *
+     * This function must be called after the surface has been resized.
+     */
+    VkResult create();
+
+    /**
+     * Sets whether the implementation supports VK_KHR_incremental_present or not.
+     */
+    void setSupportsIncrementalPresent(bool value) { m_supportsIncrementalPresent = \
value; } +
+    /**
+     * Sets the preferred number of images in the swap chain.
+     */
+    void setPreferredImageCount(int imageCount) { m_preferredImageCount = \
imageCount; } +
+    /**
+     * Returns true if the swap chain is valid, and false otherwise.
+     */
+    bool isValid() const { return m_isValid; }
+
+    /**
+     * Marks the swapchain as invalid.
+     */
+    void invalidate() { m_isValid = false; }
+
+    /**
+     * Acquires a new image from the swap chain.
+     */
+    VkResult acquireNextImage(uint64_t timeout, VkSemaphore semaphore, VkFence \
fence, int32_t *imageIndex); +
+    VkResult present(uint32_t imageIndex, const QRegion &updateRegion, VkSemaphore \
waitSemaphore); +
+    /**
+     * Returns the index of the current image, or -1 if no image has been acquired.
+     */
+    int32_t currentIndex() const { return m_index; }
+
+    VkImage image(int32_t index) const { return m_buffers[index].image; }
+    VkImageView imageView(int32_t index) const { return m_buffers[index].imageView; \
} +    VkFramebuffer framebuffer(int32_t index) const { return \
m_buffers[index].framebuffer; } +
+    VkImage currentImage() const { return image(m_index); }
+    VkImageView currentImageView() const { return imageView(m_index); }
+    VkFramebuffer currentFramebuffer() const { return framebuffer(m_index); }
+
+    std::shared_ptr<VulkanCommandBuffer> &acquireOwnershipCommandBuffer() { return \
m_buffers[m_index].acquireOwnershipCommandBuffer; } +    \
std::shared_ptr<VulkanCommandBuffer> &releaseOwnershipCommandBuffer() { return \
m_buffers[m_index].releaseOwnershipCommandBuffer; } +
+    /**
+     * Returns the width of the swapchain images.
+     */
+    uint32_t width() const { return m_extent.width; }
+
+    /**
+     * Returns the height of the swapchain images.
+     */
+    uint32_t height() const { return m_extent.height; }
+
+    /**
+     * Returns the extent of the swapchain images.
+     */
+    const VkExtent2D &extent() const { return m_extent; }
+
+    /**
+     * Returns the age of the current image.
+     *
+     * The age is the number of images that have been presented since
+     * the current image was last acquired.
+     *
+     * An age of zero means that the image contents are undefined.
+     */
+    uint32_t bufferAge() const;
+
+    /**
+     * Adds the given damage region to the damage history.
+     */
+    void addToDamageHistory(const QRegion &region);
+
+    /**
+     * Returns the union of the damage regions for the last bufferAge frames.
+     */
+    QRegion accumulatedDamageHistory(int bufferAge) const;
+
+private:
+    VulkanDevice *m_device;
+    VulkanQueue m_queue;
+    VkSurfaceKHR m_surface;
+    VkFormat m_format;
+    VkColorSpaceKHR m_colorSpace;
+    VkPresentModeKHR m_presentMode;
+    uint32_t m_preferredImageCount;
+    VkRenderPass m_renderPass;
+    VkSwapchainKHR m_swapchain;
+    VkExtent2D m_extent;
+    std::vector<Buffer> m_buffers;
+    std::deque<QRegion> m_damageHistory;
+    int32_t m_index = -1;
+    uint64_t m_sequence = 0;
+    bool m_supportsIncrementalPresent = false;
+    bool m_isValid = false;
+};
+
+} // namespace KWin
+
+#endif // SWAPCHAIN_H
diff --git a/plugins/scenes/vulkan/vulkan.json b/plugins/scenes/vulkan/vulkan.json
new file mode 100644
index 000000000..5acd95c6d
--- /dev/null
+++ b/plugins/scenes/vulkan/vulkan.json
@@ -0,0 +1,9 @@
+{
+    "CompositingType": 16,
+    "KPlugin": {
+        "Description": "KWin Vulkan compositor plugin",
+        "Id": "KWinSceneVulkan",
+        "Name": "SceneVulkan",
+        "Name[x-test]": "xxSceneVulkanxx"
+    }
+}
diff --git a/plugins/scenes/vulkan/window.cpp b/plugins/scenes/vulkan/window.cpp
new file mode 100644
index 000000000..16e91407c
--- /dev/null
+++ b/plugins/scenes/vulkan/window.cpp
@@ -0,0 +1,55 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright  © 2017-2018 Fredrik Höglund <fredrik@kde.org>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*********************************************************************/
+
+#include "window.h"
+#include "windowpixmap.h"
+
+
+namespace KWin
+{
+
+
+VulkanWindow::VulkanWindow(Toplevel *toplevel, VulkanScene *scene)
+    : Scene::Window(toplevel),
+      m_scene(scene)
+{
+}
+
+
+VulkanWindow::~VulkanWindow()
+{
+}
+
+
+void VulkanWindow::performPaint(int mask, QRegion clipRegion, WindowPaintData data)
+{
+    Q_UNUSED(mask)
+    Q_UNUSED(clipRegion)
+    Q_UNUSED(data)
+}
+
+
+WindowPixmap *VulkanWindow::createWindowPixmap()
+{
+    return new VulkanWindowPixmap(this, m_scene);
+}
+
+
+} // namespace KWin
diff --git a/plugins/scenes/vulkan/window.h b/plugins/scenes/vulkan/window.h
new file mode 100644
index 000000000..f59f3e99e
--- /dev/null
+++ b/plugins/scenes/vulkan/window.h
@@ -0,0 +1,52 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright  © 2017-2018 Fredrik Höglund <fredrik@kde.org>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*********************************************************************/
+
+#ifndef VULKAN_WINDOW_H
+#define VULKAN_WINDOW_H
+
+#include "scene.h"
+
+namespace KWin
+{
+
+class VulkanWindow : public Scene::Window
+{
+public:
+    explicit VulkanWindow(Toplevel *toplevel, VulkanScene *scene);
+    ~VulkanWindow() override;
+
+    void performPaint(int mask, QRegion region, WindowPaintData data) override;
+
+    VulkanScene *scene() const { return m_scene; }
+
+protected:
+    virtual WindowPixmap *createWindowPixmap();
+
+private:
+    VulkanPipelineManager *pipelineManager() { return scene()->pipelineManager(); }
+    VulkanUploadManager *uploadManager() { return scene()->uploadManager(); }
+
+private:
+    VulkanScene *m_scene;
+};
+
+} // namespace KWin
+
+#endif // VULKAN_WINDOW_H
diff --git a/plugins/scenes/vulkan/windowpixmap.cpp \
b/plugins/scenes/vulkan/windowpixmap.cpp new file mode 100644
index 000000000..5eb7c171b
--- /dev/null
+++ b/plugins/scenes/vulkan/windowpixmap.cpp
@@ -0,0 +1,70 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright  © 2017-2018 Fredrik Höglund <fredrik@kde.org>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*********************************************************************/
+
+#include "windowpixmap.h"
+
+namespace KWin
+{
+
+
+VulkanWindowPixmap::VulkanWindowPixmap(Scene::Window *window, VulkanScene *scene)
+    : WindowPixmap(window),
+      m_scene(scene)
+{
+}
+
+
+VulkanWindowPixmap::VulkanWindowPixmap(const \
QPointer<KWayland::Server::SubSurfaceInterface> &subSurface, +                        \
WindowPixmap *parent, +                                       VulkanScene *scene)
+    : WindowPixmap(subSurface, parent),
+      m_scene(scene)
+{
+}
+
+
+VulkanWindowPixmap::~VulkanWindowPixmap()
+{
+}
+
+
+void VulkanWindowPixmap::create()
+{
+}
+
+
+void VulkanWindowPixmap::aboutToRender()
+{
+}
+
+
+WindowPixmap *VulkanWindowPixmap::createChild(const \
QPointer<KWayland::Server::SubSurfaceInterface> &subSurface) +{
+    return new VulkanWindowPixmap(subSurface, this, m_scene);
+}
+
+
+bool VulkanWindowPixmap::isValid() const
+{
+    return WindowPixmap::isValid();
+}
+
+
+} // namespace KWin
diff --git a/plugins/scenes/vulkan/windowpixmap.h \
b/plugins/scenes/vulkan/windowpixmap.h new file mode 100644
index 000000000..1846b0219
--- /dev/null
+++ b/plugins/scenes/vulkan/windowpixmap.h
@@ -0,0 +1,70 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright  © 2017-2018 Fredrik Höglund <fredrik@kde.org>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*********************************************************************/
+
+#ifndef WINDOWPIXMAP_H
+#define WINDOWPIXMAP_H
+
+#include "../../../scene.h"
+
+#include <QPointer>
+#include <memory>
+
+namespace KWayland {
+    namespace Server {
+        class SubSurfaceInterface;
+    }
+}
+
+namespace KWin
+{
+
+class VulkanScene;
+class VulkanImage;
+class VulkanImageView;
+class VulkanDeviceMemory;
+
+class VulkanWindowPixmap : public WindowPixmap
+{
+public:
+    explicit VulkanWindowPixmap(Scene::Window *window, VulkanScene *scene);
+
+    explicit VulkanWindowPixmap(const \
QPointer<KWayland::Server::SubSurfaceInterface> &subSurface, +                        \
WindowPixmap *parent, +                                VulkanScene *scene);
+
+    ~VulkanWindowPixmap() override final;
+
+    void create() override final;
+
+    VulkanScene *scene() const { return m_scene; }
+
+    WindowPixmap *createChild(const QPointer<KWayland::Server::SubSurfaceInterface> \
&subSurface); +
+    bool isValid() const override final;
+
+    void aboutToRender();
+
+private:
+    VulkanScene *m_scene;
+};
+
+} // namespace KWin
+
+#endif // WINDOWPIXMAP_H
diff --git a/workspace.cpp b/workspace.cpp
index 9b26efbec..d5f1f5df6 100644
--- a/workspace.cpp
+++ b/workspace.cpp
@@ -1576,6 +1576,10 @@ QString Workspace::supportInformation() const
                 support.append(QStringLiteral(" no\n"));
             break;
         }
+        case VulkanCompositing:
+            support.append(QStringLiteral("Compositing Type: Vulkan\n"));
+            // TODO: Add information about the driver and GPU
+            break;
         case XRenderCompositing:
             support.append(QStringLiteral("Compositing Type: XRender\n"));
             break;
@@ -1584,7 +1588,7 @@ QString Workspace::supportInformation() const
             break;
         case NoCompositing:
         default:
-            support.append(QStringLiteral("Something is really broken, neither \
OpenGL nor XRender is used")); +            support.append(QStringLiteral("Something \
is really broken; an unknown compositing type is used"));  }
         support.append(QStringLiteral("\nLoaded Effects:\n"));
         support.append(QStringLiteral(  "---------------\n"));


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic