diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0d45a2ea43550342c957b2c987fbce155f99b3b0..aec39c09aa4d9b57ec7ea63e291357e9bf3c612f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -205,6 +205,12 @@ find_path(AVUTIL_INCLUDE_DIR libavutil/avutil.h
    ${LIBJAMI_CONTRIB_DIR}/build/ffmpeg/Build/win32/x64/include)
 include_directories(${AVUTIL_INCLUDE_DIR})
 
+find_package(Vulkan)
+if(Vulkan_FOUND)
+    add_definitions(-DHAS_VULKAN)
+    include_directories(${Vulkan_INCLUDE_DIR})
+endif()
+
 if(MSVC)
     set(WINDOWS_SYS_LIBS
         Shell32.lib
diff --git a/src/main.cpp b/src/main.cpp
index 9764b91bf212d506ce4a3e10ac08d8cbd7e26501..7a84dfb482b33961372f6bf63fcb33ad98322da3 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -26,6 +26,9 @@
 #include <QApplication>
 #include <QtWebEngineCore>
 #include <QtWebEngineQuick>
+#if defined(HAS_VULKAN)
+#include <QVulkanInstance>
+#endif
 
 #include <clocale>
 
@@ -39,7 +42,7 @@ parseInputArgument(int& argc, char* argv[], QList<char*> argsToParse)
      */
     int oldArgc = argc;
     argc += argsToParse.size();
-    char** newArgv = new char*[argc];
+    auto newArgv = new char*[argc];
     for (int i = 0; i < oldArgc; i++) {
         newArgv[i] = argv[i];
     }
@@ -92,14 +95,33 @@ main(int argc, char* argv[])
     QApplication::setHighDpiScaleFactorRoundingPolicy(
         Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
 
-#if defined(Q_OS_MACOS)
-    QQuickWindow::setGraphicsApi(QSGRendererInterface::MetalRhi);
-#endif
-
     auto newArgv = parseInputArgument(argc, argv, qtWebEngineChromiumFlags);
 
     MainApplication app(argc, newArgv);
 
+#if defined(Q_OS_MACOS)
+    QQuickWindow::setGraphicsApi(QSGRendererInterface::MetalRhi);
+#else
+    if (std::invoke([] {
+#if defined(HAS_VULKAN)
+            QVulkanInstance inst;
+            inst.setLayers({"VK_LAYER_KHRONOS_validation"});
+            return inst.create();
+#else
+            return false;
+#endif
+        })
+        && qgetenv("WAYLAND_DISPLAY").isEmpty()) {
+        // https://bugreports.qt.io/browse/QTBUG-99684 - Vulkan on
+        // Wayland is not really supported as window decorations are
+        // removed. So we need to re-implement this (custom controls)
+        // or wait for a future version
+        QQuickWindow::setGraphicsApi(QSGRendererInterface::VulkanRhi);
+    } else {
+        QQuickWindow::setGraphicsApi(QSGRendererInterface::Unknown);
+    }
+#endif
+
     // InstanceManager prevents multiple instances, and will handle
     // IPC termination requests to and from secondary instances, which
     // is used to gracefully terminate the app from an installer script
diff --git a/src/mainapplication.cpp b/src/mainapplication.cpp
index c45ea7ed8827d177eba5c5a49dd2339ccdf04c70..09a180e82942cfed8713da9beb746d239e808d72 100644
--- a/src/mainapplication.cpp
+++ b/src/mainapplication.cpp
@@ -37,6 +37,7 @@
 #include <QResource>
 #include <QTranslator>
 #include <QLibraryInfo>
+#include <QQuickWindow>
 
 #include <locale.h>
 #include <thread>
@@ -54,6 +55,25 @@
 #include <gnutls/gnutls.h>
 #endif
 
+static QString
+getRenderInterfaceString()
+{
+    using GAPI = QSGRendererInterface::GraphicsApi;
+    switch (QQuickWindow::graphicsApi()) {
+    case GAPI::Direct3D11Rhi:
+        return "Direct3D11Rhi";
+    case GAPI::MetalRhi:
+        return "MetalRhi";
+    case GAPI::OpenGLRhi:
+        return "OpenGLRhi";
+    case GAPI::VulkanRhi:
+        return "VulkanRhi";
+    default:
+        break;
+    }
+    return {};
+}
+
 void
 ScreenInfo::setCurrentFocusWindow(QWindow* window)
 {
@@ -325,6 +345,7 @@ MainApplication::initQmlLayer()
     engine_->rootContext()->setContextProperty("videoProvider", videoProvider);
 
     engine_->load(QUrl(QStringLiteral("qrc:/src/MainApplicationWindow.qml")));
+    qWarning().noquote() << "Main window loaded using" << getRenderInterfaceString();
 }
 
 void