Basic Vulkan enablers

For Android, Windows and xcb. Verified on Win10 with NVIDIA, Win10
with AMD, Android with Tegra K1, Android aarch64 with Tegra X1, and
Linux aarch64 with Tegra X1 (Jetson TX1, L4T).

Introduce QPA-based Vulkan library loader, core function resolver, and
instance creation support. In addition to creating a new VkInstance,
adopting an existing one from an external engine is supported as well.

The WSI specifics are hidden in the platform plugins. Vulkan-capable
windows use the new surface type VulkanSurface and are associated with
a QVulkanInstance.

On Windows VULKAN_SDK is picked up automatically so finding vulkan.h
needs no additional manual steps once the LunarG SDK is installed.

[ChangeLog][QtGui] Added support for rendering to QWindow via the Vulkan
graphics API.

Task-number: QTBUG-55981
Change-Id: I50fa92d313fa440e0cc73939c6d7510ca317fbc9
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@qt.io>
Reviewed-by: Andy Nichols <andy.nichols@qt.io>
This commit is contained in:
Laszlo Agocs 2017-01-31 15:04:33 +01:00
parent a512c9c2f7
commit f1a23a5467
63 changed files with 9981 additions and 19 deletions

View File

@ -0,0 +1,58 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the config.tests 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$
**
****************************************************************************/
// This is a header-only test. Qt does not rely on linking to a Vulkan library directly.
#include <vulkan/vulkan.h>
// The pData parameter has changed from uint32_t* to void* at some point.
// Ensure the headers have the updated one to prevent compile errors later on.
PFN_vkCmdUpdateBuffer cmdUpdBuf;
void testUpdateBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const void* pData)
{
cmdUpdBuf(commandBuffer, dstBuffer, dstOffset, dataSize, pData);
}
int main(int, char **)
{
VkInstanceCreateInfo info;
testUpdateBuffer(0, 0, 0, 0, 0);
return 0;
}

View File

@ -0,0 +1 @@
SOURCES = vulkan.cpp

View File

@ -35,6 +35,7 @@ QMAKE_LIBS_OPENGL = -lGL
QMAKE_LIBS_OPENGL_ES2 = -lGLESv2 QMAKE_LIBS_OPENGL_ES2 = -lGLESv2
QMAKE_LIBS_OPENVG = -lOpenVG QMAKE_LIBS_OPENVG = -lOpenVG
QMAKE_LIBS_THREAD = -lpthread QMAKE_LIBS_THREAD = -lpthread
QMAKE_LIBS_VULKAN =
QMAKE_INCDIR_WAYLAND = QMAKE_INCDIR_WAYLAND =
QMAKE_LIBS_WAYLAND_CLIENT = -lwayland-client QMAKE_LIBS_WAYLAND_CLIENT = -lwayland-client

View File

@ -106,3 +106,4 @@ VCSOLUTION_EXTENSION = .sln
VCPROJ_KEYWORD = Qt4VSv1.0 VCPROJ_KEYWORD = Qt4VSv1.0
include(windows-gles.conf) include(windows-gles.conf)
include(windows-vulkan.conf)

View File

@ -0,0 +1,5 @@
# Pick up the VULKAN_SDK env var set by the LunarG SDK so that the Vulkan
# headers are found out-of-the-box on typical Windows setups.
QMAKE_INCDIR_VULKAN = $(VULKAN_SDK)\\include
QMAKE_LIBS_VULKAN =

View File

@ -80,6 +80,7 @@ QMAKE_OBJCOPY = $${CROSS_COMPILE}objcopy
QMAKE_NM = $${CROSS_COMPILE}nm -P QMAKE_NM = $${CROSS_COMPILE}nm -P
include(../common/windows-gles.conf) include(../common/windows-gles.conf)
include(../common/windows-vulkan.conf)
include(../common/gcc-base.conf) include(../common/gcc-base.conf)
load(qt_config) load(qt_config)

View File

@ -59,5 +59,6 @@ QMAKE_LIB = xilib /NOLOGO
DSP_EXTENSION = .dsp DSP_EXTENSION = .dsp
include(../common/windows-gles.conf) include(../common/windows-gles.conf)
include(../common/windows-vulkan.conf)
load(qt_config) load(qt_config)

View File

@ -38,6 +38,7 @@
"qpa-platform-guard": "boolean", "qpa-platform-guard": "boolean",
"sm": { "type": "boolean", "name": "sessionmanager" }, "sm": { "type": "boolean", "name": "sessionmanager" },
"tslib": "boolean", "tslib": "boolean",
"vulkan": "boolean",
"xcb": { "type": "enum", "values": [ "no", "yes", "qt", "system" ] }, "xcb": { "type": "enum", "values": [ "no", "yes", "qt", "system" ] },
"xcb-xlib": "boolean", "xcb-xlib": "boolean",
"xinput2": "boolean", "xinput2": "boolean",
@ -211,6 +212,14 @@
"-lts" "-lts"
] ]
}, },
"vulkan": {
"label": "Vulkan",
"test": "qpa/vulkan",
"sources": [
{ "type": "pkgConfig", "args": "vulkan" },
{ "type": "makeSpec", "spec": "VULKAN" }
]
},
"wayland_server": { "wayland_server": {
"label": "Wayland Server", "label": "Wayland Server",
"test": "qpa/wayland-server", "test": "qpa/wayland-server",
@ -605,6 +614,11 @@
"condition": "features.opengl-desktop || features.opengl-dynamic || features.opengles2", "condition": "features.opengl-desktop || features.opengl-dynamic || features.opengles2",
"output": [ "publicFeature", "feature" ] "output": [ "publicFeature", "feature" ]
}, },
"vulkan": {
"label": "Vulkan",
"condition": "libs.vulkan",
"output": [ "publicFeature" ]
},
"openvg": { "openvg": {
"label": "OpenVG", "label": "OpenVG",
"condition": "libs.openvg", "condition": "libs.openvg",
@ -1096,6 +1110,7 @@ QMAKE_LIBDIR_OPENGL[_ES2] and QMAKE_LIBS_OPENGL[_ES2] in the mkspec for your pla
"opengles31" "opengles31"
] ]
}, },
"vulkan",
"sessionmanager" "sessionmanager"
] ]
}, },

View File

@ -46,6 +46,7 @@ include(math3d/math3d.pri)
include(opengl/opengl.pri) include(opengl/opengl.pri)
include(animation/animation.pri) include(animation/animation.pri)
include(itemmodels/itemmodels.pri) include(itemmodels/itemmodels.pri)
include(vulkan/vulkan.pri)
QMAKE_LIBS += $$QMAKE_LIBS_GUI QMAKE_LIBS += $$QMAKE_LIBS_GUI

View File

@ -629,4 +629,26 @@ void QPlatformIntegration::setApplicationIcon(const QIcon &icon) const
Q_UNUSED(icon); Q_UNUSED(icon);
} }
#if QT_CONFIG(vulkan)
/*!
Factory function for QPlatformVulkanInstance. The \a instance parameter is a
pointer to the instance for which a platform-specific backend needs to be
created.
Returns a pointer to a QPlatformOpenGLContext instance or \c NULL if the context could
not be created.
\sa QVulkanInstance
\since 5.10
*/
QPlatformVulkanInstance *QPlatformIntegration::createPlatformVulkanInstance(QVulkanInstance *instance) const
{
Q_UNUSED(instance);
qWarning("This plugin does not support createPlatformVulkanInstance");
return nullptr;
}
#endif // QT_CONFIG(vulkan)
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -78,6 +78,8 @@ class QPlatformSessionManager;
class QKeyEvent; class QKeyEvent;
class QPlatformOffscreenSurface; class QPlatformOffscreenSurface;
class QOffscreenSurface; class QOffscreenSurface;
class QPlatformVulkanInstance;
class QVulkanInstance;
class Q_GUI_EXPORT QPlatformIntegration class Q_GUI_EXPORT QPlatformIntegration
{ {
@ -190,6 +192,10 @@ public:
virtual void beep() const; virtual void beep() const;
#if QT_CONFIG(vulkan)
virtual QPlatformVulkanInstance *createPlatformVulkanInstance(QVulkanInstance *instance) const;
#endif
protected: protected:
void screenAdded(QPlatformScreen *screen, bool isPrimary = false); void screenAdded(QPlatformScreen *screen, bool isPrimary = false);
void destroyScreen(QPlatformScreen *screen); void destroyScreen(QPlatformScreen *screen);

View File

@ -78,6 +78,8 @@ QT_BEGIN_NAMESPACE
requires the use of private API. requires the use of private API.
\value OpenVGSurface The surface is an OpenVG compatible surface and can be used \value OpenVGSurface The surface is an OpenVG compatible surface and can be used
in conjunction with OpenVG contexts. in conjunction with OpenVG contexts.
\value VulkanSurface The surface is a Vulkan compatible surface and can be used
in conjunction with the Vulkan graphics API.
*/ */

View File

@ -66,6 +66,7 @@ public:
OpenGLSurface, OpenGLSurface,
RasterGLSurface, RasterGLSurface,
OpenVGSurface, OpenVGSurface,
VulkanSurface
}; };
virtual ~QSurface(); virtual ~QSurface();

View File

@ -2797,6 +2797,30 @@ QDebug operator<<(QDebug debug, const QWindow *window)
} }
#endif // !QT_NO_DEBUG_STREAM #endif // !QT_NO_DEBUG_STREAM
#if QT_CONFIG(vulkan)
/*!
Associates this window with the specified Vulkan \a instance.
\a instance must stay valid as long as this QWindow instance exists.
*/
void QWindow::setVulkanInstance(QVulkanInstance *instance)
{
Q_D(QWindow);
d->vulkanInstance = instance;
}
/*!
\return the associrated Vulkan instance or \c null if there is none.
*/
QVulkanInstance *QWindow::vulkanInstance() const
{
Q_D(const QWindow);
return d->vulkanInstance;
}
#endif // QT_CONFIG(vulkan)
QT_END_NAMESPACE QT_END_NAMESPACE
#include "moc_qwindow.cpp" #include "moc_qwindow.cpp"

View File

@ -88,6 +88,9 @@ class QWindowContainer;
#ifndef QT_NO_DEBUG_STREAM #ifndef QT_NO_DEBUG_STREAM
class QDebug; class QDebug;
#endif #endif
#if QT_CONFIG(vulkan)
class QVulkanInstance;
#endif
class Q_GUI_EXPORT QWindow : public QObject, public QSurface class Q_GUI_EXPORT QWindow : public QObject, public QSurface
{ {
@ -269,6 +272,11 @@ public:
static QWindow *fromWinId(WId id); static QWindow *fromWinId(WId id);
#if QT_CONFIG(vulkan)
void setVulkanInstance(QVulkanInstance *instance);
QVulkanInstance *vulkanInstance() const;
#endif
public Q_SLOTS: public Q_SLOTS:
Q_REVISION(1) void requestActivate(); Q_REVISION(1) void requestActivate();

View File

@ -105,6 +105,9 @@ public:
, hasCursor(false) , hasCursor(false)
#endif #endif
, compositing(false) , compositing(false)
#if QT_CONFIG(vulkan)
, vulkanInstance(nullptr)
#endif
{ {
isWindow = true; isWindow = true;
} }
@ -196,6 +199,10 @@ public:
bool compositing; bool compositing;
QElapsedTimer lastComposeTime; QElapsedTimer lastComposeTime;
#if QT_CONFIG(vulkan)
QVulkanInstance *vulkanInstance;
#endif
}; };

View File

@ -0,0 +1,88 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtGui module 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 "qplatformvulkaninstance.h"
QT_BEGIN_NAMESPACE
/*!
\class QPlatformVulkanInstance
\since 5.10
\internal
\preliminary
\ingroup qpa
\brief The QPlatformVulkanInstance class provides an abstraction for Vulkan instances.
The platform Vulkan instance is responsible for loading a Vulkan library,
resolving the basic entry points for creating instances, providing support
for creating new or adopting existing VkInstances, and abstracting some
WSI-specifics like checking if a given queue family can be used to present
using a given window.
\note platform plugins will typically subclass not this class, but rather
QBasicVulkanPlatformInstance.
\note Vulkan instance creation is split into two phases: a new
QPlatformVulkanInstance is expected to load the Vulkan library and do basic
initialization, after which the supported layers and extensions can be
queried. Everything else is deferred into createOrAdoptInstance().
*/
class QPlatformVulkanInstancePrivate
{
public:
QPlatformVulkanInstancePrivate() { }
};
QPlatformVulkanInstance::QPlatformVulkanInstance()
: d_ptr(new QPlatformVulkanInstancePrivate)
{
}
QPlatformVulkanInstance::~QPlatformVulkanInstance()
{
}
void QPlatformVulkanInstance::presentQueued(QWindow *window)
{
Q_UNUSED(window);
}
QT_END_NAMESPACE

View File

@ -0,0 +1,91 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtGui module 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$
**
****************************************************************************/
#ifndef QPLATFORMVULKANINSTANCE_H
#define QPLATFORMVULKANINSTANCE_H
//
// W A R N I N G
// -------------
//
// This file is part of the QPA API and is not meant to be used
// in applications. Usage of this API may make your code
// source and binary incompatible with future versions of Qt.
//
#include <QtGui/qtguiglobal.h>
#if QT_CONFIG(vulkan)
#include <qvulkaninstance.h>
QT_BEGIN_NAMESPACE
class QPlatformVulkanInstancePrivate;
class Q_GUI_EXPORT QPlatformVulkanInstance
{
Q_DECLARE_PRIVATE(QPlatformVulkanInstance)
public:
QPlatformVulkanInstance();
virtual ~QPlatformVulkanInstance();
virtual QVulkanInfoVector<QVulkanLayer> supportedLayers() const = 0;
virtual QVulkanInfoVector<QVulkanExtension> supportedExtensions() const = 0;
virtual void createOrAdoptInstance() = 0;
virtual bool isValid() const = 0;
virtual VkResult errorCode() const = 0;
virtual VkInstance vkInstance() const = 0;
virtual QByteArrayList enabledLayers() const = 0;
virtual QByteArrayList enabledExtensions() const = 0;
virtual PFN_vkVoidFunction getInstanceProcAddr(const char *name) = 0;
virtual bool supportsPresent(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, QWindow *window) = 0;
virtual void presentQueued(QWindow *window);
private:
QScopedPointer<QPlatformVulkanInstancePrivate> d_ptr;
Q_DISABLE_COPY(QPlatformVulkanInstance)
};
QT_END_NAMESPACE
#endif // QT_CONFIG(vulkan)
#endif // QPLATFORMVULKANINSTANCE_H

View File

@ -0,0 +1,177 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtGui module 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 "qvulkanfunctions_p.h"
QT_BEGIN_NAMESPACE
/*!
\class QVulkanFunctions
\since 5.10
\ingroup painting-3D
\inmodule QtGui
\wrapper
\brief The QVulkanFunctions class provides cross-platform access to the
instance level core Vulkan 1.0 API.
Qt and Qt applications do not link to any Vulkan libraries by default.
Instead, all functions are resolved dynamically at run time. Each
QVulkanInstance provides a QVulkanFunctions object retrievable via
QVulkanInstance::functions(). This does not contain device level functions
in order to avoid the potential overhead of an internal dispatching.
Instead, functions that rely on a device, or a dispatchable child object of
a device, are exposed via QVulkanDeviceFunctions and
QVulkanInstance::deviceFunctions(). QVulkanFunctions and
QVulkanDeviceFunctions together provides access to the full core Vulkan
API, excluding any extensions.
\note QVulkanFunctions instances cannot be constructed directly.
The typical usage is the following:
\code
void Window::render()
{
QVulkanInstance *inst = vulkanInstance();
QVulkanFunctions *f = inst->functions();
...
VkResult err = f->vkAllocateCommandBuffers(device, &cmdBufInfo, &cmdBuf);
...
}
\endcode
\note Windowing system interface (WSI) specifics and extensions are
excluded. This class only covers core Vulkan commands, with the exception
of instance creation, destruction, and function resolving, since such
functionality is covered by QVulkanInstance itself.
To access additional functions, applications can use
QVulkanInstance::getInstanceProcAddr() and vkGetDeviceProcAddr().
Applications can also decide to link to a Vulkan library directly, as
platforms with an appropriate loader will typically export function symbols
for the core commands. See
\l{https://www.khronos.org/registry/vulkan/specs/1.0/man/html/vkGetInstanceProcAddr.html}{the
man page for vkGetInstanceProcAddr} for more information.
\sa QVulkanInstance, QVulkanDeviceFunctions, QWindow::setVulkanInstance(), QWindow::setSurfaceType()
*/
/*!
\class QVulkanDeviceFunctions
\since 5.10
\ingroup painting-3D
\inmodule QtGui
\wrapper
\brief The QVulkanDeviceFunctions class provides cross-platform access to
the device level core Vulkan 1.0 API.
Qt and Qt applications do not link to any Vulkan libraries by default.
Instead, all functions are resolved dynamically at run time. Each
QVulkanInstance provides a QVulkanFunctions object retrievable via
QVulkanInstance::functions(). This does not contain device level functions
in order to avoid the potential overhead of an internal dispatching.
Instead, functions that rely on a device, or a dispatchable child object of
a device, are exposed via QVulkanDeviceFunctions and
QVulkanInstance::deviceFunctions(). QVulkanFunctions and
QVulkanDeviceFunctions together provides access to the full core Vulkan
API, excluding any extensions.
\note QVulkanDeviceFunctions instances cannot be constructed directly.
The typical usage is the following:
\code
void Window::render()
{
QVulkanInstance *inst = vulkanInstance();
QVulkanDeviceFunctions *df = inst->deviceFunctions(device);
VkResult err = df->vkAllocateCommandBuffers(device, &cmdBufInfo, &cmdBuf);
...
}
\endcode
The QVulkanDeviceFunctions object specific to the provided VkDevice is
created when QVulkanInstance::deviceFunctions() is first called with the
device in question. The object is then cached internally.
To access additional functions, applications can use
QVulkanInstance::getInstanceProcAddr() and vkGetDeviceProcAddr().
Applications can also decide to link to a Vulkan library directly, as many
implementations export function symbols for the core commands. See
\l{https://www.khronos.org/registry/vulkan/specs/1.0/man/html/vkGetInstanceProcAddr.html}{the
man page for vkGetInstanceProcAddr} for more information.
\sa QVulkanInstance, QVulkanFunctions, QWindow::setVulkanInstance(), QWindow::setSurfaceType()
*/
/*
Constructs a new QVulkanFunctions for \a inst.
\internal
*/
QVulkanFunctions::QVulkanFunctions(QVulkanInstance *inst)
: d_ptr(new QVulkanFunctionsPrivate(inst))
{
}
/*
Destructor.
*/
QVulkanFunctions::~QVulkanFunctions()
{
}
/*
Constructs a new QVulkanDeviceFunctions for \a inst and the given \a device.
\internal
*/
QVulkanDeviceFunctions::QVulkanDeviceFunctions(QVulkanInstance *inst, VkDevice device)
: d_ptr(new QVulkanDeviceFunctionsPrivate(inst, device))
{
}
/*
Destructor.
*/
QVulkanDeviceFunctions::~QVulkanDeviceFunctions()
{
}
QT_END_NAMESPACE

View File

