Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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
4#include "qrhivulkan_p.h"
5#include <qpa/qplatformvulkaninstance.h>
6
7#define VMA_IMPLEMENTATION
8#define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
9#define VMA_STATIC_VULKAN_FUNCTIONS 0
10#define VMA_RECORDING_ENABLED 0
11#define VMA_DEDICATED_ALLOCATION 0
12#ifdef QT_DEBUG
13#define VMA_DEBUG_INITIALIZE_ALLOCATIONS 1
14#endif
16QT_WARNING_DISABLE_GCC("-Wsuggest-override")
17#if defined(Q_CC_CLANG) && Q_CC_CLANG >= 1100
18QT_WARNING_DISABLE_CLANG("-Wdeprecated-copy")
19#endif
20#include "vk_mem_alloc.h"
22
23#include <qmath.h>
24#include <QVulkanFunctions>
25#include <QtGui/qwindow.h>
26#include <optional>
27
29
30/*
31 Vulkan 1.0 backend. Provides a double-buffered swapchain that throttles the
32 rendering thread to vsync. Textures and "static" buffers are device local,
33 and a separate, host visible staging buffer is used to upload data to them.
34 "Dynamic" buffers are in host visible memory and are duplicated (since there
35 can be 2 frames in flight). This is handled transparently to the application.
36
37 Barriers are generated automatically for each render or compute pass, based
38 on the resources that are used in that pass (in QRhiShaderResourceBindings,
39 vertex inputs, etc.). This implies deferring the recording of the command
40 buffer since the barriers have to be placed at the right place (before the
41 pass), and that can only be done once we know all the things the pass does.
42
43 This in turn has implications for integrating external commands
44 (beginExternal() - direct Vulkan calls - endExternal()) because that is
45 incompatible with this approach by nature. Therefore we support another mode
46 of operation, where each render or compute pass uses one or more secondary
47 command buffers (recorded right away), with each beginExternal() leading to
48 closing the current secondary cb, creating a new secondary cb for the
49 external content, and then starting yet another one in endExternal() for
50 whatever comes afterwards in the pass. This way the primary command buffer
51 only has vkCmdExecuteCommand(s) within a renderpass instance
52 (Begin-EndRenderPass). (i.e. our only subpass is then
53 VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS instead of
54 VK_SUBPASS_CONTENTS_INLINE)
55
56 The command buffer management mode is decided on a per frame basis,
57 controlled by the ExternalContentsInPass flag of beginFrame().
58*/
59
286template <class Int>
287inline Int aligned(Int v, Int byteAlign)
288{
289 return (v + byteAlign - 1) & ~(byteAlign - 1);
290}
291
293
294static VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL wrap_vkGetInstanceProcAddr(VkInstance, const char *pName)
295{
296 return globalVulkanInstance->getInstanceProcAddr(pName);
297}
298
299static VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL wrap_vkGetDeviceProcAddr(VkDevice device, const char *pName)
300{
301 return globalVulkanInstance->functions()->vkGetDeviceProcAddr(device, pName);
302}
303
304static inline VmaAllocation toVmaAllocation(QVkAlloc a)
305{
306 return reinterpret_cast<VmaAllocation>(a);
307}
308
309static inline VmaAllocator toVmaAllocator(QVkAllocator a)
310{
311 return reinterpret_cast<VmaAllocator>(a);
312}
313
321QByteArrayList QRhiVulkanInitParams::preferredInstanceExtensions()
322{
323 return {
324 QByteArrayLiteral("VK_KHR_get_physical_device_properties2")
325 };
326}
327
333QByteArrayList QRhiVulkanInitParams::preferredExtensionsForImportedDevice()
334{
335 return {
336 QByteArrayLiteral("VK_KHR_swapchain"),
337 QByteArrayLiteral("VK_EXT_vertex_attribute_divisor"),
338 QByteArrayLiteral("VK_KHR_create_renderpass2"),
339 QByteArrayLiteral("VK_KHR_depth_stencil_resolve")
340 };
341}
342
344 : ofr(this)
345{
346 inst = params->inst;
347 maybeWindow = params->window; // may be null
348 requestedDeviceExtensions = params->deviceExtensions;
349
350 if (importParams) {
351 physDev = importParams->physDev;
352 dev = importParams->dev;
353 if (dev && physDev) {
354 importedDevice = true;
355 gfxQueueFamilyIdx = importParams->gfxQueueFamilyIdx;
356 gfxQueueIdx = importParams->gfxQueueIdx;
357 // gfxQueue is output only, no point in accepting it as input
358 if (importParams->vmemAllocator) {
359 importedAllocator = true;
360 allocator = importParams->vmemAllocator;
361 }
362 }
363 }
364}
365
366static bool qvk_debug_filter(QVulkanInstance::DebugMessageSeverityFlags severity,
367 QVulkanInstance::DebugMessageTypeFlags type,
368 const void *callbackData)
369{
371 Q_UNUSED(type);
372#ifdef VK_EXT_debug_utils
373 const VkDebugUtilsMessengerCallbackDataEXT *d = static_cast<const VkDebugUtilsMessengerCallbackDataEXT *>(callbackData);
374
375 // Filter out certain misleading validation layer messages, as per
376 // VulkanMemoryAllocator documentation.
377 if (strstr(d->pMessage, "Mapping an image with layout")
378 && strstr(d->pMessage, "can result in undefined behavior if this memory is used by the device"))
379 {
380 return true;
381 }
382
383 // In certain cases allocateDescriptorSet() will attempt to allocate from a
384 // pool that does not have enough descriptors of a certain type. This makes
385 // the validation layer shout. However, this is not an error since we will
386 // then move on to another pool. If there is a real error, a qWarning
387 // message is shown by allocateDescriptorSet(), so the validation warning
388 // does not have any value and is just noise.
389 if (strstr(d->pMessage, "VUID-VkDescriptorSetAllocateInfo-descriptorPool-00307"))
390 return true;
391#else
392 Q_UNUSED(callbackData);
393#endif
394 return false;
395}
396
397static inline QRhiDriverInfo::DeviceType toRhiDeviceType(VkPhysicalDeviceType type)
398{
399 switch (type) {
400 case VK_PHYSICAL_DEVICE_TYPE_OTHER:
402 case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
404 case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
406 case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
408 case VK_PHYSICAL_DEVICE_TYPE_CPU:
410 default:
412 }
413}
414
415bool QRhiVulkan::create(QRhi::Flags flags)
416{
417 Q_ASSERT(inst);
418 if (!inst->isValid()) {
419 qWarning("Vulkan instance is not valid");
420 return false;
421 }
422
423 rhiFlags = flags;
424 qCDebug(QRHI_LOG_INFO, "Initializing QRhi Vulkan backend %p with flags %d", this, int(rhiFlags));
425
426 globalVulkanInstance = inst; // used for function resolving in vkmemalloc callbacks
427 f = inst->functions();
428 if (QRHI_LOG_INFO().isEnabled(QtDebugMsg)) {
429 qCDebug(QRHI_LOG_INFO, "Enabled instance extensions:");
430 for (const char *ext : inst->extensions())
431 qCDebug(QRHI_LOG_INFO, " %s", ext);
432 }
433
434 caps = {};
435 caps.debugUtils = inst->extensions().contains(QByteArrayLiteral("VK_EXT_debug_utils"));
436
437 QList<VkQueueFamilyProperties> queueFamilyProps;
438 auto queryQueueFamilyProps = [this, &queueFamilyProps] {
439 uint32_t queueCount = 0;
440 f->vkGetPhysicalDeviceQueueFamilyProperties(physDev, &queueCount, nullptr);
441 queueFamilyProps.resize(int(queueCount));
442 f->vkGetPhysicalDeviceQueueFamilyProperties(physDev, &queueCount, queueFamilyProps.data());
443 };
444
445 // Choose a physical device, unless one was provided in importParams.
446 if (!physDev) {
447 uint32_t physDevCount = 0;
448 f->vkEnumeratePhysicalDevices(inst->vkInstance(), &physDevCount, nullptr);
449 if (!physDevCount) {
450 qWarning("No physical devices");
451 return false;
452 }
453 QVarLengthArray<VkPhysicalDevice, 4> physDevs(physDevCount);
454 VkResult err = f->vkEnumeratePhysicalDevices(inst->vkInstance(), &physDevCount, physDevs.data());
455 if (err != VK_SUCCESS || !physDevCount) {
456 qWarning("Failed to enumerate physical devices: %d", err);
457 return false;
458 }
459
460 int physDevIndex = -1;
461 int requestedPhysDevIndex = -1;
462 if (qEnvironmentVariableIsSet("QT_VK_PHYSICAL_DEVICE_INDEX"))
463 requestedPhysDevIndex = qEnvironmentVariableIntValue("QT_VK_PHYSICAL_DEVICE_INDEX");
464
465 if (requestedPhysDevIndex < 0 && flags.testFlag(QRhi::PreferSoftwareRenderer)) {
466 for (int i = 0; i < int(physDevCount); ++i) {
467 f->vkGetPhysicalDeviceProperties(physDevs[i], &physDevProperties);
468 if (physDevProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU) {
469 requestedPhysDevIndex = i;
470 break;
471 }
472 }
473 }
474
475 for (int i = 0; i < int(physDevCount); ++i) {
476 f->vkGetPhysicalDeviceProperties(physDevs[i], &physDevProperties);
477 qCDebug(QRHI_LOG_INFO, "Physical device %d: '%s' %d.%d.%d (api %d.%d.%d vendor 0x%X device 0x%X type %d)",
478 i,
479 physDevProperties.deviceName,
480 VK_VERSION_MAJOR(physDevProperties.driverVersion),
481 VK_VERSION_MINOR(physDevProperties.driverVersion),
482 VK_VERSION_PATCH(physDevProperties.driverVersion),
483 VK_VERSION_MAJOR(physDevProperties.apiVersion),
484 VK_VERSION_MINOR(physDevProperties.apiVersion),
485 VK_VERSION_PATCH(physDevProperties.apiVersion),
486 physDevProperties.vendorID,
487 physDevProperties.deviceID,
488 physDevProperties.deviceType);
489 if (physDevIndex < 0 && (requestedPhysDevIndex < 0 || requestedPhysDevIndex == int(i))) {
490 physDevIndex = i;
491 qCDebug(QRHI_LOG_INFO, " using this physical device");
492 }
493 }
494
495 if (physDevIndex < 0) {
496 qWarning("No matching physical device");
497 return false;
498 }
499 physDev = physDevs[physDevIndex];
500 f->vkGetPhysicalDeviceProperties(physDev, &physDevProperties);
501 } else {
502 f->vkGetPhysicalDeviceProperties(physDev, &physDevProperties);
503 qCDebug(QRHI_LOG_INFO, "Using imported physical device '%s' %d.%d.%d (api %d.%d.%d vendor 0x%X device 0x%X type %d)",
504 physDevProperties.deviceName,
505 VK_VERSION_MAJOR(physDevProperties.driverVersion),
506 VK_VERSION_MINOR(physDevProperties.driverVersion),
507 VK_VERSION_PATCH(physDevProperties.driverVersion),
508 VK_VERSION_MAJOR(physDevProperties.apiVersion),
509 VK_VERSION_MINOR(physDevProperties.apiVersion),
510 VK_VERSION_PATCH(physDevProperties.apiVersion),
511 physDevProperties.vendorID,
512 physDevProperties.deviceID,
513 physDevProperties.deviceType);
514 }
515
516 caps.apiVersion = inst->apiVersion();
517
518 // Check the physical device API version against the instance API version,
519 // they do not have to match, which means whatever version was set in the
520 // QVulkanInstance may not be legally used with a given device if the
521 // physical device has a lower version.
522 const QVersionNumber physDevApiVersion(VK_VERSION_MAJOR(physDevProperties.apiVersion),
523 VK_VERSION_MINOR(physDevProperties.apiVersion)); // patch version left out intentionally
524 if (physDevApiVersion < caps.apiVersion) {
525 qCDebug(QRHI_LOG_INFO) << "Instance has api version" << caps.apiVersion
526 << "whereas the chosen physical device has" << physDevApiVersion
527 << "- restricting to the latter";
528 caps.apiVersion = physDevApiVersion;
529 }
530
535
536 bool featuresQueried = false;
537#ifdef VK_VERSION_1_1
538 VkPhysicalDeviceFeatures2 physDevFeaturesChainable = {};
539 physDevFeaturesChainable.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
540#endif
541
542 // Vulkan >=1.2 headers at build time, >=1.2 implementation at run time
543#ifdef VK_VERSION_1_2
544 if (!featuresQueried) {
545 // Vulkan11Features, Vulkan12Features, etc. are only in Vulkan 1.2 and newer.
546 if (caps.apiVersion >= QVersionNumber(1, 2)) {
547 physDevFeatures11IfApi12OrNewer = {};
548 physDevFeatures11IfApi12OrNewer.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES;
549 physDevFeatures12 = {};
550 physDevFeatures12.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;
551#ifdef VK_VERSION_1_3
552 physDevFeatures13 = {};
553 physDevFeatures13.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES;
554#endif
555 physDevFeaturesChainable.pNext = &physDevFeatures11IfApi12OrNewer;
556 physDevFeatures11IfApi12OrNewer.pNext = &physDevFeatures12;
557#ifdef VK_VERSION_1_3
558 if (caps.apiVersion >= QVersionNumber(1, 3))
559 physDevFeatures12.pNext = &physDevFeatures13;
560#endif
561 f->vkGetPhysicalDeviceFeatures2(physDev, &physDevFeaturesChainable);
562 memcpy(&physDevFeatures, &physDevFeaturesChainable.features, sizeof(VkPhysicalDeviceFeatures));
563 featuresQueried = true;
564 }
565 }
566#endif // VK_VERSION_1_2
567
568 // Vulkan >=1.1 headers at build time, 1.1 implementation at run time
569#ifdef VK_VERSION_1_1
570 if (!featuresQueried) {
571 // Vulkan versioning nightmares: if the runtime API version is 1.1,
572 // there is no Vulkan11Features (introduced in 1.2+, the headers might
573 // have the types and structs, but the Vulkan implementation version at
574 // run time is what matters). But there are individual feature structs.
575 // For multiview, it is important to get this right since at the time of
576 // writing Quest 3 Android is a Vulkan 1.1 implementation at run time on
577 // the headset.
578 if (caps.apiVersion == QVersionNumber(1, 1)) {
579 multiviewFeaturesIfApi11 = {};
580 multiviewFeaturesIfApi11.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES;
581 physDevFeaturesChainable.pNext = &multiviewFeaturesIfApi11;
582 f->vkGetPhysicalDeviceFeatures2(physDev, &physDevFeaturesChainable);
583 memcpy(&physDevFeatures, &physDevFeaturesChainable.features, sizeof(VkPhysicalDeviceFeatures));
584 featuresQueried = true;
585 }
586 }
587#endif
588
589 if (!featuresQueried) {
590 // If the API version at run time is 1.0 (or we are building with
591 // ancient 1.0 headers), then do the Vulkan 1.0 query.
592 f->vkGetPhysicalDeviceFeatures(physDev, &physDevFeatures);
593 featuresQueried = true;
594 }
595
596 // Choose queue and create device, unless the device was specified in importParams.
597 if (!importedDevice) {
598 // We only support combined graphics+present queues. When it comes to
599 // compute, only combined graphics+compute queue is used, compute gets
600 // disabled otherwise.
601 std::optional<uint32_t> gfxQueueFamilyIdxOpt;
602 std::optional<uint32_t> computelessGfxQueueCandidateIdxOpt;
603 queryQueueFamilyProps();
604 const uint32_t queueFamilyCount = uint32_t(queueFamilyProps.size());
605 for (uint32_t i = 0; i < queueFamilyCount; ++i) {
606 qCDebug(QRHI_LOG_INFO, "queue family %u: flags=0x%x count=%u",
607 i, queueFamilyProps[i].queueFlags, queueFamilyProps[i].queueCount);
608 if (!gfxQueueFamilyIdxOpt.has_value()
609 && (queueFamilyProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
610 && (!maybeWindow || inst->supportsPresent(physDev, i, maybeWindow)))
611 {
612 if (queueFamilyProps[i].queueFlags & VK_QUEUE_COMPUTE_BIT)
613 gfxQueueFamilyIdxOpt = i;
614 else if (!computelessGfxQueueCandidateIdxOpt.has_value())
615 computelessGfxQueueCandidateIdxOpt = i;
616 }
617 }
618 if (gfxQueueFamilyIdxOpt.has_value()) {
619 gfxQueueFamilyIdx = gfxQueueFamilyIdxOpt.value();
620 } else {
621 if (computelessGfxQueueCandidateIdxOpt.has_value()) {
622 gfxQueueFamilyIdx = computelessGfxQueueCandidateIdxOpt.value();
623 } else {
624 qWarning("No graphics (or no graphics+present) queue family found");
625 return false;
626 }
627 }
628
629 VkDeviceQueueCreateInfo queueInfo = {};
630 const float prio[] = { 0 };
631 queueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
632 queueInfo.queueFamilyIndex = gfxQueueFamilyIdx;
633 queueInfo.queueCount = 1;
634 queueInfo.pQueuePriorities = prio;
635
636 QList<const char *> devLayers;
637 if (inst->layers().contains("VK_LAYER_KHRONOS_validation"))
638 devLayers.append("VK_LAYER_KHRONOS_validation");
639
640 QVulkanInfoVector<QVulkanExtension> devExts;
641 uint32_t devExtCount = 0;
642 f->vkEnumerateDeviceExtensionProperties(physDev, nullptr, &devExtCount, nullptr);
643 if (devExtCount) {
644 QList<VkExtensionProperties> extProps(devExtCount);
645 f->vkEnumerateDeviceExtensionProperties(physDev, nullptr, &devExtCount, extProps.data());
646 for (const VkExtensionProperties &p : std::as_const(extProps))
647 devExts.append({ p.extensionName, p.specVersion });
648 }
649 qCDebug(QRHI_LOG_INFO, "%d device extensions available", int(devExts.size()));
650
651 QList<const char *> requestedDevExts;
652 requestedDevExts.append("VK_KHR_swapchain");
653
654 const bool hasPhysDevProp2 = inst->extensions().contains(QByteArrayLiteral("VK_KHR_get_physical_device_properties2"));
655
656 if (devExts.contains(QByteArrayLiteral("VK_KHR_portability_subset"))) {
657 if (hasPhysDevProp2) {
658 requestedDevExts.append("VK_KHR_portability_subset");
659 } else {
660 qWarning("VK_KHR_portability_subset should be enabled on the device "
661 "but the instance does not have VK_KHR_get_physical_device_properties2 enabled. "
662 "Expect problems.");
663 }
664 }
665
666#ifdef VK_EXT_vertex_attribute_divisor
667 if (devExts.contains(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME)) {
668 if (hasPhysDevProp2) {
669 requestedDevExts.append(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME);
670 caps.vertexAttribDivisor = true;
671 }
672 }
673#endif
674
675#ifdef VK_KHR_create_renderpass2
676 if (devExts.contains(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME)) {
677 requestedDevExts.append(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME);
678 caps.renderPass2KHR = true;
679 }
680#endif
681
682#ifdef VK_KHR_depth_stencil_resolve
683 if (devExts.contains(VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME)) {
684 requestedDevExts.append(VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME);
685 caps.depthStencilResolveKHR = true;
686 }
687#endif
688
689 for (const QByteArray &ext : requestedDeviceExtensions) {
690 if (!ext.isEmpty() && !requestedDevExts.contains(ext)) {
691 if (devExts.contains(ext)) {
692 requestedDevExts.append(ext.constData());
693 } else {
694 qWarning("Device extension %s requested in QRhiVulkanInitParams is not supported",
695 ext.constData());
696 }
697 }
698 }
699
700 QByteArrayList envExtList = qgetenv("QT_VULKAN_DEVICE_EXTENSIONS").split(';');
701 for (const QByteArray &ext : envExtList) {
702 if (!ext.isEmpty() && !requestedDevExts.contains(ext)) {
703 if (devExts.contains(ext)) {
704 requestedDevExts.append(ext.constData());
705 } else {
706 qWarning("Device extension %s requested in QT_VULKAN_DEVICE_EXTENSIONS is not supported",
707 ext.constData());
708 }
709 }
710 }
711
712 if (QRHI_LOG_INFO().isEnabled(QtDebugMsg)) {
713 qCDebug(QRHI_LOG_INFO, "Enabling device extensions:");
714 for (const char *ext : requestedDevExts)
715 qCDebug(QRHI_LOG_INFO, " %s", ext);
716 }
717
718 VkDeviceCreateInfo devInfo = {};
719 devInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
720 devInfo.queueCreateInfoCount = 1;
721 devInfo.pQueueCreateInfos = &queueInfo;
722 devInfo.enabledLayerCount = uint32_t(devLayers.size());
723 devInfo.ppEnabledLayerNames = devLayers.constData();
724 devInfo.enabledExtensionCount = uint32_t(requestedDevExts.size());
725 devInfo.ppEnabledExtensionNames = requestedDevExts.constData();
726
727 // Enable all features that are reported as supported, except
728 // robustness because that potentially affects performance.
729 //
730 // Enabling all features mainly serves third-party renderers that may
731 // use the VkDevice created here. For the record, the backend here
732 // optionally relies on the following features, meaning just for our
733 // (QRhi/Quick/Quick 3D) purposes it would be sufficient to
734 // enable-if-supported only the following:
735 //
736 // wideLines, largePoints, fillModeNonSolid,
737 // tessellationShader, geometryShader
738 // textureCompressionETC2, textureCompressionASTC_LDR, textureCompressionBC
739
740#ifdef VK_VERSION_1_1
741 physDevFeaturesChainable.features.robustBufferAccess = VK_FALSE;
742#endif
743#ifdef VK_VERSION_1_3
744 physDevFeatures13.robustImageAccess = VK_FALSE;
745#endif
746
747#ifdef VK_VERSION_1_1
748 if (caps.apiVersion >= QVersionNumber(1, 1)) {
749 // For a >=1.2 implementation at run time, this will enable all
750 // (1.0-1.3) features reported as supported, except the ones we turn
751 // off explicitly above. For a 1.1 implementation at run time, this
752 // only enables the 1.0 and multiview features reported as
753 // supported. We will not be bothering with the Vulkan 1.1
754 // individual feature struct nonsense.
755 devInfo.pNext = &physDevFeaturesChainable;
756 } else
757#endif
758 {
759 physDevFeatures.robustBufferAccess = VK_FALSE;
760 devInfo.pEnabledFeatures = &physDevFeatures;
761 }
762
763 VkResult err = f->vkCreateDevice(physDev, &devInfo, nullptr, &dev);
764 if (err != VK_SUCCESS) {
765 qWarning("Failed to create device: %d", err);
766 return false;
767 }
768 } else {
769 qCDebug(QRHI_LOG_INFO, "Using imported device %p", dev);
770
771 // Here we have no way to tell if the extensions got enabled or not.
772 // Pretend it's all there and supported. If getProcAddress fails, we'll
773 // handle that gracefully.
774 caps.vertexAttribDivisor = true;
775 caps.renderPass2KHR = true;
776 caps.depthStencilResolveKHR = true;
777 }
778
779 vkGetPhysicalDeviceSurfaceCapabilitiesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR>(
780 inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfaceCapabilitiesKHR"));
781 vkGetPhysicalDeviceSurfaceFormatsKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceFormatsKHR>(
782 inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfaceFormatsKHR"));
783 vkGetPhysicalDeviceSurfacePresentModesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfacePresentModesKHR>(
784 inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfacePresentModesKHR"));
785
786 df = inst->deviceFunctions(dev);
787
788 VkCommandPoolCreateInfo poolInfo = {};
789 poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
790 poolInfo.queueFamilyIndex = gfxQueueFamilyIdx;
791 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
792 VkResult err = df->vkCreateCommandPool(dev, &poolInfo, nullptr, &cmdPool[i]);
793 if (err != VK_SUCCESS) {
794 qWarning("Failed to create command pool: %d", err);
795 return false;
796 }
797 }
798
799 qCDebug(QRHI_LOG_INFO, "Using queue family index %u and queue index %u",
800 gfxQueueFamilyIdx, gfxQueueIdx);
801
802 df->vkGetDeviceQueue(dev, gfxQueueFamilyIdx, gfxQueueIdx, &gfxQueue);
803
804 if (queueFamilyProps.isEmpty())
805 queryQueueFamilyProps();
806
807 caps.compute = (queueFamilyProps[gfxQueueFamilyIdx].queueFlags & VK_QUEUE_COMPUTE_BIT) != 0;
808 timestampValidBits = queueFamilyProps[gfxQueueFamilyIdx].timestampValidBits;
809
810 ubufAlign = physDevProperties.limits.minUniformBufferOffsetAlignment;
811 // helps little with an optimal offset of 1 (on some drivers) when the spec
812 // elsewhere states that the minimum bufferOffset is 4...
813 texbufAlign = qMax<VkDeviceSize>(4, physDevProperties.limits.optimalBufferCopyOffsetAlignment);
814
815 caps.wideLines = physDevFeatures.wideLines;
816
817 caps.texture3DSliceAs2D = caps.apiVersion >= QVersionNumber(1, 1);
818
819 caps.tessellation = physDevFeatures.tessellationShader;
820 caps.geometryShader = physDevFeatures.geometryShader;
821
822 caps.nonFillPolygonMode = physDevFeatures.fillModeNonSolid;
823
824#ifdef VK_VERSION_1_2
825 if (caps.apiVersion >= QVersionNumber(1, 2))
826 caps.multiView = physDevFeatures11IfApi12OrNewer.multiview;
827#endif
828
829#ifdef VK_VERSION_1_1
830 if (caps.apiVersion == QVersionNumber(1, 1))
831 caps.multiView = multiviewFeaturesIfApi11.multiview;
832#endif
833
834 // With Vulkan 1.2 renderpass2 and depth_stencil_resolve are core, but we
835 // have to support the case of 1.1 + extensions, in particular for the Quest
836 // 3 (Android, Vulkan 1.1 at the time of writing). Therefore, always rely on
837 // the KHR extension for now.
838#ifdef VK_KHR_create_renderpass2
839 if (caps.renderPass2KHR) {
840 vkCreateRenderPass2KHR = reinterpret_cast<PFN_vkCreateRenderPass2KHR>(f->vkGetDeviceProcAddr(dev, "vkCreateRenderPass2KHR"));
841 if (!vkCreateRenderPass2KHR) // handle it gracefully, the caps flag may be incorrect when using an imported VkDevice
842 caps.renderPass2KHR = false;
843 }
844#endif
845
846 if (!importedAllocator) {
847 VmaVulkanFunctions funcs = {};
848 funcs.vkGetInstanceProcAddr = wrap_vkGetInstanceProcAddr;
849 funcs.vkGetDeviceProcAddr = wrap_vkGetDeviceProcAddr;
850
851 VmaAllocatorCreateInfo allocatorInfo = {};
852 // A QRhi is supposed to be used from one single thread only. Disable
853 // the allocator's own mutexes. This gives a performance boost.
854 allocatorInfo.flags = VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT;
855 allocatorInfo.physicalDevice = physDev;
856 allocatorInfo.device = dev;
857 allocatorInfo.pVulkanFunctions = &funcs;
858 allocatorInfo.instance = inst->vkInstance();
859 allocatorInfo.vulkanApiVersion = VK_MAKE_VERSION(caps.apiVersion.majorVersion(),
860 caps.apiVersion.minorVersion(),
861 caps.apiVersion.microVersion());
862 VmaAllocator vmaallocator;
863 VkResult err = vmaCreateAllocator(&allocatorInfo, &vmaallocator);
864 if (err != VK_SUCCESS) {
865 qWarning("Failed to create allocator: %d", err);
866 return false;
867 }
868 allocator = vmaallocator;
869 }
870
871 inst->installDebugOutputFilter(qvk_debug_filter);
872
873 VkDescriptorPool pool;
874 VkResult err = createDescriptorPool(&pool);
875 if (err == VK_SUCCESS)
876 descriptorPools.append(pool);
877 else
878 qWarning("Failed to create initial descriptor pool: %d", err);
879
880 VkQueryPoolCreateInfo timestampQueryPoolInfo = {};
881 timestampQueryPoolInfo.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
882 timestampQueryPoolInfo.queryType = VK_QUERY_TYPE_TIMESTAMP;
883 timestampQueryPoolInfo.queryCount = QVK_MAX_ACTIVE_TIMESTAMP_PAIRS * 2;
884 err = df->vkCreateQueryPool(dev, &timestampQueryPoolInfo, nullptr, &timestampQueryPool);
885 if (err != VK_SUCCESS) {
886 qWarning("Failed to create timestamp query pool: %d", err);
887 return false;
888 }
889 timestampQueryPoolMap.resize(QVK_MAX_ACTIVE_TIMESTAMP_PAIRS); // 1 bit per pair
890 timestampQueryPoolMap.fill(false);
891
892#ifdef VK_EXT_debug_utils
893 if (caps.debugUtils) {
894 vkSetDebugUtilsObjectNameEXT = reinterpret_cast<PFN_vkSetDebugUtilsObjectNameEXT>(f->vkGetDeviceProcAddr(dev, "vkSetDebugUtilsObjectNameEXT"));
895 vkCmdBeginDebugUtilsLabelEXT = reinterpret_cast<PFN_vkCmdBeginDebugUtilsLabelEXT>(f->vkGetDeviceProcAddr(dev, "vkCmdBeginDebugUtilsLabelEXT"));
896 vkCmdEndDebugUtilsLabelEXT = reinterpret_cast<PFN_vkCmdEndDebugUtilsLabelEXT>(f->vkGetDeviceProcAddr(dev, "vkCmdEndDebugUtilsLabelEXT"));
897 vkCmdInsertDebugUtilsLabelEXT = reinterpret_cast<PFN_vkCmdInsertDebugUtilsLabelEXT>(f->vkGetDeviceProcAddr(dev, "vkCmdInsertDebugUtilsLabelEXT"));
898 }
899#endif
900
901 deviceLost = false;
902
903 nativeHandlesStruct.physDev = physDev;
904 nativeHandlesStruct.dev = dev;
905 nativeHandlesStruct.gfxQueueFamilyIdx = gfxQueueFamilyIdx;
906 nativeHandlesStruct.gfxQueueIdx = gfxQueueIdx;
907 nativeHandlesStruct.gfxQueue = gfxQueue;
908 nativeHandlesStruct.vmemAllocator = allocator;
909 nativeHandlesStruct.inst = inst;
910
911 return true;
912}
913
915{
916 if (!df)
917 return;
918
919 if (!deviceLost)
920 df->vkDeviceWaitIdle(dev);
921
924
925 if (ofr.cmdFence) {
926 df->vkDestroyFence(dev, ofr.cmdFence, nullptr);
927 ofr.cmdFence = VK_NULL_HANDLE;
928 }
929
930 if (pipelineCache) {
931 df->vkDestroyPipelineCache(dev, pipelineCache, nullptr);
932 pipelineCache = VK_NULL_HANDLE;
933 }
934
936 df->vkDestroyDescriptorPool(dev, pool.pool, nullptr);
937
938 descriptorPools.clear();
939
940 if (timestampQueryPool) {
941 df->vkDestroyQueryPool(dev, timestampQueryPool, nullptr);
942 timestampQueryPool = VK_NULL_HANDLE;
943 }
944
946 vmaDestroyAllocator(toVmaAllocator(allocator));
947 allocator = nullptr;
948 }
949
950 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
951 if (cmdPool[i]) {
952 df->vkDestroyCommandPool(dev, cmdPool[i], nullptr);
953 cmdPool[i] = VK_NULL_HANDLE;
954 }
956 ofr.cbWrapper[i]->cb = VK_NULL_HANDLE;
957 }
958
959 if (!importedDevice && dev) {
960 df->vkDestroyDevice(dev, nullptr);
961 inst->resetDeviceFunctions(dev);
962 dev = VK_NULL_HANDLE;
963 }
964
965 f = nullptr;
966 df = nullptr;
967}
968
969VkResult QRhiVulkan::createDescriptorPool(VkDescriptorPool *pool)
970{
971 VkDescriptorPoolSize descPoolSizes[] = {
972 { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, QVK_UNIFORM_BUFFERS_PER_POOL },
973 { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, QVK_UNIFORM_BUFFERS_PER_POOL },
974 { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, QVK_COMBINED_IMAGE_SAMPLERS_PER_POOL },
975 { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, QVK_STORAGE_BUFFERS_PER_POOL },
976 { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, QVK_STORAGE_IMAGES_PER_POOL }
977 };
978 VkDescriptorPoolCreateInfo descPoolInfo = {};
979 descPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
980 // Do not enable vkFreeDescriptorSets - sets are never freed on their own
981 // (good so no trouble with fragmentation), they just deref their pool
982 // which is then reset at some point (or not).
983 descPoolInfo.flags = 0;
984 descPoolInfo.maxSets = QVK_DESC_SETS_PER_POOL;
985 descPoolInfo.poolSizeCount = sizeof(descPoolSizes) / sizeof(descPoolSizes[0]);
986 descPoolInfo.pPoolSizes = descPoolSizes;
987 return df->vkCreateDescriptorPool(dev, &descPoolInfo, nullptr, pool);
988}
989
990bool QRhiVulkan::allocateDescriptorSet(VkDescriptorSetAllocateInfo *allocInfo, VkDescriptorSet *result, int *resultPoolIndex)
991{
992 auto tryAllocate = [this, allocInfo, result](int poolIndex) {
993 allocInfo->descriptorPool = descriptorPools[poolIndex].pool;
994 VkResult r = df->vkAllocateDescriptorSets(dev, allocInfo, result);
995 if (r == VK_SUCCESS)
996 descriptorPools[poolIndex].refCount += 1;
997 return r;
998 };
999
1000 int lastPoolIdx = descriptorPools.size() - 1;
1001 for (int i = lastPoolIdx; i >= 0; --i) {
1002 if (descriptorPools[i].refCount == 0) {
1003 df->vkResetDescriptorPool(dev, descriptorPools[i].pool, 0);
1004 descriptorPools[i].allocedDescSets = 0;
1005 }
1006 if (descriptorPools[i].allocedDescSets + int(allocInfo->descriptorSetCount) <= QVK_DESC_SETS_PER_POOL) {
1007 VkResult err = tryAllocate(i);
1008 if (err == VK_SUCCESS) {
1009 descriptorPools[i].allocedDescSets += allocInfo->descriptorSetCount;
1010 *resultPoolIndex = i;
1011 return true;
1012 }
1013 }
1014 }
1015
1016 VkDescriptorPool newPool;
1017 VkResult poolErr = createDescriptorPool(&newPool);
1018 if (poolErr == VK_SUCCESS) {
1019 descriptorPools.append(newPool);
1020 lastPoolIdx = descriptorPools.size() - 1;
1021 VkResult err = tryAllocate(lastPoolIdx);
1022 if (err != VK_SUCCESS) {
1023 qWarning("Failed to allocate descriptor set from new pool too, giving up: %d", err);
1024 return false;
1025 }
1026 descriptorPools[lastPoolIdx].allocedDescSets += allocInfo->descriptorSetCount;
1027 *resultPoolIndex = lastPoolIdx;
1028 return true;
1029 } else {
1030 qWarning("Failed to allocate new descriptor pool: %d", poolErr);
1031 return false;
1032 }
1033}
1034
1035static inline VkFormat toVkTextureFormat(QRhiTexture::Format format, QRhiTexture::Flags flags)
1036{
1037 const bool srgb = flags.testFlag(QRhiTexture::sRGB);
1038 switch (format) {
1039 case QRhiTexture::RGBA8:
1040 return srgb ? VK_FORMAT_R8G8B8A8_SRGB : VK_FORMAT_R8G8B8A8_UNORM;
1041 case QRhiTexture::BGRA8:
1042 return srgb ? VK_FORMAT_B8G8R8A8_SRGB : VK_FORMAT_B8G8R8A8_UNORM;
1043 case QRhiTexture::R8:
1044 return srgb ? VK_FORMAT_R8_SRGB : VK_FORMAT_R8_UNORM;
1045 case QRhiTexture::RG8:
1046 return srgb ? VK_FORMAT_R8G8_SRGB : VK_FORMAT_R8G8_UNORM;
1047 case QRhiTexture::R16:
1048 return VK_FORMAT_R16_UNORM;
1049 case QRhiTexture::RG16:
1050 return VK_FORMAT_R16G16_UNORM;
1052 return VK_FORMAT_R8_UNORM;
1053
1055 return VK_FORMAT_R16G16B16A16_SFLOAT;
1057 return VK_FORMAT_R32G32B32A32_SFLOAT;
1058 case QRhiTexture::R16F:
1059 return VK_FORMAT_R16_SFLOAT;
1060 case QRhiTexture::R32F:
1061 return VK_FORMAT_R32_SFLOAT;
1062
1064 // intentionally A2B10G10R10, not A2R10G10B10
1065 return VK_FORMAT_A2B10G10R10_UNORM_PACK32;
1066
1067 case QRhiTexture::D16:
1068 return VK_FORMAT_D16_UNORM;
1069 case QRhiTexture::D24:
1070 return VK_FORMAT_X8_D24_UNORM_PACK32;
1071 case QRhiTexture::D24S8:
1072 return VK_FORMAT_D24_UNORM_S8_UINT;
1073 case QRhiTexture::D32F:
1074 return VK_FORMAT_D32_SFLOAT;
1075
1076 case QRhiTexture::BC1:
1077 return srgb ? VK_FORMAT_BC1_RGB_SRGB_BLOCK : VK_FORMAT_BC1_RGB_UNORM_BLOCK;
1078 case QRhiTexture::BC2:
1079 return srgb ? VK_FORMAT_BC2_SRGB_BLOCK : VK_FORMAT_BC2_UNORM_BLOCK;
1080 case QRhiTexture::BC3:
1081 return srgb ? VK_FORMAT_BC3_SRGB_BLOCK : VK_FORMAT_BC3_UNORM_BLOCK;
1082 case QRhiTexture::BC4:
1083 return VK_FORMAT_BC4_UNORM_BLOCK;
1084 case QRhiTexture::BC5:
1085 return VK_FORMAT_BC5_UNORM_BLOCK;
1086 case QRhiTexture::BC6H:
1087 return VK_FORMAT_BC6H_UFLOAT_BLOCK;
1088 case QRhiTexture::BC7:
1089 return srgb ? VK_FORMAT_BC7_SRGB_BLOCK : VK_FORMAT_BC7_UNORM_BLOCK;
1090
1092 return srgb ? VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK : VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK;
1094 return srgb ? VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK : VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK;
1096 return srgb ? VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK : VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK;
1097
1099 return srgb ? VK_FORMAT_ASTC_4x4_SRGB_BLOCK : VK_FORMAT_ASTC_4x4_UNORM_BLOCK;
1101 return srgb ? VK_FORMAT_ASTC_5x4_SRGB_BLOCK : VK_FORMAT_ASTC_5x4_UNORM_BLOCK;
1103 return srgb ? VK_FORMAT_ASTC_5x5_SRGB_BLOCK : VK_FORMAT_ASTC_5x5_UNORM_BLOCK;
1105 return srgb ? VK_FORMAT_ASTC_6x5_SRGB_BLOCK : VK_FORMAT_ASTC_6x5_UNORM_BLOCK;
1107 return srgb ? VK_FORMAT_ASTC_6x6_SRGB_BLOCK : VK_FORMAT_ASTC_6x6_UNORM_BLOCK;
1109 return srgb ? VK_FORMAT_ASTC_8x5_SRGB_BLOCK : VK_FORMAT_ASTC_8x5_UNORM_BLOCK;
1111 return srgb ? VK_FORMAT_ASTC_8x6_SRGB_BLOCK : VK_FORMAT_ASTC_8x6_UNORM_BLOCK;
1113 return srgb ? VK_FORMAT_ASTC_8x8_SRGB_BLOCK : VK_FORMAT_ASTC_8x8_UNORM_BLOCK;
1115 return srgb ? VK_FORMAT_ASTC_10x5_SRGB_BLOCK : VK_FORMAT_ASTC_10x5_UNORM_BLOCK;
1117 return srgb ? VK_FORMAT_ASTC_10x6_SRGB_BLOCK : VK_FORMAT_ASTC_10x6_UNORM_BLOCK;
1119 return srgb ? VK_FORMAT_ASTC_10x8_SRGB_BLOCK : VK_FORMAT_ASTC_10x8_UNORM_BLOCK;
1121 return srgb ? VK_FORMAT_ASTC_10x10_SRGB_BLOCK : VK_FORMAT_ASTC_10x10_UNORM_BLOCK;
1123 return srgb ? VK_FORMAT_ASTC_12x10_SRGB_BLOCK : VK_FORMAT_ASTC_12x10_UNORM_BLOCK;
1125 return srgb ? VK_FORMAT_ASTC_12x12_SRGB_BLOCK : VK_FORMAT_ASTC_12x12_UNORM_BLOCK;
1126
1127 default:
1128 Q_UNREACHABLE_RETURN(VK_FORMAT_R8G8B8A8_UNORM);
1129 }
1130}
1131
1132static inline QRhiTexture::Format swapchainReadbackTextureFormat(VkFormat format, QRhiTexture::Flags *flags)
1133{
1134 switch (format) {
1135 case VK_FORMAT_R8G8B8A8_UNORM:
1136 return QRhiTexture::RGBA8;
1137 case VK_FORMAT_R8G8B8A8_SRGB:
1138 if (flags)
1139 (*flags) |= QRhiTexture::sRGB;
1140 return QRhiTexture::RGBA8;
1141 case VK_FORMAT_B8G8R8A8_UNORM:
1142 return QRhiTexture::BGRA8;
1143 case VK_FORMAT_B8G8R8A8_SRGB:
1144 if (flags)
1145 (*flags) |= QRhiTexture::sRGB;
1146 return QRhiTexture::BGRA8;
1147 case VK_FORMAT_R16G16B16A16_SFLOAT:
1148 return QRhiTexture::RGBA16F;
1149 case VK_FORMAT_R32G32B32A32_SFLOAT:
1150 return QRhiTexture::RGBA32F;
1151 case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
1152 return QRhiTexture::RGB10A2;
1153 default:
1154 qWarning("VkFormat %d cannot be read back", format);
1155 break;
1156 }
1158}
1159
1161{
1162 switch (format) {
1167 return true;
1168
1169 default:
1170 return false;
1171 }
1172}
1173
1174static constexpr inline VkImageAspectFlags aspectMaskForTextureFormat(QRhiTexture::Format format)
1175{
1176 return isDepthTextureFormat(format) ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT;
1177}
1178
1179// Transient images ("render buffers") backed by lazily allocated memory are
1180// managed manually without going through vk_mem_alloc since it does not offer
1181// any support for such images. This should be ok since in practice there
1182// should be very few of such images.
1183
1184uint32_t QRhiVulkan::chooseTransientImageMemType(VkImage img, uint32_t startIndex)
1185{
1186 VkPhysicalDeviceMemoryProperties physDevMemProps;
1187 f->vkGetPhysicalDeviceMemoryProperties(physDev, &physDevMemProps);
1188
1189 VkMemoryRequirements memReq;
1190 df->vkGetImageMemoryRequirements(dev, img, &memReq);
1191 uint32_t memTypeIndex = uint32_t(-1);
1192
1193 if (memReq.memoryTypeBits) {
1194 // Find a device local + lazily allocated, or at least device local memtype.
1195 const VkMemoryType *memType = physDevMemProps.memoryTypes;
1196 bool foundDevLocal = false;
1197 for (uint32_t i = startIndex; i < physDevMemProps.memoryTypeCount; ++i) {
1198 if (memReq.memoryTypeBits & (1 << i)) {
1199 if (memType[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) {
1200 if (!foundDevLocal) {
1201 foundDevLocal = true;
1202 memTypeIndex = i;
1203 }
1204 if (memType[i].propertyFlags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) {
1205 memTypeIndex = i;
1206 break;
1207 }
1208 }
1209 }
1210 }
1211 }
1212
1213 return memTypeIndex;
1214}
1215
1217 const QSize &pixelSize,
1218 VkImageUsageFlags usage,
1219 VkImageAspectFlags aspectMask,
1220 VkSampleCountFlagBits samples,
1221 VkDeviceMemory *mem,
1222 VkImage *images,
1223 VkImageView *views,
1224 int count)
1225{
1226 VkMemoryRequirements memReq;
1227 VkResult err;
1228
1229 for (int i = 0; i < count; ++i) {
1230 VkImageCreateInfo imgInfo = {};
1231 imgInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
1232 imgInfo.imageType = VK_IMAGE_TYPE_2D;
1233 imgInfo.format = format;
1234 imgInfo.extent.width = uint32_t(pixelSize.width());
1235 imgInfo.extent.height = uint32_t(pixelSize.height());
1236 imgInfo.extent.depth = 1;
1237 imgInfo.mipLevels = imgInfo.arrayLayers = 1;
1238 imgInfo.samples = samples;
1239 imgInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
1240 imgInfo.usage = usage | VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT;
1241 imgInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1242
1243 err = df->vkCreateImage(dev, &imgInfo, nullptr, images + i);
1244 if (err != VK_SUCCESS) {
1245 qWarning("Failed to create image: %d", err);
1246 return false;
1247 }
1248
1249 // Assume the reqs are the same since the images are same in every way.
1250 // Still, call GetImageMemReq for every image, in order to prevent the
1251 // validation layer from complaining.
1252 df->vkGetImageMemoryRequirements(dev, images[i], &memReq);
1253 }
1254
1255 VkMemoryAllocateInfo memInfo = {};
1256 memInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
1257 memInfo.allocationSize = aligned(memReq.size, memReq.alignment) * VkDeviceSize(count);
1258
1259 uint32_t startIndex = 0;
1260 do {
1261 memInfo.memoryTypeIndex = chooseTransientImageMemType(images[0], startIndex);
1262 if (memInfo.memoryTypeIndex == uint32_t(-1)) {
1263 qWarning("No suitable memory type found");
1264 return false;
1265 }
1266 startIndex = memInfo.memoryTypeIndex + 1;
1267 err = df->vkAllocateMemory(dev, &memInfo, nullptr, mem);
1268 if (err != VK_SUCCESS && err != VK_ERROR_OUT_OF_DEVICE_MEMORY) {
1269 qWarning("Failed to allocate image memory: %d", err);
1270 return false;
1271 }
1272 } while (err != VK_SUCCESS);
1273
1274 VkDeviceSize ofs = 0;
1275 for (int i = 0; i < count; ++i) {
1276 err = df->vkBindImageMemory(dev, images[i], *mem, ofs);
1277 if (err != VK_SUCCESS) {
1278 qWarning("Failed to bind image memory: %d", err);
1279 return false;
1280 }
1281 ofs += aligned(memReq.size, memReq.alignment);
1282
1283 VkImageViewCreateInfo imgViewInfo = {};
1284 imgViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
1285 imgViewInfo.image = images[i];
1286 imgViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
1287 imgViewInfo.format = format;
1288 imgViewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
1289 imgViewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
1290 imgViewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
1291 imgViewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
1292 imgViewInfo.subresourceRange.aspectMask = aspectMask;
1293 imgViewInfo.subresourceRange.levelCount = imgViewInfo.subresourceRange.layerCount = 1;
1294
1295 err = df->vkCreateImageView(dev, &imgViewInfo, nullptr, views + i);
1296 if (err != VK_SUCCESS) {
1297 qWarning("Failed to create image view: %d", err);
1298 return false;
1299 }
1300 }
1301
1302 return true;
1303}
1304
1306{
1307 if (optimalDsFormat != VK_FORMAT_UNDEFINED)
1308 return optimalDsFormat;
1309
1310 const VkFormat dsFormatCandidates[] = {
1311 VK_FORMAT_D24_UNORM_S8_UINT,
1312 VK_FORMAT_D32_SFLOAT_S8_UINT,
1313 VK_FORMAT_D16_UNORM_S8_UINT
1314 };
1315 const int dsFormatCandidateCount = sizeof(dsFormatCandidates) / sizeof(VkFormat);
1316 int dsFormatIdx = 0;
1317 while (dsFormatIdx < dsFormatCandidateCount) {
1318 optimalDsFormat = dsFormatCandidates[dsFormatIdx];
1319 VkFormatProperties fmtProp;
1320 f->vkGetPhysicalDeviceFormatProperties(physDev, optimalDsFormat, &fmtProp);
1321 if (fmtProp.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)
1322 break;
1323 ++dsFormatIdx;
1324 }
1325 if (dsFormatIdx == dsFormatCandidateCount)
1326 qWarning("Failed to find an optimal depth-stencil format");
1327
1328 return optimalDsFormat;
1329}
1330
1331static void fillRenderPassCreateInfo(VkRenderPassCreateInfo *rpInfo,
1332 VkSubpassDescription *subpassDesc,
1334{
1335 memset(subpassDesc, 0, sizeof(VkSubpassDescription));
1336 subpassDesc->pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
1337 subpassDesc->colorAttachmentCount = uint32_t(rpD->colorRefs.size());
1338 subpassDesc->pColorAttachments = !rpD->colorRefs.isEmpty() ? rpD->colorRefs.constData() : nullptr;
1339 subpassDesc->pDepthStencilAttachment = rpD->hasDepthStencil ? &rpD->dsRef : nullptr;
1340 subpassDesc->pResolveAttachments = !rpD->resolveRefs.isEmpty() ? rpD->resolveRefs.constData() : nullptr;
1341
1342 memset(rpInfo, 0, sizeof(VkRenderPassCreateInfo));
1343 rpInfo->sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
1344 rpInfo->attachmentCount = uint32_t(rpD->attDescs.size());
1345 rpInfo->pAttachments = rpD->attDescs.constData();
1346 rpInfo->subpassCount = 1;
1347 rpInfo->pSubpasses = subpassDesc;
1348 rpInfo->dependencyCount = uint32_t(rpD->subpassDeps.size());
1349 rpInfo->pDependencies = !rpD->subpassDeps.isEmpty() ? rpD->subpassDeps.constData() : nullptr;
1350}
1351
1352bool QRhiVulkan::createDefaultRenderPass(QVkRenderPassDescriptor *rpD, bool hasDepthStencil, VkSampleCountFlagBits samples, VkFormat colorFormat)
1353{
1354 // attachment list layout is color (1), ds (0-1), resolve (0-1)
1355
1356 VkAttachmentDescription attDesc = {};
1357 attDesc.format = colorFormat;
1358 attDesc.samples = samples;
1359 attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1360 attDesc.storeOp = samples > VK_SAMPLE_COUNT_1_BIT ? VK_ATTACHMENT_STORE_OP_DONT_CARE : VK_ATTACHMENT_STORE_OP_STORE;
1361 attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1362 attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1363 attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1364 attDesc.finalLayout = samples > VK_SAMPLE_COUNT_1_BIT ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
1365 rpD->attDescs.append(attDesc);
1366
1367 rpD->colorRefs.append({ 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL });
1368
1369 rpD->hasDepthStencil = hasDepthStencil;
1370 rpD->hasDepthStencilResolve = false;
1371 rpD->multiViewCount = 0;
1372
1373 if (hasDepthStencil) {
1374 // clear on load + no store + lazy alloc + transient image should play
1375 // nicely with tiled GPUs (no physical backing necessary for ds buffer)
1376 memset(&attDesc, 0, sizeof(attDesc));
1377 attDesc.format = optimalDepthStencilFormat();
1378 attDesc.samples = samples;
1379 attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1380 attDesc.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1381 attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1382 attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1383 attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1384 attDesc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
1385 rpD->attDescs.append(attDesc);
1386
1387 rpD->dsRef = { 1, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
1388 }
1389
1390 if (samples > VK_SAMPLE_COUNT_1_BIT) {
1391 memset(&attDesc, 0, sizeof(attDesc));
1392 attDesc.format = colorFormat;
1393 attDesc.samples = VK_SAMPLE_COUNT_1_BIT;
1394 attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1395 attDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
1396 attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1397 attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1398 attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1399 attDesc.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
1400 rpD->attDescs.append(attDesc);
1401
1402 rpD->resolveRefs.append({ 2, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL });
1403 }
1404
1405 // Replace the first implicit dep (TOP_OF_PIPE / ALL_COMMANDS) with our own.
1406 VkSubpassDependency subpassDep = {};
1407 subpassDep.srcSubpass = VK_SUBPASS_EXTERNAL;
1408 subpassDep.dstSubpass = 0;
1409 subpassDep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
1410 subpassDep.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
1411 subpassDep.srcAccessMask = 0;
1412 subpassDep.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
1413 rpD->subpassDeps.append(subpassDep);
1414 if (hasDepthStencil) {
1415 memset(&subpassDep, 0, sizeof(subpassDep));
1416 subpassDep.srcSubpass = VK_SUBPASS_EXTERNAL;
1417 subpassDep.dstSubpass = 0;
1418 subpassDep.srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT
1419 | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
1420 subpassDep.dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT
1421 | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
1422 subpassDep.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
1423 subpassDep.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT
1424 | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
1425 rpD->subpassDeps.append(subpassDep);
1426 }
1427
1428 VkRenderPassCreateInfo rpInfo;
1429 VkSubpassDescription subpassDesc;
1430 fillRenderPassCreateInfo(&rpInfo, &subpassDesc, rpD);
1431
1432 VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, &rpD->rp);
1433 if (err != VK_SUCCESS) {
1434 qWarning("Failed to create renderpass: %d", err);
1435 return false;
1436 }
1437
1438 return true;
1439}
1440
1442{
1443 bool prepare(VkRenderPassCreateInfo *rpInfo, int multiViewCount, bool multiViewCap)
1444 {
1445 if (multiViewCount < 2)
1446 return true;
1447 if (!multiViewCap) {
1448 qWarning("Cannot create multiview render pass without support for the Vulkan 1.1 multiview feature");
1449 return false;
1450 }
1451#ifdef VK_VERSION_1_1
1452 uint32_t allViewsMask = 0;
1453 for (uint32_t i = 0; i < uint32_t(multiViewCount); ++i)
1454 allViewsMask |= (1 << i);
1455 multiViewMask = allViewsMask;
1456 multiViewCorrelationMask = allViewsMask;
1457 multiViewInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO;
1458 multiViewInfo.subpassCount = 1;
1459 multiViewInfo.pViewMasks = &multiViewMask;
1460 multiViewInfo.correlationMaskCount = 1;
1461 multiViewInfo.pCorrelationMasks = &multiViewCorrelationMask;
1462 rpInfo->pNext = &multiViewInfo;
1463#endif
1464 return true;
1465 }
1466
1467#ifdef VK_VERSION_1_1
1468 VkRenderPassMultiviewCreateInfo multiViewInfo = {};
1469 uint32_t multiViewMask = 0;
1470 uint32_t multiViewCorrelationMask = 0;
1471#endif
1472};
1473
1474#ifdef VK_KHR_create_renderpass2
1475// Effectively converts a VkRenderPassCreateInfo into a VkRenderPassCreateInfo2,
1476// adding depth-stencil resolve support. Assumes a single subpass and no subpass
1477// dependencies.
1478struct RenderPass2SetupHelper
1479{
1480 bool prepare(VkRenderPassCreateInfo2 *rpInfo2, const VkRenderPassCreateInfo *rpInfo, const QVkRenderPassDescriptor *rpD, int multiViewCount) {
1481 *rpInfo2 = {};
1482
1483 viewMask = 0;
1484 if (multiViewCount >= 2) {
1485 for (uint32_t i = 0; i < uint32_t(multiViewCount); ++i)
1486 viewMask |= (1 << i);
1487 }
1488
1489 attDescs2.resize(rpInfo->attachmentCount);
1490 for (qsizetype i = 0; i < attDescs2.count(); ++i) {
1491 VkAttachmentDescription2KHR &att2(attDescs2[i]);
1492 const VkAttachmentDescription &att(rpInfo->pAttachments[i]);
1493 att2 = {};
1494 att2.sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2;
1495 att2.flags = att.flags;
1496 att2.format = att.format;
1497 att2.samples = att.samples;
1498 att2.loadOp = att.loadOp;
1499 att2.storeOp = att.storeOp;
1500 att2.stencilLoadOp = att.stencilLoadOp;
1501 att2.stencilStoreOp = att.stencilStoreOp;
1502 att2.initialLayout = att.initialLayout;
1503 att2.finalLayout = att.finalLayout;
1504 }
1505
1506 attRefs2.clear();
1507 subpass2 = {};
1508 subpass2.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2_KHR;
1509 const VkSubpassDescription &subpassDesc(rpInfo->pSubpasses[0]);
1510 subpass2.flags = subpassDesc.flags;
1511 subpass2.pipelineBindPoint = subpassDesc.pipelineBindPoint;
1512 if (multiViewCount >= 2)
1513 subpass2.viewMask = viewMask;
1514
1515 // color attachment refs
1516 qsizetype startIndex = attRefs2.count();
1517 for (uint32_t j = 0; j < subpassDesc.colorAttachmentCount; ++j) {
1518 attRefs2.append({});
1519 VkAttachmentReference2KHR &attref2(attRefs2.last());
1520 const VkAttachmentReference &attref(subpassDesc.pColorAttachments[j]);
1521 attref2.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR;
1522 attref2.attachment = attref.attachment;
1523 attref2.layout = attref.layout;
1524 attref2.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1525 }
1526 subpass2.colorAttachmentCount = subpassDesc.colorAttachmentCount;
1527 subpass2.pColorAttachments = attRefs2.constData() + startIndex;
1528
1529 // color resolve refs
1530 if (subpassDesc.pResolveAttachments) {
1531 startIndex = attRefs2.count();
1532 for (uint32_t j = 0; j < subpassDesc.colorAttachmentCount; ++j) {
1533 attRefs2.append({});
1534 VkAttachmentReference2KHR &attref2(attRefs2.last());
1535 const VkAttachmentReference &attref(subpassDesc.pResolveAttachments[j]);
1536 attref2.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR;
1537 attref2.attachment = attref.attachment;
1538 attref2.layout = attref.layout;
1539 attref2.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1540 }
1541 subpass2.pResolveAttachments = attRefs2.constData() + startIndex;
1542 }
1543
1544 // depth-stencil ref
1545 if (subpassDesc.pDepthStencilAttachment) {
1546 startIndex = attRefs2.count();
1547 attRefs2.append({});
1548 VkAttachmentReference2KHR &attref2(attRefs2.last());
1549 const VkAttachmentReference &attref(*subpassDesc.pDepthStencilAttachment);
1550 attref2.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR;
1551 attref2.attachment = attref.attachment;
1552 attref2.layout = attref.layout;
1553 attref2.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
1554 subpass2.pDepthStencilAttachment = attRefs2.constData() + startIndex;
1555 }
1556
1557 // depth-stencil resolve ref
1558#ifdef VK_KHR_depth_stencil_resolve
1559 dsResolveDesc = {};
1560 if (rpD->hasDepthStencilResolve) {
1561 startIndex = attRefs2.count();
1562 attRefs2.append({});
1563 VkAttachmentReference2KHR &attref2(attRefs2.last());
1564 attref2.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR;
1565 attref2.attachment = rpD->dsResolveRef.attachment;
1566 attref2.layout = rpD->dsResolveRef.layout;
1567 attref2.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
1568 dsResolveDesc.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE_KHR;
1569 dsResolveDesc.depthResolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;
1570 dsResolveDesc.stencilResolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;
1571 dsResolveDesc.pDepthStencilResolveAttachment = attRefs2.constData() + startIndex;
1572 subpass2.pNext = &dsResolveDesc;
1573 }
1574#endif
1575
1576 rpInfo2->sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2_KHR;
1577 rpInfo2->pNext = nullptr; // the 1.1 VkRenderPassMultiviewCreateInfo is part of the '2' structs
1578 rpInfo2->flags = rpInfo->flags;
1579 rpInfo2->attachmentCount = rpInfo->attachmentCount;
1580 rpInfo2->pAttachments = attDescs2.constData();
1581 rpInfo2->subpassCount = 1;
1582 rpInfo2->pSubpasses = &subpass2;
1583 if (multiViewCount >= 2) {
1584 rpInfo2->correlatedViewMaskCount = 1;
1585 rpInfo2->pCorrelatedViewMasks = &viewMask;
1586 }
1587 return true;
1588 }
1589
1590 QVarLengthArray<VkAttachmentDescription2KHR, 8> attDescs2;
1591 QVarLengthArray<VkAttachmentReference2KHR, 8> attRefs2;
1592 VkSubpassDescription2KHR subpass2;
1593#ifdef VK_KHR_depth_stencil_resolve
1594 VkSubpassDescriptionDepthStencilResolveKHR dsResolveDesc;
1595#endif
1596 uint32_t viewMask;
1597};
1598#endif // VK_KHR_create_renderpass2
1599
1601 const QRhiColorAttachment *colorAttachmentsBegin,
1602 const QRhiColorAttachment *colorAttachmentsEnd,
1603 bool preserveColor,
1604 bool preserveDs,
1605 bool storeDs,
1606 QRhiRenderBuffer *depthStencilBuffer,
1607 QRhiTexture *depthTexture,
1608 QRhiTexture *depthResolveTexture)
1609{
1610 // attachment list layout is color (0-8), ds (0-1), resolve (0-8), ds resolve (0-1)
1611
1612 int multiViewCount = 0;
1613 for (auto it = colorAttachmentsBegin; it != colorAttachmentsEnd; ++it) {
1614 QVkTexture *texD = QRHI_RES(QVkTexture, it->texture());
1615 QVkRenderBuffer *rbD = QRHI_RES(QVkRenderBuffer, it->renderBuffer());
1616 Q_ASSERT(texD || rbD);
1617 const VkFormat vkformat = texD ? texD->viewFormat : rbD->vkformat;
1618 const VkSampleCountFlagBits samples = texD ? texD->samples : rbD->samples;
1619
1620 VkAttachmentDescription attDesc = {};
1621 attDesc.format = vkformat;
1622 attDesc.samples = samples;
1623 attDesc.loadOp = preserveColor ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_CLEAR;
1624 attDesc.storeOp = (it->resolveTexture() && !preserveColor) ? VK_ATTACHMENT_STORE_OP_DONT_CARE : VK_ATTACHMENT_STORE_OP_STORE;
1625 attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1626 attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1627 // this has to interact correctly with activateTextureRenderTarget(), hence leaving in COLOR_ATT
1628 attDesc.initialLayout = preserveColor ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_UNDEFINED;
1629 attDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
1630 rpD->attDescs.append(attDesc);
1631
1632 const VkAttachmentReference ref = { uint32_t(rpD->attDescs.size() - 1), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
1633 rpD->colorRefs.append(ref);
1634
1635 if (it->multiViewCount() >= 2) {
1636 if (multiViewCount > 0 && multiViewCount != it->multiViewCount())
1637 qWarning("Inconsistent multiViewCount in color attachment set");
1638 else
1639 multiViewCount = it->multiViewCount();
1640 } else if (multiViewCount > 0) {
1641 qWarning("Mixing non-multiview color attachments within a multiview render pass");
1642 }
1643 }
1644 Q_ASSERT(multiViewCount == 0 || multiViewCount >= 2);
1645 rpD->multiViewCount = uint32_t(multiViewCount);
1646
1647 rpD->hasDepthStencil = depthStencilBuffer || depthTexture;
1648 if (rpD->hasDepthStencil) {
1649 const VkFormat dsFormat = depthTexture ? QRHI_RES(QVkTexture, depthTexture)->viewFormat
1650 : QRHI_RES(QVkRenderBuffer, depthStencilBuffer)->vkformat;
1651 const VkSampleCountFlagBits samples = depthTexture ? QRHI_RES(QVkTexture, depthTexture)->samples
1652 : QRHI_RES(QVkRenderBuffer, depthStencilBuffer)->samples;
1653 const VkAttachmentLoadOp loadOp = preserveDs ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_CLEAR;
1654 const VkAttachmentStoreOp storeOp = storeDs ? VK_ATTACHMENT_STORE_OP_STORE : VK_ATTACHMENT_STORE_OP_DONT_CARE;
1655 VkAttachmentDescription attDesc = {};
1656 attDesc.format = dsFormat;
1657 attDesc.samples = samples;
1658 attDesc.loadOp = loadOp;
1659 attDesc.storeOp = storeOp;
1660 attDesc.stencilLoadOp = loadOp;
1661 attDesc.stencilStoreOp = storeOp;
1662 attDesc.initialLayout = preserveDs ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_UNDEFINED;
1663 attDesc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
1664 rpD->attDescs.append(attDesc);
1665 if (depthTexture && depthTexture->arraySize() >= 2 && colorAttachmentsBegin == colorAttachmentsEnd) {
1666 multiViewCount = depthTexture->arraySize();
1667 rpD->multiViewCount = multiViewCount;
1668 }
1669 }
1670 rpD->dsRef = { uint32_t(rpD->attDescs.size() - 1), VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
1671
1672 for (auto it = colorAttachmentsBegin; it != colorAttachmentsEnd; ++it) {
1673 if (it->resolveTexture()) {
1674 QVkTexture *rtexD = QRHI_RES(QVkTexture, it->resolveTexture());
1675 const VkFormat dstFormat = rtexD->vkformat;
1676 if (rtexD->samples > VK_SAMPLE_COUNT_1_BIT)
1677 qWarning("Resolving into a multisample texture is not supported");
1678
1679 QVkTexture *texD = QRHI_RES(QVkTexture, it->texture());
1680 QVkRenderBuffer *rbD = QRHI_RES(QVkRenderBuffer, it->renderBuffer());
1681 const VkFormat srcFormat = texD ? texD->vkformat : rbD->vkformat;
1682 if (srcFormat != dstFormat) {
1683 // This is a validation error. But some implementations survive,
1684 // actually. Warn about it however, because it's an error with
1685 // some other backends (like D3D) as well.
1686 qWarning("Multisample resolve between different formats (%d and %d) is not supported.",
1687 int(srcFormat), int(dstFormat));
1688 }
1689
1690 VkAttachmentDescription attDesc = {};
1691 attDesc.format = rtexD->viewFormat;
1692 attDesc.samples = VK_SAMPLE_COUNT_1_BIT;
1693 attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; // ignored
1694 attDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
1695 attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1696 attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1697 attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1698 attDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
1699 rpD->attDescs.append(attDesc);
1700
1701 const VkAttachmentReference ref = { uint32_t(rpD->attDescs.size() - 1), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
1702 rpD->resolveRefs.append(ref);
1703 } else {
1704 const VkAttachmentReference ref = { VK_ATTACHMENT_UNUSED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
1705 rpD->resolveRefs.append(ref);
1706 }
1707 }
1708 Q_ASSERT(rpD->colorRefs.size() == rpD->resolveRefs.size());
1709
1710 rpD->hasDepthStencilResolve = rpD->hasDepthStencil && depthResolveTexture;
1711 if (rpD->hasDepthStencilResolve) {
1712 QVkTexture *rtexD = QRHI_RES(QVkTexture, depthResolveTexture);
1713 if (rtexD->samples > VK_SAMPLE_COUNT_1_BIT)
1714 qWarning("Resolving into a multisample depth texture is not supported");
1715
1716 QVkTexture *texD = QRHI_RES(QVkTexture, depthResolveTexture);
1717 if (texD->vkformat != rtexD->vkformat) {
1718 qWarning("Multisample resolve between different depth-stencil formats (%d and %d) is not supported.",
1719 int(texD->vkformat), int(rtexD->vkformat));
1720 }
1721
1722 VkAttachmentDescription attDesc = {};
1723 attDesc.format = rtexD->viewFormat;
1724 attDesc.samples = VK_SAMPLE_COUNT_1_BIT;
1725 attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; // ignored
1726 attDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
1727 attDesc.stencilLoadOp = attDesc.loadOp;
1728 attDesc.stencilStoreOp = attDesc.storeOp;
1729 attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1730 attDesc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
1731 rpD->attDescs.append(attDesc);
1732 }
1733 rpD->dsResolveRef = { uint32_t(rpD->attDescs.size() - 1), VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
1734
1735 // rpD->subpassDeps stays empty: don't yet know the correct initial/final
1736 // access and stage stuff for the implicit deps at this point, so leave it
1737 // to the resource tracking and activateTextureRenderTarget() to generate
1738 // barriers.
1739
1740 VkRenderPassCreateInfo rpInfo;
1741 VkSubpassDescription subpassDesc;
1742 fillRenderPassCreateInfo(&rpInfo, &subpassDesc, rpD);
1743
1744 MultiViewRenderPassSetupHelper multiViewHelper;
1745 if (!multiViewHelper.prepare(&rpInfo, multiViewCount, caps.multiView))
1746 return false;
1747
1748#ifdef VK_KHR_create_renderpass2
1749 if (rpD->hasDepthStencilResolve && caps.renderPass2KHR) {
1750 // Use the KHR extension, not the 1.2 core API, in order to support Vulkan 1.1.
1751 VkRenderPassCreateInfo2KHR rpInfo2;
1752 RenderPass2SetupHelper rp2Helper;
1753 if (!rp2Helper.prepare(&rpInfo2, &rpInfo, rpD, multiViewCount))
1754 return false;
1755
1756 VkResult err = vkCreateRenderPass2KHR(dev, &rpInfo2, nullptr, &rpD->rp);
1757 if (err != VK_SUCCESS) {
1758 qWarning("Failed to create renderpass (using VkRenderPassCreateInfo2KHR): %d", err);
1759 return false;
1760 }
1761 } else
1762#endif
1763 {
1764 if (rpD->hasDepthStencilResolve) {
1765 qWarning("Resolving multisample depth-stencil buffers is not supported without "
1766 "VK_KHR_depth_stencil_resolve and VK_KHR_create_renderpass2");
1767 }
1768 VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, &rpD->rp);
1769 if (err != VK_SUCCESS) {
1770 qWarning("Failed to create renderpass: %d", err);
1771 return false;
1772 }
1773 }
1774
1775 return true;
1776}
1777
1779{
1780 QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain);
1781 if (swapChainD->pixelSize.isEmpty()) {
1782 qWarning("Surface size is 0, cannot create swapchain");
1783 return false;
1784 }
1785
1786 df->vkDeviceWaitIdle(dev);
1787
1788 if (!vkCreateSwapchainKHR) {
1789 vkCreateSwapchainKHR = reinterpret_cast<PFN_vkCreateSwapchainKHR>(f->vkGetDeviceProcAddr(dev, "vkCreateSwapchainKHR"));
1790 vkDestroySwapchainKHR = reinterpret_cast<PFN_vkDestroySwapchainKHR>(f->vkGetDeviceProcAddr(dev, "vkDestroySwapchainKHR"));
1791 vkGetSwapchainImagesKHR = reinterpret_cast<PFN_vkGetSwapchainImagesKHR>(f->vkGetDeviceProcAddr(dev, "vkGetSwapchainImagesKHR"));
1792 vkAcquireNextImageKHR = reinterpret_cast<PFN_vkAcquireNextImageKHR>(f->vkGetDeviceProcAddr(dev, "vkAcquireNextImageKHR"));
1793 vkQueuePresentKHR = reinterpret_cast<PFN_vkQueuePresentKHR>(f->vkGetDeviceProcAddr(dev, "vkQueuePresentKHR"));
1795 qWarning("Swapchain functions not available");
1796 return false;
1797 }
1798 }
1799
1800 VkSurfaceCapabilitiesKHR surfaceCaps;
1801 vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physDev, swapChainD->surface, &surfaceCaps);
1802 quint32 reqBufferCount;
1803 if (swapChainD->m_flags.testFlag(QRhiSwapChain::MinimalBufferCount) || surfaceCaps.maxImageCount == 0) {
1804 reqBufferCount = qMax<quint32>(2, surfaceCaps.minImageCount);
1805 } else {
1806 reqBufferCount = qMax(qMin<quint32>(surfaceCaps.maxImageCount, 3), surfaceCaps.minImageCount);
1807 }
1808 VkSurfaceTransformFlagBitsKHR preTransform =
1809 (surfaceCaps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)
1810 ? VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR
1811 : surfaceCaps.currentTransform;
1812
1813 // This looks odd but matches how platforms work in practice.
1814 //
1815 // On Windows with NVIDIA for example, the only supportedCompositeAlpha
1816 // value reported is OPAQUE, nothing else. Yet transparency works
1817 // regardless, as long as the native window is set up correctly, so that's
1818 // not something we need to handle here.
1819 //
1820 // On Linux with Intel and Mesa and running on xcb reports, on one
1821 // particular system, INHERIT+PRE_MULTIPLIED. Tranparency works, regardless,
1822 // presumably due to setting INHERIT.
1823 //
1824 // On the same setup with Wayland instead of xcb we see
1825 // OPAQUE+PRE_MULTIPLIED reported. Here transparency won't work unless
1826 // PRE_MULTIPLIED is set.
1827 //
1828 // Therefore our rules are:
1829 // - Prefer INHERIT over OPAQUE.
1830 // - Then based on the request, try the requested alpha mode, but if
1831 // that's not reported as supported, try also the other (PRE/POST,
1832 // POST/PRE) as that is better than nothing. This is not different from
1833 // some other backends, e.g. D3D11 with DirectComposition there is also
1834 // no control over being straight or pre-multiplied. Whereas with
1835 // WGL/GLX/EGL we never had that sort of control.
1836
1837 VkCompositeAlphaFlagBitsKHR compositeAlpha =
1838 (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR)
1839 ? VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR
1840 : VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
1841
1842 if (swapChainD->m_flags.testFlag(QRhiSwapChain::SurfaceHasPreMulAlpha)) {
1843 if (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR)
1844 compositeAlpha = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;
1845 else if (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR)
1846 compositeAlpha = VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR;
1847 } else if (swapChainD->m_flags.testFlag(QRhiSwapChain::SurfaceHasNonPreMulAlpha)) {
1848 if (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR)
1849 compositeAlpha = VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR;
1850 else if (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR)
1851 compositeAlpha = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;
1852 }
1853
1854 VkImageUsageFlags usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
1855 swapChainD->supportsReadback = (surfaceCaps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
1856 if (swapChainD->supportsReadback && swapChainD->m_flags.testFlag(QRhiSwapChain::UsedAsTransferSource))
1857 usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
1858
1859 const bool stereo = bool(swapChainD->m_window) && (swapChainD->m_window->format().stereo())
1860 && surfaceCaps.maxImageArrayLayers > 1;
1861 swapChainD->stereo = stereo;
1862
1863 VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR;
1864 if (swapChainD->m_flags.testFlag(QRhiSwapChain::NoVSync)) {
1865 // Stereo has a weird bug, when using VK_PRESENT_MODE_MAILBOX_KHR,
1866 // black screen is shown, but there is no validation error.
1867 // Detected on Windows, with NVidia RTX A series (at least 4000 and 6000) driver 535.98
1868 if (swapChainD->supportedPresentationModes.contains(VK_PRESENT_MODE_MAILBOX_KHR) && !stereo)
1869 presentMode = VK_PRESENT_MODE_MAILBOX_KHR;
1870 else if (swapChainD->supportedPresentationModes.contains(VK_PRESENT_MODE_IMMEDIATE_KHR))
1871 presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
1872 }
1873
1874 // If the surface is different than before, then passing in the old
1875 // swapchain associated with the old surface can fail the swapchain
1876 // creation. (for example, Android loses the surface when backgrounding and
1877 // restoring applications, and it also enforces failing swapchain creation
1878 // with VK_ERROR_NATIVE_WINDOW_IN_USE_KHR if the old swapchain is provided)
1879 const bool reuseExisting = swapChainD->sc && swapChainD->lastConnectedSurface == swapChainD->surface;
1880
1881 qCDebug(QRHI_LOG_INFO, "Creating %s swapchain of %u buffers, size %dx%d, presentation mode %d",
1882 reuseExisting ? "recycled" : "new",
1883 reqBufferCount, swapChainD->pixelSize.width(), swapChainD->pixelSize.height(), presentMode);
1884
1885 VkSwapchainCreateInfoKHR swapChainInfo = {};
1886 swapChainInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
1887 swapChainInfo.surface = swapChainD->surface;
1888 swapChainInfo.minImageCount = reqBufferCount;
1889 swapChainInfo.imageFormat = swapChainD->colorFormat;
1890 swapChainInfo.imageColorSpace = swapChainD->colorSpace;
1891 swapChainInfo.imageExtent = VkExtent2D { uint32_t(swapChainD->pixelSize.width()), uint32_t(swapChainD->pixelSize.height()) };
1892 swapChainInfo.imageArrayLayers = stereo ? 2u : 1u;
1893 swapChainInfo.imageUsage = usage;
1894 swapChainInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
1895 swapChainInfo.preTransform = preTransform;
1896 swapChainInfo.compositeAlpha = compositeAlpha;
1897 swapChainInfo.presentMode = presentMode;
1898 swapChainInfo.clipped = true;
1899 swapChainInfo.oldSwapchain = reuseExisting ? swapChainD->sc : VK_NULL_HANDLE;
1900
1901 VkSwapchainKHR newSwapChain;
1902 VkResult err = vkCreateSwapchainKHR(dev, &swapChainInfo, nullptr, &newSwapChain);
1903 if (err != VK_SUCCESS) {
1904 qWarning("Failed to create swapchain: %d", err);
1905 return false;
1906 }
1907
1908 if (swapChainD->sc)
1909 releaseSwapChainResources(swapChain);
1910
1911 swapChainD->sc = newSwapChain;
1912 swapChainD->lastConnectedSurface = swapChainD->surface;
1913
1914 quint32 actualSwapChainBufferCount = 0;
1915 err = vkGetSwapchainImagesKHR(dev, swapChainD->sc, &actualSwapChainBufferCount, nullptr);
1916 if (err != VK_SUCCESS || actualSwapChainBufferCount == 0) {
1917 qWarning("Failed to get swapchain images: %d", err);
1918 return false;
1919 }
1920
1921 if (actualSwapChainBufferCount != reqBufferCount)
1922 qCDebug(QRHI_LOG_INFO, "Actual swapchain buffer count is %u", actualSwapChainBufferCount);
1923 swapChainD->bufferCount = int(actualSwapChainBufferCount);
1924
1925 QVarLengthArray<VkImage, QVkSwapChain::EXPECTED_MAX_BUFFER_COUNT> swapChainImages(actualSwapChainBufferCount);
1926 err = vkGetSwapchainImagesKHR(dev, swapChainD->sc, &actualSwapChainBufferCount, swapChainImages.data());
1927 if (err != VK_SUCCESS) {
1928 qWarning("Failed to get swapchain images: %d", err);
1929 return false;
1930 }
1931
1932 QVarLengthArray<VkImage, QVkSwapChain::EXPECTED_MAX_BUFFER_COUNT> msaaImages(swapChainD->bufferCount);
1933 QVarLengthArray<VkImageView, QVkSwapChain::EXPECTED_MAX_BUFFER_COUNT> msaaViews(swapChainD->bufferCount);
1934 if (swapChainD->samples > VK_SAMPLE_COUNT_1_BIT) {
1935 if (!createTransientImage(swapChainD->colorFormat,
1936 swapChainD->pixelSize,
1937 VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
1938 VK_IMAGE_ASPECT_COLOR_BIT,
1939 swapChainD->samples,
1940 &swapChainD->msaaImageMem,
1941 msaaImages.data(),
1942 msaaViews.data(),
1943 swapChainD->bufferCount))
1944 {
1945 qWarning("Failed to create transient image for MSAA color buffer");
1946 return false;
1947 }
1948 }
1949
1950 VkFenceCreateInfo fenceInfo = {};
1951 fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
1952 fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
1953
1954 // Double up for stereo
1955 swapChainD->imageRes.resize(swapChainD->bufferCount * (stereo ? 2u : 1u));
1956
1957 for (int i = 0; i < swapChainD->bufferCount; ++i) {
1958 QVkSwapChain::ImageResources &image(swapChainD->imageRes[i]);
1959 image.image = swapChainImages[i];
1960 if (swapChainD->samples > VK_SAMPLE_COUNT_1_BIT) {
1961 image.msaaImage = msaaImages[i];
1962 image.msaaImageView = msaaViews[i];
1963 }
1964
1965 VkImageViewCreateInfo imgViewInfo = {};
1966 imgViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
1967 imgViewInfo.image = swapChainImages[i];
1968 imgViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
1969 imgViewInfo.format = swapChainD->colorFormat;
1970 imgViewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
1971 imgViewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
1972 imgViewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
1973 imgViewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
1974 imgViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1975 imgViewInfo.subresourceRange.levelCount = imgViewInfo.subresourceRange.layerCount = 1;
1976 err = df->vkCreateImageView(dev, &imgViewInfo, nullptr, &image.imageView);
1977 if (err != VK_SUCCESS) {
1978 qWarning("Failed to create swapchain image view %d: %d", i, err);
1979 return false;
1980 }
1981
1983 }
1984 if (stereo) {
1985 for (int i = 0; i < swapChainD->bufferCount; ++i) {
1986 QVkSwapChain::ImageResources &image(swapChainD->imageRes[i + swapChainD->bufferCount]);
1987 image.image = swapChainImages[i];
1988 if (swapChainD->samples > VK_SAMPLE_COUNT_1_BIT) {
1989 image.msaaImage = msaaImages[i];
1990 image.msaaImageView = msaaViews[i];
1991 }
1992
1993 VkImageViewCreateInfo imgViewInfo = {};
1994 imgViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
1995 imgViewInfo.image = swapChainImages[i];
1996 imgViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
1997 imgViewInfo.format = swapChainD->colorFormat;
1998 imgViewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
1999 imgViewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
2000 imgViewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
2001 imgViewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
2002 imgViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
2003 imgViewInfo.subresourceRange.baseArrayLayer = 1;
2004 imgViewInfo.subresourceRange.levelCount = imgViewInfo.subresourceRange.layerCount = 1;
2005 err = df->vkCreateImageView(dev, &imgViewInfo, nullptr, &image.imageView);
2006 if (err != VK_SUCCESS) {
2007 qWarning("Failed to create swapchain image view %d: %d", i, err);
2008 return false;
2009 }
2010
2012 }
2013 }
2014
2015 swapChainD->currentImageIndex = 0;
2016
2017 VkSemaphoreCreateInfo semInfo = {};
2018 semInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
2019
2020 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
2021 QVkSwapChain::FrameResources &frame(swapChainD->frameRes[i]);
2022
2023 frame.imageAcquired = false;
2024 frame.imageSemWaitable = false;
2025
2026 df->vkCreateFence(dev, &fenceInfo, nullptr, &frame.imageFence);
2027 frame.imageFenceWaitable = true; // fence was created in signaled state
2028
2029 df->vkCreateSemaphore(dev, &semInfo, nullptr, &frame.imageSem);
2030 df->vkCreateSemaphore(dev, &semInfo, nullptr, &frame.drawSem);
2031
2032 err = df->vkCreateFence(dev, &fenceInfo, nullptr, &frame.cmdFence);
2033 if (err != VK_SUCCESS) {
2034 qWarning("Failed to create command buffer fence: %d", err);
2035 return false;
2036 }
2037 frame.cmdFenceWaitable = true; // fence was created in signaled state
2038 }
2039
2040 swapChainD->currentFrameSlot = 0;
2041
2042 return true;
2043}
2044
2046{
2047 QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain);
2048
2049 if (swapChainD->sc == VK_NULL_HANDLE)
2050 return;
2051
2052 if (!deviceLost)
2053 df->vkDeviceWaitIdle(dev);
2054
2055 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
2056 QVkSwapChain::FrameResources &frame(swapChainD->frameRes[i]);
2057 if (frame.cmdFence) {
2058 if (frame.cmdFenceWaitable)
2059 df->vkWaitForFences(dev, 1, &frame.cmdFence, VK_TRUE, UINT64_MAX);
2060 df->vkDestroyFence(dev, frame.cmdFence, nullptr);
2061 frame.cmdFence = VK_NULL_HANDLE;
2062 frame.cmdFenceWaitable = false;
2063 }
2064 if (frame.imageFence) {
2065 if (frame.imageFenceWaitable)
2066 df->vkWaitForFences(dev, 1, &frame.imageFence, VK_TRUE, UINT64_MAX);
2067 df->vkDestroyFence(dev, frame.imageFence, nullptr);
2068 frame.imageFence = VK_NULL_HANDLE;
2069 frame.imageFenceWaitable = false;
2070 }
2071 if (frame.imageSem) {
2072 df->vkDestroySemaphore(dev, frame.imageSem, nullptr);
2073 frame.imageSem = VK_NULL_HANDLE;
2074 }
2075 if (frame.drawSem) {
2076 df->vkDestroySemaphore(dev, frame.drawSem, nullptr);
2077 frame.drawSem = VK_NULL_HANDLE;
2078 }
2079 }
2080
2081 for (int i = 0; i < swapChainD->bufferCount * (swapChainD->stereo ? 2 : 1); ++i) {
2082 QVkSwapChain::ImageResources &image(swapChainD->imageRes[i]);
2083 if (image.fb) {
2084 df->vkDestroyFramebuffer(dev, image.fb, nullptr);
2085 image.fb = VK_NULL_HANDLE;
2086 }
2087 if (image.imageView) {
2088 df->vkDestroyImageView(dev, image.imageView, nullptr);
2089 image.imageView = VK_NULL_HANDLE;
2090 }
2091 if (image.msaaImageView) {
2092 df->vkDestroyImageView(dev, image.msaaImageView, nullptr);
2093 image.msaaImageView = VK_NULL_HANDLE;
2094 }
2095 if (image.msaaImage) {
2096 df->vkDestroyImage(dev, image.msaaImage, nullptr);
2097 image.msaaImage = VK_NULL_HANDLE;
2098 }
2099 }
2100
2101 if (swapChainD->msaaImageMem) {
2102 df->vkFreeMemory(dev, swapChainD->msaaImageMem, nullptr);
2103 swapChainD->msaaImageMem = VK_NULL_HANDLE;
2104 }
2105
2106 vkDestroySwapchainKHR(dev, swapChainD->sc, nullptr);
2107 swapChainD->sc = VK_NULL_HANDLE;
2108
2109 // NB! surface and similar must remain intact
2110}
2111
2113{
2114 VkCommandPoolResetFlags flags = 0;
2115
2116 // While not clear what "recycles all of the resources from the command
2117 // pool back to the system" really means in practice, set it when there was
2118 // a call to releaseCachedResources() recently.
2120 flags |= VK_COMMAND_POOL_RESET_RELEASE_RESOURCES_BIT;
2121
2122 // put all command buffers allocated from this slot's pool to initial state
2123 df->vkResetCommandPool(dev, cmdPool[currentFrameSlot], flags);
2124}
2125
2127{
2128 quint64 mask = 0;
2129 for (quint64 i = 0; i < timestampValidBits; i += 8)
2130 mask |= 0xFFULL << i;
2131 const quint64 ts0 = timestamp[0] & mask;
2132 const quint64 ts1 = timestamp[1] & mask;
2133 const float nsecsPerTick = physDevProperties.limits.timestampPeriod;
2134 if (!qFuzzyIsNull(nsecsPerTick)) {
2135 const float elapsedMs = float(ts1 - ts0) * nsecsPerTick / 1000000.0f;
2136 const double elapsedSec = elapsedMs / 1000.0;
2137 *ok = true;
2138 return elapsedSec;
2139 }
2140 *ok = false;
2141 return 0;
2142}
2143
2145{
2146 QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain);
2147 const int frameResIndex = swapChainD->bufferCount > 1 ? swapChainD->currentFrameSlot : 0;
2148 QVkSwapChain::FrameResources &frame(swapChainD->frameRes[frameResIndex]);
2149
2150 inst->handle()->beginFrame(swapChainD->window);
2151
2152 if (!frame.imageAcquired) {
2153 // Wait if we are too far ahead, i.e. the thread gets throttled based on the presentation rate
2154 // (note that we are using FIFO mode -> vsync)
2155 if (frame.imageFenceWaitable) {
2156 df->vkWaitForFences(dev, 1, &frame.imageFence, VK_TRUE, UINT64_MAX);
2157 df->vkResetFences(dev, 1, &frame.imageFence);
2158 frame.imageFenceWaitable = false;
2159 }
2160
2161 // move on to next swapchain image
2162 uint32_t imageIndex = 0;
2163 VkResult err = vkAcquireNextImageKHR(dev, swapChainD->sc, UINT64_MAX,
2164 frame.imageSem, frame.imageFence, &imageIndex);
2165 if (err == VK_SUCCESS || err == VK_SUBOPTIMAL_KHR) {
2166 swapChainD->currentImageIndex = imageIndex;
2167 frame.imageSemWaitable = true;
2168 frame.imageAcquired = true;
2169 frame.imageFenceWaitable = true;
2170 } else if (err == VK_ERROR_OUT_OF_DATE_KHR) {
2172 } else {
2173 if (err == VK_ERROR_DEVICE_LOST) {
2174 qWarning("Device loss detected in vkAcquireNextImageKHR()");
2175 deviceLost = true;
2177 }
2178 qWarning("Failed to acquire next swapchain image: %d", err);
2179 return QRhi::FrameOpError;
2180 }
2181 }
2182
2183 // Make sure the previous commands for the same image have finished. (note
2184 // that this is based on the fence from the command buffer submit, nothing
2185 // to do with the Present)
2186 //
2187 // Do this also for any other swapchain's commands with the same frame slot
2188 // While this reduces concurrency, it keeps resource usage safe: swapchain
2189 // A starting its frame 0, followed by swapchain B starting its own frame 0
2190 // will make B wait for A's frame 0 commands, so if a resource is written
2191 // in B's frame or when B checks for pending resource releases, that won't
2192 // mess up A's in-flight commands (as they are not in flight anymore).
2193 waitCommandCompletion(frameResIndex);
2194
2195 currentFrameSlot = int(swapChainD->currentFrameSlot);
2196 currentSwapChain = swapChainD;
2197 if (swapChainD->ds)
2199
2200 // reset the command pool
2202
2203 // start recording to this frame's command buffer
2205 if (cbres != QRhi::FrameOpSuccess)
2206 return cbres;
2207
2208 swapChainD->cbWrapper.cb = frame.cmdBuf;
2209
2210 QVkSwapChain::ImageResources &image(swapChainD->imageRes[swapChainD->currentImageIndex]);
2211 swapChainD->rtWrapper.d.fb = image.fb;
2212
2213 if (swapChainD->stereo) {
2215 swapChainD->imageRes[swapChainD->currentImageIndex + swapChainD->bufferCount]);
2216 swapChainD->rtWrapperRight.d.fb = image.fb;
2217 }
2218
2219 prepareNewFrame(&swapChainD->cbWrapper);
2220
2221 // Read the timestamps for the previous frame for this slot.
2222 if (frame.timestampQueryIndex >= 0) {
2223 quint64 timestamp[2] = { 0, 0 };
2224 VkResult err = df->vkGetQueryPoolResults(dev, timestampQueryPool, uint32_t(frame.timestampQueryIndex), 2,
2225 2 * sizeof(quint64), timestamp, sizeof(quint64),
2226 VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT);
2227 timestampQueryPoolMap.clearBit(frame.timestampQueryIndex / 2);
2228 frame.timestampQueryIndex = -1;
2229 if (err == VK_SUCCESS) {
2230 bool ok = false;
2231 const double elapsedSec = elapsedSecondsFromTimestamp(timestamp, &ok);
2232 if (ok)
2233 swapChainD->cbWrapper.lastGpuTime = elapsedSec;
2234 } else {
2235 qWarning("Failed to query timestamp: %d", err);
2236 }
2237 }
2238
2239 // No timestamps if the client did not opt in, or when not having at least 2 frames in flight.
2240 if (rhiFlags.testFlag(QRhi::EnableTimestamps) && swapChainD->bufferCount > 1) {
2241 int timestampQueryIdx = -1;
2242 for (int i = 0; i < timestampQueryPoolMap.size(); ++i) {
2245 timestampQueryIdx = i * 2;
2246 break;
2247 }
2248 }
2249 if (timestampQueryIdx >= 0) {
2250 df->vkCmdResetQueryPool(frame.cmdBuf, timestampQueryPool, uint32_t(timestampQueryIdx), 2);
2251 // record timestamp at the start of the command buffer
2252 df->vkCmdWriteTimestamp(frame.cmdBuf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
2253 timestampQueryPool, uint32_t(timestampQueryIdx));
2254 frame.timestampQueryIndex = timestampQueryIdx;
2255 }
2256 }
2257
2258 return QRhi::FrameOpSuccess;
2259}
2260
2262{
2263 QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain);
2264 Q_ASSERT(currentSwapChain == swapChainD);
2265
2266 auto cleanup = qScopeGuard([this, swapChainD] {
2267 inst->handle()->endFrame(swapChainD->window);
2268 });
2269
2270 recordPrimaryCommandBuffer(&swapChainD->cbWrapper);
2271
2272 int frameResIndex = swapChainD->bufferCount > 1 ? swapChainD->currentFrameSlot : 0;
2273 QVkSwapChain::FrameResources &frame(swapChainD->frameRes[frameResIndex]);
2274 QVkSwapChain::ImageResources &image(swapChainD->imageRes[swapChainD->currentImageIndex]);
2275
2277 VkImageMemoryBarrier presTrans = {};
2278 presTrans.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
2279 presTrans.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
2280 presTrans.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
2281 presTrans.image = image.image;
2282 presTrans.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
2283 presTrans.subresourceRange.levelCount = presTrans.subresourceRange.layerCount = 1;
2284
2286 // was not used at all (no render pass), just transition from undefined to presentable
2287 presTrans.srcAccessMask = 0;
2288 presTrans.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
2289 df->vkCmdPipelineBarrier(frame.cmdBuf,
2290 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
2291 0, 0, nullptr, 0, nullptr,
2292 1, &presTrans);
2294 // was used in a readback as transfer source, go back to presentable layout
2295 presTrans.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
2296 presTrans.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
2297 df->vkCmdPipelineBarrier(frame.cmdBuf,
2298 VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
2299 0, 0, nullptr, 0, nullptr,
2300 1, &presTrans);
2301 }
2303 }
2304
2305 // record another timestamp, when enabled
2306 if (frame.timestampQueryIndex >= 0) {
2307 df->vkCmdWriteTimestamp(frame.cmdBuf, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
2308 timestampQueryPool, uint32_t(frame.timestampQueryIndex + 1));
2309 }
2310
2311 // stop recording and submit to the queue
2312 Q_ASSERT(!frame.cmdFenceWaitable);
2313 const bool needsPresent = !flags.testFlag(QRhi::SkipPresent);
2315 frame.cmdFence,
2316 frame.imageSemWaitable ? &frame.imageSem : nullptr,
2317 needsPresent ? &frame.drawSem : nullptr);
2318 if (submitres != QRhi::FrameOpSuccess)
2319 return submitres;
2320
2321 frame.imageSemWaitable = false;
2322 frame.cmdFenceWaitable = true;
2323
2324 if (needsPresent) {
2325 // add the Present to the queue
2326 VkPresentInfoKHR presInfo = {};
2327 presInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
2328 presInfo.swapchainCount = 1;
2329 presInfo.pSwapchains = &swapChainD->sc;
2330 presInfo.pImageIndices = &swapChainD->currentImageIndex;
2331 presInfo.waitSemaphoreCount = 1;
2332 presInfo.pWaitSemaphores = &frame.drawSem; // gfxQueueFamilyIdx == presQueueFamilyIdx ? &frame.drawSem : &frame.presTransSem;
2333
2334 // Do platform-specific WM notification. F.ex. essential on Wayland in
2335 // order to circumvent driver frame callbacks
2336 inst->presentAboutToBeQueued(swapChainD->window);
2337
2338 VkResult err = vkQueuePresentKHR(gfxQueue, &presInfo);
2339 if (err != VK_SUCCESS) {
2340 if (err == VK_ERROR_OUT_OF_DATE_KHR) {
2342 } else if (err != VK_SUBOPTIMAL_KHR) {
2343 if (err == VK_ERROR_DEVICE_LOST) {
2344 qWarning("Device loss detected in vkQueuePresentKHR()");
2345 deviceLost = true;
2347 }
2348 qWarning("Failed to present: %d", err);
2349 return QRhi::FrameOpError;
2350 }
2351 }
2352
2353 // Do platform-specific WM notification. F.ex. essential on X11 in
2354 // order to prevent glitches on resizing the window.
2355 inst->presentQueued(swapChainD->window);
2356
2357 // mark the current swapchain buffer as unused from our side
2358 frame.imageAcquired = false;
2359 // and move on to the next buffer
2360 swapChainD->currentFrameSlot = (swapChainD->currentFrameSlot + 1) % QVK_FRAMES_IN_FLIGHT;
2361 }
2362
2363 swapChainD->frameCount += 1;
2364 currentSwapChain = nullptr;
2365 return QRhi::FrameOpSuccess;
2366}
2367
2369{
2370 // Now is the time to do things for frame N-F, where N is the current one,
2371 // F is QVK_FRAMES_IN_FLIGHT, because only here it is guaranteed that that
2372 // frame has completed on the GPU (due to the fence wait in beginFrame). To
2373 // decide if something is safe to handle now a simple "lastActiveFrameSlot
2374 // == currentFrameSlot" is sufficient (remember that e.g. with F==2
2375 // currentFrameSlot goes 0, 1, 0, 1, 0, ...)
2376 //
2377 // With multiple swapchains on the same QRhi things get more convoluted
2378 // (and currentFrameSlot strictly alternating is not true anymore) but
2379 // begin(Offscreen)Frame() blocks anyway waiting for its current frame
2380 // slot's previous commands to complete so this here is safe regardless.
2381
2383
2384 QRHI_RES(QVkCommandBuffer, cb)->resetState();
2385
2386 finishActiveReadbacks(); // last, in case the readback-completed callback issues rhi calls
2387
2389}
2390
2392{
2393 if (!*cb) {
2394 VkCommandBufferAllocateInfo cmdBufInfo = {};
2395 cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
2396 cmdBufInfo.commandPool = cmdPool[currentFrameSlot];
2397 cmdBufInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
2398 cmdBufInfo.commandBufferCount = 1;
2399
2400 VkResult err = df->vkAllocateCommandBuffers(dev, &cmdBufInfo, cb);
2401 if (err != VK_SUCCESS) {
2402 if (err == VK_ERROR_DEVICE_LOST) {
2403 qWarning("Device loss detected in vkAllocateCommandBuffers()");
2404 deviceLost = true;
2406 }
2407 qWarning("Failed to allocate frame command buffer: %d", err);
2408 return QRhi::FrameOpError;
2409 }
2410 }
2411
2412 VkCommandBufferBeginInfo cmdBufBeginInfo = {};
2413 cmdBufBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
2414
2415 VkResult err = df->vkBeginCommandBuffer(*cb, &cmdBufBeginInfo);
2416 if (err != VK_SUCCESS) {
2417 if (err == VK_ERROR_DEVICE_LOST) {
2418 qWarning("Device loss detected in vkBeginCommandBuffer()");
2419 deviceLost = true;
2421 }
2422 qWarning("Failed to begin frame command buffer: %d", err);
2423 return QRhi::FrameOpError;
2424 }
2425
2426 return QRhi::FrameOpSuccess;
2427}
2428
2430 VkSemaphore *waitSem, VkSemaphore *signalSem)
2431{
2432 VkResult err = df->vkEndCommandBuffer(cb);
2433 if (err != VK_SUCCESS) {
2434 if (err == VK_ERROR_DEVICE_LOST) {
2435 qWarning("Device loss detected in vkEndCommandBuffer()");
2436 deviceLost = true;
2438 }
2439 qWarning("Failed to end frame command buffer: %d", err);
2440 return QRhi::FrameOpError;
2441 }
2442
2443 VkSubmitInfo submitInfo = {};
2444 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
2445 submitInfo.commandBufferCount = 1;
2446 submitInfo.pCommandBuffers = &cb;
2447 if (waitSem) {
2448 submitInfo.waitSemaphoreCount = 1;
2449 submitInfo.pWaitSemaphores = waitSem;
2450 }
2451 if (signalSem) {
2452 submitInfo.signalSemaphoreCount = 1;
2453 submitInfo.pSignalSemaphores = signalSem;
2454 }
2455 VkPipelineStageFlags psf = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
2456 submitInfo.pWaitDstStageMask = &psf;
2457
2458 err = df->vkQueueSubmit(gfxQueue, 1, &submitInfo, cmdFence);
2459 if (err != VK_SUCCESS) {
2460 if (err == VK_ERROR_DEVICE_LOST) {
2461 qWarning("Device loss detected in vkQueueSubmit()");
2462 deviceLost = true;
2464 }
2465 qWarning("Failed to submit to graphics queue: %d", err);
2466 return QRhi::FrameOpError;
2467 }
2468
2469 return QRhi::FrameOpSuccess;
2470}
2471
2473{
2474 for (QVkSwapChain *sc : std::as_const(swapchains)) {
2475 const int frameResIndex = sc->bufferCount > 1 ? frameSlot : 0;
2476 QVkSwapChain::FrameResources &frame(sc->frameRes[frameResIndex]);
2477 if (frame.cmdFenceWaitable) {
2478 df->vkWaitForFences(dev, 1, &frame.cmdFence, VK_TRUE, UINT64_MAX);
2479 df->vkResetFences(dev, 1, &frame.cmdFence);
2480 frame.cmdFenceWaitable = false;
2481 }
2482 }
2483}
2484
2486{
2487 // Switch to the next slot manually. Swapchains do not know about this
2488 // which is good. So for example an onscreen, onscreen, offscreen,
2489 // onscreen, onscreen, onscreen sequence of frames leads to 0, 1, 0, 0, 1,
2490 // 0. (no strict alternation anymore) But this is not different from what
2491 // happens when multiple swapchains are involved. Offscreen frames are
2492 // synchronous anyway in the sense that they wait for execution to complete
2493 // in endOffscreenFrame, so no resources used in that frame are busy
2494 // anymore in the next frame.
2495
2497
2499
2501
2502 QVkCommandBuffer *cbWrapper = ofr.cbWrapper[currentFrameSlot];
2504 if (cbres != QRhi::FrameOpSuccess)
2505 return cbres;
2506
2507 prepareNewFrame(cbWrapper);
2508 ofr.active = true;
2509
2510 if (rhiFlags.testFlag(QRhi::EnableTimestamps)) {
2511 int timestampQueryIdx = -1;
2512 for (int i = 0; i < timestampQueryPoolMap.size(); ++i) {
2515 timestampQueryIdx = i * 2;
2516 break;
2517 }
2518 }
2519 if (timestampQueryIdx >= 0) {
2520 df->vkCmdResetQueryPool(cbWrapper->cb, timestampQueryPool, uint32_t(timestampQueryIdx), 2);
2521 // record timestamp at the start of the command buffer
2522 df->vkCmdWriteTimestamp(cbWrapper->cb, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
2523 timestampQueryPool, uint32_t(timestampQueryIdx));
2524 ofr.timestampQueryIndex = timestampQueryIdx;
2525 }
2526 }
2527
2528 *cb = cbWrapper;
2529 return QRhi::FrameOpSuccess;
2530}
2531
2533{
2534 Q_UNUSED(flags);
2535 Q_ASSERT(ofr.active);
2536 ofr.active = false;
2537
2538 QVkCommandBuffer *cbWrapper(ofr.cbWrapper[currentFrameSlot]);
2539 recordPrimaryCommandBuffer(cbWrapper);
2540
2541 // record another timestamp, when enabled
2542 if (ofr.timestampQueryIndex >= 0) {
2543 df->vkCmdWriteTimestamp(cbWrapper->cb, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
2544 timestampQueryPool, uint32_t(ofr.timestampQueryIndex + 1));
2545 }
2546
2547 if (!ofr.cmdFence) {
2548 VkFenceCreateInfo fenceInfo = {};
2549 fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
2550 VkResult err = df->vkCreateFence(dev, &fenceInfo, nullptr, &ofr.cmdFence);
2551 if (err != VK_SUCCESS) {
2552 qWarning("Failed to create command buffer fence: %d", err);
2553 return QRhi::FrameOpError;
2554 }
2555 }
2556
2557 QRhi::FrameOpResult submitres = endAndSubmitPrimaryCommandBuffer(cbWrapper->cb, ofr.cmdFence, nullptr, nullptr);
2558 if (submitres != QRhi::FrameOpSuccess)
2559 return submitres;
2560
2561 // wait for completion
2562 df->vkWaitForFences(dev, 1, &ofr.cmdFence, VK_TRUE, UINT64_MAX);
2563 df->vkResetFences(dev, 1, &ofr.cmdFence);
2564
2565 // Here we know that executing the host-side reads for this (or any
2566 // previous) frame is safe since we waited for completion above.
2568
2569 // Read the timestamps, if we wrote them.
2570 if (ofr.timestampQueryIndex >= 0) {
2571 quint64 timestamp[2] = { 0, 0 };
2572 VkResult err = df->vkGetQueryPoolResults(dev, timestampQueryPool, uint32_t(ofr.timestampQueryIndex), 2,
2573 2 * sizeof(quint64), timestamp, sizeof(quint64),
2574 VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT);
2575 timestampQueryPoolMap.clearBit(ofr.timestampQueryIndex / 2);
2576 ofr.timestampQueryIndex = -1;
2577 if (err == VK_SUCCESS) {
2578 bool ok = false;
2579 const double elapsedSec = elapsedSecondsFromTimestamp(timestamp, &ok);
2580 if (ok)
2581 cbWrapper->lastGpuTime = elapsedSec;
2582 } else {
2583 qWarning("Failed to query timestamp: %d", err);
2584 }
2585 }
2586
2587 return QRhi::FrameOpSuccess;
2588}
2589
2591{
2592 QVkSwapChain *swapChainD = nullptr;
2593 if (inFrame) {
2594 // There is either a swapchain or an offscreen frame on-going.
2595 // End command recording and submit what we have.
2596 VkCommandBuffer cb;
2597 if (ofr.active) {
2599 QVkCommandBuffer *cbWrapper(ofr.cbWrapper[currentFrameSlot]);
2601 recordPrimaryCommandBuffer(cbWrapper);
2602 cbWrapper->resetCommands();
2603 cb = cbWrapper->cb;
2604 } else {
2607 swapChainD = currentSwapChain;
2608 recordPrimaryCommandBuffer(&swapChainD->cbWrapper);
2609 swapChainD->cbWrapper.resetCommands();
2610 cb = swapChainD->cbWrapper.cb;
2611 }
2612 QRhi::FrameOpResult submitres = endAndSubmitPrimaryCommandBuffer(cb, VK_NULL_HANDLE, nullptr, nullptr);
2613 if (submitres != QRhi::FrameOpSuccess)
2614 return submitres;
2615 }
2616
2617 df->vkQueueWaitIdle(gfxQueue);
2618
2619 if (inFrame) {
2620 // The current frame slot's command pool needs to be reset.
2622 // Allocate and begin recording on a new command buffer.
2623 if (ofr.active) {
2625 } else {
2626 QVkSwapChain::FrameResources &frame(swapChainD->frameRes[swapChainD->currentFrameSlot]);
2628 swapChainD->cbWrapper.cb = frame.cmdBuf;
2629 }
2630 }
2631
2634
2635 return QRhi::FrameOpSuccess;
2636}
2637
2639{
2641 u.layout = 0; // unused with buffers
2642 u.access = int(bufUsage.access);
2643 u.stage = int(bufUsage.stage);
2644 return u;
2645}
2646
2648{
2650 u.layout = texUsage.layout;
2651 u.access = int(texUsage.access);
2652 u.stage = int(texUsage.stage);
2653 return u;
2654}
2655
2657{
2658 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QVkTexture, QVkRenderBuffer>(rtD->description(), rtD->d.currentResIdList))
2659 rtD->create();
2660
2661 rtD->lastActiveFrameSlot = currentFrameSlot;
2662 rtD->d.rp->lastActiveFrameSlot = currentFrameSlot;
2664 for (auto it = rtD->m_desc.cbeginColorAttachments(), itEnd = rtD->m_desc.cendColorAttachments(); it != itEnd; ++it) {
2665 QVkTexture *texD = QRHI_RES(QVkTexture, it->texture());
2666 QVkTexture *resolveTexD = QRHI_RES(QVkTexture, it->resolveTexture());
2667 QVkRenderBuffer *rbD = QRHI_RES(QVkRenderBuffer, it->renderBuffer());
2668 if (texD) {
2669 trackedRegisterTexture(&passResTracker, texD,
2672 texD->lastActiveFrameSlot = currentFrameSlot;
2673 } else if (rbD) {
2674 // Won't register rbD->backingTexture because it cannot be used for
2675 // anything in a renderpass, its use makes only sense in
2676 // combination with a resolveTexture.
2677 rbD->lastActiveFrameSlot = currentFrameSlot;
2678 }
2679 if (resolveTexD) {
2680 trackedRegisterTexture(&passResTracker, resolveTexD,
2683 resolveTexD->lastActiveFrameSlot = currentFrameSlot;
2684 }
2685 }
2686 if (rtD->m_desc.depthStencilBuffer()) {
2687 QVkRenderBuffer *rbD = QRHI_RES(QVkRenderBuffer, rtD->m_desc.depthStencilBuffer());
2689 // We specify no explicit VkSubpassDependency for an offscreen render
2690 // target, meaning we need an explicit barrier for the depth-stencil
2691 // buffer to avoid a write-after-write hazard (as the implicit one is
2692 // not sufficient). Textures are taken care of by the resource tracking
2693 // but that excludes the (content-wise) throwaway depth-stencil buffer.
2695 rbD->lastActiveFrameSlot = currentFrameSlot;
2696 }
2697 if (rtD->m_desc.depthTexture()) {
2698 QVkTexture *depthTexD = QRHI_RES(QVkTexture, rtD->m_desc.depthTexture());
2699 trackedRegisterTexture(&passResTracker, depthTexD,
2702 depthTexD->lastActiveFrameSlot = currentFrameSlot;
2703 }
2704 if (rtD->m_desc.depthResolveTexture()) {
2705 QVkTexture *depthResolveTexD = QRHI_RES(QVkTexture, rtD->m_desc.depthResolveTexture());
2706 trackedRegisterTexture(&passResTracker, depthResolveTexD,
2709 depthResolveTexD->lastActiveFrameSlot = currentFrameSlot;
2710 }
2711}
2712
2720
2722{
2723 VkCommandBuffer secondaryCb;
2724
2725 if (!freeSecondaryCbs[currentFrameSlot].isEmpty()) {
2726 secondaryCb = freeSecondaryCbs[currentFrameSlot].last();
2728 } else {
2729 VkCommandBufferAllocateInfo cmdBufInfo = {};
2730 cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
2731 cmdBufInfo.commandPool = cmdPool[currentFrameSlot];
2732 cmdBufInfo.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY;
2733 cmdBufInfo.commandBufferCount = 1;
2734
2735 VkResult err = df->vkAllocateCommandBuffers(dev, &cmdBufInfo, &secondaryCb);
2736 if (err != VK_SUCCESS) {
2737 qWarning("Failed to create secondary command buffer: %d", err);
2738 return VK_NULL_HANDLE;
2739 }
2740 }
2741
2742 VkCommandBufferBeginInfo cmdBufBeginInfo = {};
2743 cmdBufBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
2744 cmdBufBeginInfo.flags = rtD ? VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT : 0;
2745 VkCommandBufferInheritanceInfo cmdBufInheritInfo = {};
2746 cmdBufInheritInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
2747 cmdBufInheritInfo.subpass = 0;
2748 if (rtD) {
2749 cmdBufInheritInfo.renderPass = rtD->rp->rp;
2750 cmdBufInheritInfo.framebuffer = rtD->fb;
2751 }
2752 cmdBufBeginInfo.pInheritanceInfo = &cmdBufInheritInfo;
2753
2754 VkResult err = df->vkBeginCommandBuffer(secondaryCb, &cmdBufBeginInfo);
2755 if (err != VK_SUCCESS) {
2756 qWarning("Failed to begin secondary command buffer: %d", err);
2757 return VK_NULL_HANDLE;
2758 }
2759
2760 return secondaryCb;
2761}
2762
2764{
2765 VkResult err = df->vkEndCommandBuffer(cb);
2766 if (err != VK_SUCCESS)
2767 qWarning("Failed to end secondary command buffer: %d", err);
2768
2769 QVkCommandBuffer::Command &cmd(cbD->commands.get());
2771 cmd.args.executeSecondary.cb = cb;
2772
2777 releaseQueue.append(e);
2778}
2779
2781 QRhiRenderTarget *rt,
2782 const QColor &colorClearValue,
2783 const QRhiDepthStencilClearValue &depthStencilClearValue,
2784 QRhiResourceUpdateBatch *resourceUpdates,
2785 QRhiCommandBuffer::BeginPassFlags flags)
2786{
2789
2790 if (resourceUpdates)
2791 enqueueResourceUpdates(cbD, resourceUpdates);
2792
2793 // Insert a TransitionPassResources into the command stream, pointing to
2794 // the tracker this pass is going to use. That's how we generate the
2795 // barriers later during recording the real VkCommandBuffer, right before
2796 // the vkCmdBeginRenderPass.
2798
2799 QVkRenderTargetData *rtD = nullptr;
2800 switch (rt->resourceType()) {
2802 rtD = &QRHI_RES(QVkSwapChainRenderTarget, rt)->d;
2803 rtD->rp->lastActiveFrameSlot = currentFrameSlot;
2807 break;
2809 {
2811 rtD = &rtTex->d;
2812 activateTextureRenderTarget(cbD, rtTex);
2813 }
2814 break;
2815 default:
2816 Q_UNREACHABLE();
2817 break;
2818 }
2819
2822 cbD->currentTarget = rt;
2823
2824 // No copy operations or image layout transitions allowed after this point
2825 // (up until endPass) as we are going to begin the renderpass.
2826
2827 VkRenderPassBeginInfo rpBeginInfo = {};
2828 rpBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
2829 rpBeginInfo.renderPass = rtD->rp->rp;
2830 rpBeginInfo.framebuffer = rtD->fb;
2831 rpBeginInfo.renderArea.extent.width = uint32_t(rtD->pixelSize.width());
2832 rpBeginInfo.renderArea.extent.height = uint32_t(rtD->pixelSize.height());
2833
2834 QVarLengthArray<VkClearValue, 4> cvs;
2835 for (int i = 0; i < rtD->colorAttCount; ++i) {
2836 VkClearValue cv;
2837 cv.color = { { float(colorClearValue.redF()), float(colorClearValue.greenF()), float(colorClearValue.blueF()),
2838 float(colorClearValue.alphaF()) } };
2839 cvs.append(cv);
2840 }
2841 for (int i = 0; i < rtD->dsAttCount; ++i) {
2842 VkClearValue cv;
2843 cv.depthStencil = { depthStencilClearValue.depthClearValue(), depthStencilClearValue.stencilClearValue() };
2844 cvs.append(cv);
2845 }
2846 for (int i = 0; i < rtD->resolveAttCount; ++i) {
2847 VkClearValue cv;
2848 cv.color = { { float(colorClearValue.redF()), float(colorClearValue.greenF()), float(colorClearValue.blueF()),
2849 float(colorClearValue.alphaF()) } };
2850 cvs.append(cv);
2851 }
2852 for (int i = 0; i < rtD->dsResolveAttCount; ++i) {
2853 VkClearValue cv;
2854 cv.depthStencil = { depthStencilClearValue.depthClearValue(), depthStencilClearValue.stencilClearValue() };
2855 cvs.append(cv);
2856 }
2857 rpBeginInfo.clearValueCount = uint32_t(cvs.size());
2858
2859 QVkCommandBuffer::Command &cmd(cbD->commands.get());
2861 cmd.args.beginRenderPass.desc = rpBeginInfo;
2862 cmd.args.beginRenderPass.clearValueIndex = cbD->pools.clearValue.size();
2863 cmd.args.beginRenderPass.useSecondaryCb = cbD->passUsesSecondaryCb;
2864 cbD->pools.clearValue.append(cvs.constData(), cvs.size());
2865
2866 if (cbD->passUsesSecondaryCb)
2868
2869 cbD->resetCachedState();
2870}
2871
2873{
2876
2877 if (cbD->passUsesSecondaryCb) {
2878 VkCommandBuffer secondaryCb = cbD->activeSecondaryCbStack.last();
2880 endAndEnqueueSecondaryCommandBuffer(secondaryCb, cbD);
2881 }
2882
2883 QVkCommandBuffer::Command &cmd(cbD->commands.get());
2885
2887 cbD->currentTarget = nullptr;
2888
2889 if (resourceUpdates)
2890 enqueueResourceUpdates(cbD, resourceUpdates);
2891}
2892
2894 QRhiResourceUpdateBatch *resourceUpdates,
2895 QRhiCommandBuffer::BeginPassFlags flags)
2896{
2899
2900 if (resourceUpdates)
2901 enqueueResourceUpdates(cbD, resourceUpdates);
2902
2904
2907
2908 cbD->computePassState.reset();
2909
2910 if (cbD->passUsesSecondaryCb)
2912
2913 cbD->resetCachedState();
2914}
2915
2917{
2920
2921 if (cbD->passUsesSecondaryCb) {
2922 VkCommandBuffer secondaryCb = cbD->activeSecondaryCbStack.last();
2924 endAndEnqueueSecondaryCommandBuffer(secondaryCb, cbD);
2925 }
2926
2928
2929 if (resourceUpdates)
2930 enqueueResourceUpdates(cbD, resourceUpdates);
2931}
2932
2934{
2936 Q_ASSERT(psD->pipeline);
2939
2940 if (cbD->currentComputePipeline != ps || cbD->currentPipelineGeneration != psD->generation) {
2941 if (cbD->passUsesSecondaryCb) {
2942 df->vkCmdBindPipeline(cbD->activeSecondaryCbStack.last(), VK_PIPELINE_BIND_POINT_COMPUTE, psD->pipeline);
2943 } else {
2944 QVkCommandBuffer::Command &cmd(cbD->commands.get());
2946 cmd.args.bindPipeline.bindPoint = VK_PIPELINE_BIND_POINT_COMPUTE;
2947 cmd.args.bindPipeline.pipeline = psD->pipeline;
2948 }
2949
2950 cbD->currentGraphicsPipeline = nullptr;
2951 cbD->currentComputePipeline = ps;
2952 cbD->currentPipelineGeneration = psD->generation;
2953 }
2954
2955 psD->lastActiveFrameSlot = currentFrameSlot;
2956}
2957
2958template<typename T>
2959inline void qrhivk_accumulateComputeResource(T *writtenResources, QRhiResource *resource,
2961 int loadTypeVal, int storeTypeVal, int loadStoreTypeVal)
2962{
2963 VkAccessFlags access = 0;
2964 if (bindingType == loadTypeVal) {
2965 access = VK_ACCESS_SHADER_READ_BIT;
2966 } else {
2967 access = VK_ACCESS_SHADER_WRITE_BIT;
2968 if (bindingType == loadStoreTypeVal)
2969 access |= VK_ACCESS_SHADER_READ_BIT;
2970 }
2971 auto it = writtenResources->find(resource);
2972 if (it != writtenResources->end())
2973 it->first |= access;
2974 else if (bindingType == storeTypeVal || bindingType == loadStoreTypeVal)
2975 writtenResources->insert(resource, { access, true });
2976}
2977
2979{
2982
2983 // When there are multiple dispatches, read-after-write and
2984 // write-after-write need a barrier.
2985 QVarLengthArray<VkImageMemoryBarrier, 8> imageBarriers;
2986 QVarLengthArray<VkBufferMemoryBarrier, 8> bufferBarriers;
2987 if (cbD->currentComputeSrb) {
2988 // The key in the writtenResources map indicates that the resource was
2989 // written in a previous dispatch, whereas the value accumulates the
2990 // access mask in the current one.
2991 for (auto &accessAndIsNewFlag : cbD->computePassState.writtenResources)
2992 accessAndIsNewFlag = { 0, false };
2993
2995 const int bindingCount = srbD->m_bindings.size();
2996 for (int i = 0; i < bindingCount; ++i) {
2997 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->m_bindings.at(i));
2998 switch (b->type) {
3003 b->u.simage.tex,
3004 b->type,
3008 break;
3013 b->u.sbuf.buf,
3014 b->type,
3018 break;
3019 default:
3020 break;
3021 }
3022 }
3023
3024 for (auto it = cbD->computePassState.writtenResources.begin(); it != cbD->computePassState.writtenResources.end(); ) {
3025 const int accessInThisDispatch = it->first;
3026 const bool isNewInThisDispatch = it->second;
3027 if (accessInThisDispatch && !isNewInThisDispatch) {
3028 if (it.key()->resourceType() == QRhiResource::Texture) {
3029 QVkTexture *texD = QRHI_RES(QVkTexture, it.key());
3030 VkImageMemoryBarrier barrier = {};
3031 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
3032 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
3033 // won't care about subresources, pretend the whole resource was written
3034 barrier.subresourceRange.baseMipLevel = 0;
3035 barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
3036 barrier.subresourceRange.baseArrayLayer = 0;
3037 barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
3038 barrier.oldLayout = texD->usageState.layout;
3039 barrier.newLayout = texD->usageState.layout;
3040 barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
3041 barrier.dstAccessMask = accessInThisDispatch;
3042 barrier.image = texD->image;
3043 imageBarriers.append(barrier);
3044 } else {
3045 QVkBuffer *bufD = QRHI_RES(QVkBuffer, it.key());
3046 VkBufferMemoryBarrier barrier = {};
3047 barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
3048 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
3049 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
3050 barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
3051 barrier.dstAccessMask = accessInThisDispatch;
3052 barrier.buffer = bufD->buffers[bufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0];
3053 barrier.size = VK_WHOLE_SIZE;
3054 bufferBarriers.append(barrier);
3055 }
3056 }
3057 // Anything that was previously written, but is only read now, can be
3058 // removed from the written list (because that previous write got a
3059 // corresponding barrier now).
3060 if (accessInThisDispatch == VK_ACCESS_SHADER_READ_BIT)
3061 it = cbD->computePassState.writtenResources.erase(it);
3062 else
3063 ++it;
3064 }
3065 }
3066
3067 if (cbD->passUsesSecondaryCb) {
3068 VkCommandBuffer secondaryCb = cbD->activeSecondaryCbStack.last();
3069 if (!imageBarriers.isEmpty()) {
3070 df->vkCmdPipelineBarrier(secondaryCb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
3071 0, 0, nullptr,
3072 0, nullptr,
3073 imageBarriers.size(), imageBarriers.constData());
3074 }
3075 if (!bufferBarriers.isEmpty()) {
3076 df->vkCmdPipelineBarrier(secondaryCb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
3077 0, 0, nullptr,
3078 bufferBarriers.size(), bufferBarriers.constData(),
3079 0, nullptr);
3080 }
3081 df->vkCmdDispatch(secondaryCb, uint32_t(x), uint32_t(y), uint32_t(z));
3082 } else {
3083 if (!imageBarriers.isEmpty()) {
3084 QVkCommandBuffer::Command &cmd(cbD->commands.get());
3086 cmd.args.imageBarrier.srcStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
3087 cmd.args.imageBarrier.dstStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
3088 cmd.args.imageBarrier.count = imageBarriers.size();
3089 cmd.args.imageBarrier.index = cbD->pools.imageBarrier.size();
3090 cbD->pools.imageBarrier.append(imageBarriers.constData(), imageBarriers.size());
3091 }
3092 if (!bufferBarriers.isEmpty()) {
3093 QVkCommandBuffer::Command &cmd(cbD->commands.get());
3095 cmd.args.bufferBarrier.srcStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
3096 cmd.args.bufferBarrier.dstStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
3097 cmd.args.bufferBarrier.count = bufferBarriers.size();
3098 cmd.args.bufferBarrier.index = cbD->pools.bufferBarrier.size();
3099 cbD->pools.bufferBarrier.append(bufferBarriers.constData(), bufferBarriers.size());
3100 }
3101 QVkCommandBuffer::Command &cmd(cbD->commands.get());
3103 cmd.args.dispatch.x = x;
3104 cmd.args.dispatch.y = y;
3105 cmd.args.dispatch.z = z;
3106 }
3107}
3108
3109VkShaderModule QRhiVulkan::createShader(const QByteArray &spirv)
3110{
3111 VkShaderModuleCreateInfo shaderInfo = {};
3112 shaderInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
3113 shaderInfo.codeSize = size_t(spirv.size());
3114 shaderInfo.pCode = reinterpret_cast<const quint32 *>(spirv.constData());
3115 VkShaderModule shaderModule;
3116 VkResult err = df->vkCreateShaderModule(dev, &shaderInfo, nullptr, &shaderModule);
3117 if (err != VK_SUCCESS) {
3118 qWarning("Failed to create shader module: %d", err);
3119 return VK_NULL_HANDLE;
3120 }
3121 return shaderModule;
3122}
3123
3124bool QRhiVulkan::ensurePipelineCache(const void *initialData, size_t initialDataSize)
3125{
3126 if (pipelineCache)
3127 return true;
3128
3129 VkPipelineCacheCreateInfo pipelineCacheInfo = {};
3130 pipelineCacheInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
3131 pipelineCacheInfo.initialDataSize = initialDataSize;
3132 pipelineCacheInfo.pInitialData = initialData;
3133 VkResult err = df->vkCreatePipelineCache(dev, &pipelineCacheInfo, nullptr, &pipelineCache);
3134 if (err != VK_SUCCESS) {
3135 qWarning("Failed to create pipeline cache: %d", err);
3136 return false;
3137 }
3138 return true;
3139}
3140
3142{
3144
3145 QVarLengthArray<VkDescriptorBufferInfo, 8> bufferInfos;
3146 using ArrayOfImageDesc = QVarLengthArray<VkDescriptorImageInfo, 8>;
3147 QVarLengthArray<ArrayOfImageDesc, 8> imageInfos;
3148 QVarLengthArray<VkWriteDescriptorSet, 12> writeInfos;
3149 QVarLengthArray<QPair<int, int>, 12> infoIndices;
3150
3151 const bool updateAll = descSetIdx < 0;
3152 int frameSlot = updateAll ? 0 : descSetIdx;
3153 while (frameSlot < (updateAll ? QVK_FRAMES_IN_FLIGHT : descSetIdx + 1)) {
3154 for (int i = 0, ie = srbD->sortedBindings.size(); i != ie; ++i) {
3155 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->sortedBindings.at(i));
3156 QVkShaderResourceBindings::BoundResourceData &bd(srbD->boundResourceData[frameSlot][i]);
3157
3158 VkWriteDescriptorSet writeInfo = {};
3159 writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
3160 writeInfo.dstSet = srbD->descSets[frameSlot];
3161 writeInfo.dstBinding = uint32_t(b->binding);
3162 writeInfo.descriptorCount = 1;
3163
3164 int bufferInfoIndex = -1;
3165 int imageInfoIndex = -1;
3166
3167 switch (b->type) {
3169 {
3170 writeInfo.descriptorType = b->u.ubuf.hasDynamicOffset ? VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC
3171 : VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
3172 QRhiBuffer *buf = b->u.ubuf.buf;
3173 QVkBuffer *bufD = QRHI_RES(QVkBuffer, buf);
3174 bd.ubuf.id = bufD->m_id;
3175 bd.ubuf.generation = bufD->generation;
3176 VkDescriptorBufferInfo bufInfo;
3177 bufInfo.buffer = bufD->m_type == QRhiBuffer::Dynamic ? bufD->buffers[frameSlot] : bufD->buffers[0];
3178 bufInfo.offset = b->u.ubuf.offset;
3179 bufInfo.range = b->u.ubuf.maybeSize ? b->u.ubuf.maybeSize : bufD->m_size;
3180 // be nice and assert when we know the vulkan device would die a horrible death due to non-aligned reads
3181 Q_ASSERT(aligned(bufInfo.offset, ubufAlign) == bufInfo.offset);
3182 bufferInfoIndex = bufferInfos.size();
3183 bufferInfos.append(bufInfo);
3184 }
3185 break;
3187 {
3189 writeInfo.descriptorCount = data->count; // arrays of combined image samplers are supported
3190 writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
3191 ArrayOfImageDesc imageInfo(data->count);
3192 for (int elem = 0; elem < data->count; ++elem) {
3193 QVkTexture *texD = QRHI_RES(QVkTexture, data->texSamplers[elem].tex);
3194 QVkSampler *samplerD = QRHI_RES(QVkSampler, data->texSamplers[elem].sampler);
3195 bd.stex.d[elem].texId = texD->m_id;
3196 bd.stex.d[elem].texGeneration = texD->generation;
3197 bd.stex.d[elem].samplerId = samplerD->m_id;
3198 bd.stex.d[elem].samplerGeneration = samplerD->generation;
3199 imageInfo[elem].sampler = samplerD->sampler;
3200 imageInfo[elem].imageView = texD->imageView;
3201 imageInfo[elem].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
3202 }
3203 bd.stex.count = data->count;
3204 imageInfoIndex = imageInfos.size();
3205 imageInfos.append(imageInfo);
3206 }
3207 break;
3209 {
3211 writeInfo.descriptorCount = data->count; // arrays of (separate) images are supported
3212 writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
3213 ArrayOfImageDesc imageInfo(data->count);
3214 for (int elem = 0; elem < data->count; ++elem) {
3215 QVkTexture *texD = QRHI_RES(QVkTexture, data->texSamplers[elem].tex);
3216 bd.stex.d[elem].texId = texD->m_id;
3217 bd.stex.d[elem].texGeneration = texD->generation;
3218 bd.stex.d[elem].samplerId = 0;
3219 bd.stex.d[elem].samplerGeneration = 0;
3220 imageInfo[elem].sampler = VK_NULL_HANDLE;
3221 imageInfo[elem].imageView = texD->imageView;
3222 imageInfo[elem].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
3223 }
3224 bd.stex.count = data->count;
3225 imageInfoIndex = imageInfos.size();
3226 imageInfos.append(imageInfo);
3227 }
3228 break;
3230 {
3231 QVkSampler *samplerD = QRHI_RES(QVkSampler, b->u.stex.texSamplers[0].sampler);
3232 writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;
3233 bd.stex.d[0].texId = 0;
3234 bd.stex.d[0].texGeneration = 0;
3235 bd.stex.d[0].samplerId = samplerD->m_id;
3236 bd.stex.d[0].samplerGeneration = samplerD->generation;
3237 ArrayOfImageDesc imageInfo(1);
3238 imageInfo[0].sampler = samplerD->sampler;
3239 imageInfo[0].imageView = VK_NULL_HANDLE;
3240 imageInfo[0].imageLayout = VK_IMAGE_LAYOUT_GENERAL;
3241 imageInfoIndex = imageInfos.size();
3242 imageInfos.append(imageInfo);
3243 }
3244 break;
3248 {
3249 QVkTexture *texD = QRHI_RES(QVkTexture, b->u.simage.tex);
3250 VkImageView view = texD->perLevelImageViewForLoadStore(b->u.simage.level);
3251 if (view) {
3252 writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
3253 bd.simage.id = texD->m_id;
3254 bd.simage.generation = texD->generation;
3255 ArrayOfImageDesc imageInfo(1);
3256 imageInfo[0].sampler = VK_NULL_HANDLE;
3257 imageInfo[0].imageView = view;
3258 imageInfo[0].imageLayout = VK_IMAGE_LAYOUT_GENERAL;
3259 imageInfoIndex = imageInfos.size();
3260 imageInfos.append(imageInfo);
3261 }
3262 }
3263 break;
3267 {
3268 QVkBuffer *bufD = QRHI_RES(QVkBuffer, b->u.sbuf.buf);
3269 writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
3270 bd.sbuf.id = bufD->m_id;
3271 bd.sbuf.generation = bufD->generation;
3272 VkDescriptorBufferInfo bufInfo;
3273 bufInfo.buffer = bufD->m_type == QRhiBuffer::Dynamic ? bufD->buffers[frameSlot] : bufD->buffers[0];
3274 bufInfo.offset = b->u.ubuf.offset;
3275 bufInfo.range = b->u.ubuf.maybeSize ? b->u.ubuf.maybeSize : bufD->m_size;
3276 bufferInfoIndex = bufferInfos.size();
3277 bufferInfos.append(bufInfo);
3278 }
3279 break;
3280 default:
3281 continue;
3282 }
3283
3284 writeInfos.append(writeInfo);
3285 infoIndices.append({ bufferInfoIndex, imageInfoIndex });
3286 }
3287 ++frameSlot;
3288 }
3289
3290 for (int i = 0, writeInfoCount = writeInfos.size(); i < writeInfoCount; ++i) {
3291 const int bufferInfoIndex = infoIndices[i].first;
3292 const int imageInfoIndex = infoIndices[i].second;
3293 if (bufferInfoIndex >= 0)
3294 writeInfos[i].pBufferInfo = &bufferInfos[bufferInfoIndex];
3295 else if (imageInfoIndex >= 0)
3296 writeInfos[i].pImageInfo = imageInfos[imageInfoIndex].constData();
3297 }
3298
3299 df->vkUpdateDescriptorSets(dev, uint32_t(writeInfos.size()), writeInfos.constData(), 0, nullptr);
3300}
3301
3302static inline bool accessIsWrite(VkAccessFlags access)
3303{
3304 return (access & VK_ACCESS_SHADER_WRITE_BIT) != 0
3305 || (access & VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT) != 0
3306 || (access & VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT) != 0
3307 || (access & VK_ACCESS_TRANSFER_WRITE_BIT) != 0
3308 || (access & VK_ACCESS_HOST_WRITE_BIT) != 0
3309 || (access & VK_ACCESS_MEMORY_WRITE_BIT) != 0;
3310}
3311
3313 VkAccessFlags access, VkPipelineStageFlags stage)
3314{
3316 Q_ASSERT(access && stage);
3317 QVkBuffer::UsageState &s(bufD->usageState[slot]);
3318 if (!s.stage) {
3319 s.access = access;
3320 s.stage = stage;
3321 return;
3322 }
3323
3324 if (s.access == access && s.stage == stage) {
3325 // No need to flood with unnecessary read-after-read barriers.
3326 // Write-after-write is a different matter, however.
3327 if (!accessIsWrite(access))
3328 return;
3329 }
3330
3331 VkBufferMemoryBarrier bufMemBarrier = {};
3332 bufMemBarrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
3333 bufMemBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
3334 bufMemBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
3335 bufMemBarrier.srcAccessMask = s.access;
3336 bufMemBarrier.dstAccessMask = access;
3337 bufMemBarrier.buffer = bufD->buffers[slot];
3338 bufMemBarrier.size = VK_WHOLE_SIZE;
3339
3340 QVkCommandBuffer::Command &cmd(cbD->commands.get());
3342 cmd.args.bufferBarrier.srcStageMask = s.stage;
3343 cmd.args.bufferBarrier.dstStageMask = stage;
3344 cmd.args.bufferBarrier.count = 1;
3345 cmd.args.bufferBarrier.index = cbD->pools.bufferBarrier.size();
3346 cbD->pools.bufferBarrier.append(bufMemBarrier);
3347
3348 s.access = access;
3349 s.stage = stage;
3350}
3351
3353 VkImageLayout layout, VkAccessFlags access, VkPipelineStageFlags stage)
3354{
3356 Q_ASSERT(layout && access && stage);
3357 QVkTexture::UsageState &s(texD->usageState);
3358 if (s.access == access && s.stage == stage && s.layout == layout) {
3359 if (!accessIsWrite(access))
3360 return;
3361 }
3362
3363 VkImageMemoryBarrier barrier = {};
3364 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
3365 barrier.subresourceRange.aspectMask = aspectMaskForTextureFormat(texD->m_format);
3366 barrier.subresourceRange.baseMipLevel = 0;
3367 barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
3368 barrier.subresourceRange.baseArrayLayer = 0;
3369 barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
3370 barrier.oldLayout = s.layout; // new textures have this set to PREINITIALIZED
3371 barrier.newLayout = layout;
3372 barrier.srcAccessMask = s.access; // may be 0 but that's fine
3373 barrier.dstAccessMask = access;
3374 barrier.image = texD->image;
3375
3376 VkPipelineStageFlags srcStage = s.stage;
3377 // stage mask cannot be 0
3378 if (!srcStage)
3379 srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
3380
3381 QVkCommandBuffer::Command &cmd(cbD->commands.get());
3383 cmd.args.imageBarrier.srcStageMask = srcStage;
3384 cmd.args.imageBarrier.dstStageMask = stage;
3385 cmd.args.imageBarrier.count = 1;
3386 cmd.args.imageBarrier.index = cbD->pools.imageBarrier.size();
3387 cbD->pools.imageBarrier.append(barrier);
3388
3389 s.layout = layout;
3390 s.access = access;
3391 s.stage = stage;
3392}
3393
3395{
3397
3398 VkImageMemoryBarrier barrier = {};
3399 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
3400 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
3401 barrier.subresourceRange.baseMipLevel = 0;
3402 barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
3403 barrier.subresourceRange.baseArrayLayer = 0;
3404 barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
3405 barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
3406 barrier.newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
3407 barrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
3408 barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT
3409 | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
3410 barrier.image = rbD->image;
3411
3412 const VkPipelineStageFlags stages = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT
3413 | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
3414
3415 QVkCommandBuffer::Command &cmd(cbD->commands.get());
3417 cmd.args.imageBarrier.srcStageMask = stages;
3418 cmd.args.imageBarrier.dstStageMask = stages;
3419 cmd.args.imageBarrier.count = 1;
3420 cmd.args.imageBarrier.index = cbD->pools.imageBarrier.size();
3421 cbD->pools.imageBarrier.append(barrier);
3422}
3423
3425 VkImageLayout oldLayout, VkImageLayout newLayout,
3426 VkAccessFlags srcAccess, VkAccessFlags dstAccess,
3427 VkPipelineStageFlags srcStage, VkPipelineStageFlags dstStage,
3428 int startLayer, int layerCount,
3429 int startLevel, int levelCount)
3430{
3432 VkImageMemoryBarrier barrier = {};
3433 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
3434 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
3435 barrier.subresourceRange.baseMipLevel = uint32_t(startLevel);
3436 barrier.subresourceRange.levelCount = uint32_t(levelCount);
3437 barrier.subresourceRange.baseArrayLayer = uint32_t(startLayer);
3438 barrier.subresourceRange.layerCount = uint32_t(layerCount);
3439 barrier.oldLayout = oldLayout;
3440 barrier.newLayout = newLayout;
3441 barrier.srcAccessMask = srcAccess;
3442 barrier.dstAccessMask = dstAccess;
3443 barrier.image = image;
3444
3445 QVkCommandBuffer::Command &cmd(cbD->commands.get());
3447 cmd.args.imageBarrier.srcStageMask = srcStage;
3448 cmd.args.imageBarrier.dstStageMask = dstStage;
3449 cmd.args.imageBarrier.count = 1;
3450 cmd.args.imageBarrier.index = cbD->pools.imageBarrier.size();
3451 cbD->pools.imageBarrier.append(barrier);
3452}
3453
3455{
3456 VkDeviceSize size = 0;
3457 const qsizetype imageSizeBytes = subresDesc.image().isNull() ?
3458 subresDesc.data().size() : subresDesc.image().sizeInBytes();
3459 if (imageSizeBytes > 0)
3460 size += aligned(VkDeviceSize(imageSizeBytes), texbufAlign);
3461 return size;
3462}
3463
3466 size_t *curOfs, void *mp,
3467 BufferImageCopyList *copyInfos)
3468{
3469 qsizetype copySizeBytes = 0;
3470 qsizetype imageSizeBytes = 0;
3471 const void *src = nullptr;
3472 const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3473 const bool is1D = texD->m_flags.testFlag(QRhiTexture::OneDimensional);
3474
3475 VkBufferImageCopy copyInfo = {};
3476 copyInfo.bufferOffset = *curOfs;
3477 copyInfo.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
3478 copyInfo.imageSubresource.mipLevel = uint32_t(level);
3479 copyInfo.imageSubresource.baseArrayLayer = is3D ? 0 : uint32_t(layer);
3480 copyInfo.imageSubresource.layerCount = 1;
3481 copyInfo.imageExtent.depth = 1;
3482 if (is3D)
3483 copyInfo.imageOffset.z = uint32_t(layer);
3484 if (is1D)
3485 copyInfo.imageOffset.y = uint32_t(layer);
3486
3487 const QByteArray rawData = subresDesc.data();
3488 const QPoint dp = subresDesc.destinationTopLeft();
3489 QImage image = subresDesc.image();
3490 if (!image.isNull()) {
3491 copySizeBytes = imageSizeBytes = image.sizeInBytes();
3492 QSize size = image.size();
3493 src = image.constBits();
3494 // Scanlines in QImage are 4 byte aligned so bpl must
3495 // be taken into account for bufferRowLength.
3496 int bpc = qMax(1, image.depth() / 8);
3497 // this is in pixels, not bytes, to make it more complicated...
3498 copyInfo.bufferRowLength = uint32_t(image.bytesPerLine() / bpc);
3499 if (!subresDesc.sourceSize().isEmpty() || !subresDesc.sourceTopLeft().isNull()) {
3500 const int sx = subresDesc.sourceTopLeft().x();
3501 const int sy = subresDesc.sourceTopLeft().y();
3502 if (!subresDesc.sourceSize().isEmpty())
3503 size = subresDesc.sourceSize();
3504
3505 if (size.width() == image.width()) {
3506 // No need to make a QImage copy here, can copy from the source
3507 // QImage into staging directly.
3508 src = image.constBits() + sy * image.bytesPerLine() + sx * bpc;
3509 copySizeBytes = size.height() * image.bytesPerLine();
3510 } else {
3511 image = image.copy(sx, sy, size.width(), size.height());
3512 src = image.constBits();
3513 // The staging buffer gets the slice only. The rest of the
3514 // space reserved for this mip will be unused.
3515 copySizeBytes = image.sizeInBytes();
3516 bpc = qMax(1, image.depth() / 8);
3517 copyInfo.bufferRowLength = uint32_t(image.bytesPerLine() / bpc);
3518 }
3519 }
3520 copyInfo.imageOffset.x = dp.x();
3521 copyInfo.imageOffset.y = dp.y();
3522 copyInfo.imageExtent.width = uint32_t(size.width());
3523 copyInfo.imageExtent.height = uint32_t(size.height());
3524 copyInfos->append(copyInfo);
3525 } else if (!rawData.isEmpty() && isCompressedFormat(texD->m_format)) {
3526 copySizeBytes = imageSizeBytes = rawData.size();
3527 src = rawData.constData();
3528 QSize size = q->sizeForMipLevel(level, texD->m_pixelSize);
3529 const int subresw = size.width();
3530 const int subresh = size.height();
3531 if (!subresDesc.sourceSize().isEmpty())
3532 size = subresDesc.sourceSize();
3533 const int w = size.width();
3534 const int h = size.height();
3535 QSize blockDim;
3536 compressedFormatInfo(texD->m_format, QSize(w, h), nullptr, nullptr, &blockDim);
3537 // x and y must be multiples of the block width and height
3538 copyInfo.imageOffset.x = aligned(dp.x(), blockDim.width());
3539 copyInfo.imageOffset.y = aligned(dp.y(), blockDim.height());
3540 // width and height must be multiples of the block width and height
3541 // or x + width and y + height must equal the subresource width and height
3542 copyInfo.imageExtent.width = uint32_t(dp.x() + w == subresw ? w : aligned(w, blockDim.width()));
3543 copyInfo.imageExtent.height = uint32_t(dp.y() + h == subresh ? h : aligned(h, blockDim.height()));
3544 copyInfos->append(copyInfo);
3545 } else if (!rawData.isEmpty()) {
3546 copySizeBytes = imageSizeBytes = rawData.size();
3547 src = rawData.constData();
3548 QSize size = q->sizeForMipLevel(level, texD->m_pixelSize);
3549 if (subresDesc.dataStride()) {
3550 quint32 bytesPerPixel = 0;
3551 textureFormatInfo(texD->m_format, size, nullptr, nullptr, &bytesPerPixel);
3552 if (bytesPerPixel)
3553 copyInfo.bufferRowLength = subresDesc.dataStride() / bytesPerPixel;
3554 }
3555 if (!subresDesc.sourceSize().isEmpty())
3556 size = subresDesc.sourceSize();
3557 copyInfo.imageOffset.x = dp.x();
3558 copyInfo.imageOffset.y = dp.y();
3559 copyInfo.imageExtent.width = uint32_t(size.width());
3560 copyInfo.imageExtent.height = uint32_t(size.height());
3561 copyInfos->append(copyInfo);
3562 } else {
3563 qWarning("Invalid texture upload for %p layer=%d mip=%d", texD, layer, level);
3564 }
3565
3566 if (src) {
3567 memcpy(reinterpret_cast<char *>(mp) + *curOfs, src, size_t(copySizeBytes));
3568 *curOfs += aligned(VkDeviceSize(imageSizeBytes), texbufAlign);
3569 }
3570}
3571
3573{
3574 if (err == VK_ERROR_OUT_OF_DEVICE_MEMORY)
3575 qWarning() << "Out of device memory, current allocator statistics are" << statistics();
3576}
3577
3579{
3581
3582 for (int opIdx = 0; opIdx < ud->activeBufferOpCount; ++opIdx) {
3583 const QRhiResourceUpdateBatchPrivate::BufferOp &u(ud->bufferOps[opIdx]);
3585 QVkBuffer *bufD = QRHI_RES(QVkBuffer, u.buf);
3586 Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
3587 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
3588 if (u.offset == 0 && u.data.size() == bufD->m_size)
3589 bufD->pendingDynamicUpdates[i].clear();
3590 bufD->pendingDynamicUpdates[i].append({ u.offset, u.data });
3591 }
3593 QVkBuffer *bufD = QRHI_RES(QVkBuffer, u.buf);
3594 Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic);
3595 Q_ASSERT(u.offset + u.data.size() <= bufD->m_size);
3596
3597 if (!bufD->stagingBuffers[currentFrameSlot]) {
3598 VkBufferCreateInfo bufferInfo = {};
3599 bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
3600 // must cover the entire buffer - this way multiple, partial updates per frame
3601 // are supported even when the staging buffer is reused (Static)
3602 bufferInfo.size = bufD->m_size;
3603 bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
3604
3605 VmaAllocationCreateInfo allocInfo = {};
3606 allocInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
3607
3608 VmaAllocation allocation;
3609 VkResult err = vmaCreateBuffer(toVmaAllocator(allocator), &bufferInfo, &allocInfo,
3610 &bufD->stagingBuffers[currentFrameSlot], &allocation, nullptr);
3611 if (err == VK_SUCCESS) {
3612 bufD->stagingAllocations[currentFrameSlot] = allocation;
3613 } else {
3614 qWarning("Failed to create staging buffer of size %u: %d", bufD->m_size, err);
3616 continue;
3617 }
3618 }
3619
3620 void *p = nullptr;
3621 VmaAllocation a = toVmaAllocation(bufD->stagingAllocations[currentFrameSlot]);
3622 VkResult err = vmaMapMemory(toVmaAllocator(allocator), a, &p);
3623 if (err != VK_SUCCESS) {
3624 qWarning("Failed to map buffer: %d", err);
3625 continue;
3626 }
3627 memcpy(static_cast<uchar *>(p) + u.offset, u.data.constData(), u.data.size());
3628 vmaFlushAllocation(toVmaAllocator(allocator), a, u.offset, u.data.size());
3629 vmaUnmapMemory(toVmaAllocator(allocator), a);
3630
3631 trackedBufferBarrier(cbD, bufD, 0,
3632 VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
3633
3634 VkBufferCopy copyInfo = {};
3635 copyInfo.srcOffset = u.offset;
3636 copyInfo.dstOffset = u.offset;
3637 copyInfo.size = u.data.size();
3638
3639 QVkCommandBuffer::Command &cmd(cbD->commands.get());
3641 cmd.args.copyBuffer.src = bufD->stagingBuffers[currentFrameSlot];
3642 cmd.args.copyBuffer.dst = bufD->buffers[0];
3643 cmd.args.copyBuffer.desc = copyInfo;
3644
3645 // Where's the barrier for read-after-write? (assuming the common case
3646 // of binding this buffer as vertex/index, or, less likely, as uniform
3647 // buffer, in a renderpass later on) That is handled by the pass
3648 // resource tracking: the appropriate pipeline barrier will be
3649 // generated and recorded right before the renderpass, that binds this
3650 // buffer in one of its commands, gets its BeginRenderPass recorded.
3651
3652 bufD->lastActiveFrameSlot = currentFrameSlot;
3653
3654 if (bufD->m_type == QRhiBuffer::Immutable) {
3658 e.stagingBuffer.stagingBuffer = bufD->stagingBuffers[currentFrameSlot];
3659 e.stagingBuffer.stagingAllocation = bufD->stagingAllocations[currentFrameSlot];
3660 bufD->stagingBuffers[currentFrameSlot] = VK_NULL_HANDLE;
3661 bufD->stagingAllocations[currentFrameSlot] = nullptr;
3662 releaseQueue.append(e);
3663 }
3665 QVkBuffer *bufD = QRHI_RES(QVkBuffer, u.buf);
3666 if (bufD->m_type == QRhiBuffer::Dynamic) {
3668 void *p = nullptr;
3669 VmaAllocation a = toVmaAllocation(bufD->allocations[currentFrameSlot]);
3670 VkResult err = vmaMapMemory(toVmaAllocator(allocator), a, &p);
3671 if (err == VK_SUCCESS) {
3672 u.result->data.resize(u.readSize);
3673 memcpy(u.result->data.data(), reinterpret_cast<char *>(p) + u.offset, u.readSize);
3674 vmaUnmapMemory(toVmaAllocator(allocator), a);
3675 }
3676 if (u.result->completed)
3677 u.result->completed();
3678 } else {
3679 // Non-Dynamic buffers may not be host visible, so have to
3680 // create a readback buffer, enqueue a copy from
3681 // bufD->buffers[0] to this buffer, and then once the command
3682 // buffer completes, copy the data out of the host visible
3683 // readback buffer. Quite similar to what we do for texture
3684 // readbacks.
3685 BufferReadback readback;
3687 readback.result = u.result;
3688 readback.byteSize = u.readSize;
3689
3690 VkBufferCreateInfo bufferInfo = {};
3691 bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
3692 bufferInfo.size = readback.byteSize;
3693 bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
3694
3695 VmaAllocationCreateInfo allocInfo = {};
3696 allocInfo.usage = VMA_MEMORY_USAGE_GPU_TO_CPU;
3697
3698 VmaAllocation allocation;
3699 VkResult err = vmaCreateBuffer(toVmaAllocator(allocator), &bufferInfo, &allocInfo, &readback.stagingBuf, &allocation, nullptr);
3700 if (err == VK_SUCCESS) {
3701 readback.stagingAlloc = allocation;
3702 } else {
3703 qWarning("Failed to create readback buffer of size %u: %d", readback.byteSize, err);
3705 continue;
3706 }
3707
3708 trackedBufferBarrier(cbD, bufD, 0, VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
3709
3710 VkBufferCopy copyInfo = {};
3711 copyInfo.srcOffset = u.offset;
3712 copyInfo.size = u.readSize;
3713
3714 QVkCommandBuffer::Command &cmd(cbD->commands.get());
3716 cmd.args.copyBuffer.src = bufD->buffers[0];
3717 cmd.args.copyBuffer.dst = readback.stagingBuf;
3718 cmd.args.copyBuffer.desc = copyInfo;
3719
3720 bufD->lastActiveFrameSlot = currentFrameSlot;
3721
3722 activeBufferReadbacks.append(readback);
3723 }
3724 }
3725 }
3726
3727 for (int opIdx = 0; opIdx < ud->activeTextureOpCount; ++opIdx) {
3728 const QRhiResourceUpdateBatchPrivate::TextureOp &u(ud->textureOps[opIdx]);
3730 QVkTexture *utexD = QRHI_RES(QVkTexture, u.dst);
3731 // batch into a single staging buffer and a single CopyBufferToImage with multiple copyInfos
3732 VkDeviceSize stagingSize = 0;
3733 for (int layer = 0, maxLayer = u.subresDesc.size(); layer < maxLayer; ++layer) {
3734 for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
3735 for (const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level]))
3736 stagingSize += subresUploadByteSize(subresDesc);
3737 }
3738 }
3739
3740 Q_ASSERT(!utexD->stagingBuffers[currentFrameSlot]);
3741 VkBufferCreateInfo bufferInfo = {};
3742 bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
3743 bufferInfo.size = stagingSize;
3744 bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
3745
3746 VmaAllocationCreateInfo allocInfo = {};
3747 allocInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
3748
3749 VmaAllocation allocation;
3750 VkResult err = vmaCreateBuffer(toVmaAllocator(allocator), &bufferInfo, &allocInfo,
3751 &utexD->stagingBuffers[currentFrameSlot], &allocation, nullptr);
3752 if (err != VK_SUCCESS) {
3753 qWarning("Failed to create image staging buffer of size %d: %d", int(stagingSize), err);
3755 continue;
3756 }
3757 utexD->stagingAllocations[currentFrameSlot] = allocation;
3758
3759 BufferImageCopyList copyInfos;
3760 size_t curOfs = 0;
3761 void *mp = nullptr;
3762 VmaAllocation a = toVmaAllocation(utexD->stagingAllocations[currentFrameSlot]);
3763 err = vmaMapMemory(toVmaAllocator(allocator), a, &mp);
3764 if (err != VK_SUCCESS) {
3765 qWarning("Failed to map image data: %d", err);
3766 continue;
3767 }
3768
3769 for (int layer = 0, maxLayer = u.subresDesc.size(); layer < maxLayer; ++layer) {
3770 for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
3771 const QList<QRhiTextureSubresourceUploadDescription> &srd(u.subresDesc[layer][level]);
3772 if (srd.isEmpty())
3773 continue;
3774 for (const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(srd)) {
3776 subresDesc, &curOfs, mp, &copyInfos);
3777 }
3778 }
3779 }
3780 vmaFlushAllocation(toVmaAllocator(allocator), a, 0, stagingSize);
3781 vmaUnmapMemory(toVmaAllocator(allocator), a);
3782
3783 trackedImageBarrier(cbD, utexD, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
3784 VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
3785
3786 QVkCommandBuffer::Command &cmd(cbD->commands.get());
3788 cmd.args.copyBufferToImage.src = utexD->stagingBuffers[currentFrameSlot];
3789 cmd.args.copyBufferToImage.dst = utexD->image;
3790 cmd.args.copyBufferToImage.dstLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
3791 cmd.args.copyBufferToImage.count = copyInfos.size();
3792 cmd.args.copyBufferToImage.bufferImageCopyIndex = cbD->pools.bufferImageCopy.size();
3793 cbD->pools.bufferImageCopy.append(copyInfos.constData(), copyInfos.size());
3794
3795 // no reuse of staging, this is intentional
3799 e.stagingBuffer.stagingBuffer = utexD->stagingBuffers[currentFrameSlot];
3800 e.stagingBuffer.stagingAllocation = utexD->stagingAllocations[currentFrameSlot];
3801 utexD->stagingBuffers[currentFrameSlot] = VK_NULL_HANDLE;
3802 utexD->stagingAllocations[currentFrameSlot] = nullptr;
3803 releaseQueue.append(e);
3804
3805 // Similarly to buffers, transitioning away from DST is done later,
3806 // when a renderpass using the texture is encountered.
3807
3808 utexD->lastActiveFrameSlot = currentFrameSlot;
3810 Q_ASSERT(u.src && u.dst);
3811 if (u.src == u.dst) {
3812 qWarning("Texture copy with matching source and destination is not supported");
3813 continue;
3814 }
3815 QVkTexture *srcD = QRHI_RES(QVkTexture, u.src);
3816 QVkTexture *dstD = QRHI_RES(QVkTexture, u.dst);
3817 const bool srcIs3D = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3818 const bool dstIs3D = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3819
3820 VkImageCopy region = {};
3821 region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
3822 region.srcSubresource.mipLevel = uint32_t(u.desc.sourceLevel());
3823 region.srcSubresource.baseArrayLayer = srcIs3D ? 0 : uint32_t(u.desc.sourceLayer());
3824 region.srcSubresource.layerCount = 1;
3825
3826 region.srcOffset.x = u.desc.sourceTopLeft().x();
3827 region.srcOffset.y = u.desc.sourceTopLeft().y();
3828 if (srcIs3D)
3829 region.srcOffset.z = uint32_t(u.desc.sourceLayer());
3830
3831 region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
3832 region.dstSubresource.mipLevel = uint32_t(u.desc.destinationLevel());
3833 region.dstSubresource.baseArrayLayer = dstIs3D ? 0 : uint32_t(u.desc.destinationLayer());
3834 region.dstSubresource.layerCount = 1;
3835
3836 region.dstOffset.x = u.desc.destinationTopLeft().x();
3837 region.dstOffset.y = u.desc.destinationTopLeft().y();
3838 if (dstIs3D)
3839 region.dstOffset.z = uint32_t(u.desc.destinationLayer());
3840
3841 const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize);
3842 const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize();
3843 region.extent.width = uint32_t(copySize.width());
3844 region.extent.height = uint32_t(copySize.height());
3845 region.extent.depth = 1;
3846
3847 trackedImageBarrier(cbD, srcD, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
3848 VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
3849 trackedImageBarrier(cbD, dstD, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
3850 VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
3851
3852 QVkCommandBuffer::Command &cmd(cbD->commands.get());
3854 cmd.args.copyImage.src = srcD->image;
3855 cmd.args.copyImage.srcLayout = srcD->usageState.layout;
3856 cmd.args.copyImage.dst = dstD->image;
3857 cmd.args.copyImage.dstLayout = dstD->usageState.layout;
3858 cmd.args.copyImage.desc = region;
3859
3860 srcD->lastActiveFrameSlot = dstD->lastActiveFrameSlot = currentFrameSlot;
3862 TextureReadback readback;
3864 readback.desc = u.rb;
3865 readback.result = u.result;
3866
3867 QVkTexture *texD = QRHI_RES(QVkTexture, u.rb.texture());
3868 QVkSwapChain *swapChainD = nullptr;
3869 bool is3D = false;
3870 if (texD) {
3871 if (texD->samples > VK_SAMPLE_COUNT_1_BIT) {
3872 qWarning("Multisample texture cannot be read back");
3873 continue;
3874 }
3875 is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3876 readback.pixelSize = q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize);
3877 readback.format = texD->m_format;
3878 texD->lastActiveFrameSlot = currentFrameSlot;
3879 } else {
3881 swapChainD = QRHI_RES(QVkSwapChain, currentSwapChain);
3882 if (!swapChainD->supportsReadback) {
3883 qWarning("Swapchain does not support readback");
3884 continue;
3885 }
3886 readback.pixelSize = swapChainD->pixelSize;
3887 readback.format = swapchainReadbackTextureFormat(swapChainD->colorFormat, nullptr);
3888 if (readback.format == QRhiTexture::UnknownFormat)
3889 continue;
3890
3891 // Multisample swapchains need nothing special since resolving
3892 // happens when ending a renderpass.
3893 }
3894 textureFormatInfo(readback.format, readback.pixelSize, nullptr, &readback.byteSize, nullptr);
3895
3896 // Create a host visible readback buffer.
3897 VkBufferCreateInfo bufferInfo = {};
3898 bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
3899 bufferInfo.size = readback.byteSize;
3900 bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
3901
3902 VmaAllocationCreateInfo allocInfo = {};
3903 allocInfo.usage = VMA_MEMORY_USAGE_GPU_TO_CPU;
3904
3905 VmaAllocation allocation;
3906 VkResult err = vmaCreateBuffer(toVmaAllocator(allocator), &bufferInfo, &allocInfo, &readback.stagingBuf, &allocation, nullptr);
3907 if (err == VK_SUCCESS) {
3908 readback.stagingAlloc = allocation;
3909 } else {
3910 qWarning("Failed to create readback buffer of size %u: %d", readback.byteSize, err);
3912 continue;
3913 }
3914
3915 // Copy from the (optimal and not host visible) image into the buffer.
3916 VkBufferImageCopy copyDesc = {};
3917 copyDesc.bufferOffset = 0;
3918 copyDesc.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
3919 copyDesc.imageSubresource.mipLevel = uint32_t(u.rb.level());
3920 copyDesc.imageSubresource.baseArrayLayer = is3D ? 0 : uint32_t(u.rb.layer());
3921 copyDesc.imageSubresource.layerCount = 1;
3922 if (is3D)
3923 copyDesc.imageOffset.z = u.rb.layer();
3924 copyDesc.imageExtent.width = uint32_t(readback.pixelSize.width());
3925 copyDesc.imageExtent.height = uint32_t(readback.pixelSize.height());
3926 copyDesc.imageExtent.depth = 1;
3927
3928 if (texD) {
3929 trackedImageBarrier(cbD, texD, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
3930 VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
3931 QVkCommandBuffer::Command &cmd(cbD->commands.get());
3933 cmd.args.copyImageToBuffer.src = texD->image;
3934 cmd.args.copyImageToBuffer.srcLayout = texD->usageState.layout;
3935 cmd.args.copyImageToBuffer.dst = readback.stagingBuf;
3936 cmd.args.copyImageToBuffer.desc = copyDesc;
3937 } else {
3938 // use the swapchain image
3939 QVkSwapChain::ImageResources &imageRes(swapChainD->imageRes[swapChainD->currentImageIndex]);
3940 VkImage image = imageRes.image;
3943 qWarning("Attempted to read back undefined swapchain image content, "
3944 "results are undefined. (do a render pass first)");
3945 }
3947 VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
3948 VK_ACCESS_MEMORY_READ_BIT, VK_ACCESS_TRANSFER_READ_BIT,
3949 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
3950 0, 1,
3951 0, 1);
3953 }
3954
3955 QVkCommandBuffer::Command &cmd(cbD->commands.get());
3957 cmd.args.copyImageToBuffer.src = image;
3958 cmd.args.copyImageToBuffer.srcLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
3959 cmd.args.copyImageToBuffer.dst = readback.stagingBuf;
3960 cmd.args.copyImageToBuffer.desc = copyDesc;
3961 }
3962
3963 activeTextureReadbacks.append(readback);
3965 QVkTexture *utexD = QRHI_RES(QVkTexture, u.dst);
3966 Q_ASSERT(utexD->m_flags.testFlag(QRhiTexture::UsedWithGenerateMips));
3967 const bool isCube = utexD->m_flags.testFlag(QRhiTexture::CubeMap);
3968 const bool isArray = utexD->m_flags.testFlag(QRhiTexture::TextureArray);
3969 const bool is3D = utexD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3970
3971 VkImageLayout origLayout = utexD->usageState.layout;
3972 VkAccessFlags origAccess = utexD->usageState.access;
3973 VkPipelineStageFlags origStage = utexD->usageState.stage;
3974 if (!origStage)
3975 origStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
3976
3977 for (int layer = 0; layer < (isCube ? 6 : (isArray ? qMax(0, utexD->m_arraySize) : 1)); ++layer) {
3978 int w = utexD->m_pixelSize.width();
3979 int h = utexD->m_pixelSize.height();
3980 int depth = is3D ? qMax(1, utexD->m_depth) : 1;
3981 for (int level = 1; level < int(utexD->mipLevelCount); ++level) {
3982 if (level == 1) {
3983 subresourceBarrier(cbD, utexD->image,
3984 origLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
3985 origAccess, VK_ACCESS_TRANSFER_READ_BIT,
3986 origStage, VK_PIPELINE_STAGE_TRANSFER_BIT,
3987 layer, 1,
3988 level - 1, 1);
3989 } else {
3990 subresourceBarrier(cbD, utexD->image,
3991 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
3992 VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
3993 VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
3994 layer, 1,
3995 level - 1, 1);
3996 }
3997
3998 subresourceBarrier(cbD, utexD->image,
3999 origLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
4000 origAccess, VK_ACCESS_TRANSFER_WRITE_BIT,
4001 origStage, VK_PIPELINE_STAGE_TRANSFER_BIT,
4002 layer, 1,
4003 level, 1);
4004
4005 VkImageBlit region = {};
4006 region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
4007 region.srcSubresource.mipLevel = uint32_t(level) - 1;
4008 region.srcSubresource.baseArrayLayer = uint32_t(layer);
4009 region.srcSubresource.layerCount = 1;
4010
4011 region.srcOffsets[1].x = qMax(1, w);
4012 region.srcOffsets[1].y = qMax(1, h);
4013 region.srcOffsets[1].z = qMax(1, depth);
4014
4015 region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
4016 region.dstSubresource.mipLevel = uint32_t(level);
4017 region.dstSubresource.baseArrayLayer = uint32_t(layer);
4018 region.dstSubresource.layerCount = 1;
4019
4020 region.dstOffsets[1].x = qMax(1, w >> 1);
4021 region.dstOffsets[1].y = qMax(1, h >> 1);
4022 region.dstOffsets[1].z = qMax(1, depth >> 1);
4023
4024 QVkCommandBuffer::Command &cmd(cbD->commands.get());
4026 cmd.args.blitImage.src = utexD->image;
4027 cmd.args.blitImage.srcLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
4028 cmd.args.blitImage.dst = utexD->image;
4029 cmd.args.blitImage.dstLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
4030 cmd.args.blitImage.filter = VK_FILTER_LINEAR;
4031 cmd.args.blitImage.desc = region;
4032
4033 w >>= 1;
4034 h >>= 1;
4035 depth >>= 1;
4036 }
4037
4038 if (utexD->mipLevelCount > 1) {
4039 subresourceBarrier(cbD, utexD->image,
4040 VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, origLayout,
4041 VK_ACCESS_TRANSFER_READ_BIT, origAccess,
4042 VK_PIPELINE_STAGE_TRANSFER_BIT, origStage,
4043 layer, 1,
4044 0, int(utexD->mipLevelCount) - 1);
4045 subresourceBarrier(cbD, utexD->image,
4046 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, origLayout,
4047 VK_ACCESS_TRANSFER_WRITE_BIT, origAccess,
4048 VK_PIPELINE_STAGE_TRANSFER_BIT, origStage,
4049 layer, 1,
4050 int(utexD->mipLevelCount) - 1, 1);
4051 }
4052 }
4053 utexD->lastActiveFrameSlot = currentFrameSlot;
4054 }
4055 }
4056
4057 ud->free();
4058}
4059
4061{
4062 if (bufD->pendingDynamicUpdates[slot].isEmpty())
4063 return;
4064
4065 Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
4066 void *p = nullptr;
4067 VmaAllocation a = toVmaAllocation(bufD->allocations[slot]);
4068 // The vmaMap/Unmap are basically a no-op when persistently mapped since it
4069 // refcounts; this is great because we don't need to care if the allocation
4070 // was created as persistently mapped or not.
4071 VkResult err = vmaMapMemory(toVmaAllocator(allocator), a, &p);
4072 if (err != VK_SUCCESS) {
4073 qWarning("Failed to map buffer: %d", err);
4074 return;
4075 }
4076 quint32 changeBegin = UINT32_MAX;
4077 quint32 changeEnd = 0;
4078 for (const QVkBuffer::DynamicUpdate &u : std::as_const(bufD->pendingDynamicUpdates[slot])) {
4079 memcpy(static_cast<char *>(p) + u.offset, u.data.constData(), u.data.size());
4080 if (u.offset < changeBegin)
4081 changeBegin = u.offset;
4082 if (u.offset + u.data.size() > changeEnd)
4083 changeEnd = u.offset + u.data.size();
4084 }
4085 if (changeBegin < UINT32_MAX && changeBegin < changeEnd)
4086 vmaFlushAllocation(toVmaAllocator(allocator), a, changeBegin, changeEnd - changeBegin);
4087 vmaUnmapMemory(toVmaAllocator(allocator), a);
4088
4089 bufD->pendingDynamicUpdates[slot].clear();
4090}
4091
4092static void qrhivk_releaseBuffer(const QRhiVulkan::DeferredReleaseEntry &e, void *allocator)
4093{
4094 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
4095 vmaDestroyBuffer(toVmaAllocator(allocator), e.buffer.buffers[i], toVmaAllocation(e.buffer.allocations[i]));
4096 vmaDestroyBuffer(toVmaAllocator(allocator), e.buffer.stagingBuffers[i], toVmaAllocation(e.buffer.stagingAllocations[i]));
4097 }
4098}
4099
4101{
4102 df->vkDestroyImageView(dev, e.renderBuffer.imageView, nullptr);
4103 df->vkDestroyImage(dev, e.renderBuffer.image, nullptr);
4104 df->vkFreeMemory(dev, e.renderBuffer.memory, nullptr);
4105}
4106
4107static void qrhivk_releaseTexture(const QRhiVulkan::DeferredReleaseEntry &e, VkDevice dev, QVulkanDeviceFunctions *df, void *allocator)
4108{
4109 df->vkDestroyImageView(dev, e.texture.imageView, nullptr);
4110 vmaDestroyImage(toVmaAllocator(allocator), e.texture.image, toVmaAllocation(e.texture.allocation));
4111 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
4112 vmaDestroyBuffer(toVmaAllocator(allocator), e.texture.stagingBuffers[i], toVmaAllocation(e.texture.stagingAllocations[i]));
4113 for (int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i) {
4114 if (e.texture.extraImageViews[i])
4115 df->vkDestroyImageView(dev, e.texture.extraImageViews[i], nullptr);
4116 }
4117}
4118
4120{
4121 df->vkDestroySampler(dev, e.sampler.sampler, nullptr);
4122}
4123
4125{
4126 for (int i = releaseQueue.size() - 1; i >= 0; --i) {
4128 if (forced || currentFrameSlot == e.lastActiveFrameSlot || e.lastActiveFrameSlot < 0) {
4129 switch (e.type) {
4131 df->vkDestroyPipeline(dev, e.pipelineState.pipeline, nullptr);
4132 df->vkDestroyPipelineLayout(dev, e.pipelineState.layout, nullptr);
4133 break;
4135 df->vkDestroyDescriptorSetLayout(dev, e.shaderResourceBindings.layout, nullptr);
4136 if (e.shaderResourceBindings.poolIndex >= 0) {
4137 descriptorPools[e.shaderResourceBindings.poolIndex].refCount -= 1;
4138 Q_ASSERT(descriptorPools[e.shaderResourceBindings.poolIndex].refCount >= 0);
4139 }
4140 break;
4143 break;
4146 break;
4149 break;
4152 break;
4154 df->vkDestroyFramebuffer(dev, e.textureRenderTarget.fb, nullptr);
4155 for (int att = 0; att < QVkRenderTargetData::MAX_COLOR_ATTACHMENTS; ++att) {
4156 df->vkDestroyImageView(dev, e.textureRenderTarget.rtv[att], nullptr);
4157 df->vkDestroyImageView(dev, e.textureRenderTarget.resrtv[att], nullptr);
4158 }
4159 df->vkDestroyImageView(dev, e.textureRenderTarget.dsv, nullptr);
4160 df->vkDestroyImageView(dev, e.textureRenderTarget.resdsv, nullptr);
4161 break;
4163 df->vkDestroyRenderPass(dev, e.renderPass.rp, nullptr);
4164 break;
4166 vmaDestroyBuffer(toVmaAllocator(allocator), e.stagingBuffer.stagingBuffer, toVmaAllocation(e.stagingBuffer.stagingAllocation));
4167 break;
4170 break;
4171 default:
4172 Q_UNREACHABLE();
4173 break;
4174 }
4175 releaseQueue.removeAt(i);
4176 }
4177 }
4178}
4179
4181{
4182 QVarLengthArray<std::function<void()>, 4> completedCallbacks;
4183
4184 for (int i = activeTextureReadbacks.size() - 1; i >= 0; --i) {
4186 if (forced || currentFrameSlot == readback.activeFrameSlot || readback.activeFrameSlot < 0) {
4187 readback.result->format = readback.format;
4188 readback.result->pixelSize = readback.pixelSize;
4189 VmaAllocation a = toVmaAllocation(readback.stagingAlloc);
4190 void *p = nullptr;
4191 VkResult err = vmaMapMemory(toVmaAllocator(allocator), a, &p);
4192 if (err == VK_SUCCESS && p) {
4193 readback.result->data.resize(int(readback.byteSize));
4194 memcpy(readback.result->data.data(), p, readback.byteSize);
4195 vmaUnmapMemory(toVmaAllocator(allocator), a);
4196 } else {
4197 qWarning("Failed to map texture readback buffer of size %u: %d", readback.byteSize, err);
4198 }
4199
4200 vmaDestroyBuffer(toVmaAllocator(allocator), readback.stagingBuf, a);
4201
4202 if (readback.result->completed)
4203 completedCallbacks.append(readback.result->completed);
4204
4205 activeTextureReadbacks.removeLast();
4206 }
4207 }
4208
4209 for (int i = activeBufferReadbacks.size() - 1; i >= 0; --i) {
4211 if (forced || currentFrameSlot == readback.activeFrameSlot || readback.activeFrameSlot < 0) {
4212 VmaAllocation a = toVmaAllocation(readback.stagingAlloc);
4213 void *p = nullptr;
4214 VkResult err = vmaMapMemory(toVmaAllocator(allocator), a, &p);
4215 if (err == VK_SUCCESS && p) {
4216 readback.result->data.resize(readback.byteSize);
4217 memcpy(readback.result->data.data(), p, readback.byteSize);
4218 vmaUnmapMemory(toVmaAllocator(allocator), a);
4219 } else {
4220 qWarning("Failed to map buffer readback buffer of size %d: %d", readback.byteSize, err);
4221 }
4222
4223 vmaDestroyBuffer(toVmaAllocator(allocator), readback.stagingBuf, a);
4224
4225 if (readback.result->completed)
4226 completedCallbacks.append(readback.result->completed);
4227
4228 activeBufferReadbacks.removeLast();
4229 }
4230 }
4231
4232 for (auto f : completedCallbacks)
4233 f();
4234}
4235
4236static struct {
4237 VkSampleCountFlagBits mask;
4239} qvk_sampleCounts[] = {
4240 // keep this sorted by 'count'
4241 { VK_SAMPLE_COUNT_1_BIT, 1 },
4242 { VK_SAMPLE_COUNT_2_BIT, 2 },
4243 { VK_SAMPLE_COUNT_4_BIT, 4 },
4244 { VK_SAMPLE_COUNT_8_BIT, 8 },
4245 { VK_SAMPLE_COUNT_16_BIT, 16 },
4246 { VK_SAMPLE_COUNT_32_BIT, 32 },
4247 { VK_SAMPLE_COUNT_64_BIT, 64 }
4249
4251{
4252 const VkPhysicalDeviceLimits *limits = &physDevProperties.limits;
4253 VkSampleCountFlags color = limits->framebufferColorSampleCounts;
4254 VkSampleCountFlags depth = limits->framebufferDepthSampleCounts;
4255 VkSampleCountFlags stencil = limits->framebufferStencilSampleCounts;
4256 QList<int> result;
4257
4258 for (const auto &qvk_sampleCount : qvk_sampleCounts) {
4259 if ((color & qvk_sampleCount.mask)
4260 && (depth & qvk_sampleCount.mask)
4261 && (stencil & qvk_sampleCount.mask))
4262 {
4263 result.append(qvk_sampleCount.count);
4264 }
4265 }
4266
4267 return result;
4268}
4269
4270VkSampleCountFlagBits QRhiVulkan::effectiveSampleCountBits(int sampleCount)
4271{
4272 const int s = effectiveSampleCount(sampleCount);
4273
4274 for (const auto &qvk_sampleCount : qvk_sampleCounts) {
4275 if (qvk_sampleCount.count == s)
4276 return qvk_sampleCount.mask;
4277 }
4278
4279 Q_UNREACHABLE_RETURN(VK_SAMPLE_COUNT_1_BIT);
4280}
4281
4291
4293{
4295
4296 for (auto it = cbD->commands.begin(), end = cbD->commands.end(); it != end; ++it) {
4298 switch (cmd.cmd) {
4300 df->vkCmdCopyBuffer(cbD->cb, cmd.args.copyBuffer.src, cmd.args.copyBuffer.dst,
4301 1, &cmd.args.copyBuffer.desc);
4302 break;
4304 df->vkCmdCopyBufferToImage(cbD->cb, cmd.args.copyBufferToImage.src, cmd.args.copyBufferToImage.dst,
4305 cmd.args.copyBufferToImage.dstLayout,
4306 uint32_t(cmd.args.copyBufferToImage.count),
4307 cbD->pools.bufferImageCopy.constData() + cmd.args.copyBufferToImage.bufferImageCopyIndex);
4308 break;
4310 df->vkCmdCopyImage(cbD->cb, cmd.args.copyImage.src, cmd.args.copyImage.srcLayout,
4311 cmd.args.copyImage.dst, cmd.args.copyImage.dstLayout,
4312 1, &cmd.args.copyImage.desc);
4313 break;
4315 df->vkCmdCopyImageToBuffer(cbD->cb, cmd.args.copyImageToBuffer.src, cmd.args.copyImageToBuffer.srcLayout,
4316 cmd.args.copyImageToBuffer.dst,
4317 1, &cmd.args.copyImageToBuffer.desc);
4318 break;
4320 df->vkCmdPipelineBarrier(cbD->cb, cmd.args.imageBarrier.srcStageMask, cmd.args.imageBarrier.dstStageMask,
4321 0, 0, nullptr, 0, nullptr,
4322 cmd.args.imageBarrier.count, cbD->pools.imageBarrier.constData() + cmd.args.imageBarrier.index);
4323 break;
4325 df->vkCmdPipelineBarrier(cbD->cb, cmd.args.bufferBarrier.srcStageMask, cmd.args.bufferBarrier.dstStageMask,
4326 0, 0, nullptr,
4327 cmd.args.bufferBarrier.count, cbD->pools.bufferBarrier.constData() + cmd.args.bufferBarrier.index,
4328 0, nullptr);
4329 break;
4331 df->vkCmdBlitImage(cbD->cb, cmd.args.blitImage.src, cmd.args.blitImage.srcLayout,
4332 cmd.args.blitImage.dst, cmd.args.blitImage.dstLayout,
4333 1, &cmd.args.blitImage.desc,
4334 cmd.args.blitImage.filter);
4335 break;
4337 cmd.args.beginRenderPass.desc.pClearValues = cbD->pools.clearValue.constData() + cmd.args.beginRenderPass.clearValueIndex;
4338 df->vkCmdBeginRenderPass(cbD->cb, &cmd.args.beginRenderPass.desc,
4339 cmd.args.beginRenderPass.useSecondaryCb ? VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS
4340 : VK_SUBPASS_CONTENTS_INLINE);
4341 break;
4343 df->vkCmdEndRenderPass(cbD->cb);
4344 break;
4346 df->vkCmdBindPipeline(cbD->cb, cmd.args.bindPipeline.bindPoint, cmd.args.bindPipeline.pipeline);
4347 break;
4349 {
4350 const uint32_t *offsets = nullptr;
4351 if (cmd.args.bindDescriptorSet.dynamicOffsetCount > 0)
4352 offsets = cbD->pools.dynamicOffset.constData() + cmd.args.bindDescriptorSet.dynamicOffsetIndex;
4353 df->vkCmdBindDescriptorSets(cbD->cb, cmd.args.bindDescriptorSet.bindPoint,
4354 cmd.args.bindDescriptorSet.pipelineLayout,
4355 0, 1, &cmd.args.bindDescriptorSet.descSet,
4356 uint32_t(cmd.args.bindDescriptorSet.dynamicOffsetCount),
4357 offsets);
4358 }
4359 break;
4361 df->vkCmdBindVertexBuffers(cbD->cb, uint32_t(cmd.args.bindVertexBuffer.startBinding),
4362 uint32_t(cmd.args.bindVertexBuffer.count),
4363 cbD->pools.vertexBuffer.constData() + cmd.args.bindVertexBuffer.vertexBufferIndex,
4364 cbD->pools.vertexBufferOffset.constData() + cmd.args.bindVertexBuffer.vertexBufferOffsetIndex);
4365 break;
4367 df->vkCmdBindIndexBuffer(cbD->cb, cmd.args.bindIndexBuffer.buf,
4368 cmd.args.bindIndexBuffer.ofs, cmd.args.bindIndexBuffer.type);
4369 break;
4371 df->vkCmdSetViewport(cbD->cb, 0, 1, &cmd.args.setViewport.viewport);
4372 break;
4374 df->vkCmdSetScissor(cbD->cb, 0, 1, &cmd.args.setScissor.scissor);
4375 break;
4377 df->vkCmdSetBlendConstants(cbD->cb, cmd.args.setBlendConstants.c);
4378 break;
4380 df->vkCmdSetStencilReference(cbD->cb, VK_STENCIL_FRONT_AND_BACK, cmd.args.setStencilRef.ref);
4381 break;
4383 df->vkCmdDraw(cbD->cb, cmd.args.draw.vertexCount, cmd.args.draw.instanceCount,
4384 cmd.args.draw.firstVertex, cmd.args.draw.firstInstance);
4385 break;
4387 df->vkCmdDrawIndexed(cbD->cb, cmd.args.drawIndexed.indexCount, cmd.args.drawIndexed.instanceCount,
4388 cmd.args.drawIndexed.firstIndex, cmd.args.drawIndexed.vertexOffset,
4389 cmd.args.drawIndexed.firstInstance);
4390 break;
4392#ifdef VK_EXT_debug_utils
4393 cmd.args.debugMarkerBegin.label.pLabelName =
4394 cbD->pools.debugMarkerData[cmd.args.debugMarkerBegin.labelNameIndex].constData();
4395 vkCmdBeginDebugUtilsLabelEXT(cbD->cb, &cmd.args.debugMarkerBegin.label);
4396#endif
4397 break;
4399#ifdef VK_EXT_debug_utils
4400 vkCmdEndDebugUtilsLabelEXT(cbD->cb);
4401#endif
4402 break;
4404#ifdef VK_EXT_debug_utils
4405 cmd.args.debugMarkerInsert.label.pLabelName =
4406 cbD->pools.debugMarkerData[cmd.args.debugMarkerInsert.labelNameIndex].constData();
4407 vkCmdInsertDebugUtilsLabelEXT(cbD->cb, &cmd.args.debugMarkerInsert.label);
4408#endif
4409 break;
4411 recordTransitionPassResources(cbD, cbD->passResTrackers[cmd.args.transitionResources.trackerIndex]);
4412 break;
4414 df->vkCmdDispatch(cbD->cb, uint32_t(cmd.args.dispatch.x), uint32_t(cmd.args.dispatch.y), uint32_t(cmd.args.dispatch.z));
4415 break;
4417 df->vkCmdExecuteCommands(cbD->cb, 1, &cmd.args.executeSecondary.cb);
4418 break;
4419 default:
4420 break;
4421 }
4422 }
4423}
4424
4426{
4427 switch (access) {
4429 return VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT;
4431 return VK_ACCESS_INDEX_READ_BIT;
4433 return VK_ACCESS_UNIFORM_READ_BIT;
4435 return VK_ACCESS_SHADER_READ_BIT;
4437 return VK_ACCESS_SHADER_WRITE_BIT;
4439 return VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
4440 default:
4441 Q_UNREACHABLE();
4442 break;
4443 }
4444 return 0;
4445}
4446
4447static inline VkPipelineStageFlags toVkPipelineStage(QRhiPassResourceTracker::BufferStage stage)
4448{
4449 switch (stage) {
4451 return VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
4453 return VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
4455 return VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT;
4457 return VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT;
4459 return VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
4461 return VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
4463 return VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT;
4464 default:
4465 Q_UNREACHABLE();
4466 break;
4467 }
4468 return 0;
4469}
4470
4472{
4474 u.access = VkAccessFlags(usage.access);
4475 u.stage = VkPipelineStageFlags(usage.stage);
4476 return u;
4477}
4478
4480{
4481 switch (access) {
4483 return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
4485 return VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
4487 return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
4491 return VK_IMAGE_LAYOUT_GENERAL;
4492 default:
4493 Q_UNREACHABLE();
4494 break;
4495 }
4496 return VK_IMAGE_LAYOUT_GENERAL;
4497}
4498
4500{
4501 switch (access) {
4503 return VK_ACCESS_SHADER_READ_BIT;
4505 return VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
4507 return VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
4509 return VK_ACCESS_SHADER_READ_BIT;
4511 return VK_ACCESS_SHADER_WRITE_BIT;
4513 return VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
4514 default:
4515 Q_UNREACHABLE();
4516 break;
4517 }
4518 return 0;
4519}
4520
4521static inline VkPipelineStageFlags toVkPipelineStage(QRhiPassResourceTracker::TextureStage stage)
4522{
4523 switch (stage) {
4525 return VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
4527 return VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT;
4529 return VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT;
4531 return VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
4533 return VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
4535 return VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
4537 return VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
4539 return VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT;
4540 default:
4541 Q_UNREACHABLE();
4542 break;
4543 }
4544 return 0;
4545}
4546
4548{
4550 u.layout = VkImageLayout(usage.layout);
4551 u.access = VkAccessFlags(usage.access);
4552 u.stage = VkPipelineStageFlags(usage.stage);
4553 return u;
4554}
4555
4557 QVkBuffer *bufD,
4558 int slot,
4561{
4562 QVkBuffer::UsageState &u(bufD->usageState[slot]);
4563 const VkAccessFlags newAccess = toVkAccess(access);
4564 const VkPipelineStageFlags newStage = toVkPipelineStage(stage);
4565 if (u.access == newAccess && u.stage == newStage) {
4566 if (!accessIsWrite(access))
4567 return;
4568 }
4569 passResTracker->registerBuffer(bufD, slot, &access, &stage, toPassTrackerUsageState(u));
4570 u.access = newAccess;
4571 u.stage = newStage;
4572}
4573
4575 QVkTexture *texD,
4578{
4579 QVkTexture::UsageState &u(texD->usageState);
4580 const VkAccessFlags newAccess = toVkAccess(access);
4581 const VkPipelineStageFlags newStage = toVkPipelineStage(stage);
4582 const VkImageLayout newLayout = toVkLayout(access);
4583 if (u.access == newAccess && u.stage == newStage && u.layout == newLayout) {
4584 if (!accessIsWrite(access))
4585 return;
4586 }
4587 passResTracker->registerTexture(texD, &access, &stage, toPassTrackerUsageState(u));
4588 u.layout = newLayout;
4589 u.access = newAccess;
4590 u.stage = newStage;
4591}
4592
4594{
4595 if (tracker.isEmpty())
4596 return;
4597
4598 for (auto it = tracker.cbeginBuffers(), itEnd = tracker.cendBuffers(); it != itEnd; ++it) {
4599 QVkBuffer *bufD = QRHI_RES(QVkBuffer, it.key());
4600 VkAccessFlags access = toVkAccess(it->access);
4601 VkPipelineStageFlags stage = toVkPipelineStage(it->stage);
4602 QVkBuffer::UsageState s = toVkBufferUsageState(it->stateAtPassBegin);
4603 if (!s.stage)
4604 continue;
4605 if (s.access == access && s.stage == stage) {
4606 if (!accessIsWrite(access))
4607 continue;
4608 }
4609 VkBufferMemoryBarrier bufMemBarrier = {};
4610 bufMemBarrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
4611 bufMemBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
4612 bufMemBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
4613 bufMemBarrier.srcAccessMask = s.access;
4614 bufMemBarrier.dstAccessMask = access;
4615 bufMemBarrier.buffer = bufD->buffers[it->slot];
4616 bufMemBarrier.size = VK_WHOLE_SIZE;
4617 df->vkCmdPipelineBarrier(cbD->cb, s.stage, stage, 0,
4618 0, nullptr,
4619 1, &bufMemBarrier,
4620 0, nullptr);
4621 }
4622
4623 for (auto it = tracker.cbeginTextures(), itEnd = tracker.cendTextures(); it != itEnd; ++it) {
4624 QVkTexture *texD = QRHI_RES(QVkTexture, it.key());
4625 VkImageLayout layout = toVkLayout(it->access);
4626 VkAccessFlags access = toVkAccess(it->access);
4627 VkPipelineStageFlags stage = toVkPipelineStage(it->stage);
4628 QVkTexture::UsageState s = toVkTextureUsageState(it->stateAtPassBegin);
4629 if (s.access == access && s.stage == stage && s.layout == layout) {
4630 if (!accessIsWrite(access))
4631 continue;
4632 }
4633 VkImageMemoryBarrier barrier = {};
4634 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
4635 barrier.subresourceRange.aspectMask = aspectMaskForTextureFormat(texD->m_format);
4636 barrier.subresourceRange.baseMipLevel = 0;
4637 barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
4638 barrier.subresourceRange.baseArrayLayer = 0;
4639 barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
4640 barrier.oldLayout = s.layout; // new textures have this set to PREINITIALIZED
4641 barrier.newLayout = layout;
4642 barrier.srcAccessMask = s.access; // may be 0 but that's fine
4643 barrier.dstAccessMask = access;
4644 barrier.image = texD->image;
4645 VkPipelineStageFlags srcStage = s.stage;
4646 // stage mask cannot be 0
4647 if (!srcStage)
4648 srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
4649 df->vkCmdPipelineBarrier(cbD->cb, srcStage, stage, 0,
4650 0, nullptr,
4651 0, nullptr,
4652 1, &barrier);
4653 }
4654}
4655
4657{
4661 {
4662 qWarning("Physical device surface queries not available");
4663 return nullptr;
4664 }
4665
4666 return new QVkSwapChain(this);
4667}
4668
4670{
4671 return new QVkBuffer(this, type, usage, size);
4672}
4673
4675{
4676 return int(ubufAlign); // typically 256 (bytes)
4677}
4678
4680{
4681 return false;
4682}
4683
4685{
4686 return false;
4687}
4688
4690{
4691 return true;
4692}
4693
4695{
4696 // See https://matthewwellings.com/blog/the-new-vulkan-coordinate-system/
4697
4698 static QMatrix4x4 m;
4699 if (m.isIdentity()) {
4700 // NB the ctor takes row-major
4701 m = QMatrix4x4(1.0f, 0.0f, 0.0f, 0.0f,
4702 0.0f, -1.0f, 0.0f, 0.0f,
4703 0.0f, 0.0f, 0.5f, 0.5f,
4704 0.0f, 0.0f, 0.0f, 1.0f);
4705 }
4706 return m;
4707}
4708
4710{
4711 // Note that with some SDKs the validation layer gives an odd warning about
4712 // BC not being supported, even when our check here succeeds. Not much we
4713 // can do about that.
4715 if (!physDevFeatures.textureCompressionBC)
4716 return false;
4717 }
4718
4720 if (!physDevFeatures.textureCompressionETC2)
4721 return false;
4722 }
4723
4725 if (!physDevFeatures.textureCompressionASTC_LDR)
4726 return false;
4727 }
4728
4729 VkFormat vkformat = toVkTextureFormat(format, flags);
4730 VkFormatProperties props;
4731 f->vkGetPhysicalDeviceFormatProperties(physDev, vkformat, &props);
4732 return (props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) != 0;
4733}
4734
4736{
4737 switch (feature) {
4739 return true;
4741 return true;
4742 case QRhi::DebugMarkers:
4743 return caps.debugUtils;
4744 case QRhi::Timestamps:
4745 return timestampValidBits != 0;
4746 case QRhi::Instancing:
4747 return true;
4749 return caps.vertexAttribDivisor;
4751 return true;
4753 return true;
4755 return true;
4757 return true;
4759 return true;
4761 return true;
4762 case QRhi::Compute:
4763 return caps.compute;
4764 case QRhi::WideLines:
4765 return caps.wideLines;
4767 return true;
4768 case QRhi::BaseVertex:
4769 return true;
4770 case QRhi::BaseInstance:
4771 return true;
4773 return true;
4775 return true;
4777 return true;
4778 case QRhi::TexelFetch:
4779 return true;
4781 return true;
4783 return true;
4785 return true;
4787 return true;
4789 return true;
4791 return true;
4793 return false;
4795 return true;
4797 return caps.texture3DSliceAs2D;
4799 return true;
4800 case QRhi::Tessellation:
4801 return caps.tessellation;
4803 return caps.geometryShader;
4805 return true;
4807 return caps.nonFillPolygonMode;
4809 return true;
4811 return true;
4813 return true;
4815 return true;
4817 return true;
4818 case QRhi::MultiView:
4819 return caps.multiView;
4821 return true;
4823 return caps.renderPass2KHR && caps.depthStencilResolveKHR;
4824 default:
4825 Q_UNREACHABLE_RETURN(false);
4826 }
4827}
4828
4830{
4831 switch (limit) {
4833 return 1;
4835 return int(physDevProperties.limits.maxImageDimension2D);
4837 return int(physDevProperties.limits.maxColorAttachments);
4839 return QVK_FRAMES_IN_FLIGHT;
4841 return QVK_FRAMES_IN_FLIGHT;
4843 return int(qMin(physDevProperties.limits.maxComputeWorkGroupCount[0],
4844 qMin(physDevProperties.limits.maxComputeWorkGroupCount[1],
4845 physDevProperties.limits.maxComputeWorkGroupCount[2])));
4847 return int(physDevProperties.limits.maxComputeWorkGroupInvocations);
4849 return int(physDevProperties.limits.maxComputeWorkGroupSize[0]);
4851 return int(physDevProperties.limits.maxComputeWorkGroupSize[1]);
4853 return int(physDevProperties.limits.maxComputeWorkGroupSize[2]);
4855 return int(physDevProperties.limits.maxImageArrayLayers);
4857 return int(qMin<uint32_t>(INT_MAX, physDevProperties.limits.maxUniformBufferRange));
4859 return physDevProperties.limits.maxVertexInputAttributes;
4861 return physDevProperties.limits.maxVertexOutputComponents / 4;
4862 default:
4863 Q_UNREACHABLE_RETURN(0);
4864 }
4865}
4866
4871
4876
4878{
4881
4882 VmaBudget budgets[VK_MAX_MEMORY_HEAPS];
4883 vmaGetHeapBudgets(toVmaAllocator(allocator), budgets);
4884
4885 uint32_t count = toVmaAllocator(allocator)->GetMemoryHeapCount();
4886 for (uint32_t i = 0; i < count; ++i) {
4887 const VmaStatistics &stats(budgets[i].statistics);
4888 result.blockCount += stats.blockCount;
4889 result.allocCount += stats.allocationCount;
4890 result.usedBytes += stats.allocationBytes;
4891 result.unusedBytes += stats.blockBytes - stats.allocationBytes;
4892 }
4893
4894 return result;
4895}
4896
4898{
4899 // not applicable
4900 return false;
4901}
4902
4907
4909{
4910 return deviceLost;
4911}
4912
4924
4926{
4928
4931 return data;
4932
4933 size_t dataSize = 0;
4934 VkResult err = df->vkGetPipelineCacheData(dev, pipelineCache, &dataSize, nullptr);
4935 if (err != VK_SUCCESS) {
4936 qCDebug(QRHI_LOG_INFO, "Failed to get pipeline cache data size: %d", err);
4937 return QByteArray();
4938 }
4939 const size_t headerSize = sizeof(QVkPipelineCacheDataHeader);
4940 const size_t dataOffset = headerSize + VK_UUID_SIZE;
4941 data.resize(dataOffset + dataSize);
4942 err = df->vkGetPipelineCacheData(dev, pipelineCache, &dataSize, data.data() + dataOffset);
4943 if (err != VK_SUCCESS) {
4944 qCDebug(QRHI_LOG_INFO, "Failed to get pipeline cache data of %d bytes: %d", int(dataSize), err);
4945 return QByteArray();
4946 }
4947
4949 header.rhiId = pipelineCacheRhiId();
4950 header.arch = quint32(sizeof(void*));
4951 header.driverVersion = physDevProperties.driverVersion;
4952 header.vendorId = physDevProperties.vendorID;
4953 header.deviceId = physDevProperties.deviceID;
4954 header.dataSize = quint32(dataSize);
4955 header.uuidSize = VK_UUID_SIZE;
4956 header.reserved = 0;
4957 memcpy(data.data(), &header, headerSize);
4958 memcpy(data.data() + headerSize, physDevProperties.pipelineCacheUUID, VK_UUID_SIZE);
4959
4960 return data;
4961}
4962
4964{
4965 if (data.isEmpty())
4966 return;
4967
4968 const size_t headerSize = sizeof(QVkPipelineCacheDataHeader);
4969 if (data.size() < qsizetype(headerSize)) {
4970 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob size");
4971 return;
4972 }
4974 memcpy(&header, data.constData(), headerSize);
4975
4976 const quint32 rhiId = pipelineCacheRhiId();
4977 if (header.rhiId != rhiId) {
4978 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: The data is for a different QRhi version or backend (%u, %u)",
4979 rhiId, header.rhiId);
4980 return;
4981 }
4982 const quint32 arch = quint32(sizeof(void*));
4983 if (header.arch != arch) {
4984 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Architecture does not match (%u, %u)",
4985 arch, header.arch);
4986 return;
4987 }
4988 if (header.driverVersion != physDevProperties.driverVersion) {
4989 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: driverVersion does not match (%u, %u)",
4990 physDevProperties.driverVersion, header.driverVersion);
4991 return;
4992 }
4993 if (header.vendorId != physDevProperties.vendorID) {
4994 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: vendorID does not match (%u, %u)",
4995 physDevProperties.vendorID, header.vendorId);
4996 return;
4997 }
4998 if (header.deviceId != physDevProperties.deviceID) {
4999 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: deviceID does not match (%u, %u)",
5000 physDevProperties.deviceID, header.deviceId);
5001 return;
5002 }
5003 if (header.uuidSize != VK_UUID_SIZE) {
5004 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: VK_UUID_SIZE does not match (%u, %u)",
5005 quint32(VK_UUID_SIZE), header.uuidSize);
5006 return;
5007 }
5008
5009 if (data.size() < qsizetype(headerSize + VK_UUID_SIZE)) {
5010 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob, no uuid");
5011 return;
5012 }
5013 if (memcmp(data.constData() + headerSize, physDevProperties.pipelineCacheUUID, VK_UUID_SIZE)) {
5014 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: pipelineCacheUUID does not match");
5015 return;
5016 }
5017
5018 const size_t dataOffset = headerSize + VK_UUID_SIZE;
5019 if (data.size() < qsizetype(dataOffset + header.dataSize)) {
5020 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob, data missing");
5021 return;
5022 }
5023
5024 if (pipelineCache) {
5025 df->vkDestroyPipelineCache(dev, pipelineCache, nullptr);
5026 pipelineCache = VK_NULL_HANDLE;
5027 }
5028
5029 if (ensurePipelineCache(data.constData() + dataOffset, header.dataSize)) {
5030 qCDebug(QRHI_LOG_INFO, "Created pipeline cache with initial data of %d bytes",
5031 int(header.dataSize));
5032 } else {
5033 qCDebug(QRHI_LOG_INFO, "Failed to create pipeline cache with initial data specified");
5034 }
5035}
5036
5038 int sampleCount, QRhiRenderBuffer::Flags flags,
5039 QRhiTexture::Format backingFormatHint)
5040{
5041 return new QVkRenderBuffer(this, type, pixelSize, sampleCount, flags, backingFormatHint);
5042}
5043
5045 const QSize &pixelSize, int depth, int arraySize,
5046 int sampleCount, QRhiTexture::Flags flags)
5047{
5048 return new QVkTexture(this, format, pixelSize, depth, arraySize, sampleCount, flags);
5049}
5050
5052 QRhiSampler::Filter mipmapMode,
5054{
5055 return new QVkSampler(this, magFilter, minFilter, mipmapMode, u, v, w);
5056}
5057
5059 QRhiTextureRenderTarget::Flags flags)
5060{
5061 return new QVkTextureRenderTarget(this, desc, flags);
5062}
5063
5068
5073
5078
5080{
5082 Q_ASSERT(psD->pipeline);
5085
5086 if (cbD->currentGraphicsPipeline != ps || cbD->currentPipelineGeneration != psD->generation) {
5087 if (cbD->passUsesSecondaryCb) {
5088 df->vkCmdBindPipeline(cbD->activeSecondaryCbStack.last(), VK_PIPELINE_BIND_POINT_GRAPHICS, psD->pipeline);
5089 } else {
5090 QVkCommandBuffer::Command &cmd(cbD->commands.get());
5092 cmd.args.bindPipeline.bindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
5093 cmd.args.bindPipeline.pipeline = psD->pipeline;
5094 }
5095
5096 cbD->currentGraphicsPipeline = ps;
5097 cbD->currentComputePipeline = nullptr;
5098 cbD->currentPipelineGeneration = psD->generation;
5099 }
5100
5101 psD->lastActiveFrameSlot = currentFrameSlot;
5102}
5103
5105 int dynamicOffsetCount,
5106 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
5107{
5113
5114 if (!srb) {
5115 if (gfxPsD)
5116 srb = gfxPsD->m_shaderResourceBindings;
5117 else
5118 srb = compPsD->m_shaderResourceBindings;
5119 }
5120
5122 const int descSetIdx = srbD->hasSlottedResource ? currentFrameSlot : 0;
5123 auto &descSetBd(srbD->boundResourceData[descSetIdx]);
5124 bool rewriteDescSet = false;
5125
5126 // Do host writes and mark referenced shader resources as in-use.
5127 // Also prepare to ensure the descriptor set we are going to bind refers to up-to-date Vk objects.
5128 for (int i = 0, ie = srbD->sortedBindings.size(); i != ie; ++i) {
5129 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->sortedBindings[i]);
5131 switch (b->type) {
5133 {
5134 QVkBuffer *bufD = QRHI_RES(QVkBuffer, b->u.ubuf.buf);
5135 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer));
5136
5137 if (bufD->m_type == QRhiBuffer::Dynamic)
5139
5140 bufD->lastActiveFrameSlot = currentFrameSlot;
5141 trackedRegisterBuffer(&passResTracker, bufD, bufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0,
5144
5145 // Check both the "local" id (the generation counter) and the
5146 // global id. The latter is relevant when a newly allocated
5147 // QRhiResource ends up with the same pointer as a previous one.
5148 // (and that previous one could have been in an srb...)
5149 if (bufD->generation != bd.ubuf.generation || bufD->m_id != bd.ubuf.id) {
5150 rewriteDescSet = true;
5151 bd.ubuf.id = bufD->m_id;
5152 bd.ubuf.generation = bufD->generation;
5153 }
5154 }
5155 break;
5159 {
5161 if (bd.stex.count != data->count) {
5162 bd.stex.count = data->count;
5163 rewriteDescSet = true;
5164 }
5165 for (int elem = 0; elem < data->count; ++elem) {
5166 QVkTexture *texD = QRHI_RES(QVkTexture, data->texSamplers[elem].tex);
5167 QVkSampler *samplerD = QRHI_RES(QVkSampler, data->texSamplers[elem].sampler);
5168 // We use the same code path for both combined and separate
5169 // images and samplers, so tex or sampler (but not both) can be
5170 // null here.
5171 Q_ASSERT(texD || samplerD);
5172 if (texD) {
5173 texD->lastActiveFrameSlot = currentFrameSlot;
5174 trackedRegisterTexture(&passResTracker, texD,
5177 }
5178 if (samplerD)
5179 samplerD->lastActiveFrameSlot = currentFrameSlot;
5180 const quint64 texId = texD ? texD->m_id : 0;
5181 const uint texGen = texD ? texD->generation : 0;
5182 const quint64 samplerId = samplerD ? samplerD->m_id : 0;
5183 const uint samplerGen = samplerD ? samplerD->generation : 0;
5184 if (texGen != bd.stex.d[elem].texGeneration
5185 || texId != bd.stex.d[elem].texId
5186 || samplerGen != bd.stex.d[elem].samplerGeneration
5187 || samplerId != bd.stex.d[elem].samplerId)
5188 {
5189 rewriteDescSet = true;
5190 bd.stex.d[elem].texId = texId;
5191 bd.stex.d[elem].texGeneration = texGen;
5192 bd.stex.d[elem].samplerId = samplerId;
5193 bd.stex.d[elem].samplerGeneration = samplerGen;
5194 }
5195 }
5196 }
5197 break;
5201 {
5202 QVkTexture *texD = QRHI_RES(QVkTexture, b->u.simage.tex);
5203 Q_ASSERT(texD->m_flags.testFlag(QRhiTexture::UsedWithLoadStore));
5204 texD->lastActiveFrameSlot = currentFrameSlot;
5208 else if (b->type == QRhiShaderResourceBinding::ImageStore)
5210 else
5212 trackedRegisterTexture(&passResTracker, texD,
5213 access,
5215
5216 if (texD->generation != bd.simage.generation || texD->m_id != bd.simage.id) {
5217 rewriteDescSet = true;
5218 bd.simage.id = texD->m_id;
5219 bd.simage.generation = texD->generation;
5220 }
5221 }
5222 break;
5226 {
5227 QVkBuffer *bufD = QRHI_RES(QVkBuffer, b->u.sbuf.buf);
5228 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::StorageBuffer));
5229
5230 if (bufD->m_type == QRhiBuffer::Dynamic)
5232
5233 bufD->lastActiveFrameSlot = currentFrameSlot;
5237 else if (b->type == QRhiShaderResourceBinding::BufferStore)
5239 else
5241 trackedRegisterBuffer(&passResTracker, bufD, bufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0,
5242 access,
5244
5245 if (bufD->generation != bd.sbuf.generation || bufD->m_id != bd.sbuf.id) {
5246 rewriteDescSet = true;
5247 bd.sbuf.id = bufD->m_id;
5248 bd.sbuf.generation = bufD->generation;
5249 }
5250 }
5251 break;
5252 default:
5253 Q_UNREACHABLE();
5254 break;
5255 }
5256 }
5257
5258 // write descriptor sets, if needed
5259 if (rewriteDescSet)
5260 updateShaderResourceBindings(srb, descSetIdx);
5261
5262 // make sure the descriptors for the correct slot will get bound.
5263 // also, dynamic offsets always need a bind.
5264 const bool forceRebind = (srbD->hasSlottedResource && cbD->currentDescSetSlot != descSetIdx) || srbD->hasDynamicOffset;
5265
5266 const bool srbChanged = gfxPsD ? (cbD->currentGraphicsSrb != srb) : (cbD->currentComputeSrb != srb);
5267
5268 if (forceRebind || rewriteDescSet || srbChanged || cbD->currentSrbGeneration != srbD->generation) {
5269 QVarLengthArray<uint32_t, 4> dynOfs;
5270 if (srbD->hasDynamicOffset) {
5271 // Filling out dynOfs based on the sorted bindings is important
5272 // because dynOfs has to be ordered based on the binding numbers,
5273 // and neither srb nor dynamicOffsets has any such ordering
5274 // requirement.
5275 for (const QRhiShaderResourceBinding &binding : std::as_const(srbD->sortedBindings)) {
5277 if (b->type == QRhiShaderResourceBinding::UniformBuffer && b->u.ubuf.hasDynamicOffset) {
5278 uint32_t offset = 0;
5279 for (int i = 0; i < dynamicOffsetCount; ++i) {
5280 const QRhiCommandBuffer::DynamicOffset &bindingOffsetPair(dynamicOffsets[i]);
5281 if (bindingOffsetPair.first == b->binding) {
5282 offset = bindingOffsetPair.second;
5283 break;
5284 }
5285 }
5286 dynOfs.append(offset); // use 0 if dynamicOffsets did not contain this binding
5287 }
5288 }
5289 }
5290
5291 if (cbD->passUsesSecondaryCb) {
5292 df->vkCmdBindDescriptorSets(cbD->activeSecondaryCbStack.last(),
5293 gfxPsD ? VK_PIPELINE_BIND_POINT_GRAPHICS : VK_PIPELINE_BIND_POINT_COMPUTE,
5294 gfxPsD ? gfxPsD->layout : compPsD->layout,
5295 0, 1, &srbD->descSets[descSetIdx],
5296 uint32_t(dynOfs.size()),
5297 dynOfs.size() ? dynOfs.constData() : nullptr);
5298 } else {
5299 QVkCommandBuffer::Command &cmd(cbD->commands.get());
5301 cmd.args.bindDescriptorSet.bindPoint = gfxPsD ? VK_PIPELINE_BIND_POINT_GRAPHICS
5302 : VK_PIPELINE_BIND_POINT_COMPUTE;
5303 cmd.args.bindDescriptorSet.pipelineLayout = gfxPsD ? gfxPsD->layout : compPsD->layout;
5304 cmd.args.bindDescriptorSet.descSet = srbD->descSets[descSetIdx];
5305 cmd.args.bindDescriptorSet.dynamicOffsetCount = dynOfs.size();
5306 cmd.args.bindDescriptorSet.dynamicOffsetIndex = cbD->pools.dynamicOffset.size();
5307 cbD->pools.dynamicOffset.append(dynOfs.constData(), dynOfs.size());
5308 }
5309
5310 if (gfxPsD) {
5311 cbD->currentGraphicsSrb = srb;
5312 cbD->currentComputeSrb = nullptr;
5313 } else {
5314 cbD->currentGraphicsSrb = nullptr;
5315 cbD->currentComputeSrb = srb;
5316 }
5317 cbD->currentSrbGeneration = srbD->generation;
5318 cbD->currentDescSetSlot = descSetIdx;
5319 }
5320
5321 srbD->lastActiveFrameSlot = currentFrameSlot;
5322}
5323
5325 int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
5326 QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat)
5327{
5331
5332 bool needsBindVBuf = false;
5333 for (int i = 0; i < bindingCount; ++i) {
5334 const int inputSlot = startBinding + i;
5335 QVkBuffer *bufD = QRHI_RES(QVkBuffer, bindings[i].first);
5336 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::VertexBuffer));
5337 bufD->lastActiveFrameSlot = currentFrameSlot;
5338 if (bufD->m_type == QRhiBuffer::Dynamic)
5340
5341 const VkBuffer vkvertexbuf = bufD->buffers[bufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0];
5342 if (cbD->currentVertexBuffers[inputSlot] != vkvertexbuf
5343 || cbD->currentVertexOffsets[inputSlot] != bindings[i].second)
5344 {
5345 needsBindVBuf = true;
5346 cbD->currentVertexBuffers[inputSlot] = vkvertexbuf;
5347 cbD->currentVertexOffsets[inputSlot] = bindings[i].second;
5348 }
5349 }
5350
5351 if (needsBindVBuf) {
5352 QVarLengthArray<VkBuffer, 4> bufs;
5353 QVarLengthArray<VkDeviceSize, 4> ofs;
5354 for (int i = 0; i < bindingCount; ++i) {
5355 QVkBuffer *bufD = QRHI_RES(QVkBuffer, bindings[i].first);
5356 const int slot = bufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0;
5357 bufs.append(bufD->buffers[slot]);
5358 ofs.append(bindings[i].second);
5359 trackedRegisterBuffer(&passResTracker, bufD, slot,
5362 }
5363
5364 if (cbD->passUsesSecondaryCb) {
5365 df->vkCmdBindVertexBuffers(cbD->activeSecondaryCbStack.last(), uint32_t(startBinding),
5366 uint32_t(bufs.size()), bufs.constData(), ofs.constData());
5367 } else {
5368 QVkCommandBuffer::Command &cmd(cbD->commands.get());
5370 cmd.args.bindVertexBuffer.startBinding = startBinding;
5371 cmd.args.bindVertexBuffer.count = bufs.size();
5372 cmd.args.bindVertexBuffer.vertexBufferIndex = cbD->pools.vertexBuffer.size();
5373 cbD->pools.vertexBuffer.append(bufs.constData(), bufs.size());
5374 cmd.args.bindVertexBuffer.vertexBufferOffsetIndex = cbD->pools.vertexBufferOffset.size();
5375 cbD->pools.vertexBufferOffset.append(ofs.constData(), ofs.size());
5376 }
5377 }
5378
5379 if (indexBuf) {
5380 QVkBuffer *ibufD = QRHI_RES(QVkBuffer, indexBuf);
5381 Q_ASSERT(ibufD->m_usage.testFlag(QRhiBuffer::IndexBuffer));
5382 ibufD->lastActiveFrameSlot = currentFrameSlot;
5383 if (ibufD->m_type == QRhiBuffer::Dynamic)
5385
5386 const int slot = ibufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0;
5387 const VkBuffer vkindexbuf = ibufD->buffers[slot];
5388 const VkIndexType type = indexFormat == QRhiCommandBuffer::IndexUInt16 ? VK_INDEX_TYPE_UINT16
5389 : VK_INDEX_TYPE_UINT32;
5390
5391 if (cbD->currentIndexBuffer != vkindexbuf
5392 || cbD->currentIndexOffset != indexOffset
5393 || cbD->currentIndexFormat != type)
5394 {
5395 cbD->currentIndexBuffer = vkindexbuf;
5396 cbD->currentIndexOffset = indexOffset;
5397 cbD->currentIndexFormat = type;
5398
5399 if (cbD->passUsesSecondaryCb) {
5400 df->vkCmdBindIndexBuffer(cbD->activeSecondaryCbStack.last(), vkindexbuf, indexOffset, type);
5401 } else {
5402 QVkCommandBuffer::Command &cmd(cbD->commands.get());
5404 cmd.args.bindIndexBuffer.buf = vkindexbuf;
5405 cmd.args.bindIndexBuffer.ofs = indexOffset;
5406 cmd.args.bindIndexBuffer.type = type;
5407 }
5408
5409 trackedRegisterBuffer(&passResTracker, ibufD, slot,
5412 }
5413 }
5414}
5415
5417{
5420 const QSize outputSize = cbD->currentTarget->pixelSize();
5421
5422 // x,y is top-left in VkViewport but bottom-left in QRhiViewport
5423 float x, y, w, h;
5424 if (!qrhi_toTopLeftRenderTargetRect<UnBounded>(outputSize, viewport.viewport(), &x, &y, &w, &h))
5425 return;
5426
5427 QVkCommandBuffer::Command &cmd(cbD->commands.get());
5428 VkViewport *vp = &cmd.args.setViewport.viewport;
5429 vp->x = x;
5430 vp->y = y;
5431 vp->width = w;
5432 vp->height = h;
5433 vp->minDepth = viewport.minDepth();
5434 vp->maxDepth = viewport.maxDepth();
5435
5436 if (cbD->passUsesSecondaryCb) {
5437 df->vkCmdSetViewport(cbD->activeSecondaryCbStack.last(), 0, 1, vp);
5438 cbD->commands.unget();
5439 } else {
5441 }
5442
5445 ->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor)) {
5446 QVkCommandBuffer::Command &cmd(cbD->commands.get());
5447 VkRect2D *s = &cmd.args.setScissor.scissor;
5448 qrhi_toTopLeftRenderTargetRect<Bounded>(outputSize, viewport.viewport(), &x, &y, &w, &h);
5449 s->offset.x = int32_t(x);
5450 s->offset.y = int32_t(y);
5451 s->extent.width = uint32_t(w);
5452 s->extent.height = uint32_t(h);
5453 if (cbD->passUsesSecondaryCb) {
5454 df->vkCmdSetScissor(cbD->activeSecondaryCbStack.last(), 0, 1, s);
5455 cbD->commands.unget();
5456 } else {
5458 }
5459 }
5460}
5461
5463{
5467 const QSize outputSize = cbD->currentTarget->pixelSize();
5468
5469 // x,y is top-left in VkRect2D but bottom-left in QRhiScissor
5470 int x, y, w, h;
5471 if (!qrhi_toTopLeftRenderTargetRect<Bounded>(outputSize, scissor.scissor(), &x, &y, &w, &h))
5472 return;
5473
5474 QVkCommandBuffer::Command &cmd(cbD->commands.get());
5475 VkRect2D *s = &cmd.args.setScissor.scissor;
5476 s->offset.x = x;
5477 s->offset.y = y;
5478 s->extent.width = uint32_t(w);
5479 s->extent.height = uint32_t(h);
5480
5481 if (cbD->passUsesSecondaryCb) {
5482 df->vkCmdSetScissor(cbD->activeSecondaryCbStack.last(), 0, 1, s);
5483 cbD->commands.unget();
5484 } else {
5486 }
5487}
5488
5490{
5493
5494 if (cbD->passUsesSecondaryCb) {
5495 float constants[] = { float(c.redF()), float(c.greenF()), float(c.blueF()), float(c.alphaF()) };
5496 df->vkCmdSetBlendConstants(cbD->activeSecondaryCbStack.last(), constants);
5497 } else {
5498 QVkCommandBuffer::Command &cmd(cbD->commands.get());
5500 cmd.args.setBlendConstants.c[0] = float(c.redF());
5501 cmd.args.setBlendConstants.c[1] = float(c.greenF());
5502 cmd.args.setBlendConstants.c[2] = float(c.blueF());
5503 cmd.args.setBlendConstants.c[3] = float(c.alphaF());
5504 }
5505}
5506
5508{
5511
5512 if (cbD->passUsesSecondaryCb) {
5513 df->vkCmdSetStencilReference(cbD->activeSecondaryCbStack.last(), VK_STENCIL_FRONT_AND_BACK, refValue);
5514 } else {
5515 QVkCommandBuffer::Command &cmd(cbD->commands.get());
5517 cmd.args.setStencilRef.ref = refValue;
5518 }
5519}
5520
5522 quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
5523{
5526
5527 if (cbD->passUsesSecondaryCb) {
5528 df->vkCmdDraw(cbD->activeSecondaryCbStack.last(), vertexCount, instanceCount, firstVertex, firstInstance);
5529 } else {
5530 QVkCommandBuffer::Command &cmd(cbD->commands.get());
5532 cmd.args.draw.vertexCount = vertexCount;
5533 cmd.args.draw.instanceCount = instanceCount;
5534 cmd.args.draw.firstVertex = firstVertex;
5535 cmd.args.draw.firstInstance = firstInstance;
5536 }
5537}
5538
5540 quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
5541{
5544
5545 if (cbD->passUsesSecondaryCb) {
5546 df->vkCmdDrawIndexed(cbD->activeSecondaryCbStack.last(), indexCount, instanceCount,
5547 firstIndex, vertexOffset, firstInstance);
5548 } else {
5549 QVkCommandBuffer::Command &cmd(cbD->commands.get());
5551 cmd.args.drawIndexed.indexCount = indexCount;
5552 cmd.args.drawIndexed.instanceCount = instanceCount;
5553 cmd.args.drawIndexed.firstIndex = firstIndex;
5554 cmd.args.drawIndexed.vertexOffset = vertexOffset;
5555 cmd.args.drawIndexed.firstInstance = firstInstance;
5556 }
5557}
5558
5560{
5561#ifdef VK_EXT_debug_utils
5562 if (!debugMarkers || !caps.debugUtils)
5563 return;
5564
5565 VkDebugUtilsLabelEXT label = {};
5566 label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
5567
5570 label.pLabelName = name.constData();
5571 vkCmdBeginDebugUtilsLabelEXT(cbD->activeSecondaryCbStack.last(), &label);
5572 } else {
5573 QVkCommandBuffer::Command &cmd(cbD->commands.get());
5575 cmd.args.debugMarkerBegin.label = label;
5576 cmd.args.debugMarkerBegin.labelNameIndex = cbD->pools.debugMarkerData.size();
5577 cbD->pools.debugMarkerData.append(name);
5578 }
5579#else
5580 Q_UNUSED(cb);
5581 Q_UNUSED(name);
5582#endif
5583}
5584
5586{
5587#ifdef VK_EXT_debug_utils
5588 if (!debugMarkers || !caps.debugUtils)
5589 return;
5590
5593 vkCmdEndDebugUtilsLabelEXT(cbD->activeSecondaryCbStack.last());
5594 } else {
5595 QVkCommandBuffer::Command &cmd(cbD->commands.get());
5597 }
5598#else
5599 Q_UNUSED(cb);
5600#endif
5601}
5602
5604{
5605#ifdef VK_EXT_debug_utils
5606 if (!debugMarkers || !caps.debugUtils)
5607 return;
5608
5609 VkDebugUtilsLabelEXT label = {};
5610 label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
5611
5614 label.pLabelName = msg.constData();
5615 vkCmdInsertDebugUtilsLabelEXT(cbD->activeSecondaryCbStack.last(), &label);
5616 } else {
5617 QVkCommandBuffer::Command &cmd(cbD->commands.get());
5619 cmd.args.debugMarkerInsert.label = label;
5620 cmd.args.debugMarkerInsert.labelNameIndex = cbD->pools.debugMarkerData.size();
5621 cbD->pools.debugMarkerData.append(msg);
5622 }
5623#else
5624 Q_UNUSED(cb);
5625 Q_UNUSED(msg);
5626#endif
5627}
5628
5633
5635{
5636 Q_ASSERT(cbD->currentTarget);
5637 QVkRenderTargetData *rtD = nullptr;
5639 switch (cbD->currentTarget->resourceType()) {
5642 break;
5645 break;
5646 default:
5647 Q_UNREACHABLE();
5648 break;
5649 }
5650 }
5651 return rtD;
5652}
5653
5655{
5657
5658 // When not in a pass, it is simple: record what we have (but do not
5659 // submit), the cb can then be used to record more external commands.
5662 cbD->resetCommands();
5663 return;
5664 }
5665
5666 // Otherwise, inside a pass, have a secondary command buffer (with
5667 // RENDER_PASS_CONTINUE). Using the main one is not acceptable since we
5668 // cannot just record at this stage, that would mess up the resource
5669 // tracking and commands like TransitionPassResources.
5670
5671 if (cbD->inExternal)
5672 return;
5673
5674 if (!cbD->passUsesSecondaryCb) {
5675 qWarning("beginExternal() within a pass is only supported with secondary command buffers. "
5676 "This can be enabled by passing QRhiCommandBuffer::ExternalContent to beginPass().");
5677 return;
5678 }
5679
5680 VkCommandBuffer secondaryCb = cbD->activeSecondaryCbStack.last();
5682 endAndEnqueueSecondaryCommandBuffer(secondaryCb, cbD);
5683
5684 VkCommandBuffer extCb = startSecondaryCommandBuffer(maybeRenderTargetData(cbD));
5685 if (extCb) {
5686 cbD->activeSecondaryCbStack.append(extCb);
5687 cbD->inExternal = true;
5688 }
5689}
5690
5692{
5694
5696 Q_ASSERT(cbD->commands.isEmpty() && cbD->currentPassResTrackerIndex == -1);
5697 } else if (cbD->inExternal) {
5698 VkCommandBuffer extCb = cbD->activeSecondaryCbStack.last();
5702 }
5703
5704 cbD->resetCachedState();
5705}
5706
5712
5713void QRhiVulkan::setObjectName(uint64_t object, VkObjectType type, const QByteArray &name, int slot)
5714{
5715#ifdef VK_EXT_debug_utils
5716 if (!debugMarkers || !caps.debugUtils || name.isEmpty())
5717 return;
5718
5719 VkDebugUtilsObjectNameInfoEXT nameInfo = {};
5720 nameInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
5721 nameInfo.objectType = type;
5722 nameInfo.objectHandle = object;
5723 QByteArray decoratedName = name;
5724 if (slot >= 0) {
5725 decoratedName += '/';
5726 decoratedName += QByteArray::number(slot);
5727 }
5728 nameInfo.pObjectName = decoratedName.constData();
5729 vkSetDebugUtilsObjectNameEXT(dev, &nameInfo);
5730#else
5731 Q_UNUSED(object);
5732 Q_UNUSED(type);
5733 Q_UNUSED(name);
5734 Q_UNUSED(slot);
5735#endif
5736}
5737
5738static inline VkBufferUsageFlagBits toVkBufferUsage(QRhiBuffer::UsageFlags usage)
5739{
5740 int u = 0;
5741 if (usage.testFlag(QRhiBuffer::VertexBuffer))
5742 u |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
5743 if (usage.testFlag(QRhiBuffer::IndexBuffer))
5744 u |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
5745 if (usage.testFlag(QRhiBuffer::UniformBuffer))
5746 u |= VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
5747 if (usage.testFlag(QRhiBuffer::StorageBuffer))
5748 u |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
5749 return VkBufferUsageFlagBits(u);
5750}
5751
5752static inline VkFilter toVkFilter(QRhiSampler::Filter f)
5753{
5754 switch (f) {
5756 return VK_FILTER_NEAREST;
5758 return VK_FILTER_LINEAR;
5759 default:
5760 Q_UNREACHABLE_RETURN(VK_FILTER_NEAREST);
5761 }
5762}
5763
5764static inline VkSamplerMipmapMode toVkMipmapMode(QRhiSampler::Filter f)
5765{
5766 switch (f) {
5767 case QRhiSampler::None:
5768 return VK_SAMPLER_MIPMAP_MODE_NEAREST;
5770 return VK_SAMPLER_MIPMAP_MODE_NEAREST;
5772 return VK_SAMPLER_MIPMAP_MODE_LINEAR;
5773 default:
5774 Q_UNREACHABLE_RETURN(VK_SAMPLER_MIPMAP_MODE_NEAREST);
5775 }
5776}
5777
5778static inline VkSamplerAddressMode toVkAddressMode(QRhiSampler::AddressMode m)
5779{
5780 switch (m) {
5782 return VK_SAMPLER_ADDRESS_MODE_REPEAT;
5784 return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
5786 return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
5787 default:
5788 Q_UNREACHABLE_RETURN(VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE);
5789 }
5790}
5791
5792static inline VkShaderStageFlagBits toVkShaderStage(QRhiShaderStage::Type type)
5793{
5794 switch (type) {
5796 return VK_SHADER_STAGE_VERTEX_BIT;
5798 return VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
5800 return VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
5802 return VK_SHADER_STAGE_FRAGMENT_BIT;
5804 return VK_SHADER_STAGE_COMPUTE_BIT;
5806 return VK_SHADER_STAGE_GEOMETRY_BIT;
5807 default:
5808 Q_UNREACHABLE_RETURN(VK_SHADER_STAGE_VERTEX_BIT);
5809 }
5810}
5811
5813{
5814 switch (format) {
5816 return VK_FORMAT_R32G32B32A32_SFLOAT;
5818 return VK_FORMAT_R32G32B32_SFLOAT;
5820 return VK_FORMAT_R32G32_SFLOAT;
5822 return VK_FORMAT_R32_SFLOAT;
5824 return VK_FORMAT_R8G8B8A8_UNORM;
5826 return VK_FORMAT_R8G8_UNORM;
5828 return VK_FORMAT_R8_UNORM;
5830 return VK_FORMAT_R32G32B32A32_UINT;
5832 return VK_FORMAT_R32G32B32_UINT;
5834 return VK_FORMAT_R32G32_UINT;
5836 return VK_FORMAT_R32_UINT;
5838 return VK_FORMAT_R32G32B32A32_SINT;
5840 return VK_FORMAT_R32G32B32_SINT;
5842 return VK_FORMAT_R32G32_SINT;
5844 return VK_FORMAT_R32_SINT;
5846 return VK_FORMAT_R16G16B16A16_SFLOAT;
5848 return VK_FORMAT_R16G16B16_SFLOAT;
5850 return VK_FORMAT_R16G16_SFLOAT;
5852 return VK_FORMAT_R16_SFLOAT;
5854 return VK_FORMAT_R16G16B16A16_UINT;
5856 return VK_FORMAT_R16G16B16_UINT;
5858 return VK_FORMAT_R16G16_UINT;
5860 return VK_FORMAT_R16_UINT;
5862 return VK_FORMAT_R16G16B16A16_SINT;
5864 return VK_FORMAT_R16G16B16_SINT;
5866 return VK_FORMAT_R16G16_SINT;
5868 return VK_FORMAT_R16_SINT;
5869 default:
5870 Q_UNREACHABLE_RETURN(VK_FORMAT_R32G32B32A32_SFLOAT);
5871 }
5872}
5873
5874static inline VkPrimitiveTopology toVkTopology(QRhiGraphicsPipeline::Topology t)
5875{
5876 switch (t) {
5878 return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
5880 return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
5882 return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN;
5884 return VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
5886 return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
5888 return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
5890 return VK_PRIMITIVE_TOPOLOGY_PATCH_LIST;
5891 default:
5892 Q_UNREACHABLE_RETURN(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
5893 }
5894}
5895
5896static inline VkCullModeFlags toVkCullMode(QRhiGraphicsPipeline::CullMode c)
5897{
5898 switch (c) {
5900 return VK_CULL_MODE_NONE;
5902 return VK_CULL_MODE_FRONT_BIT;
5904 return VK_CULL_MODE_BACK_BIT;
5905 default:
5906 Q_UNREACHABLE_RETURN(VK_CULL_MODE_NONE);
5907 }
5908}
5909
5911{
5912 switch (f) {
5914 return VK_FRONT_FACE_COUNTER_CLOCKWISE;
5916 return VK_FRONT_FACE_CLOCKWISE;
5917 default:
5918 Q_UNREACHABLE_RETURN(VK_FRONT_FACE_COUNTER_CLOCKWISE);
5919 }
5920}
5921
5922static inline VkColorComponentFlags toVkColorComponents(QRhiGraphicsPipeline::ColorMask c)
5923{
5924 int f = 0;
5925 if (c.testFlag(QRhiGraphicsPipeline::R))
5926 f |= VK_COLOR_COMPONENT_R_BIT;
5927 if (c.testFlag(QRhiGraphicsPipeline::G))
5928 f |= VK_COLOR_COMPONENT_G_BIT;
5929 if (c.testFlag(QRhiGraphicsPipeline::B))
5930 f |= VK_COLOR_COMPONENT_B_BIT;
5931 if (c.testFlag(QRhiGraphicsPipeline::A))
5932 f |= VK_COLOR_COMPONENT_A_BIT;
5933 return VkColorComponentFlags(f);
5934}
5935
5937{
5938 switch (f) {
5940 return VK_BLEND_FACTOR_ZERO;
5942 return VK_BLEND_FACTOR_ONE;
5944 return VK_BLEND_FACTOR_SRC_COLOR;
5946 return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR;
5948 return VK_BLEND_FACTOR_DST_COLOR;
5950 return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR;
5952 return VK_BLEND_FACTOR_SRC_ALPHA;
5954 return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
5956 return VK_BLEND_FACTOR_DST_ALPHA;
5958 return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;
5960 return VK_BLEND_FACTOR_CONSTANT_COLOR;
5962 return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR;
5964 return VK_BLEND_FACTOR_CONSTANT_ALPHA;
5966 return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA;
5968 return VK_BLEND_FACTOR_SRC_ALPHA_SATURATE;
5970 return VK_BLEND_FACTOR_SRC1_COLOR;
5972 return VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR;
5974 return VK_BLEND_FACTOR_SRC1_ALPHA;
5976 return VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA;
5977 default:
5978 Q_UNREACHABLE_RETURN(VK_BLEND_FACTOR_ZERO);
5979 }
5980}
5981
5983{
5984 switch (op) {
5986 return VK_BLEND_OP_ADD;
5988 return VK_BLEND_OP_SUBTRACT;
5990 return VK_BLEND_OP_REVERSE_SUBTRACT;
5992 return VK_BLEND_OP_MIN;
5994 return VK_BLEND_OP_MAX;
5995 default:
5996 Q_UNREACHABLE_RETURN(VK_BLEND_OP_ADD);
5997 }
5998}
5999
6001{
6002 switch (op) {
6004 return VK_COMPARE_OP_NEVER;
6006 return VK_COMPARE_OP_LESS;
6008 return VK_COMPARE_OP_EQUAL;
6010 return VK_COMPARE_OP_LESS_OR_EQUAL;
6012 return VK_COMPARE_OP_GREATER;
6014 return VK_COMPARE_OP_NOT_EQUAL;
6016 return VK_COMPARE_OP_GREATER_OR_EQUAL;
6018 return VK_COMPARE_OP_ALWAYS;
6019 default:
6020 Q_UNREACHABLE_RETURN(VK_COMPARE_OP_ALWAYS);
6021 }
6022}
6023
6025{
6026 switch (op) {
6028 return VK_STENCIL_OP_ZERO;
6030 return VK_STENCIL_OP_KEEP;
6032 return VK_STENCIL_OP_REPLACE;
6034 return VK_STENCIL_OP_INCREMENT_AND_CLAMP;
6036 return VK_STENCIL_OP_DECREMENT_AND_CLAMP;
6038 return VK_STENCIL_OP_INVERT;
6040 return VK_STENCIL_OP_INCREMENT_AND_WRAP;
6042 return VK_STENCIL_OP_DECREMENT_AND_WRAP;
6043 default:
6044 Q_UNREACHABLE_RETURN(VK_STENCIL_OP_KEEP);
6045 }
6046}
6047
6049{
6050 switch (mode) {
6052 return VK_POLYGON_MODE_FILL;
6054 return VK_POLYGON_MODE_LINE;
6055 default:
6056 Q_UNREACHABLE_RETURN(VK_POLYGON_MODE_FILL);
6057 }
6058}
6059
6060static inline void fillVkStencilOpState(VkStencilOpState *dst, const QRhiGraphicsPipeline::StencilOpState &src)
6061{
6062 dst->failOp = toVkStencilOp(src.failOp);
6063 dst->passOp = toVkStencilOp(src.passOp);
6064 dst->depthFailOp = toVkStencilOp(src.depthFailOp);
6065 dst->compareOp = toVkCompareOp(src.compareOp);
6066}
6067
6068static inline VkDescriptorType toVkDescriptorType(const QRhiShaderResourceBinding::Data *b)
6069{
6070 switch (b->type) {
6072 return b->u.ubuf.hasDynamicOffset ? VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC
6073 : VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
6074
6076 return VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
6077
6079 return VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
6080
6082 return VK_DESCRIPTOR_TYPE_SAMPLER;
6083
6087 return VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
6088
6092 return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
6093
6094 default:
6095 Q_UNREACHABLE_RETURN(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
6096 }
6097}
6098
6099static inline VkShaderStageFlags toVkShaderStageFlags(QRhiShaderResourceBinding::StageFlags stage)
6100{
6101 int s = 0;
6102 if (stage.testFlag(QRhiShaderResourceBinding::VertexStage))
6103 s |= VK_SHADER_STAGE_VERTEX_BIT;
6104 if (stage.testFlag(QRhiShaderResourceBinding::FragmentStage))
6105 s |= VK_SHADER_STAGE_FRAGMENT_BIT;
6106 if (stage.testFlag(QRhiShaderResourceBinding::ComputeStage))
6107 s |= VK_SHADER_STAGE_COMPUTE_BIT;
6109 s |= VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
6111 s |= VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
6112 if (stage.testFlag(QRhiShaderResourceBinding::GeometryStage))
6113 s |= VK_SHADER_STAGE_GEOMETRY_BIT;
6114 return VkShaderStageFlags(s);
6115}
6116
6118{
6119 switch (op) {
6120 case QRhiSampler::Never:
6121 return VK_COMPARE_OP_NEVER;
6122 case QRhiSampler::Less:
6123 return VK_COMPARE_OP_LESS;
6124 case QRhiSampler::Equal:
6125 return VK_COMPARE_OP_EQUAL;
6127 return VK_COMPARE_OP_LESS_OR_EQUAL;
6129 return VK_COMPARE_OP_GREATER;
6131 return VK_COMPARE_OP_NOT_EQUAL;
6133 return VK_COMPARE_OP_GREATER_OR_EQUAL;
6135 return VK_COMPARE_OP_ALWAYS;
6136 default:
6137 Q_UNREACHABLE_RETURN(VK_COMPARE_OP_NEVER);
6138 }
6139}
6140
6142 : QRhiBuffer(rhi, type, usage, size)
6143{
6144 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
6145 buffers[i] = stagingBuffers[i] = VK_NULL_HANDLE;
6146 allocations[i] = stagingAllocations[i] = nullptr;
6147 }
6148}
6149
6151{
6152 destroy();
6153}
6154
6156{
6157 if (!buffers[0])
6158 return;
6159
6163
6164 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
6165 e.buffer.buffers[i] = buffers[i];
6166 e.buffer.allocations[i] = allocations[i];
6167 e.buffer.stagingBuffers[i] = stagingBuffers[i];
6168 e.buffer.stagingAllocations[i] = stagingAllocations[i];
6169
6170 buffers[i] = VK_NULL_HANDLE;
6171 allocations[i] = nullptr;
6172 stagingBuffers[i] = VK_NULL_HANDLE;
6173 stagingAllocations[i] = nullptr;
6174 pendingDynamicUpdates[i].clear();
6175 }
6176
6178 // destroy() implementations, unlike other functions, are expected to test
6179 // for m_rhi being null, to allow surviving in case one attempts to destroy
6180 // a (leaked) resource after the QRhi.
6181 if (rhiD) {
6182 rhiD->releaseQueue.append(e);
6183 rhiD->unregisterResource(this);
6184 }
6185}
6186
6188{
6189 if (buffers[0])
6190 destroy();
6191
6192 if (m_usage.testFlag(QRhiBuffer::StorageBuffer) && m_type == Dynamic) {
6193 qWarning("StorageBuffer cannot be combined with Dynamic");
6194 return false;
6195 }
6196
6197 const quint32 nonZeroSize = m_size <= 0 ? 256 : m_size;
6198
6199 VkBufferCreateInfo bufferInfo = {};
6200 bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
6201 bufferInfo.size = nonZeroSize;
6202 bufferInfo.usage = toVkBufferUsage(m_usage);
6203
6204 VmaAllocationCreateInfo allocInfo = {};
6205
6206 if (m_type == Dynamic) {
6207#ifndef Q_OS_DARWIN // not for MoltenVK
6208 // Keep mapped all the time. Essential f.ex. with some mobile GPUs,
6209 // where mapping and unmapping an entire allocation every time updating
6210 // a suballocated buffer presents a significant perf. hit.
6211 allocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
6212#endif
6213 // host visible, frequent changes
6214 allocInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
6215 } else {
6216 allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
6217 bufferInfo.usage |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
6218 }
6219
6221 VkResult err = VK_SUCCESS;
6222 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
6223 buffers[i] = VK_NULL_HANDLE;
6224 allocations[i] = nullptr;
6226 if (i == 0 || m_type == Dynamic) {
6227 VmaAllocation allocation;
6228 err = vmaCreateBuffer(toVmaAllocator(rhiD->allocator), &bufferInfo, &allocInfo, &buffers[i], &allocation, nullptr);
6229 if (err != VK_SUCCESS)
6230 break;
6232 rhiD->setObjectName(uint64_t(buffers[i]), VK_OBJECT_TYPE_BUFFER, m_objectName,
6233 m_type == Dynamic ? i : -1);
6234 }
6235 }
6236
6237 if (err != VK_SUCCESS) {
6238 qWarning("Failed to create buffer of size %u: %d", nonZeroSize, err);
6239 rhiD->printExtraErrorInfo(err);
6240 return false;
6241 }
6242
6244 generation += 1;
6245 rhiD->registerResource(this);
6246 return true;
6247}
6248
6250{
6251 if (m_type == Dynamic) {
6254 Q_ASSERT(sizeof(b.objects) / sizeof(b.objects[0]) >= size_t(QVK_FRAMES_IN_FLIGHT));
6255 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
6256 rhiD->executeBufferHostWritesForSlot(this, i);
6257 b.objects[i] = &buffers[i];
6258 }
6259 b.slotCount = QVK_FRAMES_IN_FLIGHT;
6260 return b;
6261 }
6262 return { { &buffers[0] }, 1 };
6263}
6264
6266{
6267 // Shortcut the entire buffer update mechanism and allow the client to do
6268 // the host writes directly to the buffer. This will lead to unexpected
6269 // results when combined with QRhiResourceUpdateBatch-based updates for the
6270 // buffer, but provides a fast path for dynamic buffers that have all their
6271 // content changed in every frame.
6274 Q_ASSERT(rhiD->inFrame);
6275 const int slot = rhiD->currentFrameSlot;
6276 void *p = nullptr;
6277 VmaAllocation a = toVmaAllocation(allocations[slot]);
6278 VkResult err = vmaMapMemory(toVmaAllocator(rhiD->allocator), a, &p);
6279 if (err != VK_SUCCESS) {
6280 qWarning("Failed to map buffer: %d", err);
6281 return nullptr;
6282 }
6283 return static_cast<char *>(p);
6284}
6285
6287{
6289 const int slot = rhiD->currentFrameSlot;
6290 VmaAllocation a = toVmaAllocation(allocations[slot]);
6291 vmaFlushAllocation(toVmaAllocator(rhiD->allocator), a, 0, m_size);
6292 vmaUnmapMemory(toVmaAllocator(rhiD->allocator), a);
6293}
6294
6296 int sampleCount, Flags flags,
6297 QRhiTexture::Format backingFormatHint)
6298 : QRhiRenderBuffer(rhi, type, pixelSize, sampleCount, flags, backingFormatHint)
6299{
6300}
6301
6307
6309{
6310 if (!memory && !backingTexture)
6311 return;
6312
6316
6317 e.renderBuffer.memory = memory;
6318 e.renderBuffer.image = image;
6319 e.renderBuffer.imageView = imageView;
6320
6321 memory = VK_NULL_HANDLE;
6322 image = VK_NULL_HANDLE;
6323 imageView = VK_NULL_HANDLE;
6324
6325 if (backingTexture) {
6329 }
6330
6332 if (rhiD) {
6333 rhiD->releaseQueue.append(e);
6334 rhiD->unregisterResource(this);
6335 }
6336}
6337
6339{
6340 if (memory || backingTexture)
6341 destroy();
6342
6343 if (m_pixelSize.isEmpty())
6344 return false;
6345
6347 samples = rhiD->effectiveSampleCountBits(m_sampleCount);
6348
6349 switch (m_type) {
6351 {
6352 if (!backingTexture) {
6353 backingTexture = QRHI_RES(QVkTexture, rhiD->createTexture(backingFormat(),
6355 1,
6356 0,
6359 } else {
6362 }
6364 if (!backingTexture->create())
6365 return false;
6367 }
6368 break;
6370 vkformat = rhiD->optimalDepthStencilFormat();
6371 if (!rhiD->createTransientImage(vkformat,
6373 VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
6374 VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT,
6375 samples,
6376 &memory,
6377 &image,
6378 &imageView,
6379 1))
6380 {
6381 return false;
6382 }
6383 rhiD->setObjectName(uint64_t(image), VK_OBJECT_TYPE_IMAGE, m_objectName);
6384 break;
6385 default:
6386 Q_UNREACHABLE();
6387 break;
6388 }
6389
6391 generation += 1;
6392 rhiD->registerResource(this);
6393 return true;
6394}
6395
6403
6405 int arraySize, int sampleCount, Flags flags)
6406 : QRhiTexture(rhi, format, pixelSize, depth, arraySize, sampleCount, flags)
6407{
6408 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
6409 stagingBuffers[i] = VK_NULL_HANDLE;
6410 stagingAllocations[i] = nullptr;
6411 }
6412 for (int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i)
6413 perLevelImageViews[i] = VK_NULL_HANDLE;
6414}
6415
6417{
6418 destroy();
6419}
6420
6422{
6423 if (!image)
6424 return;
6425
6429
6430 e.texture.image = owns ? image : VK_NULL_HANDLE;
6431 e.texture.imageView = imageView;
6432 e.texture.allocation = owns ? imageAlloc : nullptr;
6433
6434 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
6435 e.texture.stagingBuffers[i] = stagingBuffers[i];
6436 e.texture.stagingAllocations[i] = stagingAllocations[i];
6437
6438 stagingBuffers[i] = VK_NULL_HANDLE;
6439 stagingAllocations[i] = nullptr;
6440 }
6441
6442 for (int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i) {
6443 e.texture.extraImageViews[i] = perLevelImageViews[i];
6444 perLevelImageViews[i] = VK_NULL_HANDLE;
6445 }
6446
6447 image = VK_NULL_HANDLE;
6448 imageView = VK_NULL_HANDLE;
6449 imageAlloc = nullptr;
6450
6452 if (rhiD) {
6453 rhiD->releaseQueue.append(e);
6454 rhiD->unregisterResource(this);
6455 }
6456}
6457
6459{
6460 if (image)
6461 destroy();
6462
6467 else
6471 else
6473
6474 VkFormatProperties props;
6475 rhiD->f->vkGetPhysicalDeviceFormatProperties(rhiD->physDev, vkformat, &props);
6476 const bool canSampleOptimal = (props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT);
6477 if (!canSampleOptimal) {
6478 qWarning("Texture sampling with optimal tiling for format %d not supported", vkformat);
6479 return false;
6480 }
6481
6482 const bool isCube = m_flags.testFlag(CubeMap);
6483 const bool isArray = m_flags.testFlag(TextureArray);
6484 const bool is3D = m_flags.testFlag(ThreeDimensional);
6485 const bool is1D = m_flags.testFlag(OneDimensional);
6486 const bool hasMipMaps = m_flags.testFlag(MipMapped);
6487
6488 const QSize size = is1D ? QSize(qMax(1, m_pixelSize.width()), 1)
6489 : (m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize);
6490
6491 mipLevelCount = uint(hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1);
6492 const int maxLevels = QRhi::MAX_MIP_LEVELS;
6493 if (mipLevelCount > maxLevels) {
6494 qWarning("Too many mip levels (%d, max is %d), truncating mip chain", mipLevelCount, maxLevels);
6495 mipLevelCount = maxLevels;
6496 }
6497 samples = rhiD->effectiveSampleCountBits(m_sampleCount);
6498 if (samples > VK_SAMPLE_COUNT_1_BIT) {
6499 if (isCube) {
6500 qWarning("Cubemap texture cannot be multisample");
6501 return false;
6502 }
6503 if (is3D) {
6504 qWarning("3D texture cannot be multisample");
6505 return false;
6506 }
6507 if (hasMipMaps) {
6508 qWarning("Multisample texture cannot have mipmaps");
6509 return false;
6510 }
6511 }
6512 if (isCube && is3D) {
6513 qWarning("Texture cannot be both cube and 3D");
6514 return false;
6515 }
6516 if (isArray && is3D) {
6517 qWarning("Texture cannot be both array and 3D");
6518 return false;
6519 }
6520 if (isCube && is1D) {
6521 qWarning("Texture cannot be both cube and 1D");
6522 return false;
6523 }
6524 if (is1D && is3D) {
6525 qWarning("Texture cannot be both 1D and 3D");
6526 return false;
6527 }
6528 if (m_depth > 1 && !is3D) {
6529 qWarning("Texture cannot have a depth of %d when it is not 3D", m_depth);
6530 return false;
6531 }
6532 if (m_arraySize > 0 && !isArray) {
6533 qWarning("Texture cannot have an array size of %d when it is not an array", m_arraySize);
6534 return false;
6535 }
6536 if (m_arraySize < 1 && isArray) {
6537 qWarning("Texture is an array but array size is %d", m_arraySize);
6538 return false;
6539 }
6540
6541 usageState.layout = VK_IMAGE_LAYOUT_PREINITIALIZED;
6542 usageState.access = 0;
6543 usageState.stage = 0;
6544
6545 if (adjustedSize)
6546 *adjustedSize = size;
6547
6548 return true;
6549}
6550
6552{
6554
6555 const auto aspectMask = aspectMaskForTextureFormat(m_format);
6556 const bool isCube = m_flags.testFlag(CubeMap);
6557 const bool isArray = m_flags.testFlag(TextureArray);
6558 const bool is3D = m_flags.testFlag(ThreeDimensional);
6559 const bool is1D = m_flags.testFlag(OneDimensional);
6560
6561 VkImageViewCreateInfo viewInfo = {};
6562 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
6563 viewInfo.image = image;
6564 viewInfo.viewType = isCube
6565 ? VK_IMAGE_VIEW_TYPE_CUBE
6566 : (is3D ? VK_IMAGE_VIEW_TYPE_3D
6567 : (is1D ? (isArray ? VK_IMAGE_VIEW_TYPE_1D_ARRAY : VK_IMAGE_VIEW_TYPE_1D)
6568 : (isArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D)));
6569 viewInfo.format = viewFormatForSampling;
6570 viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
6571 viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
6572 viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
6573 viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
6574 viewInfo.subresourceRange.aspectMask = aspectMask;
6575 viewInfo.subresourceRange.levelCount = mipLevelCount;
6576 if (isArray && m_arrayRangeStart >= 0 && m_arrayRangeLength >= 0) {
6577 viewInfo.subresourceRange.baseArrayLayer = uint32_t(m_arrayRangeStart);
6578 viewInfo.subresourceRange.layerCount = uint32_t(m_arrayRangeLength);
6579 } else {
6580 viewInfo.subresourceRange.layerCount = isCube ? 6 : (isArray ? qMax(0, m_arraySize) : 1);
6581 }
6582
6583 VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &imageView);
6584 if (err != VK_SUCCESS) {
6585 qWarning("Failed to create image view: %d", err);
6586 return false;
6587 }
6588
6590 generation += 1;
6591
6592 return true;
6593}
6594
6596{
6597 QSize size;
6598 if (!prepareCreate(&size))
6599 return false;
6600
6602 const bool isRenderTarget = m_flags.testFlag(QRhiTexture::RenderTarget);
6603 const bool isDepth = isDepthTextureFormat(m_format);
6604 const bool isCube = m_flags.testFlag(CubeMap);
6605 const bool isArray = m_flags.testFlag(TextureArray);
6606 const bool is3D = m_flags.testFlag(ThreeDimensional);
6607 const bool is1D = m_flags.testFlag(OneDimensional);
6608
6609 VkImageCreateInfo imageInfo = {};
6610 imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
6611 imageInfo.flags = 0;
6612 if (isCube)
6613 imageInfo.flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
6614
6615 if (is3D && isRenderTarget) {
6616 // This relies on a Vulkan 1.1 constant. For guaranteed proper behavior
6617 // this also requires that at run time the VkInstance has at least API 1.1
6618 // enabled. (though it works as expected with some Vulkan (1.2)
6619 // implementations regardless of the requested API version, but f.ex. the
6620 // validation layer complains when using this without enabling >=1.1)
6621 if (!rhiD->caps.texture3DSliceAs2D)
6622 qWarning("QRhiVulkan: Rendering to 3D texture slice may not be functional without API 1.1 on the VkInstance");
6623#ifdef VK_VERSION_1_1
6624 imageInfo.flags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT;
6625#else
6626 imageInfo.flags |= 0x00000020;
6627#endif
6628 }
6629
6630 imageInfo.imageType = is1D ? VK_IMAGE_TYPE_1D : is3D ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D;
6631 imageInfo.format = vkformat;
6632 imageInfo.extent.width = uint32_t(size.width());
6633 imageInfo.extent.height = uint32_t(size.height());
6634 imageInfo.extent.depth = is3D ? qMax(1, m_depth) : 1;
6635 imageInfo.mipLevels = mipLevelCount;
6636 imageInfo.arrayLayers = isCube ? 6 : (isArray ? qMax(0, m_arraySize) : 1);
6637 imageInfo.samples = samples;
6638 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
6639 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
6640
6641 imageInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
6642 if (isRenderTarget) {
6643 if (isDepth)
6644 imageInfo.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
6645 else
6646 imageInfo.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
6647 }
6649 imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
6651 imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
6653 imageInfo.usage |= VK_IMAGE_USAGE_STORAGE_BIT;
6654
6655 VmaAllocationCreateInfo allocInfo = {};
6656 allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
6657
6658 VmaAllocation allocation;
6659 VkResult err = vmaCreateImage(toVmaAllocator(rhiD->allocator), &imageInfo, &allocInfo, &image, &allocation, nullptr);
6660 if (err != VK_SUCCESS) {
6661 qWarning("Failed to create image (with VkImageCreateInfo %ux%u depth %u vkformat 0x%X mips %u layers %u vksamples 0x%X): %d",
6662 imageInfo.extent.width, imageInfo.extent.height, imageInfo.extent.depth,
6663 int(imageInfo.format),
6664 imageInfo.mipLevels,
6665 imageInfo.arrayLayers,
6666 int(imageInfo.samples),
6667 err);
6668 rhiD->printExtraErrorInfo(err);
6669 return false;
6670 }
6672
6673 if (!finishCreate())
6674 return false;
6675
6676 rhiD->setObjectName(uint64_t(image), VK_OBJECT_TYPE_IMAGE, m_objectName);
6677
6678 owns = true;
6679 rhiD->registerResource(this);
6680 return true;
6681}
6682
6684{
6685 VkImage img = VkImage(src.object);
6686 if (img == 0)
6687 return false;
6688
6689 if (!prepareCreate())
6690 return false;
6691
6692 image = img;
6693
6694 if (!finishCreate())
6695 return false;
6696
6697 usageState.layout = VkImageLayout(src.layout);
6698
6699 owns = false;
6701 rhiD->registerResource(this);
6702 return true;
6703}
6704
6709
6711{
6712 usageState.layout = VkImageLayout(layout);
6713}
6714
6716{
6717 Q_ASSERT(level >= 0 && level < int(mipLevelCount));
6718 if (perLevelImageViews[level] != VK_NULL_HANDLE)
6719 return perLevelImageViews[level];
6720
6721 const VkImageAspectFlags aspectMask = aspectMaskForTextureFormat(m_format);
6722 const bool isCube = m_flags.testFlag(CubeMap);
6723 const bool isArray = m_flags.testFlag(TextureArray);
6724 const bool is3D = m_flags.testFlag(ThreeDimensional);
6725 const bool is1D = m_flags.testFlag(OneDimensional);
6726
6727 VkImageViewCreateInfo viewInfo = {};
6728 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
6729 viewInfo.image = image;
6730 viewInfo.viewType = isCube
6731 ? VK_IMAGE_VIEW_TYPE_CUBE
6732 : (is3D ? VK_IMAGE_VIEW_TYPE_3D
6733 : (is1D ? (isArray ? VK_IMAGE_VIEW_TYPE_1D_ARRAY : VK_IMAGE_VIEW_TYPE_1D)
6734 : (isArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D)));
6735 viewInfo.format = viewFormat; // this is writeViewFormat, regardless of Load, Store, or LoadStore; intentional
6736 viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
6737 viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
6738 viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
6739 viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
6740 viewInfo.subresourceRange.aspectMask = aspectMask;
6741 viewInfo.subresourceRange.baseMipLevel = uint32_t(level);
6742 viewInfo.subresourceRange.levelCount = 1;
6743 viewInfo.subresourceRange.baseArrayLayer = 0;
6744 viewInfo.subresourceRange.layerCount = isCube ? 6 : (isArray ? qMax(0, m_arraySize) : 1);
6745
6746 VkImageView v = VK_NULL_HANDLE;
6748 VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &v);
6749 if (err != VK_SUCCESS) {
6750 qWarning("Failed to create image view: %d", err);
6751 return VK_NULL_HANDLE;
6752 }
6753
6755 return v;
6756}
6757
6758QVkSampler::QVkSampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
6760 : QRhiSampler(rhi, magFilter, minFilter, mipmapMode, u, v, w)
6761{
6762}
6763
6765{
6766 destroy();
6767}
6768
6770{
6771 if (!sampler)
6772 return;
6773
6777
6778 e.sampler.sampler = sampler;
6779 sampler = VK_NULL_HANDLE;
6780
6782 if (rhiD) {
6783 rhiD->releaseQueue.append(e);
6784 rhiD->unregisterResource(this);
6785 }
6786}
6787
6789{
6790 if (sampler)
6791 destroy();
6792
6793 VkSamplerCreateInfo samplerInfo = {};
6794 samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
6795 samplerInfo.magFilter = toVkFilter(m_magFilter);
6796 samplerInfo.minFilter = toVkFilter(m_minFilter);
6797 samplerInfo.mipmapMode = toVkMipmapMode(m_mipmapMode);
6798 samplerInfo.addressModeU = toVkAddressMode(m_addressU);
6799 samplerInfo.addressModeV = toVkAddressMode(m_addressV);
6800 samplerInfo.addressModeW = toVkAddressMode(m_addressW);
6801 samplerInfo.maxAnisotropy = 1.0f;
6802 samplerInfo.compareEnable = m_compareOp != Never;
6803 samplerInfo.compareOp = toVkTextureCompareOp(m_compareOp);
6804 samplerInfo.maxLod = m_mipmapMode == None ? 0.25f : 1000.0f;
6805
6807 VkResult err = rhiD->df->vkCreateSampler(rhiD->dev, &samplerInfo, nullptr, &sampler);
6808 if (err != VK_SUCCESS) {
6809 qWarning("Failed to create sampler: %d", err);
6810 return false;
6811 }
6812
6814 generation += 1;
6815 rhiD->registerResource(this);
6816 return true;
6817}
6818
6824
6829
6831{
6832 if (!rp)
6833 return;
6834
6835 if (!ownsRp) {
6836 rp = VK_NULL_HANDLE;
6837 return;
6838 }
6839
6843
6844 e.renderPass.rp = rp;
6845
6846 rp = VK_NULL_HANDLE;
6847
6849 if (rhiD) {
6850 rhiD->releaseQueue.append(e);
6851 rhiD->unregisterResource(this);
6852 }
6853}
6854
6855static inline bool attachmentDescriptionEquals(const VkAttachmentDescription &a, const VkAttachmentDescription &b)
6856{
6857 return a.format == b.format
6858 && a.samples == b.samples
6859 && a.loadOp == b.loadOp
6860 && a.storeOp == b.storeOp
6861 && a.stencilLoadOp == b.stencilLoadOp
6862 && a.stencilStoreOp == b.stencilStoreOp
6863 && a.initialLayout == b.initialLayout
6864 && a.finalLayout == b.finalLayout;
6865}
6866
6868{
6869 if (other == this)
6870 return true;
6871
6872 if (!other)
6873 return false;
6874
6876
6877 if (attDescs.size() != o->attDescs.size())
6878 return false;
6879 if (colorRefs.size() != o->colorRefs.size())
6880 return false;
6881 if (resolveRefs.size() != o->resolveRefs.size())
6882 return false;
6883 if (hasDepthStencil != o->hasDepthStencil)
6884 return false;
6885 if (hasDepthStencilResolve != o->hasDepthStencilResolve)
6886 return false;
6887 if (multiViewCount != o->multiViewCount)
6888 return false;
6889
6890 for (int i = 0, ie = colorRefs.size(); i != ie; ++i) {
6891 const uint32_t attIdx = colorRefs[i].attachment;
6892 if (attIdx != o->colorRefs[i].attachment)
6893 return false;
6894 if (attIdx != VK_ATTACHMENT_UNUSED && !attachmentDescriptionEquals(attDescs[attIdx], o->attDescs[attIdx]))
6895 return false;
6896 }
6897
6898 if (hasDepthStencil) {
6899 const uint32_t attIdx = dsRef.attachment;
6900 if (attIdx != o->dsRef.attachment)
6901 return false;
6902 if (attIdx != VK_ATTACHMENT_UNUSED && !attachmentDescriptionEquals(attDescs[attIdx], o->attDescs[attIdx]))
6903 return false;
6904 }
6905
6906 for (int i = 0, ie = resolveRefs.size(); i != ie; ++i) {
6907 const uint32_t attIdx = resolveRefs[i].attachment;
6908 if (attIdx != o->resolveRefs[i].attachment)
6909 return false;
6910 if (attIdx != VK_ATTACHMENT_UNUSED && !attachmentDescriptionEquals(attDescs[attIdx], o->attDescs[attIdx]))
6911 return false;
6912 }
6913
6915 const uint32_t attIdx = dsResolveRef.attachment;
6916 if (attIdx != o->dsResolveRef.attachment)
6917 return false;
6918 if (attIdx != VK_ATTACHMENT_UNUSED && !attachmentDescriptionEquals(attDescs[attIdx], o->attDescs[attIdx]))
6919 return false;
6920 }
6921
6922 // subpassDeps is not included
6923
6924 return true;
6925}
6926
6928{
6929 serializedFormatData.clear();
6930 auto p = std::back_inserter(serializedFormatData);
6931
6932 *p++ = attDescs.size();
6933 *p++ = colorRefs.size();
6934 *p++ = resolveRefs.size();
6935 *p++ = hasDepthStencil;
6937 *p++ = multiViewCount;
6938
6939 auto serializeAttachmentData = [this, &p](uint32_t attIdx) {
6940 const bool used = attIdx != VK_ATTACHMENT_UNUSED;
6941 const VkAttachmentDescription *a = used ? &attDescs[attIdx] : nullptr;
6942 *p++ = used ? a->format : 0;
6943 *p++ = used ? a->samples : 0;
6944 *p++ = used ? a->loadOp : 0;
6945 *p++ = used ? a->storeOp : 0;
6946 *p++ = used ? a->stencilLoadOp : 0;
6947 *p++ = used ? a->stencilStoreOp : 0;
6948 *p++ = used ? a->initialLayout : 0;
6949 *p++ = used ? a->finalLayout : 0;
6950 };
6951
6952 for (int i = 0, ie = colorRefs.size(); i != ie; ++i) {
6953 const uint32_t attIdx = colorRefs[i].attachment;
6954 *p++ = attIdx;
6955 serializeAttachmentData(attIdx);
6956 }
6957
6958 if (hasDepthStencil) {
6959 const uint32_t attIdx = dsRef.attachment;
6960 *p++ = attIdx;
6961 serializeAttachmentData(attIdx);
6962 }
6963
6964 for (int i = 0, ie = resolveRefs.size(); i != ie; ++i) {
6965 const uint32_t attIdx = resolveRefs[i].attachment;
6966 *p++ = attIdx;
6967 serializeAttachmentData(attIdx);
6968 }
6969
6971 const uint32_t attIdx = dsResolveRef.attachment;
6972 *p++ = attIdx;
6973 serializeAttachmentData(attIdx);
6974 }
6975}
6976
6978{
6980
6981 rpD->ownsRp = true;
6982 rpD->attDescs = attDescs;
6983 rpD->colorRefs = colorRefs;
6984 rpD->resolveRefs = resolveRefs;
6985 rpD->subpassDeps = subpassDeps;
6986 rpD->hasDepthStencil = hasDepthStencil;
6987 rpD->hasDepthStencilResolve = hasDepthStencilResolve;
6988 rpD->multiViewCount = multiViewCount;
6989 rpD->dsRef = dsRef;
6990 rpD->dsResolveRef = dsResolveRef;
6991
6992 VkRenderPassCreateInfo rpInfo;
6993 VkSubpassDescription subpassDesc;
6994 fillRenderPassCreateInfo(&rpInfo, &subpassDesc, rpD);
6995
6997 MultiViewRenderPassSetupHelper multiViewHelper;
6998 if (!multiViewHelper.prepare(&rpInfo, multiViewCount, rhiD->caps.multiView)) {
6999 delete rpD;
7000 return nullptr;
7001 }
7002
7003 VkResult err = rhiD->df->vkCreateRenderPass(rhiD->dev, &rpInfo, nullptr, &rpD->rp);
7004 if (err != VK_SUCCESS) {
7005 qWarning("Failed to create renderpass: %d", err);
7006 delete rpD;
7007 return nullptr;
7008 }
7009
7010 rpD->updateSerializedFormat();
7011 rhiD->registerResource(rpD);
7012 return rpD;
7013}
7014
7016{
7017 return serializedFormatData;
7018}
7019
7025
7030
7035
7037{
7038 // nothing to do here
7039}
7040
7042{
7043 return d.pixelSize;
7044}
7045
7047{
7048 return d.dpr;
7049}
7050
7052{
7053 return d.sampleCount;
7054}
7055
7058 Flags flags)
7059 : QRhiTextureRenderTarget(rhi, desc, flags)
7060{
7061 for (int att = 0; att < QVkRenderTargetData::MAX_COLOR_ATTACHMENTS; ++att) {
7062 rtv[att] = VK_NULL_HANDLE;
7063 resrtv[att] = VK_NULL_HANDLE;
7064 }
7065}
7066
7071
7073{
7074 if (!d.fb)
7075 return;
7076
7080
7081 e.textureRenderTarget.fb = d.fb;
7082 d.fb = VK_NULL_HANDLE;
7083
7084 for (int att = 0; att < QVkRenderTargetData::MAX_COLOR_ATTACHMENTS; ++att) {
7085 e.textureRenderTarget.rtv[att] = rtv[att];
7086 e.textureRenderTarget.resrtv[att] = resrtv[att];
7087 rtv[att] = VK_NULL_HANDLE;
7088 resrtv[att] = VK_NULL_HANDLE;
7089 }
7090
7091 e.textureRenderTarget.dsv = dsv;
7092 dsv = VK_NULL_HANDLE;
7093 e.textureRenderTarget.resdsv = resdsv;
7094 resdsv = VK_NULL_HANDLE;
7095
7097 if (rhiD) {
7098 rhiD->releaseQueue.append(e);
7099 rhiD->unregisterResource(this);
7100 }
7101}
7102
7104{
7105 // not yet built so cannot rely on data computed in create()
7106
7109 if (!rhiD->createOffscreenRenderPass(rp,
7118 {
7119 delete rp;
7120 return nullptr;
7121 }
7122
7123 rp->ownsRp = true;
7125 rhiD->registerResource(rp);
7126 return rp;
7127}
7128
7130{
7131 if (d.fb)
7132 destroy();
7133
7136 const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
7137
7139 QVarLengthArray<VkImageView, 8> views;
7140 d.multiViewCount = 0;
7141
7142 d.colorAttCount = 0;
7143 int attIndex = 0;
7144 for (auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it, ++attIndex) {
7145 d.colorAttCount += 1;
7146 QVkTexture *texD = QRHI_RES(QVkTexture, it->texture());
7147 QVkRenderBuffer *rbD = QRHI_RES(QVkRenderBuffer, it->renderBuffer());
7148 Q_ASSERT(texD || rbD);
7149 if (texD) {
7150 Q_ASSERT(texD->flags().testFlag(QRhiTexture::RenderTarget));
7151 const bool is1D = texD->flags().testFlag(QRhiTexture::OneDimensional);
7152 const bool isMultiView = it->multiViewCount() >= 2;
7153 if (isMultiView && d.multiViewCount == 0)
7154 d.multiViewCount = it->multiViewCount();
7155 VkImageViewCreateInfo viewInfo = {};
7156 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
7157 viewInfo.image = texD->image;
7158 viewInfo.viewType = is1D ? VK_IMAGE_VIEW_TYPE_1D
7159 : (isMultiView ? VK_IMAGE_VIEW_TYPE_2D_ARRAY
7160 : VK_IMAGE_VIEW_TYPE_2D);
7161 viewInfo.format = texD->viewFormat;
7162 viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
7163 viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
7164 viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
7165 viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
7166 viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
7167 viewInfo.subresourceRange.baseMipLevel = uint32_t(it->level());
7168 viewInfo.subresourceRange.levelCount = 1;
7169 viewInfo.subresourceRange.baseArrayLayer = uint32_t(it->layer());
7170 viewInfo.subresourceRange.layerCount = uint32_t(isMultiView ? it->multiViewCount() : 1);
7171 VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &rtv[attIndex]);
7172 if (err != VK_SUCCESS) {
7173 qWarning("Failed to create render target image view: %d", err);
7174 return false;
7175 }
7176 views.append(rtv[attIndex]);
7177 if (attIndex == 0) {
7178 d.pixelSize = rhiD->q->sizeForMipLevel(it->level(), texD->pixelSize());
7179 d.sampleCount = texD->samples;
7180 }
7181 } else if (rbD) {
7182 Q_ASSERT(rbD->backingTexture);
7183 views.append(rbD->backingTexture->imageView);
7184 if (attIndex == 0) {
7185 d.pixelSize = rbD->pixelSize();
7186 d.sampleCount = rbD->samples;
7187 }
7188 }
7189 }
7190 d.dpr = 1;
7191
7192 if (hasDepthStencil) {
7193 if (m_desc.depthTexture()) {
7195 // need a dedicated view just because viewFormat may differ from vkformat
7196 VkImageViewCreateInfo viewInfo = {};
7197 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
7198 viewInfo.image = depthTexD->image;
7199 viewInfo.viewType = d.multiViewCount > 1 ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D;
7200 viewInfo.format = depthTexD->viewFormat;
7201 viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
7202 viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
7203 viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
7204 viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
7205 viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
7206 viewInfo.subresourceRange.levelCount = 1;
7207 viewInfo.subresourceRange.layerCount = qMax<uint32_t>(1, d.multiViewCount);
7208 VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &dsv);
7209 if (err != VK_SUCCESS) {
7210 qWarning("Failed to create depth-stencil image view for rt: %d", err);
7211 return false;
7212 }
7213 views.append(dsv);
7214 if (d.colorAttCount == 0) {
7215 d.pixelSize = depthTexD->pixelSize();
7216 d.sampleCount = depthTexD->samples;
7217 }
7218 } else {
7220 views.append(depthRbD->imageView);
7221 if (d.colorAttCount == 0) {
7222 d.pixelSize = depthRbD->pixelSize();
7223 d.sampleCount = depthRbD->samples;
7224 }
7225 }
7226 d.dsAttCount = 1;
7227 } else {
7228 d.dsAttCount = 0;
7229 }
7230
7231 d.resolveAttCount = 0;
7232 attIndex = 0;
7234 for (auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it, ++attIndex) {
7235 if (it->resolveTexture()) {
7236 QVkTexture *resTexD = QRHI_RES(QVkTexture, it->resolveTexture());
7237 Q_ASSERT(resTexD->flags().testFlag(QRhiTexture::RenderTarget));
7238 d.resolveAttCount += 1;
7239
7240 VkImageViewCreateInfo viewInfo = {};
7241 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
7242 viewInfo.image = resTexD->image;
7243 viewInfo.viewType = d.multiViewCount ? VK_IMAGE_VIEW_TYPE_2D_ARRAY
7244 : VK_IMAGE_VIEW_TYPE_2D;
7245 viewInfo.format = resTexD->viewFormat;
7246 viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
7247 viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
7248 viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
7249 viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
7250 viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
7251 viewInfo.subresourceRange.baseMipLevel = uint32_t(it->resolveLevel());
7252 viewInfo.subresourceRange.levelCount = 1;
7253 viewInfo.subresourceRange.baseArrayLayer = uint32_t(it->resolveLayer());
7254 viewInfo.subresourceRange.layerCount = qMax<uint32_t>(1, d.multiViewCount);
7255 VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &resrtv[attIndex]);
7256 if (err != VK_SUCCESS) {
7257 qWarning("Failed to create render target resolve image view: %d", err);
7258 return false;
7259 }
7260 views.append(resrtv[attIndex]);
7261 }
7262 }
7263
7266 Q_ASSERT(resTexD->flags().testFlag(QRhiTexture::RenderTarget));
7267
7268 VkImageViewCreateInfo viewInfo = {};
7269 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
7270 viewInfo.image = resTexD->image;
7271 viewInfo.viewType = d.multiViewCount ? VK_IMAGE_VIEW_TYPE_2D_ARRAY
7272 : VK_IMAGE_VIEW_TYPE_2D;
7273 viewInfo.format = resTexD->viewFormat;
7274 viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
7275 viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
7276 viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
7277 viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
7278 viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
7279 viewInfo.subresourceRange.baseMipLevel = 0;
7280 viewInfo.subresourceRange.levelCount = 1;
7281 viewInfo.subresourceRange.baseArrayLayer = 0;
7282 viewInfo.subresourceRange.layerCount = qMax<uint32_t>(1, d.multiViewCount);
7283 VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &resdsv);
7284 if (err != VK_SUCCESS) {
7285 qWarning("Failed to create render target depth resolve image view: %d", err);
7286 return false;
7287 }
7288 views.append(resdsv);
7289 d.dsResolveAttCount = 1;
7290 } else {
7291 d.dsResolveAttCount = 0;
7292 }
7293
7294 if (!m_renderPassDesc)
7295 qWarning("QVkTextureRenderTarget: No renderpass descriptor set. See newCompatibleRenderPassDescriptor() and setRenderPassDescriptor().");
7296
7298 Q_ASSERT(d.rp && d.rp->rp);
7299
7300 VkFramebufferCreateInfo fbInfo = {};
7301 fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
7302 fbInfo.renderPass = d.rp->rp;
7303 fbInfo.attachmentCount = uint32_t(d.colorAttCount + d.dsAttCount + d.resolveAttCount + d.dsResolveAttCount);
7304 fbInfo.pAttachments = views.constData();
7305 fbInfo.width = uint32_t(d.pixelSize.width());
7306 fbInfo.height = uint32_t(d.pixelSize.height());
7307 fbInfo.layers = 1;
7308
7309 VkResult err = rhiD->df->vkCreateFramebuffer(rhiD->dev, &fbInfo, nullptr, &d.fb);
7310 if (err != VK_SUCCESS) {
7311 qWarning("Failed to create framebuffer: %d", err);
7312 return false;
7313 }
7314
7315 QRhiRenderTargetAttachmentTracker::updateResIdList<QVkTexture, QVkRenderBuffer>(m_desc, &d.currentResIdList);
7316
7318 rhiD->registerResource(this);
7319 return true;
7320}
7321
7323{
7324 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QVkTexture, QVkRenderBuffer>(m_desc, d.currentResIdList))
7325 const_cast<QVkTextureRenderTarget *>(this)->create();
7326
7327 return d.pixelSize;
7328}
7329
7331{
7332 return d.dpr;
7333}
7334
7336{
7337 return d.sampleCount;
7338}
7339
7344
7349
7351{
7352 if (!layout)
7353 return;
7354
7356
7360
7361 e.shaderResourceBindings.poolIndex = poolIndex;
7362 e.shaderResourceBindings.layout = layout;
7363
7364 poolIndex = -1;
7365 layout = VK_NULL_HANDLE;
7366 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
7367 descSets[i] = VK_NULL_HANDLE;
7368
7370 if (rhiD) {
7371 rhiD->releaseQueue.append(e);
7372 rhiD->unregisterResource(this);
7373 }
7374}
7375
7377{
7378 if (layout)
7379 destroy();
7380
7382 if (!rhiD->sanityCheckShaderResourceBindings(this))
7383 return false;
7384
7385 rhiD->updateLayoutDesc(this);
7386
7387 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
7388 descSets[i] = VK_NULL_HANDLE;
7389
7391 std::copy(m_bindings.cbegin(), m_bindings.cend(), std::back_inserter(sortedBindings));
7393
7394 hasSlottedResource = false;
7395 hasDynamicOffset = false;
7396 for (const QRhiShaderResourceBinding &binding : std::as_const(sortedBindings)) {
7398 if (b->type == QRhiShaderResourceBinding::UniformBuffer && b->u.ubuf.buf) {
7399 if (QRHI_RES(QVkBuffer, b->u.ubuf.buf)->type() == QRhiBuffer::Dynamic)
7400 hasSlottedResource = true;
7401 if (b->u.ubuf.hasDynamicOffset)
7402 hasDynamicOffset = true;
7403 }
7404 }
7405
7406 QVarLengthArray<VkDescriptorSetLayoutBinding, 4> vkbindings;
7407 for (const QRhiShaderResourceBinding &binding : std::as_const(sortedBindings)) {
7409 VkDescriptorSetLayoutBinding vkbinding = {};
7410 vkbinding.binding = uint32_t(b->binding);
7411 vkbinding.descriptorType = toVkDescriptorType(b);
7413 vkbinding.descriptorCount = b->u.stex.count;
7414 else
7415 vkbinding.descriptorCount = 1;
7416 vkbinding.stageFlags = toVkShaderStageFlags(b->stage);
7417 vkbindings.append(vkbinding);
7418 }
7419
7420 VkDescriptorSetLayoutCreateInfo layoutInfo = {};
7421 layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
7422 layoutInfo.bindingCount = uint32_t(vkbindings.size());
7423 layoutInfo.pBindings = vkbindings.constData();
7424
7425 VkResult err = rhiD->df->vkCreateDescriptorSetLayout(rhiD->dev, &layoutInfo, nullptr, &layout);
7426 if (err != VK_SUCCESS) {
7427 qWarning("Failed to create descriptor set layout: %d", err);
7428 return false;
7429 }
7430
7431 VkDescriptorSetAllocateInfo allocInfo = {};
7432 allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
7433 allocInfo.descriptorSetCount = QVK_FRAMES_IN_FLIGHT;
7434 VkDescriptorSetLayout layouts[QVK_FRAMES_IN_FLIGHT];
7435 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
7436 layouts[i] = layout;
7437 allocInfo.pSetLayouts = layouts;
7438 if (!rhiD->allocateDescriptorSet(&allocInfo, descSets, &poolIndex))
7439 return false;
7440
7441 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
7444 memset(&bd, 0, sizeof(BoundResourceData));
7445 }
7446
7448 generation += 1;
7449 rhiD->registerResource(this);
7450 return true;
7451}
7452
7454{
7456 std::copy(m_bindings.cbegin(), m_bindings.cend(), std::back_inserter(sortedBindings));
7457 if (!flags.testFlag(BindingsAreSorted))
7459
7460 // Reset the state tracking table too - it can deal with assigning a
7461 // different QRhiBuffer/Texture/Sampler for a binding point, but it cannot
7462 // detect changes in the associated data, such as the buffer offset. And
7463 // just like after a create(), a call to updateResources() may lead to now
7464 // specifying a different offset for the same QRhiBuffer for a given binding
7465 // point. The same applies to other type of associated data that is not part
7466 // of the layout, such as the mip level for a StorageImage. Instead of
7467 // complicating the checks in setShaderResources(), reset the table here
7468 // just like we do in create().
7469 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
7472 memset(&bd, 0, sizeof(BoundResourceData));
7473 }
7474
7475 generation += 1;
7476}
7477
7482
7487
7489{
7490 if (!pipeline && !layout)
7491 return;
7492
7496
7497 e.pipelineState.pipeline = pipeline;
7498 e.pipelineState.layout = layout;
7499
7500 pipeline = VK_NULL_HANDLE;
7501 layout = VK_NULL_HANDLE;
7502
7504 if (rhiD) {
7505 rhiD->releaseQueue.append(e);
7506 rhiD->unregisterResource(this);
7507 }
7508}
7509
7511{
7512 if (pipeline)
7513 destroy();
7514
7516 rhiD->pipelineCreationStart();
7517 if (!rhiD->sanityCheckGraphicsPipeline(this))
7518 return false;
7519
7520 if (!rhiD->ensurePipelineCache())
7521 return false;
7522
7523 VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
7524 pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
7525 pipelineLayoutInfo.setLayoutCount = 1;
7527 Q_ASSERT(m_shaderResourceBindings && srbD->layout);
7528 pipelineLayoutInfo.pSetLayouts = &srbD->layout;
7529 VkResult err = rhiD->df->vkCreatePipelineLayout(rhiD->dev, &pipelineLayoutInfo, nullptr, &layout);
7530 if (err != VK_SUCCESS) {
7531 qWarning("Failed to create pipeline layout: %d", err);
7532 return false;
7533 }
7534
7535 VkGraphicsPipelineCreateInfo pipelineInfo = {};
7536 pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
7537
7538 QVarLengthArray<VkShaderModule, 4> shaders;
7539 QVarLengthArray<VkPipelineShaderStageCreateInfo, 4> shaderStageCreateInfos;
7540 for (const QRhiShaderStage &shaderStage : m_shaderStages) {
7541 const QShader bakedShader = shaderStage.shader();
7542 const QShaderCode spirv = bakedShader.shader({ QShader::SpirvShader, 100, shaderStage.shaderVariant() });
7543 if (spirv.shader().isEmpty()) {
7544 qWarning() << "No SPIR-V 1.0 shader code found in baked shader" << bakedShader;
7545 return false;
7546 }
7547 VkShaderModule shader = rhiD->createShader(spirv.shader());
7548 if (shader) {
7549 shaders.append(shader);
7550 VkPipelineShaderStageCreateInfo shaderInfo = {};
7551 shaderInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
7552 shaderInfo.stage = toVkShaderStage(shaderStage.type());
7553 shaderInfo.module = shader;
7554 shaderInfo.pName = spirv.entryPoint().constData();
7555 shaderStageCreateInfos.append(shaderInfo);
7556 }
7557 }
7558 pipelineInfo.stageCount = uint32_t(shaderStageCreateInfos.size());
7559 pipelineInfo.pStages = shaderStageCreateInfos.constData();
7560
7561 QVarLengthArray<VkVertexInputBindingDescription, 4> vertexBindings;
7562#ifdef VK_EXT_vertex_attribute_divisor
7563 QVarLengthArray<VkVertexInputBindingDivisorDescriptionEXT> nonOneStepRates;
7564#endif
7565 int bindingIndex = 0;
7567 it != itEnd; ++it, ++bindingIndex)
7568 {
7569 VkVertexInputBindingDescription bindingInfo = {
7570 uint32_t(bindingIndex),
7571 it->stride(),
7572 it->classification() == QRhiVertexInputBinding::PerVertex
7573 ? VK_VERTEX_INPUT_RATE_VERTEX : VK_VERTEX_INPUT_RATE_INSTANCE
7574 };
7575 if (it->classification() == QRhiVertexInputBinding::PerInstance && it->instanceStepRate() != 1) {
7576#ifdef VK_EXT_vertex_attribute_divisor
7577 if (rhiD->caps.vertexAttribDivisor) {
7578 nonOneStepRates.append({ uint32_t(bindingIndex), it->instanceStepRate() });
7579 } else
7580#endif
7581 {
7582 qWarning("QRhiVulkan: Instance step rates other than 1 not supported without "
7583 "VK_EXT_vertex_attribute_divisor on the device and "
7584 "VK_KHR_get_physical_device_properties2 on the instance");
7585 }
7586 }
7587 vertexBindings.append(bindingInfo);
7588 }
7589 QVarLengthArray<VkVertexInputAttributeDescription, 4> vertexAttributes;
7591 it != itEnd; ++it)
7592 {
7593 VkVertexInputAttributeDescription attributeInfo = {
7594 uint32_t(it->location()),
7595 uint32_t(it->binding()),
7596 toVkAttributeFormat(it->format()),
7597 it->offset()
7598 };
7599 vertexAttributes.append(attributeInfo);
7600 }
7601 VkPipelineVertexInputStateCreateInfo vertexInputInfo = {};
7602 vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
7603 vertexInputInfo.vertexBindingDescriptionCount = uint32_t(vertexBindings.size());
7604 vertexInputInfo.pVertexBindingDescriptions = vertexBindings.constData();
7605 vertexInputInfo.vertexAttributeDescriptionCount = uint32_t(vertexAttributes.size());
7606 vertexInputInfo.pVertexAttributeDescriptions = vertexAttributes.constData();
7607#ifdef VK_EXT_vertex_attribute_divisor
7608 VkPipelineVertexInputDivisorStateCreateInfoEXT divisorInfo = {};
7609 if (!nonOneStepRates.isEmpty()) {
7610 divisorInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT;
7611 divisorInfo.vertexBindingDivisorCount = uint32_t(nonOneStepRates.size());
7612 divisorInfo.pVertexBindingDivisors = nonOneStepRates.constData();
7613 vertexInputInfo.pNext = &divisorInfo;
7614 }
7615#endif
7616 pipelineInfo.pVertexInputState = &vertexInputInfo;
7617
7618 QVarLengthArray<VkDynamicState, 8> dynEnable;
7619 dynEnable << VK_DYNAMIC_STATE_VIEWPORT;
7620 dynEnable << VK_DYNAMIC_STATE_SCISSOR; // ignore UsesScissor - Vulkan requires a scissor for the viewport always
7622 dynEnable << VK_DYNAMIC_STATE_BLEND_CONSTANTS;
7624 dynEnable << VK_DYNAMIC_STATE_STENCIL_REFERENCE;
7625
7626 VkPipelineDynamicStateCreateInfo dynamicInfo = {};
7627 dynamicInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
7628 dynamicInfo.dynamicStateCount = uint32_t(dynEnable.size());
7629 dynamicInfo.pDynamicStates = dynEnable.constData();
7630 pipelineInfo.pDynamicState = &dynamicInfo;
7631
7632 VkPipelineViewportStateCreateInfo viewportInfo = {};
7633 viewportInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
7634 viewportInfo.viewportCount = viewportInfo.scissorCount = 1;
7635 pipelineInfo.pViewportState = &viewportInfo;
7636
7637 VkPipelineInputAssemblyStateCreateInfo inputAsmInfo = {};
7638 inputAsmInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
7639 inputAsmInfo.topology = toVkTopology(m_topology);
7640 inputAsmInfo.primitiveRestartEnable = (m_topology == TriangleStrip || m_topology == LineStrip);
7641 pipelineInfo.pInputAssemblyState = &inputAsmInfo;
7642
7643 VkPipelineTessellationStateCreateInfo tessInfo = {};
7644#ifdef VK_VERSION_1_1
7645 VkPipelineTessellationDomainOriginStateCreateInfo originInfo = {};
7646#endif
7647 if (m_topology == Patches) {
7648 tessInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO;
7649 tessInfo.patchControlPoints = uint32_t(qMax(1, m_patchControlPointCount));
7650
7651 // To be able to use the same tess.evaluation shader with both OpenGL
7652 // and Vulkan, flip the tessellation domain origin to be lower left.
7653 // This allows declaring the winding order in the shader to be CCW and
7654 // still have it working with both APIs. This requires Vulkan 1.1 (or
7655 // VK_KHR_maintenance2 but don't bother with that).
7656#ifdef VK_VERSION_1_1
7657 if (rhiD->caps.apiVersion >= QVersionNumber(1, 1)) {
7658 originInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO;
7659 originInfo.domainOrigin = VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT;
7660 tessInfo.pNext = &originInfo;
7661 } else {
7662 qWarning("Proper tessellation support requires Vulkan 1.1 or newer, leaving domain origin unset");
7663 }
7664#else
7665 qWarning("QRhi was built without Vulkan 1.1 headers, this is not sufficient for proper tessellation support");
7666#endif
7667
7668 pipelineInfo.pTessellationState = &tessInfo;
7669 }
7670
7671 VkPipelineRasterizationStateCreateInfo rastInfo = {};
7672 rastInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
7673 rastInfo.cullMode = toVkCullMode(m_cullMode);
7674 rastInfo.frontFace = toVkFrontFace(m_frontFace);
7676 rastInfo.depthBiasEnable = true;
7677 rastInfo.depthBiasConstantFactor = float(m_depthBias);
7678 rastInfo.depthBiasSlopeFactor = m_slopeScaledDepthBias;
7679 }
7680 rastInfo.lineWidth = rhiD->caps.wideLines ? m_lineWidth : 1.0f;
7681 rastInfo.polygonMode = toVkPolygonMode(m_polygonMode);
7682 pipelineInfo.pRasterizationState = &rastInfo;
7683
7684 VkPipelineMultisampleStateCreateInfo msInfo = {};
7685 msInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
7686 msInfo.rasterizationSamples = rhiD->effectiveSampleCountBits(m_sampleCount);
7687 pipelineInfo.pMultisampleState = &msInfo;
7688
7689 VkPipelineDepthStencilStateCreateInfo dsInfo = {};
7690 dsInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
7691 dsInfo.depthTestEnable = m_depthTest;
7692 dsInfo.depthWriteEnable = m_depthWrite;
7693 dsInfo.depthCompareOp = toVkCompareOp(m_depthOp);
7694 dsInfo.stencilTestEnable = m_stencilTest;
7695 if (m_stencilTest) {
7696 fillVkStencilOpState(&dsInfo.front, m_stencilFront);
7697 dsInfo.front.compareMask = m_stencilReadMask;
7698 dsInfo.front.writeMask = m_stencilWriteMask;
7699 fillVkStencilOpState(&dsInfo.back, m_stencilBack);
7700 dsInfo.back.compareMask = m_stencilReadMask;
7701 dsInfo.back.writeMask = m_stencilWriteMask;
7702 }
7703 pipelineInfo.pDepthStencilState = &dsInfo;
7704
7705 VkPipelineColorBlendStateCreateInfo blendInfo = {};
7706 blendInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
7707 QVarLengthArray<VkPipelineColorBlendAttachmentState, 4> vktargetBlends;
7708 for (const QRhiGraphicsPipeline::TargetBlend &b : std::as_const(m_targetBlends)) {
7709 VkPipelineColorBlendAttachmentState blend = {};
7710 blend.blendEnable = b.enable;
7711 blend.srcColorBlendFactor = toVkBlendFactor(b.srcColor);
7712 blend.dstColorBlendFactor = toVkBlendFactor(b.dstColor);
7713 blend.colorBlendOp = toVkBlendOp(b.opColor);
7714 blend.srcAlphaBlendFactor = toVkBlendFactor(b.srcAlpha);
7715 blend.dstAlphaBlendFactor = toVkBlendFactor(b.dstAlpha);
7716 blend.alphaBlendOp = toVkBlendOp(b.opAlpha);
7717 blend.colorWriteMask = toVkColorComponents(b.colorWrite);
7718 vktargetBlends.append(blend);
7719 }
7720 if (vktargetBlends.isEmpty()) {
7721 VkPipelineColorBlendAttachmentState blend = {};
7722 blend.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT
7723 | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
7724 vktargetBlends.append(blend);
7725 }
7726 blendInfo.attachmentCount = uint32_t(vktargetBlends.size());
7727 blendInfo.pAttachments = vktargetBlends.constData();
7728 pipelineInfo.pColorBlendState = &blendInfo;
7729
7730 pipelineInfo.layout = layout;
7731
7733 pipelineInfo.renderPass = QRHI_RES(const QVkRenderPassDescriptor, m_renderPassDesc)->rp;
7734
7735 err = rhiD->df->vkCreateGraphicsPipelines(rhiD->dev, rhiD->pipelineCache, 1, &pipelineInfo, nullptr, &pipeline);
7736
7737 for (VkShaderModule shader : shaders)
7738 rhiD->df->vkDestroyShaderModule(rhiD->dev, shader, nullptr);
7739
7740 if (err != VK_SUCCESS) {
7741 qWarning("Failed to create graphics pipeline: %d", err);
7742 return false;
7743 }
7744
7745 rhiD->pipelineCreationEnd();
7747 generation += 1;
7748 rhiD->registerResource(this);
7749 return true;
7750}
7751
7756
7761
7763{
7764 if (!pipeline && !layout)
7765 return;
7766
7770
7771 e.pipelineState.pipeline = pipeline;
7772 e.pipelineState.layout = layout;
7773
7774 pipeline = VK_NULL_HANDLE;
7775 layout = VK_NULL_HANDLE;
7776
7778 if (rhiD) {
7779 rhiD->releaseQueue.append(e);
7780 rhiD->unregisterResource(this);
7781 }
7782}
7783
7785{
7786 if (pipeline)
7787 destroy();
7788
7790 rhiD->pipelineCreationStart();
7791 if (!rhiD->ensurePipelineCache())
7792 return false;
7793
7794 VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
7795 pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
7796 pipelineLayoutInfo.setLayoutCount = 1;
7798 Q_ASSERT(m_shaderResourceBindings && srbD->layout);
7799 pipelineLayoutInfo.pSetLayouts = &srbD->layout;
7800 VkResult err = rhiD->df->vkCreatePipelineLayout(rhiD->dev, &pipelineLayoutInfo, nullptr, &layout);
7801 if (err != VK_SUCCESS) {
7802 qWarning("Failed to create pipeline layout: %d", err);
7803 return false;
7804 }
7805
7806 VkComputePipelineCreateInfo pipelineInfo = {};
7807 pipelineInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
7808 pipelineInfo.layout = layout;
7809
7811 qWarning("Compute pipeline requires a compute shader stage");
7812 return false;
7813 }
7814 const QShader bakedShader = m_shaderStage.shader();
7815 const QShaderCode spirv = bakedShader.shader({ QShader::SpirvShader, 100, m_shaderStage.shaderVariant() });
7816 if (spirv.shader().isEmpty()) {
7817 qWarning() << "No SPIR-V 1.0 shader code found in baked shader" << bakedShader;
7818 return false;
7819 }
7820 if (bakedShader.stage() != QShader::ComputeStage) {
7821 qWarning() << bakedShader << "is not a compute shader";
7822 return false;
7823 }
7824 VkShaderModule shader = rhiD->createShader(spirv.shader());
7825 VkPipelineShaderStageCreateInfo shaderInfo = {};
7826 shaderInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
7827 shaderInfo.stage = VK_SHADER_STAGE_COMPUTE_BIT;
7828 shaderInfo.module = shader;
7829 shaderInfo.pName = spirv.entryPoint().constData();
7830 pipelineInfo.stage = shaderInfo;
7831
7832 err = rhiD->df->vkCreateComputePipelines(rhiD->dev, rhiD->pipelineCache, 1, &pipelineInfo, nullptr, &pipeline);
7833 rhiD->df->vkDestroyShaderModule(rhiD->dev, shader, nullptr);
7834 if (err != VK_SUCCESS) {
7835 qWarning("Failed to create graphics pipeline: %d", err);
7836 return false;
7837 }
7838
7839 rhiD->pipelineCreationEnd();
7841 generation += 1;
7842 rhiD->registerResource(this);
7843 return true;
7844}
7845
7851
7856
7858{
7859 // nothing to do here, cb is not owned by us
7860}
7861
7863{
7864 // Ok this is messy but no other way has been devised yet. Outside
7865 // begin(Compute)Pass - end(Compute)Pass it is simple - just return the
7866 // primary VkCommandBuffer. Inside, however, we need to provide the current
7867 // secondary command buffer (typically the one started by beginExternal(),
7868 // in case we are between beginExternal - endExternal inside a pass).
7869
7871 nativeHandlesStruct.commandBuffer = cb;
7872 } else {
7875 else
7876 nativeHandlesStruct.commandBuffer = cb;
7877 }
7878
7879 return &nativeHandlesStruct;
7880}
7881
7883 : QRhiSwapChain(rhi),
7884 rtWrapper(rhi, this),
7885 rtWrapperRight(rhi, this),
7886 cbWrapper(rhi)
7887{
7888}
7889
7894
7896{
7897 if (sc == VK_NULL_HANDLE)
7898 return;
7899
7901 if (rhiD) {
7902 rhiD->swapchains.remove(this);
7903 rhiD->releaseSwapChainResources(this);
7904 }
7905
7906 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
7908 frame.cmdBuf = VK_NULL_HANDLE;
7909 frame.timestampQueryIndex = -1;
7910 }
7911
7912 surface = lastConnectedSurface = VK_NULL_HANDLE;
7913
7914 if (rhiD)
7915 rhiD->unregisterResource(this);
7916}
7917
7922
7927
7932
7934{
7935 if (!ensureSurface())
7936 return QSize();
7937
7938 // The size from the QWindow may not exactly match the surface... so if a
7939 // size is reported from the surface, use that.
7940 VkSurfaceCapabilitiesKHR surfaceCaps = {};
7942 rhiD->vkGetPhysicalDeviceSurfaceCapabilitiesKHR(rhiD->physDev, surface, &surfaceCaps);
7943 VkExtent2D bufferSize = surfaceCaps.currentExtent;
7944 if (bufferSize.width == uint32_t(-1)) {
7945 Q_ASSERT(bufferSize.height == uint32_t(-1));
7946 return m_window->size() * m_window->devicePixelRatio();
7947 }
7948 return QSize(int(bufferSize.width), int(bufferSize.height));
7949}
7950
7951static inline bool hdrFormatMatchesVkSurfaceFormat(QRhiSwapChain::Format f, const VkSurfaceFormatKHR &s)
7952{
7953 switch (f) {
7955 return s.format == VK_FORMAT_R16G16B16A16_SFLOAT
7956 && s.colorSpace == VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT;
7958 return (s.format == VK_FORMAT_A2B10G10R10_UNORM_PACK32 || s.format == VK_FORMAT_A2R10G10B10_UNORM_PACK32)
7959 && s.colorSpace == VK_COLOR_SPACE_HDR10_ST2084_EXT;
7961 return s.format == VK_FORMAT_R16G16B16A16_SFLOAT
7962 && s.colorSpace == VK_COLOR_SPACE_DISPLAY_P3_LINEAR_EXT;
7963 default:
7964 break;
7965 }
7966 return false;
7967}
7968
7970{
7971 if (f == SDR)
7972 return true;
7973
7974 if (!m_window) {
7975 qWarning("Attempted to call isFormatSupported() without a window set");
7976 return false;
7977 }
7978
7979 // we may be called before create so query the surface
7980 VkSurfaceKHR surf = QVulkanInstance::surfaceForWindow(m_window);
7981
7983 uint32_t formatCount = 0;
7984 rhiD->vkGetPhysicalDeviceSurfaceFormatsKHR(rhiD->physDev, surf, &formatCount, nullptr);
7985 QVarLengthArray<VkSurfaceFormatKHR, 8> formats(formatCount);
7986 if (formatCount) {
7987 rhiD->vkGetPhysicalDeviceSurfaceFormatsKHR(rhiD->physDev, surf, &formatCount, formats.data());
7988 for (uint32_t i = 0; i < formatCount; ++i) {
7990 return true;
7991 }
7992 }
7993
7994 return false;
7995}
7996
7998{
7999 // not yet built so cannot rely on data computed in createOrResize()
8000
8001 if (!ensureSurface()) // make sure sampleCount and colorFormat reflect what was requested
8002 return nullptr;
8003
8006 if (!rhiD->createDefaultRenderPass(rp,
8007 m_depthStencil != nullptr,
8008 samples,
8009 colorFormat))
8010 {
8011 delete rp;
8012 return nullptr;
8013 }
8014
8015 rp->ownsRp = true;
8017 rhiD->registerResource(rp);
8018 return rp;
8019}
8020
8021static inline bool isSrgbFormat(VkFormat format)
8022{
8023 switch (format) {
8024 case VK_FORMAT_R8_SRGB:
8025 case VK_FORMAT_R8G8_SRGB:
8026 case VK_FORMAT_R8G8B8_SRGB:
8027 case VK_FORMAT_B8G8R8_SRGB:
8028 case VK_FORMAT_R8G8B8A8_SRGB:
8029 case VK_FORMAT_B8G8R8A8_SRGB:
8030 case VK_FORMAT_A8B8G8R8_SRGB_PACK32:
8031 return true;
8032 default:
8033 return false;
8034 }
8035}
8036
8038{
8039 // Do nothing when already done, however window may change so check the
8040 // surface is still the same. Some of the queries below are very expensive
8041 // with some implementations so it is important to do the rest only once
8042 // per surface.
8043
8045 VkSurfaceKHR surf = QVulkanInstance::surfaceForWindow(m_window);
8046 if (!surf) {
8047 qWarning("Failed to get surface for window");
8048 return false;
8049 }
8050 if (surface == surf)
8051 return true;
8052
8053 surface = surf;
8054
8056 if (!rhiD->inst->supportsPresent(rhiD->physDev, rhiD->gfxQueueFamilyIdx, m_window)) {
8057 qWarning("Presenting not supported on this window");
8058 return false;
8059 }
8060
8061 quint32 formatCount = 0;
8062 rhiD->vkGetPhysicalDeviceSurfaceFormatsKHR(rhiD->physDev, surface, &formatCount, nullptr);
8063 QList<VkSurfaceFormatKHR> formats(formatCount);
8064 if (formatCount)
8065 rhiD->vkGetPhysicalDeviceSurfaceFormatsKHR(rhiD->physDev, surface, &formatCount, formats.data());
8066
8067 // See if there is a better match than the default BGRA8 format. (but if
8068 // not, we will stick to the default)
8069 const bool srgbRequested = m_flags.testFlag(sRGB);
8070 for (int i = 0; i < int(formatCount); ++i) {
8071 if (formats[i].format != VK_FORMAT_UNDEFINED) {
8072 bool ok = srgbRequested == isSrgbFormat(formats[i].format);
8073 if (m_format != SDR)
8075 if (ok) {
8076 colorFormat = formats[i].format;
8077 colorSpace = formats[i].colorSpace;
8078 break;
8079 }
8080 }
8081 }
8082
8083 samples = rhiD->effectiveSampleCountBits(m_sampleCount);
8084
8085 quint32 presModeCount = 0;
8086 rhiD->vkGetPhysicalDeviceSurfacePresentModesKHR(rhiD->physDev, surface, &presModeCount, nullptr);
8087 supportedPresentationModes.resize(presModeCount);
8088 rhiD->vkGetPhysicalDeviceSurfacePresentModesKHR(rhiD->physDev, surface, &presModeCount,
8090
8091 return true;
8092}
8093
8095{
8097 const bool needsRegistration = !window || window != m_window;
8098
8099 // Can be called multiple times due to window resizes - that is not the
8100 // same as a simple destroy+create (as with other resources). Thus no
8101 // destroy() here. See recreateSwapChain().
8102
8103 // except if the window actually changes
8104 if (window && window != m_window)
8105 destroy();
8106
8107 window = m_window;
8110
8111 if (!rhiD->recreateSwapChain(this)) {
8112 qWarning("Failed to create new swapchain");
8113 return false;
8114 }
8115
8116 if (needsRegistration)
8117 rhiD->swapchains.insert(this);
8118
8120 qWarning("Depth-stencil buffer's sampleCount (%d) does not match color buffers' sample count (%d). Expect problems.",
8122 }
8126 if (!m_depthStencil->create())
8127 qWarning("Failed to rebuild swapchain's associated depth-stencil buffer for size %dx%d",
8129 } else {
8130 qWarning("Depth-stencil buffer's size (%dx%d) does not match the surface size (%dx%d). Expect problems.",
8133 }
8134 }
8135
8136 if (!m_renderPassDesc)
8137 qWarning("QVkSwapChain: No renderpass descriptor set. See newCompatibleRenderPassDescriptor() and setRenderPassDescriptor().");
8138
8139 rtWrapper.setRenderPassDescriptor(m_renderPassDesc); // for the public getter in QRhiRenderTarget
8142
8144 rtWrapper.d.dpr = float(window->devicePixelRatio());
8147 if (m_depthStencil) {
8150 } else {
8152 ds = nullptr;
8153 }
8155 if (samples > VK_SAMPLE_COUNT_1_BIT)
8157 else
8159
8160 for (int i = 0; i < bufferCount; ++i) {
8162 VkImageView views[3] = { // color, ds, resolve
8163 samples > VK_SAMPLE_COUNT_1_BIT ? image.msaaImageView : image.imageView,
8164 ds ? ds->imageView : VK_NULL_HANDLE,
8165 samples > VK_SAMPLE_COUNT_1_BIT ? image.imageView : VK_NULL_HANDLE
8166 };
8167
8168 VkFramebufferCreateInfo fbInfo = {};
8169 fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
8170 fbInfo.renderPass = rtWrapper.d.rp->rp;
8172 fbInfo.pAttachments = views;
8173 fbInfo.width = uint32_t(pixelSize.width());
8174 fbInfo.height = uint32_t(pixelSize.height());
8175 fbInfo.layers = 1;
8176
8177 VkResult err = rhiD->df->vkCreateFramebuffer(rhiD->dev, &fbInfo, nullptr, &image.fb);
8178 if (err != VK_SUCCESS) {
8179 qWarning("Failed to create framebuffer: %d", err);
8180 return false;
8181 }
8182 }
8183
8184 if (stereo) {
8186 m_renderPassDesc); // for the public getter in QRhiRenderTarget
8189
8191 rtWrapperRight.d.dpr = float(window->devicePixelRatio());
8194 if (m_depthStencil) {
8197 } else {
8199 ds = nullptr;
8200 }
8202 if (samples > VK_SAMPLE_COUNT_1_BIT)
8204 else
8206
8207 for (int i = 0; i < bufferCount; ++i) {
8209 VkImageView views[3] = {
8210 // color, ds, resolve
8211 samples > VK_SAMPLE_COUNT_1_BIT ? image.msaaImageView : image.imageView,
8212 ds ? ds->imageView : VK_NULL_HANDLE,
8213 samples > VK_SAMPLE_COUNT_1_BIT ? image.imageView : VK_NULL_HANDLE
8214 };
8215
8216 VkFramebufferCreateInfo fbInfo = {};
8217 fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
8218 fbInfo.renderPass = rtWrapperRight.d.rp->rp;
8219 fbInfo.attachmentCount = uint32_t(rtWrapperRight.d.colorAttCount + rtWrapperRight.d.dsAttCount
8221 fbInfo.pAttachments = views;
8222 fbInfo.width = uint32_t(pixelSize.width());
8223 fbInfo.height = uint32_t(pixelSize.height());
8224 fbInfo.layers = 1;
8225
8226 VkResult err = rhiD->df->vkCreateFramebuffer(rhiD->dev, &fbInfo, nullptr, &image.fb);
8227 if (err != VK_SUCCESS) {
8228 qWarning("Failed to create framebuffer: %d", err);
8229 return false;
8230 }
8231 }
8232 }
8233
8234 frameCount = 0;
8235
8236 if (needsRegistration)
8237 rhiD->registerResource(this);
8238
8239 return true;
8240}
8241
IOBluetoothDevice * device
bool testBit(qsizetype i) const
Returns true if the bit at index position i is 1; otherwise returns false.
Definition qbitarray.h:89
void setBit(qsizetype i)
Sets the bit at index position i to 1.
Definition qbitarray.h:91
void clearBit(qsizetype i)
Sets the bit at index position i to 0.
Definition qbitarray.h:95
qsizetype size() const
Returns the number of bits stored in the bit array.
Definition qbitarray.h:76
\inmodule QtCore
\inmodule QtCore
Definition qbytearray.h:57
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:611
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:494
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:124
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:107
static QByteArray number(int, int base=10)
Returns a byte-array representing the whole number n as text.
void resize(qsizetype size)
Sets the size of the byte array to size bytes.
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
\inmodule QtGui
Definition qimage.h:37
qsizetype sizeInBytes() const
Definition qimage.cpp:1548
bool isNull() const
Returns true if it is a null image, otherwise returns false.
Definition qimage.cpp:1222
The QMatrix4x4 class represents a 4x4 transformation matrix in 3D space.
Definition qmatrix4x4.h:25
\inmodule QtCore\reentrant
Definition qpoint.h:25
constexpr bool isNull() const noexcept
Returns true if both the x and y coordinates are set to 0, otherwise returns false.
Definition qpoint.h:125
constexpr int x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:130
constexpr int y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:135
quint32 size() const
Definition qrhi_p.h:357
const char * constData() const
Definition qrhi_p.h:353
\inmodule QtGui
Definition qrhi.h:846
UsageFlags m_usage
Definition qrhi.h:888
Type m_type
Definition qrhi.h:887
Type
Specifies storage type of buffer resource.
Definition qrhi.h:848
@ Immutable
Definition qrhi.h:849
@ Dynamic
Definition qrhi.h:851
@ IndexBuffer
Definition qrhi.h:856
@ VertexBuffer
Definition qrhi.h:855
@ UniformBuffer
Definition qrhi.h:857
@ StorageBuffer
Definition qrhi.h:858
quint32 m_size
Definition qrhi.h:889
\inmodule QtGui
Definition qrhi.h:576
\inmodule QtGui
Definition qrhi.h:1651
QPair< int, quint32 > DynamicOffset
Synonym for QPair<int, quint32>.
Definition qrhi.h:1676
QPair< QRhiBuffer *, quint32 > VertexInput
Synonym for QPair<QRhiBuffer *, quint32>.
Definition qrhi.h:1680
IndexFormat
Specifies the index data type.
Definition qrhi.h:1653
\inmodule QtGui
Definition qrhi.h:1622
QRhiShaderStage m_shaderStage
Definition qrhi.h:1644
QRhiShaderResourceBindings * m_shaderResourceBindings
Definition qrhi.h:1645
\inmodule QtGui
Definition qrhi.h:44
\inmodule QtGui
Definition qrhi.h:1270
QRhiRenderPassDescriptor * m_renderPassDesc
Definition qrhi.h:1503
quint32 m_stencilReadMask
Definition qrhi.h:1491
BlendOp
Specifies the blend operation.
Definition qrhi.h:1331
PolygonMode
Specifies the polygon rasterization mode.
Definition qrhi.h:1379
FrontFace
Specifies the front face winding order.
Definition qrhi.h:1296
BlendFactor
Specifies the blend factor.
Definition qrhi.h:1309
StencilOpState m_stencilFront
Definition qrhi.h:1489
quint32 m_stencilWriteMask
Definition qrhi.h:1492
CompareOp
Specifies the depth or stencil comparison function.
Definition qrhi.h:1350
Topology m_topology
Definition qrhi.h:1481
CullMode
Specifies the culling mode.
Definition qrhi.h:1290
QVarLengthArray< QRhiShaderStage, 4 > m_shaderStages
Definition qrhi.h:1500
QRhiVertexInputLayout m_vertexInputLayout
Definition qrhi.h:1501
QVarLengthArray< TargetBlend, 8 > m_targetBlends
Definition qrhi.h:1484
QRhiShaderResourceBindings * m_shaderResourceBindings
Definition qrhi.h:1502
PolygonMode m_polygonMode
Definition qrhi.h:1498
float m_slopeScaledDepthBias
Definition qrhi.h:1496
Topology
Specifies the primitive topology.
Definition qrhi.h:1280
StencilOpState m_stencilBack
Definition qrhi.h:1490
FrontFace m_frontFace
Definition qrhi.h:1483
StencilOp
Specifies the stencil operation.
Definition qrhi.h:1361
int m_patchControlPointCount
Definition qrhi.h:1497
CullMode m_cullMode
Definition qrhi.h:1482
CompareOp m_depthOp
Definition qrhi.h:1487
int effectiveSampleCount(int sampleCount) const
Definition qrhi.cpp:8386
bool isCompressedFormat(QRhiTexture::Format format) const
Definition qrhi.cpp:8058
static const QRhiShaderResourceBinding::Data * shaderResourceBindingData(const QRhiShaderResourceBinding &binding)
Definition qrhi_p.h:220
quint32 pipelineCacheRhiId() const
Definition qrhi_p.h:196
void compressedFormatInfo(QRhiTexture::Format format, const QSize &size, quint32 *bpl, quint32 *byteSize, QSize *blockDim) const
Definition qrhi.cpp:8065
static bool sortedBindingLessThan(const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b)
Definition qrhi_p.h:230
qint64 totalPipelineCreationTime() const
Definition qrhi_p.h:212
void textureFormatInfo(QRhiTexture::Format format, const QSize &size, quint32 *bpl, quint32 *byteSize, quint32 *bytesPerPixel) const
Definition qrhi.cpp:8185
static TextureStage toPassTrackerTextureStage(QRhiShaderResourceBinding::StageFlags stages)
Definition qrhi.cpp:11088
static BufferStage toPassTrackerBufferStage(QRhiShaderResourceBinding::StageFlags stages)
Definition qrhi.cpp:11069
int layer() const
Definition qrhi.h:785
QRhiTexture * texture() const
Definition qrhi.h:782
int level() const
Definition qrhi.h:788
\inmodule QtGui
Definition qrhi.h:1094
Flags flags() const
Definition qrhi.h:1121
void setPixelSize(const QSize &sz)
Sets the size (in pixels) to sz.
Definition qrhi.h:1116
QSize pixelSize() const
Definition qrhi.h:1115
int sampleCount() const
Definition qrhi.h:1118
int m_sampleCount
Definition qrhi.h:1134
QRhiTexture::Format m_backingFormatHint
Definition qrhi.h:1136
QSize m_pixelSize
Definition qrhi.h:1133
Type
Specifies the type of the renderbuffer.
Definition qrhi.h:1096
virtual bool create()=0
Creates the corresponding native graphics resources.
@ UsedWithSwapChainOnly
Definition qrhi.h:1102
\inmodule QtGui
Definition qrhi.h:1142
\inmodule QtGui
Definition qrhi.h:1158
void setRenderPassDescriptor(QRhiRenderPassDescriptor *desc)
Sets the QRhiRenderPassDescriptor desc for use with this render target.
Definition qrhi.h:1165
virtual QSize pixelSize() const =0
QRhiRenderPassDescriptor * m_renderPassDesc
Definition qrhi.h:1169
static QRhiResourceUpdateBatchPrivate * get(QRhiResourceUpdateBatch *b)
Definition qrhi_p.h:536
\inmodule QtGui
Definition qrhi.h:1731
\inmodule QtGui
Definition qrhi.h:804
QByteArray m_objectName
Definition qrhi.h:842
@ SwapChainRenderTarget
Definition qrhi.h:812
@ TextureRenderTarget
Definition qrhi.h:813
virtual Type resourceType() const =0
void setName(const QByteArray &name)
Sets a name for the object.
Definition qrhi.cpp:3581
QRhiImplementation * m_rhi
Definition qrhi.h:840
\inmodule QtGui
Definition qrhi.h:1030
Filter m_minFilter
Definition qrhi.h:1085
Filter
Specifies the minification, magnification, or mipmap filtering.
Definition qrhi.h:1032
AddressMode m_addressV
Definition qrhi.h:1088
Filter m_mipmapMode
Definition qrhi.h:1086
AddressMode m_addressU
Definition qrhi.h:1087
AddressMode
Specifies the addressing mode.
Definition qrhi.h:1038
@ ClampToEdge
Definition qrhi.h:1040
CompareOp
Specifies the texture comparison function.
Definition qrhi.h:1044
@ LessOrEqual
Definition qrhi.h:1048
@ GreaterOrEqual
Definition qrhi.h:1051
CompareOp m_compareOp
Definition qrhi.h:1090
AddressMode m_addressW
Definition qrhi.h:1089
Filter m_magFilter
Definition qrhi.h:1084
\inmodule QtGui
Definition qrhi.h:138
std::array< int, 4 > scissor() const
Definition qrhi.h:143
\inmodule QtGui
Definition qrhi.h:439
Type
Specifies type of the shader resource bound to a binding point.
Definition qrhi.h:441
\inmodule QtGui
Definition qrhi.h:1214
QVarLengthArray< QRhiShaderResourceBinding, BINDING_PREALLOC > m_bindings
Definition qrhi.h:1246
\inmodule QtGui
Definition qrhi.h:379
QShader::Variant shaderVariant() const
Definition qrhi.h:400
QShader shader() const
Definition qrhi.h:397
Type
Specifies the type of the shader stage.
Definition qrhi.h:381
@ TessellationControl
Definition qrhi.h:383
@ TessellationEvaluation
Definition qrhi.h:384
Type type() const
Definition qrhi.h:394
\inmodule QtGui
Definition qrhi.h:1173
\inmodule QtGui
Definition qrhi.h:1549
QWindow * m_window
Definition qrhi.h:1609
int m_sampleCount
Definition qrhi.h:1613
@ SurfaceHasNonPreMulAlpha
Definition qrhi.h:1553
@ UsedAsTransferSource
Definition qrhi.h:1555
@ MinimalBufferCount
Definition qrhi.h:1557
@ SurfaceHasPreMulAlpha
Definition qrhi.h:1552
QRhiRenderPassDescriptor * m_renderPassDesc
Definition qrhi.h:1614
QSize m_currentPixelSize
Definition qrhi.h:1615
Flags m_flags
Definition qrhi.h:1610
Format
Describes the swapchain format.
Definition qrhi.h:1561
@ HDRExtendedSrgbLinear
Definition qrhi.h:1563
@ HDRExtendedDisplayP3Linear
Definition qrhi.h:1565
StereoTargetBuffer
Selects the backbuffer to use with a stereoscopic swapchain.
Definition qrhi.h:1568
Format m_format
Definition qrhi.h:1611
QRhiRenderBuffer * m_depthStencil
Definition qrhi.h:1612
QPoint destinationTopLeft() const
Definition qrhi.h:761
QPoint sourceTopLeft() const
Definition qrhi.h:752
int destinationLevel() const
Definition qrhi.h:758
int sourceLevel() const
Definition qrhi.h:749
QSize pixelSize() const
Definition qrhi.h:743
int sourceLayer() const
Definition qrhi.h:746
int destinationLayer() const
Definition qrhi.h:755
const QRhiColorAttachment * cbeginColorAttachments() const
Definition qrhi.h:634
QRhiTexture * depthTexture() const
Definition qrhi.h:642
const QRhiColorAttachment * cendColorAttachments() const
Definition qrhi.h:635
QRhiRenderBuffer * depthStencilBuffer() const
Definition qrhi.h:639
qsizetype colorAttachmentCount() const
Definition qrhi.h:637
QRhiTexture * depthResolveTexture() const
Definition qrhi.h:645
\inmodule QtGui
Definition qrhi.h:1184
QRhiTextureRenderTargetDescription m_desc
Definition qrhi.h:1207
\inmodule QtGui
Definition qrhi.h:895
QSize m_pixelSize
Definition qrhi.h:1016
int m_arraySize
Definition qrhi.h:1018
int m_depth
Definition qrhi.h:1017
int arraySize() const
Definition qrhi.h:981
@ ThreeDimensional
Definition qrhi.h:907
@ UsedAsTransferSource
Definition qrhi.h:902
@ UsedWithLoadStore
Definition qrhi.h:904
@ UsedWithGenerateMips
Definition qrhi.h:903
@ MipMapped
Definition qrhi.h:900
@ RenderTarget
Definition qrhi.h:898
@ OneDimensional
Definition qrhi.h:910
@ TextureArray
Definition qrhi.h:909
@ CubeMap
Definition qrhi.h:899
ViewFormat m_writeViewFormat
Definition qrhi.h:1024
int m_arrayRangeLength
Definition qrhi.h:1022
Format
Specifies the texture format.
Definition qrhi.h:914
@ ASTC_10x8
Definition qrhi.h:959
@ ASTC_12x12
Definition qrhi.h:962
@ ASTC_8x5
Definition qrhi.h:954
@ ASTC_10x5
Definition qrhi.h:957
@ RGBA32F
Definition qrhi.h:926
@ ETC2_RGBA8
Definition qrhi.h:947
@ ASTC_5x5
Definition qrhi.h:951
@ ASTC_4x4
Definition qrhi.h:949
@ ASTC_6x6
Definition qrhi.h:953
@ ASTC_12x10
Definition qrhi.h:961
@ ETC2_RGB8
Definition qrhi.h:945
@ ASTC_5x4
Definition qrhi.h:950
@ RED_OR_ALPHA8
Definition qrhi.h:923
@ ASTC_6x5
Definition qrhi.h:952
@ ASTC_8x8
Definition qrhi.h:956
@ RGBA16F
Definition qrhi.h:925
@ RGB10A2
Definition qrhi.h:930
@ ASTC_10x6
Definition qrhi.h:958
@ ASTC_10x10
Definition qrhi.h:960
@ UnknownFormat
Definition qrhi.h:915
@ ETC2_RGB8A1
Definition qrhi.h:946
@ ASTC_8x6
Definition qrhi.h:955
ViewFormat m_readViewFormat
Definition qrhi.h:1023
void setSampleCount(int s)
Sets the sample count to s.
Definition qrhi.h:996
Format m_format
Definition qrhi.h:1015
Flags m_flags
Definition qrhi.h:1020
int m_arrayRangeStart
Definition qrhi.h:1021
int m_sampleCount
Definition qrhi.h:1019
void setPixelSize(const QSize &sz)
Sets the texture size, specified in pixels, to sz.
Definition qrhi.h:976
Format
Specifies the type of the element data.
Definition qrhi.h:234
const QRhiVertexInputAttribute * cendAttributes() const
Definition qrhi.h:345
const QRhiVertexInputBinding * cendBindings() const
Definition qrhi.h:333
const QRhiVertexInputAttribute * cbeginAttributes() const
Definition qrhi.h:344
const QRhiVertexInputBinding * cbeginBindings() const
Definition qrhi.h:332
\inmodule QtGui
Definition qrhi.h:85
\inmodule QtGui
\variable QRhiVulkanInitParams::inst
QVarLengthArray< DescriptorPoolData, 8 > descriptorPools
void recordTransitionPassResources(QVkCommandBuffer *cbD, const QRhiPassResourceTracker &tracker)
QVulkanFunctions * f
quint32 gfxQueueFamilyIdx
VkCommandBuffer startSecondaryCommandBuffer(QVkRenderTargetData *rtD=nullptr)
QRhiSwapChain * createSwapChain() override
PFN_vkGetSwapchainImagesKHR vkGetSwapchainImagesKHR
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)
VkPhysicalDeviceProperties physDevProperties
void executeDeferredReleases(bool forced=false)
uint32_t chooseTransientImageMemType(VkImage img, uint32_t startIndex)
QRhi::FrameOpResult finish() override
quint32 timestampValidBits
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
QByteArrayList requestedDeviceExtensions
bool createDefaultRenderPass(QVkRenderPassDescriptor *rpD, bool hasDepthStencil, VkSampleCountFlagBits samples, VkFormat colorFormat)
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
QRhiRenderBuffer * createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize, int sampleCount, QRhiRenderBuffer::Flags flags, QRhiTexture::Format backingFormatHint) override
QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) override
QBitArray timestampQueryPoolMap
double elapsedSecondsFromTimestamp(quint64 timestamp[2], bool *ok)
VkFormat optimalDsFormat
QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags) override
void trackedBufferBarrier(QVkCommandBuffer *cbD, QVkBuffer *bufD, int slot, VkAccessFlags access, VkPipelineStageFlags stage)
void endExternal(QRhiCommandBuffer *cb) override
QWindow * maybeWindow
void dispatch(QRhiCommandBuffer *cb, int x, int y, int z) override
VkPhysicalDeviceFeatures physDevFeatures
QRhiVulkanNativeHandles nativeHandlesStruct
bool allocateDescriptorSet(VkDescriptorSetAllocateInfo *allocInfo, VkDescriptorSet *result, int *resultPoolIndex)
void updateShaderResourceBindings(QRhiShaderResourceBindings *srb, int descSetIdx=-1)
bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const override
VkDevice dev
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)
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
QRhi::Flags rhiFlags
PFN_vkCreateSwapchainKHR vkCreateSwapchainKHR
QRhiGraphicsPipeline * createGraphicsPipeline() override
QRhiComputePipeline * createComputePipeline() override
struct QRhiVulkan::@339 caps
QRhiTexture * createTexture(QRhiTexture::Format format, const QSize &pixelSize, int depth, int arraySize, int sampleCount, QRhiTexture::Flags flags) override
void waitCommandCompletion(int frameSlot)
void debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) override
PFN_vkGetPhysicalDeviceSurfacePresentModesKHR vkGetPhysicalDeviceSurfacePresentModesKHR
bool recreateSwapChain(QRhiSwapChain *swapChain)
PFN_vkQueuePresentKHR vkQueuePresentKHR
bool ensurePipelineCache(const void *initialData=nullptr, size_t initialDataSize=0)
QRhiDriverInfo driverInfoStruct
void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override
QVarLengthArray< TextureReadback, 2 > activeTextureReadbacks
void depthStencilExplicitBarrier(QVkCommandBuffer *cbD, QVkRenderBuffer *rbD)
void trackedImageBarrier(QVkCommandBuffer *cbD, QVkTexture *texD, VkImageLayout layout, VkAccessFlags access, VkPipelineStageFlags stage)
VkCommandPool cmdPool[QVK_FRAMES_IN_FLIGHT]
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
PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR
QVarLengthArray< BufferReadback, 2 > activeBufferReadbacks
void recordPrimaryCommandBuffer(QVkCommandBuffer *cbD)
bool isYUpInFramebuffer() const override
void debugMarkEnd(QRhiCommandBuffer *cb) override
QVulkanInstance * inst
QRhiSampler * createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter, QRhiSampler::Filter mipmapMode, QRhiSampler::AddressMode u, QRhiSampler::AddressMode v, QRhiSampler::AddressMode w) override
PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vkGetPhysicalDeviceSurfaceCapabilitiesKHR
void releaseSwapChainResources(QRhiSwapChain *swapChain)
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
VkQueryPool timestampQueryPool
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 activateTextureRenderTarget(QVkCommandBuffer *cbD, QVkTextureRenderTarget *rtD)
bool createOffscreenRenderPass(QVkRenderPassDescriptor *rpD, const QRhiColorAttachment *colorAttachmentsBegin, const QRhiColorAttachment *colorAttachmentsEnd, bool preserveColor, bool preserveDs, bool storeDs, QRhiRenderBuffer *depthStencilBuffer, QRhiTexture *depthTexture, QRhiTexture *depthResolveTexture)
PFN_vkDestroySwapchainKHR vkDestroySwapchainKHR
void executeBufferHostWritesForSlot(QVkBuffer *bufD, int slot)
bool isYUpInNDC() const override
const QRhiNativeHandles * nativeHandles() 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
VkPipelineCache pipelineCache
VkDeviceSize ubufAlign
void finishActiveReadbacks(bool forced=false)
void ensureCommandPoolForNewFrame()
QByteArray pipelineCacheData() override
quint32 gfxQueueIdx
VkDeviceSize texbufAlign
QList< DeferredReleaseEntry > releaseQueue
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
QRhiBuffer * createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, quint32 size) override
bool isClipDepthZeroToOne() const override
void enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdateBatch *resourceUpdates)
QVarLengthArray< VkCommandBuffer, 4 > freeSecondaryCbs[QVK_FRAMES_IN_FLIGHT]
void setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBindings *srb, int dynamicOffsetCount, const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) override
QVkAllocator allocator
bool importedAllocator
VkQueue gfxQueue
PFN_vkAcquireNextImageKHR vkAcquireNextImageKHR
QMatrix4x4 clipSpaceCorrMatrix() const override
struct QRhiVulkan::OffscreenFrame ofr
QSet< QVkSwapChain * > swapchains
int ubufAlignment() const override
QRhiDriverInfo driverInfo() const override
void beginExternal(QRhiCommandBuffer *cb) override
void setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) override
QRhi::FrameOpResult endAndSubmitPrimaryCommandBuffer(VkCommandBuffer cb, VkFence cmdFence, VkSemaphore *waitSem, VkSemaphore *signalSem)
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override
VkSampleCountFlagBits effectiveSampleCountBits(int sampleCount)
VkPhysicalDevice physDev
bool makeThreadLocalNativeContextCurrent() override
static constexpr int MAX_MIP_LEVELS
Definition qrhi.h:1997
ResourceLimit
Describes the resource limit to query.
Definition qrhi.h:1886
@ MaxThreadsPerThreadGroup
Definition qrhi.h:1893
@ MaxThreadGroupZ
Definition qrhi.h:1896
@ FramesInFlight
Definition qrhi.h:1890
@ TextureSizeMin
Definition qrhi.h:1887
@ MaxThreadGroupsPerDimension
Definition qrhi.h:1892
@ MaxAsyncReadbackFrames
Definition qrhi.h:1891
@ TextureArraySizeMax
Definition qrhi.h:1897
@ MaxColorAttachments
Definition qrhi.h:1889
@ MaxThreadGroupY
Definition qrhi.h:1895
@ MaxVertexInputs
Definition qrhi.h:1899
@ MaxThreadGroupX
Definition qrhi.h:1894
@ TextureSizeMax
Definition qrhi.h:1888
@ MaxVertexOutputs
Definition qrhi.h:1900
@ MaxUniformBufferRange
Definition qrhi.h:1898
@ SkipPresent
Definition qrhi.h:1882
Feature
Flag values to indicate what features are supported by the backend currently in use.
Definition qrhi.h:1831
@ HalfAttributes
Definition qrhi.h:1869
@ CustomInstanceStepRate
Definition qrhi.h:1837
@ NonDynamicUniformBuffers
Definition qrhi.h:1839
@ ElementIndexUint
Definition qrhi.h:1843
@ RenderToNonBaseMipLevel
Definition qrhi.h:1853
@ MultisampleRenderBuffer
Definition qrhi.h:1833
@ RenderTo3DTextureSlice
Definition qrhi.h:1861
@ Tessellation
Definition qrhi.h:1863
@ IntAttributes
Definition qrhi.h:1854
@ TextureArrays
Definition qrhi.h:1862
@ PipelineCacheDataLoadSave
Definition qrhi.h:1857
@ ReadBackNonUniformBuffer
Definition qrhi.h:1850
@ MultiView
Definition qrhi.h:1872
@ TexelFetch
Definition qrhi.h:1852
@ TextureArrayRange
Definition qrhi.h:1865
@ RenderToOneDimensionalTexture
Definition qrhi.h:1870
@ BaseVertex
Definition qrhi.h:1847
@ GeometryShader
Definition qrhi.h:1864
@ Compute
Definition qrhi.h:1844
@ OneDimensionalTextureMipmaps
Definition qrhi.h:1868
@ WideLines
Definition qrhi.h:1845
@ TriangleFanTopology
Definition qrhi.h:1849
@ OneDimensionalTextures
Definition qrhi.h:1867
@ ImageDataStride
Definition qrhi.h:1858
@ TextureViewFormat
Definition qrhi.h:1873
@ BaseInstance
Definition qrhi.h:1848
@ DebugMarkers
Definition qrhi.h:1834
@ ReadBackNonBaseMipLevel
Definition qrhi.h:1851
@ MultisampleTexture
Definition qrhi.h:1832
@ ThreeDimensionalTextureMipmaps
Definition qrhi.h:1871
@ NonFourAlignedEffectiveIndexBufferOffset
Definition qrhi.h:1840
@ RedOrAlpha8IsRed
Definition qrhi.h:1842
@ NonFillPolygonMode
Definition qrhi.h:1866
@ Timestamps
Definition qrhi.h:1835
@ ThreeDimensionalTextures
Definition qrhi.h:1860
@ PrimitiveRestart
Definition qrhi.h:1838
@ ReadBackAnyTextureFormat
Definition qrhi.h:1856
@ RenderBufferImport
Definition qrhi.h:1859
@ ScreenSpaceDerivatives
Definition qrhi.h:1855
@ VertexShaderPointSize
Definition qrhi.h:1846
@ NPOTTextureRepeat
Definition qrhi.h:1841
@ Instancing
Definition qrhi.h:1836
@ ResolveDepthStencil
Definition qrhi.h:1874
FrameOpResult
Describes the result of operations that can have a soft failure.
Definition qrhi.h:1824
@ FrameOpSuccess
Definition qrhi.h:1825
@ FrameOpSwapChainOutOfDate
Definition qrhi.h:1827
@ FrameOpDeviceLost
Definition qrhi.h:1828
@ FrameOpError
Definition qrhi.h:1826
@ EnablePipelineCacheDataSave
Definition qrhi.h:1818
@ PreferSoftwareRenderer
Definition qrhi.h:1817
@ EnableTimestamps
Definition qrhi.h:1819
\inmodule QtGui
Definition qshader.h:60
QByteArray shader() const
Definition qshader.h:65
\inmodule QtGui
Definition qshader.h:81
QShaderCode shader(const QShaderKey &key) const
Definition qshader.cpp:395
@ SpirvShader
Definition qshader.h:93
@ ComputeStage
Definition qshader.h:89
\inmodule QtCore
Definition qsize.h:25
constexpr int height() const noexcept
Returns the height.
Definition qsize.h:133
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:130
constexpr bool isEmpty() const noexcept
Returns true if either of the width and height is less than or equal to 0; otherwise returns false.
Definition qsize.h:124
constexpr size_type size() const noexcept
bool isEmpty() const
void resize(qsizetype sz)
const_iterator cbegin() const noexcept
const_iterator cend() const noexcept
iterator end() noexcept
void append(const T &t)
T * data() noexcept
iterator begin() noexcept
\inmodule QtCore
The QVulkanDeviceFunctions class provides cross-platform access to the device level core Vulkan 1....
The QVulkanInstance class represents a native Vulkan instance, enabling Vulkan rendering onto a QSurf...
QSize size() const override
Returns the size of the window excluding any window frame.
Definition qwindow.h:210
static VulkanServerBufferGlFunctions * funcs
#define this
Definition dialogs.cpp:9
QSet< QString >::iterator it
EGLint EGLint * formats
Combined button and popup list for selecting options.
Definition image.cpp:4
#define Q_STATIC_ASSERT(Condition)
Definition qassert.h:108
#define QByteArrayLiteral(str)
Definition qbytearray.h:52
#define QT_WARNING_POP
#define QT_WARNING_DISABLE_GCC(text)
#define QT_WARNING_PUSH
#define QT_WARNING_DISABLE_CLANG(text)
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage void
static int instanceCount
static QString header(const QString &name)
static const qint64 headerSize
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
EGLOutputLayerEXT layer
bool qFuzzyIsNull(qfloat16 f) noexcept
Definition qfloat16.h:349
Flags
@ QtDebugMsg
Definition qlogging.h:30
#define qWarning
Definition qlogging.h:166
#define qCDebug(category,...)
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
GLboolean GLboolean GLboolean b
GLbitfield stages
GLsizei const GLfloat * v
[13]
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat z
GLint GLint GLint GLint GLint x
[0]
GLuint const GLuint * buffers
GLint GLenum GLsizei GLsizei GLsizei depth
GLsizei samples
GLenum mode
const GLfloat * m
GLenum GLuint GLint level
GLfloat GLfloat GLfloat w
[0]
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLboolean r
[2]
GLuint GLuint end
GLenum GLsizei dataSize
GLuint sampler
GLenum GLenum GLsizei count
const GLenum * bufs
GLuint object
[3]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLfloat GLfloat f
GLenum src
GLuint color
[2]
GLenum type
GLenum GLenum dst
GLuint GLsizei const GLchar * label
[43]
GLenum access
GLenum GLuint GLenum GLsizei const GLchar * buf
GLbitfield flags
GLenum GLuint GLsizei const GLenum * props
GLenum GLuint GLintptr offset
GLenum GLenum severity
GLint ref
GLuint name
GLint first
GLint GLint GLint GLint GLint GLint GLint GLbitfield mask
GLint GLsizei GLsizei GLenum format
GLsizei GLenum GLsizei GLsizei GLuint memory
GLint y
GLfloat GLfloat GLfloat GLfloat h
void ** params
GLuint bindingIndex
GLdouble s
[6]
Definition qopenglext.h:235
const GLubyte * c
GLint void * img
Definition qopenglext.h:233
GLuint GLsizei const GLuint const GLintptr * offsets
GLuint shader
Definition qopenglext.h:665
GLint limit
GLdouble GLdouble t
Definition qopenglext.h:243
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLsizei GLsizei GLuint * shaders
Definition qopenglext.h:677
GLuint64EXT * result
[6]
GLfloat GLfloat p
[1]
GLint GLfloat GLint stencil
GLenum GLenum colorFormat
GLsizeiptr const void GLenum usage
Definition qopenglext.h:543
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define QRHI_RES_RHI(t)
Definition qrhi_p.h:29
#define QRHI_RES(t, x)
Definition qrhi_p.h:28
Int aligned(Int v, Int byteAlign)
static bool isDepthTextureFormat(QRhiTexture::Format format)
static QRhiTexture::Format swapchainReadbackTextureFormat(DXGI_FORMAT format, QRhiTexture::Flags *flags)
static QRhiPassResourceTracker::UsageState toPassTrackerUsageState(const QGles2Buffer::UsageState &bufUsage)
static VkPolygonMode toVkPolygonMode(QRhiGraphicsPipeline::PolygonMode mode)
static VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL wrap_vkGetInstanceProcAddr(VkInstance, const char *pName)
static VkCullModeFlags toVkCullMode(QRhiGraphicsPipeline::CullMode c)
static bool accessIsWrite(VkAccessFlags access)
static VkCompareOp toVkTextureCompareOp(QRhiSampler::CompareOp op)
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 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)
Int aligned(Int v, Int byteAlign)
\variable QRhiVulkanRenderPassNativeHandles::renderPass
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 VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL wrap_vkGetDeviceProcAddr(VkDevice device, const char *pName)
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 VmaAllocator toVmaAllocator(QVkAllocator a)
static QVulkanInstance * globalVulkanInstance
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 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 struct @367 qvk_sampleCounts[]
static void qrhivk_releaseSampler(const QRhiVulkan::DeferredReleaseEntry &e, VkDevice dev, QVulkanDeviceFunctions *df)
int count
static QVkRenderTargetData * maybeRenderTargetData(QVkCommandBuffer *cbD)
static VmaAllocation toVmaAllocation(QVkAlloc a)
static VkShaderStageFlagBits toVkShaderStage(QRhiShaderStage::Type type)
static const int QVK_UNIFORM_BUFFERS_PER_POOL
static const int QVK_COMBINED_IMAGE_SAMPLERS_PER_POOL
static const int QVK_STORAGE_BUFFERS_PER_POOL
static const int QVK_STORAGE_IMAGES_PER_POOL
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
QScopeGuard< typename std::decay< F >::type > qScopeGuard(F &&f)
[qScopeGuard]
Definition qscopeguard.h:60
SSL_CTX int(* cb)(SSL *ssl, unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg)
std::unique_ptr< ThunkPool::ThunkAllocation > allocation
Definition qstdweb.cpp:276
Q_CORE_EXPORT QByteArray qgetenv(const char *varName)
Q_CORE_EXPORT bool qEnvironmentVariableIsSet(const char *varName) noexcept
Q_CORE_EXPORT int qEnvironmentVariableIntValue(const char *varName, bool *ok=nullptr) noexcept
#define Q_UNUSED(x)
unsigned int quint32
Definition qtypes.h:50
unsigned char uchar
Definition qtypes.h:32
int qint32
Definition qtypes.h:49
unsigned long long quint64
Definition qtypes.h:61
ptrdiff_t qsizetype
Definition qtypes.h:165
unsigned int uint
Definition qtypes.h:34
QList< QImage > images
[6]
QVBoxLayout * layout
QSharedPointer< T > other(t)
[5]
view viewport() -> scroll(dx, dy, deviceRect)
QFrame frame
[0]
QQuickView * view
[0]
bool prepare(VkRenderPassCreateInfo *rpInfo, int multiViewCount, bool multiViewCap)
\inmodule QtGui
Definition qrhi.h:862
\inmodule QtGui
Definition qrhi.h:1759
quint64 deviceId
Definition qrhi.h:1770
QByteArray deviceName
Definition qrhi.h:1769
DeviceType
Specifies the graphics device's type, when the information is available.
Definition qrhi.h:1760
@ IntegratedDevice
Definition qrhi.h:1762
DeviceType deviceType
Definition qrhi.h:1772
quint64 vendorId
Definition qrhi.h:1771
\variable QRhiGraphicsPipeline::TargetBlend::colorWrite
Definition qrhi.h:1372
\variable QRhiReadbackResult::completed
Definition qrhi.h:800
QByteArray data
Definition qrhi.h:1727
std::function< void()> completed
Definition qrhi.h:1724
QRhiTextureCopyDescription desc
Definition qrhi_p.h:471
QVarLengthArray< MipLevelUploadList, 6 > subresDesc
Definition qrhi_p.h:469
\inmodule QtGui
Definition qrhi.h:1782
qint64 totalPipelineCreationTime
Definition qrhi.h:1783
\inmodule QtGui
Definition qrhi.h:965
QRhiTexture::Format format
Definition qrhi.h:999
struct QRhiVulkan::DeferredReleaseEntry::@340::@349 renderPass
struct QRhiVulkan::DeferredReleaseEntry::@340::@343 shaderResourceBindings
struct QRhiVulkan::DeferredReleaseEntry::@340::@342 pipelineState
struct QRhiVulkan::DeferredReleaseEntry::@340::@351 secondaryCommandBuffer
struct QRhiVulkan::DeferredReleaseEntry::@340::@346 texture
struct QRhiVulkan::DeferredReleaseEntry::@340::@344 buffer
struct QRhiVulkan::DeferredReleaseEntry::@340::@348 textureRenderTarget
struct QRhiVulkan::DeferredReleaseEntry::@340::@345 renderBuffer
VkPipelineStageFlags stage
VkAccessFlags access
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
QVkBuffer(QRhiImplementation *rhi, Type type, UsageFlags usage, quint32 size)
UsageState usageState[QVK_FRAMES_IN_FLIGHT]
uint generation
VkBuffer stagingBuffers[QVK_FRAMES_IN_FLIGHT]
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.
QVarLengthArray< DynamicUpdate, 16 > pendingDynamicUpdates[QVK_FRAMES_IN_FLIGHT]
char * beginFullDynamicBufferUpdateForCurrentFrame() override
int lastActiveFrameSlot
union QVkCommandBuffer::Command::Args args
struct QVkCommandBuffer::@313 pools
VkBuffer currentVertexBuffers[VERTEX_INPUT_RESOURCE_SLOT_COUNT]
QVkCommandBuffer(QRhiImplementation *rhi)
QVarLengthArray< VkCommandBuffer, 4 > activeSecondaryCbStack
QRhiBackendCommandList< Command > commands
QRhiRenderTarget * currentTarget
QRhiVulkanCommandBufferNativeHandles nativeHandlesStruct
quint32 currentVertexOffsets[VERTEX_INPUT_RESOURCE_SLOT_COUNT]
QRhiComputePipeline * currentComputePipeline
const QRhiNativeHandles * nativeHandles()
QRhiShaderResourceBindings * currentComputeSrb
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
QRhiShaderResourceBindings * currentGraphicsSrb
PassType recordingPass
VkBuffer currentIndexBuffer
VkCommandBuffer cb
QRhiGraphicsPipeline * currentGraphicsPipeline
uint currentPipelineGeneration
quint32 currentIndexOffset
QVarLengthArray< QRhiPassResourceTracker, 8 > passResTrackers
struct QVkCommandBuffer::@312 computePassState
VkIndexType currentIndexFormat
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
QVkComputePipeline(QRhiImplementation *rhi)
bool create() override
VkPipelineLayout layout
QVkGraphicsPipeline(QRhiImplementation *rhi)
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
VkPipelineLayout layout
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)
VkFormat vkformat
QRhiTexture::Format backingFormat() const override
bool create() override
Creates the corresponding native graphics resources.
VkDeviceMemory memory
QVkTexture * backingTexture
VkImageView imageView
QVarLengthArray< VkSubpassDependency, 2 > subpassDeps
const QRhiNativeHandles * nativeHandles() override
QVector< quint32 > serializedFormatData
QVarLengthArray< VkAttachmentReference, 8 > colorRefs
QRhiRenderPassDescriptor * newCompatibleRenderPassDescriptor() const override
VkAttachmentReference dsRef
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
QVarLengthArray< VkAttachmentReference, 8 > resolveRefs
QVarLengthArray< VkAttachmentDescription, 8 > attDescs
QRhiVulkanRenderPassNativeHandles nativeHandlesStruct
VkAttachmentReference dsResolveRef
QVector< quint32 > serializedFormat() const override
QVkRenderPassDescriptor(QRhiImplementation *rhi)
bool isCompatible(const QRhiRenderPassDescriptor *other) const override
QVkRenderPassDescriptor * rp
QRhiRenderTargetAttachmentTracker::ResIdList currentResIdList
static const int MAX_COLOR_ATTACHMENTS
int lastActiveFrameSlot
bool create() override
VkSampler sampler
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)
VkDescriptorSetLayout layout
void updateResources(UpdateFlags flags) override
QVkShaderResourceBindings(QRhiImplementation *rhi)
QVarLengthArray< QRhiShaderResourceBinding, 8 > sortedBindings
QVarLengthArray< BoundResourceData, 8 > boundResourceData[QVK_FRAMES_IN_FLIGHT]
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
VkDescriptorSet descSets[QVK_FRAMES_IN_FLIGHT]
QSize pixelSize() const override
int sampleCount() const override
float devicePixelRatio() const override
QVkRenderTargetData d
QVkSwapChainRenderTarget(QRhiImplementation *rhi, QRhiSwapChain *swapchain)
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
VkColorSpaceKHR colorSpace
VkSurfaceKHR lastConnectedSurface
bool createOrResize() override
Creates the swapchain if not already done and resizes the swapchain buffers to match the current size...
VkSwapchainKHR sc
bool isFormatSupported(Format f) override
QVkCommandBuffer cbWrapper
QRhiRenderPassDescriptor * newCompatibleRenderPassDescriptor() override
VkSampleCountFlagBits samples
struct QVkSwapChain::FrameResources frameRes[QVK_FRAMES_IN_FLIGHT]
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
quint32 currentImageIndex
QVarLengthArray< ImageResources, EXPECTED_MAX_BUFFER_COUNT > imageRes
VkSurfaceKHR surface
QVarLengthArray< VkPresentModeKHR, 8 > supportedPresentationModes
QWindow * window
QVkSwapChainRenderTarget rtWrapperRight
bool ensureSurface()
QVkSwapChainRenderTarget rtWrapper
QRhiCommandBuffer * currentFrameCommandBuffer() override
QVkTextureRenderTarget(QRhiImplementation *rhi, const QRhiTextureRenderTargetDescription &desc, Flags flags)
float devicePixelRatio() const override
VkImageView rtv[QVkRenderTargetData::MAX_COLOR_ATTACHMENTS]
bool create() override
Creates the corresponding native graphics resources.
int sampleCount() const override
QSize pixelSize() const override
QVkRenderTargetData d
VkImageView resrtv[QVkRenderTargetData::MAX_COLOR_ATTACHMENTS]
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
QRhiRenderPassDescriptor * newCompatibleRenderPassDescriptor() override
VkPipelineStageFlags stage
bool create() override
Creates the corresponding native graphics resources.
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
VkBuffer stagingBuffers[QVK_FRAMES_IN_FLIGHT]
VkFormat vkformat
bool finishCreate()
VkImageView perLevelImageViewForLoadStore(int level)
VkSampleCountFlagBits samples
int lastActiveFrameSlot
QVkAlloc stagingAllocations[QVK_FRAMES_IN_FLIGHT]
bool createFrom(NativeTexture src) override
Similar to create(), except that no new native textures are created.
VkFormat viewFormat
QVkAlloc imageAlloc
VkImage image
void setNativeLayout(int layout) override
With some graphics APIs, such as Vulkan, integrating custom rendering code that uses the graphics API...
VkFormat viewFormatForSampling
QVkTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth, int arraySize, int sampleCount, Flags flags)
VkImageView perLevelImageViews[QRhi::MAX_MIP_LEVELS]
NativeTexture nativeTexture() override
UsageState usageState
bool prepareCreate(QSize *adjustedSize=nullptr)
uint mipLevelCount
VkImageView imageView
Definition moc.h:23