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