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