vulkan: Re-enable VK_KHR_portability drivers

The Vulkan loader as of SDK 1.3.216 and MoltenVK decided that
all existing applications need to stop working with
non-conformant Vulkan implementations such as MoltenVK, unless
they start specifying the right soup of instance flags, extensions,
and device extensions.

Set VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR in
VkInstanceCreateInfo::flags.

Automatically request VK_KHR_portability_enumeration, if supported, on
the instance.

This handles the instance side. On the device side we can add support
in QRhi so Qt Quick continues to work with MoltenVK. This involves
requesting VK_KHR_portability_subset on the device whenever the
extension is reported as supported (not doing so would be an error)
However, applications creating their own VkDevice will need to take
care of this themselves.

This device extension requires VK_KHR_get_physical_device_properties2
on the instance (which QRhi does not control). This is no problem with
Qt Quick as that already does this automatically, but in the unlikely
case of wrapping an existing VkInstance in QVulkanInstance it will be
up to the creator of VkInstance to enable that as well.

Pick-to: 6.4 6.3 6.2
Fixes: QTBUG-106912
Change-Id: Idaf277549b3ec982e99bfc49e4ad6a67976c141a
Reviewed-by: Andy Nichols <andy.nichols@qt.io>
This commit is contained in:
Laszlo Agocs 2022-09-23 16:42:21 +02:00
parent b66f8e0738
commit 7fbc741d10
3 changed files with 38 additions and 13 deletions

View File

@ -552,30 +552,46 @@ bool QRhiVulkan::create(QRhi::Flags flags)
QList<const char *> requestedDevExts; QList<const char *> requestedDevExts;
requestedDevExts.append("VK_KHR_swapchain"); requestedDevExts.append("VK_KHR_swapchain");
const bool hasPhysDevProp2 = inst->extensions().contains(QByteArrayLiteral("VK_KHR_get_physical_device_properties2"));
if (devExts.contains(QByteArrayLiteral("VK_KHR_portability_subset"))) {
if (hasPhysDevProp2) {
requestedDevExts.append("VK_KHR_portability_subset");
} else {
qWarning("VK_KHR_portability_subset should be enabled on the device "
"but the instance does not have VK_KHR_get_physical_device_properties2 enabled. "
"Expect problems.");
}
}
caps.vertexAttribDivisor = false; caps.vertexAttribDivisor = false;
if (devExts.contains(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME)) { if (devExts.contains(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME)) {
if (inst->extensions().contains(QByteArrayLiteral("VK_KHR_get_physical_device_properties2"))) { if (hasPhysDevProp2) {
requestedDevExts.append(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME); requestedDevExts.append(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME);
caps.vertexAttribDivisor = true; caps.vertexAttribDivisor = true;
} }
} }
for (const QByteArray &ext : requestedDeviceExtensions) { for (const QByteArray &ext : requestedDeviceExtensions) {
if (!ext.isEmpty()) { if (!ext.isEmpty() && !requestedDevExts.contains(ext)) {
if (devExts.contains(ext)) if (devExts.contains(ext)) {
requestedDevExts.append(ext.constData()); requestedDevExts.append(ext.constData());
else } else {
qWarning("Device extension %s is not supported", ext.constData()); qWarning("Device extension %s requested in QRhiVulkanInitParams is not supported",
ext.constData());
}
} }
} }
QByteArrayList envExtList = qgetenv("QT_VULKAN_DEVICE_EXTENSIONS").split(';'); QByteArrayList envExtList = qgetenv("QT_VULKAN_DEVICE_EXTENSIONS").split(';');
for (const QByteArray &ext : envExtList) { for (const QByteArray &ext : envExtList) {
if (!ext.isEmpty() && !requestedDevExts.contains(ext)) { if (!ext.isEmpty() && !requestedDevExts.contains(ext)) {
if (devExts.contains(ext)) if (devExts.contains(ext)) {
requestedDevExts.append(ext.constData()); requestedDevExts.append(ext.constData());
else } else {
qWarning("Device extension %s is not supported", ext.constData()); qWarning("Device extension %s requested in QT_VULKAN_DEVICE_EXTENSIONS is not supported",
ext.constData());
}
} }
} }

View File

@ -216,11 +216,11 @@ void QBasicPlatformVulkanInstance::initInstance(QVulkanInstance *instance, const
apiVersion.microVersion()); apiVersion.microVersion());
} }
m_enabledExtensions.append("VK_KHR_surface");
m_enabledExtensions.append("VK_KHR_portability_enumeration");
if (!flags.testFlag(QVulkanInstance::NoDebugOutputRedirect)) if (!flags.testFlag(QVulkanInstance::NoDebugOutputRedirect))
m_enabledExtensions.append("VK_EXT_debug_utils"); m_enabledExtensions.append("VK_EXT_debug_utils");
m_enabledExtensions.append("VK_KHR_surface");
for (const QByteArray &ext : extraExts) for (const QByteArray &ext : extraExts)
m_enabledExtensions.append(ext); m_enabledExtensions.append(ext);
@ -258,6 +258,7 @@ void QBasicPlatformVulkanInstance::initInstance(QVulkanInstance *instance, const
VkInstanceCreateInfo instInfo = {}; VkInstanceCreateInfo instInfo = {};
instInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; instInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
instInfo.pApplicationInfo = &appInfo; instInfo.pApplicationInfo = &appInfo;
instInfo.flags = 0x00000001; // VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR
QList<const char *> layerNameVec; QList<const char *> layerNameVec;
for (const QByteArray &ba : qAsConst(m_enabledLayers)) for (const QByteArray &ba : qAsConst(m_enabledLayers))

View File

@ -478,9 +478,13 @@ void QVulkanInstance::setLayers(const QByteArrayList &layers)
/*! /*!
Specifies the list of additional instance \a extensions to enable. It is Specifies the list of additional instance \a extensions to enable. It is
safe to specify unsupported extensions as well because these get ignored safe to specify unsupported extensions as well because these get ignored
when not supported at run time. The surface-related extensions required by when not supported at run time.
Qt will always be added automatically, no need to include them in this
list. \note The surface-related extensions required by Qt (for example, \c
VK_KHR_win32_surface) will always be added automatically, no need to
include them in this list.
\note \c VK_KHR_portability_enumeration is added automatically.
\note This function can only be called before create() and has no effect if \note This function can only be called before create() and has no effect if
called afterwards. called afterwards.
@ -538,6 +542,10 @@ void QVulkanInstance::setApiVersion(const QVersionNumber &vulkanVersion)
The Vulkan instance and library is available as long as this The Vulkan instance and library is available as long as this
QVulkanInstance exists, or until destroy() is called. QVulkanInstance exists, or until destroy() is called.
The VkInstance is always created with the flag
\l{https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkInstanceCreateFlagBits.html}{VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR}
set. This means that Vulkan Portability physical devices get enumerated as well.
*/ */
bool QVulkanInstance::create() bool QVulkanInstance::create()
{ {