diff options
Diffstat (limited to 'src/gui/vulkan/qvulkanwindow.cpp')
-rw-r--r-- | src/gui/vulkan/qvulkanwindow.cpp | 234 |
1 files changed, 178 insertions, 56 deletions
diff --git a/src/gui/vulkan/qvulkanwindow.cpp b/src/gui/vulkan/qvulkanwindow.cpp index c4316d453b..00a5c5f869 100644 --- a/src/gui/vulkan/qvulkanwindow.cpp +++ b/src/gui/vulkan/qvulkanwindow.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qvulkanwindow_p.h" #include "qvulkanfunctions.h" @@ -47,7 +11,7 @@ QT_BEGIN_NAMESPACE -Q_LOGGING_CATEGORY(lcGuiVk, "qt.vulkan") +Q_DECLARE_LOGGING_CATEGORY(lcGuiVk) /*! \class QVulkanWindow @@ -195,6 +159,31 @@ Q_LOGGING_CATEGORY(lcGuiVk, "qt.vulkan") \note QVulkanWindow does not expose device layers since this functionality has been deprecated since version 1.0.13 of the Vulkan API. + \section1 Layers, device features, and extensions + + To enable instance layers, call QVulkanInstance::setLayers() before creating + the QVulkanInstance. To query what instance layer are available, call + QVulkanInstance::supportedLayers(). + + To enable device extensions, call setDeviceExtensions() early on when setting + up the QVulkanWindow. To query what device extensions are available, call + supportedDeviceExtensions(). + + Specifying an unsupported layer or extension is handled gracefully: this will + not fail instance or device creation, but the layer or extension request is + rather ignored. + + When it comes to device features, QVulkanWindow enables all Vulkan 1.0 + features that are reported as supported from vkGetPhysicalDeviceFeatures(). + As an exception to this rule, \c robustBufferAccess is never enabled. Use the + callback mechanism described below, if enabling that feature is desired. + + This is not always desirable, and may be insufficient with Vulkan 1.1 and + higher. Therefore, full control over the VkPhysicalDeviceFeatures used for + device creation is possible too by registering a callback function with + setEnabledFeaturesModifier(). When set, the callback function is invoked, + letting it alter the VkPhysicalDeviceFeatures or VkPhysicalDeviceFeatures2. + \sa QVulkanInstance, QWindow */ @@ -345,7 +334,7 @@ void QVulkanWindow::setPhysicalDeviceIndex(int idx) qWarning("QVulkanWindow: Attempted to set physical device when already initialized"); return; } - const int count = availablePhysicalDevices().count(); + const int count = availablePhysicalDevices().size(); if (idx < 0 || idx >= count) { qWarning("QVulkanWindow: Invalid physical device index %d (total physical devices: %d)", idx, count); return; @@ -458,7 +447,7 @@ void QVulkanWindow::setPreferredColorFormats(const QList<VkFormat> &formats) static struct { VkSampleCountFlagBits mask; int count; -} qvk_sampleCounts[] = { +} q_vk_sampleCounts[] = { // keep this sorted by 'count' { VK_SAMPLE_COUNT_1_BIT, 1 }, { VK_SAMPLE_COUNT_2_BIT, 2 }, @@ -498,7 +487,7 @@ QList<int> QVulkanWindow::supportedSampleCounts() VkSampleCountFlags depth = limits->framebufferDepthSampleCounts; VkSampleCountFlags stencil = limits->framebufferStencilSampleCounts; - for (const auto &qvk_sampleCount : qvk_sampleCounts) { + for (const auto &qvk_sampleCount : q_vk_sampleCounts) { if ((color & qvk_sampleCount.mask) && (depth & qvk_sampleCount.mask) && (stencil & qvk_sampleCount.mask)) @@ -547,7 +536,7 @@ void QVulkanWindow::setSampleCount(int sampleCount) return; } - for (const auto &qvk_sampleCount : qvk_sampleCounts) { + for (const auto &qvk_sampleCount : q_vk_sampleCounts) { if (qvk_sampleCount.count == sampleCount) { d->sampleCount = qvk_sampleCount.mask; return; @@ -591,7 +580,7 @@ void QVulkanWindowPrivate::init() return; } - if (physDevIndex < 0 || physDevIndex >= physDevs.count()) { + if (physDevIndex < 0 || physDevIndex >= physDevs.size()) { qWarning("QVulkanWindow: Invalid physical device index; defaulting to 0"); physDevIndex = 0; } @@ -610,7 +599,7 @@ void QVulkanWindowPrivate::init() f->vkGetPhysicalDeviceQueueFamilyProperties(physDev, &queueCount, queueFamilyProps.data()); gfxQueueFamilyIdx = uint32_t(-1); presQueueFamilyIdx = uint32_t(-1); - for (int i = 0; i < queueFamilyProps.count(); ++i) { + for (int i = 0; i < queueFamilyProps.size(); ++i) { const bool supportsPresent = inst->supportsPresent(physDev, i, q); qCDebug(lcGuiVk, "queue family %d: flags=0x%x count=%d supportsPresent=%d", i, queueFamilyProps[i].queueFlags, queueFamilyProps[i].queueCount, supportsPresent); @@ -623,7 +612,7 @@ void QVulkanWindowPrivate::init() presQueueFamilyIdx = gfxQueueFamilyIdx; } else { qCDebug(lcGuiVk, "No queue with graphics+present; trying separate queues"); - for (int i = 0; i < queueFamilyProps.count(); ++i) { + for (int i = 0; i < queueFamilyProps.size(); ++i) { if (gfxQueueFamilyIdx == uint32_t(-1) && (queueFamilyProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)) gfxQueueFamilyIdx = i; if (presQueueFamilyIdx == uint32_t(-1) && inst->supportsPresent(physDev, i, q)) @@ -667,7 +656,7 @@ void QVulkanWindowPrivate::init() queueCreateInfoModifier(queueFamilyProps.constData(), queueCount, queueInfo); bool foundGfxQueue = false; bool foundPresQueue = false; - for (const VkDeviceQueueCreateInfo& createInfo : qAsConst(queueInfo)) { + for (const VkDeviceQueueCreateInfo& createInfo : std::as_const(queueInfo)) { foundGfxQueue |= createInfo.queueFamilyIndex == gfxQueueFamilyIdx; foundPresQueue |= createInfo.queueFamilyIndex == presQueueFamilyIdx; } @@ -709,9 +698,26 @@ void QVulkanWindowPrivate::init() devInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; devInfo.queueCreateInfoCount = queueInfo.size(); devInfo.pQueueCreateInfos = queueInfo.constData(); - devInfo.enabledExtensionCount = devExts.count(); + devInfo.enabledExtensionCount = devExts.size(); devInfo.ppEnabledExtensionNames = devExts.constData(); + VkPhysicalDeviceFeatures features = {}; + VkPhysicalDeviceFeatures2 features2 = {}; + if (enabledFeatures2Modifier) { + features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + enabledFeatures2Modifier(features2); + devInfo.pNext = &features2; + } else if (enabledFeaturesModifier) { + enabledFeaturesModifier(features); + devInfo.pEnabledFeatures = &features; + } else { + // Enable all supported 1.0 core features, except ones that likely + // involve a performance penalty. + f->vkGetPhysicalDeviceFeatures(physDev, &features); + features.robustBufferAccess = VK_FALSE; + devInfo.pEnabledFeatures = &features; + } + // Device layers are not supported by QVulkanWindow since that's an already deprecated // API. However, have a workaround for systems with older API and layers (f.ex. L4T // 24.2 for the Jetson TX1 provides API 1.0.13 and crashes when the validation layer @@ -722,7 +728,7 @@ void QVulkanWindowPrivate::init() && VK_VERSION_PATCH(apiVersion) <= 13) { // Make standard validation work at least. - const QByteArray stdValName = QByteArrayLiteral("VK_LAYER_LUNARG_standard_validation"); + const QByteArray stdValName = QByteArrayLiteral("VK_LAYER_KHRONOS_validation"); const char *stdValNamePtr = stdValName.constData(); if (inst->layers().contains(stdValName)) { uint32_t count = 0; @@ -732,7 +738,7 @@ void QVulkanWindowPrivate::init() err = f->vkEnumerateDeviceLayerProperties(physDev, &count, layerProps.data()); if (err == VK_SUCCESS) { for (const VkLayerProperties &prop : layerProps) { - if (!strncmp(prop.layerName, stdValNamePtr, stdValName.count())) { + if (!strncmp(prop.layerName, stdValNamePtr, stdValName.size())) { devInfo.enabledLayerCount = 1; devInfo.ppEnabledLayerNames = &stdValNamePtr; break; @@ -855,7 +861,7 @@ void QVulkanWindowPrivate::init() // Try to honor the user request. if (!formats.isEmpty() && !requestedColorFormats.isEmpty()) { - for (VkFormat reqFmt : qAsConst(requestedColorFormats)) { + for (VkFormat reqFmt : std::as_const(requestedColorFormats)) { auto r = std::find_if(formats.cbegin(), formats.cend(), [reqFmt](const VkSurfaceFormatKHR &sfmt) { return sfmt.format == reqFmt; }); if (r != formats.cend()) { @@ -1330,6 +1336,7 @@ bool QVulkanWindowPrivate::createTransientImage(VkFormat format, VkMemoryRequirements memReq; VkResult err; + Q_ASSERT(count > 0); for (int i = 0; i < count; ++i) { VkImageCreateInfo imgInfo; memset(&imgInfo, 0, sizeof(imgInfo)); @@ -1610,6 +1617,84 @@ void QVulkanWindow::setQueueCreateInfoModifier(const QueueCreateInfoModifier &mo d->queueCreateInfoModifier = modifier; } +/*! + \typedef QVulkanWindow::EnabledFeaturesModifier + + A function that is called during graphics initialization to alter the + VkPhysicalDeviceFeatures that is passed in when creating a Vulkan device + object. + + By default QVulkanWindow enables all Vulkan 1.0 core features that the + physical device reports as supported, with certain exceptions. In + praticular, \c robustBufferAccess is always disabled in order to avoid + unexpected performance hits. + + The VkPhysicalDeviceFeatures reference passed in is all zeroed out at the + point when the function is invoked. It is up to the function to change + members as it sees fit. + + \note To control Vulkan 1.1, 1.2, or 1.3 features, use + EnabledFeatures2Modifier instead. + + \sa setEnabledFeaturesModifier() + */ + +/*! + Sets the enabled device features modification function \a modifier. + + \note To control Vulkan 1.1, 1.2, or 1.3 features, use + the overload taking a EnabledFeatures2Modifier instead. + + \note \a modifier is passed to the callback function with all members set + to false. It is up to the function to change members as it sees fit. + + \since 6.7 + \sa EnabledFeaturesModifier + */ +void QVulkanWindow::setEnabledFeaturesModifier(const EnabledFeaturesModifier &modifier) +{ + Q_D(QVulkanWindow); + d->enabledFeaturesModifier = modifier; +} + +/*! + \typedef QVulkanWindow::EnabledFeatures2Modifier + + A function that is called during graphics initialization to alter the + VkPhysicalDeviceFeatures2 that is changed to the VkDeviceCreateInfo. + + By default QVulkanWindow enables all Vulkan 1.0 core features that the + physical device reports as supported, with certain exceptions. In + praticular, \c robustBufferAccess is always disabled in order to avoid + unexpected performance hits. + + This however is not always sufficient when working with Vulkan 1.1, 1.2, or + 1.3 features and extensions. Hence this callback mechanism. If only Vulkan + 1.0 is relevant at run time, use setEnabledFeaturesModifier() instead. + + The VkPhysicalDeviceFeatures2 reference passed to the callback function + with \c sType set, but the rest zeroed out. It is up to the function to + change members to true, or set up \c pNext chains as it sees fit. + + \note When setting up \c pNext chains, make sure the referenced objects + have a long enough lifetime, for example by storing them as member + variables in the QVulkanWindow subclass. + + \since 6.7 + \sa setEnabledFeaturesModifier() + */ + +/*! + Sets the enabled device features modification function \a modifier. + \overload + \since 6.7 + \sa EnabledFeatures2Modifier +*/ +void QVulkanWindow::setEnabledFeaturesModifier(EnabledFeatures2Modifier modifier) +{ + Q_D(QVulkanWindow); + d->enabledFeatures2Modifier = std::move(modifier); +} /*! Returns true if this window has successfully initialized all Vulkan @@ -1811,13 +1896,26 @@ void QVulkanWindowRenderer::logicalDeviceLost() { } +QSize QVulkanWindowPrivate::surfacePixelSize() const +{ + Q_Q(const QVulkanWindow); + VkSurfaceCapabilitiesKHR surfaceCaps = {}; + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physDevs.at(physDevIndex), surface, &surfaceCaps); + VkExtent2D bufferSize = surfaceCaps.currentExtent; + if (bufferSize.width == uint32_t(-1)) { + Q_ASSERT(bufferSize.height == uint32_t(-1)); + return q->size() * q->devicePixelRatio(); + } + return QSize(int(bufferSize.width), int(bufferSize.height)); +} + void QVulkanWindowPrivate::beginFrame() { if (!swapChain || framePending) return; Q_Q(QVulkanWindow); - if (q->size() * q->devicePixelRatio() != swapChainImageSize) { + if (swapChainImageSize != surfacePixelSize()) { recreateSwapChain(); if (!swapChain) return; @@ -1886,7 +1984,7 @@ void QVulkanWindowPrivate::beginFrame() } if (frameGrabbing) - frameGrabTargetImage = QImage(swapChainImageSize, QImage::Format_RGBA8888); + frameGrabTargetImage = QImage(swapChainImageSize, QImage::Format_RGBA8888); // the format is as documented if (renderer) { framePending = true; @@ -2020,7 +2118,7 @@ void QVulkanWindowPrivate::endFrame() // order to circumvent driver frame callbacks inst->presentAboutToBeQueued(q); - err = vkQueuePresentKHR(gfxQueue, &presInfo); + err = vkQueuePresentKHR(presQueue, &presInfo); if (err != VK_SUCCESS) { if (err == VK_ERROR_OUT_OF_DATE_KHR) { recreateSwapChain(); @@ -2232,7 +2330,7 @@ void QVulkanWindowPrivate::finishBlockingReadback() VkPhysicalDevice QVulkanWindow::physicalDevice() const { Q_D(const QVulkanWindow); - if (d->physDevIndex < d->physDevs.count()) + if (d->physDevIndex < d->physDevs.size()) return d->physDevs[d->physDevIndex]; qWarning("QVulkanWindow: Physical device not available"); return VK_NULL_HANDLE; @@ -2248,7 +2346,7 @@ VkPhysicalDevice QVulkanWindow::physicalDevice() const const VkPhysicalDeviceProperties *QVulkanWindow::physicalDeviceProperties() const { Q_D(const QVulkanWindow); - if (d->physDevIndex < d->physDevProps.count()) + if (d->physDevIndex < d->physDevProps.size()) return &d->physDevProps[d->physDevIndex]; qWarning("QVulkanWindow: Physical device properties not available"); return nullptr; @@ -2402,6 +2500,19 @@ VkFormat QVulkanWindow::depthStencilFormat() const This usually matches the size of the window, but may also differ in case \c vkGetPhysicalDeviceSurfaceCapabilitiesKHR reports a fixed size. + In addition, it has been observed on some platforms that the + Vulkan-reported surface size is different with high DPI scaling active, + meaning the QWindow-reported + \l{QWindow::}{size()} multiplied with the \l{QWindow::}{devicePixelRatio()} + was 1 pixel less or more when compared to the value returned from here, + presumably due to differences in rounding. Rendering code should be aware + of this, and any related rendering logic must be based in the value returned + from here, never on the QWindow-reported size. Regardless of which pixel size + is correct in theory, Vulkan rendering must only ever rely on the Vulkan + API-reported surface size. Otherwise validation errors may occur, e.g. when + setting the viewport, because the application-provided values may become + out-of-bounds from Vulkan's perspective. + \note Calling this function is only valid from the invocation of QVulkanWindowRenderer::initSwapChainResources() up until QVulkanWindowRenderer::releaseSwapChainResources(). @@ -2673,6 +2784,12 @@ bool QVulkanWindow::supportsGrab() const incomplete image, that has the correct size but not the content yet. The content will be delivered via the frameGrabbed() signal in the latter case. + The returned QImage always has a format of QImage::Format_RGBA8888. If the + colorFormat() is \c VK_FORMAT_B8G8R8A8_UNORM, the red and blue channels are + swapped automatically since this format is commonly used as the default + choice for swapchain color buffers. With any other color buffer format, + there is no conversion performed by this function. + \note This function should not be called when a frame is in progress (that is, frameReady() has not yet been called back by the application). @@ -2701,6 +2818,9 @@ QImage QVulkanWindow::grab() d->frameGrabbing = true; d->beginFrame(); + if (d->colorFormat == VK_FORMAT_B8G8R8A8_UNORM) + d->frameGrabTargetImage = std::move(d->frameGrabTargetImage).rgbSwapped(); + return d->frameGrabTargetImage; } @@ -2729,3 +2849,5 @@ QMatrix4x4 QVulkanWindow::clipCorrectionMatrix() } QT_END_NAMESPACE + +#include "moc_qvulkanwindow.cpp" |