diff options
Diffstat (limited to 'src/gui/rhi/qrhivulkan.cpp')
-rw-r--r-- | src/gui/rhi/qrhivulkan.cpp | 2295 |
1 files changed, 1509 insertions, 786 deletions
diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp index 025419d045..3dd3c57bd4 100644 --- a/src/gui/rhi/qrhivulkan.cpp +++ b/src/gui/rhi/qrhivulkan.cpp @@ -1,46 +1,11 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Qt Gui module -** -** $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 "qrhivulkan_p_p.h" -#include "qrhivulkanext_p.h" +// Copyright (C) 2023 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 "qrhivulkan_p.h" +#include <qpa/qplatformvulkaninstance.h> #define VMA_IMPLEMENTATION +#define VMA_DYNAMIC_VULKAN_FUNCTIONS 1 #define VMA_STATIC_VULKAN_FUNCTIONS 0 #define VMA_RECORDING_ENABLED 0 #define VMA_DEDICATED_ALLOCATION 0 @@ -58,6 +23,7 @@ QT_WARNING_POP #include <qmath.h> #include <QVulkanFunctions> #include <QtGui/qwindow.h> +#include <optional> QT_BEGIN_NAMESPACE @@ -93,10 +59,13 @@ QT_BEGIN_NAMESPACE /*! \class QRhiVulkanInitParams - \internal \inmodule QtGui + \since 6.6 \brief Vulkan specific initialization parameters. + \note This is a RHI API with limited compatibility guarantees, see \l QRhi + for details. + A Vulkan-based QRhi needs at minimum a valid QVulkanInstance. It is up to the user to ensure this is available and initialized. This is typically done in main() similarly to the following: @@ -199,34 +168,121 @@ QT_BEGIN_NAMESPACE */ /*! + \variable QRhiVulkanInitParams::inst + + The QVulkanInstance that has already been successfully + \l{QVulkanInstance::create()}{created}, required. +*/ + +/*! + \variable QRhiVulkanInitParams::window + + Optional, but recommended when targeting a QWindow. +*/ + +/*! + \variable QRhiVulkanInitParams::deviceExtensions + + Optional, empty by default. The list of Vulkan device extensions to enable. + Unsupported extensions are ignored gracefully. +*/ + +/*! \class QRhiVulkanNativeHandles - \internal \inmodule QtGui + \since 6.6 \brief Collects device, queue, and other Vulkan objects that are used by the QRhi. \note Ownership of the Vulkan objects is never transferred. + + \note This is a RHI API with limited compatibility guarantees, see \l QRhi + for details. */ /*! + \variable QRhiVulkanNativeHandles::physDev + + When different from \nullptr, specifies the Vulkan physical device to use. +*/ + +/*! + \variable QRhiVulkanNativeHandles::dev + + When wanting to import not just a physical device, but also use an already + existing VkDevice, set this and the graphics queue index and family index. +*/ + +/*! + \variable QRhiVulkanNativeHandles::gfxQueueFamilyIdx + + Graphics queue family index. +*/ + +/*! + \variable QRhiVulkanNativeHandles::gfxQueueIdx + + Graphics queue index. +*/ + +/*! + \variable QRhiVulkanNativeHandles::vmemAllocator + + Relevant only when importing an existing memory allocator object, + leave it set to \nullptr otherwise. +*/ + +/*! + \variable QRhiVulkanNativeHandles::gfxQueue + + Output only, not used by QRhi::create(), only set by the + QRhi::nativeHandles() accessor. The graphics VkQueue used by the QRhi. +*/ + +/*! + \variable QRhiVulkanNativeHandles::inst + + Output only, not used by QRhi::create(), only set by the + QRhi::nativeHandles() accessor. The QVulkanInstance used by the QRhi. +*/ + +/*! \class QRhiVulkanCommandBufferNativeHandles - \internal \inmodule QtGui + \since 6.6 \brief Holds the Vulkan command buffer object that is backing a QRhiCommandBuffer. \note The Vulkan command buffer object is only guaranteed to be valid, and in recording state, while recording a frame. That is, between a \l{QRhi::beginFrame()}{beginFrame()} - \l{QRhi::endFrame()}{endFrame()} or \l{QRhi::beginOffscreenFrame()}{beginOffscreenFrame()} - - \l{QRhi::endOffsrceenFrame()}{endOffscreenFrame()} pair. + \l{QRhi::endOffscreenFrame()}{endOffscreenFrame()} pair. + + \note This is a RHI API with limited compatibility guarantees, see \l QRhi + for details. */ /*! + \variable QRhiVulkanCommandBufferNativeHandles::commandBuffer + + The VkCommandBuffer object. +*/ + +/*! \class QRhiVulkanRenderPassNativeHandles - \internal \inmodule QtGui + \since 6.6 \brief Holds the Vulkan render pass object backing a QRhiRenderPassDescriptor. + + \note This is a RHI API with limited compatibility guarantees, see \l QRhi + for details. */ +/*! + \variable QRhiVulkanRenderPassNativeHandles::renderPass + + The VkRenderPass object. +*/ + template <class Int> inline Int aligned(Int v, Int byteAlign) { @@ -235,84 +291,14 @@ inline Int aligned(Int v, Int byteAlign) static QVulkanInstance *globalVulkanInstance; -static void VKAPI_PTR wrap_vkGetPhysicalDeviceProperties(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties* pProperties) -{ - globalVulkanInstance->functions()->vkGetPhysicalDeviceProperties(physicalDevice, pProperties); -} - -static void VKAPI_PTR wrap_vkGetPhysicalDeviceMemoryProperties(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties* pMemoryProperties) +static VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL wrap_vkGetInstanceProcAddr(VkInstance, const char *pName) { - globalVulkanInstance->functions()->vkGetPhysicalDeviceMemoryProperties(physicalDevice, pMemoryProperties); + return globalVulkanInstance->getInstanceProcAddr(pName); } -static VkResult VKAPI_PTR wrap_vkAllocateMemory(VkDevice device, const VkMemoryAllocateInfo* pAllocateInfo, const VkAllocationCallbacks* pAllocator, VkDeviceMemory* pMemory) +static VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL wrap_vkGetDeviceProcAddr(VkDevice device, const char *pName) { - return globalVulkanInstance->deviceFunctions(device)->vkAllocateMemory(device, pAllocateInfo, pAllocator, pMemory); -} - -void VKAPI_PTR wrap_vkFreeMemory(VkDevice device, VkDeviceMemory memory, const VkAllocationCallbacks* pAllocator) -{ - globalVulkanInstance->deviceFunctions(device)->vkFreeMemory(device, memory, pAllocator); -} - -VkResult VKAPI_PTR wrap_vkMapMemory(VkDevice device, VkDeviceMemory memory, VkDeviceSize offset, VkDeviceSize size, VkMemoryMapFlags flags, void** ppData) -{ - return globalVulkanInstance->deviceFunctions(device)->vkMapMemory(device, memory, offset, size, flags, ppData); -} - -void VKAPI_PTR wrap_vkUnmapMemory(VkDevice device, VkDeviceMemory memory) -{ - globalVulkanInstance->deviceFunctions(device)->vkUnmapMemory(device, memory); -} - -VkResult VKAPI_PTR wrap_vkFlushMappedMemoryRanges(VkDevice device, uint32_t memoryRangeCount, const VkMappedMemoryRange* pMemoryRanges) -{ - return globalVulkanInstance->deviceFunctions(device)->vkFlushMappedMemoryRanges(device, memoryRangeCount, pMemoryRanges); -} - -VkResult VKAPI_PTR wrap_vkInvalidateMappedMemoryRanges(VkDevice device, uint32_t memoryRangeCount, const VkMappedMemoryRange* pMemoryRanges) -{ - return globalVulkanInstance->deviceFunctions(device)->vkInvalidateMappedMemoryRanges(device, memoryRangeCount, pMemoryRanges); -} - -VkResult VKAPI_PTR wrap_vkBindBufferMemory(VkDevice device, VkBuffer buffer, VkDeviceMemory memory, VkDeviceSize memoryOffset) -{ - return globalVulkanInstance->deviceFunctions(device)->vkBindBufferMemory(device, buffer, memory, memoryOffset); -} - -VkResult VKAPI_PTR wrap_vkBindImageMemory(VkDevice device, VkImage image, VkDeviceMemory memory, VkDeviceSize memoryOffset) -{ - return globalVulkanInstance->deviceFunctions(device)->vkBindImageMemory(device, image, memory, memoryOffset); -} - -void VKAPI_PTR wrap_vkGetBufferMemoryRequirements(VkDevice device, VkBuffer buffer, VkMemoryRequirements* pMemoryRequirements) -{ - globalVulkanInstance->deviceFunctions(device)->vkGetBufferMemoryRequirements(device, buffer, pMemoryRequirements); -} - -void VKAPI_PTR wrap_vkGetImageMemoryRequirements(VkDevice device, VkImage image, VkMemoryRequirements* pMemoryRequirements) -{ - globalVulkanInstance->deviceFunctions(device)->vkGetImageMemoryRequirements(device, image, pMemoryRequirements); -} - -VkResult VKAPI_PTR wrap_vkCreateBuffer(VkDevice device, const VkBufferCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBuffer* pBuffer) -{ - return globalVulkanInstance->deviceFunctions(device)->vkCreateBuffer(device, pCreateInfo, pAllocator, pBuffer); -} - -void VKAPI_PTR wrap_vkDestroyBuffer(VkDevice device, VkBuffer buffer, const VkAllocationCallbacks* pAllocator) -{ - globalVulkanInstance->deviceFunctions(device)->vkDestroyBuffer(device, buffer, pAllocator); -} - -VkResult VKAPI_PTR wrap_vkCreateImage(VkDevice device, const VkImageCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkImage* pImage) -{ - return globalVulkanInstance->deviceFunctions(device)->vkCreateImage(device, pCreateInfo, pAllocator, pImage); -} - -void VKAPI_PTR wrap_vkDestroyImage(VkDevice device, VkImage image, const VkAllocationCallbacks* pAllocator) -{ - globalVulkanInstance->deviceFunctions(device)->vkDestroyImage(device, image, pAllocator); + return globalVulkanInstance->functions()->vkGetDeviceProcAddr(device, pName); } static inline VmaAllocation toVmaAllocation(QVkAlloc a) @@ -325,6 +311,13 @@ static inline VmaAllocator toVmaAllocator(QVkAllocator a) return reinterpret_cast<VmaAllocator>(a); } +/*! + \return the list of instance extensions that are expected to be enabled on + the QVulkanInstance that is used for the Vulkan-based QRhi. + + The returned list can be safely passed to QVulkanInstance::setExtensions() + as-is, because unsupported extensions are filtered out automatically. + */ QByteArrayList QRhiVulkanInitParams::preferredInstanceExtensions() { return { @@ -332,12 +325,18 @@ QByteArrayList QRhiVulkanInitParams::preferredInstanceExtensions() }; } +/*! + \return the list of device extensions that are expected to be enabled on the + \c VkDevice when creating a Vulkan-based QRhi with an externally created + \c VkDevice object. + */ QByteArrayList QRhiVulkanInitParams::preferredExtensionsForImportedDevice() { return { QByteArrayLiteral("VK_KHR_swapchain"), - QByteArrayLiteral("VK_EXT_debug_marker"), - QByteArrayLiteral("VK_EXT_vertex_attribute_divisor") + QByteArrayLiteral("VK_EXT_vertex_attribute_divisor"), + QByteArrayLiteral("VK_KHR_create_renderpass2"), + QByteArrayLiteral("VK_KHR_depth_stencil_resolve") }; } @@ -364,20 +363,19 @@ QRhiVulkan::QRhiVulkan(QRhiVulkanInitParams *params, QRhiVulkanNativeHandles *im } } -static bool qvk_debug_filter(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, - size_t location, int32_t messageCode, const char *pLayerPrefix, const char *pMessage) +static bool qvk_debug_filter(QVulkanInstance::DebugMessageSeverityFlags severity, + QVulkanInstance::DebugMessageTypeFlags type, + const void *callbackData) { - Q_UNUSED(flags); - Q_UNUSED(objectType); - Q_UNUSED(object); - Q_UNUSED(location); - Q_UNUSED(messageCode); - Q_UNUSED(pLayerPrefix); + Q_UNUSED(severity); + Q_UNUSED(type); +#ifdef VK_EXT_debug_utils + const VkDebugUtilsMessengerCallbackDataEXT *d = static_cast<const VkDebugUtilsMessengerCallbackDataEXT *>(callbackData); // Filter out certain misleading validation layer messages, as per // VulkanMemoryAllocator documentation. - if (strstr(pMessage, "Mapping an image with layout") - && strstr(pMessage, "can result in undefined behavior if this memory is used by the device")) + if (strstr(d->pMessage, "Mapping an image with layout") + && strstr(d->pMessage, "can result in undefined behavior if this memory is used by the device")) { return true; } @@ -388,9 +386,11 @@ static bool qvk_debug_filter(VkDebugReportFlagsEXT flags, VkDebugReportObjectTyp // then move on to another pool. If there is a real error, a qWarning // message is shown by allocateDescriptorSet(), so the validation warning // does not have any value and is just noise. - if (strstr(pMessage, "VUID-VkDescriptorSetAllocateInfo-descriptorPool-00307")) + if (strstr(d->pMessage, "VUID-VkDescriptorSetAllocateInfo-descriptorPool-00307")) return true; - +#else + Q_UNUSED(callbackData); +#endif return false; } @@ -420,13 +420,19 @@ bool QRhiVulkan::create(QRhi::Flags flags) return false; } - globalVulkanInstance = inst; // assume this will not change during the lifetime of the entire application + rhiFlags = flags; + qCDebug(QRHI_LOG_INFO, "Initializing QRhi Vulkan backend %p with flags %d", this, int(rhiFlags)); + globalVulkanInstance = inst; // used for function resolving in vkmemalloc callbacks f = inst->functions(); + if (QRHI_LOG_INFO().isEnabled(QtDebugMsg)) { + qCDebug(QRHI_LOG_INFO, "Enabled instance extensions:"); + for (const char *ext : inst->extensions()) + qCDebug(QRHI_LOG_INFO, " %s", ext); + } - caps.vulkan11OrHigher = inst->apiVersion() >= QVersionNumber(1, 1); - - rhiFlags = flags; + caps = {}; + caps.debugUtils = inst->extensions().contains(QByteArrayLiteral("VK_EXT_debug_utils")); QList<VkQueueFamilyProperties> queueFamilyProps; auto queryQueueFamilyProps = [this, &queueFamilyProps] { @@ -507,50 +513,125 @@ bool QRhiVulkan::create(QRhi::Flags flags) physDevProperties.deviceType); } + caps.apiVersion = inst->apiVersion(); + + // Check the physical device API version against the instance API version, + // they do not have to match, which means whatever version was set in the + // QVulkanInstance may not be legally used with a given device if the + // physical device has a lower version. + const QVersionNumber physDevApiVersion(VK_VERSION_MAJOR(physDevProperties.apiVersion), + VK_VERSION_MINOR(physDevProperties.apiVersion)); // patch version left out intentionally + if (physDevApiVersion < caps.apiVersion) { + qCDebug(QRHI_LOG_INFO) << "Instance has api version" << caps.apiVersion + << "whereas the chosen physical device has" << physDevApiVersion + << "- restricting to the latter"; + caps.apiVersion = physDevApiVersion; + } + driverInfoStruct.deviceName = QByteArray(physDevProperties.deviceName); driverInfoStruct.deviceId = physDevProperties.deviceID; driverInfoStruct.vendorId = physDevProperties.vendorID; driverInfoStruct.deviceType = toRhiDeviceType(physDevProperties.deviceType); - f->vkGetPhysicalDeviceFeatures(physDev, &physDevFeatures); + bool featuresQueried = false; +#ifdef VK_VERSION_1_1 + VkPhysicalDeviceFeatures2 physDevFeaturesChainable = {}; + physDevFeaturesChainable.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; +#endif + + // Vulkan >=1.2 headers at build time, >=1.2 implementation at run time +#ifdef VK_VERSION_1_2 + if (!featuresQueried) { + // Vulkan11Features, Vulkan12Features, etc. are only in Vulkan 1.2 and newer. + if (caps.apiVersion >= QVersionNumber(1, 2)) { + physDevFeatures11IfApi12OrNewer = {}; + physDevFeatures11IfApi12OrNewer.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES; + physDevFeatures12 = {}; + physDevFeatures12.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES; +#ifdef VK_VERSION_1_3 + physDevFeatures13 = {}; + physDevFeatures13.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES; +#endif + physDevFeaturesChainable.pNext = &physDevFeatures11IfApi12OrNewer; + physDevFeatures11IfApi12OrNewer.pNext = &physDevFeatures12; +#ifdef VK_VERSION_1_3 + if (caps.apiVersion >= QVersionNumber(1, 3)) + physDevFeatures12.pNext = &physDevFeatures13; +#endif + f->vkGetPhysicalDeviceFeatures2(physDev, &physDevFeaturesChainable); + memcpy(&physDevFeatures, &physDevFeaturesChainable.features, sizeof(VkPhysicalDeviceFeatures)); + featuresQueried = true; + } + } +#endif // VK_VERSION_1_2 + + // Vulkan >=1.1 headers at build time, 1.1 implementation at run time +#ifdef VK_VERSION_1_1 + if (!featuresQueried) { + // Vulkan versioning nightmares: if the runtime API version is 1.1, + // there is no Vulkan11Features (introduced in 1.2+, the headers might + // have the types and structs, but the Vulkan implementation version at + // run time is what matters). But there are individual feature structs. + // For multiview, it is important to get this right since at the time of + // writing Quest 3 Android is a Vulkan 1.1 implementation at run time on + // the headset. + if (caps.apiVersion == QVersionNumber(1, 1)) { + multiviewFeaturesIfApi11 = {}; + multiviewFeaturesIfApi11.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES; + physDevFeaturesChainable.pNext = &multiviewFeaturesIfApi11; + f->vkGetPhysicalDeviceFeatures2(physDev, &physDevFeaturesChainable); + memcpy(&physDevFeatures, &physDevFeaturesChainable.features, sizeof(VkPhysicalDeviceFeatures)); + featuresQueried = true; + } + } +#endif + + if (!featuresQueried) { + // If the API version at run time is 1.0 (or we are building with + // ancient 1.0 headers), then do the Vulkan 1.0 query. + f->vkGetPhysicalDeviceFeatures(physDev, &physDevFeatures); + featuresQueried = true; + } // Choose queue and create device, unless the device was specified in importParams. if (!importedDevice) { // We only support combined graphics+present queues. When it comes to // compute, only combined graphics+compute queue is used, compute gets // disabled otherwise. - gfxQueueFamilyIdx = -1; - int computelessGfxQueueCandidateIdx = -1; + std::optional<uint32_t> gfxQueueFamilyIdxOpt; + std::optional<uint32_t> computelessGfxQueueCandidateIdxOpt; queryQueueFamilyProps(); - for (int i = 0; i < queueFamilyProps.count(); ++i) { - qCDebug(QRHI_LOG_INFO, "queue family %d: flags=0x%x count=%d", + const uint32_t queueFamilyCount = uint32_t(queueFamilyProps.size()); + for (uint32_t i = 0; i < queueFamilyCount; ++i) { + qCDebug(QRHI_LOG_INFO, "queue family %u: flags=0x%x count=%u", i, queueFamilyProps[i].queueFlags, queueFamilyProps[i].queueCount); - if (gfxQueueFamilyIdx == -1 + if (!gfxQueueFamilyIdxOpt.has_value() && (queueFamilyProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) - && (!maybeWindow || inst->supportsPresent(physDev, uint32_t(i), maybeWindow))) + && (!maybeWindow || inst->supportsPresent(physDev, i, maybeWindow))) { if (queueFamilyProps[i].queueFlags & VK_QUEUE_COMPUTE_BIT) - gfxQueueFamilyIdx = i; - else if (computelessGfxQueueCandidateIdx == -1) - computelessGfxQueueCandidateIdx = i; + gfxQueueFamilyIdxOpt = i; + else if (!computelessGfxQueueCandidateIdxOpt.has_value()) + computelessGfxQueueCandidateIdxOpt = i; } } - if (gfxQueueFamilyIdx == -1) { - if (computelessGfxQueueCandidateIdx != -1) { - gfxQueueFamilyIdx = computelessGfxQueueCandidateIdx; + if (gfxQueueFamilyIdxOpt.has_value()) { + gfxQueueFamilyIdx = gfxQueueFamilyIdxOpt.value(); + } else { + if (computelessGfxQueueCandidateIdxOpt.has_value()) { + gfxQueueFamilyIdx = computelessGfxQueueCandidateIdxOpt.value(); } else { qWarning("No graphics (or no graphics+present) queue family found"); return false; } } - VkDeviceQueueCreateInfo queueInfo[2]; + VkDeviceQueueCreateInfo queueInfo = {}; const float prio[] = { 0 }; - memset(queueInfo, 0, sizeof(queueInfo)); - queueInfo[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - queueInfo[0].queueFamilyIndex = uint32_t(gfxQueueFamilyIdx); - queueInfo[0].queueCount = 1; - queueInfo[0].pQueuePriorities = prio; + queueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queueInfo.queueFamilyIndex = gfxQueueFamilyIdx; + queueInfo.queueCount = 1; + queueInfo.pQueuePriorities = prio; QList<const char *> devLayers; if (inst->layers().contains("VK_LAYER_KHRONOS_validation")) @@ -562,44 +643,69 @@ bool QRhiVulkan::create(QRhi::Flags flags) if (devExtCount) { QList<VkExtensionProperties> extProps(devExtCount); f->vkEnumerateDeviceExtensionProperties(physDev, nullptr, &devExtCount, extProps.data()); - for (const VkExtensionProperties &p : qAsConst(extProps)) + for (const VkExtensionProperties &p : std::as_const(extProps)) devExts.append({ p.extensionName, p.specVersion }); } - qCDebug(QRHI_LOG_INFO, "%d device extensions available", int(devExts.count())); + qCDebug(QRHI_LOG_INFO, "%d device extensions available", int(devExts.size())); QList<const char *> requestedDevExts; requestedDevExts.append("VK_KHR_swapchain"); - caps.debugMarkers = false; - if (devExts.contains(VK_EXT_DEBUG_MARKER_EXTENSION_NAME)) { - requestedDevExts.append(VK_EXT_DEBUG_MARKER_EXTENSION_NAME); - caps.debugMarkers = true; + const bool hasPhysDevProp2 = inst->extensions().contains(QByteArrayLiteral("VK_KHR_get_physical_device_properties2")); + + if (devExts.contains(QByteArrayLiteral("VK_KHR_portability_subset"))) { + if (hasPhysDevProp2) { + requestedDevExts.append("VK_KHR_portability_subset"); + } else { + qWarning("VK_KHR_portability_subset should be enabled on the device " + "but the instance does not have VK_KHR_get_physical_device_properties2 enabled. " + "Expect problems."); + } } - caps.vertexAttribDivisor = false; +#ifdef VK_EXT_vertex_attribute_divisor if (devExts.contains(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME)) { - if (inst->extensions().contains(QByteArrayLiteral("VK_KHR_get_physical_device_properties2"))) { + if (hasPhysDevProp2) { requestedDevExts.append(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME); caps.vertexAttribDivisor = true; } } +#endif + +#ifdef VK_KHR_create_renderpass2 + if (devExts.contains(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME)) { + requestedDevExts.append(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME); + caps.renderPass2KHR = true; + } +#endif + +#ifdef VK_KHR_depth_stencil_resolve + if (devExts.contains(VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME)) { + requestedDevExts.append(VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME); + caps.depthStencilResolveKHR = true; + } +#endif for (const QByteArray &ext : requestedDeviceExtensions) { - if (!ext.isEmpty()) { - if (devExts.contains(ext)) + if (!ext.isEmpty() && !requestedDevExts.contains(ext)) { + if (devExts.contains(ext)) { requestedDevExts.append(ext.constData()); - else - qWarning("Device extension %s is not supported", ext.constData()); + } else { + qWarning("Device extension %s requested in QRhiVulkanInitParams is not supported", + ext.constData()); + } } } QByteArrayList envExtList = qgetenv("QT_VULKAN_DEVICE_EXTENSIONS").split(';'); for (const QByteArray &ext : envExtList) { if (!ext.isEmpty() && !requestedDevExts.contains(ext)) { - if (devExts.contains(ext)) + if (devExts.contains(ext)) { requestedDevExts.append(ext.constData()); - else - qWarning("Device extension %s is not supported", ext.constData()); + } else { + qWarning("Device extension %s requested in QT_VULKAN_DEVICE_EXTENSIONS is not supported", + ext.constData()); + } } } @@ -609,31 +715,50 @@ bool QRhiVulkan::create(QRhi::Flags flags) qCDebug(QRHI_LOG_INFO, " %s", ext); } - VkDeviceCreateInfo devInfo; - memset(&devInfo, 0, sizeof(devInfo)); + VkDeviceCreateInfo devInfo = {}; devInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; devInfo.queueCreateInfoCount = 1; - devInfo.pQueueCreateInfos = queueInfo; - devInfo.enabledLayerCount = uint32_t(devLayers.count()); + devInfo.pQueueCreateInfos = &queueInfo; + devInfo.enabledLayerCount = uint32_t(devLayers.size()); devInfo.ppEnabledLayerNames = devLayers.constData(); - devInfo.enabledExtensionCount = uint32_t(requestedDevExts.count()); + devInfo.enabledExtensionCount = uint32_t(requestedDevExts.size()); devInfo.ppEnabledExtensionNames = requestedDevExts.constData(); - VkPhysicalDeviceFeatures features; - memset(&features, 0, sizeof(features)); - if (physDevFeatures.wideLines) - features.wideLines = VK_TRUE; - if (physDevFeatures.largePoints) - features.largePoints = VK_TRUE; - if (physDevFeatures.tessellationShader) - features.tessellationShader = VK_TRUE; - if (physDevFeatures.textureCompressionETC2) - features.textureCompressionETC2 = VK_TRUE; - if (physDevFeatures.textureCompressionASTC_LDR) - features.textureCompressionASTC_LDR = VK_TRUE; - if (physDevFeatures.textureCompressionBC) - features.textureCompressionBC = VK_TRUE; - devInfo.pEnabledFeatures = &features; + // Enable all features that are reported as supported, except + // robustness because that potentially affects performance. + // + // Enabling all features mainly serves third-party renderers that may + // use the VkDevice created here. For the record, the backend here + // optionally relies on the following features, meaning just for our + // (QRhi/Quick/Quick 3D) purposes it would be sufficient to + // enable-if-supported only the following: + // + // wideLines, largePoints, fillModeNonSolid, + // tessellationShader, geometryShader + // textureCompressionETC2, textureCompressionASTC_LDR, textureCompressionBC + +#ifdef VK_VERSION_1_1 + physDevFeaturesChainable.features.robustBufferAccess = VK_FALSE; +#endif +#ifdef VK_VERSION_1_3 + physDevFeatures13.robustImageAccess = VK_FALSE; +#endif + +#ifdef VK_VERSION_1_1 + if (caps.apiVersion >= QVersionNumber(1, 1)) { + // For a >=1.2 implementation at run time, this will enable all + // (1.0-1.3) features reported as supported, except the ones we turn + // off explicitly above. For a 1.1 implementation at run time, this + // only enables the 1.0 and multiview features reported as + // supported. We will not be bothering with the Vulkan 1.1 + // individual feature struct nonsense. + devInfo.pNext = &physDevFeaturesChainable; + } else +#endif + { + physDevFeatures.robustBufferAccess = VK_FALSE; + devInfo.pEnabledFeatures = &physDevFeatures; + } VkResult err = f->vkCreateDevice(physDev, &devInfo, nullptr, &dev); if (err != VK_SUCCESS) { @@ -642,6 +767,13 @@ bool QRhiVulkan::create(QRhi::Flags flags) } } else { qCDebug(QRHI_LOG_INFO, "Using imported device %p", dev); + + // Here we have no way to tell if the extensions got enabled or not. + // Pretend it's all there and supported. If getProcAddress fails, we'll + // handle that gracefully. + caps.vertexAttribDivisor = true; + caps.renderPass2KHR = true; + caps.depthStencilResolveKHR = true; } vkGetPhysicalDeviceSurfaceCapabilitiesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR>( @@ -650,20 +782,12 @@ bool QRhiVulkan::create(QRhi::Flags flags) inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfaceFormatsKHR")); vkGetPhysicalDeviceSurfacePresentModesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfacePresentModesKHR>( inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfacePresentModesKHR")); - if (!vkGetPhysicalDeviceSurfaceCapabilitiesKHR - || !vkGetPhysicalDeviceSurfaceFormatsKHR - || !vkGetPhysicalDeviceSurfacePresentModesKHR) - { - qWarning("Physical device surface queries not available"); - return false; - } df = inst->deviceFunctions(dev); - VkCommandPoolCreateInfo poolInfo; - memset(&poolInfo, 0, sizeof(poolInfo)); + VkCommandPoolCreateInfo poolInfo = {}; poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - poolInfo.queueFamilyIndex = uint32_t(gfxQueueFamilyIdx); + poolInfo.queueFamilyIndex = gfxQueueFamilyIdx; for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) { VkResult err = df->vkCreateCommandPool(dev, &poolInfo, nullptr, &cmdPool[i]); if (err != VK_SUCCESS) { @@ -672,13 +796,10 @@ bool QRhiVulkan::create(QRhi::Flags flags) } } - if (gfxQueueFamilyIdx < 0) { - // this is when importParams is faulty and did not specify the queue family index - qWarning("No queue family index provided"); - return false; - } + qCDebug(QRHI_LOG_INFO, "Using queue family index %u and queue index %u", + gfxQueueFamilyIdx, gfxQueueIdx); - df->vkGetDeviceQueue(dev, uint32_t(gfxQueueFamilyIdx), gfxQueueIdx, &gfxQueue); + df->vkGetDeviceQueue(dev, gfxQueueFamilyIdx, gfxQueueIdx, &gfxQueue); if (queueFamilyProps.isEmpty()) queryQueueFamilyProps(); @@ -693,37 +814,51 @@ bool QRhiVulkan::create(QRhi::Flags flags) caps.wideLines = physDevFeatures.wideLines; - caps.texture3DSliceAs2D = caps.vulkan11OrHigher; + caps.texture3DSliceAs2D = caps.apiVersion >= QVersionNumber(1, 1); caps.tessellation = physDevFeatures.tessellationShader; + caps.geometryShader = physDevFeatures.geometryShader; + + caps.nonFillPolygonMode = physDevFeatures.fillModeNonSolid; + +#ifdef VK_VERSION_1_2 + if (caps.apiVersion >= QVersionNumber(1, 2)) + caps.multiView = physDevFeatures11IfApi12OrNewer.multiview; +#endif + +#ifdef VK_VERSION_1_1 + if (caps.apiVersion == QVersionNumber(1, 1)) + caps.multiView = multiviewFeaturesIfApi11.multiview; +#endif + + // With Vulkan 1.2 renderpass2 and depth_stencil_resolve are core, but we + // have to support the case of 1.1 + extensions, in particular for the Quest + // 3 (Android, Vulkan 1.1 at the time of writing). Therefore, always rely on + // the KHR extension for now. +#ifdef VK_KHR_create_renderpass2 + if (caps.renderPass2KHR) { + vkCreateRenderPass2KHR = reinterpret_cast<PFN_vkCreateRenderPass2KHR>(f->vkGetDeviceProcAddr(dev, "vkCreateRenderPass2KHR")); + if (!vkCreateRenderPass2KHR) // handle it gracefully, the caps flag may be incorrect when using an imported VkDevice + caps.renderPass2KHR = false; + } +#endif if (!importedAllocator) { - VmaVulkanFunctions afuncs; - afuncs.vkGetPhysicalDeviceProperties = wrap_vkGetPhysicalDeviceProperties; - afuncs.vkGetPhysicalDeviceMemoryProperties = wrap_vkGetPhysicalDeviceMemoryProperties; - afuncs.vkAllocateMemory = wrap_vkAllocateMemory; - afuncs.vkFreeMemory = wrap_vkFreeMemory; - afuncs.vkMapMemory = wrap_vkMapMemory; - afuncs.vkUnmapMemory = wrap_vkUnmapMemory; - afuncs.vkFlushMappedMemoryRanges = wrap_vkFlushMappedMemoryRanges; - afuncs.vkInvalidateMappedMemoryRanges = wrap_vkInvalidateMappedMemoryRanges; - afuncs.vkBindBufferMemory = wrap_vkBindBufferMemory; - afuncs.vkBindImageMemory = wrap_vkBindImageMemory; - afuncs.vkGetBufferMemoryRequirements = wrap_vkGetBufferMemoryRequirements; - afuncs.vkGetImageMemoryRequirements = wrap_vkGetImageMemoryRequirements; - afuncs.vkCreateBuffer = wrap_vkCreateBuffer; - afuncs.vkDestroyBuffer = wrap_vkDestroyBuffer; - afuncs.vkCreateImage = wrap_vkCreateImage; - afuncs.vkDestroyImage = wrap_vkDestroyImage; - - VmaAllocatorCreateInfo allocatorInfo; - memset(&allocatorInfo, 0, sizeof(allocatorInfo)); + VmaVulkanFunctions funcs = {}; + funcs.vkGetInstanceProcAddr = wrap_vkGetInstanceProcAddr; + funcs.vkGetDeviceProcAddr = wrap_vkGetDeviceProcAddr; + + VmaAllocatorCreateInfo allocatorInfo = {}; // A QRhi is supposed to be used from one single thread only. Disable // the allocator's own mutexes. This gives a performance boost. allocatorInfo.flags = VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT; allocatorInfo.physicalDevice = physDev; allocatorInfo.device = dev; - allocatorInfo.pVulkanFunctions = &afuncs; + allocatorInfo.pVulkanFunctions = &funcs; + allocatorInfo.instance = inst->vkInstance(); + allocatorInfo.vulkanApiVersion = VK_MAKE_VERSION(caps.apiVersion.majorVersion(), + caps.apiVersion.minorVersion(), + caps.apiVersion.microVersion()); VmaAllocator vmaallocator; VkResult err = vmaCreateAllocator(&allocatorInfo, &vmaallocator); if (err != VK_SUCCESS) { @@ -742,8 +877,7 @@ bool QRhiVulkan::create(QRhi::Flags flags) else qWarning("Failed to create initial descriptor pool: %d", err); - VkQueryPoolCreateInfo timestampQueryPoolInfo; - memset(×tampQueryPoolInfo, 0, sizeof(timestampQueryPoolInfo)); + VkQueryPoolCreateInfo timestampQueryPoolInfo = {}; timestampQueryPoolInfo.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO; timestampQueryPoolInfo.queryType = VK_QUERY_TYPE_TIMESTAMP; timestampQueryPoolInfo.queryCount = QVK_MAX_ACTIVE_TIMESTAMP_PAIRS * 2; @@ -755,12 +889,14 @@ bool QRhiVulkan::create(QRhi::Flags flags) timestampQueryPoolMap.resize(QVK_MAX_ACTIVE_TIMESTAMP_PAIRS); // 1 bit per pair timestampQueryPoolMap.fill(false); - if (caps.debugMarkers) { - vkCmdDebugMarkerBegin = reinterpret_cast<PFN_vkCmdDebugMarkerBeginEXT>(f->vkGetDeviceProcAddr(dev, "vkCmdDebugMarkerBeginEXT")); - vkCmdDebugMarkerEnd = reinterpret_cast<PFN_vkCmdDebugMarkerEndEXT>(f->vkGetDeviceProcAddr(dev, "vkCmdDebugMarkerEndEXT")); - vkCmdDebugMarkerInsert = reinterpret_cast<PFN_vkCmdDebugMarkerInsertEXT>(f->vkGetDeviceProcAddr(dev, "vkCmdDebugMarkerInsertEXT")); - vkDebugMarkerSetObjectName = reinterpret_cast<PFN_vkDebugMarkerSetObjectNameEXT>(f->vkGetDeviceProcAddr(dev, "vkDebugMarkerSetObjectNameEXT")); +#ifdef VK_EXT_debug_utils + if (caps.debugUtils) { + vkSetDebugUtilsObjectNameEXT = reinterpret_cast<PFN_vkSetDebugUtilsObjectNameEXT>(f->vkGetDeviceProcAddr(dev, "vkSetDebugUtilsObjectNameEXT")); + vkCmdBeginDebugUtilsLabelEXT = reinterpret_cast<PFN_vkCmdBeginDebugUtilsLabelEXT>(f->vkGetDeviceProcAddr(dev, "vkCmdBeginDebugUtilsLabelEXT")); + vkCmdEndDebugUtilsLabelEXT = reinterpret_cast<PFN_vkCmdEndDebugUtilsLabelEXT>(f->vkGetDeviceProcAddr(dev, "vkCmdEndDebugUtilsLabelEXT")); + vkCmdInsertDebugUtilsLabelEXT = reinterpret_cast<PFN_vkCmdInsertDebugUtilsLabelEXT>(f->vkGetDeviceProcAddr(dev, "vkCmdInsertDebugUtilsLabelEXT")); } +#endif deviceLost = false; @@ -770,6 +906,7 @@ bool QRhiVulkan::create(QRhi::Flags flags) nativeHandlesStruct.gfxQueueIdx = gfxQueueIdx; nativeHandlesStruct.gfxQueue = gfxQueue; nativeHandlesStruct.vmemAllocator = allocator; + nativeHandlesStruct.inst = inst; return true; } @@ -838,8 +975,7 @@ VkResult QRhiVulkan::createDescriptorPool(VkDescriptorPool *pool) { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, QVK_STORAGE_BUFFERS_PER_POOL }, { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, QVK_STORAGE_IMAGES_PER_POOL } }; - VkDescriptorPoolCreateInfo descPoolInfo; - memset(&descPoolInfo, 0, sizeof(descPoolInfo)); + VkDescriptorPoolCreateInfo descPoolInfo = {}; descPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; // Do not enable vkFreeDescriptorSets - sets are never freed on their own // (good so no trouble with fragmentation), they just deref their pool @@ -861,7 +997,7 @@ bool QRhiVulkan::allocateDescriptorSet(VkDescriptorSetAllocateInfo *allocInfo, V return r; }; - int lastPoolIdx = descriptorPools.count() - 1; + int lastPoolIdx = descriptorPools.size() - 1; for (int i = lastPoolIdx; i >= 0; --i) { if (descriptorPools[i].refCount == 0) { df->vkResetDescriptorPool(dev, descriptorPools[i].pool, 0); @@ -881,7 +1017,7 @@ bool QRhiVulkan::allocateDescriptorSet(VkDescriptorSetAllocateInfo *allocInfo, V VkResult poolErr = createDescriptorPool(&newPool); if (poolErr == VK_SUCCESS) { descriptorPools.append(newPool); - lastPoolIdx = descriptorPools.count() - 1; + lastPoolIdx = descriptorPools.size() - 1; VkResult err = tryAllocate(lastPoolIdx); if (err != VK_SUCCESS) { qWarning("Failed to allocate descriptor set from new pool too, giving up: %d", err); @@ -989,8 +1125,7 @@ static inline VkFormat toVkTextureFormat(QRhiTexture::Format format, QRhiTexture return srgb ? VK_FORMAT_ASTC_12x12_SRGB_BLOCK : VK_FORMAT_ASTC_12x12_UNORM_BLOCK; default: - Q_UNREACHABLE(); - return VK_FORMAT_R8G8B8A8_UNORM; + Q_UNREACHABLE_RETURN(VK_FORMAT_R8G8B8A8_UNORM); } } @@ -1092,8 +1227,7 @@ bool QRhiVulkan::createTransientImage(VkFormat format, VkResult err; for (int i = 0; i < count; ++i) { - VkImageCreateInfo imgInfo; - memset(&imgInfo, 0, sizeof(imgInfo)); + VkImageCreateInfo imgInfo = {}; imgInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; imgInfo.imageType = VK_IMAGE_TYPE_2D; imgInfo.format = format; @@ -1118,8 +1252,7 @@ bool QRhiVulkan::createTransientImage(VkFormat format, df->vkGetImageMemoryRequirements(dev, images[i], &memReq); } - VkMemoryAllocateInfo memInfo; - memset(&memInfo, 0, sizeof(memInfo)); + VkMemoryAllocateInfo memInfo = {}; memInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; memInfo.allocationSize = aligned(memReq.size, memReq.alignment) * VkDeviceSize(count); @@ -1147,8 +1280,7 @@ bool QRhiVulkan::createTransientImage(VkFormat format, } ofs += aligned(memReq.size, memReq.alignment); - VkImageViewCreateInfo imgViewInfo; - memset(&imgViewInfo, 0, sizeof(imgViewInfo)); + VkImageViewCreateInfo imgViewInfo = {}; imgViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; imgViewInfo.image = images[i]; imgViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; @@ -1202,18 +1334,18 @@ static void fillRenderPassCreateInfo(VkRenderPassCreateInfo *rpInfo, { memset(subpassDesc, 0, sizeof(VkSubpassDescription)); subpassDesc->pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpassDesc->colorAttachmentCount = uint32_t(rpD->colorRefs.count()); + subpassDesc->colorAttachmentCount = uint32_t(rpD->colorRefs.size()); subpassDesc->pColorAttachments = !rpD->colorRefs.isEmpty() ? rpD->colorRefs.constData() : nullptr; subpassDesc->pDepthStencilAttachment = rpD->hasDepthStencil ? &rpD->dsRef : nullptr; subpassDesc->pResolveAttachments = !rpD->resolveRefs.isEmpty() ? rpD->resolveRefs.constData() : nullptr; memset(rpInfo, 0, sizeof(VkRenderPassCreateInfo)); rpInfo->sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - rpInfo->attachmentCount = uint32_t(rpD->attDescs.count()); + rpInfo->attachmentCount = uint32_t(rpD->attDescs.size()); rpInfo->pAttachments = rpD->attDescs.constData(); rpInfo->subpassCount = 1; rpInfo->pSubpasses = subpassDesc; - rpInfo->dependencyCount = uint32_t(rpD->subpassDeps.count()); + rpInfo->dependencyCount = uint32_t(rpD->subpassDeps.size()); rpInfo->pDependencies = !rpD->subpassDeps.isEmpty() ? rpD->subpassDeps.constData() : nullptr; } @@ -1221,8 +1353,7 @@ bool QRhiVulkan::createDefaultRenderPass(QVkRenderPassDescriptor *rpD, bool hasD { // attachment list layout is color (1), ds (0-1), resolve (0-1) - VkAttachmentDescription attDesc; - memset(&attDesc, 0, sizeof(attDesc)); + VkAttachmentDescription attDesc = {}; attDesc.format = colorFormat; attDesc.samples = samples; attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; @@ -1236,6 +1367,8 @@ bool QRhiVulkan::createDefaultRenderPass(QVkRenderPassDescriptor *rpD, bool hasD rpD->colorRefs.append({ 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }); rpD->hasDepthStencil = hasDepthStencil; + rpD->hasDepthStencilResolve = false; + rpD->multiViewCount = 0; if (hasDepthStencil) { // clear on load + no store + lazy alloc + transient image should play @@ -1270,8 +1403,7 @@ bool QRhiVulkan::createDefaultRenderPass(QVkRenderPassDescriptor *rpD, bool hasD } // Replace the first implicit dep (TOP_OF_PIPE / ALL_COMMANDS) with our own. - VkSubpassDependency subpassDep; - memset(&subpassDep, 0, sizeof(subpassDep)); + VkSubpassDependency subpassDep = {}; subpassDep.srcSubpass = VK_SUBPASS_EXTERNAL; subpassDep.dstSubpass = 0; subpassDep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; @@ -1306,29 +1438,190 @@ bool QRhiVulkan::createDefaultRenderPass(QVkRenderPassDescriptor *rpD, bool hasD return true; } +struct MultiViewRenderPassSetupHelper +{ + bool prepare(VkRenderPassCreateInfo *rpInfo, int multiViewCount, bool multiViewCap) + { + if (multiViewCount < 2) + return true; + if (!multiViewCap) { + qWarning("Cannot create multiview render pass without support for the Vulkan 1.1 multiview feature"); + return false; + } +#ifdef VK_VERSION_1_1 + uint32_t allViewsMask = 0; + for (uint32_t i = 0; i < uint32_t(multiViewCount); ++i) + allViewsMask |= (1 << i); + multiViewMask = allViewsMask; + multiViewCorrelationMask = allViewsMask; + multiViewInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO; + multiViewInfo.subpassCount = 1; + multiViewInfo.pViewMasks = &multiViewMask; + multiViewInfo.correlationMaskCount = 1; + multiViewInfo.pCorrelationMasks = &multiViewCorrelationMask; + rpInfo->pNext = &multiViewInfo; +#endif + return true; + } + +#ifdef VK_VERSION_1_1 + VkRenderPassMultiviewCreateInfo multiViewInfo = {}; + uint32_t multiViewMask = 0; + uint32_t multiViewCorrelationMask = 0; +#endif +}; + +#ifdef VK_KHR_create_renderpass2 +// Effectively converts a VkRenderPassCreateInfo into a VkRenderPassCreateInfo2, +// adding depth-stencil resolve support. Assumes a single subpass and no subpass +// dependencies. +struct RenderPass2SetupHelper +{ + bool prepare(VkRenderPassCreateInfo2 *rpInfo2, const VkRenderPassCreateInfo *rpInfo, const QVkRenderPassDescriptor *rpD, int multiViewCount) { + *rpInfo2 = {}; + + viewMask = 0; + if (multiViewCount >= 2) { + for (uint32_t i = 0; i < uint32_t(multiViewCount); ++i) + viewMask |= (1 << i); + } + + attDescs2.resize(rpInfo->attachmentCount); + for (qsizetype i = 0; i < attDescs2.count(); ++i) { + VkAttachmentDescription2KHR &att2(attDescs2[i]); + const VkAttachmentDescription &att(rpInfo->pAttachments[i]); + att2 = {}; + att2.sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2; + att2.flags = att.flags; + att2.format = att.format; + att2.samples = att.samples; + att2.loadOp = att.loadOp; + att2.storeOp = att.storeOp; + att2.stencilLoadOp = att.stencilLoadOp; + att2.stencilStoreOp = att.stencilStoreOp; + att2.initialLayout = att.initialLayout; + att2.finalLayout = att.finalLayout; + } + + attRefs2.clear(); + subpass2 = {}; + subpass2.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2_KHR; + const VkSubpassDescription &subpassDesc(rpInfo->pSubpasses[0]); + subpass2.flags = subpassDesc.flags; + subpass2.pipelineBindPoint = subpassDesc.pipelineBindPoint; + if (multiViewCount >= 2) + subpass2.viewMask = viewMask; + + // color attachment refs + qsizetype startIndex = attRefs2.count(); + for (uint32_t j = 0; j < subpassDesc.colorAttachmentCount; ++j) { + attRefs2.append({}); + VkAttachmentReference2KHR &attref2(attRefs2.last()); + const VkAttachmentReference &attref(subpassDesc.pColorAttachments[j]); + attref2.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR; + attref2.attachment = attref.attachment; + attref2.layout = attref.layout; + attref2.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + } + subpass2.colorAttachmentCount = subpassDesc.colorAttachmentCount; + subpass2.pColorAttachments = attRefs2.constData() + startIndex; + + // color resolve refs + if (subpassDesc.pResolveAttachments) { + startIndex = attRefs2.count(); + for (uint32_t j = 0; j < subpassDesc.colorAttachmentCount; ++j) { + attRefs2.append({}); + VkAttachmentReference2KHR &attref2(attRefs2.last()); + const VkAttachmentReference &attref(subpassDesc.pResolveAttachments[j]); + attref2.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR; + attref2.attachment = attref.attachment; + attref2.layout = attref.layout; + attref2.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + } + subpass2.pResolveAttachments = attRefs2.constData() + startIndex; + } + + // depth-stencil ref + if (subpassDesc.pDepthStencilAttachment) { + startIndex = attRefs2.count(); + attRefs2.append({}); + VkAttachmentReference2KHR &attref2(attRefs2.last()); + const VkAttachmentReference &attref(*subpassDesc.pDepthStencilAttachment); + attref2.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR; + attref2.attachment = attref.attachment; + attref2.layout = attref.layout; + attref2.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + subpass2.pDepthStencilAttachment = attRefs2.constData() + startIndex; + } + + // depth-stencil resolve ref +#ifdef VK_KHR_depth_stencil_resolve + dsResolveDesc = {}; + if (rpD->hasDepthStencilResolve) { + startIndex = attRefs2.count(); + attRefs2.append({}); + VkAttachmentReference2KHR &attref2(attRefs2.last()); + attref2.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR; + attref2.attachment = rpD->dsResolveRef.attachment; + attref2.layout = rpD->dsResolveRef.layout; + attref2.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + dsResolveDesc.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE_KHR; + dsResolveDesc.depthResolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT; + dsResolveDesc.stencilResolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT; + dsResolveDesc.pDepthStencilResolveAttachment = attRefs2.constData() + startIndex; + subpass2.pNext = &dsResolveDesc; + } +#endif + + rpInfo2->sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2_KHR; + rpInfo2->pNext = nullptr; // the 1.1 VkRenderPassMultiviewCreateInfo is part of the '2' structs + rpInfo2->flags = rpInfo->flags; + rpInfo2->attachmentCount = rpInfo->attachmentCount; + rpInfo2->pAttachments = attDescs2.constData(); + rpInfo2->subpassCount = 1; + rpInfo2->pSubpasses = &subpass2; + if (multiViewCount >= 2) { + rpInfo2->correlatedViewMaskCount = 1; + rpInfo2->pCorrelatedViewMasks = &viewMask; + } + return true; + } + + QVarLengthArray<VkAttachmentDescription2KHR, 8> attDescs2; + QVarLengthArray<VkAttachmentReference2KHR, 8> attRefs2; + VkSubpassDescription2KHR subpass2; +#ifdef VK_KHR_depth_stencil_resolve + VkSubpassDescriptionDepthStencilResolveKHR dsResolveDesc; +#endif + uint32_t viewMask; +}; +#endif // VK_KHR_create_renderpass2 + bool QRhiVulkan::createOffscreenRenderPass(QVkRenderPassDescriptor *rpD, - const QRhiColorAttachment *firstColorAttachment, - const QRhiColorAttachment *lastColorAttachment, + const QRhiColorAttachment *colorAttachmentsBegin, + const QRhiColorAttachment *colorAttachmentsEnd, bool preserveColor, bool preserveDs, + bool storeDs, QRhiRenderBuffer *depthStencilBuffer, - QRhiTexture *depthTexture) + QRhiTexture *depthTexture, + QRhiTexture *depthResolveTexture) { - // attachment list layout is color (0-8), ds (0-1), resolve (0-8) + // attachment list layout is color (0-8), ds (0-1), resolve (0-8), ds resolve (0-1) - for (auto it = firstColorAttachment; it != lastColorAttachment; ++it) { + int multiViewCount = 0; + for (auto it = colorAttachmentsBegin; it != colorAttachmentsEnd; ++it) { QVkTexture *texD = QRHI_RES(QVkTexture, it->texture()); QVkRenderBuffer *rbD = QRHI_RES(QVkRenderBuffer, it->renderBuffer()); Q_ASSERT(texD || rbD); - const VkFormat vkformat = texD ? texD->vkformat : rbD->vkformat; + const VkFormat vkformat = texD ? texD->viewFormat : rbD->vkformat; const VkSampleCountFlagBits samples = texD ? texD->samples : rbD->samples; - VkAttachmentDescription attDesc; - memset(&attDesc, 0, sizeof(attDesc)); + VkAttachmentDescription attDesc = {}; attDesc.format = vkformat; attDesc.samples = samples; attDesc.loadOp = preserveColor ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_CLEAR; - attDesc.storeOp = it->resolveTexture() ? VK_ATTACHMENT_STORE_OP_DONT_CARE : VK_ATTACHMENT_STORE_OP_STORE; + attDesc.storeOp = (it->resolveTexture() && !preserveColor) ? VK_ATTACHMENT_STORE_OP_DONT_CARE : VK_ATTACHMENT_STORE_OP_STORE; attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; // this has to interact correctly with activateTextureRenderTarget(), hence leaving in COLOR_ATT @@ -1336,33 +1629,47 @@ bool QRhiVulkan::createOffscreenRenderPass(QVkRenderPassDescriptor *rpD, attDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; rpD->attDescs.append(attDesc); - const VkAttachmentReference ref = { uint32_t(rpD->attDescs.count() - 1), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }; + const VkAttachmentReference ref = { uint32_t(rpD->attDescs.size() - 1), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }; rpD->colorRefs.append(ref); + + if (it->multiViewCount() >= 2) { + if (multiViewCount > 0 && multiViewCount != it->multiViewCount()) + qWarning("Inconsistent multiViewCount in color attachment set"); + else + multiViewCount = it->multiViewCount(); + } else if (multiViewCount > 0) { + qWarning("Mixing non-multiview color attachments within a multiview render pass"); + } } + Q_ASSERT(multiViewCount == 0 || multiViewCount >= 2); + rpD->multiViewCount = uint32_t(multiViewCount); rpD->hasDepthStencil = depthStencilBuffer || depthTexture; if (rpD->hasDepthStencil) { - const VkFormat dsFormat = depthTexture ? QRHI_RES(QVkTexture, depthTexture)->vkformat + const VkFormat dsFormat = depthTexture ? QRHI_RES(QVkTexture, depthTexture)->viewFormat : QRHI_RES(QVkRenderBuffer, depthStencilBuffer)->vkformat; const VkSampleCountFlagBits samples = depthTexture ? QRHI_RES(QVkTexture, depthTexture)->samples : QRHI_RES(QVkRenderBuffer, depthStencilBuffer)->samples; const VkAttachmentLoadOp loadOp = preserveDs ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_CLEAR; - const VkAttachmentStoreOp storeOp = depthTexture ? VK_ATTACHMENT_STORE_OP_STORE : VK_ATTACHMENT_STORE_OP_DONT_CARE; - VkAttachmentDescription attDesc; - memset(&attDesc, 0, sizeof(attDesc)); + const VkAttachmentStoreOp storeOp = storeDs ? VK_ATTACHMENT_STORE_OP_STORE : VK_ATTACHMENT_STORE_OP_DONT_CARE; + VkAttachmentDescription attDesc = {}; attDesc.format = dsFormat; attDesc.samples = samples; attDesc.loadOp = loadOp; attDesc.storeOp = storeOp; attDesc.stencilLoadOp = loadOp; attDesc.stencilStoreOp = storeOp; - attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attDesc.initialLayout = preserveDs ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_UNDEFINED; attDesc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; rpD->attDescs.append(attDesc); + if (depthTexture && depthTexture->arraySize() >= 2 && colorAttachmentsBegin == colorAttachmentsEnd) { + multiViewCount = depthTexture->arraySize(); + rpD->multiViewCount = multiViewCount; + } } - rpD->dsRef = { uint32_t(rpD->attDescs.count() - 1), VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL }; + rpD->dsRef = { uint32_t(rpD->attDescs.size() - 1), VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL }; - for (auto it = firstColorAttachment; it != lastColorAttachment; ++it) { + for (auto it = colorAttachmentsBegin; it != colorAttachmentsEnd; ++it) { if (it->resolveTexture()) { QVkTexture *rtexD = QRHI_RES(QVkTexture, it->resolveTexture()); const VkFormat dstFormat = rtexD->vkformat; @@ -1380,9 +1687,8 @@ bool QRhiVulkan::createOffscreenRenderPass(QVkRenderPassDescriptor *rpD, int(srcFormat), int(dstFormat)); } - VkAttachmentDescription attDesc; - memset(&attDesc, 0, sizeof(attDesc)); - attDesc.format = dstFormat; + VkAttachmentDescription attDesc = {}; + attDesc.format = rtexD->viewFormat; attDesc.samples = VK_SAMPLE_COUNT_1_BIT; attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; // ignored attDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE; @@ -1392,14 +1698,39 @@ bool QRhiVulkan::createOffscreenRenderPass(QVkRenderPassDescriptor *rpD, attDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; rpD->attDescs.append(attDesc); - const VkAttachmentReference ref = { uint32_t(rpD->attDescs.count() - 1), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }; + const VkAttachmentReference ref = { uint32_t(rpD->attDescs.size() - 1), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }; rpD->resolveRefs.append(ref); } else { const VkAttachmentReference ref = { VK_ATTACHMENT_UNUSED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }; rpD->resolveRefs.append(ref); } } - Q_ASSERT(rpD->colorRefs.count() == rpD->resolveRefs.count()); + Q_ASSERT(rpD->colorRefs.size() == rpD->resolveRefs.size()); + + rpD->hasDepthStencilResolve = rpD->hasDepthStencil && depthResolveTexture; + if (rpD->hasDepthStencilResolve) { + QVkTexture *rtexD = QRHI_RES(QVkTexture, depthResolveTexture); + if (rtexD->samples > VK_SAMPLE_COUNT_1_BIT) + qWarning("Resolving into a multisample depth texture is not supported"); + + QVkTexture *texD = QRHI_RES(QVkTexture, depthResolveTexture); + if (texD->vkformat != rtexD->vkformat) { + qWarning("Multisample resolve between different depth-stencil formats (%d and %d) is not supported.", + int(texD->vkformat), int(rtexD->vkformat)); + } + + VkAttachmentDescription attDesc = {}; + attDesc.format = rtexD->viewFormat; + attDesc.samples = VK_SAMPLE_COUNT_1_BIT; + attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; // ignored + attDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attDesc.stencilLoadOp = attDesc.loadOp; + attDesc.stencilStoreOp = attDesc.storeOp; + attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attDesc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + rpD->attDescs.append(attDesc); + } + rpD->dsResolveRef = { uint32_t(rpD->attDescs.size() - 1), VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL }; // rpD->subpassDeps stays empty: don't yet know the correct initial/final // access and stage stuff for the implicit deps at this point, so leave it @@ -1410,10 +1741,35 @@ bool QRhiVulkan::createOffscreenRenderPass(QVkRenderPassDescriptor *rpD, VkSubpassDescription subpassDesc; fillRenderPassCreateInfo(&rpInfo, &subpassDesc, rpD); - VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, &rpD->rp); - if (err != VK_SUCCESS) { - qWarning("Failed to create renderpass: %d", err); + MultiViewRenderPassSetupHelper multiViewHelper; + if (!multiViewHelper.prepare(&rpInfo, multiViewCount, caps.multiView)) return false; + +#ifdef VK_KHR_create_renderpass2 + if (rpD->hasDepthStencilResolve && caps.renderPass2KHR) { + // Use the KHR extension, not the 1.2 core API, in order to support Vulkan 1.1. + VkRenderPassCreateInfo2KHR rpInfo2; + RenderPass2SetupHelper rp2Helper; + if (!rp2Helper.prepare(&rpInfo2, &rpInfo, rpD, multiViewCount)) + return false; + + VkResult err = vkCreateRenderPass2KHR(dev, &rpInfo2, nullptr, &rpD->rp); + if (err != VK_SUCCESS) { + qWarning("Failed to create renderpass (using VkRenderPassCreateInfo2KHR): %d", err); + return false; + } + } else +#endif + { + if (rpD->hasDepthStencilResolve) { + qWarning("Resolving multisample depth-stencil buffers is not supported without " + "VK_KHR_depth_stencil_resolve and VK_KHR_create_renderpass2"); + } + VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, &rpD->rp); + if (err != VK_SUCCESS) { + qWarning("Failed to create renderpass: %d", err); + return false; + } } return true; @@ -1454,21 +1810,45 @@ bool QRhiVulkan::recreateSwapChain(QRhiSwapChain *swapChain) ? VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR : surfaceCaps.currentTransform; + // This looks odd but matches how platforms work in practice. + // + // On Windows with NVIDIA for example, the only supportedCompositeAlpha + // value reported is OPAQUE, nothing else. Yet transparency works + // regardless, as long as the native window is set up correctly, so that's + // not something we need to handle here. + // + // On Linux with Intel and Mesa and running on xcb reports, on one + // particular system, INHERIT+PRE_MULTIPLIED. Tranparency works, regardless, + // presumably due to setting INHERIT. + // + // On the same setup with Wayland instead of xcb we see + // OPAQUE+PRE_MULTIPLIED reported. Here transparency won't work unless + // PRE_MULTIPLIED is set. + // + // Therefore our rules are: + // - Prefer INHERIT over OPAQUE. + // - Then based on the request, try the requested alpha mode, but if + // that's not reported as supported, try also the other (PRE/POST, + // POST/PRE) as that is better than nothing. This is not different from + // some other backends, e.g. D3D11 with DirectComposition there is also + // no control over being straight or pre-multiplied. Whereas with + // WGL/GLX/EGL we never had that sort of control. + VkCompositeAlphaFlagBitsKHR compositeAlpha = (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR) ? VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR : VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; - if (swapChainD->m_flags.testFlag(QRhiSwapChain::SurfaceHasPreMulAlpha) - && (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR)) - { - compositeAlpha = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR; - } - - if (swapChainD->m_flags.testFlag(QRhiSwapChain::SurfaceHasNonPreMulAlpha) - && (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR)) - { - compositeAlpha = VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR; + if (swapChainD->m_flags.testFlag(QRhiSwapChain::SurfaceHasPreMulAlpha)) { + if (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR) + compositeAlpha = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR; + else if (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR) + compositeAlpha = VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR; + } else if (swapChainD->m_flags.testFlag(QRhiSwapChain::SurfaceHasNonPreMulAlpha)) { + if (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR) + compositeAlpha = VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR; + else if (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR) + compositeAlpha = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR; } VkImageUsageFlags usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; @@ -1476,9 +1856,16 @@ bool QRhiVulkan::recreateSwapChain(QRhiSwapChain *swapChain) if (swapChainD->supportsReadback && swapChainD->m_flags.testFlag(QRhiSwapChain::UsedAsTransferSource)) usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + const bool stereo = bool(swapChainD->m_window) && (swapChainD->m_window->format().stereo()) + && surfaceCaps.maxImageArrayLayers > 1; + swapChainD->stereo = stereo; + VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR; if (swapChainD->m_flags.testFlag(QRhiSwapChain::NoVSync)) { - if (swapChainD->supportedPresentationModes.contains(VK_PRESENT_MODE_MAILBOX_KHR)) + // Stereo has a weird bug, when using VK_PRESENT_MODE_MAILBOX_KHR, + // black screen is shown, but there is no validation error. + // Detected on Windows, with NVidia RTX A series (at least 4000 and 6000) driver 535.98 + if (swapChainD->supportedPresentationModes.contains(VK_PRESENT_MODE_MAILBOX_KHR) && !stereo) presentMode = VK_PRESENT_MODE_MAILBOX_KHR; else if (swapChainD->supportedPresentationModes.contains(VK_PRESENT_MODE_IMMEDIATE_KHR)) presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; @@ -1495,15 +1882,14 @@ bool QRhiVulkan::recreateSwapChain(QRhiSwapChain *swapChain) reuseExisting ? "recycled" : "new", reqBufferCount, swapChainD->pixelSize.width(), swapChainD->pixelSize.height(), presentMode); - VkSwapchainCreateInfoKHR swapChainInfo; - memset(&swapChainInfo, 0, sizeof(swapChainInfo)); + VkSwapchainCreateInfoKHR swapChainInfo = {}; swapChainInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; swapChainInfo.surface = swapChainD->surface; swapChainInfo.minImageCount = reqBufferCount; swapChainInfo.imageFormat = swapChainD->colorFormat; swapChainInfo.imageColorSpace = swapChainD->colorSpace; swapChainInfo.imageExtent = VkExtent2D { uint32_t(swapChainD->pixelSize.width()), uint32_t(swapChainD->pixelSize.height()) }; - swapChainInfo.imageArrayLayers = 1; + swapChainInfo.imageArrayLayers = stereo ? 2u : 1u; swapChainInfo.imageUsage = usage; swapChainInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; swapChainInfo.preTransform = preTransform; @@ -1561,12 +1947,13 @@ bool QRhiVulkan::recreateSwapChain(QRhiSwapChain *swapChain) } } - VkFenceCreateInfo fenceInfo; - memset(&fenceInfo, 0, sizeof(fenceInfo)); + VkFenceCreateInfo fenceInfo = {}; fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; - swapChainD->imageRes.resize(swapChainD->bufferCount); + // Double up for stereo + swapChainD->imageRes.resize(swapChainD->bufferCount * (stereo ? 2u : 1u)); + for (int i = 0; i < swapChainD->bufferCount; ++i) { QVkSwapChain::ImageResources &image(swapChainD->imageRes[i]); image.image = swapChainImages[i]; @@ -1575,8 +1962,7 @@ bool QRhiVulkan::recreateSwapChain(QRhiSwapChain *swapChain) image.msaaImageView = msaaViews[i]; } - VkImageViewCreateInfo imgViewInfo; - memset(&imgViewInfo, 0, sizeof(imgViewInfo)); + VkImageViewCreateInfo imgViewInfo = {}; imgViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; imgViewInfo.image = swapChainImages[i]; imgViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; @@ -1595,11 +1981,40 @@ bool QRhiVulkan::recreateSwapChain(QRhiSwapChain *swapChain) image.lastUse = QVkSwapChain::ImageResources::ScImageUseNone; } + if (stereo) { + for (int i = 0; i < swapChainD->bufferCount; ++i) { + QVkSwapChain::ImageResources &image(swapChainD->imageRes[i + swapChainD->bufferCount]); + image.image = swapChainImages[i]; + if (swapChainD->samples > VK_SAMPLE_COUNT_1_BIT) { + image.msaaImage = msaaImages[i]; + image.msaaImageView = msaaViews[i]; + } + + VkImageViewCreateInfo imgViewInfo = {}; + imgViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + imgViewInfo.image = swapChainImages[i]; + imgViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + imgViewInfo.format = swapChainD->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.baseArrayLayer = 1; + imgViewInfo.subresourceRange.levelCount = imgViewInfo.subresourceRange.layerCount = 1; + err = df->vkCreateImageView(dev, &imgViewInfo, nullptr, &image.imageView); + if (err != VK_SUCCESS) { + qWarning("Failed to create swapchain image view %d: %d", i, err); + return false; + } + + image.lastUse = QVkSwapChain::ImageResources::ScImageUseNone; + } + } swapChainD->currentImageIndex = 0; - VkSemaphoreCreateInfo semInfo; - memset(&semInfo, 0, sizeof(semInfo)); + VkSemaphoreCreateInfo semInfo = {}; semInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) { @@ -1663,7 +2078,7 @@ void QRhiVulkan::releaseSwapChainResources(QRhiSwapChain *swapChain) } } - for (int i = 0; i < swapChainD->bufferCount; ++i) { + for (int i = 0; i < swapChainD->bufferCount * (swapChainD->stereo ? 2 : 1); ++i) { QVkSwapChain::ImageResources &image(swapChainD->imageRes[i]); if (image.fb) { df->vkDestroyFramebuffer(dev, image.fb, nullptr); @@ -1708,12 +2123,32 @@ void QRhiVulkan::ensureCommandPoolForNewFrame() df->vkResetCommandPool(dev, cmdPool[currentFrameSlot], flags); } +double QRhiVulkan::elapsedSecondsFromTimestamp(quint64 timestamp[2], bool *ok) +{ + quint64 mask = 0; + for (quint64 i = 0; i < timestampValidBits; i += 8) + mask |= 0xFFULL << i; + const quint64 ts0 = timestamp[0] & mask; + const quint64 ts1 = timestamp[1] & mask; + const float nsecsPerTick = physDevProperties.limits.timestampPeriod; + if (!qFuzzyIsNull(nsecsPerTick)) { + const float elapsedMs = float(ts1 - ts0) * nsecsPerTick / 1000000.0f; + const double elapsedSec = elapsedMs / 1000.0; + *ok = true; + return elapsedSec; + } + *ok = false; + return 0; +} + QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags) { QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain); const int frameResIndex = swapChainD->bufferCount > 1 ? swapChainD->currentFrameSlot : 0; QVkSwapChain::FrameResources &frame(swapChainD->frameRes[frameResIndex]); + inst->handle()->beginFrame(swapChainD->window); + if (!frame.imageAcquired) { // 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) @@ -1757,30 +2192,6 @@ QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::Begin // mess up A's in-flight commands (as they are not in flight anymore). waitCommandCompletion(frameResIndex); - // Now is the time to read the timestamps for the previous frame for this slot. - if (frame.timestampQueryIndex >= 0) { - quint64 timestamp[2] = { 0, 0 }; - VkResult err = df->vkGetQueryPoolResults(dev, timestampQueryPool, uint32_t(frame.timestampQueryIndex), 2, - 2 * sizeof(quint64), timestamp, sizeof(quint64), - VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT); - timestampQueryPoolMap.clearBit(frame.timestampQueryIndex / 2); - frame.timestampQueryIndex = -1; - if (err == VK_SUCCESS) { - quint64 mask = 0; - for (quint64 i = 0; i < timestampValidBits; i += 8) - mask |= 0xFFULL << i; - const quint64 ts0 = timestamp[0] & mask; - const quint64 ts1 = timestamp[1] & mask; - const float nsecsPerTick = physDevProperties.limits.timestampPeriod; - if (!qFuzzyIsNull(nsecsPerTick)) { - const float elapsedMs = float(ts1 - ts0) * nsecsPerTick / 1000000.0f; - runGpuFrameTimeCallbacks(elapsedMs); - } - } else { - qWarning("Failed to query timestamp: %d", err); - } - } - currentFrameSlot = int(swapChainD->currentFrameSlot); currentSwapChain = swapChainD; if (swapChainD->ds) @@ -1794,31 +2205,55 @@ QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::Begin if (cbres != QRhi::FrameOpSuccess) return cbres; - // when profiling is enabled, pick a free query (pair) from the pool - int timestampQueryIdx = -1; - if (hasGpuFrameTimeCallback() && swapChainD->bufferCount > 1) { // no timestamps if not having at least 2 frames in flight - for (int i = 0; i < timestampQueryPoolMap.count(); ++i) { + swapChainD->cbWrapper.cb = frame.cmdBuf; + + QVkSwapChain::ImageResources &image(swapChainD->imageRes[swapChainD->currentImageIndex]); + swapChainD->rtWrapper.d.fb = image.fb; + + if (swapChainD->stereo) { + QVkSwapChain::ImageResources &image( + swapChainD->imageRes[swapChainD->currentImageIndex + swapChainD->bufferCount]); + swapChainD->rtWrapperRight.d.fb = image.fb; + } + + prepareNewFrame(&swapChainD->cbWrapper); + + // Read the timestamps for the previous frame for this slot. + if (frame.timestampQueryIndex >= 0) { + quint64 timestamp[2] = { 0, 0 }; + VkResult err = df->vkGetQueryPoolResults(dev, timestampQueryPool, uint32_t(frame.timestampQueryIndex), 2, + 2 * sizeof(quint64), timestamp, sizeof(quint64), + VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT); + timestampQueryPoolMap.clearBit(frame.timestampQueryIndex / 2); + frame.timestampQueryIndex = -1; + if (err == VK_SUCCESS) { + bool ok = false; + const double elapsedSec = elapsedSecondsFromTimestamp(timestamp, &ok); + if (ok) + swapChainD->cbWrapper.lastGpuTime = elapsedSec; + } else { + qWarning("Failed to query timestamp: %d", err); + } + } + + // No timestamps if the client did not opt in, or when not having at least 2 frames in flight. + if (rhiFlags.testFlag(QRhi::EnableTimestamps) && swapChainD->bufferCount > 1) { + int timestampQueryIdx = -1; + for (int i = 0; i < timestampQueryPoolMap.size(); ++i) { if (!timestampQueryPoolMap.testBit(i)) { timestampQueryPoolMap.setBit(i); timestampQueryIdx = i * 2; break; } } + if (timestampQueryIdx >= 0) { + df->vkCmdResetQueryPool(frame.cmdBuf, timestampQueryPool, uint32_t(timestampQueryIdx), 2); + // record timestamp at the start of the command buffer + df->vkCmdWriteTimestamp(frame.cmdBuf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + timestampQueryPool, uint32_t(timestampQueryIdx)); + frame.timestampQueryIndex = timestampQueryIdx; + } } - if (timestampQueryIdx >= 0) { - df->vkCmdResetQueryPool(frame.cmdBuf, timestampQueryPool, uint32_t(timestampQueryIdx), 2); - // record timestamp at the start of the command buffer - df->vkCmdWriteTimestamp(frame.cmdBuf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - timestampQueryPool, uint32_t(timestampQueryIdx)); - frame.timestampQueryIndex = timestampQueryIdx; - } - - swapChainD->cbWrapper.cb = frame.cmdBuf; - - QVkSwapChain::ImageResources &image(swapChainD->imageRes[swapChainD->currentImageIndex]); - swapChainD->rtWrapper.d.fb = image.fb; - - prepareNewFrame(&swapChainD->cbWrapper); return QRhi::FrameOpSuccess; } @@ -1828,6 +2263,10 @@ QRhi::FrameOpResult QRhiVulkan::endFrame(QRhiSwapChain *swapChain, QRhi::EndFram QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain); Q_ASSERT(currentSwapChain == swapChainD); + auto cleanup = qScopeGuard([this, swapChainD] { + inst->handle()->endFrame(swapChainD->window); + }); + recordPrimaryCommandBuffer(&swapChainD->cbWrapper); int frameResIndex = swapChainD->bufferCount > 1 ? swapChainD->currentFrameSlot : 0; @@ -1835,8 +2274,7 @@ QRhi::FrameOpResult QRhiVulkan::endFrame(QRhiSwapChain *swapChain, QRhi::EndFram QVkSwapChain::ImageResources &image(swapChainD->imageRes[swapChainD->currentImageIndex]); if (image.lastUse != QVkSwapChain::ImageResources::ScImageUseRender) { - VkImageMemoryBarrier presTrans; - memset(&presTrans, 0, sizeof(presTrans)); + VkImageMemoryBarrier presTrans = {}; presTrans.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; presTrans.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; presTrans.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; @@ -1885,8 +2323,7 @@ QRhi::FrameOpResult QRhiVulkan::endFrame(QRhiSwapChain *swapChain, QRhi::EndFram if (needsPresent) { // add the Present to the queue - VkPresentInfoKHR presInfo; - memset(&presInfo, 0, sizeof(presInfo)); + VkPresentInfoKHR presInfo = {}; presInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; presInfo.swapchainCount = 1; presInfo.pSwapchains = &swapChainD->sc; @@ -1954,8 +2391,7 @@ void QRhiVulkan::prepareNewFrame(QRhiCommandBuffer *cb) QRhi::FrameOpResult QRhiVulkan::startPrimaryCommandBuffer(VkCommandBuffer *cb) { if (!*cb) { - VkCommandBufferAllocateInfo cmdBufInfo; - memset(&cmdBufInfo, 0, sizeof(cmdBufInfo)); + VkCommandBufferAllocateInfo cmdBufInfo = {}; cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; cmdBufInfo.commandPool = cmdPool[currentFrameSlot]; cmdBufInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; @@ -1973,8 +2409,7 @@ QRhi::FrameOpResult QRhiVulkan::startPrimaryCommandBuffer(VkCommandBuffer *cb) } } - VkCommandBufferBeginInfo cmdBufBeginInfo; - memset(&cmdBufBeginInfo, 0, sizeof(cmdBufBeginInfo)); + VkCommandBufferBeginInfo cmdBufBeginInfo = {}; cmdBufBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; VkResult err = df->vkBeginCommandBuffer(*cb, &cmdBufBeginInfo); @@ -2005,8 +2440,7 @@ QRhi::FrameOpResult QRhiVulkan::endAndSubmitPrimaryCommandBuffer(VkCommandBuffer return QRhi::FrameOpError; } - VkSubmitInfo submitInfo; - memset(&submitInfo, 0, sizeof(submitInfo)); + VkSubmitInfo submitInfo = {}; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &cb; @@ -2037,7 +2471,7 @@ QRhi::FrameOpResult QRhiVulkan::endAndSubmitPrimaryCommandBuffer(VkCommandBuffer void QRhiVulkan::waitCommandCompletion(int frameSlot) { - for (QVkSwapChain *sc : qAsConst(swapchains)) { + for (QVkSwapChain *sc : std::as_const(swapchains)) { const int frameResIndex = sc->bufferCount > 1 ? frameSlot : 0; QVkSwapChain::FrameResources &frame(sc->frameRes[frameResIndex]); if (frame.cmdFenceWaitable) { @@ -2073,6 +2507,24 @@ QRhi::FrameOpResult QRhiVulkan::beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi prepareNewFrame(cbWrapper); ofr.active = true; + if (rhiFlags.testFlag(QRhi::EnableTimestamps)) { + int timestampQueryIdx = -1; + for (int i = 0; i < timestampQueryPoolMap.size(); ++i) { + if (!timestampQueryPoolMap.testBit(i)) { + timestampQueryPoolMap.setBit(i); + timestampQueryIdx = i * 2; + break; + } + } + if (timestampQueryIdx >= 0) { + df->vkCmdResetQueryPool(cbWrapper->cb, timestampQueryPool, uint32_t(timestampQueryIdx), 2); + // record timestamp at the start of the command buffer + df->vkCmdWriteTimestamp(cbWrapper->cb, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + timestampQueryPool, uint32_t(timestampQueryIdx)); + ofr.timestampQueryIndex = timestampQueryIdx; + } + } + *cb = cbWrapper; return QRhi::FrameOpSuccess; } @@ -2086,9 +2538,14 @@ QRhi::FrameOpResult QRhiVulkan::endOffscreenFrame(QRhi::EndFrameFlags flags) QVkCommandBuffer *cbWrapper(ofr.cbWrapper[currentFrameSlot]); recordPrimaryCommandBuffer(cbWrapper); + // record another timestamp, when enabled + if (ofr.timestampQueryIndex >= 0) { + df->vkCmdWriteTimestamp(cbWrapper->cb, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, + timestampQueryPool, uint32_t(ofr.timestampQueryIndex + 1)); + } + if (!ofr.cmdFence) { - VkFenceCreateInfo fenceInfo; - memset(&fenceInfo, 0, sizeof(fenceInfo)); + VkFenceCreateInfo fenceInfo = {}; fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; VkResult err = df->vkCreateFence(dev, &fenceInfo, nullptr, &ofr.cmdFence); if (err != VK_SUCCESS) { @@ -2109,6 +2566,24 @@ QRhi::FrameOpResult QRhiVulkan::endOffscreenFrame(QRhi::EndFrameFlags flags) // previous) frame is safe since we waited for completion above. finishActiveReadbacks(true); + // Read the timestamps, if we wrote them. + if (ofr.timestampQueryIndex >= 0) { + quint64 timestamp[2] = { 0, 0 }; + VkResult err = df->vkGetQueryPoolResults(dev, timestampQueryPool, uint32_t(ofr.timestampQueryIndex), 2, + 2 * sizeof(quint64), timestamp, sizeof(quint64), + VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT); + timestampQueryPoolMap.clearBit(ofr.timestampQueryIndex / 2); + ofr.timestampQueryIndex = -1; + if (err == VK_SUCCESS) { + bool ok = false; + const double elapsedSec = elapsedSecondsFromTimestamp(timestamp, &ok); + if (ok) + cbWrapper->lastGpuTime = elapsedSec; + } else { + qWarning("Failed to query timestamp: %d", err); + } + } + return QRhi::FrameOpSuccess; } @@ -2226,6 +2701,13 @@ void QRhiVulkan::activateTextureRenderTarget(QVkCommandBuffer *cbD, QVkTextureRe QRhiPassResourceTracker::TexDepthOutputStage); depthTexD->lastActiveFrameSlot = currentFrameSlot; } + if (rtD->m_desc.depthResolveTexture()) { + QVkTexture *depthResolveTexD = QRHI_RES(QVkTexture, rtD->m_desc.depthResolveTexture()); + trackedRegisterTexture(&passResTracker, depthResolveTexD, + QRhiPassResourceTracker::TexDepthOutput, + QRhiPassResourceTracker::TexDepthOutputStage); + depthResolveTexD->lastActiveFrameSlot = currentFrameSlot; + } } void QRhiVulkan::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) @@ -2244,8 +2726,7 @@ VkCommandBuffer QRhiVulkan::startSecondaryCommandBuffer(QVkRenderTargetData *rtD secondaryCb = freeSecondaryCbs[currentFrameSlot].last(); freeSecondaryCbs[currentFrameSlot].removeLast(); } else { - VkCommandBufferAllocateInfo cmdBufInfo; - memset(&cmdBufInfo, 0, sizeof(cmdBufInfo)); + VkCommandBufferAllocateInfo cmdBufInfo = {}; cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; cmdBufInfo.commandPool = cmdPool[currentFrameSlot]; cmdBufInfo.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY; @@ -2258,12 +2739,10 @@ VkCommandBuffer QRhiVulkan::startSecondaryCommandBuffer(QVkRenderTargetData *rtD } } - VkCommandBufferBeginInfo cmdBufBeginInfo; - memset(&cmdBufBeginInfo, 0, sizeof(cmdBufBeginInfo)); + VkCommandBufferBeginInfo cmdBufBeginInfo = {}; cmdBufBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; cmdBufBeginInfo.flags = rtD ? VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT : 0; - VkCommandBufferInheritanceInfo cmdBufInheritInfo; - memset(&cmdBufInheritInfo, 0, sizeof(cmdBufInheritInfo)); + VkCommandBufferInheritanceInfo cmdBufInheritInfo = {}; cmdBufInheritInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO; cmdBufInheritInfo.subpass = 0; if (rtD) { @@ -2319,8 +2798,8 @@ void QRhiVulkan::beginPass(QRhiCommandBuffer *cb, QVkRenderTargetData *rtD = nullptr; switch (rt->resourceType()) { - case QRhiResource::RenderTarget: - rtD = &QRHI_RES(QVkReferenceRenderTarget, rt)->d; + case QRhiResource::SwapChainRenderTarget: + rtD = &QRHI_RES(QVkSwapChainRenderTarget, rt)->d; rtD->rp->lastActiveFrameSlot = currentFrameSlot; Q_ASSERT(currentSwapChain); currentSwapChain->imageRes[currentSwapChain->currentImageIndex].lastUse = @@ -2345,8 +2824,7 @@ void QRhiVulkan::beginPass(QRhiCommandBuffer *cb, // No copy operations or image layout transitions allowed after this point // (up until endPass) as we are going to begin the renderpass. - VkRenderPassBeginInfo rpBeginInfo; - memset(&rpBeginInfo, 0, sizeof(rpBeginInfo)); + VkRenderPassBeginInfo rpBeginInfo = {}; rpBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; rpBeginInfo.renderPass = rtD->rp->rp; rpBeginInfo.framebuffer = rtD->fb; @@ -2371,14 +2849,19 @@ void QRhiVulkan::beginPass(QRhiCommandBuffer *cb, float(colorClearValue.alphaF()) } }; cvs.append(cv); } - rpBeginInfo.clearValueCount = uint32_t(cvs.count()); + for (int i = 0; i < rtD->dsResolveAttCount; ++i) { + VkClearValue cv; + cv.depthStencil = { depthStencilClearValue.depthClearValue(), depthStencilClearValue.stencilClearValue() }; + cvs.append(cv); + } + rpBeginInfo.clearValueCount = uint32_t(cvs.size()); QVkCommandBuffer::Command &cmd(cbD->commands.get()); cmd.cmd = QVkCommandBuffer::Command::BeginRenderPass; cmd.args.beginRenderPass.desc = rpBeginInfo; - cmd.args.beginRenderPass.clearValueIndex = cbD->pools.clearValue.count(); + cmd.args.beginRenderPass.clearValueIndex = cbD->pools.clearValue.size(); cmd.args.beginRenderPass.useSecondaryCb = cbD->passUsesSecondaryCb; - cbD->pools.clearValue.append(cvs.constData(), cvs.count()); + cbD->pools.clearValue.append(cvs.constData(), cvs.size()); if (cbD->passUsesSecondaryCb) cbD->activeSecondaryCbStack.append(startSecondaryCommandBuffer(rtD)); @@ -2509,9 +2992,9 @@ void QRhiVulkan::dispatch(QRhiCommandBuffer *cb, int x, int y, int z) accessAndIsNewFlag = { 0, false }; QVkShaderResourceBindings *srbD = QRHI_RES(QVkShaderResourceBindings, cbD->currentComputeSrb); - const int bindingCount = srbD->m_bindings.count(); + const int bindingCount = srbD->m_bindings.size(); for (int i = 0; i < bindingCount; ++i) { - const QRhiShaderResourceBinding::Data *b = srbD->m_bindings.at(i).data(); + const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->m_bindings.at(i)); switch (b->type) { case QRhiShaderResourceBinding::ImageLoad: case QRhiShaderResourceBinding::ImageStore: @@ -2544,8 +3027,7 @@ void QRhiVulkan::dispatch(QRhiCommandBuffer *cb, int x, int y, int z) if (accessInThisDispatch && !isNewInThisDispatch) { if (it.key()->resourceType() == QRhiResource::Texture) { QVkTexture *texD = QRHI_RES(QVkTexture, it.key()); - VkImageMemoryBarrier barrier; - memset(&barrier, 0, sizeof(barrier)); + VkImageMemoryBarrier barrier = {}; barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; // won't care about subresources, pretend the whole resource was written @@ -2561,8 +3043,7 @@ void QRhiVulkan::dispatch(QRhiCommandBuffer *cb, int x, int y, int z) imageBarriers.append(barrier); } else { QVkBuffer *bufD = QRHI_RES(QVkBuffer, it.key()); - VkBufferMemoryBarrier barrier; - memset(&barrier, 0, sizeof(barrier)); + VkBufferMemoryBarrier barrier = {}; barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; @@ -2589,12 +3070,12 @@ void QRhiVulkan::dispatch(QRhiCommandBuffer *cb, int x, int y, int z) df->vkCmdPipelineBarrier(secondaryCb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, - imageBarriers.count(), imageBarriers.constData()); + imageBarriers.size(), imageBarriers.constData()); } if (!bufferBarriers.isEmpty()) { df->vkCmdPipelineBarrier(secondaryCb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, - bufferBarriers.count(), bufferBarriers.constData(), + bufferBarriers.size(), bufferBarriers.constData(), 0, nullptr); } df->vkCmdDispatch(secondaryCb, uint32_t(x), uint32_t(y), uint32_t(z)); @@ -2604,18 +3085,18 @@ void QRhiVulkan::dispatch(QRhiCommandBuffer *cb, int x, int y, int z) cmd.cmd = QVkCommandBuffer::Command::ImageBarrier; cmd.args.imageBarrier.srcStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; cmd.args.imageBarrier.dstStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; - cmd.args.imageBarrier.count = imageBarriers.count(); - cmd.args.imageBarrier.index = cbD->pools.imageBarrier.count(); - cbD->pools.imageBarrier.append(imageBarriers.constData(), imageBarriers.count()); + cmd.args.imageBarrier.count = imageBarriers.size(); + cmd.args.imageBarrier.index = cbD->pools.imageBarrier.size(); + cbD->pools.imageBarrier.append(imageBarriers.constData(), imageBarriers.size()); } if (!bufferBarriers.isEmpty()) { QVkCommandBuffer::Command &cmd(cbD->commands.get()); cmd.cmd = QVkCommandBuffer::Command::BufferBarrier; cmd.args.bufferBarrier.srcStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; cmd.args.bufferBarrier.dstStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; - cmd.args.bufferBarrier.count = bufferBarriers.count(); - cmd.args.bufferBarrier.index = cbD->pools.bufferBarrier.count(); - cbD->pools.bufferBarrier.append(bufferBarriers.constData(), bufferBarriers.count()); + cmd.args.bufferBarrier.count = bufferBarriers.size(); + cmd.args.bufferBarrier.index = cbD->pools.bufferBarrier.size(); + cbD->pools.bufferBarrier.append(bufferBarriers.constData(), bufferBarriers.size()); } QVkCommandBuffer::Command &cmd(cbD->commands.get()); cmd.cmd = QVkCommandBuffer::Command::Dispatch; @@ -2627,8 +3108,7 @@ void QRhiVulkan::dispatch(QRhiCommandBuffer *cb, int x, int y, int z) VkShaderModule QRhiVulkan::createShader(const QByteArray &spirv) { - VkShaderModuleCreateInfo shaderInfo; - memset(&shaderInfo, 0, sizeof(shaderInfo)); + VkShaderModuleCreateInfo shaderInfo = {}; shaderInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; shaderInfo.codeSize = size_t(spirv.size()); shaderInfo.pCode = reinterpret_cast<const quint32 *>(spirv.constData()); @@ -2646,8 +3126,7 @@ bool QRhiVulkan::ensurePipelineCache(const void *initialData, size_t initialData if (pipelineCache) return true; - VkPipelineCacheCreateInfo pipelineCacheInfo; - memset(&pipelineCacheInfo, 0, sizeof(pipelineCacheInfo)); + VkPipelineCacheCreateInfo pipelineCacheInfo = {}; pipelineCacheInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO; pipelineCacheInfo.initialDataSize = initialDataSize; pipelineCacheInfo.pInitialData = initialData; @@ -2672,12 +3151,11 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i const bool updateAll = descSetIdx < 0; int frameSlot = updateAll ? 0 : descSetIdx; while (frameSlot < (updateAll ? QVK_FRAMES_IN_FLIGHT : descSetIdx + 1)) { - for (int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) { - const QRhiShaderResourceBinding::Data *b = srbD->sortedBindings.at(i).data(); + for (int i = 0, ie = srbD->sortedBindings.size(); i != ie; ++i) { + const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->sortedBindings.at(i)); QVkShaderResourceBindings::BoundResourceData &bd(srbD->boundResourceData[frameSlot][i]); - VkWriteDescriptorSet writeInfo; - memset(&writeInfo, 0, sizeof(writeInfo)); + VkWriteDescriptorSet writeInfo = {}; writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeInfo.dstSet = srbD->descSets[frameSlot]; writeInfo.dstBinding = uint32_t(b->binding); @@ -2697,11 +3175,11 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i bd.ubuf.generation = bufD->generation; VkDescriptorBufferInfo bufInfo; bufInfo.buffer = bufD->m_type == QRhiBuffer::Dynamic ? bufD->buffers[frameSlot] : bufD->buffers[0]; - bufInfo.offset = VkDeviceSize(b->u.ubuf.offset); - bufInfo.range = VkDeviceSize(b->u.ubuf.maybeSize ? b->u.ubuf.maybeSize : bufD->m_size); + bufInfo.offset = b->u.ubuf.offset; + bufInfo.range = b->u.ubuf.maybeSize ? b->u.ubuf.maybeSize : bufD->m_size; // be nice and assert when we know the vulkan device would die a horrible death due to non-aligned reads Q_ASSERT(aligned(bufInfo.offset, ubufAlign) == bufInfo.offset); - bufferInfoIndex = bufferInfos.count(); + bufferInfoIndex = bufferInfos.size(); bufferInfos.append(bufInfo); } break; @@ -2723,7 +3201,7 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i imageInfo[elem].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; } bd.stex.count = data->count; - imageInfoIndex = imageInfos.count(); + imageInfoIndex = imageInfos.size(); imageInfos.append(imageInfo); } break; @@ -2744,7 +3222,7 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i imageInfo[elem].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; } bd.stex.count = data->count; - imageInfoIndex = imageInfos.count(); + imageInfoIndex = imageInfos.size(); imageInfos.append(imageInfo); } break; @@ -2760,7 +3238,7 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i imageInfo[0].sampler = samplerD->sampler; imageInfo[0].imageView = VK_NULL_HANDLE; imageInfo[0].imageLayout = VK_IMAGE_LAYOUT_GENERAL; - imageInfoIndex = imageInfos.count(); + imageInfoIndex = imageInfos.size(); imageInfos.append(imageInfo); } break; @@ -2769,7 +3247,7 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i case QRhiShaderResourceBinding::ImageLoadStore: { QVkTexture *texD = QRHI_RES(QVkTexture, b->u.simage.tex); - VkImageView view = texD->imageViewForLevel(b->u.simage.level); + VkImageView view = texD->perLevelImageViewForLoadStore(b->u.simage.level); if (view) { writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; bd.simage.id = texD->m_id; @@ -2778,7 +3256,7 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i imageInfo[0].sampler = VK_NULL_HANDLE; imageInfo[0].imageView = view; imageInfo[0].imageLayout = VK_IMAGE_LAYOUT_GENERAL; - imageInfoIndex = imageInfos.count(); + imageInfoIndex = imageInfos.size(); imageInfos.append(imageInfo); } } @@ -2793,9 +3271,9 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i bd.sbuf.generation = bufD->generation; VkDescriptorBufferInfo bufInfo; bufInfo.buffer = bufD->m_type == QRhiBuffer::Dynamic ? bufD->buffers[frameSlot] : bufD->buffers[0]; - bufInfo.offset = VkDeviceSize(b->u.ubuf.offset); - bufInfo.range = VkDeviceSize(b->u.ubuf.maybeSize ? b->u.ubuf.maybeSize : bufD->m_size); - bufferInfoIndex = bufferInfos.count(); + bufInfo.offset = b->u.ubuf.offset; + bufInfo.range = b->u.ubuf.maybeSize ? b->u.ubuf.maybeSize : bufD->m_size; + bufferInfoIndex = bufferInfos.size(); bufferInfos.append(bufInfo); } break; @@ -2809,7 +3287,7 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i ++frameSlot; } - for (int i = 0, writeInfoCount = writeInfos.count(); i < writeInfoCount; ++i) { + for (int i = 0, writeInfoCount = writeInfos.size(); i < writeInfoCount; ++i) { const int bufferInfoIndex = infoIndices[i].first; const int imageInfoIndex = infoIndices[i].second; if (bufferInfoIndex >= 0) @@ -2818,7 +3296,7 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i writeInfos[i].pImageInfo = imageInfos[imageInfoIndex].constData(); } - df->vkUpdateDescriptorSets(dev, uint32_t(writeInfos.count()), writeInfos.constData(), 0, nullptr); + df->vkUpdateDescriptorSets(dev, uint32_t(writeInfos.size()), writeInfos.constData(), 0, nullptr); } static inline bool accessIsWrite(VkAccessFlags access) @@ -2850,8 +3328,7 @@ void QRhiVulkan::trackedBufferBarrier(QVkCommandBuffer *cbD, QVkBuffer *bufD, in return; } - VkBufferMemoryBarrier bufMemBarrier; - memset(&bufMemBarrier, 0, sizeof(bufMemBarrier)); + VkBufferMemoryBarrier bufMemBarrier = {}; bufMemBarrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; bufMemBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; bufMemBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; @@ -2865,7 +3342,7 @@ void QRhiVulkan::trackedBufferBarrier(QVkCommandBuffer *cbD, QVkBuffer *bufD, in cmd.args.bufferBarrier.srcStageMask = s.stage; cmd.args.bufferBarrier.dstStageMask = stage; cmd.args.bufferBarrier.count = 1; - cmd.args.bufferBarrier.index = cbD->pools.bufferBarrier.count(); + cmd.args.bufferBarrier.index = cbD->pools.bufferBarrier.size(); cbD->pools.bufferBarrier.append(bufMemBarrier); s.access = access; @@ -2883,8 +3360,7 @@ void QRhiVulkan::trackedImageBarrier(QVkCommandBuffer *cbD, QVkTexture *texD, return; } - VkImageMemoryBarrier barrier; - memset(&barrier, 0, sizeof(barrier)); + VkImageMemoryBarrier barrier = {}; barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; barrier.subresourceRange.aspectMask = aspectMaskForTextureFormat(texD->m_format); barrier.subresourceRange.baseMipLevel = 0; @@ -2907,7 +3383,7 @@ void QRhiVulkan::trackedImageBarrier(QVkCommandBuffer *cbD, QVkTexture *texD, cmd.args.imageBarrier.srcStageMask = srcStage; cmd.args.imageBarrier.dstStageMask = stage; cmd.args.imageBarrier.count = 1; - cmd.args.imageBarrier.index = cbD->pools.imageBarrier.count(); + cmd.args.imageBarrier.index = cbD->pools.imageBarrier.size(); cbD->pools.imageBarrier.append(barrier); s.layout = layout; @@ -2919,8 +3395,7 @@ void QRhiVulkan::depthStencilExplicitBarrier(QVkCommandBuffer *cbD, QVkRenderBuf { Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::NoPass); - VkImageMemoryBarrier barrier; - memset(&barrier, 0, sizeof(barrier)); + VkImageMemoryBarrier barrier = {}; barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; barrier.subresourceRange.baseMipLevel = 0; @@ -2942,7 +3417,7 @@ void QRhiVulkan::depthStencilExplicitBarrier(QVkCommandBuffer *cbD, QVkRenderBuf cmd.args.imageBarrier.srcStageMask = stages; cmd.args.imageBarrier.dstStageMask = stages; cmd.args.imageBarrier.count = 1; - cmd.args.imageBarrier.index = cbD->pools.imageBarrier.count(); + cmd.args.imageBarrier.index = cbD->pools.imageBarrier.size(); cbD->pools.imageBarrier.append(barrier); } @@ -2954,8 +3429,7 @@ void QRhiVulkan::subresourceBarrier(QVkCommandBuffer *cbD, VkImage image, int startLevel, int levelCount) { Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::NoPass); - VkImageMemoryBarrier barrier; - memset(&barrier, 0, sizeof(barrier)); + VkImageMemoryBarrier barrier = {}; barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; barrier.subresourceRange.baseMipLevel = uint32_t(startLevel); @@ -2973,7 +3447,7 @@ void QRhiVulkan::subresourceBarrier(QVkCommandBuffer *cbD, VkImage image, cmd.args.imageBarrier.srcStageMask = srcStage; cmd.args.imageBarrier.dstStageMask = dstStage; cmd.args.imageBarrier.count = 1; - cmd.args.imageBarrier.index = cbD->pools.imageBarrier.count(); + cmd.args.imageBarrier.index = cbD->pools.imageBarrier.size(); cbD->pools.imageBarrier.append(barrier); } @@ -2996,9 +3470,9 @@ void QRhiVulkan::prepareUploadSubres(QVkTexture *texD, int layer, int level, qsizetype imageSizeBytes = 0; const void *src = nullptr; const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional); + const bool is1D = texD->m_flags.testFlag(QRhiTexture::OneDimensional); - VkBufferImageCopy copyInfo; - memset(©Info, 0, sizeof(copyInfo)); + VkBufferImageCopy copyInfo = {}; copyInfo.bufferOffset = *curOfs; copyInfo.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; copyInfo.imageSubresource.mipLevel = uint32_t(level); @@ -3007,6 +3481,8 @@ void QRhiVulkan::prepareUploadSubres(QVkTexture *texD, int layer, int level, copyInfo.imageExtent.depth = 1; if (is3D) copyInfo.imageOffset.z = uint32_t(layer); + if (is1D) + copyInfo.imageOffset.y = uint32_t(layer); const QByteArray rawData = subresDesc.data(); const QPoint dp = subresDesc.destinationTopLeft(); @@ -3025,12 +3501,12 @@ void QRhiVulkan::prepareUploadSubres(QVkTexture *texD, int layer, int level, const int sy = subresDesc.sourceTopLeft().y(); if (!subresDesc.sourceSize().isEmpty()) size = subresDesc.sourceSize(); - if (image.depth() == 32) { - // The staging buffer will get the full image - // regardless, just adjust the vk - // buffer-to-image copy start offset. - copyInfo.bufferOffset += VkDeviceSize(sy * image.bytesPerLine() + sx * 4); - // bufferRowLength remains set to the original image's width + + if (size.width() == image.width()) { + // No need to make a QImage copy here, can copy from the source + // QImage into staging directly. + src = image.constBits() + sy * image.bytesPerLine() + sx * bpc; + copySizeBytes = size.height() * image.bytesPerLine(); } else { image = image.copy(sx, sy, size.width(), size.height()); src = image.constBits(); @@ -3093,6 +3569,12 @@ void QRhiVulkan::prepareUploadSubres(QVkTexture *texD, int layer, int level, } } +void QRhiVulkan::printExtraErrorInfo(VkResult err) +{ + if (err == VK_ERROR_OUT_OF_DEVICE_MEMORY) + qWarning() << "Out of device memory, current allocator statistics are" << statistics(); +} + void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdateBatch *resourceUpdates) { QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates); @@ -3113,16 +3595,14 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat Q_ASSERT(u.offset + u.data.size() <= bufD->m_size); if (!bufD->stagingBuffers[currentFrameSlot]) { - VkBufferCreateInfo bufferInfo; - memset(&bufferInfo, 0, sizeof(bufferInfo)); + VkBufferCreateInfo bufferInfo = {}; bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; // must cover the entire buffer - this way multiple, partial updates per frame // are supported even when the staging buffer is reused (Static) - bufferInfo.size = VkDeviceSize(bufD->m_size); + bufferInfo.size = bufD->m_size; bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; - VmaAllocationCreateInfo allocInfo; - memset(&allocInfo, 0, sizeof(allocInfo)); + VmaAllocationCreateInfo allocInfo = {}; allocInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY; VmaAllocation allocation; @@ -3131,7 +3611,8 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat if (err == VK_SUCCESS) { bufD->stagingAllocations[currentFrameSlot] = allocation; } else { - qWarning("Failed to create staging buffer of size %d: %d", bufD->m_size, err); + qWarning("Failed to create staging buffer of size %u: %d", bufD->m_size, err); + printExtraErrorInfo(err); continue; } } @@ -3143,18 +3624,17 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat qWarning("Failed to map buffer: %d", err); continue; } - memcpy(static_cast<uchar *>(p) + u.offset, u.data.constData(), size_t(u.data.size())); + memcpy(static_cast<uchar *>(p) + u.offset, u.data.constData(), u.data.size()); + vmaFlushAllocation(toVmaAllocator(allocator), a, u.offset, u.data.size()); vmaUnmapMemory(toVmaAllocator(allocator), a); - vmaFlushAllocation(toVmaAllocator(allocator), a, VkDeviceSize(u.offset), VkDeviceSize(u.data.size())); trackedBufferBarrier(cbD, bufD, 0, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); - VkBufferCopy copyInfo; - memset(©Info, 0, sizeof(copyInfo)); - copyInfo.srcOffset = VkDeviceSize(u.offset); - copyInfo.dstOffset = VkDeviceSize(u.offset); - copyInfo.size = VkDeviceSize(u.data.size()); + VkBufferCopy copyInfo = {}; + copyInfo.srcOffset = u.offset; + copyInfo.dstOffset = u.offset; + copyInfo.size = u.data.size(); QVkCommandBuffer::Command &cmd(cbD->commands.get()); cmd.cmd = QVkCommandBuffer::Command::CopyBuffer; @@ -3190,7 +3670,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat VkResult err = vmaMapMemory(toVmaAllocator(allocator), a, &p); if (err == VK_SUCCESS) { u.result->data.resize(u.readSize); - memcpy(u.result->data.data(), reinterpret_cast<char *>(p) + u.offset, size_t(u.readSize)); + memcpy(u.result->data.data(), reinterpret_cast<char *>(p) + u.offset, u.readSize); vmaUnmapMemory(toVmaAllocator(allocator), a); } if (u.result->completed) @@ -3207,14 +3687,12 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat readback.result = u.result; readback.byteSize = u.readSize; - VkBufferCreateInfo bufferInfo; - memset(&bufferInfo, 0, sizeof(bufferInfo)); + VkBufferCreateInfo bufferInfo = {}; bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; - bufferInfo.size = VkDeviceSize(readback.byteSize); + bufferInfo.size = readback.byteSize; bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT; - VmaAllocationCreateInfo allocInfo; - memset(&allocInfo, 0, sizeof(allocInfo)); + VmaAllocationCreateInfo allocInfo = {}; allocInfo.usage = VMA_MEMORY_USAGE_GPU_TO_CPU; VmaAllocation allocation; @@ -3223,15 +3701,15 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat readback.stagingAlloc = allocation; } else { qWarning("Failed to create readback buffer of size %u: %d", readback.byteSize, err); + printExtraErrorInfo(err); continue; } trackedBufferBarrier(cbD, bufD, 0, VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); - VkBufferCopy copyInfo; - memset(©Info, 0, sizeof(copyInfo)); - copyInfo.srcOffset = VkDeviceSize(u.offset); - copyInfo.size = VkDeviceSize(u.readSize); + VkBufferCopy copyInfo = {}; + copyInfo.srcOffset = u.offset; + copyInfo.size = u.readSize; QVkCommandBuffer::Command &cmd(cbD->commands.get()); cmd.cmd = QVkCommandBuffer::Command::CopyBuffer; @@ -3252,22 +3730,20 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat QVkTexture *utexD = QRHI_RES(QVkTexture, u.dst); // batch into a single staging buffer and a single CopyBufferToImage with multiple copyInfos VkDeviceSize stagingSize = 0; - for (int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) { + for (int layer = 0, maxLayer = u.subresDesc.size(); layer < maxLayer; ++layer) { for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) { - for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.subresDesc[layer][level])) + for (const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level])) stagingSize += subresUploadByteSize(subresDesc); } } Q_ASSERT(!utexD->stagingBuffers[currentFrameSlot]); - VkBufferCreateInfo bufferInfo; - memset(&bufferInfo, 0, sizeof(bufferInfo)); + VkBufferCreateInfo bufferInfo = {}; bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; bufferInfo.size = stagingSize; bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; - VmaAllocationCreateInfo allocInfo; - memset(&allocInfo, 0, sizeof(allocInfo)); + VmaAllocationCreateInfo allocInfo = {}; allocInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU; VmaAllocation allocation; @@ -3275,6 +3751,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat &utexD->stagingBuffers[currentFrameSlot], &allocation, nullptr); if (err != VK_SUCCESS) { qWarning("Failed to create image staging buffer of size %d: %d", int(stagingSize), err); + printExtraErrorInfo(err); continue; } utexD->stagingAllocations[currentFrameSlot] = allocation; @@ -3289,19 +3766,19 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat continue; } - for (int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) { + for (int layer = 0, maxLayer = u.subresDesc.size(); layer < maxLayer; ++layer) { for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) { const QList<QRhiTextureSubresourceUploadDescription> &srd(u.subresDesc[layer][level]); if (srd.isEmpty()) continue; - for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(srd)) { + for (const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(srd)) { prepareUploadSubres(utexD, layer, level, subresDesc, &curOfs, mp, ©Infos); } } } - vmaUnmapMemory(toVmaAllocator(allocator), a); vmaFlushAllocation(toVmaAllocator(allocator), a, 0, stagingSize); + vmaUnmapMemory(toVmaAllocator(allocator), a); trackedImageBarrier(cbD, utexD, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); @@ -3311,9 +3788,9 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat cmd.args.copyBufferToImage.src = utexD->stagingBuffers[currentFrameSlot]; cmd.args.copyBufferToImage.dst = utexD->image; cmd.args.copyBufferToImage.dstLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - cmd.args.copyBufferToImage.count = copyInfos.count(); - cmd.args.copyBufferToImage.bufferImageCopyIndex = cbD->pools.bufferImageCopy.count(); - cbD->pools.bufferImageCopy.append(copyInfos.constData(), copyInfos.count()); + cmd.args.copyBufferToImage.count = copyInfos.size(); + cmd.args.copyBufferToImage.bufferImageCopyIndex = cbD->pools.bufferImageCopy.size(); + cbD->pools.bufferImageCopy.append(copyInfos.constData(), copyInfos.size()); // no reuse of staging, this is intentional QRhiVulkan::DeferredReleaseEntry e; @@ -3340,9 +3817,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat const bool srcIs3D = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional); const bool dstIs3D = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional); - VkImageCopy region; - memset(®ion, 0, sizeof(region)); - + VkImageCopy region = {}; region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; region.srcSubresource.mipLevel = uint32_t(u.desc.sourceLevel()); region.srcSubresource.baseArrayLayer = srcIs3D ? 0 : uint32_t(u.desc.sourceLayer()); @@ -3419,14 +3894,12 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat textureFormatInfo(readback.format, readback.pixelSize, nullptr, &readback.byteSize, nullptr); // Create a host visible readback buffer. - VkBufferCreateInfo bufferInfo; - memset(&bufferInfo, 0, sizeof(bufferInfo)); + VkBufferCreateInfo bufferInfo = {}; bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; bufferInfo.size = readback.byteSize; bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT; - VmaAllocationCreateInfo allocInfo; - memset(&allocInfo, 0, sizeof(allocInfo)); + VmaAllocationCreateInfo allocInfo = {}; allocInfo.usage = VMA_MEMORY_USAGE_GPU_TO_CPU; VmaAllocation allocation; @@ -3435,12 +3908,12 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat readback.stagingAlloc = allocation; } else { qWarning("Failed to create readback buffer of size %u: %d", readback.byteSize, err); + printExtraErrorInfo(err); continue; } // Copy from the (optimal and not host visible) image into the buffer. - VkBufferImageCopy copyDesc; - memset(©Desc, 0, sizeof(copyDesc)); + VkBufferImageCopy copyDesc = {}; copyDesc.bufferOffset = 0; copyDesc.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; copyDesc.imageSubresource.mipLevel = uint32_t(u.rb.level()); @@ -3501,10 +3974,10 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat if (!origStage) origStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; - for (int layer = 0; layer < (isCube ? 6 : (isArray ? utexD->m_arraySize : 1)); ++layer) { + for (int layer = 0; layer < (isCube ? 6 : (isArray ? qMax(0, utexD->m_arraySize) : 1)); ++layer) { int w = utexD->m_pixelSize.width(); int h = utexD->m_pixelSize.height(); - int depth = is3D ? utexD->m_depth : 1; + int depth = is3D ? qMax(1, utexD->m_depth) : 1; for (int level = 1; level < int(utexD->mipLevelCount); ++level) { if (level == 1) { subresourceBarrier(cbD, utexD->image, @@ -3529,9 +4002,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat layer, 1, level, 1); - VkImageBlit region; - memset(®ion, 0, sizeof(region)); - + VkImageBlit region = {}; region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; region.srcSubresource.mipLevel = uint32_t(level) - 1; region.srcSubresource.baseArrayLayer = uint32_t(layer); @@ -3602,18 +4073,18 @@ void QRhiVulkan::executeBufferHostWritesForSlot(QVkBuffer *bufD, int slot) qWarning("Failed to map buffer: %d", err); return; } - int changeBegin = -1; - int changeEnd = -1; - for (const QVkBuffer::DynamicUpdate &u : qAsConst(bufD->pendingDynamicUpdates[slot])) { - memcpy(static_cast<char *>(p) + u.offset, u.data.constData(), size_t(u.data.size())); - if (changeBegin == -1 || u.offset < changeBegin) + quint32 changeBegin = UINT32_MAX; + quint32 changeEnd = 0; + for (const QVkBuffer::DynamicUpdate &u : std::as_const(bufD->pendingDynamicUpdates[slot])) { + memcpy(static_cast<char *>(p) + u.offset, u.data.constData(), u.data.size()); + if (u.offset < changeBegin) changeBegin = u.offset; - if (changeEnd == -1 || u.offset + u.data.size() > changeEnd) + if (u.offset + u.data.size() > changeEnd) changeEnd = u.offset + u.data.size(); } + if (changeBegin < UINT32_MAX && changeBegin < changeEnd) + vmaFlushAllocation(toVmaAllocator(allocator), a, changeBegin, changeEnd - changeBegin); vmaUnmapMemory(toVmaAllocator(allocator), a); - if (changeBegin >= 0) - vmaFlushAllocation(toVmaAllocator(allocator), a, VkDeviceSize(changeBegin), VkDeviceSize(changeEnd - changeBegin)); bufD->pendingDynamicUpdates[slot].clear(); } @@ -3652,7 +4123,7 @@ static void qrhivk_releaseSampler(const QRhiVulkan::DeferredReleaseEntry &e, VkD void QRhiVulkan::executeDeferredReleases(bool forced) { - for (int i = releaseQueue.count() - 1; i >= 0; --i) { + for (int i = releaseQueue.size() - 1; i >= 0; --i) { const QRhiVulkan::DeferredReleaseEntry &e(releaseQueue[i]); if (forced || currentFrameSlot == e.lastActiveFrameSlot || e.lastActiveFrameSlot < 0) { switch (e.type) { @@ -3685,6 +4156,8 @@ void QRhiVulkan::executeDeferredReleases(bool forced) df->vkDestroyImageView(dev, e.textureRenderTarget.rtv[att], nullptr); df->vkDestroyImageView(dev, e.textureRenderTarget.resrtv[att], nullptr); } + df->vkDestroyImageView(dev, e.textureRenderTarget.dsv, nullptr); + df->vkDestroyImageView(dev, e.textureRenderTarget.resdsv, nullptr); break; case QRhiVulkan::DeferredReleaseEntry::RenderPass: df->vkDestroyRenderPass(dev, e.renderPass.rp, nullptr); @@ -3708,7 +4181,7 @@ void QRhiVulkan::finishActiveReadbacks(bool forced) { QVarLengthArray<std::function<void()>, 4> completedCallbacks; - for (int i = activeTextureReadbacks.count() - 1; i >= 0; --i) { + for (int i = activeTextureReadbacks.size() - 1; i >= 0; --i) { const QRhiVulkan::TextureReadback &readback(activeTextureReadbacks[i]); if (forced || currentFrameSlot == readback.activeFrameSlot || readback.activeFrameSlot < 0) { readback.result->format = readback.format; @@ -3733,7 +4206,7 @@ void QRhiVulkan::finishActiveReadbacks(bool forced) } } - for (int i = activeBufferReadbacks.count() - 1; i >= 0; --i) { + for (int i = activeBufferReadbacks.size() - 1; i >= 0; --i) { const QRhiVulkan::BufferReadback &readback(activeBufferReadbacks[i]); if (forced || currentFrameSlot == readback.activeFrameSlot || readback.activeFrameSlot < 0) { VmaAllocation a = toVmaAllocation(readback.stagingAlloc); @@ -3741,7 +4214,7 @@ void QRhiVulkan::finishActiveReadbacks(bool forced) VkResult err = vmaMapMemory(toVmaAllocator(allocator), a, &p); if (err == VK_SUCCESS && p) { readback.result->data.resize(readback.byteSize); - memcpy(readback.result->data.data(), p, size_t(readback.byteSize)); + memcpy(readback.result->data.data(), p, readback.byteSize); vmaUnmapMemory(toVmaAllocator(allocator), a); } else { qWarning("Failed to map buffer readback buffer of size %d: %d", readback.byteSize, err); @@ -3794,33 +4267,26 @@ QList<int> QRhiVulkan::supportedSampleCounts() const return result; } -VkSampleCountFlagBits QRhiVulkan::effectiveSampleCount(int sampleCount) +VkSampleCountFlagBits QRhiVulkan::effectiveSampleCountBits(int sampleCount) { - // Stay compatible with QSurfaceFormat and friends where samples == 0 means the same as 1. - sampleCount = qBound(1, sampleCount, 64); - - if (!supportedSampleCounts().contains(sampleCount)) { - qWarning("Attempted to set unsupported sample count %d", sampleCount); - return VK_SAMPLE_COUNT_1_BIT; - } + const int s = effectiveSampleCount(sampleCount); for (const auto &qvk_sampleCount : qvk_sampleCounts) { - if (qvk_sampleCount.count == sampleCount) + if (qvk_sampleCount.count == s) return qvk_sampleCount.mask; } - Q_UNREACHABLE(); - return VK_SAMPLE_COUNT_1_BIT; + Q_UNREACHABLE_RETURN(VK_SAMPLE_COUNT_1_BIT); } void QRhiVulkan::enqueueTransitionPassResources(QVkCommandBuffer *cbD) { cbD->passResTrackers.append(QRhiPassResourceTracker()); - cbD->currentPassResTrackerIndex = cbD->passResTrackers.count() - 1; + cbD->currentPassResTrackerIndex = cbD->passResTrackers.size() - 1; QVkCommandBuffer::Command &cmd(cbD->commands.get()); cmd.cmd = QVkCommandBuffer::Command::TransitionPassResources; - cmd.args.transitionResources.trackerIndex = cbD->passResTrackers.count() - 1; + cmd.args.transitionResources.trackerIndex = cbD->passResTrackers.size() - 1; } void QRhiVulkan::recordPrimaryCommandBuffer(QVkCommandBuffer *cbD) @@ -3923,17 +4389,23 @@ void QRhiVulkan::recordPrimaryCommandBuffer(QVkCommandBuffer *cbD) cmd.args.drawIndexed.firstInstance); break; case QVkCommandBuffer::Command::DebugMarkerBegin: - cmd.args.debugMarkerBegin.marker.pMarkerName = - cbD->pools.debugMarkerData[cmd.args.debugMarkerBegin.markerNameIndex].constData(); - vkCmdDebugMarkerBegin(cbD->cb, &cmd.args.debugMarkerBegin.marker); +#ifdef VK_EXT_debug_utils + cmd.args.debugMarkerBegin.label.pLabelName = + cbD->pools.debugMarkerData[cmd.args.debugMarkerBegin.labelNameIndex].constData(); + vkCmdBeginDebugUtilsLabelEXT(cbD->cb, &cmd.args.debugMarkerBegin.label); +#endif break; case QVkCommandBuffer::Command::DebugMarkerEnd: - vkCmdDebugMarkerEnd(cbD->cb); +#ifdef VK_EXT_debug_utils + vkCmdEndDebugUtilsLabelEXT(cbD->cb); +#endif break; case QVkCommandBuffer::Command::DebugMarkerInsert: - cmd.args.debugMarkerInsert.marker.pMarkerName = - cbD->pools.debugMarkerData[cmd.args.debugMarkerInsert.markerNameIndex].constData(); - vkCmdDebugMarkerInsert(cbD->cb, &cmd.args.debugMarkerInsert.marker); +#ifdef VK_EXT_debug_utils + cmd.args.debugMarkerInsert.label.pLabelName = + cbD->pools.debugMarkerData[cmd.args.debugMarkerInsert.labelNameIndex].constData(); + vkCmdInsertDebugUtilsLabelEXT(cbD->cb, &cmd.args.debugMarkerInsert.label); +#endif break; case QVkCommandBuffer::Command::TransitionPassResources: recordTransitionPassResources(cbD, cbD->passResTrackers[cmd.args.transitionResources.trackerIndex]); @@ -3987,6 +4459,8 @@ static inline VkPipelineStageFlags toVkPipelineStage(QRhiPassResourceTracker::Bu return VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; case QRhiPassResourceTracker::BufComputeStage: return VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + case QRhiPassResourceTracker::BufGeometryStage: + return VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT; default: Q_UNREACHABLE(); break; @@ -4061,6 +4535,8 @@ static inline VkPipelineStageFlags toVkPipelineStage(QRhiPassResourceTracker::Te return VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; case QRhiPassResourceTracker::TexComputeStage: return VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + case QRhiPassResourceTracker::TexGeometryStage: + return VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT; default: Q_UNREACHABLE(); break; @@ -4130,8 +4606,7 @@ void QRhiVulkan::recordTransitionPassResources(QVkCommandBuffer *cbD, const QRhi if (!accessIsWrite(access)) continue; } - VkBufferMemoryBarrier bufMemBarrier; - memset(&bufMemBarrier, 0, sizeof(bufMemBarrier)); + VkBufferMemoryBarrier bufMemBarrier = {}; bufMemBarrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; bufMemBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; bufMemBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; @@ -4155,8 +4630,7 @@ void QRhiVulkan::recordTransitionPassResources(QVkCommandBuffer *cbD, const QRhi if (!accessIsWrite(access)) continue; } - VkImageMemoryBarrier barrier; - memset(&barrier, 0, sizeof(barrier)); + VkImageMemoryBarrier barrier = {}; barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; barrier.subresourceRange.aspectMask = aspectMaskForTextureFormat(texD->m_format); barrier.subresourceRange.baseMipLevel = 0; @@ -4181,10 +4655,18 @@ void QRhiVulkan::recordTransitionPassResources(QVkCommandBuffer *cbD, const QRhi QRhiSwapChain *QRhiVulkan::createSwapChain() { + if (!vkGetPhysicalDeviceSurfaceCapabilitiesKHR + || !vkGetPhysicalDeviceSurfaceFormatsKHR + || !vkGetPhysicalDeviceSurfacePresentModesKHR) + { + qWarning("Physical device surface queries not available"); + return nullptr; + } + return new QVkSwapChain(this); } -QRhiBuffer *QRhiVulkan::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, int size) +QRhiBuffer *QRhiVulkan::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, quint32 size) { return new QVkBuffer(this, type, usage, size); } @@ -4258,7 +4740,7 @@ bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const case QRhi::MultisampleRenderBuffer: return true; case QRhi::DebugMarkers: - return caps.debugMarkers; + return caps.debugUtils; case QRhi::Timestamps: return timestampValidBits != 0; case QRhi::Instancing: @@ -4317,9 +4799,30 @@ bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const return true; case QRhi::Tessellation: return caps.tessellation; + case QRhi::GeometryShader: + return caps.geometryShader; + case QRhi::TextureArrayRange: + return true; + case QRhi::NonFillPolygonMode: + return caps.nonFillPolygonMode; + case QRhi::OneDimensionalTextures: + return true; + case QRhi::OneDimensionalTextureMipmaps: + return true; + case QRhi::HalfAttributes: + return true; + case QRhi::RenderToOneDimensionalTexture: + return true; + case QRhi::ThreeDimensionalTextureMipmaps: + return true; + case QRhi::MultiView: + return caps.multiView; + case QRhi::TextureViewFormat: + return true; + case QRhi::ResolveDepthStencil: + return caps.renderPass2KHR && caps.depthStencilResolveKHR; default: - Q_UNREACHABLE(); - return false; + Q_UNREACHABLE_RETURN(false); } } @@ -4357,8 +4860,7 @@ int QRhiVulkan::resourceLimit(QRhi::ResourceLimit limit) const case QRhi::MaxVertexOutputs: return physDevProperties.limits.maxVertexOutputComponents / 4; default: - Q_UNREACHABLE(); - return 0; + Q_UNREACHABLE_RETURN(0); } } @@ -4372,16 +4874,24 @@ QRhiDriverInfo QRhiVulkan::driverInfo() const return driverInfoStruct; } -QRhiMemAllocStats QRhiVulkan::graphicsMemoryAllocationStatistics() +QRhiStats QRhiVulkan::statistics() { - VmaStats stats; - vmaCalculateStats(toVmaAllocator(allocator), &stats); - return { - stats.total.blockCount, - stats.total.allocationCount, - stats.total.usedBytes, - stats.total.unusedBytes - }; + QRhiStats result; + result.totalPipelineCreationTime = totalPipelineCreationTime(); + + VmaBudget budgets[VK_MAX_MEMORY_HEAPS]; + vmaGetHeapBudgets(toVmaAllocator(allocator), budgets); + + uint32_t count = toVmaAllocator(allocator)->GetMemoryHeapCount(); + for (uint32_t i = 0; i < count; ++i) { + const VmaStatistics &stats(budgets[i].statistics); + result.blockCount += stats.blockCount; + result.allocCount += stats.allocationCount; + result.usedBytes += stats.allocationBytes; + result.unusedBytes += stats.blockBytes - stats.allocationBytes; + } + + return result; } bool QRhiVulkan::makeThreadLocalNativeContextCurrent() @@ -4423,7 +4933,7 @@ QByteArray QRhiVulkan::pipelineCacheData() size_t dataSize = 0; VkResult err = df->vkGetPipelineCacheData(dev, pipelineCache, &dataSize, nullptr); if (err != VK_SUCCESS) { - qWarning("Failed to get pipeline cache data size: %d", err); + qCDebug(QRHI_LOG_INFO, "Failed to get pipeline cache data size: %d", err); return QByteArray(); } const size_t headerSize = sizeof(QVkPipelineCacheDataHeader); @@ -4431,7 +4941,7 @@ QByteArray QRhiVulkan::pipelineCacheData() data.resize(dataOffset + dataSize); err = df->vkGetPipelineCacheData(dev, pipelineCache, &dataSize, data.data() + dataOffset); if (err != VK_SUCCESS) { - qWarning("Failed to get pipeline cache data of %d bytes: %d", int(dataSize), err); + qCDebug(QRHI_LOG_INFO, "Failed to get pipeline cache data of %d bytes: %d", int(dataSize), err); return QByteArray(); } @@ -4443,6 +4953,7 @@ QByteArray QRhiVulkan::pipelineCacheData() header.deviceId = physDevProperties.deviceID; header.dataSize = quint32(dataSize); header.uuidSize = VK_UUID_SIZE; + header.reserved = 0; memcpy(data.data(), &header, headerSize); memcpy(data.data() + headerSize, physDevProperties.pipelineCacheUUID, VK_UUID_SIZE); @@ -4456,7 +4967,7 @@ void QRhiVulkan::setPipelineCacheData(const QByteArray &data) const size_t headerSize = sizeof(QVkPipelineCacheDataHeader); if (data.size() < qsizetype(headerSize)) { - qWarning("setPipelineCacheData: Invalid blob size"); + qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob size"); return; } QVkPipelineCacheDataHeader header; @@ -4464,49 +4975,49 @@ void QRhiVulkan::setPipelineCacheData(const QByteArray &data) const quint32 rhiId = pipelineCacheRhiId(); if (header.rhiId != rhiId) { - qWarning("setPipelineCacheData: The data is for a different QRhi version or backend (%u, %u)", - rhiId, header.rhiId); + qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: The data is for a different QRhi version or backend (%u, %u)", + rhiId, header.rhiId); return; } const quint32 arch = quint32(sizeof(void*)); if (header.arch != arch) { - qWarning("setPipelineCacheData: Architecture does not match (%u, %u)", + qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Architecture does not match (%u, %u)", arch, header.arch); return; } if (header.driverVersion != physDevProperties.driverVersion) { - qWarning("setPipelineCacheData: driverVersion does not match (%u, %u)", - physDevProperties.driverVersion, header.driverVersion); + qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: driverVersion does not match (%u, %u)", + physDevProperties.driverVersion, header.driverVersion); return; } if (header.vendorId != physDevProperties.vendorID) { - qWarning("setPipelineCacheData: vendorID does not match (%u, %u)", - physDevProperties.vendorID, header.vendorId); + qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: vendorID does not match (%u, %u)", + physDevProperties.vendorID, header.vendorId); return; } if (header.deviceId != physDevProperties.deviceID) { - qWarning("setPipelineCacheData: deviceID does not match (%u, %u)", - physDevProperties.deviceID, header.deviceId); + qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: deviceID does not match (%u, %u)", + physDevProperties.deviceID, header.deviceId); return; } if (header.uuidSize != VK_UUID_SIZE) { - qWarning("setPipelineCacheData: VK_UUID_SIZE does not match (%u, %u)", - quint32(VK_UUID_SIZE), header.uuidSize); + qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: VK_UUID_SIZE does not match (%u, %u)", + quint32(VK_UUID_SIZE), header.uuidSize); return; } if (data.size() < qsizetype(headerSize + VK_UUID_SIZE)) { - qWarning("setPipelineCacheData: Invalid blob, no uuid"); + qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob, no uuid"); return; } if (memcmp(data.constData() + headerSize, physDevProperties.pipelineCacheUUID, VK_UUID_SIZE)) { - qWarning("setPipelineCacheData: pipelineCacheUUID does not match"); + qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: pipelineCacheUUID does not match"); return; } const size_t dataOffset = headerSize + VK_UUID_SIZE; if (data.size() < qsizetype(dataOffset + header.dataSize)) { - qWarning("setPipelineCacheData: Invalid blob, data missing"); + qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob, data missing"); return; } @@ -4519,7 +5030,7 @@ void QRhiVulkan::setPipelineCacheData(const QByteArray &data) qCDebug(QRHI_LOG_INFO, "Created pipeline cache with initial data of %d bytes", int(header.dataSize)); } else { - qWarning("Failed to create pipeline cache with initial data specified"); + qCDebug(QRHI_LOG_INFO, "Failed to create pipeline cache with initial data specified"); } } @@ -4614,8 +5125,8 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin // Do host writes and mark referenced shader resources as in-use. // Also prepare to ensure the descriptor set we are going to bind refers to up-to-date Vk objects. - for (int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) { - const QRhiShaderResourceBinding::Data *b = srbD->sortedBindings[i].data(); + for (int i = 0, ie = srbD->sortedBindings.size(); i != ie; ++i) { + const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->sortedBindings[i]); QVkShaderResourceBindings::BoundResourceData &bd(descSetBd[i]); switch (b->type) { case QRhiShaderResourceBinding::UniformBuffer: @@ -4761,8 +5272,8 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin // because dynOfs has to be ordered based on the binding numbers, // and neither srb nor dynamicOffsets has any such ordering // requirement. - for (const QRhiShaderResourceBinding &binding : qAsConst(srbD->sortedBindings)) { - const QRhiShaderResourceBinding::Data *b = binding.data(); + for (const QRhiShaderResourceBinding &binding : std::as_const(srbD->sortedBindings)) { + const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(binding); if (b->type == QRhiShaderResourceBinding::UniformBuffer && b->u.ubuf.hasDynamicOffset) { uint32_t offset = 0; for (int i = 0; i < dynamicOffsetCount; ++i) { @@ -4782,8 +5293,8 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin gfxPsD ? VK_PIPELINE_BIND_POINT_GRAPHICS : VK_PIPELINE_BIND_POINT_COMPUTE, gfxPsD ? gfxPsD->layout : compPsD->layout, 0, 1, &srbD->descSets[descSetIdx], - uint32_t(dynOfs.count()), - dynOfs.count() ? dynOfs.constData() : nullptr); + uint32_t(dynOfs.size()), + dynOfs.size() ? dynOfs.constData() : nullptr); } else { QVkCommandBuffer::Command &cmd(cbD->commands.get()); cmd.cmd = QVkCommandBuffer::Command::BindDescriptorSet; @@ -4791,9 +5302,9 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin : VK_PIPELINE_BIND_POINT_COMPUTE; cmd.args.bindDescriptorSet.pipelineLayout = gfxPsD ? gfxPsD->layout : compPsD->layout; cmd.args.bindDescriptorSet.descSet = srbD->descSets[descSetIdx]; - cmd.args.bindDescriptorSet.dynamicOffsetCount = dynOfs.count(); - cmd.args.bindDescriptorSet.dynamicOffsetIndex = cbD->pools.dynamicOffset.count(); - cbD->pools.dynamicOffset.append(dynOfs.constData(), dynOfs.count()); + cmd.args.bindDescriptorSet.dynamicOffsetCount = dynOfs.size(); + cmd.args.bindDescriptorSet.dynamicOffsetIndex = cbD->pools.dynamicOffset.size(); + cbD->pools.dynamicOffset.append(dynOfs.constData(), dynOfs.size()); } if (gfxPsD) { @@ -4852,16 +5363,16 @@ void QRhiVulkan::setVertexInput(QRhiCommandBuffer *cb, if (cbD->passUsesSecondaryCb) { df->vkCmdBindVertexBuffers(cbD->activeSecondaryCbStack.last(), uint32_t(startBinding), - uint32_t(bufs.count()), bufs.constData(), ofs.constData()); + uint32_t(bufs.size()), bufs.constData(), ofs.constData()); } else { QVkCommandBuffer::Command &cmd(cbD->commands.get()); cmd.cmd = QVkCommandBuffer::Command::BindVertexBuffer; cmd.args.bindVertexBuffer.startBinding = startBinding; - cmd.args.bindVertexBuffer.count = bufs.count(); - cmd.args.bindVertexBuffer.vertexBufferIndex = cbD->pools.vertexBuffer.count(); - cbD->pools.vertexBuffer.append(bufs.constData(), bufs.count()); - cmd.args.bindVertexBuffer.vertexBufferOffsetIndex = cbD->pools.vertexBufferOffset.count(); - cbD->pools.vertexBufferOffset.append(ofs.constData(), ofs.count()); + cmd.args.bindVertexBuffer.count = bufs.size(); + cmd.args.bindVertexBuffer.vertexBufferIndex = cbD->pools.vertexBuffer.size(); + cbD->pools.vertexBuffer.append(bufs.constData(), bufs.size()); + cmd.args.bindVertexBuffer.vertexBufferOffsetIndex = cbD->pools.vertexBufferOffset.size(); + cbD->pools.vertexBufferOffset.append(ofs.constData(), ofs.size()); } } @@ -4910,7 +5421,7 @@ void QRhiVulkan::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport // x,y is top-left in VkViewport but bottom-left in QRhiViewport float x, y, w, h; - if (!qrhi_toTopLeftRenderTargetRect(outputSize, viewport.viewport(), &x, &y, &w, &h)) + if (!qrhi_toTopLeftRenderTargetRect<UnBounded>(outputSize, viewport.viewport(), &x, &y, &w, &h)) return; QVkCommandBuffer::Command &cmd(cbD->commands.get()); @@ -4929,9 +5440,12 @@ void QRhiVulkan::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport cmd.cmd = QVkCommandBuffer::Command::SetViewport; } - if (!QRHI_RES(QVkGraphicsPipeline, cbD->currentGraphicsPipeline)->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor)) { + if (cbD->currentGraphicsPipeline + && !QRHI_RES(QVkGraphicsPipeline, cbD->currentGraphicsPipeline) + ->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor)) { QVkCommandBuffer::Command &cmd(cbD->commands.get()); VkRect2D *s = &cmd.args.setScissor.scissor; + qrhi_toTopLeftRenderTargetRect<Bounded>(outputSize, viewport.viewport(), &x, &y, &w, &h); s->offset.x = int32_t(x); s->offset.y = int32_t(y); s->extent.width = uint32_t(w); @@ -4954,7 +5468,7 @@ void QRhiVulkan::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) // x,y is top-left in VkRect2D but bottom-left in QRhiScissor int x, y, w, h; - if (!qrhi_toTopLeftRenderTargetRect(outputSize, scissor.scissor(), &x, &y, &w, &h)) + if (!qrhi_toTopLeftRenderTargetRect<Bounded>(outputSize, scissor.scissor(), &x, &y, &w, &h)) return; QVkCommandBuffer::Command &cmd(cbD->commands.get()); @@ -5044,60 +5558,72 @@ void QRhiVulkan::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount, void QRhiVulkan::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) { - if (!debugMarkers || !caps.debugMarkers) +#ifdef VK_EXT_debug_utils + if (!debugMarkers || !caps.debugUtils) return; - VkDebugMarkerMarkerInfoEXT marker; - memset(&marker, 0, sizeof(marker)); - marker.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT; + VkDebugUtilsLabelEXT label = {}; + label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT; QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); if (cbD->recordingPass != QVkCommandBuffer::NoPass && cbD->passUsesSecondaryCb) { - marker.pMarkerName = name.constData(); - vkCmdDebugMarkerBegin(cbD->activeSecondaryCbStack.last(), &marker); + label.pLabelName = name.constData(); + vkCmdBeginDebugUtilsLabelEXT(cbD->activeSecondaryCbStack.last(), &label); } else { QVkCommandBuffer::Command &cmd(cbD->commands.get()); cmd.cmd = QVkCommandBuffer::Command::DebugMarkerBegin; - cmd.args.debugMarkerBegin.marker = marker; - cmd.args.debugMarkerBegin.markerNameIndex = cbD->pools.debugMarkerData.count(); + cmd.args.debugMarkerBegin.label = label; + cmd.args.debugMarkerBegin.labelNameIndex = cbD->pools.debugMarkerData.size(); cbD->pools.debugMarkerData.append(name); } +#else + Q_UNUSED(cb); + Q_UNUSED(name); +#endif } void QRhiVulkan::debugMarkEnd(QRhiCommandBuffer *cb) { - if (!debugMarkers || !caps.debugMarkers) +#ifdef VK_EXT_debug_utils + if (!debugMarkers || !caps.debugUtils) return; QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); if (cbD->recordingPass != QVkCommandBuffer::NoPass && cbD->passUsesSecondaryCb) { - vkCmdDebugMarkerEnd(cbD->activeSecondaryCbStack.last()); + vkCmdEndDebugUtilsLabelEXT(cbD->activeSecondaryCbStack.last()); } else { QVkCommandBuffer::Command &cmd(cbD->commands.get()); cmd.cmd = QVkCommandBuffer::Command::DebugMarkerEnd; } +#else + Q_UNUSED(cb); +#endif } void QRhiVulkan::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) { - if (!debugMarkers || !caps.debugMarkers) +#ifdef VK_EXT_debug_utils + if (!debugMarkers || !caps.debugUtils) return; - VkDebugMarkerMarkerInfoEXT marker; - memset(&marker, 0, sizeof(marker)); - marker.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT; + VkDebugUtilsLabelEXT label = {}; + label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT; QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); if (cbD->recordingPass != QVkCommandBuffer::NoPass && cbD->passUsesSecondaryCb) { - marker.pMarkerName = msg.constData(); - vkCmdDebugMarkerInsert(cbD->activeSecondaryCbStack.last(), &marker); + label.pLabelName = msg.constData(); + vkCmdInsertDebugUtilsLabelEXT(cbD->activeSecondaryCbStack.last(), &label); } else { QVkCommandBuffer::Command &cmd(cbD->commands.get()); cmd.cmd = QVkCommandBuffer::Command::DebugMarkerInsert; - cmd.args.debugMarkerInsert.marker = marker; - cmd.args.debugMarkerInsert.markerNameIndex = cbD->pools.debugMarkerData.count(); + cmd.args.debugMarkerInsert.label = label; + cmd.args.debugMarkerInsert.labelNameIndex = cbD->pools.debugMarkerData.size(); cbD->pools.debugMarkerData.append(msg); } +#else + Q_UNUSED(cb); + Q_UNUSED(msg); +#endif } const QRhiNativeHandles *QRhiVulkan::nativeHandles(QRhiCommandBuffer *cb) @@ -5111,8 +5637,8 @@ static inline QVkRenderTargetData *maybeRenderTargetData(QVkCommandBuffer *cbD) QVkRenderTargetData *rtD = nullptr; if (cbD->recordingPass == QVkCommandBuffer::RenderPass) { switch (cbD->currentTarget->resourceType()) { - case QRhiResource::RenderTarget: - rtD = &QRHI_RES(QVkReferenceRenderTarget, cbD->currentTarget)->d; + case QRhiResource::SwapChainRenderTarget: + rtD = &QRHI_RES(QVkSwapChainRenderTarget, cbD->currentTarget)->d; break; case QRhiResource::TextureRenderTarget: rtD = &QRHI_RES(QVkTextureRenderTarget, cbD->currentTarget)->d; @@ -5178,23 +5704,35 @@ void QRhiVulkan::endExternal(QRhiCommandBuffer *cb) cbD->resetCachedState(); } -void QRhiVulkan::setObjectName(uint64_t object, VkDebugReportObjectTypeEXT type, const QByteArray &name, int slot) +double QRhiVulkan::lastCompletedGpuTime(QRhiCommandBuffer *cb) +{ + QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); + return cbD->lastGpuTime; +} + +void QRhiVulkan::setObjectName(uint64_t object, VkObjectType type, const QByteArray &name, int slot) { - if (!debugMarkers || !caps.debugMarkers || name.isEmpty()) +#ifdef VK_EXT_debug_utils + if (!debugMarkers || !caps.debugUtils || name.isEmpty()) return; - VkDebugMarkerObjectNameInfoEXT nameInfo; - memset(&nameInfo, 0, sizeof(nameInfo)); - nameInfo.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_NAME_INFO_EXT; + VkDebugUtilsObjectNameInfoEXT nameInfo = {}; + nameInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT; nameInfo.objectType = type; - nameInfo.object = object; + nameInfo.objectHandle = object; QByteArray decoratedName = name; if (slot >= 0) { decoratedName += '/'; decoratedName += QByteArray::number(slot); } nameInfo.pObjectName = decoratedName.constData(); - vkDebugMarkerSetObjectName(dev, &nameInfo); + vkSetDebugUtilsObjectNameEXT(dev, &nameInfo); +#else + Q_UNUSED(object); + Q_UNUSED(type); + Q_UNUSED(name); + Q_UNUSED(slot); +#endif } static inline VkBufferUsageFlagBits toVkBufferUsage(QRhiBuffer::UsageFlags usage) @@ -5219,8 +5757,7 @@ static inline VkFilter toVkFilter(QRhiSampler::Filter f) case QRhiSampler::Linear: return VK_FILTER_LINEAR; default: - Q_UNREACHABLE(); - return VK_FILTER_NEAREST; + Q_UNREACHABLE_RETURN(VK_FILTER_NEAREST); } } @@ -5234,8 +5771,7 @@ static inline VkSamplerMipmapMode toVkMipmapMode(QRhiSampler::Filter f) case QRhiSampler::Linear: return VK_SAMPLER_MIPMAP_MODE_LINEAR; default: - Q_UNREACHABLE(); - return VK_SAMPLER_MIPMAP_MODE_NEAREST; + Q_UNREACHABLE_RETURN(VK_SAMPLER_MIPMAP_MODE_NEAREST); } } @@ -5249,8 +5785,7 @@ static inline VkSamplerAddressMode toVkAddressMode(QRhiSampler::AddressMode m) case QRhiSampler::Mirror: return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT; default: - Q_UNREACHABLE(); - return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + Q_UNREACHABLE_RETURN(VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE); } } @@ -5267,9 +5802,10 @@ static inline VkShaderStageFlagBits toVkShaderStage(QRhiShaderStage::Type type) return VK_SHADER_STAGE_FRAGMENT_BIT; case QRhiShaderStage::Compute: return VK_SHADER_STAGE_COMPUTE_BIT; + case QRhiShaderStage::Geometry: + return VK_SHADER_STAGE_GEOMETRY_BIT; default: - Q_UNREACHABLE(); - return VK_SHADER_STAGE_VERTEX_BIT; + Q_UNREACHABLE_RETURN(VK_SHADER_STAGE_VERTEX_BIT); } } @@ -5306,9 +5842,32 @@ static inline VkFormat toVkAttributeFormat(QRhiVertexInputAttribute::Format form return VK_FORMAT_R32G32_SINT; case QRhiVertexInputAttribute::SInt: return VK_FORMAT_R32_SINT; + case QRhiVertexInputAttribute::Half4: + return VK_FORMAT_R16G16B16A16_SFLOAT; + case QRhiVertexInputAttribute::Half3: + return VK_FORMAT_R16G16B16_SFLOAT; + case QRhiVertexInputAttribute::Half2: + return VK_FORMAT_R16G16_SFLOAT; + case QRhiVertexInputAttribute::Half: + return VK_FORMAT_R16_SFLOAT; + case QRhiVertexInputAttribute::UShort4: + return VK_FORMAT_R16G16B16A16_UINT; + case QRhiVertexInputAttribute::UShort3: + return VK_FORMAT_R16G16B16_UINT; + case QRhiVertexInputAttribute::UShort2: + return VK_FORMAT_R16G16_UINT; + case QRhiVertexInputAttribute::UShort: + return VK_FORMAT_R16_UINT; + case QRhiVertexInputAttribute::SShort4: + return VK_FORMAT_R16G16B16A16_SINT; + case QRhiVertexInputAttribute::SShort3: + return VK_FORMAT_R16G16B16_SINT; + case QRhiVertexInputAttribute::SShort2: + return VK_FORMAT_R16G16_SINT; + case QRhiVertexInputAttribute::SShort: + return VK_FORMAT_R16_SINT; default: - Q_UNREACHABLE(); - return VK_FORMAT_R32G32B32A32_SFLOAT; + Q_UNREACHABLE_RETURN(VK_FORMAT_R32G32B32A32_SFLOAT); } } @@ -5330,8 +5889,7 @@ static inline VkPrimitiveTopology toVkTopology(QRhiGraphicsPipeline::Topology t) case QRhiGraphicsPipeline::Patches: return VK_PRIMITIVE_TOPOLOGY_PATCH_LIST; default: - Q_UNREACHABLE(); - return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + Q_UNREACHABLE_RETURN(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST); } } @@ -5345,8 +5903,7 @@ static inline VkCullModeFlags toVkCullMode(QRhiGraphicsPipeline::CullMode c) case QRhiGraphicsPipeline::Back: return VK_CULL_MODE_BACK_BIT; default: - Q_UNREACHABLE(); - return VK_CULL_MODE_NONE; + Q_UNREACHABLE_RETURN(VK_CULL_MODE_NONE); } } @@ -5358,8 +5915,7 @@ static inline VkFrontFace toVkFrontFace(QRhiGraphicsPipeline::FrontFace f) case QRhiGraphicsPipeline::CW: return VK_FRONT_FACE_CLOCKWISE; default: - Q_UNREACHABLE(); - return VK_FRONT_FACE_COUNTER_CLOCKWISE; + Q_UNREACHABLE_RETURN(VK_FRONT_FACE_COUNTER_CLOCKWISE); } } @@ -5419,8 +5975,7 @@ static inline VkBlendFactor toVkBlendFactor(QRhiGraphicsPipeline::BlendFactor f) case QRhiGraphicsPipeline::OneMinusSrc1Alpha: return VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA; default: - Q_UNREACHABLE(); - return VK_BLEND_FACTOR_ZERO; + Q_UNREACHABLE_RETURN(VK_BLEND_FACTOR_ZERO); } } @@ -5438,8 +5993,7 @@ static inline VkBlendOp toVkBlendOp(QRhiGraphicsPipeline::BlendOp op) case QRhiGraphicsPipeline::Max: return VK_BLEND_OP_MAX; default: - Q_UNREACHABLE(); - return VK_BLEND_OP_ADD; + Q_UNREACHABLE_RETURN(VK_BLEND_OP_ADD); } } @@ -5463,8 +6017,7 @@ static inline VkCompareOp toVkCompareOp(QRhiGraphicsPipeline::CompareOp op) case QRhiGraphicsPipeline::Always: return VK_COMPARE_OP_ALWAYS; default: - Q_UNREACHABLE(); - return VK_COMPARE_OP_ALWAYS; + Q_UNREACHABLE_RETURN(VK_COMPARE_OP_ALWAYS); } } @@ -5488,8 +6041,19 @@ static inline VkStencilOp toVkStencilOp(QRhiGraphicsPipeline::StencilOp op) case QRhiGraphicsPipeline::DecrementAndWrap: return VK_STENCIL_OP_DECREMENT_AND_WRAP; default: - Q_UNREACHABLE(); - return VK_STENCIL_OP_KEEP; + Q_UNREACHABLE_RETURN(VK_STENCIL_OP_KEEP); + } +} + +static inline VkPolygonMode toVkPolygonMode(QRhiGraphicsPipeline::PolygonMode mode) +{ + switch (mode) { + case QRhiGraphicsPipeline::Fill: + return VK_POLYGON_MODE_FILL; + case QRhiGraphicsPipeline::Line: + return VK_POLYGON_MODE_LINE; + default: + Q_UNREACHABLE_RETURN(VK_POLYGON_MODE_FILL); } } @@ -5528,8 +6092,7 @@ static inline VkDescriptorType toVkDescriptorType(const QRhiShaderResourceBindin return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; default: - Q_UNREACHABLE(); - return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + Q_UNREACHABLE_RETURN(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER); } } @@ -5546,6 +6109,8 @@ static inline VkShaderStageFlags toVkShaderStageFlags(QRhiShaderResourceBinding: s |= VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT; if (stage.testFlag(QRhiShaderResourceBinding::TessellationEvaluationStage)) s |= VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT; + if (stage.testFlag(QRhiShaderResourceBinding::GeometryStage)) + s |= VK_SHADER_STAGE_GEOMETRY_BIT; return VkShaderStageFlags(s); } @@ -5569,12 +6134,11 @@ static inline VkCompareOp toVkTextureCompareOp(QRhiSampler::CompareOp op) case QRhiSampler::Always: return VK_COMPARE_OP_ALWAYS; default: - Q_UNREACHABLE(); - return VK_COMPARE_OP_NEVER; + Q_UNREACHABLE_RETURN(VK_COMPARE_OP_NEVER); } } -QVkBuffer::QVkBuffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size) +QVkBuffer::QVkBuffer(QRhiImplementation *rhi, Type type, UsageFlags usage, quint32 size) : QRhiBuffer(rhi, type, usage, size) { for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) { @@ -5630,16 +6194,14 @@ bool QVkBuffer::create() return false; } - const int nonZeroSize = m_size <= 0 ? 256 : m_size; + const quint32 nonZeroSize = m_size <= 0 ? 256 : m_size; - VkBufferCreateInfo bufferInfo; - memset(&bufferInfo, 0, sizeof(bufferInfo)); + VkBufferCreateInfo bufferInfo = {}; bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; - bufferInfo.size = uint32_t(nonZeroSize); + bufferInfo.size = nonZeroSize; bufferInfo.usage = toVkBufferUsage(m_usage); - VmaAllocationCreateInfo allocInfo; - memset(&allocInfo, 0, sizeof(allocInfo)); + VmaAllocationCreateInfo allocInfo = {}; if (m_type == Dynamic) { #ifndef Q_OS_DARWIN // not for MoltenVK @@ -5667,13 +6229,14 @@ bool QVkBuffer::create() if (err != VK_SUCCESS) break; allocations[i] = allocation; - rhiD->setObjectName(uint64_t(buffers[i]), VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT, m_objectName, + rhiD->setObjectName(uint64_t(buffers[i]), VK_OBJECT_TYPE_BUFFER, m_objectName, m_type == Dynamic ? i : -1); } } if (err != VK_SUCCESS) { - qWarning("Failed to create buffer: %d", err); + qWarning("Failed to create buffer of size %u: %d", nonZeroSize, err); + rhiD->printExtraErrorInfo(err); return false; } @@ -5725,8 +6288,8 @@ void QVkBuffer::endFullDynamicBufferUpdateForCurrentFrame() QRHI_RES_RHI(QRhiVulkan); const int slot = rhiD->currentFrameSlot; VmaAllocation a = toVmaAllocation(allocations[slot]); - vmaUnmapMemory(toVmaAllocator(rhiD->allocator), a); vmaFlushAllocation(toVmaAllocator(rhiD->allocator), a, 0, m_size); + vmaUnmapMemory(toVmaAllocator(rhiD->allocator), a); } QVkRenderBuffer::QVkRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize, @@ -5781,7 +6344,7 @@ bool QVkRenderBuffer::create() return false; QRHI_RES_RHI(QRhiVulkan); - samples = rhiD->effectiveSampleCount(m_sampleCount); + samples = rhiD->effectiveSampleCountBits(m_sampleCount); switch (m_type) { case QRhiRenderBuffer::Color: @@ -5817,7 +6380,7 @@ bool QVkRenderBuffer::create() { return false; } - rhiD->setObjectName(uint64_t(image), VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT, m_objectName); + rhiD->setObjectName(uint64_t(image), VK_OBJECT_TYPE_IMAGE, m_objectName); break; default: Q_UNREACHABLE(); @@ -5899,6 +6462,15 @@ bool QVkTexture::prepareCreate(QSize *adjustedSize) QRHI_RES_RHI(QRhiVulkan); vkformat = toVkTextureFormat(m_format, m_flags); + if (m_writeViewFormat.format != UnknownFormat) + viewFormat = toVkTextureFormat(m_writeViewFormat.format, m_writeViewFormat.srgb ? sRGB : Flags()); + else + viewFormat = vkformat; + if (m_readViewFormat.format != UnknownFormat) + viewFormatForSampling = toVkTextureFormat(m_readViewFormat.format, m_readViewFormat.srgb ? sRGB : Flags()); + else + viewFormatForSampling = vkformat; + VkFormatProperties props; rhiD->f->vkGetPhysicalDeviceFormatProperties(rhiD->physDev, vkformat, &props); const bool canSampleOptimal = (props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT); @@ -5907,19 +6479,22 @@ bool QVkTexture::prepareCreate(QSize *adjustedSize) return false; } - const QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize; const bool isCube = m_flags.testFlag(CubeMap); const bool isArray = m_flags.testFlag(TextureArray); const bool is3D = m_flags.testFlag(ThreeDimensional); + const bool is1D = m_flags.testFlag(OneDimensional); const bool hasMipMaps = m_flags.testFlag(MipMapped); + const QSize size = is1D ? QSize(qMax(1, m_pixelSize.width()), 1) + : (m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize); + mipLevelCount = uint(hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1); const int maxLevels = QRhi::MAX_MIP_LEVELS; if (mipLevelCount > maxLevels) { qWarning("Too many mip levels (%d, max is %d), truncating mip chain", mipLevelCount, maxLevels); mipLevelCount = maxLevels; } - samples = rhiD->effectiveSampleCount(m_sampleCount); + samples = rhiD->effectiveSampleCountBits(m_sampleCount); if (samples > VK_SAMPLE_COUNT_1_BIT) { if (isCube) { qWarning("Cubemap texture cannot be multisample"); @@ -5942,12 +6517,18 @@ bool QVkTexture::prepareCreate(QSize *adjustedSize) qWarning("Texture cannot be both array and 3D"); return false; } - m_depth = qMax(1, m_depth); + if (isCube && is1D) { + qWarning("Texture cannot be both cube and 1D"); + return false; + } + if (is1D && is3D) { + qWarning("Texture cannot be both 1D and 3D"); + return false; + } if (m_depth > 1 && !is3D) { qWarning("Texture cannot have a depth of %d when it is not 3D", m_depth); return false; } - m_arraySize = qMax(0, m_arraySize); if (m_arraySize > 0 && !isArray) { qWarning("Texture cannot have an array size of %d when it is not an array", m_arraySize); return false; @@ -5975,22 +6556,29 @@ bool QVkTexture::finishCreate() const bool isCube = m_flags.testFlag(CubeMap); const bool isArray = m_flags.testFlag(TextureArray); const bool is3D = m_flags.testFlag(ThreeDimensional); + const bool is1D = m_flags.testFlag(OneDimensional); - VkImageViewCreateInfo viewInfo; - memset(&viewInfo, 0, sizeof(viewInfo)); + VkImageViewCreateInfo viewInfo = {}; viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; viewInfo.image = image; - viewInfo.viewType = isCube ? VK_IMAGE_VIEW_TYPE_CUBE - : (is3D ? VK_IMAGE_VIEW_TYPE_3D - : (isArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D)); - viewInfo.format = vkformat; + viewInfo.viewType = isCube + ? VK_IMAGE_VIEW_TYPE_CUBE + : (is3D ? VK_IMAGE_VIEW_TYPE_3D + : (is1D ? (isArray ? VK_IMAGE_VIEW_TYPE_1D_ARRAY : VK_IMAGE_VIEW_TYPE_1D) + : (isArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D))); + viewInfo.format = viewFormatForSampling; viewInfo.components.r = VK_COMPONENT_SWIZZLE_R; viewInfo.components.g = VK_COMPONENT_SWIZZLE_G; viewInfo.components.b = VK_COMPONENT_SWIZZLE_B; viewInfo.components.a = VK_COMPONENT_SWIZZLE_A; viewInfo.subresourceRange.aspectMask = aspectMask; viewInfo.subresourceRange.levelCount = mipLevelCount; - viewInfo.subresourceRange.layerCount = isCube ? 6 : (isArray ? m_arraySize : 1); + if (isArray && m_arrayRangeStart >= 0 && m_arrayRangeLength >= 0) { + viewInfo.subresourceRange.baseArrayLayer = uint32_t(m_arrayRangeStart); + viewInfo.subresourceRange.layerCount = uint32_t(m_arrayRangeLength); + } else { + viewInfo.subresourceRange.layerCount = isCube ? 6 : (isArray ? qMax(0, m_arraySize) : 1); + } VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &imageView); if (err != VK_SUCCESS) { @@ -6016,9 +6604,9 @@ bool QVkTexture::create() const bool isCube = m_flags.testFlag(CubeMap); const bool isArray = m_flags.testFlag(TextureArray); const bool is3D = m_flags.testFlag(ThreeDimensional); + const bool is1D = m_flags.testFlag(OneDimensional); - VkImageCreateInfo imageInfo; - memset(&imageInfo, 0, sizeof(imageInfo)); + VkImageCreateInfo imageInfo = {}; imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; imageInfo.flags = 0; if (isCube) @@ -6039,13 +6627,13 @@ bool QVkTexture::create() #endif } - imageInfo.imageType = is3D ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D; + imageInfo.imageType = is1D ? VK_IMAGE_TYPE_1D : is3D ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D; imageInfo.format = vkformat; imageInfo.extent.width = uint32_t(size.width()); imageInfo.extent.height = uint32_t(size.height()); - imageInfo.extent.depth = is3D ? m_depth : 1; + imageInfo.extent.depth = is3D ? qMax(1, m_depth) : 1; imageInfo.mipLevels = mipLevelCount; - imageInfo.arrayLayers = isCube ? 6 : (isArray ? m_arraySize : 1); + imageInfo.arrayLayers = isCube ? 6 : (isArray ? qMax(0, m_arraySize) : 1); imageInfo.samples = samples; imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; @@ -6064,14 +6652,20 @@ bool QVkTexture::create() if (m_flags.testFlag(QRhiTexture::UsedWithLoadStore)) imageInfo.usage |= VK_IMAGE_USAGE_STORAGE_BIT; - VmaAllocationCreateInfo allocInfo; - memset(&allocInfo, 0, sizeof(allocInfo)); + VmaAllocationCreateInfo allocInfo = {}; allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; VmaAllocation allocation; VkResult err = vmaCreateImage(toVmaAllocator(rhiD->allocator), &imageInfo, &allocInfo, &image, &allocation, nullptr); if (err != VK_SUCCESS) { - qWarning("Failed to create image: %d", err); + qWarning("Failed to create image (with VkImageCreateInfo %ux%u depth %u vkformat 0x%X mips %u layers %u vksamples 0x%X): %d", + imageInfo.extent.width, imageInfo.extent.height, imageInfo.extent.depth, + int(imageInfo.format), + imageInfo.mipLevels, + imageInfo.arrayLayers, + int(imageInfo.samples), + err); + rhiD->printExtraErrorInfo(err); return false; } imageAlloc = allocation; @@ -6079,7 +6673,7 @@ bool QVkTexture::create() if (!finishCreate()) return false; - rhiD->setObjectName(uint64_t(image), VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT, m_objectName); + rhiD->setObjectName(uint64_t(image), VK_OBJECT_TYPE_IMAGE, m_objectName); owns = true; rhiD->registerResource(this); @@ -6118,7 +6712,7 @@ void QVkTexture::setNativeLayout(int layout) usageState.layout = VkImageLayout(layout); } -VkImageView QVkTexture::imageViewForLevel(int level) +VkImageView QVkTexture::perLevelImageViewForLoadStore(int level) { Q_ASSERT(level >= 0 && level < int(mipLevelCount)); if (perLevelImageViews[level] != VK_NULL_HANDLE) @@ -6128,15 +6722,17 @@ VkImageView QVkTexture::imageViewForLevel(int level) const bool isCube = m_flags.testFlag(CubeMap); const bool isArray = m_flags.testFlag(TextureArray); const bool is3D = m_flags.testFlag(ThreeDimensional); + const bool is1D = m_flags.testFlag(OneDimensional); - VkImageViewCreateInfo viewInfo; - memset(&viewInfo, 0, sizeof(viewInfo)); + VkImageViewCreateInfo viewInfo = {}; viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; viewInfo.image = image; - viewInfo.viewType = isCube ? VK_IMAGE_VIEW_TYPE_CUBE - : (is3D ? VK_IMAGE_VIEW_TYPE_3D - : (isArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D)); - viewInfo.format = vkformat; + viewInfo.viewType = isCube + ? VK_IMAGE_VIEW_TYPE_CUBE + : (is3D ? VK_IMAGE_VIEW_TYPE_3D + : (is1D ? (isArray ? VK_IMAGE_VIEW_TYPE_1D_ARRAY : VK_IMAGE_VIEW_TYPE_1D) + : (isArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D))); + viewInfo.format = viewFormat; // this is writeViewFormat, regardless of Load, Store, or LoadStore; intentional viewInfo.components.r = VK_COMPONENT_SWIZZLE_R; viewInfo.components.g = VK_COMPONENT_SWIZZLE_G; viewInfo.components.b = VK_COMPONENT_SWIZZLE_B; @@ -6145,7 +6741,7 @@ VkImageView QVkTexture::imageViewForLevel(int level) viewInfo.subresourceRange.baseMipLevel = uint32_t(level); viewInfo.subresourceRange.levelCount = 1; viewInfo.subresourceRange.baseArrayLayer = 0; - viewInfo.subresourceRange.layerCount = isCube ? 6 : (isArray ? m_arraySize : 1); + viewInfo.subresourceRange.layerCount = isCube ? 6 : (isArray ? qMax(0, m_arraySize) : 1); VkImageView v = VK_NULL_HANDLE; QRHI_RES_RHI(QRhiVulkan); @@ -6194,8 +6790,7 @@ bool QVkSampler::create() if (sampler) destroy(); - VkSamplerCreateInfo samplerInfo; - memset(&samplerInfo, 0, sizeof(samplerInfo)); + VkSamplerCreateInfo samplerInfo = {}; samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; samplerInfo.magFilter = toVkFilter(m_magFilter); samplerInfo.minFilter = toVkFilter(m_minFilter); @@ -6224,7 +6819,7 @@ bool QVkSampler::create() QVkRenderPassDescriptor::QVkRenderPassDescriptor(QRhiImplementation *rhi) : QRhiRenderPassDescriptor(rhi) { - serializedFormatData.reserve(32); + serializedFormatData.reserve(64); } QVkRenderPassDescriptor::~QVkRenderPassDescriptor() @@ -6279,16 +6874,20 @@ bool QVkRenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other const QVkRenderPassDescriptor *o = QRHI_RES(const QVkRenderPassDescriptor, other); - if (attDescs.count() != o->attDescs.count()) + if (attDescs.size() != o->attDescs.size()) return false; - if (colorRefs.count() != o->colorRefs.count()) + if (colorRefs.size() != o->colorRefs.size()) return false; - if (resolveRefs.count() != o->resolveRefs.count()) + if (resolveRefs.size() != o->resolveRefs.size()) return false; if (hasDepthStencil != o->hasDepthStencil) return false; + if (hasDepthStencilResolve != o->hasDepthStencilResolve) + return false; + if (multiViewCount != o->multiViewCount) + return false; - for (int i = 0, ie = colorRefs.count(); i != ie; ++i) { + for (int i = 0, ie = colorRefs.size(); i != ie; ++i) { const uint32_t attIdx = colorRefs[i].attachment; if (attIdx != o->colorRefs[i].attachment) return false; @@ -6304,7 +6903,7 @@ bool QVkRenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other return false; } - for (int i = 0, ie = resolveRefs.count(); i != ie; ++i) { + for (int i = 0, ie = resolveRefs.size(); i != ie; ++i) { const uint32_t attIdx = resolveRefs[i].attachment; if (attIdx != o->resolveRefs[i].attachment) return false; @@ -6312,6 +6911,14 @@ bool QVkRenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other return false; } + if (hasDepthStencilResolve) { + const uint32_t attIdx = dsResolveRef.attachment; + if (attIdx != o->dsResolveRef.attachment) + return false; + if (attIdx != VK_ATTACHMENT_UNUSED && !attachmentDescriptionEquals(attDescs[attIdx], o->attDescs[attIdx])) + return false; + } + // subpassDeps is not included return true; @@ -6322,10 +6929,12 @@ void QVkRenderPassDescriptor::updateSerializedFormat() serializedFormatData.clear(); auto p = std::back_inserter(serializedFormatData); - *p++ = attDescs.count(); - *p++ = colorRefs.count(); - *p++ = resolveRefs.count(); + *p++ = attDescs.size(); + *p++ = colorRefs.size(); + *p++ = resolveRefs.size(); *p++ = hasDepthStencil; + *p++ = hasDepthStencilResolve; + *p++ = multiViewCount; auto serializeAttachmentData = [this, &p](uint32_t attIdx) { const bool used = attIdx != VK_ATTACHMENT_UNUSED; @@ -6340,7 +6949,7 @@ void QVkRenderPassDescriptor::updateSerializedFormat() *p++ = used ? a->finalLayout : 0; }; - for (int i = 0, ie = colorRefs.count(); i != ie; ++i) { + for (int i = 0, ie = colorRefs.size(); i != ie; ++i) { const uint32_t attIdx = colorRefs[i].attachment; *p++ = attIdx; serializeAttachmentData(attIdx); @@ -6352,11 +6961,17 @@ void QVkRenderPassDescriptor::updateSerializedFormat() serializeAttachmentData(attIdx); } - for (int i = 0, ie = resolveRefs.count(); i != ie; ++i) { + for (int i = 0, ie = resolveRefs.size(); i != ie; ++i) { const uint32_t attIdx = resolveRefs[i].attachment; *p++ = attIdx; serializeAttachmentData(attIdx); } + + if (hasDepthStencilResolve) { + const uint32_t attIdx = dsResolveRef.attachment; + *p++ = attIdx; + serializeAttachmentData(attIdx); + } } QRhiRenderPassDescriptor *QVkRenderPassDescriptor::newCompatibleRenderPassDescriptor() const @@ -6369,13 +6984,22 @@ QRhiRenderPassDescriptor *QVkRenderPassDescriptor::newCompatibleRenderPassDescri rpD->resolveRefs = resolveRefs; rpD->subpassDeps = subpassDeps; rpD->hasDepthStencil = hasDepthStencil; + rpD->hasDepthStencilResolve = hasDepthStencilResolve; + rpD->multiViewCount = multiViewCount; rpD->dsRef = dsRef; + rpD->dsResolveRef = dsResolveRef; VkRenderPassCreateInfo rpInfo; VkSubpassDescription subpassDesc; fillRenderPassCreateInfo(&rpInfo, &subpassDesc, rpD); QRHI_RES_RHI(QRhiVulkan); + MultiViewRenderPassSetupHelper multiViewHelper; + if (!multiViewHelper.prepare(&rpInfo, multiViewCount, rhiD->caps.multiView)) { + delete rpD; + return nullptr; + } + VkResult err = rhiD->df->vkCreateRenderPass(rhiD->dev, &rpInfo, nullptr, &rpD->rp); if (err != VK_SUCCESS) { qWarning("Failed to create renderpass: %d", err); @@ -6399,32 +7023,32 @@ const QRhiNativeHandles *QVkRenderPassDescriptor::nativeHandles() return &nativeHandlesStruct; } -QVkReferenceRenderTarget::QVkReferenceRenderTarget(QRhiImplementation *rhi) - : QRhiRenderTarget(rhi) +QVkSwapChainRenderTarget::QVkSwapChainRenderTarget(QRhiImplementation *rhi, QRhiSwapChain *swapchain) + : QRhiSwapChainRenderTarget(rhi, swapchain) { } -QVkReferenceRenderTarget::~QVkReferenceRenderTarget() +QVkSwapChainRenderTarget::~QVkSwapChainRenderTarget() { destroy(); } -void QVkReferenceRenderTarget::destroy() +void QVkSwapChainRenderTarget::destroy() { // nothing to do here } -QSize QVkReferenceRenderTarget::pixelSize() const +QSize QVkSwapChainRenderTarget::pixelSize() const { return d.pixelSize; } -float QVkReferenceRenderTarget::devicePixelRatio() const +float QVkSwapChainRenderTarget::devicePixelRatio() const { return d.dpr; } -int QVkReferenceRenderTarget::sampleCount() const +int QVkSwapChainRenderTarget::sampleCount() const { return d.sampleCount; } @@ -6464,6 +7088,11 @@ void QVkTextureRenderTarget::destroy() resrtv[att] = VK_NULL_HANDLE; } + e.textureRenderTarget.dsv = dsv; + dsv = VK_NULL_HANDLE; + e.textureRenderTarget.resdsv = resdsv; + resdsv = VK_NULL_HANDLE; + QRHI_RES_RHI(QRhiVulkan); if (rhiD) { rhiD->releaseQueue.append(e); @@ -6482,8 +7111,10 @@ QRhiRenderPassDescriptor *QVkTextureRenderTarget::newCompatibleRenderPassDescrip m_desc.cendColorAttachments(), m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents), m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents), + m_desc.depthTexture() && !m_flags.testFlag(DoNotStoreDepthStencilContents) && !m_desc.depthResolveTexture(), m_desc.depthStencilBuffer(), - m_desc.depthTexture())) + m_desc.depthTexture(), + m_desc.depthResolveTexture())) { delete rp; return nullptr; @@ -6500,13 +7131,13 @@ bool QVkTextureRenderTarget::create() if (d.fb) destroy(); - const bool hasColorAttachments = m_desc.cbeginColorAttachments() != m_desc.cendColorAttachments(); - Q_ASSERT(hasColorAttachments || m_desc.depthTexture()); + Q_ASSERT(m_desc.colorAttachmentCount() > 0 || m_desc.depthTexture()); Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture()); const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture(); QRHI_RES_RHI(QRhiVulkan); QVarLengthArray<VkImageView, 8> views; + d.multiViewCount = 0; d.colorAttCount = 0; int attIndex = 0; @@ -6517,12 +7148,17 @@ bool QVkTextureRenderTarget::create() Q_ASSERT(texD || rbD); if (texD) { Q_ASSERT(texD->flags().testFlag(QRhiTexture::RenderTarget)); - VkImageViewCreateInfo viewInfo; - memset(&viewInfo, 0, sizeof(viewInfo)); + const bool is1D = texD->flags().testFlag(QRhiTexture::OneDimensional); + const bool isMultiView = it->multiViewCount() >= 2; + if (isMultiView && d.multiViewCount == 0) + d.multiViewCount = it->multiViewCount(); + VkImageViewCreateInfo viewInfo = {}; viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; viewInfo.image = texD->image; - viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - viewInfo.format = texD->vkformat; + viewInfo.viewType = is1D ? VK_IMAGE_VIEW_TYPE_1D + : (isMultiView ? VK_IMAGE_VIEW_TYPE_2D_ARRAY + : VK_IMAGE_VIEW_TYPE_2D); + viewInfo.format = texD->viewFormat; viewInfo.components.r = VK_COMPONENT_SWIZZLE_R; viewInfo.components.g = VK_COMPONENT_SWIZZLE_G; viewInfo.components.b = VK_COMPONENT_SWIZZLE_B; @@ -6531,7 +7167,7 @@ bool QVkTextureRenderTarget::create() viewInfo.subresourceRange.baseMipLevel = uint32_t(it->level()); viewInfo.subresourceRange.levelCount = 1; viewInfo.subresourceRange.baseArrayLayer = uint32_t(it->layer()); - viewInfo.subresourceRange.layerCount = 1; + viewInfo.subresourceRange.layerCount = uint32_t(isMultiView ? it->multiViewCount() : 1); VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &rtv[attIndex]); if (err != VK_SUCCESS) { qWarning("Failed to create render target image view: %d", err); @@ -6556,7 +7192,25 @@ bool QVkTextureRenderTarget::create() if (hasDepthStencil) { if (m_desc.depthTexture()) { QVkTexture *depthTexD = QRHI_RES(QVkTexture, m_desc.depthTexture()); - views.append(depthTexD->imageView); + // need a dedicated view just because viewFormat may differ from vkformat + VkImageViewCreateInfo viewInfo = {}; + viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + viewInfo.image = depthTexD->image; + viewInfo.viewType = d.multiViewCount > 1 ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D; + viewInfo.format = depthTexD->viewFormat; + viewInfo.components.r = VK_COMPONENT_SWIZZLE_R; + viewInfo.components.g = VK_COMPONENT_SWIZZLE_G; + viewInfo.components.b = VK_COMPONENT_SWIZZLE_B; + viewInfo.components.a = VK_COMPONENT_SWIZZLE_A; + viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + viewInfo.subresourceRange.levelCount = 1; + viewInfo.subresourceRange.layerCount = qMax<uint32_t>(1, d.multiViewCount); + VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &dsv); + if (err != VK_SUCCESS) { + qWarning("Failed to create depth-stencil image view for rt: %d", err); + return false; + } + views.append(dsv); if (d.colorAttCount == 0) { d.pixelSize = depthTexD->pixelSize(); d.sampleCount = depthTexD->samples; @@ -6576,18 +7230,19 @@ bool QVkTextureRenderTarget::create() d.resolveAttCount = 0; attIndex = 0; + Q_ASSERT(d.multiViewCount == 0 || d.multiViewCount >= 2); for (auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it, ++attIndex) { if (it->resolveTexture()) { QVkTexture *resTexD = QRHI_RES(QVkTexture, it->resolveTexture()); Q_ASSERT(resTexD->flags().testFlag(QRhiTexture::RenderTarget)); d.resolveAttCount += 1; - VkImageViewCreateInfo viewInfo; - memset(&viewInfo, 0, sizeof(viewInfo)); + VkImageViewCreateInfo viewInfo = {}; viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; viewInfo.image = resTexD->image; - viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - viewInfo.format = resTexD->vkformat; + viewInfo.viewType = d.multiViewCount ? VK_IMAGE_VIEW_TYPE_2D_ARRAY + : VK_IMAGE_VIEW_TYPE_2D; + viewInfo.format = resTexD->viewFormat; viewInfo.components.r = VK_COMPONENT_SWIZZLE_R; viewInfo.components.g = VK_COMPONENT_SWIZZLE_G; viewInfo.components.b = VK_COMPONENT_SWIZZLE_B; @@ -6596,7 +7251,7 @@ bool QVkTextureRenderTarget::create() viewInfo.subresourceRange.baseMipLevel = uint32_t(it->resolveLevel()); viewInfo.subresourceRange.levelCount = 1; viewInfo.subresourceRange.baseArrayLayer = uint32_t(it->resolveLayer()); - viewInfo.subresourceRange.layerCount = 1; + viewInfo.subresourceRange.layerCount = qMax<uint32_t>(1, d.multiViewCount); VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &resrtv[attIndex]); if (err != VK_SUCCESS) { qWarning("Failed to create render target resolve image view: %d", err); @@ -6606,17 +7261,46 @@ bool QVkTextureRenderTarget::create() } } + if (m_desc.depthResolveTexture()) { + QVkTexture *resTexD = QRHI_RES(QVkTexture, m_desc.depthResolveTexture()); + Q_ASSERT(resTexD->flags().testFlag(QRhiTexture::RenderTarget)); + + VkImageViewCreateInfo viewInfo = {}; + viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + viewInfo.image = resTexD->image; + viewInfo.viewType = d.multiViewCount ? VK_IMAGE_VIEW_TYPE_2D_ARRAY + : VK_IMAGE_VIEW_TYPE_2D; + viewInfo.format = resTexD->viewFormat; + viewInfo.components.r = VK_COMPONENT_SWIZZLE_R; + viewInfo.components.g = VK_COMPONENT_SWIZZLE_G; + viewInfo.components.b = VK_COMPONENT_SWIZZLE_B; + viewInfo.components.a = VK_COMPONENT_SWIZZLE_A; + viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + viewInfo.subresourceRange.baseMipLevel = 0; + viewInfo.subresourceRange.levelCount = 1; + viewInfo.subresourceRange.baseArrayLayer = 0; + viewInfo.subresourceRange.layerCount = qMax<uint32_t>(1, d.multiViewCount); + VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &resdsv); + if (err != VK_SUCCESS) { + qWarning("Failed to create render target depth resolve image view: %d", err); + return false; + } + views.append(resdsv); + d.dsResolveAttCount = 1; + } else { + d.dsResolveAttCount = 0; + } + if (!m_renderPassDesc) qWarning("QVkTextureRenderTarget: No renderpass descriptor set. See newCompatibleRenderPassDescriptor() and setRenderPassDescriptor()."); d.rp = QRHI_RES(QVkRenderPassDescriptor, m_renderPassDesc); Q_ASSERT(d.rp && d.rp->rp); - VkFramebufferCreateInfo fbInfo; - memset(&fbInfo, 0, sizeof(fbInfo)); + VkFramebufferCreateInfo fbInfo = {}; fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; fbInfo.renderPass = d.rp->rp; - fbInfo.attachmentCount = uint32_t(d.colorAttCount + d.dsAttCount + d.resolveAttCount); + fbInfo.attachmentCount = uint32_t(d.colorAttCount + d.dsAttCount + d.resolveAttCount + d.dsResolveAttCount); fbInfo.pAttachments = views.constData(); fbInfo.width = uint32_t(d.pixelSize.width()); fbInfo.height = uint32_t(d.pixelSize.height()); @@ -6637,6 +7321,9 @@ bool QVkTextureRenderTarget::create() QSize QVkTextureRenderTarget::pixelSize() const { + if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QVkTexture, QVkRenderBuffer>(m_desc, d.currentResIdList)) + const_cast<QVkTextureRenderTarget *>(this)->create(); + return d.pixelSize; } @@ -6702,16 +7389,12 @@ bool QVkShaderResourceBindings::create() sortedBindings.clear(); std::copy(m_bindings.cbegin(), m_bindings.cend(), std::back_inserter(sortedBindings)); - std::sort(sortedBindings.begin(), sortedBindings.end(), - [](const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b) - { - return a.data()->binding < b.data()->binding; - }); + std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan); hasSlottedResource = false; hasDynamicOffset = false; - for (const QRhiShaderResourceBinding &binding : qAsConst(sortedBindings)) { - const QRhiShaderResourceBinding::Data *b = binding.data(); + for (const QRhiShaderResourceBinding &binding : std::as_const(sortedBindings)) { + const QRhiShaderResourceBinding::Data *b = QRhiImplementation::shaderResourceBindingData(binding); if (b->type == QRhiShaderResourceBinding::UniformBuffer && b->u.ubuf.buf) { if (QRHI_RES(QVkBuffer, b->u.ubuf.buf)->type() == QRhiBuffer::Dynamic) hasSlottedResource = true; @@ -6721,10 +7404,9 @@ bool QVkShaderResourceBindings::create() } QVarLengthArray<VkDescriptorSetLayoutBinding, 4> vkbindings; - for (const QRhiShaderResourceBinding &binding : qAsConst(sortedBindings)) { - const QRhiShaderResourceBinding::Data *b = binding.data(); - VkDescriptorSetLayoutBinding vkbinding; - memset(&vkbinding, 0, sizeof(vkbinding)); + for (const QRhiShaderResourceBinding &binding : std::as_const(sortedBindings)) { + const QRhiShaderResourceBinding::Data *b = QRhiImplementation::shaderResourceBindingData(binding); + VkDescriptorSetLayoutBinding vkbinding = {}; vkbinding.binding = uint32_t(b->binding); vkbinding.descriptorType = toVkDescriptorType(b); if (b->type == QRhiShaderResourceBinding::SampledTexture || b->type == QRhiShaderResourceBinding::Texture) @@ -6735,10 +7417,9 @@ bool QVkShaderResourceBindings::create() vkbindings.append(vkbinding); } - VkDescriptorSetLayoutCreateInfo layoutInfo; - memset(&layoutInfo, 0, sizeof(layoutInfo)); + VkDescriptorSetLayoutCreateInfo layoutInfo = {}; layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; - layoutInfo.bindingCount = uint32_t(vkbindings.count()); + layoutInfo.bindingCount = uint32_t(vkbindings.size()); layoutInfo.pBindings = vkbindings.constData(); VkResult err = rhiD->df->vkCreateDescriptorSetLayout(rhiD->dev, &layoutInfo, nullptr, &layout); @@ -6747,8 +7428,7 @@ bool QVkShaderResourceBindings::create() return false; } - VkDescriptorSetAllocateInfo allocInfo; - memset(&allocInfo, 0, sizeof(allocInfo)); + VkDescriptorSetAllocateInfo allocInfo = {}; allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; allocInfo.descriptorSetCount = QVK_FRAMES_IN_FLIGHT; VkDescriptorSetLayout layouts[QVK_FRAMES_IN_FLIGHT]; @@ -6759,7 +7439,7 @@ bool QVkShaderResourceBindings::create() return false; for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) { - boundResourceData[i].resize(sortedBindings.count()); + boundResourceData[i].resize(sortedBindings.size()); for (BoundResourceData &bd : boundResourceData[i]) memset(&bd, 0, sizeof(BoundResourceData)); } @@ -6774,13 +7454,8 @@ void QVkShaderResourceBindings::updateResources(UpdateFlags flags) { sortedBindings.clear(); std::copy(m_bindings.cbegin(), m_bindings.cend(), std::back_inserter(sortedBindings)); - if (!flags.testFlag(BindingsAreSorted)) { - std::sort(sortedBindings.begin(), sortedBindings.end(), - [](const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b) - { - return a.data()->binding < b.data()->binding; - }); - } + if (!flags.testFlag(BindingsAreSorted)) + std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan); // Reset the state tracking table too - it can deal with assigning a // different QRhiBuffer/Texture/Sampler for a binding point, but it cannot @@ -6792,7 +7467,7 @@ void QVkShaderResourceBindings::updateResources(UpdateFlags flags) // complicating the checks in setShaderResources(), reset the table here // just like we do in create(). for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) { - Q_ASSERT(boundResourceData[i].count() == sortedBindings.count()); + Q_ASSERT(boundResourceData[i].size() == sortedBindings.size()); for (BoundResourceData &bd : boundResourceData[i]) memset(&bd, 0, sizeof(BoundResourceData)); } @@ -6838,14 +7513,14 @@ bool QVkGraphicsPipeline::create() destroy(); QRHI_RES_RHI(QRhiVulkan); + rhiD->pipelineCreationStart(); if (!rhiD->sanityCheckGraphicsPipeline(this)) return false; if (!rhiD->ensurePipelineCache()) return false; - VkPipelineLayoutCreateInfo pipelineLayoutInfo; - memset(&pipelineLayoutInfo, 0, sizeof(pipelineLayoutInfo)); + VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pipelineLayoutInfo.setLayoutCount = 1; QVkShaderResourceBindings *srbD = QRHI_RES(QVkShaderResourceBindings, m_shaderResourceBindings); @@ -6857,8 +7532,7 @@ bool QVkGraphicsPipeline::create() return false; } - VkGraphicsPipelineCreateInfo pipelineInfo; - memset(&pipelineInfo, 0, sizeof(pipelineInfo)); + VkGraphicsPipelineCreateInfo pipelineInfo = {}; pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; QVarLengthArray<VkShaderModule, 4> shaders; @@ -6873,8 +7547,7 @@ bool QVkGraphicsPipeline::create() VkShaderModule shader = rhiD->createShader(spirv.shader()); if (shader) { shaders.append(shader); - VkPipelineShaderStageCreateInfo shaderInfo; - memset(&shaderInfo, 0, sizeof(shaderInfo)); + VkPipelineShaderStageCreateInfo shaderInfo = {}; shaderInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; shaderInfo.stage = toVkShaderStage(shaderStage.type()); shaderInfo.module = shader; @@ -6882,11 +7555,13 @@ bool QVkGraphicsPipeline::create() shaderStageCreateInfos.append(shaderInfo); } } - pipelineInfo.stageCount = uint32_t(shaderStageCreateInfos.count()); + pipelineInfo.stageCount = uint32_t(shaderStageCreateInfos.size()); pipelineInfo.pStages = shaderStageCreateInfos.constData(); QVarLengthArray<VkVertexInputBindingDescription, 4> vertexBindings; +#ifdef VK_EXT_vertex_attribute_divisor QVarLengthArray<VkVertexInputBindingDivisorDescriptionEXT> nonOneStepRates; +#endif int bindingIndex = 0; for (auto it = m_vertexInputLayout.cbeginBindings(), itEnd = m_vertexInputLayout.cendBindings(); it != itEnd; ++it, ++bindingIndex) @@ -6898,9 +7573,12 @@ bool QVkGraphicsPipeline::create() ? VK_VERTEX_INPUT_RATE_VERTEX : VK_VERTEX_INPUT_RATE_INSTANCE }; if (it->classification() == QRhiVertexInputBinding::PerInstance && it->instanceStepRate() != 1) { +#ifdef VK_EXT_vertex_attribute_divisor if (rhiD->caps.vertexAttribDivisor) { - nonOneStepRates.append({ uint32_t(bindingIndex), uint32_t(it->instanceStepRate()) }); - } else { + nonOneStepRates.append({ uint32_t(bindingIndex), it->instanceStepRate() }); + } else +#endif + { qWarning("QRhiVulkan: Instance step rates other than 1 not supported without " "VK_EXT_vertex_attribute_divisor on the device and " "VK_KHR_get_physical_device_properties2 on the instance"); @@ -6920,21 +7598,21 @@ bool QVkGraphicsPipeline::create() }; vertexAttributes.append(attributeInfo); } - VkPipelineVertexInputStateCreateInfo vertexInputInfo; - memset(&vertexInputInfo, 0, sizeof(vertexInputInfo)); + VkPipelineVertexInputStateCreateInfo vertexInputInfo = {}; vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; - vertexInputInfo.vertexBindingDescriptionCount = uint32_t(vertexBindings.count()); + vertexInputInfo.vertexBindingDescriptionCount = uint32_t(vertexBindings.size()); vertexInputInfo.pVertexBindingDescriptions = vertexBindings.constData(); - vertexInputInfo.vertexAttributeDescriptionCount = uint32_t(vertexAttributes.count()); + vertexInputInfo.vertexAttributeDescriptionCount = uint32_t(vertexAttributes.size()); vertexInputInfo.pVertexAttributeDescriptions = vertexAttributes.constData(); - VkPipelineVertexInputDivisorStateCreateInfoEXT divisorInfo; +#ifdef VK_EXT_vertex_attribute_divisor + VkPipelineVertexInputDivisorStateCreateInfoEXT divisorInfo = {}; if (!nonOneStepRates.isEmpty()) { - memset(&divisorInfo, 0, sizeof(divisorInfo)); - divisorInfo.sType = VkStructureType(1000190001); // VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT - divisorInfo.vertexBindingDivisorCount = uint32_t(nonOneStepRates.count()); + divisorInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT; + divisorInfo.vertexBindingDivisorCount = uint32_t(nonOneStepRates.size()); divisorInfo.pVertexBindingDivisors = nonOneStepRates.constData(); vertexInputInfo.pNext = &divisorInfo; } +#endif pipelineInfo.pVertexInputState = &vertexInputInfo; QVarLengthArray<VkDynamicState, 8> dynEnable; @@ -6945,32 +7623,28 @@ bool QVkGraphicsPipeline::create() if (m_flags.testFlag(QRhiGraphicsPipeline::UsesStencilRef)) dynEnable << VK_DYNAMIC_STATE_STENCIL_REFERENCE; - VkPipelineDynamicStateCreateInfo dynamicInfo; - memset(&dynamicInfo, 0, sizeof(dynamicInfo)); + VkPipelineDynamicStateCreateInfo dynamicInfo = {}; dynamicInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; - dynamicInfo.dynamicStateCount = uint32_t(dynEnable.count()); + dynamicInfo.dynamicStateCount = uint32_t(dynEnable.size()); dynamicInfo.pDynamicStates = dynEnable.constData(); pipelineInfo.pDynamicState = &dynamicInfo; - VkPipelineViewportStateCreateInfo viewportInfo; - memset(&viewportInfo, 0, sizeof(viewportInfo)); + VkPipelineViewportStateCreateInfo viewportInfo = {}; viewportInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; viewportInfo.viewportCount = viewportInfo.scissorCount = 1; pipelineInfo.pViewportState = &viewportInfo; - VkPipelineInputAssemblyStateCreateInfo inputAsmInfo; - memset(&inputAsmInfo, 0, sizeof(inputAsmInfo)); + VkPipelineInputAssemblyStateCreateInfo inputAsmInfo = {}; inputAsmInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; inputAsmInfo.topology = toVkTopology(m_topology); inputAsmInfo.primitiveRestartEnable = (m_topology == TriangleStrip || m_topology == LineStrip); pipelineInfo.pInputAssemblyState = &inputAsmInfo; - VkPipelineTessellationStateCreateInfo tessInfo; + VkPipelineTessellationStateCreateInfo tessInfo = {}; #ifdef VK_VERSION_1_1 - VkPipelineTessellationDomainOriginStateCreateInfo originInfo; + VkPipelineTessellationDomainOriginStateCreateInfo originInfo = {}; #endif if (m_topology == Patches) { - memset(&tessInfo, 0, sizeof(tessInfo)); tessInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO; tessInfo.patchControlPoints = uint32_t(qMax(1, m_patchControlPointCount)); @@ -6980,8 +7654,7 @@ bool QVkGraphicsPipeline::create() // still have it working with both APIs. This requires Vulkan 1.1 (or // VK_KHR_maintenance2 but don't bother with that). #ifdef VK_VERSION_1_1 - if (rhiD->caps.vulkan11OrHigher) { - memset(&originInfo, 0, sizeof(originInfo)); + if (rhiD->caps.apiVersion >= QVersionNumber(1, 1)) { originInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO; originInfo.domainOrigin = VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT; tessInfo.pNext = &originInfo; @@ -6995,8 +7668,7 @@ bool QVkGraphicsPipeline::create() pipelineInfo.pTessellationState = &tessInfo; } - VkPipelineRasterizationStateCreateInfo rastInfo; - memset(&rastInfo, 0, sizeof(rastInfo)); + VkPipelineRasterizationStateCreateInfo rastInfo = {}; rastInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; rastInfo.cullMode = toVkCullMode(m_cullMode); rastInfo.frontFace = toVkFrontFace(m_frontFace); @@ -7006,16 +7678,15 @@ bool QVkGraphicsPipeline::create() rastInfo.depthBiasSlopeFactor = m_slopeScaledDepthBias; } rastInfo.lineWidth = rhiD->caps.wideLines ? m_lineWidth : 1.0f; + rastInfo.polygonMode = toVkPolygonMode(m_polygonMode); pipelineInfo.pRasterizationState = &rastInfo; - VkPipelineMultisampleStateCreateInfo msInfo; - memset(&msInfo, 0, sizeof(msInfo)); + VkPipelineMultisampleStateCreateInfo msInfo = {}; msInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; - msInfo.rasterizationSamples = rhiD->effectiveSampleCount(m_sampleCount); + msInfo.rasterizationSamples = rhiD->effectiveSampleCountBits(m_sampleCount); pipelineInfo.pMultisampleState = &msInfo; - VkPipelineDepthStencilStateCreateInfo dsInfo; - memset(&dsInfo, 0, sizeof(dsInfo)); + VkPipelineDepthStencilStateCreateInfo dsInfo = {}; dsInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; dsInfo.depthTestEnable = m_depthTest; dsInfo.depthWriteEnable = m_depthWrite; @@ -7031,13 +7702,11 @@ bool QVkGraphicsPipeline::create() } pipelineInfo.pDepthStencilState = &dsInfo; - VkPipelineColorBlendStateCreateInfo blendInfo; - memset(&blendInfo, 0, sizeof(blendInfo)); + VkPipelineColorBlendStateCreateInfo blendInfo = {}; blendInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; QVarLengthArray<VkPipelineColorBlendAttachmentState, 4> vktargetBlends; - for (const QRhiGraphicsPipeline::TargetBlend &b : qAsConst(m_targetBlends)) { - VkPipelineColorBlendAttachmentState blend; - memset(&blend, 0, sizeof(blend)); + for (const QRhiGraphicsPipeline::TargetBlend &b : std::as_const(m_targetBlends)) { + VkPipelineColorBlendAttachmentState blend = {}; blend.blendEnable = b.enable; blend.srcColorBlendFactor = toVkBlendFactor(b.srcColor); blend.dstColorBlendFactor = toVkBlendFactor(b.dstColor); @@ -7049,13 +7718,12 @@ bool QVkGraphicsPipeline::create() vktargetBlends.append(blend); } if (vktargetBlends.isEmpty()) { - VkPipelineColorBlendAttachmentState blend; - memset(&blend, 0, sizeof(blend)); + VkPipelineColorBlendAttachmentState blend = {}; blend.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; vktargetBlends.append(blend); } - blendInfo.attachmentCount = uint32_t(vktargetBlends.count()); + blendInfo.attachmentCount = uint32_t(vktargetBlends.size()); blendInfo.pAttachments = vktargetBlends.constData(); pipelineInfo.pColorBlendState = &blendInfo; @@ -7074,6 +7742,7 @@ bool QVkGraphicsPipeline::create() return false; } + rhiD->pipelineCreationEnd(); lastActiveFrameSlot = -1; generation += 1; rhiD->registerResource(this); @@ -7118,11 +7787,11 @@ bool QVkComputePipeline::create() destroy(); QRHI_RES_RHI(QRhiVulkan); + rhiD->pipelineCreationStart(); if (!rhiD->ensurePipelineCache()) return false; - VkPipelineLayoutCreateInfo pipelineLayoutInfo; - memset(&pipelineLayoutInfo, 0, sizeof(pipelineLayoutInfo)); + VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pipelineLayoutInfo.setLayoutCount = 1; QVkShaderResourceBindings *srbD = QRHI_RES(QVkShaderResourceBindings, m_shaderResourceBindings); @@ -7134,8 +7803,7 @@ bool QVkComputePipeline::create() return false; } - VkComputePipelineCreateInfo pipelineInfo; - memset(&pipelineInfo, 0, sizeof(pipelineInfo)); + VkComputePipelineCreateInfo pipelineInfo = {}; pipelineInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; pipelineInfo.layout = layout; @@ -7154,8 +7822,7 @@ bool QVkComputePipeline::create() return false; } VkShaderModule shader = rhiD->createShader(spirv.shader()); - VkPipelineShaderStageCreateInfo shaderInfo; - memset(&shaderInfo, 0, sizeof(shaderInfo)); + VkPipelineShaderStageCreateInfo shaderInfo = {}; shaderInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; shaderInfo.stage = VK_SHADER_STAGE_COMPUTE_BIT; shaderInfo.module = shader; @@ -7169,6 +7836,7 @@ bool QVkComputePipeline::create() return false; } + rhiD->pipelineCreationEnd(); lastActiveFrameSlot = -1; generation += 1; rhiD->registerResource(this); @@ -7213,7 +7881,8 @@ const QRhiNativeHandles *QVkCommandBuffer::nativeHandles() QVkSwapChain::QVkSwapChain(QRhiImplementation *rhi) : QRhiSwapChain(rhi), - rtWrapper(rhi), + rtWrapper(rhi, this), + rtWrapperRight(rhi, this), cbWrapper(rhi) { } @@ -7256,6 +7925,11 @@ QRhiRenderTarget *QVkSwapChain::currentFrameRenderTarget() return &rtWrapper; } +QRhiRenderTarget *QVkSwapChain::currentFrameRenderTarget(StereoTargetBuffer targetBuffer) +{ + return !stereo || targetBuffer == StereoTargetBuffer::LeftBuffer ? &rtWrapper : &rtWrapperRight; +} + QSize QVkSwapChain::surfacePixelSize() { if (!ensureSurface()) @@ -7263,8 +7937,7 @@ QSize QVkSwapChain::surfacePixelSize() // The size from the QWindow may not exactly match the surface... so if a // size is reported from the surface, use that. - VkSurfaceCapabilitiesKHR surfaceCaps; - memset(&surfaceCaps, 0, sizeof(surfaceCaps)); + VkSurfaceCapabilitiesKHR surfaceCaps = {}; QRHI_RES_RHI(QRhiVulkan); rhiD->vkGetPhysicalDeviceSurfaceCapabilitiesKHR(rhiD->physDev, surface, &surfaceCaps); VkExtent2D bufferSize = surfaceCaps.currentExtent; @@ -7284,6 +7957,9 @@ static inline bool hdrFormatMatchesVkSurfaceFormat(QRhiSwapChain::Format f, cons case QRhiSwapChain::HDR10: return (s.format == VK_FORMAT_A2B10G10R10_UNORM_PACK32 || s.format == VK_FORMAT_A2R10G10B10_UNORM_PACK32) && s.colorSpace == VK_COLOR_SPACE_HDR10_ST2084_EXT; + case QRhiSwapChain::HDRExtendedDisplayP3Linear: + return s.format == VK_FORMAT_R16G16B16A16_SFLOAT + && s.colorSpace == VK_COLOR_SPACE_DISPLAY_P3_LINEAR_EXT; default: break; } @@ -7377,11 +8053,9 @@ bool QVkSwapChain::ensureSurface() surface = surf; QRHI_RES_RHI(QRhiVulkan); - if (rhiD->gfxQueueFamilyIdx != -1) { - if (!rhiD->inst->supportsPresent(rhiD->physDev, uint32_t(rhiD->gfxQueueFamilyIdx), m_window)) { - qWarning("Presenting not supported on this window"); - return false; - } + if (!rhiD->inst->supportsPresent(rhiD->physDev, rhiD->gfxQueueFamilyIdx, m_window)) { + qWarning("Presenting not supported on this window"); + return false; } quint32 formatCount = 0; @@ -7395,11 +8069,9 @@ bool QVkSwapChain::ensureSurface() const bool srgbRequested = m_flags.testFlag(sRGB); for (int i = 0; i < int(formatCount); ++i) { if (formats[i].format != VK_FORMAT_UNDEFINED) { - bool ok = false; - if (m_format == SDR) - ok = srgbRequested == isSrgbFormat(formats[i].format); - else - ok = hdrFormatMatchesVkSurfaceFormat(m_format, formats[i]); + bool ok = srgbRequested == isSrgbFormat(formats[i].format); + if (m_format != SDR) + ok &= hdrFormatMatchesVkSurfaceFormat(m_format, formats[i]); if (ok) { colorFormat = formats[i].format; colorSpace = formats[i].colorSpace; @@ -7408,7 +8080,7 @@ bool QVkSwapChain::ensureSurface() } } - samples = rhiD->effectiveSampleCount(m_sampleCount); + samples = rhiD->effectiveSampleCountBits(m_sampleCount); quint32 presModeCount = 0; rhiD->vkGetPhysicalDeviceSurfacePresentModesKHR(rhiD->physDev, surface, &presModeCount, nullptr); @@ -7464,6 +8136,7 @@ bool QVkSwapChain::createOrResize() if (!m_renderPassDesc) qWarning("QVkSwapChain: No renderpass descriptor set. See newCompatibleRenderPassDescriptor() and setRenderPassDescriptor()."); + rtWrapper.setRenderPassDescriptor(m_renderPassDesc); // for the public getter in QRhiRenderTarget rtWrapper.d.rp = QRHI_RES(QVkRenderPassDescriptor, m_renderPassDesc); Q_ASSERT(rtWrapper.d.rp && rtWrapper.d.rp->rp); @@ -7478,6 +8151,7 @@ bool QVkSwapChain::createOrResize() rtWrapper.d.dsAttCount = 0; ds = nullptr; } + rtWrapper.d.dsResolveAttCount = 0; if (samples > VK_SAMPLE_COUNT_1_BIT) rtWrapper.d.resolveAttCount = 1; else @@ -7491,11 +8165,10 @@ bool QVkSwapChain::createOrResize() samples > VK_SAMPLE_COUNT_1_BIT ? image.imageView : VK_NULL_HANDLE }; - VkFramebufferCreateInfo fbInfo; - memset(&fbInfo, 0, sizeof(fbInfo)); + VkFramebufferCreateInfo fbInfo = {}; fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; fbInfo.renderPass = rtWrapper.d.rp->rp; - fbInfo.attachmentCount = uint32_t(rtWrapper.d.colorAttCount + rtWrapper.d.dsAttCount + rtWrapper.d.resolveAttCount); + fbInfo.attachmentCount = uint32_t(rtWrapper.d.colorAttCount + rtWrapper.d.dsAttCount + rtWrapper.d.resolveAttCount + rtWrapper.d.dsResolveAttCount); fbInfo.pAttachments = views; fbInfo.width = uint32_t(pixelSize.width()); fbInfo.height = uint32_t(pixelSize.height()); @@ -7508,6 +8181,56 @@ bool QVkSwapChain::createOrResize() } } + if (stereo) { + rtWrapperRight.setRenderPassDescriptor( + m_renderPassDesc); // for the public getter in QRhiRenderTarget + rtWrapperRight.d.rp = QRHI_RES(QVkRenderPassDescriptor, m_renderPassDesc); + Q_ASSERT(rtWrapperRight.d.rp && rtWrapperRight.d.rp->rp); + + rtWrapperRight.d.pixelSize = pixelSize; + rtWrapperRight.d.dpr = float(window->devicePixelRatio()); + rtWrapperRight.d.sampleCount = samples; + rtWrapperRight.d.colorAttCount = 1; + if (m_depthStencil) { + rtWrapperRight.d.dsAttCount = 1; + ds = QRHI_RES(QVkRenderBuffer, m_depthStencil); + } else { + rtWrapperRight.d.dsAttCount = 0; + ds = nullptr; + } + rtWrapperRight.d.dsResolveAttCount = 0; + if (samples > VK_SAMPLE_COUNT_1_BIT) + rtWrapperRight.d.resolveAttCount = 1; + else + rtWrapperRight.d.resolveAttCount = 0; + + for (int i = 0; i < bufferCount; ++i) { + QVkSwapChain::ImageResources &image(imageRes[i + bufferCount]); + VkImageView views[3] = { + // color, ds, resolve + samples > VK_SAMPLE_COUNT_1_BIT ? image.msaaImageView : image.imageView, + ds ? ds->imageView : VK_NULL_HANDLE, + samples > VK_SAMPLE_COUNT_1_BIT ? image.imageView : VK_NULL_HANDLE + }; + + VkFramebufferCreateInfo fbInfo = {}; + fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + fbInfo.renderPass = rtWrapperRight.d.rp->rp; + fbInfo.attachmentCount = uint32_t(rtWrapperRight.d.colorAttCount + rtWrapperRight.d.dsAttCount + + rtWrapperRight.d.resolveAttCount + rtWrapperRight.d.dsResolveAttCount); + fbInfo.pAttachments = views; + fbInfo.width = uint32_t(pixelSize.width()); + fbInfo.height = uint32_t(pixelSize.height()); + fbInfo.layers = 1; + + VkResult err = rhiD->df->vkCreateFramebuffer(rhiD->dev, &fbInfo, nullptr, &image.fb); + if (err != VK_SUCCESS) { + qWarning("Failed to create framebuffer: %d", err); + return false; + } + } + } + frameCount = 0; if (needsRegistration) |