Long live VK_KHR_display platform plugin!

Experiment with this once again, this time in a more forward looking
manner: move the code previously placed into eglfs's eglfs_viv backend
into its own plugin.

Move our attention to devices like the Raspberry Pi 4, where
VK_KHR_display has recently been introduced to the Mesa v3dv
backend. This is not in Mesa 20.3.3, the latest release at the time of
writing, but is available and functional when building master. This
serves as the reference system for testing the plugin, because it
looks like a fairly robust implementation.

The sole thing the plugin enables at the moment is creating a
QVulkanInstance and a QWindow with surfaceType VulkanSurface. This is
sufficient to run plain QWindow+QRhi (with QRhi::Vulkan), Qt Quick,
and Qt Quick 3D (with QSG_RHI_BACKEND=vulkan) applications.

One display and mode is chosen, by default the first in the
enumeration lists reported by the Vulkan extension. This can be
overridden with QT_VK_DISPLAY_INDEX and QT_VK_MODE_INDEX (modeled
after QT_VK_PHYSICAL_DEVICE_INDEX). The indices can be determined
based on the logs printed to the debug output. Changing the mode seems
to be working nicely with v3dv.

Multiple screen setups, where there would be more than one
VkDisplayKHR enumerated, have not been tested yet. Regardless,
multiple screens (reporting more than one QScreen, with a different
QWindow on each, eglfs style) are not currently supported. This may be
improved later (while keeping in mind that VK_KHR_display does not
have a fully-featured output management API).

Multiple (non-fullscreen) windows and especially raster windows
(QWidget) are not and will not be supported. Our single QWindow is
always forced to fullscreen.

When it comes to input, the level of support should match linuxfb and
eglfs. Note that while mouse input is fully functional, there is no
mouse cursor. (and this is unlikely to be implemented)

[ChangeLog][Platform Specific Changes][Embedded Linux] Introduced a
vkkhrdisplay platform plugin to run Vulkan-based applications in
fullscreen, without a windowing system, on systems where
VK_KHR_display and VK_KHR_display_swapchain are supported by the
Vulkan implementation.

Change-Id: I6388416f7fb2bfdc4b412a0a4971f25cc05d4668
Reviewed-by: Andy Nichols <andy.nichols@qt.io>
This commit is contained in:
Laszlo Agocs 2021-02-06 18:47:41 +01:00
parent d8c771c53a
commit faebfdea1b
17 changed files with 550 additions and 189 deletions

View File

