[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 ¤tFrame() { return m_frameData[m_frameIndex]; }
+ const FrameData ¤tFrame() const { return m_frameData[m_frameIndex]; }
+
+ PaintPassData ¤tPaintPass() { return m_paintPassData[m_paintPassIndex]; }
+ const PaintPassData ¤tPaintPass() 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 = ®ion
+ };
+
+ const VkPresentInfoKHR presentInfo = {
+ .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
+ .pNext = m_supportsIncrementalPresent ? ®ions : 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 ®ion)
+{
+ 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 ®ion);
+
+ /**
+ * 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