@ -0,0 +1,836 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtGui module 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 "qvulkaninstance.h"
#include "qvulkanfunctions_p.h"
#include <qpa/qplatformvulkaninstance.h>
#include <qpa/qplatformintegration.h>
#include <qpa/qplatformnativeinterface.h>
#include <QtGui/private/qguiapplication_p.h>
QT_BEGIN_NAMESPACE
/*!
\class QVulkanInstance
\since 5.10
\inmodule QtGui
\brief The QVulkanInstance class represents a native Vulkan instance, enabling
Vulkan rendering onto a QSurface.
\l{https://www.khronos.org/vulkan/}{Vulkan} is a cross-platform, explicit
graphics and compute API. This class provides support for loading a Vulkan
library and creating an \c instance in a cross-platform manner. For an
introduction on Vulkan instances, refer
\l{https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#initialization-instances}{to
section 3.2 of the specification}.
\note Platform-specific support for Vulkan instances and windows with
Vulkan-capable surfaces is provided by the various platform plugins. Not
all of them will support Vulkan, however. When running on such a platform,
create() will fail and always return \c false.
\note Vulkan support may get automatically disabled for a given Qt build due
to not having the necessary Vulkan headers available at build time. When
this is the case, and the output of \c configure indicates Vulkan support is
disabled, the QVulkan* classes will be unavailable.
\note Some functions changed their signature between the various Vulkan
header revisions. When building Qt and only headers with the old,
conflicting signatures are present in a system, Vulkan support will get
disabled. It is recommended to use headers from Vulkan 1.0.39 or newer.
\section1 Initialization
Similarly to QOpenGLContext, any actual Vulkan instance creation happens
only when calling create(). This allows using QVulkanInstance as a plain
member variable while retaining control over when to perform
initialization.
Querying the supported instance-level layers and extensions is possible by
calling supportedLayers() and supportedExtensions(). These ensure the
Vulkan library is loaded, and can therefore be called safely before
create() as well.
Instances store per-application Vulkan state and creating a \c VkInstance
object initializes the Vulkan library. In practice there will typically be
a single instance constructed early on in main(). The object then stays
alive until exiting the application.
Every Vulkan-based QWindow must be associated with a QVulkanInstance by
calling QWindow::setVulkanInstance(). Thus a typical application pattern is
the following:
\code
int main(int argc, char **argv)
{
QGuiApplication app(argc, argv);
QVulkanInstance inst;
if (!inst.create())
return 1;
...
window->setVulkanInstance(&inst);
window->show();
return app.exec();
}
\endcode
\section1 Configuration
QVulkanInstance automatically enables the minimum set of extensions it
needs on the newly created instance. In practice this means the
\c{VK_KHR_*_surface} family of extensions.
By default Vulkan debug output, for example messages from the validation
layers, is routed to qDebug(). This can be disabled by passing the flag
\c NoDebugOutputRedirect to setFlags() <i>before</i> invoking create().
To enable additional layers and extensions, provide the list via
setLayers() and setExtensions() <i>before</i> invoking create(). When a
given layer or extension is not reported as available from the instance,
the request is ignored. After a successful call to create(), the values
returned from functions like layers() and extensions() reflect the actual
enabled layers and extensions. When necessary, for example to avoid
requesting extensions that conflict and thus would fail the Vulkan instance
creation, the list of actually supported layers and extensions can be
examined via supportedLayers() and supportedExtensions() before calling
create().
For example, to enable the standard validation layers, one could do the
following:
\code
QVulkanInstance inst;
// Enable validation layer, if supported. Messages go to qDebug by default.
inst.setLayers(QByteArrayList() << "VK_LAYER_LUNARG_standard_validation");
bool ok = inst.create();
if (!ok)
... // Vulkan not available
if (!inst.layers().contains("VK_LAYER_LUNARG_standard_validation"))
... // validation layer not available
\endcode
Or, alternatively, to make decisions before attempting to create a Vulkan
instance:
\code
QVulkanInstance inst;
if (inst.supportedLayers().contains("VK_LAYER_LUNARG_standard_validation"))
...
bool ok = inst.create();
...
\endcode
\section1 Adopting an Existing Instance
By default QVulkanInstance creates a new Vulkan instance. When working with
external engines and renderers, this may sometimes not be desirable. When
there is a \c VkInstance handle already available, call setVkInstance()
before invoking create(). This way no additional instances will get
created, and QVulkanInstance will not own the handle.
\note It is up to the component creating the external instance to ensure
the necessary extensions are enabled on it. These are: \c{VK_KHR_surface},
the WSI-specific \c{VK_KHR_*_surface} that is appropriate for the platform
in question, and \c{VK_EXT_debug_report} in case QVulkanInstance's debug
output redirection is desired.
\section1 Accessing Core Vulkan Commands
To access the \c VkInstance handle the QVulkanInstance wraps, call
vkInstance(). To resolve Vulkan functions, call getInstanceProcAddr(). For
core Vulkan commands manual resolving is not necessary as they are provided
via the QVulkanFunctions object accessible by calling functions().
\section1 Getting a Native Vulkan Surface for a Window
The two common windowing system specific operations are getting a surface
(a \c{VkSurfaceKHR} handle) for a window, and querying if a given queue
family supports presenting to a given surface. To avoid WSI-specific bits
in the applications, these are abstracted by QVulkanInstance and the
underlying QPA layers.
To create a Vulkan surface for a window, or retrieve an existing one,
call surfaceForWindow(). Most platforms will only create the surface via
\c{VK_KHR_*_surface} when first calling surfaceForWindow(), but there may be
platform-specific variations in the internal behavior. Once created,
subsequent calls to surfaceForWindow() just return the same handle. This
fits the structure of typical Vulkan-enabled QWindow subclasses well.
To query if a given queue family within a physical device can be used to
perform presentation to a given surface, call supportsPresent(). This
encapsulates both the generic \c vkGetPhysicalDeviceSurfaceSupportKHR and
the WSI-specific \c{vkGetPhysicalDevice*PresentationSupportKHR} checks.
\section1 Troubleshooting
Besides returning \c false from create() or \c 0 from surfaceForWindow(),
critical errors will also get printed to the debug output via qWarning().
Additional logging can be requested by enabling debug output for the
logging category \c{qt.vulkan}. The actual Vulkan error code from instance
creation can be retrieved by calling errorCode() after a failing create().
In some special cases it may be necessary to override the Vulkan
library name. This can be achieved by setting the \c{QT_VULKAN_LIB}
environment variable.
\section1 Example
The following is the basic outline of creating a Vulkan-capable QWindow:
\code
class VulkanWindow : public QWindow
{
public:
VulkanWindow() {
setSurfaceType(VulkanSurface);
}
void exposeEvent(QExposeEvent *) {
if (isExposed()) {
if (!m_initialized) {
m_initialized = true;
// initialize device, swapchain, etc.
QVulkanInstance *inst = vulkanInstance();
QVulkanFunctions *f = inst->functions();
uint32_t devCount = 0;
f->vkEnumeratePhysicalDevices(inst->vkInstance(), &devCount, nullptr);
...
// build the first frame
render();
}
}
}
bool event(QEvent *e) {
if (e->type == QEvent::UpdateRequest)
render();
return QWindow::event(e);
}
void render() {
...
requestUpdate(); // render continuously
}
private:
bool m_initialized = false;
};
int main(int argc, char **argv)
{
QGuiApplication app(argc, argv);
QVulkanInstance inst;
if (!inst.create()) {
qWarning("Vulkan not available");
return 1;
}
VulkanWindow window;
window.showMaximized();
return app.exec();
}
\endcode
\note In addition to expose, a well-behaving window implementation will
also have to take care of additional events like resize and
QPlatformSurfaceEvent in order to ensure proper management of the
swap chain. Additionally, some platforms may require releasing resources
when not being exposed anymore.
\section1 Using C++ Bindings for Vulkan
Combining Qt's Vulkan enablers with a C++ Vulkan wrapper, for example
\l{https://github.com/KhronosGroup/Vulkan-Hpp}{Vulkan-Hpp}, is possible as
well. The pre-requisite here is that the C++ layer must be able to adopt
native handles (VkInstance, VkSurfaceKHR) in its classes without taking
ownership (since the ownership stays with QVulkanInstance and QWindow).
Consider also the following:
\list
\li Some wrappers require exception support to be enabled. Qt does not use
exceptions. To enable exceptions for the application, add \c{CONFIG += exceptions}
to the \c{.pro} file.
\li Some wrappers call Vulkan functions directly, assuming \c{vulkan.h}
provides prototypes and the application links to a Vulkan library exporting
all necessary symbols. Qt may not directly link to a Vulkan library.
Therefore, on some platforms it may be necessary to add
\c{LIBS += -lvulkan} or similar in the application's \c{.pro} file.
\li The headers for the QVulkan classes may include \c{vulkan.h} with
\c{VK_NO_PROTOTYPES} enabled. This can cause issues in C++ wrapper headers
that rely on the prototypes. Hence in application code it may be
necessary to include \c{vulkan.hpp} or similar before any of the QVulkan
headers.
\endlist
\sa QVulkanFunctions, QSurface::SurfaceType
*/
/*!
\enum QVulkanInstance::Flag
\since 5.10
This enum describes the flags that can be passed to setFlags(). These control
the behavior of create().
\value NoDebugOutputRedirect Disables Vulkan debug output (\c{VK_EXT_debug_report}) redirection to qDebug.
*/
class QVulkanInstancePrivate
{
public:
QVulkanInstancePrivate(QVulkanInstance *q)
: q_ptr(q),
vkInst(VK_NULL_HANDLE),
flags(0),
errorCode(VK_SUCCESS)
{ }
~QVulkanInstancePrivate() { reset(); }
bool ensureVulkan();
void reset();
QVulkanInstance *q_ptr;
QScopedPointer<QPlatformVulkanInstance> platformInst;
VkInstance vkInst;
QVulkanInstance::Flags flags;
QByteArrayList layers;
QByteArrayList extensions;
QVersionNumber apiVersion;
VkResult errorCode;
QScopedPointer<QVulkanFunctions> funcs;
QHash<VkDevice, QVulkanDeviceFunctions *> deviceFuncs;
};
bool QVulkanInstancePrivate::ensureVulkan()
{
if (!platformInst) {
platformInst.reset(QGuiApplicationPrivate::platformIntegration()->createPlatformVulkanInstance(q_ptr));
if (!platformInst) {
qWarning("QVulkanInstance: Failed to initialize Vulkan");
return false;
}
}
return true;
}
void QVulkanInstancePrivate::reset()
{
qDeleteAll(deviceFuncs);
deviceFuncs.clear();
funcs.reset();
platformInst.reset();
vkInst = VK_NULL_HANDLE;
errorCode = VK_SUCCESS;
}
/*!
Constructs a new instance.
\note No Vulkan initialization is performed in the constructor.
*/
QVulkanInstance::QVulkanInstance()
: d_ptr(new QVulkanInstancePrivate(this))
{
}
/*!
Destructor.
\note current() will return \c nullptr once the instance is destroyed.
*/
QVulkanInstance::~QVulkanInstance()
{
destroy();
}
/*!
\struct QVulkanLayer
\brief Represents information about a Vulkan layer.
*/
/*!
\value QVulkanLayer::name
\brief The name of the layer.
*/
/*!
\value QVulkanLayer::version
\brief The version of the layer. This is an integer, increasing with each backward
compatible change.
*/
/*!
\value QVulkanLayer::specVersion
\brief The Vulkan version the layer was written against.
*/
/*!
\value QVulkanLayer::description
\brief The description of the layer.
*/
/*!
\struct QVulkanExtension
\brief Represents information about a Vulkan extension.
*/
/*!
\value QVulkanExtension::name
\brief The name of the extension.
*/
/*!
\value QVulkanExtension::version
\brief The version of the extension. This is an integer, increasing with each backward
compatible change.
*/
/*!
\class QVulkanInfoVector
\brief A specialized QVector for QVulkanLayer and QVulkanExtension.
*/
/*!
\fn bool QVulkanInfoVector::contains(const QByteArray &name) const
\return true if the vector contains a layer or extension with the given \a name.
*/
/*!
\fn bool QVulkanInfoVector::contains(const QByteArray &name, int minVersion) const
\return true if the vector contains a layer or extension with the given
\a name and a version same as or newer than \a minVersion.
*/
/*!
\return the list of supported instance-level layers.
\note This function can be called before create().
*/
QVulkanInfoVector<QVulkanLayer> QVulkanInstance::supportedLayers()
{
return d_ptr->ensureVulkan() ? d_ptr->platformInst->supportedLayers() : QVulkanInfoVector<QVulkanLayer>();
}
/*!
\return the list of supported instance-level extensions.
\note This function can be called before create().
*/
QVulkanInfoVector<QVulkanExtension> QVulkanInstance::supportedExtensions()
{
return d_ptr->ensureVulkan() ? d_ptr->platformInst->supportedExtensions() : QVulkanInfoVector<QVulkanExtension>();
}
/*!
Makes QVulkanInstance adopt an existing VkInstance handle instead of
creating a new one.
\note \a existingVkInstance must have at least \c{VK_KHR_surface} and the
appropriate WSI-specific \c{VK_KHR_*_surface} extensions enabled. To ensure
debug output redirection is functional, \c{VK_EXT_debug_report} is needed as
well.
\note This function can only be called before create() and has no effect if
called afterwards.
*/
void QVulkanInstance::setVkInstance(VkInstance existingVkInstance)
{
if (isValid()) {
qWarning("QVulkanInstance already created; setVkInstance() has no effect");
return;
}
d_ptr->vkInst = existingVkInstance;
}
/*!
Configures the behavior of create() based on the provided \a flags.
\note This function can only be called before create() and has no effect if
called afterwards.
*/
void QVulkanInstance::setFlags(Flags flags)
{
if (isValid()) {
qWarning("QVulkanInstance already created; setFlags() has no effect");
return;
}
d_ptr->flags = flags;
}
/*!
Specifies the list of instance \a layers to enable. It is safe to specify
unsupported layers as well because these get ignored when not supported at
run time.
\note This function can only be called before create() and has no effect if
called afterwards.
*/
void QVulkanInstance::setLayers(const QByteArrayList &layers)
{
if (isValid()) {
qWarning("QVulkanInstance already created; setLayers() has no effect");
return;
}
d_ptr->layers = layers;
}
/*!
Specifies the list of additional instance \a extensions to enable. It is
safe to specify unsupported extensions as well because these get ignored
when not supported at run time. The surface-related extensions required by
Qt will always be added automatically, no need to include them in this
list.
\note This function can only be called before create() and has no effect if
called afterwards.
*/
void QVulkanInstance::setExtensions(const QByteArrayList &extensions)
{
if (isValid()) {
qWarning("QVulkanInstance already created; setExtensions() has no effect");
return;
}
d_ptr->extensions = extensions;
}
/*!
Specifies the Vulkan API against which the application expects to run.
By default no \a vulkanVersion is specified, and so no version check is performed
during Vulkan instance creation.
\note This function can only be called before create() and has no effect if
called afterwards.
*/
void QVulkanInstance::setApiVersion(const QVersionNumber &vulkanVersion)
{
if (isValid()) {
qWarning("QVulkanInstance already created; setApiVersion() has no effect");
return;
}
d_ptr->apiVersion = vulkanVersion;
}
/*!
Initializes the Vulkan library and creates a new or adopts and existing
Vulkan instance.
\return true if successful, false on error or when Vulkan is not supported.
When successful, the pointer to this QVulkanInstance is retrievable via the
static function current().
The Vulkan instance and library is available as long as this
QVulkanInstance exists, or until destroy() is called.
*/
bool QVulkanInstance::create()
{
if (isValid())
destroy();
if (!d_ptr->ensureVulkan())
return false;
d_ptr->platformInst->createOrAdoptInstance();
if (d_ptr->platformInst->isValid()) {
d_ptr->vkInst = d_ptr->platformInst->vkInstance();
d_ptr->layers = d_ptr->platformInst->enabledLayers();
d_ptr->extensions = d_ptr->platformInst->enabledExtensions();
d_ptr->errorCode = VK_SUCCESS;
d_ptr->funcs.reset(new QVulkanFunctions(this));
return true;
}
qWarning("Failed to create platform Vulkan instance");
if (d_ptr->platformInst) {
d_ptr->errorCode = d_ptr->platformInst->errorCode();
d_ptr->platformInst.reset();
} else {
d_ptr->errorCode = VK_NOT_READY;
}
return false;
}
/*!
Destroys the underlying platform instance, thus destroying the VkInstance
(when owned). The QVulkanInstance object is still reusable by calling
create() again.
*/
void QVulkanInstance::destroy()
{
d_ptr->reset();
}
/*!
\return true if create() was successful and the instance is valid.
*/
bool QVulkanInstance::isValid() const
{
return d_ptr->platformInst && d_ptr->platformInst->isValid();
}
/*!
\return the Vulkan error code after an unsuccessful create(), \c VK_SUCCESS otherwise.
The value is typically the return value from vkCreateInstance() (when
creating a new Vulkan instance instead of adopting an existing one), but
may also be \c VK_NOT_READY if the platform plugin does not support Vulkan.
*/
VkResult QVulkanInstance::errorCode() const
{
return d_ptr->errorCode;
}
/*!
\return the VkInstance handle this QVulkanInstance wraps, or \c null if
create() has not yet been successfully called and no existing instance has
been provided via setVkInstance().
*/
VkInstance QVulkanInstance::vkInstance() const
{
return d_ptr->vkInst;
}
/*!
\return the requested flags.
*/
QVulkanInstance::Flags QVulkanInstance::flags() const
{
return d_ptr->flags;
}
/*!
\return the enabled instance layers, if create() was called and was successful. The
requested layers otherwise.
*/
QByteArrayList QVulkanInstance::layers() const
{
return d_ptr->layers;
}
/*!
\return the enabled instance extensions, if create() was called and was
successful. The requested extensions otherwise.
*/
QByteArrayList QVulkanInstance::extensions() const
{
return d_ptr->extensions;
}
/*!
\return the requested Vulkan API version against which the application
expects to run, or a null version number if setApiVersion() was not called
before create().
*/
QVersionNumber QVulkanInstance::apiVersion() const
{
return d_ptr->apiVersion;
}
/*!
Resolves the Vulkan function with the given \a name.
For core Vulkan commands use functions() and QVulkanFunctions instead.
\note When resolving device-specific extensions, prefer using
QVulkanFunctions::vkGetDeviceProcAddr() as explained
\l{https://www.khronos.org/registry/vulkan/specs/1.0/man/html/vkGetDeviceProcAddr.html}{in
the Vulkan specification}.
*/
PFN_vkVoidFunction QVulkanInstance::getInstanceProcAddr(const char *name)
{
// The return value is PFN_vkVoidFunction instead of QFunctionPointer or
// similar because on some platforms honoring VKAPI_PTR is important.
return d_ptr->platformInst->getInstanceProcAddr(name);
}
/*!
\return the platform Vulkan instance corresponding to this QVulkanInstance.
\internal
*/
QPlatformVulkanInstance *QVulkanInstance::handle() const
{
return d_ptr->platformInst.data();
}
/*!
\return the corresponding QVulkanFunctions object that exposes the core
Vulkan command set, excluding device level functions, and is guaranteed to
be functional cross-platform.
\note The returned object is owned and managed by the QVulkanInstance. Do
not destroy or alter it.
\sa deviceFunctions()
*/
QVulkanFunctions *QVulkanInstance::functions() const
{
return d_ptr->funcs.data();
}
/*!
\return the QVulkanDeviceFunctions object that exposes the device level
core Vulkan command set and is guaranteed to be functional cross-platform.
\note The Vulkan functions in the returned object must only be called with
\a device or a child object (VkQueue, VkCommandBuffer) of \a device as
their first parameter. This is because these functions are resolved via
\l{https://www.khronos.org/registry/vulkan/specs/1.0/man/html/vkGetDeviceProcAddr.html}{vkGetDeviceProcAddr}
in order to avoid the potential overhead of internal dispatching.
\note The returned object is owned and managed by the QVulkanInstance. Do
not destroy or alter it.
\note The object is cached so calling this function with the same \a device
again is a cheap operation. However, when the device gets destroyed, it is up
to the application to notify the QVulkanInstance by calling
resetDeviceFunctions().
\sa functions(), resetDeviceFunctions()
*/
QVulkanDeviceFunctions *QVulkanInstance::deviceFunctions(VkDevice device)
{
QVulkanDeviceFunctions *&f(d_ptr->deviceFuncs[device]);
if (!f)
f = new QVulkanDeviceFunctions(this, device);
return f;
}
/*!
Invalidates and destroys the QVulkanDeviceFunctions object for the given
\a device.
This function must be called when a VkDevice, for which deviceFunctions()
was called, gets destroyed while the application intends to continue
running, possibly creating a new logical Vulkan device later on.
There is no need to call this before destroying the QVulkanInstance since
clean up is then performed automatically.
\sa deviceFunctions()
*/
void QVulkanInstance::resetDeviceFunctions(VkDevice device)
{
QVulkanDeviceFunctions *&f(d_ptr->deviceFuncs[device]);
delete f;
f = nullptr;
}
/*!
Creates or retrieves the already existing \c{VkSurfaceKHR} handle for the
given \a window.
\return the Vulkan surface handle or 0 when failed.
*/
VkSurfaceKHR QVulkanInstance::surfaceForWindow(QWindow *window)
{
QPlatformNativeInterface *nativeInterface = qGuiApp->platformNativeInterface();
// VkSurfaceKHR is non-dispatchable and maps to a pointer on x64 and a uint64 on x86.
// Therefore a pointer is returned from the platform plugin, not the value itself.
void *p = nativeInterface->nativeResourceForWindow(QByteArrayLiteral("vkSurface"), window);
return p ? *static_cast<VkSurfaceKHR *>(p) : 0;
}
/*!
\return true if the queue family with \a queueFamilyIndex within the
\a physicalDevice supports presenting to \a window.
Call this function when examining the queues of a given Vulkan device, in
order to decide which queue can be used for performing presentation.
*/
bool QVulkanInstance::supportsPresent(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, QWindow *window)
{
return d_ptr->platformInst->supportsPresent(physicalDevice, queueFamilyIndex, window);
}
/*!
This function should be called by the application's renderer after queuing
a present operation for \a window.
While on some platforms this will be a no-op, some may perform windowing
system dependent synchronization. For example, on X11 this will update
\c{_NET_WM_SYNC_REQUEST_COUNTER}.
*/
void QVulkanInstance::presentQueued(QWindow *window)
{
d_ptr->platformInst->presentQueued(window);
}
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const QVulkanLayer &layer)
{
QDebugStateSaver saver(dbg);
dbg.nospace() << "QVulkanLayer(" << layer.name << " " << layer.version
<< " " << layer.specVersion << " " << layer.description << ")";
return dbg;
}
QDebug operator<<(QDebug dbg, const QVulkanExtension &extension)
{
QDebugStateSaver saver(dbg);
dbg.nospace() << "QVulkanExtension(" << extension.name << " " << extension.version << ")";
return dbg;
}
#endif
QT_END_NAMESPACE

