Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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
5#include <QCoreApplication>
6#include <QList>
7#include <QLoggingCategory>
8#include <QVarLengthArray>
9
11
12Q_LOGGING_CATEGORY(lcPlatVk, "qt.vulkan")
13
14
31
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("QT_VULKAN_LIB"))
54 loadList.append({ QString::fromUtf8(qgetenv("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({ 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({ defaultLibraryName, -1 });
65
66 bool ok = false;
67 for (const auto &lib : loadList) {
68 m_vulkanLib.reset(new QLibrary);
69 if (lib.second >= 0)
70 m_vulkanLib->setFileNameAndVersion(lib.first, 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("Failed to load %s: %s", qPrintable(m_vulkanLib->fileName()), qPrintable(m_vulkanLib->errorString()));
81 return;
82 }
83
84 init(m_vulkanLib.get());
85}
86
88{
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("vkGetInstanceProcAddr"));
103 qWarning("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("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("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("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(layerCount);
150 for (const VkLayerProperties &p : std::as_const(layerProps)) {
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(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(extCount);
169 for (const VkExtensionProperties &p : std::as_const(extProps)) {
171 ext.name = p.extensionName;
172 ext.version = p.specVersion;
173 m_supportedExtensions.append(ext);
174 }
175 }
176 qDebug(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
190{
191 return m_supportedApiVersion;
192}
193
195{
197 qWarning("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;
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("VK_KHR_surface");
220 if (!flags.testFlag(QVulkanInstance::NoPortabilityDrivers))
221 m_enabledExtensions.append("VK_KHR_portability_enumeration");
222 if (!flags.testFlag(QVulkanInstance::NoDebugOutputRedirect))
223 m_enabledExtensions.append("VK_EXT_debug_utils");
224
225 for (const QByteArray &ext : extraExts)
226 m_enabledExtensions.append(ext);
227
228 QByteArray envExts = qgetenv("QT_VULKAN_INSTANCE_EXTENSIONS");
229 if (!envExts.isEmpty()) {
230 QByteArrayList envExtList = envExts.split(';');
231 for (auto ext : m_enabledExtensions)
232 envExtList.removeAll(ext);
233 m_enabledExtensions.append(envExtList);
234 }
235
236 QByteArray envLayers = qgetenv("QT_VULKAN_INSTANCE_LAYERS");
237 if (!envLayers.isEmpty()) {
238 QByteArrayList envLayerList = envLayers.split(';');
239 for (auto ext : m_enabledLayers)
240 envLayerList.removeAll(ext);
241 m_enabledLayers.append(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(layerName))
249 m_enabledLayers.removeAt(i--);
250 }
251 qDebug(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(extName))
255 m_enabledExtensions.removeAt(i--);
256 }
257 qDebug(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(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(m_enabledLayers))
272 layerNameVec.append(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(m_enabledExtensions))
280 extNameVec.append(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("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("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"));
305 qWarning("Failed to find vkGetPhysicalDeviceSurfaceSupportKHR");
306
307 m_destroySurface = reinterpret_cast<PFN_vkDestroySurfaceKHR>(
308 m_vkGetInstanceProcAddr(m_vkInst, "vkDestroySurfaceKHR"));
309 if (!m_destroySurface)
310 qWarning("Failed to find vkDestroySurfaceKHR");
311
312 if (!flags.testFlag(QVulkanInstance::NoDebugOutputRedirect))
313 setupDebugOutput();
314}
315
317{
318 return m_vkInst != VK_NULL_HANDLE;
319}
320
322{
323 return m_errorCode;
324}
325
327{
328 return m_vkInst;
329}
330
332{
333 return m_enabledLayers;
334}
335
337{
338 return m_enabledExtensions;
339}
340
342{
343 if (!name)
344 return nullptr;
345
346 const bool needsNullInstance = !strcmp(name, "vkEnumerateInstanceLayerProperties")
347 || !strcmp(name, "vkEnumerateInstanceExtensionProperties");
348
349 return m_vkGetInstanceProcAddr(needsNullInstance ? 0 : m_vkInst, name);
350}
351
352bool QBasicPlatformVulkanInstance::supportsPresent(VkPhysicalDevice physicalDevice,
353 uint32_t queueFamilyIndex,
355{
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("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("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("Failed to create debug report callback: %d", err);
457#endif
458}
459
A generic platform Vulkan instance implementation.
QVulkanInfoVector< QVulkanExtension > supportedExtensions() const override
void setDebugUtilsFilters(const QList< QVulkanInstance::DebugUtilsFilter > &filters) override
void setDebugFilters(const QList< QVulkanInstance::DebugFilter > &filters) override
PFN_vkVoidFunction getInstanceProcAddr(const char *name) override
QVersionNumber supportedApiVersion() const override
bool supportsPresent(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, QWindow *window) override
PFN_vkGetInstanceProcAddr m_vkGetInstanceProcAddr
QVulkanInfoVector< QVulkanLayer > supportedLayers() const override
PFN_vkGetPhysicalDeviceSurfaceSupportKHR m_getPhysDevSurfaceSupport
void destroySurface(VkSurfaceKHR surface) const
QByteArrayList enabledLayers() const override
void loadVulkanLibrary(const QString &defaultLibraryName, int defaultLibraryVersion=-1)
void initInstance(QVulkanInstance *instance, const QByteArrayList &extraExts)
QByteArrayList enabledExtensions() const override
\inmodule QtCore
\inmodule QtCore
Definition qbytearray.h:57
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:124
QString applicationName
the name of this application
\inmodule QtCore \reentrant
Definition qlibrary.h:17
QString fileName
the file name of the library
Definition qlibrary.h:19
QFunctionPointer resolve(const char *symbol)
Returns the address of the exported symbol symbol.
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
QByteArray toUtf8() const &
Definition qstring.h:634
\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.
\inmodule QtGui
The QVulkanInstance class represents a native Vulkan instance, enabling Vulkan rendering onto a QSurf...
\inmodule QtGui
\inmodule QtGui
Definition qwindow.h:63
Combined button and popup list for selecting options.
QString self
Definition language.cpp:58
EGLOutputLayerEXT layer
#define qDebug
[1]
Definition qlogging.h:164
#define qWarning
Definition qlogging.h:166
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
GLenum type
GLbitfield flags
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
GLenum GLenum severity
GLuint name
GLfloat GLfloat p
[1]
#define qPrintable(string)
Definition qstring.h:1531
Q_CORE_EXPORT QByteArray qgetenv(const char *varName)
Q_CORE_EXPORT bool qEnvironmentVariableIsSet(const char *varName) noexcept
QByteArray ba
[0]
const QStringList filters({"Image files (*.png *.xpm *.jpg)", "Text files (*.txt)", "Any files (*)" })
[6]
aWidget window() -> setWindowTitle("New Window Title")
[2]