6#include "qvulkanfunctions.h"
8#include <QLoggingCategory>
11#include <QCoreApplication>
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
225
226
227
228
229QVulkanWindow::QVulkanWindow(QWindow *parent)
230 : QWindow(*(
new QVulkanWindowPrivate), parent)
232 setSurfaceType(QSurface::VulkanSurface);
236
237
238QVulkanWindow::~QVulkanWindow()
242QVulkanWindowPrivate::~QVulkanWindowPrivate()
251
252
253
254
255
256
257
258
261
262
263
264
265
266
267void QVulkanWindow::setFlags(Flags flags)
270 if (d->status != QVulkanWindowPrivate::StatusUninitialized) {
271 qWarning(
"QVulkanWindow: Attempted to set flags when already initialized");
278
279
280QVulkanWindow::Flags QVulkanWindow::flags()
const
282 Q_D(
const QVulkanWindow);
287
288
289
290
291QList<VkPhysicalDeviceProperties> QVulkanWindow::availablePhysicalDevices()
294 if (!d->physDevs.isEmpty() && !d->physDevProps.isEmpty())
295 return d->physDevProps;
297 QVulkanInstance *inst = vulkanInstance();
299 qWarning(
"QVulkanWindow: Attempted to call availablePhysicalDevices() without a QVulkanInstance");
300 return d->physDevProps;
303 QVulkanFunctions *f = inst->functions();
305 VkResult err = f->vkEnumeratePhysicalDevices(inst->vkInstance(), &count,
nullptr);
306 if (err != VK_SUCCESS) {
307 qWarning(
"QVulkanWindow: Failed to get physical device count: %d", err);
308 return d->physDevProps;
311 qCDebug(lcGuiVk,
"%d physical devices", count);
313 return d->physDevProps;
315 QList<VkPhysicalDevice> devs(count);
316 err = f->vkEnumeratePhysicalDevices(inst->vkInstance(), &count, devs.data());
317 if (err != VK_SUCCESS) {
318 qWarning(
"QVulkanWindow: Failed to enumerate physical devices: %d", err);
319 return d->physDevProps;
323 d->physDevProps.resize(count);
324 for (uint32_t i = 0; i < count; ++i) {
325 VkPhysicalDeviceProperties *p = &d->physDevProps[i];
326 f->vkGetPhysicalDeviceProperties(d->physDevs.at(i), p);
327 qCDebug(lcGuiVk,
"Physical device [%d]: name '%s' version %d.%d.%d", i, p->deviceName,
328 VK_VERSION_MAJOR(p->driverVersion), VK_VERSION_MINOR(p->driverVersion),
329 VK_VERSION_PATCH(p->driverVersion));
332 return d->physDevProps;
336
337
338
339
340
341
342
343
344
345void QVulkanWindow::setPhysicalDeviceIndex(
int idx)
348 if (d->status != QVulkanWindowPrivate::StatusUninitialized) {
349 qWarning(
"QVulkanWindow: Attempted to set physical device when already initialized");
352 const int count = availablePhysicalDevices().size();
353 if (idx < 0 || idx >= count) {
354 qWarning(
"QVulkanWindow: Invalid physical device index %d (total physical devices: %d)", idx, count);
357 d->physDevIndex = idx;
361
362
363
364
365
366QVulkanInfoVector<QVulkanExtension> QVulkanWindow::supportedDeviceExtensions()
370 availablePhysicalDevices();
372 if (d->physDevs.isEmpty()) {
373 qWarning(
"QVulkanWindow: No physical devices found");
374 return QVulkanInfoVector<QVulkanExtension>();
377 VkPhysicalDevice physDev = d->physDevs.at(d->physDevIndex);
378 if (d->supportedDevExtensions.contains(physDev))
379 return d->supportedDevExtensions.value(physDev);
381 QVulkanFunctions *f = vulkanInstance()->functions();
383 VkResult err = f->vkEnumerateDeviceExtensionProperties(physDev,
nullptr, &count,
nullptr);
384 if (err == VK_SUCCESS) {
385 QList<VkExtensionProperties> extProps(count);
386 err = f->vkEnumerateDeviceExtensionProperties(physDev,
nullptr, &count, extProps.data());
387 if (err == VK_SUCCESS) {
388 QVulkanInfoVector<QVulkanExtension> exts;
389 for (
const VkExtensionProperties &prop : extProps) {
390 QVulkanExtension ext;
391 ext.name = prop.extensionName;
392 ext.version = prop.specVersion;
395 d->supportedDevExtensions.insert(physDev, exts);
396 qCDebug(lcGuiVk) <<
"Supported device extensions:" << exts;
401 qWarning(
"QVulkanWindow: Failed to query device extension count: %d", err);
402 return QVulkanInfoVector<QVulkanExtension>();
406
407
408
409
410
411
412
413
414
415
416
417void QVulkanWindow::setDeviceExtensions(
const QByteArrayList &extensions)
420 if (d->status != QVulkanWindowPrivate::StatusUninitialized) {
421 qWarning(
"QVulkanWindow: Attempted to set device extensions when already initialized");
424 d->requestedDevExtensions = extensions;
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452void QVulkanWindow::setPreferredColorFormats(
const QList<VkFormat> &formats)
455 if (d->status != QVulkanWindowPrivate::StatusUninitialized) {
456 qWarning(
"QVulkanWindow: Attempted to set preferred color format when already initialized");
459 d->requestedColorFormats = formats;
465} q_vk_sampleCounts[] = {
467 { VK_SAMPLE_COUNT_1_BIT, 1 },
468 { VK_SAMPLE_COUNT_2_BIT, 2 },
469 { VK_SAMPLE_COUNT_4_BIT, 4 },
470 { VK_SAMPLE_COUNT_8_BIT, 8 },
471 { VK_SAMPLE_COUNT_16_BIT, 16 },
472 { VK_SAMPLE_COUNT_32_BIT, 32 },
473 { VK_SAMPLE_COUNT_64_BIT, 64 }
477
478
479
480
481
482
483
484
485
486
487
488QList<
int> QVulkanWindow::supportedSampleCounts()
490 Q_D(
const QVulkanWindow);
493 availablePhysicalDevices();
495 if (d->physDevs.isEmpty()) {
496 qWarning(
"QVulkanWindow: No physical devices found");
500 const VkPhysicalDeviceLimits *limits = &d->physDevProps[d->physDevIndex].limits;
501 VkSampleCountFlags color = limits->framebufferColorSampleCounts;
502 VkSampleCountFlags depth = limits->framebufferDepthSampleCounts;
503 VkSampleCountFlags stencil = limits->framebufferStencilSampleCounts;
505 for (
const auto &qvk_sampleCount : q_vk_sampleCounts) {
506 if ((color & qvk_sampleCount.mask)
507 && (depth & qvk_sampleCount.mask)
508 && (stencil & qvk_sampleCount.mask))
510 result.append(qvk_sampleCount.count);
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538void QVulkanWindow::setSampleCount(
int sampleCount)
541 if (d->status != QVulkanWindowPrivate::StatusUninitialized) {
542 qWarning(
"QVulkanWindow: Attempted to set sample count when already initialized");
547 sampleCount = qBound(1, sampleCount, 64);
549 if (!supportedSampleCounts().contains(sampleCount)) {
550 qWarning(
"QVulkanWindow: Attempted to set unsupported sample count %d", sampleCount);
554 for (
const auto &qvk_sampleCount : q_vk_sampleCounts) {
555 if (qvk_sampleCount.count == sampleCount) {
556 d->sampleCount = qvk_sampleCount.mask;
564void QVulkanWindowPrivate::init()
567 Q_ASSERT(status == StatusUninitialized);
569 qCDebug(lcGuiVk,
"QVulkanWindow init");
571 inst = q->vulkanInstance();
573 qWarning(
"QVulkanWindow: Attempted to initialize without a QVulkanInstance");
576 status = StatusFailRetry;
581 renderer = q->createRenderer();
583 surface = QVulkanInstance::surfaceForWindow(q);
584 if (surface == VK_NULL_HANDLE) {
585 qWarning(
"QVulkanWindow: Failed to retrieve Vulkan surface for window");
586 status = StatusFailRetry;
590 q->availablePhysicalDevices();
592 if (physDevs.isEmpty()) {
593 qWarning(
"QVulkanWindow: No physical devices found");
598 if (physDevIndex < 0 || physDevIndex >= physDevs.size()) {
599 qWarning(
"QVulkanWindow: Invalid physical device index; defaulting to 0");
602 qCDebug(lcGuiVk,
"Using physical device [%d]", physDevIndex);
606 renderer->preInitResources();
608 VkPhysicalDevice physDev = physDevs.at(physDevIndex);
609 QVulkanFunctions *f = inst->functions();
611 uint32_t queueCount = 0;
612 f->vkGetPhysicalDeviceQueueFamilyProperties(physDev, &queueCount,
nullptr);
613 QList<VkQueueFamilyProperties> queueFamilyProps(queueCount);
614 f->vkGetPhysicalDeviceQueueFamilyProperties(physDev, &queueCount, queueFamilyProps.data());
615 gfxQueueFamilyIdx = uint32_t(-1);
616 presQueueFamilyIdx = uint32_t(-1);
617 for (
int i = 0; i < queueFamilyProps.size(); ++i) {
618 const bool supportsPresent = inst->supportsPresent(physDev, i, q);
619 qCDebug(lcGuiVk,
"queue family %d: flags=0x%x count=%d supportsPresent=%d", i,
620 queueFamilyProps[i].queueFlags, queueFamilyProps[i].queueCount, supportsPresent);
621 if (gfxQueueFamilyIdx == uint32_t(-1)
622 && (queueFamilyProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
624 gfxQueueFamilyIdx = i;
626 if (gfxQueueFamilyIdx != uint32_t(-1)) {
627 presQueueFamilyIdx = gfxQueueFamilyIdx;
629 qCDebug(lcGuiVk,
"No queue with graphics+present; trying separate queues");
630 for (
int i = 0; i < queueFamilyProps.size(); ++i) {
631 if (gfxQueueFamilyIdx == uint32_t(-1) && (queueFamilyProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT))
632 gfxQueueFamilyIdx = i;
633 if (presQueueFamilyIdx == uint32_t(-1) && inst->supportsPresent(physDev, i, q))
634 presQueueFamilyIdx = i;
637 if (gfxQueueFamilyIdx == uint32_t(-1)) {
638 qWarning(
"QVulkanWindow: No graphics queue family found");
642 if (presQueueFamilyIdx == uint32_t(-1)) {
643 qWarning(
"QVulkanWindow: No present queue family found");
649 if (qEnvironmentVariableIsSet(
"QT_VK_PRESENT_QUEUE_INDEX"))
650 presQueueFamilyIdx = qEnvironmentVariableIntValue(
"QT_VK_PRESENT_QUEUE_INDEX");
652 qCDebug(lcGuiVk,
"Using queue families: graphics = %u present = %u", gfxQueueFamilyIdx, presQueueFamilyIdx);
654 QList<VkDeviceQueueCreateInfo> queueInfo;
655 queueInfo.reserve(2);
656 const float prio[] = { 0 };
657 VkDeviceQueueCreateInfo addQueueInfo;
658 memset(&addQueueInfo, 0,
sizeof(addQueueInfo));
659 addQueueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
660 addQueueInfo.queueFamilyIndex = gfxQueueFamilyIdx;
661 addQueueInfo.queueCount = 1;
662 addQueueInfo.pQueuePriorities = prio;
663 queueInfo.append(addQueueInfo);
664 if (gfxQueueFamilyIdx != presQueueFamilyIdx) {
665 addQueueInfo.queueFamilyIndex = presQueueFamilyIdx;
666 addQueueInfo.queueCount = 1;
667 addQueueInfo.pQueuePriorities = prio;
668 queueInfo.append(addQueueInfo);
670 if (queueCreateInfoModifier) {
671 queueCreateInfoModifier(queueFamilyProps.constData(), queueCount, queueInfo);
672 bool foundGfxQueue =
false;
673 bool foundPresQueue =
false;
674 for (
const VkDeviceQueueCreateInfo& createInfo : std::as_const(queueInfo)) {
675 foundGfxQueue |= createInfo.queueFamilyIndex == gfxQueueFamilyIdx;
676 foundPresQueue |= createInfo.queueFamilyIndex == presQueueFamilyIdx;
678 if (!foundGfxQueue) {
679 qWarning(
"QVulkanWindow: Graphics queue missing after call to queueCreateInfoModifier");
683 if (!foundPresQueue) {
684 qWarning(
"QVulkanWindow: Present queue missing after call to queueCreateInfoModifier");
692 QList<
const char *> devExts;
693 QVulkanInfoVector<QVulkanExtension> supportedExtensions = q->supportedDeviceExtensions();
694 QByteArrayList reqExts = requestedDevExtensions;
695 reqExts.append(
"VK_KHR_swapchain");
697 QByteArray envExts = qgetenv(
"QT_VULKAN_DEVICE_EXTENSIONS");
698 if (!envExts.isEmpty()) {
699 QByteArrayList envExtList = envExts.split(
';');
700 for (
const QByteArray &ext : std::as_const(reqExts))
701 envExtList.removeAll(ext);
702 reqExts.append(envExtList);
705 for (
const QByteArray &ext : std::as_const(reqExts)) {
706 if (supportedExtensions.contains(ext))
707 devExts.append(ext.constData());
709 qCDebug(lcGuiVk) <<
"Enabling device extensions:" << devExts;
711 VkDeviceCreateInfo devInfo;
712 memset(&devInfo, 0,
sizeof(devInfo));
713 devInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
714 devInfo.queueCreateInfoCount = queueInfo.size();
715 devInfo.pQueueCreateInfos = queueInfo.constData();
716 devInfo.enabledExtensionCount = devExts.size();
717 devInfo.ppEnabledExtensionNames = devExts.constData();
719 VkPhysicalDeviceFeatures features = {};
720 VkPhysicalDeviceFeatures2 features2 = {};
721 if (enabledFeatures2Modifier) {
722 features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
723 enabledFeatures2Modifier(features2);
724 devInfo.pNext = &features2;
725 }
else if (enabledFeaturesModifier) {
726 enabledFeaturesModifier(features);
727 devInfo.pEnabledFeatures = &features;
731 f->vkGetPhysicalDeviceFeatures(physDev, &features);
732 features.robustBufferAccess = VK_FALSE;
733 devInfo.pEnabledFeatures = &features;
736 const QByteArray stdValName = QByteArrayLiteral(
"VK_LAYER_KHRONOS_validation");
737 const char *stdValNamePtr = stdValName.constData();
743 uint32_t apiVersion = physDevProps[physDevIndex].apiVersion;
744 if (VK_VERSION_MAJOR(apiVersion) == 1
745 && VK_VERSION_MINOR(apiVersion) == 0
746 && VK_VERSION_PATCH(apiVersion) <= 13)
749 if (inst->layers().contains(stdValName)) {
751 VkResult err = f->vkEnumerateDeviceLayerProperties(physDev, &count,
nullptr);
752 if (err == VK_SUCCESS) {
753 QList<VkLayerProperties> layerProps(count);
754 err = f->vkEnumerateDeviceLayerProperties(physDev, &count, layerProps.data());
755 if (err == VK_SUCCESS) {
756 for (
const VkLayerProperties &prop : layerProps) {
757 if (!strncmp(prop.layerName, stdValNamePtr, stdValName.size())) {
758 devInfo.enabledLayerCount = 1;
759 devInfo.ppEnabledLayerNames = &stdValNamePtr;
768 VkResult err = f->vkCreateDevice(physDev, &devInfo,
nullptr, &dev);
769 if (err == VK_ERROR_DEVICE_LOST) {
770 qWarning(
"QVulkanWindow: Physical device lost");
772 renderer->physicalDeviceLost();
775 physDevProps.clear();
776 status = StatusUninitialized;
777 qCDebug(lcGuiVk,
"Attempting to restart in 2 seconds");
778 QTimer::singleShot(2000, q, [
this]() { ensureStarted(); });
781 if (err != VK_SUCCESS) {
782 qWarning(
"QVulkanWindow: Failed to create device: %d", err);
787 devFuncs = inst->deviceFunctions(dev);
790 devFuncs->vkGetDeviceQueue(dev, gfxQueueFamilyIdx, 0, &gfxQueue);
791 if (gfxQueueFamilyIdx == presQueueFamilyIdx)
792 presQueue = gfxQueue;
794 devFuncs->vkGetDeviceQueue(dev, presQueueFamilyIdx, 0, &presQueue);
796 VkCommandPoolCreateInfo poolInfo;
797 memset(&poolInfo, 0,
sizeof(poolInfo));
798 poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
799 poolInfo.queueFamilyIndex = gfxQueueFamilyIdx;
800 err = devFuncs->vkCreateCommandPool(dev, &poolInfo,
nullptr, &cmdPool);
801 if (err != VK_SUCCESS) {
802 qWarning(
"QVulkanWindow: Failed to create command pool: %d", err);
806 if (gfxQueueFamilyIdx != presQueueFamilyIdx) {
807 poolInfo.queueFamilyIndex = presQueueFamilyIdx;
808 err = devFuncs->vkCreateCommandPool(dev, &poolInfo,
nullptr, &presCmdPool);
809 if (err != VK_SUCCESS) {
810 qWarning(
"QVulkanWindow: Failed to create command pool for present queue: %d", err);
816 hostVisibleMemIndex = 0;
817 VkPhysicalDeviceMemoryProperties physDevMemProps;
818 bool hostVisibleMemIndexSet =
false;
819 f->vkGetPhysicalDeviceMemoryProperties(physDev, &physDevMemProps);
820 for (uint32_t i = 0; i < physDevMemProps.memoryTypeCount; ++i) {
821 const VkMemoryType *memType = physDevMemProps.memoryTypes;
822 qCDebug(lcGuiVk,
"memtype %d: flags=0x%x", i, memType[i].propertyFlags);
825 const int hostVisibleAndCoherent = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
826 if ((memType[i].propertyFlags & hostVisibleAndCoherent) == hostVisibleAndCoherent) {
827 if (!hostVisibleMemIndexSet
828 || (memType[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT)) {
829 hostVisibleMemIndexSet =
true;
830 hostVisibleMemIndex = i;
834 qCDebug(lcGuiVk,
"Picked memtype %d for host visible memory", hostVisibleMemIndex);
835 deviceLocalMemIndex = 0;
836 for (uint32_t i = 0; i < physDevMemProps.memoryTypeCount; ++i) {
837 const VkMemoryType *memType = physDevMemProps.memoryTypes;
839 if (memType[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) {
840 deviceLocalMemIndex = i;
844 qCDebug(lcGuiVk,
"Picked memtype %d for device local memory", deviceLocalMemIndex);
846 if (!vkGetPhysicalDeviceSurfaceCapabilitiesKHR || !vkGetPhysicalDeviceSurfaceFormatsKHR) {
847 vkGetPhysicalDeviceSurfaceCapabilitiesKHR =
reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR>(
848 inst->getInstanceProcAddr(
"vkGetPhysicalDeviceSurfaceCapabilitiesKHR"));
849 vkGetPhysicalDeviceSurfaceFormatsKHR =
reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceFormatsKHR>(
850 inst->getInstanceProcAddr(
"vkGetPhysicalDeviceSurfaceFormatsKHR"));
851 if (!vkGetPhysicalDeviceSurfaceCapabilitiesKHR || !vkGetPhysicalDeviceSurfaceFormatsKHR) {
852 qWarning(
"QVulkanWindow: Physical device surface queries not available");
863 uint32_t formatCount = 0;
864 vkGetPhysicalDeviceSurfaceFormatsKHR(physDev, surface, &formatCount,
nullptr);
865 QList<VkSurfaceFormatKHR> formats(formatCount);
867 vkGetPhysicalDeviceSurfaceFormatsKHR(physDev, surface, &formatCount, formats.data());
869 colorFormat = VK_FORMAT_B8G8R8A8_UNORM;
870 colorSpace = VkColorSpaceKHR(0);
873 if (!formats.isEmpty() && formats[0].format != VK_FORMAT_UNDEFINED) {
874 colorFormat = formats[0].format;
875 colorSpace = formats[0].colorSpace;
879 if (!formats.isEmpty() && !requestedColorFormats.isEmpty()) {
880 for (VkFormat reqFmt : std::as_const(requestedColorFormats)) {
881 auto r = std::find_if(formats.cbegin(), formats.cend(),
882 [reqFmt](
const VkSurfaceFormatKHR &sfmt) {
return sfmt.format == reqFmt; });
883 if (r != formats.cend()) {
884 colorFormat = r->format;
885 colorSpace = r->colorSpace;
891#if QT_CONFIG(wayland)
896 const bool hasPassthrough = std::any_of(formats.cbegin(), formats.cend(), [
this](
const VkSurfaceFormatKHR &format) {
897 return format.format == colorFormat && format.colorSpace == VK_COLOR_SPACE_PASS_THROUGH_EXT;
899 if (hasPassthrough) {
900 colorSpace = VK_COLOR_SPACE_PASS_THROUGH_EXT;
904 const VkFormat dsFormatCandidates[] = {
905 VK_FORMAT_D24_UNORM_S8_UINT,
906 VK_FORMAT_D32_SFLOAT_S8_UINT,
907 VK_FORMAT_D16_UNORM_S8_UINT
909 const int dsFormatCandidateCount =
sizeof(dsFormatCandidates) /
sizeof(VkFormat);
911 while (dsFormatIdx < dsFormatCandidateCount) {
912 dsFormat = dsFormatCandidates[dsFormatIdx];
913 VkFormatProperties fmtProp;
914 f->vkGetPhysicalDeviceFormatProperties(physDev, dsFormat, &fmtProp);
915 if (fmtProp.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)
919 if (dsFormatIdx == dsFormatCandidateCount)
920 qWarning(
"QVulkanWindow: Failed to find an optimal depth-stencil format");
922 qCDebug(lcGuiVk,
"Color format: %d Depth-stencil format: %d", colorFormat, dsFormat);
924 if (!createDefaultRenderPass())
928 renderer->initResources();
930 status = StatusDeviceReady;
933void QVulkanWindowPrivate::reset()
938 qCDebug(lcGuiVk,
"QVulkanWindow reset");
940 devFuncs->vkDeviceWaitIdle(dev);
943 renderer->releaseResources();
944 devFuncs->vkDeviceWaitIdle(dev);
947 if (defaultRenderPass) {
948 devFuncs->vkDestroyRenderPass(dev, defaultRenderPass,
nullptr);
949 defaultRenderPass = VK_NULL_HANDLE;
953 devFuncs->vkDestroyCommandPool(dev, cmdPool,
nullptr);
954 cmdPool = VK_NULL_HANDLE;
958 devFuncs->vkDestroyCommandPool(dev, presCmdPool,
nullptr);
959 presCmdPool = VK_NULL_HANDLE;
962 releaseReadbackResources();
965 devFuncs->vkDestroyDevice(dev,
nullptr);
966 inst->resetDeviceFunctions(dev);
967 dev = VK_NULL_HANDLE;
968 vkCreateSwapchainKHR =
nullptr;
971 surface = VK_NULL_HANDLE;
973 status = StatusUninitialized;
976bool QVulkanWindowPrivate::createDefaultRenderPass()
978 VkAttachmentDescription attDesc[3];
979 memset(attDesc, 0,
sizeof(attDesc));
981 const bool msaa = sampleCount > VK_SAMPLE_COUNT_1_BIT;
984 attDesc[0].format = colorFormat;
985 attDesc[0].samples = VK_SAMPLE_COUNT_1_BIT;
986 attDesc[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
987 attDesc[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
988 attDesc[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
989 attDesc[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
990 attDesc[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
991 attDesc[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
993 attDesc[1].format = dsFormat;
994 attDesc[1].samples = sampleCount;
995 attDesc[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
996 attDesc[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
997 attDesc[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
998 attDesc[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
999 attDesc[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1000 attDesc[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
1004 attDesc[2].format = colorFormat;
1005 attDesc[2].samples = sampleCount;
1006 attDesc[2].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1007 attDesc[2].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
1008 attDesc[2].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1009 attDesc[2].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1010 attDesc[2].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1011 attDesc[2].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
1014 VkAttachmentReference colorRef = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
1015 VkAttachmentReference resolveRef = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
1016 VkAttachmentReference dsRef = { 1, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
1018 VkSubpassDescription subPassDesc;
1019 memset(&subPassDesc, 0,
sizeof(subPassDesc));
1020 subPassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
1021 subPassDesc.colorAttachmentCount = 1;
1022 subPassDesc.pColorAttachments = &colorRef;
1023 subPassDesc.pDepthStencilAttachment = &dsRef;
1025 VkRenderPassCreateInfo rpInfo;
1026 memset(&rpInfo, 0,
sizeof(rpInfo));
1027 rpInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
1028 rpInfo.attachmentCount = 2;
1029 rpInfo.pAttachments = attDesc;
1030 rpInfo.subpassCount = 1;
1031 rpInfo.pSubpasses = &subPassDesc;
1034 colorRef.attachment = 2;
1035 subPassDesc.pResolveAttachments = &resolveRef;
1036 rpInfo.attachmentCount = 3;
1039 VkResult err = devFuncs->vkCreateRenderPass(dev, &rpInfo,
nullptr, &defaultRenderPass);
1040 if (err != VK_SUCCESS) {
1041 qWarning(
"QVulkanWindow: Failed to create renderpass: %d", err);
1048void QVulkanWindowPrivate::recreateSwapChain()
1051 Q_ASSERT(status >= StatusDeviceReady);
1053 swapChainImageSize = q->size() * q->devicePixelRatio();
1055 if (swapChainImageSize.isEmpty())
1058 QVulkanInstance *inst = q->vulkanInstance();
1059 QVulkanFunctions *f = inst->functions();
1060 devFuncs->vkDeviceWaitIdle(dev);
1062 if (!vkCreateSwapchainKHR) {
1063 vkCreateSwapchainKHR =
reinterpret_cast<PFN_vkCreateSwapchainKHR>(f->vkGetDeviceProcAddr(dev,
"vkCreateSwapchainKHR"));
1064 vkDestroySwapchainKHR =
reinterpret_cast<PFN_vkDestroySwapchainKHR>(f->vkGetDeviceProcAddr(dev,
"vkDestroySwapchainKHR"));
1065 vkGetSwapchainImagesKHR =
reinterpret_cast<PFN_vkGetSwapchainImagesKHR>(f->vkGetDeviceProcAddr(dev,
"vkGetSwapchainImagesKHR"));
1066 vkAcquireNextImageKHR =
reinterpret_cast<PFN_vkAcquireNextImageKHR>(f->vkGetDeviceProcAddr(dev,
"vkAcquireNextImageKHR"));
1067 vkQueuePresentKHR =
reinterpret_cast<PFN_vkQueuePresentKHR>(f->vkGetDeviceProcAddr(dev,
"vkQueuePresentKHR"));
1070 VkPhysicalDevice physDev = physDevs.at(physDevIndex);
1071 VkSurfaceCapabilitiesKHR surfaceCaps = {};
1072 VkResult err = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physDev, surface, &surfaceCaps);
1073 if (err != VK_SUCCESS) {
1074 qWarning(
"QVulkanWindow: Failed to get surface capabilities: %d", err);
1077 uint32_t reqBufferCount;
1078 if (surfaceCaps.maxImageCount == 0)
1079 reqBufferCount = qMax<uint32_t>(2, surfaceCaps.minImageCount);
1081 reqBufferCount = qMax(qMin<uint32_t>(surfaceCaps.maxImageCount, 3), surfaceCaps.minImageCount);
1083 VkExtent2D bufferSize = surfaceCaps.currentExtent;
1084 if (bufferSize.width == uint32_t(-1)) {
1085 Q_ASSERT(bufferSize.height == uint32_t(-1));
1086 bufferSize.width = swapChainImageSize.width();
1087 bufferSize.height = swapChainImageSize.height();
1089 swapChainImageSize = QSize(bufferSize.width, bufferSize.height);
1092 VkSurfaceTransformFlagBitsKHR preTransform =
1093 (surfaceCaps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)
1094 ? VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR
1095 : surfaceCaps.currentTransform;
1097 VkCompositeAlphaFlagBitsKHR compositeAlpha =
1098 (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR)
1099 ? VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR
1100 : VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
1102 if (q->requestedFormat().hasAlpha()) {
1103 if (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR)
1104 compositeAlpha = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;
1105 else if (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR)
1106 compositeAlpha = VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR;
1109 VkImageUsageFlags usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
1110 swapChainSupportsReadBack = (surfaceCaps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
1111 if (swapChainSupportsReadBack)
1112 usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
1114 VkSwapchainKHR oldSwapChain = swapChain;
1115 VkSwapchainCreateInfoKHR swapChainInfo;
1116 memset(&swapChainInfo, 0,
sizeof(swapChainInfo));
1117 swapChainInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
1118 swapChainInfo.surface = surface;
1119 swapChainInfo.minImageCount = reqBufferCount;
1120 swapChainInfo.imageFormat = colorFormat;
1121 swapChainInfo.imageColorSpace = colorSpace;
1122 swapChainInfo.imageExtent = bufferSize;
1123 swapChainInfo.imageArrayLayers = 1;
1124 swapChainInfo.imageUsage = usage;
1125 swapChainInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
1126 swapChainInfo.preTransform = preTransform;
1127 swapChainInfo.compositeAlpha = compositeAlpha;
1128 swapChainInfo.presentMode = presentMode;
1129 swapChainInfo.clipped =
true;
1130 swapChainInfo.oldSwapchain = oldSwapChain;
1132 qCDebug(lcGuiVk,
"Creating new swap chain of %d buffers, size %dx%d", reqBufferCount, bufferSize.width, bufferSize.height);
1134 VkSwapchainKHR newSwapChain;
1135 err = vkCreateSwapchainKHR(dev, &swapChainInfo,
nullptr, &newSwapChain);
1136 if (err != VK_SUCCESS) {
1137 qWarning(
"QVulkanWindow: Failed to create swap chain: %d", err);
1144 swapChain = newSwapChain;
1146 if (!createSwapChainResources()) {
1147 destroySwapChainResources();
1152 renderer->initSwapChainResources();
1154 status = StatusReady;
1157bool QVulkanWindowPrivate::createSwapChainResources()
1159 uint32_t actualSwapChainBufferCount = 0;
1160 VkResult err = vkGetSwapchainImagesKHR(dev, swapChain, &actualSwapChainBufferCount,
nullptr);
1161 if (err != VK_SUCCESS || actualSwapChainBufferCount < 2) {
1162 qWarning(
"QVulkanWindow: Failed to get swapchain images: %d (count=%d)", err, actualSwapChainBufferCount);
1166 qCDebug(lcGuiVk,
"Actual swap chain buffer count: %d (supportsReadback=%d)",
1167 actualSwapChainBufferCount, swapChainSupportsReadBack);
1168 if (actualSwapChainBufferCount > MAX_SWAPCHAIN_BUFFER_COUNT) {
1169 qWarning(
"QVulkanWindow: Too many swapchain buffers (%d)", actualSwapChainBufferCount);
1172 swapChainBufferCount = actualSwapChainBufferCount;
1174 VkImage swapChainImages[MAX_SWAPCHAIN_BUFFER_COUNT];
1175 err = vkGetSwapchainImagesKHR(dev, swapChain, &actualSwapChainBufferCount, swapChainImages);
1176 if (err != VK_SUCCESS) {
1177 qWarning(
"QVulkanWindow: Failed to get swapchain images: %d", err);
1181 if (!createTransientImage(dsFormat,
1182 VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
1183 VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT,
1192 const bool msaa = sampleCount > VK_SAMPLE_COUNT_1_BIT;
1193 VkImage msaaImages[MAX_SWAPCHAIN_BUFFER_COUNT];
1194 VkImageView msaaViews[MAX_SWAPCHAIN_BUFFER_COUNT];
1197 if (!createTransientImage(colorFormat,
1198 VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
1199 VK_IMAGE_ASPECT_COLOR_BIT,
1203 swapChainBufferCount))
1209 VkFenceCreateInfo fenceInfo = { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
nullptr, VK_FENCE_CREATE_SIGNALED_BIT };
1211 for (
int i = 0; i < swapChainBufferCount; ++i) {
1212 ImageResources &image(imageRes[i]);
1213 image.image = swapChainImages[i];
1216 image.msaaImage = msaaImages[i];
1217 image.msaaImageView = msaaViews[i];
1220 VkImageViewCreateInfo imgViewInfo;
1221 memset(&imgViewInfo, 0,
sizeof(imgViewInfo));
1222 imgViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
1223 imgViewInfo.image = swapChainImages[i];
1224 imgViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
1225 imgViewInfo.format = colorFormat;
1226 imgViewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
1227 imgViewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
1228 imgViewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
1229 imgViewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
1230 imgViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1231 imgViewInfo.subresourceRange.levelCount = imgViewInfo.subresourceRange.layerCount = 1;
1232 err = devFuncs->vkCreateImageView(dev, &imgViewInfo,
nullptr, &image.imageView);
1233 if (err != VK_SUCCESS) {
1234 qWarning(
"QVulkanWindow: Failed to create swapchain image view %d: %d", i, err);
1238 VkImageView views[3] = { image.imageView,
1240 msaa ? image.msaaImageView : VK_NULL_HANDLE };
1241 VkFramebufferCreateInfo fbInfo;
1242 memset(&fbInfo, 0,
sizeof(fbInfo));
1243 fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
1244 fbInfo.renderPass = defaultRenderPass;
1245 fbInfo.attachmentCount = msaa ? 3 : 2;
1246 fbInfo.pAttachments = views;
1247 fbInfo.width = swapChainImageSize.width();
1248 fbInfo.height = swapChainImageSize.height();
1250 err = devFuncs->vkCreateFramebuffer(dev, &fbInfo,
nullptr, &image.fb);
1251 if (err != VK_SUCCESS) {
1252 qWarning(
"QVulkanWindow: Failed to create framebuffer: %d", err);
1256 if (gfxQueueFamilyIdx != presQueueFamilyIdx) {
1258 VkCommandBufferAllocateInfo cmdBufInfo = {
1259 VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
nullptr, presCmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1 };
1260 err = devFuncs->vkAllocateCommandBuffers(dev, &cmdBufInfo, &image.presTransCmdBuf);
1261 if (err != VK_SUCCESS) {
1262 qWarning(
"QVulkanWindow: Failed to allocate acquire-on-present-queue command buffer: %d", err);
1265 VkCommandBufferBeginInfo cmdBufBeginInfo = {
1266 VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
nullptr,
1267 VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT,
nullptr };
1268 err = devFuncs->vkBeginCommandBuffer(image.presTransCmdBuf, &cmdBufBeginInfo);
1269 if (err != VK_SUCCESS) {
1270 qWarning(
"QVulkanWindow: Failed to begin acquire-on-present-queue command buffer: %d", err);
1273 VkImageMemoryBarrier presTrans;
1274 memset(&presTrans, 0,
sizeof(presTrans));
1275 presTrans.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
1276 presTrans.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
1277 presTrans.oldLayout = presTrans.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
1278 presTrans.srcQueueFamilyIndex = gfxQueueFamilyIdx;
1279 presTrans.dstQueueFamilyIndex = presQueueFamilyIdx;
1280 presTrans.image = image.image;
1281 presTrans.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1282 presTrans.subresourceRange.levelCount = presTrans.subresourceRange.layerCount = 1;
1283 devFuncs->vkCmdPipelineBarrier(image.presTransCmdBuf,
1284 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
1285 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
1286 0, 0,
nullptr, 0,
nullptr,
1288 err = devFuncs->vkEndCommandBuffer(image.presTransCmdBuf);
1289 if (err != VK_SUCCESS) {
1290 qWarning(
"QVulkanWindow: Failed to end acquire-on-present-queue command buffer: %d", err);
1298 VkSemaphoreCreateInfo semInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
nullptr, 0 };
1299 for (
int i = 0; i < frameLag; ++i) {
1300 FrameResources &frame(frameRes[i]);
1302 frame.imageAcquired =
false;
1303 frame.imageSemWaitable =
false;
1305 devFuncs->vkCreateSemaphore(dev, &semInfo,
nullptr, &frame.imageSem);
1306 devFuncs->vkCreateSemaphore(dev, &semInfo,
nullptr, &frame.drawSem);
1307 if (gfxQueueFamilyIdx != presQueueFamilyIdx)
1308 devFuncs->vkCreateSemaphore(dev, &semInfo,
nullptr, &frame.presTransSem);
1310 err = devFuncs->vkCreateFence(dev, &fenceInfo,
nullptr, &frame.cmdFence);
1311 if (err != VK_SUCCESS) {
1312 qWarning(
"QVulkanWindow: Failed to create command buffer fence: %d", err);
1315 frame.cmdFenceWaitable =
true;
1323uint32_t QVulkanWindowPrivate::chooseTransientImageMemType(VkImage img, uint32_t startIndex)
1325 VkPhysicalDeviceMemoryProperties physDevMemProps;
1326 inst->functions()->vkGetPhysicalDeviceMemoryProperties(physDevs[physDevIndex], &physDevMemProps);
1328 VkMemoryRequirements memReq;
1329 devFuncs->vkGetImageMemoryRequirements(dev, img, &memReq);
1330 uint32_t memTypeIndex = uint32_t(-1);
1332 if (memReq.memoryTypeBits) {
1334 const VkMemoryType *memType = physDevMemProps.memoryTypes;
1335 bool foundDevLocal =
false;
1336 for (uint32_t i = startIndex; i < physDevMemProps.memoryTypeCount; ++i) {
1337 if (memReq.memoryTypeBits & (1 << i)) {
1338 if (memType[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) {
1339 if (!foundDevLocal) {
1340 foundDevLocal =
true;
1343 if (memType[i].propertyFlags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) {
1352 return memTypeIndex;
1357 return (v + byteAlign - 1) & ~(byteAlign - 1);
1360bool QVulkanWindowPrivate::createTransientImage(VkFormat format,
1361 VkImageUsageFlags usage,
1362 VkImageAspectFlags aspectMask,
1364 VkDeviceMemory *mem,
1368 VkMemoryRequirements memReq;
1371 Q_ASSERT(count > 0);
1372 for (
int i = 0; i < count; ++i) {
1373 VkImageCreateInfo imgInfo;
1374 memset(&imgInfo, 0,
sizeof(imgInfo));
1375 imgInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
1376 imgInfo.imageType = VK_IMAGE_TYPE_2D;
1377 imgInfo.format = format;
1378 imgInfo.extent.width = swapChainImageSize.width();
1379 imgInfo.extent.height = swapChainImageSize.height();
1380 imgInfo.extent.depth = 1;
1381 imgInfo.mipLevels = imgInfo.arrayLayers = 1;
1382 imgInfo.samples = sampleCount;
1383 imgInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
1384 imgInfo.usage = usage | VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT;
1386 err = devFuncs->vkCreateImage(dev, &imgInfo,
nullptr, images + i);
1387 if (err != VK_SUCCESS) {
1388 qWarning(
"QVulkanWindow: Failed to create image: %d", err);
1395 devFuncs->vkGetImageMemoryRequirements(dev, images[i], &memReq);
1398 VkMemoryAllocateInfo memInfo;
1399 memset(&memInfo, 0,
sizeof(memInfo));
1400 memInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
1401 memInfo.allocationSize = aligned(memReq.size, memReq.alignment) * count;
1403 uint32_t startIndex = 0;
1405 memInfo.memoryTypeIndex = chooseTransientImageMemType(images[0], startIndex);
1406 if (memInfo.memoryTypeIndex == uint32_t(-1)) {
1407 qWarning(
"QVulkanWindow: No suitable memory type found");
1410 startIndex = memInfo.memoryTypeIndex + 1;
1411 qCDebug(lcGuiVk,
"Allocating %u bytes for transient image (memtype %u)",
1412 uint32_t(memInfo.allocationSize), memInfo.memoryTypeIndex);
1413 err = devFuncs->vkAllocateMemory(dev, &memInfo,
nullptr, mem);
1414 if (err != VK_SUCCESS && err != VK_ERROR_OUT_OF_DEVICE_MEMORY) {
1415 qWarning(
"QVulkanWindow: Failed to allocate image memory: %d", err);
1418 }
while (err != VK_SUCCESS);
1420 VkDeviceSize ofs = 0;
1421 for (
int i = 0; i < count; ++i) {
1422 err = devFuncs->vkBindImageMemory(dev, images[i], *mem, ofs);
1423 if (err != VK_SUCCESS) {
1424 qWarning(
"QVulkanWindow: Failed to bind image memory: %d", err);
1427 ofs += aligned(memReq.size, memReq.alignment);
1429 VkImageViewCreateInfo imgViewInfo;
1430 memset(&imgViewInfo, 0,
sizeof(imgViewInfo));
1431 imgViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
1432 imgViewInfo.image = images[i];
1433 imgViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
1434 imgViewInfo.format = format;
1435 imgViewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
1436 imgViewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
1437 imgViewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
1438 imgViewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
1439 imgViewInfo.subresourceRange.aspectMask = aspectMask;
1440 imgViewInfo.subresourceRange.levelCount = imgViewInfo.subresourceRange.layerCount = 1;
1442 err = devFuncs->vkCreateImageView(dev, &imgViewInfo,
nullptr, views + i);
1443 if (err != VK_SUCCESS) {
1444 qWarning(
"QVulkanWindow: Failed to create image view: %d", err);
1452void QVulkanWindowPrivate::releaseSwapChain()
1454 if (!dev || !swapChain)
1457 qCDebug(lcGuiVk,
"Releasing swapchain");
1459 devFuncs->vkDeviceWaitIdle(dev);
1462 renderer->releaseSwapChainResources();
1463 devFuncs->vkDeviceWaitIdle(dev);
1466 destroySwapChainResources();
1468 if (status == StatusReady)
1469 status = StatusDeviceReady;
1472void QVulkanWindowPrivate::destroySwapChainResources()
1474 for (
int i = 0; i < frameLag; ++i) {
1475 FrameResources &frame(frameRes[i]);
1477 devFuncs->vkFreeCommandBuffers(dev, cmdPool, 1, &frame.cmdBuf);
1478 frame.cmdBuf = VK_NULL_HANDLE;
1480 if (frame.imageSem) {
1481 devFuncs->vkDestroySemaphore(dev, frame.imageSem,
nullptr);
1482 frame.imageSem = VK_NULL_HANDLE;
1484 if (frame.drawSem) {
1485 devFuncs->vkDestroySemaphore(dev, frame.drawSem,
nullptr);
1486 frame.drawSem = VK_NULL_HANDLE;
1488 if (frame.presTransSem) {
1489 devFuncs->vkDestroySemaphore(dev, frame.presTransSem,
nullptr);
1490 frame.presTransSem = VK_NULL_HANDLE;
1492 if (frame.cmdFence) {
1493 if (frame.cmdFenceWaitable)
1494 devFuncs->vkWaitForFences(dev, 1, &frame.cmdFence, VK_TRUE, UINT64_MAX);
1495 devFuncs->vkDestroyFence(dev, frame.cmdFence,
nullptr);
1496 frame.cmdFence = VK_NULL_HANDLE;
1497 frame.cmdFenceWaitable =
false;
1501 for (
int i = 0; i < swapChainBufferCount; ++i) {
1502 ImageResources &image(imageRes[i]);
1504 devFuncs->vkDestroyFramebuffer(dev, image.fb,
nullptr);
1505 image.fb = VK_NULL_HANDLE;
1507 if (image.imageView) {
1508 devFuncs->vkDestroyImageView(dev, image.imageView,
nullptr);
1509 image.imageView = VK_NULL_HANDLE;
1511 if (image.presTransCmdBuf) {
1512 devFuncs->vkFreeCommandBuffers(dev, presCmdPool, 1, &image.presTransCmdBuf);
1513 image.presTransCmdBuf = VK_NULL_HANDLE;
1515 if (image.msaaImageView) {
1516 devFuncs->vkDestroyImageView(dev, image.msaaImageView,
nullptr);
1517 image.msaaImageView = VK_NULL_HANDLE;
1519 if (image.msaaImage) {
1520 devFuncs->vkDestroyImage(dev, image.msaaImage,
nullptr);
1521 image.msaaImage = VK_NULL_HANDLE;
1526 devFuncs->vkFreeMemory(dev, msaaImageMem,
nullptr);
1527 msaaImageMem = VK_NULL_HANDLE;
1531 devFuncs->vkDestroyImageView(dev, dsView,
nullptr);
1532 dsView = VK_NULL_HANDLE;
1535 devFuncs->vkDestroyImage(dev, dsImage,
nullptr);
1536 dsImage = VK_NULL_HANDLE;
1539 devFuncs->vkFreeMemory(dev, dsMem,
nullptr);
1540 dsMem = VK_NULL_HANDLE;
1544 vkDestroySwapchainKHR(dev, swapChain,
nullptr);
1545 swapChain = VK_NULL_HANDLE;
1548 swapChainBufferCount = 0;
1552
1553
1554void QVulkanWindow::exposeEvent(QExposeEvent *)
1561 if (!d->flags.testFlag(PersistentResources)) {
1562 d->releaseSwapChain();
1568void QVulkanWindowPrivate::ensureStarted()
1571 if (status == QVulkanWindowPrivate::StatusFailRetry)
1572 status = QVulkanWindowPrivate::StatusUninitialized;
1573 if (status == QVulkanWindowPrivate::StatusUninitialized) {
1575 if (status == QVulkanWindowPrivate::StatusDeviceReady)
1576 recreateSwapChain();
1578 if (status == QVulkanWindowPrivate::StatusReady)
1583
1584
1585void QVulkanWindow::resizeEvent(QResizeEvent *)
1591
1592
1593bool QVulkanWindow::event(QEvent *e)
1597 switch (e->type()) {
1599 case QEvent::UpdateRequest:
1607 case QEvent::PlatformSurface:
1608 if (
static_cast<QPlatformSurfaceEvent *>(e)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) {
1609 d->releaseSwapChain();
1618 return QWindow::event(e);
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1641
1642
1643
1644
1645
1646
1647void QVulkanWindow::setQueueCreateInfoModifier(
const QueueCreateInfoModifier &modifier)
1650 d->queueCreateInfoModifier = modifier;
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687void QVulkanWindow::setEnabledFeaturesModifier(
const EnabledFeaturesModifier &modifier)
1690 d->enabledFeaturesModifier = modifier;
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1721
1722
1723
1724
1725
1726void QVulkanWindow::setEnabledFeaturesModifier(EnabledFeatures2Modifier modifier)
1729 d->enabledFeatures2Modifier = std::move(modifier);
1733
1734
1735
1736
1737
1738
1739bool QVulkanWindow::isValid()
const
1741 Q_D(
const QVulkanWindow);
1742 return d->status == QVulkanWindowPrivate::StatusReady;
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756QVulkanWindowRenderer *QVulkanWindow::createRenderer()
1762
1763
1764QVulkanWindowRenderer::~QVulkanWindowRenderer()
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784void QVulkanWindowRenderer::preInitResources()
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803void QVulkanWindowRenderer::initResources()
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825void QVulkanWindowRenderer::initSwapChainResources()
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848void QVulkanWindowRenderer::releaseSwapChainResources()
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864void QVulkanWindowRenderer::releaseResources()
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909void QVulkanWindowRenderer::physicalDeviceLost()
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928void QVulkanWindowRenderer::logicalDeviceLost()
1932QSize QVulkanWindowPrivate::surfacePixelSize()
const
1934 Q_Q(
const QVulkanWindow);
1935 VkSurfaceCapabilitiesKHR surfaceCaps = {};
1936 const VkResult err = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physDevs.at(physDevIndex), surface, &surfaceCaps);
1937 if (err != VK_SUCCESS) {
1938 qWarning(
"QVulkanWindow: Failed to get surface capabilities: %d", err);
1939 return q->size() * q->devicePixelRatio();
1941 VkExtent2D bufferSize = surfaceCaps.currentExtent;
1942 if (bufferSize.width == uint32_t(-1)) {
1943 Q_ASSERT(bufferSize.height == uint32_t(-1));
1944 return q->size() * q->devicePixelRatio();
1946 return QSize(
int(bufferSize.width),
int(bufferSize.height));
1949void QVulkanWindowPrivate::beginFrame()
1951 if (status != StatusReady || framePending)
1955 if (swapChainImageSize != surfacePixelSize()) {
1956 recreateSwapChain();
1957 if (status != StatusReady)
1962 FrameResources &frame(frameRes[currentFrame]);
1963 if (frame.cmdFenceWaitable) {
1964 devFuncs->vkWaitForFences(dev, 1, &frame.cmdFence, VK_TRUE, UINT64_MAX);
1965 devFuncs->vkResetFences(dev, 1, &frame.cmdFence);
1966 frame.cmdFenceWaitable =
false;
1970 if (!frame.imageAcquired) {
1971 VkResult err = vkAcquireNextImageKHR(dev, swapChain, UINT64_MAX,
1972 frame.imageSem, VK_NULL_HANDLE, ¤tImage);
1973 if (err == VK_SUCCESS || err == VK_SUBOPTIMAL_KHR) {
1974 frame.imageSemWaitable =
true;
1975 frame.imageAcquired =
true;
1976 }
else if (err == VK_ERROR_OUT_OF_DATE_KHR) {
1977 recreateSwapChain();
1981 if (!checkDeviceLost(err))
1982 qWarning(
"QVulkanWindow: Failed to acquire next swapchain image: %d", err);
1990 devFuncs->vkFreeCommandBuffers(dev, cmdPool, 1, &frame.cmdBuf);
1991 frame.cmdBuf =
nullptr;
1994 VkCommandBufferAllocateInfo cmdBufInfo = {
1995 VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
nullptr, cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1 };
1996 VkResult err = devFuncs->vkAllocateCommandBuffers(dev, &cmdBufInfo, &frame.cmdBuf);
1997 if (err != VK_SUCCESS) {
1998 if (!checkDeviceLost(err))
1999 qWarning(
"QVulkanWindow: Failed to allocate frame command buffer: %d", err);
2003 VkCommandBufferBeginInfo cmdBufBeginInfo = {
2004 VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
nullptr, 0,
nullptr };
2005 err = devFuncs->vkBeginCommandBuffer(frame.cmdBuf, &cmdBufBeginInfo);
2006 if (err != VK_SUCCESS) {
2007 if (!checkDeviceLost(err))
2008 qWarning(
"QVulkanWindow: Failed to begin frame command buffer: %d", err);
2012 if (frameGrabbing) {
2013 frameGrabTargetImage = QImage(swapChainImageSize, QImage::Format_RGBA8888);
2014 if (frameGrabTargetImage.isNull()) {
2015 qWarning(
"QVulkanWindow: Failed to allocate readback image of size %dx%d",
2016 swapChainImageSize.width(), swapChainImageSize.height());
2017 frameGrabbing =
false;
2022 ImageResources &image(imageRes[currentImage]);
2024 framePending =
true;
2025 renderer->startNextFrame();
2028 VkClearColorValue clearColor = { { 0.0f, 0.0f, 0.0f, 1.0f } };
2029 VkClearDepthStencilValue clearDS = { 1.0f, 0 };
2030 VkClearValue clearValues[3];
2031 memset(clearValues, 0,
sizeof(clearValues));
2032 clearValues[0].color = clearValues[2].color = clearColor;
2033 clearValues[1].depthStencil = clearDS;
2035 VkRenderPassBeginInfo rpBeginInfo;
2036 memset(&rpBeginInfo, 0,
sizeof(rpBeginInfo));
2037 rpBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
2038 rpBeginInfo.renderPass = defaultRenderPass;
2039 rpBeginInfo.framebuffer = image.fb;
2040 rpBeginInfo.renderArea.extent.width = swapChainImageSize.width();
2041 rpBeginInfo.renderArea.extent.height = swapChainImageSize.height();
2042 rpBeginInfo.clearValueCount = sampleCount > VK_SAMPLE_COUNT_1_BIT ? 3 : 2;
2043 rpBeginInfo.pClearValues = clearValues;
2044 devFuncs->vkCmdBeginRenderPass(frame.cmdBuf, &rpBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
2045 devFuncs->vkCmdEndRenderPass(frame.cmdBuf);
2051void QVulkanWindowPrivate::endFrame()
2055 FrameResources &frame(frameRes[currentFrame]);
2056 ImageResources &image(imageRes[currentImage]);
2058 if (gfxQueueFamilyIdx != presQueueFamilyIdx && !frameGrabbing) {
2061 VkImageMemoryBarrier presTrans;
2062 memset(&presTrans, 0,
sizeof(presTrans));
2063 presTrans.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
2064 presTrans.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
2065 presTrans.oldLayout = presTrans.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
2066 presTrans.srcQueueFamilyIndex = gfxQueueFamilyIdx;
2067 presTrans.dstQueueFamilyIndex = presQueueFamilyIdx;
2068 presTrans.image = image.image;
2069 presTrans.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
2070 presTrans.subresourceRange.levelCount = presTrans.subresourceRange.layerCount = 1;
2071 devFuncs->vkCmdPipelineBarrier(frame.cmdBuf,
2072 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
2073 VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
2074 0, 0,
nullptr, 0,
nullptr,
2079 if (frameGrabbing && !addReadback()) {
2080 devFuncs->vkEndCommandBuffer(frame.cmdBuf);
2081 releaseReadbackResources();
2082 frameGrabbing =
false;
2083 frameGrabTargetImage = QImage();
2087 VkResult err = devFuncs->vkEndCommandBuffer(frame.cmdBuf);
2088 if (err != VK_SUCCESS) {
2089 if (!checkDeviceLost(err))
2090 qWarning(
"QVulkanWindow: Failed to end frame command buffer: %d", err);
2095 VkSubmitInfo submitInfo;
2096 memset(&submitInfo, 0,
sizeof(submitInfo));
2097 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
2098 submitInfo.commandBufferCount = 1;
2099 submitInfo.pCommandBuffers = &frame.cmdBuf;
2100 if (frame.imageSemWaitable) {
2101 submitInfo.waitSemaphoreCount = 1;
2102 submitInfo.pWaitSemaphores = &frame.imageSem;
2104 if (!frameGrabbing) {
2105 submitInfo.signalSemaphoreCount = 1;
2106 submitInfo.pSignalSemaphores = &frame.drawSem;
2108 VkPipelineStageFlags psf = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
2109 submitInfo.pWaitDstStageMask = &psf;
2111 Q_ASSERT(!frame.cmdFenceWaitable);
2113 err = devFuncs->vkQueueSubmit(gfxQueue, 1, &submitInfo, frame.cmdFence);
2114 if (err == VK_SUCCESS) {
2115 frame.imageSemWaitable =
false;
2116 frame.cmdFenceWaitable =
true;
2118 if (!checkDeviceLost(err))
2119 qWarning(
"QVulkanWindow: Failed to submit to graphics queue: %d", err);
2124 if (frameGrabbing) {
2125 finishBlockingReadback();
2126 frameGrabbing =
false;
2129 emit q->frameGrabbed(frameGrabTargetImage);
2133 if (gfxQueueFamilyIdx != presQueueFamilyIdx) {
2135 submitInfo.pWaitSemaphores = &frame.drawSem;
2136 submitInfo.pSignalSemaphores = &frame.presTransSem;
2137 submitInfo.pCommandBuffers = &image.presTransCmdBuf;
2138 err = devFuncs->vkQueueSubmit(presQueue, 1, &submitInfo, VK_NULL_HANDLE);
2139 if (err != VK_SUCCESS) {
2140 if (!checkDeviceLost(err))
2141 qWarning(
"QVulkanWindow: Failed to submit to present queue: %d", err);
2147 VkPresentInfoKHR presInfo;
2148 memset(&presInfo, 0,
sizeof(presInfo));
2149 presInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
2150 presInfo.swapchainCount = 1;
2151 presInfo.pSwapchains = &swapChain;
2152 presInfo.pImageIndices = ¤tImage;
2153 presInfo.waitSemaphoreCount = 1;
2154 presInfo.pWaitSemaphores = gfxQueueFamilyIdx == presQueueFamilyIdx ? &frame.drawSem : &frame.presTransSem;
2158 inst->presentAboutToBeQueued(q);
2160 err = vkQueuePresentKHR(presQueue, &presInfo);
2161 if (err != VK_SUCCESS) {
2162 if (err == VK_ERROR_OUT_OF_DATE_KHR) {
2163 recreateSwapChain();
2166 }
else if (err != VK_SUBOPTIMAL_KHR) {
2167 if (!checkDeviceLost(err))
2168 qWarning(
"QVulkanWindow: Failed to present: %d", err);
2173 frame.imageAcquired =
false;
2175 inst->presentQueued(q);
2177 currentFrame = (currentFrame + 1) % frameLag;
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193void QVulkanWindow::frameReady()
2195 Q_ASSERT_X(QThread::isMainThread(),
2196 "QVulkanWindow",
"frameReady() can only be called from the GUI (main) thread");
2200 if (!d->framePending) {
2201 qWarning(
"QVulkanWindow: frameReady() called without a corresponding startNextFrame()");
2205 d->framePending =
false;
2210bool QVulkanWindowPrivate::checkDeviceLost(VkResult err)
2212 if (err == VK_ERROR_DEVICE_LOST) {
2213 qWarning(
"QVulkanWindow: Device lost");
2215 renderer->logicalDeviceLost();
2216 qCDebug(lcGuiVk,
"Releasing all resources due to device lost");
2219 qCDebug(lcGuiVk,
"Restarting");
2226bool QVulkanWindowPrivate::addReadback()
2228 VkImageCreateInfo imageInfo;
2229 memset(&imageInfo, 0,
sizeof(imageInfo));
2230 imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
2231 imageInfo.imageType = VK_IMAGE_TYPE_2D;
2232 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
2233 imageInfo.extent.width = frameGrabTargetImage.width();
2234 imageInfo.extent.height = frameGrabTargetImage.height();
2235 imageInfo.extent.depth = 1;
2236 imageInfo.mipLevels = 1;
2237 imageInfo.arrayLayers = 1;
2238 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
2239 imageInfo.tiling = VK_IMAGE_TILING_LINEAR;
2240 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT;
2241 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
2243 VkResult err = devFuncs->vkCreateImage(dev, &imageInfo,
nullptr, &frameGrabImage);
2244 if (err != VK_SUCCESS) {
2245 qWarning(
"QVulkanWindow: Failed to create image for readback: %d", err);
2249 VkMemoryRequirements memReq;
2250 devFuncs->vkGetImageMemoryRequirements(dev, frameGrabImage, &memReq);
2252 VkMemoryAllocateInfo allocInfo = {
2253 VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
2259 err = devFuncs->vkAllocateMemory(dev, &allocInfo,
nullptr, &frameGrabImageMem);
2260 if (err != VK_SUCCESS) {
2261 qWarning(
"QVulkanWindow: Failed to allocate memory for readback image: %d", err);
2265 err = devFuncs->vkBindImageMemory(dev, frameGrabImage, frameGrabImageMem, 0);
2266 if (err != VK_SUCCESS) {
2267 qWarning(
"QVulkanWindow: Failed to bind readback image memory: %d", err);
2271 FrameResources &frame(frameRes[currentFrame]);
2272 ImageResources &image(imageRes[currentImage]);
2274 VkImageMemoryBarrier barrier;
2275 memset(&barrier, 0,
sizeof(barrier));
2276 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
2277 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
2278 barrier.subresourceRange.levelCount = barrier.subresourceRange.layerCount = 1;
2280 barrier.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
2281 barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
2282 barrier.srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;
2283 barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
2284 barrier.image = image.image;
2286 devFuncs->vkCmdPipelineBarrier(frame.cmdBuf,
2287 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
2288 VK_PIPELINE_STAGE_TRANSFER_BIT,
2289 0, 0,
nullptr, 0,
nullptr,
2292 barrier.oldLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
2293 barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
2294 barrier.srcAccessMask = 0;
2295 barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
2296 barrier.image = frameGrabImage;
2298 devFuncs->vkCmdPipelineBarrier(frame.cmdBuf,
2299 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
2300 VK_PIPELINE_STAGE_TRANSFER_BIT,
2301 0, 0,
nullptr, 0,
nullptr,
2304 VkImageCopy copyInfo;
2305 memset(©Info, 0,
sizeof(copyInfo));
2306 copyInfo.srcSubresource.aspectMask = copyInfo.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
2307 copyInfo.srcSubresource.layerCount = copyInfo.dstSubresource.layerCount = 1;
2308 copyInfo.extent.width = frameGrabTargetImage.width();
2309 copyInfo.extent.height = frameGrabTargetImage.height();
2310 copyInfo.extent.depth = 1;
2312 devFuncs->vkCmdCopyImage(frame.cmdBuf, image.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
2313 frameGrabImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©Info);
2315 barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
2316 barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
2317 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
2318 barrier.dstAccessMask = VK_ACCESS_HOST_READ_BIT;
2319 barrier.image = frameGrabImage;
2321 devFuncs->vkCmdPipelineBarrier(frame.cmdBuf,
2322 VK_PIPELINE_STAGE_TRANSFER_BIT,
2323 VK_PIPELINE_STAGE_HOST_BIT,
2324 0, 0,
nullptr, 0,
nullptr,
2330void QVulkanWindowPrivate::releaseReadbackResources()
2332 if (frameGrabImage) {
2333 devFuncs->vkDestroyImage(dev, frameGrabImage,
nullptr);
2334 frameGrabImage = VK_NULL_HANDLE;
2336 if (frameGrabImageMem) {
2337 devFuncs->vkFreeMemory(dev, frameGrabImageMem,
nullptr);
2338 frameGrabImageMem = VK_NULL_HANDLE;
2342void QVulkanWindowPrivate::finishBlockingReadback()
2346 FrameResources &frame(frameRes[currentFrame]);
2347 if (frame.cmdFenceWaitable) {
2348 devFuncs->vkWaitForFences(dev, 1, &frame.cmdFence, VK_TRUE, UINT64_MAX);
2349 devFuncs->vkResetFences(dev, 1, &frame.cmdFence);
2350 frame.cmdFenceWaitable =
false;
2353 VkImageSubresource subres = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0 };
2354 VkSubresourceLayout layout;
2355 devFuncs->vkGetImageSubresourceLayout(dev, frameGrabImage, &subres, &layout);
2358 VkResult err = devFuncs->vkMapMemory(dev, frameGrabImageMem, layout.offset, layout.size, 0,
reinterpret_cast<
void **>(&p));
2359 if (err != VK_SUCCESS) {
2360 qWarning(
"QVulkanWindow: Failed to map readback image memory after transfer: %d", err);
2361 releaseReadbackResources();
2365 for (
int y = 0; y < frameGrabTargetImage.height(); ++y) {
2366 memcpy(frameGrabTargetImage.scanLine(y), p, frameGrabTargetImage.width() * 4);
2367 p += layout.rowPitch;
2370 devFuncs->vkUnmapMemory(dev, frameGrabImageMem);
2372 releaseReadbackResources();
2376
2377
2378
2379
2380
2381
2382VkPhysicalDevice QVulkanWindow::physicalDevice()
const
2384 Q_D(
const QVulkanWindow);
2385 if (d->physDevIndex < d->physDevs.size())
2386 return d->physDevs[d->physDevIndex];
2387 qWarning(
"QVulkanWindow: Physical device not available");
2388 return VK_NULL_HANDLE;
2392
2393
2394
2395
2396
2397
2398const VkPhysicalDeviceProperties *QVulkanWindow::physicalDeviceProperties()
const
2400 Q_D(
const QVulkanWindow);
2401 if (d->physDevIndex < d->physDevProps.size())
2402 return &d->physDevProps[d->physDevIndex];
2403 qWarning(
"QVulkanWindow: Physical device properties not available");
2408
2409
2410
2411
2412
2413
2414VkDevice QVulkanWindow::device()
const
2416 Q_D(
const QVulkanWindow);
2421
2422
2423
2424
2425
2426
2427VkQueue QVulkanWindow::graphicsQueue()
const
2429 Q_D(
const QVulkanWindow);
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444uint32_t QVulkanWindow::graphicsQueueFamilyIndex()
const
2446 Q_D(
const QVulkanWindow);
2447 return d->gfxQueueFamilyIdx;
2451
2452
2453
2454
2455
2456
2457VkCommandPool QVulkanWindow::graphicsCommandPool()
const
2459 Q_D(
const QVulkanWindow);
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473uint32_t QVulkanWindow::hostVisibleMemoryIndex()
const
2475 Q_D(
const QVulkanWindow);
2476 return d->hostVisibleMemIndex;
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491uint32_t QVulkanWindow::deviceLocalMemoryIndex()
const
2493 Q_D(
const QVulkanWindow);
2494 return d->deviceLocalMemIndex;
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515VkRenderPass QVulkanWindow::defaultRenderPass()
const
2517 Q_D(
const QVulkanWindow);
2518 return d->defaultRenderPass;
2522
2523
2524
2525
2526
2527
2528
2529
2530VkFormat QVulkanWindow::colorFormat()
const
2532 Q_D(
const QVulkanWindow);
2533 return d->colorFormat;
2537
2538
2539
2540
2541
2542
2543VkFormat QVulkanWindow::depthStencilFormat()
const
2545 Q_D(
const QVulkanWindow);
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572QSize QVulkanWindow::swapChainImageSize()
const
2574 Q_D(
const QVulkanWindow);
2575 return d->swapChainImageSize;
2579
2580
2581
2582
2583
2584
2585
2586VkCommandBuffer QVulkanWindow::currentCommandBuffer()
const
2588 Q_D(
const QVulkanWindow);
2589 if (!d->framePending) {
2590 qWarning(
"QVulkanWindow: Attempted to call currentCommandBuffer() without an active frame");
2591 return VK_NULL_HANDLE;
2593 return d->frameRes[d->currentFrame].cmdBuf;
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615VkFramebuffer QVulkanWindow::currentFramebuffer()
const
2617 Q_D(
const QVulkanWindow);
2618 if (!d->framePending) {
2619 qWarning(
"QVulkanWindow: Attempted to call currentFramebuffer() without an active frame");
2620 return VK_NULL_HANDLE;
2622 return d->imageRes[d->currentImage].fb;
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646int QVulkanWindow::currentFrame()
const
2648 Q_D(
const QVulkanWindow);
2649 if (!d->framePending)
2650 qWarning(
"QVulkanWindow: Attempted to call currentFrame() without an active frame");
2651 return d->currentFrame;
2655
2656
2657
2658
2659
2662
2663
2664
2665
2666
2667
2668
2669
2670int QVulkanWindow::concurrentFrameCount()
const
2672 Q_D(
const QVulkanWindow);
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687int QVulkanWindow::swapChainImageCount()
const
2689 Q_D(
const QVulkanWindow);
2690 return d->swapChainBufferCount;
2694
2695
2696
2697
2698
2699int QVulkanWindow::currentSwapChainImageIndex()
const
2701 Q_D(
const QVulkanWindow);
2702 if (!d->framePending)
2703 qWarning(
"QVulkanWindow: Attempted to call currentSwapChainImageIndex() without an active frame");
2704 return d->currentImage;
2708
2709
2710
2711
2712
2713
2714
2715
2716VkImage QVulkanWindow::swapChainImage(
int idx)
const
2718 Q_D(
const QVulkanWindow);
2719 return idx >= 0 && idx < d->swapChainBufferCount ? d->imageRes[idx].image : VK_NULL_HANDLE;
2723
2724
2725
2726
2727
2728
2729
2730
2731VkImageView QVulkanWindow::swapChainImageView(
int idx)
const
2733 Q_D(
const QVulkanWindow);
2734 return idx >= 0 && idx < d->swapChainBufferCount ? d->imageRes[idx].imageView : VK_NULL_HANDLE;
2738
2739
2740
2741
2742
2743
2744VkImage QVulkanWindow::depthStencilImage()
const
2746 Q_D(
const QVulkanWindow);
2751
2752
2753
2754
2755
2756
2757VkImageView QVulkanWindow::depthStencilImageView()
const
2759 Q_D(
const QVulkanWindow);
2764
2765
2766
2767
2768
2769
2770
2771VkSampleCountFlagBits QVulkanWindow::sampleCountFlagBits()
const
2773 Q_D(
const QVulkanWindow);
2774 return d->sampleCount;
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787VkImage QVulkanWindow::msaaColorImage(
int idx)
const
2789 Q_D(
const QVulkanWindow);
2790 return idx >= 0 && idx < d->swapChainBufferCount ? d->imageRes[idx].msaaImage : VK_NULL_HANDLE;
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803VkImageView QVulkanWindow::msaaColorImageView(
int idx)
const
2805 Q_D(
const QVulkanWindow);
2806 return idx >= 0 && idx < d->swapChainBufferCount ? d->imageRes[idx].msaaImageView : VK_NULL_HANDLE;
2810
2811
2812
2813
2814
2815
2816
2817bool QVulkanWindow::supportsGrab()
const
2819 Q_D(
const QVulkanWindow);
2820 return d->swapChainSupportsReadBack;
2824
2825
2826
2827
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854QImage QVulkanWindow::grab()
2857 if (!d->swapChain) {
2858 qWarning(
"QVulkanWindow: Attempted to call grab() without a swapchain");
2861 if (d->framePending) {
2862 qWarning(
"QVulkanWindow: Attempted to call grab() while a frame is still pending");
2865 if (!d->swapChainSupportsReadBack) {
2866 qWarning(
"QVulkanWindow: Attempted to call grab() with a swapchain that does not support usage as transfer source");
2870 d->frameGrabTargetImage = QImage();
2872 d->frameGrabbing =
true;
2875 if (!d->framePending)
2876 d->frameGrabbing =
false;
2878 if (d->colorFormat == VK_FORMAT_B8G8R8A8_UNORM)
2879 d->frameGrabTargetImage = std::move(d->frameGrabTargetImage).rgbSwapped();
2881 return d->frameGrabTargetImage;
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895QMatrix4x4 QVulkanWindow::clipCorrectionMatrix()
2898 static constexpr QMatrix4x4 m(1.0f, 0.0f, 0.0f, 0.0f,
2899 0.0f, -1.0f, 0.0f, 0.0f,
2900 0.0f, 0.0f, 0.5f, 0.5f,
2901 0.0f, 0.0f, 0.0f, 1.0f);
2907#include "moc_qvulkanwindow.cpp"
Combined button and popup list for selecting options.
VkSampleCountFlagBits mask
static VkDeviceSize aligned(VkDeviceSize v, VkDeviceSize byteAlign)