Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qvulkanwindow.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 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 "qvulkanwindow_p.h"
5#include "qvulkanfunctions.h"
6#include <QLoggingCategory>
7#include <QTimer>
8#include <QThread>
9#include <QCoreApplication>
10#include <qevent.h>
11
13
15
16
215QVulkanWindow::QVulkanWindow(QWindow *parent)
216 : QWindow(*(new QVulkanWindowPrivate), parent)
217{
218 setSurfaceType(QSurface::VulkanSurface);
219}
220
224QVulkanWindow::~QVulkanWindow()
225{
226}
227
228QVulkanWindowPrivate::~QVulkanWindowPrivate()
229{
230 // graphics resource cleanup is already done at this point due to
231 // QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed
232
233 delete renderer;
234}
235
253void QVulkanWindow::setFlags(Flags flags)
254{
255 Q_D(QVulkanWindow);
256 if (d->status != QVulkanWindowPrivate::StatusUninitialized) {
257 qWarning("QVulkanWindow: Attempted to set flags when already initialized");
258 return;
259 }
260 d->flags = flags;
261}
262
266QVulkanWindow::Flags QVulkanWindow::flags() const
267{
268 Q_D(const QVulkanWindow);
269 return d->flags;
270}
271
277QList<VkPhysicalDeviceProperties> QVulkanWindow::availablePhysicalDevices()
278{
279 Q_D(QVulkanWindow);
280 if (!d->physDevs.isEmpty() && !d->physDevProps.isEmpty())
281 return d->physDevProps;
282
283 QVulkanInstance *inst = vulkanInstance();
284 if (!inst) {
285 qWarning("QVulkanWindow: Attempted to call availablePhysicalDevices() without a QVulkanInstance");
286 return d->physDevProps;
287 }
288
289 QVulkanFunctions *f = inst->functions();
290 uint32_t count = 1;
291 VkResult err = f->vkEnumeratePhysicalDevices(inst->vkInstance(), &count, nullptr);
292 if (err != VK_SUCCESS) {
293 qWarning("QVulkanWindow: Failed to get physical device count: %d", err);
294 return d->physDevProps;
295 }
296
297 qCDebug(lcGuiVk, "%d physical devices", count);
298 if (!count)
299 return d->physDevProps;
300
302 err = f->vkEnumeratePhysicalDevices(inst->vkInstance(), &count, devs.data());
303 if (err != VK_SUCCESS) {
304 qWarning("QVulkanWindow: Failed to enumerate physical devices: %d", err);
305 return d->physDevProps;
306 }
307
308 d->physDevs = devs;
309 d->physDevProps.resize(count);
310 for (uint32_t i = 0; i < count; ++i) {
311 VkPhysicalDeviceProperties *p = &d->physDevProps[i];
312 f->vkGetPhysicalDeviceProperties(d->physDevs.at(i), p);
313 qCDebug(lcGuiVk, "Physical device [%d]: name '%s' version %d.%d.%d", i, p->deviceName,
314 VK_VERSION_MAJOR(p->driverVersion), VK_VERSION_MINOR(p->driverVersion),
315 VK_VERSION_PATCH(p->driverVersion));
316 }
317
318 return d->physDevProps;
319}
320
331void QVulkanWindow::setPhysicalDeviceIndex(int idx)
332{
333 Q_D(QVulkanWindow);
334 if (d->status != QVulkanWindowPrivate::StatusUninitialized) {
335 qWarning("QVulkanWindow: Attempted to set physical device when already initialized");
336 return;
337 }
338 const int count = availablePhysicalDevices().size();
339 if (idx < 0 || idx >= count) {
340 qWarning("QVulkanWindow: Invalid physical device index %d (total physical devices: %d)", idx, count);
341 return;
342 }
343 d->physDevIndex = idx;
344}
345
352QVulkanInfoVector<QVulkanExtension> QVulkanWindow::supportedDeviceExtensions()
353{
354 Q_D(QVulkanWindow);
355
356 availablePhysicalDevices();
357
358 if (d->physDevs.isEmpty()) {
359 qWarning("QVulkanWindow: No physical devices found");
361 }
362
363 VkPhysicalDevice physDev = d->physDevs.at(d->physDevIndex);
364 if (d->supportedDevExtensions.contains(physDev))
365 return d->supportedDevExtensions.value(physDev);
366
367 QVulkanFunctions *f = vulkanInstance()->functions();
368 uint32_t count = 0;
369 VkResult err = f->vkEnumerateDeviceExtensionProperties(physDev, nullptr, &count, nullptr);
370 if (err == VK_SUCCESS) {
372 err = f->vkEnumerateDeviceExtensionProperties(physDev, nullptr, &count, extProps.data());
373 if (err == VK_SUCCESS) {
375 for (const VkExtensionProperties &prop : extProps) {
377 ext.name = prop.extensionName;
378 ext.version = prop.specVersion;
379 exts.append(ext);
380 }
381 d->supportedDevExtensions.insert(physDev, exts);
382 qDebug(lcGuiVk) << "Supported device extensions:" << exts;
383 return exts;
384 }
385 }
386
387 qWarning("QVulkanWindow: Failed to query device extension count: %d", err);
389}
390
403void QVulkanWindow::setDeviceExtensions(const QByteArrayList &extensions)
404{
405 Q_D(QVulkanWindow);
406 if (d->status != QVulkanWindowPrivate::StatusUninitialized) {
407 qWarning("QVulkanWindow: Attempted to set device extensions when already initialized");
408 return;
409 }
410 d->requestedDevExtensions = extensions;
411}
412
438void QVulkanWindow::setPreferredColorFormats(const QList<VkFormat> &formats)
439{
440 Q_D(QVulkanWindow);
441 if (d->status != QVulkanWindowPrivate::StatusUninitialized) {
442 qWarning("QVulkanWindow: Attempted to set preferred color format when already initialized");
443 return;
444 }
445 d->requestedColorFormats = formats;
446}
447
448static struct {
449 VkSampleCountFlagBits mask;
450 int count;
451} q_vk_sampleCounts[] = {
452 // keep this sorted by 'count'
453 { VK_SAMPLE_COUNT_1_BIT, 1 },
454 { VK_SAMPLE_COUNT_2_BIT, 2 },
455 { VK_SAMPLE_COUNT_4_BIT, 4 },
456 { VK_SAMPLE_COUNT_8_BIT, 8 },
457 { VK_SAMPLE_COUNT_16_BIT, 16 },
458 { VK_SAMPLE_COUNT_32_BIT, 32 },
459 { VK_SAMPLE_COUNT_64_BIT, 64 }
461
474QList<int> QVulkanWindow::supportedSampleCounts()
475{
476 Q_D(const QVulkanWindow);
478
479 availablePhysicalDevices();
480
481 if (d->physDevs.isEmpty()) {
482 qWarning("QVulkanWindow: No physical devices found");
483 return result;
484 }
485
486 const VkPhysicalDeviceLimits *limits = &d->physDevProps[d->physDevIndex].limits;
487 VkSampleCountFlags color = limits->framebufferColorSampleCounts;
488 VkSampleCountFlags depth = limits->framebufferDepthSampleCounts;
489 VkSampleCountFlags stencil = limits->framebufferStencilSampleCounts;
490
491 for (const auto &qvk_sampleCount : q_vk_sampleCounts) {
492 if ((color & qvk_sampleCount.mask)
493 && (depth & qvk_sampleCount.mask)
494 && (stencil & qvk_sampleCount.mask))
495 {
496 result.append(qvk_sampleCount.count);
497 }
498 }
499
500 return result;
501}
502
524void QVulkanWindow::setSampleCount(int sampleCount)
525{
526 Q_D(QVulkanWindow);
527 if (d->status != QVulkanWindowPrivate::StatusUninitialized) {
528 qWarning("QVulkanWindow: Attempted to set sample count when already initialized");
529 return;
530 }
531
532 // Stay compatible with QSurfaceFormat and friends where samples == 0 means the same as 1.
533 sampleCount = qBound(1, sampleCount, 64);
534
535 if (!supportedSampleCounts().contains(sampleCount)) {
536 qWarning("QVulkanWindow: Attempted to set unsupported sample count %d", sampleCount);
537 return;
538 }
539
540 for (const auto &qvk_sampleCount : q_vk_sampleCounts) {
541 if (qvk_sampleCount.count == sampleCount) {
542 d->sampleCount = qvk_sampleCount.mask;
543 return;
544 }
545 }
546
547 Q_UNREACHABLE();
548}
549
550void QVulkanWindowPrivate::init()
551{
552 Q_Q(QVulkanWindow);
553 Q_ASSERT(status == StatusUninitialized);
554
555 qCDebug(lcGuiVk, "QVulkanWindow init");
556
557 inst = q->vulkanInstance();
558 if (!inst) {
559 qWarning("QVulkanWindow: Attempted to initialize without a QVulkanInstance");
560 // This is a simple user error, recheck on the next expose instead of
561 // going into the permanent failure state.
562 status = StatusFailRetry;
563 return;
564 }
565
566 if (!renderer)
567 renderer = q->createRenderer();
568
569 surface = QVulkanInstance::surfaceForWindow(q);
570 if (surface == VK_NULL_HANDLE) {
571 qWarning("QVulkanWindow: Failed to retrieve Vulkan surface for window");
572 status = StatusFailRetry;
573 return;
574 }
575
576 q->availablePhysicalDevices();
577
578 if (physDevs.isEmpty()) {
579 qWarning("QVulkanWindow: No physical devices found");
580 status = StatusFail;
581 return;
582 }
583
584 if (physDevIndex < 0 || physDevIndex >= physDevs.size()) {
585 qWarning("QVulkanWindow: Invalid physical device index; defaulting to 0");
586 physDevIndex = 0;
587 }
588 qCDebug(lcGuiVk, "Using physical device [%d]", physDevIndex);
589
590 // Give a last chance to do decisions based on the physical device and the surface.
591 if (renderer)
592 renderer->preInitResources();
593
594 VkPhysicalDevice physDev = physDevs.at(physDevIndex);
595 QVulkanFunctions *f = inst->functions();
596
597 uint32_t queueCount = 0;
598 f->vkGetPhysicalDeviceQueueFamilyProperties(physDev, &queueCount, nullptr);
599 QList<VkQueueFamilyProperties> queueFamilyProps(queueCount);
600 f->vkGetPhysicalDeviceQueueFamilyProperties(physDev, &queueCount, queueFamilyProps.data());
601 gfxQueueFamilyIdx = uint32_t(-1);
602 presQueueFamilyIdx = uint32_t(-1);
603 for (int i = 0; i < queueFamilyProps.size(); ++i) {
604 const bool supportsPresent = inst->supportsPresent(physDev, i, q);
605 qCDebug(lcGuiVk, "queue family %d: flags=0x%x count=%d supportsPresent=%d", i,
606 queueFamilyProps[i].queueFlags, queueFamilyProps[i].queueCount, supportsPresent);
607 if (gfxQueueFamilyIdx == uint32_t(-1)
608 && (queueFamilyProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
609 && supportsPresent)
610 gfxQueueFamilyIdx = i;
611 }
612 if (gfxQueueFamilyIdx != uint32_t(-1)) {
613 presQueueFamilyIdx = gfxQueueFamilyIdx;
614 } else {
615 qCDebug(lcGuiVk, "No queue with graphics+present; trying separate queues");
616 for (int i = 0; i < queueFamilyProps.size(); ++i) {
617 if (gfxQueueFamilyIdx == uint32_t(-1) && (queueFamilyProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT))
618 gfxQueueFamilyIdx = i;
619 if (presQueueFamilyIdx == uint32_t(-1) && inst->supportsPresent(physDev, i, q))
620 presQueueFamilyIdx = i;
621 }
622 }
623 if (gfxQueueFamilyIdx == uint32_t(-1)) {
624 qWarning("QVulkanWindow: No graphics queue family found");
625 status = StatusFail;
626 return;
627 }
628 if (presQueueFamilyIdx == uint32_t(-1)) {
629 qWarning("QVulkanWindow: No present queue family found");
630 status = StatusFail;
631 return;
632 }
633#ifdef QT_DEBUG
634 // allow testing the separate present queue case in debug builds on AMD cards
635 if (qEnvironmentVariableIsSet("QT_VK_PRESENT_QUEUE_INDEX"))
636 presQueueFamilyIdx = qEnvironmentVariableIntValue("QT_VK_PRESENT_QUEUE_INDEX");
637#endif
638 qCDebug(lcGuiVk, "Using queue families: graphics = %u present = %u", gfxQueueFamilyIdx, presQueueFamilyIdx);
639
641 queueInfo.reserve(2);
642 const float prio[] = { 0 };
643 VkDeviceQueueCreateInfo addQueueInfo;
644 memset(&addQueueInfo, 0, sizeof(addQueueInfo));
645 addQueueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
646 addQueueInfo.queueFamilyIndex = gfxQueueFamilyIdx;
647 addQueueInfo.queueCount = 1;
648 addQueueInfo.pQueuePriorities = prio;
649 queueInfo.append(addQueueInfo);
650 if (gfxQueueFamilyIdx != presQueueFamilyIdx) {
651 addQueueInfo.queueFamilyIndex = presQueueFamilyIdx;
652 addQueueInfo.queueCount = 1;
653 addQueueInfo.pQueuePriorities = prio;
654 queueInfo.append(addQueueInfo);
655 }
656 if (queueCreateInfoModifier) {
657 queueCreateInfoModifier(queueFamilyProps.constData(), queueCount, queueInfo);
658 bool foundGfxQueue = false;
659 bool foundPresQueue = false;
660 for (const VkDeviceQueueCreateInfo& createInfo : std::as_const(queueInfo)) {
661 foundGfxQueue |= createInfo.queueFamilyIndex == gfxQueueFamilyIdx;
662 foundPresQueue |= createInfo.queueFamilyIndex == presQueueFamilyIdx;
663 }
664 if (!foundGfxQueue) {
665 qWarning("QVulkanWindow: Graphics queue missing after call to queueCreateInfoModifier");
666 status = StatusFail;
667 return;
668 }
669 if (!foundPresQueue) {
670 qWarning("QVulkanWindow: Present queue missing after call to queueCreateInfoModifier");
671 status = StatusFail;
672 return;
673 }
674 }
675
676 // Filter out unsupported extensions in order to keep symmetry
677 // with how QVulkanInstance behaves. Add the swapchain extension.
678 QList<const char *> devExts;
679 QVulkanInfoVector<QVulkanExtension> supportedExtensions = q->supportedDeviceExtensions();
680 QByteArrayList reqExts = requestedDevExtensions;
681 reqExts.append("VK_KHR_swapchain");
682
683 QByteArray envExts = qgetenv("QT_VULKAN_DEVICE_EXTENSIONS");
684 if (!envExts.isEmpty()) {
685 QByteArrayList envExtList = envExts.split(';');
686 for (auto ext : reqExts)
687 envExtList.removeAll(ext);
688 reqExts.append(envExtList);
689 }
690
691 for (const QByteArray &ext : reqExts) {
692 if (supportedExtensions.contains(ext))
693 devExts.append(ext.constData());
694 }
695 qCDebug(lcGuiVk) << "Enabling device extensions:" << devExts;
696
697 VkDeviceCreateInfo devInfo;
698 memset(&devInfo, 0, sizeof(devInfo));
699 devInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
700 devInfo.queueCreateInfoCount = queueInfo.size();
701 devInfo.pQueueCreateInfos = queueInfo.constData();
702 devInfo.enabledExtensionCount = devExts.size();
703 devInfo.ppEnabledExtensionNames = devExts.constData();
704
705 VkPhysicalDeviceFeatures features;
706 memset(&features, 0, sizeof(features));
707 if (enabledFeaturesModifier) {
708 enabledFeaturesModifier(features);
709 } else {
710 // Enable all supported 1.0 core features, except ones that likely
711 // involve a performance penalty.
712 f->vkGetPhysicalDeviceFeatures(physDev, &features);
713 features.robustBufferAccess = VK_FALSE;
714 }
715 devInfo.pEnabledFeatures = &features;
716
717 // Device layers are not supported by QVulkanWindow since that's an already deprecated
718 // API. However, have a workaround for systems with older API and layers (f.ex. L4T
719 // 24.2 for the Jetson TX1 provides API 1.0.13 and crashes when the validation layer
720 // is enabled for the instance but not the device).
721 uint32_t apiVersion = physDevProps[physDevIndex].apiVersion;
722 if (VK_VERSION_MAJOR(apiVersion) == 1
723 && VK_VERSION_MINOR(apiVersion) == 0
724 && VK_VERSION_PATCH(apiVersion) <= 13)
725 {
726 // Make standard validation work at least.
727 const QByteArray stdValName = QByteArrayLiteral("VK_LAYER_KHRONOS_validation");
728 const char *stdValNamePtr = stdValName.constData();
729 if (inst->layers().contains(stdValName)) {
730 uint32_t count = 0;
731 VkResult err = f->vkEnumerateDeviceLayerProperties(physDev, &count, nullptr);
732 if (err == VK_SUCCESS) {
734 err = f->vkEnumerateDeviceLayerProperties(physDev, &count, layerProps.data());
735 if (err == VK_SUCCESS) {
736 for (const VkLayerProperties &prop : layerProps) {
737 if (!strncmp(prop.layerName, stdValNamePtr, stdValName.size())) {
738 devInfo.enabledLayerCount = 1;
739 devInfo.ppEnabledLayerNames = &stdValNamePtr;
740 break;
741 }
742 }
743 }
744 }
745 }
746 }
747
748 VkResult err = f->vkCreateDevice(physDev, &devInfo, nullptr, &dev);
749 if (err == VK_ERROR_DEVICE_LOST) {
750 qWarning("QVulkanWindow: Physical device lost");
751 if (renderer)
752 renderer->physicalDeviceLost();
753 // clear the caches so the list of physical devices is re-queried
754 physDevs.clear();
755 physDevProps.clear();
756 status = StatusUninitialized;
757 qCDebug(lcGuiVk, "Attempting to restart in 2 seconds");
758 QTimer::singleShot(2000, q, [this]() { ensureStarted(); });
759 return;
760 }
761 if (err != VK_SUCCESS) {
762 qWarning("QVulkanWindow: Failed to create device: %d", err);
763 status = StatusFail;
764 return;
765 }
766
767 devFuncs = inst->deviceFunctions(dev);
768 Q_ASSERT(devFuncs);
769
770 devFuncs->vkGetDeviceQueue(dev, gfxQueueFamilyIdx, 0, &gfxQueue);
771 if (gfxQueueFamilyIdx == presQueueFamilyIdx)
772 presQueue = gfxQueue;
773 else
774 devFuncs->vkGetDeviceQueue(dev, presQueueFamilyIdx, 0, &presQueue);
775
776 VkCommandPoolCreateInfo poolInfo;
777 memset(&poolInfo, 0, sizeof(poolInfo));
778 poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
779 poolInfo.queueFamilyIndex = gfxQueueFamilyIdx;
780 err = devFuncs->vkCreateCommandPool(dev, &poolInfo, nullptr, &cmdPool);
781 if (err != VK_SUCCESS) {
782 qWarning("QVulkanWindow: Failed to create command pool: %d", err);
783 status = StatusFail;
784 return;
785 }
786 if (gfxQueueFamilyIdx != presQueueFamilyIdx) {
787 poolInfo.queueFamilyIndex = presQueueFamilyIdx;
788 err = devFuncs->vkCreateCommandPool(dev, &poolInfo, nullptr, &presCmdPool);
789 if (err != VK_SUCCESS) {
790 qWarning("QVulkanWindow: Failed to create command pool for present queue: %d", err);
791 status = StatusFail;
792 return;
793 }
794 }
795
796 hostVisibleMemIndex = 0;
797 VkPhysicalDeviceMemoryProperties physDevMemProps;
798 bool hostVisibleMemIndexSet = false;
799 f->vkGetPhysicalDeviceMemoryProperties(physDev, &physDevMemProps);
800 for (uint32_t i = 0; i < physDevMemProps.memoryTypeCount; ++i) {
801 const VkMemoryType *memType = physDevMemProps.memoryTypes;
802 qCDebug(lcGuiVk, "memtype %d: flags=0x%x", i, memType[i].propertyFlags);
803 // Find a host visible, host coherent memtype. If there is one that is
804 // cached as well (in addition to being coherent), prefer that.
805 const int hostVisibleAndCoherent = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
806 if ((memType[i].propertyFlags & hostVisibleAndCoherent) == hostVisibleAndCoherent) {
807 if (!hostVisibleMemIndexSet
808 || (memType[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT)) {
809 hostVisibleMemIndexSet = true;
810 hostVisibleMemIndex = i;
811 }
812 }
813 }
814 qCDebug(lcGuiVk, "Picked memtype %d for host visible memory", hostVisibleMemIndex);
815 deviceLocalMemIndex = 0;
816 for (uint32_t i = 0; i < physDevMemProps.memoryTypeCount; ++i) {
817 const VkMemoryType *memType = physDevMemProps.memoryTypes;
818 // Just pick the first device local memtype.
819 if (memType[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) {
820 deviceLocalMemIndex = i;
821 break;
822 }
823 }
824 qCDebug(lcGuiVk, "Picked memtype %d for device local memory", deviceLocalMemIndex);
825
826 if (!vkGetPhysicalDeviceSurfaceCapabilitiesKHR || !vkGetPhysicalDeviceSurfaceFormatsKHR) {
827 vkGetPhysicalDeviceSurfaceCapabilitiesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR>(
828 inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfaceCapabilitiesKHR"));
829 vkGetPhysicalDeviceSurfaceFormatsKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceFormatsKHR>(
830 inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfaceFormatsKHR"));
831 if (!vkGetPhysicalDeviceSurfaceCapabilitiesKHR || !vkGetPhysicalDeviceSurfaceFormatsKHR) {
832 qWarning("QVulkanWindow: Physical device surface queries not available");
833 status = StatusFail;
834 return;
835 }
836 }
837
838 // Figure out the color format here. Must not wait until recreateSwapChain()
839 // because the renderpass should be available already from initResources (so
840 // that apps do not have to defer pipeline creation to
841 // initSwapChainResources), but the renderpass needs the final color format.
842
843 uint32_t formatCount = 0;
844 vkGetPhysicalDeviceSurfaceFormatsKHR(physDev, surface, &formatCount, nullptr);
846 if (formatCount)
847 vkGetPhysicalDeviceSurfaceFormatsKHR(physDev, surface, &formatCount, formats.data());
848
849 colorFormat = VK_FORMAT_B8G8R8A8_UNORM; // our documented default if all else fails
850 colorSpace = VkColorSpaceKHR(0); // this is in fact VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
851
852 // Pick the preferred format, if there is one.
853 if (!formats.isEmpty() && formats[0].format != VK_FORMAT_UNDEFINED) {
854 colorFormat = formats[0].format;
855 colorSpace = formats[0].colorSpace;
856 }
857
858 // Try to honor the user request.
859 if (!formats.isEmpty() && !requestedColorFormats.isEmpty()) {
860 for (VkFormat reqFmt : std::as_const(requestedColorFormats)) {
861 auto r = std::find_if(formats.cbegin(), formats.cend(),
862 [reqFmt](const VkSurfaceFormatKHR &sfmt) { return sfmt.format == reqFmt; });
863 if (r != formats.cend()) {
864 colorFormat = r->format;
865 colorSpace = r->colorSpace;
866 break;
867 }
868 }
869 }
870
871 const VkFormat dsFormatCandidates[] = {
872 VK_FORMAT_D24_UNORM_S8_UINT,
873 VK_FORMAT_D32_SFLOAT_S8_UINT,
874 VK_FORMAT_D16_UNORM_S8_UINT
875 };
876 const int dsFormatCandidateCount = sizeof(dsFormatCandidates) / sizeof(VkFormat);
877 int dsFormatIdx = 0;
878 while (dsFormatIdx < dsFormatCandidateCount) {
879 dsFormat = dsFormatCandidates[dsFormatIdx];
880 VkFormatProperties fmtProp;
881 f->vkGetPhysicalDeviceFormatProperties(physDev, dsFormat, &fmtProp);
882 if (fmtProp.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)
883 break;
884 ++dsFormatIdx;
885 }
886 if (dsFormatIdx == dsFormatCandidateCount)
887 qWarning("QVulkanWindow: Failed to find an optimal depth-stencil format");
888
889 qCDebug(lcGuiVk, "Color format: %d Depth-stencil format: %d", colorFormat, dsFormat);
890
891 if (!createDefaultRenderPass())
892 return;
893
894 if (renderer)
895 renderer->initResources();
896
897 status = StatusDeviceReady;
898}
899
900void QVulkanWindowPrivate::reset()
901{
902 if (!dev) // do not rely on 'status', a half done init must be cleaned properly too
903 return;
904
905 qCDebug(lcGuiVk, "QVulkanWindow reset");
906
907 devFuncs->vkDeviceWaitIdle(dev);
908
909 if (renderer) {
910 renderer->releaseResources();
911 devFuncs->vkDeviceWaitIdle(dev);
912 }
913
914 if (defaultRenderPass) {
915 devFuncs->vkDestroyRenderPass(dev, defaultRenderPass, nullptr);
916 defaultRenderPass = VK_NULL_HANDLE;
917 }
918
919 if (cmdPool) {
920 devFuncs->vkDestroyCommandPool(dev, cmdPool, nullptr);
921 cmdPool = VK_NULL_HANDLE;
922 }
923
924 if (presCmdPool) {
925 devFuncs->vkDestroyCommandPool(dev, presCmdPool, nullptr);
926 presCmdPool = VK_NULL_HANDLE;
927 }
928
929 if (frameGrabImage) {
930 devFuncs->vkDestroyImage(dev, frameGrabImage, nullptr);
931 frameGrabImage = VK_NULL_HANDLE;
932 }
933
934 if (frameGrabImageMem) {
935 devFuncs->vkFreeMemory(dev, frameGrabImageMem, nullptr);
936 frameGrabImageMem = VK_NULL_HANDLE;
937 }
938
939 if (dev) {
940 devFuncs->vkDestroyDevice(dev, nullptr);
941 inst->resetDeviceFunctions(dev);
942 dev = VK_NULL_HANDLE;
943 vkCreateSwapchainKHR = nullptr; // re-resolve swapchain funcs later on since some come via the device
944 }
945
946 surface = VK_NULL_HANDLE;
947
948 status = StatusUninitialized;
949}
950
951bool QVulkanWindowPrivate::createDefaultRenderPass()
952{
953 VkAttachmentDescription attDesc[3];
954 memset(attDesc, 0, sizeof(attDesc));
955
956 const bool msaa = sampleCount > VK_SAMPLE_COUNT_1_BIT;
957
958 // This is either the non-msaa render target or the resolve target.
959 attDesc[0].format = colorFormat;
960 attDesc[0].samples = VK_SAMPLE_COUNT_1_BIT;
961 attDesc[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; // ignored when msaa
962 attDesc[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
963 attDesc[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
964 attDesc[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
965 attDesc[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
966 attDesc[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
967
968 attDesc[1].format = dsFormat;
969 attDesc[1].samples = sampleCount;
970 attDesc[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
971 attDesc[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
972 attDesc[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
973 attDesc[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
974 attDesc[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
975 attDesc[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
976
977 if (msaa) {
978 // msaa render target
979 attDesc[2].format = colorFormat;
980 attDesc[2].samples = sampleCount;
981 attDesc[2].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
982 attDesc[2].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
983 attDesc[2].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
984 attDesc[2].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
985 attDesc[2].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
986 attDesc[2].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
987 }
988
989 VkAttachmentReference colorRef = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
990 VkAttachmentReference resolveRef = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
991 VkAttachmentReference dsRef = { 1, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
992
993 VkSubpassDescription subPassDesc;
994 memset(&subPassDesc, 0, sizeof(subPassDesc));
995 subPassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
996 subPassDesc.colorAttachmentCount = 1;
997 subPassDesc.pColorAttachments = &colorRef;
998 subPassDesc.pDepthStencilAttachment = &dsRef;
999
1000 VkRenderPassCreateInfo rpInfo;
1001 memset(&rpInfo, 0, sizeof(rpInfo));
1002 rpInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
1003 rpInfo.attachmentCount = 2;
1004 rpInfo.pAttachments = attDesc;
1005 rpInfo.subpassCount = 1;
1006 rpInfo.pSubpasses = &subPassDesc;
1007
1008 if (msaa) {
1009 colorRef.attachment = 2;
1010 subPassDesc.pResolveAttachments = &resolveRef;
1011 rpInfo.attachmentCount = 3;
1012 }
1013
1014 VkResult err = devFuncs->vkCreateRenderPass(dev, &rpInfo, nullptr, &defaultRenderPass);
1015 if (err != VK_SUCCESS) {
1016 qWarning("QVulkanWindow: Failed to create renderpass: %d", err);
1017 return false;
1018 }
1019
1020 return true;
1021}
1022
1023void QVulkanWindowPrivate::recreateSwapChain()
1024{
1025 Q_Q(QVulkanWindow);
1026 Q_ASSERT(status >= StatusDeviceReady);
1027
1028 swapChainImageSize = q->size() * q->devicePixelRatio(); // note: may change below due to surfaceCaps
1029
1030 if (swapChainImageSize.isEmpty()) // handle null window size gracefully
1031 return;
1032
1033 QVulkanInstance *inst = q->vulkanInstance();
1034 QVulkanFunctions *f = inst->functions();
1035 devFuncs->vkDeviceWaitIdle(dev);
1036
1037 if (!vkCreateSwapchainKHR) {
1038 vkCreateSwapchainKHR = reinterpret_cast<PFN_vkCreateSwapchainKHR>(f->vkGetDeviceProcAddr(dev, "vkCreateSwapchainKHR"));
1039 vkDestroySwapchainKHR = reinterpret_cast<PFN_vkDestroySwapchainKHR>(f->vkGetDeviceProcAddr(dev, "vkDestroySwapchainKHR"));
1040 vkGetSwapchainImagesKHR = reinterpret_cast<PFN_vkGetSwapchainImagesKHR>(f->vkGetDeviceProcAddr(dev, "vkGetSwapchainImagesKHR"));
1041 vkAcquireNextImageKHR = reinterpret_cast<PFN_vkAcquireNextImageKHR>(f->vkGetDeviceProcAddr(dev, "vkAcquireNextImageKHR"));
1042 vkQueuePresentKHR = reinterpret_cast<PFN_vkQueuePresentKHR>(f->vkGetDeviceProcAddr(dev, "vkQueuePresentKHR"));
1043 }
1044
1045 VkPhysicalDevice physDev = physDevs.at(physDevIndex);
1046 VkSurfaceCapabilitiesKHR surfaceCaps;
1047 vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physDev, surface, &surfaceCaps);
1048 uint32_t reqBufferCount;
1049 if (surfaceCaps.maxImageCount == 0)
1050 reqBufferCount = qMax<uint32_t>(2, surfaceCaps.minImageCount);
1051 else
1052 reqBufferCount = qMax(qMin<uint32_t>(surfaceCaps.maxImageCount, 3), surfaceCaps.minImageCount);
1053
1054 VkExtent2D bufferSize = surfaceCaps.currentExtent;
1055 if (bufferSize.width == uint32_t(-1)) {
1056 Q_ASSERT(bufferSize.height == uint32_t(-1));
1057 bufferSize.width = swapChainImageSize.width();
1058 bufferSize.height = swapChainImageSize.height();
1059 } else {
1060 swapChainImageSize = QSize(bufferSize.width, bufferSize.height);
1061 }
1062
1063 VkSurfaceTransformFlagBitsKHR preTransform =
1064 (surfaceCaps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)
1065 ? VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR
1066 : surfaceCaps.currentTransform;
1067
1068 VkCompositeAlphaFlagBitsKHR compositeAlpha =
1069 (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR)
1070 ? VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR
1071 : VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
1072
1073 if (q->requestedFormat().hasAlpha()) {
1074 if (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR)
1075 compositeAlpha = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;
1076 else if (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR)
1077 compositeAlpha = VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR;
1078 }
1079
1080 VkImageUsageFlags usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
1081 swapChainSupportsReadBack = (surfaceCaps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
1082 if (swapChainSupportsReadBack)
1083 usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
1084
1085 VkSwapchainKHR oldSwapChain = swapChain;
1086 VkSwapchainCreateInfoKHR swapChainInfo;
1087 memset(&swapChainInfo, 0, sizeof(swapChainInfo));
1088 swapChainInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
1089 swapChainInfo.surface = surface;
1090 swapChainInfo.minImageCount = reqBufferCount;
1091 swapChainInfo.imageFormat = colorFormat;
1092 swapChainInfo.imageColorSpace = colorSpace;
1093 swapChainInfo.imageExtent = bufferSize;
1094 swapChainInfo.imageArrayLayers = 1;
1095 swapChainInfo.imageUsage = usage;
1096 swapChainInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
1097 swapChainInfo.preTransform = preTransform;
1098 swapChainInfo.compositeAlpha = compositeAlpha;
1099 swapChainInfo.presentMode = presentMode;
1100 swapChainInfo.clipped = true;
1101 swapChainInfo.oldSwapchain = oldSwapChain;
1102
1103 qCDebug(lcGuiVk, "Creating new swap chain of %d buffers, size %dx%d", reqBufferCount, bufferSize.width, bufferSize.height);
1104
1105 VkSwapchainKHR newSwapChain;
1106 VkResult err = vkCreateSwapchainKHR(dev, &swapChainInfo, nullptr, &newSwapChain);
1107 if (err != VK_SUCCESS) {
1108 qWarning("QVulkanWindow: Failed to create swap chain: %d", err);
1109 return;
1110 }
1111
1112 if (oldSwapChain)
1113 releaseSwapChain();
1114
1115 swapChain = newSwapChain;
1116
1117 uint32_t actualSwapChainBufferCount = 0;
1118 err = vkGetSwapchainImagesKHR(dev, swapChain, &actualSwapChainBufferCount, nullptr);
1119 if (err != VK_SUCCESS || actualSwapChainBufferCount < 2) {
1120 qWarning("QVulkanWindow: Failed to get swapchain images: %d (count=%d)", err, actualSwapChainBufferCount);
1121 return;
1122 }
1123
1124 qCDebug(lcGuiVk, "Actual swap chain buffer count: %d (supportsReadback=%d)",
1125 actualSwapChainBufferCount, swapChainSupportsReadBack);
1126 if (actualSwapChainBufferCount > MAX_SWAPCHAIN_BUFFER_COUNT) {
1127 qWarning("QVulkanWindow: Too many swapchain buffers (%d)", actualSwapChainBufferCount);
1128 return;
1129 }
1130 swapChainBufferCount = actualSwapChainBufferCount;
1131
1132 VkImage swapChainImages[MAX_SWAPCHAIN_BUFFER_COUNT];
1133 err = vkGetSwapchainImagesKHR(dev, swapChain, &actualSwapChainBufferCount, swapChainImages);
1134 if (err != VK_SUCCESS) {
1135 qWarning("QVulkanWindow: Failed to get swapchain images: %d", err);
1136 return;
1137 }
1138
1139 if (!createTransientImage(dsFormat,
1140 VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
1141 VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT,
1142 &dsImage,
1143 &dsMem,
1144 &dsView,
1145 1))
1146 {
1147 return;
1148 }
1149
1150 const bool msaa = sampleCount > VK_SAMPLE_COUNT_1_BIT;
1151 VkImage msaaImages[MAX_SWAPCHAIN_BUFFER_COUNT];
1152 VkImageView msaaViews[MAX_SWAPCHAIN_BUFFER_COUNT];
1153
1154 if (msaa) {
1155 if (!createTransientImage(colorFormat,
1156 VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
1157 VK_IMAGE_ASPECT_COLOR_BIT,
1158 msaaImages,
1159 &msaaImageMem,
1160 msaaViews,
1161 swapChainBufferCount))
1162 {
1163 return;
1164 }
1165 }
1166
1167 VkFenceCreateInfo fenceInfo = { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, nullptr, VK_FENCE_CREATE_SIGNALED_BIT };
1168
1169 for (int i = 0; i < swapChainBufferCount; ++i) {
1170 ImageResources &image(imageRes[i]);
1171 image.image = swapChainImages[i];
1172
1173 if (msaa) {
1174 image.msaaImage = msaaImages[i];
1175 image.msaaImageView = msaaViews[i];
1176 }
1177
1178 VkImageViewCreateInfo imgViewInfo;
1179 memset(&imgViewInfo, 0, sizeof(imgViewInfo));
1180 imgViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
1181 imgViewInfo.image = swapChainImages[i];
1182 imgViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
1183 imgViewInfo.format = colorFormat;
1184 imgViewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
1185 imgViewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
1186 imgViewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
1187 imgViewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
1188 imgViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1189 imgViewInfo.subresourceRange.levelCount = imgViewInfo.subresourceRange.layerCount = 1;
1190 err = devFuncs->vkCreateImageView(dev, &imgViewInfo, nullptr, &image.imageView);
1191 if (err != VK_SUCCESS) {
1192 qWarning("QVulkanWindow: Failed to create swapchain image view %d: %d", i, err);
1193 return;
1194 }
1195
1196 err = devFuncs->vkCreateFence(dev, &fenceInfo, nullptr, &image.cmdFence);
1197 if (err != VK_SUCCESS) {
1198 qWarning("QVulkanWindow: Failed to create command buffer fence: %d", err);
1199 return;
1200 }
1201 image.cmdFenceWaitable = true; // fence was created in signaled state
1202
1203 VkImageView views[3] = { image.imageView,
1204 dsView,
1205 msaa ? image.msaaImageView : VK_NULL_HANDLE };
1206 VkFramebufferCreateInfo fbInfo;
1207 memset(&fbInfo, 0, sizeof(fbInfo));
1208 fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
1209 fbInfo.renderPass = defaultRenderPass;
1210 fbInfo.attachmentCount = msaa ? 3 : 2;
1211 fbInfo.pAttachments = views;
1212 fbInfo.width = swapChainImageSize.width();
1213 fbInfo.height = swapChainImageSize.height();
1214 fbInfo.layers = 1;
1215 VkResult err = devFuncs->vkCreateFramebuffer(dev, &fbInfo, nullptr, &image.fb);
1216 if (err != VK_SUCCESS) {
1217 qWarning("QVulkanWindow: Failed to create framebuffer: %d", err);
1218 return;
1219 }
1220
1221 if (gfxQueueFamilyIdx != presQueueFamilyIdx) {
1222 // pre-build the static image-acquire-on-present-queue command buffer
1223 VkCommandBufferAllocateInfo cmdBufInfo = {
1224 VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, nullptr, presCmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1 };
1225 err = devFuncs->vkAllocateCommandBuffers(dev, &cmdBufInfo, &image.presTransCmdBuf);
1226 if (err != VK_SUCCESS) {
1227 qWarning("QVulkanWindow: Failed to allocate acquire-on-present-queue command buffer: %d", err);
1228 return;
1229 }
1230 VkCommandBufferBeginInfo cmdBufBeginInfo = {
1231 VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, nullptr,
1232 VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT, nullptr };
1233 err = devFuncs->vkBeginCommandBuffer(image.presTransCmdBuf, &cmdBufBeginInfo);
1234 if (err != VK_SUCCESS) {
1235 qWarning("QVulkanWindow: Failed to begin acquire-on-present-queue command buffer: %d", err);
1236 return;
1237 }
1238 VkImageMemoryBarrier presTrans;
1239 memset(&presTrans, 0, sizeof(presTrans));
1240 presTrans.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
1241 presTrans.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
1242 presTrans.oldLayout = presTrans.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
1243 presTrans.srcQueueFamilyIndex = gfxQueueFamilyIdx;
1244 presTrans.dstQueueFamilyIndex = presQueueFamilyIdx;
1245 presTrans.image = image.image;
1246 presTrans.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1247 presTrans.subresourceRange.levelCount = presTrans.subresourceRange.layerCount = 1;
1248 devFuncs->vkCmdPipelineBarrier(image.presTransCmdBuf,
1249 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
1250 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
1251 0, 0, nullptr, 0, nullptr,
1252 1, &presTrans);
1253 err = devFuncs->vkEndCommandBuffer(image.presTransCmdBuf);
1254 if (err != VK_SUCCESS) {
1255 qWarning("QVulkanWindow: Failed to end acquire-on-present-queue command buffer: %d", err);
1256 return;
1257 }
1258 }
1259 }
1260
1261 currentImage = 0;
1262
1263 VkSemaphoreCreateInfo semInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, nullptr, 0 };
1264 for (int i = 0; i < frameLag; ++i) {
1265 FrameResources &frame(frameRes[i]);
1266
1267 frame.imageAcquired = false;
1268 frame.imageSemWaitable = false;
1269
1270 devFuncs->vkCreateFence(dev, &fenceInfo, nullptr, &frame.fence);
1271 frame.fenceWaitable = true; // fence was created in signaled state
1272
1273 devFuncs->vkCreateSemaphore(dev, &semInfo, nullptr, &frame.imageSem);
1274 devFuncs->vkCreateSemaphore(dev, &semInfo, nullptr, &frame.drawSem);
1275 if (gfxQueueFamilyIdx != presQueueFamilyIdx)
1276 devFuncs->vkCreateSemaphore(dev, &semInfo, nullptr, &frame.presTransSem);
1277 }
1278
1279 currentFrame = 0;
1280
1281 if (renderer)
1282 renderer->initSwapChainResources();
1283
1284 status = StatusReady;
1285}
1286
1287uint32_t QVulkanWindowPrivate::chooseTransientImageMemType(VkImage img, uint32_t startIndex)
1288{
1289 VkPhysicalDeviceMemoryProperties physDevMemProps;
1290 inst->functions()->vkGetPhysicalDeviceMemoryProperties(physDevs[physDevIndex], &physDevMemProps);
1291
1292 VkMemoryRequirements memReq;
1293 devFuncs->vkGetImageMemoryRequirements(dev, img, &memReq);
1294 uint32_t memTypeIndex = uint32_t(-1);
1295
1296 if (memReq.memoryTypeBits) {
1297 // Find a device local + lazily allocated, or at least device local memtype.
1298 const VkMemoryType *memType = physDevMemProps.memoryTypes;
1299 bool foundDevLocal = false;
1300 for (uint32_t i = startIndex; i < physDevMemProps.memoryTypeCount; ++i) {
1301 if (memReq.memoryTypeBits & (1 << i)) {
1302 if (memType[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) {
1303 if (!foundDevLocal) {
1304 foundDevLocal = true;
1305 memTypeIndex = i;
1306 }
1307 if (memType[i].propertyFlags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) {
1308 memTypeIndex = i;
1309 break;
1310 }
1311 }
1312 }
1313 }
1314 }
1315
1316 return memTypeIndex;
1317}
1318
1319static inline VkDeviceSize aligned(VkDeviceSize v, VkDeviceSize byteAlign)
1320{
1321 return (v + byteAlign - 1) & ~(byteAlign - 1);
1322}
1323
1324bool QVulkanWindowPrivate::createTransientImage(VkFormat format,
1325 VkImageUsageFlags usage,
1326 VkImageAspectFlags aspectMask,
1327 VkImage *images,
1328 VkDeviceMemory *mem,
1329 VkImageView *views,
1330 int count)
1331{
1332 VkMemoryRequirements memReq;
1333 VkResult err;
1334
1335 Q_ASSERT(count > 0);
1336 for (int i = 0; i < count; ++i) {
1337 VkImageCreateInfo imgInfo;
1338 memset(&imgInfo, 0, sizeof(imgInfo));
1339 imgInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
1340 imgInfo.imageType = VK_IMAGE_TYPE_2D;
1341 imgInfo.format = format;
1342 imgInfo.extent.width = swapChainImageSize.width();
1343 imgInfo.extent.height = swapChainImageSize.height();
1344 imgInfo.extent.depth = 1;
1345 imgInfo.mipLevels = imgInfo.arrayLayers = 1;
1346 imgInfo.samples = sampleCount;
1347 imgInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
1348 imgInfo.usage = usage | VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT;
1349
1350 err = devFuncs->vkCreateImage(dev, &imgInfo, nullptr, images + i);
1351 if (err != VK_SUCCESS) {
1352 qWarning("QVulkanWindow: Failed to create image: %d", err);
1353 return false;
1354 }
1355
1356 // Assume the reqs are the same since the images are same in every way.
1357 // Still, call GetImageMemReq for every image, in order to prevent the
1358 // validation layer from complaining.
1359 devFuncs->vkGetImageMemoryRequirements(dev, images[i], &memReq);
1360 }
1361
1362 VkMemoryAllocateInfo memInfo;
1363 memset(&memInfo, 0, sizeof(memInfo));
1364 memInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
1365 memInfo.allocationSize = aligned(memReq.size, memReq.alignment) * count;
1366
1367 uint32_t startIndex = 0;
1368 do {
1369 memInfo.memoryTypeIndex = chooseTransientImageMemType(images[0], startIndex);
1370 if (memInfo.memoryTypeIndex == uint32_t(-1)) {
1371 qWarning("QVulkanWindow: No suitable memory type found");
1372 return false;
1373 }
1374 startIndex = memInfo.memoryTypeIndex + 1;
1375 qCDebug(lcGuiVk, "Allocating %u bytes for transient image (memtype %u)",
1376 uint32_t(memInfo.allocationSize), memInfo.memoryTypeIndex);
1377 err = devFuncs->vkAllocateMemory(dev, &memInfo, nullptr, mem);
1378 if (err != VK_SUCCESS && err != VK_ERROR_OUT_OF_DEVICE_MEMORY) {
1379 qWarning("QVulkanWindow: Failed to allocate image memory: %d", err);
1380 return false;
1381 }
1382 } while (err != VK_SUCCESS);
1383
1384 VkDeviceSize ofs = 0;
1385 for (int i = 0; i < count; ++i) {
1386 err = devFuncs->vkBindImageMemory(dev, images[i], *mem, ofs);
1387 if (err != VK_SUCCESS) {
1388 qWarning("QVulkanWindow: Failed to bind image memory: %d", err);
1389 return false;
1390 }
1391 ofs += aligned(memReq.size, memReq.alignment);
1392
1393 VkImageViewCreateInfo imgViewInfo;
1394 memset(&imgViewInfo, 0, sizeof(imgViewInfo));
1395 imgViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
1396 imgViewInfo.image = images[i];
1397 imgViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
1398 imgViewInfo.format = format;
1399 imgViewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
1400 imgViewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
1401 imgViewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
1402 imgViewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
1403 imgViewInfo.subresourceRange.aspectMask = aspectMask;
1404 imgViewInfo.subresourceRange.levelCount = imgViewInfo.subresourceRange.layerCount = 1;
1405
1406 err = devFuncs->vkCreateImageView(dev, &imgViewInfo, nullptr, views + i);
1407 if (err != VK_SUCCESS) {
1408 qWarning("QVulkanWindow: Failed to create image view: %d", err);
1409 return false;
1410 }
1411 }
1412
1413 return true;
1414}
1415
1416void QVulkanWindowPrivate::releaseSwapChain()
1417{
1418 if (!dev || !swapChain) // do not rely on 'status', a half done init must be cleaned properly too
1419 return;
1420
1421 qCDebug(lcGuiVk, "Releasing swapchain");
1422
1423 devFuncs->vkDeviceWaitIdle(dev);
1424
1425 if (renderer) {
1426 renderer->releaseSwapChainResources();
1427 devFuncs->vkDeviceWaitIdle(dev);
1428 }
1429
1430 for (int i = 0; i < frameLag; ++i) {
1431 FrameResources &frame(frameRes[i]);
1432 if (frame.fence) {
1433 if (frame.fenceWaitable)
1434 devFuncs->vkWaitForFences(dev, 1, &frame.fence, VK_TRUE, UINT64_MAX);
1435 devFuncs->vkDestroyFence(dev, frame.fence, nullptr);
1436 frame.fence = VK_NULL_HANDLE;
1437 frame.fenceWaitable = false;
1438 }
1439 if (frame.imageSem) {
1440 devFuncs->vkDestroySemaphore(dev, frame.imageSem, nullptr);
1441 frame.imageSem = VK_NULL_HANDLE;
1442 }
1443 if (frame.drawSem) {
1444 devFuncs->vkDestroySemaphore(dev, frame.drawSem, nullptr);
1445 frame.drawSem = VK_NULL_HANDLE;
1446 }
1447 if (frame.presTransSem) {
1448 devFuncs->vkDestroySemaphore(dev, frame.presTransSem, nullptr);
1449 frame.presTransSem = VK_NULL_HANDLE;
1450 }
1451 }
1452
1453 for (int i = 0; i < swapChainBufferCount; ++i) {
1454 ImageResources &image(imageRes[i]);
1455 if (image.cmdFence) {
1456 if (image.cmdFenceWaitable)
1457 devFuncs->vkWaitForFences(dev, 1, &image.cmdFence, VK_TRUE, UINT64_MAX);
1458 devFuncs->vkDestroyFence(dev, image.cmdFence, nullptr);
1459 image.cmdFence = VK_NULL_HANDLE;
1460 image.cmdFenceWaitable = false;
1461 }
1462 if (image.fb) {
1463 devFuncs->vkDestroyFramebuffer(dev, image.fb, nullptr);
1464 image.fb = VK_NULL_HANDLE;
1465 }
1466 if (image.imageView) {
1467 devFuncs->vkDestroyImageView(dev, image.imageView, nullptr);
1468 image.imageView = VK_NULL_HANDLE;
1469 }
1470 if (image.cmdBuf) {
1471 devFuncs->vkFreeCommandBuffers(dev, cmdPool, 1, &image.cmdBuf);
1472 image.cmdBuf = VK_NULL_HANDLE;
1473 }
1474 if (image.presTransCmdBuf) {
1475 devFuncs->vkFreeCommandBuffers(dev, presCmdPool, 1, &image.presTransCmdBuf);
1476 image.presTransCmdBuf = VK_NULL_HANDLE;
1477 }
1478 if (image.msaaImageView) {
1479 devFuncs->vkDestroyImageView(dev, image.msaaImageView, nullptr);
1480 image.msaaImageView = VK_NULL_HANDLE;
1481 }
1482 if (image.msaaImage) {
1483 devFuncs->vkDestroyImage(dev, image.msaaImage, nullptr);
1484 image.msaaImage = VK_NULL_HANDLE;
1485 }
1486 }
1487
1488 if (msaaImageMem) {
1489 devFuncs->vkFreeMemory(dev, msaaImageMem, nullptr);
1490 msaaImageMem = VK_NULL_HANDLE;
1491 }
1492
1493 if (dsView) {
1494 devFuncs->vkDestroyImageView(dev, dsView, nullptr);
1495 dsView = VK_NULL_HANDLE;
1496 }
1497 if (dsImage) {
1498 devFuncs->vkDestroyImage(dev, dsImage, nullptr);
1499 dsImage = VK_NULL_HANDLE;
1500 }
1501 if (dsMem) {
1502 devFuncs->vkFreeMemory(dev, dsMem, nullptr);
1503 dsMem = VK_NULL_HANDLE;
1504 }
1505
1506 if (swapChain) {
1507 vkDestroySwapchainKHR(dev, swapChain, nullptr);
1508 swapChain = VK_NULL_HANDLE;
1509 }
1510
1511 if (status == StatusReady)
1512 status = StatusDeviceReady;
1513}
1514
1518void QVulkanWindow::exposeEvent(QExposeEvent *)
1519{
1520 Q_D(QVulkanWindow);
1521
1522 if (isExposed()) {
1523 d->ensureStarted();
1524 } else {
1525 if (!d->flags.testFlag(PersistentResources)) {
1526 d->releaseSwapChain();
1527 d->reset();
1528 }
1529 }
1530}
1531
1532void QVulkanWindowPrivate::ensureStarted()
1533{
1534 Q_Q(QVulkanWindow);
1535 if (status == QVulkanWindowPrivate::StatusFailRetry)
1536 status = QVulkanWindowPrivate::StatusUninitialized;
1537 if (status == QVulkanWindowPrivate::StatusUninitialized) {
1538 init();
1539 if (status == QVulkanWindowPrivate::StatusDeviceReady)
1540 recreateSwapChain();
1541 }
1542 if (status == QVulkanWindowPrivate::StatusReady)
1543 q->requestUpdate();
1544}
1545
1549void QVulkanWindow::resizeEvent(QResizeEvent *)
1550{
1551 // Nothing to do here - recreating the swapchain is handled when building the next frame.
1552}
1553
1557bool QVulkanWindow::event(QEvent *e)
1558{
1559 Q_D(QVulkanWindow);
1560
1561 switch (e->type()) {
1563 d->beginFrame();
1564 break;
1565
1566 // The swapchain must be destroyed before the surface as per spec. This is
1567 // not ideal for us because the surface is managed by the QPlatformWindow
1568 // which may be gone already when the unexpose comes, making the validation
1569 // layer scream. The solution is to listen to the PlatformSurface events.
1571 if (static_cast<QPlatformSurfaceEvent *>(e)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) {
1572 d->releaseSwapChain();
1573 d->reset();
1574 }
1575 break;
1576
1577 default:
1578 break;
1579 }
1580
1581 return QWindow::event(e);
1582}
1583
1610void QVulkanWindow::setQueueCreateInfoModifier(const QueueCreateInfoModifier &modifier)
1611{
1612 Q_D(QVulkanWindow);
1613 d->queueCreateInfoModifier = modifier;
1614}
1615
1649void QVulkanWindow::setEnabledFeaturesModifier(const EnabledFeaturesModifier &modifier)
1650{
1651 Q_D(QVulkanWindow);
1652 d->enabledFeaturesModifier = modifier;
1653}
1654
1662bool QVulkanWindow::isValid() const
1663{
1664 Q_D(const QVulkanWindow);
1665 return d->status == QVulkanWindowPrivate::StatusReady;
1666}
1667
1679QVulkanWindowRenderer *QVulkanWindow::createRenderer()
1680{
1681 return nullptr;
1682}
1683
1687QVulkanWindowRenderer::~QVulkanWindowRenderer()
1688{
1689}
1690
1707void QVulkanWindowRenderer::preInitResources()
1708{
1709}
1710
1726void QVulkanWindowRenderer::initResources()
1727{
1728}
1729
1748void QVulkanWindowRenderer::initSwapChainResources()
1749{
1750}
1751
1771void QVulkanWindowRenderer::releaseSwapChainResources()
1772{
1773}
1774
1787void QVulkanWindowRenderer::releaseResources()
1788{
1789}
1790
1832void QVulkanWindowRenderer::physicalDeviceLost()
1833{
1834}
1835
1851void QVulkanWindowRenderer::logicalDeviceLost()
1852{
1853}
1854
1855void QVulkanWindowPrivate::beginFrame()
1856{
1857 if (!swapChain || framePending)
1858 return;
1859
1860 Q_Q(QVulkanWindow);
1861 if (q->size() * q->devicePixelRatio() != swapChainImageSize) {
1862 recreateSwapChain();
1863 if (!swapChain)
1864 return;
1865 }
1866
1867 FrameResources &frame(frameRes[currentFrame]);
1868
1869 if (!frame.imageAcquired) {
1870 // Wait if we are too far ahead, i.e. the thread gets throttled based on the presentation rate
1871 // (note that we are using FIFO mode -> vsync)
1872 if (frame.fenceWaitable) {
1873 devFuncs->vkWaitForFences(dev, 1, &frame.fence, VK_TRUE, UINT64_MAX);
1874 devFuncs->vkResetFences(dev, 1, &frame.fence);
1875 frame.fenceWaitable = false;
1876 }
1877
1878 // move on to next swapchain image
1879 VkResult err = vkAcquireNextImageKHR(dev, swapChain, UINT64_MAX,
1880 frame.imageSem, frame.fence, &currentImage);
1881 if (err == VK_SUCCESS || err == VK_SUBOPTIMAL_KHR) {
1882 frame.imageSemWaitable = true;
1883 frame.imageAcquired = true;
1884 frame.fenceWaitable = true;
1885 } else if (err == VK_ERROR_OUT_OF_DATE_KHR) {
1886 recreateSwapChain();
1887 q->requestUpdate();
1888 return;
1889 } else {
1890 if (!checkDeviceLost(err))
1891 qWarning("QVulkanWindow: Failed to acquire next swapchain image: %d", err);
1892 q->requestUpdate();
1893 return;
1894 }
1895 }
1896
1897 // make sure the previous draw for the same image has finished
1898 ImageResources &image(imageRes[currentImage]);
1899 if (image.cmdFenceWaitable) {
1900 devFuncs->vkWaitForFences(dev, 1, &image.cmdFence, VK_TRUE, UINT64_MAX);
1901 devFuncs->vkResetFences(dev, 1, &image.cmdFence);
1902 image.cmdFenceWaitable = false;
1903 }
1904
1905 // build new draw command buffer
1906 if (image.cmdBuf) {
1907 devFuncs->vkFreeCommandBuffers(dev, cmdPool, 1, &image.cmdBuf);
1908 image.cmdBuf = nullptr;
1909 }
1910
1911 VkCommandBufferAllocateInfo cmdBufInfo = {
1912 VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, nullptr, cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1 };
1913 VkResult err = devFuncs->vkAllocateCommandBuffers(dev, &cmdBufInfo, &image.cmdBuf);
1914 if (err != VK_SUCCESS) {
1915 if (!checkDeviceLost(err))
1916 qWarning("QVulkanWindow: Failed to allocate frame command buffer: %d", err);
1917 return;
1918 }
1919
1920 VkCommandBufferBeginInfo cmdBufBeginInfo = {
1921 VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, nullptr, 0, nullptr };
1922 err = devFuncs->vkBeginCommandBuffer(image.cmdBuf, &cmdBufBeginInfo);
1923 if (err != VK_SUCCESS) {
1924 if (!checkDeviceLost(err))
1925 qWarning("QVulkanWindow: Failed to begin frame command buffer: %d", err);
1926 return;
1927 }
1928
1929 if (frameGrabbing)
1930 frameGrabTargetImage = QImage(swapChainImageSize, QImage::Format_RGBA8888);
1931
1932 if (renderer) {
1933 framePending = true;
1934 renderer->startNextFrame();
1935 // done for now - endFrame() will get invoked when frameReady() is called back
1936 } else {
1937 VkClearColorValue clearColor = { { 0.0f, 0.0f, 0.0f, 1.0f } };
1938 VkClearDepthStencilValue clearDS = { 1.0f, 0 };
1939 VkClearValue clearValues[3];
1940 memset(clearValues, 0, sizeof(clearValues));
1941 clearValues[0].color = clearValues[2].color = clearColor;
1942 clearValues[1].depthStencil = clearDS;
1943
1944 VkRenderPassBeginInfo rpBeginInfo;
1945 memset(&rpBeginInfo, 0, sizeof(rpBeginInfo));
1946 rpBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
1947 rpBeginInfo.renderPass = defaultRenderPass;
1948 rpBeginInfo.framebuffer = image.fb;
1949 rpBeginInfo.renderArea.extent.width = swapChainImageSize.width();
1950 rpBeginInfo.renderArea.extent.height = swapChainImageSize.height();
1951 rpBeginInfo.clearValueCount = sampleCount > VK_SAMPLE_COUNT_1_BIT ? 3 : 2;
1952 rpBeginInfo.pClearValues = clearValues;
1953 devFuncs->vkCmdBeginRenderPass(image.cmdBuf, &rpBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
1954 devFuncs->vkCmdEndRenderPass(image.cmdBuf);
1955
1956 endFrame();
1957 }
1958}
1959
1960void QVulkanWindowPrivate::endFrame()
1961{
1962 Q_Q(QVulkanWindow);
1963
1964 FrameResources &frame(frameRes[currentFrame]);
1965 ImageResources &image(imageRes[currentImage]);
1966
1967 if (gfxQueueFamilyIdx != presQueueFamilyIdx && !frameGrabbing) {
1968 // Add the swapchain image release to the command buffer that will be
1969 // submitted to the graphics queue.
1970 VkImageMemoryBarrier presTrans;
1971 memset(&presTrans, 0, sizeof(presTrans));
1972 presTrans.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
1973 presTrans.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
1974 presTrans.oldLayout = presTrans.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
1975 presTrans.srcQueueFamilyIndex = gfxQueueFamilyIdx;
1976 presTrans.dstQueueFamilyIndex = presQueueFamilyIdx;
1977 presTrans.image = image.image;
1978 presTrans.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1979 presTrans.subresourceRange.levelCount = presTrans.subresourceRange.layerCount = 1;
1980 devFuncs->vkCmdPipelineBarrier(image.cmdBuf,
1981 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
1982 VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
1983 0, 0, nullptr, 0, nullptr,
1984 1, &presTrans);
1985 }
1986
1987 // When grabbing a frame, add a readback at the end and skip presenting.
1988 if (frameGrabbing)
1989 addReadback();
1990
1991 VkResult err = devFuncs->vkEndCommandBuffer(image.cmdBuf);
1992 if (err != VK_SUCCESS) {
1993 if (!checkDeviceLost(err))
1994 qWarning("QVulkanWindow: Failed to end frame command buffer: %d", err);
1995 return;
1996 }
1997
1998 // submit draw calls
1999 VkSubmitInfo submitInfo;
2000 memset(&submitInfo, 0, sizeof(submitInfo));
2001 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
2002 submitInfo.commandBufferCount = 1;
2003 submitInfo.pCommandBuffers = &image.cmdBuf;
2004 if (frame.imageSemWaitable) {
2005 submitInfo.waitSemaphoreCount = 1;
2006 submitInfo.pWaitSemaphores = &frame.imageSem;
2007 }
2008 if (!frameGrabbing) {
2009 submitInfo.signalSemaphoreCount = 1;
2010 submitInfo.pSignalSemaphores = &frame.drawSem;
2011 }
2012 VkPipelineStageFlags psf = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
2013 submitInfo.pWaitDstStageMask = &psf;
2014
2015 Q_ASSERT(!image.cmdFenceWaitable);
2016
2017 err = devFuncs->vkQueueSubmit(gfxQueue, 1, &submitInfo, image.cmdFence);
2018 if (err == VK_SUCCESS) {
2019 frame.imageSemWaitable = false;
2020 image.cmdFenceWaitable = true;
2021 } else {
2022 if (!checkDeviceLost(err))
2023 qWarning("QVulkanWindow: Failed to submit to graphics queue: %d", err);
2024 return;
2025 }
2026
2027 // block and then bail out when grabbing
2028 if (frameGrabbing) {
2029 finishBlockingReadback();
2030 frameGrabbing = false;
2031 // Leave frame.imageAcquired set to true.
2032 // Do not change currentFrame.
2033 emit q->frameGrabbed(frameGrabTargetImage);
2034 return;
2035 }
2036
2037 if (gfxQueueFamilyIdx != presQueueFamilyIdx) {
2038 // Submit the swapchain image acquire to the present queue.
2039 submitInfo.pWaitSemaphores = &frame.drawSem;
2040 submitInfo.pSignalSemaphores = &frame.presTransSem;
2041 submitInfo.pCommandBuffers = &image.presTransCmdBuf; // must be USAGE_SIMULTANEOUS
2042 err = devFuncs->vkQueueSubmit(presQueue, 1, &submitInfo, VK_NULL_HANDLE);
2043 if (err != VK_SUCCESS) {
2044 if (!checkDeviceLost(err))
2045 qWarning("QVulkanWindow: Failed to submit to present queue: %d", err);
2046 return;
2047 }
2048 }
2049
2050 // queue present
2051 VkPresentInfoKHR presInfo;
2052 memset(&presInfo, 0, sizeof(presInfo));
2053 presInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
2054 presInfo.swapchainCount = 1;
2055 presInfo.pSwapchains = &swapChain;
2056 presInfo.pImageIndices = &currentImage;
2057 presInfo.waitSemaphoreCount = 1;
2058 presInfo.pWaitSemaphores = gfxQueueFamilyIdx == presQueueFamilyIdx ? &frame.drawSem : &frame.presTransSem;
2059
2060 // Do platform-specific WM notification. F.ex. essential on Wayland in
2061 // order to circumvent driver frame callbacks
2062 inst->presentAboutToBeQueued(q);
2063
2064 err = vkQueuePresentKHR(presQueue, &presInfo);
2065 if (err != VK_SUCCESS) {
2066 if (err == VK_ERROR_OUT_OF_DATE_KHR) {
2067 recreateSwapChain();
2068 q->requestUpdate();
2069 return;
2070 } else if (err != VK_SUBOPTIMAL_KHR) {
2071 if (!checkDeviceLost(err))
2072 qWarning("QVulkanWindow: Failed to present: %d", err);
2073 return;
2074 }
2075 }
2076
2077 frame.imageAcquired = false;
2078
2079 inst->presentQueued(q);
2080
2081 currentFrame = (currentFrame + 1) % frameLag;
2082}
2083
2097void QVulkanWindow::frameReady()
2098{
2100 "QVulkanWindow", "frameReady() can only be called from the GUI (main) thread");
2101
2102 Q_D(QVulkanWindow);
2103
2104 if (!d->framePending) {
2105 qWarning("QVulkanWindow: frameReady() called without a corresponding startNextFrame()");
2106 return;
2107 }
2108
2109 d->framePending = false;
2110
2111 d->endFrame();
2112}
2113
2114bool QVulkanWindowPrivate::checkDeviceLost(VkResult err)
2115{
2116 if (err == VK_ERROR_DEVICE_LOST) {
2117 qWarning("QVulkanWindow: Device lost");
2118 if (renderer)
2119 renderer->logicalDeviceLost();
2120 qCDebug(lcGuiVk, "Releasing all resources due to device lost");
2121 releaseSwapChain();
2122 reset();
2123 qCDebug(lcGuiVk, "Restarting");
2124 ensureStarted();
2125 return true;
2126 }
2127 return false;
2128}
2129
2130void QVulkanWindowPrivate::addReadback()
2131{
2132 VkImageCreateInfo imageInfo;
2133 memset(&imageInfo, 0, sizeof(imageInfo));
2134 imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
2135 imageInfo.imageType = VK_IMAGE_TYPE_2D;
2136 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
2137 imageInfo.extent.width = frameGrabTargetImage.width();
2138 imageInfo.extent.height = frameGrabTargetImage.height();
2139 imageInfo.extent.depth = 1;
2140 imageInfo.mipLevels = 1;
2141 imageInfo.arrayLayers = 1;
2142 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
2143 imageInfo.tiling = VK_IMAGE_TILING_LINEAR;
2144 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT;
2145 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
2146
2147 VkResult err = devFuncs->vkCreateImage(dev, &imageInfo, nullptr, &frameGrabImage);
2148 if (err != VK_SUCCESS) {
2149 qWarning("QVulkanWindow: Failed to create image for readback: %d", err);
2150 return;
2151 }
2152
2153 VkMemoryRequirements memReq;
2154 devFuncs->vkGetImageMemoryRequirements(dev, frameGrabImage, &memReq);
2155
2156 VkMemoryAllocateInfo allocInfo = {
2157 VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
2158 nullptr,
2159 memReq.size,
2160 hostVisibleMemIndex
2161 };
2162
2163 err = devFuncs->vkAllocateMemory(dev, &allocInfo, nullptr, &frameGrabImageMem);
2164 if (err != VK_SUCCESS) {
2165 qWarning("QVulkanWindow: Failed to allocate memory for readback image: %d", err);
2166 return;
2167 }
2168
2169 err = devFuncs->vkBindImageMemory(dev, frameGrabImage, frameGrabImageMem, 0);
2170 if (err != VK_SUCCESS) {
2171 qWarning("QVulkanWindow: Failed to bind readback image memory: %d", err);
2172 return;
2173 }
2174
2175 ImageResources &image(imageRes[currentImage]);
2176
2177 VkImageMemoryBarrier barrier;
2178 memset(&barrier, 0, sizeof(barrier));
2179 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
2180 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
2181 barrier.subresourceRange.levelCount = barrier.subresourceRange.layerCount = 1;
2182
2183 barrier.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
2184 barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
2185 barrier.srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;
2186 barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
2187 barrier.image = image.image;
2188
2189 devFuncs->vkCmdPipelineBarrier(image.cmdBuf,
2190 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
2191 VK_PIPELINE_STAGE_TRANSFER_BIT,
2192 0, 0, nullptr, 0, nullptr,
2193 1, &barrier);
2194
2195 barrier.oldLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
2196 barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
2197 barrier.srcAccessMask = 0;
2198 barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
2199 barrier.image = frameGrabImage;
2200
2201 devFuncs->vkCmdPipelineBarrier(image.cmdBuf,
2202 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
2203 VK_PIPELINE_STAGE_TRANSFER_BIT,
2204 0, 0, nullptr, 0, nullptr,
2205 1, &barrier);
2206
2207 VkImageCopy copyInfo;
2208 memset(&copyInfo, 0, sizeof(copyInfo));
2209 copyInfo.srcSubresource.aspectMask = copyInfo.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
2210 copyInfo.srcSubresource.layerCount = copyInfo.dstSubresource.layerCount = 1;
2211 copyInfo.extent.width = frameGrabTargetImage.width();
2212 copyInfo.extent.height = frameGrabTargetImage.height();
2213 copyInfo.extent.depth = 1;
2214
2215 devFuncs->vkCmdCopyImage(image.cmdBuf, image.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
2216 frameGrabImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &copyInfo);
2217
2218 barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
2219 barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
2220 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
2221 barrier.dstAccessMask = VK_ACCESS_HOST_READ_BIT;
2222 barrier.image = frameGrabImage;
2223
2224 devFuncs->vkCmdPipelineBarrier(image.cmdBuf,
2225 VK_PIPELINE_STAGE_TRANSFER_BIT,
2226 VK_PIPELINE_STAGE_HOST_BIT,
2227 0, 0, nullptr, 0, nullptr,
2228 1, &barrier);
2229}
2230
2231void QVulkanWindowPrivate::finishBlockingReadback()
2232{
2233 ImageResources &image(imageRes[currentImage]);
2234
2235 // Block until the current frame is done. Normally this wait would only be
2236 // done in current + concurrentFrameCount().
2237 devFuncs->vkWaitForFences(dev, 1, &image.cmdFence, VK_TRUE, UINT64_MAX);
2238 devFuncs->vkResetFences(dev, 1, &image.cmdFence);
2239 // will reuse the same image for the next "real" frame, do not wait then
2240 image.cmdFenceWaitable = false;
2241
2242 VkImageSubresource subres = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0 };
2243 VkSubresourceLayout layout;
2244 devFuncs->vkGetImageSubresourceLayout(dev, frameGrabImage, &subres, &layout);
2245
2246 uchar *p;
2247 VkResult err = devFuncs->vkMapMemory(dev, frameGrabImageMem, layout.offset, layout.size, 0, reinterpret_cast<void **>(&p));
2248 if (err != VK_SUCCESS) {
2249 qWarning("QVulkanWindow: Failed to map readback image memory after transfer: %d", err);
2250 return;
2251 }
2252
2253 for (int y = 0; y < frameGrabTargetImage.height(); ++y) {
2254 memcpy(frameGrabTargetImage.scanLine(y), p, frameGrabTargetImage.width() * 4);
2255 p += layout.rowPitch;
2256 }
2257
2258 devFuncs->vkUnmapMemory(dev, frameGrabImageMem);
2259
2260 devFuncs->vkDestroyImage(dev, frameGrabImage, nullptr);
2261 frameGrabImage = VK_NULL_HANDLE;
2262 devFuncs->vkFreeMemory(dev, frameGrabImageMem, nullptr);
2263 frameGrabImageMem = VK_NULL_HANDLE;
2264}
2265
2273VkPhysicalDevice QVulkanWindow::physicalDevice() const
2274{
2275 Q_D(const QVulkanWindow);
2276 if (d->physDevIndex < d->physDevs.size())
2277 return d->physDevs[d->physDevIndex];
2278 qWarning("QVulkanWindow: Physical device not available");
2279 return VK_NULL_HANDLE;
2280}
2281
2289const VkPhysicalDeviceProperties *QVulkanWindow::physicalDeviceProperties() const
2290{
2291 Q_D(const QVulkanWindow);
2292 if (d->physDevIndex < d->physDevProps.size())
2293 return &d->physDevProps[d->physDevIndex];
2294 qWarning("QVulkanWindow: Physical device properties not available");
2295 return nullptr;
2296}
2297
2305VkDevice QVulkanWindow::device() const
2306{
2307 Q_D(const QVulkanWindow);
2308 return d->dev;
2309}
2310
2318VkQueue QVulkanWindow::graphicsQueue() const
2319{
2320 Q_D(const QVulkanWindow);
2321 return d->gfxQueue;
2322}
2323
2335uint32_t QVulkanWindow::graphicsQueueFamilyIndex() const
2336{
2337 Q_D(const QVulkanWindow);
2338 return d->gfxQueueFamilyIdx;
2339}
2340
2348VkCommandPool QVulkanWindow::graphicsCommandPool() const
2349{
2350 Q_D(const QVulkanWindow);
2351 return d->cmdPool;
2352}
2353
2364uint32_t QVulkanWindow::hostVisibleMemoryIndex() const
2365{
2366 Q_D(const QVulkanWindow);
2367 return d->hostVisibleMemIndex;
2368}
2369
2382uint32_t QVulkanWindow::deviceLocalMemoryIndex() const
2383{
2384 Q_D(const QVulkanWindow);
2385 return d->deviceLocalMemIndex;
2386}
2387
2406VkRenderPass QVulkanWindow::defaultRenderPass() const
2407{
2408 Q_D(const QVulkanWindow);
2409 return d->defaultRenderPass;
2410}
2411
2421VkFormat QVulkanWindow::colorFormat() const
2422{
2423 Q_D(const QVulkanWindow);
2424 return d->colorFormat;
2425}
2426
2434VkFormat QVulkanWindow::depthStencilFormat() const
2435{
2436 Q_D(const QVulkanWindow);
2437 return d->dsFormat;
2438}
2439
2450QSize QVulkanWindow::swapChainImageSize() const
2451{
2452 Q_D(const QVulkanWindow);
2453 return d->swapChainImageSize;
2454}
2455
2464VkCommandBuffer QVulkanWindow::currentCommandBuffer() const
2465{
2466 Q_D(const QVulkanWindow);
2467 if (!d->framePending) {
2468 qWarning("QVulkanWindow: Attempted to call currentCommandBuffer() without an active frame");
2469 return VK_NULL_HANDLE;
2470 }
2471 return d->imageRes[d->currentImage].cmdBuf;
2472}
2473
2493VkFramebuffer QVulkanWindow::currentFramebuffer() const
2494{
2495 Q_D(const QVulkanWindow);
2496 if (!d->framePending) {
2497 qWarning("QVulkanWindow: Attempted to call currentFramebuffer() without an active frame");
2498 return VK_NULL_HANDLE;
2499 }
2500 return d->imageRes[d->currentImage].fb;
2501}
2502
2524int QVulkanWindow::currentFrame() const
2525{
2526 Q_D(const QVulkanWindow);
2527 if (!d->framePending)
2528 qWarning("QVulkanWindow: Attempted to call currentFrame() without an active frame");
2529 return d->currentFrame;
2530}
2531
2548int QVulkanWindow::concurrentFrameCount() const
2549{
2550 Q_D(const QVulkanWindow);
2551 return d->frameLag;
2552}
2553
2565int QVulkanWindow::swapChainImageCount() const
2566{
2567 Q_D(const QVulkanWindow);
2568 return d->swapChainBufferCount;
2569}
2570
2577int QVulkanWindow::currentSwapChainImageIndex() const
2578{
2579 Q_D(const QVulkanWindow);
2580 if (!d->framePending)
2581 qWarning("QVulkanWindow: Attempted to call currentSwapChainImageIndex() without an active frame");
2582 return d->currentImage;
2583}
2584
2594VkImage QVulkanWindow::swapChainImage(int idx) const
2595{
2596 Q_D(const QVulkanWindow);
2597 return idx >= 0 && idx < d->swapChainBufferCount ? d->imageRes[idx].image : VK_NULL_HANDLE;
2598}
2599
2609VkImageView QVulkanWindow::swapChainImageView(int idx) const
2610{
2611 Q_D(const QVulkanWindow);
2612 return idx >= 0 && idx < d->swapChainBufferCount ? d->imageRes[idx].imageView : VK_NULL_HANDLE;
2613}
2614
2622VkImage QVulkanWindow::depthStencilImage() const
2623{
2624 Q_D(const QVulkanWindow);
2625 return d->dsImage;
2626}
2627
2635VkImageView QVulkanWindow::depthStencilImageView() const
2636{
2637 Q_D(const QVulkanWindow);
2638 return d->dsView;
2639}
2640
2649VkSampleCountFlagBits QVulkanWindow::sampleCountFlagBits() const
2650{
2651 Q_D(const QVulkanWindow);
2652 return d->sampleCount;
2653}
2654
2665VkImage QVulkanWindow::msaaColorImage(int idx) const
2666{
2667 Q_D(const QVulkanWindow);
2668 return idx >= 0 && idx < d->swapChainBufferCount ? d->imageRes[idx].msaaImage : VK_NULL_HANDLE;
2669}
2670
2681VkImageView QVulkanWindow::msaaColorImageView(int idx) const
2682{
2683 Q_D(const QVulkanWindow);
2684 return idx >= 0 && idx < d->swapChainBufferCount ? d->imageRes[idx].msaaImageView : VK_NULL_HANDLE;
2685}
2686
2695bool QVulkanWindow::supportsGrab() const
2696{
2697 Q_D(const QVulkanWindow);
2698 return d->swapChainSupportsReadBack;
2699}
2700
2726QImage QVulkanWindow::grab()
2727{
2728 Q_D(QVulkanWindow);
2729 if (!d->swapChain) {
2730 qWarning("QVulkanWindow: Attempted to call grab() without a swapchain");
2731 return QImage();
2732 }
2733 if (d->framePending) {
2734 qWarning("QVulkanWindow: Attempted to call grab() while a frame is still pending");
2735 return QImage();
2736 }
2737 if (!d->swapChainSupportsReadBack) {
2738 qWarning("QVulkanWindow: Attempted to call grab() with a swapchain that does not support usage as transfer source");
2739 return QImage();
2740 }
2741
2742 d->frameGrabbing = true;
2743 d->beginFrame();
2744
2745 return d->frameGrabTargetImage;
2746}
2747
2759QMatrix4x4 QVulkanWindow::clipCorrectionMatrix()
2760{
2761 Q_D(QVulkanWindow);
2762 if (d->m_clipCorrect.isIdentity()) {
2763 // NB the ctor takes row-major
2764 d->m_clipCorrect = QMatrix4x4(1.0f, 0.0f, 0.0f, 0.0f,
2765 0.0f, -1.0f, 0.0f, 0.0f,
2766 0.0f, 0.0f, 0.5f, 0.5f,
2767 0.0f, 0.0f, 0.0f, 1.0f);
2768 }
2769 return d->m_clipCorrect;
2770}
2771
2773
2774#include "moc_qvulkanwindow.cpp"
\inmodule QtCore
\inmodule QtCore
Definition qbytearray.h:57
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:474
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:122
QList< QByteArray > split(char sep) const
Splits the byte array into subarrays wherever sep occurs, and returns the list of those arrays.
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:106
static QCoreApplication * instance() noexcept
Returns a pointer to the application's QCoreApplication (or QGuiApplication/QApplication) instance.
\inmodule QtCore
Definition qcoreevent.h:45
@ UpdateRequest
Definition qcoreevent.h:113
@ PlatformSurface
Definition qcoreevent.h:278
The QExposeEvent class contains event parameters for expose events. \inmodule QtGui.
Definition qevent.h:514
\inmodule QtGui
Definition qimage.h:37
@ Format_RGBA8888
Definition qimage.h:59
Definition qlist.h:74
qsizetype size() const noexcept
Definition qlist.h:386
const_pointer constData() const noexcept
Definition qlist.h:416
void reserve(qsizetype size)
Definition qlist.h:746
void append(parameter_type t)
Definition qlist.h:441
The QMatrix4x4 class represents a 4x4 transformation matrix in 3D space.
Definition qmatrix4x4.h:25
The QPlatformSurfaceEvent class is used to notify about native platform surface events....
Definition qevent.h:530
The QResizeEvent class contains event parameters for resize events.
Definition qevent.h:547
\inmodule QtCore
Definition qsize.h:25
const QChar * constData() const
Returns a pointer to the data stored in the QString.
Definition qstring.h:1101
@ VulkanSurface
Definition qsurface.h:35
static QThread * currentThread()
Definition qthread.cpp:966
bool singleShot
whether the timer is a single-shot timer
Definition qtimer.h:22
\inmodule QtGui
The QVulkanFunctions class provides cross-platform access to the instance level core Vulkan 1....
\inmodule QtGui
The QVulkanInstance class represents a native Vulkan instance, enabling Vulkan rendering onto a QSurf...
\inmodule QtGui
\inmodule QtGui
\inmodule QtGui
Definition qwindow.h:63
virtual bool event(QEvent *) override
Override this to handle any event (ev) sent to the window.
Definition qwindow.cpp:2433
double e
EGLint EGLint * formats
Combined button and popup list for selecting options.
Definition image.cpp:4
#define QByteArrayLiteral(str)
Definition qbytearray.h:52
Flags
#define qDebug
[1]
Definition qlogging.h:160
#define qWarning
Definition qlogging.h:162
#define qCDebug(category,...)
#define Q_DECLARE_LOGGING_CATEGORY(name)
constexpr const T & qBound(const T &min, const T &val, const T &max)
Definition qminmax.h:44
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
static bool contains(const QJsonArray &haystack, unsigned needle)
Definition qopengl.cpp:116
GLsizei const GLfloat * v
[13]
GLint GLenum GLsizei GLsizei GLsizei depth
GLboolean r
[2]
GLenum GLenum GLsizei count
GLfloat GLfloat f
GLbitfield flags
GLint GLsizei GLsizei GLenum format
GLint y
GLboolean reset
GLint void * img
Definition qopenglext.h:233
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
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 Q_ASSERT_X(cond, x, msg)
Definition qrandom.cpp:48
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
static QT_BEGIN_NAMESPACE void init(QTextBoundaryFinder::BoundaryType type, QStringView str, QCharAttributes *attributes)
#define emit
unsigned char uchar
Definition qtypes.h:27
VkSampleCountFlagBits mask
static struct @383 q_vk_sampleCounts[]
int count
static VkDeviceSize aligned(VkDeviceSize v, VkDeviceSize byteAlign)
QList< QImage > images
[6]
QVBoxLayout * layout
QFrame frame
[0]
QSvgRenderer * renderer
[0]
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent