Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qrhivulkan.cpp
Go to the documentation of this file.
1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qrhivulkan_p.h"
5#include <qpa/qplatformvulkaninstance.h>
6
7#define VMA_IMPLEMENTATION
8#define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
9#define VMA_STATIC_VULKAN_FUNCTIONS 0
10#define VMA_RECORDING_ENABLED 0
11#define VMA_DEDICATED_ALLOCATION 0
12#ifdef QT_DEBUG
13#define VMA_DEBUG_INITIALIZE_ALLOCATIONS 1
14#endif
16QT_WARNING_DISABLE_GCC("-Wsuggest-override")
17#if defined(Q_CC_CLANG) && Q_CC_CLANG >= 1100
18QT_WARNING_DISABLE_CLANG("-Wdeprecated-copy")
19#endif
20#include "vk_mem_alloc.h"
22
23#include <qmath.h>
24#include <QVulkanFunctions>
25#include <QtGui/qwindow.h>
26#include <optional>
27
29
30/*
31 Vulkan 1.0 backend. Provides a double-buffered swapchain that throttles the
32 rendering thread to vsync. Textures and "static" buffers are device local,
33 and a separate, host visible staging buffer is used to upload data to them.
34 "Dynamic" buffers are in host visible memory and are duplicated (since there
35 can be 2 frames in flight). This is handled transparently to the application.
36
37 Barriers are generated automatically for each render or compute pass, based
38 on the resources that are used in that pass (in QRhiShaderResourceBindings,
39 vertex inputs, etc.). This implies deferring the recording of the command
40 buffer since the barriers have to be placed at the right place (before the
41 pass), and that can only be done once we know all the things the pass does.
42
43 This in turn has implications for integrating external commands
44 (beginExternal() - direct Vulkan calls - endExternal()) because that is
45 incompatible with this approach by nature. Therefore we support another mode
46 of operation, where each render or compute pass uses one or more secondary
47 command buffers (recorded right away), with each beginExternal() leading to
48 closing the current secondary cb, creating a new secondary cb for the
49 external content, and then starting yet another one in endExternal() for
50 whatever comes afterwards in the pass. This way the primary command buffer
51 only has vkCmdExecuteCommand(s) within a renderpass instance
52 (Begin-EndRenderPass). (i.e. our only subpass is then
53 VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS instead of
54 VK_SUBPASS_CONTENTS_INLINE)
55
56 The command buffer management mode is decided on a per frame basis,
57 controlled by the ExternalContentsInPass flag of beginFrame().
58*/
59
286template <class Int>
287inline Int aligned(Int v, Int byteAlign)
288{
289 return (v + byteAlign - 1) & ~(byteAlign - 1);
290}
291
293
294static VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL wrap_vkGetInstanceProcAddr(VkInstance, const char *pName)
295{
296 return globalVulkanInstance->getInstanceProcAddr(pName);
297}
298
299static VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL wrap_vkGetDeviceProcAddr(VkDevice device, const char *pName)
300{
301 return globalVulkanInstance->functions()->vkGetDeviceProcAddr(device, pName);
302}
303
304static inline VmaAllocation toVmaAllocation(QVkAlloc a)
305{
306 return reinterpret_cast<VmaAllocation>(a);
307}
308
309static inline VmaAllocator toVmaAllocator(QVkAllocator a)
310{
311 return reinterpret_cast<VmaAllocator>(a);
312}
313
321QByteArrayList QRhiVulkanInitParams::preferredInstanceExtensions()
322{
323 return {
324 QByteArrayLiteral("VK_KHR_get_physical_device_properties2")
325 };
326}
327
333QByteArrayList QRhiVulkanInitParams::preferredExtensionsForImportedDevice()
334{
335 return {
336 QByteArrayLiteral("VK_KHR_swapchain"),
337 QByteArrayLiteral("VK_EXT_vertex_attribute_divisor")
338 };
339}
340
342 : ofr(this)
343{
344 inst = params->inst;
345 maybeWindow = params->window; // may be null
346 requestedDeviceExtensions = params->deviceExtensions;
347
348 if (importParams) {
349 physDev = importParams->physDev;
350 dev = importParams->dev;
351 if (dev && physDev) {
352 importedDevice = true;
353 gfxQueueFamilyIdx = importParams->gfxQueueFamilyIdx;
354 gfxQueueIdx = importParams->gfxQueueIdx;
355 // gfxQueue is output only, no point in accepting it as input
356 if (importParams->vmemAllocator) {
357 importedAllocator = true;
358 allocator = importParams->vmemAllocator;
359 }
360 }
361 }
362}
363
364static bool qvk_debug_filter(QVulkanInstance::DebugMessageSeverityFlags severity,
365 QVulkanInstance::DebugMessageTypeFlags type,
366 const void *callbackData)
367{
369 Q_UNUSED(type);
370#ifdef VK_EXT_debug_utils
371 const VkDebugUtilsMessengerCallbackDataEXT *d = static_cast<const VkDebugUtilsMessengerCallbackDataEXT *>(callbackData);
372
373 // Filter out certain misleading validation layer messages, as per
374 // VulkanMemoryAllocator documentation.
375 if (strstr(d->pMessage, "Mapping an image with layout")
376 && strstr(d->pMessage, "can result in undefined behavior if this memory is used by the device"))
377 {
378 return true;
379 }
380
381 // In certain cases allocateDescriptorSet() will attempt to allocate from a
382 // pool that does not have enough descriptors of a certain type. This makes
383 // the validation layer shout. However, this is not an error since we will
384 // then move on to another pool. If there is a real error, a qWarning
385 // message is shown by allocateDescriptorSet(), so the validation warning
386 // does not have any value and is just noise.
387 if (strstr(d->pMessage, "VUID-VkDescriptorSetAllocateInfo-descriptorPool-00307"))
388 return true;
389#else
390 Q_UNUSED(callbackData);
391#endif
392 return false;
393}
394
395static inline QRhiDriverInfo::DeviceType toRhiDeviceType(VkPhysicalDeviceType type)
396{
397 switch (type) {
398 case VK_PHYSICAL_DEVICE_TYPE_OTHER:
400 case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
402 case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
404 case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
406 case VK_PHYSICAL_DEVICE_TYPE_CPU:
408 default:
410 }
411}
412
413bool QRhiVulkan::create(QRhi::Flags flags)
414{
415 Q_ASSERT(inst);
416 if (!inst->isValid()) {
417 qWarning("Vulkan instance is not valid");
418 return false;
419 }
420
421 rhiFlags = flags;
422 qCDebug(QRHI_LOG_INFO, "Initializing QRhi Vulkan backend %p with flags %d", this, int(rhiFlags));
423
424 globalVulkanInstance = inst; // used for function resolving in vkmemalloc callbacks
425 f = inst->functions();
426 if (QRHI_LOG_INFO().isEnabled(QtDebugMsg)) {
427 qCDebug(QRHI_LOG_INFO, "Enabled instance extensions:");
428 for (const char *ext : inst->extensions())
429 qCDebug(QRHI_LOG_INFO, " %s", ext);
430 }
431 caps.debugUtils = inst->extensions().contains(QByteArrayLiteral("VK_EXT_debug_utils"));
432
433 QList<VkQueueFamilyProperties> queueFamilyProps;
434 auto queryQueueFamilyProps = [this, &queueFamilyProps] {
435 uint32_t queueCount = 0;
436 f->vkGetPhysicalDeviceQueueFamilyProperties(physDev, &queueCount, nullptr);
437 queueFamilyProps.resize(int(queueCount));
438 f->vkGetPhysicalDeviceQueueFamilyProperties(physDev, &queueCount, queueFamilyProps.data());
439 };
440
441 // Choose a physical device, unless one was provided in importParams.
442 if (!physDev) {
443 uint32_t physDevCount = 0;
444 f->vkEnumeratePhysicalDevices(inst->vkInstance(), &physDevCount, nullptr);
445 if (!physDevCount) {
446 qWarning("No physical devices");
447 return false;
448 }
449 QVarLengthArray<VkPhysicalDevice, 4> physDevs(physDevCount);
450 VkResult err = f->vkEnumeratePhysicalDevices(inst->vkInstance(), &physDevCount, physDevs.data());
451 if (err != VK_SUCCESS || !physDevCount) {
452 qWarning("Failed to enumerate physical devices: %d", err);
453 return false;
454 }
455
456 int physDevIndex = -1;
457 int requestedPhysDevIndex = -1;
458 if (qEnvironmentVariableIsSet("QT_VK_PHYSICAL_DEVICE_INDEX"))
459 requestedPhysDevIndex = qEnvironmentVariableIntValue("QT_VK_PHYSICAL_DEVICE_INDEX");
460
461 if (requestedPhysDevIndex < 0 && flags.testFlag(QRhi::PreferSoftwareRenderer)) {
462 for (int i = 0; i < int(physDevCount); ++i) {
463 f->vkGetPhysicalDeviceProperties(physDevs[i], &physDevProperties);
464 if (physDevProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU) {
465 requestedPhysDevIndex = i;
466 break;
467 }
468 }
469 }
470
471 for (int i = 0; i < int(physDevCount); ++i) {
472 f->vkGetPhysicalDeviceProperties(physDevs[i], &physDevProperties);
473 qCDebug(QRHI_LOG_INFO, "Physical device %d: '%s' %d.%d.%d (api %d.%d.%d vendor 0x%X device 0x%X type %d)",
474 i,
475 physDevProperties.deviceName,
476 VK_VERSION_MAJOR(physDevProperties.driverVersion),
477 VK_VERSION_MINOR(physDevProperties.driverVersion),
478 VK_VERSION_PATCH(physDevProperties.driverVersion),
479 VK_VERSION_MAJOR(physDevProperties.apiVersion),
480 VK_VERSION_MINOR(physDevProperties.apiVersion),
481 VK_VERSION_PATCH(physDevProperties.apiVersion),
482 physDevProperties.vendorID,
483 physDevProperties.deviceID,
484 physDevProperties.deviceType);
485 if (physDevIndex < 0 && (requestedPhysDevIndex < 0 || requestedPhysDevIndex == int(i))) {
486 physDevIndex = i;
487 qCDebug(QRHI_LOG_INFO, " using this physical device");
488 }
489 }
490
491 if (physDevIndex < 0) {
492 qWarning("No matching physical device");
493 return false;
494 }
495 physDev = physDevs[physDevIndex];
496 f->vkGetPhysicalDeviceProperties(physDev, &physDevProperties);
497 } else {
498 f->vkGetPhysicalDeviceProperties(physDev, &physDevProperties);
499 qCDebug(QRHI_LOG_INFO, "Using imported physical device '%s' %d.%d.%d (api %d.%d.%d vendor 0x%X device 0x%X type %d)",
500 physDevProperties.deviceName,
501 VK_VERSION_MAJOR(physDevProperties.driverVersion),
502 VK_VERSION_MINOR(physDevProperties.driverVersion),
503 VK_VERSION_PATCH(physDevProperties.driverVersion),
504 VK_VERSION_MAJOR(physDevProperties.apiVersion),
505 VK_VERSION_MINOR(physDevProperties.apiVersion),
506 VK_VERSION_PATCH(physDevProperties.apiVersion),
507 physDevProperties.vendorID,
508 physDevProperties.deviceID,
509 physDevProperties.deviceType);
510 }
511
512 caps.apiVersion = inst->apiVersion();
513
514 // Check the physical device API version against the instance API version,
515 // they do not have to match, which means whatever version was set in the
516 // QVulkanInstance may not be legally used with a given device if the
517 // physical device has a lower version.
518 const QVersionNumber physDevApiVersion(VK_VERSION_MAJOR(physDevProperties.apiVersion),
519 VK_VERSION_MINOR(physDevProperties.apiVersion)); // patch version left out intentionally
520 if (physDevApiVersion < caps.apiVersion) {
521 qCDebug(QRHI_LOG_INFO) << "Instance has api version" << caps.apiVersion
522 << "whereas the chosen physical device has" << physDevApiVersion
523 << "- restricting to the latter";
524 caps.apiVersion = physDevApiVersion;
525 }
526
531
532#ifdef VK_VERSION_1_2 // Vulkan11Features is only in Vulkan 1.2
533 VkPhysicalDeviceFeatures2 physDevFeaturesChainable = {};
534 physDevFeaturesChainable.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
535 physDevFeatures11 = {};
536 physDevFeatures11.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES;
537 physDevFeatures12 = {};
538 physDevFeatures12.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;
539#ifdef VK_VERSION_1_3
540 physDevFeatures13 = {};
541 physDevFeatures13.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES;
542#endif
543 if (caps.apiVersion >= QVersionNumber(1, 2)) {
544 physDevFeaturesChainable.pNext = &physDevFeatures11;
545 physDevFeatures11.pNext = &physDevFeatures12;
546#ifdef VK_VERSION_1_3
547 if (caps.apiVersion >= QVersionNumber(1, 3))
548 physDevFeatures12.pNext = &physDevFeatures13;
549#endif
550 f->vkGetPhysicalDeviceFeatures2(physDev, &physDevFeaturesChainable);
551 memcpy(&physDevFeatures, &physDevFeaturesChainable.features, sizeof(VkPhysicalDeviceFeatures));
552 } else
553#endif // VK_VERSION_1_2
554 {
555 f->vkGetPhysicalDeviceFeatures(physDev, &physDevFeatures);
556 }
557
558 // Choose queue and create device, unless the device was specified in importParams.
559 if (!importedDevice) {
560 // We only support combined graphics+present queues. When it comes to
561 // compute, only combined graphics+compute queue is used, compute gets
562 // disabled otherwise.
563 std::optional<uint32_t> gfxQueueFamilyIdxOpt;
564 std::optional<uint32_t> computelessGfxQueueCandidateIdxOpt;
565 queryQueueFamilyProps();
566 const uint32_t queueFamilyCount = uint32_t(queueFamilyProps.size());
567 for (uint32_t i = 0; i < queueFamilyCount; ++i) {
568 qCDebug(QRHI_LOG_INFO, "queue family %u: flags=0x%x count=%u",
569 i, queueFamilyProps[i].queueFlags, queueFamilyProps[i].queueCount);
570 if (!gfxQueueFamilyIdxOpt.has_value()
571 && (queueFamilyProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
572 && (!maybeWindow || inst->supportsPresent(physDev, i, maybeWindow)))
573 {
574 if (queueFamilyProps[i].queueFlags & VK_QUEUE_COMPUTE_BIT)
575 gfxQueueFamilyIdxOpt = i;
576 else if (!computelessGfxQueueCandidateIdxOpt.has_value())
577 computelessGfxQueueCandidateIdxOpt = i;
578 }
579 }
580 if (gfxQueueFamilyIdxOpt.has_value()) {
581 gfxQueueFamilyIdx = gfxQueueFamilyIdxOpt.value();
582 } else {
583 if (computelessGfxQueueCandidateIdxOpt.has_value()) {
584 gfxQueueFamilyIdx = computelessGfxQueueCandidateIdxOpt.value();
585 } else {
586 qWarning("No graphics (or no graphics+present) queue family found");
587 return false;
588 }
589 }
590
591 VkDeviceQueueCreateInfo queueInfo = {};
592 const float prio[] = { 0 };
593 queueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
594 queueInfo.queueFamilyIndex = gfxQueueFamilyIdx;
595 queueInfo.queueCount = 1;
596 queueInfo.pQueuePriorities = prio;
597
598 QList<const char *> devLayers;
599 if (inst->layers().contains("VK_LAYER_KHRONOS_validation"))
600 devLayers.append("VK_LAYER_KHRONOS_validation");
601
603 uint32_t devExtCount = 0;
604 f->vkEnumerateDeviceExtensionProperties(physDev, nullptr, &devExtCount, nullptr);
605 if (devExtCount) {
606 QList<VkExtensionProperties> extProps(devExtCount);
607 f->vkEnumerateDeviceExtensionProperties(physDev, nullptr, &devExtCount, extProps.data());
608 for (const VkExtensionProperties &p : std::as_const(extProps))
609 devExts.append({ p.extensionName, p.specVersion });
610 }
611 qCDebug(QRHI_LOG_INFO, "%d device extensions available", int(devExts.size()));
612
613 QList<const char *> requestedDevExts;
614 requestedDevExts.append("VK_KHR_swapchain");
615
616 const bool hasPhysDevProp2 = inst->extensions().contains(QByteArrayLiteral("VK_KHR_get_physical_device_properties2"));
617
618 if (devExts.contains(QByteArrayLiteral("VK_KHR_portability_subset"))) {
619 if (hasPhysDevProp2) {
620 requestedDevExts.append("VK_KHR_portability_subset");
621 } else {
622 qWarning("VK_KHR_portability_subset should be enabled on the device "
623 "but the instance does not have VK_KHR_get_physical_device_properties2 enabled. "
624 "Expect problems.");
625 }
626 }
627
628 caps.vertexAttribDivisor = false;
629#ifdef VK_EXT_vertex_attribute_divisor
630 if (devExts.contains(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME)) {
631 if (hasPhysDevProp2) {
632 requestedDevExts.append(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME);
633 caps.vertexAttribDivisor = true;
634 }
635 }
636#endif
637
639 if (!ext.isEmpty() && !requestedDevExts.contains(ext)) {
640 if (devExts.contains(ext)) {
641 requestedDevExts.append(ext.constData());
642 } else {
643 qWarning("Device extension %s requested in QRhiVulkanInitParams is not supported",
644 ext.constData());
645 }
646 }
647 }
648
649 QByteArrayList envExtList = qgetenv("QT_VULKAN_DEVICE_EXTENSIONS").split(';');
650 for (const QByteArray &ext : envExtList) {
651 if (!ext.isEmpty() && !requestedDevExts.contains(ext)) {
652 if (devExts.contains(ext)) {
653 requestedDevExts.append(ext.constData());
654 } else {
655 qWarning("Device extension %s requested in QT_VULKAN_DEVICE_EXTENSIONS is not supported",
656 ext.constData());
657 }
658 }
659 }
660
661 if (QRHI_LOG_INFO().isEnabled(QtDebugMsg)) {
662 qCDebug(QRHI_LOG_INFO, "Enabling device extensions:");
663 for (const char *ext : requestedDevExts)
664 qCDebug(QRHI_LOG_INFO, " %s", ext);
665 }
666
667 VkDeviceCreateInfo devInfo = {};
668 devInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
669 devInfo.queueCreateInfoCount = 1;
670 devInfo.pQueueCreateInfos = &queueInfo;
671 devInfo.enabledLayerCount = uint32_t(devLayers.size());
672 devInfo.ppEnabledLayerNames = devLayers.constData();
673 devInfo.enabledExtensionCount = uint32_t(requestedDevExts.size());
674 devInfo.ppEnabledExtensionNames = requestedDevExts.constData();
675
676 // Enable all features that are reported as supported, except
677 // robustness because that potentially affects performance.
678 //
679 // Enabling all features mainly serves third-party renderers that may
680 // use the VkDevice created here. For the record, the backend here
681 // optionally relies on the following features, meaning just for our
682 // (QRhi/Quick/Quick 3D) purposes it would be sufficient to
683 // enable-if-supported only the following:
684 //
685 // wideLines, largePoints, fillModeNonSolid,
686 // tessellationShader, geometryShader
687 // textureCompressionETC2, textureCompressionASTC_LDR, textureCompressionBC
688
689#ifdef VK_VERSION_1_2
690 if (caps.apiVersion >= QVersionNumber(1, 2)) {
691 physDevFeaturesChainable.features.robustBufferAccess = VK_FALSE;
692#ifdef VK_VERSION_1_3
693 physDevFeatures13.robustImageAccess = VK_FALSE;
694#endif
695 devInfo.pNext = &physDevFeaturesChainable;
696 } else
697#endif // VK_VERSION_1_2
698 {
699 physDevFeatures.robustBufferAccess = VK_FALSE;
700 devInfo.pEnabledFeatures = &physDevFeatures;
701 }
702
703 VkResult err = f->vkCreateDevice(physDev, &devInfo, nullptr, &dev);
704 if (err != VK_SUCCESS) {
705 qWarning("Failed to create device: %d", err);
706 return false;
707 }
708 } else {
709 qCDebug(QRHI_LOG_INFO, "Using imported device %p", dev);
710 }
711
712 vkGetPhysicalDeviceSurfaceCapabilitiesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR>(
713 inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfaceCapabilitiesKHR"));
714 vkGetPhysicalDeviceSurfaceFormatsKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceFormatsKHR>(
715 inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfaceFormatsKHR"));
716 vkGetPhysicalDeviceSurfacePresentModesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfacePresentModesKHR>(
717 inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfacePresentModesKHR"));
718
719 df = inst->deviceFunctions(dev);
720
721 VkCommandPoolCreateInfo poolInfo = {};
722 poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
723 poolInfo.queueFamilyIndex = gfxQueueFamilyIdx;
724 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
725 VkResult err = df->vkCreateCommandPool(dev, &poolInfo, nullptr, &cmdPool[i]);
726 if (err != VK_SUCCESS) {
727 qWarning("Failed to create command pool: %d", err);
728 return false;
729 }
730 }
731
732 qCDebug(QRHI_LOG_INFO, "Using queue family index %u and queue index %u",
733 gfxQueueFamilyIdx, gfxQueueIdx);
734
735 df->vkGetDeviceQueue(dev, gfxQueueFamilyIdx, gfxQueueIdx, &gfxQueue);
736
737 if (queueFamilyProps.isEmpty())
738 queryQueueFamilyProps();
739
740 caps.compute = (queueFamilyProps[gfxQueueFamilyIdx].queueFlags & VK_QUEUE_COMPUTE_BIT) != 0;
741 timestampValidBits = queueFamilyProps[gfxQueueFamilyIdx].timestampValidBits;
742
743 ubufAlign = physDevProperties.limits.minUniformBufferOffsetAlignment;
744 // helps little with an optimal offset of 1 (on some drivers) when the spec
745 // elsewhere states that the minimum bufferOffset is 4...
746 texbufAlign = qMax<VkDeviceSize>(4, physDevProperties.limits.optimalBufferCopyOffsetAlignment);
747
748 caps.wideLines = physDevFeatures.wideLines;
749
750 caps.texture3DSliceAs2D = caps.apiVersion >= QVersionNumber(1, 1);
751
752 caps.tessellation = physDevFeatures.tessellationShader;
753 caps.geometryShader = physDevFeatures.geometryShader;
754
755 caps.nonFillPolygonMode = physDevFeatures.fillModeNonSolid;
756
757#ifdef VK_VERSION_1_2
758 caps.multiView = caps.apiVersion >= QVersionNumber(1, 1) && physDevFeatures11.multiview;
759#endif
760
761 if (!importedAllocator) {
762 VmaVulkanFunctions funcs = {};
763 funcs.vkGetInstanceProcAddr = wrap_vkGetInstanceProcAddr;
764 funcs.vkGetDeviceProcAddr = wrap_vkGetDeviceProcAddr;
765
766 VmaAllocatorCreateInfo allocatorInfo = {};
767 // A QRhi is supposed to be used from one single thread only. Disable
768 // the allocator's own mutexes. This gives a performance boost.
769 allocatorInfo.flags = VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT;
770 allocatorInfo.physicalDevice = physDev;
771 allocatorInfo.device = dev;
772 allocatorInfo.pVulkanFunctions = &funcs;
773 allocatorInfo.instance = inst->vkInstance();
774 const QVersionNumber apiVer = inst->apiVersion();
775 if (!apiVer.isNull()) {
776 allocatorInfo.vulkanApiVersion = VK_MAKE_VERSION(apiVer.majorVersion(),
777 apiVer.minorVersion(),
778 apiVer.microVersion());
779 }
780 VmaAllocator vmaallocator;
781 VkResult err = vmaCreateAllocator(&allocatorInfo, &vmaallocator);
782 if (err != VK_SUCCESS) {
783 qWarning("Failed to create allocator: %d", err);
784 return false;
785 }
786 allocator = vmaallocator;
787 }
788
789 inst->installDebugOutputFilter(qvk_debug_filter);
790
791 VkDescriptorPool pool;
792 VkResult err = createDescriptorPool(&pool);
793 if (err == VK_SUCCESS)
794 descriptorPools.append(pool);
795 else
796 qWarning("Failed to create initial descriptor pool: %d", err);
797
798 VkQueryPoolCreateInfo timestampQueryPoolInfo = {};
799 timestampQueryPoolInfo.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
800 timestampQueryPoolInfo.queryType = VK_QUERY_TYPE_TIMESTAMP;
801 timestampQueryPoolInfo.queryCount = QVK_MAX_ACTIVE_TIMESTAMP_PAIRS * 2;
802 err = df->vkCreateQueryPool(dev, &timestampQueryPoolInfo, nullptr, &timestampQueryPool);
803 if (err != VK_SUCCESS) {
804 qWarning("Failed to create timestamp query pool: %d", err);
805 return false;
806 }
807 timestampQueryPoolMap.resize(QVK_MAX_ACTIVE_TIMESTAMP_PAIRS); // 1 bit per pair
808 timestampQueryPoolMap.fill(false);
809
810#ifdef VK_EXT_debug_utils
811 if (caps.debugUtils) {
812 vkSetDebugUtilsObjectNameEXT = reinterpret_cast<PFN_vkSetDebugUtilsObjectNameEXT>(f->vkGetDeviceProcAddr(dev, "vkSetDebugUtilsObjectNameEXT"));
813 vkCmdBeginDebugUtilsLabelEXT = reinterpret_cast<PFN_vkCmdBeginDebugUtilsLabelEXT>(f->vkGetDeviceProcAddr(dev, "vkCmdBeginDebugUtilsLabelEXT"));
814 vkCmdEndDebugUtilsLabelEXT = reinterpret_cast<PFN_vkCmdEndDebugUtilsLabelEXT>(f->vkGetDeviceProcAddr(dev, "vkCmdEndDebugUtilsLabelEXT"));
815 vkCmdInsertDebugUtilsLabelEXT = reinterpret_cast<PFN_vkCmdInsertDebugUtilsLabelEXT>(f->vkGetDeviceProcAddr(dev, "vkCmdInsertDebugUtilsLabelEXT"));
816 }
817#endif
818
819 deviceLost = false;
820
821 nativeHandlesStruct.physDev = physDev;
822 nativeHandlesStruct.dev = dev;
823 nativeHandlesStruct.gfxQueueFamilyIdx = gfxQueueFamilyIdx;
824 nativeHandlesStruct.gfxQueueIdx = gfxQueueIdx;
825 nativeHandlesStruct.gfxQueue = gfxQueue;
826 nativeHandlesStruct.vmemAllocator = allocator;
827 nativeHandlesStruct.inst = inst;
828
829 return true;
830}
831
833{
834 if (!df)
835 return;
836
837 if (!deviceLost)
838 df->vkDeviceWaitIdle(dev);
839
842
843 if (ofr.cmdFence) {
844 df->vkDestroyFence(dev, ofr.cmdFence, nullptr);
845 ofr.cmdFence = VK_NULL_HANDLE;
846 }
847
848 if (pipelineCache) {
849 df->vkDestroyPipelineCache(dev, pipelineCache, nullptr);
850 pipelineCache = VK_NULL_HANDLE;
851 }
852
854 df->vkDestroyDescriptorPool(dev, pool.pool, nullptr);
855
856 descriptorPools.clear();
857
858 if (timestampQueryPool) {
859 df->vkDestroyQueryPool(dev, timestampQueryPool, nullptr);
860 timestampQueryPool = VK_NULL_HANDLE;
861 }
862
864 vmaDestroyAllocator(toVmaAllocator(allocator));
865 allocator = nullptr;
866 }
867
868 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
869 if (cmdPool[i]) {
870 df->vkDestroyCommandPool(dev, cmdPool[i], nullptr);
871 cmdPool[i] = VK_NULL_HANDLE;
872 }
874 ofr.cbWrapper[i]->cb = VK_NULL_HANDLE;
875 }
876
877 if (!importedDevice && dev) {
878 df->vkDestroyDevice(dev, nullptr);
879 inst->resetDeviceFunctions(dev);
880 dev = VK_NULL_HANDLE;
881 }
882
883 f = nullptr;
884 df = nullptr;
885}
886
887VkResult QRhiVulkan::createDescriptorPool(VkDescriptorPool *pool)
888{
889 VkDescriptorPoolSize descPoolSizes[] = {
890 { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, QVK_UNIFORM_BUFFERS_PER_POOL },
891 { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, QVK_UNIFORM_BUFFERS_PER_POOL },
892 { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, QVK_COMBINED_IMAGE_SAMPLERS_PER_POOL },
893 { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, QVK_STORAGE_BUFFERS_PER_POOL },
894 { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, QVK_STORAGE_IMAGES_PER_POOL }
895 };
896 VkDescriptorPoolCreateInfo descPoolInfo = {};
897 descPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
898 // Do not enable vkFreeDescriptorSets - sets are never freed on their own
899 // (good so no trouble with fragmentation), they just deref their pool
900 // which is then reset at some point (or not).
901 descPoolInfo.flags = 0;
902 descPoolInfo.maxSets = QVK_DESC_SETS_PER_POOL;
903 descPoolInfo.poolSizeCount = sizeof(descPoolSizes) / sizeof(descPoolSizes[0]);
904 descPoolInfo.pPoolSizes = descPoolSizes;
905 return df->vkCreateDescriptorPool(dev, &descPoolInfo, nullptr, pool);
906}
907
908bool QRhiVulkan::allocateDescriptorSet(VkDescriptorSetAllocateInfo *allocInfo, VkDescriptorSet *result, int *resultPoolIndex)
909{
910 auto tryAllocate = [this, allocInfo, result](int poolIndex) {
911 allocInfo->descriptorPool = descriptorPools[poolIndex].pool;
912 VkResult r = df->vkAllocateDescriptorSets(dev, allocInfo, result);
913 if (r == VK_SUCCESS)
914 descriptorPools[poolIndex].refCount += 1;
915 return r;
916 };
917
918 int lastPoolIdx = descriptorPools.size() - 1;
919 for (int i = lastPoolIdx; i >= 0; --i) {
920 if (descriptorPools[i].refCount == 0) {
921 df->vkResetDescriptorPool(dev, descriptorPools[i].pool, 0);
922 descriptorPools[i].allocedDescSets = 0;
923 }
924 if (descriptorPools[i].allocedDescSets + int(allocInfo->descriptorSetCount) <= QVK_DESC_SETS_PER_POOL) {
925 VkResult err = tryAllocate(i);
926 if (err == VK_SUCCESS) {
927 descriptorPools[i].allocedDescSets += allocInfo->descriptorSetCount;
928 *resultPoolIndex = i;
929 return true;
930 }
931 }
932 }
933
934 VkDescriptorPool newPool;
935 VkResult poolErr = createDescriptorPool(&newPool);
936 if (poolErr == VK_SUCCESS) {
937 descriptorPools.append(newPool);
938 lastPoolIdx = descriptorPools.size() - 1;
939 VkResult err = tryAllocate(lastPoolIdx);
940 if (err != VK_SUCCESS) {
941 qWarning("Failed to allocate descriptor set from new pool too, giving up: %d", err);
942 return false;
943 }
944 descriptorPools[lastPoolIdx].allocedDescSets += allocInfo->descriptorSetCount;
945 *resultPoolIndex = lastPoolIdx;
946 return true;
947 } else {
948 qWarning("Failed to allocate new descriptor pool: %d", poolErr);
949 return false;
950 }
951}
952
953static inline VkFormat toVkTextureFormat(QRhiTexture::Format format, QRhiTexture::Flags flags)
954{
955 const bool srgb = flags.testFlag(QRhiTexture::sRGB);
956 switch (format) {
958 return srgb ? VK_FORMAT_R8G8B8A8_SRGB : VK_FORMAT_R8G8B8A8_UNORM;
960 return srgb ? VK_FORMAT_B8G8R8A8_SRGB : VK_FORMAT_B8G8R8A8_UNORM;
961 case QRhiTexture::R8:
962 return srgb ? VK_FORMAT_R8_SRGB : VK_FORMAT_R8_UNORM;
963 case QRhiTexture::RG8:
964 return srgb ? VK_FORMAT_R8G8_SRGB : VK_FORMAT_R8G8_UNORM;
965 case QRhiTexture::R16:
966 return VK_FORMAT_R16_UNORM;
968 return VK_FORMAT_R16G16_UNORM;
970 return VK_FORMAT_R8_UNORM;
971
973 return VK_FORMAT_R16G16B16A16_SFLOAT;
975 return VK_FORMAT_R32G32B32A32_SFLOAT;
977 return VK_FORMAT_R16_SFLOAT;
979 return VK_FORMAT_R32_SFLOAT;
980
982 // intentionally A2B10G10R10, not A2R10G10B10
983 return VK_FORMAT_A2B10G10R10_UNORM_PACK32;
984
985 case QRhiTexture::D16:
986 return VK_FORMAT_D16_UNORM;
987 case QRhiTexture::D24:
988 return VK_FORMAT_X8_D24_UNORM_PACK32;
990 return VK_FORMAT_D24_UNORM_S8_UINT;
992 return VK_FORMAT_D32_SFLOAT;
993
994 case QRhiTexture::BC1:
995 return srgb ? VK_FORMAT_BC1_RGB_SRGB_BLOCK : VK_FORMAT_BC1_RGB_UNORM_BLOCK;
996 case QRhiTexture::BC2:
997 return srgb ? VK_FORMAT_BC2_SRGB_BLOCK : VK_FORMAT_BC2_UNORM_BLOCK;
998 case QRhiTexture::BC3:
999 return srgb ? VK_FORMAT_BC3_SRGB_BLOCK : VK_FORMAT_BC3_UNORM_BLOCK;
1000 case QRhiTexture::BC4:
1001 return VK_FORMAT_BC4_UNORM_BLOCK;
1002 case QRhiTexture::BC5:
1003 return VK_FORMAT_BC5_UNORM_BLOCK;
1004 case QRhiTexture::BC6H:
1005 return VK_FORMAT_BC6H_UFLOAT_BLOCK;
1006 case QRhiTexture::BC7:
1007 return srgb ? VK_FORMAT_BC7_SRGB_BLOCK : VK_FORMAT_BC7_UNORM_BLOCK;
1008
1010 return srgb ? VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK : VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK;
1012 return srgb ? VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK : VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK;
1014 return srgb ? VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK : VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK;
1015
1017 return srgb ? VK_FORMAT_ASTC_4x4_SRGB_BLOCK : VK_FORMAT_ASTC_4x4_UNORM_BLOCK;
1019 return srgb ? VK_FORMAT_ASTC_5x4_SRGB_BLOCK : VK_FORMAT_ASTC_5x4_UNORM_BLOCK;
1021 return srgb ? VK_FORMAT_ASTC_5x5_SRGB_BLOCK : VK_FORMAT_ASTC_5x5_UNORM_BLOCK;
1023 return srgb ? VK_FORMAT_ASTC_6x5_SRGB_BLOCK : VK_FORMAT_ASTC_6x5_UNORM_BLOCK;
1025 return srgb ? VK_FORMAT_ASTC_6x6_SRGB_BLOCK : VK_FORMAT_ASTC_6x6_UNORM_BLOCK;
1027 return srgb ? VK_FORMAT_ASTC_8x5_SRGB_BLOCK : VK_FORMAT_ASTC_8x5_UNORM_BLOCK;
1029 return srgb ? VK_FORMAT_ASTC_8x6_SRGB_BLOCK : VK_FORMAT_ASTC_8x6_UNORM_BLOCK;
1031 return srgb ? VK_FORMAT_ASTC_8x8_SRGB_BLOCK : VK_FORMAT_ASTC_8x8_UNORM_BLOCK;
1033 return srgb ? VK_FORMAT_ASTC_10x5_SRGB_BLOCK : VK_FORMAT_ASTC_10x5_UNORM_BLOCK;
1035 return srgb ? VK_FORMAT_ASTC_10x6_SRGB_BLOCK : VK_FORMAT_ASTC_10x6_UNORM_BLOCK;
1037 return srgb ? VK_FORMAT_ASTC_10x8_SRGB_BLOCK : VK_FORMAT_ASTC_10x8_UNORM_BLOCK;
1039 return srgb ? VK_FORMAT_ASTC_10x10_SRGB_BLOCK : VK_FORMAT_ASTC_10x10_UNORM_BLOCK;
1041 return srgb ? VK_FORMAT_ASTC_12x10_SRGB_BLOCK : VK_FORMAT_ASTC_12x10_UNORM_BLOCK;
1043 return srgb ? VK_FORMAT_ASTC_12x12_SRGB_BLOCK : VK_FORMAT_ASTC_12x12_UNORM_BLOCK;
1044
1045 default:
1046 Q_UNREACHABLE_RETURN(VK_FORMAT_R8G8B8A8_UNORM);
1047 }
1048}
1049
1050static inline QRhiTexture::Format swapchainReadbackTextureFormat(VkFormat format, QRhiTexture::Flags *flags)
1051{
1052 switch (format) {
1053 case VK_FORMAT_R8G8B8A8_UNORM:
1054 return QRhiTexture::RGBA8;
1055 case VK_FORMAT_R8G8B8A8_SRGB:
1056 if (flags)
1057 (*flags) |= QRhiTexture::sRGB;
1058 return QRhiTexture::RGBA8;
1059 case VK_FORMAT_B8G8R8A8_UNORM:
1060 return QRhiTexture::BGRA8;
1061 case VK_FORMAT_B8G8R8A8_SRGB:
1062 if (flags)
1063 (*flags) |= QRhiTexture::sRGB;
1064 return QRhiTexture::BGRA8;
1065 case VK_FORMAT_R16G16B16A16_SFLOAT:
1066 return QRhiTexture::RGBA16F;
1067 case VK_FORMAT_R32G32B32A32_SFLOAT:
1068 return QRhiTexture::RGBA32F;
1069 case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
1070 return QRhiTexture::RGB10A2;
1071 default:
1072 qWarning("VkFormat %d cannot be read back", format);
1073 break;
1074 }
1076}
1077
1079{
1080 switch (format) {
1085 return true;
1086
1087 default:
1088 return false;
1089 }
1090}
1091
1092static constexpr inline VkImageAspectFlags aspectMaskForTextureFormat(QRhiTexture::Format format)
1093{
1094 return isDepthTextureFormat(format) ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT;
1095}
1096
1097// Transient images ("render buffers") backed by lazily allocated memory are
1098// managed manually without going through vk_mem_alloc since it does not offer
1099// any support for such images. This should be ok since in practice there
1100// should be very few of such images.
1101
1102uint32_t QRhiVulkan::chooseTransientImageMemType(VkImage img, uint32_t startIndex)
1103{
1104 VkPhysicalDeviceMemoryProperties physDevMemProps;
1105 f->vkGetPhysicalDeviceMemoryProperties(physDev, &physDevMemProps);
1106
1107 VkMemoryRequirements memReq;
1108 df->vkGetImageMemoryRequirements(dev, img, &memReq);
1109 uint32_t memTypeIndex = uint32_t(-1);
1110
1111 if (memReq.memoryTypeBits) {
1112 // Find a device local + lazily allocated, or at least device local memtype.
1113 const VkMemoryType *memType = physDevMemProps.memoryTypes;
1114 bool foundDevLocal = false;
1115 for (uint32_t i = startIndex; i < physDevMemProps.memoryTypeCount; ++i) {
1116 if (memReq.memoryTypeBits & (1 << i)) {
1117 if (memType[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) {
1118 if (!foundDevLocal) {
1119 foundDevLocal = true;
1120 memTypeIndex = i;
1121 }
1122 if (memType[i].propertyFlags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) {
1123 memTypeIndex = i;
1124 break;
1125 }
1126 }
1127 }
1128 }
1129 }
1130
1131 return memTypeIndex;
1132}
1133
1135 const QSize &pixelSize,
1136 VkImageUsageFlags usage,
1137 VkImageAspectFlags aspectMask,
1138 VkSampleCountFlagBits samples,
1139 VkDeviceMemory *mem,
1140 VkImage *images,
1141 VkImageView *views,
1142 int count)
1143{
1144 VkMemoryRequirements memReq;
1145 VkResult err;
1146
1147 for (int i = 0; i < count; ++i) {
1148 VkImageCreateInfo imgInfo = {};
1149 imgInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
1150 imgInfo.imageType = VK_IMAGE_TYPE_2D;
1151 imgInfo.format = format;
1152 imgInfo.extent.width = uint32_t(pixelSize.width());
1153 imgInfo.extent.height = uint32_t(pixelSize.height());
1154 imgInfo.extent.depth = 1;
1155 imgInfo.mipLevels = imgInfo.arrayLayers = 1;
1156 imgInfo.samples = samples;
1157 imgInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
1158 imgInfo.usage = usage | VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT;
1159 imgInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1160
1161 err = df->vkCreateImage(dev, &imgInfo, nullptr, images + i);
1162 if (err != VK_SUCCESS) {
1163 qWarning("Failed to create image: %d", err);
1164 return false;
1165 }
1166
1167 // Assume the reqs are the same since the images are same in every way.
1168 // Still, call GetImageMemReq for every image, in order to prevent the
1169 // validation layer from complaining.
1170 df->vkGetImageMemoryRequirements(dev, images[i], &memReq);
1171 }
1172
1173 VkMemoryAllocateInfo memInfo = {};
1174 memInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
1175 memInfo.allocationSize = aligned(memReq.size, memReq.alignment) * VkDeviceSize(count);
1176
1177 uint32_t startIndex = 0;
1178 do {
1179 memInfo.memoryTypeIndex = chooseTransientImageMemType(images[0], startIndex);
1180 if (memInfo.memoryTypeIndex == uint32_t(-1)) {
1181 qWarning("No suitable memory type found");
1182 return false;
1183 }
1184 startIndex = memInfo.memoryTypeIndex + 1;
1185 err = df->vkAllocateMemory(dev, &memInfo, nullptr, mem);
1186 if (err != VK_SUCCESS && err != VK_ERROR_OUT_OF_DEVICE_MEMORY) {
1187 qWarning("Failed to allocate image memory: %d", err);
1188 return false;
1189 }
1190 } while (err != VK_SUCCESS);
1191
1192 VkDeviceSize ofs = 0;
1193 for (int i = 0; i < count; ++i) {
1194 err = df->vkBindImageMemory(dev, images[i], *mem, ofs);
1195 if (err != VK_SUCCESS) {
1196 qWarning("Failed to bind image memory: %d", err);
1197 return false;
1198 }
1199 ofs += aligned(memReq.size, memReq.alignment);
1200
1201 VkImageViewCreateInfo imgViewInfo = {};
1202 imgViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
1203 imgViewInfo.image = images[i];
1204 imgViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
1205 imgViewInfo.format = format;
1206 imgViewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
1207 imgViewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
1208 imgViewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
1209 imgViewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
1210 imgViewInfo.subresourceRange.aspectMask = aspectMask;
1211 imgViewInfo.subresourceRange.levelCount = imgViewInfo.subresourceRange.layerCount = 1;
1212
1213 err = df->vkCreateImageView(dev, &imgViewInfo, nullptr, views + i);
1214 if (err != VK_SUCCESS) {
1215 qWarning("Failed to create image view: %d", err);
1216 return false;
1217 }
1218 }
1219
1220 return true;
1221}
1222
1224{
1225 if (optimalDsFormat != VK_FORMAT_UNDEFINED)
1226 return optimalDsFormat;
1227
1228 const VkFormat dsFormatCandidates[] = {
1229 VK_FORMAT_D24_UNORM_S8_UINT,
1230 VK_FORMAT_D32_SFLOAT_S8_UINT,
1231 VK_FORMAT_D16_UNORM_S8_UINT
1232 };
1233 const int dsFormatCandidateCount = sizeof(dsFormatCandidates) / sizeof(VkFormat);
1234 int dsFormatIdx = 0;
1235 while (dsFormatIdx < dsFormatCandidateCount) {
1236 optimalDsFormat = dsFormatCandidates[dsFormatIdx];
1237 VkFormatProperties fmtProp;
1238 f->vkGetPhysicalDeviceFormatProperties(physDev, optimalDsFormat, &fmtProp);
1239 if (fmtProp.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)
1240 break;
1241 ++dsFormatIdx;
1242 }
1243 if (dsFormatIdx == dsFormatCandidateCount)
1244 qWarning("Failed to find an optimal depth-stencil format");
1245
1246 return optimalDsFormat;
1247}
1248
1249static void fillRenderPassCreateInfo(VkRenderPassCreateInfo *rpInfo,
1250 VkSubpassDescription *subpassDesc,
1252{
1253 memset(subpassDesc, 0, sizeof(VkSubpassDescription));
1254 subpassDesc->pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
1255 subpassDesc->colorAttachmentCount = uint32_t(rpD->colorRefs.size());
1256 subpassDesc->pColorAttachments = !rpD->colorRefs.isEmpty() ? rpD->colorRefs.constData() : nullptr;
1257 subpassDesc->pDepthStencilAttachment = rpD->hasDepthStencil ? &rpD->dsRef : nullptr;
1258 subpassDesc->pResolveAttachments = !rpD->resolveRefs.isEmpty() ? rpD->resolveRefs.constData() : nullptr;
1259
1260 memset(rpInfo, 0, sizeof(VkRenderPassCreateInfo));
1261 rpInfo->sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
1262 rpInfo->attachmentCount = uint32_t(rpD->attDescs.size());
1263 rpInfo->pAttachments = rpD->attDescs.constData();
1264 rpInfo->subpassCount = 1;
1265 rpInfo->pSubpasses = subpassDesc;
1266 rpInfo->dependencyCount = uint32_t(rpD->subpassDeps.size());
1267 rpInfo->pDependencies = !rpD->subpassDeps.isEmpty() ? rpD->subpassDeps.constData() : nullptr;
1268}
1269
1270bool QRhiVulkan::createDefaultRenderPass(QVkRenderPassDescriptor *rpD, bool hasDepthStencil, VkSampleCountFlagBits samples, VkFormat colorFormat)
1271{
1272 // attachment list layout is color (1), ds (0-1), resolve (0-1)
1273
1274 VkAttachmentDescription attDesc = {};
1275 attDesc.format = colorFormat;
1276 attDesc.samples = samples;
1277 attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1278 attDesc.storeOp = samples > VK_SAMPLE_COUNT_1_BIT ? VK_ATTACHMENT_STORE_OP_DONT_CARE : VK_ATTACHMENT_STORE_OP_STORE;
1279 attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1280 attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1281 attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1282 attDesc.finalLayout = samples > VK_SAMPLE_COUNT_1_BIT ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
1283 rpD->attDescs.append(attDesc);
1284
1285 rpD->colorRefs.append({ 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL });
1286
1287 rpD->hasDepthStencil = hasDepthStencil;
1288 rpD->multiViewCount = 0;
1289
1290 if (hasDepthStencil) {
1291 // clear on load + no store + lazy alloc + transient image should play
1292 // nicely with tiled GPUs (no physical backing necessary for ds buffer)
1293 memset(&attDesc, 0, sizeof(attDesc));
1294 attDesc.format = optimalDepthStencilFormat();
1295 attDesc.samples = samples;
1296 attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1297 attDesc.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1298 attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1299 attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1300 attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1301 attDesc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
1302 rpD->attDescs.append(attDesc);
1303
1304 rpD->dsRef = { 1, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
1305 }
1306
1307 if (samples > VK_SAMPLE_COUNT_1_BIT) {
1308 memset(&attDesc, 0, sizeof(attDesc));
1309 attDesc.format = colorFormat;
1310 attDesc.samples = VK_SAMPLE_COUNT_1_BIT;
1311 attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1312 attDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
1313 attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1314 attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1315 attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1316 attDesc.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
1317 rpD->attDescs.append(attDesc);
1318
1319 rpD->resolveRefs.append({ 2, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL });
1320 }
1321
1322 // Replace the first implicit dep (TOP_OF_PIPE / ALL_COMMANDS) with our own.
1323 VkSubpassDependency subpassDep = {};
1324 subpassDep.srcSubpass = VK_SUBPASS_EXTERNAL;
1325 subpassDep.dstSubpass = 0;
1326 subpassDep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
1327 subpassDep.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
1328 subpassDep.srcAccessMask = 0;
1329 subpassDep.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
1330 rpD->subpassDeps.append(subpassDep);
1331 if (hasDepthStencil) {
1332 memset(&subpassDep, 0, sizeof(subpassDep));
1333 subpassDep.srcSubpass = VK_SUBPASS_EXTERNAL;
1334 subpassDep.dstSubpass = 0;
1335 subpassDep.srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT
1336 | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
1337 subpassDep.dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT
1338 | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
1339 subpassDep.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
1340 subpassDep.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT
1341 | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
1342 rpD->subpassDeps.append(subpassDep);
1343 }
1344
1345 VkRenderPassCreateInfo rpInfo;
1346 VkSubpassDescription subpassDesc;
1347 fillRenderPassCreateInfo(&rpInfo, &subpassDesc, rpD);
1348
1349 VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, &rpD->rp);
1350 if (err != VK_SUCCESS) {
1351 qWarning("Failed to create renderpass: %d", err);
1352 return false;
1353 }
1354
1355 return true;
1356}
1357
1359{
1360 bool prepare(VkRenderPassCreateInfo *rpInfo, int multiViewCount, bool multiViewCap)
1361 {
1362 if (multiViewCount < 2)
1363 return true;
1364 if (!multiViewCap) {
1365 qWarning("Cannot create multiview render pass without support for the Vulkan 1.1 multiview feature");
1366 return false;
1367 }
1368#ifdef VK_VERSION_1_1
1369 uint32_t allViewsMask = 0;
1370 for (uint32_t i = 0; i < uint32_t(multiViewCount); ++i)
1371 allViewsMask |= (1 << i);
1372 multiViewMask = allViewsMask;
1373 multiViewCorrelationMask = allViewsMask;
1374 multiViewInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO;
1375 multiViewInfo.subpassCount = 1;
1376 multiViewInfo.pViewMasks = &multiViewMask;
1377 multiViewInfo.correlationMaskCount = 1;
1378 multiViewInfo.pCorrelationMasks = &multiViewCorrelationMask;
1379 rpInfo->pNext = &multiViewInfo;
1380#endif
1381 return true;
1382 }
1383
1384#ifdef VK_VERSION_1_1
1385 VkRenderPassMultiviewCreateInfo multiViewInfo = {};
1386 uint32_t multiViewMask = 0;
1387 uint32_t multiViewCorrelationMask = 0;
1388#endif
1389};
1390
1392 const QRhiColorAttachment *firstColorAttachment,
1393 const QRhiColorAttachment *lastColorAttachment,
1394 bool preserveColor,
1395 bool preserveDs,
1396 QRhiRenderBuffer *depthStencilBuffer,
1397 QRhiTexture *depthTexture)
1398{
1399 // attachment list layout is color (0-8), ds (0-1), resolve (0-8)
1400
1401 int multiViewCount = 0;
1402 for (auto it = firstColorAttachment; it != lastColorAttachment; ++it) {
1403 QVkTexture *texD = QRHI_RES(QVkTexture, it->texture());
1404 QVkRenderBuffer *rbD = QRHI_RES(QVkRenderBuffer, it->renderBuffer());
1405 Q_ASSERT(texD || rbD);
1406 const VkFormat vkformat = texD ? texD->vkformat : rbD->vkformat;
1407 const VkSampleCountFlagBits samples = texD ? texD->samples : rbD->samples;
1408
1409 VkAttachmentDescription attDesc = {};
1410 attDesc.format = vkformat;
1411 attDesc.samples = samples;
1412 attDesc.loadOp = preserveColor ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_CLEAR;
1413 attDesc.storeOp = it->resolveTexture() ? VK_ATTACHMENT_STORE_OP_DONT_CARE : VK_ATTACHMENT_STORE_OP_STORE;
1414 attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1415 attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1416 // this has to interact correctly with activateTextureRenderTarget(), hence leaving in COLOR_ATT
1417 attDesc.initialLayout = preserveColor ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_UNDEFINED;
1418 attDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
1419 rpD->attDescs.append(attDesc);
1420
1421 const VkAttachmentReference ref = { uint32_t(rpD->attDescs.size() - 1), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
1422 rpD->colorRefs.append(ref);
1423
1424 if (it->multiViewCount() >= 2) {
1425 if (multiViewCount > 0 && multiViewCount != it->multiViewCount())
1426 qWarning("Inconsistent multiViewCount in color attachment set");
1427 else
1428 multiViewCount = it->multiViewCount();
1429 } else if (multiViewCount > 0) {
1430 qWarning("Mixing non-multiview color attachments within a multiview render pass");
1431 }
1432 }
1433 Q_ASSERT(multiViewCount == 0 || multiViewCount >= 2);
1434 rpD->multiViewCount = uint32_t(multiViewCount);
1435
1436 rpD->hasDepthStencil = depthStencilBuffer || depthTexture;
1437 if (rpD->hasDepthStencil) {
1438 const VkFormat dsFormat = depthTexture ? QRHI_RES(QVkTexture, depthTexture)->vkformat
1439 : QRHI_RES(QVkRenderBuffer, depthStencilBuffer)->vkformat;
1440 const VkSampleCountFlagBits samples = depthTexture ? QRHI_RES(QVkTexture, depthTexture)->samples
1441 : QRHI_RES(QVkRenderBuffer, depthStencilBuffer)->samples;
1442 const VkAttachmentLoadOp loadOp = preserveDs ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_CLEAR;
1443 const VkAttachmentStoreOp storeOp = depthTexture ? VK_ATTACHMENT_STORE_OP_STORE : VK_ATTACHMENT_STORE_OP_DONT_CARE;
1444 VkAttachmentDescription attDesc = {};
1445 attDesc.format = dsFormat;
1446 attDesc.samples = samples;
1447 attDesc.loadOp = loadOp;
1448 attDesc.storeOp = storeOp;
1449 attDesc.stencilLoadOp = loadOp;
1450 attDesc.stencilStoreOp = storeOp;
1451 attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1452 attDesc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
1453 rpD->attDescs.append(attDesc);
1454 }
1455 rpD->dsRef = { uint32_t(rpD->attDescs.size() - 1), VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
1456
1457 for (auto it = firstColorAttachment; it != lastColorAttachment; ++it) {
1458 if (it->resolveTexture()) {
1459 QVkTexture *rtexD = QRHI_RES(QVkTexture, it->resolveTexture());
1460 const VkFormat dstFormat = rtexD->vkformat;
1461 if (rtexD->samples > VK_SAMPLE_COUNT_1_BIT)
1462 qWarning("Resolving into a multisample texture is not supported");
1463
1464 QVkTexture *texD = QRHI_RES(QVkTexture, it->texture());
1465 QVkRenderBuffer *rbD = QRHI_RES(QVkRenderBuffer, it->renderBuffer());
1466 const VkFormat srcFormat = texD ? texD->vkformat : rbD->vkformat;
1467 if (srcFormat != dstFormat) {
1468 // This is a validation error. But some implementations survive,
1469 // actually. Warn about it however, because it's an error with
1470 // some other backends (like D3D) as well.
1471 qWarning("Multisample resolve between different formats (%d and %d) is not supported.",
1472 int(srcFormat), int(dstFormat));
1473 }
1474
1475 VkAttachmentDescription attDesc = {};
1476 attDesc.format = dstFormat;
1477 attDesc.samples = VK_SAMPLE_COUNT_1_BIT;
1478 attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; // ignored
1479 attDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
1480 attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1481 attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1482 attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1483 attDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
1484 rpD->attDescs.append(attDesc);
1485
1486 const VkAttachmentReference ref = { uint32_t(rpD->attDescs.size() - 1), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
1487 rpD->resolveRefs.append(ref);
1488 } else {
1489 const VkAttachmentReference ref = { VK_ATTACHMENT_UNUSED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
1490 rpD->resolveRefs.append(ref);
1491 }
1492 }
1493 Q_ASSERT(rpD->colorRefs.size() == rpD->resolveRefs.size());
1494
1495 // rpD->subpassDeps stays empty: don't yet know the correct initial/final
1496 // access and stage stuff for the implicit deps at this point, so leave it
1497 // to the resource tracking and activateTextureRenderTarget() to generate
1498 // barriers.
1499
1500 VkRenderPassCreateInfo rpInfo;
1501 VkSubpassDescription subpassDesc;
1502 fillRenderPassCreateInfo(&rpInfo, &subpassDesc, rpD);
1503
1504 MultiViewRenderPassSetupHelper multiViewHelper;
1505 if (!multiViewHelper.prepare(&rpInfo, multiViewCount, caps.multiView))
1506 return false;
1507
1508 VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, &rpD->rp);
1509 if (err != VK_SUCCESS) {
1510 qWarning("Failed to create renderpass: %d", err);
1511 return false;
1512 }
1513
1514 return true;
1515}
1516
1518{
1519 QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain);
1520 if (swapChainD->pixelSize.isEmpty()) {
1521 qWarning("Surface size is 0, cannot create swapchain");
1522 return false;
1523 }
1524
1525 df->vkDeviceWaitIdle(dev);
1526
1527 if (!vkCreateSwapchainKHR) {
1528 vkCreateSwapchainKHR = reinterpret_cast<PFN_vkCreateSwapchainKHR>(f->vkGetDeviceProcAddr(dev, "vkCreateSwapchainKHR"));
1529 vkDestroySwapchainKHR = reinterpret_cast<PFN_vkDestroySwapchainKHR>(f->vkGetDeviceProcAddr(dev, "vkDestroySwapchainKHR"));
1530 vkGetSwapchainImagesKHR = reinterpret_cast<PFN_vkGetSwapchainImagesKHR>(f->vkGetDeviceProcAddr(dev, "vkGetSwapchainImagesKHR"));
1531 vkAcquireNextImageKHR = reinterpret_cast<PFN_vkAcquireNextImageKHR>(f->vkGetDeviceProcAddr(dev, "vkAcquireNextImageKHR"));
1532 vkQueuePresentKHR = reinterpret_cast<PFN_vkQueuePresentKHR>(f->vkGetDeviceProcAddr(dev, "vkQueuePresentKHR"));
1534 qWarning("Swapchain functions not available");
1535 return false;
1536 }
1537 }
1538
1539 VkSurfaceCapabilitiesKHR surfaceCaps;
1540 vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physDev, swapChainD->surface, &surfaceCaps);
1541 quint32 reqBufferCount;
1542 if (swapChainD->m_flags.testFlag(QRhiSwapChain::MinimalBufferCount) || surfaceCaps.maxImageCount == 0) {
1543 reqBufferCount = qMax<quint32>(2, surfaceCaps.minImageCount);
1544 } else {
1545 reqBufferCount = qMax(qMin<quint32>(surfaceCaps.maxImageCount, 3), surfaceCaps.minImageCount);
1546 }
1547 VkSurfaceTransformFlagBitsKHR preTransform =
1548 (surfaceCaps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)
1549 ? VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR
1550 : surfaceCaps.currentTransform;
1551
1552 // This looks odd but matches how platforms work in practice.
1553 //
1554 // On Windows with NVIDIA for example, the only supportedCompositeAlpha
1555 // value reported is OPAQUE, nothing else. Yet transparency works
1556 // regardless, as long as the native window is set up correctly, so that's
1557 // not something we need to handle here.
1558 //
1559 // On Linux with Intel and Mesa and running on xcb reports, on one
1560 // particular system, INHERIT+PRE_MULTIPLIED. Tranparency works, regardless,
1561 // presumably due to setting INHERIT.
1562 //
1563 // On the same setup with Wayland instead of xcb we see
1564 // OPAQUE+PRE_MULTIPLIED reported. Here transparency won't work unless
1565 // PRE_MULTIPLIED is set.
1566 //
1567 // Therefore our rules are:
1568 // - Prefer INHERIT over OPAQUE.
1569 // - Then based on the request, try the requested alpha mode, but if
1570 // that's not reported as supported, try also the other (PRE/POST,
1571 // POST/PRE) as that is better than nothing. This is not different from
1572 // some other backends, e.g. D3D11 with DirectComposition there is also
1573 // no control over being straight or pre-multiplied. Whereas with
1574 // WGL/GLX/EGL we never had that sort of control.
1575
1576 VkCompositeAlphaFlagBitsKHR compositeAlpha =
1577 (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR)
1578 ? VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR
1579 : VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
1580
1581 if (swapChainD->m_flags.testFlag(QRhiSwapChain::SurfaceHasPreMulAlpha)) {
1582 if (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR)
1583 compositeAlpha = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;
1584 else if (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR)
1585 compositeAlpha = VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR;
1586 } else if (swapChainD->m_flags.testFlag(QRhiSwapChain::SurfaceHasNonPreMulAlpha)) {
1587 if (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR)
1588 compositeAlpha = VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR;
1589 else if (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR)
1590 compositeAlpha = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;
1591 }
1592
1593 VkImageUsageFlags usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
1594 swapChainD->supportsReadback = (surfaceCaps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
1595 if (swapChainD->supportsReadback && swapChainD->m_flags.testFlag(QRhiSwapChain::UsedAsTransferSource))
1596 usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
1597
1598 VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR;
1599 if (swapChainD->m_flags.testFlag(QRhiSwapChain::NoVSync)) {
1600 if (swapChainD->supportedPresentationModes.contains(VK_PRESENT_MODE_MAILBOX_KHR))
1601 presentMode = VK_PRESENT_MODE_MAILBOX_KHR;
1602 else if (swapChainD->supportedPresentationModes.contains(VK_PRESENT_MODE_IMMEDIATE_KHR))
1603 presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
1604 }
1605
1606 // If the surface is different than before, then passing in the old
1607 // swapchain associated with the old surface can fail the swapchain
1608 // creation. (for example, Android loses the surface when backgrounding and
1609 // restoring applications, and it also enforces failing swapchain creation
1610 // with VK_ERROR_NATIVE_WINDOW_IN_USE_KHR if the old swapchain is provided)
1611 const bool reuseExisting = swapChainD->sc && swapChainD->lastConnectedSurface == swapChainD->surface;
1612
1613 qCDebug(QRHI_LOG_INFO, "Creating %s swapchain of %u buffers, size %dx%d, presentation mode %d",
1614 reuseExisting ? "recycled" : "new",
1615 reqBufferCount, swapChainD->pixelSize.width(), swapChainD->pixelSize.height(), presentMode);
1616
1617 VkSwapchainCreateInfoKHR swapChainInfo = {};
1618 swapChainInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
1619 swapChainInfo.surface = swapChainD->surface;
1620 swapChainInfo.minImageCount = reqBufferCount;
1621 swapChainInfo.imageFormat = swapChainD->colorFormat;
1622 swapChainInfo.imageColorSpace = swapChainD->colorSpace;
1623 swapChainInfo.imageExtent = VkExtent2D { uint32_t(swapChainD->pixelSize.width()), uint32_t(swapChainD->pixelSize.height()) };
1624 swapChainInfo.imageArrayLayers = 1;
1625 swapChainInfo.imageUsage = usage;
1626 swapChainInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
1627 swapChainInfo.preTransform = preTransform;
1628 swapChainInfo.compositeAlpha = compositeAlpha;
1629 swapChainInfo.presentMode = presentMode;
1630 swapChainInfo.clipped = true;
1631 swapChainInfo.oldSwapchain = reuseExisting ? swapChainD->sc : VK_NULL_HANDLE;
1632
1633 VkSwapchainKHR newSwapChain;
1634 VkResult err = vkCreateSwapchainKHR(dev, &swapChainInfo, nullptr, &newSwapChain);
1635 if (err != VK_SUCCESS) {
1636 qWarning("Failed to create swapchain: %d", err);
1637 return false;
1638 }
1639
1640 if (swapChainD->sc)
1641 releaseSwapChainResources(swapChain);
1642
1643 swapChainD->sc = newSwapChain;
1644 swapChainD->lastConnectedSurface = swapChainD->surface;
1645
1646 quint32 actualSwapChainBufferCount = 0;
1647 err = vkGetSwapchainImagesKHR(dev, swapChainD->sc, &actualSwapChainBufferCount, nullptr);
1648 if (err != VK_SUCCESS || actualSwapChainBufferCount == 0) {
1649 qWarning("Failed to get swapchain images: %d", err);
1650 return false;
1651 }
1652
1653 if (actualSwapChainBufferCount != reqBufferCount)
1654 qCDebug(QRHI_LOG_INFO, "Actual swapchain buffer count is %u", actualSwapChainBufferCount);
1655 swapChainD->bufferCount = int(actualSwapChainBufferCount);
1656
1657 QVarLengthArray<VkImage, QVkSwapChain::EXPECTED_MAX_BUFFER_COUNT> swapChainImages(actualSwapChainBufferCount);
1658 err = vkGetSwapchainImagesKHR(dev, swapChainD->sc, &actualSwapChainBufferCount, swapChainImages.data());
1659 if (err != VK_SUCCESS) {
1660 qWarning("Failed to get swapchain images: %d", err);
1661 return false;
1662 }
1663
1666 if (swapChainD->samples > VK_SAMPLE_COUNT_1_BIT) {
1667 if (!createTransientImage(swapChainD->colorFormat,
1668 swapChainD->pixelSize,
1669 VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
1670 VK_IMAGE_ASPECT_COLOR_BIT,
1671 swapChainD->samples,
1672 &swapChainD->msaaImageMem,
1673 msaaImages.data(),
1674 msaaViews.data(),
1675 swapChainD->bufferCount))
1676 {
1677 qWarning("Failed to create transient image for MSAA color buffer");
1678 return false;
1679 }
1680 }
1681
1682 VkFenceCreateInfo fenceInfo = {};
1683 fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
1684 fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
1685
1686 swapChainD->imageRes.resize(swapChainD->bufferCount);
1687 for (int i = 0; i < swapChainD->bufferCount; ++i) {
1689 image.image = swapChainImages[i];
1690 if (swapChainD->samples > VK_SAMPLE_COUNT_1_BIT) {
1691 image.msaaImage = msaaImages[i];
1692 image.msaaImageView = msaaViews[i];
1693 }
1694
1695 VkImageViewCreateInfo imgViewInfo = {};
1696 imgViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
1697 imgViewInfo.image = swapChainImages[i];
1698 imgViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
1699 imgViewInfo.format = swapChainD->colorFormat;
1700 imgViewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
1701 imgViewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
1702 imgViewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
1703 imgViewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
1704 imgViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1705 imgViewInfo.subresourceRange.levelCount = imgViewInfo.subresourceRange.layerCount = 1;
1706 err = df->vkCreateImageView(dev, &imgViewInfo, nullptr, &image.imageView);
1707 if (err != VK_SUCCESS) {
1708 qWarning("Failed to create swapchain image view %d: %d", i, err);
1709 return false;
1710 }
1711
1713 }
1714
1715 swapChainD->currentImageIndex = 0;
1716
1717 VkSemaphoreCreateInfo semInfo = {};
1718 semInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
1719
1720 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
1722
1723 frame.imageAcquired = false;
1724 frame.imageSemWaitable = false;
1725
1726 df->vkCreateFence(dev, &fenceInfo, nullptr, &frame.imageFence);
1727 frame.imageFenceWaitable = true; // fence was created in signaled state
1728
1729 df->vkCreateSemaphore(dev, &semInfo, nullptr, &frame.imageSem);
1730 df->vkCreateSemaphore(dev, &semInfo, nullptr, &frame.drawSem);
1731
1732 err = df->vkCreateFence(dev, &fenceInfo, nullptr, &frame.cmdFence);
1733 if (err != VK_SUCCESS) {
1734 qWarning("Failed to create command buffer fence: %d", err);
1735 return false;
1736 }
1737 frame.cmdFenceWaitable = true; // fence was created in signaled state
1738 }
1739
1740 swapChainD->currentFrameSlot = 0;
1741
1742 return true;
1743}
1744
1746{
1747 QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain);
1748
1749 if (swapChainD->sc == VK_NULL_HANDLE)
1750 return;
1751
1752 if (!deviceLost)
1753 df->vkDeviceWaitIdle(dev);
1754
1755 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
1757 if (frame.cmdFence) {
1758 if (frame.cmdFenceWaitable)
1759 df->vkWaitForFences(dev, 1, &frame.cmdFence, VK_TRUE, UINT64_MAX);
1760 df->vkDestroyFence(dev, frame.cmdFence, nullptr);
1761 frame.cmdFence = VK_NULL_HANDLE;
1762 frame.cmdFenceWaitable = false;
1763 }
1764 if (frame.imageFence) {
1765 if (frame.imageFenceWaitable)
1766 df->vkWaitForFences(dev, 1, &frame.imageFence, VK_TRUE, UINT64_MAX);
1767 df->vkDestroyFence(dev, frame.imageFence, nullptr);
1768 frame.imageFence = VK_NULL_HANDLE;
1769 frame.imageFenceWaitable = false;
1770 }
1771 if (frame.imageSem) {
1772 df->vkDestroySemaphore(dev, frame.imageSem, nullptr);
1773 frame.imageSem = VK_NULL_HANDLE;
1774 }
1775 if (frame.drawSem) {
1776 df->vkDestroySemaphore(dev, frame.drawSem, nullptr);
1777 frame.drawSem = VK_NULL_HANDLE;
1778 }
1779 }
1780
1781 for (int i = 0; i < swapChainD->bufferCount; ++i) {
1783 if (image.fb) {
1784 df->vkDestroyFramebuffer(dev, image.fb, nullptr);
1785 image.fb = VK_NULL_HANDLE;
1786 }
1787 if (image.imageView) {
1788 df->vkDestroyImageView(dev, image.imageView, nullptr);
1789 image.imageView = VK_NULL_HANDLE;
1790 }
1791 if (image.msaaImageView) {
1792 df->vkDestroyImageView(dev, image.msaaImageView, nullptr);
1793 image.msaaImageView = VK_NULL_HANDLE;
1794 }
1795 if (image.msaaImage) {
1796 df->vkDestroyImage(dev, image.msaaImage, nullptr);
1797 image.msaaImage = VK_NULL_HANDLE;
1798 }
1799 }
1800
1801 if (swapChainD->msaaImageMem) {
1802 df->vkFreeMemory(dev, swapChainD->msaaImageMem, nullptr);
1803 swapChainD->msaaImageMem = VK_NULL_HANDLE;
1804 }
1805
1806 vkDestroySwapchainKHR(dev, swapChainD->sc, nullptr);
1807 swapChainD->sc = VK_NULL_HANDLE;
1808
1809 // NB! surface and similar must remain intact
1810}
1811
1813{
1814 VkCommandPoolResetFlags flags = 0;
1815
1816 // While not clear what "recycles all of the resources from the command
1817 // pool back to the system" really means in practice, set it when there was
1818 // a call to releaseCachedResources() recently.
1820 flags |= VK_COMMAND_POOL_RESET_RELEASE_RESOURCES_BIT;
1821
1822 // put all command buffers allocated from this slot's pool to initial state
1823 df->vkResetCommandPool(dev, cmdPool[currentFrameSlot], flags);
1824}
1825
1827{
1828 quint64 mask = 0;
1829 for (quint64 i = 0; i < timestampValidBits; i += 8)
1830 mask |= 0xFFULL << i;
1831 const quint64 ts0 = timestamp[0] & mask;
1832 const quint64 ts1 = timestamp[1] & mask;
1833 const float nsecsPerTick = physDevProperties.limits.timestampPeriod;
1834 if (!qFuzzyIsNull(nsecsPerTick)) {
1835 const float elapsedMs = float(ts1 - ts0) * nsecsPerTick / 1000000.0f;
1836 const double elapsedSec = elapsedMs / 1000.0;
1837 *ok = true;
1838 return elapsedSec;
1839 }
1840 *ok = false;
1841 return 0;
1842}
1843
1845{
1846 QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain);
1847 const int frameResIndex = swapChainD->bufferCount > 1 ? swapChainD->currentFrameSlot : 0;
1848 QVkSwapChain::FrameResources &frame(swapChainD->frameRes[frameResIndex]);
1849
1850 inst->handle()->beginFrame(swapChainD->window);
1851
1852 if (!frame.imageAcquired) {
1853 // Wait if we are too far ahead, i.e. the thread gets throttled based on the presentation rate
1854 // (note that we are using FIFO mode -> vsync)
1855 if (frame.imageFenceWaitable) {
1856 df->vkWaitForFences(dev, 1, &frame.imageFence, VK_TRUE, UINT64_MAX);
1857 df->vkResetFences(dev, 1, &frame.imageFence);
1858 frame.imageFenceWaitable = false;
1859 }
1860
1861 // move on to next swapchain image
1862 uint32_t imageIndex = 0;
1863 VkResult err = vkAcquireNextImageKHR(dev, swapChainD->sc, UINT64_MAX,
1864 frame.imageSem, frame.imageFence, &imageIndex);
1865 if (err == VK_SUCCESS || err == VK_SUBOPTIMAL_KHR) {
1866 swapChainD->currentImageIndex = imageIndex;
1867 frame.imageSemWaitable = true;
1868 frame.imageAcquired = true;
1869 frame.imageFenceWaitable = true;
1870 } else if (err == VK_ERROR_OUT_OF_DATE_KHR) {
1872 } else {
1873 if (err == VK_ERROR_DEVICE_LOST) {
1874 qWarning("Device loss detected in vkAcquireNextImageKHR()");
1875 deviceLost = true;
1877 }
1878 qWarning("Failed to acquire next swapchain image: %d", err);
1879 return QRhi::FrameOpError;
1880 }
1881 }
1882
1883 // Make sure the previous commands for the same image have finished. (note
1884 // that this is based on the fence from the command buffer submit, nothing
1885 // to do with the Present)
1886 //
1887 // Do this also for any other swapchain's commands with the same frame slot
1888 // While this reduces concurrency, it keeps resource usage safe: swapchain
1889 // A starting its frame 0, followed by swapchain B starting its own frame 0
1890 // will make B wait for A's frame 0 commands, so if a resource is written
1891 // in B's frame or when B checks for pending resource releases, that won't
1892 // mess up A's in-flight commands (as they are not in flight anymore).
1893 waitCommandCompletion(frameResIndex);
1894
1895 currentFrameSlot = int(swapChainD->currentFrameSlot);
1896 currentSwapChain = swapChainD;
1897 if (swapChainD->ds)
1899
1900 // reset the command pool
1902
1903 // start recording to this frame's command buffer
1905 if (cbres != QRhi::FrameOpSuccess)
1906 return cbres;
1907
1908 swapChainD->cbWrapper.cb = frame.cmdBuf;
1909
1911 swapChainD->rtWrapper.d.fb = image.fb;
1912
1913 prepareNewFrame(&swapChainD->cbWrapper);
1914
1915 // Read the timestamps for the previous frame for this slot.
1916 if (frame.timestampQueryIndex >= 0) {
1917 quint64 timestamp[2] = { 0, 0 };
1918 VkResult err = df->vkGetQueryPoolResults(dev, timestampQueryPool, uint32_t(frame.timestampQueryIndex), 2,
1919 2 * sizeof(quint64), timestamp, sizeof(quint64),
1920 VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT);
1921 timestampQueryPoolMap.clearBit(frame.timestampQueryIndex / 2);
1922 frame.timestampQueryIndex = -1;
1923 if (err == VK_SUCCESS) {
1924 bool ok = false;
1925 const double elapsedSec = elapsedSecondsFromTimestamp(timestamp, &ok);
1926 if (ok)
1927 swapChainD->cbWrapper.lastGpuTime = elapsedSec;
1928 } else {
1929 qWarning("Failed to query timestamp: %d", err);
1930 }
1931 }
1932
1933 // No timestamps if the client did not opt in, or when not having at least 2 frames in flight.
1934 if (rhiFlags.testFlag(QRhi::EnableTimestamps) && swapChainD->bufferCount > 1) {
1935 int timestampQueryIdx = -1;
1936 for (int i = 0; i < timestampQueryPoolMap.size(); ++i) {
1939 timestampQueryIdx = i * 2;
1940 break;
1941 }
1942 }
1943 if (timestampQueryIdx >= 0) {
1944 df->vkCmdResetQueryPool(frame.cmdBuf, timestampQueryPool, uint32_t(timestampQueryIdx), 2);
1945 // record timestamp at the start of the command buffer
1946 df->vkCmdWriteTimestamp(frame.cmdBuf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
1947 timestampQueryPool, uint32_t(timestampQueryIdx));
1948 frame.timestampQueryIndex = timestampQueryIdx;
1949 }
1950 }
1951
1952 return QRhi::FrameOpSuccess;
1953}
1954
1956{
1957 QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain);
1958 Q_ASSERT(currentSwapChain == swapChainD);
1959
1960 auto cleanup = qScopeGuard([this, swapChainD] {
1961 inst->handle()->endFrame(swapChainD->window);
1962 });
1963
1965
1966 int frameResIndex = swapChainD->bufferCount > 1 ? swapChainD->currentFrameSlot : 0;
1967 QVkSwapChain::FrameResources &frame(swapChainD->frameRes[frameResIndex]);
1969
1971 VkImageMemoryBarrier presTrans = {};
1972 presTrans.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
1973 presTrans.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
1974 presTrans.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
1975 presTrans.image = image.image;
1976 presTrans.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1977 presTrans.subresourceRange.levelCount = presTrans.subresourceRange.layerCount = 1;
1978
1980 // was not used at all (no render pass), just transition from undefined to presentable
1981 presTrans.srcAccessMask = 0;
1982 presTrans.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1983 df->vkCmdPipelineBarrier(frame.cmdBuf,
1984 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
1985 0, 0, nullptr, 0, nullptr,
1986 1, &presTrans);
1988 // was used in a readback as transfer source, go back to presentable layout
1989 presTrans.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
1990 presTrans.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
1991 df->vkCmdPipelineBarrier(frame.cmdBuf,
1992 VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
1993 0, 0, nullptr, 0, nullptr,
1994 1, &presTrans);
1995 }
1997 }
1998
1999 // record another timestamp, when enabled
2000 if (frame.timestampQueryIndex >= 0) {
2001 df->vkCmdWriteTimestamp(frame.cmdBuf, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
2002 timestampQueryPool, uint32_t(frame.timestampQueryIndex + 1));
2003 }
2004
2005 // stop recording and submit to the queue
2006 Q_ASSERT(!frame.cmdFenceWaitable);
2007 const bool needsPresent = !flags.testFlag(QRhi::SkipPresent);
2009 frame.cmdFence,
2010 frame.imageSemWaitable ? &frame.imageSem : nullptr,
2011 needsPresent ? &frame.drawSem : nullptr);
2012 if (submitres != QRhi::FrameOpSuccess)
2013 return submitres;
2014
2015 frame.imageSemWaitable = false;
2016 frame.cmdFenceWaitable = true;
2017
2018 if (needsPresent) {
2019 // add the Present to the queue
2020 VkPresentInfoKHR presInfo = {};
2021 presInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
2022 presInfo.swapchainCount = 1;
2023 presInfo.pSwapchains = &swapChainD->sc;
2024 presInfo.pImageIndices = &swapChainD->currentImageIndex;
2025 presInfo.waitSemaphoreCount = 1;
2026 presInfo.pWaitSemaphores = &frame.drawSem; // gfxQueueFamilyIdx == presQueueFamilyIdx ? &frame.drawSem : &frame.presTransSem;
2027
2028 // Do platform-specific WM notification. F.ex. essential on Wayland in
2029 // order to circumvent driver frame callbacks
2030 inst->presentAboutToBeQueued(swapChainD->window);
2031
2032 VkResult err = vkQueuePresentKHR(gfxQueue, &presInfo);
2033 if (err != VK_SUCCESS) {
2034 if (err == VK_ERROR_OUT_OF_DATE_KHR) {
2036 } else if (err != VK_SUBOPTIMAL_KHR) {
2037 if (err == VK_ERROR_DEVICE_LOST) {
2038 qWarning("Device loss detected in vkQueuePresentKHR()");
2039 deviceLost = true;
2041 }
2042 qWarning("Failed to present: %d", err);
2043 return QRhi::FrameOpError;
2044 }
2045 }
2046
2047 // Do platform-specific WM notification. F.ex. essential on X11 in
2048 // order to prevent glitches on resizing the window.
2049 inst->presentQueued(swapChainD->window);
2050
2051 // mark the current swapchain buffer as unused from our side
2052 frame.imageAcquired = false;
2053 // and move on to the next buffer
2054 swapChainD->currentFrameSlot = (swapChainD->currentFrameSlot + 1) % QVK_FRAMES_IN_FLIGHT;
2055 }
2056
2057 swapChainD->frameCount += 1;
2058 currentSwapChain = nullptr;
2059 return QRhi::FrameOpSuccess;
2060}
2061
2063{
2064 // Now is the time to do things for frame N-F, where N is the current one,
2065 // F is QVK_FRAMES_IN_FLIGHT, because only here it is guaranteed that that
2066 // frame has completed on the GPU (due to the fence wait in beginFrame). To
2067 // decide if something is safe to handle now a simple "lastActiveFrameSlot
2068 // == currentFrameSlot" is sufficient (remember that e.g. with F==2
2069 // currentFrameSlot goes 0, 1, 0, 1, 0, ...)
2070 //
2071 // With multiple swapchains on the same QRhi things get more convoluted
2072 // (and currentFrameSlot strictly alternating is not true anymore) but
2073 // begin(Offscreen)Frame() blocks anyway waiting for its current frame
2074 // slot's previous commands to complete so this here is safe regardless.
2075
2077
2078 QRHI_RES(QVkCommandBuffer, cb)->resetState();
2079
2080 finishActiveReadbacks(); // last, in case the readback-completed callback issues rhi calls
2081
2083}
2084
2086{
2087 if (!*cb) {
2088 VkCommandBufferAllocateInfo cmdBufInfo = {};
2089 cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
2090 cmdBufInfo.commandPool = cmdPool[currentFrameSlot];
2091 cmdBufInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
2092 cmdBufInfo.commandBufferCount = 1;
2093
2094 VkResult err = df->vkAllocateCommandBuffers(dev, &cmdBufInfo, cb);
2095 if (err != VK_SUCCESS) {
2096 if (err == VK_ERROR_DEVICE_LOST) {
2097 qWarning("Device loss detected in vkAllocateCommandBuffers()");
2098 deviceLost = true;
2100 }
2101 qWarning("Failed to allocate frame command buffer: %d", err);
2102 return QRhi::FrameOpError;
2103 }
2104 }
2105
2106 VkCommandBufferBeginInfo cmdBufBeginInfo = {};
2107 cmdBufBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
2108
2109 VkResult err = df->vkBeginCommandBuffer(*cb, &cmdBufBeginInfo);
2110 if (err != VK_SUCCESS) {
2111 if (err == VK_ERROR_DEVICE_LOST) {
2112 qWarning("Device loss detected in vkBeginCommandBuffer()");
2113 deviceLost = true;
2115 }
2116 qWarning("Failed to begin frame command buffer: %d", err);
2117 return QRhi::FrameOpError;
2118 }
2119
2120 return QRhi::FrameOpSuccess;
2121}
2122
2124 VkSemaphore *waitSem, VkSemaphore *signalSem)
2125{
2126 VkResult err = df->vkEndCommandBuffer(cb);
2127 if (err != VK_SUCCESS) {
2128 if (err == VK_ERROR_DEVICE_LOST) {
2129 qWarning("Device loss detected in vkEndCommandBuffer()");
2130 deviceLost = true;
2132 }
2133 qWarning("Failed to end frame command buffer: %d", err);
2134 return QRhi::FrameOpError;
2135 }
2136
2137 VkSubmitInfo submitInfo = {};
2138 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
2139 submitInfo.commandBufferCount = 1;
2140 submitInfo.pCommandBuffers = &cb;
2141 if (waitSem) {
2142 submitInfo.waitSemaphoreCount = 1;
2143 submitInfo.pWaitSemaphores = waitSem;
2144 }
2145 if (signalSem) {
2146 submitInfo.signalSemaphoreCount = 1;
2147 submitInfo.pSignalSemaphores = signalSem;
2148 }
2149 VkPipelineStageFlags psf = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
2150 submitInfo.pWaitDstStageMask = &psf;
2151
2152 err = df->vkQueueSubmit(gfxQueue, 1, &submitInfo, cmdFence);
2153 if (err != VK_SUCCESS) {
2154 if (err == VK_ERROR_DEVICE_LOST) {
2155 qWarning("Device loss detected in vkQueueSubmit()");
2156 deviceLost = true;
2158 }
2159 qWarning("Failed to submit to graphics queue: %d", err);
2160 return QRhi::FrameOpError;
2161 }
2162
2163 return QRhi::FrameOpSuccess;
2164}
2165
2167{
2168 for (QVkSwapChain *sc : std::as_const(swapchains)) {
2169 const int frameResIndex = sc->bufferCount > 1 ? frameSlot : 0;
2170 QVkSwapChain::FrameResources &frame(sc->frameRes[frameResIndex]);
2171 if (frame.cmdFenceWaitable) {
2172 df->vkWaitForFences(dev, 1, &frame.cmdFence, VK_TRUE, UINT64_MAX);
2173 df->vkResetFences(dev, 1, &frame.cmdFence);
2174 frame.cmdFenceWaitable = false;
2175 }
2176 }
2177}
2178
2180{
2181 // Switch to the next slot manually. Swapchains do not know about this
2182 // which is good. So for example an onscreen, onscreen, offscreen,
2183 // onscreen, onscreen, onscreen sequence of frames leads to 0, 1, 0, 0, 1,
2184 // 0. (no strict alternation anymore) But this is not different from what
2185 // happens when multiple swapchains are involved. Offscreen frames are
2186 // synchronous anyway in the sense that they wait for execution to complete
2187 // in endOffscreenFrame, so no resources used in that frame are busy
2188 // anymore in the next frame.
2189
2191
2193
2195
2198 if (cbres != QRhi::FrameOpSuccess)
2199 return cbres;
2200
2201 prepareNewFrame(cbWrapper);
2202 ofr.active = true;
2203
2204 if (rhiFlags.testFlag(QRhi::EnableTimestamps)) {
2205 int timestampQueryIdx = -1;
2206 for (int i = 0; i < timestampQueryPoolMap.size(); ++i) {
2209 timestampQueryIdx = i * 2;
2210 break;
2211 }
2212 }
2213 if (timestampQueryIdx >= 0) {
2214 df->vkCmdResetQueryPool(cbWrapper->cb, timestampQueryPool, uint32_t(timestampQueryIdx), 2);
2215 // record timestamp at the start of the command buffer
2216 df->vkCmdWriteTimestamp(cbWrapper->cb, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
2217 timestampQueryPool, uint32_t(timestampQueryIdx));
2218 ofr.timestampQueryIndex = timestampQueryIdx;
2219 }
2220 }
2221
2222 *cb = cbWrapper;
2223 return QRhi::FrameOpSuccess;
2224}
2225
2227{
2228 Q_UNUSED(flags);
2230 ofr.active = false;
2231
2233 recordPrimaryCommandBuffer(cbWrapper);
2234
2235 // record another timestamp, when enabled
2236 if (ofr.timestampQueryIndex >= 0) {
2237 df->vkCmdWriteTimestamp(cbWrapper->cb, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
2239 }
2240
2241 if (!ofr.cmdFence) {
2242 VkFenceCreateInfo fenceInfo = {};
2243 fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
2244 VkResult err = df->vkCreateFence(dev, &fenceInfo, nullptr, &ofr.cmdFence);
2245 if (err != VK_SUCCESS) {
2246 qWarning("Failed to create command buffer fence: %d", err);
2247 return QRhi::FrameOpError;
2248 }
2249 }
2250
2251 QRhi::FrameOpResult submitres = endAndSubmitPrimaryCommandBuffer(cbWrapper->cb, ofr.cmdFence, nullptr, nullptr);
2252 if (submitres != QRhi::FrameOpSuccess)
2253 return submitres;
2254
2255 // wait for completion
2256 df->vkWaitForFences(dev, 1, &ofr.cmdFence, VK_TRUE, UINT64_MAX);
2257 df->vkResetFences(dev, 1, &ofr.cmdFence);
2258
2259 // Here we know that executing the host-side reads for this (or any
2260 // previous) frame is safe since we waited for completion above.
2262
2263 // Read the timestamps, if we wrote them.
2264 if (ofr.timestampQueryIndex >= 0) {
2265 quint64 timestamp[2] = { 0, 0 };
2266 VkResult err = df->vkGetQueryPoolResults(dev, timestampQueryPool, uint32_t(ofr.timestampQueryIndex), 2,
2267 2 * sizeof(quint64), timestamp, sizeof(quint64),
2268 VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT);
2271 if (err == VK_SUCCESS) {
2272 bool ok = false;
2273 const double elapsedSec = elapsedSecondsFromTimestamp(timestamp, &ok);
2274 if (ok)
2275 cbWrapper->lastGpuTime = elapsedSec;
2276 } else {
2277 qWarning("Failed to query timestamp: %d", err);
2278 }
2279 }
2280
2281 return QRhi::FrameOpSuccess;
2282}
2283
2285{
2286 QVkSwapChain *swapChainD = nullptr;
2287 if (inFrame) {
2288 // There is either a swapchain or an offscreen frame on-going.
2289 // End command recording and submit what we have.
2290 VkCommandBuffer cb;
2291 if (ofr.active) {
2295 recordPrimaryCommandBuffer(cbWrapper);
2296 cbWrapper->resetCommands();
2297 cb = cbWrapper->cb;
2298 } else {
2301 swapChainD = currentSwapChain;
2303 swapChainD->cbWrapper.resetCommands();
2304 cb = swapChainD->cbWrapper.cb;
2305 }
2306 QRhi::FrameOpResult submitres = endAndSubmitPrimaryCommandBuffer(cb, VK_NULL_HANDLE, nullptr, nullptr);
2307 if (submitres != QRhi::FrameOpSuccess)
2308 return submitres;
2309 }
2310
2311 df->vkQueueWaitIdle(gfxQueue);
2312
2313 if (inFrame) {
2314 // The current frame slot's command pool needs to be reset.
2316 // Allocate and begin recording on a new command buffer.
2317 if (ofr.active) {
2319 } else {
2322 swapChainD->cbWrapper.cb = frame.cmdBuf;
2323 }
2324 }
2325
2328
2329 return QRhi::FrameOpSuccess;
2330}
2331
2333{
2335 u.layout = 0; // unused with buffers
2336 u.access = int(bufUsage.access);
2337 u.stage = int(bufUsage.stage);
2338 return u;
2339}
2340
2342{
2344 u.layout = texUsage.layout;
2345 u.access = int(texUsage.access);
2346 u.stage = int(texUsage.stage);
2347 return u;
2348}
2349
2351{
2352 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QVkTexture, QVkRenderBuffer>(rtD->description(), rtD->d.currentResIdList))
2353 rtD->create();
2354
2358 for (auto it = rtD->m_desc.cbeginColorAttachments(), itEnd = rtD->m_desc.cendColorAttachments(); it != itEnd; ++it) {
2359 QVkTexture *texD = QRHI_RES(QVkTexture, it->texture());
2360 QVkTexture *resolveTexD = QRHI_RES(QVkTexture, it->resolveTexture());
2361 QVkRenderBuffer *rbD = QRHI_RES(QVkRenderBuffer, it->renderBuffer());
2362 if (texD) {
2363 trackedRegisterTexture(&passResTracker, texD,
2367 } else if (rbD) {
2368 // Won't register rbD->backingTexture because it cannot be used for
2369 // anything in a renderpass, its use makes only sense in
2370 // combination with a resolveTexture.
2372 }
2373 if (resolveTexD) {
2374 trackedRegisterTexture(&passResTracker, resolveTexD,
2378 }
2379 }
2380 if (rtD->m_desc.depthStencilBuffer()) {
2383 // We specify no explicit VkSubpassDependency for an offscreen render
2384 // target, meaning we need an explicit barrier for the depth-stencil
2385 // buffer to avoid a write-after-write hazard (as the implicit one is
2386 // not sufficient). Textures are taken care of by the resource tracking
2387 // but that excludes the (content-wise) throwaway depth-stencil buffer.
2390 }
2391 if (rtD->m_desc.depthTexture()) {
2392 QVkTexture *depthTexD = QRHI_RES(QVkTexture, rtD->m_desc.depthTexture());
2393 trackedRegisterTexture(&passResTracker, depthTexD,
2397 }
2398}
2399
2401{
2404
2405 enqueueResourceUpdates(cbD, resourceUpdates);
2406}
2407
2409{
2410 VkCommandBuffer secondaryCb;
2411
2412 if (!freeSecondaryCbs[currentFrameSlot].isEmpty()) {
2413 secondaryCb = freeSecondaryCbs[currentFrameSlot].last();
2415 } else {
2416 VkCommandBufferAllocateInfo cmdBufInfo = {};
2417 cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
2418 cmdBufInfo.commandPool = cmdPool[currentFrameSlot];
2419 cmdBufInfo.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY;
2420 cmdBufInfo.commandBufferCount = 1;
2421
2422 VkResult err = df->vkAllocateCommandBuffers(dev, &cmdBufInfo, &secondaryCb);
2423 if (err != VK_SUCCESS) {
2424 qWarning("Failed to create secondary command buffer: %d", err);
2425 return VK_NULL_HANDLE;
2426 }
2427 }
2428
2429 VkCommandBufferBeginInfo cmdBufBeginInfo = {};
2430 cmdBufBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
2431 cmdBufBeginInfo.flags = rtD ? VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT : 0;
2432 VkCommandBufferInheritanceInfo cmdBufInheritInfo = {};
2433 cmdBufInheritInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
2434 cmdBufInheritInfo.subpass = 0;
2435 if (rtD) {
2436 cmdBufInheritInfo.renderPass = rtD->rp->rp;
2437 cmdBufInheritInfo.framebuffer = rtD->fb;
2438 }
2439 cmdBufBeginInfo.pInheritanceInfo = &cmdBufInheritInfo;
2440
2441 VkResult err = df->vkBeginCommandBuffer(secondaryCb, &cmdBufBeginInfo);
2442 if (err != VK_SUCCESS) {
2443 qWarning("Failed to begin secondary command buffer: %d", err);
2444 return VK_NULL_HANDLE;
2445 }
2446
2447 return secondaryCb;
2448}
2449
2451{
2452 VkResult err = df->vkEndCommandBuffer(cb);
2453 if (err != VK_SUCCESS)
2454 qWarning("Failed to end secondary command buffer: %d", err);
2455
2456 QVkCommandBuffer::Command &cmd(cbD->commands.get());
2458 cmd.args.executeSecondary.cb = cb;
2459
2462 e.lastActiveFrameSlot = currentFrameSlot;
2463 e.secondaryCommandBuffer.cb = cb;
2464 releaseQueue.append(e);
2465}
2466
2468 QRhiRenderTarget *rt,
2469 const QColor &colorClearValue,
2470 const QRhiDepthStencilClearValue &depthStencilClearValue,
2471 QRhiResourceUpdateBatch *resourceUpdates,
2472 QRhiCommandBuffer::BeginPassFlags flags)
2473{
2476
2477 if (resourceUpdates)
2478 enqueueResourceUpdates(cbD, resourceUpdates);
2479
2480 // Insert a TransitionPassResources into the command stream, pointing to
2481 // the tracker this pass is going to use. That's how we generate the
2482 // barriers later during recording the real VkCommandBuffer, right before
2483 // the vkCmdBeginRenderPass.
2485
2486 QVkRenderTargetData *rtD = nullptr;
2487 switch (rt->resourceType()) {
2489 rtD = &QRHI_RES(QVkSwapChainRenderTarget, rt)->d;
2494 break;
2496 {
2498 rtD = &rtTex->d;
2499 activateTextureRenderTarget(cbD, rtTex);
2500 }
2501 break;
2502 default:
2503 Q_UNREACHABLE();
2504 break;
2505 }
2506
2509 cbD->currentTarget = rt;
2510
2511 // No copy operations or image layout transitions allowed after this point
2512 // (up until endPass) as we are going to begin the renderpass.
2513
2514 VkRenderPassBeginInfo rpBeginInfo = {};
2515 rpBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
2516 rpBeginInfo.renderPass = rtD->rp->rp;
2517 rpBeginInfo.framebuffer = rtD->fb;
2518 rpBeginInfo.renderArea.extent.width = uint32_t(rtD->pixelSize.width());
2519 rpBeginInfo.renderArea.extent.height = uint32_t(rtD->pixelSize.height());
2520
2522 for (int i = 0; i < rtD->colorAttCount; ++i) {
2523 VkClearValue cv;
2524 cv.color = { { float(colorClearValue.redF()), float(colorClearValue.greenF()), float(colorClearValue.blueF()),
2525 float(colorClearValue.alphaF()) } };
2526 cvs.append(cv);
2527 }
2528 for (int i = 0; i < rtD->dsAttCount; ++i) {
2529 VkClearValue cv;
2530 cv.depthStencil = { depthStencilClearValue.depthClearValue(), depthStencilClearValue.stencilClearValue() };
2531 cvs.append(cv);
2532 }
2533 for (int i = 0; i < rtD->resolveAttCount; ++i) {
2534 VkClearValue cv;
2535 cv.color = { { float(colorClearValue.redF()), float(colorClearValue.greenF()), float(colorClearValue.blueF()),
2536 float(colorClearValue.alphaF()) } };
2537 cvs.append(cv);
2538 }
2539 rpBeginInfo.clearValueCount = uint32_t(cvs.size());
2540
2541 QVkCommandBuffer::Command &cmd(cbD->commands.get());
2543 cmd.args.beginRenderPass.desc = rpBeginInfo;
2546 cbD->pools.clearValue.append(cvs.constData(), cvs.size());
2547
2548 if (cbD->passUsesSecondaryCb)
2550
2551 cbD->resetCachedState();
2552}
2553
2555{
2558
2559 if (cbD->passUsesSecondaryCb) {
2560 VkCommandBuffer secondaryCb = cbD->activeSecondaryCbStack.last();
2562 endAndEnqueueSecondaryCommandBuffer(secondaryCb, cbD);
2563 }
2564
2565 QVkCommandBuffer::Command &cmd(cbD->commands.get());
2567
2569 cbD->currentTarget = nullptr;
2570
2571 if (resourceUpdates)
2572 enqueueResourceUpdates(cbD, resourceUpdates);
2573}
2574
2576 QRhiResourceUpdateBatch *resourceUpdates,
2577 QRhiCommandBuffer::BeginPassFlags flags)
2578{
2581
2582 if (resourceUpdates)
2583 enqueueResourceUpdates(cbD, resourceUpdates);
2584
2586
2589
2590 cbD->computePassState.reset();
2591
2592 if (cbD->passUsesSecondaryCb)
2594
2595 cbD->resetCachedState();
2596}
2597
2599{
2602
2603 if (cbD->passUsesSecondaryCb) {
2604 VkCommandBuffer secondaryCb = cbD->activeSecondaryCbStack.last();
2606 endAndEnqueueSecondaryCommandBuffer(secondaryCb, cbD);
2607 }
2608
2610
2611 if (resourceUpdates)
2612 enqueueResourceUpdates(cbD, resourceUpdates);
2613}
2614
2616{
2618 Q_ASSERT(psD->pipeline);
2621
2622 if (cbD->currentComputePipeline != ps || cbD->currentPipelineGeneration != psD->generation) {
2623 if (cbD->passUsesSecondaryCb) {
2624 df->vkCmdBindPipeline(cbD->activeSecondaryCbStack.last(), VK_PIPELINE_BIND_POINT_COMPUTE, psD->pipeline);
2625 } else {
2626 QVkCommandBuffer::Command &cmd(cbD->commands.get());
2628 cmd.args.bindPipeline.bindPoint = VK_PIPELINE_BIND_POINT_COMPUTE;
2630 }
2631
2632 cbD->currentGraphicsPipeline = nullptr;
2633 cbD->currentComputePipeline = ps;
2635 }
2636
2638}
2639
2640template<typename T>
2641inline void qrhivk_accumulateComputeResource(T *writtenResources, QRhiResource *resource,
2643 int loadTypeVal, int storeTypeVal, int loadStoreTypeVal)
2644{
2645 VkAccessFlags access = 0;
2646 if (bindingType == loadTypeVal) {
2647 access = VK_ACCESS_SHADER_READ_BIT;
2648 } else {
2649 access = VK_ACCESS_SHADER_WRITE_BIT;
2650 if (bindingType == loadStoreTypeVal)
2651 access |= VK_ACCESS_SHADER_READ_BIT;
2652 }
2653 auto it = writtenResources->find(resource);
2654 if (it != writtenResources->end())
2655 it->first |= access;
2656 else if (bindingType == storeTypeVal || bindingType == loadStoreTypeVal)
2657 writtenResources->insert(resource, { access, true });
2658}
2659
2661{
2664
2665 // When there are multiple dispatches, read-after-write and
2666 // write-after-write need a barrier.
2669 if (cbD->currentComputeSrb) {
2670 // The key in the writtenResources map indicates that the resource was
2671 // written in a previous dispatch, whereas the value accumulates the
2672 // access mask in the current one.
2673 for (auto &accessAndIsNewFlag : cbD->computePassState.writtenResources)
2674 accessAndIsNewFlag = { 0, false };
2675
2677 const int bindingCount = srbD->m_bindings.size();
2678 for (int i = 0; i < bindingCount; ++i) {
2680 switch (b->type) {
2685 b->u.simage.tex,
2686 b->type,
2690 break;
2695 b->u.sbuf.buf,
2696 b->type,
2700 break;
2701 default:
2702 break;
2703 }
2704 }
2705
2707 const int accessInThisDispatch = it->first;
2708 const bool isNewInThisDispatch = it->second;
2709 if (accessInThisDispatch && !isNewInThisDispatch) {
2710 if (it.key()->resourceType() == QRhiResource::Texture) {
2711 QVkTexture *texD = QRHI_RES(QVkTexture, it.key());
2712 VkImageMemoryBarrier barrier = {};
2713 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
2714 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
2715 // won't care about subresources, pretend the whole resource was written
2716 barrier.subresourceRange.baseMipLevel = 0;
2717 barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
2718 barrier.subresourceRange.baseArrayLayer = 0;
2719 barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
2720 barrier.oldLayout = texD->usageState.layout;
2721 barrier.newLayout = texD->usageState.layout;
2722 barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
2723 barrier.dstAccessMask = accessInThisDispatch;
2724 barrier.image = texD->image;
2725 imageBarriers.append(barrier);
2726 } else {
2727 QVkBuffer *bufD = QRHI_RES(QVkBuffer, it.key());
2728 VkBufferMemoryBarrier barrier = {};
2729 barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
2730 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
2731 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
2732 barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
2733 barrier.dstAccessMask = accessInThisDispatch;
2734 barrier.buffer = bufD->buffers[bufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0];
2735 barrier.size = VK_WHOLE_SIZE;
2736 bufferBarriers.append(barrier);
2737 }
2738 }
2739 // Anything that was previously written, but is only read now, can be
2740 // removed from the written list (because that previous write got a
2741 // corresponding barrier now).
2742 if (accessInThisDispatch == VK_ACCESS_SHADER_READ_BIT)
2744 else
2745 ++it;
2746 }
2747 }
2748
2749 if (cbD->passUsesSecondaryCb) {
2750 VkCommandBuffer secondaryCb = cbD->activeSecondaryCbStack.last();
2751 if (!imageBarriers.isEmpty()) {
2752 df->vkCmdPipelineBarrier(secondaryCb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
2753 0, 0, nullptr,
2754 0, nullptr,
2755 imageBarriers.size(), imageBarriers.constData());
2756 }
2757 if (!bufferBarriers.isEmpty()) {
2758 df->vkCmdPipelineBarrier(secondaryCb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
2759 0, 0, nullptr,
2760 bufferBarriers.size(), bufferBarriers.constData(),
2761 0, nullptr);
2762 }
2763 df->vkCmdDispatch(secondaryCb, uint32_t(x), uint32_t(y), uint32_t(z));
2764 } else {
2765 if (!imageBarriers.isEmpty()) {
2766 QVkCommandBuffer::Command &cmd(cbD->commands.get());
2768 cmd.args.imageBarrier.srcStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
2769 cmd.args.imageBarrier.dstStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
2770 cmd.args.imageBarrier.count = imageBarriers.size();
2772 cbD->pools.imageBarrier.append(imageBarriers.constData(), imageBarriers.size());
2773 }
2774 if (!bufferBarriers.isEmpty()) {
2775 QVkCommandBuffer::Command &cmd(cbD->commands.get());
2777 cmd.args.bufferBarrier.srcStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
2778 cmd.args.bufferBarrier.dstStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
2779 cmd.args.bufferBarrier.count = bufferBarriers.size();
2781 cbD->pools.bufferBarrier.append(bufferBarriers.constData(), bufferBarriers.size());
2782 }
2783 QVkCommandBuffer::Command &cmd(cbD->commands.get());
2785 cmd.args.dispatch.x = x;
2786 cmd.args.dispatch.y = y;
2787 cmd.args.dispatch.z = z;
2788 }
2789}
2790
2791VkShaderModule QRhiVulkan::createShader(const QByteArray &spirv)
2792{
2793 VkShaderModuleCreateInfo shaderInfo = {};
2794 shaderInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
2795 shaderInfo.codeSize = size_t(spirv.size());
2796 shaderInfo.pCode = reinterpret_cast<const quint32 *>(spirv.constData());
2797 VkShaderModule shaderModule;
2798 VkResult err = df->vkCreateShaderModule(dev, &shaderInfo, nullptr, &shaderModule);
2799 if (err != VK_SUCCESS) {
2800 qWarning("Failed to create shader module: %d", err);
2801 return VK_NULL_HANDLE;
2802 }
2803 return shaderModule;
2804}
2805
2806bool QRhiVulkan::ensurePipelineCache(const void *initialData, size_t initialDataSize)
2807{
2808 if (pipelineCache)
2809 return true;
2810
2811 VkPipelineCacheCreateInfo pipelineCacheInfo = {};
2812 pipelineCacheInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
2813 pipelineCacheInfo.initialDataSize = initialDataSize;
2814 pipelineCacheInfo.pInitialData = initialData;
2815 VkResult err = df->vkCreatePipelineCache(dev, &pipelineCacheInfo, nullptr, &pipelineCache);
2816 if (err != VK_SUCCESS) {
2817 qWarning("Failed to create pipeline cache: %d", err);
2818 return false;
2819 }
2820 return true;
2821}
2822
2824{
2826
2828 using ArrayOfImageDesc = QVarLengthArray<VkDescriptorImageInfo, 8>;
2831 QVarLengthArray<QPair<int, int>, 12> infoIndices;
2832
2833 const bool updateAll = descSetIdx < 0;
2834 int frameSlot = updateAll ? 0 : descSetIdx;
2835 while (frameSlot < (updateAll ? QVK_FRAMES_IN_FLIGHT : descSetIdx + 1)) {
2836 for (int i = 0, ie = srbD->sortedBindings.size(); i != ie; ++i) {
2839
2840 VkWriteDescriptorSet writeInfo = {};
2841 writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
2842 writeInfo.dstSet = srbD->descSets[frameSlot];
2843 writeInfo.dstBinding = uint32_t(b->binding);
2844 writeInfo.descriptorCount = 1;
2845
2846 int bufferInfoIndex = -1;
2847 int imageInfoIndex = -1;
2848
2849 switch (b->type) {
2851 {
2852 writeInfo.descriptorType = b->u.ubuf.hasDynamicOffset ? VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC
2853 : VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
2854 QRhiBuffer *buf = b->u.ubuf.buf;
2855 QVkBuffer *bufD = QRHI_RES(QVkBuffer, buf);
2856 bd.ubuf.id = bufD->m_id;
2857 bd.ubuf.generation = bufD->generation;
2858 VkDescriptorBufferInfo bufInfo;
2859 bufInfo.buffer = bufD->m_type == QRhiBuffer::Dynamic ? bufD->buffers[frameSlot] : bufD->buffers[0];
2860 bufInfo.offset = b->u.ubuf.offset;
2861 bufInfo.range = b->u.ubuf.maybeSize ? b->u.ubuf.maybeSize : bufD->m_size;
2862 // be nice and assert when we know the vulkan device would die a horrible death due to non-aligned reads
2863 Q_ASSERT(aligned(bufInfo.offset, ubufAlign) == bufInfo.offset);
2864 bufferInfoIndex = bufferInfos.size();
2865 bufferInfos.append(bufInfo);
2866 }
2867 break;
2869 {
2871 writeInfo.descriptorCount = data->count; // arrays of combined image samplers are supported
2872 writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
2873 ArrayOfImageDesc imageInfo(data->count);
2874 for (int elem = 0; elem < data->count; ++elem) {
2875 QVkTexture *texD = QRHI_RES(QVkTexture, data->texSamplers[elem].tex);
2876 QVkSampler *samplerD = QRHI_RES(QVkSampler, data->texSamplers[elem].sampler);
2877 bd.stex.d[elem].texId = texD->m_id;
2878 bd.stex.d[elem].texGeneration = texD->generation;
2879 bd.stex.d[elem].samplerId = samplerD->m_id;
2880 bd.stex.d[elem].samplerGeneration = samplerD->generation;
2881 imageInfo[elem].sampler = samplerD->sampler;
2882 imageInfo[elem].imageView = texD->imageView;
2883 imageInfo[elem].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
2884 }
2885 bd.stex.count = data->count;
2886 imageInfoIndex = imageInfos.size();
2887 imageInfos.append(imageInfo);
2888 }
2889 break;
2891 {
2893 writeInfo.descriptorCount = data->count; // arrays of (separate) images are supported
2894 writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
2895 ArrayOfImageDesc imageInfo(data->count);
2896 for (int elem = 0; elem < data->count; ++elem) {
2897 QVkTexture *texD = QRHI_RES(QVkTexture, data->texSamplers[elem].tex);
2898 bd.stex.d[elem].texId = texD->m_id;
2899 bd.stex.d[elem].texGeneration = texD->generation;
2900 bd.stex.d[elem].samplerId = 0;
2901 bd.stex.d[elem].samplerGeneration = 0;
2902 imageInfo[elem].sampler = VK_NULL_HANDLE;
2903 imageInfo[elem].imageView = texD->imageView;
2904 imageInfo[elem].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
2905 }
2906 bd.stex.count = data->count;
2907 imageInfoIndex = imageInfos.size();
2908 imageInfos.append(imageInfo);
2909 }
2910 break;
2912 {
2913 QVkSampler *samplerD = QRHI_RES(QVkSampler, b->u.stex.texSamplers[0].sampler);
2914 writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;
2915 bd.stex.d[0].texId = 0;
2916 bd.stex.d[0].texGeneration = 0;
2917 bd.stex.d[0].samplerId = samplerD->m_id;
2918 bd.stex.d[0].samplerGeneration = samplerD->generation;
2919 ArrayOfImageDesc imageInfo(1);
2920 imageInfo[0].sampler = samplerD->sampler;
2921 imageInfo[0].imageView = VK_NULL_HANDLE;
2922 imageInfo[0].imageLayout = VK_IMAGE_LAYOUT_GENERAL;
2923 imageInfoIndex = imageInfos.size();
2924 imageInfos.append(imageInfo);
2925 }
2926 break;
2930 {
2931 QVkTexture *texD = QRHI_RES(QVkTexture, b->u.simage.tex);
2932 VkImageView view = texD->imageViewForLevel(b->u.simage.level);
2933 if (view) {
2934 writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
2935 bd.simage.id = texD->m_id;
2936 bd.simage.generation = texD->generation;
2937 ArrayOfImageDesc imageInfo(1);
2938 imageInfo[0].sampler = VK_NULL_HANDLE;
2939 imageInfo[0].imageView = view;
2940 imageInfo[0].imageLayout = VK_IMAGE_LAYOUT_GENERAL;
2941 imageInfoIndex = imageInfos.size();
2942 imageInfos.append(imageInfo);
2943 }
2944 }
2945 break;
2949 {
2950 QVkBuffer *bufD = QRHI_RES(QVkBuffer, b->u.sbuf.buf);
2951 writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
2952 bd.sbuf.id = bufD->m_id;
2953 bd.sbuf.generation = bufD->generation;
2954 VkDescriptorBufferInfo bufInfo;
2955 bufInfo.buffer = bufD->m_type == QRhiBuffer::Dynamic ? bufD->buffers[frameSlot] : bufD->buffers[0];
2956 bufInfo.offset = b->u.ubuf.offset;
2957 bufInfo.range = b->u.ubuf.maybeSize ? b->u.ubuf.maybeSize : bufD->m_size;
2958 bufferInfoIndex = bufferInfos.size();
2959 bufferInfos.append(bufInfo);
2960 }
2961 break;
2962 default:
2963 continue;
2964 }
2965
2966 writeInfos.append(writeInfo);
2967 infoIndices.append({ bufferInfoIndex, imageInfoIndex });
2968 }
2969 ++frameSlot;
2970 }
2971
2972 for (int i = 0, writeInfoCount = writeInfos.size(); i < writeInfoCount; ++i) {
2973 const int bufferInfoIndex = infoIndices[i].first;
2974 const int imageInfoIndex = infoIndices[i].second;
2975 if (bufferInfoIndex >= 0)
2976 writeInfos[i].pBufferInfo = &bufferInfos[bufferInfoIndex];
2977 else if (imageInfoIndex >= 0)
2978 writeInfos[i].pImageInfo = imageInfos[imageInfoIndex].constData();
2979 }
2980
2981 df->vkUpdateDescriptorSets(dev, uint32_t(writeInfos.size()), writeInfos.constData(), 0, nullptr);
2982}
2983
2984static inline bool accessIsWrite(VkAccessFlags access)
2985{
2986 return (access & VK_ACCESS_SHADER_WRITE_BIT) != 0
2987 || (access & VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT) != 0
2988 || (access & VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT) != 0
2989 || (access & VK_ACCESS_TRANSFER_WRITE_BIT) != 0
2990 || (access & VK_ACCESS_HOST_WRITE_BIT) != 0
2991 || (access & VK_ACCESS_MEMORY_WRITE_BIT) != 0;
2992}
2993
2995 VkAccessFlags access, VkPipelineStageFlags stage)
2996{
2998 Q_ASSERT(access && stage);
2999 QVkBuffer::UsageState &s(bufD->usageState[slot]);
3000 if (!s.stage) {
3001 s.access = access;
3002 s.stage = stage;
3003 return;
3004 }
3005
3006 if (s.access == access && s.stage == stage) {
3007 // No need to flood with unnecessary read-after-read barriers.
3008 // Write-after-write is a different matter, however.
3009 if (!accessIsWrite(access))
3010 return;
3011 }
3012
3013 VkBufferMemoryBarrier bufMemBarrier = {};
3014 bufMemBarrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
3015 bufMemBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
3016 bufMemBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
3017 bufMemBarrier.srcAccessMask = s.access;
3018 bufMemBarrier.dstAccessMask = access;
3019 bufMemBarrier.buffer = bufD->buffers[slot];
3020 bufMemBarrier.size = VK_WHOLE_SIZE;
3021
3022 QVkCommandBuffer::Command &cmd(cbD->commands.get());
3024 cmd.args.bufferBarrier.srcStageMask = s.stage;
3025 cmd.args.bufferBarrier.dstStageMask = stage;
3026 cmd.args.bufferBarrier.count = 1;
3028 cbD->pools.bufferBarrier.append(bufMemBarrier);
3029
3030 s.access = access;
3031 s.stage = stage;
3032}
3033
3035 VkImageLayout layout, VkAccessFlags access, VkPipelineStageFlags stage)
3036{
3038 Q_ASSERT(layout && access && stage);
3040 if (s.access == access && s.stage == stage && s.layout == layout) {
3041 if (!accessIsWrite(access))
3042 return;
3043 }
3044
3045 VkImageMemoryBarrier barrier = {};
3046 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
3047 barrier.subresourceRange.aspectMask = aspectMaskForTextureFormat(texD->m_format);
3048 barrier.subresourceRange.baseMipLevel = 0;
3049 barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
3050 barrier.subresourceRange.baseArrayLayer = 0;
3051 barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
3052 barrier.oldLayout = s.layout; // new textures have this set to PREINITIALIZED
3053 barrier.newLayout = layout;
3054 barrier.srcAccessMask = s.access; // may be 0 but that's fine
3055 barrier.dstAccessMask = access;
3056 barrier.image = texD->image;
3057
3058 VkPipelineStageFlags srcStage = s.stage;
3059 // stage mask cannot be 0
3060 if (!srcStage)
3061 srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
3062
3063 QVkCommandBuffer::Command &cmd(cbD->commands.get());
3065 cmd.args.imageBarrier.srcStageMask = srcStage;
3066 cmd.args.imageBarrier.dstStageMask = stage;
3067 cmd.args.imageBarrier.count = 1;
3069 cbD->pools.imageBarrier.append(barrier);
3070
3071 s.layout = layout;
3072 s.access = access;
3073 s.stage = stage;
3074}
3075
3077{
3079
3080 VkImageMemoryBarrier barrier = {};
3081 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
3082 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
3083 barrier.subresourceRange.baseMipLevel = 0;
3084 barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
3085 barrier.subresourceRange.baseArrayLayer = 0;
3086 barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
3087 barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
3088 barrier.newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
3089 barrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
3090 barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT
3091 | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
3092 barrier.image = rbD->image;
3093
3094 const VkPipelineStageFlags stages = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT
3095 | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
3096
3097 QVkCommandBuffer::Command &cmd(cbD->commands.get());
3101 cmd.args.imageBarrier.count = 1;
3103 cbD->pools.imageBarrier.append(barrier);
3104}
3105
3107 VkImageLayout oldLayout, VkImageLayout newLayout,
3108 VkAccessFlags srcAccess, VkAccessFlags dstAccess,
3109 VkPipelineStageFlags srcStage, VkPipelineStageFlags dstStage,
3110 int startLayer, int layerCount,
3111 int startLevel, int levelCount)
3112{
3114 VkImageMemoryBarrier barrier = {};
3115 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
3116 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
3117 barrier.subresourceRange.baseMipLevel = uint32_t(startLevel);
3118 barrier.subresourceRange.levelCount = uint32_t(levelCount);
3119 barrier.subresourceRange.baseArrayLayer = uint32_t(startLayer);
3120 barrier.subresourceRange.layerCount = uint32_t(layerCount);
3121 barrier.oldLayout = oldLayout;
3122 barrier.newLayout = newLayout;
3123 barrier.srcAccessMask = srcAccess;
3124 barrier.dstAccessMask = dstAccess;
3125 barrier.image = image;
3126
3127 QVkCommandBuffer::Command &cmd(cbD->commands.get());
3129 cmd.args.imageBarrier.srcStageMask = srcStage;
3130 cmd.args.imageBarrier.dstStageMask = dstStage;
3131 cmd.args.imageBarrier.count = 1;
3133 cbD->pools.imageBarrier.append(barrier);
3134}
3135
3137{
3138 VkDeviceSize size = 0;
3139 const qsizetype imageSizeBytes = subresDesc.image().isNull() ?
3140 subresDesc.data().size() : subresDesc.image().sizeInBytes();
3141 if (imageSizeBytes > 0)
3142 size += aligned(VkDeviceSize(imageSizeBytes), texbufAlign);
3143 return size;
3144}
3145
3148 size_t *curOfs, void *mp,
3149 BufferImageCopyList *copyInfos)
3150{
3151 qsizetype copySizeBytes = 0;
3152 qsizetype imageSizeBytes = 0;
3153 const void *src = nullptr;
3154 const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3155 const bool is1D = texD->m_flags.testFlag(QRhiTexture::OneDimensional);
3156
3157 VkBufferImageCopy copyInfo = {};
3158 copyInfo.bufferOffset = *curOfs;
3159 copyInfo.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
3160 copyInfo.imageSubresource.mipLevel = uint32_t(level);
3161 copyInfo.imageSubresource.baseArrayLayer = is3D ? 0 : uint32_t(layer);
3162 copyInfo.imageSubresource.layerCount = 1;
3163 copyInfo.imageExtent.depth = 1;
3164 if (is3D)
3165 copyInfo.imageOffset.z = uint32_t(layer);
3166 if (is1D)
3167 copyInfo.imageOffset.y = uint32_t(layer);
3168
3169 const QByteArray rawData = subresDesc.data();
3170 const QPoint dp = subresDesc.destinationTopLeft();
3171 QImage image = subresDesc.image();
3172 if (!image.isNull()) {
3173 copySizeBytes = imageSizeBytes = image.sizeInBytes();
3174 QSize size = image.size();
3175 src = image.constBits();
3176 // Scanlines in QImage are 4 byte aligned so bpl must
3177 // be taken into account for bufferRowLength.
3178 int bpc = qMax(1, image.depth() / 8);
3179 // this is in pixels, not bytes, to make it more complicated...
3180 copyInfo.bufferRowLength = uint32_t(image.bytesPerLine() / bpc);
3181 if (!subresDesc.sourceSize().isEmpty() || !subresDesc.sourceTopLeft().isNull()) {
3182 const int sx = subresDesc.sourceTopLeft().x();
3183 const int sy = subresDesc.sourceTopLeft().y();
3184 if (!subresDesc.sourceSize().isEmpty())
3185 size = subresDesc.sourceSize();
3186 if (image.depth() == 32) {
3187 // The staging buffer will get the full image
3188 // regardless, just adjust the vk
3189 // buffer-to-image copy start offset.
3190 copyInfo.bufferOffset += VkDeviceSize(sy * image.bytesPerLine() + sx * 4);
3191 // bufferRowLength remains set to the original image's width
3192 } else {
3193 image = image.copy(sx, sy, size.width(), size.height());
3194 src = image.constBits();
3195 // The staging buffer gets the slice only. The rest of the
3196 // space reserved for this mip will be unused.
3197 copySizeBytes = image.sizeInBytes();
3198 bpc = qMax(1, image.depth() / 8);
3199 copyInfo.bufferRowLength = uint32_t(image.bytesPerLine() / bpc);
3200 }
3201 }
3202 copyInfo.imageOffset.x = dp.x();
3203 copyInfo.imageOffset.y = dp.y();
3204 copyInfo.imageExtent.width = uint32_t(size.width());
3205 copyInfo.imageExtent.height = uint32_t(size.height());
3206 copyInfos->append(copyInfo);
3207 } else if (!rawData.isEmpty() && isCompressedFormat(texD->m_format)) {
3208 copySizeBytes = imageSizeBytes = rawData.size();
3209 src = rawData.constData();
3210 QSize size = q->sizeForMipLevel(level, texD->m_pixelSize);
3211 const int subresw = size.width();
3212 const int subresh = size.height();
3213 if (!subresDesc.sourceSize().isEmpty())
3214 size = subresDesc.sourceSize();
3215 const int w = size.width();
3216 const int h = size.height();
3217 QSize blockDim;
3218 compressedFormatInfo(texD->m_format, QSize(w, h), nullptr, nullptr, &blockDim);
3219 // x and y must be multiples of the block width and height
3220 copyInfo.imageOffset.x = aligned(dp.x(), blockDim.width());
3221 copyInfo.imageOffset.y = aligned(dp.y(), blockDim.height());
3222 // width and height must be multiples of the block width and height
3223 // or x + width and y + height must equal the subresource width and height
3224 copyInfo.imageExtent.width = uint32_t(dp.x() + w == subresw ? w : aligned(w, blockDim.width()));
3225 copyInfo.imageExtent.height = uint32_t(dp.y() + h == subresh ? h : aligned(h, blockDim.height()));
3226 copyInfos->append(copyInfo);
3227 } else if (!rawData.isEmpty()) {
3228 copySizeBytes = imageSizeBytes = rawData.size();
3229 src = rawData.constData();
3230 QSize size = q->sizeForMipLevel(level, texD->m_pixelSize);
3231 if (subresDesc.dataStride()) {
3232 quint32 bytesPerPixel = 0;
3233 textureFormatInfo(texD->m_format, size, nullptr, nullptr, &bytesPerPixel);
3234 if (bytesPerPixel)
3235 copyInfo.bufferRowLength = subresDesc.dataStride() / bytesPerPixel;
3236 }
3237 if (!subresDesc.sourceSize().isEmpty())
3238 size = subresDesc.sourceSize();
3239 copyInfo.imageOffset.x = dp.x();
3240 copyInfo.imageOffset.y = dp.y();
3241 copyInfo.imageExtent.width = uint32_t(size.width());
3242 copyInfo.imageExtent.height = uint32_t(size.height());
3243 copyInfos->append(copyInfo);
3244 } else {
3245 qWarning("Invalid texture upload for %p layer=%d mip=%d", texD, layer, level);
3246 }
3247
3248 if (src) {
3249 memcpy(reinterpret_cast<char *>(mp) + *curOfs, src, size_t(copySizeBytes));
3250 *curOfs += aligned(VkDeviceSize(imageSizeBytes), texbufAlign);
3251 }
3252}
3253
3255{
3257
3258 for (int opIdx = 0; opIdx < ud->activeBufferOpCount; ++opIdx) {
3261 QVkBuffer *bufD = QRHI_RES(QVkBuffer, u.buf);
3263 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
3264 if (u.offset == 0 && u.data.size() == bufD->m_size)
3265 bufD->pendingDynamicUpdates[i].clear();
3266 bufD->pendingDynamicUpdates[i].append({ u.offset, u.data });
3267 }
3269 QVkBuffer *bufD = QRHI_RES(QVkBuffer, u.buf);
3271 Q_ASSERT(u.offset + u.data.size() <= bufD->m_size);
3272
3273 if (!bufD->stagingBuffers[currentFrameSlot]) {
3274 VkBufferCreateInfo bufferInfo = {};
3275 bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
3276 // must cover the entire buffer - this way multiple, partial updates per frame
3277 // are supported even when the staging buffer is reused (Static)
3278 bufferInfo.size = bufD->m_size;
3279 bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
3280
3281 VmaAllocationCreateInfo allocInfo = {};
3282 allocInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
3283
3284 VmaAllocation allocation;
3285 VkResult err = vmaCreateBuffer(toVmaAllocator(allocator), &bufferInfo, &allocInfo,
3286 &bufD->stagingBuffers[currentFrameSlot], &allocation, nullptr);
3287 if (err == VK_SUCCESS) {
3289 } else {
3290 qWarning("Failed to create staging buffer of size %u: %d", bufD->m_size, err);
3291 continue;
3292 }
3293 }
3294
3295 void *p = nullptr;
3296 VmaAllocation a = toVmaAllocation(bufD->stagingAllocations[currentFrameSlot]);
3297 VkResult err = vmaMapMemory(toVmaAllocator(allocator), a, &p);
3298 if (err != VK_SUCCESS) {
3299 qWarning("Failed to map buffer: %d", err);
3300 continue;
3301 }
3302 memcpy(static_cast<uchar *>(p) + u.offset, u.data.constData(), u.data.size());
3303 vmaFlushAllocation(toVmaAllocator(allocator), a, u.offset, u.data.size());
3304 vmaUnmapMemory(toVmaAllocator(allocator), a);
3305
3306 trackedBufferBarrier(cbD, bufD, 0,
3307 VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
3308
3309 VkBufferCopy copyInfo = {};
3310 copyInfo.srcOffset = u.offset;
3311 copyInfo.dstOffset = u.offset;
3312 copyInfo.size = u.data.size();
3313
3314 QVkCommandBuffer::Command &cmd(cbD->commands.get());
3317 cmd.args.copyBuffer.dst = bufD->buffers[0];
3318 cmd.args.copyBuffer.desc = copyInfo;
3319
3320 // Where's the barrier for read-after-write? (assuming the common case
3321 // of binding this buffer as vertex/index, or, less likely, as uniform
3322 // buffer, in a renderpass later on) That is handled by the pass
3323 // resource tracking: the appropriate pipeline barrier will be
3324 // generated and recorded right before the renderpass, that binds this
3325 // buffer in one of its commands, gets its BeginRenderPass recorded.
3326
3328
3329 if (bufD->m_type == QRhiBuffer::Immutable) {
3332 e.lastActiveFrameSlot = currentFrameSlot;
3333 e.stagingBuffer.stagingBuffer = bufD->stagingBuffers[currentFrameSlot];
3334 e.stagingBuffer.stagingAllocation = bufD->stagingAllocations[currentFrameSlot];
3335 bufD->stagingBuffers[currentFrameSlot] = VK_NULL_HANDLE;
3336 bufD->stagingAllocations[currentFrameSlot] = nullptr;
3337 releaseQueue.append(e);
3338 }
3340 QVkBuffer *bufD = QRHI_RES(QVkBuffer, u.buf);
3341 if (bufD->m_type == QRhiBuffer::Dynamic) {
3343 void *p = nullptr;
3344 VmaAllocation a = toVmaAllocation(bufD->allocations[currentFrameSlot]);
3345 VkResult err = vmaMapMemory(toVmaAllocator(allocator), a, &p);
3346 if (err == VK_SUCCESS) {
3347 u.result->data.resize(u.readSize);
3348 memcpy(u.result->data.data(), reinterpret_cast<char *>(p) + u.offset, u.readSize);
3349 vmaUnmapMemory(toVmaAllocator(allocator), a);
3350 }
3351 if (u.result->completed)
3352 u.result->completed();
3353 } else {
3354 // Non-Dynamic buffers may not be host visible, so have to
3355 // create a readback buffer, enqueue a copy from
3356 // bufD->buffers[0] to this buffer, and then once the command
3357 // buffer completes, copy the data out of the host visible
3358 // readback buffer. Quite similar to what we do for texture
3359 // readbacks.
3360 BufferReadback readback;
3362 readback.result = u.result;
3363 readback.byteSize = u.readSize;
3364
3365 VkBufferCreateInfo bufferInfo = {};
3366 bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
3367 bufferInfo.size = readback.byteSize;
3368 bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
3369
3370 VmaAllocationCreateInfo allocInfo = {};
3371 allocInfo.usage = VMA_MEMORY_USAGE_GPU_TO_CPU;
3372
3373 VmaAllocation allocation;
3374 VkResult err = vmaCreateBuffer(toVmaAllocator(allocator), &bufferInfo, &allocInfo, &readback.stagingBuf, &allocation, nullptr);
3375 if (err == VK_SUCCESS) {
3376 readback.stagingAlloc = allocation;
3377 } else {
3378 qWarning("Failed to create readback buffer of size %u: %d", readback.byteSize, err);
3379 continue;
3380 }
3381
3382 trackedBufferBarrier(cbD, bufD, 0, VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
3383
3384 VkBufferCopy copyInfo = {};
3385 copyInfo.srcOffset = u.offset;
3386 copyInfo.size = u.readSize;
3387
3388 QVkCommandBuffer::Command &cmd(cbD->commands.get());
3390 cmd.args.copyBuffer.src = bufD->buffers[0];
3391 cmd.args.copyBuffer.dst = readback.stagingBuf;
3392 cmd.args.copyBuffer.desc = copyInfo;
3393
3395
3396 activeBufferReadbacks.append(readback);
3397 }
3398 }
3399 }
3400
3401 for (int opIdx = 0; opIdx < ud->activeTextureOpCount; ++opIdx) {
3404 QVkTexture *utexD = QRHI_RES(QVkTexture, u.dst);
3405 // batch into a single staging buffer and a single CopyBufferToImage with multiple copyInfos
3406 VkDeviceSize stagingSize = 0;
3407 for (int layer = 0, maxLayer = u.subresDesc.size(); layer < maxLayer; ++layer) {
3408 for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
3409 for (const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level]))
3410 stagingSize += subresUploadByteSize(subresDesc);
3411 }
3412 }
3413
3415 VkBufferCreateInfo bufferInfo = {};
3416 bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
3417 bufferInfo.size = stagingSize;
3418 bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
3419
3420 VmaAllocationCreateInfo allocInfo = {};
3421 allocInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
3422
3423 VmaAllocation allocation;
3424 VkResult err = vmaCreateBuffer(toVmaAllocator(allocator), &bufferInfo, &allocInfo,
3425 &utexD->stagingBuffers[currentFrameSlot], &allocation, nullptr);
3426 if (err != VK_SUCCESS) {
3427 qWarning("Failed to create image staging buffer of size %d: %d", int(stagingSize), err);
3428 continue;
3429 }
3431
3432 BufferImageCopyList copyInfos;
3433 size_t curOfs = 0;
3434 void *mp = nullptr;
3435 VmaAllocation a = toVmaAllocation(utexD->stagingAllocations[currentFrameSlot]);
3436 err = vmaMapMemory(toVmaAllocator(allocator), a, &mp);
3437 if (err != VK_SUCCESS) {
3438 qWarning("Failed to map image data: %d", err);
3439 continue;
3440 }
3441
3442 for (int layer = 0, maxLayer = u.subresDesc.size(); layer < maxLayer; ++layer) {
3443 for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
3445 if (srd.isEmpty())
3446 continue;
3447 for (const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(srd)) {
3449 subresDesc, &curOfs, mp, &copyInfos);
3450 }
3451 }
3452 }
3453 vmaFlushAllocation(toVmaAllocator(allocator), a, 0, stagingSize);
3454 vmaUnmapMemory(toVmaAllocator(allocator), a);
3455
3456 trackedImageBarrier(cbD, utexD, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
3457 VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
3458
3459 QVkCommandBuffer::Command &cmd(cbD->commands.get());
3462 cmd.args.copyBufferToImage.dst = utexD->image;
3463 cmd.args.copyBufferToImage.dstLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
3464 cmd.args.copyBufferToImage.count = copyInfos.size();
3466 cbD->pools.bufferImageCopy.append(copyInfos.constData(), copyInfos.size());
3467
3468 // no reuse of staging, this is intentional
3471 e.lastActiveFrameSlot = currentFrameSlot;
3472 e.stagingBuffer.stagingBuffer = utexD->stagingBuffers[currentFrameSlot];
3473 e.stagingBuffer.stagingAllocation = utexD->stagingAllocations[currentFrameSlot];
3474 utexD->stagingBuffers[currentFrameSlot] = VK_NULL_HANDLE;
3475 utexD->stagingAllocations[currentFrameSlot] = nullptr;
3476 releaseQueue.append(e);
3477
3478 // Similarly to buffers, transitioning away from DST is done later,
3479 // when a renderpass using the texture is encountered.
3480
3483 Q_ASSERT(u.src && u.dst);
3484 if (u.src == u.dst) {
3485 qWarning("Texture copy with matching source and destination is not supported");
3486 continue;
3487 }
3488 QVkTexture *srcD = QRHI_RES(QVkTexture, u.src);
3489 QVkTexture *dstD = QRHI_RES(QVkTexture, u.dst);
3490 const bool srcIs3D = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3491 const bool dstIs3D = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3492
3493 VkImageCopy region = {};
3494 region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
3495 region.srcSubresource.mipLevel = uint32_t(u.desc.sourceLevel());
3496 region.srcSubresource.baseArrayLayer = srcIs3D ? 0 : uint32_t(u.desc.sourceLayer());
3497 region.srcSubresource.layerCount = 1;
3498
3499 region.srcOffset.x = u.desc.sourceTopLeft().x();
3500 region.srcOffset.y = u.desc.sourceTopLeft().y();
3501 if (srcIs3D)
3502 region.srcOffset.z = uint32_t(u.desc.sourceLayer());
3503
3504 region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
3505 region.dstSubresource.mipLevel = uint32_t(u.desc.destinationLevel());
3506 region.dstSubresource.baseArrayLayer = dstIs3D ? 0 : uint32_t(u.desc.destinationLayer());
3507 region.dstSubresource.layerCount = 1;
3508
3509 region.dstOffset.x = u.desc.destinationTopLeft().x();
3510 region.dstOffset.y = u.desc.destinationTopLeft().y();
3511 if (dstIs3D)
3512 region.dstOffset.z = uint32_t(u.desc.destinationLayer());
3513
3514 const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize);
3515 const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize();
3516 region.extent.width = uint32_t(copySize.width());
3517 region.extent.height = uint32_t(copySize.height());
3518 region.extent.depth = 1;
3519
3520 trackedImageBarrier(cbD, srcD, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
3521 VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
3522 trackedImageBarrier(cbD, dstD, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
3523 VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
3524
3525 QVkCommandBuffer::Command &cmd(cbD->commands.get());
3527 cmd.args.copyImage.src = srcD->image;
3529 cmd.args.copyImage.dst = dstD->image;
3531 cmd.args.copyImage.desc = region;
3532
3535 TextureReadback readback;
3537 readback.desc = u.rb;
3538 readback.result = u.result;
3539
3540 QVkTexture *texD = QRHI_RES(QVkTexture, u.rb.texture());
3541 QVkSwapChain *swapChainD = nullptr;
3542 bool is3D = false;
3543 if (texD) {
3544 if (texD->samples > VK_SAMPLE_COUNT_1_BIT) {
3545 qWarning("Multisample texture cannot be read back");
3546 continue;
3547 }
3548 is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3549 readback.pixelSize = q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize);
3550 readback.format = texD->m_format;
3552 } else {
3554 swapChainD = QRHI_RES(QVkSwapChain, currentSwapChain);
3555 if (!swapChainD->supportsReadback) {
3556 qWarning("Swapchain does not support readback");
3557 continue;
3558 }
3559 readback.pixelSize = swapChainD->pixelSize;
3560 readback.format = swapchainReadbackTextureFormat(swapChainD->colorFormat, nullptr);
3561 if (readback.format == QRhiTexture::UnknownFormat)
3562 continue;
3563
3564 // Multisample swapchains need nothing special since resolving
3565 // happens when ending a renderpass.
3566 }
3567 textureFormatInfo(readback.format, readback.pixelSize, nullptr, &readback.byteSize, nullptr);
3568
3569 // Create a host visible readback buffer.
3570 VkBufferCreateInfo bufferInfo = {};
3571 bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
3572 bufferInfo.size = readback.byteSize;
3573 bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
3574
3575 VmaAllocationCreateInfo allocInfo = {};
3576 allocInfo.usage = VMA_MEMORY_USAGE_GPU_TO_CPU;
3577
3578 VmaAllocation allocation;
3579 VkResult err = vmaCreateBuffer(toVmaAllocator(allocator), &bufferInfo, &allocInfo, &readback.stagingBuf, &allocation, nullptr);
3580 if (err == VK_SUCCESS) {
3581 readback.stagingAlloc = allocation;
3582 } else {
3583 qWarning("Failed to create readback buffer of size %u: %d", readback.byteSize, err);
3584 continue;
3585 }
3586
3587 // Copy from the (optimal and not host visible) image into the buffer.
3588 VkBufferImageCopy copyDesc = {};
3589 copyDesc.bufferOffset = 0;
3590 copyDesc.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
3591 copyDesc.imageSubresource.mipLevel = uint32_t(u.rb.level());
3592 copyDesc.imageSubresource.baseArrayLayer = is3D ? 0 : uint32_t(u.rb.layer());
3593 copyDesc.imageSubresource.layerCount = 1;
3594 if (is3D)
3595 copyDesc.imageOffset.z = u.rb.layer();
3596 copyDesc.imageExtent.width = uint32_t(readback.pixelSize.width());
3597 copyDesc.imageExtent.height = uint32_t(readback.pixelSize.height());
3598 copyDesc.imageExtent.depth = 1;
3599
3600 if (texD) {
3601 trackedImageBarrier(cbD, texD, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
3602 VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
3603 QVkCommandBuffer::Command &cmd(cbD->commands.get());
3605 cmd.args.copyImageToBuffer.src = texD->image;
3607 cmd.args.copyImageToBuffer.dst = readback.stagingBuf;
3608 cmd.args.copyImageToBuffer.desc = copyDesc;
3609 } else {
3610 // use the swapchain image
3611 QVkSwapChain::ImageResources &imageRes(swapChainD->imageRes[swapChainD->currentImageIndex]);
3612 VkImage image = imageRes.image;
3615 qWarning("Attempted to read back undefined swapchain image content, "
3616 "results are undefined. (do a render pass first)");
3617 }
3619 VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
3620 VK_ACCESS_MEMORY_READ_BIT, VK_ACCESS_TRANSFER_READ_BIT,
3621 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
3622 0, 1,
3623 0, 1);
3625 }
3626
3627 QVkCommandBuffer::Command &cmd(cbD->commands.get());
3630 cmd.args.copyImageToBuffer.srcLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
3631 cmd.args.copyImageToBuffer.dst = readback.stagingBuf;
3632 cmd.args.copyImageToBuffer.desc = copyDesc;
3633 }
3634
3635 activeTextureReadbacks.append(readback);
3637 QVkTexture *utexD = QRHI_RES(QVkTexture, u.dst);
3639 const bool isCube = utexD->m_flags.testFlag(QRhiTexture::CubeMap);
3640 const bool isArray = utexD->m_flags.testFlag(QRhiTexture::TextureArray);
3641 const bool is3D = utexD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3642
3643 VkImageLayout origLayout = utexD->usageState.layout;
3644 VkAccessFlags origAccess = utexD->usageState.access;
3645 VkPipelineStageFlags origStage = utexD->usageState.stage;
3646 if (!origStage)
3647 origStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
3648
3649 for (int layer = 0; layer < (isCube ? 6 : (isArray ? qMax(0, utexD->m_arraySize) : 1)); ++layer) {
3650 int w = utexD->m_pixelSize.width();
3651 int h = utexD->m_pixelSize.height();
3652 int depth = is3D ? qMax(1, utexD->m_depth) : 1;
3653 for (int level = 1; level < int(utexD->mipLevelCount); ++level) {
3654 if (level == 1) {
3655 subresourceBarrier(cbD, utexD->image,
3656 origLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
3657 origAccess, VK_ACCESS_TRANSFER_READ_BIT,
3658 origStage, VK_PIPELINE_STAGE_TRANSFER_BIT,
3659 layer, 1,
3660 level - 1, 1);
3661 } else {
3662 subresourceBarrier(cbD, utexD->image,
3663 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
3664 VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
3665 VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
3666 layer, 1,
3667 level - 1, 1);
3668 }
3669
3670 subresourceBarrier(cbD, utexD->image,
3671 origLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
3672 origAccess, VK_ACCESS_TRANSFER_WRITE_BIT,
3673 origStage, VK_PIPELINE_STAGE_TRANSFER_BIT,
3674 layer, 1,
3675 level, 1);
3676
3677 VkImageBlit region = {};
3678 region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
3679 region.srcSubresource.mipLevel = uint32_t(level) - 1;
3680 region.srcSubresource.baseArrayLayer = uint32_t(layer);
3681 region.srcSubresource.layerCount = 1;
3682
3683 region.srcOffsets[1].x = qMax(1, w);
3684 region.srcOffsets[1].y = qMax(1, h);
3685 region.srcOffsets[1].z = qMax(1, depth);
3686
3687 region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
3688 region.dstSubresource.mipLevel = uint32_t(level);
3689 region.dstSubresource.baseArrayLayer = uint32_t(layer);
3690 region.dstSubresource.layerCount = 1;
3691
3692 region.dstOffsets[1].x = qMax(1, w >> 1);
3693 region.dstOffsets[1].y = qMax(1, h >> 1);
3694 region.dstOffsets[1].z = qMax(1, depth >> 1);
3695
3696 QVkCommandBuffer::Command &cmd(cbD->commands.get());
3698 cmd.args.blitImage.src = utexD->image;
3699 cmd.args.blitImage.srcLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
3700 cmd.args.blitImage.dst = utexD->image;
3701 cmd.args.blitImage.dstLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
3702 cmd.args.blitImage.filter = VK_FILTER_LINEAR;
3703 cmd.args.blitImage.desc = region;
3704
3705 w >>= 1;
3706 h >>= 1;
3707 depth >>= 1;
3708 }
3709
3710 if (utexD->mipLevelCount > 1) {
3711 subresourceBarrier(cbD, utexD->image,
3712 VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, origLayout,
3713 VK_ACCESS_TRANSFER_READ_BIT, origAccess,
3714 VK_PIPELINE_STAGE_TRANSFER_BIT, origStage,
3715 layer, 1,
3716 0, int(utexD->mipLevelCount) - 1);
3717 subresourceBarrier(cbD, utexD->image,
3718 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, origLayout,
3719 VK_ACCESS_TRANSFER_WRITE_BIT, origAccess,
3720 VK_PIPELINE_STAGE_TRANSFER_BIT, origStage,
3721 layer, 1,
3722 int(utexD->mipLevelCount) - 1, 1);
3723 }
3724 }
3726 }
3727 }
3728
3729 ud->free();
3730}
3731
3733{
3734 if (bufD->pendingDynamicUpdates[slot].isEmpty())
3735 return;
3736
3738 void *p = nullptr;
3739 VmaAllocation a = toVmaAllocation(bufD->allocations[slot]);
3740 // The vmaMap/Unmap are basically a no-op when persistently mapped since it
3741 // refcounts; this is great because we don't need to care if the allocation
3742 // was created as persistently mapped or not.
3743 VkResult err = vmaMapMemory(toVmaAllocator(allocator), a, &p);
3744 if (err != VK_SUCCESS) {
3745 qWarning("Failed to map buffer: %d", err);
3746 return;
3747 }
3748 quint32 changeBegin = UINT32_MAX;
3749 quint32 changeEnd = 0;
3750 for (const QVkBuffer::DynamicUpdate &u : std::as_const(bufD->pendingDynamicUpdates[slot])) {
3751 memcpy(static_cast<char *>(p) + u.offset, u.data.constData(), u.data.size());
3752 if (u.offset < changeBegin)
3753 changeBegin = u.offset;
3754 if (u.offset + u.data.size() > changeEnd)
3755 changeEnd = u.offset + u.data.size();
3756 }
3757 if (changeBegin < UINT32_MAX && changeBegin < changeEnd)
3758 vmaFlushAllocation(toVmaAllocator(allocator), a, changeBegin, changeEnd - changeBegin);
3759 vmaUnmapMemory(toVmaAllocator(allocator), a);
3760
3761 bufD->pendingDynamicUpdates[slot].clear();
3762}
3763
3764static void qrhivk_releaseBuffer(const QRhiVulkan::DeferredReleaseEntry &e, void *allocator)
3765{
3766 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
3767 vmaDestroyBuffer(toVmaAllocator(allocator), e.buffer.buffers[i], toVmaAllocation(e.buffer.allocations[i]));
3768 vmaDestroyBuffer(toVmaAllocator(allocator), e.buffer.stagingBuffers[i], toVmaAllocation(e.buffer.stagingAllocations[i]));
3769 }
3770}
3771
3773{
3774 df->vkDestroyImageView(dev, e.renderBuffer.imageView, nullptr);
3775 df->vkDestroyImage(dev, e.renderBuffer.image, nullptr);
3776 df->vkFreeMemory(dev, e.renderBuffer.memory, nullptr);
3777}
3778
3779static void qrhivk_releaseTexture(const QRhiVulkan::DeferredReleaseEntry &e, VkDevice dev, QVulkanDeviceFunctions *df, void *allocator)
3780{
3781 df->vkDestroyImageView(dev, e.texture.imageView, nullptr);
3782 vmaDestroyImage(toVmaAllocator(allocator), e.texture.image, toVmaAllocation(e.texture.allocation));
3783 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
3784 vmaDestroyBuffer(toVmaAllocator(allocator), e.texture.stagingBuffers[i], toVmaAllocation(e.texture.stagingAllocations[i]));
3785 for (int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i) {
3786 if (e.texture.extraImageViews[i])
3787 df->vkDestroyImageView(dev, e.texture.extraImageViews[i], nullptr);
3788 }
3789}
3790
3792{
3793 df->vkDestroySampler(dev, e.sampler.sampler, nullptr);
3794}
3795
3797{
3798 for (int i = releaseQueue.size() - 1; i >= 0; --i) {
3800 if (forced || currentFrameSlot == e.lastActiveFrameSlot || e.lastActiveFrameSlot < 0) {
3801 switch (e.type) {
3803 df->vkDestroyPipeline(dev, e.pipelineState.pipeline, nullptr);
3804 df->vkDestroyPipelineLayout(dev, e.pipelineState.layout, nullptr);
3805 break;
3807 df->vkDestroyDescriptorSetLayout(dev, e.shaderResourceBindings.layout, nullptr);
3808 if (e.shaderResourceBindings.poolIndex >= 0) {
3809 descriptorPools[e.shaderResourceBindings.poolIndex].refCount -= 1;
3810 Q_ASSERT(descriptorPools[e.shaderResourceBindings.poolIndex].refCount >= 0);
3811 }
3812 break;
3815 break;
3818 break;
3821 break;
3824 break;
3826 df->vkDestroyFramebuffer(dev, e.textureRenderTarget.fb, nullptr);
3827 for (int att = 0; att < QVkRenderTargetData::MAX_COLOR_ATTACHMENTS; ++att) {
3828 df->vkDestroyImageView(dev, e.textureRenderTarget.rtv[att], nullptr);
3829 df->vkDestroyImageView(dev, e.textureRenderTarget.resrtv[att], nullptr);
3830 }
3831 break;
3833 df->vkDestroyRenderPass(dev, e.renderPass.rp, nullptr);
3834 break;
3836 vmaDestroyBuffer(toVmaAllocator(allocator), e.stagingBuffer.stagingBuffer, toVmaAllocation(e.stagingBuffer.stagingAllocation));
3837 break;
3839 freeSecondaryCbs[e.lastActiveFrameSlot].append(e.secondaryCommandBuffer.cb);
3840 break;
3841 default:
3842 Q_UNREACHABLE();
3843 break;
3844 }
3845 releaseQueue.removeAt(i);
3846 }
3847 }
3848}
3849
3851{
3852 QVarLengthArray<std::function<void()>, 4> completedCallbacks;
3853
3854 for (int i = activeTextureReadbacks.size() - 1; i >= 0; --i) {
3856 if (forced || currentFrameSlot == readback.activeFrameSlot || readback.activeFrameSlot < 0) {
3857 readback.result->format = readback.format;
3858 readback.result->pixelSize = readback.pixelSize;
3859 VmaAllocation a = toVmaAllocation(readback.stagingAlloc);
3860 void *p = nullptr;
3861 VkResult err = vmaMapMemory(toVmaAllocator(allocator), a, &p);
3862 if (err == VK_SUCCESS && p) {
3863 readback.result->data.resize(int(readback.byteSize));
3864 memcpy(readback.result->data.data(), p, readback.byteSize);
3865 vmaUnmapMemory(toVmaAllocator(allocator), a);
3866 } else {
3867 qWarning("Failed to map texture readback buffer of size %u: %d", readback.byteSize, err);
3868 }
3869
3870 vmaDestroyBuffer(toVmaAllocator(allocator), readback.stagingBuf, a);
3871
3872 if (readback.result->completed)
3873 completedCallbacks.append(readback.result->completed);
3874
3875 activeTextureReadbacks.removeLast();
3876 }
3877 }
3878
3879 for (int i = activeBufferReadbacks.size() - 1; i >= 0; --i) {
3881 if (forced || currentFrameSlot == readback.activeFrameSlot || readback.activeFrameSlot < 0) {
3882 VmaAllocation a = toVmaAllocation(readback.stagingAlloc);
3883 void *p = nullptr;
3884 VkResult err = vmaMapMemory(toVmaAllocator(allocator), a, &p);
3885 if (err == VK_SUCCESS && p) {
3886 readback.result->data.resize(readback.byteSize);
3887 memcpy(readback.result->data.data(), p, readback.byteSize);
3888 vmaUnmapMemory(toVmaAllocator(allocator), a);
3889 } else {
3890 qWarning("Failed to map buffer readback buffer of size %d: %d", readback.byteSize, err);
3891 }
3892
3893 vmaDestroyBuffer(toVmaAllocator(allocator), readback.stagingBuf, a);
3894
3895 if (readback.result->completed)
3896 completedCallbacks.append(readback.result->completed);
3897
3898 activeBufferReadbacks.removeLast();
3899 }
3900 }
3901
3902 for (auto f : completedCallbacks)
3903 f();
3904}
3905
3906static struct {
3907 VkSampleCountFlagBits mask;
3909} qvk_sampleCounts[] = {
3910 // keep this sorted by 'count'
3911 { VK_SAMPLE_COUNT_1_BIT, 1 },
3912 { VK_SAMPLE_COUNT_2_BIT, 2 },
3913 { VK_SAMPLE_COUNT_4_BIT, 4 },
3914 { VK_SAMPLE_COUNT_8_BIT, 8 },
3915 { VK_SAMPLE_COUNT_16_BIT, 16 },
3916 { VK_SAMPLE_COUNT_32_BIT, 32 },
3917 { VK_SAMPLE_COUNT_64_BIT, 64 }
3919
3921{
3922 const VkPhysicalDeviceLimits *limits = &physDevProperties.limits;
3923 VkSampleCountFlags color = limits->framebufferColorSampleCounts;
3924 VkSampleCountFlags depth = limits->framebufferDepthSampleCounts;
3925 VkSampleCountFlags stencil = limits->framebufferStencilSampleCounts;
3927
3928 for (const auto &qvk_sampleCount : qvk_sampleCounts) {
3929 if ((color & qvk_sampleCount.mask)
3930 && (depth & qvk_sampleCount.mask)
3931 && (stencil & qvk_sampleCount.mask))
3932 {
3933 result.append(qvk_sampleCount.count);
3934 }
3935 }
3936
3937 return result;
3938}
3939
3940VkSampleCountFlagBits QRhiVulkan::effectiveSampleCount(int sampleCount)
3941{
3942 // Stay compatible with QSurfaceFormat and friends where samples == 0 means the same as 1.
3943 sampleCount = qBound(1, sampleCount, 64);
3944
3945 if (!supportedSampleCounts().contains(sampleCount)) {
3946 qWarning("Attempted to set unsupported sample count %d", sampleCount);
3947 return VK_SAMPLE_COUNT_1_BIT;
3948 }
3949
3950 for (const auto &qvk_sampleCount : qvk_sampleCounts) {
3951 if (qvk_sampleCount.count == sampleCount)
3952 return qvk_sampleCount.mask;
3953 }
3954
3955 Q_UNREACHABLE_RETURN(VK_SAMPLE_COUNT_1_BIT);
3956}
3957
3959{
3962
3963 QVkCommandBuffer::Command &cmd(cbD->commands.get());
3966}
3967
3969{
3971
3972 for (auto it = cbD->commands.begin(), end = cbD->commands.end(); it != end; ++it) {
3974 switch (cmd.cmd) {
3976 df->vkCmdCopyBuffer(cbD->cb, cmd.args.copyBuffer.src, cmd.args.copyBuffer.dst,
3977 1, &cmd.args.copyBuffer.desc);
3978 break;
3980 df->vkCmdCopyBufferToImage(cbD->cb, cmd.args.copyBufferToImage.src, cmd.args.copyBufferToImage.dst,
3982 uint32_t(cmd.args.copyBufferToImage.count),
3984 break;
3986 df->vkCmdCopyImage(cbD->cb, cmd.args.copyImage.src, cmd.args.copyImage.srcLayout,
3988 1, &cmd.args.copyImage.desc);
3989 break;
3991 df->vkCmdCopyImageToBuffer(cbD->cb, cmd.args.copyImageToBuffer.src, cmd.args.copyImageToBuffer.srcLayout,
3993 1, &cmd.args.copyImageToBuffer.desc);
3994 break;
3996 df->vkCmdPipelineBarrier(cbD->cb, cmd.args.imageBarrier.srcStageMask, cmd.args.imageBarrier.dstStageMask,
3997 0, 0, nullptr, 0, nullptr,
3999 break;
4001 df->vkCmdPipelineBarrier(cbD->cb, cmd.args.bufferBarrier.srcStageMask, cmd.args.bufferBarrier.dstStageMask,
4002 0, 0, nullptr,
4004 0, nullptr);
4005 break;
4007 df->vkCmdBlitImage(cbD->cb, cmd.args.blitImage.src, cmd.args.blitImage.srcLayout,
4009 1, &cmd.args.blitImage.desc,
4010 cmd.args.blitImage.filter);
4011 break;
4014 df->vkCmdBeginRenderPass(cbD->cb, &cmd.args.beginRenderPass.desc,
4015 cmd.args.beginRenderPass.useSecondaryCb ? VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS
4016 : VK_SUBPASS_CONTENTS_INLINE);
4017 break;
4019 df->vkCmdEndRenderPass(cbD->cb);
4020 break;
4022 df->vkCmdBindPipeline(cbD->cb, cmd.args.bindPipeline.bindPoint, cmd.args.bindPipeline.pipeline);
4023 break;
4025 {
4026 const uint32_t *offsets = nullptr;
4029 df->vkCmdBindDescriptorSets(cbD->cb, cmd.args.bindDescriptorSet.bindPoint,
4031 0, 1, &cmd.args.bindDescriptorSet.descSet,
4033 offsets);
4034 }
4035 break;
4037 df->vkCmdBindVertexBuffers(cbD->cb, uint32_t(cmd.args.bindVertexBuffer.startBinding),
4038 uint32_t(cmd.args.bindVertexBuffer.count),
4041 break;
4043 df->vkCmdBindIndexBuffer(cbD->cb, cmd.args.bindIndexBuffer.buf,
4045 break;
4047 df->vkCmdSetViewport(cbD->cb, 0, 1, &cmd.args.setViewport.viewport);
4048 break;
4050 df->vkCmdSetScissor(cbD->cb, 0, 1, &cmd.args.setScissor.scissor);
4051 break;
4053 df->vkCmdSetBlendConstants(cbD->cb, cmd.args.setBlendConstants.c);
4054 break;
4056 df->vkCmdSetStencilReference(cbD->cb, VK_STENCIL_FRONT_AND_BACK, cmd.args.setStencilRef.ref);
4057 break;
4059 df->vkCmdDraw(cbD->cb, cmd.args.draw.vertexCount, cmd.args.draw.instanceCount,
4061 break;
4063 df->vkCmdDrawIndexed(cbD->cb, cmd.args.drawIndexed.indexCount, cmd.args.drawIndexed.instanceCount,
4066 break;
4068#ifdef VK_EXT_debug_utils
4069 cmd.args.debugMarkerBegin.label.pLabelName =
4070 cbD->pools.debugMarkerData[cmd.args.debugMarkerBegin.labelNameIndex].constData();
4071 vkCmdBeginDebugUtilsLabelEXT(cbD->cb, &cmd.args.debugMarkerBegin.label);
4072#endif
4073 break;
4075#ifdef VK_EXT_debug_utils
4076 vkCmdEndDebugUtilsLabelEXT(cbD->cb);
4077#endif
4078 break;
4080#ifdef VK_EXT_debug_utils
4081 cmd.args.debugMarkerInsert.label.pLabelName =
4082 cbD->pools.debugMarkerData[cmd.args.debugMarkerInsert.labelNameIndex].constData();
4083 vkCmdInsertDebugUtilsLabelEXT(cbD->cb, &cmd.args.debugMarkerInsert.label);
4084#endif
4085 break;
4088 break;
4090 df->vkCmdDispatch(cbD->cb, uint32_t(cmd.args.dispatch.x), uint32_t(cmd.args.dispatch.y), uint32_t(cmd.args.dispatch.z));
4091 break;
4093 df->vkCmdExecuteCommands(cbD->cb, 1, &cmd.args.executeSecondary.cb);
4094 break;
4095 default:
4096 break;
4097 }
4098 }
4099}
4100
4102{
4103 switch (access) {
4105 return VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT;
4107 return VK_ACCESS_INDEX_READ_BIT;
4109 return VK_ACCESS_UNIFORM_READ_BIT;
4111 return VK_ACCESS_SHADER_READ_BIT;
4113 return VK_ACCESS_SHADER_WRITE_BIT;
4115 return VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
4116 default:
4117 Q_UNREACHABLE();
4118 break;
4119 }
4120 return 0;
4121}
4122
4123static inline VkPipelineStageFlags toVkPipelineStage(QRhiPassResourceTracker::BufferStage stage)
4124{
4125 switch (stage) {
4127 return VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
4129 return VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
4131 return VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT;
4133 return VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT;
4135 return VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
4137 return VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
4139 return VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT;
4140 default:
4141 Q_UNREACHABLE();
4142 break;
4143 }
4144 return 0;
4145}
4146
4148{
4150 u.access = VkAccessFlags(usage.access);
4151 u.stage = VkPipelineStageFlags(usage.stage);
4152 return u;
4153}
4154
4156{
4157 switch (access) {
4159 return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
4161 return VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
4163 return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
4167 return VK_IMAGE_LAYOUT_GENERAL;
4168 default:
4169 Q_UNREACHABLE();
4170 break;
4171 }
4172 return VK_IMAGE_LAYOUT_GENERAL;
4173}
4174
4176{
4177 switch (access) {
4179 return VK_ACCESS_SHADER_READ_BIT;
4181 return VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
4183 return VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
4185 return VK_ACCESS_SHADER_READ_BIT;
4187 return VK_ACCESS_SHADER_WRITE_BIT;
4189 return VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
4190 default:
4191 Q_UNREACHABLE();
4192 break;
4193 }
4194 return 0;
4195}
4196
4197static inline VkPipelineStageFlags toVkPipelineStage(QRhiPassResourceTracker::TextureStage stage)
4198{
4199 switch (stage) {
4201 return VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
4203 return VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT;
4205 return VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT;
4207 return VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
4209 return VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
4211 return VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
4213 return VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
4215 return VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT;
4216 default:
4217 Q_UNREACHABLE();
4218 break;
4219 }
4220 return 0;
4221}
4222
4224{
4226 u.layout = VkImageLayout(usage.layout);
4227 u.access = VkAccessFlags(usage.access);
4228 u.stage = VkPipelineStageFlags(usage.stage);
4229 return u;
4230}
4231
4233 QVkBuffer *bufD,
4234 int slot,
4237{
4238 QVkBuffer::UsageState &u(bufD->usageState[slot]);
4239 const VkAccessFlags newAccess = toVkAccess(access);
4240 const VkPipelineStageFlags newStage = toVkPipelineStage(stage);
4241 if (u.access == newAccess && u.stage == newStage) {
4242 if (!accessIsWrite(access))
4243 return;
4244 }
4245 passResTracker->registerBuffer(bufD, slot, &access, &stage, toPassTrackerUsageState(u));
4246 u.access = newAccess;
4247 u.stage = newStage;
4248}
4249
4251 QVkTexture *texD,
4254{
4256 const VkAccessFlags newAccess = toVkAccess(access);
4257 const VkPipelineStageFlags newStage = toVkPipelineStage(stage);
4258 const VkImageLayout newLayout = toVkLayout(access);
4259 if (u.access == newAccess && u.stage == newStage && u.layout == newLayout) {
4260 if (!accessIsWrite(access))
4261 return;
4262 }
4263 passResTracker->registerTexture(texD, &access, &stage, toPassTrackerUsageState(u));
4264 u.layout = newLayout;
4265 u.access = newAccess;
4266 u.stage = newStage;
4267}
4268
4270{
4271 if (tracker.isEmpty())
4272 return;
4273
4274 for (auto it = tracker.cbeginBuffers(), itEnd = tracker.cendBuffers(); it != itEnd; ++it) {
4275 QVkBuffer *bufD = QRHI_RES(QVkBuffer, it.key());
4276 VkAccessFlags access = toVkAccess(it->access);
4277 VkPipelineStageFlags stage = toVkPipelineStage(it->stage);
4278 QVkBuffer::UsageState s = toVkBufferUsageState(it->stateAtPassBegin);
4279 if (!s.stage)
4280 continue;
4281 if (s.access == access && s.stage == stage) {
4282 if (!accessIsWrite(access))
4283 continue;
4284 }
4285 VkBufferMemoryBarrier bufMemBarrier = {};
4286 bufMemBarrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
4287 bufMemBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
4288 bufMemBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
4289 bufMemBarrier.srcAccessMask = s.access;
4290 bufMemBarrier.dstAccessMask = access;
4291 bufMemBarrier.buffer = bufD->buffers[it->slot];
4292 bufMemBarrier.size = VK_WHOLE_SIZE;
4293 df->vkCmdPipelineBarrier(cbD->cb, s.stage, stage, 0,
4294 0, nullptr,
4295 1, &bufMemBarrier,
4296 0, nullptr);
4297 }
4298
4299 for (auto it = tracker.cbeginTextures(), itEnd = tracker.cendTextures(); it != itEnd; ++it) {
4300 QVkTexture *texD = QRHI_RES(QVkTexture, it.key());
4301 VkImageLayout layout = toVkLayout(it->access);
4302 VkAccessFlags access = toVkAccess(it->access);
4303 VkPipelineStageFlags stage = toVkPipelineStage(it->stage);
4304 QVkTexture::UsageState s = toVkTextureUsageState(it->stateAtPassBegin);
4305 if (s.access == access && s.stage == stage && s.layout == layout) {
4306 if (!accessIsWrite(access))
4307 continue;
4308 }
4309 VkImageMemoryBarrier barrier = {};
4310 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
4311 barrier.subresourceRange.aspectMask = aspectMaskForTextureFormat(texD->m_format);
4312 barrier.subresourceRange.baseMipLevel = 0;
4313 barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
4314 barrier.subresourceRange.baseArrayLayer = 0;
4315 barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
4316 barrier.oldLayout = s.layout; // new textures have this set to PREINITIALIZED
4317 barrier.newLayout = layout;
4318 barrier.srcAccessMask = s.access; // may be 0 but that's fine
4319 barrier.dstAccessMask = access;
4320 barrier.image = texD->image;
4321 VkPipelineStageFlags srcStage = s.stage;
4322 // stage mask cannot be 0
4323 if (!srcStage)
4324 srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
4325 df->vkCmdPipelineBarrier(cbD->cb, srcStage, stage, 0,
4326 0, nullptr,
4327 0, nullptr,
4328 1, &barrier);
4329 }
4330}
4331
4333{
4337 {
4338 qWarning("Physical device surface queries not available");
4339 return nullptr;
4340 }
4341
4342 return new QVkSwapChain(this);
4343}
4344
4346{
4347 return new QVkBuffer(this, type, usage, size);
4348}
4349
4351{
4352 return int(ubufAlign); // typically 256 (bytes)
4353}
4354
4356{
4357 return false;
4358}
4359
4361{
4362 return false;
4363}
4364
4366{
4367 return true;
4368}
4369
4371{
4372 // See https://matthewwellings.com/blog/the-new-vulkan-coordinate-system/
4373
4374 static QMatrix4x4 m;
4375 if (m.isIdentity()) {
4376 // NB the ctor takes row-major
4377 m = QMatrix4x4(1.0f, 0.0f, 0.0f, 0.0f,
4378 0.0f, -1.0f, 0.0f, 0.0f,
4379 0.0f, 0.0f, 0.5f, 0.5f,
4380 0.0f, 0.0f, 0.0f, 1.0f);
4381 }
4382 return m;
4383}
4384
4386{
4387 // Note that with some SDKs the validation layer gives an odd warning about
4388 // BC not being supported, even when our check here succeeds. Not much we
4389 // can do about that.
4391 if (!physDevFeatures.textureCompressionBC)
4392 return false;
4393 }
4394
4396 if (!physDevFeatures.textureCompressionETC2)
4397 return false;
4398 }
4399
4401 if (!physDevFeatures.textureCompressionASTC_LDR)
4402 return false;
4403 }
4404
4405 VkFormat vkformat = toVkTextureFormat(format, flags);
4406 VkFormatProperties props;
4407 f->vkGetPhysicalDeviceFormatProperties(physDev, vkformat, &props);
4408 return (props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) != 0;
4409}
4410
4412{
4413 switch (feature) {
4415 return true;
4417 return true;
4418 case QRhi::DebugMarkers:
4419 return caps.debugUtils;
4420 case QRhi::Timestamps:
4421 return timestampValidBits != 0;
4422 case QRhi::Instancing:
4423 return true;
4425 return caps.vertexAttribDivisor;
4427 return true;
4429 return true;
4431 return true;
4433 return true;
4435 return true;
4437 return true;
4438 case QRhi::Compute:
4439 return caps.compute;
4440 case QRhi::WideLines:
4441 return caps.wideLines;
4443 return true;
4444 case QRhi::BaseVertex:
4445 return true;
4446 case QRhi::BaseInstance:
4447 return true;
4449 return true;
4451 return true;
4453 return true;
4454 case QRhi::TexelFetch:
4455 return true;
4457 return true;
4459 return true;
4461 return true;
4463 return true;
4465 return true;
4467 return true;
4469 return false;
4471 return true;
4473 return caps.texture3DSliceAs2D;
4475 return true;
4476 case QRhi::Tessellation:
4477 return caps.tessellation;
4479 return caps.geometryShader;
4481 return true;
4483 return caps.nonFillPolygonMode;
4485 return true;
4487 return true;
4489 return true;
4491 return true;
4493 return true;
4494 case QRhi::MultiView:
4495 return caps.multiView;
4496 default:
4497 Q_UNREACHABLE_RETURN(false);
4498 }
4499}
4500
4502{
4503 switch (limit) {
4505 return 1;
4507 return int(physDevProperties.limits.maxImageDimension2D);
4509 return int(physDevProperties.limits.maxColorAttachments);
4511 return QVK_FRAMES_IN_FLIGHT;
4513 return QVK_FRAMES_IN_FLIGHT;
4515 return int(qMin(physDevProperties.limits.maxComputeWorkGroupCount[0],
4516 qMin(physDevProperties.limits.maxComputeWorkGroupCount[1],
4517 physDevProperties.limits.maxComputeWorkGroupCount[2])));
4519 return int(physDevProperties.limits.maxComputeWorkGroupInvocations);
4521 return int(physDevProperties.limits.maxComputeWorkGroupSize[0]);
4523 return int(physDevProperties.limits.maxComputeWorkGroupSize[1]);
4525 return int(physDevProperties.limits.maxComputeWorkGroupSize[2]);
4527 return int(physDevProperties.limits.maxImageArrayLayers);
4529 return int(qMin<uint32_t>(INT_MAX, physDevProperties.limits.maxUniformBufferRange));
4531 return physDevProperties.limits.maxVertexInputAttributes;
4533 return physDevProperties.limits.maxVertexOutputComponents / 4;
4534 default:
4535 Q_UNREACHABLE_RETURN(0);
4536 }
4537}
4538
4540{
4541 return &nativeHandlesStruct;
4542}
4543
4545{
4546 return driverInfoStruct;
4547}
4548
4550{
4552 result.totalPipelineCreationTime = totalPipelineCreationTime();
4553
4554 VmaBudget budgets[VK_MAX_MEMORY_HEAPS];
4555 vmaGetHeapBudgets(toVmaAllocator(allocator), budgets);
4556
4557 uint32_t count = toVmaAllocator(allocator)->GetMemoryHeapCount();
4558 for (uint32_t i = 0; i < count; ++i) {
4559 const VmaStatistics &stats(budgets[i].statistics);
4560 result.blockCount += stats.blockCount;
4561 result.allocCount += stats.allocationCount;
4562 result.usedBytes += stats.allocationBytes;
4563 result.unusedBytes += stats.blockBytes - stats.allocationBytes;
4564 }
4565
4566 return result;
4567}
4568
4570{
4571 // not applicable
4572 return false;
4573}
4574
4576{
4578}
4579
4581{
4582 return deviceLost;
4583}
4584
4586{
4595};
4596
4598{
4600
4603 return data;
4604
4605 size_t dataSize = 0;
4606 VkResult err = df->vkGetPipelineCacheData(dev, pipelineCache, &dataSize, nullptr);
4607 if (err != VK_SUCCESS) {
4608 qCDebug(QRHI_LOG_INFO, "Failed to get pipeline cache data size: %d", err);
4609 return QByteArray();
4610 }
4611 const size_t headerSize = sizeof(QVkPipelineCacheDataHeader);
4612 const size_t dataOffset = headerSize + VK_UUID_SIZE;
4613 data.resize(dataOffset + dataSize);
4614 err = df->vkGetPipelineCacheData(dev, pipelineCache, &dataSize, data.data() + dataOffset);
4615 if (err != VK_SUCCESS) {
4616 qCDebug(QRHI_LOG_INFO, "Failed to get pipeline cache data of %d bytes: %d", int(dataSize), err);
4617 return QByteArray();
4618 }
4619
4621 header.rhiId = pipelineCacheRhiId();
4622 header.arch = quint32(sizeof(void*));
4623 header.driverVersion = physDevProperties.driverVersion;
4624 header.vendorId = physDevProperties.vendorID;
4625 header.deviceId = physDevProperties.deviceID;
4626 header.dataSize = quint32(dataSize);
4627 header.uuidSize = VK_UUID_SIZE;
4628 memcpy(data.data(), &header, headerSize);
4629 memcpy(data.data() + headerSize, physDevProperties.pipelineCacheUUID, VK_UUID_SIZE);
4630
4631 return data;
4632}
4633
4635{
4636 if (data.isEmpty())
4637 return;
4638
4639 const size_t headerSize = sizeof(QVkPipelineCacheDataHeader);
4640 if (data.size() < qsizetype(headerSize)) {
4641 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob size");
4642 return;
4643 }
4645 memcpy(&header, data.constData(), headerSize);
4646
4647 const quint32 rhiId = pipelineCacheRhiId();
4648 if (header.rhiId != rhiId) {
4649 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: The data is for a different QRhi version or backend (%u, %u)",
4650 rhiId, header.rhiId);
4651 return;
4652 }
4653 const quint32 arch = quint32(sizeof(void*));
4654 if (header.arch != arch) {
4655 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Architecture does not match (%u, %u)",
4656 arch, header.arch);
4657 return;
4658 }
4659 if (header.driverVersion != physDevProperties.driverVersion) {
4660 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: driverVersion does not match (%u, %u)",
4661 physDevProperties.driverVersion, header.driverVersion);
4662 return;
4663 }
4664 if (header.vendorId != physDevProperties.vendorID) {
4665 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: vendorID does not match (%u, %u)",
4666 physDevProperties.vendorID, header.vendorId);
4667 return;
4668 }
4669 if (header.deviceId != physDevProperties.deviceID) {
4670 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: deviceID does not match (%u, %u)",
4671 physDevProperties.deviceID, header.deviceId);
4672 return;
4673 }
4674 if (header.uuidSize != VK_UUID_SIZE) {
4675 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: VK_UUID_SIZE does not match (%u, %u)",
4676 quint32(VK_UUID_SIZE), header.uuidSize);
4677 return;
4678 }
4679
4680 if (data.size() < qsizetype(headerSize + VK_UUID_SIZE)) {
4681 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob, no uuid");
4682 return;
4683 }
4684 if (memcmp(data.constData() + headerSize, physDevProperties.pipelineCacheUUID, VK_UUID_SIZE)) {
4685 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: pipelineCacheUUID does not match");
4686 return;
4687 }
4688
4689 const size_t dataOffset = headerSize + VK_UUID_SIZE;
4690 if (data.size() < qsizetype(dataOffset + header.dataSize)) {
4691 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob, data missing");
4692 return;
4693 }
4694
4695 if (pipelineCache) {
4696 df->vkDestroyPipelineCache(dev, pipelineCache, nullptr);
4697 pipelineCache = VK_NULL_HANDLE;
4698 }
4699
4700 if (ensurePipelineCache(data.constData() + dataOffset, header.dataSize)) {
4701 qCDebug(QRHI_LOG_INFO, "Created pipeline cache with initial data of %d bytes",
4702 int(header.dataSize));
4703 } else {
4704 qCDebug(QRHI_LOG_INFO, "Failed to create pipeline cache with initial data specified");
4705 }
4706}
4707
4709 int sampleCount, QRhiRenderBuffer::Flags flags,
4710 QRhiTexture::Format backingFormatHint)
4711{
4712 return new QVkRenderBuffer(this, type, pixelSize, sampleCount, flags, backingFormatHint);
4713}
4714
4716 const QSize &pixelSize, int depth, int arraySize,
4717 int sampleCount, QRhiTexture::Flags flags)
4718{
4719 return new QVkTexture(this, format, pixelSize, depth, arraySize, sampleCount, flags);
4720}
4721
4723 QRhiSampler::Filter mipmapMode,
4725{
4726 return new QVkSampler(this, magFilter, minFilter, mipmapMode, u, v, w);
4727}
4728
4730 QRhiTextureRenderTarget::Flags flags)
4731{
4732 return new QVkTextureRenderTarget(this, desc, flags);
4733}
4734
4736{
4737 return new QVkGraphicsPipeline(this);
4738}
4739
4741{
4742 return new QVkComputePipeline(this);
4743}
4744
4746{
4747 return new QVkShaderResourceBindings(this);
4748}
4749
4751{
4753 Q_ASSERT(psD->pipeline);
4756
4757 if (cbD->currentGraphicsPipeline != ps || cbD->currentPipelineGeneration != psD->generation) {
4758 if (cbD->passUsesSecondaryCb) {
4759 df->vkCmdBindPipeline(cbD->activeSecondaryCbStack.last(), VK_PIPELINE_BIND_POINT_GRAPHICS, psD->pipeline);
4760 } else {
4761 QVkCommandBuffer::Command &cmd(cbD->commands.get());
4763 cmd.args.bindPipeline.bindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
4765 }
4766
4767 cbD->currentGraphicsPipeline = ps;
4768 cbD->currentComputePipeline = nullptr;
4770 }
4771
4773}
4774
4776 int dynamicOffsetCount,
4777 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
4778{
4784
4785 if (!srb) {
4786 if (gfxPsD)
4787 srb = gfxPsD->m_shaderResourceBindings;
4788 else
4789 srb = compPsD->m_shaderResourceBindings;
4790 }
4791
4793 const int descSetIdx = srbD->hasSlottedResource ? currentFrameSlot : 0;
4794 auto &descSetBd(srbD->boundResourceData[descSetIdx]);
4795 bool rewriteDescSet = false;
4796
4797 // Do host writes and mark referenced shader resources as in-use.
4798 // Also prepare to ensure the descriptor set we are going to bind refers to up-to-date Vk objects.
4799 for (int i = 0, ie = srbD->sortedBindings.size(); i != ie; ++i) {
4802 switch (b->type) {
4804 {
4805 QVkBuffer *bufD = QRHI_RES(QVkBuffer, b->u.ubuf.buf);
4807
4808 if (bufD->m_type == QRhiBuffer::Dynamic)
4810
4812 trackedRegisterBuffer(&passResTracker, bufD, bufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0,
4815
4816 // Check both the "local" id (the generation counter) and the
4817 // global id. The latter is relevant when a newly allocated
4818 // QRhiResource ends up with the same pointer as a previous one.
4819 // (and that previous one could have been in an srb...)
4820 if (bufD->generation != bd.ubuf.generation || bufD->m_id != bd.ubuf.id) {
4821 rewriteDescSet = true;
4822 bd.ubuf.id = bufD->m_id;
4823 bd.ubuf.generation = bufD->generation;
4824 }
4825 }
4826 break;
4830 {
4832 if (bd.stex.count != data->count) {
4833 bd.stex.count = data->count;
4834 rewriteDescSet = true;
4835 }
4836 for (int elem = 0; elem < data->count; ++elem) {
4837 QVkTexture *texD = QRHI_RES(QVkTexture, data->texSamplers[elem].tex);
4838 QVkSampler *samplerD = QRHI_RES(QVkSampler, data->texSamplers[elem].sampler);
4839 // We use the same code path for both combined and separate
4840 // images and samplers, so tex or sampler (but not both) can be
4841 // null here.
4842 Q_ASSERT(texD || samplerD);
4843 if (texD) {
4845 trackedRegisterTexture(&passResTracker, texD,
4848 }
4849 if (samplerD)
4851 const quint64 texId = texD ? texD->m_id : 0;
4852 const uint texGen = texD ? texD->generation : 0;
4853 const quint64 samplerId = samplerD ? samplerD->m_id : 0;
4854 const uint samplerGen = samplerD ? samplerD->generation : 0;
4855 if (texGen != bd.stex.d[elem].texGeneration
4856 || texId != bd.stex.d[elem].texId
4857 || samplerGen != bd.stex.d[elem].samplerGeneration
4858 || samplerId != bd.stex.d[elem].samplerId)
4859 {
4860 rewriteDescSet = true;
4861 bd.stex.d[elem].texId = texId;
4862 bd.stex.d[elem].texGeneration = texGen;
4863 bd.stex.d[elem].samplerId = samplerId;
4864 bd.stex.d[elem].samplerGeneration = samplerGen;
4865 }
4866 }
4867 }
4868 break;
4872 {
4873 QVkTexture *texD = QRHI_RES(QVkTexture, b->u.simage.tex);
4879 else if (b->type == QRhiShaderResourceBinding::ImageStore)
4881 else
4883 trackedRegisterTexture(&passResTracker, texD,
4884 access,
4886
4887 if (texD->generation != bd.simage.generation || texD->m_id != bd.simage.id) {
4888 rewriteDescSet = true;
4889 bd.simage.id = texD->m_id;
4890 bd.simage.generation = texD->generation;
4891 }
4892 }
4893 break;
4897 {
4898 QVkBuffer *bufD = QRHI_RES(QVkBuffer, b->u.sbuf.buf);
4900
4901 if (bufD->m_type == QRhiBuffer::Dynamic)
4903
4908 else if (b->type == QRhiShaderResourceBinding::BufferStore)
4910 else
4912 trackedRegisterBuffer(&passResTracker, bufD, bufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0,
4913 access,
4915
4916 if (bufD->generation != bd.sbuf.generation || bufD->m_id != bd.sbuf.id) {
4917 rewriteDescSet = true;
4918 bd.sbuf.id = bufD->m_id;
4919 bd.sbuf.generation = bufD->generation;
4920 }
4921 }
4922 break;
4923 default:
4924 Q_UNREACHABLE();
4925 break;
4926 }
4927 }
4928
4929 // write descriptor sets, if needed
4930 if (rewriteDescSet)
4931 updateShaderResourceBindings(srb, descSetIdx);
4932
4933 // make sure the descriptors for the correct slot will get bound.
4934 // also, dynamic offsets always need a bind.
4935 const bool forceRebind = (srbD->hasSlottedResource && cbD->currentDescSetSlot != descSetIdx) || srbD->hasDynamicOffset;
4936
4937 const bool srbChanged = gfxPsD ? (cbD->currentGraphicsSrb != srb) : (cbD->currentComputeSrb != srb);
4938
4939 if (forceRebind || rewriteDescSet || srbChanged || cbD->currentSrbGeneration != srbD->generation) {
4941 if (srbD->hasDynamicOffset) {
4942 // Filling out dynOfs based on the sorted bindings is important
4943 // because dynOfs has to be ordered based on the binding numbers,
4944 // and neither srb nor dynamicOffsets has any such ordering
4945 // requirement.
4946 for (const QRhiShaderResourceBinding &binding : std::as_const(srbD->sortedBindings)) {
4948 if (b->type == QRhiShaderResourceBinding::UniformBuffer && b->u.ubuf.hasDynamicOffset) {
4949 uint32_t offset = 0;
4950 for (int i = 0; i < dynamicOffsetCount; ++i) {
4951 const QRhiCommandBuffer::DynamicOffset &bindingOffsetPair(dynamicOffsets[i]);
4952 if (bindingOffsetPair.first == b->binding) {
4953 offset = bindingOffsetPair.second;
4954 break;
4955 }
4956 }
4957 dynOfs.append(offset); // use 0 if dynamicOffsets did not contain this binding
4958 }
4959 }
4960 }
4961
4962 if (cbD->passUsesSecondaryCb) {
4963 df->vkCmdBindDescriptorSets(cbD->activeSecondaryCbStack.last(),
4964 gfxPsD ? VK_PIPELINE_BIND_POINT_GRAPHICS : VK_PIPELINE_BIND_POINT_COMPUTE,
4965 gfxPsD ? gfxPsD->layout : compPsD->layout,
4966 0, 1, &srbD->descSets[descSetIdx],
4967 uint32_t(dynOfs.size()),
4968 dynOfs.size() ? dynOfs.constData() : nullptr);
4969 } else {
4970 QVkCommandBuffer::Command &cmd(cbD->commands.get());
4972 cmd.args.bindDescriptorSet.bindPoint = gfxPsD ? VK_PIPELINE_BIND_POINT_GRAPHICS
4973 : VK_PIPELINE_BIND_POINT_COMPUTE;
4974 cmd.args.bindDescriptorSet.pipelineLayout = gfxPsD ? gfxPsD->layout : compPsD->layout;
4975 cmd.args.bindDescriptorSet.descSet = srbD->descSets[descSetIdx];
4978 cbD->pools.dynamicOffset.append(dynOfs.constData(), dynOfs.size());
4979 }
4980
4981 if (gfxPsD) {
4982 cbD->currentGraphicsSrb = srb;
4983 cbD->currentComputeSrb = nullptr;
4984 } else {
4985 cbD->currentGraphicsSrb = nullptr;
4986 cbD->currentComputeSrb = srb;
4987 }
4988 cbD->currentSrbGeneration = srbD->generation;
4989 cbD->currentDescSetSlot = descSetIdx;
4990 }
4991
4993}
4994
4996 int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
4997 QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat)
4998{
5002
5003 bool needsBindVBuf = false;
5004 for (int i = 0; i < bindingCount; ++i) {
5005 const int inputSlot = startBinding + i;
5006 QVkBuffer *bufD = QRHI_RES(QVkBuffer, bindings[i].first);
5007 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::VertexBuffer));
5009 if (bufD->m_type == QRhiBuffer::Dynamic)
5011
5012 const VkBuffer vkvertexbuf = bufD->buffers[bufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0];
5013 if (cbD->currentVertexBuffers[inputSlot] != vkvertexbuf
5014 || cbD->currentVertexOffsets[inputSlot] != bindings[i].second)
5015 {
5016 needsBindVBuf = true;
5017 cbD->currentVertexBuffers[inputSlot] = vkvertexbuf;
5018 cbD->currentVertexOffsets[inputSlot] = bindings[i].second;
5019 }
5020 }
5021
5022 if (needsBindVBuf) {
5025 for (int i = 0; i < bindingCount; ++i) {
5026 QVkBuffer *bufD = QRHI_RES(QVkBuffer, bindings[i].first);
5027 const int slot = bufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0;
5028 bufs.append(bufD->buffers[slot]);
5029 ofs.append(bindings[i].second);
5030 trackedRegisterBuffer(&passResTracker, bufD, slot,
5033 }
5034
5035 if (cbD->passUsesSecondaryCb) {
5036 df->vkCmdBindVertexBuffers(cbD->activeSecondaryCbStack.last(), uint32_t(startBinding),
5037 uint32_t(bufs.size()), bufs.constData(), ofs.constData());
5038 } else {
5039 QVkCommandBuffer::Command &cmd(cbD->commands.get());
5041 cmd.args.bindVertexBuffer.startBinding = startBinding;
5042 cmd.args.bindVertexBuffer.count = bufs.size();
5044 cbD->pools.vertexBuffer.append(bufs.constData(), bufs.size());
5046 cbD->pools.vertexBufferOffset.append(ofs.constData(), ofs.size());
5047 }
5048 }
5049
5050 if (indexBuf) {
5051 QVkBuffer *ibufD = QRHI_RES(QVkBuffer, indexBuf);
5052 Q_ASSERT(ibufD->m_usage.testFlag(QRhiBuffer::IndexBuffer));
5054 if (ibufD->m_type == QRhiBuffer::Dynamic)
5056
5057 const int slot = ibufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0;
5058 const VkBuffer vkindexbuf = ibufD->buffers[slot];
5059 const VkIndexType type = indexFormat == QRhiCommandBuffer::IndexUInt16 ? VK_INDEX_TYPE_UINT16
5060 : VK_INDEX_TYPE_UINT32;
5061
5062 if (cbD->currentIndexBuffer != vkindexbuf
5063 || cbD->currentIndexOffset != indexOffset
5064 || cbD->currentIndexFormat != type)
5065 {
5066 cbD->currentIndexBuffer = vkindexbuf;
5067 cbD->currentIndexOffset = indexOffset;
5068 cbD->currentIndexFormat = type;
5069
5070 if (cbD->passUsesSecondaryCb) {
5071 df->vkCmdBindIndexBuffer(cbD->activeSecondaryCbStack.last(), vkindexbuf, indexOffset, type);
5072 } else {
5073 QVkCommandBuffer::Command &cmd(cbD->commands.get());
5075 cmd.args.bindIndexBuffer.buf = vkindexbuf;
5076 cmd.args.bindIndexBuffer.ofs = indexOffset;
5078 }
5079
5080 trackedRegisterBuffer(&passResTracker, ibufD, slot,
5083 }
5084 }
5085}
5086
5088{
5091 const QSize outputSize = cbD->currentTarget->pixelSize();
5092
5093 // x,y is top-left in VkViewport but bottom-left in QRhiViewport
5094 float x, y, w, h;
5095 if (!qrhi_toTopLeftRenderTargetRect<UnBounded>(outputSize, viewport.viewport(), &x, &y, &w, &h))
5096 return;
5097
5098 QVkCommandBuffer::Command &cmd(cbD->commands.get());
5099 VkViewport *vp = &cmd.args.setViewport.viewport;
5100 vp->x = x;
5101 vp->y = y;
5102 vp->width = w;
5103 vp->height = h;
5104 vp->minDepth = viewport.minDepth();
5105 vp->maxDepth = viewport.maxDepth();
5106
5107 if (cbD->passUsesSecondaryCb) {
5108 df->vkCmdSetViewport(cbD->activeSecondaryCbStack.last(), 0, 1, vp);
5109 cbD->commands.unget();
5110 } else {
5112 }
5113
5116 ->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor)) {
5117 QVkCommandBuffer::Command &cmd(cbD->commands.get());
5118 VkRect2D *s = &cmd.args.setScissor.scissor;
5119 qrhi_toTopLeftRenderTargetRect<Bounded>(outputSize, viewport.viewport(), &x, &y, &w, &h);
5120 s->offset.x = int32_t(x);
5121 s->offset.y = int32_t(y);
5122 s->extent.width = uint32_t(w);
5123 s->extent.height = uint32_t(h);
5124 if (cbD->passUsesSecondaryCb) {
5125 df->vkCmdSetScissor(cbD->activeSecondaryCbStack.last(), 0, 1, s);
5126 cbD->commands.unget();
5127 } else {
5129 }
5130 }
5131}
5132
5134{
5138 const QSize outputSize = cbD->currentTarget->pixelSize();
5139
5140 // x,y is top-left in VkRect2D but bottom-left in QRhiScissor
5141 int x, y, w, h;
5142 if (!qrhi_toTopLeftRenderTargetRect<Bounded>(outputSize, scissor.scissor(), &x, &y, &w, &h))
5143 return;
5144
5145 QVkCommandBuffer::Command &cmd(cbD->commands.get());
5146 VkRect2D *s = &cmd.args.setScissor.scissor;
5147 s->offset.x = x;
5148 s->offset.y = y;
5149 s->extent.width = uint32_t(w);
5150 s->extent.height = uint32_t(h);
5151
5152 if (cbD->passUsesSecondaryCb) {
5153 df->vkCmdSetScissor(cbD->activeSecondaryCbStack.last(), 0, 1, s);
5154 cbD->commands.unget();
5155 } else {
5157 }
5158}
5159
5161{
5164
5165 if (cbD->passUsesSecondaryCb) {
5166 float constants[] = { float(c.redF()), float(c.greenF()), float(c.blueF()), float(c.alphaF()) };
5167 df->vkCmdSetBlendConstants(cbD->activeSecondaryCbStack.last(), constants);
5168 } else {
5169 QVkCommandBuffer::Command &cmd(cbD->commands.get());
5171 cmd.args.setBlendConstants.c[0] = float(c.redF());
5172 cmd.args.setBlendConstants.c[1] = float(c.greenF());
5173 cmd.args.setBlendConstants.c[2] = float(c.blueF());
5174 cmd.args.setBlendConstants.c[3] = float(c.alphaF());
5175 }
5176}
5177
5179{
5182
5183 if (cbD->passUsesSecondaryCb) {
5184 df->vkCmdSetStencilReference(cbD->activeSecondaryCbStack.last(), VK_STENCIL_FRONT_AND_BACK, refValue);
5185 } else {
5186 QVkCommandBuffer::Command &cmd(cbD->commands.get());
5188 cmd.args.setStencilRef.ref = refValue;
5189 }
5190}
5191
5193 quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
5194{
5197
5198 if (cbD->passUsesSecondaryCb) {
5199 df->vkCmdDraw(cbD->activeSecondaryCbStack.last(), vertexCount, instanceCount, firstVertex, firstInstance);
5200 } else {
5201 QVkCommandBuffer::Command &cmd(cbD->commands.get());
5203 cmd.args.draw.vertexCount = vertexCount;
5205 cmd.args.draw.firstVertex = firstVertex;
5206 cmd.args.draw.firstInstance = firstInstance;
5207 }
5208}
5209
5211 quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
5212{
5215
5216 if (cbD->passUsesSecondaryCb) {
5217 df->vkCmdDrawIndexed(cbD->activeSecondaryCbStack.last(), indexCount, instanceCount,
5218 firstIndex, vertexOffset, firstInstance);
5219 } else {
5220 QVkCommandBuffer::Command &cmd(cbD->commands.get());
5222 cmd.args.drawIndexed.indexCount = indexCount;
5224 cmd.args.drawIndexed.firstIndex = firstIndex;
5225 cmd.args.drawIndexed.vertexOffset = vertexOffset;
5226 cmd.args.drawIndexed.firstInstance = firstInstance;
5227 }
5228}
5229
5231{
5232#ifdef VK_EXT_debug_utils
5233 if (!debugMarkers || !caps.debugUtils)
5234 return;
5235
5236 VkDebugUtilsLabelEXT label = {};
5237 label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
5238
5241 label.pLabelName = name.constData();
5242 vkCmdBeginDebugUtilsLabelEXT(cbD->activeSecondaryCbStack.last(), &label);
5243 } else {
5244 QVkCommandBuffer::Command &cmd(cbD->commands.get());
5246 cmd.args.debugMarkerBegin.label = label;
5247 cmd.args.debugMarkerBegin.labelNameIndex = cbD->pools.debugMarkerData.size();
5249 }
5250#else
5251 Q_UNUSED(cb);
5252 Q_UNUSED(name);
5253#endif
5254}
5255
5257{
5258#ifdef VK_EXT_debug_utils
5259 if (!debugMarkers || !caps.debugUtils)
5260 return;
5261
5264 vkCmdEndDebugUtilsLabelEXT(cbD->activeSecondaryCbStack.last());
5265 } else {
5266 QVkCommandBuffer::Command &cmd(cbD->commands.get());
5268 }
5269#else
5270 Q_UNUSED(cb);
5271#endif
5272}
5273
5275{
5276#ifdef VK_EXT_debug_utils
5277 if (!debugMarkers || !caps.debugUtils)
5278 return;
5279
5280 VkDebugUtilsLabelEXT label = {};
5281 label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
5282
5285 label.pLabelName = msg.constData();
5286 vkCmdInsertDebugUtilsLabelEXT(cbD->activeSecondaryCbStack.last(), &label);
5287 } else {
5288 QVkCommandBuffer::Command &cmd(cbD->commands.get());
5290 cmd.args.debugMarkerInsert.label = label;
5291 cmd.args.debugMarkerInsert.labelNameIndex = cbD->pools.debugMarkerData.size();
5292 cbD->pools.debugMarkerData.append(msg);
5293 }
5294#else
5295 Q_UNUSED(cb);
5296 Q_UNUSED(msg);
5297#endif
5298}
5299
5301{
5302 return QRHI_RES(QVkCommandBuffer, cb)->nativeHandles();
5303}
5304
5306{
5307 Q_ASSERT(cbD->currentTarget);
5308 QVkRenderTargetData *rtD = nullptr;
5310 switch (cbD->currentTarget->resourceType()) {
5313 break;
5316 break;
5317 default:
5318 Q_UNREACHABLE();
5319 break;
5320 }
5321 }
5322 return rtD;
5323}
5324
5326{
5328
5329 // When not in a pass, it is simple: record what we have (but do not
5330 // submit), the cb can then be used to record more external commands.
5333 cbD->resetCommands();
5334 return;
5335 }
5336
5337 // Otherwise, inside a pass, have a secondary command buffer (with
5338 // RENDER_PASS_CONTINUE). Using the main one is not acceptable since we
5339 // cannot just record at this stage, that would mess up the resource
5340 // tracking and commands like TransitionPassResources.
5341
5342 if (cbD->inExternal)
5343 return;
5344
5345 if (!cbD->passUsesSecondaryCb) {
5346 qWarning("beginExternal() within a pass is only supported with secondary command buffers. "
5347 "This can be enabled by passing QRhiCommandBuffer::ExternalContent to beginPass().");
5348 return;
5349 }
5350
5351 VkCommandBuffer secondaryCb = cbD->activeSecondaryCbStack.last();
5353 endAndEnqueueSecondaryCommandBuffer(secondaryCb, cbD);
5354
5355 VkCommandBuffer extCb = startSecondaryCommandBuffer(maybeRenderTargetData(cbD));
5356 if (extCb) {
5357 cbD->activeSecondaryCbStack.append(extCb);
5358 cbD->inExternal = true;
5359 }
5360}
5361
5363{
5365
5367 Q_ASSERT(cbD->commands.isEmpty() && cbD->currentPassResTrackerIndex == -1);
5368 } else if (cbD->inExternal) {
5369 VkCommandBuffer extCb = cbD->activeSecondaryCbStack.last();
5373 }
5374
5375 cbD->resetCachedState();
5376}
5377
5379{
5381 return cbD->lastGpuTime;
5382}
5383
5384void QRhiVulkan::setObjectName(uint64_t object, VkObjectType type, const QByteArray &name, int slot)
5385{
5386#ifdef VK_EXT_debug_utils
5387 if (!debugMarkers || !caps.debugUtils || name.isEmpty())
5388 return;
5389
5390 VkDebugUtilsObjectNameInfoEXT nameInfo = {};
5391 nameInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
5392 nameInfo.objectType = type;
5393 nameInfo.objectHandle = object;
5394 QByteArray decoratedName = name;
5395 if (slot >= 0) {
5396 decoratedName += '/';
5397 decoratedName += QByteArray::number(slot);
5398 }
5399 nameInfo.pObjectName = decoratedName.constData();
5400 vkSetDebugUtilsObjectNameEXT(dev, &nameInfo);
5401#else
5402 Q_UNUSED(object);
5403 Q_UNUSED(type);
5404 Q_UNUSED(name);
5405 Q_UNUSED(slot);
5406#endif
5407}
5408
5409static inline VkBufferUsageFlagBits toVkBufferUsage(QRhiBuffer::UsageFlags usage)
5410{
5411 int u = 0;
5412 if (usage.testFlag(QRhiBuffer::VertexBuffer))
5413 u |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
5414 if (usage.testFlag(QRhiBuffer::IndexBuffer))
5415 u |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
5416 if (usage.testFlag(QRhiBuffer::UniformBuffer))
5417 u |= VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
5418 if (usage.testFlag(QRhiBuffer::StorageBuffer))
5419 u |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
5420 return VkBufferUsageFlagBits(u);
5421}
5422
5423static inline VkFilter toVkFilter(QRhiSampler::Filter f)
5424{
5425 switch (f) {
5427 return VK_FILTER_NEAREST;
5429 return VK_FILTER_LINEAR;
5430 default:
5431 Q_UNREACHABLE_RETURN(VK_FILTER_NEAREST);
5432 }
5433}
5434
5435static inline VkSamplerMipmapMode toVkMipmapMode(QRhiSampler::Filter f)
5436{
5437 switch (f) {
5438 case QRhiSampler::None:
5439 return VK_SAMPLER_MIPMAP_MODE_NEAREST;
5441 return VK_SAMPLER_MIPMAP_MODE_NEAREST;
5443 return VK_SAMPLER_MIPMAP_MODE_LINEAR;
5444 default:
5445 Q_UNREACHABLE_RETURN(VK_SAMPLER_MIPMAP_MODE_NEAREST);
5446 }
5447}
5448
5449static inline VkSamplerAddressMode toVkAddressMode(QRhiSampler::AddressMode m)
5450{
5451 switch (m) {
5453 return VK_SAMPLER_ADDRESS_MODE_REPEAT;
5455 return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
5457 return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
5458 default:
5459 Q_UNREACHABLE_RETURN(VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE);
5460 }
5461}
5462
5463static inline VkShaderStageFlagBits toVkShaderStage(QRhiShaderStage::Type type)
5464{
5465 switch (type) {
5467 return VK_SHADER_STAGE_VERTEX_BIT;
5469 return VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
5471 return VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
5473 return VK_SHADER_STAGE_FRAGMENT_BIT;
5475 return VK_SHADER_STAGE_COMPUTE_BIT;
5477 return VK_SHADER_STAGE_GEOMETRY_BIT;
5478 default:
5479 Q_UNREACHABLE_RETURN(VK_SHADER_STAGE_VERTEX_BIT);
5480 }
5481}
5482
5484{
5485 switch (format) {
5487 return VK_FORMAT_R32G32B32A32_SFLOAT;
5489 return VK_FORMAT_R32G32B32_SFLOAT;
5491 return VK_FORMAT_R32G32_SFLOAT;
5493 return VK_FORMAT_R32_SFLOAT;
5495 return VK_FORMAT_R8G8B8A8_UNORM;
5497 return VK_FORMAT_R8G8_UNORM;
5499 return VK_FORMAT_R8_UNORM;
5501 return VK_FORMAT_R32G32B32A32_UINT;
5503 return VK_FORMAT_R32G32B32_UINT;
5505 return VK_FORMAT_R32G32_UINT;
5507 return VK_FORMAT_R32_UINT;
5509 return VK_FORMAT_R32G32B32A32_SINT;
5511 return VK_FORMAT_R32G32B32_SINT;
5513 return VK_FORMAT_R32G32_SINT;
5515 return VK_FORMAT_R32_SINT;
5517 return VK_FORMAT_R16G16B16A16_SFLOAT;
5519 return VK_FORMAT_R16G16B16_SFLOAT;
5521 return VK_FORMAT_R16G16_SFLOAT;
5523 return VK_FORMAT_R16_SFLOAT;
5524 default:
5525 Q_UNREACHABLE_RETURN(VK_FORMAT_R32G32B32A32_SFLOAT);
5526 }
5527}
5528
5529static inline VkPrimitiveTopology toVkTopology(QRhiGraphicsPipeline::Topology t)
5530{
5531 switch (t) {
5533 return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
5535 return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
5537 return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN;
5539 return VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
5541 return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
5543 return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
5545 return VK_PRIMITIVE_TOPOLOGY_PATCH_LIST;
5546 default:
5547 Q_UNREACHABLE_RETURN(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
5548 }
5549}
5550
5551static inline VkCullModeFlags toVkCullMode(QRhiGraphicsPipeline::CullMode c)
5552{
5553 switch (c) {
5555 return VK_CULL_MODE_NONE;
5557 return VK_CULL_MODE_FRONT_BIT;
5559 return VK_CULL_MODE_BACK_BIT;
5560 default:
5561 Q_UNREACHABLE_RETURN(VK_CULL_MODE_NONE);
5562 }
5563}
5564
5566{
5567 switch (f) {
5569 return VK_FRONT_FACE_COUNTER_CLOCKWISE;
5571 return VK_FRONT_FACE_CLOCKWISE;
5572 default:
5573 Q_UNREACHABLE_RETURN(VK_FRONT_FACE_COUNTER_CLOCKWISE);
5574 }
5575}
5576
5577static inline VkColorComponentFlags toVkColorComponents(QRhiGraphicsPipeline::ColorMask c)
5578{
5579 int f = 0;
5580 if (c.testFlag(QRhiGraphicsPipeline::R))
5581 f |= VK_COLOR_COMPONENT_R_BIT;
5582 if (c.testFlag(QRhiGraphicsPipeline::G))
5583 f |= VK_COLOR_COMPONENT_G_BIT;
5584 if (c.testFlag(QRhiGraphicsPipeline::B))
5585 f |= VK_COLOR_COMPONENT_B_BIT;
5586 if (c.testFlag(QRhiGraphicsPipeline::A))
5587 f |= VK_COLOR_COMPONENT_A_BIT;
5588 return VkColorComponentFlags(f);
5589}
5590
5592{
5593 switch (f) {
5595 return VK_BLEND_FACTOR_ZERO;
5597 return VK_BLEND_FACTOR_ONE;
5599 return VK_BLEND_FACTOR_SRC_COLOR;
5601 return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR;
5603 return VK_BLEND_FACTOR_DST_COLOR;
5605 return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR;
5607 return VK_BLEND_FACTOR_SRC_ALPHA;
5609 return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
5611 return VK_BLEND_FACTOR_DST_ALPHA;
5613 return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;
5615 return VK_BLEND_FACTOR_CONSTANT_COLOR;
5617 return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR;
5619 return VK_BLEND_FACTOR_CONSTANT_ALPHA;
5621 return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA;
5623 return VK_BLEND_FACTOR_SRC_ALPHA_SATURATE;
5625 return VK_BLEND_FACTOR_SRC1_COLOR;
5627 return VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR;
5629 return VK_BLEND_FACTOR_SRC1_ALPHA;
5631 return VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA;
5632 default:
5633 Q_UNREACHABLE_RETURN(VK_BLEND_FACTOR_ZERO);
5634 }
5635}
5636
5638{
5639 switch (op) {
5641 return VK_BLEND_OP_ADD;
5643 return VK_BLEND_OP_SUBTRACT;
5645 return VK_BLEND_OP_REVERSE_SUBTRACT;
5647 return VK_BLEND_OP_MIN;
5649 return VK_BLEND_OP_MAX;
5650 default:
5651 Q_UNREACHABLE_RETURN(VK_BLEND_OP_ADD);
5652 }
5653}
5654
5656{
5657 switch (op) {
5659 return VK_COMPARE_OP_NEVER;
5661 return VK_COMPARE_OP_LESS;
5663 return VK_COMPARE_OP_EQUAL;
5665 return VK_COMPARE_OP_LESS_OR_EQUAL;
5667 return VK_COMPARE_OP_GREATER;
5669 return VK_COMPARE_OP_NOT_EQUAL;
5671 return VK_COMPARE_OP_GREATER_OR_EQUAL;
5673 return VK_COMPARE_OP_ALWAYS;
5674 default:
5675 Q_UNREACHABLE_RETURN(VK_COMPARE_OP_ALWAYS);
5676 }
5677}
5678
5680{
5681 switch (op) {
5683 return VK_STENCIL_OP_ZERO;
5685 return VK_STENCIL_OP_KEEP;
5687 return VK_STENCIL_OP_REPLACE;
5689 return VK_STENCIL_OP_INCREMENT_AND_CLAMP;
5691 return VK_STENCIL_OP_DECREMENT_AND_CLAMP;
5693 return VK_STENCIL_OP_INVERT;
5695 return VK_STENCIL_OP_INCREMENT_AND_WRAP;
5697 return VK_STENCIL_OP_DECREMENT_AND_WRAP;
5698 default:
5699 Q_UNREACHABLE_RETURN(VK_STENCIL_OP_KEEP);
5700 }
5701}
5702
5704{
5705 switch (mode) {
5707 return VK_POLYGON_MODE_FILL;
5709 return VK_POLYGON_MODE_LINE;
5710 default:
5711 Q_UNREACHABLE_RETURN(VK_POLYGON_MODE_FILL);
5712 }
5713}
5714
5715static inline void fillVkStencilOpState(VkStencilOpState *dst, const QRhiGraphicsPipeline::StencilOpState &src)
5716{
5717 dst->failOp = toVkStencilOp(src.failOp);
5718 dst->passOp = toVkStencilOp(src.passOp);
5719 dst->depthFailOp = toVkStencilOp(src.depthFailOp);
5720 dst->compareOp = toVkCompareOp(src.compareOp);
5721}
5722
5723static inline VkDescriptorType toVkDescriptorType(const QRhiShaderResourceBinding::Data *b)
5724{
5725 switch (b->type) {
5727 return b->u.ubuf.hasDynamicOffset ? VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC
5728 : VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
5729
5731 return VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
5732
5734 return VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
5735
5737 return VK_DESCRIPTOR_TYPE_SAMPLER;
5738
5742 return VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
5743
5747 return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
5748
5749 default:
5750 Q_UNREACHABLE_RETURN(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
5751 }
5752}
5753
5754static inline VkShaderStageFlags toVkShaderStageFlags(QRhiShaderResourceBinding::StageFlags stage)
5755{
5756 int s = 0;
5757 if (stage.testFlag(QRhiShaderResourceBinding::VertexStage))
5758 s |= VK_SHADER_STAGE_VERTEX_BIT;
5759 if (stage.testFlag(QRhiShaderResourceBinding::FragmentStage))
5760 s |= VK_SHADER_STAGE_FRAGMENT_BIT;
5761 if (stage.testFlag(QRhiShaderResourceBinding::ComputeStage))
5762 s |= VK_SHADER_STAGE_COMPUTE_BIT;
5764 s |= VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
5766 s |= VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
5767 if (stage.testFlag(QRhiShaderResourceBinding::GeometryStage))
5768 s |= VK_SHADER_STAGE_GEOMETRY_BIT;
5769 return VkShaderStageFlags(s);
5770}
5771
5773{
5774 switch (op) {
5775 case QRhiSampler::Never:
5776 return VK_COMPARE_OP_NEVER;
5777 case QRhiSampler::Less:
5778 return VK_COMPARE_OP_LESS;
5779 case QRhiSampler::Equal:
5780 return VK_COMPARE_OP_EQUAL;
5782 return VK_COMPARE_OP_LESS_OR_EQUAL;
5784 return VK_COMPARE_OP_GREATER;
5786 return VK_COMPARE_OP_NOT_EQUAL;
5788 return VK_COMPARE_OP_GREATER_OR_EQUAL;
5790 return VK_COMPARE_OP_ALWAYS;
5791 default:
5792 Q_UNREACHABLE_RETURN(VK_COMPARE_OP_NEVER);
5793 }
5794}
5795
5797 : QRhiBuffer(rhi, type, usage, size)
5798{
5799 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
5800 buffers[i] = stagingBuffers[i] = VK_NULL_HANDLE;
5801 allocations[i] = stagingAllocations[i] = nullptr;
5802 }
5803}
5804
5806{
5807 destroy();
5808}
5809
5811{
5812 if (!buffers[0])
5813 return;
5814
5817 e.lastActiveFrameSlot = lastActiveFrameSlot;
5818
5819 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
5820 e.buffer.buffers[i] = buffers[i];
5821 e.buffer.allocations[i] = allocations[i];
5822 e.buffer.stagingBuffers[i] = stagingBuffers[i];
5823 e.buffer.stagingAllocations[i] = stagingAllocations[i];
5824
5825 buffers[i] = VK_NULL_HANDLE;
5826 allocations[i] = nullptr;
5827 stagingBuffers[i] = VK_NULL_HANDLE;
5828 stagingAllocations[i] = nullptr;
5829 pendingDynamicUpdates[i].clear();
5830 }
5831
5833 // destroy() implementations, unlike other functions, are expected to test
5834 // for m_rhi being null, to allow surviving in case one attempts to destroy
5835 // a (leaked) resource after the QRhi.
5836 if (rhiD) {
5837 rhiD->releaseQueue.append(e);
5838 rhiD->unregisterResource(this);
5839 }
5840}
5841
5843{
5844 if (buffers[0])
5845 destroy();
5846
5847 if (m_usage.testFlag(QRhiBuffer::StorageBuffer) && m_type == Dynamic) {
5848 qWarning("StorageBuffer cannot be combined with Dynamic");
5849 return false;
5850 }
5851
5852 const quint32 nonZeroSize = m_size <= 0 ? 256 : m_size;
5853
5854 VkBufferCreateInfo bufferInfo = {};
5855 bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
5856 bufferInfo.size = nonZeroSize;
5857 bufferInfo.usage = toVkBufferUsage(m_usage);
5858
5859 VmaAllocationCreateInfo allocInfo = {};
5860
5861 if (m_type == Dynamic) {
5862#ifndef Q_OS_DARWIN // not for MoltenVK
5863 // Keep mapped all the time. Essential f.ex. with some mobile GPUs,
5864 // where mapping and unmapping an entire allocation every time updating
5865 // a suballocated buffer presents a significant perf. hit.
5866 allocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
5867#endif
5868 // host visible, frequent changes
5869 allocInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
5870 } else {
5871 allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
5872 bufferInfo.usage |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
5873 }
5874
5876 VkResult err = VK_SUCCESS;
5877 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
5878 buffers[i] = VK_NULL_HANDLE;
5879 allocations[i] = nullptr;
5881 if (i == 0 || m_type == Dynamic) {
5882 VmaAllocation allocation;
5883 err = vmaCreateBuffer(toVmaAllocator(rhiD->allocator), &bufferInfo, &allocInfo, &buffers[i], &allocation, nullptr);
5884 if (err != VK_SUCCESS)
5885 break;
5887 rhiD->setObjectName(uint64_t(buffers[i]), VK_OBJECT_TYPE_BUFFER, m_objectName,
5888 m_type == Dynamic ? i : -1);
5889 }
5890 }
5891
5892 if (err != VK_SUCCESS) {
5893 qWarning("Failed to create buffer: %d", err);
5894 return false;
5895 }
5896
5898 generation += 1;
5899 rhiD->registerResource(this);
5900 return true;
5901}
5902
5904{
5905 if (m_type == Dynamic) {
5908 Q_ASSERT(sizeof(b.objects) / sizeof(b.objects[0]) >= size_t(QVK_FRAMES_IN_FLIGHT));
5909 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
5910 rhiD->executeBufferHostWritesForSlot(this, i);
5911 b.objects[i] = &buffers[i];
5912 }
5913 b.slotCount = QVK_FRAMES_IN_FLIGHT;
5914 return b;
5915 }
5916 return { { &buffers[0] }, 1 };
5917}
5918
5920{
5921 // Shortcut the entire buffer update mechanism and allow the client to do
5922 // the host writes directly to the buffer. This will lead to unexpected
5923 // results when combined with QRhiResourceUpdateBatch-based updates for the
5924 // buffer, but provides a fast path for dynamic buffers that have all their
5925 // content changed in every frame.
5928 Q_ASSERT(rhiD->inFrame);
5929 const int slot = rhiD->currentFrameSlot;
5930 void *p = nullptr;
5931 VmaAllocation a = toVmaAllocation(allocations[slot]);
5932 VkResult err = vmaMapMemory(toVmaAllocator(rhiD->allocator), a, &p);
5933 if (err != VK_SUCCESS) {
5934 qWarning("Failed to map buffer: %d", err);
5935 return nullptr;
5936 }
5937 return static_cast<char *>(p);
5938}
5939
5941{
5943 const int slot = rhiD->currentFrameSlot;
5944 VmaAllocation a = toVmaAllocation(allocations[slot]);
5945 vmaFlushAllocation(toVmaAllocator(rhiD->allocator), a, 0, m_size);
5946 vmaUnmapMemory(toVmaAllocator(rhiD->allocator), a);
5947}
5948
5950 int sampleCount, Flags flags,
5951 QRhiTexture::Format backingFormatHint)
5952 : QRhiRenderBuffer(rhi, type, pixelSize, sampleCount, flags, backingFormatHint)
5953{
5954}
5955
5957{
5958 destroy();
5959 delete backingTexture;
5960}
5961
5963{
5964 if (!memory && !backingTexture)
5965 return;
5966
5969 e.lastActiveFrameSlot = lastActiveFrameSlot;
5970
5971 e.renderBuffer.memory = memory;
5972 e.renderBuffer.image = image;
5973 e.renderBuffer.imageView = imageView;
5974
5975 memory = VK_NULL_HANDLE;
5976 image = VK_NULL_HANDLE;
5977 imageView = VK_NULL_HANDLE;
5978
5979 if (backingTexture) {
5981 backingTexture->lastActiveFrameSlot = e.lastActiveFrameSlot;
5983 }
5984
5986 if (rhiD) {
5987 rhiD->releaseQueue.append(e);
5988 rhiD->unregisterResource(this);
5989 }
5990}
5991
5993{
5994 if (memory || backingTexture)
5995 destroy();
5996
5997 if (m_pixelSize.isEmpty())
5998 return false;
5999
6001 samples = rhiD->effectiveSampleCount(m_sampleCount);
6002
6003 switch (m_type) {
6005 {
6006 if (!backingTexture) {
6007 backingTexture = QRHI_RES(QVkTexture, rhiD->createTexture(backingFormat(),
6009 1,
6010 0,
6013 } else {
6016 }
6018 if (!backingTexture->create())
6019 return false;
6021 }
6022 break;
6024 vkformat = rhiD->optimalDepthStencilFormat();
6025 if (!rhiD->createTransientImage(vkformat,
6027 VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
6028 VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT,
6029 samples,
6030 &memory,
6031 &image,
6032 &imageView,
6033 1))
6034 {
6035 return false;
6036 }
6037 rhiD->setObjectName(uint64_t(image), VK_OBJECT_TYPE_IMAGE, m_objectName);
6038 break;
6039 default:
6040 Q_UNREACHABLE();
6041 break;
6042 }
6043
6045 generation += 1;
6046 rhiD->registerResource(this);
6047 return true;
6048}
6049
6051{
6053 return m_backingFormatHint;
6054 else
6056}
6057
6059 int arraySize, int sampleCount, Flags flags)
6060 : QRhiTexture(rhi, format, pixelSize, depth, arraySize, sampleCount, flags)
6061{
6062 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
6063 stagingBuffers[i] = VK_NULL_HANDLE;
6064 stagingAllocations[i] = nullptr;
6065 }
6066 for (int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i)
6067 perLevelImageViews[i] = VK_NULL_HANDLE;
6068}
6069
6071{
6072 destroy();
6073}
6074
6076{
6077 if (!image)
6078 return;
6079
6082 e.lastActiveFrameSlot = lastActiveFrameSlot;
6083
6084 e.texture.image = owns ? image : VK_NULL_HANDLE;
6085 e.texture.imageView = imageView;
6086 e.texture.allocation = owns ? imageAlloc : nullptr;
6087
6088 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
6089 e.texture.stagingBuffers[i] = stagingBuffers[i];
6090 e.texture.stagingAllocations[i] = stagingAllocations[i];
6091
6092 stagingBuffers[i] = VK_NULL_HANDLE;
6093 stagingAllocations[i] = nullptr;
6094 }
6095
6096 for (int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i) {
6097 e.texture.extraImageViews[i] = perLevelImageViews[i];
6098 perLevelImageViews[i] = VK_NULL_HANDLE;
6099 }
6100
6101 image = VK_NULL_HANDLE;
6102 imageView = VK_NULL_HANDLE;
6103 imageAlloc = nullptr;
6104
6106 if (rhiD) {
6107 rhiD->releaseQueue.append(e);
6108 rhiD->unregisterResource(this);
6109 }
6110}
6111
6113{
6114 if (image)
6115 destroy();
6116
6119 VkFormatProperties props;
6120 rhiD->f->vkGetPhysicalDeviceFormatProperties(rhiD->physDev, vkformat, &props);
6121 const bool canSampleOptimal = (props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT);
6122 if (!canSampleOptimal) {
6123 qWarning("Texture sampling with optimal tiling for format %d not supported", vkformat);
6124 return false;
6125 }
6126
6127 const bool isCube = m_flags.testFlag(CubeMap);
6128 const bool isArray = m_flags.testFlag(TextureArray);
6129 const bool is3D = m_flags.testFlag(ThreeDimensional);
6130 const bool is1D = m_flags.testFlag(OneDimensional);
6131 const bool hasMipMaps = m_flags.testFlag(MipMapped);
6132
6133 const QSize size = is1D ? QSize(qMax(1, m_pixelSize.width()), 1)
6134 : (m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize);
6135
6136 mipLevelCount = uint(hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1);
6137 const int maxLevels = QRhi::MAX_MIP_LEVELS;
6138 if (mipLevelCount > maxLevels) {
6139 qWarning("Too many mip levels (%d, max is %d), truncating mip chain", mipLevelCount, maxLevels);
6140 mipLevelCount = maxLevels;
6141 }
6142 samples = rhiD->effectiveSampleCount(m_sampleCount);
6143 if (samples > VK_SAMPLE_COUNT_1_BIT) {
6144 if (isCube) {
6145 qWarning("Cubemap texture cannot be multisample");
6146 return false;
6147 }
6148 if (is3D) {
6149 qWarning("3D texture cannot be multisample");
6150 return false;
6151 }
6152 if (hasMipMaps) {
6153 qWarning("Multisample texture cannot have mipmaps");
6154 return false;
6155 }
6156 }
6157 if (isCube && is3D) {
6158 qWarning("Texture cannot be both cube and 3D");
6159 return false;
6160 }
6161 if (isArray && is3D) {
6162 qWarning("Texture cannot be both array and 3D");
6163 return false;
6164 }
6165 if (isCube && is1D) {
6166 qWarning("Texture cannot be both cube and 1D");
6167 return false;
6168 }
6169 if (is1D && is3D) {
6170 qWarning("Texture cannot be both 1D and 3D");
6171 return false;
6172 }
6173 if (m_depth > 1 && !is3D) {
6174 qWarning("Texture cannot have a depth of %d when it is not 3D", m_depth);
6175 return false;
6176 }
6177 if (m_arraySize > 0 && !isArray) {
6178 qWarning("Texture cannot have an array size of %d when it is not an array", m_arraySize);
6179 return false;
6180 }
6181 if (m_arraySize < 1 && isArray) {
6182 qWarning("Texture is an array but array size is %d", m_arraySize);
6183 return false;
6184 }
6185
6186 usageState.layout = VK_IMAGE_LAYOUT_PREINITIALIZED;
6187 usageState.access = 0;
6188 usageState.stage = 0;
6189
6190 if (adjustedSize)
6191 *adjustedSize = size;
6192
6193 return true;
6194}
6195
6197{
6199
6200 const auto aspectMask = aspectMaskForTextureFormat(m_format);
6201 const bool isCube = m_flags.testFlag(CubeMap);
6202 const bool isArray = m_flags.testFlag(TextureArray);
6203 const bool is3D = m_flags.testFlag(ThreeDimensional);
6204 const bool is1D = m_flags.testFlag(OneDimensional);
6205
6206 VkImageViewCreateInfo viewInfo = {};
6207 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
6208 viewInfo.image = image;
6209 viewInfo.viewType = isCube
6210 ? VK_IMAGE_VIEW_TYPE_CUBE
6211 : (is3D ? VK_IMAGE_VIEW_TYPE_3D
6212 : (is1D ? (isArray ? VK_IMAGE_VIEW_TYPE_1D_ARRAY : VK_IMAGE_VIEW_TYPE_1D)
6213 : (isArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D)));
6214 viewInfo.format = vkformat;
6215 viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
6216 viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
6217 viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
6218 viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
6219 viewInfo.subresourceRange.aspectMask = aspectMask;
6220 viewInfo.subresourceRange.levelCount = mipLevelCount;
6221 if (isArray && m_arrayRangeStart >= 0 && m_arrayRangeLength >= 0) {
6222 viewInfo.subresourceRange.baseArrayLayer = uint32_t(m_arrayRangeStart);
6223 viewInfo.subresourceRange.layerCount = uint32_t(m_arrayRangeLength);
6224 } else {
6225 viewInfo.subresourceRange.layerCount = isCube ? 6 : (isArray ? qMax(0, m_arraySize) : 1);
6226 }
6227
6228 VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &imageView);
6229 if (err != VK_SUCCESS) {
6230 qWarning("Failed to create image view: %d", err);
6231 return false;
6232 }
6233
6235 generation += 1;
6236
6237 return true;
6238}
6239
6241{
6242 QSize size;
6243 if (!prepareCreate(&size))
6244 return false;
6245
6247 const bool isRenderTarget = m_flags.testFlag(QRhiTexture::RenderTarget);
6248 const bool isDepth = isDepthTextureFormat(m_format);
6249 const bool isCube = m_flags.testFlag(CubeMap);
6250 const bool isArray = m_flags.testFlag(TextureArray);
6251 const bool is3D = m_flags.testFlag(ThreeDimensional);
6252 const bool is1D = m_flags.testFlag(OneDimensional);
6253
6254 VkImageCreateInfo imageInfo = {};
6255 imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
6256 imageInfo.flags = 0;
6257 if (isCube)
6258 imageInfo.flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
6259
6260 if (is3D && isRenderTarget) {
6261 // This relies on a Vulkan 1.1 constant. For guaranteed proper behavior
6262 // this also requires that at run time the VkInstance has at least API 1.1
6263 // enabled. (though it works as expected with some Vulkan (1.2)
6264 // implementations regardless of the requested API version, but f.ex. the
6265 // validation layer complains when using this without enabling >=1.1)
6266 if (!rhiD->caps.texture3DSliceAs2D)
6267 qWarning("QRhiVulkan: Rendering to 3D texture slice may not be functional without API 1.1 on the VkInstance");
6268#ifdef VK_VERSION_1_1
6269 imageInfo.flags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT;
6270#else
6271 imageInfo.flags |= 0x00000020;
6272#endif
6273 }
6274
6275 imageInfo.imageType = is1D ? VK_IMAGE_TYPE_1D : is3D ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D;
6276 imageInfo.format = vkformat;
6277 imageInfo.extent.width = uint32_t(size.width());
6278 imageInfo.extent.height = uint32_t(size.height());
6279 imageInfo.extent.depth = is3D ? qMax(1, m_depth) : 1;
6280 imageInfo.mipLevels = mipLevelCount;
6281 imageInfo.arrayLayers = isCube ? 6 : (isArray ? qMax(0, m_arraySize) : 1);
6282 imageInfo.samples = samples;
6283 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
6284 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
6285
6286 imageInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
6287 if (isRenderTarget) {
6288 if (isDepth)
6289 imageInfo.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
6290 else
6291 imageInfo.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
6292 }
6294 imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
6296 imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
6298 imageInfo.usage |= VK_IMAGE_USAGE_STORAGE_BIT;
6299
6300 VmaAllocationCreateInfo allocInfo = {};
6301 allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
6302
6303 VmaAllocation allocation;
6304 VkResult err = vmaCreateImage(toVmaAllocator(rhiD->allocator), &imageInfo, &allocInfo, &image, &allocation, nullptr);
6305 if (err != VK_SUCCESS) {
6306 qWarning("Failed to create image: %d", err);
6307 return false;
6308 }
6310
6311 if (!finishCreate())
6312 return false;
6313
6314 rhiD->setObjectName(uint64_t(image), VK_OBJECT_TYPE_IMAGE, m_objectName);
6315
6316 owns = true;
6317 rhiD->registerResource(this);
6318 return true;
6319}
6320
6322{
6323 VkImage img = VkImage(src.object);
6324 if (img == 0)
6325 return false;
6326
6327 if (!prepareCreate())
6328 return false;
6329
6330 image = img;
6331
6332 if (!finishCreate())
6333 return false;
6334
6335 usageState.layout = VkImageLayout(src.layout);
6336
6337 owns = false;
6339 rhiD->registerResource(this);
6340 return true;
6341}
6342
6344{
6345 return {quint64(image), usageState.layout};
6346}
6347
6349{
6350 usageState.layout = VkImageLayout(layout);
6351}
6352
6354{
6355 Q_ASSERT(level >= 0 && level < int(mipLevelCount));
6356 if (perLevelImageViews[level] != VK_NULL_HANDLE)
6357 return perLevelImageViews[level];
6358
6359 const VkImageAspectFlags aspectMask = aspectMaskForTextureFormat(m_format);
6360 const bool isCube = m_flags.testFlag(CubeMap);
6361 const bool isArray = m_flags.testFlag(TextureArray);
6362 const bool is3D = m_flags.testFlag(ThreeDimensional);
6363 const bool is1D = m_flags.testFlag(OneDimensional);
6364
6365 VkImageViewCreateInfo viewInfo = {};
6366 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
6367 viewInfo.image = image;
6368 viewInfo.viewType = isCube
6369 ? VK_IMAGE_VIEW_TYPE_CUBE
6370 : (is3D ? VK_IMAGE_VIEW_TYPE_3D
6371 : (is1D ? (isArray ? VK_IMAGE_VIEW_TYPE_1D_ARRAY : VK_IMAGE_VIEW_TYPE_1D)
6372 : (isArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D)));
6373 viewInfo.format = vkformat;
6374 viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
6375 viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
6376 viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
6377 viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
6378 viewInfo.subresourceRange.aspectMask = aspectMask;
6379 viewInfo.subresourceRange.baseMipLevel = uint32_t(level);
6380 viewInfo.subresourceRange.levelCount = 1;
6381 viewInfo.subresourceRange.baseArrayLayer = 0;
6382 viewInfo.subresourceRange.layerCount = isCube ? 6 : (isArray ? qMax(0, m_arraySize) : 1);
6383
6384 VkImageView v = VK_NULL_HANDLE;
6386 VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &v);
6387 if (err != VK_SUCCESS) {
6388 qWarning("Failed to create image view: %d", err);
6389 return VK_NULL_HANDLE;
6390 }
6391
6393 return v;
6394}
6395
6396QVkSampler::QVkSampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
6398 : QRhiSampler(rhi, magFilter, minFilter, mipmapMode, u, v, w)
6399{
6400}
6401
6403{
6404 destroy();
6405}
6406
6408{
6409 if (!sampler)
6410 return;
6411
6414 e.lastActiveFrameSlot = lastActiveFrameSlot;
6415
6416 e.sampler.sampler = sampler;
6417 sampler = VK_NULL_HANDLE;
6418
6420 if (rhiD) {
6421 rhiD->releaseQueue.append(e);
6422 rhiD->unregisterResource(this);
6423 }
6424}
6425
6427{
6428 if (sampler)
6429 destroy();
6430
6431 VkSamplerCreateInfo samplerInfo = {};
6432 samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
6433 samplerInfo.magFilter = toVkFilter(m_magFilter);
6434 samplerInfo.minFilter = toVkFilter(m_minFilter);
6435 samplerInfo.mipmapMode = toVkMipmapMode(m_mipmapMode);
6436 samplerInfo.addressModeU = toVkAddressMode(m_addressU);
6437 samplerInfo.addressModeV = toVkAddressMode(m_addressV);
6438 samplerInfo.addressModeW = toVkAddressMode(m_addressW);
6439 samplerInfo.maxAnisotropy = 1.0f;
6440 samplerInfo.compareEnable = m_compareOp != Never;
6441 samplerInfo.compareOp = toVkTextureCompareOp(m_compareOp);
6442 samplerInfo.maxLod = m_mipmapMode == None ? 0.25f : 1000.0f;
6443
6445 VkResult err = rhiD->df->vkCreateSampler(rhiD->dev, &samplerInfo, nullptr, &sampler);
6446 if (err != VK_SUCCESS) {
6447 qWarning("Failed to create sampler: %d", err);
6448 return false;
6449 }
6450
6452 generation += 1;
6453 rhiD->registerResource(this);
6454 return true;
6455}
6456
6459{
6460 serializedFormatData.reserve(32);
6461}
6462
6464{
6465 destroy();
6466}
6467
6469{
6470 if (!rp)
6471 return;
6472
6473 if (!ownsRp) {
6474 rp = VK_NULL_HANDLE;
6475 return;
6476 }
6477
6480 e.lastActiveFrameSlot = lastActiveFrameSlot;
6481
6482 e.renderPass.rp = rp;
6483
6484 rp = VK_NULL_HANDLE;
6485
6487 if (rhiD) {
6488 rhiD->releaseQueue.append(e);
6489 rhiD->unregisterResource(this);
6490 }
6491}
6492
6493static inline bool attachmentDescriptionEquals(const VkAttachmentDescription &a, const VkAttachmentDescription &b)
6494{
6495 return a.format == b.format
6496 && a.samples == b.samples
6497 && a.loadOp == b.loadOp
6498 && a.storeOp == b.storeOp
6499 && a.stencilLoadOp == b.stencilLoadOp
6500 && a.stencilStoreOp == b.stencilStoreOp
6501 && a.initialLayout == b.initialLayout
6502 && a.finalLayout == b.finalLayout;
6503}
6504
6506{
6507 if (other == this)
6508 return true;
6509
6510 if (!other)
6511 return false;
6512
6514
6515 if (attDescs.size() != o->attDescs.size())
6516 return false;
6517 if (colorRefs.size() != o->colorRefs.size())
6518 return false;
6519 if (resolveRefs.size() != o->resolveRefs.size())
6520 return false;
6521 if (hasDepthStencil != o->hasDepthStencil)
6522 return false;
6523 if (multiViewCount != o->multiViewCount)
6524 return false;
6525
6526 for (int i = 0, ie = colorRefs.size(); i != ie; ++i) {
6527 const uint32_t attIdx = colorRefs[i].attachment;
6528 if (attIdx != o->colorRefs[i].attachment)
6529 return false;
6530 if (attIdx != VK_ATTACHMENT_UNUSED && !attachmentDescriptionEquals(attDescs[attIdx], o->attDescs[attIdx]))
6531 return false;
6532 }
6533
6534 if (hasDepthStencil) {
6535 const uint32_t attIdx = dsRef.attachment;
6536 if (attIdx != o->dsRef.attachment)
6537 return false;
6538 if (attIdx != VK_ATTACHMENT_UNUSED && !attachmentDescriptionEquals(attDescs[attIdx], o->attDescs[attIdx]))
6539 return false;
6540 }
6541
6542 for (int i = 0, ie = resolveRefs.size(); i != ie; ++i) {
6543 const uint32_t attIdx = resolveRefs[i].attachment;
6544 if (attIdx != o->resolveRefs[i].attachment)
6545 return false;
6546 if (attIdx != VK_ATTACHMENT_UNUSED && !attachmentDescriptionEquals(attDescs[attIdx], o->attDescs[attIdx]))
6547 return false;
6548 }
6549
6550 // subpassDeps is not included
6551
6552 return true;
6553}
6554
6556{
6557 serializedFormatData.clear();
6558 auto p = std::back_inserter(serializedFormatData);
6559
6560 *p++ = attDescs.size();
6561 *p++ = colorRefs.size();
6562 *p++ = resolveRefs.size();
6563 *p++ = hasDepthStencil;
6564 *p++ = multiViewCount;
6565
6566 auto serializeAttachmentData = [this, &p](uint32_t attIdx) {
6567 const bool used = attIdx != VK_ATTACHMENT_UNUSED;
6568 const VkAttachmentDescription *a = used ? &attDescs[attIdx] : nullptr;
6569 *p++ = used ? a->format : 0;
6570 *p++ = used ? a->samples : 0;
6571 *p++ = used ? a->loadOp : 0;
6572 *p++ = used ? a->storeOp : 0;
6573 *p++ = used ? a->stencilLoadOp : 0;
6574 *p++ = used ? a->stencilStoreOp : 0;
6575 *p++ = used ? a->initialLayout : 0;
6576 *p++ = used ? a->finalLayout : 0;
6577 };
6578
6579 for (int i = 0, ie = colorRefs.size(); i != ie; ++i) {
6580 const uint32_t attIdx = colorRefs[i].attachment;
6581 *p++ = attIdx;
6582 serializeAttachmentData(attIdx);
6583 }
6584
6585 if (hasDepthStencil) {
6586 const uint32_t attIdx = dsRef.attachment;
6587 *p++ = attIdx;
6588 serializeAttachmentData(attIdx);
6589 }
6590
6591 for (int i = 0, ie = resolveRefs.size(); i != ie; ++i) {
6592 const uint32_t attIdx = resolveRefs[i].attachment;
6593 *p++ = attIdx;
6594 serializeAttachmentData(attIdx);
6595 }
6596}
6597
6599{
6601
6602 rpD->ownsRp = true;
6603 rpD->attDescs = attDescs;
6604 rpD->colorRefs = colorRefs;
6605 rpD->resolveRefs = resolveRefs;
6606 rpD->subpassDeps = subpassDeps;
6609 rpD->dsRef = dsRef;
6610
6611 VkRenderPassCreateInfo rpInfo;
6612 VkSubpassDescription subpassDesc;
6613 fillRenderPassCreateInfo(&rpInfo, &subpassDesc, rpD);
6614
6616 MultiViewRenderPassSetupHelper multiViewHelper;
6617 if (!multiViewHelper.prepare(&rpInfo, multiViewCount, rhiD->caps.multiView)) {
6618 delete rpD;
6619 return nullptr;
6620 }
6621
6622 VkResult err = rhiD->df->vkCreateRenderPass(rhiD->dev, &rpInfo, nullptr, &rpD->rp);
6623 if (err != VK_SUCCESS) {
6624 qWarning("Failed to create renderpass: %d", err);
6625 delete rpD;
6626 return nullptr;
6627 }
6628
6630 rhiD->registerResource(rpD);
6631 return rpD;
6632}
6633
6635{
6636 return serializedFormatData;
6637}
6638
6640{
6641 nativeHandlesStruct.renderPass = rp;
6642 return &nativeHandlesStruct;
6643}
6644
6646 : QRhiSwapChainRenderTarget(rhi, swapchain)
6647{
6648}
6649
6651{
6652 destroy();
6653}
6654
6656{
6657 // nothing to do here
6658}
6659
6661{
6662 return d.pixelSize;
6663}
6664
6666{
6667 return d.dpr;
6668}
6669
6671{
6672 return d.sampleCount;
6673}
6674
6677 Flags flags)
6679{
6680 for (int att = 0; att < QVkRenderTargetData::MAX_COLOR_ATTACHMENTS; ++att) {
6681 rtv[att] = VK_NULL_HANDLE;
6682 resrtv[att] = VK_NULL_HANDLE;
6683 }
6684}
6685
6687{
6688 destroy();
6689}
6690
6692{
6693 if (!d.fb)
6694 return;
6695
6698 e.lastActiveFrameSlot = lastActiveFrameSlot;
6699
6700 e.textureRenderTarget.fb = d.fb;
6701 d.fb = VK_NULL_HANDLE;
6702
6703 for (int att = 0; att < QVkRenderTargetData::MAX_COLOR_ATTACHMENTS; ++att) {
6704 e.textureRenderTarget.rtv[att] = rtv[att];
6705 e.textureRenderTarget.resrtv[att] = resrtv[att];
6706 rtv[att] = VK_NULL_HANDLE;
6707 resrtv[att] = VK_NULL_HANDLE;
6708 }
6709
6711 if (rhiD) {
6712 rhiD->releaseQueue.append(e);
6713 rhiD->unregisterResource(this);
6714 }
6715}
6716
6718{
6719 // not yet built so cannot rely on data computed in create()
6720
6723 if (!rhiD->createOffscreenRenderPass(rp,
6730 {
6731 delete rp;
6732 return nullptr;
6733 }
6734
6735 rp->ownsRp = true;
6737 rhiD->registerResource(rp);
6738 return rp;
6739}
6740
6742{
6743 if (d.fb)
6744 destroy();
6745
6748 const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
6749
6752 d.multiViewCount = 0;
6753
6754 d.colorAttCount = 0;
6755 int attIndex = 0;
6756 for (auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it, ++attIndex) {
6757 d.colorAttCount += 1;
6758 QVkTexture *texD = QRHI_RES(QVkTexture, it->texture());
6759 QVkRenderBuffer *rbD = QRHI_RES(QVkRenderBuffer, it->renderBuffer());
6760 Q_ASSERT(texD || rbD);
6761 if (texD) {
6762 Q_ASSERT(texD->flags().testFlag(QRhiTexture::RenderTarget));
6763 const bool is1D = texD->flags().testFlag(QRhiTexture::OneDimensional);
6764 const bool isMultiView = it->multiViewCount() >= 2;
6765 if (isMultiView && d.multiViewCount == 0)
6766 d.multiViewCount = it->multiViewCount();
6767 VkImageViewCreateInfo viewInfo = {};
6768 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
6769 viewInfo.image = texD->image;
6770 viewInfo.viewType = is1D ? VK_IMAGE_VIEW_TYPE_1D
6771 : (isMultiView ? VK_IMAGE_VIEW_TYPE_2D_ARRAY
6772 : VK_IMAGE_VIEW_TYPE_2D);
6773 viewInfo.format = texD->vkformat;
6774 viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
6775 viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
6776 viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
6777 viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
6778 viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
6779 viewInfo.subresourceRange.baseMipLevel = uint32_t(it->level());
6780 viewInfo.subresourceRange.levelCount = 1;
6781 viewInfo.subresourceRange.baseArrayLayer = uint32_t(it->layer());
6782 viewInfo.subresourceRange.layerCount = uint32_t(isMultiView ? it->multiViewCount() : 1);
6783 VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &rtv[attIndex]);
6784 if (err != VK_SUCCESS) {
6785 qWarning("Failed to create render target image view: %d", err);
6786 return false;
6787 }
6788 views.append(rtv[attIndex]);
6789 if (attIndex == 0) {
6790 d.pixelSize = rhiD->q->sizeForMipLevel(it->level(), texD->pixelSize());
6791 d.sampleCount = texD->samples;
6792 }
6793 } else if (rbD) {
6795 views.append(rbD->backingTexture->imageView);
6796 if (attIndex == 0) {
6797 d.pixelSize = rbD->pixelSize();
6798 d.sampleCount = rbD->samples;
6799 }
6800 }
6801 }
6802 d.dpr = 1;
6803
6804 if (hasDepthStencil) {
6805 if (m_desc.depthTexture()) {
6807 views.append(depthTexD->imageView);
6808 if (d.colorAttCount == 0) {
6809 d.pixelSize = depthTexD->pixelSize();
6810 d.sampleCount = depthTexD->samples;
6811 }
6812 } else {
6814 views.append(depthRbD->imageView);
6815 if (d.colorAttCount == 0) {
6816 d.pixelSize = depthRbD->pixelSize();
6817 d.sampleCount = depthRbD->samples;
6818 }
6819 }
6820 d.dsAttCount = 1;
6821 } else {
6822 d.dsAttCount = 0;
6823 }
6824
6825 d.resolveAttCount = 0;
6826 attIndex = 0;
6828 for (auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it, ++attIndex) {
6829 if (it->resolveTexture()) {
6830 QVkTexture *resTexD = QRHI_RES(QVkTexture, it->resolveTexture());
6831 Q_ASSERT(resTexD->flags().testFlag(QRhiTexture::RenderTarget));
6832 d.resolveAttCount += 1;
6833
6834 VkImageViewCreateInfo viewInfo = {};
6835 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
6836 viewInfo.image = resTexD->image;
6837 viewInfo.viewType = d.multiViewCount ? VK_IMAGE_VIEW_TYPE_2D_ARRAY
6838 : VK_IMAGE_VIEW_TYPE_2D;
6839 viewInfo.format = resTexD->vkformat;
6840 viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
6841 viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
6842 viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
6843 viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
6844 viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
6845 viewInfo.subresourceRange.baseMipLevel = uint32_t(it->resolveLevel());
6846 viewInfo.subresourceRange.levelCount = 1;
6847 viewInfo.subresourceRange.baseArrayLayer = uint32_t(it->resolveLayer());
6848 viewInfo.subresourceRange.layerCount = qMax<uint32_t>(1, d.multiViewCount);
6849 VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &resrtv[attIndex]);
6850 if (err != VK_SUCCESS) {
6851 qWarning("Failed to create render target resolve image view: %d", err);
6852 return false;
6853 }
6854 views.append(resrtv[attIndex]);
6855 }
6856 }
6857
6858 if (!m_renderPassDesc)
6859 qWarning("QVkTextureRenderTarget: No renderpass descriptor set. See newCompatibleRenderPassDescriptor() and setRenderPassDescriptor().");
6860
6862 Q_ASSERT(d.rp && d.rp->rp);
6863
6864 VkFramebufferCreateInfo fbInfo = {};
6865 fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
6866 fbInfo.renderPass = d.rp->rp;
6867 fbInfo.attachmentCount = uint32_t(d.colorAttCount + d.dsAttCount + d.resolveAttCount);
6868 fbInfo.pAttachments = views.constData();
6869 fbInfo.width = uint32_t(d.pixelSize.width());
6870 fbInfo.height = uint32_t(d.pixelSize.height());
6871 fbInfo.layers = 1;
6872
6873 VkResult err = rhiD->df->vkCreateFramebuffer(rhiD->dev, &fbInfo, nullptr, &d.fb);
6874 if (err != VK_SUCCESS) {
6875 qWarning("Failed to create framebuffer: %d", err);
6876 return false;
6877 }
6878
6879 QRhiRenderTargetAttachmentTracker::updateResIdList<QVkTexture, QVkRenderBuffer>(m_desc, &d.currentResIdList);
6880
6882 rhiD->registerResource(this);
6883 return true;
6884}
6885
6887{
6888 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QVkTexture, QVkRenderBuffer>(m_desc, d.currentResIdList))
6889 const_cast<QVkTextureRenderTarget *>(this)->create();
6890
6891 return d.pixelSize;
6892}
6893
6895{
6896 return d.dpr;
6897}
6898
6900{
6901 return d.sampleCount;
6902}
6903
6906{
6907}
6908
6910{
6911 destroy();
6912}
6913
6915{
6916 if (!layout)
6917 return;
6918
6920
6923 e.lastActiveFrameSlot = lastActiveFrameSlot;
6924
6925 e.shaderResourceBindings.poolIndex = poolIndex;
6926 e.shaderResourceBindings.layout = layout;
6927
6928 poolIndex = -1;
6929 layout = VK_NULL_HANDLE;
6930 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
6931 descSets[i] = VK_NULL_HANDLE;
6932
6934 if (rhiD) {
6935 rhiD->releaseQueue.append(e);
6936 rhiD->unregisterResource(this);
6937 }
6938}
6939
6941{
6942 if (layout)
6943 destroy();
6944
6946 if (!rhiD->sanityCheckShaderResourceBindings(this))
6947 return false;
6948
6949 rhiD->updateLayoutDesc(this);
6950
6951 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
6952 descSets[i] = VK_NULL_HANDLE;
6953
6955 std::copy(m_bindings.cbegin(), m_bindings.cend(), std::back_inserter(sortedBindings));
6957
6958 hasSlottedResource = false;
6959 hasDynamicOffset = false;
6960 for (const QRhiShaderResourceBinding &binding : std::as_const(sortedBindings)) {
6962 if (b->type == QRhiShaderResourceBinding::UniformBuffer && b->u.ubuf.buf) {
6963 if (QRHI_RES(QVkBuffer, b->u.ubuf.buf)->type() == QRhiBuffer::Dynamic)
6964 hasSlottedResource = true;
6965 if (b->u.ubuf.hasDynamicOffset)
6966 hasDynamicOffset = true;
6967 }
6968 }
6969
6971 for (const QRhiShaderResourceBinding &binding : std::as_const(sortedBindings)) {
6973 VkDescriptorSetLayoutBinding vkbinding = {};
6974 vkbinding.binding = uint32_t(b->binding);
6975 vkbinding.descriptorType = toVkDescriptorType(b);
6977 vkbinding.descriptorCount = b->u.stex.count;
6978 else
6979 vkbinding.descriptorCount = 1;
6980 vkbinding.stageFlags = toVkShaderStageFlags(b->stage);
6981 vkbindings.append(vkbinding);
6982 }
6983
6984 VkDescriptorSetLayoutCreateInfo layoutInfo = {};
6985 layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
6986 layoutInfo.bindingCount = uint32_t(vkbindings.size());
6987 layoutInfo.pBindings = vkbindings.constData();
6988
6989 VkResult err = rhiD->df->vkCreateDescriptorSetLayout(rhiD->dev, &layoutInfo, nullptr, &layout);
6990 if (err != VK_SUCCESS) {
6991 qWarning("Failed to create descriptor set layout: %d", err);
6992 return false;
6993 }
6994
6995 VkDescriptorSetAllocateInfo allocInfo = {};
6996 allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
6997 allocInfo.descriptorSetCount = QVK_FRAMES_IN_FLIGHT;
6998 VkDescriptorSetLayout layouts[QVK_FRAMES_IN_FLIGHT];
6999 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
7000 layouts[i] = layout;
7001 allocInfo.pSetLayouts = layouts;
7002 if (!rhiD->allocateDescriptorSet(&allocInfo, descSets, &poolIndex))
7003 return false;
7004
7005 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
7008 memset(&bd, 0, sizeof(BoundResourceData));
7009 }
7010
7012 generation += 1;
7013 rhiD->registerResource(this);
7014 return true;
7015}
7016
7018{
7020 std::copy(m_bindings.cbegin(), m_bindings.cend(), std::back_inserter(sortedBindings));
7021 if (!flags.testFlag(BindingsAreSorted))
7023
7024 // Reset the state tracking table too - it can deal with assigning a
7025 // different QRhiBuffer/Texture/Sampler for a binding point, but it cannot
7026 // detect changes in the associated data, such as the buffer offset. And
7027 // just like after a create(), a call to updateResources() may lead to now
7028 // specifying a different offset for the same QRhiBuffer for a given binding
7029 // point. The same applies to other type of associated data that is not part
7030 // of the layout, such as the mip level for a StorageImage. Instead of
7031 // complicating the checks in setShaderResources(), reset the table here
7032 // just like we do in create().
7033 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
7036 memset(&bd, 0, sizeof(BoundResourceData));
7037 }
7038
7039 generation += 1;
7040}
7041
7044{
7045}
7046
7048{
7049 destroy();
7050}
7051
7053{
7054 if (!pipeline && !layout)
7055 return;
7056
7059 e.lastActiveFrameSlot = lastActiveFrameSlot;
7060
7061 e.pipelineState.pipeline = pipeline;
7062 e.pipelineState.layout = layout;
7063
7064 pipeline = VK_NULL_HANDLE;
7065 layout = VK_NULL_HANDLE;
7066
7068 if (rhiD) {
7069 rhiD->releaseQueue.append(e);
7070 rhiD->unregisterResource(this);
7071 }
7072}
7073
7075{
7076 if (pipeline)
7077 destroy();
7078
7080 rhiD->pipelineCreationStart();
7081 if (!rhiD->sanityCheckGraphicsPipeline(this))
7082 return false;
7083
7084 if (!rhiD->ensurePipelineCache())
7085 return false;
7086
7087 VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
7088 pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
7089 pipelineLayoutInfo.setLayoutCount = 1;
7092 pipelineLayoutInfo.pSetLayouts = &srbD->layout;
7093 VkResult err = rhiD->df->vkCreatePipelineLayout(rhiD->dev, &pipelineLayoutInfo, nullptr, &layout);
7094 if (err != VK_SUCCESS) {
7095 qWarning("Failed to create pipeline layout: %d", err);
7096 return false;
7097 }
7098
7099 VkGraphicsPipelineCreateInfo pipelineInfo = {};
7100 pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
7101
7104 for (const QRhiShaderStage &shaderStage : m_shaderStages) {
7105 const QShader bakedShader = shaderStage.shader();
7106 const QShaderCode spirv = bakedShader.shader({ QShader::SpirvShader, 100, shaderStage.shaderVariant() });
7107 if (spirv.shader().isEmpty()) {
7108 qWarning() << "No SPIR-V 1.0 shader code found in baked shader" << bakedShader;
7109 return false;
7110 }
7111 VkShaderModule shader = rhiD->createShader(spirv.shader());
7112 if (shader) {
7113 shaders.append(shader);
7114 VkPipelineShaderStageCreateInfo shaderInfo = {};
7115 shaderInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
7116 shaderInfo.stage = toVkShaderStage(shaderStage.type());
7117 shaderInfo.module = shader;
7118 shaderInfo.pName = spirv.entryPoint().constData();
7119 shaderStageCreateInfos.append(shaderInfo);
7120 }
7121 }
7122 pipelineInfo.stageCount = uint32_t(shaderStageCreateInfos.size());
7123 pipelineInfo.pStages = shaderStageCreateInfos.constData();
7124
7126#ifdef VK_EXT_vertex_attribute_divisor
7128#endif
7129 int bindingIndex = 0;
7131 it != itEnd; ++it, ++bindingIndex)
7132 {
7133 VkVertexInputBindingDescription bindingInfo = {
7134 uint32_t(bindingIndex),
7135 it->stride(),
7136 it->classification() == QRhiVertexInputBinding::PerVertex
7137 ? VK_VERTEX_INPUT_RATE_VERTEX : VK_VERTEX_INPUT_RATE_INSTANCE
7138 };
7139 if (it->classification() == QRhiVertexInputBinding::PerInstance && it->instanceStepRate() != 1) {
7140#ifdef VK_EXT_vertex_attribute_divisor
7141 if (rhiD->caps.vertexAttribDivisor) {
7142 nonOneStepRates.append({ uint32_t(bindingIndex), it->instanceStepRate() });
7143 } else
7144#endif
7145 {
7146 qWarning("QRhiVulkan: Instance step rates other than 1 not supported without "
7147 "VK_EXT_vertex_attribute_divisor on the device and "
7148 "VK_KHR_get_physical_device_properties2 on the instance");
7149 }
7150 }
7151 vertexBindings.append(bindingInfo);
7152 }
7155 it != itEnd; ++it)
7156 {
7157 VkVertexInputAttributeDescription attributeInfo = {
7158 uint32_t(it->location()),
7159 uint32_t(it->binding()),
7160 toVkAttributeFormat(it->format()),
7161 it->offset()
7162 };
7163 vertexAttributes.append(attributeInfo);
7164 }
7165 VkPipelineVertexInputStateCreateInfo vertexInputInfo = {};
7166 vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
7167 vertexInputInfo.vertexBindingDescriptionCount = uint32_t(vertexBindings.size());
7168 vertexInputInfo.pVertexBindingDescriptions = vertexBindings.constData();
7169 vertexInputInfo.vertexAttributeDescriptionCount = uint32_t(vertexAttributes.size());
7170 vertexInputInfo.pVertexAttributeDescriptions = vertexAttributes.constData();
7171#ifdef VK_EXT_vertex_attribute_divisor
7172 VkPipelineVertexInputDivisorStateCreateInfoEXT divisorInfo = {};
7173 if (!nonOneStepRates.isEmpty()) {
7174 divisorInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT;
7175 divisorInfo.vertexBindingDivisorCount = uint32_t(nonOneStepRates.size());
7176 divisorInfo.pVertexBindingDivisors = nonOneStepRates.constData();
7177 vertexInputInfo.pNext = &divisorInfo;
7178 }
7179#endif
7180 pipelineInfo.pVertexInputState = &vertexInputInfo;
7181
7183 dynEnable << VK_DYNAMIC_STATE_VIEWPORT;
7184 dynEnable << VK_DYNAMIC_STATE_SCISSOR; // ignore UsesScissor - Vulkan requires a scissor for the viewport always
7186 dynEnable << VK_DYNAMIC_STATE_BLEND_CONSTANTS;
7188 dynEnable << VK_DYNAMIC_STATE_STENCIL_REFERENCE;
7189
7190 VkPipelineDynamicStateCreateInfo dynamicInfo = {};
7191 dynamicInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
7192 dynamicInfo.dynamicStateCount = uint32_t(dynEnable.size());
7193 dynamicInfo.pDynamicStates = dynEnable.constData();
7194 pipelineInfo.pDynamicState = &dynamicInfo;
7195
7196 VkPipelineViewportStateCreateInfo viewportInfo = {};
7197 viewportInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
7198 viewportInfo.viewportCount = viewportInfo.scissorCount = 1;
7199 pipelineInfo.pViewportState = &viewportInfo;
7200
7201 VkPipelineInputAssemblyStateCreateInfo inputAsmInfo = {};
7202 inputAsmInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
7203 inputAsmInfo.topology = toVkTopology(m_topology);
7204 inputAsmInfo.primitiveRestartEnable = (m_topology == TriangleStrip || m_topology == LineStrip);
7205 pipelineInfo.pInputAssemblyState = &inputAsmInfo;
7206
7207 VkPipelineTessellationStateCreateInfo tessInfo = {};
7208#ifdef VK_VERSION_1_1
7209 VkPipelineTessellationDomainOriginStateCreateInfo originInfo = {};
7210#endif
7211 if (m_topology == Patches) {
7212 tessInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO;
7213 tessInfo.patchControlPoints = uint32_t(qMax(1, m_patchControlPointCount));
7214
7215 // To be able to use the same tess.evaluation shader with both OpenGL
7216 // and Vulkan, flip the tessellation domain origin to be lower left.
7217 // This allows declaring the winding order in the shader to be CCW and
7218 // still have it working with both APIs. This requires Vulkan 1.1 (or
7219 // VK_KHR_maintenance2 but don't bother with that).
7220#ifdef VK_VERSION_1_1
7221 if (rhiD->caps.apiVersion >= QVersionNumber(1, 1)) {
7222 originInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO;
7223 originInfo.domainOrigin = VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT;
7224 tessInfo.pNext = &originInfo;
7225 } else {
7226 qWarning("Proper tessellation support requires Vulkan 1.1 or newer, leaving domain origin unset");
7227 }
7228#else
7229 qWarning("QRhi was built without Vulkan 1.1 headers, this is not sufficient for proper tessellation support");
7230#endif
7231
7232 pipelineInfo.pTessellationState = &tessInfo;
7233 }
7234
7235 VkPipelineRasterizationStateCreateInfo rastInfo = {};
7236 rastInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
7237 rastInfo.cullMode = toVkCullMode(m_cullMode);
7238 rastInfo.frontFace = toVkFrontFace(m_frontFace);
7240 rastInfo.depthBiasEnable = true;
7241 rastInfo.depthBiasConstantFactor = float(m_depthBias);
7242 rastInfo.depthBiasSlopeFactor = m_slopeScaledDepthBias;
7243 }
7244 rastInfo.lineWidth = rhiD->caps.wideLines ? m_lineWidth : 1.0f;
7245 rastInfo.polygonMode = toVkPolygonMode(m_polygonMode);
7246 pipelineInfo.pRasterizationState = &rastInfo;
7247
7248 VkPipelineMultisampleStateCreateInfo msInfo = {};
7249 msInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
7250 msInfo.rasterizationSamples = rhiD->effectiveSampleCount(m_sampleCount);
7251 pipelineInfo.pMultisampleState = &msInfo;
7252
7253 VkPipelineDepthStencilStateCreateInfo dsInfo = {};
7254 dsInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
7255 dsInfo.depthTestEnable = m_depthTest;
7256 dsInfo.depthWriteEnable = m_depthWrite;
7257 dsInfo.depthCompareOp = toVkCompareOp(m_depthOp);
7258 dsInfo.stencilTestEnable = m_stencilTest;
7259 if (m_stencilTest) {
7260 fillVkStencilOpState(&dsInfo.front, m_stencilFront);
7261 dsInfo.front.compareMask = m_stencilReadMask;
7262 dsInfo.front.writeMask = m_stencilWriteMask;
7263 fillVkStencilOpState(&dsInfo.back, m_stencilBack);
7264 dsInfo.back.compareMask = m_stencilReadMask;
7265 dsInfo.back.writeMask = m_stencilWriteMask;
7266 }
7267 pipelineInfo.pDepthStencilState = &dsInfo;
7268
7269 VkPipelineColorBlendStateCreateInfo blendInfo = {};
7270 blendInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
7272 for (const QRhiGraphicsPipeline::TargetBlend &b : std::as_const(m_targetBlends)) {
7273 VkPipelineColorBlendAttachmentState blend = {};
7274 blend.blendEnable = b.enable;
7275 blend.srcColorBlendFactor = toVkBlendFactor(b.srcColor);
7276 blend.dstColorBlendFactor = toVkBlendFactor(b.dstColor);
7277 blend.colorBlendOp = toVkBlendOp(b.opColor);
7278 blend.srcAlphaBlendFactor = toVkBlendFactor(b.srcAlpha);
7279 blend.dstAlphaBlendFactor = toVkBlendFactor(b.dstAlpha);
7280 blend.alphaBlendOp = toVkBlendOp(b.opAlpha);
7281 blend.colorWriteMask = toVkColorComponents(b.colorWrite);
7282 vktargetBlends.append(blend);
7283 }
7284 if (vktargetBlends.isEmpty()) {
7285 VkPipelineColorBlendAttachmentState blend = {};
7286 blend.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT
7287 | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
7288 vktargetBlends.append(blend);
7289 }
7290 blendInfo.attachmentCount = uint32_t(vktargetBlends.size());
7291 blendInfo.pAttachments = vktargetBlends.constData();
7292 pipelineInfo.pColorBlendState = &blendInfo;
7293
7294 pipelineInfo.layout = layout;
7295
7297 pipelineInfo.renderPass = QRHI_RES(const QVkRenderPassDescriptor, m_renderPassDesc)->rp;
7298
7299 err = rhiD->df->vkCreateGraphicsPipelines(rhiD->dev, rhiD->pipelineCache, 1, &pipelineInfo, nullptr, &pipeline);
7300
7301 for (VkShaderModule shader : shaders)
7302 rhiD->df->vkDestroyShaderModule(rhiD->dev, shader, nullptr);
7303
7304 if (err != VK_SUCCESS) {
7305 qWarning("Failed to create graphics pipeline: %d", err);
7306 return false;
7307 }
7308
7309 rhiD->pipelineCreationEnd();
7311 generation += 1;
7312 rhiD->registerResource(this);
7313 return true;
7314}
7315
7317 : QRhiComputePipeline(rhi)
7318{
7319}
7320
7322{
7323 destroy();
7324}
7325
7327{
7328 if (!pipeline && !layout)
7329 return;
7330
7333 e.lastActiveFrameSlot = lastActiveFrameSlot;
7334
7335 e.pipelineState.pipeline = pipeline;
7336 e.pipelineState.layout = layout;
7337
7338 pipeline = VK_NULL_HANDLE;
7339 layout = VK_NULL_HANDLE;
7340
7342 if (rhiD) {
7343 rhiD->releaseQueue.append(e);
7344 rhiD->unregisterResource(this);
7345 }
7346}
7347
7349{
7350 if (pipeline)
7351 destroy();
7352
7354 rhiD->pipelineCreationStart();
7355 if (!rhiD->ensurePipelineCache())
7356 return false;
7357
7358 VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
7359 pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
7360 pipelineLayoutInfo.setLayoutCount = 1;
7363 pipelineLayoutInfo.pSetLayouts = &srbD->layout;
7364 VkResult err = rhiD->df->vkCreatePipelineLayout(rhiD->dev, &pipelineLayoutInfo, nullptr, &layout);
7365 if (err != VK_SUCCESS) {
7366 qWarning("Failed to create pipeline layout: %d", err);
7367 return false;
7368 }
7369
7370 VkComputePipelineCreateInfo pipelineInfo = {};
7371 pipelineInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
7372 pipelineInfo.layout = layout;
7373
7375 qWarning("Compute pipeline requires a compute shader stage");
7376 return false;
7377 }
7378 const QShader bakedShader = m_shaderStage.shader();
7379 const QShaderCode spirv = bakedShader.shader({ QShader::SpirvShader, 100, m_shaderStage.shaderVariant() });
7380 if (spirv.shader().isEmpty()) {
7381 qWarning() << "No SPIR-V 1.0 shader code found in baked shader" << bakedShader;
7382 return false;
7383 }
7384 if (bakedShader.stage() != QShader::ComputeStage) {
7385 qWarning() << bakedShader << "is not a compute shader";
7386 return false;
7387 }
7388 VkShaderModule shader = rhiD->createShader(spirv.shader());
7389 VkPipelineShaderStageCreateInfo shaderInfo = {};
7390 shaderInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
7391 shaderInfo.stage = VK_SHADER_STAGE_COMPUTE_BIT;
7392 shaderInfo.module = shader;
7393 shaderInfo.pName = spirv.entryPoint().constData();
7394 pipelineInfo.stage = shaderInfo;
7395
7396 err = rhiD->df->vkCreateComputePipelines(rhiD->dev, rhiD->pipelineCache, 1, &pipelineInfo, nullptr, &pipeline);
7397 rhiD->df->vkDestroyShaderModule(rhiD->dev, shader, nullptr);
7398 if (err != VK_SUCCESS) {
7399 qWarning("Failed to create graphics pipeline: %d", err);
7400 return false;
7401 }
7402
7403 rhiD->pipelineCreationEnd();
7405 generation += 1;
7406 rhiD->registerResource(this);
7407 return true;
7408}
7409
7411 : QRhiCommandBuffer(rhi)
7412{
7413 resetState();
7414}
7415
7417{
7418 destroy();
7419}
7420
7422{
7423 // nothing to do here, cb is not owned by us
7424}
7425
7427{
7428 // Ok this is messy but no other way has been devised yet. Outside
7429 // begin(Compute)Pass - end(Compute)Pass it is simple - just return the
7430 // primary VkCommandBuffer. Inside, however, we need to provide the current
7431 // secondary command buffer (typically the one started by beginExternal(),
7432 // in case we are between beginExternal - endExternal inside a pass).
7433
7435 nativeHandlesStruct.commandBuffer = cb;
7436 } else {
7439 else
7440 nativeHandlesStruct.commandBuffer = cb;
7441 }
7442
7443 return &nativeHandlesStruct;
7444}
7445
7447 : QRhiSwapChain(rhi),
7448 rtWrapper(rhi, this),
7449 cbWrapper(rhi)
7450{
7451}
7452
7454{
7455 destroy();
7456}
7457
7459{
7460 if (sc == VK_NULL_HANDLE)
7461 return;
7462
7464 if (rhiD) {
7465 rhiD->swapchains.remove(this);
7466 rhiD->releaseSwapChainResources(this);
7467 }
7468
7469 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
7471 frame.cmdBuf = VK_NULL_HANDLE;
7472 frame.timestampQueryIndex = -1;
7473 }
7474
7475 surface = lastConnectedSurface = VK_NULL_HANDLE;
7476
7477 if (rhiD)
7478 rhiD->unregisterResource(this);
7479}
7480
7482{
7483 return &cbWrapper;
7484}
7485
7487{
7488 return &rtWrapper;
7489}
7490
7492{
7493 if (!ensureSurface())
7494 return QSize();
7495
7496 // The size from the QWindow may not exactly match the surface... so if a
7497 // size is reported from the surface, use that.
7498 VkSurfaceCapabilitiesKHR surfaceCaps = {};
7500 rhiD->vkGetPhysicalDeviceSurfaceCapabilitiesKHR(rhiD->physDev, surface, &surfaceCaps);
7501 VkExtent2D bufferSize = surfaceCaps.currentExtent;
7502 if (bufferSize.width == uint32_t(-1)) {
7503 Q_ASSERT(bufferSize.height == uint32_t(-1));
7504 return m_window->size() * m_window->devicePixelRatio();
7505 }
7506 return QSize(int(bufferSize.width), int(bufferSize.height));
7507}
7508
7509static inline bool hdrFormatMatchesVkSurfaceFormat(QRhiSwapChain::Format f, const VkSurfaceFormatKHR &s)
7510{
7511 switch (f) {
7513 return s.format == VK_FORMAT_R16G16B16A16_SFLOAT
7514 && s.colorSpace == VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT;
7516 return (s.format == VK_FORMAT_A2B10G10R10_UNORM_PACK32 || s.format == VK_FORMAT_A2R10G10B10_UNORM_PACK32)
7517 && s.colorSpace == VK_COLOR_SPACE_HDR10_ST2084_EXT;
7518 default:
7519 break;
7520 }
7521 return false;
7522}
7523
7525{
7526 if (f == SDR)
7527 return true;
7528
7529 if (!m_window) {
7530 qWarning("Attempted to call isFormatSupported() without a window set");
7531 return false;
7532 }
7533
7534 // we may be called before create so query the surface
7535 VkSurfaceKHR surf = QVulkanInstance::surfaceForWindow(m_window);
7536
7538 uint32_t formatCount = 0;
7539 rhiD->vkGetPhysicalDeviceSurfaceFormatsKHR(rhiD->physDev, surf, &formatCount, nullptr);
7541 if (formatCount) {
7542 rhiD->vkGetPhysicalDeviceSurfaceFormatsKHR(rhiD->physDev, surf, &formatCount, formats.data());
7543 for (uint32_t i = 0; i < formatCount; ++i) {
7545 return true;
7546 }
7547 }
7548
7549 return false;
7550}
7551
7553{
7554 // not yet built so cannot rely on data computed in createOrResize()
7555
7556 if (!ensureSurface()) // make sure sampleCount and colorFormat reflect what was requested
7557 return nullptr;
7558
7561 if (!rhiD->createDefaultRenderPass(rp,
7562 m_depthStencil != nullptr,
7563 samples,
7564 colorFormat))
7565 {
7566 delete rp;
7567 return nullptr;
7568 }
7569
7570 rp->ownsRp = true;
7572 rhiD->registerResource(rp);
7573 return rp;
7574}
7575
7576static inline bool isSrgbFormat(VkFormat format)
7577{
7578 switch (format) {
7579 case VK_FORMAT_R8_SRGB:
7580 case VK_FORMAT_R8G8_SRGB:
7581 case VK_FORMAT_R8G8B8_SRGB:
7582 case VK_FORMAT_B8G8R8_SRGB:
7583 case VK_FORMAT_R8G8B8A8_SRGB:
7584 case VK_FORMAT_B8G8R8A8_SRGB:
7585 case VK_FORMAT_A8B8G8R8_SRGB_PACK32:
7586 return true;
7587 default:
7588 return false;
7589 }
7590}
7591
7593{
7594 // Do nothing when already done, however window may change so check the
7595 // surface is still the same. Some of the queries below are very expensive
7596 // with some implementations so it is important to do the rest only once
7597 // per surface.
7598
7600 VkSurfaceKHR surf = QVulkanInstance::surfaceForWindow(m_window);
7601 if (!surf) {
7602 qWarning("Failed to get surface for window");
7603 return false;
7604 }
7605 if (surface == surf)
7606 return true;
7607
7608 surface = surf;
7609
7611 if (!rhiD->inst->supportsPresent(rhiD->physDev, rhiD->gfxQueueFamilyIdx, m_window)) {
7612 qWarning("Presenting not supported on this window");
7613 return false;
7614 }
7615
7616 quint32 formatCount = 0;
7617 rhiD->vkGetPhysicalDeviceSurfaceFormatsKHR(rhiD->physDev, surface, &formatCount, nullptr);
7619 if (formatCount)
7620 rhiD->vkGetPhysicalDeviceSurfaceFormatsKHR(rhiD->physDev, surface, &formatCount, formats.data());
7621
7622 // See if there is a better match than the default BGRA8 format. (but if
7623 // not, we will stick to the default)
7624 const bool srgbRequested = m_flags.testFlag(sRGB);
7625 for (int i = 0; i < int(formatCount); ++i) {
7626 if (formats[i].format != VK_FORMAT_UNDEFINED) {
7627 bool ok = srgbRequested == isSrgbFormat(formats[i].format);
7628 if (m_format != SDR)
7630 if (ok) {
7631 colorFormat = formats[i].format;
7632 colorSpace = formats[i].colorSpace;
7633 break;
7634 }
7635 }
7636 }
7637
7638 samples = rhiD->effectiveSampleCount(m_sampleCount);
7639
7640 quint32 presModeCount = 0;
7641 rhiD->vkGetPhysicalDeviceSurfacePresentModesKHR(rhiD->physDev, surface, &presModeCount, nullptr);
7642 supportedPresentationModes.resize(presModeCount);
7643 rhiD->vkGetPhysicalDeviceSurfacePresentModesKHR(rhiD->physDev, surface, &presModeCount,
7645
7646 return true;
7647}
7648
7650{
7652 const bool needsRegistration = !window || window != m_window;
7653
7654 // Can be called multiple times due to window resizes - that is not the
7655 // same as a simple destroy+create (as with other resources). Thus no
7656 // destroy() here. See recreateSwapChain().
7657
7658 // except if the window actually changes
7659 if (window && window != m_window)
7660 destroy();
7661
7662 window = m_window;
7665
7666 if (!rhiD->recreateSwapChain(this)) {
7667 qWarning("Failed to create new swapchain");
7668 return false;
7669 }
7670
7671 if (needsRegistration)
7672 rhiD->swapchains.insert(this);
7673
7675 qWarning("Depth-stencil buffer's sampleCount (%d) does not match color buffers' sample count (%d). Expect problems.",
7677 }
7681 if (!m_depthStencil->create())
7682 qWarning("Failed to rebuild swapchain's associated depth-stencil buffer for size %dx%d",
7684 } else {
7685 qWarning("Depth-stencil buffer's size (%dx%d) does not match the surface size (%dx%d). Expect problems.",
7688 }
7689 }
7690
7691 if (!m_renderPassDesc)
7692 qWarning("QVkSwapChain: No renderpass descriptor set. See newCompatibleRenderPassDescriptor() and setRenderPassDescriptor().");
7693
7694 rtWrapper.setRenderPassDescriptor(m_renderPassDesc); // for the public getter in QRhiRenderTarget
7697
7699 rtWrapper.d.dpr = float(window->devicePixelRatio());
7702 if (m_depthStencil) {
7705 } else {
7707 ds = nullptr;
7708 }
7709 if (samples > VK_SAMPLE_COUNT_1_BIT)
7711 else
7713
7714 for (int i = 0; i < bufferCount; ++i) {
7716 VkImageView views[3] = { // color, ds, resolve
7717 samples > VK_SAMPLE_COUNT_1_BIT ? image.msaaImageView : image.imageView,
7718 ds ? ds->imageView : VK_NULL_HANDLE,
7719 samples > VK_SAMPLE_COUNT_1_BIT ? image.imageView : VK_NULL_HANDLE
7720 };
7721
7722 VkFramebufferCreateInfo fbInfo = {};
7723 fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
7724 fbInfo.renderPass = rtWrapper.d.rp->rp;
7725 fbInfo.attachmentCount = uint32_t(rtWrapper.d.colorAttCount + rtWrapper.d.dsAttCount + rtWrapper.d.resolveAttCount);
7726 fbInfo.pAttachments = views;
7727 fbInfo.width = uint32_t(pixelSize.width());
7728 fbInfo.height = uint32_t(pixelSize.height());
7729 fbInfo.layers = 1;
7730
7731 VkResult err = rhiD->df->vkCreateFramebuffer(rhiD->dev, &fbInfo, nullptr, &image.fb);
7732 if (err != VK_SUCCESS) {
7733 qWarning("Failed to create framebuffer: %d", err);
7734 return false;
7735 }
7736 }
7737
7738 frameCount = 0;
7739
7740 if (needsRegistration)
7741 rhiD->registerResource(this);
7742
7743 return true;
7744}
7745
IOBluetoothDevice * device
bool testBit(qsizetype i) const
Returns true if the bit at index position i is 1; otherwise returns false.
Definition qbitarray.h:84
void setBit(qsizetype i)
Sets the bit at index position i to 1.
Definition qbitarray.h:88
void clearBit(qsizetype i)
Sets the bit at index position i to 0.
Definition qbitarray.h:92
qsizetype size() const
Returns the number of bits stored in the bit array.
Definition qbitarray.h:31
\inmodule QtCore
\inmodule QtCore
Definition qbytearray.h:57
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:534
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:474
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:122
QList< QByteArray > split(char sep) const
Splits the byte array into subarrays wherever sep occurs, and returns the list of those arrays.
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:106
static QByteArray number(int, int base=10)
Returns a byte-array representing the whole number n as text.
void resize(qsizetype size)
Sets the size of the byte array to size bytes.
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
float greenF() const noexcept
Returns the green color component of this color.
Definition qcolor.cpp:1643
float redF() const noexcept
Returns the red color component of this color.
Definition qcolor.cpp:1611
float alphaF() const noexcept
Returns the alpha color component of this color.
Definition qcolor.cpp:1497
float blueF() const noexcept
Returns the blue color component of this color.
Definition qcolor.cpp:1675
iterator begin()
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the first item in the hash.
Definition qhash.h:1202
iterator erase(const_iterator it)
Definition qhash.h:1223
iterator end() noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the last ...
Definition qhash.h:1206
\inmodule QtGui
Definition qimage.h:37
qsizetype sizeInBytes() const
Definition qimage.cpp:1526
bool isNull() const
Returns true if it is a null image, otherwise returns false.
Definition qimage.cpp:1197
Definition qlist.h:74
qsizetype size() const noexcept
Definition qlist.h:386
const_pointer constData() const noexcept
Definition qlist.h:416
bool isEmpty() const noexcept
Definition qlist.h:390
pointer data()
Definition qlist.h:414
void resize(qsizetype size)
Definition qlist.h:392
void append(parameter_type t)
Definition qlist.h:441
The QMatrix4x4 class represents a 4x4 transformation matrix in 3D space.
Definition qmatrix4x4.h:25
\inmodule QtCore\reentrant
Definition qpoint.h:23
constexpr bool isNull() const noexcept
Returns true if both the x and y coordinates are set to 0, otherwise returns false.
Definition qpoint.h:122
constexpr int x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:127
constexpr int y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:132
quint32 size() const
Definition qrhi_p.h:344
const char * constData() const
Definition qrhi_p.h:340
\inmodule QtGui
Definition qrhi.h:834
UsageFlags m_usage
Definition qrhi.h:876
Type m_type
Definition qrhi.h:875
Type
Specifies storage type of buffer resource.
Definition qrhi.h:836
@ Immutable
Definition qrhi.h:837
@ Dynamic
Definition qrhi.h:839
@ IndexBuffer
Definition qrhi.h:844
@ VertexBuffer
Definition qrhi.h:843
@ UniformBuffer
Definition qrhi.h:845
@ StorageBuffer
Definition qrhi.h:846
quint32 m_size
Definition qrhi.h:877
\inmodule QtGui
Definition qrhi.h:568
\inmodule QtGui
Definition qrhi.h:1614
QPair< QRhiBuffer *, quint32 > VertexInput
Synonym for QPair<QRhiBuffer *, quint32>.
Definition qrhi.h:1643
QPair< int, quint32 > DynamicOffset
Synonym for QPair<int, quint32>.
Definition qrhi.h:1639
IndexFormat
Specifies the index data type.
Definition qrhi.h:1616
\inmodule QtGui
Definition qrhi.h:1585
QRhiShaderStage m_shaderStage
Definition qrhi.h:1607
QRhiShaderResourceBindings * m_shaderResourceBindings
Definition qrhi.h:1608
\inmodule QtGui
Definition qrhi.h:44
float depthClearValue() const
Definition qrhi.h:49
quint32 stencilClearValue() const
Definition qrhi.h:52
\inmodule QtGui
Definition qrhi.h:1241
QRhiRenderPassDescriptor * m_renderPassDesc
Definition qrhi.h:1474
quint32 m_stencilReadMask
Definition qrhi.h:1462
BlendOp
Specifies the blend operation.
Definition qrhi.h:1302
PolygonMode
Specifies the polygon rasterization mode.
Definition qrhi.h:1350
FrontFace
Specifies the front face winding order.
Definition qrhi.h:1267
BlendFactor
Specifies the blend factor.
Definition qrhi.h:1280
StencilOpState m_stencilFront
Definition qrhi.h:1460
quint32 m_stencilWriteMask
Definition qrhi.h:1463
CompareOp
Specifies the depth or stencil comparison function.
Definition qrhi.h:1321
Topology m_topology
Definition qrhi.h:1452
CullMode
Specifies the culling mode.
Definition qrhi.h:1261
QVarLengthArray< QRhiShaderStage, 4 > m_shaderStages
Definition qrhi.h:1471
QRhiVertexInputLayout m_vertexInputLayout
Definition qrhi.h:1472
QVarLengthArray< TargetBlend, 8 > m_targetBlends
Definition qrhi.h:1455
QRhiShaderResourceBindings * m_shaderResourceBindings
Definition qrhi.h:1473
PolygonMode m_polygonMode
Definition qrhi.h:1469
float m_slopeScaledDepthBias
Definition qrhi.h:1467
Topology
Specifies the primitive topology.
Definition qrhi.h:1251
StencilOpState m_stencilBack
Definition qrhi.h:1461
FrontFace m_frontFace
Definition qrhi.h:1454
StencilOp
Specifies the stencil operation.
Definition qrhi.h:1332
int m_patchControlPointCount
Definition qrhi.h:1468
CullMode m_cullMode
Definition qrhi.h:1453
CompareOp m_depthOp
Definition qrhi.h:1458
bool isCompressedFormat(QRhiTexture::Format format) const
Definition qrhi.cpp:7731
static const QRhiShaderResourceBinding::Data * shaderResourceBindingData(const QRhiShaderResourceBinding &binding)
Definition qrhi_p.h:210
quint32 pipelineCacheRhiId() const
Definition qrhi_p.h:186
void compressedFormatInfo(QRhiTexture::Format format, const QSize &size, quint32 *bpl, quint32 *byteSize, QSize *blockDim) const
Definition qrhi.cpp:7738
static bool sortedBindingLessThan(const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b)
Definition qrhi_p.h:220
qint64 totalPipelineCreationTime() const
Definition qrhi_p.h:202
void textureFormatInfo(QRhiTexture::Format format, const QSize &size, quint32 *bpl, quint32 *byteSize, quint32 *bytesPerPixel) const
Definition qrhi.cpp:7858
static TextureStage toPassTrackerTextureStage(QRhiShaderResourceBinding::StageFlags stages)
Definition qrhi.cpp:10651
TextureIterator cendTextures() const
Definition qrhi_p.h:673
static BufferStage toPassTrackerBufferStage(QRhiShaderResourceBinding::StageFlags stages)
Definition qrhi.cpp:10632
BufferIterator cbeginBuffers() const
Definition qrhi_p.h:662
BufferIterator cendBuffers() const
Definition qrhi_p.h:663
TextureIterator cbeginTextures() const
Definition qrhi_p.h:672
bool isEmpty() const
Definition qrhi.cpp:10543
void registerBuffer(QRhiBuffer *buf, int slot, BufferAccess *access, BufferStage *stage, const UsageState &state)
Definition qrhi.cpp:10560
void registerTexture(QRhiTexture *tex, TextureAccess *access, TextureStage *stage, const UsageState &state)
Definition qrhi.cpp:10599
int layer() const
Definition qrhi.h:773
QRhiTexture * texture() const
Definition qrhi.h:770
int level() const
Definition qrhi.h:776
\inmodule QtGui
Definition qrhi.h:1071
Flags flags() const
Definition qrhi.h:1098
void setPixelSize(const QSize &sz)
Sets the size (in pixels) to sz.
Definition qrhi.h:1093
QSize pixelSize() const
Definition qrhi.h:1092
int sampleCount() const
Definition qrhi.h:1095
int m_sampleCount
Definition qrhi.h:1111
QRhiTexture::Format m_backingFormatHint
Definition qrhi.h:1113
QSize m_pixelSize
Definition qrhi.h:1110
Type
Specifies the type of the renderbuffer.
Definition qrhi.h:1073
virtual bool create()=0
Creates the corresponding native graphics resources.
@ UsedWithSwapChainOnly
Definition qrhi.h:1079
\inmodule QtGui
Definition qrhi.h:1119
\inmodule QtGui
Definition qrhi.h:1135
void setRenderPassDescriptor(QRhiRenderPassDescriptor *desc)
Sets the QRhiRenderPassDescriptor desc for use with this render target.
Definition qrhi.h:1142
virtual QSize pixelSize() const =0
QRhiRenderPassDescriptor * m_renderPassDesc
Definition qrhi.h:1146
QVarLengthArray< BufferOp, BUFFER_OPS_STATIC_ALLOC > bufferOps
Definition qrhi_p.h:508
QVarLengthArray< TextureOp, TEXTURE_OPS_STATIC_ALLOC > textureOps
Definition qrhi_p.h:512
static QRhiResourceUpdateBatchPrivate * get(QRhiResourceUpdateBatch *b)
Definition qrhi_p.h:523
\inmodule QtGui
Definition qrhi.h:1694
\inmodule QtGui
Definition qrhi.h:792
QByteArray m_objectName
Definition qrhi.h:830
@ SwapChainRenderTarget
Definition qrhi.h:800
@ TextureRenderTarget
Definition qrhi.h:801
quint64 m_id
Definition qrhi.h:829
virtual Type resourceType() const =0
void setName(const QByteArray &name)
Sets a name for the object.
Definition qrhi.cpp:3455
QRhiImplementation * m_rhi
Definition qrhi.h:828
\inmodule QtGui
Definition qrhi.h:1007
Filter m_minFilter
Definition qrhi.h:1062
Filter
Specifies the minification, magnification, or mipmap filtering.
Definition qrhi.h:1009
AddressMode m_addressV
Definition qrhi.h:1065
Filter m_mipmapMode
Definition qrhi.h:1063
AddressMode m_addressU
Definition qrhi.h:1064
AddressMode
Specifies the addressing mode.
Definition qrhi.h:1015
@ ClampToEdge
Definition qrhi.h:1017
CompareOp
Specifies the texture comparison function.
Definition qrhi.h:1021
@ LessOrEqual
Definition qrhi.h:1025
@ GreaterOrEqual
Definition qrhi.h:1028
CompareOp m_compareOp
Definition qrhi.h:1067
AddressMode m_addressW
Definition qrhi.h:1066
Filter m_magFilter
Definition qrhi.h:1061
\inmodule QtGui
Definition qrhi.h:138
std::array< int, 4 > scissor() const
Definition qrhi.h:143
\inmodule QtGui
Definition qrhi.h:431
Type
Specifies type of the shader resource bound to a binding point.
Definition qrhi.h:433
\inmodule QtGui
Definition qrhi.h:1190
QVarLengthArray< QRhiShaderResourceBinding, BINDING_PREALLOC > m_bindings
Definition qrhi.h:1222
\inmodule QtGui
Definition qrhi.h:371
QShader::Variant shaderVariant() const
Definition qrhi.h:392
QShader shader() const
Definition qrhi.h:389
Type
Specifies the type of the shader stage.
Definition qrhi.h:373
@ TessellationControl
Definition qrhi.h:375
@ TessellationEvaluation
Definition qrhi.h:376
Type type() const
Definition qrhi.h:386
\inmodule QtGui
Definition qrhi.h:1150
\inmodule QtGui
Definition qrhi.h:1513
QWindow * m_window
Definition qrhi.h:1572
int m_sampleCount
Definition qrhi.h:1576
@ SurfaceHasNonPreMulAlpha
Definition qrhi.h:1517
@ UsedAsTransferSource
Definition qrhi.h:1519
@ MinimalBufferCount
Definition qrhi.h:1521
@ SurfaceHasPreMulAlpha
Definition qrhi.h:1516
QRhiRenderPassDescriptor * m_renderPassDesc
Definition qrhi.h:1577
QSize m_currentPixelSize
Definition qrhi.h:1578
Flags m_flags
Definition qrhi.h:1573
Format
Describes the swapchain format.
Definition qrhi.h:1525
@ HDRExtendedSrgbLinear
Definition qrhi.h:1527
Format m_format
Definition qrhi.h:1574
QRhiRenderBuffer * m_depthStencil
Definition qrhi.h:1575
QPoint destinationTopLeft() const
Definition qrhi.h:749
QPoint sourceTopLeft() const
Definition qrhi.h:740
int destinationLevel() const
Definition qrhi.h:746
int sourceLevel() const
Definition qrhi.h:737
QSize pixelSize() const
Definition qrhi.h:731
int sourceLayer() const
Definition qrhi.h:734
int destinationLayer() const
Definition qrhi.h:743
const QRhiColorAttachment * cbeginColorAttachments() const
Definition qrhi.h:626
QRhiTexture * depthTexture() const
Definition qrhi.h:634
const QRhiColorAttachment * cendColorAttachments() const
Definition qrhi.h:627
QRhiRenderBuffer * depthStencilBuffer() const
Definition qrhi.h:631
qsizetype colorAttachmentCount() const
Definition qrhi.h:629
\inmodule QtGui
Definition qrhi.h:1161
QRhiTextureRenderTargetDescription m_desc
Definition qrhi.h:1183
QRhiTextureRenderTargetDescription description() const
Definition qrhi.h:1171
\inmodule QtGui
Definition qrhi.h:883
QSize m_pixelSize
Definition qrhi.h:995
int m_arraySize
Definition qrhi.h:997
int m_depth
Definition qrhi.h:996
Format format() const
Definition qrhi.h:960
@ ThreeDimensional
Definition qrhi.h:895
@ UsedAsTransferSource
Definition qrhi.h:890
@ UsedWithLoadStore
Definition qrhi.h:892
@ UsedWithGenerateMips
Definition qrhi.h:891
@ MipMapped
Definition qrhi.h:888
@ RenderTarget
Definition qrhi.h:886
@ OneDimensional
Definition qrhi.h:898
@ TextureArray
Definition qrhi.h:897
@ CubeMap
Definition qrhi.h:887
int m_arrayRangeLength
Definition qrhi.h:1001
Format
Specifies the texture format.
Definition qrhi.h:902
@ ASTC_10x8
Definition qrhi.h:947
@ ASTC_12x12
Definition qrhi.h:950
@ ASTC_8x5
Definition qrhi.h:942
@ ASTC_10x5
Definition qrhi.h:945
@ RGBA32F
Definition qrhi.h:914
@ ETC2_RGBA8
Definition qrhi.h:935
@ ASTC_5x5
Definition qrhi.h:939
@ ASTC_4x4
Definition qrhi.h:937
@ ASTC_6x6
Definition qrhi.h:941
@ ASTC_12x10
Definition qrhi.h:949
@ ETC2_RGB8
Definition qrhi.h:933
@ ASTC_5x4
Definition qrhi.h:938
@ RED_OR_ALPHA8
Definition qrhi.h:911
@ ASTC_6x5
Definition qrhi.h:940
@ ASTC_8x8
Definition qrhi.h:944
@ RGBA16F
Definition qrhi.h:913
@ RGB10A2
Definition qrhi.h:918
@ ASTC_10x6
Definition qrhi.h:946
@ ASTC_10x10
Definition qrhi.h:948
@ UnknownFormat
Definition qrhi.h:903
@ ETC2_RGB8A1
Definition qrhi.h:934
@ ASTC_8x6
Definition qrhi.h:943
Flags flags() const
Definition qrhi.h:980
void setSampleCount(int s)
Sets the sample count to s.
Definition qrhi.h:984
QSize pixelSize() const
Definition qrhi.h:963
Format m_format
Definition qrhi.h:994
Flags m_flags
Definition qrhi.h:999
int m_arrayRangeStart
Definition qrhi.h:1000
int m_sampleCount
Definition qrhi.h:998
void setPixelSize(const QSize &sz)
Sets the texture size, specified in pixels, to sz.
Definition qrhi.h:964
Format
Specifies the type of the element data.
Definition qrhi.h:234
const QRhiVertexInputAttribute * cendAttributes() const
Definition qrhi.h:337
const QRhiVertexInputBinding * cendBindings() const
Definition qrhi.h:325
const QRhiVertexInputAttribute * cbeginAttributes() const
Definition qrhi.h:336
const QRhiVertexInputBinding * cbeginBindings() const
Definition qrhi.h:324
\inmodule QtGui
Definition qrhi.h:85
\inmodule QtGui
\variable QRhiVulkanInitParams::inst
QVarLengthArray< DescriptorPoolData, 8 > descriptorPools
void recordTransitionPassResources(QVkCommandBuffer *cbD, const QRhiPassResourceTracker &tracker)
QVulkanFunctions * f
quint32 gfxQueueFamilyIdx
VkCommandBuffer startSecondaryCommandBuffer(QVkRenderTargetData *rtD=nullptr)
QRhiSwapChain * createSwapChain() override
PFN_vkGetSwapchainImagesKHR vkGetSwapchainImagesKHR
void debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) override
int resourceLimit(QRhi::ResourceLimit limit) const override
bool isDeviceLost() const override
void prepareUploadSubres(QVkTexture *texD, int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc, size_t *curOfs, void *mp, BufferImageCopyList *copyInfos)
VkPhysicalDeviceProperties physDevProperties
void executeDeferredReleases(bool forced=false)
uint32_t chooseTransientImageMemType(VkImage img, uint32_t startIndex)
QRhi::FrameOpResult finish() override
quint32 timestampValidBits
QRhiTextureRenderTarget * createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc, QRhiTextureRenderTarget::Flags flags) override
bool createTransientImage(VkFormat format, const QSize &pixelSize, VkImageUsageFlags usage, VkImageAspectFlags aspectMask, VkSampleCountFlagBits samples, VkDeviceMemory *mem, VkImage *images, VkImageView *views, int count)
void setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) override
void releaseCachedResources() override
VkSampleCountFlagBits effectiveSampleCount(int sampleCount)
QVkSwapChain * currentSwapChain
bool importedDevice
QByteArrayList requestedDeviceExtensions
bool createDefaultRenderPass(QVkRenderPassDescriptor *rpD, bool hasDepthStencil, VkSampleCountFlagBits samples, VkFormat colorFormat)
QRhiVulkan(QRhiVulkanInitParams *params, QRhiVulkanNativeHandles *importParams=nullptr)
void draw(QRhiCommandBuffer *cb, quint32 vertexCount, quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override
void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override
void endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override
QList< int > supportedSampleCounts() const override
void destroy() override
QRhiRenderBuffer * createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize, int sampleCount, QRhiRenderBuffer::Flags flags, QRhiTexture::Format backingFormatHint) override
QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) override
QBitArray timestampQueryPoolMap
double elapsedSecondsFromTimestamp(quint64 timestamp[2], bool *ok)
VkFormat optimalDsFormat
QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags) override
void trackedBufferBarrier(QVkCommandBuffer *cbD, QVkBuffer *bufD, int slot, VkAccessFlags access, VkPipelineStageFlags stage)
void endExternal(QRhiCommandBuffer *cb) override
QWindow * maybeWindow
void dispatch(QRhiCommandBuffer *cb, int x, int y, int z) override
VkPhysicalDeviceFeatures physDevFeatures
QRhiVulkanNativeHandles nativeHandlesStruct
bool allocateDescriptorSet(VkDescriptorSetAllocateInfo *allocInfo, VkDescriptorSet *result, int *resultPoolIndex)
void updateShaderResourceBindings(QRhiShaderResourceBindings *srb, int descSetIdx=-1)
bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const override
VkDevice dev
VkResult createDescriptorPool(VkDescriptorPool *pool)
void prepareNewFrame(QRhiCommandBuffer *cb)
void subresourceBarrier(QVkCommandBuffer *cbD, VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout, VkAccessFlags srcAccess, VkAccessFlags dstAccess, VkPipelineStageFlags srcStage, VkPipelineStageFlags dstStage, int startLayer, int layerCount, int startLevel, int levelCount)
void setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps) override
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override
QRhi::FrameOpResult startPrimaryCommandBuffer(VkCommandBuffer *cb)
double lastCompletedGpuTime(QRhiCommandBuffer *cb) override
void trackedRegisterTexture(QRhiPassResourceTracker *passResTracker, QVkTexture *texD, QRhiPassResourceTracker::TextureAccess access, QRhiPassResourceTracker::TextureStage stage)
bool releaseCachedResourcesCalledBeforeFrameStart
QRhi::Flags rhiFlags
PFN_vkCreateSwapchainKHR vkCreateSwapchainKHR
QRhiGraphicsPipeline * createGraphicsPipeline() override
QRhiComputePipeline * createComputePipeline() override
bool createOffscreenRenderPass(QVkRenderPassDescriptor *rpD, const QRhiColorAttachment *firstColorAttachment, const QRhiColorAttachment *lastColorAttachment, bool preserveColor, bool preserveDs, QRhiRenderBuffer *depthStencilBuffer, QRhiTexture *depthTexture)
QRhiTexture * createTexture(QRhiTexture::Format format, const QSize &pixelSize, int depth, int arraySize, int sampleCount, QRhiTexture::Flags flags) override
void waitCommandCompletion(int frameSlot)
void debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) override
PFN_vkGetPhysicalDeviceSurfacePresentModesKHR vkGetPhysicalDeviceSurfacePresentModesKHR
bool recreateSwapChain(QRhiSwapChain *swapChain)
PFN_vkQueuePresentKHR vkQueuePresentKHR
bool ensurePipelineCache(const void *initialData=nullptr, size_t initialDataSize=0)
QRhiDriverInfo driverInfoStruct
void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override
QVarLengthArray< TextureReadback, 2 > activeTextureReadbacks
void depthStencilExplicitBarrier(QVkCommandBuffer *cbD, QVkRenderBuffer *rbD)
void trackedImageBarrier(QVkCommandBuffer *cbD, QVkTexture *texD, VkImageLayout layout, VkAccessFlags access, VkPipelineStageFlags stage)
VkCommandPool cmdPool[QVK_FRAMES_IN_FLIGHT]
VkShaderModule createShader(const QByteArray &spirv)
void enqueueTransitionPassResources(QVkCommandBuffer *cbD)
void beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates, QRhiCommandBuffer::BeginPassFlags flags) override
bool create(QRhi::Flags flags) override
QVulkanDeviceFunctions * df
void setObjectName(uint64_t object, VkObjectType type, const QByteArray &name, int slot=-1)
bool isFeatureSupported(QRhi::Feature feature) const override
PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR
QVarLengthArray< BufferReadback, 2 > activeBufferReadbacks
void recordPrimaryCommandBuffer(QVkCommandBuffer *cbD)
bool isYUpInFramebuffer() const override
void debugMarkEnd(QRhiCommandBuffer *cb) override
QVulkanInstance * inst
QRhiSampler * createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter, QRhiSampler::Filter mipmapMode, QRhiSampler::AddressMode u, QRhiSampler::AddressMode v, QRhiSampler::AddressMode w) override
PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vkGetPhysicalDeviceSurfaceCapabilitiesKHR
void releaseSwapChainResources(QRhiSwapChain *swapChain)
void drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount, quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance) override
VkDeviceSize subresUploadByteSize(const QRhiTextureSubresourceUploadDescription &subresDesc) const
void endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override
VkQueryPool timestampQueryPool
void trackedRegisterBuffer(QRhiPassResourceTracker *passResTracker, QVkBuffer *bufD, int slot, QRhiPassResourceTracker::BufferAccess access, QRhiPassResourceTracker::BufferStage stage)
VkFormat optimalDepthStencilFormat()
QRhiStats statistics() override
void setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps) override
void activateTextureRenderTarget(QVkCommandBuffer *cbD, QVkTextureRenderTarget *rtD)
PFN_vkDestroySwapchainKHR vkDestroySwapchainKHR
void executeBufferHostWritesForSlot(QVkBuffer *bufD, int slot)
bool isYUpInNDC() const override
const QRhiNativeHandles * nativeHandles() override
void setPipelineCacheData(const QByteArray &data) override
void setVertexInput(QRhiCommandBuffer *cb, int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings, QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat) override
QRhiShaderResourceBindings * createShaderResourceBindings() override
VkPipelineCache pipelineCache
VkDeviceSize ubufAlign
void finishActiveReadbacks(bool forced=false)
void ensureCommandPoolForNewFrame()
struct QRhiVulkan::@355 caps
QByteArray pipelineCacheData() override
quint32 gfxQueueIdx
VkDeviceSize texbufAlign
QList< DeferredReleaseEntry > releaseQueue
void endAndEnqueueSecondaryCommandBuffer(VkCommandBuffer cb, QVkCommandBuffer *cbD)
void beginPass(QRhiCommandBuffer *cb, QRhiRenderTarget *rt, const QColor &colorClearValue, const QRhiDepthStencilClearValue &depthStencilClearValue, QRhiResourceUpdateBatch *resourceUpdates, QRhiCommandBuffer::BeginPassFlags flags) override
void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override
QRhiBuffer * createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, quint32 size) override
bool isClipDepthZeroToOne() const override
void enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdateBatch *resourceUpdates)
QVarLengthArray< VkCommandBuffer, 4 > freeSecondaryCbs[QVK_FRAMES_IN_FLIGHT]
void setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBindings *srb, int dynamicOffsetCount, const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) override
QVkAllocator allocator
bool importedAllocator
VkQueue gfxQueue
PFN_vkAcquireNextImageKHR vkAcquireNextImageKHR
QMatrix4x4 clipSpaceCorrMatrix() const override
struct QRhiVulkan::OffscreenFrame ofr
QSet< QVkSwapChain * > swapchains
int ubufAlignment() const override
QRhiDriverInfo driverInfo() const override
void beginExternal(QRhiCommandBuffer *cb) override
void setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) override
QRhi::FrameOpResult endAndSubmitPrimaryCommandBuffer(VkCommandBuffer cb, VkFence cmdFence, VkSemaphore *waitSem, VkSemaphore *signalSem)
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override
VkPhysicalDevice physDev
bool makeThreadLocalNativeContextCurrent() override
ResourceLimit
Describes the resource limit to query.
Definition qrhi.h:1846
@ MaxThreadsPerThreadGroup
Definition qrhi.h:1853
@ MaxThreadGroupZ
Definition qrhi.h:1856
@ FramesInFlight
Definition qrhi.h:1850
@ TextureSizeMin
Definition qrhi.h:1847
@ MaxThreadGroupsPerDimension
Definition qrhi.h:1852
@ MaxAsyncReadbackFrames
Definition qrhi.h:1851
@ TextureArraySizeMax
Definition qrhi.h:1857
@ MaxColorAttachments
Definition qrhi.h:1849
@ MaxThreadGroupY
Definition qrhi.h:1855
@ MaxVertexInputs
Definition qrhi.h:1859
@ MaxThreadGroupX
Definition qrhi.h:1854
@ TextureSizeMax
Definition qrhi.h:1848
@ MaxVertexOutputs
Definition qrhi.h:1860
@ MaxUniformBufferRange
Definition qrhi.h:1858
@ SkipPresent
Definition qrhi.h:1842
Feature
Flag values to indicate what features are supported by the backend currently in use.
Definition qrhi.h:1793
@ HalfAttributes
Definition qrhi.h:1831
@ CustomInstanceStepRate
Definition qrhi.h:1799
@ NonDynamicUniformBuffers
Definition qrhi.h:1801
@ ElementIndexUint
Definition qrhi.h:1805
@ RenderToNonBaseMipLevel
Definition qrhi.h:1815
@ MultisampleRenderBuffer
Definition qrhi.h:1795
@ RenderTo3DTextureSlice
Definition qrhi.h:1823
@ Tessellation
Definition qrhi.h:1825
@ IntAttributes
Definition qrhi.h:1816
@ TextureArrays
Definition qrhi.h:1824
@ PipelineCacheDataLoadSave
Definition qrhi.h:1819
@ ReadBackNonUniformBuffer
Definition qrhi.h:1812
@ MultiView
Definition qrhi.h:1834
@ TexelFetch
Definition qrhi.h:1814
@ TextureArrayRange
Definition qrhi.h:1827
@ RenderToOneDimensionalTexture
Definition qrhi.h:1832
@ BaseVertex
Definition qrhi.h:1809
@ GeometryShader
Definition qrhi.h:1826
@ Compute
Definition qrhi.h:1806
@ OneDimensionalTextureMipmaps
Definition qrhi.h:1830
@ WideLines
Definition qrhi.h:1807
@ TriangleFanTopology
Definition qrhi.h:1811
@ OneDimensionalTextures
Definition qrhi.h:1829
@ ImageDataStride
Definition qrhi.h:1820
@ BaseInstance
Definition qrhi.h:1810
@ DebugMarkers
Definition qrhi.h:1796
@ ReadBackNonBaseMipLevel
Definition qrhi.h:1813
@ MultisampleTexture
Definition qrhi.h:1794
@ ThreeDimensionalTextureMipmaps
Definition qrhi.h:1833
@ NonFourAlignedEffectiveIndexBufferOffset
Definition qrhi.h:1802
@ RedOrAlpha8IsRed
Definition qrhi.h:1804
@ NonFillPolygonMode
Definition qrhi.h:1828
@ Timestamps
Definition qrhi.h:1797
@ ThreeDimensionalTextures
Definition qrhi.h:1822
@ PrimitiveRestart
Definition qrhi.h:1800
@ ReadBackAnyTextureFormat
Definition qrhi.h:1818
@ RenderBufferImport
Definition qrhi.h:1821
@ ScreenSpaceDerivatives
Definition qrhi.h:1817
@ VertexShaderPointSize
Definition qrhi.h:1808
@ NPOTTextureRepeat
Definition qrhi.h:1803
@ Instancing
Definition qrhi.h:1798
static const int MAX_MIP_LEVELS
Definition qrhi.h:1955
FrameOpResult
Describes the result of operations that can have a soft failure.
Definition qrhi.h:1786
@ FrameOpSuccess
Definition qrhi.h:1787
@ FrameOpSwapChainOutOfDate
Definition qrhi.h:1789
@ FrameOpDeviceLost
Definition qrhi.h:1790
@ FrameOpError
Definition qrhi.h:1788
@ EnablePipelineCacheDataSave
Definition qrhi.h:1781
@ PreferSoftwareRenderer
Definition qrhi.h:1780
@ EnableTimestamps
Definition qrhi.h:1782
iterator end()
Definition qset.h:140
iterator find(const T &value)
Definition qset.h:159
\inmodule QtGui
Definition qshader.h:60
QByteArray shader() const
Definition qshader.h:65
QByteArray entryPoint() const
Definition qshader.h:68
\inmodule QtGui
Definition qshader.h:81
QShaderCode shader(const QShaderKey &key) const
Definition qshader.cpp:365
Stage stage() const
Definition qshader.cpp:321
@ SpirvShader
Definition qshader.h:93
@ ComputeStage
Definition qshader.h:89
\inmodule QtCore
Definition qsize.h:25
constexpr int height() const noexcept
Returns the height.
Definition qsize.h:132
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:129
constexpr bool isEmpty() const noexcept
Returns true if either of the width and height is less than or equal to 0; otherwise returns false.
Definition qsize.h:123
const QChar * constData() const
Returns a pointer to the data stored in the QString.
Definition qstring.h:1101
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
constexpr size_type size() const noexcept
bool isEmpty() const
const T & at(qsizetype idx) const
void resize(qsizetype sz)
const_iterator cbegin() const noexcept
const_iterator cend() const noexcept
iterator end() noexcept
void append(const T &t)
bool contains(const AT &t) const
const T * constData() const
T * data() noexcept
iterator begin() noexcept
\inmodule QtCore
bool isNull() const noexcept
Returns true if there are zero numerical segments, otherwise returns false.
int minorVersion() const noexcept
Returns the minor version number, that is, the second segment.
int majorVersion() const noexcept
Returns the major version number, that is, the first segment.
int microVersion() const noexcept
Returns the micro version number, that is, the third segment.
The QVulkanDeviceFunctions class provides cross-platform access to the device level core Vulkan 1....
\inmodule QtGui
The QVulkanInstance class represents a native Vulkan instance, enabling Vulkan rendering onto a QSurf...
QSize size() const override
Returns the size of the window excluding any window frame.
Definition qwindow.h:210
static VulkanServerBufferGlFunctions * funcs
#define this
Definition dialogs.cpp:9
double e
QSet< QString >::iterator it
EGLint EGLint * formats
Combined button and popup list for selecting options.
Definition image.cpp:4
#define Q_STATIC_ASSERT(Condition)
Definition qassert.h:105
#define QByteArrayLiteral(str)
Definition qbytearray.h:52
#define QT_WARNING_POP
#define QT_WARNING_DISABLE_GCC(text)
#define QT_WARNING_PUSH
#define QT_WARNING_DISABLE_CLANG(text)
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage void
static int instanceCount
static QString header(const QString &name)
static const qint64 headerSize
EGLOutputLayerEXT layer
bool qFuzzyIsNull(qfloat16 f) noexcept
Definition qfloat16.h:303
Flags
@ QtDebugMsg
Definition qlogging.h:30
#define qWarning
Definition qlogging.h:162
#define qCDebug(category,...)
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qBound(const T &min, const T &val, const T &max)
Definition qminmax.h:44
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
static bool contains(const QJsonArray &haystack, unsigned needle)
Definition qopengl.cpp:116
GLboolean GLboolean GLboolean b
GLbitfield stages
GLsizei const GLfloat * v
[13]
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat z
GLint GLint GLint GLint GLint x
[0]
GLuint const GLuint * buffers
GLint GLenum GLsizei GLsizei GLsizei depth
GLsizei samples
GLenum mode
const GLfloat * m
GLenum GLuint GLint level
GLfloat GLfloat GLfloat w
[0]
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLboolean r
[2]
GLuint GLuint end
GLenum GLsizei dataSize
GLuint sampler
GLenum GLenum GLsizei count
const GLenum * bufs
GLuint object
[3]
GLfloat GLfloat f
GLenum src
GLenum type
GLenum GLenum dst
GLuint GLsizei const GLchar * label
[43]
GLenum access
GLenum GLuint GLenum GLsizei const GLchar * buf
GLbitfield flags
GLenum GLuint GLsizei const GLenum * props
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLuint GLintptr offset
GLenum GLenum severity
GLint ref
GLuint name
GLint first
GLint GLint GLint GLint GLint GLint GLint GLbitfield mask
GLint GLsizei GLsizei GLenum format
GLsizei GLenum GLsizei GLsizei GLuint memory
GLint y
GLfloat GLfloat GLfloat GLfloat h
void ** params
GLuint bindingIndex
const GLubyte * c
GLint void * img
Definition qopenglext.h:233
GLuint GLsizei const GLuint const GLintptr * offsets
GLuint shader
Definition qopenglext.h:665
GLint limit
GLdouble GLdouble t
Definition qopenglext.h:243
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLsizei GLsizei GLuint * shaders
Definition qopenglext.h:677
GLuint64EXT * result
[6]
GLdouble s
[6]
Definition qopenglext.h:235
GLfloat GLfloat p
[1]
GLint GLfloat GLint stencil
GLenum GLenum colorFormat
GLsizeiptr const void GLenum usage
Definition qopenglext.h:543
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define QRHI_RES_RHI(t)
Definition qrhi_p.h:29
#define QRHI_RES(t, x)
Definition qrhi_p.h:28
Int aligned(Int v, Int byteAlign)
static bool isDepthTextureFormat(QRhiTexture::Format format)
static QRhiTexture::Format swapchainReadbackTextureFormat(DXGI_FORMAT format, QRhiTexture::Flags *flags)
static QRhiPassResourceTracker::UsageState toPassTrackerUsageState(const QGles2Buffer::UsageState &bufUsage)
static VkPolygonMode toVkPolygonMode(QRhiGraphicsPipeline::PolygonMode mode)
static VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL wrap_vkGetInstanceProcAddr(VkInstance, const char *pName)
static VkCullModeFlags toVkCullMode(QRhiGraphicsPipeline::CullMode c)
static bool accessIsWrite(VkAccessFlags access)
static VkCompareOp toVkTextureCompareOp(QRhiSampler::CompareOp op)
static VkBufferUsageFlagBits toVkBufferUsage(QRhiBuffer::UsageFlags usage)
static QRhiTexture::Format swapchainReadbackTextureFormat(VkFormat format, QRhiTexture::Flags *flags)
static VkStencilOp toVkStencilOp(QRhiGraphicsPipeline::StencilOp op)
static bool qvk_debug_filter(QVulkanInstance::DebugMessageSeverityFlags severity, QVulkanInstance::DebugMessageTypeFlags type, const void *callbackData)
static QVkBuffer::UsageState toVkBufferUsageState(QRhiPassResourceTracker::UsageState usage)
static bool attachmentDescriptionEquals(const VkAttachmentDescription &a, const VkAttachmentDescription &b)
static bool isSrgbFormat(VkFormat format)
static VkAccessFlags toVkAccess(QRhiPassResourceTracker::BufferAccess access)
static VkImageLayout toVkLayout(QRhiPassResourceTracker::TextureAccess access)
static VkFormat toVkAttributeFormat(QRhiVertexInputAttribute::Format format)
static QVkTexture::UsageState toVkTextureUsageState(QRhiPassResourceTracker::UsageState usage)
static VkPipelineStageFlags toVkPipelineStage(QRhiPassResourceTracker::BufferStage stage)
Int aligned(Int v, Int byteAlign)
\variable QRhiVulkanRenderPassNativeHandles::renderPass
static QRhiDriverInfo::DeviceType toRhiDeviceType(VkPhysicalDeviceType type)
static QRhiPassResourceTracker::UsageState toPassTrackerUsageState(const QVkBuffer::UsageState &bufUsage)
static VkColorComponentFlags toVkColorComponents(QRhiGraphicsPipeline::ColorMask c)
static VkFilter toVkFilter(QRhiSampler::Filter f)
static VkPrimitiveTopology toVkTopology(QRhiGraphicsPipeline::Topology t)
static VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL wrap_vkGetDeviceProcAddr(VkDevice device, const char *pName)
static void qrhivk_releaseTexture(const QRhiVulkan::DeferredReleaseEntry &e, VkDevice dev, QVulkanDeviceFunctions *df, void *allocator)
static void qrhivk_releaseBuffer(const QRhiVulkan::DeferredReleaseEntry &e, void *allocator)
static struct @283 qvk_sampleCounts[]
static VmaAllocator toVmaAllocator(QVkAllocator a)
static QVulkanInstance * globalVulkanInstance
static VkSamplerAddressMode toVkAddressMode(QRhiSampler::AddressMode m)
static constexpr bool isDepthTextureFormat(QRhiTexture::Format format)
static void fillVkStencilOpState(VkStencilOpState *dst, const QRhiGraphicsPipeline::StencilOpState &src)
VkSampleCountFlagBits mask
static VkShaderStageFlags toVkShaderStageFlags(QRhiShaderResourceBinding::StageFlags stage)
static constexpr VkImageAspectFlags aspectMaskForTextureFormat(QRhiTexture::Format format)
static VkDescriptorType toVkDescriptorType(const QRhiShaderResourceBinding::Data *b)
static VkBlendFactor toVkBlendFactor(QRhiGraphicsPipeline::BlendFactor f)
static VkBlendOp toVkBlendOp(QRhiGraphicsPipeline::BlendOp op)
static void qrhivk_releaseRenderBuffer(const QRhiVulkan::DeferredReleaseEntry &e, VkDevice dev, QVulkanDeviceFunctions *df)
static VkFrontFace toVkFrontFace(QRhiGraphicsPipeline::FrontFace f)
void qrhivk_accumulateComputeResource(T *writtenResources, QRhiResource *resource, QRhiShaderResourceBinding::Type bindingType, int loadTypeVal, int storeTypeVal, int loadStoreTypeVal)
static VkFormat toVkTextureFormat(QRhiTexture::Format format, QRhiTexture::Flags flags)
static VkCompareOp toVkCompareOp(QRhiGraphicsPipeline::CompareOp op)
static void fillRenderPassCreateInfo(VkRenderPassCreateInfo *rpInfo, VkSubpassDescription *subpassDesc, QVkRenderPassDescriptor *rpD)
static VkSamplerMipmapMode toVkMipmapMode(QRhiSampler::Filter f)
static bool hdrFormatMatchesVkSurfaceFormat(QRhiSwapChain::Format f, const VkSurfaceFormatKHR &s)
static void qrhivk_releaseSampler(const QRhiVulkan::DeferredReleaseEntry &e, VkDevice dev, QVulkanDeviceFunctions *df)
int count
static QVkRenderTargetData * maybeRenderTargetData(QVkCommandBuffer *cbD)
static VmaAllocation toVmaAllocation(QVkAlloc a)
static VkShaderStageFlagBits toVkShaderStage(QRhiShaderStage::Type type)
static const int QVK_UNIFORM_BUFFERS_PER_POOL
static const int QVK_COMBINED_IMAGE_SAMPLERS_PER_POOL
static const int QVK_STORAGE_BUFFERS_PER_POOL
static const int QVK_STORAGE_IMAGES_PER_POOL
void * QVkAllocator
static const int QVK_MAX_ACTIVE_TIMESTAMP_PAIRS
static const int QVK_DESC_SETS_PER_POOL
void * QVkAlloc
static const int QVK_FRAMES_IN_FLIGHT
QScopeGuard< typename std::decay< F >::type > qScopeGuard(F &&f)
[qScopeGuard]
Definition qscopeguard.h:60
SSL_CTX int(* cb)(SSL *ssl, unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg)
std::unique_ptr< ThunkPool::ThunkAllocation > allocation
Definition qstdweb.cpp:271
Q_CORE_EXPORT QByteArray qgetenv(const char *varName)
Q_CORE_EXPORT bool qEnvironmentVariableIsSet(const char *varName) noexcept
Q_CORE_EXPORT int qEnvironmentVariableIntValue(const char *varName, bool *ok=nullptr) noexcept
#define Q_UNUSED(x)
@ desc
unsigned int quint32
Definition qtypes.h:45
unsigned char uchar
Definition qtypes.h:27
int qint32
Definition qtypes.h:44
unsigned long long quint64
Definition qtypes.h:56
ptrdiff_t qsizetype
Definition qtypes.h:70
unsigned int uint
Definition qtypes.h:29
QList< QImage > images
[6]
QVBoxLayout * layout
QSharedPointer< T > other(t)
[5]
view viewport() -> scroll(dx, dy, deviceRect)
QFrame frame
[0]
QQuickView * view
[0]
bool prepare(VkRenderPassCreateInfo *rpInfo, int multiViewCount, bool multiViewCap)
bool contains(const AT &t) const noexcept
Definition qlist.h:44
\inmodule QtGui
Definition qrhi.h:850
\inmodule QtGui
Definition qrhi.h:1722
quint64 deviceId
Definition qrhi.h:1733
QByteArray deviceName
Definition qrhi.h:1732
DeviceType
Specifies the graphics device's type, when the information is available.
Definition qrhi.h:1723
@ IntegratedDevice
Definition qrhi.h:1725
DeviceType deviceType
Definition qrhi.h:1735
quint64 vendorId
Definition qrhi.h:1734
\variable QRhiGraphicsPipeline::TargetBlend::colorWrite
Definition qrhi.h:1343
\variable QRhiReadbackResult::completed
Definition qrhi.h:788
QByteArray data
Definition qrhi.h:1690
QRhiTexture::Format format
Definition qrhi.h:1688
std::function< void()> completed
Definition qrhi.h:1687
QRhiTextureCopyDescription desc
Definition qrhi_p.h:458
QVarLengthArray< MipLevelUploadList, 6 > subresDesc
Definition qrhi_p.h:456
\inmodule QtGui
Definition qrhi.h:1745
\inmodule QtGui
Definition qrhi.h:953
QRhiReadbackResult * result
QVkCommandBuffer * cbWrapper[QVK_FRAMES_IN_FLIGHT]
QRhiTexture::Format format
QRhiReadbackDescription desc
QRhiReadbackResult * result
VkPipelineStageFlags stage
VkAccessFlags access
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
VkBuffer buffers[QVK_FRAMES_IN_FLIGHT]
QVkBuffer(QRhiImplementation *rhi, Type type, UsageFlags usage, quint32 size)
UsageState usageState[QVK_FRAMES_IN_FLIGHT]
uint generation
VkBuffer stagingBuffers[QVK_FRAMES_IN_FLIGHT]
QVkAlloc allocations[QVK_FRAMES_IN_FLIGHT]
QVkAlloc stagingAllocations[QVK_FRAMES_IN_FLIGHT]
void endFullDynamicBufferUpdateForCurrentFrame() override
To be called when the entire contents of the buffer data has been updated in the memory block returne...
QRhiBuffer::NativeBuffer nativeBuffer() override
bool create() override
Creates the corresponding native graphics resources.
QVarLengthArray< DynamicUpdate, 16 > pendingDynamicUpdates[QVK_FRAMES_IN_FLIGHT]
char * beginFullDynamicBufferUpdateForCurrentFrame() override
int lastActiveFrameSlot
union QVkCommandBuffer::Command::Args args
struct QVkCommandBuffer::@247 pools
VkBuffer currentVertexBuffers[VERTEX_INPUT_RESOURCE_SLOT_COUNT]
struct QVkCommandBuffer::@245 computePassState
QVkCommandBuffer(QRhiImplementation *rhi)
QVarLengthArray< VkCommandBuffer, 4 > activeSecondaryCbStack
QRhiBackendCommandList< Command > commands
QRhiRenderTarget * currentTarget
QVarLengthArray< VkBufferImageCopy, 16 > bufferImageCopy
QRhiVulkanCommandBufferNativeHandles nativeHandlesStruct
QVarLengthArray< VkImageMemoryBarrier, 8 > imageBarrier
QVarLengthArray< VkBufferMemoryBarrier, 8 > bufferBarrier
quint32 currentVertexOffsets[VERTEX_INPUT_RESOURCE_SLOT_COUNT]
QHash< QRhiResource *, QPair< VkAccessFlags, bool > > writtenResources
QRhiComputePipeline * currentComputePipeline
const QRhiNativeHandles * nativeHandles()
QRhiShaderResourceBindings * currentComputeSrb
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
QRhiShaderResourceBindings * currentGraphicsSrb
PassType recordingPass
QVarLengthArray< VkDeviceSize, 4 > vertexBufferOffset
VkBuffer currentIndexBuffer
VkCommandBuffer cb
QVarLengthArray< VkBuffer, 4 > vertexBuffer
QRhiGraphicsPipeline * currentGraphicsPipeline
QVarLengthArray< VkClearValue, 4 > clearValue
uint currentPipelineGeneration
quint32 currentIndexOffset
QVarLengthArray< uint32_t, 4 > dynamicOffset
QVarLengthArray< QByteArray, 4 > debugMarkerData
QVarLengthArray< QRhiPassResourceTracker, 8 > passResTrackers
VkIndexType currentIndexFormat
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
QVkComputePipeline(QRhiImplementation *rhi)
bool create() override
VkPipelineLayout layout
QVkGraphicsPipeline(QRhiImplementation *rhi)
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
VkPipelineLayout layout
bool create() override
Creates the corresponding native graphics resources.
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
QVkRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize, int sampleCount, Flags flags, QRhiTexture::Format backingFormatHint)
VkFormat vkformat
VkSampleCountFlagBits samples
QRhiTexture::Format backingFormat() const override
bool create() override
Creates the corresponding native graphics resources.
VkDeviceMemory memory
QVkTexture * backingTexture
VkImageView imageView
QVarLengthArray< VkSubpassDependency, 2 > subpassDeps
const QRhiNativeHandles * nativeHandles() override
QVector< quint32 > serializedFormatData
QVarLengthArray< VkAttachmentReference, 8 > colorRefs
QRhiRenderPassDescriptor * newCompatibleRenderPassDescriptor() const override
VkAttachmentReference dsRef
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
QVarLengthArray< VkAttachmentReference, 8 > resolveRefs
QVarLengthArray< VkAttachmentDescription, 8 > attDescs
QRhiVulkanRenderPassNativeHandles nativeHandlesStruct
QVector< quint32 > serializedFormat() const override
QVkRenderPassDescriptor(QRhiImplementation *rhi)
bool isCompatible(const QRhiRenderPassDescriptor *other) const override
QVkRenderPassDescriptor * rp
QRhiRenderTargetAttachmentTracker::ResIdList currentResIdList
static const int MAX_COLOR_ATTACHMENTS
int lastActiveFrameSlot
bool create() override
VkSampler sampler
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
QVkSampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode, AddressMode u, AddressMode v, AddressMode w)
struct QVkShaderResourceBindings::BoundSampledTextureData::@239 d[QRhiShaderResourceBinding::Data::MAX_TEX_SAMPLER_ARRAY_SIZE]
VkDescriptorSetLayout layout
void updateResources(UpdateFlags flags) override
QVkShaderResourceBindings(QRhiImplementation *rhi)
QVarLengthArray< QRhiShaderResourceBinding, 8 > sortedBindings
QVarLengthArray< BoundResourceData, 8 > boundResourceData[QVK_FRAMES_IN_FLIGHT]
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
VkDescriptorSet descSets[QVK_FRAMES_IN_FLIGHT]
QSize pixelSize() const override
int sampleCount() const override
float devicePixelRatio() const override
QVkRenderTargetData d
QVkSwapChainRenderTarget(QRhiImplementation *rhi, QRhiSwapChain *swapchain)
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
VkColorSpaceKHR colorSpace
VkSurfaceKHR lastConnectedSurface
bool createOrResize() override
Creates the swapchain if not already done and resizes the swapchain buffers to match the current size...
VkSwapchainKHR sc
VkFormat colorFormat
bool isFormatSupported(Format f) override
QVkCommandBuffer cbWrapper
QRhiRenderPassDescriptor * newCompatibleRenderPassDescriptor() override
VkSampleCountFlagBits samples
struct QVkSwapChain::FrameResources frameRes[QVK_FRAMES_IN_FLIGHT]
bool supportsReadback
QVkSwapChain(QRhiImplementation *rhi)
QVkRenderBuffer * ds
quint32 currentFrameSlot
QSize surfacePixelSize() override
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
VkDeviceMemory msaaImageMem
QRhiRenderTarget * currentFrameRenderTarget() override
quint32 currentImageIndex
QVarLengthArray< ImageResources, EXPECTED_MAX_BUFFER_COUNT > imageRes
VkSurfaceKHR surface
QVarLengthArray< VkPresentModeKHR, 8 > supportedPresentationModes
QWindow * window
bool ensureSurface()
QVkSwapChainRenderTarget rtWrapper
QRhiCommandBuffer * currentFrameCommandBuffer() override
QVkTextureRenderTarget(QRhiImplementation *rhi, const QRhiTextureRenderTargetDescription &desc, Flags flags)
float devicePixelRatio() const override
VkImageView rtv[QVkRenderTargetData::MAX_COLOR_ATTACHMENTS]
bool create() override
Creates the corresponding native graphics resources.
int sampleCount() const override
QSize pixelSize() const override
QVkRenderTargetData d
VkImageView resrtv[QVkRenderTargetData::MAX_COLOR_ATTACHMENTS]
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
QRhiRenderPassDescriptor * newCompatibleRenderPassDescriptor() override
VkPipelineStageFlags stage
bool create() override
Creates the corresponding native graphics resources.
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
VkBuffer stagingBuffers[QVK_FRAMES_IN_FLIGHT]
VkFormat vkformat
bool finishCreate()
VkSampleCountFlagBits samples
VkImageView imageViewForLevel(int level)
int lastActiveFrameSlot
QVkAlloc stagingAllocations[QVK_FRAMES_IN_FLIGHT]
bool createFrom(NativeTexture src) override
Similar to create(), except that no new native textures are created.
QVkAlloc imageAlloc
VkImage image
void setNativeLayout(int layout) override
With some graphics APIs, such as Vulkan, integrating custom rendering code that uses the graphics API...
QVkTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth, int arraySize, int sampleCount, Flags flags)
VkImageView perLevelImageViews[QRhi::MAX_MIP_LEVELS]
NativeTexture nativeTexture() override
UsageState usageState
bool prepareCreate(QSize *adjustedSize=nullptr)
uint mipLevelCount
VkImageView imageView
Definition moc.h:24
struct QVkCommandBuffer::Command::Args::@272 copyImageToBuffer
struct QVkCommandBuffer::Command::Args::@289 bindIndexBuffer
struct QVkCommandBuffer::Command::Args::@307 debugMarkerInsert
struct QVkCommandBuffer::Command::Args::@301 drawIndexed
struct QVkCommandBuffer::Command::Args::@277 bufferBarrier
struct QVkCommandBuffer::Command::Args::@280 beginRenderPass
struct QVkCommandBuffer::Command::Args::@308 transitionResources
struct QVkCommandBuffer::Command::Args::@284 bindPipeline
struct QVkCommandBuffer::Command::Args::@291 setViewport
struct QVkCommandBuffer::Command::Args::@309 dispatch
VkPipelineStageFlags srcStageMask
struct QVkCommandBuffer::Command::Args::@286 bindDescriptorSet
struct QVkCommandBuffer::Command::Args::@269 copyBufferToImage
VkPipelineStageFlags dstStageMask
struct QVkCommandBuffer::Command::Args::@299 draw
struct QVkCommandBuffer::Command::Args::@278 blitImage
struct QVkCommandBuffer::Command::Args::@295 setBlendConstants
struct QVkCommandBuffer::Command::Args::@274 imageBarrier
struct QVkCommandBuffer::Command::Args::@293 setScissor
struct QVkCommandBuffer::Command::Args::@297 setStencilRef
VkPipelineBindPoint bindPoint
struct QVkCommandBuffer::Command::Args::@287 bindVertexBuffer
struct QVkCommandBuffer::Command::Args::@310 executeSecondary
struct QVkCommandBuffer::Command::Args::@267 copyBuffer
struct QVkCommandBuffer::Command::Args::@303 debugMarkerBegin
struct QVkCommandBuffer::Command::Args::@271 copyImage