@ -769,6 +769,11 @@ qt_feature("vulkan" PUBLIC
LABEL "Vulkan"
CONDITION QT_FEATURE_vkgen AND Vulkan_FOUND
)
qt_feature("vkkhrdisplay" PRIVATE
SECTION "Platform plugins"
LABEL "VK_KHR_display"
CONDITION NOT ANDROID AND NOT APPLE AND NOT WIN32 AND NOT WASM AND QT_FEATURE_vulkan
)
qt_feature("openvg" PUBLIC
LABEL "OpenVG"
CONDITION libs.openvg OR FIXME
@ -1256,6 +1261,7 @@ qt_configure_add_summary_entry(ARGS "eglfs_x11")
qt_configure_end_summary_section() # end of "EGLFS details" section
qt_configure_add_summary_entry(ARGS "linuxfb")
qt_configure_add_summary_entry(ARGS "vnc")
qt_configure_add_summary_entry(ARGS "vkkhrdisplay")
qt_configure_add_summary_entry(
ARGS "integrityfb"
CONDITION INTEGRITY

View File

@ -52,3 +52,6 @@ endif()
if(QT_FEATURE_integrityfb)
# add_subdirectory(integrity) # special case TODO
endif()
if(QT_FEATURE_vkkhrdisplay)
add_subdirectory(vkkhrdisplay)
endif()

View File

@ -90,11 +90,6 @@ if(QT_FEATURE_cursor)
)
endif()
qt_internal_extend_target(EglFSDeviceIntegration CONDITION QT_FEATURE_vulkan
SOURCES
api/vulkan/qeglfsvulkaninstance.cpp api/vulkan/qeglfsvulkaninstance_p.h
api/vulkan/qeglfsvulkanwindow.cpp api/vulkan/qeglfsvulkanwindow_p.h
)
#####################################################################
## QEglFSIntegrationPlugin Plugin:
#####################################################################

View File

@ -376,14 +376,6 @@ void *QEglFSDeviceIntegration::wlDisplay() const
return nullptr;
}
#if QT_CONFIG(vulkan)
QPlatformVulkanInstance *QEglFSDeviceIntegration::createPlatformVulkanInstance(QVulkanInstance *instance)
{
Q_UNUSED(instance);
return nullptr;
}
#endif
EGLConfig QEglFSDeviceIntegration::chooseConfig(EGLDisplay display, const QSurfaceFormat &format)
{
class Chooser : public QEglConfigChooser {

View File

@ -108,10 +108,6 @@ public:
virtual void *nativeResourceForScreen(const QByteArray &resource, QScreen *screen);
virtual void *wlDisplay() const;
#if QT_CONFIG(vulkan)
virtual QPlatformVulkanInstance *createPlatformVulkanInstance(QVulkanInstance *instance);
#endif
static EGLConfig chooseConfig(EGLDisplay display, const QSurfaceFormat &format);
};

View File

@ -241,13 +241,6 @@ QPlatformOffscreenSurface *QEglFSIntegration::createPlatformOffscreenSurface(QOf
}
#endif // QT_NO_OPENGL
#if QT_CONFIG(vulkan)
QPlatformVulkanInstance *QEglFSIntegration::createPlatformVulkanInstance(QVulkanInstance *instance) const
{
return qt_egl_device_integration()->createPlatformVulkanInstance(instance);
}
#endif
bool QEglFSIntegration::hasCapability(QPlatformIntegration::Capability cap) const
{
// We assume that devices will have more and not less capabilities
@ -367,12 +360,6 @@ void *QEglFSIntegration::nativeResourceForWindow(const QByteArray &resource, QWi
if (window && window->handle())
result = reinterpret_cast<void*>(static_cast<QEglFSWindow *>(window->handle())->surface());
break;
#if QT_CONFIG(vulkan)
case VkSurface:
if (window && window->handle() && window->surfaceType() == QSurface::VulkanSurface)
result = static_cast<QEglFSWindow *>(window->handle())->vulkanSurfacePtr();
break;
#endif
default:
break;
}

View File

@ -96,9 +96,6 @@ public:
QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const override;
QOpenGLContext *createOpenGLContext(EGLContext context, EGLDisplay display, QOpenGLContext *shareContext) const override;
QPlatformOffscreenSurface *createPlatformOffscreenSurface(QOffscreenSurface *surface) const override;
#endif
#if QT_CONFIG(vulkan)
QPlatformVulkanInstance *createPlatformVulkanInstance(QVulkanInstance *instance) const override;
#endif
bool hasCapability(QPlatformIntegration::Capability cap) const override;

View File

@ -97,9 +97,6 @@ public:
EGLNativeWindowType eglWindow() const;
EGLSurface surface() const;
QEglFSScreen *screen() const override;
#if QT_CONFIG(vulkan)
virtual void *vulkanSurfacePtr() { return nullptr; }
#endif
bool hasNativeWindow() const { return m_flags.testFlag(HasNativeWindow); }

View File

@ -41,11 +41,6 @@
#include <EGL/eglvivante.h>
#include <QDebug>
#if QT_CONFIG(vulkan)
#include "private/qeglfsvulkaninstance_p.h"
#include "private/qeglfsvulkanwindow_p.h"
#endif
#ifdef Q_OS_INTEGRITY
extern "C" void VivanteInit(void);
#endif
@ -102,20 +97,4 @@ void QEglFSVivIntegration::destroyNativeWindow(EGLNativeWindowType window)
fbDestroyWindow(window);
}
#if QT_CONFIG(vulkan)
QEglFSWindow *QEglFSVivIntegration::createWindow(QWindow *window) const
{
if (window->surfaceType() == QSurface::VulkanSurface)
return new QEglFSVulkanWindow(window);
return QEglFSDeviceIntegration::createWindow(window);
}
QPlatformVulkanInstance *QEglFSVivIntegration::createPlatformVulkanInstance(QVulkanInstance *instance)
{
return new QEglFSVulkanInstance(instance);
}
#endif // vulkan
QT_END_NAMESPACE

View File

@ -53,12 +53,6 @@ public:
void destroyNativeWindow(EGLNativeWindowType window) override;
EGLNativeDisplayType platformDisplay() const override;
// Vulkan support with VK_KHR_display
#if QT_CONFIG(vulkan)
QEglFSWindow *createWindow(QWindow *window) const override;
QPlatformVulkanInstance *createPlatformVulkanInstance(QVulkanInstance *instance) override;
#endif
private:
QSize mScreenSize;
EGLNativeDisplayType mNativeDisplay;

View File

@ -0,0 +1,29 @@
qt_find_package(WrapFreetype PROVIDED_TARGETS WrapFreetype::WrapFreetype)
qt_internal_add_plugin(QVkKhrDisplayIntegrationPlugin
OUTPUT_NAME qvkkhrdisplay
TYPE platforms
DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES vkkhrdisplay
SOURCES
main.cpp
qvkkhrdisplayintegration.cpp qvkkhrdisplayintegration.h
qvkkhrdisplayvulkaninstance.cpp qvkkhrdisplayvulkaninstance.h
DEFINES
QT_NO_FOREACH
PUBLIC_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
PUBLIC_LIBRARIES
Qt::InputSupportPrivate
)

View File

@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
@ -37,38 +37,27 @@
**
****************************************************************************/
#ifndef QEGLFSVULKANWINDOW_H
#define QEGLFSVULKANWINDOW_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 "qeglfsglobal_p.h"
#include "qeglfswindow_p.h"
#include "qeglfsvulkaninstance_p.h"
#include <qpa/qplatformintegrationplugin.h>
#include "qvkkhrdisplayintegration.h"
QT_BEGIN_NAMESPACE
class Q_EGLFS_EXPORT QEglFSVulkanWindow : public QEglFSWindow
class QVkKhrDisplayIntegrationPlugin : public QPlatformIntegrationPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID QPlatformIntegrationFactoryInterface_iid FILE "vkkhrdisplay.json")
public:
QEglFSVulkanWindow(QWindow *window);
~QEglFSVulkanWindow();
void *vulkanSurfacePtr() override;
private:
VkSurfaceKHR m_surface;
QPlatformIntegration *create(const QString&, const QStringList&) override;
};
QPlatformIntegration *QVkKhrDisplayIntegrationPlugin::create(const QString &system, const QStringList &paramList)
{
if (!system.compare(QLatin1String("vkkhrdisplay"), Qt::CaseInsensitive))
return new QVkKhrDisplayIntegration(paramList);
return 0;
}
QT_END_NAMESPACE
#endif
#include "main.moc"

