diff options
Diffstat (limited to 'src/plugins/platforms/vkkhrdisplay')
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 ¶mList) +{ + 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 ®ion, 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 ¶meters) +{ + 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 ¶meters); + ~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" ] +} |