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