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
2196 QVkTexture *texD = QRHI_RES(QVkTexture, depthResolveTexture);
2197 if (texD->vkformat != rtexD->vkformat) {
2198 qWarning("Multisample resolve between different depth-stencil formats (%d and %d) is not supported.",
2199 int(texD->vkformat), int(rtexD->vkformat));
2200 }
2201
2202 VkAttachmentDescription attDesc = {};
2203 attDesc.format = rtexD->viewFormat;
2204 attDesc.samples = VK_SAMPLE_COUNT_1_BIT;
2205 attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; // ignored
2206 attDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
2207 attDesc.stencilLoadOp = attDesc.loadOp;
2208 attDesc.stencilStoreOp = attDesc.storeOp;
2209 attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
2210 attDesc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
2211 rpD->attDescs.append(attDesc);
2212 rpD->dsResolveRef = { uint32_t(rpD->attDescs.size() - 1), VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
2213 } else {
2214 rpD->dsResolveRef = {};
2215 }
2216
2217 rpD->hasShadingRateMap = shadingRateMap != nullptr;
2218 rpD->shadingRateRef = {};
2219#ifdef VK_KHR_fragment_shading_rate
2220 if (shadingRateMap) {
2221 VkAttachmentDescription attDesc = {};
2222 attDesc.format = VK_FORMAT_R8_UINT;
2223 attDesc.samples = VK_SAMPLE_COUNT_1_BIT;
2224 attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
2225 attDesc.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
2226 attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
2227 attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
2228 attDesc.initialLayout = VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR;
2229 attDesc.finalLayout = VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR;
2230 rpD->attDescs.append(attDesc);
2231 rpD->shadingRateRef = { uint32_t(rpD->attDescs.size() - 1), VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR };
2232 }
2233#endif
2234
2235 // Add self-dependency to be able to add memory barriers for writes in graphics stages.
2236 VkSubpassDependency selfDependency;
2237 VkPipelineStageFlags stageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT
2238 | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT
2239 | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT
2240 | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
2241 selfDependency.srcSubpass = 0;
2242 selfDependency.dstSubpass = 0;
2243 selfDependency.srcStageMask = stageMask;
2244 selfDependency.dstStageMask = stageMask;
2245 selfDependency.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
2246 selfDependency.dstAccessMask = selfDependency.srcAccessMask;
2247 VkDependencyFlags depFlags = VK_DEPENDENCY_BY_REGION_BIT;
2248#ifdef VK_VERSION_1_1
2249 if (rpD->multiViewCount >= 2)
2250 depFlags |= VK_DEPENDENCY_VIEW_LOCAL_BIT;
2251#endif
2252 selfDependency.dependencyFlags = depFlags;
2253 rpD->subpassDeps.append(selfDependency);
2254
2255 // rpD->subpassDeps stays empty: don't yet know the correct initial/final
2256 // access and stage stuff for the implicit deps at this point, so leave it
2257 // to the resource tracking and activateTextureRenderTarget() to generate
2258 // barriers.
2259
2260 VkRenderPassCreateInfo rpInfo;
2261 VkSubpassDescription subpassDesc;
2262 fillRenderPassCreateInfo(&rpInfo, &subpassDesc, rpD);
2263
2264 MultiViewRenderPassSetupHelper multiViewHelper;
2265 if (!multiViewHelper.prepare(&rpInfo, multiViewCount, caps.multiView))
2266 return false;
2267
2268#ifdef VK_KHR_create_renderpass2
2269 if (caps.renderPass2KHR) {
2270 // Use the KHR extension, not the 1.2 core API, in order to support Vulkan 1.1.
2271 VkRenderPassCreateInfo2KHR rpInfo2;
2272 RenderPass2SetupHelper rp2Helper(this);
2273 if (!rp2Helper.prepare(&rpInfo2, &rpInfo, rpD, multiViewCount))
2274 return false;
2275
2276 VkResult err = vkCreateRenderPass2KHR(dev, &rpInfo2, nullptr, &rpD->rp);
2277 if (err != VK_SUCCESS) {
2278 qWarning("Failed to create renderpass (using VkRenderPassCreateInfo2KHR): %d", err);
2279 return false;
2280 }
2281 } else
2282#endif
2283 {
2284 if (rpD->hasDepthStencilResolve) {
2285 qWarning("Resolving multisample depth-stencil buffers is not supported without "
2286 "VK_KHR_depth_stencil_resolve and VK_KHR_create_renderpass2");
2287 }
2288 if (rpD->hasShadingRateMap)
2289 qWarning("Variable rate shading with image is not supported without VK_KHR_create_renderpass2");
2290 VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, &rpD->rp);
2291 if (err != VK_SUCCESS) {
2292 qWarning("Failed to create renderpass: %d", err);
2293 return false;
2294 }
2295 }
2296
2297 return true;
2298}
2299
2300bool QRhiVulkan::recreateSwapChain(QRhiSwapChain *swapChain)
2301{
2302 QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain);
2303 if (swapChainD->pixelSize.isEmpty()) {
2304 qWarning("Surface size is 0, cannot create swapchain");
2305 return false;
2306 }
2307
2308 df->vkDeviceWaitIdle(dev);
2309
2310 if (!vkCreateSwapchainKHR) {
2311 vkCreateSwapchainKHR = reinterpret_cast<PFN_vkCreateSwapchainKHR>(f->vkGetDeviceProcAddr(dev, "vkCreateSwapchainKHR"));
2312 vkDestroySwapchainKHR = reinterpret_cast<PFN_vkDestroySwapchainKHR>(f->vkGetDeviceProcAddr(dev, "vkDestroySwapchainKHR"));
2313 vkGetSwapchainImagesKHR = reinterpret_cast<PFN_vkGetSwapchainImagesKHR>(f->vkGetDeviceProcAddr(dev, "vkGetSwapchainImagesKHR"));
2314 vkAcquireNextImageKHR = reinterpret_cast<PFN_vkAcquireNextImageKHR>(f->vkGetDeviceProcAddr(dev, "vkAcquireNextImageKHR"));
2315 vkQueuePresentKHR = reinterpret_cast<PFN_vkQueuePresentKHR>(f->vkGetDeviceProcAddr(dev, "vkQueuePresentKHR"));
2316 if (!vkCreateSwapchainKHR || !vkDestroySwapchainKHR || !vkGetSwapchainImagesKHR || !vkAcquireNextImageKHR || !vkQueuePresentKHR) {
2317 qWarning("Swapchain functions not available");
2318 return false;
2319 }
2320 }
2321
2322 VkSurfaceCapabilitiesKHR surfaceCaps;
2323 vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physDev, swapChainD->surface, &surfaceCaps);
2324 quint32 reqBufferCount;
2325 if (swapChainD->m_flags.testFlag(QRhiSwapChain::MinimalBufferCount) || surfaceCaps.maxImageCount == 0) {
2326 reqBufferCount = qMax<quint32>(2, surfaceCaps.minImageCount);
2327 } else {
2328 reqBufferCount = qMax(qMin<quint32>(surfaceCaps.maxImageCount, 3), surfaceCaps.minImageCount);
2329 }
2330 VkSurfaceTransformFlagBitsKHR preTransform =
2331 (surfaceCaps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)
2332 ? VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR
2333 : surfaceCaps.currentTransform;
2334
2335 // This looks odd but matches how platforms work in practice.
2336 //
2337 // On Windows with NVIDIA for example, the only supportedCompositeAlpha
2338 // value reported is OPAQUE, nothing else. Yet transparency works
2339 // regardless, as long as the native window is set up correctly, so that's
2340 // not something we need to handle here.
2341 //
2342 // On Linux with Intel and Mesa and running on xcb reports, on one
2343 // particular system, INHERIT+PRE_MULTIPLIED. Tranparency works, regardless,
2344 // presumably due to setting INHERIT.
2345 //
2346 // On the same setup with Wayland instead of xcb we see
2347 // OPAQUE+PRE_MULTIPLIED reported. Here transparency won't work unless
2348 // PRE_MULTIPLIED is set.
2349 //
2350 // Therefore our rules are:
2351 // - Prefer INHERIT over OPAQUE.
2352 // - Then based on the request, try the requested alpha mode, but if
2353 // that's not reported as supported, try also the other (PRE/POST,
2354 // POST/PRE) as that is better than nothing. This is not different from
2355 // some other backends, e.g. D3D11 with DirectComposition there is also
2356 // no control over being straight or pre-multiplied. Whereas with
2357 // WGL/GLX/EGL we never had that sort of control.
2358
2359 VkCompositeAlphaFlagBitsKHR compositeAlpha =
2360 (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR)
2361 ? VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR
2362 : VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
2363
2364 if (swapChainD->m_flags.testFlag(QRhiSwapChain::SurfaceHasPreMulAlpha)) {
2365 if (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR)
2366 compositeAlpha = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;
2367 else if (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR)
2368 compositeAlpha = VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR;
2369 } else if (swapChainD->m_flags.testFlag(QRhiSwapChain::SurfaceHasNonPreMulAlpha)) {
2370 if (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR)
2371 compositeAlpha = VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR;
2372 else if (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR)
2373 compositeAlpha = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;
2374 }
2375
2376 VkImageUsageFlags usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
2377 swapChainD->supportsReadback = (surfaceCaps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
2378 if (swapChainD->supportsReadback && swapChainD->m_flags.testFlag(QRhiSwapChain::UsedAsTransferSource))
2379 usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
2380
2381 const bool stereo = bool(swapChainD->m_window) && (swapChainD->m_window->format().stereo())
2382 && surfaceCaps.maxImageArrayLayers > 1;
2383 swapChainD->stereo = stereo;
2384
2385 VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR;
2386 if (swapChainD->m_flags.testFlag(QRhiSwapChain::NoVSync)) {
2387 // Stereo has a weird bug, when using VK_PRESENT_MODE_MAILBOX_KHR,
2388 // black screen is shown, but there is no validation error.
2389 // Detected on Windows, with NVidia RTX A series (at least 4000 and 6000) driver 535.98
2390 if (swapChainD->supportedPresentationModes.contains(VK_PRESENT_MODE_MAILBOX_KHR) && !stereo)
2391 presentMode = VK_PRESENT_MODE_MAILBOX_KHR;
2392 else if (swapChainD->supportedPresentationModes.contains(VK_PRESENT_MODE_IMMEDIATE_KHR))
2393 presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
2394 }
2395
2396 // If the surface is different than before, then passing in the old
2397 // swapchain associated with the old surface can fail the swapchain
2398 // creation. (for example, Android loses the surface when backgrounding and
2399 // restoring applications, and it also enforces failing swapchain creation
2400 // with VK_ERROR_NATIVE_WINDOW_IN_USE_KHR if the old swapchain is provided)
2401 const bool reuseExisting = swapChainD->sc && swapChainD->lastConnectedSurface == swapChainD->surface;
2402
2403 qCDebug(QRHI_LOG_INFO, "Creating %s swapchain of %u buffers, size %dx%d, presentation mode %d",
2404 reuseExisting ? "recycled" : "new",
2405 reqBufferCount, swapChainD->pixelSize.width(), swapChainD->pixelSize.height(), presentMode);
2406
2407 VkSwapchainCreateInfoKHR swapChainInfo = {};
2408 swapChainInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
2409 swapChainInfo.surface = swapChainD->surface;
2410 swapChainInfo.minImageCount = reqBufferCount;
2411 swapChainInfo.imageFormat = swapChainD->colorFormat;
2412 swapChainInfo.imageColorSpace = swapChainD->colorSpace;
2413 swapChainInfo.imageExtent = VkExtent2D { uint32_t(swapChainD->pixelSize.width()), uint32_t(swapChainD->pixelSize.height()) };
2414 swapChainInfo.imageArrayLayers = stereo ? 2u : 1u;
2415 swapChainInfo.imageUsage = usage;
2416 swapChainInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
2417 swapChainInfo.preTransform = preTransform;
2418 swapChainInfo.compositeAlpha = compositeAlpha;
2419 swapChainInfo.presentMode = presentMode;
2420 swapChainInfo.clipped = true;
2421 swapChainInfo.oldSwapchain = reuseExisting ? swapChainD->sc : VK_NULL_HANDLE;
2422
2423 VkSwapchainKHR newSwapChain;
2424 VkResult err = vkCreateSwapchainKHR(dev, &swapChainInfo, nullptr, &newSwapChain);
2425 if (err != VK_SUCCESS) {
2426 qWarning("Failed to create swapchain: %d", err);
2427 return false;
2428 }
2429 setObjectName(uint64_t(newSwapChain), VK_OBJECT_TYPE_SWAPCHAIN_KHR, swapChainD->m_objectName);
2430
2431 if (swapChainD->sc)
2433
2434 swapChainD->sc = newSwapChain;
2435 swapChainD->lastConnectedSurface = swapChainD->surface;
2436
2437 quint32 actualSwapChainBufferCount = 0;
2438 err = vkGetSwapchainImagesKHR(dev, swapChainD->sc, &actualSwapChainBufferCount, nullptr);
2439 if (err != VK_SUCCESS || actualSwapChainBufferCount == 0) {
2440 qWarning("Failed to get swapchain images: %d", err);
2441 return false;
2442 }
2443
2444 if (actualSwapChainBufferCount != reqBufferCount)
2445 qCDebug(QRHI_LOG_INFO, "Actual swapchain buffer count is %u", actualSwapChainBufferCount);
2446 swapChainD->bufferCount = int(actualSwapChainBufferCount);
2447
2448 QVarLengthArray<VkImage, QVkSwapChain::EXPECTED_MAX_BUFFER_COUNT> swapChainImages(actualSwapChainBufferCount);
2449 err = vkGetSwapchainImagesKHR(dev, swapChainD->sc, &actualSwapChainBufferCount, swapChainImages.data());
2450 if (err != VK_SUCCESS) {
2451 qWarning("Failed to get swapchain images: %d", err);
2452 return false;
2453 }
2454
2455 QVarLengthArray<VkImage, QVkSwapChain::EXPECTED_MAX_BUFFER_COUNT> msaaImages(swapChainD->bufferCount);
2456 QVarLengthArray<VkImageView, QVkSwapChain::EXPECTED_MAX_BUFFER_COUNT> msaaViews(swapChainD->bufferCount);
2457 if (swapChainD->samples > VK_SAMPLE_COUNT_1_BIT) {
2458 if (!createTransientImage(swapChainD->colorFormat,
2459 swapChainD->pixelSize,
2460 VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
2461 VK_IMAGE_ASPECT_COLOR_BIT,
2462 swapChainD->samples,
2463 &swapChainD->msaaImageMem,
2464 msaaImages.data(),
2465 msaaViews.data(),
2466 swapChainD->bufferCount))
2467 {
2468 qWarning("Failed to create transient image for MSAA color buffer");
2469 return false;
2470 }
2471 }
2472
2473 VkFenceCreateInfo fenceInfo = {};
2474 fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
2475 fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
2476
2477 // Double up for stereo
2478 swapChainD->imageRes.resize(swapChainD->bufferCount * (stereo ? 2u : 1u));
2479
2480 for (int i = 0; i < swapChainD->bufferCount; ++i) {
2481 QVkSwapChain::ImageResources &image(swapChainD->imageRes[i]);
2482 image.image = swapChainImages[i];
2483 if (swapChainD->samples > VK_SAMPLE_COUNT_1_BIT) {
2484 image.msaaImage = msaaImages[i];
2485 image.msaaImageView = msaaViews[i];
2486 }
2487
2488 VkImageViewCreateInfo imgViewInfo = {};
2489 imgViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
2490 imgViewInfo.image = swapChainImages[i];
2491 imgViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
2492 imgViewInfo.format = swapChainD->colorFormat;
2493 imgViewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
2494 imgViewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
2495 imgViewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
2496 imgViewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
2497 imgViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
2498 imgViewInfo.subresourceRange.levelCount = imgViewInfo.subresourceRange.layerCount = 1;
2499 err = df->vkCreateImageView(dev, &imgViewInfo, nullptr, &image.imageView);
2500 if (err != VK_SUCCESS) {
2501 qWarning("Failed to create swapchain image view %d: %d", i, err);
2502 return false;
2503 }
2504
2506
2507 VkSemaphoreCreateInfo semInfo = {};
2508 semInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
2509 df->vkCreateSemaphore(dev, &semInfo, nullptr, &image.drawSem);
2510 }
2511 if (stereo) {
2512 for (int i = 0; i < swapChainD->bufferCount; ++i) {
2513 QVkSwapChain::ImageResources &image(swapChainD->imageRes[i + swapChainD->bufferCount]);
2514 image.image = swapChainImages[i];
2515 if (swapChainD->samples > VK_SAMPLE_COUNT_1_BIT) {
2516 image.msaaImage = msaaImages[i];
2517 image.msaaImageView = msaaViews[i];
2518 }
2519
2520 VkImageViewCreateInfo imgViewInfo = {};
2521 imgViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
2522 imgViewInfo.image = swapChainImages[i];
2523 imgViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
2524 imgViewInfo.format = swapChainD->colorFormat;
2525 imgViewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
2526 imgViewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
2527 imgViewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
2528 imgViewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
2529 imgViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
2530 imgViewInfo.subresourceRange.baseArrayLayer = 1;
2531 imgViewInfo.subresourceRange.levelCount = imgViewInfo.subresourceRange.layerCount = 1;
2532 err = df->vkCreateImageView(dev, &imgViewInfo, nullptr, &image.imageView);
2533 if (err != VK_SUCCESS) {
2534 qWarning("Failed to create swapchain image view %d: %d", i, err);
2535 return false;
2536 }
2537
2538 VkSemaphoreCreateInfo semInfo = {};
2539 semInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
2540 df->vkCreateSemaphore(dev, &semInfo, nullptr, &image.drawSem);
2541
2543 }
2544 }
2545
2546 swapChainD->currentImageIndex = 0;
2547
2548 if (swapChainD->shadingRateMap() && caps.renderPass2KHR && caps.imageBasedShadingRate) {
2549 QVkTexture *texD = QRHI_RES(QVkShadingRateMap, swapChainD->shadingRateMap())->texture;
2550 Q_ASSERT(texD->flags().testFlag(QRhiTexture::UsedAsShadingRateMap));
2551 VkImageViewCreateInfo viewInfo = {};
2552 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
2553 viewInfo.image = texD->image;
2554 viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
2555 viewInfo.format = texD->viewFormat;
2556 viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
2557 viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
2558 viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
2559 viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
2560 viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
2561 viewInfo.subresourceRange.baseMipLevel = 0;
2562 viewInfo.subresourceRange.levelCount = 1;
2563 viewInfo.subresourceRange.baseArrayLayer = 0;
2564 viewInfo.subresourceRange.layerCount = 1;
2565 VkResult err = df->vkCreateImageView(dev, &viewInfo, nullptr, &swapChainD->shadingRateMapView);
2566 if (err != VK_SUCCESS) {
2567 qWarning("Failed to create swapchain shading rate map view: %d", err);
2568 return false;
2569 }
2570 }
2571
2572 VkSemaphoreCreateInfo semInfo = {};
2573 semInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
2574
2575 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
2576 QVkSwapChain::FrameResources &frame(swapChainD->frameRes[i]);
2577
2578 frame.imageAcquired = false;
2579 frame.imageSemWaitable = false;
2580
2581 df->vkCreateSemaphore(dev, &semInfo, nullptr, &frame.imageSem);
2582
2583 err = df->vkCreateFence(dev, &fenceInfo, nullptr, &frame.cmdFence);
2584 if (err != VK_SUCCESS) {
2585 qWarning("Failed to create command buffer fence: %d", err);
2586 return false;
2587 }
2588 frame.cmdFenceWaitable = true; // fence was created in signaled state
2589 }
2590
2591 return true;
2592}
2593
2594void QRhiVulkan::releaseSwapChainResources(QRhiSwapChain *swapChain)
2595{
2596 QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain);
2597
2598 if (swapChainD->sc == VK_NULL_HANDLE)
2599 return;
2600
2601 if (!deviceLost)
2602 df->vkDeviceWaitIdle(dev);
2603
2604 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
2605 QVkSwapChain::FrameResources &frame(swapChainD->frameRes[i]);
2606 if (frame.cmdFence) {
2607 if (!deviceLost && frame.cmdFenceWaitable)
2608 df->vkWaitForFences(dev, 1, &frame.cmdFence, VK_TRUE, UINT64_MAX);
2609 df->vkDestroyFence(dev, frame.cmdFence, nullptr);
2610 frame.cmdFence = VK_NULL_HANDLE;
2611 frame.cmdFenceWaitable = false;
2612 }
2613 if (frame.imageSem) {
2614 df->vkDestroySemaphore(dev, frame.imageSem, nullptr);
2615 frame.imageSem = VK_NULL_HANDLE;
2616 }
2617 }
2618
2619 for (int i = 0; i < swapChainD->bufferCount * (swapChainD->stereo ? 2 : 1); ++i) {
2620 QVkSwapChain::ImageResources &image(swapChainD->imageRes[i]);
2621 if (image.fb) {
2622 df->vkDestroyFramebuffer(dev, image.fb, nullptr);
2623 image.fb = VK_NULL_HANDLE;
2624 }
2625 if (image.imageView) {
2626 df->vkDestroyImageView(dev, image.imageView, nullptr);
2627 image.imageView = VK_NULL_HANDLE;
2628 }
2629 if (image.msaaImageView) {
2630 df->vkDestroyImageView(dev, image.msaaImageView, nullptr);
2631 image.msaaImageView = VK_NULL_HANDLE;
2632 }
2633 if (image.msaaImage) {
2634 df->vkDestroyImage(dev, image.msaaImage, nullptr);
2635 image.msaaImage = VK_NULL_HANDLE;
2636 }
2637 if (image.drawSem) {
2638 df->vkDestroySemaphore(dev, image.drawSem, nullptr);
2639 image.drawSem = VK_NULL_HANDLE;
2640 }
2641 }
2642
2643 if (swapChainD->msaaImageMem) {
2644 df->vkFreeMemory(dev, swapChainD->msaaImageMem, nullptr);
2645 swapChainD->msaaImageMem = VK_NULL_HANDLE;
2646 }
2647
2648 if (swapChainD->shadingRateMapView) {
2649 df->vkDestroyImageView(dev, swapChainD->shadingRateMapView, nullptr);
2650 swapChainD->shadingRateMapView = VK_NULL_HANDLE;
2651 }
2652
2653 vkDestroySwapchainKHR(dev, swapChainD->sc, nullptr);
2654 swapChainD->sc = VK_NULL_HANDLE;
2655
2656 // NB! surface and similar must remain intact
2657}
2658
2660{
2661 VkCommandPoolResetFlags flags = 0;
2662
2663 // While not clear what "recycles all of the resources from the command
2664 // pool back to the system" really means in practice, set it when there was
2665 // a call to releaseCachedResources() recently.
2666 if (releaseCachedResourcesCalledBeforeFrameStart)
2667 flags |= VK_COMMAND_POOL_RESET_RELEASE_RESOURCES_BIT;
2668
2669 // put all command buffers allocated from this slot's pool to initial state
2670 df->vkResetCommandPool(dev, cmdPool[currentFrameSlot], flags);
2671}
2672
2673double QRhiVulkan::elapsedSecondsFromTimestamp(quint64 timestamp[2], bool *ok)
2674{
2675 quint64 mask = 0;
2676 for (quint64 i = 0; i < timestampValidBits; i += 8)
2677 mask |= 0xFFULL << i;
2678 const quint64 ts0 = timestamp[0] & mask;
2679 const quint64 ts1 = timestamp[1] & mask;
2680 const float nsecsPerTick = physDevProperties.limits.timestampPeriod;
2681 if (!qFuzzyIsNull(nsecsPerTick)) {
2682 const float elapsedMs = float(ts1 - ts0) * nsecsPerTick / 1000000.0f;
2683 const double elapsedSec = elapsedMs / 1000.0;
2684 *ok = true;
2685 return elapsedSec;
2686 }
2687 *ok = false;
2688 return 0;
2689}
2690
2692{
2693 QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain);
2694 const int frameResIndex = swapChainD->bufferCount > 1 ? currentFrameSlot : 0;
2695 QVkSwapChain::FrameResources &frame(swapChainD->frameRes[frameResIndex]);
2696
2697 inst->handle()->beginFrame(swapChainD->window);
2698
2699 // Make sure the previous commands for the same frame slot have finished.
2700 //
2701 // Do this also for any other swapchain's commands with the same frame slot
2702 // It keeps resource usage safe: swapchain A starting its frame 0, followed
2703 // by swapchain B starting its own frame 0 will make B wait for A's frame 0
2704 // commands, so if a resource is written in B's frame or when B checks for
2705 // pending resource releases, that won't mess up A's in-flight commands (as
2706 // they are not in flight anymore).
2707 QRhi::FrameOpResult waitResult = waitCommandCompletion(frameResIndex);
2708 if (waitResult != QRhi::FrameOpSuccess)
2709 return waitResult;
2710
2711 if (!frame.imageAcquired) {
2712 // move on to next swapchain image
2713 uint32_t imageIndex = 0;
2714 VkResult err = vkAcquireNextImageKHR(dev, swapChainD->sc, UINT64_MAX,
2715 frame.imageSem, VK_NULL_HANDLE, &imageIndex);
2716
2717 if (err == VK_SUCCESS || err == VK_SUBOPTIMAL_KHR) {
2718 swapChainD->currentImageIndex = imageIndex;
2719 frame.imageSemWaitable = true;
2720 frame.imageAcquired = true;
2721 } else if (err == VK_ERROR_OUT_OF_DATE_KHR) {
2722 return QRhi::FrameOpSwapChainOutOfDate;
2723 } else {
2724 if (err == VK_ERROR_DEVICE_LOST) {
2725 qWarning("Device loss detected in vkAcquireNextImageKHR()");
2726 printExtraErrorInfo(err);
2727 deviceLost = true;
2728 return QRhi::FrameOpDeviceLost;
2729 }
2730 qWarning("Failed to acquire next swapchain image: %d", err);
2731 return QRhi::FrameOpError;
2732 }
2733 }
2734
2735 currentSwapChain = swapChainD;
2736 if (swapChainD->ds)
2737 swapChainD->ds->lastActiveFrameSlot = currentFrameSlot;
2738
2739 // reset the command pool
2741
2742 // start recording to this frame's command buffer
2743 QRhi::FrameOpResult cbres = startPrimaryCommandBuffer(&frame.cmdBuf);
2744 if (cbres != QRhi::FrameOpSuccess)
2745 return cbres;
2746
2747 swapChainD->cbWrapper.cb = frame.cmdBuf;
2748
2749 QVkSwapChain::ImageResources &image(swapChainD->imageRes[swapChainD->currentImageIndex]);
2750 swapChainD->rtWrapper.d.fb = image.fb;
2751
2752 if (swapChainD->stereo) {
2754 swapChainD->imageRes[swapChainD->currentImageIndex + swapChainD->bufferCount]);
2755 swapChainD->rtWrapperRight.d.fb = image.fb;
2756 }
2757
2758 prepareNewFrame(&swapChainD->cbWrapper);
2759
2760 // Read the timestamps for the previous frame for this slot.
2761 if (frame.timestampQueryIndex >= 0) {
2762 quint64 timestamp[2] = { 0, 0 };
2763 VkResult err = df->vkGetQueryPoolResults(dev, timestampQueryPool, uint32_t(frame.timestampQueryIndex), 2,
2764 2 * sizeof(quint64), timestamp, sizeof(quint64),
2765 VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT);
2766 timestampQueryPoolMap.clearBit(frame.timestampQueryIndex / 2);
2767 frame.timestampQueryIndex = -1;
2768 if (err == VK_SUCCESS) {
2769 bool ok = false;
2770 const double elapsedSec = elapsedSecondsFromTimestamp(timestamp, &ok);
2771 if (ok)
2772 swapChainD->cbWrapper.lastGpuTime = elapsedSec;
2773 } else {
2774 qWarning("Failed to query timestamp: %d", err);
2775 }
2776 }
2777
2778 // No timestamps if the client did not opt in, or when not having at least 2 frames in flight.
2779 if (rhiFlags.testFlag(QRhi::EnableTimestamps) && swapChainD->bufferCount > 1) {
2780 int timestampQueryIdx = -1;
2781 for (int i = 0; i < timestampQueryPoolMap.size(); ++i) {
2782 if (!timestampQueryPoolMap.testBit(i)) {
2783 timestampQueryPoolMap.setBit(i);
2784 timestampQueryIdx = i * 2;
2785 break;
2786 }
2787 }
2788 if (timestampQueryIdx >= 0) {
2789 df->vkCmdResetQueryPool(frame.cmdBuf, timestampQueryPool, uint32_t(timestampQueryIdx), 2);
2790 // record timestamp at the start of the command buffer
2791 df->vkCmdWriteTimestamp(frame.cmdBuf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
2792 timestampQueryPool, uint32_t(timestampQueryIdx));
2793 frame.timestampQueryIndex = timestampQueryIdx;
2794 }
2795 }
2796
2797 return QRhi::FrameOpSuccess;
2798}
2799
2800QRhi::FrameOpResult QRhiVulkan::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags)
2801{
2802 QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain);
2803 Q_ASSERT(currentSwapChain == swapChainD);
2804
2805 auto cleanup = qScopeGuard([this, swapChainD] {
2806 inst->handle()->endFrame(swapChainD->window);
2807 });
2808
2809 recordPrimaryCommandBuffer(&swapChainD->cbWrapper);
2810
2811 int frameResIndex = swapChainD->bufferCount > 1 ? currentFrameSlot : 0;
2812 QVkSwapChain::FrameResources &frame(swapChainD->frameRes[frameResIndex]);
2813 QVkSwapChain::ImageResources &image(swapChainD->imageRes[swapChainD->currentImageIndex]);
2814
2816 VkImageMemoryBarrier presTrans = {};
2817 presTrans.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
2818 presTrans.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
2819 presTrans.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
2820 presTrans.image = image.image;
2821 presTrans.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
2822 presTrans.subresourceRange.levelCount = presTrans.subresourceRange.layerCount = 1;
2823
2825 // was not used at all (no render pass), just transition from undefined to presentable
2826 presTrans.srcAccessMask = 0;
2827 presTrans.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
2828 df->vkCmdPipelineBarrier(frame.cmdBuf,
2829 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
2830 0, 0, nullptr, 0, nullptr,
2831 1, &presTrans);
2833 // was used in a readback as transfer source, go back to presentable layout
2834 presTrans.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
2835 presTrans.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
2836 df->vkCmdPipelineBarrier(frame.cmdBuf,
2837 VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
2838 0, 0, nullptr, 0, nullptr,
2839 1, &presTrans);
2840 }
2842 }
2843
2844 // record another timestamp, when enabled
2845 if (frame.timestampQueryIndex >= 0) {
2846 df->vkCmdWriteTimestamp(frame.cmdBuf, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
2847 timestampQueryPool, uint32_t(frame.timestampQueryIndex + 1));
2848 }
2849
2850 // stop recording and submit to the queue
2851 Q_ASSERT(!frame.cmdFenceWaitable);
2852 const bool needsPresent = !flags.testFlag(QRhi::SkipPresent);
2853 QRhi::FrameOpResult submitres = endAndSubmitPrimaryCommandBuffer(frame.cmdBuf,
2854 frame.cmdFence,
2855 frame.imageSemWaitable ? &frame.imageSem : nullptr,
2856 needsPresent ? &image.drawSem : nullptr);
2857 if (submitres != QRhi::FrameOpSuccess)
2858 return submitres;
2859
2860 frame.imageSemWaitable = false;
2861 frame.cmdFenceWaitable = true;
2862
2863 if (needsPresent) {
2864 // add the Present to the queue
2865 VkPresentInfoKHR presInfo = {};
2866 presInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
2867 presInfo.swapchainCount = 1;
2868 presInfo.pSwapchains = &swapChainD->sc;
2869 presInfo.pImageIndices = &swapChainD->currentImageIndex;
2870 waitSemaphoresForPresent.append(image.drawSem);
2871 presInfo.waitSemaphoreCount = uint32_t(waitSemaphoresForPresent.count());;
2872 presInfo.pWaitSemaphores = waitSemaphoresForPresent.constData();
2873
2874 // Do platform-specific WM notification. F.ex. essential on Wayland in
2875 // order to circumvent driver frame callbacks
2876 inst->presentAboutToBeQueued(swapChainD->window);
2877
2878 VkResult err = vkQueuePresentKHR(gfxQueue, &presInfo);
2879 waitSemaphoresForPresent.clear();
2880 if (err != VK_SUCCESS) {
2881 if (err == VK_ERROR_OUT_OF_DATE_KHR) {
2882 return QRhi::FrameOpSwapChainOutOfDate;
2883 } else if (err != VK_SUBOPTIMAL_KHR) {
2884 if (err == VK_ERROR_DEVICE_LOST) {
2885 qWarning("Device loss detected in vkQueuePresentKHR()");
2886 printExtraErrorInfo(err);
2887 deviceLost = true;
2888 return QRhi::FrameOpDeviceLost;
2889 }
2890 qWarning("Failed to present: %d", err);
2891 return QRhi::FrameOpError;
2892 }
2893 }
2894
2895 // Do platform-specific WM notification. F.ex. essential on X11 in
2896 // order to prevent glitches on resizing the window.
2897 inst->presentQueued(swapChainD->window);
2898
2899 // mark the current swapchain buffer as unused from our side
2900 frame.imageAcquired = false;
2901 // and move on to the next slot
2902 currentFrameSlot = (currentFrameSlot + 1) % QVK_FRAMES_IN_FLIGHT;
2903 }
2904
2905 swapChainD->frameCount += 1;
2906 currentSwapChain = nullptr;
2907 return QRhi::FrameOpSuccess;
2908}
2909
2910void QRhiVulkan::prepareNewFrame(QRhiCommandBuffer *cb)
2911{
2912 // Now is the time to do things for frame N-F, where N is the current one,
2913 // F is QVK_FRAMES_IN_FLIGHT, because only here it is guaranteed that that
2914 // frame has completed on the GPU (due to the fence wait in beginFrame). To
2915 // decide if something is safe to handle now a simple "lastActiveFrameSlot
2916 // == currentFrameSlot" is sufficient (remember that e.g. with F==2
2917 // currentFrameSlot goes 0, 1, 0, 1, 0, ...)
2918
2920
2921 QRHI_RES(QVkCommandBuffer, cb)->resetState();
2922
2923 finishActiveReadbacks(); // last, in case the readback-completed callback issues rhi calls
2924
2926}
2927
2929{
2930 if (!*cb) {
2931 VkCommandBufferAllocateInfo cmdBufInfo = {};
2932 cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
2933 cmdBufInfo.commandPool = cmdPool[currentFrameSlot];
2934 cmdBufInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
2935 cmdBufInfo.commandBufferCount = 1;
2936
2937 VkResult err = df->vkAllocateCommandBuffers(dev, &cmdBufInfo, cb);
2938 if (err != VK_SUCCESS) {
2939 if (err == VK_ERROR_DEVICE_LOST) {
2940 qWarning("Device loss detected in vkAllocateCommandBuffers()");
2941 printExtraErrorInfo(err);
2942 deviceLost = true;
2943 return QRhi::FrameOpDeviceLost;
2944 }
2945 qWarning("Failed to allocate frame command buffer: %d", err);
2946 return QRhi::FrameOpError;
2947 }
2948 }
2949
2950 VkCommandBufferBeginInfo cmdBufBeginInfo = {};
2951 cmdBufBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
2952
2953 VkResult err = df->vkBeginCommandBuffer(*cb, &cmdBufBeginInfo);
2954 if (err != VK_SUCCESS) {
2955 if (err == VK_ERROR_DEVICE_LOST) {
2956 qWarning("Device loss detected in vkBeginCommandBuffer()");
2957 printExtraErrorInfo(err);
2958 deviceLost = true;
2959 return QRhi::FrameOpDeviceLost;
2960 }
2961 qWarning("Failed to begin frame command buffer: %d", err);
2962 return QRhi::FrameOpError;
2963 }
2964
2965 return QRhi::FrameOpSuccess;
2966}
2967
2968QRhi::FrameOpResult QRhiVulkan::endAndSubmitPrimaryCommandBuffer(VkCommandBuffer cb, VkFence cmdFence,
2969 VkSemaphore *waitSem, VkSemaphore *signalSem)
2970{
2971 VkResult err = df->vkEndCommandBuffer(cb);
2972 if (err != VK_SUCCESS) {
2973 if (err == VK_ERROR_DEVICE_LOST) {
2974 qWarning("Device loss detected in vkEndCommandBuffer()");
2975 printExtraErrorInfo(err);
2976 deviceLost = true;
2977 return QRhi::FrameOpDeviceLost;
2978 }
2979 qWarning("Failed to end frame command buffer: %d", err);
2980 return QRhi::FrameOpError;
2981 }
2982
2983 VkSubmitInfo submitInfo = {};
2984 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
2985 submitInfo.commandBufferCount = 1;
2986 submitInfo.pCommandBuffers = &cb;
2987
2988 if (waitSem)
2989 waitSemaphoresForQueueSubmit.append(*waitSem);
2990 if (signalSem)
2991 signalSemaphoresForQueueSubmit.append(*signalSem);
2992
2993 submitInfo.waitSemaphoreCount = uint32_t(waitSemaphoresForQueueSubmit.count());
2994 if (!waitSemaphoresForQueueSubmit.isEmpty()) {
2995 submitInfo.pWaitSemaphores = waitSemaphoresForQueueSubmit.constData();
2996 semaphoresWaitMasksForQueueSubmit.resize(waitSemaphoresForQueueSubmit.count(), VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
2997 submitInfo.pWaitDstStageMask = semaphoresWaitMasksForQueueSubmit.constData();
2998 }
2999 submitInfo.signalSemaphoreCount = uint32_t(signalSemaphoresForQueueSubmit.count());
3000 if (!signalSemaphoresForQueueSubmit.isEmpty()) {
3001 submitInfo.pSignalSemaphores = signalSemaphoresForQueueSubmit.constData();
3002 }
3003
3004 err = df->vkQueueSubmit(gfxQueue, 1, &submitInfo, cmdFence);
3005
3006 waitSemaphoresForQueueSubmit.clear();
3007 signalSemaphoresForQueueSubmit.clear();
3008
3009 if (err != VK_SUCCESS) {
3010 if (err == VK_ERROR_DEVICE_LOST) {
3011 qWarning("Device loss detected in vkQueueSubmit()");
3012 printExtraErrorInfo(err);
3013 deviceLost = true;
3014 return QRhi::FrameOpDeviceLost;
3015 }
3016 qWarning("Failed to submit to graphics queue: %d", err);
3017 return QRhi::FrameOpError;
3018 }
3019
3020 return QRhi::FrameOpSuccess;
3021}
3022
3024{
3025 for (QVkSwapChain *sc : std::as_const(swapchains)) {
3026 const int frameResIndex = sc->bufferCount > 1 ? frameSlot : 0;
3027 QVkSwapChain::FrameResources &frame(sc->frameRes[frameResIndex]);
3028 if (frame.cmdFenceWaitable) {
3029 VkResult err = df->vkWaitForFences(dev, 1, &frame.cmdFence, VK_TRUE, UINT64_MAX);
3030
3031 if (err != VK_SUCCESS) {
3032 if (err == VK_ERROR_DEVICE_LOST) {
3033 qWarning("Device loss detected in vkWaitForFences()");
3034 printExtraErrorInfo(err);
3035 deviceLost = true;
3036 return QRhi::FrameOpDeviceLost;
3037 }
3038 qWarning("Failed to wait for fence: %d", err);
3039 return QRhi::FrameOpError;
3040 }
3041
3042 df->vkResetFences(dev, 1, &frame.cmdFence);
3043 frame.cmdFenceWaitable = false;
3044 }
3045 }
3046
3047 if (ofr.cmdFenceWaitable[frameSlot]) {
3048 VkResult err = df->vkWaitForFences(dev, 1, &ofr.cmdFence[frameSlot], VK_TRUE, UINT64_MAX);
3049 if (err != VK_SUCCESS) {
3050 if (err == VK_ERROR_DEVICE_LOST) {
3051 qWarning("Device loss detected in vkWaitForFences()");
3052 printExtraErrorInfo(err);
3053 deviceLost = true;
3054 return QRhi::FrameOpDeviceLost;
3055 }
3056 qWarning("Failed to wait for offscreen fence: %d", err);
3057 return QRhi::FrameOpError;
3058 }
3059 df->vkResetFences(dev, 1, &ofr.cmdFence[frameSlot]);
3060 ofr.cmdFenceWaitable[frameSlot] = false;
3061 }
3062
3063 return QRhi::FrameOpSuccess;
3064}
3065
3067{
3068 QRhi::FrameOpResult waitResult = waitCommandCompletion(currentFrameSlot);
3069 if (waitResult != QRhi::FrameOpSuccess)
3070 return waitResult;
3071
3073
3074 QVkCommandBuffer *cbWrapper = ofr.cbWrapper[currentFrameSlot];
3075 QRhi::FrameOpResult cbres = startPrimaryCommandBuffer(&cbWrapper->cb);
3076 if (cbres != QRhi::FrameOpSuccess)
3077 return cbres;
3078
3079 prepareNewFrame(cbWrapper);
3080 ofr.active = true;
3081
3082 if (ofr.timestampQueryIndex[currentFrameSlot] >= 0) {
3083 quint64 timestamp[2] = { 0, 0 };
3084 VkResult err = df->vkGetQueryPoolResults(dev, timestampQueryPool,
3085 uint32_t(ofr.timestampQueryIndex[currentFrameSlot]), 2,
3086 2 * sizeof(quint64), timestamp, sizeof(quint64),
3087 VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT);
3088 timestampQueryPoolMap.clearBit(ofr.timestampQueryIndex[currentFrameSlot] / 2);
3089 ofr.timestampQueryIndex[currentFrameSlot] = -1;
3090 if (err == VK_SUCCESS) {
3091 bool ok = false;
3092 const double elapsedSec = elapsedSecondsFromTimestamp(timestamp, &ok);
3093 if (ok)
3094 cbWrapper->lastGpuTime = elapsedSec;
3095 } else {
3096 qWarning("Failed to query timestamp: %d", err);
3097 }
3098 }
3099
3100 if (rhiFlags.testFlag(QRhi::EnableTimestamps)) {
3101 int timestampQueryIdx = -1;
3102 for (int i = 0; i < timestampQueryPoolMap.size(); ++i) {
3103 if (!timestampQueryPoolMap.testBit(i)) {
3104 timestampQueryPoolMap.setBit(i);
3105 timestampQueryIdx = i * 2;
3106 break;
3107 }
3108 }
3109 if (timestampQueryIdx >= 0) {
3110 df->vkCmdResetQueryPool(cbWrapper->cb, timestampQueryPool, uint32_t(timestampQueryIdx), 2);
3111 // record timestamp at the start of the command buffer
3112 df->vkCmdWriteTimestamp(cbWrapper->cb, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
3113 timestampQueryPool, uint32_t(timestampQueryIdx));
3114 ofr.timestampQueryIndex[currentFrameSlot] = timestampQueryIdx;
3115 }
3116 }
3117
3118 *cb = cbWrapper;
3119 return QRhi::FrameOpSuccess;
3120}
3121
3123{
3124 Q_UNUSED(flags);
3125 Q_ASSERT(ofr.active);
3126 ofr.active = false;
3127
3128 QVkCommandBuffer *cbWrapper(ofr.cbWrapper[currentFrameSlot]);
3130
3131 const bool readbacksPending = !activeTextureReadbacks.isEmpty() || !activeBufferReadbacks.isEmpty();
3132
3133 // record the end timestamp, when enabled; results are read back in the
3134 // next beginOffscreenFrame for this slot, after the fence wait.
3135 if (ofr.timestampQueryIndex[currentFrameSlot] >= 0) {
3136 df->vkCmdWriteTimestamp(cbWrapper->cb, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
3137 timestampQueryPool, uint32_t(ofr.timestampQueryIndex[currentFrameSlot] + 1));
3138 }
3139
3140 if (!ofr.cmdFence[currentFrameSlot]) {
3141 VkFenceCreateInfo fenceInfo = {};
3142 fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
3143 VkResult err = df->vkCreateFence(dev, &fenceInfo, nullptr, &ofr.cmdFence[currentFrameSlot]);
3144 if (err != VK_SUCCESS) {
3145 qWarning("Failed to create command buffer fence: %d", err);
3146 return QRhi::FrameOpError;
3147 }
3148 }
3149
3150 QRhi::FrameOpResult submitres = endAndSubmitPrimaryCommandBuffer(cbWrapper->cb, ofr.cmdFence[currentFrameSlot], nullptr, nullptr);
3151 if (submitres != QRhi::FrameOpSuccess)
3152 return submitres;
3153
3154 ofr.cmdFenceWaitable[currentFrameSlot] = true;
3155
3156 // Synchronously wait only when readbacks were scheduled.
3157 if (readbacksPending) {
3158 df->vkWaitForFences(dev, 1, &ofr.cmdFence[currentFrameSlot], VK_TRUE, UINT64_MAX);
3159 df->vkResetFences(dev, 1, &ofr.cmdFence[currentFrameSlot]);
3160 ofr.cmdFenceWaitable[currentFrameSlot] = false;
3161
3162 // Here we know that executing the host-side reads for this (or any
3163 // previous) frame is safe since we waited for completion above.
3165 }
3166
3167 currentFrameSlot = (currentFrameSlot + 1) % QVK_FRAMES_IN_FLIGHT;
3168
3169 return QRhi::FrameOpSuccess;
3170}
3171
3173{
3174 QVkSwapChain *swapChainD = nullptr;
3175 if (inFrame) {
3176 // There is either a swapchain or an offscreen frame on-going.
3177 // End command recording and submit what we have.
3178 VkCommandBuffer cb;
3179 if (ofr.active) {
3180 Q_ASSERT(!currentSwapChain);
3181 QVkCommandBuffer *cbWrapper(ofr.cbWrapper[currentFrameSlot]);
3182 Q_ASSERT(cbWrapper->recordingPass == QVkCommandBuffer::NoPass);
3184 cbWrapper->resetCommands();
3185 cb = cbWrapper->cb;
3186 } else {
3187 Q_ASSERT(currentSwapChain);
3188 Q_ASSERT(currentSwapChain->cbWrapper.recordingPass == QVkCommandBuffer::NoPass);
3189 swapChainD = currentSwapChain;
3190 recordPrimaryCommandBuffer(&swapChainD->cbWrapper);
3191 swapChainD->cbWrapper.resetCommands();
3192 cb = swapChainD->cbWrapper.cb;
3193 }
3194 QRhi::FrameOpResult submitres = endAndSubmitPrimaryCommandBuffer(cb, VK_NULL_HANDLE, nullptr, nullptr);
3195 if (submitres != QRhi::FrameOpSuccess)
3196 return submitres;
3197 }
3198
3199 df->vkQueueWaitIdle(gfxQueue);
3200
3201 if (inFrame) {
3202 // The current frame slot's command pool needs to be reset.
3204 // Allocate and begin recording on a new command buffer.
3205 if (ofr.active) {
3206 startPrimaryCommandBuffer(&ofr.cbWrapper[currentFrameSlot]->cb);
3207 } else {
3208 QVkSwapChain::FrameResources &frame(swapChainD->frameRes[currentFrameSlot]);
3209 startPrimaryCommandBuffer(&frame.cmdBuf);
3210 swapChainD->cbWrapper.cb = frame.cmdBuf;
3211 }
3212 }
3213
3216
3217 return QRhi::FrameOpSuccess;
3218}
3219
3221{
3223 u.layout = 0; // unused with buffers
3224 u.access = int(bufUsage.access);
3225 u.stage = int(bufUsage.stage);
3226 return u;
3227}
3228
3230{
3232 u.layout = texUsage.layout;
3233 u.access = int(texUsage.access);
3234 u.stage = int(texUsage.stage);
3235 return u;
3236}
3237
3239{
3240 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QVkTexture, QVkRenderBuffer>(rtD->description(), rtD->d.currentResIdList))
3241 rtD->create();
3242
3243 rtD->lastActiveFrameSlot = currentFrameSlot;
3244 rtD->d.rp->lastActiveFrameSlot = currentFrameSlot;
3245 QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
3246 for (auto it = rtD->m_desc.cbeginColorAttachments(), itEnd = rtD->m_desc.cendColorAttachments(); it != itEnd; ++it) {
3247 QVkTexture *texD = QRHI_RES(QVkTexture, it->texture());
3248 QVkTexture *resolveTexD = QRHI_RES(QVkTexture, it->resolveTexture());
3249 QVkRenderBuffer *rbD = QRHI_RES(QVkRenderBuffer, it->renderBuffer());
3250 if (texD) {
3251 trackedRegisterTexture(&passResTracker, texD,
3254 texD->lastActiveFrameSlot = currentFrameSlot;
3255 } else if (rbD) {
3256 // Won't register rbD->backingTexture because it cannot be used for
3257 // anything in a renderpass, its use makes only sense in
3258 // combination with a resolveTexture.
3259 rbD->lastActiveFrameSlot = currentFrameSlot;
3260 }
3261 if (resolveTexD) {
3262 trackedRegisterTexture(&passResTracker, resolveTexD,
3265 resolveTexD->lastActiveFrameSlot = currentFrameSlot;
3266 }
3267 }
3268 if (rtD->m_desc.depthStencilBuffer()) {
3269 QVkRenderBuffer *rbD = QRHI_RES(QVkRenderBuffer, rtD->m_desc.depthStencilBuffer());
3270 Q_ASSERT(rbD->m_type == QRhiRenderBuffer::DepthStencil);
3271 // We specify no explicit VkSubpassDependency for an offscreen render
3272 // target, meaning we need an explicit barrier for the depth-stencil
3273 // buffer to avoid a write-after-write hazard (as the implicit one is
3274 // not sufficient). Textures are taken care of by the resource tracking
3275 // but that excludes the (content-wise) throwaway depth-stencil buffer.
3277 rbD->lastActiveFrameSlot = currentFrameSlot;
3278 }
3279 if (rtD->m_desc.depthTexture()) {
3280 QVkTexture *depthTexD = QRHI_RES(QVkTexture, rtD->m_desc.depthTexture());
3281 trackedRegisterTexture(&passResTracker, depthTexD,
3284 depthTexD->lastActiveFrameSlot = currentFrameSlot;
3285 }
3286 if (rtD->m_desc.depthResolveTexture()) {
3287 QVkTexture *depthResolveTexD = QRHI_RES(QVkTexture, rtD->m_desc.depthResolveTexture());
3288 trackedRegisterTexture(&passResTracker, depthResolveTexD,
3291 depthResolveTexD->lastActiveFrameSlot = currentFrameSlot;
3292 }
3293 if (rtD->m_desc.shadingRateMap()) {
3294 QVkTexture *texD = QRHI_RES(QVkShadingRateMap, rtD->m_desc.shadingRateMap())->texture;
3295 trackedRegisterTexture(&passResTracker, texD,
3298 texD->lastActiveFrameSlot = currentFrameSlot;
3299 }
3300}
3301
3302void QRhiVulkan::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
3303{
3304 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
3306
3307 enqueueResourceUpdates(cbD, resourceUpdates);
3308}
3309
3311{
3312 VkCommandBuffer secondaryCb;
3313
3314 if (!freeSecondaryCbs[currentFrameSlot].isEmpty()) {
3315 secondaryCb = freeSecondaryCbs[currentFrameSlot].last();
3316 freeSecondaryCbs[currentFrameSlot].removeLast();
3317 } else {
3318 VkCommandBufferAllocateInfo cmdBufInfo = {};
3319 cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
3320 cmdBufInfo.commandPool = cmdPool[currentFrameSlot];
3321 cmdBufInfo.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY;
3322 cmdBufInfo.commandBufferCount = 1;
3323
3324 VkResult err = df->vkAllocateCommandBuffers(dev, &cmdBufInfo, &secondaryCb);
3325 if (err != VK_SUCCESS) {
3326 qWarning("Failed to create secondary command buffer: %d", err);
3327 return VK_NULL_HANDLE;
3328 }
3329 }
3330
3331 VkCommandBufferBeginInfo cmdBufBeginInfo = {};
3332 cmdBufBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
3333 cmdBufBeginInfo.flags = rtD ? VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT : 0;
3334 VkCommandBufferInheritanceInfo cmdBufInheritInfo = {};
3335 cmdBufInheritInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
3336 cmdBufInheritInfo.subpass = 0;
3337 if (rtD) {
3338 cmdBufInheritInfo.renderPass = rtD->rp->rp;
3339 cmdBufInheritInfo.framebuffer = rtD->fb;
3340 }
3341 cmdBufBeginInfo.pInheritanceInfo = &cmdBufInheritInfo;
3342
3343 VkResult err = df->vkBeginCommandBuffer(secondaryCb, &cmdBufBeginInfo);
3344 if (err != VK_SUCCESS) {
3345 qWarning("Failed to begin secondary command buffer: %d", err);
3346 return VK_NULL_HANDLE;
3347 }
3348
3349 return secondaryCb;
3350}
3351
3353{
3354 VkResult err = df->vkEndCommandBuffer(cb);
3355 if (err != VK_SUCCESS)
3356 qWarning("Failed to end secondary command buffer: %d", err);
3357
3358 QVkCommandBuffer::Command &cmd(cbD->commands.get());
3360 cmd.args.executeSecondary.cb = cb;
3361
3364 e.lastActiveFrameSlot = currentFrameSlot;
3365 e.secondaryCommandBuffer.cb = cb;
3366 releaseQueue.append(e);
3367}
3368
3369void QRhiVulkan::beginPass(QRhiCommandBuffer *cb,
3370 QRhiRenderTarget *rt,
3371 const QColor &colorClearValue,
3372 const QRhiDepthStencilClearValue &depthStencilClearValue,
3373 QRhiResourceUpdateBatch *resourceUpdates,
3374 QRhiCommandBuffer::BeginPassFlags flags)
3375{
3376 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
3378
3379 if (resourceUpdates)
3380 enqueueResourceUpdates(cbD, resourceUpdates);
3381
3382 // Insert a TransitionPassResources into the command stream, pointing to
3383 // the tracker this pass is going to use. That's how we generate the
3384 // barriers later during recording the real VkCommandBuffer, right before
3385 // the vkCmdBeginRenderPass.
3387
3388 QVkRenderTargetData *rtD = nullptr;
3389 switch (rt->resourceType()) {
3390 case QRhiResource::SwapChainRenderTarget:
3391 rtD = &QRHI_RES(QVkSwapChainRenderTarget, rt)->d;
3392 rtD->rp->lastActiveFrameSlot = currentFrameSlot;
3393 Q_ASSERT(currentSwapChain);
3394 currentSwapChain->imageRes[currentSwapChain->currentImageIndex].lastUse =
3396 if (currentSwapChain->shadingRateMapView) {
3398 QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
3399 trackedRegisterTexture(&passResTracker, texD,
3402 texD->lastActiveFrameSlot = currentFrameSlot;
3403 }
3404 break;
3405 case QRhiResource::TextureRenderTarget:
3406 {
3408 rtD = &rtTex->d;
3410 }
3411 break;
3412 default:
3413 Q_UNREACHABLE();
3414 break;
3415 }
3416
3418 cbD->passUsesSecondaryCb = flags.testFlag(QRhiCommandBuffer::ExternalContent);
3419 cbD->currentTarget = rt;
3420
3421 // No copy operations or image layout transitions allowed after this point
3422 // (up until endPass) as we are going to begin the renderpass.
3423
3424 VkRenderPassBeginInfo rpBeginInfo = {};
3425 rpBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
3426 rpBeginInfo.renderPass = rtD->rp->rp;
3427 rpBeginInfo.framebuffer = rtD->fb;
3428 rpBeginInfo.renderArea.extent.width = uint32_t(rtD->pixelSize.width());
3429 rpBeginInfo.renderArea.extent.height = uint32_t(rtD->pixelSize.height());
3430
3431 const bool rpHasAnyClearOp = std::any_of(rtD->rp->attDescs.cbegin(), rtD->rp->attDescs.cend(),
3432 [](const VkAttachmentDescription &attDesc) {
3433 return (attDesc.loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR
3434 || attDesc.stencilLoadOp == VK_ATTACHMENT_LOAD_OP_CLEAR);
3435 });
3436
3437 QVarLengthArray<VkClearValue, (QVkRenderTargetData::MAX_COLOR_ATTACHMENTS + 1) * 2 + 1> cvs;
3438 if (rpHasAnyClearOp) {
3439 for (int i = 0; i < rtD->colorAttCount; ++i) {
3440 VkClearValue cv;
3441 cv.color = { { colorClearValue.redF(), colorClearValue.greenF(), colorClearValue.blueF(),
3442 colorClearValue.alphaF() } };
3443 cvs.append(cv);
3444 }
3445 for (int i = 0; i < rtD->dsAttCount; ++i) {
3446 VkClearValue cv;
3447 cv.depthStencil = { depthStencilClearValue.depthClearValue(), depthStencilClearValue.stencilClearValue() };
3448 cvs.append(cv);
3449 }
3450 for (int i = 0; i < rtD->resolveAttCount; ++i) {
3451 VkClearValue cv;
3452 cv.color = { { colorClearValue.redF(), colorClearValue.greenF(), colorClearValue.blueF(),
3453 colorClearValue.alphaF() } };
3454 cvs.append(cv);
3455 }
3456 for (int i = 0; i < rtD->dsResolveAttCount; ++i) {
3457 VkClearValue cv;
3458 cv.depthStencil = { depthStencilClearValue.depthClearValue(), depthStencilClearValue.stencilClearValue() };
3459 cvs.append(cv);
3460 }
3461 for (int i = 0; i < rtD->shadingRateAttCount; ++i) {
3462 VkClearValue cv;
3463 cv.color = { { 0.0f, 0.0f, 0.0f, 0.0f } };
3464 cvs.append(cv);
3465 }
3466 }
3467 Q_ASSERT(!rpHasAnyClearOp || cvs.size() == rtD->rp->attDescs.size());
3468 rpBeginInfo.clearValueCount = uint32_t(cvs.size());
3469
3470 QVkCommandBuffer::Command &cmd(cbD->commands.get());
3472 cmd.args.beginRenderPass.desc = rpBeginInfo;
3473 cmd.args.beginRenderPass.clearValueIndex = cbD->pools.clearValue.size();
3474 cmd.args.beginRenderPass.useSecondaryCb = cbD->passUsesSecondaryCb;
3475 cbD->pools.clearValue.append(cvs.constData(), cvs.size());
3476
3477 if (cbD->passUsesSecondaryCb)
3478 cbD->activeSecondaryCbStack.append(startSecondaryCommandBuffer(rtD));
3479
3480 if (cbD->hasShadingRateSet) {
3481 QVkCommandBuffer::Command &rateCmd(cbD->commands.get());
3483 rateCmd.args.setShadingRate.w = 1;
3484 rateCmd.args.setShadingRate.h = 1;
3485 }
3486
3488}
3489
3490void QRhiVulkan::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
3491{
3492 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
3494
3495 if (cbD->passUsesSecondaryCb) {
3496 VkCommandBuffer secondaryCb = cbD->activeSecondaryCbStack.last();
3497 cbD->activeSecondaryCbStack.removeLast();
3498 endAndEnqueueSecondaryCommandBuffer(secondaryCb, cbD);
3499 }
3500
3501 QVkCommandBuffer::Command &cmd(cbD->commands.get());
3503
3505 cbD->currentTarget = nullptr;
3506
3507 if (resourceUpdates)
3508 enqueueResourceUpdates(cbD, resourceUpdates);
3509}
3510
3511void QRhiVulkan::beginComputePass(QRhiCommandBuffer *cb,
3512 QRhiResourceUpdateBatch *resourceUpdates,
3513 QRhiCommandBuffer::BeginPassFlags flags)
3514{
3515 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
3517
3518 if (resourceUpdates)
3519 enqueueResourceUpdates(cbD, resourceUpdates);
3520
3522
3524 cbD->passUsesSecondaryCb = flags.testFlag(QRhiCommandBuffer::ExternalContent);
3525
3526 cbD->computePassState.reset();
3527
3528 if (cbD->passUsesSecondaryCb)
3529 cbD->activeSecondaryCbStack.append(startSecondaryCommandBuffer());
3530
3532}
3533
3534void QRhiVulkan::endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
3535{
3536 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
3538
3539 if (cbD->passUsesSecondaryCb) {
3540 VkCommandBuffer secondaryCb = cbD->activeSecondaryCbStack.last();
3541 cbD->activeSecondaryCbStack.removeLast();
3542 endAndEnqueueSecondaryCommandBuffer(secondaryCb, cbD);
3543 }
3544
3546
3547 if (resourceUpdates)
3548 enqueueResourceUpdates(cbD, resourceUpdates);
3549}
3550
3551void QRhiVulkan::setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps)
3552{
3554 Q_ASSERT(psD->pipeline);
3555 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
3557
3558 if (cbD->currentComputePipeline != ps || cbD->currentPipelineGeneration != psD->generation) {
3559 if (cbD->passUsesSecondaryCb) {
3560 df->vkCmdBindPipeline(cbD->activeSecondaryCbStack.last(), VK_PIPELINE_BIND_POINT_COMPUTE, psD->pipeline);
3561 } else {
3562 QVkCommandBuffer::Command &cmd(cbD->commands.get());
3564 cmd.args.bindPipeline.bindPoint = VK_PIPELINE_BIND_POINT_COMPUTE;
3565 cmd.args.bindPipeline.pipeline = psD->pipeline;
3566 }
3567
3568 cbD->currentGraphicsPipeline = nullptr;
3569 cbD->currentComputePipeline = ps;
3570 cbD->currentPipelineGeneration = psD->generation;
3571 }
3572
3573 psD->lastActiveFrameSlot = currentFrameSlot;
3574}
3575
3576template<typename T>
3577inline void qrhivk_accumulateComputeResource(T *writtenResources, QRhiResource *resource,
3578 QRhiShaderResourceBinding::Type bindingType,
3579 int loadTypeVal, int storeTypeVal, int loadStoreTypeVal)
3580{
3581 VkAccessFlags access = 0;
3582 if (bindingType == loadTypeVal) {
3583 access = VK_ACCESS_SHADER_READ_BIT;
3584 } else {
3585 access = VK_ACCESS_SHADER_WRITE_BIT;
3586 if (bindingType == loadStoreTypeVal)
3587 access |= VK_ACCESS_SHADER_READ_BIT;
3588 }
3589 auto it = writtenResources->find(resource);
3590 if (it != writtenResources->end())
3591 it->second.accessFlags |= access;
3592 else if (bindingType == storeTypeVal || bindingType == loadStoreTypeVal)
3593 writtenResources->insert(resource, { access, true });
3594}
3595
3596void QRhiVulkan::dispatch(QRhiCommandBuffer *cb, int x, int y, int z)
3597{
3598 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
3600
3601 // When there are multiple dispatches, read-after-write and
3602 // write-after-write need a barrier.
3603 QVarLengthArray<VkImageMemoryBarrier, 8> imageBarriers;
3604 QVarLengthArray<VkBufferMemoryBarrier, 8> bufferBarriers;
3605 if (cbD->currentComputeSrb) {
3606 // The key in the writtenResources map indicates that the resource was
3607 // written in a previous dispatch, whereas the value accumulates the
3608 // access mask in the current one.
3609 for (auto [res, accessAndIsNewFlag] : cbD->computePassState.writtenResources)
3610 accessAndIsNewFlag = { 0, false }; // note: accessAndIsNewFlag is a reference
3611
3612 QVkShaderResourceBindings *srbD = QRHI_RES(QVkShaderResourceBindings, cbD->currentComputeSrb);
3613 for (auto &binding : srbD->m_bindings) {
3614 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(binding);
3615 switch (b->type) {
3616 case QRhiShaderResourceBinding::ImageLoad:
3617 case QRhiShaderResourceBinding::ImageStore:
3618 case QRhiShaderResourceBinding::ImageLoadStore:
3619 qrhivk_accumulateComputeResource(&cbD->computePassState.writtenResources,
3620 b->u.simage.tex,
3621 b->type,
3622 QRhiShaderResourceBinding::ImageLoad,
3623 QRhiShaderResourceBinding::ImageStore,
3624 QRhiShaderResourceBinding::ImageLoadStore);
3625 break;
3626 case QRhiShaderResourceBinding::BufferLoad:
3627 case QRhiShaderResourceBinding::BufferStore:
3628 case QRhiShaderResourceBinding::BufferLoadStore:
3629 qrhivk_accumulateComputeResource(&cbD->computePassState.writtenResources,
3630 b->u.sbuf.buf,
3631 b->type,
3632 QRhiShaderResourceBinding::BufferLoad,
3633 QRhiShaderResourceBinding::BufferStore,
3634 QRhiShaderResourceBinding::BufferLoadStore);
3635 break;
3636 default:
3637 break;
3638 }
3639 }
3640
3641 for (auto it = cbD->computePassState.writtenResources.begin(); it != cbD->computePassState.writtenResources.end(); ) {
3642 const VkAccessFlags accessInThisDispatch = it->second.accessFlags;
3643 const bool isNewInThisDispatch = it->second.isNew;
3644 if (accessInThisDispatch && !isNewInThisDispatch) {
3645 if (it.key()->resourceType() == QRhiResource::Texture) {
3646 QVkTexture *texD = QRHI_RES(QVkTexture, it.key());
3647 VkImageMemoryBarrier barrier = {};
3648 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
3649 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
3650 // won't care about subresources, pretend the whole resource was written
3651 barrier.subresourceRange.baseMipLevel = 0;
3652 barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
3653 barrier.subresourceRange.baseArrayLayer = 0;
3654 barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
3655 barrier.oldLayout = texD->usageState.layout;
3656 barrier.newLayout = texD->usageState.layout;
3657 barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
3658 barrier.dstAccessMask = accessInThisDispatch;
3659 barrier.image = texD->image;
3660 imageBarriers.append(barrier);
3661 } else {
3662 QVkBuffer *bufD = QRHI_RES(QVkBuffer, it.key());
3663 VkBufferMemoryBarrier barrier = {};
3664 barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
3665 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
3666 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
3667 barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
3668 barrier.dstAccessMask = accessInThisDispatch;
3669 barrier.buffer = bufD->buffers[bufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0];
3670 barrier.size = VK_WHOLE_SIZE;
3671 bufferBarriers.append(barrier);
3672 }
3673 }
3674 // Anything that was previously written, but is only read now, can be
3675 // removed from the written list (because that previous write got a
3676 // corresponding barrier now).
3677 if (accessInThisDispatch == VK_ACCESS_SHADER_READ_BIT)
3678 it = cbD->computePassState.writtenResources.erase(it);
3679 else
3680 ++it;
3681 }
3682 }
3683
3684 if (cbD->passUsesSecondaryCb) {
3685 VkCommandBuffer secondaryCb = cbD->activeSecondaryCbStack.last();
3686 if (!imageBarriers.isEmpty() || !bufferBarriers.isEmpty()) {
3687 df->vkCmdPipelineBarrier(secondaryCb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
3688 0, 0, nullptr,
3689 bufferBarriers.size(), bufferBarriers.isEmpty() ? nullptr : bufferBarriers.constData(),
3690 imageBarriers.size(), imageBarriers.isEmpty() ? nullptr : imageBarriers.constData());
3691 }
3692 df->vkCmdDispatch(secondaryCb, uint32_t(x), uint32_t(y), uint32_t(z));
3693 } else {
3694 if (!imageBarriers.isEmpty() || !bufferBarriers.isEmpty()) {
3695 QVkCommandBuffer::Command &cmd(cbD->commands.get());
3697 cmd.args.imageAndBufferBarrier.srcStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
3698 cmd.args.imageAndBufferBarrier.dstStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
3699 cmd.args.imageAndBufferBarrier.imageCount = imageBarriers.size();
3700 cmd.args.imageAndBufferBarrier.imageIndex = cbD->pools.imageBarrier.size();
3701 cbD->pools.imageBarrier.append(imageBarriers.constData(), imageBarriers.size());
3702 cmd.args.imageAndBufferBarrier.bufferCount = bufferBarriers.size();
3703 cmd.args.imageAndBufferBarrier.bufferIndex = cbD->pools.bufferBarrier.size();
3704 cbD->pools.bufferBarrier.append(bufferBarriers.constData(), bufferBarriers.size());
3705 }
3706 QVkCommandBuffer::Command &cmd(cbD->commands.get());
3708 cmd.args.dispatch.x = x;
3709 cmd.args.dispatch.y = y;
3710 cmd.args.dispatch.z = z;
3711 }
3712}
3713
3714VkShaderModule QRhiVulkan::createShader(const QByteArray &spirv)
3715{
3716 VkShaderModuleCreateInfo shaderInfo = {};
3717 shaderInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
3718 shaderInfo.codeSize = size_t(spirv.size());
3719 shaderInfo.pCode = reinterpret_cast<const quint32 *>(spirv.constData());
3720 VkShaderModule shaderModule;
3721 VkResult err = df->vkCreateShaderModule(dev, &shaderInfo, nullptr, &shaderModule);
3722 if (err != VK_SUCCESS) {
3723 qWarning("Failed to create shader module: %d", err);
3724 return VK_NULL_HANDLE;
3725 }
3726 return shaderModule;
3727}
3728
3729bool QRhiVulkan::ensurePipelineCache(const void *initialData, size_t initialDataSize)
3730{
3731 if (pipelineCache)
3732 return true;
3733
3734 VkPipelineCacheCreateInfo pipelineCacheInfo = {};
3735 pipelineCacheInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
3736 pipelineCacheInfo.initialDataSize = initialDataSize;
3737 pipelineCacheInfo.pInitialData = initialData;
3738 VkResult err = df->vkCreatePipelineCache(dev, &pipelineCacheInfo, nullptr, &pipelineCache);
3739 if (err != VK_SUCCESS) {
3740 qWarning("Failed to create pipeline cache: %d", err);
3741 return false;
3742 }
3743 return true;
3744}
3745
3746void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb)
3747{
3749
3750 QVarLengthArray<VkDescriptorBufferInfo, 8> bufferInfos;
3751 using ArrayOfImageDesc = QVarLengthArray<VkDescriptorImageInfo, 8>;
3752 QVarLengthArray<ArrayOfImageDesc, 8> imageInfos;
3753 QVarLengthArray<VkWriteDescriptorSet, 12> writeInfos;
3754 QVarLengthArray<std::pair<int, int>, 12> infoIndices;
3755
3756 for (int i = 0, ie = srbD->sortedBindings.size(); i != ie; ++i) {
3757 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->sortedBindings.at(i));
3758 QVkShaderResourceBindings::BoundResourceData &bd(srbD->boundResourceData[currentFrameSlot][i]);
3759
3760 VkWriteDescriptorSet writeInfo = {};
3761 writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
3762 writeInfo.dstSet = srbD->descSets[currentFrameSlot];
3763 writeInfo.dstBinding = uint32_t(b->binding);
3764 writeInfo.descriptorCount = 1;
3765
3766 int bufferInfoIndex = -1;
3767 int imageInfoIndex = -1;
3768
3769 switch (b->type) {
3770 case QRhiShaderResourceBinding::UniformBuffer:
3771 {
3772 writeInfo.descriptorType = b->u.ubuf.hasDynamicOffset ? VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC
3773 : VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
3774 QRhiBuffer *buf = b->u.ubuf.buf;
3775 QVkBuffer *bufD = QRHI_RES(QVkBuffer, buf);
3776 bd.ubuf.id = bufD->m_id;
3777 bd.ubuf.generation = bufD->generation;
3778 VkDescriptorBufferInfo bufInfo;
3779 bufInfo.buffer = bufD->m_type == QRhiBuffer::Dynamic ? bufD->buffers[currentFrameSlot] : bufD->buffers[0];
3780 bufInfo.offset = b->u.ubuf.offset;
3781 bufInfo.range = b->u.ubuf.maybeSize ? b->u.ubuf.maybeSize : VK_WHOLE_SIZE;
3782 // be nice and assert when we know the vulkan device would die a horrible death due to non-aligned reads
3783 Q_ASSERT(aligned(bufInfo.offset, ubufAlign) == bufInfo.offset);
3784 bufferInfoIndex = bufferInfos.size();
3785 bufferInfos.append(bufInfo);
3786 }
3787 break;
3788 case QRhiShaderResourceBinding::SampledTexture:
3789 {
3790 const QRhiShaderResourceBinding::Data::TextureAndOrSamplerData *data = &b->u.stex;
3791 writeInfo.descriptorCount = data->count; // arrays of combined image samplers are supported
3792 writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
3793 ArrayOfImageDesc imageInfo(data->count);
3794 for (int elem = 0; elem < data->count; ++elem) {
3795 QVkTexture *texD = QRHI_RES(QVkTexture, data->texSamplers[elem].tex);
3796 QVkSampler *samplerD = QRHI_RES(QVkSampler, data->texSamplers[elem].sampler);
3797 bd.stex.d[elem].texId = texD->m_id;
3798 bd.stex.d[elem].texGeneration = texD->generation;
3799 bd.stex.d[elem].samplerId = samplerD->m_id;
3800 bd.stex.d[elem].samplerGeneration = samplerD->generation;
3801 imageInfo[elem].sampler = samplerD->sampler;
3802 imageInfo[elem].imageView = texD->imageView;
3803 imageInfo[elem].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
3804 }
3805 bd.stex.count = data->count;
3806 imageInfoIndex = imageInfos.size();
3807 imageInfos.append(imageInfo);
3808 }
3809 break;
3810 case QRhiShaderResourceBinding::Texture:
3811 {
3812 const QRhiShaderResourceBinding::Data::TextureAndOrSamplerData *data = &b->u.stex;
3813 writeInfo.descriptorCount = data->count; // arrays of (separate) images are supported
3814 writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
3815 ArrayOfImageDesc imageInfo(data->count);
3816 for (int elem = 0; elem < data->count; ++elem) {
3817 QVkTexture *texD = QRHI_RES(QVkTexture, data->texSamplers[elem].tex);
3818 bd.stex.d[elem].texId = texD->m_id;
3819 bd.stex.d[elem].texGeneration = texD->generation;
3820 bd.stex.d[elem].samplerId = 0;
3821 bd.stex.d[elem].samplerGeneration = 0;
3822 imageInfo[elem].sampler = VK_NULL_HANDLE;
3823 imageInfo[elem].imageView = texD->imageView;
3824 imageInfo[elem].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
3825 }
3826 bd.stex.count = data->count;
3827 imageInfoIndex = imageInfos.size();
3828 imageInfos.append(imageInfo);
3829 }
3830 break;
3831 case QRhiShaderResourceBinding::Sampler:
3832 {
3833 QVkSampler *samplerD = QRHI_RES(QVkSampler, b->u.stex.texSamplers[0].sampler);
3834 writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;
3835 bd.stex.d[0].texId = 0;
3836 bd.stex.d[0].texGeneration = 0;
3837 bd.stex.d[0].samplerId = samplerD->m_id;
3838 bd.stex.d[0].samplerGeneration = samplerD->generation;
3839 ArrayOfImageDesc imageInfo(1);
3840 imageInfo[0].sampler = samplerD->sampler;
3841 imageInfo[0].imageView = VK_NULL_HANDLE;
3842 imageInfo[0].imageLayout = VK_IMAGE_LAYOUT_GENERAL;
3843 imageInfoIndex = imageInfos.size();
3844 imageInfos.append(imageInfo);
3845 }
3846 break;
3847 case QRhiShaderResourceBinding::ImageLoad:
3848 case QRhiShaderResourceBinding::ImageStore:
3849 case QRhiShaderResourceBinding::ImageLoadStore:
3850 {
3851 QVkTexture *texD = QRHI_RES(QVkTexture, b->u.simage.tex);
3852 VkImageView view = texD->perLevelImageViewForLoadStore(b->u.simage.level);
3853 if (view) {
3854 writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
3855 bd.simage.id = texD->m_id;
3856 bd.simage.generation = texD->generation;
3857 ArrayOfImageDesc imageInfo(1);
3858 imageInfo[0].sampler = VK_NULL_HANDLE;
3859 imageInfo[0].imageView = view;
3860 imageInfo[0].imageLayout = VK_IMAGE_LAYOUT_GENERAL;
3861 imageInfoIndex = imageInfos.size();
3862 imageInfos.append(imageInfo);
3863 }
3864 }
3865 break;
3866 case QRhiShaderResourceBinding::BufferLoad:
3867 case QRhiShaderResourceBinding::BufferStore:
3868 case QRhiShaderResourceBinding::BufferLoadStore:
3869 {
3870 QVkBuffer *bufD = QRHI_RES(QVkBuffer, b->u.sbuf.buf);
3871 writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
3872 bd.sbuf.id = bufD->m_id;
3873 bd.sbuf.generation = bufD->generation;
3874 VkDescriptorBufferInfo bufInfo;
3875 bufInfo.buffer = bufD->m_type == QRhiBuffer::Dynamic ? bufD->buffers[currentFrameSlot] : bufD->buffers[0];
3876 bufInfo.offset = b->u.sbuf.offset;
3877 bufInfo.range = b->u.sbuf.maybeSize ? b->u.sbuf.maybeSize : VK_WHOLE_SIZE;
3878 bufferInfoIndex = bufferInfos.size();
3879 bufferInfos.append(bufInfo);
3880 }
3881 break;
3882 default:
3883 continue;
3884 }
3885
3886 writeInfos.append(writeInfo);
3887 infoIndices.append({ bufferInfoIndex, imageInfoIndex });
3888 }
3889
3890 for (int i = 0, writeInfoCount = writeInfos.size(); i < writeInfoCount; ++i) {
3891 const int bufferInfoIndex = infoIndices[i].first;
3892 const int imageInfoIndex = infoIndices[i].second;
3893 if (bufferInfoIndex >= 0)
3894 writeInfos[i].pBufferInfo = &bufferInfos[bufferInfoIndex];
3895 else if (imageInfoIndex >= 0)
3896 writeInfos[i].pImageInfo = imageInfos[imageInfoIndex].constData();
3897 }
3898
3899 df->vkUpdateDescriptorSets(dev, uint32_t(writeInfos.size()), writeInfos.constData(), 0, nullptr);
3900}
3901
3902static inline bool accessIsWrite(VkAccessFlags access)
3903{
3904 return (access & VK_ACCESS_SHADER_WRITE_BIT) != 0
3905 || (access & VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT) != 0
3906 || (access & VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT) != 0
3907 || (access & VK_ACCESS_TRANSFER_WRITE_BIT) != 0
3908 || (access & VK_ACCESS_HOST_WRITE_BIT) != 0
3909 || (access & VK_ACCESS_MEMORY_WRITE_BIT) != 0;
3910}
3911
3913 VkAccessFlags access, VkPipelineStageFlags stage)
3914{
3916 Q_ASSERT(access && stage);
3917 QVkBuffer::UsageState &s(bufD->usageState[slot]);
3918 if (!s.stage) {
3919 s.access = access;
3920 s.stage = stage;
3921 return;
3922 }
3923
3924 if (s.access == access && s.stage == stage) {
3925 // No need to flood with unnecessary read-after-read barriers.
3926 // Write-after-write is a different matter, however.
3927 if (!accessIsWrite(access))
3928 return;
3929 }
3930
3931 VkBufferMemoryBarrier bufMemBarrier = {};
3932 bufMemBarrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
3933 bufMemBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
3934 bufMemBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
3935 bufMemBarrier.srcAccessMask = s.access;
3936 bufMemBarrier.dstAccessMask = access;
3937 bufMemBarrier.buffer = bufD->buffers[slot];
3938 bufMemBarrier.size = VK_WHOLE_SIZE;
3939
3940 QVkCommandBuffer::Command &cmd(cbD->commands.get());
3942 cmd.args.bufferBarrier.srcStageMask = s.stage;
3943 cmd.args.bufferBarrier.dstStageMask = stage;
3944 cmd.args.bufferBarrier.count = 1;
3945 cmd.args.bufferBarrier.index = cbD->pools.bufferBarrier.size();
3946 cbD->pools.bufferBarrier.append(bufMemBarrier);
3947
3948 s.access = access;
3949 s.stage = stage;
3950}
3951
3953 VkImageLayout layout, VkAccessFlags access, VkPipelineStageFlags stage)
3954{
3956 Q_ASSERT(layout && access && stage);
3957 QVkTexture::UsageState &s(texD->usageState);
3958 if (s.access == access && s.stage == stage && s.layout == layout) {
3959 if (!accessIsWrite(access))
3960 return;
3961 }
3962
3963 VkImageMemoryBarrier barrier = {};
3964 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
3965 barrier.subresourceRange.aspectMask = aspectMaskForTextureFormat(texD->m_format);
3966 barrier.subresourceRange.baseMipLevel = 0;
3967 barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
3968 barrier.subresourceRange.baseArrayLayer = 0;
3969 barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
3970 barrier.oldLayout = s.layout; // new textures have this set to PREINITIALIZED
3971 barrier.newLayout = layout;
3972 barrier.srcAccessMask = s.access; // may be 0 but that's fine
3973 barrier.dstAccessMask = access;
3974 barrier.image = texD->image;
3975
3976 VkPipelineStageFlags srcStage = s.stage;
3977 // stage mask cannot be 0
3978 if (!srcStage)
3979 srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
3980
3981 QVkCommandBuffer::Command &cmd(cbD->commands.get());
3983 cmd.args.imageBarrier.srcStageMask = srcStage;
3984 cmd.args.imageBarrier.dstStageMask = stage;
3985 cmd.args.imageBarrier.count = 1;
3986 cmd.args.imageBarrier.index = cbD->pools.imageBarrier.size();
3987 cbD->pools.imageBarrier.append(barrier);
3988
3989 s.layout = layout;
3990 s.access = access;
3991 s.stage = stage;
3992}
3993
3995{
3997
3998 VkImageMemoryBarrier barrier = {};
3999 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
4000 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
4001 barrier.subresourceRange.baseMipLevel = 0;
4002 barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
4003 barrier.subresourceRange.baseArrayLayer = 0;
4004 barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
4005 barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
4006 barrier.newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
4007 barrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
4008 barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT
4009 | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
4010 barrier.image = rbD->image;
4011
4012 const VkPipelineStageFlags stages = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT
4013 | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
4014
4015 QVkCommandBuffer::Command &cmd(cbD->commands.get());
4017 cmd.args.imageBarrier.srcStageMask = stages;
4018 cmd.args.imageBarrier.dstStageMask = stages;
4019 cmd.args.imageBarrier.count = 1;
4020 cmd.args.imageBarrier.index = cbD->pools.imageBarrier.size();
4021 cbD->pools.imageBarrier.append(barrier);
4022}
4023
4025 VkImageLayout oldLayout, VkImageLayout newLayout,
4026 VkAccessFlags srcAccess, VkAccessFlags dstAccess,
4027 VkPipelineStageFlags srcStage, VkPipelineStageFlags dstStage,
4028 int startLayer, int layerCount,
4029 int startLevel, int levelCount)
4030{
4032 VkImageMemoryBarrier barrier = {};
4033 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
4034 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
4035 barrier.subresourceRange.baseMipLevel = uint32_t(startLevel);
4036 barrier.subresourceRange.levelCount = uint32_t(levelCount);
4037 barrier.subresourceRange.baseArrayLayer = uint32_t(startLayer);
4038 barrier.subresourceRange.layerCount = uint32_t(layerCount);
4039 barrier.oldLayout = oldLayout;
4040 barrier.newLayout = newLayout;
4041 barrier.srcAccessMask = srcAccess;
4042 barrier.dstAccessMask = dstAccess;
4043 barrier.image = image;
4044
4045 QVkCommandBuffer::Command &cmd(cbD->commands.get());
4047 cmd.args.imageBarrier.srcStageMask = srcStage;
4048 cmd.args.imageBarrier.dstStageMask = dstStage;
4049 cmd.args.imageBarrier.count = 1;
4050 cmd.args.imageBarrier.index = cbD->pools.imageBarrier.size();
4051 cbD->pools.imageBarrier.append(barrier);
4052}
4053
4054VkDeviceSize QRhiVulkan::subresUploadByteSize(const QRhiTextureSubresourceUploadDescription &subresDesc) const
4055{
4056 VkDeviceSize size = 0;
4057 const qsizetype imageSizeBytes = subresDesc.image().isNull() ?
4058 subresDesc.data().size() : subresDesc.image().sizeInBytes();
4059 if (imageSizeBytes > 0)
4060 size += aligned(VkDeviceSize(imageSizeBytes), texbufAlign);
4061 return size;
4062}
4063
4064void QRhiVulkan::prepareUploadSubres(QVkTexture *texD, int layer, int level,
4065 const QRhiTextureSubresourceUploadDescription &subresDesc,
4066 size_t *curOfs, void *mp,
4067 BufferImageCopyList *copyInfos)
4068{
4069 qsizetype copySizeBytes = 0;
4070 qsizetype imageSizeBytes = 0;
4071 const void *src = nullptr;
4072 const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
4073 const bool is1D = texD->m_flags.testFlag(QRhiTexture::OneDimensional);
4074
4075 VkBufferImageCopy copyInfo = {};
4076 copyInfo.bufferOffset = *curOfs;
4077 copyInfo.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
4078 copyInfo.imageSubresource.mipLevel = uint32_t(level);
4079 copyInfo.imageSubresource.baseArrayLayer = is3D ? 0 : uint32_t(layer);
4080 copyInfo.imageSubresource.layerCount = 1;
4081 copyInfo.imageExtent.depth = 1;
4082 if (is3D)
4083 copyInfo.imageOffset.z = uint32_t(layer);
4084 if (is1D)
4085 copyInfo.imageOffset.y = uint32_t(layer);
4086
4087 const QByteArray rawData = subresDesc.data();
4088 const QPoint dp = subresDesc.destinationTopLeft();
4089 QImage image = subresDesc.image();
4090 if (!image.isNull()) {
4091 copySizeBytes = imageSizeBytes = image.sizeInBytes();
4092 QSize size = image.size();
4093 src = image.constBits();
4094 // Scanlines in QImage are 4 byte aligned so bpl must
4095 // be taken into account for bufferRowLength.
4096 int bpc = qMax(1, image.depth() / 8);
4097 // this is in pixels, not bytes, to make it more complicated...
4098 copyInfo.bufferRowLength = uint32_t(image.bytesPerLine() / bpc);
4099 if (!subresDesc.sourceSize().isEmpty() || !subresDesc.sourceTopLeft().isNull()) {
4100 const int sx = subresDesc.sourceTopLeft().x();
4101 const int sy = subresDesc.sourceTopLeft().y();
4102 if (!subresDesc.sourceSize().isEmpty())
4103 size = subresDesc.sourceSize();
4104 size = clampedSubResourceUploadSize(size, dp, level, texD->m_pixelSize);
4105 if (size.width() == image.width()) {
4106 // No need to make a QImage copy here, can copy from the source
4107 // QImage into staging directly.
4108 src = image.constBits() + sy * image.bytesPerLine() + sx * bpc;
4109 copySizeBytes = size.height() * image.bytesPerLine();
4110 } else {
4111 image = image.copy(sx, sy, size.width(), size.height());
4112 src = image.constBits();
4113 // The staging buffer gets the slice only. The rest of the
4114 // space reserved for this mip will be unused.
4115 copySizeBytes = image.sizeInBytes();
4116 bpc = qMax(1, image.depth() / 8);
4117 copyInfo.bufferRowLength = uint32_t(image.bytesPerLine() / bpc);
4118 }
4119 } else {
4120 size = clampedSubResourceUploadSize(size, dp, level, texD->m_pixelSize);
4121 }
4122 copyInfo.imageOffset.x = dp.x();
4123 copyInfo.imageOffset.y = dp.y();
4124 copyInfo.imageExtent.width = uint32_t(size.width());
4125 copyInfo.imageExtent.height = uint32_t(size.height());
4126 copyInfos->append(copyInfo);
4127 } else if (!rawData.isEmpty() && isCompressedFormat(texD->m_format)) {
4128 copySizeBytes = imageSizeBytes = rawData.size();
4129 src = rawData.constData();
4130 QSize size = q->sizeForMipLevel(level, texD->m_pixelSize);
4131 const int subresw = size.width();
4132 const int subresh = size.height();
4133 if (!subresDesc.sourceSize().isEmpty())
4134 size = subresDesc.sourceSize();
4135 const int w = size.width();
4136 const int h = size.height();
4137 QSize blockDim;
4138 compressedFormatInfo(texD->m_format, QSize(w, h), nullptr, nullptr, &blockDim);
4139 // x and y must be multiples of the block width and height
4140 copyInfo.imageOffset.x = aligned(dp.x(), blockDim.width());
4141 copyInfo.imageOffset.y = aligned(dp.y(), blockDim.height());
4142 // width and height must be multiples of the block width and height
4143 // or x + width and y + height must equal the subresource width and height
4144 copyInfo.imageExtent.width = uint32_t(dp.x() + w == subresw ? w : aligned(w, blockDim.width()));
4145 copyInfo.imageExtent.height = uint32_t(dp.y() + h == subresh ? h : aligned(h, blockDim.height()));
4146 copyInfos->append(copyInfo);
4147 } else if (!rawData.isEmpty()) {
4148 copySizeBytes = imageSizeBytes = rawData.size();
4149 src = rawData.constData();
4150 QSize size = q->sizeForMipLevel(level, texD->m_pixelSize);
4151 if (subresDesc.dataStride()) {
4152 quint32 bytesPerPixel = 0;
4153 textureFormatInfo(texD->m_format, size, nullptr, nullptr, &bytesPerPixel);
4154 if (bytesPerPixel)
4155 copyInfo.bufferRowLength = subresDesc.dataStride() / bytesPerPixel;
4156 }
4157 if (!subresDesc.sourceSize().isEmpty())
4158 size = subresDesc.sourceSize();
4159 copyInfo.imageOffset.x = dp.x();
4160 copyInfo.imageOffset.y = dp.y();
4161 copyInfo.imageExtent.width = uint32_t(size.width());
4162 copyInfo.imageExtent.height = uint32_t(size.height());
4163 copyInfos->append(copyInfo);
4164 } else {
4165 qWarning("Invalid texture upload for %p layer=%d mip=%d", texD, layer, level);
4166 }
4167
4168 if (src) {
4169 memcpy(reinterpret_cast<char *>(mp) + *curOfs, src, size_t(copySizeBytes));
4170 *curOfs += aligned(VkDeviceSize(imageSizeBytes), texbufAlign);
4171 }
4172}
4173
4175{
4176 if (err == VK_ERROR_DEVICE_LOST)
4178 if (err == VK_ERROR_OUT_OF_DEVICE_MEMORY)
4179 qWarning() << "Out of device memory, current allocator statistics are" << statistics();
4180}
4181
4183{
4184#ifdef VK_EXT_device_fault
4185 if (!dev || !caps.deviceFault || !vkGetDeviceFaultInfoEXT)
4186 return;
4187
4188 VkDeviceFaultCountsEXT faultCounts{};
4189 faultCounts.sType = VK_STRUCTURE_TYPE_DEVICE_FAULT_COUNTS_EXT;
4190 faultCounts.pNext = nullptr;
4191
4192 VkResult result = vkGetDeviceFaultInfoEXT(dev, &faultCounts, nullptr);
4193 if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
4194 qWarning("vkGetDeviceFaultInfoEXT failed with %d", result);
4195 return;
4196 }
4197 faultCounts.vendorBinarySize = 0;
4198
4199 QVarLengthArray<VkDeviceFaultAddressInfoEXT> addressInfos;
4200 addressInfos.resize(faultCounts.addressInfoCount);
4201
4202 QVarLengthArray<VkDeviceFaultVendorInfoEXT> vendorInfos;
4203 vendorInfos.resize(faultCounts.vendorInfoCount);
4204
4205 VkDeviceFaultInfoEXT info{};
4206 info.sType = VK_STRUCTURE_TYPE_DEVICE_FAULT_INFO_EXT;
4207 info.pNext = nullptr;
4208 info.pAddressInfos = addressInfos.isEmpty() ? nullptr : addressInfos.data();
4209 info.pVendorInfos = vendorInfos.isEmpty() ? nullptr : vendorInfos.data();
4210 info.pVendorBinaryData = nullptr;
4211
4212 result = vkGetDeviceFaultInfoEXT(dev, &faultCounts, &info);
4213 if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
4214 qWarning("vkGetDeviceFaultInfoEXT failed with %d", result);
4215 return;
4216 }
4217
4218 const char *desc = info.description[0] ? info.description : "n/a";
4219 qWarning("VK_ERROR_DEVICE_LOST (VK_EXT_device_fault): %u address infos, %u vendor infos, %llu bytes vendor binary: %s",
4220 faultCounts.addressInfoCount,
4221 faultCounts.vendorInfoCount,
4222 (unsigned long long)faultCounts.vendorBinarySize,
4223 desc);
4224
4225 for (uint32_t i = 0; i < faultCounts.addressInfoCount; ++i) {
4226 const auto &a = addressInfos[i];
4227 auto addressTypeString = [](const VkDeviceFaultAddressTypeEXT type) {
4228 switch (type) {
4229 case VK_DEVICE_FAULT_ADDRESS_TYPE_NONE_EXT: return "NONE";
4230 case VK_DEVICE_FAULT_ADDRESS_TYPE_READ_INVALID_EXT: return "READ_INVALID";
4231 case VK_DEVICE_FAULT_ADDRESS_TYPE_WRITE_INVALID_EXT: return "WRITE_INVALID";
4232 case VK_DEVICE_FAULT_ADDRESS_TYPE_EXECUTE_INVALID_EXT: return "EXECUTE_INVALID";
4233 case VK_DEVICE_FAULT_ADDRESS_TYPE_INSTRUCTION_POINTER_UNKNOWN_EXT: return "INSTRUCTION_POINTER_UNKNOWN";
4234 case VK_DEVICE_FAULT_ADDRESS_TYPE_INSTRUCTION_POINTER_INVALID_EXT: return "INSTRUCTION_POINTER_INVALID";
4235 case VK_DEVICE_FAULT_ADDRESS_TYPE_INSTRUCTION_POINTER_FAULT_EXT: return "INSTRUCTION_POINTER_FAULT";
4236 default: return "UNKNOWN";
4237 };
4238 };
4239 qWarning(" AddressInfo[%02u]: type=%s addr=0x%llx precision=%llu",
4240 i,
4241 addressTypeString(a.addressType),
4242 (unsigned long long)a.reportedAddress,
4243 (unsigned long long)a.addressPrecision);
4244 }
4245
4246 for (uint32_t i = 0; i < faultCounts.vendorInfoCount; ++i) {
4247 const auto &v = vendorInfos[i];
4248 qWarning(" VendorInfo[%02u]: code=%llu data=%llu desc=%s",
4249 i,
4250 (unsigned long long)v.vendorFaultCode,
4251 (unsigned long long)v.vendorFaultData,
4252 v.description);
4253 }
4254#endif // VK_EXT_device_fault
4255}
4256
4257void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdateBatch *resourceUpdates)
4258{
4260
4261 for (int opIdx = 0; opIdx < ud->activeBufferOpCount; ++opIdx) {
4262 const QRhiResourceUpdateBatchPrivate::BufferOp &u(ud->bufferOps[opIdx]);
4264 QVkBuffer *bufD = QRHI_RES(QVkBuffer, u.buf);
4265 Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
4266 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
4267 if (u.offset == 0 && u.data.size() == bufD->m_size)
4268 bufD->pendingDynamicUpdates[i].clear();
4269 bufD->pendingDynamicUpdates[i].append({ u.offset, u.data });
4270 }
4272 QVkBuffer *bufD = QRHI_RES(QVkBuffer, u.buf);
4273 Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic);
4274 Q_ASSERT(u.offset + u.data.size() <= bufD->m_size);
4275
4276 if (!bufD->stagingBuffers[currentFrameSlot]) {
4277 VkBufferCreateInfo bufferInfo = {};
4278 bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
4279 // must cover the entire buffer - this way multiple, partial updates per frame
4280 // are supported even when the staging buffer is reused (Static)
4281 bufferInfo.size = bufD->m_size;
4282 bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
4283
4284 VmaAllocationCreateInfo allocInfo = {};
4285 allocInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
4286
4287 VmaAllocation allocation;
4288 VkResult err = vmaCreateBuffer(toVmaAllocator(allocator), &bufferInfo, &allocInfo,
4289 &bufD->stagingBuffers[currentFrameSlot], &allocation, nullptr);
4290 if (err == VK_SUCCESS) {
4291 bufD->stagingAllocations[currentFrameSlot] = allocation;
4292 setAllocationName(allocation, bufD->name());
4293 } else {
4294 qWarning("Failed to create staging buffer of size %u: %d", bufD->m_size, err);
4295 printExtraErrorInfo(err);
4296 continue;
4297 }
4298 }
4299
4300 VkResult err = vmaCopyMemoryToAllocation(toVmaAllocator(allocator), u.data.constData(),
4301 toVmaAllocation(bufD->stagingAllocations[currentFrameSlot]),
4302 u.offset, u.data.size());
4303 if (err != VK_SUCCESS) {
4304 qWarning("Failed to copy memory to buffer: %d", err);
4305 continue;
4306 }
4307
4308 trackedBufferBarrier(cbD, bufD, 0,
4309 VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
4310
4311 VkBufferCopy copyInfo = {};
4312 copyInfo.srcOffset = u.offset;
4313 copyInfo.dstOffset = u.offset;
4314 copyInfo.size = u.data.size();
4315
4316 QVkCommandBuffer::Command &cmd(cbD->commands.get());
4318 cmd.args.copyBuffer.src = bufD->stagingBuffers[currentFrameSlot];
4319 cmd.args.copyBuffer.dst = bufD->buffers[0];
4320 cmd.args.copyBuffer.desc = copyInfo;
4321
4322 // Where's the barrier for read-after-write? (assuming the common case
4323 // of binding this buffer as vertex/index, or, less likely, as uniform
4324 // buffer, in a renderpass later on) That is handled by the pass
4325 // resource tracking: the appropriate pipeline barrier will be
4326 // generated and recorded right before the renderpass, that binds this
4327 // buffer in one of its commands, gets its BeginRenderPass recorded.
4328
4329 bufD->lastActiveFrameSlot = currentFrameSlot;
4330
4331 if (bufD->m_type == QRhiBuffer::Immutable) {
4334 e.lastActiveFrameSlot = currentFrameSlot;
4335 e.stagingBuffer.stagingBuffer = bufD->stagingBuffers[currentFrameSlot];
4336 e.stagingBuffer.stagingAllocation = bufD->stagingAllocations[currentFrameSlot];
4337 bufD->stagingBuffers[currentFrameSlot] = VK_NULL_HANDLE;
4338 bufD->stagingAllocations[currentFrameSlot] = nullptr;
4339 releaseQueue.append(e);
4340 }
4342 QVkBuffer *bufD = QRHI_RES(QVkBuffer, u.buf);
4343 if (bufD->m_type == QRhiBuffer::Dynamic) {
4344 executeBufferHostWritesForSlot(bufD, currentFrameSlot);
4345 u.result->data.resizeForOverwrite(u.readSize);
4346 VkResult err = vmaCopyAllocationToMemory(toVmaAllocator(allocator),
4347 toVmaAllocation(bufD->allocations[currentFrameSlot]),
4348 u.offset, u.result->data.data(), u.readSize);
4349 if (err != VK_SUCCESS) {
4350 qWarning("Failed to copy memory from buffer: %d", err);
4351 u.result->data.clear();
4352 }
4353 if (u.result->completed)
4354 u.result->completed();
4355 } else {
4356 // Non-Dynamic buffers may not be host visible, so have to
4357 // create a readback buffer, enqueue a copy from
4358 // bufD->buffers[0] to this buffer, and then once the command
4359 // buffer completes, copy the data out of the host visible
4360 // readback buffer. Quite similar to what we do for texture
4361 // readbacks.
4362 BufferReadback readback;
4363 readback.activeFrameSlot = currentFrameSlot;
4364 readback.result = u.result;
4365 readback.byteSize = u.readSize;
4366
4367 VkBufferCreateInfo bufferInfo = {};
4368 bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
4369 bufferInfo.size = readback.byteSize;
4370 bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
4371
4372 VmaAllocationCreateInfo allocInfo = {};
4373 allocInfo.usage = VMA_MEMORY_USAGE_GPU_TO_CPU;
4374
4375 VmaAllocation allocation;
4376 VkResult err = vmaCreateBuffer(toVmaAllocator(allocator), &bufferInfo, &allocInfo, &readback.stagingBuf, &allocation, nullptr);
4377 if (err == VK_SUCCESS) {
4378 readback.stagingAlloc = allocation;
4379 setAllocationName(allocation, bufD->name());
4380 } else {
4381 qWarning("Failed to create readback buffer of size %u: %d", readback.byteSize, err);
4382 printExtraErrorInfo(err);
4383 continue;
4384 }
4385
4386 trackedBufferBarrier(cbD, bufD, 0, VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
4387
4388 VkBufferCopy copyInfo = {};
4389 copyInfo.srcOffset = u.offset;
4390 copyInfo.size = u.readSize;
4391
4392 QVkCommandBuffer::Command &cmd(cbD->commands.get());
4394 cmd.args.copyBuffer.src = bufD->buffers[0];
4395 cmd.args.copyBuffer.dst = readback.stagingBuf;
4396 cmd.args.copyBuffer.desc = copyInfo;
4397
4398 bufD->lastActiveFrameSlot = currentFrameSlot;
4399
4400 activeBufferReadbacks.append(readback);
4401 }
4402 }
4403 }
4404
4405 for (int opIdx = 0; opIdx < ud->activeTextureOpCount; ++opIdx) {
4406 const QRhiResourceUpdateBatchPrivate::TextureOp &u(ud->textureOps[opIdx]);
4408 QVkTexture *utexD = QRHI_RES(QVkTexture, u.dst);
4409 // batch into a single staging buffer and a single CopyBufferToImage with multiple copyInfos
4410 VkDeviceSize stagingSize = 0;
4411 for (int layer = 0, maxLayer = u.subresDesc.size(); layer < maxLayer; ++layer) {
4412 for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
4413 for (const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level]))
4414 stagingSize += subresUploadByteSize(subresDesc);
4415 }
4416 }
4417
4418 Q_ASSERT(!utexD->stagingBuffers[currentFrameSlot]);
4419 VkBufferCreateInfo bufferInfo = {};
4420 bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
4421 bufferInfo.size = stagingSize;
4422 bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
4423
4424 VmaAllocationCreateInfo allocInfo = {};
4425 allocInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
4426
4427 VmaAllocation allocation;
4428 VkResult err = vmaCreateBuffer(toVmaAllocator(allocator), &bufferInfo, &allocInfo,
4429 &utexD->stagingBuffers[currentFrameSlot], &allocation, nullptr);
4430 if (err != VK_SUCCESS) {
4431 qWarning("Failed to create image staging buffer of size %d: %d", int(stagingSize), err);
4432 printExtraErrorInfo(err);
4433 continue;
4434 }
4435 utexD->stagingAllocations[currentFrameSlot] = allocation;
4436 setAllocationName(allocation, utexD->name());
4437
4438 BufferImageCopyList copyInfos;
4439 size_t curOfs = 0;
4440 void *mp = nullptr;
4441 VmaAllocation a = toVmaAllocation(utexD->stagingAllocations[currentFrameSlot]);
4442 err = vmaMapMemory(toVmaAllocator(allocator), a, &mp);
4443 if (err != VK_SUCCESS) {
4444 qWarning("Failed to map image data: %d", err);
4445 continue;
4446 }
4447
4448 for (int layer = 0, maxLayer = u.subresDesc.size(); layer < maxLayer; ++layer) {
4449 for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
4450 const QList<QRhiTextureSubresourceUploadDescription> &srd(u.subresDesc[layer][level]);
4451 if (srd.isEmpty())
4452 continue;
4453 for (const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(srd)) {
4454 prepareUploadSubres(utexD, layer, level,
4455 subresDesc, &curOfs, mp, &copyInfos);
4456 }
4457 }
4458 }
4459 vmaFlushAllocation(toVmaAllocator(allocator), a, 0, stagingSize);
4460 vmaUnmapMemory(toVmaAllocator(allocator), a);
4461
4462 trackedImageBarrier(cbD, utexD, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
4463 VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
4464
4465 QVkCommandBuffer::Command &cmd(cbD->commands.get());
4467 cmd.args.copyBufferToImage.src = utexD->stagingBuffers[currentFrameSlot];
4468 cmd.args.copyBufferToImage.dst = utexD->image;
4469 cmd.args.copyBufferToImage.dstLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
4470 cmd.args.copyBufferToImage.count = copyInfos.size();
4471 cmd.args.copyBufferToImage.bufferImageCopyIndex = cbD->pools.bufferImageCopy.size();
4472 cbD->pools.bufferImageCopy.append(copyInfos.constData(), copyInfos.size());
4473
4474 // no reuse of staging, this is intentional
4477 e.lastActiveFrameSlot = currentFrameSlot;
4478 e.stagingBuffer.stagingBuffer = utexD->stagingBuffers[currentFrameSlot];
4479 e.stagingBuffer.stagingAllocation = utexD->stagingAllocations[currentFrameSlot];
4480 utexD->stagingBuffers[currentFrameSlot] = VK_NULL_HANDLE;
4481 utexD->stagingAllocations[currentFrameSlot] = nullptr;
4482 releaseQueue.append(e);
4483
4484 // Similarly to buffers, transitioning away from DST is done later,
4485 // when a renderpass using the texture is encountered.
4486
4487 utexD->lastActiveFrameSlot = currentFrameSlot;
4489 Q_ASSERT(u.src && u.dst);
4490 if (u.src == u.dst) {
4491 qWarning("Texture copy with matching source and destination is not supported");
4492 continue;
4493 }
4494 QVkTexture *srcD = QRHI_RES(QVkTexture, u.src);
4495 QVkTexture *dstD = QRHI_RES(QVkTexture, u.dst);
4496 const bool srcIs3D = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
4497 const bool dstIs3D = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
4498
4499 VkImageCopy region = {};
4500 region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
4501 region.srcSubresource.mipLevel = uint32_t(u.desc.sourceLevel());
4502 region.srcSubresource.baseArrayLayer = srcIs3D ? 0 : uint32_t(u.desc.sourceLayer());
4503 region.srcSubresource.layerCount = 1;
4504
4505 region.srcOffset.x = u.desc.sourceTopLeft().x();
4506 region.srcOffset.y = u.desc.sourceTopLeft().y();
4507 if (srcIs3D)
4508 region.srcOffset.z = uint32_t(u.desc.sourceLayer());
4509
4510 region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
4511 region.dstSubresource.mipLevel = uint32_t(u.desc.destinationLevel());
4512 region.dstSubresource.baseArrayLayer = dstIs3D ? 0 : uint32_t(u.desc.destinationLayer());
4513 region.dstSubresource.layerCount = 1;
4514
4515 region.dstOffset.x = u.desc.destinationTopLeft().x();
4516 region.dstOffset.y = u.desc.destinationTopLeft().y();
4517 if (dstIs3D)
4518 region.dstOffset.z = uint32_t(u.desc.destinationLayer());
4519
4520 const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize);
4521 const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize();
4522 region.extent.width = uint32_t(copySize.width());
4523 region.extent.height = uint32_t(copySize.height());
4524 region.extent.depth = 1;
4525
4526 trackedImageBarrier(cbD, srcD, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
4527 VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
4528 trackedImageBarrier(cbD, dstD, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
4529 VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
4530
4531 QVkCommandBuffer::Command &cmd(cbD->commands.get());
4533 cmd.args.copyImage.src = srcD->image;
4534 cmd.args.copyImage.srcLayout = srcD->usageState.layout;
4535 cmd.args.copyImage.dst = dstD->image;
4536 cmd.args.copyImage.dstLayout = dstD->usageState.layout;
4537 cmd.args.copyImage.desc = region;
4538
4539 srcD->lastActiveFrameSlot = dstD->lastActiveFrameSlot = currentFrameSlot;
4541 TextureReadback readback;
4542 readback.activeFrameSlot = currentFrameSlot;
4543 readback.desc = u.rb;
4544 readback.result = u.result;
4545
4546 QVkTexture *texD = QRHI_RES(QVkTexture, u.rb.texture());
4547 QVkSwapChain *swapChainD = nullptr;
4548 bool is3D = false;
4549 if (texD) {
4550 if (texD->samples > VK_SAMPLE_COUNT_1_BIT) {
4551 qWarning("Multisample texture cannot be read back");
4552 continue;
4553 }
4554 is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
4555 if (u.rb.rect().isValid())
4556 readback.rect = u.rb.rect();
4557 else
4558 readback.rect = QRect({0, 0}, q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize));
4559 readback.format = texD->m_format;
4560 texD->lastActiveFrameSlot = currentFrameSlot;
4561 } else {
4562 Q_ASSERT(currentSwapChain);
4563 swapChainD = QRHI_RES(QVkSwapChain, currentSwapChain);
4564 if (!swapChainD->supportsReadback) {
4565 qWarning("Swapchain does not support readback");
4566 continue;
4567 }
4568 if (u.rb.rect().isValid())
4569 readback.rect = u.rb.rect();
4570 else
4571 readback.rect = QRect({0, 0}, swapChainD->pixelSize);
4572 readback.format = swapchainReadbackTextureFormat(swapChainD->colorFormat, nullptr);
4573 if (readback.format == QRhiTexture::UnknownFormat)
4574 continue;
4575
4576 // Multisample swapchains need nothing special since resolving
4577 // happens when ending a renderpass.
4578 }
4579 textureFormatInfo(readback.format, readback.rect.size(), nullptr, &readback.byteSize, nullptr);
4580
4581 // Create a host visible readback buffer.
4582 VkBufferCreateInfo bufferInfo = {};
4583 bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
4584 bufferInfo.size = readback.byteSize;
4585 bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
4586
4587 VmaAllocationCreateInfo allocInfo = {};
4588 allocInfo.usage = VMA_MEMORY_USAGE_GPU_TO_CPU;
4589
4590 VmaAllocation allocation;
4591 VkResult err = vmaCreateBuffer(toVmaAllocator(allocator), &bufferInfo, &allocInfo, &readback.stagingBuf, &allocation, nullptr);
4592 if (err == VK_SUCCESS) {
4593 readback.stagingAlloc = allocation;
4594 setAllocationName(allocation, texD ? texD->name() : swapChainD->name());
4595 } else {
4596 qWarning("Failed to create readback buffer of size %u: %d", readback.byteSize, err);
4597 printExtraErrorInfo(err);
4598 continue;
4599 }
4600
4601 // Copy from the (optimal and not host visible) image into the buffer.
4602 VkBufferImageCopy copyDesc = {};
4603 copyDesc.bufferOffset = 0;
4604 copyDesc.imageSubresource.aspectMask = aspectMaskForTextureFormat(readback.format);
4605 copyDesc.imageSubresource.mipLevel = uint32_t(u.rb.level());
4606 copyDesc.imageSubresource.baseArrayLayer = is3D ? 0 : uint32_t(u.rb.layer());
4607 copyDesc.imageSubresource.layerCount = 1;
4608 copyDesc.imageOffset.x = readback.rect.x();
4609 copyDesc.imageOffset.y = readback.rect.y();
4610 if (is3D)
4611 copyDesc.imageOffset.z = u.rb.layer();
4612 copyDesc.imageExtent.width = uint32_t(readback.rect.width());
4613 copyDesc.imageExtent.height = uint32_t(readback.rect.height());
4614 copyDesc.imageExtent.depth = 1;
4615
4616 if (texD) {
4617 trackedImageBarrier(cbD, texD, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
4618 VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
4619 QVkCommandBuffer::Command &cmd(cbD->commands.get());
4621 cmd.args.copyImageToBuffer.src = texD->image;
4622 cmd.args.copyImageToBuffer.srcLayout = texD->usageState.layout;
4623 cmd.args.copyImageToBuffer.dst = readback.stagingBuf;
4624 cmd.args.copyImageToBuffer.desc = copyDesc;
4625 } else {
4626 // use the swapchain image
4627 QVkSwapChain::ImageResources &imageRes(swapChainD->imageRes[swapChainD->currentImageIndex]);
4628 VkImage image = imageRes.image;
4631 qWarning("Attempted to read back undefined swapchain image content, "
4632 "results are undefined. (do a render pass first)");
4633 }
4634 subresourceBarrier(cbD, image,
4635 VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
4636 VK_ACCESS_MEMORY_READ_BIT, VK_ACCESS_TRANSFER_READ_BIT,
4637 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
4638 0, 1,
4639 0, 1);
4641 }
4642
4643 QVkCommandBuffer::Command &cmd(cbD->commands.get());
4645 cmd.args.copyImageToBuffer.src = image;
4646 cmd.args.copyImageToBuffer.srcLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
4647 cmd.args.copyImageToBuffer.dst = readback.stagingBuf;
4648 cmd.args.copyImageToBuffer.desc = copyDesc;
4649 }
4650
4651 activeTextureReadbacks.append(readback);
4653 QVkTexture *utexD = QRHI_RES(QVkTexture, u.dst);
4654 Q_ASSERT(utexD->m_flags.testFlag(QRhiTexture::UsedWithGenerateMips));
4655 const bool isCube = utexD->m_flags.testFlag(QRhiTexture::CubeMap);
4656 const bool isArray = utexD->m_flags.testFlag(QRhiTexture::TextureArray);
4657 const bool is3D = utexD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
4658
4659 VkImageLayout origLayout = utexD->usageState.layout;
4660 VkAccessFlags origAccess = utexD->usageState.access;
4661 VkPipelineStageFlags origStage = utexD->usageState.stage;
4662 if (!origStage)
4663 origStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
4664
4665 for (int layer = 0; layer < (isCube ? 6 : (isArray ? qMax(0, utexD->m_arraySize) : 1)); ++layer) {
4666 int w = utexD->m_pixelSize.width();
4667 int h = utexD->m_pixelSize.height();
4668 int depth = is3D ? qMax(1, utexD->m_depth) : 1;
4669 for (int level = 1; level < int(utexD->mipLevelCount); ++level) {
4670 if (level == 1) {
4671 subresourceBarrier(cbD, utexD->image,
4672 origLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
4673 origAccess, VK_ACCESS_TRANSFER_READ_BIT,
4674 origStage, VK_PIPELINE_STAGE_TRANSFER_BIT,
4675 layer, 1,
4676 level - 1, 1);
4677 } else {
4678 subresourceBarrier(cbD, utexD->image,
4679 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
4680 VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
4681 VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
4682 layer, 1,
4683 level - 1, 1);
4684 }
4685
4686 subresourceBarrier(cbD, utexD->image,
4687 origLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
4688 origAccess, VK_ACCESS_TRANSFER_WRITE_BIT,
4689 origStage, VK_PIPELINE_STAGE_TRANSFER_BIT,
4690 layer, 1,
4691 level, 1);
4692
4693 VkImageBlit region = {};
4694 region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
4695 region.srcSubresource.mipLevel = uint32_t(level) - 1;
4696 region.srcSubresource.baseArrayLayer = uint32_t(layer);
4697 region.srcSubresource.layerCount = 1;
4698
4699 region.srcOffsets[1].x = qMax(1, w);
4700 region.srcOffsets[1].y = qMax(1, h);
4701 region.srcOffsets[1].z = qMax(1, depth);
4702
4703 region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
4704 region.dstSubresource.mipLevel = uint32_t(level);
4705 region.dstSubresource.baseArrayLayer = uint32_t(layer);
4706 region.dstSubresource.layerCount = 1;
4707
4708 region.dstOffsets[1].x = qMax(1, w >> 1);
4709 region.dstOffsets[1].y = qMax(1, h >> 1);
4710 region.dstOffsets[1].z = qMax(1, depth >> 1);
4711
4712 QVkCommandBuffer::Command &cmd(cbD->commands.get());
4714 cmd.args.blitImage.src = utexD->image;
4715 cmd.args.blitImage.srcLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
4716 cmd.args.blitImage.dst = utexD->image;
4717 cmd.args.blitImage.dstLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
4718 cmd.args.blitImage.filter = VK_FILTER_LINEAR;
4719 cmd.args.blitImage.desc = region;
4720
4721 w >>= 1;
4722 h >>= 1;
4723 depth >>= 1;
4724 }
4725
4726 if (utexD->mipLevelCount > 1) {
4727 subresourceBarrier(cbD, utexD->image,
4728 VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, origLayout,
4729 VK_ACCESS_TRANSFER_READ_BIT, origAccess,
4730 VK_PIPELINE_STAGE_TRANSFER_BIT, origStage,
4731 layer, 1,
4732 0, int(utexD->mipLevelCount) - 1);
4733 subresourceBarrier(cbD, utexD->image,
4734 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, origLayout,
4735 VK_ACCESS_TRANSFER_WRITE_BIT, origAccess,
4736 VK_PIPELINE_STAGE_TRANSFER_BIT, origStage,
4737 layer, 1,
4738 int(utexD->mipLevelCount) - 1, 1);
4739 }
4740 }
4741 utexD->lastActiveFrameSlot = currentFrameSlot;
4742 }
4743 }
4744
4745 ud->free();
4746}
4747
4749{
4750 if (bufD->pendingDynamicUpdates[slot].isEmpty())
4751 return;
4752
4753 Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
4754 void *p = nullptr;
4755 VmaAllocation a = toVmaAllocation(bufD->allocations[slot]);
4756 // The vmaMap/Unmap are basically a no-op when persistently mapped since it
4757 // refcounts; this is great because we don't need to care if the allocation
4758 // was created as persistently mapped or not.
4759 VkResult err = vmaMapMemory(toVmaAllocator(allocator), a, &p);
4760 if (err != VK_SUCCESS) {
4761 qWarning("Failed to map buffer: %d", err);
4762 return;
4763 }
4764 quint32 changeBegin = UINT32_MAX;
4765 quint32 changeEnd = 0;
4766 for (const QVkBuffer::DynamicUpdate &u : std::as_const(bufD->pendingDynamicUpdates[slot])) {
4767 memcpy(static_cast<char *>(p) + u.offset, u.data.constData(), u.data.size());
4768 if (u.offset < changeBegin)
4769 changeBegin = u.offset;
4770 if (u.offset + u.data.size() > changeEnd)
4771 changeEnd = u.offset + u.data.size();
4772 }
4773 if (changeBegin < UINT32_MAX && changeBegin < changeEnd)
4774 vmaFlushAllocation(toVmaAllocator(allocator), a, changeBegin, changeEnd - changeBegin);
4775 vmaUnmapMemory(toVmaAllocator(allocator), a);
4776
4777 bufD->pendingDynamicUpdates[slot].clear();
4778}
4779
4780static void qrhivk_releaseBuffer(const QRhiVulkan::DeferredReleaseEntry &e, void *allocator)
4781{
4782 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
4783 vmaDestroyBuffer(toVmaAllocator(allocator), e.buffer.buffers[i], toVmaAllocation(e.buffer.allocations[i]));
4784 vmaDestroyBuffer(toVmaAllocator(allocator), e.buffer.stagingBuffers[i], toVmaAllocation(e.buffer.stagingAllocations[i]));
4785 }
4786}
4787
4788static void qrhivk_releaseRenderBuffer(const QRhiVulkan::DeferredReleaseEntry &e, VkDevice dev, QVulkanDeviceFunctions *df)
4789{
4790 df->vkDestroyImageView(dev, e.renderBuffer.imageView, nullptr);
4791 df->vkDestroyImage(dev, e.renderBuffer.image, nullptr);
4792 df->vkFreeMemory(dev, e.renderBuffer.memory, nullptr);
4793}
4794
4795static void qrhivk_releaseTexture(const QRhiVulkan::DeferredReleaseEntry &e, VkDevice dev, QVulkanDeviceFunctions *df, void *allocator)
4796{
4797 df->vkDestroyImageView(dev, e.texture.imageView, nullptr);
4798 vmaDestroyImage(toVmaAllocator(allocator), e.texture.image, toVmaAllocation(e.texture.allocation));
4799 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
4800 vmaDestroyBuffer(toVmaAllocator(allocator), e.texture.stagingBuffers[i], toVmaAllocation(e.texture.stagingAllocations[i]));
4801 for (int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i) {
4802 if (e.texture.extraImageViews[i])
4803 df->vkDestroyImageView(dev, e.texture.extraImageViews[i], nullptr);
4804 }
4805}
4806
4807static void qrhivk_releaseSampler(const QRhiVulkan::DeferredReleaseEntry &e, VkDevice dev, QVulkanDeviceFunctions *df)
4808{
4809 df->vkDestroySampler(dev, e.sampler.sampler, nullptr);
4810}
4811
4813{
4814 for (int i = releaseQueue.size() - 1; i >= 0; --i) {
4815 const QRhiVulkan::DeferredReleaseEntry &e(releaseQueue[i]);
4816 if (forced || currentFrameSlot == e.lastActiveFrameSlot || e.lastActiveFrameSlot < 0) {
4817 switch (e.type) {
4819 df->vkDestroyPipeline(dev, e.pipelineState.pipeline, nullptr);
4820 df->vkDestroyPipelineLayout(dev, e.pipelineState.layout, nullptr);
4821 break;
4823 df->vkDestroyDescriptorSetLayout(dev, e.shaderResourceBindings.layout, nullptr);
4824 if (e.shaderResourceBindings.poolIndex >= 0) {
4825 descriptorPools[e.shaderResourceBindings.poolIndex].refCount -= 1;
4826 Q_ASSERT(descriptorPools[e.shaderResourceBindings.poolIndex].refCount >= 0);
4827 }
4828 break;
4831 break;
4833 qrhivk_releaseRenderBuffer(e, dev, df);
4834 break;
4836 qrhivk_releaseTexture(e, dev, df, allocator);
4837 break;
4839 qrhivk_releaseSampler(e, dev, df);
4840 break;
4842 df->vkDestroyFramebuffer(dev, e.textureRenderTarget.fb, nullptr);
4843 for (int att = 0; att < QVkRenderTargetData::MAX_COLOR_ATTACHMENTS; ++att) {
4844 df->vkDestroyImageView(dev, e.textureRenderTarget.rtv[att], nullptr);
4845 df->vkDestroyImageView(dev, e.textureRenderTarget.resrtv[att], nullptr);
4846 }
4847 df->vkDestroyImageView(dev, e.textureRenderTarget.dsv, nullptr);
4848 df->vkDestroyImageView(dev, e.textureRenderTarget.resdsv, nullptr);
4849 df->vkDestroyImageView(dev, e.textureRenderTarget.shadingRateMapView, nullptr);
4850 break;
4852 df->vkDestroyRenderPass(dev, e.renderPass.rp, nullptr);
4853 break;
4855 vmaDestroyBuffer(toVmaAllocator(allocator), e.stagingBuffer.stagingBuffer, toVmaAllocation(e.stagingBuffer.stagingAllocation));
4856 break;
4857 case QRhiVulkan::DeferredReleaseEntry::SecondaryCommandBuffer:
4858 freeSecondaryCbs[e.lastActiveFrameSlot].append(e.secondaryCommandBuffer.cb);
4859 break;
4860 default:
4861 Q_UNREACHABLE();
4862 break;
4863 }
4864 releaseQueue.removeAt(i);
4865 }
4866 }
4867}
4868
4870{
4871 QVarLengthArray<std::function<void()>, 4> completedCallbacks;
4872
4873 for (int i = activeTextureReadbacks.size() - 1; i >= 0; --i) {
4874 const QRhiVulkan::TextureReadback &readback(activeTextureReadbacks[i]);
4875 if (forced || currentFrameSlot == readback.activeFrameSlot || readback.activeFrameSlot < 0) {
4876 readback.result->format = readback.format;
4877 readback.result->pixelSize = readback.rect.size();
4878 readback.result->data.resizeForOverwrite(readback.byteSize);
4879 VkResult err = vmaCopyAllocationToMemory(toVmaAllocator(allocator),
4880 toVmaAllocation(readback.stagingAlloc),
4881 0, readback.result->data.data(), readback.byteSize);
4882 if (err != VK_SUCCESS) {
4883 qWarning("Failed to copy texture readback buffer of size %u: %d", readback.byteSize, err);
4884 readback.result->data.clear();
4885 }
4886
4887 vmaDestroyBuffer(toVmaAllocator(allocator), readback.stagingBuf, toVmaAllocation(readback.stagingAlloc));
4888
4889 if (readback.result->completed)
4890 completedCallbacks.append(readback.result->completed);
4891
4892 activeTextureReadbacks.remove(i);
4893 }
4894 }
4895
4896 for (int i = activeBufferReadbacks.size() - 1; i >= 0; --i) {
4897 const QRhiVulkan::BufferReadback &readback(activeBufferReadbacks[i]);
4898 if (forced || currentFrameSlot == readback.activeFrameSlot || readback.activeFrameSlot < 0) {
4899 readback.result->data.resizeForOverwrite(readback.byteSize);
4900 VkResult err = vmaCopyAllocationToMemory(toVmaAllocator(allocator),
4901 toVmaAllocation(readback.stagingAlloc),
4902 0, readback.result->data.data(), readback.byteSize);
4903 if (err != VK_SUCCESS) {
4904 qWarning("Failed to copy buffer readback buffer of size %d: %d", readback.byteSize, err);
4905 readback.result->data.clear();
4906 }
4907
4908 vmaDestroyBuffer(toVmaAllocator(allocator), readback.stagingBuf, toVmaAllocation(readback.stagingAlloc));
4909
4910 if (readback.result->completed)
4911 completedCallbacks.append(readback.result->completed);
4912
4913 activeBufferReadbacks.remove(i);
4914 }
4915 }
4916
4917 for (const auto &f : completedCallbacks)
4918 f();
4919}
4920
4921static struct {
4924} qvk_sampleCounts[] = {
4925 // keep this sorted by 'count'
4926 { VK_SAMPLE_COUNT_1_BIT, 1 },
4927 { VK_SAMPLE_COUNT_2_BIT, 2 },
4928 { VK_SAMPLE_COUNT_4_BIT, 4 },
4929 { VK_SAMPLE_COUNT_8_BIT, 8 },
4930 { VK_SAMPLE_COUNT_16_BIT, 16 },
4931 { VK_SAMPLE_COUNT_32_BIT, 32 },
4932 { VK_SAMPLE_COUNT_64_BIT, 64 }
4934
4936{
4937 const VkPhysicalDeviceLimits *limits = &physDevProperties.limits;
4938 VkSampleCountFlags color = limits->framebufferColorSampleCounts;
4939 VkSampleCountFlags depth = limits->framebufferDepthSampleCounts;
4940 VkSampleCountFlags stencil = limits->framebufferStencilSampleCounts;
4941 QList<int> result;
4942
4943 for (const auto &qvk_sampleCount : qvk_sampleCounts) {
4944 if ((color & qvk_sampleCount.mask)
4945 && (depth & qvk_sampleCount.mask)
4946 && (stencil & qvk_sampleCount.mask))
4947 {
4948 result.append(qvk_sampleCount.count);
4949 }
4950 }
4951
4952 return result;
4953}
4954
4956{
4957 const int s = effectiveSampleCount(sampleCount);
4958
4959 for (const auto &qvk_sampleCount : qvk_sampleCounts) {
4960 if (qvk_sampleCount.count == s)
4961 return qvk_sampleCount.mask;
4962 }
4963
4964 Q_UNREACHABLE_RETURN(VK_SAMPLE_COUNT_1_BIT);
4965}
4966
4968{
4969 QList<QSize> result;
4970#ifdef VK_KHR_fragment_shading_rate
4971 sampleCount = qMax(1, sampleCount);
4972 VkSampleCountFlagBits mask = VK_SAMPLE_COUNT_1_BIT;
4973 for (const auto &qvk_sampleCount : qvk_sampleCounts) {
4974 if (qvk_sampleCount.count == sampleCount) {
4975 mask = qvk_sampleCount.mask;
4976 break;
4977 }
4978 }
4979 for (const VkPhysicalDeviceFragmentShadingRateKHR &s : fragmentShadingRates) {
4980 if (s.sampleCounts & mask)
4981 result.append(QSize(int(s.fragmentSize.width), int(s.fragmentSize.height)));
4982 }
4983#else
4984 Q_UNUSED(sampleCount);
4985 result.append(QSize(1, 1));
4986#endif
4987 return result;
4988}
4989
4991{
4992 cbD->passResTrackers.emplace_back();
4993 cbD->currentPassResTrackerIndex = cbD->passResTrackers.size() - 1;
4994
4995 QVkCommandBuffer::Command &cmd(cbD->commands.get());
4997 cmd.args.transitionResources.trackerIndex = cbD->passResTrackers.size() - 1;
4998}
4999
5001{
5003
5004 for (auto it = cbD->commands.begin(), end = cbD->commands.end(); it != end; ++it) {
5005 QVkCommandBuffer::Command &cmd(*it);
5006 switch (cmd.cmd) {
5008 df->vkCmdCopyBuffer(cbD->cb, cmd.args.copyBuffer.src, cmd.args.copyBuffer.dst,
5009 1, &cmd.args.copyBuffer.desc);
5010 break;
5012 df->vkCmdCopyBufferToImage(cbD->cb, cmd.args.copyBufferToImage.src, cmd.args.copyBufferToImage.dst,
5013 cmd.args.copyBufferToImage.dstLayout,
5014 uint32_t(cmd.args.copyBufferToImage.count),
5015 cbD->pools.bufferImageCopy.constData() + cmd.args.copyBufferToImage.bufferImageCopyIndex);
5016 break;
5018 df->vkCmdCopyImage(cbD->cb, cmd.args.copyImage.src, cmd.args.copyImage.srcLayout,
5019 cmd.args.copyImage.dst, cmd.args.copyImage.dstLayout,
5020 1, &cmd.args.copyImage.desc);
5021 break;
5023 df->vkCmdCopyImageToBuffer(cbD->cb, cmd.args.copyImageToBuffer.src, cmd.args.copyImageToBuffer.srcLayout,
5024 cmd.args.copyImageToBuffer.dst,
5025 1, &cmd.args.copyImageToBuffer.desc);
5026 break;
5028 df->vkCmdPipelineBarrier(cbD->cb, cmd.args.imageBarrier.srcStageMask, cmd.args.imageBarrier.dstStageMask,
5029 0, 0, nullptr, 0, nullptr,
5030 cmd.args.imageBarrier.count, cbD->pools.imageBarrier.constData() + cmd.args.imageBarrier.index);
5031 break;
5033 df->vkCmdPipelineBarrier(cbD->cb, cmd.args.bufferBarrier.srcStageMask, cmd.args.bufferBarrier.dstStageMask,
5034 0, 0, nullptr,
5035 cmd.args.bufferBarrier.count, cbD->pools.bufferBarrier.constData() + cmd.args.bufferBarrier.index,
5036 0, nullptr);
5037 break;
5039 const auto &barrier = cmd.args.imageAndBufferBarrier;
5040 const VkBufferMemoryBarrier *bufferBarrierData = barrier.bufferCount
5041 ? cbD->pools.bufferBarrier.constData() + barrier.bufferIndex : nullptr;
5042 const VkImageMemoryBarrier *imageBarrierData = barrier.imageCount
5043 ? cbD->pools.imageBarrier.constData() + barrier.imageIndex : nullptr;
5044 df->vkCmdPipelineBarrier(cbD->cb, barrier.srcStageMask, barrier.dstStageMask,
5045 0, 0, nullptr,
5046 barrier.bufferCount, bufferBarrierData,
5047 barrier.imageCount, imageBarrierData);
5048 } break;
5050 df->vkCmdBlitImage(cbD->cb, cmd.args.blitImage.src, cmd.args.blitImage.srcLayout,
5051 cmd.args.blitImage.dst, cmd.args.blitImage.dstLayout,
5052 1, &cmd.args.blitImage.desc,
5053 cmd.args.blitImage.filter);
5054 break;
5056 cmd.args.beginRenderPass.desc.pClearValues = cbD->pools.clearValue.constData() + cmd.args.beginRenderPass.clearValueIndex;
5057 df->vkCmdBeginRenderPass(cbD->cb, &cmd.args.beginRenderPass.desc,
5058 cmd.args.beginRenderPass.useSecondaryCb ? VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS
5059 : VK_SUBPASS_CONTENTS_INLINE);
5060 break;
5062 VkMemoryBarrier barrier;
5063 barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
5064 barrier.pNext = nullptr;
5065 barrier.dstAccessMask = cmd.args.memoryBarrier.dstAccessMask;
5066 barrier.srcAccessMask = cmd.args.memoryBarrier.srcAccessMask;
5067 df->vkCmdPipelineBarrier(cbD->cb, cmd.args.memoryBarrier.srcStageMask, cmd.args.memoryBarrier.dstStageMask, cmd.args.memoryBarrier.dependencyFlags,
5068 1, &barrier,
5069 0, VK_NULL_HANDLE,
5070 0, VK_NULL_HANDLE);
5071 } break;
5073 df->vkCmdEndRenderPass(cbD->cb);
5074 break;
5076 df->vkCmdBindPipeline(cbD->cb, cmd.args.bindPipeline.bindPoint, cmd.args.bindPipeline.pipeline);
5077 break;
5079 {
5080 const uint32_t *offsets = nullptr;
5081 if (cmd.args.bindDescriptorSet.dynamicOffsetCount > 0)
5082 offsets = cbD->pools.dynamicOffset.constData() + cmd.args.bindDescriptorSet.dynamicOffsetIndex;
5083 df->vkCmdBindDescriptorSets(cbD->cb, cmd.args.bindDescriptorSet.bindPoint,
5084 cmd.args.bindDescriptorSet.pipelineLayout,
5085 0, 1, &cmd.args.bindDescriptorSet.descSet,
5086 uint32_t(cmd.args.bindDescriptorSet.dynamicOffsetCount),
5087 offsets);
5088 }
5089 break;
5091 df->vkCmdBindVertexBuffers(cbD->cb, uint32_t(cmd.args.bindVertexBuffer.startBinding),
5092 uint32_t(cmd.args.bindVertexBuffer.count),
5093 cbD->pools.vertexBuffer.constData() + cmd.args.bindVertexBuffer.vertexBufferIndex,
5094 cbD->pools.vertexBufferOffset.constData() + cmd.args.bindVertexBuffer.vertexBufferOffsetIndex);
5095 break;
5097 df->vkCmdBindIndexBuffer(cbD->cb, cmd.args.bindIndexBuffer.buf,
5098 cmd.args.bindIndexBuffer.ofs, cmd.args.bindIndexBuffer.type);
5099 break;
5101 df->vkCmdSetViewport(cbD->cb, 0, 1, &cmd.args.setViewport.viewport);
5102 break;
5104 df->vkCmdSetScissor(cbD->cb, 0, 1, &cmd.args.setScissor.scissor);
5105 break;
5107 df->vkCmdSetBlendConstants(cbD->cb, cmd.args.setBlendConstants.c);
5108 break;
5110 df->vkCmdSetStencilReference(cbD->cb, VK_STENCIL_FRONT_AND_BACK, cmd.args.setStencilRef.ref);
5111 break;
5113 df->vkCmdDraw(cbD->cb, cmd.args.draw.vertexCount, cmd.args.draw.instanceCount,
5114 cmd.args.draw.firstVertex, cmd.args.draw.firstInstance);
5115 break;
5117 df->vkCmdDrawIndexed(cbD->cb, cmd.args.drawIndexed.indexCount, cmd.args.drawIndexed.instanceCount,
5118 cmd.args.drawIndexed.firstIndex, cmd.args.drawIndexed.vertexOffset,
5119 cmd.args.drawIndexed.firstInstance);
5120 break;
5122 df->vkCmdDrawIndirect(cbD->cb, cmd.args.drawIndirect.indirectBuffer,
5123 cmd.args.drawIndirect.indirectBufferOffset,
5124 cmd.args.drawIndirect.drawCount,
5125 cmd.args.drawIndirect.stride);
5126 break;
5128 df->vkCmdDrawIndexedIndirect(cbD->cb, cmd.args.drawIndexedIndirect.indirectBuffer,
5129 cmd.args.drawIndexedIndirect.indirectBufferOffset,
5130 cmd.args.drawIndexedIndirect.drawCount,
5131 cmd.args.drawIndexedIndirect.stride);
5132 break;
5134#ifdef VK_EXT_debug_utils
5135 cmd.args.debugMarkerBegin.label.pLabelName =
5136 cbD->pools.debugMarkerData[cmd.args.debugMarkerBegin.labelNameIndex].constData();
5137 vkCmdBeginDebugUtilsLabelEXT(cbD->cb, &cmd.args.debugMarkerBegin.label);
5138#endif
5139 break;
5141#ifdef VK_EXT_debug_utils
5142 vkCmdEndDebugUtilsLabelEXT(cbD->cb);
5143#endif
5144 break;
5146#ifdef VK_EXT_debug_utils
5147 cmd.args.debugMarkerInsert.label.pLabelName =
5148 cbD->pools.debugMarkerData[cmd.args.debugMarkerInsert.labelNameIndex].constData();
5149 vkCmdInsertDebugUtilsLabelEXT(cbD->cb, &cmd.args.debugMarkerInsert.label);
5150#endif
5151 break;
5153 recordTransitionPassResources(cbD, cbD->passResTrackers[cmd.args.transitionResources.trackerIndex]);
5154 break;
5156 df->vkCmdDispatch(cbD->cb, uint32_t(cmd.args.dispatch.x), uint32_t(cmd.args.dispatch.y), uint32_t(cmd.args.dispatch.z));
5157 break;
5159 df->vkCmdExecuteCommands(cbD->cb, 1, &cmd.args.executeSecondary.cb);
5160 break;
5162 {
5163#ifdef VK_KHR_fragment_shading_rate
5164 VkFragmentShadingRateCombinerOpKHR op[2] = {
5165 VK_FRAGMENT_SHADING_RATE_COMBINER_OP_MAX_KHR,
5166 VK_FRAGMENT_SHADING_RATE_COMBINER_OP_MAX_KHR
5167 };
5168 VkExtent2D size = { cmd.args.setShadingRate.w, cmd.args.setShadingRate.h };
5169 vkCmdSetFragmentShadingRateKHR(cbD->cb, &size, op);
5170#endif
5171 }
5172 break;
5173 default:
5174 break;
5175 }
5176 }
5177}
5178
5180{
5181 switch (access) {
5182 case QRhiPassResourceTracker::BufIndirectDraw:
5183 return VK_ACCESS_INDIRECT_COMMAND_READ_BIT;
5184 case QRhiPassResourceTracker::BufVertexInput:
5185 return VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT;
5186 case QRhiPassResourceTracker::BufIndexRead:
5187 return VK_ACCESS_INDEX_READ_BIT;
5188 case QRhiPassResourceTracker::BufUniformRead:
5189 return VK_ACCESS_UNIFORM_READ_BIT;
5190 case QRhiPassResourceTracker::BufStorageLoad:
5191 return VK_ACCESS_SHADER_READ_BIT;
5192 case QRhiPassResourceTracker::BufStorageStore:
5193 return VK_ACCESS_SHADER_WRITE_BIT;
5194 case QRhiPassResourceTracker::BufStorageLoadStore:
5195 return VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
5196 default:
5197 Q_UNREACHABLE();
5198 break;
5199 }
5200 return 0;
5201}
5202
5204{
5205 switch (stage) {
5206 case QRhiPassResourceTracker::BufIndirectDrawStage:
5207 return VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT;
5208 case QRhiPassResourceTracker::BufVertexInputStage:
5209 return VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
5210 case QRhiPassResourceTracker::BufVertexStage:
5211 return VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
5212 case QRhiPassResourceTracker::BufTCStage:
5213 return VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT;
5214 case QRhiPassResourceTracker::BufTEStage:
5215 return VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT;
5216 case QRhiPassResourceTracker::BufFragmentStage:
5217 return VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
5218 case QRhiPassResourceTracker::BufComputeStage:
5219 return VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
5220 case QRhiPassResourceTracker::BufGeometryStage:
5221 return VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT;
5222 default:
5223 Q_UNREACHABLE();
5224 break;
5225 }
5226 return 0;
5227}
5228
5230{
5232 u.access = VkAccessFlags(usage.access);
5233 u.stage = VkPipelineStageFlags(usage.stage);
5234 return u;
5235}
5236
5238{
5239 switch (access) {
5240 case QRhiPassResourceTracker::TexSample:
5241 return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
5242 case QRhiPassResourceTracker::TexColorOutput:
5243 return VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
5244 case QRhiPassResourceTracker::TexDepthOutput:
5245 return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
5246 case QRhiPassResourceTracker::TexStorageLoad:
5247 case QRhiPassResourceTracker::TexStorageStore:
5248 case QRhiPassResourceTracker::TexStorageLoadStore:
5249 return VK_IMAGE_LAYOUT_GENERAL;
5250 case QRhiPassResourceTracker::TexShadingRate:
5251#ifdef VK_KHR_fragment_shading_rate
5252 return VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR;
5253#else
5254 return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
5255#endif
5256 default:
5257 Q_UNREACHABLE();
5258 break;
5259 }
5260 return VK_IMAGE_LAYOUT_GENERAL;
5261}
5262
5264{
5265 switch (access) {
5266 case QRhiPassResourceTracker::TexSample:
5267 return VK_ACCESS_SHADER_READ_BIT;
5268 case QRhiPassResourceTracker::TexColorOutput:
5269 return VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
5270 case QRhiPassResourceTracker::TexDepthOutput:
5271 return VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
5272 case QRhiPassResourceTracker::TexStorageLoad:
5273 return VK_ACCESS_SHADER_READ_BIT;
5274 case QRhiPassResourceTracker::TexStorageStore:
5275 return VK_ACCESS_SHADER_WRITE_BIT;
5276 case QRhiPassResourceTracker::TexStorageLoadStore:
5277 return VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
5279 return 0;
5280 default:
5281 Q_UNREACHABLE();
5282 break;
5283 }
5284 return 0;
5285}
5286
5288{
5289 switch (stage) {
5290 case QRhiPassResourceTracker::TexVertexStage:
5291 return VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
5292 case QRhiPassResourceTracker::TexTCStage:
5293 return VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT;
5294 case QRhiPassResourceTracker::TexTEStage:
5295 return VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT;
5296 case QRhiPassResourceTracker::TexFragmentStage:
5297 return VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
5298 case QRhiPassResourceTracker::TexColorOutputStage:
5299 return VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
5300 case QRhiPassResourceTracker::TexDepthOutputStage:
5301 return VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
5302 case QRhiPassResourceTracker::TexComputeStage:
5303 return VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
5304 case QRhiPassResourceTracker::TexGeometryStage:
5305 return VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT;
5306 default:
5307 Q_UNREACHABLE();
5308 break;
5309 }
5310 return 0;
5311}
5312
5314{
5316 u.layout = VkImageLayout(usage.layout);
5317 u.access = VkAccessFlags(usage.access);
5318 u.stage = VkPipelineStageFlags(usage.stage);
5319 return u;
5320}
5321
5323 QVkBuffer *bufD,
5324 int slot,
5327{
5328 QVkBuffer::UsageState &u(bufD->usageState[slot]);
5329 const VkAccessFlags newAccess = toVkAccess(access);
5330 const VkPipelineStageFlags newStage = toVkPipelineStage(stage);
5331 if (u.access == newAccess && u.stage == newStage) {
5332 if (!accessIsWrite(access))
5333 return;
5334 }
5335 passResTracker->registerBuffer(bufD, slot, &access, &stage, toPassTrackerUsageState(u));
5336 u.access = newAccess;
5337 u.stage = newStage;
5338}
5339
5341 QVkTexture *texD,
5344{
5345 QVkTexture::UsageState &u(texD->usageState);
5346 const VkAccessFlags newAccess = toVkAccess(access);
5347 const VkPipelineStageFlags newStage = toVkPipelineStage(stage);
5348 const VkImageLayout newLayout = toVkLayout(access);
5349 if (u.access == newAccess && u.stage == newStage && u.layout == newLayout) {
5350 if (!accessIsWrite(access))
5351 return;
5352 }
5353 passResTracker->registerTexture(texD, &access, &stage, toPassTrackerUsageState(u));
5354 u.layout = newLayout;
5355 u.access = newAccess;
5356 u.stage = newStage;
5357}
5358
5360{
5361 if (tracker.isEmpty())
5362 return;
5363
5364 for (const auto &[rhiB, trackedB]: tracker.buffers()) {
5365 QVkBuffer *bufD = QRHI_RES(QVkBuffer, rhiB);
5366 VkAccessFlags access = toVkAccess(trackedB.access);
5367 VkPipelineStageFlags stage = toVkPipelineStage(trackedB.stage);
5368 QVkBuffer::UsageState s = toVkBufferUsageState(trackedB.stateAtPassBegin);
5369 if (!s.stage)
5370 continue;
5371 if (s.access == access && s.stage == stage) {
5372 if (!accessIsWrite(access))
5373 continue;
5374 }
5375 VkBufferMemoryBarrier bufMemBarrier = {};
5376 bufMemBarrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
5377 bufMemBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
5378 bufMemBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
5379 bufMemBarrier.srcAccessMask = s.access;
5380 bufMemBarrier.dstAccessMask = access;
5381 bufMemBarrier.buffer = bufD->buffers[trackedB.slot];
5382 bufMemBarrier.size = VK_WHOLE_SIZE;
5383 df->vkCmdPipelineBarrier(cbD->cb, s.stage, stage, 0,
5384 0, nullptr,
5385 1, &bufMemBarrier,
5386 0, nullptr);
5387 }
5388
5389 for (const auto &[rhiT, trackedT]: tracker.textures()) {
5390 QVkTexture *texD = QRHI_RES(QVkTexture, rhiT);
5391 VkImageLayout layout = toVkLayout(trackedT.access);
5392 VkAccessFlags access = toVkAccess(trackedT.access);
5393 VkPipelineStageFlags stage = toVkPipelineStage(trackedT.stage);
5394 QVkTexture::UsageState s = toVkTextureUsageState(trackedT.stateAtPassBegin);
5395 if (s.access == access && s.stage == stage && s.layout == layout) {
5396 if (!accessIsWrite(access))
5397 continue;
5398 }
5399 VkImageMemoryBarrier barrier = {};
5400 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
5401 barrier.subresourceRange.aspectMask = aspectMaskForTextureFormat(texD->m_format);
5402 barrier.subresourceRange.baseMipLevel = 0;
5403 barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
5404 barrier.subresourceRange.baseArrayLayer = 0;
5405 barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
5406 barrier.oldLayout = s.layout; // new textures have this set to PREINITIALIZED
5407 barrier.newLayout = layout;
5408 barrier.srcAccessMask = s.access; // may be 0 but that's fine
5409 barrier.dstAccessMask = access;
5410 barrier.image = texD->image;
5411 VkPipelineStageFlags srcStage = s.stage;
5412 // stage mask cannot be 0
5413 if (!srcStage)
5414 srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
5415 df->vkCmdPipelineBarrier(cbD->cb, srcStage, stage, 0,
5416 0, nullptr,
5417 0, nullptr,
5418 1, &barrier);
5419 }
5420}
5421
5423{
5424 if (!vkGetPhysicalDeviceSurfaceCapabilitiesKHR
5425 || !vkGetPhysicalDeviceSurfaceFormatsKHR
5426 || !vkGetPhysicalDeviceSurfacePresentModesKHR)
5427 {
5428 qWarning("Physical device surface queries not available");
5429 return nullptr;
5430 }
5431
5432 return new QVkSwapChain(this);
5433}
5434
5435QRhiBuffer *QRhiVulkan::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, quint32 size)
5436{
5437 return new QVkBuffer(this, type, usage, size);
5438}
5439
5441{
5442 return int(ubufAlign); // typically 256 (bytes)
5443}
5444
5446{
5447 return false;
5448}
5449
5451{
5452 return false;
5453}
5454
5456{
5457 return true;
5458}
5459
5461{
5462 // See https://matthewwellings.com/blog/the-new-vulkan-coordinate-system/
5463
5464 static QMatrix4x4 m;
5465 if (m.isIdentity()) {
5466 // NB the ctor takes row-major
5467 m = QMatrix4x4(1.0f, 0.0f, 0.0f, 0.0f,
5468 0.0f, -1.0f, 0.0f, 0.0f,
5469 0.0f, 0.0f, 0.5f, 0.5f,
5470 0.0f, 0.0f, 0.0f, 1.0f);
5471 }
5472 return m;
5473}
5474
5475bool QRhiVulkan::isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const
5476{
5477 // Note that with some SDKs the validation layer gives an odd warning about
5478 // BC not being supported, even when our check here succeeds. Not much we
5479 // can do about that.
5480 if (format >= QRhiTexture::BC1 && format <= QRhiTexture::BC7) {
5481 if (!physDevFeatures.textureCompressionBC)
5482 return false;
5483 }
5484
5485 if (format >= QRhiTexture::ETC2_RGB8 && format <= QRhiTexture::ETC2_RGBA8) {
5486 if (!physDevFeatures.textureCompressionETC2)
5487 return false;
5488 }
5489
5490 if (format >= QRhiTexture::ASTC_4x4 && format <= QRhiTexture::ASTC_12x12) {
5491 if (!physDevFeatures.textureCompressionASTC_LDR)
5492 return false;
5493 }
5494
5495 VkFormat vkformat = toVkTextureFormat(format, flags);
5496 VkFormatProperties props;
5497 f->vkGetPhysicalDeviceFormatProperties(physDev, vkformat, &props);
5498 return (props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) != 0;
5499}
5500
5501bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const
5502{
5503 switch (feature) {
5504 case QRhi::MultisampleTexture:
5505 return true;
5506 case QRhi::MultisampleRenderBuffer:
5507 return true;
5508 case QRhi::DebugMarkers:
5509 return caps.debugUtils;
5510 case QRhi::Timestamps:
5511 return timestampValidBits != 0;
5512 case QRhi::Instancing:
5513 return true;
5514 case QRhi::CustomInstanceStepRate:
5515 return caps.vertexAttribDivisor;
5516 case QRhi::PrimitiveRestart:
5517 return true;
5518 case QRhi::NonDynamicUniformBuffers:
5519 return true;
5520 case QRhi::NonFourAlignedEffectiveIndexBufferOffset:
5521 return true;
5522 case QRhi::NPOTTextureRepeat:
5523 return true;
5524 case QRhi::RedOrAlpha8IsRed:
5525 return true;
5526 case QRhi::ElementIndexUint:
5527 return true;
5528 case QRhi::Compute:
5529 return caps.compute;
5530 case QRhi::WideLines:
5531 return caps.wideLines;
5532 case QRhi::VertexShaderPointSize:
5533 return true;
5534 case QRhi::BaseVertex:
5535 return true;
5536 case QRhi::BaseInstance:
5537 return true;
5538 case QRhi::TriangleFanTopology:
5539 return true;
5540 case QRhi::ReadBackNonUniformBuffer:
5541 return true;
5542 case QRhi::ReadBackNonBaseMipLevel:
5543 return true;
5544 case QRhi::TexelFetch:
5545 return true;
5546 case QRhi::RenderToNonBaseMipLevel:
5547 return true;
5548 case QRhi::IntAttributes:
5549 return true;
5550 case QRhi::ScreenSpaceDerivatives:
5551 return true;
5552 case QRhi::ReadBackAnyTextureFormat:
5553 return true;
5554 case QRhi::PipelineCacheDataLoadSave:
5555 return true;
5556 case QRhi::ImageDataStride:
5557 return true;
5558 case QRhi::RenderBufferImport:
5559 return false;
5560 case QRhi::ThreeDimensionalTextures:
5561 return true;
5562 case QRhi::RenderTo3DTextureSlice:
5563 return caps.texture3DSliceAs2D;
5564 case QRhi::TextureArrays:
5565 return true;
5566 case QRhi::Tessellation:
5567 return caps.tessellation;
5568 case QRhi::GeometryShader:
5569 return caps.geometryShader;
5570 case QRhi::TextureArrayRange:
5571 return true;
5572 case QRhi::NonFillPolygonMode:
5573 return caps.nonFillPolygonMode;
5574 case QRhi::OneDimensionalTextures:
5575 return true;
5576 case QRhi::OneDimensionalTextureMipmaps:
5577 return true;
5578 case QRhi::HalfAttributes:
5579 return true;
5580 case QRhi::RenderToOneDimensionalTexture:
5581 return true;
5582 case QRhi::ThreeDimensionalTextureMipmaps:
5583 return true;
5584 case QRhi::MultiView:
5585 return caps.multiView;
5586 case QRhi::TextureViewFormat:
5587 return true;
5588 case QRhi::ResolveDepthStencil:
5589 return caps.renderPass2KHR && caps.depthStencilResolveKHR;
5590 case QRhi::VariableRateShading:
5591 return caps.renderPass2KHR && caps.perDrawShadingRate;
5592 case QRhi::VariableRateShadingMap:
5593 case QRhi::VariableRateShadingMapWithTexture:
5594 return caps.renderPass2KHR && caps.imageBasedShadingRate;
5595 case QRhi::PerRenderTargetBlending:
5596 case QRhi::SampleVariables:
5597 return true;
5598 case QRhi::InstanceIndexIncludesBaseInstance:
5599 return true;
5600 case QRhi::DepthClamp:
5601 return caps.depthClamp;
5602 case QRhi::DrawIndirect:
5603 return true; // available in Vulkan 1.0
5604 case QRhi::DrawIndirectMulti:
5605 return caps.drawIndirectMulti;
5606 case QRhi::ShaderDrawParameters:
5607 return caps.shaderDrawParameters;
5608 default:
5609 Q_UNREACHABLE_RETURN(false);
5610 }
5611}
5612
5613int QRhiVulkan::resourceLimit(QRhi::ResourceLimit limit) const
5614{
5615 switch (limit) {
5616 case QRhi::TextureSizeMin:
5617 return 1;
5618 case QRhi::TextureSizeMax:
5619 return int(physDevProperties.limits.maxImageDimension2D);
5620 case QRhi::MaxColorAttachments:
5621 return int(physDevProperties.limits.maxColorAttachments);
5622 case QRhi::FramesInFlight:
5623 return QVK_FRAMES_IN_FLIGHT;
5624 case QRhi::MaxAsyncReadbackFrames:
5625 return QVK_FRAMES_IN_FLIGHT;
5626 case QRhi::MaxThreadGroupsPerDimension:
5627 return int(qMin(physDevProperties.limits.maxComputeWorkGroupCount[0],
5628 qMin(physDevProperties.limits.maxComputeWorkGroupCount[1],
5629 physDevProperties.limits.maxComputeWorkGroupCount[2])));
5630 case QRhi::MaxThreadsPerThreadGroup:
5631 return int(physDevProperties.limits.maxComputeWorkGroupInvocations);
5632 case QRhi::MaxThreadGroupX:
5633 return int(physDevProperties.limits.maxComputeWorkGroupSize[0]);
5634 case QRhi::MaxThreadGroupY:
5635 return int(physDevProperties.limits.maxComputeWorkGroupSize[1]);
5636 case QRhi::MaxThreadGroupZ:
5637 return int(physDevProperties.limits.maxComputeWorkGroupSize[2]);
5638 case QRhi::TextureArraySizeMax:
5639 return int(physDevProperties.limits.maxImageArrayLayers);
5640 case QRhi::MaxUniformBufferRange:
5641 return int(qMin<uint32_t>(INT_MAX, physDevProperties.limits.maxUniformBufferRange));
5642 case QRhi::MaxVertexInputs:
5643 return physDevProperties.limits.maxVertexInputAttributes;
5644 case QRhi::MaxVertexOutputs:
5645 return physDevProperties.limits.maxVertexOutputComponents / 4;
5646 case QRhi::ShadingRateImageTileSize:
5647 return caps.imageBasedShadingRateTileSize;
5648 default:
5649 Q_UNREACHABLE_RETURN(0);
5650 }
5651}
5652
5654{
5655 return &nativeHandlesStruct;
5656}
5657
5659{
5660 return driverInfoStruct;
5661}
5662
5664{
5665 QRhiStats result;
5666 result.totalPipelineCreationTime = totalPipelineCreationTime();
5667
5668 VmaBudget budgets[VK_MAX_MEMORY_HEAPS];
5669 vmaGetHeapBudgets(toVmaAllocator(allocator), budgets);
5670
5671 uint32_t count = toVmaAllocator(allocator)->GetMemoryHeapCount();
5672 for (uint32_t i = 0; i < count; ++i) {
5673 const VmaStatistics &stats(budgets[i].statistics);
5674 result.blockCount += stats.blockCount;
5675 result.allocCount += stats.allocationCount;
5676 result.usedBytes += stats.allocationBytes;
5677 result.unusedBytes += stats.blockBytes - stats.allocationBytes;
5678 }
5679
5680 return result;
5681}
5682
5684{
5685 // not applicable
5686 return false;
5687}
5688
5689void QRhiVulkan::setQueueSubmitParams(QRhiNativeHandles *params)
5690{
5691 QRhiVulkanQueueSubmitParams *sp = static_cast<QRhiVulkanQueueSubmitParams *>(params);
5692 if (!sp)
5693 return;
5694
5695 waitSemaphoresForQueueSubmit.clear();
5696 if (sp->waitSemaphoreCount)
5697 waitSemaphoresForQueueSubmit.append(sp->waitSemaphores, sp->waitSemaphoreCount);
5698
5699 signalSemaphoresForQueueSubmit.clear();
5700 if (sp->signalSemaphoreCount)
5701 signalSemaphoresForQueueSubmit.append(sp->signalSemaphores, sp->signalSemaphoreCount);
5702
5703 waitSemaphoresForPresent.clear();
5704 if (sp->presentWaitSemaphoreCount)
5705 waitSemaphoresForPresent.append(sp->presentWaitSemaphores, sp->presentWaitSemaphoreCount);
5706}
5707
5712
5714{
5715 return deviceLost;
5716}
5717
5729
5731{
5732 Q_STATIC_ASSERT(sizeof(QVkPipelineCacheDataHeader) == 32);
5733
5734 QByteArray data;
5735 if (!pipelineCache || !rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
5736 return data;
5737
5738 size_t dataSize = 0;
5739 VkResult err = df->vkGetPipelineCacheData(dev, pipelineCache, &dataSize, nullptr);
5740 if (err != VK_SUCCESS) {
5741 qCDebug(QRHI_LOG_INFO, "Failed to get pipeline cache data size: %d", err);
5742 return QByteArray();
5743 }
5744 const size_t headerSize = sizeof(QVkPipelineCacheDataHeader);
5745 const size_t dataOffset = headerSize + VK_UUID_SIZE;
5746 data.resize(dataOffset + dataSize);
5747 err = df->vkGetPipelineCacheData(dev, pipelineCache, &dataSize, data.data() + dataOffset);
5748 if (err != VK_SUCCESS) {
5749 qCDebug(QRHI_LOG_INFO, "Failed to get pipeline cache data of %d bytes: %d", int(dataSize), err);
5750 return QByteArray();
5751 }
5752
5754 header.rhiId = pipelineCacheRhiId();
5755 header.arch = quint32(sizeof(void*));
5756 header.driverVersion = physDevProperties.driverVersion;
5757 header.vendorId = physDevProperties.vendorID;
5758 header.deviceId = physDevProperties.deviceID;
5759 header.dataSize = quint32(dataSize);
5760 header.uuidSize = VK_UUID_SIZE;
5761 header.reserved = 0;
5762 memcpy(data.data(), &header, headerSize);
5763 memcpy(data.data() + headerSize, physDevProperties.pipelineCacheUUID, VK_UUID_SIZE);
5764
5765 return data;
5766}
5767
5768void QRhiVulkan::setPipelineCacheData(const QByteArray &data)
5769{
5770 if (data.isEmpty())
5771 return;
5772
5773 const size_t headerSize = sizeof(QVkPipelineCacheDataHeader);
5774 if (data.size() < qsizetype(headerSize)) {
5775 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob size");
5776 return;
5777 }
5779 memcpy(&header, data.constData(), headerSize);
5780
5781 const quint32 rhiId = pipelineCacheRhiId();
5782 if (header.rhiId != rhiId) {
5783 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: The data is for a different QRhi version or backend (%u, %u)",
5784 rhiId, header.rhiId);
5785 return;
5786 }
5787 const quint32 arch = quint32(sizeof(void*));
5788 if (header.arch != arch) {
5789 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Architecture does not match (%u, %u)",
5790 arch, header.arch);
5791 return;
5792 }
5793 if (header.driverVersion != physDevProperties.driverVersion) {
5794 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: driverVersion does not match (%u, %u)",
5795 physDevProperties.driverVersion, header.driverVersion);
5796 return;
5797 }
5798 if (header.vendorId != physDevProperties.vendorID) {
5799 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: vendorID does not match (%u, %u)",
5800 physDevProperties.vendorID, header.vendorId);
5801 return;
5802 }
5803 if (header.deviceId != physDevProperties.deviceID) {
5804 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: deviceID does not match (%u, %u)",
5805 physDevProperties.deviceID, header.deviceId);
5806 return;
5807 }
5808 if (header.uuidSize != VK_UUID_SIZE) {
5809 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: VK_UUID_SIZE does not match (%u, %u)",
5810 quint32(VK_UUID_SIZE), header.uuidSize);
5811 return;
5812 }
5813
5814 if (data.size() < qsizetype(headerSize + VK_UUID_SIZE)) {
5815 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob, no uuid");
5816 return;
5817 }
5818 if (memcmp(data.constData() + headerSize, physDevProperties.pipelineCacheUUID, VK_UUID_SIZE)) {
5819 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: pipelineCacheUUID does not match");
5820 return;
5821 }
5822
5823 const size_t dataOffset = headerSize + VK_UUID_SIZE;
5824 if (data.size() < qsizetype(dataOffset + header.dataSize)) {
5825 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob, data missing");
5826 return;
5827 }
5828
5829 if (pipelineCache) {
5830 df->vkDestroyPipelineCache(dev, pipelineCache, nullptr);
5831 pipelineCache = VK_NULL_HANDLE;
5832 }
5833
5834 if (ensurePipelineCache(data.constData() + dataOffset, header.dataSize)) {
5835 qCDebug(QRHI_LOG_INFO, "Created pipeline cache with initial data of %d bytes",
5836 int(header.dataSize));
5837 } else {
5838 qCDebug(QRHI_LOG_INFO, "Failed to create pipeline cache with initial data specified");
5839 }
5840}
5841
5842QRhiRenderBuffer *QRhiVulkan::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
5843 int sampleCount, QRhiRenderBuffer::Flags flags,
5844 QRhiTexture::Format backingFormatHint)
5845{
5846 return new QVkRenderBuffer(this, type, pixelSize, sampleCount, flags, backingFormatHint);
5847}
5848
5849QRhiTexture *QRhiVulkan::createTexture(QRhiTexture::Format format,
5850 const QSize &pixelSize, int depth, int arraySize,
5851 int sampleCount, QRhiTexture::Flags flags)
5852{
5853 return new QVkTexture(this, format, pixelSize, depth, arraySize, sampleCount, flags);
5854}
5855
5856QRhiSampler *QRhiVulkan::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
5857 QRhiSampler::Filter mipmapMode,
5858 QRhiSampler::AddressMode u, QRhiSampler::AddressMode v, QRhiSampler::AddressMode w)
5859{
5860 return new QVkSampler(this, magFilter, minFilter, mipmapMode, u, v, w);
5861}
5862
5863QRhiShadingRateMap *QRhiVulkan::createShadingRateMap()
5864{
5865 return new QVkShadingRateMap(this);
5866}
5867
5868QRhiTextureRenderTarget *QRhiVulkan::createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
5869 QRhiTextureRenderTarget::Flags flags)
5870{
5871 return new QVkTextureRenderTarget(this, desc, flags);
5872}
5873
5875{
5876 return new QVkGraphicsPipeline(this);
5877}
5878
5880{
5881 return new QVkComputePipeline(this);
5882}
5883
5885{
5886 return new QVkShaderResourceBindings(this);
5887}
5888
5889void QRhiVulkan::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps)
5890{
5892 Q_ASSERT(psD->pipeline);
5893 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
5895
5896 if (cbD->currentGraphicsPipeline != ps || cbD->currentPipelineGeneration != psD->generation) {
5897 if (cbD->passUsesSecondaryCb) {
5898 df->vkCmdBindPipeline(cbD->activeSecondaryCbStack.last(), VK_PIPELINE_BIND_POINT_GRAPHICS, psD->pipeline);
5899 } else {
5900 QVkCommandBuffer::Command &cmd(cbD->commands.get());
5902 cmd.args.bindPipeline.bindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
5903 cmd.args.bindPipeline.pipeline = psD->pipeline;
5904 }
5905
5906 cbD->currentGraphicsPipeline = ps;
5907 cbD->currentComputePipeline = nullptr;
5908 cbD->currentPipelineGeneration = psD->generation;
5909
5910 if (cbD->hasCustomScissorSet && !psD->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor))
5912 }
5913
5914 psD->lastActiveFrameSlot = currentFrameSlot;
5915}
5916
5917void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBindings *srb,
5918 int dynamicOffsetCount,
5919 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
5920{
5921 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
5923 QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
5924 QVkGraphicsPipeline *gfxPsD = QRHI_RES(QVkGraphicsPipeline, cbD->currentGraphicsPipeline);
5925 QVkComputePipeline *compPsD = QRHI_RES(QVkComputePipeline, cbD->currentComputePipeline);
5926
5927 if (!srb) {
5928 if (gfxPsD)
5929 srb = gfxPsD->m_shaderResourceBindings;
5930 else
5931 srb = compPsD->m_shaderResourceBindings;
5932 }
5933
5935 auto &descSetBd(srbD->boundResourceData[currentFrameSlot]);
5936 bool rewriteDescSet = false;
5937 bool addWriteBarrier = false;
5938 VkPipelineStageFlags writeBarrierSrcStageMask = 0;
5939 VkPipelineStageFlags writeBarrierDstStageMask = 0;
5940
5941 // Do host writes and mark referenced shader resources as in-use.
5942 // Also prepare to ensure the descriptor set we are going to bind refers to up-to-date Vk objects.
5943 for (int i = 0, ie = srbD->sortedBindings.size(); i != ie; ++i) {
5944 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->sortedBindings[i]);
5946 switch (b->type) {
5947 case QRhiShaderResourceBinding::UniformBuffer:
5948 {
5949 QVkBuffer *bufD = QRHI_RES(QVkBuffer, b->u.ubuf.buf);
5950 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer));
5951 sanityCheckResourceOwnership(bufD);
5952
5953 if (bufD->m_type == QRhiBuffer::Dynamic)
5954 executeBufferHostWritesForSlot(bufD, currentFrameSlot);
5955
5956 bufD->lastActiveFrameSlot = currentFrameSlot;
5957 trackedRegisterBuffer(&passResTracker, bufD, bufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0,
5959 QRhiPassResourceTracker::toPassTrackerBufferStage(b->stage));
5960
5961 // Check both the "local" id (the generation counter) and the
5962 // global id. The latter is relevant when a newly allocated
5963 // QRhiResource ends up with the same pointer as a previous one.
5964 // (and that previous one could have been in an srb...)
5965 if (bufD->generation != bd.ubuf.generation || bufD->m_id != bd.ubuf.id) {
5966 rewriteDescSet = true;
5967 bd.ubuf.id = bufD->m_id;
5968 bd.ubuf.generation = bufD->generation;
5969 }
5970 }
5971 break;
5972 case QRhiShaderResourceBinding::SampledTexture:
5973 case QRhiShaderResourceBinding::Texture:
5974 case QRhiShaderResourceBinding::Sampler:
5975 {
5976 const QRhiShaderResourceBinding::Data::TextureAndOrSamplerData *data = &b->u.stex;
5977 if (bd.stex.count != data->count) {
5978 bd.stex.count = data->count;
5979 rewriteDescSet = true;
5980 }
5981 for (int elem = 0; elem < data->count; ++elem) {
5982 QVkTexture *texD = QRHI_RES(QVkTexture, data->texSamplers[elem].tex);
5983 QVkSampler *samplerD = QRHI_RES(QVkSampler, data->texSamplers[elem].sampler);
5984 // We use the same code path for both combined and separate
5985 // images and samplers, so tex or sampler (but not both) can be
5986 // null here.
5987 Q_ASSERT(texD || samplerD);
5988 sanityCheckResourceOwnership(texD);
5989 sanityCheckResourceOwnership(samplerD);
5990 if (texD) {
5991 texD->lastActiveFrameSlot = currentFrameSlot;
5992 trackedRegisterTexture(&passResTracker, texD,
5994 QRhiPassResourceTracker::toPassTrackerTextureStage(b->stage));
5995 }
5996 if (samplerD)
5997 samplerD->lastActiveFrameSlot = currentFrameSlot;
5998 const quint64 texId = texD ? texD->m_id : 0;
5999 const uint texGen = texD ? texD->generation : 0;
6000 const quint64 samplerId = samplerD ? samplerD->m_id : 0;
6001 const uint samplerGen = samplerD ? samplerD->generation : 0;
6002 if (texGen != bd.stex.d[elem].texGeneration
6003 || texId != bd.stex.d[elem].texId
6004 || samplerGen != bd.stex.d[elem].samplerGeneration
6005 || samplerId != bd.stex.d[elem].samplerId)
6006 {
6007 rewriteDescSet = true;
6008 bd.stex.d[elem].texId = texId;
6009 bd.stex.d[elem].texGeneration = texGen;
6010 bd.stex.d[elem].samplerId = samplerId;
6011 bd.stex.d[elem].samplerGeneration = samplerGen;
6012 }
6013 }
6014 }
6015 break;
6016 case QRhiShaderResourceBinding::ImageLoad:
6017 case QRhiShaderResourceBinding::ImageStore:
6018 case QRhiShaderResourceBinding::ImageLoadStore:
6019 {
6020 QVkTexture *texD = QRHI_RES(QVkTexture, b->u.simage.tex);
6021 sanityCheckResourceOwnership(texD);
6022 Q_ASSERT(texD->m_flags.testFlag(QRhiTexture::UsedWithLoadStore));
6023 texD->lastActiveFrameSlot = currentFrameSlot;
6025 if (b->type == QRhiShaderResourceBinding::ImageLoad)
6027 else if (b->type == QRhiShaderResourceBinding::ImageStore)
6029 else
6031
6032 const auto stage = QRhiPassResourceTracker::toPassTrackerTextureStage(b->stage);
6033 const auto prevAccess = passResTracker.textures().find(texD);
6034 if (prevAccess != passResTracker.textures().end()) {
6035 const QRhiPassResourceTracker::Texture &tex = prevAccess->second;
6038 addWriteBarrier = true;
6039 writeBarrierDstStageMask |= toVkPipelineStage(stage);
6040 writeBarrierSrcStageMask |= toVkPipelineStage(tex.stage);
6041 }
6042 }
6043
6044 trackedRegisterTexture(&passResTracker, texD,
6045 access,
6046 stage);
6047
6048 if (texD->generation != bd.simage.generation || texD->m_id != bd.simage.id) {
6049 rewriteDescSet = true;
6050 bd.simage.id = texD->m_id;
6051 bd.simage.generation = texD->generation;
6052 }
6053 }
6054 break;
6055 case QRhiShaderResourceBinding::BufferLoad:
6056 case QRhiShaderResourceBinding::BufferStore:
6057 case QRhiShaderResourceBinding::BufferLoadStore:
6058 {
6059 QVkBuffer *bufD = QRHI_RES(QVkBuffer, b->u.sbuf.buf);
6060 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::StorageBuffer));
6061 sanityCheckResourceOwnership(bufD);
6062
6063 if (bufD->m_type == QRhiBuffer::Dynamic)
6064 executeBufferHostWritesForSlot(bufD, currentFrameSlot);
6065
6066 bufD->lastActiveFrameSlot = currentFrameSlot;
6068 if (b->type == QRhiShaderResourceBinding::BufferLoad)
6070 else if (b->type == QRhiShaderResourceBinding::BufferStore)
6072 else
6074
6075 const auto stage = QRhiPassResourceTracker::toPassTrackerBufferStage(b->stage);
6076 const auto prevAccess = passResTracker.buffers().find(bufD);
6077 if (prevAccess != passResTracker.buffers().end()) {
6078 const QRhiPassResourceTracker::Buffer &buf = prevAccess->second;
6081 addWriteBarrier = true;
6082 writeBarrierDstStageMask |= toVkPipelineStage(stage);
6083 writeBarrierSrcStageMask |= toVkPipelineStage(buf.stage);
6084 }
6085 }
6086 trackedRegisterBuffer(&passResTracker, bufD, bufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0,
6087 access,
6088 stage);
6089
6090 if (bufD->generation != bd.sbuf.generation || bufD->m_id != bd.sbuf.id) {
6091 rewriteDescSet = true;
6092 bd.sbuf.id = bufD->m_id;
6093 bd.sbuf.generation = bufD->generation;
6094 }
6095 }
6096 break;
6097 default:
6098 Q_UNREACHABLE();
6099 break;
6100 }
6101 }
6102
6103 if (addWriteBarrier) {
6104 if (cbD->passUsesSecondaryCb) {
6105 VkMemoryBarrier barrier;
6106 barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
6107 barrier.pNext = nullptr;
6108 barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
6109 barrier.srcAccessMask = barrier.dstAccessMask;
6110 df->vkCmdPipelineBarrier(cbD->activeSecondaryCbStack.last(), writeBarrierSrcStageMask, writeBarrierDstStageMask, 0,
6111 1, &barrier,
6112 0, VK_NULL_HANDLE,
6113 0, VK_NULL_HANDLE);
6114 } else {
6115 QVkCommandBuffer::Command &cmd(cbD->commands.get());
6117 cmd.args.memoryBarrier.dependencyFlags = 0;
6118 cmd.args.memoryBarrier.dstStageMask = writeBarrierDstStageMask;
6119 cmd.args.memoryBarrier.srcStageMask = writeBarrierSrcStageMask;
6120 cmd.args.memoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
6121 cmd.args.memoryBarrier.srcAccessMask = cmd.args.memoryBarrier.dstAccessMask;
6122 }
6123 }
6124
6125 // write descriptor sets, if needed
6126 if (rewriteDescSet)
6127 updateShaderResourceBindings(srb);
6128
6129 // make sure the descriptors for the correct slot will get bound.
6130 // also, dynamic offsets always need a bind.
6131 const bool forceRebind = cbD->currentDescSetSlot != currentFrameSlot
6132 || !srbD->sortedDynamicOffsetBindingNumbers.isEmpty();
6133
6134 const bool srbChanged = gfxPsD ? (cbD->currentGraphicsSrb != srb) : (cbD->currentComputeSrb != srb);
6135
6136 if (forceRebind || rewriteDescSet || srbChanged || cbD->currentSrbGeneration != srbD->generation) {
6137 QVarLengthArray<uint32_t, 4> dynOfs;
6138 // Filling out dynOfs based on the sorted bindings is important
6139 // because dynOfs has to be ordered based on the binding numbers,
6140 // and neither srb nor dynamicOffsets has any such ordering
6141 // requirement.
6142 for (int bindingNumber : std::as_const(srbD->sortedDynamicOffsetBindingNumbers)) {
6143 uint32_t offset = 0;
6144 for (int i = 0; i < dynamicOffsetCount; ++i) {
6145 const QRhiCommandBuffer::DynamicOffset &bindingOffsetPair(dynamicOffsets[i]);
6146 if (bindingOffsetPair.first == bindingNumber) {
6147 offset = bindingOffsetPair.second;
6148 break;
6149 }
6150 }
6151 dynOfs.append(offset); // use 0 if dynamicOffsets did not contain this binding
6152 }
6153
6154 if (cbD->passUsesSecondaryCb) {
6155 df->vkCmdBindDescriptorSets(cbD->activeSecondaryCbStack.last(),
6156 gfxPsD ? VK_PIPELINE_BIND_POINT_GRAPHICS : VK_PIPELINE_BIND_POINT_COMPUTE,
6157 gfxPsD ? gfxPsD->layout : compPsD->layout,
6158 0, 1, &srbD->descSets[currentFrameSlot],
6159 uint32_t(dynOfs.size()),
6160 dynOfs.size() ? dynOfs.constData() : nullptr);
6161 } else {
6162 QVkCommandBuffer::Command &cmd(cbD->commands.get());
6164 cmd.args.bindDescriptorSet.bindPoint = gfxPsD ? VK_PIPELINE_BIND_POINT_GRAPHICS
6165 : VK_PIPELINE_BIND_POINT_COMPUTE;
6166 cmd.args.bindDescriptorSet.pipelineLayout = gfxPsD ? gfxPsD->layout : compPsD->layout;
6167 cmd.args.bindDescriptorSet.descSet = srbD->descSets[currentFrameSlot];
6168 cmd.args.bindDescriptorSet.dynamicOffsetCount = dynOfs.size();
6169 cmd.args.bindDescriptorSet.dynamicOffsetIndex = cbD->pools.dynamicOffset.size();
6170 cbD->pools.dynamicOffset.append(dynOfs.constData(), dynOfs.size());
6171 }
6172
6173 if (gfxPsD) {
6174 cbD->currentGraphicsSrb = srb;
6175 cbD->currentComputeSrb = nullptr;
6176 } else {
6177 cbD->currentGraphicsSrb = nullptr;
6178 cbD->currentComputeSrb = srb;
6179 }
6180 cbD->currentSrbGeneration = srbD->generation;
6181 cbD->currentDescSetSlot = currentFrameSlot;
6182 }
6183
6184 srbD->lastActiveFrameSlot = currentFrameSlot;
6185}
6186
6187void QRhiVulkan::setVertexInput(QRhiCommandBuffer *cb,
6188 int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
6189 QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat)
6190{
6191 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
6193 QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
6194
6195 bool needsBindVBuf = false;
6196 for (int i = 0; i < bindingCount; ++i) {
6197 const int inputSlot = startBinding + i;
6198 QVkBuffer *bufD = QRHI_RES(QVkBuffer, bindings[i].first);
6199 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::VertexBuffer));
6200 bufD->lastActiveFrameSlot = currentFrameSlot;
6201 if (bufD->m_type == QRhiBuffer::Dynamic)
6202 executeBufferHostWritesForSlot(bufD, currentFrameSlot);
6203
6204 const VkBuffer vkvertexbuf = bufD->buffers[bufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0];
6205 if (cbD->currentVertexBuffers[inputSlot] != vkvertexbuf
6206 || cbD->currentVertexOffsets[inputSlot] != bindings[i].second)
6207 {
6208 needsBindVBuf = true;
6209 cbD->currentVertexBuffers[inputSlot] = vkvertexbuf;
6210 cbD->currentVertexOffsets[inputSlot] = bindings[i].second;
6211 }
6212 }
6213
6214 if (needsBindVBuf) {
6215 QVarLengthArray<VkBuffer, 4> bufs;
6216 QVarLengthArray<VkDeviceSize, 4> ofs;
6217 for (int i = 0; i < bindingCount; ++i) {
6218 QVkBuffer *bufD = QRHI_RES(QVkBuffer, bindings[i].first);
6219 const int slot = bufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0;
6220 bufs.append(bufD->buffers[slot]);
6221 ofs.append(bindings[i].second);
6222 trackedRegisterBuffer(&passResTracker, bufD, slot,
6225 }
6226
6227 if (cbD->passUsesSecondaryCb) {
6228 df->vkCmdBindVertexBuffers(cbD->activeSecondaryCbStack.last(), uint32_t(startBinding),
6229 uint32_t(bufs.size()), bufs.constData(), ofs.constData());
6230 } else {
6231 QVkCommandBuffer::Command &cmd(cbD->commands.get());
6233 cmd.args.bindVertexBuffer.startBinding = startBinding;
6234 cmd.args.bindVertexBuffer.count = bufs.size();
6235 cmd.args.bindVertexBuffer.vertexBufferIndex = cbD->pools.vertexBuffer.size();
6236 cbD->pools.vertexBuffer.append(bufs.constData(), bufs.size());
6237 cmd.args.bindVertexBuffer.vertexBufferOffsetIndex = cbD->pools.vertexBufferOffset.size();
6238 cbD->pools.vertexBufferOffset.append(ofs.constData(), ofs.size());
6239 }
6240 }
6241
6242 if (indexBuf) {
6243 QVkBuffer *ibufD = QRHI_RES(QVkBuffer, indexBuf);
6244 Q_ASSERT(ibufD->m_usage.testFlag(QRhiBuffer::IndexBuffer));
6245 ibufD->lastActiveFrameSlot = currentFrameSlot;
6246 if (ibufD->m_type == QRhiBuffer::Dynamic)
6247 executeBufferHostWritesForSlot(ibufD, currentFrameSlot);
6248
6249 const int slot = ibufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0;
6250 const VkBuffer vkindexbuf = ibufD->buffers[slot];
6251 const VkIndexType type = indexFormat == QRhiCommandBuffer::IndexUInt16 ? VK_INDEX_TYPE_UINT16
6252 : VK_INDEX_TYPE_UINT32;
6253
6254 if (cbD->currentIndexBuffer != vkindexbuf
6255 || cbD->currentIndexOffset != indexOffset
6256 || cbD->currentIndexFormat != type)
6257 {
6258 cbD->currentIndexBuffer = vkindexbuf;
6259 cbD->currentIndexOffset = indexOffset;
6260 cbD->currentIndexFormat = type;
6261
6262 if (cbD->passUsesSecondaryCb) {
6263 df->vkCmdBindIndexBuffer(cbD->activeSecondaryCbStack.last(), vkindexbuf, indexOffset, type);
6264 } else {
6265 QVkCommandBuffer::Command &cmd(cbD->commands.get());
6267 cmd.args.bindIndexBuffer.buf = vkindexbuf;
6268 cmd.args.bindIndexBuffer.ofs = indexOffset;
6269 cmd.args.bindIndexBuffer.type = type;
6270 }
6271
6272 trackedRegisterBuffer(&passResTracker, ibufD, slot,
6275 }
6276 }
6277}
6278
6280{
6281 cbD->hasCustomScissorSet = false;
6282
6283 const QSize outputSize = cbD->currentTarget->pixelSize();
6284 std::array<float, 4> vp = cbD->currentViewport.viewport();
6285 float x = 0, y = 0, w = 0, h = 0;
6286
6287 if (qFuzzyIsNull(vp[2]) && qFuzzyIsNull(vp[3])) {
6288 x = 0;
6289 y = 0;
6290 w = outputSize.width();
6291 h = outputSize.height();
6292 } else {
6293 // x,y is top-left in VkRect2D but bottom-left in QRhiScissor
6294 qrhi_toTopLeftRenderTargetRect<Bounded>(outputSize, vp, &x, &y, &w, &h);
6295 }
6296
6297 QVkCommandBuffer::Command &cmd(cbD->commands.get());
6298 VkRect2D *s = &cmd.args.setScissor.scissor;
6299 s->offset.x = int32_t(x);
6300 s->offset.y = int32_t(y);
6301 s->extent.width = uint32_t(w);
6302 s->extent.height = uint32_t(h);
6303
6304 if (cbD->passUsesSecondaryCb) {
6305 df->vkCmdSetScissor(cbD->activeSecondaryCbStack.last(), 0, 1, s);
6306 cbD->commands.unget();
6307 } else {
6309 }
6310}
6311
6312void QRhiVulkan::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport)
6313{
6314 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
6316 const QSize outputSize = cbD->currentTarget->pixelSize();
6317
6318 // x,y is top-left in VkViewport but bottom-left in QRhiViewport
6319 float x, y, w, h;
6320 if (!qrhi_toTopLeftRenderTargetRect<UnBounded>(outputSize, viewport.viewport(), &x, &y, &w, &h))
6321 return;
6322
6323 QVkCommandBuffer::Command &cmd(cbD->commands.get());
6324 VkViewport *vp = &cmd.args.setViewport.viewport;
6325 vp->x = x;
6326 vp->y = y;
6327 vp->width = w;
6328 vp->height = h;
6329 vp->minDepth = viewport.minDepth();
6330 vp->maxDepth = viewport.maxDepth();
6331
6332 if (cbD->passUsesSecondaryCb) {
6333 df->vkCmdSetViewport(cbD->activeSecondaryCbStack.last(), 0, 1, vp);
6334 cbD->commands.unget();
6335 } else {
6337 }
6338
6339 cbD->currentViewport = viewport;
6340 if (cbD->currentGraphicsPipeline
6341 && !cbD->currentGraphicsPipeline->flags().testFlag(QRhiGraphicsPipeline::UsesScissor))
6342 {
6344 }
6345}
6346
6347void QRhiVulkan::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor)
6348{
6349 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
6351 Q_ASSERT(!cbD->currentGraphicsPipeline
6352 || QRHI_RES(QVkGraphicsPipeline, cbD->currentGraphicsPipeline)
6353 ->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor));
6354 const QSize outputSize = cbD->currentTarget->pixelSize();
6355
6356 // x,y is top-left in VkRect2D but bottom-left in QRhiScissor
6357 int x, y, w, h;
6358 if (!qrhi_toTopLeftRenderTargetRect<Bounded>(outputSize, scissor.scissor(), &x, &y, &w, &h))
6359 return;
6360
6361 QVkCommandBuffer::Command &cmd(cbD->commands.get());
6362 VkRect2D *s = &cmd.args.setScissor.scissor;
6363 s->offset.x = x;
6364 s->offset.y = y;
6365 s->extent.width = uint32_t(w);
6366 s->extent.height = uint32_t(h);
6367
6368 if (cbD->passUsesSecondaryCb) {
6369 df->vkCmdSetScissor(cbD->activeSecondaryCbStack.last(), 0, 1, s);
6370 cbD->commands.unget();
6371 } else {
6373 }
6374
6375 cbD->hasCustomScissorSet = true;
6376}
6377
6378void QRhiVulkan::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c)
6379{
6380 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
6382
6383 if (cbD->passUsesSecondaryCb) {
6384 float constants[] = { float(c.redF()), float(c.greenF()), float(c.blueF()), float(c.alphaF()) };
6385 df->vkCmdSetBlendConstants(cbD->activeSecondaryCbStack.last(), constants);
6386 } else {
6387 QVkCommandBuffer::Command &cmd(cbD->commands.get());
6389 cmd.args.setBlendConstants.c[0] = c.redF();
6390 cmd.args.setBlendConstants.c[1] = c.greenF();
6391 cmd.args.setBlendConstants.c[2] = c.blueF();
6392 cmd.args.setBlendConstants.c[3] = c.alphaF();
6393 }
6394}
6395
6396void QRhiVulkan::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
6397{
6398 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
6400
6401 if (cbD->passUsesSecondaryCb) {
6402 df->vkCmdSetStencilReference(cbD->activeSecondaryCbStack.last(), VK_STENCIL_FRONT_AND_BACK, refValue);
6403 } else {
6404 QVkCommandBuffer::Command &cmd(cbD->commands.get());
6406 cmd.args.setStencilRef.ref = refValue;
6407 }
6408}
6409
6410void QRhiVulkan::setShadingRate(QRhiCommandBuffer *cb, const QSize &coarsePixelSize)
6411{
6412#ifdef VK_KHR_fragment_shading_rate
6413 if (!vkCmdSetFragmentShadingRateKHR)
6414 return;
6415
6416 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
6417 Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
6418 Q_ASSERT(!cbD->currentGraphicsPipeline || QRHI_RES(QVkGraphicsPipeline, cbD->currentGraphicsPipeline)->m_flags.testFlag(QRhiGraphicsPipeline::UsesShadingRate));
6419
6420 VkFragmentShadingRateCombinerOpKHR ops[2] = {
6421 VK_FRAGMENT_SHADING_RATE_COMBINER_OP_MAX_KHR,
6422 VK_FRAGMENT_SHADING_RATE_COMBINER_OP_MAX_KHR
6423 };
6424 VkExtent2D size = { uint32_t(coarsePixelSize.width()), uint32_t(coarsePixelSize.height()) };
6425 if (cbD->passUsesSecondaryCb) {
6426 vkCmdSetFragmentShadingRateKHR(cbD->activeSecondaryCbStack.last(), &size, ops);
6427 } else {
6428 QVkCommandBuffer::Command &cmd(cbD->commands.get());
6429 cmd.cmd = QVkCommandBuffer::Command::SetShadingRate;
6430 cmd.args.setShadingRate.w = size.width;
6431 cmd.args.setShadingRate.h = size.height;
6432 }
6433 if (coarsePixelSize.width() != 1 || coarsePixelSize.height() != 1)
6434 cbD->hasShadingRateSet = true;
6435#else
6436 Q_UNUSED(cb);
6437 Q_UNUSED(coarsePixelSize);
6438#endif
6439}
6440
6441void QRhiVulkan::draw(QRhiCommandBuffer *cb, quint32 vertexCount,
6442 quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
6443{
6444 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
6446
6447 if (cbD->passUsesSecondaryCb) {
6448 df->vkCmdDraw(cbD->activeSecondaryCbStack.last(), vertexCount, instanceCount, firstVertex, firstInstance);
6449 } else {
6450 QVkCommandBuffer::Command &cmd(cbD->commands.get());
6452 cmd.args.draw.vertexCount = vertexCount;
6453 cmd.args.draw.instanceCount = instanceCount;
6454 cmd.args.draw.firstVertex = firstVertex;
6455 cmd.args.draw.firstInstance = firstInstance;
6456 }
6457}
6458
6459void QRhiVulkan::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
6460 quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
6461{
6462 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
6464
6465 if (cbD->passUsesSecondaryCb) {
6466 df->vkCmdDrawIndexed(cbD->activeSecondaryCbStack.last(), indexCount, instanceCount,
6467 firstIndex, vertexOffset, firstInstance);
6468 } else {
6469 QVkCommandBuffer::Command &cmd(cbD->commands.get());
6471 cmd.args.drawIndexed.indexCount = indexCount;
6472 cmd.args.drawIndexed.instanceCount = instanceCount;
6473 cmd.args.drawIndexed.firstIndex = firstIndex;
6474 cmd.args.drawIndexed.vertexOffset = vertexOffset;
6475 cmd.args.drawIndexed.firstInstance = firstInstance;
6476 }
6477}
6478
6479void QRhiVulkan::drawIndirect(QRhiCommandBuffer *cb, QRhiBuffer *indirectBuffer,
6480 quint32 indirectBufferOffset, quint32 drawCount, quint32 stride)
6481{
6482 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
6484 QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
6485
6486 QVkBuffer *indirectBufD = QRHI_RES(QVkBuffer, indirectBuffer);
6487 indirectBufD->lastActiveFrameSlot = currentFrameSlot;
6488 if (indirectBufD->m_type == QRhiBuffer::Dynamic)
6489 executeBufferHostWritesForSlot(indirectBufD, currentFrameSlot);
6490 const int indirectBufSlot = indirectBufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0;
6491 trackedRegisterBuffer(&passResTracker, indirectBufD, indirectBufSlot,
6494 VkBuffer indirectBufVk = indirectBufD->buffers[indirectBufSlot];
6495
6496 if (cbD->passUsesSecondaryCb) {
6497 if (caps.drawIndirectMulti) {
6498 df->vkCmdDrawIndirect(cbD->activeSecondaryCbStack.last(),
6499 indirectBufVk, indirectBufferOffset,
6500 drawCount, stride);
6501 } else {
6502 VkDeviceSize offset = indirectBufferOffset;
6503 for (quint32 i = 0; i < drawCount; ++i) {
6504 df->vkCmdDrawIndirect(cbD->activeSecondaryCbStack.last(),
6505 indirectBufVk, offset,
6506 1, stride);
6507 offset += stride;
6508 }
6509 }
6510 } else {
6511 if (caps.drawIndirectMulti) {
6512 QVkCommandBuffer::Command &cmd(cbD->commands.get());
6514 cmd.args.drawIndirect.indirectBuffer = indirectBufVk;
6515 cmd.args.drawIndirect.indirectBufferOffset = indirectBufferOffset;
6516 cmd.args.drawIndirect.drawCount = drawCount;
6517 cmd.args.drawIndirect.stride = stride;
6518 } else {
6519 VkDeviceSize offset = indirectBufferOffset;
6520 for (quint32 i = 0; i < drawCount; ++i) {
6521 QVkCommandBuffer::Command &cmd(cbD->commands.get());
6523 cmd.args.drawIndirect.indirectBuffer = indirectBufVk;
6524 cmd.args.drawIndirect.indirectBufferOffset = offset;
6525 cmd.args.drawIndirect.drawCount = 1;
6526 cmd.args.drawIndirect.stride = stride;
6527 offset += stride;
6528 }
6529 }
6530 }
6531}
6532
6533void QRhiVulkan::drawIndexedIndirect(QRhiCommandBuffer *cb, QRhiBuffer *indirectBuffer,
6534 quint32 indirectBufferOffset, quint32 drawCount, quint32 stride)
6535{
6536 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
6538 QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
6539
6540 QVkBuffer *indirectBufD = QRHI_RES(QVkBuffer, indirectBuffer);
6541 indirectBufD->lastActiveFrameSlot = currentFrameSlot;
6542 if (indirectBufD->m_type == QRhiBuffer::Dynamic)
6543 executeBufferHostWritesForSlot(indirectBufD, currentFrameSlot);
6544 const int indirectBufSlot = indirectBufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0;
6545 trackedRegisterBuffer(&passResTracker, indirectBufD, indirectBufSlot,
6548 VkBuffer indirectBufVk = indirectBufD->buffers[indirectBufSlot];
6549
6550 if (cbD->passUsesSecondaryCb) {
6551 if (caps.drawIndirectMulti) {
6552 df->vkCmdDrawIndexedIndirect(cbD->activeSecondaryCbStack.last(),
6553 indirectBufVk, indirectBufferOffset,
6554 drawCount, stride);
6555 } else {
6556 VkDeviceSize offset = indirectBufferOffset;
6557 for (quint32 i = 0; i < drawCount; ++i) {
6558 df->vkCmdDrawIndexedIndirect(cbD->activeSecondaryCbStack.last(),
6559 indirectBufVk, offset,
6560 1, stride);
6561 offset += stride;
6562 }
6563 }
6564 } else {
6565 if (caps.drawIndirectMulti) {
6566 QVkCommandBuffer::Command &cmd(cbD->commands.get());
6568 cmd.args.drawIndexedIndirect.indirectBuffer = indirectBufVk;
6569 cmd.args.drawIndexedIndirect.indirectBufferOffset = indirectBufferOffset;
6570 cmd.args.drawIndexedIndirect.drawCount = drawCount;
6571 cmd.args.drawIndexedIndirect.stride = stride;
6572 } else {
6573 VkDeviceSize offset = indirectBufferOffset;
6574 for (quint32 i = 0; i < drawCount; ++i) {
6575 QVkCommandBuffer::Command &cmd(cbD->commands.get());
6577 cmd.args.drawIndexedIndirect.indirectBuffer = indirectBufVk;
6578 cmd.args.drawIndexedIndirect.indirectBufferOffset = offset;
6579 cmd.args.drawIndexedIndirect.drawCount = 1;
6580 cmd.args.drawIndexedIndirect.stride = stride;
6581 offset += stride;
6582 }
6583 }
6584 }
6585}
6586
6587void QRhiVulkan::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name)
6588{
6589#ifdef VK_EXT_debug_utils
6590 if (!debugMarkers || !caps.debugUtils)
6591 return;
6592
6593 VkDebugUtilsLabelEXT label = {};
6594 label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
6595
6596 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
6597 if (cbD->recordingPass != QVkCommandBuffer::NoPass && cbD->passUsesSecondaryCb) {
6598 label.pLabelName = name.constData();
6599 vkCmdBeginDebugUtilsLabelEXT(cbD->activeSecondaryCbStack.last(), &label);
6600 } else {
6601 QVkCommandBuffer::Command &cmd(cbD->commands.get());
6602 cmd.cmd = QVkCommandBuffer::Command::DebugMarkerBegin;
6603 cmd.args.debugMarkerBegin.label = label;
6604 cmd.args.debugMarkerBegin.labelNameIndex = cbD->pools.debugMarkerData.size();
6605 cbD->pools.debugMarkerData.append(name);
6606 }
6607#else
6608 Q_UNUSED(cb);
6609 Q_UNUSED(name);
6610#endif
6611}
6612
6613void QRhiVulkan::debugMarkEnd(QRhiCommandBuffer *cb)
6614{
6615#ifdef VK_EXT_debug_utils
6616 if (!debugMarkers || !caps.debugUtils)
6617 return;
6618
6619 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
6620 if (cbD->recordingPass != QVkCommandBuffer::NoPass && cbD->passUsesSecondaryCb) {
6621 vkCmdEndDebugUtilsLabelEXT(cbD->activeSecondaryCbStack.last());
6622 } else {
6623 QVkCommandBuffer::Command &cmd(cbD->commands.get());
6624 cmd.cmd = QVkCommandBuffer::Command::DebugMarkerEnd;
6625 }
6626#else
6627 Q_UNUSED(cb);
6628#endif
6629}
6630
6631void QRhiVulkan::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg)
6632{
6633#ifdef VK_EXT_debug_utils
6634 if (!debugMarkers || !caps.debugUtils)
6635 return;
6636
6637 VkDebugUtilsLabelEXT label = {};
6638 label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
6639
6640 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
6641 if (cbD->recordingPass != QVkCommandBuffer::NoPass && cbD->passUsesSecondaryCb) {
6642 label.pLabelName = msg.constData();
6643 vkCmdInsertDebugUtilsLabelEXT(cbD->activeSecondaryCbStack.last(), &label);
6644 } else {
6645 QVkCommandBuffer::Command &cmd(cbD->commands.get());
6646 cmd.cmd = QVkCommandBuffer::Command::DebugMarkerInsert;
6647 cmd.args.debugMarkerInsert.label = label;
6648 cmd.args.debugMarkerInsert.labelNameIndex = cbD->pools.debugMarkerData.size();
6649 cbD->pools.debugMarkerData.append(msg);
6650 }
6651#else
6652 Q_UNUSED(cb);
6653 Q_UNUSED(msg);
6654#endif
6655}
6656
6657const QRhiNativeHandles *QRhiVulkan::nativeHandles(QRhiCommandBuffer *cb)
6658{
6659 return QRHI_RES(QVkCommandBuffer, cb)->nativeHandles();
6660}
6661
6663{
6664 Q_ASSERT(cbD->currentTarget);
6665 QVkRenderTargetData *rtD = nullptr;
6667 switch (cbD->currentTarget->resourceType()) {
6668 case QRhiResource::SwapChainRenderTarget:
6669 rtD = &QRHI_RES(QVkSwapChainRenderTarget, cbD->currentTarget)->d;
6670 break;
6671 case QRhiResource::TextureRenderTarget:
6672 rtD = &QRHI_RES(QVkTextureRenderTarget, cbD->currentTarget)->d;
6673 break;
6674 default:
6675 Q_UNREACHABLE();
6676 break;
6677 }
6678 }
6679 return rtD;
6680}
6681
6682void QRhiVulkan::beginExternal(QRhiCommandBuffer *cb)
6683{
6684 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
6685
6686 // When not in a pass, it is simple: record what we have (but do not
6687 // submit), the cb can then be used to record more external commands.
6691 return;
6692 }
6693
6694 // Otherwise, inside a pass, have a secondary command buffer (with
6695 // RENDER_PASS_CONTINUE). Using the main one is not acceptable since we
6696 // cannot just record at this stage, that would mess up the resource
6697 // tracking and commands like TransitionPassResources.
6698
6699 if (cbD->inExternal)
6700 return;
6701
6702 if (!cbD->passUsesSecondaryCb) {
6703 qWarning("beginExternal() within a pass is only supported with secondary command buffers. "
6704 "This can be enabled by passing QRhiCommandBuffer::ExternalContent to beginPass().");
6705 return;
6706 }
6707
6708 VkCommandBuffer secondaryCb = cbD->activeSecondaryCbStack.last();
6709 cbD->activeSecondaryCbStack.removeLast();
6710 endAndEnqueueSecondaryCommandBuffer(secondaryCb, cbD);
6711
6712 VkCommandBuffer extCb = startSecondaryCommandBuffer(maybeRenderTargetData(cbD));
6713 if (extCb) {
6714 cbD->activeSecondaryCbStack.append(extCb);
6715 cbD->inExternal = true;
6716 }
6717}
6718
6719void QRhiVulkan::endExternal(QRhiCommandBuffer *cb)
6720{
6721 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
6722
6724 Q_ASSERT(cbD->commands.isEmpty() && cbD->currentPassResTrackerIndex == -1);
6725 } else if (cbD->inExternal) {
6726 VkCommandBuffer extCb = cbD->activeSecondaryCbStack.last();
6727 cbD->activeSecondaryCbStack.removeLast();
6728 endAndEnqueueSecondaryCommandBuffer(extCb, cbD);
6729 cbD->activeSecondaryCbStack.append(startSecondaryCommandBuffer(maybeRenderTargetData(cbD)));
6730 }
6731
6733}
6734
6735double QRhiVulkan::lastCompletedGpuTime(QRhiCommandBuffer *cb)
6736{
6737 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
6738 return cbD->lastGpuTime;
6739}
6740
6741void QRhiVulkan::setAllocationName(QVkAlloc allocation, const QByteArray &name, int slot)
6742{
6743 if (!debugMarkers || name.isEmpty())
6744 return;
6745
6746 QByteArray decoratedName = name;
6747 if (slot >= 0) {
6748 decoratedName += '/';
6749 decoratedName += QByteArray::number(slot);
6750 }
6751 vmaSetAllocationName(toVmaAllocator(allocator), toVmaAllocation(allocation), decoratedName.constData());
6752}
6753
6754void QRhiVulkan::setObjectName(uint64_t object, VkObjectType type, const QByteArray &name, int slot)
6755{
6756#ifdef VK_EXT_debug_utils
6757 if (!debugMarkers || !caps.debugUtils || name.isEmpty())
6758 return;
6759
6760 VkDebugUtilsObjectNameInfoEXT nameInfo = {};
6761 nameInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
6762 nameInfo.objectType = type;
6763 nameInfo.objectHandle = object;
6764 QByteArray decoratedName = name;
6765 if (slot >= 0) {
6766 decoratedName += '/';
6767 decoratedName += QByteArray::number(slot);
6768 }
6769 nameInfo.pObjectName = decoratedName.constData();
6770 vkSetDebugUtilsObjectNameEXT(dev, &nameInfo);
6771#else
6772 Q_UNUSED(object);
6773 Q_UNUSED(type);
6774 Q_UNUSED(name);
6775 Q_UNUSED(slot);
6776#endif
6777}
6778
6779static inline VkBufferUsageFlagBits toVkBufferUsage(QRhiBuffer::UsageFlags usage)
6780{
6781 int u = 0;
6782 if (usage.testFlag(QRhiBuffer::VertexBuffer))
6783 u |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
6784 if (usage.testFlag(QRhiBuffer::IndexBuffer))
6785 u |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
6786 if (usage.testFlag(QRhiBuffer::UniformBuffer))
6787 u |= VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
6788 if (usage.testFlag(QRhiBuffer::StorageBuffer))
6789 u |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
6790 if (usage.testFlag(QRhiBuffer::IndirectBuffer))
6791 u |= VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
6792 return VkBufferUsageFlagBits(u);
6793}
6794
6795static inline VkFilter toVkFilter(QRhiSampler::Filter f)
6796{
6797 switch (f) {
6798 case QRhiSampler::Nearest:
6799 return VK_FILTER_NEAREST;
6800 case QRhiSampler::Linear:
6801 return VK_FILTER_LINEAR;
6802 default:
6803 Q_UNREACHABLE_RETURN(VK_FILTER_NEAREST);
6804 }
6805}
6806
6807static inline VkSamplerMipmapMode toVkMipmapMode(QRhiSampler::Filter f)
6808{
6809 switch (f) {
6810 case QRhiSampler::None:
6811 return VK_SAMPLER_MIPMAP_MODE_NEAREST;
6812 case QRhiSampler::Nearest:
6813 return VK_SAMPLER_MIPMAP_MODE_NEAREST;
6814 case QRhiSampler::Linear:
6815 return VK_SAMPLER_MIPMAP_MODE_LINEAR;
6816 default:
6817 Q_UNREACHABLE_RETURN(VK_SAMPLER_MIPMAP_MODE_NEAREST);
6818 }
6819}
6820
6821static inline VkSamplerAddressMode toVkAddressMode(QRhiSampler::AddressMode m)
6822{
6823 switch (m) {
6824 case QRhiSampler::Repeat:
6825 return VK_SAMPLER_ADDRESS_MODE_REPEAT;
6826 case QRhiSampler::ClampToEdge:
6827 return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
6828 case QRhiSampler::Mirror:
6829 return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
6830 default:
6831 Q_UNREACHABLE_RETURN(VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE);
6832 }
6833}
6834
6835static inline VkShaderStageFlagBits toVkShaderStage(QRhiShaderStage::Type type)
6836{
6837 switch (type) {
6838 case QRhiShaderStage::Vertex:
6839 return VK_SHADER_STAGE_VERTEX_BIT;
6840 case QRhiShaderStage::TessellationControl:
6841 return VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
6842 case QRhiShaderStage::TessellationEvaluation:
6843 return VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
6844 case QRhiShaderStage::Fragment:
6845 return VK_SHADER_STAGE_FRAGMENT_BIT;
6846 case QRhiShaderStage::Compute:
6847 return VK_SHADER_STAGE_COMPUTE_BIT;
6848 case QRhiShaderStage::Geometry:
6849 return VK_SHADER_STAGE_GEOMETRY_BIT;
6850 default:
6851 Q_UNREACHABLE_RETURN(VK_SHADER_STAGE_VERTEX_BIT);
6852 }
6853}
6854
6855static inline VkFormat toVkAttributeFormat(QRhiVertexInputAttribute::Format format)
6856{
6857 switch (format) {
6858 case QRhiVertexInputAttribute::Float4:
6859 return VK_FORMAT_R32G32B32A32_SFLOAT;
6860 case QRhiVertexInputAttribute::Float3:
6861 return VK_FORMAT_R32G32B32_SFLOAT;
6862 case QRhiVertexInputAttribute::Float2:
6863 return VK_FORMAT_R32G32_SFLOAT;
6864 case QRhiVertexInputAttribute::Float:
6865 return VK_FORMAT_R32_SFLOAT;
6866 case QRhiVertexInputAttribute::UNormByte4:
6867 return VK_FORMAT_R8G8B8A8_UNORM;
6868 case QRhiVertexInputAttribute::UNormByte2:
6869 return VK_FORMAT_R8G8_UNORM;
6870 case QRhiVertexInputAttribute::UNormByte:
6871 return VK_FORMAT_R8_UNORM;
6872 case QRhiVertexInputAttribute::UInt4:
6873 return VK_FORMAT_R32G32B32A32_UINT;
6874 case QRhiVertexInputAttribute::UInt3:
6875 return VK_FORMAT_R32G32B32_UINT;
6876 case QRhiVertexInputAttribute::UInt2:
6877 return VK_FORMAT_R32G32_UINT;
6878 case QRhiVertexInputAttribute::UInt:
6879 return VK_FORMAT_R32_UINT;
6880 case QRhiVertexInputAttribute::SInt4:
6881 return VK_FORMAT_R32G32B32A32_SINT;
6882 case QRhiVertexInputAttribute::SInt3:
6883 return VK_FORMAT_R32G32B32_SINT;
6884 case QRhiVertexInputAttribute::SInt2:
6885 return VK_FORMAT_R32G32_SINT;
6886 case QRhiVertexInputAttribute::SInt:
6887 return VK_FORMAT_R32_SINT;
6888 case QRhiVertexInputAttribute::Half4:
6889 return VK_FORMAT_R16G16B16A16_SFLOAT;
6890 case QRhiVertexInputAttribute::Half3:
6891 return VK_FORMAT_R16G16B16_SFLOAT;
6892 case QRhiVertexInputAttribute::Half2:
6893 return VK_FORMAT_R16G16_SFLOAT;
6894 case QRhiVertexInputAttribute::Half:
6895 return VK_FORMAT_R16_SFLOAT;
6896 case QRhiVertexInputAttribute::UShort4:
6897 return VK_FORMAT_R16G16B16A16_UINT;
6898 case QRhiVertexInputAttribute::UShort3:
6899 return VK_FORMAT_R16G16B16_UINT;
6900 case QRhiVertexInputAttribute::UShort2:
6901 return VK_FORMAT_R16G16_UINT;
6902 case QRhiVertexInputAttribute::UShort:
6903 return VK_FORMAT_R16_UINT;
6904 case QRhiVertexInputAttribute::SShort4:
6905 return VK_FORMAT_R16G16B16A16_SINT;
6906 case QRhiVertexInputAttribute::SShort3:
6907 return VK_FORMAT_R16G16B16_SINT;
6908 case QRhiVertexInputAttribute::SShort2:
6909 return VK_FORMAT_R16G16_SINT;
6910 case QRhiVertexInputAttribute::SShort:
6911 return VK_FORMAT_R16_SINT;
6912 default:
6913 Q_UNREACHABLE_RETURN(VK_FORMAT_R32G32B32A32_SFLOAT);
6914 }
6915}
6916
6917static inline VkPrimitiveTopology toVkTopology(QRhiGraphicsPipeline::Topology t)
6918{
6919 switch (t) {
6920 case QRhiGraphicsPipeline::Triangles:
6921 return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
6922 case QRhiGraphicsPipeline::TriangleStrip:
6923 return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
6924 case QRhiGraphicsPipeline::TriangleFan:
6925 return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN;
6926 case QRhiGraphicsPipeline::Lines:
6927 return VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
6928 case QRhiGraphicsPipeline::LineStrip:
6929 return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
6930 case QRhiGraphicsPipeline::Points:
6931 return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
6932 case QRhiGraphicsPipeline::Patches:
6933 return VK_PRIMITIVE_TOPOLOGY_PATCH_LIST;
6934 default:
6935 Q_UNREACHABLE_RETURN(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
6936 }
6937}
6938
6939static inline VkCullModeFlags toVkCullMode(QRhiGraphicsPipeline::CullMode c)
6940{
6941 switch (c) {
6942 case QRhiGraphicsPipeline::None:
6943 return VK_CULL_MODE_NONE;
6944 case QRhiGraphicsPipeline::Front:
6945 return VK_CULL_MODE_FRONT_BIT;
6946 case QRhiGraphicsPipeline::Back:
6947 return VK_CULL_MODE_BACK_BIT;
6948 default:
6949 Q_UNREACHABLE_RETURN(VK_CULL_MODE_NONE);
6950 }
6951}
6952
6953static inline VkFrontFace toVkFrontFace(QRhiGraphicsPipeline::FrontFace f)
6954{
6955 switch (f) {
6956 case QRhiGraphicsPipeline::CCW:
6957 return VK_FRONT_FACE_COUNTER_CLOCKWISE;
6958 case QRhiGraphicsPipeline::CW:
6959 return VK_FRONT_FACE_CLOCKWISE;
6960 default:
6961 Q_UNREACHABLE_RETURN(VK_FRONT_FACE_COUNTER_CLOCKWISE);
6962 }
6963}
6964
6965static inline VkColorComponentFlags toVkColorComponents(QRhiGraphicsPipeline::ColorMask c)
6966{
6967 int f = 0;
6968 if (c.testFlag(QRhiGraphicsPipeline::R))
6969 f |= VK_COLOR_COMPONENT_R_BIT;
6970 if (c.testFlag(QRhiGraphicsPipeline::G))
6971 f |= VK_COLOR_COMPONENT_G_BIT;
6972 if (c.testFlag(QRhiGraphicsPipeline::B))
6973 f |= VK_COLOR_COMPONENT_B_BIT;
6974 if (c.testFlag(QRhiGraphicsPipeline::A))
6975 f |= VK_COLOR_COMPONENT_A_BIT;
6976 return VkColorComponentFlags(f);
6977}
6978
6979static inline VkBlendFactor toVkBlendFactor(QRhiGraphicsPipeline::BlendFactor f)
6980{
6981 switch (f) {
6982 case QRhiGraphicsPipeline::Zero:
6983 return VK_BLEND_FACTOR_ZERO;
6984 case QRhiGraphicsPipeline::One:
6985 return VK_BLEND_FACTOR_ONE;
6986 case QRhiGraphicsPipeline::SrcColor:
6987 return VK_BLEND_FACTOR_SRC_COLOR;
6988 case QRhiGraphicsPipeline::OneMinusSrcColor:
6989 return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR;
6990 case QRhiGraphicsPipeline::DstColor:
6991 return VK_BLEND_FACTOR_DST_COLOR;
6992 case QRhiGraphicsPipeline::OneMinusDstColor:
6993 return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR;
6994 case QRhiGraphicsPipeline::SrcAlpha:
6995 return VK_BLEND_FACTOR_SRC_ALPHA;
6996 case QRhiGraphicsPipeline::OneMinusSrcAlpha:
6997 return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
6998 case QRhiGraphicsPipeline::DstAlpha:
6999 return VK_BLEND_FACTOR_DST_ALPHA;
7000 case QRhiGraphicsPipeline::OneMinusDstAlpha:
7001 return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;
7002 case QRhiGraphicsPipeline::ConstantColor:
7003 return VK_BLEND_FACTOR_CONSTANT_COLOR;
7004 case QRhiGraphicsPipeline::OneMinusConstantColor:
7005 return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR;
7006 case QRhiGraphicsPipeline::ConstantAlpha:
7007 return VK_BLEND_FACTOR_CONSTANT_ALPHA;
7008 case QRhiGraphicsPipeline::OneMinusConstantAlpha:
7009 return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA;
7010 case QRhiGraphicsPipeline::SrcAlphaSaturate:
7011 return VK_BLEND_FACTOR_SRC_ALPHA_SATURATE;
7012 case QRhiGraphicsPipeline::Src1Color:
7013 return VK_BLEND_FACTOR_SRC1_COLOR;
7014 case QRhiGraphicsPipeline::OneMinusSrc1Color:
7015 return VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR;
7016 case QRhiGraphicsPipeline::Src1Alpha:
7017 return VK_BLEND_FACTOR_SRC1_ALPHA;
7018 case QRhiGraphicsPipeline::OneMinusSrc1Alpha:
7019 return VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA;
7020 default:
7021 Q_UNREACHABLE_RETURN(VK_BLEND_FACTOR_ZERO);
7022 }
7023}
7024
7025static inline VkBlendOp toVkBlendOp(QRhiGraphicsPipeline::BlendOp op)
7026{
7027 switch (op) {
7028 case QRhiGraphicsPipeline::Add:
7029 return VK_BLEND_OP_ADD;
7030 case QRhiGraphicsPipeline::Subtract:
7031 return VK_BLEND_OP_SUBTRACT;
7032 case QRhiGraphicsPipeline::ReverseSubtract:
7033 return VK_BLEND_OP_REVERSE_SUBTRACT;
7034 case QRhiGraphicsPipeline::Min:
7035 return VK_BLEND_OP_MIN;
7036 case QRhiGraphicsPipeline::Max:
7037 return VK_BLEND_OP_MAX;
7038 default:
7039 Q_UNREACHABLE_RETURN(VK_BLEND_OP_ADD);
7040 }
7041}
7042
7043static inline VkCompareOp toVkCompareOp(QRhiGraphicsPipeline::CompareOp op)
7044{
7045 switch (op) {
7046 case QRhiGraphicsPipeline::Never:
7047 return VK_COMPARE_OP_NEVER;
7048 case QRhiGraphicsPipeline::Less:
7049 return VK_COMPARE_OP_LESS;
7050 case QRhiGraphicsPipeline::Equal:
7051 return VK_COMPARE_OP_EQUAL;
7052 case QRhiGraphicsPipeline::LessOrEqual:
7053 return VK_COMPARE_OP_LESS_OR_EQUAL;
7054 case QRhiGraphicsPipeline::Greater:
7055 return VK_COMPARE_OP_GREATER;
7056 case QRhiGraphicsPipeline::NotEqual:
7057 return VK_COMPARE_OP_NOT_EQUAL;
7058 case QRhiGraphicsPipeline::GreaterOrEqual:
7059 return VK_COMPARE_OP_GREATER_OR_EQUAL;
7060 case QRhiGraphicsPipeline::Always:
7061 return VK_COMPARE_OP_ALWAYS;
7062 default:
7063 Q_UNREACHABLE_RETURN(VK_COMPARE_OP_ALWAYS);
7064 }
7065}
7066
7067static inline VkStencilOp toVkStencilOp(QRhiGraphicsPipeline::StencilOp op)
7068{
7069 switch (op) {
7070 case QRhiGraphicsPipeline::StencilZero:
7071 return VK_STENCIL_OP_ZERO;
7072 case QRhiGraphicsPipeline::Keep:
7073 return VK_STENCIL_OP_KEEP;
7074 case QRhiGraphicsPipeline::Replace:
7075 return VK_STENCIL_OP_REPLACE;
7076 case QRhiGraphicsPipeline::IncrementAndClamp:
7077 return VK_STENCIL_OP_INCREMENT_AND_CLAMP;
7078 case QRhiGraphicsPipeline::DecrementAndClamp:
7079 return VK_STENCIL_OP_DECREMENT_AND_CLAMP;
7080 case QRhiGraphicsPipeline::Invert:
7081 return VK_STENCIL_OP_INVERT;
7082 case QRhiGraphicsPipeline::IncrementAndWrap:
7083 return VK_STENCIL_OP_INCREMENT_AND_WRAP;
7084 case QRhiGraphicsPipeline::DecrementAndWrap:
7085 return VK_STENCIL_OP_DECREMENT_AND_WRAP;
7086 default:
7087 Q_UNREACHABLE_RETURN(VK_STENCIL_OP_KEEP);
7088 }
7089}
7090
7091static inline VkPolygonMode toVkPolygonMode(QRhiGraphicsPipeline::PolygonMode mode)
7092{
7093 switch (mode) {
7094 case QRhiGraphicsPipeline::Fill:
7095 return VK_POLYGON_MODE_FILL;
7096 case QRhiGraphicsPipeline::Line:
7097 return VK_POLYGON_MODE_LINE;
7098 default:
7099 Q_UNREACHABLE_RETURN(VK_POLYGON_MODE_FILL);
7100 }
7101}
7102
7103static inline void fillVkStencilOpState(VkStencilOpState *dst, const QRhiGraphicsPipeline::StencilOpState &src)
7104{
7105 dst->failOp = toVkStencilOp(src.failOp);
7106 dst->passOp = toVkStencilOp(src.passOp);
7107 dst->depthFailOp = toVkStencilOp(src.depthFailOp);
7108 dst->compareOp = toVkCompareOp(src.compareOp);
7109}
7110
7111static inline VkDescriptorType toVkDescriptorType(const QRhiShaderResourceBinding::Data *b)
7112{
7113 switch (b->type) {
7114 case QRhiShaderResourceBinding::UniformBuffer:
7115 return b->u.ubuf.hasDynamicOffset ? VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC
7116 : VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
7117
7118 case QRhiShaderResourceBinding::SampledTexture:
7119 return VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
7120
7121 case QRhiShaderResourceBinding::Texture:
7122 return VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
7123
7124 case QRhiShaderResourceBinding::Sampler:
7125 return VK_DESCRIPTOR_TYPE_SAMPLER;
7126
7127 case QRhiShaderResourceBinding::ImageLoad:
7128 case QRhiShaderResourceBinding::ImageStore:
7129 case QRhiShaderResourceBinding::ImageLoadStore:
7130 return VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
7131
7132 case QRhiShaderResourceBinding::BufferLoad:
7133 case QRhiShaderResourceBinding::BufferStore:
7134 case QRhiShaderResourceBinding::BufferLoadStore:
7135 return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
7136
7137 default:
7138 Q_UNREACHABLE_RETURN(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
7139 }
7140}
7141
7142static inline VkShaderStageFlags toVkShaderStageFlags(QRhiShaderResourceBinding::StageFlags stage)
7143{
7144 int s = 0;
7145 if (stage.testFlag(QRhiShaderResourceBinding::VertexStage))
7146 s |= VK_SHADER_STAGE_VERTEX_BIT;
7147 if (stage.testFlag(QRhiShaderResourceBinding::FragmentStage))
7148 s |= VK_SHADER_STAGE_FRAGMENT_BIT;
7149 if (stage.testFlag(QRhiShaderResourceBinding::ComputeStage))
7150 s |= VK_SHADER_STAGE_COMPUTE_BIT;
7151 if (stage.testFlag(QRhiShaderResourceBinding::TessellationControlStage))
7152 s |= VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
7153 if (stage.testFlag(QRhiShaderResourceBinding::TessellationEvaluationStage))
7154 s |= VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
7155 if (stage.testFlag(QRhiShaderResourceBinding::GeometryStage))
7156 s |= VK_SHADER_STAGE_GEOMETRY_BIT;
7157 return VkShaderStageFlags(s);
7158}
7159
7160static inline VkCompareOp toVkTextureCompareOp(QRhiSampler::CompareOp op)
7161{
7162 switch (op) {
7163 case QRhiSampler::Never:
7164 return VK_COMPARE_OP_NEVER;
7165 case QRhiSampler::Less:
7166 return VK_COMPARE_OP_LESS;
7167 case QRhiSampler::Equal:
7168 return VK_COMPARE_OP_EQUAL;
7169 case QRhiSampler::LessOrEqual:
7170 return VK_COMPARE_OP_LESS_OR_EQUAL;
7171 case QRhiSampler::Greater:
7172 return VK_COMPARE_OP_GREATER;
7173 case QRhiSampler::NotEqual:
7174 return VK_COMPARE_OP_NOT_EQUAL;
7175 case QRhiSampler::GreaterOrEqual:
7176 return VK_COMPARE_OP_GREATER_OR_EQUAL;
7177 case QRhiSampler::Always:
7178 return VK_COMPARE_OP_ALWAYS;
7179 default:
7180 Q_UNREACHABLE_RETURN(VK_COMPARE_OP_NEVER);
7181 }
7182}
7183
7184QVkBuffer::QVkBuffer(QRhiImplementation *rhi, Type type, UsageFlags usage, quint32 size)
7186{
7187 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
7188 buffers[i] = stagingBuffers[i] = VK_NULL_HANDLE;
7189 allocations[i] = stagingAllocations[i] = nullptr;
7190 }
7191}
7192
7194{
7195 destroy();
7196}
7197
7199{
7200 if (!buffers[0])
7201 return;
7202
7206
7207 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
7208 e.buffer.buffers[i] = buffers[i];
7209 e.buffer.allocations[i] = allocations[i];
7210 e.buffer.stagingBuffers[i] = stagingBuffers[i];
7211 e.buffer.stagingAllocations[i] = stagingAllocations[i];
7212
7213 buffers[i] = VK_NULL_HANDLE;
7214 allocations[i] = nullptr;
7215 stagingBuffers[i] = VK_NULL_HANDLE;
7216 stagingAllocations[i] = nullptr;
7217 pendingDynamicUpdates[i].clear();
7218 }
7219
7220 QRHI_RES_RHI(QRhiVulkan);
7221 // destroy() implementations, unlike other functions, are expected to test
7222 // for m_rhi being null, to allow surviving in case one attempts to destroy
7223 // a (leaked) resource after the QRhi.
7224 if (rhiD) {
7225 rhiD->releaseQueue.append(e);
7226 rhiD->unregisterResource(this);
7227 }
7228}
7229
7231{
7232 if (buffers[0])
7233 destroy();
7234
7235 if (m_usage.testFlag(QRhiBuffer::StorageBuffer) && m_type == Dynamic) {
7236 qWarning("StorageBuffer cannot be combined with Dynamic");
7237 return false;
7238 }
7239
7240 const quint32 nonZeroSize = m_size <= 0 ? 256 : m_size;
7241
7242 VkBufferCreateInfo bufferInfo = {};
7243 bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
7244 bufferInfo.size = nonZeroSize;
7245 bufferInfo.usage = toVkBufferUsage(m_usage);
7246
7247 VmaAllocationCreateInfo allocInfo = {};
7248
7249 if (m_type == Dynamic) {
7250#ifndef Q_OS_DARWIN // not for MoltenVK
7251 // Keep mapped all the time. Essential f.ex. with some mobile GPUs,
7252 // where mapping and unmapping an entire allocation every time updating
7253 // a suballocated buffer presents a significant perf. hit.
7254 allocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
7255#endif
7256 // host visible, frequent changes
7257 allocInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
7258 } else {
7259 allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
7260 bufferInfo.usage |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
7261 }
7262
7263 QRHI_RES_RHI(QRhiVulkan);
7264 VkResult err = VK_SUCCESS;
7265 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
7266 buffers[i] = VK_NULL_HANDLE;
7267 allocations[i] = nullptr;
7268 usageState[i].access = usageState[i].stage = 0;
7269 if (i == 0 || m_type == Dynamic) {
7270 VmaAllocation allocation;
7271 err = vmaCreateBuffer(toVmaAllocator(rhiD->allocator), &bufferInfo, &allocInfo, &buffers[i], &allocation, nullptr);
7272 if (err != VK_SUCCESS)
7273 break;
7274 allocations[i] = allocation;
7275 rhiD->setAllocationName(allocation, m_objectName, m_type == Dynamic ? i : -1);
7276 rhiD->setObjectName(uint64_t(buffers[i]), VK_OBJECT_TYPE_BUFFER, m_objectName,
7277 m_type == Dynamic ? i : -1);
7278 }
7279 }
7280
7281 if (err != VK_SUCCESS) {
7282 qWarning("Failed to create buffer of size %u: %d", nonZeroSize, err);
7283 rhiD->printExtraErrorInfo(err);
7284 return false;
7285 }
7286
7288 generation += 1;
7289 rhiD->registerResource(this);
7290 return true;
7291}
7292
7294{
7295 if (m_type == Dynamic) {
7296 QRHI_RES_RHI(QRhiVulkan);
7297 NativeBuffer b;
7298 Q_ASSERT(sizeof(b.objects) / sizeof(b.objects[0]) >= size_t(QVK_FRAMES_IN_FLIGHT));
7299 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
7301 b.objects[i] = &buffers[i];
7302 }
7303 b.slotCount = QVK_FRAMES_IN_FLIGHT;
7304 return b;
7305 }
7306 return { { &buffers[0] }, 1 };
7307}
7308
7310{
7311 // Shortcut the entire buffer update mechanism and allow the client to do
7312 // the host writes directly to the buffer. This will lead to unexpected
7313 // results when combined with QRhiResourceUpdateBatch-based updates for the
7314 // buffer, but provides a fast path for dynamic buffers that have all their
7315 // content changed in every frame.
7316 Q_ASSERT(m_type == Dynamic);
7317 QRHI_RES_RHI(QRhiVulkan);
7318 Q_ASSERT(rhiD->inFrame);
7319 const int slot = rhiD->currentFrameSlot;
7320 void *p = nullptr;
7321 VmaAllocation a = toVmaAllocation(allocations[slot]);
7322 VkResult err = vmaMapMemory(toVmaAllocator(rhiD->allocator), a, &p);
7323 if (err != VK_SUCCESS) {
7324 qWarning("Failed to map buffer: %d", err);
7325 return nullptr;
7326 }
7327 return static_cast<char *>(p);
7328}
7329
7331{
7332 QRHI_RES_RHI(QRhiVulkan);
7333 const int slot = rhiD->currentFrameSlot;
7334 VmaAllocation a = toVmaAllocation(allocations[slot]);
7335 vmaFlushAllocation(toVmaAllocator(rhiD->allocator), a, 0, m_size);
7336 vmaUnmapMemory(toVmaAllocator(rhiD->allocator), a);
7337}
7338
7339QVkRenderBuffer::QVkRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
7340 int sampleCount, Flags flags,
7341 QRhiTexture::Format backingFormatHint)
7343{
7344}
7345
7347{
7348 destroy();
7349 delete backingTexture;
7350}
7351
7353{
7354 if (!memory && !backingTexture)
7355 return;
7356
7360
7361 e.renderBuffer.memory = memory;
7362 e.renderBuffer.image = image;
7363 e.renderBuffer.imageView = imageView;
7364
7365 memory = VK_NULL_HANDLE;
7366 image = VK_NULL_HANDLE;
7367 imageView = VK_NULL_HANDLE;
7368
7369 if (backingTexture) {
7373 }
7374
7375 QRHI_RES_RHI(QRhiVulkan);
7376 if (rhiD) {
7377 rhiD->releaseQueue.append(e);
7378 rhiD->unregisterResource(this);
7379 }
7380}
7381
7383{
7384 if (memory || backingTexture)
7385 destroy();
7386
7387 if (m_pixelSize.isEmpty())
7388 return false;
7389
7390 QRHI_RES_RHI(QRhiVulkan);
7391 samples = rhiD->effectiveSampleCountBits(m_sampleCount);
7392
7393 switch (m_type) {
7394 case QRhiRenderBuffer::Color:
7395 {
7396 if (!backingTexture) {
7397 backingTexture = QRHI_RES(QVkTexture, rhiD->createTexture(backingFormat(),
7398 m_pixelSize,
7399 1,
7400 0,
7401 m_sampleCount,
7402 QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource));
7403 } else {
7404 backingTexture->setPixelSize(m_pixelSize);
7405 backingTexture->setSampleCount(m_sampleCount);
7406 }
7407 backingTexture->setName(m_objectName);
7409 return false;
7410 vkformat = backingTexture->vkformat;
7411 }
7412 break;
7413 case QRhiRenderBuffer::DepthStencil:
7414 vkformat = rhiD->optimalDepthStencilFormat();
7415 if (!rhiD->createTransientImage(vkformat,
7416 m_pixelSize,
7417 VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
7418 VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT,
7419 samples,
7420 &memory,
7421 &image,
7422 &imageView,
7423 1))
7424 {
7425 return false;
7426 }
7427 rhiD->setObjectName(uint64_t(image), VK_OBJECT_TYPE_IMAGE, m_objectName);
7428 break;
7429 default:
7430 Q_UNREACHABLE();
7431 break;
7432 }
7433
7435 generation += 1;
7436 rhiD->registerResource(this);
7437 return true;
7438}
7439
7441{
7442 if (m_backingFormatHint != QRhiTexture::UnknownFormat)
7443 return m_backingFormatHint;
7444 else
7445 return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
7446}
7447
7448QVkTexture::QVkTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
7449 int arraySize, int sampleCount, Flags flags)
7451{
7452 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
7453 stagingBuffers[i] = VK_NULL_HANDLE;
7454 stagingAllocations[i] = nullptr;
7455 }
7456 for (int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i)
7457 perLevelImageViews[i] = VK_NULL_HANDLE;
7458}
7459
7461{
7462 destroy();
7463}
7464
7466{
7467 if (!image)
7468 return;
7469
7473
7474 e.texture.image = owns ? image : VK_NULL_HANDLE;
7475 e.texture.imageView = imageView;
7476 e.texture.allocation = owns ? imageAlloc : nullptr;
7477
7478 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
7479 e.texture.stagingBuffers[i] = stagingBuffers[i];
7480 e.texture.stagingAllocations[i] = stagingAllocations[i];
7481
7482 stagingBuffers[i] = VK_NULL_HANDLE;
7483 stagingAllocations[i] = nullptr;
7484 }
7485
7486 for (int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i) {
7487 e.texture.extraImageViews[i] = perLevelImageViews[i];
7488 perLevelImageViews[i] = VK_NULL_HANDLE;
7489 }
7490
7491 image = VK_NULL_HANDLE;
7492 imageView = VK_NULL_HANDLE;
7493 imageAlloc = nullptr;
7494
7495 QRHI_RES_RHI(QRhiVulkan);
7496 if (rhiD) {
7497 rhiD->releaseQueue.append(e);
7498 rhiD->unregisterResource(this);
7499 }
7500}
7501
7502bool QVkTexture::prepareCreate(QSize *adjustedSize)
7503{
7504 if (image)
7505 destroy();
7506
7507 QRHI_RES_RHI(QRhiVulkan);
7508 vkformat = toVkTextureFormat(m_format, m_flags);
7509 if (m_writeViewFormat.format != UnknownFormat)
7510 viewFormat = toVkTextureFormat(m_writeViewFormat.format, m_writeViewFormat.srgb ? sRGB : Flags());
7511 else
7512 viewFormat = vkformat;
7513 if (m_readViewFormat.format != UnknownFormat)
7514 viewFormatForSampling = toVkTextureFormat(m_readViewFormat.format, m_readViewFormat.srgb ? sRGB : Flags());
7515 else
7516 viewFormatForSampling = vkformat;
7517
7518 VkFormatProperties props;
7519 rhiD->f->vkGetPhysicalDeviceFormatProperties(rhiD->physDev, vkformat, &props);
7520 const bool canSampleOptimal = (props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT);
7521 if (!canSampleOptimal) {
7522 qWarning("Texture sampling with optimal tiling for format %d not supported", vkformat);
7523 return false;
7524 }
7525
7526 const bool isCube = m_flags.testFlag(CubeMap);
7527 const bool isArray = m_flags.testFlag(TextureArray);
7528 const bool is3D = m_flags.testFlag(ThreeDimensional);
7529 const bool is1D = m_flags.testFlag(OneDimensional);
7530 const bool hasMipMaps = m_flags.testFlag(MipMapped);
7531
7532 const QSize size = is1D ? QSize(qMax(1, m_pixelSize.width()), 1)
7533 : (m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize);
7534
7535 mipLevelCount = uint(hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1);
7536 const int maxLevels = QRhi::MAX_MIP_LEVELS;
7537 if (mipLevelCount > maxLevels) {
7538 qWarning("Too many mip levels (%d, max is %d), truncating mip chain", mipLevelCount, maxLevels);
7539 mipLevelCount = maxLevels;
7540 }
7541 samples = rhiD->effectiveSampleCountBits(m_sampleCount);
7542 if (samples > VK_SAMPLE_COUNT_1_BIT) {
7543 if (isCube) {
7544 qWarning("Cubemap texture cannot be multisample");
7545 return false;
7546 }
7547 if (is3D) {
7548 qWarning("3D texture cannot be multisample");
7549 return false;
7550 }
7551 if (hasMipMaps) {
7552 qWarning("Multisample texture cannot have mipmaps");
7553 return false;
7554 }
7555 }
7556 if (isCube && is3D) {
7557 qWarning("Texture cannot be both cube and 3D");
7558 return false;
7559 }
7560 if (isArray && is3D) {
7561 qWarning("Texture cannot be both array and 3D");
7562 return false;
7563 }
7564 if (isCube && is1D) {
7565 qWarning("Texture cannot be both cube and 1D");
7566 return false;
7567 }
7568 if (is1D && is3D) {
7569 qWarning("Texture cannot be both 1D and 3D");
7570 return false;
7571 }
7572 if (m_depth > 1 && !is3D) {
7573 qWarning("Texture cannot have a depth of %d when it is not 3D", m_depth);
7574 return false;
7575 }
7576 if (m_arraySize > 0 && !isArray) {
7577 qWarning("Texture cannot have an array size of %d when it is not an array", m_arraySize);
7578 return false;
7579 }
7580 if (m_arraySize < 1 && isArray) {
7581 qWarning("Texture is an array but array size is %d", m_arraySize);
7582 return false;
7583 }
7584
7585 usageState.layout = VK_IMAGE_LAYOUT_PREINITIALIZED;
7586 usageState.access = 0;
7587 usageState.stage = 0;
7588
7589 if (adjustedSize)
7590 *adjustedSize = size;
7591
7592 return true;
7593}
7594
7596{
7597 QRHI_RES_RHI(QRhiVulkan);
7598
7599 const auto aspectMask = aspectMaskForTextureFormat(m_format);
7600 const bool isCube = m_flags.testFlag(CubeMap);
7601 const bool isArray = m_flags.testFlag(TextureArray);
7602 const bool is3D = m_flags.testFlag(ThreeDimensional);
7603 const bool is1D = m_flags.testFlag(OneDimensional);
7604
7605 VkImageViewCreateInfo viewInfo = {};
7606 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
7607 viewInfo.image = image;
7608 viewInfo.viewType = isCube
7609 ? VK_IMAGE_VIEW_TYPE_CUBE
7610 : (is3D ? VK_IMAGE_VIEW_TYPE_3D
7611 : (is1D ? (isArray ? VK_IMAGE_VIEW_TYPE_1D_ARRAY : VK_IMAGE_VIEW_TYPE_1D)
7612 : (isArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D)));
7613 viewInfo.format = viewFormatForSampling;
7614 viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
7615 viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
7616 viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
7617 viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
7618 viewInfo.subresourceRange.aspectMask = aspectMask;
7619 // Force-remove the VK_IMAGE_ASPECT_STENCIL_BIT
7620 // Another view with this bit is probably needed for stencil
7621 viewInfo.subresourceRange.aspectMask &= ~VK_IMAGE_ASPECT_STENCIL_BIT;
7622 viewInfo.subresourceRange.levelCount = mipLevelCount;
7623 if (isArray && m_arrayRangeStart >= 0 && m_arrayRangeLength >= 0) {
7624 viewInfo.subresourceRange.baseArrayLayer = uint32_t(m_arrayRangeStart);
7625 viewInfo.subresourceRange.layerCount = uint32_t(m_arrayRangeLength);
7626 } else {
7627 viewInfo.subresourceRange.layerCount = isCube ? 6 : (isArray ? qMax(0, m_arraySize) : 1);
7628 }
7629
7630 VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &imageView);
7631 if (err != VK_SUCCESS) {
7632 qWarning("Failed to create image view: %d", err);
7633 return false;
7634 }
7635
7637 generation += 1;
7638
7639 return true;
7640}
7641
7643{
7644 QSize size;
7645 if (!prepareCreate(&size))
7646 return false;
7647
7648 QRHI_RES_RHI(QRhiVulkan);
7649 const bool isRenderTarget = m_flags.testFlag(QRhiTexture::RenderTarget);
7650 const bool isDepth = isDepthTextureFormat(m_format);
7651 const bool isCube = m_flags.testFlag(CubeMap);
7652 const bool isArray = m_flags.testFlag(TextureArray);
7653 const bool is3D = m_flags.testFlag(ThreeDimensional);
7654 const bool is1D = m_flags.testFlag(OneDimensional);
7655
7656 VkImageCreateInfo imageInfo = {};
7657 imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
7658 imageInfo.flags = 0;
7659 if (isCube)
7660 imageInfo.flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
7661
7662 if (is3D && isRenderTarget) {
7663 // This relies on a Vulkan 1.1 constant. For guaranteed proper behavior
7664 // this also requires that at run time the VkInstance has at least API 1.1
7665 // enabled. (though it works as expected with some Vulkan (1.2)
7666 // implementations regardless of the requested API version, but f.ex. the
7667 // validation layer complains when using this without enabling >=1.1)
7668 if (!rhiD->caps.texture3DSliceAs2D)
7669 qWarning("QRhiVulkan: Rendering to 3D texture slice may not be functional without API 1.1 on the VkInstance");
7670#ifdef VK_VERSION_1_1
7671 imageInfo.flags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT;
7672#else
7673 imageInfo.flags |= 0x00000020;
7674#endif
7675 }
7676
7677 imageInfo.imageType = is1D ? VK_IMAGE_TYPE_1D : is3D ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D;
7678 imageInfo.format = vkformat;
7679 imageInfo.extent.width = uint32_t(size.width());
7680 imageInfo.extent.height = uint32_t(size.height());
7681 imageInfo.extent.depth = is3D ? qMax(1, m_depth) : 1;
7682 imageInfo.mipLevels = mipLevelCount;
7683 imageInfo.arrayLayers = isCube ? 6 : (isArray ? qMax(0, m_arraySize) : 1);
7684 imageInfo.samples = samples;
7685 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
7686 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
7687
7688 imageInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
7689 if (isRenderTarget) {
7690 if (isDepth)
7691 imageInfo.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
7692 else
7693 imageInfo.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
7694 }
7695 if (m_flags.testFlag(QRhiTexture::UsedAsTransferSource))
7696 imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
7697 if (m_flags.testFlag(QRhiTexture::UsedWithGenerateMips))
7698 imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
7699 if (m_flags.testFlag(QRhiTexture::UsedWithLoadStore))
7700 imageInfo.usage |= VK_IMAGE_USAGE_STORAGE_BIT;
7701#ifdef VK_KHR_fragment_shading_rate
7702 if (m_flags.testFlag(QRhiTexture::UsedAsShadingRateMap) && rhiD->caps.imageBasedShadingRate)
7703 imageInfo.usage |= VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR;
7704#endif
7705
7706 VmaAllocationCreateInfo allocInfo = {};
7707 allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
7708
7709 VmaAllocation allocation;
7710 VkResult err = vmaCreateImage(toVmaAllocator(rhiD->allocator), &imageInfo, &allocInfo, &image, &allocation, nullptr);
7711 if (err != VK_SUCCESS) {
7712 qWarning("Failed to create image (with VkImageCreateInfo %ux%u depth %u vkformat 0x%X mips %u layers %u vksamples 0x%X): %d",
7713 imageInfo.extent.width, imageInfo.extent.height, imageInfo.extent.depth,
7714 int(imageInfo.format),
7715 imageInfo.mipLevels,
7716 imageInfo.arrayLayers,
7717 int(imageInfo.samples),
7718 err);
7719 rhiD->printExtraErrorInfo(err);
7720 return false;
7721 }
7722 imageAlloc = allocation;
7723 rhiD->setAllocationName(allocation, m_objectName);
7724
7725 if (!finishCreate())
7726 return false;
7727
7728 rhiD->setObjectName(uint64_t(image), VK_OBJECT_TYPE_IMAGE, m_objectName);
7729
7730 owns = true;
7731 rhiD->registerResource(this);
7732 return true;
7733}
7734
7735bool QVkTexture::createFrom(QRhiTexture::NativeTexture src)
7736{
7737 VkImage img = VkImage(src.object);
7738 if (img == 0)
7739 return false;
7740
7741 if (!prepareCreate())
7742 return false;
7743
7744 image = img;
7745
7746 if (!finishCreate())
7747 return false;
7748
7749 usageState.layout = VkImageLayout(src.layout);
7750
7751 owns = false;
7752 QRHI_RES_RHI(QRhiVulkan);
7753 rhiD->registerResource(this);
7754 return true;
7755}
7756
7758{
7759 return {quint64(image), usageState.layout};
7760}
7761
7763{
7764 usageState.layout = VkImageLayout(layout);
7765}
7766
7768{
7769 Q_ASSERT(level >= 0 && level < int(mipLevelCount));
7770 if (perLevelImageViews[level] != VK_NULL_HANDLE)
7771 return perLevelImageViews[level];
7772
7773 const VkImageAspectFlags aspectMask = aspectMaskForTextureFormat(m_format);
7774 const bool isCube = m_flags.testFlag(CubeMap);
7775 const bool isArray = m_flags.testFlag(TextureArray);
7776 const bool is3D = m_flags.testFlag(ThreeDimensional);
7777 const bool is1D = m_flags.testFlag(OneDimensional);
7778
7779 VkImageViewCreateInfo viewInfo = {};
7780 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
7781 viewInfo.image = image;
7782 viewInfo.viewType = isCube
7783 ? VK_IMAGE_VIEW_TYPE_CUBE
7784 : (is3D ? VK_IMAGE_VIEW_TYPE_3D
7785 : (is1D ? (isArray ? VK_IMAGE_VIEW_TYPE_1D_ARRAY : VK_IMAGE_VIEW_TYPE_1D)
7786 : (isArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D)));
7787 viewInfo.format = viewFormat; // this is writeViewFormat, regardless of Load, Store, or LoadStore; intentional
7788 viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
7789 viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
7790 viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
7791 viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
7792 viewInfo.subresourceRange.aspectMask = aspectMask;
7793 viewInfo.subresourceRange.baseMipLevel = uint32_t(level);
7794 viewInfo.subresourceRange.levelCount = 1;
7795 viewInfo.subresourceRange.baseArrayLayer = 0;
7796 viewInfo.subresourceRange.layerCount = isCube ? 6 : (isArray ? qMax(0, m_arraySize) : 1);
7797
7798 VkImageView v = VK_NULL_HANDLE;
7799 QRHI_RES_RHI(QRhiVulkan);
7800 VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &v);
7801 if (err != VK_SUCCESS) {
7802 qWarning("Failed to create image view: %d", err);
7803 return VK_NULL_HANDLE;
7804 }
7805
7806 perLevelImageViews[level] = v;
7807 return v;
7808}
7809
7810QVkSampler::QVkSampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
7811 AddressMode u, AddressMode v, AddressMode w)
7813{
7814}
7815
7817{
7818 destroy();
7819}
7820
7822{
7823 if (!sampler)
7824 return;
7825
7829
7830 e.sampler.sampler = sampler;
7831 sampler = VK_NULL_HANDLE;
7832
7833 QRHI_RES_RHI(QRhiVulkan);
7834 if (rhiD) {
7835 rhiD->releaseQueue.append(e);
7836 rhiD->unregisterResource(this);
7837 }
7838}
7839
7841{
7842 if (sampler)
7843 destroy();
7844
7845 VkSamplerCreateInfo samplerInfo = {};
7846 samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
7847 samplerInfo.magFilter = toVkFilter(m_magFilter);
7848 samplerInfo.minFilter = toVkFilter(m_minFilter);
7849 samplerInfo.mipmapMode = toVkMipmapMode(m_mipmapMode);
7850 samplerInfo.addressModeU = toVkAddressMode(m_addressU);
7851 samplerInfo.addressModeV = toVkAddressMode(m_addressV);
7852 samplerInfo.addressModeW = toVkAddressMode(m_addressW);
7853 samplerInfo.maxAnisotropy = 1.0f;
7854 samplerInfo.compareEnable = m_compareOp != Never;
7855 samplerInfo.compareOp = toVkTextureCompareOp(m_compareOp);
7856 samplerInfo.maxLod = m_mipmapMode == None ? 0.25f : 1000.0f;
7857
7858 QRHI_RES_RHI(QRhiVulkan);
7859 VkResult err = rhiD->df->vkCreateSampler(rhiD->dev, &samplerInfo, nullptr, &sampler);
7860 if (err != VK_SUCCESS) {
7861 qWarning("Failed to create sampler: %d", err);
7862 return false;
7863 }
7864
7866 generation += 1;
7867 rhiD->registerResource(this);
7868 return true;
7869}
7870
7873{
7874 serializedFormatData.reserve(64);
7875}
7876
7881
7883{
7884 if (!rp)
7885 return;
7886
7887 if (!ownsRp) {
7888 rp = VK_NULL_HANDLE;
7889 return;
7890 }
7891
7895
7896 e.renderPass.rp = rp;
7897
7898 rp = VK_NULL_HANDLE;
7899
7900 QRHI_RES_RHI(QRhiVulkan);
7901 if (rhiD) {
7902 rhiD->releaseQueue.append(e);
7903 rhiD->unregisterResource(this);
7904 }
7905}
7906
7907static inline bool attachmentDescriptionEquals(const VkAttachmentDescription &a, const VkAttachmentDescription &b)
7908{
7909 return a.format == b.format
7910 && a.samples == b.samples
7911 && a.loadOp == b.loadOp
7912 && a.storeOp == b.storeOp
7913 && a.stencilLoadOp == b.stencilLoadOp
7914 && a.stencilStoreOp == b.stencilStoreOp
7915 && a.initialLayout == b.initialLayout
7916 && a.finalLayout == b.finalLayout;
7917}
7918
7919bool QVkRenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other) const
7920{
7921 if (other == this)
7922 return true;
7923
7924 if (!other)
7925 return false;
7926
7928
7929 if (attDescs.size() != o->attDescs.size())
7930 return false;
7931 if (colorRefs.size() != o->colorRefs.size())
7932 return false;
7933 if (resolveRefs.size() != o->resolveRefs.size())
7934 return false;
7936 return false;
7938 return false;
7939 if (multiViewCount != o->multiViewCount)
7940 return false;
7942 return false;
7943
7944 for (int i = 0, ie = colorRefs.size(); i != ie; ++i) {
7945 const uint32_t attIdx = colorRefs[i].attachment;
7946 if (attIdx != o->colorRefs[i].attachment)
7947 return false;
7948 if (attIdx != VK_ATTACHMENT_UNUSED && !attachmentDescriptionEquals(attDescs[attIdx], o->attDescs[attIdx]))
7949 return false;
7950 }
7951
7952 if (hasDepthStencil) {
7953 const uint32_t attIdx = dsRef.attachment;
7954 if (attIdx != o->dsRef.attachment)
7955 return false;
7956 if (attIdx != VK_ATTACHMENT_UNUSED && !attachmentDescriptionEquals(attDescs[attIdx], o->attDescs[attIdx]))
7957 return false;
7958 }
7959
7960 for (int i = 0, ie = resolveRefs.size(); i != ie; ++i) {
7961 const uint32_t attIdx = resolveRefs[i].attachment;
7962 if (attIdx != o->resolveRefs[i].attachment)
7963 return false;
7964 if (attIdx != VK_ATTACHMENT_UNUSED && !attachmentDescriptionEquals(attDescs[attIdx], o->attDescs[attIdx]))
7965 return false;
7966 }
7967
7969 const uint32_t attIdx = dsResolveRef.attachment;
7970 if (attIdx != o->dsResolveRef.attachment)
7971 return false;
7972 if (attIdx != VK_ATTACHMENT_UNUSED && !attachmentDescriptionEquals(attDescs[attIdx], o->attDescs[attIdx]))
7973 return false;
7974 }
7975
7976 if (hasShadingRateMap) {
7977 const uint32_t attIdx = shadingRateRef.attachment;
7978 if (attIdx != o->shadingRateRef.attachment)
7979 return false;
7980 if (attIdx != VK_ATTACHMENT_UNUSED && !attachmentDescriptionEquals(attDescs[attIdx], o->attDescs[attIdx]))
7981 return false;
7982 }
7983
7984 // subpassDeps is not included
7985
7986 return true;
7987}
7988
7990{
7991 serializedFormatData.clear();
7992 auto p = std::back_inserter(serializedFormatData);
7993
7994 *p++ = attDescs.size();
7995 *p++ = colorRefs.size();
7996 *p++ = resolveRefs.size();
7997 *p++ = hasDepthStencil;
7999 *p++ = hasShadingRateMap;
8000 *p++ = multiViewCount;
8001
8002 auto serializeAttachmentData = [this, &p](uint32_t attIdx) {
8003 const bool used = attIdx != VK_ATTACHMENT_UNUSED;
8004 const VkAttachmentDescription *a = used ? &attDescs[attIdx] : nullptr;
8005 *p++ = used ? a->format : 0;
8006 *p++ = used ? a->samples : 0;
8007 *p++ = used ? a->loadOp : 0;
8008 *p++ = used ? a->storeOp : 0;
8009 *p++ = used ? a->stencilLoadOp : 0;
8010 *p++ = used ? a->stencilStoreOp : 0;
8011 *p++ = used ? a->initialLayout : 0;
8012 *p++ = used ? a->finalLayout : 0;
8013 };
8014
8015 for (int i = 0, ie = colorRefs.size(); i != ie; ++i) {
8016 const uint32_t attIdx = colorRefs[i].attachment;
8017 *p++ = attIdx;
8018 serializeAttachmentData(attIdx);
8019 }
8020
8021 if (hasDepthStencil) {
8022 const uint32_t attIdx = dsRef.attachment;
8023 *p++ = attIdx;
8024 serializeAttachmentData(attIdx);
8025 }
8026
8027 for (int i = 0, ie = resolveRefs.size(); i != ie; ++i) {
8028 const uint32_t attIdx = resolveRefs[i].attachment;
8029 *p++ = attIdx;
8030 serializeAttachmentData(attIdx);
8031 }
8032
8034 const uint32_t attIdx = dsResolveRef.attachment;
8035 *p++ = attIdx;
8036 serializeAttachmentData(attIdx);
8037 }
8038
8039 if (hasShadingRateMap) {
8040 const uint32_t attIdx = shadingRateRef.attachment;
8041 *p++ = attIdx;
8042 serializeAttachmentData(attIdx);
8043 }
8044}
8045
8047{
8048 QVkRenderPassDescriptor *rpD = new QVkRenderPassDescriptor(m_rhi);
8049
8050 rpD->ownsRp = true;
8051 rpD->attDescs = attDescs;
8052 rpD->colorRefs = colorRefs;
8053 rpD->resolveRefs = resolveRefs;
8054 rpD->subpassDeps = subpassDeps;
8058 rpD->multiViewCount = multiViewCount;
8059 rpD->dsRef = dsRef;
8060 rpD->dsResolveRef = dsResolveRef;
8061 rpD->shadingRateRef = shadingRateRef;
8062
8063 VkRenderPassCreateInfo rpInfo;
8064 VkSubpassDescription subpassDesc;
8065 fillRenderPassCreateInfo(&rpInfo, &subpassDesc, rpD);
8066
8067 QRHI_RES_RHI(QRhiVulkan);
8068 MultiViewRenderPassSetupHelper multiViewHelper;
8069 if (!multiViewHelper.prepare(&rpInfo, multiViewCount, rhiD->caps.multiView)) {
8070 delete rpD;
8071 return nullptr;
8072 }
8073
8074#ifdef VK_KHR_create_renderpass2
8075 if (rhiD->caps.renderPass2KHR) {
8076 // Use the KHR extension, not the 1.2 core API, in order to support Vulkan 1.1.
8077 VkRenderPassCreateInfo2KHR rpInfo2;
8078 RenderPass2SetupHelper rp2Helper(rhiD);
8079 if (!rp2Helper.prepare(&rpInfo2, &rpInfo, rpD, multiViewCount)) {
8080 delete rpD;
8081 return nullptr;
8082 }
8083 VkResult err = rhiD->vkCreateRenderPass2KHR(rhiD->dev, &rpInfo2, nullptr, &rpD->rp);
8084 if (err != VK_SUCCESS) {
8085 qWarning("Failed to create renderpass (using VkRenderPassCreateInfo2KHR): %d", err);
8086 delete rpD;
8087 return nullptr;
8088 }
8089 } else
8090#endif
8091 {
8092 VkResult err = rhiD->df->vkCreateRenderPass(rhiD->dev, &rpInfo, nullptr, &rpD->rp);
8093 if (err != VK_SUCCESS) {
8094 qWarning("Failed to create renderpass: %d", err);
8095 delete rpD;
8096 return nullptr;
8097 }
8098 }
8099
8101 rhiD->registerResource(rpD);
8102 return rpD;
8103}
8104
8106{
8107 return serializedFormatData;
8108}
8109
8111{
8112 nativeHandlesStruct.renderPass = rp;
8113 return &nativeHandlesStruct;
8114}
8115
8116QVkShadingRateMap::QVkShadingRateMap(QRhiImplementation *rhi)
8118{
8119}
8120
8125
8127{
8128 if (!texture)
8129 return;
8130
8131 texture = nullptr;
8132}
8133
8134bool QVkShadingRateMap::createFrom(QRhiTexture *src)
8135{
8136 if (texture)
8137 destroy();
8138
8139 texture = QRHI_RES(QVkTexture, src);
8140
8141 return true;
8142}
8143
8144QVkSwapChainRenderTarget::QVkSwapChainRenderTarget(QRhiImplementation *rhi, QRhiSwapChain *swapchain)
8146{
8147}
8148
8153
8155{
8156 // nothing to do here
8157}
8158
8160{
8161 return d.pixelSize;
8162}
8163
8165{
8166 return d.dpr;
8167}
8168
8170{
8171 return d.sampleCount;
8172}
8173
8175 const QRhiTextureRenderTargetDescription &desc,
8176 Flags flags)
8178{
8179 for (int att = 0; att < QVkRenderTargetData::MAX_COLOR_ATTACHMENTS; ++att) {
8180 rtv[att] = VK_NULL_HANDLE;
8181 resrtv[att] = VK_NULL_HANDLE;
8182 }
8183}
8184
8189
8191{
8192 if (!d.fb)
8193 return;
8194
8198
8199 e.textureRenderTarget.fb = d.fb;
8200 d.fb = VK_NULL_HANDLE;
8201
8202 for (int att = 0; att < QVkRenderTargetData::MAX_COLOR_ATTACHMENTS; ++att) {
8203 e.textureRenderTarget.rtv[att] = rtv[att];
8204 e.textureRenderTarget.resrtv[att] = resrtv[att];
8205 rtv[att] = VK_NULL_HANDLE;
8206 resrtv[att] = VK_NULL_HANDLE;
8207 }
8208
8209 e.textureRenderTarget.dsv = dsv;
8210 dsv = VK_NULL_HANDLE;
8211 e.textureRenderTarget.resdsv = resdsv;
8212 resdsv = VK_NULL_HANDLE;
8213
8214 e.textureRenderTarget.shadingRateMapView = shadingRateMapView;
8215 shadingRateMapView = VK_NULL_HANDLE;
8216
8217 QRHI_RES_RHI(QRhiVulkan);
8218 if (rhiD) {
8219 rhiD->releaseQueue.append(e);
8220 rhiD->unregisterResource(this);
8221 }
8222}
8223
8225{
8226 // not yet built so cannot rely on data computed in create()
8227
8228 QRHI_RES_RHI(QRhiVulkan);
8229 QVkRenderPassDescriptor *rp = new QVkRenderPassDescriptor(m_rhi);
8230 if (!rhiD->createOffscreenRenderPass(rp,
8231 m_desc.cbeginColorAttachments(),
8232 m_desc.cendColorAttachments(),
8233 m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents),
8234 m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents),
8235 m_desc.depthTexture() && !m_flags.testFlag(DoNotStoreDepthStencilContents) && !m_desc.depthResolveTexture(),
8236 m_desc.depthStencilBuffer(),
8237 m_desc.depthTexture(),
8238 m_desc.depthResolveTexture(),
8239 m_desc.depthLayer(),
8240 m_desc.shadingRateMap()))
8241 {
8242 delete rp;
8243 return nullptr;
8244 }
8245
8246 rp->ownsRp = true;
8248 rhiD->registerResource(rp);
8249 return rp;
8250}
8251
8253{
8254 if (d.fb)
8255 destroy();
8256
8257 Q_ASSERT(m_desc.colorAttachmentCount() > 0 || m_desc.depthTexture());
8258 Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture());
8259 const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
8260
8261 QRHI_RES_RHI(QRhiVulkan);
8262 QVarLengthArray<VkImageView, 8> views;
8263 d.multiViewCount = 0;
8264
8265 d.colorAttCount = 0;
8266 int attIndex = 0;
8267 for (auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it, ++attIndex) {
8268 d.colorAttCount += 1;
8269 QVkTexture *texD = QRHI_RES(QVkTexture, it->texture());
8270 QVkRenderBuffer *rbD = QRHI_RES(QVkRenderBuffer, it->renderBuffer());
8271 Q_ASSERT(texD || rbD);
8272 if (texD) {
8273 Q_ASSERT(texD->flags().testFlag(QRhiTexture::RenderTarget));
8274 const bool is1D = texD->flags().testFlag(QRhiTexture::OneDimensional);
8275 const bool isMultiView = it->multiViewCount() >= 2;
8276 if (isMultiView && d.multiViewCount == 0)
8277 d.multiViewCount = it->multiViewCount();
8278 VkImageViewCreateInfo viewInfo = {};
8279 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
8280 viewInfo.image = texD->image;
8281 viewInfo.viewType = is1D ? VK_IMAGE_VIEW_TYPE_1D
8282 : (isMultiView ? VK_IMAGE_VIEW_TYPE_2D_ARRAY
8283 : VK_IMAGE_VIEW_TYPE_2D);
8284 viewInfo.format = texD->viewFormat;
8285 viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
8286 viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
8287 viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
8288 viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
8289 viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
8290 viewInfo.subresourceRange.baseMipLevel = uint32_t(it->level());
8291 viewInfo.subresourceRange.levelCount = 1;
8292 viewInfo.subresourceRange.baseArrayLayer = uint32_t(it->layer());
8293 viewInfo.subresourceRange.layerCount = uint32_t(isMultiView ? it->multiViewCount() : 1);
8294 VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &rtv[attIndex]);
8295 if (err != VK_SUCCESS) {
8296 qWarning("Failed to create render target image view: %d", err);
8297 return false;
8298 }
8299 views.append(rtv[attIndex]);
8300 if (attIndex == 0) {
8301 d.pixelSize = rhiD->q->sizeForMipLevel(it->level(), texD->pixelSize());
8302 d.sampleCount = texD->samples;
8303 }
8304 } else if (rbD) {
8305 Q_ASSERT(rbD->backingTexture);
8306 views.append(rbD->backingTexture->imageView);
8307 if (attIndex == 0) {
8308 d.pixelSize = rbD->pixelSize();
8309 d.sampleCount = rbD->samples;
8310 }
8311 }
8312 }
8313 d.dpr = 1;
8314
8315 if (hasDepthStencil) {
8316 if (m_desc.depthTexture()) {
8317 QVkTexture *depthTexD = QRHI_RES(QVkTexture, m_desc.depthTexture());
8318 // need a dedicated view just because viewFormat may differ from vkformat
8319 VkImageViewCreateInfo viewInfo = {};
8320 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
8321 viewInfo.image = depthTexD->image;
8322 viewInfo.viewType = d.multiViewCount > 1 ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D;
8323 viewInfo.format = depthTexD->viewFormat;
8324 viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
8325 viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
8326 viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
8327 viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
8328 viewInfo.subresourceRange.aspectMask = aspectMaskForTextureFormat(depthTexD->format());
8329 viewInfo.subresourceRange.levelCount = 1;
8330 if (m_desc.depthLayer() >= 0 && depthTexD->arraySize() >= 2) {
8331 viewInfo.subresourceRange.baseArrayLayer = uint32_t(m_desc.depthLayer());
8332 viewInfo.subresourceRange.layerCount = 1;
8333 }
8334 else
8335 viewInfo.subresourceRange.layerCount = qMax<uint32_t>(1, d.multiViewCount);
8336 VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &dsv);
8337 if (err != VK_SUCCESS) {
8338 qWarning("Failed to create depth-stencil image view for rt: %d", err);
8339 return false;
8340 }
8341 views.append(dsv);
8342 if (d.colorAttCount == 0) {
8343 d.pixelSize = depthTexD->pixelSize();
8344 d.sampleCount = depthTexD->samples;
8345 }
8346 } else {
8347 QVkRenderBuffer *depthRbD = QRHI_RES(QVkRenderBuffer, m_desc.depthStencilBuffer());
8348 views.append(depthRbD->imageView);
8349 if (d.colorAttCount == 0) {
8350 d.pixelSize = depthRbD->pixelSize();
8351 d.sampleCount = depthRbD->samples;
8352 }
8353 }
8354 d.dsAttCount = 1;
8355 } else {
8356 d.dsAttCount = 0;
8357 }
8358
8359 d.resolveAttCount = 0;
8360 attIndex = 0;
8361 Q_ASSERT(d.multiViewCount == 0 || d.multiViewCount >= 2);
8362 for (auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it, ++attIndex) {
8363 if (it->resolveTexture()) {
8364 QVkTexture *resTexD = QRHI_RES(QVkTexture, it->resolveTexture());
8365 Q_ASSERT(resTexD->flags().testFlag(QRhiTexture::RenderTarget));
8366 d.resolveAttCount += 1;
8367
8368 VkImageViewCreateInfo viewInfo = {};
8369 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
8370 viewInfo.image = resTexD->image;
8371 viewInfo.viewType = d.multiViewCount ? VK_IMAGE_VIEW_TYPE_2D_ARRAY
8372 : VK_IMAGE_VIEW_TYPE_2D;
8373 viewInfo.format = resTexD->viewFormat;
8374 viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
8375 viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
8376 viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
8377 viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
8378 viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
8379 viewInfo.subresourceRange.baseMipLevel = uint32_t(it->resolveLevel());
8380 viewInfo.subresourceRange.levelCount = 1;
8381 viewInfo.subresourceRange.baseArrayLayer = uint32_t(it->resolveLayer());
8382 viewInfo.subresourceRange.layerCount = qMax<uint32_t>(1, d.multiViewCount);
8383 VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &resrtv[attIndex]);
8384 if (err != VK_SUCCESS) {
8385 qWarning("Failed to create render target resolve image view: %d", err);
8386 return false;
8387 }
8388 views.append(resrtv[attIndex]);
8389 }
8390 }
8391
8392 if (m_desc.depthResolveTexture()) {
8393 QVkTexture *resTexD = QRHI_RES(QVkTexture, m_desc.depthResolveTexture());
8394 Q_ASSERT(resTexD->flags().testFlag(QRhiTexture::RenderTarget));
8395
8396 VkImageViewCreateInfo viewInfo = {};
8397 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
8398 viewInfo.image = resTexD->image;
8399 viewInfo.viewType = d.multiViewCount ? VK_IMAGE_VIEW_TYPE_2D_ARRAY
8400 : VK_IMAGE_VIEW_TYPE_2D;
8401 viewInfo.format = resTexD->viewFormat;
8402 viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
8403 viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
8404 viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
8405 viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
8406 viewInfo.subresourceRange.aspectMask = aspectMaskForTextureFormat(resTexD->format());
8407 viewInfo.subresourceRange.baseMipLevel = 0;
8408 viewInfo.subresourceRange.levelCount = 1;
8409 viewInfo.subresourceRange.baseArrayLayer = 0;
8410 viewInfo.subresourceRange.layerCount = qMax<uint32_t>(1, d.multiViewCount);
8411 VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &resdsv);
8412 if (err != VK_SUCCESS) {
8413 qWarning("Failed to create render target depth resolve image view: %d", err);
8414 return false;
8415 }
8416 views.append(resdsv);
8417 d.dsResolveAttCount = 1;
8418 } else {
8419 d.dsResolveAttCount = 0;
8420 }
8421
8422 if (m_desc.shadingRateMap() && rhiD->caps.renderPass2KHR && rhiD->caps.imageBasedShadingRate) {
8423 QVkTexture *texD = QRHI_RES(QVkShadingRateMap, m_desc.shadingRateMap())->texture;
8424 Q_ASSERT(texD->flags().testFlag(QRhiTexture::UsedAsShadingRateMap));
8425
8426 VkImageViewCreateInfo viewInfo = {};
8427 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
8428 viewInfo.image = texD->image;
8429 viewInfo.viewType = d.multiViewCount ? VK_IMAGE_VIEW_TYPE_2D_ARRAY
8430 : VK_IMAGE_VIEW_TYPE_2D;
8431 viewInfo.format = texD->viewFormat;
8432 viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
8433 viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
8434 viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
8435 viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
8436 viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
8437 viewInfo.subresourceRange.baseMipLevel = 0;
8438 viewInfo.subresourceRange.levelCount = 1;
8439 viewInfo.subresourceRange.baseArrayLayer = 0;
8440 viewInfo.subresourceRange.layerCount = qMax<uint32_t>(1, d.multiViewCount);
8441 VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &shadingRateMapView);
8442 if (err != VK_SUCCESS) {
8443 qWarning("Failed to create render target shading rate map view: %d", err);
8444 return false;
8445 }
8446 views.append(shadingRateMapView);
8447 d.shadingRateAttCount = 1;
8448 } else {
8449 d.shadingRateAttCount = 0;
8450 }
8451
8452 if (!m_renderPassDesc)
8453 qWarning("QVkTextureRenderTarget: No renderpass descriptor set. See newCompatibleRenderPassDescriptor() and setRenderPassDescriptor().");
8454
8455 d.rp = QRHI_RES(QVkRenderPassDescriptor, m_renderPassDesc);
8456 Q_ASSERT(d.rp && d.rp->rp);
8457
8458 VkFramebufferCreateInfo fbInfo = {};
8459 fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
8460 fbInfo.renderPass = d.rp->rp;
8461 fbInfo.attachmentCount = uint32_t(views.count());
8462 fbInfo.pAttachments = views.constData();
8463 fbInfo.width = uint32_t(d.pixelSize.width());
8464 fbInfo.height = uint32_t(d.pixelSize.height());
8465 fbInfo.layers = 1;
8466
8467 VkResult err = rhiD->df->vkCreateFramebuffer(rhiD->dev, &fbInfo, nullptr, &d.fb);
8468 if (err != VK_SUCCESS) {
8469 qWarning("Failed to create framebuffer: %d", err);
8470 return false;
8471 }
8472
8473 QRhiRenderTargetAttachmentTracker::updateResIdList<QVkTexture, QVkRenderBuffer>(m_desc, &d.currentResIdList);
8474
8476 rhiD->registerResource(this);
8477 return true;
8478}
8479
8481{
8482 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QVkTexture, QVkRenderBuffer>(m_desc, d.currentResIdList))
8483 const_cast<QVkTextureRenderTarget *>(this)->create();
8484
8485 return d.pixelSize;
8486}
8487
8489{
8490 return d.dpr;
8491}
8492
8494{
8495 return d.sampleCount;
8496}
8497
8502
8507
8509{
8510 if (!layout)
8511 return;
8512
8513 sortedBindings.clear();
8514 sortedDynamicOffsetBindingNumbers.clear();
8515
8519
8520 e.shaderResourceBindings.poolIndex = poolIndex;
8521 e.shaderResourceBindings.layout = layout;
8522
8523 poolIndex = -1;
8524 layout = VK_NULL_HANDLE;
8525 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
8526 descSets[i] = VK_NULL_HANDLE;
8527
8528 QRHI_RES_RHI(QRhiVulkan);
8529 if (rhiD) {
8530 rhiD->releaseQueue.append(e);
8531 rhiD->unregisterResource(this);
8532 }
8533}
8534
8536{
8537 if (layout)
8538 destroy();
8539
8540 QRHI_RES_RHI(QRhiVulkan);
8541 if (!rhiD->sanityCheckShaderResourceBindings(this))
8542 return false;
8543
8544 rhiD->updateLayoutDesc(this);
8545
8546 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
8547 descSets[i] = VK_NULL_HANDLE;
8548
8549 sortedBindings.clear();
8550 std::copy(m_bindings.cbegin(), m_bindings.cend(), std::back_inserter(sortedBindings));
8551 std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan);
8552
8553 sortedDynamicOffsetBindingNumbers.clear();
8554 for (const QRhiShaderResourceBinding &binding : std::as_const(sortedBindings)) {
8555 const QRhiShaderResourceBinding::Data *b = QRhiImplementation::shaderResourceBindingData(binding);
8556 if (b->type == QRhiShaderResourceBinding::UniformBuffer && b->u.ubuf.buf) {
8557 if (b->u.ubuf.hasDynamicOffset)
8558 sortedDynamicOffsetBindingNumbers.append(b->binding);
8559 }
8560 }
8561
8562 QVarLengthArray<VkDescriptorSetLayoutBinding, BINDING_PREALLOC> vkbindings;
8563 vkbindings.reserve(sortedBindings.size());
8564 for (const QRhiShaderResourceBinding &binding : std::as_const(sortedBindings)) {
8565 const QRhiShaderResourceBinding::Data *b = QRhiImplementation::shaderResourceBindingData(binding);
8566 VkDescriptorSetLayoutBinding &vkbinding = vkbindings.emplace_back();
8567 vkbinding.binding = uint32_t(b->binding);
8568 vkbinding.descriptorType = toVkDescriptorType(b);
8569 if (b->type == QRhiShaderResourceBinding::SampledTexture || b->type == QRhiShaderResourceBinding::Texture)
8570 vkbinding.descriptorCount = b->u.stex.count;
8571 else
8572 vkbinding.descriptorCount = 1;
8573 vkbinding.stageFlags = toVkShaderStageFlags(b->stage);
8574 }
8575
8576 VkDescriptorSetLayoutCreateInfo layoutInfo = {};
8577 layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
8578 layoutInfo.bindingCount = uint32_t(vkbindings.size());
8579 layoutInfo.pBindings = vkbindings.constData();
8580
8581 VkResult err = rhiD->df->vkCreateDescriptorSetLayout(rhiD->dev, &layoutInfo, nullptr, &layout);
8582 if (err != VK_SUCCESS) {
8583 qWarning("Failed to create descriptor set layout: %d", err);
8584 return false;
8585 }
8586 rhiD->setObjectName(uint64_t(layout), VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT, m_objectName);
8587
8588 VkDescriptorSetAllocateInfo allocInfo = {};
8589 allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
8590 allocInfo.descriptorSetCount = QVK_FRAMES_IN_FLIGHT;
8591 VkDescriptorSetLayout layouts[QVK_FRAMES_IN_FLIGHT];
8592 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
8593 layouts[i] = layout;
8594 allocInfo.pSetLayouts = layouts;
8595 if (!rhiD->allocateDescriptorSet(&allocInfo, descSets, &poolIndex))
8596 return false;
8597
8598 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
8599 boundResourceData[i].resize(sortedBindings.size());
8600 for (BoundResourceData &bd : boundResourceData[i])
8601 memset(&bd, 0, sizeof(BoundResourceData));
8602 }
8603
8604 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
8605 rhiD->setObjectName(uint64_t(descSets[i]), VK_OBJECT_TYPE_DESCRIPTOR_SET, m_objectName, i);
8606
8608 generation += 1;
8609 rhiD->registerResource(this);
8610 return true;
8611}
8612
8614{
8615 sortedBindings.clear();
8616 std::copy(m_bindings.cbegin(), m_bindings.cend(), std::back_inserter(sortedBindings));
8617 if (!flags.testFlag(BindingsAreSorted))
8618 std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan);
8619
8620 sortedDynamicOffsetBindingNumbers.clear();
8621 for (const QRhiShaderResourceBinding &binding : std::as_const(sortedBindings)) {
8622 const QRhiShaderResourceBinding::Data *b = QRhiImplementation::shaderResourceBindingData(binding);
8623 if (b->type == QRhiShaderResourceBinding::UniformBuffer && b->u.ubuf.buf) {
8624 if (b->u.ubuf.hasDynamicOffset)
8625 sortedDynamicOffsetBindingNumbers.append(b->binding);
8626 }
8627 }
8628
8629 // Reset the state tracking table too - it can deal with assigning a
8630 // different QRhiBuffer/Texture/Sampler for a binding point, but it cannot
8631 // detect changes in the associated data, such as the buffer offset. And
8632 // just like after a create(), a call to updateResources() may lead to now
8633 // specifying a different offset for the same QRhiBuffer for a given binding
8634 // point. The same applies to other type of associated data that is not part
8635 // of the layout, such as the mip level for a StorageImage. Instead of
8636 // complicating the checks in setShaderResources(), reset the table here
8637 // just like we do in create().
8638 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
8639 Q_ASSERT(boundResourceData[i].size() == sortedBindings.size());
8640 for (BoundResourceData &bd : boundResourceData[i])
8641 memset(&bd, 0, sizeof(BoundResourceData));
8642 }
8643
8644 generation += 1;
8645}
8646
8649{
8650}
8651
8656
8658{
8659 if (!pipeline && !layout)
8660 return;
8661
8665
8666 e.pipelineState.pipeline = pipeline;
8667 e.pipelineState.layout = layout;
8668
8669 pipeline = VK_NULL_HANDLE;
8670 layout = VK_NULL_HANDLE;
8671
8672 QRHI_RES_RHI(QRhiVulkan);
8673 if (rhiD) {
8674 rhiD->releaseQueue.append(e);
8675 rhiD->unregisterResource(this);
8676 }
8677}
8678
8680{
8681 if (pipeline || layout)
8682 destroy();
8683
8684 QRHI_RES_RHI(QRhiVulkan);
8685 rhiD->pipelineCreationStart();
8686 if (!rhiD->sanityCheckGraphicsPipeline(this))
8687 return false;
8688
8689 if (!rhiD->ensurePipelineCache())
8690 return false;
8691
8692 VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
8693 pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
8694 pipelineLayoutInfo.setLayoutCount = 1;
8695 QVkShaderResourceBindings *srbD = QRHI_RES(QVkShaderResourceBindings, m_shaderResourceBindings);
8696 Q_ASSERT(m_shaderResourceBindings && srbD->layout);
8697 pipelineLayoutInfo.pSetLayouts = &srbD->layout;
8698 VkResult err = rhiD->df->vkCreatePipelineLayout(rhiD->dev, &pipelineLayoutInfo, nullptr, &layout);
8699 if (err != VK_SUCCESS) {
8700 qWarning("Failed to create pipeline layout: %d", err);
8701 return false;
8702 }
8703 auto pipelineLayoutCleanup = qScopeGuard([this, rhiD] {
8704 rhiD->df->vkDestroyPipelineLayout(rhiD->dev, layout, nullptr);
8705 layout = VK_NULL_HANDLE;
8706 });
8707
8708 VkGraphicsPipelineCreateInfo pipelineInfo = {};
8709 pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
8710
8711 QVarLengthArray<VkShaderModule, 4> shaders;
8712 auto shaderModuleCleanup = qScopeGuard([rhiD, &shaders] {
8713 for (VkShaderModule shader : shaders)
8714 rhiD->df->vkDestroyShaderModule(rhiD->dev, shader, nullptr);
8715 });
8716 QVarLengthArray<VkPipelineShaderStageCreateInfo, 4> shaderStageCreateInfos;
8717 QVarLengthArray<QByteArray, 4> entryPointNames;
8718 for (const QRhiShaderStage &shaderStage : m_shaderStages) {
8719 const QShader bakedShader = shaderStage.shader();
8720 const QShaderCode spirv = bakedShader.shader({ QShader::SpirvShader, 100, shaderStage.shaderVariant() });
8721 if (spirv.shader().isEmpty()) {
8722 qWarning() << "No SPIR-V 1.0 shader code found in baked shader" << bakedShader;
8723 return false;
8724 }
8725 VkShaderModule shader = rhiD->createShader(spirv.shader());
8726 if (shader) {
8727 shaders.append(shader);
8728 VkPipelineShaderStageCreateInfo shaderInfo = {};
8729 shaderInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
8730 shaderInfo.stage = toVkShaderStage(shaderStage.type());
8731 shaderInfo.module = shader;
8732 entryPointNames.append(spirv.entryPoint());
8733 shaderInfo.pName = nullptr;
8734 shaderStageCreateInfos.append(shaderInfo);
8735 }
8736 else
8737 return false;
8738 }
8739 for (qsizetype i = 0, ie = shaders.count(); i != ie; ++i)
8740 shaderStageCreateInfos[i].pName = entryPointNames[i].constData();
8741
8742 pipelineInfo.stageCount = uint32_t(shaderStageCreateInfos.size());
8743 pipelineInfo.pStages = shaderStageCreateInfos.constData();
8744
8745 QVarLengthArray<VkVertexInputBindingDescription, 4> vertexBindings;
8746#ifdef VK_EXT_vertex_attribute_divisor
8747 QVarLengthArray<VkVertexInputBindingDivisorDescriptionEXT> nonOneStepRates;
8748#endif
8749 int bindingIndex = 0;
8750 for (auto it = m_vertexInputLayout.cbeginBindings(), itEnd = m_vertexInputLayout.cendBindings();
8751 it != itEnd; ++it, ++bindingIndex)
8752 {
8753 VkVertexInputBindingDescription bindingInfo = {
8754 uint32_t(bindingIndex),
8755 it->stride(),
8756 it->classification() == QRhiVertexInputBinding::PerVertex
8757 ? VK_VERTEX_INPUT_RATE_VERTEX : VK_VERTEX_INPUT_RATE_INSTANCE
8758 };
8759 if (it->classification() == QRhiVertexInputBinding::PerInstance && it->instanceStepRate() != 1) {
8760#ifdef VK_EXT_vertex_attribute_divisor
8761 if (rhiD->caps.vertexAttribDivisor) {
8762 nonOneStepRates.append({ uint32_t(bindingIndex), it->instanceStepRate() });
8763 } else
8764#endif
8765 {
8766 qWarning("QRhiVulkan: Instance step rates other than 1 not supported without "
8767 "VK_EXT_vertex_attribute_divisor on the device and "
8768 "VK_KHR_get_physical_device_properties2 on the instance");
8769 }
8770 }
8771 vertexBindings.append(bindingInfo);
8772 }
8773 QVarLengthArray<VkVertexInputAttributeDescription, 4> vertexAttributes;
8774 for (auto it = m_vertexInputLayout.cbeginAttributes(), itEnd = m_vertexInputLayout.cendAttributes();
8775 it != itEnd; ++it)
8776 {
8777 VkVertexInputAttributeDescription attributeInfo = {
8778 uint32_t(it->location()),
8779 uint32_t(it->binding()),
8780 toVkAttributeFormat(it->format()),
8781 it->offset()
8782 };
8783 vertexAttributes.append(attributeInfo);
8784 }
8785 VkPipelineVertexInputStateCreateInfo vertexInputInfo = {};
8786 vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
8787 vertexInputInfo.vertexBindingDescriptionCount = uint32_t(vertexBindings.size());
8788 vertexInputInfo.pVertexBindingDescriptions = vertexBindings.constData();
8789 vertexInputInfo.vertexAttributeDescriptionCount = uint32_t(vertexAttributes.size());
8790 vertexInputInfo.pVertexAttributeDescriptions = vertexAttributes.constData();
8791#ifdef VK_EXT_vertex_attribute_divisor
8792 VkPipelineVertexInputDivisorStateCreateInfoEXT divisorInfo = {};
8793 if (!nonOneStepRates.isEmpty()) {
8794 divisorInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT;
8795 divisorInfo.vertexBindingDivisorCount = uint32_t(nonOneStepRates.size());
8796 divisorInfo.pVertexBindingDivisors = nonOneStepRates.constData();
8797 vertexInputInfo.pNext = &divisorInfo;
8798 }
8799#endif
8800 pipelineInfo.pVertexInputState = &vertexInputInfo;
8801
8802 QVarLengthArray<VkDynamicState, 8> dynEnable;
8803 dynEnable << VK_DYNAMIC_STATE_VIEWPORT;
8804 dynEnable << VK_DYNAMIC_STATE_SCISSOR; // ignore UsesScissor - Vulkan requires a scissor for the viewport always
8805 if (m_flags.testFlag(QRhiGraphicsPipeline::UsesBlendConstants))
8806 dynEnable << VK_DYNAMIC_STATE_BLEND_CONSTANTS;
8807 if (m_flags.testFlag(QRhiGraphicsPipeline::UsesStencilRef))
8808 dynEnable << VK_DYNAMIC_STATE_STENCIL_REFERENCE;
8809#ifdef VK_KHR_fragment_shading_rate
8810 if (m_flags.testFlag(QRhiGraphicsPipeline::UsesShadingRate) && rhiD->caps.perDrawShadingRate)
8811 dynEnable << VK_DYNAMIC_STATE_FRAGMENT_SHADING_RATE_KHR;
8812#endif
8813
8814 VkPipelineDynamicStateCreateInfo dynamicInfo = {};
8815 dynamicInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
8816 dynamicInfo.dynamicStateCount = uint32_t(dynEnable.size());
8817 dynamicInfo.pDynamicStates = dynEnable.constData();
8818 pipelineInfo.pDynamicState = &dynamicInfo;
8819
8820 VkPipelineViewportStateCreateInfo viewportInfo = {};
8821 viewportInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
8822 viewportInfo.viewportCount = viewportInfo.scissorCount = 1;
8823 pipelineInfo.pViewportState = &viewportInfo;
8824
8825 VkPipelineInputAssemblyStateCreateInfo inputAsmInfo = {};
8826 inputAsmInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
8827 inputAsmInfo.topology = toVkTopology(m_topology);
8828 inputAsmInfo.primitiveRestartEnable = (m_topology == TriangleStrip || m_topology == LineStrip);
8829 pipelineInfo.pInputAssemblyState = &inputAsmInfo;
8830
8831 VkPipelineTessellationStateCreateInfo tessInfo = {};
8832#ifdef VK_VERSION_1_1
8833 VkPipelineTessellationDomainOriginStateCreateInfo originInfo = {};
8834#endif
8835 if (m_topology == Patches) {
8836 tessInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO;
8837 tessInfo.patchControlPoints = uint32_t(qMax(1, m_patchControlPointCount));
8838
8839 // To be able to use the same tess.evaluation shader with both OpenGL
8840 // and Vulkan, flip the tessellation domain origin to be lower left.
8841 // This allows declaring the winding order in the shader to be CCW and
8842 // still have it working with both APIs. This requires Vulkan 1.1 (or
8843 // VK_KHR_maintenance2 but don't bother with that).
8844#ifdef VK_VERSION_1_1
8845 if (rhiD->caps.apiVersion >= QVersionNumber(1, 1)) {
8846 originInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO;
8847 originInfo.domainOrigin = VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT;
8848 tessInfo.pNext = &originInfo;
8849 } else {
8850 qWarning("Proper tessellation support requires Vulkan 1.1 or newer, leaving domain origin unset");
8851 }
8852#else
8853 qWarning("QRhi was built without Vulkan 1.1 headers, this is not sufficient for proper tessellation support");
8854#endif
8855
8856 pipelineInfo.pTessellationState = &tessInfo;
8857 }
8858
8859 VkPipelineRasterizationStateCreateInfo rastInfo = {};
8860 rastInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
8861 if (m_depthClamp && rhiD->caps.depthClamp)
8862 rastInfo.depthClampEnable = m_depthClamp;
8863 rastInfo.cullMode = toVkCullMode(m_cullMode);
8864 rastInfo.frontFace = toVkFrontFace(m_frontFace);
8865 if (m_depthBias != 0 || !qFuzzyIsNull(m_slopeScaledDepthBias)) {
8866 rastInfo.depthBiasEnable = true;
8867 rastInfo.depthBiasConstantFactor = float(m_depthBias);
8868 rastInfo.depthBiasSlopeFactor = m_slopeScaledDepthBias;
8869 }
8870 rastInfo.lineWidth = rhiD->caps.wideLines ? m_lineWidth : 1.0f;
8871 rastInfo.polygonMode = toVkPolygonMode(m_polygonMode);
8872 pipelineInfo.pRasterizationState = &rastInfo;
8873
8874 VkPipelineMultisampleStateCreateInfo msInfo = {};
8875 msInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
8876 msInfo.rasterizationSamples = rhiD->effectiveSampleCountBits(m_sampleCount);
8877 pipelineInfo.pMultisampleState = &msInfo;
8878
8879 VkPipelineDepthStencilStateCreateInfo dsInfo = {};
8880 dsInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
8881 dsInfo.depthTestEnable = m_depthTest;
8882 dsInfo.depthWriteEnable = m_depthWrite;
8883 dsInfo.depthCompareOp = toVkCompareOp(m_depthOp);
8884 dsInfo.stencilTestEnable = m_stencilTest;
8885 if (m_stencilTest) {
8886 fillVkStencilOpState(&dsInfo.front, m_stencilFront);
8887 dsInfo.front.compareMask = m_stencilReadMask;
8888 dsInfo.front.writeMask = m_stencilWriteMask;
8889 fillVkStencilOpState(&dsInfo.back, m_stencilBack);
8890 dsInfo.back.compareMask = m_stencilReadMask;
8891 dsInfo.back.writeMask = m_stencilWriteMask;
8892 }
8893 pipelineInfo.pDepthStencilState = &dsInfo;
8894
8895 VkPipelineColorBlendStateCreateInfo blendInfo = {};
8896 blendInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
8897 QVarLengthArray<VkPipelineColorBlendAttachmentState, 4> vktargetBlends;
8898 for (const QRhiGraphicsPipeline::TargetBlend &b : std::as_const(m_targetBlends)) {
8899 VkPipelineColorBlendAttachmentState blend = {};
8900 blend.blendEnable = b.enable;
8901 blend.srcColorBlendFactor = toVkBlendFactor(b.srcColor);
8902 blend.dstColorBlendFactor = toVkBlendFactor(b.dstColor);
8903 blend.colorBlendOp = toVkBlendOp(b.opColor);
8904 blend.srcAlphaBlendFactor = toVkBlendFactor(b.srcAlpha);
8905 blend.dstAlphaBlendFactor = toVkBlendFactor(b.dstAlpha);
8906 blend.alphaBlendOp = toVkBlendOp(b.opAlpha);
8907 blend.colorWriteMask = toVkColorComponents(b.colorWrite);
8908 vktargetBlends.append(blend);
8909 }
8910 if (vktargetBlends.isEmpty()) {
8911 VkPipelineColorBlendAttachmentState blend = {};
8912 blend.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT
8913 | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
8914 vktargetBlends.append(blend);
8915 }
8916 blendInfo.attachmentCount = uint32_t(vktargetBlends.size());
8917 blendInfo.pAttachments = vktargetBlends.constData();
8918 pipelineInfo.pColorBlendState = &blendInfo;
8919
8920 pipelineInfo.layout = layout;
8921
8922 Q_ASSERT(m_renderPassDesc && QRHI_RES(const QVkRenderPassDescriptor, m_renderPassDesc)->rp);
8923 pipelineInfo.renderPass = QRHI_RES(const QVkRenderPassDescriptor, m_renderPassDesc)->rp;
8924
8925 err = rhiD->df->vkCreateGraphicsPipelines(rhiD->dev, rhiD->pipelineCache, 1, &pipelineInfo, nullptr, &pipeline);
8926
8927 if (err != VK_SUCCESS) {
8928 qWarning("Failed to create graphics pipeline: %d", err);
8929 return false;
8930 }
8931 pipelineLayoutCleanup.dismiss();
8932
8933 rhiD->setObjectName(uint64_t(pipeline), VK_OBJECT_TYPE_PIPELINE, m_objectName);
8934
8935 rhiD->pipelineCreationEnd();
8937 generation += 1;
8938 rhiD->registerResource(this);
8939 return true;
8940}
8941
8944{
8945}
8946
8951
8953{
8954 if (!pipeline && !layout)
8955 return;
8956
8960
8961 e.pipelineState.pipeline = pipeline;
8962 e.pipelineState.layout = layout;
8963
8964 pipeline = VK_NULL_HANDLE;
8965 layout = VK_NULL_HANDLE;
8966
8967 QRHI_RES_RHI(QRhiVulkan);
8968 if (rhiD) {
8969 rhiD->releaseQueue.append(e);
8970 rhiD->unregisterResource(this);
8971 }
8972}
8973
8975{
8976 if (pipeline || layout)
8977 destroy();
8978
8979 QRHI_RES_RHI(QRhiVulkan);
8980 rhiD->pipelineCreationStart();
8981 if (!rhiD->ensurePipelineCache())
8982 return false;
8983
8984 VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
8985 pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
8986 pipelineLayoutInfo.setLayoutCount = 1;
8987 QVkShaderResourceBindings *srbD = QRHI_RES(QVkShaderResourceBindings, m_shaderResourceBindings);
8988 Q_ASSERT(m_shaderResourceBindings && srbD->layout);
8989 pipelineLayoutInfo.pSetLayouts = &srbD->layout;
8990 VkResult err = rhiD->df->vkCreatePipelineLayout(rhiD->dev, &pipelineLayoutInfo, nullptr, &layout);
8991 if (err != VK_SUCCESS) {
8992 qWarning("Failed to create pipeline layout: %d", err);
8993 return false;
8994 }
8995 auto pipelineLayoutCleanup = qScopeGuard([this, rhiD] {
8996 rhiD->df->vkDestroyPipelineLayout(rhiD->dev, layout, nullptr);
8997 layout = VK_NULL_HANDLE;
8998 });
8999
9000 VkComputePipelineCreateInfo pipelineInfo = {};
9001 pipelineInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
9002 pipelineInfo.layout = layout;
9003
9004 if (m_shaderStage.type() != QRhiShaderStage::Compute) {
9005 qWarning("Compute pipeline requires a compute shader stage");
9006 return false;
9007 }
9008 const QShader bakedShader = m_shaderStage.shader();
9009 const QShaderCode spirv = bakedShader.shader({ QShader::SpirvShader, 100, m_shaderStage.shaderVariant() });
9010 if (spirv.shader().isEmpty()) {
9011 qWarning() << "No SPIR-V 1.0 shader code found in baked shader" << bakedShader;
9012 return false;
9013 }
9014 if (bakedShader.stage() != QShader::ComputeStage) {
9015 qWarning() << bakedShader << "is not a compute shader";
9016 return false;
9017 }
9018 VkShaderModule shader = rhiD->createShader(spirv.shader());
9019 if (!shader)
9020 return false;
9021 auto shaderModuleCleanup = qScopeGuard([rhiD, shader] {
9022 rhiD->df->vkDestroyShaderModule(rhiD->dev, shader, nullptr);
9023 });
9024 VkPipelineShaderStageCreateInfo shaderInfo = {};
9025 shaderInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
9026 shaderInfo.stage = VK_SHADER_STAGE_COMPUTE_BIT;
9027 shaderInfo.module = shader;
9028 const QByteArray entryPointName = spirv.entryPoint();
9029 shaderInfo.pName = entryPointName.constData();
9030 pipelineInfo.stage = shaderInfo;
9031
9032 err = rhiD->df->vkCreateComputePipelines(rhiD->dev, rhiD->pipelineCache, 1, &pipelineInfo, nullptr, &pipeline);
9033 if (err != VK_SUCCESS) {
9034 qWarning("Failed to create graphics pipeline: %d", err);
9035 return false;
9036 }
9037 pipelineLayoutCleanup.dismiss();
9038
9039 rhiD->setObjectName(uint64_t(pipeline), VK_OBJECT_TYPE_PIPELINE, m_objectName);
9040
9041 rhiD->pipelineCreationEnd();
9043 generation += 1;
9044 rhiD->registerResource(this);
9045 return true;
9046}
9047
9048QVkCommandBuffer::QVkCommandBuffer(QRhiImplementation *rhi)
9050{
9052}
9053
9058
9060{
9061 // nothing to do here, cb is not owned by us
9062}
9063
9065{
9066 // Ok this is messy but no other way has been devised yet. Outside
9067 // begin(Compute)Pass - end(Compute)Pass it is simple - just return the
9068 // primary VkCommandBuffer. Inside, however, we need to provide the current
9069 // secondary command buffer (typically the one started by beginExternal(),
9070 // in case we are between beginExternal - endExternal inside a pass).
9071
9073 nativeHandlesStruct.commandBuffer = cb;
9074 } else {
9075 if (passUsesSecondaryCb && !activeSecondaryCbStack.isEmpty())
9076 nativeHandlesStruct.commandBuffer = activeSecondaryCbStack.last();
9077 else
9078 nativeHandlesStruct.commandBuffer = cb;
9079 }
9080
9081 return &nativeHandlesStruct;
9082}
9083
9084QVkSwapChain::QVkSwapChain(QRhiImplementation *rhi)
9085 : QRhiSwapChain(rhi),
9086 rtWrapper(rhi, this),
9087 rtWrapperRight(rhi, this),
9088 cbWrapper(rhi)
9089{
9090}
9091
9096
9098{
9099 if (sc == VK_NULL_HANDLE)
9100 return;
9101
9102 QRHI_RES_RHI(QRhiVulkan);
9103 if (rhiD) {
9104 rhiD->swapchains.remove(this);
9106 }
9107
9108 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
9109 QVkSwapChain::FrameResources &frame(frameRes[i]);
9110 frame.cmdBuf = VK_NULL_HANDLE;
9111 frame.timestampQueryIndex = -1;
9112 }
9113
9114 surface = lastConnectedSurface = VK_NULL_HANDLE;
9115
9116 if (rhiD)
9117 rhiD->unregisterResource(this);
9118}
9119
9121{
9122 return &cbWrapper;
9123}
9124
9126{
9127 return &rtWrapper;
9128}
9129
9131{
9132 return !stereo || targetBuffer == StereoTargetBuffer::LeftBuffer ? &rtWrapper : &rtWrapperRight;
9133}
9134
9136{
9137 if (!ensureSurface())
9138 return QSize();
9139
9140 // The size from the QWindow may not exactly match the surface... so if a
9141 // size is reported from the surface, use that.
9142 VkSurfaceCapabilitiesKHR surfaceCaps = {};
9143 QRHI_RES_RHI(QRhiVulkan);
9144 rhiD->vkGetPhysicalDeviceSurfaceCapabilitiesKHR(rhiD->physDev, surface, &surfaceCaps);
9145 VkExtent2D bufferSize = surfaceCaps.currentExtent;
9146 if (bufferSize.width == uint32_t(-1)) {
9147 Q_ASSERT(bufferSize.height == uint32_t(-1));
9148 return m_window->size() * m_window->devicePixelRatio();
9149 }
9150 return QSize(int(bufferSize.width), int(bufferSize.height));
9151}
9152
9153static inline bool hdrFormatMatchesVkSurfaceFormat(QRhiSwapChain::Format f, const VkSurfaceFormatKHR &s)
9154{
9155 switch (f) {
9156 case QRhiSwapChain::HDRExtendedSrgbLinear:
9157 return s.format == VK_FORMAT_R16G16B16A16_SFLOAT
9158 && s.colorSpace == VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT;
9159 case QRhiSwapChain::HDR10:
9160 return s.format == VK_FORMAT_A2B10G10R10_UNORM_PACK32
9161 && s.colorSpace == VK_COLOR_SPACE_HDR10_ST2084_EXT;
9162 case QRhiSwapChain::HDRExtendedDisplayP3Linear:
9163 return s.format == VK_FORMAT_R16G16B16A16_SFLOAT
9164 && s.colorSpace == VK_COLOR_SPACE_DISPLAY_P3_LINEAR_EXT;
9165 default:
9166 break;
9167 }
9168 return false;
9169}
9170
9172{
9173 if (f == SDR)
9174 return true;
9175
9176 if (!m_window) {
9177 qWarning("Attempted to call isFormatSupported() without a window set");
9178 return false;
9179 }
9180
9181 // we may be called before create so query the surface
9182 VkSurfaceKHR surf = QVulkanInstance::surfaceForWindow(m_window);
9183
9184 QRHI_RES_RHI(QRhiVulkan);
9185 uint32_t formatCount = 0;
9186 rhiD->vkGetPhysicalDeviceSurfaceFormatsKHR(rhiD->physDev, surf, &formatCount, nullptr);
9187 QVarLengthArray<VkSurfaceFormatKHR, 8> formats(formatCount);
9188 if (formatCount) {
9189 rhiD->vkGetPhysicalDeviceSurfaceFormatsKHR(rhiD->physDev, surf, &formatCount, formats.data());
9190 for (uint32_t i = 0; i < formatCount; ++i) {
9191 if (hdrFormatMatchesVkSurfaceFormat(f, formats[i]))
9192 return true;
9193 }
9194 }
9195
9196 return false;
9197}
9198
9200{
9201 QRhiSwapChainHdrInfo info = QRhiSwapChain::hdrInfo();
9202#ifdef Q_OS_WIN
9203 QRHI_RES_RHI(QRhiVulkan);
9204 // Must use m_window, not window, given this may be called before createOrResize().
9205 if (m_window && rhiD->adapterLuidValid)
9206 info = rhiD->dxgiHdrInfo->queryHdrInfo(m_window);
9207#endif
9208 return info;
9209}
9210
9212{
9213 // not yet built so cannot rely on data computed in createOrResize()
9214
9215 if (!ensureSurface()) // make sure sampleCount and colorFormat reflect what was requested
9216 return nullptr;
9217
9218 QRHI_RES_RHI(QRhiVulkan);
9219 QVkRenderPassDescriptor *rp = new QVkRenderPassDescriptor(m_rhi);
9220 if (!rhiD->createDefaultRenderPass(rp,
9221 m_depthStencil != nullptr,
9222 samples,
9223 colorFormat,
9224 m_shadingRateMap))
9225 {
9226 delete rp;
9227 return nullptr;
9228 }
9229
9230 rp->ownsRp = true;
9232 rhiD->registerResource(rp);
9233 return rp;
9234}
9235
9236static inline bool isSrgbFormat(VkFormat format)
9237{
9238 switch (format) {
9239 case VK_FORMAT_R8_SRGB:
9240 case VK_FORMAT_R8G8_SRGB:
9241 case VK_FORMAT_R8G8B8_SRGB:
9242 case VK_FORMAT_B8G8R8_SRGB:
9243 case VK_FORMAT_R8G8B8A8_SRGB:
9244 case VK_FORMAT_B8G8R8A8_SRGB:
9245 case VK_FORMAT_A8B8G8R8_SRGB_PACK32:
9246 return true;
9247 default:
9248 return false;
9249 }
9250}
9251
9253{
9254 // Do nothing when already done, however window may change so check the
9255 // surface is still the same. Some of the queries below are very expensive
9256 // with some implementations so it is important to do the rest only once
9257 // per surface.
9258
9259 Q_ASSERT(m_window);
9260 VkSurfaceKHR surf = QVulkanInstance::surfaceForWindow(m_window);
9261 if (!surf) {
9262 qWarning("Failed to get surface for window");
9263 return false;
9264 }
9265 if (surface == surf)
9266 return true;
9267
9268 surface = surf;
9269
9270 QRHI_RES_RHI(QRhiVulkan);
9271 if (!rhiD->inst->supportsPresent(rhiD->physDev, rhiD->gfxQueueFamilyIdx, m_window)) {
9272 qWarning("Presenting not supported on this window");
9273 return false;
9274 }
9275
9276 quint32 formatCount = 0;
9277 rhiD->vkGetPhysicalDeviceSurfaceFormatsKHR(rhiD->physDev, surface, &formatCount, nullptr);
9278 QList<VkSurfaceFormatKHR> formats(formatCount);
9279 rhiD->vkGetPhysicalDeviceSurfaceFormatsKHR(rhiD->physDev, surface, &formatCount,
9280 formats.data());
9281
9282 // Initially select the first available format, will only be used as a worst-case fallback
9283 colorFormat = formats.constFirst().format;
9284 colorSpace = formats.constFirst().colorSpace;
9285
9286 // See if we can find the preferred SDR format
9287 const bool srgbRequested = m_flags.testFlag(sRGB);
9288 bool foundBestFormat = false;
9289 if (m_format == SDR) {
9290 for (int i = 0; i < int(formatCount); ++i) {
9291 bool ok;
9292 if (srgbRequested) {
9293 ok = defaultSrgbColorFormat == formats[i].format;
9294 } else {
9295 ok = defaultColorFormat == formats[i].format;
9296 }
9297 if (ok) {
9298 foundBestFormat = true;
9299 colorFormat = formats[i].format;
9300 colorSpace = formats[i].colorSpace;
9301 break;
9302 }
9303 }
9304 }
9305
9306 // Otherwise pick one that fits our requirements:
9307 if (!foundBestFormat && (m_format != SDR || srgbRequested)) {
9308 for (int i = 0; i < int(formatCount); ++i) {
9309 bool ok = false;
9310 if (m_format != SDR) {
9311 ok = hdrFormatMatchesVkSurfaceFormat(m_format, formats[i]);
9312 } else if (srgbRequested) {
9313 ok = isSrgbFormat(formats[i].format);
9314 }
9315 if (ok) {
9316 colorFormat = formats[i].format;
9317 colorSpace = formats[i].colorSpace;
9318 break;
9319 }
9320 }
9321 }
9322
9323 // Although this should be rare, warn when we fail to select a suitable format
9324 if (m_format != SDR) {
9325 VkSurfaceFormatKHR format{};
9326 format.format = colorFormat;
9327 format.colorSpace = colorSpace;
9328 if (!hdrFormatMatchesVkSurfaceFormat(m_format, format)) {
9329 qWarning("Failed to select a suitable VkFormat for HDR, using format %d as fallback",
9330 colorFormat);
9331 }
9332 } else {
9333 if (srgbRequested && !isSrgbFormat(colorFormat)) {
9334 qWarning("Failed to select a suitable VkFormat for sRGB, using format %d as fallback",
9335 colorFormat);
9336 }
9337 }
9338
9339#if QT_CONFIG(wayland)
9340 // On Wayland, only one color management surface can be created at a time without
9341 // triggering a protocol error, and we create one ourselves in some situations.
9342 // To avoid this problem, use VK_COLOR_SPACE_PASS_THROUGH_EXT when supported,
9343 // so that the driver doesn't create a color management surface as well.
9344 const bool hasPassThrough =
9345 std::any_of(formats.begin(), formats.end(), [this](const VkSurfaceFormatKHR &fmt) {
9346 return fmt.format == colorFormat
9347 && fmt.colorSpace == VK_COLOR_SPACE_PASS_THROUGH_EXT;
9348 });
9349 if (hasPassThrough) {
9350 colorSpace = VK_COLOR_SPACE_PASS_THROUGH_EXT;
9351 }
9352#endif
9353
9354 samples = rhiD->effectiveSampleCountBits(m_sampleCount);
9355
9356 quint32 presModeCount = 0;
9357 rhiD->vkGetPhysicalDeviceSurfacePresentModesKHR(rhiD->physDev, surface, &presModeCount, nullptr);
9358 supportedPresentationModes.resize(presModeCount);
9359 rhiD->vkGetPhysicalDeviceSurfacePresentModesKHR(rhiD->physDev, surface, &presModeCount,
9360 supportedPresentationModes.data());
9361
9362 return true;
9363}
9364
9366{
9367 QRHI_RES_RHI(QRhiVulkan);
9368 const bool needsRegistration = !window || window != m_window;
9369
9370 // Can be called multiple times due to window resizes - that is not the
9371 // same as a simple destroy+create (as with other resources). Thus no
9372 // destroy() here. See recreateSwapChain().
9373
9374 // except if the window actually changes
9375 if (window && window != m_window)
9376 destroy();
9377
9378 window = m_window;
9379 m_currentPixelSize = surfacePixelSize();
9380 pixelSize = m_currentPixelSize;
9381
9382 if (!rhiD->recreateSwapChain(this)) {
9383 qWarning("Failed to create new swapchain");
9384 return false;
9385 }
9386
9387 if (needsRegistration || !rhiD->swapchains.contains(this))
9388 rhiD->swapchains.insert(this);
9389
9390 if (m_depthStencil && m_depthStencil->sampleCount() != m_sampleCount) {
9391 qWarning("Depth-stencil buffer's sampleCount (%d) does not match color buffers' sample count (%d). Expect problems.",
9392 m_depthStencil->sampleCount(), m_sampleCount);
9393 }
9394 if (m_depthStencil && m_depthStencil->pixelSize() != pixelSize) {
9395 if (m_depthStencil->flags().testFlag(QRhiRenderBuffer::UsedWithSwapChainOnly)) {
9396 m_depthStencil->setPixelSize(pixelSize);
9397 if (!m_depthStencil->create())
9398 qWarning("Failed to rebuild swapchain's associated depth-stencil buffer for size %dx%d",
9399 pixelSize.width(), pixelSize.height());
9400 } else {
9401 qWarning("Depth-stencil buffer's size (%dx%d) does not match the surface size (%dx%d). Expect problems.",
9402 m_depthStencil->pixelSize().width(), m_depthStencil->pixelSize().height(),
9403 pixelSize.width(), pixelSize.height());
9404 }
9405 }
9406
9407 if (!m_renderPassDesc)
9408 qWarning("QVkSwapChain: No renderpass descriptor set. See newCompatibleRenderPassDescriptor() and setRenderPassDescriptor().");
9409
9410 rtWrapper.setRenderPassDescriptor(m_renderPassDesc); // for the public getter in QRhiRenderTarget
9411 rtWrapper.d.rp = QRHI_RES(QVkRenderPassDescriptor, m_renderPassDesc);
9412 Q_ASSERT(rtWrapper.d.rp && rtWrapper.d.rp->rp);
9413
9414 rtWrapper.d.pixelSize = pixelSize;
9415 rtWrapper.d.dpr = float(window->devicePixelRatio());
9416 rtWrapper.d.sampleCount = samples;
9417 rtWrapper.d.colorAttCount = 1;
9418 if (m_depthStencil) {
9419 rtWrapper.d.dsAttCount = 1;
9420 ds = QRHI_RES(QVkRenderBuffer, m_depthStencil);
9421 } else {
9422 rtWrapper.d.dsAttCount = 0;
9423 ds = nullptr;
9424 }
9425 rtWrapper.d.dsResolveAttCount = 0;
9426 if (samples > VK_SAMPLE_COUNT_1_BIT)
9427 rtWrapper.d.resolveAttCount = 1;
9428 else
9429 rtWrapper.d.resolveAttCount = 0;
9430
9431 if (shadingRateMapView)
9432 rtWrapper.d.shadingRateAttCount = 1;
9433 else
9434 rtWrapper.d.shadingRateAttCount = 0;
9435
9436 for (int i = 0; i < bufferCount; ++i) {
9437 QVkSwapChain::ImageResources &image(imageRes[i]);
9438 // color, ds, resolve, shading rate
9439 QVarLengthArray<VkImageView, 4> views;
9440 views.append(samples > VK_SAMPLE_COUNT_1_BIT ? image.msaaImageView : image.imageView);
9441 if (ds)
9442 views.append(ds->imageView);
9443 if (samples > VK_SAMPLE_COUNT_1_BIT)
9444 views.append(image.imageView);
9445 if (shadingRateMapView)
9446 views.append(shadingRateMapView);
9447
9448 VkFramebufferCreateInfo fbInfo = {};
9449 fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
9450 fbInfo.renderPass = rtWrapper.d.rp->rp;
9451 fbInfo.attachmentCount = uint32_t(views.count());
9452 fbInfo.pAttachments = views.constData();
9453 fbInfo.width = uint32_t(pixelSize.width());
9454 fbInfo.height = uint32_t(pixelSize.height());
9455 fbInfo.layers = 1;
9456
9457 VkResult err = rhiD->df->vkCreateFramebuffer(rhiD->dev, &fbInfo, nullptr, &image.fb);
9458 if (err != VK_SUCCESS) {
9459 qWarning("Failed to create framebuffer: %d", err);
9460 return false;
9461 }
9462 }
9463
9464 if (stereo) {
9465 rtWrapperRight.setRenderPassDescriptor(
9466 m_renderPassDesc); // for the public getter in QRhiRenderTarget
9467 rtWrapperRight.d.rp = QRHI_RES(QVkRenderPassDescriptor, m_renderPassDesc);
9468 Q_ASSERT(rtWrapperRight.d.rp && rtWrapperRight.d.rp->rp);
9469
9470 rtWrapperRight.d.pixelSize = pixelSize;
9471 rtWrapperRight.d.dpr = float(window->devicePixelRatio());
9472 rtWrapperRight.d.sampleCount = samples;
9473 rtWrapperRight.d.colorAttCount = 1;
9474 if (m_depthStencil) {
9475 rtWrapperRight.d.dsAttCount = 1;
9476 ds = QRHI_RES(QVkRenderBuffer, m_depthStencil);
9477 } else {
9478 rtWrapperRight.d.dsAttCount = 0;
9479 ds = nullptr;
9480 }
9481 rtWrapperRight.d.dsResolveAttCount = 0;
9482 if (samples > VK_SAMPLE_COUNT_1_BIT)
9483 rtWrapperRight.d.resolveAttCount = 1;
9484 else
9485 rtWrapperRight.d.resolveAttCount = 0;
9486
9487 for (int i = 0; i < bufferCount; ++i) {
9488 QVkSwapChain::ImageResources &image(imageRes[i + bufferCount]);
9489 // color, ds, resolve, shading rate
9490 QVarLengthArray<VkImageView, 4> views;
9491 views.append(samples > VK_SAMPLE_COUNT_1_BIT ? image.msaaImageView : image.imageView);
9492 if (ds)
9493 views.append(ds->imageView);
9494 if (samples > VK_SAMPLE_COUNT_1_BIT)
9495 views.append(image.imageView);
9496 if (shadingRateMapView)
9497 views.append(shadingRateMapView);
9498
9499 VkFramebufferCreateInfo fbInfo = {};
9500 fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
9501 fbInfo.renderPass = rtWrapperRight.d.rp->rp;
9502 fbInfo.attachmentCount = uint32_t(views.count());
9503 fbInfo.pAttachments = views.constData();
9504 fbInfo.width = uint32_t(pixelSize.width());
9505 fbInfo.height = uint32_t(pixelSize.height());
9506 fbInfo.layers = 1;
9507
9508 VkResult err = rhiD->df->vkCreateFramebuffer(rhiD->dev, &fbInfo, nullptr, &image.fb);
9509 if (err != VK_SUCCESS) {
9510 qWarning("Failed to create framebuffer: %d", err);
9511 return false;
9512 }
9513 }
9514 }
9515
9516 frameCount = 0;
9517
9518 if (needsRegistration)
9519 rhiD->registerResource(this);
9520
9521 return true;
9522}
9523
9524QT_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)