diff options
Diffstat (limited to 'src/gui/vulkan/qvulkaninstance.cpp')
-rw-r--r-- | src/gui/vulkan/qvulkaninstance.cpp | 894 |
1 files changed, 894 insertions, 0 deletions
diff --git a/src/gui/vulkan/qvulkaninstance.cpp b/src/gui/vulkan/qvulkaninstance.cpp new file mode 100644 index 0000000000..8236d9625a --- /dev/null +++ b/src/gui/vulkan/qvulkaninstance.cpp @@ -0,0 +1,894 @@ +/**************************************************************************** +** +** 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 <private/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() \e before invoking create(). + + To enable additional layers and extensions, provide the list via + setLayers() and setExtensions() \e before 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 and QVulkanDeviceFunctions objects accessible via + functions() and deviceFunctions(). + + \note QVulkanFunctions and QVulkanDeviceFunctions are generated from the + Vulkan API XML specifications when building the Qt libraries. Therefore no + documentation is provided for them. They contain the Vulkan 1.0 functions + with the same signatures as described in the + \l{https://www.khronos.org/registry/vulkan/specs/1.0/html/}{Vulkan API + documentation}. + + \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(); +} + +/*! + \class QVulkanLayer + \brief Represents information about a Vulkan layer. + */ + +/*! + \variable QVulkanLayer::name + \brief The name of the layer. + */ + +/*! + \variable QVulkanLayer::version + \brief The version of the layer. This is an integer, increasing with each backward + compatible change. + */ + +/*! + \variable QVulkanLayer::specVersion + \brief The Vulkan version the layer was written against. + */ + +/*! + \variable QVulkanLayer::description + \brief The description of the layer. + */ + +/*! + \fn bool operator==(const QVulkanLayer &lhs, const QVulkanLayer &rhs) + \since 5.10 + \relates QVulkanLayer + + Returns \c true if Vulkan layers \a lhs and \a rhs have + the same name, version, and spec version. +*/ + +/*! + \fn bool operator!=(const QVulkanLayer &lhs, const QVulkanLayer &rhs) + \since 5.10 + \relates QVulkanLayer + + Returns \c true if Vulkan layers \a lhs and \a rhs have + different name, version, or spec version. +*/ + +/*! + \fn uint qHash(const QVulkanLayer &key, uint seed) + \since 5.10 + \relates QVulkanLayer + + Returns the hash value for the \a key, using \a seed to seed the + calculation. +*/ + +/*! + \class QVulkanExtension + \brief Represents information about a Vulkan extension. + */ + +/*! + \variable QVulkanExtension::name + \brief The name of the extension. + */ + +/*! + \variable QVulkanExtension::version + \brief The version of the extension. This is an integer, increasing with each backward + compatible change. + */ + +/*! + \fn bool operator==(const QVulkanExtension &lhs, const QVulkanExtension &rhs) + \since 5.10 + \relates QVulkanExtension + + Returns \c true if Vulkan extensions \a lhs and \a rhs are have the + same name and version. +*/ + +/*! + \fn bool operator!=(const QVulkanExtension &lhs, const QVulkanExtension &rhs) + \since 5.10 + \relates QVulkanExtension + + Returns \c true if Vulkan extensions \a lhs and \a rhs are have different + name or version. +*/ + +/*! + \fn uint qHash(const QVulkanExtension &key, uint seed) + \since 5.10 + \relates QVulkanExtension + + Returns the hash value for the \a key, using \a seed to seed the + calculation. +*/ + +/*! + \class QVulkanInfoVector + \brief A specialized QVector for QVulkanLayer and QVulkanExtension. + */ + +/*! + \fn template<typename T> bool QVulkanInfoVector<T>::contains(const QByteArray &name) const + + \return true if the vector contains a layer or extension with the given \a name. + */ + +/*! + \fn template<typename T> bool QVulkanInfoVector<T>::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 prefer using the function wrappers retrievable from + functions() and deviceFunctions() instead. + */ +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 |