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:
parent
a512c9c2f7
commit
f1a23a5467
58
config.tests/qpa/vulkan/vulkan.cpp
Normal file
58
config.tests/qpa/vulkan/vulkan.cpp
Normal 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;
|
||||
}
|
1
config.tests/qpa/vulkan/vulkan.pro
Normal file
1
config.tests/qpa/vulkan/vulkan.pro
Normal file
@ -0,0 +1 @@
|
||||
SOURCES = vulkan.cpp
|
@ -35,6 +35,7 @@ QMAKE_LIBS_OPENGL = -lGL
|
||||
QMAKE_LIBS_OPENGL_ES2 = -lGLESv2
|
||||
QMAKE_LIBS_OPENVG = -lOpenVG
|
||||
QMAKE_LIBS_THREAD = -lpthread
|
||||
QMAKE_LIBS_VULKAN =
|
||||
|
||||
QMAKE_INCDIR_WAYLAND =
|
||||
QMAKE_LIBS_WAYLAND_CLIENT = -lwayland-client
|
||||
|
@ -106,3 +106,4 @@ VCSOLUTION_EXTENSION = .sln
|
||||
VCPROJ_KEYWORD = Qt4VSv1.0
|
||||
|
||||
include(windows-gles.conf)
|
||||
include(windows-vulkan.conf)
|
||||
|
5
mkspecs/common/windows-vulkan.conf
Normal file
5
mkspecs/common/windows-vulkan.conf
Normal 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 =
|
@ -80,6 +80,7 @@ QMAKE_OBJCOPY = $${CROSS_COMPILE}objcopy
|
||||
QMAKE_NM = $${CROSS_COMPILE}nm -P
|
||||
|
||||
include(../common/windows-gles.conf)
|
||||
include(../common/windows-vulkan.conf)
|
||||
include(../common/gcc-base.conf)
|
||||
|
||||
load(qt_config)
|
||||
|
@ -59,5 +59,6 @@ QMAKE_LIB = xilib /NOLOGO
|
||||
DSP_EXTENSION = .dsp
|
||||
|
||||
include(../common/windows-gles.conf)
|
||||
include(../common/windows-vulkan.conf)
|
||||
|
||||
load(qt_config)
|
||||
|
@ -38,6 +38,7 @@
|
||||
"qpa-platform-guard": "boolean",
|
||||
"sm": { "type": "boolean", "name": "sessionmanager" },
|
||||
"tslib": "boolean",
|
||||
"vulkan": "boolean",
|
||||
"xcb": { "type": "enum", "values": [ "no", "yes", "qt", "system" ] },
|
||||
"xcb-xlib": "boolean",
|
||||
"xinput2": "boolean",
|
||||
@ -211,6 +212,14 @@
|
||||
"-lts"
|
||||
]
|
||||
},
|
||||
"vulkan": {
|
||||
"label": "Vulkan",
|
||||
"test": "qpa/vulkan",
|
||||
"sources": [
|
||||
{ "type": "pkgConfig", "args": "vulkan" },
|
||||
{ "type": "makeSpec", "spec": "VULKAN" }
|
||||
]
|
||||
},
|
||||
"wayland_server": {
|
||||
"label": "Wayland Server",
|
||||
"test": "qpa/wayland-server",
|
||||
@ -605,6 +614,11 @@
|
||||
"condition": "features.opengl-desktop || features.opengl-dynamic || features.opengles2",
|
||||
"output": [ "publicFeature", "feature" ]
|
||||
},
|
||||
"vulkan": {
|
||||
"label": "Vulkan",
|
||||
"condition": "libs.vulkan",
|
||||
"output": [ "publicFeature" ]
|
||||
},
|
||||
"openvg": {
|
||||
"label": "OpenVG",
|
||||
"condition": "libs.openvg",
|
||||
@ -1096,6 +1110,7 @@ QMAKE_LIBDIR_OPENGL[_ES2] and QMAKE_LIBS_OPENGL[_ES2] in the mkspec for your pla
|
||||
"opengles31"
|
||||
]
|
||||
},
|
||||
"vulkan",
|
||||
"sessionmanager"
|
||||
]
|
||||
},
|
||||
|
@ -46,6 +46,7 @@ include(math3d/math3d.pri)
|
||||
include(opengl/opengl.pri)
|
||||
include(animation/animation.pri)
|
||||
include(itemmodels/itemmodels.pri)
|
||||
include(vulkan/vulkan.pri)
|
||||
|
||||
QMAKE_LIBS += $$QMAKE_LIBS_GUI
|
||||
|
||||
|
@ -629,4 +629,26 @@ void QPlatformIntegration::setApplicationIcon(const QIcon &icon) const
|
||||
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
|
||||
|
@ -78,6 +78,8 @@ class QPlatformSessionManager;
|
||||
class QKeyEvent;
|
||||
class QPlatformOffscreenSurface;
|
||||
class QOffscreenSurface;
|
||||
class QPlatformVulkanInstance;
|
||||
class QVulkanInstance;
|
||||
|
||||
class Q_GUI_EXPORT QPlatformIntegration
|
||||
{
|
||||
@ -190,6 +192,10 @@ public:
|
||||
|
||||
virtual void beep() const;
|
||||
|
||||
#if QT_CONFIG(vulkan)
|
||||
virtual QPlatformVulkanInstance *createPlatformVulkanInstance(QVulkanInstance *instance) const;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
void screenAdded(QPlatformScreen *screen, bool isPrimary = false);
|
||||
void destroyScreen(QPlatformScreen *screen);
|
||||
|
@ -78,6 +78,8 @@ QT_BEGIN_NAMESPACE
|
||||
requires the use of private API.
|
||||
\value OpenVGSurface The surface is an OpenVG compatible surface and can be used
|
||||
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.
|
||||
*/
|
||||
|
||||
|
||||
|
@ -66,6 +66,7 @@ public:
|
||||
OpenGLSurface,
|
||||
RasterGLSurface,
|
||||
OpenVGSurface,
|
||||
VulkanSurface
|
||||
};
|
||||
|
||||
virtual ~QSurface();
|
||||
|
@ -2797,6 +2797,30 @@ QDebug operator<<(QDebug debug, const QWindow *window)
|
||||
}
|
||||
#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
|
||||
|
||||
#include "moc_qwindow.cpp"
|
||||
|
@ -88,6 +88,9 @@ class QWindowContainer;
|
||||
#ifndef QT_NO_DEBUG_STREAM
|
||||
class QDebug;
|
||||
#endif
|
||||
#if QT_CONFIG(vulkan)
|
||||
class QVulkanInstance;
|
||||
#endif
|
||||
|
||||
class Q_GUI_EXPORT QWindow : public QObject, public QSurface
|
||||
{
|
||||
@ -269,6 +272,11 @@ public:
|
||||
|
||||
static QWindow *fromWinId(WId id);
|
||||
|
||||
#if QT_CONFIG(vulkan)
|
||||
void setVulkanInstance(QVulkanInstance *instance);
|
||||
QVulkanInstance *vulkanInstance() const;
|
||||
#endif
|
||||
|
||||
public Q_SLOTS:
|
||||
Q_REVISION(1) void requestActivate();
|
||||
|
||||
|
@ -105,6 +105,9 @@ public:
|
||||
, hasCursor(false)
|
||||
#endif
|
||||
, compositing(false)
|
||||
#if QT_CONFIG(vulkan)
|
||||
, vulkanInstance(nullptr)
|
||||
#endif
|
||||
{
|
||||
isWindow = true;
|
||||
}
|
||||
@ -196,6 +199,10 @@ public:
|
||||
|
||||
bool compositing;
|
||||
QElapsedTimer lastComposeTime;
|
||||
|
||||
#if QT_CONFIG(vulkan)
|
||||
QVulkanInstance *vulkanInstance;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
|
88
src/gui/vulkan/qplatformvulkaninstance.cpp
Normal file
88
src/gui/vulkan/qplatformvulkaninstance.cpp
Normal 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
|
91
src/gui/vulkan/qplatformvulkaninstance.h
Normal file
91
src/gui/vulkan/qplatformvulkaninstance.h
Normal 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
|
177
src/gui/vulkan/qvulkanfunctions.cpp
Normal file
177
src/gui/vulkan/qvulkanfunctions.cpp
Normal 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
|
836
src/gui/vulkan/qvulkaninstance.cpp
Normal file
836
src/gui/vulkan/qvulkaninstance.cpp
Normal 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
|
163
src/gui/vulkan/qvulkaninstance.h
Normal file
163
src/gui/vulkan/qvulkaninstance.h
Normal 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
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
47
src/gui/vulkan/vulkan.pri
Normal 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
|
@ -42,3 +42,6 @@ darwin {
|
||||
macos: \
|
||||
SUBDIRS += cglconvenience
|
||||
}
|
||||
|
||||
qtConfig(vulkan): \
|
||||
SUBDIRS += vkconvenience
|
||||
|
@ -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
|
@ -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
|
16
src/platformsupport/vkconvenience/vkconvenience.pro
Normal file
16
src/platformsupport/vkconvenience/vkconvenience.pro
Normal 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)
|
@ -11,6 +11,8 @@ QT += \
|
||||
eventdispatcher_support-private accessibility_support-private \
|
||||
fontdatabase_support-private egl_support-private
|
||||
|
||||
qtConfig(vulkan): QT += vulkan_support-private
|
||||
|
||||
OTHER_FILES += $$PWD/android.json
|
||||
|
||||
INCLUDEPATH += \
|
||||
@ -78,6 +80,13 @@ HEADERS += $$PWD/qandroidplatformintegration.h \
|
||||
qtConfig(android-style-assets): SOURCES += $$PWD/extract.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
|
||||
load(qt_plugin)
|
||||
|
||||
|
@ -69,6 +69,11 @@
|
||||
|
||||
#include <QtPlatformHeaders/QEGLNativeContext>
|
||||
|
||||
#if QT_CONFIG(vulkan)
|
||||
#include "qandroidplatformvulkanwindow.h"
|
||||
#include "qandroidplatformvulkaninstance.h"
|
||||
#endif
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
int QAndroidPlatformIntegration::m_defaultGeometryWidth = 320;
|
||||
@ -121,6 +126,23 @@ void *QAndroidPlatformNativeInterface::nativeResourceForIntegration(const QByteA
|
||||
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 ¶mList)
|
||||
: m_touchDevice(nullptr)
|
||||
#ifndef QT_NO_ACCESSIBILITY
|
||||
@ -286,6 +308,11 @@ QPlatformWindow *QAndroidPlatformIntegration::createPlatformWindow(QWindow *wind
|
||||
if (!QtAndroid::activity())
|
||||
return nullptr;
|
||||
|
||||
#if QT_CONFIG(vulkan)
|
||||
if (window->surfaceType() == QSurface::VulkanSurface)
|
||||
return new QAndroidPlatformVulkanWindow(window);
|
||||
#endif
|
||||
|
||||
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)));
|
||||
}
|
||||
|
||||
#if QT_CONFIG(vulkan)
|
||||
|
||||
QPlatformVulkanInstance *QAndroidPlatformIntegration::createPlatformVulkanInstance(QVulkanInstance *instance) const
|
||||
{
|
||||
return new QAndroidPlatformVulkanInstance(instance);
|
||||
}
|
||||
|
||||
#endif // QT_CONFIG(vulkan)
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -40,6 +40,8 @@
|
||||
#ifndef QANDROIDPLATFORMINTERATION_H
|
||||
#define QANDROIDPLATFORMINTERATION_H
|
||||
|
||||
#include <QtGui/qtguiglobal.h>
|
||||
|
||||
#include <qpa/qplatformintegration.h>
|
||||
#include <qpa/qplatformmenu.h>
|
||||
#include <qpa/qplatformnativeinterface.h>
|
||||
@ -64,6 +66,7 @@ class QAndroidPlatformNativeInterface: public QPlatformNativeInterface
|
||||
{
|
||||
public:
|
||||
void *nativeResourceForIntegration(const QByteArray &resource) override;
|
||||
void *nativeResourceForWindow(const QByteArray &resource, QWindow *window) override;
|
||||
std::shared_ptr<AndroidStyle> m_androidStyle;
|
||||
};
|
||||
|
||||
@ -124,6 +127,10 @@ public:
|
||||
void setTouchDevice(QTouchDevice *touchDevice) { m_touchDevice = touchDevice; }
|
||||
static void setDefaultApplicationState(Qt::ApplicationState applicationState) { m_defaultApplicationState = applicationState; }
|
||||
|
||||
#if QT_CONFIG(vulkan)
|
||||
QPlatformVulkanInstance *createPlatformVulkanInstance(QVulkanInstance *instance) const override;
|
||||
#endif
|
||||
|
||||
private:
|
||||
EGLDisplay m_eglDisplay;
|
||||
QTouchDevice *m_touchDevice;
|
||||
|
@ -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
|
@ -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
|
210
src/plugins/platforms/android/qandroidplatformvulkanwindow.cpp
Normal file
210
src/plugins/platforms/android/qandroidplatformvulkanwindow.cpp
Normal 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
|
91
src/plugins/platforms/android/qandroidplatformvulkanwindow.h
Normal file
91
src/plugins/platforms/android/qandroidplatformvulkanwindow.h
Normal 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
|
@ -5,6 +5,8 @@ QT += \
|
||||
eventdispatcher_support-private accessibility_support-private \
|
||||
fontdatabase_support-private theme_support-private
|
||||
|
||||
qtConfig(vulkan): QT += vulkan_support-private
|
||||
|
||||
LIBS += -ldwmapi -ld2d1 -ld3d11 -ldwrite -lVersion -lgdi32
|
||||
|
||||
include(../windows/windows.pri)
|
||||
|
@ -606,4 +606,11 @@ void QWindowsIntegration::beep() const
|
||||
MessageBeep(MB_OK); // For QApplication
|
||||
}
|
||||
|
||||
#if QT_CONFIG(vulkan)
|
||||
QPlatformVulkanInstance *QWindowsIntegration::createPlatformVulkanInstance(QVulkanInstance *instance) const
|
||||
{
|
||||
return new QWindowsVulkanInstance(instance);
|
||||
}
|
||||
#endif
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -113,6 +113,10 @@ public:
|
||||
QPlatformSessionManager *createPlatformSessionManager(const QString &id, const QString &key) const override;
|
||||
#endif
|
||||
|
||||
#if QT_CONFIG(vulkan)
|
||||
QPlatformVulkanInstance *createPlatformVulkanInstance(QVulkanInstance *instance) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
virtual QWindowsWindow *createPlatformWindowHelper(QWindow *window, const QWindowsWindowData &) const;
|
||||
|
||||
|
@ -64,7 +64,8 @@ enum ResourceType {
|
||||
HandleType,
|
||||
GlHandleType,
|
||||
GetDCType,
|
||||
ReleaseDCType
|
||||
ReleaseDCType,
|
||||
VkSurface
|
||||
};
|
||||
|
||||
static int resourceType(const QByteArray &key)
|
||||
@ -77,7 +78,8 @@ static int resourceType(const QByteArray &key)
|
||||
"handle",
|
||||
"glhandle",
|
||||
"getdc",
|
||||
"releasedc"
|
||||
"releasedc",
|
||||
"vkSurface"
|
||||
};
|
||||
const char ** const end = names + sizeof(names) / sizeof(names[0]);
|
||||
const char **result = std::find(names, end, key);
|
||||
@ -112,6 +114,12 @@ void *QWindowsNativeInterface::nativeResourceForWindow(const QByteArray &resourc
|
||||
case QWindow::OpenGLSurface:
|
||||
case QWindow::OpenVGSurface:
|
||||
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());
|
||||
return 0;
|
||||
|
136
src/plugins/platforms/windows/qwindowsvulkaninstance.cpp
Normal file
136
src/plugins/platforms/windows/qwindowsvulkaninstance.cpp
Normal 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
|
76
src/plugins/platforms/windows/qwindowsvulkaninstance.h
Normal file
76
src/plugins/platforms/windows/qwindowsvulkaninstance.h
Normal 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
|
@ -67,6 +67,10 @@
|
||||
|
||||
#include <dwmapi.h>
|
||||
|
||||
#if QT_CONFIG(vulkan)
|
||||
#include "qwindowsvulkaninstance.h"
|
||||
#endif
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
enum {
|
||||
@ -308,13 +312,15 @@ static QWindow::Visibility windowVisibility_sys(HWND hwnd)
|
||||
return QWindow::Windowed;
|
||||
}
|
||||
|
||||
static inline bool windowIsOpenGL(const QWindow *w)
|
||||
static inline bool windowIsAccelerated(const QWindow *w)
|
||||
{
|
||||
switch (w->surfaceType()) {
|
||||
case QSurface::OpenGLSurface:
|
||||
return true;
|
||||
case QSurface::RasterGLSurface:
|
||||
return qt_window_private(const_cast<QWindow *>(w))->compositing;
|
||||
case QSurface::VulkanSurface:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@ -375,11 +381,11 @@ bool QWindowsWindow::setWindowLayered(HWND hwnd, Qt::WindowFlags flags, bool has
|
||||
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)) {
|
||||
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.
|
||||
BLENDFUNCTION blend = {AC_SRC_OVER, 0, alpha, AC_SRC_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)
|
||||
{
|
||||
const bool isGL = windowIsOpenGL(w);
|
||||
const bool isAccelerated = windowIsAccelerated(w);
|
||||
const bool hasAlpha = w->format().hasAlpha();
|
||||
|
||||
if (isGL && hasAlpha)
|
||||
if (isAccelerated && hasAlpha)
|
||||
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_cursor(new CursorHandle),
|
||||
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.
|
||||
QWindowsContext::instance()->setWindowCreationContext(QSharedPointer<QWindowCreationContext>());
|
||||
@ -1068,6 +1077,10 @@ QWindowsWindow::QWindowsWindow(QWindow *aWindow, const QWindowsWindowData &data)
|
||||
setFlag(OpenGL_ES2);
|
||||
}
|
||||
#endif // QT_NO_OPENGL
|
||||
#if QT_CONFIG(vulkan)
|
||||
if (aWindow->surfaceType() == QSurface::VulkanSurface)
|
||||
setFlag(VulkanSurface);
|
||||
#endif
|
||||
updateDropSite(window()->isTopLevel());
|
||||
|
||||
registerTouchWindow();
|
||||
@ -1121,6 +1134,14 @@ void QWindowsWindow::destroyWindow()
|
||||
if (hasMouseCapture())
|
||||
setMouseGrabEnabled(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
|
||||
if (m_surface) {
|
||||
if (QWindowsStaticOpenGLContext *staticOpenGLContext = QWindowsIntegration::staticOpenGLContext())
|
||||
@ -1470,8 +1491,10 @@ void QWindowsWindow::handleHidden()
|
||||
void QWindowsWindow::handleCompositionSettingsChanged()
|
||||
{
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
static QRect normalFrameGeometry(HWND hwnd)
|
||||
@ -1693,8 +1716,11 @@ bool QWindowsWindow::handleWmPaint(HWND hwnd, UINT message,
|
||||
BeginPaint(hwnd, &ps);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// If the a window is obscured by another window (such as a child window)
|
||||
// we still need to send isExposed=true, for compatibility.
|
||||
@ -2030,7 +2056,7 @@ void QWindowsWindow::setOpacity(qreal level)
|
||||
m_opacity = level;
|
||||
if (m_data.hwnd)
|
||||
setWindowOpacity(m_data.hwnd, m_data.flags,
|
||||
window()->format().hasAlpha(), testFlag(OpenGLSurface),
|
||||
window()->format().hasAlpha(), testFlag(OpenGLSurface) || testFlag(VulkanSurface),
|
||||
level);
|
||||
}
|
||||
}
|
||||
@ -2448,11 +2474,27 @@ void QWindowsWindow::setCustomMargins(const QMargins &newCustomMargins)
|
||||
|
||||
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(nativeConfig)
|
||||
return 0;
|
||||
#else
|
||||
#endif
|
||||
#ifndef QT_NO_OPENGL
|
||||
if (!m_surface) {
|
||||
if (QWindowsStaticOpenGLContext *staticOpenGLContext = QWindowsIntegration::staticOpenGLContext())
|
||||
m_surface = staticOpenGLContext->createWindowSurface(m_data.hwnd, nativeConfig, err);
|
||||
@ -2464,6 +2506,14 @@ void *QWindowsWindow::surface(void *nativeConfig, int *err)
|
||||
|
||||
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
|
||||
if (m_surface) {
|
||||
if (QWindowsStaticOpenGLContext *staticOpenGLContext = QWindowsIntegration::staticOpenGLContext())
|
||||
|
@ -46,6 +46,10 @@
|
||||
#include <qpa/qplatformwindow.h>
|
||||
#include <QtPlatformHeaders/qwindowswindowfunctions.h>
|
||||
|
||||
#if QT_CONFIG(vulkan)
|
||||
#include "qwindowsvulkaninstance.h"
|
||||
#endif
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QWindowsOleDropTarget;
|
||||
@ -209,6 +213,7 @@ public:
|
||||
Compositing = 0x200000,
|
||||
HasBorderInFullScreen = 0x400000,
|
||||
WithinDpiChanged = 0x800000,
|
||||
VulkanSurface = 0x1000000
|
||||
};
|
||||
|
||||
QWindowsWindow(QWindow *window, const QWindowsWindowData &data);
|
||||
@ -351,6 +356,11 @@ private:
|
||||
HICON m_iconSmall = 0;
|
||||
HICON m_iconBig = 0;
|
||||
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
|
||||
|
@ -68,6 +68,11 @@ qtConfig(dynamicgl) {
|
||||
HEADERS += $$PWD/qwindowseglcontext.h
|
||||
}
|
||||
|
||||
qtConfig(vulkan) {
|
||||
SOURCES += $$PWD/qwindowsvulkaninstance.cpp
|
||||
HEADERS += $$PWD/qwindowsvulkaninstance.h
|
||||
}
|
||||
|
||||
!contains( DEFINES, QT_NO_CLIPBOARD ) {
|
||||
SOURCES += $$PWD/qwindowsclipboard.cpp
|
||||
HEADERS += $$PWD/qwindowsclipboard.h
|
||||
|
@ -5,6 +5,8 @@ QT += \
|
||||
eventdispatcher_support-private accessibility_support-private \
|
||||
fontdatabase_support-private theme_support-private
|
||||
|
||||
qtConfig(vulkan): QT += vulkan_support-private
|
||||
|
||||
LIBS += -lgdi32 -ldwmapi
|
||||
|
||||
include(windows.pri)
|
||||
|
@ -83,6 +83,11 @@
|
||||
|
||||
#include <QtCore/QFileInfo>
|
||||
|
||||
#if QT_CONFIG(vulkan)
|
||||
#include "qxcbvulkaninstance.h"
|
||||
#include "qxcbvulkanwindow.h"
|
||||
#endif
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
// 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());
|
||||
QXcbGlIntegration *glIntegration = screen->connection()->glIntegration();
|
||||
if (window->type() != Qt::Desktop && window->supportsOpenGL()) {
|
||||
if (glIntegration) {
|
||||
QXcbWindow *xcbWindow = glIntegration->createWindow(window);
|
||||
if (window->type() != Qt::Desktop) {
|
||||
if (window->supportsOpenGL()) {
|
||||
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();
|
||||
return xcbWindow;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@ -498,4 +511,11 @@ void QXcbIntegration::beep() const
|
||||
xcb_bell(connection, 0);
|
||||
}
|
||||
|
||||
#if QT_CONFIG(vulkan)
|
||||
QPlatformVulkanInstance *QXcbIntegration::createPlatformVulkanInstance(QVulkanInstance *instance) const
|
||||
{
|
||||
return new QXcbVulkanInstance(instance);
|
||||
}
|
||||
#endif
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -113,6 +113,10 @@ public:
|
||||
|
||||
void beep() const override;
|
||||
|
||||
#if QT_CONFIG(vulkan)
|
||||
QPlatformVulkanInstance *createPlatformVulkanInstance(QVulkanInstance *instance) const override;
|
||||
#endif
|
||||
|
||||
static QXcbIntegration *instance() { return m_instance; }
|
||||
|
||||
private:
|
||||
|
@ -67,6 +67,10 @@
|
||||
|
||||
#include "qxcbnativeinterfacehandler.h"
|
||||
|
||||
#if QT_CONFIG(vulkan)
|
||||
#include "qxcbvulkanwindow.h"
|
||||
#endif
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
// return QXcbNativeInterface::ResourceType for the key.
|
||||
@ -82,7 +86,8 @@ static int resourceType(const QByteArray &key)
|
||||
QByteArrayLiteral("rootwindow"),
|
||||
QByteArrayLiteral("subpixeltype"), QByteArrayLiteral("antialiasingenabled"),
|
||||
QByteArrayLiteral("atspibus"),
|
||||
QByteArrayLiteral("compositingenabled")
|
||||
QByteArrayLiteral("compositingenabled"),
|
||||
QByteArrayLiteral("vksurface")
|
||||
};
|
||||
const QByteArray *end = names + sizeof(names) / sizeof(names[0]);
|
||||
const QByteArray *result = std::find(names, end, key);
|
||||
@ -257,6 +262,14 @@ void *QXcbNativeInterface::nativeResourceForWindow(const QByteArray &resourceStr
|
||||
case Screen:
|
||||
result = screenForWindow(window);
|
||||
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:
|
||||
break;
|
||||
}
|
||||
|
@ -73,7 +73,8 @@ public:
|
||||
ScreenSubpixelType,
|
||||
ScreenAntialiasingEnabled,
|
||||
AtspiBus,
|
||||
CompositingEnabled
|
||||
CompositingEnabled,
|
||||
VkSurface
|
||||
};
|
||||
|
||||
QXcbNativeInterface();
|
||||
|
156
src/plugins/platforms/xcb/qxcbvulkaninstance.cpp
Normal file
156
src/plugins/platforms/xcb/qxcbvulkaninstance.cpp
Normal 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
|
79
src/plugins/platforms/xcb/qxcbvulkaninstance.h
Normal file
79
src/plugins/platforms/xcb/qxcbvulkaninstance.h
Normal 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
|
83
src/plugins/platforms/xcb/qxcbvulkanwindow.cpp
Normal file
83
src/plugins/platforms/xcb/qxcbvulkanwindow.cpp
Normal 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
|
65
src/plugins/platforms/xcb/qxcbvulkanwindow.h
Normal file
65
src/plugins/platforms/xcb/qxcbvulkanwindow.h
Normal 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
|
@ -10,6 +10,8 @@ QT += \
|
||||
qtHaveModule(linuxaccessibility_support-private): \
|
||||
QT += linuxaccessibility_support-private
|
||||
|
||||
qtConfig(vulkan): QT += vulkan_support-private
|
||||
|
||||
SOURCES = \
|
||||
qxcbclipboard.cpp \
|
||||
qxcbconnection.cpp \
|
||||
@ -70,6 +72,16 @@ qtConfig(xcb-sm) {
|
||||
|
||||
include(gl_integrations/gl_integrations.pri)
|
||||
|
||||
qtConfig(vulkan) {
|
||||
SOURCES += \
|
||||
qxcbvulkaninstance.cpp \
|
||||
qxcbvulkanwindow.cpp
|
||||
|
||||
HEADERS += \
|
||||
qxcbvulkaninstance.h \
|
||||
qxcbvulkanwindow.h
|
||||
}
|
||||
|
||||
!qtConfig(system-xcb) {
|
||||
DEFINES += XCB_USE_RENDER
|
||||
QMAKE_USE += xcb-static xcb
|
||||
|
@ -49,6 +49,11 @@ src_tools_qdbuscpp2xml.target = sub-qdbuscpp2xml
|
||||
force_bootstrap: src_tools_qdbuscpp2xml.depends = src_tools_bootstrap_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.target = sub-winmain
|
||||
src_winmain.depends = sub-corelib # just for the module .pri file
|
||||
@ -185,6 +190,9 @@ qtConfig(gui) {
|
||||
SUBDIRS += 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
|
||||
qtConfig(opengl): SUBDIRS += src_openglextensions
|
||||
src_plugins.depends += src_gui src_platformsupport src_platformheaders
|
||||
|
530
src/tools/qvkgen/qvkgen.cpp
Normal file
530
src/tools/qvkgen/qvkgen.cpp
Normal 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;
|
||||
}
|
5
src/tools/qvkgen/qvkgen.pro
Normal file
5
src/tools/qvkgen/qvkgen.pro
Normal file
@ -0,0 +1,5 @@
|
||||
option(host_build)
|
||||
|
||||
SOURCES += qvkgen.cpp
|
||||
|
||||
load(qt_tool)
|
@ -26,6 +26,7 @@
|
||||
"QtFbSupport" => "$basedir/src/platformsupport/fbconvenience",
|
||||
"QtGlxSupport" => "$basedir/src/platformsupport/glxconvenience",
|
||||
"QtKmsSupport" => "$basedir/src/platformsupport/kmsconvenience",
|
||||
"QtVulkanSupport" => "$basedir/src/platformsupport/vkconvenience",
|
||||
"QtPlatformHeaders" => "$basedir/src/platformheaders",
|
||||
"QtANGLE/KHR" => "!$basedir/src/3rdparty/angle/include/KHR",
|
||||
"QtANGLE/GLES2" => "!$basedir/src/3rdparty/angle/include/GLES2",
|
||||
@ -52,6 +53,7 @@
|
||||
"qnamespace.h" => "Qt",
|
||||
"qnumeric.h" => "QtNumeric",
|
||||
"qvariant.h" => "QVariantHash,QVariantList,QVariantMap",
|
||||
"qvulkanfunctions.h" => "QVulkanFunctions,QVulkanDeviceFunctions",
|
||||
"qgl.h" => "QGL",
|
||||
"qsql.h" => "QSql",
|
||||
"qssl.h" => "QSsl",
|
||||
@ -80,4 +82,7 @@ my @zlib_headers = ( "zconf.h", "zlib.h" );
|
||||
@ignore_headers = ( @internal_zlib_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);
|
||||
%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" ]
|
||||
);
|
||||
|
@ -9,8 +9,11 @@ SUBDIRS = \
|
||||
painting \
|
||||
qopenglconfig \
|
||||
qopengl \
|
||||
qvulkan \
|
||||
text \
|
||||
util \
|
||||
itemmodels \
|
||||
|
||||
!qtConfig(opengl): SUBDIRS -= qopengl qopenglconfig
|
||||
|
||||
!qtConfig(vulkan): SUBDIRS -= qvulkan
|
||||
|
9
tests/auto/gui/qvulkan/qvulkan.pro
Normal file
9
tests/auto/gui/qvulkan/qvulkan.pro
Normal 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
|
159
tests/auto/gui/qvulkan/tst_qvulkan.cpp
Normal file
159
tests/auto/gui/qvulkan/tst_qvulkan.cpp
Normal 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"
|
@ -67,3 +67,5 @@ win32: SUBDIRS -= network_remote_stresstest network_stresstest
|
||||
lessThan(QT_MAJOR_VERSION, 5): SUBDIRS -= bearerex lance qnetworkaccessmanager/qget qmimedatabase qnetworkreply \
|
||||
qpainfo qscreen socketengine xembed-raster xembed-widgets windowtransparency \
|
||||
embeddedintoforeignwindow foreignwindows
|
||||
|
||||
qtConfig(vulkan): SUBDIRS += qvulkaninstance
|
||||
|
713
tests/manual/qvulkaninstance/main.cpp
Normal file
713
tests/manual/qvulkaninstance/main.cpp
Normal 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();
|
||||
}
|
6
tests/manual/qvulkaninstance/qvulkaninstance.pro
Normal file
6
tests/manual/qvulkaninstance/qvulkaninstance.pro
Normal file
@ -0,0 +1,6 @@
|
||||
TEMPLATE = app
|
||||
TARGET = qvulkaninstance
|
||||
|
||||
QT += gui-private
|
||||
|
||||
SOURCES += main.cpp
|
Loading…
Reference in New Issue
Block a user