Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qrhivulkan.cpp
Go to the documentation of this file.
1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#include "qrhivulkan_p.h"
6#include <qpa/qplatformvulkaninstance.h>
7
8#define VMA_IMPLEMENTATION
9#define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
10#define VMA_STATIC_VULKAN_FUNCTIONS 0
11#define VMA_RECORDING_ENABLED 0
12#define VMA_DEDICATED_ALLOCATION 0
14Q_STATIC_LOGGING_CATEGORY(QRHI_LOG_VMA, "qt.rhi.vma")
15QT_END_NAMESPACE
16#define VMA_ASSERT(expr) Q_ASSERT(expr)
17#ifdef QT_DEBUG
18#define VMA_DEBUG_INITIALIZE_ALLOCATIONS 1
19#define VMA_DEBUG_LOG(str) QT_PREPEND_NAMESPACE(qDebug)(QT_PREPEND_NAMESPACE(QRHI_LOG_VMA), (str))
20#define VMA_DEBUG_LOG_FORMAT(format, ...) QT_PREPEND_NAMESPACE(qDebug)(QT_PREPEND_NAMESPACE(QRHI_LOG_VMA), format, __VA_ARGS__)
21#endif
22template<typename... Args>
23static void debugVmaLeak(const char *format, Args&&... args)
24{
25#ifndef QT_NO_DEBUG
26 // debug builds: just do it always
27 static bool leakCheck = true;
28#else
29 // release builds: opt-in
30 static bool leakCheck = QT_PREPEND_NAMESPACE(qEnvironmentVariableIntValue)("QT_RHI_LEAK_CHECK");
31#endif
32 if (leakCheck)
33 QT_PREPEND_NAMESPACE(qWarning)(QT_PREPEND_NAMESPACE(QRHI_LOG_VMA), format, std::forward<Args>(args)...);
34}
35#define VMA_LEAK_LOG_FORMAT(format, ...) debugVmaLeak(format, __VA_ARGS__)
36QT_WARNING_PUSH
37QT_WARNING_DISABLE_GCC("-Wsuggest-override")
38QT_WARNING_DISABLE_GCC("-Wundef")
39QT_WARNING_DISABLE_CLANG("-Wundef")
40#if defined(Q_CC_CLANG) && Q_CC_CLANG >= 1100
41QT_WARNING_DISABLE_CLANG("-Wdeprecated-copy")
42#endif
43#include "vk_mem_alloc.h"
44QT_WARNING_POP
45
46#include <qmath.h>
47#include <QVulkanFunctions>
48#include <QtGui/qwindow.h>
49#include <private/qvulkandefaultinstance_p.h>
50#include <optional>
51
52QT_BEGIN_NAMESPACE
53
54/*
55 Vulkan 1.0 backend. Provides a double-buffered swapchain that throttles the
56 rendering thread to vsync. Textures and "static" buffers are device local,
57 and a separate, host visible staging buffer is used to upload data to them.
58 "Dynamic" buffers are in host visible memory and are duplicated (since there
59 can be 2 frames in flight). This is handled transparently to the application.
60
61 Barriers are generated automatically for each render or compute pass, based
62 on the resources that are used in that pass (in QRhiShaderResourceBindings,
63 vertex inputs, etc.). This implies deferring the recording of the command
64 buffer since the barriers have to be placed at the right place (before the
65 pass), and that can only be done once we know all the things the pass does.
66
67 This in turn has implications for integrating external commands
68 (beginExternal() - direct Vulkan calls - endExternal()) because that is
69 incompatible with this approach by nature. Therefore we support another mode
70 of operation, where each render or compute pass uses one or more secondary
71 command buffers (recorded right away), with each beginExternal() leading to
72 closing the current secondary cb, creating a new secondary cb for the
73 external content, and then starting yet another one in endExternal() for
74 whatever comes afterwards in the pass. This way the primary command buffer
75 only has vkCmdExecuteCommand(s) within a renderpass instance
76 (Begin-EndRenderPass). (i.e. our only subpass is then
77 VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS instead of
78 VK_SUBPASS_CONTENTS_INLINE)
79
80 The command buffer management mode is decided on a per frame basis,
81 controlled by the ExternalContentsInPass flag of beginFrame().
82*/
83
84/*!
85 \class QRhiVulkanInitParams
86 \inmodule QtGuiPrivate
87 \inheaderfile rhi/qrhi.h
88 \since 6.6
89 \brief Vulkan specific initialization parameters.
90
91 \note This is a RHI API with limited compatibility guarantees, see \l QRhi
92 for details.
93
94 A Vulkan-based QRhi needs at minimum a valid QVulkanInstance. It is up to
95 the user to ensure this is available and initialized. This is typically
96 done in main() similarly to the following:
97
98 \badcode
99 int main(int argc, char **argv)
100 {
101 ...
102
103 QVulkanInstance inst;
104 inst.setLayers({ "VK_LAYER_KHRONOS_validation" }); // for debugging only, not for release builds
105 inst.setExtensions(QRhiVulkanInitParams::preferredInstanceExtensions());
106 if (!inst.create())
107 qFatal("Vulkan not available");
108
109 ...
110 }
111 \endcode
112
113 This example enables the
114 \l{https://github.com/KhronosGroup/Vulkan-ValidationLayers}{Vulkan
115 validation layers}, when they are available, and also enables the
116 instance-level extensions QRhi reports as desirable (such as,
117 VK_KHR_get_physical_device_properties2), as long as they are supported by
118 the Vulkan implementation at run time.
119
120 The former is optional, and is useful during the development phase
121 QVulkanInstance conveniently redirects messages and warnings to qDebug.
122 Avoid enabling it in production builds, however. The latter is strongly
123 recommended, and is important in order to make certain features functional
124 (for example, QRhi::CustomInstanceStepRate).
125
126 Once this is done, a Vulkan-based QRhi can be created by passing the
127 instance and a QWindow with its surface type set to
128 QSurface::VulkanSurface:
129
130 \badcode
131 QRhiVulkanInitParams params;
132 params.inst = vulkanInstance;
133 params.window = window;
134 rhi = QRhi::create(QRhi::Vulkan, &params);
135 \endcode
136
137 The window is optional and can be omitted. This is not recommended however
138 because there is then no way to ensure presenting is supported while
139 choosing a graphics queue.
140
141 \note Even when a window is specified, QRhiSwapChain objects can be created
142 for other windows as well, as long as they all have their
143 QWindow::surfaceType() set to QSurface::VulkanSurface.
144
145 To request additional extensions to be enabled on the Vulkan device, list them
146 in deviceExtensions. This can be relevant when integrating with native Vulkan
147 rendering code.
148
149 It is expected that the backend's desired list of instance extensions will
150 be queried by calling the static function preferredInstanceExtensions()
151 before initializing a QVulkanInstance. The returned list can be safely
152 passed to QVulkanInstance::setExtensions() as-is, because unsupported
153 extensions are filtered out automatically. If this is not done, certain
154 features, such as QRhi::CustomInstanceStepRate may be reported as
155 unsupported even when the Vulkan implementation on the system has support
156 for the relevant functionality.
157
158 For full functionality the QVulkanInstance needs to have API 1.1 enabled,
159 when available. This means calling QVulkanInstance::setApiVersion() with
160 1.1 or higher whenever QVulkanInstance::supportedApiVersion() reports that
161 at least Vulkan 1.1 is supported. If this is not done, certain features,
162 such as QRhi::RenderTo3DTextureSlice may be reported as unsupported even
163 when the Vulkan implementation on the system supports Vulkan 1.1 or newer.
164
165 \section2 Working with existing Vulkan devices
166
167 When interoperating with another graphics engine, it may be necessary to
168 get a QRhi instance that uses the same Vulkan device. This can be achieved
169 by passing a pointer to a QRhiVulkanNativeHandles to QRhi::create().
170
171 The physical device must always be set to a non-null value. If the
172 intention is to just specify a physical device, but leave the rest of the
173 VkDevice and queue creation to QRhi, then no other members need to be
174 filled out in the struct. For example, this is the case when working with
175 OpenXR.
176
177 To adopt an existing \c VkDevice, the device field must be set to a
178 non-null value as well. In addition, the graphics queue family index is
179 required. The queue index is optional, as the default of 0 is often
180 suitable.
181
182 Optionally, an existing command pool object can be specified as well. Also
183 optionally, vmemAllocator can be used to share the same
184 \l{https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator}{Vulkan
185 memory allocator} between two QRhi instances.
186
187 The QRhi does not take ownership of any of the external objects.
188
189 Applications are encouraged to query the list of desired device extensions
190 by calling the static function preferredExtensionsForImportedDevice(), and
191 enable them on the VkDevice. Otherwise certain QRhi features may not be
192 available.
193 */
194
195/*!
196 \variable QRhiVulkanInitParams::inst
197
198 The QVulkanInstance that has already been successfully
199 \l{QVulkanInstance::create()}{created}, required.
200*/
201
202/*!
203 \variable QRhiVulkanInitParams::window
204
205 Optional, but recommended when targeting a QWindow.
206*/
207
208/*!
209 \variable QRhiVulkanInitParams::deviceExtensions
210
211 Optional, empty by default. The list of Vulkan device extensions to enable.
212 Unsupported extensions are ignored gracefully.
213*/
214
215/*!
216 \class QRhiVulkanNativeHandles
217 \inmodule QtGuiPrivate
218 \inheaderfile rhi/qrhi.h
219 \since 6.6
220 \brief Collects device, queue, and other Vulkan objects that are used by the QRhi.
221
222 \note Ownership of the Vulkan objects is never transferred.
223
224 \note This is a RHI API with limited compatibility guarantees, see \l QRhi
225 for details.
226 */
227
228/*!
229 \variable QRhiVulkanNativeHandles::physDev
230
231 When different from \nullptr, specifies the Vulkan physical device to use.
232*/
233
234/*!
235 \variable QRhiVulkanNativeHandles::dev
236
237 When wanting to import not just a physical device, but also use an already
238 existing VkDevice, set this and the graphics queue index and family index.
239*/
240
241/*!
242 \variable QRhiVulkanNativeHandles::gfxQueueFamilyIdx
243
244 Graphics queue family index.
245*/
246
247/*!
248 \variable QRhiVulkanNativeHandles::gfxQueueIdx
249
250 Graphics queue index.
251*/
252
253/*!
254 \variable QRhiVulkanNativeHandles::vmemAllocator
255
256 Relevant only when importing an existing memory allocator object,
257 leave it set to \nullptr otherwise.
258*/
259
260/*!
261 \variable QRhiVulkanNativeHandles::gfxQueue
262
263 Output only, not used by QRhi::create(), only set by the
264 QRhi::nativeHandles() accessor. The graphics VkQueue used by the QRhi.
265*/
266
267/*!
268 \variable QRhiVulkanNativeHandles::inst
269
270 Output only, not used by QRhi::create(), only set by the
271 QRhi::nativeHandles() accessor. The QVulkanInstance used by the QRhi.
272*/
273
274/*!
275 \class QRhiVulkanCommandBufferNativeHandles
276 \inmodule QtGuiPrivate
277 \inheaderfile rhi/qrhi.h
278 \since 6.6
279 \brief Holds the Vulkan command buffer object that is backing a QRhiCommandBuffer.
280
281 \note The Vulkan command buffer object is only guaranteed to be valid, and
282 in recording state, while recording a frame. That is, between a
283 \l{QRhi::beginFrame()}{beginFrame()} - \l{QRhi::endFrame()}{endFrame()} or
284 \l{QRhi::beginOffscreenFrame()}{beginOffscreenFrame()} -
285 \l{QRhi::endOffscreenFrame()}{endOffscreenFrame()} pair.
286
287 \note This is a RHI API with limited compatibility guarantees, see \l QRhi
288 for details.
289 */
290
291/*!
292 \variable QRhiVulkanCommandBufferNativeHandles::commandBuffer
293
294 The VkCommandBuffer object.
295*/
296
297/*!
298 \class QRhiVulkanRenderPassNativeHandles
299 \inmodule QtGuiPrivate
300 \inheaderfile rhi/qrhi.h
301 \since 6.6
302 \brief Holds the Vulkan render pass object backing a QRhiRenderPassDescriptor.
303
304 \note This is a RHI API with limited compatibility guarantees, see \l QRhi
305 for details.
306 */
307
308/*!
309 \variable QRhiVulkanRenderPassNativeHandles::renderPass
310
311 The VkRenderPass object.
312*/
313
314/*!
315 \class QRhiVulkanQueueSubmitParams
316 \inmodule QtGui
317 \since 6.9
318 \brief References additional Vulkan API objects that get passed to \c vkQueueSubmit().
319
320 \note This is a RHI API with limited compatibility guarantees, see \l QRhi
321 for details.
322*/
323
324/*!
325 \variable QRhiVulkanQueueSubmitParams::waitSemaphoreCount
326
327 See
328 \l{https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkSubmitInfo.html}{VkSubmitInfo}
329 for details.
330*/
331
332/*!
333 \variable QRhiVulkanQueueSubmitParams::waitSemaphores
334
335 See
336 \l{https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkSubmitInfo.html}{VkSubmitInfo}
337 for details.
338*/
339
340/*!
341 \variable QRhiVulkanQueueSubmitParams::signalSemaphoreCount
342
343 See
344 \l{https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkSubmitInfo.html}{VkSubmitInfo}
345 for details.
346*/
347
348/*!
349 \variable QRhiVulkanQueueSubmitParams::signalSemaphores
350
351 See
352 \l{https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkSubmitInfo.html}{VkSubmitInfo}
353 for details.
354*/
355
356/*!
357 \variable QRhiVulkanQueueSubmitParams::presentWaitSemaphoreCount
358
359 When non-zero, this applies to the next \c vkQueuePresentKHR() call. See
360 \l{https://registry.khronos.org/VulkanSC/specs/1.0-extensions/man/html/VkPresentInfoKHR.html}{VkPresentInfoKHR}
361 for details.
362*/
363
364/*!
365 \variable QRhiVulkanQueueSubmitParams::presentWaitSemaphores
366
367 See
368 \l{https://registry.khronos.org/VulkanSC/specs/1.0-extensions/man/html/VkPresentInfoKHR.html}{VkPresentInfoKHR}
369 for details.
370 */
371
372template <class Int>
373inline Int aligned(Int v, Int byteAlign)
374{
375 return (v + byteAlign - 1) & ~(byteAlign - 1);
376}
377
379
380static VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL wrap_vkGetInstanceProcAddr(VkInstance, const char *pName)
381{
382 return globalVulkanInstance->getInstanceProcAddr(pName);
383}
384
385static VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL wrap_vkGetDeviceProcAddr(VkDevice device, const char *pName)
386{
387 return globalVulkanInstance->functions()->vkGetDeviceProcAddr(device, pName);
388}
389
391{
392 return reinterpret_cast<VmaAllocation>(a);
393}
394
396{
397 return reinterpret_cast<VmaAllocator>(a);
398}
399
400/*!
401 \return the list of instance extensions that are expected to be enabled on
402 the QVulkanInstance that is used for the Vulkan-based QRhi.
403
404 The returned list can be safely passed to QVulkanInstance::setExtensions()
405 as-is, because unsupported extensions are filtered out automatically.
406 */
407QByteArrayList QRhiVulkanInitParams::preferredInstanceExtensions()
408{
409 return {
410 QByteArrayLiteral("VK_KHR_get_physical_device_properties2"),
411 // to silence validation when e.g. on Wayland a surface format's colorspace is VK_COLOR_SPACE_PASS_THROUGH_EXT
412 QByteArrayLiteral("VK_EXT_swapchain_colorspace")
413 };
414}
415
416/*!
417 \return the list of device extensions that are expected to be enabled on the
418 \c VkDevice when creating a Vulkan-based QRhi with an externally created
419 \c VkDevice object.
420 */
421QByteArrayList QRhiVulkanInitParams::preferredExtensionsForImportedDevice()
422{
423 return {
424 QByteArrayLiteral("VK_KHR_swapchain"),
425 QByteArrayLiteral("VK_EXT_vertex_attribute_divisor"),
426 QByteArrayLiteral("VK_KHR_create_renderpass2"),
427 QByteArrayLiteral("VK_KHR_depth_stencil_resolve"),
428 QByteArrayLiteral("VK_KHR_fragment_shading_rate")
429 };
430}
431
432QRhiVulkan::QRhiVulkan(QRhiVulkanInitParams *params, QRhiVulkanNativeHandles *importParams)
433 : ofr(this)
434{
435 inst = params->inst;
436 if (!inst) {
437 // This builds on the fact that Qt Quick also uses QVulkanDefaultInstance. While
438 // this way we can support a null inst, it has consequences, so only do it with a
439 // warning. (e.g. if Qt Quick initializes afterwards, its attempt to set flags on
440 // QVulkanDefaultInstance will be futile)
441 qWarning("QRhi for Vulkan attempted to be initialized without a QVulkanInstance; using QVulkanDefaultInstance.");
442 inst = QVulkanDefaultInstance::instance();
443 }
444
445 maybeWindow = params->window; // may be null
446 requestedDeviceExtensions = params->deviceExtensions;
447
448 if (importParams) {
449 physDev = importParams->physDev;
450 dev = importParams->dev;
451 if (dev && physDev) {
452 importedDevice = true;
453 gfxQueueFamilyIdx = importParams->gfxQueueFamilyIdx;
454 gfxQueueIdx = importParams->gfxQueueIdx;
455 // gfxQueue is output only, no point in accepting it as input
456 if (importParams->vmemAllocator) {
457 importedAllocator = true;
458 allocator = importParams->vmemAllocator;
459 }
460 }
461 }
462}
463
464static bool qvk_debug_filter(QVulkanInstance::DebugMessageSeverityFlags severity,
465 QVulkanInstance::DebugMessageTypeFlags type,
466 const void *callbackData)
467{
468 Q_UNUSED(severity);
469 Q_UNUSED(type);
470#ifdef VK_EXT_debug_utils
471 const VkDebugUtilsMessengerCallbackDataEXT *d = static_cast<const VkDebugUtilsMessengerCallbackDataEXT *>(callbackData);
472
473 // Filter out certain misleading validation layer messages, as per
474 // VulkanMemoryAllocator documentation.
475 if (strstr(d->pMessage, "Mapping an image with layout")
476 && strstr(d->pMessage, "can result in undefined behavior if this memory is used by the device"))
477 {
478 return true;
479 }
480
481 // In certain cases allocateDescriptorSet() will attempt to allocate from a
482 // pool that does not have enough descriptors of a certain type. This makes
483 // the validation layer shout. However, this is not an error since we will
484 // then move on to another pool. If there is a real error, a qWarning
485 // message is shown by allocateDescriptorSet(), so the validation warning
486 // does not have any value and is just noise.
487 if (strstr(d->pMessage, "VUID-VkDescriptorSetAllocateInfo-descriptorPool-00307"))
488 return true;
489#else
490 Q_UNUSED(callbackData);
491#endif
492 return false;
493}
494
495static inline QRhiDriverInfo::DeviceType toRhiDeviceType(VkPhysicalDeviceType type)
496{
497 switch (type) {
498 case VK_PHYSICAL_DEVICE_TYPE_OTHER:
499 return QRhiDriverInfo::UnknownDevice;
500 case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
501 return QRhiDriverInfo::IntegratedDevice;
502 case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
503 return QRhiDriverInfo::DiscreteDevice;
504 case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
505 return QRhiDriverInfo::VirtualDevice;
506 case VK_PHYSICAL_DEVICE_TYPE_CPU:
507 return QRhiDriverInfo::CpuDevice;
508 default:
509 return QRhiDriverInfo::UnknownDevice;
510 }
511}
512
513static inline void fillDriverInfo(QRhiDriverInfo *info, const VkPhysicalDeviceProperties &physDevProperties)
514{
515 info->deviceName = QByteArray(physDevProperties.deviceName);
516 info->deviceId = physDevProperties.deviceID;
517 info->vendorId = physDevProperties.vendorID;
518 info->deviceType = toRhiDeviceType(physDevProperties.deviceType);
519}
520
521template<typename T>
522static inline void addToChain(T *head, void *entry)
523{
524 VkBaseOutStructure *s = reinterpret_cast<VkBaseOutStructure *>(head);
525 for ( ; ; ) {
526 VkBaseOutStructure *next = reinterpret_cast<VkBaseOutStructure *>(s->pNext);
527 if (next)
528 s = next;
529 else
530 break;
531 }
532 s->pNext = reinterpret_cast<VkBaseOutStructure *>(entry);
533}
534
535bool QRhiVulkan::create(QRhi::Flags flags)
536{
537 Q_ASSERT(inst);
538 if (!inst->isValid()) {
539 qWarning("Vulkan instance is not valid");
540 return false;
541 }
542
543 rhiFlags = flags;
544 qCDebug(QRHI_LOG_INFO, "Initializing QRhi Vulkan backend %p with flags %d", this, int(rhiFlags));
545
546 globalVulkanInstance = inst; // used for function resolving in vkmemalloc callbacks
547 f = inst->functions();
548 if (QRHI_LOG_INFO().isEnabled(QtDebugMsg)) {
549 qCDebug(QRHI_LOG_INFO, "Enabled instance extensions:");
550 const QByteArrayList extensions = inst->extensions();
551 for (const char *ext : extensions)
552 qCDebug(QRHI_LOG_INFO, " %s", ext);
553 }
554
555 caps = {};
556 caps.debugUtils = inst->extensions().contains(QByteArrayLiteral("VK_EXT_debug_utils"));
557
558 QList<VkQueueFamilyProperties> queueFamilyProps;
559 auto queryQueueFamilyProps = [this, &queueFamilyProps] {
560 uint32_t queueCount = 0;
561 f->vkGetPhysicalDeviceQueueFamilyProperties(physDev, &queueCount, nullptr);
562 queueFamilyProps.resize(int(queueCount));
563 f->vkGetPhysicalDeviceQueueFamilyProperties(physDev, &queueCount, queueFamilyProps.data());
564 };
565
566 // Choose a physical device, unless one was provided in importParams.
567 if (!physDev) {
568 uint32_t physDevCount = 0;
569 f->vkEnumeratePhysicalDevices(inst->vkInstance(), &physDevCount, nullptr);
570 if (!physDevCount) {
571 qWarning("No physical devices");
572 return false;
573 }
574 QVarLengthArray<VkPhysicalDevice, 4> physDevs(physDevCount);
575 VkResult err = f->vkEnumeratePhysicalDevices(inst->vkInstance(), &physDevCount, physDevs.data());
576 if (err != VK_SUCCESS || !physDevCount) {
577 qWarning("Failed to enumerate physical devices: %d", err);
578 return false;
579 }
580
581 int physDevIndex = -1;
582 int requestedPhysDevIndex = -1;
583 if (qEnvironmentVariableIsSet("QT_VK_PHYSICAL_DEVICE_INDEX"))
584 requestedPhysDevIndex = qEnvironmentVariableIntValue("QT_VK_PHYSICAL_DEVICE_INDEX");
585
586 if (requestedPhysDevIndex < 0 && requestedRhiAdapter) {
587 VkPhysicalDevice requestedPhysDev = static_cast<QVulkanAdapter *>(requestedRhiAdapter)->physDev;
588 for (int i = 0; i < int(physDevCount); ++i) {
589 if (physDevs[i] == requestedPhysDev) {
590 requestedPhysDevIndex = i;
591 break;
592 }
593 }
594 }
595
596 if (requestedPhysDevIndex < 0 && flags.testFlag(QRhi::PreferSoftwareRenderer)) {
597 for (int i = 0; i < int(physDevCount); ++i) {
598 f->vkGetPhysicalDeviceProperties(physDevs[i], &physDevProperties);
599 if (physDevProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU) {
600 requestedPhysDevIndex = i;
601 break;
602 }
603 }
604 }
605
606 for (int i = 0; i < int(physDevCount); ++i) {
607 f->vkGetPhysicalDeviceProperties(physDevs[i], &physDevProperties);
608 qCDebug(QRHI_LOG_INFO, "Physical device %d: '%s' %d.%d.%d (api %d.%d.%d vendor 0x%X device 0x%X type %d)",
609 i,
610 physDevProperties.deviceName,
611 VK_VERSION_MAJOR(physDevProperties.driverVersion),
612 VK_VERSION_MINOR(physDevProperties.driverVersion),
613 VK_VERSION_PATCH(physDevProperties.driverVersion),
614 VK_VERSION_MAJOR(physDevProperties.apiVersion),
615 VK_VERSION_MINOR(physDevProperties.apiVersion),
616 VK_VERSION_PATCH(physDevProperties.apiVersion),
617 physDevProperties.vendorID,
618 physDevProperties.deviceID,
619 physDevProperties.deviceType);
620 if (physDevIndex < 0 && (requestedPhysDevIndex < 0 || requestedPhysDevIndex == int(i))) {
621 physDevIndex = i;
622 qCDebug(QRHI_LOG_INFO, " using this physical device");
623 }
624 }
625
626 if (physDevIndex < 0) {
627 qWarning("No matching physical device");
628 return false;
629 }
630 physDev = physDevs[physDevIndex];
631 f->vkGetPhysicalDeviceProperties(physDev, &physDevProperties);
632 } else {
633 f->vkGetPhysicalDeviceProperties(physDev, &physDevProperties);
634 qCDebug(QRHI_LOG_INFO, "Using imported physical device '%s' %d.%d.%d (api %d.%d.%d vendor 0x%X device 0x%X type %d)",
635 physDevProperties.deviceName,
636 VK_VERSION_MAJOR(physDevProperties.driverVersion),
637 VK_VERSION_MINOR(physDevProperties.driverVersion),
638 VK_VERSION_PATCH(physDevProperties.driverVersion),
639 VK_VERSION_MAJOR(physDevProperties.apiVersion),
640 VK_VERSION_MINOR(physDevProperties.apiVersion),
641 VK_VERSION_PATCH(physDevProperties.apiVersion),
642 physDevProperties.vendorID,
643 physDevProperties.deviceID,
644 physDevProperties.deviceType);
645 }
646
647 caps.apiVersion = inst->apiVersion();
648
649 // Check the physical device API version against the instance API version,
650 // they do not have to match, which means whatever version was set in the
651 // QVulkanInstance may not be legally used with a given device if the
652 // physical device has a lower version.
653 const QVersionNumber physDevApiVersion(VK_VERSION_MAJOR(physDevProperties.apiVersion),
654 VK_VERSION_MINOR(physDevProperties.apiVersion)); // patch version left out intentionally
655 if (physDevApiVersion < caps.apiVersion) {
656 qCDebug(QRHI_LOG_INFO) << "Instance has api version" << caps.apiVersion
657 << "whereas the chosen physical device has" << physDevApiVersion
658 << "- restricting to the latter";
659 caps.apiVersion = physDevApiVersion;
660 }
661
662 fillDriverInfo(&driverInfoStruct, physDevProperties);
663
664 QVulkanInfoVector<QVulkanExtension> devExts;
665 uint32_t devExtCount = 0;
666 f->vkEnumerateDeviceExtensionProperties(physDev, nullptr, &devExtCount, nullptr);
667 if (devExtCount) {
668 QList<VkExtensionProperties> extProps(devExtCount);
669 f->vkEnumerateDeviceExtensionProperties(physDev, nullptr, &devExtCount, extProps.data());
670 for (const VkExtensionProperties &p : std::as_const(extProps))
671 devExts.append({ p.extensionName, p.specVersion });
672 }
673 qCDebug(QRHI_LOG_INFO, "%d device extensions available", int(devExts.size()));
674
675 bool featuresQueried = false;
676#ifdef VK_VERSION_1_1
677 VkPhysicalDeviceFeatures2 physDevFeaturesChainable = {};
678 physDevFeaturesChainable.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
679
680 // Extensions (that are really extensions in 1.1-1.3, not core)
681#ifdef VK_KHR_fragment_shading_rate
682 VkPhysicalDeviceFragmentShadingRateFeaturesKHR fragmentShadingRateFeatures = {};
683 fragmentShadingRateFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_FEATURES_KHR;
684 if (devExts.contains("VK_KHR_fragment_shading_rate"))
685 addToChain(&physDevFeaturesChainable, &fragmentShadingRateFeatures);
686#endif
687#ifdef VK_EXT_device_fault
688 VkPhysicalDeviceFaultFeaturesEXT deviceFaultFeatures = {};
689 deviceFaultFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FAULT_FEATURES_EXT;
690 if (devExts.contains(VK_EXT_DEVICE_FAULT_EXTENSION_NAME))
691 addToChain(&physDevFeaturesChainable, &deviceFaultFeatures);
692#endif
693#endif
694
695 // Vulkan >=1.2 headers at build time, >=1.2 implementation at run time
696#ifdef VK_VERSION_1_2
697 if (!featuresQueried) {
698 // Vulkan11Features, Vulkan12Features, etc. are only in Vulkan 1.2 and newer.
699 if (caps.apiVersion >= QVersionNumber(1, 2)) {
700 physDevFeatures11IfApi12OrNewer = {};
701 physDevFeatures11IfApi12OrNewer.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES;
702 physDevFeatures12 = {};
703 physDevFeatures12.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;
704#ifdef VK_VERSION_1_3
705 physDevFeatures13 = {};
706 physDevFeatures13.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES;
707#ifdef VK_VERSION_1_4
708 physDevFeatures14 = {};
709 physDevFeatures14.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_4_FEATURES;
710#endif // VK_VERSION_1_4
711#endif // VK_VERSION_1_3
712 addToChain(&physDevFeaturesChainable, &physDevFeatures11IfApi12OrNewer);
713 physDevFeatures11IfApi12OrNewer.pNext = &physDevFeatures12;
714#ifdef VK_VERSION_1_3
715 if (caps.apiVersion >= QVersionNumber(1, 3))
716 physDevFeatures12.pNext = &physDevFeatures13;
717#ifdef VK_VERSION_1_4
718 if (caps.apiVersion >= QVersionNumber(1, 4))
719 physDevFeatures13.pNext = &physDevFeatures14;
720#endif // VK_VERSION_1_4
721#endif // VK_VERSION_1_3
722 f->vkGetPhysicalDeviceFeatures2(physDev, &physDevFeaturesChainable);
723 memcpy(&physDevFeatures, &physDevFeaturesChainable.features, sizeof(VkPhysicalDeviceFeatures));
724 featuresQueried = true;
725 }
726 }
727#endif // VK_VERSION_1_2
728
729 // Vulkan >=1.1 headers at build time, 1.1 implementation at run time
730#ifdef VK_VERSION_1_1
731 if (!featuresQueried) {
732 // Vulkan versioning nightmares: if the runtime API version is 1.1,
733 // there is no Vulkan11Features (introduced in 1.2+, the headers might
734 // have the types and structs, but the Vulkan implementation version at
735 // run time is what matters). But there are individual feature structs.
736 // For multiview, it is important to get this right since at the time of
737 // writing Quest 3 Android is a Vulkan 1.1 implementation at run time on
738 // the headset.
739 if (caps.apiVersion == QVersionNumber(1, 1)) {
740 {
741 multiviewFeaturesIfApi11 = {};
742 multiviewFeaturesIfApi11.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES;
743 addToChain(&physDevFeaturesChainable, &multiviewFeaturesIfApi11);
744 }
745 {
746 shaderDrawParametersFeaturesIfApi11 = {};
747 shaderDrawParametersFeaturesIfApi11.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES;
748 addToChain(&physDevFeaturesChainable, &shaderDrawParametersFeaturesIfApi11);
749 }
750 f->vkGetPhysicalDeviceFeatures2(physDev, &physDevFeaturesChainable);
751 memcpy(&physDevFeatures, &physDevFeaturesChainable.features, sizeof(VkPhysicalDeviceFeatures));
752 featuresQueried = true;
753 }
754 }
755#endif
756
757 if (!featuresQueried) {
758 // If the API version at run time is 1.0 (or we are building with
759 // ancient 1.0 headers), then do the Vulkan 1.0 query.
760 f->vkGetPhysicalDeviceFeatures(physDev, &physDevFeatures);
761 featuresQueried = true;
762 }
763
764 // Choose queue and create device, unless the device was specified in importParams.
765 if (!importedDevice) {
766 // We only support combined graphics+present queues. When it comes to
767 // compute, only combined graphics+compute queue is used, compute gets
768 // disabled otherwise.
769 std::optional<uint32_t> gfxQueueFamilyIdxOpt;
770 std::optional<uint32_t> computelessGfxQueueCandidateIdxOpt;
771 queryQueueFamilyProps();
772 const uint32_t queueFamilyCount = uint32_t(queueFamilyProps.size());
773 for (uint32_t i = 0; i < queueFamilyCount; ++i) {
774 qCDebug(QRHI_LOG_INFO, "queue family %u: flags=0x%x count=%u",
775 i, queueFamilyProps[i].queueFlags, queueFamilyProps[i].queueCount);
776 if (!gfxQueueFamilyIdxOpt.has_value()
777 && (queueFamilyProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
778 && (!maybeWindow || inst->supportsPresent(physDev, i, maybeWindow)))
779 {
780 if (queueFamilyProps[i].queueFlags & VK_QUEUE_COMPUTE_BIT)
781 gfxQueueFamilyIdxOpt = i;
782 else if (!computelessGfxQueueCandidateIdxOpt.has_value())
783 computelessGfxQueueCandidateIdxOpt = i;
784 }
785 }
786 if (gfxQueueFamilyIdxOpt.has_value()) {
787 gfxQueueFamilyIdx = gfxQueueFamilyIdxOpt.value();
788 } else {
789 if (computelessGfxQueueCandidateIdxOpt.has_value()) {
790 gfxQueueFamilyIdx = computelessGfxQueueCandidateIdxOpt.value();
791 } else {
792 qWarning("No graphics (or no graphics+present) queue family found");
793 return false;
794 }
795 }
796
797 VkDeviceQueueCreateInfo queueInfo = {};
798 const float prio[] = { 0 };
799 queueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
800 queueInfo.queueFamilyIndex = gfxQueueFamilyIdx;
801 queueInfo.queueCount = 1;
802 queueInfo.pQueuePriorities = prio;
803
804 QList<const char *> devLayers;
805 if (inst->layers().contains("VK_LAYER_KHRONOS_validation"))
806 devLayers.append("VK_LAYER_KHRONOS_validation");
807
808 QList<const char *> requestedDevExts;
809 requestedDevExts.append("VK_KHR_swapchain");
810
811 const bool hasPhysDevProp2 = inst->extensions().contains(QByteArrayLiteral("VK_KHR_get_physical_device_properties2"));
812
813 if (devExts.contains(QByteArrayLiteral("VK_KHR_portability_subset"))) {
814 if (hasPhysDevProp2) {
815 requestedDevExts.append("VK_KHR_portability_subset");
816 } else {
817 qWarning("VK_KHR_portability_subset should be enabled on the device "
818 "but the instance does not have VK_KHR_get_physical_device_properties2 enabled. "
819 "Expect problems.");
820 }
821 }
822
823#ifdef VK_EXT_vertex_attribute_divisor
824 if (devExts.contains(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME)) {
825 if (hasPhysDevProp2) {
826 requestedDevExts.append(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME);
827 caps.vertexAttribDivisor = true;
828 }
829 }
830#endif
831
832#ifdef VK_KHR_create_renderpass2
833 if (devExts.contains(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME)) {
834 requestedDevExts.append(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME);
835 caps.renderPass2KHR = true;
836 }
837#endif
838
839#ifdef VK_KHR_depth_stencil_resolve
840 if (devExts.contains(VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME)) {
841 requestedDevExts.append(VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME);
842 caps.depthStencilResolveKHR = true;
843 }
844#endif
845
846#ifdef VK_KHR_fragment_shading_rate
847 if (devExts.contains(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME))
848 requestedDevExts.append(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME);
849#endif
850
851#ifdef VK_EXT_device_fault
852 if (devExts.contains(VK_EXT_DEVICE_FAULT_EXTENSION_NAME)) {
853 requestedDevExts.append(VK_EXT_DEVICE_FAULT_EXTENSION_NAME);
854 caps.deviceFault = true;
855 }
856#endif
857
858 for (const QByteArray &ext : std::as_const(requestedDeviceExtensions)) {
859 if (!ext.isEmpty() && !requestedDevExts.contains(ext)) {
860 if (devExts.contains(ext)) {
861 requestedDevExts.append(ext.constData());
862 } else {
863 qWarning("Device extension %s requested in QRhiVulkanInitParams is not supported",
864 ext.constData());
865 }
866 }
867 }
868
869 const QByteArrayList envExtList = qgetenv("QT_VULKAN_DEVICE_EXTENSIONS").split(';');
870 for (const QByteArray &ext : envExtList) {
871 if (!ext.isEmpty() && !requestedDevExts.contains(ext)) {
872 if (devExts.contains(ext)) {
873 requestedDevExts.append(ext.constData());
874 } else {
875 qWarning("Device extension %s requested in QT_VULKAN_DEVICE_EXTENSIONS is not supported",
876 ext.constData());
877 }
878 }
879 }
880
881 if (QRHI_LOG_INFO().isEnabled(QtDebugMsg)) {
882 qCDebug(QRHI_LOG_INFO, "Enabling device extensions:");
883 for (const char *ext : std::as_const(requestedDevExts))
884 qCDebug(QRHI_LOG_INFO, " %s", ext);
885 }
886
887 VkDeviceCreateInfo devInfo = {};
888 devInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
889 devInfo.queueCreateInfoCount = 1;
890 devInfo.pQueueCreateInfos = &queueInfo;
891 devInfo.enabledLayerCount = uint32_t(devLayers.size());
892 devInfo.ppEnabledLayerNames = devLayers.constData();
893 devInfo.enabledExtensionCount = uint32_t(requestedDevExts.size());
894 devInfo.ppEnabledExtensionNames = requestedDevExts.constData();
895
896 // Enable all features that are reported as supported, except
897 // robustness because that potentially affects performance.
898 //
899 // Enabling all features mainly serves third-party renderers that may
900 // use the VkDevice created here. For the record, the backend here
901 // optionally relies on the following features, meaning just for our
902 // (QRhi/Quick/Quick 3D) purposes it would be sufficient to
903 // enable-if-supported only the following:
904 //
905 // wideLines, largePoints, fillModeNonSolid,
906 // tessellationShader, geometryShader
907 // textureCompressionETC2, textureCompressionASTC_LDR, textureCompressionBC
908
909#ifdef VK_VERSION_1_1
910 physDevFeaturesChainable.features.robustBufferAccess = VK_FALSE;
911#endif
912#ifdef VK_VERSION_1_3
913 physDevFeatures13.robustImageAccess = VK_FALSE;
914#endif
915
916#ifdef VK_VERSION_1_1
917 if (caps.apiVersion >= QVersionNumber(1, 1)) {
918 // For a >=1.2 implementation at run time, this will enable all
919 // (1.0-1.4) features reported as supported, except the ones we turn
920 // off explicitly above. (+extensions) For a 1.1 implementation at
921 // run time, this only enables the 1.0 and multiview features (+any
922 // extensions) reported as supported. We will not be bothering with
923 // the Vulkan 1.1 individual feature struct nonsense.
924 devInfo.pNext = &physDevFeaturesChainable;
925 } else
926#endif
927 {
928 physDevFeatures.robustBufferAccess = VK_FALSE;
929 devInfo.pEnabledFeatures = &physDevFeatures;
930 }
931
932 VkResult err = f->vkCreateDevice(physDev, &devInfo, nullptr, &dev);
933 if (err != VK_SUCCESS) {
934 qWarning("Failed to create device: %d", err);
935 return false;
936 }
937 } else {
938 qCDebug(QRHI_LOG_INFO, "Using imported device %p", dev);
939
940 // Here we have no way to tell if the extensions got enabled or not.
941 // Pretend it's all there and supported. If getProcAddress fails, we'll
942 // handle that gracefully.
943 caps.deviceFault = true;
944 caps.vertexAttribDivisor = true;
945 caps.renderPass2KHR = true;
946 caps.depthStencilResolveKHR = true;
947 }
948
949 vkGetPhysicalDeviceSurfaceCapabilitiesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR>(
950 inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfaceCapabilitiesKHR"));
951 vkGetPhysicalDeviceSurfaceFormatsKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceFormatsKHR>(
952 inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfaceFormatsKHR"));
953 vkGetPhysicalDeviceSurfacePresentModesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfacePresentModesKHR>(
954 inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfacePresentModesKHR"));
955
956 df = inst->deviceFunctions(dev);
957
958 VkCommandPoolCreateInfo poolInfo = {};
959 poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
960 poolInfo.queueFamilyIndex = gfxQueueFamilyIdx;
961 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
962 VkResult err = df->vkCreateCommandPool(dev, &poolInfo, nullptr, &cmdPool[i]);
963 if (err != VK_SUCCESS) {
964 qWarning("Failed to create command pool: %d", err);
965 return false;
966 }
967 }
968
969 qCDebug(QRHI_LOG_INFO, "Using queue family index %u and queue index %u",
970 gfxQueueFamilyIdx, gfxQueueIdx);
971
972 df->vkGetDeviceQueue(dev, gfxQueueFamilyIdx, gfxQueueIdx, &gfxQueue);
973
974 if (queueFamilyProps.isEmpty())
975 queryQueueFamilyProps();
976
977 caps.compute = (queueFamilyProps[gfxQueueFamilyIdx].queueFlags & VK_QUEUE_COMPUTE_BIT) != 0;
978 timestampValidBits = queueFamilyProps[gfxQueueFamilyIdx].timestampValidBits;
979
980 ubufAlign = physDevProperties.limits.minUniformBufferOffsetAlignment;
981 // helps little with an optimal offset of 1 (on some drivers) when the spec
982 // elsewhere states that the minimum bufferOffset is 4...
983 texbufAlign = qMax<VkDeviceSize>(4, physDevProperties.limits.optimalBufferCopyOffsetAlignment);
984
985 caps.depthClamp = physDevFeatures.depthClamp;
986
987 caps.wideLines = physDevFeatures.wideLines;
988
989 caps.texture3DSliceAs2D = caps.apiVersion >= QVersionNumber(1, 1);
990
991 caps.tessellation = physDevFeatures.tessellationShader;
992 caps.geometryShader = physDevFeatures.geometryShader;
993
994 caps.nonFillPolygonMode = physDevFeatures.fillModeNonSolid;
995
996 caps.drawIndirectMulti = physDevFeatures.multiDrawIndirect;
997
998#ifdef VK_VERSION_1_2
999 if (caps.apiVersion >= QVersionNumber(1, 2)) {
1000 caps.multiView = physDevFeatures11IfApi12OrNewer.multiview;
1001 caps.shaderDrawParameters = physDevFeatures11IfApi12OrNewer.shaderDrawParameters;
1002 }
1003#endif
1004
1005#ifdef VK_VERSION_1_1
1006 if (caps.apiVersion == QVersionNumber(1, 1)) {
1007 caps.multiView = multiviewFeaturesIfApi11.multiview;
1008 caps.shaderDrawParameters = shaderDrawParametersFeaturesIfApi11.shaderDrawParameters;
1009 }
1010#endif
1011
1012#ifdef VK_KHR_fragment_shading_rate
1013 fragmentShadingRates.clear();
1014 if (caps.apiVersion >= QVersionNumber(1, 1)) {
1015 caps.perDrawShadingRate = fragmentShadingRateFeatures.pipelineFragmentShadingRate;
1016 caps.imageBasedShadingRate = fragmentShadingRateFeatures.attachmentFragmentShadingRate;
1017 if (caps.imageBasedShadingRate) {
1018 VkPhysicalDeviceFragmentShadingRatePropertiesKHR shadingRateProps = {};
1019 shadingRateProps.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_PROPERTIES_KHR;
1020 VkPhysicalDeviceProperties2 props2 = {};
1021 props2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
1022 props2.pNext = &shadingRateProps;
1023 f->vkGetPhysicalDeviceProperties2(physDev, &props2);
1024 caps.imageBasedShadingRateTileSize = int(shadingRateProps.maxFragmentShadingRateAttachmentTexelSize.width);
1025 // If it's non-square, there's nothing we can do since it is not compatible with other APIs (D3D12) then.
1026 }
1027 if (caps.perDrawShadingRate) {
1028 PFN_vkGetPhysicalDeviceFragmentShadingRatesKHR vkGetPhysicalDeviceFragmentShadingRatesKHR =
1029 reinterpret_cast<PFN_vkGetPhysicalDeviceFragmentShadingRatesKHR>(
1030 inst->getInstanceProcAddr("vkGetPhysicalDeviceFragmentShadingRatesKHR"));
1031 if (vkGetPhysicalDeviceFragmentShadingRatesKHR) {
1032 uint32_t count = 0;
1033 vkGetPhysicalDeviceFragmentShadingRatesKHR(physDev, &count, nullptr);
1034 fragmentShadingRates.resize(count);
1035 for (VkPhysicalDeviceFragmentShadingRateKHR &s : fragmentShadingRates) {
1036 s = {};
1037 s.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_KHR;
1038 }
1039 vkGetPhysicalDeviceFragmentShadingRatesKHR(physDev, &count, fragmentShadingRates.data());
1040 }
1041 vkCmdSetFragmentShadingRateKHR = reinterpret_cast<PFN_vkCmdSetFragmentShadingRateKHR>(
1042 f->vkGetDeviceProcAddr(dev, "vkCmdSetFragmentShadingRateKHR"));
1043 }
1044 }
1045#endif
1046
1047 // With Vulkan 1.2 renderpass2 and depth_stencil_resolve are core, but we
1048 // have to support the case of 1.1 + extensions, in particular for the Quest
1049 // 3 (Android, Vulkan 1.1 at the time of writing). Therefore, always rely on
1050 // the KHR extension for now.
1051#ifdef VK_KHR_create_renderpass2
1052 if (caps.renderPass2KHR) {
1053 vkCreateRenderPass2KHR = reinterpret_cast<PFN_vkCreateRenderPass2KHR>(f->vkGetDeviceProcAddr(dev, "vkCreateRenderPass2KHR"));
1054 if (!vkCreateRenderPass2KHR) // handle it gracefully, the caps flag may be incorrect when using an imported VkDevice
1055 caps.renderPass2KHR = false;
1056 }
1057#endif
1058
1059 // On Windows, figure out the DXGI adapter LUID.
1060#ifdef Q_OS_WIN
1061 adapterLuidValid = false;
1062 adapterLuid = {};
1063#ifdef VK_VERSION_1_2
1064 if (caps.apiVersion >= QVersionNumber(1, 2)) {
1065 VkPhysicalDeviceVulkan11Properties v11props = {};
1066 v11props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES;
1067 VkPhysicalDeviceProperties2 props2 = {};
1068 props2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
1069 props2.pNext = &v11props;
1070 f->vkGetPhysicalDeviceProperties2(physDev, &props2);
1071 if (v11props.deviceLUIDValid) {
1072 const LUID *luid = reinterpret_cast<const LUID *>(v11props.deviceLUID);
1073 memcpy(&adapterLuid, luid, VK_LUID_SIZE);
1074 adapterLuidValid = true;
1075 dxgiHdrInfo = new QDxgiHdrInfo(adapterLuid);
1076 qCDebug(QRHI_LOG_INFO, "DXGI adapter LUID for physical device is %lu, %lu",
1077 adapterLuid.LowPart, adapterLuid.HighPart);
1078 }
1079 }
1080#endif
1081#endif
1082
1083 if (!importedAllocator) {
1084 VmaVulkanFunctions funcs = {};
1085 funcs.vkGetInstanceProcAddr = wrap_vkGetInstanceProcAddr;
1086 funcs.vkGetDeviceProcAddr = wrap_vkGetDeviceProcAddr;
1087
1088 VmaAllocatorCreateInfo allocatorInfo = {};
1089 // A QRhi is supposed to be used from one single thread only. Disable
1090 // the allocator's own mutexes. This gives a performance boost.
1091 allocatorInfo.flags = VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT;
1092 allocatorInfo.physicalDevice = physDev;
1093 allocatorInfo.device = dev;
1094 allocatorInfo.pVulkanFunctions = &funcs;
1095 allocatorInfo.instance = inst->vkInstance();
1096
1097 // Logic would dictate setting allocatorInfo.vulkanApiVersion to caps.apiVersion.
1098 // However, VMA has asserts to test if the header version Qt was built with is
1099 // older than the runtime version. This is nice, but a bit unnecessary (in Qt we'd
1100 // rather prefer losing the affected features automatically, and perhaps printing
1101 // a warning, instead of aborting the application). Restrict the runtime version
1102 // passed in based on the preprocessor macro to keep VMA happy.
1103#ifdef VK_VERSION_1_4
1104 if (caps.apiVersion >= QVersionNumber(1, 4))
1105 allocatorInfo.vulkanApiVersion = VK_API_VERSION_1_4;
1106 else
1107#endif
1108#ifdef VK_VERSION_1_3
1109 if (caps.apiVersion >= QVersionNumber(1, 3))
1110 allocatorInfo.vulkanApiVersion = VK_API_VERSION_1_3;
1111 else
1112#endif
1113#ifdef VK_VERSION_1_2
1114 if (caps.apiVersion >= QVersionNumber(1, 2))
1115 allocatorInfo.vulkanApiVersion = VK_API_VERSION_1_2;
1116 else
1117#endif
1118#ifdef VK_VERSION_1_1
1119 if (caps.apiVersion >= QVersionNumber(1, 1))
1120 allocatorInfo.vulkanApiVersion = VK_API_VERSION_1_1;
1121 else
1122#endif
1123#ifdef VK_VERSION_1_0
1124 allocatorInfo.vulkanApiVersion = VK_API_VERSION_1_0;
1125#endif
1126
1127 VmaAllocator vmaallocator;
1128 VkResult err = vmaCreateAllocator(&allocatorInfo, &vmaallocator);
1129 if (err != VK_SUCCESS) {
1130 qWarning("Failed to create allocator: %d", err);
1131 return false;
1132 }
1133 allocator = vmaallocator;
1134 }
1135
1136 inst->installDebugOutputFilter(qvk_debug_filter);
1137
1138 VkDescriptorPool pool;
1139 VkResult err = createDescriptorPool(&pool);
1140 if (err == VK_SUCCESS)
1141 descriptorPools.append(pool);
1142 else
1143 qWarning("Failed to create initial descriptor pool: %d", err);
1144
1145 VkQueryPoolCreateInfo timestampQueryPoolInfo = {};
1146 timestampQueryPoolInfo.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
1147 timestampQueryPoolInfo.queryType = VK_QUERY_TYPE_TIMESTAMP;
1148 timestampQueryPoolInfo.queryCount = QVK_MAX_ACTIVE_TIMESTAMP_PAIRS * 2;
1149 err = df->vkCreateQueryPool(dev, &timestampQueryPoolInfo, nullptr, &timestampQueryPool);
1150 if (err != VK_SUCCESS) {
1151 qWarning("Failed to create timestamp query pool: %d", err);
1152 return false;
1153 }
1154 timestampQueryPoolMap.resize(QVK_MAX_ACTIVE_TIMESTAMP_PAIRS); // 1 bit per pair
1155 timestampQueryPoolMap.fill(false);
1156
1157#ifdef VK_EXT_debug_utils
1158 if (caps.debugUtils) {
1159 vkSetDebugUtilsObjectNameEXT = reinterpret_cast<PFN_vkSetDebugUtilsObjectNameEXT>(f->vkGetDeviceProcAddr(dev, "vkSetDebugUtilsObjectNameEXT"));
1160 vkCmdBeginDebugUtilsLabelEXT = reinterpret_cast<PFN_vkCmdBeginDebugUtilsLabelEXT>(f->vkGetDeviceProcAddr(dev, "vkCmdBeginDebugUtilsLabelEXT"));
1161 vkCmdEndDebugUtilsLabelEXT = reinterpret_cast<PFN_vkCmdEndDebugUtilsLabelEXT>(f->vkGetDeviceProcAddr(dev, "vkCmdEndDebugUtilsLabelEXT"));
1162 vkCmdInsertDebugUtilsLabelEXT = reinterpret_cast<PFN_vkCmdInsertDebugUtilsLabelEXT>(f->vkGetDeviceProcAddr(dev, "vkCmdInsertDebugUtilsLabelEXT"));
1163 }
1164#endif
1165
1166#ifdef VK_EXT_device_fault
1167 if (caps.deviceFault) {
1168 vkGetDeviceFaultInfoEXT = reinterpret_cast<PFN_vkGetDeviceFaultInfoEXT>(f->vkGetDeviceProcAddr(dev, "vkGetDeviceFaultInfoEXT"));
1169 }
1170#endif
1171
1172 deviceLost = false;
1173
1174 nativeHandlesStruct.physDev = physDev;
1175 nativeHandlesStruct.dev = dev;
1176 nativeHandlesStruct.gfxQueueFamilyIdx = gfxQueueFamilyIdx;
1177 nativeHandlesStruct.gfxQueueIdx = gfxQueueIdx;
1178 nativeHandlesStruct.gfxQueue = gfxQueue;
1179 nativeHandlesStruct.vmemAllocator = allocator;
1180 nativeHandlesStruct.inst = inst;
1181
1182 return true;
1183}
1184
1186{
1187 if (!df)
1188 return;
1189
1190 if (!deviceLost)
1191 df->vkDeviceWaitIdle(dev);
1192
1195
1196#ifdef Q_OS_WIN
1197 delete dxgiHdrInfo;
1198 dxgiHdrInfo = nullptr;
1199#endif
1200
1201 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
1202 if (ofr.cmdFence[i]) {
1203 df->vkDestroyFence(dev, ofr.cmdFence[i], nullptr);
1204 ofr.cmdFence[i] = VK_NULL_HANDLE;
1205 }
1206 ofr.cmdFenceWaitable[i] = false;
1207 }
1208
1209 if (pipelineCache) {
1210 df->vkDestroyPipelineCache(dev, pipelineCache, nullptr);
1211 pipelineCache = VK_NULL_HANDLE;
1212 }
1213
1214 for (const DescriptorPoolData &pool : descriptorPools)
1215 df->vkDestroyDescriptorPool(dev, pool.pool, nullptr);
1216
1217 descriptorPools.clear();
1218
1219 if (timestampQueryPool) {
1220 df->vkDestroyQueryPool(dev, timestampQueryPool, nullptr);
1221 timestampQueryPool = VK_NULL_HANDLE;
1222 }
1223
1224 if (!importedAllocator && allocator) {
1225 vmaDestroyAllocator(toVmaAllocator(allocator));
1226 allocator = nullptr;
1227 }
1228
1229 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
1230 if (cmdPool[i]) {
1231 df->vkDestroyCommandPool(dev, cmdPool[i], nullptr);
1232 cmdPool[i] = VK_NULL_HANDLE;
1233 }
1234 freeSecondaryCbs[i].clear();
1235 ofr.cbWrapper[i]->cb = VK_NULL_HANDLE;
1236 }
1237
1238 if (!importedDevice && dev) {
1239 df->vkDestroyDevice(dev, nullptr);
1240 inst->resetDeviceFunctions(dev);
1241 dev = VK_NULL_HANDLE;
1242 }
1243
1244 f = nullptr;
1245 df = nullptr;
1246
1247 importedDevice = false;
1248 importedAllocator = false;
1249}
1250
1251QRhi::AdapterList QRhiVulkan::enumerateAdaptersBeforeCreate(QRhiNativeHandles *nativeHandles) const
1252{
1253 VkPhysicalDevice requestedPhysDev = VK_NULL_HANDLE;
1254 if (nativeHandles) {
1255 QRhiVulkanNativeHandles *h = static_cast<QRhiVulkanNativeHandles *>(nativeHandles);
1256 requestedPhysDev = h->physDev;
1257 }
1258
1259 QRhi::AdapterList list;
1260 QVulkanFunctions *f = inst->functions();
1261 uint32_t physDevCount = 0;
1262 f->vkEnumeratePhysicalDevices(inst->vkInstance(), &physDevCount, nullptr);
1263 if (!physDevCount)
1264 return {};
1265
1266 QVarLengthArray<VkPhysicalDevice, 4> physDevs(physDevCount);
1267 VkResult err = f->vkEnumeratePhysicalDevices(inst->vkInstance(), &physDevCount, physDevs.data());
1268 if (err != VK_SUCCESS || !physDevCount)
1269 return {};
1270
1271 VkPhysicalDeviceProperties physDevProperties = {};
1272 for (uint32_t i = 0; i < physDevCount; ++i) {
1273 if (requestedPhysDev && physDevs[i] != requestedPhysDev)
1274 continue;
1275
1276 f->vkGetPhysicalDeviceProperties(physDevs[i], &physDevProperties);
1277 QVulkanAdapter *a = new QVulkanAdapter;
1278 a->physDev = physDevs[i];
1279 fillDriverInfo(&a->adapterInfo, physDevProperties);
1280 list.append(a);
1281 }
1282
1283 return list;
1284}
1285
1287{
1288 return adapterInfo;
1289}
1290
1292{
1293 VkDescriptorPoolSize descPoolSizes[] = {
1294 { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, QVK_UNIFORM_BUFFERS_PER_POOL },
1295 { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, QVK_UNIFORM_BUFFERS_PER_POOL },
1296 { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, QVK_COMBINED_IMAGE_SAMPLERS_PER_POOL },
1297 { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, QVK_SAMPLED_IMAGES_PER_POOL },
1298 { VK_DESCRIPTOR_TYPE_SAMPLER, QVK_SAMPLERS_PER_POOL },
1299 { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, QVK_STORAGE_BUFFERS_PER_POOL },
1300 { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, QVK_STORAGE_IMAGES_PER_POOL }
1301 };
1302 VkDescriptorPoolCreateInfo descPoolInfo = {};
1303 descPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
1304 // Do not enable vkFreeDescriptorSets - sets are never freed on their own
1305 // (good so no trouble with fragmentation), they just deref their pool
1306 // which is then reset at some point (or not).
1307 descPoolInfo.flags = 0;
1308 descPoolInfo.maxSets = QVK_DESC_SETS_PER_POOL;
1309 descPoolInfo.poolSizeCount = sizeof(descPoolSizes) / sizeof(descPoolSizes[0]);
1310 descPoolInfo.pPoolSizes = descPoolSizes;
1311 return df->vkCreateDescriptorPool(dev, &descPoolInfo, nullptr, pool);
1312}
1313
1314bool QRhiVulkan::allocateDescriptorSet(VkDescriptorSetAllocateInfo *allocInfo, VkDescriptorSet *result, int *resultPoolIndex)
1315{
1316 auto tryAllocate = [this, allocInfo, result](int poolIndex) {
1317 allocInfo->descriptorPool = descriptorPools[poolIndex].pool;
1318 VkResult r = df->vkAllocateDescriptorSets(dev, allocInfo, result);
1319 if (r == VK_SUCCESS)
1320 descriptorPools[poolIndex].refCount += 1;
1321 return r;
1322 };
1323
1324 int lastPoolIdx = descriptorPools.size() - 1;
1325 for (int i = lastPoolIdx; i >= 0; --i) {
1326 if (descriptorPools[i].refCount == 0) {
1327 df->vkResetDescriptorPool(dev, descriptorPools[i].pool, 0);
1328 descriptorPools[i].allocedDescSets = 0;
1329 }
1330 if (descriptorPools[i].allocedDescSets + int(allocInfo->descriptorSetCount) <= QVK_DESC_SETS_PER_POOL) {
1331 VkResult err = tryAllocate(i);
1332 if (err == VK_SUCCESS) {
1333 descriptorPools[i].allocedDescSets += allocInfo->descriptorSetCount;
1334 *resultPoolIndex = i;
1335 return true;
1336 }
1337 }
1338 }
1339
1340 VkDescriptorPool newPool;
1341 VkResult poolErr = createDescriptorPool(&newPool);
1342 if (poolErr == VK_SUCCESS) {
1343 descriptorPools.append(newPool);
1344 lastPoolIdx = descriptorPools.size() - 1;
1345 VkResult err = tryAllocate(lastPoolIdx);
1346 if (err != VK_SUCCESS) {
1347 qWarning("Failed to allocate descriptor set from new pool too, giving up: %d", err);
1348 return false;
1349 }
1350 descriptorPools[lastPoolIdx].allocedDescSets += allocInfo->descriptorSetCount;
1351 *resultPoolIndex = lastPoolIdx;
1352 return true;
1353 } else {
1354 qWarning("Failed to allocate new descriptor pool: %d", poolErr);
1355 return false;
1356 }
1357}
1358
1359static inline VkFormat toVkTextureFormat(QRhiTexture::Format format, QRhiTexture::Flags flags)
1360{
1361 const bool srgb = flags.testFlag(QRhiTexture::sRGB);
1362 switch (format) {
1363 case QRhiTexture::RGBA8:
1364 return srgb ? VK_FORMAT_R8G8B8A8_SRGB : VK_FORMAT_R8G8B8A8_UNORM;
1365 case QRhiTexture::BGRA8:
1366 return srgb ? VK_FORMAT_B8G8R8A8_SRGB : VK_FORMAT_B8G8R8A8_UNORM;
1367 case QRhiTexture::R8:
1368 return srgb ? VK_FORMAT_R8_SRGB : VK_FORMAT_R8_UNORM;
1369 case QRhiTexture::RG8:
1370 return srgb ? VK_FORMAT_R8G8_SRGB : VK_FORMAT_R8G8_UNORM;
1371 case QRhiTexture::R16:
1372 return VK_FORMAT_R16_UNORM;
1373 case QRhiTexture::RG16:
1374 return VK_FORMAT_R16G16_UNORM;
1375 case QRhiTexture::RED_OR_ALPHA8:
1376 return VK_FORMAT_R8_UNORM;
1377
1378 case QRhiTexture::RGBA16F:
1379 return VK_FORMAT_R16G16B16A16_SFLOAT;
1380 case QRhiTexture::RGBA32F:
1381 return VK_FORMAT_R32G32B32A32_SFLOAT;
1382 case QRhiTexture::R16F:
1383 return VK_FORMAT_R16_SFLOAT;
1384 case QRhiTexture::R32F:
1385 return VK_FORMAT_R32_SFLOAT;
1386
1387 case QRhiTexture::RGB10A2:
1388 // intentionally A2B10G10R10, not A2R10G10B10
1389 return VK_FORMAT_A2B10G10R10_UNORM_PACK32;
1390
1391 case QRhiTexture::R8SI:
1392 return VK_FORMAT_R8_SINT;
1393 case QRhiTexture::R32SI:
1394 return VK_FORMAT_R32_SINT;
1395 case QRhiTexture::RG32SI:
1396 return VK_FORMAT_R32G32_SINT;
1397 case QRhiTexture::RGBA32SI:
1398 return VK_FORMAT_R32G32B32A32_SINT;
1399
1400 case QRhiTexture::R8UI:
1401 return VK_FORMAT_R8_UINT;
1402 case QRhiTexture::R32UI:
1403 return VK_FORMAT_R32_UINT;
1404 case QRhiTexture::RG32UI:
1405 return VK_FORMAT_R32G32_UINT;
1406 case QRhiTexture::RGBA32UI:
1407 return VK_FORMAT_R32G32B32A32_UINT;
1408
1409 case QRhiTexture::D16:
1410 return VK_FORMAT_D16_UNORM;
1411 case QRhiTexture::D24:
1412 return VK_FORMAT_X8_D24_UNORM_PACK32;
1413 case QRhiTexture::D24S8:
1414 return VK_FORMAT_D24_UNORM_S8_UINT;
1415 case QRhiTexture::D32F:
1416 return VK_FORMAT_D32_SFLOAT;
1417 case QRhiTexture::D32FS8:
1418 return VK_FORMAT_D32_SFLOAT_S8_UINT;
1419
1420 case QRhiTexture::BC1:
1421 return srgb ? VK_FORMAT_BC1_RGB_SRGB_BLOCK : VK_FORMAT_BC1_RGB_UNORM_BLOCK;
1422 case QRhiTexture::BC2:
1423 return srgb ? VK_FORMAT_BC2_SRGB_BLOCK : VK_FORMAT_BC2_UNORM_BLOCK;
1424 case QRhiTexture::BC3:
1425 return srgb ? VK_FORMAT_BC3_SRGB_BLOCK : VK_FORMAT_BC3_UNORM_BLOCK;
1426 case QRhiTexture::BC4:
1427 return VK_FORMAT_BC4_UNORM_BLOCK;
1428 case QRhiTexture::BC5:
1429 return VK_FORMAT_BC5_UNORM_BLOCK;
1430 case QRhiTexture::BC6H:
1431 return VK_FORMAT_BC6H_UFLOAT_BLOCK;
1432 case QRhiTexture::BC7:
1433 return srgb ? VK_FORMAT_BC7_SRGB_BLOCK : VK_FORMAT_BC7_UNORM_BLOCK;
1434
1435 case QRhiTexture::ETC2_RGB8:
1436 return srgb ? VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK : VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK;
1437 case QRhiTexture::ETC2_RGB8A1:
1438 return srgb ? VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK : VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK;
1439 case QRhiTexture::ETC2_RGBA8:
1440 return srgb ? VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK : VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK;
1441
1442 case QRhiTexture::ASTC_4x4:
1443 return srgb ? VK_FORMAT_ASTC_4x4_SRGB_BLOCK : VK_FORMAT_ASTC_4x4_UNORM_BLOCK;
1444 case QRhiTexture::ASTC_5x4:
1445 return srgb ? VK_FORMAT_ASTC_5x4_SRGB_BLOCK : VK_FORMAT_ASTC_5x4_UNORM_BLOCK;
1446 case QRhiTexture::ASTC_5x5:
1447 return srgb ? VK_FORMAT_ASTC_5x5_SRGB_BLOCK : VK_FORMAT_ASTC_5x5_UNORM_BLOCK;
1448 case QRhiTexture::ASTC_6x5:
1449 return srgb ? VK_FORMAT_ASTC_6x5_SRGB_BLOCK : VK_FORMAT_ASTC_6x5_UNORM_BLOCK;
1450 case QRhiTexture::ASTC_6x6:
1451 return srgb ? VK_FORMAT_ASTC_6x6_SRGB_BLOCK : VK_FORMAT_ASTC_6x6_UNORM_BLOCK;
1452 case QRhiTexture::ASTC_8x5:
1453 return srgb ? VK_FORMAT_ASTC_8x5_SRGB_BLOCK : VK_FORMAT_ASTC_8x5_UNORM_BLOCK;
1454 case QRhiTexture::ASTC_8x6:
1455 return srgb ? VK_FORMAT_ASTC_8x6_SRGB_BLOCK : VK_FORMAT_ASTC_8x6_UNORM_BLOCK;
1456 case QRhiTexture::ASTC_8x8:
1457 return srgb ? VK_FORMAT_ASTC_8x8_SRGB_BLOCK : VK_FORMAT_ASTC_8x8_UNORM_BLOCK;
1458 case QRhiTexture::ASTC_10x5:
1459 return srgb ? VK_FORMAT_ASTC_10x5_SRGB_BLOCK : VK_FORMAT_ASTC_10x5_UNORM_BLOCK;
1460 case QRhiTexture::ASTC_10x6:
1461 return srgb ? VK_FORMAT_ASTC_10x6_SRGB_BLOCK : VK_FORMAT_ASTC_10x6_UNORM_BLOCK;
1462 case QRhiTexture::ASTC_10x8:
1463 return srgb ? VK_FORMAT_ASTC_10x8_SRGB_BLOCK : VK_FORMAT_ASTC_10x8_UNORM_BLOCK;
1464 case QRhiTexture::ASTC_10x10:
1465 return srgb ? VK_FORMAT_ASTC_10x10_SRGB_BLOCK : VK_FORMAT_ASTC_10x10_UNORM_BLOCK;
1466 case QRhiTexture::ASTC_12x10:
1467 return srgb ? VK_FORMAT_ASTC_12x10_SRGB_BLOCK : VK_FORMAT_ASTC_12x10_UNORM_BLOCK;
1468 case QRhiTexture::ASTC_12x12:
1469 return srgb ? VK_FORMAT_ASTC_12x12_SRGB_BLOCK : VK_FORMAT_ASTC_12x12_UNORM_BLOCK;
1470
1471 default:
1472 Q_UNREACHABLE_RETURN(VK_FORMAT_R8G8B8A8_UNORM);
1473 }
1474}
1475
1476static inline QRhiTexture::Format swapchainReadbackTextureFormat(VkFormat format, QRhiTexture::Flags *flags)
1477{
1478 switch (format) {
1479 case VK_FORMAT_R8G8B8A8_UNORM:
1480 return QRhiTexture::RGBA8;
1481 case VK_FORMAT_R8G8B8A8_SRGB:
1482 if (flags)
1483 (*flags) |= QRhiTexture::sRGB;
1484 return QRhiTexture::RGBA8;
1485 case VK_FORMAT_B8G8R8A8_UNORM:
1486 return QRhiTexture::BGRA8;
1487 case VK_FORMAT_B8G8R8A8_SRGB:
1488 if (flags)
1489 (*flags) |= QRhiTexture::sRGB;
1490 return QRhiTexture::BGRA8;
1491 case VK_FORMAT_R16G16B16A16_SFLOAT:
1492 return QRhiTexture::RGBA16F;
1493 case VK_FORMAT_R32G32B32A32_SFLOAT:
1494 return QRhiTexture::RGBA32F;
1495 case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
1496 return QRhiTexture::RGB10A2;
1497 default:
1498 qWarning("VkFormat %d cannot be read back", format);
1499 break;
1500 }
1501 return QRhiTexture::UnknownFormat;
1502}
1503
1504static constexpr inline bool isDepthTextureFormat(QRhiTexture::Format format)
1505{
1506 switch (format) {
1507 case QRhiTexture::Format::D16:
1508 case QRhiTexture::Format::D24:
1509 case QRhiTexture::Format::D24S8:
1510 case QRhiTexture::Format::D32F:
1511 case QRhiTexture::Format::D32FS8:
1512 return true;
1513
1514 default:
1515 return false;
1516 }
1517}
1518
1519static constexpr inline bool isStencilTextureFormat(QRhiTexture::Format format)
1520{
1521 switch (format) {
1522 case QRhiTexture::Format::D24S8:
1523 case QRhiTexture::Format::D32FS8:
1524 return true;
1525
1526 default:
1527 return false;
1528 }
1529}
1530
1531static constexpr inline VkImageAspectFlags aspectMaskForTextureFormat(QRhiTexture::Format format)
1532{
1533 if (isDepthTextureFormat(format)) {
1534 if (isStencilTextureFormat(format))
1535 return VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
1536 else
1537 return VK_IMAGE_ASPECT_DEPTH_BIT;
1538 } else {
1539 return VK_IMAGE_ASPECT_COLOR_BIT;
1540 }
1541}
1542
1543// Transient images ("render buffers") backed by lazily allocated memory are
1544// managed manually without going through vk_mem_alloc since it does not offer
1545// any support for such images. This should be ok since in practice there
1546// should be very few of such images.
1547
1548uint32_t QRhiVulkan::chooseTransientImageMemType(VkImage img, uint32_t startIndex)
1549{
1550 VkPhysicalDeviceMemoryProperties physDevMemProps;
1551 f->vkGetPhysicalDeviceMemoryProperties(physDev, &physDevMemProps);
1552
1553 VkMemoryRequirements memReq;
1554 df->vkGetImageMemoryRequirements(dev, img, &memReq);
1555 uint32_t memTypeIndex = uint32_t(-1);
1556
1557 if (memReq.memoryTypeBits) {
1558 // Find a device local + lazily allocated, or at least device local memtype.
1559 const VkMemoryType *memType = physDevMemProps.memoryTypes;
1560 bool foundDevLocal = false;
1561 for (uint32_t i = startIndex; i < physDevMemProps.memoryTypeCount; ++i) {
1562 if (memReq.memoryTypeBits & (1 << i)) {
1563 if (memType[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) {
1564 if (!foundDevLocal) {
1565 foundDevLocal = true;
1566 memTypeIndex = i;
1567 }
1568 if (memType[i].propertyFlags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) {
1569 memTypeIndex = i;
1570 break;
1571 }
1572 }
1573 }
1574 }
1575 }
1576
1577 return memTypeIndex;
1578}
1579
1580bool QRhiVulkan::createTransientImage(VkFormat format,
1581 const QSize &pixelSize,
1582 VkImageUsageFlags usage,
1583 VkImageAspectFlags aspectMask,
1584 VkSampleCountFlagBits samples,
1585 VkDeviceMemory *mem,
1586 VkImage *images,
1587 VkImageView *views,
1588 int count)
1589{
1590 VkMemoryRequirements memReq;
1591 VkResult err;
1592
1593 *mem = VK_NULL_HANDLE;
1594 for (int i = 0; i < count; ++i) {
1595 images[i] = VK_NULL_HANDLE;
1596 views[i] = VK_NULL_HANDLE;
1597 }
1598
1599 auto cleanup = qScopeGuard([this, mem, images, views, count] {
1600 for (int i = 0; i < count; ++i) {
1601 if (views[i]) {
1602 df->vkDestroyImageView(dev, views[i], nullptr);
1603 views[i] = VK_NULL_HANDLE;
1604 }
1605 if (images[i]) {
1606 df->vkDestroyImage(dev, images[i], nullptr);
1607 images[i] = VK_NULL_HANDLE;
1608 }
1609 }
1610 if (*mem) {
1611 df->vkFreeMemory(dev, *mem, nullptr);
1612 *mem = VK_NULL_HANDLE;
1613 }
1614 });
1615
1616 for (int i = 0; i < count; ++i) {
1617 VkImageCreateInfo imgInfo = {};
1618 imgInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
1619 imgInfo.imageType = VK_IMAGE_TYPE_2D;
1620 imgInfo.format = format;
1621 imgInfo.extent.width = uint32_t(pixelSize.width());
1622 imgInfo.extent.height = uint32_t(pixelSize.height());
1623 imgInfo.extent.depth = 1;
1624 imgInfo.mipLevels = imgInfo.arrayLayers = 1;
1625 imgInfo.samples = samples;
1626 imgInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
1627 imgInfo.usage = usage | VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT;
1628 imgInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1629
1630 err = df->vkCreateImage(dev, &imgInfo, nullptr, images + i);
1631 if (err != VK_SUCCESS) {
1632 qWarning("Failed to create image: %d", err);
1633 return false;
1634 }
1635
1636 // Assume the reqs are the same since the images are same in every way.
1637 // Still, call GetImageMemReq for every image, in order to prevent the
1638 // validation layer from complaining.
1639 df->vkGetImageMemoryRequirements(dev, images[i], &memReq);
1640 }
1641
1642 VkMemoryAllocateInfo memInfo = {};
1643 memInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
1644 memInfo.allocationSize = aligned(memReq.size, memReq.alignment) * VkDeviceSize(count);
1645
1646 uint32_t startIndex = 0;
1647 do {
1648 memInfo.memoryTypeIndex = chooseTransientImageMemType(images[0], startIndex);
1649 if (memInfo.memoryTypeIndex == uint32_t(-1)) {
1650 qWarning("No suitable memory type found");
1651 return false;
1652 }
1653 startIndex = memInfo.memoryTypeIndex + 1;
1654 err = df->vkAllocateMemory(dev, &memInfo, nullptr, mem);
1655 if (err != VK_SUCCESS && err != VK_ERROR_OUT_OF_DEVICE_MEMORY) {
1656 qWarning("Failed to allocate image memory: %d", err);
1657 return false;
1658 }
1659 } while (err != VK_SUCCESS);
1660
1661 VkDeviceSize ofs = 0;
1662 for (int i = 0; i < count; ++i) {
1663 err = df->vkBindImageMemory(dev, images[i], *mem, ofs);
1664 if (err != VK_SUCCESS) {
1665 qWarning("Failed to bind image memory: %d", err);
1666 return false;
1667 }
1668 ofs += aligned(memReq.size, memReq.alignment);
1669
1670 VkImageViewCreateInfo imgViewInfo = {};
1671 imgViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
1672 imgViewInfo.image = images[i];
1673 imgViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
1674 imgViewInfo.format = format;
1675 imgViewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
1676 imgViewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
1677 imgViewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
1678 imgViewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
1679 imgViewInfo.subresourceRange.aspectMask = aspectMask;
1680 imgViewInfo.subresourceRange.levelCount = imgViewInfo.subresourceRange.layerCount = 1;
1681
1682 err = df->vkCreateImageView(dev, &imgViewInfo, nullptr, views + i);
1683 if (err != VK_SUCCESS) {
1684 qWarning("Failed to create image view: %d", err);
1685 return false;
1686 }
1687 }
1688
1689 cleanup.dismiss();
1690 return true;
1691}
1692
1694{
1695 if (optimalDsFormat != VK_FORMAT_UNDEFINED)
1696 return optimalDsFormat;
1697
1698 const VkFormat dsFormatCandidates[] = {
1699 VK_FORMAT_D24_UNORM_S8_UINT,
1700 VK_FORMAT_D32_SFLOAT_S8_UINT,
1701 VK_FORMAT_D16_UNORM_S8_UINT
1702 };
1703 const int dsFormatCandidateCount = sizeof(dsFormatCandidates) / sizeof(VkFormat);
1704 int dsFormatIdx = 0;
1705 while (dsFormatIdx < dsFormatCandidateCount) {
1706 optimalDsFormat = dsFormatCandidates[dsFormatIdx];
1707 VkFormatProperties fmtProp;
1708 f->vkGetPhysicalDeviceFormatProperties(physDev, optimalDsFormat, &fmtProp);
1709 if (fmtProp.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)
1710 break;
1711 ++dsFormatIdx;
1712 }
1713 if (dsFormatIdx == dsFormatCandidateCount)
1714 qWarning("Failed to find an optimal depth-stencil format");
1715
1716 return optimalDsFormat;
1717}
1718
1720{
1721 bool prepare(VkRenderPassCreateInfo *rpInfo, int multiViewCount, bool multiViewCap)
1722 {
1723 if (multiViewCount < 2)
1724 return true;
1725 if (!multiViewCap) {
1726 qWarning("Cannot create multiview render pass without support for the Vulkan 1.1 multiview feature");
1727 return false;
1728 }
1729#ifdef VK_VERSION_1_1
1730 uint32_t allViewsMask = 0;
1731 for (uint32_t i = 0; i < uint32_t(multiViewCount); ++i)
1732 allViewsMask |= (1 << i);
1733 multiViewMask = allViewsMask;
1734 multiViewCorrelationMask = allViewsMask;
1735 multiViewInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO;
1736 multiViewInfo.subpassCount = 1;
1737 multiViewInfo.pViewMasks = &multiViewMask;
1738 multiViewInfo.correlationMaskCount = 1;
1739 multiViewInfo.pCorrelationMasks = &multiViewCorrelationMask;
1740 rpInfo->pNext = &multiViewInfo;
1741#endif
1742 return true;
1743 }
1744
1745#ifdef VK_VERSION_1_1
1749#endif
1750};
1751
1752#ifdef VK_KHR_create_renderpass2
1753// Effectively converts a VkRenderPassCreateInfo into a VkRenderPassCreateInfo2,
1754// adding depth-stencil resolve and VRS support. Incorporates multiview into the
1755// info structs (no chaining needed). Assumes a single subpass.
1756struct RenderPass2SetupHelper
1757{
1758 RenderPass2SetupHelper(QRhiVulkan *rhiD) : rhiD(rhiD) { }
1759
1760 bool prepare(VkRenderPassCreateInfo2 *rpInfo2, const VkRenderPassCreateInfo *rpInfo, const QVkRenderPassDescriptor *rpD, int multiViewCount) {
1761 *rpInfo2 = {};
1762
1763 viewMask = 0;
1764 if (multiViewCount >= 2) {
1765 for (uint32_t i = 0; i < uint32_t(multiViewCount); ++i)
1766 viewMask |= (1 << i);
1767 }
1768
1769 attDescs2.resize(rpInfo->attachmentCount);
1770 for (qsizetype i = 0; i < attDescs2.count(); ++i) {
1771 VkAttachmentDescription2KHR &att2(attDescs2[i]);
1772 const VkAttachmentDescription &att(rpInfo->pAttachments[i]);
1773 att2 = {};
1774 att2.sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2;
1775 att2.flags = att.flags;
1776 att2.format = att.format;
1777 att2.samples = att.samples;
1778 att2.loadOp = att.loadOp;
1779 att2.storeOp = att.storeOp;
1780 att2.stencilLoadOp = att.stencilLoadOp;
1781 att2.stencilStoreOp = att.stencilStoreOp;
1782 att2.initialLayout = att.initialLayout;
1783 att2.finalLayout = att.finalLayout;
1784 }
1785
1786 attRefs2.clear();
1787 subpass2 = {};
1788 subpass2.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2_KHR;
1789 const VkSubpassDescription &subpassDesc(rpInfo->pSubpasses[0]);
1790 subpass2.flags = subpassDesc.flags;
1791 subpass2.pipelineBindPoint = subpassDesc.pipelineBindPoint;
1792 if (multiViewCount >= 2)
1793 subpass2.viewMask = viewMask;
1794
1795 // color attachment refs
1796 qsizetype startIndex = attRefs2.count();
1797 for (uint32_t j = 0; j < subpassDesc.colorAttachmentCount; ++j) {
1798 attRefs2.append({});
1799 VkAttachmentReference2KHR &attref2(attRefs2.last());
1800 const VkAttachmentReference &attref(subpassDesc.pColorAttachments[j]);
1801 attref2.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR;
1802 attref2.attachment = attref.attachment;
1803 attref2.layout = attref.layout;
1804 attref2.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1805 }
1806 subpass2.colorAttachmentCount = subpassDesc.colorAttachmentCount;
1807 subpass2.pColorAttachments = attRefs2.constData() + startIndex;
1808
1809 // color resolve refs
1810 if (subpassDesc.pResolveAttachments) {
1811 startIndex = attRefs2.count();
1812 for (uint32_t j = 0; j < subpassDesc.colorAttachmentCount; ++j) {
1813 attRefs2.append({});
1814 VkAttachmentReference2KHR &attref2(attRefs2.last());
1815 const VkAttachmentReference &attref(subpassDesc.pResolveAttachments[j]);
1816 attref2.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR;
1817 attref2.attachment = attref.attachment;
1818 attref2.layout = attref.layout;
1819 attref2.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1820 }
1821 subpass2.pResolveAttachments = attRefs2.constData() + startIndex;
1822 }
1823
1824 // depth-stencil ref
1825 if (subpassDesc.pDepthStencilAttachment) {
1826 startIndex = attRefs2.count();
1827 attRefs2.append({});
1828 VkAttachmentReference2KHR &attref2(attRefs2.last());
1829 const VkAttachmentReference &attref(*subpassDesc.pDepthStencilAttachment);
1830 attref2.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR;
1831 attref2.attachment = attref.attachment;
1832 attref2.layout = attref.layout;
1833 attref2.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
1834 subpass2.pDepthStencilAttachment = attRefs2.constData() + startIndex;
1835 }
1836
1837 // depth-stencil resolve ref
1838#ifdef VK_KHR_depth_stencil_resolve
1839 dsResolveDesc = {};
1840 if (rpD->hasDepthStencilResolve) {
1841 startIndex = attRefs2.count();
1842 attRefs2.append({});
1843 VkAttachmentReference2KHR &attref2(attRefs2.last());
1844 attref2.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR;
1845 attref2.attachment = rpD->dsResolveRef.attachment;
1846 attref2.layout = rpD->dsResolveRef.layout;
1847 attref2.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
1848 dsResolveDesc.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE_KHR;
1849 dsResolveDesc.depthResolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;
1850 dsResolveDesc.stencilResolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;
1851 dsResolveDesc.pDepthStencilResolveAttachment = attRefs2.constData() + startIndex;
1852 addToChain(&subpass2, &dsResolveDesc);
1853 }
1854#endif
1855
1856#ifdef VK_KHR_fragment_shading_rate
1857 shadingRateAttInfo = {};
1858 if (rpD->hasShadingRateMap) {
1859 startIndex = attRefs2.count();
1860 attRefs2.append({});
1861 VkAttachmentReference2KHR &attref2(attRefs2.last());
1862 attref2.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR;
1863 attref2.attachment = rpD->shadingRateRef.attachment;
1864 attref2.layout = rpD->shadingRateRef.layout;
1865 shadingRateAttInfo.sType = VK_STRUCTURE_TYPE_FRAGMENT_SHADING_RATE_ATTACHMENT_INFO_KHR;
1866 shadingRateAttInfo.pFragmentShadingRateAttachment = attRefs2.constData() + startIndex;
1867 shadingRateAttInfo.shadingRateAttachmentTexelSize.width = rhiD->caps.imageBasedShadingRateTileSize;
1868 shadingRateAttInfo.shadingRateAttachmentTexelSize.height = rhiD->caps.imageBasedShadingRateTileSize;
1869 addToChain(&subpass2, &shadingRateAttInfo);
1870 }
1871#endif
1872
1873 // subpass dependencies, typically 0, 1, 2 of them,
1874 // depending on targeting swapchain or texture
1875 subpassDeps2.clear();
1876 for (uint32_t i = 0; i < rpInfo->dependencyCount; ++i) {
1877 const VkSubpassDependency &dep(rpInfo->pDependencies[i]);
1878 subpassDeps2.append({});
1879 VkSubpassDependency2 &dep2(subpassDeps2.last());
1880 dep2.sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2_KHR;
1881 dep2.srcSubpass = dep.srcSubpass;
1882 dep2.dstSubpass = dep.dstSubpass;
1883 dep2.srcStageMask = dep.srcStageMask;
1884 dep2.dstStageMask = dep.dstStageMask;
1885 dep2.srcAccessMask = dep.srcAccessMask;
1886 dep2.dstAccessMask = dep.dstAccessMask;
1887 dep2.dependencyFlags = dep.dependencyFlags;
1888 }
1889
1890 rpInfo2->sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2_KHR;
1891 rpInfo2->pNext = nullptr; // the 1.1 VkRenderPassMultiviewCreateInfo is part of the '2' structs
1892 rpInfo2->flags = rpInfo->flags;
1893 rpInfo2->attachmentCount = rpInfo->attachmentCount;
1894 rpInfo2->pAttachments = attDescs2.constData();
1895 rpInfo2->subpassCount = 1;
1896 rpInfo2->pSubpasses = &subpass2;
1897 rpInfo2->dependencyCount = subpassDeps2.count();
1898 rpInfo2->pDependencies = !subpassDeps2.isEmpty() ? subpassDeps2.constData() : nullptr;
1899 if (multiViewCount >= 2) {
1900 rpInfo2->correlatedViewMaskCount = 1;
1901 rpInfo2->pCorrelatedViewMasks = &viewMask;
1902 }
1903 return true;
1904 }
1905
1906 QRhiVulkan *rhiD;
1907 QVarLengthArray<VkAttachmentDescription2KHR, 8> attDescs2;
1908 QVarLengthArray<VkAttachmentReference2KHR, 8> attRefs2;
1909 VkSubpassDescription2KHR subpass2;
1910 QVarLengthArray<VkSubpassDependency2KHR, 4> subpassDeps2;
1911#ifdef VK_KHR_depth_stencil_resolve
1912 VkSubpassDescriptionDepthStencilResolveKHR dsResolveDesc;
1913#endif
1914#ifdef VK_KHR_fragment_shading_rate
1915 VkFragmentShadingRateAttachmentInfoKHR shadingRateAttInfo;
1916#endif
1917 uint32_t viewMask;
1918};
1919#endif // VK_KHR_create_renderpass2
1920
1921static void fillRenderPassCreateInfo(VkRenderPassCreateInfo *rpInfo,
1922 VkSubpassDescription *subpassDesc,
1924{
1925 memset(subpassDesc, 0, sizeof(VkSubpassDescription));
1926 subpassDesc->pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
1927 subpassDesc->colorAttachmentCount = uint32_t(rpD->colorRefs.size());
1928 subpassDesc->pColorAttachments = !rpD->colorRefs.isEmpty() ? rpD->colorRefs.constData() : nullptr;
1929 subpassDesc->pDepthStencilAttachment = rpD->hasDepthStencil ? &rpD->dsRef : nullptr;
1930 subpassDesc->pResolveAttachments = !rpD->resolveRefs.isEmpty() ? rpD->resolveRefs.constData() : nullptr;
1931
1932 memset(rpInfo, 0, sizeof(VkRenderPassCreateInfo));
1933 rpInfo->sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
1934 rpInfo->attachmentCount = uint32_t(rpD->attDescs.size());
1935 rpInfo->pAttachments = rpD->attDescs.constData();
1936 rpInfo->subpassCount = 1;
1937 rpInfo->pSubpasses = subpassDesc;
1938 rpInfo->dependencyCount = uint32_t(rpD->subpassDeps.size());
1939 rpInfo->pDependencies = !rpD->subpassDeps.isEmpty() ? rpD->subpassDeps.constData() : nullptr;
1940}
1941
1943 bool hasDepthStencil,
1944 VkSampleCountFlagBits samples,
1945 VkFormat colorFormat,
1946 QRhiShadingRateMap *shadingRateMap)
1947{
1948 // attachment list layout is color (1), ds (0-1), resolve (0-1), shading rate (0-1)
1949
1950 VkAttachmentDescription attDesc = {};
1951 attDesc.format = colorFormat;
1952 attDesc.samples = samples;
1953 attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1954 attDesc.storeOp = samples > VK_SAMPLE_COUNT_1_BIT ? VK_ATTACHMENT_STORE_OP_DONT_CARE : VK_ATTACHMENT_STORE_OP_STORE;
1955 attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1956 attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1957 attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1958 attDesc.finalLayout = samples > VK_SAMPLE_COUNT_1_BIT ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
1959 rpD->attDescs.append(attDesc);
1960
1961 rpD->colorRefs.append({ 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL });
1962
1963 rpD->hasDepthStencil = hasDepthStencil;
1964 rpD->hasDepthStencilResolve = false;
1965 rpD->hasShadingRateMap = shadingRateMap != nullptr;
1966 rpD->multiViewCount = 0;
1967
1968 if (hasDepthStencil) {
1969 // clear on load + no store + lazy alloc + transient image should play
1970 // nicely with tiled GPUs (no physical backing necessary for ds buffer)
1971 attDesc = {};
1972 attDesc.format = optimalDepthStencilFormat();
1973 attDesc.samples = samples;
1974 attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1975 attDesc.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1976 attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1977 attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1978 attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1979 attDesc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
1980 rpD->attDescs.append(attDesc);
1981
1982 rpD->dsRef = { uint32_t(rpD->attDescs.size() - 1), VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
1983 } else {
1984 rpD->dsRef = {};
1985 }
1986
1987 if (samples > VK_SAMPLE_COUNT_1_BIT) {
1988 attDesc = {};
1989 attDesc.format = colorFormat;
1990 attDesc.samples = VK_SAMPLE_COUNT_1_BIT;
1991 attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1992 attDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
1993 attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1994 attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1995 attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1996 attDesc.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
1997 rpD->attDescs.append(attDesc);
1998
1999 rpD->resolveRefs.append({ uint32_t(rpD->attDescs.size() - 1), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL });
2000 }
2001
2002 rpD->dsResolveRef = {};
2003
2004 rpD->shadingRateRef = {};
2005#ifdef VK_KHR_fragment_shading_rate
2006 if (shadingRateMap) {
2007 attDesc = {};
2008 attDesc.format = VK_FORMAT_R8_UINT;
2009 attDesc.samples = VK_SAMPLE_COUNT_1_BIT;
2010 attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
2011 attDesc.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
2012 attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
2013 attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
2014 attDesc.initialLayout = VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR;
2015 attDesc.finalLayout = VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR;
2016 rpD->attDescs.append(attDesc);
2017
2018 rpD->shadingRateRef = { uint32_t(rpD->attDescs.size() - 1), VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR };
2019 }
2020#endif
2021
2022 // Replace the first implicit dep (TOP_OF_PIPE / ALL_COMMANDS) with our own.
2023 VkSubpassDependency subpassDep = {};
2024 subpassDep.srcSubpass = VK_SUBPASS_EXTERNAL;
2025 subpassDep.dstSubpass = 0;
2026 subpassDep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
2027 subpassDep.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
2028 subpassDep.srcAccessMask = 0;
2029 subpassDep.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
2030 rpD->subpassDeps.append(subpassDep);
2031 if (hasDepthStencil) {
2032 memset(&subpassDep, 0, sizeof(subpassDep));
2033 subpassDep.srcSubpass = VK_SUBPASS_EXTERNAL;
2034 subpassDep.dstSubpass = 0;
2035 subpassDep.srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT
2036 | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
2037 subpassDep.dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT
2038 | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
2039 subpassDep.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
2040 subpassDep.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT
2041 | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
2042 rpD->subpassDeps.append(subpassDep);
2043 }
2044
2045 VkRenderPassCreateInfo rpInfo;
2046 VkSubpassDescription subpassDesc;
2047 fillRenderPassCreateInfo(&rpInfo, &subpassDesc, rpD);
2048
2049#ifdef VK_KHR_create_renderpass2
2050 if (caps.renderPass2KHR) {
2051 // Use the KHR extension, not the 1.2 core API, in order to support Vulkan 1.1.
2052 VkRenderPassCreateInfo2KHR rpInfo2;
2053 RenderPass2SetupHelper rp2Helper(this);
2054 if (!rp2Helper.prepare(&rpInfo2, &rpInfo, rpD, 0))
2055 return false;
2056 VkResult err = vkCreateRenderPass2KHR(dev, &rpInfo2, nullptr, &rpD->rp);
2057 if (err != VK_SUCCESS) {
2058 qWarning("Failed to create renderpass (using VkRenderPassCreateInfo2KHR): %d", err);
2059 return false;
2060 }
2061 } else
2062#endif
2063 {
2064 if (rpD->hasShadingRateMap)
2065 qWarning("Variable rate shading with image is not supported without VK_KHR_create_renderpass2");
2066 VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, &rpD->rp);
2067 if (err != VK_SUCCESS) {
2068 qWarning("Failed to create renderpass: %d", err);
2069 return false;
2070 }
2071 }
2072
2073 return true;
2074}
2075
2077 const QRhiColorAttachment *colorAttachmentsBegin,
2078 const QRhiColorAttachment *colorAttachmentsEnd,
2079 bool preserveColor,
2080 bool preserveDs,
2081 bool storeDs,
2082 QRhiRenderBuffer *depthStencilBuffer,
2083 QRhiTexture *depthTexture,
2084 QRhiTexture *depthResolveTexture,
2085 int depthLayer,
2086 QRhiShadingRateMap *shadingRateMap)
2087{
2088 // attachment list layout is color (0-8), ds (0-1), resolve (0-8), ds resolve (0-1)
2089
2090 int multiViewCount = 0;
2091 for (auto it = colorAttachmentsBegin; it != colorAttachmentsEnd; ++it) {
2092 QVkTexture *texD = QRHI_RES(QVkTexture, it->texture());
2093 QVkRenderBuffer *rbD = QRHI_RES(QVkRenderBuffer, it->renderBuffer());
2094 Q_ASSERT(texD || rbD);
2095 const VkFormat vkformat = texD ? texD->viewFormat : rbD->vkformat;
2096 const VkSampleCountFlagBits samples = texD ? texD->samples : rbD->samples;
2097
2098 VkAttachmentDescription attDesc = {};
2099 attDesc.format = vkformat;
2100 attDesc.samples = samples;
2101 attDesc.loadOp = preserveColor ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_CLEAR;
2102 attDesc.storeOp = (it->resolveTexture() && !preserveColor) ? VK_ATTACHMENT_STORE_OP_DONT_CARE : VK_ATTACHMENT_STORE_OP_STORE;
2103 attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
2104 attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
2105 // this has to interact correctly with activateTextureRenderTarget(), hence leaving in COLOR_ATT
2106 attDesc.initialLayout = preserveColor ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_UNDEFINED;
2107 attDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
2108 rpD->attDescs.append(attDesc);
2109
2110 const VkAttachmentReference ref = { uint32_t(rpD->attDescs.size() - 1), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
2111 rpD->colorRefs.append(ref);
2112
2113 if (it->multiViewCount() >= 2) {
2114 if (multiViewCount > 0 && multiViewCount != it->multiViewCount())
2115 qWarning("Inconsistent multiViewCount in color attachment set");
2116 else
2117 multiViewCount = it->multiViewCount();
2118 } else if (multiViewCount > 0) {
2119 qWarning("Mixing non-multiview color attachments within a multiview render pass");
2120 }
2121 }
2122 Q_ASSERT(multiViewCount == 0 || multiViewCount >= 2);
2123 rpD->multiViewCount = uint32_t(multiViewCount);
2124
2125 rpD->hasDepthStencil = depthStencilBuffer || depthTexture;
2126 if (rpD->hasDepthStencil) {
2127 const VkFormat dsFormat = depthTexture ? QRHI_RES(QVkTexture, depthTexture)->viewFormat
2128 : QRHI_RES(QVkRenderBuffer, depthStencilBuffer)->vkformat;
2129 const VkSampleCountFlagBits samples = depthTexture ? QRHI_RES(QVkTexture, depthTexture)->samples
2130 : QRHI_RES(QVkRenderBuffer, depthStencilBuffer)->samples;
2131 const VkAttachmentLoadOp loadOp = preserveDs ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_CLEAR;
2132 const VkAttachmentStoreOp storeOp = storeDs ? VK_ATTACHMENT_STORE_OP_STORE : VK_ATTACHMENT_STORE_OP_DONT_CARE;
2133 VkAttachmentDescription attDesc = {};
2134 attDesc.format = dsFormat;
2135 attDesc.samples = samples;
2136 attDesc.loadOp = loadOp;
2137 attDesc.storeOp = storeOp;
2138 attDesc.stencilLoadOp = loadOp;
2139 attDesc.stencilStoreOp = storeOp;
2140 attDesc.initialLayout = preserveDs ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_UNDEFINED;
2141 attDesc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
2142 rpD->attDescs.append(attDesc);
2143 if (depthTexture && depthTexture->arraySize() >= 2 && depthLayer == -1 && colorAttachmentsBegin == colorAttachmentsEnd) {
2144 multiViewCount = depthTexture->arraySize();
2145 rpD->multiViewCount = multiViewCount;
2146 }
2147 rpD->dsRef = { uint32_t(rpD->attDescs.size() - 1), VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
2148 } else {
2149 rpD->dsRef = {};
2150 }
2151
2152 for (auto it = colorAttachmentsBegin; it != colorAttachmentsEnd; ++it) {
2153 if (it->resolveTexture()) {
2154 QVkTexture *rtexD = QRHI_RES(QVkTexture, it->resolveTexture());
2155 const VkFormat dstFormat = rtexD->vkformat;
2156 if (rtexD->samples > VK_SAMPLE_COUNT_1_BIT)
2157 qWarning("Resolving into a multisample texture is not supported");
2158
2159 QVkTexture *texD = QRHI_RES(QVkTexture, it->texture());
2160 QVkRenderBuffer *rbD = QRHI_RES(QVkRenderBuffer, it->renderBuffer());
2161 const VkFormat srcFormat = texD ? texD->vkformat : rbD->vkformat;
2162 if (srcFormat != dstFormat) {
2163 // This is a validation error. But some implementations survive,
2164 // actually. Warn about it however, because it's an error with
2165 // some other backends (like D3D) as well.
2166 qWarning("Multisample resolve between different formats (%d and %d) is not supported.",
2167 int(srcFormat), int(dstFormat));
2168 }
2169
2170 VkAttachmentDescription attDesc = {};
2171 attDesc.format = rtexD->viewFormat;
2172 attDesc.samples = VK_SAMPLE_COUNT_1_BIT;
2173 attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; // ignored
2174 attDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
2175 attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
2176 attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
2177 attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
2178 attDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
2179 rpD->attDescs.append(attDesc);
2180
2181 const VkAttachmentReference ref = { uint32_t(rpD->attDescs.size() - 1), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
2182 rpD->resolveRefs.append(ref);
2183 } else {
2184 const VkAttachmentReference ref = { VK_ATTACHMENT_UNUSED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
2185 rpD->resolveRefs.append(ref);
2186 }
2187 }
2188 Q_ASSERT(rpD->colorRefs.size() == rpD->resolveRefs.size());
2189
2190 rpD->hasDepthStencilResolve = rpD->hasDepthStencil && depthResolveTexture;
2191 if (rpD->hasDepthStencilResolve) {
2192 QVkTexture *rtexD = QRHI_RES(QVkTexture, depthResolveTexture);
2193 if (rtexD->samples > VK_SAMPLE_COUNT_1_BIT)
2194 qWarning("Resolving into a multisample depth texture is not supported");
2195 const VkFormat dstFormat = rtexD->vkformat;
2196
2197 QVkTexture *texD = QRHI_RES(QVkTexture, depthTexture);
2198 QVkRenderBuffer *rbD = QRHI_RES(QVkRenderBuffer, depthStencilBuffer);
2199 Q_ASSERT(texD || rbD);
2200 const VkFormat srcFormat = texD ? texD->vkformat : rbD->vkformat;
2201 if (srcFormat != dstFormat) {
2202 qWarning("Multisample resolve between different depth-stencil formats (%d and %d) is not supported.",
2203 int(srcFormat), int(dstFormat));
2204 }
2205
2206 VkAttachmentDescription attDesc = {};
2207 attDesc.format = rtexD->viewFormat;
2208 attDesc.samples = VK_SAMPLE_COUNT_1_BIT;
2209 attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; // ignored
2210 attDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
2211 attDesc.stencilLoadOp = attDesc.loadOp;
2212 attDesc.stencilStoreOp = attDesc.storeOp;
2213 attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
2214 attDesc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
2215 rpD->attDescs.append(attDesc);
2216 rpD->dsResolveRef = { uint32_t(rpD->attDescs.size() - 1), VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
2217 } else {
2218 rpD->dsResolveRef = {};
2219 }
2220
2221 rpD->hasShadingRateMap = shadingRateMap != nullptr;
2222 rpD->shadingRateRef = {};
2223#ifdef VK_KHR_fragment_shading_rate
2224 if (shadingRateMap) {
2225 VkAttachmentDescription attDesc = {};
2226 attDesc.format = VK_FORMAT_R8_UINT;
2227 attDesc.samples = VK_SAMPLE_COUNT_1_BIT;
2228 attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
2229 attDesc.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
2230 attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
2231 attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
2232 attDesc.initialLayout = VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR;
2233 attDesc.finalLayout = VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR;
2234 rpD->attDescs.append(attDesc);
2235 rpD->shadingRateRef = { uint32_t(rpD->attDescs.size() - 1), VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR };
2236 }
2237#endif
2238
2239 // Add self-dependency to be able to add memory barriers for writes in graphics stages.
2240 VkSubpassDependency selfDependency = {};
2241 VkPipelineStageFlags stageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT
2242 | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT
2243 | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT
2244 | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
2245 selfDependency.srcSubpass = 0;
2246 selfDependency.dstSubpass = 0;
2247 selfDependency.srcStageMask = stageMask;
2248 selfDependency.dstStageMask = stageMask;
2249 selfDependency.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
2250 selfDependency.dstAccessMask = selfDependency.srcAccessMask;
2251 VkDependencyFlags depFlags = VK_DEPENDENCY_BY_REGION_BIT;
2252#ifdef VK_VERSION_1_1
2253 if (rpD->multiViewCount >= 2)
2254 depFlags |= VK_DEPENDENCY_VIEW_LOCAL_BIT;
2255#endif
2256 selfDependency.dependencyFlags = depFlags;
2257 rpD->subpassDeps.append(selfDependency);
2258
2259 // rpD->subpassDeps stays empty: don't yet know the correct initial/final
2260 // access and stage stuff for the implicit deps at this point, so leave it
2261 // to the resource tracking and activateTextureRenderTarget() to generate
2262 // barriers.
2263
2264 VkRenderPassCreateInfo rpInfo;
2265 VkSubpassDescription subpassDesc;
2266 fillRenderPassCreateInfo(&rpInfo, &subpassDesc, rpD);
2267
2268 MultiViewRenderPassSetupHelper multiViewHelper;
2269 if (!multiViewHelper.prepare(&rpInfo, multiViewCount, caps.multiView))
2270 return false;
2271
2272#ifdef VK_KHR_create_renderpass2
2273 if (caps.renderPass2KHR) {
2274 // Use the KHR extension, not the 1.2 core API, in order to support Vulkan 1.1.
2275 VkRenderPassCreateInfo2KHR rpInfo2;
2276 RenderPass2SetupHelper rp2Helper(this);
2277 if (!rp2Helper.prepare(&rpInfo2, &rpInfo, rpD, multiViewCount))
2278 return false;
2279
2280 VkResult err = vkCreateRenderPass2KHR(dev, &rpInfo2, nullptr, &rpD->rp);
2281 if (err != VK_SUCCESS) {
2282 qWarning("Failed to create renderpass (using VkRenderPassCreateInfo2KHR): %d", err);
2283 return false;
2284 }
2285 } else
2286#endif
2287 {
2288 if (rpD->hasDepthStencilResolve) {
2289 qWarning("Resolving multisample depth-stencil buffers is not supported without "
2290 "VK_KHR_depth_stencil_resolve and VK_KHR_create_renderpass2");
2291 }
2292 if (rpD->hasShadingRateMap)
2293 qWarning("Variable rate shading with image is not supported without VK_KHR_create_renderpass2");
2294 VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, &rpD->rp);
2295 if (err != VK_SUCCESS) {
2296 qWarning("Failed to create renderpass: %d", err);
2297 return false;
2298 }
2299 }
2300
2301 return true;
2302}
2303
2304bool QRhiVulkan::recreateSwapChain(QRhiSwapChain *swapChain)
2305{
2306 QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain);
2307 if (swapChainD->pixelSize.isEmpty()) {
2308 qWarning("Surface size is 0, cannot create swapchain");
2309 return false;
2310 }
2311
2312 df->vkDeviceWaitIdle(dev);
2313
2314 if (!vkCreateSwapchainKHR) {
2315 vkCreateSwapchainKHR = reinterpret_cast<PFN_vkCreateSwapchainKHR>(f->vkGetDeviceProcAddr(dev, "vkCreateSwapchainKHR"));
2316 vkDestroySwapchainKHR = reinterpret_cast<PFN_vkDestroySwapchainKHR>(f->vkGetDeviceProcAddr(dev, "vkDestroySwapchainKHR"));
2317 vkGetSwapchainImagesKHR = reinterpret_cast<PFN_vkGetSwapchainImagesKHR>(f->vkGetDeviceProcAddr(dev, "vkGetSwapchainImagesKHR"));
2318 vkAcquireNextImageKHR = reinterpret_cast<PFN_vkAcquireNextImageKHR>(f->vkGetDeviceProcAddr(dev, "vkAcquireNextImageKHR"));
2319 vkQueuePresentKHR = reinterpret_cast<PFN_vkQueuePresentKHR>(f->vkGetDeviceProcAddr(dev, "vkQueuePresentKHR"));
2320 if (!vkCreateSwapchainKHR || !vkDestroySwapchainKHR || !vkGetSwapchainImagesKHR || !vkAcquireNextImageKHR || !vkQueuePresentKHR) {
2321 qWarning("Swapchain functions not available");
2322 return false;
2323 }
2324 }
2325
2326 VkSurfaceCapabilitiesKHR surfaceCaps;
2327 vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physDev, swapChainD->surface, &surfaceCaps);
2328 quint32 reqBufferCount;
2329 if (swapChainD->m_flags.testFlag(QRhiSwapChain::MinimalBufferCount) || surfaceCaps.maxImageCount == 0) {
2330 reqBufferCount = qMax<quint32>(2, surfaceCaps.minImageCount);
2331 } else {
2332 reqBufferCount = qMax(qMin<quint32>(surfaceCaps.maxImageCount, 3), surfaceCaps.minImageCount);
2333 }
2334 VkSurfaceTransformFlagBitsKHR preTransform =
2335 (surfaceCaps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)
2336 ? VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR
2337 : surfaceCaps.currentTransform;
2338
2339 // This looks odd but matches how platforms work in practice.
2340 //
2341 // On Windows with NVIDIA for example, the only supportedCompositeAlpha
2342 // value reported is OPAQUE, nothing else. Yet transparency works
2343 // regardless, as long as the native window is set up correctly, so that's
2344 // not something we need to handle here.
2345 //
2346 // On Linux with Intel and Mesa and running on xcb reports, on one
2347 // particular system, INHERIT+PRE_MULTIPLIED. Tranparency works, regardless,
2348 // presumably due to setting INHERIT.
2349 //
2350 // On the same setup with Wayland instead of xcb we see
2351 // OPAQUE+PRE_MULTIPLIED reported. Here transparency won't work unless
2352 // PRE_MULTIPLIED is set.
2353 //
2354 // Therefore our rules are:
2355 // - Prefer INHERIT over OPAQUE.
2356 // - Then based on the request, try the requested alpha mode, but if
2357 // that's not reported as supported, try also the other (PRE/POST,
2358 // POST/PRE) as that is better than nothing. This is not different from
2359 // some other backends, e.g. D3D11 with DirectComposition there is also
2360 // no control over being straight or pre-multiplied. Whereas with
2361 // WGL/GLX/EGL we never had that sort of control.
2362
2363 VkCompositeAlphaFlagBitsKHR compositeAlpha =
2364 (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR)
2365 ? VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR
2366 : VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
2367
2368 if (swapChainD->m_flags.testFlag(QRhiSwapChain::SurfaceHasPreMulAlpha)) {
2369 if (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR)
2370 compositeAlpha = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;
2371 else if (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR)
2372 compositeAlpha = VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR;
2373 } else if (swapChainD->m_flags.testFlag(QRhiSwapChain::SurfaceHasNonPreMulAlpha)) {
2374 if (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR)
2375 compositeAlpha = VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR;
2376 else if (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR)
2377 compositeAlpha = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;
2378 }
2379
2380 VkImageUsageFlags usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
2381 swapChainD->supportsReadback = (surfaceCaps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
2382 if (swapChainD->supportsReadback && swapChainD->m_flags.testFlag(QRhiSwapChain::UsedAsTransferSource))
2383 usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
2384
2385 const bool stereo = bool(swapChainD->m_window) && (swapChainD->m_window->format().stereo())
2386 && surfaceCaps.maxImageArrayLayers > 1;
2387 swapChainD->stereo = stereo;
2388
2389 VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR;
2390 if (swapChainD->m_flags.testFlag(QRhiSwapChain::NoVSync)) {
2391 // Stereo has a weird bug, when using VK_PRESENT_MODE_MAILBOX_KHR,
2392 // black screen is shown, but there is no validation error.
2393 // Detected on Windows, with NVidia RTX A series (at least 4000 and 6000) driver 535.98
2394 if (swapChainD->supportedPresentationModes.contains(VK_PRESENT_MODE_MAILBOX_KHR) && !stereo)
2395 presentMode = VK_PRESENT_MODE_MAILBOX_KHR;
2396 else if (swapChainD->supportedPresentationModes.contains(VK_PRESENT_MODE_IMMEDIATE_KHR))
2397 presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
2398 }
2399
2400 // If the surface is different than before, then passing in the old
2401 // swapchain associated with the old surface can fail the swapchain
2402 // creation. (for example, Android loses the surface when backgrounding and
2403 // restoring applications, and it also enforces failing swapchain creation
2404 // with VK_ERROR_NATIVE_WINDOW_IN_USE_KHR if the old swapchain is provided)
2405 const bool reuseExisting = swapChainD->sc && swapChainD->lastConnectedSurface == swapChainD->surface;
2406
2407 qCDebug(QRHI_LOG_INFO, "Creating %s swapchain of %u buffers, size %dx%d, presentation mode %d",
2408 reuseExisting ? "recycled" : "new",
2409 reqBufferCount, swapChainD->pixelSize.width(), swapChainD->pixelSize.height(), presentMode);
2410
2411 VkSwapchainCreateInfoKHR swapChainInfo = {};
2412 swapChainInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
2413 swapChainInfo.surface = swapChainD->surface;
2414 swapChainInfo.minImageCount = reqBufferCount;
2415 swapChainInfo.imageFormat = swapChainD->colorFormat;
2416 swapChainInfo.imageColorSpace = swapChainD->colorSpace;
2417 swapChainInfo.imageExtent = VkExtent2D { uint32_t(swapChainD->pixelSize.width()), uint32_t(swapChainD->pixelSize.height()) };
2418 swapChainInfo.imageArrayLayers = stereo ? 2u : 1u;
2419 swapChainInfo.imageUsage = usage;
2420 swapChainInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
2421 swapChainInfo.preTransform = preTransform;
2422 swapChainInfo.compositeAlpha = compositeAlpha;
2423 swapChainInfo.presentMode = presentMode;
2424 swapChainInfo.clipped = true;
2425 swapChainInfo.oldSwapchain = reuseExisting ? swapChainD->sc : VK_NULL_HANDLE;
2426
2427 VkSwapchainKHR newSwapChain;
2428 VkResult err = vkCreateSwapchainKHR(dev, &swapChainInfo, nullptr, &newSwapChain);
2429 if (err != VK_SUCCESS) {
2430 qWarning("Failed to create swapchain: %d", err);
2431 return false;
2432 }
2433 setObjectName(uint64_t(newSwapChain), VK_OBJECT_TYPE_SWAPCHAIN_KHR, swapChainD->m_objectName);
2434
2435 if (swapChainD->sc)
2437
2438 swapChainD->sc = newSwapChain;
2439 swapChainD->lastConnectedSurface = swapChainD->surface;
2440
2441 quint32 actualSwapChainBufferCount = 0;
2442 err = vkGetSwapchainImagesKHR(dev, swapChainD->sc, &actualSwapChainBufferCount, nullptr);
2443 if (err != VK_SUCCESS || actualSwapChainBufferCount == 0) {
2444 qWarning("Failed to get swapchain images: %d", err);
2445 return false;
2446 }
2447
2448 if (actualSwapChainBufferCount != reqBufferCount)
2449 qCDebug(QRHI_LOG_INFO, "Actual swapchain buffer count is %u", actualSwapChainBufferCount);
2450 swapChainD->bufferCount = int(actualSwapChainBufferCount);
2451
2452 QVarLengthArray<VkImage, QVkSwapChain::EXPECTED_MAX_BUFFER_COUNT> swapChainImages(actualSwapChainBufferCount);
2453 err = vkGetSwapchainImagesKHR(dev, swapChainD->sc, &actualSwapChainBufferCount, swapChainImages.data());
2454 if (err != VK_SUCCESS) {
2455 qWarning("Failed to get swapchain images: %d", err);
2456 return false;
2457 }
2458
2459 QVarLengthArray<VkImage, QVkSwapChain::EXPECTED_MAX_BUFFER_COUNT> msaaImages(swapChainD->bufferCount);
2460 QVarLengthArray<VkImageView, QVkSwapChain::EXPECTED_MAX_BUFFER_COUNT> msaaViews(swapChainD->bufferCount);
2461 if (swapChainD->samples > VK_SAMPLE_COUNT_1_BIT) {
2462 if (!createTransientImage(swapChainD->colorFormat,
2463 swapChainD->pixelSize,
2464 VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
2465 VK_IMAGE_ASPECT_COLOR_BIT,
2466 swapChainD->samples,
2467 &swapChainD->msaaImageMem,
2468 msaaImages.data(),
2469 msaaViews.data(),
2470 swapChainD->bufferCount))
2471 {
2472 qWarning("Failed to create transient image for MSAA color buffer");
2473 return false;
2474 }
2475 }
2476
2477 VkFenceCreateInfo fenceInfo = {};
2478 fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
2479 fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
2480
2481 // Double up for stereo
2482 swapChainD->imageRes.resize(swapChainD->bufferCount * (stereo ? 2u : 1u));
2483
2484 for (int i = 0; i < swapChainD->bufferCount; ++i) {
2485 QVkSwapChain::ImageResources &image(swapChainD->imageRes[i]);
2486 image.image = swapChainImages[i];
2487 if (swapChainD->samples > VK_SAMPLE_COUNT_1_BIT) {
2488 image.msaaImage = msaaImages[i];
2489 image.msaaImageView = msaaViews[i];
2490 }
2491
2492 VkImageViewCreateInfo imgViewInfo = {};
2493 imgViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
2494 imgViewInfo.image = swapChainImages[i];
2495 imgViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
2496 imgViewInfo.format = swapChainD->colorFormat;
2497 imgViewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
2498 imgViewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
2499 imgViewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
2500 imgViewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
2501 imgViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
2502 imgViewInfo.subresourceRange.levelCount = imgViewInfo.subresourceRange.layerCount = 1;
2503 err = df->vkCreateImageView(dev, &imgViewInfo, nullptr, &image.imageView);
2504 if (err != VK_SUCCESS) {
2505 qWarning("Failed to create swapchain image view %d: %d", i, err);
2506 return false;
2507 }
2508
2510
2511 VkSemaphoreCreateInfo semInfo = {};
2512 semInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
2513 df->vkCreateSemaphore(dev, &semInfo, nullptr, &image.drawSem);
2514 }
2515 if (stereo) {
2516 for (int i = 0; i < swapChainD->bufferCount; ++i) {
2517 QVkSwapChain::ImageResources &image(swapChainD->imageRes[i + swapChainD->bufferCount]);
2518 image.image = swapChainImages[i];
2519 if (swapChainD->samples > VK_SAMPLE_COUNT_1_BIT) {
2520 image.msaaImage = msaaImages[i];
2521 image.msaaImageView = msaaViews[i];
2522 }
2523
2524 VkImageViewCreateInfo imgViewInfo = {};
2525 imgViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
2526 imgViewInfo.image = swapChainImages[i];
2527 imgViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
2528 imgViewInfo.format = swapChainD->colorFormat;
2529 imgViewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
2530 imgViewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
2531 imgViewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
2532 imgViewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
2533 imgViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
2534 imgViewInfo.subresourceRange.baseArrayLayer = 1;
2535 imgViewInfo.subresourceRange.levelCount = imgViewInfo.subresourceRange.layerCount = 1;
2536 err = df->vkCreateImageView(dev, &imgViewInfo, nullptr, &image.imageView);
2537 if (err != VK_SUCCESS) {
2538 qWarning("Failed to create swapchain image view %d: %d", i, err);
2539 return false;
2540 }
2541
2542 VkSemaphoreCreateInfo semInfo = {};
2543 semInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
2544 df->vkCreateSemaphore(dev, &semInfo, nullptr, &image.drawSem);
2545
2547 }
2548 }
2549
2550 swapChainD->currentImageIndex = 0;
2551
2552 if (swapChainD->shadingRateMap() && caps.renderPass2KHR && caps.imageBasedShadingRate) {
2553 QVkTexture *texD = QRHI_RES(QVkShadingRateMap, swapChainD->shadingRateMap())->texture;
2554 Q_ASSERT(texD->flags().testFlag(QRhiTexture::UsedAsShadingRateMap));
2555 VkImageViewCreateInfo viewInfo = {};
2556 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
2557 viewInfo.image = texD->image;
2558 viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
2559 viewInfo.format = texD->viewFormat;
2560 viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
2561 viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
2562 viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
2563 viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
2564 viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
2565 viewInfo.subresourceRange.baseMipLevel = 0;
2566 viewInfo.subresourceRange.levelCount = 1;
2567 viewInfo.subresourceRange.baseArrayLayer = 0;
2568 viewInfo.subresourceRange.layerCount = 1;
2569 VkResult err = df->vkCreateImageView(dev, &viewInfo, nullptr, &swapChainD->shadingRateMapView);
2570 if (err != VK_SUCCESS) {
2571 qWarning("Failed to create swapchain shading rate map view: %d", err);
2572 return false;
2573 }
2574 }
2575
2576 VkSemaphoreCreateInfo semInfo = {};
2577 semInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
2578
2579 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
2580 QVkSwapChain::FrameResources &frame(swapChainD->frameRes[i]);
2581
2582 frame.imageAcquired = false;
2583 frame.imageSemWaitable = false;
2584
2585 df->vkCreateSemaphore(dev, &semInfo, nullptr, &frame.imageSem);
2586
2587 err = df->vkCreateFence(dev, &fenceInfo, nullptr, &frame.cmdFence);
2588 if (err != VK_SUCCESS) {
2589 qWarning("Failed to create command buffer fence: %d", err);
2590 return false;
2591 }
2592 frame.cmdFenceWaitable = true; // fence was created in signaled state
2593 }
2594
2595 return true;
2596}
2597
2598void QRhiVulkan::releaseSwapChainResources(QRhiSwapChain *swapChain)
2599{
2600 QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain);
2601
2602 if (swapChainD->sc == VK_NULL_HANDLE)
2603 return;
2604
2605 if (!deviceLost)
2606 df->vkDeviceWaitIdle(dev);
2607
2608 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
2609 QVkSwapChain::FrameResources &frame(swapChainD->frameRes[i]);
2610 if (frame.cmdFence) {
2611 if (!deviceLost && frame.cmdFenceWaitable)
2612 df->vkWaitForFences(dev, 1, &frame.cmdFence, VK_TRUE, UINT64_MAX);
2613 df->vkDestroyFence(dev, frame.cmdFence, nullptr);
2614 frame.cmdFence = VK_NULL_HANDLE;
2615 frame.cmdFenceWaitable = false;
2616 }
2617 if (frame.imageSem) {
2618 df->vkDestroySemaphore(dev, frame.imageSem, nullptr);
2619 frame.imageSem = VK_NULL_HANDLE;
2620 }
2621 }
2622
2623 for (int i = 0; i < swapChainD->bufferCount * (swapChainD->stereo ? 2 : 1); ++i) {
2624 QVkSwapChain::ImageResources &image(swapChainD->imageRes[i]);
2625 if (image.fb) {
2626 df->vkDestroyFramebuffer(dev, image.fb, nullptr);
2627 image.fb = VK_NULL_HANDLE;
2628 }
2629 if (image.imageView) {
2630 df->vkDestroyImageView(dev, image.imageView, nullptr);
2631 image.imageView = VK_NULL_HANDLE;
2632 }
2633 if (image.msaaImageView) {
2634 df->vkDestroyImageView(dev, image.msaaImageView, nullptr);
2635 image.msaaImageView = VK_NULL_HANDLE;
2636 }
2637 if (image.msaaImage) {
2638 df->vkDestroyImage(dev, image.msaaImage, nullptr);
2639 image.msaaImage = VK_NULL_HANDLE;
2640 }
2641 if (image.drawSem) {
2642 df->vkDestroySemaphore(dev, image.drawSem, nullptr);
2643 image.drawSem = VK_NULL_HANDLE;
2644 }
2645 }
2646
2647 if (swapChainD->msaaImageMem) {
2648 df->vkFreeMemory(dev, swapChainD->msaaImageMem, nullptr);
2649 swapChainD->msaaImageMem = VK_NULL_HANDLE;
2650 }
2651
2652 if (swapChainD->shadingRateMapView) {
2653 df->vkDestroyImageView(dev, swapChainD->shadingRateMapView, nullptr);
2654 swapChainD->shadingRateMapView = VK_NULL_HANDLE;
2655 }
2656
2657 vkDestroySwapchainKHR(dev, swapChainD->sc, nullptr);
2658 swapChainD->sc = VK_NULL_HANDLE;
2659
2660 // NB! surface and similar must remain intact
2661}
2662
2664{
2665 VkCommandPoolResetFlags flags = 0;
2666
2667 // While not clear what "recycles all of the resources from the command
2668 // pool back to the system" really means in practice, set it when there was
2669 // a call to releaseCachedResources() recently.
2670 if (releaseCachedResourcesCalledBeforeFrameStart)
2671 flags |= VK_COMMAND_POOL_RESET_RELEASE_RESOURCES_BIT;
2672
2673 // put all command buffers allocated from this slot's pool to initial state
2674 df->vkResetCommandPool(dev, cmdPool[currentFrameSlot], flags);
2675}
2676
2677double QRhiVulkan::elapsedSecondsFromTimestamp(quint64 timestamp[2], bool *ok)
2678{
2679 quint64 mask = 0;
2680 for (quint64 i = 0; i < timestampValidBits; i += 8)
2681 mask |= 0xFFULL << i;
2682 const quint64 ts0 = timestamp[0] & mask;
2683 const quint64 ts1 = timestamp[1] & mask;
2684 const float nsecsPerTick = physDevProperties.limits.timestampPeriod;
2685 if (!qFuzzyIsNull(nsecsPerTick)) {
2686 const float elapsedMs = float(ts1 - ts0) * nsecsPerTick / 1000000.0f;
2687 const double elapsedSec = elapsedMs / 1000.0;
2688 *ok = true;
2689 return elapsedSec;
2690 }
2691 *ok = false;
2692 return 0;
2693}
2694
2696{
2697 QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain);
2698 const int frameResIndex = swapChainD->bufferCount > 1 ? currentFrameSlot : 0;
2699 QVkSwapChain::FrameResources &frame(swapChainD->frameRes[frameResIndex]);
2700
2701 inst->handle()->beginFrame(swapChainD->window);
2702
2703 // Make sure the previous commands for the same frame slot have finished.
2704 //
2705 // Do this also for any other swapchain's commands with the same frame slot
2706 // It keeps resource usage safe: swapchain A starting its frame 0, followed
2707 // by swapchain B starting its own frame 0 will make B wait for A's frame 0
2708 // commands, so if a resource is written in B's frame or when B checks for
2709 // pending resource releases, that won't mess up A's in-flight commands (as
2710 // they are not in flight anymore).
2711 QRhi::FrameOpResult waitResult = waitCommandCompletion(frameResIndex);
2712 if (waitResult != QRhi::FrameOpSuccess)
2713 return waitResult;
2714
2715 if (!frame.imageAcquired) {
2716 // move on to next swapchain image
2717 uint32_t imageIndex = 0;
2718 VkResult err = vkAcquireNextImageKHR(dev, swapChainD->sc, UINT64_MAX,
2719 frame.imageSem, VK_NULL_HANDLE, &imageIndex);
2720
2721 if (err == VK_SUCCESS || err == VK_SUBOPTIMAL_KHR) {
2722 swapChainD->currentImageIndex = imageIndex;
2723 frame.imageSemWaitable = true;
2724 frame.imageAcquired = true;
2725 } else if (err == VK_ERROR_OUT_OF_DATE_KHR) {
2726 return QRhi::FrameOpSwapChainOutOfDate;
2727 } else {
2728 if (err == VK_ERROR_DEVICE_LOST) {
2729 qWarning("Device loss detected in vkAcquireNextImageKHR()");
2730 printExtraErrorInfo(err);
2731 deviceLost = true;
2732 return QRhi::FrameOpDeviceLost;
2733 }
2734 qWarning("Failed to acquire next swapchain image: %d", err);
2735 return QRhi::FrameOpError;
2736 }
2737 }
2738
2739 currentSwapChain = swapChainD;
2740 if (swapChainD->ds)
2741 swapChainD->ds->lastActiveFrameSlot = currentFrameSlot;
2742
2743 // reset the command pool
2745
2746 // start recording to this frame's command buffer
2747 QRhi::FrameOpResult cbres = startPrimaryCommandBuffer(&frame.cmdBuf);
2748 if (cbres != QRhi::FrameOpSuccess)
2749 return cbres;
2750
2751 swapChainD->cbWrapper.cb = frame.cmdBuf;
2752
2753 QVkSwapChain::ImageResources &image(swapChainD->imageRes[swapChainD->currentImageIndex]);
2754 swapChainD->rtWrapper.d.fb = image.fb;
2755
2756 if (swapChainD->stereo) {
2758 swapChainD->imageRes[swapChainD->currentImageIndex + swapChainD->bufferCount]);
2759 swapChainD->rtWrapperRight.d.fb = image.fb;
2760 }
2761
2762 prepareNewFrame(&swapChainD->cbWrapper);
2763
2764 // Read the timestamps for the previous frame for this slot.
2765 if (frame.timestampQueryIndex >= 0) {
2766 quint64 timestamp[2] = { 0, 0 };
2767 VkResult err = df->vkGetQueryPoolResults(dev, timestampQueryPool, uint32_t(frame.timestampQueryIndex), 2,
2768 2 * sizeof(quint64), timestamp, sizeof(quint64),
2769 VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT);
2770 timestampQueryPoolMap.clearBit(frame.timestampQueryIndex / 2);
2771 frame.timestampQueryIndex = -1;
2772 if (err == VK_SUCCESS) {
2773 bool ok = false;
2774 const double elapsedSec = elapsedSecondsFromTimestamp(timestamp, &ok);
2775 if (ok)
2776 swapChainD->cbWrapper.lastGpuTime = elapsedSec;
2777 } else {
2778 qWarning("Failed to query timestamp: %d", err);
2779 }
2780 }
2781
2782 // No timestamps if the client did not opt in, or when not having at least 2 frames in flight.
2783 if (rhiFlags.testFlag(QRhi::EnableTimestamps) && swapChainD->bufferCount > 1) {
2784 int timestampQueryIdx = -1;
2785 for (int i = 0; i < timestampQueryPoolMap.size(); ++i) {
2786 if (!timestampQueryPoolMap.testBit(i)) {
2787 timestampQueryPoolMap.setBit(i);
2788 timestampQueryIdx = i * 2;
2789 break;
2790 }
2791 }
2792 if (timestampQueryIdx >= 0) {
2793 df->vkCmdResetQueryPool(frame.cmdBuf, timestampQueryPool, uint32_t(timestampQueryIdx), 2);
2794 // record timestamp at the start of the command buffer
2795 df->vkCmdWriteTimestamp(frame.cmdBuf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
2796 timestampQueryPool, uint32_t(timestampQueryIdx));
2797 frame.timestampQueryIndex = timestampQueryIdx;
2798 }
2799 }
2800
2801 return QRhi::FrameOpSuccess;
2802}
2803
2804QRhi::FrameOpResult QRhiVulkan::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags)
2805{
2806 QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain);
2807 Q_ASSERT(currentSwapChain == swapChainD);
2808
2809 auto cleanup = qScopeGuard([this, swapChainD] {
2810 inst->handle()->endFrame(swapChainD->window);
2811 });
2812
2813 recordPrimaryCommandBuffer(&swapChainD->cbWrapper);
2814
2815 int frameResIndex = swapChainD->bufferCount > 1 ? currentFrameSlot : 0;
2816 QVkSwapChain::FrameResources &frame(swapChainD->frameRes[frameResIndex]);
2817 QVkSwapChain::ImageResources &image(swapChainD->imageRes[swapChainD->currentImageIndex]);
2818
2820 VkImageMemoryBarrier presTrans = {};
2821 presTrans.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
2822 presTrans.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
2823 presTrans.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
2824 presTrans.image = image.image;
2825 presTrans.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
2826 presTrans.subresourceRange.levelCount = presTrans.subresourceRange.layerCount = 1;
2827
2829 // was not used at all (no render pass), just transition from undefined to presentable
2830 presTrans.srcAccessMask = 0;
2831 presTrans.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
2832 df->vkCmdPipelineBarrier(frame.cmdBuf,
2833 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
2834 0, 0, nullptr, 0, nullptr,
2835 1, &presTrans);
2837 // was used in a readback as transfer source, go back to presentable layout
2838 presTrans.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
2839 presTrans.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
2840 df->vkCmdPipelineBarrier(frame.cmdBuf,
2841 VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
2842 0, 0, nullptr, 0, nullptr,
2843 1, &presTrans);
2844 }
2846 }
2847
2848 // record another timestamp, when enabled
2849 if (frame.timestampQueryIndex >= 0) {
2850 df->vkCmdWriteTimestamp(frame.cmdBuf, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
2851 timestampQueryPool, uint32_t(frame.timestampQueryIndex + 1));
2852 }
2853
2854 // stop recording and submit to the queue
2855 Q_ASSERT(!frame.cmdFenceWaitable);
2856 const bool needsPresent = !flags.testFlag(QRhi::SkipPresent);
2857 QRhi::FrameOpResult submitres = endAndSubmitPrimaryCommandBuffer(frame.cmdBuf,
2858 frame.cmdFence,
2859 frame.imageSemWaitable ? &frame.imageSem : nullptr,
2860 needsPresent ? &image.drawSem : nullptr);
2861 if (submitres != QRhi::FrameOpSuccess)
2862 return submitres;
2863
2864 frame.imageSemWaitable = false;
2865 frame.cmdFenceWaitable = true;
2866
2867 if (needsPresent) {
2868 // add the Present to the queue
2869 VkPresentInfoKHR presInfo = {};
2870 presInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
2871 presInfo.swapchainCount = 1;
2872 presInfo.pSwapchains = &swapChainD->sc;
2873 presInfo.pImageIndices = &swapChainD->currentImageIndex;
2874 waitSemaphoresForPresent.append(image.drawSem);
2875 presInfo.waitSemaphoreCount = uint32_t(waitSemaphoresForPresent.count());;
2876 presInfo.pWaitSemaphores = waitSemaphoresForPresent.constData();
2877
2878 // Do platform-specific WM notification. F.ex. essential on Wayland in
2879 // order to circumvent driver frame callbacks
2880 inst->presentAboutToBeQueued(swapChainD->window);
2881
2882 VkResult err = vkQueuePresentKHR(gfxQueue, &presInfo);
2883 waitSemaphoresForPresent.clear();
2884 if (err != VK_SUCCESS) {
2885 if (err == VK_ERROR_OUT_OF_DATE_KHR) {
2886 return QRhi::FrameOpSwapChainOutOfDate;
2887 } else if (err != VK_SUBOPTIMAL_KHR) {
2888 if (err == VK_ERROR_DEVICE_LOST) {
2889 qWarning("Device loss detected in vkQueuePresentKHR()");
2890 printExtraErrorInfo(err);
2891 deviceLost = true;
2892 return QRhi::FrameOpDeviceLost;
2893 }
2894 qWarning("Failed to present: %d", err);
2895 return QRhi::FrameOpError;
2896 }
2897 }
2898
2899 // Do platform-specific WM notification. F.ex. essential on X11 in
2900 // order to prevent glitches on resizing the window.
2901 inst->presentQueued(swapChainD->window);
2902
2903 // mark the current swapchain buffer as unused from our side
2904 frame.imageAcquired = false;
2905 // and move on to the next slot
2906 currentFrameSlot = (currentFrameSlot + 1) % QVK_FRAMES_IN_FLIGHT;
2907 }
2908
2909 swapChainD->frameCount += 1;
2910 currentSwapChain = nullptr;
2911 return QRhi::FrameOpSuccess;
2912}
2913
2914void QRhiVulkan::prepareNewFrame(QRhiCommandBuffer *cb)
2915{
2916 // Now is the time to do things for frame N-F, where N is the current one,
2917 // F is QVK_FRAMES_IN_FLIGHT, because only here it is guaranteed that that
2918 // frame has completed on the GPU (due to the fence wait in beginFrame). To
2919 // decide if something is safe to handle now a simple "lastActiveFrameSlot
2920 // == currentFrameSlot" is sufficient (remember that e.g. with F==2
2921 // currentFrameSlot goes 0, 1, 0, 1, 0, ...)
2922
2924
2925 QRHI_RES(QVkCommandBuffer, cb)->resetState();
2926
2927 finishActiveReadbacks(); // last, in case the readback-completed callback issues rhi calls
2928
2930}
2931
2933{
2934 if (!*cb) {
2935 VkCommandBufferAllocateInfo cmdBufInfo = {};
2936 cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
2937 cmdBufInfo.commandPool = cmdPool[currentFrameSlot];
2938 cmdBufInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
2939 cmdBufInfo.commandBufferCount = 1;
2940
2941 VkResult err = df->vkAllocateCommandBuffers(dev, &cmdBufInfo, cb);
2942 if (err != VK_SUCCESS) {
2943 if (err == VK_ERROR_DEVICE_LOST) {
2944 qWarning("Device loss detected in vkAllocateCommandBuffers()");
2945 printExtraErrorInfo(err);
2946 deviceLost = true;
2947 return QRhi::FrameOpDeviceLost;
2948 }
2949 qWarning("Failed to allocate frame command buffer: %d", err);
2950 return QRhi::FrameOpError;
2951 }
2952 }
2953
2954 VkCommandBufferBeginInfo cmdBufBeginInfo = {};
2955 cmdBufBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
2956
2957 VkResult err = df->vkBeginCommandBuffer(*cb, &cmdBufBeginInfo);
2958 if (err != VK_SUCCESS) {
2959 if (err == VK_ERROR_DEVICE_LOST) {
2960 qWarning("Device loss detected in vkBeginCommandBuffer()");
2961 printExtraErrorInfo(err);
2962 deviceLost = true;
2963 return QRhi::FrameOpDeviceLost;
2964 }
2965 qWarning("Failed to begin frame command buffer: %d", err);
2966 return QRhi::FrameOpError;
2967 }
2968
2969 return QRhi::FrameOpSuccess;
2970}
2971
2972QRhi::FrameOpResult QRhiVulkan::endAndSubmitPrimaryCommandBuffer(VkCommandBuffer cb, VkFence cmdFence,
2973 VkSemaphore *waitSem, VkSemaphore *signalSem)
2974{
2975 VkResult err = df->vkEndCommandBuffer(cb);
2976 if (err != VK_SUCCESS) {
2977 if (err == VK_ERROR_DEVICE_LOST) {
2978 qWarning("Device loss detected in vkEndCommandBuffer()");
2979 printExtraErrorInfo(err);
2980 deviceLost = true;
2981 return QRhi::FrameOpDeviceLost;
2982 }
2983 qWarning("Failed to end frame command buffer: %d", err);
2984 return QRhi::FrameOpError;
2985 }
2986
2987 VkSubmitInfo submitInfo = {};
2988 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
2989 submitInfo.commandBufferCount = 1;
2990 submitInfo.pCommandBuffers = &cb;
2991
2992 if (waitSem)
2993 waitSemaphoresForQueueSubmit.append(*waitSem);
2994 if (signalSem)
2995 signalSemaphoresForQueueSubmit.append(*signalSem);
2996
2997 submitInfo.waitSemaphoreCount = uint32_t(waitSemaphoresForQueueSubmit.count());
2998 if (!waitSemaphoresForQueueSubmit.isEmpty()) {
2999 submitInfo.pWaitSemaphores = waitSemaphoresForQueueSubmit.constData();
3000 semaphoresWaitMasksForQueueSubmit.resize(waitSemaphoresForQueueSubmit.count(), VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
3001 submitInfo.pWaitDstStageMask = semaphoresWaitMasksForQueueSubmit.constData();
3002 }
3003 submitInfo.signalSemaphoreCount = uint32_t(signalSemaphoresForQueueSubmit.count());
3004 if (!signalSemaphoresForQueueSubmit.isEmpty()) {
3005 submitInfo.pSignalSemaphores = signalSemaphoresForQueueSubmit.constData();
3006 }
3007
3008 err = df->vkQueueSubmit(gfxQueue, 1, &submitInfo, cmdFence);
3009
3010 waitSemaphoresForQueueSubmit.clear();
3011 signalSemaphoresForQueueSubmit.clear();
3012
3013 if (err != VK_SUCCESS) {
3014 if (err == VK_ERROR_DEVICE_LOST) {
3015 qWarning("Device loss detected in vkQueueSubmit()");
3016 printExtraErrorInfo(err);
3017 deviceLost = true;
3018 return QRhi::FrameOpDeviceLost;
3019 }
3020 qWarning("Failed to submit to graphics queue: %d", err);
3021 return QRhi::FrameOpError;
3022 }
3023
3024 return QRhi::FrameOpSuccess;
3025}
3026
3028{
3029 for (QVkSwapChain *sc : std::as_const(swapchains)) {
3030 const int frameResIndex = sc->bufferCount > 1 ? frameSlot : 0;
3031 QVkSwapChain::FrameResources &frame(sc->frameRes[frameResIndex]);
3032 if (frame.cmdFenceWaitable) {
3033 VkResult err = df->vkWaitForFences(dev, 1, &frame.cmdFence, VK_TRUE, UINT64_MAX);
3034
3035 if (err != VK_SUCCESS) {
3036 if (err == VK_ERROR_DEVICE_LOST) {
3037 qWarning("Device loss detected in vkWaitForFences()");
3038 printExtraErrorInfo(err);
3039 deviceLost = true;
3040 return QRhi::FrameOpDeviceLost;
3041 }
3042 qWarning("Failed to wait for fence: %d", err);
3043 return QRhi::FrameOpError;
3044 }
3045
3046 df->vkResetFences(dev, 1, &frame.cmdFence);
3047 frame.cmdFenceWaitable = false;
3048 }
3049 }
3050
3051 if (ofr.cmdFenceWaitable[frameSlot]) {
3052 VkResult err = df->vkWaitForFences(dev, 1, &ofr.cmdFence[frameSlot], VK_TRUE, UINT64_MAX);
3053 if (err != VK_SUCCESS) {
3054 if (err == VK_ERROR_DEVICE_LOST) {
3055 qWarning("Device loss detected in vkWaitForFences()");
3056 printExtraErrorInfo(err);
3057 deviceLost = true;
3058 return QRhi::FrameOpDeviceLost;
3059 }
3060 qWarning("Failed to wait for offscreen fence: %d", err);
3061 return QRhi::FrameOpError;
3062 }
3063 df->vkResetFences(dev, 1, &ofr.cmdFence[frameSlot]);
3064 ofr.cmdFenceWaitable[frameSlot] = false;
3065 }
3066
3067 return QRhi::FrameOpSuccess;
3068}
3069
3071{
3072 QRhi::FrameOpResult waitResult = waitCommandCompletion(currentFrameSlot);
3073 if (waitResult != QRhi::FrameOpSuccess)
3074 return waitResult;
3075
3077
3078 QVkCommandBuffer *cbWrapper = ofr.cbWrapper[currentFrameSlot];
3079 QRhi::FrameOpResult cbres = startPrimaryCommandBuffer(&cbWrapper->cb);
3080 if (cbres != QRhi::FrameOpSuccess)
3081 return cbres;
3082
3083 prepareNewFrame(cbWrapper);
3084 ofr.active = true;
3085
3086 if (ofr.timestampQueryIndex[currentFrameSlot] >= 0) {
3087 quint64 timestamp[2] = { 0, 0 };
3088 VkResult err = df->vkGetQueryPoolResults(dev, timestampQueryPool,
3089 uint32_t(ofr.timestampQueryIndex[currentFrameSlot]), 2,
3090 2 * sizeof(quint64), timestamp, sizeof(quint64),
3091 VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT);
3092 timestampQueryPoolMap.clearBit(ofr.timestampQueryIndex[currentFrameSlot] / 2);
3093 ofr.timestampQueryIndex[currentFrameSlot] = -1;
3094 if (err == VK_SUCCESS) {
3095 bool ok = false;
3096 const double elapsedSec = elapsedSecondsFromTimestamp(timestamp, &ok);
3097 if (ok)
3098 cbWrapper->lastGpuTime = elapsedSec;
3099 } else {
3100 qWarning("Failed to query timestamp: %d", err);
3101 }
3102 }
3103
3104 if (rhiFlags.testFlag(QRhi::EnableTimestamps)) {
3105 int timestampQueryIdx = -1;
3106 for (int i = 0; i < timestampQueryPoolMap.size(); ++i) {
3107 if (!timestampQueryPoolMap.testBit(i)) {
3108 timestampQueryPoolMap.setBit(i);
3109 timestampQueryIdx = i * 2;
3110 break;
3111 }
3112 }
3113 if (timestampQueryIdx >= 0) {
3114 df->vkCmdResetQueryPool(cbWrapper->cb, timestampQueryPool, uint32_t(timestampQueryIdx), 2);
3115 // record timestamp at the start of the command buffer
3116 df->vkCmdWriteTimestamp(cbWrapper->cb, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
3117 timestampQueryPool, uint32_t(timestampQueryIdx));
3118 ofr.timestampQueryIndex[currentFrameSlot] = timestampQueryIdx;
3119 }
3120 }
3121
3122 *cb = cbWrapper;
3123 return QRhi::FrameOpSuccess;
3124}
3125
3127{
3128 Q_UNUSED(flags);
3129 Q_ASSERT(ofr.active);
3130 ofr.active = false;
3131
3132 QVkCommandBuffer *cbWrapper(ofr.cbWrapper[currentFrameSlot]);
3134
3135 const bool readbacksPending = !activeTextureReadbacks.isEmpty() || !activeBufferReadbacks.isEmpty();
3136
3137 // record the end timestamp, when enabled; results are read back in the
3138 // next beginOffscreenFrame for this slot, after the fence wait.
3139 if (ofr.timestampQueryIndex[currentFrameSlot] >= 0) {
3140 df->vkCmdWriteTimestamp(cbWrapper->cb, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
3141 timestampQueryPool, uint32_t(ofr.timestampQueryIndex[currentFrameSlot] + 1));
3142 }
3143
3144 if (!ofr.cmdFence[currentFrameSlot]) {
3145 VkFenceCreateInfo fenceInfo = {};
3146 fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
3147 VkResult err = df->vkCreateFence(dev, &fenceInfo, nullptr, &ofr.cmdFence[currentFrameSlot]);
3148 if (err != VK_SUCCESS) {
3149 qWarning("Failed to create command buffer fence: %d", err);
3150 return QRhi::FrameOpError;
3151 }
3152 }
3153
3154 QRhi::FrameOpResult submitres = endAndSubmitPrimaryCommandBuffer(cbWrapper->cb, ofr.cmdFence[currentFrameSlot], nullptr, nullptr);
3155 if (submitres != QRhi::FrameOpSuccess)
3156 return submitres;
3157
3158 ofr.cmdFenceWaitable[currentFrameSlot] = true;
3159
3160 // Synchronously wait only when readbacks were scheduled.
3161 if (readbacksPending) {
3162 df->vkWaitForFences(dev, 1, &ofr.cmdFence[currentFrameSlot], VK_TRUE, UINT64_MAX);
3163 df->vkResetFences(dev, 1, &ofr.cmdFence[currentFrameSlot]);
3164 ofr.cmdFenceWaitable[currentFrameSlot] = false;
3165
3166 // Here we know that executing the host-side reads for this (or any
3167 // previous) frame is safe since we waited for completion above.
3169 }
3170
3171 currentFrameSlot = (currentFrameSlot + 1) % QVK_FRAMES_IN_FLIGHT;
3172
3173 return QRhi::FrameOpSuccess;
3174}
3175
3177{
3178 QVkSwapChain *swapChainD = nullptr;
3179 if (inFrame) {
3180 // There is either a swapchain or an offscreen frame on-going.
3181 // End command recording and submit what we have.
3182 VkCommandBuffer cb;
3183 if (ofr.active) {
3184 Q_ASSERT(!currentSwapChain);
3185 QVkCommandBuffer *cbWrapper(ofr.cbWrapper[currentFrameSlot]);
3186 Q_ASSERT(cbWrapper->recordingPass == QVkCommandBuffer::NoPass);
3188 cbWrapper->resetCommands();
3189 cb = cbWrapper->cb;
3190 } else {
3191 Q_ASSERT(currentSwapChain);
3192 Q_ASSERT(currentSwapChain->cbWrapper.recordingPass == QVkCommandBuffer::NoPass);
3193 swapChainD = currentSwapChain;
3194 recordPrimaryCommandBuffer(&swapChainD->cbWrapper);
3195 swapChainD->cbWrapper.resetCommands();
3196 cb = swapChainD->cbWrapper.cb;
3197 }
3198 QRhi::FrameOpResult submitres = endAndSubmitPrimaryCommandBuffer(cb, VK_NULL_HANDLE, nullptr, nullptr);
3199 if (submitres != QRhi::FrameOpSuccess)
3200 return submitres;
3201 }
3202
3203 df->vkQueueWaitIdle(gfxQueue);
3204
3205 if (inFrame) {
3206 // The current frame slot's command pool needs to be reset.
3208 // Allocate and begin recording on a new command buffer.
3209 if (ofr.active) {
3210 startPrimaryCommandBuffer(&ofr.cbWrapper[currentFrameSlot]->cb);
3211 } else {
3212 QVkSwapChain::FrameResources &frame(swapChainD->frameRes[currentFrameSlot]);
3213 startPrimaryCommandBuffer(&frame.cmdBuf);
3214 swapChainD->cbWrapper.cb = frame.cmdBuf;
3215 }
3216 }
3217
3220
3221 return QRhi::FrameOpSuccess;
3222}
3223
3225{
3227 u.layout = 0; // unused with buffers
3228 u.access = int(bufUsage.access);
3229 u.stage = int(bufUsage.stage);
3230 return u;
3231}
3232
3234{
3236 u.layout = texUsage.layout;
3237 u.access = int(texUsage.access);
3238 u.stage = int(texUsage.stage);
3239 return u;
3240}
3241
3243{
3244 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QVkTexture, QVkRenderBuffer>(rtD->description(), rtD->d.currentResIdList))
3245 rtD->create();
3246
3247 rtD->lastActiveFrameSlot = currentFrameSlot;
3248 rtD->d.rp->lastActiveFrameSlot = currentFrameSlot;
3249 QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
3250 for (auto it = rtD->m_desc.cbeginColorAttachments(), itEnd = rtD->m_desc.cendColorAttachments(); it != itEnd; ++it) {
3251 QVkTexture *texD = QRHI_RES(QVkTexture, it->texture());
3252 QVkTexture *resolveTexD = QRHI_RES(QVkTexture, it->resolveTexture());
3253 QVkRenderBuffer *rbD = QRHI_RES(QVkRenderBuffer, it->renderBuffer());
3254 if (texD) {
3255 trackedRegisterTexture(&passResTracker, texD,
3258 texD->lastActiveFrameSlot = currentFrameSlot;
3259 } else if (rbD) {
3260 // Won't register rbD->backingTexture because it cannot be used for
3261 // anything in a renderpass, its use makes only sense in
3262 // combination with a resolveTexture.
3263 rbD->lastActiveFrameSlot = currentFrameSlot;
3264 }
3265 if (resolveTexD) {
3266 trackedRegisterTexture(&passResTracker, resolveTexD,
3269 resolveTexD->lastActiveFrameSlot = currentFrameSlot;
3270 }
3271 }
3272 if (rtD->m_desc.depthStencilBuffer()) {
3273 QVkRenderBuffer *rbD = QRHI_RES(QVkRenderBuffer, rtD->m_desc.depthStencilBuffer());
3274 Q_ASSERT(rbD->m_type == QRhiRenderBuffer::DepthStencil);
3275 // We specify no explicit VkSubpassDependency for an offscreen render
3276 // target, meaning we need an explicit barrier for the depth-stencil
3277 // buffer to avoid a write-after-write hazard (as the implicit one is
3278 // not sufficient). Textures are taken care of by the resource tracking
3279 // but that excludes the (content-wise) throwaway depth-stencil buffer.
3281 rbD->lastActiveFrameSlot = currentFrameSlot;
3282 }
3283 if (rtD->m_desc.depthTexture()) {
3284 QVkTexture *depthTexD = QRHI_RES(QVkTexture, rtD->m_desc.depthTexture());
3285 trackedRegisterTexture(&passResTracker, depthTexD,
3288 depthTexD->lastActiveFrameSlot = currentFrameSlot;
3289 }
3290 if (rtD->m_desc.depthResolveTexture()) {
3291 QVkTexture *depthResolveTexD = QRHI_RES(QVkTexture, rtD->m_desc.depthResolveTexture());
3292 trackedRegisterTexture(&passResTracker, depthResolveTexD,
3295 depthResolveTexD->lastActiveFrameSlot = currentFrameSlot;
3296 }
3297 if (rtD->m_desc.shadingRateMap()) {
3298 QVkTexture *texD = QRHI_RES(QVkShadingRateMap, rtD->m_desc.shadingRateMap())->texture;
3299 trackedRegisterTexture(&passResTracker, texD,
3302 texD->lastActiveFrameSlot = currentFrameSlot;
3303 }
3304}
3305
3306void QRhiVulkan::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
3307{
3308 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
3310
3311 enqueueResourceUpdates(cbD, resourceUpdates);
3312}
3313
3315{
3316 VkCommandBuffer secondaryCb;
3317
3318 if (!freeSecondaryCbs[currentFrameSlot].isEmpty()) {
3319 secondaryCb = freeSecondaryCbs[currentFrameSlot].last();
3320 freeSecondaryCbs[currentFrameSlot].removeLast();
3321 } else {
3322 VkCommandBufferAllocateInfo cmdBufInfo = {};
3323 cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
3324 cmdBufInfo.commandPool = cmdPool[currentFrameSlot];
3325 cmdBufInfo.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY;
3326 cmdBufInfo.commandBufferCount = 1;
3327
3328 VkResult err = df->vkAllocateCommandBuffers(dev, &cmdBufInfo, &secondaryCb);
3329 if (err != VK_SUCCESS) {
3330 qWarning("Failed to create secondary command buffer: %d", err);
3331 return VK_NULL_HANDLE;
3332 }
3333 }
3334
3335 VkCommandBufferBeginInfo cmdBufBeginInfo = {};
3336 cmdBufBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
3337 cmdBufBeginInfo.flags = rtD ? VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT : 0;
3338 VkCommandBufferInheritanceInfo cmdBufInheritInfo = {};
3339 cmdBufInheritInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
3340 cmdBufInheritInfo.subpass = 0;
3341 if (rtD) {
3342 cmdBufInheritInfo.renderPass = rtD->rp->rp;
3343 cmdBufInheritInfo.framebuffer = rtD->fb;
3344 }
3345 cmdBufBeginInfo.pInheritanceInfo = &cmdBufInheritInfo;
3346
3347 VkResult err = df->vkBeginCommandBuffer(secondaryCb, &cmdBufBeginInfo);
3348 if (err != VK_SUCCESS) {
3349 qWarning("Failed to begin secondary command buffer: %d", err);
3350 return VK_NULL_HANDLE;
3351 }
3352
3353 return secondaryCb;
3354}
3355
3357{
3358 VkResult err = df->vkEndCommandBuffer(cb);
3359 if (err != VK_SUCCESS)
3360 qWarning("Failed to end secondary command buffer: %d", err);
3361
3362 QVkCommandBuffer::Command &cmd(cbD->commands.get());
3364 cmd.args.executeSecondary.cb = cb;
3365
3368 e.lastActiveFrameSlot = currentFrameSlot;
3369 e.secondaryCommandBuffer.cb = cb;
3370 releaseQueue.append(e);
3371}
3372
3373void QRhiVulkan::beginPass(QRhiCommandBuffer *cb,
3374 QRhiRenderTarget *rt,
3375 const QColor &colorClearValue,
3376 const QRhiDepthStencilClearValue &depthStencilClearValue,
3377 QRhiResourceUpdateBatch *resourceUpdates,
3378 QRhiCommandBuffer::BeginPassFlags flags)
3379{
3380 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
3382
3383 if (resourceUpdates)
3384 enqueueResourceUpdates(cbD, resourceUpdates);
3385
3386 // Insert a TransitionPassResources into the command stream, pointing to
3387 // the tracker this pass is going to use. That's how we generate the
3388 // barriers later during recording the real VkCommandBuffer, right before
3389 // the vkCmdBeginRenderPass.
3391
3392 QVkRenderTargetData *rtD = nullptr;
3393 switch (rt->resourceType()) {
3394 case QRhiResource::SwapChainRenderTarget:
3395 rtD = &QRHI_RES(QVkSwapChainRenderTarget, rt)->d;
3396 rtD->rp->lastActiveFrameSlot = currentFrameSlot;
3397 Q_ASSERT(currentSwapChain);
3398 currentSwapChain->imageRes[currentSwapChain->currentImageIndex].lastUse =
3400 if (currentSwapChain->shadingRateMapView) {
3402 QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
3403 trackedRegisterTexture(&passResTracker, texD,
3406 texD->lastActiveFrameSlot = currentFrameSlot;
3407 }
3408 break;
3409 case QRhiResource::TextureRenderTarget:
3410 {
3412 rtD = &rtTex->d;
3414 }
3415 break;
3416 default:
3417 Q_UNREACHABLE();
3418 break;
3419 }
3420
3422 cbD->passUsesSecondaryCb = flags.testFlag(QRhiCommandBuffer::ExternalContent);
3423 cbD->currentTarget = rt;
3424
3425 // No copy operations or image layout transitions allowed after this point
3426 // (up until endPass) as we are going to begin the renderpass.
3427
3428 VkRenderPassBeginInfo rpBeginInfo = {};
3429 rpBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
3430 rpBeginInfo.renderPass = rtD->rp->rp;
3431 rpBeginInfo.framebuffer = rtD->fb;
3432 rpBeginInfo.renderArea.extent.width = uint32_t(rtD->pixelSize.width());
3433 rpBeginInfo.renderArea.extent.height = uint32_t(rtD->pixelSize.height());
3434
3435 const bool rpHasAnyClearOp = std::any_of(rtD->rp->attDescs.cbegin(), rtD->rp->attDescs.cend(),
3436 [](const VkAttachmentDescription &attDesc) {
3437 return (attDesc.loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR
3438 || attDesc.stencilLoadOp == VK_ATTACHMENT_LOAD_OP_CLEAR);
3439 });
3440
3441 QVarLengthArray<VkClearValue, (QVkRenderTargetData::MAX_COLOR_ATTACHMENTS + 1) * 2 + 1> cvs;
3442 if (rpHasAnyClearOp) {
3443 for (int i = 0; i < rtD->colorAttCount; ++i) {
3444 VkClearValue cv = {};
3445 cv.color = { { colorClearValue.redF(), colorClearValue.greenF(), colorClearValue.blueF(),
3446 colorClearValue.alphaF() } };
3447 cvs.append(cv);
3448 }
3449 for (int i = 0; i < rtD->dsAttCount; ++i) {
3450 VkClearValue cv = {};
3451 cv.depthStencil = { depthStencilClearValue.depthClearValue(), depthStencilClearValue.stencilClearValue() };
3452 cvs.append(cv);
3453 }
3454 for (int i = 0; i < rtD->resolveAttCount; ++i) {
3455 VkClearValue cv = {};
3456 cv.color = { { colorClearValue.redF(), colorClearValue.greenF(), colorClearValue.blueF(),
3457 colorClearValue.alphaF() } };
3458 cvs.append(cv);
3459 }
3460 for (int i = 0; i < rtD->dsResolveAttCount; ++i) {
3461 VkClearValue cv = {};
3462 cv.depthStencil = { depthStencilClearValue.depthClearValue(), depthStencilClearValue.stencilClearValue() };
3463 cvs.append(cv);
3464 }
3465 for (int i = 0; i < rtD->shadingRateAttCount; ++i) {
3466 VkClearValue cv = {};
3467 cv.color = { { 0.0f, 0.0f, 0.0f, 0.0f } };
3468 cvs.append(cv);
3469 }
3470 }
3471 Q_ASSERT(!rpHasAnyClearOp || cvs.size() == rtD->rp->attDescs.size());
3472 rpBeginInfo.clearValueCount = uint32_t(cvs.size());
3473
3474 QVkCommandBuffer::Command &cmd(cbD->commands.get());
3476 cmd.args.beginRenderPass.desc = rpBeginInfo;
3477 cmd.args.beginRenderPass.clearValueIndex = cbD->pools.clearValue.size();
3478 cmd.args.beginRenderPass.useSecondaryCb = cbD->passUsesSecondaryCb;
3479 cbD->pools.clearValue.append(cvs.constData(), cvs.size());
3480
3481 if (cbD->passUsesSecondaryCb)
3482 cbD->activeSecondaryCbStack.append(startSecondaryCommandBuffer(rtD));
3483
3484 if (cbD->hasShadingRateSet) {
3485 QVkCommandBuffer::Command &rateCmd(cbD->commands.get());
3487 rateCmd.args.setShadingRate.w = 1;
3488 rateCmd.args.setShadingRate.h = 1;
3489 }
3490
3492}
3493
3494void QRhiVulkan::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
3495{
3496 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
3498
3499 if (cbD->passUsesSecondaryCb) {
3500 VkCommandBuffer secondaryCb = cbD->activeSecondaryCbStack.last();
3501 cbD->activeSecondaryCbStack.removeLast();
3502 endAndEnqueueSecondaryCommandBuffer(secondaryCb, cbD);
3503 }
3504
3505 QVkCommandBuffer::Command &cmd(cbD->commands.get());
3507
3509 cbD->currentTarget = nullptr;
3510
3511 if (resourceUpdates)
3512 enqueueResourceUpdates(cbD, resourceUpdates);
3513}
3514
3515void QRhiVulkan::beginComputePass(QRhiCommandBuffer *cb,
3516 QRhiResourceUpdateBatch *resourceUpdates,
3517 QRhiCommandBuffer::BeginPassFlags flags)
3518{
3519 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
3521
3522 if (resourceUpdates)
3523 enqueueResourceUpdates(cbD, resourceUpdates);
3524
3526
3528 cbD->passUsesSecondaryCb = flags.testFlag(QRhiCommandBuffer::ExternalContent);
3529
3530 cbD->computePassState.reset();
3531
3532 if (cbD->passUsesSecondaryCb)
3533 cbD->activeSecondaryCbStack.append(startSecondaryCommandBuffer());
3534
3536}
3537
3538void QRhiVulkan::endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
3539{
3540 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
3542
3543 if (cbD->passUsesSecondaryCb) {
3544 VkCommandBuffer secondaryCb = cbD->activeSecondaryCbStack.last();
3545 cbD->activeSecondaryCbStack.removeLast();
3546 endAndEnqueueSecondaryCommandBuffer(secondaryCb, cbD);
3547 }
3548
3550
3551 if (resourceUpdates)
3552 enqueueResourceUpdates(cbD, resourceUpdates);
3553}
3554
3555void QRhiVulkan::setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps)
3556{
3558 Q_ASSERT(psD->pipeline);
3559 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
3561
3562 if (cbD->currentComputePipeline != ps || cbD->currentPipelineGeneration != psD->generation) {
3563 if (cbD->passUsesSecondaryCb) {
3564 df->vkCmdBindPipeline(cbD->activeSecondaryCbStack.last(), VK_PIPELINE_BIND_POINT_COMPUTE, psD->pipeline);
3565 } else {
3566 QVkCommandBuffer::Command &cmd(cbD->commands.get());
3568 cmd.args.bindPipeline.bindPoint = VK_PIPELINE_BIND_POINT_COMPUTE;
3569 cmd.args.bindPipeline.pipeline = psD->pipeline;
3570 }
3571
3572 cbD->currentGraphicsPipeline = nullptr;
3573 cbD->currentComputePipeline = ps;
3574 cbD->currentPipelineGeneration = psD->generation;
3575 }
3576
3577 psD->lastActiveFrameSlot = currentFrameSlot;
3578}
3579
3580template<typename T>
3581inline void qrhivk_accumulateComputeResource(T *writtenResources, QRhiResource *resource,
3582 QRhiShaderResourceBinding::Type bindingType,
3583 int loadTypeVal, int storeTypeVal, int loadStoreTypeVal)
3584{
3585 VkAccessFlags access = 0;
3586 if (bindingType == loadTypeVal) {
3587 access = VK_ACCESS_SHADER_READ_BIT;
3588 } else {
3589 access = VK_ACCESS_SHADER_WRITE_BIT;
3590 if (bindingType == loadStoreTypeVal)
3591 access |= VK_ACCESS_SHADER_READ_BIT;
3592 }
3593 auto it = writtenResources->find(resource);
3594 if (it != writtenResources->end())
3595 it->second.accessFlags |= access;
3596 else if (bindingType == storeTypeVal || bindingType == loadStoreTypeVal)
3597 writtenResources->insert(resource, { access, true });
3598}
3599
3600void QRhiVulkan::dispatch(QRhiCommandBuffer *cb, int x, int y, int z)
3601{
3602 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
3604
3605 // When there are multiple dispatches, read-after-write and
3606 // write-after-write need a barrier.
3607 QVarLengthArray<VkImageMemoryBarrier, 8> imageBarriers;
3608 QVarLengthArray<VkBufferMemoryBarrier, 8> bufferBarriers;
3609 if (cbD->currentComputeSrb) {
3610 // The key in the writtenResources map indicates that the resource was
3611 // written in a previous dispatch, whereas the value accumulates the
3612 // access mask in the current one.
3613 for (auto [res, accessAndIsNewFlag] : cbD->computePassState.writtenResources)
3614 accessAndIsNewFlag = { 0, false }; // note: accessAndIsNewFlag is a reference
3615
3616 QVkShaderResourceBindings *srbD = QRHI_RES(QVkShaderResourceBindings, cbD->currentComputeSrb);
3617 for (auto &binding : srbD->m_bindings) {
3618 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(binding);
3619 switch (b->type) {
3620 case QRhiShaderResourceBinding::ImageLoad:
3621 case QRhiShaderResourceBinding::ImageStore:
3622 case QRhiShaderResourceBinding::ImageLoadStore:
3623 qrhivk_accumulateComputeResource(&cbD->computePassState.writtenResources,
3624 b->u.simage.tex,
3625 b->type,
3626 QRhiShaderResourceBinding::ImageLoad,
3627 QRhiShaderResourceBinding::ImageStore,
3628 QRhiShaderResourceBinding::ImageLoadStore);
3629 break;
3630 case QRhiShaderResourceBinding::BufferLoad:
3631 case QRhiShaderResourceBinding::BufferStore:
3632 case QRhiShaderResourceBinding::BufferLoadStore:
3633 qrhivk_accumulateComputeResource(&cbD->computePassState.writtenResources,
3634 b->u.sbuf.buf,
3635 b->type,
3636 QRhiShaderResourceBinding::BufferLoad,
3637 QRhiShaderResourceBinding::BufferStore,
3638 QRhiShaderResourceBinding::BufferLoadStore);
3639 break;
3640 default:
3641 break;
3642 }
3643 }
3644
3645 for (auto it = cbD->computePassState.writtenResources.begin(); it != cbD->computePassState.writtenResources.end(); ) {
3646 const VkAccessFlags accessInThisDispatch = it->second.accessFlags;
3647 const bool isNewInThisDispatch = it->second.isNew;
3648 if (accessInThisDispatch && !isNewInThisDispatch) {
3649 if (it.key()->resourceType() == QRhiResource::Texture) {
3650 QVkTexture *texD = QRHI_RES(QVkTexture, it.key());
3651 VkImageMemoryBarrier barrier = {};
3652 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
3653 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
3654 // won't care about subresources, pretend the whole resource was written
3655 barrier.subresourceRange.baseMipLevel = 0;
3656 barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
3657 barrier.subresourceRange.baseArrayLayer = 0;
3658 barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
3659 barrier.oldLayout = texD->usageState.layout;
3660 barrier.newLayout = texD->usageState.layout;
3661 barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
3662 barrier.dstAccessMask = accessInThisDispatch;
3663 barrier.image = texD->image;
3664 imageBarriers.append(barrier);
3665 } else {
3666 QVkBuffer *bufD = QRHI_RES(QVkBuffer, it.key());
3667 VkBufferMemoryBarrier barrier = {};
3668 barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
3669 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
3670 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
3671 barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
3672 barrier.dstAccessMask = accessInThisDispatch;
3673 barrier.buffer = bufD->buffers[bufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0];
3674 barrier.size = VK_WHOLE_SIZE;
3675 bufferBarriers.append(barrier);
3676 }
3677 }
3678 // Anything that was previously written, but is only read now, can be
3679 // removed from the written list (because that previous write got a
3680 // corresponding barrier now).
3681 if (accessInThisDispatch == VK_ACCESS_SHADER_READ_BIT)
3682 it = cbD->computePassState.writtenResources.erase(it);
3683 else
3684 ++it;
3685 }
3686 }
3687
3688 if (cbD->passUsesSecondaryCb) {
3689 VkCommandBuffer secondaryCb = cbD->activeSecondaryCbStack.last();
3690 if (!imageBarriers.isEmpty() || !bufferBarriers.isEmpty()) {
3691 df->vkCmdPipelineBarrier(secondaryCb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
3692 0, 0, nullptr,
3693 bufferBarriers.size(), bufferBarriers.isEmpty() ? nullptr : bufferBarriers.constData(),
3694 imageBarriers.size(), imageBarriers.isEmpty() ? nullptr : imageBarriers.constData());
3695 }
3696 df->vkCmdDispatch(secondaryCb, uint32_t(x), uint32_t(y), uint32_t(z));
3697 } else {
3698 if (!imageBarriers.isEmpty() || !bufferBarriers.isEmpty()) {
3699 QVkCommandBuffer::Command &cmd(cbD->commands.get());
3701 cmd.args.imageAndBufferBarrier.srcStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
3702 cmd.args.imageAndBufferBarrier.dstStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
3703 cmd.args.imageAndBufferBarrier.imageCount = imageBarriers.size();
3704 cmd.args.imageAndBufferBarrier.imageIndex = cbD->pools.imageBarrier.size();
3705 cbD->pools.imageBarrier.append(imageBarriers.constData(), imageBarriers.size());
3706 cmd.args.imageAndBufferBarrier.bufferCount = bufferBarriers.size();
3707 cmd.args.imageAndBufferBarrier.bufferIndex = cbD->pools.bufferBarrier.size();
3708 cbD->pools.bufferBarrier.append(bufferBarriers.constData(), bufferBarriers.size());
3709 }
3710 QVkCommandBuffer::Command &cmd(cbD->commands.get());
3712 cmd.args.dispatch.x = x;
3713 cmd.args.dispatch.y = y;
3714 cmd.args.dispatch.z = z;
3715 }
3716}
3717
3718VkShaderModule QRhiVulkan::createShader(const QByteArray &spirv)
3719{
3720 VkShaderModuleCreateInfo shaderInfo = {};
3721 shaderInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
3722 shaderInfo.codeSize = size_t(spirv.size());
3723 shaderInfo.pCode = reinterpret_cast<const quint32 *>(spirv.constData());
3724 VkShaderModule shaderModule;
3725 VkResult err = df->vkCreateShaderModule(dev, &shaderInfo, nullptr, &shaderModule);
3726 if (err != VK_SUCCESS) {
3727 qWarning("Failed to create shader module: %d", err);
3728 return VK_NULL_HANDLE;
3729 }
3730 return shaderModule;
3731}
3732
3733bool QRhiVulkan::ensurePipelineCache(const void *initialData, size_t initialDataSize)
3734{
3735 if (pipelineCache)
3736 return true;
3737
3738 VkPipelineCacheCreateInfo pipelineCacheInfo = {};
3739 pipelineCacheInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
3740 pipelineCacheInfo.initialDataSize = initialDataSize;
3741 pipelineCacheInfo.pInitialData = initialData;
3742 VkResult err = df->vkCreatePipelineCache(dev, &pipelineCacheInfo, nullptr, &pipelineCache);
3743 if (err != VK_SUCCESS) {
3744 qWarning("Failed to create pipeline cache: %d", err);
3745 return false;
3746 }
3747 return true;
3748}
3749
3750void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb)
3751{
3753
3754 QVarLengthArray<VkDescriptorBufferInfo, 8> bufferInfos;
3755 using ArrayOfImageDesc = QVarLengthArray<VkDescriptorImageInfo, 8>;
3756 QVarLengthArray<ArrayOfImageDesc, 8> imageInfos;
3757 QVarLengthArray<VkWriteDescriptorSet, 12> writeInfos;
3758 QVarLengthArray<std::pair<int, int>, 12> infoIndices;
3759
3760 for (int i = 0, ie = srbD->sortedBindings.size(); i != ie; ++i) {
3761 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->sortedBindings.at(i));
3762 QVkShaderResourceBindings::BoundResourceData &bd(srbD->boundResourceData[currentFrameSlot][i]);
3763
3764 VkWriteDescriptorSet writeInfo = {};
3765 writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
3766 writeInfo.dstSet = srbD->descSets[currentFrameSlot];
3767 writeInfo.dstBinding = uint32_t(b->binding);
3768 writeInfo.descriptorCount = 1;
3769
3770 int bufferInfoIndex = -1;
3771 int imageInfoIndex = -1;
3772
3773 switch (b->type) {
3774 case QRhiShaderResourceBinding::UniformBuffer:
3775 {
3776 writeInfo.descriptorType = b->u.ubuf.hasDynamicOffset ? VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC
3777 : VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
3778 QRhiBuffer *buf = b->u.ubuf.buf;
3779 QVkBuffer *bufD = QRHI_RES(QVkBuffer, buf);
3780 bd.ubuf.id = bufD->m_id;
3781 bd.ubuf.generation = bufD->generation;
3782 VkDescriptorBufferInfo bufInfo = {};
3783 bufInfo.buffer = bufD->m_type == QRhiBuffer::Dynamic ? bufD->buffers[currentFrameSlot] : bufD->buffers[0];
3784 bufInfo.offset = b->u.ubuf.offset;
3785 bufInfo.range = b->u.ubuf.maybeSize ? b->u.ubuf.maybeSize : VK_WHOLE_SIZE;
3786 // be nice and assert when we know the vulkan device would die a horrible death due to non-aligned reads
3787 Q_ASSERT(aligned(bufInfo.offset, ubufAlign) == bufInfo.offset);
3788 bufferInfoIndex = bufferInfos.size();
3789 bufferInfos.append(bufInfo);
3790 }
3791 break;
3792 case QRhiShaderResourceBinding::SampledTexture:
3793 {
3794 const QRhiShaderResourceBinding::Data::TextureAndOrSamplerData *data = &b->u.stex;
3795 writeInfo.descriptorCount = data->count; // arrays of combined image samplers are supported
3796 writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
3797 ArrayOfImageDesc imageInfo(data->count);
3798 for (int elem = 0; elem < data->count; ++elem) {
3799 QVkTexture *texD = QRHI_RES(QVkTexture, data->texSamplers[elem].tex);
3800 QVkSampler *samplerD = QRHI_RES(QVkSampler, data->texSamplers[elem].sampler);
3801 bd.stex.d[elem].texId = texD->m_id;
3802 bd.stex.d[elem].texGeneration = texD->generation;
3803 bd.stex.d[elem].samplerId = samplerD->m_id;
3804 bd.stex.d[elem].samplerGeneration = samplerD->generation;
3805 imageInfo[elem].sampler = samplerD->sampler;
3806 imageInfo[elem].imageView = texD->imageView;
3807 imageInfo[elem].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
3808 }
3809 bd.stex.count = data->count;
3810 imageInfoIndex = imageInfos.size();
3811 imageInfos.append(imageInfo);
3812 }
3813 break;
3814 case QRhiShaderResourceBinding::Texture:
3815 {
3816 const QRhiShaderResourceBinding::Data::TextureAndOrSamplerData *data = &b->u.stex;
3817 writeInfo.descriptorCount = data->count; // arrays of (separate) images are supported
3818 writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
3819 ArrayOfImageDesc imageInfo(data->count);
3820 for (int elem = 0; elem < data->count; ++elem) {
3821 QVkTexture *texD = QRHI_RES(QVkTexture, data->texSamplers[elem].tex);
3822 bd.stex.d[elem].texId = texD->m_id;
3823 bd.stex.d[elem].texGeneration = texD->generation;
3824 bd.stex.d[elem].samplerId = 0;
3825 bd.stex.d[elem].samplerGeneration = 0;
3826 imageInfo[elem].sampler = VK_NULL_HANDLE;
3827 imageInfo[elem].imageView = texD->imageView;
3828 imageInfo[elem].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
3829 }
3830 bd.stex.count = data->count;
3831 imageInfoIndex = imageInfos.size();
3832 imageInfos.append(imageInfo);
3833 }
3834 break;
3835 case QRhiShaderResourceBinding::Sampler:
3836 {
3837 QVkSampler *samplerD = QRHI_RES(QVkSampler, b->u.stex.texSamplers[0].sampler);
3838 writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;
3839 bd.stex.d[0].texId = 0;
3840 bd.stex.d[0].texGeneration = 0;
3841 bd.stex.d[0].samplerId = samplerD->m_id;
3842 bd.stex.d[0].samplerGeneration = samplerD->generation;
3843 ArrayOfImageDesc imageInfo(1);
3844 imageInfo[0].sampler = samplerD->sampler;
3845 imageInfo[0].imageView = VK_NULL_HANDLE;
3846 imageInfo[0].imageLayout = VK_IMAGE_LAYOUT_GENERAL;
3847 imageInfoIndex = imageInfos.size();
3848 imageInfos.append(imageInfo);
3849 }
3850 break;
3851 case QRhiShaderResourceBinding::ImageLoad:
3852 case QRhiShaderResourceBinding::ImageStore:
3853 case QRhiShaderResourceBinding::ImageLoadStore:
3854 {
3855 QVkTexture *texD = QRHI_RES(QVkTexture, b->u.simage.tex);
3856 VkImageView view = texD->perLevelImageViewForLoadStore(b->u.simage.level);
3857 if (view) {
3858 writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
3859 bd.simage.id = texD->m_id;
3860 bd.simage.generation = texD->generation;
3861 ArrayOfImageDesc imageInfo(1);
3862 imageInfo[0].sampler = VK_NULL_HANDLE;
3863 imageInfo[0].imageView = view;
3864 imageInfo[0].imageLayout = VK_IMAGE_LAYOUT_GENERAL;
3865 imageInfoIndex = imageInfos.size();
3866 imageInfos.append(imageInfo);
3867 }
3868 }
3869 break;
3870 case QRhiShaderResourceBinding::BufferLoad:
3871 case QRhiShaderResourceBinding::BufferStore:
3872 case QRhiShaderResourceBinding::BufferLoadStore:
3873 {
3874 QVkBuffer *bufD = QRHI_RES(QVkBuffer, b->u.sbuf.buf);
3875 writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
3876 bd.sbuf.id = bufD->m_id;
3877 bd.sbuf.generation = bufD->generation;
3878 VkDescriptorBufferInfo bufInfo = {};
3879 bufInfo.buffer = bufD->m_type == QRhiBuffer::Dynamic ? bufD->buffers[currentFrameSlot] : bufD->buffers[0];
3880 bufInfo.offset = b->u.sbuf.offset;
3881 bufInfo.range = b->u.sbuf.maybeSize ? b->u.sbuf.maybeSize : VK_WHOLE_SIZE;
3882 bufferInfoIndex = bufferInfos.size();
3883 bufferInfos.append(bufInfo);
3884 }
3885 break;
3886 default:
3887 continue;
3888 }
3889
3890 writeInfos.append(writeInfo);
3891 infoIndices.append({ bufferInfoIndex, imageInfoIndex });
3892 }
3893
3894 for (int i = 0, writeInfoCount = writeInfos.size(); i < writeInfoCount; ++i) {
3895 const int bufferInfoIndex = infoIndices[i].first;
3896 const int imageInfoIndex = infoIndices[i].second;
3897 if (bufferInfoIndex >= 0)
3898 writeInfos[i].pBufferInfo = &bufferInfos[bufferInfoIndex];
3899 else if (imageInfoIndex >= 0)
3900 writeInfos[i].pImageInfo = imageInfos[imageInfoIndex].constData();
3901 }
3902
3903 df->vkUpdateDescriptorSets(dev, uint32_t(writeInfos.size()), writeInfos.constData(), 0, nullptr);
3904}
3905
3906static inline bool accessIsWrite(VkAccessFlags access)
3907{
3908 return (access & VK_ACCESS_SHADER_WRITE_BIT) != 0
3909 || (access & VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT) != 0
3910 || (access & VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT) != 0
3911 || (access & VK_ACCESS_TRANSFER_WRITE_BIT) != 0
3912 || (access & VK_ACCESS_HOST_WRITE_BIT) != 0
3913 || (access & VK_ACCESS_MEMORY_WRITE_BIT) != 0;
3914}
3915
3917 VkAccessFlags access, VkPipelineStageFlags stage)
3918{
3920 Q_ASSERT(access && stage);
3921 QVkBuffer::UsageState &s(bufD->usageState[slot]);
3922 if (!s.stage) {
3923 s.access = access;
3924 s.stage = stage;
3925 return;
3926 }
3927
3928 if (s.access == access && s.stage == stage) {
3929 // No need to flood with unnecessary read-after-read barriers.
3930 // Write-after-write is a different matter, however.
3931 if (!accessIsWrite(access))
3932 return;
3933 }
3934
3935 VkBufferMemoryBarrier bufMemBarrier = {};
3936 bufMemBarrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
3937 bufMemBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
3938 bufMemBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
3939 bufMemBarrier.srcAccessMask = s.access;
3940 bufMemBarrier.dstAccessMask = access;
3941 bufMemBarrier.buffer = bufD->buffers[slot];
3942 bufMemBarrier.size = VK_WHOLE_SIZE;
3943
3944 QVkCommandBuffer::Command &cmd(cbD->commands.get());
3946 cmd.args.bufferBarrier.srcStageMask = s.stage;
3947 cmd.args.bufferBarrier.dstStageMask = stage;
3948 cmd.args.bufferBarrier.count = 1;
3949 cmd.args.bufferBarrier.index = cbD->pools.bufferBarrier.size();
3950 cbD->pools.bufferBarrier.append(bufMemBarrier);
3951
3952 s.access = access;
3953 s.stage = stage;
3954}
3955
3957 VkImageLayout layout, VkAccessFlags access, VkPipelineStageFlags stage)
3958{
3960 Q_ASSERT(layout && access && stage);
3961 QVkTexture::UsageState &s(texD->usageState);
3962 if (s.access == access && s.stage == stage && s.layout == layout) {
3963 if (!accessIsWrite(access))
3964 return;
3965 }
3966
3967 VkImageMemoryBarrier barrier = {};
3968 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
3969 barrier.subresourceRange.aspectMask = aspectMaskForTextureFormat(texD->m_format);
3970 barrier.subresourceRange.baseMipLevel = 0;
3971 barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
3972 barrier.subresourceRange.baseArrayLayer = 0;
3973 barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
3974 barrier.oldLayout = s.layout; // new textures have this set to PREINITIALIZED
3975 barrier.newLayout = layout;
3976 barrier.srcAccessMask = s.access; // may be 0 but that's fine
3977 barrier.dstAccessMask = access;
3978 barrier.image = texD->image;
3979
3980 VkPipelineStageFlags srcStage = s.stage;
3981 // stage mask cannot be 0
3982 if (!srcStage)
3983 srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
3984
3985 QVkCommandBuffer::Command &cmd(cbD->commands.get());
3987 cmd.args.imageBarrier.srcStageMask = srcStage;
3988 cmd.args.imageBarrier.dstStageMask = stage;
3989 cmd.args.imageBarrier.count = 1;
3990 cmd.args.imageBarrier.index = cbD->pools.imageBarrier.size();
3991 cbD->pools.imageBarrier.append(barrier);
3992
3993 s.layout = layout;
3994 s.access = access;
3995 s.stage = stage;
3996}
3997
3999{
4001
4002 VkImageMemoryBarrier barrier = {};
4003 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
4004 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
4005 barrier.subresourceRange.baseMipLevel = 0;
4006 barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
4007 barrier.subresourceRange.baseArrayLayer = 0;
4008 barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
4009 barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
4010 barrier.newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
4011 barrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
4012 barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT
4013 | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
4014 barrier.image = rbD->image;
4015
4016 const VkPipelineStageFlags stages = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT
4017 | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
4018
4019 QVkCommandBuffer::Command &cmd(cbD->commands.get());
4021 cmd.args.imageBarrier.srcStageMask = stages;
4022 cmd.args.imageBarrier.dstStageMask = stages;
4023 cmd.args.imageBarrier.count = 1;
4024 cmd.args.imageBarrier.index = cbD->pools.imageBarrier.size();
4025 cbD->pools.imageBarrier.append(barrier);
4026}
4027
4029 VkImageLayout oldLayout, VkImageLayout newLayout,
4030 VkAccessFlags srcAccess, VkAccessFlags dstAccess,
4031 VkPipelineStageFlags srcStage, VkPipelineStageFlags dstStage,
4032 int startLayer, int layerCount,
4033 int startLevel, int levelCount)
4034{
4036 VkImageMemoryBarrier barrier = {};
4037 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
4038 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
4039 barrier.subresourceRange.baseMipLevel = uint32_t(startLevel);
4040 barrier.subresourceRange.levelCount = uint32_t(levelCount);
4041 barrier.subresourceRange.baseArrayLayer = uint32_t(startLayer);
4042 barrier.subresourceRange.layerCount = uint32_t(layerCount);
4043 barrier.oldLayout = oldLayout;
4044 barrier.newLayout = newLayout;
4045 barrier.srcAccessMask = srcAccess;
4046 barrier.dstAccessMask = dstAccess;
4047 barrier.image = image;
4048
4049 QVkCommandBuffer::Command &cmd(cbD->commands.get());
4051 cmd.args.imageBarrier.srcStageMask = srcStage;
4052 cmd.args.imageBarrier.dstStageMask = dstStage;
4053 cmd.args.imageBarrier.count = 1;
4054 cmd.args.imageBarrier.index = cbD->pools.imageBarrier.size();
4055 cbD->pools.imageBarrier.append(barrier);
4056}
4057
4058VkDeviceSize QRhiVulkan::subresUploadByteSize(const QRhiTextureSubresourceUploadDescription &subresDesc) const
4059{
4060 VkDeviceSize size = 0;
4061 const qsizetype imageSizeBytes = subresDesc.image().isNull() ?
4062 subresDesc.data().size() : subresDesc.image().sizeInBytes();
4063 if (imageSizeBytes > 0)
4064 size += aligned(VkDeviceSize(imageSizeBytes), texbufAlign);
4065 return size;
4066}
4067
4068void QRhiVulkan::prepareUploadSubres(QVkTexture *texD, int layer, int level,
4069 const QRhiTextureSubresourceUploadDescription &subresDesc,
4070 size_t *curOfs, void *mp,
4071 BufferImageCopyList *copyInfos)
4072{
4073 qsizetype copySizeBytes = 0;
4074 qsizetype imageSizeBytes = 0;
4075 const void *src = nullptr;
4076 const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
4077 const bool is1D = texD->m_flags.testFlag(QRhiTexture::OneDimensional);
4078
4079 VkBufferImageCopy copyInfo = {};
4080 copyInfo.bufferOffset = *curOfs;
4081 copyInfo.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
4082 copyInfo.imageSubresource.mipLevel = uint32_t(level);
4083 copyInfo.imageSubresource.baseArrayLayer = is3D ? 0 : uint32_t(layer);
4084 copyInfo.imageSubresource.layerCount = 1;
4085 copyInfo.imageExtent.depth = 1;
4086 if (is3D)
4087 copyInfo.imageOffset.z = uint32_t(layer);
4088 if (is1D)
4089 copyInfo.imageOffset.y = uint32_t(layer);
4090
4091 const QByteArray rawData = subresDesc.data();
4092 const QPoint dp = subresDesc.destinationTopLeft();
4093 QImage image = subresDesc.image();
4094 if (!image.isNull()) {
4095 copySizeBytes = imageSizeBytes = image.sizeInBytes();
4096 QSize size = image.size();
4097 src = image.constBits();
4098 // Scanlines in QImage are 4 byte aligned so bpl must
4099 // be taken into account for bufferRowLength.
4100 int bpc = qMax(1, image.depth() / 8);
4101 // this is in pixels, not bytes, to make it more complicated...
4102 copyInfo.bufferRowLength = uint32_t(image.bytesPerLine() / bpc);
4103 if (!subresDesc.sourceSize().isEmpty() || !subresDesc.sourceTopLeft().isNull()) {
4104 const int sx = subresDesc.sourceTopLeft().x();
4105 const int sy = subresDesc.sourceTopLeft().y();
4106 if (!subresDesc.sourceSize().isEmpty())
4107 size = subresDesc.sourceSize();
4108 size = clampedSubResourceUploadSize(size, dp, level, texD->m_pixelSize);
4109 if (size.width() == image.width()) {
4110 // No need to make a QImage copy here, can copy from the source
4111 // QImage into staging directly.
4112 src = image.constBits() + sy * image.bytesPerLine() + sx * bpc;
4113 copySizeBytes = size.height() * image.bytesPerLine();
4114 } else {
4115 image = image.copy(sx, sy, size.width(), size.height());
4116 src = image.constBits();
4117 // The staging buffer gets the slice only. The rest of the
4118 // space reserved for this mip will be unused.
4119 copySizeBytes = image.sizeInBytes();
4120 bpc = qMax(1, image.depth() / 8);
4121 copyInfo.bufferRowLength = uint32_t(image.bytesPerLine() / bpc);
4122 }
4123 } else {
4124 size = clampedSubResourceUploadSize(size, dp, level, texD->m_pixelSize);
4125 }
4126 copyInfo.imageOffset.x = dp.x();
4127 copyInfo.imageOffset.y = dp.y();
4128 copyInfo.imageExtent.width = uint32_t(size.width());
4129 copyInfo.imageExtent.height = uint32_t(size.height());
4130 copyInfos->append(copyInfo);
4131 } else if (!rawData.isEmpty() && isCompressedFormat(texD->m_format)) {
4132 copySizeBytes = imageSizeBytes = rawData.size();
4133 src = rawData.constData();
4134 QSize size = q->sizeForMipLevel(level, texD->m_pixelSize);
4135 const int subresw = size.width();
4136 const int subresh = size.height();
4137 if (!subresDesc.sourceSize().isEmpty())
4138 size = subresDesc.sourceSize();
4139 const int w = size.width();
4140 const int h = size.height();
4141 QSize blockDim;
4142 compressedFormatInfo(texD->m_format, QSize(w, h), nullptr, nullptr, &blockDim);
4143 // x and y must be multiples of the block width and height
4144 copyInfo.imageOffset.x = aligned(dp.x(), blockDim.width());
4145 copyInfo.imageOffset.y = aligned(dp.y(), blockDim.height());
4146 // width and height must be multiples of the block width and height
4147 // or x + width and y + height must equal the subresource width and height
4148 copyInfo.imageExtent.width = uint32_t(dp.x() + w == subresw ? w : aligned(w, blockDim.width()));
4149 copyInfo.imageExtent.height = uint32_t(dp.y() + h == subresh ? h : aligned(h, blockDim.height()));
4150 copyInfos->append(copyInfo);
4151 } else if (!rawData.isEmpty()) {
4152 copySizeBytes = imageSizeBytes = rawData.size();
4153 src = rawData.constData();
4154 QSize size = q->sizeForMipLevel(level, texD->m_pixelSize);
4155 if (subresDesc.dataStride()) {
4156 quint32 bytesPerPixel = 0;
4157 textureFormatInfo(texD->m_format, size, nullptr, nullptr, &bytesPerPixel);
4158 if (bytesPerPixel)
4159 copyInfo.bufferRowLength = subresDesc.dataStride() / bytesPerPixel;
4160 }
4161 if (!subresDesc.sourceSize().isEmpty())
4162 size = subresDesc.sourceSize();
4163 copyInfo.imageOffset.x = dp.x();
4164 copyInfo.imageOffset.y = dp.y();
4165 copyInfo.imageExtent.width = uint32_t(size.width());
4166 copyInfo.imageExtent.height = uint32_t(size.height());
4167 copyInfos->append(copyInfo);
4168 } else {
4169 qWarning("Invalid texture upload for %p layer=%d mip=%d", texD, layer, level);
4170 }
4171
4172 if (src) {
4173 memcpy(reinterpret_cast<char *>(mp) + *curOfs, src, size_t(copySizeBytes));
4174 *curOfs += aligned(VkDeviceSize(imageSizeBytes), texbufAlign);
4175 }
4176}
4177
4179{
4180 if (err == VK_ERROR_DEVICE_LOST)
4182 if (err == VK_ERROR_OUT_OF_DEVICE_MEMORY)
4183 qWarning() << "Out of device memory, current allocator statistics are" << statistics();
4184}
4185
4187{
4188#ifdef VK_EXT_device_fault
4189 if (!dev || !caps.deviceFault || !vkGetDeviceFaultInfoEXT)
4190 return;
4191
4192 VkDeviceFaultCountsEXT faultCounts{};
4193 faultCounts.sType = VK_STRUCTURE_TYPE_DEVICE_FAULT_COUNTS_EXT;
4194 faultCounts.pNext = nullptr;
4195
4196 VkResult result = vkGetDeviceFaultInfoEXT(dev, &faultCounts, nullptr);
4197 if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
4198 qWarning("vkGetDeviceFaultInfoEXT failed with %d", result);
4199 return;
4200 }
4201 faultCounts.vendorBinarySize = 0;
4202
4203 QVarLengthArray<VkDeviceFaultAddressInfoEXT> addressInfos;
4204 addressInfos.resize(faultCounts.addressInfoCount);
4205
4206 QVarLengthArray<VkDeviceFaultVendorInfoEXT> vendorInfos;
4207 vendorInfos.resize(faultCounts.vendorInfoCount);
4208
4209 VkDeviceFaultInfoEXT info{};
4210 info.sType = VK_STRUCTURE_TYPE_DEVICE_FAULT_INFO_EXT;
4211 info.pNext = nullptr;
4212 info.pAddressInfos = addressInfos.isEmpty() ? nullptr : addressInfos.data();
4213 info.pVendorInfos = vendorInfos.isEmpty() ? nullptr : vendorInfos.data();
4214 info.pVendorBinaryData = nullptr;
4215
4216 result = vkGetDeviceFaultInfoEXT(dev, &faultCounts, &info);
4217 if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
4218 qWarning("vkGetDeviceFaultInfoEXT failed with %d", result);
4219 return;
4220 }
4221
4222 const char *desc = info.description[0] ? info.description : "n/a";
4223 qWarning("VK_ERROR_DEVICE_LOST (VK_EXT_device_fault): %u address infos, %u vendor infos, %llu bytes vendor binary: %s",
4224 faultCounts.addressInfoCount,
4225 faultCounts.vendorInfoCount,
4226 (unsigned long long)faultCounts.vendorBinarySize,
4227 desc);
4228
4229 for (uint32_t i = 0; i < faultCounts.addressInfoCount; ++i) {
4230 const auto &a = addressInfos[i];
4231 auto addressTypeString = [](const VkDeviceFaultAddressTypeEXT type) {
4232 switch (type) {
4233 case VK_DEVICE_FAULT_ADDRESS_TYPE_NONE_EXT: return "NONE";
4234 case VK_DEVICE_FAULT_ADDRESS_TYPE_READ_INVALID_EXT: return "READ_INVALID";
4235 case VK_DEVICE_FAULT_ADDRESS_TYPE_WRITE_INVALID_EXT: return "WRITE_INVALID";
4236 case VK_DEVICE_FAULT_ADDRESS_TYPE_EXECUTE_INVALID_EXT: return "EXECUTE_INVALID";
4237 case VK_DEVICE_FAULT_ADDRESS_TYPE_INSTRUCTION_POINTER_UNKNOWN_EXT: return "INSTRUCTION_POINTER_UNKNOWN";
4238 case VK_DEVICE_FAULT_ADDRESS_TYPE_INSTRUCTION_POINTER_INVALID_EXT: return "INSTRUCTION_POINTER_INVALID";
4239 case VK_DEVICE_FAULT_ADDRESS_TYPE_INSTRUCTION_POINTER_FAULT_EXT: return "INSTRUCTION_POINTER_FAULT";
4240 default: return "UNKNOWN";
4241 };
4242 };
4243 qWarning(" AddressInfo[%02u]: type=%s addr=0x%llx precision=%llu",
4244 i,
4245 addressTypeString(a.addressType),
4246 (unsigned long long)a.reportedAddress,
4247 (unsigned long long)a.addressPrecision);
4248 }
4249
4250 for (uint32_t i = 0; i < faultCounts.vendorInfoCount; ++i) {
4251 const auto &v = vendorInfos[i];
4252 qWarning(" VendorInfo[%02u]: code=%llu data=%llu desc=%s",
4253 i,
4254 (unsigned long long)v.vendorFaultCode,
4255 (unsigned long long)v.vendorFaultData,
4256 v.description);
4257 }
4258#endif // VK_EXT_device_fault
4259}
4260
4261void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdateBatch *resourceUpdates)
4262{
4264
4265 for (int opIdx = 0; opIdx < ud->activeBufferOpCount; ++opIdx) {
4266 const QRhiResourceUpdateBatchPrivate::BufferOp &u(ud->bufferOps[opIdx]);
4268 QVkBuffer *bufD = QRHI_RES(QVkBuffer, u.buf);
4269 Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
4270 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
4271 if (u.offset == 0 && u.data.size() == bufD->m_size)
4272 bufD->pendingDynamicUpdates[i].clear();
4273 bufD->pendingDynamicUpdates[i].append({ u.offset, u.data });
4274 }
4276 QVkBuffer *bufD = QRHI_RES(QVkBuffer, u.buf);
4277 Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic);
4278 Q_ASSERT(u.offset + u.data.size() <= bufD->m_size);
4279
4280 if (!bufD->stagingBuffers[currentFrameSlot]) {
4281 VkBufferCreateInfo bufferInfo = {};
4282 bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
4283 // must cover the entire buffer - this way multiple, partial updates per frame
4284 // are supported even when the staging buffer is reused (Static)
4285 bufferInfo.size = bufD->m_size;
4286 bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
4287
4288 VmaAllocationCreateInfo allocInfo = {};
4289 allocInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
4290
4291 VmaAllocation allocation;
4292 VkResult err = vmaCreateBuffer(toVmaAllocator(allocator), &bufferInfo, &allocInfo,
4293 &bufD->stagingBuffers[currentFrameSlot], &allocation, nullptr);
4294 if (err == VK_SUCCESS) {
4295 bufD->stagingAllocations[currentFrameSlot] = allocation;
4296 setAllocationName(allocation, bufD->name());
4297 } else {
4298 qWarning("Failed to create staging buffer of size %u: %d", bufD->m_size, err);
4299 printExtraErrorInfo(err);
4300 continue;
4301 }
4302 }
4303
4304 VkResult err = vmaCopyMemoryToAllocation(toVmaAllocator(allocator), u.data.constData(),
4305 toVmaAllocation(bufD->stagingAllocations[currentFrameSlot]),
4306 u.offset, u.data.size());
4307 if (err != VK_SUCCESS) {
4308 qWarning("Failed to copy memory to buffer: %d", err);
4309 continue;
4310 }
4311
4312 trackedBufferBarrier(cbD, bufD, 0,
4313 VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
4314
4315 VkBufferCopy copyInfo = {};
4316 copyInfo.srcOffset = u.offset;
4317 copyInfo.dstOffset = u.offset;
4318 copyInfo.size = u.data.size();
4319
4320 QVkCommandBuffer::Command &cmd(cbD->commands.get());
4322 cmd.args.copyBuffer.src = bufD->stagingBuffers[currentFrameSlot];
4323 cmd.args.copyBuffer.dst = bufD->buffers[0];
4324 cmd.args.copyBuffer.desc = copyInfo;
4325
4326 // Where's the barrier for read-after-write? (assuming the common case
4327 // of binding this buffer as vertex/index, or, less likely, as uniform
4328 // buffer, in a renderpass later on) That is handled by the pass
4329 // resource tracking: the appropriate pipeline barrier will be
4330 // generated and recorded right before the renderpass, that binds this
4331 // buffer in one of its commands, gets its BeginRenderPass recorded.
4332
4333 bufD->lastActiveFrameSlot = currentFrameSlot;
4334
4335 if (bufD->m_type == QRhiBuffer::Immutable) {
4338 e.lastActiveFrameSlot = currentFrameSlot;
4339 e.stagingBuffer.stagingBuffer = bufD->stagingBuffers[currentFrameSlot];
4340 e.stagingBuffer.stagingAllocation = bufD->stagingAllocations[currentFrameSlot];
4341 bufD->stagingBuffers[currentFrameSlot] = VK_NULL_HANDLE;
4342 bufD->stagingAllocations[currentFrameSlot] = nullptr;
4343 releaseQueue.append(e);
4344 }
4346 QVkBuffer *bufD = QRHI_RES(QVkBuffer, u.buf);
4347 if (bufD->m_type == QRhiBuffer::Dynamic) {
4348 executeBufferHostWritesForSlot(bufD, currentFrameSlot);
4349 u.result->data.resizeForOverwrite(u.readSize);
4350 VkResult err = vmaCopyAllocationToMemory(toVmaAllocator(allocator),
4351 toVmaAllocation(bufD->allocations[currentFrameSlot]),
4352 u.offset, u.result->data.data(), u.readSize);
4353 if (err != VK_SUCCESS) {
4354 qWarning("Failed to copy memory from buffer: %d", err);
4355 u.result->data.clear();
4356 }
4357 if (u.result->completed)
4358 u.result->completed();
4359 } else {
4360 // Non-Dynamic buffers may not be host visible, so have to
4361 // create a readback buffer, enqueue a copy from
4362 // bufD->buffers[0] to this buffer, and then once the command
4363 // buffer completes, copy the data out of the host visible
4364 // readback buffer. Quite similar to what we do for texture
4365 // readbacks.
4366 BufferReadback readback;
4367 readback.activeFrameSlot = currentFrameSlot;
4368 readback.result = u.result;
4369 readback.byteSize = u.readSize;
4370
4371 VkBufferCreateInfo bufferInfo = {};
4372 bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
4373 bufferInfo.size = readback.byteSize;
4374 bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
4375
4376 VmaAllocationCreateInfo allocInfo = {};
4377 allocInfo.usage = VMA_MEMORY_USAGE_GPU_TO_CPU;
4378
4379 VmaAllocation allocation;
4380 VkResult err = vmaCreateBuffer(toVmaAllocator(allocator), &bufferInfo, &allocInfo, &readback.stagingBuf, &allocation, nullptr);
4381 if (err == VK_SUCCESS) {
4382 readback.stagingAlloc = allocation;
4383 setAllocationName(allocation, bufD->name());
4384 } else {
4385 qWarning("Failed to create readback buffer of size %u: %d", readback.byteSize, err);
4386 printExtraErrorInfo(err);
4387 continue;
4388 }
4389
4390 trackedBufferBarrier(cbD, bufD, 0, VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
4391
4392 VkBufferCopy copyInfo = {};
4393 copyInfo.srcOffset = u.offset;
4394 copyInfo.size = u.readSize;
4395
4396 QVkCommandBuffer::Command &cmd(cbD->commands.get());
4398 cmd.args.copyBuffer.src = bufD->buffers[0];
4399 cmd.args.copyBuffer.dst = readback.stagingBuf;
4400 cmd.args.copyBuffer.desc = copyInfo;
4401
4402 bufD->lastActiveFrameSlot = currentFrameSlot;
4403
4404 activeBufferReadbacks.append(readback);
4405 }
4406 }
4407 }
4408
4409 for (int opIdx = 0; opIdx < ud->activeTextureOpCount; ++opIdx) {
4410 const QRhiResourceUpdateBatchPrivate::TextureOp &u(ud->textureOps[opIdx]);
4412 QVkTexture *utexD = QRHI_RES(QVkTexture, u.dst);
4413 // batch into a single staging buffer and a single CopyBufferToImage with multiple copyInfos
4414 VkDeviceSize stagingSize = 0;
4415 for (int layer = 0, maxLayer = u.subresDesc.size(); layer < maxLayer; ++layer) {
4416 for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
4417 for (const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level]))
4418 stagingSize += subresUploadByteSize(subresDesc);
4419 }
4420 }
4421
4422 Q_ASSERT(!utexD->stagingBuffers[currentFrameSlot]);
4423 VkBufferCreateInfo bufferInfo = {};
4424 bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
4425 bufferInfo.size = stagingSize;
4426 bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
4427
4428 VmaAllocationCreateInfo allocInfo = {};
4429 allocInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
4430
4431 VmaAllocation allocation;
4432 VkResult err = vmaCreateBuffer(toVmaAllocator(allocator), &bufferInfo, &allocInfo,
4433 &utexD->stagingBuffers[currentFrameSlot], &allocation, nullptr);
4434 if (err != VK_SUCCESS) {
4435 qWarning("Failed to create image staging buffer of size %d: %d", int(stagingSize), err);
4436 printExtraErrorInfo(err);
4437 continue;
4438 }
4439 utexD->stagingAllocations[currentFrameSlot] = allocation;
4440 setAllocationName(allocation, utexD->name());
4441
4442 BufferImageCopyList copyInfos;
4443 size_t curOfs = 0;
4444 void *mp = nullptr;
4445 VmaAllocation a = toVmaAllocation(utexD->stagingAllocations[currentFrameSlot]);
4446 err = vmaMapMemory(toVmaAllocator(allocator), a, &mp);
4447 if (err != VK_SUCCESS) {
4448 qWarning("Failed to map image data: %d", err);
4449 vmaDestroyBuffer(toVmaAllocator(allocator), utexD->stagingBuffers[currentFrameSlot], a);
4450 utexD->stagingBuffers[currentFrameSlot] = VK_NULL_HANDLE;
4451 utexD->stagingAllocations[currentFrameSlot] = nullptr;
4452 continue;
4453 }
4454
4455 for (int layer = 0, maxLayer = u.subresDesc.size(); layer < maxLayer; ++layer) {
4456 for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
4457 const QList<QRhiTextureSubresourceUploadDescription> &srd(u.subresDesc[layer][level]);
4458 if (srd.isEmpty())
4459 continue;
4460 for (const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(srd)) {
4461 prepareUploadSubres(utexD, layer, level,
4462 subresDesc, &curOfs, mp, &copyInfos);
4463 }
4464 }
4465 }
4466 vmaFlushAllocation(toVmaAllocator(allocator), a, 0, stagingSize);
4467 vmaUnmapMemory(toVmaAllocator(allocator), a);
4468
4469 trackedImageBarrier(cbD, utexD, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
4470 VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
4471
4472 QVkCommandBuffer::Command &cmd(cbD->commands.get());
4474 cmd.args.copyBufferToImage.src = utexD->stagingBuffers[currentFrameSlot];
4475 cmd.args.copyBufferToImage.dst = utexD->image;
4476 cmd.args.copyBufferToImage.dstLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
4477 cmd.args.copyBufferToImage.count = copyInfos.size();
4478 cmd.args.copyBufferToImage.bufferImageCopyIndex = cbD->pools.bufferImageCopy.size();
4479 cbD->pools.bufferImageCopy.append(copyInfos.constData(), copyInfos.size());
4480
4481 // no reuse of staging, this is intentional
4484 e.lastActiveFrameSlot = currentFrameSlot;
4485 e.stagingBuffer.stagingBuffer = utexD->stagingBuffers[currentFrameSlot];
4486 e.stagingBuffer.stagingAllocation = utexD->stagingAllocations[currentFrameSlot];
4487 utexD->stagingBuffers[currentFrameSlot] = VK_NULL_HANDLE;
4488 utexD->stagingAllocations[currentFrameSlot] = nullptr;
4489 releaseQueue.append(e);
4490
4491 // Similarly to buffers, transitioning away from DST is done later,
4492 // when a renderpass using the texture is encountered.
4493
4494 utexD->lastActiveFrameSlot = currentFrameSlot;
4496 Q_ASSERT(u.src && u.dst);
4497 if (u.src == u.dst) {
4498 qWarning("Texture copy with matching source and destination is not supported");
4499 continue;
4500 }
4501 QVkTexture *srcD = QRHI_RES(QVkTexture, u.src);
4502 QVkTexture *dstD = QRHI_RES(QVkTexture, u.dst);
4503 const bool srcIs3D = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
4504 const bool dstIs3D = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
4505
4506 VkImageCopy region = {};
4507 region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
4508 region.srcSubresource.mipLevel = uint32_t(u.desc.sourceLevel());
4509 region.srcSubresource.baseArrayLayer = srcIs3D ? 0 : uint32_t(u.desc.sourceLayer());
4510 region.srcSubresource.layerCount = 1;
4511
4512 region.srcOffset.x = u.desc.sourceTopLeft().x();
4513 region.srcOffset.y = u.desc.sourceTopLeft().y();
4514 if (srcIs3D)
4515 region.srcOffset.z = uint32_t(u.desc.sourceLayer());
4516
4517 region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
4518 region.dstSubresource.mipLevel = uint32_t(u.desc.destinationLevel());
4519 region.dstSubresource.baseArrayLayer = dstIs3D ? 0 : uint32_t(u.desc.destinationLayer());
4520 region.dstSubresource.layerCount = 1;
4521
4522 region.dstOffset.x = u.desc.destinationTopLeft().x();
4523 region.dstOffset.y = u.desc.destinationTopLeft().y();
4524 if (dstIs3D)
4525 region.dstOffset.z = uint32_t(u.desc.destinationLayer());
4526
4527 const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize);
4528 const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize();
4529 region.extent.width = uint32_t(copySize.width());
4530 region.extent.height = uint32_t(copySize.height());
4531 region.extent.depth = 1;
4532
4533 trackedImageBarrier(cbD, srcD, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
4534 VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
4535 trackedImageBarrier(cbD, dstD, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
4536 VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
4537
4538 QVkCommandBuffer::Command &cmd(cbD->commands.get());
4540 cmd.args.copyImage.src = srcD->image;
4541 cmd.args.copyImage.srcLayout = srcD->usageState.layout;
4542 cmd.args.copyImage.dst = dstD->image;
4543 cmd.args.copyImage.dstLayout = dstD->usageState.layout;
4544 cmd.args.copyImage.desc = region;
4545
4546 srcD->lastActiveFrameSlot = dstD->lastActiveFrameSlot = currentFrameSlot;
4548 TextureReadback readback;
4549 readback.activeFrameSlot = currentFrameSlot;
4550 readback.desc = u.rb;
4551 readback.result = u.result;
4552
4553 QVkTexture *texD = QRHI_RES(QVkTexture, u.rb.texture());
4554 QVkSwapChain *swapChainD = nullptr;
4555 bool is3D = false;
4556 if (texD) {
4557 if (texD->samples > VK_SAMPLE_COUNT_1_BIT) {
4558 qWarning("Multisample texture cannot be read back");
4559 continue;
4560 }
4561 is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
4562 if (u.rb.rect().isValid())
4563 readback.rect = u.rb.rect();
4564 else
4565 readback.rect = QRect({0, 0}, q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize));
4566 readback.format = texD->m_format;
4567 texD->lastActiveFrameSlot = currentFrameSlot;
4568 } else {
4569 Q_ASSERT(currentSwapChain);
4570 swapChainD = QRHI_RES(QVkSwapChain, currentSwapChain);
4571 if (!swapChainD->supportsReadback) {
4572 qWarning("Swapchain does not support readback");
4573 continue;
4574 }
4575 if (u.rb.rect().isValid())
4576 readback.rect = u.rb.rect();
4577 else
4578 readback.rect = QRect({0, 0}, swapChainD->pixelSize);
4579 readback.format = swapchainReadbackTextureFormat(swapChainD->colorFormat, nullptr);
4580 if (readback.format == QRhiTexture::UnknownFormat)
4581 continue;
4582
4583 // Multisample swapchains need nothing special since resolving
4584 // happens when ending a renderpass.
4585 }
4586 textureFormatInfo(readback.format, readback.rect.size(), nullptr, &readback.byteSize, nullptr);
4587
4588 // Create a host visible readback buffer.
4589 VkBufferCreateInfo bufferInfo = {};
4590 bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
4591 bufferInfo.size = readback.byteSize;
4592 bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
4593
4594 VmaAllocationCreateInfo allocInfo = {};
4595 allocInfo.usage = VMA_MEMORY_USAGE_GPU_TO_CPU;
4596
4597 VmaAllocation allocation;
4598 VkResult err = vmaCreateBuffer(toVmaAllocator(allocator), &bufferInfo, &allocInfo, &readback.stagingBuf, &allocation, nullptr);
4599 if (err == VK_SUCCESS) {
4600 readback.stagingAlloc = allocation;
4601 setAllocationName(allocation, texD ? texD->name() : swapChainD->name());
4602 } else {
4603 qWarning("Failed to create readback buffer of size %u: %d", readback.byteSize, err);
4604 printExtraErrorInfo(err);
4605 continue;
4606 }
4607
4608 // Copy from the (optimal and not host visible) image into the buffer.
4609 VkBufferImageCopy copyDesc = {};
4610 copyDesc.bufferOffset = 0;
4611 copyDesc.imageSubresource.aspectMask = aspectMaskForTextureFormat(readback.format);
4612 copyDesc.imageSubresource.mipLevel = uint32_t(u.rb.level());
4613 copyDesc.imageSubresource.baseArrayLayer = is3D ? 0 : uint32_t(u.rb.layer());
4614 copyDesc.imageSubresource.layerCount = 1;
4615 copyDesc.imageOffset.x = readback.rect.x();
4616 copyDesc.imageOffset.y = readback.rect.y();
4617 if (is3D)
4618 copyDesc.imageOffset.z = u.rb.layer();
4619 copyDesc.imageExtent.width = uint32_t(readback.rect.width());
4620 copyDesc.imageExtent.height = uint32_t(readback.rect.height());
4621 copyDesc.imageExtent.depth = 1;
4622
4623 if (texD) {
4624 trackedImageBarrier(cbD, texD, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
4625 VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
4626 QVkCommandBuffer::Command &cmd(cbD->commands.get());
4628 cmd.args.copyImageToBuffer.src = texD->image;
4629 cmd.args.copyImageToBuffer.srcLayout = texD->usageState.layout;
4630 cmd.args.copyImageToBuffer.dst = readback.stagingBuf;
4631 cmd.args.copyImageToBuffer.desc = copyDesc;
4632 } else {
4633 // use the swapchain image
4634 QVkSwapChain::ImageResources &imageRes(swapChainD->imageRes[swapChainD->currentImageIndex]);
4635 VkImage image = imageRes.image;
4638 qWarning("Attempted to read back undefined swapchain image content, "
4639 "results are undefined. (do a render pass first)");
4640 }
4641 subresourceBarrier(cbD, image,
4642 VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
4643 VK_ACCESS_MEMORY_READ_BIT, VK_ACCESS_TRANSFER_READ_BIT,
4644 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
4645 0, 1,
4646 0, 1);
4648 }
4649
4650 QVkCommandBuffer::Command &cmd(cbD->commands.get());
4652 cmd.args.copyImageToBuffer.src = image;
4653 cmd.args.copyImageToBuffer.srcLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
4654 cmd.args.copyImageToBuffer.dst = readback.stagingBuf;
4655 cmd.args.copyImageToBuffer.desc = copyDesc;
4656 }
4657
4658 activeTextureReadbacks.append(readback);
4660 QVkTexture *utexD = QRHI_RES(QVkTexture, u.dst);
4661 Q_ASSERT(utexD->m_flags.testFlag(QRhiTexture::UsedWithGenerateMips));
4662 const bool isCube = utexD->m_flags.testFlag(QRhiTexture::CubeMap);
4663 const bool isArray = utexD->m_flags.testFlag(QRhiTexture::TextureArray);
4664 const bool is3D = utexD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
4665
4666 VkImageLayout origLayout = utexD->usageState.layout;
4667 VkAccessFlags origAccess = utexD->usageState.access;
4668 VkPipelineStageFlags origStage = utexD->usageState.stage;
4669 if (!origStage)
4670 origStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
4671
4672 for (int layer = 0; layer < (isCube ? 6 : (isArray ? qMax(0, utexD->m_arraySize) : 1)); ++layer) {
4673 int w = utexD->m_pixelSize.width();
4674 int h = utexD->m_pixelSize.height();
4675 int depth = is3D ? qMax(1, utexD->m_depth) : 1;
4676 for (int level = 1; level < int(utexD->mipLevelCount); ++level) {
4677 if (level == 1) {
4678 subresourceBarrier(cbD, utexD->image,
4679 origLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
4680 origAccess, VK_ACCESS_TRANSFER_READ_BIT,
4681 origStage, VK_PIPELINE_STAGE_TRANSFER_BIT,
4682 layer, 1,
4683 level - 1, 1);
4684 } else {
4685 subresourceBarrier(cbD, utexD->image,
4686 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
4687 VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
4688 VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
4689 layer, 1,
4690 level - 1, 1);
4691 }
4692
4693 subresourceBarrier(cbD, utexD->image,
4694 origLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
4695 origAccess, VK_ACCESS_TRANSFER_WRITE_BIT,
4696 origStage, VK_PIPELINE_STAGE_TRANSFER_BIT,
4697 layer, 1,
4698 level, 1);
4699
4700 VkImageBlit region = {};
4701 region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
4702 region.srcSubresource.mipLevel = uint32_t(level) - 1;
4703 region.srcSubresource.baseArrayLayer = uint32_t(layer);
4704 region.srcSubresource.layerCount = 1;
4705
4706 region.srcOffsets[1].x = qMax(1, w);
4707 region.srcOffsets[1].y = qMax(1, h);
4708 region.srcOffsets[1].z = qMax(1, depth);
4709
4710 region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
4711 region.dstSubresource.mipLevel = uint32_t(level);
4712 region.dstSubresource.baseArrayLayer = uint32_t(layer);
4713 region.dstSubresource.layerCount = 1;
4714
4715 region.dstOffsets[1].x = qMax(1, w >> 1);
4716 region.dstOffsets[1].y = qMax(1, h >> 1);
4717 region.dstOffsets[1].z = qMax(1, depth >> 1);
4718
4719 QVkCommandBuffer::Command &cmd(cbD->commands.get());
4721 cmd.args.blitImage.src = utexD->image;
4722 cmd.args.blitImage.srcLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
4723 cmd.args.blitImage.dst = utexD->image;
4724 cmd.args.blitImage.dstLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
4725 cmd.args.blitImage.filter = VK_FILTER_LINEAR;
4726 cmd.args.blitImage.desc = region;
4727
4728 w >>= 1;
4729 h >>= 1;
4730 depth >>= 1;
4731 }
4732
4733 if (utexD->mipLevelCount > 1) {
4734 subresourceBarrier(cbD, utexD->image,
4735 VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, origLayout,
4736 VK_ACCESS_TRANSFER_READ_BIT, origAccess,
4737 VK_PIPELINE_STAGE_TRANSFER_BIT, origStage,
4738 layer, 1,
4739 0, int(utexD->mipLevelCount) - 1);
4740 subresourceBarrier(cbD, utexD->image,
4741 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, origLayout,
4742 VK_ACCESS_TRANSFER_WRITE_BIT, origAccess,
4743 VK_PIPELINE_STAGE_TRANSFER_BIT, origStage,
4744 layer, 1,
4745 int(utexD->mipLevelCount) - 1, 1);
4746 }
4747 }
4748 utexD->lastActiveFrameSlot = currentFrameSlot;
4749 }
4750 }
4751
4752 ud->free();
4753}
4754
4756{
4757 if (bufD->pendingDynamicUpdates[slot].isEmpty())
4758 return;
4759
4760 Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
4761 void *p = nullptr;
4762 VmaAllocation a = toVmaAllocation(bufD->allocations[slot]);
4763 // The vmaMap/Unmap are basically a no-op when persistently mapped since it
4764 // refcounts; this is great because we don't need to care if the allocation
4765 // was created as persistently mapped or not.
4766 VkResult err = vmaMapMemory(toVmaAllocator(allocator), a, &p);
4767 if (err != VK_SUCCESS) {
4768 qWarning("Failed to map buffer: %d", err);
4769 return;
4770 }
4771 quint32 changeBegin = UINT32_MAX;
4772 quint32 changeEnd = 0;
4773 for (const QVkBuffer::DynamicUpdate &u : std::as_const(bufD->pendingDynamicUpdates[slot])) {
4774 memcpy(static_cast<char *>(p) + u.offset, u.data.constData(), u.data.size());
4775 if (u.offset < changeBegin)
4776 changeBegin = u.offset;
4777 if (u.offset + u.data.size() > changeEnd)
4778 changeEnd = u.offset + u.data.size();
4779 }
4780 if (changeBegin < UINT32_MAX && changeBegin < changeEnd)
4781 vmaFlushAllocation(toVmaAllocator(allocator), a, changeBegin, changeEnd - changeBegin);
4782 vmaUnmapMemory(toVmaAllocator(allocator), a);
4783
4784 bufD->pendingDynamicUpdates[slot].clear();
4785}
4786
4787static void qrhivk_releaseBuffer(const QRhiVulkan::DeferredReleaseEntry &e, void *allocator)
4788{
4789 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
4790 vmaDestroyBuffer(toVmaAllocator(allocator), e.buffer.buffers[i], toVmaAllocation(e.buffer.allocations[i]));
4791 vmaDestroyBuffer(toVmaAllocator(allocator), e.buffer.stagingBuffers[i], toVmaAllocation(e.buffer.stagingAllocations[i]));
4792 }
4793}
4794
4795static void qrhivk_releaseRenderBuffer(const QRhiVulkan::DeferredReleaseEntry &e, VkDevice dev, QVulkanDeviceFunctions *df)
4796{
4797 df->vkDestroyImageView(dev, e.renderBuffer.imageView, nullptr);
4798 df->vkDestroyImage(dev, e.renderBuffer.image, nullptr);
4799 df->vkFreeMemory(dev, e.renderBuffer.memory, nullptr);
4800}
4801
4802static void qrhivk_releaseTexture(const QRhiVulkan::DeferredReleaseEntry &e, VkDevice dev, QVulkanDeviceFunctions *df, void *allocator)
4803{
4804 df->vkDestroyImageView(dev, e.texture.imageView, nullptr);
4805 vmaDestroyImage(toVmaAllocator(allocator), e.texture.image, toVmaAllocation(e.texture.allocation));
4806 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
4807 vmaDestroyBuffer(toVmaAllocator(allocator), e.texture.stagingBuffers[i], toVmaAllocation(e.texture.stagingAllocations[i]));
4808 for (int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i) {
4809 if (e.texture.extraImageViews[i])
4810 df->vkDestroyImageView(dev, e.texture.extraImageViews[i], nullptr);
4811 }
4812}
4813
4814static void qrhivk_releaseSampler(const QRhiVulkan::DeferredReleaseEntry &e, VkDevice dev, QVulkanDeviceFunctions *df)
4815{
4816 df->vkDestroySampler(dev, e.sampler.sampler, nullptr);
4817}
4818
4820{
4821 for (int i = releaseQueue.size() - 1; i >= 0; --i) {
4822 const QRhiVulkan::DeferredReleaseEntry &e(releaseQueue[i]);
4823 if (forced || currentFrameSlot == e.lastActiveFrameSlot || e.lastActiveFrameSlot < 0) {
4824 switch (e.type) {
4826 df->vkDestroyPipeline(dev, e.pipelineState.pipeline, nullptr);
4827 df->vkDestroyPipelineLayout(dev, e.pipelineState.layout, nullptr);
4828 break;
4830 df->vkDestroyDescriptorSetLayout(dev, e.shaderResourceBindings.layout, nullptr);
4831 if (e.shaderResourceBindings.poolIndex >= 0) {
4832 descriptorPools[e.shaderResourceBindings.poolIndex].refCount -= 1;
4833 Q_ASSERT(descriptorPools[e.shaderResourceBindings.poolIndex].refCount >= 0);
4834 }
4835 break;
4838 break;
4840 qrhivk_releaseRenderBuffer(e, dev, df);
4841 break;
4843 qrhivk_releaseTexture(e, dev, df, allocator);
4844 break;
4846 qrhivk_releaseSampler(e, dev, df);
4847 break;
4849 df->vkDestroyFramebuffer(dev, e.textureRenderTarget.fb, nullptr);
4850 for (int att = 0; att < QVkRenderTargetData::MAX_COLOR_ATTACHMENTS; ++att) {
4851 df->vkDestroyImageView(dev, e.textureRenderTarget.rtv[att], nullptr);
4852 df->vkDestroyImageView(dev, e.textureRenderTarget.resrtv[att], nullptr);
4853 }
4854 df->vkDestroyImageView(dev, e.textureRenderTarget.dsv, nullptr);
4855 df->vkDestroyImageView(dev, e.textureRenderTarget.resdsv, nullptr);
4856 df->vkDestroyImageView(dev, e.textureRenderTarget.shadingRateMapView, nullptr);
4857 break;
4859 df->vkDestroyRenderPass(dev, e.renderPass.rp, nullptr);
4860 break;
4862 vmaDestroyBuffer(toVmaAllocator(allocator), e.stagingBuffer.stagingBuffer, toVmaAllocation(e.stagingBuffer.stagingAllocation));
4863 break;
4864 case QRhiVulkan::DeferredReleaseEntry::SecondaryCommandBuffer:
4865 freeSecondaryCbs[e.lastActiveFrameSlot].append(e.secondaryCommandBuffer.cb);
4866 break;
4867 default:
4868 Q_UNREACHABLE();
4869 break;
4870 }
4871 releaseQueue.removeAt(i);
4872 }
4873 }
4874}
4875
4877{
4878 QVarLengthArray<std::function<void()>, 4> completedCallbacks;
4879
4880 for (int i = activeTextureReadbacks.size() - 1; i >= 0; --i) {
4881 const QRhiVulkan::TextureReadback &readback(activeTextureReadbacks[i]);
4882 if (forced || currentFrameSlot == readback.activeFrameSlot || readback.activeFrameSlot < 0) {
4883 readback.result->format = readback.format;
4884 readback.result->pixelSize = readback.rect.size();
4885 readback.result->data.resizeForOverwrite(readback.byteSize);
4886 VkResult err = vmaCopyAllocationToMemory(toVmaAllocator(allocator),
4887 toVmaAllocation(readback.stagingAlloc),
4888 0, readback.result->data.data(), readback.byteSize);
4889 if (err != VK_SUCCESS) {
4890 qWarning("Failed to copy texture readback buffer of size %u: %d", readback.byteSize, err);
4891 readback.result->data.clear();
4892 }
4893
4894 vmaDestroyBuffer(toVmaAllocator(allocator), readback.stagingBuf, toVmaAllocation(readback.stagingAlloc));
4895
4896 if (readback.result->completed)
4897 completedCallbacks.append(readback.result->completed);
4898
4899 activeTextureReadbacks.remove(i);
4900 }
4901 }
4902
4903 for (int i = activeBufferReadbacks.size() - 1; i >= 0; --i) {
4904 const QRhiVulkan::BufferReadback &readback(activeBufferReadbacks[i]);
4905 if (forced || currentFrameSlot == readback.activeFrameSlot || readback.activeFrameSlot < 0) {
4906 readback.result->data.resizeForOverwrite(readback.byteSize);
4907 VkResult err = vmaCopyAllocationToMemory(toVmaAllocator(allocator),
4908 toVmaAllocation(readback.stagingAlloc),
4909 0, readback.result->data.data(), readback.byteSize);
4910 if (err != VK_SUCCESS) {
4911 qWarning("Failed to copy buffer readback buffer of size %d: %d", readback.byteSize, err);
4912 readback.result->data.clear();
4913 }
4914
4915 vmaDestroyBuffer(toVmaAllocator(allocator), readback.stagingBuf, toVmaAllocation(readback.stagingAlloc));
4916
4917 if (readback.result->completed)
4918 completedCallbacks.append(readback.result->completed);
4919
4920 activeBufferReadbacks.remove(i);
4921 }
4922 }
4923
4924 for (const auto &f : completedCallbacks)
4925 f();
4926}
4927
4928static struct {
4931} qvk_sampleCounts[] = {
4932 // keep this sorted by 'count'
4933 { VK_SAMPLE_COUNT_1_BIT, 1 },
4934 { VK_SAMPLE_COUNT_2_BIT, 2 },
4935 { VK_SAMPLE_COUNT_4_BIT, 4 },
4936 { VK_SAMPLE_COUNT_8_BIT, 8 },
4937 { VK_SAMPLE_COUNT_16_BIT, 16 },
4938 { VK_SAMPLE_COUNT_32_BIT, 32 },
4939 { VK_SAMPLE_COUNT_64_BIT, 64 }
4941
4943{
4944 const VkPhysicalDeviceLimits *limits = &physDevProperties.limits;
4945 VkSampleCountFlags color = limits->framebufferColorSampleCounts;
4946 VkSampleCountFlags depth = limits->framebufferDepthSampleCounts;
4947 VkSampleCountFlags stencil = limits->framebufferStencilSampleCounts;
4948 QList<int> result;
4949
4950 for (const auto &qvk_sampleCount : qvk_sampleCounts) {
4951 if ((color & qvk_sampleCount.mask)
4952 && (depth & qvk_sampleCount.mask)
4953 && (stencil & qvk_sampleCount.mask))
4954 {
4955 result.append(qvk_sampleCount.count);
4956 }
4957 }
4958
4959 return result;
4960}
4961
4963{
4964 const int s = effectiveSampleCount(sampleCount);
4965
4966 for (const auto &qvk_sampleCount : qvk_sampleCounts) {
4967 if (qvk_sampleCount.count == s)
4968 return qvk_sampleCount.mask;
4969 }
4970
4971 Q_UNREACHABLE_RETURN(VK_SAMPLE_COUNT_1_BIT);
4972}
4973
4975{
4976 QList<QSize> result;
4977#ifdef VK_KHR_fragment_shading_rate
4978 sampleCount = qMax(1, sampleCount);
4979 VkSampleCountFlagBits mask = VK_SAMPLE_COUNT_1_BIT;
4980 for (const auto &qvk_sampleCount : qvk_sampleCounts) {
4981 if (qvk_sampleCount.count == sampleCount) {
4982 mask = qvk_sampleCount.mask;
4983 break;
4984 }
4985 }
4986 for (const VkPhysicalDeviceFragmentShadingRateKHR &s : fragmentShadingRates) {
4987 if (s.sampleCounts & mask)
4988 result.append(QSize(int(s.fragmentSize.width), int(s.fragmentSize.height)));
4989 }
4990#else
4991 Q_UNUSED(sampleCount);
4992 result.append(QSize(1, 1));
4993#endif
4994 return result;
4995}
4996
4998{
4999 cbD->passResTrackers.emplace_back();
5000 cbD->currentPassResTrackerIndex = cbD->passResTrackers.size() - 1;
5001
5002 QVkCommandBuffer::Command &cmd(cbD->commands.get());
5004 cmd.args.transitionResources.trackerIndex = cbD->passResTrackers.size() - 1;
5005}
5006
5008{
5010
5011 for (auto it = cbD->commands.begin(), end = cbD->commands.end(); it != end; ++it) {
5012 QVkCommandBuffer::Command &cmd(*it);
5013 switch (cmd.cmd) {
5015 df->vkCmdCopyBuffer(cbD->cb, cmd.args.copyBuffer.src, cmd.args.copyBuffer.dst,
5016 1, &cmd.args.copyBuffer.desc);
5017 break;
5019 df->vkCmdCopyBufferToImage(cbD->cb, cmd.args.copyBufferToImage.src, cmd.args.copyBufferToImage.dst,
5020 cmd.args.copyBufferToImage.dstLayout,
5021 uint32_t(cmd.args.copyBufferToImage.count),
5022 cbD->pools.bufferImageCopy.constData() + cmd.args.copyBufferToImage.bufferImageCopyIndex);
5023 break;
5025 df->vkCmdCopyImage(cbD->cb, cmd.args.copyImage.src, cmd.args.copyImage.srcLayout,
5026 cmd.args.copyImage.dst, cmd.args.copyImage.dstLayout,
5027 1, &cmd.args.copyImage.desc);
5028 break;
5030 df->vkCmdCopyImageToBuffer(cbD->cb, cmd.args.copyImageToBuffer.src, cmd.args.copyImageToBuffer.srcLayout,
5031 cmd.args.copyImageToBuffer.dst,
5032 1, &cmd.args.copyImageToBuffer.desc);
5033 break;
5035 df->vkCmdPipelineBarrier(cbD->cb, cmd.args.imageBarrier.srcStageMask, cmd.args.imageBarrier.dstStageMask,
5036 0, 0, nullptr, 0, nullptr,
5037 cmd.args.imageBarrier.count, cbD->pools.imageBarrier.constData() + cmd.args.imageBarrier.index);
5038 break;
5040 df->vkCmdPipelineBarrier(cbD->cb, cmd.args.bufferBarrier.srcStageMask, cmd.args.bufferBarrier.dstStageMask,
5041 0, 0, nullptr,
5042 cmd.args.bufferBarrier.count, cbD->pools.bufferBarrier.constData() + cmd.args.bufferBarrier.index,
5043 0, nullptr);
5044 break;
5046 const auto &barrier = cmd.args.imageAndBufferBarrier;
5047 const VkBufferMemoryBarrier *bufferBarrierData = barrier.bufferCount
5048 ? cbD->pools.bufferBarrier.constData() + barrier.bufferIndex : nullptr;
5049 const VkImageMemoryBarrier *imageBarrierData = barrier.imageCount
5050 ? cbD->pools.imageBarrier.constData() + barrier.imageIndex : nullptr;
5051 df->vkCmdPipelineBarrier(cbD->cb, barrier.srcStageMask, barrier.dstStageMask,
5052 0, 0, nullptr,
5053 barrier.bufferCount, bufferBarrierData,
5054 barrier.imageCount, imageBarrierData);
5055 } break;
5057 df->vkCmdBlitImage(cbD->cb, cmd.args.blitImage.src, cmd.args.blitImage.srcLayout,
5058 cmd.args.blitImage.dst, cmd.args.blitImage.dstLayout,
5059 1, &cmd.args.blitImage.desc,
5060 cmd.args.blitImage.filter);
5061 break;
5063 cmd.args.beginRenderPass.desc.pClearValues = cbD->pools.clearValue.constData() + cmd.args.beginRenderPass.clearValueIndex;
5064 df->vkCmdBeginRenderPass(cbD->cb, &cmd.args.beginRenderPass.desc,
5065 cmd.args.beginRenderPass.useSecondaryCb ? VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS
5066 : VK_SUBPASS_CONTENTS_INLINE);
5067 break;
5069 VkMemoryBarrier barrier = {};
5070 barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
5071 barrier.pNext = nullptr;
5072 barrier.dstAccessMask = cmd.args.memoryBarrier.dstAccessMask;
5073 barrier.srcAccessMask = cmd.args.memoryBarrier.srcAccessMask;
5074 df->vkCmdPipelineBarrier(cbD->cb, cmd.args.memoryBarrier.srcStageMask, cmd.args.memoryBarrier.dstStageMask, cmd.args.memoryBarrier.dependencyFlags,
5075 1, &barrier,
5076 0, VK_NULL_HANDLE,
5077 0, VK_NULL_HANDLE);
5078 } break;
5080 df->vkCmdEndRenderPass(cbD->cb);
5081 break;
5083 df->vkCmdBindPipeline(cbD->cb, cmd.args.bindPipeline.bindPoint, cmd.args.bindPipeline.pipeline);
5084 break;
5086 {
5087 const uint32_t *offsets = nullptr;
5088 if (cmd.args.bindDescriptorSet.dynamicOffsetCount > 0)
5089 offsets = cbD->pools.dynamicOffset.constData() + cmd.args.bindDescriptorSet.dynamicOffsetIndex;
5090 df->vkCmdBindDescriptorSets(cbD->cb, cmd.args.bindDescriptorSet.bindPoint,
5091 cmd.args.bindDescriptorSet.pipelineLayout,
5092 0, 1, &cmd.args.bindDescriptorSet.descSet,
5093 uint32_t(cmd.args.bindDescriptorSet.dynamicOffsetCount),
5094 offsets);
5095 }
5096 break;
5098 df->vkCmdBindVertexBuffers(cbD->cb, uint32_t(cmd.args.bindVertexBuffer.startBinding),
5099 uint32_t(cmd.args.bindVertexBuffer.count),
5100 cbD->pools.vertexBuffer.constData() + cmd.args.bindVertexBuffer.vertexBufferIndex,
5101 cbD->pools.vertexBufferOffset.constData() + cmd.args.bindVertexBuffer.vertexBufferOffsetIndex);
5102 break;
5104 df->vkCmdBindIndexBuffer(cbD->cb, cmd.args.bindIndexBuffer.buf,
5105 cmd.args.bindIndexBuffer.ofs, cmd.args.bindIndexBuffer.type);
5106 break;
5108 df->vkCmdSetViewport(cbD->cb, 0, 1, &cmd.args.setViewport.viewport);
5109 break;
5111 df->vkCmdSetScissor(cbD->cb, 0, 1, &cmd.args.setScissor.scissor);
5112 break;
5114 df->vkCmdSetBlendConstants(cbD->cb, cmd.args.setBlendConstants.c);
5115 break;
5117 df->vkCmdSetStencilReference(cbD->cb, VK_STENCIL_FRONT_AND_BACK, cmd.args.setStencilRef.ref);
5118 break;
5120 df->vkCmdDraw(cbD->cb, cmd.args.draw.vertexCount, cmd.args.draw.instanceCount,
5121 cmd.args.draw.firstVertex, cmd.args.draw.firstInstance);
5122 break;
5124 df->vkCmdDrawIndexed(cbD->cb, cmd.args.drawIndexed.indexCount, cmd.args.drawIndexed.instanceCount,
5125 cmd.args.drawIndexed.firstIndex, cmd.args.drawIndexed.vertexOffset,
5126 cmd.args.drawIndexed.firstInstance);
5127 break;
5129 df->vkCmdDrawIndirect(cbD->cb, cmd.args.drawIndirect.indirectBuffer,
5130 cmd.args.drawIndirect.indirectBufferOffset,
5131 cmd.args.drawIndirect.drawCount,
5132 cmd.args.drawIndirect.stride);
5133 break;
5135 df->vkCmdDrawIndexedIndirect(cbD->cb, cmd.args.drawIndexedIndirect.indirectBuffer,
5136 cmd.args.drawIndexedIndirect.indirectBufferOffset,
5137 cmd.args.drawIndexedIndirect.drawCount,
5138 cmd.args.drawIndexedIndirect.stride);
5139 break;
5141#ifdef VK_EXT_debug_utils
5142 cmd.args.debugMarkerBegin.label.pLabelName =
5143 cbD->pools.debugMarkerData[cmd.args.debugMarkerBegin.labelNameIndex].constData();
5144 vkCmdBeginDebugUtilsLabelEXT(cbD->cb, &cmd.args.debugMarkerBegin.label);
5145#endif
5146 break;
5148#ifdef VK_EXT_debug_utils
5149 vkCmdEndDebugUtilsLabelEXT(cbD->cb);
5150#endif
5151 break;
5153#ifdef VK_EXT_debug_utils
5154 cmd.args.debugMarkerInsert.label.pLabelName =
5155 cbD->pools.debugMarkerData[cmd.args.debugMarkerInsert.labelNameIndex].constData();
5156 vkCmdInsertDebugUtilsLabelEXT(cbD->cb, &cmd.args.debugMarkerInsert.label);
5157#endif
5158 break;
5160 recordTransitionPassResources(cbD, cbD->passResTrackers[cmd.args.transitionResources.trackerIndex]);
5161 break;
5163 df->vkCmdDispatch(cbD->cb, uint32_t(cmd.args.dispatch.x), uint32_t(cmd.args.dispatch.y), uint32_t(cmd.args.dispatch.z));
5164 break;
5166 df->vkCmdExecuteCommands(cbD->cb, 1, &cmd.args.executeSecondary.cb);
5167 break;
5169 {
5170#ifdef VK_KHR_fragment_shading_rate
5171 VkFragmentShadingRateCombinerOpKHR op[2] = {
5172 VK_FRAGMENT_SHADING_RATE_COMBINER_OP_MAX_KHR,
5173 VK_FRAGMENT_SHADING_RATE_COMBINER_OP_MAX_KHR
5174 };
5175 VkExtent2D size = { cmd.args.setShadingRate.w, cmd.args.setShadingRate.h };
5176 vkCmdSetFragmentShadingRateKHR(cbD->cb, &size, op);
5177#endif
5178 }
5179 break;
5180 default:
5181 break;
5182 }
5183 }
5184}
5185
5187{
5188 switch (access) {
5189 case QRhiPassResourceTracker::BufIndirectDraw:
5190 return VK_ACCESS_INDIRECT_COMMAND_READ_BIT;
5191 case QRhiPassResourceTracker::BufVertexInput:
5192 return VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT;
5193 case QRhiPassResourceTracker::BufIndexRead:
5194 return VK_ACCESS_INDEX_READ_BIT;
5195 case QRhiPassResourceTracker::BufUniformRead:
5196 return VK_ACCESS_UNIFORM_READ_BIT;
5197 case QRhiPassResourceTracker::BufStorageLoad:
5198 return VK_ACCESS_SHADER_READ_BIT;
5199 case QRhiPassResourceTracker::BufStorageStore:
5200 return VK_ACCESS_SHADER_WRITE_BIT;
5201 case QRhiPassResourceTracker::BufStorageLoadStore:
5202 return VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
5203 default:
5204 Q_UNREACHABLE();
5205 break;
5206 }
5207 return 0;
5208}
5209
5211{
5212 switch (stage) {
5213 case QRhiPassResourceTracker::BufIndirectDrawStage:
5214 return VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT;
5215 case QRhiPassResourceTracker::BufVertexInputStage:
5216 return VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
5217 case QRhiPassResourceTracker::BufVertexStage:
5218 return VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
5219 case QRhiPassResourceTracker::BufTCStage:
5220 return VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT;
5221 case QRhiPassResourceTracker::BufTEStage:
5222 return VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT;
5223 case QRhiPassResourceTracker::BufFragmentStage:
5224 return VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
5225 case QRhiPassResourceTracker::BufComputeStage:
5226 return VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
5227 case QRhiPassResourceTracker::BufGeometryStage:
5228 return VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT;
5229 default:
5230 Q_UNREACHABLE();
5231 break;
5232 }
5233 return 0;
5234}
5235
5237{
5239 u.access = VkAccessFlags(usage.access);
5240 u.stage = VkPipelineStageFlags(usage.stage);
5241 return u;
5242}
5243
5245{
5246 switch (access) {
5247 case QRhiPassResourceTracker::TexSample:
5248 return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
5249 case QRhiPassResourceTracker::TexColorOutput:
5250 return VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
5251 case QRhiPassResourceTracker::TexDepthOutput:
5252 return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
5253 case QRhiPassResourceTracker::TexStorageLoad:
5254 case QRhiPassResourceTracker::TexStorageStore:
5255 case QRhiPassResourceTracker::TexStorageLoadStore:
5256 return VK_IMAGE_LAYOUT_GENERAL;
5257 case QRhiPassResourceTracker::TexShadingRate:
5258#ifdef VK_KHR_fragment_shading_rate
5259 return VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR;
5260#else
5261 return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
5262#endif
5263 default:
5264 Q_UNREACHABLE();
5265 break;
5266 }
5267 return VK_IMAGE_LAYOUT_GENERAL;
5268}
5269
5271{
5272 switch (access) {
5273 case QRhiPassResourceTracker::TexSample:
5274 return VK_ACCESS_SHADER_READ_BIT;
5275 case QRhiPassResourceTracker::TexColorOutput:
5276 return VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
5277 case QRhiPassResourceTracker::TexDepthOutput:
5278 return VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
5279 case QRhiPassResourceTracker::TexStorageLoad:
5280 return VK_ACCESS_SHADER_READ_BIT;
5281 case QRhiPassResourceTracker::TexStorageStore:
5282 return VK_ACCESS_SHADER_WRITE_BIT;
5283 case QRhiPassResourceTracker::TexStorageLoadStore:
5284 return VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
5286 return 0;
5287 default:
5288 Q_UNREACHABLE();
5289 break;
5290 }
5291 return 0;
5292}
5293
5295{
5296 switch (stage) {
5297 case QRhiPassResourceTracker::TexVertexStage:
5298 return VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
5299 case QRhiPassResourceTracker::TexTCStage:
5300 return VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT;
5301 case QRhiPassResourceTracker::TexTEStage:
5302 return VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT;
5303 case QRhiPassResourceTracker::TexFragmentStage:
5304 return VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
5305 case QRhiPassResourceTracker::TexColorOutputStage:
5306 return VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
5307 case QRhiPassResourceTracker::TexDepthOutputStage:
5308 return VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
5309 case QRhiPassResourceTracker::TexComputeStage:
5310 return VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
5311 case QRhiPassResourceTracker::TexGeometryStage:
5312 return VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT;
5313 default:
5314 Q_UNREACHABLE();
5315 break;
5316 }
5317 return 0;
5318}
5319
5321{
5323 u.layout = VkImageLayout(usage.layout);
5324 u.access = VkAccessFlags(usage.access);
5325 u.stage = VkPipelineStageFlags(usage.stage);
5326 return u;
5327}
5328
5330 QVkBuffer *bufD,
5331 int slot,
5334{
5335 QVkBuffer::UsageState &u(bufD->usageState[slot]);
5336 const VkAccessFlags newAccess = toVkAccess(access);
5337 const VkPipelineStageFlags newStage = toVkPipelineStage(stage);
5338 if (u.access == newAccess && u.stage == newStage) {
5339 if (!accessIsWrite(newAccess))
5340 return;
5341 }
5342 passResTracker->registerBuffer(bufD, slot, &access, &stage, toPassTrackerUsageState(u));
5343 u.access = newAccess;
5344 u.stage = newStage;
5345}
5346
5348 QVkTexture *texD,
5351{
5352 QVkTexture::UsageState &u(texD->usageState);
5353 const VkAccessFlags newAccess = toVkAccess(access);
5354 const VkPipelineStageFlags newStage = toVkPipelineStage(stage);
5355 const VkImageLayout newLayout = toVkLayout(access);
5356 if (u.access == newAccess && u.stage == newStage && u.layout == newLayout) {
5357 if (!accessIsWrite(newAccess))
5358 return;
5359 }
5360 passResTracker->registerTexture(texD, &access, &stage, toPassTrackerUsageState(u));
5361 u.layout = newLayout;
5362 u.access = newAccess;
5363 u.stage = newStage;
5364}
5365
5367{
5368 if (tracker.isEmpty())
5369 return;
5370
5371 for (const auto &[rhiB, trackedB]: tracker.buffers()) {
5372 QVkBuffer *bufD = QRHI_RES(QVkBuffer, rhiB);
5373 VkAccessFlags access = toVkAccess(trackedB.access);
5374 VkPipelineStageFlags stage = toVkPipelineStage(trackedB.stage);
5375 QVkBuffer::UsageState s = toVkBufferUsageState(trackedB.stateAtPassBegin);
5376 if (!s.stage)
5377 continue;
5378 if (s.access == access && s.stage == stage) {
5379 if (!accessIsWrite(access))
5380 continue;
5381 }
5382 VkBufferMemoryBarrier bufMemBarrier = {};
5383 bufMemBarrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
5384 bufMemBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
5385 bufMemBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
5386 bufMemBarrier.srcAccessMask = s.access;
5387 bufMemBarrier.dstAccessMask = access;
5388 bufMemBarrier.buffer = bufD->buffers[trackedB.slot];
5389 bufMemBarrier.size = VK_WHOLE_SIZE;
5390 df->vkCmdPipelineBarrier(cbD->cb, s.stage, stage, 0,
5391 0, nullptr,
5392 1, &bufMemBarrier,
5393 0, nullptr);
5394 }
5395
5396 for (const auto &[rhiT, trackedT]: tracker.textures()) {
5397 QVkTexture *texD = QRHI_RES(QVkTexture, rhiT);
5398 VkImageLayout layout = toVkLayout(trackedT.access);
5399 VkAccessFlags access = toVkAccess(trackedT.access);
5400 VkPipelineStageFlags stage = toVkPipelineStage(trackedT.stage);
5401 QVkTexture::UsageState s = toVkTextureUsageState(trackedT.stateAtPassBegin);
5402 if (s.access == access && s.stage == stage && s.layout == layout) {
5403 if (!accessIsWrite(access))
5404 continue;
5405 }
5406 VkImageMemoryBarrier barrier = {};
5407 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
5408 barrier.subresourceRange.aspectMask = aspectMaskForTextureFormat(texD->m_format);
5409 barrier.subresourceRange.baseMipLevel = 0;
5410 barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
5411 barrier.subresourceRange.baseArrayLayer = 0;
5412 barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
5413 barrier.oldLayout = s.layout; // new textures have this set to PREINITIALIZED
5414 barrier.newLayout = layout;
5415 barrier.srcAccessMask = s.access; // may be 0 but that's fine
5416 barrier.dstAccessMask = access;
5417 barrier.image = texD->image;
5418 VkPipelineStageFlags srcStage = s.stage;
5419 // stage mask cannot be 0
5420 if (!srcStage)
5421 srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
5422 df->vkCmdPipelineBarrier(cbD->cb, srcStage, stage, 0,
5423 0, nullptr,
5424 0, nullptr,
5425 1, &barrier);
5426 }
5427}
5428
5430{
5431 if (!vkGetPhysicalDeviceSurfaceCapabilitiesKHR
5432 || !vkGetPhysicalDeviceSurfaceFormatsKHR
5433 || !vkGetPhysicalDeviceSurfacePresentModesKHR)
5434 {
5435 qWarning("Physical device surface queries not available");
5436 return nullptr;
5437 }
5438
5439 return new QVkSwapChain(this);
5440}
5441
5442QRhiBuffer *QRhiVulkan::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, quint32 size)
5443{
5444 return new QVkBuffer(this, type, usage, size);
5445}
5446
5448{
5449 return int(ubufAlign); // typically 256 (bytes)
5450}
5451
5453{
5454 return false;
5455}
5456
5458{
5459 return false;
5460}
5461
5463{
5464 return true;
5465}
5466
5468{
5469 // See https://matthewwellings.com/blog/the-new-vulkan-coordinate-system/
5470
5471 // NB the ctor takes row-major
5472 static constexpr QMatrix4x4 m(1.0f, 0.0f, 0.0f, 0.0f,
5473 0.0f, -1.0f, 0.0f, 0.0f,
5474 0.0f, 0.0f, 0.5f, 0.5f,
5475 0.0f, 0.0f, 0.0f, 1.0f);
5476 return m;
5477}
5478
5479bool QRhiVulkan::isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const
5480{
5481 // Note that with some SDKs the validation layer gives an odd warning about
5482 // BC not being supported, even when our check here succeeds. Not much we
5483 // can do about that.
5484 if (format >= QRhiTexture::BC1 && format <= QRhiTexture::BC7) {
5485 if (!physDevFeatures.textureCompressionBC)
5486 return false;
5487 }
5488
5489 if (format >= QRhiTexture::ETC2_RGB8 && format <= QRhiTexture::ETC2_RGBA8) {
5490 if (!physDevFeatures.textureCompressionETC2)
5491 return false;
5492 }
5493
5494 if (format >= QRhiTexture::ASTC_4x4 && format <= QRhiTexture::ASTC_12x12) {
5495 if (!physDevFeatures.textureCompressionASTC_LDR)
5496 return false;
5497 }
5498
5499 VkFormat vkformat = toVkTextureFormat(format, flags);
5500 VkFormatProperties props;
5501 f->vkGetPhysicalDeviceFormatProperties(physDev, vkformat, &props);
5502 return (props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) != 0;
5503}
5504
5505bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const
5506{
5507 switch (feature) {
5508 case QRhi::MultisampleTexture:
5509 return true;
5510 case QRhi::MultisampleRenderBuffer:
5511 return true;
5512 case QRhi::DebugMarkers:
5513 return caps.debugUtils;
5514 case QRhi::Timestamps:
5515 return timestampValidBits != 0;
5516 case QRhi::Instancing:
5517 return true;
5518 case QRhi::CustomInstanceStepRate:
5519 return caps.vertexAttribDivisor;
5520 case QRhi::PrimitiveRestart:
5521 return true;
5522 case QRhi::NonDynamicUniformBuffers:
5523 return true;
5524 case QRhi::NonFourAlignedEffectiveIndexBufferOffset:
5525 return true;
5526 case QRhi::NPOTTextureRepeat:
5527 return true;
5528 case QRhi::RedOrAlpha8IsRed:
5529 return true;
5530 case QRhi::ElementIndexUint:
5531 return true;
5532 case QRhi::Compute:
5533 return caps.compute;
5534 case QRhi::WideLines:
5535 return caps.wideLines;
5536 case QRhi::VertexShaderPointSize:
5537 return true;
5538 case QRhi::BaseVertex:
5539 return true;
5540 case QRhi::BaseInstance:
5541 return true;
5542 case QRhi::TriangleFanTopology:
5543 return true;
5544 case QRhi::ReadBackNonUniformBuffer:
5545 return true;
5546 case QRhi::ReadBackNonBaseMipLevel:
5547 return true;
5548 case QRhi::TexelFetch:
5549 return true;
5550 case QRhi::RenderToNonBaseMipLevel:
5551 return true;
5552 case QRhi::IntAttributes:
5553 return true;
5554 case QRhi::ScreenSpaceDerivatives:
5555 return true;
5556 case QRhi::ReadBackAnyTextureFormat:
5557 return true;
5558 case QRhi::PipelineCacheDataLoadSave:
5559 return true;
5560 case QRhi::ImageDataStride:
5561 return true;
5562 case QRhi::RenderBufferImport:
5563 return false;
5564 case QRhi::ThreeDimensionalTextures:
5565 return true;
5566 case QRhi::RenderTo3DTextureSlice:
5567 return caps.texture3DSliceAs2D;
5568 case QRhi::TextureArrays:
5569 return true;
5570 case QRhi::Tessellation:
5571 return caps.tessellation;
5572 case QRhi::GeometryShader:
5573 return caps.geometryShader;
5574 case QRhi::TextureArrayRange:
5575 return true;
5576 case QRhi::NonFillPolygonMode:
5577 return caps.nonFillPolygonMode;
5578 case QRhi::OneDimensionalTextures:
5579 return true;
5580 case QRhi::OneDimensionalTextureMipmaps:
5581 return true;
5582 case QRhi::HalfAttributes:
5583 return true;
5584 case QRhi::RenderToOneDimensionalTexture:
5585 return true;
5586 case QRhi::ThreeDimensionalTextureMipmaps:
5587 return true;
5588 case QRhi::MultiView:
5589 return caps.multiView;
5590 case QRhi::TextureViewFormat:
5591 return true;
5592 case QRhi::ResolveDepthStencil:
5593 return caps.renderPass2KHR && caps.depthStencilResolveKHR;
5594 case QRhi::VariableRateShading:
5595 return caps.renderPass2KHR && caps.perDrawShadingRate;
5596 case QRhi::VariableRateShadingMap:
5597 case QRhi::VariableRateShadingMapWithTexture:
5598 return caps.renderPass2KHR && caps.imageBasedShadingRate;
5599 case QRhi::PerRenderTargetBlending:
5600 case QRhi::SampleVariables:
5601 return true;
5602 case QRhi::InstanceIndexIncludesBaseInstance:
5603 return true;
5604 case QRhi::DepthClamp:
5605 return caps.depthClamp;
5606 case QRhi::DrawIndirect:
5607 return true; // available in Vulkan 1.0
5608 case QRhi::DrawIndirectMulti:
5609 return caps.drawIndirectMulti;
5610 case QRhi::ShaderDrawParameters:
5611 return caps.shaderDrawParameters;
5612 default:
5613 Q_UNREACHABLE_RETURN(false);
5614 }
5615}
5616
5617int QRhiVulkan::resourceLimit(QRhi::ResourceLimit limit) const
5618{
5619 switch (limit) {
5620 case QRhi::TextureSizeMin:
5621 return 1;
5622 case QRhi::TextureSizeMax:
5623 return int(physDevProperties.limits.maxImageDimension2D);
5624 case QRhi::MaxColorAttachments:
5625 return int(physDevProperties.limits.maxColorAttachments);
5626 case QRhi::FramesInFlight:
5627 return QVK_FRAMES_IN_FLIGHT;
5628 case QRhi::MaxAsyncReadbackFrames:
5629 return QVK_FRAMES_IN_FLIGHT;
5630 case QRhi::MaxThreadGroupsPerDimension:
5631 return int(qMin(physDevProperties.limits.maxComputeWorkGroupCount[0],
5632 qMin(physDevProperties.limits.maxComputeWorkGroupCount[1],
5633 physDevProperties.limits.maxComputeWorkGroupCount[2])));
5634 case QRhi::MaxThreadsPerThreadGroup:
5635 return int(physDevProperties.limits.maxComputeWorkGroupInvocations);
5636 case QRhi::MaxThreadGroupX:
5637 return int(physDevProperties.limits.maxComputeWorkGroupSize[0]);
5638 case QRhi::MaxThreadGroupY:
5639 return int(physDevProperties.limits.maxComputeWorkGroupSize[1]);
5640 case QRhi::MaxThreadGroupZ:
5641 return int(physDevProperties.limits.maxComputeWorkGroupSize[2]);
5642 case QRhi::TextureArraySizeMax:
5643 return int(physDevProperties.limits.maxImageArrayLayers);
5644 case QRhi::MaxUniformBufferRange:
5645 return int(qMin<uint32_t>(INT_MAX, physDevProperties.limits.maxUniformBufferRange));
5646 case QRhi::MaxVertexInputs:
5647 return physDevProperties.limits.maxVertexInputAttributes;
5648 case QRhi::MaxVertexOutputs:
5649 return physDevProperties.limits.maxVertexOutputComponents / 4;
5650 case QRhi::ShadingRateImageTileSize:
5651 return caps.imageBasedShadingRateTileSize;
5652 default:
5653 Q_UNREACHABLE_RETURN(0);
5654 }
5655}
5656
5658{
5659 return &nativeHandlesStruct;
5660}
5661
5663{
5664 return driverInfoStruct;
5665}
5666
5668{
5669 QRhiStats result;
5670 result.totalPipelineCreationTime = totalPipelineCreationTime();
5671
5672 VmaBudget budgets[VK_MAX_MEMORY_HEAPS];
5673 vmaGetHeapBudgets(toVmaAllocator(allocator), budgets);
5674
5675 uint32_t count = toVmaAllocator(allocator)->GetMemoryHeapCount();
5676 for (uint32_t i = 0; i < count; ++i) {
5677 const VmaStatistics &stats(budgets[i].statistics);
5678 result.blockCount += stats.blockCount;
5679 result.allocCount += stats.allocationCount;
5680 result.usedBytes += stats.allocationBytes;
5681 result.unusedBytes += stats.blockBytes - stats.allocationBytes;
5682 }
5683
5684 return result;
5685}
5686
5688{
5689 // not applicable
5690 return false;
5691}
5692
5693void QRhiVulkan::setQueueSubmitParams(QRhiNativeHandles *params)
5694{
5695 QRhiVulkanQueueSubmitParams *sp = static_cast<QRhiVulkanQueueSubmitParams *>(params);
5696 if (!sp)
5697 return;
5698
5699 waitSemaphoresForQueueSubmit.clear();
5700 if (sp->waitSemaphoreCount)
5701 waitSemaphoresForQueueSubmit.append(sp->waitSemaphores, sp->waitSemaphoreCount);
5702
5703 signalSemaphoresForQueueSubmit.clear();
5704 if (sp->signalSemaphoreCount)
5705 signalSemaphoresForQueueSubmit.append(sp->signalSemaphores, sp->signalSemaphoreCount);
5706
5707 waitSemaphoresForPresent.clear();
5708 if (sp->presentWaitSemaphoreCount)
5709 waitSemaphoresForPresent.append(sp->presentWaitSemaphores, sp->presentWaitSemaphoreCount);
5710}
5711
5716
5718{
5719 return deviceLost;
5720}
5721
5733
5735{
5736 Q_STATIC_ASSERT(sizeof(QVkPipelineCacheDataHeader) == 32);
5737
5738 QByteArray data;
5739 if (!pipelineCache || !rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
5740 return data;
5741
5742 size_t dataSize = 0;
5743 VkResult err = df->vkGetPipelineCacheData(dev, pipelineCache, &dataSize, nullptr);
5744 if (err != VK_SUCCESS) {
5745 qCDebug(QRHI_LOG_INFO, "Failed to get pipeline cache data size: %d", err);
5746 return QByteArray();
5747 }
5748 const size_t headerSize = sizeof(QVkPipelineCacheDataHeader);
5749 const size_t dataOffset = headerSize + VK_UUID_SIZE;
5750 data.resize(dataOffset + dataSize);
5751 err = df->vkGetPipelineCacheData(dev, pipelineCache, &dataSize, data.data() + dataOffset);
5752 if (err != VK_SUCCESS) {
5753 qCDebug(QRHI_LOG_INFO, "Failed to get pipeline cache data of %d bytes: %d", int(dataSize), err);
5754 return QByteArray();
5755 }
5756
5758 header.rhiId = pipelineCacheRhiId();
5759 header.arch = quint32(sizeof(void*));
5760 header.driverVersion = physDevProperties.driverVersion;
5761 header.vendorId = physDevProperties.vendorID;
5762 header.deviceId = physDevProperties.deviceID;
5763 header.dataSize = quint32(dataSize);
5764 header.uuidSize = VK_UUID_SIZE;
5765 header.reserved = 0;
5766 memcpy(data.data(), &header, headerSize);
5767 memcpy(data.data() + headerSize, physDevProperties.pipelineCacheUUID, VK_UUID_SIZE);
5768
5769 return data;
5770}
5771
5772void QRhiVulkan::setPipelineCacheData(const QByteArray &data)
5773{
5774 if (data.isEmpty())
5775 return;
5776
5777 const size_t headerSize = sizeof(QVkPipelineCacheDataHeader);
5778 if (data.size() < qsizetype(headerSize)) {
5779 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob size");
5780 return;
5781 }
5783 memcpy(&header, data.constData(), headerSize);
5784
5785 const quint32 rhiId = pipelineCacheRhiId();
5786 if (header.rhiId != rhiId) {
5787 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: The data is for a different QRhi version or backend (%u, %u)",
5788 rhiId, header.rhiId);
5789 return;
5790 }
5791 const quint32 arch = quint32(sizeof(void*));
5792 if (header.arch != arch) {
5793 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Architecture does not match (%u, %u)",
5794 arch, header.arch);
5795 return;
5796 }
5797 if (header.driverVersion != physDevProperties.driverVersion) {
5798 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: driverVersion does not match (%u, %u)",
5799 physDevProperties.driverVersion, header.driverVersion);
5800 return;
5801 }
5802 if (header.vendorId != physDevProperties.vendorID) {
5803 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: vendorID does not match (%u, %u)",
5804 physDevProperties.vendorID, header.vendorId);
5805 return;
5806 }
5807 if (header.deviceId != physDevProperties.deviceID) {
5808 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: deviceID does not match (%u, %u)",
5809 physDevProperties.deviceID, header.deviceId);
5810 return;
5811 }
5812 if (header.uuidSize != VK_UUID_SIZE) {
5813 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: VK_UUID_SIZE does not match (%u, %u)",
5814 quint32(VK_UUID_SIZE), header.uuidSize);
5815 return;
5816 }
5817
5818 if (data.size() < qsizetype(headerSize + VK_UUID_SIZE)) {
5819 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob, no uuid");
5820 return;
5821 }
5822 if (memcmp(data.constData() + headerSize, physDevProperties.pipelineCacheUUID, VK_UUID_SIZE)) {
5823 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: pipelineCacheUUID does not match");
5824 return;
5825 }
5826
5827 const size_t dataOffset = headerSize + VK_UUID_SIZE;
5828 if (data.size() < qsizetype(dataOffset + header.dataSize)) {
5829 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob, data missing");
5830 return;
5831 }
5832
5833 if (pipelineCache) {
5834 df->vkDestroyPipelineCache(dev, pipelineCache, nullptr);
5835 pipelineCache = VK_NULL_HANDLE;
5836 }
5837
5838 if (ensurePipelineCache(data.constData() + dataOffset, header.dataSize)) {
5839 qCDebug(QRHI_LOG_INFO, "Created pipeline cache with initial data of %d bytes",
5840 int(header.dataSize));
5841 } else {
5842 qCDebug(QRHI_LOG_INFO, "Failed to create pipeline cache with initial data specified");
5843 }
5844}
5845
5846QRhiRenderBuffer *QRhiVulkan::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
5847 int sampleCount, QRhiRenderBuffer::Flags flags,
5848 QRhiTexture::Format backingFormatHint)
5849{
5850 return new QVkRenderBuffer(this, type, pixelSize, sampleCount, flags, backingFormatHint);
5851}
5852
5853QRhiTexture *QRhiVulkan::createTexture(QRhiTexture::Format format,
5854 const QSize &pixelSize, int depth, int arraySize,
5855 int sampleCount, QRhiTexture::Flags flags)
5856{
5857 return new QVkTexture(this, format, pixelSize, depth, arraySize, sampleCount, flags);
5858}
5859
5860QRhiSampler *QRhiVulkan::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
5861 QRhiSampler::Filter mipmapMode,
5862 QRhiSampler::AddressMode u, QRhiSampler::AddressMode v, QRhiSampler::AddressMode w)
5863{
5864 return new QVkSampler(this, magFilter, minFilter, mipmapMode, u, v, w);
5865}
5866
5867QRhiShadingRateMap *QRhiVulkan::createShadingRateMap()
5868{
5869 return new QVkShadingRateMap(this);
5870}
5871
5872QRhiTextureRenderTarget *QRhiVulkan::createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
5873 QRhiTextureRenderTarget::Flags flags)
5874{
5875 return new QVkTextureRenderTarget(this, desc, flags);
5876}
5877
5879{
5880 return new QVkGraphicsPipeline(this);
5881}
5882
5884{
5885 return new QVkComputePipeline(this);
5886}
5887
5889{
5890 return new QVkShaderResourceBindings(this);
5891}
5892
5893void QRhiVulkan::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps)
5894{
5896 Q_ASSERT(psD->pipeline);
5897 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
5899
5900 if (cbD->currentGraphicsPipeline != ps || cbD->currentPipelineGeneration != psD->generation) {
5901 if (cbD->passUsesSecondaryCb) {
5902 df->vkCmdBindPipeline(cbD->activeSecondaryCbStack.last(), VK_PIPELINE_BIND_POINT_GRAPHICS, psD->pipeline);
5903 } else {
5904 QVkCommandBuffer::Command &cmd(cbD->commands.get());
5906 cmd.args.bindPipeline.bindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
5907 cmd.args.bindPipeline.pipeline = psD->pipeline;
5908 }
5909
5910 cbD->currentGraphicsPipeline = ps;
5911 cbD->currentComputePipeline = nullptr;
5912 cbD->currentPipelineGeneration = psD->generation;
5913
5914 if (cbD->hasCustomScissorSet && !psD->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor))
5916 }
5917
5918 psD->lastActiveFrameSlot = currentFrameSlot;
5919}
5920
5921void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBindings *srb,
5922 int dynamicOffsetCount,
5923 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
5924{
5925 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
5927 QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
5928 QVkGraphicsPipeline *gfxPsD = QRHI_RES(QVkGraphicsPipeline, cbD->currentGraphicsPipeline);
5929 QVkComputePipeline *compPsD = QRHI_RES(QVkComputePipeline, cbD->currentComputePipeline);
5930
5931 if (!srb) {
5932 if (gfxPsD)
5933 srb = gfxPsD->m_shaderResourceBindings;
5934 else
5935 srb = compPsD->m_shaderResourceBindings;
5936 }
5937
5939 auto &descSetBd(srbD->boundResourceData[currentFrameSlot]);
5940 bool rewriteDescSet = false;
5941 bool addWriteBarrier = false;
5942 VkPipelineStageFlags writeBarrierSrcStageMask = 0;
5943 VkPipelineStageFlags writeBarrierDstStageMask = 0;
5944
5945 // Do host writes and mark referenced shader resources as in-use.
5946 // Also prepare to ensure the descriptor set we are going to bind refers to up-to-date Vk objects.
5947 for (int i = 0, ie = srbD->sortedBindings.size(); i != ie; ++i) {
5948 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->sortedBindings[i]);
5950 switch (b->type) {
5951 case QRhiShaderResourceBinding::UniformBuffer:
5952 {
5953 QVkBuffer *bufD = QRHI_RES(QVkBuffer, b->u.ubuf.buf);
5954 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer));
5955 sanityCheckResourceOwnership(bufD);
5956
5957 if (bufD->m_type == QRhiBuffer::Dynamic)
5958 executeBufferHostWritesForSlot(bufD, currentFrameSlot);
5959
5960 bufD->lastActiveFrameSlot = currentFrameSlot;
5961 trackedRegisterBuffer(&passResTracker, bufD, bufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0,
5963 QRhiPassResourceTracker::toPassTrackerBufferStage(b->stage));
5964
5965 // Check both the "local" id (the generation counter) and the
5966 // global id. The latter is relevant when a newly allocated
5967 // QRhiResource ends up with the same pointer as a previous one.
5968 // (and that previous one could have been in an srb...)
5969 if (bufD->generation != bd.ubuf.generation || bufD->m_id != bd.ubuf.id) {
5970 rewriteDescSet = true;
5971 bd.ubuf.id = bufD->m_id;
5972 bd.ubuf.generation = bufD->generation;
5973 }
5974 }
5975 break;
5976 case QRhiShaderResourceBinding::SampledTexture:
5977 case QRhiShaderResourceBinding::Texture:
5978 case QRhiShaderResourceBinding::Sampler:
5979 {
5980 const QRhiShaderResourceBinding::Data::TextureAndOrSamplerData *data = &b->u.stex;
5981 if (bd.stex.count != data->count) {
5982 bd.stex.count = data->count;
5983 rewriteDescSet = true;
5984 }
5985 for (int elem = 0; elem < data->count; ++elem) {
5986 QVkTexture *texD = QRHI_RES(QVkTexture, data->texSamplers[elem].tex);
5987 QVkSampler *samplerD = QRHI_RES(QVkSampler, data->texSamplers[elem].sampler);
5988 // We use the same code path for both combined and separate
5989 // images and samplers, so tex or sampler (but not both) can be
5990 // null here.
5991 Q_ASSERT(texD || samplerD);
5992 sanityCheckResourceOwnership(texD);
5993 sanityCheckResourceOwnership(samplerD);
5994 if (texD) {
5995 texD->lastActiveFrameSlot = currentFrameSlot;
5996 trackedRegisterTexture(&passResTracker, texD,
5998 QRhiPassResourceTracker::toPassTrackerTextureStage(b->stage));
5999 }
6000 if (samplerD)
6001 samplerD->lastActiveFrameSlot = currentFrameSlot;
6002 const quint64 texId = texD ? texD->m_id : 0;
6003 const uint texGen = texD ? texD->generation : 0;
6004 const quint64 samplerId = samplerD ? samplerD->m_id : 0;
6005 const uint samplerGen = samplerD ? samplerD->generation : 0;
6006 if (texGen != bd.stex.d[elem].texGeneration
6007 || texId != bd.stex.d[elem].texId
6008 || samplerGen != bd.stex.d[elem].samplerGeneration
6009 || samplerId != bd.stex.d[elem].samplerId)
6010 {
6011 rewriteDescSet = true;
6012 bd.stex.d[elem].texId = texId;
6013 bd.stex.d[elem].texGeneration = texGen;
6014 bd.stex.d[elem].samplerId = samplerId;
6015 bd.stex.d[elem].samplerGeneration = samplerGen;
6016 }
6017 }
6018 }
6019 break;
6020 case QRhiShaderResourceBinding::ImageLoad:
6021 case QRhiShaderResourceBinding::ImageStore:
6022 case QRhiShaderResourceBinding::ImageLoadStore:
6023 {
6024 QVkTexture *texD = QRHI_RES(QVkTexture, b->u.simage.tex);
6025 sanityCheckResourceOwnership(texD);
6026 Q_ASSERT(texD->m_flags.testFlag(QRhiTexture::UsedWithLoadStore));
6027 texD->lastActiveFrameSlot = currentFrameSlot;
6029 if (b->type == QRhiShaderResourceBinding::ImageLoad)
6031 else if (b->type == QRhiShaderResourceBinding::ImageStore)
6033 else
6035
6036 const auto stage = QRhiPassResourceTracker::toPassTrackerTextureStage(b->stage);
6037 const auto prevAccess = passResTracker.textures().find(texD);
6038 if (prevAccess != passResTracker.textures().end()) {
6039 const QRhiPassResourceTracker::Texture &tex = prevAccess->second;
6042 addWriteBarrier = true;
6043 writeBarrierDstStageMask |= toVkPipelineStage(stage);
6044 writeBarrierSrcStageMask |= toVkPipelineStage(tex.stage);
6045 }
6046 }
6047
6048 trackedRegisterTexture(&passResTracker, texD,
6049 access,
6050 stage);
6051
6052 if (texD->generation != bd.simage.generation || texD->m_id != bd.simage.id) {
6053 rewriteDescSet = true;
6054 bd.simage.id = texD->m_id;
6055 bd.simage.generation = texD->generation;
6056 }
6057 }
6058 break;
6059 case QRhiShaderResourceBinding::BufferLoad:
6060 case QRhiShaderResourceBinding::BufferStore:
6061 case QRhiShaderResourceBinding::BufferLoadStore:
6062 {
6063 QVkBuffer *bufD = QRHI_RES(QVkBuffer, b->u.sbuf.buf);
6064 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::StorageBuffer));
6065 sanityCheckResourceOwnership(bufD);
6066
6067 if (bufD->m_type == QRhiBuffer::Dynamic)
6068 executeBufferHostWritesForSlot(bufD, currentFrameSlot);
6069
6070 bufD->lastActiveFrameSlot = currentFrameSlot;
6072 if (b->type == QRhiShaderResourceBinding::BufferLoad)
6074 else if (b->type == QRhiShaderResourceBinding::BufferStore)
6076 else
6078
6079 const auto stage = QRhiPassResourceTracker::toPassTrackerBufferStage(b->stage);
6080 const auto prevAccess = passResTracker.buffers().find(bufD);
6081 if (prevAccess != passResTracker.buffers().end()) {
6082 const QRhiPassResourceTracker::Buffer &buf = prevAccess->second;
6085 addWriteBarrier = true;
6086 writeBarrierDstStageMask |= toVkPipelineStage(stage);
6087 writeBarrierSrcStageMask |= toVkPipelineStage(buf.stage);
6088 }
6089 }
6090 trackedRegisterBuffer(&passResTracker, bufD, bufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0,
6091 access,
6092 stage);
6093
6094 if (bufD->generation != bd.sbuf.generation || bufD->m_id != bd.sbuf.id) {
6095 rewriteDescSet = true;
6096 bd.sbuf.id = bufD->m_id;
6097 bd.sbuf.generation = bufD->generation;
6098 }
6099 }
6100 break;
6101 default:
6102 Q_UNREACHABLE();
6103 break;
6104 }
6105 }
6106
6107 if (addWriteBarrier) {
6108 if (cbD->passUsesSecondaryCb) {
6109 VkMemoryBarrier barrier = {};
6110 barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
6111 barrier.pNext = nullptr;
6112 barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
6113 barrier.srcAccessMask = barrier.dstAccessMask;
6114 df->vkCmdPipelineBarrier(cbD->activeSecondaryCbStack.last(), writeBarrierSrcStageMask, writeBarrierDstStageMask, 0,
6115 1, &barrier,
6116 0, VK_NULL_HANDLE,
6117 0, VK_NULL_HANDLE);
6118 } else {
6119 QVkCommandBuffer::Command &cmd(cbD->commands.get());
6121 cmd.args.memoryBarrier.dependencyFlags = 0;
6122 cmd.args.memoryBarrier.dstStageMask = writeBarrierDstStageMask;
6123 cmd.args.memoryBarrier.srcStageMask = writeBarrierSrcStageMask;
6124 cmd.args.memoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
6125 cmd.args.memoryBarrier.srcAccessMask = cmd.args.memoryBarrier.dstAccessMask;
6126 }
6127 }
6128
6129 // write descriptor sets, if needed
6130 if (rewriteDescSet)
6131 updateShaderResourceBindings(srb);
6132
6133 // make sure the descriptors for the correct slot will get bound.
6134 // also, dynamic offsets always need a bind.
6135 const bool forceRebind = cbD->currentDescSetSlot != currentFrameSlot
6136 || !srbD->sortedDynamicOffsetBindingNumbers.isEmpty();
6137
6138 const bool srbChanged = gfxPsD ? (cbD->currentGraphicsSrb != srb) : (cbD->currentComputeSrb != srb);
6139
6140 if (forceRebind || rewriteDescSet || srbChanged || cbD->currentSrbGeneration != srbD->generation) {
6141 QVarLengthArray<uint32_t, 4> dynOfs;
6142 // Filling out dynOfs based on the sorted bindings is important
6143 // because dynOfs has to be ordered based on the binding numbers,
6144 // and neither srb nor dynamicOffsets has any such ordering
6145 // requirement.
6146 for (int bindingNumber : std::as_const(srbD->sortedDynamicOffsetBindingNumbers)) {
6147 uint32_t offset = 0;
6148 for (int i = 0; i < dynamicOffsetCount; ++i) {
6149 const QRhiCommandBuffer::DynamicOffset &bindingOffsetPair(dynamicOffsets[i]);
6150 if (bindingOffsetPair.first == bindingNumber) {
6151 offset = bindingOffsetPair.second;
6152 break;
6153 }
6154 }
6155 dynOfs.append(offset); // use 0 if dynamicOffsets did not contain this binding
6156 }
6157
6158 if (cbD->passUsesSecondaryCb) {
6159 df->vkCmdBindDescriptorSets(cbD->activeSecondaryCbStack.last(),
6160 gfxPsD ? VK_PIPELINE_BIND_POINT_GRAPHICS : VK_PIPELINE_BIND_POINT_COMPUTE,
6161 gfxPsD ? gfxPsD->layout : compPsD->layout,
6162 0, 1, &srbD->descSets[currentFrameSlot],
6163 uint32_t(dynOfs.size()),
6164 dynOfs.size() ? dynOfs.constData() : nullptr);
6165 } else {
6166 QVkCommandBuffer::Command &cmd(cbD->commands.get());
6168 cmd.args.bindDescriptorSet.bindPoint = gfxPsD ? VK_PIPELINE_BIND_POINT_GRAPHICS
6169 : VK_PIPELINE_BIND_POINT_COMPUTE;
6170 cmd.args.bindDescriptorSet.pipelineLayout = gfxPsD ? gfxPsD->layout : compPsD->layout;
6171 cmd.args.bindDescriptorSet.descSet = srbD->descSets[currentFrameSlot];
6172 cmd.args.bindDescriptorSet.dynamicOffsetCount = dynOfs.size();
6173 cmd.args.bindDescriptorSet.dynamicOffsetIndex = cbD->pools.dynamicOffset.size();
6174 cbD->pools.dynamicOffset.append(dynOfs.constData(), dynOfs.size());
6175 }
6176
6177 if (gfxPsD) {
6178 cbD->currentGraphicsSrb = srb;
6179 cbD->currentComputeSrb = nullptr;
6180 } else {
6181 cbD->currentGraphicsSrb = nullptr;
6182 cbD->currentComputeSrb = srb;
6183 }
6184 cbD->currentSrbGeneration = srbD->generation;
6185 cbD->currentDescSetSlot = currentFrameSlot;
6186 }
6187
6188 srbD->lastActiveFrameSlot = currentFrameSlot;
6189}
6190
6191void QRhiVulkan::setVertexInput(QRhiCommandBuffer *cb,
6192 int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
6193 QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat)
6194{
6195 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
6197 QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
6198
6199 bool needsBindVBuf = false;
6200 for (int i = 0; i < bindingCount; ++i) {
6201 const int inputSlot = startBinding + i;
6202 QVkBuffer *bufD = QRHI_RES(QVkBuffer, bindings[i].first);
6203 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::VertexBuffer));
6204 bufD->lastActiveFrameSlot = currentFrameSlot;
6205 if (bufD->m_type == QRhiBuffer::Dynamic)
6206 executeBufferHostWritesForSlot(bufD, currentFrameSlot);
6207
6208 const VkBuffer vkvertexbuf = bufD->buffers[bufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0];
6209 if (cbD->currentVertexBuffers[inputSlot] != vkvertexbuf
6210 || cbD->currentVertexOffsets[inputSlot] != bindings[i].second)
6211 {
6212 needsBindVBuf = true;
6213 cbD->currentVertexBuffers[inputSlot] = vkvertexbuf;
6214 cbD->currentVertexOffsets[inputSlot] = bindings[i].second;
6215 }
6216 }
6217
6218 if (needsBindVBuf) {
6219 QVarLengthArray<VkBuffer, 4> bufs;
6220 QVarLengthArray<VkDeviceSize, 4> ofs;
6221 for (int i = 0; i < bindingCount; ++i) {
6222 QVkBuffer *bufD = QRHI_RES(QVkBuffer, bindings[i].first);
6223 const int slot = bufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0;
6224 bufs.append(bufD->buffers[slot]);
6225 ofs.append(bindings[i].second);
6226 trackedRegisterBuffer(&passResTracker, bufD, slot,
6229 }
6230
6231 if (cbD->passUsesSecondaryCb) {
6232 df->vkCmdBindVertexBuffers(cbD->activeSecondaryCbStack.last(), uint32_t(startBinding),
6233 uint32_t(bufs.size()), bufs.constData(), ofs.constData());
6234 } else {
6235 QVkCommandBuffer::Command &cmd(cbD->commands.get());
6237 cmd.args.bindVertexBuffer.startBinding = startBinding;
6238 cmd.args.bindVertexBuffer.count = bufs.size();
6239 cmd.args.bindVertexBuffer.vertexBufferIndex = cbD->pools.vertexBuffer.size();
6240 cbD->pools.vertexBuffer.append(bufs.constData(), bufs.size());
6241 cmd.args.bindVertexBuffer.vertexBufferOffsetIndex = cbD->pools.vertexBufferOffset.size();
6242 cbD->pools.vertexBufferOffset.append(ofs.constData(), ofs.size());
6243 }
6244 }
6245
6246 if (indexBuf) {
6247 QVkBuffer *ibufD = QRHI_RES(QVkBuffer, indexBuf);
6248 Q_ASSERT(ibufD->m_usage.testFlag(QRhiBuffer::IndexBuffer));
6249 ibufD->lastActiveFrameSlot = currentFrameSlot;
6250 if (ibufD->m_type == QRhiBuffer::Dynamic)
6251 executeBufferHostWritesForSlot(ibufD, currentFrameSlot);
6252
6253 const int slot = ibufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0;
6254 const VkBuffer vkindexbuf = ibufD->buffers[slot];
6255 const VkIndexType type = indexFormat == QRhiCommandBuffer::IndexUInt16 ? VK_INDEX_TYPE_UINT16
6256 : VK_INDEX_TYPE_UINT32;
6257
6258 if (cbD->currentIndexBuffer != vkindexbuf
6259 || cbD->currentIndexOffset != indexOffset
6260 || cbD->currentIndexFormat != type)
6261 {
6262 cbD->currentIndexBuffer = vkindexbuf;
6263 cbD->currentIndexOffset = indexOffset;
6264 cbD->currentIndexFormat = type;
6265
6266 if (cbD->passUsesSecondaryCb) {
6267 df->vkCmdBindIndexBuffer(cbD->activeSecondaryCbStack.last(), vkindexbuf, indexOffset, type);
6268 } else {
6269 QVkCommandBuffer::Command &cmd(cbD->commands.get());
6271 cmd.args.bindIndexBuffer.buf = vkindexbuf;
6272 cmd.args.bindIndexBuffer.ofs = indexOffset;
6273 cmd.args.bindIndexBuffer.type = type;
6274 }
6275
6276 trackedRegisterBuffer(&passResTracker, ibufD, slot,
6279 }
6280 }
6281}
6282
6284{
6285 cbD->hasCustomScissorSet = false;
6286
6287 const QSize outputSize = cbD->currentTarget->pixelSize();
6288 std::array<float, 4> vp = cbD->currentViewport.viewport();
6289 float x = 0, y = 0, w = 0, h = 0;
6290
6291 if (qFuzzyIsNull(vp[2]) && qFuzzyIsNull(vp[3])) {
6292 x = 0;
6293 y = 0;
6294 w = outputSize.width();
6295 h = outputSize.height();
6296 } else {
6297 // x,y is top-left in VkRect2D but bottom-left in QRhiScissor
6298 qrhi_toTopLeftRenderTargetRect<Bounded>(outputSize, vp, &x, &y, &w, &h);
6299 }
6300
6301 QVkCommandBuffer::Command &cmd(cbD->commands.get());
6302 VkRect2D *s = &cmd.args.setScissor.scissor;
6303 s->offset.x = int32_t(x);
6304 s->offset.y = int32_t(y);
6305 s->extent.width = uint32_t(w);
6306 s->extent.height = uint32_t(h);
6307
6308 if (cbD->passUsesSecondaryCb) {
6309 df->vkCmdSetScissor(cbD->activeSecondaryCbStack.last(), 0, 1, s);
6310 cbD->commands.unget();
6311 } else {
6313 }
6314}
6315
6316void QRhiVulkan::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport)
6317{
6318 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
6320 const QSize outputSize = cbD->currentTarget->pixelSize();
6321
6322 // x,y is top-left in VkViewport but bottom-left in QRhiViewport
6323 float x, y, w, h;
6324 if (!qrhi_toTopLeftRenderTargetRect<UnBounded>(outputSize, viewport.viewport(), &x, &y, &w, &h))
6325 return;
6326
6327 QVkCommandBuffer::Command &cmd(cbD->commands.get());
6328 VkViewport *vp = &cmd.args.setViewport.viewport;
6329 vp->x = x;
6330 vp->y = y;
6331 vp->width = w;
6332 vp->height = h;
6333 vp->minDepth = viewport.minDepth();
6334 vp->maxDepth = viewport.maxDepth();
6335
6336 if (cbD->passUsesSecondaryCb) {
6337 df->vkCmdSetViewport(cbD->activeSecondaryCbStack.last(), 0, 1, vp);
6338 cbD->commands.unget();
6339 } else {
6341 }
6342
6343 cbD->currentViewport = viewport;
6344 if (cbD->currentGraphicsPipeline
6345 && !cbD->currentGraphicsPipeline->flags().testFlag(QRhiGraphicsPipeline::UsesScissor))
6346 {
6348 }
6349}
6350
6351void QRhiVulkan::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor)
6352{
6353 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
6355 Q_ASSERT(!cbD->currentGraphicsPipeline
6356 || QRHI_RES(QVkGraphicsPipeline, cbD->currentGraphicsPipeline)
6357 ->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor));
6358 const QSize outputSize = cbD->currentTarget->pixelSize();
6359
6360 // x,y is top-left in VkRect2D but bottom-left in QRhiScissor
6361 int x, y, w, h;
6362 if (!qrhi_toTopLeftRenderTargetRect<Bounded>(outputSize, scissor.scissor(), &x, &y, &w, &h))
6363 return;
6364
6365 QVkCommandBuffer::Command &cmd(cbD->commands.get());
6366 VkRect2D *s = &cmd.args.setScissor.scissor;
6367 s->offset.x = x;
6368 s->offset.y = y;
6369 s->extent.width = uint32_t(w);
6370 s->extent.height = uint32_t(h);
6371
6372 if (cbD->passUsesSecondaryCb) {
6373 df->vkCmdSetScissor(cbD->activeSecondaryCbStack.last(), 0, 1, s);
6374 cbD->commands.unget();
6375 } else {
6377 }
6378
6379 cbD->hasCustomScissorSet = true;
6380}
6381
6382void QRhiVulkan::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c)
6383{
6384 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
6386
6387 if (cbD->passUsesSecondaryCb) {
6388 float constants[] = { float(c.redF()), float(c.greenF()), float(c.blueF()), float(c.alphaF()) };
6389 df->vkCmdSetBlendConstants(cbD->activeSecondaryCbStack.last(), constants);
6390 } else {
6391 QVkCommandBuffer::Command &cmd(cbD->commands.get());
6393 cmd.args.setBlendConstants.c[0] = c.redF();
6394 cmd.args.setBlendConstants.c[1] = c.greenF();
6395 cmd.args.setBlendConstants.c[2] = c.blueF();
6396 cmd.args.setBlendConstants.c[3] = c.alphaF();
6397 }
6398}
6399
6400void QRhiVulkan::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
6401{
6402 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
6404
6405 if (cbD->passUsesSecondaryCb) {
6406 df->vkCmdSetStencilReference(cbD->activeSecondaryCbStack.last(), VK_STENCIL_FRONT_AND_BACK, refValue);
6407 } else {
6408 QVkCommandBuffer::Command &cmd(cbD->commands.get());
6410 cmd.args.setStencilRef.ref = refValue;
6411 }
6412}
6413
6414void QRhiVulkan::setShadingRate(QRhiCommandBuffer *cb, const QSize &coarsePixelSize)
6415{
6416#ifdef VK_KHR_fragment_shading_rate
6417 if (!vkCmdSetFragmentShadingRateKHR)
6418 return;
6419
6420 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
6421 Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
6422 Q_ASSERT(!cbD->currentGraphicsPipeline || QRHI_RES(QVkGraphicsPipeline, cbD->currentGraphicsPipeline)->m_flags.testFlag(QRhiGraphicsPipeline::UsesShadingRate));
6423
6424 VkFragmentShadingRateCombinerOpKHR ops[2] = {
6425 VK_FRAGMENT_SHADING_RATE_COMBINER_OP_MAX_KHR,
6426 VK_FRAGMENT_SHADING_RATE_COMBINER_OP_MAX_KHR
6427 };
6428 VkExtent2D size = { uint32_t(coarsePixelSize.width()), uint32_t(coarsePixelSize.height()) };
6429 if (cbD->passUsesSecondaryCb) {
6430 vkCmdSetFragmentShadingRateKHR(cbD->activeSecondaryCbStack.last(), &size, ops);
6431 } else {
6432 QVkCommandBuffer::Command &cmd(cbD->commands.get());
6433 cmd.cmd = QVkCommandBuffer::Command::SetShadingRate;
6434 cmd.args.setShadingRate.w = size.width;
6435 cmd.args.setShadingRate.h = size.height;
6436 }
6437 if (coarsePixelSize.width() != 1 || coarsePixelSize.height() != 1)
6438 cbD->hasShadingRateSet = true;
6439#else
6440 Q_UNUSED(cb);
6441 Q_UNUSED(coarsePixelSize);
6442#endif
6443}
6444
6445void QRhiVulkan::draw(QRhiCommandBuffer *cb, quint32 vertexCount,
6446 quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
6447{
6448 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
6450
6451 if (cbD->passUsesSecondaryCb) {
6452 df->vkCmdDraw(cbD->activeSecondaryCbStack.last(), vertexCount, instanceCount, firstVertex, firstInstance);
6453 } else {
6454 QVkCommandBuffer::Command &cmd(cbD->commands.get());
6456 cmd.args.draw.vertexCount = vertexCount;
6457 cmd.args.draw.instanceCount = instanceCount;
6458 cmd.args.draw.firstVertex = firstVertex;
6459 cmd.args.draw.firstInstance = firstInstance;
6460 }
6461}
6462
6463void QRhiVulkan::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
6464 quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
6465{
6466 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
6468
6469 if (cbD->passUsesSecondaryCb) {
6470 df->vkCmdDrawIndexed(cbD->activeSecondaryCbStack.last(), indexCount, instanceCount,
6471 firstIndex, vertexOffset, firstInstance);
6472 } else {
6473 QVkCommandBuffer::Command &cmd(cbD->commands.get());
6475 cmd.args.drawIndexed.indexCount = indexCount;
6476 cmd.args.drawIndexed.instanceCount = instanceCount;
6477 cmd.args.drawIndexed.firstIndex = firstIndex;
6478 cmd.args.drawIndexed.vertexOffset = vertexOffset;
6479 cmd.args.drawIndexed.firstInstance = firstInstance;
6480 }
6481}
6482
6483void QRhiVulkan::drawIndirect(QRhiCommandBuffer *cb, QRhiBuffer *indirectBuffer,
6484 quint32 indirectBufferOffset, quint32 drawCount, quint32 stride)
6485{
6486 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
6488 QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
6489
6490 QVkBuffer *indirectBufD = QRHI_RES(QVkBuffer, indirectBuffer);
6491 indirectBufD->lastActiveFrameSlot = currentFrameSlot;
6492 if (indirectBufD->m_type == QRhiBuffer::Dynamic)
6493 executeBufferHostWritesForSlot(indirectBufD, currentFrameSlot);
6494 const int indirectBufSlot = indirectBufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0;
6495 trackedRegisterBuffer(&passResTracker, indirectBufD, indirectBufSlot,
6498 VkBuffer indirectBufVk = indirectBufD->buffers[indirectBufSlot];
6499
6500 if (cbD->passUsesSecondaryCb) {
6501 if (caps.drawIndirectMulti) {
6502 df->vkCmdDrawIndirect(cbD->activeSecondaryCbStack.last(),
6503 indirectBufVk, indirectBufferOffset,
6504 drawCount, stride);
6505 } else {
6506 VkDeviceSize offset = indirectBufferOffset;
6507 for (quint32 i = 0; i < drawCount; ++i) {
6508 df->vkCmdDrawIndirect(cbD->activeSecondaryCbStack.last(),
6509 indirectBufVk, offset,
6510 1, stride);
6511 offset += stride;
6512 }
6513 }
6514 } else {
6515 if (caps.drawIndirectMulti) {
6516 QVkCommandBuffer::Command &cmd(cbD->commands.get());
6518 cmd.args.drawIndirect.indirectBuffer = indirectBufVk;
6519 cmd.args.drawIndirect.indirectBufferOffset = indirectBufferOffset;
6520 cmd.args.drawIndirect.drawCount = drawCount;
6521 cmd.args.drawIndirect.stride = stride;
6522 } else {
6523 VkDeviceSize offset = indirectBufferOffset;
6524 for (quint32 i = 0; i < drawCount; ++i) {
6525 QVkCommandBuffer::Command &cmd(cbD->commands.get());
6527 cmd.args.drawIndirect.indirectBuffer = indirectBufVk;
6528 cmd.args.drawIndirect.indirectBufferOffset = offset;
6529 cmd.args.drawIndirect.drawCount = 1;
6530 cmd.args.drawIndirect.stride = stride;
6531 offset += stride;
6532 }
6533 }
6534 }
6535}
6536
6537void QRhiVulkan::drawIndexedIndirect(QRhiCommandBuffer *cb, QRhiBuffer *indirectBuffer,
6538 quint32 indirectBufferOffset, quint32 drawCount, quint32 stride)
6539{
6540 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
6542 QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
6543
6544 QVkBuffer *indirectBufD = QRHI_RES(QVkBuffer, indirectBuffer);
6545 indirectBufD->lastActiveFrameSlot = currentFrameSlot;
6546 if (indirectBufD->m_type == QRhiBuffer::Dynamic)
6547 executeBufferHostWritesForSlot(indirectBufD, currentFrameSlot);
6548 const int indirectBufSlot = indirectBufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0;
6549 trackedRegisterBuffer(&passResTracker, indirectBufD, indirectBufSlot,
6552 VkBuffer indirectBufVk = indirectBufD->buffers[indirectBufSlot];
6553
6554 if (cbD->passUsesSecondaryCb) {
6555 if (caps.drawIndirectMulti) {
6556 df->vkCmdDrawIndexedIndirect(cbD->activeSecondaryCbStack.last(),
6557 indirectBufVk, indirectBufferOffset,
6558 drawCount, stride);
6559 } else {
6560 VkDeviceSize offset = indirectBufferOffset;
6561 for (quint32 i = 0; i < drawCount; ++i) {
6562 df->vkCmdDrawIndexedIndirect(cbD->activeSecondaryCbStack.last(),
6563 indirectBufVk, offset,
6564 1, stride);
6565 offset += stride;
6566 }
6567 }
6568 } else {
6569 if (caps.drawIndirectMulti) {
6570 QVkCommandBuffer::Command &cmd(cbD->commands.get());
6572 cmd.args.drawIndexedIndirect.indirectBuffer = indirectBufVk;
6573 cmd.args.drawIndexedIndirect.indirectBufferOffset = indirectBufferOffset;
6574 cmd.args.drawIndexedIndirect.drawCount = drawCount;
6575 cmd.args.drawIndexedIndirect.stride = stride;
6576 } else {
6577 VkDeviceSize offset = indirectBufferOffset;
6578 for (quint32 i = 0; i < drawCount; ++i) {
6579 QVkCommandBuffer::Command &cmd(cbD->commands.get());
6581 cmd.args.drawIndexedIndirect.indirectBuffer = indirectBufVk;
6582 cmd.args.drawIndexedIndirect.indirectBufferOffset = offset;
6583 cmd.args.drawIndexedIndirect.drawCount = 1;
6584 cmd.args.drawIndexedIndirect.stride = stride;
6585 offset += stride;
6586 }
6587 }
6588 }
6589}
6590
6591void QRhiVulkan::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name)
6592{
6593#ifdef VK_EXT_debug_utils
6594 if (!debugMarkers || !caps.debugUtils)
6595 return;
6596
6597 VkDebugUtilsLabelEXT label = {};
6598 label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
6599
6600 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
6601 if (cbD->recordingPass != QVkCommandBuffer::NoPass && cbD->passUsesSecondaryCb) {
6602 label.pLabelName = name.constData();
6603 vkCmdBeginDebugUtilsLabelEXT(cbD->activeSecondaryCbStack.last(), &label);
6604 } else {
6605 QVkCommandBuffer::Command &cmd(cbD->commands.get());
6606 cmd.cmd = QVkCommandBuffer::Command::DebugMarkerBegin;
6607 cmd.args.debugMarkerBegin.label = label;
6608 cmd.args.debugMarkerBegin.labelNameIndex = cbD->pools.debugMarkerData.size();
6609 cbD->pools.debugMarkerData.append(name);
6610 }
6611#else
6612 Q_UNUSED(cb);
6613 Q_UNUSED(name);
6614#endif
6615}
6616
6617void QRhiVulkan::debugMarkEnd(QRhiCommandBuffer *cb)
6618{
6619#ifdef VK_EXT_debug_utils
6620 if (!debugMarkers || !caps.debugUtils)
6621 return;
6622
6623 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
6624 if (cbD->recordingPass != QVkCommandBuffer::NoPass && cbD->passUsesSecondaryCb) {
6625 vkCmdEndDebugUtilsLabelEXT(cbD->activeSecondaryCbStack.last());
6626 } else {
6627 QVkCommandBuffer::Command &cmd(cbD->commands.get());
6628 cmd.cmd = QVkCommandBuffer::Command::DebugMarkerEnd;
6629 }
6630#else
6631 Q_UNUSED(cb);
6632#endif
6633}
6634
6635void QRhiVulkan::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg)
6636{
6637#ifdef VK_EXT_debug_utils
6638 if (!debugMarkers || !caps.debugUtils)
6639 return;
6640
6641 VkDebugUtilsLabelEXT label = {};
6642 label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
6643
6644 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
6645 if (cbD->recordingPass != QVkCommandBuffer::NoPass && cbD->passUsesSecondaryCb) {
6646 label.pLabelName = msg.constData();
6647 vkCmdInsertDebugUtilsLabelEXT(cbD->activeSecondaryCbStack.last(), &label);
6648 } else {
6649 QVkCommandBuffer::Command &cmd(cbD->commands.get());
6650 cmd.cmd = QVkCommandBuffer::Command::DebugMarkerInsert;
6651 cmd.args.debugMarkerInsert.label = label;
6652 cmd.args.debugMarkerInsert.labelNameIndex = cbD->pools.debugMarkerData.size();
6653 cbD->pools.debugMarkerData.append(msg);
6654 }
6655#else
6656 Q_UNUSED(cb);
6657 Q_UNUSED(msg);
6658#endif
6659}
6660
6661const QRhiNativeHandles *QRhiVulkan::nativeHandles(QRhiCommandBuffer *cb)
6662{
6663 return QRHI_RES(QVkCommandBuffer, cb)->nativeHandles();
6664}
6665
6667{
6668 Q_ASSERT(cbD->currentTarget);
6669 QVkRenderTargetData *rtD = nullptr;
6671 switch (cbD->currentTarget->resourceType()) {
6672 case QRhiResource::SwapChainRenderTarget:
6673 rtD = &QRHI_RES(QVkSwapChainRenderTarget, cbD->currentTarget)->d;
6674 break;
6675 case QRhiResource::TextureRenderTarget:
6676 rtD = &QRHI_RES(QVkTextureRenderTarget, cbD->currentTarget)->d;
6677 break;
6678 default:
6679 Q_UNREACHABLE();
6680 break;
6681 }
6682 }
6683 return rtD;
6684}
6685
6686void QRhiVulkan::beginExternal(QRhiCommandBuffer *cb)
6687{
6688 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
6689
6690 // When not in a pass, it is simple: record what we have (but do not
6691 // submit), the cb can then be used to record more external commands.
6695 return;
6696 }
6697
6698 // Otherwise, inside a pass, have a secondary command buffer (with
6699 // RENDER_PASS_CONTINUE). Using the main one is not acceptable since we
6700 // cannot just record at this stage, that would mess up the resource
6701 // tracking and commands like TransitionPassResources.
6702
6703 if (cbD->inExternal)
6704 return;
6705
6706 if (!cbD->passUsesSecondaryCb) {
6707 qWarning("beginExternal() within a pass is only supported with secondary command buffers. "
6708 "This can be enabled by passing QRhiCommandBuffer::ExternalContent to beginPass().");
6709 return;
6710 }
6711
6712 VkCommandBuffer secondaryCb = cbD->activeSecondaryCbStack.last();
6713 cbD->activeSecondaryCbStack.removeLast();
6714 endAndEnqueueSecondaryCommandBuffer(secondaryCb, cbD);
6715
6716 VkCommandBuffer extCb = startSecondaryCommandBuffer(maybeRenderTargetData(cbD));
6717 if (extCb) {
6718 cbD->activeSecondaryCbStack.append(extCb);
6719 cbD->inExternal = true;
6720 }
6721}
6722
6723void QRhiVulkan::endExternal(QRhiCommandBuffer *cb)
6724{
6725 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
6726
6728 Q_ASSERT(cbD->commands.isEmpty() && cbD->currentPassResTrackerIndex == -1);
6729 } else if (cbD->inExternal) {
6730 VkCommandBuffer extCb = cbD->activeSecondaryCbStack.last();
6731 cbD->activeSecondaryCbStack.removeLast();
6732 endAndEnqueueSecondaryCommandBuffer(extCb, cbD);
6733 cbD->activeSecondaryCbStack.append(startSecondaryCommandBuffer(maybeRenderTargetData(cbD)));
6734 }
6735
6737}
6738
6739double QRhiVulkan::lastCompletedGpuTime(QRhiCommandBuffer *cb)
6740{
6741 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
6742 return cbD->lastGpuTime;
6743}
6744
6745void QRhiVulkan::setAllocationName(QVkAlloc allocation, const QByteArray &name, int slot)
6746{
6747 if (!debugMarkers || name.isEmpty())
6748 return;
6749
6750 QByteArray decoratedName = name;
6751 if (slot >= 0) {
6752 decoratedName += '/';
6753 decoratedName += QByteArray::number(slot);
6754 }
6755 vmaSetAllocationName(toVmaAllocator(allocator), toVmaAllocation(allocation), decoratedName.constData());
6756}
6757
6758void QRhiVulkan::setObjectName(uint64_t object, VkObjectType type, const QByteArray &name, int slot)
6759{
6760#ifdef VK_EXT_debug_utils
6761 if (!debugMarkers || !caps.debugUtils || name.isEmpty())
6762 return;
6763
6764 VkDebugUtilsObjectNameInfoEXT nameInfo = {};
6765 nameInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
6766 nameInfo.objectType = type;
6767 nameInfo.objectHandle = object;
6768 QByteArray decoratedName = name;
6769 if (slot >= 0) {
6770 decoratedName += '/';
6771 decoratedName += QByteArray::number(slot);
6772 }
6773 nameInfo.pObjectName = decoratedName.constData();
6774 vkSetDebugUtilsObjectNameEXT(dev, &nameInfo);
6775#else
6776 Q_UNUSED(object);
6777 Q_UNUSED(type);
6778 Q_UNUSED(name);
6779 Q_UNUSED(slot);
6780#endif
6781}
6782
6783static inline VkBufferUsageFlagBits toVkBufferUsage(QRhiBuffer::UsageFlags usage)
6784{
6785 int u = 0;
6786 if (usage.testFlag(QRhiBuffer::VertexBuffer))
6787 u |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
6788 if (usage.testFlag(QRhiBuffer::IndexBuffer))
6789 u |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
6790 if (usage.testFlag(QRhiBuffer::UniformBuffer))
6791 u |= VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
6792 if (usage.testFlag(QRhiBuffer::StorageBuffer))
6793 u |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
6794 if (usage.testFlag(QRhiBuffer::IndirectBuffer))
6795 u |= VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
6796 return VkBufferUsageFlagBits(u);
6797}
6798
6799static inline VkFilter toVkFilter(QRhiSampler::Filter f)
6800{
6801 switch (f) {
6802 case QRhiSampler::Nearest:
6803 return VK_FILTER_NEAREST;
6804 case QRhiSampler::Linear:
6805 return VK_FILTER_LINEAR;
6806 default:
6807 Q_UNREACHABLE_RETURN(VK_FILTER_NEAREST);
6808 }
6809}
6810
6811static inline VkSamplerMipmapMode toVkMipmapMode(QRhiSampler::Filter f)
6812{
6813 switch (f) {
6814 case QRhiSampler::None:
6815 return VK_SAMPLER_MIPMAP_MODE_NEAREST;
6816 case QRhiSampler::Nearest:
6817 return VK_SAMPLER_MIPMAP_MODE_NEAREST;
6818 case QRhiSampler::Linear:
6819 return VK_SAMPLER_MIPMAP_MODE_LINEAR;
6820 default:
6821 Q_UNREACHABLE_RETURN(VK_SAMPLER_MIPMAP_MODE_NEAREST);
6822 }
6823}
6824
6825static inline VkSamplerAddressMode toVkAddressMode(QRhiSampler::AddressMode m)
6826{
6827 switch (m) {
6828 case QRhiSampler::Repeat:
6829 return VK_SAMPLER_ADDRESS_MODE_REPEAT;
6830 case QRhiSampler::ClampToEdge:
6831 return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
6832 case QRhiSampler::Mirror:
6833 return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
6834 default:
6835 Q_UNREACHABLE_RETURN(VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE);
6836 }
6837}
6838
6839static inline VkShaderStageFlagBits toVkShaderStage(QRhiShaderStage::Type type)
6840{
6841 switch (type) {
6842 case QRhiShaderStage::Vertex:
6843 return VK_SHADER_STAGE_VERTEX_BIT;
6844 case QRhiShaderStage::TessellationControl:
6845 return VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
6846 case QRhiShaderStage::TessellationEvaluation:
6847 return VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
6848 case QRhiShaderStage::Fragment:
6849 return VK_SHADER_STAGE_FRAGMENT_BIT;
6850 case QRhiShaderStage::Compute:
6851 return VK_SHADER_STAGE_COMPUTE_BIT;
6852 case QRhiShaderStage::Geometry:
6853 return VK_SHADER_STAGE_GEOMETRY_BIT;
6854 default:
6855 Q_UNREACHABLE_RETURN(VK_SHADER_STAGE_VERTEX_BIT);
6856 }
6857}
6858
6859static inline VkFormat toVkAttributeFormat(QRhiVertexInputAttribute::Format format)
6860{
6861 switch (format) {
6862 case QRhiVertexInputAttribute::Float4:
6863 return VK_FORMAT_R32G32B32A32_SFLOAT;
6864 case QRhiVertexInputAttribute::Float3:
6865 return VK_FORMAT_R32G32B32_SFLOAT;
6866 case QRhiVertexInputAttribute::Float2:
6867 return VK_FORMAT_R32G32_SFLOAT;
6868 case QRhiVertexInputAttribute::Float:
6869 return VK_FORMAT_R32_SFLOAT;
6870 case QRhiVertexInputAttribute::UNormByte4:
6871 return VK_FORMAT_R8G8B8A8_UNORM;
6872 case QRhiVertexInputAttribute::UNormByte2:
6873 return VK_FORMAT_R8G8_UNORM;
6874 case QRhiVertexInputAttribute::UNormByte:
6875 return VK_FORMAT_R8_UNORM;
6876 case QRhiVertexInputAttribute::UInt4:
6877 return VK_FORMAT_R32G32B32A32_UINT;
6878 case QRhiVertexInputAttribute::UInt3:
6879 return VK_FORMAT_R32G32B32_UINT;
6880 case QRhiVertexInputAttribute::UInt2:
6881 return VK_FORMAT_R32G32_UINT;
6882 case QRhiVertexInputAttribute::UInt:
6883 return VK_FORMAT_R32_UINT;
6884 case QRhiVertexInputAttribute::SInt4:
6885 return VK_FORMAT_R32G32B32A32_SINT;
6886 case QRhiVertexInputAttribute::SInt3:
6887 return VK_FORMAT_R32G32B32_SINT;
6888 case QRhiVertexInputAttribute::SInt2:
6889 return VK_FORMAT_R32G32_SINT;
6890 case QRhiVertexInputAttribute::SInt:
6891 return VK_FORMAT_R32_SINT;
6892 case QRhiVertexInputAttribute::Half4:
6893 return VK_FORMAT_R16G16B16A16_SFLOAT;
6894 case QRhiVertexInputAttribute::Half3:
6895 return VK_FORMAT_R16G16B16_SFLOAT;
6896 case QRhiVertexInputAttribute::Half2:
6897 return VK_FORMAT_R16G16_SFLOAT;
6898 case QRhiVertexInputAttribute::Half:
6899 return VK_FORMAT_R16_SFLOAT;
6900 case QRhiVertexInputAttribute::UShort4:
6901 return VK_FORMAT_R16G16B16A16_UINT;
6902 case QRhiVertexInputAttribute::UShort3:
6903 return VK_FORMAT_R16G16B16_UINT;
6904 case QRhiVertexInputAttribute::UShort2:
6905 return VK_FORMAT_R16G16_UINT;
6906 case QRhiVertexInputAttribute::UShort:
6907 return VK_FORMAT_R16_UINT;
6908 case QRhiVertexInputAttribute::SShort4:
6909 return VK_FORMAT_R16G16B16A16_SINT;
6910 case QRhiVertexInputAttribute::SShort3:
6911 return VK_FORMAT_R16G16B16_SINT;
6912 case QRhiVertexInputAttribute::SShort2:
6913 return VK_FORMAT_R16G16_SINT;
6914 case QRhiVertexInputAttribute::SShort:
6915 return VK_FORMAT_R16_SINT;
6916 default:
6917 Q_UNREACHABLE_RETURN(VK_FORMAT_R32G32B32A32_SFLOAT);
6918 }
6919}
6920
6921static inline VkPrimitiveTopology toVkTopology(QRhiGraphicsPipeline::Topology t)
6922{
6923 switch (t) {
6924 case QRhiGraphicsPipeline::Triangles:
6925 return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
6926 case QRhiGraphicsPipeline::TriangleStrip:
6927 return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
6928 case QRhiGraphicsPipeline::TriangleFan:
6929 return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN;
6930 case QRhiGraphicsPipeline::Lines:
6931 return VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
6932 case QRhiGraphicsPipeline::LineStrip:
6933 return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
6934 case QRhiGraphicsPipeline::Points:
6935 return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
6936 case QRhiGraphicsPipeline::Patches:
6937 return VK_PRIMITIVE_TOPOLOGY_PATCH_LIST;
6938 default:
6939 Q_UNREACHABLE_RETURN(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
6940 }
6941}
6942
6943static inline VkCullModeFlags toVkCullMode(QRhiGraphicsPipeline::CullMode c)
6944{
6945 switch (c) {
6946 case QRhiGraphicsPipeline::None:
6947 return VK_CULL_MODE_NONE;
6948 case QRhiGraphicsPipeline::Front:
6949 return VK_CULL_MODE_FRONT_BIT;
6950 case QRhiGraphicsPipeline::Back:
6951 return VK_CULL_MODE_BACK_BIT;
6952 default:
6953 Q_UNREACHABLE_RETURN(VK_CULL_MODE_NONE);
6954 }
6955}
6956
6957static inline VkFrontFace toVkFrontFace(QRhiGraphicsPipeline::FrontFace f)
6958{
6959 switch (f) {
6960 case QRhiGraphicsPipeline::CCW:
6961 return VK_FRONT_FACE_COUNTER_CLOCKWISE;
6962 case QRhiGraphicsPipeline::CW:
6963 return VK_FRONT_FACE_CLOCKWISE;
6964 default:
6965 Q_UNREACHABLE_RETURN(VK_FRONT_FACE_COUNTER_CLOCKWISE);
6966 }
6967}
6968
6969static inline VkColorComponentFlags toVkColorComponents(QRhiGraphicsPipeline::ColorMask c)
6970{
6971 int f = 0;
6972 if (c.testFlag(QRhiGraphicsPipeline::R))
6973 f |= VK_COLOR_COMPONENT_R_BIT;
6974 if (c.testFlag(QRhiGraphicsPipeline::G))
6975 f |= VK_COLOR_COMPONENT_G_BIT;
6976 if (c.testFlag(QRhiGraphicsPipeline::B))
6977 f |= VK_COLOR_COMPONENT_B_BIT;
6978 if (c.testFlag(QRhiGraphicsPipeline::A))
6979 f |= VK_COLOR_COMPONENT_A_BIT;
6980 return VkColorComponentFlags(f);
6981}
6982
6983static inline VkBlendFactor toVkBlendFactor(QRhiGraphicsPipeline::BlendFactor f)
6984{
6985 switch (f) {
6986 case QRhiGraphicsPipeline::Zero:
6987 return VK_BLEND_FACTOR_ZERO;
6988 case QRhiGraphicsPipeline::One:
6989 return VK_BLEND_FACTOR_ONE;
6990 case QRhiGraphicsPipeline::SrcColor:
6991 return VK_BLEND_FACTOR_SRC_COLOR;
6992 case QRhiGraphicsPipeline::OneMinusSrcColor:
6993 return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR;
6994 case QRhiGraphicsPipeline::DstColor:
6995 return VK_BLEND_FACTOR_DST_COLOR;
6996 case QRhiGraphicsPipeline::OneMinusDstColor:
6997 return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR;
6998 case QRhiGraphicsPipeline::SrcAlpha:
6999 return VK_BLEND_FACTOR_SRC_ALPHA;
7000 case QRhiGraphicsPipeline::OneMinusSrcAlpha:
7001 return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
7002 case QRhiGraphicsPipeline::DstAlpha:
7003 return VK_BLEND_FACTOR_DST_ALPHA;
7004 case QRhiGraphicsPipeline::OneMinusDstAlpha:
7005 return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;
7006 case QRhiGraphicsPipeline::ConstantColor:
7007 return VK_BLEND_FACTOR_CONSTANT_COLOR;
7008 case QRhiGraphicsPipeline::OneMinusConstantColor:
7009 return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR;
7010 case QRhiGraphicsPipeline::ConstantAlpha:
7011 return VK_BLEND_FACTOR_CONSTANT_ALPHA;
7012 case QRhiGraphicsPipeline::OneMinusConstantAlpha:
7013 return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA;
7014 case QRhiGraphicsPipeline::SrcAlphaSaturate:
7015 return VK_BLEND_FACTOR_SRC_ALPHA_SATURATE;
7016 case QRhiGraphicsPipeline::Src1Color:
7017 return VK_BLEND_FACTOR_SRC1_COLOR;
7018 case QRhiGraphicsPipeline::OneMinusSrc1Color:
7019 return VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR;
7020 case QRhiGraphicsPipeline::Src1Alpha:
7021 return VK_BLEND_FACTOR_SRC1_ALPHA;
7022 case QRhiGraphicsPipeline::OneMinusSrc1Alpha:
7023 return VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA;
7024 default:
7025 Q_UNREACHABLE_RETURN(VK_BLEND_FACTOR_ZERO);
7026 }
7027}
7028
7029static inline VkBlendOp toVkBlendOp(QRhiGraphicsPipeline::BlendOp op)
7030{
7031 switch (op) {
7032 case QRhiGraphicsPipeline::Add:
7033 return VK_BLEND_OP_ADD;
7034 case QRhiGraphicsPipeline::Subtract:
7035 return VK_BLEND_OP_SUBTRACT;
7036 case QRhiGraphicsPipeline::ReverseSubtract:
7037 return VK_BLEND_OP_REVERSE_SUBTRACT;
7038 case QRhiGraphicsPipeline::Min:
7039 return VK_BLEND_OP_MIN;
7040 case QRhiGraphicsPipeline::Max:
7041 return VK_BLEND_OP_MAX;
7042 default:
7043 Q_UNREACHABLE_RETURN(VK_BLEND_OP_ADD);
7044 }
7045}
7046
7047static inline VkCompareOp toVkCompareOp(QRhiGraphicsPipeline::CompareOp op)
7048{
7049 switch (op) {
7050 case QRhiGraphicsPipeline::Never:
7051 return VK_COMPARE_OP_NEVER;
7052 case QRhiGraphicsPipeline::Less:
7053 return VK_COMPARE_OP_LESS;
7054 case QRhiGraphicsPipeline::Equal:
7055 return VK_COMPARE_OP_EQUAL;
7056 case QRhiGraphicsPipeline::LessOrEqual:
7057 return VK_COMPARE_OP_LESS_OR_EQUAL;
7058 case QRhiGraphicsPipeline::Greater:
7059 return VK_COMPARE_OP_GREATER;
7060 case QRhiGraphicsPipeline::NotEqual:
7061 return VK_COMPARE_OP_NOT_EQUAL;
7062 case QRhiGraphicsPipeline::GreaterOrEqual:
7063 return VK_COMPARE_OP_GREATER_OR_EQUAL;
7064 case QRhiGraphicsPipeline::Always:
7065 return VK_COMPARE_OP_ALWAYS;
7066 default:
7067 Q_UNREACHABLE_RETURN(VK_COMPARE_OP_ALWAYS);
7068 }
7069}
7070
7071static inline VkStencilOp toVkStencilOp(QRhiGraphicsPipeline::StencilOp op)
7072{
7073 switch (op) {
7074 case QRhiGraphicsPipeline::StencilZero:
7075 return VK_STENCIL_OP_ZERO;
7076 case QRhiGraphicsPipeline::Keep:
7077 return VK_STENCIL_OP_KEEP;
7078 case QRhiGraphicsPipeline::Replace:
7079 return VK_STENCIL_OP_REPLACE;
7080 case QRhiGraphicsPipeline::IncrementAndClamp:
7081 return VK_STENCIL_OP_INCREMENT_AND_CLAMP;
7082 case QRhiGraphicsPipeline::DecrementAndClamp:
7083 return VK_STENCIL_OP_DECREMENT_AND_CLAMP;
7084 case QRhiGraphicsPipeline::Invert:
7085 return VK_STENCIL_OP_INVERT;
7086 case QRhiGraphicsPipeline::IncrementAndWrap:
7087 return VK_STENCIL_OP_INCREMENT_AND_WRAP;
7088 case QRhiGraphicsPipeline::DecrementAndWrap:
7089 return VK_STENCIL_OP_DECREMENT_AND_WRAP;
7090 default:
7091 Q_UNREACHABLE_RETURN(VK_STENCIL_OP_KEEP);
7092 }
7093}
7094
7095static inline VkPolygonMode toVkPolygonMode(QRhiGraphicsPipeline::PolygonMode mode)
7096{
7097 switch (mode) {
7098 case QRhiGraphicsPipeline::Fill:
7099 return VK_POLYGON_MODE_FILL;
7100 case QRhiGraphicsPipeline::Line:
7101 return VK_POLYGON_MODE_LINE;
7102 default:
7103 Q_UNREACHABLE_RETURN(VK_POLYGON_MODE_FILL);
7104 }
7105}
7106
7107static inline void fillVkStencilOpState(VkStencilOpState *dst, const QRhiGraphicsPipeline::StencilOpState &src)
7108{
7109 dst->failOp = toVkStencilOp(src.failOp);
7110 dst->passOp = toVkStencilOp(src.passOp);
7111 dst->depthFailOp = toVkStencilOp(src.depthFailOp);
7112 dst->compareOp = toVkCompareOp(src.compareOp);
7113}
7114
7115static inline VkDescriptorType toVkDescriptorType(const QRhiShaderResourceBinding::Data *b)
7116{
7117 switch (b->type) {
7118 case QRhiShaderResourceBinding::UniformBuffer:
7119 return b->u.ubuf.hasDynamicOffset ? VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC
7120 : VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
7121
7122 case QRhiShaderResourceBinding::SampledTexture:
7123 return VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
7124
7125 case QRhiShaderResourceBinding::Texture:
7126 return VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
7127
7128 case QRhiShaderResourceBinding::Sampler:
7129 return VK_DESCRIPTOR_TYPE_SAMPLER;
7130
7131 case QRhiShaderResourceBinding::ImageLoad:
7132 case QRhiShaderResourceBinding::ImageStore:
7133 case QRhiShaderResourceBinding::ImageLoadStore:
7134 return VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
7135
7136 case QRhiShaderResourceBinding::BufferLoad:
7137 case QRhiShaderResourceBinding::BufferStore:
7138 case QRhiShaderResourceBinding::BufferLoadStore:
7139 return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
7140
7141 default:
7142 Q_UNREACHABLE_RETURN(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
7143 }
7144}
7145
7146static inline VkShaderStageFlags toVkShaderStageFlags(QRhiShaderResourceBinding::StageFlags stage)
7147{
7148 int s = 0;
7149 if (stage.testFlag(QRhiShaderResourceBinding::VertexStage))
7150 s |= VK_SHADER_STAGE_VERTEX_BIT;
7151 if (stage.testFlag(QRhiShaderResourceBinding::FragmentStage))
7152 s |= VK_SHADER_STAGE_FRAGMENT_BIT;
7153 if (stage.testFlag(QRhiShaderResourceBinding::ComputeStage))
7154 s |= VK_SHADER_STAGE_COMPUTE_BIT;
7155 if (stage.testFlag(QRhiShaderResourceBinding::TessellationControlStage))
7156 s |= VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
7157 if (stage.testFlag(QRhiShaderResourceBinding::TessellationEvaluationStage))
7158 s |= VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
7159 if (stage.testFlag(QRhiShaderResourceBinding::GeometryStage))
7160 s |= VK_SHADER_STAGE_GEOMETRY_BIT;
7161 return VkShaderStageFlags(s);
7162}
7163
7164static inline VkCompareOp toVkTextureCompareOp(QRhiSampler::CompareOp op)
7165{
7166 switch (op) {
7167 case QRhiSampler::Never:
7168 return VK_COMPARE_OP_NEVER;
7169 case QRhiSampler::Less:
7170 return VK_COMPARE_OP_LESS;
7171 case QRhiSampler::Equal:
7172 return VK_COMPARE_OP_EQUAL;
7173 case QRhiSampler::LessOrEqual:
7174 return VK_COMPARE_OP_LESS_OR_EQUAL;
7175 case QRhiSampler::Greater:
7176 return VK_COMPARE_OP_GREATER;
7177 case QRhiSampler::NotEqual:
7178 return VK_COMPARE_OP_NOT_EQUAL;
7179 case QRhiSampler::GreaterOrEqual:
7180 return VK_COMPARE_OP_GREATER_OR_EQUAL;
7181 case QRhiSampler::Always:
7182 return VK_COMPARE_OP_ALWAYS;
7183 default:
7184 Q_UNREACHABLE_RETURN(VK_COMPARE_OP_NEVER);
7185 }
7186}
7187
7188QVkBuffer::QVkBuffer(QRhiImplementation *rhi, Type type, UsageFlags usage, quint32 size)
7190{
7191 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
7192 buffers[i] = stagingBuffers[i] = VK_NULL_HANDLE;
7193 allocations[i] = stagingAllocations[i] = nullptr;
7194 }
7195}
7196
7198{
7199 destroy();
7200}
7201
7203{
7204 if (!buffers[0])
7205 return;
7206
7210
7211 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
7212 e.buffer.buffers[i] = buffers[i];
7213 e.buffer.allocations[i] = allocations[i];
7214 e.buffer.stagingBuffers[i] = stagingBuffers[i];
7215 e.buffer.stagingAllocations[i] = stagingAllocations[i];
7216
7217 buffers[i] = VK_NULL_HANDLE;
7218 allocations[i] = nullptr;
7219 stagingBuffers[i] = VK_NULL_HANDLE;
7220 stagingAllocations[i] = nullptr;
7221 pendingDynamicUpdates[i].clear();
7222 }
7223
7224 QRHI_RES_RHI(QRhiVulkan);
7225 // destroy() implementations, unlike other functions, are expected to test
7226 // for m_rhi being null, to allow surviving in case one attempts to destroy
7227 // a (leaked) resource after the QRhi.
7228 if (rhiD) {
7229 rhiD->releaseQueue.append(e);
7230 rhiD->unregisterResource(this);
7231 }
7232}
7233
7235{
7236 if (buffers[0])
7237 destroy();
7238
7239 if (m_usage.testFlag(QRhiBuffer::StorageBuffer) && m_type == Dynamic) {
7240 qWarning("StorageBuffer cannot be combined with Dynamic");
7241 return false;
7242 }
7243
7244 const quint32 nonZeroSize = m_size <= 0 ? 256 : m_size;
7245
7246 VkBufferCreateInfo bufferInfo = {};
7247 bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
7248 bufferInfo.size = nonZeroSize;
7249 bufferInfo.usage = toVkBufferUsage(m_usage);
7250
7251 VmaAllocationCreateInfo allocInfo = {};
7252
7253 if (m_type == Dynamic) {
7254#ifndef Q_OS_DARWIN // not for MoltenVK
7255 // Keep mapped all the time. Essential f.ex. with some mobile GPUs,
7256 // where mapping and unmapping an entire allocation every time updating
7257 // a suballocated buffer presents a significant perf. hit.
7258 allocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
7259#endif
7260 // host visible, frequent changes
7261 allocInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
7262 } else {
7263 allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
7264 bufferInfo.usage |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
7265 }
7266
7267 QRHI_RES_RHI(QRhiVulkan);
7268 VkResult err = VK_SUCCESS;
7269 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
7270 buffers[i] = VK_NULL_HANDLE;
7271 allocations[i] = nullptr;
7272 usageState[i].access = usageState[i].stage = 0;
7273 if (i == 0 || m_type == Dynamic) {
7274 VmaAllocation allocation;
7275 err = vmaCreateBuffer(toVmaAllocator(rhiD->allocator), &bufferInfo, &allocInfo, &buffers[i], &allocation, nullptr);
7276 if (err != VK_SUCCESS)
7277 break;
7278 allocations[i] = allocation;
7279 rhiD->setAllocationName(allocation, m_objectName, m_type == Dynamic ? i : -1);
7280 rhiD->setObjectName(uint64_t(buffers[i]), VK_OBJECT_TYPE_BUFFER, m_objectName,
7281 m_type == Dynamic ? i : -1);
7282 }
7283 }
7284
7285 if (err != VK_SUCCESS) {
7286 qWarning("Failed to create buffer of size %u: %d", nonZeroSize, err);
7287 rhiD->printExtraErrorInfo(err);
7288 return false;
7289 }
7290
7292 generation += 1;
7293 rhiD->registerResource(this);
7294 return true;
7295}
7296
7298{
7299 if (m_type == Dynamic) {
7300 QRHI_RES_RHI(QRhiVulkan);
7301 NativeBuffer b;
7302 Q_ASSERT(sizeof(b.objects) / sizeof(b.objects[0]) >= size_t(QVK_FRAMES_IN_FLIGHT));
7303 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
7305 b.objects[i] = &buffers[i];
7306 }
7307 b.slotCount = QVK_FRAMES_IN_FLIGHT;
7308 return b;
7309 }
7310 return { { &buffers[0] }, 1 };
7311}
7312
7314{
7315 // Shortcut the entire buffer update mechanism and allow the client to do
7316 // the host writes directly to the buffer. This will lead to unexpected
7317 // results when combined with QRhiResourceUpdateBatch-based updates for the
7318 // buffer, but provides a fast path for dynamic buffers that have all their
7319 // content changed in every frame.
7320 Q_ASSERT(m_type == Dynamic);
7321 QRHI_RES_RHI(QRhiVulkan);
7322 Q_ASSERT(rhiD->inFrame);
7323 const int slot = rhiD->currentFrameSlot;
7324 void *p = nullptr;
7325 VmaAllocation a = toVmaAllocation(allocations[slot]);
7326 VkResult err = vmaMapMemory(toVmaAllocator(rhiD->allocator), a, &p);
7327 if (err != VK_SUCCESS) {
7328 qWarning("Failed to map buffer: %d", err);
7329 return nullptr;
7330 }
7331 return static_cast<char *>(p);
7332}
7333
7335{
7336 QRHI_RES_RHI(QRhiVulkan);
7337 const int slot = rhiD->currentFrameSlot;
7338 VmaAllocation a = toVmaAllocation(allocations[slot]);
7339 vmaFlushAllocation(toVmaAllocator(rhiD->allocator), a, 0, m_size);
7340 vmaUnmapMemory(toVmaAllocator(rhiD->allocator), a);
7341}
7342
7343QVkRenderBuffer::QVkRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
7344 int sampleCount, Flags flags,
7345 QRhiTexture::Format backingFormatHint)
7347{
7348}
7349
7351{
7352 destroy();
7353 delete backingTexture;
7354}
7355
7357{
7358 if (!memory && !backingTexture)
7359 return;
7360
7364
7365 e.renderBuffer.memory = memory;
7366 e.renderBuffer.image = image;
7367 e.renderBuffer.imageView = imageView;
7368
7369 memory = VK_NULL_HANDLE;
7370 image = VK_NULL_HANDLE;
7371 imageView = VK_NULL_HANDLE;
7372
7373 if (backingTexture) {
7377 }
7378
7379 QRHI_RES_RHI(QRhiVulkan);
7380 if (rhiD) {
7381 rhiD->releaseQueue.append(e);
7382 rhiD->unregisterResource(this);
7383 }
7384}
7385
7387{
7388 if (memory || backingTexture)
7389 destroy();
7390
7391 if (m_pixelSize.isEmpty())
7392 return false;
7393
7394 QRHI_RES_RHI(QRhiVulkan);
7395 samples = rhiD->effectiveSampleCountBits(m_sampleCount);
7396
7397 switch (m_type) {
7398 case QRhiRenderBuffer::Color:
7399 {
7400 if (!backingTexture) {
7401 backingTexture = QRHI_RES(QVkTexture, rhiD->createTexture(backingFormat(),
7402 m_pixelSize,
7403 1,
7404 0,
7405 m_sampleCount,
7406 QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource));
7407 } else {
7408 backingTexture->setPixelSize(m_pixelSize);
7409 backingTexture->setSampleCount(m_sampleCount);
7410 }
7411 backingTexture->setName(m_objectName);
7413 return false;
7414 vkformat = backingTexture->vkformat;
7415 }
7416 break;
7417 case QRhiRenderBuffer::DepthStencil:
7418 vkformat = rhiD->optimalDepthStencilFormat();
7419 if (!rhiD->createTransientImage(vkformat,
7420 m_pixelSize,
7421 VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
7422 VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT,
7423 samples,
7424 &memory,
7425 &image,
7426 &imageView,
7427 1))
7428 {
7429 return false;
7430 }
7431 rhiD->setObjectName(uint64_t(image), VK_OBJECT_TYPE_IMAGE, m_objectName);
7432 break;
7433 default:
7434 Q_UNREACHABLE();
7435 break;
7436 }
7437
7439 generation += 1;
7440 rhiD->registerResource(this);
7441 return true;
7442}
7443
7445{
7446 if (m_backingFormatHint != QRhiTexture::UnknownFormat)
7447 return m_backingFormatHint;
7448 else
7449 return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
7450}
7451
7452QVkTexture::QVkTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
7453 int arraySize, int sampleCount, Flags flags)
7455{
7456 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
7457 stagingBuffers[i] = VK_NULL_HANDLE;
7458 stagingAllocations[i] = nullptr;
7459 }
7460 for (int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i)
7461 perLevelImageViews[i] = VK_NULL_HANDLE;
7462}
7463
7465{
7466 destroy();
7467}
7468
7470{
7471 if (!image)
7472 return;
7473
7477
7478 e.texture.image = owns ? image : VK_NULL_HANDLE;
7479 e.texture.imageView = imageView;
7480 e.texture.allocation = owns ? imageAlloc : nullptr;
7481
7482 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
7483 e.texture.stagingBuffers[i] = stagingBuffers[i];
7484 e.texture.stagingAllocations[i] = stagingAllocations[i];
7485
7486 stagingBuffers[i] = VK_NULL_HANDLE;
7487 stagingAllocations[i] = nullptr;
7488 }
7489
7490 for (int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i) {
7491 e.texture.extraImageViews[i] = perLevelImageViews[i];
7492 perLevelImageViews[i] = VK_NULL_HANDLE;
7493 }
7494
7495 image = VK_NULL_HANDLE;
7496 imageView = VK_NULL_HANDLE;
7497 imageAlloc = nullptr;
7498
7499 QRHI_RES_RHI(QRhiVulkan);
7500 if (rhiD) {
7501 rhiD->releaseQueue.append(e);
7502 rhiD->unregisterResource(this);
7503 }
7504}
7505
7506bool QVkTexture::prepareCreate(QSize *adjustedSize)
7507{
7508 if (image)
7509 destroy();
7510
7511 QRHI_RES_RHI(QRhiVulkan);
7512 vkformat = toVkTextureFormat(m_format, m_flags);
7513 if (m_writeViewFormat.format != UnknownFormat)
7514 viewFormat = toVkTextureFormat(m_writeViewFormat.format, m_writeViewFormat.srgb ? sRGB : Flags());
7515 else
7516 viewFormat = vkformat;
7517 if (m_readViewFormat.format != UnknownFormat)
7518 viewFormatForSampling = toVkTextureFormat(m_readViewFormat.format, m_readViewFormat.srgb ? sRGB : Flags());
7519 else
7520 viewFormatForSampling = vkformat;
7521
7522 VkFormatProperties props;
7523 rhiD->f->vkGetPhysicalDeviceFormatProperties(rhiD->physDev, vkformat, &props);
7524 const bool canSampleOptimal = (props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT);
7525 if (!canSampleOptimal) {
7526 qWarning("Texture sampling with optimal tiling for format %d not supported", vkformat);
7527 return false;
7528 }
7529
7530 const bool isCube = m_flags.testFlag(CubeMap);
7531 const bool isArray = m_flags.testFlag(TextureArray);
7532 const bool is3D = m_flags.testFlag(ThreeDimensional);
7533 const bool is1D = m_flags.testFlag(OneDimensional);
7534 const bool hasMipMaps = m_flags.testFlag(MipMapped);
7535
7536 const QSize size = is1D ? QSize(qMax(1, m_pixelSize.width()), 1)
7537 : (m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize);
7538
7539 mipLevelCount = uint(hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1);
7540 const int maxLevels = QRhi::MAX_MIP_LEVELS;
7541 if (mipLevelCount > maxLevels) {
7542 qWarning("Too many mip levels (%d, max is %d), truncating mip chain", mipLevelCount, maxLevels);
7543 mipLevelCount = maxLevels;
7544 }
7545 samples = rhiD->effectiveSampleCountBits(m_sampleCount);
7546 if (samples > VK_SAMPLE_COUNT_1_BIT) {
7547 if (isCube) {
7548 qWarning("Cubemap texture cannot be multisample");
7549 return false;
7550 }
7551 if (is3D) {
7552 qWarning("3D texture cannot be multisample");
7553 return false;
7554 }
7555 if (hasMipMaps) {
7556 qWarning("Multisample texture cannot have mipmaps");
7557 return false;
7558 }
7559 }
7560 if (isCube && is3D) {
7561 qWarning("Texture cannot be both cube and 3D");
7562 return false;
7563 }
7564 if (isArray && is3D) {
7565 qWarning("Texture cannot be both array and 3D");
7566 return false;
7567 }
7568 if (isCube && is1D) {
7569 qWarning("Texture cannot be both cube and 1D");
7570 return false;
7571 }
7572 if (is1D && is3D) {
7573 qWarning("Texture cannot be both 1D and 3D");
7574 return false;
7575 }
7576 if (m_depth > 1 && !is3D) {
7577 qWarning("Texture cannot have a depth of %d when it is not 3D", m_depth);
7578 return false;
7579 }
7580 if (m_arraySize > 0 && !isArray) {
7581 qWarning("Texture cannot have an array size of %d when it is not an array", m_arraySize);
7582 return false;
7583 }
7584 if (m_arraySize < 1 && isArray) {
7585 qWarning("Texture is an array but array size is %d", m_arraySize);
7586 return false;
7587 }
7588
7589 usageState.layout = VK_IMAGE_LAYOUT_PREINITIALIZED;
7590 usageState.access = 0;
7591 usageState.stage = 0;
7592
7593 if (adjustedSize)
7594 *adjustedSize = size;
7595
7596 return true;
7597}
7598
7600{
7601 QRHI_RES_RHI(QRhiVulkan);
7602
7603 const auto aspectMask = aspectMaskForTextureFormat(m_format);
7604 const bool isCube = m_flags.testFlag(CubeMap);
7605 const bool isArray = m_flags.testFlag(TextureArray);
7606 const bool is3D = m_flags.testFlag(ThreeDimensional);
7607 const bool is1D = m_flags.testFlag(OneDimensional);
7608
7609 VkImageViewCreateInfo viewInfo = {};
7610 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
7611 viewInfo.image = image;
7612 viewInfo.viewType = isCube
7613 ? VK_IMAGE_VIEW_TYPE_CUBE
7614 : (is3D ? VK_IMAGE_VIEW_TYPE_3D
7615 : (is1D ? (isArray ? VK_IMAGE_VIEW_TYPE_1D_ARRAY : VK_IMAGE_VIEW_TYPE_1D)
7616 : (isArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D)));
7617 viewInfo.format = viewFormatForSampling;
7618 viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
7619 viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
7620 viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
7621 viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
7622 viewInfo.subresourceRange.aspectMask = aspectMask;
7623 // Force-remove the VK_IMAGE_ASPECT_STENCIL_BIT
7624 // Another view with this bit is probably needed for stencil
7625 viewInfo.subresourceRange.aspectMask &= ~VK_IMAGE_ASPECT_STENCIL_BIT;
7626 viewInfo.subresourceRange.levelCount = mipLevelCount;
7627 if (isArray && m_arrayRangeStart >= 0 && m_arrayRangeLength >= 0) {
7628 viewInfo.subresourceRange.baseArrayLayer = uint32_t(m_arrayRangeStart);
7629 viewInfo.subresourceRange.layerCount = uint32_t(m_arrayRangeLength);
7630 } else {
7631 viewInfo.subresourceRange.layerCount = isCube ? 6 : (isArray ? qMax(0, m_arraySize) : 1);
7632 }
7633
7634 VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &imageView);
7635 if (err != VK_SUCCESS) {
7636 qWarning("Failed to create image view: %d", err);
7637 return false;
7638 }
7639
7641 generation += 1;
7642
7643 return true;
7644}
7645
7647{
7648 QSize size;
7649 if (!prepareCreate(&size))
7650 return false;
7651
7652 QRHI_RES_RHI(QRhiVulkan);
7653 const bool isRenderTarget = m_flags.testFlag(QRhiTexture::RenderTarget);
7654 const bool isDepth = isDepthTextureFormat(m_format);
7655 const bool isCube = m_flags.testFlag(CubeMap);
7656 const bool isArray = m_flags.testFlag(TextureArray);
7657 const bool is3D = m_flags.testFlag(ThreeDimensional);
7658 const bool is1D = m_flags.testFlag(OneDimensional);
7659
7660 VkImageCreateInfo imageInfo = {};
7661 imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
7662 imageInfo.flags = 0;
7663 if (isCube)
7664 imageInfo.flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
7665
7666 if (is3D && isRenderTarget) {
7667 // This relies on a Vulkan 1.1 constant. For guaranteed proper behavior
7668 // this also requires that at run time the VkInstance has at least API 1.1
7669 // enabled. (though it works as expected with some Vulkan (1.2)
7670 // implementations regardless of the requested API version, but f.ex. the
7671 // validation layer complains when using this without enabling >=1.1)
7672 if (!rhiD->caps.texture3DSliceAs2D)
7673 qWarning("QRhiVulkan: Rendering to 3D texture slice may not be functional without API 1.1 on the VkInstance");
7674#ifdef VK_VERSION_1_1
7675 imageInfo.flags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT;
7676#else
7677 imageInfo.flags |= 0x00000020;
7678#endif
7679 }
7680
7681 imageInfo.imageType = is1D ? VK_IMAGE_TYPE_1D : is3D ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D;
7682 imageInfo.format = vkformat;
7683 imageInfo.extent.width = uint32_t(size.width());
7684 imageInfo.extent.height = uint32_t(size.height());
7685 imageInfo.extent.depth = is3D ? qMax(1, m_depth) : 1;
7686 imageInfo.mipLevels = mipLevelCount;
7687 imageInfo.arrayLayers = isCube ? 6 : (isArray ? qMax(0, m_arraySize) : 1);
7688 imageInfo.samples = samples;
7689 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
7690 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
7691
7692 imageInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
7693 if (isRenderTarget) {
7694 if (isDepth)
7695 imageInfo.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
7696 else
7697 imageInfo.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
7698 }
7699 if (m_flags.testFlag(QRhiTexture::UsedAsTransferSource))
7700 imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
7701 if (m_flags.testFlag(QRhiTexture::UsedWithGenerateMips))
7702 imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
7703 if (m_flags.testFlag(QRhiTexture::UsedWithLoadStore))
7704 imageInfo.usage |= VK_IMAGE_USAGE_STORAGE_BIT;
7705#ifdef VK_KHR_fragment_shading_rate
7706 if (m_flags.testFlag(QRhiTexture::UsedAsShadingRateMap) && rhiD->caps.imageBasedShadingRate)
7707 imageInfo.usage |= VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR;
7708#endif
7709
7710 VmaAllocationCreateInfo allocInfo = {};
7711 allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
7712
7713 VmaAllocation allocation;
7714 VkResult err = vmaCreateImage(toVmaAllocator(rhiD->allocator), &imageInfo, &allocInfo, &image, &allocation, nullptr);
7715 if (err != VK_SUCCESS) {
7716 qWarning("Failed to create image (with VkImageCreateInfo %ux%u depth %u vkformat 0x%X mips %u layers %u vksamples 0x%X): %d",
7717 imageInfo.extent.width, imageInfo.extent.height, imageInfo.extent.depth,
7718 int(imageInfo.format),
7719 imageInfo.mipLevels,
7720 imageInfo.arrayLayers,
7721 int(imageInfo.samples),
7722 err);
7723 rhiD->printExtraErrorInfo(err);
7724 return false;
7725 }
7726 imageAlloc = allocation;
7727 rhiD->setAllocationName(allocation, m_objectName);
7728
7729 if (!finishCreate())
7730 return false;
7731
7732 rhiD->setObjectName(uint64_t(image), VK_OBJECT_TYPE_IMAGE, m_objectName);
7733
7734 owns = true;
7735 rhiD->registerResource(this);
7736 return true;
7737}
7738
7739bool QVkTexture::createFrom(QRhiTexture::NativeTexture src)
7740{
7741 VkImage img = VkImage(src.object);
7742 if (img == 0)
7743 return false;
7744
7745 if (!prepareCreate())
7746 return false;
7747
7748 image = img;
7749
7750 if (!finishCreate())
7751 return false;
7752
7753 usageState.layout = VkImageLayout(src.layout);
7754
7755 owns = false;
7756 QRHI_RES_RHI(QRhiVulkan);
7757 rhiD->registerResource(this);
7758 return true;
7759}
7760
7762{
7763 return {quint64(image), usageState.layout};
7764}
7765
7767{
7768 usageState.layout = VkImageLayout(layout);
7769}
7770
7772{
7773 Q_ASSERT(level >= 0 && level < int(mipLevelCount));
7774 if (perLevelImageViews[level] != VK_NULL_HANDLE)
7775 return perLevelImageViews[level];
7776
7777 const VkImageAspectFlags aspectMask = aspectMaskForTextureFormat(m_format);
7778 const bool isCube = m_flags.testFlag(CubeMap);
7779 const bool isArray = m_flags.testFlag(TextureArray);
7780 const bool is3D = m_flags.testFlag(ThreeDimensional);
7781 const bool is1D = m_flags.testFlag(OneDimensional);
7782
7783 VkImageViewCreateInfo viewInfo = {};
7784 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
7785 viewInfo.image = image;
7786 viewInfo.viewType = isCube
7787 ? VK_IMAGE_VIEW_TYPE_CUBE
7788 : (is3D ? VK_IMAGE_VIEW_TYPE_3D
7789 : (is1D ? (isArray ? VK_IMAGE_VIEW_TYPE_1D_ARRAY : VK_IMAGE_VIEW_TYPE_1D)
7790 : (isArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D)));
7791 viewInfo.format = viewFormat; // this is writeViewFormat, regardless of Load, Store, or LoadStore; intentional
7792 viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
7793 viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
7794 viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
7795 viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
7796 viewInfo.subresourceRange.aspectMask = aspectMask;
7797 viewInfo.subresourceRange.baseMipLevel = uint32_t(level);
7798 viewInfo.subresourceRange.levelCount = 1;
7799 viewInfo.subresourceRange.baseArrayLayer = 0;
7800 viewInfo.subresourceRange.layerCount = isCube ? 6 : (isArray ? qMax(0, m_arraySize) : 1);
7801
7802 VkImageView v = VK_NULL_HANDLE;
7803 QRHI_RES_RHI(QRhiVulkan);
7804 VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &v);
7805 if (err != VK_SUCCESS) {
7806 qWarning("Failed to create image view: %d", err);
7807 return VK_NULL_HANDLE;
7808 }
7809
7810 perLevelImageViews[level] = v;
7811 return v;
7812}
7813
7814QVkSampler::QVkSampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
7815 AddressMode u, AddressMode v, AddressMode w)
7817{
7818}
7819
7821{
7822 destroy();
7823}
7824
7826{
7827 if (!sampler)
7828 return;
7829
7833
7834 e.sampler.sampler = sampler;
7835 sampler = VK_NULL_HANDLE;
7836
7837 QRHI_RES_RHI(QRhiVulkan);
7838 if (rhiD) {
7839 rhiD->releaseQueue.append(e);
7840 rhiD->unregisterResource(this);
7841 }
7842}
7843
7845{
7846 if (sampler)
7847 destroy();
7848
7849 VkSamplerCreateInfo samplerInfo = {};
7850 samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
7851 samplerInfo.magFilter = toVkFilter(m_magFilter);
7852 samplerInfo.minFilter = toVkFilter(m_minFilter);
7853 samplerInfo.mipmapMode = toVkMipmapMode(m_mipmapMode);
7854 samplerInfo.addressModeU = toVkAddressMode(m_addressU);
7855 samplerInfo.addressModeV = toVkAddressMode(m_addressV);
7856 samplerInfo.addressModeW = toVkAddressMode(m_addressW);
7857 samplerInfo.maxAnisotropy = 1.0f;
7858 samplerInfo.compareEnable = m_compareOp != Never;
7859 samplerInfo.compareOp = toVkTextureCompareOp(m_compareOp);
7860 samplerInfo.maxLod = m_mipmapMode == None ? 0.25f : 1000.0f;
7861
7862 QRHI_RES_RHI(QRhiVulkan);
7863 VkResult err = rhiD->df->vkCreateSampler(rhiD->dev, &samplerInfo, nullptr, &sampler);
7864 if (err != VK_SUCCESS) {
7865 qWarning("Failed to create sampler: %d", err);
7866 return false;
7867 }
7868
7870 generation += 1;
7871 rhiD->registerResource(this);
7872 return true;
7873}
7874
7877{
7878 serializedFormatData.reserve(64);
7879}
7880
7885
7887{
7888 if (!rp)
7889 return;
7890
7891 if (!ownsRp) {
7892 rp = VK_NULL_HANDLE;
7893 return;
7894 }
7895
7899
7900 e.renderPass.rp = rp;
7901
7902 rp = VK_NULL_HANDLE;
7903
7904 QRHI_RES_RHI(QRhiVulkan);
7905 if (rhiD) {
7906 rhiD->releaseQueue.append(e);
7907 rhiD->unregisterResource(this);
7908 }
7909}
7910
7911static inline bool attachmentDescriptionEquals(const VkAttachmentDescription &a, const VkAttachmentDescription &b)
7912{
7913 return a.format == b.format
7914 && a.samples == b.samples
7915 && a.loadOp == b.loadOp
7916 && a.storeOp == b.storeOp
7917 && a.stencilLoadOp == b.stencilLoadOp
7918 && a.stencilStoreOp == b.stencilStoreOp
7919 && a.initialLayout == b.initialLayout
7920 && a.finalLayout == b.finalLayout;
7921}
7922
7923bool QVkRenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other) const
7924{
7925 if (other == this)
7926 return true;
7927
7928 if (!other)
7929 return false;
7930
7932
7933 if (attDescs.size() != o->attDescs.size())
7934 return false;
7935 if (colorRefs.size() != o->colorRefs.size())
7936 return false;
7937 if (resolveRefs.size() != o->resolveRefs.size())
7938 return false;
7940 return false;
7942 return false;
7943 if (multiViewCount != o->multiViewCount)
7944 return false;
7946 return false;
7947
7948 for (int i = 0, ie = colorRefs.size(); i != ie; ++i) {
7949 const uint32_t attIdx = colorRefs[i].attachment;
7950 if (attIdx != o->colorRefs[i].attachment)
7951 return false;
7952 if (attIdx != VK_ATTACHMENT_UNUSED && !attachmentDescriptionEquals(attDescs[attIdx], o->attDescs[attIdx]))
7953 return false;
7954 }
7955
7956 if (hasDepthStencil) {
7957 const uint32_t attIdx = dsRef.attachment;
7958 if (attIdx != o->dsRef.attachment)
7959 return false;
7960 if (attIdx != VK_ATTACHMENT_UNUSED && !attachmentDescriptionEquals(attDescs[attIdx], o->attDescs[attIdx]))
7961 return false;
7962 }
7963
7964 for (int i = 0, ie = resolveRefs.size(); i != ie; ++i) {
7965 const uint32_t attIdx = resolveRefs[i].attachment;
7966 if (attIdx != o->resolveRefs[i].attachment)
7967 return false;
7968 if (attIdx != VK_ATTACHMENT_UNUSED && !attachmentDescriptionEquals(attDescs[attIdx], o->attDescs[attIdx]))
7969 return false;
7970 }
7971
7973 const uint32_t attIdx = dsResolveRef.attachment;
7974 if (attIdx != o->dsResolveRef.attachment)
7975 return false;
7976 if (attIdx != VK_ATTACHMENT_UNUSED && !attachmentDescriptionEquals(attDescs[attIdx], o->attDescs[attIdx]))
7977 return false;
7978 }
7979
7980 if (hasShadingRateMap) {
7981 const uint32_t attIdx = shadingRateRef.attachment;
7982 if (attIdx != o->shadingRateRef.attachment)
7983 return false;
7984 if (attIdx != VK_ATTACHMENT_UNUSED && !attachmentDescriptionEquals(attDescs[attIdx], o->attDescs[attIdx]))
7985 return false;
7986 }
7987
7988 // subpassDeps is not included
7989
7990 return true;
7991}
7992
7994{
7995 serializedFormatData.clear();
7996 auto p = std::back_inserter(serializedFormatData);
7997
7998 *p++ = attDescs.size();
7999 *p++ = colorRefs.size();
8000 *p++ = resolveRefs.size();
8001 *p++ = hasDepthStencil;
8003 *p++ = hasShadingRateMap;
8004 *p++ = multiViewCount;
8005
8006 auto serializeAttachmentData = [this, &p](uint32_t attIdx) {
8007 const bool used = attIdx != VK_ATTACHMENT_UNUSED;
8008 const VkAttachmentDescription *a = used ? &attDescs[attIdx] : nullptr;
8009 *p++ = used ? a->format : 0;
8010 *p++ = used ? a->samples : 0;
8011 *p++ = used ? a->loadOp : 0;
8012 *p++ = used ? a->storeOp : 0;
8013 *p++ = used ? a->stencilLoadOp : 0;
8014 *p++ = used ? a->stencilStoreOp : 0;
8015 *p++ = used ? a->initialLayout : 0;
8016 *p++ = used ? a->finalLayout : 0;
8017 };
8018
8019 for (int i = 0, ie = colorRefs.size(); i != ie; ++i) {
8020 const uint32_t attIdx = colorRefs[i].attachment;
8021 *p++ = attIdx;
8022 serializeAttachmentData(attIdx);
8023 }
8024
8025 if (hasDepthStencil) {
8026 const uint32_t attIdx = dsRef.attachment;
8027 *p++ = attIdx;
8028 serializeAttachmentData(attIdx);
8029 }
8030
8031 for (int i = 0, ie = resolveRefs.size(); i != ie; ++i) {
8032 const uint32_t attIdx = resolveRefs[i].attachment;
8033 *p++ = attIdx;
8034 serializeAttachmentData(attIdx);
8035 }
8036
8038 const uint32_t attIdx = dsResolveRef.attachment;
8039 *p++ = attIdx;
8040 serializeAttachmentData(attIdx);
8041 }
8042
8043 if (hasShadingRateMap) {
8044 const uint32_t attIdx = shadingRateRef.attachment;
8045 *p++ = attIdx;
8046 serializeAttachmentData(attIdx);
8047 }
8048}
8049
8051{
8052 QVkRenderPassDescriptor *rpD = new QVkRenderPassDescriptor(m_rhi);
8053
8054 rpD->ownsRp = true;
8055 rpD->attDescs = attDescs;
8056 rpD->colorRefs = colorRefs;
8057 rpD->resolveRefs = resolveRefs;
8058 rpD->subpassDeps = subpassDeps;
8062 rpD->multiViewCount = multiViewCount;
8063 rpD->dsRef = dsRef;
8064 rpD->dsResolveRef = dsResolveRef;
8065 rpD->shadingRateRef = shadingRateRef;
8066
8067 VkRenderPassCreateInfo rpInfo;
8068 VkSubpassDescription subpassDesc;
8069 fillRenderPassCreateInfo(&rpInfo, &subpassDesc, rpD);
8070
8071 QRHI_RES_RHI(QRhiVulkan);
8072 MultiViewRenderPassSetupHelper multiViewHelper;
8073 if (!multiViewHelper.prepare(&rpInfo, multiViewCount, rhiD->caps.multiView)) {
8074 delete rpD;
8075 return nullptr;
8076 }
8077
8078#ifdef VK_KHR_create_renderpass2
8079 if (rhiD->caps.renderPass2KHR) {
8080 // Use the KHR extension, not the 1.2 core API, in order to support Vulkan 1.1.
8081 VkRenderPassCreateInfo2KHR rpInfo2;
8082 RenderPass2SetupHelper rp2Helper(rhiD);
8083 if (!rp2Helper.prepare(&rpInfo2, &rpInfo, rpD, multiViewCount)) {
8084 delete rpD;
8085 return nullptr;
8086 }
8087 VkResult err = rhiD->vkCreateRenderPass2KHR(rhiD->dev, &rpInfo2, nullptr, &rpD->rp);
8088 if (err != VK_SUCCESS) {
8089 qWarning("Failed to create renderpass (using VkRenderPassCreateInfo2KHR): %d", err);
8090 delete rpD;
8091 return nullptr;
8092 }
8093 } else
8094#endif
8095 {
8096 VkResult err = rhiD->df->vkCreateRenderPass(rhiD->dev, &rpInfo, nullptr, &rpD->rp);
8097 if (err != VK_SUCCESS) {
8098 qWarning("Failed to create renderpass: %d", err);
8099 delete rpD;
8100 return nullptr;
8101 }
8102 }
8103
8105 rhiD->registerResource(rpD);
8106 return rpD;
8107}
8108
8110{
8111 return serializedFormatData;
8112}
8113
8115{
8116 nativeHandlesStruct.renderPass = rp;
8117 return &nativeHandlesStruct;
8118}
8119
8120QVkShadingRateMap::QVkShadingRateMap(QRhiImplementation *rhi)
8122{
8123}
8124
8129
8131{
8132 if (!texture)
8133 return;
8134
8135 texture = nullptr;
8136}
8137
8138bool QVkShadingRateMap::createFrom(QRhiTexture *src)
8139{
8140 if (texture)
8141 destroy();
8142
8143 texture = QRHI_RES(QVkTexture, src);
8144
8145 return true;
8146}
8147
8148QVkSwapChainRenderTarget::QVkSwapChainRenderTarget(QRhiImplementation *rhi, QRhiSwapChain *swapchain)
8150{
8151}
8152
8157
8159{
8160 // nothing to do here
8161}
8162
8164{
8165 return d.pixelSize;
8166}
8167
8169{
8170 return d.dpr;
8171}
8172
8174{
8175 return d.sampleCount;
8176}
8177
8179 const QRhiTextureRenderTargetDescription &desc,
8180 Flags flags)
8182{
8183 for (int att = 0; att < QVkRenderTargetData::MAX_COLOR_ATTACHMENTS; ++att) {
8184 rtv[att] = VK_NULL_HANDLE;
8185 resrtv[att] = VK_NULL_HANDLE;
8186 }
8187}
8188
8193
8195{
8196 if (!d.fb)
8197 return;
8198
8202
8203 e.textureRenderTarget.fb = d.fb;
8204 d.fb = VK_NULL_HANDLE;
8205
8206 for (int att = 0; att < QVkRenderTargetData::MAX_COLOR_ATTACHMENTS; ++att) {
8207 e.textureRenderTarget.rtv[att] = rtv[att];
8208 e.textureRenderTarget.resrtv[att] = resrtv[att];
8209 rtv[att] = VK_NULL_HANDLE;
8210 resrtv[att] = VK_NULL_HANDLE;
8211 }
8212
8213 e.textureRenderTarget.dsv = dsv;
8214 dsv = VK_NULL_HANDLE;
8215 e.textureRenderTarget.resdsv = resdsv;
8216 resdsv = VK_NULL_HANDLE;
8217
8218 e.textureRenderTarget.shadingRateMapView = shadingRateMapView;
8219 shadingRateMapView = VK_NULL_HANDLE;
8220
8221 QRHI_RES_RHI(QRhiVulkan);
8222 if (rhiD) {
8223 rhiD->releaseQueue.append(e);
8224 rhiD->unregisterResource(this);
8225 }
8226}
8227
8229{
8230 // not yet built so cannot rely on data computed in create()
8231
8232 QRHI_RES_RHI(QRhiVulkan);
8233 QVkRenderPassDescriptor *rp = new QVkRenderPassDescriptor(m_rhi);
8234 if (!rhiD->createOffscreenRenderPass(rp,
8235 m_desc.cbeginColorAttachments(),
8236 m_desc.cendColorAttachments(),
8237 m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents),
8238 m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents),
8239 m_desc.depthTexture() && !m_flags.testFlag(DoNotStoreDepthStencilContents) && !m_desc.depthResolveTexture(),
8240 m_desc.depthStencilBuffer(),
8241 m_desc.depthTexture(),
8242 m_desc.depthResolveTexture(),
8243 m_desc.depthLayer(),
8244 m_desc.shadingRateMap()))
8245 {
8246 delete rp;
8247 return nullptr;
8248 }
8249
8250 rp->ownsRp = true;
8252 rhiD->registerResource(rp);
8253 return rp;
8254}
8255
8257{
8258 if (d.fb)
8259 destroy();
8260
8261 Q_ASSERT(m_desc.colorAttachmentCount() > 0 || m_desc.depthTexture());
8262 Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture());
8263 const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
8264
8265 QRHI_RES_RHI(QRhiVulkan);
8266 QVarLengthArray<VkImageView, 8> views;
8267 d.multiViewCount = 0;
8268
8269 d.colorAttCount = 0;
8270 int attIndex = 0;
8271 for (auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it, ++attIndex) {
8272 d.colorAttCount += 1;
8273 QVkTexture *texD = QRHI_RES(QVkTexture, it->texture());
8274 QVkRenderBuffer *rbD = QRHI_RES(QVkRenderBuffer, it->renderBuffer());
8275 Q_ASSERT(texD || rbD);
8276 if (texD) {
8277 Q_ASSERT(texD->flags().testFlag(QRhiTexture::RenderTarget));
8278 const bool is1D = texD->flags().testFlag(QRhiTexture::OneDimensional);
8279 const bool isMultiView = it->multiViewCount() >= 2;
8280 if (isMultiView && d.multiViewCount == 0)
8281 d.multiViewCount = it->multiViewCount();
8282 VkImageViewCreateInfo viewInfo = {};
8283 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
8284 viewInfo.image = texD->image;
8285 viewInfo.viewType = is1D ? VK_IMAGE_VIEW_TYPE_1D
8286 : (isMultiView ? VK_IMAGE_VIEW_TYPE_2D_ARRAY
8287 : VK_IMAGE_VIEW_TYPE_2D);
8288 viewInfo.format = texD->viewFormat;
8289 viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
8290 viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
8291 viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
8292 viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
8293 viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
8294 viewInfo.subresourceRange.baseMipLevel = uint32_t(it->level());
8295 viewInfo.subresourceRange.levelCount = 1;
8296 viewInfo.subresourceRange.baseArrayLayer = uint32_t(it->layer());
8297 viewInfo.subresourceRange.layerCount = uint32_t(isMultiView ? it->multiViewCount() : 1);
8298 VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &rtv[attIndex]);
8299 if (err != VK_SUCCESS) {
8300 qWarning("Failed to create render target image view: %d", err);
8301 return false;
8302 }
8303 views.append(rtv[attIndex]);
8304 if (attIndex == 0) {
8305 d.pixelSize = rhiD->q->sizeForMipLevel(it->level(), texD->pixelSize());
8306 d.sampleCount = texD->samples;
8307 }
8308 } else if (rbD) {
8309 Q_ASSERT(rbD->backingTexture);
8310 views.append(rbD->backingTexture->imageView);
8311 if (attIndex == 0) {
8312 d.pixelSize = rbD->pixelSize();
8313 d.sampleCount = rbD->samples;
8314 }
8315 }
8316 }
8317 d.dpr = 1;
8318
8319 if (hasDepthStencil) {
8320 if (m_desc.depthTexture()) {
8321 QVkTexture *depthTexD = QRHI_RES(QVkTexture, m_desc.depthTexture());
8322 // need a dedicated view just because viewFormat may differ from vkformat
8323 VkImageViewCreateInfo viewInfo = {};
8324 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
8325 viewInfo.image = depthTexD->image;
8326 viewInfo.viewType = d.multiViewCount > 1 ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D;
8327 viewInfo.format = depthTexD->viewFormat;
8328 viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
8329 viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
8330 viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
8331 viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
8332 viewInfo.subresourceRange.aspectMask = aspectMaskForTextureFormat(depthTexD->format());
8333 viewInfo.subresourceRange.levelCount = 1;
8334 if (m_desc.depthLayer() >= 0 && depthTexD->arraySize() >= 2) {
8335 viewInfo.subresourceRange.baseArrayLayer = uint32_t(m_desc.depthLayer());
8336 viewInfo.subresourceRange.layerCount = 1;
8337 }
8338 else
8339 viewInfo.subresourceRange.layerCount = qMax<uint32_t>(1, d.multiViewCount);
8340 VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &dsv);
8341 if (err != VK_SUCCESS) {
8342 qWarning("Failed to create depth-stencil image view for rt: %d", err);
8343 return false;
8344 }
8345 views.append(dsv);
8346 if (d.colorAttCount == 0) {
8347 d.pixelSize = depthTexD->pixelSize();
8348 d.sampleCount = depthTexD->samples;
8349 }
8350 } else {
8351 QVkRenderBuffer *depthRbD = QRHI_RES(QVkRenderBuffer, m_desc.depthStencilBuffer());
8352 views.append(depthRbD->imageView);
8353 if (d.colorAttCount == 0) {
8354 d.pixelSize = depthRbD->pixelSize();
8355 d.sampleCount = depthRbD->samples;
8356 }
8357 }
8358 d.dsAttCount = 1;
8359 } else {
8360 d.dsAttCount = 0;
8361 }
8362
8363 d.resolveAttCount = 0;
8364 attIndex = 0;
8365 Q_ASSERT(d.multiViewCount == 0 || d.multiViewCount >= 2);
8366 for (auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it, ++attIndex) {
8367 if (it->resolveTexture()) {
8368 QVkTexture *resTexD = QRHI_RES(QVkTexture, it->resolveTexture());
8369 Q_ASSERT(resTexD->flags().testFlag(QRhiTexture::RenderTarget));
8370 d.resolveAttCount += 1;
8371
8372 VkImageViewCreateInfo viewInfo = {};
8373 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
8374 viewInfo.image = resTexD->image;
8375 viewInfo.viewType = d.multiViewCount ? VK_IMAGE_VIEW_TYPE_2D_ARRAY
8376 : VK_IMAGE_VIEW_TYPE_2D;
8377 viewInfo.format = resTexD->viewFormat;
8378 viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
8379 viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
8380 viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
8381 viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
8382 viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
8383 viewInfo.subresourceRange.baseMipLevel = uint32_t(it->resolveLevel());
8384 viewInfo.subresourceRange.levelCount = 1;
8385 viewInfo.subresourceRange.baseArrayLayer = uint32_t(it->resolveLayer());
8386 viewInfo.subresourceRange.layerCount = qMax<uint32_t>(1, d.multiViewCount);
8387 VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &resrtv[attIndex]);
8388 if (err != VK_SUCCESS) {
8389 qWarning("Failed to create render target resolve image view: %d", err);
8390 return false;
8391 }
8392 views.append(resrtv[attIndex]);
8393 }
8394 }
8395
8396 if (m_desc.depthResolveTexture()) {
8397 QVkTexture *resTexD = QRHI_RES(QVkTexture, m_desc.depthResolveTexture());
8398 Q_ASSERT(resTexD->flags().testFlag(QRhiTexture::RenderTarget));
8399
8400 VkImageViewCreateInfo viewInfo = {};
8401 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
8402 viewInfo.image = resTexD->image;
8403 viewInfo.viewType = d.multiViewCount ? VK_IMAGE_VIEW_TYPE_2D_ARRAY
8404 : VK_IMAGE_VIEW_TYPE_2D;
8405 viewInfo.format = resTexD->viewFormat;
8406 viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
8407 viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
8408 viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
8409 viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
8410 viewInfo.subresourceRange.aspectMask = aspectMaskForTextureFormat(resTexD->format());
8411 viewInfo.subresourceRange.baseMipLevel = 0;
8412 viewInfo.subresourceRange.levelCount = 1;
8413 viewInfo.subresourceRange.baseArrayLayer = 0;
8414 viewInfo.subresourceRange.layerCount = qMax<uint32_t>(1, d.multiViewCount);
8415 VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &resdsv);
8416 if (err != VK_SUCCESS) {
8417 qWarning("Failed to create render target depth resolve image view: %d", err);
8418 return false;
8419 }
8420 views.append(resdsv);
8421 d.dsResolveAttCount = 1;
8422 } else {
8423 d.dsResolveAttCount = 0;
8424 }
8425
8426 if (m_desc.shadingRateMap() && rhiD->caps.renderPass2KHR && rhiD->caps.imageBasedShadingRate) {
8427 QVkTexture *texD = QRHI_RES(QVkShadingRateMap, m_desc.shadingRateMap())->texture;
8428 Q_ASSERT(texD->flags().testFlag(QRhiTexture::UsedAsShadingRateMap));
8429
8430 VkImageViewCreateInfo viewInfo = {};
8431 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
8432 viewInfo.image = texD->image;
8433 viewInfo.viewType = d.multiViewCount ? VK_IMAGE_VIEW_TYPE_2D_ARRAY
8434 : VK_IMAGE_VIEW_TYPE_2D;
8435 viewInfo.format = texD->viewFormat;
8436 viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
8437 viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
8438 viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
8439 viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
8440 viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
8441 viewInfo.subresourceRange.baseMipLevel = 0;
8442 viewInfo.subresourceRange.levelCount = 1;
8443 viewInfo.subresourceRange.baseArrayLayer = 0;
8444 viewInfo.subresourceRange.layerCount = qMax<uint32_t>(1, d.multiViewCount);
8445 VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &shadingRateMapView);
8446 if (err != VK_SUCCESS) {
8447 qWarning("Failed to create render target shading rate map view: %d", err);
8448 return false;
8449 }
8450 views.append(shadingRateMapView);
8451 d.shadingRateAttCount = 1;
8452 } else {
8453 d.shadingRateAttCount = 0;
8454 }
8455
8456 if (!m_renderPassDesc)
8457 qWarning("QVkTextureRenderTarget: No renderpass descriptor set. See newCompatibleRenderPassDescriptor() and setRenderPassDescriptor().");
8458
8459 d.rp = QRHI_RES(QVkRenderPassDescriptor, m_renderPassDesc);
8460 Q_ASSERT(d.rp && d.rp->rp);
8461
8462 VkFramebufferCreateInfo fbInfo = {};
8463 fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
8464 fbInfo.renderPass = d.rp->rp;
8465 fbInfo.attachmentCount = uint32_t(views.count());
8466 fbInfo.pAttachments = views.constData();
8467 fbInfo.width = uint32_t(d.pixelSize.width());
8468 fbInfo.height = uint32_t(d.pixelSize.height());
8469 fbInfo.layers = 1;
8470
8471 VkResult err = rhiD->df->vkCreateFramebuffer(rhiD->dev, &fbInfo, nullptr, &d.fb);
8472 if (err != VK_SUCCESS) {
8473 qWarning("Failed to create framebuffer: %d", err);
8474 return false;
8475 }
8476
8477 QRhiRenderTargetAttachmentTracker::updateResIdList<QVkTexture, QVkRenderBuffer>(m_desc, &d.currentResIdList);
8478
8480 rhiD->registerResource(this);
8481 return true;
8482}
8483
8485{
8486 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QVkTexture, QVkRenderBuffer>(m_desc, d.currentResIdList))
8487 const_cast<QVkTextureRenderTarget *>(this)->create();
8488
8489 return d.pixelSize;
8490}
8491
8493{
8494 return d.dpr;
8495}
8496
8498{
8499 return d.sampleCount;
8500}
8501
8506
8511
8513{
8514 if (!layout)
8515 return;
8516
8517 sortedBindings.clear();
8518 sortedDynamicOffsetBindingNumbers.clear();
8519
8523
8524 e.shaderResourceBindings.poolIndex = poolIndex;
8525 e.shaderResourceBindings.layout = layout;
8526
8527 poolIndex = -1;
8528 layout = VK_NULL_HANDLE;
8529 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
8530 descSets[i] = VK_NULL_HANDLE;
8531
8532 QRHI_RES_RHI(QRhiVulkan);
8533 if (rhiD) {
8534 rhiD->releaseQueue.append(e);
8535 rhiD->unregisterResource(this);
8536 }
8537}
8538
8540{
8541 if (layout)
8542 destroy();
8543
8544 QRHI_RES_RHI(QRhiVulkan);
8545 if (!rhiD->sanityCheckShaderResourceBindings(this))
8546 return false;
8547
8548 rhiD->updateLayoutDesc(this);
8549
8550 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
8551 descSets[i] = VK_NULL_HANDLE;
8552
8553 sortedBindings.clear();
8554 std::copy(m_bindings.cbegin(), m_bindings.cend(), std::back_inserter(sortedBindings));
8555 std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan);
8556
8557 sortedDynamicOffsetBindingNumbers.clear();
8558 for (const QRhiShaderResourceBinding &binding : std::as_const(sortedBindings)) {
8559 const QRhiShaderResourceBinding::Data *b = QRhiImplementation::shaderResourceBindingData(binding);
8560 if (b->type == QRhiShaderResourceBinding::UniformBuffer && b->u.ubuf.buf) {
8561 if (b->u.ubuf.hasDynamicOffset)
8562 sortedDynamicOffsetBindingNumbers.append(b->binding);
8563 }
8564 }
8565
8566 QVarLengthArray<VkDescriptorSetLayoutBinding, BINDING_PREALLOC> vkbindings;
8567 vkbindings.reserve(sortedBindings.size());
8568 for (const QRhiShaderResourceBinding &binding : std::as_const(sortedBindings)) {
8569 const QRhiShaderResourceBinding::Data *b = QRhiImplementation::shaderResourceBindingData(binding);
8570 VkDescriptorSetLayoutBinding &vkbinding = vkbindings.emplace_back();
8571 vkbinding.binding = uint32_t(b->binding);
8572 vkbinding.descriptorType = toVkDescriptorType(b);
8573 if (b->type == QRhiShaderResourceBinding::SampledTexture || b->type == QRhiShaderResourceBinding::Texture)
8574 vkbinding.descriptorCount = b->u.stex.count;
8575 else
8576 vkbinding.descriptorCount = 1;
8577 vkbinding.stageFlags = toVkShaderStageFlags(b->stage);
8578 }
8579
8580 VkDescriptorSetLayoutCreateInfo layoutInfo = {};
8581 layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
8582 layoutInfo.bindingCount = uint32_t(vkbindings.size());
8583 layoutInfo.pBindings = vkbindings.constData();
8584
8585 VkResult err = rhiD->df->vkCreateDescriptorSetLayout(rhiD->dev, &layoutInfo, nullptr, &layout);
8586 if (err != VK_SUCCESS) {
8587 qWarning("Failed to create descriptor set layout: %d", err);
8588 return false;
8589 }
8590 rhiD->setObjectName(uint64_t(layout), VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT, m_objectName);
8591
8592 VkDescriptorSetAllocateInfo allocInfo = {};
8593 allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
8594 allocInfo.descriptorSetCount = QVK_FRAMES_IN_FLIGHT;
8595 VkDescriptorSetLayout layouts[QVK_FRAMES_IN_FLIGHT];
8596 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
8597 layouts[i] = layout;
8598 allocInfo.pSetLayouts = layouts;
8599 if (!rhiD->allocateDescriptorSet(&allocInfo, descSets, &poolIndex))
8600 return false;
8601
8602 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
8603 boundResourceData[i].resize(sortedBindings.size());
8604 for (BoundResourceData &bd : boundResourceData[i])
8605 memset(&bd, 0, sizeof(BoundResourceData));
8606 }
8607
8608 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
8609 rhiD->setObjectName(uint64_t(descSets[i]), VK_OBJECT_TYPE_DESCRIPTOR_SET, m_objectName, i);
8610
8612 generation += 1;
8613 rhiD->registerResource(this);
8614 return true;
8615}
8616
8618{
8619 sortedBindings.clear();
8620 std::copy(m_bindings.cbegin(), m_bindings.cend(), std::back_inserter(sortedBindings));
8621 if (!flags.testFlag(BindingsAreSorted))
8622 std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan);
8623
8624 sortedDynamicOffsetBindingNumbers.clear();
8625 for (const QRhiShaderResourceBinding &binding : std::as_const(sortedBindings)) {
8626 const QRhiShaderResourceBinding::Data *b = QRhiImplementation::shaderResourceBindingData(binding);
8627 if (b->type == QRhiShaderResourceBinding::UniformBuffer && b->u.ubuf.buf) {
8628 if (b->u.ubuf.hasDynamicOffset)
8629 sortedDynamicOffsetBindingNumbers.append(b->binding);
8630 }
8631 }
8632
8633 // Reset the state tracking table too - it can deal with assigning a
8634 // different QRhiBuffer/Texture/Sampler for a binding point, but it cannot
8635 // detect changes in the associated data, such as the buffer offset. And
8636 // just like after a create(), a call to updateResources() may lead to now
8637 // specifying a different offset for the same QRhiBuffer for a given binding
8638 // point. The same applies to other type of associated data that is not part
8639 // of the layout, such as the mip level for a StorageImage. Instead of
8640 // complicating the checks in setShaderResources(), reset the table here
8641 // just like we do in create().
8642 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
8643 Q_ASSERT(boundResourceData[i].size() == sortedBindings.size());
8644 for (BoundResourceData &bd : boundResourceData[i])
8645 memset(&bd, 0, sizeof(BoundResourceData));
8646 }
8647
8648 generation += 1;
8649}
8650
8653{
8654}
8655
8660
8662{
8663 if (!pipeline && !layout)
8664 return;
8665
8669
8670 e.pipelineState.pipeline = pipeline;
8671 e.pipelineState.layout = layout;
8672
8673 pipeline = VK_NULL_HANDLE;
8674 layout = VK_NULL_HANDLE;
8675
8676 QRHI_RES_RHI(QRhiVulkan);
8677 if (rhiD) {
8678 rhiD->releaseQueue.append(e);
8679 rhiD->unregisterResource(this);
8680 }
8681}
8682
8684{
8685 if (pipeline || layout)
8686 destroy();
8687
8688 QRHI_RES_RHI(QRhiVulkan);
8689 rhiD->pipelineCreationStart();
8690 if (!rhiD->sanityCheckGraphicsPipeline(this))
8691 return false;
8692
8693 if (!rhiD->ensurePipelineCache())
8694 return false;
8695
8696 VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
8697 pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
8698 pipelineLayoutInfo.setLayoutCount = 1;
8699 QVkShaderResourceBindings *srbD = QRHI_RES(QVkShaderResourceBindings, m_shaderResourceBindings);
8700 Q_ASSERT(m_shaderResourceBindings && srbD->layout);
8701 pipelineLayoutInfo.pSetLayouts = &srbD->layout;
8702 VkResult err = rhiD->df->vkCreatePipelineLayout(rhiD->dev, &pipelineLayoutInfo, nullptr, &layout);
8703 if (err != VK_SUCCESS) {
8704 qWarning("Failed to create pipeline layout: %d", err);
8705 return false;
8706 }
8707 auto pipelineLayoutCleanup = qScopeGuard([this, rhiD] {
8708 rhiD->df->vkDestroyPipelineLayout(rhiD->dev, layout, nullptr);
8709 layout = VK_NULL_HANDLE;
8710 });
8711
8712 VkGraphicsPipelineCreateInfo pipelineInfo = {};
8713 pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
8714
8715 QVarLengthArray<VkShaderModule, 4> shaders;
8716 auto shaderModuleCleanup = qScopeGuard([rhiD, &shaders] {
8717 for (VkShaderModule shader : shaders)
8718 rhiD->df->vkDestroyShaderModule(rhiD->dev, shader, nullptr);
8719 });
8720 QVarLengthArray<VkPipelineShaderStageCreateInfo, 4> shaderStageCreateInfos;
8721 QVarLengthArray<QByteArray, 4> entryPointNames;
8722 for (const QRhiShaderStage &shaderStage : m_shaderStages) {
8723 const QShader bakedShader = shaderStage.shader();
8724 const QShaderCode spirv = bakedShader.shader({ QShader::SpirvShader, 100, shaderStage.shaderVariant() });
8725 if (spirv.shader().isEmpty()) {
8726 qWarning() << "No SPIR-V 1.0 shader code found in baked shader" << bakedShader;
8727 return false;
8728 }
8729 VkShaderModule shader = rhiD->createShader(spirv.shader());
8730 if (shader) {
8731 shaders.append(shader);
8732 VkPipelineShaderStageCreateInfo shaderInfo = {};
8733 shaderInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
8734 shaderInfo.stage = toVkShaderStage(shaderStage.type());
8735 shaderInfo.module = shader;
8736 entryPointNames.append(spirv.entryPoint());
8737 shaderInfo.pName = nullptr;
8738 shaderStageCreateInfos.append(shaderInfo);
8739 }
8740 else
8741 return false;
8742 }
8743 for (qsizetype i = 0, ie = shaders.count(); i != ie; ++i)
8744 shaderStageCreateInfos[i].pName = entryPointNames[i].constData();
8745
8746 pipelineInfo.stageCount = uint32_t(shaderStageCreateInfos.size());
8747 pipelineInfo.pStages = shaderStageCreateInfos.constData();
8748
8749 QVarLengthArray<VkVertexInputBindingDescription, 4> vertexBindings;
8750#ifdef VK_EXT_vertex_attribute_divisor
8751 QVarLengthArray<VkVertexInputBindingDivisorDescriptionEXT> nonOneStepRates;
8752#endif
8753 int bindingIndex = 0;
8754 for (auto it = m_vertexInputLayout.cbeginBindings(), itEnd = m_vertexInputLayout.cendBindings();
8755 it != itEnd; ++it, ++bindingIndex)
8756 {
8757 VkVertexInputBindingDescription bindingInfo = {
8758 uint32_t(bindingIndex),
8759 it->stride(),
8760 it->classification() == QRhiVertexInputBinding::PerVertex
8761 ? VK_VERTEX_INPUT_RATE_VERTEX : VK_VERTEX_INPUT_RATE_INSTANCE
8762 };
8763 if (it->classification() == QRhiVertexInputBinding::PerInstance && it->instanceStepRate() != 1) {
8764#ifdef VK_EXT_vertex_attribute_divisor
8765 if (rhiD->caps.vertexAttribDivisor) {
8766 nonOneStepRates.append({ uint32_t(bindingIndex), it->instanceStepRate() });
8767 } else
8768#endif
8769 {
8770 qWarning("QRhiVulkan: Instance step rates other than 1 not supported without "
8771 "VK_EXT_vertex_attribute_divisor on the device and "
8772 "VK_KHR_get_physical_device_properties2 on the instance");
8773 }
8774 }
8775 vertexBindings.append(bindingInfo);
8776 }
8777 QVarLengthArray<VkVertexInputAttributeDescription, 4> vertexAttributes;
8778 for (auto it = m_vertexInputLayout.cbeginAttributes(), itEnd = m_vertexInputLayout.cendAttributes();
8779 it != itEnd; ++it)
8780 {
8781 VkVertexInputAttributeDescription attributeInfo = {
8782 uint32_t(it->location()),
8783 uint32_t(it->binding()),
8784 toVkAttributeFormat(it->format()),
8785 it->offset()
8786 };
8787 vertexAttributes.append(attributeInfo);
8788 }
8789 VkPipelineVertexInputStateCreateInfo vertexInputInfo = {};
8790 vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
8791 vertexInputInfo.vertexBindingDescriptionCount = uint32_t(vertexBindings.size());
8792 vertexInputInfo.pVertexBindingDescriptions = vertexBindings.constData();
8793 vertexInputInfo.vertexAttributeDescriptionCount = uint32_t(vertexAttributes.size());
8794 vertexInputInfo.pVertexAttributeDescriptions = vertexAttributes.constData();
8795#ifdef VK_EXT_vertex_attribute_divisor
8796 VkPipelineVertexInputDivisorStateCreateInfoEXT divisorInfo = {};
8797 if (!nonOneStepRates.isEmpty()) {
8798 divisorInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT;
8799 divisorInfo.vertexBindingDivisorCount = uint32_t(nonOneStepRates.size());
8800 divisorInfo.pVertexBindingDivisors = nonOneStepRates.constData();
8801 vertexInputInfo.pNext = &divisorInfo;
8802 }
8803#endif
8804 pipelineInfo.pVertexInputState = &vertexInputInfo;
8805
8806 QVarLengthArray<VkDynamicState, 8> dynEnable;
8807 dynEnable << VK_DYNAMIC_STATE_VIEWPORT;
8808 dynEnable << VK_DYNAMIC_STATE_SCISSOR; // ignore UsesScissor - Vulkan requires a scissor for the viewport always
8809 if (m_flags.testFlag(QRhiGraphicsPipeline::UsesBlendConstants))
8810 dynEnable << VK_DYNAMIC_STATE_BLEND_CONSTANTS;
8811 if (m_flags.testFlag(QRhiGraphicsPipeline::UsesStencilRef))
8812 dynEnable << VK_DYNAMIC_STATE_STENCIL_REFERENCE;
8813#ifdef VK_KHR_fragment_shading_rate
8814 if (m_flags.testFlag(QRhiGraphicsPipeline::UsesShadingRate) && rhiD->caps.perDrawShadingRate)
8815 dynEnable << VK_DYNAMIC_STATE_FRAGMENT_SHADING_RATE_KHR;
8816#endif
8817
8818 VkPipelineDynamicStateCreateInfo dynamicInfo = {};
8819 dynamicInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
8820 dynamicInfo.dynamicStateCount = uint32_t(dynEnable.size());
8821 dynamicInfo.pDynamicStates = dynEnable.constData();
8822 pipelineInfo.pDynamicState = &dynamicInfo;
8823
8824 VkPipelineViewportStateCreateInfo viewportInfo = {};
8825 viewportInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
8826 viewportInfo.viewportCount = viewportInfo.scissorCount = 1;
8827 pipelineInfo.pViewportState = &viewportInfo;
8828
8829 VkPipelineInputAssemblyStateCreateInfo inputAsmInfo = {};
8830 inputAsmInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
8831 inputAsmInfo.topology = toVkTopology(m_topology);
8832 inputAsmInfo.primitiveRestartEnable = (m_topology == TriangleStrip || m_topology == LineStrip);
8833 pipelineInfo.pInputAssemblyState = &inputAsmInfo;
8834
8835 VkPipelineTessellationStateCreateInfo tessInfo = {};
8836#ifdef VK_VERSION_1_1
8837 VkPipelineTessellationDomainOriginStateCreateInfo originInfo = {};
8838#endif
8839 if (m_topology == Patches) {
8840 tessInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO;
8841 tessInfo.patchControlPoints = uint32_t(qMax(1, m_patchControlPointCount));
8842
8843 // To be able to use the same tess.evaluation shader with both OpenGL
8844 // and Vulkan, flip the tessellation domain origin to be lower left.
8845 // This allows declaring the winding order in the shader to be CCW and
8846 // still have it working with both APIs. This requires Vulkan 1.1 (or
8847 // VK_KHR_maintenance2 but don't bother with that).
8848#ifdef VK_VERSION_1_1
8849 if (rhiD->caps.apiVersion >= QVersionNumber(1, 1)) {
8850 originInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO;
8851 originInfo.domainOrigin = VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT;
8852 tessInfo.pNext = &originInfo;
8853 } else {
8854 qWarning("Proper tessellation support requires Vulkan 1.1 or newer, leaving domain origin unset");
8855 }
8856#else
8857 qWarning("QRhi was built without Vulkan 1.1 headers, this is not sufficient for proper tessellation support");
8858#endif
8859
8860 pipelineInfo.pTessellationState = &tessInfo;
8861 }
8862
8863 VkPipelineRasterizationStateCreateInfo rastInfo = {};
8864 rastInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
8865 if (m_depthClamp && rhiD->caps.depthClamp)
8866 rastInfo.depthClampEnable = m_depthClamp;
8867 rastInfo.cullMode = toVkCullMode(m_cullMode);
8868 rastInfo.frontFace = toVkFrontFace(m_frontFace);
8869 if (m_depthBias != 0 || !qFuzzyIsNull(m_slopeScaledDepthBias)) {
8870 rastInfo.depthBiasEnable = true;
8871 rastInfo.depthBiasConstantFactor = float(m_depthBias);
8872 rastInfo.depthBiasSlopeFactor = m_slopeScaledDepthBias;
8873 }
8874 rastInfo.lineWidth = rhiD->caps.wideLines ? m_lineWidth : 1.0f;
8875 rastInfo.polygonMode = toVkPolygonMode(m_polygonMode);
8876 pipelineInfo.pRasterizationState = &rastInfo;
8877
8878 VkPipelineMultisampleStateCreateInfo msInfo = {};
8879 msInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
8880 msInfo.rasterizationSamples = rhiD->effectiveSampleCountBits(m_sampleCount);
8881 pipelineInfo.pMultisampleState = &msInfo;
8882
8883 VkPipelineDepthStencilStateCreateInfo dsInfo = {};
8884 dsInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
8885 dsInfo.depthTestEnable = m_depthTest;
8886 dsInfo.depthWriteEnable = m_depthWrite;
8887 dsInfo.depthCompareOp = toVkCompareOp(m_depthOp);
8888 dsInfo.stencilTestEnable = m_stencilTest;
8889 if (m_stencilTest) {
8890 fillVkStencilOpState(&dsInfo.front, m_stencilFront);
8891 dsInfo.front.compareMask = m_stencilReadMask;
8892 dsInfo.front.writeMask = m_stencilWriteMask;
8893 fillVkStencilOpState(&dsInfo.back, m_stencilBack);
8894 dsInfo.back.compareMask = m_stencilReadMask;
8895 dsInfo.back.writeMask = m_stencilWriteMask;
8896 }
8897 pipelineInfo.pDepthStencilState = &dsInfo;
8898
8899 VkPipelineColorBlendStateCreateInfo blendInfo = {};
8900 blendInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
8901 QVarLengthArray<VkPipelineColorBlendAttachmentState, 4> vktargetBlends;
8902 for (const QRhiGraphicsPipeline::TargetBlend &b : std::as_const(m_targetBlends)) {
8903 VkPipelineColorBlendAttachmentState blend = {};
8904 blend.blendEnable = b.enable;
8905 blend.srcColorBlendFactor = toVkBlendFactor(b.srcColor);
8906 blend.dstColorBlendFactor = toVkBlendFactor(b.dstColor);
8907 blend.colorBlendOp = toVkBlendOp(b.opColor);
8908 blend.srcAlphaBlendFactor = toVkBlendFactor(b.srcAlpha);
8909 blend.dstAlphaBlendFactor = toVkBlendFactor(b.dstAlpha);
8910 blend.alphaBlendOp = toVkBlendOp(b.opAlpha);
8911 blend.colorWriteMask = toVkColorComponents(b.colorWrite);
8912 vktargetBlends.append(blend);
8913 }
8914 if (vktargetBlends.isEmpty()) {
8915 VkPipelineColorBlendAttachmentState blend = {};
8916 blend.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT
8917 | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
8918 vktargetBlends.append(blend);
8919 }
8920 blendInfo.attachmentCount = uint32_t(vktargetBlends.size());
8921 blendInfo.pAttachments = vktargetBlends.constData();
8922 pipelineInfo.pColorBlendState = &blendInfo;
8923
8924 pipelineInfo.layout = layout;
8925
8926 Q_ASSERT(m_renderPassDesc && QRHI_RES(const QVkRenderPassDescriptor, m_renderPassDesc)->rp);
8927 pipelineInfo.renderPass = QRHI_RES(const QVkRenderPassDescriptor, m_renderPassDesc)->rp;
8928
8929 err = rhiD->df->vkCreateGraphicsPipelines(rhiD->dev, rhiD->pipelineCache, 1, &pipelineInfo, nullptr, &pipeline);
8930
8931 if (err != VK_SUCCESS) {
8932 qWarning("Failed to create graphics pipeline: %d", err);
8933 return false;
8934 }
8935 pipelineLayoutCleanup.dismiss();
8936
8937 rhiD->setObjectName(uint64_t(pipeline), VK_OBJECT_TYPE_PIPELINE, m_objectName);
8938
8939 rhiD->pipelineCreationEnd();
8941 generation += 1;
8942 rhiD->registerResource(this);
8943 return true;
8944}
8945
8948{
8949}
8950
8955
8957{
8958 if (!pipeline && !layout)
8959 return;
8960
8964
8965 e.pipelineState.pipeline = pipeline;
8966 e.pipelineState.layout = layout;
8967
8968 pipeline = VK_NULL_HANDLE;
8969 layout = VK_NULL_HANDLE;
8970
8971 QRHI_RES_RHI(QRhiVulkan);
8972 if (rhiD) {
8973 rhiD->releaseQueue.append(e);
8974 rhiD->unregisterResource(this);
8975 }
8976}
8977
8979{
8980 if (pipeline || layout)
8981 destroy();
8982
8983 QRHI_RES_RHI(QRhiVulkan);
8984 rhiD->pipelineCreationStart();
8985 if (!rhiD->ensurePipelineCache())
8986 return false;
8987
8988 VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
8989 pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
8990 pipelineLayoutInfo.setLayoutCount = 1;
8991 QVkShaderResourceBindings *srbD = QRHI_RES(QVkShaderResourceBindings, m_shaderResourceBindings);
8992 Q_ASSERT(m_shaderResourceBindings && srbD->layout);
8993 pipelineLayoutInfo.pSetLayouts = &srbD->layout;
8994 VkResult err = rhiD->df->vkCreatePipelineLayout(rhiD->dev, &pipelineLayoutInfo, nullptr, &layout);
8995 if (err != VK_SUCCESS) {
8996 qWarning("Failed to create pipeline layout: %d", err);
8997 return false;
8998 }
8999 auto pipelineLayoutCleanup = qScopeGuard([this, rhiD] {
9000 rhiD->df->vkDestroyPipelineLayout(rhiD->dev, layout, nullptr);
9001 layout = VK_NULL_HANDLE;
9002 });
9003
9004 VkComputePipelineCreateInfo pipelineInfo = {};
9005 pipelineInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
9006 pipelineInfo.layout = layout;
9007
9008 if (m_shaderStage.type() != QRhiShaderStage::Compute) {
9009 qWarning("Compute pipeline requires a compute shader stage");
9010 return false;
9011 }
9012 const QShader bakedShader = m_shaderStage.shader();
9013 const QShaderCode spirv = bakedShader.shader({ QShader::SpirvShader, 100, m_shaderStage.shaderVariant() });
9014 if (spirv.shader().isEmpty()) {
9015 qWarning() << "No SPIR-V 1.0 shader code found in baked shader" << bakedShader;
9016 return false;
9017 }
9018 if (bakedShader.stage() != QShader::ComputeStage) {
9019 qWarning() << bakedShader << "is not a compute shader";
9020 return false;
9021 }
9022 VkShaderModule shader = rhiD->createShader(spirv.shader());
9023 if (!shader)
9024 return false;
9025 auto shaderModuleCleanup = qScopeGuard([rhiD, shader] {
9026 rhiD->df->vkDestroyShaderModule(rhiD->dev, shader, nullptr);
9027 });
9028 VkPipelineShaderStageCreateInfo shaderInfo = {};
9029 shaderInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
9030 shaderInfo.stage = VK_SHADER_STAGE_COMPUTE_BIT;
9031 shaderInfo.module = shader;
9032 const QByteArray entryPointName = spirv.entryPoint();
9033 shaderInfo.pName = entryPointName.constData();
9034 pipelineInfo.stage = shaderInfo;
9035
9036 err = rhiD->df->vkCreateComputePipelines(rhiD->dev, rhiD->pipelineCache, 1, &pipelineInfo, nullptr, &pipeline);
9037 if (err != VK_SUCCESS) {
9038 qWarning("Failed to create graphics pipeline: %d", err);
9039 return false;
9040 }
9041 pipelineLayoutCleanup.dismiss();
9042
9043 rhiD->setObjectName(uint64_t(pipeline), VK_OBJECT_TYPE_PIPELINE, m_objectName);
9044
9045 rhiD->pipelineCreationEnd();
9047 generation += 1;
9048 rhiD->registerResource(this);
9049 return true;
9050}
9051
9052QVkCommandBuffer::QVkCommandBuffer(QRhiImplementation *rhi)
9054{
9056}
9057
9062
9064{
9065 // nothing to do here, cb is not owned by us
9066}
9067
9069{
9070 // Ok this is messy but no other way has been devised yet. Outside
9071 // begin(Compute)Pass - end(Compute)Pass it is simple - just return the
9072 // primary VkCommandBuffer. Inside, however, we need to provide the current
9073 // secondary command buffer (typically the one started by beginExternal(),
9074 // in case we are between beginExternal - endExternal inside a pass).
9075
9077 nativeHandlesStruct.commandBuffer = cb;
9078 } else {
9079 if (passUsesSecondaryCb && !activeSecondaryCbStack.isEmpty())
9080 nativeHandlesStruct.commandBuffer = activeSecondaryCbStack.last();
9081 else
9082 nativeHandlesStruct.commandBuffer = cb;
9083 }
9084
9085 return &nativeHandlesStruct;
9086}
9087
9088QVkSwapChain::QVkSwapChain(QRhiImplementation *rhi)
9089 : QRhiSwapChain(rhi),
9090 rtWrapper(rhi, this),
9091 rtWrapperRight(rhi, this),
9092 cbWrapper(rhi)
9093{
9094}
9095
9100
9102{
9103 if (sc == VK_NULL_HANDLE)
9104 return;
9105
9106 QRHI_RES_RHI(QRhiVulkan);
9107 if (rhiD) {
9108 rhiD->swapchains.remove(this);
9110 }
9111
9112 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
9113 QVkSwapChain::FrameResources &frame(frameRes[i]);
9114 frame.cmdBuf = VK_NULL_HANDLE;
9115 frame.timestampQueryIndex = -1;
9116 }
9117
9118 surface = lastConnectedSurface = VK_NULL_HANDLE;
9119
9120 if (rhiD)
9121 rhiD->unregisterResource(this);
9122}
9123
9125{
9126 return &cbWrapper;
9127}
9128
9130{
9131 return &rtWrapper;
9132}
9133
9135{
9136 return !stereo || targetBuffer == StereoTargetBuffer::LeftBuffer ? &rtWrapper : &rtWrapperRight;
9137}
9138
9140{
9141 if (!ensureSurface())
9142 return QSize();
9143
9144 // The size from the QWindow may not exactly match the surface... so if a
9145 // size is reported from the surface, use that.
9146 VkSurfaceCapabilitiesKHR surfaceCaps = {};
9147 QRHI_RES_RHI(QRhiVulkan);
9148 rhiD->vkGetPhysicalDeviceSurfaceCapabilitiesKHR(rhiD->physDev, surface, &surfaceCaps);
9149 VkExtent2D bufferSize = surfaceCaps.currentExtent;
9150 if (bufferSize.width == uint32_t(-1)) {
9151 Q_ASSERT(bufferSize.height == uint32_t(-1));
9152 return m_window->size() * m_window->devicePixelRatio();
9153 }
9154 return QSize(int(bufferSize.width), int(bufferSize.height));
9155}
9156
9157static inline bool hdrFormatMatchesVkSurfaceFormat(QRhiSwapChain::Format f, const VkSurfaceFormatKHR &s)
9158{
9159 switch (f) {
9160 case QRhiSwapChain::HDRExtendedSrgbLinear:
9161 return s.format == VK_FORMAT_R16G16B16A16_SFLOAT
9162 && s.colorSpace == VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT;
9163 case QRhiSwapChain::HDR10:
9164 return s.format == VK_FORMAT_A2B10G10R10_UNORM_PACK32
9165 && s.colorSpace == VK_COLOR_SPACE_HDR10_ST2084_EXT;
9166 case QRhiSwapChain::HDRExtendedDisplayP3Linear:
9167 return s.format == VK_FORMAT_R16G16B16A16_SFLOAT
9168 && s.colorSpace == VK_COLOR_SPACE_DISPLAY_P3_LINEAR_EXT;
9169 default:
9170 break;
9171 }
9172 return false;
9173}
9174
9176{
9177 if (f == SDR)
9178 return true;
9179
9180 if (!m_window) {
9181 qWarning("Attempted to call isFormatSupported() without a window set");
9182 return false;
9183 }
9184
9185 // we may be called before create so query the surface
9186 VkSurfaceKHR surf = QVulkanInstance::surfaceForWindow(m_window);
9187
9188 QRHI_RES_RHI(QRhiVulkan);
9189 uint32_t formatCount = 0;
9190 rhiD->vkGetPhysicalDeviceSurfaceFormatsKHR(rhiD->physDev, surf, &formatCount, nullptr);
9191 QVarLengthArray<VkSurfaceFormatKHR, 8> formats(formatCount);
9192 if (formatCount) {
9193 rhiD->vkGetPhysicalDeviceSurfaceFormatsKHR(rhiD->physDev, surf, &formatCount, formats.data());
9194 for (uint32_t i = 0; i < formatCount; ++i) {
9195 if (hdrFormatMatchesVkSurfaceFormat(f, formats[i]))
9196 return true;
9197 }
9198 }
9199
9200 return false;
9201}
9202
9204{
9205 QRhiSwapChainHdrInfo info = QRhiSwapChain::hdrInfo();
9206#ifdef Q_OS_WIN
9207 QRHI_RES_RHI(QRhiVulkan);
9208 // Must use m_window, not window, given this may be called before createOrResize().
9209 if (m_window && rhiD->adapterLuidValid)
9210 info = rhiD->dxgiHdrInfo->queryHdrInfo(m_window);
9211#endif
9212 return info;
9213}
9214
9216{
9217 // not yet built so cannot rely on data computed in createOrResize()
9218
9219 if (!ensureSurface()) // make sure sampleCount and colorFormat reflect what was requested
9220 return nullptr;
9221
9222 QRHI_RES_RHI(QRhiVulkan);
9223 QVkRenderPassDescriptor *rp = new QVkRenderPassDescriptor(m_rhi);
9224 if (!rhiD->createDefaultRenderPass(rp,
9225 m_depthStencil != nullptr,
9226 samples,
9227 colorFormat,
9228 m_shadingRateMap))
9229 {
9230 delete rp;
9231 return nullptr;
9232 }
9233
9234 rp->ownsRp = true;
9236 rhiD->registerResource(rp);
9237 return rp;
9238}
9239
9240static inline bool isSrgbFormat(VkFormat format)
9241{
9242 switch (format) {
9243 case VK_FORMAT_R8_SRGB:
9244 case VK_FORMAT_R8G8_SRGB:
9245 case VK_FORMAT_R8G8B8_SRGB:
9246 case VK_FORMAT_B8G8R8_SRGB:
9247 case VK_FORMAT_R8G8B8A8_SRGB:
9248 case VK_FORMAT_B8G8R8A8_SRGB:
9249 case VK_FORMAT_A8B8G8R8_SRGB_PACK32:
9250 return true;
9251 default:
9252 return false;
9253 }
9254}
9255
9257{
9258 // Do nothing when already done, however window may change so check the
9259 // surface is still the same. Some of the queries below are very expensive
9260 // with some implementations so it is important to do the rest only once
9261 // per surface.
9262
9263 Q_ASSERT(m_window);
9264 VkSurfaceKHR surf = QVulkanInstance::surfaceForWindow(m_window);
9265 if (!surf) {
9266 qWarning("Failed to get surface for window");
9267 return false;
9268 }
9269 if (surface == surf)
9270 return true;
9271
9272 surface = surf;
9273
9274 QRHI_RES_RHI(QRhiVulkan);
9275 if (!rhiD->inst->supportsPresent(rhiD->physDev, rhiD->gfxQueueFamilyIdx, m_window)) {
9276 qWarning("Presenting not supported on this window");
9277 return false;
9278 }
9279
9280 quint32 formatCount = 0;
9281 rhiD->vkGetPhysicalDeviceSurfaceFormatsKHR(rhiD->physDev, surface, &formatCount, nullptr);
9282 QList<VkSurfaceFormatKHR> formats(formatCount);
9283 rhiD->vkGetPhysicalDeviceSurfaceFormatsKHR(rhiD->physDev, surface, &formatCount,
9284 formats.data());
9285
9286 // Initially select the first available format, will only be used as a worst-case fallback
9287 colorFormat = formats.constFirst().format;
9288 colorSpace = formats.constFirst().colorSpace;
9289
9290 // See if we can find the preferred SDR format
9291 const bool srgbRequested = m_flags.testFlag(sRGB);
9292 bool foundBestFormat = false;
9293 if (m_format == SDR) {
9294 for (int i = 0; i < int(formatCount); ++i) {
9295 bool ok;
9296 if (srgbRequested) {
9297 ok = defaultSrgbColorFormat == formats[i].format;
9298 } else {
9299 ok = defaultColorFormat == formats[i].format;
9300 }
9301 if (ok) {
9302 foundBestFormat = true;
9303 colorFormat = formats[i].format;
9304 colorSpace = formats[i].colorSpace;
9305 break;
9306 }
9307 }
9308 }
9309
9310 // Otherwise pick one that fits our requirements:
9311 if (!foundBestFormat && (m_format != SDR || srgbRequested)) {
9312 for (int i = 0; i < int(formatCount); ++i) {
9313 bool ok = false;
9314 if (m_format != SDR) {
9315 ok = hdrFormatMatchesVkSurfaceFormat(m_format, formats[i]);
9316 } else if (srgbRequested) {
9317 ok = isSrgbFormat(formats[i].format);
9318 }
9319 if (ok) {
9320 colorFormat = formats[i].format;
9321 colorSpace = formats[i].colorSpace;
9322 break;
9323 }
9324 }
9325 }
9326
9327 // Although this should be rare, warn when we fail to select a suitable format
9328 if (m_format != SDR) {
9329 VkSurfaceFormatKHR format{};
9330 format.format = colorFormat;
9331 format.colorSpace = colorSpace;
9332 if (!hdrFormatMatchesVkSurfaceFormat(m_format, format)) {
9333 qWarning("Failed to select a suitable VkFormat for HDR, using format %d as fallback",
9334 colorFormat);
9335 }
9336 } else {
9337 if (srgbRequested && !isSrgbFormat(colorFormat)) {
9338 qWarning("Failed to select a suitable VkFormat for sRGB, using format %d as fallback",
9339 colorFormat);
9340 }
9341 }
9342
9343#if QT_CONFIG(wayland)
9344 // On Wayland, only one color management surface can be created at a time without
9345 // triggering a protocol error, and we create one ourselves in some situations.
9346 // To avoid this problem, use VK_COLOR_SPACE_PASS_THROUGH_EXT when supported,
9347 // so that the driver doesn't create a color management surface as well.
9348 const bool hasPassThrough =
9349 std::any_of(formats.begin(), formats.end(), [this](const VkSurfaceFormatKHR &fmt) {
9350 return fmt.format == colorFormat
9351 && fmt.colorSpace == VK_COLOR_SPACE_PASS_THROUGH_EXT;
9352 });
9353 if (hasPassThrough) {
9354 colorSpace = VK_COLOR_SPACE_PASS_THROUGH_EXT;
9355 }
9356#endif
9357
9358 samples = rhiD->effectiveSampleCountBits(m_sampleCount);
9359
9360 quint32 presModeCount = 0;
9361 rhiD->vkGetPhysicalDeviceSurfacePresentModesKHR(rhiD->physDev, surface, &presModeCount, nullptr);
9362 supportedPresentationModes.resize(presModeCount);
9363 rhiD->vkGetPhysicalDeviceSurfacePresentModesKHR(rhiD->physDev, surface, &presModeCount,
9364 supportedPresentationModes.data());
9365
9366 return true;
9367}
9368
9370{
9371 QRHI_RES_RHI(QRhiVulkan);
9372 const bool needsRegistration = !window || window != m_window;
9373
9374 // Can be called multiple times due to window resizes - that is not the
9375 // same as a simple destroy+create (as with other resources). Thus no
9376 // destroy() here. See recreateSwapChain().
9377
9378 // except if the window actually changes
9379 if (window && window != m_window)
9380 destroy();
9381
9382 window = m_window;
9383 m_currentPixelSize = surfacePixelSize();
9384 pixelSize = m_currentPixelSize;
9385
9386 if (!rhiD->recreateSwapChain(this)) {
9387 qWarning("Failed to create new swapchain");
9388 return false;
9389 }
9390
9391 if (needsRegistration || !rhiD->swapchains.contains(this))
9392 rhiD->swapchains.insert(this);
9393
9394 if (m_depthStencil && m_depthStencil->sampleCount() != m_sampleCount) {
9395 qWarning("Depth-stencil buffer's sampleCount (%d) does not match color buffers' sample count (%d). Expect problems.",
9396 m_depthStencil->sampleCount(), m_sampleCount);
9397 }
9398 if (m_depthStencil && m_depthStencil->pixelSize() != pixelSize) {
9399 if (m_depthStencil->flags().testFlag(QRhiRenderBuffer::UsedWithSwapChainOnly)) {
9400 m_depthStencil->setPixelSize(pixelSize);
9401 if (!m_depthStencil->create())
9402 qWarning("Failed to rebuild swapchain's associated depth-stencil buffer for size %dx%d",
9403 pixelSize.width(), pixelSize.height());
9404 } else {
9405 qWarning("Depth-stencil buffer's size (%dx%d) does not match the surface size (%dx%d). Expect problems.",
9406 m_depthStencil->pixelSize().width(), m_depthStencil->pixelSize().height(),
9407 pixelSize.width(), pixelSize.height());
9408 }
9409 }
9410
9411 if (!m_renderPassDesc)
9412 qWarning("QVkSwapChain: No renderpass descriptor set. See newCompatibleRenderPassDescriptor() and setRenderPassDescriptor().");
9413
9414 rtWrapper.setRenderPassDescriptor(m_renderPassDesc); // for the public getter in QRhiRenderTarget
9415 rtWrapper.d.rp = QRHI_RES(QVkRenderPassDescriptor, m_renderPassDesc);
9416 Q_ASSERT(rtWrapper.d.rp && rtWrapper.d.rp->rp);
9417
9418 rtWrapper.d.pixelSize = pixelSize;
9419 rtWrapper.d.dpr = float(window->devicePixelRatio());
9420 rtWrapper.d.sampleCount = samples;
9421 rtWrapper.d.colorAttCount = 1;
9422 if (m_depthStencil) {
9423 rtWrapper.d.dsAttCount = 1;
9424 ds = QRHI_RES(QVkRenderBuffer, m_depthStencil);
9425 } else {
9426 rtWrapper.d.dsAttCount = 0;
9427 ds = nullptr;
9428 }
9429 rtWrapper.d.dsResolveAttCount = 0;
9430 if (samples > VK_SAMPLE_COUNT_1_BIT)
9431 rtWrapper.d.resolveAttCount = 1;
9432 else
9433 rtWrapper.d.resolveAttCount = 0;
9434
9435 if (shadingRateMapView)
9436 rtWrapper.d.shadingRateAttCount = 1;
9437 else
9438 rtWrapper.d.shadingRateAttCount = 0;
9439
9440 for (int i = 0; i < bufferCount; ++i) {
9441 QVkSwapChain::ImageResources &image(imageRes[i]);
9442 // color, ds, resolve, shading rate
9443 QVarLengthArray<VkImageView, 4> views;
9444 views.append(samples > VK_SAMPLE_COUNT_1_BIT ? image.msaaImageView : image.imageView);
9445 if (ds)
9446 views.append(ds->imageView);
9447 if (samples > VK_SAMPLE_COUNT_1_BIT)
9448 views.append(image.imageView);
9449 if (shadingRateMapView)
9450 views.append(shadingRateMapView);
9451
9452 VkFramebufferCreateInfo fbInfo = {};
9453 fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
9454 fbInfo.renderPass = rtWrapper.d.rp->rp;
9455 fbInfo.attachmentCount = uint32_t(views.count());
9456 fbInfo.pAttachments = views.constData();
9457 fbInfo.width = uint32_t(pixelSize.width());
9458 fbInfo.height = uint32_t(pixelSize.height());
9459 fbInfo.layers = 1;
9460
9461 VkResult err = rhiD->df->vkCreateFramebuffer(rhiD->dev, &fbInfo, nullptr, &image.fb);
9462 if (err != VK_SUCCESS) {
9463 qWarning("Failed to create framebuffer: %d", err);
9464 return false;
9465 }
9466 }
9467
9468 if (stereo) {
9469 rtWrapperRight.setRenderPassDescriptor(
9470 m_renderPassDesc); // for the public getter in QRhiRenderTarget
9471 rtWrapperRight.d.rp = QRHI_RES(QVkRenderPassDescriptor, m_renderPassDesc);
9472 Q_ASSERT(rtWrapperRight.d.rp && rtWrapperRight.d.rp->rp);
9473
9474 rtWrapperRight.d.pixelSize = pixelSize;
9475 rtWrapperRight.d.dpr = float(window->devicePixelRatio());
9476 rtWrapperRight.d.sampleCount = samples;
9477 rtWrapperRight.d.colorAttCount = 1;
9478 if (m_depthStencil) {
9479 rtWrapperRight.d.dsAttCount = 1;
9480 ds = QRHI_RES(QVkRenderBuffer, m_depthStencil);
9481 } else {
9482 rtWrapperRight.d.dsAttCount = 0;
9483 ds = nullptr;
9484 }
9485 rtWrapperRight.d.dsResolveAttCount = 0;
9486 if (samples > VK_SAMPLE_COUNT_1_BIT)
9487 rtWrapperRight.d.resolveAttCount = 1;
9488 else
9489 rtWrapperRight.d.resolveAttCount = 0;
9490
9491 for (int i = 0; i < bufferCount; ++i) {
9492 QVkSwapChain::ImageResources &image(imageRes[i + bufferCount]);
9493 // color, ds, resolve, shading rate
9494 QVarLengthArray<VkImageView, 4> views;
9495 views.append(samples > VK_SAMPLE_COUNT_1_BIT ? image.msaaImageView : image.imageView);
9496 if (ds)
9497 views.append(ds->imageView);
9498 if (samples > VK_SAMPLE_COUNT_1_BIT)
9499 views.append(image.imageView);
9500 if (shadingRateMapView)
9501 views.append(shadingRateMapView);
9502
9503 VkFramebufferCreateInfo fbInfo = {};
9504 fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
9505 fbInfo.renderPass = rtWrapperRight.d.rp->rp;
9506 fbInfo.attachmentCount = uint32_t(views.count());
9507 fbInfo.pAttachments = views.constData();
9508 fbInfo.width = uint32_t(pixelSize.width());
9509 fbInfo.height = uint32_t(pixelSize.height());
9510 fbInfo.layers = 1;
9511
9512 VkResult err = rhiD->df->vkCreateFramebuffer(rhiD->dev, &fbInfo, nullptr, &image.fb);
9513 if (err != VK_SUCCESS) {
9514 qWarning("Failed to create framebuffer: %d", err);
9515 return false;
9516 }
9517 }
9518 }
9519
9520 frameCount = 0;
9521
9522 if (needsRegistration)
9523 rhiD->registerResource(this);
9524
9525 return true;
9526}
9527
9528QT_END_NAMESPACE
const char * constData() const
Definition qrhi_p.h:372
bool isEmpty() const
Definition qrhi.cpp:12023
void registerBuffer(QRhiBuffer *buf, int slot, BufferAccess *access, BufferStage *stage, const UsageState &state)
Definition qrhi.cpp:12040
void registerTexture(QRhiTexture *tex, TextureAccess *access, TextureStage *stage, const UsageState &state)
Definition qrhi.cpp:12080
static QRhiResourceUpdateBatchPrivate * get(QRhiResourceUpdateBatch *b)
Definition qrhi_p.h:597
void recordTransitionPassResources(QVkCommandBuffer *cbD, const QRhiPassResourceTracker &tracker)
QVulkanFunctions * f
VkCommandBuffer startSecondaryCommandBuffer(QVkRenderTargetData *rtD=nullptr)
QRhiSwapChain * createSwapChain() override
void debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) override
int resourceLimit(QRhi::ResourceLimit limit) const override
bool isDeviceLost() const override
void prepareUploadSubres(QVkTexture *texD, int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc, size_t *curOfs, void *mp, BufferImageCopyList *copyInfos)
void executeDeferredReleases(bool forced=false)
uint32_t chooseTransientImageMemType(VkImage img, uint32_t startIndex)
QRhi::FrameOpResult finish() override
QRhiTextureRenderTarget * createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc, QRhiTextureRenderTarget::Flags flags) override
bool createTransientImage(VkFormat format, const QSize &pixelSize, VkImageUsageFlags usage, VkImageAspectFlags aspectMask, VkSampleCountFlagBits samples, VkDeviceMemory *mem, VkImage *images, VkImageView *views, int count)
void setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) override
void releaseCachedResources() override
QVkSwapChain * currentSwapChain
bool importedDevice
QRhiVulkan(QRhiVulkanInitParams *params, QRhiVulkanNativeHandles *importParams=nullptr)
void draw(QRhiCommandBuffer *cb, quint32 vertexCount, quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override
void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override
void endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override
QList< int > supportedSampleCounts() const override
void destroy() override
QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) override
void updateShaderResourceBindings(QRhiShaderResourceBindings *srb)
double elapsedSecondsFromTimestamp(quint64 timestamp[2], bool *ok)
QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags) override
void trackedBufferBarrier(QVkCommandBuffer *cbD, QVkBuffer *bufD, int slot, VkAccessFlags access, VkPipelineStageFlags stage)
QRhi::FrameOpResult waitCommandCompletion(int frameSlot)
void endExternal(QRhiCommandBuffer *cb) override
QWindow * maybeWindow
void dispatch(QRhiCommandBuffer *cb, int x, int y, int z) override
bool allocateDescriptorSet(VkDescriptorSetAllocateInfo *allocInfo, VkDescriptorSet *result, int *resultPoolIndex)
bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const override
void drawIndexedIndirect(QRhiCommandBuffer *cb, QRhiBuffer *indirectBuffer, quint32 indirectBufferOffset, quint32 drawCount, quint32 stride) override
VkResult createDescriptorPool(VkDescriptorPool *pool)
void prepareNewFrame(QRhiCommandBuffer *cb)
void subresourceBarrier(QVkCommandBuffer *cbD, VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout, VkAccessFlags srcAccess, VkAccessFlags dstAccess, VkPipelineStageFlags srcStage, VkPipelineStageFlags dstStage, int startLayer, int layerCount, int startLevel, int levelCount)
QList< QSize > supportedShadingRates(int sampleCount) const override
void printExtraErrorInfo(VkResult err)
void setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps) override
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override
QRhi::FrameOpResult startPrimaryCommandBuffer(VkCommandBuffer *cb)
double lastCompletedGpuTime(QRhiCommandBuffer *cb) override
void trackedRegisterTexture(QRhiPassResourceTracker *passResTracker, QVkTexture *texD, QRhiPassResourceTracker::TextureAccess access, QRhiPassResourceTracker::TextureStage stage)
bool releaseCachedResourcesCalledBeforeFrameStart
QRhiGraphicsPipeline * createGraphicsPipeline() override
QRhiComputePipeline * createComputePipeline() override
void setAllocationName(QVkAlloc allocation, const QByteArray &name, int slot=-1)
QRhiTexture * createTexture(QRhiTexture::Format format, const QSize &pixelSize, int depth, int arraySize, int sampleCount, QRhiTexture::Flags flags) override
void debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) override
const QRhiNativeHandles * nativeHandles(QRhiCommandBuffer *cb) override
bool recreateSwapChain(QRhiSwapChain *swapChain)
void printDeviceLossErrorInfo() const
bool ensurePipelineCache(const void *initialData=nullptr, size_t initialDataSize=0)
void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override
void depthStencilExplicitBarrier(QVkCommandBuffer *cbD, QVkRenderBuffer *rbD)
void trackedImageBarrier(QVkCommandBuffer *cbD, QVkTexture *texD, VkImageLayout layout, VkAccessFlags access, VkPipelineStageFlags stage)
VkShaderModule createShader(const QByteArray &spirv)
void enqueueTransitionPassResources(QVkCommandBuffer *cbD)
void beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates, QRhiCommandBuffer::BeginPassFlags flags) override
bool create(QRhi::Flags flags) override
QVulkanDeviceFunctions * df
void setObjectName(uint64_t object, VkObjectType type, const QByteArray &name, int slot=-1)
bool isFeatureSupported(QRhi::Feature feature) const override
void recordPrimaryCommandBuffer(QVkCommandBuffer *cbD)
bool isYUpInFramebuffer() const override
void setShadingRate(QRhiCommandBuffer *cb, const QSize &coarsePixelSize) override
void debugMarkEnd(QRhiCommandBuffer *cb) override
QRhiSampler * createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter, QRhiSampler::Filter mipmapMode, QRhiSampler::AddressMode u, QRhiSampler::AddressMode v, QRhiSampler::AddressMode w) override
void releaseSwapChainResources(QRhiSwapChain *swapChain)
bool createOffscreenRenderPass(QVkRenderPassDescriptor *rpD, const QRhiColorAttachment *colorAttachmentsBegin, const QRhiColorAttachment *colorAttachmentsEnd, bool preserveColor, bool preserveDs, bool storeDs, QRhiRenderBuffer *depthStencilBuffer, QRhiTexture *depthTexture, QRhiTexture *depthResolveTexture, int depthLayer, QRhiShadingRateMap *shadingRateMap)
void drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount, quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance) override
VkDeviceSize subresUploadByteSize(const QRhiTextureSubresourceUploadDescription &subresDesc) const
void endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override
void trackedRegisterBuffer(QRhiPassResourceTracker *passResTracker, QVkBuffer *bufD, int slot, QRhiPassResourceTracker::BufferAccess access, QRhiPassResourceTracker::BufferStage stage)
VkFormat optimalDepthStencilFormat()
QRhiStats statistics() override
void setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps) override
void drawIndirect(QRhiCommandBuffer *cb, QRhiBuffer *indirectBuffer, quint32 indirectBufferOffset, quint32 drawCount, quint32 stride) override
void activateTextureRenderTarget(QVkCommandBuffer *cbD, QVkTextureRenderTarget *rtD)
void executeBufferHostWritesForSlot(QVkBuffer *bufD, int slot)
void setDefaultScissor(QVkCommandBuffer *cbD)
bool isYUpInNDC() const override
const QRhiNativeHandles * nativeHandles() override
QRhiShadingRateMap * createShadingRateMap() override
void setPipelineCacheData(const QByteArray &data) override
void setVertexInput(QRhiCommandBuffer *cb, int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings, QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat) override
QRhiShaderResourceBindings * createShaderResourceBindings() override
void finishActiveReadbacks(bool forced=false)
void ensureCommandPoolForNewFrame()
QByteArray pipelineCacheData() override
void endAndEnqueueSecondaryCommandBuffer(VkCommandBuffer cb, QVkCommandBuffer *cbD)
void beginPass(QRhiCommandBuffer *cb, QRhiRenderTarget *rt, const QColor &colorClearValue, const QRhiDepthStencilClearValue &depthStencilClearValue, QRhiResourceUpdateBatch *resourceUpdates, QRhiCommandBuffer::BeginPassFlags flags) override
void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override
bool isClipDepthZeroToOne() const override
void enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdateBatch *resourceUpdates)
void setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBindings *srb, int dynamicOffsetCount, const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) override
QVkAllocator allocator
bool importedAllocator
QMatrix4x4 clipSpaceCorrMatrix() const override
int ubufAlignment() const override
QRhiDriverInfo driverInfo() const override
void beginExternal(QRhiCommandBuffer *cb) override
void setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) override
bool createDefaultRenderPass(QVkRenderPassDescriptor *rpD, bool hasDepthStencil, VkSampleCountFlagBits samples, VkFormat colorFormat, QRhiShadingRateMap *shadingRateMap)
QRhi::FrameOpResult endAndSubmitPrimaryCommandBuffer(VkCommandBuffer cb, VkFence cmdFence, VkSemaphore *waitSem, VkSemaphore *signalSem)
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override
VkSampleCountFlagBits effectiveSampleCountBits(int sampleCount)
bool makeThreadLocalNativeContextCurrent() override
QRhiDriverInfo info() const override
Combined button and popup list for selecting options.
@ UnBounded
Definition qrhi_p.h:285
@ Bounded
Definition qrhi_p.h:286
#define QRHI_RES_RHI(t)
Definition qrhi_p.h:31
#define QRHI_RES(t, x)
Definition qrhi_p.h:30
static VkPolygonMode toVkPolygonMode(QRhiGraphicsPipeline::PolygonMode mode)
static VkCullModeFlags toVkCullMode(QRhiGraphicsPipeline::CullMode c)
static bool accessIsWrite(VkAccessFlags access)
static VkCompareOp toVkTextureCompareOp(QRhiSampler::CompareOp op)
static QVulkanInstance * globalVulkanInstance
static VkBufferUsageFlagBits toVkBufferUsage(QRhiBuffer::UsageFlags usage)
static QRhiTexture::Format swapchainReadbackTextureFormat(VkFormat format, QRhiTexture::Flags *flags)
static VkStencilOp toVkStencilOp(QRhiGraphicsPipeline::StencilOp op)
static bool qvk_debug_filter(QVulkanInstance::DebugMessageSeverityFlags severity, QVulkanInstance::DebugMessageTypeFlags type, const void *callbackData)
static QVkBuffer::UsageState toVkBufferUsageState(QRhiPassResourceTracker::UsageState usage)
static bool attachmentDescriptionEquals(const VkAttachmentDescription &a, const VkAttachmentDescription &b)
static bool isSrgbFormat(VkFormat format)
static VkPipelineStageFlags toVkPipelineStage(QRhiPassResourceTracker::TextureStage stage)
static VkAccessFlags toVkAccess(QRhiPassResourceTracker::BufferAccess access)
static VkImageLayout toVkLayout(QRhiPassResourceTracker::TextureAccess access)
static VkFormat toVkAttributeFormat(QRhiVertexInputAttribute::Format format)
static QVkTexture::UsageState toVkTextureUsageState(QRhiPassResourceTracker::UsageState usage)
static VkPipelineStageFlags toVkPipelineStage(QRhiPassResourceTracker::BufferStage stage)
static QRhiDriverInfo::DeviceType toRhiDeviceType(VkPhysicalDeviceType type)
static QRhiPassResourceTracker::UsageState toPassTrackerUsageState(const QVkBuffer::UsageState &bufUsage)
static VkColorComponentFlags toVkColorComponents(QRhiGraphicsPipeline::ColorMask c)
static VkFilter toVkFilter(QRhiSampler::Filter f)
static VkPrimitiveTopology toVkTopology(QRhiGraphicsPipeline::Topology t)
static void qrhivk_releaseTexture(const QRhiVulkan::DeferredReleaseEntry &e, VkDevice dev, QVulkanDeviceFunctions *df, void *allocator)
static void qrhivk_releaseBuffer(const QRhiVulkan::DeferredReleaseEntry &e, void *allocator)
static VkAccessFlags toVkAccess(QRhiPassResourceTracker::TextureAccess access)
static VmaAllocator toVmaAllocator(QVkAllocator a)
static VkSamplerAddressMode toVkAddressMode(QRhiSampler::AddressMode m)
static constexpr bool isDepthTextureFormat(QRhiTexture::Format format)
static void fillVkStencilOpState(VkStencilOpState *dst, const QRhiGraphicsPipeline::StencilOpState &src)
VkSampleCountFlagBits mask
static VkShaderStageFlags toVkShaderStageFlags(QRhiShaderResourceBinding::StageFlags stage)
static constexpr VkImageAspectFlags aspectMaskForTextureFormat(QRhiTexture::Format format)
static VkDescriptorType toVkDescriptorType(const QRhiShaderResourceBinding::Data *b)
static VkBlendFactor toVkBlendFactor(QRhiGraphicsPipeline::BlendFactor f)
static void fillDriverInfo(QRhiDriverInfo *info, const VkPhysicalDeviceProperties &physDevProperties)
static VkBlendOp toVkBlendOp(QRhiGraphicsPipeline::BlendOp op)
static void qrhivk_releaseRenderBuffer(const QRhiVulkan::DeferredReleaseEntry &e, VkDevice dev, QVulkanDeviceFunctions *df)
static VkFrontFace toVkFrontFace(QRhiGraphicsPipeline::FrontFace f)
void qrhivk_accumulateComputeResource(T *writtenResources, QRhiResource *resource, QRhiShaderResourceBinding::Type bindingType, int loadTypeVal, int storeTypeVal, int loadStoreTypeVal)
static VkFormat toVkTextureFormat(QRhiTexture::Format format, QRhiTexture::Flags flags)
static VkCompareOp toVkCompareOp(QRhiGraphicsPipeline::CompareOp op)
static void fillRenderPassCreateInfo(VkRenderPassCreateInfo *rpInfo, VkSubpassDescription *subpassDesc, QVkRenderPassDescriptor *rpD)
static VkSamplerMipmapMode toVkMipmapMode(QRhiSampler::Filter f)
static bool hdrFormatMatchesVkSurfaceFormat(QRhiSwapChain::Format f, const VkSurfaceFormatKHR &s)
static void qrhivk_releaseSampler(const QRhiVulkan::DeferredReleaseEntry &e, VkDevice dev, QVulkanDeviceFunctions *df)
int count
static constexpr bool isStencilTextureFormat(QRhiTexture::Format format)
static QVkRenderTargetData * maybeRenderTargetData(QVkCommandBuffer *cbD)
static QRhiPassResourceTracker::UsageState toPassTrackerUsageState(const QVkTexture::UsageState &texUsage)
static VmaAllocation toVmaAllocation(QVkAlloc a)
static void addToChain(T *head, void *entry)
static VkShaderStageFlagBits toVkShaderStage(QRhiShaderStage::Type type)
void * QVkAllocator
static const int QVK_MAX_ACTIVE_TIMESTAMP_PAIRS
static const int QVK_DESC_SETS_PER_POOL
void * QVkAlloc
static const int QVK_FRAMES_IN_FLIGHT
bool prepare(VkRenderPassCreateInfo *rpInfo, int multiViewCount, bool multiViewCap)
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h
Definition qrhi.h:1872
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h
Definition qrhi.h:1562
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
QVkBuffer(QRhiImplementation *rhi, Type type, UsageFlags usage, quint32 size)
QVkAlloc allocations[QVK_FRAMES_IN_FLIGHT]
QVkAlloc stagingAllocations[QVK_FRAMES_IN_FLIGHT]
void endFullDynamicBufferUpdateForCurrentFrame() override
To be called when the entire contents of the buffer data has been updated in the memory block returne...
QRhiBuffer::NativeBuffer nativeBuffer() override
bool create() override
Creates the corresponding native graphics resources.
char * beginFullDynamicBufferUpdateForCurrentFrame() override
int lastActiveFrameSlot
QVkCommandBuffer(QRhiImplementation *rhi)
const QRhiNativeHandles * nativeHandles()
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
PassType recordingPass
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
QVkComputePipeline(QRhiImplementation *rhi)
bool create() override
QVkGraphicsPipeline(QRhiImplementation *rhi)
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
bool create() override
Creates the corresponding native graphics resources.
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
QVkRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize, int sampleCount, Flags flags, QRhiTexture::Format backingFormatHint)
QRhiTexture::Format backingFormat() const override
bool create() override
Creates the corresponding native graphics resources.
QVkTexture * backingTexture
const QRhiNativeHandles * nativeHandles() override
QRhiRenderPassDescriptor * newCompatibleRenderPassDescriptor() const override
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
QVector< quint32 > serializedFormat() const override
QVkRenderPassDescriptor(QRhiImplementation *rhi)
bool isCompatible(const QRhiRenderPassDescriptor *other) const override
QVkRenderPassDescriptor * rp
static const int MAX_COLOR_ATTACHMENTS
int lastActiveFrameSlot
bool create() override
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
QVkSampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode, AddressMode u, AddressMode v, AddressMode w)
void updateResources(UpdateFlags flags) override
QVkShaderResourceBindings(QRhiImplementation *rhi)
bool create() override
Creates the corresponding resource binding set.
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
bool createFrom(QRhiTexture *src) override
Sets up the shading rate map to use the texture src as the image containing the per-tile shading rate...
QVkShadingRateMap(QRhiImplementation *rhi)
QVkTexture * texture
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
QSize pixelSize() const override
int sampleCount() const override
float devicePixelRatio() const override
QVkSwapChainRenderTarget(QRhiImplementation *rhi, QRhiSwapChain *swapchain)
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
bool createOrResize() override
Creates the swapchain if not already done and resizes the swapchain buffers to match the current size...
QRhiRenderTarget * currentFrameRenderTarget(StereoTargetBuffer targetBuffer) override
bool isFormatSupported(Format f) override
QRhiRenderPassDescriptor * newCompatibleRenderPassDescriptor() override
bool supportsReadback
QVkSwapChain(QRhiImplementation *rhi)
QVkRenderBuffer * ds
QSize surfacePixelSize() override
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
QRhiRenderTarget * currentFrameRenderTarget() override
bool ensureSurface()
QRhiSwapChainHdrInfo hdrInfo() override
\variable QRhiSwapChainHdrInfo::limitsType
QRhiCommandBuffer * currentFrameCommandBuffer() override
QVkTextureRenderTarget(QRhiImplementation *rhi, const QRhiTextureRenderTargetDescription &desc, Flags flags)
float devicePixelRatio() const override
bool create() override
Creates the corresponding native graphics resources.
int sampleCount() const override
QSize pixelSize() const override
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
QRhiRenderPassDescriptor * newCompatibleRenderPassDescriptor() override
bool create() override
Creates the corresponding native graphics resources.
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
bool finishCreate()
VkImageView perLevelImageViewForLoadStore(int level)
int lastActiveFrameSlot
QVkAlloc stagingAllocations[QVK_FRAMES_IN_FLIGHT]
bool createFrom(NativeTexture src) override
Similar to create(), except that no new native textures are created.
QVkAlloc imageAlloc
void setNativeLayout(int layout) override
With some graphics APIs, such as Vulkan, integrating custom rendering code that uses the graphics API...
QVkTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth, int arraySize, int sampleCount, Flags flags)
NativeTexture nativeTexture() override
bool prepareCreate(QSize *adjustedSize=nullptr)