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 | |
10 | QT_BEGIN_NAMESPACE |
11 | |
12 | Q_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 | |
28 | QBasicPlatformVulkanInstance::QBasicPlatformVulkanInstance() |
29 | { |
30 | } |
31 | |
32 | QBasicPlatformVulkanInstance::~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 | |
46 | void 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 | |
87 | void 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 | |
179 | QVulkanInfoVector<QVulkanLayer> QBasicPlatformVulkanInstance::supportedLayers() const |
180 | { |
181 | return m_supportedLayers; |
182 | } |
183 | |
184 | QVulkanInfoVector<QVulkanExtension> QBasicPlatformVulkanInstance::supportedExtensions() const |
185 | { |
186 | return m_supportedExtensions; |
187 | } |
188 | |
189 | QVersionNumber QBasicPlatformVulkanInstance::supportedApiVersion() const |
190 | { |
191 | return m_supportedApiVersion; |
192 | } |
193 | |
194 | void 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 | |
316 | bool QBasicPlatformVulkanInstance::isValid() const |
317 | { |
318 | return m_vkInst != VK_NULL_HANDLE; |
319 | } |
320 | |
321 | VkResult QBasicPlatformVulkanInstance::errorCode() const |
322 | { |
323 | return m_errorCode; |
324 | } |
325 | |
326 | VkInstance QBasicPlatformVulkanInstance::vkInstance() const |
327 | { |
328 | return m_vkInst; |
329 | } |
330 | |
331 | QByteArrayList QBasicPlatformVulkanInstance::enabledLayers() const |
332 | { |
333 | return m_enabledLayers; |
334 | } |
335 | |
336 | QByteArrayList QBasicPlatformVulkanInstance::enabledExtensions() const |
337 | { |
338 | return m_enabledExtensions; |
339 | } |
340 | |
341 | PFN_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 | |
352 | bool 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 | |
366 | void QBasicPlatformVulkanInstance::setDebugFilters(const QList<QVulkanInstance::DebugFilter> &filters) |
367 | { |
368 | m_debugFilters = filters; |
369 | } |
370 | |
371 | void QBasicPlatformVulkanInstance::setDebugUtilsFilters(const QList<QVulkanInstance::DebugUtilsFilter> &filters) |
372 | { |
373 | m_debugUtilsFilters = filters; |
374 | } |
375 | |
376 | void 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 |
383 | static 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 | |
433 | void 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 | |
460 | QT_END_NAMESPACE |
461 |
Definitions
- lcPlatVk
- QBasicPlatformVulkanInstance
- ~QBasicPlatformVulkanInstance
- loadVulkanLibrary
- init
- supportedLayers
- supportedExtensions
- supportedApiVersion
- initInstance
- isValid
- errorCode
- vkInstance
- enabledLayers
- enabledExtensions
- getInstanceProcAddr
- supportsPresent
- setDebugFilters
- setDebugUtilsFilters
- destroySurface
- defaultDebugCallbackFunc
Start learning QML with our Intro Training
Find out more