From kde-commits Fri Feb 16 17:01:48 2018 From: =?utf-8?q?Fredrik_H=C3=B6glund?= Date: Fri, 16 Feb 2018 17:01:48 +0000 To: kde-commits Subject: [kwin/fredrik/vulkan] /: Add the beginnings of a Vulkan compositing plugin Message-Id: X-MARC-Message: https://marc.info/?l=kde-commits&m=151880053508145 Git commit 2cae8ad3f571fd78c1a38af753b0521e4a9dbab3 by Fredrik H=C3=B6glund. 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 (v= 2)] 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 [Li= cense: GPL (v2)] A +62 -0 plugins/platforms/wayland/wayland_vulkan_backend.h [Lice= nse: GPL (v2)] M +10 -0 plugins/platforms/x11/standalone/CMakeLists.txt A +130 -0 plugins/platforms/x11/standalone/vulkanbackend.cpp [Lice= nse: GPL (v2)] A +63 -0 plugins/platforms/x11/standalone/vulkanbackend.h [Licens= e: 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 [L= icense: GPL (v2)] A +60 -0 plugins/platforms/x11/windowed/x11_vulkan_backend.h [Lic= ense: 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: G= PL (v2)] A +58 -0 plugins/scenes/vulkan/decorationrenderer.h [License: GPL= (v2)] A +305 -0 plugins/scenes/vulkan/descriptorset.cpp [License: GPL (v= 2)] 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 =3D 1, XRenderCompositing =3D 1<<1, QPainterCompositing =3D 1<< 2, - OpenGL2Compositing =3D 1<<3 | OpenGLCompositing + OpenGL2Compositing =3D 1<<3 | OpenGLCompositing, + VulkanCompositing =3D 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 =3D XRenderCompositing; else if (compositingBackend =3D=3D "QPainter") compositingMode =3D QPainterCompositing; + else if (compositingBackend =3D=3D "Vulkan") + compositingMode =3D VulkanCompositing; else compositingMode =3D OpenGLCompositing; = @@ -921,6 +923,11 @@ bool Options::loadCompositingConfig (bool force) compositingMode =3D QPainterCompositing; useCompositing =3D true; break; + case 'V': + qCDebug(KWIN_CORE) << "Compositing forced to Vulkan mode by en= vironment variable"; + compositingMode =3D VulkanCompositing; + useCompositing =3D 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 =3D 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/sc= enes/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=C3=B6glund + +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 . +*********************************************************************/ + +#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/scen= es/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=C3=B6glund + +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 . +*********************************************************************/ + +#ifndef BACKEND_H +#define BACKEND_H + +#include + +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 other= wise. + */ + virtual bool usesOverlayWindow() const =3D 0; + + /** + * Shows the overlay window. + */ + virtual void showOverlay(); + + /** + * Returns true if each screen has a separate surface, and false other= wise. + */ + 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 surface= s. + */ + void setInstance(VulkanInstance *instance) { m_instance =3D 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 =3D 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 queueFamily= Index) const =3D 0; + + /** + * Creates the Vulkan surface(s). + * + * Returns true if successful, and false otherwise. + */ + virtual bool createSurfaces() =3D 0; + + /** + * Returns a handle to the Vulkan surface. + */ + virtual std::shared_ptr surface() const =3D 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 =3D nullptr; +}; + +} // namespace KWin + +#endif // BACKEND_H diff --git a/plugins/platforms/wayland/CMakeLists.txt b/plugins/platforms/w= ayland/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 Sc= eneQPainterBackend) = @@ -16,6 +21,10 @@ if(HAVE_WAYLAND_EGL) target_link_libraries(KWinWaylandWaylandBackend SceneOpenGLBackend Way= land::Egl) endif() = +if(Vulkan_FOUND) + target_link_libraries(KWinWaylandWaylandBackend SceneVulkanBackend kwi= nvulkanutils) +endif() + install( TARGETS KWinWaylandWaylandBackend diff --git a/plugins/platforms/wayland/wayland_backend.cpp b/plugins/platfo= rms/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 . #if HAVE_WAYLAND_EGL #include "egl_wayland_backend.h" #endif +#if HAVE_VULKAN +#include "wayland_vulkan_backend.h" +#endif #include #include #include @@ -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 WaylandBackend::supportedCompositors() const { + QVector types; #if HAVE_WAYLAND_EGL - return QVector{OpenGLCompositing, QPainterCompositing= }; -#else - return QVector{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/platform= s/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 =3D 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=C3=B6glund + +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 . +*********************************************************************/ + +#include "wayland_vulkan_backend.h" +#include "wayland_backend.h" + +#include + +#include "kwinvulkanutils.h" + +namespace KWin +{ + + +WaylandVulkanBackend::WaylandVulkanBackend(Wayland::WaylandBackend *platfo= rm) + : 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 =3D std::make_shared(m_instance, m_platform->= display(), *m_platform->surface()); + return m_surface->isValid(); +} + + +std::shared_ptr WaylandVulkanBackend::surface() const +{ + return m_surface; +} + + +bool WaylandVulkanBackend::getPhysicalDevicePresentationSupport(VulkanPhys= icalDevice device, uint32_t queueFamilyIndex) const +{ + return device.getWaylandPresentationSupportKHR(queueFamilyIndex, m_pla= tform->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/p= latforms/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=C3=B6glund + +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 . +*********************************************************************/ + +#ifndef VULKAN_WAYLAND_BACKEND_H +#define VULKAN_WAYLAND_BACKEND_H + +#include "platformsupport/scenes/vulkan/backend.h" + +#include + +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) c= onst override; + bool createSurfaces() override; + std::shared_ptr surface() const override; + bool isValid() const override; + +private: + Wayland::WaylandBackend *m_platform; + std::shared_ptr m_surface; +}; + +} // namespace KWin + +#endif // X11VULKANBACKEND_H diff --git a/plugins/platforms/x11/standalone/CMakeLists.txt b/plugins/plat= forms/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_cont= ext_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 S= ceneOpenGLBackend 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 kwinvulkanuti= ls) +endif() + if(HAVE_DL_LIBRARY) target_link_libraries(KWinX11Platform ${DL_LIBRARY}) endif() diff --git a/plugins/platforms/x11/standalone/vulkanbackend.cpp b/plugins/p= latforms/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=C3=B6glund + +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 . +*********************************************************************/ + +#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 =3D connection(); + const QSize size =3D screens()->size(); + + m_window =3D 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_CLA= SS_INPUT_OUTPUT, + XCB_COPY_FROM_PARENT, 0, nullptr); + + m_surface =3D std::make_shared(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 X11VulkanBackend::surface() const +{ + return m_surface; +} + + +bool X11VulkanBackend::getPhysicalDevicePresentationSupport(VulkanPhysical= Device device, uint32_t queueFamilyIndex) const +{ + return device.getXcbPresentationSupportKHR(queueFamilyIndex, connectio= n(), defaultScreen()->root_visual); +} + + +void X11VulkanBackend::screenGeometryChanged(const QSize &size) +{ + // Resize the X window + const uint16_t mask =3D XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XC= B_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT; + const uint32_t values[] =3D { 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/pla= tforms/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=C3=B6glund + +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 . +*********************************************************************/ + +#ifndef X11_VULKAN_BACKEND_H +#define X11_VULKAN_BACKEND_H + +#include +#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) c= onst override; + bool createSurfaces() override; + std::shared_ptr surface() const override; + bool isValid() const override; + +private: + X11StandalonePlatform *m_platform; + OverlayWindow *m_overlayWindow; + std::shared_ptr 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/pl= atforms/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 . #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::createOpenGLBack= end() } } = +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 X11StandalonePlatform::support= edCompositors() 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/plat= forms/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 =3D nullptr) override; OpenGLBackend *createOpenGLBackend() override; + VulkanBackend *createVulkanBackend() override; Edge *createScreenEdge(ScreenEdges *parent) override; void createPlatformCursor(QObject *parent =3D nullptr) override; bool requiresCompositing() const override; diff --git a/plugins/platforms/x11/windowed/CMakeLists.txt b/plugins/platfo= rms/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 kwinxrenderu= tils X11::XCB SceneQPainterBackend SceneOpenGLBackend) = +if(Vulkan_FOUND) +target_link_libraries(KWinWaylandX11Backend kwin X11::XCB SceneVulkanBacke= nd kwinvulkanutils) +endif() + install( TARGETS KWinWaylandX11Backend diff --git a/plugins/platforms/x11/windowed/x11_vulkan_backend.cpp b/plugin= s/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=C3=B6glund + +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 . +*********************************************************************/ + +#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 =3D 0; i < screens()->count(); ++i) { + auto surface =3D std::make_shared(m_instance, m_pla= tform->connection(), m_platform->windowForScreen(i)); + + if (!surface->isValid()) + surface =3D nullptr; + + m_surfaces.emplace_back(surface); + } + + if (m_surfaces.empty()) + return false; + + return true; +} + + +std::shared_ptr X11VulkanBackend::surface() const +{ + if (m_surfaces.empty()) + return nullptr; + + return m_surfaces.front(); +} + + +bool X11VulkanBackend::getPhysicalDevicePresentationSupport(VulkanPhysical= Device device, uint32_t queueFamilyIndex) const +{ + return device.getXcbPresentationSupportKHR(queueFamilyIndex, m_platfor= m->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=C3=B6glund + +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 . +*********************************************************************/ + +#ifndef X11_VULKAN_BACKEND_H +#define X11_VULKAN_BACKEND_H + +#include +#include + +#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) c= onst override; + bool createSurfaces() override; + std::shared_ptr surface() const override; + bool isValid() const override; + +private: + X11WindowedBackend *m_platform; + std::vector> m_surfaces; +}; + +} // namespace KWin + +#endif // X11_VULKAN_BACKEND_H diff --git a/plugins/platforms/x11/windowed/x11windowed_backend.cpp b/plugi= ns/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 . *********************************************************************/ +#include #include "x11windowed_backend.h" #include "scene_qpainter_x11_backend.h" #include "logging.h" @@ -25,6 +26,9 @@ along with this program. If not, see . #include "egl_x11_backend.h" #include "screens.h" #include +#if HAVE_VULKAN +#include "x11_vulkan_backend.h" +#endif // KDE #include #include @@ -454,6 +458,15 @@ QPainterBackend *X11WindowedBackend::createQPainterBac= kend() 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 =3D m_windows.at(0).window; @@ -461,6 +474,19 @@ void X11WindowedBackend::warpPointer(const QPointF &gl= obalPos) xcb_flush(m_connection); } = +QVector X11WindowedBackend::supportedCompositors() const +{ + QVector 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 =3D nullptr) override; OpenGLBackend *createOpenGLBackend() override; QPainterBackend* createQPainterBackend() override; + VulkanBackend* createVulkanBackend() override; void warpPointer(const QPointF &globalPos) override; = - QVector supportedCompositors() const override { - return QVector{OpenGLCompositing, QPainterComposi= ting}; - } + QVector 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/C= MakeLists.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 =C2=A9 2017-2018 Fredrik H=C3=B6glund + +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 . +*********************************************************************/ + +#include "decorationrenderer.h" + +namespace KWin +{ + + +VulkanDecorationRenderer::VulkanDecorationRenderer(Decoration::DecoratedCl= ientImpl *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/vu= lkan/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 =C2=A9 2017-2018 Fredrik H=C3=B6glund + +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 . +*********************************************************************/ + +#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 =3D 0, + Top, + Right, + Bottom, + Count + }; + + explicit VulkanDecorationRenderer(Decoration::DecoratedClientImpl *cli= ent, 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/vulka= n/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 =C2=A9 2017-2018 Fredrik H=C3=B6glund + +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 . +*********************************************************************/ + +#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 =3D m_unusedSets.front(); + m_unusedSets.pop(); + return set; + } + + // Create a new descriptor pool + if (m_available =3D=3D 0) { + // Note that we don't set VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTO= R_SET_BIT + const VkDescriptorPoolCreateInfo createInfo =3D { + .sType =3D VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .pNext =3D nullptr, + .flags =3D 0, + .maxSets =3D m_maxSets, + .poolSizeCount =3D (uint32_t) m_poolSizes.size(), + .pPoolSizes =3D m_poolSizes.data() + }; + + VkDescriptorPool pool; + m_device->createDescriptorPool(&createInfo, nullptr, &pool); + + m_pools.push_back(pool); + m_available =3D m_maxSets; + } + + // Allocate a new descriptor set from the pool + const VkDescriptorSetAllocateInfo allocInfo =3D { + .sType =3D VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + .pNext =3D nullptr, + .descriptorPool =3D m_pools.back(), + .descriptorSetCount =3D 1, + .pSetLayouts =3D &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 =3D m_pool->allocateDescriptorSet(); +} + + +TextureDescriptorSet::~TextureDescriptorSet() +{ + if (m_pool && m_set) + m_pool->freeDescriptorSet(m_set); +} + + +void TextureDescriptorSet::update(VkSampler sampler, + const std::shared_ptr &imageView, + VkImageLayout imageLayout, + const std::shared_ptr = &uniformBuffer) +{ + const VkDescriptorBufferInfo uniformBufferInfo =3D { + .buffer =3D uniformBuffer->handle(), + .offset =3D 0, + .range =3D sizeof(VulkanPipelineManager::TextureUniformData) + }; + + const VkDescriptorImageInfo imageInfo =3D { + .sampler =3D sampler, + .imageView =3D imageView->handle(), + .imageLayout =3D imageLayout + }; + + const VkWriteDescriptorSet descriptorWrites[] =3D { + { + .sType =3D VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .pNext =3D nullptr, + .dstSet =3D m_set, + .dstBinding =3D 0, + .dstArrayElement =3D 0, + .descriptorCount =3D 1, + .descriptorType =3D VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .pImageInfo =3D &imageInfo, + .pBufferInfo =3D nullptr, + .pTexelBufferView =3D nullptr + }, + { + .sType =3D VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .pNext =3D nullptr, + .dstSet =3D m_set, + .dstBinding =3D 1, + .dstArrayElement =3D 0, + .descriptorCount =3D 1, + .descriptorType =3D VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, + .pImageInfo =3D nullptr, + .pBufferInfo =3D &uniformBufferInfo, + .pTexelBufferView =3D nullptr + }, + }; + + m_pool->device()->updateDescriptorSets(ARRAY_SIZE(descriptorWrites), d= escriptorWrites, 0, nullptr); + + m_sampler =3D sampler; + m_imageView =3D imageView; + m_imageLayout =3D imageLayout; + m_uniformBuffer =3D uniformBuffer; +} + + + +// ------------------------------------------------------------------ + + + +CrossFadeDescriptorSet::CrossFadeDescriptorSet(SceneDescriptorPool *pool) + : m_pool(pool) +{ + m_set =3D m_pool->allocateDescriptorSet(); +} + + +CrossFadeDescriptorSet::~CrossFadeDescriptorSet() +{ + if (m_pool && m_set) + m_pool->freeDescriptorSet(m_set); +} + + +void CrossFadeDescriptorSet::update(VkSampler sampler, + const std::shared_ptr &imageView1, + const std::shared_ptr &imageView2, + VkImageLayout imageLayout, + const std::shared_ptr = &uniformBuffer) +{ + const VkDescriptorBufferInfo uniformBufferInfo =3D { + .buffer =3D uniformBuffer->handle(), + .offset =3D 0, + .range =3D sizeof(VulkanPipelineManager::TextureUniformData) + }; + + const VkDescriptorImageInfo imageInfo[] =3D { + { + .sampler =3D sampler, + .imageView =3D imageView1->handle(), + .imageLayout =3D imageLayout + }, + { + .sampler =3D sampler, + .imageView =3D imageView2->handle(), + .imageLayout =3D imageLayout + } + }; + + const VkWriteDescriptorSet descriptorWrites[] =3D { + { + .sType =3D VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .pNext =3D nullptr, + .dstSet =3D m_set, + .dstBinding =3D 0, + .dstArrayElement =3D 0, + .descriptorCount =3D 2, + .descriptorType =3D VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + .pImageInfo =3D imageInfo, + .pBufferInfo =3D nullptr, + .pTexelBufferView =3D nullptr + }, + { + .sType =3D VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .pNext =3D nullptr, + .dstSet =3D m_set, + .dstBinding =3D 1, + .dstArrayElement =3D 0, + .descriptorCount =3D 1, + .descriptorType =3D VK_DESCRIPTOR_TYPE_SAMPLER, + .pImageInfo =3D imageInfo, + .pBufferInfo =3D nullptr, + .pTexelBufferView =3D nullptr + }, + { + .sType =3D VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .pNext =3D nullptr, + .dstSet =3D m_set, + .dstBinding =3D 2, + .dstArrayElement =3D 0, + .descriptorCount =3D 1, + .descriptorType =3D VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, + .pImageInfo =3D nullptr, + .pBufferInfo =3D &uniformBufferInfo, + .pTexelBufferView =3D nullptr + }, + }; + + m_pool->device()->updateDescriptorSets(ARRAY_SIZE(descriptorWrites), d= escriptorWrites, 0, nullptr); + + m_sampler =3D sampler; + m_imageView1 =3D imageView1; + m_imageView2 =3D imageView2; + m_imageLayout =3D imageLayout; + m_uniformBuffer =3D uniformBuffer; +} + + + +// ------------------------------------------------------------------ + + + + +ColorDescriptorSet::ColorDescriptorSet(SceneDescriptorPool *pool) + : m_pool(pool) +{ + m_set =3D m_pool->allocateDescriptorSet(); +} + + +ColorDescriptorSet::~ColorDescriptorSet() +{ + if (m_pool && m_set) + m_pool->freeDescriptorSet(m_set); +} + + +void ColorDescriptorSet::update(const std::shared_ptr &unifo= rmBuffer) +{ + const VkDescriptorBufferInfo uniformBufferInfo =3D { + .buffer =3D uniformBuffer->handle(), + .offset =3D 0, + .range =3D sizeof(VulkanPipelineManager::ColorUniformData) + }; + + const VkWriteDescriptorSet descriptorWrite =3D { + .sType =3D VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .pNext =3D nullptr, + .dstSet =3D m_set, + .dstBinding =3D 0, + .dstArrayElement =3D 0, + .descriptorCount =3D 1, + .descriptorType =3D VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, + .pImageInfo =3D nullptr, + .pBufferInfo =3D &uniformBufferInfo, + .pTexelBufferView =3D nullptr + }; + + m_pool->device()->updateDescriptorSets(1, &descriptorWrite, 0, nullptr= ); + + m_uniformBuffer =3D 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 =C2=A9 2017-2018 Fredrik H=C3=B6glund + +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 . +*********************************************************************/ + +#ifndef DESCRIPTOR_SET_H +#define DESCRIPTOR_SET_H + +#include "kwinvulkanutils.h" + +#include +#include + + +namespace KWin { + + +class SceneDescriptorPool; + + +/** + * TextureDescriptorSet encapsulates a descriptor set with a combined imag= e-sampler + * and a dynamic uniform buffer binding. + */ + class TextureDescriptorSet : public VulkanObject +{ +public: + TextureDescriptorSet(SceneDescriptorPool *pool); + + TextureDescriptorSet(const TextureDescriptorSet &other) =3D 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 =3D nullptr; + other.m_set =3D VK_NULL_HANDLE; + other.m_sampler =3D VK_NULL_HANDLE; + other.m_imageView =3D nullptr; + other.m_imageLayout =3D VK_IMAGE_LAYOUT_UNDEFINED; + other.m_uniformBuffer =3D nullptr; + } + + ~TextureDescriptorSet() override; + + void update(VkSampler sampler, + const std::shared_ptr &imageView, + VkImageLayout imageLayout, + const std::shared_ptr &uniformBuffer); + + VkSampler sampler() const { return m_sampler; } + std::shared_ptr imageView() const { return m_imageVie= w; } + VkImageLayout imageLayout() const { return m_imageLayout; } + std::shared_ptr uniformBuffer() const { return m_uniform= Buffer; } + + VkDescriptorSet handle() const { return m_set; } + operator VkDescriptorSet () const { return m_set; } + + TextureDescriptorSet &operator =3D (const TextureDescriptorSet &other)= =3D delete; + + TextureDescriptorSet &operator =3D (TextureDescriptorSet &&other) { + m_pool =3D other.m_pool; + m_set =3D other.m_set; + m_sampler =3D other.m_sampler; + m_imageView =3D other.m_imageView; + m_imageLayout =3D other.m_imageLayout; + m_uniformBuffer =3D std::move(other.m_uniformBuffer); + + other.m_pool =3D nullptr; + other.m_set =3D VK_NULL_HANDLE; + other.m_sampler =3D VK_NULL_HANDLE; + other.m_imageView =3D nullptr; + other.m_imageLayout =3D VK_IMAGE_LAYOUT_UNDEFINED; + other.m_uniformBuffer =3D nullptr; + return *this; + } + +private: + SceneDescriptorPool *m_pool; + VkDescriptorSet m_set; + VkSampler m_sampler; + std::shared_ptr m_imageView; + VkImageLayout m_imageLayout; + std::shared_ptr m_uniformBuffer; +}; + + + +// ------------------------------------------------------------------ + + + +/** + * CrossFadeDescriptorSet encapsulates a descriptor set with an array of t= wo sampled-images, + * and a dynamic uniform buffer binding. + */ +class CrossFadeDescriptorSet : public VulkanObject +{ +public: + CrossFadeDescriptorSet(SceneDescriptorPool *pool); + + CrossFadeDescriptorSet(const CrossFadeDescriptorSet &other) =3D 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 =3D nullptr; + other.m_set =3D VK_NULL_HANDLE; + other.m_sampler =3D VK_NULL_HANDLE; + other.m_imageView1 =3D nullptr; + other.m_imageView2 =3D nullptr; + other.m_imageLayout =3D VK_IMAGE_LAYOUT_UNDEFINED; + other.m_uniformBuffer =3D nullptr; + } + + ~CrossFadeDescriptorSet() override; + + void update(VkSampler sampler, + const std::shared_ptr &imageView1, + const std::shared_ptr &imageView2, + VkImageLayout imageLayout, + const std::shared_ptr &uniformBuffer); + + VkSampler sampler() const { return m_sampler; } + std::shared_ptr imageView1() const { return m_imageVi= ew1; } + std::shared_ptr imageView2() const { return m_imageVi= ew2; } + VkImageLayout imageLayout() const { return m_imageLayout; } + std::shared_ptr uniformBuffer() const { return m_uniform= Buffer; } + + VkDescriptorSet handle() const { return m_set; } + operator VkDescriptorSet () const { return m_set; } + + CrossFadeDescriptorSet &operator =3D (const CrossFadeDescriptorSet &ot= her) =3D delete; + + CrossFadeDescriptorSet &operator =3D (CrossFadeDescriptorSet &&other) { + m_pool =3D other.m_pool; + m_set =3D other.m_set; + m_sampler =3D other.m_sampler; + m_imageView1 =3D other.m_imageView1; + m_imageView1 =3D other.m_imageView2; + m_imageLayout =3D other.m_imageLayout; + m_uniformBuffer =3D std::move(other.m_uniformBuffer); + + other.m_pool =3D nullptr; + other.m_set =3D VK_NULL_HANDLE; + other.m_sampler =3D VK_NULL_HANDLE; + other.m_imageView1 =3D nullptr; + other.m_imageView2 =3D nullptr; + other.m_imageLayout =3D VK_IMAGE_LAYOUT_UNDEFINED; + other.m_uniformBuffer =3D nullptr; + return *this; + } + +private: + SceneDescriptorPool *m_pool; + VkDescriptorSet m_set; + VkSampler m_sampler; + std::shared_ptr m_imageView1; + std::shared_ptr m_imageView2; + VkImageLayout m_imageLayout; + std::shared_ptr 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) =3D delete; + + ColorDescriptorSet(ColorDescriptorSet &&other) + : m_pool(other.m_pool), + m_set(other.m_set), + m_uniformBuffer(std::move(other.m_uniformBuffer)) + { + other.m_pool =3D nullptr; + other.m_set =3D VK_NULL_HANDLE; + other.m_uniformBuffer =3D nullptr; + } + + ~ColorDescriptorSet() override; + + void update(const std::shared_ptr &uniformBuffer); + + std::shared_ptr uniformBuffer() const { return m_uniform= Buffer; } + + VkDescriptorSet handle() const { return m_set; } + operator VkDescriptorSet () const { return m_set; } + + ColorDescriptorSet &operator =3D (const ColorDescriptorSet &other) =3D= delete; + + ColorDescriptorSet &operator =3D (ColorDescriptorSet &&other) { + m_pool =3D other.m_pool; + m_set =3D other.m_set; + m_uniformBuffer =3D std::move(other.m_uniformBuffer); + + other.m_pool =3D nullptr; + other.m_set =3D VK_NULL_HANDLE; + other.m_uniformBuffer =3D nullptr; + return *this; + } + +private: + SceneDescriptorPool *m_pool; + VkDescriptorSet m_set; + std::shared_ptr m_uniformBuffer; +}; + + + +// ------------------------------------------------------------------ + + + +/** + * SceneDescriptorPool allocates descriptors for descriptor sets. + */ +class SceneDescriptorPool +{ +public: + struct CreateInfo { + VulkanDevice *device; + VkDescriptorSetLayout descriptorSetLayout; + uint32_t maxSets; + std::initializer_list 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 m_pools; + std::queue m_unusedSets; + std::vector m_poolSizes; + VkDescriptorSetLayout m_layout =3D VK_NULL_HANDLE; + uint32_t m_maxSets =3D 0; + uint32_t m_available =3D 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 =C2=A9 2017-2018 Fredrik H=C3=B6glund + +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 . +*********************************************************************/ + +#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 fram= eOpacity) +{ + 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/ef= fectframe.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 =C2=A9 2017-2018 Fredrik H=C3=B6glund + +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 . +*********************************************************************/ + +#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) overr= ide 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 =C2=A9 2017-2018 Fredrik H=C3=B6glund + +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 . +*********************************************************************/ + +#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 +#include +#include + + +using namespace std::placeholders; + + +namespace KWin { + + +VulkanFactory::VulkanFactory(QObject *parent) + : SceneFactory(parent) +{ +} + + +VulkanFactory::~VulkanFactory() =3D default; + + +Scene *VulkanFactory::create(QObject *parent) const +{ + auto backend =3D std::unique_ptr(kwinApp()->platform()-= >createVulkanBackend()); + + if (!backend || !backend->isValid()) + return nullptr; + + VulkanScene *scene =3D new VulkanScene(std::move(backend), parent); + + if (scene && scene->initFailed()) { + delete scene; + scene =3D nullptr; + } + + return scene; +} + + + +// ------------------------------------------------------------------ + + + +VulkanScene::VulkanScene(std::unique_ptr &&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 enabledLayers; + + if (qgetenv("KWIN_ENABLE_VULKAN_VALIDATION") =3D=3D "1") { + // Note that VK_LAYER_LUNARG_standard_validation is a meta layer t= hat + // 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 =3D VulkanInstance::suppor= tedLayers(); + enabledLayers.reserve(std::extent()); + + for (const char *layer : validationLayers) { + if (supportedLayers.contains(layer)) { + enabledLayers.push_back(layer); + } + } + } + + // Instance extensions + // ------------------- + const VulkanExtensionVector supportedInstanceExtensions =3D VulkanInst= ance::supportedExtensions(); + + const char *requiredInstanceExtensions[] =3D { + VK_KHR_SURFACE_EXTENSION_NAME, + m_backend->platformSurfaceExtensionName() + }; + + std::vector enabledInstanceExtensions; + enabledInstanceExtensions.reserve(std::extent()); + + for (auto extension : requiredInstanceExtensions) { + if (!supportedInstanceExtensions.contains(extension)) { + qCCritical(KWIN_VULKAN) << "Required Vulkan extension" << exte= nsion << "is not supported."; + return false; + } + + enabledInstanceExtensions.push_back(extension); + } + + bool haveGetPhysicalDeviceProperties2 =3D supportedInstanceExtensions.= contains(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); + if (haveGetPhysicalDeviceProperties2) + enabledInstanceExtensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PRO= PERTIES_2_EXTENSION_NAME); + + bool haveExtDebugReport =3D supportedInstanceExtensions.contains(VK_EX= T_DEBUG_REPORT_EXTENSION_NAME); + if (haveExtDebugReport) + enabledInstanceExtensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_= NAME); + + + // Create the vulkan instance + // -------------------------- + const VkApplicationInfo applicationInfo { + .sType =3D VK_STRUCTURE_TYPE_APPLICATION_INFO, + .pNext =3D nullptr, + .pApplicationName =3D KWIN_NAME, + .applicationVersion =3D VK_MAKE_VERSION(KWIN_VERSION_MAJOR, KWIN_V= ERSION_MINOR, KWIN_VERSION_PATCH), + .pEngineName =3D nullptr, + .engineVersion =3D VK_MAKE_VERSION(0, 0, 0), + .apiVersion =3D VK_API_VERSION_1_0 + }; + + m_instance =3D VulkanInstance(applicationInfo, enabledLayers, enabledI= nstanceExtensions); + 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 =3D [](VkFlags msgFlags, VkDebugReportObjectTypeEXT= objType, + uint64_t srcObject, size_t location, int32_t m= sgCode, + const char *pLayerPrefix, const char *pMsg, vo= id *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", pLayerP= refix, msgCode, pMsg); + } else if (msgFlags & VK_DEBUG_REPORT_WARNING_BIT_EXT) { + qCWarning(KWIN_VULKAN, "WARNING: [%s] Code %d : %s", pLaye= rPrefix, msgCode, pMsg); + } + + return false; + }; + + m_debugReportCallback =3D 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 s= urface(s)"; + return false; + } + + std::shared_ptr surface =3D m_backend->surface(); + + + // Enumerate the available GPU's + // ----------------------------- + const char *requiredDeviceExtensions[] =3D { + VK_KHR_SWAPCHAIN_EXTENSION_NAME, + }; + + const char *optionalDeviceExtensions[] =3D { + 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 =3D 0; + uint32_t presentQueueFamilyIndex =3D 0; + uint32_t bestScore =3D 0; + + for (VulkanPhysicalDevice &device : m_instance.enumeratePhysicalDevice= s()) { + // Enumerate the device extensions + supportedDeviceExtensions =3D device.enumerateDeviceExtensionPrope= rties(); + + // The device must support all required extensions + if (!supportedDeviceExtensions.containsAll(requiredDeviceExtension= s)) + continue; + + // The device must support sampling linear VK_FORMAT_B8G8R8A8_UNOR= M images. + // This is used to upload decoration textures. + // AMD, Intel, and NVIDIA all support this. + VkFormatProperties formatProperties; + device.getFormatProperties(VK_FORMAT_B8G8R8A8_UNORM, &formatProper= ties); + + if (!(formatProperties.linearTilingFeatures & VK_FORMAT_FEATURE_SA= MPLED_IMAGE_BIT)) + continue; + + // Swapchain images must be renderable + VkSurfaceCapabilitiesKHR capabilities; + device.getSurfaceCapabilitiesKHR(surface->handle(), &capabilities); + + if (!(capabilities.supportedUsageFlags & VK_IMAGE_USAGE_COLOR_ATTA= CHMENT_BIT)) + continue; + + // Enumerate the available queue families + const std::vector queueFamilies =3D devic= e.getQueueFamilyProperties(); + + // Find queue families that support graphics and presentation + uint32_t graphicsIndex =3D UINT32_MAX; + uint32_t presentIndex =3D UINT32_MAX; + + for (unsigned i =3D 0; i < queueFamilies.size(); i++) { + const VkQueueFamilyProperties &family =3D 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 =3D=3D UINT32_MAX && (family.queueFlags & VK= _QUEUE_GRAPHICS_BIT)) + graphicsIndex =3D i; + + // Check if the queue family can present images to the surface + if (presentIndex =3D=3D UINT32_MAX) { + VkBool32 supported =3D VK_FALSE; + if (device.getSurfaceSupportKHR(i, surface->handle(), &sup= ported) =3D=3D VK_SUCCESS) { + // Check for platform specific present support + if (supported && m_backend->getPhysicalDevicePresentat= ionSupport(device, i)) + presentIndex =3D i; + } + } + } + + // The device must support graphics and presentation + if (graphicsIndex =3D=3D UINT32_MAX || presentIndex =3D=3D 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[] =3D { + { 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 =3D 100; + + const auto it =3D std::find_if(std::cbegin(scores), std::cend(scor= es), [&](const auto &entry) { return entry.type =3D=3D properties.deviceTyp= e; }); + if (it !=3D std::cend(scores)) + score =3D it->score; + + if (score > bestScore) { + bestScore =3D score; + physicalDevice =3D device; + graphicsQueueFamilyIndex =3D graphicsIndex; + presentQueueFamilyIndex =3D presentIndex; + } + } + + if (!physicalDevice.isValid()) { + qCCritical(KWIN_VULKAN) << "Failed to find a usable GPU"; + return false; + } + + + // Get the physical device properties + // ---------------------------------- + if (haveGetPhysicalDeviceProperties2) { + VkPhysicalDevicePushDescriptorPropertiesKHR pushDescriptorProperti= es =3D { + .sType =3D VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_P= ROPERTIES_KHR, + .pNext =3D nullptr, + .maxPushDescriptors =3D 0 + }; + + VkPhysicalDeviceDiscardRectanglePropertiesEXT discardRectangleProp= erties =3D { + .sType =3D VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DISCARD_RECTANGLE= _PROPERTIES_EXT, + .pNext =3D &pushDescriptorProperties, + .maxDiscardRectangles =3D 0 + }; + + VkPhysicalDeviceProperties2KHR physicalDeviceProperties2 =3D { + .sType =3D VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR, + .pNext =3D &discardRectangleProperties, + .properties =3D {} + }; + + physicalDevice.getProperties2KHR(&physicalDeviceProperties2); + + m_physicalDeviceProperties =3D physicalDeviceProperties2.propertie= s; + m_maxDiscardRects =3D discardRectangleProperties.maxDiscardRectang= les; + m_maxPushDescriptors =3D pushDescriptorProperties.maxPushDescripto= rs; + } else { + physicalDevice.getProperties(&m_physicalDeviceProperties); + + m_maxDiscardRects =3D 0; + m_maxPushDescriptors =3D 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_phys= icalDeviceProperties.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_physic= alDeviceProperties.deviceName; + qCInfo(KWIN_VULKAN).nospace().noquote() << "Device type: " << enumToSt= ring(m_physicalDeviceProperties.deviceType); + + + // Get the supported surface formats + // --------------------------------- + const std::vector supportedFormats =3D physicalDev= ice.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 =3D VK_FORMAT_UNDEFINED; + m_surfaceColorSpace =3D VK_COLORSPACE_SRGB_NONLINEAR_KHR; + + if (supportedFormats.size() =3D=3D 1 && supportedFormats[0].format =3D= =3D VK_FORMAT_UNDEFINED) { + // The surface supports any format - pick the first in our list of= preferred formats + m_surfaceFormat =3D preferredFormats[0]; + + qCDebug(KWIN_VULKAN).noquote() << "Using" << enumToString(m_surfac= eFormat) << "for swapchain images"; + } else if (supportedFormats.size() > 0) { + // Default to the first supported format in case none of our prefe= rred formats are supported + m_surfaceFormat =3D supportedFormats[0].format; + m_surfaceColorSpace =3D supportedFormats[0].colorSpace; + + // Find the first supported format in preferredFormats + for (VkFormat preferred : preferredFormats) { + auto it =3D std::find_if(supportedFormats.begin(), supportedFo= rmats.end(), + [=3D](const VkSurfaceFormatKHR &support= ed) { return supported.format =3D=3D preferred; }); + if (it !=3D supportedFormats.end()) { + m_surfaceFormat =3D it->format; + m_surfaceColorSpace =3D it->colorSpace; + break; + } + } + + qCInfo(KWIN_VULKAN) << "Supported surface formats:"; + for (const VkSurfaceFormatKHR &supported : supportedFormats) { + if (supported.format =3D=3D m_surfaceFormat) + qCInfo(KWIN_VULKAN).nospace().noquote() << " > " << enumT= oString(supported.format); + else + qCInfo(KWIN_VULKAN).nospace().noquote() << " " << enumT= oString(supported.format); + } + } else { + qCCritical(KWIN_VULKAN) << "The surface does not support any image= formats"; + return false; + } + + + // Get the supported present modes + // ------------------------------- + const std::vector supportedPresentModes =3D physical= Device.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 =3D std::find_first_of(std::begin(preferredPresentMode= s), std::end(preferredPresentModes), + std::begin(supportedPresentModes)= , std::end(supportedPresentModes)); + + if (result !=3D std::end(preferredPresentModes)) + m_presentMode =3D *result; + else + m_presentMode =3D 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 =3D 2; + break; + + case VK_PRESENT_MODE_MAILBOX_KHR: + swapchainImageCount =3D 3; + break; + } + + qCInfo(KWIN_VULKAN) << "Supported present modes:"; + for (const VkPresentModeKHR mode : supportedPresentModes) { + if (mode =3D=3D m_presentMode) + qCInfo(KWIN_VULKAN).nospace().noquote() << " > " << enumT= oString(mode); + else + qCInfo(KWIN_VULKAN).nospace().noquote() << " " << enumT= oString(mode); + } + } else { + qCCritical(KWIN_VULKAN) << "The physical device does not support a= ny presentation modes."; + return false; + } + + + // Create the logical device + // ------------------------- + const float queuePriorities[] { 1.0f }; + std::vector 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 =3D graphicsQueueFamilyIndex !=3D presentQueueF= amilyIndex; + + const VkDeviceQueueCreateInfo queueCreateInfo[] =3D { + { + .sType =3D VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, + .pNext =3D nullptr, + .flags =3D 0, + .queueFamilyIndex =3D graphicsQueueFamilyIndex, + .queueCount =3D 1, + .pQueuePriorities =3D queuePriorities + }, + { + .sType =3D VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, + .pNext =3D nullptr, + .flags =3D 0, + .queueFamilyIndex =3D presentQueueFamilyIndex, + .queueCount =3D 1, + .pQueuePriorities =3D queuePriorities + } + }; + + const VkPhysicalDeviceFeatures enabledFeatures =3D {}; + + const VkDeviceCreateInfo deviceCreateInfo =3D { + .sType =3D VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, + .pNext =3D nullptr, + .flags =3D 0, + .queueCreateInfoCount =3D m_separatePresentQueue ? 2u : 1u, + .pQueueCreateInfos =3D queueCreateInfo, + .enabledLayerCount =3D 0, + .ppEnabledLayerNames =3D nullptr, + .enabledExtensionCount =3D (uint32_t) enabledDeviceExtensions.size= (), + .ppEnabledExtensionNames =3D enabledDeviceExtensions.data(), + .pEnabledFeatures =3D &enabledFeatures + }; + + m_device =3D std::make_unique(physicalDevice, &deviceCre= ateInfo); + if (!m_device->isValid()) { + qCCritical(KWIN_VULKAN) << "Failed to create the logical device"; + return false; + } + + // Get the queues from the device + m_graphicsQueue =3D m_device->getQueue(graphicsQueueFamilyIndex, 0); + + if (m_separatePresentQueue) + m_presentQueue =3D m_device->getQueue(presentQueueFamilyIndex, 0); + else + m_presentQueue =3D 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 =3D VulkanCommandPool(device(), 0, m_presentQ= ueue.familyIndex()); + + + // Create the render passes + // ------------------------ + if (!createRenderPasses()) { + qCCritical(KWIN_VULKAN) << "Failed to create the render passes"; + return false; + } + + + // Create the samplers + // ------------------- + m_nearestSampler =3D + VulkanSampler(device(), + { + .sType =3D VK_STRUCTURE_TYPE_SAMPLER_CREATE_= INFO, + .pNext =3D nullptr, + .flags =3D 0, + .magFilter =3D VK_FILTER_NEAREST, + .minFilter =3D VK_FILTER_NEAREST, + .mipmapMode =3D VK_SAMPLER_MIPMAP_MODE_NEARE= ST, + .addressModeU =3D VK_SAMPLER_ADDRESS_MODE_CL= AMP_TO_EDGE, + .addressModeV =3D VK_SAMPLER_ADDRESS_MODE_CL= AMP_TO_EDGE, + .addressModeW =3D VK_SAMPLER_ADDRESS_MODE_CL= AMP_TO_EDGE, + .mipLodBias =3D 0.0f, + .anisotropyEnable =3D VK_FALSE, + .maxAnisotropy =3D 1.0f, + .compareEnable =3D VK_FALSE, + .compareOp =3D VK_COMPARE_OP_ALWAYS, + .minLod =3D 0.0f, + .maxLod =3D 1.0f, + .borderColor =3D VK_BORDER_COLOR_FLOAT_TRANS= PARENT_BLACK, + .unnormalizedCoordinates =3D VK_TRUE + }); + + m_linearSampler =3D + VulkanSampler(device(), + { + .sType =3D VK_STRUCTURE_TYPE_SAMPLER_CREATE_= INFO, + .pNext =3D nullptr, + .flags =3D 0, + .magFilter =3D VK_FILTER_LINEAR, + .minFilter =3D VK_FILTER_LINEAR, + .mipmapMode =3D VK_SAMPLER_MIPMAP_MODE_NEARE= ST, + .addressModeU =3D VK_SAMPLER_ADDRESS_MODE_CL= AMP_TO_EDGE, + .addressModeV =3D VK_SAMPLER_ADDRESS_MODE_CL= AMP_TO_EDGE, + .addressModeW =3D VK_SAMPLER_ADDRESS_MODE_CL= AMP_TO_EDGE, + .mipLodBias =3D 0.0f, + .anisotropyEnable =3D VK_FALSE, + .maxAnisotropy =3D 1.0f, + .compareEnable =3D VK_FALSE, + .compareOp =3D VK_COMPARE_OP_ALWAYS, + .minLod =3D 0.0f, + .maxLod =3D 1.0f, + .borderColor =3D VK_BORDER_COLOR_FLOAT_TRANS= PARENT_BLACK, + .unnormalizedCoordinates =3D VK_TRUE + }); + + + // Create the swapchain + // -------------------- + m_swapchain =3D std::make_unique(device(), m_presentQ= ueue, surface->handle(), m_surfaceFormat, m_surfaceColorSpace, m_presentMod= e, m_loadRenderPass); + m_swapchain->setSupportsIncrementalPresent(supportedDeviceExtensions.c= ontains(VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME)); + m_swapchain->setPreferredImageCount(swapchainImageCount); + + + // Create the pipeline manager + // --------------------------- + m_pipelineCache =3D std::make_unique(device()); + + m_pipelineManager =3D std::make_unique(device()= , m_pipelineCache.get(), + m_nearestS= ampler, m_linearSampler, + m_loadRend= erPass, (VkRenderPass) VK_NULL_HANDLE, + supportedD= eviceExtensions.contains(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME)); + + if (!m_pipelineManager->isValid()) { + qCCritical(KWIN_VULKAN) << "Failed to create the Vulkan pipeline m= anager"; + return false; + } + + + // Prepare the frame & paint pass ring buffers + // ------------------------------------------- + for (FrameData &data : m_frameData) { + data.imageAcquisitionFence =3D VulkanFence(device()); + data.imageAcquisitionFenceSubmitted =3D false; + + data.imageAcquisitionSemaphore =3D std::make_shared(device()); + + if (m_separatePresentQueue) { + data.acquireOwnershipSemaphore =3D std::make_shared(device()); + data.releaseOwnershipSemaphore =3D std::make_shared(device()); + } + } + + for (PaintPassData &data : m_paintPassData) { + data.semaphore =3D std::make_shared(device()); + data.commandPools[GraphicsQueueFamily] =3D VulkanCommandPool(devic= e(), VK_COMMAND_POOL_CREATE_TRANSIENT_BIT, m_graphicsQueue.familyIndex()); + data.setupCommandBuffer =3D std::make_unique(= device(), data.commandPools[GraphicsQueueFamily], VK_COMMAND_BUFFER_LEVEL_P= RIMARY); + data.mainCommandBuffer =3D std::make_unique(d= evice(), data.commandPools[GraphicsQueueFamily], VK_COMMAND_BUFFER_LEVEL_PR= IMARY); + data.fence =3D VulkanFence(device()); + data.fenceSubmitted =3D false; + } + + + // Create the device memory allocator + // ---------------------------------- + m_deviceMemoryAllocator =3D std::make_unique(device(), enabledDeviceExtensions); + + + // Create the upload managers + // -------------------------- + m_uploadManager =3D + std::make_unique(device(), + deviceMemoryAllocator(), + 16 * 1024, + VK_BUFFER_USAGE_TRANSFER= _SRC_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFF= ER_BIT, + VK_MEMORY_PROPERTY_HOST_= VISIBLE_BIT, + m_physicalDeviceProperti= es.limits); + + m_imageUploadManager =3D + std::make_unique(device(), + deviceMemoryAllocator(), + 8 * 1024 * 1024, + VK_BUFFER_USAGE_TRANSFER= _SRC_BIT, + VK_MEMORY_PROPERTY_HOST_= VISIBLE_BIT, + m_physicalDeviceProperti= es.limits); + + + // Create the descriptor pools + // --------------------------- + m_textureDescriptorPool =3D + std::make_unique(SceneDescriptorPool::Cre= ateInfo { + .device =3D device(), + .descriptorSetLayout= =3D + pipelineMana= ger()->descriptorSetLayout(VulkanPipelineManager::Texture), + .maxSets =3D 72, + .poolSizes =3D { + { VK_DESCRIPTOR_= TYPE_COMBINED_IMAGE_SAMPLER, 72 }, + { VK_DESCRIPTOR_= TYPE_UNIFORM_BUFFER_DYNAMIC, 72 } + } + }); + + m_crossFadeDescriptorPool =3D + std::make_unique(SceneDescriptorPool::Cre= ateInfo { + .device =3D device(), + .descriptorSetLayout = =3D + pipelineManag= er()->descriptorSetLayout(VulkanPipelineManager::TwoTextures), + .maxSets =3D 8, + .poolSizes =3D { + { VK_DESCRIPTOR_T= YPE_SAMPLED_IMAGE, 16 }, + { VK_DESCRIPTOR_T= YPE_SAMPLER, 8 }, + { VK_DESCRIPTOR_T= YPE_UNIFORM_BUFFER_DYNAMIC, 8 } + } + }); + + m_colorDescriptorPool =3D + std::make_unique(SceneDescriptorPool::Cre= ateInfo { + .device =3D device(), + .descriptorSetLayout= =3D + pipelineMana= ger()->descriptorSetLayout(VulkanPipelineManager::FlatColor), + .maxSets =3D 16, + .poolSizes =3D { + { VK_DESCRIPTOR_= TYPE_UNIFORM_BUFFER_DYNAMIC, 16 } + } + }); + + m_valid =3D 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 c= oordinate 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 O= penGL coordinate +// system, where (-1, -1) correspond to the lower-left corner, and the nea= r 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, flo= at top, float zNear, float zFar) const +{ + const float x =3D 2.0f * zNear / (right - left); + const float y =3D 2.0f * zNear / (bottom - top); + const float a =3D (right + left) / (right - left); + const float b =3D (top + bottom) / (bottom - top); + const float c =3D zFar / (zNear - zFar); + const float d =3D 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=C2=B0 field-of-view, + // and an aspect ratio of 1.0. + const float fovY =3D 60.0f; + const float aspect =3D 1.0f; + const float zNear =3D 0.1f; + const float zFar =3D 100.0f; + + const float yMax =3D zNear * std::tan(fovY * M_PI / 360.0f); + const float yMin =3D -yMax; + const float xMin =3D yMin * aspect; + const float xMax =3D yMax * aspect; + + const QMatrix4x4 projection =3D frustum(xMin, xMax, yMin, yMax, zNear,= zFar); + + // Create a second matrix that transforms screen coordinates + // to world coordinates. + const float scaleFactor =3D 1.1 * std::tan(fovY * M_PI / 360.0f) / yMa= x; + const QSize size =3D 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() =3D=3D 0.0) + return matrix; + + // Apply the rotation + // + // Note that we cannot use data.rotation->applyTo(&matrix) as QGraphic= sRotation + // uses projectedRotate() to map back to 2D + matrix.translate(data.rotationOrigin()); + const QVector3D axis =3D 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::QueuedConnecti= on); + + KNotification::event(QStringLiteral("graphicsreset"), i18n("Desktop ef= fects were restarted due to a graphics reset")); +} + + +void VulkanScene::handleFatalError() +{ + QMetaObject::invokeMethod(this, "compositingFailed", Qt::QueuedConnect= ion); + KNotification::event(QStringLiteral("compositingfailed"), + i18n("Desktop effects have been suspended due to = a fatal error")); +} + + +bool VulkanScene::checkResult(VkResult result, std::function p= rintDebugMessage) +{ + 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 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 =3D 0; + + // (Re)create the swap chain if necessary + if (!m_swapchain->isValid()) { + const VkResult result =3D m_swapchain->create(); + + if (!checkResult(result, []{ qCCritical(KWIN_VULKAN) << "Swapc= hain creation failed"; })) + return 0; + + m_imageAcquired =3D false; + } + + FrameData &frame =3D m_frameData[m_frameIndex]; + + if (!m_imageAcquired) { + if (frame.imageAcquisitionFenceSubmitted) { + // Block on the acquisition fence to prevent us from getti= ng more than + // m_frameData.size() frames ahead of the presentation eng= ine. + frame.imageAcquisitionFence.wait(); + frame.imageAcquisitionFence.reset(); + + frame.imageAcquisitionFenceSubmitted =3D false; + } + + // Attempt to acquire an image from the swapchain + int32_t imageIndex =3D -1; + VkResult result =3D m_swapchain->acquireNextImage(UINT64_MAX, = frame.imageAcquisitionSemaphore->handle(), frame.imageAcquisitionFence, &im= ageIndex); + + 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() << "vkAcquireNextImageKH= R returned" << enumToString(result); + handleFatalError(); + return 0; + }; + + m_waitSemaphores.insert(frame.imageAcquisitionSemaphore); + frame.imageAcquisitionFenceSubmitted =3D true; + + m_bufferAge =3D m_swapchain->bufferAge(); + m_surfaceLayout =3D m_bufferAge =3D=3D 0 ? VK_IMAGE_LAYOUT_UND= EFINED : VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + m_needQueueOwnershipTransfer =3D false; // Will depend on whet= her we load or clear the contents + m_imageAcquired =3D true; + } + + QRegion repaint =3D m_swapchain->accumulatedDamageHistory(m_buffer= Age); + + m_projectionMatrix =3D createProjectionMatrix(); + m_renderPassStarted =3D false; + m_clearPending =3D false; + m_usedIndexBuffer =3D false; + + PaintPassData &paintPass =3D 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 =3D false; + } + + + // After this call, updateRegion will contain the damaged region i= n the + // back buffer. This is the region that needs to be posted to repa= ir + // the front buffer. It does not include the additional damage ret= urned + // by accumulatedDamageHistory(). validRegion is the region that h= as been + // repainted, and it may be larger than updateRegion. + QRegion updateRegion, validRegion; + paintScreen(&mask, damage, repaint, &updateRegion, &validRegion, p= rojectionMatrix()); + + updateRegion &=3D QRegion(0, 0, m_swapchain->width(), m_swapchain-= >height()); + + + // Submit the command buffers + // -------------------------- + if (m_commandBuffersPending || m_clearPending) { + VulkanCommandBuffer *setupCommandBuffer =3D paintPass.setupCom= mandBuffer.get(); + VulkanCommandBuffer *mainCommandBuffer =3D paintPass.mainComma= ndBuffer.get(); + + std::vector waitSemaphores =3D m_waitSemaphores.t= oNativeHandleVector(); + + // If the present queue has ownership of the image we need to = submit a + // command buffer to the present queue that relinquishes owner= ship before + // we submit the main command buffer to the graphics queue. Th= e graphics + // queue ownership acquisition operation is handled by beginRe= nderPass(). + if (m_needQueueOwnershipTransfer) { + std::shared_ptr &cmd =3D m_swapchain-= >releaseOwnershipCommandBuffer(); + + // This command buffer is recorded once and reused + if (!cmd) { + cmd =3D std::make_shared(device()= , m_presentCommandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY); + + cmd->begin(VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BI= T); + cmd->pipelineBarrier(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_= BIT, + 0, + {}, + {}, + { + { + .sType =3D VK_STRUCTURE_T= YPE_IMAGE_MEMORY_BARRIER, + .pNext =3D nullptr, + .srcAccessMask =3D VK_ACC= ESS_MEMORY_READ_BIT, + .dstAccessMask =3D 0, + .oldLayout =3D VK_IMAGE_L= AYOUT_PRESENT_SRC_KHR, + .newLayout =3D VK_IMAGE_L= AYOUT_COLOR_ATTACHMENT_OPTIMAL, + .srcQueueFamilyIndex =3D = m_presentQueue.familyIndex(), + .dstQueueFamilyIndex =3D = m_graphicsQueue.familyIndex(), + .image =3D m_swapchain->c= urrentImage(), + .subresourceRange =3D { + .aspectMask =3D VK_IM= AGE_ASPECT_COLOR_BIT, + .baseMipLevel =3D 0, + .levelCount =3D 1, + .baseArrayLayer =3D 0, + .layerCount =3D 1 + } + } + }); + cmd->end(); + } + + // Submit the command buffer to the present queue + const VkCommandBuffer commandBuffers[] =3D { cmd->handle()= }; + const VkSemaphore signalSemaphores[] =3D { frame.releaseOw= nershipSemaphore->handle() }; + std::vector waitDstStageMasks(waitSe= maphores.size(), VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); + + const VkResult result =3D m_presentQueue.submit({ + { + .sTy= pe =3D VK_STRUCTURE_TYPE_SUBMIT_INFO, + .pNe= xt =3D nullptr, + .wai= tSemaphoreCount =3D (uint32_t) waitSemaphores.size(), + .pWa= itSemaphores =3D waitSemaphores.data(), + .pWa= itDstStageMask =3D waitDstStageMasks.data(), + .com= mandBufferCount =3D 1, + .pCo= mmandBuffers =3D commandBuffers, + .sig= nalSemaphoreCount =3D 1, + .pSi= gnalSemaphores=3D signalSemaphores + } + }); + + if (!checkResult(result, [=3D]{ qCCritical(KWIN_VULKAN).no= quote() << "VkQueueSubmit returned" << enumToString(result); })) + return 0; + + waitSemaphores =3D { signalSemaphores[0] }; + m_needQueueOwnershipTransfer =3D false; + } + + // Force the clear render pass to begin + if (m_clearPending && !m_renderPassStarted) + beginRenderPass(mainCommandBuffer); + + if (mainCommandBuffer->isRenderPassActive()) + mainCommandBuffer->endRenderPass(); + + m_renderPassStarted =3D false; + + // Transition the image to VK_IMAGE_LAYOUT_PRESENT_SRC_KHR + if (!updateRegion.isEmpty() && m_surfaceLayout !=3D VK_IMAGE_L= AYOUT_PRESENT_SRC_KHR) { + uint32_t srcAccessMask =3D VK_ACCESS_COLOR_ATTACHMENT_WRIT= E_BIT; + uint32_t dstAccessMask =3D VK_ACCESS_MEMORY_READ_BIT; + uint32_t srcQueueFamilyIndex =3D VK_QUEUE_FAMILY_IGNORED; + uint32_t dstQueueFamilyIndex =3D VK_QUEUE_FAMILY_IGNORED; + + if (m_separatePresentQueue) { + // Transfer ownership to the present queue + srcAccessMask =3D VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dstAccessMask =3D 0; + srcQueueFamilyIndex =3D m_graphicsQueue.familyIndex(); + dstQueueFamilyIndex =3D m_presentQueue.familyIndex(); + } + + const VkImageMemoryBarrier barrier =3D { + .sType =3D VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext =3D nullptr, + .srcAccessMask =3D srcAccessMask, + .dstAccessMask =3D dstAccessMask, + .oldLayout =3D m_surfaceLayout, + .newLayout =3D VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + .srcQueueFamilyIndex =3D srcQueueFamilyIndex, + .dstQueueFamilyIndex =3D dstQueueFamilyIndex, + .image =3D m_swapchain->currentImage(), + .subresourceRange =3D { + .aspectMask =3D VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel =3D 0, + .levelCount =3D 1, + .baseArrayLayer =3D 0, + .layerCount =3D 1 + } + }; + + assert(mainCommandBuffer->isActive()); + mainCommandBuffer->pipelineBarrier(VK_PIPELINE_STAGE_COLOR= _ATTACHMENT_OUTPUT_BIT, + VK_PIPELINE_STAGE_BOTTO= M_OF_PIPE_BIT, + 0, {}, {}, { barrier }); + m_surfaceLayout =3D VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + } + + + // Flush the upload managers + std::vector ranges; + + m_uploadManager->getNonCoherentAllocatedRanges(std::back_inser= ter(ranges)); + m_imageUploadManager->getNonCoherentAllocatedRanges(std::back_= inserter(ranges)); + + if (!ranges.empty()) + m_device->flushMappedMemoryRanges(ranges.size(), ranges.da= ta()); + + addBusyReference(m_uploadManager->createFrameBoundary()); + addBusyReference(m_imageUploadManager->createFrameBoundary()); + + + // Submit the command buffers to the graphics queue + VkCommandBuffer commandBuffers[2]; + uint32_t commandBufferCount =3D 0; + + if (setupCommandBuffer->isActive()) { + commandBuffers[commandBufferCount++] =3D setupCommandBuffe= r->handle(); + setupCommandBuffer->end(); + } + + if (mainCommandBuffer->isActive()) { + commandBuffers[commandBufferCount++] =3D mainCommandBuffer= ->handle(); + mainCommandBuffer->end(); + } + + assert(commandBufferCount > 0); + + const VkSemaphore signalSemaphores[] =3D { paintPass.semaphore= ->handle() }; + std::vector waitDstStageMasks(waitSemaph= ores.size(), VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT); + + const VkResult result =3D m_graphicsQueue.submit({ + { + .sType = =3D VK_STRUCTURE_TYPE_SUBMIT_INFO, + .pNext = =3D nullptr, + .waitSe= maphoreCount =3D (uint32_t) waitSemaphores.size(), + .pWaitS= emaphores =3D waitSemaphores.data(), + .pWaitD= stStageMask =3D waitDstStageMasks.data(), + .comman= dBufferCount =3D commandBufferCount, + .pComma= ndBuffers =3D commandBuffers, + .signal= SemaphoreCount =3D uint32_t(updateRegion.isEmpty() ? 0 : 1), + .pSigna= lSemaphores =3D updateRegion.isEmpty() ? nullptr : signalSemaphores + } + }, + paintPass.fence= ); + + m_commandBuffersPending =3D false; + + if (!checkResult(result, [=3D]{ qCCritical(KWIN_VULKAN).noquot= e() << "vkQueueSubmit returned" << enumToString(result); })) + return 0; + + // Acquire present queue ownership of the swapchain image + if (m_separatePresentQueue && !updateRegion.isEmpty()) { + std::shared_ptr &cmd =3D m_swapchain-= >acquireOwnershipCommandBuffer(); + + // This command buffer is recorded once and reused + if (!cmd) { + cmd =3D std::make_shared(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_BI= T); + cmd->pipelineBarrier(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_= BIT, + 0, + {}, + {}, + { + { + .sType =3D VK_STRUCTURE_T= YPE_IMAGE_MEMORY_BARRIER, + .pNext =3D nullptr, + .srcAccessMask =3D 0, + .dstAccessMask =3D 0, + .oldLayout =3D VK_IMAGE_L= AYOUT_COLOR_ATTACHMENT_OPTIMAL, + .newLayout =3D VK_IMAGE_L= AYOUT_PRESENT_SRC_KHR, + .srcQueueFamilyIndex =3D = m_graphicsQueue.familyIndex(), + .dstQueueFamilyIndex =3D = m_presentQueue.familyIndex(), + .image =3D m_swapchain->c= urrentImage(), + .subresourceRange =3D { + .aspectMask =3D VK_IM= AGE_ASPECT_COLOR_BIT, + .baseMipLevel =3D 0, + .levelCount =3D 1, + .baseArrayLayer =3D 0, + .layerCount =3D 1 + } + } + }); + cmd->end(); + } + + // Submit the command buffer to the present queue + const VkCommandBuffer commandBuffers[] =3D { cmd->handle()= }; + const VkSemaphore waitSemaphores[] =3D { paintPass.semapho= re->handle() }; + const VkPipelineStageFlags waitDstStageMasks[] =3D { VK_PI= PELINE_STAGE_ALL_COMMANDS_BIT }; + const VkSemaphore signalSemaphores[] =3D { frame.acquireOw= nershipSemaphore->handle() }; + + const VkResult result =3D m_presentQueue.submit({ + { + .sTy= pe =3D VK_STRUCTURE_TYPE_SUBMIT_INFO, + .pNe= xt =3D nullptr, + .wai= tSemaphoreCount =3D 1, + .pWa= itSemaphores =3D waitSemaphores, + .pWa= itDstStageMask =3D waitDstStageMasks, + .com= mandBufferCount =3D 1, + .pCo= mmandBuffers =3D commandBuffers, + .sig= nalSemaphoreCount =3D 1, + .pSi= gnalSemaphores =3D signalSemaphores + } + }); + + if (!checkResult(result, [=3D]{ qCCritical(KWIN_VULKAN).no= quote() << "VkQueueSubmit returned" << enumToString(result); })) + return 0; + } + + paintPass.fenceSubmitted =3D true; + + m_waitSemaphores.clear(); + m_paintPassIndex =3D (m_paintPassIndex + 1) % m_paintPassData.= size(); + } + + + // Present the image + // ----------------- + if (!updateRegion.isEmpty()) { + m_backend->showOverlay(); + + std::shared_ptr semaphore =3D m_separatePrese= ntQueue ? + frame.acquireOwnershipSemaphore : paintPass.semaphore; + + const VkResult result =3D m_swapchain->present(m_swapchain->cu= rrentIndex(), updateRegion, semaphore->handle()); + m_imageAcquired =3D 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 re= turned" << enumToString(result); + handleFatalError(); + return 0; + } + + m_swapchain->addToDamageHistory(updateRegion); + + m_frameIndex =3D (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 j= ust + // set the buffer age to one, so the repaired regions won't be + // rendered again in the next paint pass. + m_bufferAge =3D 1; + } + } + + return timer.nsecsElapsed(); +} + + +void VulkanScene::paintSimpleScreen(int mask, QRegion region) +{ + m_screenProjectionMatrix =3D m_projectionMatrix; + + Scene::paintSimpleScreen(mask, region); +} + + +void VulkanScene::paintGenericScreen(int mask, ScreenPaintData data) +{ + m_screenProjectionMatrix =3D m_projectionMatrix * screenMatrix(mask, d= ata); + + Scene::paintGenericScreen(mask, data); +} + + +void VulkanScene::paintBackground(QRegion region) +{ + const QRegion swapchainRegion(0, 0, m_swapchain->width(), m_swapchain-= >height()); + region &=3D swapchainRegion; + + if (region.isEmpty()) + return; + + if (region =3D=3D 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 wil= l mark + // the clear as pending, and let beginRenderPass() handle it. + m_clearPending =3D true; + return; + } + + auto vbo =3D uploadManager()->allocate(region.rectCount() * 4 * sizeof= (QVector2D)); + auto ubo =3D uploadManager()->emplaceUniform(m_projectionMatrix, QVector4D(0.0f, 0.0f, 0.0f, 1.0f)); + + QVector2D *vertex =3D static_cast(vbo.data()); + for (const QRect &r : region) { + const float x0 =3D r.x(); + const float y0 =3D r.y(); + const float x1 =3D r.x() + r.width(); + const float y1 =3D r.y() + r.height(); + + *vertex++ =3D { x0, y0 }; // Top-left + *vertex++ =3D { x1, y0 }; // Top-right + *vertex++ =3D { x1, y1 }; // Bottom-right + *vertex++ =3D { x0, y1 }; // Bottom-left + } + + const QRect r =3D region.boundingRect(); + + const VkRect2D scissor =3D { + .offset =3D { (int32_t) r.x(), (int32_t) r.y() }, + .extent =3D { (uint32_t) r.width(), (uint32_t) r.height() } + }; + + VkPipeline pipeline; + VkPipelineLayout pipelineLayout; + + std::tie(pipeline, pipelineLayout) =3D + pipelineManager()->pipeline(VulkanPipelineManager::FlatColor, + VulkanPipelineManager::NoTraits, + VulkanPipelineManager::DescriptorS= et, + VulkanPipelineManager::TriangleLis= t, + VulkanPipelineManager::SwapchainRe= nderPass); + + if (!m_clearDescriptorSet || m_clearDescriptorSet->uniformBuffer() != =3D ubo.buffer()) { + if (!m_clearDescriptorSet || m_clearDescriptorSet.use_count() > 1) + m_clearDescriptorSet =3D std::make_shared(= colorDescriptorPool()); + + m_clearDescriptorSet->update(ubo.buffer()); + } + + auto cmd =3D mainCommandBuffer(); + + cmd->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + cmd->bindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayou= t, 0, { m_clearDescriptorSet->handle() }, { (uint32_t) ubo.offset() }); + cmd->bindVertexBuffers(0, { vbo.handle() }, { vbo.offset() } ); + cmd->bindIndexBuffer(indexBufferForQuadCount(region.rectCount()), 0, V= K_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 =3D m_swapchain->currentFramebuffer(); + VkRenderPass renderPass; + + if (m_clearPending || m_surfaceLayout =3D=3D VK_IMAGE_LAYOUT_UNDEFINED= ) { + renderPass =3D m_clearRenderPass; + m_clearPending =3D false; + } else if (m_surfaceLayout =3D=3D VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) { + if (m_separatePresentQueue) { + renderPass =3D m_loadRenderPass; + m_needQueueOwnershipTransfer =3D true; + } else { + renderPass =3D m_transitionRenderPass; + } + } else { + assert(m_surfaceLayout =3D=3D VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPT= IMAL); + renderPass =3D m_loadRenderPass; + } + + const VkClearColorValue clearColor =3D { + .float32 =3D { 0.0f, 0.0f, 0.0f, 1.0f } + }; + + const VkClearValue clearValue =3D { + .color =3D clearColor, + }; + + uint32_t width =3D m_swapchain->width(); + uint32_t height =3D m_swapchain->height(); + + const VkRenderPassBeginInfo renderPassBeginInfo =3D { + .sType =3D VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, + .pNext =3D nullptr, + .renderPass =3D renderPass, + .framebuffer =3D framebuffer, + .renderArea =3D { + .offset =3D { 0, 0 }, + .extent =3D { width, height } + }, + .clearValueCount =3D 1, + .pClearValues =3D &clearValue + }; + + const VkViewport viewport =3D { + .x =3D 0.0f, + .y =3D 0.0f, + .width =3D float(width), + .height =3D float(height), + .minDepth =3D 0.0f, + .maxDepth =3D 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 =3D VK_STRUCTURE_TYPE_IMAGE_ME= MORY_BARRIER, + .pNext =3D nullptr, + .srcAccessMask =3D 0, + .dstAccessMask =3D VK_ACCESS_COLOR_AT= TACHMENT_READ_BIT | + VK_ACCESS_COLOR_ATTA= CHMENT_WRITE_BIT, + .oldLayout =3D VK_IMAGE_LAYOUT_PRESEN= T_SRC_KHR, + .newLayout =3D VK_IMAGE_LAYOUT_COLOR_= ATTACHMENT_OPTIMAL, + .srcQueueFamilyIndex =3D m_presentQue= ue.familyIndex(), + .dstQueueFamilyIndex =3D m_graphicsQu= eue.familyIndex(), + .image =3D m_swapchain->currentImage(= ), + .subresourceRange =3D { + .aspectMask =3D VK_IMAGE_ASPECT_C= OLOR_BIT, + .baseMipLevel =3D 0, + .levelCount =3D 1, + .baseArrayLayer =3D 0, + .layerCount =3D 1 + } + } + }); + } + + cmd->beginRenderPass(&renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); + cmd->setViewport(0, 1, &viewport); + + m_surfaceLayout =3D VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + m_renderPassStarted =3D true; +} + + +VulkanCommandBuffer *VulkanScene::mainCommandBuffer() +{ + auto &paintPass =3D currentPaintPass(); + auto cmd =3D currentPaintPass().mainCommandBuffer.get(); + + if (!cmd->isActive()) { + assert(!paintPass.fenceSubmitted); + assert(!m_renderPassStarted); + beginRenderPass(cmd); + m_commandBuffersPending =3D true; + } + + return cmd; +} + + +VulkanCommandBuffer *VulkanScene::setupCommandBuffer() +{ + auto &paintPass =3D currentPaintPass(); + auto cmd =3D paintPass.setupCommandBuffer.get(); + + if (!cmd->isActive()) { + if (paintPass.fenceSubmitted) { + paintPass.fence.wait(); + paintPass.fenceSubmitted =3D 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 =3D 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::De= coratedClientImpl *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 =3D 0, + .format =3D m_surfaceFormat, + .samples =3D VK_SAMPLE_COUNT_1_BIT, + .loadOp =3D VK_ATTACHMENT_LOAD_OP_LOAD, + .storeOp =3D VK_ATTACHMENT_STORE_OP_STORE, + .stencilLoadOp =3D VK_ATTACHMENT_LOAD_OP_DONT_CARE, + .stencilStoreOp =3D VK_ATTACHMENT_STORE_OP_DONT_CARE, + .initialLayout =3D VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + .finalLayout =3D VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL + }; + + const VkAttachmentDescription transitionBackBufferAttachment { + .flags =3D 0, + .format =3D m_surfaceFormat, + .samples =3D VK_SAMPLE_COUNT_1_BIT, + .loadOp =3D VK_ATTACHMENT_LOAD_OP_LOAD, + .storeOp =3D VK_ATTACHMENT_STORE_OP_STORE, + .stencilLoadOp =3D VK_ATTACHMENT_LOAD_OP_DONT_CARE, + .stencilStoreOp =3D VK_ATTACHMENT_STORE_OP_DONT_CARE, + .initialLayout =3D VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + .finalLayout =3D VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL + }; + + const VkAttachmentDescription clearBackBufferAttachment { + .flags =3D 0, + .format =3D m_surfaceFormat, + .samples =3D VK_SAMPLE_COUNT_1_BIT, + .loadOp =3D VK_ATTACHMENT_LOAD_OP_CLEAR, + .storeOp =3D VK_ATTACHMENT_STORE_OP_STORE, + .stencilLoadOp =3D VK_ATTACHMENT_LOAD_OP_DONT_CARE, + .stencilStoreOp =3D VK_ATTACHMENT_STORE_OP_DONT_CARE, + .initialLayout =3D VK_IMAGE_LAYOUT_UNDEFINED, + .finalLayout =3D VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL + }; + + static const VkAttachmentReference colorAttachmentRef { + .attachment =3D 0, + .layout =3D VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL + }; + + const VkSubpassDescription subpass { + .flags =3D 0, + .pipelineBindPoint =3D VK_PIPELINE_BIND_POINT_GRAPHICS, + .inputAttachmentCount =3D 0, + .pInputAttachments =3D nullptr, + .colorAttachmentCount =3D 1, + .pColorAttachments =3D &colorAttachmentRef, + .pResolveAttachments =3D nullptr, + .pDepthStencilAttachment =3D nullptr, + .preserveAttachmentCount =3D 0, + .pPreserveAttachments =3D nullptr + }; + + static const VkSubpassDependency clearStartDependency =3D { + .srcSubpass =3D VK_SUBPASS_EXTERNAL, + .dstSubpass =3D 0, + .srcStageMask =3D VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + .dstStageMask =3D VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + .srcAccessMask =3D 0, + .dstAccessMask =3D VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + .dependencyFlags =3D 0 + }; + + static const VkSubpassDependency transitionStartDependency =3D { + .srcSubpass =3D VK_SUBPASS_EXTERNAL, + .dstSubpass =3D 0, + .srcStageMask =3D VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + .dstStageMask =3D VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + .srcAccessMask =3D 0, + .dstAccessMask =3D VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS= _COLOR_ATTACHMENT_WRITE_BIT, + .dependencyFlags =3D 0 + }; + + static const VkSubpassDependency loadStartDependency =3D { + .srcSubpass =3D VK_SUBPASS_EXTERNAL, + .dstSubpass =3D 0, + .srcStageMask =3D VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + .dstStageMask =3D VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + .srcAccessMask =3D 0, + .dstAccessMask =3D VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS= _COLOR_ATTACHMENT_WRITE_BIT, + .dependencyFlags =3D 0 + }; + + static const VkSubpassDependency endDependency =3D { + .srcSubpass =3D 0, + .dstSubpass =3D VK_SUBPASS_EXTERNAL, + .srcStageMask =3D VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + .dstStageMask =3D VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, + .srcAccessMask =3D VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + .dstAccessMask =3D 0, + .dependencyFlags =3D 0 + }; + + m_clearRenderPass =3D VulkanRenderPass(device(), { clearBackB= ufferAttachment }, { subpass }, { clearStartDependency, endDepend= ency }); + m_transitionRenderPass =3D VulkanRenderPass(device(), { transition= BackBufferAttachment }, { subpass }, { transitionStartDependency, endDepend= ency }); + m_loadRenderPass =3D VulkanRenderPass(device(), { loadBackBu= fferAttachment }, { subpass }, { loadStartDependency, endDepend= ency }); + + return true; +} + + +VkBuffer VulkanScene::indexBufferForQuadCount(uint32_t quadCount) +{ + if (m_indexBufferQuadCount >=3D quadCount) { + if (!m_usedIndexBuffer) { + addBusyReference(m_indexBuffer); + addBusyReference(m_indexBufferMemory); + + m_usedIndexBuffer =3D true; + } + + return m_indexBuffer->handle(); + } + + const size_t bytesPerQuad =3D 6 * sizeof(uint16_t); + quadCount =3D std::max(align(quadCount * bytesPerQuad, 4096)= , 16384) / bytesPerQuad; + + auto writeIndices =3D [](uint16_t *dst, uint32_t quadCount) { + for (uint32_t i =3D 0; i < quadCount; i++) { + for (int j : {1, 0, 3, 3, 2, 1}) { + *dst++ =3D i * 4 + j; + } + } + }; + + size_t size =3D quadCount * bytesPerQuad; + m_indexBuffer =3D std::make_shared(device(), + VkBufferCreateInfo { + .sType =3D VK_STRUC= TURE_TYPE_BUFFER_CREATE_INFO, + .pNext =3D nullptr, + .flags =3D 0, + .size =3D size, + .usage =3D VK_BUFFE= R_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, + .sharingMode =3D VK= _SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCo= unt =3D 0, + .pQueueFamilyIndice= s =3D nullptr, + }); + + m_indexBufferMemory =3D deviceMemoryAllocator()->allocateMemory(m_inde= xBuffer, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 0); + + if (m_indexBufferMemory->isHostVisible()) { + // Map the memory if it is host visible and write the indices dire= ctly into it + uint16_t *indices =3D nullptr; + m_indexBufferMemory->map(0, (void **) &indices); + + writeIndices(indices, quadCount); + + if (!m_indexBufferMemory->isHostCoherent()) { + m_device->flushMappedMemoryRanges({ + { + .sType =3D VK_STRUCT= URE_TYPE_MAPPED_MEMORY_RANGE, + .pNext =3D nullptr, + .memory =3D m_indexB= ufferMemory->handle(), + .offset =3D 0, + .size =3D VK_WHOLE_S= IZE + } + }); + } + + m_indexBufferMemory->unmap(); + } else { + // Upload the indices to staging memory and copy them to the index= buffer + auto range =3D uploadManager()->allocate(size); + writeIndices((uint16_t *) range.data(), quadCount); + auto cmd =3D setupCommandBuffer(); + + cmd->copyBuffer(range.buffer()->handle(), + m_indexBuffer->handle(), + { + { + .srcOffset =3D range.offset(), + .dstOffset =3D 0, + .size =3D size + } + }); + + const VkBufferMemoryBarrier barrier { + .sType =3D VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, + .pNext =3D nullptr, + .srcAccessMask =3D VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask =3D VK_ACCESS_INDEX_READ_BIT, + .srcQueueFamilyIndex =3D VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex =3D VK_QUEUE_FAMILY_IGNORED, + .buffer =3D m_indexBuffer->handle(), + .offset =3D 0, + .size =3D size + }; + + cmd->pipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, + 0, {}, { barrier }, {}); + } + + addBusyReference(m_indexBuffer); + addBusyReference(m_indexBufferMemory); + + m_indexBufferQuadCount =3D quadCount; + m_usedIndexBuffer =3D 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 =C2=A9 2017-2018 Fredrik H=C3=B6glund + +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 . +*********************************************************************/ + +#ifndef VULKAN_SCENE_H +#define VULKAN_SCENE_H + +#include "../../../scene.h" +#include "decorations/decorationrenderer.h" +#include "kwinvulkanutils.h" + +#include +#include +#include + +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 =3D nullptr); + ~VulkanFactory() override; + + Scene *create(QObject *parent =3D nullptr) const override; +}; + + + +// ------------------------------------------------------------------ + + + +template +class SharedVulkanObjectSet : public std::unordered_set> +{ +public: + SharedVulkanObjectSet() : std::unordered_set>() {} + + auto toNativeHandleVector() const { + std::vector 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 =3D 0, + QueueFamilyCount + }; + + struct FrameData + { + std::shared_ptr imageAcquisitionSemaphore; + std::shared_ptr acquireOwnershipSemaphore; + std::shared_ptr releaseOwnershipSemaphore; + VulkanFence imageAcquisitionFence; + bool imageAcquisitionFenceSubmitted; + }; + + struct PaintPassData + { + VulkanCommandPool commandPools[QueueFamilyCount]; + std::unique_ptr setupCommandBuffer; + std::unique_ptr mainCommandBuffer; + std::unordered_set> busyObjects; + std::unordered_set> busyGraph= icsCommandBuffers; + std::shared_ptr semaphore; + VulkanFence fence; + bool fenceSubmitted; + }; + + enum { FramesInFlight =3D 2 }; + +public: + explicit VulkanScene(std::unique_ptr &&backend, QObject= *parent =3D 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::DecoratedCl= ientImpl *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_pipelineMana= ger.get(); } + VulkanUploadManager *uploadManager() const { return m_uploadManager.ge= t(); } + VulkanUploadManager *imageUploadManager() const { return m_imageUpload= Manager.get(); } + VulkanDeviceMemoryAllocator *deviceMemoryAllocator() const { return m_= deviceMemoryAllocator.get(); } + SceneDescriptorPool *textureDescriptorPool() const { return m_textureD= escriptorPool.get(); } + SceneDescriptorPool *crossFadeDescriptorPool() const { return m_crossF= adeDescriptorPool.get(); } + SceneDescriptorPool *colorDescriptorPool() const { return m_colorDescr= iptorPool.get(); } + + VkBuffer indexBufferForQuadCount(uint32_t quadCount); + + FrameData ¤tFrame() { return m_frameData[m_frameIndex]; } + const FrameData ¤tFrame() const { return m_frameData[m_frameInde= x]; } + + PaintPassData ¤tPaintPass() { return m_paintPassData[m_paintPass= Index]; } + const PaintPassData ¤tPaintPass() const { return m_paintPassData= [m_paintPassIndex]; } + + /** + * Adds the given object to the list of objects referenced by the curr= ent frame. + */ + void addBusyReference(std::shared_ptr object) { currentP= aintPass().busyObjects.insert(object); } + + QMatrix4x4 projectionMatrix() const { return m_projectionMatrix; } + QMatrix4x4 screenProjectionMatrix() const override final { return m_sc= reenProjectionMatrix; } + + VulkanCommandBuffer *mainCommandBuffer(); + VulkanCommandBuffer *setupCommandBuffer(); + + VkSampler linearSampler() const { return m_linearSampler.handle(); } + VkSampler nearestSampler() const { return m_nearestSampler.handle(); } + + uint32_t optimalBufferCopyOffsetAlignment() const { return m_physicalD= eviceProperties.limits.optimalBufferCopyOffsetAlignment; } + uint32_t optimalBufferCopyRowPitchAlignment() const { return m_physica= lDeviceProperties.limits.optimalBufferCopyRowPitchAlignment; } + + QMatrix4x4 ortho(float left, float right, float bottom, float top, flo= at near, float far) const; + QMatrix4x4 frustum(float left, float right, float bottom, float top, f= loat 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 debugMessa= ge); + +private: + VulkanInstance m_instance; + VulkanDebugReportCallback m_debugReport= Callback; + std::unique_ptr m_backend; + std::unique_ptr m_device; + std::unique_ptr m_pipelineCac= he; + std::unique_ptr m_pipelineMan= ager; + std::unique_ptr m_uploadManag= er; + std::unique_ptr m_imageUpload= Manager; + std::unique_ptr m_deviceMemor= yAllocator; + std::unique_ptr m_textureDesc= riptorPool; + std::unique_ptr m_crossFadeDe= scriptorPool; + std::unique_ptr m_colorDescri= ptorPool; + VulkanCommandPool m_presentComm= andPool; + std::unique_ptr m_swapchain; + VulkanQueue m_graphicsQue= ue; + VulkanQueue m_presentQueu= e; + VulkanRenderPass m_clearRender= Pass; + VulkanRenderPass m_transitionR= enderPass; + VulkanRenderPass m_loadRenderP= ass; + VkFormat m_surfaceForm= at; + VkColorSpaceKHR m_surfaceColo= rSpace; + VkPresentModeKHR m_presentMode; + VkPhysicalDeviceProperties m_physicalDev= iceProperties; + uint32_t m_maxDiscardR= ects; + uint32_t m_maxPushDesc= riptors; + VkImageLayout m_surfaceLayo= ut =3D VK_IMAGE_LAYOUT_UNDEFINED; + std::array m_frameData; + std::array m_paintPassDa= ta; + SharedVulkanObjectSet m_waitSemapho= res; + std::shared_ptr m_indexBuffer; + std::shared_ptr m_indexBuffer= Memory; + uint32_t m_indexBuffer= QuadCount =3D 0; + std::shared_ptr m_clearDescri= ptorSet; + VulkanSampler m_nearestSamp= ler; + VulkanSampler m_linearSampl= er; + QMatrix4x4 m_projectionM= atrix; + QMatrix4x4 m_screenProje= ctionMatrix; + int m_frameIndex = =3D 0; + int m_paintPassIn= dex =3D 0; + int m_bufferAge = =3D 0; + bool m_valid =3D f= alse; + bool m_separatePre= sentQueue =3D false; + bool m_needQueueOw= nershipTransfer =3D false; + bool m_imageAcquir= ed =3D false; + bool m_renderPassS= tarted =3D false; + bool m_commandBuff= ersPending =3D false; + bool m_clearPendin= g =3D false; + bool m_usedIndexBu= ffer =3D false; +}; + +} // namespace KWin + +#endif // VULKAN_SCENE_H diff --git a/plugins/scenes/vulkan/shadow.cpp b/plugins/scenes/vulkan/shado= w.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 =C2=A9 2017-2018 Fredrik H=C3=B6glund + +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 . +*********************************************************************/ + +#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 =C2=A9 2017-2018 Fredrik H=C3=B6glund + +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 . +*********************************************************************/ + +#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/sw= apchain.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 =C2=A9 2017-2018 Fredrik H=C3=B6glund + +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 . +*********************************************************************/ + +#include "swapchain.h" +#include "screens.h" +#include "logging.h" + +namespace KWin +{ + +template +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 =3D false; + + auto physicalDevice =3D m_device->physicalDevice(); + + VkSurfaceCapabilitiesKHR capabilities; + VkResult result =3D physicalDevice.getSurfaceCapabilitiesKHR(m_surface= , &capabilities); + + if (result !=3D VK_SUCCESS) { + qCCritical(KWIN_VULKAN) << "vkPhysicalDeviceGetSurfaceCapabilities= KHR returned" << enumToString(result); + return result; + } + + if (capabilities.currentExtent.width =3D=3D UINT32_MAX) { + const QSize size =3D Screens::self()->size(); + m_extent.width =3D clamp(size.width(), capabilities.mi= nImageExtent.width, capabilities.maxImageExtent.width); + m_extent.height =3D clamp(size.height(), capabilities.mi= nImageExtent.height, capabilities.maxImageExtent.height); + } else { + m_extent =3D capabilities.currentExtent; + } + + uint32_t imageCount =3D std::max(capabilities.minImageCount,= m_preferredImageCount); + if (capabilities.maxImageCount > 0) + imageCount =3D std::min(imageCount, capabilities.maxImageCount); + + VkSurfaceTransformFlagBitsKHR preTransform; + if (capabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_B= IT_KHR) + preTransform =3D VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + else + preTransform =3D capabilities.currentTransform; + + VkCompositeAlphaFlagBitsKHR compositeAlpha; + + if (capabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_B= IT_KHR) + compositeAlpha =3D VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + else if (capabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INH= ERIT_BIT_KHR) + compositeAlpha =3D VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR; + else if (capabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE= _MULTIPLIED_BIT_KHR) + compositeAlpha =3D VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR; + else + compositeAlpha =3D VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR; + + VkImageUsageFlags imageUsage =3D VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + imageUsage &=3D capabilities.supportedUsageFlags; + + VkSwapchainKHR oldSwapchain =3D m_swapchain; + + const VkSwapchainCreateInfoKHR createInfo =3D { + .sType =3D VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, + .pNext =3D nullptr, + .flags =3D 0, + .surface =3D m_surface, + .minImageCount =3D imageCount, + .imageFormat =3D m_format, + .imageColorSpace =3D m_colorSpace, + .imageExtent =3D m_extent, + .imageArrayLayers =3D 1, + .imageUsage =3D imageUsage, + .imageSharingMode =3D VK_SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCount =3D 0, + .pQueueFamilyIndices =3D nullptr, + .preTransform =3D preTransform, + .compositeAlpha =3D compositeAlpha, + .presentMode =3D m_presentMode, + .clipped =3D VK_FALSE, + .oldSwapchain =3D oldSwapchain + }; + + result =3D m_device->createSwapchainKHR(&createInfo, nullptr, &m_swapc= hain); + + if (result !=3D VK_SUCCESS) { + qCCritical(KWIN_VULKAN) << "vkCreateSwapchainKHR returned" << enum= ToString(result); + return result; + } + + m_queue.waitIdle(); + + // Destroy the old swap chain now if we are recreating it + if (oldSwapchain !=3D 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 =3D nullptr; + buffer.releaseOwnershipCommandBuffer =3D nullptr; + } + + // Get the actual swapchain image count + if ((result =3D m_device->getSwapchainImagesKHR(m_swapchain, &imageCou= nt, nullptr)) !=3D VK_SUCCESS) { + qCCritical(KWIN_VULKAN) << "vkGetSwapchainImagesKHR returned" << e= numToString(result); + return result; + } + + // Get the swapchain images + std::vector images(imageCount); + if ((result =3D m_device->getSwapchainImagesKHR(m_swapchain, &imageCou= nt, images.data())) !=3D VK_SUCCESS) { + qCCritical(KWIN_VULKAN) << "vkGetSwapchainImagesKHR returned" << e= numToString(result); + return result; + } + + m_buffers.resize(images.size()); + + // Create the image views and framebuffers + for (unsigned int i =3D 0; i < m_buffers.size(); i++) { + m_buffers[i].image =3D images[i]; + m_buffers[i].imageView =3D VK_NULL_HANDLE; + m_buffers[i].framebuffer =3D VK_NULL_HANDLE; + m_buffers[i].sequence =3D 0; + + const VkImageViewCreateInfo imageViewCreateInfo =3D { + .sType =3D VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .pNext =3D nullptr, + .flags =3D 0, + .image =3D images[i], + .viewType =3D VK_IMAGE_VIEW_TYPE_2D, + .format =3D m_format, + .components =3D { + .r =3D VK_COMPONENT_SWIZZLE_IDENTITY, + .g =3D VK_COMPONENT_SWIZZLE_IDENTITY, + .b =3D VK_COMPONENT_SWIZZLE_IDENTITY, + .a =3D VK_COMPONENT_SWIZZLE_IDENTITY + }, + .subresourceRange =3D { + .aspectMask =3D VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel =3D 0, + .levelCount =3D 1, + .baseArrayLayer =3D 0, + .layerCount =3D 1 + } + }; + + if ((result =3D m_device->createImageView(&imageViewCreateInfo, nu= llptr, &m_buffers[i].imageView)) !=3D VK_SUCCESS) { + qCCritical(KWIN_VULKAN) << "vkCreateImageView returned" << enu= mToString(result); + return result; + } + + const VkFramebufferCreateInfo framebufferCreateInfo =3D { + .sType =3D VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, + .pNext =3D nullptr, + .flags =3D 0, + .renderPass =3D m_renderPass, + .attachmentCount =3D 1, + .pAttachments =3D &m_buffers[i].imageView, + .width =3D m_extent.width, + .height =3D m_extent.height, + .layers =3D 1 + }; + + if ((result =3D m_device->createFramebuffer(&framebufferCreateInfo= , nullptr, &m_buffers[i].framebuffer)) !=3D VK_SUCCESS) { + qCCritical(KWIN_VULKAN) << "vkCreateFramebuffer returned" << e= numToString(result); + return result; + } + } + + m_damageHistory.clear(); + m_isValid =3D true; + m_sequence =3D 0; + m_index =3D -1; + + return VK_SUCCESS; +} + + +VkResult VulkanSwapchain::acquireNextImage(uint64_t timeout, VkSemaphore s= emaphore, VkFence fence, int32_t *imageIndex) +{ + ++m_sequence; + + if (m_index !=3D -1) + m_buffers[m_index].sequence =3D m_sequence; + + VkResult result =3D m_device->acquireNextImageKHR(m_swapchain, timeout= , semaphore, fence, (uint32_t *) &m_index); + + m_isValid =3D (result =3D=3D VK_SUCCESS || result =3D=3D VK_SUBOPTIMAL= _KHR); + + if (imageIndex && m_isValid) + *imageIndex =3D m_index; + + return result; +} + + +VkResult VulkanSwapchain::present(uint32_t imageIndex, const QRegion &upda= teRegion, VkSemaphore waitSemaphore) +{ + std::vector rects; + + if (m_supportsIncrementalPresent) { + rects.reserve(updateRegion.rectCount()); + + for (const QRect &r : updateRegion) { + rects.emplace_back(VkRectLayerKHR { + .offset =3D { r.x(), r.= y() }, + .extent =3D { (uint32_t) r.width(), (u= int32_t) r.height() }, + .layer =3D 0 + }); + + } + } + + const VkPresentRegionKHR region =3D { + .rectangleCount =3D (uint32_t) rects.size(), + .pRectangles =3D rects.data() + }; + + const VkPresentRegionsKHR regions =3D { + .sType =3D VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR, + .pNext =3D nullptr, + .swapchainCount =3D 1, + .pRegions =3D ®ion + }; + + const VkPresentInfoKHR presentInfo =3D { + .sType =3D VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, + .pNext =3D m_supportsIncrementalPresent ? ®ions : nullptr, + .waitSemaphoreCount =3D waitSemaphore ? 1u : 0u, + .pWaitSemaphores =3D &waitSemaphore, + .swapchainCount =3D 1, + .pSwapchains =3D &m_swapchain, + .pImageIndices =3D &imageIndex, + .pResults =3D nullptr + }; + + VkResult result =3D m_queue.presentKHR(&presentInfo); + + if (result !=3D VK_SUCCESS) + m_isValid =3D false; + + return result; +} + + +uint32_t VulkanSwapchain::bufferAge() const +{ + if (m_index !=3D -1 && m_buffers[m_index].sequence !=3D 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 <=3D (int) m_damageHistory.size()) { + auto it =3D m_damageHistory.cbegin(); + for (int i =3D 0; i < bufferAge - 1; i++) + region |=3D *it++; + } else { + const QSize &s =3D screens()->size(); + region =3D QRegion(0, 0, s.width(), s.height()); + } + + return region; +} + +} // namespace KWin diff --git a/plugins/scenes/vulkan/swapchain.h b/plugins/scenes/vulkan/swap= chain.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 =C2=A9 2017-2018 Fredrik H=C3=B6glund + +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 . +*********************************************************************/ + +#ifndef SWAPCHAIN_H +#define SWAPCHAIN_H + +#include "scene.h" + +namespace KWin +{ + +class VulkanSwapchain +{ + struct Buffer { + VkImage image; + VkImageView imageView; + VkFramebuffer framebuffer; + std::shared_ptr acquireOwnershipCommandBuffer; + std::shared_ptr 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_supportsIncremental= Present =3D value; } + + /** + * Sets the preferred number of images in the swap chain. + */ + void setPreferredImageCount(int imageCount) { m_preferredImageCount = =3D 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 =3D false; } + + /** + * Acquires a new image from the swap chain. + */ + VkResult acquireNextImage(uint64_t timeout, VkSemaphore semaphore, VkF= ence fence, int32_t *imageIndex); + + VkResult present(uint32_t imageIndex, const QRegion &updateRegion, VkS= emaphore 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].i= mageView; } + VkFramebuffer framebuffer(int32_t index) const { return m_buffers[inde= x].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 &acquireOwnershipCommandBuffer() = { return m_buffers[m_index].acquireOwnershipCommandBuffer; } + std::shared_ptr &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 fram= es. + */ + 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 m_buffers; + std::deque m_damageHistory; + int32_t m_index =3D -1; + uint64_t m_sequence =3D 0; + bool m_supportsIncrementalPresent =3D false; + bool m_isValid =3D false; +}; + +} // namespace KWin + +#endif // SWAPCHAIN_H diff --git a/plugins/scenes/vulkan/vulkan.json b/plugins/scenes/vulkan/vulk= an.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/windo= w.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 =C2=A9 2017-2018 Fredrik H=C3=B6glund + +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 . +*********************************************************************/ + +#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, WindowPaintD= ata 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 =C2=A9 2017-2018 Fredrik H=C3=B6glund + +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 . +*********************************************************************/ + +#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) over= ride; + + VulkanScene *scene() const { return m_scene; } + +protected: + virtual WindowPixmap *createWindowPixmap(); + +private: + VulkanPipelineManager *pipelineManager() { return scene()->pipelineMan= ager(); } + 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 =C2=A9 2017-2018 Fredrik H=C3=B6glund + +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 . +*********************************************************************/ + +#include "windowpixmap.h" + +namespace KWin +{ + + +VulkanWindowPixmap::VulkanWindowPixmap(Scene::Window *window, VulkanScene = *scene) + : WindowPixmap(window), + m_scene(scene) +{ +} + + +VulkanWindowPixmap::VulkanWindowPixmap(const QPointer &subSurface, + WindowPixmap *parent, + VulkanScene *scene) + : WindowPixmap(subSurface, parent), + m_scene(scene) +{ +} + + +VulkanWindowPixmap::~VulkanWindowPixmap() +{ +} + + +void VulkanWindowPixmap::create() +{ +} + + +void VulkanWindowPixmap::aboutToRender() +{ +} + + +WindowPixmap *VulkanWindowPixmap::createChild(const QPointer &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/w= indowpixmap.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 =C2=A9 2017-2018 Fredrik H=C3=B6glund + +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 . +*********************************************************************/ + +#ifndef WINDOWPIXMAP_H +#define WINDOWPIXMAP_H + +#include "../../../scene.h" + +#include +#include + +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 &subSurface, + WindowPixmap *parent, + VulkanScene *scene); + + ~VulkanWindowPixmap() override final; + + void create() override final; + + VulkanScene *scene() const { return m_scene; } + + WindowPixmap *createChild(const QPointer &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, nei= ther 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"));