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 "qvulkaninstance_p.h" |
5 | #include <qpa/qplatformvulkaninstance.h> |
6 | #include <qpa/qplatformintegration.h> |
7 | #include <qpa/qplatformnativeinterface.h> |
8 | #include <QtGui/private/qguiapplication_p.h> |
9 | |
10 | QT_BEGIN_NAMESPACE |
11 | |
12 | /*! |
13 | \class QVulkanInstance |
14 | \since 5.10 |
15 | \ingroup painting-3D |
16 | \inmodule QtGui |
17 | |
18 | \brief The QVulkanInstance class represents a native Vulkan instance, enabling |
19 | Vulkan rendering onto a QSurface. |
20 | |
21 | \l{https://www.khronos.org/vulkan/}{Vulkan} is a cross-platform, explicit |
22 | graphics and compute API. This class provides support for loading a Vulkan |
23 | library and creating an \c instance in a cross-platform manner. For an |
24 | introduction on Vulkan instances, refer |
25 | \l{https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#initialization-instances}{to |
26 | section 3.2 of the specification}. |
27 | |
28 | \note Platform-specific support for Vulkan instances and windows with |
29 | Vulkan-capable surfaces is provided by the various platform plugins. Not |
30 | all of them will support Vulkan, however. When running on such a platform, |
31 | create() will fail and always return \c false. |
32 | |
33 | \note Vulkan support may get automatically disabled for a given Qt build due |
34 | to not having the necessary Vulkan headers available at build time. When |
35 | this is the case, and the output of \c configure indicates Vulkan support is |
36 | disabled, the QVulkan* classes will be unavailable. |
37 | |
38 | \note Some functions changed their signature between the various Vulkan |
39 | header revisions. When building Qt and only headers with the old, |
40 | conflicting signatures are present in a system, Vulkan support will get |
41 | disabled. It is recommended to use headers from Vulkan 1.0.39 or newer. |
42 | |
43 | \section1 Initialization |
44 | |
45 | Similarly to QOpenGLContext, any actual Vulkan instance creation happens |
46 | only when calling create(). This allows using QVulkanInstance as a plain |
47 | member variable while retaining control over when to perform |
48 | initialization. |
49 | |
50 | Querying the supported instance-level layers and extensions is possible by |
51 | calling supportedLayers() and supportedExtensions(). These ensure the |
52 | Vulkan library is loaded, and can therefore be called safely before |
53 | create() as well. |
54 | |
55 | Instances store per-application Vulkan state and creating a \c VkInstance |
56 | object initializes the Vulkan library. In practice there will typically be |
57 | a single instance constructed early on in main(). The object then stays |
58 | alive until exiting the application. |
59 | |
60 | Every Vulkan-based QWindow must be associated with a QVulkanInstance by |
61 | calling QWindow::setVulkanInstance(). Thus a typical application pattern is |
62 | the following: |
63 | |
64 | \snippet code/src_gui_vulkan_qvulkaninstance.cpp 0 |
65 | |
66 | \section1 Configuration |
67 | |
68 | QVulkanInstance automatically enables the minimum set of extensions it |
69 | needs on the newly created instance. In practice this means the |
70 | \c{VK_KHR_*_surface} family of extensions. |
71 | |
72 | By default Vulkan debug output, for example messages from the validation |
73 | layers, is routed to qDebug(). This can be disabled by passing the flag |
74 | \c NoDebugOutputRedirect to setFlags() \e before invoking create(). |
75 | |
76 | To enable additional layers and extensions, provide the list via |
77 | setLayers() and setExtensions() \e before invoking create(). When a |
78 | given layer or extension is not reported as available from the instance, |
79 | the request is ignored. After a successful call to create(), the values |
80 | returned from functions like layers() and extensions() reflect the actual |
81 | enabled layers and extensions. When necessary, for example to avoid |
82 | requesting extensions that conflict and thus would fail the Vulkan instance |
83 | creation, the list of actually supported layers and extensions can be |
84 | examined via supportedLayers() and supportedExtensions() before calling |
85 | create(). |
86 | |
87 | For example, to enable the standard validation layers, one could do the |
88 | following: |
89 | |
90 | \snippet code/src_gui_vulkan_qvulkaninstance.cpp 1 |
91 | |
92 | Or, alternatively, to make decisions before attempting to create a Vulkan |
93 | instance: |
94 | |
95 | \snippet code/src_gui_vulkan_qvulkaninstance.cpp 2 |
96 | |
97 | \section1 Adopting an Existing Instance |
98 | |
99 | By default QVulkanInstance creates a new Vulkan instance. When working with |
100 | external engines and renderers, this may sometimes not be desirable. When |
101 | there is a \c VkInstance handle already available, call setVkInstance() |
102 | before invoking create(). This way no additional instances will get |
103 | created, and QVulkanInstance will not own the handle. |
104 | |
105 | \note It is up to the component creating the external instance to ensure |
106 | the necessary extensions are enabled on it. These are: \c{VK_KHR_surface}, |
107 | the WSI-specific \c{VK_KHR_*_surface} that is appropriate for the platform |
108 | in question, and \c{VK_EXT_debug_utils} in case QVulkanInstance's debug |
109 | output redirection is desired. |
110 | |
111 | \section1 Accessing Core Vulkan Commands |
112 | |
113 | To access the \c VkInstance handle the QVulkanInstance wraps, call |
114 | vkInstance(). To resolve Vulkan functions, call getInstanceProcAddr(). For |
115 | core Vulkan commands manual resolving is not necessary as they are provided |
116 | via the QVulkanFunctions and QVulkanDeviceFunctions objects accessible via |
117 | functions() and deviceFunctions(). |
118 | |
119 | \note QVulkanFunctions and QVulkanDeviceFunctions are generated from the |
120 | Vulkan API XML specifications when building the Qt libraries. Therefore no |
121 | documentation is provided for them. They contain the Vulkan 1.2 functions |
122 | with the same signatures as described in the |
123 | \l{https://www.khronos.org/registry/vulkan/specs/1.2/html/}{Vulkan API |
124 | documentation}. |
125 | |
126 | \section1 Getting a Native Vulkan Surface for a Window |
127 | |
128 | The two common windowing system specific operations are getting a surface |
129 | (a \c{VkSurfaceKHR} handle) for a window, and querying if a given queue |
130 | family supports presenting to a given surface. To avoid WSI-specific bits |
131 | in the applications, these are abstracted by QVulkanInstance and the |
132 | underlying QPA layers. |
133 | |
134 | To create a Vulkan surface for a window, or retrieve an existing one, |
135 | call surfaceForWindow(). Most platforms will only create the surface via |
136 | \c{VK_KHR_*_surface} when first calling surfaceForWindow(), but there may be |
137 | platform-specific variations in the internal behavior. Once created, |
138 | subsequent calls to surfaceForWindow() just return the same handle. This |
139 | fits the structure of typical Vulkan-enabled QWindow subclasses well. |
140 | |
141 | To query if a given queue family within a physical device can be used to |
142 | perform presentation to a given surface, call supportsPresent(). This |
143 | encapsulates both the generic \c vkGetPhysicalDeviceSurfaceSupportKHR and |
144 | the WSI-specific \c{vkGetPhysicalDevice*PresentationSupportKHR} checks. |
145 | |
146 | \section1 Troubleshooting |
147 | |
148 | Besides returning \c false from create() or \c 0 from surfaceForWindow(), |
149 | critical errors will also get printed to the debug output via qWarning(). |
150 | Additional logging can be requested by enabling debug output for the |
151 | logging category \c{qt.vulkan}. The actual Vulkan error code from instance |
152 | creation can be retrieved by calling errorCode() after a failing create(). |
153 | |
154 | In some special cases it may be necessary to override the Vulkan |
155 | library name. This can be achieved by setting the \c{QT_VULKAN_LIB} |
156 | environment variable. |
157 | |
158 | \section1 Example |
159 | |
160 | The following is the basic outline of creating a Vulkan-capable QWindow: |
161 | |
162 | \snippet code/src_gui_vulkan_qvulkaninstance.cpp 3 |
163 | |
164 | \note In addition to expose, a well-behaving window implementation will |
165 | also have to take care of additional events like resize and |
166 | QPlatformSurfaceEvent in order to ensure proper management of the |
167 | swap chain. Additionally, some platforms may require releasing resources |
168 | when not being exposed anymore. |
169 | |
170 | \section1 Using C++ Bindings for Vulkan |
171 | |
172 | Combining Qt's Vulkan enablers with a C++ Vulkan wrapper, for example |
173 | \l{https://github.com/KhronosGroup/Vulkan-Hpp}{Vulkan-Hpp}, is possible as |
174 | well. The pre-requisite here is that the C++ layer must be able to adopt |
175 | native handles (VkInstance, VkSurfaceKHR) in its classes without taking |
176 | ownership (since the ownership stays with QVulkanInstance and QWindow). |
177 | Consider also the following: |
178 | |
179 | \list |
180 | |
181 | \li Some wrappers require exception support to be enabled. Qt does not use |
182 | exceptions. To enable exceptions for the application, add \c{CONFIG += exceptions} |
183 | to the \c{.pro} file. |
184 | |
185 | \li Some wrappers call Vulkan functions directly, assuming \c{vulkan.h} |
186 | provides prototypes and the application links to a Vulkan library exporting |
187 | all necessary symbols. Qt may not directly link to a Vulkan library. |
188 | Therefore, on some platforms it may be necessary to add |
189 | \c{LIBS += -lvulkan} or similar in the application's \c{.pro} file. |
190 | |
191 | \li The headers for the QVulkan classes may include \c{vulkan.h} with |
192 | \c{VK_NO_PROTOTYPES} enabled. This can cause issues in C++ wrapper headers |
193 | that rely on the prototypes. Hence in application code it may be |
194 | necessary to include \c{vulkan.hpp} or similar before any of the QVulkan |
195 | headers. |
196 | |
197 | \endlist |
198 | |
199 | \sa QVulkanFunctions, QSurface::SurfaceType |
200 | */ |
201 | |
202 | /*! |
203 | \enum QVulkanInstance::Flag |
204 | \since 5.10 |
205 | |
206 | This enum describes the flags that can be passed to setFlags(). These control |
207 | the behavior of create(). |
208 | |
209 | \value NoDebugOutputRedirect Disables Vulkan debug output (\c{VK_EXT_debug_utils}) redirection to qDebug. |
210 | \value [since 6.5] NoPortabilityDrivers Disables enumerating physical devices marked as Vulkan Portability. |
211 | */ |
212 | |
213 | bool QVulkanInstancePrivate::ensureVulkan() |
214 | { |
215 | if (!platformInst) { |
216 | platformInst.reset(other: QGuiApplicationPrivate::platformIntegration()->createPlatformVulkanInstance(instance: q_ptr)); |
217 | if (!platformInst) { |
218 | qWarning(msg: "QVulkanInstance: Failed to initialize Vulkan" ); |
219 | return false; |
220 | } |
221 | } |
222 | return true; |
223 | } |
224 | |
225 | void QVulkanInstancePrivate::reset() |
226 | { |
227 | qDeleteAll(c: deviceFuncs); |
228 | deviceFuncs.clear(); |
229 | funcs.reset(); |
230 | platformInst.reset(); |
231 | vkInst = VK_NULL_HANDLE; |
232 | errorCode = VK_SUCCESS; |
233 | } |
234 | |
235 | /*! |
236 | Constructs a new instance. |
237 | |
238 | \note No Vulkan initialization is performed in the constructor. |
239 | */ |
240 | QVulkanInstance::QVulkanInstance() |
241 | : d_ptr(new QVulkanInstancePrivate(this)) |
242 | { |
243 | } |
244 | |
245 | /*! |
246 | Destructor. |
247 | |
248 | \note \l {QVulkanInstance::}{vkInstance()} will return \nullptr once the |
249 | instance is destroyed. |
250 | */ |
251 | QVulkanInstance::~QVulkanInstance() |
252 | { |
253 | destroy(); |
254 | } |
255 | |
256 | /*! |
257 | \class QVulkanLayer |
258 | \inmodule QtGui |
259 | \brief Represents information about a Vulkan layer. |
260 | */ |
261 | |
262 | /*! |
263 | \variable QVulkanLayer::name |
264 | \brief The name of the layer. |
265 | */ |
266 | |
267 | /*! |
268 | \variable QVulkanLayer::version |
269 | \brief The version of the layer. This is an integer, increasing with each backward |
270 | compatible change. |
271 | */ |
272 | |
273 | /*! |
274 | \variable QVulkanLayer::specVersion |
275 | \brief The Vulkan version the layer was written against. |
276 | */ |
277 | |
278 | /*! |
279 | \variable QVulkanLayer::description |
280 | \brief The description of the layer. |
281 | */ |
282 | |
283 | /*! |
284 | \fn bool operator==(const QVulkanLayer &lhs, const QVulkanLayer &rhs) |
285 | \since 5.10 |
286 | \relates QVulkanLayer |
287 | |
288 | Returns \c true if Vulkan layers \a lhs and \a rhs have |
289 | the same name, version, and spec version. |
290 | */ |
291 | |
292 | /*! |
293 | \fn bool operator!=(const QVulkanLayer &lhs, const QVulkanLayer &rhs) |
294 | \since 5.10 |
295 | \relates QVulkanLayer |
296 | |
297 | Returns \c true if Vulkan layers \a lhs and \a rhs have |
298 | different name, version, or spec version. |
299 | */ |
300 | |
301 | /*! |
302 | \fn size_t qHash(const QVulkanLayer &key, size_t seed = 0) |
303 | \since 5.10 |
304 | \relates QVulkanLayer |
305 | |
306 | Returns the hash value for the \a key, using \a seed to seed the |
307 | calculation. |
308 | */ |
309 | |
310 | /*! |
311 | \class QVulkanExtension |
312 | \inmodule QtGui |
313 | \brief Represents information about a Vulkan extension. |
314 | */ |
315 | |
316 | /*! |
317 | \variable QVulkanExtension::name |
318 | \brief The name of the extension. |
319 | */ |
320 | |
321 | /*! |
322 | \variable QVulkanExtension::version |
323 | \brief The version of the extension. This is an integer, increasing with each backward |
324 | compatible change. |
325 | */ |
326 | |
327 | /*! |
328 | \fn bool operator==(const QVulkanExtension &lhs, const QVulkanExtension &rhs) |
329 | \since 5.10 |
330 | \relates QVulkanExtension |
331 | |
332 | Returns \c true if Vulkan extensions \a lhs and \a rhs are have the |
333 | same name and version. |
334 | */ |
335 | |
336 | /*! |
337 | \fn bool operator!=(const QVulkanExtension &lhs, const QVulkanExtension &rhs) |
338 | \since 5.10 |
339 | \relates QVulkanExtension |
340 | |
341 | Returns \c true if Vulkan extensions \a lhs and \a rhs are have different |
342 | name or version. |
343 | */ |
344 | |
345 | /*! |
346 | \fn size_t qHash(const QVulkanExtension &key, size_t seed = 0) |
347 | \since 5.10 |
348 | \relates QVulkanExtension |
349 | |
350 | Returns the hash value for the \a key, using \a seed to seed the |
351 | calculation. |
352 | */ |
353 | |
354 | /*! |
355 | \class QVulkanInfoVector |
356 | \inmodule QtGui |
357 | \brief A specialized QList for QVulkanLayer and QVulkanExtension. |
358 | */ |
359 | |
360 | /*! |
361 | \fn template<typename T> bool QVulkanInfoVector<T>::contains(const QByteArray &name) const |
362 | |
363 | \return true if the list contains a layer or extension with the given \a name. |
364 | */ |
365 | |
366 | /*! |
367 | \fn template<typename T> bool QVulkanInfoVector<T>::contains(const QByteArray &name, int minVersion) const |
368 | |
369 | \return true if the list contains a layer or extension with the given |
370 | \a name and a version same as or newer than \a minVersion. |
371 | */ |
372 | |
373 | /*! |
374 | \fn QVulkanInfoVector<QVulkanLayer> QVulkanInstance::supportedLayers() const |
375 | \return the list of supported instance-level layers. |
376 | |
377 | \note This function can be called before create(). |
378 | */ |
379 | |
380 | /*! |
381 | \internal |
382 | */ |
383 | QVulkanInfoVector<QVulkanLayer> QVulkanInstance::supportedLayers() |
384 | { |
385 | return d_ptr->ensureVulkan() ? d_ptr->platformInst->supportedLayers() : QVulkanInfoVector<QVulkanLayer>(); |
386 | } |
387 | |
388 | /*! |
389 | \fn QVulkanInfoVector<QVulkanExtension> QVulkanInstance::supportedExtensions() const |
390 | \return the list of supported instance-level extensions. |
391 | |
392 | \note This function can be called before create(). |
393 | */ |
394 | |
395 | /*! |
396 | \internal |
397 | */ |
398 | QVulkanInfoVector<QVulkanExtension> QVulkanInstance::supportedExtensions() |
399 | { |
400 | return d_ptr->ensureVulkan() ? d_ptr->platformInst->supportedExtensions() : QVulkanInfoVector<QVulkanExtension>(); |
401 | } |
402 | |
403 | /*! |
404 | \return the version of instance-level functionality supported by the Vulkan |
405 | implementation. |
406 | |
407 | In practice this is either the value returned from |
408 | vkEnumerateInstanceVersion, if that function is available (with Vulkan 1.1 |
409 | and newer), or 1.0. |
410 | |
411 | Applications that want to branch in their Vulkan feature and API usage |
412 | based on what Vulkan version is available at run time, can use this function |
413 | to determine what version to pass in to setApiVersion() before calling |
414 | create(). |
415 | |
416 | \note This function can be called before create(). |
417 | |
418 | \sa setApiVersion() |
419 | */ |
420 | QVersionNumber QVulkanInstance::supportedApiVersion() const |
421 | { |
422 | return d_ptr->ensureVulkan() ? d_ptr->platformInst->supportedApiVersion() : QVersionNumber(); |
423 | } |
424 | |
425 | /*! |
426 | Makes QVulkanInstance adopt an existing VkInstance handle instead of |
427 | creating a new one. |
428 | |
429 | \note \a existingVkInstance must have at least \c{VK_KHR_surface} and the |
430 | appropriate WSI-specific \c{VK_KHR_*_surface} extensions enabled. To ensure |
431 | debug output redirection is functional, \c{VK_EXT_debug_utils} is needed as |
432 | well. |
433 | |
434 | \note This function can only be called before create() and has no effect if |
435 | called afterwards. |
436 | */ |
437 | void QVulkanInstance::setVkInstance(VkInstance existingVkInstance) |
438 | { |
439 | if (isValid()) { |
440 | qWarning(msg: "QVulkanInstance already created; setVkInstance() has no effect" ); |
441 | return; |
442 | } |
443 | |
444 | d_ptr->vkInst = existingVkInstance; |
445 | } |
446 | |
447 | /*! |
448 | Configures the behavior of create() based on the provided \a flags. |
449 | |
450 | \note This function can only be called before create() and has no effect if |
451 | called afterwards. |
452 | */ |
453 | void QVulkanInstance::setFlags(Flags flags) |
454 | { |
455 | if (isValid()) { |
456 | qWarning(msg: "QVulkanInstance already created; setFlags() has no effect" ); |
457 | return; |
458 | } |
459 | |
460 | d_ptr->flags = flags; |
461 | } |
462 | |
463 | /*! |
464 | Specifies the list of instance \a layers to enable. It is safe to specify |
465 | unsupported layers as well because these get ignored when not supported at |
466 | run time. |
467 | |
468 | \note This function can only be called before create() and has no effect if |
469 | called afterwards. |
470 | */ |
471 | void QVulkanInstance::setLayers(const QByteArrayList &layers) |
472 | { |
473 | if (isValid()) { |
474 | qWarning(msg: "QVulkanInstance already created; setLayers() has no effect" ); |
475 | return; |
476 | } |
477 | |
478 | d_ptr->layers = layers; |
479 | } |
480 | |
481 | /*! |
482 | Specifies the list of additional instance \a extensions to enable. It is |
483 | safe to specify unsupported extensions as well because these get ignored |
484 | when not supported at run time. |
485 | |
486 | \note The surface-related extensions required by Qt (for example, \c |
487 | VK_KHR_win32_surface) will always be added automatically, no need to |
488 | include them in this list. |
489 | |
490 | \note \c VK_KHR_portability_enumeration is added automatically unless the |
491 | NoPortabilityDrivers flag is set. This value was introduced in Qt 6.5. |
492 | |
493 | \note This function can only be called before create() and has no effect if |
494 | called afterwards. |
495 | */ |
496 | void QVulkanInstance::setExtensions(const QByteArrayList &extensions) |
497 | { |
498 | if (isValid()) { |
499 | qWarning(msg: "QVulkanInstance already created; setExtensions() has no effect" ); |
500 | return; |
501 | } |
502 | |
503 | d_ptr->extensions = extensions; |
504 | } |
505 | |
506 | /*! |
507 | Specifies the highest Vulkan API version the application is designed to use. |
508 | |
509 | By default \a vulkanVersion is 0, which maps to Vulkan 1.0. |
510 | |
511 | \note This function can only be called before create() and has no effect if |
512 | called afterwards. |
513 | |
514 | \note Be aware that Vulkan 1.1 changes the behavior with regards to the |
515 | Vulkan API version field. In Vulkan 1.0 specifying an unsupported \a |
516 | vulkanVersion led to failing create() with \c VK_ERROR_INCOMPATIBLE_DRIVER, |
517 | as was mandated by the specification. Starting with Vulkan 1.1, the |
518 | specification disallows this, the driver must accept any version without |
519 | failing the instance creation. |
520 | |
521 | Application developers are advised to familiarize themselves with the \c |
522 | apiVersion notes in |
523 | \l{https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkApplicationInfo.html}{the |
524 | Vulkan specification}. |
525 | |
526 | \sa supportedApiVersion() |
527 | */ |
528 | void QVulkanInstance::setApiVersion(const QVersionNumber &vulkanVersion) |
529 | { |
530 | if (isValid()) { |
531 | qWarning(msg: "QVulkanInstance already created; setApiVersion() has no effect" ); |
532 | return; |
533 | } |
534 | |
535 | d_ptr->apiVersion = vulkanVersion; |
536 | } |
537 | |
538 | /*! |
539 | Initializes the Vulkan library and creates a new or adopts and existing |
540 | Vulkan instance. |
541 | |
542 | \return true if successful, false on error or when Vulkan is not supported. |
543 | |
544 | When successful, the pointer to this QVulkanInstance is retrievable via |
545 | \l {QVulkanInstance::}{vkInstance()}. |
546 | |
547 | The Vulkan instance and library is available as long as this |
548 | QVulkanInstance exists, or until destroy() is called. |
549 | |
550 | By default the VkInstance is created with the flag |
551 | \l{https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkInstanceCreateFlagBits.html}{VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR} |
552 | set. This means that Vulkan Portability physical devices get enumerated as |
553 | well. If this is not desired, set the NoPortabilityDrivers flag. |
554 | */ |
555 | bool QVulkanInstance::create() |
556 | { |
557 | if (isValid()) |
558 | destroy(); |
559 | |
560 | if (!d_ptr->ensureVulkan()) |
561 | return false; |
562 | |
563 | d_ptr->platformInst->createOrAdoptInstance(); |
564 | |
565 | if (d_ptr->platformInst->isValid()) { |
566 | d_ptr->vkInst = d_ptr->platformInst->vkInstance(); |
567 | d_ptr->layers = d_ptr->platformInst->enabledLayers(); |
568 | d_ptr->extensions = d_ptr->platformInst->enabledExtensions(); |
569 | d_ptr->errorCode = VK_SUCCESS; |
570 | d_ptr->funcs.reset(new QVulkanFunctions(this)); |
571 | d_ptr->platformInst->setDebugFilters(d_ptr->debugFilters); |
572 | d_ptr->platformInst->setDebugUtilsFilters(d_ptr->debugUtilsFilters); |
573 | return true; |
574 | } |
575 | |
576 | qWarning(msg: "Failed to create platform Vulkan instance" ); |
577 | if (d_ptr->platformInst) { |
578 | d_ptr->errorCode = d_ptr->platformInst->errorCode(); |
579 | d_ptr->platformInst.reset(); |
580 | } else { |
581 | d_ptr->errorCode = VK_NOT_READY; |
582 | } |
583 | return false; |
584 | } |
585 | |
586 | /*! |
587 | Destroys the underlying platform instance, thus destroying the VkInstance |
588 | (when owned). The QVulkanInstance object is still reusable by calling |
589 | create() again. |
590 | */ |
591 | void QVulkanInstance::destroy() |
592 | { |
593 | d_ptr->reset(); |
594 | } |
595 | |
596 | /*! |
597 | \return true if create() was successful and the instance is valid. |
598 | */ |
599 | bool QVulkanInstance::isValid() const |
600 | { |
601 | return d_ptr->platformInst && d_ptr->platformInst->isValid(); |
602 | } |
603 | |
604 | /*! |
605 | \return the Vulkan error code after an unsuccessful create(), \c VK_SUCCESS otherwise. |
606 | |
607 | The value is typically the return value from vkCreateInstance() (when |
608 | creating a new Vulkan instance instead of adopting an existing one), but |
609 | may also be \c VK_NOT_READY if the platform plugin does not support Vulkan. |
610 | */ |
611 | VkResult QVulkanInstance::errorCode() const |
612 | { |
613 | return d_ptr->errorCode; |
614 | } |
615 | |
616 | /*! |
617 | \return the VkInstance handle this QVulkanInstance wraps, or \nullptr if |
618 | create() has not yet been successfully called and no existing instance has |
619 | been provided via setVkInstance(). |
620 | */ |
621 | VkInstance QVulkanInstance::vkInstance() const |
622 | { |
623 | return d_ptr->vkInst; |
624 | } |
625 | |
626 | /*! |
627 | \return the requested flags. |
628 | */ |
629 | QVulkanInstance::Flags QVulkanInstance::flags() const |
630 | { |
631 | return d_ptr->flags; |
632 | } |
633 | |
634 | /*! |
635 | \return the enabled instance layers, if create() was called and was successful. The |
636 | requested layers otherwise. |
637 | */ |
638 | QByteArrayList QVulkanInstance::layers() const |
639 | { |
640 | return d_ptr->layers; |
641 | } |
642 | |
643 | /*! |
644 | \return the enabled instance extensions, if create() was called and was |
645 | successful. The requested extensions otherwise. |
646 | */ |
647 | QByteArrayList QVulkanInstance::extensions() const |
648 | { |
649 | return d_ptr->extensions; |
650 | } |
651 | |
652 | /*! |
653 | \return the requested Vulkan API version against which the application |
654 | expects to run, or a null version number if setApiVersion() was not called |
655 | before create(). |
656 | */ |
657 | QVersionNumber QVulkanInstance::apiVersion() const |
658 | { |
659 | return d_ptr->apiVersion; |
660 | } |
661 | |
662 | /*! |
663 | Resolves the Vulkan function with the given \a name. |
664 | |
665 | For core Vulkan commands prefer using the function wrappers retrievable from |
666 | functions() and deviceFunctions() instead. |
667 | */ |
668 | PFN_vkVoidFunction QVulkanInstance::getInstanceProcAddr(const char *name) |
669 | { |
670 | // The return value is PFN_vkVoidFunction instead of QFunctionPointer or |
671 | // similar because on some platforms honoring VKAPI_PTR is important. |
672 | return d_ptr->platformInst->getInstanceProcAddr(name); |
673 | } |
674 | |
675 | /*! |
676 | \return the platform Vulkan instance corresponding to this QVulkanInstance. |
677 | |
678 | \internal |
679 | */ |
680 | QPlatformVulkanInstance *QVulkanInstance::handle() const |
681 | { |
682 | return d_ptr->platformInst.data(); |
683 | } |
684 | |
685 | /*! |
686 | \return the corresponding QVulkanFunctions object that exposes the core |
687 | Vulkan command set, excluding device level functions, and is guaranteed to |
688 | be functional cross-platform. |
689 | |
690 | \note The returned object is owned and managed by the QVulkanInstance. Do |
691 | not destroy or alter it. |
692 | |
693 | The functions from the core Vulkan 1.0 API will be available always. When it |
694 | comes to higher Vulkan versions, such as, 1.1 and 1.2, the QVulkanFunctions |
695 | object will try to resolve the core API functions for those as well, but if |
696 | the Vulkan instance implementation at run time has no support for those, |
697 | calling any such unsupported function will lead to unspecified behavior. In |
698 | addition, to properly enable support for Vulkan versions higher than 1.0, an |
699 | appropriate instance API version may need to be set by calling |
700 | setApiVersion() before create(). To query the Vulkan implementation's |
701 | instance-level version, call supportedApiVersion(). |
702 | |
703 | \sa deviceFunctions(), supportedApiVersion() |
704 | */ |
705 | QVulkanFunctions *QVulkanInstance::functions() const |
706 | { |
707 | return d_ptr->funcs.data(); |
708 | } |
709 | |
710 | /*! |
711 | \return the QVulkanDeviceFunctions object that exposes the device level |
712 | core Vulkan command set and is guaranteed to be functional cross-platform. |
713 | |
714 | \note The Vulkan functions in the returned object must only be called with |
715 | \a device or a child object (VkQueue, VkCommandBuffer) of \a device as |
716 | their first parameter. This is because these functions are resolved via |
717 | \l{https://www.khronos.org/registry/vulkan/specs/1.0/man/html/vkGetDeviceProcAddr.html}{vkGetDeviceProcAddr} |
718 | in order to avoid the potential overhead of internal dispatching. |
719 | |
720 | \note The returned object is owned and managed by the QVulkanInstance. Do |
721 | not destroy or alter it. |
722 | |
723 | \note The object is cached so calling this function with the same \a device |
724 | again is a cheap operation. However, when the device gets destroyed, it is up |
725 | to the application to notify the QVulkanInstance by calling |
726 | resetDeviceFunctions(). |
727 | |
728 | The functions from the core Vulkan 1.0 API will be available always. When |
729 | it comes to higher Vulkan versions, such as, 1.1 and 1.2, the |
730 | QVulkanDeviceFunctions object will try to resolve the core API functions |
731 | for those as well, but if the Vulkan physical device at run time has no |
732 | support for those, calling any such unsupported function will lead to |
733 | unspecified behavior. To properly enable support for Vulkan versions higher |
734 | than 1.0, an appropriate instance API version may need to be set by calling |
735 | setApiVersion() before create(). In addition, applications are expected to |
736 | check the physical device's apiVersion in VkPhysicalDeviceProperties. |
737 | |
738 | \sa functions(), resetDeviceFunctions() |
739 | */ |
740 | QVulkanDeviceFunctions *QVulkanInstance::deviceFunctions(VkDevice device) |
741 | { |
742 | QVulkanDeviceFunctions *&f = d_ptr->deviceFuncs[device]; |
743 | if (!f) |
744 | f = new QVulkanDeviceFunctions(this, device); |
745 | return f; |
746 | } |
747 | |
748 | /*! |
749 | Invalidates and destroys the QVulkanDeviceFunctions object for the given |
750 | \a device. |
751 | |
752 | This function must be called when a VkDevice, for which deviceFunctions() |
753 | was called, gets destroyed while the application intends to continue |
754 | running, possibly creating a new logical Vulkan device later on. |
755 | |
756 | There is no need to call this before destroying the QVulkanInstance since |
757 | clean up is then performed automatically. |
758 | |
759 | \sa deviceFunctions() |
760 | */ |
761 | void QVulkanInstance::resetDeviceFunctions(VkDevice device) |
762 | { |
763 | QVulkanDeviceFunctions *&f = d_ptr->deviceFuncs[device]; |
764 | delete f; |
765 | f = nullptr; |
766 | } |
767 | |
768 | /*! |
769 | Creates or retrieves the already existing \c{VkSurfaceKHR} handle for the |
770 | given \a window. |
771 | |
772 | \return the Vulkan surface handle or 0 when failed. |
773 | */ |
774 | VkSurfaceKHR QVulkanInstance::surfaceForWindow(QWindow *window) |
775 | { |
776 | QPlatformNativeInterface *nativeInterface = qGuiApp->platformNativeInterface(); |
777 | // VkSurfaceKHR is non-dispatchable and maps to a pointer on x64 and a uint64 on x86. |
778 | // Therefore a pointer is returned from the platform plugin, not the value itself. |
779 | void *p = nativeInterface->nativeResourceForWindow(QByteArrayLiteral("vkSurface" ), window); |
780 | return p ? *static_cast<VkSurfaceKHR *>(p) : VK_NULL_HANDLE; |
781 | } |
782 | |
783 | /*! |
784 | \return true if the queue family with \a queueFamilyIndex within the |
785 | \a physicalDevice supports presenting to \a window. |
786 | |
787 | Call this function when examining the queues of a given Vulkan device, in |
788 | order to decide which queue can be used for performing presentation. |
789 | */ |
790 | bool QVulkanInstance::supportsPresent(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, QWindow *window) |
791 | { |
792 | return d_ptr->platformInst->supportsPresent(physicalDevice, queueFamilyIndex, window); |
793 | } |
794 | |
795 | /*! |
796 | This function should be called by the application's renderer before queuing |
797 | a present operation for \a window. |
798 | |
799 | While on some platforms this will be a no-op, some may perform windowing |
800 | system dependent synchronization. For example, on Wayland this will |
801 | add send a wl_surface.frame request in order to prevent the driver from |
802 | blocking for minimized windows. |
803 | |
804 | \since 5.15 |
805 | */ |
806 | void QVulkanInstance::presentAboutToBeQueued(QWindow *window) |
807 | { |
808 | d_ptr->platformInst->presentAboutToBeQueued(window); |
809 | } |
810 | |
811 | /*! |
812 | This function should be called by the application's renderer after queuing |
813 | a present operation for \a window. |
814 | |
815 | While on some platforms this will be a no-op, some may perform windowing |
816 | system dependent synchronization. For example, on X11 this will update |
817 | \c{_NET_WM_SYNC_REQUEST_COUNTER}. |
818 | */ |
819 | void QVulkanInstance::presentQueued(QWindow *window) |
820 | { |
821 | d_ptr->platformInst->presentQueued(window); |
822 | } |
823 | |
824 | /*! |
825 | \typedef QVulkanInstance::DebugFilter |
826 | |
827 | Typedef for debug filtering callback functions, with the following signature: |
828 | |
829 | \code |
830 | bool myDebugFilter(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, |
831 | size_t location, int32_t messageCode, const char *pLayerPrefix, const char *pMessage) |
832 | \endcode |
833 | |
834 | Returning \c true suppresses the printing of the message. |
835 | |
836 | \note Starting with Qt 6.5 \c{VK_EXT_debug_utils} is used instead of the |
837 | deprecated \c{VK_EXT_debug_report}. The callback signature is based on |
838 | VK_EXT_debug_report. Therefore, not all arguments can be expected to be |
839 | valid anymore. Avoid relying on arguments other than \c pMessage, \c |
840 | messageCode, and \c object. Applications wishing to access all the callback |
841 | data as specified in VK_EXT_debug_utils should migrate to DebugUtilsFilter. |
842 | |
843 | \sa installDebugOutputFilter(), removeDebugOutputFilter() |
844 | */ |
845 | |
846 | /*! |
847 | \overload |
848 | |
849 | Installs a \a filter function that is called for every Vulkan debug |
850 | message. When the callback returns \c true, the message is stopped (filtered |
851 | out) and will not appear on the debug output. |
852 | |
853 | \note Filtering is only effective when NoDebugOutputRedirect is not |
854 | \l{setFlags()}{set}. Installing filters has no effect otherwise. |
855 | |
856 | \note This function can be called before create(). |
857 | |
858 | \sa removeDebugOutputFilter() |
859 | */ |
860 | void QVulkanInstance::installDebugOutputFilter(DebugFilter filter) |
861 | { |
862 | if (!d_ptr->debugFilters.contains(t: filter)) { |
863 | d_ptr->debugFilters.append(t: filter); |
864 | if (d_ptr->platformInst) |
865 | d_ptr->platformInst->setDebugFilters(d_ptr->debugFilters); |
866 | } |
867 | } |
868 | |
869 | /*! |
870 | \overload |
871 | |
872 | Removes a \a filter function previously installed by |
873 | installDebugOutputFilter(). |
874 | |
875 | \note This function can be called before create(). |
876 | |
877 | \sa installDebugOutputFilter() |
878 | */ |
879 | void QVulkanInstance::removeDebugOutputFilter(DebugFilter filter) |
880 | { |
881 | d_ptr->debugFilters.removeOne(t: filter); |
882 | if (d_ptr->platformInst) |
883 | d_ptr->platformInst->setDebugFilters(d_ptr->debugFilters); |
884 | } |
885 | |
886 | /*! |
887 | \typedef QVulkanInstance::DebugUtilsFilter |
888 | |
889 | Typedef for debug filtering callback functions, with the following signature: |
890 | |
891 | \code |
892 | std::function<bool(DebugMessageSeverityFlags severity, DebugMessageTypeFlags type, const void *message)>; |
893 | \endcode |
894 | |
895 | The \c message argument is a pointer to the |
896 | VkDebugUtilsMessengerCallbackDataEXT structure. Refer to the documentation |
897 | of \c{VK_EXT_debug_utils} for details. The Qt headers do not use the real |
898 | type in order to avoid introducing a dependency on post-1.0 Vulkan headers. |
899 | |
900 | Returning \c true suppresses the printing of the message. |
901 | |
902 | \sa installDebugOutputFilter(), removeDebugOutputFilter() |
903 | \since 6.5 |
904 | */ |
905 | |
906 | /*! |
907 | \enum QVulkanInstance::DebugMessageSeverityFlag |
908 | \since 6.5 |
909 | |
910 | \value VerboseSeverity |
911 | \value InfoSeverity |
912 | \value WarningSeverity |
913 | \value ErrorSeverity |
914 | */ |
915 | |
916 | /*! |
917 | \enum QVulkanInstance::DebugMessageTypeFlag |
918 | \since 6.5 |
919 | |
920 | \value GeneralMessage |
921 | \value ValidationMessage |
922 | \value PerformanceMessage |
923 | */ |
924 | |
925 | /*! |
926 | Installs a \a filter function that is called for every Vulkan debug |
927 | message. When the callback returns \c true, the message is stopped (filtered |
928 | out) and will not appear on the debug output. |
929 | |
930 | \note Filtering is only effective when NoDebugOutputRedirect is not |
931 | \l{setFlags()}{set}. Installing filters has no effect otherwise. |
932 | |
933 | \note This function can be called before create(). |
934 | |
935 | \sa clearDebugOutputFilters() |
936 | \since 6.5 |
937 | */ |
938 | void QVulkanInstance::installDebugOutputFilter(DebugUtilsFilter filter) |
939 | { |
940 | d_ptr->debugUtilsFilters.append(t: filter); |
941 | if (d_ptr->platformInst) |
942 | d_ptr->platformInst->setDebugUtilsFilters(d_ptr->debugUtilsFilters); |
943 | } |
944 | |
945 | /*! |
946 | Removes all filter functions installed previously by |
947 | installDebugOutputFilter(). |
948 | |
949 | \note This function can be called before create(). |
950 | |
951 | \sa installDebugOutputFilter() |
952 | \since 6.5 |
953 | */ |
954 | void QVulkanInstance::clearDebugOutputFilters() |
955 | { |
956 | d_ptr->debugFilters.clear(); |
957 | d_ptr->debugUtilsFilters.clear(); |
958 | if (d_ptr->platformInst) { |
959 | d_ptr->platformInst->setDebugFilters(d_ptr->debugFilters); |
960 | d_ptr->platformInst->setDebugUtilsFilters(d_ptr->debugUtilsFilters); |
961 | } |
962 | } |
963 | |
964 | #ifndef QT_NO_DEBUG_STREAM |
965 | QDebug operator<<(QDebug dbg, const QVulkanLayer &layer) |
966 | { |
967 | QDebugStateSaver saver(dbg); |
968 | dbg.nospace() << "QVulkanLayer(" << layer.name << " " << layer.version |
969 | << " " << layer.specVersion << " " << layer.description << ")" ; |
970 | return dbg; |
971 | } |
972 | |
973 | QDebug operator<<(QDebug dbg, const QVulkanExtension &extension) |
974 | { |
975 | QDebugStateSaver saver(dbg); |
976 | dbg.nospace() << "QVulkanExtension(" << extension.name << " " << extension.version << ")" ; |
977 | return dbg; |
978 | } |
979 | #endif |
980 | |
981 | QT_END_NAMESPACE |
982 | |