5#include "qvulkanfunctions.h"
6#include <QLoggingCategory>
9#include <QCoreApplication>
224QVulkanWindow::~QVulkanWindow()
228QVulkanWindowPrivate::~QVulkanWindowPrivate()
256 if (
d->status != QVulkanWindowPrivate::StatusUninitialized) {
257 qWarning(
"QVulkanWindow: Attempted to set flags when already initialized");
266QVulkanWindow::Flags QVulkanWindow::flags()
const
280 if (!
d->physDevs.isEmpty() && !
d->physDevProps.isEmpty())
281 return d->physDevProps;
285 qWarning(
"QVulkanWindow: Attempted to call availablePhysicalDevices() without a QVulkanInstance");
286 return d->physDevProps;
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;
299 return d->physDevProps;
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;
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));
318 return d->physDevProps;
331void QVulkanWindow::setPhysicalDeviceIndex(
int idx)
334 if (
d->status != QVulkanWindowPrivate::StatusUninitialized) {
335 qWarning(
"QVulkanWindow: Attempted to set physical device when already initialized");
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);
343 d->physDevIndex = idx;
356 availablePhysicalDevices();
358 if (
d->physDevs.isEmpty()) {
359 qWarning(
"QVulkanWindow: No physical devices found");
363 VkPhysicalDevice physDev =
d->physDevs.at(
d->physDevIndex);
364 if (
d->supportedDevExtensions.contains(physDev))
365 return d->supportedDevExtensions.value(physDev);
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;
381 d->supportedDevExtensions.insert(physDev, exts);
382 qDebug(lcGuiVk) <<
"Supported device extensions:" << exts;
387 qWarning(
"QVulkanWindow: Failed to query device extension count: %d", err);
403void QVulkanWindow::setDeviceExtensions(
const QByteArrayList &extensions)
406 if (
d->status != QVulkanWindowPrivate::StatusUninitialized) {
407 qWarning(
"QVulkanWindow: Attempted to set device extensions when already initialized");
410 d->requestedDevExtensions = extensions;
441 if (
d->status != QVulkanWindowPrivate::StatusUninitialized) {
442 qWarning(
"QVulkanWindow: Attempted to set preferred color format when already initialized");
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 }
474QList<int> QVulkanWindow::supportedSampleCounts()
479 availablePhysicalDevices();
481 if (
d->physDevs.isEmpty()) {
482 qWarning(
"QVulkanWindow: No physical devices found");
486 const VkPhysicalDeviceLimits *limits = &
d->physDevProps[
d->physDevIndex].limits;
487 VkSampleCountFlags
color = limits->framebufferColorSampleCounts;
488 VkSampleCountFlags
depth = limits->framebufferDepthSampleCounts;
489 VkSampleCountFlags
stencil = limits->framebufferStencilSampleCounts;
492 if ((
color & qvk_sampleCount.mask)
493 && (
depth & qvk_sampleCount.mask)
494 && (
stencil & qvk_sampleCount.mask))
496 result.append(qvk_sampleCount.count);
524void QVulkanWindow::setSampleCount(
int sampleCount)
527 if (
d->status != QVulkanWindowPrivate::StatusUninitialized) {
528 qWarning(
"QVulkanWindow: Attempted to set sample count when already initialized");
533 sampleCount =
qBound(1, sampleCount, 64);
535 if (!supportedSampleCounts().
contains(sampleCount)) {
536 qWarning(
"QVulkanWindow: Attempted to set unsupported sample count %d", sampleCount);
541 if (qvk_sampleCount.count == sampleCount) {
542 d->sampleCount = qvk_sampleCount.mask;
550void QVulkanWindowPrivate::init()
553 Q_ASSERT(status == StatusUninitialized);
555 qCDebug(lcGuiVk,
"QVulkanWindow init");
557 inst =
q->vulkanInstance();
559 qWarning(
"QVulkanWindow: Attempted to initialize without a QVulkanInstance");
562 status = StatusFailRetry;
569 surface = QVulkanInstance::surfaceForWindow(
q);
570 if (surface == VK_NULL_HANDLE) {
571 qWarning(
"QVulkanWindow: Failed to retrieve Vulkan surface for window");
572 status = StatusFailRetry;
576 q->availablePhysicalDevices();
578 if (physDevs.isEmpty()) {
579 qWarning(
"QVulkanWindow: No physical devices found");
584 if (physDevIndex < 0 || physDevIndex >= physDevs.size()) {
585 qWarning(
"QVulkanWindow: Invalid physical device index; defaulting to 0");
588 qCDebug(lcGuiVk,
"Using physical device [%d]", physDevIndex);
594 VkPhysicalDevice physDev = physDevs.at(physDevIndex);
597 uint32_t queueCount = 0;
598 f->vkGetPhysicalDeviceQueueFamilyProperties(physDev, &queueCount,
nullptr);
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)
610 gfxQueueFamilyIdx =
i;
612 if (gfxQueueFamilyIdx != uint32_t(-1)) {
613 presQueueFamilyIdx = gfxQueueFamilyIdx;
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;
623 if (gfxQueueFamilyIdx == uint32_t(-1)) {
624 qWarning(
"QVulkanWindow: No graphics queue family found");
628 if (presQueueFamilyIdx == uint32_t(-1)) {
629 qWarning(
"QVulkanWindow: No present queue family found");
638 qCDebug(lcGuiVk,
"Using queue families: graphics = %u present = %u", gfxQueueFamilyIdx, presQueueFamilyIdx);
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);
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;
664 if (!foundGfxQueue) {
665 qWarning(
"QVulkanWindow: Graphics queue missing after call to queueCreateInfoModifier");
669 if (!foundPresQueue) {
670 qWarning(
"QVulkanWindow: Present queue missing after call to queueCreateInfoModifier");
681 reqExts.append(
"VK_KHR_swapchain");
686 for (
auto ext : reqExts)
687 envExtList.removeAll(
ext);
688 reqExts.append(envExtList);
692 if (supportedExtensions.contains(
ext))
695 qCDebug(lcGuiVk) <<
"Enabling device extensions:" << devExts;
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();
705 VkPhysicalDeviceFeatures features;
706 memset(&features, 0,
sizeof(features));
707 if (enabledFeaturesModifier) {
708 enabledFeaturesModifier(features);
712 f->vkGetPhysicalDeviceFeatures(physDev, &features);
713 features.robustBufferAccess = VK_FALSE;
715 devInfo.pEnabledFeatures = &features;
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)
728 const char *stdValNamePtr = stdValName.
constData();
729 if (inst->layers().contains(stdValName)) {
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;
748 VkResult err =
f->vkCreateDevice(physDev, &devInfo,
nullptr, &dev);
749 if (err == VK_ERROR_DEVICE_LOST) {
750 qWarning(
"QVulkanWindow: Physical device lost");
755 physDevProps.clear();
756 status = StatusUninitialized;
757 qCDebug(lcGuiVk,
"Attempting to restart in 2 seconds");
761 if (err != VK_SUCCESS) {
762 qWarning(
"QVulkanWindow: Failed to create device: %d", err);
767 devFuncs = inst->deviceFunctions(dev);
770 devFuncs->vkGetDeviceQueue(dev, gfxQueueFamilyIdx, 0, &gfxQueue);
771 if (gfxQueueFamilyIdx == presQueueFamilyIdx)
772 presQueue = gfxQueue;
774 devFuncs->vkGetDeviceQueue(dev, presQueueFamilyIdx, 0, &presQueue);
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);
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);
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);
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;
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;
819 if (memType[
i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) {
820 deviceLocalMemIndex =
i;
824 qCDebug(lcGuiVk,
"Picked memtype %d for device local memory", deviceLocalMemIndex);
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");
843 uint32_t formatCount = 0;
844 vkGetPhysicalDeviceSurfaceFormatsKHR(physDev, surface, &formatCount,
nullptr);
847 vkGetPhysicalDeviceSurfaceFormatsKHR(physDev, surface, &formatCount,
formats.data());
850 colorSpace = VkColorSpaceKHR(0);
853 if (!
formats.isEmpty() &&
formats[0].format != VK_FORMAT_UNDEFINED) {
855 colorSpace =
formats[0].colorSpace;
859 if (!
formats.isEmpty() && !requestedColorFormats.isEmpty()) {
860 for (VkFormat reqFmt :
std::as_const(requestedColorFormats)) {
862 [reqFmt](
const VkSurfaceFormatKHR &sfmt) { return sfmt.format == reqFmt; });
865 colorSpace =
r->colorSpace;
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
876 const int dsFormatCandidateCount =
sizeof(dsFormatCandidates) /
sizeof(VkFormat);
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)
886 if (dsFormatIdx == dsFormatCandidateCount)
887 qWarning(
"QVulkanWindow: Failed to find an optimal depth-stencil format");
891 if (!createDefaultRenderPass())
897 status = StatusDeviceReady;
900void QVulkanWindowPrivate::reset()
905 qCDebug(lcGuiVk,
"QVulkanWindow reset");
907 devFuncs->vkDeviceWaitIdle(dev);
911 devFuncs->vkDeviceWaitIdle(dev);
914 if (defaultRenderPass) {
915 devFuncs->vkDestroyRenderPass(dev, defaultRenderPass,
nullptr);
916 defaultRenderPass = VK_NULL_HANDLE;
920 devFuncs->vkDestroyCommandPool(dev, cmdPool,
nullptr);
921 cmdPool = VK_NULL_HANDLE;
925 devFuncs->vkDestroyCommandPool(dev, presCmdPool,
nullptr);
926 presCmdPool = VK_NULL_HANDLE;
929 if (frameGrabImage) {
930 devFuncs->vkDestroyImage(dev, frameGrabImage,
nullptr);
931 frameGrabImage = VK_NULL_HANDLE;
934 if (frameGrabImageMem) {
935 devFuncs->vkFreeMemory(dev, frameGrabImageMem,
nullptr);
936 frameGrabImageMem = VK_NULL_HANDLE;
940 devFuncs->vkDestroyDevice(dev,
nullptr);
941 inst->resetDeviceFunctions(dev);
942 dev = VK_NULL_HANDLE;
943 vkCreateSwapchainKHR =
nullptr;
946 surface = VK_NULL_HANDLE;
948 status = StatusUninitialized;
951bool QVulkanWindowPrivate::createDefaultRenderPass()
953 VkAttachmentDescription attDesc[3];
954 memset(attDesc, 0,
sizeof(attDesc));
956 const bool msaa = sampleCount > VK_SAMPLE_COUNT_1_BIT;
960 attDesc[0].samples = VK_SAMPLE_COUNT_1_BIT;
961 attDesc[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
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;
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;
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;
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 };
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;
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;
1009 colorRef.attachment = 2;
1010 subPassDesc.pResolveAttachments = &resolveRef;
1011 rpInfo.attachmentCount = 3;
1014 VkResult err = devFuncs->vkCreateRenderPass(dev, &rpInfo,
nullptr, &defaultRenderPass);
1015 if (err != VK_SUCCESS) {
1016 qWarning(
"QVulkanWindow: Failed to create renderpass: %d", err);
1023void QVulkanWindowPrivate::recreateSwapChain()
1026 Q_ASSERT(status >= StatusDeviceReady);
1028 swapChainImageSize =
q->size() *
q->devicePixelRatio();
1030 if (swapChainImageSize.isEmpty())
1035 devFuncs->vkDeviceWaitIdle(dev);
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"));
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);
1052 reqBufferCount =
qMax(qMin<uint32_t>(surfaceCaps.maxImageCount, 3), surfaceCaps.minImageCount);
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();
1060 swapChainImageSize =
QSize(bufferSize.width, bufferSize.height);
1063 VkSurfaceTransformFlagBitsKHR preTransform =
1064 (surfaceCaps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)
1065 ? VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR
1066 : surfaceCaps.currentTransform;
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;
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;
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;
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;
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;
1103 qCDebug(lcGuiVk,
"Creating new swap chain of %d buffers, size %dx%d", reqBufferCount, bufferSize.width, bufferSize.height);
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);
1115 swapChain = newSwapChain;
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);
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);
1130 swapChainBufferCount = actualSwapChainBufferCount;
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);
1139 if (!createTransientImage(dsFormat,
1140 VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
1141 VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT,
1150 const bool msaa = sampleCount > VK_SAMPLE_COUNT_1_BIT;
1151 VkImage msaaImages[MAX_SWAPCHAIN_BUFFER_COUNT];
1152 VkImageView msaaViews[MAX_SWAPCHAIN_BUFFER_COUNT];
1156 VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
1157 VK_IMAGE_ASPECT_COLOR_BIT,
1161 swapChainBufferCount))
1167 VkFenceCreateInfo fenceInfo = { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
nullptr, VK_FENCE_CREATE_SIGNALED_BIT };
1169 for (
int i = 0;
i < swapChainBufferCount; ++
i) {
1170 ImageResources &
image(imageRes[
i]);
1171 image.image = swapChainImages[
i];
1174 image.msaaImage = msaaImages[
i];
1175 image.msaaImageView = msaaViews[
i];
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;
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);
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);
1201 image.cmdFenceWaitable =
true;
1203 VkImageView views[3] = {
image.imageView,
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();
1215 VkResult err = devFuncs->vkCreateFramebuffer(dev, &fbInfo,
nullptr, &
image.fb);
1216 if (err != VK_SUCCESS) {
1217 qWarning(
"QVulkanWindow: Failed to create framebuffer: %d", err);
1221 if (gfxQueueFamilyIdx != presQueueFamilyIdx) {
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);
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);
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,
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);
1263 VkSemaphoreCreateInfo semInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
nullptr, 0 };
1264 for (
int i = 0;
i < frameLag; ++
i) {
1265 FrameResources &
frame(frameRes[
i]);
1267 frame.imageAcquired =
false;
1268 frame.imageSemWaitable =
false;
1270 devFuncs->vkCreateFence(dev, &fenceInfo,
nullptr, &
frame.fence);
1271 frame.fenceWaitable =
true;
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);
1282 renderer->initSwapChainResources();
1284 status = StatusReady;
1287uint32_t QVulkanWindowPrivate::chooseTransientImageMemType(VkImage
img, uint32_t startIndex)
1289 VkPhysicalDeviceMemoryProperties physDevMemProps;
1290 inst->functions()->vkGetPhysicalDeviceMemoryProperties(physDevs[physDevIndex], &physDevMemProps);
1292 VkMemoryRequirements memReq;
1293 devFuncs->vkGetImageMemoryRequirements(dev,
img, &memReq);
1294 uint32_t memTypeIndex = uint32_t(-1);
1296 if (memReq.memoryTypeBits) {
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;
1307 if (memType[
i].propertyFlags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) {
1316 return memTypeIndex;
1319static inline VkDeviceSize
aligned(VkDeviceSize
v, VkDeviceSize byteAlign)
1321 return (
v + byteAlign - 1) & ~(byteAlign - 1);
1324bool QVulkanWindowPrivate::createTransientImage(VkFormat
format,
1325 VkImageUsageFlags
usage,
1326 VkImageAspectFlags aspectMask,
1328 VkDeviceMemory *mem,
1332 VkMemoryRequirements memReq;
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;
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;
1350 err = devFuncs->vkCreateImage(dev, &imgInfo,
nullptr,
images +
i);
1351 if (err != VK_SUCCESS) {
1352 qWarning(
"QVulkanWindow: Failed to create image: %d", err);
1359 devFuncs->vkGetImageMemoryRequirements(dev,
images[
i], &memReq);
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;
1367 uint32_t startIndex = 0;
1369 memInfo.memoryTypeIndex = chooseTransientImageMemType(
images[0], startIndex);
1370 if (memInfo.memoryTypeIndex == uint32_t(-1)) {
1371 qWarning(
"QVulkanWindow: No suitable memory type found");
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);
1382 }
while (err != VK_SUCCESS);
1384 VkDeviceSize ofs = 0;
1386 err = devFuncs->vkBindImageMemory(dev,
images[
i], *mem, ofs);
1387 if (err != VK_SUCCESS) {
1388 qWarning(
"QVulkanWindow: Failed to bind image memory: %d", err);
1391 ofs +=
aligned(memReq.size, memReq.alignment);
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;
1406 err = devFuncs->vkCreateImageView(dev, &imgViewInfo,
nullptr, views +
i);
1407 if (err != VK_SUCCESS) {
1408 qWarning(
"QVulkanWindow: Failed to create image view: %d", err);
1416void QVulkanWindowPrivate::releaseSwapChain()
1418 if (!dev || !swapChain)
1421 qCDebug(lcGuiVk,
"Releasing swapchain");
1423 devFuncs->vkDeviceWaitIdle(dev);
1426 renderer->releaseSwapChainResources();
1427 devFuncs->vkDeviceWaitIdle(dev);
1430 for (
int i = 0;
i < frameLag; ++
i) {
1431 FrameResources &
frame(frameRes[
i]);
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;
1439 if (
frame.imageSem) {
1440 devFuncs->vkDestroySemaphore(dev,
frame.imageSem,
nullptr);
1441 frame.imageSem = VK_NULL_HANDLE;
1443 if (
frame.drawSem) {
1444 devFuncs->vkDestroySemaphore(dev,
frame.drawSem,
nullptr);
1445 frame.drawSem = VK_NULL_HANDLE;
1447 if (
frame.presTransSem) {
1448 devFuncs->vkDestroySemaphore(dev,
frame.presTransSem,
nullptr);
1449 frame.presTransSem = VK_NULL_HANDLE;
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;
1463 devFuncs->vkDestroyFramebuffer(dev,
image.fb,
nullptr);
1464 image.fb = VK_NULL_HANDLE;
1466 if (
image.imageView) {
1467 devFuncs->vkDestroyImageView(dev,
image.imageView,
nullptr);
1468 image.imageView = VK_NULL_HANDLE;
1471 devFuncs->vkFreeCommandBuffers(dev, cmdPool, 1, &
image.cmdBuf);
1472 image.cmdBuf = VK_NULL_HANDLE;
1474 if (
image.presTransCmdBuf) {
1475 devFuncs->vkFreeCommandBuffers(dev, presCmdPool, 1, &
image.presTransCmdBuf);
1476 image.presTransCmdBuf = VK_NULL_HANDLE;
1478 if (
image.msaaImageView) {
1479 devFuncs->vkDestroyImageView(dev,
image.msaaImageView,
nullptr);
1480 image.msaaImageView = VK_NULL_HANDLE;
1482 if (
image.msaaImage) {
1483 devFuncs->vkDestroyImage(dev,
image.msaaImage,
nullptr);
1484 image.msaaImage = VK_NULL_HANDLE;
1489 devFuncs->vkFreeMemory(dev, msaaImageMem,
nullptr);
1490 msaaImageMem = VK_NULL_HANDLE;
1494 devFuncs->vkDestroyImageView(dev, dsView,
nullptr);
1495 dsView = VK_NULL_HANDLE;
1498 devFuncs->vkDestroyImage(dev, dsImage,
nullptr);
1499 dsImage = VK_NULL_HANDLE;
1502 devFuncs->vkFreeMemory(dev, dsMem,
nullptr);
1503 dsMem = VK_NULL_HANDLE;
1507 vkDestroySwapchainKHR(dev, swapChain,
nullptr);
1508 swapChain = VK_NULL_HANDLE;
1511 if (status == StatusReady)
1512 status = StatusDeviceReady;
1525 if (!
d->flags.testFlag(PersistentResources)) {
1526 d->releaseSwapChain();
1532void QVulkanWindowPrivate::ensureStarted()
1535 if (status == QVulkanWindowPrivate::StatusFailRetry)
1536 status = QVulkanWindowPrivate::StatusUninitialized;
1537 if (status == QVulkanWindowPrivate::StatusUninitialized) {
1539 if (status == QVulkanWindowPrivate::StatusDeviceReady)
1540 recreateSwapChain();
1542 if (status == QVulkanWindowPrivate::StatusReady)
1557bool QVulkanWindow::event(
QEvent *
e)
1561 switch (
e->type()) {
1572 d->releaseSwapChain();
1610void QVulkanWindow::setQueueCreateInfoModifier(
const QueueCreateInfoModifier &modifier)
1613 d->queueCreateInfoModifier = modifier;
1649void QVulkanWindow::setEnabledFeaturesModifier(
const EnabledFeaturesModifier &modifier)
1652 d->enabledFeaturesModifier = modifier;
1662bool QVulkanWindow::isValid()
const
1665 return d->status == QVulkanWindowPrivate::StatusReady;
1687QVulkanWindowRenderer::~QVulkanWindowRenderer()
1707void QVulkanWindowRenderer::preInitResources()
1726void QVulkanWindowRenderer::initResources()
1748void QVulkanWindowRenderer::initSwapChainResources()
1771void QVulkanWindowRenderer::releaseSwapChainResources()
1787void QVulkanWindowRenderer::releaseResources()
1832void QVulkanWindowRenderer::physicalDeviceLost()
1851void QVulkanWindowRenderer::logicalDeviceLost()
1855void QVulkanWindowPrivate::beginFrame()
1857 if (!swapChain || framePending)
1861 if (
q->size() *
q->devicePixelRatio() != swapChainImageSize) {
1862 recreateSwapChain();
1867 FrameResources &
frame(frameRes[currentFrame]);
1869 if (!
frame.imageAcquired) {
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;
1879 VkResult err = vkAcquireNextImageKHR(dev, swapChain, UINT64_MAX,
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();
1890 if (!checkDeviceLost(err))
1891 qWarning(
"QVulkanWindow: Failed to acquire next swapchain image: %d", err);
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;
1907 devFuncs->vkFreeCommandBuffers(dev, cmdPool, 1, &
image.cmdBuf);
1908 image.cmdBuf =
nullptr;
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);
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);
1933 framePending =
true;
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;
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);
1960void QVulkanWindowPrivate::endFrame()
1964 FrameResources &
frame(frameRes[currentFrame]);
1965 ImageResources &
image(imageRes[currentImage]);
1967 if (gfxQueueFamilyIdx != presQueueFamilyIdx && !frameGrabbing) {
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,
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);
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;
2008 if (!frameGrabbing) {
2009 submitInfo.signalSemaphoreCount = 1;
2010 submitInfo.pSignalSemaphores = &
frame.drawSem;
2012 VkPipelineStageFlags psf = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
2013 submitInfo.pWaitDstStageMask = &psf;
2017 err = devFuncs->vkQueueSubmit(gfxQueue, 1, &submitInfo,
image.cmdFence);
2018 if (err == VK_SUCCESS) {
2019 frame.imageSemWaitable =
false;
2020 image.cmdFenceWaitable =
true;
2022 if (!checkDeviceLost(err))
2023 qWarning(
"QVulkanWindow: Failed to submit to graphics queue: %d", err);
2028 if (frameGrabbing) {
2029 finishBlockingReadback();
2030 frameGrabbing =
false;
2033 emit q->frameGrabbed(frameGrabTargetImage);
2037 if (gfxQueueFamilyIdx != presQueueFamilyIdx) {
2039 submitInfo.pWaitSemaphores = &
frame.drawSem;
2040 submitInfo.pSignalSemaphores = &
frame.presTransSem;
2041 submitInfo.pCommandBuffers = &
image.presTransCmdBuf;
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);
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 = ¤tImage;
2057 presInfo.waitSemaphoreCount = 1;
2058 presInfo.pWaitSemaphores = gfxQueueFamilyIdx == presQueueFamilyIdx ? &
frame.drawSem : &
frame.presTransSem;
2062 inst->presentAboutToBeQueued(
q);
2064 err = vkQueuePresentKHR(presQueue, &presInfo);
2065 if (err != VK_SUCCESS) {
2066 if (err == VK_ERROR_OUT_OF_DATE_KHR) {
2067 recreateSwapChain();
2070 }
else if (err != VK_SUBOPTIMAL_KHR) {
2071 if (!checkDeviceLost(err))
2072 qWarning(
"QVulkanWindow: Failed to present: %d", err);
2077 frame.imageAcquired =
false;
2079 inst->presentQueued(
q);
2081 currentFrame = (currentFrame + 1) % frameLag;
2097void QVulkanWindow::frameReady()
2100 "QVulkanWindow",
"frameReady() can only be called from the GUI (main) thread");
2104 if (!
d->framePending) {
2105 qWarning(
"QVulkanWindow: frameReady() called without a corresponding startNextFrame()");
2109 d->framePending =
false;
2114bool QVulkanWindowPrivate::checkDeviceLost(VkResult err)
2116 if (err == VK_ERROR_DEVICE_LOST) {
2117 qWarning(
"QVulkanWindow: Device lost");
2120 qCDebug(lcGuiVk,
"Releasing all resources due to device lost");
2123 qCDebug(lcGuiVk,
"Restarting");
2130void QVulkanWindowPrivate::addReadback()
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;
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);
2153 VkMemoryRequirements memReq;
2154 devFuncs->vkGetImageMemoryRequirements(dev, frameGrabImage, &memReq);
2156 VkMemoryAllocateInfo allocInfo = {
2157 VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
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);
2169 err = devFuncs->vkBindImageMemory(dev, frameGrabImage, frameGrabImageMem, 0);
2170 if (err != VK_SUCCESS) {
2171 qWarning(
"QVulkanWindow: Failed to bind readback image memory: %d", err);
2175 ImageResources &
image(imageRes[currentImage]);
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;
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;
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,
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;
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,
2207 VkImageCopy copyInfo;
2208 memset(©Info, 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;
2215 devFuncs->vkCmdCopyImage(
image.cmdBuf,
image.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
2216 frameGrabImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©Info);
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;
2224 devFuncs->vkCmdPipelineBarrier(
image.cmdBuf,
2225 VK_PIPELINE_STAGE_TRANSFER_BIT,
2226 VK_PIPELINE_STAGE_HOST_BIT,
2227 0, 0,
nullptr, 0,
nullptr,
2231void QVulkanWindowPrivate::finishBlockingReadback()
2233 ImageResources &
image(imageRes[currentImage]);
2237 devFuncs->vkWaitForFences(dev, 1, &
image.cmdFence, VK_TRUE, UINT64_MAX);
2238 devFuncs->vkResetFences(dev, 1, &
image.cmdFence);
2240 image.cmdFenceWaitable =
false;
2242 VkImageSubresource subres = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0 };
2243 VkSubresourceLayout
layout;
2244 devFuncs->vkGetImageSubresourceLayout(dev, frameGrabImage, &subres, &
layout);
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);
2253 for (
int y = 0;
y < frameGrabTargetImage.height(); ++
y) {
2254 memcpy(frameGrabTargetImage.scanLine(
y),
p, frameGrabTargetImage.width() * 4);
2258 devFuncs->vkUnmapMemory(dev, frameGrabImageMem);
2260 devFuncs->vkDestroyImage(dev, frameGrabImage,
nullptr);
2261 frameGrabImage = VK_NULL_HANDLE;
2262 devFuncs->vkFreeMemory(dev, frameGrabImageMem,
nullptr);
2263 frameGrabImageMem = VK_NULL_HANDLE;
2273VkPhysicalDevice QVulkanWindow::physicalDevice()
const
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;
2289const VkPhysicalDeviceProperties *QVulkanWindow::physicalDeviceProperties()
const
2292 if (
d->physDevIndex <
d->physDevProps.size())
2293 return &
d->physDevProps[
d->physDevIndex];
2294 qWarning(
"QVulkanWindow: Physical device properties not available");
2305VkDevice QVulkanWindow::device()
const
2318VkQueue QVulkanWindow::graphicsQueue()
const
2335uint32_t QVulkanWindow::graphicsQueueFamilyIndex()
const
2338 return d->gfxQueueFamilyIdx;
2348VkCommandPool QVulkanWindow::graphicsCommandPool()
const
2364uint32_t QVulkanWindow::hostVisibleMemoryIndex()
const
2367 return d->hostVisibleMemIndex;
2382uint32_t QVulkanWindow::deviceLocalMemoryIndex()
const
2385 return d->deviceLocalMemIndex;
2406VkRenderPass QVulkanWindow::defaultRenderPass()
const
2409 return d->defaultRenderPass;
2421VkFormat QVulkanWindow::colorFormat()
const
2424 return d->colorFormat;
2434VkFormat QVulkanWindow::depthStencilFormat()
const
2450QSize QVulkanWindow::swapChainImageSize()
const
2453 return d->swapChainImageSize;
2464VkCommandBuffer QVulkanWindow::currentCommandBuffer()
const
2467 if (!
d->framePending) {
2468 qWarning(
"QVulkanWindow: Attempted to call currentCommandBuffer() without an active frame");
2469 return VK_NULL_HANDLE;
2471 return d->imageRes[
d->currentImage].cmdBuf;
2493VkFramebuffer QVulkanWindow::currentFramebuffer()
const
2496 if (!
d->framePending) {
2497 qWarning(
"QVulkanWindow: Attempted to call currentFramebuffer() without an active frame");
2498 return VK_NULL_HANDLE;
2500 return d->imageRes[
d->currentImage].fb;
2524int QVulkanWindow::currentFrame()
const
2527 if (!
d->framePending)
2528 qWarning(
"QVulkanWindow: Attempted to call currentFrame() without an active frame");
2529 return d->currentFrame;
2548int QVulkanWindow::concurrentFrameCount()
const
2565int QVulkanWindow::swapChainImageCount()
const
2568 return d->swapChainBufferCount;
2577int QVulkanWindow::currentSwapChainImageIndex()
const
2580 if (!
d->framePending)
2581 qWarning(
"QVulkanWindow: Attempted to call currentSwapChainImageIndex() without an active frame");
2582 return d->currentImage;
2594VkImage QVulkanWindow::swapChainImage(
int idx)
const
2597 return idx >= 0 && idx <
d->swapChainBufferCount ?
d->imageRes[idx].image : VK_NULL_HANDLE;
2609VkImageView QVulkanWindow::swapChainImageView(
int idx)
const
2612 return idx >= 0 && idx <
d->swapChainBufferCount ?
d->imageRes[idx].imageView : VK_NULL_HANDLE;
2622VkImage QVulkanWindow::depthStencilImage()
const
2635VkImageView QVulkanWindow::depthStencilImageView()
const
2649VkSampleCountFlagBits QVulkanWindow::sampleCountFlagBits()
const
2652 return d->sampleCount;
2665VkImage QVulkanWindow::msaaColorImage(
int idx)
const
2668 return idx >= 0 && idx <
d->swapChainBufferCount ?
d->imageRes[idx].msaaImage : VK_NULL_HANDLE;
2681VkImageView QVulkanWindow::msaaColorImageView(
int idx)
const
2684 return idx >= 0 && idx <
d->swapChainBufferCount ?
d->imageRes[idx].msaaImageView : VK_NULL_HANDLE;
2695bool QVulkanWindow::supportsGrab()
const
2698 return d->swapChainSupportsReadBack;
2726QImage QVulkanWindow::grab()
2729 if (!
d->swapChain) {
2730 qWarning(
"QVulkanWindow: Attempted to call grab() without a swapchain");
2733 if (
d->framePending) {
2734 qWarning(
"QVulkanWindow: Attempted to call grab() while a frame is still pending");
2737 if (!
d->swapChainSupportsReadBack) {
2738 qWarning(
"QVulkanWindow: Attempted to call grab() with a swapchain that does not support usage as transfer source");
2742 d->frameGrabbing =
true;
2745 return d->frameGrabTargetImage;
2759QMatrix4x4 QVulkanWindow::clipCorrectionMatrix()
2762 if (
d->m_clipCorrect.isIdentity()) {
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);
2769 return d->m_clipCorrect;
2774#include "moc_qvulkanwindow.cpp"
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
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.
static QCoreApplication * instance() noexcept
Returns a pointer to the application's QCoreApplication (or QGuiApplication/QApplication) instance.
The QExposeEvent class contains event parameters for expose events. \inmodule QtGui.
qsizetype size() const noexcept
const_pointer constData() const noexcept
void reserve(qsizetype size)
void append(parameter_type t)
The QMatrix4x4 class represents a 4x4 transformation matrix in 3D space.
The QResizeEvent class contains event parameters for resize events.
const QChar * constData() const
Returns a pointer to the data stored in the QString.
static QThread * currentThread()
bool singleShot
whether the timer is a single-shot timer
The QVulkanFunctions class provides cross-platform access to the instance level core Vulkan 1....
The QVulkanInstance class represents a native Vulkan instance, enabling Vulkan rendering onto a QSurf...
virtual bool event(QEvent *) override
Override this to handle any event (ev) sent to the window.
Combined button and popup list for selecting options.
VkCommandBufferAllocateInfo cmdBufInfo
#define QByteArrayLiteral(str)
#define qCDebug(category,...)
#define Q_DECLARE_LOGGING_CATEGORY(name)
constexpr const T & qBound(const T &min, const T &val, const T &max)
constexpr const T & qMax(const T &a, const T &b)
static bool contains(const QJsonArray &haystack, unsigned needle)
GLsizei const GLfloat * v
[13]
GLint GLenum GLsizei GLsizei GLsizei depth
GLenum GLenum GLsizei count
GLint GLsizei GLsizei GLenum format
GLdouble GLdouble GLdouble GLdouble q
GLint GLfloat GLint stencil
GLenum GLenum colorFormat
GLsizeiptr const void GLenum usage
#define Q_ASSERT_X(cond, x, msg)
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)
VkSampleCountFlagBits mask
static struct @383 q_vk_sampleCounts[]
static VkDeviceSize aligned(VkDeviceSize v, VkDeviceSize byteAlign)
QList< QImage > images
[6]
QSvgRenderer * renderer
[0]
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent