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

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

source code of qtbase/src/gui/vulkan/qbasicvulkanplatforminstance.cpp