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