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