Qt 6.x
The Qt SDK
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
29{
30}
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{
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
180{
181 return m_supportedLayers;
182}
183
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 instInfo.flags |= 0x00000001; // VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR
264
265 QList<const char *> layerNameVec;
266 for (const QByteArray &ba : std::as_const(m_enabledLayers))
267 layerNameVec.append(ba.constData());
268 if (!layerNameVec.isEmpty()) {
269 instInfo.enabledLayerCount = layerNameVec.size();
270 instInfo.ppEnabledLayerNames = layerNameVec.constData();
271 }
272
273 QList<const char *> extNameVec;
274 for (const QByteArray &ba : std::as_const(m_enabledExtensions))
275 extNameVec.append(ba.constData());
276 if (!extNameVec.isEmpty()) {
277 instInfo.enabledExtensionCount = extNameVec.size();
278 instInfo.ppEnabledExtensionNames = extNameVec.constData();
279 }
280
281 m_errorCode = m_vkCreateInstance(&instInfo, nullptr, &m_vkInst);
282 if (m_errorCode != VK_SUCCESS || !m_vkInst) {
283 qWarning("Failed to create Vulkan instance: %d", m_errorCode);
284 return;
285 }
286
287 m_vkDestroyInstance = reinterpret_cast<PFN_vkDestroyInstance>(m_vkGetInstanceProcAddr(m_vkInst, "vkDestroyInstance"));
288 if (!m_vkDestroyInstance) {
289 qWarning("Failed to find vkDestroyInstance");
290 m_vkInst = VK_NULL_HANDLE;
291 return;
292 }
293
294 m_ownsVkInst = true;
295 }
296
297 m_getPhysDevSurfaceSupport = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceSupportKHR>(
298 m_vkGetInstanceProcAddr(m_vkInst, "vkGetPhysicalDeviceSurfaceSupportKHR"));
300 qWarning("Failed to find vkGetPhysicalDeviceSurfaceSupportKHR");
301
302 m_destroySurface = reinterpret_cast<PFN_vkDestroySurfaceKHR>(
303 m_vkGetInstanceProcAddr(m_vkInst, "vkDestroySurfaceKHR"));
304 if (!m_destroySurface)
305 qWarning("Failed to find vkDestroySurfaceKHR");
306
307 if (!flags.testFlag(QVulkanInstance::NoDebugOutputRedirect))
308 setupDebugOutput();
309}
310
312{
313 return m_vkInst != VK_NULL_HANDLE;
314}
315
317{
318 return m_errorCode;
319}
320
322{
323 return m_vkInst;
324}
325
327{
328 return m_enabledLayers;
329}
330
332{
333 return m_enabledExtensions;
334}
335
337{
338 if (!name)
339 return nullptr;
340
341 const bool needsNullInstance = !strcmp(name, "vkEnumerateInstanceLayerProperties")
342 || !strcmp(name, "vkEnumerateInstanceExtensionProperties");
343
344 return m_vkGetInstanceProcAddr(needsNullInstance ? 0 : m_vkInst, name);
345}
346
347bool QBasicPlatformVulkanInstance::supportsPresent(VkPhysicalDevice physicalDevice,
348 uint32_t queueFamilyIndex,
350{
352 return true;
353
354 VkSurfaceKHR surface = QVulkanInstance::surfaceForWindow(window);
355 VkBool32 supported = false;
356 m_getPhysDevSurfaceSupport(physicalDevice, queueFamilyIndex, surface, &supported);
357
358 return supported;
359}
360
362{
363 m_debugFilters = filters;
364}
365
367{
368 m_debugUtilsFilters = filters;
369}
370
371void QBasicPlatformVulkanInstance::destroySurface(VkSurfaceKHR surface) const
372{
373 if (m_destroySurface && surface)
374 m_destroySurface(m_vkInst, surface, nullptr);
375}
376
377#ifdef VK_EXT_debug_utils
378static VKAPI_ATTR VkBool32 VKAPI_CALL defaultDebugCallbackFunc(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
379 VkDebugUtilsMessageTypeFlagsEXT messageType,
380 const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData,
381 void *pUserData)
382{
383 QBasicPlatformVulkanInstance *self = static_cast<QBasicPlatformVulkanInstance *>(pUserData);
384
385 // legacy filters
386 for (QVulkanInstance::DebugFilter filter : *self->debugFilters()) {
387 // As per docs in qvulkaninstance.cpp we pass object, messageCode,
388 // pMessage to the callback with the legacy signature.
389 uint64_t object = 0;
390 if (pCallbackData->objectCount > 0)
391 object = pCallbackData->pObjects[0].objectHandle;
392 if (filter(0, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, object, 0,
393 pCallbackData->messageIdNumber, "", pCallbackData->pMessage))
394 {
395 return VK_FALSE;
396 }
397 }
398
399 // filters with new signature
400 for (QVulkanInstance::DebugUtilsFilter filter : *self->debugUtilsFilters()) {
401 QVulkanInstance::DebugMessageSeverityFlags severity;
402 if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT)
403 severity |= QVulkanInstance::VerboseSeverity;
404 if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT)
405 severity |= QVulkanInstance::InfoSeverity;
406 if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT)
407 severity |= QVulkanInstance::WarningSeverity;
408 if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT)
409 severity |= QVulkanInstance::ErrorSeverity;
410 QVulkanInstance::DebugMessageTypeFlags type;
411 if (messageType & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT)
412 type |= QVulkanInstance::GeneralMessage;
413 if (messageType & VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT)
414 type |= QVulkanInstance::ValidationMessage;
415 if (messageType & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT)
416 type |= QVulkanInstance::PerformanceMessage;
417 if (filter(severity, type, pCallbackData))
418 return VK_FALSE;
419 }
420
421 // not categorized, just route to plain old qDebug
422 qDebug("vkDebug: %s", pCallbackData->pMessage);
423
424 return VK_FALSE;
425}
426#endif
427
428void QBasicPlatformVulkanInstance::setupDebugOutput()
429{
430#ifdef VK_EXT_debug_utils
431 if (!m_enabledExtensions.contains("VK_EXT_debug_utils"))
432 return;
433
434 PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT = reinterpret_cast<PFN_vkCreateDebugUtilsMessengerEXT>(
435 m_vkGetInstanceProcAddr(m_vkInst, "vkCreateDebugUtilsMessengerEXT"));
436
437 m_vkDestroyDebugUtilsMessengerEXT = reinterpret_cast<PFN_vkDestroyDebugUtilsMessengerEXT>(
438 m_vkGetInstanceProcAddr(m_vkInst, "vkDestroyDebugUtilsMessengerEXT"));
439
440 VkDebugUtilsMessengerCreateInfoEXT messengerInfo = {};
441 messengerInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
442 messengerInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT
443 | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
444 messengerInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT
445 | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT
446 | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
447 messengerInfo.pfnUserCallback = defaultDebugCallbackFunc;
448 messengerInfo.pUserData = this;
449 VkResult err = vkCreateDebugUtilsMessengerEXT(m_vkInst, &messengerInfo, nullptr, &m_debugMessenger);
450 if (err != VK_SUCCESS)
451 qWarning("Failed to create debug report callback: %d", err);
452#endif
453}
454
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:122
QList< QByteArray > split(char sep) const
Splits the byte array into subarrays wherever sep occurs, and returns the list of those arrays.
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:106
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.
Definition qlist.h:74
qsizetype size() const noexcept
Definition qlist.h:386
const_pointer constData() const noexcept
Definition qlist.h:416
bool isEmpty() const noexcept
Definition qlist.h:390
pointer data()
Definition qlist.h:414
void append(parameter_type t)
Definition qlist.h:441
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5857
QByteArray toUtf8() const &
Definition qstring.h:563
void append(const T &t)
\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
\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:57
EGLOutputLayerEXT layer
#define qDebug
[1]
Definition qlogging.h:160
#define qWarning
Definition qlogging.h:162
#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:1391
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]