Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qbasicvulkanplatforminstance.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 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// Qt-Security score:significant reason:default
4
6#include <QCoreApplication>
7#include <QList>
8#include <QLoggingCategory>
9#include <QVarLengthArray>
10
12
13Q_STATIC_LOGGING_CATEGORY(lcPlatVk, "qt.vulkan")
14
15/*!
16 \class QBasicPlatformVulkanInstance
17 \brief A generic platform Vulkan instance implementation.
18 \since 5.10
19 \internal
20 \ingroup qpa
21
22 Implements QPlatformVulkanInstance, serving as a base for platform-specific
23 implementations. The library loading and any WSI-specifics are excluded.
24
25 Subclasses are expected to call init() from their constructor and
26 initInstance() from their createOrAdoptInstance() implementation.
27 */
28
29QBasicPlatformVulkanInstance::QBasicPlatformVulkanInstance()
30{
31}
32
33QBasicPlatformVulkanInstance::~QBasicPlatformVulkanInstance()
34{
35 if (!m_vkInst)
36 return;
37
38#ifdef VK_EXT_debug_utils
39 if (m_debugMessenger)
40 m_vkDestroyDebugUtilsMessengerEXT(m_vkInst, m_debugMessenger, nullptr);
41#endif
42
43 if (m_ownsVkInst)
44 m_vkDestroyInstance(m_vkInst, nullptr);
45}
46
47void QBasicPlatformVulkanInstance::loadVulkanLibrary(const QString &defaultLibraryName, int defaultLibraryVersion)
48{
49 QVarLengthArray<std::pair<QString, int>, 3> loadList;
50
51 // First in the list of libraries to try is the manual override, relevant on
52 // embedded systems without a Vulkan loader and possibly with custom vendor
53 // library names.
54 if (qEnvironmentVariableIsSet("QT_VULKAN_LIB"))
55 loadList.append({ qEnvironmentVariable("QT_VULKAN_LIB"), -1 });
56
57 // Then what the platform specified. On Linux the version is likely 1, thus
58 // preferring libvulkan.so.1 over libvulkan.so.
59 loadList.append({ defaultLibraryName, defaultLibraryVersion });
60
61 // If there was a version given, we must still try without it if the first
62 // attempt fails, so that libvulkan.so is picked up if the .so.1 is not
63 // present on the system (so loaderless embedded systems still work).
64 if (defaultLibraryVersion >= 0)
65 loadList.append({ defaultLibraryName, -1 });
66
67 bool ok = false;
68 for (const auto &lib : loadList) {
69 m_vulkanLib.reset(new QLibrary);
70 if (lib.second >= 0)
71 m_vulkanLib->setFileNameAndVersion(lib.first, lib.second);
72 else
73 m_vulkanLib->setFileName(lib.first);
74 if (m_vulkanLib->load()) {
75 ok = true;
76 break;
77 }
78 }
79
80 if (!ok) {
81 qWarning("Failed to load %s: %s", qPrintable(m_vulkanLib->fileName()), qPrintable(m_vulkanLib->errorString()));
82 return;
83 }
84
85 init(m_vulkanLib.get());
86}
87
88void QBasicPlatformVulkanInstance::init(QLibrary *lib)
89{
90 if (m_vkGetInstanceProcAddr)
91 return;
92
93 qCDebug(lcPlatVk, "Vulkan init (%s)", qPrintable(lib->fileName()));
94
95 // While not strictly required with every implementation, try to follow the spec
96 // and do not rely on core functions being exported.
97 //
98 // 1. dlsym vkGetInstanceProcAddr
99 // 2. with a special null instance resolve vkCreateInstance and vkEnumerateInstance*
100 // 3. all other core functions are resolved with the created instance
101
102 m_vkGetInstanceProcAddr = reinterpret_cast<PFN_vkGetInstanceProcAddr>(lib->resolve("vkGetInstanceProcAddr"));
103 if (!m_vkGetInstanceProcAddr) {
104 qWarning("Failed to find vkGetInstanceProcAddr");
105 return;
106 }
107
108 m_vkCreateInstance = reinterpret_cast<PFN_vkCreateInstance>(m_vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkCreateInstance"));
109 if (!m_vkCreateInstance) {
110 qWarning("Failed to find vkCreateInstance");
111 return;
112 }
113 m_vkEnumerateInstanceLayerProperties = reinterpret_cast<PFN_vkEnumerateInstanceLayerProperties>(
114 m_vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkEnumerateInstanceLayerProperties"));
115 if (!m_vkEnumerateInstanceLayerProperties) {
116 qWarning("Failed to find vkEnumerateInstanceLayerProperties");
117 return;
118 }
119 m_vkEnumerateInstanceExtensionProperties = reinterpret_cast<PFN_vkEnumerateInstanceExtensionProperties>(
120 m_vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkEnumerateInstanceExtensionProperties"));
121 if (!m_vkEnumerateInstanceExtensionProperties) {
122 qWarning("Failed to find vkEnumerateInstanceExtensionProperties");
123 return;
124 }
125
126 // Do not rely on non-1.0 header typedefs here.
127 typedef VkResult (VKAPI_PTR *T_enumerateInstanceVersion)(uint32_t* pApiVersion);
128 // Determine instance-level version as described in the Vulkan 1.2 spec.
129 T_enumerateInstanceVersion enumerateInstanceVersion = reinterpret_cast<T_enumerateInstanceVersion>(
130 m_vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkEnumerateInstanceVersion"));
131 if (enumerateInstanceVersion) {
132 uint32_t ver = 0;
133 if (enumerateInstanceVersion(&ver) == VK_SUCCESS) {
134 m_supportedApiVersion = QVersionNumber(VK_VERSION_MAJOR(ver),
135 VK_VERSION_MINOR(ver),
136 VK_VERSION_PATCH(ver));
137 } else {
138 m_supportedApiVersion = QVersionNumber(1, 0, 0);
139 }
140 } else {
141 // Vulkan 1.0
142 m_supportedApiVersion = QVersionNumber(1, 0, 0);
143 }
144
145 uint32_t layerCount = 0;
146 m_vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
147 if (layerCount) {
148 QList<VkLayerProperties> layerProps(layerCount);
149 m_vkEnumerateInstanceLayerProperties(&layerCount, layerProps.data());
150 m_supportedLayers.reserve(layerCount);
151 for (const VkLayerProperties &p : std::as_const(layerProps)) {
152 QVulkanLayer layer;
153 layer.name = p.layerName;
154 layer.version = p.implementationVersion;
155 layer.specVersion = QVersionNumber(VK_VERSION_MAJOR(p.specVersion),
156 VK_VERSION_MINOR(p.specVersion),
157 VK_VERSION_PATCH(p.specVersion));
158 layer.description = p.description;
159 m_supportedLayers.append(layer);
160 }
161 }
162 qCDebug(lcPlatVk) << "Supported Vulkan instance layers:" << m_supportedLayers;
163
164 uint32_t extCount = 0;
165 m_vkEnumerateInstanceExtensionProperties(nullptr, &extCount, nullptr);
166 if (extCount) {
167 QList<VkExtensionProperties> extProps(extCount);
168 m_vkEnumerateInstanceExtensionProperties(nullptr, &extCount, extProps.data());
169 m_supportedExtensions.reserve(extCount);
170 for (const VkExtensionProperties &p : std::as_const(extProps)) {
171 QVulkanExtension ext;
172 ext.name = p.extensionName;
173 ext.version = p.specVersion;
174 m_supportedExtensions.append(ext);
175 }
176 }
177 qCDebug(lcPlatVk) << "Supported Vulkan instance extensions:" << m_supportedExtensions;
178}
179
180QVulkanInfoVector<QVulkanLayer> QBasicPlatformVulkanInstance::supportedLayers() const
181{
182 return m_supportedLayers;
183}
184
185QVulkanInfoVector<QVulkanExtension> QBasicPlatformVulkanInstance::supportedExtensions() const
186{
187 return m_supportedExtensions;
188}
189
190QVersionNumber QBasicPlatformVulkanInstance::supportedApiVersion() const
191{
192 return m_supportedApiVersion;
193}
194
195void QBasicPlatformVulkanInstance::initInstance(QVulkanInstance *instance, const QByteArrayList &extraExts)
196{
197 if (!m_vkGetInstanceProcAddr) {
198 qWarning("initInstance: No Vulkan library available");
199 return;
200 }
201
202 m_vkInst = instance->vkInstance(); // when non-null we are adopting an existing instance
203
204 QVulkanInstance::Flags flags = instance->flags();
205 m_enabledLayers = instance->layers();
206 m_enabledExtensions = instance->extensions();
207
208 if (!m_vkInst) {
209 VkApplicationInfo appInfo = {};
210 appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
211 QByteArray appName = QCoreApplication::applicationName().toUtf8();
212 appInfo.pApplicationName = appName.constData();
213 const QVersionNumber apiVersion = instance->apiVersion();
214 if (!apiVersion.isNull()) {
215 appInfo.apiVersion = VK_MAKE_VERSION(apiVersion.majorVersion(),
216 apiVersion.minorVersion(),
217 apiVersion.microVersion());
218 }
219
220 m_enabledExtensions.append("VK_KHR_surface");
221 if (!flags.testFlag(QVulkanInstance::NoPortabilityDrivers))
222 m_enabledExtensions.append("VK_KHR_portability_enumeration");
223 if (!flags.testFlag(QVulkanInstance::NoDebugOutputRedirect))
224 m_enabledExtensions.append("VK_EXT_debug_utils");
225
226 for (const QByteArray &ext : extraExts)
227 m_enabledExtensions.append(ext);
228
229 QByteArray envExts = qgetenv("QT_VULKAN_INSTANCE_EXTENSIONS");
230 if (!envExts.isEmpty()) {
231 QByteArrayList envExtList = envExts.split(';');
232 for (auto ext : m_enabledExtensions)
233 envExtList.removeAll(ext);
234 m_enabledExtensions.append(envExtList);
235 }
236
237 QByteArray envLayers = qgetenv("QT_VULKAN_INSTANCE_LAYERS");
238 if (!envLayers.isEmpty()) {
239 QByteArrayList envLayerList = envLayers.split(';');
240 for (auto ext : m_enabledLayers)
241 envLayerList.removeAll(ext);
242 m_enabledLayers.append(envLayerList);
243 }
244
245 // No clever stuff with QSet and friends: the order for layers matters
246 // and the user-provided order must be kept.
247 for (int i = 0; i < m_enabledLayers.size(); ++i) {
248 const QByteArray &layerName(m_enabledLayers[i]);
249 if (!m_supportedLayers.contains(layerName))
250 m_enabledLayers.removeAt(i--);
251 }
252 qCDebug(lcPlatVk) << "Enabling Vulkan instance layers:" << m_enabledLayers;
253 for (int i = 0; i < m_enabledExtensions.size(); ++i) {
254 const QByteArray &extName(m_enabledExtensions[i]);
255 if (!m_supportedExtensions.contains(extName))
256 m_enabledExtensions.removeAt(i--);
257 }
258 qCDebug(lcPlatVk) << "Enabling Vulkan instance extensions:" << m_enabledExtensions;
259
260 VkInstanceCreateInfo instInfo = {};
261 instInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
262 instInfo.pApplicationInfo = &appInfo;
263 if (!flags.testFlag(QVulkanInstance::NoPortabilityDrivers)) {
264 // With old Vulkan SDKs setting a non-zero flags gives a validation error.
265 // Whereas from 1.3.216 on the portability bit is required for MoltenVK to function.
266 // Hence the version check.
267 if (m_supportedApiVersion >= QVersionNumber(1, 3, 216))
268 instInfo.flags |= 0x00000001; // VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR
269 }
270
271 QList<const char *> layerNameVec;
272 for (const QByteArray &ba : std::as_const(m_enabledLayers))
273 layerNameVec.append(ba.constData());
274 if (!layerNameVec.isEmpty()) {
275 instInfo.enabledLayerCount = layerNameVec.size();
276 instInfo.ppEnabledLayerNames = layerNameVec.constData();
277 }
278
279 QList<const char *> extNameVec;
280 for (const QByteArray &ba : std::as_const(m_enabledExtensions))
281 extNameVec.append(ba.constData());
282 if (!extNameVec.isEmpty()) {
283 instInfo.enabledExtensionCount = extNameVec.size();
284 instInfo.ppEnabledExtensionNames = extNameVec.constData();
285 }
286
287 m_errorCode = m_vkCreateInstance(&instInfo, nullptr, &m_vkInst);
288 if (m_errorCode != VK_SUCCESS || !m_vkInst) {
289 qWarning("Failed to create Vulkan instance: %d", m_errorCode);
290 return;
291 }
292
293 m_vkDestroyInstance = reinterpret_cast<PFN_vkDestroyInstance>(m_vkGetInstanceProcAddr(m_vkInst, "vkDestroyInstance"));
294 if (!m_vkDestroyInstance) {
295 qWarning("Failed to find vkDestroyInstance");
296 m_vkInst = VK_NULL_HANDLE;
297 return;
298 }
299
300 m_ownsVkInst = true;
301 }
302
303 m_getPhysDevSurfaceSupport = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceSupportKHR>(
304 m_vkGetInstanceProcAddr(m_vkInst, "vkGetPhysicalDeviceSurfaceSupportKHR"));
305 if (!m_getPhysDevSurfaceSupport)
306 qWarning("Failed to find vkGetPhysicalDeviceSurfaceSupportKHR");
307
308 m_destroySurface = reinterpret_cast<PFN_vkDestroySurfaceKHR>(
309 m_vkGetInstanceProcAddr(m_vkInst, "vkDestroySurfaceKHR"));
310 if (!m_destroySurface)
311 qWarning("Failed to find vkDestroySurfaceKHR");
312
313 if (!flags.testFlag(QVulkanInstance::NoDebugOutputRedirect))
314 setupDebugOutput();
315}
316
317bool QBasicPlatformVulkanInstance::isValid() const
318{
319 return m_vkInst != VK_NULL_HANDLE;
320}
321
322VkResult QBasicPlatformVulkanInstance::errorCode() const
323{
324 return m_errorCode;
325}
326
327VkInstance QBasicPlatformVulkanInstance::vkInstance() const
328{
329 return m_vkInst;
330}
331
332QByteArrayList QBasicPlatformVulkanInstance::enabledLayers() const
333{
334 return m_enabledLayers;
335}
336
337QByteArrayList QBasicPlatformVulkanInstance::enabledExtensions() const
338{
339 return m_enabledExtensions;
340}
341
342PFN_vkVoidFunction QBasicPlatformVulkanInstance::getInstanceProcAddr(const char *name)
343{
344 if (!name)
345 return nullptr;
346
347 const bool needsNullInstance = !strcmp(name, "vkEnumerateInstanceLayerProperties")
348 || !strcmp(name, "vkEnumerateInstanceExtensionProperties");
349
350 return m_vkGetInstanceProcAddr(needsNullInstance ? 0 : m_vkInst, name);
351}
352
353bool QBasicPlatformVulkanInstance::supportsPresent(VkPhysicalDevice physicalDevice,
354 uint32_t queueFamilyIndex,
355 QWindow *window)
356{
357 if (!m_getPhysDevSurfaceSupport)
358 return true;
359
360 VkSurfaceKHR surface = QVulkanInstance::surfaceForWindow(window);
361 VkBool32 supported = false;
362 m_getPhysDevSurfaceSupport(physicalDevice, queueFamilyIndex, surface, &supported);
363
364 return supported;
365}
366
367void QBasicPlatformVulkanInstance::setDebugFilters(const QList<QVulkanInstance::DebugFilter> &filters)
368{
369 m_debugFilters = filters;
370}
371
372void QBasicPlatformVulkanInstance::setDebugUtilsFilters(const QList<QVulkanInstance::DebugUtilsFilter> &filters)
373{
374 m_debugUtilsFilters = filters;
375}
376
377void QBasicPlatformVulkanInstance::destroySurface(VkSurfaceKHR surface) const
378{
379 if (m_destroySurface && surface)
380 m_destroySurface(m_vkInst, surface, nullptr);
381}
382
383#ifdef VK_EXT_debug_utils
384static VKAPI_ATTR VkBool32 VKAPI_CALL defaultDebugCallbackFunc(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
385 VkDebugUtilsMessageTypeFlagsEXT messageType,
386 const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData,
387 void *pUserData)
388{
389 QBasicPlatformVulkanInstance *self = static_cast<QBasicPlatformVulkanInstance *>(pUserData);
390
391 // legacy filters
392 for (QVulkanInstance::DebugFilter filter : *self->debugFilters()) {
393 // As per docs in qvulkaninstance.cpp we pass object, messageCode,
394 // pMessage to the callback with the legacy signature.
395 uint64_t object = 0;
396 if (pCallbackData->objectCount > 0)
397 object = pCallbackData->pObjects[0].objectHandle;
398 if (filter(0, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, object, 0,
399 pCallbackData->messageIdNumber, "", pCallbackData->pMessage))
400 {
401 return VK_FALSE;
402 }
403 }
404
405 // filters with new signature
406 for (QVulkanInstance::DebugUtilsFilter filter : *self->debugUtilsFilters()) {
407 QVulkanInstance::DebugMessageSeverityFlags severity;
408 if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT)
409 severity |= QVulkanInstance::VerboseSeverity;
410 if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT)
411 severity |= QVulkanInstance::InfoSeverity;
412 if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT)
413 severity |= QVulkanInstance::WarningSeverity;
414 if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT)
415 severity |= QVulkanInstance::ErrorSeverity;
416 QVulkanInstance::DebugMessageTypeFlags type;
417 if (messageType & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT)
418 type |= QVulkanInstance::GeneralMessage;
419 if (messageType & VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT)
420 type |= QVulkanInstance::ValidationMessage;
421 if (messageType & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT)
422 type |= QVulkanInstance::PerformanceMessage;
423 if (filter(severity, type, pCallbackData))
424 return VK_FALSE;
425 }
426
427 // not categorized, just route to plain old qDebug
428 qDebug("vkDebug: %s", pCallbackData->pMessage);
429
430 return VK_FALSE;
431}
432#endif
433
434void QBasicPlatformVulkanInstance::setupDebugOutput()
435{
436#ifdef VK_EXT_debug_utils
437 if (!m_enabledExtensions.contains("VK_EXT_debug_utils"))
438 return;
439
440 PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT = reinterpret_cast<PFN_vkCreateDebugUtilsMessengerEXT>(
441 m_vkGetInstanceProcAddr(m_vkInst, "vkCreateDebugUtilsMessengerEXT"));
442
443 m_vkDestroyDebugUtilsMessengerEXT = reinterpret_cast<PFN_vkDestroyDebugUtilsMessengerEXT>(
444 m_vkGetInstanceProcAddr(m_vkInst, "vkDestroyDebugUtilsMessengerEXT"));
445
446 VkDebugUtilsMessengerCreateInfoEXT messengerInfo = {};
447 messengerInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
448 messengerInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT
449 | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
450 messengerInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT
451 | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT
452 | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
453 messengerInfo.pfnUserCallback = defaultDebugCallbackFunc;
454 messengerInfo.pUserData = this;
455 VkResult err = vkCreateDebugUtilsMessengerEXT(m_vkInst, &messengerInfo, nullptr, &m_debugMessenger);
456 if (err != VK_SUCCESS)
457 qWarning("Failed to create debug report callback: %d", err);
458#endif
459}
460
461QT_END_NAMESPACE
Combined button and popup list for selecting options.