View File

@ -0,0 +1,163 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtGui module 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$
**
****************************************************************************/
#ifndef QVULKANINSTANCE_H
#define QVULKANINSTANCE_H
#include <QtGui/qtguiglobal.h>
#if QT_CONFIG(vulkan)
#ifndef VK_NO_PROTOTYPES
#define VK_NO_PROTOTYPES
#endif
#include <vulkan/vulkan.h>
#include <QtCore/qscopedpointer.h>
#include <QtCore/qvector.h>
#include <QtCore/qbytearraylist.h>
#include <QtCore/qversionnumber.h>
#include <QtCore/qdebug.h>
QT_BEGIN_NAMESPACE
class QVulkanInstancePrivate;
class QPlatformVulkanInstance;
class QVulkanFunctions;
class QVulkanDeviceFunctions;
class QWindow;
struct Q_GUI_EXPORT QVulkanLayer
{
QByteArray name;
uint32_t version;
QVersionNumber specVersion;
QByteArray description;
bool operator==(const QVulkanLayer &other) const {
return name == other.name && version == other.version && specVersion == other.specVersion;
}
};
struct Q_GUI_EXPORT QVulkanExtension
{
QByteArray name;
uint32_t version;
bool operator==(const QVulkanExtension &other) const {
return name == other.name && version == other.version;
}
};
#ifndef QT_NO_DEBUG_STREAM
Q_GUI_EXPORT QDebug operator<<(QDebug, const QVulkanLayer &);
Q_GUI_EXPORT QDebug operator<<(QDebug, const QVulkanExtension &);
#endif
template<typename T>
class QVulkanInfoVector : public QVector<T>
{
public:
bool contains(const QByteArray &name) const {
return std::any_of(this->cbegin(), this->cend(), [&](const T &entry) {
return entry.name == name; });
}
bool contains(const QByteArray &name, int minVersion) const {
return std::any_of(this->cbegin(), this->cend(), [&](const T &entry) {
return entry.name == name && entry.version >= minVersion; });
}
};
class Q_GUI_EXPORT QVulkanInstance
{
public:
QVulkanInstance();
~QVulkanInstance();
enum Flag {
NoDebugOutputRedirect = 0x01
};
Q_DECLARE_FLAGS(Flags, Flag)
QVulkanInfoVector<QVulkanLayer> supportedLayers();
QVulkanInfoVector<QVulkanExtension> supportedExtensions();
void setVkInstance(VkInstance existingVkInstance);
void setFlags(Flags flags);
void setLayers(const QByteArrayList &layers);
void setExtensions(const QByteArrayList &extensions);
void setApiVersion(const QVersionNumber &vulkanVersion);
bool create();
void destroy();
bool isValid() const;
VkResult errorCode() const;
VkInstance vkInstance() const;
Flags flags() const;
QByteArrayList layers() const;
QByteArrayList extensions() const;
QVersionNumber apiVersion() const;
PFN_vkVoidFunction getInstanceProcAddr(const char *name);
QPlatformVulkanInstance *handle() const;
QVulkanFunctions *functions() const;
QVulkanDeviceFunctions *deviceFunctions(VkDevice device);
void resetDeviceFunctions(VkDevice device);
static VkSurfaceKHR surfaceForWindow(QWindow *window);
bool supportsPresent(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, QWindow *window);
void presentQueued(QWindow *window);
private:
QScopedPointer<QVulkanInstancePrivate> d_ptr;
Q_DISABLE_COPY(QVulkanInstance)
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QVulkanInstance::Flags)
QT_END_NAMESPACE
#endif // QT_CONFIG(vulkan)
#endif // QVULKANINSTANCE_H

5269
src/gui/vulkan/vk.xml Normal file

File diff suppressed because it is too large Load Diff

47
src/gui/vulkan/vulkan.pri Normal file
View File

@ -0,0 +1,47 @@
qtConfig(vulkan) {
HEADERS += \
vulkan/qvulkaninstance.h \
vulkan/qplatformvulkaninstance.h
SOURCES += \
vulkan/qvulkaninstance.cpp \
vulkan/qplatformvulkaninstance.cpp \
vulkan/qvulkanfunctions.cpp
# Applications must inherit the Vulkan header include path.
QMAKE_USE += vulkan/nolink
}
# Generate qvulkanfunctions.h, qvulkanfunctions_p.h, qvulkanfunctions_p.cpp
QMAKE_QVKGEN_INPUT = vulkan/vk.xml
QMAKE_QVKGEN_LICENSE_HEADER = $$QT_SOURCE_TREE/header.LGPL
qtPrepareTool(QMAKE_QVKGEN, qvkgen)
qvkgen_h.commands = $$QMAKE_QVKGEN ${QMAKE_FILE_IN} $$shell_quote($$QMAKE_QVKGEN_LICENSE_HEADER) ${QMAKE_FILE_OUT_PATH}/${QMAKE_FILE_OUT_BASE}
qvkgen_h.output = vulkan/qvulkanfunctions.h
qvkgen_h.input = QMAKE_QVKGEN_INPUT
qtConfig(vulkan): \
qvkgen_h.variable_out = HEADERS
else: \
qvkgen_h.CONFIG += target_predeps no_link
QMAKE_EXTRA_COMPILERS += qvkgen_h
qvkgen_ph.commands = $$escape_expand(\\n)
qvkgen_ph.output = vulkan/qvulkanfunctions_p.h
qvkgen_ph.input = QMAKE_QVKGEN_INPUT
qvkgen_ph.depends += vulkan/qvulkanfunctions.h
qtConfig(vulkan): \
qvkgen_ph.variable_out = HEADERS
else: \
qvkgen_ph.CONFIG += target_predeps no_link
QMAKE_EXTRA_COMPILERS += qvkgen_ph
qvkgen_pimpl.commands = $$escape_expand(\\n)
qvkgen_pimpl.output = vulkan/qvulkanfunctions_p.cpp
qvkgen_pimpl.input = QMAKE_QVKGEN_INPUT
qvkgen_pimpl.depends += vulkan/qvulkanfunctions_p.h
qtConfig(vulkan): \
qvkgen_pimpl.variable_out = SOURCES
else: \
qvkgen_pimpl.CONFIG += target_predeps no_link
QMAKE_EXTRA_COMPILERS += qvkgen_pimpl

View File

@ -42,3 +42,6 @@ darwin {
macos: \ macos: \
SUBDIRS += cglconvenience SUBDIRS += cglconvenience
} }
qtConfig(vulkan): \
SUBDIRS += vkconvenience

View File

@ -0,0 +1,357 @@
/****************************************************************************
**
** Copyright (C) 2017 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 "qbasicvulkanplatforminstance_p.h"
#include <QLibrary>
#include <QCoreApplication>
#include <QVector>
#include <QLoggingCategory>
QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcVk, "qt.vulkan")
/*!
\class QBasicPlatformVulkanInstance
\brief A generic platform Vulkan instance implementation.
\since 5.10
\internal
\ingroup qpa
Implements QPlatformVulkanInstance, serving as a base for platform-specific
implementations. The library loading and any WSI-specifics are excluded.
Subclasses are expected to call init() from their constructor and
initInstance() from their createOrAdoptInstance() implementation.
*/
QBasicPlatformVulkanInstance::QBasicPlatformVulkanInstance()
: m_vkInst(VK_NULL_HANDLE),
m_vkGetInstanceProcAddr(nullptr),
m_ownsVkInst(false),
m_errorCode(VK_SUCCESS),
m_debugCallback(0)
{
}
QBasicPlatformVulkanInstance::~QBasicPlatformVulkanInstance()
{
if (!m_vkInst)
return;
if (m_debugCallback && m_vkDestroyDebugReportCallbackEXT)
m_vkDestroyDebugReportCallbackEXT(m_vkInst, m_debugCallback, nullptr);
if (m_ownsVkInst)
m_vkDestroyInstance(m_vkInst, nullptr);
}
void QBasicPlatformVulkanInstance::init(QLibrary *lib)
{
if (m_vkGetInstanceProcAddr)
return;
qCDebug(lcVk, "Vulkan init (%s)", qPrintable(lib->fileName()));
// While not strictly required with every implementation, try to follow the spec
// and do not rely on core functions being exported.
//
// 1. dlsym vkGetInstanceProcAddr
// 2. with a special null instance resolve vkCreateInstance and vkEnumerateInstance*
// 3. all other core functions are resolved with the created instance
m_vkGetInstanceProcAddr = reinterpret_cast<PFN_vkGetInstanceProcAddr>(lib->resolve("vkGetInstanceProcAddr"));
if (!m_vkGetInstanceProcAddr) {
qWarning("Failed to find vkGetInstanceProcAddr");
return;
}
m_vkCreateInstance = reinterpret_cast<PFN_vkCreateInstance>(m_vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkCreateInstance"));
if (!m_vkCreateInstance) {
qWarning("Failed to find vkCreateInstance");
return;
}
m_vkEnumerateInstanceLayerProperties = reinterpret_cast<PFN_vkEnumerateInstanceLayerProperties>(
m_vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkEnumerateInstanceLayerProperties"));
if (!m_vkEnumerateInstanceLayerProperties) {
qWarning("Failed to find vkEnumerateInstanceLayerProperties");
return;
}
m_vkEnumerateInstanceExtensionProperties = reinterpret_cast<PFN_vkEnumerateInstanceExtensionProperties>(
m_vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkEnumerateInstanceExtensionProperties"));
if (!m_vkEnumerateInstanceExtensionProperties) {
qWarning("Failed to find vkEnumerateInstanceExtensionProperties");
return;
}
uint32_t layerCount = 0;
m_vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
if (layerCount) {
QVector<VkLayerProperties> layerProps(layerCount);
m_vkEnumerateInstanceLayerProperties(&layerCount, layerProps.data());
m_supportedLayers.reserve(layerCount);
for (const VkLayerProperties &p : qAsConst(layerProps)) {
QVulkanLayer layer;
layer.name = p.layerName;
layer.version = p.implementationVersion;
layer.specVersion = QVersionNumber(VK_VERSION_MAJOR(p.specVersion),
VK_VERSION_MINOR(p.specVersion),
VK_VERSION_PATCH(p.specVersion));
layer.description = p.description;
m_supportedLayers.append(layer);
}
}
qCDebug(lcVk) << "Supported Vulkan instance layers:" << m_supportedLayers;
uint32_t extCount = 0;
m_vkEnumerateInstanceExtensionProperties(nullptr, &extCount, nullptr);
if (extCount) {
QVector<VkExtensionProperties> extProps(extCount);
m_vkEnumerateInstanceExtensionProperties(nullptr, &extCount, extProps.data());
m_supportedExtensions.reserve(extCount);
for (const VkExtensionProperties &p : qAsConst(extProps)) {
QVulkanExtension ext;
ext.name = p.extensionName;
ext.version = p.specVersion;
m_supportedExtensions.append(ext);
}
}
qDebug(lcVk) << "Supported Vulkan instance extensions:" << m_supportedExtensions;
}
QVulkanInfoVector<QVulkanLayer> QBasicPlatformVulkanInstance::supportedLayers() const
{
return m_supportedLayers;
}
QVulkanInfoVector<QVulkanExtension> QBasicPlatformVulkanInstance::supportedExtensions() const
{
return m_supportedExtensions;
}
void QBasicPlatformVulkanInstance::initInstance(QVulkanInstance *instance, const QByteArrayList &extraExts)
{
if (!m_vkGetInstanceProcAddr) {
qWarning("initInstance: No Vulkan library available");
return;
}
m_vkInst = instance->vkInstance(); // when non-null we are adopting an existing instance
QVulkanInstance::Flags flags = instance->flags();
m_enabledLayers = instance->layers();
m_enabledExtensions = instance->extensions();
if (!m_vkInst) {
VkApplicationInfo appInfo;
memset(&appInfo, 0, sizeof(appInfo));
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
QByteArray appName = QCoreApplication::applicationName().toUtf8();
appInfo.pApplicationName = appName.constData();
const QVersionNumber apiVersion = instance->apiVersion();
if (!apiVersion.isNull()) {
appInfo.apiVersion = VK_MAKE_VERSION(apiVersion.majorVersion(),
apiVersion.minorVersion(),
apiVersion.microVersion());
}
if (!flags.testFlag(QVulkanInstance::NoDebugOutputRedirect))
m_enabledExtensions.append("VK_EXT_debug_report");
m_enabledExtensions.append("VK_KHR_surface");
for (const QByteArray &ext : extraExts)
m_enabledExtensions.append(ext);
// No clever stuff with QSet and friends: the order for layers matters
// and the user-provided order must be kept.
for (int i = 0; i < m_enabledLayers.count(); ++i) {
const QByteArray &layerName(m_enabledLayers[i]);
if (!m_supportedLayers.contains(layerName))
m_enabledLayers.removeAt(i--);
}
qDebug(lcVk) << "Enabling Vulkan instance layers:" << m_enabledLayers;
for (int i = 0; i < m_enabledExtensions.count(); ++i) {
const QByteArray &extName(m_enabledExtensions[i]);
if (!m_supportedExtensions.contains(extName))
m_enabledExtensions.removeAt(i--);
}
qDebug(lcVk) << "Enabling Vulkan instance extensions:" << m_enabledExtensions;
VkInstanceCreateInfo instInfo;
memset(&instInfo, 0, sizeof(instInfo));
instInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
instInfo.pApplicationInfo = &appInfo;
QVector<const char *> layerNameVec;
for (const QByteArray &ba : qAsConst(m_enabledLayers))
layerNameVec.append(ba.constData());
if (!layerNameVec.isEmpty()) {
instInfo.enabledLayerCount = layerNameVec.count();
instInfo.ppEnabledLayerNames = layerNameVec.constData();
}
QVector<const char *> extNameVec;
for (const QByteArray &ba : qAsConst(m_enabledExtensions))
extNameVec.append(ba.constData());
if (!extNameVec.isEmpty()) {
instInfo.enabledExtensionCount = extNameVec.count();
instInfo.ppEnabledExtensionNames = extNameVec.constData();
}
m_errorCode = m_vkCreateInstance(&instInfo, nullptr, &m_vkInst);
if (m_errorCode != VK_SUCCESS || !m_vkInst) {
qWarning("Failed to create Vulkan instance: %d", m_errorCode);
return;
}
m_vkDestroyInstance = reinterpret_cast<PFN_vkDestroyInstance>(m_vkGetInstanceProcAddr(m_vkInst, "vkDestroyInstance"));
if (!m_vkDestroyInstance) {
qWarning("Failed to find vkDestroyInstance");
m_vkInst = VK_NULL_HANDLE;
return;
}
m_ownsVkInst = true;
}
m_getPhysDevSurfaceSupport = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceSupportKHR>(
m_vkGetInstanceProcAddr(m_vkInst, "vkGetPhysicalDeviceSurfaceSupportKHR"));
if (!m_getPhysDevSurfaceSupport)
qWarning("Failed to find vkGetPhysicalDeviceSurfaceSupportKHR");
if (!flags.testFlag(QVulkanInstance::NoDebugOutputRedirect))
setupDebugOutput();
}
bool QBasicPlatformVulkanInstance::isValid() const
{
return m_vkInst != VK_NULL_HANDLE;
}
VkResult QBasicPlatformVulkanInstance::errorCode() const
{
return m_errorCode;
}
VkInstance QBasicPlatformVulkanInstance::vkInstance() const
{
return m_vkInst;
}
QByteArrayList QBasicPlatformVulkanInstance::enabledLayers() const
{
return m_enabledLayers;
}
QByteArrayList QBasicPlatformVulkanInstance::enabledExtensions() const
{
return m_enabledExtensions;
}
PFN_vkVoidFunction QBasicPlatformVulkanInstance::getInstanceProcAddr(const char *name)
{
if (!name)
return nullptr;
const bool needsNullInstance = !strcmp(name, "vkEnumerateInstanceLayerProperties")
|| !strcmp(name, "vkEnumerateInstanceExtensionProperties");
return m_vkGetInstanceProcAddr(needsNullInstance ? 0 : m_vkInst, name);
}
bool QBasicPlatformVulkanInstance::supportsPresent(VkPhysicalDevice physicalDevice,
uint32_t queueFamilyIndex,
QWindow *window)
{
if (!m_getPhysDevSurfaceSupport)
return true;
VkSurfaceKHR surface = QVulkanInstance::surfaceForWindow(window);
VkBool32 supported = false;
m_getPhysDevSurfaceSupport(physicalDevice, queueFamilyIndex, surface, &supported);
return supported;
}
static VKAPI_ATTR VkBool32 VKAPI_CALL defaultDebugCallbackFunc(VkDebugReportFlagsEXT flags,
VkDebugReportObjectTypeEXT objectType,
uint64_t object,
size_t location,
int32_t messageCode,
const char *pLayerPrefix,
const char *pMessage,
void *pUserData)
{
Q_UNUSED(flags);
Q_UNUSED(objectType);
Q_UNUSED(object);
Q_UNUSED(location);
Q_UNUSED(pUserData);
// not categorized, just route to plain old qDebug
qDebug("vkDebug: %s: %d: %s", pLayerPrefix, messageCode, pMessage);
return VK_FALSE;
}
void QBasicPlatformVulkanInstance::setupDebugOutput()
{
if (!m_enabledExtensions.contains("VK_EXT_debug_report"))
return;
PFN_vkCreateDebugReportCallbackEXT createDebugReportCallback = reinterpret_cast<PFN_vkCreateDebugReportCallbackEXT>(
m_vkGetInstanceProcAddr(m_vkInst, "vkCreateDebugReportCallbackEXT"));
m_vkDestroyDebugReportCallbackEXT = reinterpret_cast<PFN_vkDestroyDebugReportCallbackEXT>(
m_vkGetInstanceProcAddr(m_vkInst, "vkDestroyDebugReportCallbackEXT"));
VkDebugReportCallbackCreateInfoEXT dbgCallbackInfo;
memset(&dbgCallbackInfo, 0, sizeof(dbgCallbackInfo));
dbgCallbackInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT;
dbgCallbackInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT
| VK_DEBUG_REPORT_WARNING_BIT_EXT
| VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT;
dbgCallbackInfo.pfnCallback = defaultDebugCallbackFunc;
VkResult err = createDebugReportCallback(m_vkInst, &dbgCallbackInfo, nullptr, &m_debugCallback);
if (err != VK_SUCCESS)
qWarning("Failed to create debug report callback: %d", err);
}
QT_END_NAMESPACE

View File

@ -0,0 +1,106 @@
/****************************************************************************
**
** Copyright (C) 2017 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$
**
****************************************************************************/
#ifndef QBASICVULKANPLATFORMINSTANCE_P_H
#define QBASICVULKANPLATFORMINSTANCE_P_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 <qpa/qplatformvulkaninstance.h>
QT_BEGIN_NAMESPACE
class QLibrary;
class QBasicPlatformVulkanInstance : public QPlatformVulkanInstance
{
public:
QBasicPlatformVulkanInstance();
~QBasicPlatformVulkanInstance();
QVulkanInfoVector<QVulkanLayer> supportedLayers() const override;
QVulkanInfoVector<QVulkanExtension> supportedExtensions() const override;
bool isValid() const override;
VkResult errorCode() const override;
VkInstance vkInstance() const override;
QByteArrayList enabledLayers() const override;
QByteArrayList enabledExtensions() const override;
PFN_vkVoidFunction getInstanceProcAddr(const char *name) override;
bool supportsPresent(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, QWindow *window) override;
protected:
void init(QLibrary *lib);
void initInstance(QVulkanInstance *instance, const QByteArrayList &extraExts);
VkInstance m_vkInst;
PFN_vkGetInstanceProcAddr m_vkGetInstanceProcAddr;
PFN_vkGetPhysicalDeviceSurfaceSupportKHR m_getPhysDevSurfaceSupport;
private:
void setupDebugOutput();
bool m_ownsVkInst;
VkResult m_errorCode;
QVulkanInfoVector<QVulkanLayer> m_supportedLayers;
QVulkanInfoVector<QVulkanExtension> m_supportedExtensions;
QByteArrayList m_enabledLayers;
QByteArrayList m_enabledExtensions;
PFN_vkCreateInstance m_vkCreateInstance;
PFN_vkEnumerateInstanceLayerProperties m_vkEnumerateInstanceLayerProperties;
PFN_vkEnumerateInstanceExtensionProperties m_vkEnumerateInstanceExtensionProperties;
PFN_vkDestroyInstance m_vkDestroyInstance;
VkDebugReportCallbackEXT m_debugCallback;
PFN_vkDestroyDebugReportCallbackEXT m_vkDestroyDebugReportCallbackEXT;
};
QT_END_NAMESPACE
#endif // QBASICVULKANPLATFORMINSTANCE_P_H

View File

@ -0,0 +1,16 @@
TARGET = QtVulkanSupport
MODULE = vulkan_support
QT = core-private gui-private
CONFIG += static internal_module
DEFINES += QT_NO_CAST_FROM_ASCII
PRECOMPILED_HEADER = ../../corelib/global/qt_pch.h
SOURCES += \
qbasicvulkanplatforminstance.cpp
HEADERS += \
qbasicvulkanplatforminstance_p.h
load(qt_module)

View File

@ -11,6 +11,8 @@ QT += \
eventdispatcher_support-private accessibility_support-private \ eventdispatcher_support-private accessibility_support-private \
fontdatabase_support-private egl_support-private fontdatabase_support-private egl_support-private
qtConfig(vulkan): QT += vulkan_support-private
OTHER_FILES += $$PWD/android.json OTHER_FILES += $$PWD/android.json
INCLUDEPATH += \ INCLUDEPATH += \
@ -78,6 +80,13 @@ HEADERS += $$PWD/qandroidplatformintegration.h \
qtConfig(android-style-assets): SOURCES += $$PWD/extract.cpp qtConfig(android-style-assets): SOURCES += $$PWD/extract.cpp
else: SOURCES += $$PWD/extract-dummy.cpp else: SOURCES += $$PWD/extract-dummy.cpp
qtConfig(vulkan) {
SOURCES += $$PWD/qandroidplatformvulkaninstance.cpp \
$$PWD/qandroidplatformvulkanwindow.cpp
HEADERS += $$PWD/qandroidplatformvulkaninstance.h \
$$PWD/qandroidplatformvulkanwindow.h
}
PLUGIN_TYPE = platforms PLUGIN_TYPE = platforms
load(qt_plugin) load(qt_plugin)

View File

@ -69,6 +69,11 @@
#include <QtPlatformHeaders/QEGLNativeContext> #include <QtPlatformHeaders/QEGLNativeContext>
#if QT_CONFIG(vulkan)
#include "qandroidplatformvulkanwindow.h"
#include "qandroidplatformvulkaninstance.h"
#endif
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
int QAndroidPlatformIntegration::m_defaultGeometryWidth = 320; int QAndroidPlatformIntegration::m_defaultGeometryWidth = 320;
@ -121,6 +126,23 @@ void *QAndroidPlatformNativeInterface::nativeResourceForIntegration(const QByteA
return 0; return 0;
} }
void *QAndroidPlatformNativeInterface::nativeResourceForWindow(const QByteArray &resource, QWindow *window)
{
#if QT_CONFIG(vulkan)
if (resource == "vkSurface") {
if (window->surfaceType() == QSurface::VulkanSurface) {
QAndroidPlatformVulkanWindow *w = static_cast<QAndroidPlatformVulkanWindow *>(window->handle());
// return a pointer to the VkSurfaceKHR, not the value
return w ? w->vkSurface() : nullptr;
}
}
#else
Q_UNUSED(resource);
Q_UNUSED(window);
#endif
return nullptr;
}
QAndroidPlatformIntegration::QAndroidPlatformIntegration(const QStringList &paramList) QAndroidPlatformIntegration::QAndroidPlatformIntegration(const QStringList &paramList)
: m_touchDevice(nullptr) : m_touchDevice(nullptr)
#ifndef QT_NO_ACCESSIBILITY #ifndef QT_NO_ACCESSIBILITY
@ -286,6 +308,11 @@ QPlatformWindow *QAndroidPlatformIntegration::createPlatformWindow(QWindow *wind
if (!QtAndroid::activity()) if (!QtAndroid::activity())
return nullptr; return nullptr;
#if QT_CONFIG(vulkan)
if (window->surfaceType() == QSurface::VulkanSurface)
return new QAndroidPlatformVulkanWindow(window);
#endif
return new QAndroidPlatformOpenGLWindow(window, m_eglDisplay); return new QAndroidPlatformOpenGLWindow(window, m_eglDisplay);
} }
@ -426,4 +453,13 @@ void QAndroidPlatformIntegration::setScreenSize(int width, int height)
QMetaObject::invokeMethod(m_primaryScreen, "setSize", Qt::AutoConnection, Q_ARG(QSize, QSize(width, height))); QMetaObject::invokeMethod(m_primaryScreen, "setSize", Qt::AutoConnection, Q_ARG(QSize, QSize(width, height)));
} }
#if QT_CONFIG(vulkan)
QPlatformVulkanInstance *QAndroidPlatformIntegration::createPlatformVulkanInstance(QVulkanInstance *instance) const
{
return new QAndroidPlatformVulkanInstance(instance);
}
#endif // QT_CONFIG(vulkan)
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -40,6 +40,8 @@
#ifndef QANDROIDPLATFORMINTERATION_H #ifndef QANDROIDPLATFORMINTERATION_H
#define QANDROIDPLATFORMINTERATION_H #define QANDROIDPLATFORMINTERATION_H
#include <QtGui/qtguiglobal.h>
#include <qpa/qplatformintegration.h> #include <qpa/qplatformintegration.h>
#include <qpa/qplatformmenu.h> #include <qpa/qplatformmenu.h>
#include <qpa/qplatformnativeinterface.h> #include <qpa/qplatformnativeinterface.h>
@ -64,6 +66,7 @@ class QAndroidPlatformNativeInterface: public QPlatformNativeInterface
{ {
public: public:
void *nativeResourceForIntegration(const QByteArray &resource) override; void *nativeResourceForIntegration(const QByteArray &resource) override;
void *nativeResourceForWindow(const QByteArray &resource, QWindow *window) override;
std::shared_ptr<AndroidStyle> m_androidStyle; std::shared_ptr<AndroidStyle> m_androidStyle;
}; };
@ -124,6 +127,10 @@ public:
void setTouchDevice(QTouchDevice *touchDevice) { m_touchDevice = touchDevice; } void setTouchDevice(QTouchDevice *touchDevice) { m_touchDevice = touchDevice; }
static void setDefaultApplicationState(Qt::ApplicationState applicationState) { m_defaultApplicationState = applicationState; } static void setDefaultApplicationState(Qt::ApplicationState applicationState) { m_defaultApplicationState = applicationState; }
#if QT_CONFIG(vulkan)
QPlatformVulkanInstance *createPlatformVulkanInstance(QVulkanInstance *instance) const override;
#endif
private: private:
EGLDisplay m_eglDisplay; EGLDisplay m_eglDisplay;
QTouchDevice *m_touchDevice; QTouchDevice *m_touchDevice;

View File

@ -0,0 +1,66 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of 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 "qandroidplatformvulkaninstance.h"
QT_BEGIN_NAMESPACE
QAndroidPlatformVulkanInstance::QAndroidPlatformVulkanInstance(QVulkanInstance *instance)
: m_instance(instance)
{
m_lib.setFileName(QStringLiteral("vulkan"));
if (!m_lib.load()) {
qWarning("Failed to load %s", qPrintable(m_lib.fileName()));
return;
}
init(&m_lib);
}
void QAndroidPlatformVulkanInstance::createOrAdoptInstance()
{
initInstance(m_instance, QByteArrayList() << QByteArrayLiteral("VK_KHR_android_surface"));
}
QAndroidPlatformVulkanInstance::~QAndroidPlatformVulkanInstance()
{
}
QT_END_NAMESPACE

View File

@ -0,0 +1,63 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of 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$
**
****************************************************************************/
#ifndef QANDROIDPLATFORMVULKANINSTANCE_H
#define QANDROIDPLATFORMVULKANINSTANCE_H
#include <QtVulkanSupport/private/qbasicvulkanplatforminstance_p.h>
#include <QLibrary>
QT_BEGIN_NAMESPACE
class QAndroidPlatformVulkanInstance : public QBasicPlatformVulkanInstance
{
public:
QAndroidPlatformVulkanInstance(QVulkanInstance *instance);
~QAndroidPlatformVulkanInstance();
void createOrAdoptInstance() override;
private:
QVulkanInstance *m_instance;
QLibrary m_lib;
};
QT_END_NAMESPACE
#endif // QANDROIDPLATFORMVULKANINSTANCE_H

View File

@ -0,0 +1,210 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of 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 "qandroidplatformvulkanwindow.h"
#include "qandroidplatformscreen.h"
#include "androidjnimain.h"
#include "qandroideventdispatcher.h"
#include "androiddeadlockprotector.h"
#include <QSurfaceFormat>
#include <qpa/qwindowsysteminterface.h>
#include <qpa/qplatformscreen.h>
#include <android/native_window.h>
#include <android/native_window_jni.h>
QT_BEGIN_NAMESPACE
QAndroidPlatformVulkanWindow::QAndroidPlatformVulkanWindow(QWindow *window)
: QAndroidPlatformWindow(window),
m_nativeSurfaceId(-1),
m_nativeWindow(nullptr),
m_vkSurface(0),
m_createVkSurface(nullptr),
m_destroyVkSurface(nullptr)
{
}
QAndroidPlatformVulkanWindow::~QAndroidPlatformVulkanWindow()
{
m_surfaceWaitCondition.wakeOne();
lockSurface();
if (m_nativeSurfaceId != -1)
QtAndroid::destroySurface(m_nativeSurfaceId);
clearSurface();
unlockSurface();
}
void QAndroidPlatformVulkanWindow::setGeometry(const QRect &rect)
{
if (rect == geometry())
return;
m_oldGeometry = geometry();
QAndroidPlatformWindow::setGeometry(rect);
if (m_nativeSurfaceId != -1)
QtAndroid::setSurfaceGeometry(m_nativeSurfaceId, rect);
QRect availableGeometry = screen()->availableGeometry();
if (m_oldGeometry.width() == 0
&& m_oldGeometry.height() == 0
&& rect.width() > 0
&& rect.height() > 0
&& availableGeometry.width() > 0
&& availableGeometry.height() > 0) {
QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), rect.size()));
}
if (rect.topLeft() != m_oldGeometry.topLeft())
repaint(QRegion(rect));
}
void QAndroidPlatformVulkanWindow::applicationStateChanged(Qt::ApplicationState state)
{
QAndroidPlatformWindow::applicationStateChanged(state);
if (state <= Qt::ApplicationHidden) {
lockSurface();
if (m_nativeSurfaceId != -1) {
QtAndroid::destroySurface(m_nativeSurfaceId);
m_nativeSurfaceId = -1;
}
clearSurface();
unlockSurface();
}
}
QSurfaceFormat QAndroidPlatformVulkanWindow::format() const
{
return window()->requestedFormat();
}
void QAndroidPlatformVulkanWindow::clearSurface()
{
if (m_vkSurface && m_destroyVkSurface) {
m_destroyVkSurface(window()->vulkanInstance()->vkInstance(), m_vkSurface, nullptr);
m_vkSurface = 0;
}
if (m_nativeWindow) {
ANativeWindow_release(m_nativeWindow);
m_nativeWindow = nullptr;
}
}
void QAndroidPlatformVulkanWindow::sendExpose()
{
QRect availableGeometry = screen()->availableGeometry();
if (geometry().width() > 0 && geometry().height() > 0 && availableGeometry.width() > 0 && availableGeometry.height() > 0)
QWindowSystemInterface::handleExposeEvent(window(), QRegion(QRect(QPoint(), geometry().size())));
}
void QAndroidPlatformVulkanWindow::surfaceChanged(JNIEnv *jniEnv, jobject surface, int w, int h)
{
Q_UNUSED(jniEnv);
Q_UNUSED(w);
Q_UNUSED(h);
lockSurface();
m_androidSurfaceObject = surface;
if (surface)
m_surfaceWaitCondition.wakeOne();
unlockSurface();
if (surface)
sendExpose();
}
VkSurfaceKHR *QAndroidPlatformVulkanWindow::vkSurface()
{
if (QAndroidEventDispatcherStopper::stopped())
return &m_vkSurface;
bool needsExpose = false;
if (!m_vkSurface) {
clearSurface();
QMutexLocker lock(&m_surfaceMutex);
if (m_nativeSurfaceId == -1) {
AndroidDeadlockProtector protector;
if (!protector.acquire())
return &m_vkSurface;
const bool windowStaysOnTop = bool(window()->flags() & Qt::WindowStaysOnTopHint);
m_nativeSurfaceId = QtAndroid::createSurface(this, geometry(), windowStaysOnTop, 32);
m_surfaceWaitCondition.wait(&m_surfaceMutex);
}
if (m_nativeSurfaceId == -1 || !m_androidSurfaceObject.isValid())
return &m_vkSurface;
QJNIEnvironmentPrivate env;
m_nativeWindow = ANativeWindow_fromSurface(env, m_androidSurfaceObject.object());
VkAndroidSurfaceCreateInfoKHR surfaceInfo;
memset(&surfaceInfo, 0, sizeof(surfaceInfo));
surfaceInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
surfaceInfo.window = m_nativeWindow;
QVulkanInstance *inst = window()->vulkanInstance();
if (!inst) {
qWarning("Attempted to create Vulkan surface without an instance; was QWindow::setVulkanInstance() called?");
return &m_vkSurface;
}
if (!m_createVkSurface) {
m_createVkSurface = reinterpret_cast<PFN_vkCreateAndroidSurfaceKHR>(
inst->getInstanceProcAddr("vkCreateAndroidSurfaceKHR"));
}
if (!m_destroyVkSurface) {
m_destroyVkSurface = reinterpret_cast<PFN_vkDestroySurfaceKHR>(
inst->getInstanceProcAddr("vkDestroySurfaceKHR"));
}
VkResult err = m_createVkSurface(inst->vkInstance(), &surfaceInfo, nullptr, &m_vkSurface);
if (err != VK_SUCCESS)
qWarning("Failed to create Android VkSurface: %d", err);
needsExpose = true;
}
if (needsExpose)
sendExpose();
return &m_vkSurface;
}
QT_END_NAMESPACE

View File

@ -0,0 +1,91 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of 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$
**
****************************************************************************/
#ifndef QANDROIDPLATFORMVULKANWINDOW_H
#define QANDROIDPLATFORMVULKANWINDOW_H
#if defined(VULKAN_H_) && !defined(VK_USE_PLATFORM_ANDROID_KHR)
#error "vulkan.h included without Android WSI"
#endif
#define VK_USE_PLATFORM_ANDROID_KHR
#include <QWaitCondition>
#include <QtCore/private/qjni_p.h>
#include "androidsurfaceclient.h"
#include "qandroidplatformwindow.h"
#include "qandroidplatformvulkaninstance.h"
QT_BEGIN_NAMESPACE
class QAndroidPlatformVulkanWindow : public QAndroidPlatformWindow, public AndroidSurfaceClient
{
public:
explicit QAndroidPlatformVulkanWindow(QWindow *window);
~QAndroidPlatformVulkanWindow();
void setGeometry(const QRect &rect) override;
QSurfaceFormat format() const override;
void applicationStateChanged(Qt::ApplicationState) override;
VkSurfaceKHR *vkSurface();
protected:
void surfaceChanged(JNIEnv *jniEnv, jobject surface, int w, int h) override;
private:
void sendExpose();
void clearSurface();
int m_nativeSurfaceId;
ANativeWindow *m_nativeWindow;
QJNIObjectPrivate m_androidSurfaceObject;
QWaitCondition m_surfaceWaitCondition;
QSurfaceFormat m_format;
QRect m_oldGeometry;
VkSurfaceKHR m_vkSurface;
PFN_vkCreateAndroidSurfaceKHR m_createVkSurface;
PFN_vkDestroySurfaceKHR m_destroyVkSurface;
};
QT_END_NAMESPACE
#endif // QANDROIDPLATFORMVULKANWINDOW_H

View File

@ -5,6 +5,8 @@ QT += \
eventdispatcher_support-private accessibility_support-private \ eventdispatcher_support-private accessibility_support-private \
fontdatabase_support-private theme_support-private fontdatabase_support-private theme_support-private
qtConfig(vulkan): QT += vulkan_support-private
LIBS += -ldwmapi -ld2d1 -ld3d11 -ldwrite -lVersion -lgdi32 LIBS += -ldwmapi -ld2d1 -ld3d11 -ldwrite -lVersion -lgdi32
include(../windows/windows.pri) include(../windows/windows.pri)

View File

@ -606,4 +606,11 @@ void QWindowsIntegration::beep() const
MessageBeep(MB_OK); // For QApplication MessageBeep(MB_OK); // For QApplication
} }
#if QT_CONFIG(vulkan)
QPlatformVulkanInstance *QWindowsIntegration::createPlatformVulkanInstance(QVulkanInstance *instance) const
{
return new QWindowsVulkanInstance(instance);
}
#endif
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -113,6 +113,10 @@ public:
QPlatformSessionManager *createPlatformSessionManager(const QString &id, const QString &key) const override; QPlatformSessionManager *createPlatformSessionManager(const QString &id, const QString &key) const override;
#endif #endif
#if QT_CONFIG(vulkan)
QPlatformVulkanInstance *createPlatformVulkanInstance(QVulkanInstance *instance) const override;
#endif
protected: protected:
virtual QWindowsWindow *createPlatformWindowHelper(QWindow *window, const QWindowsWindowData &) const; virtual QWindowsWindow *createPlatformWindowHelper(QWindow *window, const QWindowsWindowData &) const;

View File

@ -64,7 +64,8 @@ enum ResourceType {
HandleType, HandleType,
GlHandleType, GlHandleType,
GetDCType, GetDCType,
ReleaseDCType ReleaseDCType,
VkSurface
}; };
static int resourceType(const QByteArray &key) static int resourceType(const QByteArray &key)
@ -77,7 +78,8 @@ static int resourceType(const QByteArray &key)
"handle", "handle",
"glhandle", "glhandle",
"getdc", "getdc",
"releasedc" "releasedc",
"vkSurface"
}; };
const char ** const end = names + sizeof(names) / sizeof(names[0]); const char ** const end = names + sizeof(names) / sizeof(names[0]);
const char **result = std::find(names, end, key); const char **result = std::find(names, end, key);
@ -112,6 +114,12 @@ void *QWindowsNativeInterface::nativeResourceForWindow(const QByteArray &resourc
case QWindow::OpenGLSurface: case QWindow::OpenGLSurface:
case QWindow::OpenVGSurface: case QWindow::OpenVGSurface:
break; break;
#if QT_CONFIG(vulkan)
case QWindow::VulkanSurface:
if (type == VkSurface)
return bw->surface(nullptr, nullptr); // returns the address of the VkSurfaceKHR, not the value, as expected
break;
#endif
} }
qWarning("%s: Invalid key '%s' requested.", __FUNCTION__, resource.constData()); qWarning("%s: Invalid key '%s' requested.", __FUNCTION__, resource.constData());
return 0; return 0;

View File

@ -0,0 +1,136 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of 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 "qwindowsvulkaninstance.h"
QT_BEGIN_NAMESPACE
QWindowsVulkanInstance::QWindowsVulkanInstance(QVulkanInstance *instance)
: m_instance(instance),
m_getPhysDevPresSupport(nullptr),
m_createSurface(nullptr),
m_destroySurface(nullptr)
{
if (qEnvironmentVariableIsSet("QT_VULKAN_LIB"))
m_lib.setFileName(QString::fromUtf8(qgetenv("QT_VULKAN_LIB")));
else
m_lib.setFileName(QStringLiteral("vulkan-1"));
if (!m_lib.load()) {
qWarning("Failed to load %s: %s", qPrintable(m_lib.fileName()), qPrintable(m_lib.errorString()));
return;
}
init(&m_lib);
}
void QWindowsVulkanInstance::createOrAdoptInstance()
{
initInstance(m_instance, QByteArrayList() << QByteArrayLiteral("VK_KHR_win32_surface"));
if (!m_vkInst)
return;
m_getPhysDevPresSupport = reinterpret_cast<PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR>(
m_vkGetInstanceProcAddr(m_vkInst, "vkGetPhysicalDeviceWin32PresentationSupportKHR"));
if (!m_getPhysDevPresSupport)
qWarning("Failed to find vkGetPhysicalDeviceWin32PresentationSupportKHR");
}
QWindowsVulkanInstance::~QWindowsVulkanInstance()
{
}
bool QWindowsVulkanInstance::supportsPresent(VkPhysicalDevice physicalDevice,
uint32_t queueFamilyIndex,
QWindow *window)
{
if (!m_getPhysDevPresSupport || !m_getPhysDevSurfaceSupport)
return true;
bool ok = m_getPhysDevPresSupport(physicalDevice, queueFamilyIndex);
VkSurfaceKHR surface = QVulkanInstance::surfaceForWindow(window);
VkBool32 supported = false;
m_getPhysDevSurfaceSupport(physicalDevice, queueFamilyIndex, surface, &supported);
ok &= bool(supported);
return ok;
}
VkSurfaceKHR QWindowsVulkanInstance::createSurface(HWND win)
{
VkSurfaceKHR surface = 0;
if (!m_createSurface) {
m_createSurface = reinterpret_cast<PFN_vkCreateWin32SurfaceKHR>(
m_vkGetInstanceProcAddr(m_vkInst, "vkCreateWin32SurfaceKHR"));
}
if (!m_createSurface) {
qWarning("Failed to find vkCreateWin32SurfaceKHR");
return surface;
}
if (!m_destroySurface) {
m_destroySurface = reinterpret_cast<PFN_vkDestroySurfaceKHR>(
m_vkGetInstanceProcAddr(m_vkInst, "vkDestroySurfaceKHR"));
}
if (!m_destroySurface) {
qWarning("Failed to find vkDestroySurfaceKHR");
return surface;
}
VkWin32SurfaceCreateInfoKHR surfaceInfo;
memset(&surfaceInfo, 0, sizeof(surfaceInfo));
surfaceInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
surfaceInfo.hinstance = GetModuleHandle(nullptr);
surfaceInfo.hwnd = win;
VkResult err = m_createSurface(m_vkInst, &surfaceInfo, nullptr, &surface);
if (err != VK_SUCCESS)
qWarning("Failed to create Vulkan surface: %d", err);
return surface;
}
void QWindowsVulkanInstance::destroySurface(VkSurfaceKHR surface)
{
if (m_destroySurface && surface)
m_destroySurface(m_vkInst, surface, nullptr);
}
QT_END_NAMESPACE

View File

@ -0,0 +1,76 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of 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$
**
****************************************************************************/
#ifndef QWINDOWSVULKANINSTANCE_H
#define QWINDOWSVULKANINSTANCE_H
#if defined(VULKAN_H_) && !defined(VK_USE_PLATFORM_WIN32_KHR)
#error "vulkan.h included without Win32 WSI"
#endif
#define VK_USE_PLATFORM_WIN32_KHR
#include <QtVulkanSupport/private/qbasicvulkanplatforminstance_p.h>
#include <QLibrary>
QT_BEGIN_NAMESPACE
class QWindowsVulkanInstance : public QBasicPlatformVulkanInstance
{
public:
QWindowsVulkanInstance(QVulkanInstance *instance);
~QWindowsVulkanInstance();
void createOrAdoptInstance() override;
bool supportsPresent(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, QWindow *window) override;
VkSurfaceKHR createSurface(HWND win);
void destroySurface(VkSurfaceKHR surface);
private:
QVulkanInstance *m_instance;
QLibrary m_lib;
PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR m_getPhysDevPresSupport;
PFN_vkCreateWin32SurfaceKHR m_createSurface;
PFN_vkDestroySurfaceKHR m_destroySurface;
};
QT_END_NAMESPACE
#endif // QWINDOWSVULKANINSTANCE_H

View File

@ -67,6 +67,10 @@
#include <dwmapi.h> #include <dwmapi.h>
#if QT_CONFIG(vulkan)
#include "qwindowsvulkaninstance.h"
#endif
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
enum { enum {
@ -308,13 +312,15 @@ static QWindow::Visibility windowVisibility_sys(HWND hwnd)
return QWindow::Windowed; return QWindow::Windowed;
} }
static inline bool windowIsOpenGL(const QWindow *w) static inline bool windowIsAccelerated(const QWindow *w)
{ {
switch (w->surfaceType()) { switch (w->surfaceType()) {
case QSurface::OpenGLSurface: case QSurface::OpenGLSurface:
return true; return true;
case QSurface::RasterGLSurface: case QSurface::RasterGLSurface:
return qt_window_private(const_cast<QWindow *>(w))->compositing; return qt_window_private(const_cast<QWindow *>(w))->compositing;
case QSurface::VulkanSurface:
return true;
default: default:
return false; return false;
} }
@ -375,11 +381,11 @@ bool QWindowsWindow::setWindowLayered(HWND hwnd, Qt::WindowFlags flags, bool has
return needsLayered; return needsLayered;
} }
static void setWindowOpacity(HWND hwnd, Qt::WindowFlags flags, bool hasAlpha, bool openGL, qreal level) static void setWindowOpacity(HWND hwnd, Qt::WindowFlags flags, bool hasAlpha, bool accelerated, qreal level)
{ {
if (QWindowsWindow::setWindowLayered(hwnd, flags, hasAlpha, level)) { if (QWindowsWindow::setWindowLayered(hwnd, flags, hasAlpha, level)) {
const BYTE alpha = BYTE(qRound(255.0 * level)); const BYTE alpha = BYTE(qRound(255.0 * level));
if (hasAlpha && !openGL && (flags & Qt::FramelessWindowHint)) { if (hasAlpha && !accelerated && (flags & Qt::FramelessWindowHint)) {
// Non-GL windows with alpha: Use blend function to update. // Non-GL windows with alpha: Use blend function to update.
BLENDFUNCTION blend = {AC_SRC_OVER, 0, alpha, AC_SRC_ALPHA}; BLENDFUNCTION blend = {AC_SRC_OVER, 0, alpha, AC_SRC_ALPHA};
UpdateLayeredWindow(hwnd, NULL, NULL, NULL, NULL, NULL, 0, &blend, ULW_ALPHA); UpdateLayeredWindow(hwnd, NULL, NULL, NULL, NULL, NULL, 0, &blend, ULW_ALPHA);
@ -393,13 +399,13 @@ static void setWindowOpacity(HWND hwnd, Qt::WindowFlags flags, bool hasAlpha, bo
static inline void updateGLWindowSettings(const QWindow *w, HWND hwnd, Qt::WindowFlags flags, qreal opacity) static inline void updateGLWindowSettings(const QWindow *w, HWND hwnd, Qt::WindowFlags flags, qreal opacity)
{ {
const bool isGL = windowIsOpenGL(w); const bool isAccelerated = windowIsAccelerated(w);
const bool hasAlpha = w->format().hasAlpha(); const bool hasAlpha = w->format().hasAlpha();
if (isGL && hasAlpha) if (isAccelerated && hasAlpha)
applyBlurBehindWindow(hwnd); applyBlurBehindWindow(hwnd);
setWindowOpacity(hwnd, flags, hasAlpha, isGL, opacity); setWindowOpacity(hwnd, flags, hasAlpha, isAccelerated, opacity);
} }
/*! /*!
@ -1053,6 +1059,9 @@ QWindowsWindow::QWindowsWindow(QWindow *aWindow, const QWindowsWindowData &data)
m_data(data), m_data(data),
m_cursor(new CursorHandle), m_cursor(new CursorHandle),
m_format(aWindow->requestedFormat()) m_format(aWindow->requestedFormat())
#if QT_CONFIG(vulkan)
, m_vkSurface(0)
#endif
{ {
// Clear the creation context as the window can be found in QWindowsContext's map. // Clear the creation context as the window can be found in QWindowsContext's map.
QWindowsContext::instance()->setWindowCreationContext(QSharedPointer<QWindowCreationContext>()); QWindowsContext::instance()->setWindowCreationContext(QSharedPointer<QWindowCreationContext>());
@ -1068,6 +1077,10 @@ QWindowsWindow::QWindowsWindow(QWindow *aWindow, const QWindowsWindowData &data)
setFlag(OpenGL_ES2); setFlag(OpenGL_ES2);
} }
#endif // QT_NO_OPENGL #endif // QT_NO_OPENGL
#if QT_CONFIG(vulkan)
if (aWindow->surfaceType() == QSurface::VulkanSurface)
setFlag(VulkanSurface);
#endif
updateDropSite(window()->isTopLevel()); updateDropSite(window()->isTopLevel());
registerTouchWindow(); registerTouchWindow();
@ -1121,6 +1134,14 @@ void QWindowsWindow::destroyWindow()
if (hasMouseCapture()) if (hasMouseCapture())
setMouseGrabEnabled(false); setMouseGrabEnabled(false);
setDropSiteEnabled(false); setDropSiteEnabled(false);
#if QT_CONFIG(vulkan)
if (m_vkSurface) {
QVulkanInstance *inst = window()->vulkanInstance();
if (inst)
static_cast<QWindowsVulkanInstance *>(inst->handle())->destroySurface(m_vkSurface);
m_vkSurface = 0;
}
#endif
#ifndef QT_NO_OPENGL #ifndef QT_NO_OPENGL
if (m_surface) { if (m_surface) {
if (QWindowsStaticOpenGLContext *staticOpenGLContext = QWindowsIntegration::staticOpenGLContext()) if (QWindowsStaticOpenGLContext *staticOpenGLContext = QWindowsIntegration::staticOpenGLContext())
@ -1470,8 +1491,10 @@ void QWindowsWindow::handleHidden()
void QWindowsWindow::handleCompositionSettingsChanged() void QWindowsWindow::handleCompositionSettingsChanged()
{ {
const QWindow *w = window(); const QWindow *w = window();
if (w->surfaceType() == QWindow::OpenGLSurface && w->format().hasAlpha()) if ((w->surfaceType() == QWindow::OpenGLSurface || w->surfaceType() == QWindow::VulkanSurface)
&& w->format().hasAlpha()) {
applyBlurBehindWindow(handle()); applyBlurBehindWindow(handle());
}
} }
static QRect normalFrameGeometry(HWND hwnd) static QRect normalFrameGeometry(HWND hwnd)
@ -1693,8 +1716,11 @@ bool QWindowsWindow::handleWmPaint(HWND hwnd, UINT message,
BeginPaint(hwnd, &ps); BeginPaint(hwnd, &ps);
// Observed painting problems with Aero style disabled (QTBUG-7865). // Observed painting problems with Aero style disabled (QTBUG-7865).
if (Q_UNLIKELY(testFlag(OpenGLSurface) && testFlag(OpenGLDoubleBuffered) && !dwmIsCompositionEnabled())) if (Q_UNLIKELY(!dwmIsCompositionEnabled())
&& ((testFlag(OpenGLSurface) && testFlag(OpenGLDoubleBuffered)) || testFlag(VulkanSurface)))
{
SelectClipRgn(ps.hdc, NULL); SelectClipRgn(ps.hdc, NULL);
}
// If the a window is obscured by another window (such as a child window) // If the a window is obscured by another window (such as a child window)
// we still need to send isExposed=true, for compatibility. // we still need to send isExposed=true, for compatibility.
@ -2030,7 +2056,7 @@ void QWindowsWindow::setOpacity(qreal level)
m_opacity = level; m_opacity = level;
if (m_data.hwnd) if (m_data.hwnd)
setWindowOpacity(m_data.hwnd, m_data.flags, setWindowOpacity(m_data.hwnd, m_data.flags,
window()->format().hasAlpha(), testFlag(OpenGLSurface), window()->format().hasAlpha(), testFlag(OpenGLSurface) || testFlag(VulkanSurface),
level); level);
} }
} }
@ -2448,11 +2474,27 @@ void QWindowsWindow::setCustomMargins(const QMargins &newCustomMargins)
void *QWindowsWindow::surface(void *nativeConfig, int *err) void *QWindowsWindow::surface(void *nativeConfig, int *err)
{ {
#ifdef QT_NO_OPENGL #if QT_CONFIG(vulkan)
Q_UNUSED(nativeConfig);
Q_UNUSED(err);
if (window()->surfaceType() == QSurface::VulkanSurface) {
if (!m_vkSurface) {
QVulkanInstance *inst = window()->vulkanInstance();
if (inst)
m_vkSurface = static_cast<QWindowsVulkanInstance *>(inst->handle())->createSurface(handle());
else
qWarning("Attempted to create Vulkan surface without an instance; was QWindow::setVulkanInstance() called?");
}
// Different semantics for VkSurfaces: the return value is the address,
// not the value, given that this is a 64-bit handle even on x86.
return &m_vkSurface;
}
#elif defined(QT_NO_OPENGL)
Q_UNUSED(err) Q_UNUSED(err)
Q_UNUSED(nativeConfig) Q_UNUSED(nativeConfig)
return 0; return 0;
#else #endif
#ifndef QT_NO_OPENGL
if (!m_surface) { if (!m_surface) {
if (QWindowsStaticOpenGLContext *staticOpenGLContext = QWindowsIntegration::staticOpenGLContext()) if (QWindowsStaticOpenGLContext *staticOpenGLContext = QWindowsIntegration::staticOpenGLContext())
m_surface = staticOpenGLContext->createWindowSurface(m_data.hwnd, nativeConfig, err); m_surface = staticOpenGLContext->createWindowSurface(m_data.hwnd, nativeConfig, err);
@ -2464,6 +2506,14 @@ void *QWindowsWindow::surface(void *nativeConfig, int *err)
void QWindowsWindow::invalidateSurface() void QWindowsWindow::invalidateSurface()
{ {
#if QT_CONFIG(vulkan)
if (m_vkSurface) {
QVulkanInstance *inst = window()->vulkanInstance();
if (inst)
static_cast<QWindowsVulkanInstance *>(inst->handle())->destroySurface(m_vkSurface);
m_vkSurface = 0;
}
#endif
#ifndef QT_NO_OPENGL #ifndef QT_NO_OPENGL
if (m_surface) { if (m_surface) {
if (QWindowsStaticOpenGLContext *staticOpenGLContext = QWindowsIntegration::staticOpenGLContext()) if (QWindowsStaticOpenGLContext *staticOpenGLContext = QWindowsIntegration::staticOpenGLContext())

View File

@ -46,6 +46,10 @@
#include <qpa/qplatformwindow.h> #include <qpa/qplatformwindow.h>
#include <QtPlatformHeaders/qwindowswindowfunctions.h> #include <QtPlatformHeaders/qwindowswindowfunctions.h>
#if QT_CONFIG(vulkan)
#include "qwindowsvulkaninstance.h"
#endif
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QWindowsOleDropTarget; class QWindowsOleDropTarget;
@ -209,6 +213,7 @@ public:
Compositing = 0x200000, Compositing = 0x200000,
HasBorderInFullScreen = 0x400000, HasBorderInFullScreen = 0x400000,
WithinDpiChanged = 0x800000, WithinDpiChanged = 0x800000,
VulkanSurface = 0x1000000
}; };
QWindowsWindow(QWindow *window, const QWindowsWindowData &data); QWindowsWindow(QWindow *window, const QWindowsWindowData &data);
@ -351,6 +356,11 @@ private:
HICON m_iconSmall = 0; HICON m_iconSmall = 0;
HICON m_iconBig = 0; HICON m_iconBig = 0;
void *m_surface = nullptr; void *m_surface = nullptr;
#if QT_CONFIG(vulkan)
// note: intentionally not using void * in order to avoid breaking x86
VkSurfaceKHR m_vkSurface = 0;
#endif
}; };
#ifndef QT_NO_DEBUG_STREAM #ifndef QT_NO_DEBUG_STREAM

View File

@ -68,6 +68,11 @@ qtConfig(dynamicgl) {
HEADERS += $$PWD/qwindowseglcontext.h HEADERS += $$PWD/qwindowseglcontext.h
} }
qtConfig(vulkan) {
SOURCES += $$PWD/qwindowsvulkaninstance.cpp
HEADERS += $$PWD/qwindowsvulkaninstance.h
}
!contains( DEFINES, QT_NO_CLIPBOARD ) { !contains( DEFINES, QT_NO_CLIPBOARD ) {
SOURCES += $$PWD/qwindowsclipboard.cpp SOURCES += $$PWD/qwindowsclipboard.cpp
HEADERS += $$PWD/qwindowsclipboard.h HEADERS += $$PWD/qwindowsclipboard.h

View File

@ -5,6 +5,8 @@ QT += \
eventdispatcher_support-private accessibility_support-private \ eventdispatcher_support-private accessibility_support-private \
fontdatabase_support-private theme_support-private fontdatabase_support-private theme_support-private
qtConfig(vulkan): QT += vulkan_support-private
LIBS += -lgdi32 -ldwmapi LIBS += -lgdi32 -ldwmapi
include(windows.pri) include(windows.pri)

View File

@ -83,6 +83,11 @@
#include <QtCore/QFileInfo> #include <QtCore/QFileInfo>
#if QT_CONFIG(vulkan)
#include "qxcbvulkaninstance.h"
#include "qxcbvulkanwindow.h"
#endif
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
// Find out if our parent process is gdb by looking at the 'exe' symlink under /proc,. // Find out if our parent process is gdb by looking at the 'exe' symlink under /proc,.
@ -199,11 +204,19 @@ QPlatformWindow *QXcbIntegration::createPlatformWindow(QWindow *window) const
{ {
QXcbScreen *screen = static_cast<QXcbScreen *>(window->screen()->handle()); QXcbScreen *screen = static_cast<QXcbScreen *>(window->screen()->handle());
QXcbGlIntegration *glIntegration = screen->connection()->glIntegration(); QXcbGlIntegration *glIntegration = screen->connection()->glIntegration();
if (window->type() != Qt::Desktop && window->supportsOpenGL()) { if (window->type() != Qt::Desktop) {
if (glIntegration) { if (window->supportsOpenGL()) {
QXcbWindow *xcbWindow = glIntegration->createWindow(window); if (glIntegration) {
QXcbWindow *xcbWindow = glIntegration->createWindow(window);
xcbWindow->create();
return xcbWindow;
}
#if QT_CONFIG(vulkan)
} else if (window->surfaceType() == QSurface::VulkanSurface) {
QXcbWindow *xcbWindow = new QXcbVulkanWindow(window);
xcbWindow->create(); xcbWindow->create();
return xcbWindow; return xcbWindow;
#endif
} }
} }
@ -498,4 +511,11 @@ void QXcbIntegration::beep() const
xcb_bell(connection, 0); xcb_bell(connection, 0);
} }
#if QT_CONFIG(vulkan)
QPlatformVulkanInstance *QXcbIntegration::createPlatformVulkanInstance(QVulkanInstance *instance) const
{
return new QXcbVulkanInstance(instance);
}
#endif
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -113,6 +113,10 @@ public:
void beep() const override; void beep() const override;
#if QT_CONFIG(vulkan)
QPlatformVulkanInstance *createPlatformVulkanInstance(QVulkanInstance *instance) const override;
#endif
static QXcbIntegration *instance() { return m_instance; } static QXcbIntegration *instance() { return m_instance; }
private: private:

View File

@ -67,6 +67,10 @@
#include "qxcbnativeinterfacehandler.h" #include "qxcbnativeinterfacehandler.h"
#if QT_CONFIG(vulkan)
#include "qxcbvulkanwindow.h"
#endif
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
// return QXcbNativeInterface::ResourceType for the key. // return QXcbNativeInterface::ResourceType for the key.
@ -82,7 +86,8 @@ static int resourceType(const QByteArray &key)
QByteArrayLiteral("rootwindow"), QByteArrayLiteral("rootwindow"),
QByteArrayLiteral("subpixeltype"), QByteArrayLiteral("antialiasingenabled"), QByteArrayLiteral("subpixeltype"), QByteArrayLiteral("antialiasingenabled"),
QByteArrayLiteral("atspibus"), QByteArrayLiteral("atspibus"),
QByteArrayLiteral("compositingenabled") QByteArrayLiteral("compositingenabled"),
QByteArrayLiteral("vksurface")
}; };
const QByteArray *end = names + sizeof(names) / sizeof(names[0]); const QByteArray *end = names + sizeof(names) / sizeof(names[0]);
const QByteArray *result = std::find(names, end, key); const QByteArray *result = std::find(names, end, key);
@ -257,6 +262,14 @@ void *QXcbNativeInterface::nativeResourceForWindow(const QByteArray &resourceStr
case Screen: case Screen:
result = screenForWindow(window); result = screenForWindow(window);
break; break;
#if QT_CONFIG(vulkan)
case VkSurface:
if (window->surfaceType() == QSurface::VulkanSurface && window->handle()) {
// return a pointer to the VkSurfaceKHR value, not the value itself
result = static_cast<QXcbVulkanWindow *>(window->handle())->surface();
}
break;
#endif
default: default:
break; break;
} }

View File

@ -73,7 +73,8 @@ public:
ScreenSubpixelType, ScreenSubpixelType,
ScreenAntialiasingEnabled, ScreenAntialiasingEnabled,
AtspiBus, AtspiBus,
CompositingEnabled CompositingEnabled,
VkSurface
}; };
QXcbNativeInterface(); QXcbNativeInterface();

View File

@ -0,0 +1,156 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of 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 "qxcbvulkaninstance.h"
#include "qxcbwindow.h"
#include "qxcbscreen.h"
QT_BEGIN_NAMESPACE
QXcbVulkanInstance::QXcbVulkanInstance(QVulkanInstance *instance)
: m_instance(instance),
m_getPhysDevPresSupport(nullptr),
m_createSurface(nullptr),
m_destroySurface(nullptr)
{
if (qEnvironmentVariableIsSet("QT_VULKAN_LIB"))
m_lib.setFileName(QString::fromUtf8(qgetenv("QT_VULKAN_LIB")));
else
m_lib.setFileName(QStringLiteral("vulkan"));
if (!m_lib.load()) {
qWarning("Failed to load %s: %s", qPrintable(m_lib.fileName()), qPrintable(m_lib.errorString()));
return;
}
init(&m_lib);
}
QXcbVulkanInstance::~QXcbVulkanInstance()
{
}
void QXcbVulkanInstance::createOrAdoptInstance()
{
initInstance(m_instance, QByteArrayList() << QByteArrayLiteral("VK_KHR_xcb_surface"));
if (!m_vkInst)
return;
m_getPhysDevPresSupport = reinterpret_cast<PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR>(
m_vkGetInstanceProcAddr(m_vkInst, "vkGetPhysicalDeviceXcbPresentationSupportKHR"));
if (!m_getPhysDevPresSupport)
qWarning("Failed to find vkGetPhysicalDeviceXcbPresentationSupportKHR");
}
bool QXcbVulkanInstance::supportsPresent(VkPhysicalDevice physicalDevice,
uint32_t queueFamilyIndex,
QWindow *window)
{
if (!m_getPhysDevPresSupport || !m_getPhysDevSurfaceSupport)
return true;
QXcbWindow *w = static_cast<QXcbWindow *>(window->handle());
if (!w) {
qWarning("Attempted to call supportsPresent() without a valid platform window");
return false;
}
xcb_connection_t *connection = w->xcbScreen()->xcb_connection();
bool ok = m_getPhysDevPresSupport(physicalDevice, queueFamilyIndex, connection, w->visualId());
VkSurfaceKHR surface = QVulkanInstance::surfaceForWindow(window);
VkBool32 supported = false;
m_getPhysDevSurfaceSupport(physicalDevice, queueFamilyIndex, surface, &supported);
ok &= bool(supported);
return ok;
}
VkSurfaceKHR QXcbVulkanInstance::createSurface(QXcbWindow *window)
{
VkSurfaceKHR surface = 0;
if (!m_createSurface) {
m_createSurface = reinterpret_cast<PFN_vkCreateXcbSurfaceKHR>(
m_vkGetInstanceProcAddr(m_vkInst, "vkCreateXcbSurfaceKHR"));
}
if (!m_createSurface) {
qWarning("Failed to find vkCreateXcbSurfaceKHR");
return surface;
}
if (!m_destroySurface) {
m_destroySurface = reinterpret_cast<PFN_vkDestroySurfaceKHR>(
m_vkGetInstanceProcAddr(m_vkInst, "vkDestroySurfaceKHR"));
}
if (!m_destroySurface) {
qWarning("Failed to find vkDestroySurfaceKHR");
return surface;
}
VkXcbSurfaceCreateInfoKHR surfaceInfo;
memset(&surfaceInfo, 0, sizeof(surfaceInfo));
surfaceInfo.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
surfaceInfo.connection = window->xcbScreen()->xcb_connection();
surfaceInfo.window = window->xcb_window();
VkResult err = m_createSurface(m_vkInst, &surfaceInfo, nullptr, &surface);
if (err != VK_SUCCESS)
qWarning("Failed to create Vulkan surface: %d", err);
return surface;
}
void QXcbVulkanInstance::destroySurface(VkSurfaceKHR surface)
{
if (m_destroySurface && surface)
m_destroySurface(m_vkInst, surface, nullptr);
}
void QXcbVulkanInstance::presentQueued(QWindow *window)
{
QXcbWindow *w = static_cast<QXcbWindow *>(window->handle());
if (!w) {
qWarning("Attempted to call presentQueued() without a valid platform window");
return;
}
if (w->needsSync())
w->postSyncWindowRequest();
}
QT_END_NAMESPACE

View File

@ -0,0 +1,79 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of 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$
**
****************************************************************************/
#ifndef QXCBVULKANINSTANCE_H
#define QXCBVULKANINSTANCE_H
#if defined(VULKAN_H_) && !defined(VK_USE_PLATFORM_XCB_KHR)
#error "vulkan.h included without xcb WSI"
#endif
#define VK_USE_PLATFORM_XCB_KHR
#include <QtVulkanSupport/private/qbasicvulkanplatforminstance_p.h>
#include <QLibrary>
QT_BEGIN_NAMESPACE
class QXcbWindow;
class QXcbVulkanInstance : public QBasicPlatformVulkanInstance
{
public:
QXcbVulkanInstance(QVulkanInstance *instance);
~QXcbVulkanInstance();
void createOrAdoptInstance() override;
bool supportsPresent(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, QWindow *window) override;
void presentQueued(QWindow *window) override;
VkSurfaceKHR createSurface(QXcbWindow *window);
void destroySurface(VkSurfaceKHR surface);
private:
QVulkanInstance *m_instance;
QLibrary m_lib;
PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR m_getPhysDevPresSupport;
PFN_vkCreateXcbSurfaceKHR m_createSurface;
PFN_vkDestroySurfaceKHR m_destroySurface;
};
QT_END_NAMESPACE
#endif // QXCBVULKANINSTANCE_H

View File

@ -0,0 +1,83 @@
/****************************************************************************
**
** Copyright (C) 2017 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 "qxcbvulkanwindow.h"
QT_BEGIN_NAMESPACE
QXcbVulkanWindow::QXcbVulkanWindow(QWindow *window)
: QXcbWindow(window),
m_surface(0)
{
}
QXcbVulkanWindow::~QXcbVulkanWindow()
{
if (m_surface) {
QVulkanInstance *inst = window()->vulkanInstance();
if (inst)
static_cast<QXcbVulkanInstance *>(inst->handle())->destroySurface(m_surface);
}
}
void QXcbVulkanWindow::resolveFormat(const QSurfaceFormat &format)
{
m_format = format;
m_format.setRedBufferSize(8);
m_format.setGreenBufferSize(8);
m_format.setBlueBufferSize(8);
}
VkSurfaceKHR *QXcbVulkanWindow::surface()
{
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;
}
QXcbVulkanInstance *xcbinst = static_cast<QXcbVulkanInstance *>(inst->handle());
m_surface = xcbinst->createSurface(this);
return &m_surface;
}
QT_END_NAMESPACE

View File

@ -0,0 +1,65 @@
/****************************************************************************
**
** Copyright (C) 2017 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$
**
****************************************************************************/
#ifndef QXCBVULKANWINDOW_H
#define QXCBVULKANWINDOW_H
#include "qxcbwindow.h"
#include "qxcbvulkaninstance.h"
QT_BEGIN_NAMESPACE
class QXcbVulkanWindow : public QXcbWindow
{
public:
QXcbVulkanWindow(QWindow *window);
~QXcbVulkanWindow();
VkSurfaceKHR *surface();
protected:
void resolveFormat(const QSurfaceFormat &format) override;
private:
VkSurfaceKHR m_surface;
};
QT_END_NAMESPACE
#endif // QXCBVULKANWINDOW_H

View File

@ -10,6 +10,8 @@ QT += \
qtHaveModule(linuxaccessibility_support-private): \ qtHaveModule(linuxaccessibility_support-private): \
QT += linuxaccessibility_support-private QT += linuxaccessibility_support-private
qtConfig(vulkan): QT += vulkan_support-private
SOURCES = \ SOURCES = \
qxcbclipboard.cpp \ qxcbclipboard.cpp \
qxcbconnection.cpp \ qxcbconnection.cpp \
@ -70,6 +72,16 @@ qtConfig(xcb-sm) {
include(gl_integrations/gl_integrations.pri) include(gl_integrations/gl_integrations.pri)
qtConfig(vulkan) {
SOURCES += \
qxcbvulkaninstance.cpp \
qxcbvulkanwindow.cpp
HEADERS += \
qxcbvulkaninstance.h \
qxcbvulkanwindow.h
}
!qtConfig(system-xcb) { !qtConfig(system-xcb) {
DEFINES += XCB_USE_RENDER DEFINES += XCB_USE_RENDER
QMAKE_USE += xcb-static xcb QMAKE_USE += xcb-static xcb

View File

@ -49,6 +49,11 @@ src_tools_qdbuscpp2xml.target = sub-qdbuscpp2xml
force_bootstrap: src_tools_qdbuscpp2xml.depends = src_tools_bootstrap_dbus force_bootstrap: src_tools_qdbuscpp2xml.depends = src_tools_bootstrap_dbus
else: src_tools_qdbuscpp2xml.depends = src_dbus else: src_tools_qdbuscpp2xml.depends = src_dbus
src_tools_qvkgen.subdir = tools/qvkgen
src_tools_qvkgen.target = sub-qvkgen
force_bootstrap: src_tools_qvkgen.depends = src_tools_bootstrap
else: src_tools_qvkgen.depends = src_corelib
src_winmain.subdir = $$PWD/winmain src_winmain.subdir = $$PWD/winmain
src_winmain.target = sub-winmain src_winmain.target = sub-winmain
src_winmain.depends = sub-corelib # just for the module .pri file src_winmain.depends = sub-corelib # just for the module .pri file
@ -185,6 +190,9 @@ qtConfig(gui) {
SUBDIRS += src_3rdparty_freetype SUBDIRS += src_3rdparty_freetype
src_platformsupport.depends += src_3rdparty_freetype src_platformsupport.depends += src_3rdparty_freetype
} }
SUBDIRS += src_tools_qvkgen
src_gui.depends += src_tools_qvkgen
TOOLS += src_tools_qvkgen
SUBDIRS += src_gui src_platformsupport src_platformheaders SUBDIRS += src_gui src_platformsupport src_platformheaders
qtConfig(opengl): SUBDIRS += src_openglextensions qtConfig(opengl): SUBDIRS += src_openglextensions
src_plugins.depends += src_gui src_platformsupport src_platformheaders src_plugins.depends += src_gui src_platformsupport src_platformheaders

530
src/tools/qvkgen/qvkgen.cpp Normal file
View File

@ -0,0 +1,530 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the tools applications of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** 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 General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** 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-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtCore/qcoreapplication.h>
#include <QtCore/qvector.h>
#include <QtCore/qfile.h>
#include <QtCore/qfileinfo.h>
#include <QtCore/qxmlstream.h>
class VkSpecParser
{
public:
bool parse();
struct TypedName {
QString name;
QString type;
QString typeSuffix;
};
struct Command {
TypedName cmd;
QVector<TypedName> args;
bool deviceLevel;
};
QVector<Command> commands() const { return m_commands; }
void setFileName(const QString &fn) { m_fn = fn; }
private:
void skip();
void parseCommands();
Command parseCommand();
TypedName parseParamOrProto(const QString &tag);
QString parseName();
QFile m_file;
QXmlStreamReader m_reader;
QVector<Command> m_commands;
QString m_fn;
};
bool VkSpecParser::parse()
{
m_file.setFileName(m_fn);
if (!m_file.open(QIODevice::ReadOnly | QIODevice::Text)) {
qWarning("Failed to open %s", qPrintable(m_file.fileName()));
return false;
}
m_reader.setDevice(&m_file);
while (!m_reader.atEnd()) {
m_reader.readNext();
if (m_reader.isStartElement()) {
if (m_reader.name() == QStringLiteral("commands"))
parseCommands();
}
}
return true;
}
void VkSpecParser::skip()
{
QString tag = m_reader.name().toString();
while (!m_reader.atEnd()) {
m_reader.readNext();
if (m_reader.isEndElement() && m_reader.name() == tag)
break;
}
}
void VkSpecParser::parseCommands()
{
m_commands.clear();
while (!m_reader.atEnd()) {
m_reader.readNext();
if (m_reader.isEndElement() && m_reader.name() == QStringLiteral("commands"))
return;
if (m_reader.isStartElement() && m_reader.name() == "command")
m_commands.append(parseCommand());
}
}
VkSpecParser::Command VkSpecParser::parseCommand()
{
Command c;
while (!m_reader.atEnd()) {
m_reader.readNext();
if (m_reader.isEndElement() && m_reader.name() == QStringLiteral("command"))
break;
if (m_reader.isStartElement()) {
const QString protoStr = QStringLiteral("proto");
const QString paramStr = QStringLiteral("param");
if (m_reader.name() == protoStr) {
c.cmd = parseParamOrProto(protoStr);
} else if (m_reader.name() == paramStr) {
c.args.append(parseParamOrProto(paramStr));
} else {
skip();
}
}
}
c.deviceLevel = false;
if (!c.args.isEmpty()) {
QStringList dispatchableDeviceAndChildTypes {
QStringLiteral("VkDevice"),
QStringLiteral("VkQueue"),
QStringLiteral("VkCommandBuffer")
};
if (dispatchableDeviceAndChildTypes.contains(c.args[0].type)
&& c.cmd.name != QStringLiteral("vkGetDeviceProcAddr"))
{
c.deviceLevel = true;
}
}
return c;
}
VkSpecParser::TypedName VkSpecParser::parseParamOrProto(const QString &tag)
{
TypedName t;
while (!m_reader.atEnd()) {
m_reader.readNext();
if (m_reader.isEndElement() && m_reader.name() == tag)
break;
if (m_reader.isStartElement()) {
if (m_reader.name() == QStringLiteral("name")) {
t.name = parseName();
} else if (m_reader.name() != QStringLiteral("type")) {
skip();
}
} else {
QStringRef text = m_reader.text().trimmed();
if (!text.isEmpty()) {
if (text.startsWith(QLatin1Char('['))) {
t.typeSuffix += text;
} else {
if (!t.type.isEmpty())
t.type += QLatin1Char(' ');
t.type += text;
}
}
}
}
return t;
}
QString VkSpecParser::parseName()
{
QString name;
while (!m_reader.atEnd()) {
m_reader.readNext();
if (m_reader.isEndElement() && m_reader.name() == QStringLiteral("name"))
break;
name += m_reader.text();
}
return name.trimmed();
}
QString funcSig(const VkSpecParser::Command &c, const char *className = nullptr)
{
QString s;
s.sprintf("%s %s%s%s", qPrintable(c.cmd.type),
(className ? className : ""), (className ? "::" : ""),
qPrintable(c.cmd.name));
if (!c.args.isEmpty()) {
s += QLatin1Char('(');
bool first = true;
for (const VkSpecParser::TypedName &a : c.args) {
QString argStr;
argStr.sprintf("%s%s%s%s", qPrintable(a.type), (a.type.endsWith(QLatin1Char('*')) ? "" : " "),
qPrintable(a.name), qPrintable(a.typeSuffix));
if (!first)
s += QStringLiteral(", ");
else
first = false;
s += argStr;
}
s += QLatin1Char(')');
}
return s;
}
QString funcCall(const VkSpecParser::Command &c, int idx)
{
QString s;
// template:
// [return] reinterpret_cast<PFN_vkEnumeratePhysicalDevices>(d_ptr->m_funcs[0])(instance, pPhysicalDeviceCount, pPhysicalDevices);
s.sprintf("%sreinterpret_cast<PFN_%s>(d_ptr->m_funcs[%d])",
(c.cmd.type == QStringLiteral("void") ? "" : "return "),
qPrintable(c.cmd.name),
idx);
if (!c.args.isEmpty()) {
s += QLatin1Char('(');
bool first = true;
for (const VkSpecParser::TypedName &a : c.args) {
if (!first)
s += QStringLiteral(", ");
else
first = false;
s += a.name;
}
s += QLatin1Char(')');
}
return s;
}
class Preamble
{
public:
QByteArray get(const QString &fn);
private:
QByteArray m_str;
} preamble;
QByteArray Preamble::get(const QString &fn)
{
if (!m_str.isEmpty())
return m_str;
QFile f(fn);
if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
qWarning("Failed to open %s", qPrintable(fn));
return m_str;
}
m_str = f.readAll();
m_str.replace("FOO", "QtGui");
m_str += "\n// This file is automatically generated by qvkgen. Do not edit.\n";
return m_str;
}
bool genVulkanFunctionsH(const QVector<VkSpecParser::Command> &commands, const QString &licHeaderFn, const QString &outputBase)
{
QFile f(outputBase + QStringLiteral(".h"));
if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
qWarning("Failed to write %s", qPrintable(f.fileName()));
return false;
}
static const char *s =
"%s\n"
"#ifndef QVULKANFUNCTIONS_H\n"
"#define QVULKANFUNCTIONS_H\n"
"\n"
"#include <QtGui/qtguiglobal.h>\n"
"\n"
"#if QT_CONFIG(vulkan)\n"
"\n"
"#ifndef VK_NO_PROTOTYPES\n"
"#define VK_NO_PROTOTYPES\n"
"#endif\n"
"#include <vulkan/vulkan.h>\n"
"\n"
"#include <QtCore/qscopedpointer.h>\n"
"\n"
"QT_BEGIN_NAMESPACE\n"
"\n"
"class QVulkanInstance;\n"
"class QVulkanFunctionsPrivate;\n"
"class QVulkanDeviceFunctionsPrivate;\n"
"\n"
"class Q_GUI_EXPORT QVulkanFunctions\n"
"{\n"
"public:\n"
" ~QVulkanFunctions();\n"
"\n"
"%s\n"
"private:\n"
" Q_DISABLE_COPY(QVulkanFunctions)\n"
" QVulkanFunctions(QVulkanInstance *inst);\n"
"\n"
" QScopedPointer<QVulkanFunctionsPrivate> d_ptr;\n"
" friend class QVulkanInstance;\n"
"};\n"
"\n"
"class Q_GUI_EXPORT QVulkanDeviceFunctions\n"
"{\n"
"public:\n"
" ~QVulkanDeviceFunctions();\n"
"\n"
"%s\n"
"private:\n"
" Q_DISABLE_COPY(QVulkanDeviceFunctions)\n"
" QVulkanDeviceFunctions(QVulkanInstance *inst, VkDevice device);\n"
"\n"
" QScopedPointer<QVulkanDeviceFunctionsPrivate> d_ptr;\n"
" friend class QVulkanInstance;\n"
"};\n"
"\n"
"QT_END_NAMESPACE\n"
"\n"
"#endif // QT_CONFIG(vulkan)\n"
"\n"
"#endif // QVULKANFUNCTIONS_H\n";
QString instCmdStr;
QString devCmdStr;
for (const VkSpecParser::Command &c : commands) {
QString *dst = c.deviceLevel ? &devCmdStr : &instCmdStr;
*dst += QStringLiteral(" ");
*dst += funcSig(c);
*dst += QStringLiteral(";\n");
}
QString str;
str.sprintf(s, preamble.get(licHeaderFn).constData(), instCmdStr.toUtf8().constData(), devCmdStr.toUtf8().constData());
f.write(str.toUtf8());
return true;
}
bool genVulkanFunctionsPH(const QVector<VkSpecParser::Command> &commands, const QString &licHeaderFn, const QString &outputBase)
{
QFile f(outputBase + QStringLiteral("_p.h"));
if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
qWarning("Failed to write %s", qPrintable(f.fileName()));
return false;
}
static const char *s =
"%s\n"
"#ifndef QVULKANFUNCTIONS_P_H\n"
"#define QVULKANFUNCTIONS_P_H\n"
"\n"
"//\n"
"// W A R N I N G\n"
"// -------------\n"
"//\n"
"// This file is not part of the Qt API. It exists purely as an\n"
"// implementation detail. This header file may change from version to\n"
"// version without notice, or even be removed.\n"
"//\n"
"// We mean it.\n"
"//\n"
"\n"
"#include \"qvulkanfunctions.h\"\n"
"\n"
"QT_BEGIN_NAMESPACE\n"
"\n"
"class QVulkanInstance;\n"
"\n"
"class QVulkanFunctionsPrivate\n"
"{\n"
"public:\n"
" QVulkanFunctionsPrivate(QVulkanInstance *inst);\n"
"\n"
" PFN_vkVoidFunction m_funcs[%d];\n"
"};\n"
"\n"
"class QVulkanDeviceFunctionsPrivate\n"
"{\n"
"public:\n"
" QVulkanDeviceFunctionsPrivate(QVulkanInstance *inst, VkDevice device);\n"
"\n"
" PFN_vkVoidFunction m_funcs[%d];\n"
"};\n"
"\n"
"QT_END_NAMESPACE\n"
"\n"
"#endif // QVULKANFUNCTIONS_P_H\n";
const int devLevelCount = std::count_if(commands.cbegin(), commands.cend(),
[](const VkSpecParser::Command &c) { return c.deviceLevel; });
const int instLevelCount = commands.count() - devLevelCount;
QString str;
str.sprintf(s, preamble.get(licHeaderFn).constData(), instLevelCount, devLevelCount);
f.write(str.toUtf8());
return true;
}
bool genVulkanFunctionsPC(const QVector<VkSpecParser::Command> &commands, const QString &licHeaderFn, const QString &outputBase)
{
QFile f(outputBase + QStringLiteral("_p.cpp"));
if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
qWarning("Failed to write %s", qPrintable(f.fileName()));
return false;
}
static const char *s =
"%s\n"
"#include \"qvulkanfunctions_p.h\"\n"
"#include \"qvulkaninstance.h\"\n"
"\n"
"QT_BEGIN_NAMESPACE\n"
"\n%s"
"QVulkanFunctionsPrivate::QVulkanFunctionsPrivate(QVulkanInstance *inst)\n"
"{\n"
" static const char *funcNames[] = {\n"
"%s\n"
" };\n"
" for (int i = 0; i < %d; ++i) {\n"
" m_funcs[i] = inst->getInstanceProcAddr(funcNames[i]);\n"
" if (!m_funcs[i])\n"
" qWarning(\"QVulkanFunctions: Failed to resolve %%s\", funcNames[i]);\n"
" }\n"
"}\n"
"\n%s"
"QVulkanDeviceFunctionsPrivate::QVulkanDeviceFunctionsPrivate(QVulkanInstance *inst, VkDevice device)\n"
"{\n"
" QVulkanFunctions *f = inst->functions();\n"
" Q_ASSERT(f);\n\n"
" static const char *funcNames[] = {\n"
"%s\n"
" };\n"
" for (int i = 0; i < %d; ++i) {\n"
" m_funcs[i] = f->vkGetDeviceProcAddr(device, funcNames[i]);\n"
" if (!m_funcs[i])\n"
" qWarning(\"QVulkanDeviceFunctions: Failed to resolve %%s\", funcNames[i]);\n"
" }\n"
"}\n"
"\n"
"QT_END_NAMESPACE\n";
QString devCmdWrapperStr;
QString instCmdWrapperStr;
int devIdx = 0;
int instIdx = 0;
QString devCmdNamesStr;
QString instCmdNamesStr;
for (int i = 0; i < commands.count(); ++i) {
QString *dst = commands[i].deviceLevel ? &devCmdWrapperStr : &instCmdWrapperStr;
int *idx = commands[i].deviceLevel ? &devIdx : &instIdx;
*dst += funcSig(commands[i], commands[i].deviceLevel ? "QVulkanDeviceFunctions" : "QVulkanFunctions");
*dst += QString(QStringLiteral("\n{\n Q_ASSERT(d_ptr->m_funcs[%1]);\n ")).arg(*idx);
*dst += funcCall(commands[i], *idx);
*dst += QStringLiteral(";\n}\n\n");
++*idx;
dst = commands[i].deviceLevel ? &devCmdNamesStr : &instCmdNamesStr;
*dst += QStringLiteral(" \"");
*dst += commands[i].cmd.name;
*dst += QStringLiteral("\",\n");
}
if (devCmdNamesStr.count() > 2)
devCmdNamesStr = devCmdNamesStr.left(devCmdNamesStr.count() - 2);
if (instCmdNamesStr.count() > 2)
instCmdNamesStr = instCmdNamesStr.left(instCmdNamesStr.count() - 2);
QString str;
str.sprintf(s, preamble.get(licHeaderFn).constData(),
instCmdWrapperStr.toUtf8().constData(), instCmdNamesStr.toUtf8().constData(), instIdx,
devCmdWrapperStr.toUtf8().constData(), devCmdNamesStr.toUtf8().constData(), commands.count() - instIdx);
f.write(str.toUtf8());
return true;
}
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
VkSpecParser parser;
if (argc < 4) {
qWarning("Usage: qvkgen input_vk_xml input_license_header output_base\n"
" For example: qvkgen vulkan/vk.xml vulkan/qvulkanfunctions.header vulkan/qvulkanfunctions");
return 1;
}
parser.setFileName(QString::fromUtf8(argv[1]));
if (!parser.parse())
return 1;
QVector<VkSpecParser::Command> commands = parser.commands();
QStringList ignoredFuncs {
QStringLiteral("vkCreateInstance"),
QStringLiteral("vkDestroyInstance"),
QStringLiteral("vkGetInstanceProcAddr")
};
// Filter out extensions and unwanted functions.
// The check for the former is rather simplistic for now: skip if the last letter is uppercase...
for (int i = 0; i < commands.count(); ++i) {
QString name = commands[i].cmd.name;
QChar c = name[name.count() - 1];
if (c.isUpper() || ignoredFuncs.contains(name))
commands.remove(i--);
}
QString licenseHeaderFileName = QString::fromUtf8(argv[2]);
QString outputBase = QString::fromUtf8(argv[3]);
genVulkanFunctionsH(commands, licenseHeaderFileName, outputBase);
genVulkanFunctionsPH(commands, licenseHeaderFileName, outputBase);
genVulkanFunctionsPC(commands, licenseHeaderFileName, outputBase);
return 0;
}

View File

@ -0,0 +1,5 @@
option(host_build)
SOURCES += qvkgen.cpp
load(qt_tool)

View File

@ -26,6 +26,7 @@
"QtFbSupport" => "$basedir/src/platformsupport/fbconvenience", "QtFbSupport" => "$basedir/src/platformsupport/fbconvenience",
"QtGlxSupport" => "$basedir/src/platformsupport/glxconvenience", "QtGlxSupport" => "$basedir/src/platformsupport/glxconvenience",
"QtKmsSupport" => "$basedir/src/platformsupport/kmsconvenience", "QtKmsSupport" => "$basedir/src/platformsupport/kmsconvenience",
"QtVulkanSupport" => "$basedir/src/platformsupport/vkconvenience",
"QtPlatformHeaders" => "$basedir/src/platformheaders", "QtPlatformHeaders" => "$basedir/src/platformheaders",
"QtANGLE/KHR" => "!$basedir/src/3rdparty/angle/include/KHR", "QtANGLE/KHR" => "!$basedir/src/3rdparty/angle/include/KHR",
"QtANGLE/GLES2" => "!$basedir/src/3rdparty/angle/include/GLES2", "QtANGLE/GLES2" => "!$basedir/src/3rdparty/angle/include/GLES2",
@ -52,6 +53,7 @@
"qnamespace.h" => "Qt", "qnamespace.h" => "Qt",
"qnumeric.h" => "QtNumeric", "qnumeric.h" => "QtNumeric",
"qvariant.h" => "QVariantHash,QVariantList,QVariantMap", "qvariant.h" => "QVariantHash,QVariantList,QVariantMap",
"qvulkanfunctions.h" => "QVulkanFunctions,QVulkanDeviceFunctions",
"qgl.h" => "QGL", "qgl.h" => "QGL",
"qsql.h" => "QSql", "qsql.h" => "QSql",
"qssl.h" => "QSsl", "qssl.h" => "QSsl",
@ -80,4 +82,7 @@ my @zlib_headers = ( "zconf.h", "zlib.h" );
@ignore_headers = ( @internal_zlib_headers ); @ignore_headers = ( @internal_zlib_headers );
@ignore_for_include_check = ( "qsystemdetection.h", "qcompilerdetection.h", "qprocessordetection.h", @zlib_headers, @angle_headers); @ignore_for_include_check = ( "qsystemdetection.h", "qcompilerdetection.h", "qprocessordetection.h", @zlib_headers, @angle_headers);
@ignore_for_qt_begin_namespace_check = ( "qt_windows.h", @zlib_headers, @angle_headers); @ignore_for_qt_begin_namespace_check = ( "qt_windows.h", @zlib_headers, @angle_headers);
%inject_headers = ( "$basedir/src/corelib/global" => [ "qconfig.h", "qconfig_p.h" ] ); %inject_headers = (
"$basedir/src/corelib/global" => [ "qconfig.h", "qconfig_p.h" ],
"$basedir/src/gui/vulkan" => [ "qvulkanfunctions.h" ]
);

View File

@ -9,8 +9,11 @@ SUBDIRS = \
painting \ painting \
qopenglconfig \ qopenglconfig \
qopengl \ qopengl \
qvulkan \
text \ text \
util \ util \
itemmodels \ itemmodels \
!qtConfig(opengl): SUBDIRS -= qopengl qopenglconfig !qtConfig(opengl): SUBDIRS -= qopengl qopenglconfig
!qtConfig(vulkan): SUBDIRS -= qvulkan

View File

@ -0,0 +1,9 @@
############################################################
# Project file for autotest for gui/vulkan functionality
############################################################
CONFIG += testcase
TARGET = tst_qvulkan
QT += gui-private core-private testlib
SOURCES += tst_qvulkan.cpp

View File

@ -0,0 +1,159 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** 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 General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** 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-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtGui/QVulkanInstance>
#include <QtGui/QVulkanFunctions>
#include <QtGui/QWindow>
#include <QtTest/QtTest>
#include <QSignalSpy>
class tst_QVulkan : public QObject
{
Q_OBJECT
private slots:
void vulkanInstance();
void vulkanCheckSupported();
void vulkanWindow();
void vulkanVersionRequest();
};
void tst_QVulkan::vulkanInstance()
{
QVulkanInstance inst;
if (!inst.create())
QSKIP("Vulkan init failed; skip");
QVERIFY(inst.isValid());
QVERIFY(inst.vkInstance() != VK_NULL_HANDLE);
QVERIFY(inst.functions());
QVERIFY(!inst.flags().testFlag(QVulkanInstance::NoDebugOutputRedirect));
inst.destroy();
QVERIFY(!inst.isValid());
QVERIFY(inst.handle() == nullptr);
inst.setFlags(QVulkanInstance::NoDebugOutputRedirect);
// pass a bogus layer and extension
inst.setExtensions(QByteArrayList() << "abcdefg" << "notanextension");
inst.setLayers(QByteArrayList() << "notalayer");
QVERIFY(inst.create());
QVERIFY(inst.isValid());
QVERIFY(inst.vkInstance() != VK_NULL_HANDLE);
QVERIFY(inst.handle() != nullptr);
QVERIFY(inst.functions());
QVERIFY(inst.flags().testFlag(QVulkanInstance::NoDebugOutputRedirect));
QVERIFY(!inst.extensions().contains("abcdefg"));
QVERIFY(!inst.extensions().contains("notanextension"));
QVERIFY(!inst.extensions().contains("notalayer"));
// at least the surface extensions should be there however
QVERIFY(inst.extensions().contains("VK_KHR_surface"));
QVERIFY(inst.getInstanceProcAddr("vkGetDeviceQueue"));
}
void tst_QVulkan::vulkanCheckSupported()
{
// Test the early calls to supportedLayers/extensions that need the library
// and some basics, but do not initialize the instance.
QVulkanInstance inst;
QVERIFY(!inst.isValid());
QVulkanInfoVector<QVulkanLayer> vl = inst.supportedLayers();
qDebug() << vl;
QVERIFY(!inst.isValid());
QVulkanInfoVector<QVulkanExtension> ve = inst.supportedExtensions();
qDebug() << ve;
QVERIFY(!inst.isValid());
if (inst.create()) { // skip the rest when Vulkan is not supported at all
QVERIFY(!ve.isEmpty());
QVERIFY(ve == inst.supportedExtensions());
}
}
void tst_QVulkan::vulkanWindow()
{
QVulkanInstance inst;
if (!inst.create())
QSKIP("Vulkan init failed; skip");
QWindow w;
w.setSurfaceType(QSurface::VulkanSurface);
w.setVulkanInstance(&inst);
w.resize(1024, 768);
w.show();
QTest::qWaitForWindowExposed(&w);
QCOMPARE(w.vulkanInstance(), &inst);
VkSurfaceKHR surface = QVulkanInstance::surfaceForWindow(&w);
QVERIFY(surface != VK_NULL_HANDLE);
// exercise supportsPresent (and QVulkanFunctions) a bit
QVulkanFunctions *f = inst.functions();
VkPhysicalDevice physDev;
uint32_t count = 1;
VkResult err = f->vkEnumeratePhysicalDevices(inst.vkInstance(), &count, &physDev);
if (err != VK_SUCCESS)
QSKIP("No physical devices; skip");
VkPhysicalDeviceProperties physDevProps;
f->vkGetPhysicalDeviceProperties(physDev, &physDevProps);
qDebug("Device name: %s Driver version: %d.%d.%d", physDevProps.deviceName,
VK_VERSION_MAJOR(physDevProps.driverVersion), VK_VERSION_MINOR(physDevProps.driverVersion),
VK_VERSION_PATCH(physDevProps.driverVersion));
bool supports = inst.supportsPresent(physDev, 0, &w);
qDebug("queue family 0 supports presenting to window = %d", supports);
}
void tst_QVulkan::vulkanVersionRequest()
{
QVulkanInstance inst;
if (!inst.create())
QSKIP("Vulkan init failed; skip");
// Now that we know Vulkan is functional, check the requested apiVersion is
// passed to vkCreateInstance as expected.
inst.destroy();
inst.setApiVersion(QVersionNumber(10, 0, 0));
QVERIFY(!inst.create());
QCOMPARE(inst.errorCode(), VK_ERROR_INCOMPATIBLE_DRIVER);
}
QTEST_MAIN(tst_QVulkan)
#include "tst_qvulkan.moc"

View File

@ -67,3 +67,5 @@ win32: SUBDIRS -= network_remote_stresstest network_stresstest
lessThan(QT_MAJOR_VERSION, 5): SUBDIRS -= bearerex lance qnetworkaccessmanager/qget qmimedatabase qnetworkreply \ lessThan(QT_MAJOR_VERSION, 5): SUBDIRS -= bearerex lance qnetworkaccessmanager/qget qmimedatabase qnetworkreply \
qpainfo qscreen socketengine xembed-raster xembed-widgets windowtransparency \ qpainfo qscreen socketengine xembed-raster xembed-widgets windowtransparency \
embeddedintoforeignwindow foreignwindows embeddedintoforeignwindow foreignwindows
qtConfig(vulkan): SUBDIRS += qvulkaninstance

View File

@ -0,0 +1,713 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** 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 General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** 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-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QGuiApplication>
#include <QVulkanInstance>
#include <QVulkanFunctions>
#include <QWindow>
#include <QLoggingCategory>
#include <qevent.h>
static const int SWAPCHAIN_BUFFER_COUNT = 2;
static const int FRAME_LAG = 2;
class VWindow : public QWindow
{
public:
VWindow() { setSurfaceType(VulkanSurface); }
~VWindow() { releaseResources(); }
private:
void exposeEvent(QExposeEvent *) override;
void resizeEvent(QResizeEvent *) override;
bool event(QEvent *) override;
void init();
void releaseResources();
void recreateSwapChain();
void createDefaultRenderPass();
void releaseSwapChain();
void render();
void buildDrawCalls();
bool m_inited = false;
VkSurfaceKHR m_vkSurface;
VkPhysicalDevice m_vkPhysDev;
VkPhysicalDeviceProperties m_physDevProps;
VkDevice m_vkDev = 0;
QVulkanDeviceFunctions *m_devFuncs;
VkQueue m_vkGfxQueue;
VkQueue m_vkPresQueue;
VkCommandPool m_vkCmdPool = 0;
PFN_vkCreateSwapchainKHR m_vkCreateSwapchainKHR = nullptr;
PFN_vkDestroySwapchainKHR m_vkDestroySwapchainKHR;
PFN_vkGetSwapchainImagesKHR m_vkGetSwapchainImagesKHR;
PFN_vkAcquireNextImageKHR m_vkAcquireNextImageKHR;
PFN_vkQueuePresentKHR m_vkQueuePresentKHR;
PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR m_vkGetPhysicalDeviceSurfaceCapabilitiesKHR;
PFN_vkGetPhysicalDeviceSurfaceFormatsKHR m_vkGetPhysicalDeviceSurfaceFormatsKHR;
QSize m_swapChainImageSize;
VkFormat m_colorFormat;
VkSwapchainKHR m_swapChain = 0;
uint32_t m_swapChainBufferCount = 0;
struct ImageResources {
VkImage image = 0;
VkImageView imageView = 0;
VkCommandBuffer cmdBuf = 0;
VkFence cmdFence = 0;
bool cmdFenceWaitable = false;
VkFramebuffer fb = 0;
} m_imageRes[SWAPCHAIN_BUFFER_COUNT];
uint32_t m_currentImage;
struct FrameResources {
VkFence fence = 0;
bool fenceWaitable = false;
VkSemaphore imageSem = 0;
VkSemaphore drawSem = 0;
} m_frameRes[FRAME_LAG];
uint32_t m_currentFrame;
VkRenderPass m_defaultRenderPass = 0;
};
void VWindow::exposeEvent(QExposeEvent *)
{
if (isExposed() && !m_inited) {
qDebug("initializing");
m_inited = true;
init();
recreateSwapChain();
render();
}
// Release everything when unexposed - the meaning of which is platform specific.
// Can be essential on mobile, to release resources while in background.
#if 1
if (!isExposed() && m_inited) {
m_inited = false;
releaseSwapChain();
releaseResources();
}
#endif
}
void VWindow::resizeEvent(QResizeEvent *)
{
// Nothing to do here - recreating the swapchain is handled in render(),
// in fact calling recreateSwapChain() from here leads to problems.
}
bool VWindow::event(QEvent *e)
{
switch (e->type()) {
case QEvent::UpdateRequest:
render();
break;
// Now the fun part: the swapchain must be destroyed before the surface as per
// spec. This is not ideal for us because the surface is managed by the
// QPlatformWindow which may be gone already when the unexpose comes, making the
// validation layer scream. The solution is to listen to the PlatformSurface events.
case QEvent::PlatformSurface:
if (static_cast<QPlatformSurfaceEvent *>(e)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed)
releaseSwapChain();
break;
default:
break;
}
return QWindow::event(e);
}
void VWindow::init()
{
m_vkSurface = QVulkanInstance::surfaceForWindow(this);
if (!m_vkSurface)
qFatal("Failed to get surface for window");
QVulkanInstance *inst = vulkanInstance();
QVulkanFunctions *f = inst->functions();
uint32_t devCount = 0;
f->vkEnumeratePhysicalDevices(inst->vkInstance(), &devCount, nullptr);
qDebug("%d physical devices", devCount);
if (!devCount)
qFatal("No physical devices");
// Just pick the first physical device for now.
devCount = 1;
VkResult err = f->vkEnumeratePhysicalDevices(inst->vkInstance(), &devCount, &m_vkPhysDev);
if (err != VK_SUCCESS)
qFatal("Failed to enumerate physical devices: %d", err);
f->vkGetPhysicalDeviceProperties(m_vkPhysDev, &m_physDevProps);
qDebug("Device name: %s Driver version: %d.%d.%d", m_physDevProps.deviceName,
VK_VERSION_MAJOR(m_physDevProps.driverVersion), VK_VERSION_MINOR(m_physDevProps.driverVersion),
VK_VERSION_PATCH(m_physDevProps.driverVersion));
uint32_t queueCount = 0;
f->vkGetPhysicalDeviceQueueFamilyProperties(m_vkPhysDev, &queueCount, nullptr);
QVector<VkQueueFamilyProperties> queueFamilyProps(queueCount);
f->vkGetPhysicalDeviceQueueFamilyProperties(m_vkPhysDev, &queueCount, queueFamilyProps.data());
int gfxQueueFamilyIdx = -1;
int presQueueFamilyIdx = -1;
// First look for a queue that supports both.
for (int i = 0; i < queueFamilyProps.count(); ++i) {
qDebug("queue family %d: flags=0x%x count=%d", i, queueFamilyProps[i].queueFlags, queueFamilyProps[i].queueCount);
if (gfxQueueFamilyIdx == -1
&& (queueFamilyProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
&& inst->supportsPresent(m_vkPhysDev, i, this))
gfxQueueFamilyIdx = i;
}
if (gfxQueueFamilyIdx != -1) {
presQueueFamilyIdx = gfxQueueFamilyIdx;
} else {
// Separate queues then.
qDebug("No queue with graphics+present; trying separate queues");
for (int i = 0; i < queueFamilyProps.count(); ++i) {
if (gfxQueueFamilyIdx == -1 && (queueFamilyProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT))
gfxQueueFamilyIdx = i;
if (presQueueFamilyIdx == -1 && inst->supportsPresent(m_vkPhysDev, i, this))
presQueueFamilyIdx = i;
}
}
if (gfxQueueFamilyIdx == -1)
qFatal("No graphics queue family found");
if (presQueueFamilyIdx == -1)
qFatal("No present queue family found");
VkDeviceQueueCreateInfo queueInfo[2];
const float prio[] = { 0 };
memset(queueInfo, 0, sizeof(queueInfo));
queueInfo[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueInfo[0].queueFamilyIndex = gfxQueueFamilyIdx;
queueInfo[0].queueCount = 1;
queueInfo[0].pQueuePriorities = prio;
if (gfxQueueFamilyIdx != presQueueFamilyIdx) {
queueInfo[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueInfo[1].queueFamilyIndex = presQueueFamilyIdx;
queueInfo[1].queueCount = 1;
queueInfo[1].pQueuePriorities = prio;
}
QVector<const char *> devLayers;
if (inst->layers().contains("VK_LAYER_LUNARG_standard_validation"))
devLayers.append("VK_LAYER_LUNARG_standard_validation");
QVector<const char *> devExts;
devExts.append("VK_KHR_swapchain");
VkDeviceCreateInfo devInfo;
memset(&devInfo, 0, sizeof(devInfo));
devInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
devInfo.queueCreateInfoCount = gfxQueueFamilyIdx == presQueueFamilyIdx ? 1 : 2;
devInfo.pQueueCreateInfos = queueInfo;
devInfo.enabledLayerCount = devLayers.count();
devInfo.ppEnabledLayerNames = devLayers.constData();
devInfo.enabledExtensionCount = devExts.count();
devInfo.ppEnabledExtensionNames = devExts.constData();
err = f->vkCreateDevice(m_vkPhysDev, &devInfo, nullptr, &m_vkDev);
if (err != VK_SUCCESS)
qFatal("Failed to create device: %d", err);
m_devFuncs = inst->deviceFunctions(m_vkDev);
m_devFuncs->vkGetDeviceQueue(m_vkDev, gfxQueueFamilyIdx, 0, &m_vkGfxQueue);
if (gfxQueueFamilyIdx == presQueueFamilyIdx)
m_vkPresQueue = m_vkGfxQueue;
else
m_devFuncs->vkGetDeviceQueue(m_vkDev, presQueueFamilyIdx, 0, &m_vkPresQueue);
VkCommandPoolCreateInfo poolInfo;
memset(&poolInfo, 0, sizeof(poolInfo));
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
poolInfo.queueFamilyIndex = gfxQueueFamilyIdx;
err = m_devFuncs->vkCreateCommandPool(m_vkDev, &poolInfo, nullptr, &m_vkCmdPool);
if (err != VK_SUCCESS)
qFatal("Failed to create command pool: %d", err);
m_colorFormat = VK_FORMAT_B8G8R8A8_UNORM; // may get changed later when setting up the swapchain
}
void VWindow::releaseResources()
{
if (!m_vkDev)
return;
m_devFuncs->vkDeviceWaitIdle(m_vkDev);
if (m_vkCmdPool) {
m_devFuncs->vkDestroyCommandPool(m_vkDev, m_vkCmdPool, nullptr);
m_vkCmdPool = 0;
}
if (m_vkDev) {
m_devFuncs->vkDestroyDevice(m_vkDev, nullptr);
// Play nice and notify QVulkanInstance that the QVulkanDeviceFunctions
// for m_vkDev needs to be invalidated.
vulkanInstance()->resetDeviceFunctions(m_vkDev);
m_vkDev = 0;
}
m_vkSurface = 0;
}
void VWindow::recreateSwapChain()
{
m_swapChainImageSize = size();
if (m_swapChainImageSize.isEmpty())
return;
QVulkanInstance *inst = vulkanInstance();
QVulkanFunctions *f = inst->functions();
m_devFuncs->vkDeviceWaitIdle(m_vkDev);
if (!m_vkCreateSwapchainKHR) {
m_vkGetPhysicalDeviceSurfaceCapabilitiesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR>(
inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfaceCapabilitiesKHR"));
m_vkGetPhysicalDeviceSurfaceFormatsKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceFormatsKHR>(
inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfaceFormatsKHR"));
// note: device-specific functions
m_vkCreateSwapchainKHR = reinterpret_cast<PFN_vkCreateSwapchainKHR>(f->vkGetDeviceProcAddr(m_vkDev, "vkCreateSwapchainKHR"));
m_vkDestroySwapchainKHR = reinterpret_cast<PFN_vkDestroySwapchainKHR>(f->vkGetDeviceProcAddr(m_vkDev, "vkDestroySwapchainKHR"));
m_vkGetSwapchainImagesKHR = reinterpret_cast<PFN_vkGetSwapchainImagesKHR>(f->vkGetDeviceProcAddr(m_vkDev, "vkGetSwapchainImagesKHR"));
m_vkAcquireNextImageKHR = reinterpret_cast<PFN_vkAcquireNextImageKHR>(f->vkGetDeviceProcAddr(m_vkDev, "vkAcquireNextImageKHR"));
m_vkQueuePresentKHR = reinterpret_cast<PFN_vkQueuePresentKHR>(f->vkGetDeviceProcAddr(m_vkDev, "vkQueuePresentKHR"));
}
VkColorSpaceKHR colorSpace = VkColorSpaceKHR(0);
uint32_t formatCount = 0;
m_vkGetPhysicalDeviceSurfaceFormatsKHR(m_vkPhysDev, m_vkSurface, &formatCount, nullptr);
if (formatCount) {
QVector<VkSurfaceFormatKHR> formats(formatCount);
m_vkGetPhysicalDeviceSurfaceFormatsKHR(m_vkPhysDev, m_vkSurface, &formatCount, formats.data());
if (formats[0].format != VK_FORMAT_UNDEFINED) {
m_colorFormat = formats[0].format;
colorSpace = formats[0].colorSpace;
}
}
VkSurfaceCapabilitiesKHR surfaceCaps;
m_vkGetPhysicalDeviceSurfaceCapabilitiesKHR(m_vkPhysDev, m_vkSurface, &surfaceCaps);
uint32_t reqBufferCount = SWAPCHAIN_BUFFER_COUNT;
if (surfaceCaps.maxImageCount)
reqBufferCount = qBound(surfaceCaps.minImageCount, reqBufferCount, surfaceCaps.maxImageCount);
VkExtent2D bufferSize = surfaceCaps.currentExtent;
if (bufferSize.width == uint32_t(-1))
bufferSize.width = m_swapChainImageSize.width();
if (bufferSize.height == uint32_t(-1))
bufferSize.height = m_swapChainImageSize.height();
VkSurfaceTransformFlagBitsKHR preTransform =
(surfaceCaps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)
? VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR
: surfaceCaps.currentTransform;
VkCompositeAlphaFlagBitsKHR compositeAlpha =
(surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR)
? VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR
: VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR;
VkSwapchainKHR oldSwapChain = m_swapChain;
VkSwapchainCreateInfoKHR swapChainInfo;
memset(&swapChainInfo, 0, sizeof(swapChainInfo));
swapChainInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
swapChainInfo.surface = m_vkSurface;
swapChainInfo.minImageCount = reqBufferCount;
swapChainInfo.imageFormat = m_colorFormat;
swapChainInfo.imageColorSpace = colorSpace;
swapChainInfo.imageExtent = bufferSize;
swapChainInfo.imageArrayLayers = 1;
swapChainInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
swapChainInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
swapChainInfo.preTransform = preTransform;
swapChainInfo.compositeAlpha = compositeAlpha;
swapChainInfo.presentMode = presentMode;
swapChainInfo.clipped = true;
swapChainInfo.oldSwapchain = oldSwapChain;
qDebug("creating new swap chain of %d buffers, size %dx%d", reqBufferCount, bufferSize.width, bufferSize.height);
VkSwapchainKHR newSwapChain;
VkResult err = m_vkCreateSwapchainKHR(m_vkDev, &swapChainInfo, nullptr, &newSwapChain);
if (err != VK_SUCCESS)
qFatal("Failed to create swap chain: %d", err);
if (oldSwapChain)
releaseSwapChain();
m_swapChain = newSwapChain;
m_swapChainBufferCount = 0;
err = m_vkGetSwapchainImagesKHR(m_vkDev, m_swapChain, &m_swapChainBufferCount, nullptr);
if (err != VK_SUCCESS || m_swapChainBufferCount < 2)
qFatal("Failed to get swapchain images: %d (count=%d)", err, m_swapChainBufferCount);
qDebug("actual swap chain buffer count: %d", m_swapChainBufferCount);
Q_ASSERT(m_swapChainBufferCount <= SWAPCHAIN_BUFFER_COUNT);
VkImage swapChainImages[SWAPCHAIN_BUFFER_COUNT];
err = m_vkGetSwapchainImagesKHR(m_vkDev, m_swapChain, &m_swapChainBufferCount, swapChainImages);
if (err != VK_SUCCESS)
qFatal("Failed to get swapchain images: %d", err);
// Now that we know m_colorFormat, create the default renderpass, the framebuffers will need it.
createDefaultRenderPass();
VkFenceCreateInfo fenceInfo = { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, nullptr, VK_FENCE_CREATE_SIGNALED_BIT };
for (uint32_t i = 0; i < m_swapChainBufferCount; ++i) {
ImageResources &image(m_imageRes[i]);
image.image = swapChainImages[i];
VkImageViewCreateInfo imgViewInfo;
memset(&imgViewInfo, 0, sizeof(imgViewInfo));
imgViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
imgViewInfo.image = swapChainImages[i];
imgViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
imgViewInfo.format = m_colorFormat;
imgViewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
imgViewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
imgViewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
imgViewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
imgViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
imgViewInfo.subresourceRange.levelCount = imgViewInfo.subresourceRange.layerCount = 1;
err = m_devFuncs->vkCreateImageView(m_vkDev, &imgViewInfo, nullptr, &image.imageView);
if (err != VK_SUCCESS)
qFatal("Failed to create swapchain image view %d: %d", i, err);
err = m_devFuncs->vkCreateFence(m_vkDev, &fenceInfo, nullptr, &image.cmdFence);
if (err != VK_SUCCESS)
qFatal("Failed to create command buffer fence: %d", err);
image.cmdFenceWaitable = true;
VkImageView views[1] = { image.imageView };
VkFramebufferCreateInfo fbInfo;
memset(&fbInfo, 0, sizeof(fbInfo));
fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
fbInfo.renderPass = m_defaultRenderPass;
fbInfo.attachmentCount = 1;
fbInfo.pAttachments = views;
fbInfo.width = m_swapChainImageSize.width();
fbInfo.height = m_swapChainImageSize.height();
fbInfo.layers = 1;
VkResult err = m_devFuncs->vkCreateFramebuffer(m_vkDev, &fbInfo, nullptr, &image.fb);
if (err != VK_SUCCESS)
qFatal("Failed to create framebuffer: %d", err);
}
m_currentImage = 0;
VkSemaphoreCreateInfo semInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, nullptr, 0 };
for (uint32_t i = 0; i < FRAME_LAG; ++i) {
FrameResources &frame(m_frameRes[i]);
m_devFuncs->vkCreateFence(m_vkDev, &fenceInfo, nullptr, &frame.fence);
frame.fenceWaitable = true;
m_devFuncs->vkCreateSemaphore(m_vkDev, &semInfo, nullptr, &frame.imageSem);
m_devFuncs->vkCreateSemaphore(m_vkDev, &semInfo, nullptr, &frame.drawSem);
}
m_currentFrame = 0;
}
void VWindow::createDefaultRenderPass()
{
VkAttachmentDescription attDesc[1];
memset(attDesc, 0, sizeof(attDesc));
attDesc[0].format = m_colorFormat;
attDesc[0].samples = VK_SAMPLE_COUNT_1_BIT;
attDesc[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attDesc[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attDesc[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
attDesc[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
attDesc[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attDesc[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
VkAttachmentReference colorRef = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
VkSubpassDescription subPassDesc;
memset(&subPassDesc, 0, sizeof(subPassDesc));
subPassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subPassDesc.colorAttachmentCount = 1;
subPassDesc.pColorAttachments = &colorRef;
VkRenderPassCreateInfo rpInfo;
memset(&rpInfo, 0, sizeof(rpInfo));
rpInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
rpInfo.attachmentCount = 1;
rpInfo.pAttachments = attDesc;
rpInfo.subpassCount = 1;
rpInfo.pSubpasses = &subPassDesc;
VkResult err = m_devFuncs->vkCreateRenderPass(m_vkDev, &rpInfo, nullptr, &m_defaultRenderPass);
if (err != VK_SUCCESS)
qFatal("Failed to create renderpass: %d", err);
}
void VWindow::releaseSwapChain()
{
if (!m_vkDev)
return;
m_devFuncs->vkDeviceWaitIdle(m_vkDev);
if (m_defaultRenderPass) {
m_devFuncs->vkDestroyRenderPass(m_vkDev, m_defaultRenderPass, nullptr);
m_defaultRenderPass = 0;
}
for (uint32_t i = 0; i < FRAME_LAG; ++i) {
FrameResources &frame(m_frameRes[i]);
if (frame.fence) {
if (frame.fenceWaitable)
m_devFuncs->vkWaitForFences(m_vkDev, 1, &frame.fence, VK_TRUE, UINT64_MAX);
m_devFuncs->vkDestroyFence(m_vkDev, frame.fence, nullptr);
frame.fence = 0;
frame.fenceWaitable = false;
}
if (frame.imageSem) {
m_devFuncs->vkDestroySemaphore(m_vkDev, frame.imageSem, nullptr);
frame.imageSem = 0;
}
if (frame.drawSem) {
m_devFuncs->vkDestroySemaphore(m_vkDev, frame.drawSem, nullptr);
frame.drawSem = 0;
}
}
for (uint32_t i = 0; i < m_swapChainBufferCount; ++i) {
ImageResources &image(m_imageRes[i]);
if (image.cmdFence) {
if (image.cmdFenceWaitable)
m_devFuncs->vkWaitForFences(m_vkDev, 1, &image.cmdFence, VK_TRUE, UINT64_MAX);
m_devFuncs->vkDestroyFence(m_vkDev, image.cmdFence, nullptr);
image.cmdFence = 0;
image.cmdFenceWaitable = false;
}
if (image.fb) {
m_devFuncs->vkDestroyFramebuffer(m_vkDev, image.fb, nullptr);
image.fb = 0;
}
if (image.imageView) {
m_devFuncs->vkDestroyImageView(m_vkDev, image.imageView, nullptr);
image.imageView = 0;
}
if (image.cmdBuf) {
m_devFuncs->vkFreeCommandBuffers(m_vkDev, m_vkCmdPool, 1, &image.cmdBuf);
image.cmdBuf = 0;
}
}
if (m_swapChain) {
m_vkDestroySwapchainKHR(m_vkDev, m_swapChain, nullptr);
m_swapChain = 0;
}
}
void VWindow::render()
{
if (!m_swapChain)
return;
if (size() != m_swapChainImageSize) {
recreateSwapChain();
if (!m_swapChain)
return;
}
FrameResources &frame(m_frameRes[m_currentFrame]);
// Wait if we are too far ahead, i.e. the thread gets throttled based on the presentation rate
// (note that we are using FIFO mode -> vsync)
if (frame.fenceWaitable) {
m_devFuncs->vkWaitForFences(m_vkDev, 1, &frame.fence, VK_TRUE, UINT64_MAX);
m_devFuncs->vkResetFences(m_vkDev, 1, &frame.fence);
}
// move on to next swapchain image
VkResult err = m_vkAcquireNextImageKHR(m_vkDev, m_swapChain, UINT64_MAX,
frame.imageSem, frame.fence, &m_currentImage);
if (err == VK_SUCCESS || err == VK_SUBOPTIMAL_KHR) {
frame.fenceWaitable = true;
} else if (err == VK_ERROR_OUT_OF_DATE_KHR) {
frame.fenceWaitable = false;
recreateSwapChain();
requestUpdate();
return;
} else {
qWarning("Failed to acquire next swapchain image: %d", err);
frame.fenceWaitable = false;
requestUpdate();
return;
}
// make sure the previous draw for the same image has finished
ImageResources &image(m_imageRes[m_currentImage]);
if (image.cmdFenceWaitable) {
m_devFuncs->vkWaitForFences(m_vkDev, 1, &image.cmdFence, VK_TRUE, UINT64_MAX);
m_devFuncs->vkResetFences(m_vkDev, 1, &image.cmdFence);
}
// build new draw command buffer
buildDrawCalls();
// submit draw calls
VkSubmitInfo submitInfo;
memset(&submitInfo, 0, sizeof(submitInfo));
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &image.cmdBuf;
submitInfo.waitSemaphoreCount = 1;
submitInfo.pWaitSemaphores = &frame.imageSem;
submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = &frame.drawSem;
VkPipelineStageFlags psf = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
submitInfo.pWaitDstStageMask = &psf;
err = m_devFuncs->vkQueueSubmit(m_vkGfxQueue, 1, &submitInfo, image.cmdFence);
if (err == VK_SUCCESS) {
image.cmdFenceWaitable = true;
} else {
qWarning("Failed to submit to command queue: %d", err);
image.cmdFenceWaitable = false;
}
// queue present
VkPresentInfoKHR presInfo;
memset(&presInfo, 0, sizeof(presInfo));
presInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presInfo.swapchainCount = 1;
presInfo.pSwapchains = &m_swapChain;
presInfo.pImageIndices = &m_currentImage;
presInfo.waitSemaphoreCount = 1;
presInfo.pWaitSemaphores = &frame.drawSem;
// we do not currently handle the case when the present queue is separate
Q_ASSERT(m_vkGfxQueue == m_vkPresQueue);
err = m_vkQueuePresentKHR(m_vkGfxQueue, &presInfo);
if (err != VK_SUCCESS) {
if (err == VK_ERROR_OUT_OF_DATE_KHR) {
recreateSwapChain();
requestUpdate();
return;
} else if (err != VK_SUBOPTIMAL_KHR) {
qWarning("Failed to present: %d", err);
}
}
vulkanInstance()->presentQueued(this);
m_currentFrame = (m_currentFrame + 1) % FRAME_LAG;
requestUpdate();
}
void VWindow::buildDrawCalls()
{
ImageResources &image(m_imageRes[m_currentImage]);
if (image.cmdBuf) {
m_devFuncs->vkFreeCommandBuffers(m_vkDev, m_vkCmdPool, 1, &image.cmdBuf);
image.cmdBuf = 0;
}
VkCommandBufferAllocateInfo cmdBufInfo = {
VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, nullptr, m_vkCmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1 };
VkResult err = m_devFuncs->vkAllocateCommandBuffers(m_vkDev, &cmdBufInfo, &image.cmdBuf);
if (err != VK_SUCCESS)
qFatal("Failed to allocate frame command buffer: %d", err);
VkCommandBufferBeginInfo cmdBufBeginInfo = {
VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, nullptr, 0, nullptr };
err = m_devFuncs->vkBeginCommandBuffer(image.cmdBuf, &cmdBufBeginInfo);
if (err != VK_SUCCESS)
qFatal("Failed to begin frame command buffer: %d", err);
static float g = 0;
g += 0.005f;
if (g > 1.0f)
g = 0.0f;
VkClearColorValue clearColor = { 0.0f, g, 0.0f, 1.0f };
VkClearValue clearValues[1];
clearValues[0].color = clearColor;
VkRenderPassBeginInfo rpBeginInfo;
memset(&rpBeginInfo, 0, sizeof(rpBeginInfo));
rpBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
rpBeginInfo.renderPass = m_defaultRenderPass;
rpBeginInfo.framebuffer = image.fb;
rpBeginInfo.renderArea.extent.width = m_swapChainImageSize.width();
rpBeginInfo.renderArea.extent.height = m_swapChainImageSize.height();
rpBeginInfo.clearValueCount = 1;
rpBeginInfo.pClearValues = clearValues;
m_devFuncs->vkCmdBeginRenderPass(image.cmdBuf, &rpBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
m_devFuncs->vkCmdEndRenderPass(image.cmdBuf);
err = m_devFuncs->vkEndCommandBuffer(image.cmdBuf);
if (err != VK_SUCCESS)
qFatal("Failed to end frame command buffer: %d", err);
}
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QLoggingCategory::setFilterRules(QStringLiteral("qt.vulkan=true"));
QVulkanInstance inst;
// Test the early queries for supported layers/exts.
qDebug() << inst.supportedLayers() << inst.supportedExtensions();
// Enable validation layer, if supported.
inst.setLayers(QByteArrayList() << "VK_LAYER_LUNARG_standard_validation");
bool ok = inst.create();
qDebug("QVulkanInstance::create() returned %d", ok);
if (!ok)
return 1;
VWindow w;
w.setVulkanInstance(&inst);
w.resize(1024, 768);
w.show();
return app.exec();
}

View File

@ -0,0 +1,6 @@
TEMPLATE = app
TARGET = qvulkaninstance
QT += gui-private
SOURCES += main.cpp