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