summaryrefslogtreecommitdiffstats
path: root/src/gui/rhi/qrhivulkan.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/rhi/qrhivulkan.cpp')
-rw-r--r--src/gui/rhi/qrhivulkan.cpp2579
1 files changed, 1664 insertions, 915 deletions
diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp
index e7e31b1b2d..da8c8fc52a 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
@@ -49,12 +14,16 @@
#endif
QT_WARNING_PUSH
QT_WARNING_DISABLE_GCC("-Wsuggest-override")
+#if defined(Q_CC_CLANG) && Q_CC_CLANG >= 1100
+QT_WARNING_DISABLE_CLANG("-Wdeprecated-copy")
+#endif
#include "vk_mem_alloc.h"
QT_WARNING_POP
#include <qmath.h>
#include <QVulkanFunctions>
#include <QtGui/qwindow.h>
+#include <optional>
QT_BEGIN_NAMESPACE
@@ -90,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:
@@ -104,18 +76,7 @@ QT_BEGIN_NAMESPACE
...
QVulkanInstance inst;
- #ifndef Q_OS_ANDROID
- inst.setLayers(QByteArrayList() << "VK_LAYER_LUNARG_standard_validation");
- #else
- inst.setLayers(QByteArrayList()
- << "VK_LAYER_GOOGLE_threading"
- << "VK_LAYER_LUNARG_parameter_validation"
- << "VK_LAYER_LUNARG_object_tracker"
- << "VK_LAYER_LUNARG_core_validation"
- << "VK_LAYER_LUNARG_image"
- << "VK_LAYER_LUNARG_swapchain"
- << "VK_LAYER_GOOGLE_unique_objects");
- #endif
+ inst.setLayers({ "VK_LAYER_KHRONOS_validation" }); // for debugging only, not for release builds
inst.setExtensions(QRhiVulkanInitParams::preferredInstanceExtensions());
if (!inst.create())
qFatal("Vulkan not available");
@@ -160,11 +121,21 @@ QT_BEGIN_NAMESPACE
in deviceExtensions. This can be relevant when integrating with native Vulkan
rendering code.
- It is expected that the desired list of instance extensions will be queried
- by calling the static function preferredInstanceExtensions() before
- initializing a QVulkanInstance. The returned list can be passed to
- QVulkanInstance::setExtensions() as-is, because unsupported extensions are
- filtered out automatically.
+ It is expected that the backend's desired list of instance extensions will
+ be queried by calling the static function preferredInstanceExtensions()
+ before initializing a QVulkanInstance. The returned list can be safely
+ passed to QVulkanInstance::setExtensions() as-is, because unsupported
+ extensions are filtered out automatically. If this is not done, certain
+ features, such as QRhi::CustomInstanceStepRate may be reported as
+ unsupported even when the Vulkan implementation on the system has support
+ for the relevant functionality.
+
+ For full functionality the QVulkanInstance needs to have API 1.1 enabled,
+ when available. This means calling QVulkanInstance::setApiVersion() with
+ 1.1 or higher whenever QVulkanInstance::supportedApiVersion() reports that
+ at least Vulkan 1.1 is supported. If this is not done, certain features,
+ such as QRhi::RenderTo3DTextureSlice may be reported as unsupported even
+ when the Vulkan implementation on the system supports Vulkan 1.1 or newer.
\section2 Working with existing Vulkan devices
@@ -197,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)
{
@@ -233,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)
-{
- globalVulkanInstance->functions()->vkGetPhysicalDeviceMemoryProperties(physicalDevice, pMemoryProperties);
-}
-
-static VkResult VKAPI_PTR wrap_vkAllocateMemory(VkDevice device, const VkMemoryAllocateInfo* pAllocateInfo, const VkAllocationCallbacks* pAllocator, VkDeviceMemory* pMemory)
-{
- 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)
+static VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL wrap_vkGetInstanceProcAddr(VkInstance, const char *pName)
{
- globalVulkanInstance->deviceFunctions(device)->vkUnmapMemory(device, memory);
+ return globalVulkanInstance->getInstanceProcAddr(pName);
}
-VkResult VKAPI_PTR wrap_vkFlushMappedMemoryRanges(VkDevice device, uint32_t memoryRangeCount, const VkMappedMemoryRange* pMemoryRanges)
+static VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL wrap_vkGetDeviceProcAddr(VkDevice device, const char *pName)
{
- 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)
@@ -323,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 {
@@ -330,11 +325,15 @@ 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")
};
}
@@ -362,20 +361,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;
}
@@ -386,9 +384,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;
}
@@ -418,11 +418,17 @@ 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();
-
- rhiFlags = flags;
+ 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.debugUtils = inst->extensions().contains(QByteArrayLiteral("VK_EXT_debug_utils"));
QList<VkQueueFamilyProperties> queueFamilyProps;
auto queryQueueFamilyProps = [this, &queueFamilyProps] {
@@ -503,54 +509,129 @@ 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_LUNARG_standard_validation"))
- devLayers.append("VK_LAYER_LUNARG_standard_validation");
+ if (inst->layers().contains("VK_LAYER_KHRONOS_validation"))
+ devLayers.append("VK_LAYER_KHRONOS_validation");
QVulkanInfoVector<QVulkanExtension> devExts;
uint32_t devExtCount = 0;
@@ -558,44 +639,56 @@ 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");
- debugMarkersAvailable = false;
- if (devExts.contains(VK_EXT_DEBUG_MARKER_EXTENSION_NAME)) {
- requestedDevExts.append(VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
- debugMarkersAvailable = 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.");
+ }
}
- vertexAttribDivisorAvailable = false;
+ 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);
- vertexAttribDivisorAvailable = true;
+ caps.vertexAttribDivisor = 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());
+ }
}
}
@@ -605,29 +698,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.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) {
@@ -638,12 +752,18 @@ bool QRhiVulkan::create(QRhi::Flags flags)
qCDebug(QRHI_LOG_INFO, "Using imported device %p", dev);
}
+ vkGetPhysicalDeviceSurfaceCapabilitiesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR>(
+ inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfaceCapabilitiesKHR"));
+ vkGetPhysicalDeviceSurfaceFormatsKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceFormatsKHR>(
+ inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfaceFormatsKHR"));
+ vkGetPhysicalDeviceSurfacePresentModesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfacePresentModesKHR>(
+ inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfacePresentModesKHR"));
+
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) {
@@ -652,18 +772,15 @@ 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();
- hasCompute = (queueFamilyProps[gfxQueueFamilyIdx].queueFlags & VK_QUEUE_COMPUTE_BIT) != 0;
+ caps.compute = (queueFamilyProps[gfxQueueFamilyIdx].queueFlags & VK_QUEUE_COMPUTE_BIT) != 0;
timestampValidBits = queueFamilyProps[gfxQueueFamilyIdx].timestampValidBits;
ubufAlign = physDevProperties.limits.minUniformBufferOffsetAlignment;
@@ -671,35 +788,41 @@ bool QRhiVulkan::create(QRhi::Flags flags)
// elsewhere states that the minimum bufferOffset is 4...
texbufAlign = qMax<VkDeviceSize>(4, physDevProperties.limits.optimalBufferCopyOffsetAlignment);
- hasWideLines = physDevFeatures.wideLines;
+ caps.wideLines = physDevFeatures.wideLines;
+
+ 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
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) {
@@ -718,8 +841,7 @@ bool QRhiVulkan::create(QRhi::Flags flags)
else
qWarning("Failed to create initial descriptor pool: %d", err);
- VkQueryPoolCreateInfo timestampQueryPoolInfo;
- memset(&timestampQueryPoolInfo, 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;
@@ -731,12 +853,14 @@ bool QRhiVulkan::create(QRhi::Flags flags)
timestampQueryPoolMap.resize(QVK_MAX_ACTIVE_TIMESTAMP_PAIRS); // 1 bit per pair
timestampQueryPoolMap.fill(false);
- if (debugMarkersAvailable) {
- 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;
@@ -746,6 +870,7 @@ bool QRhiVulkan::create(QRhi::Flags flags)
nativeHandlesStruct.gfxQueueIdx = gfxQueueIdx;
nativeHandlesStruct.gfxQueue = gfxQueue;
nativeHandlesStruct.vmemAllocator = allocator;
+ nativeHandlesStruct.inst = inst;
return true;
}
@@ -814,8 +939,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
@@ -837,7 +961,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);
@@ -857,7 +981,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);
@@ -900,6 +1024,10 @@ static inline VkFormat toVkTextureFormat(QRhiTexture::Format format, QRhiTexture
case QRhiTexture::R32F:
return VK_FORMAT_R32_SFLOAT;
+ case QRhiTexture::RGB10A2:
+ // intentionally A2B10G10R10, not A2R10G10B10
+ return VK_FORMAT_A2B10G10R10_UNORM_PACK32;
+
case QRhiTexture::D16:
return VK_FORMAT_D16_UNORM;
case QRhiTexture::D24:
@@ -961,12 +1089,11 @@ 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);
}
}
-static inline QRhiTexture::Format colorTextureFormatFromVkFormat(VkFormat format, QRhiTexture::Flags *flags)
+static inline QRhiTexture::Format swapchainReadbackTextureFormat(VkFormat format, QRhiTexture::Flags *flags)
{
switch (format) {
case VK_FORMAT_R8G8B8A8_UNORM:
@@ -981,24 +1108,14 @@ static inline QRhiTexture::Format colorTextureFormatFromVkFormat(VkFormat format
if (flags)
(*flags) |= QRhiTexture::sRGB;
return QRhiTexture::BGRA8;
- case VK_FORMAT_R8_UNORM:
- return QRhiTexture::R8;
- case VK_FORMAT_R8G8_UNORM:
- return QRhiTexture::RG8;
- case VK_FORMAT_R8_SRGB:
- if (flags)
- (*flags) |= QRhiTexture::sRGB;
- return QRhiTexture::R8;
- case VK_FORMAT_R8G8_SRGB:
- if (flags)
- (*flags) |= QRhiTexture::sRGB;
- return QRhiTexture::RG8;
- case VK_FORMAT_R16_UNORM:
- return QRhiTexture::R16;
- case VK_FORMAT_R16G16_UNORM:
- return QRhiTexture::RG16;
- default: // this cannot assert, must warn and return unknown
- qWarning("VkFormat %d is not a recognized uncompressed color format", format);
+ case VK_FORMAT_R16G16B16A16_SFLOAT:
+ return QRhiTexture::RGBA16F;
+ case VK_FORMAT_R32G32B32A32_SFLOAT:
+ return QRhiTexture::RGBA32F;
+ case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
+ return QRhiTexture::RGB10A2;
+ default:
+ qWarning("VkFormat %d cannot be read back", format);
break;
}
return QRhiTexture::UnknownFormat;
@@ -1074,8 +1191,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;
@@ -1100,8 +1216,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);
@@ -1129,8 +1244,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;
@@ -1184,18 +1298,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;
}
@@ -1203,8 +1317,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;
@@ -1218,6 +1331,7 @@ bool QRhiVulkan::createDefaultRenderPass(QVkRenderPassDescriptor *rpD, bool hasD
rpD->colorRefs.append({ 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL });
rpD->hasDepthStencil = hasDepthStencil;
+ rpD->multiViewCount = 0;
if (hasDepthStencil) {
// clear on load + no store + lazy alloc + transient image should play
@@ -1252,8 +1366,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;
@@ -1288,29 +1401,63 @@ 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
+};
+
bool QRhiVulkan::createOffscreenRenderPass(QVkRenderPassDescriptor *rpD,
const QRhiColorAttachment *firstColorAttachment,
const QRhiColorAttachment *lastColorAttachment,
bool preserveColor,
bool preserveDs,
+ bool storeDs,
QRhiRenderBuffer *depthStencilBuffer,
QRhiTexture *depthTexture)
{
// attachment list layout is color (0-8), ds (0-1), resolve (0-8)
+ int multiViewCount = 0;
for (auto it = firstColorAttachment; it != lastColorAttachment; ++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
@@ -1318,31 +1465,41 @@ 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);
}
- 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) {
if (it->resolveTexture()) {
@@ -1362,9 +1519,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;
@@ -1374,14 +1530,14 @@ 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->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
@@ -1392,6 +1548,10 @@ bool QRhiVulkan::createOffscreenRenderPass(QVkRenderPassDescriptor *rpD,
VkSubpassDescription subpassDesc;
fillRenderPassCreateInfo(&rpInfo, &subpassDesc, rpD);
+ MultiViewRenderPassSetupHelper multiViewHelper;
+ if (!multiViewHelper.prepare(&rpInfo, multiViewCount, caps.multiView))
+ return false;
+
VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, &rpD->rp);
if (err != VK_SUCCESS) {
qWarning("Failed to create renderpass: %d", err);
@@ -1436,21 +1596,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;
@@ -1458,9 +1642,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;
@@ -1477,15 +1668,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;
@@ -1543,12 +1733,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];
@@ -1557,8 +1748,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;
@@ -1577,11 +1767,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) {
@@ -1645,7 +1864,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);
@@ -1687,7 +1906,25 @@ void QRhiVulkan::ensureCommandPoolForNewFrame()
flags |= VK_COMMAND_POOL_RESET_RELEASE_RESOURCES_BIT;
// put all command buffers allocated from this slot's pool to initial state
- df->vkResetCommandPool(dev, cmdPool[currentFrameSlot], 0);
+ 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)
@@ -1695,7 +1932,8 @@ QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::Begin
QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain);
const int frameResIndex = swapChainD->bufferCount > 1 ? swapChainD->currentFrameSlot : 0;
QVkSwapChain::FrameResources &frame(swapChainD->frameRes[frameResIndex]);
- QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
+
+ 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
@@ -1740,32 +1978,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;
- // now we have the gpu time for the previous frame for this slot, report it
- // (does not matter that it is not for this frame)
- QRHI_PROF_F(swapChainFrameGpuTime(swapChain, elapsedMs));
- }
- } else {
- qWarning("Failed to query timestamp: %d", err);
- }
- }
-
currentFrameSlot = int(swapChainD->currentFrameSlot);
currentSwapChain = swapChainD;
if (swapChainD->ds)
@@ -1779,34 +1991,56 @@ 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 (profilerPrivateOrNull() && swapChainD->bufferCount > 1) { // no timestamps if not having at least 2 frames in flight
- for (int i = 0; i < timestampQueryPoolMap.count(); ++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;
- }
-
swapChainD->cbWrapper.cb = frame.cmdBuf;
QVkSwapChain::ImageResources &image(swapChainD->imageRes[swapChainD->currentImageIndex]);
swapChainD->rtWrapper.d.fb = image.fb;
- QRHI_PROF_F(beginSwapChainFrame(swapChain));
+ 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;
+ }
+ }
+
return QRhi::FrameOpSuccess;
}
@@ -1815,6 +2049,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;
@@ -1822,8 +2060,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;
@@ -1870,14 +2107,9 @@ QRhi::FrameOpResult QRhiVulkan::endFrame(QRhiSwapChain *swapChain, QRhi::EndFram
frame.imageSemWaitable = false;
frame.cmdFenceWaitable = true;
- QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
- // this must be done before the Present
- QRHI_PROF_F(endSwapChainFrame(swapChain, swapChainD->frameCount + 1));
-
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;
@@ -1930,8 +2162,8 @@ void QRhiVulkan::prepareNewFrame(QRhiCommandBuffer *cb)
//
// With multiple swapchains on the same QRhi things get more convoluted
// (and currentFrameSlot strictly alternating is not true anymore) but
- // beginNonWrapperFrame() solves that by blocking as necessary so the rest
- // here is safe regardless.
+ // begin(Offscreen)Frame() blocks anyway waiting for its current frame
+ // slot's previous commands to complete so this here is safe regardless.
executeDeferredReleases();
@@ -1945,8 +2177,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;
@@ -1964,8 +2195,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);
@@ -1996,8 +2226,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;
@@ -2028,7 +2257,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) {
@@ -2042,17 +2271,17 @@ void QRhiVulkan::waitCommandCompletion(int frameSlot)
QRhi::FrameOpResult QRhiVulkan::beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags)
{
// Switch to the next slot manually. Swapchains do not know about this
- // which is good. So for example a - unusual but possible - onscreen,
- // onscreen, offscreen, onscreen, onscreen, onscreen sequence of
- // begin/endFrame leads to 0, 1, 0, 0, 1, 0. This works because the
- // offscreen frame is synchronous in the sense that we wait for execution
- // to complete in endFrame, and so no resources used in that frame are busy
+ // which is good. So for example an onscreen, onscreen, offscreen,
+ // onscreen, onscreen, onscreen sequence of frames leads to 0, 1, 0, 0, 1,
+ // 0. (no strict alternation anymore) But this is not different from what
+ // happens when multiple swapchains are involved. Offscreen frames are
+ // synchronous anyway in the sense that they wait for execution to complete
+ // in endOffscreenFrame, so no resources used in that frame are busy
// anymore in the next frame.
+
currentFrameSlot = (currentFrameSlot + 1) % QVK_FRAMES_IN_FLIGHT;
- // except that this gets complicated with multiple swapchains so make sure
- // any pending commands have finished for the frame slot we are going to use
- if (swapchains.count() > 1)
- waitCommandCompletion(currentFrameSlot);
+
+ waitCommandCompletion(currentFrameSlot);
ensureCommandPoolForNewFrame();
@@ -2064,6 +2293,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;
}
@@ -2077,9 +2324,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) {
@@ -2100,6 +2352,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;
}
@@ -2171,6 +2441,9 @@ static inline QRhiPassResourceTracker::UsageState toPassTrackerUsageState(const
void QRhiVulkan::activateTextureRenderTarget(QVkCommandBuffer *cbD, QVkTextureRenderTarget *rtD)
{
+ if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QVkTexture, QVkRenderBuffer>(rtD->description(), rtD->d.currentResIdList))
+ rtD->create();
+
rtD->lastActiveFrameSlot = currentFrameSlot;
rtD->d.rp->lastActiveFrameSlot = currentFrameSlot;
QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
@@ -2232,8 +2505,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;
@@ -2246,12 +2518,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) {
@@ -2307,8 +2577,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 =
@@ -2333,8 +2603,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;
@@ -2359,17 +2628,19 @@ void QRhiVulkan::beginPass(QRhiCommandBuffer *cb,
float(colorClearValue.alphaF()) } };
cvs.append(cv);
}
- rpBeginInfo.clearValueCount = uint32_t(cvs.count());
+ 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));
+
+ cbD->resetCachedState();
}
void QRhiVulkan::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
@@ -2381,7 +2652,6 @@ void QRhiVulkan::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourc
VkCommandBuffer secondaryCb = cbD->activeSecondaryCbStack.last();
cbD->activeSecondaryCbStack.removeLast();
endAndEnqueueSecondaryCommandBuffer(secondaryCb, cbD);
- cbD->resetCachedState();
}
QVkCommandBuffer::Command &cmd(cbD->commands.get());
@@ -2413,6 +2683,8 @@ void QRhiVulkan::beginComputePass(QRhiCommandBuffer *cb,
if (cbD->passUsesSecondaryCb)
cbD->activeSecondaryCbStack.append(startSecondaryCommandBuffer());
+
+ cbD->resetCachedState();
}
void QRhiVulkan::endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
@@ -2424,7 +2696,6 @@ void QRhiVulkan::endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *
VkCommandBuffer secondaryCb = cbD->activeSecondaryCbStack.last();
cbD->activeSecondaryCbStack.removeLast();
endAndEnqueueSecondaryCommandBuffer(secondaryCb, cbD);
- cbD->resetCachedState();
}
cbD->recordingPass = QVkCommandBuffer::NoPass;
@@ -2495,9 +2766,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:
@@ -2530,8 +2801,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
@@ -2547,8 +2817,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;
@@ -2575,12 +2844,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));
@@ -2590,18 +2859,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;
@@ -2613,8 +2882,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());
@@ -2632,8 +2900,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;
@@ -2658,12 +2925,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);
@@ -2683,17 +2949,17 @@ 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;
case QRhiShaderResourceBinding::SampledTexture:
{
- const QRhiShaderResourceBinding::Data::SampledTextureData *data = &b->u.stex;
+ const QRhiShaderResourceBinding::Data::TextureAndOrSamplerData *data = &b->u.stex;
writeInfo.descriptorCount = data->count; // arrays of combined image samplers are supported
writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
ArrayOfImageDesc imageInfo(data->count);
@@ -2709,7 +2975,44 @@ 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;
+ case QRhiShaderResourceBinding::Texture:
+ {
+ const QRhiShaderResourceBinding::Data::TextureAndOrSamplerData *data = &b->u.stex;
+ writeInfo.descriptorCount = data->count; // arrays of (separate) images are supported
+ writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
+ ArrayOfImageDesc imageInfo(data->count);
+ for (int elem = 0; elem < data->count; ++elem) {
+ QVkTexture *texD = QRHI_RES(QVkTexture, data->texSamplers[elem].tex);
+ bd.stex.d[elem].texId = texD->m_id;
+ bd.stex.d[elem].texGeneration = texD->generation;
+ bd.stex.d[elem].samplerId = 0;
+ bd.stex.d[elem].samplerGeneration = 0;
+ imageInfo[elem].sampler = VK_NULL_HANDLE;
+ imageInfo[elem].imageView = texD->imageView;
+ imageInfo[elem].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+ }
+ bd.stex.count = data->count;
+ imageInfoIndex = imageInfos.size();
+ imageInfos.append(imageInfo);
+ }
+ break;
+ case QRhiShaderResourceBinding::Sampler:
+ {
+ QVkSampler *samplerD = QRHI_RES(QVkSampler, b->u.stex.texSamplers[0].sampler);
+ writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;
+ bd.stex.d[0].texId = 0;
+ bd.stex.d[0].texGeneration = 0;
+ bd.stex.d[0].samplerId = samplerD->m_id;
+ bd.stex.d[0].samplerGeneration = samplerD->generation;
+ ArrayOfImageDesc imageInfo(1);
+ imageInfo[0].sampler = samplerD->sampler;
+ imageInfo[0].imageView = VK_NULL_HANDLE;
+ imageInfo[0].imageLayout = VK_IMAGE_LAYOUT_GENERAL;
+ imageInfoIndex = imageInfos.size();
imageInfos.append(imageInfo);
}
break;
@@ -2718,7 +3021,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;
@@ -2727,7 +3030,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);
}
}
@@ -2742,9 +3045,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;
@@ -2758,7 +3061,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)
@@ -2767,7 +3070,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)
@@ -2799,8 +3102,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;
@@ -2814,7 +3116,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;
@@ -2832,8 +3134,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;
@@ -2856,7 +3157,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;
@@ -2868,8 +3169,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;
@@ -2891,7 +3191,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);
}
@@ -2903,8 +3203,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);
@@ -2922,7 +3221,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);
}
@@ -2944,15 +3243,20 @@ void QRhiVulkan::prepareUploadSubres(QVkTexture *texD, int layer, int level,
qsizetype copySizeBytes = 0;
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(&copyInfo, 0, sizeof(copyInfo));
+ VkBufferImageCopy copyInfo = {};
copyInfo.bufferOffset = *curOfs;
copyInfo.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
copyInfo.imageSubresource.mipLevel = uint32_t(level);
- copyInfo.imageSubresource.baseArrayLayer = uint32_t(layer);
+ copyInfo.imageSubresource.baseArrayLayer = is3D ? 0 : uint32_t(layer);
copyInfo.imageSubresource.layerCount = 1;
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();
@@ -3039,10 +3343,15 @@ 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);
- QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
for (int opIdx = 0; opIdx < ud->activeBufferOpCount; ++opIdx) {
const QRhiResourceUpdateBatchPrivate::BufferOp &u(ud->bufferOps[opIdx]);
@@ -3060,16 +3369,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;
@@ -3077,9 +3384,9 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
&bufD->stagingBuffers[currentFrameSlot], &allocation, nullptr);
if (err == VK_SUCCESS) {
bufD->stagingAllocations[currentFrameSlot] = allocation;
- QRHI_PROF_F(newBufferStagingArea(bufD, currentFrameSlot, quint32(bufD->m_size)));
} 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;
}
}
@@ -3091,18 +3398,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(&copyInfo, 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;
@@ -3128,7 +3434,6 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
bufD->stagingBuffers[currentFrameSlot] = VK_NULL_HANDLE;
bufD->stagingAllocations[currentFrameSlot] = nullptr;
releaseQueue.append(e);
- QRHI_PROF_F(releaseBufferStagingArea(bufD, currentFrameSlot));
}
} else if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::Read) {
QVkBuffer *bufD = QRHI_RES(QVkBuffer, u.buf);
@@ -3139,7 +3444,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)
@@ -3156,32 +3461,29 @@ 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;
VkResult err = vmaCreateBuffer(toVmaAllocator(allocator), &bufferInfo, &allocInfo, &readback.stagingBuf, &allocation, nullptr);
if (err == VK_SUCCESS) {
readback.stagingAlloc = allocation;
- QRHI_PROF_F(newReadbackBuffer(qint64(readback.stagingBuf), bufD, uint(readback.byteSize)));
} 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(&copyInfo, 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;
@@ -3202,22 +3504,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; layer < QRhi::MAX_LAYERS; ++layer) {
- for (int level = 0; level < QRhi::MAX_LEVELS; ++level) {
- for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.subresDesc[layer][level]))
+ 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 : 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;
@@ -3225,10 +3525,10 @@ 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;
- QRHI_PROF_F(newTextureStagingArea(utexD, currentFrameSlot, quint32(stagingSize)));
BufferImageCopyList copyInfos;
size_t curOfs = 0;
@@ -3240,19 +3540,19 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
continue;
}
- for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) {
- for (int level = 0; level < QRhi::MAX_LEVELS; ++level) {
+ 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, &copyInfos);
}
}
}
- 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);
@@ -3262,9 +3562,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;
@@ -3275,7 +3575,6 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
utexD->stagingBuffers[currentFrameSlot] = VK_NULL_HANDLE;
utexD->stagingAllocations[currentFrameSlot] = nullptr;
releaseQueue.append(e);
- QRHI_PROF_F(releaseTextureStagingArea(utexD, currentFrameSlot));
// Similarly to buffers, transitioning away from DST is done later,
// when a renderpass using the texture is encountered.
@@ -3289,25 +3588,29 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
}
QVkTexture *srcD = QRHI_RES(QVkTexture, u.src);
QVkTexture *dstD = QRHI_RES(QVkTexture, u.dst);
+ const bool srcIs3D = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
+ const bool dstIs3D = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
- VkImageCopy region;
- memset(&region, 0, sizeof(region));
-
+ VkImageCopy region = {};
region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.srcSubresource.mipLevel = uint32_t(u.desc.sourceLevel());
- region.srcSubresource.baseArrayLayer = uint32_t(u.desc.sourceLayer());
+ region.srcSubresource.baseArrayLayer = srcIs3D ? 0 : uint32_t(u.desc.sourceLayer());
region.srcSubresource.layerCount = 1;
region.srcOffset.x = u.desc.sourceTopLeft().x();
region.srcOffset.y = u.desc.sourceTopLeft().y();
+ if (srcIs3D)
+ region.srcOffset.z = uint32_t(u.desc.sourceLayer());
region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.dstSubresource.mipLevel = uint32_t(u.desc.destinationLevel());
- region.dstSubresource.baseArrayLayer = uint32_t(u.desc.destinationLayer());
+ region.dstSubresource.baseArrayLayer = dstIs3D ? 0 : uint32_t(u.desc.destinationLayer());
region.dstSubresource.layerCount = 1;
region.dstOffset.x = u.desc.destinationTopLeft().x();
region.dstOffset.y = u.desc.destinationTopLeft().y();
+ if (dstIs3D)
+ region.dstOffset.z = uint32_t(u.desc.destinationLayer());
const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize);
const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize();
@@ -3337,11 +3640,13 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
QVkTexture *texD = QRHI_RES(QVkTexture, u.rb.texture());
QVkSwapChain *swapChainD = nullptr;
+ bool is3D = false;
if (texD) {
if (texD->samples > VK_SAMPLE_COUNT_1_BIT) {
qWarning("Multisample texture cannot be read back");
continue;
}
+ is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
readback.pixelSize = q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize);
readback.format = texD->m_format;
texD->lastActiveFrameSlot = currentFrameSlot;
@@ -3353,7 +3658,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
continue;
}
readback.pixelSize = swapChainD->pixelSize;
- readback.format = colorTextureFormatFromVkFormat(swapChainD->colorFormat, nullptr);
+ readback.format = swapchainReadbackTextureFormat(swapChainD->colorFormat, nullptr);
if (readback.format == QRhiTexture::UnknownFormat)
continue;
@@ -3363,36 +3668,33 @@ 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;
VkResult err = vmaCreateBuffer(toVmaAllocator(allocator), &bufferInfo, &allocInfo, &readback.stagingBuf, &allocation, nullptr);
if (err == VK_SUCCESS) {
readback.stagingAlloc = allocation;
- QRHI_PROF_F(newReadbackBuffer(qint64(readback.stagingBuf),
- texD ? static_cast<QRhiResource *>(texD) : static_cast<QRhiResource *>(swapChainD),
- readback.byteSize));
} 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(&copyDesc, 0, sizeof(copyDesc));
+ VkBufferImageCopy copyDesc = {};
copyDesc.bufferOffset = 0;
copyDesc.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
copyDesc.imageSubresource.mipLevel = uint32_t(u.rb.level());
- copyDesc.imageSubresource.baseArrayLayer = uint32_t(u.rb.layer());
+ copyDesc.imageSubresource.baseArrayLayer = is3D ? 0 : uint32_t(u.rb.layer());
copyDesc.imageSubresource.layerCount = 1;
+ if (is3D)
+ copyDesc.imageOffset.z = u.rb.layer();
copyDesc.imageExtent.width = uint32_t(readback.pixelSize.width());
copyDesc.imageExtent.height = uint32_t(readback.pixelSize.height());
copyDesc.imageExtent.depth = 1;
@@ -3437,6 +3739,8 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
QVkTexture *utexD = QRHI_RES(QVkTexture, u.dst);
Q_ASSERT(utexD->m_flags.testFlag(QRhiTexture::UsedWithGenerateMips));
const bool isCube = utexD->m_flags.testFlag(QRhiTexture::CubeMap);
+ const bool isArray = utexD->m_flags.testFlag(QRhiTexture::TextureArray);
+ const bool is3D = utexD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
VkImageLayout origLayout = utexD->usageState.layout;
VkAccessFlags origAccess = utexD->usageState.access;
@@ -3444,9 +3748,10 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
if (!origStage)
origStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
- for (int layer = 0; layer < (isCube ? 6 : 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 ? qMax(1, utexD->m_depth) : 1;
for (int level = 1; level < int(utexD->mipLevelCount); ++level) {
if (level == 1) {
subresourceBarrier(cbD, utexD->image,
@@ -3471,9 +3776,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
layer, 1,
level, 1);
- VkImageBlit region;
- memset(&region, 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);
@@ -3481,7 +3784,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
region.srcOffsets[1].x = qMax(1, w);
region.srcOffsets[1].y = qMax(1, h);
- region.srcOffsets[1].z = 1;
+ region.srcOffsets[1].z = qMax(1, depth);
region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.dstSubresource.mipLevel = uint32_t(level);
@@ -3490,7 +3793,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
region.dstOffsets[1].x = qMax(1, w >> 1);
region.dstOffsets[1].y = qMax(1, h >> 1);
- region.dstOffsets[1].z = 1;
+ region.dstOffsets[1].z = qMax(1, depth >> 1);
QVkCommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QVkCommandBuffer::Command::BlitImage;
@@ -3503,6 +3806,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
w >>= 1;
h >>= 1;
+ depth >>= 1;
}
if (utexD->mipLevelCount > 1) {
@@ -3543,18 +3847,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();
}
@@ -3580,7 +3884,7 @@ static void qrhivk_releaseTexture(const QRhiVulkan::DeferredReleaseEntry &e, VkD
vmaDestroyImage(toVmaAllocator(allocator), e.texture.image, toVmaAllocation(e.texture.allocation));
for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
vmaDestroyBuffer(toVmaAllocator(allocator), e.texture.stagingBuffers[i], toVmaAllocation(e.texture.stagingAllocations[i]));
- for (int i = 0; i < QRhi::MAX_LEVELS; ++i) {
+ for (int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i) {
if (e.texture.extraImageViews[i])
df->vkDestroyImageView(dev, e.texture.extraImageViews[i], nullptr);
}
@@ -3593,7 +3897,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) {
@@ -3626,6 +3930,7 @@ 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);
break;
case QRhiVulkan::DeferredReleaseEntry::RenderPass:
df->vkDestroyRenderPass(dev, e.renderPass.rp, nullptr);
@@ -3648,9 +3953,8 @@ void QRhiVulkan::executeDeferredReleases(bool forced)
void QRhiVulkan::finishActiveReadbacks(bool forced)
{
QVarLengthArray<std::function<void()>, 4> completedCallbacks;
- QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
- 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;
@@ -3667,7 +3971,6 @@ void QRhiVulkan::finishActiveReadbacks(bool forced)
}
vmaDestroyBuffer(toVmaAllocator(allocator), readback.stagingBuf, a);
- QRHI_PROF_F(releaseReadbackBuffer(qint64(readback.stagingBuf)));
if (readback.result->completed)
completedCallbacks.append(readback.result->completed);
@@ -3676,7 +3979,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);
@@ -3684,14 +3987,13 @@ 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);
}
vmaDestroyBuffer(toVmaAllocator(allocator), readback.stagingBuf, a);
- QRHI_PROF_F(releaseReadbackBuffer(qint64(readback.stagingBuf)));
if (readback.result->completed)
completedCallbacks.append(readback.result->completed);
@@ -3738,33 +4040,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)
@@ -3867,17 +4162,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]);
@@ -3923,10 +4224,16 @@ static inline VkPipelineStageFlags toVkPipelineStage(QRhiPassResourceTracker::Bu
return VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
case QRhiPassResourceTracker::BufVertexStage:
return VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
+ case QRhiPassResourceTracker::BufTCStage:
+ return VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT;
+ case QRhiPassResourceTracker::BufTEStage:
+ return VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT;
case QRhiPassResourceTracker::BufFragmentStage:
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;
@@ -3989,6 +4296,10 @@ static inline VkPipelineStageFlags toVkPipelineStage(QRhiPassResourceTracker::Te
switch (stage) {
case QRhiPassResourceTracker::TexVertexStage:
return VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
+ case QRhiPassResourceTracker::TexTCStage:
+ return VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT;
+ case QRhiPassResourceTracker::TexTEStage:
+ return VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT;
case QRhiPassResourceTracker::TexFragmentStage:
return VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
case QRhiPassResourceTracker::TexColorOutputStage:
@@ -3997,6 +4308,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;
@@ -4066,8 +4379,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;
@@ -4091,8 +4403,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;
@@ -4117,10 +4428,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);
}
@@ -4194,13 +4513,13 @@ bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const
case QRhi::MultisampleRenderBuffer:
return true;
case QRhi::DebugMarkers:
- return debugMarkersAvailable;
+ return caps.debugUtils;
case QRhi::Timestamps:
return timestampValidBits != 0;
case QRhi::Instancing:
return true;
case QRhi::CustomInstanceStepRate:
- return vertexAttribDivisorAvailable;
+ return caps.vertexAttribDivisor;
case QRhi::PrimitiveRestart:
return true;
case QRhi::NonDynamicUniformBuffers:
@@ -4214,9 +4533,9 @@ bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const
case QRhi::ElementIndexUint:
return true;
case QRhi::Compute:
- return hasCompute;
+ return caps.compute;
case QRhi::WideLines:
- return hasWideLines;
+ return caps.wideLines;
case QRhi::VertexShaderPointSize:
return true;
case QRhi::BaseVertex:
@@ -4243,9 +4562,38 @@ bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const
return true;
case QRhi::ImageDataStride:
return true;
- default:
- Q_UNREACHABLE();
+ case QRhi::RenderBufferImport:
return false;
+ case QRhi::ThreeDimensionalTextures:
+ return true;
+ case QRhi::RenderTo3DTextureSlice:
+ return caps.texture3DSliceAs2D;
+ case QRhi::TextureArrays:
+ 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;
+ default:
+ Q_UNREACHABLE_RETURN(false);
}
}
@@ -4274,9 +4622,16 @@ int QRhiVulkan::resourceLimit(QRhi::ResourceLimit limit) const
return int(physDevProperties.limits.maxComputeWorkGroupSize[1]);
case QRhi::MaxThreadGroupZ:
return int(physDevProperties.limits.maxComputeWorkGroupSize[2]);
+ case QRhi::TextureArraySizeMax:
+ return int(physDevProperties.limits.maxImageArrayLayers);
+ case QRhi::MaxUniformBufferRange:
+ return int(qMin<uint32_t>(INT_MAX, physDevProperties.limits.maxUniformBufferRange));
+ case QRhi::MaxVertexInputs:
+ return physDevProperties.limits.maxVertexInputAttributes;
+ case QRhi::MaxVertexOutputs:
+ return physDevProperties.limits.maxVertexOutputComponents / 4;
default:
- Q_UNREACHABLE();
- return 0;
+ Q_UNREACHABLE_RETURN(0);
}
}
@@ -4290,16 +4645,24 @@ QRhiDriverInfo QRhiVulkan::driverInfo() const
return driverInfoStruct;
}
-void QRhiVulkan::sendVMemStatsToProfiler()
+QRhiStats QRhiVulkan::statistics()
{
- QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
- if (!rhiP)
- return;
+ QRhiStats result;
+ result.totalPipelineCreationTime = totalPipelineCreationTime();
+
+ VmaBudget budgets[VK_MAX_MEMORY_HEAPS];
+ vmaGetHeapBudgets(toVmaAllocator(allocator), budgets);
- VmaStats stats;
- vmaCalculateStats(toVmaAllocator(allocator), &stats);
- QRHI_PROF_F(vmemStat(stats.total.blockCount, stats.total.allocationCount,
- quint32(stats.total.usedBytes), quint32(stats.total.unusedBytes)));
+ 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()
@@ -4341,7 +4704,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);
@@ -4349,7 +4712,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();
}
@@ -4361,6 +4724,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);
@@ -4374,7 +4738,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;
@@ -4382,49 +4746,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;
}
@@ -4437,7 +4801,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");
}
}
@@ -4448,10 +4812,11 @@ QRhiRenderBuffer *QRhiVulkan::createRenderBuffer(QRhiRenderBuffer::Type type, co
return new QVkRenderBuffer(this, type, pixelSize, sampleCount, flags, backingFormatHint);
}
-QRhiTexture *QRhiVulkan::createTexture(QRhiTexture::Format format, const QSize &pixelSize,
+QRhiTexture *QRhiVulkan::createTexture(QRhiTexture::Format format,
+ const QSize &pixelSize, int depth, int arraySize,
int sampleCount, QRhiTexture::Flags flags)
{
- return new QVkTexture(this, format, pixelSize, sampleCount, flags);
+ return new QVkTexture(this, format, pixelSize, depth, arraySize, sampleCount, flags);
}
QRhiSampler *QRhiVulkan::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
@@ -4531,8 +4896,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:
@@ -4560,8 +4925,10 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin
}
break;
case QRhiShaderResourceBinding::SampledTexture:
+ case QRhiShaderResourceBinding::Texture:
+ case QRhiShaderResourceBinding::Sampler:
{
- const QRhiShaderResourceBinding::Data::SampledTextureData *data = &b->u.stex;
+ const QRhiShaderResourceBinding::Data::TextureAndOrSamplerData *data = &b->u.stex;
if (bd.stex.count != data->count) {
bd.stex.count = data->count;
rewriteDescSet = true;
@@ -4569,21 +4936,32 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin
for (int elem = 0; elem < data->count; ++elem) {
QVkTexture *texD = QRHI_RES(QVkTexture, data->texSamplers[elem].tex);
QVkSampler *samplerD = QRHI_RES(QVkSampler, data->texSamplers[elem].sampler);
- texD->lastActiveFrameSlot = currentFrameSlot;
- samplerD->lastActiveFrameSlot = currentFrameSlot;
- trackedRegisterTexture(&passResTracker, texD,
- QRhiPassResourceTracker::TexSample,
- QRhiPassResourceTracker::toPassTrackerTextureStage(b->stage));
- if (texD->generation != bd.stex.d[elem].texGeneration
- || texD->m_id != bd.stex.d[elem].texId
- || samplerD->generation != bd.stex.d[elem].samplerGeneration
- || samplerD->m_id != bd.stex.d[elem].samplerId)
+ // We use the same code path for both combined and separate
+ // images and samplers, so tex or sampler (but not both) can be
+ // null here.
+ Q_ASSERT(texD || samplerD);
+ if (texD) {
+ texD->lastActiveFrameSlot = currentFrameSlot;
+ trackedRegisterTexture(&passResTracker, texD,
+ QRhiPassResourceTracker::TexSample,
+ QRhiPassResourceTracker::toPassTrackerTextureStage(b->stage));
+ }
+ if (samplerD)
+ samplerD->lastActiveFrameSlot = currentFrameSlot;
+ const quint64 texId = texD ? texD->m_id : 0;
+ const uint texGen = texD ? texD->generation : 0;
+ const quint64 samplerId = samplerD ? samplerD->m_id : 0;
+ const uint samplerGen = samplerD ? samplerD->generation : 0;
+ if (texGen != bd.stex.d[elem].texGeneration
+ || texId != bd.stex.d[elem].texId
+ || samplerGen != bd.stex.d[elem].samplerGeneration
+ || samplerId != bd.stex.d[elem].samplerId)
{
rewriteDescSet = true;
- bd.stex.d[elem].texId = texD->m_id;
- bd.stex.d[elem].texGeneration = texD->generation;
- bd.stex.d[elem].samplerId = samplerD->m_id;
- bd.stex.d[elem].samplerGeneration = samplerD->generation;
+ bd.stex.d[elem].texId = texId;
+ bd.stex.d[elem].texGeneration = texGen;
+ bd.stex.d[elem].samplerId = samplerId;
+ bd.stex.d[elem].samplerGeneration = samplerGen;
}
}
}
@@ -4665,8 +5043,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) {
@@ -4686,8 +5064,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;
@@ -4695,9 +5073,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) {
@@ -4756,16 +5134,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());
}
}
@@ -4814,7 +5192,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());
@@ -4833,9 +5211,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);
@@ -4858,7 +5239,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());
@@ -4948,60 +5329,72 @@ void QRhiVulkan::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
void QRhiVulkan::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name)
{
- if (!debugMarkers || !debugMarkersAvailable)
+#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 || !debugMarkersAvailable)
+#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 || !debugMarkersAvailable)
+#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)
@@ -5015,8 +5408,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;
@@ -5082,23 +5475,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)
{
- if (!debugMarkers || !debugMarkersAvailable || name.isEmpty())
+ QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
+ return cbD->lastGpuTime;
+}
+
+void QRhiVulkan::setObjectName(uint64_t object, VkObjectType type, const QByteArray &name, int slot)
+{
+#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)
@@ -5123,8 +5528,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);
}
}
@@ -5138,8 +5542,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);
}
}
@@ -5153,8 +5556,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);
}
}
@@ -5163,13 +5565,18 @@ static inline VkShaderStageFlagBits toVkShaderStage(QRhiShaderStage::Type type)
switch (type) {
case QRhiShaderStage::Vertex:
return VK_SHADER_STAGE_VERTEX_BIT;
+ case QRhiShaderStage::TessellationControl:
+ return VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
+ case QRhiShaderStage::TessellationEvaluation:
+ return VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
case QRhiShaderStage::Fragment:
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);
}
}
@@ -5206,9 +5613,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);
}
}
@@ -5227,9 +5657,10 @@ static inline VkPrimitiveTopology toVkTopology(QRhiGraphicsPipeline::Topology t)
return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
case QRhiGraphicsPipeline::Points:
return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
+ 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);
}
}
@@ -5243,8 +5674,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);
}
}
@@ -5256,8 +5686,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);
}
}
@@ -5317,8 +5746,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);
}
}
@@ -5336,8 +5764,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);
}
}
@@ -5361,8 +5788,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);
}
}
@@ -5386,8 +5812,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);
}
}
@@ -5409,6 +5846,12 @@ static inline VkDescriptorType toVkDescriptorType(const QRhiShaderResourceBindin
case QRhiShaderResourceBinding::SampledTexture:
return VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+ case QRhiShaderResourceBinding::Texture:
+ return VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
+
+ case QRhiShaderResourceBinding::Sampler:
+ return VK_DESCRIPTOR_TYPE_SAMPLER;
+
case QRhiShaderResourceBinding::ImageLoad:
case QRhiShaderResourceBinding::ImageStore:
case QRhiShaderResourceBinding::ImageLoadStore:
@@ -5420,8 +5863,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);
}
}
@@ -5434,6 +5876,12 @@ static inline VkShaderStageFlags toVkShaderStageFlags(QRhiShaderResourceBinding:
s |= VK_SHADER_STAGE_FRAGMENT_BIT;
if (stage.testFlag(QRhiShaderResourceBinding::ComputeStage))
s |= VK_SHADER_STAGE_COMPUTE_BIT;
+ if (stage.testFlag(QRhiShaderResourceBinding::TessellationControlStage))
+ 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);
}
@@ -5457,12 +5905,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) {
@@ -5499,12 +5946,13 @@ void QVkBuffer::destroy()
}
QRHI_RES_RHI(QRhiVulkan);
- rhiD->releaseQueue.append(e);
-
- QRHI_PROF;
- QRHI_PROF_F(releaseBuffer(this));
-
- rhiD->unregisterResource(this);
+ // destroy() implementations, unlike other functions, are expected to test
+ // for m_rhi being null, to allow surviving in case one attempts to destroy
+ // a (leaked) resource after the QRhi.
+ if (rhiD) {
+ rhiD->releaseQueue.append(e);
+ rhiD->unregisterResource(this);
+ }
}
bool QVkBuffer::create()
@@ -5517,16 +5965,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
@@ -5554,19 +6000,17 @@ 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;
}
- QRHI_PROF;
- QRHI_PROF_F(newBuffer(this, uint(nonZeroSize), m_type != Dynamic ? 1 : QVK_FRAMES_IN_FLIGHT, 0));
-
lastActiveFrameSlot = -1;
generation += 1;
rhiD->registerResource(this);
@@ -5615,8 +6059,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,
@@ -5656,12 +6100,10 @@ void QVkRenderBuffer::destroy()
}
QRHI_RES_RHI(QRhiVulkan);
- rhiD->releaseQueue.append(e);
-
- QRHI_PROF;
- QRHI_PROF_F(releaseRenderBuffer(this));
-
- rhiD->unregisterResource(this);
+ if (rhiD) {
+ rhiD->releaseQueue.append(e);
+ rhiD->unregisterResource(this);
+ }
}
bool QVkRenderBuffer::create()
@@ -5673,8 +6115,7 @@ bool QVkRenderBuffer::create()
return false;
QRHI_RES_RHI(QRhiVulkan);
- QRHI_PROF;
- samples = rhiD->effectiveSampleCount(m_sampleCount);
+ samples = rhiD->effectiveSampleCountBits(m_sampleCount);
switch (m_type) {
case QRhiRenderBuffer::Color:
@@ -5682,6 +6123,8 @@ bool QVkRenderBuffer::create()
if (!backingTexture) {
backingTexture = QRHI_RES(QVkTexture, rhiD->createTexture(backingFormat(),
m_pixelSize,
+ 1,
+ 0,
m_sampleCount,
QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource));
} else {
@@ -5692,7 +6135,6 @@ bool QVkRenderBuffer::create()
if (!backingTexture->create())
return false;
vkformat = backingTexture->vkformat;
- QRHI_PROF_F(newRenderBuffer(this, false, false, samples));
}
break;
case QRhiRenderBuffer::DepthStencil:
@@ -5709,8 +6151,7 @@ bool QVkRenderBuffer::create()
{
return false;
}
- rhiD->setObjectName(uint64_t(image), VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT, m_objectName);
- QRHI_PROF_F(newRenderBuffer(this, true, false, samples));
+ rhiD->setObjectName(uint64_t(image), VK_OBJECT_TYPE_IMAGE, m_objectName);
break;
default:
Q_UNREACHABLE();
@@ -5718,6 +6159,7 @@ bool QVkRenderBuffer::create()
}
lastActiveFrameSlot = -1;
+ generation += 1;
rhiD->registerResource(this);
return true;
}
@@ -5730,15 +6172,15 @@ QRhiTexture::Format QVkRenderBuffer::backingFormat() const
return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
}
-QVkTexture::QVkTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
- int sampleCount, Flags flags)
- : QRhiTexture(rhi, format, pixelSize, sampleCount, flags)
+QVkTexture::QVkTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
+ int arraySize, int sampleCount, Flags flags)
+ : QRhiTexture(rhi, format, pixelSize, depth, arraySize, sampleCount, flags)
{
for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
stagingBuffers[i] = VK_NULL_HANDLE;
stagingAllocations[i] = nullptr;
}
- for (int i = 0; i < QRhi::MAX_LEVELS; ++i)
+ for (int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i)
perLevelImageViews[i] = VK_NULL_HANDLE;
}
@@ -5768,7 +6210,7 @@ void QVkTexture::destroy()
stagingAllocations[i] = nullptr;
}
- for (int i = 0; i < QRhi::MAX_LEVELS; ++i) {
+ for (int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i) {
e.texture.extraImageViews[i] = perLevelImageViews[i];
perLevelImageViews[i] = VK_NULL_HANDLE;
}
@@ -5778,12 +6220,10 @@ void QVkTexture::destroy()
imageAlloc = nullptr;
QRHI_RES_RHI(QRhiVulkan);
- rhiD->releaseQueue.append(e);
-
- QRHI_PROF;
- QRHI_PROF_F(releaseTexture(this));
-
- rhiD->unregisterResource(this);
+ if (rhiD) {
+ rhiD->releaseQueue.append(e);
+ rhiD->unregisterResource(this);
+ }
}
bool QVkTexture::prepareCreate(QSize *adjustedSize)
@@ -5793,6 +6233,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);
@@ -5801,27 +6250,64 @@ 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_LEVELS;
+ 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");
return false;
}
+ if (is3D) {
+ qWarning("3D texture cannot be multisample");
+ return false;
+ }
if (hasMipMaps) {
qWarning("Multisample texture cannot have mipmaps");
return false;
}
}
+ if (isCube && is3D) {
+ qWarning("Texture cannot be both cube and 3D");
+ return false;
+ }
+ if (isArray && is3D) {
+ qWarning("Texture cannot be both array and 3D");
+ return false;
+ }
+ 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;
+ }
+ if (m_arraySize > 0 && !isArray) {
+ qWarning("Texture cannot have an array size of %d when it is not an array", m_arraySize);
+ return false;
+ }
+ if (m_arraySize < 1 && isArray) {
+ qWarning("Texture is an array but array size is %d", m_arraySize);
+ return false;
+ }
usageState.layout = VK_IMAGE_LAYOUT_PREINITIALIZED;
usageState.access = 0;
@@ -5839,20 +6325,31 @@ bool QVkTexture::finishCreate()
const auto aspectMask = aspectMaskForTextureFormat(m_format);
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 : 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 : 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) {
@@ -5872,21 +6369,42 @@ bool QVkTexture::create()
if (!prepareCreate(&size))
return false;
+ QRHI_RES_RHI(QRhiVulkan);
const bool isRenderTarget = m_flags.testFlag(QRhiTexture::RenderTarget);
const bool isDepth = isDepthTextureFormat(m_format);
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 = isCube ? VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT : 0;
- imageInfo.imageType = VK_IMAGE_TYPE_2D;
+ imageInfo.flags = 0;
+ if (isCube)
+ imageInfo.flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
+
+ if (is3D && isRenderTarget) {
+ // This relies on a Vulkan 1.1 constant. For guaranteed proper behavior
+ // this also requires that at run time the VkInstance has at least API 1.1
+ // enabled. (though it works as expected with some Vulkan (1.2)
+ // implementations regardless of the requested API version, but f.ex. the
+ // validation layer complains when using this without enabling >=1.1)
+ if (!rhiD->caps.texture3DSliceAs2D)
+ qWarning("QRhiVulkan: Rendering to 3D texture slice may not be functional without API 1.1 on the VkInstance");
+#ifdef VK_VERSION_1_1
+ imageInfo.flags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT;
+#else
+ imageInfo.flags |= 0x00000020;
+#endif
+ }
+
+ 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 = 1;
+ imageInfo.extent.depth = is3D ? qMax(1, m_depth) : 1;
imageInfo.mipLevels = mipLevelCount;
- imageInfo.arrayLayers = isCube ? 6 : 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;
@@ -5905,15 +6423,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;
- QRHI_RES_RHI(QRhiVulkan);
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;
@@ -5921,10 +6444,7 @@ bool QVkTexture::create()
if (!finishCreate())
return false;
- rhiD->setObjectName(uint64_t(image), VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT, m_objectName);
-
- QRHI_PROF;
- QRHI_PROF_F(newTexture(this, true, int(mipLevelCount), isCube ? 6 : 1, samples));
+ rhiD->setObjectName(uint64_t(image), VK_OBJECT_TYPE_IMAGE, m_objectName);
owns = true;
rhiD->registerResource(this);
@@ -5945,9 +6465,6 @@ bool QVkTexture::createFrom(QRhiTexture::NativeTexture src)
if (!finishCreate())
return false;
- QRHI_PROF;
- QRHI_PROF_F(newTexture(this, false, int(mipLevelCount), m_flags.testFlag(CubeMap) ? 6 : 1, samples));
-
usageState.layout = VkImageLayout(src.layout);
owns = false;
@@ -5966,7 +6483,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)
@@ -5974,13 +6491,19 @@ VkImageView QVkTexture::imageViewForLevel(int level)
const VkImageAspectFlags aspectMask = aspectMaskForTextureFormat(m_format);
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 : 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;
@@ -5989,7 +6512,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 : 1;
+ viewInfo.subresourceRange.layerCount = isCube ? 6 : (isArray ? qMax(0, m_arraySize) : 1);
VkImageView v = VK_NULL_HANDLE;
QRHI_RES_RHI(QRhiVulkan);
@@ -6027,8 +6550,10 @@ void QVkSampler::destroy()
sampler = VK_NULL_HANDLE;
QRHI_RES_RHI(QRhiVulkan);
- rhiD->releaseQueue.append(e);
- rhiD->unregisterResource(this);
+ if (rhiD) {
+ rhiD->releaseQueue.append(e);
+ rhiD->unregisterResource(this);
+ }
}
bool QVkSampler::create()
@@ -6036,8 +6561,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);
@@ -6066,6 +6590,7 @@ bool QVkSampler::create()
QVkRenderPassDescriptor::QVkRenderPassDescriptor(QRhiImplementation *rhi)
: QRhiRenderPassDescriptor(rhi)
{
+ serializedFormatData.reserve(32);
}
QVkRenderPassDescriptor::~QVkRenderPassDescriptor()
@@ -6092,9 +6617,10 @@ void QVkRenderPassDescriptor::destroy()
rp = VK_NULL_HANDLE;
QRHI_RES_RHI(QRhiVulkan);
- rhiD->releaseQueue.append(e);
-
- rhiD->unregisterResource(this);
+ if (rhiD) {
+ rhiD->releaseQueue.append(e);
+ rhiD->unregisterResource(this);
+ }
}
static inline bool attachmentDescriptionEquals(const VkAttachmentDescription &a, const VkAttachmentDescription &b)
@@ -6119,16 +6645,18 @@ 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 (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;
@@ -6144,7 +6672,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;
@@ -6157,6 +6685,49 @@ bool QVkRenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other
return true;
}
+void QVkRenderPassDescriptor::updateSerializedFormat()
+{
+ serializedFormatData.clear();
+ auto p = std::back_inserter(serializedFormatData);
+
+ *p++ = attDescs.size();
+ *p++ = colorRefs.size();
+ *p++ = resolveRefs.size();
+ *p++ = hasDepthStencil;
+ *p++ = multiViewCount;
+
+ auto serializeAttachmentData = [this, &p](uint32_t attIdx) {
+ const bool used = attIdx != VK_ATTACHMENT_UNUSED;
+ const VkAttachmentDescription *a = used ? &attDescs[attIdx] : nullptr;
+ *p++ = used ? a->format : 0;
+ *p++ = used ? a->samples : 0;
+ *p++ = used ? a->loadOp : 0;
+ *p++ = used ? a->storeOp : 0;
+ *p++ = used ? a->stencilLoadOp : 0;
+ *p++ = used ? a->stencilStoreOp : 0;
+ *p++ = used ? a->initialLayout : 0;
+ *p++ = used ? a->finalLayout : 0;
+ };
+
+ for (int i = 0, ie = colorRefs.size(); i != ie; ++i) {
+ const uint32_t attIdx = colorRefs[i].attachment;
+ *p++ = attIdx;
+ serializeAttachmentData(attIdx);
+ }
+
+ if (hasDepthStencil) {
+ const uint32_t attIdx = dsRef.attachment;
+ *p++ = attIdx;
+ serializeAttachmentData(attIdx);
+ }
+
+ for (int i = 0, ie = resolveRefs.size(); i != ie; ++i) {
+ const uint32_t attIdx = resolveRefs[i].attachment;
+ *p++ = attIdx;
+ serializeAttachmentData(attIdx);
+ }
+}
+
QRhiRenderPassDescriptor *QVkRenderPassDescriptor::newCompatibleRenderPassDescriptor() const
{
QVkRenderPassDescriptor *rpD = new QVkRenderPassDescriptor(m_rhi);
@@ -6167,6 +6738,7 @@ QRhiRenderPassDescriptor *QVkRenderPassDescriptor::newCompatibleRenderPassDescri
rpD->resolveRefs = resolveRefs;
rpD->subpassDeps = subpassDeps;
rpD->hasDepthStencil = hasDepthStencil;
+ rpD->multiViewCount = multiViewCount;
rpD->dsRef = dsRef;
VkRenderPassCreateInfo rpInfo;
@@ -6174,6 +6746,12 @@ QRhiRenderPassDescriptor *QVkRenderPassDescriptor::newCompatibleRenderPassDescri
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);
@@ -6181,42 +6759,48 @@ QRhiRenderPassDescriptor *QVkRenderPassDescriptor::newCompatibleRenderPassDescri
return nullptr;
}
+ rpD->updateSerializedFormat();
rhiD->registerResource(rpD);
return rpD;
}
+QVector<quint32> QVkRenderPassDescriptor::serializedFormat() const
+{
+ return serializedFormatData;
+}
+
const QRhiNativeHandles *QVkRenderPassDescriptor::nativeHandles()
{
nativeHandlesStruct.renderPass = rp;
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;
}
@@ -6256,10 +6840,14 @@ void QVkTextureRenderTarget::destroy()
resrtv[att] = VK_NULL_HANDLE;
}
- QRHI_RES_RHI(QRhiVulkan);
- rhiD->releaseQueue.append(e);
+ e.textureRenderTarget.dsv = dsv;
+ dsv = VK_NULL_HANDLE;
- rhiD->unregisterResource(this);
+ QRHI_RES_RHI(QRhiVulkan);
+ if (rhiD) {
+ rhiD->releaseQueue.append(e);
+ rhiD->unregisterResource(this);
+ }
}
QRhiRenderPassDescriptor *QVkTextureRenderTarget::newCompatibleRenderPassDescriptor()
@@ -6273,6 +6861,7 @@ 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.depthStencilBuffer(),
m_desc.depthTexture()))
{
@@ -6281,6 +6870,7 @@ QRhiRenderPassDescriptor *QVkTextureRenderTarget::newCompatibleRenderPassDescrip
}
rp->ownsRp = true;
+ rp->updateSerializedFormat();
rhiD->registerResource(rp);
return rp;
}
@@ -6290,13 +6880,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;
@@ -6307,12 +6897,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;
@@ -6321,7 +6916,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);
@@ -6346,7 +6941,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;
@@ -6366,18 +6979,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;
@@ -6386,7 +7000,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);
@@ -6402,8 +7016,7 @@ bool QVkTextureRenderTarget::create()
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);
@@ -6418,6 +7031,8 @@ bool QVkTextureRenderTarget::create()
return false;
}
+ QRhiRenderTargetAttachmentTracker::updateResIdList<QVkTexture, QVkRenderBuffer>(m_desc, &d.currentResIdList);
+
lastActiveFrameSlot = -1;
rhiD->registerResource(this);
return true;
@@ -6425,6 +7040,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;
}
@@ -6468,9 +7086,10 @@ void QVkShaderResourceBindings::destroy()
descSets[i] = VK_NULL_HANDLE;
QRHI_RES_RHI(QRhiVulkan);
- rhiD->releaseQueue.append(e);
-
- rhiD->unregisterResource(this);
+ if (rhiD) {
+ rhiD->releaseQueue.append(e);
+ rhiD->unregisterResource(this);
+ }
}
bool QVkShaderResourceBindings::create()
@@ -6489,16 +7108,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;
@@ -6508,13 +7123,12 @@ 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)
+ if (b->type == QRhiShaderResourceBinding::SampledTexture || b->type == QRhiShaderResourceBinding::Texture)
vkbinding.descriptorCount = b->u.stex.count;
else
vkbinding.descriptorCount = 1;
@@ -6522,10 +7136,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);
@@ -6534,8 +7147,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];
@@ -6546,7 +7158,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));
}
@@ -6557,6 +7169,31 @@ bool QVkShaderResourceBindings::create()
return true;
}
+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(), 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
+ // detect changes in the associated data, such as the buffer offset. And
+ // just like after a create(), a call to updateResources() may lead to now
+ // specifying a different offset for the same QRhiBuffer for a given binding
+ // point. The same applies to other type of associated data that is not part
+ // of the layout, such as the mip level for a StorageImage. Instead of
+ // 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].size() == sortedBindings.size());
+ for (BoundResourceData &bd : boundResourceData[i])
+ memset(&bd, 0, sizeof(BoundResourceData));
+ }
+
+ generation += 1;
+}
+
QVkGraphicsPipeline::QVkGraphicsPipeline(QRhiImplementation *rhi)
: QRhiGraphicsPipeline(rhi)
{
@@ -6583,9 +7220,10 @@ void QVkGraphicsPipeline::destroy()
layout = VK_NULL_HANDLE;
QRHI_RES_RHI(QRhiVulkan);
- rhiD->releaseQueue.append(e);
-
- rhiD->unregisterResource(this);
+ if (rhiD) {
+ rhiD->releaseQueue.append(e);
+ rhiD->unregisterResource(this);
+ }
}
bool QVkGraphicsPipeline::create()
@@ -6594,14 +7232,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);
@@ -6613,8 +7251,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;
@@ -6629,8 +7266,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;
@@ -6638,11 +7274,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)
@@ -6654,9 +7292,12 @@ bool QVkGraphicsPipeline::create()
? VK_VERTEX_INPUT_RATE_VERTEX : VK_VERTEX_INPUT_RATE_INSTANCE
};
if (it->classification() == QRhiVertexInputBinding::PerInstance && it->instanceStepRate() != 1) {
- if (rhiD->vertexAttribDivisorAvailable) {
- nonOneStepRates.append({ uint32_t(bindingIndex), uint32_t(it->instanceStepRate()) });
- } else {
+#ifdef VK_EXT_vertex_attribute_divisor
+ if (rhiD->caps.vertexAttribDivisor) {
+ 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");
@@ -6676,21 +7317,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;
@@ -6701,28 +7342,52 @@ 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;
- VkPipelineRasterizationStateCreateInfo rastInfo;
- memset(&rastInfo, 0, sizeof(rastInfo));
+ VkPipelineTessellationStateCreateInfo tessInfo = {};
+#ifdef VK_VERSION_1_1
+ VkPipelineTessellationDomainOriginStateCreateInfo originInfo = {};
+#endif
+ if (m_topology == Patches) {
+ tessInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO;
+ tessInfo.patchControlPoints = uint32_t(qMax(1, m_patchControlPointCount));
+
+ // To be able to use the same tess.evaluation shader with both OpenGL
+ // and Vulkan, flip the tessellation domain origin to be lower left.
+ // This allows declaring the winding order in the shader to be CCW and
+ // 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.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;
+ } else {
+ qWarning("Proper tessellation support requires Vulkan 1.1 or newer, leaving domain origin unset");
+ }
+#else
+ qWarning("QRhi was built without Vulkan 1.1 headers, this is not sufficient for proper tessellation support");
+#endif
+
+ pipelineInfo.pTessellationState = &tessInfo;
+ }
+
+ VkPipelineRasterizationStateCreateInfo rastInfo = {};
rastInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rastInfo.cullMode = toVkCullMode(m_cullMode);
rastInfo.frontFace = toVkFrontFace(m_frontFace);
@@ -6731,17 +7396,16 @@ bool QVkGraphicsPipeline::create()
rastInfo.depthBiasConstantFactor = float(m_depthBias);
rastInfo.depthBiasSlopeFactor = m_slopeScaledDepthBias;
}
- rastInfo.lineWidth = rhiD->hasWideLines ? m_lineWidth : 1.0f;
+ 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;
@@ -6757,13 +7421,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);
@@ -6775,13 +7437,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;
@@ -6800,6 +7461,7 @@ bool QVkGraphicsPipeline::create()
return false;
}
+ rhiD->pipelineCreationEnd();
lastActiveFrameSlot = -1;
generation += 1;
rhiD->registerResource(this);
@@ -6832,9 +7494,10 @@ void QVkComputePipeline::destroy()
layout = VK_NULL_HANDLE;
QRHI_RES_RHI(QRhiVulkan);
- rhiD->releaseQueue.append(e);
-
- rhiD->unregisterResource(this);
+ if (rhiD) {
+ rhiD->releaseQueue.append(e);
+ rhiD->unregisterResource(this);
+ }
}
bool QVkComputePipeline::create()
@@ -6843,11 +7506,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);
@@ -6859,8 +7522,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;
@@ -6879,8 +7541,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;
@@ -6894,6 +7555,7 @@ bool QVkComputePipeline::create()
return false;
}
+ rhiD->pipelineCreationEnd();
lastActiveFrameSlot = -1;
generation += 1;
rhiD->registerResource(this);
@@ -6938,7 +7600,8 @@ const QRhiNativeHandles *QVkCommandBuffer::nativeHandles()
QVkSwapChain::QVkSwapChain(QRhiImplementation *rhi)
: QRhiSwapChain(rhi),
- rtWrapper(rhi),
+ rtWrapper(rhi, this),
+ rtWrapperRight(rhi, this),
cbWrapper(rhi)
{
}
@@ -6954,8 +7617,10 @@ void QVkSwapChain::destroy()
return;
QRHI_RES_RHI(QRhiVulkan);
- rhiD->swapchains.remove(this);
- rhiD->releaseSwapChainResources(this);
+ if (rhiD) {
+ rhiD->swapchains.remove(this);
+ rhiD->releaseSwapChainResources(this);
+ }
for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
QVkSwapChain::FrameResources &frame(frameRes[i]);
@@ -6965,10 +7630,8 @@ void QVkSwapChain::destroy()
surface = lastConnectedSurface = VK_NULL_HANDLE;
- QRHI_PROF;
- QRHI_PROF_F(releaseSwapChain(this));
-
- rhiD->unregisterResource(this);
+ if (rhiD)
+ rhiD->unregisterResource(this);
}
QRhiCommandBuffer *QVkSwapChain::currentFrameCommandBuffer()
@@ -6981,6 +7644,11 @@ QRhiRenderTarget *QVkSwapChain::currentFrameRenderTarget()
return &rtWrapper;
}
+QRhiRenderTarget *QVkSwapChain::currentFrameRenderTarget(StereoTargetBuffer targetBuffer)
+{
+ return !stereo || targetBuffer == StereoTargetBuffer::LeftBuffer ? &rtWrapper : &rtWrapperRight;
+}
+
QSize QVkSwapChain::surfacePixelSize()
{
if (!ensureSurface())
@@ -6988,8 +7656,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;
@@ -7000,6 +7667,52 @@ QSize QVkSwapChain::surfacePixelSize()
return QSize(int(bufferSize.width), int(bufferSize.height));
}
+static inline bool hdrFormatMatchesVkSurfaceFormat(QRhiSwapChain::Format f, const VkSurfaceFormatKHR &s)
+{
+ switch (f) {
+ case QRhiSwapChain::HDRExtendedSrgbLinear:
+ return s.format == VK_FORMAT_R16G16B16A16_SFLOAT
+ && s.colorSpace == VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT;
+ 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;
+ }
+ return false;
+}
+
+bool QVkSwapChain::isFormatSupported(Format f)
+{
+ if (f == SDR)
+ return true;
+
+ if (!m_window) {
+ qWarning("Attempted to call isFormatSupported() without a window set");
+ return false;
+ }
+
+ // we may be called before create so query the surface
+ VkSurfaceKHR surf = QVulkanInstance::surfaceForWindow(m_window);
+
+ QRHI_RES_RHI(QRhiVulkan);
+ uint32_t formatCount = 0;
+ rhiD->vkGetPhysicalDeviceSurfaceFormatsKHR(rhiD->physDev, surf, &formatCount, nullptr);
+ QVarLengthArray<VkSurfaceFormatKHR, 8> formats(formatCount);
+ if (formatCount) {
+ rhiD->vkGetPhysicalDeviceSurfaceFormatsKHR(rhiD->physDev, surf, &formatCount, formats.data());
+ for (uint32_t i = 0; i < formatCount; ++i) {
+ if (hdrFormatMatchesVkSurfaceFormat(f, formats[i]))
+ return true;
+ }
+ }
+
+ return false;
+}
+
QRhiRenderPassDescriptor *QVkSwapChain::newCompatibleRenderPassDescriptor()
{
// not yet built so cannot rely on data computed in createOrResize()
@@ -7019,6 +7732,7 @@ QRhiRenderPassDescriptor *QVkSwapChain::newCompatibleRenderPassDescriptor()
}
rp->ownsRp = true;
+ rp->updateSerializedFormat();
rhiD->registerResource(rp);
return rp;
}
@@ -7058,27 +7772,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->vkGetPhysicalDeviceSurfaceCapabilitiesKHR) {
- rhiD->vkGetPhysicalDeviceSurfaceCapabilitiesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR>(
- rhiD->inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfaceCapabilitiesKHR"));
- rhiD->vkGetPhysicalDeviceSurfaceFormatsKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceFormatsKHR>(
- rhiD->inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfaceFormatsKHR"));
- rhiD->vkGetPhysicalDeviceSurfacePresentModesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfacePresentModesKHR>(
- rhiD->inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfacePresentModesKHR"));
- if (!rhiD->vkGetPhysicalDeviceSurfaceCapabilitiesKHR
- || !rhiD->vkGetPhysicalDeviceSurfaceFormatsKHR
- || !rhiD->vkGetPhysicalDeviceSurfacePresentModesKHR)
- {
- qWarning("Physical device surface queries not available");
- return false;
- }
+ if (!rhiD->inst->supportsPresent(rhiD->physDev, rhiD->gfxQueueFamilyIdx, m_window)) {
+ qWarning("Presenting not supported on this window");
+ return false;
}
quint32 formatCount = 0;
@@ -7087,16 +7783,23 @@ bool QVkSwapChain::ensureSurface()
if (formatCount)
rhiD->vkGetPhysicalDeviceSurfaceFormatsKHR(rhiD->physDev, surface, &formatCount, formats.data());
+ // See if there is a better match than the default BGRA8 format. (but if
+ // not, we will stick to the default)
const bool srgbRequested = m_flags.testFlag(sRGB);
for (int i = 0; i < int(formatCount); ++i) {
- if (formats[i].format != VK_FORMAT_UNDEFINED && srgbRequested == isSrgbFormat(formats[i].format)) {
- colorFormat = formats[i].format;
- colorSpace = formats[i].colorSpace;
- break;
+ if (formats[i].format != VK_FORMAT_UNDEFINED) {
+ 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;
+ break;
+ }
}
}
- samples = rhiD->effectiveSampleCount(m_sampleCount);
+ samples = rhiD->effectiveSampleCountBits(m_sampleCount);
quint32 presModeCount = 0;
rhiD->vkGetPhysicalDeviceSurfacePresentModesKHR(rhiD->physDev, surface, &presModeCount, nullptr);
@@ -7152,6 +7855,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);
@@ -7179,8 +7883,7 @@ 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);
@@ -7196,10 +7899,56 @@ bool QVkSwapChain::createOrResize()
}
}
- frameCount = 0;
+ 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);
- QRHI_PROF;
- QRHI_PROF_F(resizeSwapChain(this, QVK_FRAMES_IN_FLIGHT, samples > VK_SAMPLE_COUNT_1_BIT ? QVK_FRAMES_IN_FLIGHT : 0, samples));
+ 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;
+ }
+ 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);
+ 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)
rhiD->registerResource(this);