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.cpp3816
1 files changed, 2590 insertions, 1226 deletions
diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp
index e4ad562805..f0b51146cc 100644
--- a/src/gui/rhi/qrhivulkan.cpp
+++ b/src/gui/rhi/qrhivulkan.cpp
@@ -1,43 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the Qt Gui module
-**
-** $QT_BEGIN_LICENSE:LGPL3$
-** 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 http://www.qt.io/terms-conditions. For further
-** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free
-** Software Foundation and appearing in the file LICENSE.GPL included in
-** the packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 2.0 requirements will be
-** met: http://www.gnu.org/licenses/gpl-2.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
@@ -46,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
@@ -87,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:
@@ -101,20 +76,8 @@ 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.setExtensions(QByteArrayList()
- << "VK_KHR_get_physical_device_properties2");
+ inst.setLayers({ "VK_LAYER_KHRONOS_validation" }); // for debugging only, not for release builds
+ inst.setExtensions(QRhiVulkanInitParams::preferredInstanceExtensions());
if (!inst.create())
qFatal("Vulkan not available");
@@ -122,16 +85,18 @@ QT_BEGIN_NAMESPACE
}
\endcode
- The example here has two optional aspects: it enables the
+ This example enables the
\l{https://github.com/KhronosGroup/Vulkan-ValidationLayers}{Vulkan
validation layers}, when they are available, and also enables the
- VK_KHR_get_physical_device_properties2 extension (part of Vulkan 1.1), when
- available. The former is useful during the development phase (remember that
- QVulkanInstance conveniently redirects messages and warnings to qDebug).
- Avoid enabling it in production builds, however. The latter is important in
- order to make QRhi::CustomInstanceStepRate available with Vulkan since
- VK_EXT_vertex_attribute_divisor (part of Vulkan 1.1) depends on it. It can
- be omitted when instanced drawing with a non-one step rate is not used.
+ instance-level extensions QRhi reports as desirable (such as,
+ VK_KHR_get_physical_device_properties2), as long as they are supported by
+ the Vulkan implementation at run time.
+
+ The former is optional, and is useful during the development phase
+ QVulkanInstance conveniently redirects messages and warnings to qDebug.
+ Avoid enabling it in production builds, however. The latter is strongly
+ recommended, and is important in order to make certain features functional
+ (for example, QRhi::CustomInstanceStepRate).
Once this is done, a Vulkan-based QRhi can be created by passing the
instance and a QWindow with its surface type set to
@@ -156,6 +121,22 @@ QT_BEGIN_NAMESPACE
in deviceExtensions. This can be relevant when integrating with native Vulkan
rendering code.
+ 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
When interoperating with another graphics engine, it may be necessary to
@@ -179,37 +160,129 @@ QT_BEGIN_NAMESPACE
memory allocator} between two QRhi instances.
The QRhi does not take ownership of any of the external objects.
+
+ Applications are encouraged to query the list of desired device extensions
+ by calling the static function preferredExtensionsForImportedDevice(), and
+ enable them on the VkDevice. Otherwise certain QRhi features may not be
+ available.
*/
/*!
+ \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)
{
@@ -218,94 +291,53 @@ 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)
-{
- globalVulkanInstance->deviceFunctions(device)->vkUnmapMemory(device, memory);
-}
-
-VkResult VKAPI_PTR wrap_vkFlushMappedMemoryRanges(VkDevice device, uint32_t memoryRangeCount, const VkMappedMemoryRange* pMemoryRanges)
-{
- return globalVulkanInstance->deviceFunctions(device)->vkFlushMappedMemoryRanges(device, memoryRangeCount, pMemoryRanges);
-}
-
-VkResult VKAPI_PTR wrap_vkInvalidateMappedMemoryRanges(VkDevice device, uint32_t memoryRangeCount, const VkMappedMemoryRange* pMemoryRanges)
+static VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL wrap_vkGetInstanceProcAddr(VkInstance, const char *pName)
{
- return globalVulkanInstance->deviceFunctions(device)->vkInvalidateMappedMemoryRanges(device, memoryRangeCount, pMemoryRanges);
+ return globalVulkanInstance->getInstanceProcAddr(pName);
}
-VkResult VKAPI_PTR wrap_vkBindBufferMemory(VkDevice device, VkBuffer buffer, VkDeviceMemory memory, VkDeviceSize memoryOffset)
+static VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL wrap_vkGetDeviceProcAddr(VkDevice device, const char *pName)
{
- return globalVulkanInstance->deviceFunctions(device)->vkBindBufferMemory(device, buffer, memory, memoryOffset);
+ return globalVulkanInstance->functions()->vkGetDeviceProcAddr(device, pName);
}
-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)
+static inline VmaAllocation toVmaAllocation(QVkAlloc a)
{
- globalVulkanInstance->deviceFunctions(device)->vkDestroyBuffer(device, buffer, pAllocator);
+ return reinterpret_cast<VmaAllocation>(a);
}
-VkResult VKAPI_PTR wrap_vkCreateImage(VkDevice device, const VkImageCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkImage* pImage)
+static inline VmaAllocator toVmaAllocator(QVkAllocator a)
{
- return globalVulkanInstance->deviceFunctions(device)->vkCreateImage(device, pCreateInfo, pAllocator, pImage);
+ return reinterpret_cast<VmaAllocator>(a);
}
-void VKAPI_PTR wrap_vkDestroyImage(VkDevice device, VkImage image, const VkAllocationCallbacks* pAllocator)
-{
- globalVulkanInstance->deviceFunctions(device)->vkDestroyImage(device, image, pAllocator);
-}
+/*!
+ \return the list of instance extensions that are expected to be enabled on
+ the QVulkanInstance that is used for the Vulkan-based QRhi.
-static inline VmaAllocation toVmaAllocation(QVkAlloc a)
+ The returned list can be safely passed to QVulkanInstance::setExtensions()
+ as-is, because unsupported extensions are filtered out automatically.
+ */
+QByteArrayList QRhiVulkanInitParams::preferredInstanceExtensions()
{
- return reinterpret_cast<VmaAllocation>(a);
+ return {
+ QByteArrayLiteral("VK_KHR_get_physical_device_properties2")
+ };
}
-static inline VmaAllocator toVmaAllocator(QVkAllocator a)
+/*!
+ \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 reinterpret_cast<VmaAllocator>(a);
+ return {
+ QByteArrayLiteral("VK_KHR_swapchain"),
+ QByteArrayLiteral("VK_EXT_vertex_attribute_divisor"),
+ QByteArrayLiteral("VK_KHR_create_renderpass2"),
+ QByteArrayLiteral("VK_KHR_depth_stencil_resolve")
+ };
}
QRhiVulkan::QRhiVulkan(QRhiVulkanInitParams *params, QRhiVulkanNativeHandles *importParams)
@@ -323,10 +355,6 @@ QRhiVulkan::QRhiVulkan(QRhiVulkanInitParams *params, QRhiVulkanNativeHandles *im
gfxQueueFamilyIdx = importParams->gfxQueueFamilyIdx;
gfxQueueIdx = importParams->gfxQueueIdx;
// gfxQueue is output only, no point in accepting it as input
- if (importParams->cmdPool) {
- importedCmdPool = true;
- cmdPool = importParams->cmdPool;
- }
if (importParams->vmemAllocator) {
importedAllocator = true;
allocator = importParams->vmemAllocator;
@@ -335,20 +363,19 @@ QRhiVulkan::QRhiVulkan(QRhiVulkanInitParams *params, QRhiVulkanNativeHandles *im
}
}
-static bool qvk_debug_filter(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object,
- size_t location, int32_t messageCode, const char *pLayerPrefix, const char *pMessage)
+static bool qvk_debug_filter(QVulkanInstance::DebugMessageSeverityFlags severity,
+ QVulkanInstance::DebugMessageTypeFlags type,
+ const void *callbackData)
{
- Q_UNUSED(flags);
- Q_UNUSED(objectType);
- Q_UNUSED(object);
- Q_UNUSED(location);
- Q_UNUSED(messageCode);
- Q_UNUSED(pLayerPrefix);
+ Q_UNUSED(severity);
+ Q_UNUSED(type);
+#ifdef VK_EXT_debug_utils
+ const VkDebugUtilsMessengerCallbackDataEXT *d = static_cast<const VkDebugUtilsMessengerCallbackDataEXT *>(callbackData);
// Filter out certain misleading validation layer messages, as per
// VulkanMemoryAllocator documentation.
- if (strstr(pMessage, "Mapping an image with layout")
- && strstr(pMessage, "can result in undefined behavior if this memory is used by the device"))
+ if (strstr(d->pMessage, "Mapping an image with layout")
+ && strstr(d->pMessage, "can result in undefined behavior if this memory is used by the device"))
{
return true;
}
@@ -359,25 +386,53 @@ 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;
}
+static inline QRhiDriverInfo::DeviceType toRhiDeviceType(VkPhysicalDeviceType type)
+{
+ switch (type) {
+ case VK_PHYSICAL_DEVICE_TYPE_OTHER:
+ return QRhiDriverInfo::UnknownDevice;
+ case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
+ return QRhiDriverInfo::IntegratedDevice;
+ case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
+ return QRhiDriverInfo::DiscreteDevice;
+ case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
+ return QRhiDriverInfo::VirtualDevice;
+ case VK_PHYSICAL_DEVICE_TYPE_CPU:
+ return QRhiDriverInfo::CpuDevice;
+ default:
+ return QRhiDriverInfo::UnknownDevice;
+ }
+}
+
bool QRhiVulkan::create(QRhi::Flags flags)
{
- Q_UNUSED(flags);
Q_ASSERT(inst);
-
if (!inst->isValid()) {
qWarning("Vulkan instance is not valid");
return false;
}
- globalVulkanInstance = inst; // assume this will not change during the lifetime of the entire application
+ rhiFlags = flags;
+ qCDebug(QRHI_LOG_INFO, "Initializing QRhi Vulkan backend %p with flags %d", this, int(rhiFlags));
+ globalVulkanInstance = inst; // used for function resolving in vkmemalloc callbacks
f = inst->functions();
+ if (QRHI_LOG_INFO().isEnabled(QtDebugMsg)) {
+ qCDebug(QRHI_LOG_INFO, "Enabled instance extensions:");
+ for (const char *ext : inst->extensions())
+ qCDebug(QRHI_LOG_INFO, " %s", ext);
+ }
+
+ caps = {};
+ caps.debugUtils = inst->extensions().contains(QByteArrayLiteral("VK_EXT_debug_utils"));
QList<VkQueueFamilyProperties> queueFamilyProps;
auto queryQueueFamilyProps = [this, &queueFamilyProps] {
@@ -458,49 +513,129 @@ bool QRhiVulkan::create(QRhi::Flags flags)
physDevProperties.deviceType);
}
- f->vkGetPhysicalDeviceFeatures(physDev, &physDevFeatures);
+ 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);
+
+ 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;
@@ -508,44 +643,69 @@ bool QRhiVulkan::create(QRhi::Flags flags)
if (devExtCount) {
QList<VkExtensionProperties> extProps(devExtCount);
f->vkEnumerateDeviceExtensionProperties(physDev, nullptr, &devExtCount, extProps.data());
- for (const VkExtensionProperties &p : qAsConst(extProps))
+ for (const VkExtensionProperties &p : std::as_const(extProps))
devExts.append({ p.extensionName, p.specVersion });
}
- qCDebug(QRHI_LOG_INFO, "%d device extensions available", int(devExts.count()));
+ qCDebug(QRHI_LOG_INFO, "%d device extensions available", int(devExts.size()));
QList<const char *> requestedDevExts;
requestedDevExts.append("VK_KHR_swapchain");
- 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;
+#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
+
+#ifdef VK_KHR_create_renderpass2
+ if (devExts.contains(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME)) {
+ requestedDevExts.append(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME);
+ caps.renderPass2KHR = true;
+ }
+#endif
+
+#ifdef VK_KHR_depth_stencil_resolve
+ if (devExts.contains(VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME)) {
+ requestedDevExts.append(VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME);
+ caps.depthStencilResolveKHR = true;
+ }
+#endif
for (const QByteArray &ext : requestedDeviceExtensions) {
- if (!ext.isEmpty()) {
- if (devExts.contains(ext))
+ if (!ext.isEmpty() && !requestedDevExts.contains(ext)) {
+ if (devExts.contains(ext)) {
requestedDevExts.append(ext.constData());
- else
- qWarning("Device extension %s is not supported", ext.constData());
+ } else {
+ qWarning("Device extension %s requested in QRhiVulkanInitParams is not supported",
+ ext.constData());
+ }
}
}
QByteArrayList envExtList = qgetenv("QT_VULKAN_DEVICE_EXTENSIONS").split(';');
for (const QByteArray &ext : envExtList) {
if (!ext.isEmpty() && !requestedDevExts.contains(ext)) {
- if (devExts.contains(ext))
+ if (devExts.contains(ext)) {
requestedDevExts.append(ext.constData());
- else
- qWarning("Device extension %s is not supported", ext.constData());
+ } else {
+ qWarning("Device extension %s requested in QT_VULKAN_DEVICE_EXTENSIONS is not supported",
+ ext.constData());
+ }
}
}
@@ -555,29 +715,50 @@ bool QRhiVulkan::create(QRhi::Flags flags)
qCDebug(QRHI_LOG_INFO, " %s", ext);
}
- VkDeviceCreateInfo devInfo;
- memset(&devInfo, 0, sizeof(devInfo));
+ VkDeviceCreateInfo devInfo = {};
devInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
devInfo.queueCreateInfoCount = 1;
- devInfo.pQueueCreateInfos = queueInfo;
- devInfo.enabledLayerCount = uint32_t(devLayers.count());
+ devInfo.pQueueCreateInfos = &queueInfo;
+ devInfo.enabledLayerCount = uint32_t(devLayers.size());
devInfo.ppEnabledLayerNames = devLayers.constData();
- devInfo.enabledExtensionCount = uint32_t(requestedDevExts.count());
+ devInfo.enabledExtensionCount = uint32_t(requestedDevExts.size());
devInfo.ppEnabledExtensionNames = requestedDevExts.constData();
- VkPhysicalDeviceFeatures features;
- memset(&features, 0, sizeof(features));
- if (physDevFeatures.wideLines)
- features.wideLines = VK_TRUE;
- if (physDevFeatures.largePoints)
- features.largePoints = VK_TRUE;
- if (physDevFeatures.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) {
@@ -586,34 +767,44 @@ bool QRhiVulkan::create(QRhi::Flags flags)
}
} else {
qCDebug(QRHI_LOG_INFO, "Using imported device %p", dev);
+
+ // Here we have no way to tell if the extensions got enabled or not.
+ // Pretend it's all there and supported. If getProcAddress fails, we'll
+ // handle that gracefully.
+ caps.vertexAttribDivisor = true;
+ caps.renderPass2KHR = true;
+ caps.depthStencilResolveKHR = true;
}
+ vkGetPhysicalDeviceSurfaceCapabilitiesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR>(
+ inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfaceCapabilitiesKHR"));
+ vkGetPhysicalDeviceSurfaceFormatsKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceFormatsKHR>(
+ inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfaceFormatsKHR"));
+ vkGetPhysicalDeviceSurfacePresentModesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfacePresentModesKHR>(
+ inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfacePresentModesKHR"));
+
df = inst->deviceFunctions(dev);
- if (!importedCmdPool) {
- VkCommandPoolCreateInfo poolInfo;
- memset(&poolInfo, 0, sizeof(poolInfo));
- poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
- poolInfo.queueFamilyIndex = uint32_t(gfxQueueFamilyIdx);
- VkResult err = df->vkCreateCommandPool(dev, &poolInfo, nullptr, &cmdPool);
+ VkCommandPoolCreateInfo poolInfo = {};
+ poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
+ 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) {
qWarning("Failed to create command pool: %d", err);
return false;
}
}
- 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;
@@ -621,35 +812,53 @@ 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
+
+ // With Vulkan 1.2 renderpass2 and depth_stencil_resolve are core, but we
+ // have to support the case of 1.1 + extensions, in particular for the Quest
+ // 3 (Android, Vulkan 1.1 at the time of writing). Therefore, always rely on
+ // the KHR extension for now.
+#ifdef VK_KHR_create_renderpass2
+ if (caps.renderPass2KHR) {
+ vkCreateRenderPass2KHR = reinterpret_cast<PFN_vkCreateRenderPass2KHR>(f->vkGetDeviceProcAddr(dev, "vkCreateRenderPass2KHR"));
+ if (!vkCreateRenderPass2KHR) // handle it gracefully, the caps flag may be incorrect when using an imported VkDevice
+ caps.renderPass2KHR = false;
+ }
+#endif
if (!importedAllocator) {
- VmaVulkanFunctions afuncs;
- afuncs.vkGetPhysicalDeviceProperties = wrap_vkGetPhysicalDeviceProperties;
- afuncs.vkGetPhysicalDeviceMemoryProperties = wrap_vkGetPhysicalDeviceMemoryProperties;
- afuncs.vkAllocateMemory = wrap_vkAllocateMemory;
- afuncs.vkFreeMemory = wrap_vkFreeMemory;
- afuncs.vkMapMemory = wrap_vkMapMemory;
- afuncs.vkUnmapMemory = wrap_vkUnmapMemory;
- afuncs.vkFlushMappedMemoryRanges = wrap_vkFlushMappedMemoryRanges;
- afuncs.vkInvalidateMappedMemoryRanges = wrap_vkInvalidateMappedMemoryRanges;
- afuncs.vkBindBufferMemory = wrap_vkBindBufferMemory;
- afuncs.vkBindImageMemory = wrap_vkBindImageMemory;
- afuncs.vkGetBufferMemoryRequirements = wrap_vkGetBufferMemoryRequirements;
- afuncs.vkGetImageMemoryRequirements = wrap_vkGetImageMemoryRequirements;
- afuncs.vkCreateBuffer = wrap_vkCreateBuffer;
- afuncs.vkDestroyBuffer = wrap_vkDestroyBuffer;
- afuncs.vkCreateImage = wrap_vkCreateImage;
- afuncs.vkDestroyImage = wrap_vkDestroyImage;
-
- VmaAllocatorCreateInfo allocatorInfo;
- memset(&allocatorInfo, 0, sizeof(allocatorInfo));
+ VmaVulkanFunctions funcs = {};
+ funcs.vkGetInstanceProcAddr = wrap_vkGetInstanceProcAddr;
+ funcs.vkGetDeviceProcAddr = wrap_vkGetDeviceProcAddr;
+
+ VmaAllocatorCreateInfo allocatorInfo = {};
// A QRhi is supposed to be used from one single thread only. Disable
// the allocator's own mutexes. This gives a performance boost.
allocatorInfo.flags = VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT;
allocatorInfo.physicalDevice = physDev;
allocatorInfo.device = dev;
- allocatorInfo.pVulkanFunctions = &afuncs;
+ allocatorInfo.pVulkanFunctions = &funcs;
+ allocatorInfo.instance = inst->vkInstance();
+ allocatorInfo.vulkanApiVersion = VK_MAKE_VERSION(caps.apiVersion.majorVersion(),
+ caps.apiVersion.minorVersion(),
+ caps.apiVersion.microVersion());
VmaAllocator vmaallocator;
VkResult err = vmaCreateAllocator(&allocatorInfo, &vmaallocator);
if (err != VK_SUCCESS) {
@@ -668,8 +877,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;
@@ -681,12 +889,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;
@@ -695,8 +905,8 @@ bool QRhiVulkan::create(QRhi::Flags flags)
nativeHandlesStruct.gfxQueueFamilyIdx = gfxQueueFamilyIdx;
nativeHandlesStruct.gfxQueueIdx = gfxQueueIdx;
nativeHandlesStruct.gfxQueue = gfxQueue;
- nativeHandlesStruct.cmdPool = cmdPool;
nativeHandlesStruct.vmemAllocator = allocator;
+ nativeHandlesStruct.inst = inst;
return true;
}
@@ -717,11 +927,6 @@ void QRhiVulkan::destroy()
ofr.cmdFence = VK_NULL_HANDLE;
}
- if (ofr.cbWrapper.cb) {
- df->vkFreeCommandBuffers(dev, cmdPool, 1, &ofr.cbWrapper.cb);
- ofr.cbWrapper.cb = VK_NULL_HANDLE;
- }
-
if (pipelineCache) {
df->vkDestroyPipelineCache(dev, pipelineCache, nullptr);
pipelineCache = VK_NULL_HANDLE;
@@ -742,9 +947,13 @@ void QRhiVulkan::destroy()
allocator = nullptr;
}
- if (!importedCmdPool && cmdPool) {
- df->vkDestroyCommandPool(dev, cmdPool, nullptr);
- cmdPool = VK_NULL_HANDLE;
+ for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
+ if (cmdPool[i]) {
+ df->vkDestroyCommandPool(dev, cmdPool[i], nullptr);
+ cmdPool[i] = VK_NULL_HANDLE;
+ }
+ freeSecondaryCbs[i].clear();
+ ofr.cbWrapper[i]->cb = VK_NULL_HANDLE;
}
if (!importedDevice && dev) {
@@ -766,8 +975,7 @@ VkResult QRhiVulkan::createDescriptorPool(VkDescriptorPool *pool)
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, QVK_STORAGE_BUFFERS_PER_POOL },
{ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, QVK_STORAGE_IMAGES_PER_POOL }
};
- VkDescriptorPoolCreateInfo descPoolInfo;
- memset(&descPoolInfo, 0, sizeof(descPoolInfo));
+ VkDescriptorPoolCreateInfo descPoolInfo = {};
descPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
// Do not enable vkFreeDescriptorSets - sets are never freed on their own
// (good so no trouble with fragmentation), they just deref their pool
@@ -789,7 +997,7 @@ bool QRhiVulkan::allocateDescriptorSet(VkDescriptorSetAllocateInfo *allocInfo, V
return r;
};
- int lastPoolIdx = descriptorPools.count() - 1;
+ int lastPoolIdx = descriptorPools.size() - 1;
for (int i = lastPoolIdx; i >= 0; --i) {
if (descriptorPools[i].refCount == 0) {
df->vkResetDescriptorPool(dev, descriptorPools[i].pool, 0);
@@ -809,7 +1017,7 @@ bool QRhiVulkan::allocateDescriptorSet(VkDescriptorSetAllocateInfo *allocInfo, V
VkResult poolErr = createDescriptorPool(&newPool);
if (poolErr == VK_SUCCESS) {
descriptorPools.append(newPool);
- lastPoolIdx = descriptorPools.count() - 1;
+ lastPoolIdx = descriptorPools.size() - 1;
VkResult err = tryAllocate(lastPoolIdx);
if (err != VK_SUCCESS) {
qWarning("Failed to allocate descriptor set from new pool too, giving up: %d", err);
@@ -838,6 +1046,8 @@ static inline VkFormat toVkTextureFormat(QRhiTexture::Format format, QRhiTexture
return srgb ? VK_FORMAT_R8G8_SRGB : VK_FORMAT_R8G8_UNORM;
case QRhiTexture::R16:
return VK_FORMAT_R16_UNORM;
+ case QRhiTexture::RG16:
+ return VK_FORMAT_R16G16_UNORM;
case QRhiTexture::RED_OR_ALPHA8:
return VK_FORMAT_R8_UNORM;
@@ -850,6 +1060,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:
@@ -911,12 +1125,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:
@@ -931,22 +1144,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;
- 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;
@@ -1022,8 +1227,7 @@ bool QRhiVulkan::createTransientImage(VkFormat format,
VkResult err;
for (int i = 0; i < count; ++i) {
- VkImageCreateInfo imgInfo;
- memset(&imgInfo, 0, sizeof(imgInfo));
+ VkImageCreateInfo imgInfo = {};
imgInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imgInfo.imageType = VK_IMAGE_TYPE_2D;
imgInfo.format = format;
@@ -1048,8 +1252,7 @@ bool QRhiVulkan::createTransientImage(VkFormat format,
df->vkGetImageMemoryRequirements(dev, images[i], &memReq);
}
- VkMemoryAllocateInfo memInfo;
- memset(&memInfo, 0, sizeof(memInfo));
+ VkMemoryAllocateInfo memInfo = {};
memInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
memInfo.allocationSize = aligned(memReq.size, memReq.alignment) * VkDeviceSize(count);
@@ -1077,8 +1280,7 @@ bool QRhiVulkan::createTransientImage(VkFormat format,
}
ofs += aligned(memReq.size, memReq.alignment);
- VkImageViewCreateInfo imgViewInfo;
- memset(&imgViewInfo, 0, sizeof(imgViewInfo));
+ VkImageViewCreateInfo imgViewInfo = {};
imgViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
imgViewInfo.image = images[i];
imgViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
@@ -1126,12 +1328,32 @@ VkFormat QRhiVulkan::optimalDepthStencilFormat()
return optimalDsFormat;
}
+static void fillRenderPassCreateInfo(VkRenderPassCreateInfo *rpInfo,
+ VkSubpassDescription *subpassDesc,
+ QVkRenderPassDescriptor *rpD)
+{
+ memset(subpassDesc, 0, sizeof(VkSubpassDescription));
+ subpassDesc->pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
+ 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.size());
+ rpInfo->pAttachments = rpD->attDescs.constData();
+ rpInfo->subpassCount = 1;
+ rpInfo->pSubpasses = subpassDesc;
+ rpInfo->dependencyCount = uint32_t(rpD->subpassDeps.size());
+ rpInfo->pDependencies = !rpD->subpassDeps.isEmpty() ? rpD->subpassDeps.constData() : nullptr;
+}
+
bool QRhiVulkan::createDefaultRenderPass(QVkRenderPassDescriptor *rpD, bool hasDepthStencil, VkSampleCountFlagBits samples, VkFormat colorFormat)
{
// 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;
@@ -1144,6 +1366,10 @@ bool QRhiVulkan::createDefaultRenderPass(QVkRenderPassDescriptor *rpD, bool hasD
rpD->colorRefs.append({ 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL });
+ rpD->hasDepthStencil = hasDepthStencil;
+ rpD->hasDepthStencilResolve = false;
+ rpD->multiViewCount = 0;
+
if (hasDepthStencil) {
// clear on load + no store + lazy alloc + transient image should play
// nicely with tiled GPUs (no physical backing necessary for ds buffer)
@@ -1176,40 +1402,32 @@ bool QRhiVulkan::createDefaultRenderPass(QVkRenderPassDescriptor *rpD, bool hasD
rpD->resolveRefs.append({ 2, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL });
}
- VkSubpassDescription subpassDesc;
- memset(&subpassDesc, 0, sizeof(subpassDesc));
- subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
- subpassDesc.colorAttachmentCount = 1;
- subpassDesc.pColorAttachments = rpD->colorRefs.constData();
- subpassDesc.pDepthStencilAttachment = hasDepthStencil ? &rpD->dsRef : nullptr;
-
// 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;
subpassDep.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
subpassDep.srcAccessMask = 0;
subpassDep.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+ rpD->subpassDeps.append(subpassDep);
+ if (hasDepthStencil) {
+ memset(&subpassDep, 0, sizeof(subpassDep));
+ subpassDep.srcSubpass = VK_SUBPASS_EXTERNAL;
+ subpassDep.dstSubpass = 0;
+ subpassDep.srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT
+ | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
+ subpassDep.dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT
+ | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
+ subpassDep.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
+ subpassDep.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT
+ | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
+ rpD->subpassDeps.append(subpassDep);
+ }
VkRenderPassCreateInfo rpInfo;
- memset(&rpInfo, 0, sizeof(rpInfo));
- rpInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
- rpInfo.attachmentCount = 1;
- rpInfo.pAttachments = rpD->attDescs.constData();
- rpInfo.subpassCount = 1;
- rpInfo.pSubpasses = &subpassDesc;
- rpInfo.dependencyCount = 1;
- rpInfo.pDependencies = &subpassDep;
-
- if (hasDepthStencil)
- rpInfo.attachmentCount += 1;
-
- if (samples > VK_SAMPLE_COUNT_1_BIT) {
- rpInfo.attachmentCount += 1;
- subpassDesc.pResolveAttachments = rpD->resolveRefs.constData();
- }
+ VkSubpassDescription subpassDesc;
+ fillRenderPassCreateInfo(&rpInfo, &subpassDesc, rpD);
VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, &rpD->rp);
if (err != VK_SUCCESS) {
@@ -1217,34 +1435,193 @@ bool QRhiVulkan::createDefaultRenderPass(QVkRenderPassDescriptor *rpD, bool hasD
return false;
}
- rpD->hasDepthStencil = hasDepthStencil;
-
return true;
}
+struct MultiViewRenderPassSetupHelper
+{
+ bool prepare(VkRenderPassCreateInfo *rpInfo, int multiViewCount, bool multiViewCap)
+ {
+ if (multiViewCount < 2)
+ return true;
+ if (!multiViewCap) {
+ qWarning("Cannot create multiview render pass without support for the Vulkan 1.1 multiview feature");
+ return false;
+ }
+#ifdef VK_VERSION_1_1
+ uint32_t allViewsMask = 0;
+ for (uint32_t i = 0; i < uint32_t(multiViewCount); ++i)
+ allViewsMask |= (1 << i);
+ multiViewMask = allViewsMask;
+ multiViewCorrelationMask = allViewsMask;
+ multiViewInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO;
+ multiViewInfo.subpassCount = 1;
+ multiViewInfo.pViewMasks = &multiViewMask;
+ multiViewInfo.correlationMaskCount = 1;
+ multiViewInfo.pCorrelationMasks = &multiViewCorrelationMask;
+ rpInfo->pNext = &multiViewInfo;
+#endif
+ return true;
+ }
+
+#ifdef VK_VERSION_1_1
+ VkRenderPassMultiviewCreateInfo multiViewInfo = {};
+ uint32_t multiViewMask = 0;
+ uint32_t multiViewCorrelationMask = 0;
+#endif
+};
+
+#ifdef VK_KHR_create_renderpass2
+// Effectively converts a VkRenderPassCreateInfo into a VkRenderPassCreateInfo2,
+// adding depth-stencil resolve support. Assumes a single subpass and no subpass
+// dependencies.
+struct RenderPass2SetupHelper
+{
+ bool prepare(VkRenderPassCreateInfo2 *rpInfo2, const VkRenderPassCreateInfo *rpInfo, const QVkRenderPassDescriptor *rpD, int multiViewCount) {
+ *rpInfo2 = {};
+
+ viewMask = 0;
+ if (multiViewCount >= 2) {
+ for (uint32_t i = 0; i < uint32_t(multiViewCount); ++i)
+ viewMask |= (1 << i);
+ }
+
+ attDescs2.resize(rpInfo->attachmentCount);
+ for (qsizetype i = 0; i < attDescs2.count(); ++i) {
+ VkAttachmentDescription2KHR &att2(attDescs2[i]);
+ const VkAttachmentDescription &att(rpInfo->pAttachments[i]);
+ att2 = {};
+ att2.sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2;
+ att2.flags = att.flags;
+ att2.format = att.format;
+ att2.samples = att.samples;
+ att2.loadOp = att.loadOp;
+ att2.storeOp = att.storeOp;
+ att2.stencilLoadOp = att.stencilLoadOp;
+ att2.stencilStoreOp = att.stencilStoreOp;
+ att2.initialLayout = att.initialLayout;
+ att2.finalLayout = att.finalLayout;
+ }
+
+ attRefs2.clear();
+ subpass2 = {};
+ subpass2.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2_KHR;
+ const VkSubpassDescription &subpassDesc(rpInfo->pSubpasses[0]);
+ subpass2.flags = subpassDesc.flags;
+ subpass2.pipelineBindPoint = subpassDesc.pipelineBindPoint;
+ if (multiViewCount >= 2)
+ subpass2.viewMask = viewMask;
+
+ // color attachment refs
+ qsizetype startIndex = attRefs2.count();
+ for (uint32_t j = 0; j < subpassDesc.colorAttachmentCount; ++j) {
+ attRefs2.append({});
+ VkAttachmentReference2KHR &attref2(attRefs2.last());
+ const VkAttachmentReference &attref(subpassDesc.pColorAttachments[j]);
+ attref2.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR;
+ attref2.attachment = attref.attachment;
+ attref2.layout = attref.layout;
+ attref2.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ }
+ subpass2.colorAttachmentCount = subpassDesc.colorAttachmentCount;
+ subpass2.pColorAttachments = attRefs2.constData() + startIndex;
+
+ // color resolve refs
+ if (subpassDesc.pResolveAttachments) {
+ startIndex = attRefs2.count();
+ for (uint32_t j = 0; j < subpassDesc.colorAttachmentCount; ++j) {
+ attRefs2.append({});
+ VkAttachmentReference2KHR &attref2(attRefs2.last());
+ const VkAttachmentReference &attref(subpassDesc.pResolveAttachments[j]);
+ attref2.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR;
+ attref2.attachment = attref.attachment;
+ attref2.layout = attref.layout;
+ attref2.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ }
+ subpass2.pResolveAttachments = attRefs2.constData() + startIndex;
+ }
+
+ // depth-stencil ref
+ if (subpassDesc.pDepthStencilAttachment) {
+ startIndex = attRefs2.count();
+ attRefs2.append({});
+ VkAttachmentReference2KHR &attref2(attRefs2.last());
+ const VkAttachmentReference &attref(*subpassDesc.pDepthStencilAttachment);
+ attref2.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR;
+ attref2.attachment = attref.attachment;
+ attref2.layout = attref.layout;
+ attref2.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
+ subpass2.pDepthStencilAttachment = attRefs2.constData() + startIndex;
+ }
+
+ // depth-stencil resolve ref
+#ifdef VK_KHR_depth_stencil_resolve
+ dsResolveDesc = {};
+ if (rpD->hasDepthStencilResolve) {
+ startIndex = attRefs2.count();
+ attRefs2.append({});
+ VkAttachmentReference2KHR &attref2(attRefs2.last());
+ attref2.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR;
+ attref2.attachment = rpD->dsResolveRef.attachment;
+ attref2.layout = rpD->dsResolveRef.layout;
+ attref2.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
+ dsResolveDesc.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE_KHR;
+ dsResolveDesc.depthResolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;
+ dsResolveDesc.stencilResolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;
+ dsResolveDesc.pDepthStencilResolveAttachment = attRefs2.constData() + startIndex;
+ subpass2.pNext = &dsResolveDesc;
+ }
+#endif
+
+ rpInfo2->sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2_KHR;
+ rpInfo2->pNext = nullptr; // the 1.1 VkRenderPassMultiviewCreateInfo is part of the '2' structs
+ rpInfo2->flags = rpInfo->flags;
+ rpInfo2->attachmentCount = rpInfo->attachmentCount;
+ rpInfo2->pAttachments = attDescs2.constData();
+ rpInfo2->subpassCount = 1;
+ rpInfo2->pSubpasses = &subpass2;
+ if (multiViewCount >= 2) {
+ rpInfo2->correlatedViewMaskCount = 1;
+ rpInfo2->pCorrelatedViewMasks = &viewMask;
+ }
+ return true;
+ }
+
+ QVarLengthArray<VkAttachmentDescription2KHR, 8> attDescs2;
+ QVarLengthArray<VkAttachmentReference2KHR, 8> attRefs2;
+ VkSubpassDescription2KHR subpass2;
+#ifdef VK_KHR_depth_stencil_resolve
+ VkSubpassDescriptionDepthStencilResolveKHR dsResolveDesc;
+#endif
+ uint32_t viewMask;
+};
+#endif // VK_KHR_create_renderpass2
+
bool QRhiVulkan::createOffscreenRenderPass(QVkRenderPassDescriptor *rpD,
const QRhiColorAttachment *firstColorAttachment,
const QRhiColorAttachment *lastColorAttachment,
bool preserveColor,
bool preserveDs,
+ bool storeDs,
QRhiRenderBuffer *depthStencilBuffer,
- QRhiTexture *depthTexture)
+ QRhiTexture *depthTexture,
+ QRhiTexture *depthResolveTexture)
{
- // attachment list layout is color (0-8), ds (0-1), resolve (0-8)
+ // attachment list layout is color (0-8), ds (0-1), resolve (0-8), ds resolve (0-1)
+ 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
@@ -1252,31 +1629,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()) {
@@ -1296,9 +1683,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;
@@ -1308,38 +1694,78 @@ 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.size() == rpD->resolveRefs.size());
- VkSubpassDescription subpassDesc;
- memset(&subpassDesc, 0, sizeof(subpassDesc));
- subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
- subpassDesc.colorAttachmentCount = uint32_t(rpD->colorRefs.count());
- Q_ASSERT(rpD->colorRefs.count() == rpD->resolveRefs.count());
- 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;
+ rpD->hasDepthStencilResolve = rpD->hasDepthStencil && depthResolveTexture;
+ if (rpD->hasDepthStencilResolve) {
+ QVkTexture *rtexD = QRHI_RES(QVkTexture, depthResolveTexture);
+ if (rtexD->samples > VK_SAMPLE_COUNT_1_BIT)
+ qWarning("Resolving into a multisample depth texture is not supported");
+
+ QVkTexture *texD = QRHI_RES(QVkTexture, depthResolveTexture);
+ if (texD->vkformat != rtexD->vkformat) {
+ qWarning("Multisample resolve between different depth-stencil formats (%d and %d) is not supported.",
+ int(texD->vkformat), int(rtexD->vkformat));
+ }
+
+ VkAttachmentDescription attDesc = {};
+ attDesc.format = rtexD->viewFormat;
+ attDesc.samples = VK_SAMPLE_COUNT_1_BIT;
+ attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; // ignored
+ attDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
+ attDesc.stencilLoadOp = attDesc.loadOp;
+ attDesc.stencilStoreOp = attDesc.storeOp;
+ attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ attDesc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+ rpD->attDescs.append(attDesc);
+ }
+ rpD->dsResolveRef = { uint32_t(rpD->attDescs.size() - 1), VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
+
+ // rpD->subpassDeps stays empty: don't yet know the correct initial/final
+ // access and stage stuff for the implicit deps at this point, so leave it
+ // to the resource tracking and activateTextureRenderTarget() to generate
+ // barriers.
VkRenderPassCreateInfo rpInfo;
- memset(&rpInfo, 0, sizeof(rpInfo));
- rpInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
- rpInfo.attachmentCount = uint32_t(rpD->attDescs.count());
- rpInfo.pAttachments = rpD->attDescs.constData();
- rpInfo.subpassCount = 1;
- rpInfo.pSubpasses = &subpassDesc;
- // don't yet know the correct initial/final access and stage stuff for the
- // implicit deps at this point, so leave it to the resource tracking to
- // generate barriers
+ VkSubpassDescription subpassDesc;
+ fillRenderPassCreateInfo(&rpInfo, &subpassDesc, rpD);
- VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, &rpD->rp);
- if (err != VK_SUCCESS) {
- qWarning("Failed to create renderpass: %d", err);
+ MultiViewRenderPassSetupHelper multiViewHelper;
+ if (!multiViewHelper.prepare(&rpInfo, multiViewCount, caps.multiView))
return false;
+
+#ifdef VK_KHR_create_renderpass2
+ if (rpD->hasDepthStencilResolve && caps.renderPass2KHR) {
+ // Use the KHR extension, not the 1.2 core API, in order to support Vulkan 1.1.
+ VkRenderPassCreateInfo2KHR rpInfo2;
+ RenderPass2SetupHelper rp2Helper;
+ if (!rp2Helper.prepare(&rpInfo2, &rpInfo, rpD, multiViewCount))
+ return false;
+
+ VkResult err = vkCreateRenderPass2KHR(dev, &rpInfo2, nullptr, &rpD->rp);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create renderpass (using VkRenderPassCreateInfo2KHR): %d", err);
+ return false;
+ }
+ } else
+#endif
+ {
+ if (rpD->hasDepthStencilResolve) {
+ qWarning("Resolving multisample depth-stencil buffers is not supported without "
+ "VK_KHR_depth_stencil_resolve and VK_KHR_create_renderpass2");
+ }
+ VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, &rpD->rp);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create renderpass: %d", err);
+ return false;
+ }
}
return true;
@@ -1380,21 +1806,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;
@@ -1402,9 +1852,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;
@@ -1421,15 +1878,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;
@@ -1487,12 +1943,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];
@@ -1501,8 +1958,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;
@@ -1521,11 +1977,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) {
@@ -1587,13 +2072,9 @@ void QRhiVulkan::releaseSwapChainResources(QRhiSwapChain *swapChain)
df->vkDestroySemaphore(dev, frame.drawSem, nullptr);
frame.drawSem = VK_NULL_HANDLE;
}
- if (frame.cmdBuf) {
- df->vkFreeCommandBuffers(dev, cmdPool, 1, &frame.cmdBuf);
- frame.cmdBuf = VK_NULL_HANDLE;
- }
}
- 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);
@@ -1624,12 +2105,45 @@ void QRhiVulkan::releaseSwapChainResources(QRhiSwapChain *swapChain)
// NB! surface and similar must remain intact
}
-QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags)
+void QRhiVulkan::ensureCommandPoolForNewFrame()
+{
+ VkCommandPoolResetFlags flags = 0;
+
+ // While not clear what "recycles all of the resources from the command
+ // pool back to the system" really means in practice, set it when there was
+ // a call to releaseCachedResources() recently.
+ if (releaseCachedResourcesCalledBeforeFrameStart)
+ 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], flags);
+}
+
+double QRhiVulkan::elapsedSecondsFromTimestamp(quint64 timestamp[2], bool *ok)
+{
+ quint64 mask = 0;
+ for (quint64 i = 0; i < timestampValidBits; i += 8)
+ mask |= 0xFFULL << i;
+ const quint64 ts0 = timestamp[0] & mask;
+ const quint64 ts1 = timestamp[1] & mask;
+ const float nsecsPerTick = physDevProperties.limits.timestampPeriod;
+ if (!qFuzzyIsNull(nsecsPerTick)) {
+ const float elapsedMs = float(ts1 - ts0) * nsecsPerTick / 1000000.0f;
+ const double elapsedSec = elapsedMs / 1000.0;
+ *ok = true;
+ return elapsedSec;
+ }
+ *ok = false;
+ return 0;
+}
+
+QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags)
{
QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain);
const int frameResIndex = swapChainD->bufferCount > 1 ? swapChainD->currentFrameSlot : 0;
QVkSwapChain::FrameResources &frame(swapChainD->frameRes[frameResIndex]);
- 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
@@ -1641,10 +2155,11 @@ QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::Begin
}
// move on to next swapchain image
+ uint32_t imageIndex = 0;
VkResult err = vkAcquireNextImageKHR(dev, swapChainD->sc, UINT64_MAX,
- frame.imageSem, frame.imageFence, &frame.imageIndex);
+ frame.imageSem, frame.imageFence, &imageIndex);
if (err == VK_SUCCESS || err == VK_SUBOPTIMAL_KHR) {
- swapChainD->currentImageIndex = frame.imageIndex;
+ swapChainD->currentImageIndex = imageIndex;
frame.imageSemWaitable = true;
frame.imageAcquired = true;
frame.imageFenceWaitable = true;
@@ -1673,7 +2188,33 @@ 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.
+ currentFrameSlot = int(swapChainD->currentFrameSlot);
+ currentSwapChain = swapChainD;
+ if (swapChainD->ds)
+ swapChainD->ds->lastActiveFrameSlot = currentFrameSlot;
+
+ // reset the command pool
+ ensureCommandPoolForNewFrame();
+
+ // start recording to this frame's command buffer
+ QRhi::FrameOpResult cbres = startPrimaryCommandBuffer(&frame.cmdBuf);
+ if (cbres != QRhi::FrameOpSuccess)
+ return cbres;
+
+ swapChainD->cbWrapper.cb = frame.cmdBuf;
+
+ QVkSwapChain::ImageResources &image(swapChainD->imageRes[swapChainD->currentImageIndex]);
+ swapChainD->rtWrapper.d.fb = image.fb;
+
+ if (swapChainD->stereo) {
+ QVkSwapChain::ImageResources &image(
+ swapChainD->imageRes[swapChainD->currentImageIndex + swapChainD->bufferCount]);
+ swapChainD->rtWrapperRight.d.fb = image.fb;
+ }
+
+ prepareNewFrame(&swapChainD->cbWrapper);
+
+ // Read the timestamps for the previous frame for this slot.
if (frame.timestampQueryIndex >= 0) {
quint64 timestamp[2] = { 0, 0 };
VkResult err = df->vkGetQueryPoolResults(dev, timestampQueryPool, uint32_t(frame.timestampQueryIndex), 2,
@@ -1682,61 +2223,33 @@ QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::Begin
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));
- }
+ bool ok = false;
+ const double elapsedSec = elapsedSecondsFromTimestamp(timestamp, &ok);
+ if (ok)
+ swapChainD->cbWrapper.lastGpuTime = elapsedSec;
} else {
qWarning("Failed to query timestamp: %d", err);
}
}
- // build new draw command buffer
- QRhi::FrameOpResult cbres = startPrimaryCommandBuffer(&frame.cmdBuf);
- 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) {
+ // No timestamps if the client did not opt in, or when not having at least 2 frames in flight.
+ if (rhiFlags.testFlag(QRhi::EnableTimestamps) && swapChainD->bufferCount > 1) {
+ int timestampQueryIdx = -1;
+ for (int i = 0; i < timestampQueryPoolMap.size(); ++i) {
if (!timestampQueryPoolMap.testBit(i)) {
timestampQueryPoolMap.setBit(i);
timestampQueryIdx = i * 2;
break;
}
}
+ if (timestampQueryIdx >= 0) {
+ df->vkCmdResetQueryPool(frame.cmdBuf, timestampQueryPool, uint32_t(timestampQueryIdx), 2);
+ // record timestamp at the start of the command buffer
+ df->vkCmdWriteTimestamp(frame.cmdBuf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
+ timestampQueryPool, uint32_t(timestampQueryIdx));
+ frame.timestampQueryIndex = timestampQueryIdx;
+ }
}
- if (timestampQueryIdx >= 0) {
- df->vkCmdResetQueryPool(frame.cmdBuf, timestampQueryPool, uint32_t(timestampQueryIdx), 2);
- // record timestamp at the start of the command buffer
- df->vkCmdWriteTimestamp(frame.cmdBuf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
- timestampQueryPool, uint32_t(timestampQueryIdx));
- frame.timestampQueryIndex = timestampQueryIdx;
- }
-
- swapChainD->cbWrapper.cb = frame.cmdBuf;
- swapChainD->cbWrapper.useSecondaryCb = flags.testFlag(QRhi::ExternalContentsInPass);
-
- QVkSwapChain::ImageResources &image(swapChainD->imageRes[swapChainD->currentImageIndex]);
- swapChainD->rtWrapper.d.fb = image.fb;
-
- currentFrameSlot = int(swapChainD->currentFrameSlot);
- currentSwapChain = swapChainD;
- if (swapChainD->ds)
- swapChainD->ds->lastActiveFrameSlot = currentFrameSlot;
-
- QRHI_PROF_F(beginSwapChainFrame(swapChain));
-
- prepareNewFrame(&swapChainD->cbWrapper);
return QRhi::FrameOpSuccess;
}
@@ -1746,6 +2259,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;
@@ -1753,8 +2270,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;
@@ -1801,14 +2317,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;
@@ -1861,46 +2372,43 @@ 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();
QRHI_RES(QVkCommandBuffer, cb)->resetState();
finishActiveReadbacks(); // last, in case the readback-completed callback issues rhi calls
+
+ releaseCachedResourcesCalledBeforeFrameStart = false;
}
QRhi::FrameOpResult QRhiVulkan::startPrimaryCommandBuffer(VkCommandBuffer *cb)
{
- if (*cb) {
- df->vkFreeCommandBuffers(dev, cmdPool, 1, cb);
- *cb = VK_NULL_HANDLE;
- }
-
- VkCommandBufferAllocateInfo cmdBufInfo;
- memset(&cmdBufInfo, 0, sizeof(cmdBufInfo));
- cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
- cmdBufInfo.commandPool = cmdPool;
- cmdBufInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
- cmdBufInfo.commandBufferCount = 1;
+ if (!*cb) {
+ VkCommandBufferAllocateInfo cmdBufInfo = {};
+ cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
+ cmdBufInfo.commandPool = cmdPool[currentFrameSlot];
+ cmdBufInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
+ cmdBufInfo.commandBufferCount = 1;
- VkResult err = df->vkAllocateCommandBuffers(dev, &cmdBufInfo, cb);
- if (err != VK_SUCCESS) {
- if (err == VK_ERROR_DEVICE_LOST) {
- qWarning("Device loss detected in vkAllocateCommandBuffers()");
- deviceLost = true;
- return QRhi::FrameOpDeviceLost;
+ VkResult err = df->vkAllocateCommandBuffers(dev, &cmdBufInfo, cb);
+ if (err != VK_SUCCESS) {
+ if (err == VK_ERROR_DEVICE_LOST) {
+ qWarning("Device loss detected in vkAllocateCommandBuffers()");
+ deviceLost = true;
+ return QRhi::FrameOpDeviceLost;
+ }
+ qWarning("Failed to allocate frame command buffer: %d", err);
+ return QRhi::FrameOpError;
}
- qWarning("Failed to allocate frame command buffer: %d", err);
- return QRhi::FrameOpError;
}
- VkCommandBufferBeginInfo cmdBufBeginInfo;
- memset(&cmdBufBeginInfo, 0, sizeof(cmdBufBeginInfo));
+ VkCommandBufferBeginInfo cmdBufBeginInfo = {};
cmdBufBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
- err = df->vkBeginCommandBuffer(*cb, &cmdBufBeginInfo);
+ VkResult err = df->vkBeginCommandBuffer(*cb, &cmdBufBeginInfo);
if (err != VK_SUCCESS) {
if (err == VK_ERROR_DEVICE_LOST) {
qWarning("Device loss detected in vkBeginCommandBuffer()");
@@ -1928,8 +2436,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;
@@ -1960,7 +2467,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) {
@@ -1971,31 +2478,50 @@ void QRhiVulkan::waitCommandCompletion(int frameSlot)
}
}
-QRhi::FrameOpResult QRhiVulkan::beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags)
+QRhi::FrameOpResult QRhiVulkan::beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags)
{
- QRhi::FrameOpResult cbres = startPrimaryCommandBuffer(&ofr.cbWrapper.cb);
- if (cbres != QRhi::FrameOpSuccess)
- return cbres;
-
// 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);
- ofr.cbWrapper.useSecondaryCb = flags.testFlag(QRhi::ExternalContentsInPass);
+ waitCommandCompletion(currentFrameSlot);
+
+ ensureCommandPoolForNewFrame();
- prepareNewFrame(&ofr.cbWrapper);
+ QVkCommandBuffer *cbWrapper = ofr.cbWrapper[currentFrameSlot];
+ QRhi::FrameOpResult cbres = startPrimaryCommandBuffer(&cbWrapper->cb);
+ if (cbres != QRhi::FrameOpSuccess)
+ return cbres;
+
+ prepareNewFrame(cbWrapper);
ofr.active = true;
- *cb = &ofr.cbWrapper;
+ 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;
}
@@ -2005,11 +2531,17 @@ QRhi::FrameOpResult QRhiVulkan::endOffscreenFrame(QRhi::EndFrameFlags flags)
Q_ASSERT(ofr.active);
ofr.active = false;
- recordPrimaryCommandBuffer(&ofr.cbWrapper);
+ 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) {
@@ -2018,7 +2550,7 @@ QRhi::FrameOpResult QRhiVulkan::endOffscreenFrame(QRhi::EndFrameFlags flags)
}
}
- QRhi::FrameOpResult submitres = endAndSubmitPrimaryCommandBuffer(ofr.cbWrapper.cb, ofr.cmdFence, nullptr, nullptr);
+ QRhi::FrameOpResult submitres = endAndSubmitPrimaryCommandBuffer(cbWrapper->cb, ofr.cmdFence, nullptr, nullptr);
if (submitres != QRhi::FrameOpSuccess)
return submitres;
@@ -2030,6 +2562,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;
}
@@ -2042,10 +2592,11 @@ QRhi::FrameOpResult QRhiVulkan::finish()
VkCommandBuffer cb;
if (ofr.active) {
Q_ASSERT(!currentSwapChain);
- Q_ASSERT(ofr.cbWrapper.recordingPass == QVkCommandBuffer::NoPass);
- recordPrimaryCommandBuffer(&ofr.cbWrapper);
- ofr.cbWrapper.resetCommands();
- cb = ofr.cbWrapper.cb;
+ QVkCommandBuffer *cbWrapper(ofr.cbWrapper[currentFrameSlot]);
+ Q_ASSERT(cbWrapper->recordingPass == QVkCommandBuffer::NoPass);
+ recordPrimaryCommandBuffer(cbWrapper);
+ cbWrapper->resetCommands();
+ cb = cbWrapper->cb;
} else {
Q_ASSERT(currentSwapChain);
Q_ASSERT(currentSwapChain->cbWrapper.recordingPass == QVkCommandBuffer::NoPass);
@@ -2062,9 +2613,11 @@ QRhi::FrameOpResult QRhiVulkan::finish()
df->vkQueueWaitIdle(gfxQueue);
if (inFrame) {
+ // The current frame slot's command pool needs to be reset.
+ ensureCommandPoolForNewFrame();
// Allocate and begin recording on a new command buffer.
if (ofr.active) {
- startPrimaryCommandBuffer(&ofr.cbWrapper.cb);
+ startPrimaryCommandBuffer(&ofr.cbWrapper[currentFrameSlot]->cb);
} else {
QVkSwapChain::FrameResources &frame(swapChainD->frameRes[swapChainD->currentFrameSlot]);
startPrimaryCommandBuffer(&frame.cmdBuf);
@@ -2098,6 +2651,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]);
@@ -2123,8 +2679,17 @@ void QRhiVulkan::activateTextureRenderTarget(QVkCommandBuffer *cbD, QVkTextureRe
resolveTexD->lastActiveFrameSlot = currentFrameSlot;
}
}
- if (rtD->m_desc.depthStencilBuffer())
- QRHI_RES(QVkRenderBuffer, rtD->m_desc.depthStencilBuffer())->lastActiveFrameSlot = currentFrameSlot;
+ if (rtD->m_desc.depthStencilBuffer()) {
+ QVkRenderBuffer *rbD = QRHI_RES(QVkRenderBuffer, rtD->m_desc.depthStencilBuffer());
+ Q_ASSERT(rbD->m_type == QRhiRenderBuffer::DepthStencil);
+ // We specify no explicit VkSubpassDependency for an offscreen render
+ // target, meaning we need an explicit barrier for the depth-stencil
+ // buffer to avoid a write-after-write hazard (as the implicit one is
+ // not sufficient). Textures are taken care of by the resource tracking
+ // but that excludes the (content-wise) throwaway depth-stencil buffer.
+ depthStencilExplicitBarrier(cbD, rbD);
+ rbD->lastActiveFrameSlot = currentFrameSlot;
+ }
if (rtD->m_desc.depthTexture()) {
QVkTexture *depthTexD = QRHI_RES(QVkTexture, rtD->m_desc.depthTexture());
trackedRegisterTexture(&passResTracker, depthTexD,
@@ -2132,6 +2697,13 @@ void QRhiVulkan::activateTextureRenderTarget(QVkCommandBuffer *cbD, QVkTextureRe
QRhiPassResourceTracker::TexDepthOutputStage);
depthTexD->lastActiveFrameSlot = currentFrameSlot;
}
+ if (rtD->m_desc.depthResolveTexture()) {
+ QVkTexture *depthResolveTexD = QRHI_RES(QVkTexture, rtD->m_desc.depthResolveTexture());
+ trackedRegisterTexture(&passResTracker, depthResolveTexD,
+ QRhiPassResourceTracker::TexDepthOutput,
+ QRhiPassResourceTracker::TexDepthOutputStage);
+ depthResolveTexD->lastActiveFrameSlot = currentFrameSlot;
+ }
}
void QRhiVulkan::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
@@ -2146,24 +2718,27 @@ VkCommandBuffer QRhiVulkan::startSecondaryCommandBuffer(QVkRenderTargetData *rtD
{
VkCommandBuffer secondaryCb;
- VkCommandBufferAllocateInfo cmdBufInfo;
- memset(&cmdBufInfo, 0, sizeof(cmdBufInfo));
- cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
- cmdBufInfo.commandPool = cmdPool;
- cmdBufInfo.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY;
- cmdBufInfo.commandBufferCount = 1;
- VkResult err = df->vkAllocateCommandBuffers(dev, &cmdBufInfo, &secondaryCb);
- if (err != VK_SUCCESS) {
- qWarning("Failed to create secondary command buffer: %d", err);
- return VK_NULL_HANDLE;
+ if (!freeSecondaryCbs[currentFrameSlot].isEmpty()) {
+ secondaryCb = freeSecondaryCbs[currentFrameSlot].last();
+ freeSecondaryCbs[currentFrameSlot].removeLast();
+ } else {
+ VkCommandBufferAllocateInfo cmdBufInfo = {};
+ cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
+ cmdBufInfo.commandPool = cmdPool[currentFrameSlot];
+ cmdBufInfo.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY;
+ cmdBufInfo.commandBufferCount = 1;
+
+ VkResult err = df->vkAllocateCommandBuffers(dev, &cmdBufInfo, &secondaryCb);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create secondary command buffer: %d", err);
+ return VK_NULL_HANDLE;
+ }
}
- 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) {
@@ -2172,10 +2747,9 @@ VkCommandBuffer QRhiVulkan::startSecondaryCommandBuffer(QVkRenderTargetData *rtD
}
cmdBufBeginInfo.pInheritanceInfo = &cmdBufInheritInfo;
- err = df->vkBeginCommandBuffer(secondaryCb, &cmdBufBeginInfo);
+ VkResult err = df->vkBeginCommandBuffer(secondaryCb, &cmdBufBeginInfo);
if (err != VK_SUCCESS) {
qWarning("Failed to begin secondary command buffer: %d", err);
- df->vkFreeCommandBuffers(dev, cmdPool, 1, &secondaryCb);
return VK_NULL_HANDLE;
}
@@ -2188,20 +2762,14 @@ void QRhiVulkan::endAndEnqueueSecondaryCommandBuffer(VkCommandBuffer cb, QVkComm
if (err != VK_SUCCESS)
qWarning("Failed to end secondary command buffer: %d", err);
- QVkCommandBuffer::Command cmd;
+ QVkCommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QVkCommandBuffer::Command::ExecuteSecondary;
cmd.args.executeSecondary.cb = cb;
- cbD->commands.append(cmd);
-
- deferredReleaseSecondaryCommandBuffer(cb);
-}
-void QRhiVulkan::deferredReleaseSecondaryCommandBuffer(VkCommandBuffer cb)
-{
QRhiVulkan::DeferredReleaseEntry e;
- e.type = QRhiVulkan::DeferredReleaseEntry::CommandBuffer;
+ e.type = QRhiVulkan::DeferredReleaseEntry::SecondaryCommandBuffer;
e.lastActiveFrameSlot = currentFrameSlot;
- e.commandBuffer.cb = cb;
+ e.secondaryCommandBuffer.cb = cb;
releaseQueue.append(e);
}
@@ -2209,7 +2777,8 @@ void QRhiVulkan::beginPass(QRhiCommandBuffer *cb,
QRhiRenderTarget *rt,
const QColor &colorClearValue,
const QRhiDepthStencilClearValue &depthStencilClearValue,
- QRhiResourceUpdateBatch *resourceUpdates)
+ QRhiResourceUpdateBatch *resourceUpdates,
+ QRhiCommandBuffer::BeginPassFlags flags)
{
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::NoPass);
@@ -2225,8 +2794,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 =
@@ -2245,13 +2814,13 @@ void QRhiVulkan::beginPass(QRhiCommandBuffer *cb,
}
cbD->recordingPass = QVkCommandBuffer::RenderPass;
+ cbD->passUsesSecondaryCb = flags.testFlag(QRhiCommandBuffer::ExternalContent);
cbD->currentTarget = rt;
// 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;
@@ -2276,17 +2845,24 @@ void QRhiVulkan::beginPass(QRhiCommandBuffer *cb,
float(colorClearValue.alphaF()) } };
cvs.append(cv);
}
- rpBeginInfo.clearValueCount = uint32_t(cvs.count());
+ for (int i = 0; i < rtD->dsResolveAttCount; ++i) {
+ VkClearValue cv;
+ cv.depthStencil = { depthStencilClearValue.depthClearValue(), depthStencilClearValue.stencilClearValue() };
+ cvs.append(cv);
+ }
+ rpBeginInfo.clearValueCount = uint32_t(cvs.size());
- QVkCommandBuffer::Command cmd;
+ QVkCommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QVkCommandBuffer::Command::BeginRenderPass;
cmd.args.beginRenderPass.desc = rpBeginInfo;
- cmd.args.beginRenderPass.clearValueIndex = cbD->pools.clearValue.count();
- cbD->pools.clearValue.append(cvs.constData(), cvs.count());
- cbD->commands.append(cmd);
+ cmd.args.beginRenderPass.clearValueIndex = cbD->pools.clearValue.size();
+ cmd.args.beginRenderPass.useSecondaryCb = cbD->passUsesSecondaryCb;
+ cbD->pools.clearValue.append(cvs.constData(), cvs.size());
+
+ if (cbD->passUsesSecondaryCb)
+ cbD->activeSecondaryCbStack.append(startSecondaryCommandBuffer(rtD));
- if (cbD->useSecondaryCb)
- cbD->secondaryCbs.append(startSecondaryCommandBuffer(rtD));
+ cbD->resetCachedState();
}
void QRhiVulkan::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
@@ -2294,16 +2870,14 @@ void QRhiVulkan::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourc
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
- if (cbD->useSecondaryCb) {
- VkCommandBuffer secondaryCb = cbD->secondaryCbs.last();
- cbD->secondaryCbs.removeLast();
+ if (cbD->passUsesSecondaryCb) {
+ VkCommandBuffer secondaryCb = cbD->activeSecondaryCbStack.last();
+ cbD->activeSecondaryCbStack.removeLast();
endAndEnqueueSecondaryCommandBuffer(secondaryCb, cbD);
- cbD->resetCachedState();
}
- QVkCommandBuffer::Command cmd;
+ QVkCommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QVkCommandBuffer::Command::EndRenderPass;
- cbD->commands.append(cmd);
cbD->recordingPass = QVkCommandBuffer::NoPass;
cbD->currentTarget = nullptr;
@@ -2312,7 +2886,9 @@ void QRhiVulkan::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourc
enqueueResourceUpdates(cbD, resourceUpdates);
}
-void QRhiVulkan::beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
+void QRhiVulkan::beginComputePass(QRhiCommandBuffer *cb,
+ QRhiResourceUpdateBatch *resourceUpdates,
+ QRhiCommandBuffer::BeginPassFlags flags)
{
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::NoPass);
@@ -2323,11 +2899,14 @@ void QRhiVulkan::beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch
enqueueTransitionPassResources(cbD);
cbD->recordingPass = QVkCommandBuffer::ComputePass;
+ cbD->passUsesSecondaryCb = flags.testFlag(QRhiCommandBuffer::ExternalContent);
cbD->computePassState.reset();
- if (cbD->useSecondaryCb)
- cbD->secondaryCbs.append(startSecondaryCommandBuffer());
+ if (cbD->passUsesSecondaryCb)
+ cbD->activeSecondaryCbStack.append(startSecondaryCommandBuffer());
+
+ cbD->resetCachedState();
}
void QRhiVulkan::endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
@@ -2335,11 +2914,10 @@ void QRhiVulkan::endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::ComputePass);
- if (cbD->useSecondaryCb) {
- VkCommandBuffer secondaryCb = cbD->secondaryCbs.last();
- cbD->secondaryCbs.removeLast();
+ if (cbD->passUsesSecondaryCb) {
+ VkCommandBuffer secondaryCb = cbD->activeSecondaryCbStack.last();
+ cbD->activeSecondaryCbStack.removeLast();
endAndEnqueueSecondaryCommandBuffer(secondaryCb, cbD);
- cbD->resetCachedState();
}
cbD->recordingPass = QVkCommandBuffer::NoPass;
@@ -2356,14 +2934,13 @@ void QRhiVulkan::setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::ComputePass);
if (cbD->currentComputePipeline != ps || cbD->currentPipelineGeneration != psD->generation) {
- if (cbD->useSecondaryCb) {
- df->vkCmdBindPipeline(cbD->secondaryCbs.last(), VK_PIPELINE_BIND_POINT_COMPUTE, psD->pipeline);
+ if (cbD->passUsesSecondaryCb) {
+ df->vkCmdBindPipeline(cbD->activeSecondaryCbStack.last(), VK_PIPELINE_BIND_POINT_COMPUTE, psD->pipeline);
} else {
- QVkCommandBuffer::Command cmd;
+ QVkCommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QVkCommandBuffer::Command::BindPipeline;
cmd.args.bindPipeline.bindPoint = VK_PIPELINE_BIND_POINT_COMPUTE;
cmd.args.bindPipeline.pipeline = psD->pipeline;
- cbD->commands.append(cmd);
}
cbD->currentGraphicsPipeline = nullptr;
@@ -2411,9 +2988,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:
@@ -2446,8 +3023,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
@@ -2463,8 +3039,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;
@@ -2485,53 +3060,51 @@ void QRhiVulkan::dispatch(QRhiCommandBuffer *cb, int x, int y, int z)
}
}
- if (cbD->useSecondaryCb) {
- VkCommandBuffer secondaryCb = cbD->secondaryCbs.last();
+ if (cbD->passUsesSecondaryCb) {
+ VkCommandBuffer secondaryCb = cbD->activeSecondaryCbStack.last();
if (!imageBarriers.isEmpty()) {
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));
} else {
- QVkCommandBuffer::Command cmd;
if (!imageBarriers.isEmpty()) {
+ QVkCommandBuffer::Command &cmd(cbD->commands.get());
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());
- cbD->commands.append(cmd);
+ 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());
- cbD->commands.append(cmd);
+ 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;
cmd.args.dispatch.x = x;
cmd.args.dispatch.y = y;
cmd.args.dispatch.z = z;
- cbD->commands.append(cmd);
}
}
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());
@@ -2544,14 +3117,15 @@ VkShaderModule QRhiVulkan::createShader(const QByteArray &spirv)
return shaderModule;
}
-bool QRhiVulkan::ensurePipelineCache()
+bool QRhiVulkan::ensurePipelineCache(const void *initialData, size_t initialDataSize)
{
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;
VkResult err = df->vkCreatePipelineCache(dev, &pipelineCacheInfo, nullptr, &pipelineCache);
if (err != VK_SUCCESS) {
qWarning("Failed to create pipeline cache: %d", err);
@@ -2573,12 +3147,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);
@@ -2598,17 +3171,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);
@@ -2624,7 +3197,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;
@@ -2633,7 +3243,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;
@@ -2642,7 +3252,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);
}
}
@@ -2657,9 +3267,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;
@@ -2673,7 +3283,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)
@@ -2682,7 +3292,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)
@@ -2714,8 +3324,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;
@@ -2724,14 +3333,13 @@ void QRhiVulkan::trackedBufferBarrier(QVkCommandBuffer *cbD, QVkBuffer *bufD, in
bufMemBarrier.buffer = bufD->buffers[slot];
bufMemBarrier.size = VK_WHOLE_SIZE;
- QVkCommandBuffer::Command cmd;
+ QVkCommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QVkCommandBuffer::Command::BufferBarrier;
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);
- cbD->commands.append(cmd);
s.access = access;
s.stage = stage;
@@ -2748,8 +3356,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;
@@ -2767,20 +3374,49 @@ void QRhiVulkan::trackedImageBarrier(QVkCommandBuffer *cbD, QVkTexture *texD,
if (!srcStage)
srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
- QVkCommandBuffer::Command cmd;
+ QVkCommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QVkCommandBuffer::Command::ImageBarrier;
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);
- cbD->commands.append(cmd);
s.layout = layout;
s.access = access;
s.stage = stage;
}
+void QRhiVulkan::depthStencilExplicitBarrier(QVkCommandBuffer *cbD, QVkRenderBuffer *rbD)
+{
+ Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::NoPass);
+
+ 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;
+ barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
+ barrier.subresourceRange.baseArrayLayer = 0;
+ barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
+ barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ barrier.newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+ barrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
+ barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT
+ | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
+ barrier.image = rbD->image;
+
+ const VkPipelineStageFlags stages = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT
+ | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
+
+ QVkCommandBuffer::Command &cmd(cbD->commands.get());
+ cmd.cmd = QVkCommandBuffer::Command::ImageBarrier;
+ cmd.args.imageBarrier.srcStageMask = stages;
+ cmd.args.imageBarrier.dstStageMask = stages;
+ cmd.args.imageBarrier.count = 1;
+ cmd.args.imageBarrier.index = cbD->pools.imageBarrier.size();
+ cbD->pools.imageBarrier.append(barrier);
+}
+
void QRhiVulkan::subresourceBarrier(QVkCommandBuffer *cbD, VkImage image,
VkImageLayout oldLayout, VkImageLayout newLayout,
VkAccessFlags srcAccess, VkAccessFlags dstAccess,
@@ -2789,8 +3425,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);
@@ -2803,14 +3438,13 @@ void QRhiVulkan::subresourceBarrier(QVkCommandBuffer *cbD, VkImage image,
barrier.dstAccessMask = dstAccess;
barrier.image = image;
- QVkCommandBuffer::Command cmd;
+ QVkCommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QVkCommandBuffer::Command::ImageBarrier;
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);
- cbD->commands.append(cmd);
}
VkDeviceSize QRhiVulkan::subresUploadByteSize(const QRhiTextureSubresourceUploadDescription &subresDesc) const
@@ -2831,15 +3465,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();
@@ -2903,6 +3542,12 @@ void QRhiVulkan::prepareUploadSubres(QVkTexture *texD, int layer, int level,
copySizeBytes = imageSizeBytes = rawData.size();
src = rawData.constData();
QSize size = q->sizeForMipLevel(level, texD->m_pixelSize);
+ if (subresDesc.dataStride()) {
+ quint32 bytesPerPixel = 0;
+ textureFormatInfo(texD->m_format, size, nullptr, nullptr, &bytesPerPixel);
+ if (bytesPerPixel)
+ copyInfo.bufferRowLength = subresDesc.dataStride() / bytesPerPixel;
+ }
if (!subresDesc.sourceSize().isEmpty())
size = subresDesc.sourceSize();
copyInfo.imageOffset.x = dp.x();
@@ -2914,37 +3559,46 @@ void QRhiVulkan::prepareUploadSubres(QVkTexture *texD, int layer, int level,
qWarning("Invalid texture upload for %p layer=%d mip=%d", texD, layer, level);
}
- memcpy(reinterpret_cast<char *>(mp) + *curOfs, src, size_t(copySizeBytes));
- *curOfs += aligned(VkDeviceSize(imageSizeBytes), texbufAlign);
+ if (src) {
+ memcpy(reinterpret_cast<char *>(mp) + *curOfs, src, size_t(copySizeBytes));
+ *curOfs += aligned(VkDeviceSize(imageSizeBytes), texbufAlign);
+ }
+}
+
+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 (const QRhiResourceUpdateBatchPrivate::BufferOp &u : ud->bufferOps) {
+ for (int opIdx = 0; opIdx < ud->activeBufferOpCount; ++opIdx) {
+ const QRhiResourceUpdateBatchPrivate::BufferOp &u(ud->bufferOps[opIdx]);
if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::DynamicUpdate) {
QVkBuffer *bufD = QRHI_RES(QVkBuffer, u.buf);
Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
- for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
- bufD->pendingDynamicUpdates[i].append(u);
+ for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
+ if (u.offset == 0 && u.data.size() == bufD->m_size)
+ bufD->pendingDynamicUpdates[i].clear();
+ bufD->pendingDynamicUpdates[i].append({ u.offset, u.data });
+ }
} else if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::StaticUpload) {
QVkBuffer *bufD = QRHI_RES(QVkBuffer, u.buf);
Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic);
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;
@@ -2952,9 +3606,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;
}
}
@@ -2966,25 +3620,23 @@ 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;
+ QVkCommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QVkCommandBuffer::Command::CopyBuffer;
cmd.args.copyBuffer.src = bufD->stagingBuffers[currentFrameSlot];
cmd.args.copyBuffer.dst = bufD->buffers[0];
cmd.args.copyBuffer.desc = copyInfo;
- cbD->commands.append(cmd);
// Where's the barrier for read-after-write? (assuming the common case
// of binding this buffer as vertex/index, or, less likely, as uniform
@@ -3004,7 +3656,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);
@@ -3015,7 +3666,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)
@@ -3032,39 +3683,35 @@ 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;
+ QVkCommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QVkCommandBuffer::Command::CopyBuffer;
cmd.args.copyBuffer.src = bufD->buffers[0];
cmd.args.copyBuffer.dst = readback.stagingBuf;
cmd.args.copyBuffer.desc = copyInfo;
- cbD->commands.append(cmd);
bufD->lastActiveFrameSlot = currentFrameSlot;
@@ -3073,27 +3720,26 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
}
}
- for (const QRhiResourceUpdateBatchPrivate::TextureOp &u : ud->textureOps) {
+ for (int opIdx = 0; opIdx < ud->activeTextureOpCount; ++opIdx) {
+ const QRhiResourceUpdateBatchPrivate::TextureOp &u(ud->textureOps[opIdx]);
if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) {
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;
@@ -3101,10 +3747,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;
@@ -3116,32 +3762,31 @@ 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);
- QVkCommandBuffer::Command cmd;
+ QVkCommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QVkCommandBuffer::Command::CopyBufferToImage;
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());
- cbD->commands.append(cmd);
+ 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;
@@ -3152,7 +3797,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.
@@ -3166,25 +3810,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();
@@ -3197,14 +3845,13 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
trackedImageBarrier(cbD, dstD, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
- QVkCommandBuffer::Command cmd;
+ QVkCommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QVkCommandBuffer::Command::CopyImage;
cmd.args.copyImage.src = srcD->image;
cmd.args.copyImage.srcLayout = srcD->usageState.layout;
cmd.args.copyImage.dst = dstD->image;
cmd.args.copyImage.dstLayout = dstD->usageState.layout;
cmd.args.copyImage.desc = region;
- cbD->commands.append(cmd);
srcD->lastActiveFrameSlot = dstD->lastActiveFrameSlot = currentFrameSlot;
} else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) {
@@ -3215,11 +3862,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;
@@ -3231,46 +3880,43 @@ 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;
// Multisample swapchains need nothing special since resolving
// happens when ending a renderpass.
}
- textureFormatInfo(readback.format, readback.pixelSize, nullptr, &readback.byteSize);
+ 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;
@@ -3278,13 +3924,12 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
if (texD) {
trackedImageBarrier(cbD, texD, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
- QVkCommandBuffer::Command cmd;
+ QVkCommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QVkCommandBuffer::Command::CopyImageToBuffer;
cmd.args.copyImageToBuffer.src = texD->image;
cmd.args.copyImageToBuffer.srcLayout = texD->usageState.layout;
cmd.args.copyImageToBuffer.dst = readback.stagingBuf;
cmd.args.copyImageToBuffer.desc = copyDesc;
- cbD->commands.append(cmd);
} else {
// use the swapchain image
QVkSwapChain::ImageResources &imageRes(swapChainD->imageRes[swapChainD->currentImageIndex]);
@@ -3303,21 +3948,21 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
imageRes.lastUse = QVkSwapChain::ImageResources::ScImageUseTransferSource;
}
- QVkCommandBuffer::Command cmd;
+ QVkCommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QVkCommandBuffer::Command::CopyImageToBuffer;
cmd.args.copyImageToBuffer.src = image;
cmd.args.copyImageToBuffer.srcLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
cmd.args.copyImageToBuffer.dst = readback.stagingBuf;
cmd.args.copyImageToBuffer.desc = copyDesc;
- cbD->commands.append(cmd);
}
activeTextureReadbacks.append(readback);
} else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::GenMips) {
QVkTexture *utexD = QRHI_RES(QVkTexture, u.dst);
Q_ASSERT(utexD->m_flags.testFlag(QRhiTexture::UsedWithGenerateMips));
- int w = utexD->m_pixelSize.width();
- int h = utexD->m_pixelSize.height();
+ 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;
@@ -3325,80 +3970,82 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
if (!origStage)
origStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
- for (int level = 1; level < int(utexD->mipLevelCount); ++level) {
- if (level == 1) {
+ 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,
+ origLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ origAccess, VK_ACCESS_TRANSFER_READ_BIT,
+ origStage, VK_PIPELINE_STAGE_TRANSFER_BIT,
+ layer, 1,
+ level - 1, 1);
+ } else {
+ subresourceBarrier(cbD, utexD->image,
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
+ VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
+ layer, 1,
+ level - 1, 1);
+ }
+
subresourceBarrier(cbD, utexD->image,
- origLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
- origAccess, VK_ACCESS_TRANSFER_READ_BIT,
+ origLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ origAccess, VK_ACCESS_TRANSFER_WRITE_BIT,
origStage, VK_PIPELINE_STAGE_TRANSFER_BIT,
- u.layer, 1,
- level - 1, 1);
- } else {
- subresourceBarrier(cbD, utexD->image,
- VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
- VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
- VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
- u.layer, 1,
- level - 1, 1);
+ layer, 1,
+ level, 1);
+
+ VkImageBlit region = {};
+ region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ region.srcSubresource.mipLevel = uint32_t(level) - 1;
+ region.srcSubresource.baseArrayLayer = uint32_t(layer);
+ region.srcSubresource.layerCount = 1;
+
+ region.srcOffsets[1].x = qMax(1, w);
+ region.srcOffsets[1].y = qMax(1, h);
+ region.srcOffsets[1].z = qMax(1, depth);
+
+ region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ region.dstSubresource.mipLevel = uint32_t(level);
+ region.dstSubresource.baseArrayLayer = uint32_t(layer);
+ region.dstSubresource.layerCount = 1;
+
+ region.dstOffsets[1].x = qMax(1, w >> 1);
+ region.dstOffsets[1].y = qMax(1, h >> 1);
+ region.dstOffsets[1].z = qMax(1, depth >> 1);
+
+ QVkCommandBuffer::Command &cmd(cbD->commands.get());
+ cmd.cmd = QVkCommandBuffer::Command::BlitImage;
+ cmd.args.blitImage.src = utexD->image;
+ cmd.args.blitImage.srcLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
+ cmd.args.blitImage.dst = utexD->image;
+ cmd.args.blitImage.dstLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
+ cmd.args.blitImage.filter = VK_FILTER_LINEAR;
+ cmd.args.blitImage.desc = region;
+
+ w >>= 1;
+ h >>= 1;
+ depth >>= 1;
}
- subresourceBarrier(cbD, utexD->image,
- origLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
- origAccess, VK_ACCESS_TRANSFER_WRITE_BIT,
- origStage, VK_PIPELINE_STAGE_TRANSFER_BIT,
- u.layer, 1,
- level, 1);
-
- VkImageBlit region;
- memset(&region, 0, sizeof(region));
-
- region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
- region.srcSubresource.mipLevel = uint32_t(level) - 1;
- region.srcSubresource.baseArrayLayer = uint32_t(u.layer);
- region.srcSubresource.layerCount = 1;
-
- region.srcOffsets[1].x = qMax(1, w);
- region.srcOffsets[1].y = qMax(1, h);
- region.srcOffsets[1].z = 1;
-
- region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
- region.dstSubresource.mipLevel = uint32_t(level);
- region.dstSubresource.baseArrayLayer = uint32_t(u.layer);
- region.dstSubresource.layerCount = 1;
-
- region.dstOffsets[1].x = qMax(1, w >> 1);
- region.dstOffsets[1].y = qMax(1, h >> 1);
- region.dstOffsets[1].z = 1;
-
- QVkCommandBuffer::Command cmd;
- cmd.cmd = QVkCommandBuffer::Command::BlitImage;
- cmd.args.blitImage.src = utexD->image;
- cmd.args.blitImage.srcLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
- cmd.args.blitImage.dst = utexD->image;
- cmd.args.blitImage.dstLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
- cmd.args.blitImage.filter = VK_FILTER_LINEAR;
- cmd.args.blitImage.desc = region;
- cbD->commands.append(cmd);
-
- w >>= 1;
- h >>= 1;
- }
-
- if (utexD->mipLevelCount > 1) {
- subresourceBarrier(cbD, utexD->image,
- VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, origLayout,
- VK_ACCESS_TRANSFER_READ_BIT, origAccess,
- VK_PIPELINE_STAGE_TRANSFER_BIT, origStage,
- u.layer, 1,
- 0, int(utexD->mipLevelCount) - 1);
- subresourceBarrier(cbD, utexD->image,
- VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, origLayout,
- VK_ACCESS_TRANSFER_WRITE_BIT, origAccess,
- VK_PIPELINE_STAGE_TRANSFER_BIT, origStage,
- u.layer, 1,
- int(utexD->mipLevelCount) - 1, 1);
+ if (utexD->mipLevelCount > 1) {
+ subresourceBarrier(cbD, utexD->image,
+ VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, origLayout,
+ VK_ACCESS_TRANSFER_READ_BIT, origAccess,
+ VK_PIPELINE_STAGE_TRANSFER_BIT, origStage,
+ layer, 1,
+ 0, int(utexD->mipLevelCount) - 1);
+ subresourceBarrier(cbD, utexD->image,
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, origLayout,
+ VK_ACCESS_TRANSFER_WRITE_BIT, origAccess,
+ VK_PIPELINE_STAGE_TRANSFER_BIT, origStage,
+ layer, 1,
+ int(utexD->mipLevelCount) - 1, 1);
+ }
}
-
utexD->lastActiveFrameSlot = currentFrameSlot;
}
}
@@ -3422,19 +4069,18 @@ void QRhiVulkan::executeBufferHostWritesForSlot(QVkBuffer *bufD, int slot)
qWarning("Failed to map buffer: %d", err);
return;
}
- int changeBegin = -1;
- int changeEnd = -1;
- for (const QRhiResourceUpdateBatchPrivate::BufferOp &u : qAsConst(bufD->pendingDynamicUpdates[slot])) {
- Q_ASSERT(bufD == QRHI_RES(QVkBuffer, u.buf));
- 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();
}
@@ -3460,7 +4106,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);
}
@@ -3473,7 +4119,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) {
@@ -3506,6 +4152,8 @@ void QRhiVulkan::executeDeferredReleases(bool forced)
df->vkDestroyImageView(dev, e.textureRenderTarget.rtv[att], nullptr);
df->vkDestroyImageView(dev, e.textureRenderTarget.resrtv[att], nullptr);
}
+ df->vkDestroyImageView(dev, e.textureRenderTarget.dsv, nullptr);
+ df->vkDestroyImageView(dev, e.textureRenderTarget.resdsv, nullptr);
break;
case QRhiVulkan::DeferredReleaseEntry::RenderPass:
df->vkDestroyRenderPass(dev, e.renderPass.rp, nullptr);
@@ -3513,8 +4161,8 @@ void QRhiVulkan::executeDeferredReleases(bool forced)
case QRhiVulkan::DeferredReleaseEntry::StagingBuffer:
vmaDestroyBuffer(toVmaAllocator(allocator), e.stagingBuffer.stagingBuffer, toVmaAllocation(e.stagingBuffer.stagingAllocation));
break;
- case QRhiVulkan::DeferredReleaseEntry::CommandBuffer:
- df->vkFreeCommandBuffers(dev, cmdPool, 1, &e.commandBuffer.cb);
+ case QRhiVulkan::DeferredReleaseEntry::SecondaryCommandBuffer:
+ freeSecondaryCbs[e.lastActiveFrameSlot].append(e.secondaryCommandBuffer.cb);
break;
default:
Q_UNREACHABLE();
@@ -3528,9 +4176,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;
@@ -3547,7 +4194,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);
@@ -3556,7 +4202,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);
@@ -3564,14 +4210,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);
@@ -3618,41 +4263,34 @@ 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;
+ QVkCommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QVkCommandBuffer::Command::TransitionPassResources;
- cmd.args.transitionResources.trackerIndex = cbD->passResTrackers.count() - 1;
- cbD->commands.append(cmd);
+ cmd.args.transitionResources.trackerIndex = cbD->passResTrackers.size() - 1;
}
void QRhiVulkan::recordPrimaryCommandBuffer(QVkCommandBuffer *cbD)
{
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::NoPass);
- for (QVkCommandBuffer::Command &cmd : cbD->commands) {
+ for (auto it = cbD->commands.begin(), end = cbD->commands.end(); it != end; ++it) {
+ QVkCommandBuffer::Command &cmd(*it);
switch (cmd.cmd) {
case QVkCommandBuffer::Command::CopyBuffer:
df->vkCmdCopyBuffer(cbD->cb, cmd.args.copyBuffer.src, cmd.args.copyBuffer.dst,
@@ -3694,7 +4332,8 @@ void QRhiVulkan::recordPrimaryCommandBuffer(QVkCommandBuffer *cbD)
case QVkCommandBuffer::Command::BeginRenderPass:
cmd.args.beginRenderPass.desc.pClearValues = cbD->pools.clearValue.constData() + cmd.args.beginRenderPass.clearValueIndex;
df->vkCmdBeginRenderPass(cbD->cb, &cmd.args.beginRenderPass.desc,
- cbD->useSecondaryCb ? VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS : VK_SUBPASS_CONTENTS_INLINE);
+ cmd.args.beginRenderPass.useSecondaryCb ? VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS
+ : VK_SUBPASS_CONTENTS_INLINE);
break;
case QVkCommandBuffer::Command::EndRenderPass:
df->vkCmdEndRenderPass(cbD->cb);
@@ -3746,17 +4385,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]);
@@ -3802,10 +4447,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;
@@ -3868,6 +4519,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:
@@ -3876,6 +4531,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;
@@ -3899,9 +4556,15 @@ void QRhiVulkan::trackedRegisterBuffer(QRhiPassResourceTracker *passResTracker,
QRhiPassResourceTracker::BufferStage stage)
{
QVkBuffer::UsageState &u(bufD->usageState[slot]);
+ const VkAccessFlags newAccess = toVkAccess(access);
+ const VkPipelineStageFlags newStage = toVkPipelineStage(stage);
+ if (u.access == newAccess && u.stage == newStage) {
+ if (!accessIsWrite(access))
+ return;
+ }
passResTracker->registerBuffer(bufD, slot, &access, &stage, toPassTrackerUsageState(u));
- u.access = toVkAccess(access);
- u.stage = toVkPipelineStage(stage);
+ u.access = newAccess;
+ u.stage = newStage;
}
void QRhiVulkan::trackedRegisterTexture(QRhiPassResourceTracker *passResTracker,
@@ -3910,10 +4573,17 @@ void QRhiVulkan::trackedRegisterTexture(QRhiPassResourceTracker *passResTracker,
QRhiPassResourceTracker::TextureStage stage)
{
QVkTexture::UsageState &u(texD->usageState);
+ const VkAccessFlags newAccess = toVkAccess(access);
+ const VkPipelineStageFlags newStage = toVkPipelineStage(stage);
+ const VkImageLayout newLayout = toVkLayout(access);
+ if (u.access == newAccess && u.stage == newStage && u.layout == newLayout) {
+ if (!accessIsWrite(access))
+ return;
+ }
passResTracker->registerTexture(texD, &access, &stage, toPassTrackerUsageState(u));
- u.layout = toVkLayout(access);
- u.access = toVkAccess(access);
- u.stage = toVkPipelineStage(stage);
+ u.layout = newLayout;
+ u.access = newAccess;
+ u.stage = newStage;
}
void QRhiVulkan::recordTransitionPassResources(QVkCommandBuffer *cbD, const QRhiPassResourceTracker &tracker)
@@ -3932,8 +4602,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;
@@ -3957,8 +4626,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;
@@ -3983,10 +4651,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);
}
@@ -4060,13 +4736,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:
@@ -4080,9 +4756,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:
@@ -4099,15 +4775,50 @@ bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const
return true;
case QRhi::RenderToNonBaseMipLevel:
return true;
- case QRhi::UIntAttributes:
+ case QRhi::IntAttributes:
return true;
case QRhi::ScreenSpaceDerivatives:
return true;
case QRhi::ReadBackAnyTextureFormat:
return true;
- default:
- Q_UNREACHABLE();
+ case QRhi::PipelineCacheDataLoadSave:
+ return true;
+ case QRhi::ImageDataStride:
+ return true;
+ 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;
+ case QRhi::ResolveDepthStencil:
+ return caps.renderPass2KHR && caps.depthStencilResolveKHR;
+ default:
+ Q_UNREACHABLE_RETURN(false);
}
}
@@ -4136,9 +4847,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);
}
}
@@ -4147,16 +4865,29 @@ const QRhiNativeHandles *QRhiVulkan::nativeHandles()
return &nativeHandlesStruct;
}
-void QRhiVulkan::sendVMemStatsToProfiler()
+QRhiDriverInfo QRhiVulkan::driverInfo() const
{
- QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
- if (!rhiP)
- return;
+ return driverInfoStruct;
+}
- VmaStats stats;
- vmaCalculateStats(toVmaAllocator(allocator), &stats);
- QRHI_PROF_F(vmemStat(stats.total.blockCount, stats.total.allocationCount,
- quint32(stats.total.usedBytes), quint32(stats.total.unusedBytes)));
+QRhiStats QRhiVulkan::statistics()
+{
+ QRhiStats result;
+ result.totalPipelineCreationTime = totalPipelineCreationTime();
+
+ VmaBudget budgets[VK_MAX_MEMORY_HEAPS];
+ vmaGetHeapBudgets(toVmaAllocator(allocator), budgets);
+
+ uint32_t count = toVmaAllocator(allocator)->GetMemoryHeapCount();
+ for (uint32_t i = 0; i < count; ++i) {
+ const VmaStatistics &stats(budgets[i].statistics);
+ result.blockCount += stats.blockCount;
+ result.allocCount += stats.allocationCount;
+ result.usedBytes += stats.allocationBytes;
+ result.unusedBytes += stats.blockBytes - stats.allocationBytes;
+ }
+
+ return result;
}
bool QRhiVulkan::makeThreadLocalNativeContextCurrent()
@@ -4167,7 +4898,7 @@ bool QRhiVulkan::makeThreadLocalNativeContextCurrent()
void QRhiVulkan::releaseCachedResources()
{
- // nothing to do here
+ releaseCachedResourcesCalledBeforeFrameStart = true;
}
bool QRhiVulkan::isDeviceLost() const
@@ -4175,6 +4906,130 @@ bool QRhiVulkan::isDeviceLost() const
return deviceLost;
}
+struct QVkPipelineCacheDataHeader
+{
+ quint32 rhiId;
+ quint32 arch;
+ quint32 driverVersion;
+ quint32 vendorId;
+ quint32 deviceId;
+ quint32 dataSize;
+ quint32 uuidSize;
+ quint32 reserved;
+};
+
+QByteArray QRhiVulkan::pipelineCacheData()
+{
+ Q_STATIC_ASSERT(sizeof(QVkPipelineCacheDataHeader) == 32);
+
+ QByteArray data;
+ if (!pipelineCache || !rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
+ return data;
+
+ size_t dataSize = 0;
+ VkResult err = df->vkGetPipelineCacheData(dev, pipelineCache, &dataSize, nullptr);
+ if (err != VK_SUCCESS) {
+ qCDebug(QRHI_LOG_INFO, "Failed to get pipeline cache data size: %d", err);
+ return QByteArray();
+ }
+ const size_t headerSize = sizeof(QVkPipelineCacheDataHeader);
+ const size_t dataOffset = headerSize + VK_UUID_SIZE;
+ data.resize(dataOffset + dataSize);
+ err = df->vkGetPipelineCacheData(dev, pipelineCache, &dataSize, data.data() + dataOffset);
+ if (err != VK_SUCCESS) {
+ qCDebug(QRHI_LOG_INFO, "Failed to get pipeline cache data of %d bytes: %d", int(dataSize), err);
+ return QByteArray();
+ }
+
+ QVkPipelineCacheDataHeader header;
+ header.rhiId = pipelineCacheRhiId();
+ header.arch = quint32(sizeof(void*));
+ header.driverVersion = physDevProperties.driverVersion;
+ header.vendorId = physDevProperties.vendorID;
+ 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);
+
+ return data;
+}
+
+void QRhiVulkan::setPipelineCacheData(const QByteArray &data)
+{
+ if (data.isEmpty())
+ return;
+
+ const size_t headerSize = sizeof(QVkPipelineCacheDataHeader);
+ if (data.size() < qsizetype(headerSize)) {
+ qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob size");
+ return;
+ }
+ QVkPipelineCacheDataHeader header;
+ memcpy(&header, data.constData(), headerSize);
+
+ const quint32 rhiId = pipelineCacheRhiId();
+ if (header.rhiId != 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) {
+ qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Architecture does not match (%u, %u)",
+ arch, header.arch);
+ return;
+ }
+ if (header.driverVersion != physDevProperties.driverVersion) {
+ qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: driverVersion does not match (%u, %u)",
+ physDevProperties.driverVersion, header.driverVersion);
+ return;
+ }
+ if (header.vendorId != physDevProperties.vendorID) {
+ qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: vendorID does not match (%u, %u)",
+ physDevProperties.vendorID, header.vendorId);
+ return;
+ }
+ if (header.deviceId != physDevProperties.deviceID) {
+ qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: deviceID does not match (%u, %u)",
+ physDevProperties.deviceID, header.deviceId);
+ return;
+ }
+ if (header.uuidSize != VK_UUID_SIZE) {
+ 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)) {
+ qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob, no uuid");
+ return;
+ }
+ if (memcmp(data.constData() + headerSize, physDevProperties.pipelineCacheUUID, VK_UUID_SIZE)) {
+ 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)) {
+ qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob, data missing");
+ return;
+ }
+
+ if (pipelineCache) {
+ df->vkDestroyPipelineCache(dev, pipelineCache, nullptr);
+ pipelineCache = VK_NULL_HANDLE;
+ }
+
+ if (ensurePipelineCache(data.constData() + dataOffset, header.dataSize)) {
+ qCDebug(QRHI_LOG_INFO, "Created pipeline cache with initial data of %d bytes",
+ int(header.dataSize));
+ } else {
+ qCDebug(QRHI_LOG_INFO, "Failed to create pipeline cache with initial data specified");
+ }
+}
+
QRhiRenderBuffer *QRhiVulkan::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
int sampleCount, QRhiRenderBuffer::Flags flags,
QRhiTexture::Format backingFormatHint)
@@ -4182,10 +5037,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,
@@ -4224,14 +5080,13 @@ void QRhiVulkan::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
if (cbD->currentGraphicsPipeline != ps || cbD->currentPipelineGeneration != psD->generation) {
- if (cbD->useSecondaryCb) {
- df->vkCmdBindPipeline(cbD->secondaryCbs.last(), VK_PIPELINE_BIND_POINT_GRAPHICS, psD->pipeline);
+ if (cbD->passUsesSecondaryCb) {
+ df->vkCmdBindPipeline(cbD->activeSecondaryCbStack.last(), VK_PIPELINE_BIND_POINT_GRAPHICS, psD->pipeline);
} else {
- QVkCommandBuffer::Command cmd;
+ QVkCommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QVkCommandBuffer::Command::BindPipeline;
cmd.args.bindPipeline.bindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
cmd.args.bindPipeline.pipeline = psD->pipeline;
- cbD->commands.append(cmd);
}
cbD->currentGraphicsPipeline = ps;
@@ -4248,6 +5103,7 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin
{
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
Q_ASSERT(cbD->recordingPass != QVkCommandBuffer::NoPass);
+ QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
QVkGraphicsPipeline *gfxPsD = QRHI_RES(QVkGraphicsPipeline, cbD->currentGraphicsPipeline);
QVkComputePipeline *compPsD = QRHI_RES(QVkComputePipeline, cbD->currentComputePipeline);
@@ -4259,32 +5115,15 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin
}
QVkShaderResourceBindings *srbD = QRHI_RES(QVkShaderResourceBindings, srb);
- bool hasSlottedResourceInSrb = false;
- bool hasDynamicOffsetInSrb = false;
-
- for (const QRhiShaderResourceBinding &binding : qAsConst(srbD->sortedBindings)) {
- const QRhiShaderResourceBinding::Data *b = binding.data();
- switch (b->type) {
- case QRhiShaderResourceBinding::UniformBuffer:
- if (QRHI_RES(QVkBuffer, b->u.ubuf.buf)->m_type == QRhiBuffer::Dynamic)
- hasSlottedResourceInSrb = true;
- if (b->u.ubuf.hasDynamicOffset)
- hasDynamicOffsetInSrb = true;
- break;
- default:
- break;
- }
- }
-
- const int descSetIdx = hasSlottedResourceInSrb ? currentFrameSlot : 0;
+ const int descSetIdx = srbD->hasSlottedResource ? currentFrameSlot : 0;
+ auto &descSetBd(srbD->boundResourceData[descSetIdx]);
bool rewriteDescSet = false;
// 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.at(i).data();
- QVkShaderResourceBindings::BoundResourceData &bd(srbD->boundResourceData[descSetIdx][i]);
- QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
+ 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:
{
@@ -4311,8 +5150,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;
@@ -4320,21 +5161,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;
}
}
}
@@ -4405,25 +5257,25 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin
// make sure the descriptors for the correct slot will get bound.
// also, dynamic offsets always need a bind.
- const bool forceRebind = (hasSlottedResourceInSrb && cbD->currentDescSetSlot != descSetIdx) || hasDynamicOffsetInSrb;
+ const bool forceRebind = (srbD->hasSlottedResource && cbD->currentDescSetSlot != descSetIdx) || srbD->hasDynamicOffset;
const bool srbChanged = gfxPsD ? (cbD->currentGraphicsSrb != srb) : (cbD->currentComputeSrb != srb);
if (forceRebind || rewriteDescSet || srbChanged || cbD->currentSrbGeneration != srbD->generation) {
QVarLengthArray<uint32_t, 4> dynOfs;
- if (hasDynamicOffsetInSrb) {
+ if (srbD->hasDynamicOffset) {
// Filling out dynOfs based on the sorted bindings is important
// 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) {
- const QRhiCommandBuffer::DynamicOffset &dynOfs(dynamicOffsets[i]);
- if (dynOfs.first == b->binding) {
- offset = dynOfs.second;
+ const QRhiCommandBuffer::DynamicOffset &bindingOffsetPair(dynamicOffsets[i]);
+ if (bindingOffsetPair.first == b->binding) {
+ offset = bindingOffsetPair.second;
break;
}
}
@@ -4432,24 +5284,23 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin
}
}
- if (cbD->useSecondaryCb) {
- df->vkCmdBindDescriptorSets(cbD->secondaryCbs.last(),
+ if (cbD->passUsesSecondaryCb) {
+ df->vkCmdBindDescriptorSets(cbD->activeSecondaryCbStack.last(),
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;
+ QVkCommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QVkCommandBuffer::Command::BindDescriptorSet;
cmd.args.bindDescriptorSet.bindPoint = gfxPsD ? VK_PIPELINE_BIND_POINT_GRAPHICS
: 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());
- cbD->commands.append(cmd);
+ cmd.args.bindDescriptorSet.dynamicOffsetCount = dynOfs.size();
+ cmd.args.bindDescriptorSet.dynamicOffsetIndex = cbD->pools.dynamicOffset.size();
+ cbD->pools.dynamicOffset.append(dynOfs.constData(), dynOfs.size());
}
if (gfxPsD) {
@@ -4506,19 +5357,18 @@ void QRhiVulkan::setVertexInput(QRhiCommandBuffer *cb,
QRhiPassResourceTracker::BufVertexInputStage);
}
- if (cbD->useSecondaryCb) {
- df->vkCmdBindVertexBuffers(cbD->secondaryCbs.last(), uint32_t(startBinding),
- uint32_t(bufs.count()), bufs.constData(), ofs.constData());
+ if (cbD->passUsesSecondaryCb) {
+ df->vkCmdBindVertexBuffers(cbD->activeSecondaryCbStack.last(), uint32_t(startBinding),
+ uint32_t(bufs.size()), bufs.constData(), ofs.constData());
} else {
- QVkCommandBuffer::Command cmd;
+ 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());
- cbD->commands.append(cmd);
+ 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());
}
}
@@ -4542,15 +5392,14 @@ void QRhiVulkan::setVertexInput(QRhiCommandBuffer *cb,
cbD->currentIndexOffset = indexOffset;
cbD->currentIndexFormat = type;
- if (cbD->useSecondaryCb) {
- df->vkCmdBindIndexBuffer(cbD->secondaryCbs.last(), vkindexbuf, indexOffset, type);
+ if (cbD->passUsesSecondaryCb) {
+ df->vkCmdBindIndexBuffer(cbD->activeSecondaryCbStack.last(), vkindexbuf, indexOffset, type);
} else {
- QVkCommandBuffer::Command cmd;
+ QVkCommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QVkCommandBuffer::Command::BindIndexBuffer;
cmd.args.bindIndexBuffer.buf = vkindexbuf;
cmd.args.bindIndexBuffer.ofs = indexOffset;
cmd.args.bindIndexBuffer.type = type;
- cbD->commands.append(cmd);
}
trackedRegisterBuffer(&passResTracker, ibufD, slot,
@@ -4568,10 +5417,10 @@ 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;
+ QVkCommandBuffer::Command &cmd(cbD->commands.get());
VkViewport *vp = &cmd.args.setViewport.viewport;
vp->x = x;
vp->y = y;
@@ -4580,24 +5429,28 @@ void QRhiVulkan::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport
vp->minDepth = viewport.minDepth();
vp->maxDepth = viewport.maxDepth();
- if (cbD->useSecondaryCb) {
- df->vkCmdSetViewport(cbD->secondaryCbs.last(), 0, 1, vp);
+ if (cbD->passUsesSecondaryCb) {
+ df->vkCmdSetViewport(cbD->activeSecondaryCbStack.last(), 0, 1, vp);
+ cbD->commands.unget();
} else {
cmd.cmd = QVkCommandBuffer::Command::SetViewport;
- cbD->commands.append(cmd);
}
- 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);
s->extent.height = uint32_t(h);
- if (cbD->useSecondaryCb) {
- df->vkCmdSetScissor(cbD->secondaryCbs.last(), 0, 1, s);
+ if (cbD->passUsesSecondaryCb) {
+ df->vkCmdSetScissor(cbD->activeSecondaryCbStack.last(), 0, 1, s);
+ cbD->commands.unget();
} else {
cmd.cmd = QVkCommandBuffer::Command::SetScissor;
- cbD->commands.append(cmd);
}
}
}
@@ -4611,21 +5464,21 @@ 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;
+ QVkCommandBuffer::Command &cmd(cbD->commands.get());
VkRect2D *s = &cmd.args.setScissor.scissor;
s->offset.x = x;
s->offset.y = y;
s->extent.width = uint32_t(w);
s->extent.height = uint32_t(h);
- if (cbD->useSecondaryCb) {
- df->vkCmdSetScissor(cbD->secondaryCbs.last(), 0, 1, s);
+ if (cbD->passUsesSecondaryCb) {
+ df->vkCmdSetScissor(cbD->activeSecondaryCbStack.last(), 0, 1, s);
+ cbD->commands.unget();
} else {
cmd.cmd = QVkCommandBuffer::Command::SetScissor;
- cbD->commands.append(cmd);
}
}
@@ -4634,17 +5487,16 @@ void QRhiVulkan::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c)
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
- if (cbD->useSecondaryCb) {
+ if (cbD->passUsesSecondaryCb) {
float constants[] = { float(c.redF()), float(c.greenF()), float(c.blueF()), float(c.alphaF()) };
- df->vkCmdSetBlendConstants(cbD->secondaryCbs.last(), constants);
+ df->vkCmdSetBlendConstants(cbD->activeSecondaryCbStack.last(), constants);
} else {
- QVkCommandBuffer::Command cmd;
+ QVkCommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QVkCommandBuffer::Command::SetBlendConstants;
cmd.args.setBlendConstants.c[0] = float(c.redF());
cmd.args.setBlendConstants.c[1] = float(c.greenF());
cmd.args.setBlendConstants.c[2] = float(c.blueF());
cmd.args.setBlendConstants.c[3] = float(c.alphaF());
- cbD->commands.append(cmd);
}
}
@@ -4653,13 +5505,12 @@ void QRhiVulkan::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
- if (cbD->useSecondaryCb) {
- df->vkCmdSetStencilReference(cbD->secondaryCbs.last(), VK_STENCIL_FRONT_AND_BACK, refValue);
+ if (cbD->passUsesSecondaryCb) {
+ df->vkCmdSetStencilReference(cbD->activeSecondaryCbStack.last(), VK_STENCIL_FRONT_AND_BACK, refValue);
} else {
- QVkCommandBuffer::Command cmd;
+ QVkCommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QVkCommandBuffer::Command::SetStencilRef;
cmd.args.setStencilRef.ref = refValue;
- cbD->commands.append(cmd);
}
}
@@ -4669,16 +5520,15 @@ void QRhiVulkan::draw(QRhiCommandBuffer *cb, quint32 vertexCount,
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
- if (cbD->useSecondaryCb) {
- df->vkCmdDraw(cbD->secondaryCbs.last(), vertexCount, instanceCount, firstVertex, firstInstance);
+ if (cbD->passUsesSecondaryCb) {
+ df->vkCmdDraw(cbD->activeSecondaryCbStack.last(), vertexCount, instanceCount, firstVertex, firstInstance);
} else {
- QVkCommandBuffer::Command cmd;
+ QVkCommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QVkCommandBuffer::Command::Draw;
cmd.args.draw.vertexCount = vertexCount;
cmd.args.draw.instanceCount = instanceCount;
cmd.args.draw.firstVertex = firstVertex;
cmd.args.draw.firstInstance = firstInstance;
- cbD->commands.append(cmd);
}
}
@@ -4688,80 +5538,88 @@ void QRhiVulkan::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
- if (cbD->useSecondaryCb) {
- df->vkCmdDrawIndexed(cbD->secondaryCbs.last(), indexCount, instanceCount,
+ if (cbD->passUsesSecondaryCb) {
+ df->vkCmdDrawIndexed(cbD->activeSecondaryCbStack.last(), indexCount, instanceCount,
firstIndex, vertexOffset, firstInstance);
} else {
- QVkCommandBuffer::Command cmd;
+ QVkCommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QVkCommandBuffer::Command::DrawIndexed;
cmd.args.drawIndexed.indexCount = indexCount;
cmd.args.drawIndexed.instanceCount = instanceCount;
cmd.args.drawIndexed.firstIndex = firstIndex;
cmd.args.drawIndexed.vertexOffset = vertexOffset;
cmd.args.drawIndexed.firstInstance = firstInstance;
- cbD->commands.append(cmd);
}
}
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->useSecondaryCb) {
- marker.pMarkerName = name.constData();
- vkCmdDebugMarkerBegin(cbD->secondaryCbs.last(), &marker);
+ if (cbD->recordingPass != QVkCommandBuffer::NoPass && cbD->passUsesSecondaryCb) {
+ label.pLabelName = name.constData();
+ vkCmdBeginDebugUtilsLabelEXT(cbD->activeSecondaryCbStack.last(), &label);
} else {
- QVkCommandBuffer::Command cmd;
+ 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);
- cbD->commands.append(cmd);
}
+#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->useSecondaryCb) {
- vkCmdDebugMarkerEnd(cbD->secondaryCbs.last());
+ if (cbD->recordingPass != QVkCommandBuffer::NoPass && cbD->passUsesSecondaryCb) {
+ vkCmdEndDebugUtilsLabelEXT(cbD->activeSecondaryCbStack.last());
} else {
- QVkCommandBuffer::Command cmd;
+ QVkCommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QVkCommandBuffer::Command::DebugMarkerEnd;
- cbD->commands.append(cmd);
}
+#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->useSecondaryCb) {
- marker.pMarkerName = msg.constData();
- vkCmdDebugMarkerInsert(cbD->secondaryCbs.last(), &marker);
+ if (cbD->recordingPass != QVkCommandBuffer::NoPass && cbD->passUsesSecondaryCb) {
+ label.pLabelName = msg.constData();
+ vkCmdInsertDebugUtilsLabelEXT(cbD->activeSecondaryCbStack.last(), &label);
} else {
- QVkCommandBuffer::Command cmd;
+ 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);
- cbD->commands.append(cmd);
}
+#else
+ Q_UNUSED(cb);
+ Q_UNUSED(msg);
+#endif
}
const QRhiNativeHandles *QRhiVulkan::nativeHandles(QRhiCommandBuffer *cb)
@@ -4775,8 +5633,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;
@@ -4809,19 +5667,19 @@ void QRhiVulkan::beginExternal(QRhiCommandBuffer *cb)
if (cbD->inExternal)
return;
- if (!cbD->useSecondaryCb) {
+ if (!cbD->passUsesSecondaryCb) {
qWarning("beginExternal() within a pass is only supported with secondary command buffers. "
- "This can be enabled by passing QRhi::ExternalContentsInPass to beginFrame().");
+ "This can be enabled by passing QRhiCommandBuffer::ExternalContent to beginPass().");
return;
}
- VkCommandBuffer secondaryCb = cbD->secondaryCbs.last();
- cbD->secondaryCbs.removeLast();
+ VkCommandBuffer secondaryCb = cbD->activeSecondaryCbStack.last();
+ cbD->activeSecondaryCbStack.removeLast();
endAndEnqueueSecondaryCommandBuffer(secondaryCb, cbD);
VkCommandBuffer extCb = startSecondaryCommandBuffer(maybeRenderTargetData(cbD));
if (extCb) {
- cbD->secondaryCbs.append(extCb);
+ cbD->activeSecondaryCbStack.append(extCb);
cbD->inExternal = true;
}
}
@@ -4833,32 +5691,44 @@ void QRhiVulkan::endExternal(QRhiCommandBuffer *cb)
if (cbD->recordingPass == QVkCommandBuffer::NoPass) {
Q_ASSERT(cbD->commands.isEmpty() && cbD->currentPassResTrackerIndex == -1);
} else if (cbD->inExternal) {
- VkCommandBuffer extCb = cbD->secondaryCbs.last();
- cbD->secondaryCbs.removeLast();
+ VkCommandBuffer extCb = cbD->activeSecondaryCbStack.last();
+ cbD->activeSecondaryCbStack.removeLast();
endAndEnqueueSecondaryCommandBuffer(extCb, cbD);
- cbD->secondaryCbs.append(startSecondaryCommandBuffer(maybeRenderTargetData(cbD)));
+ cbD->activeSecondaryCbStack.append(startSecondaryCommandBuffer(maybeRenderTargetData(cbD)));
}
cbD->resetCachedState();
}
-void QRhiVulkan::setObjectName(uint64_t object, VkDebugReportObjectTypeEXT type, const QByteArray &name, int slot)
+double QRhiVulkan::lastCompletedGpuTime(QRhiCommandBuffer *cb)
+{
+ QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
+ return cbD->lastGpuTime;
+}
+
+void QRhiVulkan::setObjectName(uint64_t object, VkObjectType type, const QByteArray &name, int slot)
{
- if (!debugMarkers || !debugMarkersAvailable || name.isEmpty())
+#ifdef VK_EXT_debug_utils
+ if (!debugMarkers || !caps.debugUtils || name.isEmpty())
return;
- VkDebugMarkerObjectNameInfoEXT nameInfo;
- memset(&nameInfo, 0, sizeof(nameInfo));
- nameInfo.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_NAME_INFO_EXT;
+ VkDebugUtilsObjectNameInfoEXT nameInfo = {};
+ nameInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
nameInfo.objectType = type;
- nameInfo.object = object;
+ nameInfo.objectHandle = object;
QByteArray decoratedName = name;
if (slot >= 0) {
decoratedName += '/';
decoratedName += QByteArray::number(slot);
}
nameInfo.pObjectName = decoratedName.constData();
- vkDebugMarkerSetObjectName(dev, &nameInfo);
+ vkSetDebugUtilsObjectNameEXT(dev, &nameInfo);
+#else
+ Q_UNUSED(object);
+ Q_UNUSED(type);
+ Q_UNUSED(name);
+ Q_UNUSED(slot);
+#endif
}
static inline VkBufferUsageFlagBits toVkBufferUsage(QRhiBuffer::UsageFlags usage)
@@ -4883,8 +5753,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);
}
}
@@ -4898,8 +5767,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);
}
}
@@ -4913,8 +5781,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);
}
}
@@ -4923,13 +5790,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);
}
}
@@ -4958,9 +5830,40 @@ static inline VkFormat toVkAttributeFormat(QRhiVertexInputAttribute::Format form
return VK_FORMAT_R32G32_UINT;
case QRhiVertexInputAttribute::UInt:
return VK_FORMAT_R32_UINT;
+ case QRhiVertexInputAttribute::SInt4:
+ return VK_FORMAT_R32G32B32A32_SINT;
+ case QRhiVertexInputAttribute::SInt3:
+ return VK_FORMAT_R32G32B32_SINT;
+ case QRhiVertexInputAttribute::SInt2:
+ 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);
}
}
@@ -4979,9 +5882,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);
}
}
@@ -4995,8 +5899,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);
}
}
@@ -5008,8 +5911,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);
}
}
@@ -5069,8 +5971,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);
}
}
@@ -5088,8 +5989,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);
}
}
@@ -5113,8 +6013,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);
}
}
@@ -5138,8 +6037,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);
}
}
@@ -5161,6 +6071,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:
@@ -5172,8 +6088,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);
}
}
@@ -5186,6 +6101,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);
}
@@ -5209,12 +6130,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) {
@@ -5251,12 +6171,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()
@@ -5269,16 +6190,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
@@ -5306,19 +6225,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);
@@ -5341,6 +6258,36 @@ QRhiBuffer::NativeBuffer QVkBuffer::nativeBuffer()
return { { &buffers[0] }, 1 };
}
+char *QVkBuffer::beginFullDynamicBufferUpdateForCurrentFrame()
+{
+ // Shortcut the entire buffer update mechanism and allow the client to do
+ // the host writes directly to the buffer. This will lead to unexpected
+ // results when combined with QRhiResourceUpdateBatch-based updates for the
+ // buffer, but provides a fast path for dynamic buffers that have all their
+ // content changed in every frame.
+ Q_ASSERT(m_type == Dynamic);
+ QRHI_RES_RHI(QRhiVulkan);
+ Q_ASSERT(rhiD->inFrame);
+ const int slot = rhiD->currentFrameSlot;
+ void *p = nullptr;
+ VmaAllocation a = toVmaAllocation(allocations[slot]);
+ VkResult err = vmaMapMemory(toVmaAllocator(rhiD->allocator), a, &p);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to map buffer: %d", err);
+ return nullptr;
+ }
+ return static_cast<char *>(p);
+}
+
+void QVkBuffer::endFullDynamicBufferUpdateForCurrentFrame()
+{
+ QRHI_RES_RHI(QRhiVulkan);
+ const int slot = rhiD->currentFrameSlot;
+ VmaAllocation a = toVmaAllocation(allocations[slot]);
+ vmaFlushAllocation(toVmaAllocator(rhiD->allocator), a, 0, m_size);
+ vmaUnmapMemory(toVmaAllocator(rhiD->allocator), a);
+}
+
QVkRenderBuffer::QVkRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
int sampleCount, Flags flags,
QRhiTexture::Format backingFormatHint)
@@ -5378,12 +6325,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()
@@ -5395,8 +6340,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:
@@ -5404,6 +6348,8 @@ bool QVkRenderBuffer::create()
if (!backingTexture) {
backingTexture = QRHI_RES(QVkTexture, rhiD->createTexture(backingFormat(),
m_pixelSize,
+ 1,
+ 0,
m_sampleCount,
QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource));
} else {
@@ -5414,7 +6360,6 @@ bool QVkRenderBuffer::create()
if (!backingTexture->create())
return false;
vkformat = backingTexture->vkformat;
- QRHI_PROF_F(newRenderBuffer(this, false, false, samples));
}
break;
case QRhiRenderBuffer::DepthStencil:
@@ -5431,8 +6376,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();
@@ -5440,6 +6384,7 @@ bool QVkRenderBuffer::create()
}
lastActiveFrameSlot = -1;
+ generation += 1;
rhiD->registerResource(this);
return true;
}
@@ -5452,15 +6397,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;
}
@@ -5490,7 +6435,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;
}
@@ -5500,12 +6445,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)
@@ -5515,6 +6458,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);
@@ -5523,27 +6475,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;
@@ -5561,20 +6550,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) {
@@ -5594,21 +6594,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;
@@ -5627,15 +6648,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;
@@ -5643,10 +6669,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);
@@ -5667,9 +6690,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;
@@ -5688,7 +6708,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)
@@ -5696,13 +6716,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;
@@ -5711,7 +6737,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);
@@ -5749,8 +6775,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()
@@ -5758,8 +6786,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);
@@ -5788,6 +6815,7 @@ bool QVkSampler::create()
QVkRenderPassDescriptor::QVkRenderPassDescriptor(QRhiImplementation *rhi)
: QRhiRenderPassDescriptor(rhi)
{
+ serializedFormatData.reserve(64);
}
QVkRenderPassDescriptor::~QVkRenderPassDescriptor()
@@ -5814,9 +6842,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)
@@ -5833,21 +6862,28 @@ static inline bool attachmentDescriptionEquals(const VkAttachmentDescription &a,
bool QVkRenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other) const
{
+ if (other == this)
+ return true;
+
if (!other)
return false;
const QVkRenderPassDescriptor *o = QRHI_RES(const QVkRenderPassDescriptor, other);
- if (attDescs.count() != o->attDescs.count())
+ if (attDescs.size() != o->attDescs.size())
return false;
- if (colorRefs.count() != o->colorRefs.count())
+ if (colorRefs.size() != o->colorRefs.size())
return false;
- if (resolveRefs.count() != o->resolveRefs.count())
+ if (resolveRefs.size() != o->resolveRefs.size())
return false;
if (hasDepthStencil != o->hasDepthStencil)
return false;
+ if (hasDepthStencilResolve != o->hasDepthStencilResolve)
+ return false;
+ if (multiViewCount != o->multiViewCount)
+ return false;
- for (int i = 0, ie = colorRefs.count(); i != ie; ++i) {
+ for (int i = 0, ie = colorRefs.size(); i != ie; ++i) {
const uint32_t attIdx = colorRefs[i].attachment;
if (attIdx != o->colorRefs[i].attachment)
return false;
@@ -5863,7 +6899,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;
@@ -5871,41 +6907,144 @@ bool QVkRenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other
return false;
}
+ if (hasDepthStencilResolve) {
+ const uint32_t attIdx = dsResolveRef.attachment;
+ if (attIdx != o->dsResolveRef.attachment)
+ return false;
+ if (attIdx != VK_ATTACHMENT_UNUSED && !attachmentDescriptionEquals(attDescs[attIdx], o->attDescs[attIdx]))
+ return false;
+ }
+
+ // subpassDeps is not included
+
return true;
}
+void QVkRenderPassDescriptor::updateSerializedFormat()
+{
+ serializedFormatData.clear();
+ auto p = std::back_inserter(serializedFormatData);
+
+ *p++ = attDescs.size();
+ *p++ = colorRefs.size();
+ *p++ = resolveRefs.size();
+ *p++ = hasDepthStencil;
+ *p++ = hasDepthStencilResolve;
+ *p++ = multiViewCount;
+
+ auto serializeAttachmentData = [this, &p](uint32_t attIdx) {
+ const bool used = attIdx != VK_ATTACHMENT_UNUSED;
+ 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);
+ }
+
+ if (hasDepthStencilResolve) {
+ const uint32_t attIdx = dsResolveRef.attachment;
+ *p++ = attIdx;
+ serializeAttachmentData(attIdx);
+ }
+}
+
+QRhiRenderPassDescriptor *QVkRenderPassDescriptor::newCompatibleRenderPassDescriptor() const
+{
+ QVkRenderPassDescriptor *rpD = new QVkRenderPassDescriptor(m_rhi);
+
+ rpD->ownsRp = true;
+ rpD->attDescs = attDescs;
+ rpD->colorRefs = colorRefs;
+ rpD->resolveRefs = resolveRefs;
+ rpD->subpassDeps = subpassDeps;
+ rpD->hasDepthStencil = hasDepthStencil;
+ rpD->hasDepthStencilResolve = hasDepthStencilResolve;
+ rpD->multiViewCount = multiViewCount;
+ rpD->dsRef = dsRef;
+ rpD->dsResolveRef = dsResolveRef;
+
+ VkRenderPassCreateInfo rpInfo;
+ VkSubpassDescription subpassDesc;
+ fillRenderPassCreateInfo(&rpInfo, &subpassDesc, rpD);
+
+ QRHI_RES_RHI(QRhiVulkan);
+ MultiViewRenderPassSetupHelper multiViewHelper;
+ if (!multiViewHelper.prepare(&rpInfo, multiViewCount, rhiD->caps.multiView)) {
+ delete rpD;
+ return nullptr;
+ }
+
+ VkResult err = rhiD->df->vkCreateRenderPass(rhiD->dev, &rpInfo, nullptr, &rpD->rp);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create renderpass: %d", err);
+ delete rpD;
+ 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;
}
@@ -5945,10 +7084,16 @@ void QVkTextureRenderTarget::destroy()
resrtv[att] = VK_NULL_HANDLE;
}
- QRHI_RES_RHI(QRhiVulkan);
- rhiD->releaseQueue.append(e);
+ e.textureRenderTarget.dsv = dsv;
+ dsv = VK_NULL_HANDLE;
+ e.textureRenderTarget.resdsv = resdsv;
+ resdsv = VK_NULL_HANDLE;
- rhiD->unregisterResource(this);
+ QRHI_RES_RHI(QRhiVulkan);
+ if (rhiD) {
+ rhiD->releaseQueue.append(e);
+ rhiD->unregisterResource(this);
+ }
}
QRhiRenderPassDescriptor *QVkTextureRenderTarget::newCompatibleRenderPassDescriptor()
@@ -5962,14 +7107,17 @@ QRhiRenderPassDescriptor *QVkTextureRenderTarget::newCompatibleRenderPassDescrip
m_desc.cendColorAttachments(),
m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents),
m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents),
+ m_desc.depthTexture() && !m_flags.testFlag(DoNotStoreDepthStencilContents) && !m_desc.depthResolveTexture(),
m_desc.depthStencilBuffer(),
- m_desc.depthTexture()))
+ m_desc.depthTexture(),
+ m_desc.depthResolveTexture()))
{
delete rp;
return nullptr;
}
rp->ownsRp = true;
+ rp->updateSerializedFormat();
rhiD->registerResource(rp);
return rp;
}
@@ -5979,13 +7127,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;
@@ -5996,12 +7144,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;
@@ -6010,7 +7163,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);
@@ -6035,7 +7188,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;
@@ -6055,18 +7226,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;
@@ -6075,7 +7247,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);
@@ -6085,17 +7257,46 @@ bool QVkTextureRenderTarget::create()
}
}
+ if (m_desc.depthResolveTexture()) {
+ QVkTexture *resTexD = QRHI_RES(QVkTexture, m_desc.depthResolveTexture());
+ Q_ASSERT(resTexD->flags().testFlag(QRhiTexture::RenderTarget));
+
+ VkImageViewCreateInfo viewInfo = {};
+ viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+ viewInfo.image = resTexD->image;
+ viewInfo.viewType = d.multiViewCount ? VK_IMAGE_VIEW_TYPE_2D_ARRAY
+ : VK_IMAGE_VIEW_TYPE_2D;
+ viewInfo.format = resTexD->viewFormat;
+ viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
+ viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
+ viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
+ viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
+ viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
+ viewInfo.subresourceRange.baseMipLevel = 0;
+ viewInfo.subresourceRange.levelCount = 1;
+ viewInfo.subresourceRange.baseArrayLayer = 0;
+ viewInfo.subresourceRange.layerCount = qMax<uint32_t>(1, d.multiViewCount);
+ VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &resdsv);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create render target depth resolve image view: %d", err);
+ return false;
+ }
+ views.append(resdsv);
+ d.dsResolveAttCount = 1;
+ } else {
+ d.dsResolveAttCount = 0;
+ }
+
if (!m_renderPassDesc)
qWarning("QVkTextureRenderTarget: No renderpass descriptor set. See newCompatibleRenderPassDescriptor() and setRenderPassDescriptor().");
d.rp = QRHI_RES(QVkRenderPassDescriptor, m_renderPassDesc);
Q_ASSERT(d.rp && d.rp->rp);
- VkFramebufferCreateInfo fbInfo;
- memset(&fbInfo, 0, sizeof(fbInfo));
+ VkFramebufferCreateInfo fbInfo = {};
fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
fbInfo.renderPass = d.rp->rp;
- fbInfo.attachmentCount = uint32_t(d.colorAttCount + d.dsAttCount + d.resolveAttCount);
+ fbInfo.attachmentCount = uint32_t(d.colorAttCount + d.dsAttCount + d.resolveAttCount + d.dsResolveAttCount);
fbInfo.pAttachments = views.constData();
fbInfo.width = uint32_t(d.pixelSize.width());
fbInfo.height = uint32_t(d.pixelSize.height());
@@ -6107,6 +7308,8 @@ bool QVkTextureRenderTarget::create()
return false;
}
+ QRhiRenderTargetAttachmentTracker::updateResIdList<QVkTexture, QVkRenderBuffer>(m_desc, &d.currentResIdList);
+
lastActiveFrameSlot = -1;
rhiD->registerResource(this);
return true;
@@ -6114,6 +7317,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;
}
@@ -6157,9 +7363,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()
@@ -6171,25 +7378,34 @@ bool QVkShaderResourceBindings::create()
if (!rhiD->sanityCheckShaderResourceBindings(this))
return false;
+ rhiD->updateLayoutDesc(this);
+
for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
descSets[i] = VK_NULL_HANDLE;
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 : 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;
+ if (b->u.ubuf.hasDynamicOffset)
+ hasDynamicOffset = true;
+ }
+ }
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;
@@ -6197,10 +7413,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);
@@ -6209,8 +7424,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];
@@ -6221,7 +7435,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));
}
@@ -6232,6 +7446,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)
{
@@ -6258,9 +7497,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()
@@ -6269,14 +7509,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);
@@ -6288,8 +7528,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;
@@ -6304,8 +7543,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;
@@ -6313,11 +7551,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)
@@ -6329,9 +7569,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");
@@ -6351,21 +7594,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;
@@ -6376,28 +7619,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);
@@ -6406,17 +7673,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;
@@ -6432,13 +7698,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);
@@ -6450,13 +7714,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;
@@ -6475,6 +7738,7 @@ bool QVkGraphicsPipeline::create()
return false;
}
+ rhiD->pipelineCreationEnd();
lastActiveFrameSlot = -1;
generation += 1;
rhiD->registerResource(this);
@@ -6507,9 +7771,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()
@@ -6518,11 +7783,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);
@@ -6534,8 +7799,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;
@@ -6554,8 +7818,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;
@@ -6569,6 +7832,7 @@ bool QVkComputePipeline::create()
return false;
}
+ rhiD->pipelineCreationEnd();
lastActiveFrameSlot = -1;
generation += 1;
rhiD->registerResource(this);
@@ -6599,17 +7863,22 @@ const QRhiNativeHandles *QVkCommandBuffer::nativeHandles()
// secondary command buffer (typically the one started by beginExternal(),
// in case we are between beginExternal - endExternal inside a pass).
- if (useSecondaryCb && !secondaryCbs.isEmpty())
- nativeHandlesStruct.commandBuffer = secondaryCbs.last();
- else
+ if (recordingPass == QVkCommandBuffer::NoPass) {
nativeHandlesStruct.commandBuffer = cb;
+ } else {
+ if (passUsesSecondaryCb && !activeSecondaryCbStack.isEmpty())
+ nativeHandlesStruct.commandBuffer = activeSecondaryCbStack.last();
+ else
+ nativeHandlesStruct.commandBuffer = cb;
+ }
return &nativeHandlesStruct;
}
QVkSwapChain::QVkSwapChain(QRhiImplementation *rhi)
: QRhiSwapChain(rhi),
- rtWrapper(rhi),
+ rtWrapper(rhi, this),
+ rtWrapperRight(rhi, this),
cbWrapper(rhi)
{
}
@@ -6625,14 +7894,21 @@ void QVkSwapChain::destroy()
return;
QRHI_RES_RHI(QRhiVulkan);
- rhiD->swapchains.remove(this);
- rhiD->releaseSwapChainResources(this);
- surface = lastConnectedSurface = VK_NULL_HANDLE;
+ if (rhiD) {
+ rhiD->swapchains.remove(this);
+ rhiD->releaseSwapChainResources(this);
+ }
- QRHI_PROF;
- QRHI_PROF_F(releaseSwapChain(this));
+ for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
+ QVkSwapChain::FrameResources &frame(frameRes[i]);
+ frame.cmdBuf = VK_NULL_HANDLE;
+ frame.timestampQueryIndex = -1;
+ }
- rhiD->unregisterResource(this);
+ surface = lastConnectedSurface = VK_NULL_HANDLE;
+
+ if (rhiD)
+ rhiD->unregisterResource(this);
}
QRhiCommandBuffer *QVkSwapChain::currentFrameCommandBuffer()
@@ -6645,6 +7921,11 @@ QRhiRenderTarget *QVkSwapChain::currentFrameRenderTarget()
return &rtWrapper;
}
+QRhiRenderTarget *QVkSwapChain::currentFrameRenderTarget(StereoTargetBuffer targetBuffer)
+{
+ return !stereo || targetBuffer == StereoTargetBuffer::LeftBuffer ? &rtWrapper : &rtWrapperRight;
+}
+
QSize QVkSwapChain::surfacePixelSize()
{
if (!ensureSurface())
@@ -6652,8 +7933,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;
@@ -6664,6 +7944,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()
@@ -6683,6 +8009,7 @@ QRhiRenderPassDescriptor *QVkSwapChain::newCompatibleRenderPassDescriptor()
}
rp->ownsRp = true;
+ rp->updateSerializedFormat();
rhiD->registerResource(rp);
return rp;
}
@@ -6722,27 +8049,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;
@@ -6751,16 +8060,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);
@@ -6816,6 +8132,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);
@@ -6830,6 +8147,7 @@ bool QVkSwapChain::createOrResize()
rtWrapper.d.dsAttCount = 0;
ds = nullptr;
}
+ rtWrapper.d.dsResolveAttCount = 0;
if (samples > VK_SAMPLE_COUNT_1_BIT)
rtWrapper.d.resolveAttCount = 1;
else
@@ -6843,11 +8161,10 @@ bool QVkSwapChain::createOrResize()
samples > VK_SAMPLE_COUNT_1_BIT ? image.imageView : VK_NULL_HANDLE
};
- VkFramebufferCreateInfo fbInfo;
- memset(&fbInfo, 0, sizeof(fbInfo));
+ VkFramebufferCreateInfo fbInfo = {};
fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
fbInfo.renderPass = rtWrapper.d.rp->rp;
- fbInfo.attachmentCount = uint32_t(rtWrapper.d.colorAttCount + rtWrapper.d.dsAttCount + rtWrapper.d.resolveAttCount);
+ fbInfo.attachmentCount = uint32_t(rtWrapper.d.colorAttCount + rtWrapper.d.dsAttCount + rtWrapper.d.resolveAttCount + rtWrapper.d.dsResolveAttCount);
fbInfo.pAttachments = views;
fbInfo.width = uint32_t(pixelSize.width());
fbInfo.height = uint32_t(pixelSize.height());
@@ -6860,10 +8177,57 @@ 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);
+
+ rtWrapperRight.d.pixelSize = pixelSize;
+ rtWrapperRight.d.dpr = float(window->devicePixelRatio());
+ rtWrapperRight.d.sampleCount = samples;
+ rtWrapperRight.d.colorAttCount = 1;
+ if (m_depthStencil) {
+ rtWrapperRight.d.dsAttCount = 1;
+ ds = QRHI_RES(QVkRenderBuffer, m_depthStencil);
+ } else {
+ rtWrapperRight.d.dsAttCount = 0;
+ ds = nullptr;
+ }
+ rtWrapperRight.d.dsResolveAttCount = 0;
+ if (samples > VK_SAMPLE_COUNT_1_BIT)
+ rtWrapperRight.d.resolveAttCount = 1;
+ else
+ rtWrapperRight.d.resolveAttCount = 0;
+
+ for (int i = 0; i < bufferCount; ++i) {
+ QVkSwapChain::ImageResources &image(imageRes[i + bufferCount]);
+ VkImageView views[3] = {
+ // color, ds, resolve
+ samples > VK_SAMPLE_COUNT_1_BIT ? image.msaaImageView : image.imageView,
+ ds ? ds->imageView : VK_NULL_HANDLE,
+ samples > VK_SAMPLE_COUNT_1_BIT ? image.imageView : VK_NULL_HANDLE
+ };
+
+ VkFramebufferCreateInfo fbInfo = {};
+ fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
+ fbInfo.renderPass = rtWrapperRight.d.rp->rp;
+ fbInfo.attachmentCount = uint32_t(rtWrapperRight.d.colorAttCount + rtWrapperRight.d.dsAttCount
+ + rtWrapperRight.d.resolveAttCount + rtWrapperRight.d.dsResolveAttCount);
+ fbInfo.pAttachments = views;
+ fbInfo.width = uint32_t(pixelSize.width());
+ fbInfo.height = uint32_t(pixelSize.height());
+ fbInfo.layers = 1;
+
+ VkResult err = rhiD->df->vkCreateFramebuffer(rhiD->dev, &fbInfo, nullptr, &image.fb);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create framebuffer: %d", err);
+ return false;
+ }
+ }
+ }
- QRHI_PROF;
- QRHI_PROF_F(resizeSwapChain(this, QVK_FRAMES_IN_FLIGHT, samples > VK_SAMPLE_COUNT_1_BIT ? QVK_FRAMES_IN_FLIGHT : 0, samples));
+ frameCount = 0;
if (needsRegistration)
rhiD->registerResource(this);