View File

@ -0,0 +1,345 @@
/****************************************************************************
**
** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#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
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");
return nullptr;
}
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(QLatin1String("libinput"), QString());
return;
}
#endif
#if QT_CONFIG(tslib)
bool useTslib = qEnvironmentVariableIntValue("QT_QPA_TSLIB");
if (useTslib)
new QTsLibMouseHandler(QLatin1String("TsLib"), QString());
#endif
#if QT_CONFIG(evdev)
new QEvdevKeyboardManager(QLatin1String("EvdevKeyboard"), QString(), this);
new QEvdevMouseManager(QLatin1String("EvdevMouse"), QString(), this);
#if QT_CONFIG(tslib)
if (!useTslib)
#endif
new QEvdevTouchManager(QLatin1String("EvdevTouch"), QString() /* spec */, this);
#endif
}
QT_END_NAMESPACE

View File

@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
@ -37,39 +37,52 @@
**
****************************************************************************/
#include "qeglfsvulkanwindow_p.h"
#ifndef QPLATFORMINTEGRATION_VKKHRDISPLAY_H
#define QPLATFORMINTEGRATION_VKKHRDISPLAY_H
#include <qpa/qplatformintegration.h>
#include <qpa/qplatformnativeinterface.h>
#include <qpa/qplatformscreen.h>
QT_BEGIN_NAMESPACE
QEglFSVulkanWindow::QEglFSVulkanWindow(QWindow *window)
: QEglFSWindow(window),
m_surface(VK_NULL_HANDLE)
class QVkKhrDisplayScreen;
class QVkKhrDisplayVulkanInstance;
class QFbVtHandler;
class QVkKhrDisplayIntegration : public QPlatformIntegration, public QPlatformNativeInterface
{
}
public:
explicit QVkKhrDisplayIntegration(const QStringList &parameters);
~QVkKhrDisplayIntegration();
QEglFSVulkanWindow::~QEglFSVulkanWindow()
{
if (m_surface) {
QVulkanInstance *inst = window()->vulkanInstance();
if (inst)
static_cast<QEglFSVulkanInstance *>(inst->handle())->destroySurface(m_surface);
}
}
void initialize() override;
void *QEglFSVulkanWindow::vulkanSurfacePtr()
{
if (m_surface)
return &m_surface;
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;
QVulkanInstance *inst = window()->vulkanInstance();
if (!inst) {
qWarning("Attempted to create Vulkan surface without an instance; was QWindow::setVulkanInstance() called?");
return nullptr;
}
QEglFSVulkanInstance *eglfsInst = static_cast<QEglFSVulkanInstance *>(inst->handle());
m_surface = eglfsInst->createSurface(this);
QPlatformWindow *createPlatformWindow(QWindow *window) const override;
QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const override;
QAbstractEventDispatcher *createEventDispatcher() const override;
QPlatformVulkanInstance *createPlatformVulkanInstance(QVulkanInstance *instance) const override;
return &m_surface;
}
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

