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