rhi: vulkan: Sanitize device extension handling

Instead of

qputenv("QT_VULKAN_DEVICE_EXTENSIONS", "VK_KHR_get_memory_requirements2;VK_NV_ray_tracing");

one can now do

params.deviceExtensions = { "VK_KHR_get_memory_requirements2", "VK_NV_ray_tracing" };

on the QRhiVulkanInitParams passed to QRhi::create().

The environment variable stays important for Qt Quick applications, which provide no
configurability for the QRhi construction (yet). On the other hand, applications using
QRhi directly can now also use the new approach to specify the list of device extensions
to enable.

In addition, take QVulkanInfoVector<QVulkanExtension> into use. There is no reason not to
rely on the infrastructure provided by QVulkanInstance. This also implies showing an
informative warning for unsupported extensions, instead of merely failing the device
creation. (applications will likely not be able to recover of course, but at least the
reason for failing is made obvious this way)

Task-number: QTBUG-82435
Change-Id: Ib47fd1a10c02be5ceef2c973e61e896c34f92fa3
Reviewed-by: Eirik Aavitsland <eirik.aavitsland@qt.io>
This commit is contained in:
Laszlo Agocs 2020-02-29 15:38:07 +01:00
parent 5bbc9986f9
commit bd2b77120e
3 changed files with 44 additions and 18 deletions

View File

@ -149,6 +149,10 @@ QT_BEGIN_NAMESPACE
for other windows as well, as long as they all have their
QWindow::surfaceType() set to QSurface::VulkanSurface.
To request additional extensions to be enabled on the Vulkan device, list them
in deviceExtensions. This can be relevant when integrating with native Vulkan
rendering code.
\section2 Working with existing Vulkan devices
When interoperating with another graphics engine, it may be necessary to
@ -299,6 +303,7 @@ QRhiVulkan::QRhiVulkan(QRhiVulkanInitParams *params, QRhiVulkanNativeHandles *im
{
inst = params->inst;
maybeWindow = params->window; // may be null
requestedDeviceExtensions = params->deviceExtensions;
importedDevice = importDevice != nullptr;
if (importedDevice) {
@ -463,40 +468,59 @@ bool QRhiVulkan::create(QRhi::Flags flags)
if (inst->layers().contains("VK_LAYER_LUNARG_standard_validation"))
devLayers.append("VK_LAYER_LUNARG_standard_validation");
QVulkanInfoVector<QVulkanExtension> devExts;
uint32_t devExtCount = 0;
f->vkEnumerateDeviceExtensionProperties(physDev, nullptr, &devExtCount, nullptr);
QVector<VkExtensionProperties> devExts(devExtCount);
f->vkEnumerateDeviceExtensionProperties(physDev, nullptr, &devExtCount, devExts.data());
if (devExtCount) {
QVector<VkExtensionProperties> extProps(devExtCount);
f->vkEnumerateDeviceExtensionProperties(physDev, nullptr, &devExtCount, extProps.data());
for (const VkExtensionProperties &p : qAsConst(extProps))
devExts.append({ p.extensionName, p.specVersion });
}
qCDebug(QRHI_LOG_INFO, "%d device extensions available", devExts.count());
QVector<const char *> requestedDevExts;
requestedDevExts.append("VK_KHR_swapchain");
debugMarkersAvailable = false;
if (devExts.contains(VK_EXT_DEBUG_MARKER_EXTENSION_NAME)) {
requestedDevExts.append(VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
debugMarkersAvailable = true;
}
vertexAttribDivisorAvailable = false;
for (const VkExtensionProperties &ext : devExts) {
if (!strcmp(ext.extensionName, VK_EXT_DEBUG_MARKER_EXTENSION_NAME)) {
requestedDevExts.append(VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
debugMarkersAvailable = true;
} else if (!strcmp(ext.extensionName, VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME)) {
if (inst->extensions().contains(QByteArrayLiteral("VK_KHR_get_physical_device_properties2"))) {
requestedDevExts.append(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME);
vertexAttribDivisorAvailable = true;
}
if (devExts.contains(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME)) {
if (inst->extensions().contains(QByteArrayLiteral("VK_KHR_get_physical_device_properties2"))) {
requestedDevExts.append(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME);
vertexAttribDivisorAvailable = true;
}
}
QByteArrayList envExtList;
if (qEnvironmentVariableIsSet("QT_VULKAN_DEVICE_EXTENSIONS")) {
envExtList = qgetenv("QT_VULKAN_DEVICE_EXTENSIONS").split(';');
for (auto ext : requestedDevExts)
envExtList.removeAll(ext);
for (const QByteArray &ext : envExtList) {
if (!ext.isEmpty())
for (const QByteArray &ext : requestedDeviceExtensions) {
if (!ext.isEmpty()) {
if (devExts.contains(ext))
requestedDevExts.append(ext.constData());
else
qWarning("Device extension %s is not supported", ext.constData());
}
}
QByteArrayList envExtList = qgetenv("QT_VULKAN_DEVICE_EXTENSIONS").split(';');
for (const QByteArray &ext : envExtList) {
if (!ext.isEmpty() && !requestedDevExts.contains(ext)) {
if (devExts.contains(ext))
requestedDevExts.append(ext.constData());
else
qWarning("Device extension %s is not supported", ext.constData());
}
}
if (QRHI_LOG_INFO().isEnabled(QtDebugMsg)) {
qCDebug(QRHI_LOG_INFO, "Enabling device extensions:");
for (const char *ext : requestedDevExts)
qCDebug(QRHI_LOG_INFO, " %s", ext);
}
VkDeviceCreateInfo devInfo;
memset(&devInfo, 0, sizeof(devInfo));
devInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;

View File

@ -57,6 +57,7 @@ struct Q_GUI_EXPORT QRhiVulkanInitParams : public QRhiInitParams
{
QVulkanInstance *inst = nullptr;
QWindow *window = nullptr;
QByteArrayList deviceExtensions;
};
struct Q_GUI_EXPORT QRhiVulkanNativeHandles : public QRhiNativeHandles

View File

@ -810,6 +810,7 @@ public:
QVulkanInstance *inst = nullptr;
QWindow *maybeWindow = nullptr;
QByteArrayList requestedDeviceExtensions;
bool importedDevice = false;
VkPhysicalDevice physDev = VK_NULL_HANDLE;
VkDevice dev = VK_NULL_HANDLE;