View File

@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
@ -37,29 +37,25 @@
**
****************************************************************************/
#include "qeglfsvulkaninstance_p.h"
#include "qeglfswindow_p.h"
#include "qeglfshooks_p.h"
#include <QLoggingCategory>
#include "qvkkhrdisplayvulkaninstance.h"
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(qLcEglDevDebug)
QEglFSVulkanInstance::QEglFSVulkanInstance(QVulkanInstance *instance)
QVkKhrDisplayVulkanInstance::QVkKhrDisplayVulkanInstance(QVulkanInstance *instance)
: m_instance(instance)
{
loadVulkanLibrary(QStringLiteral("vulkan"));
}
void QEglFSVulkanInstance::createOrAdoptInstance()
void QVkKhrDisplayVulkanInstance::createOrAdoptInstance()
{
qCDebug(qLcEglDevDebug, "Creating Vulkan instance for VK_KHR_display");
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;
@ -109,11 +105,16 @@ void QEglFSVulkanInstance::createOrAdoptInstance()
if (m_physDev == VK_NULL_HANDLE)
m_physDev = physDevs[0];
if (chooseDisplay()) {
if (m_createdCallback)
m_createdCallback(this, m_createdCallbackUserData);
}
}
bool QEglFSVulkanInstance::supportsPresent(VkPhysicalDevice physicalDevice,
uint32_t queueFamilyIndex,
QWindow *window)
bool QVkKhrDisplayVulkanInstance::supportsPresent(VkPhysicalDevice physicalDevice,
uint32_t queueFamilyIndex,
QWindow *window)
{
Q_UNUSED(physicalDevice);
Q_UNUSED(queueFamilyIndex);
@ -121,43 +122,41 @@ bool QEglFSVulkanInstance::supportsPresent(VkPhysicalDevice physicalDevice,
return true;
}
VkSurfaceKHR QEglFSVulkanInstance::createSurface(QEglFSWindow *window)
bool QVkKhrDisplayVulkanInstance::chooseDisplay()
{
#if VK_KHR_display
qCDebug(qLcEglDevDebug, "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;
}
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 VK_NULL_HANDLE;
return false;
}
qCDebug(qLcEglDevDebug, "Display count: %u", displayCount);
qDebug("Display count: %u", displayCount);
QVarLengthArray<VkDisplayPropertiesKHR, 4> displayProps(displayCount);
m_getPhysicalDeviceDisplayPropertiesKHR(m_physDev, &displayCount, displayProps.data());
VkDisplayKHR display = VK_NULL_HANDLE;
VkDisplayModeKHR displayMode = VK_NULL_HANDLE;
uint32_t width = 0;
uint32_t height = 0;
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]);
qCDebug(qLcEglDevDebug, "Display #%u:\n display: %p\n name: %s\n dimensions: %ux%u\n resolution: %ux%u",
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);
// Just pick the first display and the first mode.
if (i == 0)
display = disp.display;
if (i == wantedDisplayIndex)
m_display = disp.display;
uint32_t modeCount = 0;
if (m_getDisplayModePropertiesKHR(m_physDev, disp.display, &modeCount, nullptr) != VK_SUCCESS) {
@ -168,56 +167,59 @@ VkSurfaceKHR QEglFSVulkanInstance::createSurface(QEglFSWindow *window)
m_getDisplayModePropertiesKHR(m_physDev, disp.display, &modeCount, modeProps.data());
for (uint32_t j = 0; j < modeCount; ++j) {
const VkDisplayModePropertiesKHR &mode(modeProps[j]);
qCDebug(qLcEglDevDebug, " Mode #%u:\n mode: %p\n visibleRegion: %ux%u\n refreshRate: %u",
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 == 0) {
displayMode = mode.displayMode;
width = mode.parameters.visibleRegion.width;
height = mode.parameters.visibleRegion.height;
if (j == wantedModeIndex) {
m_displayMode = mode.displayMode;
m_width = mode.parameters.visibleRegion.width;
m_height = mode.parameters.visibleRegion.height;
}
}
}
if (display == VK_NULL_HANDLE || displayMode == VK_NULL_HANDLE) {
if (m_display == VK_NULL_HANDLE || m_displayMode == VK_NULL_HANDLE) {
qWarning("Failed to choose display and mode");
return VK_NULL_HANDLE;
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 VK_NULL_HANDLE;
return false;
}
qCDebug(qLcEglDevDebug, "Plane count: %u", planeCount);
qDebug("Plane count: %u", planeCount);
QVarLengthArray<VkDisplayPlanePropertiesKHR, 4> planeProps(planeCount);
m_getPhysicalDeviceDisplayPlanePropertiesKHR(m_physDev, &planeCount, planeProps.data());
uint32_t planeIndex = UINT_MAX;
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 VK_NULL_HANDLE;
return false;
}
QVarLengthArray<VkDisplayKHR, 4> supportedDisplays(supportedDisplayCount);
m_getDisplayPlaneSupportedDisplaysKHR(m_physDev, i, &supportedDisplayCount, supportedDisplays.data());
qCDebug(qLcEglDevDebug, "Plane #%u supports %u displays, currently bound to display %p",
qDebug("Plane #%u supports %u displays, currently bound to display %p",
i, supportedDisplayCount, (void *) planeProps[i].currentDisplay);
VkDisplayPlaneCapabilitiesKHR caps;
err = m_getDisplayPlaneCapabilitiesKHR(m_physDev, displayMode, i, &caps);
err = m_getDisplayPlaneCapabilitiesKHR(m_physDev, m_displayMode, i, &caps);
if (err != VK_SUCCESS) {
qWarning("Failed to query plane capabilities: %d", err);
return VK_NULL_HANDLE;
return false;
}
qCDebug(qLcEglDevDebug, " supportedAlpha: %d (1=no, 2=global, 4=per pixel, 8=per pixel premul)\n"
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"
@ -229,52 +231,69 @@ VkSurfaceKHR QEglFSVulkanInstance::createSurface(QEglFSWindow *window)
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(display)
&& (planeProps[i].currentDisplay == VK_NULL_HANDLE || planeProps[i].currentDisplay == display))
if (supportedDisplays.contains(m_display)
&& (planeProps[i].currentDisplay == VK_NULL_HANDLE || planeProps[i].currentDisplay == m_display))
{
planeIndex = i;
m_planeIndex = i;
m_planeStackIndex = planeProps[i].currentStackIndex;
}
}
if (planeIndex == UINT_MAX) {
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;
}
qCDebug(qLcEglDevDebug, "Using plane #%u", planeIndex);
VkDisplaySurfaceCreateInfoKHR surfaceCreateInfo = {};
surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR;
surfaceCreateInfo.displayMode = displayMode;
surfaceCreateInfo.planeIndex = planeIndex;
surfaceCreateInfo.planeStackIndex = planeProps[planeIndex].currentStackIndex;
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 = { width, height };
surfaceCreateInfo.imageExtent = { m_width, m_height };
VkSurfaceKHR surface = VK_NULL_HANDLE;
err = m_createDisplayPlaneSurfaceKHR(m_vkInst, &surfaceCreateInfo, nullptr, &surface);
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;
}
qCDebug(qLcEglDevDebug, "Created surface %p", (void *) surface);
qDebug("Created surface %p", (void *) surface);
return surface;
#else
Q_UNUSED(window);
qWarning("VK_KHR_display support was not compiled in, cannot create surface");
return VK_NULL_HANDLE;
#endif
}
void QEglFSVulkanInstance::presentAboutToBeQueued(QWindow *window)
void QVkKhrDisplayVulkanInstance::presentAboutToBeQueued(QWindow *window)
{
// support QT_QPA_EGLFS_FORCEVSYNC (i.MX8 with eglfs_viv)
qt_egl_device_integration()->waitForVSync(window->handle());
Q_UNUSED(window);
}
QT_END_NAMESPACE

View File

@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
@ -37,8 +37,8 @@
**
****************************************************************************/
#ifndef QEGLFSVULKANINSTANCE_H
#define QEGLFSVULKANINSTANCE_H
#ifndef QVKKHRDISPLAYVULKANINSTANCE_H
#define QVKKHRDISPLAYVULKANINSTANCE_H
//
// W A R N I N G
@ -51,25 +51,33 @@
// We mean it.
//
#include "qeglfsglobal_p.h"
#include <QtGui/private/qbasicvulkanplatforminstance_p.h>
#include <QtCore/qsize.h>
QT_BEGIN_NAMESPACE
class QEglFSWindow;
class Q_EGLFS_EXPORT QEglFSVulkanInstance : public QBasicPlatformVulkanInstance
class QVkKhrDisplayVulkanInstance : public QBasicPlatformVulkanInstance
{
public:
QEglFSVulkanInstance(QVulkanInstance *instance);
QVkKhrDisplayVulkanInstance(QVulkanInstance *instance);
void createOrAdoptInstance() override;
bool supportsPresent(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, QWindow *window) override;
void presentAboutToBeQueued(QWindow *window) override;
VkSurfaceKHR createSurface(QEglFSWindow *window);
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;
@ -81,6 +89,15 @@ private:
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

View File

@ -0,0 +1,3 @@
{
"Keys": [ "vkkhrdisplay" ]
}