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