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.cpp1625
1 files changed, 1162 insertions, 463 deletions
diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp
index bd6a168e31..f0b51146cc 100644
--- a/src/gui/rhi/qrhivulkan.cpp
+++ b/src/gui/rhi/qrhivulkan.cpp
@@ -1,10 +1,11 @@
-// Copyright (C) 2019 The Qt Company Ltd.
+// 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_p.h"
-#include "qrhivulkanext_p.h"
+#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
@@ -22,6 +23,7 @@ QT_WARNING_POP
#include <qmath.h>
#include <QVulkanFunctions>
#include <QtGui/qwindow.h>
+#include <optional>
QT_BEGIN_NAMESPACE
@@ -57,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:
@@ -163,34 +168,121 @@ QT_BEGIN_NAMESPACE
*/
/*!
+ \variable QRhiVulkanInitParams::inst
+
+ The QVulkanInstance that has already been successfully
+ \l{QVulkanInstance::create()}{created}, required.
+*/
+
+/*!
+ \variable QRhiVulkanInitParams::window
+
+ Optional, but recommended when targeting a QWindow.
+*/
+
+/*!
+ \variable QRhiVulkanInitParams::deviceExtensions
+
+ Optional, empty by default. The list of Vulkan device extensions to enable.
+ Unsupported extensions are ignored gracefully.
+*/
+
+/*!
\class QRhiVulkanNativeHandles
- \internal
\inmodule QtGui
+ \since 6.6
\brief Collects device, queue, and other Vulkan objects that are used by the QRhi.
\note Ownership of the Vulkan objects is never transferred.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
*/
/*!
+ \variable QRhiVulkanNativeHandles::physDev
+
+ When different from \nullptr, specifies the Vulkan physical device to use.
+*/
+
+/*!
+ \variable QRhiVulkanNativeHandles::dev
+
+ When wanting to import not just a physical device, but also use an already
+ existing VkDevice, set this and the graphics queue index and family index.
+*/
+
+/*!
+ \variable QRhiVulkanNativeHandles::gfxQueueFamilyIdx
+
+ Graphics queue family index.
+*/
+
+/*!
+ \variable QRhiVulkanNativeHandles::gfxQueueIdx
+
+ Graphics queue index.
+*/
+
+/*!
+ \variable QRhiVulkanNativeHandles::vmemAllocator
+
+ Relevant only when importing an existing memory allocator object,
+ leave it set to \nullptr otherwise.
+*/
+
+/*!
+ \variable QRhiVulkanNativeHandles::gfxQueue
+
+ Output only, not used by QRhi::create(), only set by the
+ QRhi::nativeHandles() accessor. The graphics VkQueue used by the QRhi.
+*/
+
+/*!
+ \variable QRhiVulkanNativeHandles::inst
+
+ Output only, not used by QRhi::create(), only set by the
+ QRhi::nativeHandles() accessor. The QVulkanInstance used by the QRhi.
+*/
+
+/*!
\class QRhiVulkanCommandBufferNativeHandles
- \internal
\inmodule QtGui
+ \since 6.6
\brief Holds the Vulkan command buffer object that is backing a QRhiCommandBuffer.
\note The Vulkan command buffer object is only guaranteed to be valid, and
in recording state, while recording a frame. That is, between a
\l{QRhi::beginFrame()}{beginFrame()} - \l{QRhi::endFrame()}{endFrame()} or
\l{QRhi::beginOffscreenFrame()}{beginOffscreenFrame()} -
- \l{QRhi::endOffsrceenFrame()}{endOffscreenFrame()} pair.
+ \l{QRhi::endOffscreenFrame()}{endOffscreenFrame()} pair.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
*/
/*!
+ \variable QRhiVulkanCommandBufferNativeHandles::commandBuffer
+
+ The VkCommandBuffer object.
+*/
+
+/*!
\class QRhiVulkanRenderPassNativeHandles
- \internal
\inmodule QtGui
+ \since 6.6
\brief Holds the Vulkan render pass object backing a QRhiRenderPassDescriptor.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
*/
+/*!
+ \variable QRhiVulkanRenderPassNativeHandles::renderPass
+
+ The VkRenderPass object.
+*/
+
template <class Int>
inline Int aligned(Int v, Int byteAlign)
{
@@ -199,84 +291,14 @@ inline Int aligned(Int v, Int byteAlign)
static QVulkanInstance *globalVulkanInstance;
-static void VKAPI_PTR wrap_vkGetPhysicalDeviceProperties(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties* pProperties)
-{
- globalVulkanInstance->functions()->vkGetPhysicalDeviceProperties(physicalDevice, pProperties);
-}
-
-static void VKAPI_PTR wrap_vkGetPhysicalDeviceMemoryProperties(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties* pMemoryProperties)
-{
- globalVulkanInstance->functions()->vkGetPhysicalDeviceMemoryProperties(physicalDevice, pMemoryProperties);
-}
-
-static VkResult VKAPI_PTR wrap_vkAllocateMemory(VkDevice device, const VkMemoryAllocateInfo* pAllocateInfo, const VkAllocationCallbacks* pAllocator, VkDeviceMemory* pMemory)
+static VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL wrap_vkGetInstanceProcAddr(VkInstance, const char *pName)
{
- return globalVulkanInstance->deviceFunctions(device)->vkAllocateMemory(device, pAllocateInfo, pAllocator, pMemory);
+ return globalVulkanInstance->getInstanceProcAddr(pName);
}
-void VKAPI_PTR wrap_vkFreeMemory(VkDevice device, VkDeviceMemory memory, const VkAllocationCallbacks* pAllocator)
+static VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL wrap_vkGetDeviceProcAddr(VkDevice device, const char *pName)
{
- globalVulkanInstance->deviceFunctions(device)->vkFreeMemory(device, memory, pAllocator);
-}
-
-VkResult VKAPI_PTR wrap_vkMapMemory(VkDevice device, VkDeviceMemory memory, VkDeviceSize offset, VkDeviceSize size, VkMemoryMapFlags flags, void** ppData)
-{
- return globalVulkanInstance->deviceFunctions(device)->vkMapMemory(device, memory, offset, size, flags, ppData);
-}
-
-void VKAPI_PTR wrap_vkUnmapMemory(VkDevice device, VkDeviceMemory memory)
-{
- globalVulkanInstance->deviceFunctions(device)->vkUnmapMemory(device, memory);
-}
-
-VkResult VKAPI_PTR wrap_vkFlushMappedMemoryRanges(VkDevice device, uint32_t memoryRangeCount, const VkMappedMemoryRange* pMemoryRanges)
-{
- return globalVulkanInstance->deviceFunctions(device)->vkFlushMappedMemoryRanges(device, memoryRangeCount, pMemoryRanges);
-}
-
-VkResult VKAPI_PTR wrap_vkInvalidateMappedMemoryRanges(VkDevice device, uint32_t memoryRangeCount, const VkMappedMemoryRange* pMemoryRanges)
-{
- return globalVulkanInstance->deviceFunctions(device)->vkInvalidateMappedMemoryRanges(device, memoryRangeCount, pMemoryRanges);
-}
-
-VkResult VKAPI_PTR wrap_vkBindBufferMemory(VkDevice device, VkBuffer buffer, VkDeviceMemory memory, VkDeviceSize memoryOffset)
-{
- return globalVulkanInstance->deviceFunctions(device)->vkBindBufferMemory(device, buffer, memory, memoryOffset);
-}
-
-VkResult VKAPI_PTR wrap_vkBindImageMemory(VkDevice device, VkImage image, VkDeviceMemory memory, VkDeviceSize memoryOffset)
-{
- return globalVulkanInstance->deviceFunctions(device)->vkBindImageMemory(device, image, memory, memoryOffset);
-}
-
-void VKAPI_PTR wrap_vkGetBufferMemoryRequirements(VkDevice device, VkBuffer buffer, VkMemoryRequirements* pMemoryRequirements)
-{
- globalVulkanInstance->deviceFunctions(device)->vkGetBufferMemoryRequirements(device, buffer, pMemoryRequirements);
-}
-
-void VKAPI_PTR wrap_vkGetImageMemoryRequirements(VkDevice device, VkImage image, VkMemoryRequirements* pMemoryRequirements)
-{
- globalVulkanInstance->deviceFunctions(device)->vkGetImageMemoryRequirements(device, image, pMemoryRequirements);
-}
-
-VkResult VKAPI_PTR wrap_vkCreateBuffer(VkDevice device, const VkBufferCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBuffer* pBuffer)
-{
- return globalVulkanInstance->deviceFunctions(device)->vkCreateBuffer(device, pCreateInfo, pAllocator, pBuffer);
-}
-
-void VKAPI_PTR wrap_vkDestroyBuffer(VkDevice device, VkBuffer buffer, const VkAllocationCallbacks* pAllocator)
-{
- globalVulkanInstance->deviceFunctions(device)->vkDestroyBuffer(device, buffer, pAllocator);
-}
-
-VkResult VKAPI_PTR wrap_vkCreateImage(VkDevice device, const VkImageCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkImage* pImage)
-{
- return globalVulkanInstance->deviceFunctions(device)->vkCreateImage(device, pCreateInfo, pAllocator, pImage);
-}
-
-void VKAPI_PTR wrap_vkDestroyImage(VkDevice device, VkImage image, const VkAllocationCallbacks* pAllocator)
-{
- globalVulkanInstance->deviceFunctions(device)->vkDestroyImage(device, image, pAllocator);
+ return globalVulkanInstance->functions()->vkGetDeviceProcAddr(device, pName);
}
static inline VmaAllocation toVmaAllocation(QVkAlloc a)
@@ -289,6 +311,13 @@ static inline VmaAllocator toVmaAllocator(QVkAllocator a)
return reinterpret_cast<VmaAllocator>(a);
}
+/*!
+ \return the list of instance extensions that are expected to be enabled on
+ the QVulkanInstance that is used for the Vulkan-based QRhi.
+
+ The returned list can be safely passed to QVulkanInstance::setExtensions()
+ as-is, because unsupported extensions are filtered out automatically.
+ */
QByteArrayList QRhiVulkanInitParams::preferredInstanceExtensions()
{
return {
@@ -296,11 +325,18 @@ QByteArrayList QRhiVulkanInitParams::preferredInstanceExtensions()
};
}
+/*!
+ \return the list of device extensions that are expected to be enabled on the
+ \c VkDevice when creating a Vulkan-based QRhi with an externally created
+ \c VkDevice object.
+ */
QByteArrayList QRhiVulkanInitParams::preferredExtensionsForImportedDevice()
{
return {
QByteArrayLiteral("VK_KHR_swapchain"),
- QByteArrayLiteral("VK_EXT_vertex_attribute_divisor")
+ QByteArrayLiteral("VK_EXT_vertex_attribute_divisor"),
+ QByteArrayLiteral("VK_KHR_create_renderpass2"),
+ QByteArrayLiteral("VK_KHR_depth_stencil_resolve")
};
}
@@ -394,6 +430,8 @@ bool QRhiVulkan::create(QRhi::Flags flags)
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;
@@ -495,32 +533,93 @@ bool QRhiVulkan::create(QRhi::Flags flags)
driverInfoStruct.vendorId = physDevProperties.vendorID;
driverInfoStruct.deviceType = toRhiDeviceType(physDevProperties.deviceType);
- f->vkGetPhysicalDeviceFeatures(physDev, &physDevFeatures);
+ bool featuresQueried = false;
+#ifdef VK_VERSION_1_1
+ VkPhysicalDeviceFeatures2 physDevFeaturesChainable = {};
+ physDevFeaturesChainable.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
+#endif
+
+ // Vulkan >=1.2 headers at build time, >=1.2 implementation at run time
+#ifdef VK_VERSION_1_2
+ if (!featuresQueried) {
+ // Vulkan11Features, Vulkan12Features, etc. are only in Vulkan 1.2 and newer.
+ if (caps.apiVersion >= QVersionNumber(1, 2)) {
+ physDevFeatures11IfApi12OrNewer = {};
+ physDevFeatures11IfApi12OrNewer.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES;
+ physDevFeatures12 = {};
+ physDevFeatures12.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;
+#ifdef VK_VERSION_1_3
+ physDevFeatures13 = {};
+ physDevFeatures13.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES;
+#endif
+ physDevFeaturesChainable.pNext = &physDevFeatures11IfApi12OrNewer;
+ physDevFeatures11IfApi12OrNewer.pNext = &physDevFeatures12;
+#ifdef VK_VERSION_1_3
+ if (caps.apiVersion >= QVersionNumber(1, 3))
+ physDevFeatures12.pNext = &physDevFeatures13;
+#endif
+ f->vkGetPhysicalDeviceFeatures2(physDev, &physDevFeaturesChainable);
+ memcpy(&physDevFeatures, &physDevFeaturesChainable.features, sizeof(VkPhysicalDeviceFeatures));
+ featuresQueried = true;
+ }
+ }
+#endif // VK_VERSION_1_2
+
+ // Vulkan >=1.1 headers at build time, 1.1 implementation at run time
+#ifdef VK_VERSION_1_1
+ if (!featuresQueried) {
+ // Vulkan versioning nightmares: if the runtime API version is 1.1,
+ // there is no Vulkan11Features (introduced in 1.2+, the headers might
+ // have the types and structs, but the Vulkan implementation version at
+ // run time is what matters). But there are individual feature structs.
+ // For multiview, it is important to get this right since at the time of
+ // writing Quest 3 Android is a Vulkan 1.1 implementation at run time on
+ // the headset.
+ if (caps.apiVersion == QVersionNumber(1, 1)) {
+ multiviewFeaturesIfApi11 = {};
+ multiviewFeaturesIfApi11.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES;
+ physDevFeaturesChainable.pNext = &multiviewFeaturesIfApi11;
+ f->vkGetPhysicalDeviceFeatures2(physDev, &physDevFeaturesChainable);
+ memcpy(&physDevFeatures, &physDevFeaturesChainable.features, sizeof(VkPhysicalDeviceFeatures));
+ featuresQueried = true;
+ }
+ }
+#endif
+
+ if (!featuresQueried) {
+ // If the API version at run time is 1.0 (or we are building with
+ // ancient 1.0 headers), then do the Vulkan 1.0 query.
+ f->vkGetPhysicalDeviceFeatures(physDev, &physDevFeatures);
+ featuresQueried = true;
+ }
// Choose queue and create device, unless the device was specified in importParams.
if (!importedDevice) {
// We only support combined graphics+present queues. When it comes to
// compute, only combined graphics+compute queue is used, compute gets
// disabled otherwise.
- gfxQueueFamilyIdx = -1;
- int computelessGfxQueueCandidateIdx = -1;
+ std::optional<uint32_t> gfxQueueFamilyIdxOpt;
+ std::optional<uint32_t> computelessGfxQueueCandidateIdxOpt;
queryQueueFamilyProps();
- for (int i = 0; i < queueFamilyProps.count(); ++i) {
- qCDebug(QRHI_LOG_INFO, "queue family %d: flags=0x%x count=%d",
+ const uint32_t queueFamilyCount = uint32_t(queueFamilyProps.size());
+ for (uint32_t i = 0; i < queueFamilyCount; ++i) {
+ qCDebug(QRHI_LOG_INFO, "queue family %u: flags=0x%x count=%u",
i, queueFamilyProps[i].queueFlags, queueFamilyProps[i].queueCount);
- if (gfxQueueFamilyIdx == -1
+ if (!gfxQueueFamilyIdxOpt.has_value()
&& (queueFamilyProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
- && (!maybeWindow || inst->supportsPresent(physDev, uint32_t(i), maybeWindow)))
+ && (!maybeWindow || inst->supportsPresent(physDev, i, maybeWindow)))
{
if (queueFamilyProps[i].queueFlags & VK_QUEUE_COMPUTE_BIT)
- gfxQueueFamilyIdx = i;
- else if (computelessGfxQueueCandidateIdx == -1)
- computelessGfxQueueCandidateIdx = i;
+ gfxQueueFamilyIdxOpt = i;
+ else if (!computelessGfxQueueCandidateIdxOpt.has_value())
+ computelessGfxQueueCandidateIdxOpt = i;
}
}
- if (gfxQueueFamilyIdx == -1) {
- if (computelessGfxQueueCandidateIdx != -1) {
- gfxQueueFamilyIdx = computelessGfxQueueCandidateIdx;
+ if (gfxQueueFamilyIdxOpt.has_value()) {
+ gfxQueueFamilyIdx = gfxQueueFamilyIdxOpt.value();
+ } else {
+ if (computelessGfxQueueCandidateIdxOpt.has_value()) {
+ gfxQueueFamilyIdx = computelessGfxQueueCandidateIdxOpt.value();
} else {
qWarning("No graphics (or no graphics+present) queue family found");
return false;
@@ -530,7 +629,7 @@ bool QRhiVulkan::create(QRhi::Flags flags)
VkDeviceQueueCreateInfo queueInfo = {};
const float prio[] = { 0 };
queueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
- queueInfo.queueFamilyIndex = uint32_t(gfxQueueFamilyIdx);
+ queueInfo.queueFamilyIndex = gfxQueueFamilyIdx;
queueInfo.queueCount = 1;
queueInfo.pQueuePriorities = prio;
@@ -544,38 +643,69 @@ bool QRhiVulkan::create(QRhi::Flags flags)
if (devExtCount) {
QList<VkExtensionProperties> extProps(devExtCount);
f->vkEnumerateDeviceExtensionProperties(physDev, nullptr, &devExtCount, extProps.data());
- for (const VkExtensionProperties &p : qAsConst(extProps))
+ for (const VkExtensionProperties &p : std::as_const(extProps))
devExts.append({ p.extensionName, p.specVersion });
}
- qCDebug(QRHI_LOG_INFO, "%d device extensions available", int(devExts.count()));
+ qCDebug(QRHI_LOG_INFO, "%d device extensions available", int(devExts.size()));
QList<const char *> requestedDevExts;
requestedDevExts.append("VK_KHR_swapchain");
- caps.vertexAttribDivisor = false;
+ 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.");
+ }
+ }
+
+#ifdef VK_EXT_vertex_attribute_divisor
if (devExts.contains(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME)) {
- if (inst->extensions().contains(QByteArrayLiteral("VK_KHR_get_physical_device_properties2"))) {
+ if (hasPhysDevProp2) {
requestedDevExts.append(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME);
caps.vertexAttribDivisor = true;
}
}
+#endif
+
+#ifdef VK_KHR_create_renderpass2
+ if (devExts.contains(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME)) {
+ requestedDevExts.append(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME);
+ caps.renderPass2KHR = true;
+ }
+#endif
+
+#ifdef VK_KHR_depth_stencil_resolve
+ if (devExts.contains(VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME)) {
+ requestedDevExts.append(VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME);
+ caps.depthStencilResolveKHR = true;
+ }
+#endif
for (const QByteArray &ext : requestedDeviceExtensions) {
- if (!ext.isEmpty()) {
- if (devExts.contains(ext))
+ if (!ext.isEmpty() && !requestedDevExts.contains(ext)) {
+ if (devExts.contains(ext)) {
requestedDevExts.append(ext.constData());
- else
- qWarning("Device extension %s is not supported", ext.constData());
+ } else {
+ qWarning("Device extension %s requested in QRhiVulkanInitParams is not supported",
+ ext.constData());
+ }
}
}
QByteArrayList envExtList = qgetenv("QT_VULKAN_DEVICE_EXTENSIONS").split(';');
for (const QByteArray &ext : envExtList) {
if (!ext.isEmpty() && !requestedDevExts.contains(ext)) {
- if (devExts.contains(ext))
+ if (devExts.contains(ext)) {
requestedDevExts.append(ext.constData());
- else
- qWarning("Device extension %s is not supported", ext.constData());
+ } else {
+ qWarning("Device extension %s requested in QT_VULKAN_DEVICE_EXTENSIONS is not supported",
+ ext.constData());
+ }
}
}
@@ -589,9 +719,9 @@ bool QRhiVulkan::create(QRhi::Flags flags)
devInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
devInfo.queueCreateInfoCount = 1;
devInfo.pQueueCreateInfos = &queueInfo;
- devInfo.enabledLayerCount = uint32_t(devLayers.count());
+ 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();
// Enable all features that are reported as supported, except
@@ -607,42 +737,27 @@ bool QRhiVulkan::create(QRhi::Flags flags)
// tessellationShader, geometryShader
// textureCompressionETC2, textureCompressionASTC_LDR, textureCompressionBC
-#ifdef VK_VERSION_1_2 // Vulkan11Features is only in Vulkan 1.2
- VkPhysicalDeviceFeatures2 physDevFeatures2 = {};
- physDevFeatures2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
-
- VkPhysicalDeviceVulkan11Features features11 = {};
- features11.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES;
- VkPhysicalDeviceVulkan12Features features12 = {};
- features12.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;
-#ifdef VK_VERSION_1_3
- VkPhysicalDeviceVulkan13Features features13 = {};
- features13.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES;
+#ifdef VK_VERSION_1_1
+ physDevFeaturesChainable.features.robustBufferAccess = VK_FALSE;
#endif
-
- if (caps.apiVersion >= QVersionNumber(1, 2)) {
- physDevFeatures2.pNext = &features11;
- features11.pNext = &features12;
#ifdef VK_VERSION_1_3
- if (caps.apiVersion >= QVersionNumber(1, 3))
- features12.pNext = &features13;
+ physDevFeatures13.robustImageAccess = VK_FALSE;
#endif
- f->vkGetPhysicalDeviceFeatures2(physDev, &physDevFeatures2);
- physDevFeatures2.features.robustBufferAccess = VK_FALSE;
-#ifdef VK_VERSION_1_3
- features13.robustImageAccess = VK_FALSE;
+#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
-
- devInfo.pNext = &physDevFeatures2;
- }
-#endif // VK_VERSION_1_2
-
- VkPhysicalDeviceFeatures features;
- if (!devInfo.pNext) {
- memcpy(&features, &physDevFeatures, sizeof(features));
- features.robustBufferAccess = VK_FALSE;
- devInfo.pEnabledFeatures = &features;
+ {
+ physDevFeatures.robustBufferAccess = VK_FALSE;
+ devInfo.pEnabledFeatures = &physDevFeatures;
}
VkResult err = f->vkCreateDevice(physDev, &devInfo, nullptr, &dev);
@@ -652,6 +767,13 @@ bool QRhiVulkan::create(QRhi::Flags flags)
}
} else {
qCDebug(QRHI_LOG_INFO, "Using imported device %p", dev);
+
+ // Here we have no way to tell if the extensions got enabled or not.
+ // Pretend it's all there and supported. If getProcAddress fails, we'll
+ // handle that gracefully.
+ caps.vertexAttribDivisor = true;
+ caps.renderPass2KHR = true;
+ caps.depthStencilResolveKHR = true;
}
vkGetPhysicalDeviceSurfaceCapabilitiesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR>(
@@ -660,19 +782,12 @@ bool QRhiVulkan::create(QRhi::Flags flags)
inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfaceFormatsKHR"));
vkGetPhysicalDeviceSurfacePresentModesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfacePresentModesKHR>(
inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfacePresentModesKHR"));
- if (!vkGetPhysicalDeviceSurfaceCapabilitiesKHR
- || !vkGetPhysicalDeviceSurfaceFormatsKHR
- || !vkGetPhysicalDeviceSurfacePresentModesKHR)
- {
- qWarning("Physical device surface queries not available");
- return false;
- }
df = inst->deviceFunctions(dev);
VkCommandPoolCreateInfo poolInfo = {};
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
- poolInfo.queueFamilyIndex = uint32_t(gfxQueueFamilyIdx);
+ poolInfo.queueFamilyIndex = gfxQueueFamilyIdx;
for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
VkResult err = df->vkCreateCommandPool(dev, &poolInfo, nullptr, &cmdPool[i]);
if (err != VK_SUCCESS) {
@@ -681,13 +796,10 @@ bool QRhiVulkan::create(QRhi::Flags flags)
}
}
- if (gfxQueueFamilyIdx < 0) {
- // this is when importParams is faulty and did not specify the queue family index
- qWarning("No queue family index provided");
- return false;
- }
+ qCDebug(QRHI_LOG_INFO, "Using queue family index %u and queue index %u",
+ gfxQueueFamilyIdx, gfxQueueIdx);
- df->vkGetDeviceQueue(dev, uint32_t(gfxQueueFamilyIdx), gfxQueueIdx, &gfxQueue);
+ df->vkGetDeviceQueue(dev, gfxQueueFamilyIdx, gfxQueueIdx, &gfxQueue);
if (queueFamilyProps.isEmpty())
queryQueueFamilyProps();
@@ -709,24 +821,32 @@ bool QRhiVulkan::create(QRhi::Flags flags)
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;
+ 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
@@ -734,7 +854,11 @@ bool QRhiVulkan::create(QRhi::Flags flags)
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) {
@@ -782,6 +906,7 @@ bool QRhiVulkan::create(QRhi::Flags flags)
nativeHandlesStruct.gfxQueueIdx = gfxQueueIdx;
nativeHandlesStruct.gfxQueue = gfxQueue;
nativeHandlesStruct.vmemAllocator = allocator;
+ nativeHandlesStruct.inst = inst;
return true;
}
@@ -872,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);
@@ -892,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);
@@ -1000,8 +1125,7 @@ static inline VkFormat toVkTextureFormat(QRhiTexture::Format format, QRhiTexture
return srgb ? VK_FORMAT_ASTC_12x12_SRGB_BLOCK : VK_FORMAT_ASTC_12x12_UNORM_BLOCK;
default:
- Q_UNREACHABLE();
- return VK_FORMAT_R8G8B8A8_UNORM;
+ Q_UNREACHABLE_RETURN(VK_FORMAT_R8G8B8A8_UNORM);
}
}
@@ -1210,18 +1334,18 @@ static void fillRenderPassCreateInfo(VkRenderPassCreateInfo *rpInfo,
{
memset(subpassDesc, 0, sizeof(VkSubpassDescription));
subpassDesc->pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
- subpassDesc->colorAttachmentCount = uint32_t(rpD->colorRefs.count());
+ subpassDesc->colorAttachmentCount = uint32_t(rpD->colorRefs.size());
subpassDesc->pColorAttachments = !rpD->colorRefs.isEmpty() ? rpD->colorRefs.constData() : nullptr;
subpassDesc->pDepthStencilAttachment = rpD->hasDepthStencil ? &rpD->dsRef : nullptr;
subpassDesc->pResolveAttachments = !rpD->resolveRefs.isEmpty() ? rpD->resolveRefs.constData() : nullptr;
memset(rpInfo, 0, sizeof(VkRenderPassCreateInfo));
rpInfo->sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
- rpInfo->attachmentCount = uint32_t(rpD->attDescs.count());
+ rpInfo->attachmentCount = uint32_t(rpD->attDescs.size());
rpInfo->pAttachments = rpD->attDescs.constData();
rpInfo->subpassCount = 1;
rpInfo->pSubpasses = subpassDesc;
- rpInfo->dependencyCount = uint32_t(rpD->subpassDeps.count());
+ rpInfo->dependencyCount = uint32_t(rpD->subpassDeps.size());
rpInfo->pDependencies = !rpD->subpassDeps.isEmpty() ? rpD->subpassDeps.constData() : nullptr;
}
@@ -1243,6 +1367,8 @@ bool QRhiVulkan::createDefaultRenderPass(QVkRenderPassDescriptor *rpD, bool hasD
rpD->colorRefs.append({ 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL });
rpD->hasDepthStencil = hasDepthStencil;
+ rpD->hasDepthStencilResolve = false;
+ rpD->multiViewCount = 0;
if (hasDepthStencil) {
// clear on load + no store + lazy alloc + transient image should play
@@ -1312,28 +1438,190 @@ bool QRhiVulkan::createDefaultRenderPass(QVkRenderPassDescriptor *rpD, bool hasD
return true;
}
+struct MultiViewRenderPassSetupHelper
+{
+ bool prepare(VkRenderPassCreateInfo *rpInfo, int multiViewCount, bool multiViewCap)
+ {
+ if (multiViewCount < 2)
+ return true;
+ if (!multiViewCap) {
+ qWarning("Cannot create multiview render pass without support for the Vulkan 1.1 multiview feature");
+ return false;
+ }
+#ifdef VK_VERSION_1_1
+ uint32_t allViewsMask = 0;
+ for (uint32_t i = 0; i < uint32_t(multiViewCount); ++i)
+ allViewsMask |= (1 << i);
+ multiViewMask = allViewsMask;
+ multiViewCorrelationMask = allViewsMask;
+ multiViewInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO;
+ multiViewInfo.subpassCount = 1;
+ multiViewInfo.pViewMasks = &multiViewMask;
+ multiViewInfo.correlationMaskCount = 1;
+ multiViewInfo.pCorrelationMasks = &multiViewCorrelationMask;
+ rpInfo->pNext = &multiViewInfo;
+#endif
+ return true;
+ }
+
+#ifdef VK_VERSION_1_1
+ VkRenderPassMultiviewCreateInfo multiViewInfo = {};
+ uint32_t multiViewMask = 0;
+ uint32_t multiViewCorrelationMask = 0;
+#endif
+};
+
+#ifdef VK_KHR_create_renderpass2
+// Effectively converts a VkRenderPassCreateInfo into a VkRenderPassCreateInfo2,
+// adding depth-stencil resolve support. Assumes a single subpass and no subpass
+// dependencies.
+struct RenderPass2SetupHelper
+{
+ bool prepare(VkRenderPassCreateInfo2 *rpInfo2, const VkRenderPassCreateInfo *rpInfo, const QVkRenderPassDescriptor *rpD, int multiViewCount) {
+ *rpInfo2 = {};
+
+ viewMask = 0;
+ if (multiViewCount >= 2) {
+ for (uint32_t i = 0; i < uint32_t(multiViewCount); ++i)
+ viewMask |= (1 << i);
+ }
+
+ attDescs2.resize(rpInfo->attachmentCount);
+ for (qsizetype i = 0; i < attDescs2.count(); ++i) {
+ VkAttachmentDescription2KHR &att2(attDescs2[i]);
+ const VkAttachmentDescription &att(rpInfo->pAttachments[i]);
+ att2 = {};
+ att2.sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2;
+ att2.flags = att.flags;
+ att2.format = att.format;
+ att2.samples = att.samples;
+ att2.loadOp = att.loadOp;
+ att2.storeOp = att.storeOp;
+ att2.stencilLoadOp = att.stencilLoadOp;
+ att2.stencilStoreOp = att.stencilStoreOp;
+ att2.initialLayout = att.initialLayout;
+ att2.finalLayout = att.finalLayout;
+ }
+
+ attRefs2.clear();
+ subpass2 = {};
+ subpass2.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2_KHR;
+ const VkSubpassDescription &subpassDesc(rpInfo->pSubpasses[0]);
+ subpass2.flags = subpassDesc.flags;
+ subpass2.pipelineBindPoint = subpassDesc.pipelineBindPoint;
+ if (multiViewCount >= 2)
+ subpass2.viewMask = viewMask;
+
+ // color attachment refs
+ qsizetype startIndex = attRefs2.count();
+ for (uint32_t j = 0; j < subpassDesc.colorAttachmentCount; ++j) {
+ attRefs2.append({});
+ VkAttachmentReference2KHR &attref2(attRefs2.last());
+ const VkAttachmentReference &attref(subpassDesc.pColorAttachments[j]);
+ attref2.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR;
+ attref2.attachment = attref.attachment;
+ attref2.layout = attref.layout;
+ attref2.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ }
+ subpass2.colorAttachmentCount = subpassDesc.colorAttachmentCount;
+ subpass2.pColorAttachments = attRefs2.constData() + startIndex;
+
+ // color resolve refs
+ if (subpassDesc.pResolveAttachments) {
+ startIndex = attRefs2.count();
+ for (uint32_t j = 0; j < subpassDesc.colorAttachmentCount; ++j) {
+ attRefs2.append({});
+ VkAttachmentReference2KHR &attref2(attRefs2.last());
+ const VkAttachmentReference &attref(subpassDesc.pResolveAttachments[j]);
+ attref2.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR;
+ attref2.attachment = attref.attachment;
+ attref2.layout = attref.layout;
+ attref2.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ }
+ subpass2.pResolveAttachments = attRefs2.constData() + startIndex;
+ }
+
+ // depth-stencil ref
+ if (subpassDesc.pDepthStencilAttachment) {
+ startIndex = attRefs2.count();
+ attRefs2.append({});
+ VkAttachmentReference2KHR &attref2(attRefs2.last());
+ const VkAttachmentReference &attref(*subpassDesc.pDepthStencilAttachment);
+ attref2.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR;
+ attref2.attachment = attref.attachment;
+ attref2.layout = attref.layout;
+ attref2.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
+ subpass2.pDepthStencilAttachment = attRefs2.constData() + startIndex;
+ }
+
+ // depth-stencil resolve ref
+#ifdef VK_KHR_depth_stencil_resolve
+ dsResolveDesc = {};
+ if (rpD->hasDepthStencilResolve) {
+ startIndex = attRefs2.count();
+ attRefs2.append({});
+ VkAttachmentReference2KHR &attref2(attRefs2.last());
+ attref2.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR;
+ attref2.attachment = rpD->dsResolveRef.attachment;
+ attref2.layout = rpD->dsResolveRef.layout;
+ attref2.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
+ dsResolveDesc.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE_KHR;
+ dsResolveDesc.depthResolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;
+ dsResolveDesc.stencilResolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;
+ dsResolveDesc.pDepthStencilResolveAttachment = attRefs2.constData() + startIndex;
+ subpass2.pNext = &dsResolveDesc;
+ }
+#endif
+
+ rpInfo2->sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2_KHR;
+ rpInfo2->pNext = nullptr; // the 1.1 VkRenderPassMultiviewCreateInfo is part of the '2' structs
+ rpInfo2->flags = rpInfo->flags;
+ rpInfo2->attachmentCount = rpInfo->attachmentCount;
+ rpInfo2->pAttachments = attDescs2.constData();
+ rpInfo2->subpassCount = 1;
+ rpInfo2->pSubpasses = &subpass2;
+ if (multiViewCount >= 2) {
+ rpInfo2->correlatedViewMaskCount = 1;
+ rpInfo2->pCorrelatedViewMasks = &viewMask;
+ }
+ return true;
+ }
+
+ QVarLengthArray<VkAttachmentDescription2KHR, 8> attDescs2;
+ QVarLengthArray<VkAttachmentReference2KHR, 8> attRefs2;
+ VkSubpassDescription2KHR subpass2;
+#ifdef VK_KHR_depth_stencil_resolve
+ VkSubpassDescriptionDepthStencilResolveKHR dsResolveDesc;
+#endif
+ uint32_t viewMask;
+};
+#endif // VK_KHR_create_renderpass2
+
bool QRhiVulkan::createOffscreenRenderPass(QVkRenderPassDescriptor *rpD,
const QRhiColorAttachment *firstColorAttachment,
const QRhiColorAttachment *lastColorAttachment,
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 = {};
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
@@ -1341,18 +1629,29 @@ 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;
+ const VkAttachmentStoreOp storeOp = storeDs ? VK_ATTACHMENT_STORE_OP_STORE : VK_ATTACHMENT_STORE_OP_DONT_CARE;
VkAttachmentDescription attDesc = {};
attDesc.format = dsFormat;
attDesc.samples = samples;
@@ -1360,11 +1659,11 @@ bool QRhiVulkan::createOffscreenRenderPass(QVkRenderPassDescriptor *rpD,
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()) {
@@ -1385,7 +1684,7 @@ bool QRhiVulkan::createOffscreenRenderPass(QVkRenderPassDescriptor *rpD,
}
VkAttachmentDescription attDesc = {};
- attDesc.format = dstFormat;
+ 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;
@@ -1395,14 +1694,39 @@ bool QRhiVulkan::createOffscreenRenderPass(QVkRenderPassDescriptor *rpD,
attDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
rpD->attDescs.append(attDesc);
- const VkAttachmentReference ref = { uint32_t(rpD->attDescs.count() - 1), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
+ const VkAttachmentReference ref = { uint32_t(rpD->attDescs.size() - 1), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
rpD->resolveRefs.append(ref);
} else {
const VkAttachmentReference ref = { VK_ATTACHMENT_UNUSED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
rpD->resolveRefs.append(ref);
}
}
- Q_ASSERT(rpD->colorRefs.count() == rpD->resolveRefs.count());
+ Q_ASSERT(rpD->colorRefs.size() == rpD->resolveRefs.size());
+
+ rpD->hasDepthStencilResolve = rpD->hasDepthStencil && depthResolveTexture;
+ if (rpD->hasDepthStencilResolve) {
+ QVkTexture *rtexD = QRHI_RES(QVkTexture, depthResolveTexture);
+ if (rtexD->samples > VK_SAMPLE_COUNT_1_BIT)
+ qWarning("Resolving into a multisample depth texture is not supported");
+
+ QVkTexture *texD = QRHI_RES(QVkTexture, depthResolveTexture);
+ if (texD->vkformat != rtexD->vkformat) {
+ qWarning("Multisample resolve between different depth-stencil formats (%d and %d) is not supported.",
+ int(texD->vkformat), int(rtexD->vkformat));
+ }
+
+ VkAttachmentDescription attDesc = {};
+ attDesc.format = rtexD->viewFormat;
+ attDesc.samples = VK_SAMPLE_COUNT_1_BIT;
+ attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; // ignored
+ attDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
+ attDesc.stencilLoadOp = attDesc.loadOp;
+ attDesc.stencilStoreOp = attDesc.storeOp;
+ attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ attDesc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+ rpD->attDescs.append(attDesc);
+ }
+ rpD->dsResolveRef = { uint32_t(rpD->attDescs.size() - 1), VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
// rpD->subpassDeps stays empty: don't yet know the correct initial/final
// access and stage stuff for the implicit deps at this point, so leave it
@@ -1413,10 +1737,35 @@ bool QRhiVulkan::createOffscreenRenderPass(QVkRenderPassDescriptor *rpD,
VkSubpassDescription subpassDesc;
fillRenderPassCreateInfo(&rpInfo, &subpassDesc, rpD);
- VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, &rpD->rp);
- if (err != VK_SUCCESS) {
- qWarning("Failed to create renderpass: %d", err);
+ MultiViewRenderPassSetupHelper multiViewHelper;
+ if (!multiViewHelper.prepare(&rpInfo, multiViewCount, caps.multiView))
return false;
+
+#ifdef VK_KHR_create_renderpass2
+ if (rpD->hasDepthStencilResolve && caps.renderPass2KHR) {
+ // Use the KHR extension, not the 1.2 core API, in order to support Vulkan 1.1.
+ VkRenderPassCreateInfo2KHR rpInfo2;
+ RenderPass2SetupHelper rp2Helper;
+ if (!rp2Helper.prepare(&rpInfo2, &rpInfo, rpD, multiViewCount))
+ return false;
+
+ VkResult err = vkCreateRenderPass2KHR(dev, &rpInfo2, nullptr, &rpD->rp);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create renderpass (using VkRenderPassCreateInfo2KHR): %d", err);
+ return false;
+ }
+ } else
+#endif
+ {
+ if (rpD->hasDepthStencilResolve) {
+ qWarning("Resolving multisample depth-stencil buffers is not supported without "
+ "VK_KHR_depth_stencil_resolve and VK_KHR_create_renderpass2");
+ }
+ VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, &rpD->rp);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create renderpass: %d", err);
+ return false;
+ }
}
return true;
@@ -1503,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;
@@ -1529,7 +1885,7 @@ bool QRhiVulkan::recreateSwapChain(QRhiSwapChain *swapChain)
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;
@@ -1591,7 +1947,9 @@ bool QRhiVulkan::recreateSwapChain(QRhiSwapChain *swapChain)
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];
@@ -1619,6 +1977,36 @@ 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;
@@ -1686,7 +2074,7 @@ void QRhiVulkan::releaseSwapChainResources(QRhiSwapChain *swapChain)
}
}
- for (int i = 0; i < swapChainD->bufferCount; ++i) {
+ for (int i = 0; i < swapChainD->bufferCount * (swapChainD->stereo ? 2 : 1); ++i) {
QVkSwapChain::ImageResources &image(swapChainD->imageRes[i]);
if (image.fb) {
df->vkDestroyFramebuffer(dev, image.fb, nullptr);
@@ -1731,12 +2119,32 @@ void QRhiVulkan::ensureCommandPoolForNewFrame()
df->vkResetCommandPool(dev, cmdPool[currentFrameSlot], flags);
}
+double QRhiVulkan::elapsedSecondsFromTimestamp(quint64 timestamp[2], bool *ok)
+{
+ quint64 mask = 0;
+ for (quint64 i = 0; i < timestampValidBits; i += 8)
+ mask |= 0xFFULL << i;
+ const quint64 ts0 = timestamp[0] & mask;
+ const quint64 ts1 = timestamp[1] & mask;
+ const float nsecsPerTick = physDevProperties.limits.timestampPeriod;
+ if (!qFuzzyIsNull(nsecsPerTick)) {
+ const float elapsedMs = float(ts1 - ts0) * nsecsPerTick / 1000000.0f;
+ const double elapsedSec = elapsedMs / 1000.0;
+ *ok = true;
+ return elapsedSec;
+ }
+ *ok = false;
+ return 0;
+}
+
QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags)
{
QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain);
const int frameResIndex = swapChainD->bufferCount > 1 ? swapChainD->currentFrameSlot : 0;
QVkSwapChain::FrameResources &frame(swapChainD->frameRes[frameResIndex]);
+ inst->handle()->beginFrame(swapChainD->window);
+
if (!frame.imageAcquired) {
// Wait if we are too far ahead, i.e. the thread gets throttled based on the presentation rate
// (note that we are using FIFO mode -> vsync)
@@ -1780,30 +2188,6 @@ QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::Begin
// mess up A's in-flight commands (as they are not in flight anymore).
waitCommandCompletion(frameResIndex);
- // Now is the time to read the timestamps for the previous frame for this slot.
- if (frame.timestampQueryIndex >= 0) {
- quint64 timestamp[2] = { 0, 0 };
- VkResult err = df->vkGetQueryPoolResults(dev, timestampQueryPool, uint32_t(frame.timestampQueryIndex), 2,
- 2 * sizeof(quint64), timestamp, sizeof(quint64),
- VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT);
- timestampQueryPoolMap.clearBit(frame.timestampQueryIndex / 2);
- frame.timestampQueryIndex = -1;
- if (err == VK_SUCCESS) {
- quint64 mask = 0;
- for (quint64 i = 0; i < timestampValidBits; i += 8)
- mask |= 0xFFULL << i;
- const quint64 ts0 = timestamp[0] & mask;
- const quint64 ts1 = timestamp[1] & mask;
- const float nsecsPerTick = physDevProperties.limits.timestampPeriod;
- if (!qFuzzyIsNull(nsecsPerTick)) {
- const float elapsedMs = float(ts1 - ts0) * nsecsPerTick / 1000000.0f;
- runGpuFrameTimeCallbacks(elapsedMs);
- }
- } else {
- qWarning("Failed to query timestamp: %d", err);
- }
- }
-
currentFrameSlot = int(swapChainD->currentFrameSlot);
currentSwapChain = swapChainD;
if (swapChainD->ds)
@@ -1817,32 +2201,56 @@ QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::Begin
if (cbres != QRhi::FrameOpSuccess)
return cbres;
- // when profiling is enabled, pick a free query (pair) from the pool
- int timestampQueryIdx = -1;
- if (hasGpuFrameTimeCallback() && swapChainD->bufferCount > 1) { // no timestamps if not having at least 2 frames in flight
- for (int i = 0; i < timestampQueryPoolMap.count(); ++i) {
+ swapChainD->cbWrapper.cb = frame.cmdBuf;
+
+ QVkSwapChain::ImageResources &image(swapChainD->imageRes[swapChainD->currentImageIndex]);
+ swapChainD->rtWrapper.d.fb = image.fb;
+
+ if (swapChainD->stereo) {
+ QVkSwapChain::ImageResources &image(
+ swapChainD->imageRes[swapChainD->currentImageIndex + swapChainD->bufferCount]);
+ swapChainD->rtWrapperRight.d.fb = image.fb;
+ }
+
+ prepareNewFrame(&swapChainD->cbWrapper);
+
+ // Read the timestamps for the previous frame for this slot.
+ if (frame.timestampQueryIndex >= 0) {
+ quint64 timestamp[2] = { 0, 0 };
+ VkResult err = df->vkGetQueryPoolResults(dev, timestampQueryPool, uint32_t(frame.timestampQueryIndex), 2,
+ 2 * sizeof(quint64), timestamp, sizeof(quint64),
+ VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT);
+ timestampQueryPoolMap.clearBit(frame.timestampQueryIndex / 2);
+ frame.timestampQueryIndex = -1;
+ if (err == VK_SUCCESS) {
+ bool ok = false;
+ const double elapsedSec = elapsedSecondsFromTimestamp(timestamp, &ok);
+ if (ok)
+ swapChainD->cbWrapper.lastGpuTime = elapsedSec;
+ } else {
+ qWarning("Failed to query timestamp: %d", err);
+ }
+ }
+
+ // No timestamps if the client did not opt in, or when not having at least 2 frames in flight.
+ if (rhiFlags.testFlag(QRhi::EnableTimestamps) && swapChainD->bufferCount > 1) {
+ int timestampQueryIdx = -1;
+ for (int i = 0; i < timestampQueryPoolMap.size(); ++i) {
if (!timestampQueryPoolMap.testBit(i)) {
timestampQueryPoolMap.setBit(i);
timestampQueryIdx = i * 2;
break;
}
}
- }
- if (timestampQueryIdx >= 0) {
- df->vkCmdResetQueryPool(frame.cmdBuf, timestampQueryPool, uint32_t(timestampQueryIdx), 2);
- // record timestamp at the start of the command buffer
- df->vkCmdWriteTimestamp(frame.cmdBuf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
- timestampQueryPool, uint32_t(timestampQueryIdx));
- frame.timestampQueryIndex = timestampQueryIdx;
+ if (timestampQueryIdx >= 0) {
+ df->vkCmdResetQueryPool(frame.cmdBuf, timestampQueryPool, uint32_t(timestampQueryIdx), 2);
+ // record timestamp at the start of the command buffer
+ df->vkCmdWriteTimestamp(frame.cmdBuf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
+ timestampQueryPool, uint32_t(timestampQueryIdx));
+ frame.timestampQueryIndex = timestampQueryIdx;
+ }
}
- swapChainD->cbWrapper.cb = frame.cmdBuf;
-
- QVkSwapChain::ImageResources &image(swapChainD->imageRes[swapChainD->currentImageIndex]);
- swapChainD->rtWrapper.d.fb = image.fb;
-
- prepareNewFrame(&swapChainD->cbWrapper);
-
return QRhi::FrameOpSuccess;
}
@@ -1851,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;
@@ -2055,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) {
@@ -2091,6 +2503,24 @@ QRhi::FrameOpResult QRhiVulkan::beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi
prepareNewFrame(cbWrapper);
ofr.active = true;
+ if (rhiFlags.testFlag(QRhi::EnableTimestamps)) {
+ int timestampQueryIdx = -1;
+ for (int i = 0; i < timestampQueryPoolMap.size(); ++i) {
+ if (!timestampQueryPoolMap.testBit(i)) {
+ timestampQueryPoolMap.setBit(i);
+ timestampQueryIdx = i * 2;
+ break;
+ }
+ }
+ if (timestampQueryIdx >= 0) {
+ df->vkCmdResetQueryPool(cbWrapper->cb, timestampQueryPool, uint32_t(timestampQueryIdx), 2);
+ // record timestamp at the start of the command buffer
+ df->vkCmdWriteTimestamp(cbWrapper->cb, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
+ timestampQueryPool, uint32_t(timestampQueryIdx));
+ ofr.timestampQueryIndex = timestampQueryIdx;
+ }
+ }
+
*cb = cbWrapper;
return QRhi::FrameOpSuccess;
}
@@ -2104,6 +2534,12 @@ QRhi::FrameOpResult QRhiVulkan::endOffscreenFrame(QRhi::EndFrameFlags flags)
QVkCommandBuffer *cbWrapper(ofr.cbWrapper[currentFrameSlot]);
recordPrimaryCommandBuffer(cbWrapper);
+ // record another timestamp, when enabled
+ if (ofr.timestampQueryIndex >= 0) {
+ df->vkCmdWriteTimestamp(cbWrapper->cb, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
+ timestampQueryPool, uint32_t(ofr.timestampQueryIndex + 1));
+ }
+
if (!ofr.cmdFence) {
VkFenceCreateInfo fenceInfo = {};
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
@@ -2126,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;
}
@@ -2243,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)
@@ -2384,14 +2845,19 @@ void QRhiVulkan::beginPass(QRhiCommandBuffer *cb,
float(colorClearValue.alphaF()) } };
cvs.append(cv);
}
- rpBeginInfo.clearValueCount = uint32_t(cvs.count());
+ for (int i = 0; i < rtD->dsResolveAttCount; ++i) {
+ VkClearValue cv;
+ cv.depthStencil = { depthStencilClearValue.depthClearValue(), depthStencilClearValue.stencilClearValue() };
+ cvs.append(cv);
+ }
+ rpBeginInfo.clearValueCount = uint32_t(cvs.size());
QVkCommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QVkCommandBuffer::Command::BeginRenderPass;
cmd.args.beginRenderPass.desc = rpBeginInfo;
- cmd.args.beginRenderPass.clearValueIndex = cbD->pools.clearValue.count();
+ cmd.args.beginRenderPass.clearValueIndex = cbD->pools.clearValue.size();
cmd.args.beginRenderPass.useSecondaryCb = cbD->passUsesSecondaryCb;
- cbD->pools.clearValue.append(cvs.constData(), cvs.count());
+ cbD->pools.clearValue.append(cvs.constData(), cvs.size());
if (cbD->passUsesSecondaryCb)
cbD->activeSecondaryCbStack.append(startSecondaryCommandBuffer(rtD));
@@ -2522,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:
@@ -2600,12 +3066,12 @@ void QRhiVulkan::dispatch(QRhiCommandBuffer *cb, int x, int y, int z)
df->vkCmdPipelineBarrier(secondaryCb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
0, 0, nullptr,
0, nullptr,
- imageBarriers.count(), imageBarriers.constData());
+ imageBarriers.size(), imageBarriers.constData());
}
if (!bufferBarriers.isEmpty()) {
df->vkCmdPipelineBarrier(secondaryCb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
0, 0, nullptr,
- bufferBarriers.count(), bufferBarriers.constData(),
+ bufferBarriers.size(), bufferBarriers.constData(),
0, nullptr);
}
df->vkCmdDispatch(secondaryCb, uint32_t(x), uint32_t(y), uint32_t(z));
@@ -2615,18 +3081,18 @@ void QRhiVulkan::dispatch(QRhiCommandBuffer *cb, int x, int y, int z)
cmd.cmd = QVkCommandBuffer::Command::ImageBarrier;
cmd.args.imageBarrier.srcStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
cmd.args.imageBarrier.dstStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
- cmd.args.imageBarrier.count = imageBarriers.count();
- cmd.args.imageBarrier.index = cbD->pools.imageBarrier.count();
- cbD->pools.imageBarrier.append(imageBarriers.constData(), imageBarriers.count());
+ cmd.args.imageBarrier.count = imageBarriers.size();
+ cmd.args.imageBarrier.index = cbD->pools.imageBarrier.size();
+ cbD->pools.imageBarrier.append(imageBarriers.constData(), imageBarriers.size());
}
if (!bufferBarriers.isEmpty()) {
QVkCommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QVkCommandBuffer::Command::BufferBarrier;
cmd.args.bufferBarrier.srcStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
cmd.args.bufferBarrier.dstStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
- cmd.args.bufferBarrier.count = bufferBarriers.count();
- cmd.args.bufferBarrier.index = cbD->pools.bufferBarrier.count();
- cbD->pools.bufferBarrier.append(bufferBarriers.constData(), bufferBarriers.count());
+ cmd.args.bufferBarrier.count = bufferBarriers.size();
+ cmd.args.bufferBarrier.index = cbD->pools.bufferBarrier.size();
+ cbD->pools.bufferBarrier.append(bufferBarriers.constData(), bufferBarriers.size());
}
QVkCommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QVkCommandBuffer::Command::Dispatch;
@@ -2681,8 +3147,8 @@ 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 = {};
@@ -2709,7 +3175,7 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i
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;
@@ -2731,7 +3197,7 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i
imageInfo[elem].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
}
bd.stex.count = data->count;
- imageInfoIndex = imageInfos.count();
+ imageInfoIndex = imageInfos.size();
imageInfos.append(imageInfo);
}
break;
@@ -2752,7 +3218,7 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i
imageInfo[elem].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
}
bd.stex.count = data->count;
- imageInfoIndex = imageInfos.count();
+ imageInfoIndex = imageInfos.size();
imageInfos.append(imageInfo);
}
break;
@@ -2768,7 +3234,7 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i
imageInfo[0].sampler = samplerD->sampler;
imageInfo[0].imageView = VK_NULL_HANDLE;
imageInfo[0].imageLayout = VK_IMAGE_LAYOUT_GENERAL;
- imageInfoIndex = imageInfos.count();
+ imageInfoIndex = imageInfos.size();
imageInfos.append(imageInfo);
}
break;
@@ -2777,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;
@@ -2786,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);
}
}
@@ -2803,7 +3269,7 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i
bufInfo.buffer = bufD->m_type == QRhiBuffer::Dynamic ? bufD->buffers[frameSlot] : bufD->buffers[0];
bufInfo.offset = b->u.ubuf.offset;
bufInfo.range = b->u.ubuf.maybeSize ? b->u.ubuf.maybeSize : bufD->m_size;
- bufferInfoIndex = bufferInfos.count();
+ bufferInfoIndex = bufferInfos.size();
bufferInfos.append(bufInfo);
}
break;
@@ -2817,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)
@@ -2826,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)
@@ -2872,7 +3338,7 @@ void QRhiVulkan::trackedBufferBarrier(QVkCommandBuffer *cbD, QVkBuffer *bufD, in
cmd.args.bufferBarrier.srcStageMask = s.stage;
cmd.args.bufferBarrier.dstStageMask = stage;
cmd.args.bufferBarrier.count = 1;
- cmd.args.bufferBarrier.index = cbD->pools.bufferBarrier.count();
+ cmd.args.bufferBarrier.index = cbD->pools.bufferBarrier.size();
cbD->pools.bufferBarrier.append(bufMemBarrier);
s.access = access;
@@ -2913,7 +3379,7 @@ void QRhiVulkan::trackedImageBarrier(QVkCommandBuffer *cbD, QVkTexture *texD,
cmd.args.imageBarrier.srcStageMask = srcStage;
cmd.args.imageBarrier.dstStageMask = stage;
cmd.args.imageBarrier.count = 1;
- cmd.args.imageBarrier.index = cbD->pools.imageBarrier.count();
+ cmd.args.imageBarrier.index = cbD->pools.imageBarrier.size();
cbD->pools.imageBarrier.append(barrier);
s.layout = layout;
@@ -2947,7 +3413,7 @@ void QRhiVulkan::depthStencilExplicitBarrier(QVkCommandBuffer *cbD, QVkRenderBuf
cmd.args.imageBarrier.srcStageMask = stages;
cmd.args.imageBarrier.dstStageMask = stages;
cmd.args.imageBarrier.count = 1;
- cmd.args.imageBarrier.index = cbD->pools.imageBarrier.count();
+ cmd.args.imageBarrier.index = cbD->pools.imageBarrier.size();
cbD->pools.imageBarrier.append(barrier);
}
@@ -2977,7 +3443,7 @@ void QRhiVulkan::subresourceBarrier(QVkCommandBuffer *cbD, VkImage image,
cmd.args.imageBarrier.srcStageMask = srcStage;
cmd.args.imageBarrier.dstStageMask = dstStage;
cmd.args.imageBarrier.count = 1;
- cmd.args.imageBarrier.index = cbD->pools.imageBarrier.count();
+ cmd.args.imageBarrier.index = cbD->pools.imageBarrier.size();
cbD->pools.imageBarrier.append(barrier);
}
@@ -3000,6 +3466,7 @@ void QRhiVulkan::prepareUploadSubres(QVkTexture *texD, int layer, int level,
qsizetype imageSizeBytes = 0;
const void *src = nullptr;
const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
+ const bool is1D = texD->m_flags.testFlag(QRhiTexture::OneDimensional);
VkBufferImageCopy copyInfo = {};
copyInfo.bufferOffset = *curOfs;
@@ -3010,6 +3477,8 @@ void QRhiVulkan::prepareUploadSubres(QVkTexture *texD, int layer, int level,
copyInfo.imageExtent.depth = 1;
if (is3D)
copyInfo.imageOffset.z = uint32_t(layer);
+ if (is1D)
+ copyInfo.imageOffset.y = uint32_t(layer);
const QByteArray rawData = subresDesc.data();
const QPoint dp = subresDesc.destinationTopLeft();
@@ -3096,6 +3565,12 @@ void QRhiVulkan::prepareUploadSubres(QVkTexture *texD, int layer, int level,
}
}
+void QRhiVulkan::printExtraErrorInfo(VkResult err)
+{
+ if (err == VK_ERROR_OUT_OF_DEVICE_MEMORY)
+ qWarning() << "Out of device memory, current allocator statistics are" << statistics();
+}
+
void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdateBatch *resourceUpdates)
{
QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates);
@@ -3133,6 +3608,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
bufD->stagingAllocations[currentFrameSlot] = allocation;
} else {
qWarning("Failed to create staging buffer of size %u: %d", bufD->m_size, err);
+ printExtraErrorInfo(err);
continue;
}
}
@@ -3145,8 +3621,8 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
continue;
}
memcpy(static_cast<uchar *>(p) + u.offset, u.data.constData(), u.data.size());
- vmaUnmapMemory(toVmaAllocator(allocator), a);
vmaFlushAllocation(toVmaAllocator(allocator), a, u.offset, u.data.size());
+ vmaUnmapMemory(toVmaAllocator(allocator), a);
trackedBufferBarrier(cbD, bufD, 0,
VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
@@ -3221,6 +3697,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
readback.stagingAlloc = allocation;
} else {
qWarning("Failed to create readback buffer of size %u: %d", readback.byteSize, err);
+ printExtraErrorInfo(err);
continue;
}
@@ -3249,9 +3726,9 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
QVkTexture *utexD = QRHI_RES(QVkTexture, u.dst);
// batch into a single staging buffer and a single CopyBufferToImage with multiple copyInfos
VkDeviceSize stagingSize = 0;
- for (int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
+ for (int layer = 0, maxLayer = u.subresDesc.size(); layer < maxLayer; ++layer) {
for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
- for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.subresDesc[layer][level]))
+ for (const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level]))
stagingSize += subresUploadByteSize(subresDesc);
}
}
@@ -3270,6 +3747,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
&utexD->stagingBuffers[currentFrameSlot], &allocation, nullptr);
if (err != VK_SUCCESS) {
qWarning("Failed to create image staging buffer of size %d: %d", int(stagingSize), err);
+ printExtraErrorInfo(err);
continue;
}
utexD->stagingAllocations[currentFrameSlot] = allocation;
@@ -3284,19 +3762,19 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
continue;
}
- for (int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
+ for (int layer = 0, maxLayer = u.subresDesc.size(); layer < maxLayer; ++layer) {
for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
const QList<QRhiTextureSubresourceUploadDescription> &srd(u.subresDesc[layer][level]);
if (srd.isEmpty())
continue;
- for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(srd)) {
+ for (const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(srd)) {
prepareUploadSubres(utexD, layer, level,
subresDesc, &curOfs, mp, &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);
@@ -3306,9 +3784,9 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
cmd.args.copyBufferToImage.src = utexD->stagingBuffers[currentFrameSlot];
cmd.args.copyBufferToImage.dst = utexD->image;
cmd.args.copyBufferToImage.dstLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
- cmd.args.copyBufferToImage.count = copyInfos.count();
- cmd.args.copyBufferToImage.bufferImageCopyIndex = cbD->pools.bufferImageCopy.count();
- cbD->pools.bufferImageCopy.append(copyInfos.constData(), copyInfos.count());
+ cmd.args.copyBufferToImage.count = copyInfos.size();
+ cmd.args.copyBufferToImage.bufferImageCopyIndex = cbD->pools.bufferImageCopy.size();
+ cbD->pools.bufferImageCopy.append(copyInfos.constData(), copyInfos.size());
// no reuse of staging, this is intentional
QRhiVulkan::DeferredReleaseEntry e;
@@ -3426,6 +3904,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
readback.stagingAlloc = allocation;
} else {
qWarning("Failed to create readback buffer of size %u: %d", readback.byteSize, err);
+ printExtraErrorInfo(err);
continue;
}
@@ -3491,10 +3970,10 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
if (!origStage)
origStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
- for (int layer = 0; layer < (isCube ? 6 : (isArray ? utexD->m_arraySize : 1)); ++layer) {
+ for (int layer = 0; layer < (isCube ? 6 : (isArray ? qMax(0, utexD->m_arraySize) : 1)); ++layer) {
int w = utexD->m_pixelSize.width();
int h = utexD->m_pixelSize.height();
- int depth = is3D ? utexD->m_depth : 1;
+ int depth = is3D ? qMax(1, utexD->m_depth) : 1;
for (int level = 1; level < int(utexD->mipLevelCount); ++level) {
if (level == 1) {
subresourceBarrier(cbD, utexD->image,
@@ -3592,16 +4071,16 @@ void QRhiVulkan::executeBufferHostWritesForSlot(QVkBuffer *bufD, int slot)
}
quint32 changeBegin = UINT32_MAX;
quint32 changeEnd = 0;
- for (const QVkBuffer::DynamicUpdate &u : qAsConst(bufD->pendingDynamicUpdates[slot])) {
+ 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 (u.offset + u.data.size() > changeEnd)
changeEnd = u.offset + u.data.size();
}
- vmaUnmapMemory(toVmaAllocator(allocator), a);
if (changeBegin < UINT32_MAX && changeBegin < changeEnd)
vmaFlushAllocation(toVmaAllocator(allocator), a, changeBegin, changeEnd - changeBegin);
+ vmaUnmapMemory(toVmaAllocator(allocator), a);
bufD->pendingDynamicUpdates[slot].clear();
}
@@ -3640,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) {
@@ -3673,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);
@@ -3696,7 +4177,7 @@ void QRhiVulkan::finishActiveReadbacks(bool forced)
{
QVarLengthArray<std::function<void()>, 4> completedCallbacks;
- for (int i = activeTextureReadbacks.count() - 1; i >= 0; --i) {
+ for (int i = activeTextureReadbacks.size() - 1; i >= 0; --i) {
const QRhiVulkan::TextureReadback &readback(activeTextureReadbacks[i]);
if (forced || currentFrameSlot == readback.activeFrameSlot || readback.activeFrameSlot < 0) {
readback.result->format = readback.format;
@@ -3721,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);
@@ -3782,33 +4263,26 @@ QList<int> QRhiVulkan::supportedSampleCounts() const
return result;
}
-VkSampleCountFlagBits QRhiVulkan::effectiveSampleCount(int sampleCount)
+VkSampleCountFlagBits QRhiVulkan::effectiveSampleCountBits(int sampleCount)
{
- // Stay compatible with QSurfaceFormat and friends where samples == 0 means the same as 1.
- sampleCount = qBound(1, sampleCount, 64);
-
- if (!supportedSampleCounts().contains(sampleCount)) {
- qWarning("Attempted to set unsupported sample count %d", sampleCount);
- return VK_SAMPLE_COUNT_1_BIT;
- }
+ const int s = effectiveSampleCount(sampleCount);
for (const auto &qvk_sampleCount : qvk_sampleCounts) {
- if (qvk_sampleCount.count == sampleCount)
+ if (qvk_sampleCount.count == s)
return qvk_sampleCount.mask;
}
- Q_UNREACHABLE();
- return VK_SAMPLE_COUNT_1_BIT;
+ Q_UNREACHABLE_RETURN(VK_SAMPLE_COUNT_1_BIT);
}
void QRhiVulkan::enqueueTransitionPassResources(QVkCommandBuffer *cbD)
{
cbD->passResTrackers.append(QRhiPassResourceTracker());
- cbD->currentPassResTrackerIndex = cbD->passResTrackers.count() - 1;
+ cbD->currentPassResTrackerIndex = cbD->passResTrackers.size() - 1;
QVkCommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QVkCommandBuffer::Command::TransitionPassResources;
- cmd.args.transitionResources.trackerIndex = cbD->passResTrackers.count() - 1;
+ cmd.args.transitionResources.trackerIndex = cbD->passResTrackers.size() - 1;
}
void QRhiVulkan::recordPrimaryCommandBuffer(QVkCommandBuffer *cbD)
@@ -4177,6 +4651,14 @@ 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);
}
@@ -4319,9 +4801,24 @@ bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const
return true;
case QRhi::NonFillPolygonMode:
return caps.nonFillPolygonMode;
+ case QRhi::OneDimensionalTextures:
+ return true;
+ case QRhi::OneDimensionalTextureMipmaps:
+ return true;
+ case QRhi::HalfAttributes:
+ return true;
+ case QRhi::RenderToOneDimensionalTexture:
+ return true;
+ case QRhi::ThreeDimensionalTextureMipmaps:
+ return true;
+ case QRhi::MultiView:
+ return caps.multiView;
+ case QRhi::TextureViewFormat:
+ return true;
+ case QRhi::ResolveDepthStencil:
+ return caps.renderPass2KHR && caps.depthStencilResolveKHR;
default:
- Q_UNREACHABLE();
- return false;
+ Q_UNREACHABLE_RETURN(false);
}
}
@@ -4359,8 +4856,7 @@ int QRhiVulkan::resourceLimit(QRhi::ResourceLimit limit) const
case QRhi::MaxVertexOutputs:
return physDevProperties.limits.maxVertexOutputComponents / 4;
default:
- Q_UNREACHABLE();
- return 0;
+ Q_UNREACHABLE_RETURN(0);
}
}
@@ -4376,15 +4872,20 @@ QRhiDriverInfo QRhiVulkan::driverInfo() const
QRhiStats QRhiVulkan::statistics()
{
- VmaStats stats;
- vmaCalculateStats(toVmaAllocator(allocator), &stats);
-
QRhiStats result;
result.totalPipelineCreationTime = totalPipelineCreationTime();
- result.blockCount = stats.total.blockCount;
- result.allocCount = stats.total.allocationCount;
- result.usedBytes = stats.total.usedBytes;
- result.unusedBytes = stats.total.unusedBytes;
+
+ 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;
}
@@ -4428,7 +4929,7 @@ QByteArray QRhiVulkan::pipelineCacheData()
size_t dataSize = 0;
VkResult err = df->vkGetPipelineCacheData(dev, pipelineCache, &dataSize, nullptr);
if (err != VK_SUCCESS) {
- qWarning("Failed to get pipeline cache data size: %d", err);
+ qCDebug(QRHI_LOG_INFO, "Failed to get pipeline cache data size: %d", err);
return QByteArray();
}
const size_t headerSize = sizeof(QVkPipelineCacheDataHeader);
@@ -4436,7 +4937,7 @@ QByteArray QRhiVulkan::pipelineCacheData()
data.resize(dataOffset + dataSize);
err = df->vkGetPipelineCacheData(dev, pipelineCache, &dataSize, data.data() + dataOffset);
if (err != VK_SUCCESS) {
- qWarning("Failed to get pipeline cache data of %d bytes: %d", int(dataSize), err);
+ qCDebug(QRHI_LOG_INFO, "Failed to get pipeline cache data of %d bytes: %d", int(dataSize), err);
return QByteArray();
}
@@ -4448,6 +4949,7 @@ QByteArray QRhiVulkan::pipelineCacheData()
header.deviceId = physDevProperties.deviceID;
header.dataSize = quint32(dataSize);
header.uuidSize = VK_UUID_SIZE;
+ header.reserved = 0;
memcpy(data.data(), &header, headerSize);
memcpy(data.data() + headerSize, physDevProperties.pipelineCacheUUID, VK_UUID_SIZE);
@@ -4461,7 +4963,7 @@ void QRhiVulkan::setPipelineCacheData(const QByteArray &data)
const size_t headerSize = sizeof(QVkPipelineCacheDataHeader);
if (data.size() < qsizetype(headerSize)) {
- qWarning("setPipelineCacheData: Invalid blob size");
+ qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob size");
return;
}
QVkPipelineCacheDataHeader header;
@@ -4469,49 +4971,49 @@ void QRhiVulkan::setPipelineCacheData(const QByteArray &data)
const quint32 rhiId = pipelineCacheRhiId();
if (header.rhiId != rhiId) {
- qWarning("setPipelineCacheData: The data is for a different QRhi version or backend (%u, %u)",
- rhiId, header.rhiId);
+ qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: The data is for a different QRhi version or backend (%u, %u)",
+ rhiId, header.rhiId);
return;
}
const quint32 arch = quint32(sizeof(void*));
if (header.arch != arch) {
- qWarning("setPipelineCacheData: Architecture does not match (%u, %u)",
+ qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Architecture does not match (%u, %u)",
arch, header.arch);
return;
}
if (header.driverVersion != physDevProperties.driverVersion) {
- qWarning("setPipelineCacheData: driverVersion does not match (%u, %u)",
- physDevProperties.driverVersion, header.driverVersion);
+ qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: driverVersion does not match (%u, %u)",
+ physDevProperties.driverVersion, header.driverVersion);
return;
}
if (header.vendorId != physDevProperties.vendorID) {
- qWarning("setPipelineCacheData: vendorID does not match (%u, %u)",
- physDevProperties.vendorID, header.vendorId);
+ qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: vendorID does not match (%u, %u)",
+ physDevProperties.vendorID, header.vendorId);
return;
}
if (header.deviceId != physDevProperties.deviceID) {
- qWarning("setPipelineCacheData: deviceID does not match (%u, %u)",
- physDevProperties.deviceID, header.deviceId);
+ qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: deviceID does not match (%u, %u)",
+ physDevProperties.deviceID, header.deviceId);
return;
}
if (header.uuidSize != VK_UUID_SIZE) {
- qWarning("setPipelineCacheData: VK_UUID_SIZE does not match (%u, %u)",
- quint32(VK_UUID_SIZE), header.uuidSize);
+ qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: VK_UUID_SIZE does not match (%u, %u)",
+ quint32(VK_UUID_SIZE), header.uuidSize);
return;
}
if (data.size() < qsizetype(headerSize + VK_UUID_SIZE)) {
- qWarning("setPipelineCacheData: Invalid blob, no uuid");
+ qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob, no uuid");
return;
}
if (memcmp(data.constData() + headerSize, physDevProperties.pipelineCacheUUID, VK_UUID_SIZE)) {
- qWarning("setPipelineCacheData: pipelineCacheUUID does not match");
+ qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: pipelineCacheUUID does not match");
return;
}
const size_t dataOffset = headerSize + VK_UUID_SIZE;
if (data.size() < qsizetype(dataOffset + header.dataSize)) {
- qWarning("setPipelineCacheData: Invalid blob, data missing");
+ qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob, data missing");
return;
}
@@ -4524,7 +5026,7 @@ void QRhiVulkan::setPipelineCacheData(const QByteArray &data)
qCDebug(QRHI_LOG_INFO, "Created pipeline cache with initial data of %d bytes",
int(header.dataSize));
} else {
- qWarning("Failed to create pipeline cache with initial data specified");
+ qCDebug(QRHI_LOG_INFO, "Failed to create pipeline cache with initial data specified");
}
}
@@ -4619,8 +5121,8 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin
// Do host writes and mark referenced shader resources as in-use.
// Also prepare to ensure the descriptor set we are going to bind refers to up-to-date Vk objects.
- for (int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) {
- const QRhiShaderResourceBinding::Data *b = srbD->sortedBindings[i].data();
+ for (int i = 0, ie = srbD->sortedBindings.size(); i != ie; ++i) {
+ const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->sortedBindings[i]);
QVkShaderResourceBindings::BoundResourceData &bd(descSetBd[i]);
switch (b->type) {
case QRhiShaderResourceBinding::UniformBuffer:
@@ -4766,8 +5268,8 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin
// because dynOfs has to be ordered based on the binding numbers,
// and neither srb nor dynamicOffsets has any such ordering
// requirement.
- for (const QRhiShaderResourceBinding &binding : qAsConst(srbD->sortedBindings)) {
- const QRhiShaderResourceBinding::Data *b = binding.data();
+ for (const QRhiShaderResourceBinding &binding : std::as_const(srbD->sortedBindings)) {
+ const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(binding);
if (b->type == QRhiShaderResourceBinding::UniformBuffer && b->u.ubuf.hasDynamicOffset) {
uint32_t offset = 0;
for (int i = 0; i < dynamicOffsetCount; ++i) {
@@ -4787,8 +5289,8 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin
gfxPsD ? VK_PIPELINE_BIND_POINT_GRAPHICS : VK_PIPELINE_BIND_POINT_COMPUTE,
gfxPsD ? gfxPsD->layout : compPsD->layout,
0, 1, &srbD->descSets[descSetIdx],
- uint32_t(dynOfs.count()),
- dynOfs.count() ? dynOfs.constData() : nullptr);
+ uint32_t(dynOfs.size()),
+ dynOfs.size() ? dynOfs.constData() : nullptr);
} else {
QVkCommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QVkCommandBuffer::Command::BindDescriptorSet;
@@ -4796,9 +5298,9 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin
: VK_PIPELINE_BIND_POINT_COMPUTE;
cmd.args.bindDescriptorSet.pipelineLayout = gfxPsD ? gfxPsD->layout : compPsD->layout;
cmd.args.bindDescriptorSet.descSet = srbD->descSets[descSetIdx];
- cmd.args.bindDescriptorSet.dynamicOffsetCount = dynOfs.count();
- cmd.args.bindDescriptorSet.dynamicOffsetIndex = cbD->pools.dynamicOffset.count();
- cbD->pools.dynamicOffset.append(dynOfs.constData(), dynOfs.count());
+ cmd.args.bindDescriptorSet.dynamicOffsetCount = dynOfs.size();
+ cmd.args.bindDescriptorSet.dynamicOffsetIndex = cbD->pools.dynamicOffset.size();
+ cbD->pools.dynamicOffset.append(dynOfs.constData(), dynOfs.size());
}
if (gfxPsD) {
@@ -4857,16 +5359,16 @@ void QRhiVulkan::setVertexInput(QRhiCommandBuffer *cb,
if (cbD->passUsesSecondaryCb) {
df->vkCmdBindVertexBuffers(cbD->activeSecondaryCbStack.last(), uint32_t(startBinding),
- uint32_t(bufs.count()), bufs.constData(), ofs.constData());
+ uint32_t(bufs.size()), bufs.constData(), ofs.constData());
} else {
QVkCommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QVkCommandBuffer::Command::BindVertexBuffer;
cmd.args.bindVertexBuffer.startBinding = startBinding;
- cmd.args.bindVertexBuffer.count = bufs.count();
- cmd.args.bindVertexBuffer.vertexBufferIndex = cbD->pools.vertexBuffer.count();
- cbD->pools.vertexBuffer.append(bufs.constData(), bufs.count());
- cmd.args.bindVertexBuffer.vertexBufferOffsetIndex = cbD->pools.vertexBufferOffset.count();
- cbD->pools.vertexBufferOffset.append(ofs.constData(), ofs.count());
+ cmd.args.bindVertexBuffer.count = bufs.size();
+ cmd.args.bindVertexBuffer.vertexBufferIndex = cbD->pools.vertexBuffer.size();
+ cbD->pools.vertexBuffer.append(bufs.constData(), bufs.size());
+ cmd.args.bindVertexBuffer.vertexBufferOffsetIndex = cbD->pools.vertexBufferOffset.size();
+ cbD->pools.vertexBufferOffset.append(ofs.constData(), ofs.size());
}
}
@@ -4915,7 +5417,7 @@ void QRhiVulkan::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport
// x,y is top-left in VkViewport but bottom-left in QRhiViewport
float x, y, w, h;
- if (!qrhi_toTopLeftRenderTargetRect(outputSize, viewport.viewport(), &x, &y, &w, &h))
+ if (!qrhi_toTopLeftRenderTargetRect<UnBounded>(outputSize, viewport.viewport(), &x, &y, &w, &h))
return;
QVkCommandBuffer::Command &cmd(cbD->commands.get());
@@ -4934,9 +5436,12 @@ void QRhiVulkan::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport
cmd.cmd = QVkCommandBuffer::Command::SetViewport;
}
- if (!QRHI_RES(QVkGraphicsPipeline, cbD->currentGraphicsPipeline)->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor)) {
+ if (cbD->currentGraphicsPipeline
+ && !QRHI_RES(QVkGraphicsPipeline, cbD->currentGraphicsPipeline)
+ ->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor)) {
QVkCommandBuffer::Command &cmd(cbD->commands.get());
VkRect2D *s = &cmd.args.setScissor.scissor;
+ qrhi_toTopLeftRenderTargetRect<Bounded>(outputSize, viewport.viewport(), &x, &y, &w, &h);
s->offset.x = int32_t(x);
s->offset.y = int32_t(y);
s->extent.width = uint32_t(w);
@@ -4959,7 +5464,7 @@ void QRhiVulkan::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor)
// x,y is top-left in VkRect2D but bottom-left in QRhiScissor
int x, y, w, h;
- if (!qrhi_toTopLeftRenderTargetRect(outputSize, scissor.scissor(), &x, &y, &w, &h))
+ if (!qrhi_toTopLeftRenderTargetRect<Bounded>(outputSize, scissor.scissor(), &x, &y, &w, &h))
return;
QVkCommandBuffer::Command &cmd(cbD->commands.get());
@@ -5064,7 +5569,7 @@ void QRhiVulkan::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name)
QVkCommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QVkCommandBuffer::Command::DebugMarkerBegin;
cmd.args.debugMarkerBegin.label = label;
- cmd.args.debugMarkerBegin.labelNameIndex = cbD->pools.debugMarkerData.count();
+ cmd.args.debugMarkerBegin.labelNameIndex = cbD->pools.debugMarkerData.size();
cbD->pools.debugMarkerData.append(name);
}
#else
@@ -5108,7 +5613,7 @@ void QRhiVulkan::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg)
QVkCommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QVkCommandBuffer::Command::DebugMarkerInsert;
cmd.args.debugMarkerInsert.label = label;
- cmd.args.debugMarkerInsert.labelNameIndex = cbD->pools.debugMarkerData.count();
+ cmd.args.debugMarkerInsert.labelNameIndex = cbD->pools.debugMarkerData.size();
cbD->pools.debugMarkerData.append(msg);
}
#else
@@ -5195,6 +5700,12 @@ void QRhiVulkan::endExternal(QRhiCommandBuffer *cb)
cbD->resetCachedState();
}
+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)
{
#ifdef VK_EXT_debug_utils
@@ -5242,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);
}
}
@@ -5257,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);
}
}
@@ -5272,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);
}
}
@@ -5293,8 +5801,7 @@ static inline VkShaderStageFlagBits toVkShaderStage(QRhiShaderStage::Type type)
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);
}
}
@@ -5331,9 +5838,32 @@ static inline VkFormat toVkAttributeFormat(QRhiVertexInputAttribute::Format form
return VK_FORMAT_R32G32_SINT;
case QRhiVertexInputAttribute::SInt:
return VK_FORMAT_R32_SINT;
+ case QRhiVertexInputAttribute::Half4:
+ return VK_FORMAT_R16G16B16A16_SFLOAT;
+ case QRhiVertexInputAttribute::Half3:
+ return VK_FORMAT_R16G16B16_SFLOAT;
+ case QRhiVertexInputAttribute::Half2:
+ return VK_FORMAT_R16G16_SFLOAT;
+ case QRhiVertexInputAttribute::Half:
+ return VK_FORMAT_R16_SFLOAT;
+ case QRhiVertexInputAttribute::UShort4:
+ return VK_FORMAT_R16G16B16A16_UINT;
+ case QRhiVertexInputAttribute::UShort3:
+ return VK_FORMAT_R16G16B16_UINT;
+ case QRhiVertexInputAttribute::UShort2:
+ return VK_FORMAT_R16G16_UINT;
+ case QRhiVertexInputAttribute::UShort:
+ return VK_FORMAT_R16_UINT;
+ case QRhiVertexInputAttribute::SShort4:
+ return VK_FORMAT_R16G16B16A16_SINT;
+ case QRhiVertexInputAttribute::SShort3:
+ return VK_FORMAT_R16G16B16_SINT;
+ case QRhiVertexInputAttribute::SShort2:
+ return VK_FORMAT_R16G16_SINT;
+ case QRhiVertexInputAttribute::SShort:
+ return VK_FORMAT_R16_SINT;
default:
- Q_UNREACHABLE();
- return VK_FORMAT_R32G32B32A32_SFLOAT;
+ Q_UNREACHABLE_RETURN(VK_FORMAT_R32G32B32A32_SFLOAT);
}
}
@@ -5355,8 +5885,7 @@ static inline VkPrimitiveTopology toVkTopology(QRhiGraphicsPipeline::Topology t)
case QRhiGraphicsPipeline::Patches:
return VK_PRIMITIVE_TOPOLOGY_PATCH_LIST;
default:
- Q_UNREACHABLE();
- return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
+ Q_UNREACHABLE_RETURN(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
}
}
@@ -5370,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);
}
}
@@ -5383,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);
}
}
@@ -5444,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);
}
}
@@ -5463,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);
}
}
@@ -5488,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);
}
}
@@ -5513,8 +6037,7 @@ 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);
}
}
@@ -5526,8 +6049,7 @@ static inline VkPolygonMode toVkPolygonMode(QRhiGraphicsPipeline::PolygonMode mo
case QRhiGraphicsPipeline::Line:
return VK_POLYGON_MODE_LINE;
default:
- Q_UNREACHABLE();
- return VK_POLYGON_MODE_FILL;
+ Q_UNREACHABLE_RETURN(VK_POLYGON_MODE_FILL);
}
}
@@ -5566,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);
}
}
@@ -5609,8 +6130,7 @@ 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);
}
}
@@ -5711,7 +6231,8 @@ bool QVkBuffer::create()
}
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;
}
@@ -5763,8 +6284,8 @@ void QVkBuffer::endFullDynamicBufferUpdateForCurrentFrame()
QRHI_RES_RHI(QRhiVulkan);
const int slot = rhiD->currentFrameSlot;
VmaAllocation a = toVmaAllocation(allocations[slot]);
- vmaUnmapMemory(toVmaAllocator(rhiD->allocator), a);
vmaFlushAllocation(toVmaAllocator(rhiD->allocator), a, 0, m_size);
+ vmaUnmapMemory(toVmaAllocator(rhiD->allocator), a);
}
QVkRenderBuffer::QVkRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
@@ -5819,7 +6340,7 @@ bool QVkRenderBuffer::create()
return false;
QRHI_RES_RHI(QRhiVulkan);
- samples = rhiD->effectiveSampleCount(m_sampleCount);
+ samples = rhiD->effectiveSampleCountBits(m_sampleCount);
switch (m_type) {
case QRhiRenderBuffer::Color:
@@ -5937,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);
@@ -5945,19 +6475,22 @@ bool QVkTexture::prepareCreate(QSize *adjustedSize)
return false;
}
- const QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
const bool isCube = m_flags.testFlag(CubeMap);
const bool isArray = m_flags.testFlag(TextureArray);
const bool is3D = m_flags.testFlag(ThreeDimensional);
+ const bool is1D = m_flags.testFlag(OneDimensional);
const bool hasMipMaps = m_flags.testFlag(MipMapped);
+ const QSize size = is1D ? QSize(qMax(1, m_pixelSize.width()), 1)
+ : (m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize);
+
mipLevelCount = uint(hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1);
const int maxLevels = QRhi::MAX_MIP_LEVELS;
if (mipLevelCount > maxLevels) {
qWarning("Too many mip levels (%d, max is %d), truncating mip chain", mipLevelCount, maxLevels);
mipLevelCount = maxLevels;
}
- samples = rhiD->effectiveSampleCount(m_sampleCount);
+ samples = rhiD->effectiveSampleCountBits(m_sampleCount);
if (samples > VK_SAMPLE_COUNT_1_BIT) {
if (isCube) {
qWarning("Cubemap texture cannot be multisample");
@@ -5980,12 +6513,18 @@ bool QVkTexture::prepareCreate(QSize *adjustedSize)
qWarning("Texture cannot be both array and 3D");
return false;
}
- m_depth = qMax(1, m_depth);
+ if (isCube && is1D) {
+ qWarning("Texture cannot be both cube and 1D");
+ return false;
+ }
+ if (is1D && is3D) {
+ qWarning("Texture cannot be both 1D and 3D");
+ return false;
+ }
if (m_depth > 1 && !is3D) {
qWarning("Texture cannot have a depth of %d when it is not 3D", m_depth);
return false;
}
- m_arraySize = qMax(0, m_arraySize);
if (m_arraySize > 0 && !isArray) {
qWarning("Texture cannot have an array size of %d when it is not an array", m_arraySize);
return false;
@@ -6013,14 +6552,17 @@ bool QVkTexture::finishCreate()
const bool isCube = m_flags.testFlag(CubeMap);
const bool isArray = m_flags.testFlag(TextureArray);
const bool is3D = m_flags.testFlag(ThreeDimensional);
+ const bool is1D = m_flags.testFlag(OneDimensional);
VkImageViewCreateInfo viewInfo = {};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = image;
- viewInfo.viewType = isCube ? VK_IMAGE_VIEW_TYPE_CUBE
- : (is3D ? VK_IMAGE_VIEW_TYPE_3D
- : (isArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D));
- viewInfo.format = vkformat;
+ viewInfo.viewType = isCube
+ ? VK_IMAGE_VIEW_TYPE_CUBE
+ : (is3D ? VK_IMAGE_VIEW_TYPE_3D
+ : (is1D ? (isArray ? VK_IMAGE_VIEW_TYPE_1D_ARRAY : VK_IMAGE_VIEW_TYPE_1D)
+ : (isArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D)));
+ viewInfo.format = viewFormatForSampling;
viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
@@ -6031,7 +6573,7 @@ bool QVkTexture::finishCreate()
viewInfo.subresourceRange.baseArrayLayer = uint32_t(m_arrayRangeStart);
viewInfo.subresourceRange.layerCount = uint32_t(m_arrayRangeLength);
} else {
- viewInfo.subresourceRange.layerCount = isCube ? 6 : (isArray ? m_arraySize : 1);
+ viewInfo.subresourceRange.layerCount = isCube ? 6 : (isArray ? qMax(0, m_arraySize) : 1);
}
VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &imageView);
@@ -6058,6 +6600,7 @@ bool QVkTexture::create()
const bool isCube = m_flags.testFlag(CubeMap);
const bool isArray = m_flags.testFlag(TextureArray);
const bool is3D = m_flags.testFlag(ThreeDimensional);
+ const bool is1D = m_flags.testFlag(OneDimensional);
VkImageCreateInfo imageInfo = {};
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
@@ -6080,13 +6623,13 @@ bool QVkTexture::create()
#endif
}
- imageInfo.imageType = is3D ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D;
+ imageInfo.imageType = is1D ? VK_IMAGE_TYPE_1D : is3D ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D;
imageInfo.format = vkformat;
imageInfo.extent.width = uint32_t(size.width());
imageInfo.extent.height = uint32_t(size.height());
- imageInfo.extent.depth = is3D ? m_depth : 1;
+ imageInfo.extent.depth = is3D ? qMax(1, m_depth) : 1;
imageInfo.mipLevels = mipLevelCount;
- imageInfo.arrayLayers = isCube ? 6 : (isArray ? m_arraySize : 1);
+ imageInfo.arrayLayers = isCube ? 6 : (isArray ? qMax(0, m_arraySize) : 1);
imageInfo.samples = samples;
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
@@ -6111,7 +6654,14 @@ bool QVkTexture::create()
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;
@@ -6158,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)
@@ -6168,14 +6718,17 @@ VkImageView QVkTexture::imageViewForLevel(int level)
const bool isCube = m_flags.testFlag(CubeMap);
const bool isArray = m_flags.testFlag(TextureArray);
const bool is3D = m_flags.testFlag(ThreeDimensional);
+ const bool is1D = m_flags.testFlag(OneDimensional);
VkImageViewCreateInfo viewInfo = {};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = image;
- viewInfo.viewType = isCube ? VK_IMAGE_VIEW_TYPE_CUBE
- : (is3D ? VK_IMAGE_VIEW_TYPE_3D
- : (isArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D));
- viewInfo.format = vkformat;
+ viewInfo.viewType = isCube
+ ? VK_IMAGE_VIEW_TYPE_CUBE
+ : (is3D ? VK_IMAGE_VIEW_TYPE_3D
+ : (is1D ? (isArray ? VK_IMAGE_VIEW_TYPE_1D_ARRAY : VK_IMAGE_VIEW_TYPE_1D)
+ : (isArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D)));
+ viewInfo.format = viewFormat; // this is writeViewFormat, regardless of Load, Store, or LoadStore; intentional
viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
@@ -6184,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 : (isArray ? m_arraySize : 1);
+ viewInfo.subresourceRange.layerCount = isCube ? 6 : (isArray ? qMax(0, m_arraySize) : 1);
VkImageView v = VK_NULL_HANDLE;
QRHI_RES_RHI(QRhiVulkan);
@@ -6262,7 +6815,7 @@ bool QVkSampler::create()
QVkRenderPassDescriptor::QVkRenderPassDescriptor(QRhiImplementation *rhi)
: QRhiRenderPassDescriptor(rhi)
{
- serializedFormatData.reserve(32);
+ serializedFormatData.reserve(64);
}
QVkRenderPassDescriptor::~QVkRenderPassDescriptor()
@@ -6317,16 +6870,20 @@ bool QVkRenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other
const QVkRenderPassDescriptor *o = QRHI_RES(const QVkRenderPassDescriptor, other);
- if (attDescs.count() != o->attDescs.count())
+ if (attDescs.size() != o->attDescs.size())
return false;
- if (colorRefs.count() != o->colorRefs.count())
+ if (colorRefs.size() != o->colorRefs.size())
return false;
- if (resolveRefs.count() != o->resolveRefs.count())
+ if (resolveRefs.size() != o->resolveRefs.size())
return false;
if (hasDepthStencil != o->hasDepthStencil)
return false;
+ if (hasDepthStencilResolve != o->hasDepthStencilResolve)
+ return false;
+ if (multiViewCount != o->multiViewCount)
+ return false;
- for (int i = 0, ie = colorRefs.count(); i != ie; ++i) {
+ for (int i = 0, ie = colorRefs.size(); i != ie; ++i) {
const uint32_t attIdx = colorRefs[i].attachment;
if (attIdx != o->colorRefs[i].attachment)
return false;
@@ -6342,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;
@@ -6350,6 +6907,14 @@ bool QVkRenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other
return false;
}
+ if (hasDepthStencilResolve) {
+ const uint32_t attIdx = dsResolveRef.attachment;
+ if (attIdx != o->dsResolveRef.attachment)
+ return false;
+ if (attIdx != VK_ATTACHMENT_UNUSED && !attachmentDescriptionEquals(attDescs[attIdx], o->attDescs[attIdx]))
+ return false;
+ }
+
// subpassDeps is not included
return true;
@@ -6360,10 +6925,12 @@ void QVkRenderPassDescriptor::updateSerializedFormat()
serializedFormatData.clear();
auto p = std::back_inserter(serializedFormatData);
- *p++ = attDescs.count();
- *p++ = colorRefs.count();
- *p++ = resolveRefs.count();
+ *p++ = attDescs.size();
+ *p++ = colorRefs.size();
+ *p++ = resolveRefs.size();
*p++ = hasDepthStencil;
+ *p++ = hasDepthStencilResolve;
+ *p++ = multiViewCount;
auto serializeAttachmentData = [this, &p](uint32_t attIdx) {
const bool used = attIdx != VK_ATTACHMENT_UNUSED;
@@ -6378,7 +6945,7 @@ void QVkRenderPassDescriptor::updateSerializedFormat()
*p++ = used ? a->finalLayout : 0;
};
- for (int i = 0, ie = colorRefs.count(); i != ie; ++i) {
+ for (int i = 0, ie = colorRefs.size(); i != ie; ++i) {
const uint32_t attIdx = colorRefs[i].attachment;
*p++ = attIdx;
serializeAttachmentData(attIdx);
@@ -6390,11 +6957,17 @@ void QVkRenderPassDescriptor::updateSerializedFormat()
serializeAttachmentData(attIdx);
}
- for (int i = 0, ie = resolveRefs.count(); i != ie; ++i) {
+ for (int i = 0, ie = resolveRefs.size(); i != ie; ++i) {
const uint32_t attIdx = resolveRefs[i].attachment;
*p++ = attIdx;
serializeAttachmentData(attIdx);
}
+
+ if (hasDepthStencilResolve) {
+ const uint32_t attIdx = dsResolveRef.attachment;
+ *p++ = attIdx;
+ serializeAttachmentData(attIdx);
+ }
}
QRhiRenderPassDescriptor *QVkRenderPassDescriptor::newCompatibleRenderPassDescriptor() const
@@ -6407,13 +6980,22 @@ QRhiRenderPassDescriptor *QVkRenderPassDescriptor::newCompatibleRenderPassDescri
rpD->resolveRefs = resolveRefs;
rpD->subpassDeps = subpassDeps;
rpD->hasDepthStencil = hasDepthStencil;
+ rpD->hasDepthStencilResolve = hasDepthStencilResolve;
+ rpD->multiViewCount = multiViewCount;
rpD->dsRef = dsRef;
+ rpD->dsResolveRef = dsResolveRef;
VkRenderPassCreateInfo rpInfo;
VkSubpassDescription subpassDesc;
fillRenderPassCreateInfo(&rpInfo, &subpassDesc, rpD);
QRHI_RES_RHI(QRhiVulkan);
+ MultiViewRenderPassSetupHelper multiViewHelper;
+ if (!multiViewHelper.prepare(&rpInfo, multiViewCount, rhiD->caps.multiView)) {
+ delete rpD;
+ return nullptr;
+ }
+
VkResult err = rhiD->df->vkCreateRenderPass(rhiD->dev, &rpInfo, nullptr, &rpD->rp);
if (err != VK_SUCCESS) {
qWarning("Failed to create renderpass: %d", err);
@@ -6502,6 +7084,11 @@ void QVkTextureRenderTarget::destroy()
resrtv[att] = VK_NULL_HANDLE;
}
+ e.textureRenderTarget.dsv = dsv;
+ dsv = VK_NULL_HANDLE;
+ e.textureRenderTarget.resdsv = resdsv;
+ resdsv = VK_NULL_HANDLE;
+
QRHI_RES_RHI(QRhiVulkan);
if (rhiD) {
rhiD->releaseQueue.append(e);
@@ -6520,8 +7107,10 @@ QRhiRenderPassDescriptor *QVkTextureRenderTarget::newCompatibleRenderPassDescrip
m_desc.cendColorAttachments(),
m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents),
m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents),
+ m_desc.depthTexture() && !m_flags.testFlag(DoNotStoreDepthStencilContents) && !m_desc.depthResolveTexture(),
m_desc.depthStencilBuffer(),
- m_desc.depthTexture()))
+ m_desc.depthTexture(),
+ m_desc.depthResolveTexture()))
{
delete rp;
return nullptr;
@@ -6538,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;
@@ -6555,11 +7144,17 @@ bool QVkTextureRenderTarget::create()
Q_ASSERT(texD || rbD);
if (texD) {
Q_ASSERT(texD->flags().testFlag(QRhiTexture::RenderTarget));
+ 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;
@@ -6568,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);
@@ -6593,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;
@@ -6613,6 +7226,7 @@ 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());
@@ -6622,8 +7236,9 @@ bool QVkTextureRenderTarget::create()
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;
@@ -6632,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);
@@ -6642,6 +7257,36 @@ 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().");
@@ -6651,7 +7296,7 @@ bool QVkTextureRenderTarget::create()
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());
@@ -6740,16 +7385,12 @@ bool QVkShaderResourceBindings::create()
sortedBindings.clear();
std::copy(m_bindings.cbegin(), m_bindings.cend(), std::back_inserter(sortedBindings));
- std::sort(sortedBindings.begin(), sortedBindings.end(),
- [](const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b)
- {
- return a.data()->binding < b.data()->binding;
- });
+ std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan);
hasSlottedResource = false;
hasDynamicOffset = false;
- for (const QRhiShaderResourceBinding &binding : qAsConst(sortedBindings)) {
- const QRhiShaderResourceBinding::Data *b = binding.data();
+ for (const QRhiShaderResourceBinding &binding : std::as_const(sortedBindings)) {
+ const QRhiShaderResourceBinding::Data *b = QRhiImplementation::shaderResourceBindingData(binding);
if (b->type == QRhiShaderResourceBinding::UniformBuffer && b->u.ubuf.buf) {
if (QRHI_RES(QVkBuffer, b->u.ubuf.buf)->type() == QRhiBuffer::Dynamic)
hasSlottedResource = true;
@@ -6759,8 +7400,8 @@ bool QVkShaderResourceBindings::create()
}
QVarLengthArray<VkDescriptorSetLayoutBinding, 4> vkbindings;
- for (const QRhiShaderResourceBinding &binding : qAsConst(sortedBindings)) {
- const QRhiShaderResourceBinding::Data *b = binding.data();
+ for (const QRhiShaderResourceBinding &binding : std::as_const(sortedBindings)) {
+ const QRhiShaderResourceBinding::Data *b = QRhiImplementation::shaderResourceBindingData(binding);
VkDescriptorSetLayoutBinding vkbinding = {};
vkbinding.binding = uint32_t(b->binding);
vkbinding.descriptorType = toVkDescriptorType(b);
@@ -6774,7 +7415,7 @@ bool QVkShaderResourceBindings::create()
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);
@@ -6794,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));
}
@@ -6809,13 +7450,8 @@ void QVkShaderResourceBindings::updateResources(UpdateFlags flags)
{
sortedBindings.clear();
std::copy(m_bindings.cbegin(), m_bindings.cend(), std::back_inserter(sortedBindings));
- if (!flags.testFlag(BindingsAreSorted)) {
- std::sort(sortedBindings.begin(), sortedBindings.end(),
- [](const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b)
- {
- return a.data()->binding < b.data()->binding;
- });
- }
+ if (!flags.testFlag(BindingsAreSorted))
+ std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan);
// Reset the state tracking table too - it can deal with assigning a
// different QRhiBuffer/Texture/Sampler for a binding point, but it cannot
@@ -6827,7 +7463,7 @@ void QVkShaderResourceBindings::updateResources(UpdateFlags flags)
// complicating the checks in setShaderResources(), reset the table here
// just like we do in create().
for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
- Q_ASSERT(boundResourceData[i].count() == sortedBindings.count());
+ Q_ASSERT(boundResourceData[i].size() == sortedBindings.size());
for (BoundResourceData &bd : boundResourceData[i])
memset(&bd, 0, sizeof(BoundResourceData));
}
@@ -6915,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)
@@ -6931,9 +7569,12 @@ bool QVkGraphicsPipeline::create()
? VK_VERTEX_INPUT_RATE_VERTEX : VK_VERTEX_INPUT_RATE_INSTANCE
};
if (it->classification() == QRhiVertexInputBinding::PerInstance && it->instanceStepRate() != 1) {
+#ifdef VK_EXT_vertex_attribute_divisor
if (rhiD->caps.vertexAttribDivisor) {
nonOneStepRates.append({ uint32_t(bindingIndex), it->instanceStepRate() });
- } else {
+ } 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");
@@ -6955,17 +7596,19 @@ bool QVkGraphicsPipeline::create()
}
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();
+#ifdef VK_EXT_vertex_attribute_divisor
VkPipelineVertexInputDivisorStateCreateInfoEXT divisorInfo = {};
if (!nonOneStepRates.isEmpty()) {
- 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;
@@ -6978,7 +7621,7 @@ bool QVkGraphicsPipeline::create()
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;
@@ -7036,7 +7679,7 @@ bool QVkGraphicsPipeline::create()
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 = {};
@@ -7058,7 +7701,7 @@ bool QVkGraphicsPipeline::create()
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)) {
+ for (const QRhiGraphicsPipeline::TargetBlend &b : std::as_const(m_targetBlends)) {
VkPipelineColorBlendAttachmentState blend = {};
blend.blendEnable = b.enable;
blend.srcColorBlendFactor = toVkBlendFactor(b.srcColor);
@@ -7076,7 +7719,7 @@ bool QVkGraphicsPipeline::create()
| 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;
@@ -7235,6 +7878,7 @@ const QRhiNativeHandles *QVkCommandBuffer::nativeHandles()
QVkSwapChain::QVkSwapChain(QRhiImplementation *rhi)
: QRhiSwapChain(rhi),
rtWrapper(rhi, this),
+ rtWrapperRight(rhi, this),
cbWrapper(rhi)
{
}
@@ -7277,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())
@@ -7304,6 +7953,9 @@ static inline bool hdrFormatMatchesVkSurfaceFormat(QRhiSwapChain::Format f, cons
case QRhiSwapChain::HDR10:
return (s.format == VK_FORMAT_A2B10G10R10_UNORM_PACK32 || s.format == VK_FORMAT_A2R10G10B10_UNORM_PACK32)
&& s.colorSpace == VK_COLOR_SPACE_HDR10_ST2084_EXT;
+ case QRhiSwapChain::HDRExtendedDisplayP3Linear:
+ return s.format == VK_FORMAT_R16G16B16A16_SFLOAT
+ && s.colorSpace == VK_COLOR_SPACE_DISPLAY_P3_LINEAR_EXT;
default:
break;
}
@@ -7397,11 +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->inst->supportsPresent(rhiD->physDev, rhiD->gfxQueueFamilyIdx, m_window)) {
+ qWarning("Presenting not supported on this window");
+ return false;
}
quint32 formatCount = 0;
@@ -7415,11 +8065,9 @@ bool QVkSwapChain::ensureSurface()
const bool srgbRequested = m_flags.testFlag(sRGB);
for (int i = 0; i < int(formatCount); ++i) {
if (formats[i].format != VK_FORMAT_UNDEFINED) {
- bool ok = false;
- if (m_format == SDR)
- ok = srgbRequested == isSrgbFormat(formats[i].format);
- else
- ok = hdrFormatMatchesVkSurfaceFormat(m_format, formats[i]);
+ bool ok = srgbRequested == isSrgbFormat(formats[i].format);
+ if (m_format != SDR)
+ ok &= hdrFormatMatchesVkSurfaceFormat(m_format, formats[i]);
if (ok) {
colorFormat = formats[i].format;
colorSpace = formats[i].colorSpace;
@@ -7428,7 +8076,7 @@ bool QVkSwapChain::ensureSurface()
}
}
- samples = rhiD->effectiveSampleCount(m_sampleCount);
+ samples = rhiD->effectiveSampleCountBits(m_sampleCount);
quint32 presModeCount = 0;
rhiD->vkGetPhysicalDeviceSurfacePresentModesKHR(rhiD->physDev, surface, &presModeCount, nullptr);
@@ -7499,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
@@ -7515,7 +8164,7 @@ bool QVkSwapChain::createOrResize()
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());
@@ -7528,6 +8177,56 @@ bool QVkSwapChain::createOrResize()
}
}
+ if (stereo) {
+ rtWrapperRight.setRenderPassDescriptor(
+ m_renderPassDesc); // for the public getter in QRhiRenderTarget
+ rtWrapperRight.d.rp = QRHI_RES(QVkRenderPassDescriptor, m_renderPassDesc);
+ Q_ASSERT(rtWrapperRight.d.rp && rtWrapperRight.d.rp->rp);
+
+ rtWrapperRight.d.pixelSize = pixelSize;
+ rtWrapperRight.d.dpr = float(window->devicePixelRatio());
+ rtWrapperRight.d.sampleCount = samples;
+ rtWrapperRight.d.colorAttCount = 1;
+ if (m_depthStencil) {
+ rtWrapperRight.d.dsAttCount = 1;
+ ds = QRHI_RES(QVkRenderBuffer, m_depthStencil);
+ } else {
+ rtWrapperRight.d.dsAttCount = 0;
+ ds = nullptr;
+ }
+ rtWrapperRight.d.dsResolveAttCount = 0;
+ if (samples > VK_SAMPLE_COUNT_1_BIT)
+ rtWrapperRight.d.resolveAttCount = 1;
+ else
+ rtWrapperRight.d.resolveAttCount = 0;
+
+ for (int i = 0; i < bufferCount; ++i) {
+ QVkSwapChain::ImageResources &image(imageRes[i + bufferCount]);
+ VkImageView views[3] = {
+ // color, ds, resolve
+ samples > VK_SAMPLE_COUNT_1_BIT ? image.msaaImageView : image.imageView,
+ ds ? ds->imageView : VK_NULL_HANDLE,
+ samples > VK_SAMPLE_COUNT_1_BIT ? image.imageView : VK_NULL_HANDLE
+ };
+
+ VkFramebufferCreateInfo fbInfo = {};
+ fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
+ fbInfo.renderPass = rtWrapperRight.d.rp->rp;
+ fbInfo.attachmentCount = uint32_t(rtWrapperRight.d.colorAttCount + rtWrapperRight.d.dsAttCount
+ + rtWrapperRight.d.resolveAttCount + rtWrapperRight.d.dsResolveAttCount);
+ fbInfo.pAttachments = views;
+ fbInfo.width = uint32_t(pixelSize.width());
+ fbInfo.height = uint32_t(pixelSize.height());
+ fbInfo.layers = 1;
+
+ VkResult err = rhiD->df->vkCreateFramebuffer(rhiD->dev, &fbInfo, nullptr, &image.fb);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create framebuffer: %d", err);
+ return false;
+ }
+ }
+ }
+
frameCount = 0;
if (needsRegistration)