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