summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/vkkhrdisplay
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/platforms/vkkhrdisplay')
-rw-r--r--src/plugins/platforms/vkkhrdisplay/CMakeLists.txt32
-rw-r--r--src/plugins/platforms/vkkhrdisplay/main.cpp29
-rw-r--r--src/plugins/platforms/vkkhrdisplay/qvkkhrdisplayintegration.cpp315
-rw-r--r--src/plugins/platforms/vkkhrdisplay/qvkkhrdisplayintegration.h52
-rw-r--r--src/plugins/platforms/vkkhrdisplay/qvkkhrdisplayvulkaninstance.cpp271
-rw-r--r--src/plugins/platforms/vkkhrdisplay/qvkkhrdisplayvulkaninstance.h70
-rw-r--r--src/plugins/platforms/vkkhrdisplay/vkkhrdisplay.json3
7 files changed, 772 insertions, 0 deletions
diff --git a/src/plugins/platforms/vkkhrdisplay/CMakeLists.txt b/src/plugins/platforms/vkkhrdisplay/CMakeLists.txt
new file mode 100644
index 0000000000..406487f1e9
--- /dev/null
+++ b/src/plugins/platforms/vkkhrdisplay/CMakeLists.txt
@@ -0,0 +1,32 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_find_package(WrapFreetype PROVIDED_TARGETS WrapFreetype::WrapFreetype)
+
+qt_internal_add_plugin(QVkKhrDisplayIntegrationPlugin
+ OUTPUT_NAME qvkkhrdisplay
+ PLUGIN_TYPE platforms
+ DEFAULT_IF "vkkhrdisplay" IN_LIST QT_QPA_PLATFORMS
+ SOURCES
+ main.cpp
+ qvkkhrdisplayintegration.cpp qvkkhrdisplayintegration.h
+ qvkkhrdisplayvulkaninstance.cpp qvkkhrdisplayvulkaninstance.h
+ DEFINES
+ QT_NO_FOREACH
+ LIBRARIES
+ Qt::Core
+ Qt::CorePrivate
+ Qt::FbSupportPrivate
+ Qt::Gui
+ Qt::GuiPrivate
+)
+
+qt_internal_extend_target(QVkKhrDisplayIntegrationPlugin CONDITION QT_FEATURE_freetype
+ LIBRARIES
+ WrapFreetype::WrapFreetype
+)
+
+qt_internal_extend_target(QVkKhrDisplayIntegrationPlugin CONDITION TARGET Qt::InputSupportPrivate
+ LIBRARIES
+ Qt::InputSupportPrivate
+)
diff --git a/src/plugins/platforms/vkkhrdisplay/main.cpp b/src/plugins/platforms/vkkhrdisplay/main.cpp
new file mode 100644
index 0000000000..aa2dc3abf5
--- /dev/null
+++ b/src/plugins/platforms/vkkhrdisplay/main.cpp
@@ -0,0 +1,29 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <qpa/qplatformintegrationplugin.h>
+#include "qvkkhrdisplayintegration.h"
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+class QVkKhrDisplayIntegrationPlugin : public QPlatformIntegrationPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QPlatformIntegrationFactoryInterface_iid FILE "vkkhrdisplay.json")
+public:
+ QPlatformIntegration *create(const QString&, const QStringList&) override;
+};
+
+QPlatformIntegration *QVkKhrDisplayIntegrationPlugin::create(const QString &system, const QStringList &paramList)
+{
+ if (!system.compare("vkkhrdisplay"_L1, Qt::CaseInsensitive))
+ return new QVkKhrDisplayIntegration(paramList);
+
+ return nullptr;
+}
+
+QT_END_NAMESPACE
+
+#include "main.moc"
diff --git a/src/plugins/platforms/vkkhrdisplay/qvkkhrdisplayintegration.cpp b/src/plugins/platforms/vkkhrdisplay/qvkkhrdisplayintegration.cpp
new file mode 100644
index 0000000000..502c2518f2
--- /dev/null
+++ b/src/plugins/platforms/vkkhrdisplay/qvkkhrdisplayintegration.cpp
@@ -0,0 +1,315 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qvkkhrdisplayintegration.h"
+#include "qvkkhrdisplayvulkaninstance.h"
+
+#include <qpa/qplatformwindow.h>
+#include <qpa/qplatformbackingstore.h>
+#include <qpa/qplatforminputcontextfactory_p.h>
+#include <qpa/qwindowsysteminterface.h>
+
+#include <QtGui/private/qguiapplication_p.h>
+#include <QtGui/private/qwindow_p.h>
+#include <QtGui/private/qgenericunixeventdispatcher_p.h>
+#include <QtGui/private/qgenericunixfontdatabase_p.h>
+#include <QtGui/private/qgenericunixthemes_p.h>
+#include <QtGui/private/qgenericunixservices_p.h>
+
+#include <QtFbSupport/private/qfbvthandler_p.h>
+
+#if QT_CONFIG(libinput)
+#include <QtInputSupport/private/qlibinputhandler_p.h>
+#endif
+
+#if QT_CONFIG(evdev)
+#include <QtInputSupport/private/qevdevmousemanager_p.h>
+#include <QtInputSupport/private/qevdevkeyboardmanager_p.h>
+#include <QtInputSupport/private/qevdevtouchmanager_p.h>
+#endif
+
+#if QT_CONFIG(tslib)
+#include <QtInputSupport/private/qtslib_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+class QVkKhrDisplayScreen : public QPlatformScreen
+{
+public:
+ QRect geometry() const override { return m_geometry; }
+ int depth() const override { return m_depth; }
+ QImage::Format format() const override { return m_format; }
+ void setVk(QVkKhrDisplayVulkanInstance *inst);
+
+private:
+ QVkKhrDisplayVulkanInstance *m_vk = nullptr;
+ QRect m_geometry;
+ int m_depth = 32;
+ QImage::Format m_format = QImage::Format_ARGB32_Premultiplied;
+ friend class QVkKhrDisplayIntegration;
+};
+
+void QVkKhrDisplayScreen::setVk(QVkKhrDisplayVulkanInstance *inst)
+{
+ m_vk = inst;
+ m_geometry = QRect(QPoint(0, 0), m_vk->displaySize());
+ QWindowSystemInterface::handleScreenGeometryChange(screen(), m_geometry, m_geometry);
+ qDebug() << "Screen will report geometry" << m_geometry;
+
+ // Thanks to this deferred screen setup, a QWindow with a size based on the
+ // dummy screen size may already exist. Try to resize it.
+ QScreen *thisScreen = screen();
+ for (QWindow *window : QGuiApplication::allWindows()) {
+ if (window->isTopLevel() && window->screen() == thisScreen)
+ window->handle()->setGeometry(QRect()); // set fullscreen geometry
+ }
+}
+
+class QVkKhrDisplayWindow : public QPlatformWindow
+{
+public:
+ QVkKhrDisplayWindow(QWindow *window) : QPlatformWindow(window) { }
+ ~QVkKhrDisplayWindow();
+
+ void *vulkanSurfacePtr();
+
+ void setGeometry(const QRect &r) override;
+
+private:
+ VkSurfaceKHR m_surface = VK_NULL_HANDLE;
+};
+
+QVkKhrDisplayWindow::~QVkKhrDisplayWindow()
+{
+ if (m_surface) {
+ QVulkanInstance *inst = window()->vulkanInstance();
+ if (inst)
+ static_cast<QVkKhrDisplayVulkanInstance *>(inst->handle())->destroySurface(m_surface);
+ }
+}
+
+void *QVkKhrDisplayWindow::vulkanSurfacePtr()
+{
+ if (m_surface)
+ return &m_surface;
+
+ QVulkanInstance *inst = window()->vulkanInstance();
+ if (!inst) {
+ qWarning("Attempted to create Vulkan surface without an instance; was QWindow::setVulkanInstance() called?");
+ return nullptr;
+ }
+ QVkKhrDisplayVulkanInstance *vkdinst = static_cast<QVkKhrDisplayVulkanInstance *>(inst->handle());
+ m_surface = vkdinst->createSurface(window());
+
+ return &m_surface;
+}
+
+void QVkKhrDisplayWindow::setGeometry(const QRect &)
+{
+ // We only support full-screen windows
+ QRect rect(screen()->availableGeometry());
+ QWindowSystemInterface::handleGeometryChange(window(), rect);
+ QPlatformWindow::setGeometry(rect);
+
+ const QRect lastReportedGeometry = qt_window_private(window())->geometry;
+ if (rect != lastReportedGeometry)
+ QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), rect.size()));
+}
+
+// does not actually support raster content, just paint into a QImage and that's it for now
+class QVkKhrDisplayBackingStore : public QPlatformBackingStore
+{
+public:
+ QVkKhrDisplayBackingStore(QWindow *window) : QPlatformBackingStore(window) { }
+
+ QPaintDevice *paintDevice() override { return &m_image; }
+ void flush(QWindow *window, const QRegion &region, const QPoint &offset) override {
+ Q_UNUSED(window);
+ Q_UNUSED(region);
+ Q_UNUSED(offset);
+ }
+ void resize(const QSize &size, const QRegion &staticContents) override {
+ Q_UNUSED(staticContents);
+ QImage::Format format = QGuiApplication::primaryScreen()->handle()->format();
+ if (m_image.size() != size)
+ m_image = QImage(size, format);
+ }
+
+private:
+ QImage m_image;
+};
+
+QVkKhrDisplayIntegration::QVkKhrDisplayIntegration(const QStringList &parameters)
+{
+ Q_UNUSED(parameters);
+}
+
+QVkKhrDisplayIntegration::~QVkKhrDisplayIntegration()
+{
+ QWindowSystemInterface::handleScreenRemoved(m_primaryScreen);
+ delete m_services;
+ delete m_fontDatabase;
+ delete m_vtHandler;
+}
+
+bool QVkKhrDisplayIntegration::hasCapability(QPlatformIntegration::Capability cap) const
+{
+ switch (cap) {
+ case ThreadedPixmaps: return true;
+ case WindowManagement: return false;
+ default: return QPlatformIntegration::hasCapability(cap);
+ }
+}
+
+void QVkKhrDisplayIntegration::initialize()
+{
+ m_primaryScreen = new QVkKhrDisplayScreen;
+
+ // The real values are only known when the QVulkanInstance initializes, use
+ // dummy values until then.
+ m_primaryScreen->m_geometry = QRect(0, 0, 1920, 1080);
+ m_primaryScreen->m_depth = 32;
+ m_primaryScreen->m_format = QImage::Format_ARGB32_Premultiplied;
+
+ QWindowSystemInterface::handleScreenAdded(m_primaryScreen);
+
+ m_inputContext = QPlatformInputContextFactory::create();
+
+ m_vtHandler = new QFbVtHandler;
+
+ if (!qEnvironmentVariableIntValue("QT_QPA_DISABLE_INPUT"))
+ createInputHandlers();
+}
+
+QPlatformFontDatabase *QVkKhrDisplayIntegration::fontDatabase() const
+{
+ if (!m_fontDatabase)
+ m_fontDatabase = new QGenericUnixFontDatabase;
+
+ return m_fontDatabase;
+}
+
+QPlatformServices *QVkKhrDisplayIntegration::services() const
+{
+ if (!m_services)
+ m_services = new QGenericUnixServices;
+
+ return m_services;
+}
+
+QPlatformInputContext *QVkKhrDisplayIntegration::inputContext() const
+{
+ return m_inputContext;
+}
+
+QPlatformTheme *QVkKhrDisplayIntegration::createPlatformTheme(const QString &name) const
+{
+ return QGenericUnixTheme::createUnixTheme(name);
+}
+
+QPlatformNativeInterface *QVkKhrDisplayIntegration::nativeInterface() const
+{
+ return const_cast<QVkKhrDisplayIntegration *>(this);
+}
+
+QPlatformWindow *QVkKhrDisplayIntegration::createPlatformWindow(QWindow *window) const
+{
+ if (window->surfaceType() != QSurface::VulkanSurface) {
+ qWarning("vkkhrdisplay platform plugin only supports QWindow with surfaceType == VulkanSurface");
+ // Assume VulkanSurface, better than crashing. Consider e.g. an autotest
+ // creating a default QWindow just to have something to be used with
+ // QRhi's Null backend. Continuing to set up a Vulkan window (even
+ // though the request was Raster or something) is better than failing to
+ // create a platform window, and may even be sufficient in some cases.
+ }
+
+ QVkKhrDisplayWindow *w = new QVkKhrDisplayWindow(window);
+ w->setGeometry(QRect()); // set fullscreen geometry
+ w->requestActivateWindow();
+ return w;
+}
+
+QPlatformBackingStore *QVkKhrDisplayIntegration::createPlatformBackingStore(QWindow *window) const
+{
+ return new QVkKhrDisplayBackingStore(window);
+}
+
+QAbstractEventDispatcher *QVkKhrDisplayIntegration::createEventDispatcher() const
+{
+ return createUnixEventDispatcher();
+}
+
+void QVkKhrDisplayIntegration::handleInstanceCreated(QVkKhrDisplayVulkanInstance *inst, void *userData)
+{
+ QVkKhrDisplayIntegration *self = static_cast<QVkKhrDisplayIntegration *>(userData);
+ self->m_primaryScreen->setVk(inst);
+}
+
+QPlatformVulkanInstance *QVkKhrDisplayIntegration::createPlatformVulkanInstance(QVulkanInstance *instance) const
+{
+ QVkKhrDisplayVulkanInstance *inst = new QVkKhrDisplayVulkanInstance(instance);
+ inst->setCreatedCallback(handleInstanceCreated, const_cast<QVkKhrDisplayIntegration *>(this));
+ return inst;
+}
+
+enum ResourceType {
+ VkSurface
+};
+
+static int resourceType(const QByteArray &key)
+{
+ static const QByteArray names[] = { // match ResourceType
+ QByteArrayLiteral("vksurface")
+ };
+ const QByteArray *end = names + sizeof(names) / sizeof(names[0]);
+ const QByteArray *result = std::find(names, end, key);
+ if (result == end)
+ result = std::find(names, end, key.toLower());
+ return int(result - names);
+}
+
+void *QVkKhrDisplayIntegration::nativeResourceForWindow(const QByteArray &resource, QWindow *window)
+{
+ void *result = nullptr;
+
+ switch (resourceType(resource)) {
+ case VkSurface:
+ if (window && window->handle() && window->surfaceType() == QSurface::VulkanSurface)
+ result = static_cast<QVkKhrDisplayWindow *>(window->handle())->vulkanSurfacePtr();
+ break;
+ default:
+ break;
+ }
+
+ return result;
+}
+
+void QVkKhrDisplayIntegration::createInputHandlers()
+{
+#if QT_CONFIG(libinput)
+ if (!qEnvironmentVariableIntValue("QT_QPA_NO_LIBINPUT")) {
+ new QLibInputHandler("libinput"_L1, QString());
+ return;
+ }
+#endif
+
+#if QT_CONFIG(tslib)
+ bool useTslib = qEnvironmentVariableIntValue("QT_QPA_TSLIB");
+ if (useTslib)
+ new QTsLibMouseHandler("TsLib"_L1, QString());
+#endif
+
+#if QT_CONFIG(evdev)
+ new QEvdevKeyboardManager("EvdevKeyboard"_L1, QString(), this);
+ new QEvdevMouseManager("EvdevMouse"_L1, QString(), this);
+#if QT_CONFIG(tslib)
+ if (!useTslib)
+#endif
+ new QEvdevTouchManager("EvdevTouch"_L1, QString() /* spec */, this);
+#endif
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/vkkhrdisplay/qvkkhrdisplayintegration.h b/src/plugins/platforms/vkkhrdisplay/qvkkhrdisplayintegration.h
new file mode 100644
index 0000000000..a80b831226
--- /dev/null
+++ b/src/plugins/platforms/vkkhrdisplay/qvkkhrdisplayintegration.h
@@ -0,0 +1,52 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QPLATFORMINTEGRATION_VKKHRDISPLAY_H
+#define QPLATFORMINTEGRATION_VKKHRDISPLAY_H
+
+#include <qpa/qplatformintegration.h>
+#include <qpa/qplatformnativeinterface.h>
+#include <qpa/qplatformscreen.h>
+
+QT_BEGIN_NAMESPACE
+
+class QVkKhrDisplayScreen;
+class QVkKhrDisplayVulkanInstance;
+class QFbVtHandler;
+
+class QVkKhrDisplayIntegration : public QPlatformIntegration, public QPlatformNativeInterface
+{
+public:
+ explicit QVkKhrDisplayIntegration(const QStringList &parameters);
+ ~QVkKhrDisplayIntegration();
+
+ void initialize() override;
+
+ bool hasCapability(QPlatformIntegration::Capability cap) const override;
+ QPlatformFontDatabase *fontDatabase() const override;
+ QPlatformServices *services() const override;
+ QPlatformInputContext *inputContext() const override;
+ QPlatformTheme *createPlatformTheme(const QString &name) const override;
+ QPlatformNativeInterface *nativeInterface() const override;
+
+ QPlatformWindow *createPlatformWindow(QWindow *window) const override;
+ QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const override;
+ QAbstractEventDispatcher *createEventDispatcher() const override;
+ QPlatformVulkanInstance *createPlatformVulkanInstance(QVulkanInstance *instance) const override;
+
+ void *nativeResourceForWindow(const QByteArray &resource, QWindow *window) override;
+
+private:
+ static void handleInstanceCreated(QVkKhrDisplayVulkanInstance *, void *);
+ void createInputHandlers();
+
+ mutable QPlatformFontDatabase *m_fontDatabase = nullptr;
+ mutable QPlatformServices *m_services = nullptr;
+ QPlatformInputContext *m_inputContext = nullptr;
+ QFbVtHandler *m_vtHandler = nullptr;
+ QVkKhrDisplayScreen *m_primaryScreen = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/platforms/vkkhrdisplay/qvkkhrdisplayvulkaninstance.cpp b/src/plugins/platforms/vkkhrdisplay/qvkkhrdisplayvulkaninstance.cpp
new file mode 100644
index 0000000000..2e8d60209e
--- /dev/null
+++ b/src/plugins/platforms/vkkhrdisplay/qvkkhrdisplayvulkaninstance.cpp
@@ -0,0 +1,271 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qvkkhrdisplayvulkaninstance.h"
+#include <QVarLengthArray>
+
+QT_BEGIN_NAMESPACE
+
+QVkKhrDisplayVulkanInstance::QVkKhrDisplayVulkanInstance(QVulkanInstance *instance)
+ : m_instance(instance)
+{
+ loadVulkanLibrary(QStringLiteral("vulkan"), 1);
+}
+
+void QVkKhrDisplayVulkanInstance::createOrAdoptInstance()
+{
+ qDebug("Creating Vulkan instance for VK_KHR_display");
+
+ const QByteArray extName = QByteArrayLiteral("VK_KHR_display");
+ initInstance(m_instance, { extName });
+ if (!m_vkInst)
+ return;
+
+ if (!enabledExtensions().contains(extName)) {
+ qWarning("Failed to enable VK_KHR_display extension");
+ return;
+ }
+
+#if VK_KHR_display
+ m_getPhysicalDeviceDisplayPropertiesKHR = (PFN_vkGetPhysicalDeviceDisplayPropertiesKHR)
+ m_vkGetInstanceProcAddr(m_vkInst, "vkGetPhysicalDeviceDisplayPropertiesKHR");
+ m_getDisplayModePropertiesKHR = (PFN_vkGetDisplayModePropertiesKHR)
+ m_vkGetInstanceProcAddr(m_vkInst, "vkGetDisplayModePropertiesKHR");
+ m_getPhysicalDeviceDisplayPlanePropertiesKHR = (PFN_vkGetPhysicalDeviceDisplayPlanePropertiesKHR)
+ m_vkGetInstanceProcAddr(m_vkInst, "vkGetPhysicalDeviceDisplayPlanePropertiesKHR");
+
+ m_getDisplayPlaneSupportedDisplaysKHR = (PFN_vkGetDisplayPlaneSupportedDisplaysKHR)
+ m_vkGetInstanceProcAddr(m_vkInst, "vkGetDisplayPlaneSupportedDisplaysKHR");
+ m_getDisplayPlaneCapabilitiesKHR = (PFN_vkGetDisplayPlaneCapabilitiesKHR)
+ m_vkGetInstanceProcAddr(m_vkInst, "vkGetDisplayPlaneCapabilitiesKHR");
+
+ m_createDisplayPlaneSurfaceKHR = (PFN_vkCreateDisplayPlaneSurfaceKHR)
+ m_vkGetInstanceProcAddr(m_vkInst, "vkCreateDisplayPlaneSurfaceKHR");
+#endif
+
+ m_enumeratePhysicalDevices = (PFN_vkEnumeratePhysicalDevices)
+ m_vkGetInstanceProcAddr(m_vkInst, "vkEnumeratePhysicalDevices");
+
+ m_getPhysicalDeviceSurfaceSupportKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceSupportKHR>(
+ m_vkGetInstanceProcAddr(m_vkInst, "vkGetPhysicalDeviceSurfaceSupportKHR"));
+
+ // Use for first physical device, unless overridden by QT_VK_PHYSICAL_DEVICE_INDEX.
+ // This behavior matches what the Vulkan backend of QRhi would do.
+
+ uint32_t physDevCount = 0;
+ m_enumeratePhysicalDevices(m_vkInst, &physDevCount, nullptr);
+ if (!physDevCount) {
+ qWarning("No physical devices");
+ return;
+ }
+ QVarLengthArray<VkPhysicalDevice, 4> physDevs(physDevCount);
+ VkResult err = m_enumeratePhysicalDevices(m_vkInst, &physDevCount, physDevs.data());
+ if (err != VK_SUCCESS || !physDevCount) {
+ qWarning("Failed to enumerate physical devices: %d", err);
+ return;
+ }
+
+ if (qEnvironmentVariableIsSet("QT_VK_PHYSICAL_DEVICE_INDEX")) {
+ int requestedPhysDevIndex = qEnvironmentVariableIntValue("QT_VK_PHYSICAL_DEVICE_INDEX");
+ if (requestedPhysDevIndex >= 0 && uint32_t(requestedPhysDevIndex) < physDevCount)
+ m_physDev = physDevs[requestedPhysDevIndex];
+ }
+
+ if (m_physDev == VK_NULL_HANDLE)
+ m_physDev = physDevs[0];
+
+ if (chooseDisplay()) {
+ if (m_createdCallback)
+ m_createdCallback(this, m_createdCallbackUserData);
+ }
+}
+
+bool QVkKhrDisplayVulkanInstance::supportsPresent(VkPhysicalDevice physicalDevice,
+ uint32_t queueFamilyIndex,
+ QWindow *window)
+{
+ if (!m_getPhysicalDeviceSurfaceSupportKHR)
+ return true;
+
+ VkSurfaceKHR surface = QVulkanInstance::surfaceForWindow(window);
+ VkBool32 supported = false;
+ m_getPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex, surface, &supported);
+
+ return supported;
+}
+
+bool QVkKhrDisplayVulkanInstance::chooseDisplay()
+{
+#if VK_KHR_display
+ uint32_t displayCount = 0;
+ VkResult err = m_getPhysicalDeviceDisplayPropertiesKHR(m_physDev, &displayCount, nullptr);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to get display properties: %d", err);
+ return false;
+ }
+
+ qDebug("Display count: %u", displayCount);
+
+ QVarLengthArray<VkDisplayPropertiesKHR, 4> displayProps(displayCount);
+ m_getPhysicalDeviceDisplayPropertiesKHR(m_physDev, &displayCount, displayProps.data());
+
+ m_display = VK_NULL_HANDLE;
+ m_displayMode = VK_NULL_HANDLE;
+
+ // Pick the first display and the first mode, unless specified via env.vars.
+ uint32_t wantedDisplayIndex = 0;
+ uint32_t wantedModeIndex = 0;
+ if (qEnvironmentVariableIsSet("QT_VK_DISPLAY_INDEX"))
+ wantedDisplayIndex = uint32_t(qEnvironmentVariableIntValue("QT_VK_DISPLAY_INDEX"));
+ if (qEnvironmentVariableIsSet("QT_VK_MODE_INDEX"))
+ wantedModeIndex = uint32_t(qEnvironmentVariableIntValue("QT_VK_MODE_INDEX"));
+
+ for (uint32_t i = 0; i < displayCount; ++i) {
+ const VkDisplayPropertiesKHR &disp(displayProps[i]);
+ qDebug("Display #%u:\n display: %p\n name: %s\n dimensions: %ux%u\n resolution: %ux%u",
+ i, (void *) disp.display, disp.displayName,
+ disp.physicalDimensions.width, disp.physicalDimensions.height,
+ disp.physicalResolution.width, disp.physicalResolution.height);
+
+ if (i == wantedDisplayIndex)
+ m_display = disp.display;
+
+ uint32_t modeCount = 0;
+ if (m_getDisplayModePropertiesKHR(m_physDev, disp.display, &modeCount, nullptr) != VK_SUCCESS) {
+ qWarning("Failed to get modes for display");
+ continue;
+ }
+ QVarLengthArray<VkDisplayModePropertiesKHR, 16> modeProps(modeCount);
+ m_getDisplayModePropertiesKHR(m_physDev, disp.display, &modeCount, modeProps.data());
+ for (uint32_t j = 0; j < modeCount; ++j) {
+ const VkDisplayModePropertiesKHR &mode(modeProps[j]);
+ qDebug(" Mode #%u:\n mode: %p\n visibleRegion: %ux%u\n refreshRate: %u",
+ j, (void *) mode.displayMode,
+ mode.parameters.visibleRegion.width, mode.parameters.visibleRegion.height,
+ mode.parameters.refreshRate);
+ if (j == wantedModeIndex && i == wantedDisplayIndex) {
+ m_displayMode = mode.displayMode;
+ m_width = mode.parameters.visibleRegion.width;
+ m_height = mode.parameters.visibleRegion.height;
+ }
+ }
+ }
+
+ if (m_display == VK_NULL_HANDLE || m_displayMode == VK_NULL_HANDLE) {
+ qWarning("Failed to choose display and mode");
+ return false;
+ }
+
+ qDebug("Using display #%u with mode #%u", wantedDisplayIndex, wantedModeIndex);
+
+ uint32_t planeCount = 0;
+ err = m_getPhysicalDeviceDisplayPlanePropertiesKHR(m_physDev, &planeCount, nullptr);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to get plane properties: %d", err);
+ return false;
+ }
+
+ qDebug("Plane count: %u", planeCount);
+
+ QVarLengthArray<VkDisplayPlanePropertiesKHR, 4> planeProps(planeCount);
+ m_getPhysicalDeviceDisplayPlanePropertiesKHR(m_physDev, &planeCount, planeProps.data());
+
+ m_planeIndex = UINT_MAX;
+ for (uint32_t i = 0; i < planeCount; ++i) {
+ uint32_t supportedDisplayCount = 0;
+ err = m_getDisplayPlaneSupportedDisplaysKHR(m_physDev, i, &supportedDisplayCount, nullptr);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to query supported displays for plane: %d", err);
+ return false;
+ }
+
+ QVarLengthArray<VkDisplayKHR, 4> supportedDisplays(supportedDisplayCount);
+ m_getDisplayPlaneSupportedDisplaysKHR(m_physDev, i, &supportedDisplayCount, supportedDisplays.data());
+ qDebug("Plane #%u supports %u displays, currently bound to display %p",
+ i, supportedDisplayCount, (void *) planeProps[i].currentDisplay);
+
+ VkDisplayPlaneCapabilitiesKHR caps;
+ err = m_getDisplayPlaneCapabilitiesKHR(m_physDev, m_displayMode, i, &caps);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to query plane capabilities: %d", err);
+ return false;
+ }
+
+ qDebug(" supportedAlpha: %d (1=no, 2=global, 4=per pixel, 8=per pixel premul)\n"
+ " minSrc=%d, %d %ux%u\n"
+ " maxSrc=%d, %d %ux%u\n"
+ " minDst=%d, %d %ux%u\n"
+ " maxDst=%d, %d %ux%u",
+ int(caps.supportedAlpha),
+ caps.minSrcPosition.x, caps.minSrcPosition.y, caps.minSrcExtent.width, caps.minSrcExtent.height,
+ caps.maxSrcPosition.x, caps.maxSrcPosition.y, caps.maxSrcExtent.width, caps.maxSrcExtent.height,
+ caps.minDstPosition.x, caps.minDstPosition.y, caps.minDstExtent.width, caps.minDstExtent.height,
+ caps.maxDstPosition.x, caps.maxDstPosition.y, caps.maxDstExtent.width, caps.maxDstExtent.height);
+
+ // if the plane is not in use and supports our chosen display, use that plane
+ if (supportedDisplays.contains(m_display)
+ && (planeProps[i].currentDisplay == VK_NULL_HANDLE || planeProps[i].currentDisplay == m_display))
+ {
+ m_planeIndex = i;
+ m_planeStackIndex = planeProps[i].currentStackIndex;
+ }
+ }
+
+ if (m_planeIndex == UINT_MAX) {
+ qWarning("Failed to find a suitable plane");
+ return false;
+ }
+
+ qDebug("Using plane #%u", m_planeIndex);
+ return true;
+#else
+ return false;
+#endif
+}
+
+VkSurfaceKHR QVkKhrDisplayVulkanInstance::createSurface(QWindow *window)
+{
+#if VK_KHR_display
+ qDebug("Creating VkSurfaceKHR via VK_KHR_display for window %p", (void *) window);
+
+ if (!m_physDev) {
+ qWarning("No physical device, cannot create surface");
+ return VK_NULL_HANDLE;
+ }
+ if (!m_display || !m_displayMode) {
+ qWarning("No display mode chosen, cannot create surface");
+ return VK_NULL_HANDLE;
+ }
+
+ VkDisplaySurfaceCreateInfoKHR surfaceCreateInfo = {};
+ surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR;
+ surfaceCreateInfo.displayMode = m_displayMode;
+ surfaceCreateInfo.planeIndex = m_planeIndex;
+ surfaceCreateInfo.planeStackIndex = m_planeStackIndex;
+ surfaceCreateInfo.transform = VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR;
+ surfaceCreateInfo.globalAlpha = 1.0f;
+ surfaceCreateInfo.alphaMode = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR;
+ surfaceCreateInfo.imageExtent = { m_width, m_height };
+
+ VkSurfaceKHR surface = VK_NULL_HANDLE;
+ VkResult err = m_createDisplayPlaneSurfaceKHR(m_vkInst, &surfaceCreateInfo, nullptr, &surface);
+ if (err != VK_SUCCESS || surface == VK_NULL_HANDLE) {
+ qWarning("Failed to create surface: %d", err);
+ return VK_NULL_HANDLE;
+ }
+
+ qDebug("Created surface %p", (void *) surface);
+
+ return surface;
+#else
+ Q_UNUSED(window);
+ return VK_NULL_HANDLE;
+#endif
+}
+
+void QVkKhrDisplayVulkanInstance::presentAboutToBeQueued(QWindow *window)
+{
+ Q_UNUSED(window);
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/vkkhrdisplay/qvkkhrdisplayvulkaninstance.h b/src/plugins/platforms/vkkhrdisplay/qvkkhrdisplayvulkaninstance.h
new file mode 100644
index 0000000000..bf99dc037f
--- /dev/null
+++ b/src/plugins/platforms/vkkhrdisplay/qvkkhrdisplayvulkaninstance.h
@@ -0,0 +1,70 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QVKKHRDISPLAYVULKANINSTANCE_H
+#define QVKKHRDISPLAYVULKANINSTANCE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/private/qbasicvulkanplatforminstance_p.h>
+#include <QtCore/qsize.h>
+
+QT_BEGIN_NAMESPACE
+
+class QVkKhrDisplayVulkanInstance : public QBasicPlatformVulkanInstance
+{
+public:
+ QVkKhrDisplayVulkanInstance(QVulkanInstance *instance);
+
+ void createOrAdoptInstance() override;
+ bool supportsPresent(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, QWindow *window) override;
+ void presentAboutToBeQueued(QWindow *window) override;
+
+ VkSurfaceKHR createSurface(QWindow *window);
+
+ QSize displaySize() const { return QSize(int(m_width), int(m_height)); }
+
+ using CreatedCallback = void (*)(QVkKhrDisplayVulkanInstance *, void *);
+ void setCreatedCallback(CreatedCallback callback, void *userData) {
+ m_createdCallback = callback;
+ m_createdCallbackUserData = userData;
+ }
+
+private:
+ bool chooseDisplay();
+
+ QVulkanInstance *m_instance;
+ VkPhysicalDevice m_physDev = VK_NULL_HANDLE;
+ PFN_vkEnumeratePhysicalDevices m_enumeratePhysicalDevices = nullptr;
+ PFN_vkGetPhysicalDeviceSurfaceSupportKHR m_getPhysicalDeviceSurfaceSupportKHR = nullptr;
+#if VK_KHR_display
+ PFN_vkGetPhysicalDeviceDisplayPropertiesKHR m_getPhysicalDeviceDisplayPropertiesKHR = nullptr;
+ PFN_vkGetDisplayModePropertiesKHR m_getDisplayModePropertiesKHR = nullptr;
+ PFN_vkGetPhysicalDeviceDisplayPlanePropertiesKHR m_getPhysicalDeviceDisplayPlanePropertiesKHR = nullptr;
+ PFN_vkGetDisplayPlaneSupportedDisplaysKHR m_getDisplayPlaneSupportedDisplaysKHR = nullptr;
+ PFN_vkGetDisplayPlaneCapabilitiesKHR m_getDisplayPlaneCapabilitiesKHR = nullptr;
+ PFN_vkCreateDisplayPlaneSurfaceKHR m_createDisplayPlaneSurfaceKHR = nullptr;
+#endif
+
+ CreatedCallback m_createdCallback = nullptr;
+ void *m_createdCallbackUserData = nullptr;
+ VkDisplayKHR m_display = VK_NULL_HANDLE;
+ VkDisplayModeKHR m_displayMode = VK_NULL_HANDLE;
+ uint32_t m_width = 0;
+ uint32_t m_height = 0;
+ uint32_t m_planeIndex = UINT_MAX;
+ uint32_t m_planeStackIndex = UINT_MAX;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/platforms/vkkhrdisplay/vkkhrdisplay.json b/src/plugins/platforms/vkkhrdisplay/vkkhrdisplay.json
new file mode 100644
index 0000000000..dd7a77eb68
--- /dev/null
+++ b/src/plugins/platforms/vkkhrdisplay/vkkhrdisplay.json
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "vkkhrdisplay" ]
+}